WebSocket RPC
After authentication, the frontend performs all business operations over a persistent WebSocket connection. The RPC defines 16 methods in total, divided into two types: Action and Query.
RPC Classification
Section titled “RPC Classification”| Comparison | Action RPC | Query RPC |
|---|---|---|
| Operation Type | Create / Modify / Delete | Read-only queries |
| Response includes updates | ✅ Yes | ❌ No |
| Idempotency support | ✅ client_id | — |
| Typical scenarios | Send message, close session | Load message list |
Method Summary
Section titled “Method Summary”The 16 methods are organized across 4 business domains:
| Domain | Action | Query |
|---|---|---|
| Session | v1.session.close · v1.session.update · v1.session.fork · v1.session.compact | v1.session.list · v1.session.get |
| Message | v1.message.send | v1.message.list · v1.message.get |
| Turn | v1.turn.stop | v1.turn.list · v1.turn.get |
| RTC | v1.rtc.update_status · v1.rtc.submit_result | v1.rtc.list · v1.rtc.get |
Session Domain
Section titled “Session Domain”A session is a container for user-AI conversations. Each session contains multiple messages and turns.
| Method | Type | Function | Key Parameters |
|---|---|---|---|
v1.session.list | 🔍 Query | Get session list | cursor (pagination), limit (default 20) |
v1.session.get | 🔍 Query | Get a single session | session_id |
v1.session.close | ⚡ Action | Close a session | session_id |
v1.session.update | ⚡ Action | Update session title / soft delete | session_id, title, deleted_at |
v1.session.fork | ⚡ Action | Fork a conversation (create a new session based on history) | old_server_session_id, new_client_session_id, new_client_message_id, old_server_message_id, content_data, limit (default 200, max 1000) |
v1.session.compact | ⚡ Action | Manually trigger context compression (no updates returned) | session_id, custom_instruction |
Fork a Conversation
Section titled “Fork a Conversation”💡 Typical Fork scenario: The user wants to start a new direction from a branching point in the historical conversation. Specify the old message position, and the system copies the preceding context, replaces content after the specified position with new messages, and triggers new AI reasoning.
Fork response: Returns {session_id, turn_id, message_ids[]}. Note that turn_id is an empty string — the Turn is created asynchronously by the background turn-agent.
Message Domain
Section titled “Message Domain”Messages are the basic communication units within a session, supporting multiple content types.
| Method | Type | Function | Key Parameters |
|---|---|---|---|
v1.message.send | ⚡ Action | Send a message (automatically creates session and turn) | content_data, client_session_id, client_id (required), server_session_id (optional), agent_prompt (optional) |
v1.message.list | 🔍 Query | Get message list | session_id, cursor (global_offset, uint32), limit (default 50) |
v1.message.get | 🔍 Query | Get a single message | message_id |
Message Sending Flow
Section titled “Message Sending Flow”💡 Auto-creation: When calling
v1.message.send, ifserver_session_idis empty, the server will automatically create a new session. This means “create new conversation” and “send message” can be combined into a single call.💡 Async Turn: The
turn_idin responses fromv1.message.sendandv1.session.forkis an empty string — the Turn is created asynchronously by the background turn-agent and is not returned synchronously with the request.
Content Types
Section titled “Content Types”| Type | Description | Data Structure |
|---|---|---|
markdown | Markdown text | String |
text | Plain text | String |
thinking | AI reasoning process | String |
summary | Context summary | SummaryItem[] |
toolcall_input | Tool call request | ToolCall object |
toolcall_output | Tool call result | ToolCall object |
Turn Domain
Section titled “Turn Domain”A Turn represents a complete AI reasoning round — from the user sending a message to the AI completing its response.
| Method | Type | Function | Key Parameters |
|---|---|---|---|
v1.turn.stop | ⚡ Action | Stop the current Turn (no updates returned) | session_id |
v1.turn.list | 🔍 Query | Get Turn list | session_id, cursor, limit (default 50) |
v1.turn.get | 🔍 Query | Get a single Turn | turn_id |
| State | Description |
|---|---|
pending | Waiting to start |
running | AI is reasoning |
completed | Reasoning completed |
failed | Reasoning failed |
cancelled | User explicitly cancelled |
interrupted | Interrupted by the system |
merged | Merged with another Turn |
RTC Domain
Section titled “RTC Domain”RTC (Remote Tool Calling) is the mechanism by which AI calls frontend tools. The frontend uses RTC domain RPCs to report tool execution status and results.
| Method | Type | Function | Key Parameters |
|---|---|---|---|
v1.rtc.update_status | ⚡ Action | Update RTC execution status | rtc_id, status, client_id |
v1.rtc.submit_result | ⚡ Action | Submit tool execution result | rtc_id, success, result / error, client_id |
v1.rtc.list | 🔍 Query | Get RTC list | session_id, cursor, limit (default 50) |
v1.rtc.get | 🔍 Query | Get a single RTC | rtc_id |
RTC State Transitions
Section titled “RTC State Transitions”| State | Description | Frontend Action |
|---|---|---|
pending | Waiting for frontend to receive | Receive RTC event |
sent | Delivered to frontend | Display tool call UI |
executing | Currently executing | Call update_status |
completed | Execution successful | Call submit_result(success=true) |
failed | Execution failed | Call submit_result(success=false) |
timeout | Execution timed out | Automatically marked by the system |
rejected | User rejected | User clicks reject |
Idempotency Mechanism
Section titled “Idempotency Mechanism”Some Action RPCs accept client_id for deduplication or ownership validation, but behavior varies by method:
| Method | client_id | Actual Behavior |
|---|---|---|
message.send | ✅ Required | Duplicate client_id returns client_id_conflict error |
rtc.submit_result | ✅ Optional | Terminal state + same client_id → returns cached result (idempotent); terminal state + different client_id → returns existing data |
rtc.update_status | ✅ Optional | Used for ownership validation + state machine transition checks, not simple dedup |
session.close | ✅ Optional | Field accepted but no dedup performed |
turn.stop | ✅ Optional | Field accepted but no dedup performed |
session.update | ❌ No such field | — |
session.compact | ❌ No such field | Uses internal queue dedup (no duplicate compaction for same session) |
session.fork | ❌ Uses new_client_message_id | Stored as the new message’s ClientID, no fork-level dedup |
💡 Design note:
client_idserves different roles in different methods — sometimes an idempotency key, sometimes an ownership identifier, sometimes just recorded. Integrators should refer to each method’s specific behavior rather than assuming uniform idempotency semantics.
Update Model
Section titled “Update Model”Most Action RPC responses include both result and updates (except session.compact and turn.stop, whose updates are always empty):
{ "result": { "...business data..." }, "updates": [ { "id": "update-uuid", "items": [ { "entity": "message", "action": "created", "entity_id": "msg-uuid" } ], "data_list": [{ "...complete entity data..." }], "offset": 42 } ]}💡 After receiving updates: The frontend directly updates local state (IndexedDB / memory), keeping it consistent with the server. No need to send another query request — this is the “operation equals synchronization” design philosophy.
RPC Error Format
Section titled “RPC Error Format”RPC errors use a structured format different from HTTP errors:
{ "code": "session.not_found", "message": "session xxx not found", "details": "optional additional info"}| Field | Type | Description |
|---|---|---|
code | string | Machine-readable error code, e.g., session.not_found, client_id_conflict, method_not_found |
message | string | Human-readable description |
details | any | Optional additional information (only included in some errors) |
Message Ordering
Section titled “Message Ordering”The Message model contains two ordering fields:
| Field | Type | Description |
|---|---|---|
global_offset | uint32 | Global message order within a session, monotonically increasing; used as the pagination cursor for v1.message.list |
turn_offset | uint32 | Message order within a Turn |
Next Steps
Section titled “Next Steps”- Real-Time Events — Learn about the dual-channel push mechanism behind updates
- Remote Tool Calling — Deep dive into the full RTC tool call lifecycle
- Protocol Overview — Return to the protocol panorama