navigera
v0.8.0
Published
Drive headless Chrome and Electron apps from the shell, one command per step: a native Rust CDP CLI for AI agents (accessibility-tree refs, sessions, tabs, iframes, dialogs) plus a JSON-lines serve mode.
Maintainers
Readme
navigera
Browser automation for AI agents. One native binary (Rust, no Node) that
an agent drives from its shell, one command per step, against a warm Chrome.
It reads pages as a compact accessibility tree with [ref=N] handles, acts
with real mouse and keyboard events, and after each action prints what
changed on the page, so the agent seldom needs a second look.
navigera -s work start # a warm headless browser named "work"
navigera -s work goto https://example.com
navigera -s work ax # the page as a tree; actionable nodes carry [ref=N]
navigera -s work click 12 # prints what the click changed, or ok
navigera -s work quitFaster software for a better planet — less energy, less compute, more time for life.
Why navigera
- Fewer tokens and turns for agents. When one model drives six browser tools through the same tasks, navigera's sessions use the fewest tokens (8.7K against 10.4K–15.1K) at the lowest cost, and every session succeeds.
- Fast at every step. 28 ms per command an agent sends, against 80 ms for agent-browser and 189–703 ms for the MCP servers and playwright-cli; a 1,000-row page reads in 76 ms as 41 KB.
- Light. 7 MB for navigera itself; eight agents at once in 942 MB where the other agent tools need 2.3–2.5 GB, because sessions share one browser with a private context each.
- It tells the agent what happened. A click that loads results prints the
new lines (
+ status: Results for: headphones), one that changes nothing printsok, and an error says what to do next. - The hard parts are handled: new tabs, iframes, shadow DOM, dialogs, uploads and downloads, your own signed-in browser, Electron desktop apps, and an MCP server for IDE agents.
At a glance
From one benchmark run on GitHub's runners (bench 41659246459) and the agent eval (run 41579693623); how each was measured is under Benchmarks and Agent eval.
| | navigera | the best of the others | |---|---|---| | tokens per agent session (one model, six tools) | 8,705 | playwright-cli 10,355 | | cost of the 24 agent sessions | $0.0161 | agent-browser $0.0168 | | time per agent command | 28 ms | agent-browser 80 ms | | a 1,000-row page as a snapshot | 76 ms, 41 KB | Playwright MCP 138 ms, 144 KB | | scripted session, same Chrome | 0.51 s | go-rod 0.54 s | | cold start, same Chrome | 0.21 s | go-rod 0.23 s | | memory of the driver itself | 7 MB | chromedp 8 MB | | eight agents at once | 942 MB | agent-browser 2,338 MB |
Where navigera is not first
- Browsing GitHub, live: 3.37 s against go-rod's 3.29 s. Two things
navigera does for agents cost time there. It records every request a page
makes, so
axcan wait for data that is still loading and a click can say what it changed; Chrome charges for those events, and go-rod with the same events turned on slowed down by more than this gap. And navigera's click returns once the new page is there, where go-rod's returns at once. - One JavaScript
eval, warm: 0.43 ms against go-rod's 0.26 ms. Part of it is that navigera is a separate process an agent's shell talks to, and go-rod a library inside its caller; but in this run navigera's own round trip to the page was slower too (0.36 ms). Not explained yet, and not an agent trade-off. - example.com, live: a tie, 0.35 s against go-rod's 0.34 s.
- In the agent eval Playwright MCP finished the price table in fewer turns
(median 3.5 against 4), and with
batchallowed navigera's catalog sessions cost more tokens than without it (25.9K against 11.1K). - navigera's fastest rows (
nv-shell) run chrome-headless-shell, a lighter build of Chrome the other drivers here don't use; the comparisons above are all on the same Google Chrome.
Install
curl -fsSL https://raw.githubusercontent.com/andrey-usa/navigera/master/install.sh | shWindows (PowerShell):
irm https://raw.githubusercontent.com/andrey-usa/navigera/master/install.ps1 | iexOr with a package manager — each one installs the same prebuilt binary:
| Channel | Command |
|---|---|
| npm | npm install -g navigera (or npx navigera …) |
| pip / uv | pip install navigera · uv tool install navigera |
| Cargo | cargo install --locked navigera |
| Homebrew | brew install andrey-usa/tap/navigera |
| Scoop | scoop bucket add andrey-usa https://github.com/andrey-usa/scoop-bucket then scoop install navigera |
Then give your agent the usage guide:
navigera install-skill # -> ./.agents/skills (Gemini CLI, Codex, Cline, Kilo, …)
# --claude -> ./.claude/skills, --continue -> ./.continue/skillsLinux, macOS and Windows are supported. On Windows the session socket is a loopback TCP port that only accepts clients presenting a random token from the session file in your temp directory, CDP runs over a DevTools WebSocket, and Chrome sits in a kill-on-close job object, so it still exits when navigera dies.
navigera finds Chrome on its own: $CHROME_BIN, a system Chrome,
Chromium or Edge, or one it installed itself. For the fastest cold start,
navigera install-browser downloads
chrome-headless-shell
from Google's Chrome for Testing
into navigera's own cache: the same Chrome without its browser UI layer, about
three times faster to start (see Benchmarks). Once it is there,
headless sessions use it and headed ones the system Chrome (navigera
install-browser chrome puts the full Chrome there too, for a machine with
none). Set $CHROME_BIN to choose explicitly.
Use
navigera -s work start # warm headless browser behind a local socket
navigera -s work goto example.com
navigera -s work ax # page as an indented tree, [ref=N] on actionable nodes
navigera -s work click 12 # act on a ref
navigera -s work fill 31 "hello" && navigera -s work press Enter
navigera -s work wait --text "Saved"
navigera -s work quitSteps you are sure of go in one call, one model turn: batch reads one
command per line (or JSON ops) and prints what the steps read and the page
state once, stopping at the first failure:
navigera -s work batch <<'EOF'
goto https://example.com/signup
fill --label Email [email protected]
select --label Plan Pro
check --label "I accept the terms"
click --role button --name "Create account"
text --selector .welcome
EOFpage "Checkout — Acme" http://shop.test/checkout
- main:
- heading "Checkout" [level=1]
- textbox "Full name" [ref=21]
- combobox "Country" [ref=22] value="Select…" options: "Canada", "Mexico", …
- radio "Express (1–2 days)" [checked, ref=25]
- Iframe "Card payment":
- document "Card details":
- textbox "Card number" [ref=41]
- button "Place order" [ref=30]The commands are goto, ax, click, fill, type, press, select,
hover, drag, scroll, upload, wait, eval, back/forward/reload,
screenshot, pdf, the tab-* commands, dialog and quit, with get
and is for one value, console and network for what the page logged and
fetched, cookies, storage and state save|load (cookies and localStorage as one
JSON file) for sign-ins, set for viewport, device, media, geo
and the like, route to fail, answer or change requests, or replay a
recorded HAR, and webmcp-list / webmcp-call for the tools a page offers
agents through WebMCP (on in the browsers navigera starts; a tool that may
change things needs --confirm).
- Targets: elements can be named by ref,
--selector <css>,--text "<visible text>",--role/--name,--label,--placeholderor--testid. - Output: compact by default, for agents: an action prints what it did
to the page (the new title and url, or, after an
ax, the lines of that tree that are new, gone or different, with their refs;okwhen nothing visible changed), a read only its value; errors go to stderr.--output json(orNAVIGERA_OUTPUT=json) prints one JSON line per command, for programs. - Help:
navigera help <command>. - Agent guide: the version-matched guide is
skills/navigera/SKILL.md, also printed bynavigera skill. - Reference: docs/navigera.md has the full reference, and llms.txt is an index for LLMs.
Hand the agent your browser
Three ways, from most direct to most portable:
| Way | the agent gets | needs |
|---|---|---|
| start --attach | your running Chrome itself, in a window of its own: your logins, settings, extensions, live | Chrome 144+, remote debugging turned on once at chrome://inspect/#remote-debugging, and a click on Allow |
| start --profile agent --from chrome | a browser of its own, seeded with a copy of your Chrome profile's sign-ins (cookies, site storage, saved logins); it can run --headless while you keep working | any Chrome; same machine and user. On Windows close Chrome for the copy (it locks its cookies) |
| state save / state load | the cookies and site storage of a session, as a file, to any other machine or CI job | nothing; one JSON file of cookies and site storage |
The other way round, show puts a headless session on the person's screen
with the same tabs and sign-ins, for the step only they can do (a login, a
2FA prompt, a CAPTCHA), and hide takes it back; MCP clients get the same as
browser_handoff.
navigera profiles lists what can be handed over: each installed browser's
profiles (name, signed-in email), whether --attach reaches it right now,
and navigera's own --profile browsers. --profile work without --from is
a persistent browser you sign in to yourself. Either way navigera works in windows of its own, quit only
disconnects, and quit --close-browser waits until the profile is free.
Electron apps
start --electron <app> launches a desktop app built
on Electron and drives its windows like tabs: the same tree, refs, clicks,
waits and eval. <app> is the app's directory or main script (navigera
runs the Electron in its node_modules), a packaged app's executable, a
macOS .app bundle or a command on PATH; --electron-arg <arg> passes
arguments to the app.
navigera -s app start --electron ./my-app # the window the app opens first
navigera -s app ax
navigera -s app click --role button --name "Settings"
navigera -s app tab-list # the app's new window is a tab, and the active one
navigera -s app eval --main "({ app }) => app.getPath('userData')" # the app's main process
navigera -s app quit # ends the appWindows the app opens later are adopted as they appear; a window that
closes itself is dropped, and the session goes on in the one before.
eval --main runs JavaScript in the app's main process (Node), where a
function gets the electron module: read the app's state, call its IPC
handlers' logic, check what a menu or dialog would do. The app
runs as it would for a person (a visible window, its own profile and
storage), so show, hide, reset and tab-new don't apply. start
waits for a window the app shows to have something in it, starts on that
window (hidden helper windows are tabs, not the active one), and says the
app's Electron version and whether its main process can be reached. On
Linux it needs a display: on a server, one xvfb-run around the whole
session. An app that is already running with a DevTools port
(--remote-debugging-port=9222) is driven with start --attach 9222: its
windows are the tabs, and quit leaves it running.
Natural mode
--natural makes a headless browser look like the browser
it is: no HeadlessChrome in the user agent (page, workers, request
headers), and a real screen size. navigator.webdriver is false in every
mode, and navigera never passes --enable-automation. It is not a CAPTCHA
solver and fakes no fingerprint: it is a regular Chrome, run for you.
Details: docs/navigera.md.
Parallel agents
Give each agent its own -s <name>. Sessions share
one browser, each in a browser context of its own (cookies, storage, cache,
tabs and downloads apart), so N agents cost one browser plus a page each,
as Playwright's one driver with a context per test does, not N browsers.
The browser stays on the private pipe (no DevTools port) and closes 30 s
after its last session. start --own-browser gives a session a browser of
its own. navigera sessions lists the sessions with their memory and idle
time; sessions --clean quits the idle ones (--all: every one), and
start --parent <pid> ends a session when its agent's process does. An
unused session ends after 10 min.
From a program
Programs can keep one browser on a pipe instead: navigera serve reads
one JSON command per stdin line, for example
{"id":1,"op":"goto","url":"https://example.com"}. The session socket speaks
the same protocol.
IDE agents (MCP)
navigera mcp is a Model Context Protocol server for Cline, Roo Code, Kilo
Code, Continue, VS Code, Claude Desktop and Cursor, with the same tree, refs and
waits as the CLI, in 23 tools (a tool list of about 16 KB). Add it as {"command": "navigera", "args": ["mcp"]}, or without
installing anything as
{"command": "npx", "args": ["-y", "navigera", "mcp"]}; per-client snippets
are in docs/navigera.md. Agents that run
shell commands (Kilo, Cline CLI, Gemini CLI, Codex) can use the CLI and the
skill directly: navigera install-skill writes it to .agents/skills, where
they look for it.
Decisions by a fast model (optional)
Two small decisions an agent otherwise spends a page tree and an LLM turn on,
which element and did it work, can go to a System One model:
TypeSafe's models (Jev, Kev, ...) when
NAVIGERA_DECIDER_KEY is set (NAVIGERA_DECIDER_MODEL picks the model, the
key stays the same; TYPESAFE_API_KEY still works), or any endpoint with the
same API through NAVIGERA_DECIDER_URL.
navigera -s work click --find "accept the cookies" # acts on the element the model picks
navigera -s work check "Is an error message shown?" # {"yes":0.02,"answer":false}find returns the ref, its probability and the runners-up; below 0.5,
--find refuses and lists the candidates instead of acting. start reports
"decider" when one is configured, and MCP clients get browser_find and
browser_check only then. Nothing leaves the machine otherwise; typed values
are sent as (filled).
Measured (2026-10-14). On 39 decisions with known answers, Jev picked
25/25 elements and answered 14/14 yes/no questions (151 ms median; $0.0086
for all 39). Gemini 3.5 Flash-Lite reading the same tree got 23/25 and 14/14
(724 ms median) and once chose the button that text planted in the page
pointed at. In the agent eval, Gemini CLI told to prefer --find on two large
reference pages (56–95 KB trees) used about half the Gemini tokens of the runs
that read the tree (114–119K against a median of 250K; small samples).
Unprompted, it read the tree and never used the decider. Details and caveats:
docs/multi-model.md.
Measured again (2026-10-21, run 39530836547). On 78 decisions (50
elements, 28 yes/no questions, public pages included), Jev got 50/50 and
27/28 at an 80 ms median, $0.017 for all of them; DeepSeek-V4.1-Flash
reading the same tree got the same 50/50 and 27/28 at a 2.0 s median.
Neither followed text planted in the page. Since start and long trees
tell the agent how to use the decider, Gemini CLI used it unprompted in 3 of
15 runs and in 14 of 15 when the prompt asked (run 39561380436, three runs
of five tasks per arm); median tokens per run were 199K without the
decider, 172K with it and 190K when told, a spread within the run-to-run
noise at this sample size.
Supported browsers
CI runs the full end-to-end suite on:
- the current Chrome Stable and the three milestones before it (about four months of releases, covering Extended Stable), all from Chrome for Testing;
- chrome-headless-shell;
- Chrome Beta, as a non-blocking early warning;
- the runner's own Chrome, on Linux and on Windows.
The suite is weekly as well as on every push, so a new Chrome release is
caught before agents hit it. In its first runs the matrix caught two real
bugs that the runner's Chrome alone missed: Chrome 151 segfaulted with one of
the automation flags (--disable-features=OptimizationHints, bisected with
chrome-bisect.yml), and on Chrome 153 an Enter-submitted form returned
before its redirect landed. Both are fixed.
Each run also checks CDP protocol correctness. navigera records
every command it sends (NAVIGERA_CDP_TRACE), and
tools/cdp_check.py validates every method, parameter
and enum value against the /json/protocol that that browser serves.
Deprecated or unknown usage fails the build. Edge and Brave are covered by
the benchmark.
Lightpanda (--engine lightpanda) runs the same end-to-end suite in CI,
on its newest stable release. It is a different engine (one tab, no layout),
so navigera fills in what its CDP leaves out: shadow roots and password
fields in ax, confirm()/prompt() answers, navigations a click starts.
The differences that remain are listed in
docs/navigera.md.
Electron (start --electron) has its own end-to-end test on Linux,
macOS and Windows: a fixture app whose form is saved through the main
process over IPC and which opens a second window of its own. The test
reads and fills the form, follows the new window, closes it from the page
and checks that quit ends the app.
Benchmarks
Same scripted session (launch → goto listing → title/count/extract evals →
fill+click filter → visible-count eval → screenshot → new tab → goto detail →
title/row-count evals → close) driven against the same headless browser by
each driver. Fixtures are deterministic and local (500-card listing,
1000-row detail). navigera is the static Linux binary the releases ship.
Reps are interleaved across contenders. Run in CI via .github/workflows/bench.yml.
| contender | session wall, best (median) of 5 | warm eval mean (200×) | cold start, best (median) of 5 | driver CPU (own) | driver peak RSS (own) | browser memory (PSS) |
|---|---|---|---|---|---|---|
| nv-shell (this repo on chrome-headless-shell) | 0.25s (0.27) | 0.21 ms | 0.07s (0.07) | <10 ms | 7 MB | 267 MB |
| nv-brave (this repo on Brave) | 0.50s (0.52) | 0.30 ms | 0.24s (0.25) | <10 ms | 7 MB | 584 MB |
| nv-serve (this repo on Chrome) | 0.51s (0.53) | 0.43 ms | 0.21s (0.24) | <10 ms | 7 MB | 363 MB |
| nv-edge (this repo on Edge) | 0.53s (0.55) | 0.33 ms | 0.26s (0.27) | <10 ms | 6 MB | 732 MB |
| gorod (go-rod 0.116.2, Go) | 0.54s (0.55) | 0.26 ms | 0.23s (0.24) | 10 ms | 12 MB | 400 MB |
| chromiumoxide 0.7.0 (Rust) | 0.59s (0.63) | 0.63 ms | 0.27s (0.30) | 10 ms | 9 MB | 418 MB |
| chromedp 0.19.1 (Go) | 0.78s (0.81) | 0.50 ms | 0.35s (0.37) | 20 ms | 8 MB | 474 MB |
| chromey 2.58.2 (Rust, maintained chromiumoxide fork) | 0.78s (0.80) | 0.66 ms | 0.27s (0.28) | 30 ms | 16 MB | 450 MB |
| puppeteer-core 24.43.1 (Node) | 0.82s (0.83) | 0.75 ms | 0.49s (0.50) | 0.28 s | 80 MB | 414 MB |
| playwright-core 1.63.0 (Node) | 0.97s (1.01) | 1.13 ms | 0.59s (0.60) | 0.54 s | 149 MB | 411 MB |
| nv-lightpanda (this repo on Lightpanda, 1 tab) | 0.16s (0.17) | 0.24 ms | 0.11s (0.11) | <10 ms | 7 MB | 15 MB |
Measured 2026-11-03 on GitHub-hosted ubuntu-latest (INTEL(R) XEON(R) PLATINUM 8573C — 4 vCPU · Chrome 156.0.8078.47).
Every driver produced byte-identical extracted data and counts (correctness
gate); wall is best-of-N.
The ladder reruns weekly (bench.yml) against
the latest contender releases. GitHub runners vary between runs, so compare
rows within one run.
How driver CPU/RSS are measured. Each driver's own utime+stime,
read from its zombie's /proc/<pid>/stat before reaping (waitid with
WNOWAIT), and its own VmHWM. The kernel counts CPU in 10 ms ticks, so
values under ~50 ms are a tie. Not wait4: its rusage is RUSAGE_BOTH and
adds in every child the driver reaped. navigera, chromedp, puppeteer
and playwright wait() on their Chrome, so wait4 charged them Chrome's
CPU and RSS; go-rod (its leakless helper reaps Chrome) and chromiumoxide
were never charged. Earlier versions of this table reported exactly that
artifact ("0.82s vs 0.03s"). Browser memory is the whole browser process
tree (every renderer/GPU/utility process), sampled as summed PSS every
250 ms (shared pages counted once).
Reading the table. Session wall order: nv-shell 0.25s, nv-brave 0.50s, nv-serve 0.51s, nv-edge 0.53s, gorod 0.54s, chromiumoxide 0.59s, chromedp 0.78s, chromey 0.78s, puppeteer 0.82s, playwright 0.97s. Every driver but nv-shell runs the same Google Chrome; on it, nv-serve (0.51s) is ahead of the fastest other driver, gorod (0.54s). nv-shell (0.25s) is navigera on chrome-headless-shell, a lighter build of the same browser that no other row runs, so its lead is partly the browser's. All native drivers (navigera,
go-rod, chromiumoxide, chromedp) spend tens of milliseconds of their own CPU
or less; the Node drivers spend hundreds and carry 80–150 MB of their own RSS.
Real-world (public internet, best of 3, same run)
example.com goto → title check: 10 of 10 pass; nv-shell 0.18s, gorod 0.34s, chromey 0.35s, nv-serve 0.35s, chromiumoxide 0.35s, nv-brave 0.37s, nv-edge 0.39s, chromedp 0.52s, puppeteer 0.63s, playwright 0.70s.
GitHub browse (awesome-list scroll + trending click-through): 10 of 10 pass; nv-shell 3.18s, gorod 3.29s, nv-serve 3.37s, chromey 3.40s, nv-brave 3.41s, chromiumoxide 3.43s, puppeteer 3.62s, nv-edge 3.70s, playwright 3.81s, chromedp 3.95s.
navigera's click returns once the new page has settled; the other drivers'
clicks return at once and they then poll the URL, so the browse rows do not
time the same readiness. The example.com check is the title; h1 is read, not checked.
Live pages change between runs, so these are informational, not part of the
correctness gate. (The scroll check used to fail at random for every driver:
GitHub sets CSS scroll-behavior: smooth, so scrollY was read mid-animation;
contenders now scroll with behavior: 'instant'.)
chrome-headless-shell (the same Chrome build without its browser UI layer): cold start 0.21s → 0.07s, session 0.51s → 0.25s, browser memory 363 MB → 267 MB.
CDP transport A/B (same build and run): --remote-debugging-pipe (default on Linux/macOS)
vs a DevTools WebSocket port: cold start 0.21s vs 0.23s,
session 0.51s vs 0.51s. No speed difference: the WebSocket handshake itself is ~10 ms. The pipe is the default for two other reasons:
it opens no TCP port that another local process could attach to, and Chrome exits when
navigera dies (EOF on its command pipe), so a killed agent leaks no browser
(tests/edge_cases.rs: over a WebSocket port the browser outlives its driver).
Windows has no pipe transport here and uses the WebSocket, with a kill-on-close
job object giving the same guarantee (killed_session_server_takes_its_browser_down
runs there too).
This build vs navigera 0.7.1, same machine and run (nv-baseline,
built the same way by the ladder's baseline_ref A/B):
session 0.50s → 0.51s (+2%), cold start 0.23s → 0.21s (-8%), browser CPU per session 0.92s → 0.87s. Driver peak RSS 18.5 → 6.6 MB: the static build's allocator now commits memory on demand.
Where navigera's cold start goes (NAVIGERA_TIMINGS, best run): browser up
118 ms, first page 87 ms, close 1 ms.
Agent tools (scripted, warm session, best of 3)
How an agent's tool calls drive a browser: CLI tools run one process per
step (a shell tool), MCP servers get one tools/call per step over a
persistent connection. Same canonical session plus one page snapshot.
Snapshot sizes are each tool's default output, what the agent reads.
Outside the session's total, each tool also snapshots the 1000-row page,
and navigera starts a second agent's session while the first one's browser
is up (sessions share one browser, a context each).
| tool | kind | calls | total wall | per call | snapshot | its size | 1000-row snapshot | its size | 2nd session start | gate |
|---|---|---|---|---|---|---|---|---|---|---|
| navigera (this repo) | CLI | 14 | 0.58s | 28 ms | 37 ms | 34 KB | 76 ms | 41 KB | 168 ms | ✓ |
| navigera on chrome-headless-shell | CLI | 14 | 0.35s | 21 ms | 30 ms | 34 KB | 64 ms | 41 KB | 27 ms | ✓ |
| navigera, known steps in batch | CLI | 6 | 0.55s | 79 ms | 34 ms | 34 KB | 71 ms | 41 KB | — | ✓ |
| agent-browser 0.41.1 (Vercel Labs, Rust) | CLI | 13 | 1.08s | 80 ms | 108 ms | 44 KB | 320 ms | 116 KB | — | ✓ |
| playwright-cli 0.1.23 (Microsoft) | CLI | 13 | 8.81s | 703 ms | 345 ms | 50 KB | 431 ms | 144 KB | — | ✓ |
| Playwright MCP 0.0.84 (Microsoft) | MCP | 13 | 4.91s | 378 ms | 53 ms | 50 KB | 138 ms | 144 KB | — | ✓ |
| Chrome DevTools MCP 1.12.0 (Google) | MCP | 13 | 2.97s | 189 ms | 79 ms | 34 KB | 242 ms | 78 KB | — | ✓ |
Per-call time includes process start (CLI) or JSON-RPC (MCP), the hop to
the daemon, and the CDP work. playwright-cli and Playwright MCP share Playwright's tool
backend, which waits a fixed 500 ms after every action (timeouts.settle); playwright-cli
also starts a Node process per step. batch saves calls, which are an agent's turns,
not time: its row runs the same steps in fewer, longer calls.
Parallel agents (memory, same run)
N agents, each with a session of its own (navigera -s <name>, agent-browser and
playwright-cli sessions, one MCP server each), all open at once on the listing
after a snapshot. Memory is every process the tool started (browsers, daemons,
servers), summed as PSS.
| tool | 1 agent | 4 agents | 8 agents | per extra agent |
|---|---|---|---|---|
| navigera (sessions share one browser) | 397 MB | 636 MB | 942 MB | 78 MB |
| navigera --own-browser (a browser each) | 395 MB | 931 MB | 1604 MB | 173 MB |
| agent-browser 0.41.1 (Vercel Labs, Rust) | 505 MB | 1304 MB | 2338 MB | 262 MB |
| playwright-cli 0.1.23 (Microsoft) | 554 MB | 1422 MB | 2538 MB | 283 MB |
| Playwright MCP 0.0.84 (Microsoft) | 545 MB | 1393 MB | 2496 MB | 279 MB |
| Chrome DevTools MCP 1.12.0 (Google) | 554 MB | 1421 MB | 2544 MB | 284 MB |
Lightpanda (experimental) passes the same correctness gate in 0.16s with ~15 MB of browser memory. It has no rendering engine and one tab (the session opens the second page in the same tab), so it is listed apart from the Chrome-family ranking.
Agent eval: how a generic agent copes with each tool
One model, six tools
DeepSeek-V4.1-Flash drives each tool through one shell function on a local
shop the harness serves: a search, an order form, a price table and a
catalog that loads in pages. Success is judged from what the site recorded
(the search run, the order placed) and from the final answer, never from
the agent's claim; the table and the catalog are drawn per session, so no
file holds an answer. CLI tools run without a shell (the command must start
with the tool, one per call), MCP servers get one tools/call per call,
and every tool starts on the home page. Four tasks, 6 reps each, the tools
at their latest npm releases; means over each tool's 24 sessions
(llm-bench.yml,
run 41579693623):
| tool | success | turns per session | tokens per session | $ for the 24 sessions |
|---|---|---|---|---|
| navigera | 24/24 | 6.8 | 8,705 | $0.0161 |
| navigera, batch allowed | 24/24 | 6.6 | 11,689 | $0.0208 |
| playwright-cli 0.1.23 | 24/24 | 6.9 | 10,355 | $0.0198 |
| agent-browser 0.41.1 | 24/24 | 8.0 | 10,467 | $0.0168 |
| Chrome DevTools MCP 1.12.0 | 24/24 | 9.8 | 14,643 | $0.0235 |
| Playwright MCP 0.0.84 | 24/24 | 8.4 | 15,069 | $0.0248 |
Per task, the lowest median tokens: search and form navigera with
batch (5.3K, 5.8K; agent-browser 5.6K, 9.7K), the table navigera with
batch (4.1K; Playwright MCP 4.9K), the catalog navigera (11.1K;
playwright-cli 14.1K).
What navigera lost: with batch allowed, the catalog cost it 25.9K tokens
(median) against 11.1K without. And a tool's catalog cost varies two to
seven times between its reps, which can reorder the table: in a 3-rep run of
the same build at the same time (run
41579694107)
agent-browser averaged 9,213 tokens a session and navigera 13,462. The
same harness against navigera 0.7.1, same day (run
41568863979):
16,661 tokens a session without batch and 12,755 with it. Since then an
action says what it changed on the page, so the agent sees the result of a
click without another look, and a list item is one line of the tree
instead of three.
Benchmarks time scripted steps. The agent eval asks the question that
matters for agents: can a generic coding agent, given only the tool and its
own documentation, finish real tasks? agent-eval.yml
runs Gemini CLI headless on a
free-tier API key against the local Acme Supply site, with navigera,
playwright-cli and agent-browser in turn:
- skilled: the tool is installed and its vendor's own Agent Skill is in
the workspace (
.agents/skills/); - onboard: the agent gets only the tool's name and repository URL and must install it from its docs, then do a short task.
The six tasks are a price lookup, a purchase with a coupon and an iframe
payment form, a login plus a paginated order table, docs in a new tab with a
collapsed FAQ, a support form inside an iframe, and four named changes on a
170-line settings form that must leave everything else as it was. Success is judged from
the site's recorded state and the final answer, never from the agent's
claim. Every run also records model requests, tokens, shell commands and
off-tool workarounds (curl, ad-hoc scripts). Model, prompt template, Chrome
and site are identical across tools. os: windows (or both) runs the same
jobs on Windows runners, where the agent's shell is PowerShell.
The three tools run as parallel jobs. Keys live in the main environment,
one slot per tool, because Gemini's free quota is per Google Cloud project:
GEMINI_API_KEY (navigera), GEMINI_API_KEY_2 (agent-browser) and
GEMINI_API_KEY_3 (playwright-cli). An empty slot falls back to
GEMINI_API_KEY. Without any key only the scripted baseline runs: it checks
the site, the success checks and the harness with known-good navigera
command plans. Runs that die on a model quota or outage are reported as
⚠ infra, not counted as a tool failure.
agent: openai runs the same tasks, prompt, turn limit and timeout with
any OpenAI-compatible model instead of Gemini CLI (DeepSeek-V4.1-Flash
through SiliconFlow by default; OPENAI_BASE_URL, SILICONFLOW_API_KEY), so
a result can be checked against a second model family.
navigera+jev runs navigera with the decider configured, and
navigera+jev-told also asks for --find / check in the prompt; the
TypeSafe key (TYPESAFE_API_KEY in main) stays in a local relay that the
agent's shell never sees, and each run records the decider's calls and
tokens. decisions: true adds the Jev-against-Gemini decision comparison
above (bench/decide/compare.py).
0.5.0 (2026-10-22), skilled, the six tasks twice per tool:
- DeepSeek-V4.1-Flash (run 39699303497): navigera passed 12/12 at a median 50K tokens and 9 model turns, and 12/12 on Lightpanda (121K); playwright-cli 11/12 (96K), agent-browser 12/12 (153K). With the decider on, navigera passed 12/12 at 43K and the agent used it in 3 of 12 runs unprompted; asked to, in 11 of 12 (12/12, 62K).
- Gemini 3.5 Flash-Lite (run 39691538974): navigera 12/12 at 127K, and 12/12 on Lightpanda (167K).
0.4.1 (2026-10-14), Gemini 3.5 Flash-Lite, skilled: navigera passed 10/10 runs of the five tasks (2 each), median 7 model turns and 103K tokens.
0.3.0 vs 0.2.1, same model, launched together (purchase + account, 2
runs each, all passed): ax now waits for data a page is still fetching,
and the skill teaches chaining sure steps in one shell call. Median model
turns 18 → 10, median tokens 288K → 167K; every 0.3.0 run used fewer tokens
than every 0.2.1 run of the same task.
The scripted session didn't slow down: 0.51 s vs 0.49 s, cold start 0.23 s
both.
Natural checks (agent-check.yml,
every master push, PRs and nightly): Gemini is told in plain words to buy
something with navigera and its skill. On the local shop, once on Linux
and once on Windows through PowerShell (real checks, judged from the
recorded order), and once on Sauce Labs' public demo
shop saucedemo.com, which exists for automation
practice and takes no real payment (informational, judged from the
confirmation and the order total).
Contributing
See CONTRIBUTING.md for building, the test suites, reading CI results, A/B benchmarks and releasing.
Layout
src/protocol.rs: CLI parsing, the JSON-lines protocol andDriver(theOPStable is--help)src/browser.rs:BrowserSession: browser discovery (incl. chrome-headless-shell), launch flags, tabssrc/cdp/: from-scratch CDP engine: pipe/WebSocket transport, JSON-RPC client (NAVIGERA_CDP_TRACE), page ops,ax.rs(snapshot tree),events.rs(dialogs, popups, navigation),procjob.rs(Windows job object)src/session.rs: named sessions: socket server (Unix socket; loopback TCP + token on Windows), client, detachedstartsrc/cdp/attach.rs:--attach(your running browser) and--profile(persistent navigera browser)src/mcp.rs:navigera mcp, the Model Context Protocol server on stdiosrc/decide.rs:find/check: the decision model's client and how page elements become optionssrc/install.rs:install-browser(Chrome for Testing's index, the download through curl, the zip unpacked and checked)src/timing.rs:NAVIGERA_TIMINGS=1launch/close phase timings on stderrtests/: browser e2e: serve protocol, sessions, and the Acme Supply scenariobench/site/server.py: Acme Supply, a deterministic local shop (SPA, iframes, shadow DOM, dialogs, popups, login, upload)bench/ladder/: driver ladder and agent-tool benchmark (publish.jsonper run,gen_readme.py)bench/agent-eval/: Gemini CLI agent eval across navigera, playwright-cli and agent-browserbench/llm/: one model drives each tool (navigera, agent-browser, playwright-cli, Playwright MCP, Chrome DevTools MCP) on a site the driver serves, judged from its recorded state (llm-bench.yml)bench/decide/: decision benchmark (compare.py), TypeSafe relay (relay.py) and a stand-in for runs without a key (mock_jev.py)tools/:cdp_check.py(protocol correctness),protocol_dump.sh,cft_matrix.py(Chrome version matrix)skills/navigera/SKILL.md: the agent guide (compiled into the binary:navigera skill).github/workflows/:ci.yml(tests, Chrome matrix, protocol check),bench.yml,agent-eval.yml,llm-bench.yml,release.yml,chrome-bisect.yml,vendor.yml
