- Home
- Documentation
- configuration
- RPC Protocol Reference
RPC Protocol Reference
RPC Protocol Reference
Section titled “RPC Protocol Reference”RPC mode runs the coding agent as a newline-delimited JSON protocol over stdio.
- stdin: commands (
RpcCommand) and extension UI responses - stdout: command responses (
RpcResponse), session/agent events, extension UI requests
Primary implementation:
src/modes/rpc/rpc-mode.tssrc/modes/rpc/rpc-types.tssrc/session/agent-session.tspackages/agent/src/agent.tspackages/agent/src/agent-loop.ts
Startup
Section titled “Startup”xcsh --mode rpc [regular CLI options]Behavior notes:
@fileCLI arguments are rejected in RPC mode.- RPC mode disables automatic session title generation by default to avoid an extra model call.
- RPC mode resets workflow-altering
todo.*,task.*, andasync.*settings to their built-in defaults instead of inheriting user overrides. - The process reads stdin as JSONL (
readJsonl(Bun.stdin.stream())). - When stdin closes, the process exits with code
0. - Responses/events are written as one JSON object per line.
Transport and Framing
Section titled “Transport and Framing”Each frame is a single JSON object followed by \n.
There is no envelope beyond the object shape itself.
Outbound frame categories (stdout)
Section titled “Outbound frame categories (stdout)”RpcResponse({ type: "response", ... })AgentSessionEventobjects (agent_start,message_update, etc.)RpcExtensionUIRequest({ type: "extension_ui_request", ... })- Extension errors (
{ type: "extension_error", extensionPath, event, error })
Inbound frame categories (stdin)
Section titled “Inbound frame categories (stdin)”RpcCommandRpcExtensionUIResponse({ type: "extension_ui_response", ... })
Request/Response Correlation
Section titled “Request/Response Correlation”All commands accept optional id?: string.
- If provided, normal command responses echo the same
id. RpcClientrelies on this for pending-request resolution.
Important edge behavior from runtime:
- Unknown command responses are emitted with
id: undefined(even if the request had anid). - Parse/handler exceptions in the input loop emit
command: "parse"withid: undefined. promptandabort_and_promptreturn immediate success, then may emit a later error response with the same id if async prompt scheduling fails.
Command Schema (canonical)
Section titled “Command Schema (canonical)”RpcCommand is defined in src/modes/rpc/rpc-types.ts:
Prompting
Section titled “Prompting”{ id?, type: "prompt", message: string, images?: ImageContent[], streamingBehavior?: "steer" | "followUp" }{ id?, type: "steer", message: string, images?: ImageContent[] }{ id?, type: "follow_up", message: string, images?: ImageContent[] }{ id?, type: "abort" }{ id?, type: "abort_and_prompt", message: string, images?: ImageContent[] }{ id?, type: "new_session", parentSession?: string }
{ id?, type: "get_state" }{ id?, type: "set_todos", phases: TodoPhase[] }{ id?, type: "set_host_tools", tools: RpcHostToolDefinition[] }
{ id?, type: "set_model", provider: string, modelId: string }{ id?, type: "cycle_model" }{ id?, type: "get_available_models" }
Thinking
Section titled “Thinking”{ id?, type: "set_thinking_level", level: ThinkingLevel }{ id?, type: "cycle_thinking_level" }
Queue modes
Section titled “Queue modes”{ id?, type: "set_steering_mode", mode: "all" | "one-at-a-time" }{ id?, type: "set_follow_up_mode", mode: "all" | "one-at-a-time" }{ id?, type: "set_interrupt_mode", mode: "immediate" | "wait" }
Compaction
Section titled “Compaction”{ id?, type: "compact", customInstructions?: string }{ id?, type: "set_auto_compaction", enabled: boolean }
{ id?, type: "set_auto_retry", enabled: boolean }{ id?, type: "abort_retry" }
{ id?, type: "bash", command: string }{ id?, type: "abort_bash" }
Session
Section titled “Session”{ id?, type: "get_session_stats" }{ id?, type: "export_html", outputPath?: string }{ id?, type: "switch_session", sessionPath: string }{ id?, type: "branch", entryId: string }{ id?, type: "get_branch_messages" }{ id?, type: "get_last_assistant_text" }{ id?, type: "set_session_name", name: string }
Messages
Section titled “Messages”{ id?, type: "get_messages" }
Response Schema
Section titled “Response Schema”All command results use RpcResponse:
- Success:
{ id?, type: "response", command: <command>, success: true, data?: ... } - Failure:
{ id?, type: "response", command: string, success: false, error: string }
Data payloads are command-specific and defined in rpc-types.ts.
get_state payload
Section titled “get_state payload”{ "model": { "provider": "...", "id": "..." }, "thinkingLevel": "off|minimal|low|medium|high|xhigh", "isStreaming": false, "isCompacting": false, "steeringMode": "all|one-at-a-time", "followUpMode": "all|one-at-a-time", "interruptMode": "immediate|wait", "sessionFile": "...", "sessionId": "...", "sessionName": "...", "autoCompactionEnabled": true, "messageCount": 0, "queuedMessageCount": 0, "todoPhases": [ { "id": "phase-1", "name": "Todos", "tasks": [ { "id": "task-1", "content": "Map the tool surface", "status": "in_progress" } ] } ]}set_todos payload
Section titled “set_todos payload”Replaces the in-memory todo state for the current session and returns the normalized phase list:
{ "id": "req_2", "type": "set_todos", "phases": [ { "id": "phase-1", "name": "Evaluation", "tasks": [ { "id": "task-1", "content": "Map the read tool surface", "status": "in_progress" }, { "id": "task-2", "content": "Exercise edit operations", "status": "pending" } ] } ]}This is useful for hosts that want to pre-seed a plan before the first prompt.
set_host_tools payload
Section titled “set_host_tools payload”Replaces the current set of host-owned tools that the RPC server may call back into over stdio:
{ "id": "req_3", "type": "set_host_tools", "tools": [ { "name": "echo_host", "label": "Echo Host", "description": "Echo a value from the embedding host", "parameters": { "type": "object", "properties": { "message": { "type": "string" } }, "required": ["message"], "additionalProperties": false } } ]}The response payload is:
{ "toolNames": ["echo_host"]}These tools are added to the active session tool registry before the next model
call. Re-sending set_host_tools replaces the previous host-owned set.
Event Stream Schema
Section titled “Event Stream Schema”RPC mode forwards AgentSessionEvent objects from AgentSession.subscribe(...).
Common event types:
agent_start,agent_endturn_start,turn_endmessage_start,message_update,message_endtool_execution_start,tool_execution_update,tool_execution_endauto_compaction_start,auto_compaction_endauto_retry_start,auto_retry_endttsr_triggeredtodo_remindertodo_auto_clear
Extension runner errors are emitted separately as:
{ "type": "extension_error", "extensionPath": "...", "event": "...", "error": "..." }message_update includes streaming deltas in assistantMessageEvent (text/thinking/toolcall deltas).
Prompt/Queue Concurrency and Ordering
Section titled “Prompt/Queue Concurrency and Ordering”This is the most important operational behavior.
Immediate ack vs completion
Section titled “Immediate ack vs completion”prompt and abort_and_prompt are acknowledged immediately:
{ "id": "req_1", "type": "response", "command": "prompt", "success": true }That means:
- command acceptance != run completion
- final completion is observed via
agent_end
While streaming
Section titled “While streaming”AgentSession.prompt() requires streamingBehavior during active streaming:
"steer"=> queued steering message (interrupt path)"followUp"=> queued follow-up message (post-turn path)
If omitted during streaming, prompt fails.
Queue defaults
Section titled “Queue defaults”From the coding-agent settings schema (packages/coding-agent/src/config/settings-schema.ts):
steeringMode:"one-at-a-time"followUpMode:"one-at-a-time"interruptMode:"wait"
Mode semantics
Section titled “Mode semantics”set_steering_mode/set_follow_up_mode"one-at-a-time": dequeue one queued message per turn"all": dequeue entire queue at once
set_interrupt_mode"immediate": tool execution checks steering between tool calls; pending steering can abort remaining tool calls in the turn"wait": defer steering until turn completion
Extension UI Sub-Protocol
Section titled “Extension UI Sub-Protocol”Extensions in RPC mode use request/response UI frames.
Outbound request
Section titled “Outbound request”RpcExtensionUIRequest (type: "extension_ui_request") methods:
select,confirm,input,editornotify,setStatus,setWidget,setTitle,set_editor_text
Runtime note:
- Automatic session title generation is disabled in RPC mode, and
setTitleUI requests are also suppressed by default because most hosts do not have a meaningful terminal-title surface. SetPI_RPC_EMIT_TITLE=1to opt back in to the UI event only.
Example:
{ "type": "extension_ui_request", "id": "123", "method": "confirm", "title": "Confirm", "message": "Continue?", "timeout": 30000 }Inbound response
Section titled “Inbound response”RpcExtensionUIResponse (type: "extension_ui_response"):
{ type: "extension_ui_response", id: string, value: string }{ type: "extension_ui_response", id: string, confirmed: boolean }{ type: "extension_ui_response", id: string, cancelled: true }
If a dialog has a timeout, RPC mode resolves to a default value when timeout/abort fires.
Host Tool Sub-Protocol
Section titled “Host Tool Sub-Protocol”RPC hosts can expose custom tools to the agent by sending set_host_tools, then
serving execution requests over the same transport.
Outbound request
Section titled “Outbound request”When the agent wants the host to execute one of those tools, RPC mode emits:
{ "type": "host_tool_call", "id": "host_1", "toolCallId": "toolu_123", "toolName": "echo_host", "arguments": { "message": "hello" }}If the tool execution is later aborted, RPC mode emits:
{ "type": "host_tool_cancel", "id": "host_cancel_1", "targetId": "host_1"}Inbound updates and completion
Section titled “Inbound updates and completion”Hosts can optionally stream progress:
{ "type": "host_tool_update", "id": "host_1", "partialResult": { "content": [{ "type": "text", "text": "working" }] }}Completion uses:
{ "type": "host_tool_result", "id": "host_1", "result": { "content": [{ "type": "text", "text": "done" }] }}Set isError: true on host_tool_result to surface the returned content as a
tool error.
Error Model and Recoverability
Section titled “Error Model and Recoverability”Command-level failures
Section titled “Command-level failures”Failures are success: false with string error.
{ "id": "req_2", "type": "response", "command": "set_model", "success": false, "error": "Model not found: provider/model" }Recoverability expectations
Section titled “Recoverability expectations”- Most command failures are recoverable; process remains alive.
- Malformed JSONL / parse-loop exceptions emit a
parseerror response and continue reading subsequent lines. - Empty
set_session_nameis rejected (Session name cannot be empty). - Extension UI responses with unknown
idare ignored. - Process termination conditions are stdin close or explicit extension-triggered shutdown.
Compact Command Flows
Section titled “Compact Command Flows”1) Prompt and stream
Section titled “1) Prompt and stream”stdin:
{ "id": "req_1", "type": "prompt", "message": "Summarize this repo" }stdout sequence (typical):
{ "id": "req_1", "type": "response", "command": "prompt", "success": true }{ "type": "agent_start" }{ "type": "message_update", "assistantMessageEvent": { "type": "text_delta", "delta": "..." }, "message": { "role": "assistant", "content": [] } }{ "type": "agent_end", "messages": [] }2) Prompt during streaming with explicit queue policy
Section titled “2) Prompt during streaming with explicit queue policy”stdin:
{ "id": "req_2", "type": "prompt", "message": "Also include risks", "streamingBehavior": "followUp" }3) Inspect and tune queue behavior
Section titled “3) Inspect and tune queue behavior”stdin:
{ "id": "q1", "type": "get_state" }{ "id": "q2", "type": "set_steering_mode", "mode": "all" }{ "id": "q3", "type": "set_interrupt_mode", "mode": "wait" }4) Extension UI round trip
Section titled “4) Extension UI round trip”stdout:
{ "type": "extension_ui_request", "id": "ui_7", "method": "input", "title": "Branch name", "placeholder": "feature/..." }stdin:
{ "type": "extension_ui_response", "id": "ui_7", "value": "feature/rpc-host" }Notes on RpcClient helper
Section titled “Notes on RpcClient helper”src/modes/rpc/rpc-client.ts is a convenience wrapper, not the protocol definition.
Current helper characteristics:
- Spawns
bun <cliPath> --mode rpc - Correlates responses by generated
req_<n>ids - Dispatches only recognized
AgentEventtypes to listeners - Supports host-owned custom tools via
setCustomTools()and automatic handling ofhost_tool_call/host_tool_cancel - Does not expose helper methods for every protocol command (for example,
set_interrupt_modeandset_session_nameare in protocol types but not wrapped as dedicated methods)
Use raw protocol frames if you need complete surface coverage.