Safety guardrails for tool-using AI agents. Evaluate every tool call as allow / deny / quarantine — with spend limits, allowlists, command-safety checks, path protection, rate limits, and prompt-injection defense via authority levels. Zero runtime dependencies, pure TypeScript, works with any agent framework (LangChain, the Vercel AI SDK, the Anthropic/OpenAI SDKs, or your own loop).
A policy engine and safety guardrails for tool-using AI agents — evaluate every tool call as allow / deny / quarantine with spend limits, allowlists, command-safety, path protection, rate limits, and authority levels.
Autonomous, tool-using LLM agents can spend real money, run shell commands, modify their own files, and act on untrusted input. A single prompt injection or a runaway loop can drain a treasury or wreck a machine. agent-policy-engine puts a deterministic checkpoint in front of every tool call:
- Three outcomes, not two. Each call resolves to
allow,deny, orquarantine(needs human confirmation) — so high-value actions can pause instead of being silently blocked or blindly executed. - Spend limits. Per-transfer caps, hourly/daily rolling windows, per-turn transfer counts, and inference budgets stop iterative credit drain.
- Allowlists. x402 / payment domains must be explicitly allowlisted; an empty allowlist disables payments entirely (deny-by-default).
- Command safety. Shell-metacharacter injection detection plus a forbidden-command pattern list (self-destruction, credential harvesting, DB destruction).
- Path protection. Deny writes to protected files, reads of secrets (wallets,
.env, keys), and path-traversal escapes. - Prompt-injection defense via authority levels. Input from untrusted/external sources is derived to a lower authority and blocked from destructive tools and protected-file self-modification.
- First-deny-wins. Rules are priority-ordered; the first
denystops evaluation, so a cheap validation check short-circuits before an expensive one.
npm i github:Princeu3/agent-policy-enginenpm registry release coming soon. (Until then, install straight from GitHub.)
import {
PolicyEngine,
createDefaultRules,
SpendTracker,
} from "agent-policy-engine";
// Build an engine with the batteries-included rule set.
const engine = new PolicyEngine(createDefaultRules());
const sessionSpend = new SpendTracker(); // in-memory by default
// Evaluate a tool call BEFORE you execute it.
const decision = engine.evaluate({
tool: { name: "transfer_credits", category: "financial", riskLevel: "dangerous" },
args: { to_address: "0x1234567890123456789012345678901234567890", amount_cents: 6000 },
turnContext: {
inputSource: "agent", // where this turn's input came from
turnToolCallCount: 0,
sessionSpend,
},
});
if (decision.action === "deny") {
throw new Error(`${decision.reasonCode}: ${decision.humanMessage}`);
} else if (decision.action === "quarantine") {
// pause for human confirmation
} else {
// allow: execute the tool, then record the spend
sessionSpend.recordSpend({ toolName: "transfer_credits", amountCents: 6000, category: "transfer" });
}Tune the limits with a custom treasury policy:
import { createDefaultRules, DEFAULT_TREASURY_POLICY } from "agent-policy-engine";
const rules = createDefaultRules({
...DEFAULT_TREASURY_POLICY,
maxSingleTransferCents: 2000,
x402AllowedDomains: ["api.example.com"],
});rules: PolicyRule[]— evaluated in ascendingpriority.store?: PolicyStore— optional sink for decision audit rows (recordDecision(row)). Defaults to a no-op, so the engine runs with zero wiring. Inject your own to persist to a DB.
Runs applicable rules in priority order. First deny wins and stops evaluation. A quarantine is remembered but does not stop other rules (a later deny can still override it). Returns the overall action, reasonCode, humanMessage, derived authorityLevel, a SHA-256 argsHash, and the lists of rules evaluated/triggered.
Persists a decision via the injected store. Never throws — logging failures can't block tool execution.
heartbeat / undefined → external; creator / agent → agent; system / wakeup → system. External input is the low-trust bucket used by the authority rules.
Returns the full bundled rule set: validation, command-safety, path-protection, financial, authority, and rate-limit rules. Each factory (createValidationRules, createCommandSafetyRules, createPathProtectionRules, createFinancialRules, createAuthorityRules, createRateLimitRules) is also exported so you can compose your own set.
Hourly/daily window spend aggregation used by the financial rules. InMemorySpendStore is the default; implement SpendStore to back it with a real database. checkLimit(amount, category, policy) returns whether an amount fits the hourly and daily caps.
Rate-limit rules count recent allowed decisions via the optional PolicyContext.recentDecisionCount(toolName, windowMs) hook on the request. Wire it to your decision store to activate; when absent, rate-limit rules stay inactive rather than failing closed.
Helper predicates are also exported: isForbiddenCommand, getForbiddenCommandMatch, isProtectedFile, isSensitiveFile, DEFAULT_PROTECTED_FILES, and DEFAULT_TREASURY_POLICY.
Extracted, refactored, and decoupled by Prince Upadhyay (@Princeu3) from the MIT-licensed Conway-Research/automaton project. The extraction removes project-specific coupling (Conway APIs, SQLite schema, internal types) and re-packages the reusable guardrail logic as a standalone, zero-dependency library. See NOTICE for details.
ai-agents · llm-agents · guardrails · policy-engine · tool-use · agent-safety · spend-limits · prompt-injection · authorization · allowlist · rate-limiting · autonomous-agents · agent-security · typescript · llm
MIT © 2026 Prince Upadhyay