Skip to main content
Who is asking, and what their screen shows.

The picture

An agent that knows neither has to ask. Vendo fills two blocks so it does not have to.
the agent's prompt
Maple's payments page with the panel open, moving $200 to savings without asking which accounts to use
[User] is your server’s assertion about the person, refreshed every request. [Situation] is their screen right now, and it lives for one turn only.

Assert facts about the user

Facts ride the auth preset you already pass. The preset decodes the session once per request, and facts come off that same decode.
1

Resolve identity through a preset

Facts are a preset-only channel. Pass auth, not the per-seam principal / actAs / oauth trio, or createVendo throws a validation error for filling the same seams twice.
app/api/vendo/[...vendo]/route.ts
2

Return facts from the user resolver

Add a facts object beside the display and email the resolver already returns. Values are any JSON, and returning null still means the host does not know this subject.
app/api/vendo/[...vendo]/route.ts
Each fact renders as one key: value line in the [User] block. A live host wiring this is examples/demo-bank/src/vendo/server.ts.
Facts go to the model verbatim. Put nothing in them you would not paste into a chat window: no tokens, no keys, no identifiers you rely on staying private.

The screen rides along already

You wire nothing for this. On every send, the widget snapshots the visible page and attaches it as context. The snapshot is the page’s accessibility tree: URL and title, then headings, landmarks, links, buttons, table contents, form values, and control states. The block is labeled as observation, so page text reads as evidence rather than as instructions to the model.

Publish what the page does not show

A cart total, a selected row id, a wizard step. useVendoContext merges your own data into the same [Situation] block and retires it on unmount, so the agent never sees a screen the user has left.
app/checkout/payment-step.tsx
Several mounted callers coexist and merge, and on a repeated key the later one wins. The hook returns nothing and does not need to sit inside the provider.
It republishes whenever the object’s identity changes, so an inline literal republishes on every render. Harmless, but wrap it in useMemo if the object is expensive to build.

Keep something out

data-vendo-ignore drops an element and everything under it. Vendo’s own chrome carries it, so the widget never snapshots itself.
To stop page capture entirely, set captureScreen={false} on the provider. Data you publish through useVendoContext still rides.
app/layout.tsx

What actually reaches the model

[User] is server-trust. It comes from your resolver, on your server, on every request. The client cannot set it. No principal, no turn. A principal resolver that returns null gets a forbidden (403) before either block is built. [Situation] is one turn only. It rides the request onto that turn’s prompt and is never written to the transcript. The next turn on the same thread carries no situation. 8 KB, enforced twice. The client truncates before sending, retrying from <main> alone and then hard-truncating with a …[truncated] marker. The server re-caps whatever arrives and drops entries past the budget rather than refusing the turn. Nothing can forge a section. Values render as key: value, non-strings are JSON-encoded, and every continuation line of a multi-line value is indented. A fact cannot close its own block and impersonate one of Vendo’s.

Where to go next

Knowledge

Answers from your docs, with citations.

Instructions

The prose your agent reads before every turn.

Theming

The token file every surface renders through.