Skip to content

JavaScript client

Terminal window
npm install @getstead/client@0.2.0

The package exports SteadClient, SteadAuth, SteadApiError, applyEvent, isTerminal, and their TypeScript types. It works with standard Fetch and streaming response bodies. React bindings are a separate package.

import { SteadClient } from '@getstead/client';
export function connect(token: string | (() => Promise<string>)) {
return new SteadClient({ url: 'https://api.getstead.dev', token });
}
Constructor option Meaning
url API base URL. Use https://api.getstead.dev.
token Bearer string or sync/async function returning one. Customer token in frontend; project secret only on trusted backends.
reconnectDelayMs Initial stream retry delay; default 250 ms.
maxReconnectDelayMs Backoff ceiling; default 5000 ms.
maxConnectAttempts Consecutive attempts without stream progress; default 20. Infinity opts into unlimited retries.
fetch Optional Fetch implementation.

All request methods below accept an optional final options object with signal?: AbortSignal. Aborting the HTTP request does not cancel an acknowledged run.

Method Authority and result
startRun(scenario, options?) Customer or project. Returns { run, outcome }. Customer scenario accepts agent, optional input, async, conversationId.
cancelRun(runId, options?) Customer’s own run or project. Returns { run: { id, status, cancelRequestedAt } }.
resolveApproval(approvalId, decision, options?) Project only. Decision is 'approved' or 'denied'; returns { approval }.
exchangeToken(externalId, options?) Project only. Returns { token, expiresAt, endUser: { id, externalId } }.
createConversation(agent, { key?, signal? }?) Customer only. Returns { conversation: { id, created } }.
listConversationMessages(id, { cursor?, limit?, signal? }?) Customer’s own conversation. Returns { messages, nextCursor? }.
select<Row>(table, { where?, limit?, signal? }?) Customer only. Returns { rows: Row[], rowCount }. The generic describes your data; it does not validate it at runtime.
streamRun(runId, options?) Project’s run or customer’s own run. Returns a RunStream immediately.

Successful JSON responses also carry the wire’s ok: true. outcome contains status and may include approvalId or failure. Customer run summaries expose id, endUserId, and status; project summaries additionally include agent ID, timestamps, budget, spend, and recovery/cancellation details. Dollar totals are decimal strings in run summaries.

For project starts, StartRunScenario requires the server’s scenario contract: agent, endUser, and steps, with optional budget, tools, and async. To resume, send resume without redefining the program. Version 0.2.0’s TypeScript steps type represents tool steps; use the HTTP contract for project scenarios containing model steps. Customer starts do not need to send steps.

Options: lastEventId?: number resumes after that sequence, signal closes the local stream on abort, connect: false defers dialing, and maxConnectAttempts overrides the client setting.

The stream implements AsyncIterable<RunStreamItem> and provides:

Member Contract
state Stable snapshot with runStatus, approvalId, lastEventId, connection, and error.
done Promise resolving when the stream ends or is closed; rejects on permanent refusal or exhausted retries. Local close need not mean the run is terminal.
subscribe(listener) Notify on state changes; returns an unsubscribe function.
connect() Start a deferred connection; idempotent.
close() Stop watching. Does not cancel the server run.

Iterator items have one of two shapes:

type Item =
| { kind: 'event'; seq: number; type: string; payload: Record<string, unknown> }
| { kind: 'model-delta'; stepIndex: number; text: string };

Durable events resume by sequence. Temporary deltas do not replay; use completed results to reconcile displayed text. The local iterator buffer is bounded; if you need full history later, open a stream from your saved durable cursor. Handle both connection failures and terminal run failures.

Constructor: { url, projectId, sessionToken?, onSessionToken?, fetch? }. url must use HTTPS, except loopback HTTP development; it cannot contain credentials, query, or fragment. projectId is a UUID. onSessionToken(token | undefined) lets your app persist or clear the signed customer session.

Method Parameters and behavior
signUp({ name, email, password, callbackURL }) Register and request verification. Resolves void.
signIn({ email, password }) Obtain the customer session. Clears a previous session before signing in.
signOut() Clear locally and request server sign-out.
sendVerificationEmail(email, callbackURL) Request a new verification email.
requestPasswordReset(email, redirectTo) Request a password reset email.
resetPassword(token, newPassword) Complete reset and clear the local session.
getToken() Return a customer token, cached until 30 seconds before expiry; coalesces simultaneous refreshes. Pass this method directly as SteadClient.token.

Auth requests have a 30-second timeout, omit ambient cookies, and refuse redirects. Callbacks must use an allowed application origin. See the authentication guide for a complete flow and storage tradeoffs.

SteadApiError extends Error and has code: string, status: number, and message. HTTP refusal codes come from the server. Non-JSON responses become internal errors. Transport failures may be ordinary Fetch errors rather than SteadApiError.

import { SteadApiError } from '@getstead/client';
export function describeFailure(error: unknown): string {
if (error instanceof SteadApiError) {
return `${error.status} ${error.code}: ${error.message}`;
}
return error instanceof Error ? error.message : 'Request failed';
}

Render errors as text, not HTML. Preserve an opaque request reference when reporting a server failure, but remove credentials and private payloads.

The package’s /vite export prevents dependency prebundling from breaking stream parsing:

import { defineConfig } from 'vite';
import { getsteadClient } from '@getstead/client/vite';
export default defineConfig({ plugins: [getsteadClient()] });

Keep your other Vite plugins as well. The starter already includes this configuration.

applyEvent(state, seq, type, payload) mutates a FoldState (runStatus, approvalId, lastEventId) from a durable event. Unknown event types advance the cursor without inventing a status. isTerminal(status) recognizes completed, failed, budget-exceeded, and cancelled.

Exported types include SteadClientOptions, SteadToken, SteadAuthOptions, RequestOptions, StartRunScenario, StartRunResult, RunSummary, EndUserRunSummary, CancelRunResult, ResolveApprovalResult, ExchangeTokenResult, CreateConversationResult, ConversationMessage, ListConversationMessagesResult, SelectResult, FoldState, RunStream, RunStreamItem, RunStreamOptions, RunStreamState, RunStatus, and ConnectionStatus.