Paybond agent middleware replaces per-tool guardTool ceremony with a run-scoped layer that binds one intent and capability per agent task, auto-guards registered side-effecting tools, and automatically submits evidence after every successful side-effecting call.
The core lives in @paybond/kit/agent (TypeScript) and paybond_kit.agent (Python). It has zero agent-framework dependencies — OpenAI, Claude, Gemini, LangGraph, MCP, and custom runtimes plug in through optional framework adapters.
When to use middleware vs manual guard
| Approach | Best for |
|---|---|
Agent middleware (PaybondAgentRun + registry) | Multi-tool agents, coding-agent automation, any integration that must not forget evidence on side-effecting tools |
Per-tool guardTool | Single paid handler, legacy code, or advanced cases where you control evidence submission explicitly |
Prefer middleware when building agents with several side-effecting tools. Use defaultDeny: true so unregistered tools that match intent allowedTools fail closed at intercept time.
For GitOps-friendly configuration, use a versioned paybond.policy.yaml instead of scattering registry and intent fields across code.
One-liner: instrument()
Add one line and every side-effecting tool becomes spend-controlled. paybond.instrument() loads policy and returns deferred tool shells by default — safe to register with any agent framework at startup. Bind a funded intent per session with .bind() (or pass context / sandbox: true when you already know the binding mode).
No framework argument is required for agent-agnostic tool maps. Use framework-specific helpers only when your SDK needs native hooks.
import { Paybond } from "@paybond/kit"; const paybond = await Paybond.open({ apiKey: process.env.PAYBOND_API_KEY! }); // Static instrumentation — no intent yet (deferred by default) const instrumented = await paybond.instrument({ policy: "./paybond.policy.yaml", // or a preset id such as "travel" tools: { "travel.book_hotel": async (args) => bookHotel(args), searchWeb: async (args) => searchWeb(args), }, }); // Safe to pass to your agent constructor — execution throws until bound const agent = new Agent({ tools: instrumented.tools }); // Per session / request: const runtime = await instrumented.bind({ intentId, capabilityToken }); const tools = runtime.tools; // guarded tools for this session // runtime.run — bound PaybondAgentRun for additional wraps or hooks // runtime.binding — { phase: "bound", mode: "attach", intentId, tenantId, ... } // instrumented.policy — loaded policy document
Local sandbox (opt-in):
const runtime = await paybond.instrument({ policy: "./paybond.policy.yaml", tools, sandbox: true, }); // runtime.binding === { phase: "bound", mode: "sandbox", intentId: "intent-sandbox", ... }
Immediate bind at instrument time:
const runtime = await paybond.instrument({ policy: "./paybond.policy.yaml", tools, context: { intentId, capabilityToken }, });
from paybond_kit import Paybond paybond = await Paybond.open(api_key=os.environ["PAYBOND_API_KEY"]) instrumented = await paybond.instrument( policy="./paybond.policy.yaml", # or a preset id such as "travel" tools={ "travel.book_hotel": book_hotel, "search.web": search_web, }, ) # Safe at startup — execution throws until bound agent = Agent(tools=instrumented.tools) # Per session: runtime = await instrumented.bind(intent_id=intent_id, capability_token=capability_token) tools = runtime.tools # runtime.run — bound PaybondAgentRun for additional wraps or hooks # runtime.binding — {"phase": "bound", "mode": "attach", "intent_id", "tenant_id", ...}
Inline policies (tutorials)
const instrumented = await paybond.instrument({ policy: { budget: "$500/day", approve: ["travel.*"], deny: ["crypto.*"], }, tools: myTools, });
Policy presets
policy: "travel" | "shopping" | "saas" | "aws" resolve bundled vertical presets.
Fluent builder
const instrumented = await paybond .policy("./paybond.policy.yaml") .instrument(tools); // or const instrumented = await paybond.usePolicy("travel").instrument(tools);
Framework-specific adapters (only when needed)
| Helper | When |
|---|---|
paybond.instrument({ policy, tools }) | Default — any { name, execute } or tool map |
paybond.instrumentLangGraph({ policy, tools }) | LangGraph awrapToolCall / createToolNode hooks on .hooks |
paybond.instrumentOpenAI({ policy, tools }) | OpenAI Agents SDK + .hooks.runConfig |
paybond.instrumentVercel({ policy, tools }) | Vercel AI SDK + .hooks.toolApproval |
paybond.instrumentClaudeAgents({ policy, tools }) | Claude Agents SDK MCP surface |
paybond.instrumentMCP({ policy, tools }) | MCP-style tool hosts (alias for agent-agnostic) |
Or pass framework: "langgraph" only when you prefer a single entry point.
Static instrumentation vs per-request context
instrument() splits policy + tool registration (static, reusable) from intent binding (per request).
| Phase | What happens | Where intentId comes from |
|---|---|---|
instrument({ policy, tools }) | Loads policy, registers deferred tool shells | Deferred default — instrumented.binding.phase === "deferred". Tools are safe to register; side-effecting execution throws until bound. |
instrument({ ..., sandbox: true }) | Bootstraps a sandbox intent immediately | Kit sandbox bootstrap — inspect runtime.binding.intentId. |
instrument({ ..., context: () => ... }) | Lazy per-execution binding via request-local provider | You declare a context function up front; each tool call resolves the active request. instrumented.binding.phase === "lazy". |
instrument({ ..., context: { intentId, ... } }) or instrumented.bind(...) | Eager bind for one session | You supply intent + capability. Returns a PaybondInstrumentRuntime. |
Deferred (production default):
// Static: load policy once at process startup — no implicit intent const instrumented = await paybond.instrument({ policy: "./paybond.policy.yaml", tools, }); // instrumented.binding === { phase: "deferred" } // instrumented.tools — deferred shells; register with your agent framework // Per request / session / agent task (immutable — safe for concurrency): const runtime = await instrumented.bind({ intentId, // from your session — never guessed by Kit capabilityToken, userId, // optional audit attribution }); const tools = runtime.tools; // runtime.binding === { phase: "bound", mode: "attach", intentId, tenantId, ... }
Lazy context (request-scoped binding):
const purchasing = await paybond.instrument({ policy: "./paybond.policy.yaml", tools, context: () => activeRequest.paybond, // { intentId, capabilityToken, userId? } }); // purchasing.binding === { phase: "lazy" } const agent = new Agent({ model: "gpt-5", tools: purchasing.tools }); // Each tool execution resolves context from the provider and binds automatically.
instrumented = await paybond.instrument( policy="./paybond.policy.yaml", tools=tools, context=lambda: active_request.paybond, ) # instrumented.binding == {"phase": "lazy"}
The provider is declared at instrumentation time (explicit), but binding happens at execution time. Runtimes are cached per (intentId, capabilityToken) for the lifetime of the instrumented instance so multi-tool agent turns do not re-bind on every call.
Sandbox (local dev — opt-in):
const runtime = await paybond.instrument({ policy: "./paybond.policy.yaml", tools, sandbox: true }); // runtime.binding === { phase: "bound", mode: "sandbox", intentId, capabilityToken, tenantId } const tools = runtime.tools;
Do not share one bound runtime across concurrent requests. Call instrumented.bind() (or pass context to instrument()) to obtain a fresh immutable runtime per task.
Tenant id always comes from the authenticated Paybond.open() session (paybond.harbor.tenantId), not from client input.
Per-request context (API reference)
const instrumented = await paybond.instrument({ policy: "./paybond.policy.yaml", tools }); const runtime = await instrumented.bind({ intentId, capabilityToken, userId, }); const requestTools = runtime.tools;
withContext() remains as a deprecated alias for bind().
Inspect binding state at any time via .binding or .status on instrumented or runtime.
Already bound a run? Wrap additional tools without reloading policy:
- TypeScript:
paybond.wrapTools(run, tools) - Python:
paybond.wrap_tools(run, tools)
LangGraph does not wrap tools in place — use instrumentLangGraph() or createPaybondLangGraphHooks(run) / create_paybond_langgraph_hooks(run) for awrapToolCall hooks.
Preset quickstart: paybond.agent({ policy: "travel", tools }) resolves bundled presets before calling instrument().
Automatic tool discovery: pass a framework agent instance — tools are read from .tools (or .functionTools), policy from agent.policy / PAYBOND_POLICY / ./paybond.policy.yaml:
class Agent { constructor(public tools: Record<string, ToolFn>) {} } const agent = new Agent({ "travel.book_hotel": bookHotel }); agent.policy = "travel"; await paybond.instrument(agent); // or: const agent = await paybond.instrument(agent); // agent.tools — guarded after bind; agent.paybond — { policy, binding, bind, ... }
Lower-level escape hatch: createGuardedAgent / create_guarded_agent when you need full control over bind inputs. Per-tool guardTool / guard_tool remains available for single-handler integrations.
Architecture
Agent app → PaybondAgentRun.bind() # sandbox bootstrap or attach existing intent → PaybondToolRegistry # side-effecting map + evidence presets → PaybondToolInterceptor # authorize → execute → complete spend → auto-evidence → Harbor / Gateway # POST /verify, evidence submit
Framework adapters (for example createGenericToolExecutor()) translate framework tool definitions into run.interceptor.wrapExecute calls. Adapters must not embed Harbor logic.
Core API
Tool registry
Every side-effecting tool must declare an evidencePreset (required). Read-only tools pass through without Harbor verify or evidence.
import { Paybond } from "@paybond/kit"; const paybond = await Paybond.open({ apiKey: process.env.PAYBOND_API_KEY! }); const registry = paybond.toolRegistry({ sideEffecting: { "travel.book_hotel": { spendCents: (args: { estimatedPriceCents: number }) => args.estimatedPriceCents, evidencePreset: "cost_and_completion", }, searchWeb: { operation: "search.web", evidencePreset: "api_response_ok", }, }, defaultDeny: true, });
Python parity:
registry = paybond.tool_registry( { "side_effecting": { "travel.book_hotel": { "spend_cents": lambda args: args["estimated_price_cents"], "evidence_preset": "cost_and_completion", }, }, "default_deny": True, } )
Registry rules:
operationdefaults to the tool name; override when the Harbor operation differs from the handler name.defaultDeny: truerejects tool calls whose operation is in intentallowedToolsbut missing from the registry.- Bind-time validation fails if
defaultDenyis enabled and an allowed operation has no registered side-effecting entry. - Unknown or missing
evidencePresetvalues raise at registry construction.
Import the core directly when tree-shaking matters:
import { createPaybondToolRegistry } from "@paybond/kit/agent";
from paybond_kit.agent import create_paybond_tool_registry
Run binding
Bind once per agent task. Do not share a PaybondAgentRun across concurrent runs.
Sandbox bootstrap — funds a sandbox intent for one operation:
const run = await paybond.agentRun.bind({ bootstrap: { kind: "sandbox", operation: "travel.book_hotel", requestedSpendCents: 20_000, completionPreset: "cost_and_completion", }, registry, });
Production attach — reuse an existing funded intent with intentId and capabilityToken. Not required for sandbox smoke — see Going to production.
const run = await paybond.agentRun.bind({ attach: { intentId: process.env.PAYBOND_INTENT_ID!, capabilityToken: process.env.PAYBOND_CAPABILITY_TOKEN!, }, registry, });
When attach.allowedTools is omitted, allowed tools are loaded from Harbor for the bound intent.
Python sandbox bootstrap:
run = await paybond.agent_run.bind( { "bootstrap": { "kind": "sandbox", "operation": "travel.book_hotel", "requested_spend_cents": 20_000, "completion_preset": "cost_and_completion", }, "registry": registry, } )
For Python production attach, see Going to production.
Interceptor
The interceptor is the heart of middleware. For side-effecting tools it:
- Resolves operation and requested spend from the registry and call arguments.
- Calls
guard.assertSpendAuthorized(...)— approval holds and hard denials propagate to the caller. - Runs your handler on allow.
- Calls
completeSpendAuthorization(decisionId, "consumed")on success or"released"on handler failure. - Automatically submits evidence on success using the registry
evidencePresetand completion catalog.
const result = await run.interceptor.wrapExecute({ toolName: "travel.book_hotel", toolCallId: "call-1", arguments: { city: "Lisbon", estimatedPriceCents: 18_700 }, execute: () => hotelVendor.book(input.arguments), }); // result.toolResult — handler output // result.authorization — allow + decision metadata (side-effecting only) // result.evidence — auto-evidence submit result (side-effecting only)
Read-only tools skip steps 2–5 and return { toolResult } only.
Evidence idempotency key: evidence:{intentId}:{toolCallId}. Evidence submit failures throw PaybondAutoEvidenceSubmitError / PaybondEvidenceSubmitError with the successful toolResult attached — they are not logged and swallowed.
Optional per-tool customization:
evidenceMapper: (result, ctx) => ({ status: result.reservation.status === "confirmed" ? "completed" : result.reservation.status, cost_cents: result.reservation.price_cents, })
Trace events and observability
For the visual trace dashboard, timeline UI, data flow, and when to use local vs CLI vs hosted replay, see Middleware trace.
The interceptor emits structured PaybondTraceEvent records as each side-effecting tool moves through authorize → execute → finalize → evidence. Opt in with traceSink on PaybondAgentRun.bind() (or pass traceSink through paybond.instrument() / createGuardedAgent).
| Event | When emitted | Key fields |
|---|---|---|
tool_selected | Side-effecting tool resolved | toolName, toolCallId, operation |
spend_authorized | Harbor allows the spend | decisionId, amountCents, auditId |
spend_denied | Hard denial | message, code, auditId |
approval_required | Approval hold | message, code, auditId |
tool_executed | Handler finished | durationMs |
spend_finalized | Decision consumed or released | status: consumed | released |
evidence_submitted | Auto-evidence succeeded | evidenceId, presetId |
Each event also includes runId, operation, and recordedAt for correlation. Evidence idempotency key (evidenceId) matches the interceptor: evidence:{intentId}:{toolCallId}.
TypeScript — custom sink on bind:
import type { PaybondTraceEvent } from "@paybond/kit/agent"; const events: PaybondTraceEvent[] = []; const run = await paybond.agentRun.bind({ bootstrap: { kind: "sandbox", operation: "travel.book_hotel", requestedSpendCents: 20_000, }, registry, traceSink: (event) => events.push(event), }); await run.interceptor.wrapExecute({ /* ... */ }); // events: tool_selected → spend_authorized → tool_executed → spend_finalized → evidence_submitted
Python — same shape with snake_case event fields:
trace_events: list[dict[str, object]] = [] run = await paybond.agent_run.bind( { "bootstrap": { "kind": "sandbox", "operation": "travel.book_hotel", "requested_spend_cents": 20_000, }, "registry": registry, "trace_sink": trace_events.append, } )
Local dev dashboard — paybond dev smoke and paybond dev loop activate an in-process collector during the smoke run. Events are written to .paybond/dev-trace.jsonl so paybond dev trace can load them from another terminal in the same project directory (no traceSink required for CLI workflows):
paybond dev loop --offline # terminal 1 — or paybond dev loop after login paybond dev trace # terminal 2 — http://127.0.0.1:9477
The dashboard renders a vertical timeline with phase-colored steps, per-run detail JSON, and a live refresh every two seconds. See Middleware trace for the full UI and event model.
Per-run CLI trace — after a multi-step bind and execute, inspect the same event stream without the dashboard or Harbor logs:
paybond agent run bind --sandbox --operation travel.book_hotel --requested-spend-cents 20000 --completion-preset cost_and_completion paybond agent tool execute --run-id <id> --operation travel.book_hotel --tool-call-id call-1 \ --result-body '{"reservation":{"status":"confirmed","price_cents":18700}}' paybond agent run trace --run-id <id> --format table
paybond agent run trace reads .paybond/runs/<run_id>.trace.json (written during tool execute) and prints a vertical checklist in table mode or full trace_events[] / steps[] in JSON mode. Denied or approval-held calls include spend_denied / approval_required events so you can debug policy failures from the trace output alone.
onTrace / on_trace remain supported as deprecated aliases for traceSink / trace_sink.
Generic tool executor adapter
Drop-in replacement for manual paybondRuntimeToolCallAdapter setup when your runtime exposes { name, execute } tools:
import { createGenericToolExecutor } from "@paybond/kit/agent"; const adapter = createGenericToolExecutor(); const guardedTools = adapter.wrapTools(run, [ { name: "travel.book_hotel", execute: async (args) => hotelVendor.book(args), }, { name: "lookup.weather", execute: async (args) => weather.lookup(args), }, ]); // In your orchestrator: const wrapped = guardedTools[0]; await wrapped.execute({ toolName: "travel.book_hotel", toolCallId: "call-1", arguments: { city: "Lisbon", estimatedPriceCents: 18_700 }, });
Python:
from paybond_kit.agent import create_generic_tool_executor adapter = create_generic_tool_executor() guarded = adapter.wrap_tools(run, [{"name": "travel.book_hotel", "execute": book_hotel}])
Framework adapter contract
export interface PaybondFrameworkAdapter { readonly name: string; wrapTools(run: PaybondAgentRun, tools: unknown): unknown; }
Optional framework packs (Vercel AI, LangGraph TS, OpenAI Agents) implement this contract without changing Harbor semantics. The generic adapter is always available.
Vercel AI SDK
TypeScript-only adapter at @paybond/kit/vercel-ai:
paybondVercelToolApproval(run)— centralizedtoolApprovalfor Harbor pre-checks.paybondVercelWrapTools(run, tools)— wraps side-effectingexecutefor auto-evidence.
Full guide: Vercel AI adapter.
paybond init agent-middleware --framework vercel-ai --out paybond-vercel-ai.ts paybond agent demo vercel-ai smoke \ --operation paid-tool \ --requested-spend-cents 100 \ --evidence-preset cost_and_completion \ --format json
LangGraph
TypeScript and Python adapters at @paybond/kit/langgraph and paybond_kit.langgraph_hooks:
paybondAwrapToolCall(run)/paybond_awrap_tool_call(run)—ToolNodehook for Harbor pre-checks and auto-evidence.paybondToolNode(tools, run)— convenience factory (TypeScript).
Full guide: LangGraph adapter.
paybond init agent-middleware --framework langgraph --out paybond-langgraph.ts paybond agent demo langgraph smoke \ --operation paid-tool \ --requested-spend-cents 100 \ --evidence-preset cost_and_completion \ --format json
Agent-agnostic (default)
TypeScript and Python — createPaybondGenericAgentConfig / create_paybond_generic_agent_config:
Full guide: Agent-agnostic adapter.
paybond init agent-middleware --framework generic --out paybond-generic.ts paybond agent demo generic smoke \ --operation paid-tool \ --requested-spend-cents 100 \ --evidence-preset cost_and_completion \ --format json
Claude Agents SDK
TypeScript and Python — in-process MCP custom tools via createPaybondClaudeAgentsConfig:
Full guide: Claude Agents adapter (beta).
paybond init agent-middleware --framework claude-agents --out paybond-claude-agents.ts paybond agent demo claude-agents smoke \ --operation paid-tool \ --requested-spend-cents 100 \ --evidence-preset cost_and_completion \ --format json
OpenAI Agents SDK
TypeScript only — input guardrails + wrapped invoke:
Full guide: OpenAI Agents adapter.
paybond init agent-middleware --framework openai --out paybond-openai-agents.ts paybond agent demo openai-agents smoke \ --operation paid-tool \ --requested-spend-cents 100 \ --evidence-preset cost_and_completion \ --format json
MCP hosts
Stdio paybond-mcp-server for Claude Desktop, Codex, and generic MCP orchestrators:
Full guide: MCP server. Setup walkthrough: Coding-agent setup.
paybond login npx -y -p @paybond/kit paybond-mcp-server
Planned adapters
Native runner helpers for Mastra, CrewAI, and Cloudflare Agents are planned. Use agent-agnostic or MCP today; request an adapter to prioritize native support:
Tenant isolation
- Run binding always derives
tenantIdfromPaybond.open()/ session credentials — never from tool arguments. - The interceptor rejects operations not in the bound intent
allowedTools. - Evidence submit uses the same
(tenantId, intentId)as authorization. - Do not share one
PaybondAgentRunacross concurrent agent tasks.
Approval holds vs hard denials
assertSpendAuthorized may return allow: false for an approval hold or a hard denial. Middleware propagates:
PaybondSpendApprovalRequiredError— surface to operators, approve, retry withapprovalToken.PaybondSpendDeniedError— do not execute the tool.
See Agent integrations — approval holds.
Migration from per-tool guardTool
Before (manual, one handler at a time):
const guard = paybond.spendGuard(intentId, capabilityToken); const bookHotel = guard.guardTool({ operation: "travel.book_hotel", requestedSpendCents: 20_000 }, handler); // You must also call submitEvidence yourself after success.
After (instrument() — policy file or preset, one call):
const { agentTools } = await paybond.instrument({ policy: "./paybond.policy.yaml", tools: { "travel.book_hotel": handler }, }); // Evidence submits automatically on successful side-effecting calls.
Already using registry + bind manually? paybond.wrapTools(run, tools) or createGenericToolExecutor().wrapTools(run, tools) still work for an existing bound run.
Advanced: per-tool guardTool / guard_tool
Reserve per-tool guardTool / guard_tool for single-handler integrations or when you need full control over evidence timing. Multi-tool agents should use instrument() or wrapTools() / wrap_tools() instead.
See Agent integrations — Advanced: per-tool guardTool for TypeScript and Python single-handler examples.
CLI commands
The Kit CLI exposes the same middleware surface for automation and CI. Commands default to sandbox unless --production is passed.
Local dev — guided smoke with checklist output and a trace dashboard (no app code):
paybond dev loop --offline paybond dev trace
With sandbox credentials, use paybond dev loop (login when needed) instead of --offline. See Agent quickstart.
paybond login paybond agent sandbox smoke \ --policy-file paybond.policy.yaml \ --result-body '{"status":"completed","cost_cents":18700}' \ --format json
Or with explicit flags (no policy file):
paybond agent sandbox smoke \ --operation paid-tool \ --requested-spend-cents 100 \ --evidence-preset cost_and_completion \ --result-body '{"status":"ok","cost_cents":100}' \ --format json
Production attach (--production) is documented under Going to production — not required for sandbox smoke.
| Command | Purpose |
|---|---|
paybond init agent-middleware | Scaffold a registry + PaybondAgentRun integration file. |
paybond agent run bind | Sandbox bootstrap or production attach; persist .paybond/runs/<run_id>.json. Accepts --policy-file. |
paybond agent run status | Inspect a persisted run. |
paybond agent run trace | Show middleware trace events for a run (--format json|table). Reads .paybond/runs/<run_id>.trace.json from tool execute. |
paybond agent tool execute | Full intercept path with --result-body (v1 does not spawn external processes). |
paybond agent tool validate | Authorization-only check. |
paybond agent registry validate --file <path> | Validate registry YAML/JSON before bind. |
paybond policy validate-tools --file <path> | Validate policy YAML locally (--local-only) or against Gateway (--remote). |
paybond agent sandbox smoke | End-to-end bind + execute in one invocation. Accepts --policy-file. |
paybond dev smoke | Travel-preset smoke with checklist table and local trace event. --offline for no API key. |
paybond dev trace | Local HTTP trace dashboard on port 9477; vertical timeline from .paybond/dev-trace.jsonl. See Middleware trace. |
paybond dev loop | Login (optional) → policy scaffold → validate → smoke; prints trace and audit log paths. |
paybond dev up | Start/stop local WireMock Gateway (Docker). |
paybond agent demo vercel-ai smoke | No-LLM Vercel AI SDK demo: toolApproval + wrapped execute + auto-evidence. |
Registry file format (v1):
version: 1 default_deny: true tools: paid-tool: side_effecting: true evidence_preset: cost_and_completion
See CLI contract for JSON field shapes and exit codes.
paybond doctor --agent runs the same sandbox smoke path as a doctor check when sandbox credentials are present.
Package paths
| Language | Import |
|---|---|
| TypeScript (facade) | Paybond, paybond.toolRegistry(), paybond.agentRun.bind() from @paybond/kit |
| TypeScript (core) | @paybond/kit/agent |
| Python (facade) | paybond.tool_registry(), paybond.agent_run.bind() |
| Python (core) | paybond_kit.agent |
Going to production
Sandbox paths (paybond.agent(), paybond.instrument({ sandbox: true }), CLI smoke, and paybond dev loop) never require payee identities or signing credentials — Gateway issues sandbox capabilities.
When you have a funded production intent, attach middleware so every successful side-effecting call auto-submits signed evidence. See Production attach for CLI flags, SDK productionEvidence, console-managed attach bundles, and env fallbacks.
Related
- Agent integrations — runtime patterns and framework scaffolds
- Agent-agnostic adapter
- Claude Agents adapter
- OpenAI Agents adapter
- Vercel AI adapter
- LangGraph adapter
- MCP server
- Mastra adapter (planned)
- CrewAI adapter (planned)
- Cloudflare Agents adapter (planned)
- Completion presets — catalog preset IDs for
evidencePreset - Capabilities — obtaining
intent_idandcapability_token - Agent middleware guide — selection rule for buyers and coding agents
- Agent policy-as-code —
paybond.policy.yamlformat and CI validation - Policy hot-reload — runtime policy updates for long-lived runs
- Agent policy validate — local and server-authoritative policy checks