Public
Remixed
Describe a task; Jev decides each step in a KERNEL cloud browser
ai-agentdemokerneltypesafe
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.

Jev Browser Use

Jev Browser Use finding Airtable API examples, with the live browser, scores, and action history visible.

Describe a task. Jev picks every next move in a live cloud browser you watch in an iframe.

Setup

Two keys, two owners.

TYPESAFE_API_KEY — from console.typesafe.ai — belongs to this val, because the val pays for the model calls. If it's missing the page says so up front and /api/config reports the gap.

Your Kernel API key — from dashboard.onkernel.com — is not stored on this val at all. The browser is a real cloud Chromium on your own Kernel account, so the page asks you to paste a key before Run. It lives in your browser's localStorage, and on the server only for as long as your run is live: it travels with POST /api/session, the session row carries it while the browser exists, and closing the run erases it. Nothing here can substitute for it, and there is no shared browser credential to leak or spend.

Without both, Run stays disabled and the page says which one is missing.

The live browser tests in tests/ are the exception: they run on the owner's own key, read from KERNEL_API_KEY in the environment. No request path reads that variable.

How the demo works

Val Town runs the app, Kernel supplies the remote browser, and Jev chooses the next action from extracted page text and controls. The live view is read-only: you can watch the browser and action log while the agent works. Action history scrolls within a bounded panel, following new steps unless you scroll up to inspect earlier ones.

Before Run, use Include site navigation (on by default) to choose whether surrounding page controls are eligible. Turn it off to focus on content; Back remains available. The setting is fixed for that run.

Each session saves actions and observed outcomes in SQLite. Returning to the same page state excludes previously attempted controls; repeating the same typed action is also blocked.

When a route stops being useful, Jev can choose Back if the previous browser entry has untried controls. Returning keeps explored routes excluded, so it can try an alternative. Back consumes a normal step and verifies the history entry and URL without waiting for DOMContentLoaded.

A run ends on completion, stuck, Stop, or a step/time limit. The app then closes the remote browser. See Scoring for how Jev evaluates controls and decides whether the goal is reached.

The loop

  1. Read: KERNEL returns the page text and controls.
  2. Choose: The app filters controls using the navigation setting and history, then Jev scores and picks the next action.
  3. Act: The app saves the attempt, and KERNEL clicks or goes Back.
  4. Repeat: Show the result and read the page again, or close the browser when the run ends.

A run ends when the goal is reached, no useful action remains, you press Stop, or a step/time limit is reached.

Scoring

Jev scores up to 60 controls, then compares up to 24 finalists. The same rules apply to every task; page regions provide context rather than a relevance bonus.

StepHow it works
Build the poolFilter attempted actions, collapse links to the same destination, and exclude text fields without usable quoted text.
Pick 60 controlsReserve 45 content / 15 surrounding controls, borrowing unused slots. Within each group, alternate two picks ordered by task-word overlap with one in page order so indirect routes still get a chance. With navigation off, content can use all 60.
ScoreJev estimates direct relevance (goal) and useful progress (closer). Rank with max(goal, 0.85 × closer), so two middling estimates do not inflate each other.
Use historyMultiply the rank by 0.5 for a past failure, otherwise 0.65 for no change, otherwise 0.8 for a visited destination. These are soft penalties; changed page states can still justify revisits.
ChooseJev compares finalists with their observed outcomes and eligible Back. Stage-1 numbers are omitted from this comparison to reduce anchoring.
FinishSuccess requires an explicit completion choice and goal_reached ≥ 0.8. Instructions require evidence on the current page; a link to the destination is not completion.

Both model stages receive the task, current page text, recent outcomes, and execution limits. Merely related topics are weak evidence of progress. Back can be preferable to a weak forward choice. Text actions must use an editable field and an exact, untried quoted string from the task.

The score-strip values are heuristic ranks. Action-history percentages and confidence are model estimates, not measured success rates. The weights and completion threshold are starting heuristics, not calibrated probabilities.

Two stages keep the final comparison manageable without adding model calls: stage 1 builds a shortlist; stage 2 makes the decision. Deterministic checks in tests/scoring.ts cover the selection and execution rules. They do not establish improved live-model accuracy.

Background

Everything in this section is from Steve of Val Town, who got this up and running — thanks, Steve.

The thing I went looking for and didn't find

TypeSafe's docs have no computer-use guide. docs.typesafe.ai/demos.md lists exactly one demo (Smart Home Assistant). The wikiracing demo that looks like computer use only exists as a launch-blog demo — no docs page, no code, no walkthrough.

What the blog does say, in its "Nuance" sections, is the whole spec:

Jev supports a cardinality up to 255. For the higher cardinality choices, we do a 2 stage-system of scoring independently then making an explicit choice, hence the occasional slowdown.

The demo is on structured state as a data structure with text, not on images (yet…)

So Jev never sees the screen, and it never emits an action. It has no string output at all — only choice, score, noul. "Browser use with Jev" therefore can't be a model emitting tool calls. It's:

code builds a closed set out of the page → Jev ranks and chooses within it → code does the clicking.

That constraint is the design, not a workaround. Every action is a value Jev was handed, so it cannot invent a selector, hallucinate a URL, or make a type error.

Three bugs a real page found

The perceive() snippet was measured against a live Wikipedia article, not reasoned about — see @stevekrouse/browser-tester's tests/jev-perceive.ts, which runs the exact snippet. All three were real:

  1. Text came from document.body, which on Wikipedia is mostly furniture — "Jump to content Main menu Search Appearance Donate Create account". Jev was being asked to navigate a page whose text was the chrome. Now it reads the content root (article, #mw-content-text, …) and only falls back to the body.
  2. A candidate's centre could sit below the fold. The measured page had one at y=519 in a 513px viewport — an element whose top is on screen can have its centre off it, and the centre is what gets clicked. Centres are now required to be inside the viewport.
  3. Nothing below the fold was reachable at all. Every candidate is now tagged with data-jev in the live DOM, so act() can scroll it into view and re-read where it actually is before clicking. That took the candidate count on the same page from 10 to 195.

Limits, honestly

  • No extracted answer output. Page text informs navigation and completion decisions, but the demo does not return a written answer or structured data. Reaching a pricing page, for example, does not produce a price report.
  • Typing is the weak spot. Jev can't write text, so harvestTexts() pulls quoted phrases out of your task and Jev chooses which to type. A task with no quoted phrase can't type anything. This is the one place the no-strings constraint genuinely costs capability rather than buying safety.
  • Back is bounded recovery. Jev can choose Back when the actual previous browser history entry has saved, untried non-editable controls. It returns one entry, counts toward the step budget, and keeps explored routes excluded. Failed or repeated Back attempts from the same state are blocked. If the previous entry is unknown or exhausted, Back is not offered; the agent does not jump over it to older pages. Back restores navigation, not submitted forms or other side effects. If no useful action remains, the run reports "stuck" rather than claiming success.
  • History sees extracted state. It compares the URL, extracted text, and control identities, not screenshots. Dynamic content can make a revisited page look new; meaningful changes outside the extracted content can be missed.
  • Selection is still bounded. Region budgets keep both content and surrounding controls represented; they do not guarantee that every relevant control on a large page reaches scoring.
  • Run limits. Up to 12 steps or 180 seconds per run, with five concurrent demo sessions by default. Use Stop to release a session when finished testing. Wikiracing can need more steps than this budget allows.

Testing

Refresh the demo and start a fresh run to exercise the full history and Back behavior. Existing sessions may lack the navigation snapshots needed to offer Back. Watch for a different choice after revisiting a page, an explicit "go back to …" entry when recovery is selected, and "stuck" when no useful action remains. Back is conditional; it will not appear on every run.

Regression scripts can be run individually with Val Town's run-file tool:

  • tests/scoring.ts: 33 checks covering relevance before the cutoff, duplicate links, history discounts, completion, valid text actions, and independent final comparison. Uses model stubs and creates no browser.

  • tests/navigation-setting.ts: 13 checks for content-only filtering, full content quotas, Back eligibility, defaults, and per-session SQLite persistence. Creates no browser.

  • tests/candidates.ts: 15 checks for balanced quotas, borrowing unused slots, ordering, region context, and history filtering before allocation. Uses model stubs and creates no browser.

  • tests/regions-browser.ts: 13 checks with generic long-page fixtures and a live Val Town pricing navigation regression. Covers footer controls after 320 content links and content after 320 navigation links. Makes live Jev calls and deletes its fixture browser.

  • tests/history.ts: 38 checks covering repeat filtering, Back eligibility, invalid choices, and SQLite persistence. Uses model stubs and creates no browser.

  • tests/layout-browser.ts: 15 checks with mocked run APIs for layout, scrolling, auto-follow, and mobile overflow. Creates and deletes one fixture browser; makes no model calls.

  • tests/back-browser.ts: 14 checks in a real Kernel browser against fixture pages, including returning to an untried route, rejecting stale Back decisions, same-document navigation, and returning before DOMContentLoaded on a page with a blocked script. Makes no model calls and deletes its test browser in a finally block.

  • tests/decide.ts: the original decision fixture; makes live Jev calls without creating a browser.

After the personal-fork scoring changes, all 99 deterministic checks passed: 33 scoring, 38 history, 15 candidate, and 13 navigation-setting checks. Live-model and browser suites have not been rerun for these scoring changes; the fork still needs its own TypeSafe key. Earlier upstream runs passed the pricing-navigation, Back, and layout fixtures. Those results do not establish accuracy for the new scoring rules or success on every task.

Files

See AGENTS.md for the file map.