Skip to main content

WebSocket Streaming

ClawDesk supports real-time bidirectional communication via WebSocket. This is used by the Tauri desktop app and can be consumed by any WebSocket client.

Connection

Endpoint

ws://localhost:1420/ws

Connection Example

const ws = new WebSocket("ws://localhost:1420/ws");

ws.onopen = () => {
console.log("Connected to ClawDesk");
};

ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
handleMessage(msg);
};

ws.onclose = (event) => {
console.log(`Disconnected: ${event.code} ${event.reason}`);
};

ws.onerror = (error) => {
console.error("WebSocket error:", error);
};

Connection Parameters

ParameterTypeDescription
tokenquery stringAuthentication token (if auth enabled)
session_idquery stringResume an existing session
ws://localhost:1420/ws?token=abc123&session_id=sess_def456

Message Protocol

All WebSocket messages are JSON objects with a type field:

{
"type": "message_type",
"data": { ... }
}

Client → Server Messages

chat.message

Send a chat message to the agent pipeline.

{
"type": "chat.message",
"data": {
"session_id": "sess_abc123",
"content": "What is Rust's ownership model?",
"metadata": {}
}
}

chat.cancel

Cancel a streaming response in progress.

{
"type": "chat.cancel",
"data": {
"session_id": "sess_abc123",
"message_id": "msg_def456"
}
}

session.create

Create a new session.

{
"type": "session.create",
"data": {
"channel_id": "desktop",
"model": "claude-sonnet-4-20250514"
}
}

session.switch

Switch to a different session.

{
"type": "session.switch",
"data": {
"session_id": "sess_abc123"
}
}

ping

Keep-alive ping.

{
"type": "ping",
"data": {
"timestamp": 1708167000
}
}

Server → Client Messages

chat.stream.start

Indicates the start of a streaming response.

{
"type": "chat.stream.start",
"data": {
"message_id": "msg_def456",
"session_id": "sess_abc123",
"model": "claude-sonnet-4-20250514"
}
}

chat.stream.delta

A chunk of streaming content.

{
"type": "chat.stream.delta",
"data": {
"message_id": "msg_def456",
"content": "Rust's ownership model ",
"index": 3
}
}

chat.stream.end

The streaming response is complete.

{
"type": "chat.stream.end",
"data": {
"message_id": "msg_def456",
"finish_reason": "stop",
"usage": {
"input_tokens": 42,
"output_tokens": 256
}
}
}

chat.response

A complete (non-streamed) response.

{
"type": "chat.response",
"data": {
"message_id": "msg_def456",
"session_id": "sess_abc123",
"role": "assistant",
"content": "Full response content here...",
"model": "claude-sonnet-4-20250514",
"usage": {
"input_tokens": 42,
"output_tokens": 128
}
}
}

session.created

Confirmation that a session was created.

{
"type": "session.created",
"data": {
"session_id": "sess_new789",
"channel_id": "desktop",
"created_at": "2026-02-17T10:30:00Z"
}
}

error

An error occurred processing a request.

{
"type": "error",
"data": {
"code": "provider_error",
"message": "All providers failed for this request",
"request_type": "chat.message",
"details": {
"attempts": [
{ "provider": "anthropic", "error": "rate_limited" },
{ "provider": "openai", "error": "timeout" }
]
}
}
}

pong

Response to a client ping.

{
"type": "pong",
"data": {
"timestamp": 1708167000,
"server_time": 1708167001
}
}

Error Codes

CodeDescription
invalid_messageMalformed WebSocket message
session_not_foundReferenced session doesn't exist
provider_errorAI provider returned an error
rate_limitedRequest rate limit exceeded
cancelledResponse was cancelled by client
internal_errorInternal server error

Connection Lifecycle

Client                          Server
│ │
│──── WebSocket Upgrade ───────▶│
│◀──── 101 Switching ──────────│
│ │
│──── session.create ──────────▶│
│◀──── session.created ────────│
│ │
│──── chat.message ────────────▶│
│◀──── chat.stream.start ──────│
│◀──── chat.stream.delta ──────│
│◀──── chat.stream.delta ──────│
│◀──── chat.stream.end ────────│
│ │
│──── ping ────────────────────▶│
│◀──── pong ───────────────────│
│ │
│──── close ───────────────────▶│
│◀──── close ──────────────────│

Reconnection

Clients should implement exponential backoff for reconnection:

let reconnectDelay = 1000;

function connect() {
const ws = new WebSocket("ws://localhost:1420/ws");

ws.onopen = () => {
reconnectDelay = 1000; // Reset on success
};

ws.onclose = () => {
setTimeout(connect, reconnectDelay);
reconnectDelay = Math.min(reconnectDelay * 2, 30000);
};
}
info

The WebSocket connection supports automatic session resumption. Pass session_id as a query parameter to resume where you left off.