niceeval 0.6.0 → 0.6.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 (314) hide show
  1. package/dist/agents/types.d.ts +72 -6
  2. package/dist/context/types.d.ts +32 -12
  3. package/dist/i18n/en.d.ts +54 -0
  4. package/dist/i18n/zh-CN.d.ts +55 -1
  5. package/dist/o11y/types.d.ts +16 -2
  6. package/dist/report/aggregate.d.ts +5 -3
  7. package/dist/report/aggregate.js +32 -5
  8. package/dist/report/built-ins/experiment-comparison.d.ts +39 -0
  9. package/dist/report/built-ins/experiment-comparison.js +119 -0
  10. package/dist/report/built-ins/index.d.ts +2 -1
  11. package/dist/report/built-ins/index.js +2 -2
  12. package/dist/report/components.d.ts +10 -2
  13. package/dist/report/components.js +3 -3
  14. package/dist/report/compute.d.ts +11 -18
  15. package/dist/report/compute.js +68 -66
  16. package/dist/report/flag.d.ts +16 -1
  17. package/dist/report/flag.js +19 -1
  18. package/dist/report/format.d.ts +16 -14
  19. package/dist/report/format.js +28 -30
  20. package/dist/report/index.d.ts +5 -4
  21. package/dist/report/index.js +6 -5
  22. package/dist/report/locale.d.ts +23 -3
  23. package/dist/report/locale.js +47 -6
  24. package/dist/report/metrics.d.ts +13 -1
  25. package/dist/report/metrics.js +66 -15
  26. package/dist/report/primitives.d.ts +6 -0
  27. package/dist/report/react/AttemptList.d.ts +4 -4
  28. package/dist/report/react/AttemptList.js +8 -10
  29. package/dist/report/react/EvalList.d.ts +1 -1
  30. package/dist/report/react/EvalList.js +0 -0
  31. package/dist/report/react/ExperimentComparison.d.ts +8 -0
  32. package/dist/report/react/ExperimentComparison.js +11 -0
  33. package/dist/report/react/ExperimentList.d.ts +4 -2
  34. package/dist/report/react/ExperimentList.js +57 -7
  35. package/dist/report/react/MetricScatter.js +6 -16
  36. package/dist/report/react/chart-math.d.ts +23 -6
  37. package/dist/report/react/chart-math.js +71 -19
  38. package/dist/report/react/fixtures.d.ts +3 -3
  39. package/dist/report/react/fixtures.js +30 -18
  40. package/dist/report/react/format.d.ts +1 -1
  41. package/dist/report/react/format.js +1 -1
  42. package/dist/report/react/index.d.ts +1 -1
  43. package/dist/report/report.d.ts +5 -1
  44. package/dist/report/report.js +6 -2
  45. package/dist/report/text/faces.d.ts +1 -1
  46. package/dist/report/text/faces.js +100 -61
  47. package/dist/report/text/table.js +36 -5
  48. package/dist/report/types.d.ts +40 -34
  49. package/dist/results/types.d.ts +11 -0
  50. package/dist/runner/feedback/sink.d.ts +110 -0
  51. package/dist/runner/types.d.ts +513 -22
  52. package/dist/sandbox/docker.d.ts +23 -2
  53. package/dist/sandbox/e2b.d.ts +15 -1
  54. package/dist/sandbox/errors.d.ts +30 -3
  55. package/dist/sandbox/io-retry.d.ts +17 -0
  56. package/dist/sandbox/registry.d.ts +2 -0
  57. package/dist/sandbox/resolve.d.ts +18 -5
  58. package/dist/sandbox/retry.d.ts +11 -1
  59. package/dist/sandbox/types.d.ts +39 -5
  60. package/dist/sandbox/vercel.d.ts +7 -1
  61. package/dist/scoring/coverage.d.ts +30 -0
  62. package/dist/scoring/display.d.ts +21 -0
  63. package/dist/scoring/display.js +120 -0
  64. package/dist/scoring/types.d.ts +103 -20
  65. package/dist/shared/aggregate.d.ts +1 -0
  66. package/dist/shared/aggregate.js +3 -3
  67. package/dist/shared/types.d.ts +28 -0
  68. package/dist/tty-line.d.ts +0 -4
  69. package/dist/util.d.ts +23 -0
  70. package/docs-site/zh/concepts/adapter.mdx +24 -6
  71. package/docs-site/zh/concepts/assert.mdx +11 -10
  72. package/docs-site/zh/concepts/evals.mdx +7 -6
  73. package/docs-site/zh/concepts/experiment.mdx +1 -1
  74. package/docs-site/zh/concepts/overview.mdx +7 -7
  75. package/docs-site/zh/guides/agent-feedback-loop.mdx +35 -31
  76. package/docs-site/zh/guides/authoring.mdx +33 -0
  77. package/docs-site/zh/guides/ci-integration.mdx +23 -12
  78. package/docs-site/zh/guides/connect-your-agent.mdx +29 -3
  79. package/docs-site/zh/guides/custom-reports.mdx +29 -34
  80. package/docs-site/zh/guides/dataset-fanout.mdx +25 -3
  81. package/docs-site/zh/guides/debug-sandbox.mdx +57 -0
  82. package/docs-site/zh/guides/debugging.mdx +210 -0
  83. package/docs-site/zh/guides/experiments.mdx +10 -3
  84. package/docs-site/zh/guides/fixtures.mdx +3 -1
  85. package/docs-site/zh/guides/official-adapters.mdx +27 -3
  86. package/docs-site/zh/guides/publish-report.mdx +30 -16
  87. package/docs-site/zh/guides/report-components.mdx +49 -37
  88. package/docs-site/zh/guides/reporters.mdx +2 -2
  89. package/docs-site/zh/guides/results-data.mdx +42 -8
  90. package/docs-site/zh/guides/runner.mdx +17 -7
  91. package/docs-site/zh/guides/sandbox-agent.mdx +57 -7
  92. package/docs-site/zh/guides/sandbox-providers.mdx +258 -10
  93. package/docs-site/zh/guides/scoring-guide.mdx +4 -4
  94. package/docs-site/zh/guides/viewing-results.mdx +85 -41
  95. package/docs-site/zh/guides/write-experiment.mdx +5 -3
  96. package/docs-site/zh/guides/write-send.mdx +19 -2
  97. package/docs-site/zh/index.mdx +1 -1
  98. package/docs-site/zh/reference/builtin-agents.mdx +27 -0
  99. package/docs-site/zh/reference/capabilities.mdx +2 -2
  100. package/docs-site/zh/reference/cli.mdx +35 -9
  101. package/docs-site/zh/reference/define-agent.mdx +60 -5
  102. package/docs-site/zh/reference/define-config.mdx +1 -1
  103. package/docs-site/zh/reference/define-eval.mdx +42 -9
  104. package/docs-site/zh/reference/events.mdx +2 -2
  105. package/docs-site/zh/reference/expect.mdx +36 -6
  106. package/package.json +5 -1
  107. package/src/agents/ai-sdk-otel.test.ts +1 -0
  108. package/src/agents/ai-sdk.test.ts +3 -0
  109. package/src/agents/ai-sdk.ts +3 -0
  110. package/src/agents/bub-install-spec.test.ts +34 -0
  111. package/src/agents/bub-install-spec.ts +32 -0
  112. package/src/agents/bub.ts +31 -32
  113. package/src/agents/claude-code.test.ts +130 -9
  114. package/src/agents/claude-code.ts +76 -4
  115. package/src/agents/codex.test.ts +189 -40
  116. package/src/agents/codex.ts +155 -14
  117. package/src/agents/coding-cli-versions.test.ts +15 -0
  118. package/src/agents/coding-cli-versions.ts +3 -0
  119. package/src/agents/index.ts +11 -0
  120. package/src/agents/langgraph.test.ts +204 -0
  121. package/src/agents/langgraph.ts +495 -0
  122. package/src/agents/marketplace.ts +85 -0
  123. package/src/agents/native-config.test.ts +179 -0
  124. package/src/agents/native-config.ts +267 -0
  125. package/src/agents/openai-compat.test.ts +1 -0
  126. package/src/agents/openclaw.test.ts +31 -0
  127. package/src/agents/openclaw.ts +171 -0
  128. package/src/agents/plugin-config.test.ts +1 -0
  129. package/src/agents/sdk-streams.test.ts +79 -0
  130. package/src/agents/sdk-streams.ts +55 -10
  131. package/src/agents/skills.test.ts +1 -0
  132. package/src/agents/streaming.test.ts +3 -9
  133. package/src/agents/types.ts +73 -6
  134. package/src/agents/ui-message-stream.test.ts +3 -0
  135. package/src/cli.ts +411 -108
  136. package/src/context/context.test.ts +51 -12
  137. package/src/context/context.ts +161 -29
  138. package/src/context/session.test.ts +1 -0
  139. package/src/context/session.ts +114 -6
  140. package/src/context/types.ts +30 -12
  141. package/src/define.test.ts +13 -8
  142. package/src/define.ts +25 -4
  143. package/src/expect/index.ts +53 -23
  144. package/src/i18n/en.ts +65 -4
  145. package/src/i18n/zh-CN.ts +66 -4
  146. package/src/o11y/cost.test.ts +1 -0
  147. package/src/o11y/execution-tree.test.ts +1 -20
  148. package/src/o11y/otlp/mappers/claude-code.test.ts +1 -0
  149. package/src/o11y/otlp/parse.test.ts +1 -0
  150. package/src/o11y/otlp/turn-otel.test.ts +1 -0
  151. package/src/o11y/parsers/bub.test.ts +1 -0
  152. package/src/o11y/parsers/claude-code.test.ts +1 -34
  153. package/src/o11y/parsers/openclaw.test.ts +154 -0
  154. package/src/o11y/parsers/openclaw.ts +310 -0
  155. package/src/o11y/prices.json +746 -311
  156. package/src/o11y/tool-names.test.ts +1 -0
  157. package/src/o11y/types.ts +16 -2
  158. package/src/report/aggregate.ts +34 -5
  159. package/src/report/built-in-user-parity.test.tsx +127 -173
  160. package/src/report/built-ins/experiment-comparison.tsx +179 -0
  161. package/src/report/built-ins/index.ts +7 -2
  162. package/src/report/components.tsx +11 -3
  163. package/src/report/compute.ts +80 -74
  164. package/src/report/dual-render.test.tsx +222 -91
  165. package/src/report/flag.ts +30 -2
  166. package/src/report/format.ts +36 -27
  167. package/src/report/index.ts +23 -6
  168. package/src/report/locale.ts +49 -6
  169. package/src/report/metrics.ts +68 -15
  170. package/src/report/primitives.tsx +6 -0
  171. package/src/report/react/AttemptList.tsx +9 -36
  172. package/src/report/react/EvalList.tsx +0 -0
  173. package/src/report/react/ExperimentComparison.tsx +68 -0
  174. package/src/report/react/ExperimentList.tsx +173 -55
  175. package/src/report/react/MetricScatter.tsx +13 -25
  176. package/src/report/react/chart-math.test.ts +85 -0
  177. package/src/report/react/chart-math.ts +101 -22
  178. package/src/report/react/enhance.js +72 -1
  179. package/src/report/react/fixtures.ts +34 -21
  180. package/src/report/react/format.ts +1 -1
  181. package/src/report/react/index.tsx +0 -1
  182. package/src/report/react/render.test.tsx +30 -69
  183. package/src/report/react/styles.css +112 -14
  184. package/src/report/report.test.ts +308 -105
  185. package/src/report/report.ts +6 -2
  186. package/src/report/text/faces.ts +111 -67
  187. package/src/report/text/table.ts +42 -5
  188. package/src/report/types.ts +42 -34
  189. package/src/results/annotated-source.test.ts +62 -9
  190. package/src/results/annotated-source.ts +64 -6
  191. package/src/results/attempt-evidence.test.ts +9 -7
  192. package/src/results/attempt-evidence.ts +15 -8
  193. package/src/results/attempt-source.ts +6 -3
  194. package/src/results/copy.ts +145 -55
  195. package/src/results/host-equivalence.test.ts +11 -9
  196. package/src/results/index.ts +2 -0
  197. package/src/results/locator.test.ts +1 -22
  198. package/src/results/open.ts +7 -1
  199. package/src/results/publish.ts +149 -0
  200. package/src/results/results.test.ts +85 -51
  201. package/src/results/truncate.ts +90 -0
  202. package/src/results/types.ts +7 -0
  203. package/src/results/writer.ts +31 -13
  204. package/src/runner/attempt.test.ts +138 -7
  205. package/src/runner/attempt.ts +603 -104
  206. package/src/runner/discover.test.ts +47 -0
  207. package/src/runner/discover.ts +36 -2
  208. package/src/runner/eval-source.test.ts +1 -27
  209. package/src/runner/feedback/agent.test.ts +504 -0
  210. package/src/runner/feedback/agent.ts +409 -0
  211. package/src/runner/feedback/ci.test.ts +562 -0
  212. package/src/runner/feedback/ci.ts +401 -0
  213. package/src/runner/feedback/coordinator.test.ts +317 -0
  214. package/src/runner/feedback/coordinator.ts +397 -0
  215. package/src/runner/feedback/failure.ts +40 -0
  216. package/src/runner/feedback/human.test.ts +616 -0
  217. package/src/runner/feedback/human.ts +535 -0
  218. package/src/runner/feedback/index.ts +66 -0
  219. package/src/runner/feedback/io.ts +78 -0
  220. package/src/runner/feedback/profile.test.ts +50 -0
  221. package/src/runner/feedback/profile.ts +58 -0
  222. package/src/runner/feedback/reducer.test.ts +395 -0
  223. package/src/runner/feedback/reducer.ts +260 -0
  224. package/src/runner/feedback/renderer.ts +82 -0
  225. package/src/runner/feedback/sink.ts +203 -0
  226. package/src/runner/feedback/testing.ts +106 -0
  227. package/src/runner/ledger.test.ts +230 -0
  228. package/src/runner/ledger.ts +329 -0
  229. package/src/runner/report.test.ts +128 -3
  230. package/src/runner/report.ts +33 -9
  231. package/src/runner/reporters/artifacts.ts +8 -2
  232. package/src/runner/reporters/braintrust.test.ts +8 -7
  233. package/src/runner/reporters/braintrust.ts +9 -2
  234. package/src/runner/reporters/index.ts +2 -2
  235. package/src/runner/reporters/json.test.ts +162 -0
  236. package/src/runner/reporters/json.ts +35 -8
  237. package/src/runner/reporters/shared.ts +1 -5
  238. package/src/runner/run.test.ts +760 -3
  239. package/src/runner/run.ts +242 -36
  240. package/src/runner/sandbox-prep.ts +3 -42
  241. package/src/runner/timing.ts +158 -0
  242. package/src/runner/types.ts +518 -22
  243. package/src/sandbox/checkpoint.test.ts +55 -0
  244. package/src/sandbox/checkpoint.ts +29 -8
  245. package/src/sandbox/cli-commands.ts +407 -0
  246. package/src/sandbox/docker.ts +115 -16
  247. package/src/sandbox/e2b-agent-template.test.ts +56 -0
  248. package/src/sandbox/e2b-agent-template.ts +94 -0
  249. package/src/sandbox/e2b.ts +74 -9
  250. package/src/sandbox/errors.ts +111 -4
  251. package/src/sandbox/index.ts +2 -0
  252. package/src/sandbox/io-retry.test.ts +58 -0
  253. package/src/sandbox/io-retry.ts +45 -0
  254. package/src/sandbox/keep-registry.test.ts +86 -0
  255. package/src/sandbox/keep-registry.ts +142 -0
  256. package/src/sandbox/keep.ts +178 -0
  257. package/src/sandbox/paths.test.ts +1 -0
  258. package/src/sandbox/paths.ts +19 -8
  259. package/src/sandbox/registry.ts +20 -3
  260. package/src/sandbox/resolve.ts +76 -11
  261. package/src/sandbox/retry.test.ts +70 -0
  262. package/src/sandbox/retry.ts +46 -4
  263. package/src/sandbox/types.ts +44 -6
  264. package/src/sandbox/vercel.ts +43 -20
  265. package/src/scoring/collector.ts +60 -17
  266. package/src/scoring/coverage.ts +95 -0
  267. package/src/scoring/diff.ts +81 -0
  268. package/src/scoring/display.test.ts +121 -0
  269. package/src/scoring/display.ts +133 -0
  270. package/src/scoring/evidence.test.ts +189 -0
  271. package/src/scoring/judge.test.ts +142 -0
  272. package/src/scoring/judge.ts +15 -18
  273. package/src/scoring/scoped.ts +217 -50
  274. package/src/scoring/types.ts +117 -20
  275. package/src/scoring/verdict.ts +16 -4
  276. package/src/shared/aggregate.ts +3 -2
  277. package/src/shared/types.ts +31 -0
  278. package/src/show/compose.ts +2 -2
  279. package/src/show/index.ts +29 -16
  280. package/src/show/render.ts +626 -308
  281. package/src/show/show.test.ts +251 -36
  282. package/src/tty-line.ts +8 -26
  283. package/src/util.test.ts +1 -0
  284. package/src/util.ts +41 -0
  285. package/src/view/app/components/AttemptModal.tsx +153 -2
  286. package/src/view/app/components/CodeView.tsx +32 -11
  287. package/src/view/app/components/CopyControls.tsx +2 -2
  288. package/src/view/app/i18n.ts +6 -0
  289. package/src/view/app/lib/attempt-route.test.ts +1 -0
  290. package/src/view/app/lib/verdict.ts +7 -9
  291. package/src/view/artifact-serving.test.ts +2 -1
  292. package/src/view/client-dist/app.css +1 -1
  293. package/src/view/client-dist/app.js +17 -17
  294. package/src/view/data.test.ts +2 -1
  295. package/src/view/data.ts +17 -7
  296. package/src/view/index.ts +12 -1
  297. package/src/view/server.ts +2 -0
  298. package/src/view/shared/types.ts +1 -1
  299. package/src/view/styles.css +3 -0
  300. package/src/view/view-report.test.ts +11 -10
  301. package/dist/o11y/execution-tree.d.ts +0 -103
  302. package/dist/o11y/otlp/select.d.ts +0 -22
  303. package/dist/report/built-ins/cost-pass-rate-comparison.d.ts +0 -1
  304. package/dist/report/built-ins/cost-pass-rate-comparison.js +0 -17
  305. package/dist/results/annotated-source.d.ts +0 -61
  306. package/dist/results/attempt-evidence.d.ts +0 -69
  307. package/dist/results/attempt-source.d.ts +0 -15
  308. package/src/report/built-ins/cost-pass-rate-comparison.tsx +0 -23
  309. package/src/runner/reporters/console.ts +0 -70
  310. package/src/runner/reporters/live.test.ts +0 -56
  311. package/src/runner/reporters/live.ts +0 -247
  312. package/src/runner/reporters/quiet.test.ts +0 -66
  313. package/src/runner/reporters/quiet.ts +0 -49
  314. package/src/runner/reporters/table.ts +0 -277
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { formatMetricValue } from "../format.js";
3
3
  import { DEFAULT_REPORT_LOCALE, countText, localeText, resolveMetricLabel } from "../locale.js";
4
- import { layoutLabelOffsets, niceTicks } from "./chart-math.js";
4
+ import { niceTicks, placePointLabels } from "./chart-math.js";
5
5
  import { colorClassForKey, seriesClassForKey } from "./colors.js";
6
6
  import { cx } from "./format.js";
7
7
  const WIDTH = 640;
@@ -40,10 +40,6 @@ export function MetricScatter({ data, pointHref, className, locale = DEFAULT_REP
40
40
  if (drawableRows.length === 0) {
41
41
  return (_jsxs("figure", { className: cx("nre", "nre-metric-scatter", className), children: [_jsx("p", { className: "nre-scatter-empty nre-missing", children: localeText(locale, "scatter.noData", { x: xLabel, y: yLabel }) }), missingNote] }));
42
42
  }
43
- // 恰好 1 个可画点:成本 × 通过率的对比至少要两个实验,单点不成图 —— 显式说清而不是画个孤点。
44
- if (drawableRows.length === 1) {
45
- return (_jsxs("figure", { className: cx("nre", "nre-metric-scatter", className), children: [_jsx("p", { className: "nre-scatter-empty", children: localeText(locale, "scatter.needTwo", { x: xLabel, y: yLabel }) }), missingNote] }));
46
- }
47
43
  const axisLabel = (label, col) => `${label}${col.unit ? `(${col.unit})` : ""}`;
48
44
  const xScale = axisScale(drawableRows.map((r) => r.x.value), data.x.better, MARGIN.left, MARGIN.left + PLOT_W);
49
45
  // y 像素轴向下增长:better:"higher" 高值在上 → 映射到 [bottom, top];"lower" 由 axisScale 反向后同样落到上方
@@ -57,7 +53,7 @@ export function MetricScatter({ data, pointHref, className, locale = DEFAULT_REP
57
53
  label: pointLabel(r.key),
58
54
  xValue,
59
55
  yValue,
60
- // hover 内容:experiment(点键)+ 系列(series,CostPassRateComparison 传的是 agent 维度,
56
+ // hover 内容:experiment(点键)+ 系列(series,ExperimentComparison 传的是 agent 维度,
61
57
  // 有则加一行;无系列的散点没有这行)+ 两轴 display 与 samples/total(docs/feature/reports/library.md「MetricScatter」行为清单)
62
58
  title: `${r.key}${r.series !== undefined ? `\n${r.series}` : ""}\n${xLabel}: ${r.x.display}(${r.x.samples}/${r.x.total})\n${yLabel}: ${r.y.display}(${r.y.samples}/${r.y.total})`,
63
59
  px: xScale.scale(xValue),
@@ -78,22 +74,16 @@ export function MetricScatter({ data, pointHref, className, locale = DEFAULT_REP
78
74
  }
79
75
  for (const list of bySeries.values())
80
76
  list.sort((a, b) => a.xValue - b.xValue);
81
- // 直接标签的防撞布局:靠右的点把标签锚到左侧,重叠的往下推、补 leader line
82
- const positions = points.map((p) => {
83
- const anchorLeft = p.px >= MARGIN.left + PLOT_W * 0.72;
84
- return { cx: p.px, cy: p.py, anchorLeft, width: p.label.length * 6.4 + 10 };
85
- });
86
- const labelOffsets = layoutLabelOffsets(positions);
77
+ // 直接标签的候选位择优布局:锚向、避让方向都由布局按空间决定,画布边界含边距
78
+ const labels = placePointLabels(points.map((p) => ({ cx: p.px, cy: p.py, width: p.label.length * 6.4 + 10 })), { x0: 2, y0: 2, x1: WIDTH - 2, y1: HEIGHT - 2 });
87
79
  return (_jsxs("figure", { className: cx("nre", "nre-metric-scatter", className), children: [_jsxs("svg", { className: "nre-scatter-svg", viewBox: `0 0 ${WIDTH} ${HEIGHT}`, role: "img", "aria-label": `${axisLabel(xLabel, data.x)} × ${axisLabel(yLabel, data.y)}`, children: [_jsxs("g", { className: "nre-scatter-grid", children: [yScale.ticks.map((tick) => (_jsx("line", { x1: MARGIN.left, x2: MARGIN.left + PLOT_W, y1: yScale.scale(tick), y2: yScale.scale(tick) }, `gy${tick}`))), xScale.ticks.map((tick) => (_jsx("line", { y1: MARGIN.top, y2: MARGIN.top + PLOT_H, x1: xScale.scale(tick), x2: xScale.scale(tick) }, `gx${tick}`)))] }), _jsx("text", { className: "nre-scatter-better-hint", x: MARGIN.left + PLOT_W - 6, y: MARGIN.top + 14, textAnchor: "end", children: localeText(locale, "scatter.betterHint") }), _jsx("g", { className: "nre-scatter-axis nre-scatter-axis-y", children: yScale.ticks.map((tick) => (_jsx("text", { className: "nre-scatter-tick", x: MARGIN.left - 8, y: yScale.scale(tick) + 3, textAnchor: "end", children: formatMetricValue(tick, data.y.unit) }, `ay${tick}`))) }), _jsx("g", { className: "nre-scatter-axis nre-scatter-axis-x", children: xScale.ticks.map((tick) => (_jsx("text", { className: "nre-scatter-tick", x: xScale.scale(tick), y: MARGIN.top + PLOT_H + 16, textAnchor: "middle", children: formatMetricValue(tick, data.x.unit) }, `ax${tick}`))) }), _jsx("text", { className: "nre-scatter-xlabel", x: MARGIN.left + PLOT_W / 2, y: HEIGHT - 8, textAnchor: "middle", children: axisLabel(xLabel, data.x) }), _jsx("text", { className: "nre-scatter-ylabel", x: 14, y: MARGIN.top + PLOT_H / 2, textAnchor: "middle", transform: `rotate(-90 14 ${MARGIN.top + PLOT_H / 2})`, children: axisLabel(yLabel, data.y) }), seriesOrder.map((series) => {
88
80
  const list = bySeries.get(series);
89
81
  if (list.length < 2)
90
82
  return null;
91
83
  return (_jsx("polyline", { className: cx("nre-scatter-line", seriesClassForKey(series)), "data-series": series, points: list.map((p) => `${p.px},${p.py}`).join(" "), fill: "none" }, series));
92
84
  }), points.map((p, i) => {
93
- const { anchorLeft } = positions[i];
94
- const labelX = p.px + (anchorLeft ? -10 : 10);
95
- const labelY = p.py + 4 + labelOffsets[i];
96
- const group = (_jsxs("g", { className: cx("nre-scatter-point", p.series !== undefined ? seriesClassForKey(p.series) : "nre-series-none"), "data-key": p.key, children: [_jsx("title", { children: p.title }), labelOffsets[i] !== 0 && _jsx("line", { className: "nre-leader", x1: p.px, y1: p.py, x2: labelX, y2: labelY - 4 }), _jsx("circle", { className: "nre-scatter-hit", cx: p.px, cy: p.py, r: 12 }), _jsx("circle", { className: "nre-scatter-dot", cx: p.px, cy: p.py, r: 4.5 }), _jsx("text", { className: "nre-scatter-point-label", x: labelX, y: labelY, textAnchor: anchorLeft ? "end" : "start", children: p.label })] }));
85
+ const label = labels[i];
86
+ const group = (_jsxs("g", { className: cx("nre-scatter-point", p.series !== undefined ? seriesClassForKey(p.series) : "nre-series-none"), "data-key": p.key, children: [_jsx("title", { children: p.title }), label.leader && _jsx("line", { className: "nre-leader", x1: p.px, y1: p.py, x2: label.x, y2: label.y - 4 }), _jsx("circle", { className: "nre-scatter-hit", cx: p.px, cy: p.py, r: 12 }), _jsx("circle", { className: "nre-scatter-dot", cx: p.px, cy: p.py, r: 4.5 }), _jsx("text", { className: "nre-scatter-point-label", x: label.x, y: label.y, textAnchor: label.anchor, children: p.label })] }));
97
87
  const row = drawableRows[i];
98
88
  return pointHref ? (_jsx("a", { className: "nre-scatter-point-link", href: pointHref(row), children: group }, p.key)) : (_jsx("g", { children: group }, p.key));
99
89
  })] }), seriesOrder.length > 0 && (_jsx("figcaption", { className: "nre-scatter-legend", children: seriesOrder.map((series) => (_jsx("span", { className: cx("nre-legend-key", "nre-key", colorClassForKey(series)), children: series }, series))) })), missingNote] }));
@@ -1,14 +1,31 @@
1
1
  /** [min, max] 区间上生成 count 个左右的“整齐”刻度(Heckbert nice-numbers)。 */
2
2
  export declare function niceTicks(min: number, max: number, count?: number): number[];
3
- export interface LabelInput {
3
+ export interface PointLabelInput {
4
4
  cx: number;
5
5
  cy: number;
6
+ /** 标签文本的估算宽度(px)。 */
6
7
  width: number;
7
- /** true = 文字锚在点左侧(text-anchor: end),false = 锚在右侧(text-anchor: start)。 */
8
- anchorLeft: boolean;
8
+ }
9
+ /** 标签允许占用的画布范围(含坐标轴边距,标签可以借用空白边距)。 */
10
+ export interface LabelBounds {
11
+ x0: number;
12
+ y0: number;
13
+ x1: number;
14
+ y1: number;
15
+ }
16
+ export interface PlacedPointLabel {
17
+ /** <text> 的锚点坐标(y 为文字基线)。 */
18
+ x: number;
19
+ y: number;
20
+ anchor: "start" | "end" | "middle";
21
+ /** 标签不在点左右紧邻位:渲染层补 leader line 连回原点。 */
22
+ leader: boolean;
9
23
  }
10
24
  /**
11
- * 贪心地把互相重叠的直接标签往下推开(按 y 排序、逐个查重叠再下移),点本身位置不变。
12
- * 散点常见同分不同价的聚簇,这一步避免标签叠成一团糊字。
25
+ * 散点直接标签的候选位择优布局(docs/feature/reports/library.md「MetricScatter」):
26
+ * 每个标签在点四周由近及远的候选环上打分——与已放置标签的重叠、与任何数据点的重叠、
27
+ * 越出画布的面积、离点距离逐项累加,取代价最小的候选。存在无冲突候选时标签必不遮点、
28
+ * 不叠标签、不越界;全候选冲突时取重叠最小者,绝不丢标签。重合点簇因此向不同方向散开,
29
+ * 而不是单向堆叠压到下方的数据点上。
13
30
  */
14
- export declare function layoutLabelOffsets(items: LabelInput[], lineHeight?: number, padX?: number): number[];
31
+ export declare function placePointLabels(points: PointLabelInput[], bounds: LabelBounds): PlacedPointLabel[];
@@ -1,5 +1,4 @@
1
- // 通用图表数值工具:round-number 刻度生成与标签防撞布局,零依赖纯函数。
2
- // 从 src/view/app/lib/chart.ts 迁来(view 侧从这里 re-export),报告组件与 view 共用同一份。
1
+ // 通用图表数值工具:round-number 刻度生成与点标签布局,零依赖纯函数。
3
2
  function niceNumber(range, round) {
4
3
  if (range <= 0)
5
4
  return 1;
@@ -43,26 +42,79 @@ export function niceTicks(min, max, count = 5) {
43
42
  ticks.push(Number(v.toFixed(10)));
44
43
  return ticks;
45
44
  }
45
+ /** 标签框高度(基线上方 10px、下方 2px,对应 11px 字号)。 */
46
+ const LABEL_ASCENT = 10;
47
+ const LABEL_DESCENT = 2;
48
+ /** 数据点视为边长 2×DOT_R 的方形障碍(点半径 4.5 + 命中余量)。 */
49
+ const DOT_R = 6;
50
+ /** 紧邻位与点的水平间距;候选环按它逐环外扩。 */
51
+ const NEAR = 10;
52
+ const RINGS = [NEAR, NEAR + 12, NEAR + 26];
53
+ function labelBoxAt(x, y, anchor, width) {
54
+ const x0 = anchor === "start" ? x : anchor === "end" ? x - width : x - width / 2;
55
+ return { x0, y0: y - LABEL_ASCENT, x1: x0 + width, y1: y + LABEL_DESCENT };
56
+ }
57
+ function overlapArea(a, b) {
58
+ const w = Math.min(a.x1, b.x1) - Math.max(a.x0, b.x0);
59
+ const h = Math.min(a.y1, b.y1) - Math.max(a.y0, b.y0);
60
+ return w > 0 && h > 0 ? w * h : 0;
61
+ }
62
+ function outOfBoundsArea(box, bounds) {
63
+ const total = (box.x1 - box.x0) * (box.y1 - box.y0);
64
+ const w = Math.min(box.x1, bounds.x1) - Math.max(box.x0, bounds.x0);
65
+ const h = Math.min(box.y1, bounds.y1) - Math.max(box.y0, bounds.y0);
66
+ const inside = w > 0 && h > 0 ? w * h : 0;
67
+ return total - inside;
68
+ }
69
+ /** 一个点在距离 d 的候选环:左右紧邻、四个斜角、正上正下。顺序即同代价时的偏好序。 */
70
+ function ringCandidates(p, d) {
71
+ const diag = d * 0.75;
72
+ return [
73
+ { x: p.cx + d, y: p.cy + 4, anchor: "start" },
74
+ { x: p.cx - d, y: p.cy + 4, anchor: "end" },
75
+ { x: p.cx + diag, y: p.cy - diag - 2, anchor: "start" },
76
+ { x: p.cx - diag, y: p.cy - diag - 2, anchor: "end" },
77
+ { x: p.cx, y: p.cy - d - LABEL_DESCENT - 2, anchor: "middle" },
78
+ { x: p.cx + diag, y: p.cy + diag + LABEL_ASCENT, anchor: "start" },
79
+ { x: p.cx - diag, y: p.cy + diag + LABEL_ASCENT, anchor: "end" },
80
+ { x: p.cx, y: p.cy + d + LABEL_ASCENT + 2, anchor: "middle" },
81
+ ];
82
+ }
46
83
  /**
47
- * 贪心地把互相重叠的直接标签往下推开(按 y 排序、逐个查重叠再下移),点本身位置不变。
48
- * 散点常见同分不同价的聚簇,这一步避免标签叠成一团糊字。
84
+ * 散点直接标签的候选位择优布局(docs/feature/reports/library.md「MetricScatter」):
85
+ * 每个标签在点四周由近及远的候选环上打分——与已放置标签的重叠、与任何数据点的重叠、
86
+ * 越出画布的面积、离点距离逐项累加,取代价最小的候选。存在无冲突候选时标签必不遮点、
87
+ * 不叠标签、不越界;全候选冲突时取重叠最小者,绝不丢标签。重合点簇因此向不同方向散开,
88
+ * 而不是单向堆叠压到下方的数据点上。
49
89
  */
50
- export function layoutLabelOffsets(items, lineHeight = 14, padX = 4) {
51
- const offsets = new Array(items.length).fill(0);
52
- const order = [...items.keys()].sort((a, b) => items[a].cy - items[b].cy || items[a].cx - items[b].cx);
53
- const placed = [];
90
+ export function placePointLabels(points, bounds) {
91
+ const dots = points.map((p) => ({ x0: p.cx - DOT_R, y0: p.cy - DOT_R, x1: p.cx + DOT_R, y1: p.cy + DOT_R }));
92
+ const placedBoxes = [];
93
+ const result = new Array(points.length);
94
+ // 由上到下、由左到右逐个放置,布局确定可复现
95
+ const order = [...points.keys()].sort((a, b) => points[a].cy - points[b].cy || points[a].cx - points[b].cx);
54
96
  for (const i of order) {
55
- const it = items[i];
56
- const x0 = it.anchorLeft ? it.cx - it.width : it.cx;
57
- const x1 = it.anchorLeft ? it.cx : it.cx + it.width;
58
- let y = it.cy;
59
- let guard = 0;
60
- while (guard < 40 && placed.some((p) => x0 < p.x1 + padX && x1 > p.x0 - padX && Math.abs(y - p.y) < lineHeight)) {
61
- y += lineHeight;
62
- guard++;
97
+ const p = points[i];
98
+ let best = null;
99
+ for (let ring = 0; ring < RINGS.length; ring++) {
100
+ const candidates = ringCandidates(p, RINGS[ring]);
101
+ for (let c = 0; c < candidates.length; c++) {
102
+ const candidate = candidates[c];
103
+ const box = labelBoxAt(candidate.x, candidate.y, candidate.anchor, p.width);
104
+ let cost = 0;
105
+ for (const placed of placedBoxes)
106
+ cost += overlapArea(box, placed) * 4;
107
+ for (const dot of dots)
108
+ cost += overlapArea(box, dot) * 3;
109
+ cost += outOfBoundsArea(box, bounds) * 6;
110
+ cost += ring * 8 + c * 0.5; // 近环、偏好序靠前者优先,只作同冲突程度下的排序
111
+ if (best === null || cost < best.cost) {
112
+ best = { candidate, box, cost, leader: !(ring === 0 && c <= 1) };
113
+ }
114
+ }
63
115
  }
64
- placed.push({ x0, x1, y });
65
- offsets[i] = y - it.cy;
116
+ placedBoxes.push(best.box);
117
+ result[i] = { ...best.candidate, leader: best.leader };
66
118
  }
67
- return offsets;
119
+ return result;
68
120
  }
@@ -6,14 +6,14 @@ export declare const overviewData: OverviewData;
6
6
  export declare const overviewWithCost: OverviewData;
7
7
  /** GroupSummary.data 的产物形态:eval 级折叠计票 + 旧 GroupSelector 口径的通过率(包成 MetricCell)。 */
8
8
  export declare const groupSummaryData: GroupSummaryData;
9
- export declare const tableData: TableData<"pass-rate" | "code-lines">;
9
+ export declare const tableData: TableData<"task-pass-rate" | "code-lines">;
10
10
  /** rows: "experiment" 的榜单形态:行携带 agent/model 元信息与 eval 级折叠计票。 */
11
- export declare const tableDataWithMeta: TableData<"pass-rate">;
11
+ export declare const tableDataWithMeta: TableData<"task-pass-rate">;
12
12
  export declare const matrixData: MatrixData;
13
13
  export declare const scoreboardData: ScoreboardData;
14
14
  export declare const scatterData: ScatterData;
15
15
  export declare const lineData: LineData;
16
- export declare const deltaData: DeltaData<"pass-rate" | "cost">;
16
+ export declare const deltaData: DeltaData<"task-pass-rate" | "cost">;
17
17
  export declare const attemptListItems: AttemptListItem[];
18
18
  export declare const evalListItems: EvalListItem[];
19
19
  export declare const experimentListItems: ExperimentListItem[];
@@ -3,7 +3,7 @@
3
3
  // 数字刻意覆盖诚实细节:coverage 角标(samples<total)、全 null 格子、
4
4
  // 稀疏矩阵、缺数据的散点、delta 的 null 不硬算、truncated 计数。
5
5
  const locator = (s) => s;
6
- export const passRateColumn = { key: "pass-rate", label: "pass rate", unit: "%", better: "higher" };
6
+ export const passRateColumn = { key: "task-pass-rate", label: "pass rate", unit: "%", better: "higher" };
7
7
  export const codeLinesColumn = { key: "code-lines", label: "code lines", unit: "lines", better: "lower" };
8
8
  export const costColumn = { key: "cost", label: "cost", unit: "$", better: "lower" };
9
9
  export const overviewData = {
@@ -59,7 +59,7 @@ export const tableData = {
59
59
  {
60
60
  key: "codex",
61
61
  cells: {
62
- "pass-rate": { value: 0.5, display: "50%", samples: 6, total: 6, refs: [] },
62
+ "task-pass-rate": { value: 0.5, display: "50%", samples: 6, total: 6, refs: [] },
63
63
  // 全 null:一个有效样本都没有 → 缺数据文案,绝不画 0
64
64
  "code-lines": { value: null, display: "—", samples: 0, total: 6, refs: [] },
65
65
  },
@@ -67,7 +67,7 @@ export const tableData = {
67
67
  {
68
68
  key: "bub",
69
69
  cells: {
70
- "pass-rate": {
70
+ "task-pass-rate": {
71
71
  value: 0.87,
72
72
  display: "87%",
73
73
  samples: 6,
@@ -87,7 +87,7 @@ export const tableDataWithMeta = {
87
87
  rows: [
88
88
  {
89
89
  key: "compare/bub",
90
- cells: { "pass-rate": { value: 0.5, display: "50%", samples: 2, total: 2, refs: [] } },
90
+ cells: { "task-pass-rate": { value: 0.5, display: "50%", samples: 2, total: 2, refs: [] } },
91
91
  meta: {
92
92
  agent: "bub",
93
93
  model: "gpt-5.4",
@@ -96,7 +96,7 @@ export const tableDataWithMeta = {
96
96
  },
97
97
  {
98
98
  key: "compare/codex",
99
- cells: { "pass-rate": { value: 1, display: "100%", samples: 2, total: 2, refs: [] } },
99
+ cells: { "task-pass-rate": { value: 1, display: "100%", samples: 2, total: 2, refs: [] } },
100
100
  meta: {
101
101
  agent: "codex",
102
102
  verdicts: { passed: 2, failed: 0, errored: 0, skipped: 0 },
@@ -242,7 +242,7 @@ export const deltaData = {
242
242
  b: { experimentId: "compare/bub--agents-md" },
243
243
  cells: {
244
244
  // 通过率 +12pp:better higher → 好(绿)
245
- "pass-rate": {
245
+ "task-pass-rate": {
246
246
  a: { value: 0.5, display: "50%", samples: 6, total: 6, refs: [] },
247
247
  b: { value: 0.62, display: "62%", samples: 6, total: 6, refs: [] },
248
248
  delta: 0.12,
@@ -262,7 +262,7 @@ export const deltaData = {
262
262
  a: { experimentId: "compare/codex" },
263
263
  b: { experimentId: "compare/codex--agents-md" },
264
264
  cells: {
265
- "pass-rate": {
265
+ "task-pass-rate": {
266
266
  a: { value: 0.4, display: "40%", samples: 6, total: 6, refs: [] },
267
267
  b: { value: 0.4, display: "40%", samples: 6, total: 6, refs: [] },
268
268
  delta: 0,
@@ -292,15 +292,15 @@ const failedAttempt = {
292
292
  name: "roots-correct",
293
293
  severity: "gate",
294
294
  score: 0,
295
- passed: false,
296
- detail: "expected x=2, got x=3",
295
+ outcome: "failed",
296
+ expected: "x=2",
297
+ received: "x=3",
297
298
  evidence: "judge: sign flipped when substituting into the quadratic formula",
298
299
  },
299
300
  ],
300
301
  durationMs: 32_000,
301
302
  costUSD: 0.12,
302
303
  locator: locator("@1a4a4a4"),
303
- capabilities: { eval: true, execution: true, timing: true, diff: false },
304
304
  };
305
305
  const erroredAttempt = {
306
306
  evalId: "geometry/angles",
@@ -308,11 +308,26 @@ const erroredAttempt = {
308
308
  attempt: 0,
309
309
  agent: "codex",
310
310
  verdict: "errored",
311
- error: "TypeError: cannot read properties of undefined (reading 'foo')",
311
+ // 结构化 error:列表只渲染 message 一层摘要;cause/stack diagnostics 是下钻详情,随数据携带
312
+ error: {
313
+ code: "unexpected-error",
314
+ message: "TypeError: cannot read properties of undefined (reading 'foo')",
315
+ phase: "eval.run",
316
+ stack: "TypeError: cannot read properties of undefined (reading 'foo')\n at run (adapter.ts:42:7)",
317
+ cause: { name: "TypeError", message: "cannot read properties of undefined (reading 'foo')" },
318
+ },
319
+ diagnostics: [
320
+ { code: "sandbox-teardown-failed", level: "warning", message: "sandbox teardown timed out", phase: "sandbox.teardown" },
321
+ ],
312
322
  assertions: [],
313
323
  durationMs: 4_500,
314
324
  locator: locator("@1c1c1c1"),
315
- capabilities: { eval: false, execution: true, timing: false, diff: false },
325
+ };
326
+ const failedRetryAttempt = {
327
+ ...failedAttempt,
328
+ attempt: 4,
329
+ durationMs: 35_000,
330
+ locator: locator("@1b5b5b5"),
316
331
  };
317
332
  export const attemptListItems = [failedAttempt, erroredAttempt];
318
333
  export const evalListItems = [
@@ -320,7 +335,6 @@ export const evalListItems = [
320
335
  evalId: "algebra/quadratic",
321
336
  experimentId: "compare/bub",
322
337
  verdict: "failed",
323
- reason: "roots-correct: expected x=2, got x=3",
324
338
  score: { value: 0, display: "0%", samples: 1, total: 1, refs: [failedAttempt.locator] },
325
339
  duration: { value: 32_000, display: "32.0s", samples: 1, total: 1, refs: [failedAttempt.locator] },
326
340
  cost: { value: 0.12, display: "$0.12", samples: 1, total: 1, refs: [failedAttempt.locator] },
@@ -330,7 +344,6 @@ export const evalListItems = [
330
344
  evalId: "geometry/angles",
331
345
  experimentId: "compare/codex",
332
346
  verdict: "errored",
333
- reason: erroredAttempt.error,
334
347
  score: { value: 0, display: "0%", samples: 1, total: 1, refs: [erroredAttempt.locator] },
335
348
  duration: { value: 4_500, display: "4.5s", samples: 1, total: 1, refs: [erroredAttempt.locator] },
336
349
  cost: { value: null, display: "—", samples: 0, total: 1, refs: [] },
@@ -342,22 +355,22 @@ export const experimentListItems = [
342
355
  experimentId: "compare/bub",
343
356
  agent: "bub",
344
357
  model: "gpt-5.4",
358
+ flags: { memory: true },
345
359
  verdicts: { passed: 1, failed: 1, errored: 0, skipped: 0 },
346
360
  passRate: { value: 0.5, display: "50%", samples: 2, total: 2, refs: [] },
347
361
  cost: { value: 0.12, display: "$0.12", samples: 1, total: 2, refs: [failedAttempt.locator] },
348
362
  duration: { value: 32_000, display: "32.0s", samples: 2, total: 2, refs: [] },
349
363
  tokens: { value: null, display: "—", samples: 0, total: 2, refs: [] },
350
364
  evals: 2,
351
- attempts: 2,
365
+ attempts: 3,
352
366
  lastRunAt: "2026-07-01T10:00:00Z",
353
367
  evalRows: [
354
368
  {
355
369
  evalId: "algebra/quadratic",
356
370
  verdict: "failed",
357
- reason: "roots-correct: expected x=2, got x=3",
358
371
  duration: { value: 32_000, display: "32.0s", samples: 1, total: 1, refs: [failedAttempt.locator] },
359
372
  cost: { value: 0.12, display: "$0.12", samples: 1, total: 1, refs: [failedAttempt.locator] },
360
- attempts: [failedAttempt],
373
+ attempts: [failedAttempt, failedRetryAttempt],
361
374
  },
362
375
  {
363
376
  evalId: "algebra/simple",
@@ -383,7 +396,6 @@ export const experimentListItems = [
383
396
  {
384
397
  evalId: "geometry/angles",
385
398
  verdict: "errored",
386
- reason: erroredAttempt.error,
387
399
  duration: { value: 4_500, display: "4.5s", samples: 1, total: 1, refs: [erroredAttempt.locator] },
388
400
  cost: { value: null, display: "—", samples: 0, total: 1, refs: [] },
389
401
  attempts: [erroredAttempt],
@@ -1,3 +1,3 @@
1
- export { MISSING_TEXT, capabilityBadge, formatDurationMs, formatPercent, formatUSD, verdictMark } from "../format.ts";
1
+ export { MISSING_TEXT, formatDurationMs, formatPercent, formatUSD, verdictMark } from "../format.ts";
2
2
  /** 拼 class 名:过滤空值,末尾接使用者透传的 className。 */
3
3
  export declare function cx(...parts: (string | undefined | false)[]): string;
@@ -1,6 +1,6 @@
1
1
  // web 面的小工具:展示格式化统一住在计算侧的 ../format.ts(两个渲染面同一份),
2
2
  // 这里 re-export 并补 class 名拼接。MetricCell 一律自带 display,组件不重算。
3
- export { MISSING_TEXT, capabilityBadge, formatDurationMs, formatPercent, formatUSD, verdictMark } from "../format.js";
3
+ export { MISSING_TEXT, formatDurationMs, formatPercent, formatUSD, verdictMark } from "../format.js";
4
4
  /** 拼 class 名:过滤空值,末尾接使用者透传的 className。 */
5
5
  export function cx(...parts) {
6
6
  return parts.filter(Boolean).join(" ");
@@ -1,6 +1,6 @@
1
1
  export { AttemptList, DeltaTable, EvalList, ExperimentList, GroupSummary, MetricBars, MetricLine, MetricMatrix, MetricScatter, MetricTable, RunOverview, Scoreboard, } from "../components.tsx";
2
2
  export type { AttemptListProps, DeltaTableProps, EvalListProps, ExperimentListProps, GroupSummaryProps, MetricLineProps, MetricMatrixProps, MetricScatterProps, MetricTableProps, RunOverviewProps, ScoreboardProps, } from "../components.tsx";
3
- export type { AttemptEvidenceCapabilities, AttemptListItem, AttemptLocator, DeltaData, EvalListItem, ExperimentListEvalRow, ExperimentListItem, GroupSummaryData, LineAxis, LineData, MatrixData, MetricCell, MetricColumn, OverviewData, ScatterData, ScoreboardData, SelectionWarning, TableData, TableRowMeta, } from "../types.ts";
3
+ export type { AttemptListItem, AttemptLocator, DeltaData, EvalListItem, ExperimentListEvalRow, ExperimentListItem, GroupSummaryData, LineAxis, LineData, MatrixData, MetricCell, MetricColumn, OverviewData, ScatterData, ScoreboardData, SelectionWarning, TableData, TableRowMeta, } from "../types.ts";
4
4
  export { DEFAULT_REPORT_LOCALE, resolveMetricLabel } from "../locale.ts";
5
5
  export type { LocalizedLabel, ReportLocale } from "../locale.ts";
6
6
  export { NRE_PALETTE, colorClassForKey, colorHexForKey, colorIndexForKey, seriesClassForKey } from "./colors.ts";
@@ -13,7 +13,11 @@ export interface ReportDefinition {
13
13
  [REPORT_DEFINITION]: true;
14
14
  }
15
15
  export declare function defineReport(build: (ctx: ReportContext) => ReportNode | Promise<ReportNode>): ReportDefinition;
16
- /** 宿主装载报告文件时用:默认导出是不是 defineReport 的产物。 */
16
+ /**
17
+ * 宿主装载报告文件时用:默认导出是不是 ReportDefinition。defineReport 的产物是普通对象;
18
+ * 「组件兼报告」(把 defineReport 产物 Object.assign 到双面组件上)是挂了 build 面的
19
+ * 可调用函数,同样算数——判据只看 build 面与标记,不看宿主形态。
20
+ */
17
21
  export declare function isReportDefinition(value: unknown): value is ReportDefinition;
18
22
  /**
19
23
  * text 宿主的装载语义:build → 渲染前解析数据组件(唯一的 await 边界)→ 树校验 → 遍历渲染
@@ -14,9 +14,13 @@ export function defineReport(build) {
14
14
  }
15
15
  return { build, [REPORT_DEFINITION]: true };
16
16
  }
17
- /** 宿主装载报告文件时用:默认导出是不是 defineReport 的产物。 */
17
+ /**
18
+ * 宿主装载报告文件时用:默认导出是不是 ReportDefinition。defineReport 的产物是普通对象;
19
+ * 「组件兼报告」(把 defineReport 产物 Object.assign 到双面组件上)是挂了 build 面的
20
+ * 可调用函数,同样算数——判据只看 build 面与标记,不看宿主形态。
21
+ */
18
22
  export function isReportDefinition(value) {
19
- return (typeof value === "object" &&
23
+ return ((typeof value === "object" || typeof value === "function") &&
20
24
  value !== null &&
21
25
  value[REPORT_DEFINITION] === true &&
22
26
  typeof value.build === "function");
@@ -24,6 +24,6 @@ export declare function scoreboardText(data: ScoreboardData, ctx: TextContext):
24
24
  export declare function scatterText(data: ScatterData, ctx: TextContext): string;
25
25
  export declare function lineText(data: LineData, ctx: TextContext): string;
26
26
  export declare function deltaText(data: DeltaData, ctx: TextContext): string;
27
- export declare function experimentListText(items: ExperimentListItem[], ctx: TextContext): string;
27
+ export declare function experimentListText(items: ExperimentListItem[], ctx: TextContext, relativeTo?: string): string;
28
28
  export declare function evalListText(items: EvalListItem[], ctx: TextContext): string;
29
29
  export declare function attemptListText(items: AttemptListItem[], total: number | undefined, ctx: TextContext): string;