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
package/src/agents/bub.ts CHANGED
@@ -11,7 +11,7 @@ import {
11
11
  import { writeAgentSetupManifest } from "./manifest.ts";
12
12
  import { createCheckpoint, restoreCheckpoint } from "../sandbox/checkpoint.ts";
13
13
  import { mapBubSpans } from "../o11y/otlp/mappers/bub.ts";
14
- import { runPostSetupHooks } from "./post-setup.ts";
14
+ import { runPostSetupHooks, runPreTeardownHooks } from "./post-setup.ts";
15
15
  import type { Agent, AgentContext, AgentSetupManifest, Sandbox, SandboxHook, SkillSpec } from "../types.ts";
16
16
  import { createHash, randomUUID } from "node:crypto";
17
17
  import { readFile, writeFile, mkdir } from "node:fs/promises";
@@ -52,23 +52,30 @@ export interface BubConfig {
52
52
  /** OpenAI 兼容代理的 base URL。省略时读 BUB_API_BASE env。 */
53
53
  apiBase?: string;
54
54
  /**
55
- * 装进沙箱的 Skill(本地目录/文件,或 repo + 可钉 ref + 可选启用集)。
55
+ * 装进 Sandbox 的 Skill(本地目录/文件,或 repo + 可钉 ref + 可选启用集)。
56
56
  * 落在 `.agents/skills/<name>/`,并写一段发现指引进 AGENTS.md(bub 没有原生 Skill 加载机制)。
57
57
  */
58
58
  skills?: SkillSpec[];
59
59
  /**
60
- * 额外装进 bub tool 环境的 Python Package,每个沙箱 setup 时进 `uv tool install … --with <pkg>`。
60
+ * 额外装进 bub tool 环境的 Python Package,每个 Sandbox setup 时进 `uv tool install … --with <pkg>`。
61
61
  * 规范化后的 package 列表进安装 checkpoint key:plugin 集合不同的两个 agent 变体不会复用同一个
62
62
  * 安装 checkpoint(否则第二个变体会静默拿到第一个变体的环境)。
63
63
  */
64
64
  pythonPlugins?: PythonPluginSpec[];
65
65
  /**
66
- * 安装后按数组顺序运行的用户钩子(复用 SandboxHook 的窄上下文):在装 bub、装 Skills /
67
- * Python package、写 manifest 全部完成后执行。钩子返回的 cleanup 按 LIFO 与 teardown 一起
68
- * 收尾;抛错按基础设施错误计(attempt errored)。
66
+ * 安装后按数组顺序运行的用户 Hook(复用 SandboxHook 的窄上下文):在装 bub、装 Skills /
67
+ * Python package、写 manifest 全部完成后执行。抛错按基础设施错误计(attempt errored)。
69
68
  * 见 docs/feature/adapters/library/coding-agent-extensions.md「安装后运行脚本」。
70
69
  */
71
70
  postSetup?: SandboxHook[];
71
+ /**
72
+ * 与 `postSetup` 成对的收尾 Hook:按 `postSetup` 的逆序语义,在 agent 自己的 teardown 步骤
73
+ * 之前执行(LIFO 镜像 —— `postSetup` 跑在 agent 安装之后,`preTeardown` 就跑在 agent 收尾
74
+ * 之前),当且仅当 `postSetup` 的时点走到过才触发。抛错按基础设施错误计,由 teardown 段
75
+ * 按 teardown-failed 诊断收束。
76
+ * 见 docs/feature/adapters/library/coding-agent-extensions.md「安装后运行脚本」。
77
+ */
78
+ preTeardown?: SandboxHook[];
72
79
  }
73
80
 
74
81
  const UV = "$HOME/.local/bin/uv";
@@ -288,8 +295,14 @@ export function bubAgent(config?: BubConfig): Agent {
288
295
  }
289
296
 
290
297
  // 安装后钩子(postSetup):排在 manifest 之后——manifest 审计 Adapter 自身的安装事实,
291
- // 钩子失败不该丢掉这份证据。返回的合成 cleanup 交给 runner,与 teardown 一起 LIFO 收尾。
292
- return await runPostSetupHooks(sb, ctx, config?.postSetup);
298
+ // 钩子失败不该丢掉这份证据。
299
+ await runPostSetupHooks(sb, ctx, config?.postSetup);
300
+ },
301
+
302
+ async teardown(sb, ctx) {
303
+ // preTeardown 与 postSetup 成对:LIFO 镜像,先于 agent 自己的收尾步骤执行。
304
+ // bub 目前没有其它收尾步骤,这段就是整个 teardown。
305
+ await runPreTeardownHooks(sb, ctx, config?.preTeardown);
293
306
  },
294
307
 
295
308
  async send(input, ctx) {
@@ -15,7 +15,7 @@ import { mapClaudeCodeSpans } from "../o11y/otlp/mappers/claude-code.ts";
15
15
  import { t } from "../i18n/index.ts";
16
16
  import { DEFAULT_CLAUDE_CODE_CLI_VERSION } from "./coding-cli-versions.ts";
17
17
  import { assertMcpServers, isHttpMcp, mcpManifestEntries } from "./mcp.ts";
18
- import { runPostSetupHooks } from "./post-setup.ts";
18
+ import { runPostSetupHooks, runPreTeardownHooks } from "./post-setup.ts";
19
19
  import type { Agent, AgentSetupManifest, McpServer, Sandbox, SandboxHook, SkillSpec } from "../types.ts";
20
20
 
21
21
  // ───────────────────────────────────────────────────────────────────────────
@@ -66,17 +66,17 @@ export interface ClaudeCodeConfig {
66
66
  baseUrl?: string;
67
67
  /**
68
68
  * 最多跑几个 tool-use 轮次(→ `--max-turns`)。
69
- * 控制 eval 成本上限;省略时用 CLI 原生默认(无限制)。
69
+ * 控制评估用例成本上限;省略时用 CLI 原生默认(无限制)。
70
70
  */
71
71
  maxTurns?: number;
72
72
  /**
73
- * 额外 MCP server(每个沙箱 setup 时写进用户级 ~/.claude.json)。
73
+ * 额外 MCP server(每个 Sandbox setup 时写进用户级 ~/.claude.json)。
74
74
  * stdio 形态写 command(可带 args / env);Streamable HTTP 形态写 url(可带 headers,
75
75
  * 逐字进请求头),落成 { "type": "http", "url": …, "headers": … } 条目。
76
76
  */
77
77
  mcpServers?: McpServer[];
78
78
  /**
79
- * 装进沙箱的 Skill(本地目录/文件,或 repo + 可钉 ref + 可选启用集)。
79
+ * 装进 Sandbox 的 Skill(本地目录/文件,或 repo + 可钉 ref + 可选启用集)。
80
80
  * 落在 project 级 `.claude/skills/<name>/`,claude CLI 原生发现。
81
81
  */
82
82
  skills?: SkillSpec[];
@@ -86,19 +86,26 @@ export interface ClaudeCodeConfig {
86
86
  * 一份完整的 Claude Code `settings.json`(官方格式)在本地项目里的路径 —— 相对运行
87
87
  * niceeval 的项目根(含 `niceeval.config.ts` 的目录)解析,不是 Sandbox 内路径;只接受
88
88
  * 项目根内的相对路径,包含 `..` 的路径、绝对路径、`~` 路径和解析后逃出项目根的符号链接
89
- * 都在 setup 阶段报错。原始字节原样上传为沙箱里原本为空的用户级 `~/.claude/settings.json`
89
+ * 都在 setup 阶段报错。原始字节原样上传为 Sandbox 里原本为空的用户级 `~/.claude/settings.json`
90
90
  * (不继承宿主机配置、不拼接、不重新序列化);保留键 `model` 与 `env` 出现在文件里
91
91
  * setup 报错。manifest 只记项目相对路径与字节 SHA-256,不落正文。
92
92
  */
93
93
  settingsFile?: string;
94
94
  /**
95
- * 安装后按数组顺序运行的用户钩子(复用 SandboxHook 的窄上下文):在写 settings、挂 MCP、
95
+ * 安装后按数组顺序运行的用户 Hook(复用 SandboxHook 的窄上下文):在写 settings、挂 MCP、
96
96
  * 装 Skills / Plugin、写 manifest 全部完成后执行,适合跑插件自带的 setup 脚本这类
97
- * 「安装产物就位后才能跑」的过程动作。钩子返回的 cleanup 按 LIFO 与 teardown 一起收尾;
98
- * 抛错按基础设施错误计(attempt errored)。
97
+ * 「安装产物就位后才能跑」的过程动作。抛错按基础设施错误计(attempt errored)。
99
98
  * 见 docs/feature/adapters/library/coding-agent-extensions.md「安装后运行脚本」。
100
99
  */
101
100
  postSetup?: SandboxHook[];
101
+ /**
102
+ * 与 `postSetup` 成对的收尾 Hook:按 `postSetup` 的逆序语义,在 agent 自己的 teardown 步骤
103
+ * 之前执行(LIFO 镜像 —— `postSetup` 跑在 agent 安装之后,`preTeardown` 就跑在 agent 收尾
104
+ * 之前),当且仅当 `postSetup` 的时点走到过才触发。抛错按基础设施错误计,由 teardown 段
105
+ * 按 teardown-failed 诊断收束。
106
+ * 见 docs/feature/adapters/library/coding-agent-extensions.md「安装后运行脚本」。
107
+ */
108
+ preTeardown?: SandboxHook[];
102
109
  }
103
110
 
104
111
  export function claudeCodeAgent(config?: ClaudeCodeConfig): Agent {
@@ -197,8 +204,14 @@ export function claudeCodeAgent(config?: ClaudeCodeConfig): Agent {
197
204
  }
198
205
 
199
206
  // 安装后钩子(postSetup):排在 manifest 之后——manifest 审计 Adapter 自身的安装事实,
200
- // 钩子失败不该丢掉这份证据。返回的合成 cleanup 交给 runner,与 teardown 一起 LIFO 收尾。
201
- return await runPostSetupHooks(sb, ctx, config?.postSetup);
207
+ // 钩子失败不该丢掉这份证据。
208
+ await runPostSetupHooks(sb, ctx, config?.postSetup);
209
+ },
210
+
211
+ async teardown(sb, ctx) {
212
+ // preTeardown 与 postSetup 成对:LIFO 镜像,先于 agent 自己的收尾步骤执行。
213
+ // claude-code 目前没有其它收尾步骤,这段就是整个 teardown。
214
+ await runPreTeardownHooks(sb, ctx, config?.preTeardown);
202
215
  },
203
216
 
204
217
  async send(input, ctx) {
@@ -479,17 +479,32 @@ describe("codexAgent postSetup · 安装后钩子", () => {
479
479
  expect(bIdx).toBeGreaterThan(aIdx);
480
480
  });
481
481
 
482
- it("钩子返回的 cleanup 合成一个闭包交还 runner,按 LIFO 执行", async () => {
482
+ it("preTeardown postSetup 成对,按逆序(LIFO)在 teardown 时点执行", async () => {
483
483
  const box = sb();
484
484
  const order: string[] = [];
485
- const cleanup = await codexAgent({
485
+ const agent = codexAgent({
486
486
  apiKey: "k",
487
- postSetup: [() => () => void order.push("a"), () => () => void order.push("b")],
488
- }).setup!(asSandbox(box), mkCtx());
487
+ postSetup: [async () => void order.push("setup:a"), async () => void order.push("setup:b")],
488
+ preTeardown: [async () => void order.push("teardown:a"), async () => void order.push("teardown:b")],
489
+ });
490
+
491
+ await agent.setup!(asSandbox(box), mkCtx());
492
+ await agent.teardown!(asSandbox(box), mkCtx());
493
+
494
+ expect(order).toEqual(["setup:a", "setup:b", "teardown:b", "teardown:a"]);
495
+ });
496
+
497
+ it("postSetup 时点未走到(setup 从未调用)时,preTeardown 不执行", async () => {
498
+ const box = sb();
499
+ const order: string[] = [];
500
+ const agent = codexAgent({
501
+ apiKey: "k",
502
+ preTeardown: [async () => void order.push("teardown:a")],
503
+ });
504
+
505
+ await agent.teardown!(asSandbox(box), mkCtx());
489
506
 
490
- expect(typeof cleanup).toBe("function");
491
- await (cleanup as () => Promise<void> | void)();
492
- expect(order).toEqual(["b", "a"]);
507
+ expect(order).toEqual([]);
493
508
  });
494
509
 
495
510
  it("反例:钩子抛错从 setup 传播(attempt errored 通道)", async () => {
@@ -20,7 +20,7 @@ import { mapCodexSpans } from "../o11y/otlp/mappers/codex.ts";
20
20
  import { t } from "../i18n/index.ts";
21
21
  import { DEFAULT_CODEX_CLI_VERSION } from "./coding-cli-versions.ts";
22
22
  import { assertMcpServers, isHttpMcp, mcpManifestEntries } from "./mcp.ts";
23
- import { runPostSetupHooks } from "./post-setup.ts";
23
+ import { runPostSetupHooks, runPreTeardownHooks } from "./post-setup.ts";
24
24
  import type { Agent, AgentSetupManifest, McpServer, Sandbox, SandboxHook, SkillSpec } from "../types.ts";
25
25
 
26
26
  // ───────────────────────────────────────────────────────────────────────────
@@ -78,13 +78,13 @@ export interface CodexConfig {
78
78
  /** OpenAI 兼容代理 base URL(如 https://s2a.example.com/v1)。省略时读 CODEX_BASE_URL env。 */
79
79
  baseUrl?: string;
80
80
  /**
81
- * 额外 MCP server(每个沙箱 setup 时追加进 ~/.codex/config.toml)。
81
+ * 额外 MCP server(每个 Sandbox setup 时追加进 ~/.codex/config.toml)。
82
82
  * stdio 形态(command/args/env)写 [mcp_servers.<name>] 的 command 行;
83
83
  * Streamable HTTP 形态(url/headers)写 url 行,headers 进 [mcp_servers.<name>.http_headers] 子表。
84
84
  */
85
85
  mcpServers?: McpServer[];
86
86
  /**
87
- * 装进沙箱的 Skill(本地目录/文件,或 repo + 可钉 ref + 可选启用集)。
87
+ * 装进 Sandbox 的 Skill(本地目录/文件,或 repo + 可钉 ref + 可选启用集)。
88
88
  * 落在 `.agents/skills/<name>/`,并写一段发现指引进 AGENTS.md —— codex 没有 Claude Code 那种
89
89
  * 原生 Skill 工具,只把文件装进去它不会自己去读(见 memory/codex-no-native-skill-tool.md)。
90
90
  */
@@ -95,20 +95,27 @@ export interface CodexConfig {
95
95
  * 一份完整的 Codex `config.toml`(官方 TOML 格式)在本地项目里的路径 —— 相对运行
96
96
  * niceeval 的项目根(含 `niceeval.config.ts` 的目录)解析,不是 Sandbox 内路径;只接受
97
97
  * 项目根内的相对路径,包含 `..` 的路径、绝对路径、`~` 路径和解析后逃出项目根的符号链接
98
- * 都在 setup 阶段报错。原始字节原样并入沙箱里原本为空的用户级 `~/.codex/config.toml`
98
+ * 都在 setup 阶段报错。原始字节原样并入 Sandbox 里原本为空的用户级 `~/.codex/config.toml`
99
99
  * (不继承宿主机配置、不解析后重写);保留键 `model`、`model_provider`、`model_providers`、
100
100
  * `model_reasoning_effort`、`mcp_servers`、`otel` 出现在文件里 setup 报错。manifest 只记
101
101
  * 项目相对路径与字节 SHA-256,不落正文。
102
102
  */
103
103
  configFile?: string;
104
104
  /**
105
- * 安装后按数组顺序运行的用户钩子(复用 SandboxHook 的窄上下文):在写主配置、挂 MCP、
105
+ * 安装后按数组顺序运行的用户 Hook(复用 SandboxHook 的窄上下文):在写主配置、挂 MCP、
106
106
  * 装 Skills / Plugin、写 manifest 全部完成后执行,适合跑插件自带的 setup 脚本这类
107
- * 「安装产物就位后才能跑」的过程动作。钩子返回的 cleanup 按 LIFO 与 teardown 一起收尾;
108
- * 抛错按基础设施错误计(attempt errored)。
107
+ * 「安装产物就位后才能跑」的过程动作。抛错按基础设施错误计(attempt errored)。
109
108
  * 见 docs/feature/adapters/library/coding-agent-extensions.md「安装后运行脚本」。
110
109
  */
111
110
  postSetup?: SandboxHook[];
111
+ /**
112
+ * 与 `postSetup` 成对的收尾 Hook:按 `postSetup` 的逆序语义,在 agent 自己的 teardown 步骤
113
+ * 之前执行(LIFO 镜像 —— `postSetup` 跑在 agent 安装之后,`preTeardown` 就跑在 agent 收尾
114
+ * 之前),当且仅当 `postSetup` 的时点走到过才触发。抛错按基础设施错误计,由 teardown 段
115
+ * 按 teardown-failed 诊断收束。
116
+ * 见 docs/feature/adapters/library/coding-agent-extensions.md「安装后运行脚本」。
117
+ */
118
+ preTeardown?: SandboxHook[];
112
119
  }
113
120
 
114
121
  export function codexAgent(config?: CodexConfig): Agent {
@@ -228,8 +235,14 @@ export function codexAgent(config?: CodexConfig): Agent {
228
235
  }
229
236
 
230
237
  // 安装后钩子(postSetup):排在 manifest 之后——manifest 审计 Adapter 自身的安装事实,
231
- // 钩子失败不该丢掉这份证据。返回的合成 cleanup 交给 runner,与 teardown 一起 LIFO 收尾。
232
- return await runPostSetupHooks(sb, ctx, config?.postSetup);
238
+ // 钩子失败不该丢掉这份证据。
239
+ await runPostSetupHooks(sb, ctx, config?.postSetup);
240
+ },
241
+
242
+ async teardown(sb, ctx) {
243
+ // preTeardown 与 postSetup 成对:LIFO 镜像,先于 agent 自己的收尾步骤执行。
244
+ // codex 目前没有其它收尾步骤,这段就是整个 teardown。
245
+ await runPreTeardownHooks(sb, ctx, config?.preTeardown);
233
246
  },
234
247
 
235
248
  tracing: {
@@ -12,7 +12,7 @@ export type { CoverageStatus, CoverageDeclaration, EvidenceCoverage } from "../t
12
12
  // span → canonical GenAI 归一(只服务瀑布图,不喂断言)。私有埋点写自己的 spanMapper 时用:
13
13
  // tagSpan 把判定写回 span(原属性只增不改),heuristicTag 是通用兜底判定;mapCodexSpans 是
14
14
  // 现成的参考实现(无侵入接 codex 后端时直接声明 `spanMapper: mapCodexSpans`)。
15
- // 映射目标(什么属性亮起瀑布图的什么)见 docs-site/zh/how-to/connect-otel.mdx「瀑布图画得准不准」。
15
+ // 映射目标(什么属性亮起瀑布图的什么)见 docs-site/zh/tutorials/connect-otel.mdx「瀑布图画得准不准」。
16
16
  export { tagSpan, heuristicTag } from "../o11y/otlp/canonical.ts";
17
17
  export type { SpanTag } from "../o11y/otlp/canonical.ts";
18
18
  export { mapCodexSpans } from "../o11y/otlp/mappers/codex.ts";
@@ -52,7 +52,7 @@ export type {
52
52
  export { fromLangGraphEvents } from "./langgraph.ts";
53
53
  export type { LangGraphEventLike, LangGraphContentBlockLike, LangGraphStream } from "./langgraph.ts";
54
54
 
55
- // 通用「拼装方式」件:逐帧驱动循环、逐 token/参数增量累加器。见 docs-site/zh/how-to/write-send.mdx——
55
+ // 通用「拼装方式」件:逐帧驱动循环、逐 token/参数增量累加器。见 docs-site/zh/tutorials/write-send.mdx——
56
56
  // 这些和任何具体协议无关,自己写 adapter 时优先拿这些拼,只有 transport(怎么发)与
57
57
  // 「帧类型 → 操作」这张映射表才是真正要手写的。会话续接与 HITL 停轮现场不再是可选件,
58
58
  // 而是 ctx.session(AgentSession)本身自带的存取器(history()/id+capture()、hold()/take())。
@@ -2,7 +2,7 @@
2
2
  // 表格里的 fromChatCompletion(res) / fromResponses(res)。和 fromAiSdk 同一先例:结构化
3
3
  // *Like 类型,不依赖 openai 包,兼容任何声明自己走这两种协议形状的服务(不止 OpenAI 官方)。
4
4
  //
5
- // 两种形状对负断言的可信度不同(见 docs-site/zh/how-to/write-send.mdx):
5
+ // 两种形状对负断言的可信度不同(见 docs-site/zh/tutorials/write-send.mdx):
6
6
  // · Chat Completions 不承诺「响应 = 完整过程」(应用可能在服务端跑完工具循环,只把最终
7
7
  // 答案给你),所以 notCalledTool 这类负断言只能当「没看到」,不能当「确实没发生」。
8
8
  // · Responses 的协议契约里 output 数组记录了模型这一轮决定做的全部事(包括每个
@@ -1,33 +1,56 @@
1
- // factory 的 postSetup 钩子执行器 —— Claude Code / Codex / Bub 共用。
1
+ // factory 的 postSetup / preTeardown 钩子执行器 —— Claude Code / Codex / Bub 共用。
2
2
  // 契约见 docs/feature/adapters/library/coding-agent-extensions.md「安装后运行脚本」:
3
- // 在 adapter 全部安装步骤(含 manifest)之后按数组顺序执行;复用 SandboxHook 的窄上下文;
4
- // 返回的 cleanup 合成一个 LIFO 闭包,由 runner agent teardown 一起收尾;钩子抛错
5
- // 直接传播(attempt errored)
3
+ // postSetup 在 adapter 全部安装步骤(含 manifest)之后按数组顺序执行;preTeardown 与它成对,
4
+ // 按逆序、先于 agent 自己的 teardown 步骤执行(LIFO 镜像 —— postSetup 跑在 agent 安装之后,
5
+ // preTeardown 跑在 agent 收尾之前)。两者复用 SandboxHook 的窄上下文,不消费钩子返回值。
6
+ // 钩子抛错直接传播:postSetup 处于 setup 阶段(attempt errored);preTeardown 处于 teardown
7
+ // 阶段,由 runner 的 teardown 段按 teardown-failed 诊断收束。
6
8
 
7
- import type { Cleanup } from "../shared/types.ts";
8
9
  import type { Sandbox, SandboxHook, SandboxHookContext } from "../sandbox/types.ts";
9
10
  import type { AgentContext } from "./types.ts";
10
11
 
11
- export async function runPostSetupHooks(
12
- sb: Sandbox,
13
- ctx: AgentContext,
14
- hooks: readonly SandboxHook[] | undefined,
15
- ): Promise<Cleanup | void> {
16
- if (!hooks?.length) return;
17
- // 窄上下文与沙箱钩子同款:不把 session / model / telemetry 借给过程钩子。
18
- const hookCtx: SandboxHookContext = {
12
+ /** 窄上下文与沙箱钩子同款:不把 session / model / telemetry 借给过程钩子。 */
13
+ function narrowHookContext(ctx: AgentContext): SandboxHookContext {
14
+ return {
19
15
  experimentId: ctx.experimentId,
20
16
  signal: ctx.signal,
21
17
  progress: (update) => ctx.progress(update),
22
18
  diagnostic: (input) => ctx.diagnostic(input),
23
19
  };
24
- const cleanups: Cleanup[] = [];
25
- for (const hook of hooks) {
26
- const cleanup = await hook(sb, hookCtx);
27
- if (typeof cleanup === "function") cleanups.push(cleanup);
28
- }
29
- if (!cleanups.length) return;
30
- return async () => {
31
- for (const cleanup of [...cleanups].reverse()) await cleanup();
32
- };
20
+ }
21
+
22
+ /**
23
+ * postSetup 时点已走到的沙箱集合——preTeardown 的触发条件(成对触发规则:当且仅当同层 setup
24
+ * 时点走到过)。按 sandbox 实例作键:并发 attempt 共享同一 factory 配置,沙箱是天然的
25
+ * per-attempt 键;WeakSet 随沙箱对象回收,不泄漏。
26
+ */
27
+ const postSetupPointReached = new WeakSet<Sandbox>();
28
+
29
+ /** 按数组顺序执行 postSetup 钩子。调用方在 adapter 全部安装步骤(含 manifest)之后调用一次;
30
+ * 即使数组为空也要调——它同时标记「postSetup 时点走到过」(preTeardown 的触发条件)。 */
31
+ export async function runPostSetupHooks(
32
+ sb: Sandbox,
33
+ ctx: AgentContext,
34
+ hooks: readonly SandboxHook[] | undefined,
35
+ ): Promise<void> {
36
+ postSetupPointReached.add(sb);
37
+ if (!hooks?.length) return;
38
+ const hookCtx = narrowHookContext(ctx);
39
+ for (const hook of hooks) await hook(sb, hookCtx);
40
+ }
41
+
42
+ /**
43
+ * 按逆序执行 preTeardown 钩子(LIFO 镜像 postSetup)。调用方在各自 `teardown` 方法的最前面
44
+ * 调用一次,先于 agent 自己的收尾步骤。当且仅当本沙箱的 postSetup 时点走到过才执行——
45
+ * adapter setup 在安装步骤中途抛错时,preTeardown 的成对前提不存在,静默跳过。
46
+ */
47
+ export async function runPreTeardownHooks(
48
+ sb: Sandbox,
49
+ ctx: AgentContext,
50
+ hooks: readonly SandboxHook[] | undefined,
51
+ ): Promise<void> {
52
+ if (!postSetupPointReached.has(sb)) return;
53
+ if (!hooks?.length) return;
54
+ const hookCtx = narrowHookContext(ctx);
55
+ for (const hook of [...hooks].reverse()) await hook(sb, hookCtx);
33
56
  }
@@ -1,7 +1,7 @@
1
1
  // 官方的「拼装方式」件:把一个手写 send 里反复出现的事收成可复用的小东西,
2
2
  // 而不是每接一个新后端就重写一遍循环 + Map + if/else。
3
3
  //
4
- // 背景(见 docs-site/zh/how-to/write-send.mdx):一轮交互里真正互不相干的事只有几类——
4
+ // 背景(见 docs-site/zh/tutorials/write-send.mdx):一轮交互里真正互不相干的事只有几类——
5
5
  // 1. 怎么把输入发出去(transport)——真做不掉,adapter 只写这个;
6
6
  // 2. 原始数据怎么变成 StreamEvent[]——按数据到达形状分「整段落地」(sdk-streams.ts 的
7
7
  // fromXxxEvents,已有)和「逐 token / 逐参数增量」(deltaStream,这里新增)两种官方 reducer;
@@ -2,7 +2,7 @@
2
2
  // 「连到哪个被测对象、协议怎么说」的全部契约在这里(见 docs-site/zh/explanation/adapter.mdx)。
3
3
  // 能力不再是问卷式声明:t 上解锁什么完全由构造证据决定(见 docs-site 「能力从哪来」一节)。
4
4
 
5
- import type { Cleanup, DiagnosticInput, ProgressUpdate } from "../shared/types.ts";
5
+ import type { DiagnosticInput, ProgressUpdate } from "../shared/types.ts";
6
6
  import type { StreamEvent, TraceSpan, Usage } from "../o11y/types.ts";
7
7
  import type { Sandbox } from "../sandbox/types.ts";
8
8
 
@@ -263,10 +263,10 @@ export interface AgentSession {
263
263
 
264
264
  export interface AgentContext {
265
265
  /**
266
- * 软取消信号:合并了 attempt 超时、run 级中断(用户 Ctrl+C)与 eval 自身的中断请求
266
+ * 软取消信号:合并了 attempt 超时、run 级中断(用户 Ctrl+C)与评估用例自身的中断请求
267
267
  * (见 src/runner/attempt.ts)。adapter 可以选择性检查它(或直接传给 `fetch`)以提前
268
268
  * 优雅退出,但这不是唯一的硬边界——即便 adapter 完全忽略它,运行器也会用
269
- * `Effect.timeoutTo` 兜底强制收尾(停沙箱容器)。
269
+ * `Effect.timeoutTo` 兜底强制收尾(停 Sandbox 容器)。
270
270
  */
271
271
  readonly signal: AbortSignal;
272
272
  /** 本次 attempt 用的模型名,透传自 experiment 的 `model` 字段。sandbox 型 agent 通常在 setup 里用它写配置,remote 型通常在 send 里用它选模型。 */
@@ -283,13 +283,13 @@ export interface AgentContext {
283
283
  /**
284
284
  * 路径推导出的实验 id(与结果归属 `runWho` / `AgentRun.experimentId` 同源);不经
285
285
  * experiment 跑(如脱离 CLI、直接构造 `AgentRun` 的场景)时为 undefined。典型用途:
286
- * `SandboxSpec.setup` 钩子按实验隔离跨 attempt 的状态(缓存目录名、快照 tag 等按
286
+ * `SandboxSpec.setup` Hook 按实验隔离跨 attempt 的状态(缓存目录名、快照 tag 等按
287
287
  * `ctx.experimentId` 分区),或 adapter 按实验切换鉴权 / 路由。与 `flags`(实验条件的
288
288
  * 具体取值)是两个维度——这里只是「跑的是哪个实验」的稳定标识,不携带条件内容。
289
289
  */
290
290
  readonly experimentId?: string;
291
291
  /**
292
- * 所有 agent 都有:沙箱型是运行器按项目/experiment 配置备好的真实沙箱句柄,remote 型是
292
+ * 所有 agent 都有:Sandbox 型是运行器按项目/experiment 配置备好的真实 Sandbox 句柄,remote 型是
293
293
  * `createRemoteSandbox()` 产出的 stub(仅含 `workdir`/`sandboxId`/`otlpHost`/`stop` 等
294
294
  * 元信息,其余方法调用即抛错)。
295
295
  */
@@ -328,10 +328,12 @@ export interface AgentContext {
328
328
  * agent 自己的沙箱生命周期(每个沙箱一次,与「每轮 send」分开):
329
329
  * `setup` 装 CLI、写配置(model/base/auth 等本轮内不变的东西),`send` 只管把一轮 prompt
330
330
  * 跑起来(第一次 fresh / 后续 resume)+ 解析 transcript,`teardown` 清理。
331
- * 运行器在备好沙箱(上传 / 基线 / eval.setup)后、第一次 send 前调一次 `setup`;
332
- * `setup` 可返回 cleanup 闭包,与 `teardown` 都在 finally 跑。
331
+ * 运行器在备好沙箱(上传 / 基线 / eval.setup)后、第一次 send 前调一次 `setup`,不返回值;
332
+ * `teardown` 当且仅当本 attempt 走到过 `setup` 时点才执行(`setup` 抛错不豁免——半初始化
333
+ * 的现场同样要扫尾),在 finally 里跑。要把 `setup` 里创建的句柄传给 `teardown`,以
334
+ * `sandbox` 实例为键存取(同一个 Agent 实例服务并发 attempt,不要用实例字段或模块变量)。
333
335
  */
334
- export type AgentSetup = (sandbox: Sandbox, ctx: AgentContext) => Promise<void | Cleanup> | void | Cleanup;
336
+ export type AgentSetup = (sandbox: Sandbox, ctx: AgentContext) => Promise<void> | void;
335
337
  export type AgentTeardown = (sandbox: Sandbox, ctx: AgentContext) => Promise<void> | void;
336
338
 
337
339
  /**
@@ -368,18 +370,19 @@ export interface SandboxAgentDef {
368
370
  /** 该 Adapter 的常态证据覆盖声明(完整采集的用 `completeCoverage` 常量);省略 = 全通道 unknown。 */
369
371
  coverage?: EvidenceCoverage;
370
372
  /**
371
- * 每个沙箱一次(不是每轮一次):装 CLI、写 config.toml / 鉴权配置(model/base/auth 等
372
- * 本轮内不变的东西)。运行器在沙箱备好(上传/基线/eval.setup 之后)、第一次 send 前
373
- * 调用一次。可返回一个 cleanup 闭包,与 `teardown` 一起在 finally 里跑。
373
+ * 每个 Sandbox 一次(不是每轮一次):装 CLI、写 config.toml / 鉴权配置(model/base/auth 等
374
+ * 本轮内不变的东西)。运行器在 Sandbox 备好(上传/基线/eval.setup 之后)、第一次 send 前
375
+ * 调用一次,不返回值。
374
376
  */
375
377
  setup?: AgentSetup;
376
- /** OTLP 导出配置:沙箱里怎么让 CLI 把 trace 发到 endpoint(env / 配置文件),从 setup 拆出。 */
378
+ /** OTLP 导出配置:Sandbox 里怎么让 CLI 把 trace 发到 endpoint(env / 配置文件),从 setup 拆出。 */
377
379
  tracing?: AgentTracing;
378
380
  /** 原生 span → canonical 的薄 mapper;省略走通用 heuristic。只影响瀑布图。 */
379
381
  spanMapper?: SpanMapper;
380
382
  /** 每轮一次:跑 prompt(fresh / resume)+ 解析成 events。 */
381
383
  send(input: TurnInput, ctx: AgentContext): Promise<Turn>;
382
- /** 沙箱销毁前的清理(与 setup 返回的 cleanup 一起、都在 finally 里跑一次)。 */
384
+ /** Sandbox 销毁前的清理,当且仅当本 attempt 走到过 `setup` 时点才执行(`setup` 抛错不豁免),
385
+ * 在 finally 里跑一次。 */
383
386
  teardown?: AgentTeardown;
384
387
  }
385
388
 
@@ -390,10 +393,9 @@ export interface RemoteAgentDef {
390
393
  /** 该 Adapter 的常态证据覆盖声明(完整采集的用 `completeCoverage` 常量);省略 = 全通道 unknown。 */
391
394
  coverage?: EvidenceCoverage;
392
395
  /**
393
- * 每个 attempt 一次(remote agent 没有真实沙箱,运行器会传入一个仅含 `workdir`/`sandboxId`/
396
+ * 每个 attempt 一次(remote agent 没有真实 Sandbox,运行器会传入一个仅含 `workdir`/`sandboxId`/
394
397
  * `otlpHost`/`stop` 等元信息的 stub `Sandbox`,其余方法调用即抛错——不要在这里调用
395
- * 文件/命令类沙箱方法)。常用于建立连接、鉴权等一次性准备。可返回一个 cleanup 闭包,
396
- * 与 `teardown` 都在 finally 里跑。
398
+ * 文件/命令类 Sandbox 方法)。常用于建立连接、鉴权等一次性准备,不返回值。
397
399
  */
398
400
  setup?: AgentSetup;
399
401
  /** OTLP 导出配置:远程被测对象怎么把 trace 发到 endpoint(env-based 注入 / file-based 配置)。 */
@@ -402,6 +404,7 @@ export interface RemoteAgentDef {
402
404
  spanMapper?: SpanMapper;
403
405
  /** 每轮一次:把一轮 prompt 发给远程被测对象(HTTP/SDK 等),解析响应成 events。 */
404
406
  send(input: TurnInput, ctx: AgentContext): Promise<Turn>;
405
- /** 运行结束前的清理(与 setup 返回的 cleanup 一起、都在 finally 里跑一次)。 */
407
+ /** 运行结束前的清理,当且仅当本 attempt 走到过 `setup` 时点才执行(`setup` 抛错不豁免),
408
+ * 在 finally 里跑一次。 */
406
409
  teardown?: AgentTeardown;
407
410
  }
@@ -93,6 +93,16 @@ afterEach(() => {
93
93
  });
94
94
 
95
95
  describe("uiMessageStreamAgent", () => {
96
+ it("声明全通道 complete,usage 例外(协议帧不带 token 计数)——官方 SDK 适配器的覆盖声明义务", () => {
97
+ const agent = uiMessageStreamAgent({ name: "t", url: "http://x/api/chat" });
98
+ expect(agent.coverage?.events?.status).toBe("complete");
99
+ expect(agent.coverage?.actions?.status).toBe("complete");
100
+ expect(agent.coverage?.messages?.status).toBe("complete");
101
+ expect(agent.coverage?.status?.status).toBe("complete");
102
+ expect(agent.coverage?.data?.status).toBe("complete");
103
+ expect(agent.coverage?.usage?.status).toBe("unavailable");
104
+ });
105
+
96
106
  it("纯文本轮:message 事件 + completed;第二轮重放全量历史(客户端带历史的协议语义)", async () => {
97
107
  const agent = uiMessageStreamAgent({ name: "t", url: "http://x/api/chat", body: (c) => ({ model: c.model }) });
98
108
  fetchMock.mockResolvedValueOnce(sse([{ type: "text-delta", delta: "你好" }, { type: "text-delta", delta: "!" }]));
@@ -25,7 +25,17 @@
25
25
  import { randomUUID } from "node:crypto";
26
26
 
27
27
  import { defineAgent } from "../define.ts";
28
- import type { Agent, AgentContext, AgentTracing, InputResponse, JsonValue, SpanMapper, StreamEvent, TurnInput } from "../types.ts";
28
+ import { completeCoverage } from "../scoring/coverage.ts";
29
+ import type { Agent, AgentContext, AgentTracing, EvidenceCoverage, InputResponse, JsonValue, SpanMapper, StreamEvent, TurnInput } from "../types.ts";
30
+
31
+ // UI Message Stream 帧里没有 usage(协议本身不带 token 计数,见 docs/engineering/e2e-ci/README.md
32
+ // 第 2 节);events/actions/messages/status 都直接来自完整归约的协议帧,和 fromAiSdk/aiSdkAgent
33
+ // 同等完整——官方 SDK 适配器应声明全通道 complete(docs/feature/adapters/architecture/evidence.md),
34
+ // 这里唯一的例外是协议本身没有的 usage 通道,如实标 unavailable 而不是留成 unknown。
35
+ const COVERAGE: EvidenceCoverage = Object.freeze({
36
+ ...completeCoverage,
37
+ usage: { status: "unavailable" as const, reason: "UI Message Stream frames carry no token usage" },
38
+ });
29
39
 
30
40
  // ───────────────────────── 协议的结构化类型(structural,不依赖 ai 包的类型) ─────────────────────────
31
41
 
@@ -239,6 +249,7 @@ const DEFAULT_DENY_REASON = "用户拒绝了这次调用,不要重试,直接告
239
249
  export function uiMessageStreamAgent(options: UiMessageStreamAgentOptions): Agent {
240
250
  return defineAgent({
241
251
  name: options.name ?? "ui-message-stream",
252
+ coverage: COVERAGE,
242
253
  tracing: options.tracing,
243
254
  spanMapper: options.spanMapper,
244
255