@tangle-network/agent-bench 0.3.6 → 0.3.7

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 (91) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/adapters.js +2 -2
  3. package/dist/benchmarks/humaneval.d.ts +10 -1
  4. package/dist/benchmarks/humaneval.js +5 -3
  5. package/dist/{chunk-PPYSEKFM.js → chunk-5H5XV76F.js} +73 -15
  6. package/dist/chunk-5H5XV76F.js.map +1 -0
  7. package/dist/{chunk-5SBJCB6W.js → chunk-PWQVGAJB.js} +2 -2
  8. package/dist/index.js +2 -2
  9. package/package.json +5 -4
  10. package/scripts/run-package-tests.mjs +30 -8
  11. package/scripts/verify-pier-agent.mts +1 -0
  12. package/src/benchmarks/humaneval.test.mts +122 -0
  13. package/src/benchmarks/humaneval.ts +100 -27
  14. package/src/david-attribution.mts +78 -0
  15. package/src/david-goliath.mts +149 -0
  16. package/src/hev-improve.mts +25 -6
  17. package/src/humaneval-object-ablation.mts +201 -0
  18. package/src/live-improve-campaign-mbpp.mts +641 -0
  19. package/src/live-improve-campaign.mts +500 -0
  20. package/src/mbpp-structural.mts +12 -7
  21. package/src/stream-observe.py +45 -0
  22. package/src/stream-observe.tpl.html +247 -0
  23. package/src/supervisor-arena.mts +816 -0
  24. package/src/swe-arena/analyze.ts +211 -0
  25. package/src/swe-arena/arms.ts +788 -0
  26. package/src/swe-arena/bootstrap-meta.mts +188 -0
  27. package/src/swe-arena/bootstrap-meta.test.mts +51 -0
  28. package/src/swe-arena/calibrate.ts +116 -0
  29. package/src/swe-arena/capabilities.mts +76 -0
  30. package/src/swe-arena/capabilities.test.mts +57 -0
  31. package/src/swe-arena/capacity.ts +194 -0
  32. package/src/swe-arena/cell-evidence.mts +405 -0
  33. package/src/swe-arena/cell-evidence.test.mts +248 -0
  34. package/src/swe-arena/diagnosis-ensemble.test.mts +210 -0
  35. package/src/swe-arena/diagnosis-ensemble.ts +520 -0
  36. package/src/swe-arena/execution.test.mts +1170 -0
  37. package/src/swe-arena/fixtures/analyze.py +80 -0
  38. package/src/swe-arena/fixtures/excludes.txt +8 -0
  39. package/src/swe-arena/fixtures/gen1-salvage/README.md +45 -0
  40. package/src/swe-arena/fixtures/gen1-salvage/cand0-e6d7361.diff +116 -0
  41. package/src/swe-arena/fixtures/gen1-salvage/cand1-76a8590.diff +293 -0
  42. package/src/swe-arena/fixtures/holdout-preregister.log +12 -0
  43. package/src/swe-arena/fixtures/holdout.json +44 -0
  44. package/src/swe-arena/fixtures/instances.json +146 -0
  45. package/src/swe-arena/fixtures/ledger.jsonl +12 -0
  46. package/src/swe-arena/fixtures/patches/pallets__flask-5014.solo.patch +36 -0
  47. package/src/swe-arena/fixtures/patches/pydata__xarray-4687.sup.patch +33 -0
  48. package/src/swe-arena/fixtures/rejudge.jsonl +15 -0
  49. package/src/swe-arena/fixtures/rematch.jsonl +3 -0
  50. package/src/swe-arena/fixtures/rematch2.jsonl +3 -0
  51. package/src/swe-arena/fixtures/rematch3.jsonl +3 -0
  52. package/src/swe-arena/fixtures/sup-journal-true.json +19 -0
  53. package/src/swe-arena/fixtures/verify/astropy__astropy-13033.sh +48 -0
  54. package/src/swe-arena/fixtures/verify/django__django-11532.sh +50 -0
  55. package/src/swe-arena/fixtures/verify/matplotlib__matplotlib-20826.sh +76 -0
  56. package/src/swe-arena/fixtures/verify/pydata__xarray-4687.sh +44 -0
  57. package/src/swe-arena/fixtures/verify/pytest-dev__pytest-6197.sh +32 -0
  58. package/src/swe-arena/fixtures/verify/sphinx-doc__sphinx-9658.sh +51 -0
  59. package/src/swe-arena/fixtures/worker-tokens.json +42 -0
  60. package/src/swe-arena/fixtures.ts +104 -0
  61. package/src/swe-arena/holdout-certify.mts +408 -0
  62. package/src/swe-arena/holdout-certify.test.mts +160 -0
  63. package/src/swe-arena/judge-child.mts +37 -0
  64. package/src/swe-arena/manifest.mts +293 -0
  65. package/src/swe-arena/manifest.test.mts +169 -0
  66. package/src/swe-arena/materialize.ts +142 -0
  67. package/src/swe-arena/outer-loop.mts +2145 -0
  68. package/src/swe-arena/outer-loop.test.mts +696 -0
  69. package/src/swe-arena/parity.test.mts +87 -0
  70. package/src/swe-arena/proc.test.mts +174 -0
  71. package/src/swe-arena/proc.ts +260 -0
  72. package/src/swe-arena/profiles/default-author.profile.json +4 -0
  73. package/src/swe-arena/proposer-fanout.mts +489 -0
  74. package/src/swe-arena/proposer-fanout.test.mts +372 -0
  75. package/src/swe-arena/reconcile.ts +0 -0
  76. package/src/swe-arena/replay.mts +183 -0
  77. package/src/swe-arena/replay.test.mts +300 -0
  78. package/src/swe-arena/run-experiment.mts +361 -0
  79. package/src/swe-arena/run-supervisor.mjs +297 -0
  80. package/src/swe-arena/run-supervisor.test.mts +498 -0
  81. package/src/swe-arena/serialized-judge.ts +414 -0
  82. package/src/swe-arena/types.ts +166 -0
  83. package/src/swe-code-improve.mts +328 -0
  84. package/src/swe-emit-patch.mts +104 -0
  85. package/src/swe-improve.mts +232 -0
  86. package/src/swe-jail.ts +2 -2
  87. package/src/swe-local-proof.mts +169 -0
  88. package/src/swe-repro-calibrate.mts +446 -0
  89. package/src/swe-stream.mts +1497 -0
  90. package/dist/chunk-PPYSEKFM.js.map +0 -1
  91. /package/dist/{chunk-5SBJCB6W.js.map → chunk-PWQVGAJB.js.map} +0 -0
@@ -0,0 +1,816 @@
1
+ /**
2
+ * Supervisor showdown arena — implements supervisor-lab
3
+ * docs/results/PREREG-supervisor-showdown.md (2026-07-09) EXACTLY. The prereg is
4
+ * the contract: task sets, arms, equal-compute rule, plan contract, and the
5
+ * Phase A/B separation are fixed there and must not be tuned after results.
6
+ *
7
+ * Arms (SET=hard: the 62 problems the committed structural run ended hiddenFinal=0;
8
+ * SET=control: 20 problems it solved — supervision must NOT move these):
9
+ * A — more-blind-compute control: one fresh structural round (k=5, testgen=6,
10
+ * <=2 repairs, Llama-3-8B temp 0.8), no evidence shown.
11
+ * B — self-supervisor: Llama reads the visible evidence package, writes a
12
+ * diagnosis+plan (no code), then the arm-A loop runs conditioned on it.
13
+ * C — smart supervisor: glm-4.5-air writes the plan (<=400 tokens by explicit
14
+ * instruction; fenced code / `def ` lines stripped + planLeaked recorded),
15
+ * same Llama loop conditioned on it.
16
+ * D — smart-solo anchor: one glm-4.5-air call, plain solve instruction.
17
+ *
18
+ * Evidence (arms B/C) comes ONLY from the committed run rows (EVIDENCE jsonl):
19
+ * samples, visible-check outputs, authored asserts, repair transcript. The rows'
20
+ * hiddenSamples/hiddenFinal fields are NEVER read into any prompt — hidden info
21
+ * defines the task sets (already hardcoded here) and nothing else.
22
+ *
23
+ * Phase separation (as hev-structural.mts, whose runJailed/nonce-judge/oracle
24
+ * this file replicates — that module runs main() on import, so its internals
25
+ * cannot be imported without side effects):
26
+ * Phase A: all arm decisions from visible info only; rows appended to OUT.phaseA.
27
+ * Phase B: rig-local nonce-sentinel hidden judge grades every sample and the
28
+ * locked final, script-side; graded rows appended to OUT.
29
+ *
30
+ * cd bench && ARM=A|B|C|D SET=hard|control N=… OUT=/abs/rows.jsonl \
31
+ * EVIDENCE=/abs/full-llama3-8b-testgen.jsonl HUMANEVAL_GZ=/abs/HumanEval.jsonl.gz \
32
+ * TANGLE_API_KEY=$TOGETHER_API_KEY ZAI_API_KEY=… npx tsx src/supervisor-arena.mts
33
+ */
34
+ import { execFile, execFileSync } from 'node:child_process'
35
+ import { randomBytes } from 'node:crypto'
36
+ import { appendFileSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
37
+ import { tmpdir } from 'node:os'
38
+ import { join } from 'node:path'
39
+ import { type HumanEvalTask, basePrompt, extractCode, loadHumanEval } from './benchmarks/humaneval'
40
+ import { pool } from './stats.mts'
41
+
42
+ // ---------- pre-registered task sets (verbatim from the prereg; DO NOT EDIT) ----------
43
+
44
+ const hardIds = [
45
+ 2, 5, 10, 16, 18, 20, 21, 26, 27, 1, 33, 37, 39, 38, 41, 46, 54, 52, 62, 64, 65, 73, 75, 80, 81, 85, 84, 92, 91, 93,
46
+ 100, 99, 101, 70, 32, 103, 102, 108, 110, 112, 109, 115, 118, 119, 120, 122, 125, 126, 127, 131, 129, 130, 134, 138,
47
+ 135, 137, 140, 145, 148, 157, 160, 163,
48
+ ].map((n) => `HumanEval/${n}`)
49
+
50
+ const controlIds = [0, 3, 4, 6, 7, 8, 9, 11, 12, 13, 14, 15, 17, 19, 22, 23, 24, 25, 28, 29].map((n) => `HumanEval/${n}`)
51
+
52
+ // ---------- models + list prices (per 1M tokens; printed as list-price $, not billed truth) ----------
53
+
54
+ const workerModelDefault = 'meta-llama/Meta-Llama-3-8B-Instruct-Lite'
55
+ const glmModelDefault = 'glm-4.5-air'
56
+ const listPrices: Record<string, { in: number; out: number }> = {
57
+ [workerModelDefault]: { in: 0.1, out: 0.1 },
58
+ [glmModelDefault]: { in: 0.2, out: 1.1 },
59
+ }
60
+
61
+ const dockerImage = 'python:3.12-slim'
62
+ const dockerTimeoutMs = Number(process.env.DOCKER_TIMEOUT_MS ?? 20000)
63
+
64
+ function must(name: string): string {
65
+ const v = process.env[name]
66
+ if (!v) throw new Error(`env ${name} is required`)
67
+ return v
68
+ }
69
+
70
+ // ---------- docker semaphore + jailed runner (replicated from hev-structural) ----------
71
+
72
+ let dockerSlots = 6
73
+ let dockerInFlight = 0
74
+ const dockerWaiters: Array<() => void> = []
75
+ async function withDockerSlot<T>(fn: () => Promise<T>): Promise<T> {
76
+ if (dockerInFlight >= dockerSlots) await new Promise<void>((r) => dockerWaiters.push(r))
77
+ dockerInFlight += 1
78
+ try {
79
+ return await fn()
80
+ } finally {
81
+ dockerInFlight -= 1
82
+ dockerWaiters.shift()?.()
83
+ }
84
+ }
85
+
86
+ const containerPrefix = `supv-${process.pid}`
87
+ let containerSeq = 0
88
+
89
+ function reapContainers(): void {
90
+ try {
91
+ const ids = execFileSync('docker', ['ps', '-aq', '--filter', `name=${containerPrefix}`], { timeout: 10000 }).toString().trim()
92
+ if (ids) execFileSync('docker', ['rm', '-f', ...ids.split('\n')], { timeout: 15000 })
93
+ } catch {
94
+ /* reaper is best-effort by design */
95
+ }
96
+ }
97
+ process.on('SIGINT', () => {
98
+ reapContainers()
99
+ process.exit(130)
100
+ })
101
+ process.on('SIGTERM', () => {
102
+ reapContainers()
103
+ process.exit(143)
104
+ })
105
+
106
+ interface JailResult {
107
+ exitCode: number
108
+ stdout: string
109
+ stderr: string
110
+ }
111
+
112
+ /** One python program in the jail: --network=none, cpu/mem caps, in-container
113
+ * `timeout -s KILL`, client timeout, backstop. Docker INFRA faults throw. */
114
+ function runJailed(program: string): Promise<JailResult> {
115
+ return withDockerSlot(
116
+ () =>
117
+ new Promise<JailResult>((resolvePromise, reject) => {
118
+ const dir = mkdtempSync(join(tmpdir(), 'supv-'))
119
+ writeFileSync(join(dir, 'p.py'), program)
120
+ const name = `${containerPrefix}-${containerSeq++}`
121
+ let settled = false
122
+ const cleanup = () => {
123
+ rmSync(dir, { recursive: true, force: true })
124
+ execFile('docker', ['rm', '-f', name], () => {})
125
+ }
126
+ const finish = (res: JailResult) => {
127
+ if (settled) return
128
+ settled = true
129
+ clearTimeout(backstop)
130
+ cleanup()
131
+ resolvePromise(res)
132
+ }
133
+ const fail = (e: Error) => {
134
+ if (settled) return
135
+ settled = true
136
+ clearTimeout(backstop)
137
+ cleanup()
138
+ reject(e)
139
+ }
140
+ const backstop = setTimeout(() => finish({ exitCode: 124, stdout: '', stderr: 'backstop timeout (no output)' }), dockerTimeoutMs + 5000)
141
+ const inContainerSecs = Math.ceil(dockerTimeoutMs / 1000) + 2
142
+ execFile(
143
+ 'docker',
144
+ [
145
+ 'run', '--rm', '--name', name, '--network=none', '--cpus=1', '--memory=512m',
146
+ '-v', `${dir}:/w:ro`, '-w', '/w', dockerImage,
147
+ 'timeout', '-s', 'KILL', String(inContainerSecs), 'python', '/w/p.py',
148
+ ],
149
+ { timeout: dockerTimeoutMs + 3000, killSignal: 'SIGKILL', maxBuffer: 4 * 1024 * 1024 },
150
+ (err, stdout, stderr) => {
151
+ if (err) {
152
+ const e = err as NodeJS.ErrnoException & { code?: number | string }
153
+ if (e.code === 'ENOENT') return fail(new Error('docker binary not found on PATH'))
154
+ const se = stderr ?? ''
155
+ if (/cannot connect to the docker daemon|is the docker daemon running|permission denied while trying to connect/i.test(se)) {
156
+ return fail(new Error(`docker daemon unreachable: ${se.slice(0, 200)}`))
157
+ }
158
+ if (/(unable to find image|pull access denied|manifest unknown|error response from daemon).*(pull|repository|registry)/i.test(se)) {
159
+ return fail(new Error(`docker image ${dockerImage} unavailable: ${se.slice(0, 200)}`))
160
+ }
161
+ const code = typeof e.code === 'number' ? e.code : 1
162
+ return finish({ exitCode: code, stdout: stdout ?? '', stderr: se })
163
+ }
164
+ finish({ exitCode: 0, stdout: stdout ?? '', stderr: stderr ?? '' })
165
+ },
166
+ )
167
+ }),
168
+ )
169
+ }
170
+
171
+ // ---------- honest oracle (visible docstring doctests + authored asserts; replicated) ----------
172
+
173
+ interface HonestResult {
174
+ attempted: number
175
+ failed: number
176
+ failureOutput: string
177
+ pass: boolean
178
+ dAttempted?: number
179
+ dFailed?: number
180
+ gAttempted?: number
181
+ gFailed?: number
182
+ }
183
+
184
+ function buildHonestProgram(task: HumanEvalTask, candidate: string, nonce: string, genTests: string[] = []): string {
185
+ const promptB64 = Buffer.from(task.prompt, 'utf8').toString('base64')
186
+ const entryB64 = Buffer.from(task.entryPoint, 'utf8').toString('base64')
187
+ const genB64 = Buffer.from(JSON.stringify(genTests), 'utf8').toString('base64')
188
+ return `${task.prompt}\n${candidate}\n
189
+ import ast as _ast, base64 as _b64, doctest as _doctest, io as _io, json as _json, sys as _sys
190
+ _prompt_text = _b64.b64decode("${promptB64}").decode("utf8")
191
+ _entry = _b64.b64decode("${entryB64}").decode("utf8")
192
+ _gen_tests = _json.loads(_b64.b64decode("${genB64}").decode("utf8"))
193
+ _stub_ns = {}
194
+ exec(_prompt_text, _stub_ns)
195
+ _doc = getattr(_stub_ns.get(_entry), "__doc__", None) or ""
196
+ try:
197
+ _examples = _doctest.DocTestParser().get_examples(_doc)
198
+ except ValueError:
199
+ _examples = [] # malformed docstring indentation -> no usable signal, not a crash
200
+
201
+ class _Checker(_doctest.OutputChecker):
202
+ def check_output(self, want, got, optionflags):
203
+ if super().check_output(want, got, optionflags):
204
+ return True
205
+ if want.strip() == "" and got.strip() == "True":
206
+ return True
207
+ try:
208
+ return _ast.literal_eval(want.strip()) == _ast.literal_eval(got.strip())
209
+ except Exception:
210
+ return False
211
+
212
+ _test = _doctest.DocTest(_examples, globs=dict(globals()), name="visible", filename="p", lineno=0, docstring=_doc)
213
+ _runner = _doctest.DocTestRunner(checker=_Checker(), verbose=False, optionflags=_doctest.NORMALIZE_WHITESPACE | _doctest.IGNORE_EXCEPTION_DETAIL)
214
+ _buf = _io.StringIO()
215
+ _res = _runner.run(_test, out=_buf.write)
216
+
217
+ _g_att, _g_fail = 0, 0
218
+ for _t in _gen_tests:
219
+ _g_att += 1
220
+ try:
221
+ exec(_t, dict(globals()))
222
+ except Exception as _e:
223
+ _g_fail += 1
224
+ _buf.write("GENTEST FAILED: %s -> %s: %s\\n" % (_t.strip()[:200], type(_e).__name__, str(_e)[:200]))
225
+
226
+ _att = _res.attempted + _g_att
227
+ _fail = _res.failed + _g_fail
228
+ print("HONEST-${nonce} attempted=%d failed=%d datt=%d dfail=%d gatt=%d gfail=%d" % (_att, _fail, _res.attempted, _res.failed, _g_att, _g_fail))
229
+ _sys.stdout.write(_buf.getvalue()[-1500:])
230
+ _sys.exit(0 if _att > 0 and _fail == 0 else 1)
231
+ `
232
+ }
233
+
234
+ async function runHonestOracle(task: HumanEvalTask, candidate: string, genTests: string[] = []): Promise<HonestResult> {
235
+ const nonce = randomBytes(8).toString('hex')
236
+ const r = await runJailed(buildHonestProgram(task, candidate, nonce, genTests))
237
+ const summary = new RegExp(`HONEST-${nonce} attempted=(\\d+) failed=(\\d+) datt=(\\d+) dfail=(\\d+) gatt=(\\d+) gfail=(\\d+)`).exec(r.stdout)
238
+ if (!summary) {
239
+ const detail = (r.stderr || r.stdout).slice(-1500) || 'timed out (no output)'
240
+ return { attempted: -1, failed: -1, failureOutput: detail, pass: false }
241
+ }
242
+ const attempted = Number(summary[1])
243
+ const failed = Number(summary[2])
244
+ const failureOutput = r.stdout.replace(summary[0], '').slice(-1500)
245
+ return {
246
+ attempted,
247
+ failed,
248
+ failureOutput,
249
+ pass: attempted > 0 && failed === 0,
250
+ dAttempted: Number(summary[3]),
251
+ dFailed: Number(summary[4]),
252
+ gAttempted: Number(summary[5]),
253
+ gFailed: Number(summary[6]),
254
+ }
255
+ }
256
+
257
+ function honestScore(h: HonestResult): number {
258
+ if (h.attempted <= 0) return h.attempted === 0 ? 0 : -1
259
+ return (h.attempted - h.failed) / h.attempted
260
+ }
261
+
262
+ // ---------- hidden judge (Phase B ONLY; nonce sentinel, not exit-0; replicated) ----------
263
+
264
+ function buildHiddenProgram(task: HumanEvalTask, candidate: string, nonce: string): string {
265
+ return `${task.prompt}\n${candidate}\n\n${task.test}\n\ncheck(${task.entryPoint})\nprint("HIDDEN-${nonce} PASS")\n`
266
+ }
267
+
268
+ async function runHiddenJudge(task: HumanEvalTask, candidate: string): Promise<{ pass: number; detail?: string }> {
269
+ const nonce = randomBytes(8).toString('hex')
270
+ const r = await runJailed(buildHiddenProgram(task, candidate, nonce))
271
+ if (r.exitCode === 0 && r.stdout.includes(`HIDDEN-${nonce} PASS`)) return { pass: 1 }
272
+ return { pass: 0, detail: (r.stderr || r.stdout).slice(-600) || 'timed out (no output)' }
273
+ }
274
+
275
+ // ---------- model client (plain fetch; retries transient HTTP AND empty content — the
276
+ // glm-4.5-air reasoning path starves `content` when reasoning eats max_tokens) ----------
277
+
278
+ interface ClientCfg {
279
+ base: string
280
+ key: string
281
+ model: string
282
+ maxTokens: number
283
+ temperature: number
284
+ }
285
+
286
+ interface Completion {
287
+ content: string
288
+ attempts: number
289
+ tokensIn: number
290
+ tokensOut: number
291
+ }
292
+
293
+ async function complete(cfg: ClientCfg, messages: Array<{ role: string; content: string }>): Promise<Completion> {
294
+ let lastErr = ''
295
+ for (let attempt = 1; attempt <= 4; attempt += 1) {
296
+ if (attempt > 1) await new Promise((r) => setTimeout(r, 2000 * 2 ** attempt))
297
+ const ctl = new AbortController()
298
+ const timer = setTimeout(() => ctl.abort(), Number(process.env.LLM_TIMEOUT_MS ?? 240_000))
299
+ try {
300
+ const res = await fetch(`${cfg.base}/chat/completions`, {
301
+ method: 'POST',
302
+ headers: { Authorization: `Bearer ${cfg.key}`, 'Content-Type': 'application/json' },
303
+ body: JSON.stringify({ model: cfg.model, max_tokens: cfg.maxTokens, temperature: cfg.temperature, messages }),
304
+ signal: ctl.signal,
305
+ })
306
+ if (!res.ok) {
307
+ lastErr = `HTTP ${res.status}: ${(await res.text()).slice(0, 200)}`
308
+ continue
309
+ }
310
+ const d = (await res.json()) as {
311
+ choices?: Array<{ message?: { content?: string } }>
312
+ usage?: { prompt_tokens?: number; completion_tokens?: number }
313
+ }
314
+ const content = d.choices?.[0]?.message?.content ?? ''
315
+ if (content.trim() === '') {
316
+ lastErr = 'empty content'
317
+ continue
318
+ }
319
+ return { content, attempts: attempt, tokensIn: d.usage?.prompt_tokens ?? 0, tokensOut: d.usage?.completion_tokens ?? 0 }
320
+ } catch (e) {
321
+ lastErr = e instanceof Error ? e.message : String(e)
322
+ } finally {
323
+ clearTimeout(timer)
324
+ }
325
+ }
326
+ throw new Error(`completion failed after retries: ${lastErr}`)
327
+ }
328
+
329
+ // zai 429s above ~4 concurrent requests — a dedicated semaphore, independent of the
330
+ // task pool width, caps every glm call (plans in C, solo solves in D).
331
+ let zaiSlots = 4
332
+ let zaiInFlight = 0
333
+ const zaiWaiters: Array<() => void> = []
334
+ async function withZaiSlot<T>(fn: () => Promise<T>): Promise<T> {
335
+ if (zaiInFlight >= zaiSlots) await new Promise<void>((r) => zaiWaiters.push(r))
336
+ zaiInFlight += 1
337
+ try {
338
+ return await fn()
339
+ } finally {
340
+ zaiInFlight -= 1
341
+ zaiWaiters.shift()?.()
342
+ }
343
+ }
344
+
345
+ // ---------- CodeT-style test generation (visible info only, BEFORE any candidate; replicated) ----------
346
+
347
+ const testGenInstruction = (count: number, entry: string) =>
348
+ `Read the following Python function signature and docstring. Write exactly ${count} single-line assert statements that test the function \`${entry}\`, based ONLY on the behavior the docstring describes. Each assert must be one physical line of the form \`assert ${entry}(...) == expected\` (or a True/False check). Do NOT implement the function. Do NOT copy examples verbatim if you can test other cases too. Output ONLY the assert lines inside a single \`\`\`python code block.`
349
+
350
+ async function generateTests(cfg: ClientCfg, task: HumanEvalTask, count: number): Promise<{ tests: string[]; completion: Completion }> {
351
+ const c = await complete(cfg, [
352
+ { role: 'user', content: `${testGenInstruction(count, task.entryPoint)}\n\n\`\`\`python\n${task.prompt}\`\`\`` },
353
+ ])
354
+ const block = extractCode(c.content)
355
+ const balanced = (s: string) => {
356
+ let d = 0
357
+ for (const ch of s) {
358
+ if (ch === '(' || ch === '[' || ch === '{') d += 1
359
+ else if (ch === ')' || ch === ']' || ch === '}') d -= 1
360
+ if (d < 0) return false
361
+ }
362
+ return d === 0
363
+ }
364
+ const tests = block
365
+ .split('\n')
366
+ .map((l) => l.trim())
367
+ .filter((l) => l.startsWith('assert ') && l.includes(task.entryPoint) && balanced(l))
368
+ .slice(0, count)
369
+ return { tests, completion: c }
370
+ }
371
+
372
+ /** Prefer the LAST fenced block containing a `def` (repair replies echo the failure
373
+ * report in a bare fence first), else the shared extractor. */
374
+ function extractRepairCode(reply: string): string {
375
+ const fences = [...reply.matchAll(/```(?:python|py)?\s*\n([\s\S]*?)```/gi)].map((m) => (m[1] ?? '').trim())
376
+ for (let i = fences.length - 1; i >= 0; i -= 1) {
377
+ if (/(^|\n)\s*def\s+\w+/.test(fences[i] as string)) return fences[i] as string
378
+ }
379
+ return extractCode(reply)
380
+ }
381
+
382
+ // ---------- evidence package (arms B/C) — visible fields of the committed run rows ONLY ----------
383
+
384
+ /** The visible slice of a committed run row. hiddenSamples/hiddenFinal exist in the
385
+ * file but are deliberately absent here: parsing narrows to these fields so hidden
386
+ * info cannot reach a prompt by accident. */
387
+ interface EvidenceRow {
388
+ taskId: string
389
+ samples: string[]
390
+ honest: HonestResult[]
391
+ repairs: Array<{ code: string; honest: HonestResult }>
392
+ genTests: string[]
393
+ selectedIdx: number
394
+ }
395
+
396
+ function loadEvidence(path: string): Map<string, EvidenceRow> {
397
+ const rows = new Map<string, EvidenceRow>()
398
+ for (const line of readFileSync(path, 'utf8').split('\n')) {
399
+ if (line.trim() === '') continue
400
+ const d = JSON.parse(line) as EvidenceRow & Record<string, unknown>
401
+ if (!d.taskId || !Array.isArray(d.samples) || !Array.isArray(d.honest)) throw new Error(`malformed evidence row: ${line.slice(0, 120)}`)
402
+ rows.set(d.taskId, {
403
+ taskId: d.taskId,
404
+ samples: d.samples,
405
+ honest: d.honest,
406
+ repairs: Array.isArray(d.repairs) ? d.repairs : [],
407
+ genTests: Array.isArray(d.genTests) ? d.genTests : [],
408
+ selectedIdx: Number(d.selectedIdx ?? 0),
409
+ })
410
+ }
411
+ return rows
412
+ }
413
+
414
+ const clip = (s: string, n: number): string => {
415
+ const t = s.trim()
416
+ return t.length <= n ? t : `${t.slice(0, n)} …[+${t.length - n} chars trimmed]`
417
+ }
418
+
419
+ const firstLines = (code: string, n = 2): string =>
420
+ code
421
+ .split('\n')
422
+ .filter((l) => l.trim() !== '')
423
+ .slice(0, n)
424
+ .join(' | ')
425
+
426
+ const checkStatus = (h: HonestResult): string =>
427
+ h.attempted < 0
428
+ ? 'crashed before the visible checks could run'
429
+ : h.attempted === 0
430
+ ? 'no visible checks were runnable'
431
+ : h.pass
432
+ ? `passed all ${h.attempted} visible checks`
433
+ : `failed ${h.failed}/${h.attempted} visible checks`
434
+
435
+ /** Problem prompt + per-attempt one-line summary (first 2 code lines + trimmed
436
+ * visible-check output) + authored asserts + repair outcomes, bounded to
437
+ * ~3000 tokens (maxChars chars). Visible information only. */
438
+ function renderEvidence(task: HumanEvalTask, ev: EvidenceRow, maxChars: number): string {
439
+ const parts: string[] = []
440
+ parts.push('The problem:', '```python', task.prompt.trimEnd(), '```', '')
441
+ parts.push(`Previous attempts by the programmer, each with the result of the VISIBLE checks (docstring examples + the authored asserts listed below):`)
442
+ ev.samples.forEach((code, i) => {
443
+ const h = ev.honest[i] ?? { attempted: -1, failed: -1, failureOutput: '', pass: false }
444
+ parts.push(`[attempt ${i + 1}]${i === ev.selectedIdx ? ' (selected as best)' : ''} starts: ${firstLines(code)}`)
445
+ parts.push(` result: ${checkStatus(h)}`)
446
+ const fo = clip(h.failureOutput ?? '', 350)
447
+ if (fo !== '') parts.push(` check output: ${fo}`)
448
+ })
449
+ parts.push('', 'Authored asserts used as visible checks:')
450
+ for (const t of ev.genTests) parts.push(` ${t}`)
451
+ if (ev.genTests.length === 0) parts.push(' (none survived filtering)')
452
+ if (ev.repairs.length > 0) {
453
+ parts.push('', 'Repair attempts (revisions of the selected attempt, steered by the check output):')
454
+ ev.repairs.forEach((r, i) => {
455
+ parts.push(`[repair ${i + 1}] starts: ${firstLines(r.code)}`)
456
+ parts.push(` result: ${checkStatus(r.honest)}`)
457
+ const fo = clip(r.honest.failureOutput ?? '', 350)
458
+ if (fo !== '') parts.push(` check output: ${fo}`)
459
+ })
460
+ } else {
461
+ parts.push('', 'No repair attempts were made on this problem.')
462
+ }
463
+ let text = parts.join('\n')
464
+ if (text.length > maxChars) text = `${text.slice(0, maxChars)}\n…[evidence truncated at ${maxChars} chars]`
465
+ return text
466
+ }
467
+
468
+ // ---------- supervisor plan (arms B/C): diagnose + steer, never solve ----------
469
+
470
+ const planContract =
471
+ "Diagnose why every attempt failed and write a plan: the specific mistake, the correct approach, edge cases to handle. You may include at most 3 short illustrative expressions. Do NOT write the function; any fenced code block or line starting with 'def ' disqualifies the plan. Keep the plan under 400 tokens."
472
+
473
+ const supervisorPrompt = (evidence: string): string =>
474
+ ["You are reviewing a programmer's previous attempts at a Python problem.", '', evidence, '', planContract].join('\n')
475
+
476
+ /** Enforce the no-code contract: strip fenced blocks and `def ` lines, record the
477
+ * leak (leak RATE is a pre-registered measurement, so the stripped plan is still
478
+ * used rather than the task being dropped). */
479
+ function stripPlanCode(raw: string): { plan: string; leaked: boolean } {
480
+ let leaked = false
481
+ let s = raw
482
+ if (s.includes('```')) {
483
+ // an unterminated fence leaks to end-of-text, so strip greedily to the closer or EOF
484
+ const stripped = s.replace(/```[a-zA-Z]*[^\n]*\n?[\s\S]*?(?:```|$)/g, '')
485
+ if (stripped !== s) leaked = true
486
+ s = stripped
487
+ }
488
+ const lines = s.split('\n')
489
+ const kept = lines.filter((l) => !/^\s*def\s/.test(l))
490
+ if (kept.length !== lines.length) leaked = true
491
+ return { plan: kept.join('\n').trim(), leaked }
492
+ }
493
+
494
+ // ---------- Phase A worker loop — the hev-structural recipe verbatim (k samples temp 0.8,
495
+ // fresh testgen, honest argmax select, <=R honest-grounded repairs); arms B/C prepend the plan ----------
496
+
497
+ interface WorkerOutcome {
498
+ samples: string[]
499
+ honest: HonestResult[]
500
+ selectedIdx: number
501
+ repairs: Array<{ code: string; honest: HonestResult }>
502
+ finalCode: string
503
+ repairStop: string
504
+ genTests: string[]
505
+ calls: number
506
+ attempts: number
507
+ tokensIn: number
508
+ tokensOut: number
509
+ }
510
+
511
+ async function runWorkerLoop(cfg: ClientCfg, task: HumanEvalTask, k: number, maxRepairs: number, testGen: number, planPreamble: string | null): Promise<WorkerOutcome> {
512
+ const samplePrompt = planPreamble === null ? basePrompt(task) : `${planPreamble}\n\n${basePrompt(task)}`
513
+ let calls = 0
514
+ let attempts = 0
515
+ let tokensIn = 0
516
+ let tokensOut = 0
517
+ const track = (c: Completion) => {
518
+ calls += 1
519
+ attempts += c.attempts
520
+ tokensIn += c.tokensIn
521
+ tokensOut += c.tokensOut
522
+ }
523
+
524
+ // asserts regenerated FRESH for this arm's round, from the prompt alone (plan-free:
525
+ // testgen is oracle authorship, not solving — conditioning it would contaminate the oracle)
526
+ let genTests: string[] = []
527
+ if (testGen > 0) {
528
+ const g = await generateTests(cfg, task, testGen)
529
+ track(g.completion)
530
+ genTests = g.tests
531
+ }
532
+
533
+ const samples: string[] = []
534
+ for (let i = 0; i < k; i += 1) {
535
+ const c = await complete(cfg, [{ role: 'user', content: samplePrompt }])
536
+ track(c)
537
+ samples.push(extractCode(c.content))
538
+ }
539
+ const honest: HonestResult[] = []
540
+ for (const s of samples) honest.push(await runHonestOracle(task, s, genTests))
541
+
542
+ let selectedIdx = 0
543
+ for (let i = 1; i < k; i += 1) {
544
+ if (honestScore(honest[i] as HonestResult) > honestScore(honest[selectedIdx] as HonestResult)) selectedIdx = i
545
+ }
546
+
547
+ const selHonest = honest[selectedIdx] as HonestResult
548
+ let best = { code: samples[selectedIdx] as string, honest: selHonest }
549
+ const repairs: WorkerOutcome['repairs'] = []
550
+ let repairStop = 'already-passing'
551
+ if (!selHonest.pass) {
552
+ if (selHonest.attempted === 0) {
553
+ repairStop = 'no-signal'
554
+ } else {
555
+ repairStop = 'rounds-exhausted'
556
+ let current = best
557
+ for (let r = 0; r < maxRepairs; r += 1) {
558
+ const repairPrompt = [
559
+ 'Your Python function failed some of the example checks shown in its own docstring.',
560
+ 'Here is the task again:',
561
+ '```python',
562
+ task.prompt.trimEnd(),
563
+ '```',
564
+ 'Your current attempt:',
565
+ '```python',
566
+ current.code,
567
+ '```',
568
+ 'Result of running the docstring examples against your attempt:',
569
+ '```',
570
+ current.honest.failureOutput.trim() || '(the code crashed before the examples could run)',
571
+ '```',
572
+ 'Fix the function so the docstring examples pass. Output the COMPLETE corrected function definition inside a single ```python code block. Do not write tests or example calls.',
573
+ ].join('\n')
574
+ const c = await complete(cfg, [{ role: 'user', content: repairPrompt }])
575
+ track(c)
576
+ const code = extractRepairCode(c.content)
577
+ const h = await runHonestOracle(task, code, genTests)
578
+ repairs.push({ code, honest: h })
579
+ if (honestScore(h) > honestScore(current.honest)) current = { code, honest: h }
580
+ if (honestScore(current.honest) > honestScore(best.honest)) best = current
581
+ if (h.pass) {
582
+ repairStop = 'repaired-pass'
583
+ break
584
+ }
585
+ }
586
+ }
587
+ }
588
+
589
+ return { samples, honest, selectedIdx, repairs, finalCode: best.code, repairStop, genTests, calls, attempts, tokensIn, tokensOut }
590
+ }
591
+
592
+ // ---------- rows ----------
593
+
594
+ interface ModelSpend {
595
+ model: string
596
+ calls: number
597
+ attempts: number
598
+ tokensIn: number
599
+ tokensOut: number
600
+ }
601
+
602
+ interface ArenaRow {
603
+ arm: string
604
+ set: string
605
+ taskId: string
606
+ k: number
607
+ maxRepairs: number
608
+ temperature: number
609
+ planRaw: string | null
610
+ plan: string | null
611
+ planLeaked: boolean | null
612
+ planEmptyAfterStrip: boolean | null
613
+ evidenceChars: number | null
614
+ genTests: string[]
615
+ samples: string[]
616
+ honest: HonestResult[]
617
+ selectedIdx: number
618
+ repairs: Array<{ code: string; honest: HonestResult }>
619
+ finalCode: string
620
+ repairStop: string
621
+ worker: ModelSpend
622
+ supervisor: ModelSpend | null
623
+ }
624
+
625
+ interface GradedRow extends ArenaRow {
626
+ hiddenSamples: number[]
627
+ hiddenFinal: number
628
+ }
629
+
630
+ // ---------- main ----------
631
+
632
+ const pct = (x: number) => `${(x * 100).toFixed(1)}%`
633
+
634
+ function costLine(s: ModelSpend): string {
635
+ const p = listPrices[s.model]
636
+ const dollars = p ? (s.tokensIn / 1e6) * p.in + (s.tokensOut / 1e6) * p.out : null
637
+ const d = dollars === null ? '$=null (no list price)' : `$${dollars.toFixed(4)} (list $${p?.in}/1M in, $${p?.out}/1M out)`
638
+ return `${s.model}: in=${s.tokensIn} out=${s.tokensOut} calls=${s.calls} (attempts incl. retries=${s.attempts}) → ${d}`
639
+ }
640
+
641
+ async function main(): Promise<void> {
642
+ const arm = must('ARM').toUpperCase()
643
+ if (!['A', 'B', 'C', 'D'].includes(arm)) throw new Error(`ARM must be A|B|C|D, got ${arm}`)
644
+ const setName = must('SET').toLowerCase()
645
+ if (!['hard', 'control'].includes(setName)) throw new Error(`SET must be hard|control, got ${setName}`)
646
+ const ids = (setName === 'hard' ? hardIds : controlIds).slice(0, Number(process.env.N ?? Number.POSITIVE_INFINITY))
647
+ const out = must('OUT')
648
+
649
+ const k = Number(process.env.K ?? 5)
650
+ const maxRepairs = Number(process.env.REPAIRS ?? 2)
651
+ const testGen = Number(process.env.TESTGEN ?? 6)
652
+ const temperature = Number(process.env.TEMPERATURE ?? 0.8)
653
+ const supTemperature = Number(process.env.SUPERVISOR_TEMPERATURE ?? 0.2)
654
+ const solveConc = Number(process.env.CONCURRENCY ?? 6)
655
+ dockerSlots = Number(process.env.DOCKER_CONCURRENCY ?? 6)
656
+ zaiSlots = Math.min(4, Number(process.env.ZAI_CONCURRENCY ?? 4))
657
+ const evidenceMaxChars = Number(process.env.EVIDENCE_MAX_CHARS ?? 12000)
658
+
659
+ const workerModel = process.env.WORKER_MODEL ?? workerModelDefault
660
+ const workerBase = process.env.ROUTER_BASE ?? 'https://api.together.xyz/v1'
661
+ const glmModel = process.env.GLM_MODEL ?? glmModelDefault
662
+ const glmBase = process.env.ZAI_BASE ?? 'https://api.z.ai/api/coding/paas/v4'
663
+ const glmMaxTokens = Math.max(8000, Number(process.env.ZAI_MAX_TOKENS ?? 8000))
664
+
665
+ const needsWorker = arm !== 'D'
666
+ const needsGlm = arm === 'C' || arm === 'D'
667
+ const workerCfg: ClientCfg | null = needsWorker
668
+ ? { base: workerBase, key: process.env.TANGLE_API_KEY ?? must('TOGETHER_API_KEY'), model: workerModel, maxTokens: Number(process.env.MAX_TOKENS ?? 2500), temperature }
669
+ : null
670
+ const glmCfg: ClientCfg | null = needsGlm ? { base: glmBase, key: must('ZAI_API_KEY'), model: glmModel, maxTokens: glmMaxTokens, temperature: supTemperature } : null
671
+
672
+ const all = await loadHumanEval(164, 0)
673
+ const byId = new Map(all.map((t) => [t.taskId, t]))
674
+ const tasks = ids.map((id) => {
675
+ const t = byId.get(id)
676
+ if (!t) throw new Error(`task ${id} not found in HumanEval`)
677
+ return t
678
+ })
679
+
680
+ const needsEvidence = arm === 'B' || arm === 'C'
681
+ let evidence: Map<string, EvidenceRow> | null = null
682
+ if (needsEvidence) {
683
+ evidence = loadEvidence(must('EVIDENCE'))
684
+ for (const id of ids) if (!evidence.has(id)) throw new Error(`evidence row missing for ${id}`)
685
+ }
686
+
687
+ console.log(`=== supervisor-arena · ARM=${arm} SET=${setName} n=${tasks.length} · k=${k} repairs<=${maxRepairs} testgen=${testGen} temp=${temperature} ===`)
688
+ console.log(` worker=${needsWorker ? `${workerModel} @ ${workerBase}` : '(none)'} glm=${needsGlm ? `${glmModel} @ ${glmBase} maxTokens=${glmMaxTokens} temp=${supTemperature} conc<=${zaiSlots}` : '(none)'}`)
689
+ console.log(` llm-conc=${solveConc} docker-conc=${dockerSlots} evidence=${needsEvidence ? `${process.env.EVIDENCE} (<=${evidenceMaxChars} chars rendered)` : '(none)'}`)
690
+ console.log(` Phase A (visible-only arm decisions) → Phase B (hidden nonce-judge grading, script-side)`)
691
+
692
+ // Phase A — per-task faults become error rows (persisted, excluded), >15% aborts loud.
693
+ let done = 0
694
+ let errCount = 0
695
+ const outcomes = await pool(tasks, solveConc, async (task): Promise<ArenaRow | { taskId: string; error: string }> => {
696
+ try {
697
+ let planRaw: string | null = null
698
+ let plan: string | null = null
699
+ let planLeaked: boolean | null = null
700
+ let planEmptyAfterStrip: boolean | null = null
701
+ let evidenceChars: number | null = null
702
+ let supervisor: ModelSpend | null = null
703
+
704
+ if (arm === 'B' || arm === 'C') {
705
+ const ev = (evidence as Map<string, EvidenceRow>).get(task.taskId) as EvidenceRow
706
+ const rendered = renderEvidence(task, ev, evidenceMaxChars)
707
+ evidenceChars = rendered.length
708
+ const supCfg: ClientCfg = arm === 'C' ? (glmCfg as ClientCfg) : { ...(workerCfg as ClientCfg), temperature: supTemperature }
709
+ const call = () => complete(supCfg, [{ role: 'user', content: supervisorPrompt(rendered) }])
710
+ const c = arm === 'C' ? await withZaiSlot(call) : await call()
711
+ supervisor = { model: supCfg.model, calls: 1, attempts: c.attempts, tokensIn: c.tokensIn, tokensOut: c.tokensOut }
712
+ planRaw = c.content
713
+ const stripped = stripPlanCode(c.content)
714
+ plan = stripped.plan
715
+ planLeaked = stripped.leaked
716
+ planEmptyAfterStrip = stripped.leaked && stripped.plan === ''
717
+ }
718
+
719
+ let row: ArenaRow
720
+ if (arm === 'D') {
721
+ const c = await withZaiSlot(() => complete(glmCfg as ClientCfg, [{ role: 'user', content: basePrompt(task) }]))
722
+ const code = extractCode(c.content)
723
+ row = {
724
+ arm, set: setName, taskId: task.taskId, k: 1, maxRepairs: 0, temperature: supTemperature,
725
+ planRaw: null, plan: null, planLeaked: null, planEmptyAfterStrip: null, evidenceChars: null,
726
+ genTests: [], samples: [code], honest: [], selectedIdx: 0, repairs: [], finalCode: code, repairStop: 'solo',
727
+ worker: { model: glmModel, calls: 1, attempts: c.attempts, tokensIn: c.tokensIn, tokensOut: c.tokensOut },
728
+ supervisor: null,
729
+ }
730
+ } else {
731
+ // A plan stripped to nothing carries no steering signal: the loop runs
732
+ // unconditioned (= arm A) and the row records the degenerate plan.
733
+ const preamble = plan !== null && plan !== '' ? `A reviewer analyzed previous failed attempts: ${plan}. Follow this guidance.` : null
734
+ const w = await runWorkerLoop(workerCfg as ClientCfg, task, k, maxRepairs, testGen, preamble)
735
+ row = {
736
+ arm, set: setName, taskId: task.taskId, k, maxRepairs, temperature,
737
+ planRaw, plan, planLeaked, planEmptyAfterStrip, evidenceChars,
738
+ genTests: w.genTests, samples: w.samples, honest: w.honest, selectedIdx: w.selectedIdx,
739
+ repairs: w.repairs, finalCode: w.finalCode, repairStop: w.repairStop,
740
+ worker: { model: workerModel, calls: w.calls, attempts: w.attempts, tokensIn: w.tokensIn, tokensOut: w.tokensOut },
741
+ supervisor,
742
+ }
743
+ }
744
+ done += 1
745
+ appendFileSync(`${out}.phaseA`, `${JSON.stringify(row)}\n`)
746
+ process.stderr.write(
747
+ ` [A ${done}/${tasks.length}] ${task.taskId}: sel=${row.selectedIdx} honest=${row.honest.map((h) => honestScore(h).toFixed(2)).join('/')} repairs=${row.repairs.length} stop=${row.repairStop}${row.planLeaked === null ? '' : ` planLeaked=${row.planLeaked}`}\n`,
748
+ )
749
+ return row
750
+ } catch (e) {
751
+ errCount += 1
752
+ const error = e instanceof Error ? e.message : String(e)
753
+ appendFileSync(`${out}.phaseA`, `${JSON.stringify({ arm, set: setName, taskId: task.taskId, error })}\n`)
754
+ process.stderr.write(` [A ERROR] ${task.taskId}: ${error.slice(0, 160)}\n`)
755
+ if (errCount > Math.max(3, 0.15 * tasks.length)) throw new Error(`aborting: ${errCount} task errors — harness-level fault, not task noise (last: ${error})`)
756
+ return { taskId: task.taskId, error }
757
+ }
758
+ })
759
+
760
+ const okRows = outcomes.filter((o): o is ArenaRow => !('error' in o))
761
+ if (errCount > 0) console.log(` WARNING: ${errCount}/${tasks.length} task(s) errored in Phase A — excluded from stats, recorded in ${out}.phaseA`)
762
+
763
+ console.log(`\n▶ Phase B: hidden grading (${okRows.length} tasks × samples + finals)`)
764
+ const graded: GradedRow[] = await pool(okRows, 16, async (o) => {
765
+ const task = byId.get(o.taskId) as HumanEvalTask
766
+ const hiddenSamples: number[] = []
767
+ for (const s of o.samples) hiddenSamples.push((await runHiddenJudge(task, s)).pass)
768
+ const finalIsSelected = o.finalCode === o.samples[o.selectedIdx]
769
+ const hiddenFinal = finalIsSelected ? (hiddenSamples[o.selectedIdx] as number) : (await runHiddenJudge(task, o.finalCode)).pass
770
+ const g: GradedRow = { ...o, hiddenSamples, hiddenFinal }
771
+ appendFileSync(out, `${JSON.stringify(g)}\n`)
772
+ return g
773
+ })
774
+ console.log(` raw rows appended to ${out} (phase-A rows incl. errors: ${out}.phaseA)`)
775
+
776
+ // ---------- per-arm summary ----------
777
+ const passes = graded.filter((g) => g.hiddenFinal === 1)
778
+ const rateName = setName === 'hard' ? 'rescue rate' : 'retention rate'
779
+ console.log(`\n${'='.repeat(78)}`)
780
+ console.log(`RESULTS · supervisor-arena · ARM=${arm} SET=${setName} n=${graded.length}`)
781
+ console.log('='.repeat(78))
782
+ console.log(` per-problem hiddenFinal (paired analysis input):`)
783
+ console.log(` ${graded.map((g) => `${g.taskId.replace('HumanEval/', 'HE')}=${g.hiddenFinal}`).join(' ')}`)
784
+ console.log(` ${rateName}: ${passes.length}/${graded.length} = ${pct(graded.length > 0 ? passes.length / graded.length : 0)}${setName === 'hard' ? ' (every task in this set was hiddenFinal=0 in the committed run)' : ' (every task in this set was hiddenFinal=1 in the committed run)'}`)
785
+ if (arm === 'B' || arm === 'C') {
786
+ const leaked = graded.filter((g) => g.planLeaked === true)
787
+ const emptied = graded.filter((g) => g.planEmptyAfterStrip === true)
788
+ console.log(` plan leak rate: ${leaked.length}/${graded.length} plans contained code (stripped before use; ${emptied.length} stripped to empty → ran unconditioned)`)
789
+ const evChars = graded.map((g) => g.evidenceChars ?? 0)
790
+ if (evChars.length > 0) console.log(` evidence rendered chars: min=${Math.min(...evChars)} max=${Math.max(...evChars)} (cap ${evidenceMaxChars})`)
791
+ }
792
+ const spend = new Map<string, ModelSpend>()
793
+ const add = (s: ModelSpend | null) => {
794
+ if (!s) return
795
+ const cur = spend.get(s.model) ?? { model: s.model, calls: 0, attempts: 0, tokensIn: 0, tokensOut: 0 }
796
+ cur.calls += s.calls
797
+ cur.attempts += s.attempts
798
+ cur.tokensIn += s.tokensIn
799
+ cur.tokensOut += s.tokensOut
800
+ spend.set(s.model, cur)
801
+ }
802
+ for (const g of graded) {
803
+ add(g.worker)
804
+ add(g.supervisor)
805
+ }
806
+ console.log(` spend per model:`)
807
+ for (const s of spend.values()) console.log(` ${costLine(s)}`)
808
+ const stops = [...new Set(graded.map((g) => g.repairStop))]
809
+ console.log(` repair stops: ${stops.map((st) => `${st}=${graded.filter((g) => g.repairStop === st).length}`).join(', ')}`)
810
+ }
811
+
812
+ main().catch((e) => {
813
+ reapContainers()
814
+ console.error(`supervisor-arena: ${e instanceof Error ? (e.stack ?? e.message) : String(e)}`)
815
+ process.exit(1)
816
+ })