opencode-swarm 7.136.1 → 7.136.3

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 (112) hide show
  1. package/.opencode/skills/brainstorm/SKILL.md +1 -1
  2. package/.opencode/skills/clarify/SKILL.md +3 -3
  3. package/.opencode/skills/clarify-spec/SKILL.md +2 -2
  4. package/.opencode/skills/consult/SKILL.md +1 -1
  5. package/.opencode/skills/council/SKILL.md +1 -1
  6. package/.opencode/skills/critic-gate/SKILL.md +8 -3
  7. package/.opencode/skills/discover/SKILL.md +1 -1
  8. package/.opencode/skills/execute/SKILL.md +1 -1
  9. package/.opencode/skills/gate-attribution/SKILL.md +1 -1
  10. package/.opencode/skills/issue-ingest/SKILL.md +3 -3
  11. package/.opencode/skills/phase-wrap/SKILL.md +1 -1
  12. package/.opencode/skills/plan/SKILL.md +3 -3
  13. package/.opencode/skills/pre-phase-briefing/SKILL.md +1 -1
  14. package/.opencode/skills/resume/SKILL.md +1 -1
  15. package/.opencode/skills/specify/SKILL.md +1 -1
  16. package/.opencode/skills/swarm-pr-feedback/SKILL.md +41 -1
  17. package/.opencode/skills/swarm-pr-review/SKILL.md +72 -24
  18. package/README.md +18 -0
  19. package/dist/background/workspace-snapshot.d.ts +54 -0
  20. package/dist/cli/{config-doctor-6fphg5xm.js → config-doctor-g71mz50j.js} +2 -2
  21. package/dist/cli/{core-4z1s2ak1.js → core-jpjk2qvt.js} +1 -1
  22. package/dist/cli/{curation-policy-m24jbyag.js → curation-policy-zhyttjpa.js} +6 -6
  23. package/dist/cli/{curator-zrt5sqj2.js → curator-36dxw38r.js} +27 -24
  24. package/dist/cli/{curator-llm-factory-w5arq7tr.js → curator-llm-factory-k2a7cv41.js} +27 -24
  25. package/dist/cli/{evidence-summary-service-w006jnpg.js → evidence-summary-service-nse06dqr.js} +8 -6
  26. package/dist/cli/{gate-evidence-aenyz6vt.js → gate-evidence-kdygjr56.js} +4 -4
  27. package/dist/cli/{guardrail-explain-462jwhbh.js → guardrail-explain-1k337z5d.js} +28 -25
  28. package/dist/cli/{guardrail-log-ax21jd9e.js → guardrail-log-xbc9bvdt.js} +3 -3
  29. package/dist/cli/hashing-mjwn6j4y.js +34 -0
  30. package/dist/cli/{hive-promoter-2vacnbws.js → hive-promoter-daxrn62p.js} +27 -24
  31. package/dist/cli/{index-r9v98d0y.js → index-0vq9gnqd.js} +46 -5
  32. package/dist/cli/{index-jwrydqjp.js → index-176zwqcq.js} +2 -2
  33. package/dist/cli/{index-cggqh2dz.js → index-45y0y3xh.js} +57 -1000
  34. package/dist/cli/{index-gf7hqbmz.js → index-68tjkaqk.js} +5 -1
  35. package/dist/cli/{index-dyy3hvk3.js → index-79pgzj9a.js} +4 -4
  36. package/dist/cli/{index-45t7w06b.js → index-7t3vjw5e.js} +1 -1
  37. package/dist/cli/{index-ey29aap6.js → index-b3jfcptk.js} +1 -1
  38. package/dist/cli/{index-prnjt2jr.js → index-brtg922w.js} +4576 -2316
  39. package/dist/cli/{index-7a2hm51h.js → index-cze4bq1x.js} +41 -0
  40. package/dist/cli/{index-mrtms113.js → index-d2sf9an1.js} +2 -2
  41. package/dist/cli/{index-wxyxf0bd.js → index-d61h3f3h.js} +2 -2
  42. package/dist/cli/{index-rhgctfn3.js → index-g2kpqpvh.js} +29 -26
  43. package/dist/cli/{index-g7g4hqh3.js → index-g5rtcb1n.js} +1 -1
  44. package/dist/cli/{index-1sq47n6t.js → index-gj5jerzz.js} +12 -6
  45. package/dist/cli/{index-c9ddxv4k.js → index-hh34tyv7.js} +1 -1
  46. package/dist/cli/{index-h3bweb88.js → index-kp5h245k.js} +5 -5
  47. package/dist/cli/{index-tcn457d5.js → index-kym0cctr.js} +1 -1
  48. package/dist/cli/{index-kzc2ygea.js → index-mgbpbs1f.js} +3 -3
  49. package/dist/cli/{index-tfa40hwb.js → index-n89fdxwg.js} +19 -2
  50. package/dist/cli/index-ne28wyyc.js +783 -0
  51. package/dist/cli/index-nfm9f10v.js +1100 -0
  52. package/dist/cli/{index-zkddc5ye.js → index-pff46kfv.js} +2 -2
  53. package/dist/cli/{index-f4cmtd89.js → index-rtgqy0yg.js} +2 -2
  54. package/dist/cli/{index-09b9zncg.js → index-tncy55bp.js} +1 -1
  55. package/dist/cli/{index-vg3yx648.js → index-w6j1n5az.js} +1 -1
  56. package/dist/cli/{index-hzkvbycn.js → index-wbdxmf1a.js} +6 -6
  57. package/dist/cli/{index-y6a7gjtj.js → index-wsdnttf4.js} +228 -81
  58. package/dist/cli/index-y8552snf.js +264 -0
  59. package/dist/cli/{index-9gxp450h.js → index-zcrvn579.js} +2 -2
  60. package/dist/cli/{index-p1pqqwgp.js → index-zwh5dewz.js} +1 -1
  61. package/dist/cli/{index-dzyjb33e.js → index-zzhyws9g.js} +1 -1
  62. package/dist/cli/index.js +27 -24
  63. package/dist/cli/{knowledge-escalator-1zt8qy1c.js → knowledge-escalator-h7fspgph.js} +7 -7
  64. package/dist/cli/{knowledge-events-ej3s9tsm.js → knowledge-events-vkf7an5n.js} +5 -5
  65. package/dist/cli/{knowledge-link-mm1w967j.js → knowledge-link-zr40rnwr.js} +4 -4
  66. package/dist/cli/{knowledge-store-ey4cbkp4.js → knowledge-store-s4976v9c.js} +5 -5
  67. package/dist/cli/{knowledge-validator-zwmq7s2c.js → knowledge-validator-64ppqy4y.js} +8 -8
  68. package/dist/cli/{pending-delegations-qajsxct0.js → pending-delegations-0h5b18p7.js} +3 -3
  69. package/dist/cli/{pr-subscriptions-qhr41epq.js → pr-subscriptions-jn0h047q.js} +3 -3
  70. package/dist/cli/runner-deeswadt.js +21 -0
  71. package/dist/cli/{scan-cursor-809hf2n1.js → scan-cursor-xbkae12h.js} +6 -6
  72. package/dist/cli/{schema-mhd7xqwr.js → schema-7jm70cab.js} +5 -1
  73. package/dist/cli/{scope-persistence-h2fpgxww.js → scope-persistence-5xc9ntdh.js} +4 -4
  74. package/dist/cli/{skill-generator-yyc8zd9j.js → skill-generator-8gtq1ajr.js} +9 -9
  75. package/dist/cli/{telemetry-859khp82.js → telemetry-6678gya0.js} +1 -1
  76. package/dist/cli/{workspace-snapshot-jmyamqnv.js → workspace-snapshot-h5rzw37b.js} +5 -1
  77. package/dist/cli/{worktree-collision-ownership-13btcj9g.js → worktree-collision-ownership-wt7cc850.js} +3 -3
  78. package/dist/commands/registry.d.ts +72 -0
  79. package/dist/commands/skill-opt.d.ts +41 -0
  80. package/dist/config/schema.d.ts +57 -0
  81. package/dist/hooks/delegation-gate.d.ts +12 -1
  82. package/dist/hooks/gate-denial-tracker.d.ts +175 -0
  83. package/dist/hooks/guardrails/execution-episode.d.ts +41 -0
  84. package/dist/hooks/guardrails/execution-stall.d.ts +285 -0
  85. package/dist/hooks/guardrails/file-authority.d.ts +11 -2
  86. package/dist/hooks/guardrails/internals-guard.d.ts +117 -0
  87. package/dist/hooks/guardrails/messages-transform.d.ts +85 -0
  88. package/dist/hooks/pr-workflow-gate.d.ts +261 -4
  89. package/dist/hooks/pr-workflow-response-gate.d.ts +27 -9
  90. package/dist/hooks/trajectory-logger.d.ts +76 -0
  91. package/dist/hooks/write-target-resolver.d.ts +19 -0
  92. package/dist/index.js +511 -462
  93. package/dist/memory/schema.d.ts +3 -3
  94. package/dist/prm/index.d.ts +2 -0
  95. package/dist/services/skill-evaluator.d.ts +17 -0
  96. package/dist/services/skill-optimizer/activation.d.ts +58 -0
  97. package/dist/services/skill-optimizer/candidates.d.ts +88 -0
  98. package/dist/services/skill-optimizer/controller.d.ts +146 -0
  99. package/dist/services/skill-optimizer/deterministic-seed.d.ts +29 -0
  100. package/dist/services/skill-optimizer/lifecycle.d.ts +70 -0
  101. package/dist/services/skill-optimizer/promoted-external-staleness.d.ts +98 -0
  102. package/dist/services/skill-optimizer/retirement.d.ts +54 -0
  103. package/dist/services/skill-optimizer/skill-eval-tasks.d.ts +47 -0
  104. package/dist/services/skill-optimizer/smoke.d.ts +46 -0
  105. package/dist/services/skill-optimizer/store.d.ts +118 -0
  106. package/dist/state.d.ts +32 -0
  107. package/dist/telemetry.d.ts +66 -1
  108. package/dist/tools/write-pr-review-trigger-eval.d.ts +2 -1
  109. package/dist/types/events.d.ts +14 -1
  110. package/dist/utils/stable-stringify.d.ts +46 -0
  111. package/evaluation-fixtures/skill-eval/scoring/score-skill-eval.cjs +97 -0
  112. package/package.json +2 -1
@@ -0,0 +1,1100 @@
1
+ // @bun
2
+ import {
3
+ CostRecordSchema,
4
+ EvaluationCandidateV1Schema,
5
+ EvaluationRunV1Schema,
6
+ EvaluationTaskV1Schema,
7
+ admitEvaluationTask,
8
+ claimHeldOutTest,
9
+ readEvaluationRun,
10
+ saveEvaluationRun,
11
+ saveTaskSetSnapshot
12
+ } from "./index-ne28wyyc.js";
13
+ import {
14
+ canonicalHash,
15
+ computeCandidateInputContentHash,
16
+ computeRunIntegrityHash,
17
+ computeTaskInputContentHash,
18
+ computeTaskSetContentHash,
19
+ resolveContainedExistingPath
20
+ } from "./index-y8552snf.js";
21
+ import {
22
+ exports_external,
23
+ init_zod
24
+ } from "./index-bpmtbmy9.js";
25
+ import {
26
+ bunSpawn
27
+ } from "./index-y111zefa.js";
28
+
29
+ // src/evaluation/runner.ts
30
+ import * as fs3 from "fs";
31
+ import * as path3 from "path";
32
+
33
+ // node_modules/yocto-queue/index.js
34
+ class Node {
35
+ value;
36
+ next;
37
+ constructor(value) {
38
+ this.value = value;
39
+ }
40
+ }
41
+
42
+ class Queue {
43
+ #head;
44
+ #tail;
45
+ #size;
46
+ constructor() {
47
+ this.clear();
48
+ }
49
+ enqueue(value) {
50
+ const node = new Node(value);
51
+ if (this.#head) {
52
+ this.#tail.next = node;
53
+ this.#tail = node;
54
+ } else {
55
+ this.#head = node;
56
+ this.#tail = node;
57
+ }
58
+ this.#size++;
59
+ }
60
+ dequeue() {
61
+ const current = this.#head;
62
+ if (!current) {
63
+ return;
64
+ }
65
+ this.#head = this.#head.next;
66
+ this.#size--;
67
+ if (!this.#head) {
68
+ this.#tail = undefined;
69
+ }
70
+ return current.value;
71
+ }
72
+ peek() {
73
+ if (!this.#head) {
74
+ return;
75
+ }
76
+ return this.#head.value;
77
+ }
78
+ clear() {
79
+ this.#head = undefined;
80
+ this.#tail = undefined;
81
+ this.#size = 0;
82
+ }
83
+ get size() {
84
+ return this.#size;
85
+ }
86
+ *[Symbol.iterator]() {
87
+ let current = this.#head;
88
+ while (current) {
89
+ yield current.value;
90
+ current = current.next;
91
+ }
92
+ }
93
+ *drain() {
94
+ while (this.#head) {
95
+ yield this.dequeue();
96
+ }
97
+ }
98
+ }
99
+
100
+ // node_modules/p-limit/index.js
101
+ function pLimit(concurrency) {
102
+ let rejectOnClear = false;
103
+ if (typeof concurrency === "object") {
104
+ ({ concurrency, rejectOnClear = false } = concurrency);
105
+ }
106
+ validateConcurrency(concurrency);
107
+ if (typeof rejectOnClear !== "boolean") {
108
+ throw new TypeError("Expected `rejectOnClear` to be a boolean");
109
+ }
110
+ const queue = new Queue;
111
+ let activeCount = 0;
112
+ const resumeNext = () => {
113
+ if (activeCount < concurrency && queue.size > 0) {
114
+ activeCount++;
115
+ queue.dequeue().run();
116
+ }
117
+ };
118
+ const next = () => {
119
+ activeCount--;
120
+ resumeNext();
121
+ };
122
+ const run = async (function_, resolve, arguments_) => {
123
+ const result = (async () => function_(...arguments_))();
124
+ resolve(result);
125
+ try {
126
+ await result;
127
+ } catch {}
128
+ next();
129
+ };
130
+ const enqueue = (function_, resolve, reject, arguments_) => {
131
+ const queueItem = { reject };
132
+ new Promise((internalResolve) => {
133
+ queueItem.run = internalResolve;
134
+ queue.enqueue(queueItem);
135
+ }).then(run.bind(undefined, function_, resolve, arguments_));
136
+ if (activeCount < concurrency) {
137
+ resumeNext();
138
+ }
139
+ };
140
+ const generator = (function_, ...arguments_) => new Promise((resolve, reject) => {
141
+ enqueue(function_, resolve, reject, arguments_);
142
+ });
143
+ Object.defineProperties(generator, {
144
+ activeCount: {
145
+ get: () => activeCount
146
+ },
147
+ pendingCount: {
148
+ get: () => queue.size
149
+ },
150
+ clearQueue: {
151
+ value() {
152
+ if (!rejectOnClear) {
153
+ queue.clear();
154
+ return;
155
+ }
156
+ const abortError = AbortSignal.abort().reason;
157
+ while (queue.size > 0) {
158
+ queue.dequeue().reject(abortError);
159
+ }
160
+ }
161
+ },
162
+ concurrency: {
163
+ get: () => concurrency,
164
+ set(newConcurrency) {
165
+ validateConcurrency(newConcurrency);
166
+ concurrency = newConcurrency;
167
+ queueMicrotask(() => {
168
+ while (activeCount < concurrency && queue.size > 0) {
169
+ resumeNext();
170
+ }
171
+ });
172
+ }
173
+ },
174
+ map: {
175
+ async value(iterable, function_) {
176
+ const promises = Array.from(iterable, (value, index) => this(function_, value, index));
177
+ return Promise.all(promises);
178
+ }
179
+ }
180
+ });
181
+ return generator;
182
+ }
183
+ function validateConcurrency(concurrency) {
184
+ if (!((Number.isInteger(concurrency) || concurrency === Number.POSITIVE_INFINITY) && concurrency > 0)) {
185
+ throw new TypeError("Expected `concurrency` to be a number from 1 and up");
186
+ }
187
+ }
188
+
189
+ // src/evaluation/runner.ts
190
+ init_zod();
191
+
192
+ // src/utils/external-tool-runner.ts
193
+ import * as fs from "fs";
194
+ import * as path from "path";
195
+ var DEFAULT_WINDOWS_EXTENSIONS = [".exe", ".cmd", ".bat"];
196
+ function isExecutableFile(candidate) {
197
+ try {
198
+ const stats = fs.statSync(candidate);
199
+ return stats.isFile();
200
+ } catch {
201
+ return false;
202
+ }
203
+ }
204
+ function resolveExecutableFromPath(names, envPath = process.env.PATH ?? "", platform = process.platform) {
205
+ const pathEntries = envPath.split(path.delimiter).filter(Boolean);
206
+ const isWindows = platform === "win32";
207
+ for (const rawName of names) {
208
+ if (!rawName)
209
+ continue;
210
+ if (path.isAbsolute(rawName) && isExecutableFile(rawName)) {
211
+ return rawName;
212
+ }
213
+ const extensions = isWindows && path.extname(rawName) === "" ? DEFAULT_WINDOWS_EXTENSIONS : [""];
214
+ for (const dir of pathEntries) {
215
+ for (const ext of extensions) {
216
+ const candidate = path.join(dir, `${rawName}${ext}`);
217
+ if (isExecutableFile(candidate)) {
218
+ return candidate;
219
+ }
220
+ }
221
+ }
222
+ }
223
+ return null;
224
+ }
225
+ async function readBoundedStream(stream, maxBytes) {
226
+ const reader = stream.getReader();
227
+ let lockReleased = false;
228
+ const releaseLock = () => {
229
+ if (lockReleased)
230
+ return;
231
+ lockReleased = true;
232
+ try {
233
+ reader.releaseLock();
234
+ } catch {}
235
+ };
236
+ const cancelAndRelease = async () => {
237
+ try {
238
+ await reader.cancel();
239
+ } catch {} finally {
240
+ releaseLock();
241
+ }
242
+ };
243
+ if (maxBytes <= 0) {
244
+ await cancelAndRelease();
245
+ return { text: "", truncated: true };
246
+ }
247
+ const chunks = [];
248
+ let total = 0;
249
+ let truncated = false;
250
+ try {
251
+ for (;; ) {
252
+ const { done, value } = await reader.read();
253
+ if (done)
254
+ break;
255
+ if (!value)
256
+ continue;
257
+ const remaining = maxBytes - total;
258
+ if (value.byteLength > remaining) {
259
+ if (remaining > 0) {
260
+ chunks.push(value.slice(0, remaining));
261
+ total += remaining;
262
+ }
263
+ truncated = true;
264
+ await cancelAndRelease();
265
+ break;
266
+ }
267
+ chunks.push(value);
268
+ total += value.byteLength;
269
+ }
270
+ } finally {
271
+ releaseLock();
272
+ }
273
+ const out = new Uint8Array(total);
274
+ let offset = 0;
275
+ for (const chunk of chunks) {
276
+ out.set(chunk, offset);
277
+ offset += chunk.byteLength;
278
+ }
279
+ return {
280
+ text: new TextDecoder().decode(out),
281
+ truncated
282
+ };
283
+ }
284
+ function timeoutKillSignal(platform) {
285
+ return platform === "win32" ? "SIGTERM" : "SIGKILL";
286
+ }
287
+ function killProcess(proc) {
288
+ try {
289
+ proc?.kill(timeoutKillSignal(_internals.platform()));
290
+ } catch {}
291
+ }
292
+ async function runExternalTool(options) {
293
+ if (!path.isAbsolute(options.cwd)) {
294
+ return {
295
+ status: "spawn-error",
296
+ exitCode: null,
297
+ stdout: "",
298
+ stderr: "",
299
+ stdoutTruncated: false,
300
+ stderrTruncated: false,
301
+ message: "external tool cwd must be absolute"
302
+ };
303
+ }
304
+ if (options.abortSignal?.aborted) {
305
+ return {
306
+ status: "cancelled",
307
+ exitCode: null,
308
+ stdout: "",
309
+ stderr: "",
310
+ stdoutTruncated: false,
311
+ stderrTruncated: false,
312
+ message: "external tool execution cancelled before spawn"
313
+ };
314
+ }
315
+ let proc;
316
+ let timeoutHandle;
317
+ let abortListener;
318
+ let exitSettled = false;
319
+ let settledExitCode = null;
320
+ try {
321
+ proc = _internals.bunSpawn([options.executable, ...options.args], {
322
+ cwd: options.cwd,
323
+ env: options.env,
324
+ stdin: "ignore",
325
+ stdout: "pipe",
326
+ stderr: "pipe",
327
+ timeout: options.timeoutMs
328
+ });
329
+ const exitPromise = proc.exited.then((exitCode) => {
330
+ exitSettled = true;
331
+ settledExitCode = exitCode;
332
+ return { kind: "exit", exitCode };
333
+ });
334
+ const timeout = new Promise((resolve) => {
335
+ timeoutHandle = setTimeout(() => {
336
+ if (exitSettled) {
337
+ resolve({
338
+ kind: "exit",
339
+ exitCode: settledExitCode ?? proc?.exitCode ?? 0
340
+ });
341
+ return;
342
+ }
343
+ killProcess(proc);
344
+ resolve({ kind: "timeout" });
345
+ }, options.timeoutMs);
346
+ });
347
+ const cancelled = new Promise((resolve) => {
348
+ if (!options.abortSignal)
349
+ return;
350
+ let handled = false;
351
+ abortListener = () => {
352
+ if (handled)
353
+ return;
354
+ handled = true;
355
+ killProcess(proc);
356
+ resolve({ kind: "cancelled" });
357
+ };
358
+ options.abortSignal.addEventListener("abort", abortListener, {
359
+ once: true
360
+ });
361
+ if (options.abortSignal.aborted)
362
+ abortListener();
363
+ });
364
+ const stdoutPromise = readBoundedStream(proc.stdout, options.maxStdoutBytes);
365
+ const stderrPromise = readBoundedStream(proc.stderr, options.maxStderrBytes);
366
+ const exitResult = await Promise.race([exitPromise, timeout, cancelled]);
367
+ if (exitResult.kind === "timeout") {
368
+ return {
369
+ status: "timeout",
370
+ exitCode: proc.exitCode,
371
+ stdout: "",
372
+ stderr: "",
373
+ stdoutTruncated: false,
374
+ stderrTruncated: false
375
+ };
376
+ }
377
+ if (exitResult.kind === "cancelled") {
378
+ return {
379
+ status: "cancelled",
380
+ exitCode: proc.exitCode,
381
+ stdout: "",
382
+ stderr: "",
383
+ stdoutTruncated: false,
384
+ stderrTruncated: false,
385
+ message: "external tool execution cancelled"
386
+ };
387
+ }
388
+ const [stdout, stderr] = await Promise.all([stdoutPromise, stderrPromise]);
389
+ return {
390
+ status: "completed",
391
+ exitCode: exitResult.exitCode,
392
+ stdout: stdout.text,
393
+ stderr: stderr.text,
394
+ stdoutTruncated: stdout.truncated,
395
+ stderrTruncated: stderr.truncated
396
+ };
397
+ } catch (err) {
398
+ return {
399
+ status: "spawn-error",
400
+ exitCode: proc?.exitCode ?? null,
401
+ stdout: "",
402
+ stderr: "",
403
+ stdoutTruncated: false,
404
+ stderrTruncated: false,
405
+ message: err instanceof Error ? err.message : String(err)
406
+ };
407
+ } finally {
408
+ if (timeoutHandle !== undefined)
409
+ clearTimeout(timeoutHandle);
410
+ if (abortListener && options.abortSignal) {
411
+ options.abortSignal.removeEventListener("abort", abortListener);
412
+ }
413
+ if (proc) {
414
+ try {
415
+ proc.kill();
416
+ } catch {}
417
+ }
418
+ }
419
+ }
420
+ var _internals = {
421
+ bunSpawn,
422
+ platform: () => process.platform
423
+ };
424
+
425
+ // src/evaluation/disposable-worktree.ts
426
+ import * as crypto from "crypto";
427
+ import * as fs2 from "fs";
428
+ import * as os from "os";
429
+ import * as path2 from "path";
430
+ var GIT_TIMEOUT_MS = 30000;
431
+ var MAX_GIT_OUTPUT_BYTES = 4 * 1024 * 1024;
432
+ var WORKTREE_PARENT = "opencode-swarm-evaluation";
433
+
434
+ class DisposableWorktreeCleanupError extends Error {
435
+ worktreePath;
436
+ pathPresent;
437
+ registrationPresent;
438
+ cleanupErrors;
439
+ code = "evaluation-worktree-cleanup-failed";
440
+ constructor(worktreePath, pathPresent, registrationPresent, cleanupErrors) {
441
+ super(`Evaluation worktree cleanup could not be verified for ${worktreePath} ` + `(pathPresent=${pathPresent}, registrationPresent=${String(registrationPresent)})`);
442
+ this.worktreePath = worktreePath;
443
+ this.pathPresent = pathPresent;
444
+ this.registrationPresent = registrationPresent;
445
+ this.cleanupErrors = cleanupErrors;
446
+ this.name = "DisposableWorktreeCleanupError";
447
+ }
448
+ }
449
+ async function git(projectRoot, args, abortSignal) {
450
+ const executable = _internals2.resolveExecutableFromPath(["git"]);
451
+ if (!executable)
452
+ throw new Error("git executable not found");
453
+ const result = await _internals2.runExternalTool({
454
+ executable,
455
+ args: ["-C", projectRoot, ...args],
456
+ cwd: projectRoot,
457
+ timeoutMs: GIT_TIMEOUT_MS,
458
+ maxStdoutBytes: MAX_GIT_OUTPUT_BYTES,
459
+ maxStderrBytes: MAX_GIT_OUTPUT_BYTES,
460
+ abortSignal
461
+ });
462
+ if (result.status !== "completed" || result.exitCode !== 0) {
463
+ throw new Error(`git ${args[0] ?? ""} failed: ${result.message ?? result.stderr.trim() ?? result.status}`);
464
+ }
465
+ return { stdout: result.stdout, stderr: result.stderr };
466
+ }
467
+ async function captureWorkingTreeFingerprint(projectRoot, abortSignal) {
468
+ const canonicalRoot = fs2.realpathSync(projectRoot);
469
+ const head = await git(canonicalRoot, ["rev-parse", "HEAD"], abortSignal);
470
+ const porcelain = await git(canonicalRoot, [
471
+ "status",
472
+ "--porcelain=v1",
473
+ "-z",
474
+ "--untracked-files=all",
475
+ "--",
476
+ ".",
477
+ ":(exclude).swarm/**"
478
+ ], abortSignal);
479
+ return {
480
+ head: head.stdout.trim(),
481
+ porcelainHash: crypto.createHash("sha256").update(porcelain.stdout).digest("hex")
482
+ };
483
+ }
484
+ async function createDisposableWorktree(projectRoot, baseRef, abortSignal) {
485
+ const canonicalRoot = fs2.realpathSync(projectRoot);
486
+ if (!/^[A-Fa-f0-9]{40,64}$/.test(baseRef)) {
487
+ throw new Error("Evaluation baseRef must be a full commit hash");
488
+ }
489
+ const parent = path2.join(_internals2.tmpdir(), WORKTREE_PARENT);
490
+ fs2.mkdirSync(parent, { recursive: true });
491
+ const canonicalParent = fs2.realpathSync(parent);
492
+ const worktreePath = path2.join(canonicalParent, crypto.randomUUID());
493
+ await git(canonicalRoot, ["worktree", "add", "--detach", worktreePath, baseRef], abortSignal);
494
+ return { path: fs2.realpathSync(worktreePath), baseSha: baseRef };
495
+ }
496
+ async function removeDisposableWorktree(projectRoot, worktreePath) {
497
+ const canonicalRoot = fs2.realpathSync(projectRoot);
498
+ const expectedParent = fs2.realpathSync(path2.join(_internals2.tmpdir(), WORKTREE_PARENT));
499
+ const resolved = path2.resolve(worktreePath);
500
+ const relative2 = path2.relative(expectedParent, resolved);
501
+ if (!relative2 || relative2 === ".." || relative2.startsWith(`..${path2.sep}`) || path2.isAbsolute(relative2)) {
502
+ throw new Error("Refusing to remove a worktree outside the evaluation root");
503
+ }
504
+ const cleanupErrors = [];
505
+ try {
506
+ await git(canonicalRoot, ["worktree", "remove", "--force", resolved]);
507
+ } catch (error) {
508
+ cleanupErrors.push(`git-remove: ${error instanceof Error ? error.message : String(error)}`);
509
+ }
510
+ try {
511
+ _internals2.rmSync(resolved, { recursive: true, force: true });
512
+ } catch (error) {
513
+ cleanupErrors.push(`filesystem-remove: ${error instanceof Error ? error.message : String(error)}`);
514
+ }
515
+ try {
516
+ await git(canonicalRoot, ["worktree", "prune", "--expire", "now"]);
517
+ } catch (error) {
518
+ cleanupErrors.push(`git-prune: ${error instanceof Error ? error.message : String(error)}`);
519
+ }
520
+ const pathPresent = _internals2.existsSync(resolved);
521
+ let registrationPresent;
522
+ try {
523
+ const listed = await git(canonicalRoot, [
524
+ "worktree",
525
+ "list",
526
+ "--porcelain",
527
+ "-z"
528
+ ]);
529
+ const normalizedTarget = normalizeWorktreePath(resolved);
530
+ registrationPresent = listed.stdout.split("\x00").filter((field) => field.startsWith("worktree ")).some((field) => normalizeWorktreePath(field.slice("worktree ".length)) === normalizedTarget);
531
+ } catch (error) {
532
+ cleanupErrors.push(`git-list: ${error instanceof Error ? error.message : String(error)}`);
533
+ }
534
+ if (pathPresent || registrationPresent !== false) {
535
+ throw new DisposableWorktreeCleanupError(resolved, pathPresent, registrationPresent, cleanupErrors);
536
+ }
537
+ }
538
+ function normalizeWorktreePath(value) {
539
+ const resolved = path2.resolve(value);
540
+ return process.platform === "win32" ? resolved.toLowerCase() : resolved;
541
+ }
542
+ async function withDisposableWorktree(args) {
543
+ const before = await _internals2.captureWorkingTreeFingerprint(args.projectRoot, args.abortSignal);
544
+ let worktree;
545
+ try {
546
+ worktree = await _internals2.createDisposableWorktree(args.projectRoot, args.baseRef, args.abortSignal);
547
+ return await args.run(worktree);
548
+ } finally {
549
+ let cleanupFailure;
550
+ if (worktree) {
551
+ try {
552
+ await _internals2.removeDisposableWorktree(args.projectRoot, worktree.path);
553
+ } catch (error) {
554
+ cleanupFailure = error;
555
+ }
556
+ }
557
+ const after = await _internals2.captureWorkingTreeFingerprint(args.projectRoot);
558
+ if (before.head !== after.head || before.porcelainHash !== after.porcelainHash) {
559
+ throw new Error("Evaluation changed the active working tree");
560
+ }
561
+ if (cleanupFailure) {
562
+ throw cleanupFailure;
563
+ }
564
+ }
565
+ }
566
+ var _internals2 = {
567
+ runExternalTool,
568
+ resolveExecutableFromPath,
569
+ tmpdir: os.tmpdir,
570
+ rmSync: fs2.rmSync,
571
+ existsSync: fs2.existsSync,
572
+ captureWorkingTreeFingerprint,
573
+ createDisposableWorktree,
574
+ removeDisposableWorktree
575
+ };
576
+
577
+ // src/evaluation/runner.ts
578
+ var ScorerOutputSchema = exports_external.object({
579
+ v: exports_external.literal(1),
580
+ score: exports_external.number().finite(),
581
+ cost: CostRecordSchema,
582
+ metadata: exports_external.record(exports_external.string(), exports_external.unknown()).optional()
583
+ }).strict();
584
+ var BuiltinVerdictSchema = exports_external.object({
585
+ v: exports_external.literal(1),
586
+ caught: exports_external.boolean(),
587
+ reason: exports_external.string().optional()
588
+ }).strict();
589
+
590
+ class ScorerFailure extends Error {
591
+ outcome;
592
+ failureCode;
593
+ retryable;
594
+ constructor(outcome, failureCode, retryable) {
595
+ super(failureCode);
596
+ this.outcome = outcome;
597
+ this.failureCode = failureCode;
598
+ this.retryable = retryable;
599
+ this.name = "ScorerFailure";
600
+ }
601
+ }
602
+ function aggregateCost(results) {
603
+ if (results.some((result) => result.cost.source === "unavailable")) {
604
+ return { source: "unavailable" };
605
+ }
606
+ return {
607
+ source: results.some((result) => result.cost.source === "estimated") ? "estimated" : "reported",
608
+ usd: results.reduce((sum, result) => sum + (result.cost.usd ?? 0), 0)
609
+ };
610
+ }
611
+ function failureResult(args) {
612
+ return {
613
+ v: 1,
614
+ taskId: args.task.id,
615
+ category: args.task.category,
616
+ protected: args.task.protected,
617
+ repetition: args.repetition,
618
+ candidateId: args.candidate.id,
619
+ seed: args.seed,
620
+ outcome: args.outcome,
621
+ scoreRange: args.task.scorer.scoreRange,
622
+ cost: args.cost ?? { source: "unavailable" },
623
+ durationMs: args.durationMs,
624
+ retries: 0,
625
+ failureCode: args.failureCode
626
+ };
627
+ }
628
+ function parseBuiltinScore(text) {
629
+ let value;
630
+ try {
631
+ value = JSON.parse(text.trim());
632
+ } catch {
633
+ throw new ScorerFailure("malformed", "builtin-invalid-json", false);
634
+ }
635
+ const parsed = BuiltinVerdictSchema.safeParse(value);
636
+ if (!parsed.success) {
637
+ throw new ScorerFailure("malformed", "builtin-schema-invalid", false);
638
+ }
639
+ const verdict = parsed.data;
640
+ return {
641
+ v: 1,
642
+ score: verdict.caught ? 1 : 0,
643
+ cost: { source: "unavailable" },
644
+ metadata: verdict.reason ? { reason: verdict.reason } : undefined
645
+ };
646
+ }
647
+ async function scoreExecution(args) {
648
+ if (args.task.scorer.kind === "builtin")
649
+ return parseBuiltinScore(args.execution.text);
650
+ const sourceEnvironment = resolveContainedExistingPath(args.inputRoot, args.task.environment.path);
651
+ const sourceExecutable = resolveContainedExistingPath(args.inputRoot, args.task.scorer.argv[0]);
652
+ const scorerRelative = path3.relative(sourceEnvironment, sourceExecutable);
653
+ if (!scorerRelative || scorerRelative === ".." || scorerRelative.startsWith(`..${path3.sep}`) || path3.isAbsolute(scorerRelative)) {
654
+ throw new ScorerFailure("malformed", "scorer-outside-task-package", false);
655
+ }
656
+ const isolatedExecutable = resolveContainedExistingPath(args.isolatedRoot, scorerRelative);
657
+ if (!fs3.statSync(isolatedExecutable).isFile()) {
658
+ throw new ScorerFailure("malformed", "scorer-not-a-file", false);
659
+ }
660
+ const extension = path3.extname(isolatedExecutable).toLowerCase();
661
+ const isJavaScript = [".cjs", ".js", ".mjs"].includes(extension);
662
+ const executable = isJavaScript ? process.execPath : isolatedExecutable;
663
+ const scorerArgs = isJavaScript ? [isolatedExecutable, ...args.task.scorer.argv.slice(1)] : args.task.scorer.argv.slice(1);
664
+ const artifactDirectory = path3.join(args.isolatedRoot, ".artifacts");
665
+ fs3.mkdirSync(artifactDirectory, { recursive: true });
666
+ fs3.writeFileSync(path3.join(artifactDirectory, "model-output.json"), JSON.stringify({ v: 1, text: args.execution.text }));
667
+ const result = await _internals3.runExternalTool({
668
+ executable,
669
+ args: scorerArgs,
670
+ cwd: args.isolatedRoot,
671
+ timeoutMs: Math.min(args.task.scorer.timeoutMs, args.timeoutMs),
672
+ maxStdoutBytes: args.maxOutputBytes,
673
+ maxStderrBytes: args.maxOutputBytes,
674
+ abortSignal: args.abortSignal,
675
+ env: {
676
+ ...process.env,
677
+ SWARM_EVAL_TASK_ID: args.task.id,
678
+ SWARM_EVAL_CANDIDATE_ID: args.candidate.id,
679
+ SWARM_EVAL_ARTIFACT_DIR: artifactDirectory,
680
+ SWARM_EVAL_SEED: args.seed
681
+ }
682
+ });
683
+ if (result.status === "timeout")
684
+ throw new ScorerFailure("timeout", "scorer-timeout", false);
685
+ if (result.status === "cancelled")
686
+ throw new ScorerFailure("cancelled", "scorer-cancelled", false);
687
+ if (result.status === "spawn-error")
688
+ throw new ScorerFailure("infrastructure_failure", "scorer-spawn-failure", true);
689
+ if (result.exitCode !== 0) {
690
+ throw new ScorerFailure("infrastructure_failure", `scorer-exit-${result.exitCode ?? "none"}`, true);
691
+ }
692
+ if (result.stdoutTruncated || result.stderrTruncated) {
693
+ throw new ScorerFailure("malformed", "scorer-output-truncated", false);
694
+ }
695
+ let value;
696
+ try {
697
+ value = JSON.parse(result.stdout.trim());
698
+ } catch {
699
+ throw new ScorerFailure("malformed", "scorer-invalid-json", false);
700
+ }
701
+ const parsed = ScorerOutputSchema.safeParse(value);
702
+ if (!parsed.success) {
703
+ throw new ScorerFailure("malformed", "scorer-schema-invalid", false);
704
+ }
705
+ return parsed.data;
706
+ }
707
+ async function runExecutorWithinBudget(args) {
708
+ const startedAt = Date.now();
709
+ const controller = new AbortController;
710
+ let timedOut = false;
711
+ const parentAbort = () => controller.abort();
712
+ args.parentSignal.addEventListener("abort", parentAbort, { once: true });
713
+ if (args.parentSignal.aborted)
714
+ controller.abort();
715
+ const timer = setTimeout(() => {
716
+ timedOut = true;
717
+ controller.abort();
718
+ }, args.maxTimeMs);
719
+ try {
720
+ const aborted = new Promise((resolve2) => {
721
+ const finish = () => resolve2({
722
+ status: timedOut ? "timeout" : "cancelled",
723
+ text: "",
724
+ durationMs: Date.now() - startedAt,
725
+ cost: { source: "unavailable" },
726
+ error: timedOut ? "executor-task-timeout" : "executor-cancelled"
727
+ });
728
+ controller.signal.addEventListener("abort", finish, { once: true });
729
+ if (controller.signal.aborted)
730
+ finish();
731
+ });
732
+ return await Promise.race([
733
+ args.options.executor({
734
+ task: args.task,
735
+ candidate: args.candidate,
736
+ isolatedRoot: args.isolatedRoot,
737
+ instruction: args.instruction,
738
+ payload: args.payload,
739
+ seed: args.seed,
740
+ abortSignal: controller.signal
741
+ }),
742
+ aborted
743
+ ]);
744
+ } finally {
745
+ clearTimeout(timer);
746
+ args.parentSignal.removeEventListener("abort", parentAbort);
747
+ controller.abort();
748
+ }
749
+ }
750
+ async function executePairMember(args) {
751
+ const startedAt = Date.now();
752
+ const seed = canonicalHash(`${args.options.seed}:${args.task.id}:${args.candidate.id}:${args.repetition}`);
753
+ if (args.signal.aborted) {
754
+ return failureResult({
755
+ ...args,
756
+ seed,
757
+ outcome: "cancelled",
758
+ durationMs: 0,
759
+ failureCode: "cancelled-before-execution"
760
+ });
761
+ }
762
+ if (args.task.environment.kind === "container") {
763
+ return failureResult({
764
+ ...args,
765
+ seed,
766
+ outcome: "unsupported",
767
+ durationMs: 0,
768
+ failureCode: "container-runner-unavailable"
769
+ });
770
+ }
771
+ try {
772
+ return await _internals3.withDisposableWorktree({
773
+ projectRoot: args.options.projectRoot,
774
+ baseRef: args.baseSha,
775
+ abortSignal: args.signal,
776
+ run: async (worktree) => {
777
+ const isolatedRoot = path3.join(worktree.path, ".swarm-evaluation-task");
778
+ fs3.mkdirSync(isolatedRoot, { recursive: true });
779
+ try {
780
+ fs3.cpSync(resolveContainedExistingPath(args.inputRoot, args.task.environment.path), isolatedRoot, { recursive: true, errorOnExist: false });
781
+ const instruction = fs3.readFileSync(resolveContainedExistingPath(args.inputRoot, args.task.instructionPath), "utf8");
782
+ const payload = fs3.readFileSync(resolveContainedExistingPath(args.inputRoot, args.candidate.payloadPath), "utf8");
783
+ const remainingBeforeExecution = args.options.budgets.maxTaskTimeMs - (Date.now() - startedAt);
784
+ if (remainingBeforeExecution <= 0) {
785
+ return failureResult({
786
+ ...args,
787
+ seed,
788
+ outcome: "timeout",
789
+ durationMs: Date.now() - startedAt,
790
+ failureCode: "task-budget-exhausted-before-executor"
791
+ });
792
+ }
793
+ const execution = await runExecutorWithinBudget({
794
+ options: args.options,
795
+ task: args.task,
796
+ candidate: args.candidate,
797
+ isolatedRoot,
798
+ instruction,
799
+ payload,
800
+ seed,
801
+ parentSignal: args.signal,
802
+ maxTimeMs: remainingBeforeExecution
803
+ });
804
+ if (execution.status !== "completed") {
805
+ return failureResult({
806
+ ...args,
807
+ seed,
808
+ outcome: execution.status === "infrastructure_failure" ? "infrastructure_failure" : execution.status,
809
+ durationMs: execution.durationMs,
810
+ cost: execution.cost,
811
+ failureCode: execution.error ?? `executor-${execution.status}`
812
+ });
813
+ }
814
+ if (Buffer.byteLength(execution.text, "utf8") > args.options.budgets.maxOutputBytes) {
815
+ return failureResult({
816
+ ...args,
817
+ seed,
818
+ outcome: "malformed",
819
+ durationMs: Date.now() - startedAt,
820
+ cost: execution.cost,
821
+ failureCode: "executor-output-truncated"
822
+ });
823
+ }
824
+ let scored;
825
+ try {
826
+ const remainingBeforeScorer = args.options.budgets.maxTaskTimeMs - (Date.now() - startedAt);
827
+ if (remainingBeforeScorer <= 0) {
828
+ throw new ScorerFailure("timeout", "task-budget-exhausted-before-scorer", false);
829
+ }
830
+ scored = await scoreExecution({
831
+ task: args.task,
832
+ candidate: args.candidate,
833
+ inputRoot: args.inputRoot,
834
+ isolatedRoot,
835
+ execution,
836
+ seed,
837
+ maxOutputBytes: args.options.budgets.maxOutputBytes,
838
+ timeoutMs: remainingBeforeScorer,
839
+ abortSignal: args.signal
840
+ });
841
+ } catch (error) {
842
+ const scorerFailure = error instanceof ScorerFailure ? error : undefined;
843
+ return failureResult({
844
+ ...args,
845
+ seed,
846
+ outcome: args.signal.aborted ? "cancelled" : scorerFailure?.outcome ?? "malformed",
847
+ durationMs: Date.now() - startedAt,
848
+ cost: execution.cost,
849
+ failureCode: scorerFailure?.failureCode ?? (error instanceof Error ? error.message.slice(0, 160) : "scorer-error")
850
+ });
851
+ }
852
+ const [minimum, maximum] = args.task.scorer.scoreRange;
853
+ if (scored.score < minimum || scored.score > maximum) {
854
+ return failureResult({
855
+ ...args,
856
+ seed,
857
+ outcome: "malformed",
858
+ durationMs: Date.now() - startedAt,
859
+ cost: scored.cost,
860
+ failureCode: "score-out-of-range"
861
+ });
862
+ }
863
+ return {
864
+ v: 1,
865
+ taskId: args.task.id,
866
+ category: args.task.category,
867
+ protected: args.task.protected,
868
+ repetition: args.repetition,
869
+ candidateId: args.candidate.id,
870
+ seed,
871
+ outcome: "scored",
872
+ score: scored.score,
873
+ scoreRange: args.task.scorer.scoreRange,
874
+ cost: scored.cost,
875
+ durationMs: Date.now() - startedAt,
876
+ retries: 0,
877
+ metadata: scored.metadata
878
+ };
879
+ } finally {
880
+ fs3.rmSync(isolatedRoot, { recursive: true, force: true });
881
+ }
882
+ }
883
+ });
884
+ } catch (error) {
885
+ return failureResult({
886
+ ...args,
887
+ seed,
888
+ outcome: "infrastructure_failure",
889
+ durationMs: Date.now() - startedAt,
890
+ failureCode: canonicalHash(String(error)).slice(0, 32)
891
+ });
892
+ }
893
+ }
894
+ async function executePairMemberWithRetries(args) {
895
+ let result;
896
+ for (let attempt = 0;attempt <= args.options.budgets.maxRetries; attempt++) {
897
+ result = await executePairMember(args);
898
+ result.retries = attempt;
899
+ if (result.outcome !== "infrastructure_failure" || args.signal.aborted) {
900
+ return result;
901
+ }
902
+ }
903
+ return result;
904
+ }
905
+ async function runEvaluation(options) {
906
+ const inputRoot = options.inputRoot ?? options.projectRoot;
907
+ const tasks = options.tasks.map((task) => EvaluationTaskV1Schema.parse(task));
908
+ if (tasks.length === 0 || tasks.length > options.budgets.maxTasks) {
909
+ throw new Error("evaluation task count exceeds its declared budget");
910
+ }
911
+ if (tasks.some((task) => task.split !== options.split)) {
912
+ throw new Error("evaluation task split does not match the run split");
913
+ }
914
+ const baseline = EvaluationCandidateV1Schema.parse(options.baseline);
915
+ const candidate = EvaluationCandidateV1Schema.parse(options.candidate);
916
+ for (const item of [baseline, candidate]) {
917
+ if (await computeCandidateInputContentHash(inputRoot, item) !== item.contentHash) {
918
+ throw new Error(`candidate ${item.id} content hash is invalid`);
919
+ }
920
+ }
921
+ const snapshotDraft = {
922
+ v: 1,
923
+ id: `taskset-${canonicalHash(tasks.map((task) => task.id)).slice(0, 20)}`,
924
+ split: options.split,
925
+ tasks,
926
+ createdAt: new Date().toISOString()
927
+ };
928
+ for (const task of tasks) {
929
+ await admitEvaluationTask(options.projectRoot, task, inputRoot);
930
+ }
931
+ const taskSet = {
932
+ ...snapshotDraft,
933
+ contentHash: computeTaskSetContentHash(snapshotDraft)
934
+ };
935
+ const storedTaskSet = await saveTaskSetSnapshot(options.projectRoot, taskSet, inputRoot);
936
+ const runId = `eval-${canonicalHash({
937
+ baseline: baseline.contentHash,
938
+ candidate: candidate.contentHash,
939
+ taskSet: storedTaskSet.contentHash,
940
+ split: options.split,
941
+ seed: options.seed,
942
+ models: options.models,
943
+ budgets: options.budgets
944
+ }).slice(0, 32)}`;
945
+ const existingRun = await readEvaluationRun(options.projectRoot, runId);
946
+ if (existingRun)
947
+ return existingRun;
948
+ if (options.split === "test") {
949
+ await claimHeldOutTest(options.projectRoot, {
950
+ v: 1,
951
+ runId,
952
+ taskSetHash: storedTaskSet.contentHash,
953
+ baselineHash: baseline.contentHash,
954
+ candidateHash: candidate.contentHash,
955
+ claimedAt: new Date().toISOString()
956
+ });
957
+ }
958
+ const before = await _internals3.captureWorkingTreeFingerprint(options.projectRoot);
959
+ const controller = new AbortController;
960
+ let externallyCancelled = options.abortSignal?.aborted ?? false;
961
+ let timedOut = false;
962
+ let budgetInconclusive = false;
963
+ let scheduledSpendUsd = 0;
964
+ const externalAbort = () => {
965
+ externallyCancelled = true;
966
+ controller.abort();
967
+ };
968
+ options.abortSignal?.addEventListener("abort", externalAbort, { once: true });
969
+ if (externallyCancelled)
970
+ controller.abort();
971
+ const totalExecutions = tasks.length * 2 * options.budgets.maxRepetitions;
972
+ const executionWaves = Math.ceil(totalExecutions / options.budgets.maxConcurrency);
973
+ const totalTimeoutMs = Math.min(2147483647, options.budgets.maxTaskTimeMs * executionWaves * (options.budgets.maxRetries + 1) + 1000);
974
+ const timeout = setTimeout(() => {
975
+ timedOut = true;
976
+ controller.abort();
977
+ }, totalTimeoutMs);
978
+ try {
979
+ const limit = pLimit(options.budgets.maxConcurrency);
980
+ const jobs = [];
981
+ for (const task of tasks) {
982
+ for (const item of [baseline, candidate]) {
983
+ for (let repetition = 0;repetition < options.budgets.maxRepetitions; repetition++) {
984
+ jobs.push(limit(() => executePairMemberWithRetries({
985
+ options,
986
+ inputRoot,
987
+ task,
988
+ candidate: item,
989
+ repetition,
990
+ signal: controller.signal,
991
+ baseSha: before.head
992
+ }).then((result) => {
993
+ if (options.budgets.maxSpendUsd !== undefined) {
994
+ if (result.cost.source === "unavailable") {
995
+ budgetInconclusive = true;
996
+ controller.abort();
997
+ } else {
998
+ scheduledSpendUsd += result.cost.usd ?? 0;
999
+ if (scheduledSpendUsd > options.budgets.maxSpendUsd) {
1000
+ budgetInconclusive = true;
1001
+ controller.abort();
1002
+ }
1003
+ }
1004
+ }
1005
+ return result;
1006
+ })));
1007
+ }
1008
+ }
1009
+ }
1010
+ const results = await Promise.all(jobs);
1011
+ for (const task of tasks) {
1012
+ if (await computeTaskInputContentHash(inputRoot, task) !== task.contentHash) {
1013
+ throw new Error(`task ${task.id} changed during evaluation`);
1014
+ }
1015
+ }
1016
+ for (const item of [baseline, candidate]) {
1017
+ if (await computeCandidateInputContentHash(inputRoot, item) !== item.contentHash) {
1018
+ throw new Error(`candidate ${item.id} changed during evaluation`);
1019
+ }
1020
+ }
1021
+ const cost = aggregateCost(results);
1022
+ const spendUnknown = options.budgets.maxSpendUsd !== undefined && cost.source === "unavailable";
1023
+ const overSpend = options.budgets.maxSpendUsd !== undefined && cost.source !== "unavailable" && (cost.usd ?? 0) > options.budgets.maxSpendUsd;
1024
+ const incomplete = results.some((result) => result.outcome !== "scored");
1025
+ const runDraft = {
1026
+ v: 1,
1027
+ runId,
1028
+ createdAt: new Date().toISOString(),
1029
+ status: externallyCancelled || timedOut ? "cancelled" : incomplete || spendUnknown || overSpend || budgetInconclusive ? "inconclusive" : "complete",
1030
+ baseline,
1031
+ candidate,
1032
+ taskSet: {
1033
+ id: storedTaskSet.id,
1034
+ contentHash: storedTaskSet.contentHash,
1035
+ taskIds: tasks.map((task) => task.id)
1036
+ },
1037
+ split: options.split,
1038
+ seed: options.seed,
1039
+ models: options.models,
1040
+ environment: {
1041
+ platform: process.platform,
1042
+ arch: process.arch,
1043
+ runtime: `node-${process.versions.node};bun-${process.versions.bun ?? "compat"}`,
1044
+ baseSha: before.head,
1045
+ activeTreeHash: canonicalHash(before),
1046
+ taskSetHash: storedTaskSet.contentHash
1047
+ },
1048
+ budgets: options.budgets,
1049
+ results,
1050
+ cost
1051
+ };
1052
+ const run = EvaluationRunV1Schema.parse({
1053
+ ...runDraft,
1054
+ integrityHash: computeRunIntegrityHash(runDraft)
1055
+ });
1056
+ await saveEvaluationRun(options.projectRoot, run);
1057
+ return run;
1058
+ } finally {
1059
+ clearTimeout(timeout);
1060
+ options.abortSignal?.removeEventListener("abort", externalAbort);
1061
+ const after = await _internals3.captureWorkingTreeFingerprint(options.projectRoot);
1062
+ if (before.head !== after.head || before.porcelainHash !== after.porcelainHash) {
1063
+ throw new Error("evaluation changed the active working tree");
1064
+ }
1065
+ }
1066
+ }
1067
+ var _internals3 = {
1068
+ captureWorkingTreeFingerprint,
1069
+ runExternalTool,
1070
+ withDisposableWorktree
1071
+ };
1072
+ function createModelEvaluationExecutor(dispatcher, parentSessionId) {
1073
+ return async (args) => {
1074
+ const result = await dispatcher({
1075
+ directory: args.isolatedRoot,
1076
+ agentName: args.candidate.agent ?? "reviewer",
1077
+ modelId: args.candidate.model,
1078
+ system: args.payload,
1079
+ prompt: [
1080
+ "EVALUATION TASK:",
1081
+ args.instruction,
1082
+ "",
1083
+ "Return the task-required scorer payload exactly."
1084
+ ].join(`
1085
+ `),
1086
+ timeoutMs: args.task.scorer.timeoutMs,
1087
+ parentSessionId,
1088
+ abortSignal: args.abortSignal
1089
+ });
1090
+ return {
1091
+ status: result.status === "error" ? "infrastructure_failure" : result.status,
1092
+ text: result.text,
1093
+ durationMs: result.durationMs,
1094
+ cost: { source: "unavailable" },
1095
+ error: result.error
1096
+ };
1097
+ };
1098
+ }
1099
+
1100
+ export { resolveExecutableFromPath, runExternalTool, pLimit, captureWorkingTreeFingerprint, runEvaluation, _internals3 as _internals, createModelEvaluationExecutor };