jev-layer 0.1.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.
Files changed (88) hide show
  1. package/CHANGELOG.md +73 -34
  2. package/CONTRIBUTING.md +146 -146
  3. package/LICENSE +21 -21
  4. package/README.md +125 -126
  5. package/README.ru.md +140 -117
  6. package/README.zh-CN.md +139 -117
  7. package/RELEASE.md +53 -53
  8. package/SECURITY.md +64 -56
  9. package/bin/jev.mjs +215 -213
  10. package/config/codex.mcp.toml +8 -8
  11. package/config/generic-mcp.json +13 -13
  12. package/config/hermes.mcp.yaml +9 -9
  13. package/config/jev.example.json +23 -22
  14. package/config/omp.mcp.json +14 -14
  15. package/config/providers.env.example +13 -13
  16. package/docs/AGENT-IMPLEMENTATION.md +306 -0
  17. package/docs/COMPATIBILITY.md +24 -15
  18. package/docs/HERMES-LOCAL-AGENT-HANDOFF.ru.md +187 -0
  19. package/docs/PROVIDERS.md +109 -0
  20. package/docs/SCHEMA-VERSIONING.md +101 -94
  21. package/docs/architecture.svg +77 -0
  22. package/docs/jev-layer-banner.svg +38 -0
  23. package/examples/capabilities.json +39 -39
  24. package/examples/route-request.json +28 -28
  25. package/integrations/codex/.codex-plugin/plugin.json +19 -19
  26. package/integrations/codex/.mcp.json +11 -11
  27. package/integrations/codex/AGENTS.md +1 -1
  28. package/integrations/codex/run-mcp.mjs +9 -9
  29. package/integrations/codex/skills/jev-route/SKILL.md +17 -17
  30. package/integrations/hermes/__init__.py +156 -51
  31. package/integrations/hermes/plugin.yaml +11 -5
  32. package/integrations/hermes/schemas.py +120 -12
  33. package/integrations/omp/extension.js +106 -105
  34. package/integrations/template/README.md +10 -10
  35. package/integrations/template/adapter.mjs +87 -87
  36. package/package.json +60 -59
  37. package/scripts/benchmark.mjs +35 -35
  38. package/scripts/browser-benchmark.mjs +92 -92
  39. package/scripts/browser-e2e.mjs +117 -117
  40. package/scripts/capability-e2e.mjs +31 -31
  41. package/scripts/clean-install-smoke.mjs +176 -162
  42. package/scripts/codex-mcp-smoke.mjs +119 -119
  43. package/scripts/context-filter-e2e.mjs +45 -45
  44. package/scripts/fail-open-smoke.mjs +120 -120
  45. package/scripts/feature-flags-smoke.mjs +46 -46
  46. package/scripts/mcp-receipt-smoke.mjs +115 -98
  47. package/scripts/model-route-report.mjs +15 -0
  48. package/scripts/openrouter-choice.mjs +59 -59
  49. package/scripts/replay-eval.mjs +124 -124
  50. package/scripts/smoke.mjs +34 -34
  51. package/scripts/supervision-e2e.mjs +138 -109
  52. package/src/browser.mjs +583 -569
  53. package/src/cli.mjs +33 -33
  54. package/src/computer.mjs +196 -0
  55. package/src/config.mjs +86 -75
  56. package/src/context-filter.mjs +56 -56
  57. package/src/contract.mjs +74 -72
  58. package/src/discovery.mjs +65 -65
  59. package/src/hermes-adapter.mjs +134 -0
  60. package/src/images.mjs +98 -0
  61. package/src/mcp-server.mjs +313 -210
  62. package/src/model-route-metrics.mjs +89 -0
  63. package/src/model-routing.mjs +87 -0
  64. package/src/providers/demo.mjs +52 -52
  65. package/src/providers/index.mjs +12 -0
  66. package/src/providers/typesafe.mjs +206 -126
  67. package/src/receipts.mjs +228 -226
  68. package/src/registry.mjs +109 -109
  69. package/src/relevance-filter.mjs +99 -99
  70. package/src/route.mjs +228 -221
  71. package/src/shadow-compaction.mjs +170 -0
  72. package/src/supervision.mjs +268 -244
  73. package/test/browser.test.mjs +222 -199
  74. package/test/capability.test.mjs +54 -54
  75. package/test/computer.test.mjs +195 -0
  76. package/test/context-filter.test.mjs +65 -65
  77. package/test/hermes-adapter.test.mjs +63 -0
  78. package/test/image-fixture.mjs +2 -0
  79. package/test/images.test.mjs +85 -0
  80. package/test/model-route-metrics.test.mjs +61 -0
  81. package/test/model-routing.test.mjs +59 -0
  82. package/test/openrouter-provider.test.mjs +55 -55
  83. package/test/provider-transports.test.mjs +205 -0
  84. package/test/providers.test.mjs +125 -0
  85. package/test/receipts.test.mjs +71 -71
  86. package/test/route.test.mjs +86 -74
  87. package/test/shadow-compaction.test.mjs +67 -0
  88. package/test/supervision.test.mjs +117 -99
@@ -0,0 +1,306 @@
1
+ # Agent implementation guide
2
+
3
+ This is the implementation brief to hand to a coding agent before integrating jev-layer into a harness. It is intentionally operational: inspect the target harness first, preserve host ownership, and prove the fallback path. Do not treat a link to this repository as permission to invent an adapter or change the host's execution model.
4
+
5
+ ## Contract in one paragraph
6
+
7
+ jev-layer is a **System-1 decision layer**, not an executor, permission system, security boundary, retry loop, or browser worker. The host owns capability discovery, the closed candidate set, permissions, approvals, native execution, retries, recovery, and final output. Jev receives bounded input, returns one bounded decision with a `correlation_id`, and records the host's result. If Jev is disabled, unavailable, invalid, inconclusive, or refused by policy, the host continues through its normal native path.
8
+
9
+ ## Before editing
10
+
11
+ 1. Identify the harness's native capability registry and native execution function.
12
+ 2. Identify the existing permission and approval checks, especially for consequential actions.
13
+ 3. Identify the host's normal fallback path and its error/retry boundaries.
14
+ 4. Read [CONTRIBUTING.md](../CONTRIBUTING.md), [SECURITY.md](../SECURITY.md), and [SCHEMA-VERSIONING.md](SCHEMA-VERSIONING.md).
15
+ 5. Confirm the harness version and transport: CLI, stdio MCP, or an existing adapter boundary.
16
+ 6. Write a short integration plan naming the exact adapter entry point and the unchanged host-owned functions.
17
+ 7. Only then edit the smallest possible adapter/configuration surface.
18
+
19
+ Do not begin by pasting the repository URL to an agent and asking it to "integrate Jev" without supplying the target harness, its native execution path, and its approval semantics.
20
+
21
+ ## Required data flow
22
+
23
+ ### 1. Build a closed capability set in the host
24
+
25
+ The host must provide only capabilities it already knows how to execute. Each candidate needs a stable id and enough metadata for bounded selection:
26
+
27
+ ```json
28
+ {
29
+ "id": "repo:search",
30
+ "kind": "tool",
31
+ "name": "Search repository",
32
+ "description": "Find text in tracked repository files",
33
+ "permissions": ["read"],
34
+ "risk": "low",
35
+ "available": true
36
+ }
37
+ ```
38
+
39
+ Do not ask Jev to discover hidden host tools. Do not pass arbitrary shell commands as capability ids. Do not expose a capability that the host cannot validate and execute through its native registry.
40
+
41
+ ### 2. Call `jev_route` with bounded input
42
+
43
+ Preserve schema version 1 and include the host-owned facts needed for the decision:
44
+
45
+ ```json
46
+ {
47
+ "schema_version": 1,
48
+ "harness": "my-harness",
49
+ "intent": "Find the implementation of the routing contract",
50
+ "context": {
51
+ "task": "repository inspection"
52
+ },
53
+ "actor_permissions": ["read"],
54
+ "capabilities": [
55
+ {
56
+ "id": "repo:search",
57
+ "kind": "tool",
58
+ "name": "Search repository",
59
+ "description": "Find text in tracked repository files",
60
+ "permissions": ["read"],
61
+ "risk": "low",
62
+ "available": true
63
+ }
64
+ ],
65
+ "policy": {
66
+ "deterministic": true
67
+ }
68
+ }
69
+ ```
70
+
71
+ Keep `intent`, `context`, and the candidate set bounded. Do not send credentials, unbounded conversation history, hidden tool descriptions, or machine-specific paths.
72
+
73
+ ### 3. Treat the response as advice, never authorization
74
+
75
+ A normal decision has this shape:
76
+
77
+ ```json
78
+ {
79
+ "schema_version": 1,
80
+ "correlation_id": "decision-id",
81
+ "status": "selected",
82
+ "selected": "repo:search",
83
+ "execution": {
84
+ "enabled": false,
85
+ "status": "not_started"
86
+ }
87
+ }
88
+ ```
89
+
90
+ The adapter must validate all of the following before execution:
91
+
92
+ - `status` permits a host action (`selected` is different from `fallback`, `no_decision`, and `needs_confirmation`);
93
+ - `selected` is non-null and exactly matches an id in the request's candidate set;
94
+ - the candidate is still available and its permissions/risk are acceptable;
95
+ - the host's current approval policy allows the action.
96
+
97
+ Never execute an id returned by Jev as a shell command, import path, URL, or arbitrary function name. Resolve it through the harness's native registry.
98
+
99
+ ### 4. Let the host approve consequential work
100
+
101
+ Jev cannot approve, elevate, or override the host. For a consequential candidate:
102
+
103
+ 1. pause after the decision;
104
+ 2. run the host's existing approval flow;
105
+ 3. if denied, do not execute and record `status: "not_started"`;
106
+ 4. if approved, execute through the native host path.
107
+
108
+ The Jev decision does not replace the harness confirmation dialog, policy engine, or permission check.
109
+
110
+ ### 5. Execute only through the native host path
111
+
112
+ The adapter must not become a second executor. Preserve the host's existing:
113
+
114
+ - capability lookup;
115
+ - permission checks;
116
+ - approval prompts;
117
+ - process/browser/tool execution;
118
+ - retry and recovery behavior;
119
+ - result shaping and user-visible output.
120
+
121
+ Jev returns a decision. The host performs the work.
122
+
123
+ ### 6. Record one receipt with the same correlation id
124
+
125
+ After the host knows what happened, call `jev_record_execution`:
126
+
127
+ ```json
128
+ {
129
+ "correlation_id": "decision-id",
130
+ "harness": "my-harness",
131
+ "capability_id": "repo:search",
132
+ "status": "completed",
133
+ "result": {
134
+ "matches": 3
135
+ },
136
+ "exit_status": 0,
137
+ "duration_ms": 12
138
+ }
139
+ ```
140
+
141
+ Use the same `correlation_id` from `jev_route`. Use:
142
+
143
+ - `completed` when native execution finished successfully;
144
+ - `failed` when native execution ran and failed;
145
+ - `not_started` when approval, policy, or fail-open handling prevented execution.
146
+
147
+ Bound results and errors before recording them. Never write secrets or full untrusted logs into receipts.
148
+
149
+ ### 7. Preserve fail-open behavior
150
+
151
+ The following conditions must return control to the host's ordinary path without guessing a capability:
152
+
153
+ - Jev is disabled by configuration;
154
+ - the MCP/CLI process is unavailable or times out;
155
+ - the provider returns an error or malformed response;
156
+ - the decision is `fallback`, `no_decision`, or otherwise unusable;
157
+ - the selected id is not in the closed candidate set;
158
+ - permissions or approval reject the action.
159
+
160
+ A Jev failure is not permission to execute. Do not add an adapter retry loop that changes the host's existing retry semantics.
161
+
162
+ ## Installing and wiring the package
163
+
164
+ For a published local CLI:
165
+
166
+ ```sh
167
+ npm install --global jev-layer
168
+ jev init --project /path/to/workspace
169
+ jev add generic --project /path/to/workspace
170
+ jev doctor --project /path/to/workspace
171
+ ```
172
+
173
+ For a stdio MCP server:
174
+
175
+ ```sh
176
+ jev mcp
177
+ ```
178
+
179
+ The generated generic adapter uses the `jev mcp` command. Keep the adapter configuration in the project/workspace where the harness owns it; do not overwrite global harness configuration. Use `JEV_LAYER_PROVIDER=demo` for offline deterministic development. Keep OpenRouter or TypeSafe credentials in the process environment or another user-owned secret store.
180
+
181
+ For an existing harness, copy the smallest relevant example from `integrations/` and adapt its transport. Do not copy an integration wholesale without checking the harness's actual registry, approval, and execution APIs.
182
+
183
+ ## Copy-paste handoff for a coding agent
184
+
185
+ Use this prompt with the guide and the target harness details:
186
+
187
+ ```text
188
+ Implement jev-layer as a thin System-1 decision layer in this harness.
189
+
190
+ Read docs/AGENT-IMPLEMENTATION.md first. Then inspect the harness's native
191
+ capability registry, permission checks, approval flow, execution function,
192
+ retry/recovery boundaries, and existing MCP/CLI integration point.
193
+
194
+ Target harness: <name and version>
195
+ Target integration point: <file/function/config>
196
+ Native execution function: <file/function>
197
+ Approval authority: <file/function>
198
+ Normal fallback path: <file/function>
199
+
200
+ Requirements:
201
+ - Build a closed capabilities[] set from the host registry.
202
+ - Call jev_route with schema_version: 1, bounded intent/context,
203
+ actor_permissions, capabilities, and policy.
204
+ - Validate status, selected id, availability, permissions, and approval in
205
+ the host before execution.
206
+ - Resolve selected ids through the native registry; never shell out to ids
207
+ returned by Jev and never let Jev execute host work.
208
+ - Preserve the existing host fallback path when Jev is disabled, unavailable,
209
+ invalid, inconclusive, or rejected.
210
+ - Call jev_record_execution with the same correlation_id after the host knows
211
+ the result.
212
+ - Add offline tests for selected execution, fail-open, unknown-id rejection,
213
+ approval denial, and receipt correlation.
214
+ - Do not add provider credentials, global config writes, hidden capability
215
+ discovery, arbitrary command execution, or unrelated runtime features.
216
+
217
+ Before changing code, show the files/symbols you will touch. After changing
218
+ code, report the exact native execution path preserved and the verification
219
+ commands/results.
220
+ ```
221
+
222
+ Replace every `<...>` placeholder with facts from the target harness. Do not leave the agent to infer them from a generic repository link.
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
+
280
+ ## Minimum verification matrix
281
+
282
+ An implementation is not complete until it proves these observable behaviors:
283
+
284
+ | Scenario | Required result |
285
+ | --- | --- |
286
+ | Valid low-risk selection | Host executes the matching native capability once. |
287
+ | Unknown or stale selected id | Host does not execute; normal fallback remains available. |
288
+ | `needs_confirmation` | Host approval is requested before execution. |
289
+ | Approval denied | No host execution; receipt is `not_started`. |
290
+ | Jev disabled/unavailable/error | Normal host path continues without guessed selection. |
291
+ | Native execution failure | Receipt is `failed` with the same `correlation_id`. |
292
+ | Successful native execution | Receipt is `completed` with bounded result/timing metadata. |
293
+ | Replay evaluation | Routing/receipt cases replay without host execution. |
294
+
295
+ Run the existing project checks plus the matching adapter smoke fixture. At minimum, a jev-layer integration should preserve the checks in `CONTRIBUTING.md` and add a harness-specific offline fixture; provider credentials are not required for ordinary CI.
296
+
297
+ ## Common incorrect implementations
298
+
299
+ - **Calling Jev after executing the tool.** Routing must happen before the host action.
300
+ - **Passing every hidden tool to Jev.** Send only a closed, host-owned candidate set.
301
+ - **Treating `selected` as authorization.** The host still validates permissions and approval.
302
+ - **Executing the returned id directly.** Map it through the native registry; never use it as a command.
303
+ - **Replacing fail-open with a retry loop.** Return to the existing host path.
304
+ - **Letting Jev discover or mutate global config.** Discovery and configuration remain explicit and user-owned.
305
+ - **Adding provider calls to ordinary CI.** Use the deterministic `demo` provider and injected fixtures.
306
+ - **Reporting only that an adapter was added.** Show the preserved execution path, receipt correlation, and fallback evidence.
@@ -1,15 +1,24 @@
1
- # Harness compatibility baseline
2
-
3
- [English](../README.md) | [Русский](../README.ru.md) | [简体中文](../README.zh-CN.md)
4
-
5
- This is the first-release compatibility baseline observed on 2026-09-19. It records the installed harness versions used for the release-preparation checks; it is not a claim that every provider/model combination was exercised.
6
-
7
- | Harness | Version observed | Release-preparation coverage |
8
- | --- | --- | --- |
9
- | OMP | `18.2.6` | `omp --version`; OMP extension contract and host-owned execution path reviewed; Jev core smoke uses the same MCP/CLI contract. |
10
- | Hermes | `0.21.3` (`upstream b675e6de`) | `hermes --version`; Python adapter syntax/contract reviewed; Jev core smoke uses the same CLI/MCP contract. |
11
- | Codex CLI | `0.155.1` | `codex --version`; Codex MCP transport fixture and host-execution ownership path reviewed. The fixture does not invoke a Codex model. |
12
-
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
-
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.
1
+ # Harness compatibility baseline
2
+
3
+ [English](../README.md) | [Русский](../README.ru.md) | [简体中文](../README.zh-CN.md)
4
+
5
+ This is the first-release compatibility baseline observed on 2026-09-19. It records the installed harness versions used for the release-preparation checks; it is not a claim that every provider/model combination was exercised.
6
+
7
+ | Harness | Version observed | Release-preparation coverage |
8
+ | --- | --- | --- |
9
+ | OMP | `18.2.6` | `omp --version`; OMP extension contract and host-owned execution path reviewed; Jev core smoke uses the same MCP/CLI contract. |
10
+ | Hermes | `0.21.3` (`upstream b675e6de`) | `hermes --version`; Python adapter syntax/contract reviewed; Jev core smoke uses the same CLI/MCP contract. |
11
+ | Codex CLI | `0.155.1` | `codex --version`; Codex MCP transport fixture and host-execution ownership path reviewed. The fixture does not invoke a Codex model. |
12
+
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
+
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.
@@ -0,0 +1,187 @@
1
+ # Hermes integration: handoff для локального агента
2
+
3
+ Этот документ описывает **текущую рабочую интеграцию** `jev-layer` в Hermes. Он нужен локальному агенту, чтобы сначала понять границы и точки входа, а не повторно «интегрировать Jev» через глобальный конфиг, произвольные shell-команды или второй executor.
4
+
5
+ ## Что уже установлено
6
+
7
+ - Исходный репозиторий: `/home/hermes/jev-layer`.
8
+ - Активная plugin-копия Hermes: `~/.hermes/plugins/jev-layer`.
9
+ - Набор tools текущего исходника (новый `jev_computer_step` появится в gateway только после синхронизации plugin-копии и restart):
10
+ - `jev_route`;
11
+ - `jev_record_execution`;
12
+ - `jev_supervise`;
13
+ - `jev_browser_step`;
14
+ - `jev_computer_step`;
15
+ - `jev_shadow_compaction`;
16
+ - `jev_model_route`.
17
+ - Replay evidence активного профиля: `~/.hermes/jev-layer/replay/cases.jsonl`.
18
+ - Gateway загружает Python plugin при старте. После изменения `integrations/hermes/*`, `plugin.yaml` или списка tools нужен пользовательский `/restart` gateway.
19
+
20
+ Не путай surfaces:
21
+
22
+ ```text
23
+ /home/hermes/jev-layer source checkout, тесты и документация
24
+ ~/.hermes/plugins/jev-layer установленная plugin-копия текущего профиля
25
+ ~/.hermes/jev-layer/replay/cases.jsonl profile-scoped evidence, не source artifact
26
+ ~/.hermes/config.yaml глобальный Hermes config; не менять для этой plugin без явного задания
27
+ ```
28
+
29
+ ## Главный принцип
30
+
31
+ ```text
32
+ Hermes registry/permissions/approval/executor/recovery
33
+ │ closed, bounded input
34
+ ▼
35
+ jev-layer decision
36
+ │ correlation_id, advice only
37
+ ▼
38
+ Hermes validates → native execution → jev_record_execution → JSONL evidence
39
+ ```
40
+
41
+ Jev **никогда** не получает право исполнять tool, shell, URL или browser action. Любое решение — advisory. Hermes остаётся владельцем discovery, policy, approval, native execution, retry, recovery и финального ответа.
42
+
43
+ Если Jev выключен, недоступен, даёт невалидный ответ или низкую уверенность, текущий normal Hermes path продолжается. Не добавляй обходной executor и не меняй retry semantics.
44
+
45
+ ## Файловая карта
46
+
47
+ | Зачем | Файл |
48
+ | --- | --- |
49
+ | Hermes tool registration + profile-scoped secret bridge | `integrations/hermes/__init__.py` |
50
+ | JSON schemas шести tool surfaces | `integrations/hermes/schemas.py` |
51
+ | Plugin manifest | `integrations/hermes/plugin.yaml`, `plugin.yaml` |
52
+ | Один JSONL request/response adapter process | `src/hermes-adapter.mjs` |
53
+ | Общая bounded routing contract | `src/route.mjs`, `src/contract.mjs` |
54
+ | Receipt/replay JSONL format | `src/receipts.mjs` |
55
+ | Browser action space, progress и recovery | `src/browser.mjs` |
56
+ | Model recommendation | `src/model-routing.mjs` |
57
+ | Model-route evidence report | `src/model-route-metrics.mjs`, `scripts/model-route-report.mjs` |
58
+ | Context keep/drop report | `src/shadow-compaction.mjs` |
59
+ | Work-state judgement | `src/supervision.mjs` |
60
+ | Harness-neutral integration contract | `docs/AGENT-IMPLEMENTATION.md` |
61
+
62
+ ## Как работают tools
63
+
64
+ ### `jev_route`
65
+
66
+ Hermes сначала собирает **закрытый** `capabilities[]` из своего registry. Jev выбирает один известный id. До исполнения Hermes снова проверяет: `status`, точное совпадение id, availability, permission, risk и approval. После результата используется `jev_record_execution` с тем же `correlation_id`.
67
+
68
+ Нельзя передавать в candidates hidden tools, raw shell command, credential, неограниченную историю или capability, для которой у Hermes нет native executor.
69
+
70
+ ### `jev_record_execution`
71
+
72
+ Принимает результат уже выполненного/отклонённого host action. `correlation_id` должен принадлежать decision того же gateway process. Статусы: `completed`, `failed`, `not_started`.
73
+
74
+ Receipt — evidence, не источник авторизации. В `result` нельзя писать токены, пароли, номера карт и полный неочищенный лог.
75
+
76
+ ### `jev_model_route`
77
+
78
+ Возвращает рекомендацию одного из **host-declared** `models[]`. Всегда `route_mode: "shadow"`; он не меняет Hermes provider, model или reasoning effort.
79
+
80
+ Когда host закончил реальный model call, он записывает outcome через `jev_record_execution`:
81
+
82
+ ```json
83
+ {
84
+ "correlation_id": "из jev_model_route",
85
+ "capability_id": "recommended model id",
86
+ "status": "completed",
87
+ "result": {
88
+ "model_route": {
89
+ "actual_model_id": "фактически использованный id",
90
+ "retry_count": 0,
91
+ "outcome": "verified"
92
+ }
93
+ }
94
+ }
95
+ ```
96
+
97
+ Отчёт только читает receipts:
98
+
99
+ ```bash
100
+ npm run model-route:report -- ~/.hermes/jev-layer/replay/cases.jsonl
101
+ ```
102
+
103
+ Пока не накоплены репрезентативные outcomes/retries/latency/cost, **не включать** auto-switching моделей.
104
+
105
+ ### `jev_browser_step`
106
+
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.
108
+
109
+ - `scroll` и `switch_tab` могут быть исполнены только через native Hermes browser executor.
110
+ - `click`, `select`, `navigate` требуют host approval.
111
+ - `submit`, login, payment, delete, секретный ввод и `TYPE_TEXT` не являются быстрым Jev execution path.
112
+ - Progress не даёт повторять уже выполненный target и блокирует повторный scroll без измеримого состояния страницы.
113
+ - После каждого host шага можно записать routing case и execution receipt; это позволяет replay без повторного browser execution.
114
+
115
+ `browser-use/jev-ultrafast` был использован как архитектурный reference (динамическая индексированная action space), но его cloud/browser worker **не установлен и не запущен**. Не подменяй Hermes browser security model его executor'ом.
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
+
126
+ ### `jev_shadow_compaction`
127
+
128
+ Возвращает консервативный report keep/drop для переданного context. Не меняет prompt, не удаляет сообщения и не заменяет host compaction. Pinned requirements, paths, errors и commands сохраняются без provider review; provider error удерживает всё.
129
+
130
+ ### `jev_supervise`
131
+
132
+ Делает bounded judgement о work state. Hermes, не Jev, преобразует его в `continue`, `verify`, `retry`, `finish` или `escalate`. Contradictory evidence не может закончиться `finish`.
133
+
134
+ ## Secrets и providers
135
+
136
+ `integrations/hermes/__init__.py` получает `OPENROUTER_API_KEY` только через Hermes profile-scoped secret store (`agent.secret_scope.get_secret`). Ключ передаётся лишь в короткоживущий локальный Node adapter; не возвращается в tool output и не логируется.
137
+
138
+ Для локальных/offline tests используй `provider: "demo"`. Provider-backed проверки — отдельные, opt-in; ключи никогда не клади в repo, plugin manifest, test fixture или обычный shell history.
139
+
140
+ ## Безопасный change workflow
141
+
142
+ 1. Прочитай этот документ и `docs/AGENT-IMPLEMENTATION.md`.
143
+ 2. Проверь target surface: source checkout, installed plugin copy, active profile или global config.
144
+ 3. Измени source в `/home/hermes/jev-layer` и сначала добавь/измени offline test.
145
+ 4. Запусти узкий test, затем полный suite.
146
+ 5. Синхронизируй **только нужные** source files в `~/.hermes/plugins/jev-layer`.
147
+ 6. Запусти `hermes plugins doctor jev-layer`.
148
+ 7. Если менялась регистрация/handler/schema/manifest, попроси пользователя о `/restart`.
149
+ 8. После restart проверь живой gateway tool path и read back exact receipt/report.
150
+ 9. Не делай `git push`, npm publish, GitHub release или изменение глобального Hermes config без отдельной команды пользователя.
151
+
152
+ ## Проверки
153
+
154
+ Из source checkout:
155
+
156
+ ```bash
157
+ npm test
158
+ npm run receipt:mcp-smoke
159
+ npm run browser:e2e
160
+ npm run model-route:report -- /tmp/nonexistent-cases.jsonl
161
+ python3 -m py_compile integrations/hermes/__init__.py integrations/hermes/schemas.py
162
+ git diff --check
163
+ hermes plugins doctor jev-layer
164
+ ```
165
+
166
+ Текущее доказанное состояние: `npm test` — 35/35; MCP receipt smoke и browser fixture E2E проходят; gateway-live model route был записан и успешно связан с execution receipt/report. Это не доказательство автоматического роутинга моделей и не утверждение о real-browser speedup.
167
+
168
+ ## Готовый prompt локальному агенту
169
+
170
+ ```text
171
+ Работай только с jev-layer в указанном target surface. Сначала прочитай:
172
+ - docs/HERMES-LOCAL-AGENT-HANDOFF.ru.md
173
+ - docs/AGENT-IMPLEMENTATION.md
174
+
175
+ Сохрани host ownership: Jev даёт bounded advisory decision; Hermes владеет registry,
176
+ permissions, approval, execution, retry/recovery и финальным результатом. Не добавляй
177
+ автоматическое переключение моделей, browser executor, глобальный config write, provider
178
+ credential или network publication без отдельного явного требования.
179
+
180
+ Перед изменением назови source files, installed plugin files и gateway impact. Используй TDD:
181
+ сначала узкий offline test, затем implementation, потом npm test + релевантные smoke tests.
182
+ Если touch'нуты registration/handler/schema/manifest, синхронизируй plugin copy, прогоняй
183
+ hermes plugins doctor jev-layer и попроси /restart. После рестарта сделай live tool call,
184
+ запиши/прочитай exact receipt и только затем заявляй, что integration активна.
185
+
186
+ Не делай git push, npm publish, release или удаление replay/artifact без отдельной команды.
187
+ ```
@@ -0,0 +1,109 @@
1
+ # Routing providers
2
+
3
+ CLI, stdio MCP, and the Hermes/OMP adapters use the same three providers. Routing, supervision, and browser decisions share provider resolution. The host still owns execution and approval.
4
+
5
+ | Provider | Default endpoint | Default model | Credentials |
6
+ | --- | --- | --- | --- |
7
+ | `demo` | None; deterministic word matching, no HTTP | `jev-demo` | None |
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
+
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
+
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
+
55
+ ## Select a provider
56
+
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:
58
+
59
+ ```sh
60
+ JEV_LAYER_PROVIDER=demo jev mcp
61
+ JEV_LAYER_PROVIDER=openrouter jev mcp
62
+ JEV_LAYER_PROVIDER=typesafe jev mcp
63
+ ```
64
+
65
+ For a single CLI request from the repository root:
66
+
67
+ ```sh
68
+ jev cli --provider demo --input examples/route-request.json
69
+ jev cli --provider openrouter --input examples/route-request.json
70
+ jev cli --provider typesafe --input examples/route-request.json
71
+ ```
72
+
73
+ Selection precedence is: request `provider` → CLI `--provider` (CLI only) → `JEV_LAYER_PROVIDER` → `.jev/config.json` `provider` → `demo`. MCP launcher `env` entries count as process environment; update them when switching providers. Hermes and OMP delegate this choice to the CLI instead of forcing `demo`. The Hermes adapter's default process timeout is 10 seconds, allowing either provider's HTTP timeout to complete; `JEV_LAYER_TIMEOUT_S` overrides it.
74
+
75
+ `jev add <harness> --provider typesafe` generates configuration for TypeSafe in the target workspace. Use `--provider openrouter` or `--provider demo` for the other modes. Keep keys outside generated configuration.
76
+
77
+ ## Endpoints, models, and custom environment names
78
+
79
+ - TypeSafe: `TYPESAFE_ENDPOINT`, `TYPESAFE_MODEL`.
80
+ - OpenRouter: `OPENROUTER_DECISIONS_ENDPOINT`, `OPENROUTER_DECISIONS_MODEL`; optional attribution headers use `OPENROUTER_HTTP_REFERER` and `OPENROUTER_APP_TITLE`.
81
+ - Endpoints are complete request URLs, including `/v1/systemone` or `/api/alpha/decisions`.
82
+ - Default HTTP timeouts are 2 seconds for TypeSafe and 5 seconds for OpenRouter. There are no provider retries; failures return control to the host.
83
+
84
+ The CLI and MCP read `.jev/config.json` from their working directory. `JEV_CONFIG` selects another file. To use secret-store-specific environment names, set only their names in the config:
85
+
86
+ ```json
87
+ {
88
+ "provider": "typesafe",
89
+ "providers": {
90
+ "typesafe": {
91
+ "api_key_env": "MY_TYPESAFE_KEY",
92
+ "endpoint_env": "MY_TYPESAFE_ENDPOINT",
93
+ "model_env": "MY_TYPESAFE_MODEL"
94
+ }
95
+ }
96
+ }
97
+ ```
98
+
99
+ Omitted fields retain the default environment names. A missing configured key returns `fallback` without an HTTP call, even if `TYPESAFE_API_KEY` is set. The same mapping is supported under `providers.openrouter`.
100
+
101
+ ## Verification
102
+
103
+ `jev doctor` checks configuration and key presence; it does not run inference or validate a key with the provider.
104
+
105
+ ```sh
106
+ node --test test/providers.test.mjs test/provider-transports.test.mjs test/images.test.mjs
107
+ ```
108
+
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.