al-instrument-ts
Auto-instrument TypeScript AI agents with AgentLattice governance. One command adds identity, authorization, and audit trails to every tool your agent calls.
Works with Claude Agent SDK, Vercel AI SDK, and OpenAI SDK out of the box.
Install
npm install -g @agentlattice/instrument
Requires Node.js 18+. Dependencies: ts-morph, commander.
Quick Start
# See what al-instrument-ts would change (diff to stdout)
al-instrument-ts agent.ts
# Apply the changes
al-instrument-ts agent.ts --apply
# Apply + register the agent + verify governance wiring
al-instrument-ts agent.ts --apply --register --test
The diff is the tutorial. Review the generated code to understand exactly what governance means for your agent.
What It Does
al-instrument-ts runs a five-stage pipeline on your TypeScript file:
1. Analyze
Uses ts-morph (TypeScript AST) to find every AI agent tool definition in your code. Three SDK patterns are detected:
// Pattern 1: Claude Agent SDK — tool() call
import { tool } from "@anthropic-ai/claude-agent-sdk";
const searchTool = tool("search_database", "Search the customer database", {
query: z.string(),
}, async ({ query }) => {
return db.search(query);
});
// Pattern 2: Vercel AI SDK — tool({ description, parameters, execute })
import { tool } from "ai";
const emailTool = tool({
description: "Send an email to a customer",
parameters: z.object({ to: z.string(), subject: z.string(), body: z.string() }),
execute: async ({ to, subject, body }) => {
return emailClient.send(to, subject, body);
},
});
// Pattern 3: OpenAI SDK — function tool definitions
const tools = [
{
type: "function" as const,
function: {
name: "delete_records",
description: "Delete records from the database",
parameters: {
type: "object",
properties: {
ids: { type: "array", items: { type: "string" } },
},
},
},
},
];
Detection is deterministic. No LLM involved. Handles aliased imports (import { tool as myTool } from "ai").
2. Classify
Each detected tool is classified with an action type and risk level using the {domain}.{verb} taxonomy.
Domains: api, code, db, email, file, payment, system, user, web
Risk is determined by the verb, not the domain:
| Verb | Risk level | Default policy |
|---|---|---|
query |
read | auto-approve |
read |
read | auto-approve |
write |
write | log |
send |
write | log |
execute |
write | log |
create |
write | log |
update |
write | log |
delete |
delete | require approval |
So db.query is read risk, db.delete is delete risk, and system.execute is write risk. The domain provides context for audit trails and policy rules, but the verb drives the default risk level.
If ANTHROPIC_API_KEY or OPENAI_API_KEY is set, classification uses an LLM for intelligent action type assignment. Without an API key, it falls back to tool.{name} (e.g., tool.search_database) with a default risk level of write. The tool.* namespace is outside the standard taxonomy and won't match domain-specific policies until you reclassify.
3. Generate
Transforms your source code using ts-morph (preserving AST structure and indentation):
Claude Agent SDK and Vercel AI SDK get a gate() call injected at the top of the handler function:
// BEFORE
import { tool } from "@anthropic-ai/claude-agent-sdk";
const searchTool = tool("search_database", "Search the customer database", {
query: z.string(),
}, async ({ query }) => {
return db.search(query);
});
// AFTER
import { tool } from "@anthropic-ai/claude-agent-sdk";
import { AgentLattice } from "@agentlattice/sdk";
const al = new AgentLattice({ apiKey: process.env.AL_API_KEY ?? "" });
const searchTool = tool("search_database", "Search the customer database", {
query: z.string(),
}, async ({ query }) => {
await al.gate("db.query"); // AgentLattice: read-only database access, auto-approved by default policy
return db.search(query);
});
OpenAI SDK tools are data-only definitions (JSON Schema, no handler function). al-instrument-ts emits TODO comments pointing you to where gate() calls should go in your dispatch handler:
// TODO(al-instrument): Add gate() calls in your tool dispatch handler:
// case "delete_records": await al.gate("db.delete"); break; // destructive database operation, requires approval
4. Register (optional)
With --register, al-instrument-ts registers your agent with the AgentLattice API and writes the API key to your .env file. If AL_API_KEY is already present in .env, the file is not modified.
AL_SERVICE_KEY=sk-svc-... al-instrument-ts agent.ts --apply --register
You can also pass the key directly:
al-instrument-ts agent.ts --apply --register --service-key sk-svc-...
5. Verify (optional)
With --test, al-instrument-ts calls the AgentLattice API for each action type to verify that governance policies are correctly wired:
al-instrument-ts agent.ts --apply --register --test
The verifier checks that each action type gets a policy decision from AgentLattice, confirming that the governance pipeline is live end-to-end. In CI mode (--ci), any verification failure returns exit code 1.
CLI Reference
al-instrument-ts <file> [options]
Arguments:
file TypeScript file containing AI agent tool definitions
Options:
--apply Write instrumented code back to the file (default: show diff)
--register Register the agent with AgentLattice (requires --apply)
--test Verify governance wiring (requires --register)
--service-key <key> AL_SERVICE_KEY for registration (default: $AL_SERVICE_KEY)
--agent-name <name> Agent name for registration (default: derived from filename)
--base-url <url> AgentLattice API base URL (default: https://www.agentlattice.io)
--ci CI mode: non-interactive, exit codes only
Idempotency
Running al-instrument-ts on an already-instrumented file is safe. It detects the @agentlattice/sdk import and skips with a message:
File already has AgentLattice governance (@agentlattice/sdk imported). Skipping.
Report Output
After every run, al-instrument-ts prints a structured report:
════════════════════════════════════════════════════
al-instrument-ts report
════════════════════════════════════════════════════
TOOLS FOUND: 3
search_database → db.query (read, auto-approved) [claude-agent]
send_email → email.send (write, requires approval) [vercel-ai]
delete_records → db.delete (delete, requires approval) [openai]
CLASSIFICATION: LLM-assisted (Claude)
CHANGES: written to agent.ts
ACTION REQUIRED (1 item):
1. agent.ts:42 — TODO(al-instrument): Add gate() calls
in your tool dispatch handler
NEXT STEPS:
1. Review the diff: git diff agent.ts
2. Fix the TODO above (add gate() calls to OpenAI dispatch)
3. Set AL_API_KEY in your environment
4. Run your agent — governance is active
5. Visit https://www.agentlattice.io/dashboard to configure policies
════════════════════════════════════════════════════
Supported SDKs
| SDK | Pattern | Governance injection |
|---|---|---|
| Claude Agent SDK | tool("name", "desc", schema, handler) |
gate() injected at top of handler |
| Vercel AI SDK | tool({ description, parameters, execute }) |
gate() injected at top of execute |
| OpenAI SDK | { type: "function", function: { name, ... } } |
TODO comments (data-only, no handler) |
How It Fits Together
al-instrument-ts generates code that uses the @agentlattice/sdk TypeScript package:
- @agentlattice/sdk provides the
AgentLatticeclient andgate()method that communicates with the API
After instrumentation, every tool call flows through: Tool invoked → al.gate() checks authorization → AgentLattice evaluates policy → Approved/Denied → Tool executes or throws error.
Configure policies, approval flows, and audit rules in the AgentLattice dashboard.