@tangle-network/agent-bench 0.3.7 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (211) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/HARNESS.md +43 -0
  3. package/README.md +7 -0
  4. package/dist/adapters.js +23 -23
  5. package/dist/benchmarks/_harness.d.ts +1 -1
  6. package/dist/benchmarks/_harness.js +1 -1
  7. package/dist/benchmarks/aec-bench.js +2 -2
  8. package/dist/benchmarks/agentbench.js +2 -2
  9. package/dist/benchmarks/appworld.js +2 -2
  10. package/dist/benchmarks/bfcl.js +2 -2
  11. package/dist/benchmarks/commit0.js +2 -2
  12. package/dist/benchmarks/crag.js +2 -2
  13. package/dist/benchmarks/dabstep.js +2 -2
  14. package/dist/benchmarks/enterpriseops-gym.js +2 -2
  15. package/dist/benchmarks/finresearchbench.js +2 -2
  16. package/dist/benchmarks/nomiracl.js +2 -2
  17. package/dist/benchmarks/open-rag-bench.js +2 -2
  18. package/dist/benchmarks/programbench.js +2 -2
  19. package/dist/benchmarks/ragbench.js +2 -2
  20. package/dist/benchmarks/swe-bench.js +2 -2
  21. package/dist/benchmarks/t2-ragbench.js +2 -2
  22. package/dist/benchmarks/tau-bench-shared.js +2 -2
  23. package/dist/benchmarks/tau2-bench.js +3 -3
  24. package/dist/benchmarks/tau3-banking.js +3 -3
  25. package/dist/benchmarks/terminal-bench.js +2 -2
  26. package/dist/benchmarks/toollm.js +2 -2
  27. package/dist/benchmarks/webarena-verified.js +2 -2
  28. package/dist/{chunk-CKUVRZ2T.js → chunk-3U5TXJZS.js} +2 -2
  29. package/dist/{chunk-YCGY7UIZ.js → chunk-7GRVHU22.js} +2 -2
  30. package/dist/{chunk-Z7ML6L77.js → chunk-HWST3SED.js} +2 -2
  31. package/dist/{chunk-SYDW647C.js → chunk-IA2FBTWC.js} +2 -2
  32. package/dist/{chunk-R67DFVLO.js → chunk-IFVINJ4B.js} +2 -2
  33. package/dist/{chunk-R36V2VP7.js → chunk-IZ5M6OAC.js} +2 -2
  34. package/dist/{chunk-ODT47UAY.js → chunk-K3BQGZCT.js} +2 -2
  35. package/dist/{chunk-IFAV6KEM.js → chunk-KP5KD6EN.js} +2 -2
  36. package/dist/{chunk-ZEWMTR5M.js → chunk-MQMRLGOG.js} +2 -2
  37. package/dist/{chunk-TSWPNOYM.js → chunk-NQG5XDSB.js} +2 -2
  38. package/dist/{chunk-7WSD27QQ.js → chunk-PB64GYIG.js} +2 -2
  39. package/dist/{chunk-HBSWHQNJ.js → chunk-RCYQEFNX.js} +3 -3
  40. package/dist/{chunk-J3KDJNX2.js → chunk-RH5F53JT.js} +2 -2
  41. package/dist/{chunk-UAIOHCUK.js → chunk-SFLA7OH3.js} +3 -3
  42. package/dist/{chunk-Y6O2OCUO.js → chunk-SHM6MRRF.js} +2 -2
  43. package/dist/{chunk-KDIKRJGB.js → chunk-SHYIRB7I.js} +2 -2
  44. package/dist/{chunk-HHXFIHXC.js → chunk-SVR2LKYI.js} +2 -2
  45. package/dist/{chunk-PWQVGAJB.js → chunk-V7AEBY6U.js} +21 -21
  46. package/dist/{chunk-LRRD7NAG.js → chunk-WSKWVEQB.js} +18 -2
  47. package/dist/chunk-WSKWVEQB.js.map +1 -0
  48. package/dist/{chunk-2PVVP7GN.js → chunk-XKEFIFIC.js} +2 -2
  49. package/dist/{chunk-JRWWGMK7.js → chunk-XYA4XSNU.js} +2 -2
  50. package/dist/{chunk-X5YKXC6V.js → chunk-YSMEKBTD.js} +2 -2
  51. package/dist/{chunk-2XU6OGEN.js → chunk-Z4TZ76N7.js} +2 -2
  52. package/dist/index.js +23 -23
  53. package/package.json +5 -5
  54. package/scripts/trata-hedge/README.md +6 -5
  55. package/scripts/verify-packed-consumer.mjs +1 -1
  56. package/src/benchmarks/_harness.ts +20 -2
  57. package/src/benchmarks/humaneval.test.mts +2 -2
  58. package/src/gate.ts +1 -1
  59. package/src/hev-eval.mts +5 -2
  60. package/src/hev-improve.mts +118 -73
  61. package/src/official-optimizer-config.mts +89 -0
  62. package/src/official-optimizer-config.test.mts +88 -0
  63. package/src/profiles.ts +2 -2
  64. package/src/quant-arena/README.md +144 -0
  65. package/src/quant-arena/backtest.test.mts +135 -0
  66. package/src/quant-arena/backtest.ts +218 -0
  67. package/src/quant-arena/data.test.mts +44 -0
  68. package/src/quant-arena/data.ts +141 -0
  69. package/src/quant-arena/driver.test.mts +253 -0
  70. package/src/quant-arena/driver.ts +219 -0
  71. package/src/quant-arena/fixtures/data/PROVENANCE.md +26 -0
  72. package/src/quant-arena/fixtures/data/holdout/IDX.csv +523 -0
  73. package/src/quant-arena/fixtures/data/holdout/S01.csv +523 -0
  74. package/src/quant-arena/fixtures/data/holdout/S02.csv +523 -0
  75. package/src/quant-arena/fixtures/data/holdout/S03.csv +523 -0
  76. package/src/quant-arena/fixtures/data/holdout/S04.csv +523 -0
  77. package/src/quant-arena/fixtures/data/holdout/S05.csv +523 -0
  78. package/src/quant-arena/fixtures/data/holdout/S06.csv +523 -0
  79. package/src/quant-arena/fixtures/data/holdout/S07.csv +523 -0
  80. package/src/quant-arena/fixtures/data/holdout/S08.csv +523 -0
  81. package/src/quant-arena/fixtures/data/holdout/S09.csv +523 -0
  82. package/src/quant-arena/fixtures/data/holdout/S10.csv +523 -0
  83. package/src/quant-arena/fixtures/data/insample/IDX.csv +2087 -0
  84. package/src/quant-arena/fixtures/data/insample/S01.csv +2087 -0
  85. package/src/quant-arena/fixtures/data/insample/S02.csv +2087 -0
  86. package/src/quant-arena/fixtures/data/insample/S03.csv +2087 -0
  87. package/src/quant-arena/fixtures/data/insample/S04.csv +2087 -0
  88. package/src/quant-arena/fixtures/data/insample/S05.csv +2087 -0
  89. package/src/quant-arena/fixtures/data/insample/S06.csv +2087 -0
  90. package/src/quant-arena/fixtures/data/insample/S07.csv +2087 -0
  91. package/src/quant-arena/fixtures/data/insample/S08.csv +2087 -0
  92. package/src/quant-arena/fixtures/data/insample/S09.csv +2087 -0
  93. package/src/quant-arena/fixtures/data/insample/S10.csv +2087 -0
  94. package/src/quant-arena/fixtures/demo-campaign/cost-ledger.jsonl +16 -0
  95. package/src/quant-arena/fixtures/demo-campaign/notebook.jsonl +5 -0
  96. package/src/quant-arena/fixtures/demo-campaign/rollout-manifest.json +171 -0
  97. package/src/quant-arena/fixtures/demo-campaign/strategies/cand-001-default-author/strategy.ts +119 -0
  98. package/src/quant-arena/fixtures/demo-campaign/strategies/cand-002-default-author/strategy.ts +119 -0
  99. package/src/quant-arena/fixtures/demo-campaign/strategies/cand-003-quant-researcher/strategy.ts +105 -0
  100. package/src/quant-arena/fixtures/demo-campaign/strategies/cand-004-quant-researcher/strategy.ts +102 -0
  101. package/src/quant-arena/fixtures/demo-campaign-v2/cost-ledger.jsonl +4 -0
  102. package/src/quant-arena/fixtures/demo-campaign-v2/notebook.jsonl +2 -0
  103. package/src/quant-arena/fixtures/demo-campaign-v2/rollout-manifest.json +84 -0
  104. package/src/quant-arena/fixtures/demo-campaign-v2/strategies/cand-001-quant-researcher/strategy.ts +117 -0
  105. package/src/quant-arena/holdout-certify.mts +206 -0
  106. package/src/quant-arena/holdout-certify.test.mts +82 -0
  107. package/src/quant-arena/leak-audit.test.mts +79 -0
  108. package/src/quant-arena/leak-audit.ts +95 -0
  109. package/src/quant-arena/make-fixtures.mts +161 -0
  110. package/src/quant-arena/multiplicity.test.mts +68 -0
  111. package/src/quant-arena/multiplicity.ts +87 -0
  112. package/src/quant-arena/nautilus-certify.ts +31 -0
  113. package/src/quant-arena/oms.ts +90 -0
  114. package/src/quant-arena/profiles/quant-researcher.profile.json +7 -0
  115. package/src/quant-arena/python/pyproject.toml +8 -0
  116. package/src/quant-arena/python/uv.lock +1297 -0
  117. package/src/quant-arena/python/vbt-worker.py +192 -0
  118. package/src/quant-arena/quant-loop.mts +813 -0
  119. package/src/quant-arena/quant-loop.test.mts +75 -0
  120. package/src/quant-arena/strategies/buy-hold-index/strategy.ts +11 -0
  121. package/src/quant-arena/strategies/equal-weight/strategy.ts +20 -0
  122. package/src/quant-arena/strategies/sma-crossover/strategy.ts +42 -0
  123. package/src/quant-arena/types.ts +133 -0
  124. package/src/quant-arena/vbt-client.ts +321 -0
  125. package/src/quant-arena/vbt-parity.test.mts +183 -0
  126. package/src/quant-arena/windows.test.mts +45 -0
  127. package/src/quant-arena/windows.ts +54 -0
  128. package/src/rollout-ledger/backfill-swe-arena.mts +606 -0
  129. package/src/rollout-ledger/backfill-swe-arena.test.mts +342 -0
  130. package/src/rollout-ledger/settle-capture.mts +442 -0
  131. package/src/rollout-ledger/settle-capture.test.mts +270 -0
  132. package/src/smoke-structural-rollout.mts +15 -9
  133. package/src/swe-arena/activation.mts +225 -0
  134. package/src/swe-arena/activation.test.mts +300 -0
  135. package/src/swe-arena/arms.ts +30 -14
  136. package/src/swe-arena/briefing.mts +217 -0
  137. package/src/swe-arena/briefing.test.mts +178 -0
  138. package/src/swe-arena/calibrate.ts +102 -1
  139. package/src/swe-arena/cell-evidence.mts +41 -9
  140. package/src/swe-arena/factory-command-container.ts +284 -0
  141. package/src/swe-arena/factory-judge-child.mts +228 -0
  142. package/src/swe-arena/factory.test.mts +643 -0
  143. package/src/swe-arena/fixtures/factory/agent-eval-309/calibration.md +51 -0
  144. package/src/swe-arena/fixtures/factory/agent-eval-309/manifest.json +29 -0
  145. package/src/swe-arena/fixtures/factory/agent-eval-309/spec.md +64 -0
  146. package/src/swe-arena/fixtures/factory/agent-runtime-232/calibration.md +48 -0
  147. package/src/swe-arena/fixtures/factory/agent-runtime-232/manifest.json +29 -0
  148. package/src/swe-arena/fixtures/factory/agent-runtime-232/spec.md +48 -0
  149. package/src/swe-arena/fixtures/factory/loops-28/calibration.md +47 -0
  150. package/src/swe-arena/fixtures/factory/loops-28/manifest.json +30 -0
  151. package/src/swe-arena/fixtures/factory/loops-28/spec.md +50 -0
  152. package/src/swe-arena/fixtures/run-report/README.md +43 -0
  153. package/src/swe-arena/fixtures/run-report/factory-agent-eval-309-FSUP0.json +173 -0
  154. package/src/swe-arena/fixtures/run-report/factory-agent-eval-309-FSUP0.md +100 -0
  155. package/src/swe-arena/fixtures/run-report/gen3-rollup.json +551 -0
  156. package/src/swe-arena/fixtures/run-report/gen3-rollup.md +64 -0
  157. package/src/swe-arena/fixtures.ts +135 -2
  158. package/src/swe-arena/gepa-seat.mts +877 -0
  159. package/src/swe-arena/gepa-seat.test.mts +1059 -0
  160. package/src/swe-arena/implementation-ref.test.mts +64 -0
  161. package/src/swe-arena/implementation-ref.ts +62 -0
  162. package/src/swe-arena/ledger-orphans.mts +77 -0
  163. package/src/swe-arena/ledger-orphans.test.mts +147 -0
  164. package/src/swe-arena/outer-loop.mts +710 -33
  165. package/src/swe-arena/premeasured-from-cells.mts +281 -0
  166. package/src/swe-arena/premeasured-from-cells.test.mts +180 -0
  167. package/src/swe-arena/proposer-fanout.mts +327 -31
  168. package/src/swe-arena/proposer-fanout.test.mts +246 -0
  169. package/src/swe-arena/proposer-provenance.mts +172 -0
  170. package/src/swe-arena/proposer-provenance.test.mts +106 -0
  171. package/src/swe-arena/run-experiment.mts +367 -1
  172. package/src/swe-arena/run-report.mts +75 -0
  173. package/src/swe-arena/run-supervisor.test.mts +5 -3
  174. package/src/swe-arena/score-split.mts +140 -0
  175. package/src/swe-arena/score-split.test.mts +123 -0
  176. package/src/swe-arena/scratch-worktree.test.mts +55 -0
  177. package/src/swe-arena/scratch-worktree.ts +34 -0
  178. package/src/swe-arena/types.ts +52 -0
  179. package/src/swe-code-improve.mts +24 -25
  180. package/src/swe-improve.mts +129 -96
  181. package/src/swe-local-proof.mts +6 -1
  182. package/src/swe-stream.mts +4 -2
  183. package/src/swe-structural.mts +245 -837
  184. package/src/tb-container-executor.test.mts +30 -6
  185. package/src/tb-supervisor-sidecar.mts +2 -1
  186. package/src/trata-gepa.mts +182 -245
  187. package/dist/chunk-LRRD7NAG.js.map +0 -1
  188. package/src/live-improve-campaign-mbpp.mts +0 -641
  189. package/src/live-improve-campaign.mts +0 -500
  190. /package/dist/{chunk-CKUVRZ2T.js.map → chunk-3U5TXJZS.js.map} +0 -0
  191. /package/dist/{chunk-YCGY7UIZ.js.map → chunk-7GRVHU22.js.map} +0 -0
  192. /package/dist/{chunk-Z7ML6L77.js.map → chunk-HWST3SED.js.map} +0 -0
  193. /package/dist/{chunk-SYDW647C.js.map → chunk-IA2FBTWC.js.map} +0 -0
  194. /package/dist/{chunk-R67DFVLO.js.map → chunk-IFVINJ4B.js.map} +0 -0
  195. /package/dist/{chunk-R36V2VP7.js.map → chunk-IZ5M6OAC.js.map} +0 -0
  196. /package/dist/{chunk-ODT47UAY.js.map → chunk-K3BQGZCT.js.map} +0 -0
  197. /package/dist/{chunk-IFAV6KEM.js.map → chunk-KP5KD6EN.js.map} +0 -0
  198. /package/dist/{chunk-ZEWMTR5M.js.map → chunk-MQMRLGOG.js.map} +0 -0
  199. /package/dist/{chunk-TSWPNOYM.js.map → chunk-NQG5XDSB.js.map} +0 -0
  200. /package/dist/{chunk-7WSD27QQ.js.map → chunk-PB64GYIG.js.map} +0 -0
  201. /package/dist/{chunk-HBSWHQNJ.js.map → chunk-RCYQEFNX.js.map} +0 -0
  202. /package/dist/{chunk-J3KDJNX2.js.map → chunk-RH5F53JT.js.map} +0 -0
  203. /package/dist/{chunk-UAIOHCUK.js.map → chunk-SFLA7OH3.js.map} +0 -0
  204. /package/dist/{chunk-Y6O2OCUO.js.map → chunk-SHM6MRRF.js.map} +0 -0
  205. /package/dist/{chunk-KDIKRJGB.js.map → chunk-SHYIRB7I.js.map} +0 -0
  206. /package/dist/{chunk-HHXFIHXC.js.map → chunk-SVR2LKYI.js.map} +0 -0
  207. /package/dist/{chunk-PWQVGAJB.js.map → chunk-V7AEBY6U.js.map} +0 -0
  208. /package/dist/{chunk-2PVVP7GN.js.map → chunk-XKEFIFIC.js.map} +0 -0
  209. /package/dist/{chunk-JRWWGMK7.js.map → chunk-XYA4XSNU.js.map} +0 -0
  210. /package/dist/{chunk-X5YKXC6V.js.map → chunk-YSMEKBTD.js.map} +0 -0
  211. /package/dist/{chunk-2XU6OGEN.js.map → chunk-Z4TZ76N7.js.map} +0 -0
@@ -1,10 +1,9 @@
1
1
  /**
2
- * SELF-IMPROVEMENT on the SEE-able LOCAL SWE-bench path — NO tangle sandbox.
2
+ * Official GEPA prompt optimization on the local SWE-bench path.
3
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.
4
+ * Composes three pieces:
5
+ * 1. `improve({ method: officialGepa(...) })` runs GEPA's upstream
6
+ * Optimize Anything engine on explicit train and selection partitions.
8
7
  * 2. Per candidate + scenario, the `agent` fn runs the LOCAL SWE env
9
8
  * (`createSweBenchEnvironment` + `runAgentic`): clone the instance repo to a
10
9
  * host tmpdir, run the jailed list/read/edit tool loop with the CANDIDATE
@@ -16,23 +15,33 @@
16
15
  * IN-LOOP score is a cheap patch-exists proxy (NOT the Docker judge) so the ONLY
17
16
  * Docker run per cell is the improve judge — one deterministic verdict per cell.
18
17
  *
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 \
18
+ * TANGLE_API_KEY=... dotenvx run -f .../agent-state.env -- \
19
+ * TRAIN_IDS=psf__requests-2931 SELECTION_IDS=pallets__flask-5014 \
20
+ * TEST_IDS=psf__requests-1142,psf__requests-1921 \
21
+ * MAX_EVALUATIONS=4 MAX_PROPOSER_COST_USD=2 \
24
22
  * node_modules/.bin/tsx bench/src/swe-improve.mts
25
23
  */
26
24
  import { execFile } from 'node:child_process'
27
25
  import { promisify } from 'node:util'
28
- import { improve } from '@tangle-network/agent-runtime'
29
- import type { AgentProfile } from '@tangle-network/agent-interface'
26
+ import {
27
+ improve,
28
+ officialGepa,
29
+ type ReadonlyAgentProfile,
30
+ } from '@tangle-network/agent-runtime'
31
+ import {
32
+ canonicalCandidateDigest,
33
+ type AgentProfile,
34
+ } from '@tangle-network/agent-interface'
30
35
  import type { AgenticSurface, ArtifactHandle, SurfaceScore } from '@tangle-network/agent-runtime/loops'
31
36
  import { refine, runAgentic } from '@tangle-network/agent-runtime/loops'
32
37
  import type { DispatchContext, JudgeConfig, Scenario } from '@tangle-network/agent-eval/contract'
33
- import { gepaProposer } from '@tangle-network/agent-eval/campaign'
34
38
  import { createSweBenchAdapter } from './benchmarks/swe-bench'
35
39
  import type { BenchTask } from './benchmarks/types'
40
+ import {
41
+ assertCompleteCost,
42
+ officialOptimizerModel,
43
+ requiredTokenPricing,
44
+ } from './official-optimizer-config.mjs'
36
45
  import { createSweBenchEnvironment, SWE_SEED_PROMPT, SWE_SEED_PROMPT_WITH_RUN } from './swe-bench-env'
37
46
 
38
47
  const exec = promisify(execFile)
@@ -42,36 +51,47 @@ async function main(): Promise<void> {
42
51
  if (!routerKey) throw new Error('TANGLE_API_KEY required (the worker + reflection call the router)')
43
52
  const routerBaseUrl = process.env.ROUTER_BASE ?? 'https://router.tangle.tools/v1'
44
53
  const workerModel = process.env.WORKER_MODEL ?? 'glm-4.6'
54
+ const reflectBase = process.env.REFLECT_BASE ?? routerBaseUrl
55
+ const reflectKey = process.env.REFLECT_KEY ?? routerKey
45
56
  const reflectModel = process.env.REFLECT_MODEL ?? 'glm-4.6'
46
57
  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)
58
+ const selectionIds = (process.env.SELECTION_IDS ?? 'pallets__flask-5014').split(',').map((s) => s.trim()).filter(Boolean)
59
+ const testIds = (process.env.TEST_IDS ?? 'psf__requests-1142,psf__requests-1921').split(',').map((s) => s.trim()).filter(Boolean)
60
+ const maxEvaluations = Number(process.env.MAX_EVALUATIONS ?? 4)
61
+ const maxProposerCostUsd = Number(process.env.MAX_PROPOSER_COST_USD ?? 2)
50
62
  const innerTurns = Number(process.env.INNER_TURNS ?? 40)
51
63
  const workerMaxTokens = Number(process.env.MAX_TOKENS ?? 8000)
52
64
  const reflectMaxTokens = Number(process.env.REFLECT_MAX_TOKENS ?? 12000)
53
65
  const maxConcurrency = Number(process.env.MAX_CONCURRENCY ?? 1)
54
66
  const budgetShots = Number(process.env.BUDGET ?? 1)
67
+ const runDir = process.env.RUN_DIR ?? '.runs/swe-official-gepa'
55
68
  // WITH-TOOLS arm: RUN_TOOL=1 exposes the jailed `run` tool AND swaps the seed to the run-aware prompt.
56
69
  // Default OFF ⇒ reproduces the read/edit-only baseline denominator unchanged.
57
70
  const enableRun = ['1', 'true', 'yes'].includes((process.env.RUN_TOOL ?? '').toLowerCase())
58
71
  const SEED_PROMPT = enableRun ? SWE_SEED_PROMPT_WITH_RUN : SWE_SEED_PROMPT
72
+ const allIds = [...new Set([...trainIds, ...selectionIds, ...testIds])]
59
73
 
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`)
74
+ console.log('=== SWE-bench prompt optimization with official GEPA ===')
75
+ console.log(`worker=${workerModel} reflect=${reflectModel} router=${routerBaseUrl}`)
76
+ console.log(`train=[${trainIds.join(', ')}] selection=[${selectionIds.join(', ')}] test=[${testIds.join(', ')}]`)
77
+ console.log(`maxEvaluations=${maxEvaluations} maxProposerCostUsd=${maxProposerCostUsd} innerTurns=${innerTurns} workerMaxTokens=${workerMaxTokens} runTool=${enableRun}`)
78
+ console.log(`runDir=${runDir}\n`)
68
79
 
69
80
  if (process.env.DRYRUN) {
70
81
  // Import + wiring smoke: prove every module resolves and the plan is well-formed
71
82
  // 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})`)
83
+ console.log(`DRYRUN: imports OK (improve=${typeof improve}, officialGepa=${typeof officialGepa}, runAgentic=${typeof runAgentic}, refine=${typeof refine})`)
73
84
  return
74
85
  }
86
+ const workerPricing = requiredTokenPricing(process.env, 'WORKER')
87
+ const optimizer = officialOptimizerModel({
88
+ env: process.env,
89
+ model: reflectModel,
90
+ baseUrl: reflectBase,
91
+ apiKey: reflectKey,
92
+ maxCostUsd: maxProposerCostUsd,
93
+ maxOutputTokensPerRequest: reflectMaxTokens,
94
+ })
75
95
 
76
96
  const { environment, adapter } = await createSweBenchEnvironment(allIds.length, { ids: allIds, enableRun })
77
97
  const pool = await adapter.loadTasks({ ids: allIds, split: 'test' })
@@ -82,8 +102,9 @@ async function main(): Promise<void> {
82
102
  // one instance, return the git-diff patch. A per-call proxy captures the patch in
83
103
  // score() BEFORE runAgentic closes (rm) the workspace; its score is a cheap
84
104
  // 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)
105
+ const agent = async (candidate: ReadonlyAgentProfile, scenario: Scenario, ctx: DispatchContext): Promise<string | null> => {
106
+ const promptText = candidate.prompt?.systemPrompt
107
+ if (promptText === undefined) throw new Error('agent: candidate profile has no system prompt')
87
108
  const bt = byId.get(scenario.id)
88
109
  if (!bt) throw new Error(`agent: unknown scenario ${scenario.id}`)
89
110
  const task = { id: bt.id, systemPrompt: promptText, userPrompt: bt.prompt, meta: { instanceId: bt.id } }
@@ -111,40 +132,44 @@ async function main(): Promise<void> {
111
132
  },
112
133
  }
113
134
  const t0 = Date.now()
114
- const r = await runAgentic({
115
- surface: proxy,
116
- task,
117
- strategy: refine,
118
- routerBaseUrl,
119
- routerKey,
135
+ const paid = await ctx.cost.runPaidCall({
136
+ channel: 'agent',
137
+ actor: 'swe-worker',
120
138
  model: workerModel,
121
- maxTokens: workerMaxTokens,
122
- innerTurns,
123
- budget: budgetShots,
139
+ execute: () =>
140
+ runAgentic({
141
+ surface: proxy,
142
+ task,
143
+ strategy: refine,
144
+ routerBaseUrl,
145
+ routerKey,
146
+ model: workerModel,
147
+ maxTokens: workerMaxTokens,
148
+ innerTurns,
149
+ budget: budgetShots,
150
+ }),
151
+ receipt: (result) => {
152
+ const inputTokens = result.tokens.input ?? 0
153
+ const outputTokens = result.tokens.output ?? 0
154
+ const usageUnknown = inputTokens === 0 && outputTokens === 0
155
+ return {
156
+ model: workerModel,
157
+ inputTokens,
158
+ outputTokens,
159
+ customTokenPricing: workerPricing,
160
+ ...(usageUnknown ? { usageUnknown: true } : {}),
161
+ }
162
+ },
124
163
  })
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.
164
+ if (!paid.succeeded) throw paid.error
165
+ const r = paid.value
135
166
  const zeroUsage = (r.tokens.input ?? 0) === 0 && (r.tokens.output ?? 0) === 0
136
167
  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
168
  const files = capturedPatch ? [...capturedPatch.matchAll(/^diff --git a\/(\S+)/gm)].map((m) => m[1]) : []
144
169
  console.log(
145
170
  ` [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]') : ''}`,
171
+ `patch=${capturedPatch.length}b files=[${files.join(', ') || 'none'}] tok=in:${r.tokens.input}/out:${r.tokens.output} ${Math.round((Date.now() - t0) / 1000)}s` +
172
+ `${zeroUsage ? ' [provider usage unavailable]' : ''}`,
148
173
  )
149
174
  // A cell with no patch produced NO artifact. Return null (not '') so the
150
175
  // backend-integrity guard's own contract (`artifact == null → skip`) applies:
@@ -156,7 +181,7 @@ async function main(): Promise<void> {
156
181
 
157
182
  // The judge: the OFFICIAL swebench Docker harness. Deterministic FAIL_TO_PASS +
158
183
  // PASS_TO_PASS → resolved 0/1. This is the held-out gate's scoring axis.
159
- const judge: JudgeConfig<string, Scenario> = {
184
+ const judge: JudgeConfig<string | null, Scenario> = {
160
185
  name: 'swebench-docker',
161
186
  dimensions: [{ key: 'resolved', description: 'FAIL_TO_PASS + PASS_TO_PASS resolved by the official swebench Docker harness' }],
162
187
  async score({ artifact, scenario }) {
@@ -177,53 +202,61 @@ async function main(): Promise<void> {
177
202
  }
178
203
 
179
204
  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' }))
205
+ const scenario = (id: string): Scenario => ({ id, kind: 'swe-bench-verified' })
190
206
 
191
- const out = await improve(profile, [], {
207
+ const out = await improve(profile, {
192
208
  surface: 'prompt',
193
- gate: 'holdout',
194
- generator: proposer,
195
- scenarios,
196
- judge,
209
+ executionRef: canonicalCandidateDigest({
210
+ callback: 'bench/swe-improve',
211
+ model: workerModel,
212
+ endpoint: new URL(routerBaseUrl).origin,
213
+ innerTurns,
214
+ maxTokens: workerMaxTokens,
215
+ budgetShots,
216
+ enableRun,
217
+ }),
218
+ method: officialGepa<Scenario, string | null>({
219
+ objective:
220
+ 'Improve the system prompt of a coding agent that fixes real GitHub bugs with list_files, read_file, edit_file, and optional run tools.',
221
+ background:
222
+ 'Return the complete system prompt. Preserve tool names and require evidence from repository files and tests.',
223
+ recipe: {
224
+ kind: 'engine',
225
+ run: {
226
+ engine: 'gepa',
227
+ maxEvaluations,
228
+ maxProposerCostUsd,
229
+ },
230
+ },
231
+ optimizer,
232
+ resume: 'if-compatible',
233
+ trustResumeState: true,
234
+ describeScenario: (item) => ({ prompt: byId.get(item.id)?.prompt ?? item.id }),
235
+ }),
236
+ trainScenarios: trainIds.map(scenario),
237
+ selectionScenarios: selectionIds.map(scenario),
238
+ testScenarios: testIds.map(scenario),
239
+ judges: [judge],
197
240
  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
241
  expectUsage: 'warn',
205
- budget: { generations, populationSize: population, holdoutScenarios, maxConcurrency, reps: 1 },
206
- llm: { baseUrl: routerBaseUrl, apiKey: routerKey, model: reflectModel },
242
+ maxConcurrency,
243
+ reps: 1,
244
+ runDir,
245
+ optimizationRunOptions: {
246
+ expectUsage: 'warn',
247
+ maxConcurrency,
248
+ reps: 1,
249
+ },
207
250
  })
208
251
 
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
- }
252
+ assertCompleteCost('SWE-bench official GEPA run', out.cost)
253
+ console.log('\n=== RESULT ===')
254
+ console.log(`decision=${out.decision} lift=${out.lift} interval=[${out.liftInterval.low}, ${out.liftInterval.high}]`)
255
+ console.log(`baseline test composite=${out.raw.best.baselineComposite}`)
256
+ console.log(`winner test composite=${out.raw.best.winnerComposite}`)
257
+ console.log(`test scenarios=${JSON.stringify(out.raw.best.scenarioScores)}`)
258
+ console.log(`cost=${JSON.stringify(out.cost)}`)
259
+ console.log(`candidate prompt:\n${String(out.candidate.value).slice(0, 2000)}`)
227
260
  }
228
261
 
229
262
  main().catch((e) => {
@@ -46,6 +46,9 @@ async function main(): Promise<void> {
46
46
 
47
47
  const { environment, tasks, adapter } = await createSweBenchEnvironment(ids.length, { ids, enableRun })
48
48
  const taskList = await tasks(0, ids.length)
49
+ const benchTaskById = new Map(
50
+ (await adapter.loadTasks({ ids, split: 'test' })).map((task) => [task.id, task]),
51
+ )
49
52
 
50
53
  // One shot per pinned id: proxy score() to capture the exact judged bytes + a NON-DESTRUCTIVE
51
54
  // apply-coherence check, then delegate the verdict to the real Docker judge.
@@ -123,7 +126,9 @@ async function main(): Promise<void> {
123
126
  // Cached judge: identical patch ⇒ identical verdict; don't pay for a second Docker run.
124
127
  let s = judged.get(effPatch)
125
128
  if (!s) {
126
- s = await adapter.judge(task, effPatch)
129
+ const benchTask = benchTaskById.get(task.id)
130
+ if (!benchTask) throw new Error(`swe-local-proof: unknown benchmark task ${task.id}`)
131
+ s = await adapter.judge(benchTask, effPatch)
127
132
  judged.set(effPatch, s)
128
133
  }
129
134
  rec.score = s
@@ -700,6 +700,8 @@ async function acquireRepro(
700
700
  // hands the worker a no-code plan. Primitives replicated from supervisor-arena.mts (that file runs
701
701
  // main() on import, so it cannot be imported) — evidence from execution-verified inputs only. ----------
702
702
 
703
+ type RepairFailureKind = 'wrong-fix' | 'apply-failed' | 'empty-diff'
704
+
703
705
  /** Evidence for the supervisor: the issue, the worker's own candidate diff, and the tail of the
704
706
  * gold-verified reproduction's output on that diff. Execution-verified / model-visible ONLY — never
705
707
  * FAIL_TO_PASS, never the gold patch, never any worker self-report. Bounded to maxChars. */
@@ -709,7 +711,7 @@ function renderRepairEvidence(
709
711
  reproTail: string,
710
712
  reproExit: number | null,
711
713
  maxChars: number,
712
- failureKind: 'wrong-fix' | 'apply-failed' = 'wrong-fix',
714
+ failureKind: RepairFailureKind = 'wrong-fix',
713
715
  ): string {
714
716
  const header =
715
717
  failureKind === 'apply-failed'
@@ -789,7 +791,7 @@ async function superviseRepair(
789
791
  reproExit: number | null,
790
792
  marks: readonly string[],
791
793
  deadlineAt: number,
792
- failureKind: 'wrong-fix' | 'apply-failed' = 'wrong-fix',
794
+ failureKind: RepairFailureKind = 'wrong-fix',
793
795
  ): Promise<SupervisorPlanReceipt> {
794
796
  const md = bt.metadata as Record<string, string>
795
797
  const evidence = renderRepairEvidence(String(md.problem_statement ?? ''), candidateDiff, reproTail, reproExit, 14_000, failureKind)