Skip to main content
This is the transport layer beneath the UI: a typed client that speaks the Gateway’s HTTP RPC and WebSocket protocol, plus the TanStack DB collection factory that turns those calls into live, reconnecting data. It is framework-free on purpose.
Most UIs should not touch this. Reach for the React bindings in /reference/gateway-react, which hold one client for you and expose live hooks. Drop down to the client only for non-React hosts, custom transports, or scripts.
Everything on this page lives at the subpath smithers-orchestrator/gateway-client, not on the bare smithers-orchestrator facade. Import from the subpath or the build fails.
import {
  SmithersGatewayClient,
  SmithersGatewayConnection,
  GatewayRpcError,
  createSmithersGatewayTransport,
  gatewayBackoffDelay,
  createGatewayCollection,
  gatewayKeys,
} from "smithers-orchestrator/gateway-client";

SmithersGatewayClient

The typed client. Construct it with a base URL (and optional auth token); call a named RPC method, or open a WebSocket and subscribe to a run’s event stream.
const client = new SmithersGatewayClient({
  baseUrl: "http://127.0.0.1:7331",
  token: process.env.SMITHERS_TOKEN,
});
options
SmithersGatewayClientOptions

RPC methods

Each method is a thin typed wrapper over rpc(method, params), which POSTs to /v1/rpc/<method> and unwraps the response frame. Params and return shapes come from the method catalog; see /rpc/launch-run for one method in full.
const { runId } = await client.launchRun({ workflowId: "review", input: {} });
await client.submitApproval({ runId, nodeId: NODE_ID, decision: "approve" });
const runs = await client.listRuns();
await client.cancelRun({ runId });
run lifecycle
methods
launchRun, resumeRun, cancelRun, hijackRun, rewindRun.
human-in-the-loop
methods
submitApproval, submitSignal, listApprovals.
reads
methods
getRun, listRuns, listWorkflows, getNodeOutput, getNodeDiff.
crons
methods
cronList, cronCreate, cronDelete, cronRun.
escape hatches
methods
rpc(method, params, { signal? }) for any typed method, rpcRaw(method, params?, { signal? }) for an untyped one, and extensionRpc(namespace, key, params?) / streamExtension(namespace, key, params?) for gateway extensions.

Subscribing to events

connect() opens a SmithersGatewayConnection. The higher-level generators filter and yield frames for you, so most callers iterate one of those instead of driving the connection directly.
const ac = new AbortController();
for await (const frame of client.streamRunEvents(
  { runId: RUN_ID },
  { signal: ac.signal },
)) {
  console.log(frame.event, frame.payload);
}
streamRunEvents(params, { signal? })
AsyncGenerator<GatewayEventFrame>
Subscribes to one run and yields its run.* frames (run.event, run.gap_resync, run.heartbeat, run.error). Pass afterSeq to resume. Closes the socket when iteration ends.
streamRunEventsResilient(params, options?)
AsyncGenerator<GatewayEventFrame>
Same frames, but reconnects with backoff + jitter on a silent drop and resumes from the last observed seq. Stops on terminal completion or abort. Options: signal, backoff (GatewayBackoffOptions), healthyAfterMs, and onReconnect(event).
streamDevTools(params, { signal? })
AsyncGenerator<GatewayEventFrame>
Subscribes to a run’s DevTools snapshot stream.
connect({ subscribe?, signal? })
Promise<SmithersGatewayConnection>
Opens the WebSocket, performs the connect handshake (protocol + auth), and returns the live connection. Use this only when you need request/response over the socket or raw event frames.

SmithersGatewayConnection

A single open WebSocket. RPC over the socket via request / requestRaw, and a single in-order async stream of server event frames via events(signal?). One consumer per connection; the generators above own a connection each.
const conn = await client.connect({ subscribe: [RUN_ID] });
const sub = await conn.request("streamRunEvents", { runId: RUN_ID });
for await (const frame of conn.events()) {
  if (frame.event === "run.event") console.log(frame.payload);
}
conn.close();
request(method, params)
Promise<payload>
Typed request/response over the socket.
requestRaw(method, params?)
Promise<unknown>
Untyped request/response over the socket.
events(signal?)
AsyncGenerator<GatewayEventFrame>
In-order server event frames. Ends on close; throws on an error or invalid frame.
close()
void
Closes the socket, rejects pending requests, and ends events().
Source SmithersGatewayClient.ts · SmithersGatewayConnection.ts · Tests SmithersGatewayClient.test.ts · See also /rpc/launch-run, Gateway integration

GatewayRpcError

The error every RPC rejects with on a failed frame or HTTP error. Inspect code to branch (for example, requiredScope is set on an authorization failure).
try {
  await client.launchRun({ workflowId: "review", input: {} });
} catch (error) {
  if (error instanceof GatewayRpcError) {
    console.error(error.method, error.code, error.requiredScope);
  }
}
method
string
The RPC method that failed (or "websocket" for a malformed socket frame).
code
string
Machine-readable error code, e.g. HTTP_ERROR, INVALID_GATEWAY_RESPONSE, or the gateway’s own code from the response frame.
status
number
HTTP status when the failure came over HTTP RPC.
requiredScope
string
The scope the call needed, on an authorization failure.
refresh
string
Set when the gateway asks the client to refresh credentials.
details
unknown
Extra context attached by the gateway.
Source GatewayRpcError.ts · Tests gateway-client.test.ts

createSmithersGatewayTransport

Adapts a SmithersGatewayClient into the narrow SyncTransport that createGatewayCollection and the React provider consume. RPC is wired straight through; streamRunEvents uses the resilient generator, streamDevTools the plain one. Unknown stream scopes throw, so a typo surfaces loudly instead of stalling.
const transport = createSmithersGatewayTransport(client, {
  streamHealthyAfterMs: 1000,
});
client
SmithersGatewayClient
required
The client whose RPC and streams the transport forwards.
options
CreateSmithersGatewayTransportOptions
SyncTransport
object
{ rpc(method, params, options?), stream(scope, params, options) }. scope is "streamRunEvents" or "streamDevTools"; both require params.runId.
Source createSmithersGatewayTransport.ts · Tests sync

gatewayBackoffDelay

Exponential backoff with full jitter for one 0-based attempt. The resilient stream uses it internally; call it directly when you write your own reconnect loop.
const delayMs = gatewayBackoffDelay(attempt, { baseMs: 250, maxMs: 10_000 });
attempt
number
required
0-based attempt index. The base delay grows by factor ** attempt, capped at maxMs.
options
GatewayBackoffOptions
number
number
Milliseconds to wait, never negative.
Source gatewayBackoffDelay.ts · Tests gatewayBackoffDelay.test.ts

Collections

createGatewayCollection builds a TanStack DB CollectionConfig backed by a SyncTransport: it loads an initial RPC payload, then keeps the collection live off a stream (or by refetching on each frame). gatewayKeys produces the stable cache keys so two consumers of the same data never disagree on the key.
import { createCollection } from "@tanstack/db";

const runs = createCollection(
  createGatewayCollection<GatewayRunRow>({
    key: gatewayKeys.runs(),
    client: transport,
    getKey: (row) => row.runId,
    method: "listRuns",
  }),
);
config
GatewayCollectionConfig<TRow, TKey>
required
CollectionConfig<TRow, TKey>
object
A config to hand to TanStack DB’s createCollection. Its id is the fingerprint of key.

gatewayKeys

Typed cache-key factories for the known gateway reads and streams. Each returns a readonly SyncKey tuple.
gatewayKeys.runs();                 // ["gateway:listRuns", {}]
gatewayKeys.run(RUN_ID);            // ["gateway:getRun", { runId }]
gatewayKeys.runEvents(RUN_ID);      // ["gateway:streamRunEvents", { runId }]
gatewayKeys.nodeOutput(RUN_ID, NODE_ID);
gatewayKeys
object
Factories include workflows, runs, run, approvals, nodeOutput, nodeDiff, cronList, memoryFacts, prompts, scores, tickets, devtoolsSnapshot, runEvents, and devtools.
Source createGatewayCollection.ts · gatewayKeys.ts · Tests sync

Sync internals

The subpath also exports lower-level building blocks used to assemble the collections above: electricCollectionDefs / gatewayCollectionDefs (the registry of known collections), flattenGatewayRunNode / snapshotToGatewayRunNode / reconcileSnapshotNodes (run-tree shaping), syncKeyFingerprint / syncKeyMatches (key hashing + prefix match), syncBackoffDelay, the Electric source (createElectricCollection), and the row types (GatewayRunRow, GatewayRunNode, GatewayApprovalRow, …). They are implementation surface for custom transports and rarely needed directly. See the package source.

Source index.ts · Tests packages/gateway-client/tests · See also Gateway React API, Gateway integration, /rpc/launch-run