Supr WebSocket Protocol
This page documents the WebSocket protocol used by SuprClaw web chat (supr channel).
Source of truth in code:
pkg/channels/supr/pkg/bus/types.gopkg/agent/activity_events.goweb/backend/api/supr.goweb/frontend/src/features/chat/protocol.ts
Note: some frontend symbols still use legacy pico naming, but the actual protocol/channel is supr.
Endpoint and handshake
- WebSocket endpoint:
GET /supr/ws - Query:
session_id(optional; server generates one if omitted)
Example:
ws://<host>:<port>/supr/ws?session_id=sess-123
Authentication (any one succeeds):
Authorization: Bearer <token>Sec-WebSocket-Protocol: token.<token>?token=<token>only whenchannels.supr.allow_token_query=true
Connection behavior
- One active connection per
session_id(new connection replaces old one). - First frame sent by server is
agent.list. - Server sends WS ping frames periodically.
- Protocol-level
ping/pongJSON messages also exist.
Wire envelopes
1) Typed frame (SuprMessage)
{
"type": "string",
"id": "optional string",
"session_id": "optional string",
"timestamp": 1710000000000,
"payload": {}
}
2) Canonical activity envelope (ActivityEventEnvelope)
{
"v": "1.0",
"event_id": "evt_xxx",
"event_type": "run.started",
"timestamp": "2026-03-30T10:12:41.203Z",
"sequence": 1,
"session_id": "sess-123",
"run_id": "run_xxx",
"parent_run_id": null,
"agent_id": "optional",
"trace_id": "optional",
"span_id": "optional",
"idempotency_key": "run_xxx_1",
"replay": false,
"data": {}
}
agent_id is the actual responding agent. For canonical activity events,
data.agent_id mirrors top-level agent_id and both must match.
Client -> server messages
message.send
Required:
payload.content: string(non-empty)
Optional:
payload.agent_id: stringpayload.model: stringpayload.reasoning: "off" | "low" | "medium" | "high" | "xhigh" | "adaptive"
Example:
{
"type": "message.send",
"id": "msg-1",
"payload": {
"content": "Summarize this log",
"agent_id": "main",
"reasoning": "high"
}
}
media.send
Two modes:
- Scalar fields (
dataorurl) attachmentsarray
Attachment item fields:
data?: string(base64)url?: stringfilename?: stringcontent_type?: stringcaption?: string
Rules:
- Each item must include
dataorurl. - Max decoded media size per item: 25 MB.
- Optional
agent_id,model,reasoningare also supported.
ping
Server replies with typed pong and mirrors id when present.
run.stop
Requests cancellation of the active run for the session.
Payload fields:
run_id?: stringreason?: string
Validation:
- If there is no active run: typed
errorwithcode: "no_active_run". - If
run_idis provided and does not match the active run: typederrorwithcode: "run_mismatch". - On success, no typed ack is sent; cancellation outcome is reported via canonical events.
Example:
{
"type": "run.stop",
"payload": {
"run_id": "run_abc123",
"reason": "Stopped by user."
}
}
Server -> client messages
Typed frames
agent.list(sent on connect)pongerrormedia.create
agent.list example
{
"type": "agent.list",
"timestamp": 1710000000000,
"payload": {
"agents": [{ "id": "main", "name": "Main Agent" }],
"default": "main"
}
}
error example
{
"type": "error",
"timestamp": 1710000000000,
"payload": {
"code": "invalid_reasoning",
"message": "invalid reasoning \"ultra\". Allowed: off|low|medium|high|xhigh|adaptive"
}
}
Common protocol error codes:
invalid_messageunknown_typeempty_contentinvalid_reasoningno_active_runrun_mismatchmedia_store_unavailableinvalid_media_datamedia_write_failed
media.create example
{
"type": "media.create",
"session_id": "sess-123",
"timestamp": 1710000000000,
"payload": {
"type": "image",
"data": "<base64>",
"filename": "diagram.png",
"content_type": "image/png",
"caption": "optional"
}
}
Canonical activity events
Observed event_type values:
run.startedrun.completedrun.failedmessage.startedmessage.completedstep.startedstep.updatedstep.completedstep.failedreasoning.summarytool.calledtool.progresstool.completedtool.failederror.raised
Important data keys used by the frontend timeline:
- message:
message_id,text,format,agent_id - step:
step_id,kind,title,headline,summary,message - tool:
tool_call_id,tool_name,display_name,arg_preview,result_preview - error:
scope,code,message,retryable,agent_id
Optional routing/observability keys may be included:
model_usedresolved_agent_idroute_matched_by
Cancellation outcome for accepted run.stop:
message.completedwith stop text (default:"Stopped by user.")run.failedwitherror_code: "RUN_CANCELLED"
Ordering and dedupe
sequenceis monotonic perrun_id.event_idis unique and used for dedupe.idempotency_keyis emitted as<run_id>_<sequence>.- Frontend sorts events by
sequence.
Config (channels.supr)
enabledtokenallow_token_queryallow_originsping_interval(seconds)read_timeout(seconds)write_timeout(seconds)max_connectionsallow_from
Defaults:
ping_interval = 30read_timeout = 60write_timeout = 10max_connections = 100
Related HTTP endpoints
GET /api/supr/tokenPOST /api/supr/tokenPOST /api/supr/setup
Full internal spec
For the repository-level full spec (same protocol, expanded detail), see: