ZeroDOM
Parses a live web page into a token-optimized interaction graph for AI browser agents — 71% fewer tokens than Playwright's ARIA snapshot mode. Every CSS selector is checked for actual document-wide uniqueness before being returned, not assumed from an attribute.
Documentation
Every tool here answers the same question: what can this page do, addressed precisely enough that an agent can act on it without guessing. An agent working from raw HTML either burns its context window re-reading a 30,000-token page every turn, or gets a hand-rolled subset of the DOM that misses the one button it needed. ZeroDOM sits between the two: a deterministic pass over the page that keeps everything an agent can click or fill, drops everything it can't, and hands back a graph small enough to reread on every single action without it mattering.
Nothing about that pass touches a model. Parsing is lxml (Python) or linkedom (TypeScript) over HTML you already have — from a Playwright page, from a raw string, from a file — and it runs the same way every time. The only thing that ever reaches an LLM is the text you choose to send it: graph.to_compact_text(), a few hundred tokens instead of the page. Start with the quickstart if you already know Playwright, or install first if you're setting up from scratch.
Measured across 111 live sites — static pages, SPAs, web components, shadow DOM, iframes, dashboards, commerce, and login walls — 10,756 audited nodes resolved to exactly one live element 99.00% of the time, with 0.03% ambiguous and zero invalid selectors. Median token savings against raw HTML: 98.9%.
How it fits together
The selector never leaves your process. A model only ever sees a node id like [03] — the CSS path that actually addresses the element lives in selector_map(), on your side, and gets resolved right before the click. On real pages that selector is often longer than the label describing it; keeping it out of the prompt is most of where the token savings above come from.
1
Live page
a Playwright Page, or HTML you already have
2
ZeroDOMParser
one DFS pass — prune, collect, label
3
InteractionGraph
compact text or JSON, sent to the model
4
Model picks a node
e.g. click 03 — never a selector
5
selector_map()[node]
resolved on your side, not the model's
6
Act
click/fill via Playwright, then re-read