AGNT5 TypeScript SDK
Build reliable AI agents and durable workflows with TypeScript. AGNT5 checkpoints each workflow step, so a run that fails partway resumes from its last completed step instead of starting over. The SDK provides typed functions and workflows, retries, streaming, tool calling, multi-agent handoffs, human-in-the-loop (HITL) approvals, MCP, evals, and runtime observability.
Documentation · Starter templates · Website
Requirements
- Node.js 18 or newer
- An AGNT5 runtime for deployed execution
Installation
npm install @agnt5/sdk
Gateway configuration
Client uses https://gw.agnt5.com when no gateway URL is supplied or
AGNT5_GATEWAY_URL is empty. Set AGNT5_API_KEY for authentication. An
explicit gatewayUrl option takes precedence over AGNT5_GATEWAY_URL.
Worker session-history requests use the same environment setting and default.
For a local runtime, set its endpoint explicitly, for example
AGNT5_GATEWAY_URL=http://localhost:34183. A separately started app needs
its own environment settings: values set by agnt5 dev reach its worker
process, not sibling app processes.
Quick start
Define a typed function and start a worker:
import { fn, Worker } from '@agnt5/sdk';
const greet = fn('greet').run(async (ctx, name: string) => {
ctx.logger.info(`Greeting ${name}`);
return { message: `Hello, ${name}!` };
});
const worker = new Worker('hello-typescript');
await worker.run();
Imported functions, workflows, tools, and scorers register with the worker.
Enable autoRegister to include agents, or call worker.registerAgents(...). See examples/simple-worker.ts for a
complete entrypoint.
Durable workflows
Use named steps for operations that should be checkpointed and replayed safely:
import { workflow } from '@agnt5/sdk';
export const prepareReport = workflow(
'prepare-report',
async (ctx, reportId: string) => {
const source = await ctx.step('load-source', () => loadSource(reportId));
const report = await ctx.step('build-report', () => buildReport(source));
return { reportId, report };
},
);
Give a step its input with input and the body receives it. On a managed
worker, replay compares it with the input the step first ran with. If it
changed, the step raises NON_DETERMINISTIC_REPLAY instead of returning the
old result. Serverless and local runs pass the input to the body without
checking it:
const receipt = await ctx.step(
'charge',
({ orderId, amount }) => charge(orderId, amount),
{ input: { orderId, amount } },
);
A step without input isn't checked this way: values its closure captures can
change and the recorded result still comes back. Derive an input from the
workflow input or earlier step results, never from Date.now() or random IDs,
or every replay fails. The step takes a copy of the input when it's called,
with undefined properties dropped, and the body runs on that copy. Pass plain
data: dates as strings (toISOString()), and value ?? null for a value that
may be undefined, since undefined and null hash the same. A Date, Map, Set, function,
other class instance or undefined array item is rejected. The input is also
shown on the step's journal record, so pass a secret's name rather than its
value.
Keep step names and ordering stable across retries so completed work can be
reused. Direct calls to functions created with fn(...).run(...) also create a
checkpoint when called from a managed workflow. An explicit ctx.step keeps
its boundary when it contains a function call. Nested functions honor their
retry and backoff settings; ctx.attempt is zero-based. Make external effects
idempotent, because an interrupted attempt can execute again before its result
is recorded.
A nested streaming function keeps its checkpoint open until its iterator
finishes or is closed. Consume it with for await, or call return() before
the workflow waits or returns. Completed streams replay their recorded iterator
operations and validate their arguments without calling the handler again.
Changed arguments raise NON_DETERMINISTIC_REPLAY. A stream is not retried after
its iterator has been exposed to the caller. Replay iterators must also consume
the recorded terminal operation before the workflow waits or completes.
Calling a registered workflow from a managed workflow, or using
executeChildWorkflow, submits a separate child run and joins its result.
The submission uses a stable idempotency key. Child workflows need a gateway
URL and credentials for the parent's project, and their own worker capacity.
Standalone child calls use a separate local context and inherit cancellation
from the parent, including cancellation triggered by withTimeout.
Call ctx.waitForUser, ctx.waitForSignal, and durable ctx.sleep between
steps. A wait inside an unfinished step or an active tool/function activation
raises ConfigurationError. Signal
responses and completed steps survive subsequent pauses. For managed and
workerless user/signal waits, { timeoutMs } sets a durable wait deadline;
expiration fails the run with WAIT_TIMEOUT. This needs a runtime version
that supports wait deadlines. It differs from the client's response wait
setting described below.
From your application, resume a run waiting in ctx.waitForSignal with
client.signal(runId, name, payload). The receipt's resumed says whether the
run was waiting for that signal. To show a run's progress, follow its events
with client.followEvents(runId), including what it sent with ctx.emit. It
yields the history, then new events as they're written, and ends after the
run's final event. If the connection drops, it resumes from the last offset it
yielded:
await client.signal(runId, 'page_read', { url });
for await (const event of client.followEvents(runId)) {
if (event.eventType === 'crm.progress') render(event.data);
}
saga(ctx, steps, { name: 'order' }) checkpoints forward actions and reverse
compensations. Keep the name stable across dispatches. A pause unwinds without
starting compensation. Failed compensations do not stop the remaining rollback;
SagaCompensationError preserves the original cause and every compensation
error. Compensations must also be idempotent.
The failed action's outcome is recorded before rollback begins. Later dispatches
resume rollback and preserve that failure, even if the action would now succeed.
Start a new saga invocation to retry the transaction after compensation.
In pull-worker workflows, await ctx.set(key, value) and
await ctx.delete(key) wait for durable state-change acknowledgments.
ctx.get(key) reads the local workflow state. Before successful completion,
the worker persists the final WorkflowEntity snapshot with the active lease
and a version check, matching Python's workflow state persistence. State
writes are ordered within each workflow; independent workflows can proceed
concurrently. Standalone in-process contexts retain their local state behavior.
Declared durable steps
Declare workflow-local operations with step. Invoke them through ctx.step;
only the workflow is registered for standalone invocation.
import { step, workflow } from '@agnt5/sdk';
const prepareSearch = step('prepare-search', async (ctx, input: { topic: string }) => {
return { query: input.topic.trim() };
}, { retries: { maxAttempts: 3 }, timeoutMs: 5000 });
const research = workflow('research', async ctx => {
return ctx.step(prepareSearch, { input: { topic: 'durable agents' }, key: 'query' });
});
The handler starts after durable admission, and its result returns after
completion is acknowledged. These two activation calls record the lifecycle;
no extra function checkpoints are emitted. Each retry receives a fresh copy of
the input and a zero-based ctx.attempt. Completed steps replay their committed
result. External effects still need idempotency; the admitted handler can use
ctx.activation.idempotencyKey.
Return a JSON-serializable result; use null for an empty result.
Use explicit keys for concurrent or repeated work. Declared steps require
runtime activation support and fail explicitly without it. Existing fn(...)
functions and string/callback ctx.step(...) calls retain their behavior.
Event triggers
Use event(...) or webhook(...) in a workflow's triggers declarations.
Filtering, input mapping, batching, and delays are not supported yet. Leave
filterExpression, inputMapping, batchWindowMs, and delayExpression
unset; workflow registration rejects nonempty expressions and nonzero batch
windows with an error naming the option.
Agent limits and cancellation
An agent raises MaxIterationsExceededError when it exhausts maxIterations.
maxHandoffDepth defaults to 10 and applies across the whole handoff chain;
exceeding it raises HandoffDepthExceededError.
Pass an abort signal as agent.run(input, ctx, history, { signal }) or in an
LM generation request. The SDK combines it with the run's cancellation signal.
Native and edge LM providers cancel HTTP generation and streaming. Custom model
implementations receive the signal and must honor it to stop their own I/O.
Agent tools receive the combined signal through ctx.signal; cancellation also
stops the agent waiting for an in-flight tool. Tool implementations must honor
the signal to stop their own I/O.
Set autoRegister: true to discover registered agents, or use
worker.registerAgents(...) for an explicit agent list. Auto-discovery of agents
is disabled when the option is omitted. Workers contain
detached promise rejections and timer exceptions, fail their originating run,
and retain the error class and stack in result.error.type and
result.error.stack. containProcessErrors: false disables
those process guards for applications that own process error handling.
Errors outside an active run retain default fatal handling or the application's
existing process handlers, including detached errors after a run has settled.
Package entrypoints
| Import | Purpose |
|---|---|
@agnt5/sdk | Components, clients, workers, agents, tools, and workflows |
@agnt5/sdk/integrations | Third-party OpenAI, Agents SDK, Vercel AI SDK, and Google ADK capture |
@agnt5/sdk/serverless | Shared serverless adapters |
@agnt5/sdk/serverless/node | Node.js serverless adapter |
@agnt5/sdk/serverless/cloudflare | Cloudflare serverless adapter |
@agnt5/sdk/workerless/node | Node.js workerless HTTP adapter |
@agnt5/sdk/workerless/cloudflare | Cloudflare workerless HTTP adapter |
The default worker uses the published native binding for its supported Node.js platform. Serverless and workerless entrypoints have separate runtime requirements; review the relevant example before deploying to an edge runtime.
Third-party capture
Persistent workers automatically observe supported third-party libraries when
they are installed by the application. The integrations are soft-loaded; the
SDK does not install or bundle those libraries as runtime dependencies. Set
AGNT5_CAPTURE=off to disable all capture, or use
AGNT5_CAPTURE_OPENAI, AGNT5_CAPTURE_OPENAI_AGENTS,
AGNT5_CAPTURE_VERCEL_AI, and AGNT5_CAPTURE_GOOGLE_ADK as per-library
switches (off, 0, false, and no disable a switch).
Captured lifecycle events carry string provenance metadata: source
identifies the integration (openai, openai_agents, vercel_ai, or
google_adk) and capture_mode=observed distinguishes best-effort third-party
observation from explicitly tagged native SDK events (capture_mode=native).
Google ADK capture supports @google/adk 1.0.0 and newer; legacy 0.x releases
are intentionally unsupported.
Vercel AI SDK 7+ is captured through its public global telemetry registry.
Applications on earlier AI SDK versions can either enable the library's
experimental_telemetry option with an existing OpenTelemetry provider, or
use the explicit wrapper:
import * as ai from 'ai';
import { wrapAISDK } from '@agnt5/sdk/integrations';
const { generateText, streamText } = wrapAISDK(ai);
JournalSpanProcessor is also exported for applications that construct their
own OpenTelemetry tracer provider. The processor and wrapper emit only when a
call runs inside an AGNT5 component context, and capture failures never change
the provider call's result.
The workerless/serverless entrypoint invokes the same auto-enable hook for API parity, but it does not establish AsyncLocalStorage execution context today. Third-party capture therefore remains a no-op on that path until workerless context propagation is implemented.
Examples and documentation
examples/includes functions, workflows, agents, streaming, HITL, MCP, chat, and workerless HTTP examples.docs/contains the TypeScript SDK guides.- AGNT5 documentation covers platform concepts and deployment.
The shared Rust foundation lives in
agnt5dev/sdk-core. Vendor sandbox
adapters live in
agnt5dev/sdk-integrations.
Managed evaluation
Use client.eval() to run a registered component and score its output through
POST /v1/eval. This requires a reachable AGNT5 runtime and a worker serving the
component; it is not an offline experiment runner.
const result = await client.eval('greet', { name: 'Alice' }, {
expected: 'Hello, Alice!', // Defaults to the built-in exact_match scorer.
});
console.log(result.isSuccess, result.passed, result.scores);
isSuccess reports successful component execution; passed reports the scoring
outcome. A score mismatch can therefore have isSuccess === true and
passed === false. Built-in scorers do not require application registration.
For multiple inputs, use client.batchEval(component, items, { maxConcurrency: 5 }).
Each item is evaluated through the managed endpoint. maxConcurrency must be a
positive integer. Local custom scorer calls remain available through runScorer;
they do not create a managed experiment run or provide an offline dataset runner.
Response wait
Run and stream calls wait up to 5 minutes by default. Set the per-call wait to any value from zero to 24 hours. Zero returns a pending receipt immediately after acceptance. This controls response waiting, not the workflow execution deadline: accepted work continues when the wait expires or the client disconnects.
const options = { componentType: 'workflow' as const, waitTimeoutMs: 60000 };
const result = await client.run('process_order', order, options);
for await (const event of client.events('process_order', order, options)) {
console.log(event.eventType, event.runId);
}
waitTimeoutMs uses whole milliseconds. Run calls return 202 pending receipts
directly, without additional polling. Event streams emit stream.wait_expired
when the wait expires, or stream.detached for a 202 receipt. Use the run ID
to read status/results. Chunk-only stream raises RunError with the run ID
when waiting ends.
The default HTTP timeout allows at least the wait plus 10 seconds, or the client
timeout if longer. Pass timeoutMs: 75000 to set it explicitly.
Structured assertions
structured_assertions is a reserved built-in scorer with automatic worker
dispatch. In Node, the local helper calls SDK-core through the native binding:
import { structuredAssertions } from '@agnt5/sdk';
const result = structuredAssertions({
output: [1, 2, 3],
expected: { expected_length: 3 },
config: { assertions: [
{ name: 'unique_ids', expr: 'unique(output_json)' },
{ name: 'count', expr: 'size(output_json) == expected.expected_length' },
] },
});
The score is the fraction of assertions that pass; score_threshold defaults to
- Configuration and input errors always fail. See the SDK-core contract for supported expressions and execution limits. Edge clients may submit recipes for runtime execution; local evaluation requires Node and the matching native binding.
Serverless signing
Pass signingSecret to serve() from @agnt5/sdk/serverless or to the Node or
Cloudflare adapter. Use a string or a resolver that reads your provider's
environment bindings. Invokes with no resolved secret return HTTP 503
(WORKERLESS_SIGNING_SECRET_REQUIRED) before running user code. Missing or
invalid signatures with a configured secret return 401. The manifest remains
available for discovery.
For local development only, serve({ allowUnsigned: true }) permits unsigned
invokes when no secret resolves and logs a warning at startup. It still verifies
requests when a secret is configured. Missing static secrets warn at startup;
an empty request-time resolver warns on its first failed invoke. AGNT5 activation
and dispatch still require a signing secret, regardless of this SDK option.
Development
npm ci
npm run build:ts
npm test
Native binding development also requires a stable Rust toolchain and a sibling
checkout of sdk-core.
Release verification
A successful npm upload does not guarantee immediate package availability. npm scans new versions and may hold them for review. The release workflow waits up to 20 minutes (plus request time) for public native-package metadata and tarballs before publishing the main SDK, then verifies the main package too.
If a version remains unavailable, inspect its scan/staged status in npm using a maintainer account. Complete any required review or appeal through npm. Do not keep retrying an immutable version that returns "previously staged version"; a new version alone does not resolve a scan or approval hold.
See npm publish-time scanning and staged publishing.
Contributing
See CONTRIBUTING.md. Report security issues according to SECURITY.md.
License
Licensed under the Apache License 2.0.
Declared-step timeouts abort the handler’s signal. Handlers should pass that signal to cancellable I/O. Failure, cancellation, and retries wait until the handler settles, so ignoring the signal delays settlement; late results are discarded.
