niceeval 0.9.1 → 0.10.2

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 (432) 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 +14 -6
  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 +10 -0
  17. package/dist/report/components/attempt-detail/AttemptConversation.js +74 -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 +97 -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 +297 -0
  38. package/dist/report/components/attempt-detail/faces.d.ts +14 -0
  39. package/dist/report/components/attempt-detail/faces.js +239 -0
  40. package/dist/report/components/attempt-detail/index.d.ts +38 -0
  41. package/dist/report/components/attempt-detail/index.js +547 -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 +4 -5
  49. package/dist/report/{react → components/entity-lists}/ExperimentList.js +12 -11
  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 +175 -0
  54. package/dist/report/components/entity-lists/index.d.ts +49 -0
  55. package/dist/report/components/entity-lists/index.js +169 -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 +27 -60
  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} +43 -11
  118. package/dist/report/{tree.js → definition/tree.js} +9 -11
  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} +13 -7
  126. package/dist/report/{format.js → model/format.js} +79 -9
  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} +154 -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 +11 -1
  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 +151 -24
  148. package/dist/sandbox/docker.d.ts +6 -0
  149. package/dist/sandbox/e2b.d.ts +0 -1
  150. package/dist/sandbox/types.d.ts +23 -21
  151. package/dist/scoring/display.d.ts +7 -2
  152. package/dist/scoring/display.js +42 -4
  153. package/dist/scoring/types.d.ts +3 -3
  154. package/dist/shared/aggregate.d.ts +14 -0
  155. package/dist/shared/aggregate.js +31 -0
  156. package/dist/shared/types.d.ts +6 -1
  157. package/docs-site/images/logo-dark.svg +7 -0
  158. package/docs-site/images/logo.svg +6 -5
  159. package/docs-site/zh/README.md +8 -7
  160. package/docs-site/zh/examples/ai-agent-application.mdx +5 -5
  161. package/docs-site/zh/examples/coding-agent-extensions.mdx +4 -4
  162. package/docs-site/zh/examples/index.mdx +3 -3
  163. package/docs-site/zh/examples/integrations/ai-sdk-v7.mdx +6 -6
  164. package/docs-site/zh/examples/integrations/claude-sdk.mdx +3 -3
  165. package/docs-site/zh/examples/integrations/codex-sdk.mdx +3 -3
  166. package/docs-site/zh/examples/integrations/langgraph.mdx +3 -3
  167. package/docs-site/zh/examples/integrations/pi-sdk.mdx +2 -2
  168. package/docs-site/zh/explanation/adapter.mdx +19 -19
  169. package/docs-site/zh/explanation/assert.mdx +9 -9
  170. package/docs-site/zh/explanation/drive.mdx +4 -4
  171. package/docs-site/zh/explanation/evals.mdx +8 -8
  172. package/docs-site/zh/explanation/experiment.mdx +8 -8
  173. package/docs-site/zh/explanation/hitl.mdx +13 -13
  174. package/docs-site/zh/explanation/judge.mdx +3 -3
  175. package/docs-site/zh/explanation/overview.mdx +7 -7
  176. package/docs-site/zh/explanation/runner.mdx +17 -13
  177. package/docs-site/zh/explanation/tier.mdx +6 -6
  178. package/docs-site/zh/index.mdx +14 -14
  179. package/docs-site/zh/introduction.mdx +13 -16
  180. package/docs-site/zh/reference/builtin-agents.mdx +61 -28
  181. package/docs-site/zh/reference/capabilities.mdx +7 -27
  182. package/docs-site/zh/reference/cli.mdx +36 -33
  183. package/docs-site/zh/reference/define-agent.mdx +32 -31
  184. package/docs-site/zh/reference/define-config.mdx +6 -6
  185. package/docs-site/zh/reference/define-eval.mdx +33 -20
  186. package/docs-site/zh/reference/events.mdx +4 -4
  187. package/docs-site/zh/reference/expect.mdx +3 -3
  188. package/docs-site/zh/reference/official-adapters.mdx +17 -17
  189. package/docs-site/zh/reference/report-components.mdx +195 -121
  190. package/docs-site/zh/reference/results-data.mdx +13 -13
  191. package/docs-site/zh/troubleshooting/debug-sandbox.mdx +11 -11
  192. package/docs-site/zh/troubleshooting/debugging.mdx +40 -16
  193. package/docs-site/zh/{how-to → tutorials}/agent-feedback-loop.mdx +38 -38
  194. package/docs-site/zh/tutorials/agent-onboarding.mdx +100 -0
  195. package/docs-site/zh/tutorials/authoring.mdx +211 -0
  196. package/docs-site/zh/{how-to → tutorials}/ci-integration.mdx +8 -8
  197. package/docs-site/zh/{how-to → tutorials}/connect-otel.mdx +22 -22
  198. package/docs-site/zh/{how-to → tutorials}/connect-your-agent.mdx +58 -63
  199. package/docs-site/zh/tutorials/custom-reports.mdx +453 -0
  200. package/docs-site/zh/{how-to → tutorials}/dataset-fanout.mdx +4 -4
  201. package/docs-site/zh/tutorials/experiments.mdx +102 -0
  202. package/docs-site/zh/{how-to → tutorials}/fixtures.mdx +9 -9
  203. package/docs-site/zh/{how-to → tutorials}/publish-report.mdx +6 -6
  204. package/docs-site/zh/tutorials/quickstart.mdx +18 -18
  205. package/docs-site/zh/{how-to → tutorials}/reporters.mdx +6 -6
  206. package/docs-site/zh/{how-to → tutorials}/sandbox-agent.mdx +6 -6
  207. package/docs-site/zh/{how-to → tutorials}/sandbox-providers.mdx +41 -19
  208. package/docs-site/zh/{how-to → tutorials}/scoring-guide.mdx +4 -4
  209. package/docs-site/zh/{how-to → tutorials}/viewing-results.mdx +60 -57
  210. package/docs-site/zh/tutorials/write-experiment.mdx +353 -0
  211. package/docs-site/zh/{how-to → tutorials}/write-send.mdx +40 -40
  212. package/package.json +10 -7
  213. package/src/agents/bub.ts +21 -8
  214. package/src/agents/claude-code.ts +23 -10
  215. package/src/agents/codex.test.ts +22 -7
  216. package/src/agents/codex.ts +22 -9
  217. package/src/agents/index.ts +2 -2
  218. package/src/agents/openai-compat.ts +1 -1
  219. package/src/agents/post-setup.ts +45 -22
  220. package/src/agents/streaming.ts +1 -1
  221. package/src/agents/types.ts +20 -17
  222. package/src/agents/ui-message-stream.test.ts +10 -0
  223. package/src/agents/ui-message-stream.ts +12 -1
  224. package/src/cli.ts +75 -59
  225. package/src/context/types.ts +21 -21
  226. package/src/define.ts +13 -0
  227. package/src/i18n/en.ts +27 -12
  228. package/src/i18n/zh-CN.ts +27 -12
  229. package/src/index.ts +2 -0
  230. package/src/o11y/prices.json +176 -99
  231. package/src/report/{react → assets}/colors.ts +22 -0
  232. package/src/report/{react → assets}/enhance.js +1 -33
  233. package/src/report/assets/styles.css +1785 -0
  234. package/src/report/built-in/index.tsx +2 -2
  235. package/src/report/built-in/standard.tsx +18 -6
  236. package/src/report/components/attempt-detail/AttemptAssertions.tsx +67 -0
  237. package/src/report/components/attempt-detail/AttemptConversation.tsx +158 -0
  238. package/src/report/components/attempt-detail/AttemptDiagnostics.tsx +38 -0
  239. package/src/report/components/attempt-detail/AttemptDiff.tsx +35 -0
  240. package/src/report/components/attempt-detail/AttemptError.tsx +31 -0
  241. package/src/report/components/attempt-detail/AttemptFixPrompt.tsx +26 -0
  242. package/src/report/components/attempt-detail/AttemptSource.tsx +224 -0
  243. package/src/report/components/attempt-detail/AttemptSummary.tsx +74 -0
  244. package/src/report/components/attempt-detail/AttemptTimeline.tsx +108 -0
  245. package/src/report/components/attempt-detail/AttemptTrace.tsx +63 -0
  246. package/src/report/components/attempt-detail/AttemptUsage.tsx +29 -0
  247. package/src/report/components/attempt-detail/attempt-components.test.tsx +758 -0
  248. package/src/report/components/attempt-detail/compute.ts +336 -0
  249. package/src/report/components/attempt-detail/faces.ts +260 -0
  250. package/src/report/components/attempt-detail/index.tsx +600 -0
  251. package/src/report/components/attempt-detail/validate.test.ts +252 -0
  252. package/src/report/{react → components}/cell.tsx +6 -3
  253. package/src/report/{report.test.ts → components/compute.test.ts} +316 -46
  254. package/src/report/{react → components/entity-lists}/AttemptList.tsx +24 -16
  255. package/src/report/{react → components/entity-lists}/EvalList.tsx +0 -0
  256. package/src/report/{react → components/entity-lists}/ExperimentList.tsx +33 -27
  257. package/src/report/components/entity-lists/compute.ts +196 -0
  258. package/src/report/components/entity-lists/faces.ts +208 -0
  259. package/src/report/components/entity-lists/index.tsx +238 -0
  260. package/src/report/components/entity-lists/validate.test.ts +114 -0
  261. package/src/report/{react → components}/fixtures.ts +2 -2
  262. package/src/report/{react → components/metric-views}/DeltaTable.tsx +5 -5
  263. package/src/report/{react → components/metric-views}/MetricBars.tsx +5 -5
  264. package/src/report/{react → components/metric-views}/MetricLine.tsx +9 -6
  265. package/src/report/{react → components/metric-views}/MetricMatrix.tsx +6 -6
  266. package/src/report/{react → components/metric-views}/MetricScatter.tsx +42 -70
  267. package/src/report/{react → components/metric-views}/MetricTable.tsx +6 -6
  268. package/src/report/{react → components/metric-views}/Scoreboard.tsx +4 -4
  269. package/src/report/{compute.ts → components/metric-views/compute.ts} +51 -477
  270. package/src/report/components/metric-views/faces.ts +423 -0
  271. package/src/report/components/metric-views/index.tsx +352 -0
  272. package/src/report/{text → components/metric-views}/plot.ts +1 -1
  273. package/src/report/components/metric-views/validate.test.ts +211 -0
  274. package/src/report/{react → components}/render.test.tsx +66 -26
  275. package/src/report/components/shared-compute.ts +59 -0
  276. package/src/report/components/shared-faces.ts +30 -0
  277. package/src/report/components/shared.ts +192 -0
  278. package/src/report/{react → components/site-components}/CopyFixPrompt.tsx +3 -3
  279. package/src/report/{react → components/site-components}/HeroCard.tsx +3 -3
  280. package/src/report/{react → components/site-components}/ScopeWarnings.tsx +4 -4
  281. package/src/report/{react → components/site-components}/TraceWaterfall.tsx +13 -10
  282. package/src/report/components/site-components/compute.ts +144 -0
  283. package/src/report/components/site-components/faces.ts +80 -0
  284. package/src/report/components/site-components/index.tsx +265 -0
  285. package/src/report/{scope-warnings.ts → components/site-components/scope-warnings.ts} +3 -3
  286. package/src/report/{site-components.test.tsx → components/site-components/site-components.test.tsx} +23 -21
  287. package/src/report/components/site-components/validate.test.ts +89 -0
  288. package/src/report/{react → components/summaries}/ScopeSummary.tsx +26 -11
  289. package/src/report/components/summaries/compute.ts +56 -0
  290. package/src/report/components/summaries/faces.ts +48 -0
  291. package/src/report/components/summaries/index.tsx +127 -0
  292. package/src/report/components/summaries/validate.test.ts +49 -0
  293. package/src/report/definition/grid-layout.test.ts +124 -0
  294. package/src/report/definition/grid-layout.ts +146 -0
  295. package/src/report/{primitives.tsx → definition/primitives.tsx} +168 -18
  296. package/src/report/{report.ts → definition/report.ts} +98 -165
  297. package/src/report/{shell-head.test.ts → definition/shell-head.test.ts} +2 -2
  298. package/src/report/{text/table.ts → definition/table-text.ts} +4 -4
  299. package/src/report/{tree.ts → definition/tree.ts} +56 -20
  300. package/src/report/index.ts +128 -66
  301. package/src/report/{aggregate.ts → model/aggregate.ts} +31 -9
  302. package/src/report/{flag.ts → model/flag.ts} +39 -1
  303. package/src/report/{format.ts → model/format.ts} +89 -10
  304. package/src/report/{locale.ts → model/locale.ts} +34 -36
  305. package/src/report/{metrics.ts → model/metrics.ts} +3 -3
  306. package/src/report/{types.ts → model/types.ts} +159 -22
  307. package/src/report/react/index.tsx +54 -26
  308. package/src/report/{dual-render.test.tsx → runtime/dual-render.test.tsx} +601 -53
  309. package/src/report/runtime/host.test.ts +44 -0
  310. package/src/report/runtime/host.ts +138 -0
  311. package/src/report/{load.ts → runtime/load.ts} +1 -1
  312. package/src/report/runtime/text.ts +192 -0
  313. package/src/report/runtime/web.ts +106 -0
  314. package/src/results/attempt-evidence.ts +5 -1
  315. package/src/results/host-equivalence.test.ts +53 -2
  316. package/src/results/select.ts +29 -5
  317. package/src/runner/attempt.test.ts +48 -1
  318. package/src/runner/attempt.ts +62 -42
  319. package/src/runner/cleanup-timeout.test.ts +16 -0
  320. package/src/runner/cleanup-timeout.ts +23 -0
  321. package/src/runner/discover.ts +1 -2
  322. package/src/runner/eval-selection.test.ts +187 -0
  323. package/src/runner/eval-selection.ts +109 -0
  324. package/src/runner/experiment-cleanup-registry.test.ts +89 -0
  325. package/src/runner/experiment-cleanup-registry.ts +39 -0
  326. package/src/runner/experiment-labels.test.ts +71 -0
  327. package/src/runner/feedback/agent.test.ts +33 -1
  328. package/src/runner/feedback/agent.ts +12 -16
  329. package/src/runner/feedback/ci.test.ts +35 -1
  330. package/src/runner/feedback/ci.ts +16 -18
  331. package/src/runner/feedback/coordinator.ts +28 -0
  332. package/src/runner/feedback/human.test.ts +108 -1
  333. package/src/runner/feedback/human.ts +71 -21
  334. package/src/runner/feedback/reducer.test.ts +64 -0
  335. package/src/runner/feedback/reducer.ts +26 -0
  336. package/src/runner/feedback/sink.ts +40 -1
  337. package/src/runner/fingerprint.ts +2 -1
  338. package/src/runner/report.test.ts +14 -0
  339. package/src/runner/report.ts +3 -1
  340. package/src/runner/run.test.ts +390 -12
  341. package/src/runner/run.ts +259 -26
  342. package/src/runner/sandbox-selection.test.ts +13 -3
  343. package/src/runner/sandbox-selection.ts +4 -2
  344. package/src/runner/types.ts +160 -24
  345. package/src/sandbox/docker.ts +7 -0
  346. package/src/sandbox/e2b-reconcile.test.ts +125 -0
  347. package/src/sandbox/e2b.ts +47 -12
  348. package/src/sandbox/types.ts +23 -21
  349. package/src/scoring/display.test.ts +28 -2
  350. package/src/scoring/display.ts +38 -5
  351. package/src/scoring/types.ts +3 -3
  352. package/src/shared/aggregate.test.ts +52 -0
  353. package/src/shared/aggregate.ts +29 -0
  354. package/src/shared/types.ts +6 -1
  355. package/src/show/command.test.ts +34 -0
  356. package/src/show/command.ts +16 -0
  357. package/src/show/compose.ts +1 -1
  358. package/src/show/index.ts +56 -20
  359. package/src/show/render.ts +15 -239
  360. package/src/show/show.test.ts +127 -34
  361. package/src/view/app/App.test.tsx +1 -1
  362. package/src/view/app/App.tsx +119 -47
  363. package/src/view/app/i18n.ts +7 -147
  364. package/src/view/app/lib/attempt-dialog.test.ts +65 -0
  365. package/src/view/app/lib/attempt-dialog.ts +69 -0
  366. package/src/view/app/main.tsx +0 -1
  367. package/src/view/app/types.ts +3 -86
  368. package/src/view/artifact-serving.test.ts +22 -38
  369. package/src/view/client-dist/app.css +1 -1
  370. package/src/view/client-dist/app.js +15 -22
  371. package/src/view/data.test.ts +48 -36
  372. package/src/view/data.ts +105 -83
  373. package/src/view/index.ts +1 -1
  374. package/src/view/server.ts +32 -2
  375. package/src/view/shared/types.ts +6 -31
  376. package/src/view/site-parity.test.ts +24 -1
  377. package/src/view/site.ts +144 -17
  378. package/src/view/styles.css +0 -805
  379. package/src/view/view-report.test.ts +143 -22
  380. package/dist/report/components.d.ts +0 -179
  381. package/dist/report/components.js +0 -544
  382. package/dist/report/compute.d.ts +0 -139
  383. package/dist/report/react/ExperimentComparison.d.ts +0 -10
  384. package/dist/report/react/ExperimentComparison.js +0 -12
  385. package/dist/report/react/MetricScatter.d.ts +0 -9
  386. package/dist/report/react/format.d.ts +0 -3
  387. package/dist/report/react/format.js +0 -7
  388. package/dist/report/text/faces.d.ts +0 -45
  389. package/dist/report/text/faces.js +0 -671
  390. package/dist/report/web.d.ts +0 -32
  391. package/dist/report/web.js +0 -48
  392. package/docs-site/zh/how-to/authoring.mdx +0 -162
  393. package/docs-site/zh/how-to/custom-reports.mdx +0 -414
  394. package/docs-site/zh/how-to/experiments.mdx +0 -86
  395. package/docs-site/zh/how-to/write-experiment.mdx +0 -164
  396. package/src/report/components.tsx +0 -925
  397. package/src/report/react/ExperimentComparison.tsx +0 -73
  398. package/src/report/react/format.ts +0 -9
  399. package/src/report/react/styles.css +0 -945
  400. package/src/report/text/faces.ts +0 -767
  401. package/src/report/web.ts +0 -77
  402. package/src/show/report-host.test.ts +0 -205
  403. package/src/show/report-host.ts +0 -389
  404. package/src/view/app/components/AttemptModal.tsx +0 -496
  405. package/src/view/app/components/CodeView.test.tsx +0 -142
  406. package/src/view/app/components/CodeView.tsx +0 -310
  407. package/src/view/app/components/CopyControls.tsx +0 -106
  408. package/src/view/app/components/Trace.tsx +0 -100
  409. package/src/view/app/components/Transcript.tsx +0 -157
  410. package/src/view/app/components/ui/badge.tsx +0 -21
  411. package/src/view/app/lib/artifact-url.ts +0 -17
  412. package/src/view/app/lib/attempt-route.test.ts +0 -80
  413. package/src/view/app/lib/attempt-route.ts +0 -52
  414. package/src/view/app/lib/format.ts +0 -70
  415. package/src/view/app/lib/guards.test.ts +0 -108
  416. package/src/view/app/lib/guards.ts +0 -71
  417. package/src/view/app/lib/rows.ts +0 -22
  418. package/src/view/app/lib/transcript-data.tsx +0 -151
  419. package/src/view/app/lib/verdict.ts +0 -23
  420. package/src/view/app/shared.ts +0 -8
  421. /package/dist/report/{react → components/metric-views}/chart-math.d.ts +0 -0
  422. /package/dist/report/{react → components/metric-views}/chart-math.js +0 -0
  423. /package/dist/report/{text → components/metric-views}/plot.d.ts +0 -0
  424. /package/dist/report/{react → components/site-components}/PoweredBy.d.ts +0 -0
  425. /package/dist/report/{react → components/site-components}/PoweredBy.js +0 -0
  426. /package/dist/report/{text/layout.d.ts → model/text-layout.d.ts} +0 -0
  427. /package/dist/report/{text/layout.js → model/text-layout.js} +0 -0
  428. /package/dist/report/{types.js → model/types.js} +0 -0
  429. /package/src/report/{react → components/metric-views}/chart-math.test.ts +0 -0
  430. /package/src/report/{react → components/metric-views}/chart-math.ts +0 -0
  431. /package/src/report/{react → components/site-components}/PoweredBy.tsx +0 -0
  432. /package/src/report/{text/layout.ts → model/text-layout.ts} +0 -0
@@ -1,9 +1,15 @@
1
- import { useCallback, useEffect, useMemo, useRef, useState } from "react";
1
+ import { useCallback, useEffect, useMemo, useRef, useState, type CSSProperties } from "react";
2
2
  import { detectLocale, makeTranslator, persistLocale, setDocumentLocale } from "./i18n.ts";
3
- import type { Locale, LocalizedText, ReportSlotHtml, Tab, ViewData, ViewReportPageMeta, ViewResult } from "./types.ts";
4
- import { resultFromUrl } from "./lib/rows.ts";
5
- import { parseAttemptHash, resolveAttemptLocator, unresolvedAttemptWarning } from "./lib/attempt-route.ts";
6
- import { AttemptModal } from "./components/AttemptModal.tsx";
3
+ import type { Locale, LocalizedText, ReportSlotHtml, Tab, ViewData, ViewReportPageMeta } from "./types.ts";
4
+ import {
5
+ attemptHrefFor,
6
+ attemptLocatorFromHref,
7
+ hashForAttempt,
8
+ locatorFromHash,
9
+ parseAttemptDocument,
10
+ type AttemptDocumentContent,
11
+ } from "./lib/attempt-dialog.ts";
12
+ import { Dialog, DialogClose, DialogContent, DialogTitle } from "./components/ui/dialog.tsx";
7
13
  import { Tabs, TabsContent, TabsList, TabsTrigger } from "./components/ui/tabs.tsx";
8
14
 
9
15
  // 导航组成只有一条规则(docs/feature/reports/view.md「页面构成」):导航项 = 报告定义声明的页,
@@ -17,6 +23,20 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "./components/ui/tabs.t
17
23
  // utm_medium 区分点击来自哪个品牌位(shell.md「行为约束」)。
18
24
  const BRAND_HREF = "https://niceeval.com/?utm_source=report&utm_medium=brand";
19
25
 
26
+ /** 屏幕阅读器可用、视觉上不占位:Radix Dialog 需要一个可访问标题,内容本身(身份 / verdict)
27
+ * 已经在 dialog 里可见,不需要再视觉重复一遍。 */
28
+ const VISUALLY_HIDDEN: CSSProperties = {
29
+ position: "absolute",
30
+ width: 1,
31
+ height: 1,
32
+ padding: 0,
33
+ margin: -1,
34
+ overflow: "hidden",
35
+ clip: "rect(0, 0, 0, 0)",
36
+ whiteSpace: "nowrap",
37
+ border: 0,
38
+ };
39
+
20
40
  /**
21
41
  * LocalizedText 的确定回退(docs/feature/reports/library/shell.md):当前 locale → en →
22
42
  * 按 locale 键字典序的第一个非空值。字符串原样返回。
@@ -32,19 +52,6 @@ export function localizedText(text: LocalizedText | undefined, locale: Locale):
32
52
  return undefined;
33
53
  }
34
54
 
35
- /** 初始 URL → 直接打开的 attempt:先认 #/attempt/@<locator> 深链,回退旧版 ?modal= 参数。 */
36
- function modalResultFromLocation(snapshots: ViewData["snapshots"]): ViewResult | null {
37
- const locator = parseAttemptHash(location.hash);
38
- if (locator) {
39
- const found = resolveAttemptLocator(snapshots, locator);
40
- if (found) return found;
41
- // 定位不到(locator 不在、快照未加载、旧格式数据):不开空 modal,页面照常渲染。
42
- console.warn(unresolvedAttemptWarning(location.hash));
43
- return null;
44
- }
45
- return resultFromUrl(snapshots);
46
- }
47
-
48
55
  /**
49
56
  * 报告槽:React 19 对 dangerouslySetInnerHTML 只比较对象身份,身份一变就无条件重设
50
57
  * innerHTML(不再比对 __html 字符串值)。{__html} 必须 memo 住,否则 App 任意一次重渲染
@@ -68,7 +75,6 @@ function hashForTab(tab: Tab): string {
68
75
  }
69
76
 
70
77
  export function App({ data, reportPages }: { data: ViewData; reportPages: Record<string, ReportSlotHtml> }) {
71
- const snapshots = data.snapshots ?? [];
72
78
  const [locale, setLocale] = useState<Locale>(() => detectLocale());
73
79
  const t = useMemo(() => makeTranslator(locale), [locale]);
74
80
 
@@ -79,11 +85,16 @@ export function App({ data, reportPages }: { data: ViewData; reportPages: Record
79
85
  const initialPageId = data.report?.initialPageId ?? pages[0]!.id;
80
86
 
81
87
  const [tab, setTab] = useState<Tab>(() => tabFromHash(location.hash, pages) ?? `page:${initialPageId}`);
82
- const [modalResult, setModalResult] = useState<ViewResult | null>(() => modalResultFromLocation(snapshots));
83
- // 当前 modal 的 hash 历史条目前面是否还有本页条目(本页 push 的 / 前进键回到的):
88
+
89
+ // attempt 详情弹窗:内容是独立文档(attempt/<locator>.html)fetch 回来的同一份 server-rendered
90
+ // 片段,不维护第二份客户端渲染(docs/feature/reports/view.md「静态导出」)。dialogLocator 为
91
+ // null 即关闭。
92
+ const [dialogLocator, setDialogLocator] = useState<string | null>(null);
93
+ const [dialogContent, setDialogContent] = useState<AttemptDocumentContent | null>(null);
94
+ // 当前 dialog 的 hash 历史条目前面是否还有本页条目(点击链接 push 的 / 前进键回到的):
84
95
  // true → UI 关闭走 history.back(),前进键还能重新打开;false(深链直接落地)→ 原地抹 hash,
85
96
  // 免得 back 把用户弹出站外。
86
- const modalOwnsHistory = useRef(false);
97
+ const dialogOwnsHistory = useRef(false);
87
98
 
88
99
  useEffect(() => {
89
100
  setDocumentLocale(locale);
@@ -98,48 +109,88 @@ export function App({ data, reportPages }: { data: ViewData; reportPages: Record
98
109
  document.title = shellTitle;
99
110
  }, [shellTitle]);
100
111
 
101
- const closeModal = useCallback(() => {
102
- setModalResult(null);
103
- if (modalOwnsHistory.current) {
104
- modalOwnsHistory.current = false;
112
+ /** fetch 一个 locator 的独立文档、抠出两种语言内容并打开 dialog;定位不到就直说,不开空 dialog。 */
113
+ const openAttempt = useCallback(async (locator: string, ownsHistory: boolean) => {
114
+ try {
115
+ const res = await fetch(attemptHrefFor(locator));
116
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
117
+ const content = parseAttemptDocument(await res.text());
118
+ if (!content) throw new Error("response is not a recognized attempt document");
119
+ dialogOwnsHistory.current = ownsHistory;
120
+ setDialogLocator(locator);
121
+ setDialogContent(content);
122
+ } catch (e) {
123
+ console.warn(
124
+ `[niceeval view] failed to open attempt "${locator}": ${e instanceof Error ? e.message : String(e)}`,
125
+ );
126
+ }
127
+ }, []);
128
+
129
+ const closeDialog = useCallback(() => {
130
+ setDialogLocator(null);
131
+ setDialogContent(null);
132
+ if (dialogOwnsHistory.current) {
133
+ dialogOwnsHistory.current = false;
105
134
  history.back();
106
135
  return;
107
136
  }
108
137
  try {
109
- // 深链直接落地 / 旧版 ?modal= 链接:没有可回退的本页条目,原地还原成无 modal 的 URL。
110
- history.replaceState(null, "", location.pathname);
138
+ // 深链直接落地:没有可回退的本页条目,原地还原成无 hash 的 URL。
139
+ history.replaceState(null, "", location.pathname + location.search);
111
140
  } catch {
112
141
  // 还原 URL 失败不影响关闭。
113
142
  }
114
143
  }, []);
115
144
 
116
- // 浏览器前进/后退、手改 hash、页内链接(attempt 深链与 `#/page/<id>` 页路由)统一从
117
- // hashchange 分发:attempt hash 开证据室弹窗,页 hash 切当前 tab。
118
- // attempt 详情路由对有效根解析(viewData.snapshots 即有效根):收窄之内、不在页面统计
119
- // 口径(现刻水位)里的历史 attempt 仍能经深链打开,报告里的证据引用不因统计口径失效。
145
+ // 初始 URL 已经是 #/attempt/@<locator> 深链:直接打开(不经 hashchange——那只在后续变化时触发)
146
+ // 有效根即收窄后的结果(view.md「打开与收窄」):这份路由对完整结果根解析,不受当前统计口径
147
+ // (现刻水位)限制,深链因此对历史 attempt 同样可达;收窄之外的 locator 由这份文档自身
148
+ // 的宿主寻址语义处理(本地越过收窄解析,导出站按证据缺失呈现,不是这里的关注点)
149
+ // 空依赖数组是有意的:只在挂载时检查一次初始 hash,openAttempt 本身是 useCallback(deps: [])
150
+ // 的稳定引用,不会随后续渲染变化。
151
+ useEffect(() => {
152
+ const locator = locatorFromHash(location.hash);
153
+ if (locator) void openAttempt(locator, false);
154
+ }, [openAttempt]);
155
+
156
+ // 浏览器前进/后退、手改 hash、页内链接点击(经下面的点击拦截转成 hash 变化)统一从
157
+ // hashchange 分发:attempt hash 开证据室 dialog,页 hash 切当前 tab。
120
158
  useEffect(() => {
121
159
  const onHashChange = () => {
122
- const locator = parseAttemptHash(location.hash);
160
+ const locator = locatorFromHash(location.hash);
123
161
  if (locator) {
124
- const found = resolveAttemptLocator(snapshots, locator);
125
- if (!found) {
126
- console.warn(unresolvedAttemptWarning(location.hash));
127
- setModalResult(null);
128
- return;
129
- }
130
- // 经浏览器导航打开:前一条历史仍是本页,UI 关闭可以安全 back()。
131
- modalOwnsHistory.current = true;
132
- setModalResult(found);
162
+ // 经浏览器导航打开:前一条历史仍是本页,dialog 关闭可以安全 back()
163
+ void openAttempt(locator, true);
133
164
  return;
134
165
  }
135
- modalOwnsHistory.current = false;
136
- setModalResult(null);
166
+ setDialogLocator(null);
167
+ setDialogContent(null);
137
168
  const routed = tabFromHash(location.hash, pages);
138
169
  if (routed) setTab(routed);
139
170
  };
140
171
  window.addEventListener("hashchange", onHashChange);
141
172
  return () => window.removeEventListener("hashchange", onHashChange);
142
- }, [snapshots, pages]);
173
+ }, [pages, openAttempt]);
174
+
175
+ // 拦截 attempt 文档链接(报告页里的 locator 引用,attemptHref 缺省产出的
176
+ // `attempt/<encodeURIComponent(locator)>.html`):点击时改写成 hash 路由,交给上面的
177
+ // hashchange 统一打开——无 JavaScript 时这些链接原样导航到独立文档,同样完整可读。
178
+ // 修饰键 / 非左键点击放行,让「新标签页打开」这类浏览器原生行为不受影响。
179
+ useEffect(() => {
180
+ const onClick = (e: MouseEvent) => {
181
+ if (e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
182
+ const target = e.target;
183
+ if (!(target instanceof Element)) return;
184
+ const anchor = target.closest("a[href]");
185
+ if (!anchor) return;
186
+ const locator = attemptLocatorFromHref(anchor.getAttribute("href") ?? "");
187
+ if (!locator) return;
188
+ e.preventDefault();
189
+ location.hash = hashForAttempt(locator);
190
+ };
191
+ document.addEventListener("click", onClick);
192
+ return () => document.removeEventListener("click", onClick);
193
+ }, []);
143
194
 
144
195
  const selectTab = useCallback((value: Tab) => {
145
196
  setTab(value);
@@ -202,7 +253,7 @@ export function App({ data, reportPages }: { data: ViewData; reportPages: Record
202
253
  <TabsContent key={`page:${page.id}`} value={`page:${page.id}`} id={`tab-page-${page.id}`}>
203
254
  {/* 报告槽:server 侧逐页渲染好的静态 HTML(含 <Style> 产物),按当前页与界面语言
204
255
  摆放对应块;hero、品牌行、Scope 警告、批量修复 prompt 都是页内组件,壳不再渲染。
205
- attempt 深链是普通 <a href="#/attempt/…">,经 hashchange 打开证据室弹窗。 */}
256
+ attempt 深链是普通 <a href="attempt/…html">,经上面的点击拦截打开 dialog。 */}
206
257
  <ReportSlot html={reportPages[page.id]?.[locale] || reportPages[page.id]?.en || ""} />
207
258
  </TabsContent>
208
259
  ))}
@@ -212,7 +263,28 @@ export function App({ data, reportPages }: { data: ViewData; reportPages: Record
212
263
  <span className="site-footer-text">{footerText}</span>
213
264
  </footer>
214
265
  ) : null}
215
- {modalResult && <AttemptModal result={modalResult} onClose={closeModal} t={t} />}
266
+ {dialogLocator && dialogContent ? (
267
+ <Dialog open onOpenChange={(o) => { if (!o) closeDialog(); }}>
268
+ <DialogContent aria-describedby={undefined}>
269
+ {/* 屏幕阅读器用的可访问标题:视觉上隐藏,身份 / verdict 等实际内容已经在下面
270
+ fetch 回来的片段里可见,这里不重复渲染。 */}
271
+ <DialogTitle style={VISUALLY_HIDDEN}>{t("dialog.attemptTitle")}</DialogTitle>
272
+ <div className="flex min-w-0 shrink-0 items-center justify-end border-b border-line px-7 pb-3 pt-4">
273
+ <DialogClose
274
+ aria-label={t("action.close")}
275
+ className="grid h-7 w-7 shrink-0 place-items-center rounded-md border border-transparent text-sm text-muted transition-colors hover:border-line hover:bg-panel-2 hover:text-text"
276
+ >
277
+ x
278
+ </DialogClose>
279
+ </div>
280
+ {/* 与直接打开 attempt/<locator>.html 看到的是同一份 server-rendered 片段
281
+ (docs/engineering/unit-tests/reports/cases.md 第 207 行),不是客户端重新渲染。 */}
282
+ <div className="flex-1 overflow-y-auto px-7 pb-7 pt-2">
283
+ <ReportSlot html={dialogContent[locale] || dialogContent.en} />
284
+ </div>
285
+ </DialogContent>
286
+ </Dialog>
287
+ ) : null}
216
288
  </Tabs>
217
289
  );
218
290
  }
@@ -1,61 +1,11 @@
1
1
  // view 前端 i18n:内核(插值/归一)在 src/i18n/core.ts;这里只注入
2
2
  // localStorage + navigator 的 locale 来源与 en 默认值。字典与 CLI 侧分开维护。
3
- // 词条只覆盖宿主机器(导航标签、attempt 详情弹窗):页面内容(hero、警告、列表、瀑布)
4
- // 是报告组件,文案在 niceeval/report 的组件词典里(src/report/locale.ts)。
3
+ // 词条只覆盖宿主机器(导航标签、attempt dialog 的关闭按钮):页面内容(hero、警告、列表、瀑布、
4
+ // attempt 详情)是报告组件,文案在 niceeval/report 的组件词典里(src/report/locale.ts)。
5
5
 
6
6
  import { interpolate, normalizeLocale, type Locale, type Vars } from "../../i18n/core.ts";
7
7
 
8
- export type MessageKey =
9
- | "nav.label"
10
- | "hero.title"
11
- | "status.pass"
12
- | "status.fail"
13
- | "status.error"
14
- | "status.skipped"
15
- | "action.close"
16
- | "action.copyPrompt"
17
- | "action.copied"
18
- | "trace.loading"
19
- | "trace.loadFailed"
20
- | "trace.timing"
21
- | "trace.noSpans"
22
- | "trace.total"
23
- | "trace.spans"
24
- | "trace.clickDetails"
25
- | "trace.enableHint"
26
- | "trace.enableHintLink"
27
- | "trace.enableHintUrl"
28
- | "transcript.noEvents"
29
- | "transcript.user"
30
- | "transcript.assistant"
31
- | "transcript.thinking"
32
- | "transcript.inputRequested"
33
- | "transcript.awaitingInput"
34
- | "transcript.contextCompacted"
35
- | "transcript.skillLoaded"
36
- | "transcript.rawEvent"
37
- | "transcript.running"
38
- | "transcript.input"
39
- | "transcript.output"
40
- | "transcript.empty"
41
- | "code.otherAssertions"
42
- | "code.noReply"
43
- | "code.reply"
44
- | "code.hide"
45
- | "code.checks"
46
- | "code.conversation"
47
- | "code.noSource"
48
- | "code.sourceUnavailable"
49
- | "attempt.timing"
50
- | "attempt.teardown"
51
- | "attempt.diagnostics"
52
- | "assert.pass"
53
- | "assert.fail"
54
- | "assert.passedCollapsed"
55
- | "assert.unavailable"
56
- | "assert.optional"
57
- | "assert.soft"
58
- | "assert.evidence";
8
+ export type MessageKey = "nav.label" | "hero.title" | "action.close" | "dialog.attemptTitle";
59
9
 
60
10
  type Dictionary = Record<MessageKey, string>;
61
11
 
@@ -65,106 +15,16 @@ const dictionaries: Record<Locale, Dictionary> = {
65
15
  // 标题回退链终点的内置文案(shell.md:「Eval 运行结果 / Eval Results」);
66
16
  // 正常路径 server 侧已走完回退链,这里只兜旧数据 / 缺声明。
67
17
  "hero.title": "Eval Results",
68
- "status.pass": "pass",
69
- "status.fail": "fail",
70
- "status.error": "error",
71
- "status.skipped": "skipped",
72
18
  "action.close": "Close",
73
- "action.copyPrompt": "Copy fix prompt",
74
- "action.copied": "Copied",
75
- "trace.loading": "loading...",
76
- "trace.loadFailed": "load failed (static report has no server - use niceeval view):",
77
- "trace.timing": "timing trace",
78
- "trace.noSpans": "no spans",
79
- "trace.total": "total",
80
- "trace.spans": "spans",
81
- "trace.clickDetails": "click a row for details",
82
- "trace.enableHint": "No trace for this run. Wire up OTel to get a call waterfall — see the ",
83
- "trace.enableHintLink": "OTel guide",
84
- "trace.enableHintUrl": "https://niceeval.com/docs/guides/connect-otel",
85
- "transcript.noEvents": "no events",
86
- "transcript.user": "user",
87
- "transcript.assistant": "assistant",
88
- "transcript.thinking": "thinking",
89
- "transcript.inputRequested": "input requested",
90
- "transcript.awaitingInput": "(awaiting input)",
91
- "transcript.contextCompacted": "context compacted",
92
- "transcript.skillLoaded": "skill loaded",
93
- "transcript.rawEvent": "unrecognized event, shown as-is",
94
- "transcript.running": "running...",
95
- "transcript.input": "input",
96
- "transcript.output": "output",
97
- "transcript.empty": "(empty)",
98
- "code.otherAssertions": "other assertions",
99
- "code.noReply": "(no reply)",
100
- "code.reply": "reply",
101
- "code.hide": "hide",
102
- "code.checks": "checks",
103
- "code.conversation": "conversation",
104
- "code.noSource": "Source was not captured. This run may predate source-loc or the source may be unavailable. Re-run this eval to see the code view.",
105
- "code.sourceUnavailable": "Source was captured for this run, but its artifact files are missing from this deployment. Re-export with `niceeval view --out <dir>` (directory mode bundles artifacts), or open the results locally with `niceeval view`.",
106
- "attempt.timing": "timing",
107
- "attempt.teardown": "teardown (not counted in total)",
108
- "attempt.diagnostics": "diagnostics",
109
- "assert.pass": "pass",
110
- "assert.fail": "fail",
111
- "assert.passedCollapsed": "{{count}} passed",
112
- "assert.unavailable": "unavailable",
113
- "assert.optional": "optional",
114
- "assert.soft": "soft",
115
- "assert.evidence": "What was checked",
19
+ // 屏幕阅读器专用(Radix Dialog 的可访问标题),视觉上不出现——内容本身的身份 / verdict
20
+ // 已经在 dialog 里可见。
21
+ "dialog.attemptTitle": "Attempt details",
116
22
  },
117
23
  "zh-CN": {
118
24
  "nav.label": "报告",
119
25
  "hero.title": "Eval 运行结果",
120
- "status.pass": "通过",
121
- "status.fail": "失败",
122
- "status.error": "错误",
123
- "status.skipped": "跳过",
124
26
  "action.close": "关闭",
125
- "action.copyPrompt": "复制修复 Prompt",
126
- "action.copied": "已复制",
127
- "trace.loading": "加载中...",
128
- "trace.loadFailed": "加载失败(静态报告没有服务端 - 请用 niceeval view):",
129
- "trace.timing": "耗时追踪",
130
- "trace.noSpans": "没有 span",
131
- "trace.total": "总计",
132
- "trace.spans": "spans",
133
- "trace.clickDetails": "点击行查看详情",
134
- "trace.enableHint": "这次运行没有 trace。接入 OTel 才有调用瀑布图——看",
135
- "trace.enableHintLink": "OTel 接入指南",
136
- "trace.enableHintUrl": "https://niceeval.com/docs/zh/how-to/connect-otel",
137
- "transcript.noEvents": "没有事件",
138
- "transcript.user": "user",
139
- "transcript.assistant": "assistant",
140
- "transcript.thinking": "thinking",
141
- "transcript.inputRequested": "请求输入",
142
- "transcript.awaitingInput": "(等待输入)",
143
- "transcript.contextCompacted": "上下文已压缩",
144
- "transcript.skillLoaded": "已加载 Skill",
145
- "transcript.rawEvent": "未识别事件,原样展示",
146
- "transcript.running": "运行中...",
147
- "transcript.input": "输入",
148
- "transcript.output": "输出",
149
- "transcript.empty": "(空)",
150
- "code.otherAssertions": "其它断言",
151
- "code.noReply": "(无回复)",
152
- "code.reply": "回复",
153
- "code.hide": "收起",
154
- "code.checks": "检查",
155
- "code.conversation": "会话",
156
- "code.noSource": "源码未捕获。此 run 可能早于 source-loc,或源码不可读。重跑此 eval 即可看到代码视图。",
157
- "code.sourceUnavailable": "此 run 捕获过源码,但当前部署里缺少它的 artifact 文件。用 `niceeval view --out <目录>` 重新导出(目录模式会带上 artifact),或在本地 `niceeval view` 查看。",
158
- "attempt.timing": "耗时",
159
- "attempt.teardown": "收尾(不计入总耗时)",
160
- "attempt.diagnostics": "诊断",
161
- "assert.pass": "通过",
162
- "assert.fail": "失败",
163
- "assert.passedCollapsed": "{{count}} 条通过",
164
- "assert.unavailable": "评不了",
165
- "assert.optional": "可缺席",
166
- "assert.soft": "soft",
167
- "assert.evidence": "实际被检查的内容",
27
+ "dialog.attemptTitle": "Attempt 详情",
168
28
  },
169
29
  };
170
30
 
@@ -0,0 +1,65 @@
1
+ // cases: docs/engineering/unit-tests/reports/cases.md
2
+ // 第 207 行:增强脚本拦截 locator 链接后,dialog 内容与直接打开该文档的内容是同一份
3
+ // server-rendered 字节/DOM 片段。
4
+ import { describe, expect, it } from "vitest";
5
+ import { attemptHrefFor, attemptLocatorFromHref, hashForAttempt, locatorFromHash, parseAttemptDocument } from "./attempt-dialog.ts";
6
+
7
+ describe("attemptHrefFor / attemptLocatorFromHref", () => {
8
+ it("互为逆运算(编码/解码往返)", () => {
9
+ const locator = "@1kijjdij";
10
+ const href = attemptHrefFor(locator);
11
+ expect(href).toBe("attempt/%401kijjdij.html");
12
+ expect(attemptLocatorFromHref(href)).toBe(locator);
13
+ });
14
+
15
+ it("不是 attempt 文档链接或形状不对时返回 undefined", () => {
16
+ expect(attemptLocatorFromHref("index.html")).toBeUndefined();
17
+ expect(attemptLocatorFromHref("artifact/e/run/a0/events.json")).toBeUndefined();
18
+ expect(attemptLocatorFromHref("attempt/not-a-locator.html")).toBeUndefined();
19
+ });
20
+ });
21
+
22
+ describe("hashForAttempt / locatorFromHash", () => {
23
+ it("互为逆运算", () => {
24
+ expect(locatorFromHash(hashForAttempt("@abc123"))).toBe("@abc123");
25
+ });
26
+
27
+ it("非 attempt 路由或形状不对返回 undefined", () => {
28
+ expect(locatorFromHash("#/page/report")).toBeUndefined();
29
+ expect(locatorFromHash("#/attempt/not-valid")).toBeUndefined();
30
+ expect(locatorFromHash("")).toBeUndefined();
31
+ });
32
+ });
33
+
34
+ describe("parseAttemptDocument", () => {
35
+ function fakeDocument(en: string, zh: string): string {
36
+ return [
37
+ "<!doctype html>",
38
+ "<html><head></head><body>",
39
+ `<div data-nre-locale="en">${en}</div>`,
40
+ `<div data-nre-locale="zh-CN" hidden>${zh}</div>`,
41
+ "<script>/* locale swap */</script>",
42
+ "</body></html>",
43
+ ].join("\n");
44
+ }
45
+
46
+ it("取出两种语言的内容片段,不含包裹的 div 标签本身", () => {
47
+ const html = fakeDocument("<p>hello</p>", "<p>你好</p>");
48
+ expect(parseAttemptDocument(html)).toEqual({ en: "<p>hello</p>", "zh-CN": "<p>你好</p>" });
49
+ });
50
+
51
+ it("内容里嵌套的 <div> 不会打断切分(现实里的 AttemptDetail 输出全是嵌套 div)", () => {
52
+ const html = fakeDocument(
53
+ '<div class="nre nre-col"><div class="nre-attempt-summary">stuff</div></div>',
54
+ '<div class="nre nre-col"><div class="nre-attempt-summary">东西</div></div>',
55
+ );
56
+ expect(parseAttemptDocument(html)).toEqual({
57
+ en: '<div class="nre nre-col"><div class="nre-attempt-summary">stuff</div></div>',
58
+ "zh-CN": '<div class="nre nre-col"><div class="nre-attempt-summary">东西</div></div>',
59
+ });
60
+ });
61
+
62
+ it("不是这份渲染器产出的文档(缺少标记)返回 null,不开空 dialog", () => {
63
+ expect(parseAttemptDocument("<html><body>not an attempt document</body></html>")).toBeNull();
64
+ });
65
+ });
@@ -0,0 +1,69 @@
1
+ // 独立 attempt 文档(attempt/<locator>.html,site.ts 的 renderAttemptDocument)的客户端消费面:
2
+ // 拦截同源链接、按 locator 拼 href、把 fetch 回来的文档两种语言内容抠出来塞进 dialog——
3
+ // 内容片段与直接打开该文档看到的完全一致,不维护第二份客户端渲染(view.md「静态导出」)。
4
+ // 纯字符串切分,不用 DOMParser:结构由同一个 site.ts 的固定模板产出,不需要通用 HTML 解析。
5
+
6
+ const EN_MARK = '<div data-nre-locale="en">';
7
+ const ZH_MARK = '<div data-nre-locale="zh-CN"';
8
+ const SCRIPT_MARK = "<script>";
9
+
10
+ export interface AttemptDocumentContent {
11
+ en: string;
12
+ "zh-CN": string;
13
+ }
14
+
15
+ /** attempt/<locator>.html 链接的形状(相对于当前文档的相对 href,与 encodeURIComponent(locator) 对应)。 */
16
+ const ATTEMPT_HREF_PATTERN = /^attempt\/(.+)\.html$/;
17
+
18
+ /** locator 串的最小形状:`@` + 至少一个 base36 字符——与 site.ts DEFAULT_ATTEMPT_HREF 编码前的原始值同形。 */
19
+ const LOCATOR_SHAPE = /^@[0-9a-z]+$/;
20
+
21
+ /** 相对 href → 解码后的 locator;不是 attempt 文档链接或 locator 形状不对都返回 undefined。 */
22
+ export function attemptLocatorFromHref(href: string): string | undefined {
23
+ const match = ATTEMPT_HREF_PATTERN.exec(href);
24
+ if (!match) return undefined;
25
+ const locator = decodeURIComponent(match[1]!);
26
+ return LOCATOR_SHAPE.test(locator) ? locator : undefined;
27
+ }
28
+
29
+ /** locator → 该文档的根相对 href(与 report/web.ts DEFAULT_ATTEMPT_HREF 同一编码规则)。 */
30
+ export function attemptHrefFor(locator: string): string {
31
+ return `attempt/${encodeURIComponent(locator)}.html`;
32
+ }
33
+
34
+ const ATTEMPT_HASH_PREFIX = "#/attempt/";
35
+
36
+ /** hash → locator;不是这条路由或形状不对(旧格式深链、手打错的 hash)都返回 undefined。 */
37
+ export function locatorFromHash(hash: string): string | undefined {
38
+ if (!hash.startsWith(ATTEMPT_HASH_PREFIX)) return undefined;
39
+ const locator = hash.slice(ATTEMPT_HASH_PREFIX.length);
40
+ return LOCATOR_SHAPE.test(locator) ? locator : undefined;
41
+ }
42
+
43
+ export function hashForAttempt(locator: string): string {
44
+ return `${ATTEMPT_HASH_PREFIX}${locator}`;
45
+ }
46
+
47
+ /**
48
+ * 从 attempt 文档的响应文本里取出两种语言的内容片段。两个 locale 块紧邻、且两者之间/之后
49
+ * 不会出现这里搜索的标记字符串(site.ts 的固定模板保证),纯字符串切分足够,不需要 DOMParser。
50
+ * 形状不对(不是这份渲染器产出的文档)返回 null,调用方不开空 dialog。
51
+ */
52
+ export function parseAttemptDocument(html: string): AttemptDocumentContent | null {
53
+ const enStart = html.indexOf(EN_MARK);
54
+ if (enStart === -1) return null;
55
+ const afterEnOpen = html.slice(enStart + EN_MARK.length);
56
+ const zhMarkIdx = afterEnOpen.indexOf(ZH_MARK);
57
+ if (zhMarkIdx === -1) return null;
58
+ const en = afterEnOpen.slice(0, zhMarkIdx).replace(/<\/div>\s*$/, "");
59
+
60
+ const afterZhMark = afterEnOpen.slice(zhMarkIdx);
61
+ const zhTagEnd = afterZhMark.indexOf(">");
62
+ if (zhTagEnd === -1) return null;
63
+ const afterZhOpen = afterZhMark.slice(zhTagEnd + 1);
64
+ const scriptIdx = afterZhOpen.indexOf(SCRIPT_MARK);
65
+ if (scriptIdx === -1) return null;
66
+ const zh = afterZhOpen.slice(0, scriptIdx).replace(/<\/div>\s*$/, "");
67
+
68
+ return { en, "zh-CN": zh };
69
+ }
@@ -6,7 +6,6 @@ import "../styles.css";
6
6
  // 没有烘焙数据(比如直接打开裸产物)时的空页面兜底。
7
7
  const emptyViewData: ViewData = {
8
8
  composedRuns: 0,
9
- snapshots: [],
10
9
  };
11
10
 
12
11
  const initialData: ViewData = window.__NICEEVAL_VIEW_DATA__ ?? emptyViewData;
@@ -1,96 +1,13 @@
1
- import type {
2
- AssertionResult,
3
- JsonValue,
4
- LocalizedText,
5
- SourceArtifact,
6
- SourceLoc,
7
- StreamEvent,
8
- TraceSpan,
9
- Usage,
10
- } from "../../types.ts";
11
- import type { ViewData, ViewEvalResult } from "../shared/types.ts";
12
-
13
- export type { LocalizedText };
14
1
  // Locale 只在 i18n 内核声明一次;页面数据形状与 server 共用 shared/types.ts 的声明。
15
2
  export type { Locale } from "../../i18n/core.ts";
16
- export type {
17
- AttemptLocator,
18
- ReportSlotHtml,
19
- SkippedRunNotice,
20
- ViewData,
21
- ViewReportMeta,
22
- ViewReportPageMeta,
23
- ViewSnapshot,
24
- } from "../shared/types.ts";
3
+ export type { LocalizedText } from "../../types.ts";
4
+ export type { ReportSlotHtml, ViewData, ViewReportPageMeta } from "../shared/types.ts";
25
5
 
26
6
  /** 导航 tab:只有报告定义声明的页(`page:<id>`,路由 `#/page/<id>`),按声明序;宿主不追加任何项。 */
27
7
  export type Tab = `page:${string}`;
28
8
 
29
- /** 前端拿到的单条 attempt 结果就是瘦身后的 EvalResult(locator / artifactBase 由 loader 注入)。 */
30
- export type ViewResult = ViewEvalResult;
31
-
32
- export type Assertion = AssertionResult;
33
-
34
- export type Verdict = "passed" | "failed" | "errored" | "skipped" | string;
35
-
36
- export interface SourceTurn {
37
- loc?: SourceLoc;
38
- sent: string;
39
- replies: Reply[];
40
- }
41
-
42
- export type Reply =
43
- | { kind: "text"; text: string }
44
- | { kind: "user"; text: string }
45
- | { kind: "thinking"; text: string }
46
- | { kind: "error"; text: string }
47
- | { kind: "skill"; skill: string }
48
- | { kind: "raw"; raw: ObjectRecord }
49
- | { kind: "tool"; ev: ActionCalledEvent; result?: ActionResultEvent }
50
- | { kind: "input"; ev: InputRequestedEvent };
51
-
52
- export type ActionCalledEvent = Extract<StreamEvent, { type: "action.called" }>;
53
- export type InputRequestedEvent = Extract<StreamEvent, { type: "input.requested" }>;
54
- export type ActionResultEvent = Extract<StreamEvent, { type: "action.result" }>;
55
- export type SubagentCalledEvent = Extract<StreamEvent, { type: "subagent.called" }>;
56
- export type SubagentCompletedEvent = Extract<StreamEvent, { type: "subagent.completed" }>;
57
- export type ToolResultEvent = ActionResultEvent | SubagentCompletedEvent;
58
- export type ViewJson = JsonValue;
59
- export type ViewUsage = Usage;
60
-
61
- /** 标准事件流里前端认识的词汇。 */
62
- export type KnownTranscriptEvent = StreamEvent;
63
-
64
- /**
65
- * 未识别或形状不合的事件条目,由 asEvents 包装:不静默丢弃,渲染面原样展示
66
- * (带原始 type 标签),让词汇演进在界面上可被发现、后续补一等呈现。
67
- * `view.raw` 是 view 内部标记,不属于标准事件流词汇。
68
- */
69
- export interface RawTranscriptEvent {
70
- type: "view.raw";
71
- raw: ObjectRecord;
72
- }
73
-
74
- export type TranscriptEvent = KnownTranscriptEvent | RawTranscriptEvent;
75
-
76
- export interface Indexed<T> {
77
- byKey: Map<string, T[]>;
78
- noloc: T[];
79
- }
80
-
81
- export interface IndexedTurns {
82
- byKey: Map<string, SourceTurn>;
83
- noloc: SourceTurn[];
84
- }
85
-
86
- export interface CodeSource extends SourceArtifact {}
87
-
88
- export type Span = TraceSpan;
89
-
90
- export type ObjectRecord = Record<string, JsonValue | undefined>;
91
-
92
9
  declare global {
93
10
  interface Window {
94
- __NICEEVAL_VIEW_DATA__?: ViewData;
11
+ __NICEEVAL_VIEW_DATA__?: import("../shared/types.ts").ViewData;
95
12
  }
96
13
  }