Skip to content

Add a Jev planner for ACT behind --jev - #21

Merged
brynary merged 3 commits into
act-planner-seamfrom
act-jev-planner
Oct 5, 2026
Merged

brynary merged 3 commits into
act-planner-seamfrom
act-jev-planner

Conversation

@brynary

@brynary brynary commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

Stacked on #20. Steps 2–3 of the ACT planner plan. Depends on lithoscomputer/twins#14: the twins pin points at that PR's commit until it merges.

What

--jev makes ACT ask TypeSafe's Jev first, with the model option as the fallback. It follows Stagehand's experimental Jev act path (browserbase/stagehand#2951–#2953, #2960).

  • Client (run/act/jev/client.rs): Jev through lithos-llm's evaluation API as typesafe/jev-latest: Client::evaluate_with_context with the ACT deadline, the shared retry middleware, verdict validation, and catalog pricing ($0.042 per million input tokens). WHIRL_JEV_ENDPOINT is a catalog overlay that points the typesafe provider at another server. A small breaker pauses Jev for 60 s after a rejected key, or for 30 s after three failures in a row. (The first version had its own reqwest client; commit e6a806e replaced it.)
  • Candidates (jev/outline.rs): the raw snapshot parsed as a tree. There are separate views for pointer actions, fills, native selects, and key presses. Each candidate is described by role, name, value, row or list-item text, named sections, nearest heading, and "n of m" among look-alikes. Views over 40 are cut to the 30 best BM25 matches.
  • Planner (jev.rs): one request for the intent, one for the pick (best without a "none" option, and strict with one). It accepts at ≥ 0.7 unless strict's "none" is > 0.9. Arguments come from the instruction (jev/args.rs). Every pick still goes through PageSnapshot::decide.
  • Fallback: when Jev is unsure (it still passes its likely matches), cannot supply an argument, is asked for step two, or fails, the LLM planner plans with Jev's likely matches in the prompt. JevPlanner holds its fallback as another Arc<dyn ActPlanner>.
  • Report: act.planner, actions[].plannedBy, and usage.jev (requests, tokens, and cost). A step's costUsdMicros is the model's cost plus Jev's. All three are additive, so the report stays version 2. The HTML report shows them too.
  • Docs: SPEC §7.4, §12, §13, §15; machine-output.md; report.schema.json; CHANGELOG.

Tests

Acceptance tests against the model twin's systemone route:

  • a confident Jev pick acts with no model call;
  • an unsure pick falls back, and the model sees Jev's likely matches;
  • a Jev 401 falls back;
  • a masked value fills the page but never reaches Jev (checked in the logged request bodies);
  • --jev without TYPESAFE_API_KEY exits 3.

Unit tests cover the outline, the views, descriptions, BM25, arguments, the breaker, and the wire shapes. mise run check passes.

A live smoke run against TypeSafe with the Flash-Lite fallback:

  • native-select, login-with-secret, and similar-buttons were planned by Jev alone in about 0.45–0.5 s, with no model call.
  • delete-invoice-row fell back: the intent was unsure for "delete invoice 1037".

The evals in the next PR measure all tasks.

🤖 Generated with Claude Code

brynary and others added 2 commits September 26, 2026 21:11
With --jev, TypeSafe's Jev plans ACT first, and the model option plans
when Jev is unsure. This follows Stagehand's experimental Jev act path
(browserbase/stagehand#2951-#2953, #2960).

Jev is a classifier: it answers closed questions quickly but cannot
write text. One request asks which kind of action the instruction wants.
A second picks the element from the snapshot elements that action can
target, each described by its row or list item text, named sections,
nearest heading, and place among look-alikes. It asks twice: "best"
must choose, and "strict" may say none fits. Lists over 40 are cut to
the 30 best BM25 word matches. Arguments come from the instruction: the
one quoted string or placeholder that is not the field's name, the
named key, or the one native select option the instruction names.

Jev's choice goes through PageSnapshot::decide like a model answer. When
Jev is not confident, has no argument, is asked for step two, or fails,
the LLM planner plans the step, with Jev's likely matches in its prompt.
A breaker pauses Jev after a rejected key or three failures in a row.

TYPESAFE_API_KEY holds the key; WHIRL_JEV_ENDPOINT points at another
server. Reports add the planner, each action's plannedBy, and Jev's
requests and tokens. Tests use twin-openai's systemone route, pinned to
the twins commit that logs request bodies (lithoscomputer/twins#14).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A step that Jev planned alone made no model call, so its usage had no
cost and the report left costUsdMicros out, as if a call were unpriced.
It now reports 0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Jev planner called TypeSafe's /v1/systemone with its own reqwest
client, but lithos-llm already serves Jev: the built-in catalog has
typesafe/jev-latest on the systemone codec, and Client::evaluate checks
each verdict, retries through the shared middleware, keeps the ACT
deadline, and prices the verdict from the catalog.

JevClient is now a thin wrapper over a lithos-llm client with the
built-in catalog. WHIRL_JEV_ENDPOINT is an overlay that points the
typesafe provider's base_url at another server. The breaker stays.

Jev is now priced ($0.042 per million input tokens). The report's jev
usage gains costUsdMicros, and a step's costUsdMicros is the model's
cost plus Jev's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@brynary
brynary added this pull request to stack #56 October 5, 2026 13:22
@brynary
brynary merged commit fc13a32 into main Oct 5, 2026
3 checks passed
@brynary
brynary deleted the act-jev-planner branch October 5, 2026 18:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant