Skip to content

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#

Terminal
export AIW_API_KEY="aiw_sk_live_…"export AIW_BASE_URL="https://gateway.worksi.ai"      # the gateway root, without /v1
TypeScript
import { 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")  },)
Table: Option, Environment, Default, Notes
OptionEnvironmentDefaultNotes
apiKeyAIW_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
baseURLAIW_BASE_URLhttps://gateway.worksi.aiThe gateway root. Set it for a self-hosted gateway. A trailing /v1 is removed.
serviceAIW_SERVICE—Default agent when none is set on the run or step
captureAIW_CAPTUREserver settingredacted or metadata_only. Can only make capture stricter.
enabledAIW_ENABLEDtrueMaster switch for export, headers and checks
toolPoliciesAIW_TOOL_POLICIESenforceoff skips the tool policy pre-check

Tracing API#

Tracing API
CallSpanNotes
aiw.run(opts, fn)run.started, then workflow_runEnds ok, error or cancelled. Options: workflow (required), agent, team, metadata, runId, traceId, tags, endUser, capture.
aiw.step(name, fn, { agent })agent_stepModel calls inside it become its children
aiw.tool(name, args, fn, { dataSource })tool_callThe arguments are hashed for loop detection and tool policies. The return value becomes the output.
aiw.span(name, fn, { kind })retrieval, guardrail or customFor 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.

TypeScript
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() throws AIWConfigError.
  • Direct mode covers OpenAI clients only.

Controls and approvals#

  • await aiw.checkStatus() throws AgentPausedError or RunStoppedError when 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 takes type, title, risk, payload, tool and timeoutMs. A rejection throws ApprovalRejectedError; a timeout throws ApprovalTimeoutError.
  • aiw.tool() checks tool policies before the callback runs, exactly like the Python SDK: an enforced disallow throws ToolNotAllowedError, 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).

TypeScript
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.