TypeScript SDK
Wire the OpenAI and Anthropic Node.js clients to the gateway and trace runs, steps and tools with typed errors and approvals.
On this page
@aiworkforce/sdk wires the official openai package to the gateway and records what your agents do. It runs on Node.js 18.18 or later, ships ESM and CommonJS builds with type declarations, and takes openai as a peer dependency (v4.77 to v7). @anthropic-ai/sdk (0.50 or later, before 1.0) is an optional peer dependency, needed only for aiw.anthropic().
Getting the SDK
The SDK packages are distributed by Worksi rather than through a public package registry. Email info@xeoo.io to get them for your account. You don’t need them to start: any OpenAI or Anthropic SDK works with the gateway as it is.
Configure and trace a workflow#
export AIW_API_KEY="aiw_sk_live_…"export AIW_BASE_URL="https://gateway.worksi.ai" # the gateway root, without /v1import { AIW } from "@aiworkforce/sdk" // reads AIW_API_KEY and AIW_BASE_URLconst aiw = new AIW({ service: "support-bot",})const openai = aiw.openai() await aiw.run( { workflow: "customer-chat" }, async (run) => { await aiw.step("classify", () => openai.chat.completions.create({ model: "gpt-5-mini", messages, }), ) // tool_call span with aiw.args_hash await aiw.tool( "lookup_order", { orderId }, () => ordersDb.get(orderId), { dataSource: "orders-db" }, ) await aiw.checkStatus() run.outcome("ticket_resolved") },)| Option | Environment | Default | Notes |
|---|---|---|---|
apiKey | AIW_API_KEY | — | Without a key the SDK logs a warning: runs, steps and tools still work, but nothing is exported and aiw.openai() throws AIWConfigError |
baseURL | AIW_BASE_URL | https:// | The gateway root. Set it for a self-hosted gateway. A trailing / is removed. |
service | AIW_SERVICE | — | Default agent when none is set on the run or step |
capture | AIW_CAPTURE | server setting | redacted or metadata_only. Can only make capture stricter. |
enabled | AIW_ENABLED | true | Master switch for export, headers and checks |
toolPolicies | AIW_TOOL_POLICIES | enforce | off skips the tool policy pre-check |
Tracing API#
| Call | Span | Notes |
|---|---|---|
aiw. | run.started, then workflow_run | Ends ok, error or cancelled. Options: workflow (required), agent, team, metadata, runId, traceId, tags, endUser, capture. |
aiw. | agent_step | Model calls inside it become its children |
aiw. | tool_call | The arguments are hashed for loop detection and tool policies. The return value becomes the output. |
aiw. | retrieval, guardrail or custom | For anything else worth timing |
Context propagates through await, timers and concurrent work with AsyncLocalStorage. Errors thrown inside a callback are recorded on the span and re-thrown unchanged. aiw.contextHeaders() returns the X-AIW-* headers for HTTP clients other than aiw.openai() and aiw.anthropic().
Claude and other providers#
The same aiw.openai() client reaches every provider your organization connects: prefix the model with the credential’s route prefix. For code written for @anthropic-ai/sdk, aiw.anthropic(Anthropic) takes the SDK’s Anthropic class (the Worksi SDK never imports the package) and returns a client bound to the gateway’s native Messages API.
import Anthropic from "@anthropic-ai/sdk" // one client, one Worksi key, every providerconst openai = aiw.openai()await openai.chat.completions.create({ model: "deepseek/deepseek-flash", messages,}) // an Anthropic client bound to the gateway rootconst claude = aiw.anthropic(Anthropic) await aiw.run({ workflow: "customer-chat" }, () => aiw.step("answer", () => claude.messages.create({ model: "anthropic/claude-sonnet-5", max_tokens: 1024, messages, }), ),)- Requests carry the active context’s attribution headers, like
aiw.openai(). No Anthropic key from your environment is sent to the gateway: it calls Anthropic with your organization’s credential. - Gateway refusals are thrown as the typed errors below, with the Anthropic error as
cause. Without the class,aiw.anthropic()throwsAIWConfigError. - Direct mode covers OpenAI clients only.
Controls and approvals#
await aiw.checkStatus()throwsAgentPausedErrororRunStoppedErrorwhen an operator paused the agent or workflow, or stopped the run.await aiw.requireApproval(request)waits for a human decision and fails closed. The request takestype,title,risk,payload,toolandtimeoutMs. A rejection throwsApprovalRejectedError; a timeout throwsApprovalTimeoutError.aiw.tool()checks tool policies before the callback runs, exactly like the Python SDK: an enforced disallow throwsToolNotAllowedError, an enforced approval requirement requests the approval first, and monitor-mode rules never block.
Gateway errors#
Clients from aiw.openai() and aiw.anthropic() throw typed errors, all subclasses of AIWAPIError, with status, code, requestId and the original SDK error as cause: AgentPausedError (423), RunStoppedError (409), BudgetExceededError (402), ModelNotAllowedError, ApprovalRequiredError, ToolNotAllowedError, TokenLimitExceededError (403) and RateLimitedError (429, with retryAfterSeconds).
try { await openai.chat.completions.create({ model: "gpt-4.1", messages })} catch (err) { if (err instanceof ModelNotAllowedError) { // fall back to a model your policies allow } else if (err instanceof AgentPausedError) { // stop work and surface err.reason } else { throw err }}End users#
Pass endUser on a run to attribute its model calls to a pseudonymous end user. To apply per-plan limits, also send the user’s plan in the X-AIW-End-User-Plan header, as shown in end‑user plans.
Direct mode#
aiw.instrumentOpenAI(new OpenAI()) traces a client that calls OpenAI directly: chat.completions.create, responses.create and embeddings.create, including streams. Gateway enforcement does not apply to these calls.
Delivery#
Telemetry is exported in gzip batches with retries and never throws into your code. Pending telemetry is flushed on beforeExit. In serverless handlers, await aiw.flush() before returning; on SIGTERM, await aiw.shutdown().
Questions? Email info@xeoo.io.