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/cli.ts CHANGED
@@ -5,17 +5,20 @@
5
5
  // niceeval clean 删除 .niceeval/ 历史运行 artifact
6
6
 
7
7
  import { spawn } from "node:child_process";
8
- import { createHash } from "node:crypto";
9
8
  import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
10
9
  import { existsSync } from "node:fs";
11
10
  import { join, relative, resolve as resolvePath } from "node:path";
12
11
  import { pathToFileURL } from "node:url";
13
12
  import { parseArgs as nodeParseArgs } from "node:util";
14
- import { discoverEvals, discoverExperiments, makeFilter } from "./runner/discover.ts";
13
+ import { discoverEvals, discoverExperiments } from "./runner/discover.ts";
14
+ import { browsableExperimentPaths, matchExperimentSelector } from "./shared/aggregate.ts";
15
15
  import { runEvals, type AgentRun } from "./runner/run.ts";
16
16
  import { planCarry } from "./runner/fingerprint.ts";
17
+ import { fingerprintEvalsFilter, resolveExperimentEvals, selectedEvalsForRun } from "./runner/eval-selection.ts";
17
18
  import { failureDetailFromResult } from "./runner/feedback/failure.ts";
18
19
  import { stopAllSandboxes, liveSandboxCount } from "./sandbox/registry.ts";
20
+ import { drainExperimentTeardowns } from "./runner/experiment-cleanup-registry.ts";
21
+ import { CLEANUP_TIMEOUT_MS } from "./runner/cleanup-timeout.ts";
19
22
  import { evalLevelStats } from "./shared/verdict.ts";
20
23
  import { prepareRunSandboxes, resolvedSandboxRecommendedConcurrency } from "./runner/sandbox-selection.ts";
21
24
  import { Json, JUnit } from "./runner/reporters/json.ts";
@@ -47,14 +50,13 @@ import {
47
50
  // --report 用的那份(dist/report/**,见 tsconfig.report-build.json)是同一个模块实例——
48
51
  // `unique symbol` 品牌与 class 的 instanceof 都按声明处的模块身份判定,raw src 和编译产物
49
52
  // 是两份不同源码位置,即使运行时同名同形,TS 类型与 instanceof 都不认。
50
- import { ReportLoadError } from "../dist/report/load.js";
53
+ import { ReportLoadError } from "../dist/report/runtime/load.js";
51
54
  import { runShow } from "./show/index.ts";
52
55
  import { t } from "./i18n/index.ts";
53
56
  import { formatThrown, upsertManagedBlock } from "./util.ts";
54
57
  import type {
55
58
  CompletionStatus,
56
59
  Config,
57
- DiscoveredExperiment,
58
60
  ReporterError,
59
61
  ReporterRegistration,
60
62
  RunCompletion,
@@ -135,9 +137,9 @@ const FLAG_OPTIONS = {
135
137
  timeout: { type: "string" },
136
138
  /** 整次运行的预算上限(美元)。 */
137
139
  budget: { type: "string" },
138
- /** `exp` 命令专用:跑完留下 failed/errored attempt 的沙箱现场(= `--keep-sandbox=failed`);`--keep-sandbox=all` 连 passed 也留。事后用 `niceeval sandbox list/enter/stop` 查看与销毁。 */
140
+ /** `exp` 命令专用:跑完留下 failed/errored attempt 的 Sandbox 现场(= `--keep-sandbox=failed`);`--keep-sandbox=all` 连 passed 也留。事后用 `niceeval sandbox list/enter/stop` 查看与销毁。 */
139
141
  "keep-sandbox": { type: "boolean" },
140
- /** `sandbox stop` 专用:销毁全部留存沙箱。 */
142
+ /** `sandbox stop` 专用:销毁全部留存 Sandbox。 */
141
143
  all: { type: "boolean" },
142
144
  /** `sandbox diff` 专用:只看某个 send 窗口(如 `--window s1/t2`);省略输出全部窗口的串联视图。 */
143
145
  window: { type: "string" },
@@ -155,7 +157,7 @@ const FLAG_OPTIONS = {
155
157
  out: { type: "string" },
156
158
  /** `view` 命令专用:指定本地服务器监听端口。 */
157
159
  port: { type: "string" },
158
- // show 的证据切面 / 时间轴 / 报告装载(docs-site/zh/how-to/viewing-results.mdx)。
160
+ // show 的证据切面 / 时间轴 / 报告装载(docs-site/zh/tutorials/viewing-results.mdx)。
159
161
  // 证据切面只认 `@<locator>`(或收窄到单个 eval 的前缀)选出的那一个 attempt——不再有
160
162
  // 数字 `--attempt`,选哪个 attempt 由 locator 精确指名,不是「先选 eval 再挑第几次」。
161
163
  /** `show` 命令专用:该 attempt 运行时保存的 Eval 源码,gate/soft 断言标回源码行(证据切面)。 */
@@ -170,8 +172,8 @@ const FLAG_OPTIONS = {
170
172
  diff: { type: "boolean" },
171
173
  /** `show` 命令专用:执行时间轴——对匹配的每个 experiment × eval 分节,逐 attempt 列时间 / verdict / 摘要 / 耗时 / 成本 / locator;与 `--report` 互斥。 */
172
174
  history: { type: "boolean" },
173
- /** `show` / `view` 命令专用:按路径段前缀收窄 experiment;组名会选中组内全部配置。 */
174
- experiment: { type: "string" },
175
+ /** `show` / `view` 命令专用:按路径段前缀收窄 experiment(与 `niceeval exp` 位置参数同一套匹配);目录路径会选中其下全部配置。`view --out` 时同一收窄决定出站内容。 */
176
+ exp: { type: "string" },
175
177
  /** `show` / `view` / `sandbox enter|list|stop` 共用:结果根目录(`.niceeval` 之外的另一个根,如 `copySnapshots` 产出的发布根)。 */
176
178
  results: { type: "string" },
177
179
  /** `view` 命令专用:只打开这一份快照文件(`snapshot.json`);文件不可读时命令失败(扫描模式只跳过)。 */
@@ -308,7 +310,7 @@ function parseArgs(argv: string[]): { command: string; positionals: string[]; fl
308
310
  sandboxPath: values.path as string | undefined,
309
311
  leaveRunning: values["leave-running"] === true,
310
312
  history: values.history === true,
311
- experiment: values.experiment as string | undefined,
313
+ experiment: values.exp as string | undefined,
312
314
  results: values.results as string | undefined,
313
315
  snapshot: values.snapshot as string | undefined,
314
316
  report: values.report as string | undefined,
@@ -320,7 +322,7 @@ function parseArgs(argv: string[]): { command: string; positionals: string[]; fl
320
322
  /**
321
323
  * exp 只接受两类输入:位置参数选「跑哪些 eval」+ 调度/输出/机器出口 flag 选「对着哪个 agent、
322
324
  * 怎么跑」。show / view 专属的证据切面(`--source`/`--execution`/`--diff`)、时间轴(`--history`)、
323
- * Scope 收窄(`--experiment`/`--results`)、报告装载(`--report`/`--page`)、查看器
325
+ * Scope 收窄(`--exp`/`--results`)、报告装载(`--report`/`--page`)、查看器
324
326
  * (`--snapshot`/`--out`/`--port`/`--open`)不能被 exp 静默忽略(见 docs/feature/experiments/
325
327
  * cli.md「用法错误」)。返回第一个被误用的 flag 及其归属命令(用于报错),没有误用返回 undefined。
326
328
  */
@@ -333,7 +335,7 @@ function firstViewerOnlyFlag(flags: Flags): { flag: string; command: string } |
333
335
  if (flags.timing !== undefined) return { flag: "--timing", command: SHOW };
334
336
  if (flags.diff || flags.diffPath !== undefined) return { flag: "--diff", command: SHOW };
335
337
  if (flags.history) return { flag: "--history", command: SHOW };
336
- if (flags.experiment !== undefined) return { flag: "--experiment", command: BOTH };
338
+ if (flags.experiment !== undefined) return { flag: "--exp", command: BOTH };
337
339
  if (flags.results !== undefined) return { flag: "--results", command: BOTH };
338
340
  if (flags.report !== undefined) return { flag: "--report", command: BOTH };
339
341
  if (flags.page !== undefined) return { flag: "--page", command: BOTH };
@@ -462,32 +464,6 @@ async function openBrowser(url: string): Promise<boolean> {
462
464
  });
463
465
  }
464
466
 
465
- function evalsFilterFromExperiment(
466
- evals: DiscoveredExperiment["evals"],
467
- patterns: string[],
468
- ): (id: string) => boolean {
469
- const patternFilter = makeFilter(patterns);
470
- let expFilter: (id: string) => boolean = () => true;
471
- if (Array.isArray(evals)) expFilter = makeFilter(evals);
472
- else if (typeof evals === "function") expFilter = evals;
473
- return (id) => expFilter(id) && patternFilter(id);
474
- }
475
-
476
- /**
477
- * evals 过滤器的指纹(进 ExperimentRunInfo.evalFilterFingerprint,供「配置没变」判断):
478
- * 数组按内容、函数按函数体哈希;CLI 追加的位置参数前缀一并计入。不存过滤器本身——
479
- * 求值结果在 selectedEvalIds(见 runEvals)。
480
- */
481
- function fingerprintEvalsFilter(evals: DiscoveredExperiment["evals"], patterns: string[]): string {
482
- const basis =
483
- evals === undefined || evals === "*"
484
- ? "*"
485
- : Array.isArray(evals)
486
- ? JSON.stringify([...evals].sort())
487
- : evals.toString();
488
- return createHash("sha256").update(JSON.stringify({ basis, patterns })).digest("hex").slice(0, 16);
489
- }
490
-
491
467
  /**
492
468
  * run 结束后把 coordinator 累计的诊断折成 `RunCompletion`(见 docs/feature/experiments/cli.md
493
469
  * 「运行完成状态不只看 verdict 计数」)。只读已经真实发生过的诊断,不额外发明信号:
@@ -566,11 +542,10 @@ async function main(): Promise<void> {
566
542
  }
567
543
 
568
544
  if (command === "view") {
569
- // 位置参数只有一种含义:eval id 前缀(收窄报告槽 Scope)。结果根经 --results 递入,
545
+ // 位置参数只有一种含义:eval id 前缀(收窄有效根)。结果根经 --results 递入,
570
546
  // 单开一份快照经 --snapshot 递入;--report 整槽替换报告槽(与 show --report 吃同一个文件),
571
547
  // --page 定初始页。文件与目录都不进位置参数(docs/feature/reports/view.md「打开与收窄」)。
572
- // --out 与位置参数 / --experiment 的互斥在 buildView 内校验(单点,报错文案含 copySnapshots
573
- // + filter 的下一步),经 exitOnViewUserError 按用法错误退出。
548
+ // --out 接受同一收窄:出站内容即收窄后的有效根(docs/feature/reports/view.md「静态导出」)。
574
549
  let viewInput: { input?: string; patterns: string[] };
575
550
  try {
576
551
  viewInput = resolveViewInput(cwd, positionals, {
@@ -664,7 +639,7 @@ async function main(): Promise<void> {
664
639
 
665
640
  const agentRuns: AgentRun[] = [];
666
641
  let experimentSelection = t("cli.all");
667
- let availableExperimentGroups = t("cli.none");
642
+ let availableExperimentPaths = t("cli.none");
668
643
 
669
644
  if (command === "exp") {
670
645
  if (flags.agent || flags.model) {
@@ -680,16 +655,13 @@ async function main(): Promise<void> {
680
655
  const expArg = positionals[0];
681
656
  const extraPatterns = positionals.slice(1);
682
657
  experimentSelection = positionals.join(" ") || t("cli.all");
683
- availableExperimentGroups = [...new Set(experiments.map((experiment) => experiment.group || experiment.id))]
684
- .sort()
685
- .join(", ") || t("cli.none");
686
- const selected = expArg
687
- ? experiments.filter((e) => e.group === expArg || e.id === expArg || e.id.startsWith(expArg + "/"))
688
- : experiments;
658
+ availableExperimentPaths = browsableExperimentPaths(experiments.map((e) => e.id)).join(", ") || t("cli.none");
659
+ const selectedIds = expArg ? new Set(matchExperimentSelector(experiments.map((e) => e.id), expArg)) : undefined;
660
+ const selected = selectedIds ? experiments.filter((e) => selectedIds.has(e.id)) : experiments;
689
661
  if (selected.length === 0) {
690
662
  process.stderr.write(t("cli.experiment.noMatch", {
691
663
  arg: expArg ?? t("cli.all"),
692
- experiments: experiments.map((e) => e.id).join(", ") || t("cli.none"),
664
+ experiments: availableExperimentPaths,
693
665
  }));
694
666
  // show / view 是顶层命令。只有同名 experiment 确实不存在时才纠错,不能抢占合法 id。
695
667
  if (expArg === "show" || expArg === "view") {
@@ -708,6 +680,15 @@ async function main(): Promise<void> {
708
680
  }
709
681
  for (const exp of selected) {
710
682
  // 一个实验 = 一个配置(单 model)。跨模型对比写多个实验文件,各钉一个 model。
683
+ // evals 谓词在这里对本次 invocation 的候选 eval 各求值一次;下游(dry-run、sandbox 查表、
684
+ // fingerprint/carry、attempt 展开)只消费 selectedEvalIds,不重新调用谓词
685
+ // (见 docs/feature/experiments/library.md「evals」)。
686
+ const { selectedEvalIds } = resolveExperimentEvals({
687
+ experimentId: exp.id,
688
+ selector: exp.evals,
689
+ cliPatterns: extraPatterns,
690
+ evals,
691
+ });
711
692
  agentRuns.push({
712
693
  agent: exp.agent,
713
694
  model: exp.model,
@@ -718,23 +699,25 @@ async function main(): Promise<void> {
718
699
  sandbox: exp.sandbox ?? config.sandbox,
719
700
  timeoutMs: flags.timeout ?? envNumber("NICEEVAL_TIMEOUT") ?? exp.timeoutMs ?? config.timeoutMs,
720
701
  budget: flags.budget ?? envNumber("NICEEVAL_BUDGET") ?? exp.budget,
721
- evalFilter: evalsFilterFromExperiment(exp.evals, extraPatterns),
702
+ selectedEvalIds,
722
703
  experimentId: exp.id,
723
704
  description: exp.description,
705
+ labels: exp.labels,
724
706
  evalFilterFingerprint: fingerprintEvalsFilter(exp.evals, extraPatterns),
725
707
  strict: flags.strict,
726
708
  // 实验级并发上限:随 AgentRun 进调度器按实验单独限流(runner 两级信号量),
727
709
  // 不再取所有选中实验的最小值钳全局——那会让一个串行实验拖慢整批基线。
728
710
  maxConcurrency: exp.maxConcurrency,
711
+ setup: exp.setup,
729
712
  });
730
713
  }
731
714
  } else {
732
715
  // 裸 run / `niceeval <eval>` 不再执行。运行配置必须来自 experiments/,
733
716
  // 这样 agent/model/flags/runs/budget 与结果聚合都有可签入的身份。
734
717
  const experiments = await discoverExperiments(cwd);
735
- const asExp = experiments.filter((e) =>
736
- positionals.some((p) => e.group === p || e.id === p || e.id.startsWith(p + "/")),
737
- );
718
+ const ids = experiments.map((e) => e.id);
719
+ const matchedIds = new Set(positionals.flatMap((p) => matchExperimentSelector(ids, p)));
720
+ const asExp = experiments.filter((e) => matchedIds.has(e.id));
738
721
  process.stderr.write(t("cli.run.experimentRequired"));
739
722
  if (asExp.length > 0) {
740
723
  process.stderr.write(t("cli.run.experimentRequiredHint", {
@@ -759,14 +742,14 @@ async function main(): Promise<void> {
759
742
 
760
743
  // matchedByRun[i] 对应 agentRuns[i] 匹配到的 eval 集合;--dry 预览与真正开跑时的
761
744
  // RunFeedbackPlan(总量、去重 eval 数)共用同一份计算,不重复过滤一遍。
762
- const matchedByRun = agentRuns.map((run) => evals.filter((e) => run.evalFilter(e.id)));
745
+ const matchedByRun = agentRuns.map((run) => selectedEvalsForRun(evals, run));
763
746
  const totalRuns = agentRuns.reduce((sum, run, i) => sum + matchedByRun[i]!.length * run.runs, 0);
764
747
  const uniqueEvalIds = new Set(matchedByRun.flat().map((e) => e.id));
765
748
 
766
749
  if (totalRuns === 0) {
767
750
  process.stderr.write(t("cli.experiment.noEvalsSelected", {
768
751
  selection: experimentSelection,
769
- experiments: availableExperimentGroups,
752
+ experiments: availableExperimentPaths,
770
753
  }));
771
754
  process.exit(1);
772
755
  }
@@ -874,13 +857,34 @@ async function main(): Promise<void> {
874
857
  // 3 次:真不耐烦了,硬退(此时多半已无可清理的)。
875
858
  const ctrl = new AbortController();
876
859
  let signalCount = 0;
877
- // 兜底强清 + 退出:只跑一次,带超时(stopAllSandboxes 内每个 stop 各自有超时)。先停 dashboard
878
- // tick/动态区域(coordinator.stopDynamic()),避免硬退时终端卡在半帧 ANSI 状态。
860
+ // 强清 = 加速收尾,不是绕过收尾(docs/cli.md「中断:三级响应」)。顺序:先强停沙箱(卡在
861
+ // 沙箱 I/O 上的收尾立刻失败返回),然后事件驱动收口——并发等待「在飞收尾链 settle」与
862
+ // 「实验级 teardown 注册表排空」(drain 会启动未启动的、等待在飞的同一 memoized promise),
863
+ // 两者都 settle 即退。兜底上限从单可调用体清理上限推导(docs 声明的不等式:provider stop 8s
864
+ // < 看门狗 < CLEANUP_TIMEOUT_MS ≤ 本上限),不是第 2 级的语义——settle 才是——只拦
865
+ // 「收尾可调用体绕过了自己的超时」的失守病态,到点放弃退出(职责同第 3 级硬退)。
866
+ // 只跑一次;先停 dashboard 的 tick/动态区域(coordinator.stopDynamic()),
867
+ // 避免硬退时终端卡在半帧 ANSI 状态。
868
+ const FORCE_SETTLE_CAP_MS = CLEANUP_TIMEOUT_MS * 2;
869
+ let runInFlight: Promise<unknown> | undefined;
879
870
  let forcing = false;
880
871
  const forceCleanupAndExit = (code: number) => {
881
872
  if (forcing) return;
882
873
  forcing = true;
883
- void Promise.all([coordinator.stopDynamic(), stopAllSandboxes()]).finally(() => process.exit(code));
874
+ void (async () => {
875
+ await Promise.all([coordinator.stopDynamic(), stopAllSandboxes()]);
876
+ const settled = Promise.allSettled([
877
+ ...(runInFlight ? [runInFlight] : []),
878
+ drainExperimentTeardowns(),
879
+ ]);
880
+ await Promise.race([
881
+ settled.then(() => {}),
882
+ new Promise<void>((r) => {
883
+ setTimeout(r, FORCE_SETTLE_CAP_MS).unref();
884
+ }),
885
+ ]);
886
+ process.exit(code);
887
+ })();
884
888
  };
885
889
  for (const sig of ["SIGINT", "SIGTERM"] as const) {
886
890
  process.on(sig, () => {
@@ -888,13 +892,16 @@ async function main(): Promise<void> {
888
892
  if (signalCount === 1) {
889
893
  reportActivity(t("cli.interruptCleanup").trimEnd());
890
894
  ctrl.abort();
891
- // 看门狗:graceful 清理 12s 还没让进程自己收口,就强清兜底。
895
+ // 看门狗:graceful 清理迟迟没让进程自己收口,就强清兜底。取值在 docs/cli.md 声明的
896
+ // 不等式链里:> provider stop 超时(8s,一次正常停容器超时后才升级,不误伤),
897
+ // < CLEANUP_TIMEOUT_MS(30s)。
898
+ const GRACEFUL_WATCHDOG_MS = 12_000;
892
899
  setTimeout(() => {
893
900
  if (liveSandboxCount() > 0) {
894
901
  reportActivity(t("cli.fallbackCleanupTimeout").trimEnd());
895
902
  forceCleanupAndExit(130);
896
903
  }
897
- }, 12_000).unref();
904
+ }, GRACEFUL_WATCHDOG_MS).unref();
898
905
  } else if (signalCount === 2) {
899
906
  reportActivity(t("cli.forceCleanupExit").trimEnd());
900
907
  forceCleanupAndExit(130);
@@ -922,7 +929,7 @@ async function main(): Promise<void> {
922
929
 
923
930
  let summary: RunSummary;
924
931
  try {
925
- summary = await runEvals({
932
+ const inFlight = runEvals({
926
933
  config,
927
934
  evals,
928
935
  agentRuns,
@@ -934,15 +941,20 @@ async function main(): Promise<void> {
934
941
  keepSandbox: flags.keepSandbox,
935
942
  niceevalRoot: resolvePath(cwd, ".niceeval"),
936
943
  });
944
+ // 交给强清路径一个可等待的收尾句柄:二次中断/看门狗强清时先有界等它收口,让在飞的
945
+ // teardown 链跑完,而不是 process.exit 把它们连同进程一起杀掉。
946
+ runInFlight = inFlight;
947
+ summary = await inFlight;
937
948
  } catch (e) {
938
949
  // 真崩溃前先撤下 dashboard,不让半帧 ANSI 状态和下面 main().catch 打印的错误交织。
939
950
  await coordinator.stopDynamic();
940
951
  throw e;
941
952
  }
942
953
 
943
- // 正常返回(含被中断后走部分汇总)后再兜一刀:Scope finalizer 没停掉的残留沙箱在这里强清。
944
- // 跑顺利时登记表已空,是 no-op。
954
+ // 正常返回(含被中断后走部分汇总)后再兜一刀:Scope finalizer 没停掉的残留沙箱、没被运行
955
+ // 路径消费的实验级 cleanup 在这里强清。跑顺利时两份登记表都已空,是 no-op。
945
956
  await stopAllSandboxes();
957
+ await drainExperimentTeardowns();
946
958
 
947
959
  // completion 要先算好,--json/--junit 是否"这次真的写出"才有依据(见下)。
948
960
  const completion = assembleRunCompletion(coordinator.state);
@@ -980,7 +992,8 @@ async function main(): Promise<void> {
980
992
 
981
993
  main().catch(async (e) => {
982
994
  process.stderr.write(t("cli.error", { error: formatThrown(e) }));
983
- // 真·崩溃路径也别留孤儿:强清还活着的沙箱(带超时),再退。
995
+ // 真·崩溃路径也别留孤儿:强清还活着的沙箱(带超时)、排空实验级 cleanup 注册表,再退。
984
996
  await stopAllSandboxes();
997
+ await drainExperimentTeardowns();
985
998
  process.exit(2);
986
999
  });
@@ -140,35 +140,35 @@ export interface RespondAnswer {
140
140
  readonly text?: string;
141
141
  }
142
142
 
143
- /** eval 作者可见的受限沙箱视图:能执行命令 / 文件 IO / 读最终 diff,但不能 stop。 */
143
+ /** 评估用例作者可见的受限 Sandbox 视图:能执行命令 / 文件 IO / 读最终 diff,但不能 stop。 */
144
144
  export interface SandboxHandle {
145
- /** 沙箱内的工作目录绝对路径。 */
145
+ /** Sandbox 内的工作目录绝对路径。 */
146
146
  readonly workdir: string;
147
- /** 在沙箱里执行一条命令(argv 形式,不经 shell)。装系统依赖等需要 root 时传 `{ root: true }`。 */
147
+ /** 在 Sandbox 里执行一条命令(argv 形式,不经 shell)。装系统依赖等需要 root 时传 `{ root: true }`。 */
148
148
  runCommand(cmd: string, args?: string[], opts?: CommandOptions): Promise<CommandResult>;
149
- /** 在沙箱里执行一段 shell 脚本(经 shell 解释,支持管道 / 重定向)。 */
149
+ /** Sandbox 里执行一段 shell 脚本(经 shell 解释,支持管道 / 重定向)。 */
150
150
  runShell(script: string, opts?: CommandOptions): Promise<CommandResult>;
151
- /** 读沙箱内某文件此刻的文本内容(实时读,不是 diff 快照)。 */
151
+ /** 读 Sandbox 内某文件此刻的文本内容(实时读,不是 diff 快照)。 */
152
152
  readFile(path: string): Promise<string>;
153
- /** 沙箱内某路径此刻是否存在。 */
153
+ /** Sandbox 内某路径此刻是否存在。 */
154
154
  fileExists(path: string): Promise<boolean>;
155
- /** 按选项批量读回沙箱里的源文件,供比对 / judge 用材料。 */
155
+ /** 按选项批量读回 Sandbox 里的源文件,供比对 / judge 用材料。 */
156
156
  readSourceFiles(opts?: ReadSourceFilesOptions): Promise<SourceFiles>;
157
- /** 把一组内容写进沙箱(路径相对 targetDir,默认 workdir)。 */
157
+ /** 把一组内容写进 Sandbox(路径相对 targetDir,默认 workdir)。 */
158
158
  writeFiles(files: Record<string, string>, targetDir?: string): Promise<void>;
159
- /** 把一批内存中的文件上传进沙箱。 */
159
+ /** 把一批内存中的文件上传进 Sandbox。 */
160
160
  uploadFiles(files: SandboxFile[], targetDir?: string): Promise<void>;
161
- /** 把本地目录整体上传进沙箱;`opts.ignore` 排除指定路径。 */
161
+ /** 把本地目录整体上传进 Sandbox;`opts.ignore` 排除指定路径。 */
162
162
  uploadDirectory(localDir: string, targetDir?: string, opts?: { ignore?: string[] }): Promise<void>;
163
- /** 从沙箱下载某文件内容。 */
163
+ /** Sandbox 下载某文件内容。 */
164
164
  downloadFile(path: string): Promise<Buffer>;
165
- /** 把一段内容上传成沙箱里的单个文件。 */
165
+ /** 把一段内容上传成 Sandbox 里的单个文件。 */
166
166
  uploadFile(path: string, content: Buffer): Promise<void>;
167
- /** 沙箱 provider 分配的实例 id,用于排查 / 关联日志。 */
167
+ /** Sandbox provider 分配的实例 id,用于排查 / 关联日志。 */
168
168
  readonly sandboxId: string;
169
169
  /** 相对 git 基线的最终 diff 视图(test() 跑完、finalize 前才落定)。 */
170
170
  readonly diff: DiffView;
171
- /** 取沙箱内某文件的最终内容,占位延迟到 finalize 才真正读取;只能配合 t.check 使用。 */
171
+ /** Sandbox 内某文件的最终内容,占位延迟到 finalize 才真正读取;只能配合 t.check 使用。 */
172
172
  file(path: string): string;
173
173
  /** 断言 diff 里某路径发生了变化(新增或修改)。 */
174
174
  fileChanged(path: string): AssertionHandle;
@@ -176,7 +176,7 @@ export interface SandboxHandle {
176
176
  fileDeleted(path: string): AssertionHandle;
177
177
  /** 断言 diff 的路径与内容都不匹配给定正则(常用来否定式检查不该出现的改动)。 */
178
178
  notInDiff(re: RegExp): AssertionHandle;
179
- /** 断言沙箱里执行过的命令都没有失败退出。 */
179
+ /** 断言 Sandbox 里执行过的命令都没有失败退出。 */
180
180
  noFailedShellCommands(): AssertionHandle;
181
181
  }
182
182
 
@@ -285,7 +285,7 @@ export interface TestContext {
285
285
  /** 本次 attempt 生效的实验 flags(experiment.flags 的只读视图;实验条件,非命令行开关)。 */
286
286
  readonly flags: Readonly<Record<string, unknown>>;
287
287
  /**
288
- * 作用域反馈:报告 eval 自己执行的长步骤(上传 fixture、跑构建……)。短命状态,scope 固定
288
+ * 作用域反馈:报告评估用例自己执行的长步骤(上传 Fixture、跑构建……)。短命状态,scope 固定
289
289
  * 为 `eval.run`;只报告不断言(见 docs/feature/eval/library/context.md「向运行反馈长步骤」)。
290
290
  */
291
291
  progress(update: ProgressUpdate): void;
@@ -296,18 +296,18 @@ export interface TestContext {
296
296
  diagnostic(input: DiagnosticInput): void;
297
297
  /** `progress({ message: msg })` 的别名(调试日志),不出现在最终结果里。 */
298
298
  log(msg: string): void;
299
- /** 立即中止本 eval 并标记为 skipped(verdict / EvalResult.skipReason),reason 不能为空。 */
299
+ /** 立即中止本评估用例并标记为 skipped(verdict / EvalResult.skipReason),reason 不能为空。 */
300
300
  skip(reason: string): never;
301
301
 
302
302
  // 值断言
303
303
  /**
304
304
  * 对任意值跑一个 ValueAssertion,返回可链 `.gate()` / `.atLeast()` 的 AssertionHandle。
305
- * 打分延迟到 eval 结束后统一 finalize,调用本身同步、不抛错——不通过只是记一条失败断言,
306
- * 不会中止后续代码。要「不满足就立即中止 eval」用 require。
305
+ * 打分延迟到评估用例结束后统一 finalize,调用本身同步、不抛错——不通过只是记一条失败断言,
306
+ * 不会中止后续代码。要「不满足就立即中止评估用例」用 require。
307
307
  */
308
308
  check(value: unknown, assertion: ValueAssertion): AssertionHandle;
309
309
  /**
310
- * 对任意值跑一个 ValueAssertion,立即(await 时)求值;不满足就抛错中止整个 eval 剩余步骤
310
+ * 对任意值跑一个 ValueAssertion,立即(await 时)求值;不满足就抛错中止整个评估用例剩余步骤
311
311
  * (仍会把这条断言计入报告,不影响已记录的其它断言)。跟 check 的区别:check 只记录、
312
312
  * 从不抛错,打分留到最后统一算;require 当场判定、失败即中止,适合「前置条件不满足,
313
313
  * 后面写了也没意义」的场景。
@@ -352,7 +352,7 @@ export interface TestContext {
352
352
  eventsSatisfy(label: string, predicate: (events: readonly StreamEvent[]) => boolean): AssertionHandle;
353
353
 
354
354
  // 工作区 / 沙箱
355
- /** 受限沙箱视图:能执行命令 / 读写文件 / 看最终 diff,不能 stop 沙箱本身(见 SandboxHandle)。 */
355
+ /** 受限 Sandbox 视图:能执行命令 / 读写文件 / 看最终 diff,不能 stop Sandbox 本身(见 SandboxHandle)。 */
356
356
  readonly sandbox: SandboxHandle;
357
357
 
358
358
  // 效率 / 成本
package/src/define.ts CHANGED
@@ -67,6 +67,11 @@ export function defineExperiment(def: ExperimentDef): ExperimentDef {
67
67
  throw new Error(t("define.experimentIdRejected"));
68
68
  }
69
69
  if (!def.agent) throw new Error(t("define.experimentAgentRequired"));
70
+ // setup 是实验级生命周期钩子(整场一次,宿主机侧,见 runner/types.ts 的 ExperimentDef.setup);
71
+ // 传成非函数(如误把 sandbox 钩子对象塞进来)在解析时就报,不等到调度才炸。
72
+ if (def.setup !== undefined && typeof def.setup !== "function") {
73
+ throw new Error(t("define.experimentSetupNotFunction"));
74
+ }
70
75
  // flags 必须可 JSON 序列化(进结果快照的 ExperimentRunInfo.flags):解析时即校验,
71
76
  // 非 JSON 值(函数 / undefined / 循环引用 / bigint)直接报错,不等到落盘才炸。
72
77
  if (def.flags !== undefined) {
@@ -76,6 +81,14 @@ export function defineExperiment(def: ExperimentDef): ExperimentDef {
76
81
  }
77
82
  }
78
83
  }
84
+ // labels 是报告归类坐标(进 ExperimentRunInfo.labels,不透传 ctx/t):值域 string | number,
85
+ // 解析时即校验,布尔 / 对象 / NaN 直接报错,不等到落盘或报告分组才炸。
86
+ if (def.labels !== undefined) {
87
+ for (const [key, value] of Object.entries(def.labels)) {
88
+ const ok = typeof value === "string" || (typeof value === "number" && Number.isFinite(value));
89
+ if (!ok) throw new Error(t("define.experimentLabelInvalid", { key }));
90
+ }
91
+ }
79
92
  return def;
80
93
  }
81
94
 
package/src/i18n/en.ts CHANGED
@@ -60,9 +60,16 @@ export const en = {
60
60
  "cli.flag.invalidOutput": "Flag --output expects one of auto|human|agent|ci, got \"{{value}}\".\n",
61
61
  "runner.budgetUnenforceable":
62
62
  "budget for {{budgetKey}}: several attempts completed without any cost data (agent reports no usage and the model is not in the price table) — the budget cannot be enforced for this agent; continuing without the guard.\n",
63
+ "runner.experimentTeardownFailed":
64
+ "teardown for experiment {{experimentId}} failed: {{message}}. Results are unaffected, but host-side resources started by this experiment may not have been released; check manually.\n",
65
+ "runner.cleanupTimeout": "cleanup timed out after {{timeoutMs}}ms\n",
66
+ "runner.setupReturnedCleanup":
67
+ "{{layer}} returned a function. setup does not carry cleanup and the returned value will not be executed — put the cleanup in the paired teardown of the same layer ({{hint}}); see the experiments tutorial on docs-site or docs/runner.md.\n",
68
+ "runner.experimentTeardownLate":
69
+ "experiment {{experimentId}}'s teardown was not triggered by the normal countdown path; it has been executed by the end-of-run sweep instead. Results are unaffected; seeing this line means an unlocated intermittent scheduling issue fired — please record this run in the memory ledger.\n",
63
70
  "judge.modelMissing":
64
71
  "No judge model configured. Set it in defineConfig({ judge: { model: \"...\" } }), the eval's judge config, or the NICEEVAL_JUDGE_MODEL environment variable (there is no built-in default model).\n" +
65
- " Docs: node_modules/niceeval/docs-site/zh/how-to/scoring-guide.mdx",
72
+ " Docs: node_modules/niceeval/docs-site/zh/tutorials/scoring-guide.mdx",
66
73
  "loaders.yamlMissing":
67
74
  "loadYaml(\"{{path}}\") needs a YAML parser: run `pnpm add yaml` first (or switch to loadJson with a JSON dataset).",
68
75
  "cli.flag.parseError": "{{message}}\nRun `niceeval --help` for usage.\n",
@@ -70,7 +77,7 @@ export const en = {
70
77
  "cli.help":
71
78
  "niceeval — agent-native evals\n\n" +
72
79
  "Usage:\n" +
73
- " niceeval exp [group|experiment] [eval-id-prefix…] run experiments\n" +
80
+ " niceeval exp [path|experiment] [eval-id-prefix…] run experiments\n" +
74
81
  " niceeval show [eval-id-prefix… | @<locator>] read results in the terminal\n" +
75
82
  " bare: current verdicts per experiment (composed across runs), each row\n" +
76
83
  " with a compact attempt index (locator + failure reason)\n" +
@@ -84,7 +91,7 @@ export const en = {
84
91
  " --timing unified timing tree for the attempt (phases + hooks/commands/turns + per-turn OTel)\n" +
85
92
  " --diff[=file] sandbox workspace file-change summary; =file expands one file\n" +
86
93
  " --history per experiment × eval execution timeline (mutually exclusive with --report)\n" +
87
- " --results <dir> pin a results root --experiment <id> one experiment\n" +
94
+ " --results <dir> pin a results root --exp <id> one experiment\n" +
88
95
  " --report <file> custom report --page <id> pick the initial page (multi-page\n" +
89
96
  " reports render it, then list the rest as a page index with copyable commands)\n" +
90
97
  " niceeval list list discovered evals\n" +
@@ -92,7 +99,7 @@ export const en = {
92
99
  " report pages + evidence rooms; --report <file> swaps in your report\n" +
93
100
  " (same file as show); --page <id> picks the initial page;\n" +
94
101
  " --results <dir> pins a results root; --snapshot <file> opens exactly\n" +
95
- " one snapshot; --experiment <id> one experiment\n" +
102
+ " one snapshot; --exp <id> one experiment\n" +
96
103
  " --out <dir> exports a static site: index.html plus the viewer\n" +
97
104
  " artifacts, ready for any static host\n" +
98
105
  " niceeval sandbox list|enter|history|diff|stop inspect & destroy sandboxes kept by --keep-sandbox\n" +
@@ -109,7 +116,7 @@ export const en = {
109
116
  "cli.show.noResults": "No results found under {{root}}. Run `niceeval exp` first, then `niceeval show`.\n",
110
117
  "cli.show.runDirMissing": "Results directory not found: {{dir}}\n",
111
118
  "cli.show.noEvalMatch": "No results matched: {{patterns}}. Evals with results: {{evals}}\n",
112
- "cli.show.noExperimentMatch": "No experiment matched --experiment {{arg}}. Experiments with results: {{experiments}}\n",
119
+ "cli.show.noExperimentMatch": "No experiment matched --exp {{arg}}. Experiments with results: {{experiments}}\n",
113
120
  "cli.show.historyReportConflict":
114
121
  "`--history` and `--report` are mutually exclusive: both take over the main output. --history is the host's per-attempt execution timeline; for snapshot-level trends, compose exp.snapshots inside your report file instead.\n",
115
122
  "cli.show.evidenceNeedsEval":
@@ -121,10 +128,14 @@ export const en = {
121
128
  "cli.eval.noMatchKnown": "Discovered {{count}} evals: {{evals}}\n",
122
129
  "cli.exp.agentModelFlagUnsupported": "experiment runs do not support --agent / --model. Add or copy an experiment file and change its model instead.\n",
123
130
  "cli.exp.viewerFlagUnsupported": "`{{flag}}` only applies to niceeval {{command}}, not niceeval exp.\n",
124
- "cli.experiment.noMatch": "No experiment matched: {{arg}}. Discovered: {{experiments}}\n",
131
+ "cli.experiment.noMatch":
132
+ "No experiment matched: {{arg}}. Available paths: {{experiments}}.\n" +
133
+ "Run `niceeval exp <path> --dry` to preview a plan.\n",
125
134
  "cli.experiment.viewerCommandHint": "Did you mean: niceeval {{command}}{{args}}\n",
126
- "cli.experiment.noEvalsSelected": "No evals selected: {{selection}} matched 0 evals. Available experiments: {{experiments}}.\n",
127
- "cli.experimentGroup": " group",
135
+ "cli.experiment.noEvalsSelected":
136
+ "No evals selected: {{selection}} matched 0 evals. Available eval prefixes: {{experiments}}.\n" +
137
+ "Run `niceeval exp {{selection}} --dry` to see what it covers, or drop the eval filter to run every eval selected by those experiments.\n",
138
+ "cli.experimentGroup": " path",
128
139
  "cli.fallbackCleanupTimeout": "\ngraceful cleanup timed out; force-cleaning sandboxes...\n",
129
140
  "cli.forceCleanupExit": "\nForce-cleaning sandboxes and exiting...\n",
130
141
  "cli.init.done": "Ready: evals/, niceeval.config.ts, and the niceeval agent-rules block in AGENTS.md (points coding agents at node_modules/niceeval/docs-site/zh).\n",
@@ -135,8 +146,8 @@ export const en = {
135
146
  "cli.pressCtrlC": "Press Ctrl+C to exit.\n",
136
147
  "cli.resultsPath": "Structured results: {{path}} (snapshot.json + per-attempt result.json / events.json / trace.json / diff.json)\n",
137
148
  "cli.run.experimentRequired":
138
- "Run evals through an experiment: use `niceeval exp [group|config] [eval id prefix]`.\n" +
139
- " Docs: node_modules/niceeval/docs-site/zh/how-to/write-experiment.mdx\n",
149
+ "Run evals through an experiment: use `niceeval exp [path|config] [eval id prefix]`.\n" +
150
+ " Docs: node_modules/niceeval/docs-site/zh/tutorials/write-experiment.mdx\n",
140
151
  "cli.run.experimentRequiredHint": "Hint: \"{{pattern}}\" is an experiment{{kind}}; you probably meant: niceeval exp {{pattern}}\n",
141
152
  "cli.run.experimentRequiredKnown": "Discovered experiments: {{experiments}}\n",
142
153
  "cli.unimplemented": "Command \"{{command}}\" is not implemented yet (MVP).\n",
@@ -147,7 +158,7 @@ export const en = {
147
158
  "cli.view.url": "niceeval view: {{url}}\n",
148
159
  "context.capabilityMissing":
149
160
  "Agent \"{{agent}}\" is not sandbox-backed (built with defineSandboxAgent), so t.{{method}} is unavailable. Use an agent built with defineSandboxAgent, or drop this assertion.\n" +
150
- " Docs: node_modules/niceeval/docs-site/zh/how-to/sandbox-agent.mdx",
161
+ " Docs: node_modules/niceeval/docs-site/zh/tutorials/sandbox-agent.mdx",
151
162
  "context.skipEmpty": "skip() requires a non-empty reason.",
152
163
  "context.turnFailed": "This send returned failed (turn status = failed): {{message}}",
153
164
  "context.turnFailedDefault": "This send returned failed (turn status = failed)",
@@ -157,6 +168,8 @@ export const en = {
157
168
  "define.evalTestRequired": "defineEval requires an async test(t) function.",
158
169
  "define.experimentAgentRequired": "defineExperiment requires agent.",
159
170
  "define.experimentFlagNotJson": "experiment.flags.{{key}} is not JSON-serializable (functions / undefined / cycles / bigint are not allowed); flags are persisted verbatim into result snapshots and must be plain JSON.",
171
+ "define.experimentLabelInvalid": "experiment.labels.{{key}} must be a string or a finite number; labels are report-side grouping coordinates persisted verbatim into result snapshots.",
172
+ "define.experimentSetupNotFunction": "experiment.setup must be a function ((ctx) => void); use experiment.teardown for cleanup; to prepare the in-sandbox environment per experiment, chain .setup() hooks on the sandbox spec instead.",
160
173
  "define.experimentIdRejected": "defineExperiment does not accept id; ids are derived from file paths.",
161
174
  "define.sandboxAgentNameRequired": "defineSandboxAgent requires name.",
162
175
  "define.sandboxCreateRequired": "defineSandbox requires a create() function.",
@@ -169,7 +182,7 @@ export const en = {
169
182
  "docker.unsupportedRuntime": "Unsupported runtime: {{runtime}}",
170
183
  "feedback.human.active": "ACTIVE",
171
184
  "feedback.human.budgetExhausted": "budget exhausted for {{experimentId}} (spent {{spent}}, unstarted {{unstarted}})",
172
- "feedback.human.compare": "Compare: niceeval view {{group}}",
185
+ "feedback.human.compare": "Compare: niceeval view",
173
186
  "feedback.human.counts": "{{total}} total · {{reused}} reused · {{running}} running · {{queued}} queued · {{completed}} completed",
174
187
  "feedback.human.diffHint": "Diff: niceeval show {{locator}} --diff",
175
188
  "feedback.human.evalHint": "Eval: niceeval show {{locator}} --source",
@@ -194,6 +207,10 @@ export const en = {
194
207
  "feedback.phase.evalSetup": "eval setup",
195
208
  "feedback.phase.sandboxCreate": "creating sandbox",
196
209
  "feedback.phase.sandboxQueue": "queued for sandbox",
210
+ "feedback.phase.experimentSetup": "experiment setup",
211
+ "feedback.phase.experimentTeardown": "experiment teardown",
212
+ "feedback.human.hookDone": "done",
213
+ "feedback.human.hookFailed": "failed",
197
214
  "feedback.phase.sandboxSetup": "sandbox setup",
198
215
  "feedback.phase.scoring": "scoring",
199
216
  "feedback.phase.teardown": "cleaning up",
@@ -273,13 +290,11 @@ export const en = {
273
290
  "runner.startSandboxTeardown": "sandbox teardown (environment provisioning hooks)...",
274
291
  "runner.timeout": "attempt timed out ({{timeoutMs}}ms)\nRecent progress:\n{{recentLogs}}",
275
292
  "runner.traceSelected": " -> kept {{count}} semantic spans",
276
- "runner.resumeCarry": " · reusing {{carried}} settled results from last run, re-running {{retry}} evals\n",
277
- "runner.resumeCarryDetail": " carried [{{experiment}}] {{evals}}\n",
278
293
  "runner.useRemoteAgent": "using remote agent (no sandbox created)...",
279
294
  "sandbox.providerNotImplemented": "{{provider}} sandbox provider is not implemented; use docker, vercel, or e2b",
280
295
  "sandbox.missingSpec":
281
296
  "sandbox agent needs a sandbox, but none was given. niceeval no longer picks a default — set `sandbox` in defineExperiment()/defineConfig() to dockerSandbox() / vercelSandbox() / e2bSandbox() (import from \"niceeval/sandbox\").\n" +
282
- " Docs: node_modules/niceeval/docs-site/zh/how-to/sandbox-providers.mdx",
297
+ " Docs: node_modules/niceeval/docs-site/zh/tutorials/sandbox-providers.mdx",
283
298
  "sandbox.dependencyMissing.docker": "Docker sandbox requires 'dockerode'. Install it with: pnpm add dockerode @types/dockerode",
284
299
  "sandbox.dependencyMissing.e2b": "E2B sandbox requires 'e2b'. Install it with: pnpm add e2b",
285
300
  "sandbox.dependencyMissing.vercel": "Vercel sandbox requires '@vercel/sandbox'. Install it with: pnpm add @vercel/sandbox",