@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,498 @@
1
+ import { access, mkdtemp, mkdir, readFile, rm, writeFile } from 'node:fs/promises'
2
+ import { tmpdir } from 'node:os'
3
+ import { join } from 'node:path'
4
+ import { afterAll, describe, expect, it } from 'vitest'
5
+ import { runSupervisorShim, supervisorDriverKillGraceMs } from './arms.ts'
6
+ import { run, TIMEOUT_RC } from './proc.ts'
7
+
8
+ const dirs: string[] = []
9
+ const workerPids: number[] = []
10
+
11
+ afterAll(async () => {
12
+ for (const pid of workerPids) {
13
+ try {
14
+ process.kill(-pid, 'SIGKILL')
15
+ } catch {
16
+ // The expected path already reaped the process group.
17
+ }
18
+ }
19
+ await Promise.all(dirs.map((dir) => rm(dir, { recursive: true, force: true })))
20
+ })
21
+
22
+ describe('supervisor driver cancellation', () => {
23
+ it('waits for worker cleanup before returning its deadline result', async () => {
24
+ const dir = await mkdtemp(join(tmpdir(), 'swe-driver-cancel-'))
25
+ dirs.push(dir)
26
+ const workspace = join(dir, 'workspace')
27
+ const params = join(dir, 'params.json')
28
+ const extension = join(dir, 'fake-extension.mjs')
29
+ await mkdir(workspace)
30
+ await writeFile(params, '{}')
31
+ const workerScript = `const fs=require('node:fs'); const marker=process.argv[1]; process.on('SIGTERM',()=>setTimeout(()=>{fs.writeFileSync(marker,'clean'); process.exit(0)},30)); setInterval(()=>{},1000)`
32
+ await writeFile(extension, `
33
+ import { spawn } from 'node:child_process'
34
+ import { appendFileSync, mkdirSync, writeFileSync } from 'node:fs'
35
+ import { join } from 'node:path'
36
+
37
+ const id = 'sup-1-fake12'
38
+ const text = (value) => ({ content: [{ type: 'text', text: value }] })
39
+ let child
40
+
41
+ export default function fakeExtension(pi) {
42
+ pi.registerTool({
43
+ name: 'spawn_supervisor',
44
+ async execute(_call, _params, _signal, _update, ctx) {
45
+ const runDir = join(ctx.cwd, '.loops', 'supervisor', id)
46
+ mkdirSync(runDir, { recursive: true })
47
+ writeFileSync(join(runDir, 'state.json'), JSON.stringify({ status: 'running', progress: 'driving' }))
48
+ appendFileSync(join(runDir, 'journal.jsonl'), JSON.stringify({ kind: 'spawned', id, label: 'root' }) + '\\n')
49
+ appendFileSync(join(runDir, 'journal.jsonl'), JSON.stringify({ kind: 'spawned', id: id + ':s1', parent: id, label: 'w-0' }) + '\\n')
50
+ child = spawn(process.execPath, ['-e', ${JSON.stringify(workerScript)}, join(ctx.cwd, 'worker-cleaned.txt')], {
51
+ detached: true,
52
+ stdio: 'ignore',
53
+ })
54
+ writeFileSync(join(ctx.cwd, 'worker.pid'), String(child.pid))
55
+ return text('spawned supervisor ' + id)
56
+ },
57
+ })
58
+ pi.registerTool({
59
+ name: 'supervisor_cancel',
60
+ async execute(_call, _params, _signal, _update, ctx) {
61
+ appendFileSync(join(ctx.cwd, 'cancel-count.txt'), '1\\n')
62
+ process.kill(-child.pid, 'SIGTERM')
63
+ child.once('close', () => {
64
+ const runDir = join(ctx.cwd, '.loops', 'supervisor', id)
65
+ appendFileSync(join(runDir, 'journal.jsonl'), JSON.stringify({ kind: 'cancelled', id: id + ':s1', reason: 'test cancellation' }) + '\\n')
66
+ writeFileSync(join(runDir, 'state.json'), JSON.stringify({ status: 'cancelled', progress: 'cancelled' }))
67
+ writeFileSync(join(ctx.cwd, 'cancel-settled.txt'), 'settled')
68
+ })
69
+ return text('cancellation signalled before worker cleanup')
70
+ },
71
+ })
72
+ }
73
+ `)
74
+
75
+ const result = await run(process.execPath, [
76
+ '--import', 'tsx', runSupervisorShim, extension, workspace, params,
77
+ ], {
78
+ timeoutMs: 5_000,
79
+ killGraceMs: 500,
80
+ env: {
81
+ ...process.env,
82
+ DRIVER_DEADLINE_MS: '40',
83
+ DRIVER_POLL_MS: '10',
84
+ DRIVER_CANCEL_TIMEOUT_MS: '2000',
85
+ },
86
+ })
87
+ const workerPid = Number(await readFile(join(workspace, 'worker.pid'), 'utf8'))
88
+ workerPids.push(workerPid)
89
+
90
+ expect(result.code, result.stdout + result.stderr).toBe(3)
91
+ expect(result.timedOut).toBe(false)
92
+ expect(await readFile(join(workspace, 'cancel-count.txt'), 'utf8')).toBe('1\n')
93
+ expect(await readFile(join(workspace, 'worker-cleaned.txt'), 'utf8')).toBe('clean')
94
+ expect(await readFile(join(workspace, 'cancel-settled.txt'), 'utf8')).toBe('settled')
95
+ expect(JSON.parse(await readFile(join(workspace, '.loops', 'supervisor', 'sup-1-fake12', 'state.json'), 'utf8')))
96
+ .toMatchObject({ status: 'cancelled' })
97
+ await expect(access(`/proc/${workerPid}`)).rejects.toMatchObject({ code: 'ENOENT' })
98
+ })
99
+
100
+ it('lets near-deadline cancellation settle after outer SIGTERM', async () => {
101
+ const dir = await mkdtemp(join(tmpdir(), 'swe-driver-outer-term-'))
102
+ dirs.push(dir)
103
+ const workspace = join(dir, 'workspace')
104
+ const params = join(dir, 'params.json')
105
+ const extension = join(dir, 'fake-extension.mjs')
106
+ await mkdir(workspace)
107
+ await writeFile(params, '{}')
108
+ await writeFile(extension, `
109
+ import { appendFileSync, mkdirSync, writeFileSync } from 'node:fs'
110
+ import { join } from 'node:path'
111
+
112
+ const id = 'sup-2-fake34'
113
+ const text = (value) => ({ content: [{ type: 'text', text: value }] })
114
+ export default function fakeExtension(pi) {
115
+ pi.registerTool({
116
+ name: 'spawn_supervisor',
117
+ async execute(_call, _params, _signal, _update, ctx) {
118
+ const runDir = join(ctx.cwd, '.loops', 'supervisor', id)
119
+ mkdirSync(runDir, { recursive: true })
120
+ writeFileSync(join(runDir, 'state.json'), JSON.stringify({ status: 'running' }))
121
+ appendFileSync(join(runDir, 'journal.jsonl'), JSON.stringify({ kind: 'spawned', id, label: 'root' }) + '\\n')
122
+ appendFileSync(join(runDir, 'journal.jsonl'), JSON.stringify({ kind: 'spawned', id: id + ':s1', parent: id, label: 'w-0' }) + '\\n')
123
+ return text('spawned supervisor ' + id)
124
+ },
125
+ })
126
+ pi.registerTool({
127
+ name: 'supervisor_cancel',
128
+ async execute(_call, _params, _signal, _update, ctx) {
129
+ await new Promise((resolve) => setTimeout(resolve, 1200))
130
+ const runDir = join(ctx.cwd, '.loops', 'supervisor', id)
131
+ appendFileSync(join(runDir, 'journal.jsonl'), JSON.stringify({ kind: 'cancelled', id: id + ':s1' }) + '\\n')
132
+ writeFileSync(join(runDir, 'state.json'), JSON.stringify({ status: 'cancelled' }))
133
+ writeFileSync(join(ctx.cwd, 'outer-cancel-settled.txt'), 'settled')
134
+ return text('cancellation settled')
135
+ },
136
+ })
137
+ }
138
+ `)
139
+
140
+ const cancelTimeoutMs = 1_500
141
+ const result = await run(process.execPath, [
142
+ '--import', 'tsx', runSupervisorShim, extension, workspace, params,
143
+ ], {
144
+ timeoutMs: 1_000,
145
+ killGraceMs: supervisorDriverKillGraceMs({
146
+ DRIVER_CANCEL_TIMEOUT_MS: String(cancelTimeoutMs),
147
+ }),
148
+ env: {
149
+ ...process.env,
150
+ DRIVER_DEADLINE_MS: '60000',
151
+ DRIVER_POLL_MS: '10',
152
+ DRIVER_CANCEL_TIMEOUT_MS: String(cancelTimeoutMs),
153
+ },
154
+ })
155
+
156
+ expect(result.code, result.stdout + result.stderr).toBe(TIMEOUT_RC)
157
+ expect(result.timedOut).toBe(true)
158
+ expect(await readFile(join(workspace, 'outer-cancel-settled.txt'), 'utf8')).toBe('settled')
159
+ expect(JSON.parse(await readFile(join(workspace, '.loops', 'supervisor', 'sup-2-fake34', 'state.json'), 'utf8')))
160
+ .toMatchObject({ status: 'cancelled' })
161
+ }, 15_000)
162
+
163
+ it('latches SIGTERM while spawn_supervisor is still returning and cancels its detached worker', async () => {
164
+ const dir = await mkdtemp(join(tmpdir(), 'swe-driver-signal-during-spawn-'))
165
+ dirs.push(dir)
166
+ const workspace = join(dir, 'workspace')
167
+ const params = join(dir, 'params.json')
168
+ const extension = join(dir, 'fake-extension.mjs')
169
+ await mkdir(workspace)
170
+ await writeFile(params, '{}')
171
+ const workerScript = `const fs=require('node:fs'); const marker=process.argv[1]; process.on('SIGTERM',()=>setTimeout(()=>{fs.writeFileSync(marker,'clean'); process.exit(0)},30)); setInterval(()=>{},1000)`
172
+ await writeFile(extension, `
173
+ import { spawn } from 'node:child_process'
174
+ import { appendFileSync, mkdirSync, writeFileSync } from 'node:fs'
175
+ import { join } from 'node:path'
176
+
177
+ const id = 'sup-3-signal56'
178
+ const text = (value) => ({ content: [{ type: 'text', text: value }] })
179
+ let child
180
+ export default function fakeExtension(pi) {
181
+ pi.registerTool({
182
+ name: 'spawn_supervisor',
183
+ async execute(_call, _params, _signal, _update, ctx) {
184
+ const runDir = join(ctx.cwd, '.loops', 'supervisor', id)
185
+ mkdirSync(runDir, { recursive: true })
186
+ writeFileSync(join(runDir, 'state.json'), JSON.stringify({ status: 'running' }))
187
+ appendFileSync(join(runDir, 'journal.jsonl'), JSON.stringify({ kind: 'spawned', id, label: 'root' }) + '\\n')
188
+ appendFileSync(join(runDir, 'journal.jsonl'), JSON.stringify({ kind: 'spawned', id: id + ':s1', parent: id, label: 'w-0' }) + '\\n')
189
+ child = spawn(process.execPath, ['-e', ${JSON.stringify(workerScript)}, join(ctx.cwd, 'signal-worker-cleaned.txt')], {
190
+ detached: true,
191
+ stdio: 'ignore',
192
+ })
193
+ writeFileSync(join(ctx.cwd, 'signal-worker.pid'), String(child.pid))
194
+ writeFileSync(join(ctx.cwd, 'spawn-entered.txt'), 'entered')
195
+ await new Promise((resolve) => setTimeout(resolve, 30_000))
196
+ return text('spawned supervisor ' + id)
197
+ },
198
+ })
199
+ pi.registerTool({
200
+ name: 'supervisor_cancel',
201
+ async execute(_call, _params, _signal, _update, ctx) {
202
+ process.kill(-child.pid, 'SIGTERM')
203
+ await new Promise((resolve) => child.once('close', resolve))
204
+ const runDir = join(ctx.cwd, '.loops', 'supervisor', id)
205
+ appendFileSync(join(runDir, 'journal.jsonl'), JSON.stringify({ kind: 'cancelled', id: id + ':s1' }) + '\\n')
206
+ writeFileSync(join(runDir, 'state.json'), JSON.stringify({ status: 'cancelled' }))
207
+ writeFileSync(join(ctx.cwd, 'signal-cancel-settled.txt'), 'settled')
208
+ return text('cancellation settled')
209
+ },
210
+ })
211
+ }
212
+ `)
213
+
214
+ const cancelTimeoutMs = 2_000
215
+ const result = await run(process.execPath, [
216
+ '--import', 'tsx', runSupervisorShim, extension, workspace, params,
217
+ ], {
218
+ timeoutMs: 1_000,
219
+ killGraceMs: supervisorDriverKillGraceMs({
220
+ DRIVER_CANCEL_TIMEOUT_MS: String(cancelTimeoutMs),
221
+ }),
222
+ env: {
223
+ ...process.env,
224
+ DRIVER_DEADLINE_MS: '60000',
225
+ DRIVER_POLL_MS: '10',
226
+ DRIVER_CANCEL_TIMEOUT_MS: String(cancelTimeoutMs),
227
+ },
228
+ })
229
+ const workerPid = Number(await readFile(join(workspace, 'signal-worker.pid'), 'utf8'))
230
+ workerPids.push(workerPid)
231
+
232
+ expect(result.code, result.stdout + result.stderr).toBe(TIMEOUT_RC)
233
+ expect(result.timedOut).toBe(true)
234
+ expect(await readFile(join(workspace, 'spawn-entered.txt'), 'utf8')).toBe('entered')
235
+ expect(await readFile(join(workspace, 'signal-worker-cleaned.txt'), 'utf8')).toBe('clean')
236
+ expect(await readFile(join(workspace, 'signal-cancel-settled.txt'), 'utf8')).toBe('settled')
237
+ await expect(access(`/proc/${workerPid}`)).rejects.toMatchObject({ code: 'ENOENT' })
238
+ }, 15_000)
239
+
240
+ it('recovers an on-disk supervisor id from an unparseable spawn response and cancels it', async () => {
241
+ const dir = await mkdtemp(join(tmpdir(), 'swe-driver-id-recovery-'))
242
+ dirs.push(dir)
243
+ const workspace = join(dir, 'workspace')
244
+ const params = join(dir, 'params.json')
245
+ const extension = join(dir, 'fake-extension.mjs')
246
+ await mkdir(workspace)
247
+ await writeFile(params, '{}')
248
+ const workerScript = `const fs=require('node:fs'); const marker=process.argv[1]; process.on('SIGTERM',()=>setTimeout(()=>{fs.writeFileSync(marker,'clean'); process.exit(0)},30)); setInterval(()=>{},1000)`
249
+ await writeFile(extension, `
250
+ import { spawn } from 'node:child_process'
251
+ import { appendFileSync, mkdirSync, writeFileSync } from 'node:fs'
252
+ import { join } from 'node:path'
253
+
254
+ const id = 'sup-4-recover78'
255
+ const text = (value) => ({ content: [{ type: 'text', text: value }] })
256
+ let child
257
+ export default function fakeExtension(pi) {
258
+ pi.registerTool({
259
+ name: 'spawn_supervisor',
260
+ async execute(_call, _params, _signal, _update, ctx) {
261
+ const runDir = join(ctx.cwd, '.loops', 'supervisor', id)
262
+ mkdirSync(runDir, { recursive: true })
263
+ writeFileSync(join(runDir, 'state.json'), JSON.stringify({ status: 'running' }))
264
+ appendFileSync(join(runDir, 'journal.jsonl'), JSON.stringify({ kind: 'spawned', id, label: 'root' }) + '\\n')
265
+ appendFileSync(join(runDir, 'journal.jsonl'), JSON.stringify({ kind: 'spawned', id: id + ':s1', parent: id, label: 'w-0' }) + '\\n')
266
+ child = spawn(process.execPath, ['-e', ${JSON.stringify(workerScript)}, join(ctx.cwd, 'recovered-worker-cleaned.txt')], {
267
+ detached: true,
268
+ stdio: 'ignore',
269
+ })
270
+ writeFileSync(join(ctx.cwd, 'recovered-worker.pid'), String(child.pid))
271
+ return text('the supervisor started, but this response omitted its identifier')
272
+ },
273
+ })
274
+ pi.registerTool({
275
+ name: 'supervisor_cancel',
276
+ async execute(_call, params, _signal, _update, ctx) {
277
+ writeFileSync(join(ctx.cwd, 'recovered-id.txt'), params.id)
278
+ process.kill(-child.pid, 'SIGTERM')
279
+ await new Promise((resolve) => child.once('close', resolve))
280
+ const runDir = join(ctx.cwd, '.loops', 'supervisor', id)
281
+ appendFileSync(join(runDir, 'journal.jsonl'), JSON.stringify({ kind: 'cancelled', id: id + ':s1' }) + '\\n')
282
+ writeFileSync(join(runDir, 'state.json'), JSON.stringify({ status: 'cancelled' }))
283
+ return text('cancellation settled')
284
+ },
285
+ })
286
+ }
287
+ `)
288
+
289
+ const result = await run(process.execPath, [
290
+ '--import', 'tsx', runSupervisorShim, extension, workspace, params,
291
+ ], {
292
+ timeoutMs: 5_000,
293
+ killGraceMs: 500,
294
+ env: {
295
+ ...process.env,
296
+ DRIVER_DEADLINE_MS: '60000',
297
+ DRIVER_POLL_MS: '10',
298
+ DRIVER_CANCEL_TIMEOUT_MS: '2000',
299
+ },
300
+ })
301
+ const workerPid = Number(await readFile(join(workspace, 'recovered-worker.pid'), 'utf8'))
302
+ workerPids.push(workerPid)
303
+
304
+ expect(result.code, result.stdout + result.stderr).toBe(1)
305
+ expect(result.timedOut).toBe(false)
306
+ expect(result.stderr).toMatch(/cancelling recovered run\(s\): sup-4-recover78/)
307
+ expect(await readFile(join(workspace, 'recovered-id.txt'), 'utf8')).toBe('sup-4-recover78')
308
+ await expect(access(`/proc/${workerPid}`)).rejects.toMatchObject({ code: 'ENOENT' })
309
+ }, 15_000)
310
+
311
+ it('waits for a late worker terminal record before accepting completed state', async () => {
312
+ const dir = await mkdtemp(join(tmpdir(), 'swe-driver-late-settlement-'))
313
+ dirs.push(dir)
314
+ const workspace = join(dir, 'workspace')
315
+ const params = join(dir, 'params.json')
316
+ const extension = join(dir, 'fake-extension.mjs')
317
+ await mkdir(workspace)
318
+ await writeFile(params, '{}')
319
+ await writeFile(extension, `
320
+ import { appendFileSync, mkdirSync, writeFileSync } from 'node:fs'
321
+ import { join } from 'node:path'
322
+
323
+ const id = 'sup-5-late90'
324
+ const text = (value) => ({ content: [{ type: 'text', text: value }] })
325
+ export default function fakeExtension(pi) {
326
+ pi.registerTool({
327
+ name: 'spawn_supervisor',
328
+ async execute(_call, _params, _signal, _update, ctx) {
329
+ const runDir = join(ctx.cwd, '.loops', 'supervisor', id)
330
+ mkdirSync(runDir, { recursive: true })
331
+ writeFileSync(join(runDir, 'state.json'), JSON.stringify({ status: 'completed', verdict: 'delivered' }))
332
+ appendFileSync(join(runDir, 'journal.jsonl'), JSON.stringify({ kind: 'spawned', id, label: 'root' }) + '\\n')
333
+ appendFileSync(join(runDir, 'journal.jsonl'), JSON.stringify({ kind: 'spawned', id: id + ':s1', parent: id, label: 'w-0' }) + '\\n')
334
+ setTimeout(() => {
335
+ appendFileSync(join(runDir, 'journal.jsonl'), JSON.stringify({ kind: 'settled', id: id + ':s1', status: 'done' }) + '\\n')
336
+ }, 40)
337
+ return text('spawned supervisor ' + id)
338
+ },
339
+ })
340
+ pi.registerTool({
341
+ name: 'supervisor_cancel',
342
+ async execute() { return text('already completed') },
343
+ })
344
+ }
345
+ `)
346
+
347
+ const result = await run(process.execPath, [
348
+ '--import', 'tsx', runSupervisorShim, extension, workspace, params,
349
+ ], {
350
+ timeoutMs: 5_000,
351
+ killGraceMs: 500,
352
+ env: {
353
+ ...process.env,
354
+ DRIVER_DEADLINE_MS: '60000',
355
+ DRIVER_POLL_MS: '10',
356
+ DRIVER_CANCEL_TIMEOUT_MS: '2000',
357
+ DRIVER_SETTLEMENT_TIMEOUT_MS: '500',
358
+ },
359
+ })
360
+
361
+ expect(result.code, result.stdout + result.stderr).toBe(0)
362
+ expect(result.timedOut).toBe(false)
363
+ expect(result.stdout).toMatch(/worker settlement: workers=1, terminal=1/)
364
+ })
365
+
366
+ it('refuses completed state while a journal worker remains live', async () => {
367
+ const dir = await mkdtemp(join(tmpdir(), 'swe-driver-live-completed-worker-'))
368
+ dirs.push(dir)
369
+ const workspace = join(dir, 'workspace')
370
+ const params = join(dir, 'params.json')
371
+ const extension = join(dir, 'fake-extension.mjs')
372
+ await mkdir(workspace)
373
+ await writeFile(params, '{}')
374
+ await writeFile(extension, `
375
+ import { appendFileSync, mkdirSync, writeFileSync } from 'node:fs'
376
+ import { join } from 'node:path'
377
+
378
+ const id = 'sup-5-live90'
379
+ const text = (value) => ({ content: [{ type: 'text', text: value }] })
380
+ export default function fakeExtension(pi) {
381
+ pi.registerTool({
382
+ name: 'spawn_supervisor',
383
+ async execute(_call, _params, _signal, _update, ctx) {
384
+ const runDir = join(ctx.cwd, '.loops', 'supervisor', id)
385
+ mkdirSync(runDir, { recursive: true })
386
+ writeFileSync(join(runDir, 'state.json'), JSON.stringify({ status: 'completed', verdict: 'delivered' }))
387
+ appendFileSync(join(runDir, 'journal.jsonl'), JSON.stringify({ kind: 'spawned', id, label: 'root' }) + '\\n')
388
+ appendFileSync(join(runDir, 'journal.jsonl'), JSON.stringify({ kind: 'spawned', id: id + ':s1', parent: id, label: 'w-0' }) + '\\n')
389
+ return text('spawned supervisor ' + id)
390
+ },
391
+ })
392
+ pi.registerTool({
393
+ name: 'supervisor_cancel',
394
+ async execute() { return text('already completed') },
395
+ })
396
+ }
397
+ `)
398
+
399
+ const result = await run(process.execPath, [
400
+ '--import', 'tsx', runSupervisorShim, extension, workspace, params,
401
+ ], {
402
+ timeoutMs: 5_000,
403
+ killGraceMs: 500,
404
+ env: {
405
+ ...process.env,
406
+ DRIVER_DEADLINE_MS: '60000',
407
+ DRIVER_POLL_MS: '10',
408
+ DRIVER_CANCEL_TIMEOUT_MS: '2000',
409
+ DRIVER_SETTLEMENT_TIMEOUT_MS: '80',
410
+ },
411
+ })
412
+
413
+ expect(result.code, result.stdout + result.stderr).toBe(4)
414
+ expect(result.timedOut).toBe(false)
415
+ expect(result.stderr).toMatch(/refusing completed supervisor/)
416
+ expect(result.stderr).toMatch(/workers=1, terminal=0, unsettled=1/)
417
+ expect(result.stdout).not.toContain('[done] id=')
418
+ })
419
+
420
+ it.each(['failed', 'cancelled'] as const)(
421
+ 'settles a detached worker before returning terminal status $status',
422
+ async (status) => {
423
+ const dir = await mkdtemp(join(tmpdir(), `swe-driver-${status}-worker-`))
424
+ dirs.push(dir)
425
+ const workspace = join(dir, 'workspace')
426
+ const params = join(dir, 'params.json')
427
+ const extension = join(dir, 'fake-extension.mjs')
428
+ await mkdir(workspace)
429
+ await writeFile(params, '{}')
430
+ const workerScript = `const fs=require('node:fs'); const marker=process.argv[1]; const ready=process.argv[2]; process.on('SIGTERM',()=>setTimeout(()=>{fs.writeFileSync(marker,'clean'); process.exit(0)},30)); fs.writeFileSync(ready,'ready'); setInterval(()=>{},1000)`
431
+ await writeFile(extension, `
432
+ import { spawn } from 'node:child_process'
433
+ import { appendFileSync, existsSync, mkdirSync, writeFileSync } from 'node:fs'
434
+ import { join } from 'node:path'
435
+
436
+ const id = 'sup-6-${status}12'
437
+ const text = (value) => ({ content: [{ type: 'text', text: value }] })
438
+ let child
439
+ export default function fakeExtension(pi) {
440
+ pi.registerTool({
441
+ name: 'spawn_supervisor',
442
+ async execute(_call, _params, _signal, _update, ctx) {
443
+ const runDir = join(ctx.cwd, '.loops', 'supervisor', id)
444
+ mkdirSync(runDir, { recursive: true })
445
+ appendFileSync(join(runDir, 'journal.jsonl'), JSON.stringify({ kind: 'spawned', id, label: 'root' }) + '\\n')
446
+ appendFileSync(join(runDir, 'journal.jsonl'), JSON.stringify({ kind: 'spawned', id: id + ':s1', parent: id, label: 'w-0' }) + '\\n')
447
+ const readyPath = join(ctx.cwd, 'terminal-worker-ready.txt')
448
+ child = spawn(process.execPath, ['-e', ${JSON.stringify(workerScript)}, join(ctx.cwd, 'terminal-worker-cleaned.txt'), readyPath], {
449
+ detached: true,
450
+ stdio: 'ignore',
451
+ })
452
+ writeFileSync(join(ctx.cwd, 'terminal-worker.pid'), String(child.pid))
453
+ while (!existsSync(readyPath)) await new Promise((resolve) => setTimeout(resolve, 5))
454
+ writeFileSync(join(runDir, 'state.json'), JSON.stringify({ status: '${status}', error: 'fixture terminal state' }))
455
+ return text('spawned supervisor ' + id)
456
+ },
457
+ })
458
+ pi.registerTool({
459
+ name: 'supervisor_cancel',
460
+ async execute(_call, _params, _signal, _update, ctx) {
461
+ appendFileSync(join(ctx.cwd, 'terminal-cancel-count.txt'), '1\\n')
462
+ process.kill(-child.pid, 'SIGTERM')
463
+ await new Promise((resolve) => child.once('close', resolve))
464
+ const runDir = join(ctx.cwd, '.loops', 'supervisor', id)
465
+ appendFileSync(join(runDir, 'journal.jsonl'), JSON.stringify({ kind: 'cancelled', id: id + ':s1' }) + '\\n')
466
+ writeFileSync(join(runDir, 'state.json'), JSON.stringify({ status: 'cancelled' }))
467
+ return text('terminal worker settled')
468
+ },
469
+ })
470
+ }
471
+ `)
472
+
473
+ const result = await run(process.execPath, [
474
+ '--import', 'tsx', runSupervisorShim, extension, workspace, params,
475
+ ], {
476
+ timeoutMs: 5_000,
477
+ killGraceMs: 500,
478
+ env: {
479
+ ...process.env,
480
+ DRIVER_DEADLINE_MS: '60000',
481
+ DRIVER_POLL_MS: '10',
482
+ DRIVER_CANCEL_TIMEOUT_MS: '2000',
483
+ },
484
+ })
485
+ const workerPid = Number(await readFile(join(workspace, 'terminal-worker.pid'), 'utf8'))
486
+ workerPids.push(workerPid)
487
+
488
+ expect(result.code, result.stdout + result.stderr).toBe(1)
489
+ expect(result.timedOut).toBe(false)
490
+ expect(result.stdout).toContain(`[done] id=sup-6-${status}12 status=${status}`)
491
+ expect(result.stdout).toContain('worker settlement: workers=1, terminal=1')
492
+ expect(await readFile(join(workspace, 'terminal-cancel-count.txt'), 'utf8')).toBe('1\n')
493
+ expect(await readFile(join(workspace, 'terminal-worker-cleaned.txt'), 'utf8')).toBe('clean')
494
+ await expect(access(`/proc/${workerPid}`)).rejects.toMatchObject({ code: 'ENOENT' })
495
+ },
496
+ 15_000,
497
+ )
498
+ })