jiva-core 0.3.47 → 0.3.48

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 (93) hide show
  1. package/README.md +31 -1
  2. package/dist/code/agent.d.ts +13 -0
  3. package/dist/code/agent.d.ts.map +1 -1
  4. package/dist/code/agent.js +81 -7
  5. package/dist/code/agent.js.map +1 -1
  6. package/dist/code/benchmark/agent-factory.d.ts +18 -0
  7. package/dist/code/benchmark/agent-factory.d.ts.map +1 -0
  8. package/dist/code/benchmark/agent-factory.js +21 -0
  9. package/dist/code/benchmark/agent-factory.js.map +1 -0
  10. package/dist/code/benchmark/fixtures.d.ts +28 -0
  11. package/dist/code/benchmark/fixtures.d.ts.map +1 -0
  12. package/dist/code/benchmark/fixtures.js +177 -0
  13. package/dist/code/benchmark/fixtures.js.map +1 -0
  14. package/dist/code/benchmark/index.d.ts +16 -0
  15. package/dist/code/benchmark/index.d.ts.map +1 -0
  16. package/dist/code/benchmark/index.js +14 -0
  17. package/dist/code/benchmark/index.js.map +1 -0
  18. package/dist/code/benchmark/microcrm/assets/api.test.mjs +254 -0
  19. package/dist/code/benchmark/microcrm/assets/app.full.reference.mjs +267 -0
  20. package/dist/code/benchmark/microcrm/assets/app.reference.mjs +191 -0
  21. package/dist/code/benchmark/microcrm/assets/bulk.test.mjs +63 -0
  22. package/dist/code/benchmark/microcrm/assets/idempotency.test.mjs +64 -0
  23. package/dist/code/benchmark/microcrm/assets/query.test.mjs +96 -0
  24. package/dist/code/benchmark/microcrm/assets/stats.test.mjs +67 -0
  25. package/dist/code/benchmark/microcrm/index.d.ts +23 -0
  26. package/dist/code/benchmark/microcrm/index.d.ts.map +1 -0
  27. package/dist/code/benchmark/microcrm/index.js +72 -0
  28. package/dist/code/benchmark/microcrm/index.js.map +1 -0
  29. package/dist/code/benchmark/microcrm/spec.d.ts +13 -0
  30. package/dist/code/benchmark/microcrm/spec.d.ts.map +1 -0
  31. package/dist/code/benchmark/microcrm/spec.js +115 -0
  32. package/dist/code/benchmark/microcrm/spec.js.map +1 -0
  33. package/dist/code/benchmark/orchestrator-factory.d.ts +36 -0
  34. package/dist/code/benchmark/orchestrator-factory.d.ts.map +1 -0
  35. package/dist/code/benchmark/orchestrator-factory.js +57 -0
  36. package/dist/code/benchmark/orchestrator-factory.js.map +1 -0
  37. package/dist/code/benchmark/report.d.ts +8 -0
  38. package/dist/code/benchmark/report.d.ts.map +1 -0
  39. package/dist/code/benchmark/report.js +103 -0
  40. package/dist/code/benchmark/report.js.map +1 -0
  41. package/dist/code/benchmark/runner.d.ts +29 -0
  42. package/dist/code/benchmark/runner.d.ts.map +1 -0
  43. package/dist/code/benchmark/runner.js +219 -0
  44. package/dist/code/benchmark/runner.js.map +1 -0
  45. package/dist/code/benchmark/suites.d.ts +19 -0
  46. package/dist/code/benchmark/suites.d.ts.map +1 -0
  47. package/dist/code/benchmark/suites.js +48 -0
  48. package/dist/code/benchmark/suites.js.map +1 -0
  49. package/dist/code/benchmark/tasks.d.ts +16 -0
  50. package/dist/code/benchmark/tasks.d.ts.map +1 -0
  51. package/dist/code/benchmark/tasks.js +153 -0
  52. package/dist/code/benchmark/tasks.js.map +1 -0
  53. package/dist/code/benchmark/tests.d.ts +17 -0
  54. package/dist/code/benchmark/tests.d.ts.map +1 -0
  55. package/dist/code/benchmark/tests.js +234 -0
  56. package/dist/code/benchmark/tests.js.map +1 -0
  57. package/dist/code/benchmark/types.d.ts +161 -0
  58. package/dist/code/benchmark/types.d.ts.map +1 -0
  59. package/dist/code/benchmark/types.js +11 -0
  60. package/dist/code/benchmark/types.js.map +1 -0
  61. package/dist/code/benchmark/verify.d.ts +21 -0
  62. package/dist/code/benchmark/verify.d.ts.map +1 -0
  63. package/dist/code/benchmark/verify.js +108 -0
  64. package/dist/code/benchmark/verify.js.map +1 -0
  65. package/dist/code/tools/edit.d.ts.map +1 -1
  66. package/dist/code/tools/edit.js +12 -3
  67. package/dist/code/tools/edit.js.map +1 -1
  68. package/dist/code/tools/write.d.ts.map +1 -1
  69. package/dist/code/tools/write.js +13 -3
  70. package/dist/code/tools/write.js.map +1 -1
  71. package/dist/core/agent-interface.d.ts +5 -0
  72. package/dist/core/agent-interface.d.ts.map +1 -1
  73. package/dist/interfaces/cli/index.js +125 -0
  74. package/dist/interfaces/cli/index.js.map +1 -1
  75. package/dist/interfaces/cli/repl.d.ts.map +1 -1
  76. package/dist/interfaces/cli/repl.js +41 -15
  77. package/dist/interfaces/cli/repl.js.map +1 -1
  78. package/dist/interfaces/cli/setup-wizard.d.ts.map +1 -1
  79. package/dist/interfaces/cli/setup-wizard.js +2 -1
  80. package/dist/interfaces/cli/setup-wizard.js.map +1 -1
  81. package/dist/interfaces/http/index.d.ts.map +1 -1
  82. package/dist/interfaces/http/index.js +2 -0
  83. package/dist/interfaces/http/index.js.map +1 -1
  84. package/dist/interfaces/http/routes/benchmark.d.ts +14 -0
  85. package/dist/interfaces/http/routes/benchmark.d.ts.map +1 -0
  86. package/dist/interfaces/http/routes/benchmark.js +130 -0
  87. package/dist/interfaces/http/routes/benchmark.js.map +1 -0
  88. package/dist/models/model-client.d.ts +2 -1
  89. package/dist/models/model-client.d.ts.map +1 -1
  90. package/dist/models/model-client.js.map +1 -1
  91. package/package.json +2 -2
  92. package/scripts/bench-selftest.mjs +112 -0
  93. package/scripts/copy-benchmark-assets.mjs +17 -0
@@ -0,0 +1,234 @@
1
+ /**
2
+ * Cumulative node:test files for the benchmark suite.
3
+ *
4
+ * Tier N's workspace contains every test file test1..testN, so the verifier
5
+ * catches regressions in earlier tiers (critical for the tier-5 refactor and
6
+ * the tier-8 debug task). All tests import the library through `../src/index.js`
7
+ * except the tier-5 structural test, which deliberately imports the split
8
+ * modules to force the refactor.
9
+ *
10
+ * The agent must NOT modify any of these files (see BenchmarkTask.protectedPaths).
11
+ */
12
+ const TEST_FILES = {
13
+ 1: {
14
+ path: 'test/t01.test.mjs',
15
+ content: `import { test } from 'node:test';
16
+ import assert from 'node:assert/strict';
17
+ import { createTask } from '../src/index.js';
18
+
19
+ test('createTask sets the title and defaults done to false', () => {
20
+ const t = createTask('Buy milk');
21
+ assert.equal(t.title, 'Buy milk');
22
+ assert.equal(t.done, false);
23
+ });
24
+
25
+ test('createTask rejects empty or blank titles', () => {
26
+ assert.throws(() => createTask(''));
27
+ assert.throws(() => createTask(' '));
28
+ });
29
+ `,
30
+ },
31
+ 2: {
32
+ path: 'test/t02.test.mjs',
33
+ content: `import { test } from 'node:test';
34
+ import assert from 'node:assert/strict';
35
+ import { addTask, removeTask, listTasks } from '../src/index.js';
36
+
37
+ test('addTask appends a task with an incrementing id', () => {
38
+ let list = [];
39
+ list = addTask(list, 'a');
40
+ assert.equal(list.length, 1);
41
+ assert.equal(list[0].id, 1);
42
+ list = addTask(list, 'b');
43
+ assert.equal(list.length, 2);
44
+ assert.equal(list[1].id, 2);
45
+ });
46
+
47
+ test('addTask does not mutate the input list', () => {
48
+ const original = [];
49
+ const next = addTask(original, 'a');
50
+ assert.equal(original.length, 0);
51
+ assert.equal(next.length, 1);
52
+ });
53
+
54
+ test('removeTask removes the task with the given id', () => {
55
+ let list = addTask(addTask(addTask([], 'a'), 'b'), 'c'); // ids 1,2,3
56
+ list = removeTask(list, 2);
57
+ assert.deepEqual(list.map((t) => t.id), [1, 3]);
58
+ });
59
+
60
+ test('listTasks returns all tasks', () => {
61
+ const list = addTask(addTask([], 'a'), 'b');
62
+ assert.equal(listTasks(list).length, 2);
63
+ });
64
+ `,
65
+ },
66
+ 3: {
67
+ path: 'test/t03.test.mjs',
68
+ content: `import { test } from 'node:test';
69
+ import assert from 'node:assert/strict';
70
+ import { addTask, toggleTask } from '../src/index.js';
71
+
72
+ test('toggleTask flips done for the task with the matching id', () => {
73
+ let list = addTask(addTask([], 'a'), 'b'); // ids 1,2
74
+ list = toggleTask(list, 2);
75
+ assert.equal(list.find((t) => t.id === 1).done, false);
76
+ assert.equal(list.find((t) => t.id === 2).done, true);
77
+ });
78
+
79
+ test('toggleTask toggles back to false when called twice', () => {
80
+ let list = addTask([], 'a'); // id 1
81
+ list = toggleTask(list, 1);
82
+ list = toggleTask(list, 1);
83
+ assert.equal(list.find((t) => t.id === 1).done, false);
84
+ });
85
+ `,
86
+ },
87
+ 4: {
88
+ path: 'test/t04.test.mjs',
89
+ content: `import { test } from 'node:test';
90
+ import assert from 'node:assert/strict';
91
+ import { addTask, setPriority, tasksByPriority } from '../src/index.js';
92
+
93
+ test('new tasks default to normal priority', () => {
94
+ const list = addTask([], 'a');
95
+ assert.equal(list[0].priority, 'normal');
96
+ });
97
+
98
+ test('setPriority updates the priority of the matching task', () => {
99
+ let list = addTask(addTask([], 'a'), 'b'); // ids 1,2
100
+ list = setPriority(list, 2, 'high');
101
+ assert.equal(list.find((t) => t.id === 2).priority, 'high');
102
+ assert.equal(list.find((t) => t.id === 1).priority, 'normal');
103
+ });
104
+
105
+ test('setPriority rejects an invalid priority', () => {
106
+ const list = addTask([], 'a');
107
+ assert.throws(() => setPriority(list, 1, 'urgent'));
108
+ });
109
+
110
+ test('tasksByPriority filters by priority', () => {
111
+ let list = addTask(addTask([], 'a'), 'b');
112
+ list = setPriority(list, 1, 'high');
113
+ assert.equal(tasksByPriority(list, 'high').length, 1);
114
+ assert.equal(tasksByPriority(list, 'normal').length, 1);
115
+ });
116
+ `,
117
+ },
118
+ 5: {
119
+ path: 'test/t05.test.mjs',
120
+ content: `import { test } from 'node:test';
121
+ import assert from 'node:assert/strict';
122
+ import * as model from '../src/model.js';
123
+ import * as query from '../src/query.js';
124
+ import * as index from '../src/index.js';
125
+
126
+ test('model module exposes the mutation functions', () => {
127
+ for (const fn of ['createTask', 'addTask', 'removeTask', 'toggleTask', 'setPriority']) {
128
+ assert.equal(typeof model[fn], 'function', 'model.' + fn);
129
+ }
130
+ });
131
+
132
+ test('query module exposes the read functions', () => {
133
+ for (const fn of ['listTasks', 'tasksByPriority']) {
134
+ assert.equal(typeof query[fn], 'function', 'query.' + fn);
135
+ }
136
+ });
137
+
138
+ test('index re-exports the full public API', () => {
139
+ for (const fn of ['createTask', 'addTask', 'removeTask', 'toggleTask', 'setPriority', 'listTasks', 'tasksByPriority']) {
140
+ assert.equal(typeof index[fn], 'function', 'index.' + fn);
141
+ }
142
+ });
143
+ `,
144
+ },
145
+ 6: {
146
+ path: 'test/t06.test.mjs',
147
+ content: `import { test } from 'node:test';
148
+ import assert from 'node:assert/strict';
149
+ import { sortTasks } from '../src/index.js';
150
+
151
+ const mk = (id, due) => ({ id, title: 't' + id, done: false, priority: 'normal', due });
152
+
153
+ test('sorts by due date ascending', () => {
154
+ const list = [mk(1, '2026-03-01'), mk(2, '2026-01-01'), mk(3, '2026-02-01')];
155
+ assert.deepEqual(sortTasks(list, { by: 'due' }).map((t) => t.id), [2, 3, 1]);
156
+ });
157
+
158
+ test('places tasks without a due date last', () => {
159
+ const list = [mk(1, null), mk(2, '2026-01-01'), mk(3, undefined)];
160
+ const ids = sortTasks(list, { by: 'due' }).map((t) => t.id);
161
+ assert.equal(ids[0], 2);
162
+ assert.deepEqual(ids.slice(1).sort(), [1, 3]);
163
+ });
164
+
165
+ test('is stable for equal due dates', () => {
166
+ const list = [mk(1, '2026-01-01'), mk(2, '2026-01-01'), mk(3, '2026-01-01')];
167
+ assert.deepEqual(sortTasks(list, { by: 'due' }).map((t) => t.id), [1, 2, 3]);
168
+ });
169
+
170
+ test('does not mutate the input list', () => {
171
+ const list = [mk(2, '2026-02-01'), mk(1, '2026-01-01')];
172
+ sortTasks(list, { by: 'due' });
173
+ assert.deepEqual(list.map((t) => t.id), [2, 1]);
174
+ });
175
+ `,
176
+ },
177
+ 7: {
178
+ path: 'test/t07.test.mjs',
179
+ content: `import { test } from 'node:test';
180
+ import assert from 'node:assert/strict';
181
+ import { addTask, setPriority, serialize, deserialize } from '../src/index.js';
182
+
183
+ test('serialize/deserialize round-trips a list preserving all fields', () => {
184
+ let list = addTask(addTask([], 'a'), 'b');
185
+ list = setPriority(list, 1, 'high');
186
+ const restored = deserialize(serialize(list));
187
+ assert.deepEqual(restored, list);
188
+ });
189
+
190
+ test('deserialize rejects JSON that is not an array', () => {
191
+ assert.throws(() => deserialize('{}'));
192
+ });
193
+ `,
194
+ },
195
+ 8: {
196
+ path: 'test/t08.test.mjs',
197
+ content: `import { test } from 'node:test';
198
+ import assert from 'node:assert/strict';
199
+ import { addTask, removeTask, serialize, deserialize } from '../src/index.js';
200
+
201
+ test('ids stay unique after a removal and re-add', () => {
202
+ let list = addTask(addTask(addTask([], 'a'), 'b'), 'c'); // ids 1,2,3
203
+ list = removeTask(list, 2); // ids 1,3
204
+ list = addTask(list, 'd');
205
+ const ids = list.map((t) => t.id);
206
+ assert.equal(new Set(ids).size, ids.length, 'duplicate ids: ' + ids.join(','));
207
+ });
208
+
209
+ test('ids stay unique after a serialize round-trip and add', () => {
210
+ let list = addTask(addTask(addTask([], 'a'), 'b'), 'c');
211
+ list = removeTask(list, 2);
212
+ list = deserialize(serialize(list));
213
+ list = addTask(list, 'e');
214
+ const ids = list.map((t) => t.id);
215
+ assert.equal(new Set(ids).size, ids.length, 'duplicate ids: ' + ids.join(','));
216
+ });
217
+ `,
218
+ },
219
+ };
220
+ /** All test files for tiers 1..tier (cumulative). */
221
+ export function cumulativeTests(tier) {
222
+ const files = {};
223
+ for (let t = 1; t <= tier; t++) {
224
+ const entry = TEST_FILES[t];
225
+ if (entry)
226
+ files[entry.path] = entry.content;
227
+ }
228
+ return files;
229
+ }
230
+ /** Paths of all test files for tiers 1..tier (the protected set). */
231
+ export function cumulativeTestPaths(tier) {
232
+ return Object.keys(cumulativeTests(tier));
233
+ }
234
+ //# sourceMappingURL=tests.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tests.js","sourceRoot":"","sources":["../../../src/code/benchmark/tests.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,MAAM,UAAU,GAAsD;IACpE,CAAC,EAAE;QACD,IAAI,EAAE,mBAAmB;QACzB,OAAO,EAAE;;;;;;;;;;;;;;CAcZ;KACE;IACD,CAAC,EAAE;QACD,IAAI,EAAE,mBAAmB;QACzB,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+BZ;KACE;IACD,CAAC,EAAE;QACD,IAAI,EAAE,mBAAmB;QACzB,OAAO,EAAE;;;;;;;;;;;;;;;;;CAiBZ;KACE;IACD,CAAC,EAAE;QACD,IAAI,EAAE,mBAAmB;QACzB,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2BZ;KACE;IACD,CAAC,EAAE;QACD,IAAI,EAAE,mBAAmB;QACzB,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;CAuBZ;KACE;IACD,CAAC,EAAE;QACD,IAAI,EAAE,mBAAmB;QACzB,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4BZ;KACE;IACD,CAAC,EAAE;QACD,IAAI,EAAE,mBAAmB;QACzB,OAAO,EAAE;;;;;;;;;;;;;;CAcZ;KACE;IACD,CAAC,EAAE;QACD,IAAI,EAAE,mBAAmB;QACzB,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;CAoBZ;KACE;CACF,CAAC;AAEF,qDAAqD;AACrD,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC;QAC/B,MAAM,KAAK,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;QAC5B,IAAI,KAAK;YAAE,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC;IAC/C,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,OAAO,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC;AAC5C,CAAC"}
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Benchmark types — shared by the runner, the CLI command and the HTTP routes.
3
+ *
4
+ * The benchmark measures how well a given model + config performs in code mode
5
+ * across a TDD-style suite of increasing complexity. Each task scaffolds an
6
+ * isolated workspace containing failing tests; the agent must make them pass
7
+ * without editing the protected test files. Scoring is deterministic — it is the
8
+ * exit status of Node's built-in test runner, not an LLM judgement.
9
+ */
10
+ import type { TokenUsageSnapshot } from '../../models/token-tracker.js';
11
+ /** A map of workspace-relative path → file content, written before the agent runs. */
12
+ export type TaskFile = Record<string, string>;
13
+ /**
14
+ * A single benchmark task. Tasks are ordered by `tier` (1 = easiest) and build
15
+ * on one another: the scaffold for tier N is the canonical solution of tiers
16
+ * 1..N-1 plus the new failing test(s) for tier N.
17
+ */
18
+ export interface BenchmarkTask {
19
+ /** Stable identifier, e.g. "t03-bugfix-toggle". */
20
+ id: string;
21
+ /** Complexity tier, 1 (trivial) .. 8 (long-horizon debug). */
22
+ tier: number;
23
+ /** Short human title. */
24
+ title: string;
25
+ /** The capability this tier primarily exercises (shown in reports). */
26
+ capability: string;
27
+ /** Whether the task builds from scratch or improves/fixes existing code. */
28
+ kind: 'scratch' | 'extend' | 'bugfix' | 'refactor';
29
+ /** The instruction handed to the agent. */
30
+ prompt: string;
31
+ /** Files written into the workspace before the agent runs (the scaffold). */
32
+ scaffold: TaskFile;
33
+ /**
34
+ * Workspace-relative paths the agent must NOT modify (the tests). If any of
35
+ * these differ after the run, the task fails with reason `tests-modified`.
36
+ */
37
+ protectedPaths: string[];
38
+ /** Per-task iteration cap (overridable globally). */
39
+ maxIterations: number;
40
+ /** Per-task wall-clock timeout in ms (overridable globally). */
41
+ timeoutMs: number;
42
+ }
43
+ /**
44
+ * Scoring mode for a suite:
45
+ * - 'gating' : binary pass/fail per task; tasks build on one another (the baseline
46
+ * taskstore suite). Measures "does code mode work at all".
47
+ * - 'scored' : graded by the FRACTION of spec tests passed (the capability/frontier
48
+ * suites). The headline metric is the pass-rate, not all-or-nothing.
49
+ */
50
+ export type ScoringMode = 'gating' | 'scored';
51
+ /** A named collection of tasks with a shared purpose and scoring philosophy. */
52
+ export interface BenchmarkSuite {
53
+ id: string;
54
+ name: string;
55
+ description: string;
56
+ /** Difficulty/intent tier of the whole suite. */
57
+ level: 'baseline' | 'capability' | 'frontier';
58
+ scoring: ScoringMode;
59
+ tasks: BenchmarkTask[];
60
+ }
61
+ /** Outcome of running the deterministic verifier against a workspace. */
62
+ export interface VerifyResult {
63
+ passed: boolean;
64
+ /** Number of passing / failing test cases parsed from the runner output. */
65
+ testsPassed: number;
66
+ testsFailed: number;
67
+ /** Names of failing test cases (parsed from `not ok` lines) — drives scored reports. */
68
+ failingTests: string[];
69
+ /** Categorised failure reason when !passed. */
70
+ reason?: 'tests-failed' | 'tests-modified' | 'runner-error' | 'timeout';
71
+ /** Trimmed runner output (stdout+stderr), useful for diagnosis. */
72
+ output: string;
73
+ }
74
+ /** Per-task result with diagnostic metrics. */
75
+ export interface TaskResult {
76
+ id: string;
77
+ tier: number;
78
+ title: string;
79
+ capability: string;
80
+ kind: BenchmarkTask['kind'];
81
+ passed: boolean;
82
+ /** Categorised reason when !passed (mirrors VerifyResult.reason or 'agent-error'). */
83
+ reason?: VerifyResult['reason'] | 'agent-error';
84
+ /** Agent loop iterations consumed. */
85
+ iterations: number;
86
+ /** True when the agent hit its iteration cap (a strong "struggling" signal). */
87
+ hitMaxIterations: boolean;
88
+ /** Tool names the agent invoked. */
89
+ toolsUsed: string[];
90
+ /** Token usage accumulated by the agent during this task. */
91
+ tokenUsage?: TokenUsageSnapshot;
92
+ /** Wall-clock duration of the agent turn (not counting verification). */
93
+ wallTimeMs: number;
94
+ testsPassed: number;
95
+ testsFailed: number;
96
+ /** Names of failing test cases (for scored suites, this is the per-capability gap list). */
97
+ failingTests?: string[];
98
+ /** Times the model hit its output-token limit mid tool-call (write/edit cut off). */
99
+ truncationEvents?: number;
100
+ /** True when this task failed AND hit output-token limits — an output-length limitation. */
101
+ outputLimited?: boolean;
102
+ /** Free-form diagnostic notes (errors, truncation hints, verifier output head). */
103
+ notes?: string;
104
+ }
105
+ /** Aggregate result for a full suite run. */
106
+ export interface SuiteResult {
107
+ startedAt: string;
108
+ finishedAt: string;
109
+ /** Suite identity and scoring mode (drives report rendering). */
110
+ suiteId?: string;
111
+ suiteName?: string;
112
+ scoring?: ScoringMode;
113
+ /** Model label, when resolvable from the orchestrator config. */
114
+ model?: string;
115
+ totalTasks: number;
116
+ passed: number;
117
+ failed: number;
118
+ /** Highest tier reached with a pass (a single capability ceiling number). */
119
+ highestTierPassed: number;
120
+ /** Aggregate spec-test counts across the suite (the headline for scored suites). */
121
+ totalTestsPassed: number;
122
+ totalTestsRun: number;
123
+ /** Percentage of spec tests passed (0–100). */
124
+ scorePct: number;
125
+ totalWallTimeMs: number;
126
+ totalTokens?: number;
127
+ tasks: TaskResult[];
128
+ }
129
+ /** Options controlling a suite run. */
130
+ export interface RunnerOptions {
131
+ /** Run only tiers 1..maxTier. */
132
+ maxTier?: number;
133
+ /** Run only these task ids (overrides maxTier). */
134
+ taskIds?: string[];
135
+ /** Global override for each task's iteration cap. */
136
+ maxIterations?: number;
137
+ /** Global override for each task's timeout. */
138
+ timeoutMs?: number;
139
+ /** Enable LSP during the run (default: off, to reduce variance). */
140
+ lspEnabled?: boolean;
141
+ /**
142
+ * Carry the agent's own resulting workspace forward to the next tier instead
143
+ * of scaffolding from the canonical golden baseline. Surfaces cascading
144
+ * failures the way a real session would.
145
+ */
146
+ continuous?: boolean;
147
+ /** Keep temp workspaces on disk for debugging. */
148
+ keepWorkspaces?: boolean;
149
+ /** Model label for reporting. */
150
+ model?: string;
151
+ /** Suite identity + scoring, embedded into the result for reporting. */
152
+ suiteId?: string;
153
+ suiteName?: string;
154
+ scoring?: ScoringMode;
155
+ }
156
+ /** Progress callback fired around each task (enables CLI lines and SSE). */
157
+ export interface ProgressEvents {
158
+ onTaskStart?: (task: BenchmarkTask, index: number, total: number) => void;
159
+ onTaskDone?: (result: TaskResult, index: number, total: number) => void;
160
+ }
161
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/code/benchmark/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAExE,sFAAsF;AACtF,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAE9C;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,mDAAmD;IACnD,EAAE,EAAE,MAAM,CAAC;IACX,8DAA8D;IAC9D,IAAI,EAAE,MAAM,CAAC;IACb,yBAAyB;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,UAAU,EAAE,MAAM,CAAC;IACnB,4EAA4E;IAC5E,IAAI,EAAE,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,UAAU,CAAC;IACnD,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,6EAA6E;IAC7E,QAAQ,EAAE,QAAQ,CAAC;IACnB;;;OAGG;IACH,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,qDAAqD;IACrD,aAAa,EAAE,MAAM,CAAC;IACtB,gEAAgE;IAChE,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;GAMG;AACH,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE9C,gFAAgF;AAChF,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,iDAAiD;IACjD,KAAK,EAAE,UAAU,GAAG,YAAY,GAAG,UAAU,CAAC;IAC9C,OAAO,EAAE,WAAW,CAAC;IACrB,KAAK,EAAE,aAAa,EAAE,CAAC;CACxB;AAED,yEAAyE;AACzE,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,OAAO,CAAC;IAChB,4EAA4E;IAC5E,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,wFAAwF;IACxF,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,+CAA+C;IAC/C,MAAM,CAAC,EAAE,cAAc,GAAG,gBAAgB,GAAG,cAAc,GAAG,SAAS,CAAC;IACxE,mEAAmE;IACnE,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,+CAA+C;AAC/C,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC5B,MAAM,EAAE,OAAO,CAAC;IAChB,sFAAsF;IACtF,MAAM,CAAC,EAAE,YAAY,CAAC,QAAQ,CAAC,GAAG,aAAa,CAAC;IAChD,sCAAsC;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,gFAAgF;IAChF,gBAAgB,EAAE,OAAO,CAAC;IAC1B,oCAAoC;IACpC,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,6DAA6D;IAC7D,UAAU,CAAC,EAAE,kBAAkB,CAAC;IAChC,yEAAyE;IACzE,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,4FAA4F;IAC5F,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,qFAAqF;IACrF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,4FAA4F;IAC5F,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,mFAAmF;IACnF,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,6CAA6C;AAC7C,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,iEAAiE;IACjE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,iEAAiE;IACjE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,6EAA6E;IAC7E,iBAAiB,EAAE,MAAM,CAAC;IAC1B,oFAAoF;IACpF,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,+CAA+C;IAC/C,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,UAAU,EAAE,CAAC;CACrB;AAED,uCAAuC;AACvC,MAAM,WAAW,aAAa;IAC5B,iCAAiC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mDAAmD;IACnD,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,qDAAqD;IACrD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,+CAA+C;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oEAAoE;IACpE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,kDAAkD;IAClD,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,iCAAiC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wEAAwE;IACxE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,WAAW,CAAC;CACvB;AAED,4EAA4E;AAC5E,MAAM,WAAW,cAAc;IAC7B,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1E,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACzE"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Benchmark types — shared by the runner, the CLI command and the HTTP routes.
3
+ *
4
+ * The benchmark measures how well a given model + config performs in code mode
5
+ * across a TDD-style suite of increasing complexity. Each task scaffolds an
6
+ * isolated workspace containing failing tests; the agent must make them pass
7
+ * without editing the protected test files. Scoring is deterministic — it is the
8
+ * exit status of Node's built-in test runner, not an LLM judgement.
9
+ */
10
+ export {};
11
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/code/benchmark/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Deterministic verifier — runs Node's built-in test runner against a workspace
3
+ * and reports pass/fail. No external test framework, no network.
4
+ *
5
+ * Two checks gate a pass:
6
+ * 1. Tamper check — the protected test files must be byte-identical to what was
7
+ * scaffolded. If the agent edited a test to make it pass, the task fails
8
+ * with reason `tests-modified`.
9
+ * 2. Test run — `node --test` must exit 0 with at least one passing test and
10
+ * zero failures.
11
+ */
12
+ import type { VerifyResult } from './types.js';
13
+ /**
14
+ * Run the verifier.
15
+ *
16
+ * @param workspaceDir absolute path of the task workspace
17
+ * @param protectedFiles map of relative path → original content (the scaffolded tests)
18
+ * @param timeoutMs hard cap for the test process
19
+ */
20
+ export declare function runNodeTests(workspaceDir: string, protectedFiles: Record<string, string>, timeoutMs?: number): Promise<VerifyResult>;
21
+ //# sourceMappingURL=verify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../../src/code/benchmark/verify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AA+B/C;;;;;;GAMG;AACH,wBAAsB,YAAY,CAChC,YAAY,EAAE,MAAM,EACpB,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACtC,SAAS,SAAS,GACjB,OAAO,CAAC,YAAY,CAAC,CAwDvB"}
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Deterministic verifier — runs Node's built-in test runner against a workspace
3
+ * and reports pass/fail. No external test framework, no network.
4
+ *
5
+ * Two checks gate a pass:
6
+ * 1. Tamper check — the protected test files must be byte-identical to what was
7
+ * scaffolded. If the agent edited a test to make it pass, the task fails
8
+ * with reason `tests-modified`.
9
+ * 2. Test run — `node --test` must exit 0 with at least one passing test and
10
+ * zero failures.
11
+ */
12
+ import { spawn } from 'child_process';
13
+ import { readFile } from 'fs/promises';
14
+ import { join } from 'path';
15
+ /** Read a workspace file, returning null if it is missing. */
16
+ async function tryRead(workspaceDir, relPath) {
17
+ try {
18
+ return await readFile(join(workspaceDir, relPath), 'utf-8');
19
+ }
20
+ catch {
21
+ return null;
22
+ }
23
+ }
24
+ /** Parse `# pass N` / `# fail N` from Node's TAP-ish test output. */
25
+ function parseCounts(output) {
26
+ const pass = output.match(/^# pass (\d+)/m);
27
+ const fail = output.match(/^# fail (\d+)/m);
28
+ return {
29
+ passed: pass ? parseInt(pass[1], 10) : 0,
30
+ failed: fail ? parseInt(fail[1], 10) : 0,
31
+ };
32
+ }
33
+ /** Parse failing top-level test names from the TAP `not ok N - <name>` lines. */
34
+ function parseFailingTests(output) {
35
+ const names = [];
36
+ for (const line of output.split('\n')) {
37
+ const m = line.match(/^not ok \d+ - (.+?)(?: # .*)?$/);
38
+ if (m)
39
+ names.push(m[1].trim());
40
+ }
41
+ return names;
42
+ }
43
+ /**
44
+ * Run the verifier.
45
+ *
46
+ * @param workspaceDir absolute path of the task workspace
47
+ * @param protectedFiles map of relative path → original content (the scaffolded tests)
48
+ * @param timeoutMs hard cap for the test process
49
+ */
50
+ export async function runNodeTests(workspaceDir, protectedFiles, timeoutMs = 60_000) {
51
+ // 1. Tamper check.
52
+ for (const [relPath, original] of Object.entries(protectedFiles)) {
53
+ const current = await tryRead(workspaceDir, relPath);
54
+ if (current === null) {
55
+ return { passed: false, testsPassed: 0, testsFailed: 0, failingTests: [], reason: 'tests-modified', output: `Protected test file was removed: ${relPath}` };
56
+ }
57
+ if (current !== original) {
58
+ return { passed: false, testsPassed: 0, testsFailed: 0, failingTests: [], reason: 'tests-modified', output: `Protected test file was modified: ${relPath}` };
59
+ }
60
+ }
61
+ // 2. Run `node --test`, scoped to ONLY the canonical protected test files.
62
+ // Bare `node --test` discovers any test file in the workspace, so an agent's own
63
+ // scratch tests would pollute the counts and could cause false failures. Passing the
64
+ // protected paths explicitly makes scoring depend solely on the canonical suite.
65
+ const testPaths = Object.keys(protectedFiles);
66
+ const args = testPaths.length > 0 ? ['--test', ...testPaths] : ['--test'];
67
+ return new Promise((resolve) => {
68
+ const child = spawn(process.execPath, args, {
69
+ cwd: workspaceDir,
70
+ env: { ...process.env, NODE_OPTIONS: '' },
71
+ });
72
+ let out = '';
73
+ const append = (buf) => {
74
+ out += buf.toString();
75
+ if (out.length > 200_000)
76
+ out = out.slice(-200_000); // cap memory
77
+ };
78
+ child.stdout.on('data', append);
79
+ child.stderr.on('data', append);
80
+ const timer = setTimeout(() => {
81
+ child.kill('SIGKILL');
82
+ resolve({ passed: false, testsPassed: 0, testsFailed: 0, failingTests: parseFailingTests(out), reason: 'timeout', output: trimOutput(out) });
83
+ }, timeoutMs);
84
+ child.on('error', (err) => {
85
+ clearTimeout(timer);
86
+ resolve({ passed: false, testsPassed: 0, testsFailed: 0, failingTests: [], reason: 'runner-error', output: `${err.message}\n${trimOutput(out)}` });
87
+ });
88
+ child.on('close', (code) => {
89
+ clearTimeout(timer);
90
+ const { passed, failed } = parseCounts(out);
91
+ const ok = code === 0 && failed === 0 && passed > 0;
92
+ resolve({
93
+ passed: ok,
94
+ testsPassed: passed,
95
+ testsFailed: failed,
96
+ failingTests: parseFailingTests(out),
97
+ reason: ok ? undefined : 'tests-failed',
98
+ output: trimOutput(out),
99
+ });
100
+ });
101
+ });
102
+ }
103
+ /** Keep the most relevant tail of the runner output for diagnostics. */
104
+ function trimOutput(out) {
105
+ const max = 4_000;
106
+ return out.length > max ? out.slice(-max) : out;
107
+ }
108
+ //# sourceMappingURL=verify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify.js","sourceRoot":"","sources":["../../../src/code/benchmark/verify.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"edit.d.ts","sourceRoot":"","sources":["../../../src/code/tools/edit.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,KAAK,EAAE,SAAS,EAAmB,MAAM,YAAY,CAAC;AAI7D,eAAO,MAAM,YAAY,EAAE,SAwI1B,CAAC;AA2OF,qDAAqD;AACrD,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,UAAQ,GAAG,MAAM,CAiCzG"}
1
+ {"version":3,"file":"edit.d.ts","sourceRoot":"","sources":["../../../src/code/tools/edit.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,KAAK,EAAE,SAAS,EAAmB,MAAM,YAAY,CAAC;AAI7D,eAAO,MAAM,YAAY,EAAE,SAiJ1B,CAAC;AA2OF,qDAAqD;AACrD,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,UAAQ,GAAG,MAAM,CAiCzG"}
@@ -38,7 +38,11 @@ Returns a diff of the changes made, plus any LSP errors detected.`,
38
38
  properties: {
39
39
  file_path: {
40
40
  type: 'string',
41
- description: 'Absolute path to the file to edit',
41
+ description: 'Absolute path to the file to edit (preferred). The alias "path" is also accepted.',
42
+ },
43
+ path: {
44
+ type: 'string',
45
+ description: 'Alias for file_path. Prefer file_path; either is accepted.',
42
46
  },
43
47
  old_string: {
44
48
  type: 'string',
@@ -53,10 +57,15 @@ Returns a diff of the changes made, plus any LSP errors detected.`,
53
57
  description: 'Replace all occurrences of old_string (default: false)',
54
58
  },
55
59
  },
56
- required: ['file_path', 'old_string', 'new_string'],
60
+ // file_path is intentionally not in `required` so the `path` alias is accepted by
61
+ // provider-side validation; it is validated in execute() below.
62
+ required: ['old_string', 'new_string'],
57
63
  },
58
64
  async execute(args, ctx) {
59
- const rawPath = args.file_path;
65
+ const rawPath = (args.file_path ?? args.path);
66
+ if (typeof rawPath !== 'string' || rawPath.trim() === '') {
67
+ return 'Error: file_path is required — provide the absolute path to the file to edit.';
68
+ }
60
69
  const filePath = path.isAbsolute(rawPath) ? rawPath : path.resolve(ctx.workspaceDir, rawPath);
61
70
  const oldString = args.old_string;
62
71
  const newString = args.new_string;