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
@@ -5,26 +5,13 @@
5
5
  // `extends` 在另一份报告上叠外壳——页归 base、外壳逐字段覆盖,合并在调用时折叠完成,
6
6
  // 宿主装载看到的永远是已折叠的普通产物。
7
7
  //
8
- // renderReportToText text 宿主(show)的装载入口;web 宿主(view)的
9
- // renderReportToStaticHtml ./web.ts(那一侧才 import react-dom)。管线以页为单位执行:
10
- // 装载(规范化 + 静态校验)→ resolve → validate → render。
11
-
12
- import type { Results, Scope } from "../results/types.ts";
13
- import {
14
- createTextContext,
15
- renderNodeToText,
16
- resolveReportTree,
17
- validateReportTree,
18
- ResolveMemo,
19
- type ReportNode,
20
- type TextRenderOptions,
21
- } from "./tree.ts";
22
- import {
23
- localizedTextEquals,
24
- resolveLocalizedText,
25
- type LocalizedText,
26
- type ReportLocale,
27
- } from "./locale.ts";
8
+ // text/web 两个宿主的渲染入口(renderReportToText / renderReportToStaticHtml) ../runtime/
9
+ // (text.ts / web.ts);这里只有 ReportDefinition 的类型体系、装载规范化与元数据折叠
10
+ // (buildReportMeta / resolveReportTitle),不做任何渲染。
11
+
12
+ import type { Scope } from "../../results/types.ts";
13
+ import type { ReportNode } from "./tree.ts";
14
+ import { localizedTextEquals, type LocalizedText } from "../model/locale.ts";
28
15
 
29
16
  // ───────────────────────── 公开形状 ─────────────────────────
30
17
 
@@ -73,7 +60,7 @@ export interface ReportShell {
73
60
 
74
61
  export type NonEmptyArray<T> = readonly [T, ...T[]];
75
62
 
76
- export interface ReportPage {
63
+ export interface ReportPageBase {
77
64
  /** 页面身份:`--page <id>` 的取值、web 路由 `#/page/<id>` 与导航锚。小写字母、数字与连字符。 */
78
65
  id: string;
79
66
  /** 导航中的页名。 */
@@ -82,6 +69,18 @@ export interface ReportPage {
82
69
  content: ReportNode;
83
70
  }
84
71
 
72
+ /**
73
+ * 页按输入分两种形态,仍是同一个类型族,走同一条 resolve → validate → render 管线
74
+ * (docs/feature/reports/architecture.md「Attempt 详情是一张参数化 page」):
75
+ * - `input` 省略或为 `"scope"`:消费宿主选择的 Scope;省略时规范化为 `navigation: true`。
76
+ * - `input: "attempt"`:以 locator 为参数,消费一份 AttemptEvidence;没有 locator 时不能打开,
77
+ * 必须显式 `navigation: false`,不进导航。
78
+ * 一份报告至多声明一张 attempt-input page。
79
+ */
80
+ export type ReportPage =
81
+ | (ReportPageBase & { input?: "scope"; navigation?: boolean })
82
+ | (ReportPageBase & { input: "attempt"; navigation: false });
83
+
85
84
  /** content / pages / extends 三选一由类型表达,不把非法状态留到运行期。 */
86
85
  export type ReportDef = ReportShell &
87
86
  (
@@ -92,7 +91,7 @@ export type ReportDef = ReportShell &
92
91
  extends?: never;
93
92
  }
94
93
  | {
95
- /** 非空页列表;导航按数组顺序显示。 */
94
+ /** 非空页列表;`navigation !== false` 的项按数组顺序显示。 */
96
95
  pages: NonEmptyArray<ReportPage>;
97
96
  content?: never;
98
97
  extends?: never;
@@ -128,17 +127,27 @@ export interface ReportDefinition {
128
127
  readonly pages: NonEmptyArray<ReportPage>;
129
128
  }
130
129
 
131
- /** 规范化后的报告声明,经组合组件 ctx.report 只读可见(scripts / styles 是注入资产,不进)。 */
130
+ /** 规范化后页列表在 ctx.report 上的元数据形态(id / 导航页名 / 输入声明 / 导航资格)。 */
131
+ export interface ReportMetaPage {
132
+ id: string;
133
+ title: LocalizedText;
134
+ input: "scope" | "attempt";
135
+ navigation: boolean;
136
+ }
137
+
138
+ /**
139
+ * 规范化后的报告声明,经组合组件 ctx.report 只读可见(scripts / styles 是注入资产,不进)。
140
+ * 不携带"当前是哪一页"——那由 ctx.page(PageContext)表达,两者不是同一份状态
141
+ * (docs/feature/reports/library/shell.md「行为约束」)。
142
+ */
132
143
  export interface ReportMeta {
133
144
  /** 走完回退链(声明 title → 唯一快照 name → 内置文案「Eval 运行结果 / Eval Results」)后的标题。 */
134
145
  title: LocalizedText;
135
146
  /** 页头外链;声明省略时为空数组。 */
136
147
  links: readonly ReportLink[];
137
148
  footer?: LocalizedText;
138
- /** 规范化后的页列表(id 与导航页名),恒非空。 */
139
- pages: NonEmptyArray<{ id: string; title: LocalizedText }>;
140
- /** 当前渲染中的页 id。 */
141
- pageId: string;
149
+ /** 规范化后的页列表,恒非空。 */
150
+ pages: NonEmptyArray<ReportMetaPage>;
142
151
  }
143
152
 
144
153
  /** 单页缩写展开出的唯一页 id 与内置页名。 */
@@ -332,6 +341,53 @@ function assertHeadTags(tags: unknown): HeadTag[] {
332
341
  return tags as HeadTag[];
333
342
  }
334
343
 
344
+ /**
345
+ * page 的 input / navigation 规范化(shell.md「page 显式声明输入」):省略或 "scope" 时补
346
+ * `input: "scope"`,`navigation` 缺省为 true;"attempt" 必须显式 `navigation: false`——
347
+ * 没有 locator 时不可打开,不能悄悄挤进导航,省略或传 true 都在装载期报错。
348
+ */
349
+ function normalizePageInputAndNavigation(page: Record<string, unknown>): ReportPage {
350
+ const input = page.input;
351
+ if (input === undefined || input === "scope") {
352
+ return {
353
+ id: page.id as string,
354
+ title: page.title as LocalizedText,
355
+ content: page.content as ReportNode,
356
+ input: "scope",
357
+ navigation: page.navigation !== false,
358
+ };
359
+ }
360
+ if (input === "attempt") {
361
+ if (page.navigation !== false) {
362
+ throw new Error(
363
+ `Report page "${page.id}" declares input: "attempt" but not navigation: false — an attempt-input page has no content without a locator, so it must not appear in navigation. Add navigation: false.`,
364
+ );
365
+ }
366
+ return {
367
+ id: page.id as string,
368
+ title: page.title as LocalizedText,
369
+ content: page.content as ReportNode,
370
+ input: "attempt",
371
+ navigation: false,
372
+ };
373
+ }
374
+ throw new Error(
375
+ `Report page "${page.id}" input ${JSON.stringify(input)} is not valid — input is omitted, "scope", or "attempt".`,
376
+ );
377
+ }
378
+
379
+ /** 一份报告至多一张 attempt-input page,避免 show @<locator> 与 locator 链接出现多个目标。 */
380
+ function assertAtMostOneAttemptPage(pages: readonly ReportPage[]): void {
381
+ const attemptPages = pages.filter((p) => p.input === "attempt");
382
+ if (attemptPages.length > 1) {
383
+ throw new Error(
384
+ `A report can declare at most one input: "attempt" page — got ${attemptPages.length} (${attemptPages
385
+ .map((p) => `"${p.id}"`)
386
+ .join(", ")}). Keep one and remove the others, or fold their content into a single attempt-input page.`,
387
+ );
388
+ }
389
+ }
390
+
335
391
  export function defineReport(content: ReportNode): ReportDefinition;
336
392
  export function defineReport(def: ReportDef): ReportDefinition;
337
393
  export function defineReport(input: ReportNode | ReportDef): ReportDefinition {
@@ -379,7 +435,9 @@ export function defineReport(input: ReportNode | ReportDef): ReportDefinition {
379
435
  pages = base.pages;
380
436
  } else if (hasContent) {
381
437
  assertNotDefinition(def.content, 'defineReport "content"');
382
- pages = [{ id: DEFAULT_PAGE_ID, title: DEFAULT_PAGE_TITLE, content: def.content as ReportNode }];
438
+ pages = [
439
+ { id: DEFAULT_PAGE_ID, title: DEFAULT_PAGE_TITLE, input: "scope", navigation: true, content: def.content as ReportNode },
440
+ ];
383
441
  } else {
384
442
  const raw = def.pages as unknown;
385
443
  if (!Array.isArray(raw) || raw.length === 0) {
@@ -388,6 +446,7 @@ export function defineReport(input: ReportNode | ReportDef): ReportDefinition {
388
446
  );
389
447
  }
390
448
  const seen = new Set<string>();
449
+ const normalized: ReportPage[] = [];
391
450
  for (const page of raw as Array<Record<string, unknown>>) {
392
451
  if (typeof page?.id !== "string" || !PAGE_ID_PATTERN.test(page.id)) {
393
452
  throw new Error(
@@ -402,9 +461,11 @@ export function defineReport(input: ReportNode | ReportDef): ReportDefinition {
402
461
  seen.add(page.id);
403
462
  assertLocalizedText(page.title, `Report page "${page.id}" title`);
404
463
  assertNotDefinition(page.content, `Report page "${page.id}" content`);
464
+ normalized.push(normalizePageInputAndNavigation(page));
405
465
  }
406
- pages = raw as ReportPage[];
466
+ pages = normalized;
407
467
  }
468
+ assertAtMostOneAttemptPage(pages);
408
469
 
409
470
  if (def.title !== undefined) assertLocalizedText(def.title, "defineReport title");
410
471
  if (def.footer !== undefined) assertLocalizedText(def.footer, "defineReport footer");
@@ -482,145 +543,17 @@ export function resolveReportTitle(definition: ReportDefinition, scope: Scope):
482
543
  return names.every((name) => localizedTextEquals(name, first)) ? first : FALLBACK_REPORT_TITLE;
483
544
  }
484
545
 
485
- /** 规范化声明 → 组合组件可见的 ReportMeta(scripts / styles 是注入资产,不进)。 */
486
- export function buildReportMeta(definition: ReportDefinition, scope: Scope, pageId: string): ReportMeta {
546
+ /** 规范化声明 → 组合组件可见的 ReportMeta(scripts / styles 是注入资产,不进;不携带当前页)。 */
547
+ export function buildReportMeta(definition: ReportDefinition, scope: Scope): ReportMeta {
487
548
  return {
488
549
  title: resolveReportTitle(definition, scope),
489
550
  links: definition.links,
490
551
  ...(definition.footer !== undefined ? { footer: definition.footer } : {}),
491
- pages: definition.pages.map((page) => ({ id: page.id, title: page.title })) as unknown as NonEmptyArray<{
492
- id: string;
493
- title: LocalizedText;
494
- }>,
495
- pageId,
496
- };
497
- }
498
-
499
- // ───────────────────────── 页选择与 text 宿主入口 ─────────────────────────
500
-
501
- /** `--page` 未命中:宿主据此按用法错误退出并列出可用页 id。 */
502
- export class ReportPageNotFoundError extends Error {
503
- readonly pageId: string;
504
- readonly available: string[];
505
- constructor(pageId: string, available: string[]) {
506
- super(`page "${pageId}" not found. Available pages: ${available.join(", ")}`);
507
- this.pageId = pageId;
508
- this.available = available;
509
- }
510
- }
511
-
512
- export function pickReportPage(definition: ReportDefinition, pageId?: string): ReportPage {
513
- if (pageId === undefined) return definition.pages[0];
514
- const page = definition.pages.find((p) => p.id === pageId);
515
- if (!page) {
516
- throw new ReportPageNotFoundError(
517
- pageId,
518
- definition.pages.map((p) => p.id),
519
- );
520
- }
521
- return page;
522
- }
523
-
524
- /** 宿主注入的渲染上下文:官方口径挑好的 Scope 与结果根完整读取面。 */
525
- export interface ReportHostContext {
526
- scope: Scope;
527
- /** 组合组件 ctx.results 的来源;历史视图从这里自行挑 Snapshot[]。 */
528
- results: Results;
529
- }
530
-
531
- export interface RenderReportTextOptions extends TextRenderOptions {
532
- /** 渲染哪一页;缺省第一页。未命中抛 ReportPageNotFoundError。 */
533
- pageId?: string;
534
- }
535
-
536
- /**
537
- * text 宿主的装载语义:选页 → resolve(组合展开 + spec 取数,唯一的 await 边界)→ 树校验 →
538
- * 遍历渲染 text 面。不需要 react-dom。宿主不在报告树外另设警告通道——挑选警告的呈现件是
539
- * `ScopeWarnings` 组件,内建报告每页都放它,自定义报告放不放是作者义务
540
- * (docs/feature/reports/architecture.md「Scope 是计算入口」)。
541
- */
542
- export async function renderReportToText(
543
- definition: ReportDefinition,
544
- ctx: ReportHostContext,
545
- options?: RenderReportTextOptions,
546
- ): Promise<string> {
547
- const page = pickReportPage(definition, options?.pageId);
548
- const meta = buildReportMeta(definition, ctx.scope, page.id);
549
- const resolved = await resolveReportTree(page.content, {
550
- scope: ctx.scope,
551
- results: ctx.results,
552
- report: meta,
553
- memo: new ResolveMemo(),
554
- });
555
- validateReportTree(resolved);
556
- return renderNodeToText(resolved, createTextContext(options));
557
- }
558
-
559
- /** 页索引标题行(show 多页索引 / view 导航共用的解析结果):按 locale 解析的标题字符串。 */
560
- export function reportTitleText(definition: ReportDefinition, scope: Scope, locale: ReportLocale): string {
561
- return resolveLocalizedText(resolveReportTitle(definition, scope), locale);
562
- }
563
-
564
- // ───────────────────────── 逐页(树)渲染入口:宿主联系面 ─────────────────────────
565
-
566
- /** 宿主索引命令的完整上下文(docs/feature/reports/show/reports.md「索引命令携带完整上下文」)。 */
567
- export interface HostCommandContext {
568
- patterns: string[];
569
- results?: string;
570
- experiment?: string;
571
- report?: string;
572
- page?: string;
573
- }
574
-
575
- function quoteArg(value: string): string {
576
- return /^[A-Za-z0-9._/@-]+$/.test(value) ? value : `'${value.replaceAll("'", `'"'"'`)}'`;
577
- }
578
-
579
- /** 按上下文拼组索引的可复制命令:`niceeval show <patterns> --exp <id> [--results/--report/--page]`。 */
580
- function experimentCommandFor(ctx: HostCommandContext): (experimentIdPrefix: string) => string {
581
- return (prefix) => {
582
- const parts = ["niceeval show", ...ctx.patterns.map(quoteArg), `--exp ${quoteArg(prefix)}`];
583
- if (ctx.results !== undefined) parts.push(`--results ${quoteArg(ctx.results)}`);
584
- if (ctx.report !== undefined) parts.push(`--report ${quoteArg(ctx.report)}`);
585
- if (ctx.page !== undefined) parts.push(`--page ${quoteArg(ctx.page)}`);
586
- return parts.join(" ");
552
+ pages: definition.pages.map((page) => ({
553
+ id: page.id,
554
+ title: page.title,
555
+ input: page.input ?? "scope",
556
+ navigation: page.navigation ?? true,
557
+ })) as unknown as NonEmptyArray<ReportMetaPage>,
587
558
  };
588
559
  }
589
-
590
- /** 逐页渲染的宿主上下文:官方口径的 Scope、结果根读取面与规范化声明(ctx.report)。 */
591
- export interface ReportTreeHostContext {
592
- scope: Scope;
593
- results: Results;
594
- report: ReportMeta;
595
- }
596
-
597
- export interface RenderTreeTextOptions extends TextRenderOptions {
598
- /** 组索引命令的完整上下文;给了就按它拼命令,experimentCommand 显式注入时以后者为准。 */
599
- commandContext?: HostCommandContext;
600
- }
601
-
602
- /**
603
- * 渲染一页报告树的 text 面(宿主逐页调用;页选择归宿主):
604
- * resolve(组合展开 + spec 取数)→ validate → render。宿主不在报告树外另设警告通道,
605
- * 挑选警告由页内的 `ScopeWarnings` 组件呈现(内建报告每页都放它)。
606
- */
607
- export async function renderReportTreeToText(
608
- tree: ReportNode,
609
- ctx: ReportTreeHostContext,
610
- options?: RenderTreeTextOptions,
611
- ): Promise<string> {
612
- const resolved = await resolveReportTree(tree, {
613
- scope: ctx.scope,
614
- results: ctx.results,
615
- report: ctx.report,
616
- memo: new ResolveMemo(),
617
- });
618
- validateReportTree(resolved);
619
- const textCtx = createTextContext({
620
- ...options,
621
- ...(options?.experimentCommand === undefined && options?.commandContext !== undefined
622
- ? { experimentCommand: experimentCommandFor(options.commandContext) }
623
- : {}),
624
- });
625
- return renderNodeToText(resolved, textCtx);
626
- }
@@ -5,7 +5,7 @@
5
5
 
6
6
  import { describe, expect, it } from "vitest";
7
7
 
8
- import type { Scope } from "../results/index.ts";
8
+ import type { Scope } from "../../results/index.ts";
9
9
  import { buildReportMeta, defineReport } from "./report.ts";
10
10
 
11
11
  const emptyScope = { snapshots: [] } as unknown as Scope;
@@ -97,6 +97,6 @@ describe("defineReport head 通道(装载校验)", () => {
97
97
  ];
98
98
  const definition = defineReport({ content: null, head });
99
99
  expect(definition.head).toEqual(head);
100
- expect(buildReportMeta(definition, emptyScope, "report")).not.toHaveProperty("head");
100
+ expect(buildReportMeta(definition, emptyScope)).not.toHaveProperty("head");
101
101
  });
102
102
  });
@@ -6,10 +6,10 @@
6
6
  // 列宽按显示宽度算(CJK 记 2 列);null 渲染 —,不补 0;超宽先折最宽的左对齐列,
7
7
  // 压到下限仍放不下就从右侧丢列并如实报数(「截断报剩余」是既有契约,不在这里破例)。
8
8
 
9
- import type { TableProps, TableRow } from "../primitives.tsx";
10
- import type { TextContext } from "../tree.ts";
11
- import { countText, localeText, resolveLocalizedText } from "../locale.ts";
12
- import { charDisplayWidth, renderAlignedRows, stringWidth, wrapDisplay, type ColumnAlign } from "./layout.ts";
9
+ import type { TableProps, TableRow } from "./primitives.tsx";
10
+ import type { TextContext } from "./tree.ts";
11
+ import { countText, localeText, resolveLocalizedText } from "../model/locale.ts";
12
+ import { charDisplayWidth, renderAlignedRows, stringWidth, wrapDisplay, type ColumnAlign } from "../model/text-layout.ts";
13
13
 
14
14
  const MISSING_MARK = "—";
15
15
  /** 列间距,与 renderAlignedRows 的 join(" ") 一致。 */
@@ -10,12 +10,37 @@
10
10
  // 只在 resolve 阶段被懒加载,永远不进渲染路径。
11
11
 
12
12
  import type { ReactNode } from "react";
13
- import type { AttemptLocator } from "../results/locator.ts";
14
- import type { Results, Scope } from "../results/types.ts";
15
- import { DEFAULT_REPORT_LOCALE, type ReportLocale } from "./locale.ts";
16
- import type { ReportInput } from "./types.ts";
13
+ import type { AttemptLocator } from "../../results/locator.ts";
14
+ import type { AttemptEvidence } from "../../results/attempt-evidence.ts";
15
+ import type { Results, Scope } from "../../results/types.ts";
16
+ import { DEFAULT_REPORT_LOCALE, type ReportLocale } from "../model/locale.ts";
17
+ import type { ReportInput } from "../model/types.ts";
17
18
  import type { ReportMeta } from "./report.ts";
18
19
 
20
+ // ───────────────────────── 当前页判别(PageContext) ─────────────────────────
21
+
22
+ /** scope-input page 的当前页上下文:消费宿主选择的 Scope,没有 locator/evidence。 */
23
+ export interface ScopePageContext {
24
+ id: string;
25
+ input: "scope";
26
+ }
27
+
28
+ /** attempt-input page 的当前页上下文:按 locator 消费一份 AttemptEvidence。 */
29
+ export interface AttemptPageContext {
30
+ id: string;
31
+ input: "attempt";
32
+ locator: AttemptLocator;
33
+ evidence: AttemptEvidence;
34
+ }
35
+
36
+ /**
37
+ * 当前渲染中的页判别联合(docs/feature/reports/library/shell.md「行为约束」、
38
+ * library/attempt-detail.md「page 输入与 spec / data 形态」)。经 ComposeContext.page 与
39
+ * ResolveContext.page 双双可见:组合组件靠它读当前页 id 与输入分支;attempt 叶子组件靠它
40
+ * 取省略 input 时的缺省 evidence。
41
+ */
42
+ export type PageContext = ScopePageContext | AttemptPageContext;
43
+
19
44
  // ───────────────────────── 节点形状 ─────────────────────────
20
45
 
21
46
  /** 标准 jsx-runtime 元素形状;text 宿主只认 type / props,不管 $$typeof。 */
@@ -67,8 +92,12 @@ export interface TextContext {
67
92
  locale: ReportLocale;
68
93
  /** 容器组件渲染 children 用,宽度显式传递。 */
69
94
  render(node: ReportNode, width?: number): string;
70
- /** 下钻命令,通证据室:`niceeval show @<locator>`。 */
71
- attemptCommand(locator: AttemptLocator): string;
95
+ /**
96
+ * 下钻命令,通 attempt-input page:`niceeval show @<locator>`。当前报告没有声明
97
+ * attempt-input page 时不存在——宿主不生成假命令,locator 只是文本(architecture.md
98
+ * 「Attempt 详情是一张参数化 page」)。
99
+ */
100
+ attemptCommand?(locator: AttemptLocator): string;
72
101
  /**
73
102
  * 组索引一类「按实验收窄」命令的生成;宿主注入以携带完整上下文(--results / --report /
74
103
  * --page 与位置参数),默认 `niceeval show --exp <id>`。非契约字段,官方组件内部用。
@@ -77,8 +106,12 @@ export interface TextContext {
77
106
  }
78
107
 
79
108
  export interface WebContext {
80
- /** 证据室深链,同 view 的 attempt 路由(`#/attempt/@<locator>`,单段、不透明)。 */
81
- attemptHref(locator: AttemptLocator): string;
109
+ /**
110
+ * attempt-input page 深链,同 view 的 attempt 路由(`#/attempt/@<locator>`,单段、不透明)
111
+ * 当前报告没有声明 attempt-input page 时不存在——宿主不生成空 href 或假链接,locator 只是
112
+ * 文本(architecture.md「Attempt 详情是一张参数化 page」)。
113
+ */
114
+ attemptHref?(locator: AttemptLocator): string;
82
115
  /** chrome 文案的 locale;官方组件渲染面经上下文读取,宿主外默认 "en"。 */
83
116
  locale: ReportLocale;
84
117
  }
@@ -86,6 +119,8 @@ export interface WebContext {
86
119
  /** 双面组件解析面的上下文:宿主注入的数据来源;props 显式给出 input 时以 props 为准。 */
87
120
  export interface ResolveContext {
88
121
  input: ReportInput;
122
+ /** 当前页判别;attempt 叶子组件省略 input 时从这里的 attempt 分支取缺省 evidence。 */
123
+ page: PageContext;
89
124
  }
90
125
 
91
126
  /** 组合组件的上下文:宿主 Scope、结果根完整读取面与规范化后的报告声明。 */
@@ -96,6 +131,8 @@ export interface ComposeContext {
96
131
  results: Results;
97
132
  /** 规范化后的报告声明,只读(docs/feature/reports/library/layout.md「自定义组件」)。 */
98
133
  report: ReportMeta;
134
+ /** 当前页判别:scope 分支只有 id;attempt 分支带 locator + evidence。 */
135
+ page: PageContext;
99
136
  }
100
137
 
101
138
  export interface ComponentFaces<P, R = P> {
@@ -125,9 +162,9 @@ export type ReportComponent<P> = ((props: P) => ReactNode) & {
125
162
  };
126
163
 
127
164
  // web 面的环境上下文:web 宿主渲染前设好;宿主之外(组件直接嵌进用户 React 应用)
128
- // 用默认值 —— attemptHref 默认 view 的 attempt 路由格式。
165
+ // 用默认值——没有 attemptHref,组件显式传 prop 才产生外部链接(architecture.md
166
+ // 「Attempt 详情是一张参数化 page」:没有 target 时 locator 只是文本,不生成假 href)。
129
167
  const DEFAULT_WEB_CONTEXT: WebContext = {
130
- attemptHref: (locator) => `#/attempt/${locator}`,
131
168
  locale: DEFAULT_REPORT_LOCALE,
132
169
  };
133
170
  let activeWebContext: WebContext | null = null;
@@ -143,11 +180,6 @@ export function runWithWebContext<T>(ctx: WebContext, fn: () => T): T {
143
180
  }
144
181
  }
145
182
 
146
- /** 官方组件的装配用:宿主上下文激活时才把 ctx.attemptHref 当默认下钻。 */
147
- export function isHostWebContextActive(): boolean {
148
- return activeWebContext !== null;
149
- }
150
-
151
183
  /** 函数形态:组合组件,只装配已有组件,可以异步;ctx 携带 scope / results / report。 */
152
184
  export function defineComponent<P>(
153
185
  compose: (props: P, context: ComposeContext) => ReportNode | Promise<ReportNode>,
@@ -269,11 +301,13 @@ export class ResolveMemo {
269
301
  }
270
302
  }
271
303
 
272
- /** resolveReportTree 的环境:宿主注入的 Scope / 读取面 / 规范化声明。 */
304
+ /** resolveReportTree 的环境:宿主注入的 Scope / 读取面 / 规范化声明 / 当前页判别。 */
273
305
  export interface ResolveEnv {
274
306
  scope: Scope;
275
307
  results: Results;
276
308
  report: ReportMeta;
309
+ /** 当前渲染的页:scope 分支只有 id;attempt 分支带 locator + evidence。 */
310
+ page: PageContext;
277
311
  /** 一次页渲染一份;跨页共享缓存时由宿主显式传同一实例。 */
278
312
  memo?: ResolveMemo;
279
313
  }
@@ -323,7 +357,7 @@ function bareTextError(value: string | number, path: string[]): Error {
323
357
  */
324
358
  export async function resolveReportTree(node: ReportNode, env: ResolveEnv): Promise<ReportNode> {
325
359
  const memo = env.memo ?? new ResolveMemo();
326
- const composeCtx: ComposeContext = { scope: env.scope, results: env.results, report: env.report };
360
+ const composeCtx: ComposeContext = { scope: env.scope, results: env.results, report: env.report, page: env.page };
327
361
  return resolveNode(node, { memo, composeCtx }, []);
328
362
  }
329
363
 
@@ -367,6 +401,7 @@ async function resolveNode(node: ReportNode | string | number, state: ResolveSta
367
401
  const input = ((props as { input?: unknown }).input as ReportInput | undefined) ?? state.composeCtx.scope;
368
402
  const ctx: InternalResolveContext = {
369
403
  input,
404
+ page: state.composeCtx.page,
370
405
  memoFetch: <T,>(dataFn: unknown, dataInput: unknown, options: unknown, compute: () => Promise<T>) =>
371
406
  state.memo.fetch(dataFn, dataInput, options, compute) as Promise<T>,
372
407
  };
@@ -528,9 +563,10 @@ function shellQuote(value: string): string {
528
563
  export function createTextContext(options?: TextRenderOptions): TextContext {
529
564
  const width = Math.max(20, options?.width ?? 80);
530
565
  const locale = options?.locale ?? DEFAULT_REPORT_LOCALE;
531
- // 默认下钻命令:AttemptLocator `@` 前缀的不透明短串,`niceeval show @<locator>` 是
532
- // show 已实现的真实 CLI 语法,不需要反查 eval id 再拼一条近似命令。
533
- const attemptCommand = options?.attemptCommand ?? ((locator: AttemptLocator) => `niceeval show ${locator}`);
566
+ // 没有默认值:当前报告没有声明 attempt-input page 时不存在下钻命令——宿主(report.ts /
567
+ // web.ts 的渲染入口)据规范化 definition 是否有 attempt page 决定要不要注入生成器,
568
+ // 这里只透传调用方给的值(architecture.md「Attempt 详情是一张参数化 page」)
569
+ const attemptCommand = options?.attemptCommand;
534
570
  const experimentCommand =
535
571
  options?.experimentCommand ?? ((prefix: string) => `niceeval show --exp ${shellQuote(prefix)}`);
536
572
  const make = (w: number): TextContext => ({