@tangle-network/agent-bench 0.1.0 → 0.3.6
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.
- package/CHANGELOG.md +23 -0
- package/HARNESS.md +302 -0
- package/README.md +39 -2
- package/dist/adapters.d.ts +15 -0
- package/dist/adapters.js +43 -0
- package/dist/adapters.js.map +1 -0
- package/dist/benchmarks/_harness.d.ts +125 -0
- package/dist/benchmarks/_harness.js +33 -0
- package/dist/benchmarks/_harness.js.map +1 -0
- package/dist/benchmarks/aec-bench.d.ts +27 -0
- package/dist/benchmarks/aec-bench.js +8 -0
- package/dist/benchmarks/aec-bench.js.map +1 -0
- package/dist/benchmarks/agentbench.d.ts +16 -0
- package/dist/benchmarks/agentbench.js +10 -0
- package/dist/benchmarks/agentbench.js.map +1 -0
- package/dist/benchmarks/appworld.d.ts +37 -0
- package/dist/benchmarks/appworld.js +14 -0
- package/dist/benchmarks/appworld.js.map +1 -0
- package/dist/benchmarks/bfcl.d.ts +18 -0
- package/dist/benchmarks/bfcl.js +10 -0
- package/dist/benchmarks/bfcl.js.map +1 -0
- package/dist/benchmarks/cad-design.d.ts +45 -0
- package/dist/benchmarks/cad-design.js +7 -0
- package/dist/benchmarks/cad-design.js.map +1 -0
- package/dist/benchmarks/cadbench.d.ts +19 -0
- package/dist/benchmarks/cadbench.js +8 -0
- package/dist/benchmarks/cadbench.js.map +1 -0
- package/dist/benchmarks/cadgenbench.d.ts +22 -0
- package/dist/benchmarks/cadgenbench.js +8 -0
- package/dist/benchmarks/cadgenbench.js.map +1 -0
- package/dist/benchmarks/commit0.d.ts +31 -0
- package/dist/benchmarks/commit0.js +10 -0
- package/dist/benchmarks/commit0.js.map +1 -0
- package/dist/benchmarks/crag.d.ts +14 -0
- package/dist/benchmarks/crag.js +9 -0
- package/dist/benchmarks/crag.js.map +1 -0
- package/dist/benchmarks/dabstep.d.ts +18 -0
- package/dist/benchmarks/dabstep.js +10 -0
- package/dist/benchmarks/dabstep.js.map +1 -0
- package/dist/benchmarks/enterpriseops-gym.d.ts +38 -0
- package/dist/benchmarks/enterpriseops-gym.js +10 -0
- package/dist/benchmarks/enterpriseops-gym.js.map +1 -0
- package/dist/benchmarks/finresearchbench.d.ts +15 -0
- package/dist/benchmarks/finresearchbench.js +8 -0
- package/dist/benchmarks/finresearchbench.js.map +1 -0
- package/dist/benchmarks/finsearchcomp.d.ts +49 -0
- package/dist/benchmarks/finsearchcomp.js +7 -0
- package/dist/benchmarks/finsearchcomp.js.map +1 -0
- package/dist/benchmarks/frames.d.ts +59 -0
- package/dist/benchmarks/frames.js +13 -0
- package/dist/benchmarks/frames.js.map +1 -0
- package/dist/benchmarks/hotpotqa.d.ts +48 -0
- package/dist/benchmarks/hotpotqa.js +15 -0
- package/dist/benchmarks/hotpotqa.js.map +1 -0
- package/dist/benchmarks/humaneval.d.ts +53 -0
- package/dist/benchmarks/humaneval.js +15 -0
- package/dist/benchmarks/humaneval.js.map +1 -0
- package/dist/benchmarks/mind2web.d.ts +41 -0
- package/dist/benchmarks/mind2web.js +9 -0
- package/dist/benchmarks/mind2web.js.map +1 -0
- package/dist/benchmarks/nomiracl.d.ts +15 -0
- package/dist/benchmarks/nomiracl.js +9 -0
- package/dist/benchmarks/nomiracl.js.map +1 -0
- package/dist/benchmarks/open-rag-bench.d.ts +14 -0
- package/dist/benchmarks/open-rag-bench.js +9 -0
- package/dist/benchmarks/open-rag-bench.js.map +1 -0
- package/dist/benchmarks/programbench.d.ts +38 -0
- package/dist/benchmarks/programbench.js +10 -0
- package/dist/benchmarks/programbench.js.map +1 -0
- package/dist/benchmarks/rag-shared.d.ts +42 -0
- package/dist/benchmarks/rag-shared.js +39 -0
- package/dist/benchmarks/rag-shared.js.map +1 -0
- package/dist/benchmarks/ragbench.d.ts +16 -0
- package/dist/benchmarks/ragbench.js +9 -0
- package/dist/benchmarks/ragbench.js.map +1 -0
- package/dist/benchmarks/simpleqa.d.ts +64 -0
- package/dist/benchmarks/simpleqa.js +11 -0
- package/dist/benchmarks/simpleqa.js.map +1 -0
- package/dist/benchmarks/swe-bench.d.ts +56 -0
- package/dist/benchmarks/swe-bench.js +14 -0
- package/dist/benchmarks/swe-bench.js.map +1 -0
- package/dist/benchmarks/t2-ragbench.d.ts +14 -0
- package/dist/benchmarks/t2-ragbench.js +9 -0
- package/dist/benchmarks/t2-ragbench.js.map +1 -0
- package/dist/benchmarks/tau-bench-shared.d.ts +26 -0
- package/dist/benchmarks/tau-bench-shared.js +10 -0
- package/dist/benchmarks/tau-bench-shared.js.map +1 -0
- package/dist/benchmarks/tau2-bench.d.ts +7 -0
- package/dist/benchmarks/tau2-bench.js +11 -0
- package/dist/benchmarks/tau2-bench.js.map +1 -0
- package/dist/benchmarks/tau3-banking.d.ts +15 -0
- package/dist/benchmarks/tau3-banking.js +9 -0
- package/dist/benchmarks/tau3-banking.js.map +1 -0
- package/dist/benchmarks/terminal-bench.d.ts +24 -0
- package/dist/benchmarks/terminal-bench.js +8 -0
- package/dist/benchmarks/terminal-bench.js.map +1 -0
- package/dist/benchmarks/toollm.d.ts +16 -0
- package/dist/benchmarks/toollm.js +10 -0
- package/dist/benchmarks/toollm.js.map +1 -0
- package/dist/benchmarks/trata-hedge.d.ts +32 -0
- package/dist/benchmarks/trata-hedge.js +7 -0
- package/dist/benchmarks/trata-hedge.js.map +1 -0
- package/dist/benchmarks/types.d.ts +107 -0
- package/dist/benchmarks/types.js +1 -0
- package/dist/benchmarks/types.js.map +1 -0
- package/dist/benchmarks/webarena-verified.d.ts +16 -0
- package/dist/benchmarks/webarena-verified.js +10 -0
- package/dist/benchmarks/webarena-verified.js.map +1 -0
- package/dist/chunk-2PVVP7GN.js +197 -0
- package/dist/chunk-2PVVP7GN.js.map +1 -0
- package/dist/chunk-2XU6OGEN.js +170 -0
- package/dist/chunk-2XU6OGEN.js.map +1 -0
- package/dist/chunk-53UPUNBZ.js +325 -0
- package/dist/chunk-53UPUNBZ.js.map +1 -0
- package/dist/chunk-5SBJCB6W.js +144 -0
- package/dist/chunk-5SBJCB6W.js.map +1 -0
- package/dist/chunk-7WSD27QQ.js +118 -0
- package/dist/chunk-7WSD27QQ.js.map +1 -0
- package/dist/chunk-C7T7WEK2.js +103 -0
- package/dist/chunk-C7T7WEK2.js.map +1 -0
- package/dist/chunk-CKUVRZ2T.js +251 -0
- package/dist/chunk-CKUVRZ2T.js.map +1 -0
- package/dist/chunk-HBSWHQNJ.js +30 -0
- package/dist/chunk-HBSWHQNJ.js.map +1 -0
- package/dist/chunk-HHXFIHXC.js +116 -0
- package/dist/chunk-HHXFIHXC.js.map +1 -0
- package/dist/chunk-IFAV6KEM.js +276 -0
- package/dist/chunk-IFAV6KEM.js.map +1 -0
- package/dist/chunk-INNOYXCP.js +387 -0
- package/dist/chunk-INNOYXCP.js.map +1 -0
- package/dist/chunk-J3KDJNX2.js +182 -0
- package/dist/chunk-J3KDJNX2.js.map +1 -0
- package/dist/chunk-JRWWGMK7.js +148 -0
- package/dist/chunk-JRWWGMK7.js.map +1 -0
- package/dist/chunk-JTHWEDEW.js +32 -0
- package/dist/chunk-JTHWEDEW.js.map +1 -0
- package/dist/chunk-KDIKRJGB.js +120 -0
- package/dist/chunk-KDIKRJGB.js.map +1 -0
- package/dist/chunk-LRRD7NAG.js +301 -0
- package/dist/chunk-LRRD7NAG.js.map +1 -0
- package/dist/chunk-ODT47UAY.js +221 -0
- package/dist/chunk-ODT47UAY.js.map +1 -0
- package/dist/chunk-PA2ZKHJC.js +230 -0
- package/dist/chunk-PA2ZKHJC.js.map +1 -0
- package/dist/chunk-PPYSEKFM.js +182 -0
- package/dist/chunk-PPYSEKFM.js.map +1 -0
- package/dist/chunk-PUIRNYI7.js +189 -0
- package/dist/chunk-PUIRNYI7.js.map +1 -0
- package/dist/chunk-R36V2VP7.js +169 -0
- package/dist/chunk-R36V2VP7.js.map +1 -0
- package/dist/chunk-R67DFVLO.js +142 -0
- package/dist/chunk-R67DFVLO.js.map +1 -0
- package/dist/chunk-SEVJPLZC.js +260 -0
- package/dist/chunk-SEVJPLZC.js.map +1 -0
- package/dist/chunk-SYDW647C.js +318 -0
- package/dist/chunk-SYDW647C.js.map +1 -0
- package/dist/chunk-TBKU5XQI.js +228 -0
- package/dist/chunk-TBKU5XQI.js.map +1 -0
- package/dist/chunk-TSWPNOYM.js +147 -0
- package/dist/chunk-TSWPNOYM.js.map +1 -0
- package/dist/chunk-UAIOHCUK.js +27 -0
- package/dist/chunk-UAIOHCUK.js.map +1 -0
- package/dist/chunk-UPAMRDX4.js +233 -0
- package/dist/chunk-UPAMRDX4.js.map +1 -0
- package/dist/chunk-VQRS7VUC.js +342 -0
- package/dist/chunk-VQRS7VUC.js.map +1 -0
- package/dist/chunk-X3BTXCJ4.js +262 -0
- package/dist/chunk-X3BTXCJ4.js.map +1 -0
- package/dist/chunk-X5YKXC6V.js +211 -0
- package/dist/chunk-X5YKXC6V.js.map +1 -0
- package/dist/chunk-Y6O2OCUO.js +130 -0
- package/dist/chunk-Y6O2OCUO.js.map +1 -0
- package/dist/chunk-YCGY7UIZ.js +208 -0
- package/dist/chunk-YCGY7UIZ.js.map +1 -0
- package/dist/chunk-Z7ML6L77.js +162 -0
- package/dist/chunk-Z7ML6L77.js.map +1 -0
- package/dist/chunk-ZEWMTR5M.js +136 -0
- package/dist/chunk-ZEWMTR5M.js.map +1 -0
- package/dist/index.d.ts +355 -0
- package/dist/index.js +1908 -0
- package/dist/index.js.map +1 -0
- package/fixtures/aec-bench.json +18 -0
- package/fixtures/agentbench-dbbench.json +22 -0
- package/fixtures/bfcl.json +45 -0
- package/fixtures/commit0.json +72 -0
- package/fixtures/crag.json +10 -0
- package/fixtures/dabstep.json +22 -0
- package/fixtures/enterpriseops-gym.json +103 -0
- package/fixtures/finresearchbench.json +21 -0
- package/fixtures/finsearchcomp.json +66 -0
- package/fixtures/frames.json +26 -0
- package/fixtures/hotpotqa.json +182 -0
- package/fixtures/nomiracl.json +26 -0
- package/fixtures/open-rag-bench.json +16 -0
- package/fixtures/pier-agent/no-model-task/environment/Dockerfile +16 -0
- package/fixtures/pier-agent/no-model-task/environment/seed/src/status.txt +1 -0
- package/fixtures/pier-agent/no-model-task/instruction.md +6 -0
- package/fixtures/pier-agent/no-model-task/pre_artifacts.sh +6 -0
- package/fixtures/pier-agent/no-model-task/task.toml +35 -0
- package/fixtures/pier-agent/no-model-task/tests/Dockerfile +17 -0
- package/fixtures/pier-agent/no-model-task/tests/seed/src/status.txt +1 -0
- package/fixtures/pier-agent/no-model-task/tests/test.sh +19 -0
- package/fixtures/programbench.json +17 -0
- package/fixtures/ragbench.json +21 -0
- package/fixtures/simpleqa.json +121 -0
- package/fixtures/t2-ragbench.json +13 -0
- package/fixtures/tau2-bench.json +16 -0
- package/fixtures/tau3-banking.json +16 -0
- package/fixtures/toollm.json +28 -0
- package/fixtures/webarena-verified.json +20 -0
- package/package.json +60 -20
- package/pier_agents/__init__.py +18 -0
- package/pier_agents/candidate_contract.py +755 -0
- package/pier_agents/process_boundary.py +321 -0
- package/pier_agents/tangle_candidate.py +907 -0
- package/pier_agents/workspace_boundary.py +368 -0
- package/scripts/appworld_driver.py +359 -0
- package/scripts/cadbench_prepare.py +22 -0
- package/scripts/cadgenbench_hard_parts.py +48 -0
- package/scripts/clbench_codebase_judge.py +73 -0
- package/scripts/commit0_judge.py +170 -0
- package/scripts/dabstep_judge.py +42 -0
- package/scripts/enterpriseops_gym_judge.py +281 -0
- package/scripts/programbench_judge.py +120 -0
- package/scripts/render-gate-chart.mjs +176 -0
- package/scripts/run-package-tests.mjs +56 -0
- package/scripts/terminate-pier-trial.mts +66 -0
- package/scripts/trata-hedge/README.md +56 -0
- package/scripts/trata-hedge/run.sh +60 -0
- package/scripts/trata-hedge/solve.py +83 -0
- package/scripts/verify-packed-consumer.mjs +235 -0
- package/scripts/verify-pier-agent.mts +715 -0
- package/scripts/verify-pier-pair.mts +74 -0
- package/scripts/verify-pier-recovery.mts +139 -0
- package/src/adapters.ts +26 -0
- package/src/benchmarks/_harness.test.mts +178 -0
- package/src/benchmarks/_harness.ts +239 -16
- package/src/benchmarks/agentbench.ts +163 -0
- package/src/benchmarks/appworld.test.mts +15 -9
- package/src/benchmarks/bfcl.ts +346 -0
- package/src/benchmarks/crag.ts +137 -0
- package/src/benchmarks/dabstep.test.mts +70 -0
- package/src/benchmarks/dabstep.ts +212 -0
- package/src/benchmarks/external-adapters.test.mts +150 -0
- package/src/benchmarks/finresearchbench.ts +269 -0
- package/src/benchmarks/humaneval.ts +20 -8
- package/src/benchmarks/nomiracl.ts +180 -0
- package/src/benchmarks/open-rag-bench.ts +153 -0
- package/src/benchmarks/rag-benchmarks.test.mts +138 -0
- package/src/benchmarks/rag-shared.ts +327 -0
- package/src/benchmarks/ragbench.ts +171 -0
- package/src/benchmarks/swe-bench.test.mts +61 -0
- package/src/benchmarks/swe-bench.ts +201 -19
- package/src/benchmarks/t2-ragbench.ts +166 -0
- package/src/benchmarks/tau-bench-shared.ts +214 -0
- package/src/benchmarks/tau2-bench.ts +30 -0
- package/src/benchmarks/tau3-banking.ts +29 -0
- package/src/benchmarks/terminal-bench.test.mts +33 -0
- package/src/benchmarks/terminal-bench.ts +23 -8
- package/src/benchmarks/toollm.ts +254 -0
- package/src/benchmarks/types.ts +42 -0
- package/src/benchmarks/webarena-verified.ts +200 -0
- package/src/commit0-prereqs.sh +0 -0
- package/src/coordination-mcp-container-reach.mts +181 -0
- package/src/decoder-live.mts +1 -1
- package/src/examples/README.md +103 -39
- package/src/examples/benchmark-matrix.mts +101 -0
- package/src/examples/lean-proof-gate.README.md +77 -0
- package/src/examples/lean-proof-gate.mts +162 -0
- package/src/examples/lean-verify.ts +95 -0
- package/src/examples/lean.Dockerfile +12 -0
- package/src/examples/math-demo.mts +9 -7
- package/src/examples/strategy-demo.mts +10 -12
- package/src/gate.ts +3 -2
- package/src/hev-eval.mts +69 -0
- package/src/hev-improve.mts +169 -0
- package/src/hev-structural.mts +688 -0
- package/src/index.ts +73 -0
- package/src/mbpp-structural.mts +662 -0
- package/src/pier-agent.test-fixtures.mts +19 -0
- package/src/pier-agent.test.mts +363 -0
- package/src/pier-agent.ts +657 -0
- package/src/pier-result-grader.mjs +30 -0
- package/src/pier-result-grader.test.mts +62 -0
- package/src/pier-result-grader.ts +108 -0
- package/src/pier-task-outcome.test.mts +117 -0
- package/src/pier-task-outcome.ts +240 -0
- package/src/pier-trial-controller.test.mts +412 -0
- package/src/pier-trial-controller.ts +858 -0
- package/src/pier-trial-supervisor.mjs +352 -0
- package/src/resolve-client.ts +25 -2
- package/src/run-benchmarks-cli.mts +72 -0
- package/src/run-benchmarks-report.ts +66 -0
- package/src/run-benchmarks.test.mts +231 -0
- package/src/run-benchmarks.ts +589 -0
- package/src/smoke-structural-rollout.mts +393 -0
- package/src/swe-bench-env.test.ts +207 -0
- package/src/swe-bench-env.ts +554 -0
- package/src/swe-jail.ts +293 -0
- package/src/swe-self-improve.mts +84 -0
- package/src/swe-structural-judge-policy.test.ts +117 -0
- package/src/swe-structural-judge-policy.ts +133 -0
- package/src/swe-structural-policy.test.ts +124 -0
- package/src/swe-structural-policy.ts +132 -0
- package/src/swe-structural-provenance.test.ts +93 -0
- package/src/swe-structural-provenance.ts +138 -0
- package/src/swe-structural.mts +1260 -0
- package/src/swe-temp.ts +14 -0
- package/src/tb-container-executor.mts +234 -0
- package/src/tb-container-executor.test.mts +99 -0
- package/src/tb-supervisor-sidecar.mts +222 -0
- package/src/trata-gepa.mts +1 -1
- package/steerers/eops-itsm-population.json +1 -0
- package/tb_agents/opencode_refine_agent.py +117 -0
- package/tb_agents/opencode_router_agent.py +406 -0
- package/tb_agents/opencode_supervisor_agent.py +239 -0
- package/tb_agents/script_agent.py +66 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import assert from 'node:assert/strict'
|
|
2
|
+
import { describe, it } from 'node:test'
|
|
3
|
+
import { assertNoHiddenLeak } from './swe-jail'
|
|
4
|
+
import {
|
|
5
|
+
assertExactCompletedWorkerSessions,
|
|
6
|
+
continuationDisposition,
|
|
7
|
+
continuationStateNotice,
|
|
8
|
+
preferLaterCandidate,
|
|
9
|
+
resolveExperimentArm,
|
|
10
|
+
resolveExperimentTemperature,
|
|
11
|
+
shouldAcceptContinuation,
|
|
12
|
+
shouldRunContinuation,
|
|
13
|
+
} from './swe-structural-policy'
|
|
14
|
+
|
|
15
|
+
describe('structural experiment arms', () => {
|
|
16
|
+
it('derives exactly two independent sessions without repair', () => {
|
|
17
|
+
assert.deepEqual(resolveExperimentArm('independent-2'), {
|
|
18
|
+
arm: 'independent-2',
|
|
19
|
+
k: 2,
|
|
20
|
+
repairs: 0,
|
|
21
|
+
alwaysRunContinuation: false,
|
|
22
|
+
persistent: false,
|
|
23
|
+
workerSessions: 2,
|
|
24
|
+
})
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it('derives exactly one initial plus one persistent continuation session', () => {
|
|
28
|
+
const preset = resolveExperimentArm('persistent-refine-2')
|
|
29
|
+
assert.deepEqual(preset, {
|
|
30
|
+
arm: 'persistent-refine-2',
|
|
31
|
+
k: 1,
|
|
32
|
+
repairs: 1,
|
|
33
|
+
alwaysRunContinuation: true,
|
|
34
|
+
persistent: true,
|
|
35
|
+
workerSessions: 2,
|
|
36
|
+
})
|
|
37
|
+
assert.equal(shouldRunContinuation({ round: 1, preset }), true)
|
|
38
|
+
assert.equal(shouldRunContinuation({ round: 2, preset }), false)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('rejects every untyped arm instead of accepting free-form K/repair settings', () => {
|
|
42
|
+
assert.throws(() => resolveExperimentArm('system'), /independent-2\|persistent-refine-2/)
|
|
43
|
+
assert.throws(() => resolveExperimentArm(''), /independent-2\|persistent-refine-2/)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('uses TEMPERATURE without repurposing Node TEMP as a model knob', () => {
|
|
47
|
+
assert.equal(resolveExperimentTemperature({}), 0.8)
|
|
48
|
+
assert.equal(resolveExperimentTemperature({ TEMPERATURE: '0.35' }), 0.35)
|
|
49
|
+
assert.equal(resolveExperimentTemperature({ TEMP: '/tmp', TEMPERATURE: '0.35' }), 0.35)
|
|
50
|
+
assert.throws(
|
|
51
|
+
() => resolveExperimentTemperature({ TEMP: '0.8' }),
|
|
52
|
+
/TEMP is the operating-system temporary-directory root.*TEMPERATURE/,
|
|
53
|
+
)
|
|
54
|
+
assert.throws(() => resolveExperimentTemperature({ TEMPERATURE: 'hot' }), /finite number/)
|
|
55
|
+
})
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
describe('shared later-on-visible-tie policy', () => {
|
|
59
|
+
it('prefers session two on a visible tie in both arm shapes', () => {
|
|
60
|
+
assert.equal(preferLaterCandidate(0, 0), true)
|
|
61
|
+
assert.equal(preferLaterCandidate(1, 1), true)
|
|
62
|
+
assert.equal(shouldAcceptContinuation(0, 0), true)
|
|
63
|
+
assert.equal(shouldAcceptContinuation(1, 1), true)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
it('prefers a visible improvement and rejects a visible regression', () => {
|
|
67
|
+
assert.equal(preferLaterCandidate(1, 0), true)
|
|
68
|
+
assert.equal(preferLaterCandidate(0, 1), false)
|
|
69
|
+
assert.equal(shouldAcceptContinuation(1, 0), true)
|
|
70
|
+
assert.equal(shouldAcceptContinuation(0, 1), false)
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
it('never labels an identical second patch as a refinement', () => {
|
|
74
|
+
assert.deepEqual(continuationDisposition(true, false), {
|
|
75
|
+
finalFrom: 'session:2-identical',
|
|
76
|
+
stop: 'continuation-accepted-identical',
|
|
77
|
+
})
|
|
78
|
+
assert.equal(continuationDisposition(true, true).finalFrom, 'session:2-refinement')
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
it('inherits parent state without echoing parent patch bytes into session two', () => {
|
|
82
|
+
const parent = 'diff --git a/a.py b/a.py\n+gold-like-added-line\n'
|
|
83
|
+
const notice = continuationStateNotice(parent)
|
|
84
|
+
assert.match(notice, /already applied to this checkout/)
|
|
85
|
+
assert.equal(notice.includes('gold-like-added-line'), false)
|
|
86
|
+
assert.doesNotThrow(() => assertNoHiddenLeak(['+gold-like-added-line'], [{ role: 'user', content: notice }]))
|
|
87
|
+
})
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
describe('exact completed worker compute', () => {
|
|
91
|
+
const complete = { calls: 1, completions: 1, attemptError: null }
|
|
92
|
+
|
|
93
|
+
it('admits exactly two completed model-backed sessions', () => {
|
|
94
|
+
assert.doesNotThrow(() =>
|
|
95
|
+
assertExactCompletedWorkerSessions({
|
|
96
|
+
started: 2,
|
|
97
|
+
completed: 2,
|
|
98
|
+
sessions: [complete, complete],
|
|
99
|
+
context: 'row',
|
|
100
|
+
}),
|
|
101
|
+
)
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
it('rejects an attempted-but-failed or zero-call session', () => {
|
|
105
|
+
assert.throws(() =>
|
|
106
|
+
assertExactCompletedWorkerSessions({
|
|
107
|
+
started: 2,
|
|
108
|
+
completed: 1,
|
|
109
|
+
sessions: [complete, { calls: 0, completions: 0, attemptError: 'timeout' }],
|
|
110
|
+
context: 'row',
|
|
111
|
+
}),
|
|
112
|
+
/expected exactly 2 started and completed/,
|
|
113
|
+
)
|
|
114
|
+
assert.throws(() =>
|
|
115
|
+
assertExactCompletedWorkerSessions({
|
|
116
|
+
started: 2,
|
|
117
|
+
completed: 2,
|
|
118
|
+
sessions: [complete, { calls: 0, completions: 1, attemptError: null }],
|
|
119
|
+
context: 'row',
|
|
120
|
+
}),
|
|
121
|
+
/session 2 did not complete successfully/,
|
|
122
|
+
)
|
|
123
|
+
})
|
|
124
|
+
})
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { isAbsolute } from 'node:path'
|
|
2
|
+
|
|
3
|
+
export type ExperimentArm = 'independent-2' | 'persistent-refine-2'
|
|
4
|
+
|
|
5
|
+
export interface ExperimentArmPreset {
|
|
6
|
+
arm: ExperimentArm
|
|
7
|
+
k: 1 | 2
|
|
8
|
+
repairs: 0 | 1
|
|
9
|
+
alwaysRunContinuation: boolean
|
|
10
|
+
persistent: boolean
|
|
11
|
+
workerSessions: 2
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface WorkerSessionReceipt {
|
|
15
|
+
calls: number
|
|
16
|
+
completions: number
|
|
17
|
+
attemptError: string | null
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const PRESETS: Record<ExperimentArm, ExperimentArmPreset> = {
|
|
21
|
+
'independent-2': {
|
|
22
|
+
arm: 'independent-2',
|
|
23
|
+
k: 2,
|
|
24
|
+
repairs: 0,
|
|
25
|
+
alwaysRunContinuation: false,
|
|
26
|
+
persistent: false,
|
|
27
|
+
workerSessions: 2,
|
|
28
|
+
},
|
|
29
|
+
'persistent-refine-2': {
|
|
30
|
+
arm: 'persistent-refine-2',
|
|
31
|
+
k: 1,
|
|
32
|
+
repairs: 1,
|
|
33
|
+
alwaysRunContinuation: true,
|
|
34
|
+
persistent: true,
|
|
35
|
+
workerSessions: 2,
|
|
36
|
+
},
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function resolveExperimentArm(value: string): ExperimentArmPreset {
|
|
40
|
+
const preset = PRESETS[value as ExperimentArm]
|
|
41
|
+
if (!preset) {
|
|
42
|
+
throw new Error(
|
|
43
|
+
`EXPERIMENT_ARM must be independent-2|persistent-refine-2, got "${value}"`,
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
return preset
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Keep model sampling configuration disjoint from Node's TEMP-controlled filesystem root. */
|
|
50
|
+
export function resolveExperimentTemperature(
|
|
51
|
+
env: Readonly<Record<string, string | undefined>>,
|
|
52
|
+
): number {
|
|
53
|
+
if (env.TEMP?.trim() && !isAbsolute(env.TEMP)) {
|
|
54
|
+
throw new Error(
|
|
55
|
+
`TEMP is the operating-system temporary-directory root and must be absolute, got "${env.TEMP}"; ` +
|
|
56
|
+
'use TEMPERATURE for model sampling',
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
const value = Number(env.TEMPERATURE ?? 0.8)
|
|
60
|
+
if (!Number.isFinite(value)) {
|
|
61
|
+
throw new Error(`TEMPERATURE must be a finite number, got "${env.TEMPERATURE}"`)
|
|
62
|
+
}
|
|
63
|
+
return value
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Both arms use one selection rule: lower visible severity wins and a tie prefers session two. */
|
|
67
|
+
export function preferLaterCandidate(currentSeverity: number, laterSeverity: number): boolean {
|
|
68
|
+
return laterSeverity <= currentSeverity
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** The persistent arm always starts its one continuation, including after a visible pass. */
|
|
72
|
+
export function shouldRunContinuation(input: {
|
|
73
|
+
round: number
|
|
74
|
+
preset: ExperimentArmPreset
|
|
75
|
+
}): boolean {
|
|
76
|
+
return input.preset.alwaysRunContinuation && input.preset.repairs === 1 && input.round === 1
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** A continuation replaces its parent under the same later-on-tie rule as independent selection. */
|
|
80
|
+
export function shouldAcceptContinuation(currentSeverity: number, continuationSeverity: number): boolean {
|
|
81
|
+
return preferLaterCandidate(currentSeverity, continuationSeverity)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function continuationDisposition(accepted: boolean, changedFromParent: boolean): {
|
|
85
|
+
finalFrom: 'session:2-refinement' | 'session:2-identical' | 'session:1-visible-better'
|
|
86
|
+
stop: 'continuation-accepted-changed' | 'continuation-accepted-identical' | 'continuation-rejected-visible-regression'
|
|
87
|
+
} {
|
|
88
|
+
if (!accepted) {
|
|
89
|
+
return { finalFrom: 'session:1-visible-better', stop: 'continuation-rejected-visible-regression' }
|
|
90
|
+
}
|
|
91
|
+
return changedFromParent
|
|
92
|
+
? { finalFrom: 'session:2-refinement', stop: 'continuation-accepted-changed' }
|
|
93
|
+
: { finalFrom: 'session:2-identical', stop: 'continuation-accepted-identical' }
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Describe inherited workspace state without echoing model-authored patch bytes back through the
|
|
97
|
+
* outbound prompt. The checkout already contains the parent state; repeating its diff can collide
|
|
98
|
+
* with hidden-patch leak marks when session one independently finds the correct line. */
|
|
99
|
+
export function continuationStateNotice(parentDiff: string): string {
|
|
100
|
+
return parentDiff.trim()
|
|
101
|
+
? '--- PREVIOUS FIX STATE ---\nSession one changes are already applied to this checkout. Inspect the current files directly.\n\n'
|
|
102
|
+
: '--- NO FIX APPLIED YET (session one produced no change) ---\n\n'
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Admit only two genuinely completed model-backed sessions, never two attempted dispatches. */
|
|
106
|
+
export function assertExactCompletedWorkerSessions(input: {
|
|
107
|
+
started: number
|
|
108
|
+
completed: number
|
|
109
|
+
sessions: readonly WorkerSessionReceipt[]
|
|
110
|
+
context: string
|
|
111
|
+
}): void {
|
|
112
|
+
if (input.started !== 2 || input.completed !== 2 || input.sessions.length !== 2) {
|
|
113
|
+
throw new Error(
|
|
114
|
+
`${input.context}: expected exactly 2 started and completed worker sessions ` +
|
|
115
|
+
`(started=${input.started}, completed=${input.completed}, receipts=${input.sessions.length})`,
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
input.sessions.forEach((session, index) => {
|
|
119
|
+
if (
|
|
120
|
+
session.attemptError !== null ||
|
|
121
|
+
!Number.isInteger(session.calls) ||
|
|
122
|
+
session.calls < 1 ||
|
|
123
|
+
!Number.isInteger(session.completions) ||
|
|
124
|
+
session.completions < 1
|
|
125
|
+
) {
|
|
126
|
+
throw new Error(
|
|
127
|
+
`${input.context}: session ${index + 1} did not complete successfully ` +
|
|
128
|
+
`(error=${session.attemptError ?? 'none'}, calls=${session.calls}, completions=${session.completions})`,
|
|
129
|
+
)
|
|
130
|
+
}
|
|
131
|
+
})
|
|
132
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import assert from 'node:assert/strict'
|
|
2
|
+
import { describe, it } from 'node:test'
|
|
3
|
+
import {
|
|
4
|
+
assertFingerprintsEqual,
|
|
5
|
+
createExecutionReceipt,
|
|
6
|
+
createFingerprints,
|
|
7
|
+
diffChanged,
|
|
8
|
+
diffFingerprint,
|
|
9
|
+
fingerprint,
|
|
10
|
+
runtimeImplementationFingerprint,
|
|
11
|
+
} from './swe-structural-provenance'
|
|
12
|
+
|
|
13
|
+
const inputs = {
|
|
14
|
+
source: { files: ['runner', 'environment'] },
|
|
15
|
+
config: { arm: 'independent-2', k: 2 },
|
|
16
|
+
commonConfig: { model: 'worker' },
|
|
17
|
+
repro: { script: 'raise SystemExit(1)' },
|
|
18
|
+
prompt: 'prompt',
|
|
19
|
+
tools: [{ name: 'run' }],
|
|
20
|
+
task: { id: 'repo__repo-1' },
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe('structural provenance', () => {
|
|
24
|
+
it('hashes objects independent of key insertion order', () => {
|
|
25
|
+
assert.equal(fingerprint({ b: 2, a: 1 }), fingerprint({ a: 1, b: 2 }))
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('rejects a changed resume input by its named fingerprint', () => {
|
|
29
|
+
const expected = createFingerprints(inputs)
|
|
30
|
+
const actual = createFingerprints({ ...inputs, prompt: 'changed prompt' })
|
|
31
|
+
assert.throws(() => assertFingerprintsEqual(actual, expected, 'resume row'), /prompt fingerprint mismatch/)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('records identical and changed continuation patches exactly', () => {
|
|
35
|
+
const parent = 'diff --git a/a.py b/a.py\n+x\n'
|
|
36
|
+
assert.match(diffFingerprint(parent), /^sha256:[0-9a-f]{64}$/)
|
|
37
|
+
assert.equal(diffChanged(parent, parent), false)
|
|
38
|
+
assert.equal(diffChanged(parent, `${parent}+y\n`), true)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('binds actual runtime callables, run settings, scorer version, and immutable image identity', () => {
|
|
42
|
+
const runtime = runtimeImplementationFingerprint({
|
|
43
|
+
runAgentic: function runAgenticA() { return 'a' },
|
|
44
|
+
refine: { name: 'refine', driver: function refineA() { return 'a' } },
|
|
45
|
+
})
|
|
46
|
+
const changedRuntime = runtimeImplementationFingerprint({
|
|
47
|
+
runAgentic: function runAgenticB() { return 'b' },
|
|
48
|
+
refine: { name: 'refine', driver: function refineA() { return 'a' } },
|
|
49
|
+
})
|
|
50
|
+
assert.notEqual(changedRuntime, runtime)
|
|
51
|
+
|
|
52
|
+
const shared = {
|
|
53
|
+
runTool: { timeoutS: 120, outputLimit: 10_000 },
|
|
54
|
+
runtimeImplementationFingerprint: runtime,
|
|
55
|
+
runtimeTreeFingerprint: fingerprint({ runtime: 'tree-a' }),
|
|
56
|
+
officialScorer: {
|
|
57
|
+
package: 'swebench' as const,
|
|
58
|
+
version: '4.0.0',
|
|
59
|
+
cacheLevel: 'instance' as const,
|
|
60
|
+
namespacePolicy: 'phase-a-image' as const,
|
|
61
|
+
},
|
|
62
|
+
}
|
|
63
|
+
const receipt = createExecutionReceipt(shared, {
|
|
64
|
+
tag: 'sweb.eval.example:latest',
|
|
65
|
+
namespace: 'swebench',
|
|
66
|
+
identity: { id: 'sha256:image-a', repoDigests: ['sweb.eval.example@sha256:digest-a'] },
|
|
67
|
+
})
|
|
68
|
+
const movedTag = createExecutionReceipt(shared, {
|
|
69
|
+
tag: 'sweb.eval.example:latest',
|
|
70
|
+
namespace: 'swebench',
|
|
71
|
+
identity: { id: 'sha256:image-b', repoDigests: ['sweb.eval.example@sha256:digest-b'] },
|
|
72
|
+
})
|
|
73
|
+
const movedRuntimeTree = createExecutionReceipt(
|
|
74
|
+
{ ...shared, runtimeTreeFingerprint: fingerprint({ runtime: 'tree-b' }) },
|
|
75
|
+
{
|
|
76
|
+
tag: 'sweb.eval.example:latest',
|
|
77
|
+
namespace: 'swebench',
|
|
78
|
+
identity: { id: 'sha256:image-a', repoDigests: ['sweb.eval.example@sha256:digest-a'] },
|
|
79
|
+
},
|
|
80
|
+
)
|
|
81
|
+
const movedScorer = createExecutionReceipt(
|
|
82
|
+
{ ...shared, officialScorer: { ...shared.officialScorer, version: '4.0.1' } },
|
|
83
|
+
{
|
|
84
|
+
tag: 'sweb.eval.example:latest',
|
|
85
|
+
namespace: 'swebench',
|
|
86
|
+
identity: { id: 'sha256:image-a', repoDigests: ['sweb.eval.example@sha256:digest-a'] },
|
|
87
|
+
},
|
|
88
|
+
)
|
|
89
|
+
assert.notEqual(fingerprint(receipt), fingerprint(movedTag))
|
|
90
|
+
assert.notEqual(fingerprint(receipt), fingerprint(movedRuntimeTree))
|
|
91
|
+
assert.notEqual(fingerprint(receipt), fingerprint(movedScorer))
|
|
92
|
+
})
|
|
93
|
+
})
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto'
|
|
2
|
+
|
|
3
|
+
export const FINGERPRINT_SCHEMA = 'swe-structural-v2' as const
|
|
4
|
+
|
|
5
|
+
export interface Fingerprints {
|
|
6
|
+
schema: typeof FINGERPRINT_SCHEMA
|
|
7
|
+
source: string
|
|
8
|
+
config: string
|
|
9
|
+
commonConfig: string
|
|
10
|
+
repro: string
|
|
11
|
+
prompt: string
|
|
12
|
+
tools: string
|
|
13
|
+
task: string
|
|
14
|
+
composite: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface FingerprintInputs {
|
|
18
|
+
source: unknown
|
|
19
|
+
config: unknown
|
|
20
|
+
commonConfig: unknown
|
|
21
|
+
repro: unknown
|
|
22
|
+
prompt: unknown
|
|
23
|
+
tools: unknown
|
|
24
|
+
task: unknown
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface SharedExecutionReceipt {
|
|
28
|
+
runTool: {
|
|
29
|
+
timeoutS: number
|
|
30
|
+
outputLimit: number
|
|
31
|
+
}
|
|
32
|
+
runtimeImplementationFingerprint: string
|
|
33
|
+
runtimeTreeFingerprint: string
|
|
34
|
+
officialScorer: {
|
|
35
|
+
package: 'swebench'
|
|
36
|
+
version: string
|
|
37
|
+
cacheLevel: 'instance'
|
|
38
|
+
namespacePolicy: 'phase-a-image'
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface ExecutionReceipt extends SharedExecutionReceipt {
|
|
43
|
+
image: {
|
|
44
|
+
tag: string
|
|
45
|
+
namespace: 'swebench' | 'none'
|
|
46
|
+
id: string
|
|
47
|
+
repoDigests: string[]
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function canonicalize(value: unknown): unknown {
|
|
52
|
+
if (Array.isArray(value)) return value.map(canonicalize)
|
|
53
|
+
if (value && typeof value === 'object') {
|
|
54
|
+
return Object.fromEntries(
|
|
55
|
+
Object.entries(value as Record<string, unknown>)
|
|
56
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
57
|
+
.map(([key, child]) => [key, canonicalize(child)]),
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
return value
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function canonicalJson(value: unknown): string {
|
|
64
|
+
return JSON.stringify(canonicalize(value))
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function fingerprint(value: unknown): string {
|
|
68
|
+
return `sha256:${createHash('sha256').update(canonicalJson(value)).digest('hex')}`
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function createFingerprints(inputs: FingerprintInputs): Fingerprints {
|
|
72
|
+
const parts = {
|
|
73
|
+
schema: FINGERPRINT_SCHEMA,
|
|
74
|
+
source: fingerprint(inputs.source),
|
|
75
|
+
config: fingerprint(inputs.config),
|
|
76
|
+
commonConfig: fingerprint(inputs.commonConfig),
|
|
77
|
+
repro: fingerprint(inputs.repro),
|
|
78
|
+
prompt: fingerprint(inputs.prompt),
|
|
79
|
+
tools: fingerprint(inputs.tools),
|
|
80
|
+
task: fingerprint(inputs.task),
|
|
81
|
+
}
|
|
82
|
+
return { ...parts, composite: fingerprint(parts) }
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function assertFingerprintsEqual(
|
|
86
|
+
actual: Fingerprints | undefined,
|
|
87
|
+
expected: Fingerprints,
|
|
88
|
+
context: string,
|
|
89
|
+
): void {
|
|
90
|
+
if (!actual) throw new Error(`${context}: missing ${FINGERPRINT_SCHEMA} fingerprints`)
|
|
91
|
+
for (const key of Object.keys(expected) as Array<keyof Fingerprints>) {
|
|
92
|
+
if (actual[key] !== expected[key]) {
|
|
93
|
+
throw new Error(`${context}: ${key} fingerprint mismatch (${actual[key]} != ${expected[key]})`)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function diffFingerprint(diff: string): string {
|
|
99
|
+
return fingerprint({ diff })
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function diffChanged(parent: string, candidate: string): boolean {
|
|
103
|
+
return diffFingerprint(parent) !== diffFingerprint(candidate)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Hash the actual imported callables, not merely the source file expected to contain them. */
|
|
107
|
+
export function runtimeImplementationFingerprint(input: {
|
|
108
|
+
runAgentic: unknown
|
|
109
|
+
refine: { name: string; driver: unknown }
|
|
110
|
+
}): string {
|
|
111
|
+
if (typeof input.runAgentic !== 'function' || typeof input.refine.driver !== 'function') {
|
|
112
|
+
throw new Error('runtime implementation receipt requires callable runAgentic and refine.driver')
|
|
113
|
+
}
|
|
114
|
+
return fingerprint({
|
|
115
|
+
runAgentic: Function.prototype.toString.call(input.runAgentic),
|
|
116
|
+
refine: {
|
|
117
|
+
name: input.refine.name,
|
|
118
|
+
driver: Function.prototype.toString.call(input.refine.driver),
|
|
119
|
+
},
|
|
120
|
+
})
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function createExecutionReceipt(
|
|
124
|
+
shared: SharedExecutionReceipt,
|
|
125
|
+
image: { tag: string; namespace: 'swebench' | 'none'; identity: { id: string; repoDigests: string[] } },
|
|
126
|
+
): ExecutionReceipt {
|
|
127
|
+
return {
|
|
128
|
+
...shared,
|
|
129
|
+
runTool: { ...shared.runTool },
|
|
130
|
+
officialScorer: { ...shared.officialScorer },
|
|
131
|
+
image: {
|
|
132
|
+
tag: image.tag,
|
|
133
|
+
namespace: image.namespace,
|
|
134
|
+
id: image.identity.id,
|
|
135
|
+
repoDigests: [...image.identity.repoDigests],
|
|
136
|
+
},
|
|
137
|
+
}
|
|
138
|
+
}
|