Browser use & computer use for coding agents, powered by TypeSafe Jev.
Calibrated judgments from a System One model. Control loop in code. No vision model, no prompt-and-parse.
English · 简体中文
Real runs in ego lite, recorded by the tool itself (run --step-screenshots). Left: the page
exactly as Jev saw it before each step. Right: Jev's calibrated judgment for that step and the
action the code controller executed. Every demo is a single jev-browser run command.
Wikipedia, multi-hop on a real site — type into the search box and press Enter, pick the right result, follow links across three articles (a 500-link page: goal-aware candidate ordering keeps the relevant links in the list). 6 steps · $0.0029 · 21 s. ▶ MP4 1080p
Sign in with a secret, then fill a form with a dropdown — the password is typed but shown to
Jev only as inputs.password; the topic is chosen from the dropdown's options in a second
question. 10 steps · $0.0013 · 18 s. ▶ MP4 1080p
Three identical "Start free trial" buttons — the goal names the Team plan; Jev picks the right button from page structure alone at probability 1.00. 3 steps · $0.0003 · 3 s. ▶ MP4 1080p · Bonus: GitHub repository navigation (MP4)
GitHub does not play repository MP4s inline, so the GIFs above are previews; the MP4s are the
full-quality recordings (1080p, crossfades). Reproduce any of them with
node scripts/make-demo.mjs <run.json> out.mp4 --gif out.gif after a run made with --step-screenshots <dir>.
| Works in the user's browser | Default backend is ego lite: the agent reuses your signed-in sessions, and hands the browser to you when it hits a login, CAPTCHA or consent wall. Resume from the same tab afterwards. |
| Calibrated, not chatty | Jev returns probabilities, not prose. Every decision is a number you can threshold, journal and tune. A step costs about $0.0003 and takes about a second. |
| Never invents text | Values that must be typed come from inputs; Jev only selects among them. secrets are typed but never sent to the model or written to disk. |
| Code stays in control | Legal actions are filtered in code, tried/blocked edges are remembered, loops are detected, step/cost/time budgets are enforced, and the final page is verified before reporting success. |
| Fits every agent | One self-contained skill directory. CLI for Claude Code, Codex, Cursor and any shell-capable agent; a dependency-free MCP server for Claude Desktop, Cursor and Codex. |
| Nothing to install | Node 22+ and a TYPESAFE_API_KEY. Zero npm dependencies. |
flowchart LR
O[observe page<br/>url · text · elements] --> Q[one Jev request<br/>goal_done · blocker · action<br/>click_target · type_target · type_value …]
Q --> C{code controller<br/>memory · budgets · loop detection}
C -- success / needs_user / stuck --> R[result + journal]
C -- act --> A[click · type · select · scroll · back · navigate]
A --> O
Per step, one POST /v1/systemone carries the page state and the questions below. They are
independent and evaluated in parallel, so speculative ones are asked up front and consumed only
when relevant.
| question | type | used by code as |
|---|---|---|
goal_done |
noul | success when ≥ 0.85 (≥ 0.7 on the final verification pass) |
blocker |
choice: none · login_required · verification_challenge · consent_or_permission_dialog · error_page · missing_information | needs_user when a non-none option ≥ 0.6 |
action |
choice over the legal actions only (click · type · select · scroll · go_back · navigate · wait · stop) | preference order |
click_target / type_target / select_target |
choice over element ids + none |
which element |
type_value |
choice over input keys + none |
which provided value |
submit_after_type |
noul | press Enter after typing |
progress |
score: moved away · no change · closer · accomplished | go back on regression |
Full details: references/questions.md.
git clone https://github.com/ChenYCL/jev-browser-skill.git && cd jev-browser-skill
export TYPESAFE_API_KEY=... # https://console.typesafe.ai
node skills/jev-browser/bin/jev-browser.mjs doctor # key, API, ego lite, Chrome, Safari, install status
node skills/jev-browser/bin/jev-browser.mjs install # link into every agent + register Claude Desktop MCPOptional: npm i -g . puts jev-browser on your PATH (the examples below assume it).
install is idempotent, previews with --dry-run, backs up every file it edits and reverts with --uninstall.
| target | what it does | default |
|---|---|---|
claude-code |
symlink ~/.claude/skills/jev-browser |
✔ |
codex |
symlink ~/.codex/skills/jev-browser |
✔ |
agents |
symlink ~/.agents/skills/jev-browser (skills.sh convention: Codex, opencode, Gemini CLI, …) |
✔ |
cursor |
symlink ~/.cursor/skills/jev-browser |
✔ |
claude-desktop |
mcpServers.jev-browser in claude_desktop_config.json |
✔ |
cursor-mcp |
mcpServers.jev-browser in ~/.cursor/mcp.json |
|
codex-mcp |
[mcp_servers.jev-browser] in ~/.codex/config.toml |
jev-browser install --targets claude-code,claude-desktop --dry-runOther routes:
- Claude Code plugin:
claude plugin marketplace add ChenYCL/jev-browser-skillthenclaude plugin install jev-browser@jev-browser-skill - skills.sh:
npx skills add ChenYCL/jev-browser-skill --skill jev-browser - Manual: copy
skills/jev-browser/anywhere your agent reads skills from. - Windows: prefer
install --copy(symlinks need Developer Mode or elevation).
MCP hosts start servers without your shell environment, so the installer stores the key in
~/.config/jev-browser/config.json (mode 0600) when TYPESAFE_API_KEY is exported. Restart the host afterwards.
# navigate
jev-browser run --goal "Open the pricing page" --url https://example.com
# type a provided value, then press Enter (Jev decides when Enter is the natural submit)
jev-browser run --goal 'Search the catalog for "blue widget" and open its product page' \
--url https://shop.example.com --input query="blue widget"
# sign in: the email is an input, the password is a secret (typed, never sent to the model)
jev-browser run --goal "Sign in and reach the dashboard" --url https://app.example.com/login \
--input email=ada@example.com --secret password=hunter2
# dedicated headless Chrome, JSON result only
jev-browser run --goal "Add the Red Gadget to the cart" --url https://shop.example.com \
--backend chrome --headless --json --screenshot /tmp/cart.png
# look before acting: the page exactly as Jev sees it / the first-step questions without spending a request
jev-browser observe --url https://example.com --json
jev-browser run --dry-run --goal "…" --url https://example.com
# raw Jev judgments, browser-independent
jev-browser judge --state '{"ticket":"My card was charged twice"}' \
--questions '{"refund":{"type":"noul","instructions":"Does `ticket` ask for a refund?"}}'
jev-browser pick --question "Which link opens the plans page?" --candidate pricing="link 'Pricing'" --candidate docs="link 'Docs'"Tips that matter: write goals in English describing the end state; put everything that
must be typed in --input (quoted strings in the goal are added automatically); use --secret
for credentials.
| command | purpose |
|---|---|
run |
accomplish a goal (--goal, --url, --input k=v…, --secret k=v…) |
observe |
print the page as Jev sees it (--url, --screenshot) |
judge |
raw System One call (--state / --state-file, --questions / --questions-file, --model) |
pick |
one Choice over named candidates (--question, --candidate id=desc…, --context, --no-none) |
doctor |
environment check (--offline skips the live API probe, --json) |
config |
show · path · set <key.path> <value> · unset <key.path> · set-key [<key> | --from-env] |
install |
--targets a,b · --dry-run · --copy (copy instead of symlink; use on Windows) · --uninstall · --home <dir> |
mcp |
MCP server over stdio |
run options: -g/--goal · -u/--url · -i/--input · -s/--secret · -b/--backend ego\|chrome\|safari ·
--max-steps · --budget-usd · --max-ms · --model · --space-id and --page-label (ego: resume a
task space) · --keep / --no-keep (leave the final page open; default keep on success) · --headless ·
--cdp-url (chrome: attach) · --screenshot <file> · --step-screenshots <dir> (one PNG per step, the page as Jev saw it) · --dry-run · --journal-dir <dir> · --no-journal ·
--json · -q/--quiet. jev-browser --help prints the same list.
| status | meaning | exit |
|---|---|---|
success |
goal verified on the final page | 0 |
needs_user |
blocker detected; on ego the tab was handed to you. Continue with --space-id <id> |
3 |
stuck |
repeated no-effect actions, a loop, or Jev judged nothing listed helps | 2 |
max_steps · budget_exhausted · timeout |
a limit was hit (--max-steps 25, --budget-usd 0.25, --max-ms 300000) |
2 |
error |
backend or API failure | 2 |
Every run writes <journalDir>/<runId>/steps.jsonl (state hash, compact answers, chosen action,
whether the page changed, cost), requests.jsonl and run.json, with secrets redacted.
| backend | when | notes |
|---|---|---|
ego (default) |
you want the agent in your browser with your logins, and the option to take over | keeps the result tab open on success; handOff on blockers; resume with --space-id |
chrome |
unattended runs, CI, no window | own profile dir, --headless, or --cdp-url http://127.0.0.1:9222 to attach |
safari |
WebKit | enable Develop → Allow Remote Automation once |
Three judging backends serve the same /v1/systemone contract, and all three are first-class
options — but hosted Jev is the default: with no config file and no environment, a run uses
https://api.typesafe.ai (and a loopback baseUrl — a local model — is priced at $0).
jev-browser tier list # the three tiers: what each is, what it needs, how to start it, its port, its score, its bar
jev-browser tier status # what a run would use right now: baseUrl, tier, resolved goal_done bar, endpoint
jev-browser tier use kev # one tier's export line and start command (nothing is written unless you add --persist)The GGUF readout (node skills/jev-browser/bin/jev-local.mjs, port 8092) needs no Python and
serves an Apple Silicon llama.cpp server; Kev (node skills/jev-browser/bin/jev-kev.mjs, port
8008) trades a Python venv, a 9.34 GB base and tens of GB of GPU memory for the accuracy tier. The
tier table with every measured number — scores, latency, disk, memory, each backend's goal_done
bar and the known holes — lives in SKILL.md.
A local tier is one command: setup fetches whatever is missing through the launcher itself, starts
the server in the background, writes baseUrl and the placeholder key into your config, then proves
the endpoint works by asking it one real question.
jev-browser setup local-readout # llama.cpp + the registry GGUF; prints the log path and the pid
jev-browser setup status # installed / running / configured, per local tier
jev-browser setup stop local-readoutjev-browser setup kev is the same for the accuracy tier and needs uv as well (it clones the Kev
checkout and runs uv sync --extra serve). Both write under ~/.jev-browser/, never into this repo.
The manual route still exists — start bin/jev-local.mjs yourself and export the two variables it
prints — and jev-browser tier use <tier> --persist still stores that line's values for you.
node skills/jev-browser/bin/jev-webui.mjs # prints http://127.0.0.1:8765/
node skills/jev-browser/bin/jev-webui.mjs --port 9000 --openOne page over the same skill, not a second implementation: the panels call the same lib/tiers.mjs,
lib/doctor.mjs and lib/config.mjs the CLI does — so they cannot disagree with tier status,
doctor or config show — and edits go to the same ~/.config/jev-browser/config.json. Every
command it starts is one of this repo's own bin/*.mjs scripts, spawned with an argv array (never a
shell), so no field on the page can become a command.
The server binds 127.0.0.1 only — never 0.0.0.0, so nothing on it is reachable from your
network — and no route returns your API key: the config panel shows only whether one is set, and
secrets typed into the Run panel are never echoed back into the page or the log pane.
| panel | what it does |
|---|---|
| Tiers | the three judging tiers — what each is, what it needs, its port, its 20-item score and its goal_done bar, hosted marked as the default — plus what a run would use right now, start/stop for the two local servers with their output streaming into the page, and tier use's text (saving it to the user config is its own labelled button) |
| Config | the effective configuration with its sources, editable, with the diff a save produced and an unset for every key the user file sets |
| Doctor | doctor() live or offline: every check with its status, detail and hint |
| Models | the local registry: which GGUF is downloaded, which is the default, which file is serving, and a one-click start with --model-name or an existing on-disk --model <path> |
| Judge | a state and a question set against the configured endpoint, with each answer's choice, confidence and probability table |
| Run | a real run (jev-browser run --json) with live progress, the final status / steps / cost and the per-step journal |
jev-browser mcp speaks MCP over stdio with zero dependencies. Tools: jev_browse, jev_observe,
jev_judge, jev_pick, jev_doctor, jev_config. Results come back as JSON text and
structuredContent. See references/mcp.md.
Precedence: defaults → ~/.config/jev-browser/config.json → ./jev-browser.config.json (or
$JEV_BROWSER_CONFIG) → environment → flags.
jev-browser config show
jev-browser config set model jev-1.13.0 # pin the model version
jev-browser config set thresholds.goalDone 0.9 # stricter success
jev-browser config set-key --from-env # persist the key for MCP hostsEnvironment: TYPESAFE_API_KEY TYPESAFE_BASE_URL TYPESAFE_DEFAULT_MODEL JEV_BROWSER_BACKEND
JEV_BROWSER_MAX_STEPS JEV_BROWSER_BUDGET_USD JEV_BROWSER_JOURNAL_DIR JEV_BROWSER_CHROME_CDP_URL
JEV_BROWSER_HEADLESS JEV_BROWSER_EGO_SERVER_NAME CHROME_PATH (Chrome executable override)
JEV_BROWSER_CONFIG (explicit project config file). Every key is documented in
references/config.md.
npm test # unit + e2e; live Jev when TYPESAFE_API_KEY is set, else a local mock
npm run test:e2e:mock # fully offline (needs Chrome)
JEV_BROWSER_TEST_SAFARI=1 npm run test:e2e # also drive SafariCI (.github/workflows/test.yml) runs the unit suite and the mock e2e suite on Ubuntu with headless Chrome;
the live suite is meant to be run locally, so no API key is ever needed in the cloud.
The e2e suite serves a fixture site (catalog, search, login, pricing/trial, cart, contact form with a dropdown, long docs page, restricted area) and runs nine goal scenarios per backend: navigation, search with typed input, login with a secret, choosing the right one of three identical "Start free trial" buttons, add to cart, form + dropdown, scroll to reveal, hand-off on a blocker, limits on an impossible goal, plus observe/dry-run, the CLI round-trip and ego hand-off → resume. Tests never write outside the repo.
Measured on macOS with live Jev (2026-09-22):
| full suite | 54 passed, 0 failed, 5 skipped (Safari opt-in), ~40 s, three consecutive runs identical |
| per-scenario determinism | same status every run; step counts identical on ego, ±1 on two Chrome scenarios |
| cost | $0.00006–0.00094 per scenario run (always under $0.001), about $0.02 per full suite |
| line coverage | 87 % overall (controller 92 %, questions/config/util 100 %, observe 99.6 %) |
Real sites, first try, via ego lite: TypeSafe docs → the Choice page in 2 steps / 9 s / $0.0005;
GitHub → docs/ATOMIC_PLANNING.md in NanoJev in 4 steps / 23 s / $0.0016.
Each step sends one request to api.typesafe.ai containing the goal, the non-secret inputs, and a
compact view of the current page: URL, title, headings, up to observation.maxTextChars (3000) of
visible text, the descriptions of listed interactive elements (role, name, href, placeholder,
current value), the previous page's excerpt and the last action. Nothing else is sent: no
screenshots, no cookies, no HTML, no secrets (their values are replaced by a fixed marker, and
password fields report (hidden)). Journals stay local under journalDir with secrets redacted.
TypeSafe states that API requests are not used for training; see their
legal page. Pin a model version with config set model jev-1.13.0
if reproducibility matters.
- Dynamic legal moves. Like NanoJev's Snake controller, code filters the action set
(no
scroll_downat the bottom, notypewithout inputs) and the model breaks ties. - Edge memory.
(page state, action)pairs that produced no change are blocked; pairs already tried are deprioritised, so A → B → back → A does not repeat forever. - Select, don't generate. Typed values, dropdown options and URLs are chosen from candidates supplied by code; Jev 1.13 reads literally and does not generate text.
- Goal-aware candidates. Before truncating to
observation.maxCandidates, code ranks elements whose name or href mention goal/input keywords first, then viewport, then position, so a relevant link far down a long page is still offered. Keywords matching most elements are ignored. - "Probably done" is not done. A model
stopat 70–85 % goal probability first spends one more step on an untried action; only ≥ 85 % (or no alternatives) ends the run. - Speculative fan-out. All questions of a step go in one request; unused answers are free in latency and cheap in tokens.
- Verification before success. Success needs
goal_done ≥ 0.85on the current page, and a final check runs when the step budget ends.
- Same-origin iframes are not enumerated; clicks that open a new tab are not followed.
- Canvas apps, drag and drop, file uploads and hover-only menus are not handled.
- Pages with more than
observation.maxCandidates(100, API max 255) interactive elements are truncated; scrolling compensates. - Safari was implemented against the W3C WebDriver spec but not exercised on a machine with remote automation enabled.
- Jev's primary training language is English; translate goals for best accuracy.
skills/jev-browser/ the skill (self-contained; this is what installers link)
SKILL.md agent-facing instructions
bin/jev-browser.mjs CLI + MCP entry point
lib/controller.mjs code controller
lib/questions.mjs the atomic question set
lib/observe.mjs page enumerator shared by all backends
lib/typesafe.mjs HTTP client (retry, cost, cache)
lib/backends/ ego · chrome · safari
lib/mcp.mjs MCP stdio server
references/ questions · config · backends · mcp
tests/ unit + e2e (fixture site, mock TypeSafe, per-backend scenarios)
.claude-plugin/ .mcp.json Claude Code plugin packaging
Issues and PRs are welcome. npm test must stay green in mock mode (no key needed); add a
fixture page and a scenario in tests/e2e/scenarios.mjs for new behaviours. Question wording
and thresholds live in lib/questions.mjs and lib/config.mjs; keep them in one place.
TypeSafe for Jev and the System One API · NanoJev for the atomic-judgments-plus-code-planning design · ego lite for a browser built for humans and agents together.
MIT



