Overview
The whitelist and source slices cover the CLI's outbound bridge/remote-control flow (local CLI → Anthropic session ingress) and inbound SDK stream-json consumers, but they do not contain an inbound WebSocket/SSE server that an external SDK consumer would "connect to" on a running Claude Code session . The closest real flow is the reverse: the CLI exposes itself as a bridge environment that relays SDK messages to/from a remote session .
Steps
- The external entry is
claude remote-control, dispatched from the CLI bootstrap which gates on OAuth tokens and policy before handing off tobridgeMain.bridgeMainparses args, enables configs, initializes sinks, and resolves the bridge access token before any network I/O . - Authentication uses the stored claude.ai OAuth token via
getBridgeAccessToken; if absent,bridgeMainexits withBRIDGE_LOGIN_ERROR, and the REPL-side variant additionally proactively refreshes expired tokens before registering . - After auth, the bridge registers an environment and enters
runBridgeLoop, which long-polls the ingress for work items, tracks active sessions, and heartbeats each work item with its per-session ingress JWT . - Inbound SDK messages arriving over the ingress transport are parsed by
handleIngressMessage, which dedupes echoes viarecentPostedUUIDs, routescontrol_responseto the permission callback,control_requestto the control handler, anduserSDKMessages toonInboundMessage. - A user message is then fed into the local query loop via
QueryEngine.submitMessage, which builds the system prompt, wirescanUseTool, and yieldsSDKMessageevents (assistant, stream_event, result, status) as an async generator . - For the inverse direction — an SDK/React consumer attached to a remote session —
useRemoteSession'sonMessagereceives eachSDKMessage, drops echoes by uuid, handlestask_started/task_notification/status/compact_boundarysystem events, and converts the rest viaconvertSDKMessage. convertSDKMessagedispatches bymsg.typeto per-kind converters —convertAssistantMessage,convertStreamEvent,convertResultMessage,convertInitMessage,convertStatusMessage,convertCompactBoundaryMessage— producing renderable REPL messages .- Headless SDK consumers using stream-json stdio instead go through
runHeadless→runHeadlessStreaming, wherehandleInitializeRequestprocesses the initialcontrol_request(systemPrompt, hooks, agents, jsonSchema) and replies with acontrol_responsecarrying commands/agents/models . - Subsequent prompts are queued and drained by
drainCommandQueue, which batches consecutive prompt commands sharing a workload into oneask()turn and emits replay events for merged uuids . - Clean disconnect on the bridge side happens when
runBridgeLoop's abort signal fires: pending cleanups (stopWork, worktree removal) are awaited, heartbeats stop, and sessions are archived; on the REPL-consumer sideonMessageobservesisSessionEndMessageand flips loading off . - The whitelist does not expose a server-side WebSocket/SSE accept path inside this repo — the ingress is an external Anthropic service, and this CLI is only a client of it, so the literal "SDK consumer connects to a running Claude Code session over WebSocket/SSE" scenario is not answerable from the provided source .
State touched
Decisions
(none in whitelist)