Skip to content

WebSocket events

The WebSocket endpoint streams agent execution in real time: tokens as the model generates them, tool calls as they fire, results as they come back, and turn boundaries. The dashboard chat is essentially a thin shell over this stream.

wss://app.vonz.io/v1/stream
Sec-WebSocket-Protocol: Bearer.<your-token>

Or pass the token as ?token=rc_… query parameter when subprotocols aren’t an option (in-browser clients).

Auth is the same as the REST API: an rc_… token or a Better Auth session cookie.

Messages are JSON, one event per frame. All messages have a type field. Most have a session_id (which workspace they belong to).

Same shape as POST /v1/tasks. Submit and start streaming back over this socket.

{
"type": "task.submit",
"mode": "session",
"profile_id": "prof_…",
"session_id": "sess_…",
"prompt": "What's the weather?"
}
{ "type": "task.cancel", "task_id": "task_…" }

Create a new session (without submitting a task yet). Useful for surfaces that want to register a session id ahead of the user’s first message.

{ "type": "session.start", "profile_id": "prof_…" }

Server replies with session.ready carrying the new session_id.

Re-attach to an existing session and request a transcript replay.

{ "type": "session.resume", "session_id": "sess_…" }

Server first emits session.replay_start, then a sequence of historical events, then session.replay_end. After that, live events flow.

Send a new user message into an existing session. Same as task.submit with mode=session, but defaults the profile from the session.

{ "type": "session.turn", "session_id": "sess_…", "prompt": "Follow-up question" }

Reply to an AskUserQuestion event the agent emitted.

{ "type": "task.answer", "task_id": "task_…", "answer": "Option B" }

A session was created (in response to session.start or task.submit with no session_id).

{
"type": "session.ready",
"session_id": "sess_…",
"container_id": "ctr_…",
"container_name": "agent-abc123",
"profile_id": "prof_…"
}

Begins a transcript replay (response to session.resume).

{ "type": "session.replay_start", "session_id": "sess_…" }

End of replay; live events follow.

A single chunk of model-generated text. Emitted as the agent streams its assistant response. Multiple task:token events make up one assistant message.

{ "type": "task:token", "session_id": "sess_…", "text": "Hello " }

The agent called a tool. input contains the tool’s arguments; the result comes later as task:tool_result.

{
"type": "task:tool_use",
"session_id": "sess_…",
"tool": "Bash",
"input": { "command": "ls /workspace" }
}

For AskUserQuestion, the input contains a questions array — the dashboard renders inline buttons.

{
"type": "task:tool_result",
"session_id": "sess_…",
"tool": "Bash",
"output": "file1\nfile2\n"
}

A flattened variant of an AskUserQuestion tool_use, surfaced specifically so chat surfaces (dashboard, Slack, Telegram) can render interactive choices uniformly.

{
"type": "task:ask_user",
"session_id": "sess_…",
"task_id": "task_…",
"questions": [
{ "question": "Pick one", "options": [{ "label": "Yes" }, { "label": "No" }] }
]
}

Mid-chain marker: the orchestrator is auto-continuing into a new chain (multi-chain playbooks).

{ "type": "turn.continuing", "continuation": 2, "max_continuations": 20 }

End of a single agent turn. result_text is the server-polished version of the assistant’s final text — image URLs are signed _pvt links, markdown is normalized.

{
"type": "turn.done",
"session_id": "sess_…",
"result_text": "Here's the summary…",
"input_tokens": 1234,
"output_tokens": 567,
"cost_usd": 0.0123
}

If you’ve been streaming tokens for this turn, replace the accumulated text with result_text — it has the signed URLs needed for in-browser image rendering.

End of a task (a single task.submit lifecycle). For session-mode tasks, fires alongside turn.done.

{ "type": "task:done", "task_id": "task_…", "session_id": "sess_…", "status": "completed" }
{ "type": "task:failed", "task_id": "task_…", "error": "Container exited 1" }

(Debug-tier.) The full system prompt the agent received this turn. Surfaced for dashboard “view system prompt” debug affordances. Only emitted when the user has a privileged role.

  • Every task:token for a turn arrives before that turn’s turn.done.
  • Every task:tool_use is followed by exactly one matching task:tool_result (or task:failed if the tool errored).
  • A turn can interleave token events and tool calls. The dashboard’s chat layout uses turn-grouping to render them coherently under one agent header.
  • session.replay_* events frame any transcript playback. Live events come strictly after session.replay_end.

Either side can close cleanly. The server preserves session state on disconnect — reconnect + session.resume brings the user back to the same conversation with full replay.

Frames with type: "error" carry a code (see Errors) and a human-readable message.

{ "type": "error", "code": "UNAUTHORIZED", "message": "Token invalid" }

A WebSocket close with code 4001 means “authentication failed; reconnect with a valid token.”