Skip to content

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.

ComparisonAction RPCQuery RPC
Operation TypeCreate / Modify / DeleteRead-only queries
Response includes updates✅ Yes❌ No
Idempotency support✅ client_id
Typical scenariosSend message, close sessionLoad message list

The 16 methods are organized across 4 business domains:

DomainActionQuery
Sessionv1.session.close · v1.session.update · v1.session.fork · v1.session.compactv1.session.list · v1.session.get
Messagev1.message.sendv1.message.list · v1.message.get
Turnv1.turn.stopv1.turn.list · v1.turn.get
RTCv1.rtc.update_status · v1.rtc.submit_resultv1.rtc.list · v1.rtc.get

A session is a container for user-AI conversations. Each session contains multiple messages and turns.

MethodTypeFunctionKey Parameters
v1.session.list🔍 QueryGet session listcursor (pagination), limit (default 20)
v1.session.get🔍 QueryGet a single sessionsession_id
v1.session.close⚡ ActionClose a sessionsession_id
v1.session.update⚡ ActionUpdate session title / soft deletesession_id, title, deleted_at
v1.session.fork⚡ ActionFork 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⚡ ActionManually trigger context compression (no updates returned)session_id, custom_instruction

💡 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.


Messages are the basic communication units within a session, supporting multiple content types.

MethodTypeFunctionKey Parameters
v1.message.send⚡ ActionSend 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🔍 QueryGet message listsession_id, cursor (global_offset, uint32), limit (default 50)
v1.message.get🔍 QueryGet a single messagemessage_id

💡 Auto-creation: When calling v1.message.send, if server_session_id is 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_id in responses from v1.message.send and v1.session.fork is an empty string — the Turn is created asynchronously by the background turn-agent and is not returned synchronously with the request.

TypeDescriptionData Structure
markdownMarkdown textString
textPlain textString
thinkingAI reasoning processString
summaryContext summarySummaryItem[]
toolcall_inputTool call requestToolCall object
toolcall_outputTool call resultToolCall object

A Turn represents a complete AI reasoning round — from the user sending a message to the AI completing its response.

MethodTypeFunctionKey Parameters
v1.turn.stop⚡ ActionStop the current Turn (no updates returned)session_id
v1.turn.list🔍 QueryGet Turn listsession_id, cursor, limit (default 50)
v1.turn.get🔍 QueryGet a single Turnturn_id
StateDescription
pendingWaiting to start
runningAI is reasoning
completedReasoning completed
failedReasoning failed
cancelledUser explicitly cancelled
interruptedInterrupted by the system
mergedMerged with another Turn

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.

MethodTypeFunctionKey Parameters
v1.rtc.update_status⚡ ActionUpdate RTC execution statusrtc_id, status, client_id
v1.rtc.submit_result⚡ ActionSubmit tool execution resultrtc_id, success, result / error, client_id
v1.rtc.list🔍 QueryGet RTC listsession_id, cursor, limit (default 50)
v1.rtc.get🔍 QueryGet a single RTCrtc_id
StateDescriptionFrontend Action
pendingWaiting for frontend to receiveReceive RTC event
sentDelivered to frontendDisplay tool call UI
executingCurrently executingCall update_status
completedExecution successfulCall submit_result(success=true)
failedExecution failedCall submit_result(success=false)
timeoutExecution timed outAutomatically marked by the system
rejectedUser rejectedUser clicks reject

Some Action RPCs accept client_id for deduplication or ownership validation, but behavior varies by method:

Methodclient_idActual Behavior
message.send✅ RequiredDuplicate client_id returns client_id_conflict error
rtc.submit_result✅ OptionalTerminal state + same client_id → returns cached result (idempotent); terminal state + different client_id → returns existing data
rtc.update_status✅ OptionalUsed for ownership validation + state machine transition checks, not simple dedup
session.close✅ OptionalField accepted but no dedup performed
turn.stop✅ OptionalField accepted but no dedup performed
session.update❌ No such field
session.compact❌ No such fieldUses internal queue dedup (no duplicate compaction for same session)
session.fork❌ Uses new_client_message_idStored as the new message’s ClientID, no fork-level dedup

💡 Design note: client_id serves 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.


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 errors use a structured format different from HTTP errors:

{
"code": "session.not_found",
"message": "session xxx not found",
"details": "optional additional info"
}
FieldTypeDescription
codestringMachine-readable error code, e.g., session.not_found, client_id_conflict, method_not_found
messagestringHuman-readable description
detailsanyOptional additional information (only included in some errors)

The Message model contains two ordering fields:

FieldTypeDescription
global_offsetuint32Global message order within a session, monotonically increasing; used as the pagination cursor for v1.message.list
turn_offsetuint32Message order within a Turn