@tangle-network/agent-bench 0.1.0 → 0.3.6

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 (317) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/HARNESS.md +302 -0
  3. package/README.md +39 -2
  4. package/dist/adapters.d.ts +15 -0
  5. package/dist/adapters.js +43 -0
  6. package/dist/adapters.js.map +1 -0
  7. package/dist/benchmarks/_harness.d.ts +125 -0
  8. package/dist/benchmarks/_harness.js +33 -0
  9. package/dist/benchmarks/_harness.js.map +1 -0
  10. package/dist/benchmarks/aec-bench.d.ts +27 -0
  11. package/dist/benchmarks/aec-bench.js +8 -0
  12. package/dist/benchmarks/aec-bench.js.map +1 -0
  13. package/dist/benchmarks/agentbench.d.ts +16 -0
  14. package/dist/benchmarks/agentbench.js +10 -0
  15. package/dist/benchmarks/agentbench.js.map +1 -0
  16. package/dist/benchmarks/appworld.d.ts +37 -0
  17. package/dist/benchmarks/appworld.js +14 -0
  18. package/dist/benchmarks/appworld.js.map +1 -0
  19. package/dist/benchmarks/bfcl.d.ts +18 -0
  20. package/dist/benchmarks/bfcl.js +10 -0
  21. package/dist/benchmarks/bfcl.js.map +1 -0
  22. package/dist/benchmarks/cad-design.d.ts +45 -0
  23. package/dist/benchmarks/cad-design.js +7 -0
  24. package/dist/benchmarks/cad-design.js.map +1 -0
  25. package/dist/benchmarks/cadbench.d.ts +19 -0
  26. package/dist/benchmarks/cadbench.js +8 -0
  27. package/dist/benchmarks/cadbench.js.map +1 -0
  28. package/dist/benchmarks/cadgenbench.d.ts +22 -0
  29. package/dist/benchmarks/cadgenbench.js +8 -0
  30. package/dist/benchmarks/cadgenbench.js.map +1 -0
  31. package/dist/benchmarks/commit0.d.ts +31 -0
  32. package/dist/benchmarks/commit0.js +10 -0
  33. package/dist/benchmarks/commit0.js.map +1 -0
  34. package/dist/benchmarks/crag.d.ts +14 -0
  35. package/dist/benchmarks/crag.js +9 -0
  36. package/dist/benchmarks/crag.js.map +1 -0
  37. package/dist/benchmarks/dabstep.d.ts +18 -0
  38. package/dist/benchmarks/dabstep.js +10 -0
  39. package/dist/benchmarks/dabstep.js.map +1 -0
  40. package/dist/benchmarks/enterpriseops-gym.d.ts +38 -0
  41. package/dist/benchmarks/enterpriseops-gym.js +10 -0
  42. package/dist/benchmarks/enterpriseops-gym.js.map +1 -0
  43. package/dist/benchmarks/finresearchbench.d.ts +15 -0
  44. package/dist/benchmarks/finresearchbench.js +8 -0
  45. package/dist/benchmarks/finresearchbench.js.map +1 -0
  46. package/dist/benchmarks/finsearchcomp.d.ts +49 -0
  47. package/dist/benchmarks/finsearchcomp.js +7 -0
  48. package/dist/benchmarks/finsearchcomp.js.map +1 -0
  49. package/dist/benchmarks/frames.d.ts +59 -0
  50. package/dist/benchmarks/frames.js +13 -0
  51. package/dist/benchmarks/frames.js.map +1 -0
  52. package/dist/benchmarks/hotpotqa.d.ts +48 -0
  53. package/dist/benchmarks/hotpotqa.js +15 -0
  54. package/dist/benchmarks/hotpotqa.js.map +1 -0
  55. package/dist/benchmarks/humaneval.d.ts +53 -0
  56. package/dist/benchmarks/humaneval.js +15 -0
  57. package/dist/benchmarks/humaneval.js.map +1 -0
  58. package/dist/benchmarks/mind2web.d.ts +41 -0
  59. package/dist/benchmarks/mind2web.js +9 -0
  60. package/dist/benchmarks/mind2web.js.map +1 -0
  61. package/dist/benchmarks/nomiracl.d.ts +15 -0
  62. package/dist/benchmarks/nomiracl.js +9 -0
  63. package/dist/benchmarks/nomiracl.js.map +1 -0
  64. package/dist/benchmarks/open-rag-bench.d.ts +14 -0
  65. package/dist/benchmarks/open-rag-bench.js +9 -0
  66. package/dist/benchmarks/open-rag-bench.js.map +1 -0
  67. package/dist/benchmarks/programbench.d.ts +38 -0
  68. package/dist/benchmarks/programbench.js +10 -0
  69. package/dist/benchmarks/programbench.js.map +1 -0
  70. package/dist/benchmarks/rag-shared.d.ts +42 -0
  71. package/dist/benchmarks/rag-shared.js +39 -0
  72. package/dist/benchmarks/rag-shared.js.map +1 -0
  73. package/dist/benchmarks/ragbench.d.ts +16 -0
  74. package/dist/benchmarks/ragbench.js +9 -0
  75. package/dist/benchmarks/ragbench.js.map +1 -0
  76. package/dist/benchmarks/simpleqa.d.ts +64 -0
  77. package/dist/benchmarks/simpleqa.js +11 -0
  78. package/dist/benchmarks/simpleqa.js.map +1 -0
  79. package/dist/benchmarks/swe-bench.d.ts +56 -0
  80. package/dist/benchmarks/swe-bench.js +14 -0
  81. package/dist/benchmarks/swe-bench.js.map +1 -0
  82. package/dist/benchmarks/t2-ragbench.d.ts +14 -0
  83. package/dist/benchmarks/t2-ragbench.js +9 -0
  84. package/dist/benchmarks/t2-ragbench.js.map +1 -0
  85. package/dist/benchmarks/tau-bench-shared.d.ts +26 -0
  86. package/dist/benchmarks/tau-bench-shared.js +10 -0
  87. package/dist/benchmarks/tau-bench-shared.js.map +1 -0
  88. package/dist/benchmarks/tau2-bench.d.ts +7 -0
  89. package/dist/benchmarks/tau2-bench.js +11 -0
  90. package/dist/benchmarks/tau2-bench.js.map +1 -0
  91. package/dist/benchmarks/tau3-banking.d.ts +15 -0
  92. package/dist/benchmarks/tau3-banking.js +9 -0
  93. package/dist/benchmarks/tau3-banking.js.map +1 -0
  94. package/dist/benchmarks/terminal-bench.d.ts +24 -0
  95. package/dist/benchmarks/terminal-bench.js +8 -0
  96. package/dist/benchmarks/terminal-bench.js.map +1 -0
  97. package/dist/benchmarks/toollm.d.ts +16 -0
  98. package/dist/benchmarks/toollm.js +10 -0
  99. package/dist/benchmarks/toollm.js.map +1 -0
  100. package/dist/benchmarks/trata-hedge.d.ts +32 -0
  101. package/dist/benchmarks/trata-hedge.js +7 -0
  102. package/dist/benchmarks/trata-hedge.js.map +1 -0
  103. package/dist/benchmarks/types.d.ts +107 -0
  104. package/dist/benchmarks/types.js +1 -0
  105. package/dist/benchmarks/types.js.map +1 -0
  106. package/dist/benchmarks/webarena-verified.d.ts +16 -0
  107. package/dist/benchmarks/webarena-verified.js +10 -0
  108. package/dist/benchmarks/webarena-verified.js.map +1 -0
  109. package/dist/chunk-2PVVP7GN.js +197 -0
  110. package/dist/chunk-2PVVP7GN.js.map +1 -0
  111. package/dist/chunk-2XU6OGEN.js +170 -0
  112. package/dist/chunk-2XU6OGEN.js.map +1 -0
  113. package/dist/chunk-53UPUNBZ.js +325 -0
  114. package/dist/chunk-53UPUNBZ.js.map +1 -0
  115. package/dist/chunk-5SBJCB6W.js +144 -0
  116. package/dist/chunk-5SBJCB6W.js.map +1 -0
  117. package/dist/chunk-7WSD27QQ.js +118 -0
  118. package/dist/chunk-7WSD27QQ.js.map +1 -0
  119. package/dist/chunk-C7T7WEK2.js +103 -0
  120. package/dist/chunk-C7T7WEK2.js.map +1 -0
  121. package/dist/chunk-CKUVRZ2T.js +251 -0
  122. package/dist/chunk-CKUVRZ2T.js.map +1 -0
  123. package/dist/chunk-HBSWHQNJ.js +30 -0
  124. package/dist/chunk-HBSWHQNJ.js.map +1 -0
  125. package/dist/chunk-HHXFIHXC.js +116 -0
  126. package/dist/chunk-HHXFIHXC.js.map +1 -0
  127. package/dist/chunk-IFAV6KEM.js +276 -0
  128. package/dist/chunk-IFAV6KEM.js.map +1 -0
  129. package/dist/chunk-INNOYXCP.js +387 -0
  130. package/dist/chunk-INNOYXCP.js.map +1 -0
  131. package/dist/chunk-J3KDJNX2.js +182 -0
  132. package/dist/chunk-J3KDJNX2.js.map +1 -0
  133. package/dist/chunk-JRWWGMK7.js +148 -0
  134. package/dist/chunk-JRWWGMK7.js.map +1 -0
  135. package/dist/chunk-JTHWEDEW.js +32 -0
  136. package/dist/chunk-JTHWEDEW.js.map +1 -0
  137. package/dist/chunk-KDIKRJGB.js +120 -0
  138. package/dist/chunk-KDIKRJGB.js.map +1 -0
  139. package/dist/chunk-LRRD7NAG.js +301 -0
  140. package/dist/chunk-LRRD7NAG.js.map +1 -0
  141. package/dist/chunk-ODT47UAY.js +221 -0
  142. package/dist/chunk-ODT47UAY.js.map +1 -0
  143. package/dist/chunk-PA2ZKHJC.js +230 -0
  144. package/dist/chunk-PA2ZKHJC.js.map +1 -0
  145. package/dist/chunk-PPYSEKFM.js +182 -0
  146. package/dist/chunk-PPYSEKFM.js.map +1 -0
  147. package/dist/chunk-PUIRNYI7.js +189 -0
  148. package/dist/chunk-PUIRNYI7.js.map +1 -0
  149. package/dist/chunk-R36V2VP7.js +169 -0
  150. package/dist/chunk-R36V2VP7.js.map +1 -0
  151. package/dist/chunk-R67DFVLO.js +142 -0
  152. package/dist/chunk-R67DFVLO.js.map +1 -0
  153. package/dist/chunk-SEVJPLZC.js +260 -0
  154. package/dist/chunk-SEVJPLZC.js.map +1 -0
  155. package/dist/chunk-SYDW647C.js +318 -0
  156. package/dist/chunk-SYDW647C.js.map +1 -0
  157. package/dist/chunk-TBKU5XQI.js +228 -0
  158. package/dist/chunk-TBKU5XQI.js.map +1 -0
  159. package/dist/chunk-TSWPNOYM.js +147 -0
  160. package/dist/chunk-TSWPNOYM.js.map +1 -0
  161. package/dist/chunk-UAIOHCUK.js +27 -0
  162. package/dist/chunk-UAIOHCUK.js.map +1 -0
  163. package/dist/chunk-UPAMRDX4.js +233 -0
  164. package/dist/chunk-UPAMRDX4.js.map +1 -0
  165. package/dist/chunk-VQRS7VUC.js +342 -0
  166. package/dist/chunk-VQRS7VUC.js.map +1 -0
  167. package/dist/chunk-X3BTXCJ4.js +262 -0
  168. package/dist/chunk-X3BTXCJ4.js.map +1 -0
  169. package/dist/chunk-X5YKXC6V.js +211 -0
  170. package/dist/chunk-X5YKXC6V.js.map +1 -0
  171. package/dist/chunk-Y6O2OCUO.js +130 -0
  172. package/dist/chunk-Y6O2OCUO.js.map +1 -0
  173. package/dist/chunk-YCGY7UIZ.js +208 -0
  174. package/dist/chunk-YCGY7UIZ.js.map +1 -0
  175. package/dist/chunk-Z7ML6L77.js +162 -0
  176. package/dist/chunk-Z7ML6L77.js.map +1 -0
  177. package/dist/chunk-ZEWMTR5M.js +136 -0
  178. package/dist/chunk-ZEWMTR5M.js.map +1 -0
  179. package/dist/index.d.ts +355 -0
  180. package/dist/index.js +1908 -0
  181. package/dist/index.js.map +1 -0
  182. package/fixtures/aec-bench.json +18 -0
  183. package/fixtures/agentbench-dbbench.json +22 -0
  184. package/fixtures/bfcl.json +45 -0
  185. package/fixtures/commit0.json +72 -0
  186. package/fixtures/crag.json +10 -0
  187. package/fixtures/dabstep.json +22 -0
  188. package/fixtures/enterpriseops-gym.json +103 -0
  189. package/fixtures/finresearchbench.json +21 -0
  190. package/fixtures/finsearchcomp.json +66 -0
  191. package/fixtures/frames.json +26 -0
  192. package/fixtures/hotpotqa.json +182 -0
  193. package/fixtures/nomiracl.json +26 -0
  194. package/fixtures/open-rag-bench.json +16 -0
  195. package/fixtures/pier-agent/no-model-task/environment/Dockerfile +16 -0
  196. package/fixtures/pier-agent/no-model-task/environment/seed/src/status.txt +1 -0
  197. package/fixtures/pier-agent/no-model-task/instruction.md +6 -0
  198. package/fixtures/pier-agent/no-model-task/pre_artifacts.sh +6 -0
  199. package/fixtures/pier-agent/no-model-task/task.toml +35 -0
  200. package/fixtures/pier-agent/no-model-task/tests/Dockerfile +17 -0
  201. package/fixtures/pier-agent/no-model-task/tests/seed/src/status.txt +1 -0
  202. package/fixtures/pier-agent/no-model-task/tests/test.sh +19 -0
  203. package/fixtures/programbench.json +17 -0
  204. package/fixtures/ragbench.json +21 -0
  205. package/fixtures/simpleqa.json +121 -0
  206. package/fixtures/t2-ragbench.json +13 -0
  207. package/fixtures/tau2-bench.json +16 -0
  208. package/fixtures/tau3-banking.json +16 -0
  209. package/fixtures/toollm.json +28 -0
  210. package/fixtures/webarena-verified.json +20 -0
  211. package/package.json +60 -20
  212. package/pier_agents/__init__.py +18 -0
  213. package/pier_agents/candidate_contract.py +755 -0
  214. package/pier_agents/process_boundary.py +321 -0
  215. package/pier_agents/tangle_candidate.py +907 -0
  216. package/pier_agents/workspace_boundary.py +368 -0
  217. package/scripts/appworld_driver.py +359 -0
  218. package/scripts/cadbench_prepare.py +22 -0
  219. package/scripts/cadgenbench_hard_parts.py +48 -0
  220. package/scripts/clbench_codebase_judge.py +73 -0
  221. package/scripts/commit0_judge.py +170 -0
  222. package/scripts/dabstep_judge.py +42 -0
  223. package/scripts/enterpriseops_gym_judge.py +281 -0
  224. package/scripts/programbench_judge.py +120 -0
  225. package/scripts/render-gate-chart.mjs +176 -0
  226. package/scripts/run-package-tests.mjs +56 -0
  227. package/scripts/terminate-pier-trial.mts +66 -0
  228. package/scripts/trata-hedge/README.md +56 -0
  229. package/scripts/trata-hedge/run.sh +60 -0
  230. package/scripts/trata-hedge/solve.py +83 -0
  231. package/scripts/verify-packed-consumer.mjs +235 -0
  232. package/scripts/verify-pier-agent.mts +715 -0
  233. package/scripts/verify-pier-pair.mts +74 -0
  234. package/scripts/verify-pier-recovery.mts +139 -0
  235. package/src/adapters.ts +26 -0
  236. package/src/benchmarks/_harness.test.mts +178 -0
  237. package/src/benchmarks/_harness.ts +239 -16
  238. package/src/benchmarks/agentbench.ts +163 -0
  239. package/src/benchmarks/appworld.test.mts +15 -9
  240. package/src/benchmarks/bfcl.ts +346 -0
  241. package/src/benchmarks/crag.ts +137 -0
  242. package/src/benchmarks/dabstep.test.mts +70 -0
  243. package/src/benchmarks/dabstep.ts +212 -0
  244. package/src/benchmarks/external-adapters.test.mts +150 -0
  245. package/src/benchmarks/finresearchbench.ts +269 -0
  246. package/src/benchmarks/humaneval.ts +20 -8
  247. package/src/benchmarks/nomiracl.ts +180 -0
  248. package/src/benchmarks/open-rag-bench.ts +153 -0
  249. package/src/benchmarks/rag-benchmarks.test.mts +138 -0
  250. package/src/benchmarks/rag-shared.ts +327 -0
  251. package/src/benchmarks/ragbench.ts +171 -0
  252. package/src/benchmarks/swe-bench.test.mts +61 -0
  253. package/src/benchmarks/swe-bench.ts +201 -19
  254. package/src/benchmarks/t2-ragbench.ts +166 -0
  255. package/src/benchmarks/tau-bench-shared.ts +214 -0
  256. package/src/benchmarks/tau2-bench.ts +30 -0
  257. package/src/benchmarks/tau3-banking.ts +29 -0
  258. package/src/benchmarks/terminal-bench.test.mts +33 -0
  259. package/src/benchmarks/terminal-bench.ts +23 -8
  260. package/src/benchmarks/toollm.ts +254 -0
  261. package/src/benchmarks/types.ts +42 -0
  262. package/src/benchmarks/webarena-verified.ts +200 -0
  263. package/src/commit0-prereqs.sh +0 -0
  264. package/src/coordination-mcp-container-reach.mts +181 -0
  265. package/src/decoder-live.mts +1 -1
  266. package/src/examples/README.md +103 -39
  267. package/src/examples/benchmark-matrix.mts +101 -0
  268. package/src/examples/lean-proof-gate.README.md +77 -0
  269. package/src/examples/lean-proof-gate.mts +162 -0
  270. package/src/examples/lean-verify.ts +95 -0
  271. package/src/examples/lean.Dockerfile +12 -0
  272. package/src/examples/math-demo.mts +9 -7
  273. package/src/examples/strategy-demo.mts +10 -12
  274. package/src/gate.ts +3 -2
  275. package/src/hev-eval.mts +69 -0
  276. package/src/hev-improve.mts +169 -0
  277. package/src/hev-structural.mts +688 -0
  278. package/src/index.ts +73 -0
  279. package/src/mbpp-structural.mts +662 -0
  280. package/src/pier-agent.test-fixtures.mts +19 -0
  281. package/src/pier-agent.test.mts +363 -0
  282. package/src/pier-agent.ts +657 -0
  283. package/src/pier-result-grader.mjs +30 -0
  284. package/src/pier-result-grader.test.mts +62 -0
  285. package/src/pier-result-grader.ts +108 -0
  286. package/src/pier-task-outcome.test.mts +117 -0
  287. package/src/pier-task-outcome.ts +240 -0
  288. package/src/pier-trial-controller.test.mts +412 -0
  289. package/src/pier-trial-controller.ts +858 -0
  290. package/src/pier-trial-supervisor.mjs +352 -0
  291. package/src/resolve-client.ts +25 -2
  292. package/src/run-benchmarks-cli.mts +72 -0
  293. package/src/run-benchmarks-report.ts +66 -0
  294. package/src/run-benchmarks.test.mts +231 -0
  295. package/src/run-benchmarks.ts +589 -0
  296. package/src/smoke-structural-rollout.mts +393 -0
  297. package/src/swe-bench-env.test.ts +207 -0
  298. package/src/swe-bench-env.ts +554 -0
  299. package/src/swe-jail.ts +293 -0
  300. package/src/swe-self-improve.mts +84 -0
  301. package/src/swe-structural-judge-policy.test.ts +117 -0
  302. package/src/swe-structural-judge-policy.ts +133 -0
  303. package/src/swe-structural-policy.test.ts +124 -0
  304. package/src/swe-structural-policy.ts +132 -0
  305. package/src/swe-structural-provenance.test.ts +93 -0
  306. package/src/swe-structural-provenance.ts +138 -0
  307. package/src/swe-structural.mts +1260 -0
  308. package/src/swe-temp.ts +14 -0
  309. package/src/tb-container-executor.mts +234 -0
  310. package/src/tb-container-executor.test.mts +99 -0
  311. package/src/tb-supervisor-sidecar.mts +222 -0
  312. package/src/trata-gepa.mts +1 -1
  313. package/steerers/eops-itsm-population.json +1 -0
  314. package/tb_agents/opencode_refine_agent.py +117 -0
  315. package/tb_agents/opencode_router_agent.py +406 -0
  316. package/tb_agents/opencode_supervisor_agent.py +239 -0
  317. package/tb_agents/script_agent.py +66 -0
@@ -0,0 +1,29 @@
1
+ /**
2
+ * tau3-banking adapter.
3
+ *
4
+ * The current upstream tau3 release is the `sierra-research/tau2-bench` repo with
5
+ * package namespace `tau2` and a new `banking_knowledge` domain. This adapter
6
+ * reuses the shared tau reward recomputation path and only changes the default
7
+ * domain/env names.
8
+ */
9
+
10
+ import { join } from 'node:path'
11
+ import { benchRoot } from './_harness'
12
+ import { createTauBenchAdapter } from './tau-bench-shared'
13
+ import type { BenchmarkAdapter } from './types'
14
+
15
+ const FIXTURES = join(benchRoot, 'fixtures', 'tau3-banking.json')
16
+
17
+ export function createTau3BankingAdapter(): BenchmarkAdapter {
18
+ return createTauBenchAdapter({
19
+ name: 'tau3-banking',
20
+ fixturePath: FIXTURES,
21
+ fixturesEnv: 'TAU3_FIXTURES',
22
+ dirEnv: 'TAU3_BENCH_DIR',
23
+ domainEnv: 'TAU3_DOMAIN',
24
+ defaultDomain: 'banking_knowledge',
25
+ taskIntro: 'Run this tau3 banking task in the official tau3 knowledge benchmark.',
26
+ installHint:
27
+ 'clone https://github.com/sierra-research/tau2-bench, run `uv sync --extra knowledge`, install/import it from bench/.venv, and set TAU3_BENCH_DIR=/path/to/tau2-bench.',
28
+ })
29
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Offline Terminal-Bench adapter test. Live judging needs the official
3
+ * terminal-bench harness + Docker, so this asserts the offline surfaces and the
4
+ * fail-loud preflight path. Run: npx tsx --test src/benchmarks/terminal-bench.test.mts
5
+ */
6
+ import assert from 'node:assert/strict'
7
+ import { test } from 'node:test'
8
+ import { createTerminalBenchAdapter } from './terminal-bench'
9
+
10
+ test('goldArtifact returns portable shell solutions and rejects missing ones', async () => {
11
+ const a = createTerminalBenchAdapter()
12
+ assert.equal(await a.goldArtifact({ id: 'with-solution', prompt: '', metadata: { solution: 'echo ok\n' } }), 'echo ok\n')
13
+ assert.equal(await a.goldArtifact({ id: 'without-solution', prompt: '', metadata: {} }), undefined)
14
+ })
15
+
16
+ test('preflight FAILS LOUD with isolated Terminal-Bench venv instructions when harness is absent', async () => {
17
+ const prev = process.env.TERMINAL_BENCH_VENV
18
+ process.env.TERMINAL_BENCH_VENV = '.venv-terminal-bench-does-not-exist'
19
+ try {
20
+ const a = createTerminalBenchAdapter()
21
+ await assert.rejects(a.preflight(), (e: Error) => {
22
+ assert.match(e.message, /pip install terminal-bench/)
23
+ assert.match(e.message, /ISOLATED venv/)
24
+ assert.match(e.message, /TERMINAL_BENCH_VENV/)
25
+ assert.match(e.message, /Docker daemon/)
26
+ return true
27
+ })
28
+ } finally {
29
+ if (prev === undefined) delete process.env.TERMINAL_BENCH_VENV
30
+ else process.env.TERMINAL_BENCH_VENV = prev
31
+ }
32
+ })
33
+
@@ -6,9 +6,10 @@
6
6
  * our ScriptAgent), then the task's own verifier scores the resulting state.
7
7
  * Fully deterministic — no LLM judge, no self-authored score.
8
8
  *
9
- * Requires: the bench `.venv` with `terminal-bench` installed + a running Docker
10
- * daemon (per-task images are built on first run). loadTasks caches the dataset
11
- * from the Terminal-Bench registry on first run.
9
+ * Requires: an isolated bench `.venv-terminal-bench` with `terminal-bench`
10
+ * installed + a running Docker daemon (per-task images are built on first run).
11
+ * Override with TERMINAL_BENCH_VENV. loadTasks caches the dataset from the
12
+ * Terminal-Bench registry on first run.
12
13
  *
13
14
  * Process/Docker/report plumbing is shared via ./_harness; this file owns the
14
15
  * Terminal-Bench-specific pieces: the Dataset enumeration, the ScriptAgent replay
@@ -24,11 +25,16 @@ import {
24
25
  runVenvPython,
25
26
  safeRunId,
26
27
  stageFile,
27
- venvBin,
28
+ venvPythonAt,
28
29
  } from './_harness'
29
30
  import type { BenchmarkAdapter, BenchScore, BenchTask, LoadOptions } from './types'
30
31
 
31
- const TB = venvBin('tb')
32
+ // Terminal-Bench imports LiteLLM/Pydantic-2 APIs, while AppWorld pins Pydantic 1.
33
+ // Keep it out of the shared bench .venv. Resolved at call-time so tests/runs can
34
+ // override the env without reloading this module.
35
+ const terminalBenchVenvDir = (): string => process.env.TERMINAL_BENCH_VENV ?? '.venv-terminal-bench'
36
+ const terminalBenchPython = (): string => venvPythonAt(terminalBenchVenvDir())
37
+ const terminalBenchBin = (): string => join(benchRoot, terminalBenchVenvDir(), 'bin', 'tb')
32
38
 
33
39
  // Pinned dataset: the 0.1.1 core set is patched for terminal-bench >=0.2.4 (the
34
40
  // installed CLI) and is the published launch task set. name==version is what `tb
@@ -90,7 +96,12 @@ for task_dir in ds:
90
96
  })
91
97
  print(json.dumps(out))
92
98
  `
93
- const stdout = await runVenvPython(script, [ids ? JSON.stringify(ids) : '', limit !== null ? String(limit) : ''])
99
+ const stdout = await runVenvPython(
100
+ script,
101
+ [ids ? JSON.stringify(ids) : '', limit !== null ? String(limit) : ''],
102
+ 0,
103
+ terminalBenchPython(),
104
+ )
94
105
  return JSON.parse(stdout) as TbTaskRow[]
95
106
  }
96
107
 
@@ -102,8 +113,12 @@ export function createTerminalBenchAdapter(): BenchmarkAdapter {
102
113
  await preflightVenvImports({
103
114
  modules: ['terminal_bench'],
104
115
  requireDocker: true,
116
+ python: terminalBenchPython(),
105
117
  fix:
106
- `Fix: (1) python3 -m venv bench/.venv && bench/.venv/bin/pip install terminal-bench ; ` +
118
+ `Fix: (1) python3 -m venv bench/${terminalBenchVenvDir()} && ` +
119
+ `bench/${terminalBenchVenvDir()}/bin/pip install terminal-bench ` +
120
+ `(an ISOLATED venv — Terminal-Bench/LiteLLM require Pydantic 2 while AppWorld pins Pydantic 1; ` +
121
+ `override the dir with TERMINAL_BENCH_VENV) ; ` +
107
122
  `(2) ensure the Docker daemon is running (the judge builds per-task images on first run). ` +
108
123
  `The ${DATASET_REF} dataset is cached from the Terminal-Bench registry on first loadTasks.`,
109
124
  })
@@ -146,7 +161,7 @@ export function createTerminalBenchAdapter(): BenchmarkAdapter {
146
161
  const runId = safeRunId('bench', `${task.id}-${Date.now()}`)
147
162
  return runStagedJudge({
148
163
  tmpPrefix: 'tbench-',
149
- bin: TB,
164
+ bin: terminalBenchBin(),
150
165
  cwd: () => benchRoot,
151
166
  async stage(dir) {
152
167
  await stageFile(join(dir, 'attempt.sh'), artifact)
@@ -0,0 +1,254 @@
1
+ /**
2
+ * ToolLLM/ToolBench adapter.
3
+ *
4
+ * ToolBench task loading is useful for breadth, but the official ToolEval pass
5
+ * rate evaluator is LLM-based and stochastic. This adapter therefore scores
6
+ * only ToolBench's deterministic API-selection labels (`relevant APIs`). It
7
+ * never records a full ToolEval pass-rate score.
8
+ */
9
+
10
+ import { readFile } from 'node:fs/promises'
11
+ import { join } from 'node:path'
12
+ import type { OutputAdapter } from '@tangle-network/agent-runtime/loops'
13
+ import { benchRoot } from './_harness'
14
+ import type { BenchmarkAdapter, BenchScore, BenchTask, LoadOptions } from './types'
15
+
16
+ const FIXTURES = join(benchRoot, 'fixtures', 'toollm.json')
17
+ const DEFAULT_QUERY_REL = join('data_example', 'instruction', 'G1_query.json')
18
+
19
+ interface ToolApi {
20
+ category_name?: string
21
+ tool_name: string
22
+ api_name: string
23
+ api_description?: string
24
+ required_parameters?: unknown[]
25
+ optional_parameters?: unknown[]
26
+ method?: string
27
+ }
28
+
29
+ interface ToolBenchRow {
30
+ query_id: number | string
31
+ query: string
32
+ api_list?: ToolApi[]
33
+ 'relevant APIs'?: Array<[string, string]>
34
+ }
35
+
36
+ interface ToolBenchMeta {
37
+ queryId: string
38
+ apiList: ToolApi[]
39
+ relevantApis: Array<[string, string]>
40
+ deterministicJudge: 'api-selection'
41
+ }
42
+
43
+ const toolbenchDir = (): string | undefined => process.env.TOOLBENCH_DIR
44
+ const queryFile = (dir: string): string => process.env.TOOLLM_QUERY_FILE ?? join(dir, DEFAULT_QUERY_REL)
45
+
46
+ export const toollmOutput: OutputAdapter<string> = {
47
+ parse(events) {
48
+ let text = ''
49
+ for (const ev of events) {
50
+ const d = (ev as { data?: Record<string, unknown> })?.data
51
+ const t = d?.finalText ?? d?.text ?? d?.result
52
+ if (typeof t === 'string' && t.length > 0) text = t
53
+ }
54
+ return text.trim()
55
+ },
56
+ }
57
+
58
+ function rowToTask(row: ToolBenchRow): BenchTask {
59
+ const relevantApis = normalizeApiPairs(row['relevant APIs'] ?? [])
60
+ const meta: ToolBenchMeta = {
61
+ queryId: String(row.query_id),
62
+ apiList: row.api_list ?? [],
63
+ relevantApis,
64
+ deterministicJudge: 'api-selection',
65
+ }
66
+ return {
67
+ id: String(row.query_id),
68
+ prompt: [
69
+ 'Solve this ToolLLM/ToolBench API-use task.',
70
+ 'Use only the listed APIs/tools and return the completed tool-use trace plus final answer.',
71
+ '',
72
+ `Query: ${row.query}`,
73
+ '',
74
+ `Available APIs: ${JSON.stringify(row.api_list ?? [], null, 2)}`,
75
+ '',
76
+ 'Return the APIs you used as JSON: {"api_calls":[{"tool_name":"...","api_name":"..."}]}.',
77
+ ].join('\n'),
78
+ metadata: meta as unknown as Record<string, unknown>,
79
+ }
80
+ }
81
+
82
+ function normalizeApiPart(value: string): string {
83
+ return value.toLowerCase().replace(/[^a-z0-9]+/g, '')
84
+ }
85
+
86
+ function apiKey(pair: readonly [string, string]): string {
87
+ return `${normalizeApiPart(pair[0])}.${normalizeApiPart(pair[1])}`
88
+ }
89
+
90
+ function normalizeApiPairs(value: unknown): Array<[string, string]> {
91
+ if (!Array.isArray(value)) return []
92
+ const out: Array<[string, string]> = []
93
+ for (const item of value) {
94
+ if (Array.isArray(item) && typeof item[0] === 'string' && typeof item[1] === 'string') {
95
+ out.push([item[0], item[1]])
96
+ } else if (
97
+ item && typeof item === 'object'
98
+ && typeof (item as { tool_name?: unknown }).tool_name === 'string'
99
+ && typeof (item as { api_name?: unknown }).api_name === 'string'
100
+ ) {
101
+ out.push([(item as { tool_name: string }).tool_name, (item as { api_name: string }).api_name])
102
+ }
103
+ }
104
+ return out
105
+ }
106
+
107
+ function readMeta(task: BenchTask): ToolBenchMeta {
108
+ const md = task.metadata
109
+ if (!md || !Array.isArray(md.relevantApis)) {
110
+ throw new Error(`ToolLLM task ${task.id} missing metadata — loadTasks did not populate deterministic API-selection labels`)
111
+ }
112
+ return md as unknown as ToolBenchMeta
113
+ }
114
+
115
+ function selectRows(rows: ToolBenchRow[], opts: LoadOptions): BenchTask[] {
116
+ let tasks = rows.map(rowToTask)
117
+ if (opts.ids) {
118
+ const want = new Set(opts.ids)
119
+ tasks = tasks.filter((task) => want.has(task.id))
120
+ } else if (opts.limit !== undefined) {
121
+ tasks = tasks.slice(0, opts.limit)
122
+ }
123
+ if (tasks.length === 0) throw new Error(`ToolLLM: no tasks matched ${JSON.stringify(opts)}`)
124
+ return tasks
125
+ }
126
+
127
+ function assertDeterministicSubset(tasks: readonly BenchTask[], source: string): void {
128
+ const missing = tasks.filter((task) => readMeta(task).relevantApis.length === 0).map((task) => task.id)
129
+ if (missing.length > 0) {
130
+ throw new Error(
131
+ `ToolLLM deterministic API-selection labels missing for ${missing.length}/${tasks.length} task(s) from ${source}: ${missing.slice(0, 5).join(', ')}. ` +
132
+ 'Use a ToolBench query file that includes "relevant APIs" labels, or do not score ToolLLM in agent-bench.',
133
+ )
134
+ }
135
+ }
136
+
137
+ async function loadFixtures(opts: LoadOptions): Promise<BenchTask[]> {
138
+ const rows = JSON.parse(await readFile(FIXTURES, 'utf8')) as ToolBenchRow[]
139
+ console.warn(`[toollm] TOOLLM_FIXTURES=1 — loading ${rows.length} adapter fixtures`)
140
+ const tasks = selectRows(rows, opts)
141
+ assertDeterministicSubset(tasks, FIXTURES)
142
+ return tasks
143
+ }
144
+
145
+ async function loadOfficialTasks(dir: string, opts: LoadOptions): Promise<BenchTask[]> {
146
+ const source = queryFile(dir)
147
+ const tasks = selectRows(JSON.parse(await readFile(source, 'utf8')) as ToolBenchRow[], opts)
148
+ assertDeterministicSubset(tasks, source)
149
+ return tasks
150
+ }
151
+
152
+ function extractJsonBlock(text: string): unknown {
153
+ const fences = [...text.matchAll(/```(?:json)?\s*\n([\s\S]*?)```/g)]
154
+ const raw = (fences.at(-1)?.[1] ?? text).trim()
155
+ try {
156
+ return JSON.parse(raw)
157
+ } catch {
158
+ return undefined
159
+ }
160
+ }
161
+
162
+ interface ExtractedApis {
163
+ pairs: Array<[string, string]>
164
+ source: 'structured-json' | 'text-mention'
165
+ }
166
+
167
+ function extractCalledApis(text: string, expected: readonly [string, string][]): ExtractedApis {
168
+ const parsed = extractJsonBlock(text)
169
+ if (parsed && typeof parsed === 'object') {
170
+ const raw = parsed as Record<string, unknown>
171
+ const fromApiCalls = normalizeApiPairs(raw.api_calls)
172
+ if (fromApiCalls.length > 0) return { pairs: fromApiCalls, source: 'structured-json' }
173
+ const fromCalls = normalizeApiPairs(raw.calls)
174
+ if (fromCalls.length > 0) return { pairs: fromCalls, source: 'structured-json' }
175
+ }
176
+
177
+ const lower = text.toLowerCase()
178
+ return {
179
+ pairs: expected.filter(([tool, api]) => {
180
+ const toolNeedle = normalizeApiPart(tool)
181
+ const apiNeedle = normalizeApiPart(api)
182
+ const compactText = lower.replace(/[^a-z0-9]+/g, '')
183
+ return compactText.includes(`${toolNeedle}${apiNeedle}`) || (lower.includes(tool.toLowerCase()) && lower.includes(api.toLowerCase()))
184
+ }),
185
+ source: 'text-mention',
186
+ }
187
+ }
188
+
189
+ function scoreApiSelection(task: BenchTask, artifact: string): BenchScore {
190
+ const meta = readMeta(task)
191
+ if (meta.relevantApis.length === 0) {
192
+ throw new Error(`ToolLLM task ${task.id} has no deterministic API-selection labels; refusing to score`)
193
+ }
194
+ const expected = new Set(meta.relevantApis.map(apiKey))
195
+ const extracted = extractCalledApis(artifact, meta.relevantApis)
196
+ const calledPairs = extracted.pairs
197
+ const called = new Set(calledPairs.map(apiKey))
198
+ const truePositives = [...called].filter((key) => expected.has(key)).length
199
+ const precision = called.size === 0 ? 0 : truePositives / called.size
200
+ const recall = truePositives / expected.size
201
+ const score = expected.size === 0 ? 0 : recall
202
+ const resolved = extracted.source === 'structured-json' && recall === 1 && precision === 1
203
+ return {
204
+ resolved,
205
+ score,
206
+ detail: JSON.stringify({
207
+ scoring: 'api-selection-only',
208
+ extractionSource: extracted.source,
209
+ queryId: meta.queryId,
210
+ expected: meta.relevantApis,
211
+ called: calledPairs,
212
+ precision,
213
+ recall,
214
+ fullToolEvalScore: null,
215
+ }),
216
+ }
217
+ }
218
+
219
+ export function createToolLlmAdapter(): BenchmarkAdapter {
220
+ const fixturesMode = process.env.TOOLLM_FIXTURES === '1'
221
+
222
+ return {
223
+ name: 'toollm',
224
+ output: toollmOutput,
225
+
226
+ async preflight() {
227
+ if (fixturesMode) return
228
+ const dir = toolbenchDir()
229
+ if (!dir) {
230
+ throw new Error('TOOLBENCH_DIR is required. Fix: clone https://github.com/OpenBMB/ToolBench and set TOOLBENCH_DIR=/path/to/ToolBench.')
231
+ }
232
+ await loadOfficialTasks(dir, { limit: 1 })
233
+ },
234
+
235
+ async loadTasks(opts: LoadOptions = {}) {
236
+ if (fixturesMode) return loadFixtures(opts)
237
+ const dir = toolbenchDir()
238
+ if (!dir) throw new Error('TOOLBENCH_DIR is required to load ToolLLM tasks')
239
+ return loadOfficialTasks(dir, opts)
240
+ },
241
+
242
+ async goldArtifact(task: BenchTask) {
243
+ const meta = readMeta(task)
244
+ if (meta.relevantApis.length === 0) return undefined
245
+ return JSON.stringify({
246
+ api_calls: meta.relevantApis.map(([tool_name, api_name]) => ({ tool_name, api_name })),
247
+ }, null, 2)
248
+ },
249
+
250
+ async judge(task: BenchTask, artifact: string): Promise<BenchScore> {
251
+ return scoreApiSelection(task, artifact)
252
+ },
253
+ }
254
+ }
@@ -20,12 +20,40 @@ export interface BenchTask {
20
20
  metadata?: Record<string, unknown>
21
21
  }
22
22
 
23
+ export interface JudgeArtifactFileReceipt {
24
+ /** POSIX path relative to the capture directory. */
25
+ path: string
26
+ /** Exact byte length of the retained file or symbolic-link target. */
27
+ byteLength: number
28
+ /** SHA-256 over the retained file bytes or UTF-8 symbolic-link target. */
29
+ sha256: `sha256:${string}`
30
+ kind: 'file' | 'symlink'
31
+ }
32
+
33
+ /** Durable evidence written before a staged evaluator's temporary directory is removed. */
34
+ export interface JudgeArtifactReceipt {
35
+ schema: 'agent-bench/judge-artifacts/v1'
36
+ /** Absolute directory containing `evaluator/`, `process/`, and `receipt.json`. */
37
+ directory: string
38
+ /** Exact copy of the evaluator working directory. */
39
+ evaluatorDirectory: string
40
+ manifestPath: string
41
+ evaluatorSucceeded: boolean
42
+ files: JudgeArtifactFileReceipt[]
43
+ fileCount: number
44
+ byteLength: number
45
+ /** SHA-256 over every sorted path, kind, byte length, and content hash. */
46
+ treeSha256: `sha256:${string}`
47
+ }
48
+
23
49
  export interface BenchScore {
24
50
  /** Did the deterministic judge pass (tests resolved / state correct)? */
25
51
  resolved: boolean
26
52
  /** 0..1 — 1 = fully resolved; partial credit where the harness supports it. */
27
53
  score: number
28
54
  detail?: string
55
+ /** Present only when the caller explicitly requested durable judge evidence. */
56
+ judgeArtifacts?: JudgeArtifactReceipt
29
57
  }
30
58
 
31
59
  export interface LoadOptions {
@@ -49,6 +77,20 @@ export interface BenchmarkAdapter {
49
77
  * so the gate runner (`runGate` / `runBenchmark`) needs no
50
78
  * per-benchmark branching. */
51
79
  output?: OutputAdapter<string>
80
+ /** Post-shot deliverable extraction from the box FILESYSTEM, not the event stream.
81
+ * When set, the shot runner execs `command` in the STILL-ALIVE box after the agent
82
+ * turn drains and uses its stdout as the judged artifact — the durable way to capture
83
+ * a git diff of the agent's in-box edits (standard SWE-bench practice: SWE-agent /
84
+ * OpenHands read the diff from repo STATE), instead of hoping the model printed a
85
+ * fenced diff in its reply. Empty stdout ⇒ the runner falls back to `output` (the
86
+ * event-stream parse). `cwd` defaults to the box root. */
87
+ boxExtract?(task: BenchTask): { command: string; cwd?: string }
88
+ /** Optional workspace pre-stage run in the box BEFORE the agent shot (same
89
+ * session as `boxExtract`). For repo-state benchmarks (SWE-bench) this clones
90
+ * the instance repo at `base_commit` into a fixed path so the agent only edits
91
+ * — the harness owns the checkout, not the (stochastic) model. A non-zero exit
92
+ * fails the shot loud rather than letting the agent run against an empty box. */
93
+ boxSetup?(task: BenchTask): { command: string; cwd?: string }
52
94
  /** Benchmark-owned worker leaf. Set when the benchmark's native protocol IS the
53
95
  * worker (e.g. AppWorld's interactive ReAct episode runs inside the engine,
54
96
  * not as a chat completion) — the experiment uses this instead of the
@@ -0,0 +1,200 @@
1
+ /**
2
+ * WebArena-Verified adapter (ServiceNow/webarena-verified).
3
+ *
4
+ * Worker artifact = a WebArena-Verified run output directory, not final chat text.
5
+ * Judge = the official `webarena_verified eval-tasks` evaluator over that output
6
+ * directory. The adapter refuses to score a plain answer so we do not turn a DOM
7
+ * benchmark into a fake text benchmark.
8
+ */
9
+
10
+ import { access, readFile, stat } from 'node:fs/promises'
11
+ import { join, resolve } from 'node:path'
12
+ import type { OutputAdapter } from '@tangle-network/agent-runtime/loops'
13
+ import { benchRoot, runVenvPython } from './_harness'
14
+ import type { BenchmarkAdapter, BenchScore, BenchTask, LoadOptions } from './types'
15
+
16
+ const FIXTURES = join(benchRoot, 'fixtures', 'webarena-verified.json')
17
+ const DATASET_REL = join('assets', 'dataset', 'webarena-verified.json')
18
+
19
+ interface WebArenaRow {
20
+ task_id: number
21
+ intent: string
22
+ intent_template_id?: number
23
+ sites?: string[]
24
+ start_urls?: string[]
25
+ eval?: unknown[]
26
+ revision?: number
27
+ }
28
+
29
+ interface WebArenaMeta {
30
+ taskId: number
31
+ intentTemplateId?: number
32
+ sites: string[]
33
+ startUrls: string[]
34
+ revision?: number
35
+ eval?: unknown[]
36
+ }
37
+
38
+ const webarenaDir = (): string | undefined => process.env.WEBARENA_VERIFIED_DIR
39
+
40
+ export const webarenaOutputDirOutput: OutputAdapter<string> = {
41
+ parse(events) {
42
+ let text = ''
43
+ for (const ev of events) {
44
+ const d = (ev as { data?: Record<string, unknown> })?.data
45
+ const t = d?.finalText ?? d?.text ?? d?.result
46
+ if (typeof t === 'string' && t.length > 0) text = t
47
+ }
48
+ const fences = [...text.matchAll(/```(?:text|path)?\s*\n([\s\S]*?)```/g)]
49
+ return (fences.at(-1)?.[1] ?? text).trim()
50
+ },
51
+ }
52
+
53
+ async function assertFile(path: string, label: string): Promise<void> {
54
+ try {
55
+ await access(path)
56
+ } catch (err) {
57
+ throw new Error(`WebArena-Verified: missing ${label} at ${path} (${err instanceof Error ? err.message : err})`)
58
+ }
59
+ }
60
+
61
+ async function assertDir(path: string, label: string): Promise<void> {
62
+ try {
63
+ const s = await stat(path)
64
+ if (!s.isDirectory()) throw new Error('not a directory')
65
+ } catch (err) {
66
+ throw new Error(`WebArena-Verified: missing ${label} at ${path} (${err instanceof Error ? err.message : err})`)
67
+ }
68
+ }
69
+
70
+ function rowToTask(row: WebArenaRow): BenchTask {
71
+ const meta: WebArenaMeta = {
72
+ taskId: row.task_id,
73
+ intentTemplateId: row.intent_template_id,
74
+ sites: row.sites ?? [],
75
+ startUrls: row.start_urls ?? [],
76
+ revision: row.revision,
77
+ eval: row.eval,
78
+ }
79
+ return {
80
+ id: String(row.task_id),
81
+ prompt: [
82
+ 'Run this WebArena-Verified browser task in the official environment.',
83
+ `Goal: ${row.intent}`,
84
+ row.start_urls?.length ? `Start URL templates: ${row.start_urls.join(', ')}` : undefined,
85
+ row.sites?.length ? `Sites: ${row.sites.join(', ')}` : undefined,
86
+ '',
87
+ 'Return the path to the official WebArena-Verified run output directory for this task.',
88
+ 'The judge expects that directory to contain the task response and network trace files.',
89
+ ]
90
+ .filter(Boolean)
91
+ .join('\n'),
92
+ metadata: meta as unknown as Record<string, unknown>,
93
+ }
94
+ }
95
+
96
+ function readMeta(task: BenchTask): WebArenaMeta {
97
+ const md = task.metadata
98
+ if (!md || typeof md.taskId !== 'number') {
99
+ throw new Error(`webarena-verified task ${task.id} missing metadata — loadTasks did not populate it`)
100
+ }
101
+ return md as unknown as WebArenaMeta
102
+ }
103
+
104
+ function selectRows(rows: WebArenaRow[], opts: LoadOptions): BenchTask[] {
105
+ let tasks = rows.map(rowToTask)
106
+ if (opts.ids) {
107
+ const want = new Set(opts.ids)
108
+ tasks = tasks.filter((task) => want.has(task.id))
109
+ } else if (opts.limit !== undefined) {
110
+ tasks = tasks.slice(0, opts.limit)
111
+ }
112
+ if (tasks.length === 0) throw new Error(`WebArena-Verified: no tasks matched ${JSON.stringify(opts)}`)
113
+ return tasks
114
+ }
115
+
116
+ async function loadFixtures(opts: LoadOptions): Promise<BenchTask[]> {
117
+ const rows = JSON.parse(await readFile(FIXTURES, 'utf8')) as WebArenaRow[]
118
+ console.warn(`[webarena-verified] WEBARENA_VERIFIED_FIXTURES=1 — loading ${rows.length} adapter fixtures`)
119
+ return selectRows(rows, opts)
120
+ }
121
+
122
+ async function loadOfficialRows(dir: string, opts: LoadOptions): Promise<BenchTask[]> {
123
+ const rows = JSON.parse(await readFile(join(dir, DATASET_REL), 'utf8')) as WebArenaRow[]
124
+ return selectRows(rows, opts)
125
+ }
126
+
127
+ async function runOfficialEval(root: string, taskId: number, outputDir: string): Promise<Record<string, unknown>> {
128
+ const script = `
129
+ import json, os, subprocess, sys
130
+ from pathlib import Path
131
+
132
+ root = Path(sys.argv[1])
133
+ task_id = sys.argv[2]
134
+ output_dir = Path(sys.argv[3])
135
+ env = os.environ.copy()
136
+ env["PYTHONPATH"] = str(root / "src") + os.pathsep + env.get("PYTHONPATH", "")
137
+ cmd = [sys.executable, "-m", "webarena_verified", "eval-tasks", "--task-ids", task_id, "--output-dir", str(output_dir)]
138
+ proc = subprocess.run(cmd, cwd=root, env=env, text=True, capture_output=True)
139
+ if proc.returncode != 0:
140
+ raise SystemExit((proc.stderr or proc.stdout or f"exit {proc.returncode}")[:2000])
141
+ result_path = output_dir / task_id / "eval_result.json"
142
+ if not result_path.exists():
143
+ raise SystemExit(f"official evaluator did not write {result_path}")
144
+ print(json.dumps(json.loads(result_path.read_text())))
145
+ `
146
+ const stdout = await runVenvPython(script, [root, String(taskId), outputDir], 0)
147
+ return JSON.parse(stdout.trim().split('\n').at(-1) ?? '{}') as Record<string, unknown>
148
+ }
149
+
150
+ export function createWebArenaVerifiedAdapter(): BenchmarkAdapter {
151
+ const fixturesMode = process.env.WEBARENA_VERIFIED_FIXTURES === '1'
152
+
153
+ return {
154
+ name: 'webarena-verified',
155
+ output: webarenaOutputDirOutput,
156
+
157
+ async preflight() {
158
+ if (fixturesMode) return
159
+ const dir = webarenaDir()
160
+ if (!dir) {
161
+ throw new Error(
162
+ 'WEBARENA_VERIFIED_DIR is required. Fix: clone https://github.com/ServiceNow/webarena-verified, install its deps in bench/.venv, and set WEBARENA_VERIFIED_DIR=/path/to/webarena-verified.',
163
+ )
164
+ }
165
+ await assertFile(join(dir, DATASET_REL), 'official dataset')
166
+ await assertFile(join(dir, 'src', 'webarena_verified', '__main__.py'), 'official CLI module')
167
+ await runVenvPython(
168
+ 'import sys; sys.path.insert(0, sys.argv[1]); import webarena_verified; print("ok")',
169
+ [join(dir, 'src')],
170
+ )
171
+ },
172
+
173
+ async loadTasks(opts: LoadOptions = {}) {
174
+ if (fixturesMode) return loadFixtures(opts)
175
+ const dir = webarenaDir()
176
+ if (!dir) throw new Error('WEBARENA_VERIFIED_DIR is required to load official WebArena-Verified tasks')
177
+ return loadOfficialRows(dir, opts)
178
+ },
179
+
180
+ async goldArtifact() {
181
+ return undefined
182
+ },
183
+
184
+ async judge(task: BenchTask, artifact: string): Promise<BenchScore> {
185
+ const dir = webarenaDir()
186
+ if (!dir) throw new Error('WEBARENA_VERIFIED_DIR is required to judge WebArena-Verified artifacts')
187
+ const outputDir = resolve(artifact.trim())
188
+ await assertDir(outputDir, 'run output directory')
189
+ const meta = readMeta(task)
190
+ const report = await runOfficialEval(dir, meta.taskId, outputDir)
191
+ const score = typeof report.score === 'number' ? report.score : 0
192
+ const status = typeof report.status === 'string' ? report.status : 'unknown'
193
+ return {
194
+ resolved: score === 1,
195
+ score,
196
+ detail: JSON.stringify({ taskId: meta.taskId, status, outputDir }),
197
+ }
198
+ },
199
+ }
200
+ }
File without changes