jev-layer 0.2.0 → 0.3.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.
package/CHANGELOG.md CHANGED
@@ -2,18 +2,57 @@
2
2
 
3
3
  All notable changes to jev-layer are recorded here. Entries describe public behavior and compatibility impact; provider-specific experiments are listed only when they affect a public contract.
4
4
 
5
- ## Unreleased
5
+ ## [0.3.0] - 2026-10-15
6
+
7
+ ### Added
8
+
9
+ - Direct TypeSafe PNG/JPEG image input with bounded asset validation and the experimental Images/Box opt-in header.
10
+ - OpenRouter vision/Box support via inline Decisions `image_url` parts, with automatic conversion from the layer's shared assets/references format.
11
+ - Opt-in `jev_computer_step` in MCP and Hermes: closed host actions, explicit computer permissions, confirmation, paired visibility/Box grounding and screenshot-bound coordinates.
12
+ - Screenshot input for browser decisions and the host-owned browser fast-path.
13
+
14
+ ### Changed
15
+
16
+ - Pin direct TypeSafe to `jev-1.14.0` and OpenRouter to `typesafe/jev-1.14`; explicit model overrides remain supported. Image-path errors fail open without copying image bytes into receipts.
17
+ - Preserve browser action arguments in MCP/Hermes output. Raise the Hermes process timeout to 10 seconds for multi-call grounding.
18
+ - Exclude screenshot bytes from text projection, durable replay and pending receipt requests; visual replay requires original images.
19
+ - Keep all contracts on schema version 1 and computer use disabled by default.
20
+
21
+ ### Fixed
22
+
23
+ - Generate native Node/entrypoint adapter commands on Windows instead of relying on npm `.cmd` shims.
24
+ - Exercise a real installed package in cross-platform clean-install smoke; correct Windows URL paths and shutdown handling in MCP receipt smoke.
25
+ - Report the installed package version during MCP initialization.
26
+
27
+ ### Distribution
28
+
29
+ - [npm `jev-layer@0.3.0`](https://www.npmjs.com/package/jev-layer/v/0.3.0) · [GitHub `v0.3.0`](https://github.com/typakon4/jev-layer/releases/tag/v0.3.0).
30
+ - Rollback: pin `jev-layer@0.2.0` and restart the MCP process; that version does not provide the new vision/computer-use surface. Report release issues through the repository issue tracker.
31
+
32
+ ## [0.2.0] - 2026-09-25
33
+
34
+ ### Added
35
+
36
+ - Advisory `jev_model_route` recommendations with correlated execution outcomes and a local evidence report. Recommendations do not change the host's provider or model.
37
+ - Report-only `jev_shadow_compaction` candidates for host-supplied context. It does not summarize, mutate, or delete context.
38
+ - Expanded Hermes integration for provider selection, receipts, model-route correlation, supervision, and browser decisions.
39
+ - Evidence-state handling for supervision; contradictory evidence cannot produce a `finish` decision.
6
40
 
7
41
  ### Fixed
8
42
 
9
- - Route `typesafe` through the official System One provider; share provider resolution with supervision and preserve fail-open decisions for unsupported providers.
10
- - Apply configured provider environment names in CLI/MCP, including supervision and browser decisions; let Hermes/OMP honor project provider selection.
11
- - Send OpenRouter's optional app title and record the configured model in provider metadata; allow the Hermes process to outlive the provider HTTP timeout.
12
- - Cover demo, OpenRouter, and TypeSafe routing with offline provider and CLI/MCP/adapter regression checks; document setup and configuration precedence.
43
+ - Route `typesafe` through the official System One provider in the CLI, stdio MCP server, and adapters. Added offline regression coverage for the reported failure.
44
+ - Apply configured provider environment names consistently across CLI and MCP surfaces, including supervision and browser decisions.
45
+ - Preserve configured provider options in the Hermes integration and send OpenRouter's optional app title.
13
46
 
14
- ## [0.1.0]
47
+ ### Compatibility
15
48
 
16
- First public-release candidate. This version is prepared but has not been pushed, released on GitHub, or published to npm.
49
+ - Node.js `>=20`.
50
+ - Public request, decision, MCP tool, receipt, replay, and adapter contracts remain schema version 1.
51
+ - Provider-backed tests are not required for ordinary PR CI; normal CI covers provider behavior with offline fixtures.
52
+
53
+ ## [0.1.0] - 2026-09-19
54
+
55
+ Initial public release.
17
56
 
18
57
  ### Added
19
58
 
@@ -22,8 +61,6 @@ First public-release candidate. This version is prepared but has not been pushed
22
61
  - `jev_record_execution` and append-only JSONL routing/execution receipts joined by `correlation_id`.
23
62
  - Offline replay/evaluation without host execution calls.
24
63
  - Opt-in supervision judgments with deterministic host policy.
25
- - Opt-in, report-only `jev_model_route` with correlated execution outcomes and a local evidence report.
26
- - Hermes plugin integration with profile-scoped OpenRouter secret resolution, replay receipts, model-route correlation, and a local-agent handoff guide.
27
64
  - Opt-in deterministic context filtering (`shadow` and `conservative`).
28
65
  - Explicit capability discovery for skills, MCP, CLI, DSH, tools, subagents, and models.
29
66
  - Experimental, opt-in browser fast-path over host-supplied observations.
@@ -33,13 +70,4 @@ First public-release candidate. This version is prepared but has not been pushed
33
70
 
34
71
  - Node.js `>=20`.
35
72
  - Public request, decision, MCP tool, receipt, replay, and adapter contracts remain schema version 1.
36
- - Release-preparation baseline: OMP `18.2.6`, Hermes `0.21.3` (`b675e6de`), and Codex CLI `0.155.1` observed in the preparation environment. See [docs/COMPATIBILITY.md](docs/COMPATIBILITY.md).
37
- - Browser reliability is validated against the current real-browser fixtures; browser performance optimization remains experimental.
38
-
39
- ### Release constraints
40
-
41
- - Host harnesses retain ownership of permissions, approvals, native execution, retries, recovery, and final results.
42
- - Provider-backed tests require explicit secret-managed environments and are not required for ordinary PR CI.
43
- - No npm publication, GitHub release, or remote push is part of this preparation.
44
-
45
- Proposed repository: https://github.com/typakon4/jev-layer. GitHub release and comparison links become active after repository creation.
73
+ - Browser reliability was validated against real-browser fixtures; browser performance optimization remains experimental.
package/CONTRIBUTING.md CHANGED
@@ -109,7 +109,7 @@ Add a fixture covering empty input, malformed entries, duplicate ids, unavailabl
109
109
 
110
110
  ## Adding a browser executor
111
111
 
112
- Browser executors are host-owned and optional. Implement `observe()` plus `execute(action)`, and implement `select(action)` when the host has a native select mechanism. The observation must be bounded and structured; the executor must validate target ids, preserve host approval, return the next observation, and report failures without retrying implicitly. Do not add screenshots, text entry, browser workers, or permission logic to jev-layer as part of an executor contribution.
112
+ Browser executors are host-owned and optional. Implement `observe()` plus `execute(action)`, and implement `select(action)` when the host has a native select mechanism. The observation must be bounded and structured; the executor must validate target ids, preserve host approval, return the next observation, and report failures without retrying implicitly. The host may supply a prepared PNG/JPEG screenshot through the existing advisory image contract. Do not add screenshot capture, text generation, browser workers, or permission logic to jev-layer as part of an executor contribution.
113
113
 
114
114
  ## Adding an eval or replay fixture
115
115
 
package/README.md CHANGED
@@ -68,12 +68,14 @@ A provider can be deterministic `demo`, OpenRouter Decisions, or TypeSafe. Provi
68
68
  - **Shadow compaction:** `jev_shadow_compaction` produces report-only keep/drop candidates for host-supplied context. It does not summarize, mutate, or delete context, and keeps pinned evidence on provider failure.
69
69
  - **Context filtering:** optional deterministic `shadow` or `conservative` filtering reduces stale context without LLM summarization.
70
70
  - **Experimental browser fast-path:** `jev_browser_step` recommends one bounded action from a host observation. The host supplies approval, native execution, and recovery; Jev does not start a browser worker.
71
+ - **Jev 1.14 vision and computer use:** TypeSafe and OpenRouter accept PNG/JPEG screenshots. Opt-in `jev_computer_step` recommends host-declared desktop actions, with visibility-gated Box grounding and host confirmation. The adapter handles each provider's image format. See the [image/computer contract](docs/AGENT-IMPLEMENTATION.md#vision-and-computer-use).
71
72
  - **Fail open:** optional Jev surfaces are disabled by default. Jev never widens permissions or guesses execution.
72
73
 
73
74
  Enable optional surfaces explicitly:
74
75
 
75
76
  ```sh
76
77
  JEV_BROWSER_FAST_PATH=1 jev mcp
78
+ JEV_LAYER_PROVIDER=openrouter JEV_COMPUTER_USE=1 jev mcp
77
79
  JEV_SUPERVISION=1 jev mcp
78
80
  JEV_CONTEXT_FILTER=shadow jev cli --input examples/route-request.json
79
81
  ```
package/README.ru.md CHANGED
@@ -81,12 +81,14 @@ jev doctor --project /path/to/workspace
81
81
  - **Shadow compaction:** `jev_shadow_compaction` делает пакетный консервативный report с keep/drop-кандидатами для context, который передал host. Он никогда не меняет, не суммаризирует и не удаляет context; path, error, command и requirement pin-ятся до provider review, а provider failure оставляет все остальные items.
82
82
  - **Context filtering:** опциональная детерминированная фильтрация `shadow` или `conservative` убирает устаревший context без LLM-суммаризации.
83
83
  - **Experimental browser fast-path:** `jev_browser_step` выбирает одно ограниченное действие из observation host. Host предоставляет observation, approval, native execution и recovery.
84
+ - **Jev 1.14, зрение и computer use:** TypeSafe и OpenRouter принимают PNG/JPEG-скриншоты; адаптер преобразует формат изображений для каждого API. Опциональный `jev_computer_step` выбирает desktop-действие из набора host; Box-координаты требуют проверки видимости цели и host approval. См. [контракт](docs/AGENT-IMPLEMENTATION.md#vision-and-computer-use).
84
85
  - **Fail-open:** при отключённом, недоступном, ошибочном или неубедительном Jev вызове управление возвращается в обычный host path. Jev не расширяет permissions и не угадывает execution.
85
86
 
86
87
  Опциональные поверхности по умолчанию отключены:
87
88
 
88
89
  ```sh
89
90
  JEV_BROWSER_FAST_PATH=1 jev mcp
91
+ JEV_LAYER_PROVIDER=openrouter JEV_COMPUTER_USE=1 jev mcp
90
92
  JEV_SUPERVISION=1 jev mcp
91
93
  JEV_CONTEXT_FILTER=shadow jev cli --input examples/route-request.json
92
94
  ```
package/README.zh-CN.md CHANGED
@@ -80,12 +80,14 @@ jev doctor --project /path/to/workspace
80
80
  - **Shadow compaction:** `jev_shadow_compaction` 为 host 提供的 context 生成批量、保守、只报告的 keep/drop 候选。它不会修改、总结或删除 context;path、error、command 和 requirement 在 provider review 前被保留,provider 失败时保留所有其他 items。
81
81
  - **Context filtering:** 可选的确定性 `shadow` 或 `conservative` 过滤器减少过期 context,不使用 LLM 摘要。
82
82
  - **Experimental browser fast-path:** `jev_browser_step` 根据 host observation 选择一个有界浏览器动作。observation、审批、原生执行和恢复都由 host 提供。
83
+ - **Jev 1.14 视觉与 computer use:** TypeSafe 和 OpenRouter 都支持 PNG/JPEG 截图,适配器会转换各 API 的图片格式。可选 `jev_computer_step` 仅推荐 host 声明的桌面动作;Box 定位需验证目标可见性,执行仍需 host 审批。见[契约](docs/AGENT-IMPLEMENTATION.md#vision-and-computer-use)。
83
84
  - **Fail-open:** Jev 被禁用、不可用、出错或无法确定时,控制权返回 host 的正常路径。Jev 不扩大权限,也不猜测执行。
84
85
 
85
86
  所有可选表面默认关闭:
86
87
 
87
88
  ```sh
88
89
  JEV_BROWSER_FAST_PATH=1 jev mcp
90
+ JEV_LAYER_PROVIDER=openrouter JEV_COMPUTER_USE=1 jev mcp
89
91
  JEV_SUPERVISION=1 jev mcp
90
92
  JEV_CONTEXT_FILTER=shadow jev cli --input examples/route-request.json
91
93
  ```
package/SECURITY.md CHANGED
@@ -43,6 +43,14 @@ Provider output is untrusted data. Prompt injection, malicious capability descri
43
43
 
44
44
  The host must validate selected ids against its current registry, enforce permissions and approval, and reject stale, unavailable, or malformed selections. The browser fast-path is experimental, opt-in, and must not be used as a security control.
45
45
 
46
+ ## Screenshots and computer use
47
+
48
+ Screenshots can expose secrets even when text keys have been redacted. Capture/redact/crop them in the host before upload; image understanding is a remote, experimental provider feature, not local vision. Inline images are excluded from durable replay and pending receipt requests, and `data_base64` fields are redacted from context/host results.
49
+
50
+ `jev_computer_step` only chooses from host-declared click, type, key, scroll, or handoff actions. Computer permissions must be explicit. Click/type/key always require host confirmation, including when their declared risk is low. A Box is used only with a positive target-visibility probability and sufficient confidence; it is not proof that an action is safe.
51
+
52
+ Every returned computer action is bound to the supplied screenshot's SHA-256 and dimensions. Before acting, the host must verify that it is still current, re-observe if stale, map screenshot pixels to its desktop/window/crop/DPI coordinates, and enforce native permissions, focus, approvals and cancellation. The layer never captures the desktop or injects mouse/keyboard events.
53
+
46
54
  ## Reporting a vulnerability
47
55
 
48
56
  Do not open a public issue for an unpatched vulnerability or include secrets in a report. Use a private GitHub Security Advisory for the repository when that channel is available. If it is not available, contact the project maintainers through the private contact channel listed by the repository owner and provide:
package/bin/jev.mjs CHANGED
@@ -56,11 +56,13 @@ async function add(argv) {
56
56
  const project = projectPath(options);
57
57
  const { config } = await loadConfig({ cwd: project });
58
58
  const provider = configuredProvider(config, options.provider);
59
- const commandName = options.command ?? process.env.JEV_COMMAND ?? "jev";
59
+ const customCommand = options.command ?? process.env.JEV_COMMAND;
60
+ const nativeNode = process.platform === "win32" && !customCommand;
61
+ const commandName = customCommand ?? (nativeNode ? process.execPath : "jev");
60
62
  const server = {
61
63
  type: "stdio",
62
64
  command: commandName,
63
- args: ["mcp"],
65
+ args: nativeNode ? [join(PACKAGE_ROOT, "bin", "jev.mjs"), "mcp"] : ["mcp"],
64
66
  cwd: ".",
65
67
  env: { JEV_LAYER_PROVIDER: provider },
66
68
  };
@@ -168,11 +170,11 @@ async function writeSnippet(project, name, content, extension = ".yaml") {
168
170
  }
169
171
 
170
172
  function codexSnippet(server) {
171
- return `[mcp_servers.jev]\ncommand = ${JSON.stringify(server.command)}\nargs = ["mcp"]\ncwd = "."\n\n[mcp_servers.jev.env]\nJEV_LAYER_PROVIDER = ${JSON.stringify(server.env.JEV_LAYER_PROVIDER)}\n`;
173
+ return `[mcp_servers.jev]\ncommand = ${JSON.stringify(server.command)}\nargs = ${JSON.stringify(server.args)}\ncwd = "."\n\n[mcp_servers.jev.env]\nJEV_LAYER_PROVIDER = ${JSON.stringify(server.env.JEV_LAYER_PROVIDER)}\n`;
172
174
  }
173
175
 
174
176
  function hermesSnippet(server) {
175
- return `mcp_servers:\n jev:\n command: ${server.command}\n args:\n - mcp\n env:\n JEV_LAYER_PROVIDER: ${server.env.JEV_LAYER_PROVIDER}\n # Export OPENROUTER_API_KEY or TYPESAFE_API_KEY in the Hermes process environment.\n`;
177
+ return `mcp_servers:\n jev:\n command: ${JSON.stringify(server.command)}\n args:\n${server.args.map((arg) => ` - ${JSON.stringify(arg)}\n`).join("")} env:\n JEV_LAYER_PROVIDER: ${server.env.JEV_LAYER_PROVIDER}\n # Export OPENROUTER_API_KEY or TYPESAFE_API_KEY in the Hermes process environment.\n`;
176
178
  }
177
179
 
178
180
  async function writeJson(path, value) {
@@ -210,5 +212,5 @@ function parseOptions(argv) {
210
212
  }
211
213
 
212
214
  function printHelp() {
213
- console.log(`jev-layer portable setup\n\nCommands:\n jev install [--project DIR] initialize local state\n jev init [--project DIR] write .jev/config.json and env example\n jev add <harness> [options] add omp, hermes, codex, or generic MCP adapter\n jev doctor [--project DIR] check runtime, config, provider and secrets\n jev mcp run the stdio MCP server\n jev cli run the JSONL CLI adapter\n\nOptions:\n --project DIR target workspace; defaults to cwd\n --command CMD MCP command; defaults to jev\n --provider NAME override provider for generated config\n --force replace generated init files\n`);
215
+ console.log(`jev-layer portable setup\n\nCommands:\n jev install [--project DIR] initialize local state\n jev init [--project DIR] write .jev/config.json and env example\n jev add <harness> [options] add omp, hermes, codex, or generic MCP adapter\n jev doctor [--project DIR] check runtime, config, provider and secrets\n jev mcp run the stdio MCP server\n jev cli run the JSONL CLI adapter\n\nOptions:\n --project DIR target workspace; defaults to cwd\n --command CMD MCP command; defaults to jev (Node entrypoint on Windows)\n --provider NAME override provider for generated config\n --force replace generated init files\n`);
214
216
  }
@@ -4,6 +4,7 @@
4
4
  "replay_cases": ".jev/replay/cases.jsonl",
5
5
  "features": {
6
6
  "browser_fast_path": false,
7
+ "computer_use": false,
7
8
  "supervision": false,
8
9
  "context_filter": null
9
10
  },
@@ -5,9 +5,9 @@ JEV_LAYER_PROVIDER=demo
5
5
  # JEV_LAYER_PROVIDER=openrouter
6
6
  # OPENROUTER_API_KEY=replace-in-your-shell-only
7
7
  # OPENROUTER_DECISIONS_ENDPOINT=https://openrouter.ai/api/alpha/decisions
8
- # OPENROUTER_DECISIONS_MODEL=typesafe/jev-1.13
8
+ # OPENROUTER_DECISIONS_MODEL=typesafe/jev-1.14
9
9
 
10
10
  # JEV_LAYER_PROVIDER=typesafe
11
11
  # TYPESAFE_API_KEY=replace-in-your-shell-only
12
12
  # TYPESAFE_ENDPOINT=https://api.typesafe.ai/v1/systemone
13
- # TYPESAFE_MODEL=jev-latest
13
+ # TYPESAFE_MODEL=jev-1.14.0
@@ -221,6 +221,62 @@ commands/results.
221
221
 
222
222
  Replace every `<...>` placeholder with facts from the target harness. Do not leave the agent to infer them from a generic repository link.
223
223
 
224
+ ## Vision and computer use
225
+
226
+ Jev 1.14 image input and Box are experimental. Direct `typesafe` is pinned to `jev-1.14.0` and sends the required Images opt-in header. `openrouter` uses `typesafe/jev-1.14` and converts layer assets into inline `image_url` parts. Both support screenshots; live OpenRouter checks verified color perception and screenshot-only Box grounding. See [provider formats, limits and enablement](PROVIDERS.md#images-and-box-questions).
227
+
228
+ ### Browser screenshots
229
+
230
+ `jev_browser_step` accepts optional `observation.screenshot = { format, data_base64, width, height }` alongside the existing visible text, DOM/accessibility targets, tabs, scroll and progress. Existing structured actions remain authoritative; an image does not invent additional browser capabilities. The host-owned `runBrowserFastPath` also forwards screenshots from `executor.observe()` and subsequent observations.
231
+
232
+ ### Desktop decision
233
+
234
+ Enable with `features.computer_use: true`, `JEV_COMPUTER_USE=1`, or per-call `enabled: true`. Explicit per-call `enabled: false` wins. Send `jev_computer_step` (MCP) or `computer_step` (Hermes core) this shape:
235
+
236
+ ```json
237
+ {
238
+ "provider": "openrouter",
239
+ "enabled": true,
240
+ "goal": "Click the visible Save button",
241
+ "actor_permissions": ["computer"],
242
+ "observation": {
243
+ "application": "Editor",
244
+ "screenshot": {
245
+ "format": "png",
246
+ "data_base64": "<base64 of the host-captured image>",
247
+ "width": 400,
248
+ "height": 300
249
+ }
250
+ },
251
+ "actions": [
252
+ { "id": "save", "operation": "click", "target": "the Save button", "args": {} },
253
+ { "id": "handoff", "operation": "handoff", "args": {} }
254
+ ]
255
+ }
256
+ ```
257
+
258
+ The screenshot must be a prepared PNG/JPEG within upstream limits; its declared dimensions must match its bytes. No file paths, URLs, implicit screenshot capture or image resizing. Supply at most 32 unique host-owned action ids. The supported argument shapes are:
259
+
260
+ | Operation | Host-supplied `args` |
261
+ | --- | --- |
262
+ | `click` | `{ x, y, button? }`, or `{ button? }` plus a unique top-level `target` description |
263
+ | `type` | `{ text }`; exact host text, never generated by Jev |
264
+ | `key` | `{ keys: ["Control", "S"] }`; exact host key names |
265
+ | `scroll` | `{ direction: "up|down|left|right", amount?, x?, y? }` |
266
+ | `handoff` | `{ reason? }` |
267
+
268
+ Availability, additional permissions, risk and confirmation metadata use the existing capability policy. Missing `computer` permission cannot select a desktop action. Click/type/key always return `needs_confirmation`; the layer does not approve or execute them.
269
+
270
+ For a selected described click, a second request asks Noul whether the target is clearly visible and unique, and Box where it is. Both must pass `policy.min_target_visibility` (default `0.9`) and `policy.min_box_confidence` (default `0.8`). These are configurable prototype thresholds, not validated safety guarantees. Absent, ambiguous, low-confidence, malformed or failed grounding yields `fallback` with `computer_action: null`.
271
+
272
+ `computer_action.args.x/y` are the box center in **screenshot pixels**, rounded down and bounded to the image. `grounding` contains normalized edges and visibility/confidence. `computer_action.screenshot` contains the SHA-256 of decoded image bytes, dimensions and `requires_current: true`. The host must re-check freshness, focus, coordinate mapping (including crop/window origin and DPI), availability and native approval immediately before dispatch. Re-observe after execution; stop or fall back on stale state or lack of progress. Record the result using the original `correlation_id`.
273
+
274
+ ### Image privacy and replay
275
+
276
+ Images travel in route `assets`, outside the bounded text projection; generic `jev_route` attaches their references once as `state.visual_assets`. Do not duplicate those references in context unless another image expansion is intended. Do not place base64 in capability descriptions or free-text prompts.
277
+
278
+ Routing cases and retained receipt requests omit images and set `assets_omitted: true`; bounded host results redact `data_base64`. Visual replay fails open until the host explicitly restores the original assets and removes that marker. Screenshots may still contain secrets: redact them before sending to a remote provider. The layer does not provide local vision or an OS executor.
279
+
224
280
  ## Minimum verification matrix
225
281
 
226
282
  An implementation is not complete until it proves these observable behaviors:
@@ -13,3 +13,12 @@ This is the first-release compatibility baseline observed on 2026-09-19. It reco
13
13
  The adapters are thin and target the stable v1 contract documented in [CONTRIBUTING.md](../CONTRIBUTING.md) and [SCHEMA-VERSIONING.md](SCHEMA-VERSIONING.md). Full interactive harness/model/provider coverage is environment-specific and is not required for ordinary PR CI.
14
14
 
15
15
  When a public release supports a different harness version, update this table, rerun the adapter/transport checks, and document any compatibility change in `CHANGELOG.md` before tagging.
16
+
17
+ ## Jev 1.14 verification — 2026-10-15
18
+
19
+ - Existing v1 text contracts remain unchanged. Direct TypeSafe defaults to `jev-1.14.0`; OpenRouter defaults to `typesafe/jev-1.14`.
20
+ - Live OpenRouter alpha Decisions routing resolved `typesafe/jev-1.14-20261014` and selected an eligible capability without execution.
21
+ - Vision/browser/computer-use transport was exercised through an actual stdio MCP process and a loopback HTTP fixture: image bytes/header/model, confirmation, absent-target fallback, screenshot coordinate mapping, correlated denial receipt, browser-loop execution by the fixture host, and image-free replay.
22
+ - Live OpenRouter vision with the corrected `image_url` Decisions format classified red and blue PNGs correctly (confidence 1 each). The actual MCP computer tool selected a screenshot-only red rectangle and grounded its click at `(200, 148)` within the known rectangle, with visibility 0.97 and Box confidence 0.98. Host confirmation was required and no desktop input was dispatched; the correlated denial receipt omitted screenshot bytes.
23
+ - No direct TypeSafe API key was available for live accuracy testing. Earlier OpenRouter probes with TypeSafe's `assets` format were invalid transport probes, not evidence against OpenRouter vision support. Physical input dispatch, full interactive harnesses, DPI/crop mapping and general visual accuracy remain host/environment-specific and are not established by these controlled checks.
24
+ - Windows release checks: `npm test` passed 117 tests with two optional Python-transport skips; core smoke, fail-open, browser MCP e2e, MCP receipts, feature flags, supervision, context filtering and capability e2e passed. A separate Python → Node → loopback HTTP smoke exercised computer grounding and a correlated denial receipt. Cross-platform clean-install smoke now passes on Windows using native Node entrypoints, with installed CLI/MCP, correlated receipts and execution-free replay.
@@ -6,11 +6,12 @@
6
6
 
7
7
  - Исходный репозиторий: `/home/hermes/jev-layer`.
8
8
  - Активная plugin-копия Hermes: `~/.hermes/plugins/jev-layer`.
9
- - Plugin регистрирует ровно шесть tools:
9
+ - Набор tools текущего исходника (новый `jev_computer_step` появится в gateway только после синхронизации plugin-копии и restart):
10
10
  - `jev_route`;
11
11
  - `jev_record_execution`;
12
12
  - `jev_supervise`;
13
13
  - `jev_browser_step`;
14
+ - `jev_computer_step`;
14
15
  - `jev_shadow_compaction`;
15
16
  - `jev_model_route`.
16
17
  - Replay evidence активного профиля: `~/.hermes/jev-layer/replay/cases.jsonl`.
@@ -103,7 +104,7 @@ npm run model-route:report -- ~/.hermes/jev-layer/replay/cases.jsonl
103
104
 
104
105
  ### `jev_browser_step`
105
106
 
106
- Принимает только snapshot, который сделал host: `url`, visible text, targets, tabs, scroll и progress. Возвращает один bounded action: scroll, switch tab, visible link navigation, click, native select или handoff.
107
+ Принимает только snapshot, который сделал host: `url`, visible text, targets, tabs, scroll, progress и опциональный `screenshot: {format, data_base64, width, height}`. Возвращает один bounded action: scroll, switch tab, visible link navigation, click, native select или handoff.
107
108
 
108
109
  - `scroll` и `switch_tab` могут быть исполнены только через native Hermes browser executor.
109
110
  - `click`, `select`, `navigate` требуют host approval.
@@ -113,6 +114,15 @@ npm run model-route:report -- ~/.hermes/jev-layer/replay/cases.jsonl
113
114
 
114
115
  `browser-use/jev-ultrafast` был использован как архитектурный reference (динамическая индексированная action space), но его cloud/browser worker **не установлен и не запущен**. Не подменяй Hermes browser security model его executor'ом.
115
116
 
117
+ ### `jev_computer_step`
118
+
119
+ Новая opt-in поверхность для desktop: host передаёт скриншот и закрытый `actions` из click/type/key/scroll/handoff. Нужны явные `actor_permissions: ["computer"]` и Jev 1.14 через `openrouter` или `typesafe`. Включение: `JEV_COMPUTER_USE=1`, `features.computer_use: true` или `enabled: true`; явное `enabled: false` отключает вызов.
120
+
121
+ Клик по описанной цели проходит отдельные Noul + Box проверки. Координаты привязаны к SHA-256 и пикселям исходного скриншота; freshness, focus, DPI/crop mapping и исполнение проверяет host. Click/type/key требуют host confirmation даже при low risk. OpenRouter принимает изображения как inline `image_url` parts, а не TypeSafe `assets`; адаптер делает преобразование автоматически. Live проверки подтвердили различение красного/синего и grounding прямоугольника по скриншоту.
122
+
123
+ Полный [контракт и пример](AGENT-IMPLEMENTATION.md#vision-and-computer-use). Скриншоты не сохраняются в replay; visual replay без исходных assets возвращает fallback. Никакой desktop executor, OCR service или browser worker этой поверхностью не устанавливается.
124
+
125
+
116
126
  ### `jev_shadow_compaction`
117
127
 
118
128
  Возвращает консервативный report keep/drop для переданного context. Не меняет prompt, не удаляет сообщения и не заменяет host compaction. Pinned requirements, paths, errors и commands сохраняются без provider review; provider error удерживает всё.
package/docs/PROVIDERS.md CHANGED
@@ -5,11 +5,53 @@ CLI, stdio MCP, and the Hermes/OMP adapters use the same three providers. Routin
5
5
  | Provider | Default endpoint | Default model | Credentials |
6
6
  | --- | --- | --- | --- |
7
7
  | `demo` | None; deterministic word matching, no HTTP | `jev-demo` | None |
8
- | `openrouter` | `https://openrouter.ai/api/alpha/decisions` | `typesafe/jev-1.13` | `OPENROUTER_API_KEY` |
9
- | `typesafe` | `https://api.typesafe.ai/v1/systemone` | `jev-latest` | `TYPESAFE_API_KEY` |
8
+ | `openrouter` | `https://openrouter.ai/api/alpha/decisions` | `typesafe/jev-1.14` | `OPENROUTER_API_KEY` |
9
+ | `typesafe` | `https://api.typesafe.ai/v1/systemone` | `jev-1.14.0` | `TYPESAFE_API_KEY` |
10
10
 
11
11
  Both remote providers send `{ model, state, questions }` with a Choice question and Bearer authentication. They use the [OpenRouter Decisions API](https://openrouter.ai/docs/api/api-reference/alphadecisions/submit-a-decisions-questions-and-answers-request) and the [official TypeSafe API](https://docs.typesafe.ai/api), respectively.
12
12
 
13
+ TypeSafe is pinned to the [official Jev 1.14 release](https://docs.typesafe.ai/models); `TYPESAFE_MODEL` still overrides it. The upstream `jev-latest` alias currently points to 1.13, so it does not select 1.14. OpenRouter defaults to its verified [Jev 1.14 model ID](https://openrouter.ai/typesafe/jev-1.14); `OPENROUTER_DECISIONS_MODEL` still overrides it. Both providers support PNG/JPEG images and Box with Jev 1.14, using different HTTP image formats.
14
+
15
+ ## Images and Box questions
16
+
17
+ Both providers advertise `supportsImages = true`. Their `decide({ state, candidates, assets })` and `evaluate({ state, questions, assets })` accept the same optional layer-level `assets` map:
18
+
19
+ ```json
20
+ {
21
+ "state": { "screenshot": "frame-6e1d0b7c" },
22
+ "assets": {
23
+ "frame-6e1d0b7c": { "type": "image", "format": "png", "data_base64": "<base64 image bytes>" }
24
+ },
25
+ "questions": {
26
+ "visible": { "type": "noul", "instructions": "Is the target visible in `screenshot`?" },
27
+ "target": { "type": "box", "instructions": "the target in `screenshot`" }
28
+ }
29
+ }
30
+ ```
31
+
32
+ Use real standard base64, with padding and no whitespace or `data:` prefix. Only inline PNG/JPG/JPEG still images are supported; the adapter never fetches files or URLs. Each asset reference must occur in a string value in `state`, question `instructions`, or `criteria`. Prefer unique references: upstream replaces plain-text matches, including mid-sentence matches, but never object keys. Put a shared image in state once; repeated references expand the image repeatedly upstream.
33
+
34
+ `normalizeImageAssets(assets)` returns a canonical map (or `{}` when omitted) and rejects invalid map entries, references, base64, format signatures, truncated headers, animated PNGs, and local bounds. Local limits follow the [official image contract](https://docs.typesafe.ai/images): 16 MiB decoded per image, 32 MiB serialized request body, references at most 1,024 UTF-8 bytes each and 65,536 bytes total, width/height 256–1568 pixels each, and at least 78,400 pixels. No artificial image-count or 4 MB proxy limit is imposed. Header inspection is not a complete image decoder: upstream still validates image integrity and its processed/expanded 32 MiB limits. Images are never silently resized.
35
+
36
+ Direct TypeSafe adds `TypeSafe-Danger-Zone-Images: true` for assets or [Box questions](https://docs.typesafe.ai/primitives/box), without an extra environment switch. These upstream surfaces are experimental. Box requires Jev 1.14 and returns normalized edges plus confidence, even when the target is absent; pair it with Noul (`answer.noul`, not a confidence field) before grounding an action. Hosts retain approval, execution, and screenshot-to-desktop/DPI/crop mapping.
37
+
38
+ OpenRouter uses the [Multimodal Decisions format](https://openrouter.ai/docs/guides/community/multimodal-decisions): `{ "type": "image_url", "image_url": { "url": "data:image/png;base64,..." } }` parts in `state`, question instructions or criteria. The adapter expands layer asset references into these parts, including references inside longer strings, without replacing object keys or changing question types. PNG remains `image/png`; JPG/JPEG use `image/jpeg`. It does **not** send TypeSafe's top-level `assets` field to OpenRouter. No extra vision flag or direct TypeSafe key is needed.
39
+
40
+ Live checks on October 15, 2026 against `/api/alpha/decisions` resolved `typesafe/jev-1.14-20261014`: solid-red and solid-blue images were each classified correctly with confidence 1. A screenshot-only red rectangle was grounded through the actual MCP computer tool at `(200, 148)` inside the known rectangle, with visibility 0.97 and Box confidence 0.98; the response required host confirmation and no desktop action was executed. Earlier probes using TypeSafe assets/references with OpenRouter returned `unknown` because they used the wrong image input format; they do not establish lack of OpenRouter vision support.
41
+
42
+ For Windows PowerShell, with `OPENROUTER_API_KEY` already supplied by your secret store:
43
+
44
+ ```powershell
45
+ $env:JEV_LAYER_PROVIDER = "openrouter"
46
+ $env:OPENROUTER_DECISIONS_MODEL = "typesafe/jev-1.14"
47
+ $env:JEV_COMPUTER_USE = "1"
48
+ npm run mcp
49
+ ```
50
+
51
+ Restart an already-running MCP server after updating the source/environment. Vision is used when the host supplies `observation.screenshot` to `jev_computer_step`/`jev_browser_step`, or `assets` to `jev_route`; the layer does not capture the screen itself. `JEV_COMPUTER_USE` opts into desktop advice, not image input generally. Browser advice still requires its own enabled setting.
52
+
53
+ Missing credentials, unsupported image providers, timeouts, HTTP errors, and rejected images fail open through the host's existing caller error path; no retries or autonomous computer executor are added. Image-path transport, HTTP-body, and invalid-JSON error details are redacted so an upstream echo of base64 cannot enter durable error receipts.
54
+
13
55
  ## Select a provider
14
56
 
15
57
  For a stdio MCP process, export the selected provider's API key through your secret store into the harness environment, then choose one command:
@@ -61,7 +103,7 @@ Omitted fields retain the default environment names. A missing configured key re
61
103
  `jev doctor` checks configuration and key presence; it does not run inference or validate a key with the provider.
62
104
 
63
105
  ```sh
64
- node --test test/providers.test.mjs test/provider-transports.test.mjs
106
+ node --test test/providers.test.mjs test/provider-transports.test.mjs test/images.test.mjs
65
107
  ```
66
108
 
67
109
  These offline checks cover all three provider selections, CLI/MCP precedence, custom environment names, Hermes/OMP transport, and fail-open behavior. Remote-provider requests use injected responses or a loopback HTTP server with fixture credentials. No paid inference, agent model, or real browser is invoked. Live provider/account availability requires a separately configured environment.
@@ -20,6 +20,7 @@ The MCP server currently exposes these stable tool names:
20
20
 
21
21
  - `jev_route`
22
22
  - `jev_browser_step` (experimental, opt-in)
23
+ - `jev_computer_step` (experimental, opt-in; host-declared desktop actions)
23
24
  - `jev_supervise` (experimental, opt-in)
24
25
  - `jev_model_route` (report-only; experimental, opt-in)
25
26
  - `jev_shadow_compaction` (report-only; experimental, opt-in)
@@ -45,6 +46,8 @@ A v1 decision includes at least:
45
46
 
46
47
  The selected id is advisory until the host resolves it against its own capability registry and permissions. `correlation_id` is the join key for routing and execution records.
47
48
 
49
+ Optional route `assets` carries inline PNG/JPEG images outside the bounded text projection; the provider receives each reference in `state.visual_assets`. TypeSafe sends assets/references upstream; OpenRouter expands them into inline `image_url` parts. Existing text-only requests are unchanged. Browser observations may include `screenshot: { format, data_base64, width, height }`; `browser_action` includes the native action arguments (`direction`, `tab_id`, or `url`) as applicable. `jev_computer_step` adds a separate optional `computer_action`, never an executor or an approval.
50
+
48
51
  ## Execution receipts
49
52
 
50
53
  A v1 `execution_receipt` is append-only JSONL data with:
@@ -70,6 +73,8 @@ A v1 replay file is JSONL. Supported records include:
70
73
 
71
74
  Routing cases retain a sanitized request and a decision summary. `supervision_case.supervision.evidence_state` is an optional v1 object with `state` (`present`, `missing`, or `contradictory`) and bounded source labels. Replay reads cases without calling host tools. New optional record fields are compatible; changing record type, correlation semantics, or the meaning of a recorded status requires a new replay schema and migration.
72
75
 
76
+ Images are not stored in routing cases. Visual requests are marked `assets_omitted: true`; replay returns `fallback` until the original images are explicitly reattached and that marker removed. Never interpret a text-only replay as evidence of the original visual decision. Screenshot base64 is also redacted from bounded host results.
77
+
73
78
  ## Adapter contract
74
79
 
75
80
  The adapter contract is currently **v1** and is intentionally separate from provider/model versions. A v1 adapter must:
@@ -16,7 +16,7 @@ from collections import OrderedDict
16
16
  from pathlib import Path
17
17
  from typing import Any
18
18
 
19
- from .schemas import BROWSER_STEP, MODEL_ROUTE, RECORD_EXECUTION, ROUTE, SHADOW_COMPACTION, SUPERVISE
19
+ from .schemas import BROWSER_STEP, COMPUTER_STEP, MODEL_ROUTE, RECORD_EXECUTION, ROUTE, SHADOW_COMPACTION, SUPERVISE
20
20
 
21
21
  ROOT = Path(os.environ.get("JEV_LAYER_ROOT", Path(__file__).resolve().parents[2])).expanduser().resolve()
22
22
  CORE = ROOT / "src" / "hermes-adapter.mjs"
@@ -61,7 +61,7 @@ def _call_core(operation: str, args: dict, *, decision: dict | None = None) -> d
61
61
  completed = subprocess.run(
62
62
  [os.environ.get("JEV_NODE", "node"), str(CORE)],
63
63
  input=json.dumps(envelope) + "\n", capture_output=True, text=True,
64
- timeout=float(os.environ.get("JEV_LAYER_TIMEOUT_S", "5")), cwd=ROOT,
64
+ timeout=float(os.environ.get("JEV_LAYER_TIMEOUT_S", "10")), cwd=ROOT,
65
65
  env=_runtime_env(), check=False,
66
66
  )
67
67
  if completed.returncode != 0:
@@ -112,6 +112,11 @@ def jev_browser_step(args: dict, **kwargs) -> str:
112
112
  return _route(args, operation="browser_step")
113
113
 
114
114
 
115
+ def jev_computer_step(args: dict, **kwargs) -> str:
116
+ """Recommend a closed-set desktop action; host owns screenshot freshness and execution."""
117
+ return _route(args, operation="computer_step")
118
+
119
+
115
120
  def jev_model_route(args: dict, **kwargs) -> str:
116
121
  """Recommend a declared model profile; this never changes Hermes's model route."""
117
122
  return _route(args, operation="model_route")
@@ -148,3 +153,4 @@ def register(ctx):
148
153
  ctx.register_tool(name="jev_record_execution", toolset="jev_layer", schema=RECORD_EXECUTION, handler=jev_record_execution)
149
154
  ctx.register_tool(name="jev_supervise", toolset="jev_layer", schema=SUPERVISE, handler=jev_supervise)
150
155
  ctx.register_tool(name="jev_browser_step", toolset="jev_layer", schema=BROWSER_STEP, handler=jev_browser_step)
156
+ ctx.register_tool(name="jev_computer_step", toolset="jev_layer", schema=COMPUTER_STEP, handler=jev_computer_step)
@@ -1,6 +1,6 @@
1
1
  name: jev-layer
2
- version: 0.1.0
3
- description: Full host-owned Jev routing, receipts, supervision, shadow compaction, model routing, and browser decisions
2
+ version: 0.3.0
3
+ description: Host-owned Jev routing, receipts, supervision, context, model, browser and computer-use decisions
4
4
  provides_tools:
5
5
  - jev_route
6
6
  - jev_model_route
@@ -8,3 +8,4 @@ provides_tools:
8
8
  - jev_record_execution
9
9
  - jev_supervise
10
10
  - jev_browser_step
11
+ - jev_computer_step
@@ -5,6 +5,7 @@ ROUTE = {
5
5
  "intent": {"type": "string"},
6
6
  "harness": {"type": "string"},
7
7
  "context": {"type": "object"},
8
+ "assets": {"type": "object"},
8
9
  "actor_permissions": {"type": "array", "items": {"type": "string"}},
9
10
  "capabilities": {"type": "array", "items": {"type": "object"}},
10
11
  "policy": {"type": "object"},
@@ -82,6 +83,36 @@ BROWSER_STEP = {
82
83
  "start_url": {"type": ["string", "null"]},
83
84
  "observation": {"type": "object"},
84
85
  "progress": {"type": "object"},
86
+ "actor_permissions": {"type": "array", "items": {"type": "string"}},
87
+ "policy": {"type": "object"},
88
+ "provider": {"type": "string", "enum": ["demo", "typesafe", "openrouter"]},
89
+ "enabled": {"type": "boolean"},
90
+ },
91
+ }
92
+
93
+ COMPUTER_STEP = {
94
+ "type": "object",
95
+ "required": ["goal", "observation", "actions", "actor_permissions"],
96
+ "properties": {
97
+ "goal": {"type": "string"},
98
+ "harness": {"type": "string"},
99
+ "observation": {"type": "object", "description": "Host screenshot {format,data_base64,width,height}, application and visible_text."},
100
+ "actions": {"type": "array", "maxItems": 32, "items": {
101
+ "type": "object", "required": ["id", "operation"],
102
+ "properties": {
103
+ "id": {"type": "string"},
104
+ "operation": {"type": "string", "enum": ["click", "type", "key", "scroll", "handoff"]},
105
+ "args": {"type": "object"},
106
+ "target": {"type": "string"},
107
+ "description": {"type": "string"},
108
+ "risk": {"type": "string", "enum": ["low", "medium", "high", "critical"]},
109
+ "available": {"type": "boolean"},
110
+ "availability": {"type": "object"},
111
+ "permissions": {"type": "array", "items": {"type": "string"}},
112
+ "policy": {"type": "object"},
113
+ },
114
+ }},
115
+ "actor_permissions": {"type": "array", "items": {"type": "string"}},
85
116
  "policy": {"type": "object"},
86
117
  "provider": {"type": "string", "enum": ["demo", "typesafe", "openrouter"]},
87
118
  "enabled": {"type": "boolean"},
@@ -16,6 +16,7 @@ export default function jevLayerExtension(pi) {
16
16
  intent: z.string(),
17
17
  harness: z.string().optional(),
18
18
  context: z.unknown().optional(),
19
+ assets: z.unknown().optional(),
19
20
  actor_permissions: z.array(z.string()).optional(),
20
21
  capabilities: z.array(z.unknown()),
21
22
  policy: z.unknown().optional(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jev-layer",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",