Skip to main content
The Smithers UI surface is React. This package is the SDK that backs it: a provider that holds one SmithersGatewayClient, a set of live data hooks that read a run (or runs, approvals, crons, scores, …) over TanStack DB collections, and action hooks that launch, approve, and cancel. Most hooks return the same GatewayAsyncState shape, so once you know one you know them all.
Everything on this page lives at the subpath smithers-orchestrator/gateway-react, not on the bare smithers-orchestrator facade. Import from the subpath or the build fails.
import {
  SmithersGatewayProvider,
  useGatewayRun,
  useGatewayActions,
} from "smithers-orchestrator/gateway-react";

function App() {
  return (
    <SmithersGatewayProvider options={{ baseUrl: "/" }}>
      <Run runId={RUN_ID} />
    </SmithersGatewayProvider>
  );
}

function Run({ runId }: { runId: string }) {
  const { data, loading, error } = useGatewayRun(runId);
  const { launchRun, submitApproval } = useGatewayActions();
  if (loading) return <p>loading…</p>;
  if (error) return <p>{error.message}</p>;
  return <pre>{String(data?.status ?? "")}</pre>;
}
For a UI bundled and served by the gateway itself, skip the JSX provider and mount with createGatewayReactRoot instead. It wires both contexts (on-demand hooks and the live collections) in one call.

GatewayAsyncState

The return shape shared by the live data hooks. data is undefined until the first payload lands; loading is true only while there is no data yet and a fetch is in flight; refetch() forces a re-pull.
type GatewayAsyncState<T> = {
  data: T | undefined;
  error: Error | undefined;
  loading: boolean;
  refetch: () => Promise<void>;
};
GatewayAsyncState<T>
object
Source GatewayAsyncState.ts · See also Custom workflow UI

Provider & root

SmithersGatewayProvider

Holds one SmithersGatewayClient in context for every hook below. Pass a ready client, or options and it constructs one. The client is memoized on baseUrl/token so an inline options literal does not trigger a reconnect storm.
function SmithersGatewayProvider(props: {
  client?: SmithersGatewayClient;
  options?: SmithersGatewayClientOptions;
  children?: ReactNode;
}): ReactElement;
client
SmithersGatewayClient
A pre-built client. Takes precedence over options. See the Gateway Client reference.
options
SmithersGatewayClientOptions
Client config (baseUrl, token, …) used to construct a client when none is passed.
children
ReactNode
The bare provider mounts only the on-demand context, which feeds useGatewayActions, useGatewayNodeOutput, and useGatewayRpc. The live collection hooks (useGatewayRun, useGatewayRuns, …) also need a SyncProvider. Use createGatewayReactRoot to mount both at once.
Source SmithersGatewayProvider.ts · Tests SmithersGatewayProvider.test.ts · See also SyncProvider

createGatewayReactRoot

Mount a full custom UI in one call. Builds a client, a GatewayCollections registry, and renders your element inside both the gateway provider and a SyncProvider, so every hook on this page works. This is what a gateway-served ui entry uses.
function createGatewayReactRoot(
  element: ReactElement,
  options?: SmithersGatewayClientOptions & { rootId?: string },
): SmithersGatewayClient;
element
ReactElement
required
The app root to render.
options
SmithersGatewayClientOptions & { rootId?: string }
Client options plus rootId (the DOM element id to mount into; default "root"). Throws if the element is not found.
SmithersGatewayClient
object
The client it created, for imperative use outside React.
createGatewayReactRoot(<App />, { baseUrl: "/", rootId: "root" });
Source createGatewayReactRoot.ts · See also Custom UI

useSmithersGateway

Read the raw SmithersGatewayClient from context. Throws if called outside a provider. The escape hatch for client methods the hooks do not wrap.
function useSmithersGateway(): SmithersGatewayClient;
Source useSmithersGateway.ts · See also Gateway Client

useGatewayConnectionStatus

The link’s connection lifecycle, derived from real transport traffic: RPC resolves and stream frames mark it online, transport errors mark it offline, auth failures mark it unauthorized.
function useGatewayConnectionStatus(): {
  status: "idle" | "connecting" | "online" | "offline" | "unauthorized";
  isOnline: boolean;
  reconnectingSince?: number;
};
status
"idle" | "connecting" | "online" | "offline" | "unauthorized"
Current connection state.
isOnline
boolean
Shorthand for status === "online".
reconnectingSince
number
Epoch ms of the first failure in the current offline streak, when offline.
Source useGatewayConnectionStatus.ts · Tests sync.test.ts

Data hooks

Each hook reads one gateway resource as a live TanStack DB collection. They share the GatewayAsyncState return shape unless noted, and may be called unconditionally: pass undefined (or an empty runId) and the hook resolves to an empty, stable state.

useGatewayRun

Live single-run record. Seeds from getRun, then each lifecycle frame upserts the row without a whole-tree refetch.
function useGatewayRun(
  runId: string | undefined,
): GatewayAsyncState<GatewayRunRow>;
runId
string | undefined
required
The run to read. undefined yields an empty state.
GatewayAsyncState<GatewayRunRow>
object
See GatewayAsyncState. data is the run record.
Source useGatewayRun.ts · Tests gateway-react.test.ts

useGatewayRunEvents

Live, bounded run-event buffer (resilient stream with afterSeq resume). Heartbeats are surfaced separately and never enter events; the array is capped to maxEvents, most-recent wins. Returns its own shape, not GatewayAsyncState.
function useGatewayRunEvents(
  runId: string | undefined,
  options?: { afterSeq?: number; maxEvents?: number },
): {
  events: GatewayEventFrame[];
  lastHeartbeat: GatewayEventFrame | undefined;
  error: Error | undefined;
  streaming: boolean;
};
runId
string | undefined
required
options.afterSeq
number
Drop events at or before this sequence number.
options.maxEvents
number
default:"1000"
Cap on the retained event buffer.
events
GatewayEventFrame[]
Ordered, capped run events (heartbeats excluded).
lastHeartbeat
GatewayEventFrame | undefined
The most recent heartbeat frame, surfaced apart from events.
error
Error | undefined
Set when the stream fails (offline / unauthorized).
streaming
boolean
True while the stream is live.
Source useGatewayRunEvents.ts · See also Event types

useGatewayRuns

Live run list. Seeds from listRuns, re-pulls on invalidate.
function useGatewayRuns(
  params?: ListRunsRequest,
): GatewayAsyncState<GatewayRunSummaryRow[]>;
params
ListRunsRequest
Optional filters (status, workflow, paging). Defaults to all runs.
GatewayAsyncState<GatewayRunSummaryRow[]>
object
See GatewayAsyncState. data is the run summaries.
Source useGatewayRuns.ts · Tests gateway-react.test.ts

useGatewayWorkflows

Live registered-workflow list (listWorkflows).
function useGatewayWorkflows(
  params?: ListWorkflowsRequest,
): GatewayAsyncState<ListWorkflowsResponse>;
params
ListWorkflowsRequest
Optional filter.
GatewayAsyncState<ListWorkflowsResponse>
object
Source useGatewayWorkflows.ts

useGatewayApprovals

Live pending-approval list (listApprovals). Re-pulls when a run reaches waiting-approval or after a submitApproval.
function useGatewayApprovals(
  params?: ListApprovalsRequest,
): GatewayAsyncState<ListApprovalsResponse>;
params
ListApprovalsRequest
Optional filters (e.g. by run).
GatewayAsyncState<ListApprovalsResponse>
object
Source useGatewayApprovals.ts · See also useGatewayActions

useGatewayCrons

Live cron-schedule list (cronList). Includes enabled and disabled rows; re-pulls after a cronCreate / cronDelete / cronRun.
function useGatewayCrons(
  params?: CronListRequest,
): GatewayAsyncState<GatewayCronRow[]>;
params
CronListRequest
GatewayAsyncState<GatewayCronRow[]>
object
Source useGatewayCrons.ts

useGatewayMemoryFacts

Live cross-run memory facts (listMemoryFacts). Pass a namespace to scope; omit it for every namespace. Read-only on the wire, so query-only.
function useGatewayMemoryFacts(
  namespace?: string,
): GatewayAsyncState<GatewayMemoryFactRow[]>;
namespace
string
Optional namespace filter.
GatewayAsyncState<GatewayMemoryFactRow[]>
object
See GatewayAsyncState. refetch works; there is no write RPC.
Source useGatewayMemoryFacts.ts

useGatewayNodeOutput

On-demand output of one node (getNodeOutput). Built on useGatewayRpc; disabled until both ids are set.
function useGatewayNodeOutput(params: {
  runId: string | undefined;
  nodeId: string | undefined;
  iteration?: number;
}): GatewayAsyncState<GatewayRpcPayload<"getNodeOutput">>;
params.runId
string | undefined
required
params.nodeId
string | undefined
required
params.iteration
number
default:"0"
Loop iteration to read.
GatewayAsyncState<GetNodeOutput>
object
Source useGatewayNodeOutput.ts

useGatewayScores

Live scorer/eval results for one run (listScores). Pass nodeId to scope to one node. An empty runId resolves to a stable empty collection. Read-only, so query-only.
function useGatewayScores(
  runId: string,
  nodeId?: string,
): GatewayAsyncState<GatewayScoreRow[]>;
runId
string
required
The run to score. Empty string yields an empty state.
nodeId
string
Optional node scope.
GatewayAsyncState<GatewayScoreRow[]>
object
Source useGatewayScores.ts

useGatewayTickets

Live work docs (tickets, plans, specs, proposals) via listTickets. Tombstones are filtered server-side, so every row is renderable. Pass a kind to scope.
function useGatewayTickets(
  params?: ListTicketsRequest,
): GatewayAsyncState<GatewayTicketRow[]>;
params
ListTicketsRequest
Optional kind and other filters.
GatewayAsyncState<GatewayTicketRow[]>
object
Source useGatewayTickets.ts

useGatewayPrompts

Live registered-prompt list (listPrompts, walked from .smithers/prompts/). Read-only on the wire, so query-only; takes no arguments.
function useGatewayPrompts(): GatewayAsyncState<GatewayPromptRow[]>;
GatewayAsyncState<GatewayPromptRow[]>
object
Source useGatewayPrompts.ts

useGatewayRpc

The generic escape hatch: call any gateway RPC by name and get its typed payload back in GatewayAsyncState. The typed hooks above wrap this; reach for it when no dedicated hook exists. Disable with enabled: false; a disabled or key-changed query clears stale data instead of surfacing it.
function useGatewayRpc<Method extends GatewayRpcMethod>(
  method: Method,
  params: GatewayRpcParams<Method>,
  options?: { enabled?: boolean; deps?: readonly unknown[] },
): GatewayAsyncState<GatewayRpcPayload<Method>>;
method
GatewayRpcMethod
required
The RPC method name.
params
GatewayRpcParams<Method>
required
Typed params for that method.
options.enabled
boolean
default:"true"
Skip the request when false.
options.deps
readonly unknown[]
Re-fetch trigger. Defaults to the serialized params.
GatewayAsyncState<GatewayRpcPayload<Method>>
object
Source useGatewayRpc.ts · See also Gateway Client

Action hooks

useGatewayActions

Bound, memoized mutation methods off the client. Call them to drive a run: launch, resume, cancel, hijack, rewind, approve/deny, signal, and manage crons. Each takes the matching RPC params and returns a promise.
function useGatewayActions(): {
  launchRun: SmithersGatewayClient["launchRun"];
  resumeRun: SmithersGatewayClient["resumeRun"];
  cancelRun: SmithersGatewayClient["cancelRun"];
  hijackRun: SmithersGatewayClient["hijackRun"];
  rewindRun: SmithersGatewayClient["rewindRun"];
  submitApproval: SmithersGatewayClient["submitApproval"];
  submitSignal: SmithersGatewayClient["submitSignal"];
  cronCreate: SmithersGatewayClient["cronCreate"];
  cronDelete: SmithersGatewayClient["cronDelete"];
  cronRun: SmithersGatewayClient["cronRun"];
};
actions
object
const { launchRun, submitApproval } = useGatewayActions();
await launchRun({ workflow: "research", input: { topic: "smithers" } });
await submitApproval({ runId: RUN_ID, nodeId: NODE_ID, approved: true });
Source useGatewayActions.ts · See also Gateway Client

useGatewayMutation

Typed mutation for a single RPC by name, with optimistic updates, rollback, and invalidate-on-success. Wires the runner to registry.rpc(method, vars).
function useGatewayMutation<TVars extends Record<string, unknown>, TData = unknown>(
  method: string,
  options?: SyncMutationOptions<TVars, TData>,
): UseSyncMutationResult<TVars, TData>;
method
string
required
The RPC method to call.
options
SyncMutationOptions<TVars, TData>
onMutate / onSuccess / onError callbacks and invalidate keys. See useSyncMutation.
UseSyncMutationResult<TVars, TData>
object
{ mutate, mutateSafe, status, isLoading, data, error, reset }. See useSyncMutation.
Source useGatewayMutation.ts · See also useSyncMutation

Declarative Sync SDK

The lower layer the typed hooks are built on: a GatewayCollections registry of TanStack DB collections plus generic query / mutation / subscription hooks keyed by an arbitrary SyncKey. Use it for resources the typed hooks do not cover.

SyncProvider

Wrap a subtree with a GatewayCollections registry so descendants can call useSyncQuery, useSyncMutation, useSyncSubscription, and the live typed hooks against the same collections.
function SyncProvider(props: {
  client: GatewayCollections;
  children?: ReactNode;
}): ReactElement;
client
GatewayCollections
required
The registry, usually from createGatewayCollections.
children
ReactNode
Source SyncProvider.ts · See also createGatewayReactRoot

createGatewayCollections

Build the registry: one collection per gateway resource over an instrumented transport, plus the generic query/stream collections the sync hooks resolve on demand. createGatewayReactRoot calls this for you.
function createGatewayCollections(
  options: CreateGatewayCollectionsOptions,
): GatewayCollections;
options
CreateGatewayCollectionsOptions
required
GatewayCollections
object
The registry. Owns runs / run / approvals / … collections plus rpc, invalidate, query, stream, connect, and reset. Handed to SyncProvider.
Source createGatewayCollections.ts · Tests sync.test.ts

useSyncQuery

Declarative fetch over the registry. Concurrent components with the same key share one collection and one in-flight fetch.
function useSyncQuery<T>(
  key: SyncKey,
  fetcher: () => Promise<T>,
  options?: { enabled?: boolean; staleTimeMs?: number },
): UseSyncQueryResult<T>;
key
SyncKey
required
Cache key; identical keys multiplex onto one collection.
fetcher
() => Promise<T>
required
Runs on subscribe and on refetch.
options.enabled
boolean
default:"true"
Skip subscribe + fetch.
options.staleTimeMs
number
Treat cached data as fresh for this long.
UseSyncQueryResult<T>
object
{ data, error, status, isLoading, isRefreshing, refetch }, where status is "idle" | "loading" | "success" | "error".
Source useSyncQuery.ts · Tests sync.test.ts

useSyncMutation

Mutation with optimistic updates and invalidate-on-success. runner performs the write; onMutate may stage an optimistic value and return a rollback context for onError.
function useSyncMutation<TVars, TData, TContext = unknown>(
  runner: (vars: TVars) => Promise<TData>,
  options?: SyncMutationOptions<TVars, TData, TContext>,
): UseSyncMutationResult<TVars, TData>;
runner
(vars: TVars) => Promise<TData>
required
Performs the write, typically registry.rpc(method, vars).
options
SyncMutationOptions<TVars, TData, TContext>
UseSyncMutationResult<TVars, TData>
object
Source useSyncMutation.ts · Tests sync.test.ts

useSyncSubscription

Subscribe to a streaming source through a bounded collection. Returns the rolling buffer of frames; older frames drop off the front past maxFrames. N components on the same key share one upstream socket.
function useSyncSubscription(
  key: SyncKey,
  scope: string,
  params: unknown,
  options?: { enabled?: boolean; maxFrames?: number },
): {
  frames: ReadonlyArray<SyncStreamFrame>;
  last: SyncStreamFrame | undefined;
  dropped: number;
};
key
SyncKey
required
Cache key; identical keys multiplex onto one socket.
scope
string
required
The stream scope (e.g. a run-event channel).
params
unknown
required
Stream params.
options.enabled
boolean
default:"true"
Drop the subscription when false.
options.maxFrames
number
default:"200"
Bounded buffer size.
result
object
{ frames, last, dropped }: the bounded buffer, its newest frame, and the count dropped off the front.
Source useSyncSubscription.ts · Tests sync.test.ts

Type exports

These types are exported from the same subpath for typing your own components.
GatewayAsyncState<T>
type
The shared data-hook return shape. See above.
CreateGatewayCollectionsOptions
type
GatewayCollections
type
The registry handed to SyncProvider.
import type {
  CreateGatewayCollectionsOptions,
  GatewayCollections,
} from "smithers-orchestrator/gateway-react";
// GatewayAsyncState<T> is generic; see its section above for the full shape.
Source index.ts · Tests gateway-react.test.ts
This is the UI side of the control plane. For the underlying transport and its imperative methods, see the Gateway Client reference; to embed a UI in a gateway-served workflow, see Custom UI and the Custom workflow UI guide.