vendo_* tool answers your loop with either plain data or a small versioned ref. One component turns that ref into the right surface, so your loop never learns what an app or an approval is.
The dispatch
The envelope
status on an app ref is always "building", win or lose. A build that fails terminally is never wrapped in one, so the ref never means done.
Readers tolerate unknown extra fields. Anything additive stays inside @1, and a breaking change bumps the kind.
The three embeds
Import all three from@vendoai/vendo/react, which re-exports them so you need no second package. Each reads the wire through the surrounding provider and polls on its own until it reaches a terminal state.
None of the three takes config props beyond the one shown. Theme and behavior come from the provider, and a failure speaks the failed or expired vocabulary rather than a silent blank.
Default to
<VendoToolResult> wherever you render a finished tool part. Reach for the other two only when you hold a ref that did not come from a live tool call, such as one you stored and want to re-render later.
Set up the provider
Wrap your chat once inVendoProvider, pointed at the wire route. Auth rides your host session cookie and theme rides the --vendo-* tokens, so the embeds render on-brand.
app/page.tsx
baseUrl defaults to /api/vendo, so pass it only when your wire route is mounted somewhere else. The headless hooks need the same wrapper, so this step covers both.
Approvals never block the loop
A guarded call that needs approval does not throw and does not stall your turn. The tool returns an approval ref right away, your model reads “pending, the person has to approve this”, and the call itself parks server-side. When the person approves in the card, the wire runs the parked call and the card resolves in place. Deny throws it away.
createVendo({ guard: guard({ approvals: { parkedCallTtlMs } }) }), where 0 means never.
Dispatching it yourself
parseVendoToolEnvelope(output) returns the typed envelope or null, which is exactly what <VendoToolResult> calls. Use it server-side, or anywhere you want the branch in your own code.
@vendoai/core. Add that package to your dependencies to import it.
A malformed envelope returns null too. The tool pack is the only writer, so a bad shape is a bug there rather than something your chat should half-render.