Someone on your support team has an overdue invoice open. It is right there: the number, the customer, the amount, the date it went past due three weeks ago. They open the assistant you shipped last quarter and type "why is this late?"
The assistant asks which invoice.
Nothing is broken in that exchange. The model is good, the tools are wired. The person and the assistant are sitting on the same screen and only one of them can read it.
It does not know who is asking, either, so it hedges. Ask it to void the invoice and you get a paragraph about how, depending on your permissions, you may be able to do that from billing settings. That answer is written for a stranger.
You can make the user re-type all of it, which is the tax that stops people opening the panel. Or you can hand-wire a context object into every route. I have built that one. It works on the three pages you build it for, then goes stale the first time someone ships a fourth.
Vendo does neither. On every message, before the request leaves the browser, it takes an accessibility snapshot of the host page and sends it along with what the user typed. It is the same tree a screen reader walks, not a screenshot and not the raw DOM. Fresh each time, so opening a modal or filtering a table changes what the next message carries. There is nothing to wire up. It is on by default.
Identity is the other half, and the page does not say whether someone is an admin. Your server already knows that, in the code that decodes the session, so that is where you say it.
export const auth = authJs({
secret: authSecret,
user: (subject) => {
const user = resolveSubject(subject);
if (!user) return null;
return {
display: user.display,
email: user.email,
// Model-visible, resolved fresh on every request. Data, never secrets.
facts: {
name: user.display,
plan: user.plan,
role: user.admin ? "org admin" : "member",
},
};
},
});Those facts go into the agent's prompt every turn, off the same session decode as the principal. No second lookup. They are model-visible, so put the plan in, not the API key.
Now the invoice question answers itself. The agent sees the invoice and knows an org admin is asking, so it says what happened and offers to void it, and the void goes through the same approval gate as every other write.
None of the screen text is stored. It rides the request and this turn's prompt and nothing else, and captureScreen={false} turns the capture off.
Vendo is open source and screen context is on by default, so point it at your own app and ask it about something you are looking at. I want to hear what it got wrong.
