@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.
- package/CHANGELOG.md +11 -0
- package/HARNESS.md +43 -0
- package/README.md +7 -0
- package/dist/adapters.js +23 -23
- 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/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-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-PWQVGAJB.js → chunk-V7AEBY6U.js} +21 -21
- 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 +23 -23
- package/package.json +5 -5
- package/scripts/trata-hedge/README.md +6 -5
- package/scripts/verify-packed-consumer.mjs +1 -1
- package/src/benchmarks/_harness.ts +20 -2
- package/src/benchmarks/humaneval.test.mts +2 -2
- package/src/gate.ts +1 -1
- package/src/hev-eval.mts +5 -2
- package/src/hev-improve.mts +118 -73
- package/src/official-optimizer-config.mts +89 -0
- package/src/official-optimizer-config.test.mts +88 -0
- package/src/profiles.ts +2 -2
- 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 +342 -0
- package/src/rollout-ledger/settle-capture.mts +442 -0
- package/src/rollout-ledger/settle-capture.test.mts +270 -0
- package/src/smoke-structural-rollout.mts +15 -9
- package/src/swe-arena/activation.mts +225 -0
- package/src/swe-arena/activation.test.mts +300 -0
- package/src/swe-arena/arms.ts +30 -14
- package/src/swe-arena/briefing.mts +217 -0
- package/src/swe-arena/briefing.test.mts +178 -0
- package/src/swe-arena/calibrate.ts +102 -1
- package/src/swe-arena/cell-evidence.mts +41 -9
- 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/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/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.ts +135 -2
- package/src/swe-arena/gepa-seat.mts +877 -0
- package/src/swe-arena/gepa-seat.test.mts +1059 -0
- package/src/swe-arena/implementation-ref.test.mts +64 -0
- package/src/swe-arena/implementation-ref.ts +62 -0
- package/src/swe-arena/ledger-orphans.mts +77 -0
- package/src/swe-arena/ledger-orphans.test.mts +147 -0
- package/src/swe-arena/outer-loop.mts +710 -33
- 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/proposer-fanout.mts +327 -31
- package/src/swe-arena/proposer-fanout.test.mts +246 -0
- package/src/swe-arena/proposer-provenance.mts +172 -0
- package/src/swe-arena/proposer-provenance.test.mts +106 -0
- package/src/swe-arena/run-experiment.mts +367 -1
- package/src/swe-arena/run-report.mts +75 -0
- package/src/swe-arena/run-supervisor.test.mts +5 -3
- package/src/swe-arena/score-split.mts +140 -0
- package/src/swe-arena/score-split.test.mts +123 -0
- package/src/swe-arena/scratch-worktree.test.mts +55 -0
- package/src/swe-arena/scratch-worktree.ts +34 -0
- package/src/swe-arena/types.ts +52 -0
- package/src/swe-code-improve.mts +24 -25
- package/src/swe-improve.mts +129 -96
- package/src/swe-local-proof.mts +6 -1
- package/src/swe-stream.mts +4 -2
- package/src/swe-structural.mts +245 -837
- package/src/tb-container-executor.test.mts +30 -6
- package/src/tb-supervisor-sidecar.mts +2 -1
- package/src/trata-gepa.mts +182 -245
- package/dist/chunk-LRRD7NAG.js.map +0 -1
- package/src/live-improve-campaign-mbpp.mts +0 -641
- package/src/live-improve-campaign.mts +0 -500
- /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-PWQVGAJB.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
|
@@ -1,641 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* live-improve-campaign-mbpp — the kill-flow follow-up to live-improve-campaign.mts
|
|
3
|
-
* (HumanEval × Qwen2.5-7B: DEV baseline 90%, saturated, gate correctly HELD). Identical
|
|
4
|
-
* loop, moved to a config WITH headroom: sanitized MBPP, where the same worker measures
|
|
5
|
-
* 76.7% at the baseline recipe k5/r2/t6 against an 85.9% pass@5 bound (~9pts of room).
|
|
6
|
-
* The OPEN question this run answers: does dial-tuning ship a win when there is room
|
|
7
|
-
* to win? `improve()` (surface 'rollout-policy') tunes { k, repairRounds, testgen }
|
|
8
|
-
* and the library's own held-out gate makes the ship/hold call — a HOLD is a valid
|
|
9
|
-
* result; the gate is never loosened.
|
|
10
|
-
*
|
|
11
|
-
* Wiring is live-improve-campaign.mts verbatim except the dataset seams:
|
|
12
|
-
* - Tasks come from mbpp-structural.mts's loadMbpp (sanitized MBPP, MBPP_JSON env);
|
|
13
|
-
* prompt = its basePrompt (description + the shown official assert).
|
|
14
|
-
* - VISIBLE checks: the shown assert (test_list[0]) rides task.meta.visibleChecks so
|
|
15
|
-
* the strategy's default officialChecksFromMeta() source ranks it as the OFFICIAL
|
|
16
|
-
* check, lexicographically above the model-authored guesses (mbpp-structural's
|
|
17
|
-
* measured lesson: unweighted guesses flip selection negative).
|
|
18
|
-
* - HIDDEN grading: test_list[1:] (+ test_imports), script-side nonce-sentinel judge
|
|
19
|
-
* in docker --network=none, AFTER the strategy locks its artifact.
|
|
20
|
-
* - test_imports are prepended to every candidate before visible-check execution
|
|
21
|
-
* (a CheckRunner prelude wrapper) and inside the hidden program, mirroring the rig.
|
|
22
|
-
*
|
|
23
|
-
* Honesty split (identical to the prior run):
|
|
24
|
-
* - DEV = sanitized-MBPP usable-task index [0, DEV_N) and HELD-OUT = [DEV_N,
|
|
25
|
-
* DEV_N+HOLD_N) — fixed, disjoint slices, passed as explicit
|
|
26
|
-
* `budget.holdoutScenarios` so the library's own split machinery enforces
|
|
27
|
-
* disjointness (it throws on overlap).
|
|
28
|
-
* - Deterministic proposer; `analyzeGeneration: null` keeps the findings channel
|
|
29
|
-
* empty — the improver's context is ONLY the DEV composites the loop accumulates.
|
|
30
|
-
* - The gate decision is `result.gateDecision` from the library — never recomputed
|
|
31
|
-
* here. The recompute-from-raw block at the end cross-checks the held-out pass
|
|
32
|
-
* counts against the durable per-cell files; a mismatch voids any ship claim.
|
|
33
|
-
*
|
|
34
|
-
* Guards (all from the prior run): SMOKE=1 first (6 dev + 6 held-out tasks, 1
|
|
35
|
-
* generation, population 2 — proves the full path completes before the burn);
|
|
36
|
-
* fail-loud model preflight; MAX_CALLS hard cap (default 8000 — the run aborts loud,
|
|
37
|
-
* durable provenance survives in RUN_DIR); dollars ceiling.
|
|
38
|
-
*
|
|
39
|
-
* Run (key via dotenvx; never in the shell history):
|
|
40
|
-
* cd ~/company/devops/secrets && dotenvx run -f agent-state.env -- bash -c ' \
|
|
41
|
-
* cd /home/drew/code/agent-runtime-swe && \
|
|
42
|
-
* MBPP_JSON=/abs/sanitized-mbpp.json npx tsx bench/src/live-improve-campaign-mbpp.mts'
|
|
43
|
-
*/
|
|
44
|
-
|
|
45
|
-
import { execFile, execFileSync } from 'node:child_process'
|
|
46
|
-
import { randomBytes } from 'node:crypto'
|
|
47
|
-
import { appendFileSync, existsSync, mkdirSync, readdirSync, readFileSync } from 'node:fs'
|
|
48
|
-
import { tmpdir } from 'node:os'
|
|
49
|
-
import { join } from 'node:path'
|
|
50
|
-
import type {
|
|
51
|
-
DispatchContext,
|
|
52
|
-
JudgeConfig,
|
|
53
|
-
MutableSurface,
|
|
54
|
-
Scenario,
|
|
55
|
-
} from '@tangle-network/agent-eval/contract'
|
|
56
|
-
import type { AgentProfile } from '@tangle-network/agent-interface'
|
|
57
|
-
import { improve } from '../../src/improvement/improve'
|
|
58
|
-
import {
|
|
59
|
-
parseRolloutPolicy,
|
|
60
|
-
ROLLOUT_POLICY_EXTENSION,
|
|
61
|
-
serializeRolloutPolicy,
|
|
62
|
-
structuralRolloutPolicyFromProfile,
|
|
63
|
-
} from '../../src/improvement/rollout-policy'
|
|
64
|
-
import {
|
|
65
|
-
type AgenticRunResult,
|
|
66
|
-
type CheckExecChannel,
|
|
67
|
-
type CheckOutcome,
|
|
68
|
-
type CheckRunner,
|
|
69
|
-
createVerifierEnvironment,
|
|
70
|
-
runAgentic,
|
|
71
|
-
sandboxCheckRunner,
|
|
72
|
-
structuralRollout,
|
|
73
|
-
type StructuralRolloutResult,
|
|
74
|
-
} from '../../src/runtime/index'
|
|
75
|
-
import { basePrompt, loadMbpp, type MbppTask } from './mbpp-structural.mts'
|
|
76
|
-
|
|
77
|
-
function must(name: string): string {
|
|
78
|
-
const v = process.env[name]
|
|
79
|
-
if (!v) throw new Error(`env ${name} is required`)
|
|
80
|
-
return v
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const SMOKE = process.env.SMOKE === '1'
|
|
84
|
-
const DEV_N = Number(process.env.DEV_N ?? (SMOKE ? 6 : 150))
|
|
85
|
-
const HOLD_N = Number(process.env.HOLD_N ?? (SMOKE ? 6 : 150))
|
|
86
|
-
const GENERATIONS = Number(process.env.GENERATIONS ?? (SMOKE ? 1 : 2))
|
|
87
|
-
const POPULATION = Number(process.env.POPULATION ?? (SMOKE ? 2 : 4))
|
|
88
|
-
const CONCURRENCY = Number(process.env.CONCURRENCY ?? 8)
|
|
89
|
-
const DOCKER_CONCURRENCY = Number(process.env.DOCKER_CONCURRENCY ?? 6)
|
|
90
|
-
// The worker of the measured MBPP basis: 76.7% repaired@1 at k5/r2/t6, 85.9% pass@5
|
|
91
|
-
// bound — the headroom config the saturated HumanEval run lacked.
|
|
92
|
-
const MODEL = process.env.MODEL ?? 'Qwen/Qwen2.5-7B-Instruct-Turbo'
|
|
93
|
-
const BASE = process.env.ROUTER_BASE ?? 'https://api.together.xyz/v1'
|
|
94
|
-
const TEMP = Number(process.env.TEMPERATURE ?? 0.8)
|
|
95
|
-
const MAX_TOKENS = Number(process.env.MAX_TOKENS ?? 2500)
|
|
96
|
-
const DOLLARS = Number(process.env.DOLLARS ?? 15)
|
|
97
|
-
/** Hard LLM-call cap across every phase — the runaway guard. Hitting it aborts the
|
|
98
|
-
* process loud (durable provenance survives in RUN_DIR); it never silently degrades. */
|
|
99
|
-
const MAX_CALLS = Number(process.env.MAX_CALLS ?? 8000)
|
|
100
|
-
const RUN_DIR = process.env.RUN_DIR ?? join(tmpdir(), `live-improve-campaign-mbpp-${Date.now()}`)
|
|
101
|
-
|
|
102
|
-
const systemPrompt = 'You are an expert Python programmer.'
|
|
103
|
-
const dockerImage = 'python:3.12-slim'
|
|
104
|
-
const dockerTimeoutMs = Number(process.env.DOCKER_TIMEOUT_MS ?? 20000)
|
|
105
|
-
|
|
106
|
-
// ── Docker: ONE semaphored --network=none exec channel for BOTH judges ───────────────
|
|
107
|
-
|
|
108
|
-
let dockerInFlight = 0
|
|
109
|
-
const dockerWaiters: Array<() => void> = []
|
|
110
|
-
async function withDockerSlot<T>(fn: () => Promise<T>): Promise<T> {
|
|
111
|
-
if (dockerInFlight >= DOCKER_CONCURRENCY) await new Promise<void>((r) => dockerWaiters.push(r))
|
|
112
|
-
dockerInFlight += 1
|
|
113
|
-
try {
|
|
114
|
-
return await fn()
|
|
115
|
-
} finally {
|
|
116
|
-
dockerInFlight -= 1
|
|
117
|
-
dockerWaiters.shift()?.()
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
const containerPrefix = `licm-${process.pid}`
|
|
122
|
-
let containerSeq = 0
|
|
123
|
-
|
|
124
|
-
function reapContainers(): void {
|
|
125
|
-
try {
|
|
126
|
-
const ids = execFileSync('docker', ['ps', '-aq', '--filter', `name=${containerPrefix}`], {
|
|
127
|
-
timeout: 10000,
|
|
128
|
-
})
|
|
129
|
-
.toString()
|
|
130
|
-
.trim()
|
|
131
|
-
if (ids) execFileSync('docker', ['rm', '-f', ...ids.split('\n')], { timeout: 15000 })
|
|
132
|
-
} catch {
|
|
133
|
-
/* reaper is best-effort by design */
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
process.on('SIGINT', () => {
|
|
137
|
-
reapContainers()
|
|
138
|
-
process.exit(130)
|
|
139
|
-
})
|
|
140
|
-
process.on('SIGTERM', () => {
|
|
141
|
-
reapContainers()
|
|
142
|
-
process.exit(143)
|
|
143
|
-
})
|
|
144
|
-
|
|
145
|
-
const dockerBox: CheckExecChannel = {
|
|
146
|
-
exec(command, options) {
|
|
147
|
-
const timeoutMs = options?.timeoutMs ?? dockerTimeoutMs
|
|
148
|
-
return withDockerSlot(
|
|
149
|
-
() =>
|
|
150
|
-
new Promise((resolve, reject) => {
|
|
151
|
-
const name = `${containerPrefix}-${containerSeq++}`
|
|
152
|
-
let settled = false
|
|
153
|
-
const reap = () => execFile('docker', ['rm', '-f', name], () => {})
|
|
154
|
-
const finish = (r: { exitCode: number; stdout: string; stderr: string }) => {
|
|
155
|
-
if (settled) return
|
|
156
|
-
settled = true
|
|
157
|
-
clearTimeout(backstop)
|
|
158
|
-
reap()
|
|
159
|
-
resolve(r)
|
|
160
|
-
}
|
|
161
|
-
const fail = (e: Error) => {
|
|
162
|
-
if (settled) return
|
|
163
|
-
settled = true
|
|
164
|
-
clearTimeout(backstop)
|
|
165
|
-
reap()
|
|
166
|
-
reject(e)
|
|
167
|
-
}
|
|
168
|
-
// execFile's timeout kills the docker CLIENT; a hung container could leave
|
|
169
|
-
// the callback unfired. The backstop guarantees resolution and the named
|
|
170
|
-
// reap kills the stray container.
|
|
171
|
-
const backstop = setTimeout(
|
|
172
|
-
() => finish({ exitCode: 124, stdout: '', stderr: 'timed out (backstop)' }),
|
|
173
|
-
timeoutMs + 3000,
|
|
174
|
-
)
|
|
175
|
-
execFile(
|
|
176
|
-
'docker',
|
|
177
|
-
['run', '--rm', '--name', name, '--network=none', '--cpus=1', '--memory=512m', dockerImage, 'sh', '-c', command],
|
|
178
|
-
{ timeout: timeoutMs, killSignal: 'SIGKILL', maxBuffer: 4 * 1024 * 1024 },
|
|
179
|
-
(err, stdout, stderr) => {
|
|
180
|
-
if (err) {
|
|
181
|
-
const e = err as NodeJS.ErrnoException & { code?: number | string }
|
|
182
|
-
if (e.code === 'ENOENT') {
|
|
183
|
-
fail(new Error('docker binary not found on PATH'))
|
|
184
|
-
return
|
|
185
|
-
}
|
|
186
|
-
if (/cannot connect to the docker daemon|is the docker daemon running|permission denied while trying to connect/i.test(stderr ?? '')) {
|
|
187
|
-
fail(new Error(`docker daemon unreachable: ${(stderr ?? '').slice(0, 200)}`))
|
|
188
|
-
return
|
|
189
|
-
}
|
|
190
|
-
finish({ exitCode: typeof e.code === 'number' ? e.code : 1, stdout: stdout ?? '', stderr: stderr ?? '' })
|
|
191
|
-
return
|
|
192
|
-
}
|
|
193
|
-
finish({ exitCode: 0, stdout: stdout ?? '', stderr: stderr ?? '' })
|
|
194
|
-
},
|
|
195
|
-
)
|
|
196
|
-
}),
|
|
197
|
-
)
|
|
198
|
-
},
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// ── The hidden nonce judge (script-side, AFTER the strategy locks its artifact) ──────
|
|
202
|
-
// MBPP hidden suite = test_list[1:] with test_imports; pass requires exit 0 AND the
|
|
203
|
-
// per-call nonce sentinel in stdout — a candidate printing a forged verdict cannot pass.
|
|
204
|
-
|
|
205
|
-
function buildHiddenProgram(task: MbppTask, candidate: string, nonce: string): string {
|
|
206
|
-
return `${task.testImports.join('\n')}\n${candidate}\n\n${task.hiddenAsserts.join('\n')}\nprint("HIDDEN-${nonce} PASS")\n`
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
async function runHiddenJudge(
|
|
210
|
-
task: MbppTask,
|
|
211
|
-
candidate: string,
|
|
212
|
-
): Promise<{ pass: number; detail?: string }> {
|
|
213
|
-
const nonce = randomBytes(8).toString('hex')
|
|
214
|
-
const b64 = Buffer.from(buildHiddenProgram(task, candidate, nonce), 'utf8').toString('base64')
|
|
215
|
-
const r = await dockerBox.exec(`printf '%s' '${b64}' | base64 -d | python3 -`, {
|
|
216
|
-
timeoutMs: dockerTimeoutMs,
|
|
217
|
-
})
|
|
218
|
-
if (r.exitCode === 0 && r.stdout.includes(`HIDDEN-${nonce} PASS`)) return { pass: 1 }
|
|
219
|
-
return { pass: 0, detail: (r.stderr || r.stdout).slice(-300) || 'timed out (no output)' }
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// ── Scenarios: fixed disjoint slices of sanitized MBPP ────────────────────────────────
|
|
223
|
-
|
|
224
|
-
interface MbppScenario extends Scenario {
|
|
225
|
-
kind: 'mbpp'
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
const taskById = new Map<string, MbppTask>()
|
|
229
|
-
const scenarioId = (t: MbppTask) => `mbpp/${t.taskId}`
|
|
230
|
-
|
|
231
|
-
// ── The real evaluator: one cell = one structuralRollout run + hidden grade ──────────
|
|
232
|
-
|
|
233
|
-
interface CellArtifact {
|
|
234
|
-
taskId: string
|
|
235
|
-
policy: string
|
|
236
|
-
/** Hidden nonce-judge grade of the FINAL selected candidate: {0,1}. */
|
|
237
|
-
pass: number
|
|
238
|
-
detail?: string
|
|
239
|
-
repairStop: string
|
|
240
|
-
shots: number
|
|
241
|
-
completions: number
|
|
242
|
-
authoredChecks: number
|
|
243
|
-
officialChecks: number
|
|
244
|
-
tokens: { input: number; output: number }
|
|
245
|
-
usd: number
|
|
246
|
-
ms: number
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
interface ScoredCandidate {
|
|
250
|
-
candidate: string
|
|
251
|
-
outcome: CheckOutcome
|
|
252
|
-
}
|
|
253
|
-
function recordingRunner(inner: CheckRunner, log: ScoredCandidate[]): CheckRunner {
|
|
254
|
-
return {
|
|
255
|
-
async run(candidate, checks, ctx) {
|
|
256
|
-
const outcome = await inner.run(candidate, checks, ctx)
|
|
257
|
-
log.push({ candidate, outcome })
|
|
258
|
-
return outcome
|
|
259
|
-
},
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
/** MBPP's test_imports must be in scope when the visible checks run (the rig prepends
|
|
264
|
-
* them to every judged program). The recorded candidate stays RAW — the hidden judge
|
|
265
|
-
* prepends the same imports itself. */
|
|
266
|
-
function testImportsPrelude(inner: CheckRunner): CheckRunner {
|
|
267
|
-
return {
|
|
268
|
-
run(candidate, checks, ctx) {
|
|
269
|
-
const raw = ctx.task.meta?.testImports
|
|
270
|
-
const imports = Array.isArray(raw) ? raw.filter((i): i is string => typeof i === 'string') : []
|
|
271
|
-
return inner.run(imports.length > 0 ? `${imports.join('\n')}\n${candidate}` : candidate, checks, ctx)
|
|
272
|
-
},
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
// Global spend meter (every cell of every phase — baseline, generations, holdout).
|
|
277
|
-
const spend = { cells: 0, llmCalls: 0, tokensIn: 0, tokensOut: 0, usd: 0, hiddenPass: 0 }
|
|
278
|
-
|
|
279
|
-
async function evaluateCell(
|
|
280
|
-
surface: MutableSurface,
|
|
281
|
-
scenario: MbppScenario,
|
|
282
|
-
ctx: DispatchContext,
|
|
283
|
-
): Promise<CellArtifact> {
|
|
284
|
-
if (spend.llmCalls >= MAX_CALLS) {
|
|
285
|
-
console.error(
|
|
286
|
-
`\nBUDGET CAP HIT: ${spend.llmCalls} llm calls >= MAX_CALLS=${MAX_CALLS} — aborting loud; durable provenance in ${RUN_DIR}`,
|
|
287
|
-
)
|
|
288
|
-
reapContainers()
|
|
289
|
-
process.exit(1)
|
|
290
|
-
}
|
|
291
|
-
const policy = parseRolloutPolicy(surface)
|
|
292
|
-
if (!policy) {
|
|
293
|
-
throw new Error(`agent: surface carries no valid rollout policy: ${String(surface).slice(0, 120)}`)
|
|
294
|
-
}
|
|
295
|
-
const task = taskById.get(scenario.id)
|
|
296
|
-
if (!task) throw new Error(`agent: unknown scenario id ${scenario.id}`)
|
|
297
|
-
|
|
298
|
-
const scored: ScoredCandidate[] = []
|
|
299
|
-
const strategy = structuralRollout({
|
|
300
|
-
policy: { ...policy, temperature: TEMP },
|
|
301
|
-
checkRunner: recordingRunner(testImportsPrelude(sandboxCheckRunner({ box: dockerBox })), scored),
|
|
302
|
-
})
|
|
303
|
-
// INERT check: the strategy's harness-verified score channel carries no hidden
|
|
304
|
-
// signal — hidden grading happens below, after the rollout locks its artifact.
|
|
305
|
-
const inertSurface = createVerifierEnvironment({
|
|
306
|
-
name: 'mbpp-inert',
|
|
307
|
-
check: () => ({ passes: 0, total: 1, errored: 0 }),
|
|
308
|
-
})
|
|
309
|
-
const result = (await runAgentic({
|
|
310
|
-
surface: inertSurface,
|
|
311
|
-
task: {
|
|
312
|
-
id: scenario.id,
|
|
313
|
-
systemPrompt,
|
|
314
|
-
userPrompt: basePrompt(task),
|
|
315
|
-
meta: {
|
|
316
|
-
entryPoint: task.entryPoint,
|
|
317
|
-
// The OFFICIAL check: the shown assert (test_list[0], printed in the prompt)
|
|
318
|
-
// feeds the strategy's default officialChecksFromMeta() source, so it ranks
|
|
319
|
-
// above the model-authored guesses in selection and repair.
|
|
320
|
-
visibleChecks: [task.shownAssert],
|
|
321
|
-
testImports: task.testImports,
|
|
322
|
-
},
|
|
323
|
-
},
|
|
324
|
-
routerBaseUrl: BASE,
|
|
325
|
-
routerKey: must('TOGETHER_API_KEY'),
|
|
326
|
-
model: MODEL,
|
|
327
|
-
temperature: TEMP,
|
|
328
|
-
maxTokens: MAX_TOKENS,
|
|
329
|
-
innerTurns: 2,
|
|
330
|
-
strategy,
|
|
331
|
-
// The strategy's documented sizing: k samples + repair rounds + the check-author consult.
|
|
332
|
-
budget: policy.k + policy.repairRounds + 1,
|
|
333
|
-
})) as AgenticRunResult & StructuralRolloutResult
|
|
334
|
-
|
|
335
|
-
// Backend integrity: report REAL usage on every cell (expectUsage 'assert' upstream).
|
|
336
|
-
ctx.cost.observe(result.usd, 'together')
|
|
337
|
-
ctx.cost.observeTokens(result.tokens)
|
|
338
|
-
|
|
339
|
-
const winner = result.selection.find((r) => r.selected)
|
|
340
|
-
if (!winner) {
|
|
341
|
-
// Zero candidates means EVERY shot for this cell returned null — the signature of a
|
|
342
|
-
// dead worker (credits exhausted / rate-limited / outage), not a hard task. The
|
|
343
|
-
// runtime swallows exhausted-retry shots as null, so without this the run degrades
|
|
344
|
-
// silently for hundreds of cells and then dies at the holdout with a cryptic empty-
|
|
345
|
-
// gate error. Probe the API once and abort LOUD with the real HTTP status so the
|
|
346
|
-
// operator sees the actual cause (e.g. HTTP 402 credit exceeded) immediately.
|
|
347
|
-
if (result.repairStop === 'no-candidates') {
|
|
348
|
-
await abortOnDeadWorker(scenario.id)
|
|
349
|
-
}
|
|
350
|
-
throw new Error(`${scenario.id}: no receipt marked selected (repairStop=${result.repairStop})`)
|
|
351
|
-
}
|
|
352
|
-
const rec = scored[winner.candidateIndex]
|
|
353
|
-
if (!rec) {
|
|
354
|
-
throw new Error(
|
|
355
|
-
`${scenario.id}: selected receipt #${winner.candidateIndex} has no recorded candidate (${scored.length} scored)`,
|
|
356
|
-
)
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
const hidden = await runHiddenJudge(task, rec.candidate)
|
|
360
|
-
|
|
361
|
-
spend.cells += 1
|
|
362
|
-
spend.llmCalls += result.completions
|
|
363
|
-
spend.tokensIn += result.tokens.input
|
|
364
|
-
spend.tokensOut += result.tokens.output
|
|
365
|
-
spend.usd += result.usd
|
|
366
|
-
spend.hiddenPass += hidden.pass
|
|
367
|
-
|
|
368
|
-
const artifact: CellArtifact = {
|
|
369
|
-
taskId: scenario.id,
|
|
370
|
-
policy: serializeRolloutPolicy(policy),
|
|
371
|
-
pass: hidden.pass,
|
|
372
|
-
...(hidden.detail ? { detail: hidden.detail } : {}),
|
|
373
|
-
repairStop: result.repairStop,
|
|
374
|
-
shots: result.shots,
|
|
375
|
-
completions: result.completions,
|
|
376
|
-
authoredChecks: result.authoredChecks,
|
|
377
|
-
officialChecks: result.officialChecks,
|
|
378
|
-
tokens: result.tokens,
|
|
379
|
-
usd: result.usd,
|
|
380
|
-
ms: result.ms,
|
|
381
|
-
}
|
|
382
|
-
appendFileSync(
|
|
383
|
-
join(RUN_DIR, 'cells.jsonl'),
|
|
384
|
-
`${JSON.stringify({ cellId: ctx.cellId, generation: ctx.generation ?? null, ...artifact, detail: undefined })}\n`,
|
|
385
|
-
)
|
|
386
|
-
console.log(
|
|
387
|
-
` [cell ${String(spend.cells).padStart(4)}] ${scenario.id.padEnd(10)} ${artifact.policy.padEnd(38)} hidden=${hidden.pass ? 'PASS' : 'fail'} ${result.repairStop} calls=${result.completions}`,
|
|
388
|
-
)
|
|
389
|
-
return artifact
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
// The in-loop judge is a deterministic transcriber of the script-side hidden grade —
|
|
393
|
-
// the grading itself never runs inside the strategy or the proposer's view.
|
|
394
|
-
const hiddenJudge: JudgeConfig<CellArtifact, MbppScenario> = {
|
|
395
|
-
name: 'hidden-nonce-judge',
|
|
396
|
-
dimensions: [
|
|
397
|
-
{ key: 'hidden', description: 'MBPP hidden suite test_list[1:] (docker --network=none, nonce sentinel)' },
|
|
398
|
-
],
|
|
399
|
-
score: ({ artifact }) => ({
|
|
400
|
-
dimensions: { hidden: artifact.pass },
|
|
401
|
-
composite: artifact.pass,
|
|
402
|
-
notes: artifact.pass ? 'hidden PASS' : `hidden fail: ${(artifact.detail ?? '').slice(0, 160)}`,
|
|
403
|
-
}),
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
// ── Fail-loud model preflight (cost gate: prove the worker is live before any burn) ──
|
|
407
|
-
|
|
408
|
-
/** One direct 8-token probe of the worker. Returns HTTP status + a short body slice —
|
|
409
|
-
* the shared health check for the startup preflight and the mid-run dead-worker guard. */
|
|
410
|
-
async function probeWorker(): Promise<{ ok: boolean; status: number; body: string; content: string }> {
|
|
411
|
-
const res = await fetch(`${BASE}/chat/completions`, {
|
|
412
|
-
method: 'POST',
|
|
413
|
-
headers: { Authorization: `Bearer ${must('TOGETHER_API_KEY')}`, 'Content-Type': 'application/json' },
|
|
414
|
-
body: JSON.stringify({
|
|
415
|
-
model: MODEL,
|
|
416
|
-
max_tokens: 16,
|
|
417
|
-
temperature: 0,
|
|
418
|
-
messages: [{ role: 'user', content: 'Reply with the single word: ready' }],
|
|
419
|
-
}),
|
|
420
|
-
})
|
|
421
|
-
if (!res.ok) return { ok: false, status: res.status, body: (await res.text()).slice(0, 300), content: '' }
|
|
422
|
-
const d = (await res.json()) as { choices?: Array<{ message?: { content?: string } }> }
|
|
423
|
-
return { ok: true, status: res.status, body: '', content: (d.choices?.[0]?.message?.content ?? '').trim() }
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
async function preflightModel(): Promise<void> {
|
|
427
|
-
const p = await probeWorker()
|
|
428
|
-
if (!p.ok) throw new Error(`model preflight FAILED: ${MODEL} @ ${BASE} → HTTP ${p.status}: ${p.body}`)
|
|
429
|
-
if (p.content === '') throw new Error(`model preflight FAILED: ${MODEL} returned empty content`)
|
|
430
|
-
console.log(` preflight: ${MODEL} is live (replied ${JSON.stringify(p.content.slice(0, 40))})`)
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
/** Called when a cell produced zero candidates (every shot null). Probes the worker; if
|
|
434
|
-
* it is unhealthy (e.g. HTTP 402 credit exceeded, 429 rate limit, outage) the whole run
|
|
435
|
-
* is doomed — abort LOUD now rather than degrade through hundreds more null cells into
|
|
436
|
-
* a cryptic empty-holdout gate error. If the probe is HEALTHY the null was a one-off, so
|
|
437
|
-
* return and let the per-cell throw handle just this cell. */
|
|
438
|
-
async function abortOnDeadWorker(scenarioId: string): Promise<void> {
|
|
439
|
-
const p = await probeWorker()
|
|
440
|
-
if (p.ok && p.content !== '') return
|
|
441
|
-
console.error(
|
|
442
|
-
`\nDEAD WORKER: cell ${scenarioId} produced zero candidates and a direct probe returned ` +
|
|
443
|
-
`${p.ok ? `empty content` : `HTTP ${p.status}: ${p.body}`}. Every shot is failing — aborting ` +
|
|
444
|
-
`loud (durable provenance in ${RUN_DIR}). If this is HTTP 402, add Together credits and re-run.`,
|
|
445
|
-
)
|
|
446
|
-
reapContainers()
|
|
447
|
-
process.exit(1)
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
// ── Reporting helpers (read the library's own result objects; never re-decide) ───────
|
|
451
|
-
|
|
452
|
-
interface CampaignLike {
|
|
453
|
-
cells: Array<{ error?: string | null; judgeScores: Record<string, { composite: number }> }>
|
|
454
|
-
}
|
|
455
|
-
function passStats(campaign: CampaignLike): { passed: number; scored: number; errored: number; rate: number } {
|
|
456
|
-
let passed = 0
|
|
457
|
-
let scored = 0
|
|
458
|
-
let errored = 0
|
|
459
|
-
for (const cell of campaign.cells) {
|
|
460
|
-
if (cell.error) {
|
|
461
|
-
errored += 1
|
|
462
|
-
continue
|
|
463
|
-
}
|
|
464
|
-
scored += 1
|
|
465
|
-
const scores = Object.values(cell.judgeScores)
|
|
466
|
-
const composite = scores.length === 0 ? 0 : scores.reduce((s, j) => s + j.composite, 0) / scores.length
|
|
467
|
-
if (composite >= 0.999) passed += 1
|
|
468
|
-
}
|
|
469
|
-
return { passed, scored, errored, rate: scored > 0 ? passed / scored : 0 }
|
|
470
|
-
}
|
|
471
|
-
const pct = (x: number) => `${(100 * x).toFixed(1)}%`
|
|
472
|
-
|
|
473
|
-
/** Recompute pass counts from the DURABLE per-cell files the loop's fs storage wrote
|
|
474
|
-
* (`<runDir>/<phase>/<scenario>_<rep>/cached-result.json`, artifact.pass = the hidden
|
|
475
|
-
* nonce-judge grade). Independent of the in-memory campaign objects — the ship-claim
|
|
476
|
-
* cross-check. */
|
|
477
|
-
function recomputeFromRaw(dir: string): { passed: number; scored: number; errored: number } | null {
|
|
478
|
-
if (!existsSync(dir)) return null
|
|
479
|
-
let passed = 0
|
|
480
|
-
let scored = 0
|
|
481
|
-
let errored = 0
|
|
482
|
-
for (const entry of readdirSync(dir)) {
|
|
483
|
-
const file = join(dir, entry, 'cached-result.json')
|
|
484
|
-
if (!existsSync(file)) continue
|
|
485
|
-
const cell = JSON.parse(readFileSync(file, 'utf8')) as {
|
|
486
|
-
error?: string | null
|
|
487
|
-
artifact?: { pass?: number }
|
|
488
|
-
}
|
|
489
|
-
if (cell.error) {
|
|
490
|
-
errored += 1
|
|
491
|
-
continue
|
|
492
|
-
}
|
|
493
|
-
scored += 1
|
|
494
|
-
if ((cell.artifact?.pass ?? 0) >= 1) passed += 1
|
|
495
|
-
}
|
|
496
|
-
return { passed, scored, errored }
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
async function main(): Promise<void> {
|
|
500
|
-
must('TOGETHER_API_KEY')
|
|
501
|
-
mkdirSync(RUN_DIR, { recursive: true })
|
|
502
|
-
const started = Date.now()
|
|
503
|
-
|
|
504
|
-
const { tasks: all, droppedShort, droppedEntry } = loadMbpp(DEV_N + HOLD_N, 0)
|
|
505
|
-
if (all.length !== DEV_N + HOLD_N) {
|
|
506
|
-
throw new Error(`expected ${DEV_N + HOLD_N} usable MBPP tasks, loaded ${all.length}`)
|
|
507
|
-
}
|
|
508
|
-
const devTasks = all.slice(0, DEV_N)
|
|
509
|
-
const holdTasks = all.slice(DEV_N)
|
|
510
|
-
for (const t of all) taskById.set(scenarioId(t), t)
|
|
511
|
-
|
|
512
|
-
const toScenario = (t: MbppTask): MbppScenario => ({ id: scenarioId(t), kind: 'mbpp' })
|
|
513
|
-
const scenarios = all.map(toScenario)
|
|
514
|
-
const holdoutScenarios = holdTasks.map(toScenario)
|
|
515
|
-
|
|
516
|
-
const baselinePolicy = { k: 5, repairRounds: 2, testgen: 6 }
|
|
517
|
-
const profile: AgentProfile = {
|
|
518
|
-
name: 'mbpp-structural-worker',
|
|
519
|
-
extensions: { [ROLLOUT_POLICY_EXTENSION]: baselinePolicy },
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
console.log('=== LIVE self-improvement campaign · MBPP (headroom config) · improve() surface rollout-policy ===')
|
|
523
|
-
console.log(` worker: ${MODEL} @ ${BASE} (temp=${TEMP}, maxTokens=${MAX_TOKENS}, innerTurns=2)`)
|
|
524
|
-
console.log(
|
|
525
|
-
` dataset: sanitized MBPP (${process.env.MBPP_JSON}); dropped at load: ${droppedShort.length} (<2 asserts), ${droppedEntry.length} (entry unresolved)`,
|
|
526
|
-
)
|
|
527
|
-
console.log(
|
|
528
|
-
` DEV slice : usable-task index [0, ${DEV_N}) — mbpp/${devTasks[0]?.taskId} .. mbpp/${devTasks[devTasks.length - 1]?.taskId} (n=${devTasks.length})`,
|
|
529
|
-
)
|
|
530
|
-
console.log(
|
|
531
|
-
` HELD-OUT slice : usable-task index [${DEV_N}, ${DEV_N + HOLD_N}) — mbpp/${holdTasks[0]?.taskId} .. mbpp/${holdTasks[holdTasks.length - 1]?.taskId} (n=${holdTasks.length})`,
|
|
532
|
-
)
|
|
533
|
-
console.log(` baseline policy: ${JSON.stringify(baselinePolicy)} (measured basis: 76.7% repaired@1, 85.9% pass@5 bound)`)
|
|
534
|
-
console.log(
|
|
535
|
-
` budget: generations=${GENERATIONS} population<=${POPULATION} reps=1 concurrency=${CONCURRENCY} docker<=${DOCKER_CONCURRENCY} ceiling=$${DOLLARS} maxCalls=${MAX_CALLS}`,
|
|
536
|
-
)
|
|
537
|
-
console.log(` gate: library defaultProductionGate (paired bootstrap on held-out, ship iff CI.low > 0.05) — UNCHANGED`)
|
|
538
|
-
console.log(` runDir: ${RUN_DIR}`)
|
|
539
|
-
await preflightModel()
|
|
540
|
-
console.log(`\n profile BEFORE: ${JSON.stringify(profile)}\n`)
|
|
541
|
-
|
|
542
|
-
const result = await improve<MbppScenario, CellArtifact>(profile, [], {
|
|
543
|
-
surface: 'rollout-policy',
|
|
544
|
-
scenarios,
|
|
545
|
-
judge: hiddenJudge,
|
|
546
|
-
agent: evaluateCell,
|
|
547
|
-
budget: {
|
|
548
|
-
generations: GENERATIONS,
|
|
549
|
-
populationSize: POPULATION,
|
|
550
|
-
maxConcurrency: CONCURRENCY,
|
|
551
|
-
holdoutScenarios,
|
|
552
|
-
reps: 1,
|
|
553
|
-
dollars: DOLLARS,
|
|
554
|
-
},
|
|
555
|
-
runDir: RUN_DIR,
|
|
556
|
-
// Deterministic proposer, empty findings channel: the improver's context is ONLY
|
|
557
|
-
// the DEV composites the loop accumulates — no distilled failure text, no trace
|
|
558
|
-
// paths, and (by the loop's own structure) never a held-out cell.
|
|
559
|
-
analyzeGeneration: null,
|
|
560
|
-
})
|
|
561
|
-
|
|
562
|
-
const loop = result.raw.raw
|
|
563
|
-
const wallMin = (Date.now() - started) / 60000
|
|
564
|
-
|
|
565
|
-
console.log('\n── DEV (train) results — what the improver saw ──')
|
|
566
|
-
const baseDev = passStats(loop.baselineCampaign)
|
|
567
|
-
console.log(
|
|
568
|
-
` gen -1 baseline ${serializeRolloutPolicy(structuralRolloutPolicyFromProfile(profile)!).padEnd(38)} DEV ${baseDev.passed}/${baseDev.scored} = ${pct(baseDev.rate)} (errored ${baseDev.errored})`,
|
|
569
|
-
)
|
|
570
|
-
for (const gen of loop.generations) {
|
|
571
|
-
const surfaceByHash = new Map(gen.surfaces.map((s) => [s.surfaceHash, s]))
|
|
572
|
-
const promotedHashes = new Set(gen.record.promoted)
|
|
573
|
-
for (const cand of gen.record.candidates) {
|
|
574
|
-
const s = surfaceByHash.get(cand.surfaceHash)
|
|
575
|
-
const stats = s ? passStats(s.campaign) : undefined
|
|
576
|
-
console.log(
|
|
577
|
-
` gen ${String(gen.record.generationIndex).padStart(2)} ${String(cand.label ?? '').padEnd(16)} ${String(s?.surface ?? '?').padEnd(38)} DEV ${stats ? `${stats.passed}/${stats.scored} = ${pct(stats.rate)} (errored ${stats.errored})` : '?'}${promotedHashes.has(cand.surfaceHash) ? ' [promoted]' : ''}`,
|
|
578
|
-
)
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
console.log(` training winner: ${String(loop.winnerSurface)}${loop.winnerLabel ? ` (${loop.winnerLabel})` : ''}`)
|
|
582
|
-
|
|
583
|
-
console.log('\n── HELD-OUT gate — the library decides ──')
|
|
584
|
-
const baseHold = passStats(loop.baselineOnHoldout)
|
|
585
|
-
const winHold = passStats(loop.winnerOnHoldout)
|
|
586
|
-
console.log(
|
|
587
|
-
` baseline on held-out : ${baseHold.passed}/${baseHold.scored} = ${pct(baseHold.rate)} (errored ${baseHold.errored})`,
|
|
588
|
-
)
|
|
589
|
-
console.log(
|
|
590
|
-
` winner on held-out : ${winHold.passed}/${winHold.scored} = ${pct(winHold.rate)} (errored ${winHold.errored})`,
|
|
591
|
-
)
|
|
592
|
-
console.log(` gate decision: ${result.gateDecision.toUpperCase()} (lift ${result.lift >= 0 ? '+' : ''}${result.lift.toFixed(3)})`)
|
|
593
|
-
for (const reason of loop.gateResult.reasons) console.log(` reason: ${reason}`)
|
|
594
|
-
for (const g of loop.gateResult.contributingGates) {
|
|
595
|
-
console.log(` gate[${g.name}] passed=${g.passed} detail=${JSON.stringify(g.detail).slice(0, 300)}`)
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
console.log('\n── recompute-from-raw check (durable cell files, artifact.pass) ──')
|
|
599
|
-
const rawBase = recomputeFromRaw(join(RUN_DIR, 'holdout-baseline'))
|
|
600
|
-
const rawWin = recomputeFromRaw(join(RUN_DIR, 'holdout-winner'))
|
|
601
|
-
if (rawBase && rawWin && rawBase.scored > 0 && rawWin.scored > 0) {
|
|
602
|
-
const rawLift = rawWin.passed / rawWin.scored - rawBase.passed / rawBase.scored
|
|
603
|
-
console.log(` holdout-baseline raw: ${rawBase.passed}/${rawBase.scored} = ${pct(rawBase.passed / rawBase.scored)} (errored ${rawBase.errored})`)
|
|
604
|
-
console.log(` holdout-winner raw: ${rawWin.passed}/${rawWin.scored} = ${pct(rawWin.passed / rawWin.scored)} (errored ${rawWin.errored})`)
|
|
605
|
-
console.log(` raw pass-rate lift: ${rawLift >= 0 ? '+' : ''}${rawLift.toFixed(3)} (loop-reported composite lift ${result.lift >= 0 ? '+' : ''}${result.lift.toFixed(3)})`)
|
|
606
|
-
const agree = rawBase.passed === baseHold.passed && rawWin.passed === winHold.passed
|
|
607
|
-
console.log(` agreement with library objects: ${agree ? 'EXACT' : 'MISMATCH — investigate before any ship claim'}`)
|
|
608
|
-
} else if (rawBase && rawBase.scored > 0 && !rawWin) {
|
|
609
|
-
// The library skips the winner holdout campaign when winner == baseline (empty
|
|
610
|
-
// diff) — nothing shipped, so there is nothing separate to recompute.
|
|
611
|
-
console.log(
|
|
612
|
-
` holdout-baseline raw: ${rawBase.passed}/${rawBase.scored} = ${pct(rawBase.passed / rawBase.scored)} (errored ${rawBase.errored})`,
|
|
613
|
-
)
|
|
614
|
-
console.log(' holdout-winner absent: winner == baseline, no separate winner campaign ran (ship impossible this run)')
|
|
615
|
-
} else {
|
|
616
|
-
console.log(' raw holdout cell files missing — cannot recompute (storage did not persist cells?)')
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
console.log('\n── ship/hold outcome ──')
|
|
620
|
-
console.log(` shipped: ${result.shipped}`)
|
|
621
|
-
console.log(` profile AFTER : ${JSON.stringify(result.profile)}`)
|
|
622
|
-
if (result.shipped) {
|
|
623
|
-
console.log(` policy change : ${serializeRolloutPolicy(structuralRolloutPolicyFromProfile(profile)!)} → ${serializeRolloutPolicy(structuralRolloutPolicyFromProfile(result.profile)!)}`)
|
|
624
|
-
} else {
|
|
625
|
-
console.log(' policy change : none (gate held — baseline policy stays)')
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
console.log('\n── spend / provenance ──')
|
|
629
|
-
console.log(
|
|
630
|
-
` cells ${spend.cells} · llm calls ${spend.llmCalls} · tokens ${spend.tokensIn} in / ${spend.tokensOut} out · router-priced $${spend.usd.toFixed(4)} · loop-reported $${result.raw.totalCostUsd.toFixed(4)}`,
|
|
631
|
-
)
|
|
632
|
-
console.log(` wall ${wallMin.toFixed(1)} min · runDir ${RUN_DIR} (cells.jsonl + campaign cells + loop provenance)`)
|
|
633
|
-
reapContainers()
|
|
634
|
-
process.exit(0)
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
main().catch((e) => {
|
|
638
|
-
console.error(e)
|
|
639
|
-
reapContainers()
|
|
640
|
-
process.exit(1)
|
|
641
|
-
})
|