jevway 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/INSTALL-FOR-AGENTS.md +32 -38
  2. package/README.md +193 -150
  3. package/dist/cli/daemon-call.js +78 -0
  4. package/dist/cli/dispatch.js +74 -0
  5. package/dist/cli/flags.js +152 -0
  6. package/dist/cli/help.js +46 -0
  7. package/dist/cli/journey-command.js +162 -0
  8. package/dist/cli/look-command.js +47 -0
  9. package/dist/cli/main.js +64 -0
  10. package/dist/cli/output.js +27 -0
  11. package/dist/cli/run-command.js +173 -0
  12. package/dist/cli/session-commands.js +132 -0
  13. package/dist/daemon/client.js +175 -0
  14. package/dist/daemon/paths.js +35 -0
  15. package/dist/daemon/protocol.js +28 -0
  16. package/dist/daemon/server.js +320 -0
  17. package/dist/jev/replay.js +7 -3
  18. package/dist/jev/retry.js +29 -0
  19. package/dist/journeys/journey-doc.js +2 -1
  20. package/dist/journeys/journey-session.js +14 -3
  21. package/dist/journeys/render-viewer.js +163 -150
  22. package/dist/mcp/instructions.js +18 -0
  23. package/dist/mcp/journey-tools.js +117 -0
  24. package/dist/mcp/results.js +53 -0
  25. package/dist/mcp/server.js +50 -0
  26. package/dist/mcp/task-tools.js +197 -0
  27. package/dist/mcp.js +10 -337
  28. package/dist/output/layout.js +50 -0
  29. package/dist/output/manifest.js +80 -0
  30. package/dist/output/run-folder.js +38 -0
  31. package/dist/recording/describe-step.js +66 -0
  32. package/dist/{task → recording}/recording-page.js +164 -18
  33. package/dist/{task → recording}/recording.js +33 -91
  34. package/dist/recording/run-page.js +155 -0
  35. package/dist/session/session-service.js +193 -0
  36. package/dist/setup/atomic-write.js +13 -0
  37. package/dist/setup/clients.js +180 -0
  38. package/dist/setup/command-output.js +15 -0
  39. package/dist/setup/doctor-command.js +16 -7
  40. package/dist/setup/doctor.js +17 -2
  41. package/dist/setup/env-file.js +22 -0
  42. package/dist/setup/setup-command.js +104 -0
  43. package/dist/setup/setup-wizard.js +216 -0
  44. package/dist/setup/skill-command.js +101 -0
  45. package/dist/setup/terminal-prompter.js +51 -0
  46. package/dist/setup/unified-diff.js +69 -0
  47. package/dist/task/present.js +90 -0
  48. package/dist/task/run-task.js +74 -11
  49. package/dist/task/start-task.js +21 -5
  50. package/package.json +9 -7
  51. package/skill/jevway/SKILL.md +50 -14
  52. package/dist/cli.js +0 -112
@@ -4,11 +4,17 @@ You are an agent asked to install jevway for a person. jevway is an MCP server,
4
4
  It hands browser goals to TypeSafe Jev. Both come from the npm package `jevway`.
5
5
 
6
6
  Read this whole file before you run anything. Run only these:
7
- - `npx -y jevway doctor` (or `jevway doctor`);
7
+ - `npx -y jevway doctor --json` (or `--plan`, or `jevway doctor`);
8
+ - `npx -y jevway setup --client <name> --print [--json]`, which prints how to register jevway with a client and
9
+ changes nothing;
10
+ - `npx -y jevway skill --install <skills folder>`;
8
11
  - `npm install -g jevway`;
9
12
  - the client's own `mcp add` command, or an edit to its MCP config file that the person approved.
10
13
 
11
- Done means: `jevway doctor` exits 0, and the client lists the `jevway_*` tools.
14
+ Never run plain `jevway setup` yourself: it is the person's interactive setup, and without a terminal it exits 2 with
15
+ `{ "ok": false, "error": { "code": "needs_tty", … } }`.
16
+
17
+ Done means: `jevway doctor --json` prints `"ready": true` (exit 0), and the client lists the `jevway_*` tools.
12
18
 
13
19
  ## Rules
14
20
 
@@ -29,10 +35,11 @@ Done means: `jevway doctor` exits 0, and the client lists the `jevway_*` tools.
29
35
  ## 1. Check the machine
30
36
 
31
37
  ```bash
32
- npx -y jevway doctor --plan
38
+ npx -y jevway doctor --json
33
39
  ```
34
40
 
35
- It prints the setup steps still missing, numbered. It needs no key to run and never prints one. It checks:
41
+ It prints `{ ready, findings: [{ check, level, message, fix? }] }`; `--plan` prints the steps still missing, numbered.
42
+ It needs no key to run and never prints one. It checks:
36
43
  - Node 24 or later;
37
44
  - a key, and which route it selects;
38
45
  - the browser source;
@@ -47,7 +54,11 @@ jevway needs one of these:
47
54
  - `TYPESAFE_API_KEY` (preferred): Jev on the TypeSafe API, with screenshots and Box.
48
55
  - `OPENROUTER_API_KEY`: Jev, and the other models, through OpenRouter.
49
56
 
50
- Ask the person to add the line themselves, in their own editor or terminal, never in this chat:
57
+ The easiest way for the person: run `npx -y jevway setup` in their own terminal. It reads the key with hidden input,
58
+ writes only that line to `~/.config/jevway/env` (mode 600), tests the browser, and offers to register their MCP
59
+ clients, showing each change first.
60
+
61
+ Or they add the line themselves, in their own editor or terminal, never in this chat:
51
62
 
52
63
  ```bash
53
64
  mkdir -p ~/.config/jevway && touch ~/.config/jevway/env && chmod 600 ~/.config/jevway/env
@@ -73,39 +84,16 @@ Use the server name `jevway`. Add `JEVWAY_BROWSER` only if the person chose some
73
84
  /plugin install jevway@jevway
74
85
  ```
75
86
 
76
- Or the server alone:
77
-
78
- ```bash
79
- claude mcp add jevway -s user -- npx -y -p jevway jevway-mcp
80
- ```
81
-
82
- **Codex**:
87
+ Otherwise ask jevway for the exact command or snippet. It prints, and changes nothing:
83
88
 
84
89
  ```bash
85
- codex mcp add jevway -- npx -y -p jevway jevway-mcp
90
+ npx -y jevway setup --client claude-code --print # also: codex, cursor, opencode, other
91
+ npx -y jevway setup --client cursor --print --json # { client, kind, command?, file?, snippet }
86
92
  ```
87
93
 
88
- That writes this to `~/.codex/config.toml`:
89
-
90
- ```toml
91
- [mcp_servers.jevway]
92
- command = "npx"
93
- args = ["-y", "-p", "jevway", "jevway-mcp"]
94
- ```
95
-
96
- **Cursor**: in `~/.cursor/mcp.json`, add to `mcpServers` (diff first):
97
-
98
- ```json
99
- "jevway": { "command": "npx", "args": ["-y", "-p", "jevway", "jevway-mcp"] }
100
- ```
101
-
102
- **OpenCode**: in `~/.config/opencode/opencode.json`, add to `mcp` (diff first):
103
-
104
- ```json
105
- "jevway": { "type": "local", "command": ["npx", "-y", "-p", "jevway", "jevway-mcp"], "enabled": true }
106
- ```
107
-
108
- **Any other stdio MCP client**: command `npx`, args `["-y", "-p", "jevway", "jevway-mcp"]`.
94
+ - `kind: "command"` (Claude Code, Codex): run `command`. For Codex, `snippet` is the table it writes to `file`.
95
+ - `kind: "config"` (Cursor, OpenCode): merge `snippet` into `file`, keeping every other server; show the person the
96
+ diff first and wait for approval. `other` gives the `command` and `args` for any stdio MCP client.
109
97
 
110
98
  ## 4. The browser
111
99
 
@@ -119,13 +107,19 @@ args = ["-y", "-p", "jevway", "jevway-mcp"]
119
107
 
120
108
  ## 5. The skill (clients other than the Claude Code plugin)
121
109
 
122
- The skill says when to use `jevway_task` and when to use chrome-devtools. It is shipped in the package, at
123
- `skill/jevway/SKILL.md`. Copy that folder into the client's skills folder if it has one, e.g.
124
- `~/.claude/skills/jevway/` or `~/.codex/skills/jevway/`.
110
+ The skill says when to use `jevway_task` and when to use chrome-devtools. It ships in the package; `jevway skill`
111
+ prints it, and this copies it into a client's skills folder as `<folder>/jevway/`:
112
+
113
+ ```bash
114
+ npx -y jevway skill --install ~/.codex/skills # or ~/.claude/skills, ~/.cursor/skills, ~/.config/opencode/skills
115
+ ```
116
+
117
+ It prints `{ ok, status, path, files }`. A different `jevway` skill already there (or a symlink to one) is left alone
118
+ with `"code": "skill_exists"` and exit 1; replace it with `--force` only if the person agrees.
125
119
 
126
120
  ## 6. Check the outcome
127
121
 
128
- 1. `npx -y jevway doctor` exits 0. Every line is `ok` or `warn`, and the last line starts with `Ready`.
122
+ 1. `npx -y jevway doctor --json` prints `"ready": true` and exits 0 (every finding `ok` or `warn`).
129
123
  2. The client lists the tools: `jevway_task`, `jevway_look`, `jevway_end` and `jevway_journey_start`, `_step`,
130
124
  `_autopilot`, `_annotate`, `_finish`.
131
125
  - In Claude Code: `/mcp`.
package/README.md CHANGED
@@ -1,219 +1,262 @@
1
1
  # jevway
2
2
 
3
- Give your coding agent a browser goal. [TypeSafe Jev](https://docs.typesafe.ai) picks each click, type, and select in about 300 ms, and the agent gets back **one short summary** instead of making every decision itself.
3
+ [![npm](https://img.shields.io/npm/v/jevway)](https://www.npmjs.com/package/jevway)
4
+ [![CI](https://github.com/AbdallahAHO/jevway/actions/workflows/ci.yml/badge.svg)](https://github.com/AbdallahAHO/jevway/actions/workflows/ci.yml)
5
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
4
6
 
5
- ```text
6
- agent ──jevway_task(goal, values, done_when)──▶ jevway ──one Jev request per step──▶ Chrome
7
- agent ◀── { status, url, steps, stuck? } ──────┘ (same Chrome chrome-devtools inspects)
8
- ```
7
+ Give your coding agent a browser goal and get back one short, checked summary. [TypeSafe Jev](https://docs.typesafe.ai)
8
+ picks each click, type and select in about 300 ms, so your agent stops spending a turn on every step.
9
9
 
10
- The only thing it needs is `TYPESAFE_API_KEY` or `OPENROUTER_API_KEY`. Other decision models, some of which read screenshots, are described [below](#models). No second LLM is involved: the agent passes the text values it already knows, and Jev *selects* which value goes into which field (a speculative `value_for_<field>` head in the same request).
10
+ <!-- Source: docs/images/how-it-flows.mmd (render with @mermaid-js/mermaid-cli: -t default -b white -s 2) -->
11
+ ![Your agent hands jevway a goal; each step jevway reads the page, asks Jev for one decision and acts in Chrome; the agent gets one summary back](docs/images/how-it-flows.png)
11
12
 
12
- Formerly jev-browse: its tools were `browser_task`, `browser_look` and `browser_task_end`, and its environment variables `JEV_*`; none of those names work any more.
13
+ jevway is an MCP server and a CLI. It works with Claude Code, Codex, Cursor, OpenCode and any other MCP client, in your
14
+ own signed-in Chrome or a throwaway one.
13
15
 
14
- Built on the loop, DOM snapshot, and guards from [browser-use/jev-ultrafast](https://github.com/browser-use/jev-ultrafast) (MIT).
16
+ ## Why
15
17
 
16
- ## Install into any agent
18
+ Driving a browser click by click costs an agent a model turn, a page snapshot and a few seconds per step. A 15-step
19
+ form takes minutes and fills the context window with snapshots. jevway hands the whole goal to Jev, a model built to
20
+ choose browser actions, and returns when it's done or when it needs the agent:
17
21
 
18
- **Claude Code plugin** (the MCP server and the skill in one install), inside Claude Code:
22
+ - **It never invents input.** Jev types only the `values` the agent passed. A missing value comes back as
23
+ `needs_input`, naming the field.
24
+ - **It stops before anything irreversible.** Pay, send, delete and submit come back as `needs_confirmation`. One
25
+ confirmation allows one click, on the control that was named.
26
+ - **It checks its own success.** `done` needs a separate check of `done_when` on the final page, plus any exact
27
+ `checks` you add, and comes with the page's own words as evidence.
28
+ - **Model output only ever selects a listed element.** It never becomes a selector, a script or typed text.
29
+ - **It can record what it did.** A video of the run, a screenshot before every action, and an HTML page to review it,
30
+ built in. See [Recordings](#recordings).
19
31
 
20
- ```text
21
- /plugin marketplace add AbdallahAHO/jevway
22
- /plugin install jevway@jevway
23
- ```
32
+ ## What you need
24
33
 
25
- On Claude Code 2.1.275 or later this works in one step: `/plugin install jevway --marketplace AbdallahAHO/jevway`.
34
+ jevway has no browser extension and no Playwright: it drives Chrome directly. Three things are required, and two
35
+ optional tools unlock more.
26
36
 
27
- **Or the MCP server alone**, one line per client:
37
+ | | Needed for | Without it |
38
+ | --- | --- | --- |
39
+ | **Node.js 24+** | running jevway (`npx` fetches it) | it won't start |
40
+ | **Google Chrome** (or Chromium) | every run: your running Chrome, or a throwaway one jevway starts | runs fail; set `CHROME_PATH` if Chrome isn't in the usual place |
41
+ | **An API key**: `TYPESAFE_API_KEY` (recommended) or `OPENROUTER_API_KEY` | every decision Jev makes | the server still starts and lists its tools, and `jevway_look` works, but tasks and journeys answer with the setup fix |
42
+ | `ffmpeg` *(optional)* | the `run.mp4` video in a recording | recordings keep their step screenshots and HTML page, without the video |
43
+ | chrome-devtools MCP *(optional)* | inspecting what jevway left on screen: console, network, performance, DOM details | nothing is lost in jevway itself; your agent just has no deeper inspection tool |
28
44
 
29
- ```bash
30
- claude mcp add jevway -s user -- npx -y -p jevway jevway-mcp # Claude Code
31
- codex mcp add jevway -- npx -y -p jevway jevway-mcp # Codex
32
- ```
45
+ **Which key?** Both run the same Jev model. The TypeSafe key adds **Box**, which locates controls on the screenshot:
46
+ it double-checks the click Jev chose and whether it's covered, rescues a step when Jev is stuck, can click a canvas
47
+ control (opt-in), and marks each journey screen's primary action and the controls missing from the page's code. Through OpenRouter those Box features are off; everything else works.
33
48
 
34
- Cursor (`~/.cursor/mcp.json`, under `mcpServers`), or any other stdio MCP client:
49
+ `npx -y jevway doctor` checks all of this and names the fix for anything missing.
35
50
 
36
- ```json
37
- "jevway": { "command": "npx", "args": ["-y", "-p", "jevway", "jevway-mcp"] }
38
- ```
51
+ ## Quick start
39
52
 
40
- OpenCode (`opencode.json`, under `mcp`):
41
-
42
- ```json
43
- "jevway": { "type": "local", "command": ["npx", "-y", "-p", "jevway", "jevway-mcp"], "enabled": true }
44
- ```
53
+ 1. **Get a key** from [TypeSafe](https://docs.typesafe.ai) (or use an [OpenRouter](https://openrouter.ai) key).
45
54
 
46
- **The key** goes in `~/.config/jevway/env` (`$XDG_CONFIG_HOME/jevway/env` when that is set), so it never has to go
47
- into a chat or a synced client config:
55
+ 2. **Run the setup** in your terminal:
48
56
 
49
- ```bash
50
- mkdir -p ~/.config/jevway && touch ~/.config/jevway/env && chmod 600 ~/.config/jevway/env
51
- # then add the line TYPESAFE_API_KEY=… (or OPENROUTER_API_KEY=…) in an editor
52
- ```
57
+ ```bash
58
+ npx -y jevway setup
59
+ ```
53
60
 
54
- - `jevway` and `jevway-mcp` read the file at start. A variable already in the environment wins, so a client's `env`
55
- block works too.
56
- - Some clients pass servers a filtered environment; the file still works there.
57
- - Without a key the server still starts and lists its tools: `jevway_look` works, and the other tools answer with the fix.
61
+ It asks for the key without showing it and saves it to `~/.config/jevway/env` (mode 600, never a chat or a synced
62
+ config), tests the browser, and registers jevway with the clients it finds: Claude Code, Codex, Cursor, OpenCode.
63
+ Every change is shown first, as a command or a diff, and made only on a yes. It ends with `jevway doctor`.
58
64
 
59
- **Check the setup:**
65
+ In Claude Code you can instead install the plugin, which brings the server and the skill that tells the agent when
66
+ to use it (then put the key in `~/.config/jevway/env` yourself):
60
67
 
61
- ```bash
62
- npx -y jevway doctor # Node, key and route, browser, ffmpeg; exit 0 when ready; never prints the key
63
- npx -y jevway doctor --plan # the steps still missing, numbered
64
- npx -y jevway doctor --live # plus one real Jev request (a fraction of a cent)
65
- ```
68
+ ```text
69
+ /plugin marketplace add AbdallahAHO/jevway
70
+ /plugin install jevway@jevway
71
+ ```
66
72
 
67
- Installing through an agent? Point it at [INSTALL-FOR-AGENTS.md](INSTALL-FOR-AGENTS.md): rules (a diff before any
68
- config edit, no key in the chat, no relaunching a browser it didn't start), steps per client, and checks.
73
+ 3. **Allow the browser.** By default jevway uses your running Chrome: open `chrome://inspect/#remote-debugging` and
74
+ allow it. To use a throwaway browser instead, set `JEVWAY_BROWSER=launch`.
69
75
 
70
- If a client times out on `npx`'s first download, run `npm install -g jevway` once and use `jevway-mcp` as the command.
76
+ 4. **Ask your agent** for something in the browser: "Find a 2-night stay in Lisbon for 2 guests under €150 and open the
77
+ best-rated one."
71
78
 
72
- `JEVWAY_BROWSER` (the server's `env`, or the env file) selects the browser:
79
+ `npx -y jevway doctor` checks the setup at any time (`--json` for agents) and names the fix for anything missing.
73
80
 
74
- - `auto` (default): your running Chrome, after you allow it at `chrome://inspect/#remote-debugging`. This is the same Chrome `chrome-devtools-mcp --auto-connect` uses, so Jev's tab is visible to chrome-devtools.
75
- - `launch` or `launch-headless`: a throwaway profile.
76
- - `http://127.0.0.1:9222`: an explicit debugging port.
81
+ ## Install
77
82
 
78
- Outside the Claude Code plugin, copy `skill/jevway/` (in the repository, or `node_modules/jevway/skill/jevway/`) into
79
- your agent's skills, so it knows when to use Jev and when to use chrome-devtools.
83
+ **Claude Code:** the plugin above, or the server alone:
80
84
 
81
- From a checkout: `pnpm install && pnpm build`, then `node /absolute/path/to/jevway/dist/mcp.js` as the command.
85
+ ```bash
86
+ claude mcp add jevway -s user -- npx -y -p jevway jevway-mcp
87
+ ```
82
88
 
83
- For agents without MCP, use the CLI:
89
+ **Codex:**
84
90
 
85
91
  ```bash
86
- npx -y jevway --browser launch-headless \
87
- --url https://en.wikipedia.org/wiki/Main_Page \
88
- --goal "Open the Wikipedia article about the Rosetta Stone" \
89
- --values '{"search_query":"Rosetta Stone"}' \
90
- --done-when "The Rosetta Stone article is open"
91
- # prints the JSON summary; exit 0 only when status is "done"
92
+ codex mcp add jevway -- npx -y -p jevway jevway-mcp
92
93
  ```
93
94
 
94
- ## Models
95
+ **Cursor** (`~/.cursor/mcp.json`, under `mcpServers`) **or any stdio MCP client:**
95
96
 
96
- `JEVWAY_MODEL` (or `--model`) picks the decision model. All of them speak the same System One API through the same SDK. With `TYPESAFE_API_KEY` set, Jev runs on the TypeSafe API as `jev-1.14.0`: screenshots go in as `assets` and Box questions are available. Everything else, and Jev without that key, goes through OpenRouter with `OPENROUTER_API_KEY`. OpenRouter drops TypeSafe's `assets` map, so Box only runs on the direct route.
97
-
98
- | `JEVWAY_MODEL` | Model | Screenshots | Fixture + live web (`pnpm verify`) | Journeys |
99
- | --- | --- | --- | --- | --- |
100
- | `jev` (default) | `jev-1.14.0` (TypeSafe API: `assets` + Box) / `typesafe/jev-1.14` (OpenRouter) | `fallback` | direct: 7/7 `fallback` and `always`, 6/6 off, ~240 ms/request | 8/8 off and `always` |
101
- | `pplx-decider` | `perplexity/pplx-decider-v1.1-27b` | `always` | 6/6 off and `always`, 0.4–1.3 s/request | 8/8 |
102
- | `clef-omni` | `cloudflare/clef-omni` | `always` | 4/6 (3/6 off) | 7/8 (8/8 off) |
103
- | `clef` | `cloudflare/clef` | none (images cost 16k tokens) | 6/6, 0.7–1.2 s/request | — |
104
- | `clef-flash` | `cloudflare/clef-flash` | `always` | 1/6 | — |
105
- | `gpt-6-luna` | `openai/gpt-6-luna-decisions` | `always` | 0–1/6: OpenAI refuses target questions (502) | — |
97
+ ```json
98
+ "jevway": { "command": "npx", "args": ["-y", "-p", "jevway", "jevway-mcp"] }
99
+ ```
106
100
 
107
- **Jev 1.14 is the fastest model that passes everything, and it reads screenshots. Perplexity's decider also passes everything with screenshots, but slower. Clef passes text-only, and is slower still.**
101
+ **OpenCode** (`opencode.json`, under `mcp`):
108
102
 
109
- `JEVWAY_SCREENSHOTS` (or `--screenshots`) controls screenshots for the vision models:
103
+ ```json
104
+ "jevway": { "type": "local", "command": ["npx", "-y", "-p", "jevway", "jevway-mcp"], "enabled": true }
105
+ ```
110
106
 
111
- - `always`: on every step.
112
- - `fallback`: only when a text-only decision would stop as uncertain or blocked, has its DONE rejected, or follows an action that changed nothing.
113
- - `off`: never.
107
+ Outside the Claude Code plugin, copy `skill/jevway/` into your agent's skills folder, so it knows when to use jevway and
108
+ when to use chrome-devtools. If a client times out on `npx`'s first download, run `npm install -g jevway` once and use
109
+ `jevway-mcp` as the command.
114
110
 
115
- Journeys send one with each screen's judgments whenever the mode isn't `off`. Each observed element index is drawn on the image as a numbered box, taken from the snapshot's own rects. Covered elements get no box, and the overlay is removed before any input. The model still only selects observed ids.
111
+ Having an agent install it? `npx -y jevway setup --client <claude-code|codex|cursor|opencode|other> --print` prints the
112
+ exact command or config for that client and changes nothing. [INSTALL-FOR-AGENTS.md](INSTALL-FOR-AGENTS.md) has the
113
+ steps per client, the rules (a diff before any config edit, no key in the chat) and the checks.
116
114
 
117
- ### Box (Jev 1.14 on the TypeSafe API)
115
+ **Choosing a browser** (`JEVWAY_BROWSER` in the server's env or the env file):
118
116
 
119
- On every request that carries a screenshot, Box questions run alongside the decision. Box answers always land back on an **observed element** (`src/jev/box.ts`), with one opt-in exception:
117
+ | Value | Uses |
118
+ | --- | --- |
119
+ | `auto` (default) | your running Chrome, after you allow it at `chrome://inspect/#remote-debugging` |
120
+ | `launch` / `launch-headless` | a throwaway profile jevway starts and closes |
121
+ | `http://127.0.0.1:9222` | a Chrome you started with that debugging port and its own profile |
120
122
 
121
- - **Cross-check:** Box asks where the next click belongs. If it confidently points at a different element than Jev chose, and the box doesn't also cover Jev's choice, the step hands back as `uncertain`.
122
- - **Covered check:** for a chosen click, a follow-up asks whether the target is visibly unobstructed and boxes it. A clearly covered target is fed back as history ("covered; clear it first") up to twice, then hands back.
123
- - **Rescue:** when Jev is BLOCKED, unsure, or claims DONE that `done_when` rejects, and the box confidently covers an observed element, jevway clicks that element. It is still gated by the box's own commit-risk score.
124
- - **Coordinate clicks (opt-in):** `allow_coordinate_clicks` / `--allow-coordinate-clicks`. When Jev is stuck and the box matches nothing the DOM reader reported (canvas, or a frame that was skipped), jevway clicks the box centre after a hit-test. Irreversible-looking clicks still need `confirm_irreversible`.
125
- - **Journeys:** each screen's primary action is boxed (a dashed outline in the viewer, `primary_action` in reports). A tappable control with no DOM element becomes a `control_not_in_dom` QA finding.
123
+ **Without MCP**, use the CLI. It's built for agents: every command prints exactly one JSON document on stdout (the
124
+ result, or `{ "ok": false, "error": { "code", "message", "hint" } }`), progress goes to stderr as one JSON event per
125
+ line, and the exit code is 0 for success, 1 for a hand-back or a failure, 2 for bad usage. `--help` starts with
126
+ examples, and `jevway skill` prints the skill that matches the installed version.
126
127
 
127
- Every model judges commit risk **per click candidate by name**, in one follow-up request per step. Jev also keeps its page-level guess, and a click is held if either says so. Per-model thresholds, rules and results are in [docs/verification.md](docs/verification.md#decision-models-on-openrouter-2026-10-11).
128
+ ```bash
129
+ # One-shot: runs, closes its tab, prints the result
130
+ npx -y jevway run --browser launch-headless --url https://en.wikipedia.org \
131
+ --goal "Open the Wikipedia article about the Rosetta Stone" --done-when "The Rosetta Stone article is open"
132
+
133
+ # A session: a background daemon keeps the tab between commands, so a hand-back can be answered
134
+ jevway run --session new --url https://example-stays.com --goal "Book 2 nights in Lisbon" --values '{"city":"Lisbon"}'
135
+ jevway run --session <id> --goal "Book 2 nights in Lisbon" --values '{"email":"ada@example.com"}'
136
+ jevway look --session <id> # what the page shows now, no model request
137
+ jevway sessions # open sessions; jevway end <id> closes one
138
+ ```
128
139
 
129
- ## Tools
140
+ ## Use
130
141
 
131
- **`jevway_task`** takes `goal`, `url` | `tab`, `values`, `secret_keys`, `files`, `done_when`, `checks`, `session`, `confirm_irreversible`, `confirm_target`, `max_steps`, `max_seconds`, `allowed_hosts`, `blocked_hosts`, and, for new sessions only, `allow_coordinate_clicks` and `record_dir`. It returns:
142
+ The agent calls `jevway_task` with the outcome it wants, the values it may type, and the evidence of success:
132
143
 
133
144
  ```json
134
- { "session": "be63210d", "status": "needs_input", "reason": "Blocked: no supplied value for \"Email address\"",
135
- "url": "…", "title": "…", "page_text": "…", "elapsed_ms": 899,
136
- "steps": [{ "operation": "TYPE_TEXT", "element": "Full name", "value_key": "full_name", "page_changed": true, "confidence": 0.9 }],
137
- "jev": { "requests": 3, "input_tokens": 7534, "median_latency_ms": 284, "screenshots": 0, "model": "jev-1.14.0", "est_cost_usd": 0.000316 },
138
- "stuck": { "operation": "BLOCKED", "candidates": [] },
139
- "missing": [{ "label": "Email address", "role": "textbox", "path": "/stays" }] }
145
+ {
146
+ "url": "https://example-stays.com",
147
+ "goal": "Search stays in Lisbon for 2 guests, filter to free cancellation, and open the first result",
148
+ "values": { "destination": "Lisbon", "guests": "2" },
149
+ "done_when": "A stay's detail page is open",
150
+ "allowed_hosts": ["example-stays.com"]
151
+ }
140
152
  ```
141
153
 
142
- `jev.model` is the model id the requests went to. `jev.est_cost_usd` estimates the call's cost from its input tokens (every request, Box and the done's evidence request included) at the model's published rate, rounded to 6 decimals: Jev on the TypeSafe API is priced at $0.042 per 1M input tokens (output is free); Jev through OpenRouter gets the same rate (OpenRouter's margin, if any, unchecked); a model with no rate written down gets `null`. It is an estimate, not billing.
154
+ It gets back one concise result, `status` and `reason` first:
155
+
156
+ ```json
157
+ { "status": "done", "reason": "Completion condition verified: a stay's detail page is open.",
158
+ "story": ["Typed the \"destination\" value into \"Destination\".", "Clicked \"Search\".", "Clicked \"Casa Flora\"."],
159
+ "next": "Read the result in `page_text` (and `evidence`); end the session when you no longer need its tab.",
160
+ "session": "a63a13f0-…", "url": "…", "title": "Casa Flora · Staylist",
161
+ "files": { "run_dir": "…/.jevway/runs/2026-11-02_1028_search-stays-in-lisbon_a63a", "screenshot": "…/final.jpg" },
162
+ "page_text": "…" }
163
+ ```
143
164
 
144
- `secret_keys` names the keys of `values` that are secrets (an API token, a one-time code). Jev sees `[secret:<key>]` in place of such a value and never reads it; it is typed as given, and wherever the page echoes it, `page_text`, `steps`, reasons, traces and `record_dir`'s `summary.json` and `index.html` show the marker instead. Recorded screenshots and video are pixels of the page and are not masked. Mark only real secrets: every occurrence of the value is replaced, even inside other words.
165
+ `story` says what happened in plain sentences, `next` says what to do now, and `files` points at the run folder and a
166
+ screenshot of the end page. On a hand-back the screenshot also comes inline, so the agent sees the page it has to decide
167
+ about. `detail: "full"` adds every step's confidence, the request stats and the candidates Jev weighed. When the status
168
+ hands control back, the agent continues with the same `session`:
145
169
 
146
- | Status | Meaning |
170
+ | Status | What the agent does |
147
171
  | --- | --- |
148
- | `done` | Jev chose DONE **and** a separate `done_when` check passed, or that check alone scored ≥ 0.9 once or ≥ 0.75 on 3 decisions in a row. A rejected DONE waits and re-decides, up to 3 times. |
149
- | `needs_input` | A field needs a value that isn't in `values`. Nothing is invented. `missing` lists each field (label, role, page path). |
150
- | `needs_confirmation` | The next click looks irreversible (pay, send, delete, submit). `confirm_irreversible: true` allows exactly one such click, and an agent should set it only for the action and target the user's own request names (a category such as "anything unused" names no target: it lists the candidates and asks the user); otherwise it asks the user. It allows only the control that needs_confirmation named (same label, same page path); a different irreversible control is handed back as a new needs_confirmation. It counts only on the call that answers a needs_confirmation (or a new session's first call, which has no flagged control and allows any one); anywhere else it is ignored. Pass `confirm_target` with the user's words that name the target; jevway checks the page shows them (the control's own words, like "delete", do not count) and refuses otherwise. |
151
- | `uncertain` | Operation or target confidence was below the model threshold (Jev 0.3; per model in `src/jev/model.ts`), 6 actions in a row ran under 0.5 confidence (exploring, not progressing), or DONE could not be verified. |
152
- | `blocked` | No supported operation can progress, 3 actions in a row changed nothing, the same target was covered 3 times, one action only toggled the page back and forth 4 times, or the same element was used 6 times in 7 actions; the page shows an error after this call acted (the reason quotes it); or a sign-in screen blocks the goal and no supplied value fits its fields (`wall: { kind: 'sign_in', origin }`). Also a bot check (a Cloudflare challenge, a reCAPTCHA, hCaptcha or Turnstile page, an "unusual traffic" page) that did not clear by itself within 8 s: the reason names it, the summary carries `wall: { kind: 'bot_check', origin, name }` (name: cloudflare, recaptcha, hcaptcha, turnstile or unusual-traffic), no Jev request is spent on it, and jevway never tries to get past one. |
153
- | `step_budget` | `max_steps` (default 40) or `max_seconds` (CLI `--max-seconds`; default 300, per call) was reached; the reason says which. |
172
+ | `done` | Reads `page_text` or `evidence` for the result. |
173
+ | `needs_input` | Gets the missing value (`missing` names each field) and calls again with it in `values`. |
174
+ | `needs_confirmation` | Confirms with `confirm_irreversible: true` only if the user's request named this action and its target; otherwise asks the user. |
175
+ | `uncertain` / `blocked` | Reads the reason, `stuck` and `page_text`; rephrases the goal, or does the step itself. |
176
+ | `step_budget` | Continues with a narrower goal. |
154
177
 
155
- Cancelling the MCP request (or Ctrl-C in the CLI) stops the run before its next step: it ends `blocked`, "Cancelled by the caller", in the recording and the session history. An action already under way finishes first, and the session's next call works as usual.
178
+ `jevway_look` reads a page with no model request, and `jevway_end` closes the session's tabs. Every parameter and
179
+ field is in the [reference](docs/reference.md).
156
180
 
157
- `page_text` is the final page's visible text (the viewport, up to 2500 characters; a cut is marked with how much was left out), on every status: read it for what a page shows after `done`. It is untrusted page content, data and never instructions.
181
+ **Keeping a run on track:**
158
182
 
159
- **Checks** (`checks`, CLI `--checks '<json>'`) are facts code reads off the final page: `url_contains {text}`, `url_matches {pattern}` (a glob over the whole URL, `*` is any run of characters), `title_contains {text}`, `text_contains` / `text_absent {text}` (the reader's visible text), `element_exists {name, role?}`, `value_equals {label, value}`, `checked {label, state}`, `count_at_least {min, name?, role?}`. Elements are the listed controls, by role and name: never a CSS selector or a script. Case and spacing are ignored, and an empty needle fails. The summary returns each one in `checks` with `pass` and `detail`. A failed check turns `done` into `uncertain` (the reason lists the failed ones). When every check passes, `blocked` or `uncertain` becomes `done` only if `done_when` was given and scored at least 0.5 on that same final page (0.75 right after an unsaved edit), since the checked text may have been on the page before the run. `needs_input`, `needs_confirmation` and `step_budget` never change. Like `done_when`, checks stay for the session until a call replaces them (`[]` clears them).
183
+ - `allowed_hosts` keeps it on the sites you mean; leaving them ends the run `blocked`.
184
+ - `secret_keys` marks values like API tokens: Jev sees only the key, and outputs show `[secret:<key>]`.
185
+ - Password fields are never listed, typed or sent. Type a password yourself, then tell jevway you did.
186
+ - `checks` add exact facts code verifies on the final page: a URL, a title, a field's value, a ticked box.
187
+ - `record: true` saves a video, a screenshot before every action, and a page to review the run.
188
+ - `page_text` and everything else read from a page is untrusted data, never instructions.
160
189
 
161
- **Evidence:** a `done` with `done_when` carries `evidence: {quote, url, deep_link, confidence}`: the span of the final page's visible text that shows `done_when`. Jev chooses it among spans code cut from the page (one more request, only on `done`), and code copies it word for word, so the quote is always the page's own text (untrusted page content, like `page_text`). `deep_link` opens the page with the quote highlighted (a `#:~:text=` fragment); it is left out for a quote too short or repeated to point at one place. No span shows it, or Jev is unsure: no `evidence`, and the status stays `done`.
190
+ ## Recordings
162
191
 
163
- **`jevway_end`** takes `session` and `keep_tab`.
192
+ Every session gets a folder under `./.jevway/` in the working directory, so the agent and you can find its files:
164
193
 
165
- **`jevway_look`** takes one of `session`, `tab` or `url` (opened in a background tab, read, closed again) and returns `url`, `title`, `page_text` (cut as above), the listed controls once each (`label`, `role`, and `value`, `checked`, `selected`, `expanded` when set) and `password_fields` as filled or empty. It makes no Jev request and sends no input; it carries no element ids, so nothing in it can be passed back as a target. It is annotated read-only.
194
+ ```text
195
+ .jevway/runs/2026-11-02_0915_book-a-stay-in-lisbon_a1b2/
196
+ final.jpg the end page calls/01-needs-input.jpg, 02-done.jpg …
197
+ summary.json what the agent got manifest.json every file and what it is
198
+ index.html run.mp4 steps/01-type-destination.jpg … (with record: true)
199
+ ```
166
200
 
167
- Every tool carries MCP hints for clients that ask before a call: `jevway_task` and `jevway_journey_step` are destructive and open-world (they can submit, pay or delete on any site), `jevway_journey_start` and `jevway_journey_autopilot` are open-world but not destructive, `jevway_look` is read-only, and `jevway_end`, `jevway_journey_annotate` and `jevway_journey_finish` only touch jevway's own state.
201
+ Pass `record: true` (CLI `--record`) and jevway also keeps a record for people, with no extra setup:
168
202
 
169
- **Recording:** `record_dir` (CLI `--record <dir>`) saves a screencast video (`run.mp4`, needs `ffmpeg` on the PATH, otherwise skipped), a 2× JPEG before every action with its target outlined, the final page, `summary.json`, and an `index.html` page that opens on the video with its steps on a timeline and a strip kept in sync, then the run's calls, reason, address and cost. These shots are for people; the model still gets its own 1024 px screenshots.
203
+ - `index.html`: opens with the goal and how the run ended, then the video, with every step told as a sentence on a
204
+ timeline and a strip of screenshots kept in sync; click a step to play from there. It works offline, in light and
205
+ dark, and embeds the summary as JSON for agents.
206
+ - `run.mp4`: a screencast of the whole run (needs `ffmpeg`).
207
+ - A sharp screenshot before every action, named for what it did, with its target outlined and labelled.
170
208
 
171
- ## Safety
209
+ ![A recorded run: the video, its 14 steps on a timeline, and the screenshot strip](docs/images/recording.jpg)
172
210
 
173
- - Model output only ever selects an observed element index. It never becomes a selector, script, or text, and it becomes a coordinate only through the opt-in coordinate click (stuck, a confident box matching no observed element, a hit-test, and the commit-risk gate).
174
- - Typing uses real key events per character, so autocompletes register it. Enter is a separate fixed key, offered only right after typing into a search-like field, and pressed only while that field still has focus. Escape is offered only while a menu, popover or suggestion list is expanded, and pressed once automatically when a popup covers the chosen target (then a blur and a synthetic outside press, for lists that ignore Escape).
175
- - Every input re-checks the page for freshness and for covered targets first (from jev-ultrafast).
176
- - Page text is treated as untrusted data in every instruction.
177
- - **Hosts:** `allowed_hosts` / `blocked_hosts` (CLI `--allowed-hosts`, `--blocked-hosts`; server-wide `JEVWAY_ALLOWED_HOSTS`, `JEVWAY_BLOCKED_HOSTS`) take exact hosts or `*.example.com` (subdomains only, not `example.com`). A call can only narrow the server's lists, and a later call only the session's. A start URL outside is never opened; the page URL is checked before every decision and after every step, and leaving ends the run `blocked` naming the host. The run never navigates back. `about:blank` and Chrome error pages always pass; `data:`, `file:` and other host-less pages are outside an allowed list. Only the top page's URL is checked: iframes, popup tabs and the page's own requests are not.
211
+ Values marked secret are masked in the text files; screenshots and video show the page as it was. The model never sees
212
+ these images. Journey maps get their own viewer, described next.
178
213
 
179
- ## Verify
214
+ ## Journey maps
180
215
 
181
- ```bash
182
- pnpm test # offline: action space, request shape, answer validation, task-loop rules
183
- pnpm verify # live: headless Chrome + real Jev, checked against independent ground truth
184
- pnpm verify:real # live public sites, a fresh browser per run, 5 runs each (JEVWAY_REAL_RUNS, JEVWAY_REAL_ONLY)
185
- pnpm verify:langfuse # 10 jobs in Langfuse's demo project, signed-in Chrome on :9333, recorded into report.html
186
- pnpm report:render verify/out/<folder> # rebuild a suite's report.html (or a run folder's index.html), offline
187
- pnpm verify:replay --record verify/out/replay/<name> # live: record every Jev answer of the fixture suites
188
- pnpm verify:replay verify/out/replay/<name> # offline: replay them, no key, no credits
189
- ```
216
+ The `jevway_journey_*` tools map how a person moves through a site on a phone: every screen with its type and UI
217
+ patterns, the taps between them, and QA findings like tap targets under 44 px, console errors and dead ends. You pick
218
+ the taps, or let Jev explore. The result is a shareable viewer. See [docs/journeys.md](docs/journeys.md).
219
+
220
+ ## How it works
190
221
 
191
- `pnpm verify` runs:
222
+ Each step, jevway reads the page through the Chrome DevTools Protocol into a numbered list of controls and the visible
223
+ text, and sends one request to Jev with the goal, that list and the names of the supplied values. Jev answers with an
224
+ operation, a listed element, which value to type, its confidence and a commit-risk score. Code checks the answer
225
+ against the list, re-checks the page is fresh and the target isn't covered, and acts with real input events. Guards
226
+ stop loops, and a separate check decides `done`.
192
227
 
193
- - **Local booking fixture:** async autocomplete, native select, checkbox, async results, then open a result. Next, `needs_input` for a missing email, then `needs_confirmation` on "Confirm booking and pay", then the confirmed booking. All four scenarios share one session.
194
- - **Real Wikipedia search.**
195
- - **Canvas-only control** (Box profiles with screenshots): no click without the opt-in, and the game starts with it.
196
- - **Wide table rows** (like Langfuse's traces table): rows wider than their scroll box are listed and opened, not their checkboxes. The run is recorded.
197
- - **MCP stdio round trip.**
228
+ The full technical specification is in [docs/how-it-works.md](docs/how-it-works.md).
198
229
 
199
- `pnpm verify:real` runs Wikipedia, MDN, GitHub, Hacker News, npm, Booking.com and Google Flights. Every task stops before login or payment, passes are checked against the final URL, and bot walls (Cloudflare, captchas) are reported apart from failures, judged by the same detector the run uses (`src/browser/bot-wall.ts`).
230
+ ## Models
231
+
232
+ Jev 1.14 is the default, on the TypeSafe API with `TYPESAFE_API_KEY` (with screenshots and Box, which locates
233
+ controls on the screenshot to cross-check and rescue clicks) or through OpenRouter with `OPENROUTER_API_KEY`. Perplexity's and Cloudflare's decision models run through
234
+ OpenRouter too. Jev is priced at $0.042 per 1M input tokens, so a typical 10-step task costs well under a cent. See
235
+ [Models](docs/reference.md#models) for results per model.
236
+
237
+ ## Documentation
238
+
239
+ | Doc | For |
240
+ | --- | --- |
241
+ | [docs/reference.md](docs/reference.md) | every tool, parameter, status, CLI flag, environment variable and model |
242
+ | [docs/how-it-works.md](docs/how-it-works.md) | the technical specification: one step end to end, guards, safety |
243
+ | [docs/journeys.md](docs/journeys.md) | the journey-map tools |
244
+ | [INSTALL-FOR-AGENTS.md](INSTALL-FOR-AGENTS.md) | agents installing jevway for a person |
245
+ | [skill/jevway/SKILL.md](skill/jevway/SKILL.md) | the skill agents load: when and how to call jevway |
246
+ | [docs/verification.md](docs/verification.md) | the test suites and their results |
247
+ | [docs/prior-art.md](docs/prior-art.md) | other open-source Jev browser projects, and what was taken from them |
248
+ | [CONTRIBUTING.md](CONTRIBUTING.md) | building, testing and releasing |
200
249
 
201
- **Replay.** `pnpm verify:replay` re-runs the fixture suites (`verify`, `verify:gotchas`, `verify:redteam`, `verify:capabilities`) against recorded Jev answers, so a loop or guard change can be regression-tested offline and for free. `JEVWAY_REPLAY=record` saves each answer under the sha256 of its request (keys sorted, screenshots left out, local fixture ports normalised) in `JEVWAY_REPLAY_DIR`; `JEVWAY_REPLAY=replay` answers only from there, with no key and no network. A request never recorded is a miss that names the goal and step and shows what changed against the nearest recording, and any miss fails the suite. A change to what Jev is asked is meant to miss: record again after it. Gotchas replay their `normal` profile (the `churn` and `slow` pages change with timing), and the live Wikipedia scenario always misses.
250
+ ## Contributing
202
251
 
203
- Traces go to `verify/out/`. A jobs suite's `report.html` opens on an overview of its jobs, each with its featured run's step shots; a job's view starts with the run's video, its steps on a timeline and a strip that follow the video, every run one click away, then what Jev was given and how the run was judged. A step shot on the overview opens its run at that step (`#<job>-r<run>-s<step>`), and a phone recording sits in a frame of its own aspect. `verify:agent`'s page has the same layout: each chore's runs, with the agent's tool calls, their arguments and statuses, its final answer and the judgement. Each run's cost row shows its estimated cost (the sum of its calls' `est_cost_usd`), and the overview the median per run and the suite's total; a run saved before summaries carried a cost, or on a model without a price, shows "unknown". Results are in [docs/verification.md](docs/verification.md).
252
+ Bug reports, site failures and pull requests are welcome. [CONTRIBUTING.md](CONTRIBUTING.md) covers the setup, the
253
+ offline test suite (no API key needed) and the conventions. Report security issues privately, as described in
254
+ [SECURITY.md](SECURITY.md).
204
255
 
205
- How jevway compares with other open-source Jev browser projects, and which of their ideas are planned or rejected: [docs/prior-art.md](docs/prior-art.md).
256
+ ## License
206
257
 
207
- ## Limits
258
+ MIT. The task loop, DOM snapshot and guards build on [browser-use/jev-ultrafast](https://github.com/browser-use/jev-ultrafast)
259
+ (MIT; see [LICENSE.jev-ultrafast](LICENSE.jev-ultrafast)).
208
260
 
209
- - An element whose centre is off-screen (a row wider than its scroll box) is clicked at the centre of its visible part. A target clipped by a scrolling sidebar or table is scrolled into view just before the click.
210
- - Also read: sortable column headers (including ones scrolled sideways), plain pointer-cursor items inside open popups (date-range menus), and contenteditable search bars. Rows are labelled by their visible text.
211
- - The DOM reader covers common HTML/ARIA controls, including inside open shadow roots and iframes. Every visible frame, same-origin or cross-origin (out-of-process frames too), is read with the same reader: its controls continue the page's numbering, its text follows the page's under `[Frame: …]` within the same 6000 characters, and clicks, typing and every guard run inside the frame. A frame is skipped when it shows less than 16×16 px, sits outside an open modal, is still blank, takes over 1.5 s to read, or is past the caps (10 frames, 3 levels deep); controls a banner or sheet covers in the page around the frame aren't listed. CSS-transformed (scaled, rotated) iframes are placed as if untransformed.
212
- - Closed shadow roots and canvas aren't read, and file inputs inside iframes aren't supported: they are not listed, and an upload is never set inside a frame. Box can see canvas controls (and frames skipped above), and click them with the opt-in; otherwise use chrome-devtools.
213
- - Uploads take only files the caller declares in `files` (key → absolute path, checked before the run), never a path from `values`. Only then are file inputs listed (hidden ones through their label or styled button) and UPLOAD offered; Jev sees each key and file name, never the path, and a native file picker is intercepted. No declared file fitting a field is `needs_input`.
214
- - A tab the session's own click opens (`target=_blank`, `window.open`) is followed, and a closed one falls back to the tab that opened it; tabs anyone else opens are never touched. `jevway_end` closes the tabs the session's clicks opened (and its first tab when it opened it) unless `keep_tab`. The new tab is set up when it appears, so on a page that loads within ~20 ms its first document's own scripts run before the click counter; that can only end a run `blocked`, never repeat a click. A dialog the new tab raises before it is set up freezes it; following then fails after 5 s and the run ends `blocked`. In `auto` mode Chrome shows the new tab in front. A `record_dir` video stays on the first tab.
215
- - Only on-screen elements are listed; Jev scrolls to reveal more. On a page whose document doesn't scroll (an app shell), the scroll goes to the largest visible pane that does; a scroll that moved nothing is reported to Jev as such. Exception: an off-screen link, button or row containing one of the supplied `values` is listed too, and scrolled into view when clicked.
216
- - The scroll aimed at an inner pane works on the main page only; inside an iframe, scrolling stays page-level.
217
- - Confidence thresholds (per model, in `src/jev/model.ts`) and the 0.5 risk threshold are starting points, tuned on a small set of runs.
218
- - Jev's commit-risk check is a speculative page-level judgment ("the click that best advances the goal") plus a per-element score for the chosen click; the higher one counts. Other models score each candidate only, and Box rescues use the box's own score.
219
- - A click scoring 0.25–0.5 on risk gets a screenshot look before it runs, but only with screenshots on (`fallback` or `always`). With screenshots `off`, text alone under-reads some checkout pages (a bare "Continue" can score under 0.5), so keep screenshots on when a task can reach a payment step.
261
+ jevway was called jev-browse until 0.1.0. Its old tool names (`browser_task`, `browser_look`, `browser_task_end`,
262
+ `journey_*`) and `JEV_*` environment variables no longer work.