npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@pallattu/aeg-intent-gate

v0.12.0

Published

A tiny TypeScript approval gate for AI tool calls, MCP tools, and human-in-the-loop workflows.

Readme

aeg-intent-gate

npm version license dependencies

Add an approval gate before AI tool calls hit your real executor.

aeg-intent-gate is a tiny TypeScript approval gate for AI agents, OpenAI function calls, Anthropic tool use, and MCP tools. Use it to block dangerous actions, require human approval for risky actions, emit audit-friendly lifecycle events, and only create executable commands after a decision is approved.

Live demo: aeg-intent-gate.pages.dev

Live starter app: aeg-intent-gate-starter.pages.dev

aeg-intent-gate approval flow

Install

npm install @pallattu/aeg-intent-gate

Try the demo without writing code:

npx @pallattu/aeg-intent-gate

Clone a complete starter app:

git clone https://github.com/jacob-git/aeg-intent-gate-starter.git
cd aeg-intent-gate-starter
npm install
npm start

Run a local approval queue in the browser:

npm run example:approval-server

Run a durable approval queue example:

npm run example:durable-queue

Quick Start: Gate An AI Tool Call

import { createIntentGate, createPolicy, gateToolCall } from "@pallattu/aeg-intent-gate";

const refundAmount = (metadata?: Record<string, unknown>) => {
  const args = metadata?.args as { amount?: number } | undefined;
  return args?.amount ?? 0;
};

const gate = createIntentGate({
  agent: {
    agentId: "support-agent",
    capabilities: ["ticket.read", "refund.create", "email.send"],
  },
  policies: [
    createPolicy({
      name: "large-refunds-need-human",
      match: (intent) => intent.type === "refund.create" && refundAmount(intent.metadata) > 100,
      evaluate: () => ({
        outcome: "requires_approval",
        reason: "Refunds above $100 require human approval.",
      }),
    }),
    createPolicy({
      name: "never-delete-users",
      match: (intent) => intent.type === "user.delete",
      evaluate: () => ({
        outcome: "blocked",
        reason: "Agents cannot delete users.",
      }),
    }),
  ],
});

const result = await gateToolCall(gate, {
  tool: "refund.create",
  target: "stripe",
  args: {
    customerId: "cus_123",
    amount: 250,
    reason: "Duplicate charge.",
  },
});

if (result.decision.outcome === "requires_approval") {
  const approved = await gate.approveIntent(result.intent, result.decision, {
    approvedBy: "human_operator",
    reason: "Customer history reviewed.",
  });

  const command = gate.toCommand(result.intent, approved);
  // Pass command to your executor.
}

Copy-Paste Adapters

The fastest way to adopt the gate is to wrap the tool-call object you already receive from your model or agent runtime.

OpenAI Responses API Function Calls

OpenAI function calls include a name, JSON-encoded arguments, and a call_id. Gate the call before routing it to real application code:

import { gateOpenAIToolCall } from "@pallattu/aeg-intent-gate";

const result = await gateOpenAIToolCall(gate, {
  id: "fc_123",
  call_id: "call_123",
  type: "function_call",
  name: "email.send",
  arguments: JSON.stringify({
    to: "[email protected]",
    subject: "Refund update",
  }),
}, {
  target: "postmark",
});

if (result.command) {
  await execute(result.command);
}

OpenAI Chat Completions Tool Calls

import { gateOpenAIToolCall } from "@pallattu/aeg-intent-gate";

const result = await gateOpenAIToolCall(gate, {
  id: "call_456",
  type: "function",
  function: {
    name: "ticket.create",
    arguments: JSON.stringify({ title: "Refund request" }),
  },
});

Anthropic Tool Use

import { gateAnthropicToolUse } from "@pallattu/aeg-intent-gate";

const result = await gateAnthropicToolUse(gate, {
  id: "toolu_123",
  type: "tool_use",
  name: "service.restart",
  input: {
    service: "api",
    region: "us-central1",
  },
}, {
  target: "production",
});

MCP Tool Calls

import { gateMcpToolCall } from "@pallattu/aeg-intent-gate";

const result = await gateMcpToolCall(gate, {
  server: "local-dev",
  name: "filesystem.write",
  arguments: {
    path: "/tmp/demo.txt",
    content: "hello",
  },
});

By default, unmatched actions require approval. If you want fail-open behavior for a trusted local workflow, opt in explicitly:

const gate = createIntentGate({
  agent,
  policies,
  fallbackDecision: { outcome: "approved" },
});

When To Use This

Use this package when an AI agent can propose actions with side effects:

  • OpenAI or Anthropic tool calls
  • MCP tool execution
  • shell commands
  • database writes
  • refunds, credits, or payments
  • emails and customer messages
  • deploys, restarts, or admin actions

This package is not a full policy engine, agent framework, queue, database, sandbox, identity provider, or durable audit log. It is the small runtime boundary before execution.

Security Model

aeg-intent-gate protects command construction inside the process where you use it. It verifies that an executable command comes from an approved decision produced by the same gate instance for the same evaluated intent.

The gate snapshots the sanitized command payload when an intent is evaluated. If caller code later mutates intent metadata, toCommand() still returns the evaluated payload snapshot.

Your application is still responsible for:

  • authenticating agents and human approvers
  • persisting audit records and approval history
  • isolating or sandboxing executors
  • making side-effecting code accept only ApprovedCommand objects
  • preventing callers from bypassing the gate and invoking executors directly

Treat this package as an in-process enforcement point, not as a complete security boundary by itself.

Examples

Complete browser starter app:

https://github.com/jacob-git/aeg-intent-gate-starter

Live starter app:

https://aeg-intent-gate-starter.pages.dev

Live hosted demo:

https://aeg-intent-gate.pages.dev

Run the lifecycle example:

npm run example

Run a tool-call approval example:

npm run example:tool-call

Run an OpenAI-style function-call approval example:

npm run example:openai

Run an Anthropic-style tool-use approval example:

npm run example:anthropic

Run an MCP-style tool gate example:

npm run example:mcp

Run an MCP proxy starter that gates tools/call requests before forwarding:

npm run example:mcp-proxy

Run a local browser approval queue:

npm run example:approval-server

Run a durable approval queue example that persists pending approvals and approves after a simulated restart:

npm run example:durable-queue

More integration notes are in docs/INTEGRATIONS.md. Cloudflare Pages setup notes are in docs/CLOUDFLARE_PAGES.md. Deployment examples are in docs/DEPLOYMENT.md. Framework examples are in examples/frameworks. MCP proxy starter is in examples/mcp-proxy-starter.mjs. OpenAI Agents SDK approval example is in examples/frameworks/openai-agents-sdk-approval-gate.ts. Roadmap items are in docs/ROADMAP.md. Launch asset instructions are in docs/LAUNCH_ASSETS.md. Launch screenshots and GIFs are in docs/assets.

Core Lifecycle

The runtime models an action as an intent lifecycle:

proposed -> evaluated -> approved | blocked | requires_approval -> approved

Execution remains separate:

Tool Call -> Intent -> Policy Decision -> ApprovedCommand -> Executor

An Intent describes what an agent wants to do. proposeIntent() assigns an id and marks it as proposed. evaluateIntent() checks agent capabilities first, then evaluates matching policies in order. approveIntent() converts a requires_approval decision into an approved decision after a human or external system approves it. toCommand() only creates an executable command from an approved decision produced by the same gate instance.

API

gateToolCall(gate, toolCall)

Gates a familiar tool-call shape and returns the intent, decision, and optional command.

const result = await gateToolCall(gate, {
  tool: "email.send",
  target: "postmark",
  args: {
    to: "[email protected]",
    subject: "Deployment finished",
  },
});

if (result.command) {
  await execute(result.command);
}

requestedCapabilities defaults to [tool], and target defaults to tool.

gateOpenAIToolCall(gate, toolCall, options?)

Gates OpenAI Responses API function calls and Chat Completions tool calls.

const result = await gateOpenAIToolCall(gate, openAIToolCall, {
  target: "stripe",
  requestedCapabilities: ["refund.create"],
});

Responses API calls use call_id as the intent id when present. Chat Completions calls use the tool call id. JSON arguments must decode to an object.

gateAnthropicToolUse(gate, toolUse, options?)

Gates Anthropic tool-use blocks.

const result = await gateAnthropicToolUse(gate, toolUse, {
  target: "production",
});

gateMcpToolCall(gate, toolCall, options?)

Gates MCP-style tool calls.

const result = await gateMcpToolCall(gate, {
  server: "local-dev",
  name: "shell.exec",
  arguments: { command: "npm test" },
});

createIntentGate(config)

Creates an intent gate.

const gate = createIntentGate({
  agent: {
    agentId: "agent_123",
    capabilities: ["logs.read"],
  },
  policies: [],
  fallbackDecision: { outcome: "requires_approval" },
  onEvent: (event) => {},
});

Config fields:

  • agent: agent identity and granted capabilities.
  • policies: ordered policy list. The first matching policy returns the decision.
  • fallbackDecision: optional decision when no policy matches. Defaults to requires_approval.
  • onEvent: optional in-memory lifecycle event listener.

createPolicy({ match, evaluate })

Defines a typed policy. match and evaluate can be synchronous or asynchronous.

const policy = createPolicy({
  name: "restart-requires-approval",
  match: (intent) => intent.type === "service.restart",
  evaluate: () => ({ outcome: "requires_approval" }),
});

gate.proposeIntent(intent)

Validates an intent, assigns an id when needed, sets status to proposed, and emits IntentProposed.

const proposed = await gate.proposeIntent({
  type: "logs.read",
  target: "api",
  requestedCapabilities: ["logs.read"],
});

gate.evaluateIntent(intent)

Evaluates a proposed intent and returns a decision.

const decision = await gate.evaluateIntent(proposed);

If the agent lacks a requested capability, the decision is blocked before custom policies run. Evaluation emits IntentEvaluated followed by IntentApproved, IntentBlocked, or ApprovalRequired.

gate.approveIntent(intent, decision, approval)

Approves an intent that previously returned requires_approval.

const approved = await gate.approveIntent(proposed, decision, {
  approvedBy: "human_1",
  reason: "Approved for the maintenance window.",
});

The original decision must have been produced by this gate for the same intent.

gate.toCommand(intent, decision)

Creates an executable command only from an approved lifecycle decision.

if (decision.outcome === "approved") {
  const command = gate.toCommand(proposed, decision);
}

toCommand() throws if the decision is blocked, requires approval, was not produced by this gate, or does not match the evaluated intent. The command payload is sanitized JSON derived from intent metadata.

Events

The gate emits lifecycle events through onEvent:

  • IntentProposed
  • IntentEvaluated
  • IntentApproved
  • IntentBlocked
  • ApprovalRequired
  • IntentApprovalGranted

Events include an id, timestamp, intent id, current intent status, optional decision, and metadata. Events are delivered in memory and are not persisted.

Why This Exists

AI agents often produce structured requests that look executable. Treating those requests as commands too early makes authorization, auditability, and human approval harder to enforce.

aeg-intent-gate keeps proposal, policy evaluation, approval, lifecycle events, and execution handoff as separate steps. That gives applications a small guardrail layer without introducing a server, database, or framework.