@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
@@ -0,0 +1,877 @@
1
+ /**
2
+ * GEPA proposer seat using agent-eval's official
3
+ * `gepaOptimizationMethod` as one author in the swe-arena fan-out.
4
+ *
5
+ * Two-tier evaluator, the critical shape:
6
+ *
7
+ * INNER (what GEPA's own loop calls, many times, budget-capped): the
8
+ * candidate is ONE change-space file's content as a string. Each inner call
9
+ * gets a detached worktree at the incumbent commit, writes its own candidate,
10
+ * and runs the existing pre-filter smoke cell on one PUBLIC instance through
11
+ * the injected `SmokeRunner`. Score = smoke resolve (1/0) + verify-pass
12
+ * fraction as a bounded tiebreak. Inner calls are capped by
13
+ * `maxMetricCalls` (default 10; each smoke costs minutes of arm time).
14
+ *
15
+ * OUTER: GEPA's best candidate is written back to the surface file in the
16
+ * scratch worktree and the seat returns `applied: true` — from there the
17
+ * fan-out treats it EXACTLY like any other author's work: change-space
18
+ * check, activation-predicate gate, smoke pre-filter, then the full exam,
19
+ * with staircase label = the seat name (`gepa-author`).
20
+ *
21
+ * DATA BOUNDARIES (both fail-closed):
22
+ * - PUBLIC ONLY crosses the bridge: the only scenario ids serialized to the
23
+ * GEPA process name the public smoke instance; `assertNoPrivateLeak`
24
+ * re-checks every string headed to the bridge against the score split.
25
+ * - Holdout/final cases NEVER cross: the adapter's own API has no test-set
26
+ * field (`GepaBridgeInput` in agent-eval src/campaign/gepa-optimization-
27
+ * method.ts — "The final comparison cases are not accepted by this API and
28
+ * cannot be serialized here"), and its Python side hard-rejects one
29
+ * (`gepa_bridge.py` `_validate_input`: `if "testSet" in value ... raise`).
30
+ * This module never mentions holdout instances to begin with.
31
+ *
32
+ * OPTIONAL RUNTIME (fails at provenance time, before a candidate slot is used):
33
+ * - Python: `agent_eval_rpc.gepa_bridge` + a GEPA build with
34
+ * `optimize_anything`/`OptimizeAnythingConfig` must import —
35
+ * `probeGepaRuntime` throws with the pip install instruction otherwise.
36
+ * The TypeScript adapter is a pinned package dependency and imported directly.
37
+ */
38
+
39
+ import { createHash, randomUUID } from 'node:crypto'
40
+ import { mkdir, readFile, readdir, rename, rm, writeFile } from 'node:fs/promises'
41
+ import { dirname, join } from 'node:path'
42
+ import {
43
+ type DispatchContext,
44
+ createRunCostLedger,
45
+ fsCampaignStorage,
46
+ type GepaOptimizationMethodConfig,
47
+ type GepaOptimizationRecipe,
48
+ gepaOptimizationMethod,
49
+ type JudgeConfig,
50
+ type MutableSurface,
51
+ type OptimizationMethod,
52
+ type OptimizationMethodInput,
53
+ type OptimizationMethodProvenance,
54
+ type Scenario,
55
+ } from '@tangle-network/agent-eval/campaign'
56
+ import { officialOptimizerModel } from '../official-optimizer-config.mts'
57
+ import { ACTIVATION_PREDICATE_RELPATH, type ActivationPredicate } from './activation.mts'
58
+ import { changeSpaceViolations, type OuterLoopConfig } from './outer-loop.mts'
59
+ import type { AuthorFn, ProposerSpec, SmokeRunner, SmokeVerdict } from './proposer-fanout.mts'
60
+ import { runOk } from './proc.ts'
61
+ import {
62
+ createDetachedWorktree,
63
+ pruneDetachedWorktrees,
64
+ removeDetachedWorktree,
65
+ } from './scratch-worktree.ts'
66
+ import type { ScoreSplit } from './score-split.mts'
67
+
68
+ // ---------------------------------------------------------------------------
69
+ // Spec.
70
+ // ---------------------------------------------------------------------------
71
+
72
+ export const GEPA_ENGINES = ['gepa', 'omni'] as const
73
+ export type GepaEngineName = (typeof GEPA_ENGINES)[number]
74
+
75
+ export const DEFAULT_MAX_METRIC_CALLS = 10
76
+ export const DEFAULT_MAX_PROPOSER_COST_USD = 10
77
+ /** Omni = 3 bounded explore runs + 1 continuation (GEPA's published shape). */
78
+ export const OMNI_RUN_COUNT = 4
79
+
80
+ /** A `ProposerSpec` whose `engine` marks it as a GEPA seat. */
81
+ export type GepaSeatSpec = ProposerSpec & { engine: GepaEngineName; surface: string }
82
+
83
+ export function isGepaSeat(spec: ProposerSpec): spec is GepaSeatSpec {
84
+ return spec.engine !== undefined
85
+ }
86
+
87
+ /** Fail-closed spec validation, run at generator construction. A GEPA seat is
88
+ * an ENGINE invocation: harness/profile/model/merge belong to CLI-authored
89
+ * seats and are rejected here rather than silently ignored. */
90
+ export function validateGepaSeat(spec: ProposerSpec): asserts spec is GepaSeatSpec {
91
+ const label = `gepa seat '${spec.name}'`
92
+ if (spec.engine === undefined || !GEPA_ENGINES.includes(spec.engine)) {
93
+ throw new Error(`${label}: engine must be one of ${GEPA_ENGINES.join('|')}, got ${JSON.stringify(spec.engine)}`)
94
+ }
95
+ if (typeof spec.surface !== 'string' || spec.surface.length === 0) {
96
+ throw new Error(`${label}: surface is required — the ONE repo-relative file GEPA optimizes as a string`)
97
+ }
98
+ const violations = changeSpaceViolations([spec.surface])
99
+ if (violations.length > 0) {
100
+ throw new Error(`${label}: surface ${JSON.stringify(spec.surface)} is outside the declared change-space`)
101
+ }
102
+ for (const field of ['harness', 'profile', 'model', 'merge', 'lens', 'diagnosisSlice'] as const) {
103
+ if (spec[field] !== undefined) {
104
+ throw new Error(`${label}: field '${field}' belongs to harness-authored seats and must be unset on an engine seat`)
105
+ }
106
+ }
107
+ const calls = spec.maxMetricCalls ?? DEFAULT_MAX_METRIC_CALLS
108
+ if (!Number.isSafeInteger(calls) || calls <= 0) {
109
+ throw new Error(`${label}: maxMetricCalls must be a positive integer, got ${JSON.stringify(spec.maxMetricCalls)}`)
110
+ }
111
+ if (spec.engine === 'omni' && calls < OMNI_RUN_COUNT) {
112
+ throw new Error(`${label}: engine 'omni' runs ${OMNI_RUN_COUNT} bounded engine runs and needs maxMetricCalls >= ${OMNI_RUN_COUNT}, got ${calls}`)
113
+ }
114
+ const cost = spec.maxProposerCostUsd ?? DEFAULT_MAX_PROPOSER_COST_USD
115
+ if (!Number.isFinite(cost) || cost <= 0) {
116
+ throw new Error(`${label}: maxProposerCostUsd must be a positive finite number, got ${JSON.stringify(spec.maxProposerCostUsd)}`)
117
+ }
118
+ }
119
+
120
+ // ---------------------------------------------------------------------------
121
+ // Recipe.
122
+ // ---------------------------------------------------------------------------
123
+
124
+ export type GepaSeatRecipe = Extract<
125
+ GepaOptimizationRecipe,
126
+ { kind: 'engine' | 'omni' }
127
+ >
128
+
129
+ /** Build the bounded recipe for a seat. The TOTAL inner-evaluation budget is
130
+ * exactly `maxMetricCalls`. The adapter's local callback enforces the sum
131
+ * of per-run limits, and the seat's own dispatch wrapper re-enforces it. */
132
+ export function recipeForSeat(spec: GepaSeatSpec): GepaSeatRecipe {
133
+ const calls = spec.maxMetricCalls ?? DEFAULT_MAX_METRIC_CALLS
134
+ const cost = spec.maxProposerCostUsd ?? DEFAULT_MAX_PROPOSER_COST_USD
135
+ if (spec.engine === 'gepa') {
136
+ return { kind: 'engine', run: { engine: 'gepa', maxEvaluations: calls, maxProposerCostUsd: cost } }
137
+ }
138
+ // Omni: explore {gepa, autoresearch, meta_harness} then continue with gepa,
139
+ // splitting the call budget so the four bounded runs sum to `calls`.
140
+ const perExplore = Math.max(1, Math.floor(calls / OMNI_RUN_COUNT))
141
+ const continueCalls = calls - 3 * perExplore
142
+ const perRunCost = cost / OMNI_RUN_COUNT
143
+ const explore = ['gepa', 'autoresearch', 'meta_harness'].map((engine) => ({
144
+ engine,
145
+ maxEvaluations: perExplore,
146
+ maxProposerCostUsd: perRunCost,
147
+ }))
148
+ return {
149
+ kind: 'omni',
150
+ explore,
151
+ continueWith: { engine: 'gepa', maxEvaluations: continueCalls, maxProposerCostUsd: perRunCost },
152
+ }
153
+ }
154
+
155
+ export function recipeEvaluationBudget(recipe: GepaSeatRecipe): number {
156
+ const runs = recipe.kind === 'engine' ? [recipe.run] : [...recipe.explore, recipe.continueWith]
157
+ return runs.reduce((sum, run) => sum + run.maxEvaluations, 0)
158
+ }
159
+
160
+ // ---------------------------------------------------------------------------
161
+ // Public-only bridge examples.
162
+ // ---------------------------------------------------------------------------
163
+
164
+ export interface GepaSeatScenario extends Scenario {
165
+ /** The PUBLIC smoke instance this scenario dispatches to. */
166
+ smokeIid: string
167
+ }
168
+
169
+ /** Throws when any private instance id appears in text headed to the bridge. */
170
+ export function assertNoPrivateLeak(
171
+ text: string,
172
+ split: Pick<ScoreSplit, 'privateInstances'> | null,
173
+ what: string,
174
+ ): void {
175
+ if (split === null) return
176
+ const leaked = split.privateInstances.filter((iid) => text.includes(iid))
177
+ if (leaked.length > 0) {
178
+ throw new Error(`gepa seat: ${what} would leak private instance id(s) [${leaked.join(', ')}] to the GEPA bridge`)
179
+ }
180
+ }
181
+
182
+ /** The ONLY scenarios the bridge ever sees: the public smoke instance as the
183
+ * train example plus a distinct-id alias as the selection example (the
184
+ * adapter requires disjoint train/selection ids; both dispatch to the same
185
+ * smoke cell). Fails loud when the smoke instance is private. */
186
+ export function gepaBridgeScenarios(
187
+ smokeIid: string,
188
+ split: Pick<ScoreSplit, 'privateInstances'> | null,
189
+ ): { train: GepaSeatScenario[]; selection: GepaSeatScenario[] } {
190
+ if (split !== null && split.privateInstances.includes(smokeIid)) {
191
+ throw new Error(
192
+ `gepa seat: smoke instance ${smokeIid} is PRIVATE under the score split — private ids never cross the bridge`,
193
+ )
194
+ }
195
+ assertNoPrivateLeak(smokeIid, split, `smoke instance id '${smokeIid}'`)
196
+ return {
197
+ train: [{ id: smokeIid, kind: 'swe-smoke', smokeIid }],
198
+ selection: [{ id: `${smokeIid}::selection`, kind: 'swe-smoke', smokeIid }],
199
+ }
200
+ }
201
+
202
+ // ---------------------------------------------------------------------------
203
+ // Inner score.
204
+ // ---------------------------------------------------------------------------
205
+
206
+ /** Resolve dominates; verify-pass is a bounded tiebreak that can never beat a
207
+ * resolve (0.25 < 1). Range {0, 0.25, 1, 1.25}. */
208
+ export function innerSmokeComposite(verdict: Pick<SmokeVerdict, 'resolved' | 'verifyPass'>): number {
209
+ return (verdict.resolved === true ? 1 : 0) + (verdict.verifyPass === true ? 0.25 : 0)
210
+ }
211
+
212
+ export function innerSmokeJudge(): JudgeConfig<SmokeVerdict, GepaSeatScenario> {
213
+ return {
214
+ name: 'gepa-inner-smoke',
215
+ judgeVersion: 'gepa-inner-smoke',
216
+ dimensions: [
217
+ { key: 'resolved', description: 'Official SWE-bench judge verdict for the smoke cell (1 resolved / 0 not).' },
218
+ { key: 'verifyPass', description: 'Committed verify fixture passed for the smoke cell (tiebreak).' },
219
+ ],
220
+ score: ({ artifact }) => ({
221
+ dimensions: {
222
+ resolved: artifact.resolved === true ? 1 : 0,
223
+ verifyPass: artifact.verifyPass === true ? 1 : 0,
224
+ },
225
+ composite: innerSmokeComposite(artifact),
226
+ notes: artifact.reason,
227
+ }),
228
+ }
229
+ }
230
+
231
+ // ---------------------------------------------------------------------------
232
+ // Optional Python runtime.
233
+ // ---------------------------------------------------------------------------
234
+
235
+ export const GEPA_PYTHON_INSTALL_HINT =
236
+ 'install `agent-eval-rpc==0.126.6`, then install ' +
237
+ '`gepa[full] @ git+https://github.com/gepa-ai/gepa.git@f919db0a622e2e9f9204779b81fe00cc1b2d808f`'
238
+
239
+ export type GepaMethodFactory = (
240
+ config: GepaOptimizationMethodConfig<GepaSeatScenario, SmokeVerdict>,
241
+ ) => OptimizationMethod<GepaSeatScenario, SmokeVerdict>
242
+
243
+ export type ProbeExec = (
244
+ command: string,
245
+ args: string[],
246
+ ) => Promise<{ code: number | null; stdout: string; stderr: string }>
247
+
248
+ export interface GepaRuntimeProbe {
249
+ pythonVersion: string
250
+ gepaVersion: string
251
+ }
252
+
253
+ export const DEFAULT_GEPA_PYTHON = 'python3'
254
+
255
+ /** Prove the Python side of the bridge can run, or throw install
256
+ * instructions. Mirrors the codex seat's login-status gate: run at t=0 so a
257
+ * dead seat fails the launch, never a mid-run candidate slot. */
258
+ export async function probeGepaRuntime(python: string, exec: ProbeExec, seatName: string): Promise<GepaRuntimeProbe> {
259
+ const version = await exec(python, ['--version'])
260
+ if (version.code !== 0) {
261
+ throw new Error(
262
+ `gepa seat '${seatName}': '${python} --version' failed (rc=${version.code}) — ${GEPA_PYTHON_INSTALL_HINT}`,
263
+ )
264
+ }
265
+ const bridge = await exec(python, ['-c', 'import agent_eval_rpc.gepa_bridge'])
266
+ if (bridge.code !== 0) {
267
+ throw new Error(
268
+ `gepa seat '${seatName}': GEPA Python runtime is not installed ` +
269
+ `(python=${python}; 'import agent_eval_rpc.gepa_bridge' failed: ${bridge.stderr.trim().slice(0, 300)}). ` +
270
+ GEPA_PYTHON_INSTALL_HINT,
271
+ )
272
+ }
273
+ const gepa = await exec(python, [
274
+ '-c',
275
+ "from gepa.optimize_anything import optimize_anything, OptimizeAnythingConfig; " +
276
+ "import gepa; print(getattr(gepa, '__version__', 'source'))",
277
+ ])
278
+ if (gepa.code !== 0) {
279
+ throw new Error(
280
+ `gepa seat '${seatName}': installed gepa lacks the multi-engine optimize_anything API ` +
281
+ `(${gepa.stderr.trim().slice(0, 300)}). ` +
282
+ GEPA_PYTHON_INSTALL_HINT,
283
+ )
284
+ }
285
+ return { pythonVersion: (version.stdout + version.stderr).trim(), gepaVersion: gepa.stdout.trim() }
286
+ }
287
+
288
+ // ---------------------------------------------------------------------------
289
+ // Inner-run provenance.
290
+ // ---------------------------------------------------------------------------
291
+
292
+ export interface GepaInnerCall {
293
+ call: number
294
+ scenarioId: string
295
+ smokeIid: string
296
+ candidateSha256: string
297
+ composite: number
298
+ resolved: boolean | null
299
+ verifyPass: boolean | null
300
+ pass: boolean
301
+ wallS: number
302
+ }
303
+
304
+ type OptimizationPackageSource = OptimizationMethodProvenance['source']
305
+ type OptimizationModuleSource = NonNullable<OptimizationMethodProvenance['modules']>[number]
306
+ type OptimizationPythonRuntime = NonNullable<OptimizationMethodProvenance['python']>
307
+ type OptimizationTokenUsage = NonNullable<OptimizationMethodProvenance['tokenUsage']>
308
+
309
+ export interface GepaSeatInnerRun {
310
+ seat: string
311
+ engine: GepaEngineName
312
+ surface: string
313
+ generation: number
314
+ budget: number
315
+ innerCallCount: number
316
+ innerScores: GepaInnerCall[]
317
+ bestComposite: number | null
318
+ source: OptimizationPackageSource & { revision: string; sourceSha256: string }
319
+ bridge: OptimizationPackageSource & { sourceSha256: string }
320
+ modules: OptimizationModuleSource[]
321
+ python: OptimizationPythonRuntime
322
+ runId: string
323
+ compatibleRunId: string
324
+ resumed: boolean
325
+ evaluationCount: number
326
+ tokenUsage: OptimizationTokenUsage
327
+ artifactDir: string
328
+ totalCostUsd: number
329
+ accountingComplete: boolean
330
+ incompleteReasons: string[]
331
+ durationMs: number
332
+ }
333
+
334
+ export const PROPOSER_PROVENANCE_FILENAME = 'proposer-provenance.json'
335
+ export const GEPA_INNER_RUNS_DIRNAME = 'gepa-inner-runs'
336
+
337
+ function isRecord(value: unknown): value is Record<string, unknown> {
338
+ return typeof value === 'object' && value !== null && !Array.isArray(value)
339
+ }
340
+
341
+ function errorCode(error: unknown): string | undefined {
342
+ return isRecord(error) && typeof error.code === 'string' ? error.code : undefined
343
+ }
344
+
345
+ async function readJsonObject(path: string): Promise<Record<string, unknown>> {
346
+ const raw = await readFile(path, 'utf8')
347
+ let value: unknown
348
+ try {
349
+ value = JSON.parse(raw)
350
+ } catch (error) {
351
+ throw new Error(`gepa seat: malformed JSON in existing provenance file ${path}`, { cause: error })
352
+ }
353
+ if (!isRecord(value)) {
354
+ throw new Error(`gepa seat: existing provenance file ${path} must contain a JSON object`)
355
+ }
356
+ return value
357
+ }
358
+
359
+ async function validateExistingRunRecords(outDir: string): Promise<void> {
360
+ const launchRecordPath = join(outDir, PROPOSER_PROVENANCE_FILENAME)
361
+ try {
362
+ await readJsonObject(launchRecordPath)
363
+ } catch (error) {
364
+ if (errorCode(error) !== 'ENOENT') throw error
365
+ }
366
+
367
+ const recordsDir = join(outDir, GEPA_INNER_RUNS_DIRNAME)
368
+ let entries
369
+ try {
370
+ entries = await readdir(recordsDir, { withFileTypes: true })
371
+ } catch (error) {
372
+ if (errorCode(error) === 'ENOENT') return
373
+ throw error
374
+ }
375
+ for (const entry of entries) {
376
+ if (entry.isFile() && entry.name.endsWith('.json')) {
377
+ await readJsonObject(join(recordsDir, entry.name))
378
+ }
379
+ }
380
+ }
381
+
382
+ /** Persist one immutable record without mutating the shared launch record. */
383
+ export async function recordGepaSeatInnerRun(outDir: string, run: GepaSeatInnerRun): Promise<string> {
384
+ await validateExistingRunRecords(outDir)
385
+ const recordsDir = join(outDir, GEPA_INNER_RUNS_DIRNAME)
386
+ await mkdir(recordsDir, { recursive: true })
387
+ const identity = createHash('sha256')
388
+ .update(JSON.stringify({ seat: run.seat, generation: run.generation, runId: run.runId }))
389
+ .digest('hex')
390
+ .slice(0, 16)
391
+ const nonce = randomUUID()
392
+ const filename = `${identity}-${nonce}.json`
393
+ const finalPath = join(recordsDir, filename)
394
+ const temporaryPath = join(recordsDir, `.${filename}.tmp`)
395
+ try {
396
+ await writeFile(temporaryPath, JSON.stringify(run, null, 2), { flag: 'wx' })
397
+ await rename(temporaryPath, finalPath)
398
+ } finally {
399
+ await rm(temporaryPath, { force: true })
400
+ }
401
+ return finalPath
402
+ }
403
+
404
+ function requiredText(value: unknown, label: string): string {
405
+ if (typeof value !== 'string' || value.length === 0 || value !== value.trim()) {
406
+ throw new Error(`gepa seat: optimizer result omitted ${label}`)
407
+ }
408
+ return value
409
+ }
410
+
411
+ function requiredSha256(value: unknown, label: string): string {
412
+ const hash = requiredText(value, label)
413
+ if (!/^[0-9a-f]{64}$/.test(hash)) {
414
+ throw new Error(`gepa seat: optimizer result returned invalid ${label}`)
415
+ }
416
+ return hash
417
+ }
418
+
419
+ function requiredCount(value: unknown, label: string): number {
420
+ if (!Number.isSafeInteger(value) || (value as number) < 0) {
421
+ throw new Error(`gepa seat: optimizer result returned invalid ${label}`)
422
+ }
423
+ return value as number
424
+ }
425
+
426
+ function completeProvenance(
427
+ provenance: OptimizationMethodProvenance | undefined,
428
+ seatName: string,
429
+ ): Pick<
430
+ GepaSeatInnerRun,
431
+ | 'source'
432
+ | 'bridge'
433
+ | 'modules'
434
+ | 'python'
435
+ | 'runId'
436
+ | 'compatibleRunId'
437
+ | 'resumed'
438
+ | 'evaluationCount'
439
+ | 'tokenUsage'
440
+ | 'artifactDir'
441
+ > {
442
+ const label = `gepa seat '${seatName}'`
443
+ if (provenance === undefined) {
444
+ throw new Error(`${label}: optimizer result omitted provenance`)
445
+ }
446
+ if (
447
+ provenance.source.kind !== 'package' ||
448
+ provenance.source.evidence !== 'observed' ||
449
+ requiredText(provenance.source.package, 'source.package') !== 'gepa'
450
+ ) {
451
+ throw new Error(`${label}: optimizer result returned invalid source package`)
452
+ }
453
+ requiredText(provenance.source.version, 'source.version')
454
+ const sourceRevision = requiredText(provenance.source.revision, 'source.revision')
455
+ const sourceSha256 = requiredSha256(provenance.source.sourceSha256, 'source.sourceSha256')
456
+ if (provenance.bridge === undefined) {
457
+ throw new Error(`${label}: optimizer result omitted bridge provenance`)
458
+ }
459
+ if (
460
+ provenance.bridge.kind !== 'package' ||
461
+ provenance.bridge.evidence !== 'observed' ||
462
+ requiredText(provenance.bridge.package, 'bridge.package') !== 'agent-eval-rpc'
463
+ ) {
464
+ throw new Error(`${label}: optimizer result returned invalid bridge package`)
465
+ }
466
+ requiredText(provenance.bridge.version, 'bridge.version')
467
+ const bridgeSha256 = requiredSha256(provenance.bridge.sourceSha256, 'bridge.sourceSha256')
468
+ if (provenance.modules === undefined) {
469
+ throw new Error(`${label}: optimizer result omitted module provenance`)
470
+ }
471
+ const modules = provenance.modules.map((module, index) => ({
472
+ module: requiredText(module.module, `modules[${index}].module`),
473
+ sourceSha256: requiredSha256(module.sourceSha256, `modules[${index}].sourceSha256`),
474
+ }))
475
+ if (provenance.python === undefined) {
476
+ throw new Error(`${label}: optimizer result omitted Python provenance`)
477
+ }
478
+ const python = {
479
+ implementation: requiredText(provenance.python.implementation, 'python.implementation'),
480
+ version: requiredText(provenance.python.version, 'python.version'),
481
+ }
482
+ if (provenance.compatibleRunId === undefined) {
483
+ throw new Error(`${label}: optimizer result omitted compatibleRunId`)
484
+ }
485
+ if (provenance.tokenUsage === undefined) {
486
+ throw new Error(`${label}: optimizer result omitted token usage`)
487
+ }
488
+ const tokenUsage = {
489
+ inputTokens: requiredCount(provenance.tokenUsage.inputTokens, 'tokenUsage.inputTokens'),
490
+ ...(provenance.tokenUsage.cachedInputTokens === undefined
491
+ ? {}
492
+ : {
493
+ cachedInputTokens: requiredCount(
494
+ provenance.tokenUsage.cachedInputTokens,
495
+ 'tokenUsage.cachedInputTokens',
496
+ ),
497
+ }),
498
+ ...(provenance.tokenUsage.cacheWriteInputTokens === undefined
499
+ ? {}
500
+ : {
501
+ cacheWriteInputTokens: requiredCount(
502
+ provenance.tokenUsage.cacheWriteInputTokens,
503
+ 'tokenUsage.cacheWriteInputTokens',
504
+ ),
505
+ }),
506
+ outputTokens: requiredCount(provenance.tokenUsage.outputTokens, 'tokenUsage.outputTokens'),
507
+ ...(provenance.tokenUsage.reasoningTokens === undefined
508
+ ? {}
509
+ : {
510
+ reasoningTokens: requiredCount(
511
+ provenance.tokenUsage.reasoningTokens,
512
+ 'tokenUsage.reasoningTokens',
513
+ ),
514
+ }),
515
+ totalTokens: requiredCount(provenance.tokenUsage.totalTokens, 'tokenUsage.totalTokens'),
516
+ calls: requiredCount(provenance.tokenUsage.calls, 'tokenUsage.calls'),
517
+ }
518
+ if (tokenUsage.totalTokens !== tokenUsage.inputTokens + tokenUsage.outputTokens) {
519
+ throw new Error(`${label}: optimizer result returned inconsistent token usage`)
520
+ }
521
+ return {
522
+ source: { ...provenance.source, revision: sourceRevision, sourceSha256 },
523
+ bridge: { ...provenance.bridge, sourceSha256: bridgeSha256 },
524
+ modules,
525
+ python,
526
+ runId: requiredText(provenance.runId, 'runId'),
527
+ compatibleRunId: requiredText(provenance.compatibleRunId, 'compatibleRunId'),
528
+ resumed: provenance.resumed,
529
+ evaluationCount: requiredCount(provenance.evaluationCount, 'evaluationCount'),
530
+ tokenUsage,
531
+ artifactDir: requiredText(provenance.artifactDir, 'artifactDir'),
532
+ }
533
+ }
534
+
535
+ function assertCompleteCost(
536
+ cost: { totalCostUsd: number; accountingComplete: boolean; incompleteReasons: string[] },
537
+ seatName: string,
538
+ ): void {
539
+ if (!Number.isFinite(cost.totalCostUsd) || cost.totalCostUsd < 0) {
540
+ throw new Error(`gepa seat '${seatName}': optimizer returned invalid total cost`)
541
+ }
542
+ if (
543
+ !Array.isArray(cost.incompleteReasons) ||
544
+ cost.incompleteReasons.some(
545
+ (reason) => typeof reason !== 'string' || reason.length === 0 || reason !== reason.trim(),
546
+ )
547
+ ) {
548
+ throw new Error(`gepa seat '${seatName}': optimizer returned invalid incomplete reasons`)
549
+ }
550
+ if (cost.accountingComplete !== (cost.incompleteReasons.length === 0)) {
551
+ throw new Error(`gepa seat '${seatName}': optimizer returned inconsistent cost accounting`)
552
+ }
553
+ if (!cost.accountingComplete) {
554
+ throw new Error(
555
+ `gepa seat '${seatName}': cost accounting is incomplete: ${cost.incompleteReasons.join('; ') || 'no reason provided'}`,
556
+ )
557
+ }
558
+ }
559
+
560
+ // ---------------------------------------------------------------------------
561
+ // Mechanical activation predicate.
562
+ // ---------------------------------------------------------------------------
563
+
564
+ const escapeRegExp = (s: string): string => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
565
+
566
+ export const MIN_PREDICATE_LINE_CHARS = 12
567
+
568
+ /** Derive a machine-checkable predicate from the surface change: the longest
569
+ * ADDED line must render in the candidate's own run artifacts (for a prompt
570
+ * surface, the changed text appearing in composed prompts IS the mechanism
571
+ * firing). Returns null when no added line is distinctive enough — the
572
+ * caller fails the candidate loud instead of shipping an unverifiable one. */
573
+ export function mechanicalActivationPredicate(
574
+ seed: string,
575
+ winner: string,
576
+ surface: string,
577
+ ): ActivationPredicate | null {
578
+ const seedLines = new Set(seed.split('\n').map((l) => l.trim()))
579
+ const added = winner
580
+ .split('\n')
581
+ .map((l) => l.trim())
582
+ .filter((l) => l.length >= MIN_PREDICATE_LINE_CHARS && !seedLines.has(l))
583
+ if (added.length === 0) return null
584
+ const line = added.reduce((a, b) => (b.length > a.length ? b : a))
585
+ return {
586
+ description: `gepa-author surface change fired: candidate text from ${surface} appears in run artifacts`,
587
+ kind: 'grep',
588
+ pattern: escapeRegExp(line),
589
+ }
590
+ }
591
+
592
+ // ---------------------------------------------------------------------------
593
+ // The seat author.
594
+ // ---------------------------------------------------------------------------
595
+
596
+ export interface GepaSeatDeps {
597
+ smokeRunner: SmokeRunner
598
+ runnerImplementationRef: string
599
+ judgeImplementationRef: string
600
+ /** Resolved PUBLIC smoke instance (outer-loop restricts the choice to the
601
+ * split's public set; re-asserted here fail-closed). */
602
+ smokeInstanceId: string
603
+ scoreSplit: Pick<ScoreSplit, 'privateInstances'> | null
604
+ /** Test override. Default: agent-eval's official GEPA method. */
605
+ methodFactory?: GepaMethodFactory
606
+ /** Explicit model override. Production otherwise resolves the metered model from env. */
607
+ optimizer?: NonNullable<
608
+ GepaOptimizationMethodConfig<GepaSeatScenario, SmokeVerdict>['optimizer']
609
+ >
610
+ log?: (msg: string) => void
611
+ }
612
+
613
+ const sha256 = (s: string): string => `sha256:${createHash('sha256').update(s).digest('hex')}`
614
+
615
+ export function gepaSeatEvaluationId(input: {
616
+ smokeInstanceId: string
617
+ dispatchTimeoutMs: number
618
+ incumbentCommit: string
619
+ runnerImplementationRef: string
620
+ judgeImplementationRef: string
621
+ }): string {
622
+ if (!/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(input.incumbentCommit)) {
623
+ throw new Error('gepa seat: incumbentCommit must be an immutable git object id')
624
+ }
625
+ const requireImplementationRef = (value: string, label: string): string => {
626
+ if (!/^sha256:[a-f0-9]{64}$/.test(value)) {
627
+ throw new Error(`gepa seat: ${label} must be an immutable sha256 reference`)
628
+ }
629
+ return value
630
+ }
631
+ const runnerRef = requireImplementationRef(
632
+ input.runnerImplementationRef,
633
+ 'runnerImplementationRef',
634
+ )
635
+ const judgeRef = requireImplementationRef(
636
+ input.judgeImplementationRef,
637
+ 'judgeImplementationRef',
638
+ )
639
+ return [
640
+ 'swe-arena-gepa-seat',
641
+ `smoke=${input.smokeInstanceId}`,
642
+ `incumbent=${input.incumbentCommit}`,
643
+ `runner=${runnerRef}`,
644
+ `judge=${judgeRef}`,
645
+ `dispatchTimeoutMs=${input.dispatchTimeoutMs}`,
646
+ ].join('|')
647
+ }
648
+
649
+ /** Build the seat's `AuthorFn`. The fan-out calls it with the seat's scratch
650
+ * worktree (checked out at the incumbent commit); everything this function
651
+ * leaves in that worktree becomes the candidate diff. */
652
+ export function gepaSeatAuthor(config: OuterLoopConfig, deps: GepaSeatDeps): AuthorFn {
653
+ const log = deps.log ?? (() => {})
654
+ return async (proposer, args) => {
655
+ validateGepaSeat(proposer)
656
+ const spec: GepaSeatSpec = proposer
657
+ const generation = args.generation ?? 0
658
+ const budget = spec.maxMetricCalls ?? DEFAULT_MAX_METRIC_CALLS
659
+ const recipe = recipeForSeat(spec)
660
+ const scenarios = gepaBridgeScenarios(deps.smokeInstanceId, deps.scoreSplit)
661
+ const surfacePath = join(args.worktreePath, spec.surface)
662
+ const seed = await readFile(surfacePath, 'utf8').catch(() => {
663
+ throw new Error(`gepa seat '${spec.name}': surface ${spec.surface} does not exist at the incumbent commit`)
664
+ })
665
+ const incumbentCommit = (
666
+ await runOk('git', ['-C', args.worktreePath, 'rev-parse', 'HEAD'])
667
+ ).stdout.trim()
668
+ const runDir = join(config.outDir, 'gepa-seat', `gen${generation}-${spec.name.replace(/[^a-zA-Z0-9_-]/g, '_')}`)
669
+ await mkdir(runDir, { recursive: true })
670
+ await pruneDetachedWorktrees(args.worktreePath)
671
+
672
+ const objective =
673
+ `Improve the supervisor-loop file '${spec.surface}' (returned as the COMPLETE new file content) so the ` +
674
+ 'SWE-bench smoke evaluation scores higher. Score = 1 for an officially resolved instance plus 0.25 when ' +
675
+ 'the verify fixture passes. Keep the file coherent and self-contained; only its content is applied.'
676
+ const background =
677
+ `The candidate string replaces ${spec.surface} in a checkout of the loops supervisor repo; every other ` +
678
+ 'file stays at the incumbent commit. Each evaluation runs one real SWE-bench instance end-to-end and ' +
679
+ 'takes minutes — spend evaluations deliberately.'
680
+ assertNoPrivateLeak(objective + background + JSON.stringify([...scenarios.train, ...scenarios.selection]),
681
+ deps.scoreSplit, 'bridge payload')
682
+
683
+ const storage = fsCampaignStorage()
684
+ const costLedger =
685
+ args.costLedger ??
686
+ createRunCostLedger({
687
+ storage,
688
+ runDir: `${runDir}/cost`,
689
+ })
690
+ const innerScores: GepaInnerCall[] = []
691
+ let dispatchedCalls = 0
692
+ const dispatchWithSurface = async (
693
+ surface: MutableSurface,
694
+ scenario: GepaSeatScenario,
695
+ _ctx: DispatchContext,
696
+ ): Promise<SmokeVerdict> => {
697
+ if (typeof surface !== 'string') {
698
+ throw new Error(`gepa seat '${spec.name}': candidate surface must be a string`)
699
+ }
700
+ const call = ++dispatchedCalls
701
+ if (call > budget) {
702
+ // Defense-in-depth: the adapter's callback enforces the same cap.
703
+ throw new Error(`gepa seat '${spec.name}': inner-call budget ${budget} exhausted`)
704
+ }
705
+ const candidateSha256 = sha256(surface)
706
+ const evaluationKey = `inner-${call}-${candidateSha256.slice(7, 19)}`
707
+ const candidateWorktree = join(runDir, 'candidate-worktrees', evaluationKey)
708
+ await createDetachedWorktree(args.worktreePath, incumbentCommit, candidateWorktree)
709
+ let verdict: SmokeVerdict
710
+ try {
711
+ await writeFile(join(candidateWorktree, spec.surface), surface)
712
+ verdict = await deps.smokeRunner({
713
+ scratchPath: candidateWorktree,
714
+ generation,
715
+ proposer: spec,
716
+ evaluationKey,
717
+ costLedger,
718
+ })
719
+ } finally {
720
+ await removeDetachedWorktree(args.worktreePath, candidateWorktree)
721
+ }
722
+ if (deps.scoreSplit !== null && deps.scoreSplit.privateInstances.includes(verdict.iid)) {
723
+ throw new Error(
724
+ `gepa seat '${spec.name}': smoke ran PRIVATE instance ${verdict.iid} — refusing to feed its score to the bridge`,
725
+ )
726
+ }
727
+ innerScores.push({
728
+ call,
729
+ scenarioId: scenario.id,
730
+ smokeIid: verdict.iid,
731
+ candidateSha256,
732
+ composite: innerSmokeComposite(verdict),
733
+ resolved: verdict.resolved,
734
+ verifyPass: verdict.verifyPass ?? null,
735
+ pass: verdict.pass,
736
+ wallS: verdict.wallS,
737
+ })
738
+ log(
739
+ `gepa seat ${spec.name} inner call ${call}/${budget}: ` +
740
+ `composite=${innerSmokeComposite(verdict)} (${verdict.reason})`,
741
+ )
742
+ return verdict
743
+ }
744
+
745
+ const factory: GepaMethodFactory =
746
+ deps.methodFactory ?? gepaOptimizationMethod<GepaSeatScenario, SmokeVerdict>
747
+ const innerJudge = innerSmokeJudge()
748
+ const optimizer =
749
+ deps.optimizer ??
750
+ (deps.methodFactory
751
+ ? undefined
752
+ : officialOptimizerModel({
753
+ env: process.env,
754
+ envPrefix: 'GEPA_OPTIMIZER',
755
+ model: process.env.GEPA_OPTIMIZER_MODEL ?? config.arm.driverModel,
756
+ baseUrl:
757
+ process.env.GEPA_OPTIMIZER_BASE_URL ??
758
+ process.env.ROUTER_BASE ??
759
+ 'https://router.tangle.tools/v1',
760
+ apiKey: process.env.GEPA_OPTIMIZER_API_KEY ?? process.env.TANGLE_API_KEY ?? '',
761
+ maxCostUsd: spec.maxProposerCostUsd ?? DEFAULT_MAX_PROPOSER_COST_USD,
762
+ maxOutputTokensPerRequest: Number(
763
+ process.env.GEPA_OPTIMIZER_MAX_OUTPUT_TOKENS ?? 16_384,
764
+ ),
765
+ }))
766
+ const method = factory({
767
+ name: `gepa-seat:${spec.name}`,
768
+ recipe,
769
+ objective,
770
+ evaluationId: gepaSeatEvaluationId({
771
+ smokeInstanceId: deps.smokeInstanceId,
772
+ dispatchTimeoutMs: config.dispatchTimeoutMs,
773
+ incumbentCommit,
774
+ runnerImplementationRef: deps.runnerImplementationRef,
775
+ judgeImplementationRef: deps.judgeImplementationRef,
776
+ }),
777
+ background,
778
+ describeScenario: (scenario) => ({ id: scenario.id }),
779
+ ...(optimizer ? { optimizer } : {}),
780
+ // Upper bound, not expectation: every inner call is a real arm cell.
781
+ timeoutMs: budget * config.dispatchTimeoutMs,
782
+ resume: 'if-compatible',
783
+ trustResumeState: true,
784
+ runner: { command: spec.python ?? DEFAULT_GEPA_PYTHON },
785
+ })
786
+
787
+ const input: OptimizationMethodInput<GepaSeatScenario, SmokeVerdict> = {
788
+ baselineSurface: seed,
789
+ trainScenarios: scenarios.train,
790
+ selectionScenarios: scenarios.selection,
791
+ dispatchWithSurface,
792
+ judges: [innerJudge],
793
+ runDir,
794
+ seed: config.round * 1000 + generation,
795
+ runOptions: {
796
+ storage,
797
+ maxConcurrency: 1,
798
+ dispatchTimeoutMs: config.dispatchTimeoutMs,
799
+ labeledStore: 'off',
800
+ tracing: 'off',
801
+ expectUsage: 'off',
802
+ resumable: false,
803
+ },
804
+ costLedger,
805
+ }
806
+
807
+ const started = Date.now()
808
+ const result = await method.optimize(input)
809
+ let innerRun: GepaSeatInnerRun
810
+ try {
811
+ const orderedInnerScores = [...innerScores].sort((a, b) => a.call - b.call)
812
+ innerRun = {
813
+ seat: spec.name,
814
+ engine: spec.engine,
815
+ surface: spec.surface,
816
+ generation,
817
+ budget,
818
+ innerCallCount: orderedInnerScores.length,
819
+ innerScores: orderedInnerScores,
820
+ bestComposite:
821
+ orderedInnerScores.length > 0
822
+ ? Math.max(...orderedInnerScores.map((score) => score.composite))
823
+ : null,
824
+ ...completeProvenance(result.provenance, spec.name),
825
+ totalCostUsd: result.cost.totalCostUsd,
826
+ accountingComplete: result.cost.accountingComplete,
827
+ incompleteReasons: [...result.cost.incompleteReasons],
828
+ durationMs: Date.now() - started,
829
+ }
830
+ await writeFile(join(runDir, 'inner-provenance.json'), JSON.stringify(innerRun, null, 2))
831
+ await recordGepaSeatInnerRun(config.outDir, innerRun)
832
+ assertCompleteCost(result.cost, spec.name)
833
+ } catch (error) {
834
+ await writeFile(surfacePath, seed)
835
+ throw error
836
+ }
837
+
838
+ const winner = result.winnerSurface
839
+ if (typeof winner !== 'string' || winner.trim().length === 0) {
840
+ await writeFile(surfacePath, seed)
841
+ throw new Error(`gepa seat '${spec.name}': adapter returned a non-string winner surface`)
842
+ }
843
+
844
+ if (winner === seed) {
845
+ // Restore the seed (the last inner call may have left another candidate)
846
+ // and decline the slot — an unchanged surface has no candidate diff.
847
+ await writeFile(surfacePath, seed)
848
+ return {
849
+ applied: false,
850
+ summary: `gepa ${spec.engine}: best candidate equals the seed after ${innerScores.length} inner call(s)`,
851
+ }
852
+ }
853
+
854
+ await writeFile(surfacePath, winner)
855
+ if (config.activationGate === true) {
856
+ const predicate = mechanicalActivationPredicate(seed, winner, spec.surface)
857
+ if (predicate === null) {
858
+ await writeFile(surfacePath, seed)
859
+ return {
860
+ applied: false,
861
+ summary:
862
+ `gepa ${spec.engine}: winner adds no line of >=${MIN_PREDICATE_LINE_CHARS} chars — ` +
863
+ 'cannot derive a machine-checkable activation predicate; candidate declined',
864
+ }
865
+ }
866
+ const predicatePath = join(args.worktreePath, ACTIVATION_PREDICATE_RELPATH)
867
+ await mkdir(dirname(predicatePath), { recursive: true })
868
+ await writeFile(predicatePath, JSON.stringify(predicate, null, 2))
869
+ }
870
+ return {
871
+ applied: true,
872
+ summary:
873
+ `gepa ${spec.engine} optimized ${spec.surface} over ${innerScores.length}/${budget} inner smoke call(s); ` +
874
+ `best inner composite ${innerRun.bestComposite}`,
875
+ }
876
+ }
877
+ }