LoopGuard Documentation
LoopGuard is a runtime guardrail SDK for autonomous AI agents. It wraps your LLM tool calls and enforces limits locally — no network overhead on the happy path.
Installation
npm install @loopguard/sdk
Also available via pnpm or yarn:
pnpm add @loopguard/sdk yarn add @loopguard/sdk
Getting started
Four steps to go from zero to a guarded agent connected to the dashboard.
Go to chainpass.app/dashboard — use the "Generate a new key" tab to create one in your browser, or run this curl command. The key is shown once, so save it.
curl -X POST https://chainpass.app/api/lg/keys \
-H 'Content-Type: application/json' \
-d '{"userId": "your@email.com"}'
# { "apiKey": "lg_abc123..." }In a terminal inside your own project folder (not the LoopGuard repo), run one of:
npm install @loopguard/sdk # or: pnpm add @loopguard/sdk | yarn add @loopguard/sdk
You don't need a new file — add these lines to your existing agent. Replace fetchData with the actual async function your agent calls:
import { createGuard, LoopGuardError } from "@loopguard/sdk";
const guard = createGuard({
agentId: "my-agent", // shown in the dashboard
apiKey: process.env.LOOPGUARD_API_KEY,
loopDepthLimit: 50, // stop after 50 tool calls
tokenBudget: 100_000, // stop after 100k tokens
});
// Replace fetchData with your actual tool function
const safeFetch = guard.wrapTool(fetchData, { name: "fetch_data" });
// Use safeFetch exactly like fetchData — same signature
const result = await safeFetch(myArgs);Set the env var and run as normal. When a limit trips, your agent appears in the dashboard:
LOOPGUARD_API_KEY=lg_abc123... node your-agent.js
Note: Agents appear in the dashboard only when a limit is tripped — not on every tool call. Before integrating the SDK, use the Send test event button on the Agents page to verify your key is working — no code required.
Quick start
Minimal working example with dashboard connection:
import { createGuard, LoopGuardError } from "@loopguard/sdk";
const guard = createGuard({
agentId: "doc-processor",
apiKey: process.env.LOOPGUARD_API_KEY, // connects to dashboard
loopDepthLimit: 50, // stop after 50 tool calls
tokenBudget: 100_000, // stop after 100k tokens
perToolCaps: { web_search: 20 },
});
const search = guard.wrapTool(webSearch, { name: "web_search" });
try {
await runAgentLoop({ search });
} catch (err) {
if (err instanceof LoopGuardError) {
console.log("Stopped:", err.tripReason, err.currentValue, "/", err.limit);
}
}Configuration
All options passed to createGuard(config):
wrapTool
Wraps any async function. Before each call, increments hop count and checks all limits. After the call, extracts token usage from the return value if present.
const safeFetch = guard.wrapTool(fetchData, { name: "fetch_data" });
// safeFetch has the same signature as fetchData
const result = await safeFetch(url);If a limit is tripped, wrapTool throws a LoopGuardError before calling the underlying function.
wrapClient
Wraps an OpenAI or Anthropic client. Intercepts .chat.completions.create() and .messages.create() automatically.
import OpenAI from "openai";
const raw = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const client = guard.wrapClient(raw);
// Use client exactly like the original
const response = await client.chat.completions.create({
model: "gpt-4o",
messages: [{ role: "user", content: "Hello" }],
});Kill switch
Call guard.kill() to immediately stop all future tool calls for this agent. The killed flag is checked before every wrapped call.
// Kill the current session
guard.kill();
// Kill all sessions for this agent
guard.kill("all");With an apiKey set, the remote kill switch is also checked before each call via GET /api/lg/check (200ms timeout, fails open). Flip it from the dashboard without redeploying.
Alerts
Pass a Slack or Discord webhook URL to get notified instantly when a limit trips:
const guard = createGuard({
agentId: "my-agent",
loopDepthLimit: 50,
alertWebhook: process.env.SLACK_WEBHOOK_URL,
});Webhook failures are swallowed silently — alerts never block agent execution. The payload is Slack-compatible JSON with an emoji-prefixed text field.
Control plane
Add your API key to enable cloud features: remote kill switch, event ingestion, and replay logs (Pro).
const guard = createGuard({
agentId: "my-agent",
apiKey: process.env.LOOPGUARD_API_KEY,
loopDepthLimit: 50,
});Generate an API key:
curl -X POST https://chainpass.app/api/lg/keys \
-H "Content-Type: application/json" \
-d '{"userId": "your@email.com"}'With an API key set, the SDK calls GET /api/lg/check before each tool execution and POST /api/lg/event on every trip. Both calls have a hard 200ms timeout and fail open — your agent is never blocked by a network issue.
Vercel AI SDK
Two helpers for users of the ai package. Install it in your project first:
npm install ai @ai-sdk/openai
guardTools(tools, guard)
Wraps every tool's execute function with the guard. Drop-in replacement for the tools record passed to generateText or streamText. Enforces loopDepthLimit, perToolCaps, and the kill flag.
import { generateText, tool } from "ai";
import { openai } from "@ai-sdk/openai";
import { z } from "zod";
import { createGuard, guardTools, LoopGuardError } from "@loopguard/sdk";
const guard = createGuard({
agentId: "my-agent",
apiKey: process.env.LOOPGUARD_API_KEY,
loopDepthLimit: 50,
tokenBudget: 100_000,
});
const tools = guardTools({
searchWeb: tool({
description: "Search the web",
parameters: z.object({ query: z.string() }),
execute: async ({ query }) => fetchSearchResults(query),
}),
fetchPage: tool({
description: "Fetch a web page",
parameters: z.object({ url: z.string() }),
execute: async ({ url }) => fetchPage(url),
}),
}, guard);
try {
const { text } = await generateText({
model: openai("gpt-4o"),
tools,
prompt: "Research the latest AI news and summarize it.",
});
} catch (err) {
if (err instanceof LoopGuardError) {
console.log("Stopped:", err.tripReason, err.currentValue, "/", err.limit);
}
}loopguardMiddleware(guard)
A LanguageModelV1Middleware that intercepts every generateText / streamTextcall. Checks the kill flag before each call and feeds token usage from the response into the guard's tokenBudget. Use alongside guardTools for full coverage.
import { wrapLanguageModel } from "ai";
import { openai } from "@ai-sdk/openai";
import { loopguardMiddleware } from "@loopguard/sdk";
const model = wrapLanguageModel({
model: openai("gpt-4o"),
middleware: loopguardMiddleware(guard),
});
// Use model exactly like the original — token usage is tracked automatically
const { text } = await generateText({ model, tools, prompt });API reference
Control plane endpoints. All require Authorization: Bearer lg_....