Skip to main content
You already have an agent: its own loop, its own model, its own chat UI. Vendo does not replace any of it. One tool pack, spread into your agent’s tools, adds three things to the loop you already run:
  1. Guarded tools. Your host actions and connected-account tools, with every call routed through policy, approval, and audit. The pack wraps the same guard-bound registry Vendo’s own loop uses, so no tool reachable from your loop has an unguarded route.
  2. Generated UI in your chat. A vendo_create_app tool builds live, interactive apps that render inline in your own chat surface via embed components.
  3. Vendo as a delegate. A vendo_delegate tool hands Vendo’s agent a whole task and returns the run report.

AI SDK walkthrough

The unmodified Next.js quickstart chatbot plus a four-touch diff.

Mastra walkthrough

The create-mastra weather starter plus the same four touches.

The composition

You run createVendo and mount the wire route exactly as the quickstart describes. What you drop is Vendo’s chat loop and chat UI. The wire keeps serving apps, approvals, and connected accounts to the embeds: Vendo minus the conversation.
Then build the tool pack per framework and spread it into your agent:
  • @vendoai/vendo/ai-sdk exports vendoTools(vendo, { principal }), an AI SDK ToolSet for streamText and generateText. Built per request, because tool execution needs a principal-scoped context.
  • @vendoai/vendo/mastra exports vendoMastraTools(vendo), the same pack in Mastra createTool shape for Agent({ tools }). Mastra agent definitions are static, so the caller’s principal resolves per call from Mastra’s request context instead.
Both return a Promise. Both are thin format shims over one framework-neutral tool pack, so the semantics below hold for either.

The tool pack

Every pack tool is namespaced vendo_* to avoid collisions with your own tools. A registered host action host_x ships as vendo_host_x; two built-ins are already prefixed: include and exclude filter the pack by final (namespaced) tool name, exact match. include unset means the whole pack; exclude wins over include.

Approvals in your loop

A guarded call that needs approval does not throw and does not block your loop. The tool returns the approval-ref envelope immediately, so your model sees “pending, the user must approve in the UI”. The pending call parks server-side. When the user approves in <VendoApprovalEmbed>, the wire executes the parked call and the embed resolves in place to the executed result. Deny resolves it to “declined” and discards the parked call. Parked calls expire on a TTL sweep, 60 minutes by default. Tune it with createVendo({ approvals: { parkedCallTtlMs } }); 0 disables expiry. An expired call resolves the embed to “expired” and never runs. Your agent learns the outcome on a later turn if it matters. This is the same eventual consistency Vendo’s own thread uses for abandoned approvals, in a new venue. See Embeds and envelopes for the envelope contract and the three @vendoai/ui embed components that render it inside your own chat.

Two models, deliberately

Your agent keeps its own model. Vendo’s model seam still powers app generation and the delegate: an explicit createVendo({ model }) wins, and when you omit it Vendo resolves one through the dev-mode credential ladder. In the common case that means zero extra model config.

In-process vs. the MCP door

This seam is for your own agent running in your own backend, in process with createVendo. If you want third-party agents (Claude, ChatGPT, Cursor) to reach your product from outside, that is the MCP door.