Most UIs shouldn’t touch this directly. Use the React bindings in
/reference/gateway-react, which hold one client
for you and expose live hooks. Drop to this client only for non-React hosts,
custom transports, or scripts.smthrs/gateway-client, not the bare
smthrs facade; import from the subpath or the build fails.
SmithersGatewayClient
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.SmithersGatewayClientOptions
RPC methods
Each method is a thin typed wrapper overrpc(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 in
full.
methods
launchRun, resumeRun, pauseRun, cancelRun, hijackRun, rewindRun.LaunchRunRequest.options.startedBy accepts { harness?, sessionId?, prompt?, detected?: true } as durable caller-provided provenance. It is not an auth
credential; use the configured bearer/token identity for access control.
methods
submitApproval, submitSignal, listApprovals.methods
getRun, listRuns, listWorkflows, getNodeOutput, getNodeDiff, getRunDiff,
whatHappened.methods
cronList, cronCreate, cronDelete, cronRun.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
below filter and yield frames for you; most callers iterate one of those
instead of driving the connection directly.
GatewayEventFrame.event is the stream frame kind. For an outer
"run.event" frame, frame.payload.event is the run lifecycle name (such as
"node.started"), and frame.payload.payload contains that lifecycle event’s
fields:
frame.event for control frames (run.heartbeat, run.gap_resync).
Only read frame.payload.event and frame.payload.payload when
frame.event === "run.event". Use frame.payload.seq, not the outer
connection frame.seq, to order run events or persist an afterSeq cursor. A
reducer must not compare the outer frame.event to lifecycle names such as
"node.started"; heartbeat frames report transport liveness and do not
represent lifecycle progress.
AsyncGenerator<GatewayEventFrame>
Subscribes to one run and yields its
run.* frames (run.event,
run.gap_resync, run.heartbeat). Pass afterSeq to resume. A run.error
frame (the gateway dropping the subscription, e.g. a backpressure disconnect)
throws a GatewayRpcError carrying the gateway’s code. Closes the socket when
iteration ends.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).AsyncGenerator<GatewayEventFrame>
Subscribes to a run’s DevTools snapshot stream.
Promise<SmithersGatewayConnection>
Opens the WebSocket, performs the
connect handshake (protocol + auth), and
returns the live connection. Use only for request/response over the socket
or raw event frames.Disposal
void
Releases everything the client owns: aborts its in-flight RPCs and streams,
closes the WebSockets
connect() opened, and marks the client closed so a
later call throws instead of opening a new socket. Idempotent, and it never
aborts a caller’s own AbortSignal.boolean
Whether
close() has run.SmithersGatewayProvider closes the client it built from
options when the options rotate one in and on unmount, and never closes a
client you passed as the client prop, so a client shared across trees stays
yours to dispose.
SmithersGatewayConnection
A single open WebSocket: RPC viarequest / requestRaw, plus a single
in-order async stream of server event frames via events(signal?). One
consumer per connection; the generators above each own one.
Promise<payload>
Typed request/response over the socket.
Promise<unknown>
Untyped request/response over the socket.
AsyncGenerator<GatewayEventFrame>
In-order server event frames. Ends on close; throws on an error or invalid
frame.
void
Closes the socket, rejects pending requests, and ends
events().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. Inspectcode
to branch (for example, requiredScope is set on an authorization failure).
string
The RPC method that failed (or
"websocket" for a malformed socket frame).string
Machine-readable error code, e.g.
HTTP_ERROR, INVALID_GATEWAY_RESPONSE, or
the gateway’s own code from the response frame.number
HTTP status when the failure came over HTTP RPC.
string
The scope the call needed, on an authorization failure.
string
Set when the gateway asks the client to refresh credentials.
unknown
Extra context attached by the gateway.
GatewayRpcError.ts · Tests gateway-client.test.ts
isGatewayUnavailableError
True when nothing that speaks the gateway protocol answered the URL: a 2xx response whose body isn’t an{ ok, ... } envelope, or a fetch-level
rejection (connection refused, DNS failure) because nothing is listening. The
usual cause is a host app’s SPA fallback serving index.html for /v1/api/*
because no gateway is wired. The data client throws these as code
GATEWAY_UNAVAILABLE, and the QueryCollection layer already degrades them
quietly: collections settle on the last rows a real gateway served (empty
before the first success), one session-level console.info notice replaces
the per-collection [QueryCollection] error spam, and the SSE change stream
refuses non-text/event-stream answers so the connection status parks
offline instead of flapping online. Recovery is automatic: the stream’s
reconnect backoff keeps probing, and the reset emitted on a real reconnect
refetches every collection. Branch on it when calling api.* directly for the
same behavior.
isGatewayUnavailableError.ts · Tests gatewayUnavailableQuietDegrade.test.ts
gatewayBackoffDelay
Exponential backoff with full jitter for one 0-based attempt. The resilient stream uses it internally; call it directly for your own reconnect loop.number
required
0-based attempt index. The base delay grows by
factor ** attempt, capped at
maxMs.GatewayBackoffOptions
number
Milliseconds to wait, never negative.
gatewayBackoffDelay.ts · Tests gatewayBackoffDelay.test.ts
createSmithersCollections
Builds the TanStack DB collection registry. Local mode uses the Gateway REST domain API plus/v1/api/stream SSE invalidation. Multiplayer mode uses
Electric shapes from electricBaseUrl for reads and keeps the same domain API
write path.
WorkspaceMode
required
{ kind: "local", apiBaseUrl, token? } or { kind: "multiplayer", apiBaseUrl, electricBaseUrl, workspaceId, token? }.QueryClient
required
The TanStack Query client used by QueryCollection and invalidation.
object
Registry methods include
runs, run, runTree, runEvents, nodes,
approvals, workflows, docs, prompts, scores, tickets,
memoryFacts, and crons, plus connect, invalidate, and close.createSmithersCollections.ts · Tests smithers-collections
createSmithersDataClient
Creates the domain API client that collection mutation handlers call. Reads and writes target/v1/api/*; stream.subscribe opens /v1/api/stream and emits
change, reset, and heartbeat invalidation events.
WorkspaceMode
required
Workspace mode and auth token.
typeof fetch
Fetch override for tests or custom runtimes.
typeof EventSource
SSE implementation override. When omitted, the client falls back to streaming
fetch.HeadersInit
Extra headers merged into every
/v1/api/* request and the change stream, so
an API-key or proxy header authorizes the collection API and SSE exactly as it
does RPC. The content type and authorization: Bearer <mode.token> win over
conflicting entries. SmithersGatewayProvider forwards the gateway client’s
headers here automatically.object
{ mode, api, stream, close }. The api object covers run lifecycle,
approvals, signals, crons, docs, prompts, memory facts, scores, tickets, node
output, node diffs, and schema signatures.Collection keys and row types
smithersCollectionKeys holds the TanStack Query keys used by the registry.
The same package exports the row types: GatewayRunRow,
GatewayRunSummaryRow, GatewayRunEventRow, GatewayRunNode,
GatewayApprovalRow, GatewayWorkflowRow, GatewayCronRow,
GatewayMemoryFactRow, GatewayScoreRow, GatewayTicketRow, and
GatewayPromptRow.
Run-tree helpers stay public for custom inspectors: flattenGatewayRunNode,
snapshotToGatewayRunNode, and reconcileSnapshotNodes.
Source
index.ts · Tests packages/gateway-client/tests · See also Gateway React API, Gateway integration, /rpc/launch-run