@reallyartificial/approval-protocol-core
v0.1.1
Published
A protocol for AI agent human-in-the-loop approval. What MCP did for tool access, AP does for human oversight.
Maintainers
Readme
@reallyartificial/approval-protocol-core
The safety layer between AI agents and the real world.
What MCP did for tool access, Approval Protocol does for human oversight.
AI agents take actions in the real world — send emails, spend money, modify databases, deploy code. Every agent framework has its own half-baked human-in-the-loop mechanism, all framework-locked, all missing notification channels and escalation.
Approval Protocol (AP) is a protocol — not a library — that standardizes how agents get human permission before executing actions, confirm execution results, and enable rollback when things go wrong.
Install
npm install @reallyartificial/approval-protocol-coreHow It Works
Agent AP Server Human
| | |
|-- ap/negotiate -------->| |
|<-- policies + session --| |
| | |
|-- ap/request ---------->| |
|<-- request_id + mode ---|-- notify --------------->|
| | |
|-- ap/status ----------->| ap/decide <------|
|<-- approved ------------| |
| | |
|-- [execute action] | |
| | |
|-- ap/confirm ---------->| (result + undo info) |
|<-- confirmed -----------| |
| | |
| ap/rollback <---------|--------------------------|
|<-- undo instructions ---| |Quickstart
Server
import { ApprovalServer, CliChannel } from "@reallyartificial/approval-protocol-core";
const server = new ApprovalServer({
port: 4000,
channel: new CliChannel(),
policies: {
send_email: { requires: "always" },
search_kb: { requires: "never" },
issue_refund: { requires: "conditional", when: "params.amount > 100" },
delete_records: { requires: "always", execution: "sandbox" },
},
});
await server.start();Client
import { ApprovalClient } from "@reallyartificial/approval-protocol-core";
const client = new ApprovalClient("http://localhost:4000");
// 1. Negotiate — declare what actions you may take
const session = await client.negotiate("my-agent", ["send_email", "search_kb"]);
// 2. Request approval
const { request_id, status, execution_mode } = await client.request({
action: { name: "send_email", params: { to: "[email protected]", body: "Hello" } },
context: { agent: "my-agent", reason: "Customer requested help" },
risk: { reversible: false, blast_radius: "external" },
});
// 3. Wait for human decision
if (status === "pending") {
const result = await client.waitForDecision(request_id, { timeout: 60_000 });
if (result.status === "approved") {
// 4. Execute the action
const emailResult = sendEmail("[email protected]", "Hello");
// 5. Confirm execution with undo instructions
await client.confirm({
request_id,
success: true,
result: { message_id: emailResult.id },
undo: {
type: "api_call",
description: "Recall the email",
instructions: { method: "POST", url: `/recall/${emailResult.id}` },
},
confirmed_by: "my-agent",
});
}
}
// 6. Later, if something went wrong:
await client.rollback({
request_id,
reason: "Email contained incorrect information",
initiated_by: "[email protected]",
});withApproval Wrapper
For transparent lifecycle management without manual request/confirm calls:
import { withApproval } from "@reallyartificial/approval-protocol-core/client";
const sendEmail = withApproval(
async (to: string, body: string) => { /* actual send logic */ },
{
action: "send_email",
client,
risk: { reversible: false, blast_radius: "external" },
undo: { type: "manual", description: "Contact recipient to disregard" },
}
);
// Approval + execution + confirmation happens transparently
await sendEmail("[email protected]", "Your refund is ready");The Protocol
6 methods over HTTP:
| Method | Purpose |
|--------|---------|
| ap/negotiate | Trust handshake — agent declares capabilities, server returns policies |
| ap/request | Agent requests approval for an action |
| ap/decide | Human submits approval decision (approve / deny / edit) |
| ap/confirm | Agent confirms execution result with undo metadata |
| ap/rollback | Request rollback of a confirmed action |
| ap/status | Check full lifecycle status of any request |
Policies
Per-action rules that control approval behavior:
policies: {
send_email: { requires: "always" }, // always ask a human
search_kb: { requires: "never" }, // auto-approve
issue_refund: { requires: "conditional", when: "params.amount > 100" }, // ask if > $100
delete_records: { requires: "always", execution: "sandbox" }, // ask + sandbox mode
}"always"— human approval required"never"— auto-approved, no human needed"conditional"— evaluate expression against action params and risk metadata
Execution Modes
Policies can specify how the agent should execute:
| Mode | Behavior |
|------|----------|
| live | Normal execution (default) |
| sandbox | Run in isolated/test environment |
| dry_run | Simulate without side effects |
Channels
AP routes approval requests to humans via channels:
- CliChannel — Terminal prompt (built-in, for development and demos)
- WebhookChannel — POST to any URL (Slack bot, email service, custom UI)
import { WebhookChannel } from "@reallyartificial/approval-protocol-core";
const channel = new WebhookChannel({
url: "https://your-slack-bot.example.com/approvals",
});Exports
// Server
import { ApprovalServer, CliChannel, WebhookChannel } from "@reallyartificial/approval-protocol-core";
import { ApprovalServer } from "@reallyartificial/approval-protocol-core/server";
// Client
import { ApprovalClient, withApproval } from "@reallyartificial/approval-protocol-core";
import { ApprovalClient } from "@reallyartificial/approval-protocol-core/client";
// Types
import type { ApPolicy, ApRiskMetadata, ApUndoMetadata, ApChannel } from "@reallyartificial/approval-protocol-core";
// Zod Schemas (for validation in your own code)
import { ApRequestParamsSchema, ApDecideParamsSchema } from "@reallyartificial/approval-protocol-core";Full Specification
See PROTOCOL.md for the complete protocol specification.
Related
- @reallyartificial/approval-protocol-langchain — LangChain integration. Wrap any LangChain tool with AP approval in one function call.
License
MIT
