7 months ago
1c42952* feat: add websocket protocol negotiation foundation Co-authored-by: Max Weinbach <mweinbach@users.noreply.github.com> * feat: add core jsonrpc websocket thread flows Co-authored-by: Max Weinbach <mweinbach@users.noreply.github.com> * feat: add jsonrpc thread journal replay Co-authored-by: Max Weinbach <mweinbach@users.noreply.github.com> * feat: add jsonrpc client transport and queue bounds Co-authored-by: Max Weinbach <mweinbach@users.noreply.github.com> * docs: add websocket jsonrpc smoke examples Co-authored-by: Max Weinbach <mweinbach@users.noreply.github.com> * feat: add desktop jsonrpc single connection path Co-authored-by: Max Weinbach <mweinbach@users.noreply.github.com> * feat: add jsonrpc overload limits and schema codegen Co-authored-by: Max Weinbach <mweinbach@users.noreply.github.com> * feat: add jsonrpc control namespace foundation Co-authored-by: Max Weinbach <mweinbach@users.noreply.github.com> * feat: route desktop control actions over jsonrpc Co-authored-by: Max Weinbach <mweinbach@users.noreply.github.com> * feat: add jsonrpc session control methods Co-authored-by: Max Weinbach <mweinbach@users.noreply.github.com> * docs: cover cowork jsonrpc control methods Co-authored-by: Max Weinbach <mweinbach@users.noreply.github.com> * fix(desktop): canonicalize jsonrpc workspace state * fix(desktop): route workspace live state through jsonrpc * docs: mark desktop websocket path as jsonrpc-only * refactor(desktop): remove legacy live transport paths * test(desktop): rewrite transport coverage for jsonrpc * fix(desktop): keep ipc handlers live during quit * fix(desktop): sync thread defaults over jsonrpc live state * test(jsonrpc): cover live session state updates * docs: record jsonrpc optimistic send lesson * fix(jsonrpc): restore desktop live event parity * test(jsonrpc): cover live event reconciliation * test: stabilize desktop json-rpc socket mocks * fix: guard desktop json-rpc sends without a workspace socket * fix: negotiate first supported ws subprotocol * fix: handle json-rpc turn send failures * fix(jsonrpc): harden thread replay and summary counts * fix(desktop): unify queued sends and provider refresh * fix(desktop): harden refresh and default sync timing * fix(jsonrpc): fail terminal reconnect states cleanly * fix(desktop): apply fresh jsonrpc defaults before hydration * test(server): await jsonrpc flow shutdowns * refactor(desktop): isolate provider refresh helper * fix: harden desktop workspace json-rpc lifecycle * fix: tighten json-rpc reconnect and resume replay * chore: capture PR review rescan lesson * fix: dedupe migrated desktop json-rpc connects * fix: harden json-rpc replay and retry recovery * fix: address remaining PR 56 review feedback Return the negotiated websocket subprotocol in the Bun upgrade response so multi-offer clients see the correct wire protocol. Clear provider status refresh spinners even when follow-up control RPCs succeed without event envelopes, and add focused regressions for both behaviors. Verification: - bun test test/server.jsonrpc.test.ts apps/desktop/test/provider-actions.test.ts apps/desktop/test/control-socket.test.ts - bun run typecheck - bun test * test: harden remote MCP smoke retries Retry transient upstream 5xx responses in the default remote MCP smoke test so Docs + Tests keeps its connectivity signal without failing on brief mcp.grep.app outages. Add deterministic helper coverage for retry classification and record the CI lesson in tasks/lessons.md. * fix: stabilize remote MCP smoke coverage in CI Split the default Docs + Tests lane from remote MCP smoke coverage so bun test stays deterministic while CI still exercises connect/discover against mcp.grep.app. Gate live remote tool execution behind RUN_REMOTE_MCP_AGENT_TESTS and record the babysitting lesson in tasks/lessons.md. * Remove duplicate JSON-RPC regression tests * fix: address JSON-RPC PR review (defaults, refresh race, errors, teardown) Preserve allowBeforeHydration on busy-thread deferral; clear providerStatusRefreshing only when the matching refresh generation completes; return JSON-RPC internal errors when route handlers reject; close the workspace socket after thread session_close/unsubscribe during workspace remove and restart. Made-with: Cursor * fix: emit live JSON-RPC reasoning items Suppress commentary-phase assistant deltas in the live JSON-RPC projectors, accumulate reasoning stream chunks into completed reasoning items, and add projector plus desktop reasoning regressions so live OpenAI/Codex turns match replay. * fix: preserve live JSON-RPC feed during hydration Prevent delayed thread/read snapshots from clobbering fresher live feed items or optimistic first sends, refresh control-socket lifecycle callbacks to use the latest store getter, and add desktop regressions for the hydration race and lifecycle reconnect path. * fix: tighten workspace defaults control persistence Narrow the workspace-defaults control message builder to the apply_session_defaults client message shape and remove the redundant runtime type guard before the control RPC so future builder changes cannot silently skip persistence. * chore: record PR comment cleanup status Update the task tracker after closing the remaining PR review threads so the repo-local execution ledger matches the branch and PR state. * fix: preserve reconnect retry queue semantics Keep queued retryable JSON-RPC requests alive across transient initialize-handshake failures while auto-reconnect is still in progress, and apply notification opt-out filtering to replayed journal events during thread resume. Add runtime and server flow regressions plus tracker updates. * fix: guard provider refresh spinner by generation Align desktop bootstrap refresh handling with the existing providerStatusRefreshGeneration contract so older provider refresh responses cannot clear the spinner while a newer refresh is still in flight. Stop provider_status and generic control error events from owning spinner teardown, add control-socket regressions for the provider_status and bootstrap races, and record the verification evidence in tasks/todo.md. * fix: wait for JSON-RPC turn request acceptance Make turn/start and turn/steer wait for the first real session outcome before replying so JSON-RPC callers do not get guessed success payloads while the session is still deciding whether to accept the request. Use the existing session-event capture helper to promote session_busy and steer_accepted into request results, convert session-level rejections into JSON-RPC errors, add focused server-flow regressions, and record the verification evidence in tasks/todo.md. * fix: ignore stale JSON-RPC socket closes Guard workspace socket lifecycle callbacks against stale socket instances so a deferred close from an old socket cannot clear active control state after a server URL swap. Add a focused desktop regression that defers the old socket close until after the replacement socket opens, record the verification evidence in tasks/todo.md, and keep the change scoped to the shared JSON-RPC socket helper. * chore: record final PR review sweep Capture the final review-thread revalidation evidence for the JSON-RPC subprotocol transport path and repeated socket/typecheck verification before resolving the remaining GitHub comments. * fix(desktop): hydrate jsonrpc control state before diffing defaults Add a JSON-RPC cowork/session/state/read control method so desktop bootstrap can hydrate the workspace control session's provider/model, session settings, and editable config before computing workspace-default diffs. Restore truthful control-socket waiter diagnostics by tracking in-flight waits for control session readiness, workspace session listing, and session snapshot reads. Keep the local workspace runtime in sync after workspace default applies so repeated saves do not resend redundant cowork/session/defaults/apply calls. Update schema/docs/generated JSON-RPC artifacts and add focused desktop/server regressions covering bootstrap hydration, bundled legacy event envelopes, waiter accounting, and no-op workspace resaves. * chore: verify provider refresh false positive Co-authored-by: mweinbach <mweinbach@users.noreply.github.com> * fix: preserve workspace cleanup on removal Co-authored-by: mweinbach <mweinbach@users.noreply.github.com> * fix: simplify desktop provider refresh helper Co-authored-by: mweinbach <mweinbach@users.noreply.github.com> * Update startServer.ts * Update startServer.ts * fix: keep desktop workspace default sync consistent Co-authored-by: mweinbach <mweinbach@users.noreply.github.com> * fix(jsonrpc): stream reasoning delta notifications Add a dedicated item/reasoning/delta JSON-RPC notification and stop collapsing streamed reasoning into a late completed item. Persist and replay the new reasoning delta events through the journal/read path, regenerate the JSON-RPC schema artifacts, and update the server-side regression coverage for live streaming and replay. * fix(desktop): consume streamed reasoning JSON-RPC events Route shared-socket reasoning notifications through the desktop model-stream pipeline so reasoning cards render before the first assistant reply. Add the desktop regression coverage for incremental reasoning rendering and record the implementation plus verification in the task ledger. * Clean Up Server * fix(jsonrpc): stabilize reconnects and large journal reads Add handshake timeouts and query-mode fallback so JSON-RPC sockets recover quickly when Bun subprotocol negotiation stalls. Batch journal persistence and replay, and lower CI test concurrency to keep the full suite stable under Bun. Made-with: Cursor * fix(server): self-clean thread journal write queues Keep per-thread journal writes serialized without leaking settled tail promises. Preserve best-effort idle waits and existing thread replay semantics while adding focused queue coverage. Made-with: Cursor * refactor(desktop): clarify user message send acceptance vs JSON-RPC Made-with: Cursor * Update protocol-v2-events.test.ts * fix: JSON-RPC pending prompt reconnects - Clear pendingServerRequests on JSON-RPC socket close after unsubscribing threads - Add non-emitting pending prompt accessor; resume seeds projector without replayPendingPrompts - Dedupe journal afterSeq replay vs pending-prompt re-registration - Add reconnect regressions for ask, approval, and afterSeq dedupe Made-with: Cursor * fix(desktop): gate workspace JSON-RPC socket on generation Replace map-entry identity checks with per-workspace socket generation so retired sockets cannot clear control state, disconnect threads, or route events after URL swap or explicit workspace teardown. - Track workspaceJsonRpcSocketGenerations in RUNTIME; bump on URL change and workspace remove/restart - Stamp sockets and gate onOpen/onClose/onNotification/onServerRequest - Extend control-socket, thread-reconnect, and protocol-v2-events tests Made-with: Cursor * fix(desktop): teardown JSON-RPC workspace state on removeWorkspace - Add disposeWorkspaceJsonRpcState and call it only from removeWorkspace - Clear jsonRpcSocket/controlSocket/threadEventReducer per-workspace maps and block late async work after permanent deletion - Reject skill install waiters when control state is disposed without store refs - Extend workspace-settings-sync tests for teardown vs restart semantics Made-with: Cursor * fix(desktop): dispose JSON-RPC state on workspace removal and renderer shutdown - Thread reducer: store-aware disposal clears reconnect + pending connects per workspace - Control helpers: disposeAllControlState for full teardown - store.helpers: disposeWorkspaceJsonRpcState(get, id) and disposeAllJsonRpcState - removeWorkspace: run disposal in finally after stopWorkspaceServer attempt - App: beforeunload + unmount cleanup (StrictMode-safe scheduling) - Tests: ordering assert for thread/unsubscribe, control disposal, app shutdown Made-with: Cursor * Update workspace-settings-sync.test.ts * fix(desktop): guard reasoning item/completed dispatch against existing stream item When a reasoning item/completed JSON-RPC notification arrives, check whether a feed item already exists for this stream key (created by streaming deltas). If so, update it in-place instead of dispatching a legacy reasoning event, which would create a duplicate item appended to the bottom of the feed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(desktop): apply reasoning-before-assistant repositioning for all turn types Removed the rawBackedTurns guard from reasoningInsertBeforeAssistantAfterStreamReplay so that late-arriving reasoning items are inserted before the assistant message item for JSON-RPC (non-raw-backed) turns as well, not just raw-event-backed turns. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(desktop): ensure reasoning precedes tool calls in activity trace entries Reorder buildActivityTraceEntries to always place reasoning items before tool items within an activity group. During streaming, tool feed items can arrive before the reasoning block is flushed, causing tools to appear before reasoning in the rendered trace even though reasoning logically precedes tool use. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(desktop): register reasoning stream key eagerly on item/started to prevent empty-placeholder push Always dispatch reasoning_delta from item/started (even when initial text is empty) so the stream key is registered in reasoningItemIdByStream before tool items can arrive. Guard feed item creation in the reasoning_delta handler so an empty-text delta registers the key without pushing a blank item to the feed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(server): clear disconnected replay buffer before journal replay on thread resume When a client reconnects via thread/resume with afterSeq > 0, the server was calling ensureDisconnectedReplayBuffer() which preserves events that accumulated in the buffer since the client disconnected. The subsequent replayThreadJournalEvents() then sends those same events via the journal, and the buffer drain in subscribeJsonRpcThread sends them a second time, producing duplicate turn/item/reasoning/delta notifications. Switch to beginDisconnectedReplayBuffer() to reset the buffer before the journal replay. Since all code between the reset and the buffer drain in subscribeJsonRpcThread runs synchronously (no event-loop yields), no new events can be generated between the reset and the drain, so the drain is effectively a no-op. The journal replay covers all missed events cleanly with no duplicates. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(desktop): mirror control config defaults Co-authored-by: mweinbach <mweinbach@users.noreply.github.com> * fix: restore desktop replay and bootstrap guards Co-authored-by: mweinbach <mweinbach@users.noreply.github.com> * fix: restore reusable desktop jsonrpc workspace state Co-authored-by: mweinbach <mweinbach@users.noreply.github.com> * fix: harden jsonrpc control review paths * docs: record jsonrpc review thread fixes * fix: return jsonrpc skill installation errors * fix: restore jsonrpc replay and activity order * docs: record full test lane cleanup * fix: resolve final review comment issues --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Max Weinbach <mweinbach@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Parent0167f73