Navigate docs

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