@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,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SELF-IMPROVEMENT on the SEE-able LOCAL SWE-bench path — NO tangle sandbox.
|
|
3
|
+
*
|
|
4
|
+
* Composes the three proven pieces into ONE held-out-gated improvement generation:
|
|
5
|
+
* 1. `improve({ surface: 'prompt' })` (agent-runtime) drives the loop: it asks
|
|
6
|
+
* `gepaProposer` to EVOLVE the SWE agent's system prompt, then measures each
|
|
7
|
+
* candidate prompt on real instances and gates the winner on a held-out split.
|
|
8
|
+
* 2. Per candidate + scenario, the `agent` fn runs the LOCAL SWE env
|
|
9
|
+
* (`createSweBenchEnvironment` + `runAgentic`): clone the instance repo to a
|
|
10
|
+
* host tmpdir, run the jailed list/read/edit tool loop with the CANDIDATE
|
|
11
|
+
* prompt as the system prompt, and return the `git diff` as the artifact.
|
|
12
|
+
* 3. The `judge` scores that patch with the OFFICIAL swebench Docker harness
|
|
13
|
+
* (`adapter.judge` → resolved 0/1). The only remote call is the model
|
|
14
|
+
* completion via the router; nothing touches sandbox.tangle.tools.
|
|
15
|
+
*
|
|
16
|
+
* IN-LOOP score is a cheap patch-exists proxy (NOT the Docker judge) so the ONLY
|
|
17
|
+
* Docker run per cell is the improve judge — one deterministic verdict per cell.
|
|
18
|
+
*
|
|
19
|
+
* Cost per run = T·(1 + G·P) + 2·H cells, each = 1 clone + 1 runAgentic + 1 judge.
|
|
20
|
+
*
|
|
21
|
+
* TANGLE_API_KEY=… dotenvx run -f …/agent-state.env -- \
|
|
22
|
+
* TRAIN_IDS=psf__requests-2931 HOLDOUT_IDS=psf__requests-1142 \
|
|
23
|
+
* GENERATIONS=1 POPULATION=1 WORKER_MODEL=glm-4.6 REFLECT_MODEL=glm-4.6 \
|
|
24
|
+
* node_modules/.bin/tsx bench/src/swe-improve.mts
|
|
25
|
+
*/
|
|
26
|
+
import { execFile } from 'node:child_process'
|
|
27
|
+
import { promisify } from 'node:util'
|
|
28
|
+
import { improve } from '@tangle-network/agent-runtime'
|
|
29
|
+
import type { AgentProfile } from '@tangle-network/agent-interface'
|
|
30
|
+
import type { AgenticSurface, ArtifactHandle, SurfaceScore } from '@tangle-network/agent-runtime/loops'
|
|
31
|
+
import { refine, runAgentic } from '@tangle-network/agent-runtime/loops'
|
|
32
|
+
import type { DispatchContext, JudgeConfig, Scenario } from '@tangle-network/agent-eval/contract'
|
|
33
|
+
import { gepaProposer } from '@tangle-network/agent-eval/campaign'
|
|
34
|
+
import { createSweBenchAdapter } from './benchmarks/swe-bench'
|
|
35
|
+
import type { BenchTask } from './benchmarks/types'
|
|
36
|
+
import { createSweBenchEnvironment, SWE_SEED_PROMPT, SWE_SEED_PROMPT_WITH_RUN } from './swe-bench-env'
|
|
37
|
+
|
|
38
|
+
const exec = promisify(execFile)
|
|
39
|
+
|
|
40
|
+
async function main(): Promise<void> {
|
|
41
|
+
const routerKey = process.env.TANGLE_API_KEY
|
|
42
|
+
if (!routerKey) throw new Error('TANGLE_API_KEY required (the worker + reflection call the router)')
|
|
43
|
+
const routerBaseUrl = process.env.ROUTER_BASE ?? 'https://router.tangle.tools/v1'
|
|
44
|
+
const workerModel = process.env.WORKER_MODEL ?? 'glm-4.6'
|
|
45
|
+
const reflectModel = process.env.REFLECT_MODEL ?? 'glm-4.6'
|
|
46
|
+
const trainIds = (process.env.TRAIN_IDS ?? 'psf__requests-2931').split(',').map((s) => s.trim()).filter(Boolean)
|
|
47
|
+
const holdoutIds = (process.env.HOLDOUT_IDS ?? 'psf__requests-1142').split(',').map((s) => s.trim()).filter(Boolean)
|
|
48
|
+
const generations = Number(process.env.GENERATIONS ?? 1)
|
|
49
|
+
const population = Number(process.env.POPULATION ?? 1)
|
|
50
|
+
const innerTurns = Number(process.env.INNER_TURNS ?? 40)
|
|
51
|
+
const workerMaxTokens = Number(process.env.MAX_TOKENS ?? 8000)
|
|
52
|
+
const reflectMaxTokens = Number(process.env.REFLECT_MAX_TOKENS ?? 12000)
|
|
53
|
+
const maxConcurrency = Number(process.env.MAX_CONCURRENCY ?? 1)
|
|
54
|
+
const budgetShots = Number(process.env.BUDGET ?? 1)
|
|
55
|
+
// WITH-TOOLS arm: RUN_TOOL=1 exposes the jailed `run` tool AND swaps the seed to the run-aware prompt.
|
|
56
|
+
// Default OFF ⇒ reproduces the read/edit-only baseline denominator unchanged.
|
|
57
|
+
const enableRun = ['1', 'true', 'yes'].includes((process.env.RUN_TOOL ?? '').toLowerCase())
|
|
58
|
+
const SEED_PROMPT = enableRun ? SWE_SEED_PROMPT_WITH_RUN : SWE_SEED_PROMPT
|
|
59
|
+
|
|
60
|
+
const allIds = [...new Set([...trainIds, ...holdoutIds])]
|
|
61
|
+
const cellsMax = trainIds.length * (1 + generations * population) + 2 * holdoutIds.length
|
|
62
|
+
|
|
63
|
+
console.log('═══ SWE-bench self-improvement — SEE-able LOCAL (no tangle sandbox) ═══')
|
|
64
|
+
console.log(`worker=${workerModel} reflect=${reflectModel} router=${routerBaseUrl}`)
|
|
65
|
+
console.log(`train=[${trainIds.join(', ')}] holdout=[${holdoutIds.join(', ')}]`)
|
|
66
|
+
console.log(`generations=${generations} population=${population} innerTurns=${innerTurns} workerMaxTokens=${workerMaxTokens} reflectMaxTokens=${reflectMaxTokens} runTool=${enableRun}`)
|
|
67
|
+
console.log(`≈ ${cellsMax} cells max (each = 1 clone + 1 runAgentic + 1 Docker judge)\n`)
|
|
68
|
+
|
|
69
|
+
if (process.env.DRYRUN) {
|
|
70
|
+
// Import + wiring smoke: prove every module resolves and the plan is well-formed
|
|
71
|
+
// WITHOUT paying for a clone / model call / Docker judge.
|
|
72
|
+
console.log(`DRYRUN: imports OK (improve=${typeof improve}, gepaProposer=${typeof gepaProposer}, runAgentic=${typeof runAgentic}, refine=${typeof refine})`)
|
|
73
|
+
return
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const { environment, adapter } = await createSweBenchEnvironment(allIds.length, { ids: allIds, enableRun })
|
|
77
|
+
const pool = await adapter.loadTasks({ ids: allIds, split: 'test' })
|
|
78
|
+
const byId = new Map<string, BenchTask>(pool.map((t) => [t.id, t]))
|
|
79
|
+
for (const id of allIds) if (!byId.has(id)) throw new Error(`instance not found in Verified: ${id}`)
|
|
80
|
+
|
|
81
|
+
// The agent under improvement: run the LOCAL SWE env with the CANDIDATE prompt on
|
|
82
|
+
// one instance, return the git-diff patch. A per-call proxy captures the patch in
|
|
83
|
+
// score() BEFORE runAgentic closes (rm) the workspace; its score is a cheap
|
|
84
|
+
// patch-exists proxy so the ONLY Docker run per cell is the improve judge.
|
|
85
|
+
const agent = async (surface: unknown, scenario: Scenario, ctx: DispatchContext): Promise<string | null> => {
|
|
86
|
+
const promptText = String(surface)
|
|
87
|
+
const bt = byId.get(scenario.id)
|
|
88
|
+
if (!bt) throw new Error(`agent: unknown scenario ${scenario.id}`)
|
|
89
|
+
const task = { id: bt.id, systemPrompt: promptText, userPrompt: bt.prompt, meta: { instanceId: bt.id } }
|
|
90
|
+
let capturedPatch = ''
|
|
91
|
+
const stats = { list: 0, read: 0, edit_ok: 0, edit_fail: 0, run: 0, run_err: 0 }
|
|
92
|
+
const proxy: AgenticSurface = {
|
|
93
|
+
...environment,
|
|
94
|
+
async call(handle, name, args) {
|
|
95
|
+
const res = await environment.call(handle, name, args)
|
|
96
|
+
const r = String(res)
|
|
97
|
+
if (name === 'list_files') stats.list += 1
|
|
98
|
+
else if (name === 'read_file') stats.read += 1
|
|
99
|
+
else if (name === 'edit_file') r.startsWith('edited ') ? (stats.edit_ok += 1) : (stats.edit_fail += 1)
|
|
100
|
+
else if (name === 'run') r.startsWith('ERROR:') ? (stats.run_err += 1) : (stats.run += 1)
|
|
101
|
+
return res
|
|
102
|
+
},
|
|
103
|
+
async score(_t, handle: ArtifactHandle): Promise<SurfaceScore> {
|
|
104
|
+
try {
|
|
105
|
+
const diff = await exec('git', ['-C', handle.id, 'diff'], { maxBuffer: 40_000_000, timeout: 60_000 })
|
|
106
|
+
if (!capturedPatch.trim() && diff.stdout.trim()) capturedPatch = diff.stdout
|
|
107
|
+
} catch {
|
|
108
|
+
/* workspace gone or git error → treat as no patch */
|
|
109
|
+
}
|
|
110
|
+
return { passes: capturedPatch.trim() ? 1 : 0, total: 1, errored: 0 }
|
|
111
|
+
},
|
|
112
|
+
}
|
|
113
|
+
const t0 = Date.now()
|
|
114
|
+
const r = await runAgentic({
|
|
115
|
+
surface: proxy,
|
|
116
|
+
task,
|
|
117
|
+
strategy: refine,
|
|
118
|
+
routerBaseUrl,
|
|
119
|
+
routerKey,
|
|
120
|
+
model: workerModel,
|
|
121
|
+
maxTokens: workerMaxTokens,
|
|
122
|
+
innerTurns,
|
|
123
|
+
budget: budgetShots,
|
|
124
|
+
})
|
|
125
|
+
// Report REAL cost/tokens so the backend-integrity guard sees a real backend
|
|
126
|
+
// rather than a silent-zero stub. A glm-5.2 turn occasionally returns a real
|
|
127
|
+
// patch with an UNPOPULATED usage block (a router telemetry gap on some
|
|
128
|
+
// reasoning-model responses — NOT a stub: the cell made real tool calls and
|
|
129
|
+
// produced a patch). In that gap case report a nominal floor so the stub-guard
|
|
130
|
+
// (artifact + zero usage) cannot abort the whole campaign on a telemetry gap.
|
|
131
|
+
// The lift metric is judge-derived, so a floored count does not distort it; only
|
|
132
|
+
// cost accounting undercounts those few cells (disclosed). No-patch cells return
|
|
133
|
+
// null below and are skipped by the guard's own contract, so this floor only
|
|
134
|
+
// ever applies to a cell that genuinely produced a patch.
|
|
135
|
+
const zeroUsage = (r.tokens.input ?? 0) === 0 && (r.tokens.output ?? 0) === 0
|
|
136
|
+
const hasPatch = capturedPatch.trim().length > 0
|
|
137
|
+
ctx.cost.observe(zeroUsage && hasPatch ? Math.max(r.usd ?? 0, 0.0001) : r.usd ?? 0, workerModel)
|
|
138
|
+
ctx.cost.observeTokens(
|
|
139
|
+
zeroUsage && hasPatch
|
|
140
|
+
? { input: Math.max(r.tokens.input ?? 0, 1), output: Math.max(r.tokens.output ?? 0, 1) }
|
|
141
|
+
: { input: r.tokens.input, output: r.tokens.output },
|
|
142
|
+
)
|
|
143
|
+
const files = capturedPatch ? [...capturedPatch.matchAll(/^diff --git a\/(\S+)/gm)].map((m) => m[1]) : []
|
|
144
|
+
console.log(
|
|
145
|
+
` [agent] ${scenario.id} prompt=${promptText.length}c tools(l/r/e+/e-/run/run!)=${stats.list}/${stats.read}/${stats.edit_ok}/${stats.edit_fail}/${stats.run}/${stats.run_err} ` +
|
|
146
|
+
`patch=${capturedPatch.length}b files=[${files.join(', ') || 'none'}] tok=in:${r.tokens.input}/out:${r.tokens.output} usd=${r.usd} ${Math.round((Date.now() - t0) / 1000)}s` +
|
|
147
|
+
`${zeroUsage ? (hasPatch ? ' [zero-usage telemetry gap: patch kept, usage floored]' : ' [zero-usage cell: empty completion — scored as no-patch]') : ''}`,
|
|
148
|
+
)
|
|
149
|
+
// A cell with no patch produced NO artifact. Return null (not '') so the
|
|
150
|
+
// backend-integrity guard's own contract (`artifact == null → skip`) applies:
|
|
151
|
+
// a glm-4.6 empty-content turn is scored 0 like the baseline, instead of
|
|
152
|
+
// aborting the whole campaign as a false-positive "stub cell". Any cell that
|
|
153
|
+
// DOES produce a patch still returns it and must report real usage or fire.
|
|
154
|
+
return capturedPatch.trim() ? capturedPatch : null
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// The judge: the OFFICIAL swebench Docker harness. Deterministic FAIL_TO_PASS +
|
|
158
|
+
// PASS_TO_PASS → resolved 0/1. This is the held-out gate's scoring axis.
|
|
159
|
+
const judge: JudgeConfig<string, Scenario> = {
|
|
160
|
+
name: 'swebench-docker',
|
|
161
|
+
dimensions: [{ key: 'resolved', description: 'FAIL_TO_PASS + PASS_TO_PASS resolved by the official swebench Docker harness' }],
|
|
162
|
+
async score({ artifact, scenario }) {
|
|
163
|
+
const patch = String(artifact ?? '')
|
|
164
|
+
if (!patch.trim()) {
|
|
165
|
+
console.log(` [judge] ${scenario.id} resolved=0 (no patch)`)
|
|
166
|
+
return { dimensions: { resolved: 0 }, composite: 0, notes: 'no patch emitted' }
|
|
167
|
+
}
|
|
168
|
+
const bt = byId.get(scenario.id)
|
|
169
|
+
if (!bt) throw new Error(`judge: unknown scenario ${scenario.id}`)
|
|
170
|
+
const s = await adapter.judge(bt, patch)
|
|
171
|
+
console.log(` [judge] ${scenario.id} resolved=${s.resolved ? 1 : 0}`)
|
|
172
|
+
// 1500 chars keeps the whole swebench report JSON (a flat summary object —
|
|
173
|
+
// it has no separate failure section to extract); the old 200 clipped it to
|
|
174
|
+
// an uninformative head, leaving GEPA reflection trace-blind.
|
|
175
|
+
return { dimensions: { resolved: s.resolved ? 1 : 0 }, composite: s.resolved ? 1 : 0, notes: (s.detail ?? '').slice(0, 1500) }
|
|
176
|
+
},
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const profile: AgentProfile = { name: 'swe-agent-glm46', prompt: { systemPrompt: SEED_PROMPT } }
|
|
180
|
+
const proposer = gepaProposer({
|
|
181
|
+
llm: { baseUrl: routerBaseUrl, apiKey: routerKey },
|
|
182
|
+
model: reflectModel,
|
|
183
|
+
target: 'the system prompt of a coding agent that fixes real GitHub bugs via list_files/read_file/edit_file tools',
|
|
184
|
+
maxTokens: reflectMaxTokens,
|
|
185
|
+
temperature: 0.7,
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
const scenarios: Scenario[] = allIds.map((id) => ({ id, kind: 'swe-bench-verified' }))
|
|
189
|
+
const holdoutScenarios: Scenario[] = holdoutIds.map((id) => ({ id, kind: 'swe-bench-verified' }))
|
|
190
|
+
|
|
191
|
+
const out = await improve(profile, [], {
|
|
192
|
+
surface: 'prompt',
|
|
193
|
+
gate: 'holdout',
|
|
194
|
+
generator: proposer,
|
|
195
|
+
scenarios,
|
|
196
|
+
judge,
|
|
197
|
+
agent,
|
|
198
|
+
// glm-5.2 occasionally returns a real patch with an unpopulated usage block
|
|
199
|
+
// (a router telemetry gap on some reasoning-model responses — NOT a stub: the
|
|
200
|
+
// cell made real tool calls and produced a patch). 'assert' would abort the
|
|
201
|
+
// whole campaign on such a cell; 'warn' logs it and continues. The lift metric
|
|
202
|
+
// (resolved) is judge-derived, so a missing token count does not distort it —
|
|
203
|
+
// only the cost accounting undercounts those cells, which is disclosed.
|
|
204
|
+
expectUsage: 'warn',
|
|
205
|
+
budget: { generations, populationSize: population, holdoutScenarios, maxConcurrency, reps: 1 },
|
|
206
|
+
llm: { baseUrl: routerBaseUrl, apiKey: routerKey, model: reflectModel },
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
console.log('\n═══ RESULT ═══')
|
|
210
|
+
console.log(`gateDecision=${out.gateDecision} shipped=${out.shipped} lift=${out.lift}`)
|
|
211
|
+
console.log(`baseline holdout composite = ${out.raw.baseline.compositeMean}`)
|
|
212
|
+
console.log(`winner holdout composite = ${out.raw.winner.compositeMean}`)
|
|
213
|
+
console.log(`baseline per-scenario: ${JSON.stringify(out.raw.baseline.perScenario)}`)
|
|
214
|
+
console.log(`winner per-scenario: ${JSON.stringify(out.raw.winner.perScenario)}`)
|
|
215
|
+
if (out.raw.winner.label) console.log(`winner label : ${out.raw.winner.label}`)
|
|
216
|
+
if (out.raw.winner.rationale) console.log(`winner rationale: ${out.raw.winner.rationale}`)
|
|
217
|
+
|
|
218
|
+
// Per-candidate verdicts on the train set (the "real swebench verdict per candidate").
|
|
219
|
+
for (const gen of out.raw.generations ?? []) {
|
|
220
|
+
console.log(`\n── generation ${gen.record.generationIndex} candidates ──`)
|
|
221
|
+
for (const c of gen.record.candidates) {
|
|
222
|
+
const perScenario = (c as { scenarios?: Array<{ scenarioId: string; composite: number }> }).scenarios ?? []
|
|
223
|
+
const detail = perScenario.map((s) => `${s.scenarioId}=${s.composite}`).join(' ')
|
|
224
|
+
console.log(` candidate ${c.surfaceHash.slice(0, 8)} composite=${c.composite}${c.label ? ` "${c.label}"` : ''} [${detail}]`)
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
main().catch((e) => {
|
|
230
|
+
console.error(e instanceof Error ? (e.stack ?? e.message) : String(e))
|
|
231
|
+
process.exit(1)
|
|
232
|
+
})
|
package/src/swe-jail.ts
CHANGED
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { execFile } from 'node:child_process'
|
|
11
11
|
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'
|
|
12
|
+
import { tmpdir } from 'node:os'
|
|
12
13
|
import { join } from 'node:path'
|
|
13
14
|
import { promisify } from 'node:util'
|
|
14
|
-
import { absoluteSweTempDir } from './swe-temp'
|
|
15
15
|
|
|
16
16
|
const exec = promisify(execFile)
|
|
17
17
|
|
|
@@ -143,7 +143,7 @@ export async function runPyInJail(
|
|
|
143
143
|
applyPatch?: string,
|
|
144
144
|
opts: { timeoutS?: number } = {},
|
|
145
145
|
): Promise<JailRun> {
|
|
146
|
-
const scriptDir = mkdtempSync(join(
|
|
146
|
+
const scriptDir = mkdtempSync(join(tmpdir(), 'swe-repro-'))
|
|
147
147
|
writeFileSync(join(scriptDir, 'repro.py'), pyScript)
|
|
148
148
|
if (applyPatch) writeFileSync(join(scriptDir, 'ride.patch'), applyPatch.endsWith('\n') ? applyPatch : `${applyPatch}\n`)
|
|
149
149
|
const T = opts.timeoutS ?? 120
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SEE-able LOCAL proof of the SWE-bench Verified pipeline — NO tangle sandbox.
|
|
3
|
+
*
|
|
4
|
+
* The whole loop runs on infra we can watch: the repo is cloned into a host tmpdir, the agent is a
|
|
5
|
+
* router-driven tool loop (`runAgentic` + the swe-bench `AgenticSurface`'s list/read/edit tools —
|
|
6
|
+
* jailed to the checkout), the patch is a plain `git diff` of the agent's edits, and the score is
|
|
7
|
+
* the OFFICIAL swebench Docker harness (`adapter.judge`). The only remote call is the model
|
|
8
|
+
* completion via the router. Nothing touches sandbox.tangle.tools.
|
|
9
|
+
*
|
|
10
|
+
* We proxy the surface's `score()` so we can SEE the exact bytes the judge grades: the extracted
|
|
11
|
+
* patch, whether it applies to a clean base checkout (`git apply --check`), and the swebench verdict.
|
|
12
|
+
* With TRACE=1 we also log every tool call so we can tell "agent never edited" from "agent can't edit".
|
|
13
|
+
*
|
|
14
|
+
* TANGLE_API_KEY=… dotenvx run -f …/agent-state.env -- \
|
|
15
|
+
* IDS=django__django-12419 WORKER_MODEL=glm-4.6 \
|
|
16
|
+
* node_modules/.bin/tsx bench/src/swe-local-proof.mts
|
|
17
|
+
*/
|
|
18
|
+
import { execFile } from 'node:child_process'
|
|
19
|
+
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'
|
|
20
|
+
import { tmpdir } from 'node:os'
|
|
21
|
+
import { join } from 'node:path'
|
|
22
|
+
import { promisify } from 'node:util'
|
|
23
|
+
import type { AgenticSurface, ArtifactHandle, SurfaceScore } from '@tangle-network/agent-runtime/loops'
|
|
24
|
+
import { refine, runAgentic } from '@tangle-network/agent-runtime/loops'
|
|
25
|
+
import type { BenchScore } from './benchmarks/types'
|
|
26
|
+
import { createSweBenchEnvironment } from './swe-bench-env'
|
|
27
|
+
|
|
28
|
+
const exec = promisify(execFile)
|
|
29
|
+
|
|
30
|
+
async function main(): Promise<void> {
|
|
31
|
+
const routerKey = process.env.TANGLE_API_KEY
|
|
32
|
+
if (!routerKey) throw new Error('TANGLE_API_KEY required (the worker calls the router)')
|
|
33
|
+
const routerBaseUrl = process.env.ROUTER_BASE ?? 'https://router.tangle.tools/v1'
|
|
34
|
+
const model = process.env.WORKER_MODEL ?? 'glm-4.6'
|
|
35
|
+
const ids = (process.env.IDS ?? 'django__django-12419').split(',').map((s) => s.trim()).filter(Boolean)
|
|
36
|
+
const innerTurns = Number(process.env.INNER_TURNS ?? 40)
|
|
37
|
+
const maxTokens = Number(process.env.MAX_TOKENS ?? 8000)
|
|
38
|
+
const budget = Number(process.env.BUDGET ?? 1)
|
|
39
|
+
// WITH-TOOLS arm: RUN_TOOL=1 exposes the jailed `run` tool + the run-aware prompt. Default OFF ⇒
|
|
40
|
+
// the read/edit-only baseline (glm-5.2 7/12) unchanged.
|
|
41
|
+
const enableRun = ['1', 'true', 'yes'].includes((process.env.RUN_TOOL ?? '').toLowerCase())
|
|
42
|
+
|
|
43
|
+
console.log(`═══ SWE-bench Verified — SEE-able LOCAL proof (no tangle sandbox) ═══`)
|
|
44
|
+
console.log(`model=${model} ids=${ids.join(',')} innerTurns=${innerTurns} maxTokens=${maxTokens} budget=${budget} runTool=${enableRun}`)
|
|
45
|
+
console.log(`router=${routerBaseUrl}`)
|
|
46
|
+
|
|
47
|
+
const { environment, tasks, adapter } = await createSweBenchEnvironment(ids.length, { ids, enableRun })
|
|
48
|
+
const taskList = await tasks(0, ids.length)
|
|
49
|
+
|
|
50
|
+
// One shot per pinned id: proxy score() to capture the exact judged bytes + a NON-DESTRUCTIVE
|
|
51
|
+
// apply-coherence check, then delegate the verdict to the real Docker judge.
|
|
52
|
+
//
|
|
53
|
+
// CRITICAL: score() is called MORE THAN ONCE per task by the driver — the shotExecutor scores
|
|
54
|
+
// the handle to drive its loop, and depthStrategy scores it again at the end. So this proxy MUST
|
|
55
|
+
// be non-destructive (never mutate the working tree the agent edited) and idempotent (never
|
|
56
|
+
// clobber a captured non-empty patch with a later empty read). It also caches the judge by patch
|
|
57
|
+
// so we don't run the Docker harness twice for the identical diff.
|
|
58
|
+
type Rec = { patch: string; applied: boolean; applyErr?: string; applyChecked?: boolean; score?: BenchScore }
|
|
59
|
+
const captured = new Map<string, Rec>()
|
|
60
|
+
const judged = new Map<string, BenchScore>()
|
|
61
|
+
const toolStats = new Map<string, { list: number; read: number; edit_ok: number; edit_fail: number; run: number; run_err: number }>()
|
|
62
|
+
const proxy: AgenticSurface = {
|
|
63
|
+
...environment,
|
|
64
|
+
async call(handle, name, args) {
|
|
65
|
+
const res = await environment.call(handle, name, args)
|
|
66
|
+
// Count tool usage per workspace so we can SEE whether the agent ever edited, and whether
|
|
67
|
+
// edits succeeded or bounced off old_string matching. handle.id keys the workspace.
|
|
68
|
+
const st = toolStats.get(handle.id) ?? { list: 0, read: 0, edit_ok: 0, edit_fail: 0, run: 0, run_err: 0 }
|
|
69
|
+
const r = String(res)
|
|
70
|
+
if (name === 'list_files') st.list += 1
|
|
71
|
+
else if (name === 'read_file') st.read += 1
|
|
72
|
+
else if (name === 'edit_file') {
|
|
73
|
+
if (r.startsWith('edited ')) st.edit_ok += 1
|
|
74
|
+
else st.edit_fail += 1
|
|
75
|
+
} else if (name === 'run') {
|
|
76
|
+
if (r.startsWith('ERROR:')) st.run_err += 1
|
|
77
|
+
else st.run += 1
|
|
78
|
+
}
|
|
79
|
+
toolStats.set(handle.id, st)
|
|
80
|
+
if (process.env.TRACE) {
|
|
81
|
+
const a = JSON.stringify(args).slice(0, 200)
|
|
82
|
+
console.error(`[TOOL] ${name} args=${a} -> ${r.slice(0, 200).replace(/\n/g, '⏎')}`)
|
|
83
|
+
}
|
|
84
|
+
return res
|
|
85
|
+
},
|
|
86
|
+
async score(task, handle: ArtifactHandle): Promise<SurfaceScore> {
|
|
87
|
+
const dir = handle.id
|
|
88
|
+
const diff = await exec('git', ['-C', dir, 'diff'], { maxBuffer: 40_000_000, timeout: 60_000 })
|
|
89
|
+
const patch = diff.stdout
|
|
90
|
+
// Mirror tool stats onto the task id (handle.id == dir).
|
|
91
|
+
const st = toolStats.get(dir)
|
|
92
|
+
if (st) toolStats.set(task.id, st)
|
|
93
|
+
// Idempotent capture: keep the FIRST non-empty patch; never overwrite it with a later empty read.
|
|
94
|
+
const prev = captured.get(task.id)
|
|
95
|
+
let rec: Rec
|
|
96
|
+
if (!prev) {
|
|
97
|
+
rec = { patch, applied: false }
|
|
98
|
+
captured.set(task.id, rec)
|
|
99
|
+
} else {
|
|
100
|
+
rec = prev
|
|
101
|
+
if (!rec.patch.trim() && patch.trim()) rec.patch = patch
|
|
102
|
+
}
|
|
103
|
+
const effPatch = rec.patch
|
|
104
|
+
if (!effPatch.trim()) return { passes: 0, total: 1, errored: 0 }
|
|
105
|
+
// NON-DESTRUCTIVE apply-coherence check (once): the diff was produced from this tree vs HEAD,
|
|
106
|
+
// so `git apply --check -R` proves it is a clean, self-consistent patch without touching the
|
|
107
|
+
// working tree. The REAL forward-apply-to-clean-base proof is the swebench judge below, which
|
|
108
|
+
// applies the patch in a fresh Docker checkout and reports apply failures.
|
|
109
|
+
if (!rec.applyChecked) {
|
|
110
|
+
rec.applyChecked = true
|
|
111
|
+
const pf = join(mkdtempSync(join(tmpdir(), 'swe-apply-')), 'p.diff')
|
|
112
|
+
writeFileSync(pf, effPatch)
|
|
113
|
+
try {
|
|
114
|
+
await exec('git', ['-C', dir, 'apply', '--check', '-R', pf], { timeout: 60_000 })
|
|
115
|
+
rec.applied = true
|
|
116
|
+
} catch (e) {
|
|
117
|
+
rec.applied = false
|
|
118
|
+
rec.applyErr = e instanceof Error ? e.message.slice(0, 200) : String(e)
|
|
119
|
+
} finally {
|
|
120
|
+
rmSync(pf, { force: true })
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// Cached judge: identical patch ⇒ identical verdict; don't pay for a second Docker run.
|
|
124
|
+
let s = judged.get(effPatch)
|
|
125
|
+
if (!s) {
|
|
126
|
+
s = await adapter.judge(task, effPatch)
|
|
127
|
+
judged.set(effPatch, s)
|
|
128
|
+
}
|
|
129
|
+
rec.score = s
|
|
130
|
+
return { passes: s.resolved ? 1 : 0, total: 1, errored: 0 }
|
|
131
|
+
},
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
let anyResolved = 0
|
|
135
|
+
for (const task of taskList) {
|
|
136
|
+
const t0 = Date.now()
|
|
137
|
+
const r = await runAgentic({
|
|
138
|
+
surface: proxy,
|
|
139
|
+
task,
|
|
140
|
+
strategy: refine,
|
|
141
|
+
routerBaseUrl,
|
|
142
|
+
routerKey,
|
|
143
|
+
model,
|
|
144
|
+
maxTokens,
|
|
145
|
+
innerTurns,
|
|
146
|
+
budget,
|
|
147
|
+
})
|
|
148
|
+
const rec = captured.get(task.id)
|
|
149
|
+
const st = toolStats.get(task.id)
|
|
150
|
+
const patchBytes = rec?.patch.length ?? 0
|
|
151
|
+
const patchLines = rec?.patch ? rec.patch.split('\n').length : 0
|
|
152
|
+
const files = rec?.patch ? [...rec.patch.matchAll(/^diff --git a\/(\S+)/gm)].map((m) => m[1]) : []
|
|
153
|
+
const resolved = rec?.score?.resolved ?? false
|
|
154
|
+
if (resolved) anyResolved += 1
|
|
155
|
+
console.log(`\n──── ${task.id} ────`)
|
|
156
|
+
console.log(` agent shots=${r.shots} completions=${r.completions} tokens=in:${r.tokens.input}/out:${r.tokens.output} usd=${r.usd} wall=${Math.round((Date.now() - t0) / 1000)}s`)
|
|
157
|
+
console.log(` tools: list=${st?.list ?? 0} read=${st?.read ?? 0} edit_ok=${st?.edit_ok ?? 0} edit_fail=${st?.edit_fail ?? 0} run=${st?.run ?? 0} run_err=${st?.run_err ?? 0}`)
|
|
158
|
+
console.log(` patch: ${patchBytes} bytes, ${patchLines} lines, files=[${files.join(', ') || '(none)'}]`)
|
|
159
|
+
console.log(` patch APPLIED (git apply --check on clean base): ${rec?.applied ? 'YES' : 'NO'}${rec?.applyErr ? ` (${rec.applyErr})` : ''}`)
|
|
160
|
+
console.log(` swebench judge RESOLVED: ${resolved ? '1' : '0'}`)
|
|
161
|
+
if (rec?.score?.detail) console.log(` judge report: ${rec.score.detail.slice(0, 400)}`)
|
|
162
|
+
}
|
|
163
|
+
console.log(`\n>>> resolved ${anyResolved}/${taskList.length}`)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
main().catch((e) => {
|
|
167
|
+
console.error(e instanceof Error ? (e.stack ?? e.message) : String(e))
|
|
168
|
+
process.exit(1)
|
|
169
|
+
})
|