{"id":"f26ffd62-470f-4f9f-b9e1-cad8a3b3f2b7","slug":"zhuboer-ego-jev","kind":"skill","name":"ego-jev","summary":"Complete browser tasks with Ego Lite and actively call Jev for semantic target selection, filtering, ranking, classification and text evidence judgments.","owner":"ZHUBoer","repoUrl":"https://github.com/ZHUBoer/ego-jev","license":"MIT","licenseTier":"permissive","trust":"community","stars":2,"match":null,"fewStars":true,"format":"skill","createdAt":"2026-09-19T18:37:21.000Z","addedAt":"2026-10-22T01:52:08.311Z","logoUrl":"https://avatars.githubusercontent.com/u/40383266?u=1b81adf0f295fc29fa0f73c95485b5d7f6ff48a0&v=4","linkOnly":false,"collection":null,"skillPath":null,"categories":["browser-web"],"tags":[],"runtime":"binary","local":true,"cloudCapable":false,"cloudCapability":"unknown","cloudReadyOnPlatform":false,"needsApiKey":false,"reviewed":false,"syncedAt":"2026-10-26T21:37:38.742Z","sample":false,"url":"https://conduits.dev/tools/zhuboer-ego-jev","language":"JavaScript","transports":["stdio"],"install":{},"requirements":{"env":[],"upstreamMayCharge":false},"tools":null,"readme":"# ego-jev\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)\n\nComplete browser tasks with **Ego Lite** and actively call **Jev** (TypeSafe) for semantic target selection, filtering, ranking, classification and text evidence judgments.\n\nListed on [skills.sh](https://skills.sh/zhuboer/ego-jev/ego-jev) — install with the skills CLI:\n\n```sh\nnpx skills add ZHUBoer/ego-jev\n```\n\nAn agent keeps the goal and the plan. Ego Lite drives a real Chromium browser — snapshots, DOM actions, keyboard/mouse, tabs, uploads/downloads, screenshots. Jev resolves the semantic choices: which observed link, button or card actually matches the intent, how to categorise or rank items, whether the page text is real evidence. Exact work — prices, counts, sorting, matching — stays in local code.\n\n## Why split it this way\n\n| Work | Owner |\n| --- | --- |\n| Understand the goal, plan exploration, reason across steps, write text/code | Agent (route observed semantic choices to Jev) |\n| Observe and operate the browser, enforce waits, map verified candidates to actions | Ego Lite + local code |\n| Finite semantic selection, classification, per-item relevance/quality, evidence checks | Jev by default |\n| Prices, arithmetic, counts, sorting, date comparison, exact matching | Local code |\n| Screenshots, canvas, images, visual quality | Agent vision + Ego Lite |\n\nThe speedup comes from fewer planner turns and batched judgments — not from routing every browser action through an API call.\n\nA **known action** — exact target or value already supplied by the user, deterministic matching, an established plan, or an earlier Jev choice — needs no further inference. Deciding which target *means* what the user wants is a semantic choice, and that goes through Jev. Do not relabel a semantic choice as navigation or a known click to bypass the helper.\n\n## Layout\n\n```\nSKILL.md                        Agent-facing contract and workflow\nagents/\n  openai.yaml                   Agent interface manifest: display name, default prompt, implicit invocation\nreferences/\n  client.md                     Jev HTTP helper: credentials, schemas, batching, choose(), errors\n  decision-design.md            How to write atomic questions and read typed answers\n  workflows.md                  Bounded continuous subflow runner\n  browser-integration.md        Ego Lite integration examples\n  audit.md                      Local call journal: fields, limits, HTML export\n  sources.md                    Source notes\nscripts/\n  jev.mjs                       Dependency-free TypeSafe client (choose / evaluate / evaluateMany)\n  workflow.mjs                  runWorkflow: observe → decide → execute → verify loop\n  audit.mjs                     Append-only local JSONL journal + list/show/report\n  *.test.mjs                    Test suites\nexamples/\n  triage.json                   Example request: Choice + Score + Noul in one call\n```\n\n## Requirements\n\n- Node.js 20+ (tested on Node 24)\n- A working [Ego Lite](https://github.com/ZHUBoer) / `ego-browser` installation\n- A TypeSafe API key\n\nNo npm packages are required — the helper calls the documented HTTP API directly, so it imports cleanly inside the `ego-browser` Node runtime.\n\n## Install\n\n```sh\ngit clone git@github.com:ZHUBoer/ego-jev.git ~/.agents/skills/ego-jev\n```\n\nThe skill is loaded from a skills directory such as `~/.agents/skills/` or `~/.codex/skills/`.\n\n## Credentials\n\nThe helper reads `TYPESAFE_API_KEY`, then `TYPESAFE_API_KEY_FILE`, then `~/.config/ego-jev/api-key`:\n\n```sh\nmkdir -p ~/.config/ego-jev\nprintf '%s' 'YOUR_KEY' > ~/.config/ego-jev/api-key\nchmod 600 ~/.config/ego-jev/api-key\n```\n\nThe credential is user-wide and independent of the current task directory, so new local processes under the same OS user load it automatically. Keep the key outside the skill directory. Run Jev from Node — never inside `page.evaluate()` or `page.fetch()`, so credentials never enter page code.\n\n## Minimal semantic selection\n\nThe shortest path for \"which of these observed candidates fulfils the goal?\" — one Choice request with an explicit no-match option, using the same credential and journal:\n\n```js\nconst {choose} = await import('/absolute/path/ego-jev/scripts/jev.mjs');\n\nconst decision = await choose({\n  goal: 'Open the help page about duplicate charges',\n  evidence: observedRelevantText,\n  candidates: observedCandidates.map(c => ({id: c.id, description: c.text})),\n});\n\n// decision.selectedId is a supplied id, or null when nothing fits.\nconsole.log({selectedId: decision.selectedId, model: decision.model, audit: decision.audit});\n```\n\n`selectedId` is one of the ids you supplied, or `null` for no match. `choose` performs no browser action and applies no universal confidence cutoff — recheck the page state and resolve the id through your local candidate map before executing. Supply 1–254 candidates with unique non-empty ids; `__none__` is reserved.\n\n## Asking several questions about one page state\n\nOne request, many independent typed questions. Question **ids are not visible** to Jev, so put the full judgment in `instructions`.\n\n```js\nconst {evaluate} = await import('/absolute/path/ego-jev/scripts/jev.mjs');\n\nconst result = await evaluate({\n  state: {ticket: 'I was charged twice. Please refund the duplicate.'},\n  questions: {\n    department: {\n      type: 'choice',\n      instructions: 'Which team should handle `ticket`?',\n      criteria: {\n        billing: 'Payments and refunds',\n        technical: 'Software faults',\n        unknown: 'No relevant team',\n      },\n    },\n    urgency: {\n      type: 'score',\n      instructions: 'How explicitly urgent is `ticket`?',\n      criteria: ['No urgency stated', 'Urgency indirectly implied', 'Urgency explicitly stated'],\n    },\n    refund: {type: 'noul', instructions: 'Does `ticket` explicitly request a refund?'},\n  },\n});\nconsole.log(result.answers, result.model, result.metrics);\n```\n\nReturns `audit`, `model`, keyed `answers`, `usage` and `metrics: {apiMs, attempts, questionCount, requestId?}`.\n\nAnswer types behave differently, and mixing them up is the usual source of bugs:\n\n- **Choice** — `.choice`, `.probabilities`, `.confidence`. Relative across the options you supplied; a winner does not prove any option is *relevant*.\n- **Noul** — `.noul` is P(yes), not intensity. There is no `.confidence`.\n- **Score** — `.score` is a probability-weighted index in `0..N-1`, with `.legend` preserving levels. Not an extracted number.\n\nOptional local policy helper: `routeAnswer(answer, {minConfidence, yesAt, noAt})` returns `{route:'accept', value}` or `{route:'review'}`. Its defaults are illustrative, not guarantees.\n\n## Independent states in parallel\n\n```js\nconst {evaluateMany} = await import('/absolute/path/ego-jev/scripts/jev.mjs');\nconst results = await evaluateMany([\n  {id: 'a', state: 'Please refund my order.', questions: {refund: {type: 'noul', instructions: 'Is a refund explicitly requested?'}}},\n  {id: 'b', state: 'Where is my package?',   questions: {refund: {type: 'noul', instructions: 'Is a refund explicitly requested?'}}},\n], {concurrency: 4});\n```\n\nReturns input-ordered `{id, ok, result}` or `{id, ok:false, error, audit?}` records. Apply only successful results and surface failed ids — never treat a failure as a negative answer.\n\n## Bounded subflows\n\n`runWorkflow` runs a short semantic subgoal inside one browser invocation: it observes, decides, executes with fully bound parameters, and requires a task-specific verifier to pass. Stale decisions are discarded, repeated state/action cycles are detected.\n\n```js\nconst {runWorkflow} = await import('/absolute/path/ego-jev/scripts/workflow.mjs');\nconst result = await runWorkflow({\n  goal: 'Save the quiet keyboard as my office pick',\n  observe: async () => ({fingerprint, state, actions}),\n  verify:  async o => ({done: /* real verified outcome */ false, evidence}),\n  isCurrent: async o => /* evidence still current after inference */ true,\n  execute: async (action, {remainingMs}) => { /* documented Ego methods */ },\n});\n```\n\nTerminal results: `completed`, `needs_context`, `needs_review`, `stuck`, `budget_exhausted`. Only `completed` means the verifier passed — an action receipt or a Jev answer is never proof of success. Preserve returned `attempts` and pass them as `previousAttempts` when continuing the same subgoal.\n\n## CLI\n\n```sh\nnode scripts/jev.mjs doctor                       # credential presence (not validity)\nnode scripts/jev.mjs models                       # available Jev versions\nnode scripts/jev.mjs evaluate examples/triage.json\nnode scripts/jev.mjs batch batch.json             # {\"items\":[...], \"concurrency\":4}\n```\n\n## Local call journal\n\nEvery call appends a local JSONL record under `~/.local/state/ego-jev/logs/` (directory `0700`, files `0600`), grouped by an opaque `EGO_JEV_RUN_ID`:\n\n```sh\nnode scripts/audit.mjs list\nnode scripts/audit.mjs show   /absolute/run.jsonl\nnode scripts/audit.mjs report /absolute/run.jsonl /absolute/review.html\n```\n\nThe journal stores metadata only — no API keys, no authorization headers, no page contents, no entered text. It stays local and is never uploaded. Treat it as a local execution record, not a provider-signed attestation.\n\nBefore claiming Jev-assisted completion, check this run's real records: a task with semantic decisions should show successful network calls, or a recorded attempted call plus an explicit fallback explanation. A task containing only exact operations can legitimately use zero calls — say why. No record means usage unverified, not zero.\n\n## Tests\n\n```sh\nnode --test scripts/*.test.mjs\n```\n\nTest clients inject a fetch implementation and are labeled `transport: injected`, so tests never inflate the live-call count.\n\n## License\n\nMIT — see [LICENSE](LICENSE).\n"}