@imladri/sdk
v0.1.0
Published
Imladri TypeScript SDK for wrapping agent actions, enforcing policy, and producing proof evidence.
Maintainers
Readme
@imladri/sdk
TypeScript SDK for Imladri agent enforcement, signed audit logging, and constitution publishing.
Installation
npm install @imladri/sdkNode.js 18+ is required. The package ships with zero runtime dependencies.
Quickstart: one control-surface policy
Start new integrations with one typed policy covering action enforcement, governed database branches, protected compute, and agent identity. That is the default path for producing a buyer proof packet with one constitution.
import { ConstitutionBuilder } from "@imladri/sdk";
const constitution = new ConstitutionBuilder({ agentId: "credit-servicing-agent" })
.mission("Service credit cases without unsafe money, data, or compute actions.")
.defaultUnknownAction("REVIEW")
.controlSurfacePolicy({
actions: {
allow: ["case.read"],
review: ["credit.limit.change"],
block: ["payment.transfer", "customer.pii.export"],
},
data: {
actionId: "db.branch.proof",
targetId: "credit-ledger",
allowedTables: ["public.accounts", "public.cases"],
redactedColumns: ["email", "ssn", "access_token"],
requiredWorkspaceFilterColumns: ["workspace_id"],
},
compute: {
actionId: "protected_compute.run",
providerClasses: ["h100"],
maxRuntimeSeconds: 3900,
maxSpendUsd: 30,
},
identity: {
organizationId: "org_123",
workspaceId: "risk-review",
maxDelegationDepth: 1,
},
})
.build("2026.6.1");ConstitutionalAgent
import { ConstitutionalAgent, ConstitutionalViolationError } from "@imladri/sdk";
const agent = new ConstitutionalAgent({
agentId: "support-agent",
apiKey: process.env.IMLADRI_API_KEY!,
apiUrl: process.env.IMLADRI_API_URL!,
preflightUrl: process.env.IMLADRI_PREFLIGHT_URL,
constitution: {
prohibited: ["delete_customer"],
allowed: ["read_customer", "send_email"],
},
pollInterval: 5,
});
const readCustomer = agent.action("read_customer", async (customerId: string) => {
return { id: customerId };
});
try {
await readCustomer("cust_123");
} catch (err) {
if (err instanceof ConstitutionalViolationError) {
console.error(err.message);
}
}If constitution is omitted, the agent fetches from Constitution Service and keeps polling for updates.
Control-surface policy details
Use controlSurfacePolicy when the same agent must prove action enforcement,
database branching, protected compute, and identity authority under one
constitution.
import { ConstitutionBuilder } from "@imladri/sdk";
const constitution = new ConstitutionBuilder({ agentId: "credit-servicing-agent" })
.mission("Service credit cases without unsafe money, data, or compute actions.")
.defaultUnknownAction("REVIEW")
.controlSurfacePolicy({
actions: {
allow: ["case.read"],
review: ["credit.limit.change"],
block: ["payment.transfer", "customer.pii.export"],
},
data: {
actionId: "db.branch.proof",
targetId: "credit-ledger",
allowedTables: ["public.accounts", "public.cases"],
redactedColumns: ["email", "ssn", "access_token"],
requiredWorkspaceFilterColumns: ["workspace_id"],
},
compute: {
actionId: "protected_compute.run",
providerClasses: ["h100"],
maxRuntimeSeconds: 3900,
maxSpendUsd: 30,
},
identity: {
organizationId: "org_123",
workspaceId: "risk-review",
maxDelegationDepth: 1,
},
})
.build("2026.6.1");For high-risk side effects, keep IMLADRI_API_URL on the normal signed evidence endpoint and set IMLADRI_PREFLIGHT_URL to the direct Glasshouse /api/v1/action-preflight endpoint. If that endpoint is Cloudflare Access protected, provide preflightHeaders or set IMLADRI_PREFLIGHT_ACCESS_CLIENT_ID / IMLADRI_PREFLIGHT_ACCESS_CLIENT_SECRET. Then enable strict preflight on the wrapped action:
const transfer = agent.action(
"payment.transfer",
async (amountUsd: number) => paymentProvider.transfer(amountUsd),
{
strictPreflight: true,
intent: (amountUsd) => ({ amountUsd }),
},
);If Glasshouse returns AGENT_HALTED, the SDK latches that halt locally so later guarded calls block without another network round trip. Call agent.clearLocalHalt() only after an operator has explicitly unhalted the agent.
Boundary caveat: capabilities not routed through the SDK boundary are observable and haltable, but not pre-execution preventable. Dangerous capabilities should be wrapped with strictPreflight, wrapped tools, sandbox/database action helpers, or an explicit staged agent.preflight(intent) before commit.
Framework adapters
The SDK ships dependency-light adapters for common tool shapes. They do not import LangChain, Vercel AI SDK, OpenAI Agents, CrewAI, LlamaIndex, LangGraph, Semantic Kernel, AutoGen/AG2, Haystack, Mastra, Dify, Flowise, n8n, Zapier AI, or Botpress; your application brings those frameworks and Imladri wraps the tool boundary.
import { ConstitutionalAgent, wrapVercelAITools, wrapLangChainTools } from "@imladri/sdk";
const agent = new ConstitutionalAgent({
agentId: "support-agent",
apiKey: process.env.IMLADRI_API_KEY!,
apiUrl: process.env.IMLADRI_API_URL!,
preflightUrl: process.env.IMLADRI_PREFLIGHT_URL,
constitution: {
allowed: ["email.send"],
prohibited: ["payment.transfer"],
},
});
const tools = wrapVercelAITools(agent, {
sendEmail: {
execute: async (args: { to: string }) => ({ sent: args.to }),
},
}, {
strictTools: ["email.send"],
actionAliases: { sendEmail: "email.send" },
});For object-style tool or node maps, the map key is used as the framework action
name for plain functions. That lets wrappers such as wrapLangGraphNodes guard
a generic handler function under a framework-safe key:
const nodes = wrapLangGraphNodes(agent, {
customer_lookup: async function handler(state) {
return state;
},
}, {
strictTools: ["customer.lookup"],
actionAliases: { customer_lookup: "customer.lookup" },
});For Vercel AI SDK projects, actionAliases lets you keep JavaScript-safe tool
keys such as sendEmail in the SDK tools object while enforcing the dotted
Imladri policy action email.send.
For LlamaIndex TypeScript, wrapLlamaIndexTools resolves FunctionTool
metadata.name before wrapping. Use actionAliases when that metadata name is
framework-safe but the Imladri policy action is dotted:
const guarded = wrapLlamaIndexTools(agent, llamaTools, {
strictTools: ["customer.data.export"],
actionAliases: { customerExport: "customer.data.export" },
});For CrewAI-shaped TypeScript tool objects, wrapCrewAITools guards run,
_run, invoke, and func execution methods. Use actionAliases when a
framework-safe tool name should enforce a dotted Imladri action:
const guarded = wrapCrewAITools(agent, crewAiShapedTools, {
strictTools: ["customer.data.export"],
actionAliases: { customerExport: "customer.data.export" },
});For Haystack-shaped TypeScript component objects, wrapHaystackComponents
guards run and run_component execution methods. Use actionAliases when a
component key such as customer_export should enforce a dotted action:
const guarded = wrapHaystackComponents(agent, {
customer_export: customerExportComponent,
}, {
strictTools: ["customer.data.export"],
actionAliases: { customer_export: "customer.data.export" },
});Available helpers:
wrapImladriTool/wrapImladriToolswrapLangChainToolscreateLangChainMiddlewarewrapVercelAIToolswrapOpenAIAgentsToolsnormalizeOpenAIAgentsToolNamewrapCrewAIToolswrapLlamaIndexToolswrapLangGraphTools/wrapLangGraphNodeswrapSemanticKernelFunctionswrapAutoGenToolswrapHaystackComponentswrapMastraToolswrapDifyToolswrapFlowiseToolswrapN8nToolswrapZapierAIToolswrapBotpressToolscreateLangChainCallbackHandlercreateOpenAIAgentsToolGuard
The OpenAI Agents JavaScript SDK normalizes function-tool names such as
payment.transfer to payment_transfer. When the constitution uses dotted
Imladri action names, wrap with actionAliases, for example:
const action = "payment.transfer";
const toolName = normalizeOpenAIAgentsToolName(action);
const guarded = wrapOpenAIAgentsTools(agent, tools, {
strictTools: [action],
actionAliases: { [toolName]: action },
});For LangChain JS agent apps where LangChain owns the tool loop, use
createLangChainMiddleware(agent) as the fail-closed execution boundary.
The callback handler is still useful for direct callback integration, but
current LangChain JS callback machinery consumes handler errors; middleware or
wrapped tools are the enforcement path.
For AutoGen-shaped JavaScript tool objects, wrapAutoGenTools guards run_json,
runJson, and run. Use actionAliases when the local tool name is
framework-safe but the Imladri policy action is dotted:
const guarded = wrapAutoGenTools(agent, autogenTools, {
strictTools: ["customer.lookup"],
actionAliases: {
customer_lookup: "customer.lookup",
transfer_payment: "payment.transfer",
},
});For Semantic Kernel-shaped JavaScript function objects, wrapSemanticKernelFunctions
guards invoke and resolves names from metadata.name, functionName, or
function_name:
const guarded = wrapSemanticKernelFunctions(agent, semanticFunctions, {
strictTools: ["customer.lookup"],
actionAliases: {
customer_lookup: "customer.lookup",
transfer_payment: "payment.transfer",
},
});Adapter coverage has two repo-level certification layers:
node scripts/certify-sdk-adopters.mjs --output logs/sdk-adopter-certification.json
node cli/imladri.mjs sdk certify --real --output logs/sdk-certification-profile-proof.jsonThe first command proves every exported wrapper contract. The second installs
real framework packages in tmp/sdk-real-adopters and smoke-tests the common
local tool objects; hosted or heavyweight runtimes are reported explicitly. Add
--upload --worker-url <worker> --agent-id <agent> --sdk-key <key> to put the
combined proof on the agent Profile.
Delegated authority tokens
Use authority tokens when an MCP server or sub-agent should act under its own short-lived identity instead of receiving the customer's SDK key.
const owner = new ConstitutionalAgent({
agentId: "support-agent",
apiKey: process.env.IMLADRI_API_KEY!,
apiUrl: "https://api.imladri.com/api/v1/agent-log",
constitution: { allowed: ["customer.read"] },
});
const grant = await owner.issueAuthorityToken({
identityType: "mcp_server",
externalId: "github-mcp",
scope: ["action:customer.read"],
ttlSeconds: 600,
});
const mcpServer = owner.withAuthorityToken(grant.token);
await mcpServer.action("customer.read", async () => ({ ok: true }))();
await owner.revokeAuthorityToken(grant.token);Authority-token runtime calls use Authorization: Bearer <token> and do not
send X-API-Key or X-SDK-Signature. The token must be used through the
Worker SDK proxy and must include every action scope the delegated process will
call, for example action:customer.read or action:constitution.fetch.
ConstitutionBuilder
ConstitutionBuilder publishes constitutions to Constitution Service so the SDKs and services can consume the same canonical payload.
Basic example
import { ConstitutionBuilder } from "@imladri/sdk";
const builder = new ConstitutionBuilder({
agentId: "support-agent",
constitutionServiceUrl: "http://localhost:3002",
});
builder
.addProhibited("delete_customer")
.addAllowed("read_customer", "send_email")
.addBaseline("send_email", "Send support follow-ups", {
threshold: 0.8,
severity: "WARN",
});
await builder.publish("1.0.0");Rich policy example
const builder = new ConstitutionBuilder({
agentId: "support-agent",
constitutionServiceUrl: "http://localhost:3002",
serviceKey: process.env.CONSTITUTION_SERVICE_KEY,
});
builder
.mission("Resolve support requests without changing or deleting customer data.")
.defaultUnknownAction("REVIEW")
.addProhibited("delete_customer", "export_all_data")
.addAllowed("read_customer", "send_email")
.allowWithGuardrails("issue_refund", [
"Only for verified customer-owned orders",
"Require human approval above $100",
], {
description: "Issue refunds within stated guardrails",
severity: "BLOCK",
riskLevel: "HIGH",
category: "billing",
})
.monitor("summarize_ticket", {
description: "Monitor ticket summarization quality",
severity: "WARN",
category: "support",
})
.registerAction("send_email", {
label: "Send Email",
description: "Send customer-visible follow-up email",
category: "communications",
riskLevel: "MEDIUM",
tags: ["external", "customer"],
})
.addNote("Escalate policy changes through operator review.");
const payload = builder.build("1.2.0");
await builder.publish("1.2.0");Public builder surface
mission(text)defaultUnknownAction(mode)addBaseline(actionType, description, options?)rule(actionType, description?, severity?)addProhibited(...actionTypes)addAllowed(...actionTypes)registerAction(actionId, definition?)defineAction(actionId, policy)allowWithGuardrails(actionId, guardrails, options?)monitor(actionId, options?)addNote(note)tool(toolName, options?)build(version?)publish(version?)
The built payload matches the current repo contract:
hardBlocksallowedActionsrulespolicyactionRegistryunknownActionMode
ActionRegistryClient
The TypeScript package also exports ActionRegistryClient for operator-side action registry and review queue workflows against Constitution Service.
import { ActionRegistryClient } from "@imladri/sdk";
const registry = new ActionRegistryClient({
constitutionServiceUrl: "http://localhost:3002",
serviceKey: process.env.CONSTITUTION_SERVICE_KEY,
});
await registry.registerAction({
name: "issue_refund",
description: "Issue a customer refund",
classification: "guardrail",
severity: "BLOCK",
classifiedBy: "operator-1",
});
const pending = await registry.getReviewQueue("pending");
const stats = await registry.getReviewStats();Worker-backed violation replay logging
Tool-call violations are logged on a best-effort basis to the Cloudflare worker used by the website/admin surface.
Set these when you want explicit control over that path:
IMLADRI_WORKER_URLIMLADRI_SDK_KEY
If either is unset, the SDK skips worker replay logging entirely and only enforces locally.
Environment variables
IMLADRI_API_URLIMLADRI_AUTHORITY_TOKENIMLADRI_PREFLIGHT_URLIMLADRI_PREFLIGHT_ACCESS_CLIENT_IDIMLADRI_PREFLIGHT_ACCESS_CLIENT_SECRETCONSTITUTION_SERVICE_URLCONSTITUTION_SERVICE_KEYIMLADRI_WORKER_URLIMLADRI_SDK_KEY
Local verification
cd sdk/typescript
npm ci
npm test
npm run build
npm run pack:dry-runnpm test now runs real Node test-runner coverage against the built SDK surface, including builder publish requests, action-registry client writes, and local constitutional enforcement behavior.
Release packaging
The npm package publishes dist and this README only. Release dry run:
cd sdk/typescript
npm ci
npm test
npm run pack:dry-runPublish from a tagged CI release with NPM_TOKEN; the workflow builds first and uses npm provenance.
License
Unlicensed/proprietary. All rights reserved.
