How it worksDocsAboutLive demoStar on GitHub
Questions

Including the ones with awkward answers.

Can I put it on a site I do not control?

No. Virgil is a component you mount inside your own app, and that is a deliberate limit rather than a missing feature.

The snapshot is built in the page, by your code, under your rules — which is the only reason it can promise that input values and table cells never leave the browser. A bookmarklet or an extension reading somebody else’s product would be making that promise on their behalf, and could not keep it.

What does a tour cost?

Measured on the demo, not promised: one step on a screen of about fifteen controls is ≈900 tokens in and ≈60 out — the system prompt is most of it, the screen itself is under a thousand characters. A four-step tour is therefore in the region of four thousand tokens.

Multiply by your provider’s price; on a current mid-tier model that is well under a cent per tour. The chat answer is one call of roughly the same size. Nothing is spent when nobody is asking: the guide makes no request until a goal is given or an element is clicked.

What is actually sent to the model?

Roles, authored labels — an aria-label, a <label>, a placeholder — the section a control sits in, a link’s in-app destination, and three booleans: filled, disabled, visible. Capped at sixty elements.

Never the value of an input, and never the text of anything inside a row, a cell or an element you mark data-virgil-data: those are described by their shape instead — “a cell in Supplier”. It all happens in the browser, before the request is built.

The honest caveat: an authored label is forwarded, so a label that interpolates data — aria-label="Approve PR-2048 for Nordgren" — is data, and no rule can tell. The whole payload is here.

Can the model point at «Delete everything»?

It can point at anything the snapshot contains, so keep destructive controls out of it: anything under data-virgil-ignore is invisible to the guide and keeps its own clicks.

Worth knowing the shape of the risk: the page’s own text goes into the prompt, so text an untrusted user can write into your app is text that can try to argue with the guide. It cannot invent a target — it answers with an id from the list the browser just sent — but it can be talked into choosing a different one. Nothing is ever clicked on the user’s behalf; the guide only points.

Does it work from the keyboard?

Yes, and it is one of the reasons it exists. The outline follows focus as well as the pointer, so tabbing to a control and pressing the shortcut asks about that control. Every step is announced to a screen reader, and the answer panel is a real dialog.

The tooltip tours most products ship are unusable this way — their steps are decoration painted over the app. A guide that only helps people who can already drive the interface is helping the wrong half of the room.

Which models, and which providers?

Any model the AI SDK can reach, including a local one. Two are configured, and they can be different: the step model does the harder job — reading a screen and deciding — and the chat model only explains what is already on it.

You bring the key and the endpoint runs in your app. There is no Virgil account, no dashboard, and nothing to phone home to.

How much of my app do I have to annotate?

None of it. The guide reads roles and accessible names, which your app already has if it is usable at all — and where a name is missing, that is a real accessibility bug the guide has just found for you.

What is worth writing is the part the DOM cannot say: a virgil.md beside a route holds the rules of that screen, like a protocol number that is never reused. Ten lines per screen, and only for the screens that have rules.

What happens when the model is wrong?

It points at the wrong control and the user does not click it, which is the failure mode we chose. A tour that advances on its own «Next» button would have moved on regardless, and the two would then be describing different screens.

A step is only complete when the real action happens: the click, the route change, or an explicit confirmation. A tour that cannot get anywhere stops itself after twelve steps rather than circling on your budget.

And when not to use it.

A scripted tour is a promise about an interface — which is a real problem, and also a real feature when the promise is the point. Three cases where the older libraries are the right answer, written out because you will find them anyway.

A path that must be identical every time
Regulated flows, a scripted product demo, a training course that a human signs off on. You want the promise a written path makes. Use Shepherd, Intro.js or Driver.js — they are small, free to run, and they will do exactly what you wrote, forever.
No budget for model calls, or no network
Every step here is a model call. Offline, air-gapped, or on a plan where per-user inference is not going to be approved, a scripted tour costs nothing to run and this costs something every time.
You want analytics, segmentation and campaigns
Appcues, Userpilot, Pendo and the rest are products for a growth team: who saw what, which cohort converted, a tour scheduled for users on the trial plan. Virgil is an MIT package with no account and no dashboard. Different tool, different department.