TypeScript/Node.js SDK for Agent Assembly, licensed under Apache 2.0.
Pre-1.0 / beta. The current release line is
0.0.1-beta.x, published to npm under thebetadist-tag and cut as a GitHub Release. The public surface (initAssembly,withAssembly) is stabilizing but may change between pre-releases, and per-platform native packaging is still being hardened. Pin an exact version for reproducible installs and review the release notes before upgrading. Production deployments should track the first0.1.0release.
pnpm add @agent-assembly/sdk@beta # latest beta (tracks @beta dist-tag)
pnpm add @agent-assembly/sdk@<X.Y.Z-beta.N> # pin exact — replace the placeholder with the
# desired version from npmjs.com/package/@agent-assembly/sdkBefore installing or contributing, ensure your environment has:
- Node.js ≥ 18.18.0 (LTS). The active LTS lines (18, 20, 22, 24) are exercised in CI.
- pnpm ≥ 10. The repository enforces pnpm via
enginesand ships apnpm-lock.yaml. - Rust toolchain (only required when rebuilding the native
aa-ffi-nodebinding from source — most consumers receive a prebuilt platform binary viaoptionalDependencies).
pnpm add @agent-assembly/sdk
# or
npm install @agent-assembly/sdk
# or
yarn add @agent-assembly/sdkThe SDK ships dual ESM/CJS entries and selects a prebuilt native binding for your platform
during postinstall. No additional build step is required for typical consumers.
Pass your LangChain-style tools ({ name, invoke }) to initAssembly under
langchain.tools. Each tool is wrapped in place so every invoke() is checked
against gateway policy before it runs.
import { initAssembly } from "@agent-assembly/sdk";
const searchWeb = {
name: "search_web",
invoke: async (input: { q: string }) => `results for ${input.q}`
};
const ctx = await initAssembly({
gatewayUrl: "http://localhost:7391",
agentId: "demo",
langchain: { tools: { searchWeb } }
});
await searchWeb.invoke({ q: "agent assembly" }); // governed; throws on policy deny
await ctx.shutdown();const { initAssembly } = require("@agent-assembly/sdk");
const searchWeb = {
name: "search_web",
invoke: async (input) => `results for ${input.q}`
};
const ctx = await initAssembly({
gatewayUrl: "http://localhost:7391",
agentId: "demo",
langchain: { tools: { searchWeb } }
});
await searchWeb.invoke({ q: "agent assembly" });
await ctx.shutdown();Both entrypoints resolve to the same governance pipeline; the package's exports field
selects ESM or CJS automatically based on how the consumer imports it.
initAssembly() registers the LangChain callback handler and auto-wraps the configured
tools, so each is checked against gateway policy before invocation. For more frameworks
and the lower-level withAssembly() wrapper, see the Examples guide on the
documentation site.
The SDK is tested against every active Node.js LTS line on every supported operating
system. The matrix is enforced by .github/workflows/test-matrix.yml:
| Node.js | Linux (ubuntu-latest) | macOS (macos-latest) | Windows (windows-latest) |
|---|---|---|---|
| 18 | ✅ | ✅ | ✅ |
| 20 | ✅ | ✅ | ✅ |
| 22 | ✅ | ✅ | ✅ |
| 24 | ✅ | ✅ | ✅ |
Older Node.js lines (≤ 16) are unsupported because the napi-rs ABI used by the native binding requires Node 18.18 or newer.
The SDK is a thin TypeScript wrapper around the Agent Assembly Rust runtime. It reaches the runtime over one of two transports:
- a gRPC sidecar client that talks to a separate gateway process, or
- a native in-process binding built with napi-rs.
initAssembly() is the primary entrypoint. It resolves the gateway, registers the agent,
and installs governance hooks for whichever supported framework it detects, so every tool
call is checked against policy before it runs.
| Export | Purpose |
|---|---|
initAssembly(config) |
Set up governance and auto-wire detected frameworks. The main entrypoint. |
withAssembly(tools, options) |
Lower-level wrapper to govern a tool map when you manage the gateway client yourself. |
createNoopGatewayClient(mode) |
Build an allow-all GatewayClient for offline demos and tests, or as a base to wrap. |
PolicyViolationError |
Thrown by a governed tool when the gateway client denies the call. |
currentAgentId(), runWithAgentId() |
Read and set the active agent id in the async-context lineage store. |
encodeAuditEvent() / decodeAuditEvent() (and the call-stack codecs) |
Encode and decode audit events to and from their wire shape. |
findAasmBinary(), INSTALL_HINT |
Locate the bundled aasm runtime binary and the install hint shown when it is missing. |
ENFORCEMENT_MODES |
The allowed enforcementMode values. |
Type-only exports (AssemblyConfig, AssemblyContext, AssemblyMode, EnforcementMode,
ToolMap, GatewayClient, the Gateway* governance types, and friends) are documented in
the API reference.
withAssembly(tools, options) needs a GatewayClient. For offline demos, tests, or custom
in-process policies you can build one yourself — no running gateway required:
import {
createNoopGatewayClient,
withAssembly,
type GatewayClient
} from "@agent-assembly/sdk";
// Allow-all client — handy for offline smoke tests:
withAssembly(
{ search: { description: "Search", execute: async (q: string) => `result:${q}` } },
{ gatewayClient: createNoopGatewayClient("sdk-only") }
);
// Or enforce your own in-process policy by implementing the GatewayClient interface:
const policyClient: GatewayClient = {
mode: "sdk-only",
start: async () => undefined,
close: async () => undefined,
check: async (request) =>
request.toolName === "delete_file"
? { denied: true, reason: "blocked by local policy" }
: { denied: false },
waitForApproval: async () => ({ denied: false }),
record: async () => undefined,
recordResult: async () => undefined,
scanPrompts: async () => undefined
};
// A governed tool throws `PolicyViolationError` when `check` denies it.LangChain's handleToolStart callback cannot stop a tool from running by its return
value, so the SDK governs LangChain tools with two cooperating layers:
- Callback layer (
AssemblyCallbackHandler) — tracks deferred denials and redacts output athandleToolEnd. - Wrapper layer (
wrapToolWithAssembly) — enforces the real pre-execution allow / deny / pending check.
initAssembly() registers the callback handler and wraps your configured LangChain tools
for you, so you do not wire either layer by hand.
Most frameworks expose a tool name that policies match on. Vercel AI SDK tools do
not — they have no .name field — so for that framework, write policies that match on
the tool's description content (or, in withAssembly, the tool-map key) instead of a
framework-level tool name.
src/
index.ts # public entrypoint — re-exports the supported surface
core/ # init-assembly flow + gateway/API-key resolution
adapters/ # Adapter interface + AdapterRegistry, LangChain adapter
hooks/ # auto-detection patches (Vercel AI, OpenAI Agents, LangGraph, Mastra)
gateway/ # gateway client (gRPC sidecar transport)
native/ # loader for the napi-rs in-process binding
wrappers/ # low-level withAssembly() tool wrapper
lineage/ # async-context store for parent/child agent lineage
audit/ # audit-event wire encode/decode helpers
errors/ # ConfigurationError, GatewayError, PolicyViolationError, …
types/ # AssemblyConfig, AssemblyContext, AssemblyMode, EnforcementMode, …
proto/generated/ # generated protobuf types
native/aa-ffi-node/ # the Rust napi-rs crate (built into a per-platform .node binary)
tests/ # unit + architecture tests
For how these layers fit together, see the Architecture guide.
Most consumers never build the native binding — a prebuilt binary is installed for their
platform. You only need this when working on the aa-ffi-node Rust crate (at
native/aa-ffi-node) or running on a platform with no prebuild.
Build commands:
pnpm native:build— debug build, for local development.pnpm native:build:release— release build that produces the per-platform artifact.pnpm native:check-types— strict type-check of the generated napiindex.d.ts.
Run the native integration acceptance test (skipped unless the binding is built):
AA_NATIVE_TEST=1 pnpm vitest run tests/native-napi-integration.test.ts
The build-addon GitHub workflow compiles the native addon (Node 20 and 22): an
ubuntu-only debug build on pull requests, and ubuntu + macOS builds on master and release
tags. The addon embeds a Unix-domain-socket transport and does not build on Windows;
Windows consumers use grpc-sidecar mode.
The package publishes dual module outputs behind conditional exports:
- ESM entry:
./dist/esm/index.js - CJS entry:
./dist/cjs/index.js - Type declarations:
./dist/types/index.d.ts
The four @agent-assembly/runtime-* packages (runtime-linux-x64, runtime-linux-arm64,
runtime-darwin-x64, runtime-darwin-arm64) are declared as optionalDependencies and
constrained by os/cpu, so only the one matching your platform is installed. Each
carries the aasm runtime binary; there is no Windows runtime package. The napi-rs .node
addon is loaded at runtime by native/aa-ffi-node/index.cjs.
CI verifies the published shape with:
- ESM and CJS entry smoke tests,
- an
exportstypesmapping assertion, and npm packcontent and package-size guard tests.
Full guides, architecture deep-dives, and the complete API reference are published at:
https://ai-agent-assembly.github.io/node-sdk/
The site is built from the docs/ (content) and website/ (Docusaurus app) directories
and is re-published on every push to master via the publish-docs.yml workflow.
@agent-assembly/sdk is one client of the Agent Assembly platform. The governance
decisions it enforces are made by the core Rust runtime; the protocol it speaks is shared
across all SDKs.
| Project | What it is |
|---|---|
| agent-assembly | Core Rust runtime — gateway, policy engine, proxy, eBPF, CLI (aasm). The protocol specification lives here. |
| Documentation site | Canonical, cross-repo documentation for the whole platform. |
| python-sdk | Sibling SDK for Python. |
| go-sdk | Sibling SDK for Go. |
| agent-assembly-examples | Runnable examples — learn by running small, framework-specific Node.js/TypeScript (and Python/Go) samples for policy enforcement, approvals, audit, trace, and runtime workflows. |
| Release notes | Per-version changelog for this package. |
| Organization profile | Index of every Agent Assembly repository and its status. |
- Questions and bug reports — open an issue on the
node-sdk issue tracker. Include
your Node.js version, OS/arch, and the SDK version (
pnpm why @agent-assembly/sdk). - Security vulnerabilities — please do not file a public issue. Report privately via the repository's security advisories page so a fix can be coordinated before disclosure.
- Contributing — see CONTRIBUTING.md for environment setup, the adapter-authoring guide, and the test/commit conventions.