@agentlattice/sdk
Governance SDK for AI agents. Every action your agent takes goes through AgentLattice's audit trail, policy engine, and approval gates.
Install
npm install @agentlattice/sdk
Requires Node.js >=14.17.0.
Method Inventory
Core
| Capability | Method | Usage | Details |
|---|---|---|---|
| Execute action | al.execute(actionType, opts?) |
await al.execute("pr.open") |
Reference |
| Gate (block until approved) | al.gate(actionType, opts?) |
await al.gate("pr.merge") |
Reference |
Managed Agents
| Capability | Method | Usage | Details |
|---|---|---|---|
| Govern a managed agent | al.govern(agentId, message, opts?) |
await al.govern("agent_011Ca...", "Analyze this report") |
Reference |
Delegation
| Capability | Method | Usage | Details |
|---|---|---|---|
| Delegate sub-agent | al.delegate(name, opts, cb) |
await al.delegate("reader", { capabilities: [...], ttl: 300 }, async (sub) => { ... }) |
Reference |
| Parallel fan-out | AgentLattice.parallel([...]) |
await AgentLattice.parallel([al.delegate(...), al.delegate(...)]) |
Reference |
| Execute as child | sub.execute(actionType, opts?) |
await sub.execute("read_data") |
Reference |
| Gate as child | sub.gate(actionType, opts?) |
await sub.gate("read_data") |
Reference |
| Chain delegation | sub.delegate(name, opts, cb) |
await sub.delegate("sub-reader", { capabilities: [...], ttl: 60 }, async (s) => { ... }) |
Reference |
Types & Errors
| Export | Description | Details |
|---|---|---|
ActionResult |
Return type of execute() — status, audit ID, conditions |
Reference |
ActionOptions |
Options for execute() and gate() — data_accessed, metadata, event_id |
Reference |
AgentLatticeError |
Base error — code, message, details | Reference |
AgentLatticeDeniedError |
Policy denial or reviewer rejection — reason, policy, conditions | Reference |
AgentLatticeTimeoutError |
Approval window expired — approvalId | Reference |
Quickstart
import { AgentLattice } from "@agentlattice/sdk";
const al = new AgentLattice({ apiKey: process.env.AL_API_KEY! });
// gate() blocks until a human approves (or throws if denied/timed out)
await al.gate("pr.merge");
// Your agent code only runs if the action was approved
await mergePR(42);
With richer context for risk scoring:
await al.gate("pr.merge", {
data_accessed: [
{ type: "source_code", count: 142, sensitivity: "medium" },
],
metadata: { pr_number: 42, repo: "acme/backend" },
});
API Reference
new AgentLattice(config)
| Field | Required | Default | Description |
|---|---|---|---|
apiKey |
Yes | — | Bearer token from your operator config |
baseUrl |
No | https://www.agentlattice.io |
Override for self-hosted or staging |
gatePollTimeoutMs |
No | 8 * 60 * 60 * 1000 (8 hours) |
How long gate() polls for approval |
gatePollIntervalMs |
No | 5000 (5 seconds) |
How often to check approval status |
al.govern(agentId, message, options?)
Run a governed Anthropic Managed Agent session. Requires @anthropic-ai/sdk as a peer dependency.
const al = new AgentLattice({
apiKey: process.env.AL_API_KEY,
anthropicApiKey: process.env.ANTHROPIC_API_KEY,
});
const result = await al.govern("agent_011Ca...", "Analyze this earnings report", {
onToolCall: (tool) => console.log(`Tool: ${tool.name}`),
onDecision: (d) => console.log(`${d.allowed ? "Allowed" : "Denied"}: ${d.actionType}`),
onMessage: (text) => console.log(`Agent: ${text}`),
failOpen: false, // default: deny if AL unreachable
});
console.log(result.output); // Agent's text response
console.log(result.decisions); // Per-tool governance decisions
console.log(result.sessionId); // Anthropic session ID
Returns GovernResult with output, decisions (array of GovernDecision), sessionId, environmentId, and completed.
Pass sessionId and environmentId in options to reuse an existing Anthropic session instead of creating one.
See Managed Agents guide for full setup instructions.
al.gate(actionType, options?)
Blocks until the action is approved. Returns void on approval.
Throws on denial or timeout — use a try/catch to handle gracefully:
try {
await al.gate("pr.merge", { metadata: { pr_number: 42 } });
await mergePR(42);
} catch (e) {
if (e instanceof AgentLatticeDeniedError) {
// Denied — could be a policy rule or a human reviewer
console.log(`Denied: ${e.reason}`); // "CONDITIONS_DENIED" or "DENIED_BY_REVIEWER"
console.log(`Policy: ${e.policy}`); // "PR merge policy"
console.log(`Failed rules:`, e.conditions); // [{field:"pr_size", operator:"lt", expected:100, result:false}]
} else if (e instanceof AgentLatticeTimeoutError) {
// Approval window expired — log and abort
console.log(`Approval timed out (id: ${e.approvalId})`);
}
}
al.execute(actionType, options?)
Submit an action and return immediately without waiting for approval. Use this for logging or when you don't need to block.
Returns ActionResult:
type ActionResult = {
status: "executed" | "requested" | "denied" | "timed_out" | "policy_not_found";
audit_event_id: string;
approval_id?: string; // set when status is "requested" — poll this to check resolution
denial_reason?: string; // set when status is "denied" — e.g. "CONDITIONS_DENIED"
policy_name?: string; // the policy that governs this action type
conditions_evaluated?: Array<{
field: string;
operator: string;
expected: unknown;
result: boolean; // false = this rule failed
}>;
};
Options
interface ActionOptions {
resource?: string; // target of the action: URL, table name, file path, API endpoint.
// used for resource-scoped policies (e.g., allow web.query only for linkedin.com)
data_accessed?: Array<{
type: string; // what kind of resource: "source_code" | "database" | "external_api" |
// "user_data" | "credentials" | "filesystem" | "network_request"
count: number; // how many records/files/rows accessed
sensitivity: "low" | "medium" | "high" | "critical";
}>;
metadata?: Record<string, unknown>; // arbitrary context (pr_number, repo, filename...)
event_id?: string; // idempotency key — duplicate submissions with the same key return
// the existing audit record without creating a new one. Pass a
// stable ID (e.g. task ID) when your agent might retry.
}
Error classes
// Thrown by gate() when a policy rule fires or a reviewer declines
class AgentLatticeDeniedError extends AgentLatticeError {
approvalId?: string; // present for reviewer-denied actions, absent for policy denials
reason?: string; // "CONDITIONS_DENIED" | "POLICY_TAMPERED" | "DENIED_BY_REVIEWER"
policy?: string; // policy name that governed this action
conditions?: Array<{ field: string; operator: string; expected: unknown; result: boolean }>;
}
// Thrown by gate() when the approval window expires before a decision
class AgentLatticeTimeoutError extends AgentLatticeError {
approvalId: string;
}
// Base class — check error.code for programmatic handling
class AgentLatticeError extends Error {
code: string; // "MISSING_API_KEY" | "POLICY_NOT_FOUND" | "API_ERROR" | ...
details?: unknown;
}
Delegation — Scoped Sub-Agents
Create short-lived child agents with narrowed capabilities. Cleanup is automatic.
const result = await al.delegate("data-processor", {
capabilities: ["read_data", "write_results"],
ttl: 300, // seconds
}, async (sub) => {
await sub.execute("read_data", { metadata: { rows: 100 } });
return sub.execute("write_results");
});
al.delegate(name, options, callback)
| Field | Required | Description |
|---|---|---|
name |
Yes | Display name for the ephemeral child agent |
options.capabilities |
Yes | Array of action types the child can perform (must be subset of parent's) |
options.ttl |
Yes | Time-to-live in seconds (max 86400 = 24 hours) |
callback |
Yes | Async function receiving a DelegatedAgent |
The callback runs inside a try/finally — cleanup always happens, even on error.
The child agent's scope is enforced server-side: any sub.execute() call outside
the granted capabilities returns a 403.
DelegatedAgent
| Method | Description |
|---|---|
sub.execute(actionType, options?) |
Execute an action as the child (includes X-AL-Delegation-ID header) |
sub.gate(actionType, options?) |
Execute + block until approved |
sub.delegate(name, options, callback) |
Chain further — create a sub-sub-agent with narrower scope |
AgentLattice.parallel(delegates)
Runs N delegation callbacks concurrently via Promise.all. Each delegation
manages its own cleanup independently.
const results = await AgentLattice.parallel([
al.delegate("reader-1", { capabilities: ["read_data"], ttl: 60 }, async (sub) => {
return sub.execute("read_data", { metadata: { shard: 1 } });
}),
al.delegate("reader-2", { capabilities: ["read_data"], ttl: 60 }, async (sub) => {
return sub.execute("read_data", { metadata: { shard: 2 } });
}),
]);
See docs/ephemeral-agents.md for architecture details, security model, and cleanup guarantees.
Action Types
Action types are free-form strings matched against your configured policies. Use dot-notation namespaces:
| Action | Example |
|---|---|
code.commit |
Agent commits code changes |
pr.open |
Agent opens a pull request |
pr.merge |
Agent merges a pull request |
file.write |
Agent writes to a file |
file.delete |
Agent deletes a file |
db.migrate |
Agent runs a database migration |
secret.read |
Agent reads a secret or credential |
deploy.trigger |
Agent triggers a deployment |
Define policies for each action type in your AgentLattice dashboard.
Patterns
Self-correcting agent
conditions_evaluated closes the feedback loop — agents can read which specific
rule fired and adapt, not just fail.
const al = new AgentLattice({ apiKey: process.env.AL_API_KEY! });
async function openPRWithGovernance(files: string[]): Promise<void> {
try {
await al.gate("pr.open", {
metadata: { pr_size: files.length, repo: "acme/backend" },
});
await openPR(files);
} catch (e) {
if (e instanceof AgentLatticeDeniedError && e.reason === "CONDITIONS_DENIED") {
const failed = e.conditions?.filter(c => !c.result) ?? [];
// e.g. [{field:"pr_size", operator:"lt", expected:50, result:false}]
const prSizeRule = failed.find(c => c.field === "pr_size");
if (prSizeRule) {
// Policy says PRs must be < 50 files — split and retry
const mid = Math.floor(files.length / 2);
await openPRWithGovernance(files.slice(0, mid));
await openPRWithGovernance(files.slice(mid));
return;
}
}
throw e; // reviewer denial or policy tamper — no retry
}
}
LangGraph integration
import { tool } from "@langchain/core/tools";
import { AgentLattice } from "@agentlattice/sdk";
const al = new AgentLattice({ apiKey: process.env.AL_API_KEY! });
const mergeCodeTool = tool(async ({ pr_number, repo }) => {
await al.gate("pr.merge", {
metadata: { pr_number, repo },
});
return await mergePR(pr_number); // only reached if approved
}, {
name: "merge_pr",
description: "Merge a pull request (requires human approval)",
schema: z.object({ pr_number: z.number(), repo: z.string() }),
});
Fire-and-forget logging
// Doesn't block — submits the audit event and continues
const { audit_event_id } = await al.execute("file.read", {
data_accessed: [{ type: "filesystem", count: 1, sensitivity: "low" }],
metadata: { path: "/etc/config.yaml" },
event_id: `task-${taskId}-read`, // stable ID for idempotency
});
Reliability
execute() and gate() retry automatically on 5xx and network errors — up to 3 attempts with 1s/2s/4s exponential backoff. Each request has a 30-second timeout. Pass event_id to make retries idempotent — the server deduplicates by that key and returns the existing audit record instead of creating a duplicate.