ego-jev
September 18, 2026 · View on GitHub
A small, bounded browser loop that joins ego-lite browser control with TypeSafe Jev decisions.
ego-lite snapshot -> canonical state + candidate set -> Jev choice
-> optional text model -> ego-lite action -> new snapshot
It is deliberately not a general-purpose browser agent. Jev can choose only a supported operation and a target from the current ego snapshot. It never receives or emits JavaScript, CSS selectors, browser coordinates, or an action ref. The executor maps the selected opaque candidate back to a ref that was observed in that same snapshot, then discards it after the action.
Requirements
- Node
26.7.0for the checked development baseline (.nvmrc). The package accepts Node>=22.15.0 <27. - npm
11.19.0(packageManagerfield) and the committedpackage-lock.json. - An installed, connected ego-lite command:
ego-browser. This project invokes it as an external runtime; it does not bundle, fork, or import its closed browser runtime. TYPESAFE_API_KEYfor Jev/System One.TEXT_MODEL_API_KEY(orOPENAI_API_KEY) andTEXT_MODELonly when Jev selectsTYPE_TEXT. This MVP calls the OpenAI Responses API with structured output; it does not use the legacy Chat Completions API.
The implementation was checked against ego-lite runtime 0.5.0.28 and
ego-browser CLI output on 2026-09-18. See NOTICE for the pinned
upstream commits and licenses.
Version boundaries are committed: Node 26.7.0, npm 11.19.0, TypeScript
5.8.3, and dotenv 16.6.1 are exact checked baselines; the lockfile pins
their resolved dependency graph. Set TEXT_MODEL to the exact model identifier
your account permits (the checked local configuration uses gpt-5.6-luna).
Install
git clone https://github.com/phd-peter/ego-jev.git
cd ego-jev
npm ci
cp .env.example .env
Set the required values in .env; it is ignored by Git.
TYPESAFE_API_KEY=...
TYPESAFE_MODEL=jev-latest
TEXT_MODEL_API_KEY=...
TEXT_MODEL_BASE_URL=https://api.openai.com/v1
TEXT_MODEL=gpt-5.6-luna
Confirm the browser runtime before the first run:
ego-browser --version
npm run check
Run
After npm link, the package exposes the requested CLI form:
ego-jev --url https://en.wikipedia.org/wiki/Main_Page \
--goal "Find and open the Wikipedia article about Godel's incompleteness theorems."
Without a global link, use the identical interface through npm:
npm run start -- --url https://en.wikipedia.org/wiki/Main_Page \
--goal "Find and open the Wikipedia article about Godel's incompleteness theorems."
npm run smoke:wikipedia is the same live end-to-end command with a bounded
12-action budget. It requires both configured model credentials because the
example first types a search phrase. --keep-space leaves a successful
TaskSpace open; otherwise the agent calls finish({ keep: [] }) after DONE.
On BLOCKED, max steps, a decision error, or an execution error, the space is
left open for inspection rather than being replaced with a new one.
npm run smoke:runtime:wikipedia is the credential-free, deterministic
ego-lite adapter smoke. It performs the same Wikipedia field fill, bounded
wait, fresh-snapshot result selection, and ref-grounded click, then closes its
TaskSpace. It tests no Jev or text-model request; its query is isolated in the
example and is never part of the production policy.
Options:
--max-steps <n> 1..100; default 12
--log-dir <path> default .ego-jev-runs
--space-name <n> default ego-jev
--keep-space do not close a successful TaskSpace
Supported action space
| Jev operation | ego-lite execution | Candidate source |
|---|---|---|
CLICK | page.click("@ref") | current button, link, checkbox, radio, menuitem, option, or combobox |
TYPE_TEXT | page.fill("@ref", text) | current textbox or searchbox; text is requested only after the choice |
SELECT | page.selectOption("@ref", { label }) | an observed option nested under a current combobox |
SCROLL_UP, SCROLL_DOWN | page.mouse.wheel() | bounded control operation |
WAIT | page.waitForTimeout(500) | bounded control operation |
DONE, BLOCKED | no browser input | bounded control operation |
page.snapshot() in the installed ego-lite API is a string containing semantic
lines and markers such as [ref=3]; it is not a {content, refs} value. The
adapter parses only the specified roles and normalizes a marker to @3 at
execution. For TYPE_TEXT only, it also derives an exact loc=role:…[name=…]
fallback from that same current role/name when the snapshot ref is a wrapper
rather than the editable HTML control (observed on Wikipedia's searchbox).
Jev never sees or emits either form. ego-lite invalidates refs after inputs and
waits, which is why this project always observes after an action and never
stores refs in history.
How a Jev step is safe
- ego-lite returns the current semantic snapshot.
ego-statederives canonical page text;action-spacederives ref-grounded candidates.jev-clientsends one System One request with an operation head plus speculative operation-specific target heads. Target keys are opaque (click_1, etc.); snapshot refs are not sent.- The response must contain exactly the current choice keys, finite
probabilities in
[0,1]summing to one, and a selected maximum-probability key. The selected target is validated again by the loop. - Only a validated
TYPE_TEXTcalls the OpenAI Responses API. The request sends noreasoningparameter, so the model's configured default applies. Structured output constrains the result to exact JSON{ "text": "..." }, which is accepted only when non-empty and at most 2,000 characters. - ego-lite executes one input and returns a fresh snapshot. The JSONL trace records snapshot, Jev, text-model, and action latencies without recording generated text.
The trace is written to .ego-jev-runs/<timestamp>.jsonl and is excluded from
Git. It includes snapshot, action_space, jev_decision, text_model,
action_executed, and terminal records.
Verification
npm run check is offline and covers:
- semantic snapshot parsing and ref-bounded candidate construction;
- a Jev request containing speculative target heads but no
@refvalues; - validation rejection for an invented target;
- loop behavior: type, click, fresh snapshots, and clean completion only after
DONE. - an OpenAI Responses request with
text.formatJSON Schema, no explicitreasoningparameter, and output extraction across all response items.
The live smoke:wikipedia command is intentionally separate because it uses
paid, account-scoped model calls. It is the reproducible actual-browser smoke
target; its success requires a current TypeSafe key and a compatible text
model. A DONE decision is not an independent assertion that a site's goal was
achieved, so applications needing stronger guarantees should add a task-specific
verifier outside this MVP.
On 2026-09-18 the checked local configuration completed this command in five
executed actions. It used live TypeSafe Jev decisions, one live OpenAI Responses
text generation with gpt-5.6-luna, navigated to
/wiki/G%C3%B6del%27s_incompleteness_theorems, received DONE, and closed its
TaskSpace. The redacted latency trace is local-only under .ego-jev-runs/.
Confirmed interfaces and upstream references
- ego-lite: the installed TaskSpace/Page surface is
taskSpace(nameOrId),task.page("p1"),page.snapshot(),page.click("@N"),page.fill("@N", value),page.selectOption("@N", {label}),page.mouse.wheel(), andtask.finish({keep: []}). The checked source package exposes an older Playwright-like helper API, so it is intentionally not imported here. The adapter launches the documentedego-browser nodejsstdin/heredoc protocol rather thannodejs -e: in the tested macOS runtime,-ecould hang when spawned by a Node parent, and the embedded console transcript was written to stderr when stdout was piped. The adapter accepts its unique structured marker from either stream. - Jev/System One:
POST https://api.typesafe.ai/v1/systemonewithAuthorization: Bearer …,model,state, and typedchoicequestions. The response suppliesanswers.<id>.choice,probabilities, andconfidence. The live smoke receivedjev-1.13.0. - Text model:
POST https://api.openai.com/v1/responseswithinstructions,input,max_output_tokens, and a stricttext.formatJSON Schema. Response parsing scans everymessageitem foroutput_text; it deliberately does not assumeoutput[0]contains text. - Jev Ultrafast reference: its current
model.pysupplies the dynamic operation-specific action-space pattern. Its browser implementation uses Browser Harness/CDP and is not an ego-lite dependency. Its current source only maps click/fill/select as dynamic element actions; this project adds the bounded scroll/wait controls at its own loop boundary.
Both checked source repositories are MIT licensed. This repository contains an independent TypeScript implementation and no copied upstream source; see NOTICE for attribution and exact checked commits.
Current limits
- Snapshot parsing supports only the roles in the table. Frames, shadow-root edge cases, canvas, uploads, pop-ups, nested scrolling, and custom keyboard widgets are out of scope.
SELECTsupports only visible option labels nested beneath a snapshot combobox. ARIA listboxes are treated as clickable options when they have a ref.- There is no site skill, planning model, retry of browser mutations, screenshot policy, benchmark suite, or final task verifier.
- The Wikipedia smoke confirms the requested navigation, but generic
DONEremains a model decision rather than a reusable semantic success verifier.