docs(docs): moved documentation to root docs directory and updated all references
- Moved documentation files from packages/coding-agent/docs/ to root docs/ directory to flatten the documentation structure. - Updated all internal documentation links to account for the new file locations, adjusting relative paths to maintain correct references across the monorepo. - Updated README.md and issue template configuration to reference documentation at the new root docs/ location instead of packages/coding-agent/docs/.
This commit is contained in:
+325
@@ -0,0 +1,325 @@
|
||||
# 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.ts`
|
||||
- `src/modes/rpc/rpc-types.ts`
|
||||
- `src/session/agent-session.ts`
|
||||
- `packages/agent/src/agent.ts`
|
||||
- `packages/agent/src/agent-loop.ts`
|
||||
|
||||
## Startup
|
||||
|
||||
```bash
|
||||
omp --mode rpc [regular CLI options]
|
||||
```
|
||||
|
||||
Behavior notes:
|
||||
|
||||
- `@file` CLI arguments are rejected in RPC mode.
|
||||
- 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
|
||||
|
||||
Each frame is a single JSON object followed by `\n`.
|
||||
|
||||
There is no envelope beyond the object shape itself.
|
||||
|
||||
### Outbound frame categories (stdout)
|
||||
|
||||
1. `RpcResponse` (`{ type: "response", ... }`)
|
||||
2. `AgentSessionEvent` objects (`agent_start`, `message_update`, etc.)
|
||||
3. `RpcExtensionUIRequest` (`{ type: "extension_ui_request", ... }`)
|
||||
4. Extension errors (`{ type: "extension_error", extensionPath, event, error }`)
|
||||
|
||||
### Inbound frame categories (stdin)
|
||||
|
||||
1. `RpcCommand`
|
||||
2. `RpcExtensionUIResponse` (`{ type: "extension_ui_response", ... }`)
|
||||
|
||||
## Request/Response Correlation
|
||||
|
||||
All commands accept optional `id?: string`.
|
||||
|
||||
- If provided, normal command responses echo the same `id`.
|
||||
- `RpcClient` relies on this for pending-request resolution.
|
||||
|
||||
Important edge behavior from runtime:
|
||||
|
||||
- Unknown command responses are emitted with `id: undefined` (even if the request had an `id`).
|
||||
- Parse/handler exceptions in the input loop emit `command: "parse"` with `id: undefined`.
|
||||
- `prompt` and `abort_and_prompt` return immediate success, then may emit a later error response with the **same** id if async prompt scheduling fails.
|
||||
|
||||
## Command Schema (canonical)
|
||||
|
||||
`RpcCommand` is defined in `src/modes/rpc/rpc-types.ts`:
|
||||
|
||||
### 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 }`
|
||||
|
||||
### State
|
||||
|
||||
- `{ id?, type: "get_state" }`
|
||||
|
||||
### Model
|
||||
|
||||
- `{ id?, type: "set_model", provider: string, modelId: string }`
|
||||
- `{ id?, type: "cycle_model" }`
|
||||
- `{ id?, type: "get_available_models" }`
|
||||
|
||||
### Thinking
|
||||
|
||||
- `{ id?, type: "set_thinking_level", level: ThinkingLevel }`
|
||||
- `{ id?, type: "cycle_thinking_level" }`
|
||||
|
||||
### 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
|
||||
|
||||
- `{ id?, type: "compact", customInstructions?: string }`
|
||||
- `{ id?, type: "set_auto_compaction", enabled: boolean }`
|
||||
|
||||
### Retry
|
||||
|
||||
- `{ id?, type: "set_auto_retry", enabled: boolean }`
|
||||
- `{ id?, type: "abort_retry" }`
|
||||
|
||||
### Bash
|
||||
|
||||
- `{ id?, type: "bash", command: string }`
|
||||
- `{ id?, type: "abort_bash" }`
|
||||
|
||||
### 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
|
||||
|
||||
- `{ id?, type: "get_messages" }`
|
||||
|
||||
## 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
|
||||
|
||||
```json
|
||||
{
|
||||
"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
|
||||
}
|
||||
```
|
||||
|
||||
## Event Stream Schema
|
||||
|
||||
RPC mode forwards `AgentSessionEvent` objects from `AgentSession.subscribe(...)`.
|
||||
|
||||
Common event types:
|
||||
|
||||
- `agent_start`, `agent_end`
|
||||
- `turn_start`, `turn_end`
|
||||
- `message_start`, `message_update`, `message_end`
|
||||
- `tool_execution_start`, `tool_execution_update`, `tool_execution_end`
|
||||
- `auto_compaction_start`, `auto_compaction_end`
|
||||
- `auto_retry_start`, `auto_retry_end`
|
||||
- `ttsr_triggered`
|
||||
- `todo_reminder`
|
||||
|
||||
Extension runner errors are emitted separately as:
|
||||
|
||||
```json
|
||||
{ "type": "extension_error", "extensionPath": "...", "event": "...", "error": "..." }
|
||||
```
|
||||
|
||||
`message_update` includes streaming deltas in `assistantMessageEvent` (text/thinking/toolcall deltas).
|
||||
|
||||
## Prompt/Queue Concurrency and Ordering
|
||||
|
||||
This is the most important operational behavior.
|
||||
|
||||
### Immediate ack vs completion
|
||||
|
||||
`prompt` and `abort_and_prompt` are **acknowledged immediately**:
|
||||
|
||||
```json
|
||||
{ "id": "req_1", "type": "response", "command": "prompt", "success": true }
|
||||
```
|
||||
|
||||
That means:
|
||||
|
||||
- command acceptance != run completion
|
||||
- final completion is observed via `agent_end`
|
||||
|
||||
### 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
|
||||
|
||||
From `packages/agent/src/agent.ts` defaults:
|
||||
|
||||
- `steeringMode`: `"one-at-a-time"`
|
||||
- `followUpMode`: `"one-at-a-time"`
|
||||
- `interruptMode`: `"immediate"`
|
||||
|
||||
### 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
|
||||
|
||||
Extensions in RPC mode use request/response UI frames.
|
||||
|
||||
### Outbound request
|
||||
|
||||
`RpcExtensionUIRequest` (`type: "extension_ui_request"`) methods:
|
||||
|
||||
- `select`, `confirm`, `input`, `editor`
|
||||
- `notify`, `setStatus`, `setWidget`, `setTitle`, `set_editor_text`
|
||||
|
||||
Example:
|
||||
|
||||
```json
|
||||
{ "type": "extension_ui_request", "id": "123", "method": "confirm", "title": "Confirm", "message": "Continue?", "timeout": 30000 }
|
||||
```
|
||||
|
||||
### 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.
|
||||
|
||||
## Error Model and Recoverability
|
||||
|
||||
### Command-level failures
|
||||
|
||||
Failures are `success: false` with string `error`.
|
||||
|
||||
```json
|
||||
{ "id": "req_2", "type": "response", "command": "set_model", "success": false, "error": "Model not found: provider/model" }
|
||||
```
|
||||
|
||||
### Recoverability expectations
|
||||
|
||||
- Most command failures are recoverable; process remains alive.
|
||||
- Malformed JSONL / parse-loop exceptions emit a `parse` error response and continue reading subsequent lines.
|
||||
- Empty `set_session_name` is rejected (`Session name cannot be empty`).
|
||||
- Extension UI responses with unknown `id` are ignored.
|
||||
- Process termination conditions are stdin close or explicit extension-triggered shutdown.
|
||||
|
||||
## Compact Command Flows
|
||||
|
||||
### 1) Prompt and stream
|
||||
|
||||
stdin:
|
||||
|
||||
```json
|
||||
{ "id": "req_1", "type": "prompt", "message": "Summarize this repo" }
|
||||
```
|
||||
|
||||
stdout sequence (typical):
|
||||
|
||||
```json
|
||||
{ "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
|
||||
|
||||
stdin:
|
||||
|
||||
```json
|
||||
{ "id": "req_2", "type": "prompt", "message": "Also include risks", "streamingBehavior": "followUp" }
|
||||
```
|
||||
|
||||
### 3) Inspect and tune queue behavior
|
||||
|
||||
stdin:
|
||||
|
||||
```json
|
||||
{ "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
|
||||
|
||||
stdout:
|
||||
|
||||
```json
|
||||
{ "type": "extension_ui_request", "id": "ui_7", "method": "input", "title": "Branch name", "placeholder": "feature/..." }
|
||||
```
|
||||
|
||||
stdin:
|
||||
|
||||
```json
|
||||
{ "type": "extension_ui_response", "id": "ui_7", "value": "feature/rpc-host" }
|
||||
```
|
||||
|
||||
## 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 `AgentEvent` types to listeners
|
||||
- Does **not** expose helper methods for every protocol command (for example, `set_interrupt_mode` and `set_session_name` are in protocol types but not wrapped as dedicated methods)
|
||||
|
||||
Use raw protocol frames if you need complete surface coverage.
|
||||
Reference in New Issue
Block a user