jev-layer 0.1.0 → 0.2.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 (83) hide show
  1. package/CHANGELOG.md +45 -34
  2. package/CONTRIBUTING.md +146 -146
  3. package/LICENSE +21 -21
  4. package/README.md +123 -126
  5. package/README.ru.md +138 -117
  6. package/README.zh-CN.md +137 -117
  7. package/RELEASE.md +53 -53
  8. package/SECURITY.md +56 -56
  9. package/bin/jev.mjs +213 -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 +22 -22
  14. package/config/omp.mcp.json +14 -14
  15. package/config/providers.env.example +13 -13
  16. package/docs/AGENT-IMPLEMENTATION.md +250 -0
  17. package/docs/COMPATIBILITY.md +15 -15
  18. package/docs/HERMES-LOCAL-AGENT-HANDOFF.ru.md +177 -0
  19. package/docs/PROVIDERS.md +67 -0
  20. package/docs/SCHEMA-VERSIONING.md +96 -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 +150 -51
  31. package/integrations/hermes/plugin.yaml +10 -5
  32. package/integrations/hermes/schemas.py +89 -12
  33. package/integrations/omp/extension.js +105 -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 +162 -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 +113 -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 +571 -569
  53. package/src/cli.mjs +33 -33
  54. package/src/config.mjs +85 -75
  55. package/src/context-filter.mjs +56 -56
  56. package/src/contract.mjs +72 -72
  57. package/src/discovery.mjs +65 -65
  58. package/src/hermes-adapter.mjs +123 -0
  59. package/src/mcp-server.mjs +267 -210
  60. package/src/model-route-metrics.mjs +89 -0
  61. package/src/model-routing.mjs +87 -0
  62. package/src/providers/demo.mjs +52 -52
  63. package/src/providers/index.mjs +12 -0
  64. package/src/providers/typesafe.mjs +126 -126
  65. package/src/receipts.mjs +227 -226
  66. package/src/registry.mjs +109 -109
  67. package/src/relevance-filter.mjs +99 -99
  68. package/src/route.mjs +219 -221
  69. package/src/shadow-compaction.mjs +170 -0
  70. package/src/supervision.mjs +268 -244
  71. package/test/browser.test.mjs +199 -199
  72. package/test/capability.test.mjs +54 -54
  73. package/test/context-filter.test.mjs +65 -65
  74. package/test/hermes-adapter.test.mjs +62 -0
  75. package/test/model-route-metrics.test.mjs +61 -0
  76. package/test/model-routing.test.mjs +59 -0
  77. package/test/openrouter-provider.test.mjs +55 -55
  78. package/test/provider-transports.test.mjs +205 -0
  79. package/test/providers.test.mjs +126 -0
  80. package/test/receipts.test.mjs +71 -71
  81. package/test/route.test.mjs +74 -74
  82. package/test/shadow-compaction.test.mjs +67 -0
  83. package/test/supervision.test.mjs +117 -99
@@ -1,119 +1,119 @@
1
- import assert from "node:assert/strict";
2
- import { readFile } from "node:fs/promises";
3
- import { spawn } from "node:child_process";
4
- import { createInterface } from "node:readline";
5
- import { fileURLToPath } from "node:url";
6
- import { dirname, resolve } from "node:path";
7
-
8
- const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
9
- const runner = resolve(root, "integrations/codex/run-mcp.mjs");
10
- const child = spawn(process.env.JEV_NODE ?? "node", [runner], {
11
- cwd: root,
12
- env: {
13
- ...process.env,
14
- JEV_LAYER_ROOT: root,
15
- JEV_LAYER_PROVIDER: "openrouter",
16
- },
17
- stdio: ["pipe", "pipe", "pipe"],
18
- });
19
- const lines = createInterface({ input: child.stdout });
20
- const pending = new Map();
21
- const stderr = [];
22
- child.stderr.on("data", (chunk) => stderr.push(String(chunk)));
23
- lines.on("line", (line) => {
24
- if (!line.trim()) return;
25
- const message = JSON.parse(line);
26
- const waiter = pending.get(message.id);
27
- if (waiter) {
28
- pending.delete(message.id);
29
- waiter(message);
30
- }
31
- });
32
-
33
- try {
34
- const initialized = await request(1, "initialize", {});
35
- assert.equal(initialized.result.serverInfo.name, "jev-layer");
36
- await notification("notifications/initialized", {});
37
- const listed = await request(2, "tools/list", {});
38
- assert.ok(listed.result.tools.some((tool) => tool.name === "jev_route"));
39
-
40
- const response = await request(3, "tools/call", {
41
- name: "jev_route",
42
- arguments: {
43
- intent: "inspect the routing implementation",
44
- harness: "codex",
45
- context: { workspace: "jev-layer", operation: "read-only" },
46
- actor_permissions: ["read"],
47
- capabilities: [
48
- {
49
- id: "read_route",
50
- name: "read",
51
- kind: "tool",
52
- description: "Read src/route.mjs without modifying it.",
53
- execution: { mode: "host", target: "src/route.mjs" },
54
- permissions: ["read"],
55
- risk: "low",
56
- },
57
- {
58
- id: "read_manifest",
59
- name: "read",
60
- kind: "tool",
61
- description: "Read package.json without modifying it.",
62
- execution: { mode: "host", target: "package.json" },
63
- permissions: ["read"],
64
- risk: "low",
65
- },
66
- ],
67
- },
68
- });
69
- const decision = response.result.structuredContent;
70
- assert.equal(decision.status, "selected");
71
- assert.ok(decision.selected);
72
-
73
- const target = decision.selected === "read_route" ? "src/route.mjs" : "package.json";
74
- const content = await readFile(resolve(root, target), "utf8");
75
- console.log(JSON.stringify({
76
- ok: true,
77
- transport: "codex-mcp-runner",
78
- tool: "jev_route",
79
- decision: {
80
- selected: decision.selected,
81
- probabilities: decision.probabilities,
82
- confidence: decision.confidence,
83
- status: decision.status,
84
- latency_ms: decision.receipt?.latency_ms ?? null,
85
- cost_usd: decision.raw_jev?.usage?.cost ?? null,
86
- fallback: decision.fallback,
87
- provider: decision.receipt?.provider ?? null,
88
- },
89
- host_execution: {
90
- mode: "adapter-transport-fixture",
91
- target,
92
- bytes: Buffer.byteLength(content),
93
- note: "Codex CLI model execution was not exercised by this fixture.",
94
- },
95
- }, null, 2));
96
- } finally {
97
- child.kill();
98
- await new Promise((resolveExit) => child.once("close", resolveExit));
99
- if (stderr.length > 0) process.stderr.write(stderr.join(""));
100
- }
101
-
102
- function request(id, method, params) {
103
- return new Promise((resolveResponse, reject) => {
104
- const timer = setTimeout(() => {
105
- pending.delete(id);
106
- reject(new Error(`MCP request timed out: ${method}`));
107
- }, 15_000);
108
- pending.set(id, (message) => {
109
- clearTimeout(timer);
110
- resolveResponse(message);
111
- });
112
- child.stdin.write(`${JSON.stringify({ jsonrpc: "2.0", id, method, params })}\n`);
113
- });
114
- }
115
-
116
- function notification(method, params) {
117
- child.stdin.write(`${JSON.stringify({ jsonrpc: "2.0", method, params })}\n`);
118
- return new Promise((resolveNotification) => setImmediate(resolveNotification));
119
- }
1
+ import assert from "node:assert/strict";
2
+ import { readFile } from "node:fs/promises";
3
+ import { spawn } from "node:child_process";
4
+ import { createInterface } from "node:readline";
5
+ import { fileURLToPath } from "node:url";
6
+ import { dirname, resolve } from "node:path";
7
+
8
+ const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
9
+ const runner = resolve(root, "integrations/codex/run-mcp.mjs");
10
+ const child = spawn(process.env.JEV_NODE ?? "node", [runner], {
11
+ cwd: root,
12
+ env: {
13
+ ...process.env,
14
+ JEV_LAYER_ROOT: root,
15
+ JEV_LAYER_PROVIDER: "openrouter",
16
+ },
17
+ stdio: ["pipe", "pipe", "pipe"],
18
+ });
19
+ const lines = createInterface({ input: child.stdout });
20
+ const pending = new Map();
21
+ const stderr = [];
22
+ child.stderr.on("data", (chunk) => stderr.push(String(chunk)));
23
+ lines.on("line", (line) => {
24
+ if (!line.trim()) return;
25
+ const message = JSON.parse(line);
26
+ const waiter = pending.get(message.id);
27
+ if (waiter) {
28
+ pending.delete(message.id);
29
+ waiter(message);
30
+ }
31
+ });
32
+
33
+ try {
34
+ const initialized = await request(1, "initialize", {});
35
+ assert.equal(initialized.result.serverInfo.name, "jev-layer");
36
+ await notification("notifications/initialized", {});
37
+ const listed = await request(2, "tools/list", {});
38
+ assert.ok(listed.result.tools.some((tool) => tool.name === "jev_route"));
39
+
40
+ const response = await request(3, "tools/call", {
41
+ name: "jev_route",
42
+ arguments: {
43
+ intent: "inspect the routing implementation",
44
+ harness: "codex",
45
+ context: { workspace: "jev-layer", operation: "read-only" },
46
+ actor_permissions: ["read"],
47
+ capabilities: [
48
+ {
49
+ id: "read_route",
50
+ name: "read",
51
+ kind: "tool",
52
+ description: "Read src/route.mjs without modifying it.",
53
+ execution: { mode: "host", target: "src/route.mjs" },
54
+ permissions: ["read"],
55
+ risk: "low",
56
+ },
57
+ {
58
+ id: "read_manifest",
59
+ name: "read",
60
+ kind: "tool",
61
+ description: "Read package.json without modifying it.",
62
+ execution: { mode: "host", target: "package.json" },
63
+ permissions: ["read"],
64
+ risk: "low",
65
+ },
66
+ ],
67
+ },
68
+ });
69
+ const decision = response.result.structuredContent;
70
+ assert.equal(decision.status, "selected");
71
+ assert.ok(decision.selected);
72
+
73
+ const target = decision.selected === "read_route" ? "src/route.mjs" : "package.json";
74
+ const content = await readFile(resolve(root, target), "utf8");
75
+ console.log(JSON.stringify({
76
+ ok: true,
77
+ transport: "codex-mcp-runner",
78
+ tool: "jev_route",
79
+ decision: {
80
+ selected: decision.selected,
81
+ probabilities: decision.probabilities,
82
+ confidence: decision.confidence,
83
+ status: decision.status,
84
+ latency_ms: decision.receipt?.latency_ms ?? null,
85
+ cost_usd: decision.raw_jev?.usage?.cost ?? null,
86
+ fallback: decision.fallback,
87
+ provider: decision.receipt?.provider ?? null,
88
+ },
89
+ host_execution: {
90
+ mode: "adapter-transport-fixture",
91
+ target,
92
+ bytes: Buffer.byteLength(content),
93
+ note: "Codex CLI model execution was not exercised by this fixture.",
94
+ },
95
+ }, null, 2));
96
+ } finally {
97
+ child.kill();
98
+ await new Promise((resolveExit) => child.once("close", resolveExit));
99
+ if (stderr.length > 0) process.stderr.write(stderr.join(""));
100
+ }
101
+
102
+ function request(id, method, params) {
103
+ return new Promise((resolveResponse, reject) => {
104
+ const timer = setTimeout(() => {
105
+ pending.delete(id);
106
+ reject(new Error(`MCP request timed out: ${method}`));
107
+ }, 15_000);
108
+ pending.set(id, (message) => {
109
+ clearTimeout(timer);
110
+ resolveResponse(message);
111
+ });
112
+ child.stdin.write(`${JSON.stringify({ jsonrpc: "2.0", id, method, params })}\n`);
113
+ });
114
+ }
115
+
116
+ function notification(method, params) {
117
+ child.stdin.write(`${JSON.stringify({ jsonrpc: "2.0", method, params })}\n`);
118
+ return new Promise((resolveNotification) => setImmediate(resolveNotification));
119
+ }
@@ -1,45 +1,45 @@
1
- import assert from "node:assert/strict";
2
- import { routeRequest } from "../src/route.mjs";
3
-
4
- const messages = [
5
- ...Array.from({ length: 12 }, (_, index) => `stale unrelated worker note ${index}`),
6
- "Requirement: preserve /workspace/project/config.json exactly",
7
- "Error: command `npm test` failed with exit status 1",
8
- "recent worker state",
9
- ];
10
- const context = { messages };
11
- const before = JSON.stringify(context);
12
- let projected;
13
- const decision = await routeRequest({
14
- harness: "context-filter-e2e",
15
- intent: "inspect project state",
16
- context,
17
- policy: { context_filter_mode: "conservative" },
18
- capabilities: [{ id: "inspect", kind: "tool", name: "Inspect", description: "Inspect context", risk: "low" }],
19
- }, {
20
- provider: {
21
- name: "context-filter-e2e-provider",
22
- async decide({ state }) {
23
- projected = state;
24
- return { answers: { tool: { type: "choice", choice: "inspect", probabilities: { inspect: 1 }, confidence: 1 } } };
25
- },
26
- },
27
- });
28
- assert.equal(decision.status, "selected");
29
- assert.equal(projected.context_filter.mode, "conservative");
30
- assert.ok(projected.context_filter.dropped > 0);
31
- assert.ok(projected.context.messages.includes("Requirement: preserve /workspace/project/config.json exactly"));
32
- assert.ok(projected.context.messages.includes("Error: command `npm test` failed with exit status 1"));
33
- assert.equal(JSON.stringify(context), before);
34
- console.log(JSON.stringify({
35
- ok: true,
36
- status: decision.status,
37
- mode: projected.context_filter.mode,
38
- kept: projected.context_filter.kept,
39
- dropped: projected.context_filter.dropped,
40
- pinned: projected.context_filter.pinned,
41
- preserved: [
42
- projected.context.messages.includes("Requirement: preserve /workspace/project/config.json exactly"),
43
- projected.context.messages.includes("Error: command `npm test` failed with exit status 1"),
44
- ],
45
- }, null, 2));
1
+ import assert from "node:assert/strict";
2
+ import { routeRequest } from "../src/route.mjs";
3
+
4
+ const messages = [
5
+ ...Array.from({ length: 12 }, (_, index) => `stale unrelated worker note ${index}`),
6
+ "Requirement: preserve /workspace/project/config.json exactly",
7
+ "Error: command `npm test` failed with exit status 1",
8
+ "recent worker state",
9
+ ];
10
+ const context = { messages };
11
+ const before = JSON.stringify(context);
12
+ let projected;
13
+ const decision = await routeRequest({
14
+ harness: "context-filter-e2e",
15
+ intent: "inspect project state",
16
+ context,
17
+ policy: { context_filter_mode: "conservative" },
18
+ capabilities: [{ id: "inspect", kind: "tool", name: "Inspect", description: "Inspect context", risk: "low" }],
19
+ }, {
20
+ provider: {
21
+ name: "context-filter-e2e-provider",
22
+ async decide({ state }) {
23
+ projected = state;
24
+ return { answers: { tool: { type: "choice", choice: "inspect", probabilities: { inspect: 1 }, confidence: 1 } } };
25
+ },
26
+ },
27
+ });
28
+ assert.equal(decision.status, "selected");
29
+ assert.equal(projected.context_filter.mode, "conservative");
30
+ assert.ok(projected.context_filter.dropped > 0);
31
+ assert.ok(projected.context.messages.includes("Requirement: preserve /workspace/project/config.json exactly"));
32
+ assert.ok(projected.context.messages.includes("Error: command `npm test` failed with exit status 1"));
33
+ assert.equal(JSON.stringify(context), before);
34
+ console.log(JSON.stringify({
35
+ ok: true,
36
+ status: decision.status,
37
+ mode: projected.context_filter.mode,
38
+ kept: projected.context_filter.kept,
39
+ dropped: projected.context_filter.dropped,
40
+ pinned: projected.context_filter.pinned,
41
+ preserved: [
42
+ projected.context.messages.includes("Requirement: preserve /workspace/project/config.json exactly"),
43
+ projected.context.messages.includes("Error: command `npm test` failed with exit status 1"),
44
+ ],
45
+ }, null, 2));
@@ -1,120 +1,120 @@
1
- import assert from "node:assert/strict";
2
- import { routeRequest } from "../src/route.mjs";
3
-
4
- const candidates = [
5
- {
6
- id: "read_manifest",
7
- name: "read",
8
- kind: "tool",
9
- description: "Read the repository package manifest without modifying it.",
10
- execution: { mode: "native", target: "read" },
11
- permissions: ["read"],
12
- risk: "low",
13
- },
14
- {
15
- id: "read_route",
16
- name: "read",
17
- kind: "tool",
18
- description: "Read the routing implementation without modifying it.",
19
- execution: { mode: "native", target: "read" },
20
- permissions: ["read"],
21
- risk: "low",
22
- },
23
- ];
24
-
25
- const request = {
26
- intent: "inspect repository metadata",
27
- harness: "fail-open-smoke",
28
- context: { workspace: "jev-layer", operation: "read-only" },
29
- actor_permissions: ["read"],
30
- capabilities: candidates,
31
- };
32
-
33
- const originalEnabled = process.env.JEV_LAYER_ENABLED;
34
- const disabled = await withEnv("JEV_LAYER_ENABLED", "0", () => routeRequest(request, { provider: "openrouter" }));
35
- restoreEnv("JEV_LAYER_ENABLED", originalEnabled);
36
- assert.equal(disabled.status, "fallback");
37
- assert.equal(disabled.fallback.type, "disabled");
38
- assert.equal(disabled.execution.enabled, false);
39
-
40
- const unavailable = await routeRequest(request, {
41
- provider: "openrouter",
42
- openrouter: {
43
- apiKey: "smoke-token",
44
- endpoint: "http://127.0.0.1:1/unavailable",
45
- timeoutMs: 100,
46
- },
47
- });
48
- assert.equal(unavailable.status, "fallback");
49
- assert.equal(unavailable.fallback.type, "provider_error");
50
- assert.equal(unavailable.execution.enabled, false);
51
-
52
- const noDecision = await routeRequest({
53
- ...request,
54
- capabilities: candidates.map((candidate) => ({
55
- ...candidate,
56
- availability: { available: false, reason: "host capability intentionally unavailable" },
57
- })),
58
- }, { provider: "demo" });
59
- assert.equal(noDecision.status, "no_decision");
60
- assert.equal(noDecision.execution.enabled, false);
61
-
62
- const lowConfidence = await routeRequest(request, {
63
- provider: {
64
- name: "low-confidence-smoke-provider",
65
- async decide() {
66
- return {
67
- answers: {
68
- tool: {
69
- type: "choice",
70
- choice: "read_manifest",
71
- probabilities: { read_manifest: 0.51, read_route: 0.49 },
72
- confidence: 0.51,
73
- },
74
- },
75
- };
76
- },
77
- },
78
- policy: { min_confidence: 0.8 },
79
- });
80
- assert.equal(lowConfidence.status, "fallback");
81
- assert.equal(lowConfidence.fallback.type, "low_confidence");
82
- assert.equal(lowConfidence.selected, "read_manifest");
83
- assert.equal(lowConfidence.execution.enabled, false);
84
-
85
- console.log(JSON.stringify({
86
- ok: true,
87
- scenarios: [
88
- summarize("disabled", disabled),
89
- summarize("provider_unavailable", unavailable),
90
- summarize("no_decision", noDecision),
91
- summarize("low_confidence", lowConfidence),
92
- ],
93
- }, null, 2));
94
-
95
- async function withEnv(name, value, operation) {
96
- const previous = process.env[name];
97
- process.env[name] = value;
98
- try {
99
- return await operation();
100
- } finally {
101
- restoreEnv(name, previous);
102
- }
103
- }
104
-
105
- function restoreEnv(name, value) {
106
- if (value === undefined) delete process.env[name];
107
- else process.env[name] = value;
108
- }
109
-
110
- function summarize(name, result) {
111
- return {
112
- name,
113
- status: result.status,
114
- selected: result.selected,
115
- fallback: result.fallback?.type ?? null,
116
- confidence: result.confidence ?? null,
117
- latency_ms: result.latency_ms,
118
- execution: result.execution,
119
- };
120
- }
1
+ import assert from "node:assert/strict";
2
+ import { routeRequest } from "../src/route.mjs";
3
+
4
+ const candidates = [
5
+ {
6
+ id: "read_manifest",
7
+ name: "read",
8
+ kind: "tool",
9
+ description: "Read the repository package manifest without modifying it.",
10
+ execution: { mode: "native", target: "read" },
11
+ permissions: ["read"],
12
+ risk: "low",
13
+ },
14
+ {
15
+ id: "read_route",
16
+ name: "read",
17
+ kind: "tool",
18
+ description: "Read the routing implementation without modifying it.",
19
+ execution: { mode: "native", target: "read" },
20
+ permissions: ["read"],
21
+ risk: "low",
22
+ },
23
+ ];
24
+
25
+ const request = {
26
+ intent: "inspect repository metadata",
27
+ harness: "fail-open-smoke",
28
+ context: { workspace: "jev-layer", operation: "read-only" },
29
+ actor_permissions: ["read"],
30
+ capabilities: candidates,
31
+ };
32
+
33
+ const originalEnabled = process.env.JEV_LAYER_ENABLED;
34
+ const disabled = await withEnv("JEV_LAYER_ENABLED", "0", () => routeRequest(request, { provider: "openrouter" }));
35
+ restoreEnv("JEV_LAYER_ENABLED", originalEnabled);
36
+ assert.equal(disabled.status, "fallback");
37
+ assert.equal(disabled.fallback.type, "disabled");
38
+ assert.equal(disabled.execution.enabled, false);
39
+
40
+ const unavailable = await routeRequest(request, {
41
+ provider: "openrouter",
42
+ openrouter: {
43
+ apiKey: "smoke-token",
44
+ endpoint: "http://127.0.0.1:1/unavailable",
45
+ timeoutMs: 100,
46
+ },
47
+ });
48
+ assert.equal(unavailable.status, "fallback");
49
+ assert.equal(unavailable.fallback.type, "provider_error");
50
+ assert.equal(unavailable.execution.enabled, false);
51
+
52
+ const noDecision = await routeRequest({
53
+ ...request,
54
+ capabilities: candidates.map((candidate) => ({
55
+ ...candidate,
56
+ availability: { available: false, reason: "host capability intentionally unavailable" },
57
+ })),
58
+ }, { provider: "demo" });
59
+ assert.equal(noDecision.status, "no_decision");
60
+ assert.equal(noDecision.execution.enabled, false);
61
+
62
+ const lowConfidence = await routeRequest(request, {
63
+ provider: {
64
+ name: "low-confidence-smoke-provider",
65
+ async decide() {
66
+ return {
67
+ answers: {
68
+ tool: {
69
+ type: "choice",
70
+ choice: "read_manifest",
71
+ probabilities: { read_manifest: 0.51, read_route: 0.49 },
72
+ confidence: 0.51,
73
+ },
74
+ },
75
+ };
76
+ },
77
+ },
78
+ policy: { min_confidence: 0.8 },
79
+ });
80
+ assert.equal(lowConfidence.status, "fallback");
81
+ assert.equal(lowConfidence.fallback.type, "low_confidence");
82
+ assert.equal(lowConfidence.selected, "read_manifest");
83
+ assert.equal(lowConfidence.execution.enabled, false);
84
+
85
+ console.log(JSON.stringify({
86
+ ok: true,
87
+ scenarios: [
88
+ summarize("disabled", disabled),
89
+ summarize("provider_unavailable", unavailable),
90
+ summarize("no_decision", noDecision),
91
+ summarize("low_confidence", lowConfidence),
92
+ ],
93
+ }, null, 2));
94
+
95
+ async function withEnv(name, value, operation) {
96
+ const previous = process.env[name];
97
+ process.env[name] = value;
98
+ try {
99
+ return await operation();
100
+ } finally {
101
+ restoreEnv(name, previous);
102
+ }
103
+ }
104
+
105
+ function restoreEnv(name, value) {
106
+ if (value === undefined) delete process.env[name];
107
+ else process.env[name] = value;
108
+ }
109
+
110
+ function summarize(name, result) {
111
+ return {
112
+ name,
113
+ status: result.status,
114
+ selected: result.selected,
115
+ fallback: result.fallback?.type ?? null,
116
+ confidence: result.confidence ?? null,
117
+ latency_ms: result.latency_ms,
118
+ execution: result.execution,
119
+ };
120
+ }
@@ -1,46 +1,46 @@
1
- import assert from "node:assert/strict";
2
- import { loadConfig } from "../src/config.mjs";
3
- import { decideBrowserStep } from "../src/browser.mjs";
4
- import { routeRequest } from "../src/route.mjs";
5
- import { superviseWork } from "../src/supervision.mjs";
6
-
7
- const { config } = await loadConfig({ cwd: "/tmp/jev-no-config" });
8
- assert.equal(config.features.browser_fast_path, false);
9
- assert.equal(config.features.supervision, false);
10
- assert.equal(config.features.context_filter, null);
11
-
12
- const browser = await decideBrowserStep({ goal: "scroll down", observation: { scroll: { down: true } } }, {
13
- provider: { name: "must-not-run", async decide() { throw new Error("disabled browser must not call Jev"); } },
14
- });
15
- assert.equal(browser.decision.fallback.type, "browser_fast_path_disabled");
16
-
17
- const previousSupervision = process.env.JEV_SUPERVISION;
18
- delete process.env.JEV_SUPERVISION;
19
- try {
20
- const supervision = await superviseWork({ provider: { async evaluate() { throw new Error("disabled supervision must not call Jev"); } } });
21
- assert.equal(supervision.fallback.type, "disabled");
22
- } finally {
23
- if (previousSupervision === undefined) delete process.env.JEV_SUPERVISION;
24
- else process.env.JEV_SUPERVISION = previousSupervision;
25
- }
26
-
27
- let calls = 0;
28
- const route = await routeRequest({
29
- intent: "inspect",
30
- context: { messages: ["plain context"] },
31
- capabilities: [{ id: "inspect", kind: "tool", name: "Inspect", description: "Inspect context", risk: "low" }],
32
- }, {
33
- provider: { name: "default-route-provider", async decide() {
34
- calls += 1;
35
- return { answers: { tool: { type: "choice", choice: "inspect", probabilities: { inspect: 1 }, confidence: 1 } } };
36
- } },
37
- });
38
- assert.equal(route.status, "selected");
39
- assert.equal(calls, 1);
40
- console.log(JSON.stringify({
41
- ok: true,
42
- browser: browser.decision.fallback.type,
43
- supervision: "disabled",
44
- context_filter: "disabled",
45
- default_route_provider_calls: calls,
46
- }, null, 2));
1
+ import assert from "node:assert/strict";
2
+ import { loadConfig } from "../src/config.mjs";
3
+ import { decideBrowserStep } from "../src/browser.mjs";
4
+ import { routeRequest } from "../src/route.mjs";
5
+ import { superviseWork } from "../src/supervision.mjs";
6
+
7
+ const { config } = await loadConfig({ cwd: "/tmp/jev-no-config" });
8
+ assert.equal(config.features.browser_fast_path, false);
9
+ assert.equal(config.features.supervision, false);
10
+ assert.equal(config.features.context_filter, null);
11
+
12
+ const browser = await decideBrowserStep({ goal: "scroll down", observation: { scroll: { down: true } } }, {
13
+ provider: { name: "must-not-run", async decide() { throw new Error("disabled browser must not call Jev"); } },
14
+ });
15
+ assert.equal(browser.decision.fallback.type, "browser_fast_path_disabled");
16
+
17
+ const previousSupervision = process.env.JEV_SUPERVISION;
18
+ delete process.env.JEV_SUPERVISION;
19
+ try {
20
+ const supervision = await superviseWork({ provider: { async evaluate() { throw new Error("disabled supervision must not call Jev"); } } });
21
+ assert.equal(supervision.fallback.type, "disabled");
22
+ } finally {
23
+ if (previousSupervision === undefined) delete process.env.JEV_SUPERVISION;
24
+ else process.env.JEV_SUPERVISION = previousSupervision;
25
+ }
26
+
27
+ let calls = 0;
28
+ const route = await routeRequest({
29
+ intent: "inspect",
30
+ context: { messages: ["plain context"] },
31
+ capabilities: [{ id: "inspect", kind: "tool", name: "Inspect", description: "Inspect context", risk: "low" }],
32
+ }, {
33
+ provider: { name: "default-route-provider", async decide() {
34
+ calls += 1;
35
+ return { answers: { tool: { type: "choice", choice: "inspect", probabilities: { inspect: 1 }, confidence: 1 } } };
36
+ } },
37
+ });
38
+ assert.equal(route.status, "selected");
39
+ assert.equal(calls, 1);
40
+ console.log(JSON.stringify({
41
+ ok: true,
42
+ browser: browser.decision.fallback.type,
43
+ supervision: "disabled",
44
+ context_filter: "disabled",
45
+ default_route_provider_calls: calls,
46
+ }, null, 2));