Paybond integrates with the Vercel AI SDK at the tool approval and execute boundary — not as model middleware. Keep your provider client for inference; use Paybond for Harbor verify, approval holds, spend finalize, and auto-evidence on side-effecting tools.
TypeScript only. Python Kit does not ship a Vercel AI adapter; use this page with
@paybond/kitor see Agent-agnostic adapter for Python parity.
Install
Install
npm install @paybond/vercel-ai aiImport from @paybond/vercel-ai
import {
paybondVercelToolApproval,
paybondVercelWrapTools,
} from "@paybond/vercel-ai";- Equivalent subpath on the core package: `@paybond/kit/vercel-ai` — use `@paybond/kit` when you need multiple adapters in one app.
- TypeScript only. Python Kit does not ship a Vercel AI adapter — use the agent-agnostic adapter for Python parity.
- Optional peer: ai >= 4.0.0 (tested with v7).
Recommended wiring
One-liner (sandbox): paybond.instrument({ policy, framework: "vercel-ai", tools, sandbox: true }) returns guarded tools plus toolApproval for generateText / streamText.
import { generateText, tool } from "ai"; import { z } from "zod"; import { Paybond } from "@paybond/kit"; const paybond = await Paybond.open({ apiKey: process.env.PAYBOND_API_KEY! }); const { agentTools: tools, toolApproval } = await paybond.instrument({ policy: "./paybond.policy.yaml", // or preset id "travel" framework: "vercel-ai", tools: { bookHotel: tool({ description: "Book a hotel room", inputSchema: z.object({ city: z.string(), estimatedPriceCents: z.number().int().nonnegative(), }), execute: async (args) => bookHotel(args), }), searchWeb: tool({ description: "Search the web", inputSchema: z.object({ query: z.string() }), execute: async (args) => searchWeb(args), }), }, }); const result = await generateText({ model: openai("gpt-4.1"), tools, toolApproval, prompt: "Find hotels in Lisbon and book one under budget.", });
Already bound a run? paybond.wrapTools(run, tools, { framework: "vercel-ai" }) returns the same { tools, toolApproval } shape without reloading policy.
See Agent middleware for policy files, registry rules, and tenant isolation.
Advanced / manual wiring
When you need step-by-step control over registry and bind, use both primitives:
| Primitive | Paybond helper | Purpose |
|---|---|---|
toolApproval on generateText / streamText | paybondVercelToolApproval(run) | Pre-execution Harbor verify, deny, HITL hold |
Wrapped execute on side-effecting tools | paybondVercelWrapTools(run, tools) | Post-success spend finalize + auto-evidence |
import { generateText, tool } from "ai"; import { z } from "zod"; import { Paybond, createPaybondToolRegistry } from "@paybond/kit"; import { paybondVercelToolApproval, paybondVercelWrapTools, } from "@paybond/kit/vercel-ai"; const paybond = await Paybond.open({ apiKey: process.env.PAYBOND_API_KEY! }); const registry = createPaybondToolRegistry({ defaultDeny: true, sideEffecting: { "travel.book_hotel": { spendCents: (args: { estimatedPriceCents: number }) => args.estimatedPriceCents, evidencePreset: "cost_and_completion", evidenceMapper: (result) => ({ status: result.reservation.status === "confirmed" ? "completed" : result.reservation.status, cost_cents: result.reservation.price_cents, }), }, }, }); const run = await paybond.agentRun.bind({ bootstrap: { kind: "sandbox", operation: "travel.book_hotel", requestedSpendCents: 20_000, completionPreset: "cost_and_completion", }, registry, }); const tools = paybondVercelWrapTools(run, { bookHotel: tool({ description: "Book a hotel room", inputSchema: z.object({ city: z.string(), estimatedPriceCents: z.number().int().nonnegative(), }), execute: async (args) => bookHotel(args), }), searchWeb: tool({ description: "Search the web", inputSchema: z.object({ query: z.string() }), execute: async (args) => searchWeb(args), }), }); const result = await generateText({ model: openai("gpt-4.1"), tools, toolApproval: paybondVercelToolApproval(run), prompt: "Find hotels in Lisbon and book one under budget.", });
paybondVercelToolApproval(run)
- Looks up
toolCall.toolNamein the registry. - Read-only tools →
'approved'(no Harbor verify). - Side-effecting tools → middleware interceptor pre-check (
authorizeToolCall). - Approval hold →
'user-approval'. - Hard deny →
{ type: 'denied', reason }(surfaced to the model as a tool error).
paybondVercelWrapTools(run, tools)
Wraps only client-executed, registry side-effecting tools. Each successful execution:
- Finalizes the spend decision (
consumed/releasedon failure). - Builds evidence from the registry
evidencePresetand optionalevidenceMapper. - Submits with idempotency key
evidence:{intentId}:{toolCallId}(safe forstreamTextmulti-step loops).
Provider-executed tools (isProviderExecuted: true) are never wrapped — they bypass local approval and evidence.
Approval hold + retry
When Harbor returns approvalRequired, toolApproval yields 'user-approval'. After operator approval:
run.storeApprovalToken(toolCallId, approvalTokenFromConsole); // Retry generateText / streamText with the same run binding.
Both paybondVercelToolApproval and wrapped execute read stored tokens via run.getApprovalToken(toolCallId).
Scaffold and smoke
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
The smoke command uses MockLanguageModelV4 from ai/test — no OpenAI API key in CI.
Example app: examples/paybond-kit-vercel-ai-typescript.
Known limitations
- Provider-executed tools (web search, code execution on-provider) bypass local
toolApprovalandwrapTools. Register explicit registry entries only for tools you execute locally. LanguageModelV3Middleware/wrapLanguageModeloperate on model calls only; they cannot block tool execution. Do not document Paybond as model middleware.experimental_sandboxtools need explicit registry entries like any other side-effecting tool.
Related
- Agent middleware —
PaybondAgentRun, registry, interceptor - Agent integrations — runtime-neutral patterns
- OpenAI Agents adapter — input guardrail pattern (TypeScript)