niceeval 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (428) hide show
  1. package/INDEX.md +31 -33
  2. package/dist/agents/types.d.ts +20 -17
  3. package/dist/context/types.d.ts +21 -21
  4. package/dist/i18n/en.d.ts +10 -2
  5. package/dist/i18n/zh-CN.d.ts +15 -7
  6. package/dist/o11y/execution-tree.d.ts +103 -0
  7. package/dist/o11y/otlp/select.d.ts +22 -0
  8. package/dist/report/{react → assets}/colors.d.ts +8 -0
  9. package/dist/report/{react → assets}/colors.js +23 -0
  10. package/dist/report/built-in/index.d.ts +2 -2
  11. package/dist/report/built-in/index.js +2 -2
  12. package/dist/report/built-in/standard.d.ts +8 -1
  13. package/dist/report/built-in/standard.js +17 -7
  14. package/dist/report/components/attempt-detail/AttemptAssertions.d.ts +6 -0
  15. package/dist/report/components/attempt-detail/AttemptAssertions.js +17 -0
  16. package/dist/report/components/attempt-detail/AttemptConversation.d.ts +6 -0
  17. package/dist/report/components/attempt-detail/AttemptConversation.js +34 -0
  18. package/dist/report/components/attempt-detail/AttemptDiagnostics.d.ts +6 -0
  19. package/dist/report/components/attempt-detail/AttemptDiagnostics.js +7 -0
  20. package/dist/report/components/attempt-detail/AttemptDiff.d.ts +6 -0
  21. package/dist/report/components/attempt-detail/AttemptDiff.js +11 -0
  22. package/dist/report/components/attempt-detail/AttemptError.d.ts +6 -0
  23. package/dist/report/components/attempt-detail/AttemptError.js +16 -0
  24. package/dist/report/components/attempt-detail/AttemptFixPrompt.d.ts +6 -0
  25. package/dist/report/components/attempt-detail/AttemptFixPrompt.js +7 -0
  26. package/dist/report/components/attempt-detail/AttemptSource.d.ts +6 -0
  27. package/dist/report/components/attempt-detail/AttemptSource.js +27 -0
  28. package/dist/report/components/attempt-detail/AttemptSummary.d.ts +8 -0
  29. package/dist/report/components/attempt-detail/AttemptSummary.js +20 -0
  30. package/dist/report/components/attempt-detail/AttemptTimeline.d.ts +6 -0
  31. package/dist/report/components/attempt-detail/AttemptTimeline.js +28 -0
  32. package/dist/report/components/attempt-detail/AttemptTrace.d.ts +6 -0
  33. package/dist/report/components/attempt-detail/AttemptTrace.js +29 -0
  34. package/dist/report/components/attempt-detail/AttemptUsage.d.ts +6 -0
  35. package/dist/report/components/attempt-detail/AttemptUsage.js +20 -0
  36. package/dist/report/components/attempt-detail/compute.d.ts +24 -0
  37. package/dist/report/components/attempt-detail/compute.js +254 -0
  38. package/dist/report/components/attempt-detail/faces.d.ts +14 -0
  39. package/dist/report/components/attempt-detail/faces.js +235 -0
  40. package/dist/report/components/attempt-detail/index.d.ts +38 -0
  41. package/dist/report/components/attempt-detail/index.js +527 -0
  42. package/dist/report/{react → components}/cell.d.ts +5 -3
  43. package/dist/report/{react → components}/cell.js +3 -3
  44. package/dist/report/{react → components/entity-lists}/AttemptList.d.ts +8 -4
  45. package/dist/report/{react → components/entity-lists}/AttemptList.js +16 -10
  46. package/dist/report/{react → components/entity-lists}/EvalList.d.ts +3 -3
  47. package/dist/report/{react → components/entity-lists}/EvalList.js +0 -0
  48. package/dist/report/{react → components/entity-lists}/ExperimentList.d.ts +3 -3
  49. package/dist/report/{react → components/entity-lists}/ExperimentList.js +10 -10
  50. package/dist/report/components/entity-lists/compute.d.ts +23 -0
  51. package/dist/report/components/entity-lists/compute.js +171 -0
  52. package/dist/report/components/entity-lists/faces.d.ts +5 -0
  53. package/dist/report/components/entity-lists/faces.js +174 -0
  54. package/dist/report/components/entity-lists/index.d.ts +51 -0
  55. package/dist/report/components/entity-lists/index.js +165 -0
  56. package/dist/report/{react → components}/fixtures.d.ts +2 -2
  57. package/dist/report/{react → components}/fixtures.js +1 -1
  58. package/dist/report/{react → components/metric-views}/DeltaTable.d.ts +2 -2
  59. package/dist/report/{react → components/metric-views}/DeltaTable.js +4 -4
  60. package/dist/report/{react → components/metric-views}/MetricBars.d.ts +3 -3
  61. package/dist/report/{react → components/metric-views}/MetricBars.js +3 -3
  62. package/dist/report/{react → components/metric-views}/MetricLine.d.ts +2 -2
  63. package/dist/report/{react → components/metric-views}/MetricLine.js +8 -5
  64. package/dist/report/{react → components/metric-views}/MetricMatrix.d.ts +3 -3
  65. package/dist/report/{react → components/metric-views}/MetricMatrix.js +4 -4
  66. package/dist/report/components/metric-views/MetricScatter.d.ts +11 -0
  67. package/dist/report/{react → components/metric-views}/MetricScatter.js +26 -23
  68. package/dist/report/{react → components/metric-views}/MetricTable.d.ts +3 -3
  69. package/dist/report/{react → components/metric-views}/MetricTable.js +4 -4
  70. package/dist/report/{react → components/metric-views}/Scoreboard.d.ts +2 -2
  71. package/dist/report/{react → components/metric-views}/Scoreboard.js +3 -3
  72. package/dist/report/components/metric-views/compute.d.ts +89 -0
  73. package/dist/report/{compute.js → components/metric-views/compute.js} +32 -415
  74. package/dist/report/components/metric-views/faces.d.ts +13 -0
  75. package/dist/report/components/metric-views/faces.js +381 -0
  76. package/dist/report/components/metric-views/index.d.ts +50 -0
  77. package/dist/report/components/metric-views/index.js +273 -0
  78. package/dist/report/{text → components/metric-views}/plot.js +1 -1
  79. package/dist/report/components/shared-compute.d.ts +22 -0
  80. package/dist/report/components/shared-compute.js +47 -0
  81. package/dist/report/components/shared-faces.d.ts +9 -0
  82. package/dist/report/components/shared-faces.js +26 -0
  83. package/dist/report/components/shared.d.ts +68 -0
  84. package/dist/report/components/shared.js +125 -0
  85. package/dist/report/{react → components/site-components}/CopyFixPrompt.d.ts +2 -2
  86. package/dist/report/{react → components/site-components}/CopyFixPrompt.js +2 -2
  87. package/dist/report/{react → components/site-components}/HeroCard.d.ts +2 -2
  88. package/dist/report/{react → components/site-components}/HeroCard.js +2 -2
  89. package/dist/report/{react → components/site-components}/ScopeWarnings.d.ts +2 -2
  90. package/dist/report/{react → components/site-components}/ScopeWarnings.js +3 -3
  91. package/dist/report/{react → components/site-components}/TraceWaterfall.d.ts +3 -3
  92. package/dist/report/{react → components/site-components}/TraceWaterfall.js +5 -5
  93. package/dist/report/components/site-components/compute.d.ts +28 -0
  94. package/dist/report/components/site-components/compute.js +132 -0
  95. package/dist/report/components/site-components/faces.d.ts +21 -0
  96. package/dist/report/components/site-components/faces.js +75 -0
  97. package/dist/report/components/site-components/index.d.ts +74 -0
  98. package/dist/report/components/site-components/index.js +220 -0
  99. package/dist/report/{scope-warnings.d.ts → components/site-components/scope-warnings.d.ts} +2 -2
  100. package/dist/report/{scope-warnings.js → components/site-components/scope-warnings.js} +2 -2
  101. package/dist/report/{react → components/summaries}/ScopeSummary.d.ts +2 -2
  102. package/dist/report/{react → components/summaries}/ScopeSummary.js +17 -8
  103. package/dist/report/components/summaries/compute.d.ts +7 -0
  104. package/dist/report/components/summaries/compute.js +51 -0
  105. package/dist/report/components/summaries/faces.d.ts +7 -0
  106. package/dist/report/components/summaries/faces.js +38 -0
  107. package/dist/report/components/summaries/index.d.ts +27 -0
  108. package/dist/report/components/summaries/index.js +78 -0
  109. package/dist/report/definition/grid-layout.d.ts +50 -0
  110. package/dist/report/definition/grid-layout.js +89 -0
  111. package/dist/report/{primitives.d.ts → definition/primitives.d.ts} +34 -4
  112. package/dist/report/{primitives.js → definition/primitives.js} +101 -10
  113. package/dist/report/{report.d.ts → definition/report.d.ts} +36 -65
  114. package/dist/report/{report.js → definition/report.js} +58 -90
  115. package/dist/report/{text/table.d.ts → definition/table-text.d.ts} +2 -2
  116. package/dist/report/{text/table.js → definition/table-text.js} +2 -2
  117. package/dist/report/{tree.d.ts → definition/tree.d.ts} +44 -12
  118. package/dist/report/{tree.js → definition/tree.js} +10 -12
  119. package/dist/report/index.d.ts +33 -18
  120. package/dist/report/index.js +23 -14
  121. package/dist/report/{aggregate.d.ts → model/aggregate.d.ts} +12 -4
  122. package/dist/report/{aggregate.js → model/aggregate.js} +28 -8
  123. package/dist/report/{flag.d.ts → model/flag.d.ts} +14 -1
  124. package/dist/report/{flag.js → model/flag.js} +35 -0
  125. package/dist/report/{format.d.ts → model/format.d.ts} +11 -5
  126. package/dist/report/{format.js → model/format.js} +48 -4
  127. package/dist/report/{locale.d.ts → model/locale.d.ts} +17 -17
  128. package/dist/report/{locale.js → model/locale.js} +31 -33
  129. package/dist/report/{metrics.d.ts → model/metrics.d.ts} +2 -2
  130. package/dist/report/{metrics.js → model/metrics.js} +2 -2
  131. package/dist/report/{types.d.ts → model/types.d.ts} +140 -20
  132. package/dist/report/react/index.d.ts +32 -21
  133. package/dist/report/react/index.js +33 -21
  134. package/dist/report/{load.d.ts → runtime/load.d.ts} +1 -1
  135. package/dist/report/{load.js → runtime/load.js} +1 -1
  136. package/dist/report/runtime/text.d.ts +67 -0
  137. package/dist/report/runtime/text.js +114 -0
  138. package/dist/report/runtime/web.d.ts +35 -0
  139. package/dist/report/runtime/web.js +70 -0
  140. package/dist/results/annotated-source.d.ts +87 -0
  141. package/dist/results/attempt-evidence.d.ts +74 -0
  142. package/dist/results/attempt-source.d.ts +15 -0
  143. package/dist/results/select.d.ts +12 -2
  144. package/dist/results/select.js +27 -5
  145. package/dist/runner/eval-selection.d.ts +26 -0
  146. package/dist/runner/feedback/sink.d.ts +26 -1
  147. package/dist/runner/types.d.ts +149 -23
  148. package/dist/sandbox/docker.d.ts +6 -0
  149. package/dist/sandbox/types.d.ts +23 -21
  150. package/dist/scoring/display.d.ts +7 -2
  151. package/dist/scoring/display.js +42 -4
  152. package/dist/scoring/types.d.ts +3 -3
  153. package/dist/shared/aggregate.d.ts +15 -1
  154. package/dist/shared/aggregate.js +32 -1
  155. package/dist/shared/types.d.ts +6 -1
  156. package/docs-site/images/logo-dark.svg +7 -0
  157. package/docs-site/images/logo.svg +6 -5
  158. package/docs-site/zh/README.md +8 -7
  159. package/docs-site/zh/examples/ai-agent-application.mdx +5 -5
  160. package/docs-site/zh/examples/coding-agent-extensions.mdx +4 -4
  161. package/docs-site/zh/examples/index.mdx +3 -3
  162. package/docs-site/zh/examples/integrations/ai-sdk-v7.mdx +1 -1
  163. package/docs-site/zh/examples/integrations/claude-sdk.mdx +3 -3
  164. package/docs-site/zh/examples/integrations/codex-sdk.mdx +3 -3
  165. package/docs-site/zh/examples/integrations/langgraph.mdx +3 -3
  166. package/docs-site/zh/examples/integrations/pi-sdk.mdx +2 -2
  167. package/docs-site/zh/explanation/adapter.mdx +19 -19
  168. package/docs-site/zh/explanation/assert.mdx +9 -9
  169. package/docs-site/zh/explanation/drive.mdx +4 -4
  170. package/docs-site/zh/explanation/evals.mdx +8 -8
  171. package/docs-site/zh/explanation/experiment.mdx +8 -8
  172. package/docs-site/zh/explanation/hitl.mdx +13 -13
  173. package/docs-site/zh/explanation/judge.mdx +3 -3
  174. package/docs-site/zh/explanation/overview.mdx +7 -7
  175. package/docs-site/zh/explanation/runner.mdx +11 -11
  176. package/docs-site/zh/explanation/tier.mdx +6 -6
  177. package/docs-site/zh/index.mdx +14 -14
  178. package/docs-site/zh/introduction.mdx +13 -16
  179. package/docs-site/zh/reference/builtin-agents.mdx +61 -28
  180. package/docs-site/zh/reference/capabilities.mdx +7 -27
  181. package/docs-site/zh/reference/cli.mdx +35 -32
  182. package/docs-site/zh/reference/define-agent.mdx +32 -31
  183. package/docs-site/zh/reference/define-config.mdx +6 -6
  184. package/docs-site/zh/reference/define-eval.mdx +33 -20
  185. package/docs-site/zh/reference/events.mdx +4 -4
  186. package/docs-site/zh/reference/expect.mdx +3 -3
  187. package/docs-site/zh/reference/official-adapters.mdx +17 -17
  188. package/docs-site/zh/reference/report-components.mdx +193 -121
  189. package/docs-site/zh/reference/results-data.mdx +13 -13
  190. package/docs-site/zh/troubleshooting/debug-sandbox.mdx +11 -11
  191. package/docs-site/zh/troubleshooting/debugging.mdx +43 -19
  192. package/docs-site/zh/{how-to → tutorials}/agent-feedback-loop.mdx +38 -38
  193. package/docs-site/zh/tutorials/agent-onboarding.mdx +100 -0
  194. package/docs-site/zh/tutorials/authoring.mdx +211 -0
  195. package/docs-site/zh/{how-to → tutorials}/ci-integration.mdx +8 -8
  196. package/docs-site/zh/{how-to → tutorials}/connect-otel.mdx +22 -22
  197. package/docs-site/zh/{how-to → tutorials}/connect-your-agent.mdx +58 -63
  198. package/docs-site/zh/tutorials/custom-reports.mdx +453 -0
  199. package/docs-site/zh/{how-to → tutorials}/dataset-fanout.mdx +4 -4
  200. package/docs-site/zh/tutorials/experiments.mdx +103 -0
  201. package/docs-site/zh/{how-to → tutorials}/fixtures.mdx +9 -9
  202. package/docs-site/zh/{how-to → tutorials}/publish-report.mdx +11 -5
  203. package/docs-site/zh/tutorials/quickstart.mdx +18 -18
  204. package/docs-site/zh/{how-to → tutorials}/reporters.mdx +6 -6
  205. package/docs-site/zh/{how-to → tutorials}/sandbox-agent.mdx +6 -6
  206. package/docs-site/zh/{how-to → tutorials}/sandbox-providers.mdx +41 -19
  207. package/docs-site/zh/{how-to → tutorials}/scoring-guide.mdx +4 -4
  208. package/docs-site/zh/{how-to → tutorials}/viewing-results.mdx +59 -50
  209. package/docs-site/zh/tutorials/write-experiment.mdx +355 -0
  210. package/docs-site/zh/{how-to → tutorials}/write-send.mdx +40 -40
  211. package/package.json +8 -4
  212. package/src/agents/bub.ts +21 -8
  213. package/src/agents/claude-code.ts +23 -10
  214. package/src/agents/codex.test.ts +22 -7
  215. package/src/agents/codex.ts +22 -9
  216. package/src/agents/index.ts +2 -2
  217. package/src/agents/openai-compat.ts +1 -1
  218. package/src/agents/post-setup.ts +45 -22
  219. package/src/agents/streaming.ts +1 -1
  220. package/src/agents/types.ts +20 -17
  221. package/src/agents/ui-message-stream.test.ts +10 -0
  222. package/src/agents/ui-message-stream.ts +12 -1
  223. package/src/cli.ts +77 -64
  224. package/src/context/types.ts +21 -21
  225. package/src/define.ts +13 -0
  226. package/src/i18n/en.ts +30 -15
  227. package/src/i18n/zh-CN.ts +30 -15
  228. package/src/index.ts +2 -0
  229. package/src/report/{react → assets}/colors.ts +22 -0
  230. package/src/report/{react → assets}/enhance.js +1 -33
  231. package/src/report/{react → assets}/styles.css +136 -112
  232. package/src/report/built-in/index.tsx +2 -2
  233. package/src/report/built-in/standard.tsx +18 -6
  234. package/src/report/components/attempt-detail/AttemptAssertions.tsx +67 -0
  235. package/src/report/components/attempt-detail/AttemptConversation.tsx +110 -0
  236. package/src/report/components/attempt-detail/AttemptDiagnostics.tsx +38 -0
  237. package/src/report/components/attempt-detail/AttemptDiff.tsx +35 -0
  238. package/src/report/components/attempt-detail/AttemptError.tsx +31 -0
  239. package/src/report/components/attempt-detail/AttemptFixPrompt.tsx +26 -0
  240. package/src/report/components/attempt-detail/AttemptSource.tsx +80 -0
  241. package/src/report/components/attempt-detail/AttemptSummary.tsx +74 -0
  242. package/src/report/components/attempt-detail/AttemptTimeline.tsx +108 -0
  243. package/src/report/components/attempt-detail/AttemptTrace.tsx +63 -0
  244. package/src/report/components/attempt-detail/AttemptUsage.tsx +29 -0
  245. package/src/report/components/attempt-detail/attempt-components.test.tsx +680 -0
  246. package/src/report/components/attempt-detail/compute.ts +287 -0
  247. package/src/report/components/attempt-detail/faces.ts +257 -0
  248. package/src/report/components/attempt-detail/index.tsx +583 -0
  249. package/src/report/components/attempt-detail/validate.test.ts +235 -0
  250. package/src/report/{react → components}/cell.tsx +6 -3
  251. package/src/report/{report.test.ts → components/compute.test.ts} +316 -46
  252. package/src/report/{react → components/entity-lists}/AttemptList.tsx +24 -16
  253. package/src/report/{react → components/entity-lists}/EvalList.tsx +0 -0
  254. package/src/report/{react → components/entity-lists}/ExperimentList.tsx +23 -20
  255. package/src/report/components/entity-lists/compute.ts +196 -0
  256. package/src/report/components/entity-lists/faces.ts +207 -0
  257. package/src/report/components/entity-lists/index.tsx +241 -0
  258. package/src/report/components/entity-lists/validate.test.ts +114 -0
  259. package/src/report/{react → components}/fixtures.ts +2 -2
  260. package/src/report/{react → components/metric-views}/DeltaTable.tsx +5 -5
  261. package/src/report/{react → components/metric-views}/MetricBars.tsx +5 -5
  262. package/src/report/{react → components/metric-views}/MetricLine.tsx +9 -6
  263. package/src/report/{react → components/metric-views}/MetricMatrix.tsx +6 -6
  264. package/src/report/{react → components/metric-views}/MetricScatter.tsx +41 -33
  265. package/src/report/{react → components/metric-views}/MetricTable.tsx +6 -6
  266. package/src/report/{react → components/metric-views}/Scoreboard.tsx +4 -4
  267. package/src/report/{compute.ts → components/metric-views/compute.ts} +51 -477
  268. package/src/report/components/metric-views/faces.ts +423 -0
  269. package/src/report/components/metric-views/index.tsx +352 -0
  270. package/src/report/{text → components/metric-views}/plot.ts +1 -1
  271. package/src/report/components/metric-views/validate.test.ts +211 -0
  272. package/src/report/{react → components}/render.test.tsx +47 -17
  273. package/src/report/components/shared-compute.ts +59 -0
  274. package/src/report/components/shared-faces.ts +30 -0
  275. package/src/report/components/shared.ts +192 -0
  276. package/src/report/{react → components/site-components}/CopyFixPrompt.tsx +3 -3
  277. package/src/report/{react → components/site-components}/HeroCard.tsx +3 -3
  278. package/src/report/{react → components/site-components}/ScopeWarnings.tsx +4 -4
  279. package/src/report/{react → components/site-components}/TraceWaterfall.tsx +13 -10
  280. package/src/report/components/site-components/compute.ts +144 -0
  281. package/src/report/components/site-components/faces.ts +80 -0
  282. package/src/report/components/site-components/index.tsx +265 -0
  283. package/src/report/{scope-warnings.ts → components/site-components/scope-warnings.ts} +3 -3
  284. package/src/report/{site-components.test.tsx → components/site-components/site-components.test.tsx} +23 -21
  285. package/src/report/components/site-components/validate.test.ts +89 -0
  286. package/src/report/{react → components/summaries}/ScopeSummary.tsx +26 -11
  287. package/src/report/components/summaries/compute.ts +56 -0
  288. package/src/report/components/summaries/faces.ts +48 -0
  289. package/src/report/components/summaries/index.tsx +127 -0
  290. package/src/report/components/summaries/validate.test.ts +49 -0
  291. package/src/report/definition/grid-layout.test.ts +124 -0
  292. package/src/report/definition/grid-layout.ts +146 -0
  293. package/src/report/{primitives.tsx → definition/primitives.tsx} +168 -18
  294. package/src/report/{report.ts → definition/report.ts} +98 -165
  295. package/src/report/{shell-head.test.ts → definition/shell-head.test.ts} +2 -2
  296. package/src/report/{text/table.ts → definition/table-text.ts} +4 -4
  297. package/src/report/{tree.ts → definition/tree.ts} +58 -22
  298. package/src/report/index.ts +126 -66
  299. package/src/report/{aggregate.ts → model/aggregate.ts} +31 -9
  300. package/src/report/{flag.ts → model/flag.ts} +39 -1
  301. package/src/report/{format.ts → model/format.ts} +59 -5
  302. package/src/report/{locale.ts → model/locale.ts} +34 -36
  303. package/src/report/{metrics.ts → model/metrics.ts} +3 -3
  304. package/src/report/{types.ts → model/types.ts} +143 -22
  305. package/src/report/react/index.tsx +52 -26
  306. package/src/report/{dual-render.test.tsx → runtime/dual-render.test.tsx} +564 -49
  307. package/src/report/runtime/host.test.ts +44 -0
  308. package/src/report/runtime/host.ts +138 -0
  309. package/src/report/{load.ts → runtime/load.ts} +1 -1
  310. package/src/report/runtime/text.ts +192 -0
  311. package/src/report/runtime/web.ts +106 -0
  312. package/src/results/attempt-evidence.ts +5 -1
  313. package/src/results/host-equivalence.test.ts +54 -3
  314. package/src/results/select.ts +30 -6
  315. package/src/runner/attempt.test.ts +48 -1
  316. package/src/runner/attempt.ts +62 -42
  317. package/src/runner/cleanup-timeout.test.ts +16 -0
  318. package/src/runner/cleanup-timeout.ts +23 -0
  319. package/src/runner/discover.ts +1 -2
  320. package/src/runner/eval-selection.test.ts +187 -0
  321. package/src/runner/eval-selection.ts +109 -0
  322. package/src/runner/experiment-cleanup-registry.test.ts +89 -0
  323. package/src/runner/experiment-cleanup-registry.ts +39 -0
  324. package/src/runner/experiment-labels.test.ts +71 -0
  325. package/src/runner/feedback/agent.test.ts +33 -1
  326. package/src/runner/feedback/agent.ts +12 -16
  327. package/src/runner/feedback/ci.test.ts +35 -1
  328. package/src/runner/feedback/ci.ts +16 -18
  329. package/src/runner/feedback/coordinator.ts +28 -0
  330. package/src/runner/feedback/human.test.ts +108 -1
  331. package/src/runner/feedback/human.ts +71 -21
  332. package/src/runner/feedback/reducer.test.ts +64 -0
  333. package/src/runner/feedback/reducer.ts +26 -0
  334. package/src/runner/feedback/sink.ts +40 -1
  335. package/src/runner/fingerprint.ts +2 -1
  336. package/src/runner/report.test.ts +14 -0
  337. package/src/runner/report.ts +3 -1
  338. package/src/runner/run.test.ts +390 -12
  339. package/src/runner/run.ts +259 -26
  340. package/src/runner/sandbox-selection.test.ts +13 -3
  341. package/src/runner/sandbox-selection.ts +4 -2
  342. package/src/runner/types.ts +158 -23
  343. package/src/sandbox/docker.ts +7 -0
  344. package/src/sandbox/e2b.ts +12 -13
  345. package/src/sandbox/types.ts +23 -21
  346. package/src/scoring/display.test.ts +28 -2
  347. package/src/scoring/display.ts +38 -5
  348. package/src/scoring/types.ts +3 -3
  349. package/src/shared/aggregate.test.ts +52 -0
  350. package/src/shared/aggregate.ts +30 -1
  351. package/src/shared/types.ts +6 -1
  352. package/src/show/command.test.ts +34 -0
  353. package/src/show/command.ts +16 -0
  354. package/src/show/compose.ts +1 -1
  355. package/src/show/index.ts +58 -22
  356. package/src/show/render.ts +16 -240
  357. package/src/show/show.test.ts +128 -35
  358. package/src/view/app/App.test.tsx +8 -7
  359. package/src/view/app/App.tsx +135 -51
  360. package/src/view/app/i18n.ts +7 -147
  361. package/src/view/app/lib/attempt-dialog.test.ts +65 -0
  362. package/src/view/app/lib/attempt-dialog.ts +69 -0
  363. package/src/view/app/main.tsx +0 -1
  364. package/src/view/app/types.ts +3 -86
  365. package/src/view/artifact-serving.test.ts +22 -38
  366. package/src/view/client-dist/app.css +1 -1
  367. package/src/view/client-dist/app.js +15 -22
  368. package/src/view/data.test.ts +48 -36
  369. package/src/view/data.ts +122 -89
  370. package/src/view/index.ts +3 -13
  371. package/src/view/server.ts +36 -6
  372. package/src/view/shared/types.ts +6 -31
  373. package/src/view/site-parity.test.ts +24 -1
  374. package/src/view/site.ts +144 -17
  375. package/src/view/styles.css +18 -806
  376. package/src/view/view-report.test.ts +182 -34
  377. package/dist/report/components.d.ts +0 -179
  378. package/dist/report/components.js +0 -544
  379. package/dist/report/compute.d.ts +0 -139
  380. package/dist/report/react/ExperimentComparison.d.ts +0 -10
  381. package/dist/report/react/ExperimentComparison.js +0 -12
  382. package/dist/report/react/MetricScatter.d.ts +0 -9
  383. package/dist/report/react/format.d.ts +0 -3
  384. package/dist/report/react/format.js +0 -7
  385. package/dist/report/text/faces.d.ts +0 -45
  386. package/dist/report/text/faces.js +0 -671
  387. package/dist/report/web.d.ts +0 -32
  388. package/dist/report/web.js +0 -48
  389. package/docs-site/zh/how-to/authoring.mdx +0 -162
  390. package/docs-site/zh/how-to/custom-reports.mdx +0 -414
  391. package/docs-site/zh/how-to/experiments.mdx +0 -86
  392. package/docs-site/zh/how-to/write-experiment.mdx +0 -164
  393. package/src/report/components.tsx +0 -925
  394. package/src/report/react/ExperimentComparison.tsx +0 -73
  395. package/src/report/react/format.ts +0 -9
  396. package/src/report/text/faces.ts +0 -767
  397. package/src/report/web.ts +0 -77
  398. package/src/show/report-host.test.ts +0 -205
  399. package/src/show/report-host.ts +0 -389
  400. package/src/view/app/components/AttemptModal.tsx +0 -496
  401. package/src/view/app/components/CodeView.test.tsx +0 -142
  402. package/src/view/app/components/CodeView.tsx +0 -310
  403. package/src/view/app/components/CopyControls.tsx +0 -106
  404. package/src/view/app/components/Trace.tsx +0 -100
  405. package/src/view/app/components/Transcript.tsx +0 -157
  406. package/src/view/app/components/ui/badge.tsx +0 -21
  407. package/src/view/app/lib/artifact-url.ts +0 -17
  408. package/src/view/app/lib/attempt-route.test.ts +0 -80
  409. package/src/view/app/lib/attempt-route.ts +0 -52
  410. package/src/view/app/lib/format.ts +0 -70
  411. package/src/view/app/lib/guards.test.ts +0 -108
  412. package/src/view/app/lib/guards.ts +0 -71
  413. package/src/view/app/lib/rows.ts +0 -22
  414. package/src/view/app/lib/transcript-data.tsx +0 -151
  415. package/src/view/app/lib/verdict.ts +0 -23
  416. package/src/view/app/shared.ts +0 -8
  417. /package/dist/report/{react → components/metric-views}/chart-math.d.ts +0 -0
  418. /package/dist/report/{react → components/metric-views}/chart-math.js +0 -0
  419. /package/dist/report/{text → components/metric-views}/plot.d.ts +0 -0
  420. /package/dist/report/{react → components/site-components}/PoweredBy.d.ts +0 -0
  421. /package/dist/report/{react → components/site-components}/PoweredBy.js +0 -0
  422. /package/dist/report/{text/layout.d.ts → model/text-layout.d.ts} +0 -0
  423. /package/dist/report/{text/layout.js → model/text-layout.js} +0 -0
  424. /package/dist/report/{types.js → model/types.js} +0 -0
  425. /package/src/report/{react → components/metric-views}/chart-math.test.ts +0 -0
  426. /package/src/report/{react → components/metric-views}/chart-math.ts +0 -0
  427. /package/src/report/{react → components/site-components}/PoweredBy.tsx +0 -0
  428. /package/src/report/{text/layout.ts → model/text-layout.ts} +0 -0
@@ -12,7 +12,13 @@ import { encodeAttemptLocator } from "../results/locator.ts";
12
12
  import { equals } from "../expect/index.ts";
13
13
  import { createFeedbackCoordinator, type FeedbackCoordinator } from "./feedback/coordinator.ts";
14
14
  import { createFakeFeedbackIO } from "./feedback/testing.ts";
15
- import { activeFeedbackSinkCount } from "./feedback/sink.ts";
15
+ import {
16
+ activateFeedbackSink,
17
+ activeFeedbackSinkCount,
18
+ type ExperimentHookInput,
19
+ type ExperimentProgressInput,
20
+ } from "./feedback/sink.ts";
21
+ import { drainExperimentTeardowns, pendingExperimentTeardownCount } from "./experiment-cleanup-registry.ts";
16
22
  import type { CapturedEvalSource } from "./eval-source.ts";
17
23
  import type { CarryPlan } from "./fingerprint.ts";
18
24
  import type { AgentRun, RunFeedbackPlan, RunOptions } from "./types.ts";
@@ -251,7 +257,7 @@ describe("runEvals · fresh EvalResult.locator 在 reporter 观察到之前已
251
257
  earlyExit: true,
252
258
  sandbox: fakeSandboxSpec(),
253
259
  timeoutMs: 5_000,
254
- evalFilter: () => true,
260
+ selectedEvalIds: ["ok", "boom"],
255
261
  experimentId,
256
262
  };
257
263
 
@@ -281,7 +287,7 @@ describe("runEvals · fresh EvalResult.locator 在 reporter 观察到之前已
281
287
  earlyExit: true,
282
288
  sandbox: fakeSandboxSpec(),
283
289
  timeoutMs: 5_000,
284
- evalFilter: () => true,
290
+ selectedEvalIds: ["algebra/q1"],
285
291
  experimentId,
286
292
  });
287
293
 
@@ -310,7 +316,7 @@ describe("runEvals · fresh EvalResult.locator 在 reporter 观察到之前已
310
316
  earlyExit: false, // 两次都要真的跑,不能被首过即停吞掉其中一次
311
317
  sandbox: fakeSandboxSpec(),
312
318
  timeoutMs: 5_000,
313
- evalFilter: () => true,
319
+ selectedEvalIds: ["flaky"],
314
320
  experimentId,
315
321
  };
316
322
 
@@ -358,7 +364,7 @@ describe("runEvals · fresh EvalResult.locator 在 reporter 观察到之前已
358
364
  earlyExit: true,
359
365
  sandbox: fakeSandboxSpec(),
360
366
  timeoutMs: 5_000,
361
- evalFilter: () => true,
367
+ selectedEvalIds: [evalId],
362
368
  experimentId,
363
369
  };
364
370
 
@@ -410,7 +416,7 @@ describe("runEvals · fresh EvalResult.locator 在 reporter 观察到之前已
410
416
  earlyExit: true,
411
417
  sandbox: fakeSandboxSpec(),
412
418
  timeoutMs: 5_000,
413
- evalFilter: () => true,
419
+ selectedEvalIds: ["c-slow", "c-mid", "c-fast"],
414
420
  experimentId,
415
421
  };
416
422
  const completionOrder: string[] = [];
@@ -487,7 +493,7 @@ describe("runEvals · failure 永久事件在真实失败/errored attempt 上被
487
493
  earlyExit: true,
488
494
  sandbox: fakeSandboxSpec(),
489
495
  timeoutMs: 5_000,
490
- evalFilter: () => true,
496
+ selectedEvalIds: ["ok", "boom", "gate-fail"],
491
497
  experimentId,
492
498
  };
493
499
  const plan: RunFeedbackPlan = {
@@ -555,7 +561,7 @@ describe("runEvals · budget-exhausted 永久事件按每个被跳过的 attempt
555
561
  earlyExit: false,
556
562
  sandbox: fakeSandboxSpec(),
557
563
  timeoutMs: 5_000,
558
- evalFilter: () => true,
564
+ selectedEvalIds: ["a", "b", "c"],
559
565
  experimentId,
560
566
  budget: 0, // 花费从 0 起算,>= budget 恒成立——每个 attempt 在 preflight 就被跳过。
561
567
  };
@@ -604,7 +610,7 @@ describe("runEvals · budget-unenforceable 只统计真正发起过 agent turn
604
610
  earlyExit: false,
605
611
  sandbox: missingTemplate,
606
612
  timeoutMs: 5_000,
607
- evalFilter: () => true,
613
+ selectedEvalIds: ["a", "b", "c"],
608
614
  experimentId,
609
615
  budget: 10,
610
616
  };
@@ -637,7 +643,7 @@ describe("runEvals · budget-unenforceable 只统计真正发起过 agent turn
637
643
  earlyExit: false,
638
644
  sandbox: fakeSandboxSpec(),
639
645
  timeoutMs: 5_000,
640
- evalFilter: () => true,
646
+ selectedEvalIds: ["a", "b", "c"],
641
647
  experimentId,
642
648
  budget: 10,
643
649
  };
@@ -703,7 +709,7 @@ describe("runEvals · 携入数量少于本次请求的 runs 时,差额必须真
703
709
  earlyExit: true,
704
710
  sandbox: fakeSandboxSpec(),
705
711
  timeoutMs: 5_000,
706
- evalFilter: () => true,
712
+ selectedEvalIds: [evalId],
707
713
  experimentId,
708
714
  };
709
715
  const plan: RunFeedbackPlan = {
@@ -767,7 +773,7 @@ describe("runEvals · 携入数量少于本次请求的 runs 时,差额必须真
767
773
  earlyExit: true, // failed 不触发 earlyExit(只有 passed/errored 会),回填的两次应该真的跑
768
774
  sandbox: fakeSandboxSpec(),
769
775
  timeoutMs: 5_000,
770
- evalFilter: () => true,
776
+ selectedEvalIds: [evalId],
771
777
  experimentId,
772
778
  };
773
779
  const plan: RunFeedbackPlan = {
@@ -805,3 +811,375 @@ describe("runEvals · 携入数量少于本次请求的 runs 时,差额必须真
805
811
  });
806
812
  });
807
813
  });
814
+
815
+ // ───────────────────────── 实验级生命周期(ExperimentDef.setup / .teardown) ─────────────────────────
816
+ // 契约见 docs/feature/experiments/architecture.md「实验级生命周期」与 docs/runner.md「环境预置不进
817
+ // 运行器,但按顺序调它」:成对 setup/teardown,setup 不返回值——teardown 是独立字段,当且仅当
818
+ // 同层 setup 时点走到过才触发(setup 抛错不豁免、未声明 setup 不影响触发、时点没走到则跳过);
819
+ // setup 抛错 → 本实验所有 attempt 逐条合成 errored;teardown 抛错只作运行级诊断。
820
+
821
+ describe("runEvals · 实验级 setup/teardown", () => {
822
+ function runWithHooks(
823
+ experimentId: string,
824
+ setup: AgentRun["setup"],
825
+ teardown: AgentRun["teardown"],
826
+ overrides: Partial<AgentRun> = {},
827
+ ): AgentRun {
828
+ return {
829
+ agent: makeAgent(`agent-${experimentId}`),
830
+ flags: {},
831
+ runs: 1,
832
+ earlyExit: true,
833
+ sandbox: fakeSandboxSpec(),
834
+ timeoutMs: 5_000,
835
+ selectedEvalIds: [],
836
+ experimentId,
837
+ setup,
838
+ teardown,
839
+ ...overrides,
840
+ };
841
+ }
842
+
843
+ it("setup 整场恰好一次:并发 attempt 共享 memoized 结果,teardown 在全部 attempt 收尾后恰好一次", async () => {
844
+ let setupCalls = 0;
845
+ let teardownCalls = 0;
846
+ let completedAtTeardown = -1;
847
+ let completed = 0;
848
+ const evals = ["a", "b", "c"].map((id) =>
849
+ makeEval(id, () => {
850
+ completed += 1;
851
+ }),
852
+ );
853
+ const agentRun = runWithHooks(
854
+ "lifecycle-exp",
855
+ async () => {
856
+ setupCalls += 1;
857
+ // 给并发的其它 attempt 一个真实的等待窗口,验证它们不各自重跑 setup
858
+ await new Promise((r) => setTimeout(r, 20));
859
+ },
860
+ () => {
861
+ teardownCalls += 1;
862
+ completedAtTeardown = completed;
863
+ },
864
+ { runs: 2, selectedEvalIds: ["a", "b", "c"] },
865
+ );
866
+
867
+ const { summary } = await run(evals, [agentRun], { maxConcurrency: 4 });
868
+
869
+ expect(setupCalls).toBe(1);
870
+ expect(teardownCalls).toBe(1);
871
+ expect(summary.results).toHaveLength(6);
872
+ expect(summary.results.every((r) => r.verdict === "passed")).toBe(true);
873
+ // teardown 必须晚于本实验全部 attempt 的执行(runs:2 但 earlyExit 会省略第二轮,
874
+ // 至少 3 条 eval 各完成一次)
875
+ expect(completedAtTeardown).toBeGreaterThanOrEqual(3);
876
+ });
877
+
878
+ it("两个实验各自的 setup/teardown 各跑一次,互不共享", async () => {
879
+ const calls: string[] = [];
880
+ const evals = [makeEval("shared", () => {})];
881
+ const mk = (id: string) =>
882
+ runWithHooks(
883
+ id,
884
+ () => {
885
+ calls.push(`setup:${id}`);
886
+ },
887
+ () => {
888
+ calls.push(`teardown:${id}`);
889
+ },
890
+ { selectedEvalIds: ["shared"] },
891
+ );
892
+
893
+ await run(evals, [mk("exp-a"), mk("exp-b")]);
894
+
895
+ expect(calls.filter((c) => c === "setup:exp-a")).toHaveLength(1);
896
+ expect(calls.filter((c) => c === "setup:exp-b")).toHaveLength(1);
897
+ expect(calls.filter((c) => c === "teardown:exp-a")).toHaveLength(1);
898
+ expect(calls.filter((c) => c === "teardown:exp-b")).toHaveLength(1);
899
+ });
900
+
901
+ it("全部结果被 carry 携入、无 attempt 派发时 setup 与 teardown 都不执行", async () => {
902
+ let setupCalls = 0;
903
+ let teardownCalls = 0;
904
+ const experimentId = "carry-exp";
905
+ const evalDef = makeEval("done", () => {});
906
+ const agentRun = runWithHooks(
907
+ experimentId,
908
+ () => {
909
+ setupCalls += 1;
910
+ },
911
+ () => {
912
+ teardownCalls += 1;
913
+ },
914
+ { selectedEvalIds: ["done"] },
915
+ );
916
+ const carried: EvalResult = {
917
+ id: "done",
918
+ experimentId,
919
+ agent: agentRun.agent.name,
920
+ verdict: "passed",
921
+ attempt: 0,
922
+ durationMs: 1,
923
+ assertions: [],
924
+ };
925
+
926
+ const { summary } = await run([evalDef], [agentRun], {
927
+ carryPlan: {
928
+ plannedFingerprints: new Map(),
929
+ priorRunKeys: new Set([`${experimentId}|done`]),
930
+ carriedResults: [carried],
931
+ },
932
+ });
933
+
934
+ expect(setupCalls).toBe(0);
935
+ expect(teardownCalls).toBe(0);
936
+ expect(summary.results).toHaveLength(1);
937
+ expect(summary.results[0]!.verdict).toBe("passed");
938
+ });
939
+
940
+ it("setup 抛错:本实验所有 attempt 合成 errored(code/phase 结构化),同批其它实验不受影响", async () => {
941
+ const evals = [makeEval("m1", () => {}), makeEval("m2", () => {})];
942
+ const broken = runWithHooks(
943
+ "broken-exp",
944
+ () => {
945
+ throw new Error("tunnel refused to start");
946
+ },
947
+ undefined,
948
+ { runs: 2, earlyExit: false, selectedEvalIds: ["m1", "m2"] },
949
+ );
950
+ const healthy = runWithHooks("healthy-exp", () => {}, undefined, { selectedEvalIds: ["m1", "m2"] });
951
+
952
+ const { summary } = await run(evals, [broken, healthy], { maxConcurrency: 4 });
953
+
954
+ const brokenResults = summary.results.filter((r) => r.experimentId === "broken-exp");
955
+ // 2 eval × runs 2 全部逐条 errored——setup 失败不派发 agent、零成本,不被 fail-fast 截短
956
+ expect(brokenResults).toHaveLength(4);
957
+ for (const r of brokenResults) {
958
+ expect(r.verdict).toBe("errored");
959
+ expect(r.error).toMatchObject({ code: "experiment-setup-failed", phase: "experiment.setup" });
960
+ expect(r.error?.message).toContain("tunnel refused to start");
961
+ expect(r.locator).toBeDefined();
962
+ }
963
+ const healthyResults = summary.results.filter((r) => r.experimentId === "healthy-exp");
964
+ expect(healthyResults).toHaveLength(2);
965
+ expect(healthyResults.every((r) => r.verdict === "passed")).toBe(true);
966
+ });
967
+
968
+ it("setup 抛错后 teardown 仍执行:半初始化现场同样要扫尾,setup 抛错不豁免", async () => {
969
+ let teardownCalls = 0;
970
+ const evals = [makeEval("m1", () => {}), makeEval("m2", () => {})];
971
+ const broken = runWithHooks(
972
+ "broken-with-teardown-exp",
973
+ () => {
974
+ throw new Error("tunnel refused to start");
975
+ },
976
+ () => {
977
+ teardownCalls += 1;
978
+ },
979
+ { runs: 2, earlyExit: false, selectedEvalIds: ["m1", "m2"] },
980
+ );
981
+
982
+ const { summary } = await run(evals, [broken], { maxConcurrency: 4 });
983
+
984
+ expect(teardownCalls).toBe(1);
985
+ expect(summary.results).toHaveLength(4);
986
+ expect(summary.results.every((r) => r.verdict === "errored")).toBe(true);
987
+ });
988
+
989
+ it("运行被中断(signal abort)时 teardown 仍执行", async () => {
990
+ let teardownCalls = 0;
991
+ const controller = new AbortController();
992
+ const evalDef = makeEval("abort-me", async () => {
993
+ controller.abort();
994
+ await new Promise((r) => setTimeout(r, 100));
995
+ });
996
+ const agentRun = runWithHooks(
997
+ "interrupted-exp",
998
+ () => {},
999
+ () => {
1000
+ teardownCalls += 1;
1001
+ },
1002
+ { selectedEvalIds: ["abort-me"] },
1003
+ );
1004
+
1005
+ await run([evalDef], [agentRun], { signal: controller.signal });
1006
+
1007
+ expect(teardownCalls).toBe(1);
1008
+ });
1009
+
1010
+ it("ctx 携带 experimentId / selectedEvalIds / signal;未声明 teardown 时无收尾动作、也不产生诊断", async () => {
1011
+ let seen: { experimentId: string; selectedEvalIds: readonly string[]; hasSignal: boolean } | undefined;
1012
+ const controller = new AbortController();
1013
+ const evals = [makeEval("ctx-a", () => {}), makeEval("ctx-b", () => {})];
1014
+ const experimentId = "ctx-exp";
1015
+ const agentRun = runWithHooks(
1016
+ experimentId,
1017
+ (ctx) => {
1018
+ seen = {
1019
+ experimentId: ctx.experimentId,
1020
+ selectedEvalIds: ctx.selectedEvalIds,
1021
+ hasSignal: ctx.signal !== undefined,
1022
+ };
1023
+ ctx.progress({ message: "warming" });
1024
+ },
1025
+ undefined,
1026
+ { selectedEvalIds: ["ctx-a", "ctx-b"] },
1027
+ );
1028
+ const plan: RunFeedbackPlan = {
1029
+ shape: { evals: 2, configs: 1, totalRuns: 2, maxConcurrency: 3 },
1030
+ reused: 0,
1031
+ reusedFailures: [],
1032
+ };
1033
+
1034
+ await withCoordinator(plan, async (coordinator) => {
1035
+ const { summary } = await run(evals, [agentRun], { signal: controller.signal });
1036
+
1037
+ expect(seen).toEqual({ experimentId: "ctx-exp", selectedEvalIds: ["ctx-a", "ctx-b"], hasSignal: true });
1038
+ expect(summary.results.every((r) => r.verdict === "passed")).toBe(true);
1039
+ expect(coordinator.state.diagnostics.find((d) => d.key === `experiment-teardown-failed:${experimentId}`)).toBeUndefined();
1040
+ });
1041
+ });
1042
+
1043
+ it("钩子起止经 feedback sink 发布:成功 started+done、抛错 started+failed(done/failed 带耗时),progress 压成运行级 detail", async () => {
1044
+ const hookEvents: ExperimentHookInput[] = [];
1045
+ const progressEvents: ExperimentProgressInput[] = [];
1046
+ const deactivate = activateFeedbackSink({
1047
+ activity() {},
1048
+ diagnostic() {},
1049
+ interrupted() {},
1050
+ reporterError() {},
1051
+ failure() {},
1052
+ budgetExhausted() {},
1053
+ kept() {},
1054
+ experimentHook(input) {
1055
+ hookEvents.push(input);
1056
+ },
1057
+ experimentProgress(input) {
1058
+ progressEvents.push(input);
1059
+ },
1060
+ lifecycle() {},
1061
+ });
1062
+ try {
1063
+ const evals = [makeEval("ok", () => {})];
1064
+ const good = runWithHooks(
1065
+ "good-exp",
1066
+ (ctx) => {
1067
+ ctx.progress({ message: "starting tunnel", current: 2, total: 5 });
1068
+ },
1069
+ () => {},
1070
+ { selectedEvalIds: ["ok"] },
1071
+ );
1072
+ const bad = runWithHooks(
1073
+ "bad-exp",
1074
+ () => {
1075
+ throw new Error("boom");
1076
+ },
1077
+ undefined,
1078
+ { selectedEvalIds: ["ok"] },
1079
+ );
1080
+ await run(evals, [good, bad], { maxConcurrency: 2 });
1081
+ } finally {
1082
+ deactivate();
1083
+ }
1084
+
1085
+ const good = hookEvents.filter((e) => e.experimentId === "good-exp");
1086
+ expect(good.map((e) => `${e.hook}:${e.status}`)).toEqual([
1087
+ "setup:started",
1088
+ "setup:done",
1089
+ "teardown:started",
1090
+ "teardown:done",
1091
+ ]);
1092
+ expect(good[0]!.durationMs).toBeUndefined();
1093
+ expect(good[1]!.durationMs).toBeGreaterThanOrEqual(0);
1094
+ const bad = hookEvents.filter((e) => e.experimentId === "bad-exp").map((e) => `${e.hook}:${e.status}`);
1095
+ expect(bad).toEqual(["setup:started", "setup:failed"]);
1096
+ expect(progressEvents).toContainEqual({ experimentId: "good-exp", detail: "starting tunnel (2/5)" });
1097
+ });
1098
+ });
1099
+
1100
+ describe("runEvals · 实验级 teardown 失败只作运行级诊断", () => {
1101
+ afterEach(() => {
1102
+ expect(activeFeedbackSinkCount()).toBe(0);
1103
+ });
1104
+
1105
+ // bug: memory/force-exit-skips-experiment-teardown.md
1106
+ it("正常完整跑完后强清兜底注册表为空:teardown 已被运行路径消费恰好一次,drain 无动作", async () => {
1107
+ let teardownCalls = 0;
1108
+ const evalDef = makeEval("tidy", () => {});
1109
+ const agentRun: AgentRun = {
1110
+ agent: makeAgent("agent-registry"),
1111
+ flags: {},
1112
+ runs: 1,
1113
+ earlyExit: true,
1114
+ sandbox: fakeSandboxSpec(),
1115
+ timeoutMs: 5_000,
1116
+ selectedEvalIds: ["tidy"],
1117
+ experimentId: "registry-exp",
1118
+ setup: () => {},
1119
+ teardown: () => {
1120
+ teardownCalls += 1;
1121
+ },
1122
+ };
1123
+
1124
+ await run([evalDef], [agentRun]);
1125
+
1126
+ expect(teardownCalls).toBe(1);
1127
+ expect(pendingExperimentTeardownCount()).toBe(0);
1128
+ expect(await drainExperimentTeardowns()).toBe(0);
1129
+ expect(teardownCalls).toBe(1);
1130
+ });
1131
+
1132
+ it("teardown 抛错:verdict 不变,产生 experiment-teardown-failed 诊断", async () => {
1133
+ const experimentId = "leaky-exp";
1134
+ const evalDef = makeEval("ok", () => {});
1135
+ const agentRun: AgentRun = {
1136
+ agent: makeAgent("agent-leaky"),
1137
+ flags: {},
1138
+ runs: 1,
1139
+ earlyExit: true,
1140
+ sandbox: fakeSandboxSpec(),
1141
+ timeoutMs: 5_000,
1142
+ selectedEvalIds: ["ok"],
1143
+ experimentId,
1144
+ setup: () => {},
1145
+ teardown: () => {
1146
+ throw new Error("port already released");
1147
+ },
1148
+ };
1149
+ const plan: RunFeedbackPlan = {
1150
+ shape: { evals: 1, configs: 1, totalRuns: 1, maxConcurrency: 3 },
1151
+ reused: 0,
1152
+ reusedFailures: [],
1153
+ };
1154
+
1155
+ await withCoordinator(plan, async (coordinator) => {
1156
+ const { summary } = await run([evalDef], [agentRun]);
1157
+
1158
+ expect(summary.results).toHaveLength(1);
1159
+ expect(summary.results[0]!.verdict).toBe("passed");
1160
+ const diag = coordinator.state.diagnostics.find((d) => d.key === `experiment-teardown-failed:${experimentId}`);
1161
+ expect(diag).toBeDefined();
1162
+ expect(diag!.severity).toBe("warning");
1163
+ expect(diag!.message).toContain("port already released");
1164
+ });
1165
+ });
1166
+ });
1167
+
1168
+ describe("computeFingerprint · 实验级钩子不进 fingerprint", () => {
1169
+ it("只改 setup / teardown 函数体不改变 fingerprint(改钩子要重跑用 --force,与 sandbox 钩子同规则)", async () => {
1170
+ const evalDef = makeEval("fp", () => {});
1171
+ const base: AgentRun = {
1172
+ agent: makeAgent("agent-fp"),
1173
+ flags: {},
1174
+ runs: 1,
1175
+ earlyExit: true,
1176
+ timeoutMs: 5_000,
1177
+ selectedEvalIds: ["fp"],
1178
+ experimentId: "fp-exp",
1179
+ };
1180
+ const withHook: AgentRun = { ...base, setup: () => {}, teardown: () => {} };
1181
+
1182
+ const { computeFingerprint } = await import("./fingerprint.ts");
1183
+ expect(await computeFingerprint(evalDef, withHook)).toBe(await computeFingerprint(evalDef, base));
1184
+ });
1185
+ });