WebSocket Transport
Drive an InteractiveSession over a WebSocket connection with @robota-sdk/agent-transport/ws. The
client sends JSON messages (submit a prompt, run a command, answer a permission prompt) and receives
the session's events as they happen.
Basic setup
createWsTransport() connects one session to one socket you already have. It adds no
authentication: whoever can open a socket on your server can drive the session, so bind the server
to loopback or authenticate the upgrade yourself.
import { InteractiveSession } from '@robota-sdk/agent-framework';
import { createNodeFrameworkHost } from '@robota-sdk/agent-framework/node';
import { createDefaultTools } from '@robota-sdk/agent-tools/defaults';
import { AnthropicProvider } from '@robota-sdk/agent-provider/anthropic';
import { createWsTransport } from '@robota-sdk/agent-transport/ws';
import { WebSocketServer } from 'ws';
const wss = new WebSocketServer({ host: '127.0.0.1', port: 8080 });
const provider = new AnthropicProvider({ apiKey: process.env.ANTHROPIC_API_KEY });
wss.on('connection', async (ws) => {
const session = new InteractiveSession({
cwd: process.cwd(),
host: createNodeFrameworkHost({ createDefaultTools }),
provider,
});
const transport = createWsTransport({
send: (msg) => ws.send(JSON.stringify(msg)),
// Called once if a message cannot be delivered; the transport stops forwarding after it.
onDeliveryError: () => ws.close(1011, 'Outbound delivery failed'),
});
session.attachTransport(transport);
await transport.start();
ws.on('message', (data) => transport.onMessage?.(String(data)));
ws.on('close', () => {
void transport.stop();
void session.shutdown();
});
});transport.onMessage is set by start() and cleared by stop() or a delivery failure, hence the
?..
A connection drives the session unless you grant less: pass role: 'observe' to createWsTransport()
for a read-only connection that follows the session's conversation and state but cannot submit, answer
prompts or control the session.
Message protocol
The main messages are below. The full set is the TClientMessage and TServerMessage types exported
by @robota-sdk/agent-transport.
Client → server
{ "type": "submit", "prompt": "Fix the bug" }
{ "type": "command", "name": "compact", "args": "focus on the API" }
{ "type": "abort" }
{ "type": "cancel-queue" }
{ "type": "get-messages" }
{ "type": "get-context" }
{ "type": "permission-response", "id": "<request id>", "result": true }command runs one of the session's commands. An InteractiveSession has only the commands passed
in its commandModules option; the CLI composes its slash commands this way.
Server → client
{ "type": "text_delta", "delta": "Here is..." }
{ "type": "tool_start", "state": { "toolName": "Read", "isRunning": true } }
{ "type": "complete", "result": { "response": "Done." } }
{ "type": "command_result", "name": "compact", "message": "Context compacted.", "success": true }
{ "type": "permission_request", "event": { "id": "<request id>", "toolName": "Bash", "toolArgs": { "command": "pnpm test" } } }A permission_request waits for a permission-response with the same id. result is true (allow
once), false (deny), "allow-session" or "allow-project".
Advanced: direct handler
createWsTransport() is a thin wrapper. To control the connection yourself, pair the carrier-neutral
session handler with an outbound delivery boundary:
import {
createOutboundDelivery,
createSessionMessageHandler,
type IProtocolSession,
} from '@robota-sdk/agent-transport';
import type { WebSocketServer } from 'ws';
declare const wss: WebSocketServer;
declare const session: IProtocolSession; // for example an InteractiveSession
wss.on('connection', (ws) => {
const deliver = createOutboundDelivery(
(message) => ws.send(JSON.stringify(message)),
() => ws.close(1011, 'Outbound delivery failed'),
);
const { onMessage, cleanup } = createSessionMessageHandler({ session, deliver });
ws.on('message', (data) => onMessage(String(data)));
ws.on('close', cleanup);
});For a ready-made server with token admission, use the package's WsTransport class;
examples/capabilities/multi-surface-deploy
serves one session over WebSocket and HTTP together.
examples/websocket-chat is a complete chat server that
defines its own small message protocol instead.