@tangle-network/agent-bench 0.1.0 → 0.3.5

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 (139) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/HARNESS.md +302 -0
  3. package/README.md +26 -1
  4. package/fixtures/aec-bench.json +18 -0
  5. package/fixtures/agentbench-dbbench.json +22 -0
  6. package/fixtures/bfcl.json +45 -0
  7. package/fixtures/commit0.json +72 -0
  8. package/fixtures/crag.json +10 -0
  9. package/fixtures/dabstep.json +22 -0
  10. package/fixtures/enterpriseops-gym.json +103 -0
  11. package/fixtures/finresearchbench.json +21 -0
  12. package/fixtures/finsearchcomp.json +66 -0
  13. package/fixtures/frames.json +26 -0
  14. package/fixtures/hotpotqa.json +182 -0
  15. package/fixtures/nomiracl.json +26 -0
  16. package/fixtures/open-rag-bench.json +16 -0
  17. package/fixtures/pier-agent/no-model-task/environment/Dockerfile +16 -0
  18. package/fixtures/pier-agent/no-model-task/environment/seed/src/status.txt +1 -0
  19. package/fixtures/pier-agent/no-model-task/instruction.md +6 -0
  20. package/fixtures/pier-agent/no-model-task/pre_artifacts.sh +6 -0
  21. package/fixtures/pier-agent/no-model-task/task.toml +35 -0
  22. package/fixtures/pier-agent/no-model-task/tests/Dockerfile +17 -0
  23. package/fixtures/pier-agent/no-model-task/tests/seed/src/status.txt +1 -0
  24. package/fixtures/pier-agent/no-model-task/tests/test.sh +19 -0
  25. package/fixtures/programbench.json +17 -0
  26. package/fixtures/ragbench.json +21 -0
  27. package/fixtures/simpleqa.json +121 -0
  28. package/fixtures/t2-ragbench.json +13 -0
  29. package/fixtures/tau2-bench.json +16 -0
  30. package/fixtures/tau3-banking.json +16 -0
  31. package/fixtures/toollm.json +28 -0
  32. package/fixtures/webarena-verified.json +20 -0
  33. package/package.json +39 -15
  34. package/pier_agents/__init__.py +18 -0
  35. package/pier_agents/candidate_contract.py +755 -0
  36. package/pier_agents/process_boundary.py +321 -0
  37. package/pier_agents/tangle_candidate.py +907 -0
  38. package/pier_agents/workspace_boundary.py +368 -0
  39. package/scripts/appworld_driver.py +359 -0
  40. package/scripts/cadbench_prepare.py +22 -0
  41. package/scripts/cadgenbench_hard_parts.py +48 -0
  42. package/scripts/clbench_codebase_judge.py +73 -0
  43. package/scripts/commit0_judge.py +170 -0
  44. package/scripts/dabstep_judge.py +42 -0
  45. package/scripts/enterpriseops_gym_judge.py +281 -0
  46. package/scripts/programbench_judge.py +120 -0
  47. package/scripts/render-gate-chart.mjs +176 -0
  48. package/scripts/run-package-tests.mjs +56 -0
  49. package/scripts/terminate-pier-trial.mts +66 -0
  50. package/scripts/trata-hedge/README.md +56 -0
  51. package/scripts/trata-hedge/run.sh +60 -0
  52. package/scripts/trata-hedge/solve.py +83 -0
  53. package/scripts/verify-packed-consumer.mjs +224 -0
  54. package/scripts/verify-pier-agent.mts +715 -0
  55. package/scripts/verify-pier-pair.mts +74 -0
  56. package/scripts/verify-pier-recovery.mts +139 -0
  57. package/src/adapters.ts +26 -0
  58. package/src/benchmarks/_harness.test.mts +178 -0
  59. package/src/benchmarks/_harness.ts +239 -16
  60. package/src/benchmarks/agentbench.ts +163 -0
  61. package/src/benchmarks/appworld.test.mts +15 -9
  62. package/src/benchmarks/bfcl.ts +346 -0
  63. package/src/benchmarks/crag.ts +137 -0
  64. package/src/benchmarks/dabstep.test.mts +70 -0
  65. package/src/benchmarks/dabstep.ts +212 -0
  66. package/src/benchmarks/external-adapters.test.mts +150 -0
  67. package/src/benchmarks/finresearchbench.ts +269 -0
  68. package/src/benchmarks/humaneval.ts +20 -8
  69. package/src/benchmarks/nomiracl.ts +180 -0
  70. package/src/benchmarks/open-rag-bench.ts +153 -0
  71. package/src/benchmarks/rag-benchmarks.test.mts +138 -0
  72. package/src/benchmarks/rag-shared.ts +327 -0
  73. package/src/benchmarks/ragbench.ts +171 -0
  74. package/src/benchmarks/swe-bench.test.mts +61 -0
  75. package/src/benchmarks/swe-bench.ts +201 -19
  76. package/src/benchmarks/t2-ragbench.ts +166 -0
  77. package/src/benchmarks/tau-bench-shared.ts +214 -0
  78. package/src/benchmarks/tau2-bench.ts +30 -0
  79. package/src/benchmarks/tau3-banking.ts +29 -0
  80. package/src/benchmarks/terminal-bench.test.mts +33 -0
  81. package/src/benchmarks/terminal-bench.ts +23 -8
  82. package/src/benchmarks/toollm.ts +254 -0
  83. package/src/benchmarks/types.ts +42 -0
  84. package/src/benchmarks/webarena-verified.ts +200 -0
  85. package/src/commit0-prereqs.sh +0 -0
  86. package/src/coordination-mcp-container-reach.mts +181 -0
  87. package/src/decoder-live.mts +1 -1
  88. package/src/examples/README.md +103 -39
  89. package/src/examples/benchmark-matrix.mts +101 -0
  90. package/src/examples/lean-proof-gate.README.md +77 -0
  91. package/src/examples/lean-proof-gate.mts +162 -0
  92. package/src/examples/lean-verify.ts +95 -0
  93. package/src/examples/lean.Dockerfile +12 -0
  94. package/src/examples/math-demo.mts +9 -7
  95. package/src/examples/strategy-demo.mts +10 -12
  96. package/src/gate.ts +3 -2
  97. package/src/hev-eval.mts +69 -0
  98. package/src/hev-improve.mts +169 -0
  99. package/src/hev-structural.mts +688 -0
  100. package/src/index.ts +73 -0
  101. package/src/mbpp-structural.mts +662 -0
  102. package/src/pier-agent.test-fixtures.mts +19 -0
  103. package/src/pier-agent.test.mts +363 -0
  104. package/src/pier-agent.ts +657 -0
  105. package/src/pier-result-grader.mjs +30 -0
  106. package/src/pier-result-grader.test.mts +62 -0
  107. package/src/pier-result-grader.ts +108 -0
  108. package/src/pier-task-outcome.test.mts +117 -0
  109. package/src/pier-task-outcome.ts +240 -0
  110. package/src/pier-trial-controller.test.mts +412 -0
  111. package/src/pier-trial-controller.ts +858 -0
  112. package/src/pier-trial-supervisor.mjs +352 -0
  113. package/src/resolve-client.ts +25 -2
  114. package/src/run-benchmarks-cli.mts +72 -0
  115. package/src/run-benchmarks-report.ts +66 -0
  116. package/src/run-benchmarks.test.mts +231 -0
  117. package/src/run-benchmarks.ts +589 -0
  118. package/src/smoke-structural-rollout.mts +393 -0
  119. package/src/swe-bench-env.test.ts +207 -0
  120. package/src/swe-bench-env.ts +554 -0
  121. package/src/swe-jail.ts +293 -0
  122. package/src/swe-self-improve.mts +84 -0
  123. package/src/swe-structural-judge-policy.test.ts +117 -0
  124. package/src/swe-structural-judge-policy.ts +133 -0
  125. package/src/swe-structural-policy.test.ts +124 -0
  126. package/src/swe-structural-policy.ts +132 -0
  127. package/src/swe-structural-provenance.test.ts +93 -0
  128. package/src/swe-structural-provenance.ts +138 -0
  129. package/src/swe-structural.mts +1260 -0
  130. package/src/swe-temp.ts +14 -0
  131. package/src/tb-container-executor.mts +234 -0
  132. package/src/tb-container-executor.test.mts +99 -0
  133. package/src/tb-supervisor-sidecar.mts +222 -0
  134. package/src/trata-gepa.mts +1 -1
  135. package/steerers/eops-itsm-population.json +1 -0
  136. package/tb_agents/opencode_refine_agent.py +117 -0
  137. package/tb_agents/opencode_router_agent.py +406 -0
  138. package/tb_agents/opencode_supervisor_agent.py +239 -0
  139. package/tb_agents/script_agent.py +66 -0
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Manual reachability probe for the coordination MCP from inside a Docker container.
3
+ *
4
+ * Run: `npx tsx bench/src/coordination-mcp-container-reach.mts`.
5
+ */
6
+
7
+ import { execFile } from 'node:child_process'
8
+ import { promisify } from 'node:util'
9
+
10
+ const execFileAsync = promisify(execFile)
11
+ import {
12
+ type Agent,
13
+ type AgentProfile,
14
+ type AgentSpec,
15
+ createExecutorRegistry,
16
+ createSupervisor,
17
+ type Executor,
18
+ type ExecutorResult,
19
+ InMemoryResultBlobStore,
20
+ InMemorySpawnJournal,
21
+ type Scope,
22
+ type UsageEvent,
23
+ } from '../../src/runtime/index'
24
+ import { serveCoordinationMcp } from '../../src/runtime/supervise/coordination-mcp'
25
+
26
+ const HOST_BIND = '0.0.0.0'
27
+ const DOCKER_BRIDGE_GATEWAY = process.env.DOCKER_BRIDGE_GATEWAY ?? '172.17.0.1'
28
+
29
+ function trivialWorker(name: string): Agent<unknown, unknown> {
30
+ const ex: Executor<unknown> = {
31
+ runtime: 'router',
32
+ execute() {
33
+ return (async function* () {
34
+ yield { kind: 'iteration' } as UsageEvent
35
+ })()
36
+ },
37
+ teardown: () => Promise.resolve({ destroyed: true }),
38
+ resultArtifact: (): ExecutorResult<unknown> => ({
39
+ outRef: `w:${name}`,
40
+ out: { ok: true },
41
+ verdict: { valid: true, score: 1 },
42
+ spent: { iterations: 1, tokens: { input: 0, output: 0 }, usd: 0, ms: 0 },
43
+ }),
44
+ }
45
+ const spec: AgentSpec = { profile: { name } as AgentProfile, harness: null, executor: ex }
46
+ return { name, act: async () => ({ ok: true }), executorSpec: spec } as Agent<unknown, unknown> & {
47
+ executorSpec: AgentSpec
48
+ }
49
+ }
50
+
51
+ interface ToolsListResponse {
52
+ result?: { tools?: Array<{ name: string; description?: string; inputSchema?: unknown }> }
53
+ error?: { code: number; message: string }
54
+ }
55
+
56
+ async function dockerCurlToolsList(
57
+ containerUrl: string,
58
+ ): Promise<{ raw: string; parsed: ToolsListResponse; cmd: string }> {
59
+ const rpc = JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'tools/list' })
60
+ const args = [
61
+ 'run',
62
+ '--rm',
63
+ '--add-host',
64
+ 'host.docker.internal:host-gateway',
65
+ 'curlimages/curl',
66
+ '-s',
67
+ '-X',
68
+ 'POST',
69
+ containerUrl,
70
+ '-H',
71
+ 'content-type: application/json',
72
+ '-d',
73
+ rpc,
74
+ ]
75
+ const cmd = `docker ${args.join(' ')}`
76
+ const { stdout } = await execFileAsync('docker', args, { encoding: 'utf8', timeout: 120_000 })
77
+ return { raw: stdout, parsed: JSON.parse(stdout) as ToolsListResponse, cmd }
78
+ }
79
+
80
+ function opencodeConfigSnippet(containerUrl: string): string {
81
+ return JSON.stringify(
82
+ {
83
+ $schema: 'https://opencode.ai/config.json',
84
+ permission: {
85
+ external_directory: 'allow',
86
+ bash: 'allow',
87
+ edit: 'allow',
88
+ read: 'allow',
89
+ write: 'allow',
90
+ webfetch: 'allow',
91
+ task: 'allow',
92
+ plan_enter: 'allow',
93
+ plan_exit: 'allow',
94
+ question: 'allow',
95
+ },
96
+ mcp: {
97
+ coordination: {
98
+ type: 'remote',
99
+ url: containerUrl,
100
+ enabled: true,
101
+ },
102
+ },
103
+ },
104
+ null,
105
+ 2,
106
+ )
107
+ }
108
+
109
+ async function main(): Promise<void> {
110
+ const blobs = new InMemoryResultBlobStore()
111
+ let ok = false
112
+
113
+ const root: Agent<unknown, unknown> = {
114
+ name: 'coordination-mcp-container-reach',
115
+ async act(_t, scope: Scope<unknown>) {
116
+ const mcp = await serveCoordinationMcp({
117
+ scope,
118
+ blobs,
119
+ makeWorkerAgent: () => trivialWorker('w'),
120
+ perWorker: { maxIterations: 4, maxTokens: 2000 },
121
+ host: HOST_BIND,
122
+ })
123
+ // Docker containers reach the host through the bridge gateway, not the 0.0.0.0 bind URL.
124
+ const containerUrl = `http://${DOCKER_BRIDGE_GATEWAY}:${mcp.port}/mcp`
125
+ const hostDockerInternalUrl = `http://host.docker.internal:${mcp.port}/mcp`
126
+
127
+ console.error(`[probe] host-binding used : ${HOST_BIND}`)
128
+ console.error(`[probe] server url (mcp.url) : ${mcp.url}`)
129
+ console.error(`[probe] container-reachable url : ${containerUrl}`)
130
+ console.error(`[probe] alt (host.docker.internal) : ${hostDockerInternalUrl}`)
131
+ console.error('')
132
+
133
+ try {
134
+ console.error('[probe] POSTing tools/list from inside a docker container ...')
135
+ const { raw, parsed, cmd } = await dockerCurlToolsList(containerUrl)
136
+ console.error(`[probe] container ran: ${cmd}`)
137
+ console.error(`[probe] container got back (${raw.length} bytes):`)
138
+ console.error(raw)
139
+ console.error('')
140
+
141
+ const toolNames = (parsed.result?.tools ?? []).map((t) => t.name)
142
+ const hasSpawn = toolNames.includes('spawn_agent')
143
+ const hasAwait = toolNames.includes('await_event')
144
+ console.error(`[probe] tools advertised: ${toolNames.join(', ')}`)
145
+ console.error(`[probe] spawn_agent present = ${hasSpawn}; await_event present = ${hasAwait}`)
146
+ ok = hasSpawn && hasAwait
147
+
148
+ if (ok) {
149
+ console.error('')
150
+ console.error('[probe] opencode.json snippet (arm B writes this via OPENCODE_CONFIG):')
151
+ console.error(opencodeConfigSnippet(containerUrl))
152
+ }
153
+ return ok ? { reachable: true } : undefined
154
+ } finally {
155
+ await mcp.close()
156
+ }
157
+ },
158
+ }
159
+
160
+ await createSupervisor<unknown, unknown>().run(root, 'reach', {
161
+ budget: { maxIterations: 100, maxTokens: 400_000 },
162
+ runId: 'coordination-mcp-container-reach',
163
+ journal: new InMemorySpawnJournal(),
164
+ blobs,
165
+ executors: createExecutorRegistry(),
166
+ maxDepth: 4,
167
+ now: () => Date.now(),
168
+ })
169
+
170
+ console.error(
171
+ ok
172
+ ? 'CONTAINER-REACHABLE: docker tools/list returned spawn_agent and await_event.'
173
+ : 'NOT reachable from container; see output above.',
174
+ )
175
+ process.exit(ok ? 0 : 1)
176
+ }
177
+
178
+ main().catch((e) => {
179
+ console.error(e instanceof Error ? (e.stack ?? e.message) : String(e))
180
+ process.exit(1)
181
+ })
@@ -47,7 +47,7 @@ async function main(): Promise<number> {
47
47
  },
48
48
  profile: { name: 'decoder-live' },
49
49
  },
50
- } as never)) as Record<string, (...a: never[]) => unknown> & { id?: string }
50
+ } as never)) as unknown as Record<string, (...a: never[]) => unknown> & { id?: string }
51
51
 
52
52
  try {
53
53
  console.error('[live] box', box.id, '— waiting for running…')
@@ -1,58 +1,122 @@
1
- # Examplesthe optimization suite in three layers
1
+ # Bench examples make an AI actually earn its answer
2
2
 
3
- `strategy-demo.mts` is the smallest end-to-end demonstration of the optimization suite.
4
- It runs on a toy "counter" `Environment` so it needs only a router key no benchmark
5
- dataset, no sandbox, no gym.
3
+ Four runnable programs that share one idea: never trust an AI's answer on its word — put a **check**
4
+ in front of it and let the AI keep trying until the check passes. A check is just a function that
5
+ returns pass/fail (grade a number, run a test suite, compile a proof). Give the AI a check and a
6
+ compute budget and this toolkit finds the best way to spend that budget: try many answers and keep
7
+ the best, or let a critic read the failure and steer the next attempt.
8
+
9
+ All four run from the `bench/` directory (`cd bench` first). Nothing here needs a special framework —
10
+ each is a single file you can read top to bottom.
11
+
12
+ ## Which one to run first
13
+
14
+ | run this | what you learn | needs a key? |
15
+ |---|---|---|
16
+ | **`strategy-demo.mts`** | the whole idea on a 20-line toy task | yes (one router key) |
17
+ | **`math-demo.mts`** | the same thing grading real answers (word problems) | yes |
18
+ | **`lean-proof-gate.mts`** | a check the AI literally cannot fool (a theorem prover) | no — offline mode |
19
+ | **`benchmark-matrix.mts`** | rank many AI agents against public benchmarks | no — offline demo |
20
+
21
+ ---
22
+
23
+ ## `strategy-demo.mts` — the whole idea in one small file
24
+
25
+ The smallest end-to-end demo. The task is a toy: drive a counter to exactly 5 using an `increment`
26
+ tool. Because the task is trivial, it needs only a router key — no dataset, no cloud sandbox — so you
27
+ can watch the machinery instead of the problem.
28
+
29
+ It shows three ways to use the toolkit, each a few lines:
30
+
31
+ 1. **Just run it.** Hand it your task and your check; it compares the built-in ways of spending the
32
+ budget and reports which won.
33
+ 2. **Pick the tactics.** Choose from `sample` (make N independent attempts, keep the best-scoring
34
+ one), `refine` (attempt, let a critic read what went wrong, steer the next attempt, repeat), and
35
+ `adaptiveRefine` (refine, but abandon and restart a line that stops improving).
36
+ 3. **Write your own.** `defineStrategy(name, body)` composes two moves — `shot()` (one worker
37
+ attempt) and `critique()` (a separate reviewer reads the transcript and returns a steer) — into
38
+ any tactic you invent. The demo authors one called `doubleCheck` in ~10 lines.
39
+
40
+ ```bash
41
+ TANGLE_API_KEY=... WORKER_MODEL=gpt-4o-mini tsx src/examples/strategy-demo.mts
42
+ ```
43
+
44
+ Prints two leaderboards: the built-in tactics, then the built-ins plus your authored `doubleCheck`,
45
+ each scored by the counter task's own check. Default model is `deepseek-v4-flash`; override with
46
+ `WORKER_MODEL`.
47
+
48
+ ## `math-demo.mts` — the same toolkit grading real answers
49
+
50
+ Identical machinery, but the check now grades **answers**: three grade-school word problems, scored
51
+ by exact numeric match against the known answer. The worker gets a `calculator` tool and must submit
52
+ a final number. This is the template for any domain where success means "did the final answer come
53
+ out right" — a tax return value, a computed total, a rubric score. To move it to your domain you swap
54
+ one function: the check.
6
55
 
7
56
  ```bash
8
- TANGLE_API_KEY=... WORKER_MODEL=gpt-4o-mini pnpm tsx src/examples/strategy-demo.mts
57
+ TANGLE_API_KEY=... WORKER_MODEL=gpt-4o-mini BUDGET=3 tsx src/examples/math-demo.mts
9
58
  ```
10
59
 
11
- ## The model
60
+ Compares `sample`, `refine`, and `sampleThenRefine` (N fresh attempts, then critique-and-iterate on
61
+ the best) at an equal budget of `BUDGET` attempts each, so you can see whether iterating on feedback
62
+ actually beats blind resampling. Needs `TANGLE_API_KEY`; default model `deepseek-v4-flash`.
12
63
 
13
- You have a **task**, a deployable **check**, and a compute **budget**. A *strategy* is
14
- **how you spend the budget to beat the check**. You implement an `Environment` (5 hooks)
15
- and get the strategies compared, scored by your own check, for free.
64
+ ## `lean-proof-gate.mts` a check the AI cannot fool
16
65
 
17
- ## The three layers (each is a few lines in the demo)
66
+ An AI writes a mathematical proof and **Lean 4** (a real theorem prover) compiles it. A wrong proof
67
+ is rejected with a precise error the AI reads and fixes; the loop repeats until Lean accepts a
68
+ genuinely correct proof. This is the most honest check in the folder: correctness is machine-verified,
69
+ not judged by another model. It has its own full walkthrough in
70
+ [`lean-proof-gate.README.md`](./lean-proof-gate.README.md).
18
71
 
19
- 1. **Just run it** — `runBenchmark({ environment, tasks, worker })` compares the default
20
- strategies and reports the paired lift. Black box; no vocabulary needed.
72
+ ```bash
73
+ # See the checker work no API key (builds a Lean image, first run ~3 min):
74
+ tsx src/examples/lean-proof-gate.mts --verify-only
21
75
 
22
- 2. **Pick strategies** pass `strategies: [sample, refine, adaptiveRefine]`. Named by
23
- what they *do*:
24
- - **`sample`** — N independent attempts, keep the best-verifying (best-of-N / resample).
25
- - **`refine`** — attempt → a critic reads the trace → steer the next → repeat (iterate).
26
- - **`adaptiveRefine`** — refine, but abandon-and-restart a line that stops improving
27
- (branch-when-stuck).
76
+ # Run the full write-check-retry loop needs a model:
77
+ TANGLE_API_KEY=... WORKER_MODEL=gpt-4.1 BUDGET=3 tsx src/examples/lean-proof-gate.mts
78
+ ```
28
79
 
29
- 3. **Author your own** `defineStrategy(name, body)`. A strategy body composes two steps
30
- `shot()` (one worker attempt over an artifact) and `critique()` (the firewalled
31
- analyst reads the trace → a steer) — with **zero** Supervisor/Scope ceremony. The demo
32
- authors `doubleCheck` inline in ~10 lines. This is the unit a skill (or an agent) emits.
80
+ The `--verify-only` mode compiles five real proofs plus one deliberately wrong one and shows the
81
+ wrong one getting rejected proof the check is real before you spend a cent on a model.
33
82
 
34
- ## The answer-shaped template — `math-demo.mts`
83
+ ## `benchmark-matrix.mts` — rank many agents across many benchmarks
35
84
 
36
- `math-demo.mts` is the same suite on the **answer-shaped** domain template the shape
37
- tax/legal/gtm products use. `createVerifierEnvironment({ name, check, extraTools,
38
- callExtra })` builds the whole `Environment` from one deterministic `check` (here: 3
39
- GSM8K-style problems, graded by exact numeric match), and `sampleThenRefine` joins the
40
- built-ins compared at equal budget.
85
+ The other three grade one task domain. This one builds a **leaderboard**: it runs a set of AI agents
86
+ against a set of public coding/research benchmarks (HumanEval, SWE-bench, terminal-bench, and more)
87
+ and ranks them. Each benchmark brings its **own** grader, so the score is the benchmark's, not a
88
+ number we made up. An "agent" here is a **cell** — a combination of coding harness (opencode, Codex,
89
+ a bare router call) and model (e.g. `glm-4.6`, `gpt-5`).
41
90
 
42
91
  ```bash
43
- TANGLE_API_KEY=... WORKER_MODEL=gpt-4o-mini pnpm tsx src/examples/math-demo.mts
92
+ # Offline demo no keys. A stub benchmark with a deterministic grader proves the
93
+ # whole pipeline and prints a ranked board:
94
+ tsx src/examples/benchmark-matrix.mts
95
+
96
+ # Live — real benchmarks graded by their real graders:
97
+ TANGLE_API_KEY=... BENCHMARKS=humaneval tsx src/examples/benchmark-matrix.mts
44
98
  ```
45
99
 
46
- ## The hooks you customize (world-class-DX surface)
100
+ Pick the benchmarks with the `BENCHMARKS` env var (comma-separated). The **cells** (which
101
+ harness+model agents to rank) are the `cells` array at the top of the file — edit that array to
102
+ change who competes. Other knobs: `N` (tasks per benchmark, default 10), `CONCURRENCY` (default 4),
103
+ and `SANDBOX_BASE` if your harnesses run in a remote sandbox.
104
+
105
+ ---
106
+
107
+ ## Where the interesting results are
108
+
109
+ On the toy and math tasks the tactics often tie — the task is too easy for the way-you-spend-budget
110
+ to matter. The gap shows up on hard, stateful work: on an internal operations benchmark, `refine`
111
+ (iterate on a critic's feedback) beats blind `sample` by **+16.4 percentage points**. See
112
+ `bench/HARNESS.md` and `bench/src/agentic-run.mts` for that harder environment.
47
113
 
48
- - **the check / verifier** → `Environment.score` (your deployable success criterion)
49
- - **the critic / steerer** → `worker.analystInstruction` (the analyst prompt; GEPA tunes this)
50
- - **the worker** → the model (`worker.model`)
51
- - **the strategy** → `defineStrategy` (or drop to `runAgentic` / the Supervisor for novel topologies)
114
+ ## The four hooks you customize
52
115
 
53
- ## Where the real results live
116
+ Everything above is built from four swappable pieces. To adapt any of these examples to your problem,
117
+ you change one of these and leave the rest alone:
54
118
 
55
- On a trivial task all strategies tie. The differences (e.g. refine/adaptiveRefine beating
56
- sample on stateful agentic work, +16.4pp on EnterpriseOps-Gym) show on real domains see
57
- `bench/HARNESS.md` and `bench/src/agentic-run.mts` (the EOPS Environment), and
58
- `bench/src/eops-gepa.mts` (GEPA evolving the analyst/critic prompt against the check).
119
+ - **the check** your pass/fail function (grade the answer, run the tests, compile the proof).
120
+ - **the reviewer** the prompt the critic uses to read a failed attempt and suggest a fix.
121
+ - **the worker** the model that does the work (`WORKER_MODEL`).
122
+ - **the tactic** — how the budget is spent (`sample` / `refine` / your own `defineStrategy`).
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Rank a set of AI agents across a set of public benchmarks, and print a leaderboard.
3
+ *
4
+ * A "cell" is one agent = a coding harness (opencode, Codex, a bare router call) + a model.
5
+ * Each benchmark (humaneval, swe-bench, terminal-bench, …) brings its OWN deterministic
6
+ * grader, so the score is the benchmark's, not one we invented. Pick the benchmarks with
7
+ * the BENCHMARKS env var; edit the `cells` array below to change who competes.
8
+ *
9
+ * Run from bench/:
10
+ * # offline demo (no creds): a stub benchmark + stub agent prove the pipeline end to end
11
+ * tsx src/examples/benchmark-matrix.mts
12
+ *
13
+ * # live: real benchmarks graded by their real graders
14
+ * TANGLE_API_KEY=... BENCHMARKS=humaneval tsx src/examples/benchmark-matrix.mts
15
+ */
16
+ import type { BenchmarkAdapter, BenchScore, BenchTask } from '../benchmarks/types'
17
+ import { type BenchCell, type BenchShot, printBenchmarksReport, runBenchmarks } from '../run-benchmarks'
18
+
19
+ /** The matrix: three cells spanning harnesses, models, and transports. */
20
+ const cells: BenchCell[] = [
21
+ { label: 'opencode/glm-4.6', harness: 'opencode', model: 'glm-4.6', backend: 'sandbox' },
22
+ { label: 'codex/gpt-5', harness: 'codex', model: 'gpt-5', backend: 'sandbox' },
23
+ { label: 'router/deepseek-v4-flash', model: 'deepseek-v4-flash', backend: 'router' },
24
+ ]
25
+
26
+ async function live(): Promise<void> {
27
+ const benchmarks = (process.env.BENCHMARKS ?? 'humaneval')
28
+ .split(',')
29
+ .map((s) => s.trim())
30
+ .filter(Boolean)
31
+ const report = await runBenchmarks({
32
+ benchmarks,
33
+ cells,
34
+ routerBaseUrl: process.env.ROUTER_BASE ?? 'https://router.tangle.tools/v1',
35
+ routerKey: process.env.TANGLE_API_KEY!,
36
+ ...(process.env.SANDBOX_BASE ? { sandboxBaseUrl: process.env.SANDBOX_BASE } : {}),
37
+ n: Number(process.env.N ?? 10),
38
+ concurrency: Number(process.env.CONCURRENCY ?? 4),
39
+ onResult: (r) =>
40
+ console.log(` [${r.ok ? (r.resolved ? 'PASS' : 'fail') : 'ERR '}] ${r.benchmark} ${r.cell} ${r.taskId}`),
41
+ })
42
+ console.log(`\n=== live: ${report.benchmarks.join(', ')} × ${report.cells.length} cells ===`)
43
+ console.log(printBenchmarksReport(report))
44
+ }
45
+
46
+ /** Offline proof: a stub registry whose judge is deterministic, and a stub shot whose "ability"
47
+ * differs per cell — so the leaderboard ORDER reflects the cells, with no model or sandbox. */
48
+ async function demo(): Promise<void> {
49
+ const stub = (name: string, n: number): BenchmarkAdapter => ({
50
+ name,
51
+ preflight: async () => {},
52
+ loadTasks: async (o) =>
53
+ Array.from({ length: o?.limit ?? n }, (_, i) => ({
54
+ id: `${name}-${i}`,
55
+ prompt: `task ${i}`,
56
+ metadata: { gold: `G-${name}-${i}` },
57
+ })),
58
+ judge: async (t: BenchTask, a: string): Promise<BenchScore> => ({
59
+ resolved: a === t.metadata?.gold,
60
+ score: a === t.metadata?.gold ? 1 : 0,
61
+ }),
62
+ goldArtifact: async (t: BenchTask) => String(t.metadata?.gold),
63
+ })
64
+ const registry: Record<string, BenchmarkAdapter> = {
65
+ 'demo-coding': stub('demo-coding', 8),
66
+ 'demo-research': stub('demo-research', 8),
67
+ }
68
+
69
+ // Cell ability: opencode/glm solves the most, router the least — deterministic by task index.
70
+ const ability: Record<string, number> = {
71
+ 'opencode/glm-4.6': 0.88,
72
+ 'codex/gpt-5': 0.62,
73
+ 'router/deepseek-v4-flash': 0.38,
74
+ }
75
+ const shot: BenchShot = async ({ task, cell }) => {
76
+ const idx = Number(task.id.split('-').pop())
77
+ const solves = idx / 8 < (ability[cell.label] ?? 0.5)
78
+ return { artifact: solves ? String(task.metadata?.gold) : 'WRONG', ok: true }
79
+ }
80
+
81
+ const report = await runBenchmarks({
82
+ benchmarks: ['demo-coding', 'demo-research'],
83
+ cells,
84
+ routerBaseUrl: 'demo',
85
+ routerKey: 'demo',
86
+ runShot: shot,
87
+ resolveAdapter: (k) => {
88
+ const a = registry[k]
89
+ if (!a) throw new Error(`unknown: ${k}`)
90
+ return a
91
+ },
92
+ })
93
+ console.log('=== offline demo: 2 benchmarks × 3 cells (stub judge, no creds) ===')
94
+ console.log(printBenchmarksReport(report))
95
+ console.log('\nSet TANGLE_API_KEY + BENCHMARKS to run real benchmarks against these cells.')
96
+ }
97
+
98
+ void (process.env.TANGLE_API_KEY ? live() : demo()).catch((e) => {
99
+ console.error(e instanceof Error ? (e.stack ?? e.message) : String(e))
100
+ process.exit(1)
101
+ })
@@ -0,0 +1,77 @@
1
+ # Prove math with an AI that can't bluff
2
+
3
+ An AI writes a mathematical proof; a real theorem prover — **Lean 4** — checks it. If the proof is
4
+ wrong, Lean says exactly why, the AI reads that error and tries again, and it repeats until Lean
5
+ accepts a genuinely correct proof. Nothing is taken on the model's word: every proof that passes
6
+ here is machine-verified.
7
+
8
+ This is the automatable core of [pipeline-math](https://github.com/Pengbinghui/pipeline-math), which
9
+ used the same prover-and-checker idea to crack open mathematics problems.
10
+
11
+ ## Why it matters
12
+
13
+ Ask a language model to "prove X" and it will hand you a confident, fluent, and often **wrong**
14
+ proof. Here it cannot get away with that. Lean compiles the proof and rejects anything that doesn't
15
+ actually follow — and we also reject `sorry`/`admit` (Lean's "trust me, skip this" placeholders). The
16
+ checker is ground truth, so looping against it turns a plausible-sounding guesser into a prover whose
17
+ every answer is verified.
18
+
19
+ ## How it works — you don't need a special framework
20
+
21
+ A "prover-verifier loop" sounds exotic; it's two ordinary parts:
22
+
23
+ 1. a **checker** — a function that returns pass/fail. Here: run the Lean compiler on the proof.
24
+ 2. a **retry strategy** — show the model its failure and let it try again (`refine`, below).
25
+
26
+ Point the strategy at the checker and you have the loop. Swap Lean for unit tests and the same setup
27
+ proves *code*; swap it for a rubric and it writes *to spec*.
28
+
29
+ Two retries actually stack here: **within one attempt** the model calls a `lean_check` tool (the real
30
+ compiler) to fix its proof before answering; **across attempts** `refine` carries the last failure
31
+ forward. `sample` is the honest baseline — one blind attempt, no feedback.
32
+
33
+ ## See the checker work — no API key needed
34
+
35
+ ```bash
36
+ tsx src/examples/lean-proof-gate.mts --verify-only
37
+ ```
38
+
39
+ This builds a Lean 4 image (first run ~3 min) and compiles five real proofs plus one deliberately
40
+ wrong one:
41
+
42
+ ```
43
+ PASS and-swap (⟨h.2, h.1⟩)
44
+ PASS or-swap (h.symm)
45
+ PASS add-comm (by omega)
46
+ PASS mul-one (by simp)
47
+ PASS reverse-reverse (by simp)
48
+ negative control (deliberately wrong): FAIL ✓ rejected
49
+ verifier: 5/5 reference proofs accepted, wrong proof rejected=true
50
+ ```
51
+
52
+ The wrong proof fails with a real Lean error — `argument h.left has type p but is expected to have
53
+ type q` — which is exactly the message the AI reads and fixes.
54
+
55
+ ## Run the full loop — needs a model
56
+
57
+ ```bash
58
+ TANGLE_API_KEY=… WORKER_MODEL=gpt-4.1 BUDGET=3 tsx src/examples/lean-proof-gate.mts
59
+ ```
60
+
61
+ A real model proves the theorems, calling Lean to check each attempt. The report compares one blind
62
+ attempt (`sample`) against iterating on the checker's feedback (`refine`), so you can see whether the
63
+ loop actually earns its cost.
64
+
65
+ ## Files
66
+
67
+ | file | what it is |
68
+ |---|---|
69
+ | `lean-proof-gate.mts` | the theorems, the checker wiring, and the run |
70
+ | `lean-verify.ts` | the checker: compile in Docker, reject `sorry`/`admit`, run with no network so a proof can't fetch its way to a pass |
71
+ | `lean.Dockerfile` | the Lean 4 toolchain image |
72
+
73
+ ## Honest scope
74
+
75
+ The **checker** is real and runs on your machine today (it needs Docker). The **prover** half needs a
76
+ model key for the live loop. The theorems are small and use only core Lean (no `mathlib`) so they
77
+ compile in seconds — the point is the loop and the un-foolable check, not the difficulty of the math.