@tangle-network/agent-bench 0.3.6 → 0.3.8
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 +11 -0
- package/HARNESS.md +43 -0
- package/dist/adapters.js +24 -24
- package/dist/benchmarks/_harness.d.ts +1 -1
- package/dist/benchmarks/_harness.js +1 -1
- package/dist/benchmarks/aec-bench.js +2 -2
- package/dist/benchmarks/agentbench.js +2 -2
- package/dist/benchmarks/appworld.js +2 -2
- package/dist/benchmarks/bfcl.js +2 -2
- package/dist/benchmarks/commit0.js +2 -2
- package/dist/benchmarks/crag.js +2 -2
- package/dist/benchmarks/dabstep.js +2 -2
- package/dist/benchmarks/enterpriseops-gym.js +2 -2
- package/dist/benchmarks/finresearchbench.js +2 -2
- package/dist/benchmarks/humaneval.d.ts +10 -1
- package/dist/benchmarks/humaneval.js +5 -3
- package/dist/benchmarks/nomiracl.js +2 -2
- package/dist/benchmarks/open-rag-bench.js +2 -2
- package/dist/benchmarks/programbench.js +2 -2
- package/dist/benchmarks/ragbench.js +2 -2
- package/dist/benchmarks/swe-bench.js +2 -2
- package/dist/benchmarks/t2-ragbench.js +2 -2
- package/dist/benchmarks/tau-bench-shared.js +2 -2
- package/dist/benchmarks/tau2-bench.js +3 -3
- package/dist/benchmarks/tau3-banking.js +3 -3
- package/dist/benchmarks/terminal-bench.js +2 -2
- package/dist/benchmarks/toollm.js +2 -2
- package/dist/benchmarks/webarena-verified.js +2 -2
- package/dist/{chunk-CKUVRZ2T.js → chunk-3U5TXJZS.js} +2 -2
- package/dist/{chunk-PPYSEKFM.js → chunk-5H5XV76F.js} +73 -15
- package/dist/chunk-5H5XV76F.js.map +1 -0
- package/dist/{chunk-YCGY7UIZ.js → chunk-7GRVHU22.js} +2 -2
- package/dist/{chunk-Z7ML6L77.js → chunk-HWST3SED.js} +2 -2
- package/dist/{chunk-SYDW647C.js → chunk-IA2FBTWC.js} +2 -2
- package/dist/{chunk-R67DFVLO.js → chunk-IFVINJ4B.js} +2 -2
- package/dist/{chunk-R36V2VP7.js → chunk-IZ5M6OAC.js} +2 -2
- package/dist/{chunk-ODT47UAY.js → chunk-K3BQGZCT.js} +2 -2
- package/dist/{chunk-IFAV6KEM.js → chunk-KP5KD6EN.js} +2 -2
- package/dist/{chunk-ZEWMTR5M.js → chunk-MQMRLGOG.js} +2 -2
- package/dist/{chunk-TSWPNOYM.js → chunk-NQG5XDSB.js} +2 -2
- package/dist/{chunk-7WSD27QQ.js → chunk-PB64GYIG.js} +2 -2
- package/dist/{chunk-HBSWHQNJ.js → chunk-RCYQEFNX.js} +3 -3
- package/dist/{chunk-J3KDJNX2.js → chunk-RH5F53JT.js} +2 -2
- package/dist/{chunk-UAIOHCUK.js → chunk-SFLA7OH3.js} +3 -3
- package/dist/{chunk-Y6O2OCUO.js → chunk-SHM6MRRF.js} +2 -2
- package/dist/{chunk-KDIKRJGB.js → chunk-SHYIRB7I.js} +2 -2
- package/dist/{chunk-HHXFIHXC.js → chunk-SVR2LKYI.js} +2 -2
- package/dist/{chunk-5SBJCB6W.js → chunk-V7AEBY6U.js} +22 -22
- package/dist/{chunk-LRRD7NAG.js → chunk-WSKWVEQB.js} +18 -2
- package/dist/chunk-WSKWVEQB.js.map +1 -0
- package/dist/{chunk-2PVVP7GN.js → chunk-XKEFIFIC.js} +2 -2
- package/dist/{chunk-JRWWGMK7.js → chunk-XYA4XSNU.js} +2 -2
- package/dist/{chunk-X5YKXC6V.js → chunk-YSMEKBTD.js} +2 -2
- package/dist/{chunk-2XU6OGEN.js → chunk-Z4TZ76N7.js} +2 -2
- package/dist/index.js +24 -24
- package/package.json +6 -5
- package/scripts/run-package-tests.mjs +30 -8
- package/scripts/verify-packed-consumer.mjs +1 -1
- package/scripts/verify-pier-agent.mts +1 -0
- package/src/benchmarks/_harness.ts +20 -2
- package/src/benchmarks/humaneval.test.mts +122 -0
- package/src/benchmarks/humaneval.ts +100 -27
- package/src/david-attribution.mts +78 -0
- package/src/david-goliath.mts +149 -0
- package/src/hev-improve.mts +25 -6
- package/src/humaneval-object-ablation.mts +201 -0
- package/src/live-improve-campaign-mbpp.mts +641 -0
- package/src/live-improve-campaign.mts +500 -0
- package/src/mbpp-structural.mts +12 -7
- package/src/quant-arena/README.md +144 -0
- package/src/quant-arena/backtest.test.mts +135 -0
- package/src/quant-arena/backtest.ts +218 -0
- package/src/quant-arena/data.test.mts +44 -0
- package/src/quant-arena/data.ts +141 -0
- package/src/quant-arena/driver.test.mts +253 -0
- package/src/quant-arena/driver.ts +219 -0
- package/src/quant-arena/fixtures/data/PROVENANCE.md +26 -0
- package/src/quant-arena/fixtures/data/holdout/IDX.csv +523 -0
- package/src/quant-arena/fixtures/data/holdout/S01.csv +523 -0
- package/src/quant-arena/fixtures/data/holdout/S02.csv +523 -0
- package/src/quant-arena/fixtures/data/holdout/S03.csv +523 -0
- package/src/quant-arena/fixtures/data/holdout/S04.csv +523 -0
- package/src/quant-arena/fixtures/data/holdout/S05.csv +523 -0
- package/src/quant-arena/fixtures/data/holdout/S06.csv +523 -0
- package/src/quant-arena/fixtures/data/holdout/S07.csv +523 -0
- package/src/quant-arena/fixtures/data/holdout/S08.csv +523 -0
- package/src/quant-arena/fixtures/data/holdout/S09.csv +523 -0
- package/src/quant-arena/fixtures/data/holdout/S10.csv +523 -0
- package/src/quant-arena/fixtures/data/insample/IDX.csv +2087 -0
- package/src/quant-arena/fixtures/data/insample/S01.csv +2087 -0
- package/src/quant-arena/fixtures/data/insample/S02.csv +2087 -0
- package/src/quant-arena/fixtures/data/insample/S03.csv +2087 -0
- package/src/quant-arena/fixtures/data/insample/S04.csv +2087 -0
- package/src/quant-arena/fixtures/data/insample/S05.csv +2087 -0
- package/src/quant-arena/fixtures/data/insample/S06.csv +2087 -0
- package/src/quant-arena/fixtures/data/insample/S07.csv +2087 -0
- package/src/quant-arena/fixtures/data/insample/S08.csv +2087 -0
- package/src/quant-arena/fixtures/data/insample/S09.csv +2087 -0
- package/src/quant-arena/fixtures/data/insample/S10.csv +2087 -0
- package/src/quant-arena/fixtures/demo-campaign/cost-ledger.jsonl +16 -0
- package/src/quant-arena/fixtures/demo-campaign/notebook.jsonl +5 -0
- package/src/quant-arena/fixtures/demo-campaign/rollout-manifest.json +171 -0
- package/src/quant-arena/fixtures/demo-campaign/strategies/cand-001-default-author/strategy.ts +119 -0
- package/src/quant-arena/fixtures/demo-campaign/strategies/cand-002-default-author/strategy.ts +119 -0
- package/src/quant-arena/fixtures/demo-campaign/strategies/cand-003-quant-researcher/strategy.ts +105 -0
- package/src/quant-arena/fixtures/demo-campaign/strategies/cand-004-quant-researcher/strategy.ts +102 -0
- package/src/quant-arena/fixtures/demo-campaign-v2/cost-ledger.jsonl +4 -0
- package/src/quant-arena/fixtures/demo-campaign-v2/notebook.jsonl +2 -0
- package/src/quant-arena/fixtures/demo-campaign-v2/rollout-manifest.json +84 -0
- package/src/quant-arena/fixtures/demo-campaign-v2/strategies/cand-001-quant-researcher/strategy.ts +117 -0
- package/src/quant-arena/holdout-certify.mts +206 -0
- package/src/quant-arena/holdout-certify.test.mts +82 -0
- package/src/quant-arena/leak-audit.test.mts +79 -0
- package/src/quant-arena/leak-audit.ts +95 -0
- package/src/quant-arena/make-fixtures.mts +161 -0
- package/src/quant-arena/multiplicity.test.mts +68 -0
- package/src/quant-arena/multiplicity.ts +87 -0
- package/src/quant-arena/nautilus-certify.ts +31 -0
- package/src/quant-arena/oms.ts +90 -0
- package/src/quant-arena/profiles/quant-researcher.profile.json +7 -0
- package/src/quant-arena/python/pyproject.toml +8 -0
- package/src/quant-arena/python/uv.lock +1297 -0
- package/src/quant-arena/python/vbt-worker.py +192 -0
- package/src/quant-arena/quant-loop.mts +813 -0
- package/src/quant-arena/quant-loop.test.mts +75 -0
- package/src/quant-arena/strategies/buy-hold-index/strategy.ts +11 -0
- package/src/quant-arena/strategies/equal-weight/strategy.ts +20 -0
- package/src/quant-arena/strategies/sma-crossover/strategy.ts +42 -0
- package/src/quant-arena/types.ts +133 -0
- package/src/quant-arena/vbt-client.ts +321 -0
- package/src/quant-arena/vbt-parity.test.mts +183 -0
- package/src/quant-arena/windows.test.mts +45 -0
- package/src/quant-arena/windows.ts +54 -0
- package/src/rollout-ledger/backfill-swe-arena.mts +606 -0
- package/src/rollout-ledger/backfill-swe-arena.test.mts +338 -0
- package/src/rollout-ledger/settle-capture.mts +442 -0
- package/src/rollout-ledger/settle-capture.test.mts +270 -0
- package/src/stream-observe.py +45 -0
- package/src/stream-observe.tpl.html +247 -0
- package/src/supervisor-arena.mts +816 -0
- package/src/swe-arena/activation.mts +228 -0
- package/src/swe-arena/activation.test.mts +303 -0
- package/src/swe-arena/analyze.ts +211 -0
- package/src/swe-arena/arms.ts +804 -0
- package/src/swe-arena/bootstrap-meta.mts +188 -0
- package/src/swe-arena/bootstrap-meta.test.mts +51 -0
- package/src/swe-arena/briefing.mts +217 -0
- package/src/swe-arena/briefing.test.mts +178 -0
- package/src/swe-arena/calibrate.ts +217 -0
- package/src/swe-arena/capabilities.mts +76 -0
- package/src/swe-arena/capabilities.test.mts +57 -0
- package/src/swe-arena/capacity.ts +194 -0
- package/src/swe-arena/cell-evidence.mts +437 -0
- package/src/swe-arena/cell-evidence.test.mts +248 -0
- package/src/swe-arena/diagnosis-ensemble.test.mts +210 -0
- package/src/swe-arena/diagnosis-ensemble.ts +520 -0
- package/src/swe-arena/execution.test.mts +1170 -0
- package/src/swe-arena/factory-command-container.ts +284 -0
- package/src/swe-arena/factory-judge-child.mts +228 -0
- package/src/swe-arena/factory.test.mts +643 -0
- package/src/swe-arena/fixtures/analyze.py +80 -0
- package/src/swe-arena/fixtures/excludes.txt +8 -0
- package/src/swe-arena/fixtures/factory/agent-eval-309/calibration.md +51 -0
- package/src/swe-arena/fixtures/factory/agent-eval-309/manifest.json +29 -0
- package/src/swe-arena/fixtures/factory/agent-eval-309/spec.md +64 -0
- package/src/swe-arena/fixtures/factory/agent-runtime-232/calibration.md +48 -0
- package/src/swe-arena/fixtures/factory/agent-runtime-232/manifest.json +29 -0
- package/src/swe-arena/fixtures/factory/agent-runtime-232/spec.md +48 -0
- package/src/swe-arena/fixtures/factory/loops-28/calibration.md +47 -0
- package/src/swe-arena/fixtures/factory/loops-28/manifest.json +30 -0
- package/src/swe-arena/fixtures/factory/loops-28/spec.md +50 -0
- package/src/swe-arena/fixtures/gen1-salvage/README.md +45 -0
- package/src/swe-arena/fixtures/gen1-salvage/cand0-e6d7361.diff +116 -0
- package/src/swe-arena/fixtures/gen1-salvage/cand1-76a8590.diff +293 -0
- package/src/swe-arena/fixtures/holdout-preregister.log +12 -0
- package/src/swe-arena/fixtures/holdout.json +44 -0
- package/src/swe-arena/fixtures/instances.json +146 -0
- package/src/swe-arena/fixtures/ledger.jsonl +12 -0
- package/src/swe-arena/fixtures/patches/pallets__flask-5014.solo.patch +36 -0
- package/src/swe-arena/fixtures/patches/pydata__xarray-4687.sup.patch +33 -0
- package/src/swe-arena/fixtures/rejudge.jsonl +15 -0
- package/src/swe-arena/fixtures/rematch.jsonl +3 -0
- package/src/swe-arena/fixtures/rematch2.jsonl +3 -0
- package/src/swe-arena/fixtures/rematch3.jsonl +3 -0
- package/src/swe-arena/fixtures/run-report/README.md +43 -0
- package/src/swe-arena/fixtures/run-report/factory-agent-eval-309-FSUP0.json +173 -0
- package/src/swe-arena/fixtures/run-report/factory-agent-eval-309-FSUP0.md +100 -0
- package/src/swe-arena/fixtures/run-report/gen3-rollup.json +551 -0
- package/src/swe-arena/fixtures/run-report/gen3-rollup.md +64 -0
- package/src/swe-arena/fixtures/sup-journal-true.json +19 -0
- package/src/swe-arena/fixtures/verify/astropy__astropy-13033.sh +48 -0
- package/src/swe-arena/fixtures/verify/django__django-11532.sh +50 -0
- package/src/swe-arena/fixtures/verify/matplotlib__matplotlib-20826.sh +76 -0
- package/src/swe-arena/fixtures/verify/pydata__xarray-4687.sh +44 -0
- package/src/swe-arena/fixtures/verify/pytest-dev__pytest-6197.sh +32 -0
- package/src/swe-arena/fixtures/verify/sphinx-doc__sphinx-9658.sh +51 -0
- package/src/swe-arena/fixtures/worker-tokens.json +42 -0
- package/src/swe-arena/fixtures.ts +237 -0
- package/src/swe-arena/gepa-seat.mts +583 -0
- package/src/swe-arena/gepa-seat.test.mts +635 -0
- package/src/swe-arena/holdout-certify.mts +408 -0
- package/src/swe-arena/holdout-certify.test.mts +160 -0
- package/src/swe-arena/judge-child.mts +37 -0
- package/src/swe-arena/ledger-orphans.mts +77 -0
- package/src/swe-arena/ledger-orphans.test.mts +147 -0
- package/src/swe-arena/lineage-record.mts +164 -0
- package/src/swe-arena/lineage-record.test.mts +115 -0
- package/src/swe-arena/manifest.mts +293 -0
- package/src/swe-arena/manifest.test.mts +169 -0
- package/src/swe-arena/materialize.ts +142 -0
- package/src/swe-arena/outer-loop.mts +2795 -0
- package/src/swe-arena/outer-loop.test.mts +696 -0
- package/src/swe-arena/parity.test.mts +87 -0
- package/src/swe-arena/premeasured-from-cells.mts +281 -0
- package/src/swe-arena/premeasured-from-cells.test.mts +180 -0
- package/src/swe-arena/proc.test.mts +174 -0
- package/src/swe-arena/proc.ts +260 -0
- package/src/swe-arena/profiles/default-author.profile.json +4 -0
- package/src/swe-arena/proposer-fanout.mts +770 -0
- package/src/swe-arena/proposer-fanout.test.mts +619 -0
- package/src/swe-arena/proposer-provenance.mts +177 -0
- package/src/swe-arena/proposer-provenance.test.mts +106 -0
- package/src/swe-arena/reconcile.ts +0 -0
- package/src/swe-arena/replay.mts +183 -0
- package/src/swe-arena/replay.test.mts +300 -0
- package/src/swe-arena/run-experiment.mts +727 -0
- package/src/swe-arena/run-report.mts +75 -0
- package/src/swe-arena/run-supervisor.mjs +297 -0
- package/src/swe-arena/run-supervisor.test.mts +500 -0
- package/src/swe-arena/score-split.mts +140 -0
- package/src/swe-arena/score-split.test.mts +123 -0
- package/src/swe-arena/serialized-judge.ts +414 -0
- package/src/swe-arena/types.ts +218 -0
- package/src/swe-code-improve.mts +328 -0
- package/src/swe-emit-patch.mts +104 -0
- package/src/swe-improve.mts +232 -0
- package/src/swe-jail.ts +2 -2
- package/src/swe-local-proof.mts +169 -0
- package/src/swe-repro-calibrate.mts +446 -0
- package/src/swe-stream.mts +1497 -0
- package/src/swe-structural.mts +245 -837
- package/dist/chunk-LRRD7NAG.js.map +0 -1
- package/dist/chunk-PPYSEKFM.js.map +0 -1
- /package/dist/{chunk-CKUVRZ2T.js.map → chunk-3U5TXJZS.js.map} +0 -0
- /package/dist/{chunk-YCGY7UIZ.js.map → chunk-7GRVHU22.js.map} +0 -0
- /package/dist/{chunk-Z7ML6L77.js.map → chunk-HWST3SED.js.map} +0 -0
- /package/dist/{chunk-SYDW647C.js.map → chunk-IA2FBTWC.js.map} +0 -0
- /package/dist/{chunk-R67DFVLO.js.map → chunk-IFVINJ4B.js.map} +0 -0
- /package/dist/{chunk-R36V2VP7.js.map → chunk-IZ5M6OAC.js.map} +0 -0
- /package/dist/{chunk-ODT47UAY.js.map → chunk-K3BQGZCT.js.map} +0 -0
- /package/dist/{chunk-IFAV6KEM.js.map → chunk-KP5KD6EN.js.map} +0 -0
- /package/dist/{chunk-ZEWMTR5M.js.map → chunk-MQMRLGOG.js.map} +0 -0
- /package/dist/{chunk-TSWPNOYM.js.map → chunk-NQG5XDSB.js.map} +0 -0
- /package/dist/{chunk-7WSD27QQ.js.map → chunk-PB64GYIG.js.map} +0 -0
- /package/dist/{chunk-HBSWHQNJ.js.map → chunk-RCYQEFNX.js.map} +0 -0
- /package/dist/{chunk-J3KDJNX2.js.map → chunk-RH5F53JT.js.map} +0 -0
- /package/dist/{chunk-UAIOHCUK.js.map → chunk-SFLA7OH3.js.map} +0 -0
- /package/dist/{chunk-Y6O2OCUO.js.map → chunk-SHM6MRRF.js.map} +0 -0
- /package/dist/{chunk-KDIKRJGB.js.map → chunk-SHYIRB7I.js.map} +0 -0
- /package/dist/{chunk-HHXFIHXC.js.map → chunk-SVR2LKYI.js.map} +0 -0
- /package/dist/{chunk-5SBJCB6W.js.map → chunk-V7AEBY6U.js.map} +0 -0
- /package/dist/{chunk-2PVVP7GN.js.map → chunk-XKEFIFIC.js.map} +0 -0
- /package/dist/{chunk-JRWWGMK7.js.map → chunk-XYA4XSNU.js.map} +0 -0
- /package/dist/{chunk-X5YKXC6V.js.map → chunk-YSMEKBTD.js.map} +0 -0
- /package/dist/{chunk-2XU6OGEN.js.map → chunk-Z4TZ76N7.js.map} +0 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* swe-arena — typed replay of the committed SOLO-vs-SUPERVISOR head-to-head
|
|
3
|
+
* artifacts (SWE-bench Verified, glm-5.2 both arms).
|
|
4
|
+
*
|
|
5
|
+
* MILESTONE 1: these types mirror the fixture files byte-for-byte semantics.
|
|
6
|
+
* They are the proof-of-faithfulness layer: `reconcile.ts` + `analyze.ts`
|
|
7
|
+
* must reproduce the reference `fixtures/analyze.py` output exactly (pinned in
|
|
8
|
+
* `replay.test.mts`) before any typed execution path is built on top.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The fields of one SWE-bench Verified instance we actually consume from
|
|
13
|
+
* `task-meta.json` (generated by the experiment's `load_meta.py` from
|
|
14
|
+
* princeton-nlp/SWE-bench_Verified). `FAIL_TO_PASS` / `PASS_TO_PASS` are
|
|
15
|
+
* JSON-encoded string arrays as shipped by the HF dataset — kept as raw
|
|
16
|
+
* strings here; decode at the point of use.
|
|
17
|
+
*/
|
|
18
|
+
export interface SweInstance {
|
|
19
|
+
instance_id: string
|
|
20
|
+
repo: string
|
|
21
|
+
base_commit: string
|
|
22
|
+
problem_statement: string
|
|
23
|
+
patch: string
|
|
24
|
+
test_patch: string
|
|
25
|
+
/** JSON-encoded string[] (raw HF dataset encoding). */
|
|
26
|
+
FAIL_TO_PASS: string
|
|
27
|
+
/** JSON-encoded string[] (raw HF dataset encoding). */
|
|
28
|
+
PASS_TO_PASS: string
|
|
29
|
+
version: string | null
|
|
30
|
+
environment_setup_commit: string | null
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* One factory-bench instance — a merged feature PR from our own repo history
|
|
35
|
+
* turned into a gradable end-to-end feature-building task (see
|
|
36
|
+
* supervisor-lab/factory-bench/docs/design.md). The worker sees only the tree
|
|
37
|
+
* at `base_commit` (archive export, synthetic git history) plus the rewritten
|
|
38
|
+
* spec; the PR's own added test files are the hidden judge, overlaid from
|
|
39
|
+
* `judge_ref` at judge time. Field names mirror the instance dirs'
|
|
40
|
+
* `manifest.json` byte-for-byte, same policy as SweInstance vs task-meta.json.
|
|
41
|
+
*/
|
|
42
|
+
export interface FactoryInstance {
|
|
43
|
+
/** `factory.<repo>.<pr>` */
|
|
44
|
+
id: string
|
|
45
|
+
/** `owner/name` */
|
|
46
|
+
repo: string
|
|
47
|
+
/** Judge-side local mirror; NEVER exposed to the worker workspace. */
|
|
48
|
+
repo_local_mirror: string
|
|
49
|
+
/** The worker's world — the PR's base commit. */
|
|
50
|
+
base_commit: string
|
|
51
|
+
/** Merge commit the judge tests are read from (`git show <judge_ref>:<path>`). */
|
|
52
|
+
judge_ref: string
|
|
53
|
+
/** Instance-dir-relative spec file (PM-ticket grade rewrite of the PR body). */
|
|
54
|
+
spec_md: string
|
|
55
|
+
/** Hidden judge test files, overlaid at judge time only. */
|
|
56
|
+
judge_tests: string[]
|
|
57
|
+
/** Flaky/env-dependent tests excluded at calibration, reasons in calibration.md. */
|
|
58
|
+
excluded_tests: string[]
|
|
59
|
+
/** Immutable Node container image used for setup and judge commands. */
|
|
60
|
+
command_image: string
|
|
61
|
+
setup_cmds: string[]
|
|
62
|
+
judge_cmds: string[]
|
|
63
|
+
/** e.g. "all 30 judge tests pass; partial score = passed/30" — the /NN is parsed. */
|
|
64
|
+
resolved_criterion: string
|
|
65
|
+
timeout_s: number
|
|
66
|
+
worker_visible_paths_note?: string
|
|
67
|
+
runtime?: string
|
|
68
|
+
calibration?: { gold: string; base: string; receipts: string }
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Discriminated instance union for the seams that used to assume SweInstance.
|
|
73
|
+
* A tagged wrapper (not a structural union) because both shapes mirror their
|
|
74
|
+
* on-disk artifacts byte-for-byte and neither may grow a discriminant field.
|
|
75
|
+
*/
|
|
76
|
+
export type ArenaInstance =
|
|
77
|
+
| { kind: 'swe'; instance: SweInstance }
|
|
78
|
+
| { kind: 'factory'; instance: FactoryInstance }
|
|
79
|
+
|
|
80
|
+
/** The ledger/judge identity: SWE `instance_id` or factory `id`. */
|
|
81
|
+
export function arenaInstanceId(a: ArenaInstance): string {
|
|
82
|
+
return a.kind === 'swe' ? a.instance.instance_id : a.instance.id
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Per-run opencode usage breakdown captured on the SOLO arm. */
|
|
86
|
+
export interface SoloUsage {
|
|
87
|
+
steps: number
|
|
88
|
+
in: number
|
|
89
|
+
out: number
|
|
90
|
+
reasoning: number
|
|
91
|
+
cache_w: number
|
|
92
|
+
cache_r: number
|
|
93
|
+
max_ctx: number
|
|
94
|
+
oc_cost: number
|
|
95
|
+
total_io: number
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* One paired row of `ledger.jsonl` — exactly the schema the experiment wrote.
|
|
100
|
+
* `null` values are real telemetry gaps (e.g. `sup_spentTokens: null` when the
|
|
101
|
+
* supervisor driver exited rc=3 mid-run), not absent data to be defaulted.
|
|
102
|
+
*/
|
|
103
|
+
export interface LedgerRow {
|
|
104
|
+
iid: string
|
|
105
|
+
solo_resolved: boolean
|
|
106
|
+
sup_resolved: boolean
|
|
107
|
+
solo_verify_pass: boolean
|
|
108
|
+
sup_verify_pass: boolean
|
|
109
|
+
solo_patch_lines: number
|
|
110
|
+
sup_patch_lines: number
|
|
111
|
+
solo_wall_s: number
|
|
112
|
+
sup_wall_s: number
|
|
113
|
+
solo_tokens: number
|
|
114
|
+
solo_usage: SoloUsage
|
|
115
|
+
sup_spentTokens: number | null
|
|
116
|
+
sup_spentUsd: number | null
|
|
117
|
+
sup_spawned: number
|
|
118
|
+
/** Missing on rows written before the field was added (pallets__flask-5014). */
|
|
119
|
+
sup_workers?: number
|
|
120
|
+
sup_settled: number
|
|
121
|
+
sup_subtasks: string[]
|
|
122
|
+
sup_delivered: boolean | null
|
|
123
|
+
/**
|
|
124
|
+
* The fixture ledger observed only completed/running; the driver can also
|
|
125
|
+
* settle failed/cancelled (M2 widened the union — the typed execution path
|
|
126
|
+
* records those honestly instead of coercing them).
|
|
127
|
+
*/
|
|
128
|
+
sup_status: 'completed' | 'running' | 'failed' | 'cancelled' | null
|
|
129
|
+
sup_verdict: 'delivered' | 'no-winner' | 'best-effort' | null
|
|
130
|
+
solo_oc_rc: number
|
|
131
|
+
sup_driver_rc: number
|
|
132
|
+
solo_patch: string
|
|
133
|
+
sup_patch: string
|
|
134
|
+
/** Free-form correction note (e.g. psf__requests-1766 sup re-judge). */
|
|
135
|
+
_note?: string
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Tags carried by `rejudge.jsonl` rows. Gold-family tags grade the OFFICIAL
|
|
140
|
+
* gold patch (calibrating the judge itself); final/my-reverify tags are the
|
|
141
|
+
* personal authoritative re-judgements of arm patches that override the
|
|
142
|
+
* automated ledger verdicts.
|
|
143
|
+
*/
|
|
144
|
+
export type RejudgeTag =
|
|
145
|
+
| 'gold-control'
|
|
146
|
+
| 'gold'
|
|
147
|
+
| 'gold2'
|
|
148
|
+
| 'solo-final'
|
|
149
|
+
| 'sup-final'
|
|
150
|
+
| 'solo-final2'
|
|
151
|
+
| 'sup-final2'
|
|
152
|
+
| 'my-reverify'
|
|
153
|
+
|
|
154
|
+
/** One row of `rejudge.jsonl`. `resolved: null` = the judge run failed to parse (inconclusive). */
|
|
155
|
+
export interface RejudgeRow {
|
|
156
|
+
iid: string
|
|
157
|
+
tag: RejudgeTag
|
|
158
|
+
patch: string
|
|
159
|
+
resolved: boolean | null
|
|
160
|
+
score?: number
|
|
161
|
+
secs?: number
|
|
162
|
+
patch_bytes?: number
|
|
163
|
+
error?: string
|
|
164
|
+
raw?: string
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** Evolution-round labels: SUP2/3/4 = rematch.jsonl / rematch2.jsonl / rematch3.jsonl. */
|
|
168
|
+
export type RematchArm = 'SUP2' | 'SUP3' | 'SUP4'
|
|
169
|
+
|
|
170
|
+
/** One row of a rematch*.jsonl evolution round. */
|
|
171
|
+
export interface RematchRow {
|
|
172
|
+
iid: string
|
|
173
|
+
arm: RematchArm
|
|
174
|
+
resolved: boolean
|
|
175
|
+
verify_pass: boolean
|
|
176
|
+
patch_lines: number
|
|
177
|
+
wall_s: number
|
|
178
|
+
spawned: number
|
|
179
|
+
workers: number
|
|
180
|
+
sup_status: 'completed' | 'running'
|
|
181
|
+
sup_verdict: 'delivered' | 'no-winner' | 'best-effort' | null
|
|
182
|
+
delivered: boolean | null
|
|
183
|
+
spentTokens: number | null
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/** Declarative arm identity for typed execution paths built on this module. */
|
|
187
|
+
export interface ArmSpec {
|
|
188
|
+
name: string
|
|
189
|
+
kind: 'solo' | 'supervisor'
|
|
190
|
+
env: Record<string, string>
|
|
191
|
+
provenance: { repo: string; commit: string }
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** One pre-registered holdout instance from `holdout.json`. */
|
|
195
|
+
export interface HoldoutEntry {
|
|
196
|
+
iid: string
|
|
197
|
+
repo: string
|
|
198
|
+
gold_official_resolved: boolean
|
|
199
|
+
verify_calibrated: boolean
|
|
200
|
+
selected_at_commit: string
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* The holdout registry: instances selected BEFORE any arm ran (see
|
|
205
|
+
* `holdout-preregister.log`), each with its gold patch verified against the
|
|
206
|
+
* calibrated judge. Only entries passing both checks are usable.
|
|
207
|
+
*/
|
|
208
|
+
export interface HoldoutRegistry {
|
|
209
|
+
entries: HoldoutEntry[]
|
|
210
|
+
/** Commit of the supervisor runtime (`loops`) at selection time. */
|
|
211
|
+
selectedAtCommit: string
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** Per-instance supervisor worker-session spend from `worker-tokens.json`. */
|
|
215
|
+
export interface WorkerTokens {
|
|
216
|
+
worker_sessions: number
|
|
217
|
+
worker_tok: number
|
|
218
|
+
}
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* META-HARNESS on the SWE scaffold — improve({ surface: 'code' }).
|
|
3
|
+
*
|
|
4
|
+
* A coding agent (Claude Code) REWRITES the scaffold LOGIC under bench/src (the seed prompt/playbook,
|
|
5
|
+
* runAgentic strategy/params, context handling, retry/patch synthesis) with the MODEL (glm worker) +
|
|
6
|
+
* the TOOL surface (list/read/edit[/run]) + the JUDGE held FIXED, judged on the official swebench
|
|
7
|
+
* Docker verdict, gated on a held-out instance split. This is the DGM/meta-harness recipe: let the
|
|
8
|
+
* SYSTEM find the scaffold lever from the RAW failure traces, not hand-build it.
|
|
9
|
+
*
|
|
10
|
+
* Wiring (all verified in this worktree):
|
|
11
|
+
* - improve()/codeProposerFor + rawTraceContext come from the LOCAL agent-runtime build, linked into
|
|
12
|
+
* this bench's node_modules (bench/node_modules/@tangle-network/agent-runtime -> /home/drew/code/agent-runtime).
|
|
13
|
+
* - The candidate proposer is agenticGenerator(harness:'claude'), BUT the shipped runLocalHarness
|
|
14
|
+
* spawns `claude --headless -p` and --headless is an unknown option on the current CLI (exit 1, no
|
|
15
|
+
* edits ever). We pass code.generator with a corrected runHarness that spawns
|
|
16
|
+
* `claude -p <prompt> --dangerously-skip-permissions` so the coding agent can actually edit the
|
|
17
|
+
* worktree. This is a harness-spawn fix, NOT a hand-authored scaffold edit — Claude still finds the
|
|
18
|
+
* lever itself from the traces.
|
|
19
|
+
* - Each candidate is a git worktree the driver forks off baseRef; Claude edits bench/src in place;
|
|
20
|
+
* `verify` (an import smoke of the edited scaffold) gates it before the expensive measurement.
|
|
21
|
+
* - MEASUREMENT: the code-aware agent fn shells into the candidate scaffold's OWN judge-free emit
|
|
22
|
+
* entrypoint (swe-emit-patch.mts) with cwd = the worktree, captures the unified diff, and returns
|
|
23
|
+
* it. improve()'s FIXED swebench judge scores that diff OUTSIDE the candidate, so the scaffold can
|
|
24
|
+
* never game its own axis. Baseline (empty surface) runs the UNEDITED scaffold from the main tree.
|
|
25
|
+
*
|
|
26
|
+
* Run (router WAF: keep example commands OUT of backticks):
|
|
27
|
+
* TANGLE_API_KEY=... dotenvx run --quiet -f .../agent-state.env --
|
|
28
|
+
* TRAIN_IDS=... HOLDOUT_IDS=... WORKER_MODEL=glm-4.6 GENERATIONS=1 POPULATION=2
|
|
29
|
+
* RUN_DIR=/abs/run BASE_REF=meta/swe-scaffold-baseline
|
|
30
|
+
* node_modules/.bin/tsx bench/src/swe-code-improve.mts
|
|
31
|
+
*/
|
|
32
|
+
import { spawn, spawnSync } from 'node:child_process'
|
|
33
|
+
import { existsSync, mkdirSync, symlinkSync } from 'node:fs'
|
|
34
|
+
import { join } from 'node:path'
|
|
35
|
+
import { improve, agenticGenerator } from '@tangle-network/agent-runtime'
|
|
36
|
+
import type { AgentProfile } from '@tangle-network/agent-interface'
|
|
37
|
+
import type { DispatchContext, JudgeConfig, Scenario } from '@tangle-network/agent-eval/contract'
|
|
38
|
+
import { createSweBenchAdapter } from './benchmarks/swe-bench'
|
|
39
|
+
import type { BenchTask } from './benchmarks/types'
|
|
40
|
+
|
|
41
|
+
/** The agent-runtime repo the worktree adapter forks candidate checkouts from (agent-runtime-swe is a
|
|
42
|
+
* worktree of it, so bench/src is tracked here). */
|
|
43
|
+
const REPO_ROOT = process.env.REPO_ROOT ?? '/home/drew/code/agent-runtime'
|
|
44
|
+
/** The main working tree = the baseline scaffold (its bench has the linked node_modules). */
|
|
45
|
+
const SWE_MAIN_ROOT = process.env.SWE_MAIN_ROOT ?? '/home/drew/code/agent-runtime-swe'
|
|
46
|
+
const MAIN_BENCH = join(SWE_MAIN_ROOT, 'bench')
|
|
47
|
+
const TSX_BIN = join(MAIN_BENCH, 'node_modules/.bin/tsx')
|
|
48
|
+
|
|
49
|
+
/** A candidate worktree is a fresh checkout with NO node_modules. Symlink the main bench's (already
|
|
50
|
+
* carrying the linked local runtime) so both the verifier and the measurement can run the scaffold. */
|
|
51
|
+
function ensureNodeModules(rootDir: string): void {
|
|
52
|
+
const target = join(rootDir, 'bench', 'node_modules')
|
|
53
|
+
if (existsSync(target)) return
|
|
54
|
+
try {
|
|
55
|
+
symlinkSync(join(MAIN_BENCH, 'node_modules'), target, 'dir')
|
|
56
|
+
} catch {
|
|
57
|
+
/* concurrent create / already exists — fine */
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Run the scaffold's judge-free emit entrypoint on ONE instance, from `rootDir`'s bench/src. Returns
|
|
62
|
+
* the unified diff (stdout, kept clean of any banner) + the tokens the worker reported (parsed from
|
|
63
|
+
* the [emit] stderr line). NO judge — the judge is held outside, in this file. */
|
|
64
|
+
function runEmit(
|
|
65
|
+
rootDir: string,
|
|
66
|
+
id: string,
|
|
67
|
+
env: Record<string, string>,
|
|
68
|
+
timeoutMs: number,
|
|
69
|
+
): Promise<{ patch: string; tokIn: number; tokOut: number; usd: number; code: number | null }> {
|
|
70
|
+
ensureNodeModules(rootDir)
|
|
71
|
+
const benchDir = join(rootDir, 'bench')
|
|
72
|
+
return new Promise((resolve) => {
|
|
73
|
+
const child = spawn(TSX_BIN, [join(benchDir, 'src/swe-emit-patch.mts')], {
|
|
74
|
+
cwd: benchDir,
|
|
75
|
+
env: { ...process.env, ...env, IDS: id },
|
|
76
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
77
|
+
})
|
|
78
|
+
let out = ''
|
|
79
|
+
let err = ''
|
|
80
|
+
child.stdout.on('data', (d) => (out += String(d)))
|
|
81
|
+
child.stderr.on('data', (d) => (err += String(d)))
|
|
82
|
+
const timer = setTimeout(() => {
|
|
83
|
+
if (!child.killed) child.kill('SIGKILL')
|
|
84
|
+
}, timeoutMs)
|
|
85
|
+
;(timer as { unref?: () => void }).unref?.()
|
|
86
|
+
child.on('error', () => {
|
|
87
|
+
clearTimeout(timer)
|
|
88
|
+
resolve({ patch: '', tokIn: 0, tokOut: 0, usd: 0, code: 1 })
|
|
89
|
+
})
|
|
90
|
+
child.on('close', (code) => {
|
|
91
|
+
clearTimeout(timer)
|
|
92
|
+
const m = /tok=in:(\d+)\/out:(\d+)/.exec(err)
|
|
93
|
+
const u = /usd=([0-9.]+)/.exec(err)
|
|
94
|
+
if (code !== 0) console.error(` [emit ${id}] exit=${code} stderr: ${err.slice(-400).replace(/\n/g, ' ')}`)
|
|
95
|
+
resolve({
|
|
96
|
+
patch: out,
|
|
97
|
+
tokIn: m ? Number(m[1]) : 0,
|
|
98
|
+
tokOut: m ? Number(m[2]) : 0,
|
|
99
|
+
usd: u ? Number(u[1]) : 0,
|
|
100
|
+
code,
|
|
101
|
+
})
|
|
102
|
+
})
|
|
103
|
+
})
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function main(): Promise<void> {
|
|
107
|
+
const routerKey = process.env.TANGLE_API_KEY
|
|
108
|
+
if (!routerKey) throw new Error('TANGLE_API_KEY required (worker calls the router)')
|
|
109
|
+
const routerBaseUrl = process.env.ROUTER_BASE ?? 'https://router.tangle.tools/v1'
|
|
110
|
+
const workerModel = process.env.WORKER_MODEL ?? 'glm-4.6'
|
|
111
|
+
const reflectModel = process.env.REFLECT_MODEL ?? 'glm-4.6'
|
|
112
|
+
const trainIds = (process.env.TRAIN_IDS ?? 'psf__requests-2931,pallets__flask-5014').split(',').map((s) => s.trim()).filter(Boolean)
|
|
113
|
+
const holdoutIds = (process.env.HOLDOUT_IDS ?? 'psf__requests-1142,psf__requests-1921').split(',').map((s) => s.trim()).filter(Boolean)
|
|
114
|
+
const generations = Number(process.env.GENERATIONS ?? 1)
|
|
115
|
+
const population = Number(process.env.POPULATION ?? 2)
|
|
116
|
+
const innerTurns = Number(process.env.INNER_TURNS ?? 40)
|
|
117
|
+
const maxTokens = Number(process.env.MAX_TOKENS ?? 12000)
|
|
118
|
+
const enableRun = ['1', 'true', 'yes'].includes((process.env.RUN_TOOL ?? '').toLowerCase())
|
|
119
|
+
const baseRef = process.env.BASE_REF ?? 'meta/swe-scaffold-baseline'
|
|
120
|
+
const worktreeDir = process.env.WORKTREE_DIR ?? '/tmp/claude-1000/-home-drew-code-supervisor-lab/9ee6a456-a94f-474c-9888-b4afc9bc26bd/scratchpad/mh-worktrees'
|
|
121
|
+
const runDir = process.env.RUN_DIR ?? '/tmp/claude-1000/-home-drew-code-supervisor-lab/9ee6a456-a94f-474c-9888-b4afc9bc26bd/scratchpad/mh-run'
|
|
122
|
+
const emitTimeoutMs = Number(process.env.EMIT_TIMEOUT_MS ?? 600_000)
|
|
123
|
+
const harnessTimeoutMs = Number(process.env.HARNESS_TIMEOUT_MS ?? 900_000)
|
|
124
|
+
mkdirSync(worktreeDir, { recursive: true })
|
|
125
|
+
mkdirSync(runDir, { recursive: true })
|
|
126
|
+
|
|
127
|
+
const allIds = [...new Set([...trainIds, ...holdoutIds])]
|
|
128
|
+
|
|
129
|
+
console.log('=== META-HARNESS on the SWE scaffold — improve(surface:code) ===')
|
|
130
|
+
console.log(`worker=${workerModel} reflect=${reflectModel} router=${routerBaseUrl} runTool=${enableRun}`)
|
|
131
|
+
console.log(`train=[${trainIds.join(', ')}] holdout=[${holdoutIds.join(', ')}]`)
|
|
132
|
+
console.log(`generations=${generations} population=${population} innerTurns=${innerTurns} maxTokens=${maxTokens}`)
|
|
133
|
+
console.log(`repoRoot=${REPO_ROOT} baseRef=${baseRef}`)
|
|
134
|
+
console.log(`runDir=${runDir} worktreeDir=${worktreeDir}`)
|
|
135
|
+
|
|
136
|
+
// Fixed swebench Docker judge (held OUTSIDE every candidate) + task pool.
|
|
137
|
+
const adapter = createSweBenchAdapter()
|
|
138
|
+
const pool = await adapter.loadTasks({ ids: allIds, split: 'test' })
|
|
139
|
+
const byId = new Map<string, BenchTask>(pool.map((t) => [t.id, t]))
|
|
140
|
+
for (const id of allIds) if (!byId.has(id)) throw new Error(`instance not found in Verified: ${id}`)
|
|
141
|
+
|
|
142
|
+
const workerEnv: Record<string, string> = {
|
|
143
|
+
WORKER_MODEL: workerModel,
|
|
144
|
+
MAX_TOKENS: String(maxTokens),
|
|
145
|
+
INNER_TURNS: String(innerTurns),
|
|
146
|
+
ROUTER_BASE: routerBaseUrl,
|
|
147
|
+
TANGLE_API_KEY: routerKey,
|
|
148
|
+
RUN_TOOL: enableRun ? '1' : '0',
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// The code-aware measurement agent. A CodeSurface -> run the candidate scaffold in its worktree; an
|
|
152
|
+
// empty/string surface (the baseline arm) -> run the UNEDITED scaffold from the main tree. Same path,
|
|
153
|
+
// so baseline and candidates are measured identically.
|
|
154
|
+
const agent = async (surface: unknown, scenario: Scenario, ctx: DispatchContext): Promise<string | null> => {
|
|
155
|
+
const isCode = !!surface && typeof surface === 'object' && (surface as { kind?: string }).kind === 'code'
|
|
156
|
+
const worktreeRef = isCode ? String((surface as { worktreeRef?: string }).worktreeRef ?? '') : ''
|
|
157
|
+
const rootDir = isCode && worktreeRef && existsSync(worktreeRef) ? worktreeRef : SWE_MAIN_ROOT
|
|
158
|
+
const t0 = Date.now()
|
|
159
|
+
const r = await runEmit(rootDir, scenario.id, workerEnv, emitTimeoutMs)
|
|
160
|
+
const hasPatch = r.patch.trim().length > 0
|
|
161
|
+
// Report real usage; floor a patch-bearing zero-usage cell so the stub-guard cannot abort on a
|
|
162
|
+
// router telemetry gap (lift is judge-derived, so this only affects cost accounting).
|
|
163
|
+
const zeroUsage = r.tokIn === 0 && r.tokOut === 0
|
|
164
|
+
ctx.cost.observe(zeroUsage && hasPatch ? Math.max(r.usd, 0.0001) : r.usd, workerModel)
|
|
165
|
+
ctx.cost.observeTokens(
|
|
166
|
+
zeroUsage && hasPatch ? { input: Math.max(r.tokIn, 1), output: Math.max(r.tokOut, 1) } : { input: r.tokIn, output: r.tokOut },
|
|
167
|
+
)
|
|
168
|
+
const files = hasPatch ? [...r.patch.matchAll(/^diff --git a\/(\S+)/gm)].map((m) => m[1]) : []
|
|
169
|
+
console.log(
|
|
170
|
+
` [measure] ${isCode ? 'cand' : 'base'} ${scenario.id} patch=${r.patch.length}b files=[${files.join(', ') || 'none'}] ` +
|
|
171
|
+
`tok=in:${r.tokIn}/out:${r.tokOut} ${Math.round((Date.now() - t0) / 1000)}s${isCode ? ` @ ${worktreeRef}` : ''}`,
|
|
172
|
+
)
|
|
173
|
+
return hasPatch ? r.patch : null
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const judge: JudgeConfig<string, Scenario> = {
|
|
177
|
+
name: 'swebench-docker',
|
|
178
|
+
dimensions: [{ key: 'resolved', description: 'FAIL_TO_PASS + PASS_TO_PASS resolved by the official swebench Docker harness' }],
|
|
179
|
+
async score({ artifact, scenario }) {
|
|
180
|
+
const patch = String(artifact ?? '')
|
|
181
|
+
if (!patch.trim()) {
|
|
182
|
+
console.log(` [judge] ${scenario.id} resolved=0 (no patch)`)
|
|
183
|
+
return { dimensions: { resolved: 0 }, composite: 0, notes: 'no patch emitted' }
|
|
184
|
+
}
|
|
185
|
+
const bt = byId.get(scenario.id)
|
|
186
|
+
if (!bt) throw new Error(`judge: unknown scenario ${scenario.id}`)
|
|
187
|
+
const s = await adapter.judge(bt, patch)
|
|
188
|
+
console.log(` [judge] ${scenario.id} resolved=${s.resolved ? 1 : 0}`)
|
|
189
|
+
return { dimensions: { resolved: s.resolved ? 1 : 0 }, composite: s.resolved ? 1 : 0, notes: (s.detail ?? '').slice(0, 200) }
|
|
190
|
+
},
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// The corrected coding-harness spawn: `claude -p <prompt> --dangerously-skip-permissions`. The shipped
|
|
194
|
+
// runLocalHarness uses `claude --headless -p` (unknown option on this CLI). agenticGenerator ignores
|
|
195
|
+
// the return value (it reads worktree dirtiness), so a minimal result shape is enough.
|
|
196
|
+
const runHarness = (o: { cwd: string; taskPrompt: string; timeoutMs?: number; signal?: AbortSignal }): Promise<{ exitCode: number | null; stdout: string; stderr: string; killedBySignal: NodeJS.Signals | null; durationMs: number; timedOut: boolean }> => {
|
|
197
|
+
const started = Date.now()
|
|
198
|
+
return new Promise((resolve) => {
|
|
199
|
+
const child = spawn('claude', ['-p', o.taskPrompt, '--dangerously-skip-permissions'], {
|
|
200
|
+
cwd: o.cwd,
|
|
201
|
+
env: process.env,
|
|
202
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
203
|
+
})
|
|
204
|
+
let stdout = ''
|
|
205
|
+
let stderr = ''
|
|
206
|
+
let timedOut = false
|
|
207
|
+
child.stdout?.on('data', (d) => (stdout += String(d)))
|
|
208
|
+
child.stderr?.on('data', (d) => (stderr += String(d)))
|
|
209
|
+
const timer = setTimeout(() => {
|
|
210
|
+
timedOut = true
|
|
211
|
+
if (!child.killed) child.kill('SIGTERM')
|
|
212
|
+
}, o.timeoutMs ?? harnessTimeoutMs)
|
|
213
|
+
;(timer as { unref?: () => void }).unref?.()
|
|
214
|
+
const onAbort = () => {
|
|
215
|
+
if (!child.killed) child.kill('SIGTERM')
|
|
216
|
+
}
|
|
217
|
+
o.signal?.addEventListener('abort', onAbort, { once: true })
|
|
218
|
+
child.on('error', () => {
|
|
219
|
+
clearTimeout(timer)
|
|
220
|
+
resolve({ exitCode: 1, stdout, stderr: `${stderr}\n[spawn error]`, killedBySignal: null, durationMs: Date.now() - started, timedOut })
|
|
221
|
+
})
|
|
222
|
+
child.on('close', (code, signal) => {
|
|
223
|
+
clearTimeout(timer)
|
|
224
|
+
o.signal?.removeEventListener('abort', onAbort)
|
|
225
|
+
console.error(` [proposer:claude] exit=${code} wall=${Math.round((Date.now() - started) / 1000)}s out=${stdout.length}b`)
|
|
226
|
+
resolve({ exitCode: code, stdout, stderr, killedBySignal: signal, durationMs: Date.now() - started, timedOut })
|
|
227
|
+
})
|
|
228
|
+
})
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Domain prompt: name the EDIT BOUNDARY (scaffold logic only) + keep the raw-trace evidence discipline
|
|
232
|
+
// (agenticGenerator discards a raw-trace candidate that doesn't inspect a trace + write the diagnosis).
|
|
233
|
+
const buildPrompt = (args: { report: unknown; findings: Array<{ severity?: string; claim?: string; recommended_action?: string }> }): string => {
|
|
234
|
+
const lines: string[] = [
|
|
235
|
+
'You are improving a SWE-bench coding SCAFFOLD: a harness that drives a FIXED worker model to fix real GitHub bugs via list_files/read_file/edit_file tools. Your job is to rewrite the SCAFFOLD LOGIC so the SAME worker model resolves MORE instances on a held-out split.',
|
|
236
|
+
'',
|
|
237
|
+
'EDIT ONLY the scaffold logic under bench/src:',
|
|
238
|
+
' - the seed prompt / playbook: SWE_SEED_PROMPT and SWE_SEED_PROMPT_WITH_RUN in bench/src/swe-bench-env.ts',
|
|
239
|
+
' - the exploration/context handling in bench/src/swe-bench-env.ts: list_files walk depth and 240-entry cap, the read_file 24000-char truncation, edit_file retry messaging, patch synthesis (git diff)',
|
|
240
|
+
' - the runAgentic strategy/params in bench/src/swe-emit-patch.mts: innerTurns default, budget, how the patch is captured',
|
|
241
|
+
'',
|
|
242
|
+
'DO NOT change (FIXED for this search):',
|
|
243
|
+
' - the TOOL surface: the list_files/read_file/edit_file/run tool NAMES, JSON signatures, or the path jail. Do not add or remove a tool.',
|
|
244
|
+
' - the worker MODEL or MAX_TOKENS (passed via env — never hardcode a different model).',
|
|
245
|
+
' - the swebench Docker JUDGE.',
|
|
246
|
+
' - the swe-emit-patch.mts I/O contract: it still reads IDS + WORKER_MODEL + router env and prints ONLY the unified diff to stdout (diagnostics to stderr). You may change HOW the patch is produced, never this stdin/stdout contract.',
|
|
247
|
+
'',
|
|
248
|
+
'Make the smallest set of edits that addresses the failure evidence below, then stop. Leave changes in the working tree; do NOT commit.',
|
|
249
|
+
'',
|
|
250
|
+
'Failure evidence from the previous generation (its RAW run traces are on disk):',
|
|
251
|
+
]
|
|
252
|
+
for (const f of args.findings) {
|
|
253
|
+
lines.push(`- (${f.severity ?? 'info'}) ${f.claim ?? ''}`)
|
|
254
|
+
if (f.recommended_action) lines.push(` -> ${f.recommended_action}`)
|
|
255
|
+
}
|
|
256
|
+
lines.push(
|
|
257
|
+
'',
|
|
258
|
+
'Raw-trace evidence requirement (enforced — a candidate that skips this is discarded):',
|
|
259
|
+
' - Inspect at least one raw trace path named above (grep/cat/ls it) BEFORE editing.',
|
|
260
|
+
' - Write .improve/raw-trace-diagnosis.md in this worktree containing: the exact trace path(s) you inspected, the failure mechanism you found, and the scaffold-logic change you made.',
|
|
261
|
+
' - A candidate with ONLY that file changed (no real scaffold edit) is discarded.',
|
|
262
|
+
)
|
|
263
|
+
return lines.join('\n')
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const verify = (worktreePath: string): { ok: boolean; feedback?: string } => {
|
|
267
|
+
ensureNodeModules(worktreePath)
|
|
268
|
+
const res = spawnSync(TSX_BIN, [join(worktreePath, 'bench/src/swe-emit-patch.mts')], {
|
|
269
|
+
cwd: join(worktreePath, 'bench'),
|
|
270
|
+
env: { ...process.env, SWE_EMIT_SMOKE: '1' },
|
|
271
|
+
encoding: 'utf-8',
|
|
272
|
+
timeout: 180_000,
|
|
273
|
+
})
|
|
274
|
+
if (res.status === 0) return { ok: true }
|
|
275
|
+
const out = `${res.stdout ?? ''}${res.stderr ?? ''}`.trim()
|
|
276
|
+
return { ok: false, feedback: `edited scaffold failed import smoke (tsx swe-emit-patch SWE_EMIT_SMOKE=1):\n${out.slice(0, 3000)}` }
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const generator = agenticGenerator({
|
|
280
|
+
harness: 'claude',
|
|
281
|
+
verify,
|
|
282
|
+
timeoutMs: harnessTimeoutMs,
|
|
283
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
284
|
+
buildPrompt: buildPrompt as any,
|
|
285
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
286
|
+
runHarness: runHarness as any,
|
|
287
|
+
})
|
|
288
|
+
|
|
289
|
+
const profile: AgentProfile = { name: 'swe-scaffold', prompt: { systemPrompt: '' } }
|
|
290
|
+
const scenarios: Scenario[] = allIds.map((id) => ({ id, kind: 'swe-bench-verified' }))
|
|
291
|
+
const holdoutScenarios: Scenario[] = holdoutIds.map((id) => ({ id, kind: 'swe-bench-verified' }))
|
|
292
|
+
|
|
293
|
+
const out = await improve(profile, [], {
|
|
294
|
+
surface: 'code',
|
|
295
|
+
gate: 'holdout',
|
|
296
|
+
code: { repoRoot: REPO_ROOT, baseRef, worktreeDir, generator },
|
|
297
|
+
rawTraceContext: true,
|
|
298
|
+
runDir,
|
|
299
|
+
scenarios,
|
|
300
|
+
judge,
|
|
301
|
+
agent,
|
|
302
|
+
expectUsage: 'warn',
|
|
303
|
+
budget: { generations, populationSize: population, holdoutScenarios, maxConcurrency: 1, reps: 1 },
|
|
304
|
+
llm: { baseUrl: routerBaseUrl, apiKey: routerKey, model: reflectModel },
|
|
305
|
+
})
|
|
306
|
+
|
|
307
|
+
console.log('\n=== RESULT ===')
|
|
308
|
+
console.log(`gateDecision=${out.gateDecision} shipped=${out.shipped} lift=${out.lift}`)
|
|
309
|
+
console.log(`baseline holdout composite = ${out.raw.baseline.compositeMean}`)
|
|
310
|
+
console.log(`winner holdout composite = ${out.raw.winner.compositeMean}`)
|
|
311
|
+
console.log(`baseline per-scenario: ${JSON.stringify(out.raw.baseline.perScenario)}`)
|
|
312
|
+
console.log(`winner per-scenario: ${JSON.stringify(out.raw.winner.perScenario)}`)
|
|
313
|
+
if (out.raw.winner.label) console.log(`winner label: ${out.raw.winner.label}`)
|
|
314
|
+
if (out.raw.winner.summary) console.log(`winner summary: ${out.raw.winner.summary}`)
|
|
315
|
+
for (const gen of out.raw.generations ?? []) {
|
|
316
|
+
console.log(`\n-- generation ${gen.record.generationIndex} candidates --`)
|
|
317
|
+
for (const c of gen.record.candidates) {
|
|
318
|
+
const perScenario = (c as { scenarios?: Array<{ scenarioId: string; composite: number }> }).scenarios ?? []
|
|
319
|
+
const detail = perScenario.map((s) => `${s.scenarioId}=${s.composite}`).join(' ')
|
|
320
|
+
console.log(` candidate ${c.surfaceHash.slice(0, 8)} composite=${c.composite}${c.label ? ` "${c.label}"` : ''} [${detail}]`)
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
main().catch((e) => {
|
|
326
|
+
console.error(e instanceof Error ? (e.stack ?? e.message) : String(e))
|
|
327
|
+
process.exit(1)
|
|
328
|
+
})
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Judge-FREE patch emitter for the SWE scaffold — the FIXED measurement entrypoint.
|
|
3
|
+
*
|
|
4
|
+
* Given ONE instance id (IDS) + worker model + router env, this runs the candidate scaffold's OWN
|
|
5
|
+
* `createSweBenchEnvironment` + `runAgentic` IN THIS worktree and prints the unified `git diff` of
|
|
6
|
+
* the agent's edits to STDOUT. It does NOT judge — the swebench Docker judge is held OUTSIDE the
|
|
7
|
+
* candidate (in swe-code-improve.mts) so the scaffold can never grade its own axis.
|
|
8
|
+
*
|
|
9
|
+
* This is the seam the code-aware improve() agent shells into. Its I/O contract is FIXED and the
|
|
10
|
+
* scaffold proposer must NOT change it:
|
|
11
|
+
* IN (env): IDS=<one instance id>, WORKER_MODEL, MAX_TOKENS, ROUTER_BASE, TANGLE_API_KEY,
|
|
12
|
+
* INNER_TURNS, BUDGET, RUN_TOOL
|
|
13
|
+
* OUT (fd1): the unified diff (empty string when the agent made no edit)
|
|
14
|
+
* diagnostics go to STDERR only, so stdout stays a clean patch.
|
|
15
|
+
*
|
|
16
|
+
* SWE_EMIT_SMOKE=1 → import/wiring check only: the module (and swe-bench-env) loaded, print READY on
|
|
17
|
+
* stderr and exit 0 WITHOUT a clone / model call / dataset read. This is what the candidate verifier
|
|
18
|
+
* runs to discard a scaffold edit that no longer imports.
|
|
19
|
+
*
|
|
20
|
+
* Command examples in prompts are kept OUT of backticks on purpose (router WAF 403s backtick-wrapped
|
|
21
|
+
* command text) — see swe-bench-env.ts.
|
|
22
|
+
*/
|
|
23
|
+
import { execFile } from 'node:child_process'
|
|
24
|
+
import { promisify } from 'node:util'
|
|
25
|
+
import type { AgenticSurface, ArtifactHandle, SurfaceScore } from '@tangle-network/agent-runtime/loops'
|
|
26
|
+
import { refine, runAgentic } from '@tangle-network/agent-runtime/loops'
|
|
27
|
+
import { createSweBenchEnvironment, SWE_SEED_PROMPT, SWE_SEED_PROMPT_WITH_RUN } from './swe-bench-env'
|
|
28
|
+
|
|
29
|
+
const exec = promisify(execFile)
|
|
30
|
+
|
|
31
|
+
async function main(): Promise<void> {
|
|
32
|
+
const smoke = ['1', 'true', 'yes'].includes((process.env.SWE_EMIT_SMOKE ?? '').toLowerCase())
|
|
33
|
+
if (smoke) {
|
|
34
|
+
// The import graph (this file + swe-bench-env + the linked runtime) resolved by the time we get
|
|
35
|
+
// here. That is the whole check — no clone, no model call, no dataset read.
|
|
36
|
+
console.error('SWE_EMIT_SMOKE ok: swe-emit-patch + swe-bench-env import graph loaded')
|
|
37
|
+
return
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const routerKey = process.env.TANGLE_API_KEY
|
|
41
|
+
if (!routerKey) throw new Error('TANGLE_API_KEY required (the worker calls the router)')
|
|
42
|
+
const routerBaseUrl = process.env.ROUTER_BASE ?? 'https://router.tangle.tools/v1'
|
|
43
|
+
const model = process.env.WORKER_MODEL ?? 'glm-5.2'
|
|
44
|
+
const ids = (process.env.IDS ?? '').split(',').map((s) => s.trim()).filter(Boolean)
|
|
45
|
+
if (ids.length !== 1) throw new Error(`swe-emit-patch: exactly one IDS required, got [${ids.join(', ')}]`)
|
|
46
|
+
const id = ids[0]!
|
|
47
|
+
const innerTurns = Number(process.env.INNER_TURNS ?? 40)
|
|
48
|
+
const maxTokens = Number(process.env.MAX_TOKENS ?? 12000)
|
|
49
|
+
const budget = Number(process.env.BUDGET ?? 1)
|
|
50
|
+
const enableRun = ['1', 'true', 'yes'].includes((process.env.RUN_TOOL ?? '').toLowerCase())
|
|
51
|
+
|
|
52
|
+
const { environment, adapter } = await createSweBenchEnvironment(1, { ids, enableRun })
|
|
53
|
+
const pool = await adapter.loadTasks({ ids, split: 'test' })
|
|
54
|
+
const bt = pool.find((t) => t.id === id)
|
|
55
|
+
if (!bt) throw new Error(`swe-emit-patch: instance not found in Verified: ${id}`)
|
|
56
|
+
|
|
57
|
+
const task = {
|
|
58
|
+
id: bt.id,
|
|
59
|
+
systemPrompt: enableRun ? SWE_SEED_PROMPT_WITH_RUN : SWE_SEED_PROMPT,
|
|
60
|
+
userPrompt: bt.prompt,
|
|
61
|
+
meta: { instanceId: bt.id },
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Capture the patch from inside score() (called during the refine loop, BEFORE the surface closes
|
|
65
|
+
// and rms the checkout). Keep the LATEST non-empty diff so accumulated refinements win and a later
|
|
66
|
+
// empty read never clobbers a real patch.
|
|
67
|
+
let capturedPatch = ''
|
|
68
|
+
const proxy: AgenticSurface = {
|
|
69
|
+
...environment,
|
|
70
|
+
async score(_t, handle: ArtifactHandle): Promise<SurfaceScore> {
|
|
71
|
+
try {
|
|
72
|
+
const d = await exec('git', ['-C', handle.id, 'diff'], { maxBuffer: 40_000_000, timeout: 60_000 })
|
|
73
|
+
if (d.stdout.trim()) capturedPatch = d.stdout
|
|
74
|
+
} catch {
|
|
75
|
+
/* workspace gone or git error → keep whatever we already captured */
|
|
76
|
+
}
|
|
77
|
+
return { passes: capturedPatch.trim() ? 1 : 0, total: 1, errored: 0 }
|
|
78
|
+
},
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const t0 = Date.now()
|
|
82
|
+
const r = await runAgentic({
|
|
83
|
+
surface: proxy,
|
|
84
|
+
task,
|
|
85
|
+
strategy: refine,
|
|
86
|
+
routerBaseUrl,
|
|
87
|
+
routerKey,
|
|
88
|
+
model,
|
|
89
|
+
maxTokens,
|
|
90
|
+
innerTurns,
|
|
91
|
+
budget,
|
|
92
|
+
})
|
|
93
|
+
const files = capturedPatch ? [...capturedPatch.matchAll(/^diff --git a\/(\S+)/gm)].map((m) => m[1]) : []
|
|
94
|
+
console.error(
|
|
95
|
+
`[emit] ${id} shots=${r.shots} completions=${r.completions} tok=in:${r.tokens.input}/out:${r.tokens.output} ` +
|
|
96
|
+
`patch=${capturedPatch.length}b files=[${files.join(', ') || 'none'}] ${Math.round((Date.now() - t0) / 1000)}s`,
|
|
97
|
+
)
|
|
98
|
+
process.stdout.write(capturedPatch)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
main().catch((e) => {
|
|
102
|
+
console.error(e instanceof Error ? (e.stack ?? e.message) : String(e))
|
|
103
|
+
process.exit(1)
|
|
104
|
+
})
|