Skip to content
Robotadocs

@robota-sdk/agent-transport/ws

WebSocket transport for the Robota SDK. WsTransport serves a running agent session on a loopback WebSocket server, so a local client (a browser page, a desktop app, another process) can drive the session in real time: submit prompts, receive streamed output, answer permission prompts, and read history and status. The wire protocol and the session bridge come from @robota-sdk/agent-transport; this subpath adds the socket server, connection admission and lifecycle, and pulls in only ws.

Installation

npm install @robota-sdk/agent-transport ws

Requires Node.js 22.12 or later. ws is an optional peer dependency of the package, installed for this subpath only. @robota-sdk/agent-transport provides the session and message types used below.

Usage

import { WsTransport } from '@robota-sdk/agent-transport/ws';
import type { IProtocolSession } from '@robota-sdk/agent-transport';
 
declare const session: IProtocolSession; // e.g. an InteractiveSession from @robota-sdk/agent-framework
 
const transport = new WsTransport({ port: 7070 });
transport.attach(session);
await transport.start();
 
// Hand both to your client; it connects to ws://127.0.0.1:<port>/?token=<token>
console.log(transport.boundPort, transport.resolvedToken);
 
await transport.stop();

The server listens on 127.0.0.1 only. If the port is taken it tries the next one, up to maxRetries times; boundPort reports the port actually bound.

Every connection must present the token, as a ?token= query parameter or as the Sec-WebSocket-Protocol value, or it is closed before any session data is sent. Without a token option, a random token is minted per launch and exposed as resolvedToken. The Host header must be a loopback name, and a browser's Origin must be loopback, so another web page cannot reach the socket. Running without a token takes open: true with an openReason, and is discouraged: any local process could then drive the session.

Clients exchange JSON text frames typed as TClientMessage / TServerMessage from @robota-sdk/agent-transport, whose ./client entry point also provides browser-safe decoders.

Roles

A listener grants drive unless the host sets role. A read-only observer listener has its own token and the usual binder:

const observer = new WsTransport({ role: 'observe', token: observerToken, sessionBinder });

A connection may ask for less than its listener grants, never for more. A client on a driving listener asks to observe with ?role=observe beside ?token=:

ws://127.0.0.1:<port>/?token=<token>&role=observe

A request the listener cannot grant, or an unknown role, is closed (code 1008) before any session data. A client can send get-surface-role to learn its effective role. A host that needs both roles runs one listener per role, each with its own token.

WsTransport options

OptionTypeDescription
portnumberPort to bind. Default 7070.
maxRetriesnumberNext ports to try when the port is in use. Default 20.
tokenstringCredential every connection must present. Omitted: one is minted per launch.
open / openReasonboolean / stringRun without a token; openReason must say why. Rejected together with token.
allowedHosts / allowedOriginsreadonly string[]Extra Host names and browser Origins to accept beyond loopback.
role'drive' / 'observe'Most any connection may do. Default drive; a client may ask for less with ?role=observe.
driverId / surfaceTDriverId / TUsageSurfaceIdentity recorded on turns submitted through this transport, set by the host rather than the client.
sessionDirectoryISessionDirectoryLets clients list, start and switch the host's sessions.
sessionBinderISessionBinderGives each connection its own session binding, so one client's switch does not move the others.
personalUsageReporter / usageReporter / storedSessionUsageReporterreportersHost-owned usage read models, available to admitted connections.

start() before attach() throws, as does a second start() while running. stop() closes every client (forcing any that do not close within a few seconds), is safe to call more than once, and requires attach() again before the next start().

Delivery failures are the connection's problem, not the session's: if a socket is closed or a send fails, that connection is cleaned up once and the session operation that produced the event is unaffected.

Other exports

  • createWsTransport(options) — the same session bridge without a server, for a WebSocket you already own. Pass a send function; after start(), feed each inbound text frame to transport.onMessage.
  • registerChannel(descriptor) on WsTransport and the standalone PayloadChannelRegistry — carry application-defined events and binary frames on the same connection as the agent protocol. Binary frames go to channels; text frames go to the agent protocol.
  • Types: IWsTransportConfig (the options above), IWsTransport, IWsTransportOptions, TChannelSink.