niceeval 0.11.4-canary.20 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. package/INDEX.md +1 -0
  2. package/dist/i18n/en.d.ts +9 -1
  3. package/dist/i18n/en.js +11 -2
  4. package/dist/i18n/zh-CN.d.ts +10 -2
  5. package/dist/i18n/zh-CN.js +11 -2
  6. package/dist/record/attempt-evidence.d.ts +1 -1
  7. package/dist/record/open.js +25 -0
  8. package/dist/report/assets/series-encoding.d.ts +36 -0
  9. package/dist/report/assets/series-encoding.js +104 -0
  10. package/dist/report/built-in/result-components.js +12 -2
  11. package/dist/report/components/attempt-detail/compute.d.ts +8 -1
  12. package/dist/report/components/attempt-detail/compute.js +19 -1
  13. package/dist/report/components/attempt-detail/index.js +25 -3
  14. package/dist/report/components/attempt-detail/validate.js +3 -2
  15. package/dist/report/components/entity-lists/compute.d.ts +6 -0
  16. package/dist/report/components/entity-lists/compute.js +55 -8
  17. package/dist/report/components/entity-lists/content.d.ts +2 -0
  18. package/dist/report/components/entity-lists/content.js +59 -6
  19. package/dist/report/components/entity-lists/index.d.ts +21 -1
  20. package/dist/report/components/entity-lists/index.js +40 -7
  21. package/dist/report/components/entity-lists/validate.js +21 -0
  22. package/dist/report/components/fixtures.js +2 -0
  23. package/dist/report/definition/cell.d.ts +22 -0
  24. package/dist/report/definition/cell.js +23 -9
  25. package/dist/report/definition/primitives/chart.js +125 -25
  26. package/dist/report/definition/primitives/conversation.d.ts +1 -1
  27. package/dist/report/definition/primitives/conversation.js +5 -3
  28. package/dist/report/definition/primitives.js +32 -7
  29. package/dist/report/definition/tree.d.ts +5 -2
  30. package/dist/report/definition/tree.js +1 -0
  31. package/dist/report/index.d.ts +5 -5
  32. package/dist/report/index.js +2 -2
  33. package/dist/report/model/aggregate.d.ts +14 -1
  34. package/dist/report/model/aggregate.js +34 -2
  35. package/dist/report/model/conversions.d.ts +1 -0
  36. package/dist/report/model/conversions.js +4 -1
  37. package/dist/report/model/format.d.ts +6 -5
  38. package/dist/report/model/format.js +30 -9
  39. package/dist/report/model/locale.d.ts +19 -3
  40. package/dist/report/model/locale.js +31 -5
  41. package/dist/report/model/types.d.ts +41 -4
  42. package/dist/report/presentation.d.ts +53 -6
  43. package/dist/report/presentation.js +40 -5
  44. package/dist/report/react/index.d.ts +3 -3
  45. package/dist/report/react/index.js +1 -1
  46. package/dist/report/slices/compute.js +52 -4
  47. package/dist/report/slices/content.d.ts +2 -1
  48. package/dist/report/slices/content.js +48 -15
  49. package/dist/report/slices/validate.js +23 -0
  50. package/dist/report/tasks.d.ts +7 -1
  51. package/dist/report/tasks.js +5 -3
  52. package/dist/runner/types.d.ts +30 -10
  53. package/dist/sample/index.d.ts +0 -9
  54. package/dist/sample/index.js +0 -18
  55. package/dist/sandbox/backend.d.ts +1 -1
  56. package/dist/sandbox/commands.d.ts +1 -1
  57. package/dist/sandbox/compose.d.ts +2 -1
  58. package/dist/sandbox/docker.d.ts +28 -7
  59. package/dist/sandbox/e2b.d.ts +11 -0
  60. package/dist/sandbox/identity.d.ts +6 -1
  61. package/dist/sandbox/layer.d.ts +36 -3
  62. package/dist/sandbox/local.d.ts +9 -0
  63. package/dist/sandbox/types.d.ts +14 -8
  64. package/dist/sandbox/vercel.d.ts +9 -0
  65. package/docs-site/zh/reference/builtin-agents.mdx +2 -0
  66. package/docs-site/zh/reference/cli.mdx +1 -0
  67. package/docs-site/zh/reference/define-agent.mdx +13 -9
  68. package/docs-site/zh/reference/define-config.mdx +8 -0
  69. package/docs-site/zh/reference/official-adapters.mdx +1 -1
  70. package/docs-site/zh/reference/results-data.mdx +1 -1
  71. package/docs-site/zh/tutorials/deploy-report-site.mdx +157 -0
  72. package/docs-site/zh/tutorials/publish-report.mdx +1 -1
  73. package/docs-site/zh/tutorials/rerun-and-cache.mdx +1 -1
  74. package/docs-site/zh/tutorials/sandbox-providers.mdx +19 -4
  75. package/package.json +1 -1
  76. package/src/agents/codex-env.test.ts +8 -1
  77. package/src/agents/codex.ts +8 -0
  78. package/src/agents/coding-cli-versions.ts +22 -6
  79. package/src/cli.ts +23 -1
  80. package/src/i18n/en.ts +14 -3
  81. package/src/i18n/zh-CN.ts +13 -2
  82. package/src/record/attempt-evidence.ts +1 -1
  83. package/src/record/open.ts +21 -0
  84. package/src/record/results.test.ts +74 -13
  85. package/src/record/truncate.ts +32 -7
  86. package/src/record/writer.ts +39 -8
  87. package/src/report/assets/series-encoding.tsx +177 -0
  88. package/src/report/assets/styles.css +97 -5
  89. package/src/report/built-in/result-components.tsx +23 -2
  90. package/src/report/components/attempt-detail/attempt-components.test.tsx +41 -0
  91. package/src/report/components/attempt-detail/compute.ts +20 -2
  92. package/src/report/components/attempt-detail/index.tsx +32 -2
  93. package/src/report/components/attempt-detail/validate.test.ts +1 -0
  94. package/src/report/components/attempt-detail/validate.tsx +3 -1
  95. package/src/report/components/compute.test.ts +196 -0
  96. package/src/report/components/entity-lists/compute.ts +73 -7
  97. package/src/report/components/entity-lists/content.test.ts +14 -8
  98. package/src/report/components/entity-lists/content.ts +63 -6
  99. package/src/report/components/entity-lists/index.tsx +93 -8
  100. package/src/report/components/entity-lists/validate.test.ts +9 -0
  101. package/src/report/components/entity-lists/validate.ts +16 -0
  102. package/src/report/components/fixtures.ts +2 -0
  103. package/src/report/definition/cell.ts +46 -11
  104. package/src/report/definition/conversation.test.tsx +5 -1
  105. package/src/report/definition/primitives/chart.tsx +197 -32
  106. package/src/report/definition/primitives/conversation.tsx +7 -4
  107. package/src/report/definition/primitives.tsx +81 -9
  108. package/src/report/definition/tree.ts +6 -2
  109. package/src/report/index.ts +15 -2
  110. package/src/report/model/aggregate.ts +40 -2
  111. package/src/report/model/conversions.ts +5 -0
  112. package/src/report/model/format.ts +32 -9
  113. package/src/report/model/locale.ts +35 -5
  114. package/src/report/model/presentation.test.ts +23 -1
  115. package/src/report/model/types.ts +40 -4
  116. package/src/report/presentation.test.tsx +179 -19
  117. package/src/report/presentation.ts +114 -11
  118. package/src/report/react/index.tsx +13 -2
  119. package/src/report/runtime/dual-render.test.tsx +38 -2
  120. package/src/report/slices/compute.ts +59 -4
  121. package/src/report/slices/content.ts +55 -16
  122. package/src/report/slices/delta-table.test.ts +138 -3
  123. package/src/report/slices/validate.test.ts +29 -0
  124. package/src/report/slices/validate.ts +16 -0
  125. package/src/report/tasks.ts +12 -2
  126. package/src/runner/accept.test.ts +328 -10
  127. package/src/runner/accept.ts +239 -72
  128. package/src/runner/attempt.test.ts +25 -13
  129. package/src/runner/attempt.ts +9 -9
  130. package/src/runner/config-identity.test.ts +25 -0
  131. package/src/runner/config-identity.ts +6 -7
  132. package/src/runner/feedback/coordinator.test.ts +51 -0
  133. package/src/runner/feedback/coordinator.ts +23 -0
  134. package/src/runner/feedback/failure.test.ts +15 -0
  135. package/src/runner/feedback/failure.ts +7 -0
  136. package/src/runner/feedback/human.test.ts +548 -27
  137. package/src/runner/feedback/human.ts +321 -47
  138. package/src/runner/feedback/index.ts +10 -0
  139. package/src/runner/feedback/input-guard.test.ts +251 -0
  140. package/src/runner/feedback/input-guard.ts +171 -0
  141. package/src/runner/feedback/reducer.ts +1 -0
  142. package/src/runner/fingerprint.test.ts +24 -0
  143. package/src/runner/gate-lease.test.ts +38 -14
  144. package/src/runner/ledger.test.ts +10 -7
  145. package/src/runner/ledger.ts +9 -2
  146. package/src/runner/lock.test.ts +40 -16
  147. package/src/runner/manifest.ts +8 -9
  148. package/src/runner/reporters/artifacts.ts +25 -3
  149. package/src/runner/run.test.ts +61 -0
  150. package/src/runner/run.ts +34 -9
  151. package/src/runner/sandbox-pool.test.ts +216 -12
  152. package/src/runner/sandbox-pool.ts +20 -2
  153. package/src/runner/types.ts +30 -10
  154. package/src/sample/index.ts +0 -16
  155. package/src/sample/sample.test.ts +78 -0
  156. package/src/sandbox/backend.ts +1 -1
  157. package/src/sandbox/commands.ts +4 -7
  158. package/src/sandbox/compose.test.ts +26 -1
  159. package/src/sandbox/compose.ts +7 -3
  160. package/src/sandbox/docker.test.ts +66 -1
  161. package/src/sandbox/docker.ts +89 -65
  162. package/src/sandbox/dockerfile-build.ts +4 -1
  163. package/src/sandbox/e2b-agent-template.test.ts +18 -1
  164. package/src/sandbox/e2b-agent-template.ts +22 -6
  165. package/src/sandbox/e2b.test.ts +64 -0
  166. package/src/sandbox/e2b.ts +27 -5
  167. package/src/sandbox/identity.ts +7 -2
  168. package/src/sandbox/layer.test.ts +23 -4
  169. package/src/sandbox/layer.ts +127 -43
  170. package/src/sandbox/link.test.ts +5 -2
  171. package/src/sandbox/local.test.ts +8 -8
  172. package/src/sandbox/local.ts +28 -6
  173. package/src/sandbox/operations.test.ts +4 -4
  174. package/src/sandbox/operations.ts +5 -5
  175. package/src/sandbox/runtime.ts +18 -3
  176. package/src/sandbox/types.ts +14 -8
  177. package/src/sandbox/vercel.test.ts +58 -3
  178. package/src/sandbox/vercel.ts +38 -6
  179. package/src/show/index.ts +11 -30
  180. package/src/show/render.test.ts +23 -1
  181. package/src/show/render.ts +6 -3
@@ -1,4 +1,5 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { SeriesPatternDefs, seriesClassFromColorVar, seriesClassesFromFill, } from "../../assets/series-encoding.js";
2
3
  import { defineComponent } from "../tree.js";
3
4
  import { countText, localeText, resolveLocalizedText } from "../../model/locale.js";
4
5
  import { formatAxisTick, formatMetricValue, shortestUniqueLabels } from "../../model/format.js";
@@ -91,7 +92,8 @@ function chartFieldLabel(field, meta, locale) {
91
92
  const base = dictionary[field] ? localeText(locale, dictionary[field]) : field;
92
93
  return meta.unit ? `${base}(${meta.unit})` : base;
93
94
  }
94
- function seriesClass(mapped, series, point, ctx) {
95
+ /** 解析点所属 series 句柄与下标;单系列隐式图不声明视觉身份。 */
96
+ function seriesPresentationOf(mapped, series, point, ctx) {
95
97
  let handle;
96
98
  let index;
97
99
  if (series.byField !== undefined && point.seriesValue !== undefined) {
@@ -101,12 +103,104 @@ function seriesClass(mapped, series, point, ctx) {
101
103
  else {
102
104
  const ids = mapped.filter((item) => !item.hidden && item.byField === undefined).map((item) => item.id);
103
105
  if (ids.length < 2)
104
- return "niceeval-series-none";
106
+ return undefined;
105
107
  handle = IMPLICIT_SERIES_HANDLE;
106
108
  index = ids.indexOf(series.id);
107
109
  }
108
- const colorIndex = ctx.dimension(handle).at(index).colorIndex;
109
- return colorIndex === undefined ? "niceeval-series-none" : `niceeval-series-c${colorIndex - 1}`;
110
+ if (index < 0)
111
+ return undefined;
112
+ return ctx.dimension(handle).at(index);
113
+ }
114
+ /** SVG 路径:只挂 series-cN 设 --series;pattern 由 fill 属性 / style 承载。 */
115
+ function seriesColorClass(presentation) {
116
+ if (!presentation)
117
+ return "niceeval-series-none";
118
+ if (presentation.kind === "color")
119
+ return seriesClassFromColorVar(presentation.color);
120
+ if (presentation.kind !== "series")
121
+ return "niceeval-series-none";
122
+ // switch 按 mark 收窄;Fill 的 mark 是 "bar"|"area" 联合,if/|| 在部分 TS 版本下剔不干净。
123
+ switch (presentation.mark) {
124
+ case "bar":
125
+ case "area": {
126
+ // 只要色类,不要 HTML 专用的 fill-vN(SVG 用 url(#pattern))。
127
+ const classes = seriesClassesFromFill(presentation.fill).split(" ");
128
+ return classes.find((c) => c.startsWith("niceeval-series-c") || c === "niceeval-series-none") ?? "niceeval-series-none";
129
+ }
130
+ case "line":
131
+ return seriesClassFromColorVar(presentation.stroke);
132
+ case "scatter":
133
+ return seriesClassFromColorVar(presentation.marker.fill);
134
+ }
135
+ }
136
+ /** HTML 横向柱:色类 + fill-vN 图案类(CSS repeating-linear-gradient 等效 SVG pattern)。 */
137
+ function seriesHtmlBarClass(presentation) {
138
+ if (!presentation)
139
+ return "niceeval-series-none";
140
+ if (presentation.kind === "series" && (presentation.mark === "bar" || presentation.mark === "area")) {
141
+ return seriesClassesFromFill(presentation.fill);
142
+ }
143
+ return seriesColorClass(presentation);
144
+ }
145
+ /**
146
+ * 作者在 `<Series line>` 上显式声明的 dashed/dotted **优先于** 页级 variant 的 strokeDasharray。
147
+ * 未声明时消费 LineSeriesPresentation / FillSeriesPresentation 的 strokeDasharray(空串 = 实线)。
148
+ */
149
+ function resolveStrokeDasharray(series, presentation) {
150
+ if (series.line !== undefined)
151
+ return lineDash(series.line);
152
+ if (presentation?.kind === "series" && "strokeDasharray" in presentation) {
153
+ return presentation.strokeDasharray || undefined;
154
+ }
155
+ return undefined;
156
+ }
157
+ /** SVG 柱/面:pattern fill 必须用 style 压过 `.niceeval-chart-bar { fill: var(--series) }`。 */
158
+ function seriesSvgFillStyle(presentation) {
159
+ if (presentation?.kind !== "series")
160
+ return undefined;
161
+ if (presentation.mark !== "bar" && presentation.mark !== "area")
162
+ return undefined;
163
+ if (!presentation.fill.startsWith("url("))
164
+ return undefined;
165
+ return { fill: presentation.fill };
166
+ }
167
+ function renderMarkerShape(presentation, px, py, colorClass, title) {
168
+ const marker = presentation?.kind === "series" && (presentation.mark === "scatter" || presentation.mark === "line")
169
+ ? presentation.marker
170
+ : undefined;
171
+ if (!marker) {
172
+ return (_jsx("circle", { className: cx("niceeval-chart-dot", colorClass), cx: px, cy: py, r: 4.5, children: title }));
173
+ }
174
+ // path 在 0..12 viewBox;缩放到 ~9px 并居中到 (px, py)。
175
+ return (_jsx("path", { className: cx("niceeval-chart-dot", colorClass), d: marker.path, transform: `translate(${px} ${py}) scale(0.75) translate(-6 -6)`, fill: marker.fill, stroke: "var(--panel)", strokeWidth: 1.6, children: title }));
176
+ }
177
+ function legendSwatch(presentation, mark) {
178
+ if (!presentation || presentation.kind !== "series") {
179
+ return _jsx("span", { className: cx("niceeval-chart-legend-swatch", "niceeval-series-none") });
180
+ }
181
+ // 图例方块跟 series mark 取义:柱/面用填充图案,线用 dash+marker,散点用 marker 形状。
182
+ if (mark === "bar" || mark === "area") {
183
+ const fillClass = presentation.mark === "bar" || presentation.mark === "area"
184
+ ? seriesClassesFromFill(presentation.fill)
185
+ : seriesColorClass(presentation);
186
+ return _jsx("span", { className: cx("niceeval-chart-legend-swatch", fillClass) });
187
+ }
188
+ if (mark === "line") {
189
+ // 线系图例需要 stroke + marker;presentation 可能是 line 或 scatter(by 切分后 mark 对齐)。
190
+ if (presentation.mark === "line") {
191
+ return (_jsxs("svg", { className: "niceeval-chart-legend-swatch-svg", width: "16", height: "10", "aria-hidden": "true", children: [_jsx("line", { x1: "0", y1: "5", x2: "16", y2: "5", stroke: presentation.stroke, strokeWidth: 2, strokeDasharray: presentation.strokeDasharray || undefined }), _jsx("path", { d: presentation.marker.path, transform: "translate(8 5) scale(0.55) translate(-6 -6)", fill: presentation.marker.fill })] }));
192
+ }
193
+ if (presentation.mark === "scatter") {
194
+ return (_jsxs("svg", { className: "niceeval-chart-legend-swatch-svg", width: "16", height: "10", "aria-hidden": "true", children: [_jsx("line", { x1: "0", y1: "5", x2: "16", y2: "5", stroke: presentation.marker.fill, strokeWidth: 2 }), _jsx("path", { d: presentation.marker.path, transform: "translate(8 5) scale(0.55) translate(-6 -6)", fill: presentation.marker.fill })] }));
195
+ }
196
+ // fill presentation 配 line mark 的退化:只画色块。
197
+ return _jsx("span", { className: cx("niceeval-chart-legend-swatch", seriesColorClass(presentation)) });
198
+ }
199
+ // mark === "scatter"
200
+ if (presentation.mark === "scatter" || presentation.mark === "line") {
201
+ return (_jsx("svg", { className: "niceeval-chart-legend-swatch-svg", width: "10", height: "10", "aria-hidden": "true", children: _jsx("path", { d: presentation.marker.path, transform: "translate(5 5) scale(0.7) translate(-6 -6)", fill: presentation.marker.fill }) }));
202
+ }
203
+ return _jsx("span", { className: cx("niceeval-chart-legend-swatch", seriesColorClass(presentation)) });
110
204
  }
111
205
  function metricDisplay(point, axis, meta, locale) {
112
206
  if (meta.kind === "dimension") {
@@ -129,10 +223,7 @@ function renderLegend(mapped, visible, locale, ctx) {
129
223
  : implicitIds.indexOf(series.id);
130
224
  const presentation = handle ? ctx.dimension(handle).at(index) : undefined;
131
225
  const label = presentation?.label ?? (series.label ? resolveLocalizedText(series.label, locale) : series.id);
132
- const colorClass = presentation?.colorIndex === undefined
133
- ? "niceeval-series-none"
134
- : `niceeval-series-c${presentation.colorIndex - 1}`;
135
- return (_jsx("li", { className: cx("niceeval-chart-legend-item", colorClass), children: label }, `${series.id}:${value}`));
226
+ return (_jsxs("li", { className: "niceeval-chart-legend-item", children: [legendSwatch(presentation, series.mark), label] }, `${series.id}:${value}`));
136
227
  });
137
228
  }) }));
138
229
  }
@@ -142,12 +233,13 @@ function renderHorizontalBarsWeb(mapped, visible, axes, locale, ctx, options) {
142
233
  const values = entries.map(({ point }) => point.y);
143
234
  const boundMax = axes.yMeta.kind === "metric" ? axes.yMeta.bounds?.max : undefined;
144
235
  const max = boundMax !== undefined && boundMax > 0 ? boundMax : Math.max(0, ...values);
145
- return (_jsxs("figure", { className: cx("niceeval-report", "niceeval-chart", "niceeval-chart--bars-horizontal", options.className), children: [_jsx("div", { className: "niceeval-chart-bars-heading", children: chartFieldLabel(axes.yField, axes.yMeta, locale) }), _jsx("ol", { className: "niceeval-chart-bars", children: entries.map(({ series, point }) => {
236
+ return (_jsxs("figure", { className: cx("niceeval-report", "niceeval-chart", "niceeval-chart--bars-horizontal", options.className), children: [_jsx(SeriesPatternDefs, {}), _jsx("div", { className: "niceeval-chart-bars-heading", children: chartFieldLabel(axes.yField, axes.yMeta, locale) }), _jsx("ol", { className: "niceeval-chart-bars", children: entries.map(({ series, point }) => {
146
237
  const rawLabel = point.xLabel ?? point.pointLabel;
147
238
  const label = labels.get(rawLabel) ?? rawLabel;
148
239
  const display = metricDisplay(point, "y", axes.yMeta, locale);
149
240
  const href = pointHref(point, ctx, options.pointTarget);
150
- const colorClass = seriesClass(mapped, series, point, ctx);
241
+ const presentation = seriesPresentationOf(mapped, series, point, ctx);
242
+ const colorClass = seriesHtmlBarClass(presentation);
151
243
  const ratio = max > 0 ? Math.max(0, Math.min(1, point.y / max)) : 0;
152
244
  const value = (_jsxs("span", { className: "niceeval-chart-bar-value", children: [display, point.yCell && point.yCell.samples < point.yCell.total ? (_jsxs("sup", { children: [point.yCell.samples, "/", point.yCell.total] })) : null] }));
153
245
  return (_jsxs("li", { className: "niceeval-chart-bar-row", children: [_jsx("span", { className: "niceeval-chart-bar-label", title: rawLabel, children: label }), _jsx("span", { className: "niceeval-chart-bar-track", children: _jsx("span", { className: cx("niceeval-chart-bar-fill", colorClass), style: { width: `${ratio * 100}%` }, title: `${rawLabel}\n${axes.yField}: ${display}` }) }), href ? _jsx("a", { className: "niceeval-locator", href: href, children: value }) : value] }, `${series.id}:${point.key}`));
@@ -203,16 +295,20 @@ function renderChartWeb(mapped, axes, locale, ctx, options) {
203
295
  const labelByKey = shortestUniqueLabels(allPoints.map((p) => p.pointLabel));
204
296
  const xLabel = chartFieldLabel(axes.xField, axes.xMeta, locale);
205
297
  const yLabel = chartFieldLabel(axes.yField, axes.yMeta, locale);
206
- const drawable = visible.flatMap((series) => series.points.map((point) => ({
207
- ...point,
208
- sourceSeriesId: series.id,
209
- label: labelByKey.get(point.pointLabel) ?? point.pointLabel,
210
- px: xScale.scale(point.x),
211
- py: yScale.scale(point.y),
212
- seriesClass: seriesClass(mapped, series, point, ctx),
213
- })));
298
+ const drawable = visible.flatMap((series) => series.points.map((point) => {
299
+ const presentation = seriesPresentationOf(mapped, series, point, ctx);
300
+ return {
301
+ ...point,
302
+ sourceSeriesId: series.id,
303
+ label: labelByKey.get(point.pointLabel) ?? point.pointLabel,
304
+ px: xScale.scale(point.x),
305
+ py: yScale.scale(point.y),
306
+ presentation,
307
+ seriesClass: seriesColorClass(presentation),
308
+ };
309
+ }));
214
310
  const labels = placePointLabels(drawable.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 });
215
- return (_jsxs("figure", { className: cx("niceeval-report", "niceeval-chart", "niceeval-chart--scatter", options.className), children: [_jsxs("svg", { className: "niceeval-chart-svg", viewBox: `0 0 ${WIDTH} ${HEIGHT}`, role: "img", "aria-label": `${axes.xField} × ${axes.yField}`, children: [options.grid !== false ? (_jsxs("g", { className: "niceeval-chart-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}`)))] })) : null, _jsx("g", { className: "niceeval-chart-axis niceeval-chart-axis-y", children: (axes.yMeta.kind === "dimension"
311
+ return (_jsxs("figure", { className: cx("niceeval-report", "niceeval-chart", "niceeval-chart--scatter", options.className), children: [_jsx(SeriesPatternDefs, {}), _jsxs("svg", { className: "niceeval-chart-svg", viewBox: `0 0 ${WIDTH} ${HEIGHT}`, role: "img", "aria-label": `${axes.xField} × ${axes.yField}`, children: [options.grid !== false ? (_jsxs("g", { className: "niceeval-chart-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}`)))] })) : null, _jsx("g", { className: "niceeval-chart-axis niceeval-chart-axis-y", children: (axes.yMeta.kind === "dimension"
216
312
  ? [...new Set(drawable.map((point) => point.y))]
217
313
  : yScale.ticks).map((tick) => (_jsx("text", { className: "niceeval-chart-tick", x: MARGIN.left - 8, y: yScale.scale(tick) + 3, textAnchor: "end", children: axes.yMeta.kind === "dimension"
218
314
  ? drawable.find((point) => point.y === tick)?.yLabel
@@ -226,7 +322,8 @@ function renderChartWeb(mapped, axes, locale, ctx, options) {
226
322
  const seriesPoints = drawable.filter((p) => p.sourceSeriesId === series.id &&
227
323
  (series.byField === undefined || p.seriesValue === value));
228
324
  const ordered = series.connect ? [...seriesPoints].sort((a, b) => a.x - b.x) : seriesPoints;
229
- const seriesClass = ordered[0]?.seriesClass ?? "niceeval-series-none";
325
+ const seriesClassName = ordered[0]?.seriesClass ?? "niceeval-series-none";
326
+ const seriesPresentation = ordered[0]?.presentation;
230
327
  const baseline = yScale.scale(0);
231
328
  const barGroups = [...new Set(visible
232
329
  .filter((item) => item.mark === "bar")
@@ -235,11 +332,13 @@ function renderChartWeb(mapped, axes, locale, ctx, options) {
235
332
  const groupIndex = Math.max(0, barGroups.indexOf(barGroup));
236
333
  const totalBarWidth = Math.max(8, Math.min(48, PLOT_W / Math.max(1, allPoints.length)));
237
334
  const barWidth = totalBarWidth / Math.max(1, barGroups.length);
238
- return (_jsxs("g", { className: cx("niceeval-chart-series", seriesClass), "data-series": `${series.id}:${value}`, children: [series.mark === "area" && ordered.length > 1 ? (_jsx("polygon", { className: "niceeval-chart-area", points: [
335
+ const dash = resolveStrokeDasharray(series, seriesPresentation);
336
+ const areaFillStyle = seriesSvgFillStyle(seriesPresentation);
337
+ return (_jsxs("g", { className: cx("niceeval-chart-series", seriesClassName), "data-series": `${series.id}:${value}`, children: [series.mark === "area" && ordered.length > 1 ? (_jsx("polygon", { className: "niceeval-chart-area", style: areaFillStyle, points: [
239
338
  `${ordered[0].px},${baseline}`,
240
339
  ...ordered.map((point) => `${point.px},${point.py}`),
241
340
  `${ordered[ordered.length - 1].px},${baseline}`,
242
- ].join(" ") })) : null, (series.mark === "line" || series.mark === "area" || series.connect) && ordered.length > 1 ? (_jsx("polyline", { className: "niceeval-chart-line", points: ordered.map((p) => `${p.px},${p.py}`).join(" "), strokeDasharray: lineDash(series.line) })) : null, ordered.map((p) => {
341
+ ].join(" ") })) : null, (series.mark === "line" || series.mark === "area" || series.connect) && ordered.length > 1 ? (_jsx("polyline", { className: "niceeval-chart-line", points: ordered.map((p) => `${p.px},${p.py}`).join(" "), strokeDasharray: dash })) : null, ordered.map((p) => {
243
342
  const placed = labels[drawable.indexOf(p)];
244
343
  const href = pointHref(p, ctx, options.pointTarget);
245
344
  let shape;
@@ -247,10 +346,10 @@ function renderChartWeb(mapped, axes, locale, ctx, options) {
247
346
  const baseValue = stackedBarBase(visible, series, p.x);
248
347
  const baseY = yScale.scale(baseValue);
249
348
  const topY = yScale.scale(baseValue + p.y);
250
- shape = (_jsx("rect", { className: cx("niceeval-chart-bar", p.seriesClass), x: p.px - totalBarWidth / 2 + groupIndex * barWidth, y: Math.min(topY, baseY), width: barWidth, height: Math.max(1, Math.abs(baseY - topY)), children: _jsx("title", { children: `${p.pointLabel}\n${series.id}: ${metricDisplay(p, "y", axes.yMeta, locale)}` }) }));
349
+ shape = (_jsx("rect", { className: cx("niceeval-chart-bar", p.seriesClass), style: seriesSvgFillStyle(p.presentation), x: p.px - totalBarWidth / 2 + groupIndex * barWidth, y: Math.min(topY, baseY), width: barWidth, height: Math.max(1, Math.abs(baseY - topY)), children: _jsx("title", { children: `${p.pointLabel}\n${series.id}: ${metricDisplay(p, "y", axes.yMeta, locale)}` }) }));
251
350
  }
252
351
  else {
253
- shape = (_jsx("circle", { className: cx("niceeval-chart-dot", p.seriesClass), cx: p.px, cy: p.py, r: 4.5, children: _jsx("title", { children: `${p.pointLabel}\n${axes.xField}: ${metricDisplay(p, "x", axes.xMeta, locale)}\n${axes.yField}: ${metricDisplay(p, "y", axes.yMeta, locale)}` }) }));
352
+ shape = renderMarkerShape(p.presentation, p.px, p.py, p.seriesClass, _jsx("title", { children: `${p.pointLabel}\n${axes.xField}: ${metricDisplay(p, "x", axes.xMeta, locale)}\n${axes.yField}: ${metricDisplay(p, "y", axes.yMeta, locale)}` }));
254
353
  }
255
354
  return (_jsxs("g", { className: "niceeval-chart-point", children: [href ? _jsx("a", { href: href, children: shape }) : shape, series.mark === "scatter" && placed ? (_jsx("text", { className: "niceeval-chart-point-label", x: placed.x, y: placed.y, textAnchor: placed.anchor, children: p.label })) : null] }, p.key));
256
355
  })] }, `${series.id}:${value}`));
@@ -480,7 +579,8 @@ export const Chart = defineComponent({
480
579
  continue;
481
580
  decls[spec.by] = {
482
581
  dimension: spec.by,
483
- encoding: { kind: "series", mark: spec.mark === "area" ? "line" : spec.mark },
582
+ // area 是 FillSeriesPresentation(填充图案),不再折叠成 line。
583
+ encoding: { kind: "series", mark: spec.mark },
484
584
  values,
485
585
  };
486
586
  }
@@ -22,7 +22,7 @@ export interface CommandEvidenceItem {
22
22
  phase: string;
23
23
  display: string;
24
24
  exitCode: number;
25
- classification: "observed" | "failed";
25
+ classification: "succeeded" | "observed" | "failed";
26
26
  durationMs?: number;
27
27
  stdout?: string;
28
28
  stderr?: string;
@@ -110,7 +110,9 @@ function TurnCard({ turn, locale }) {
110
110
  return (_jsxs("article", { className: cx("niceeval-conversation-turn", turn.verdict && `niceeval-conversation-turn--${turn.verdict}`), children: [_jsxs("header", { className: "niceeval-conversation-turn-head", children: [_jsx("span", { className: "niceeval-conversation-turn-label", children: resolveLocalizedText(turn.label, locale) }), turn.verdict ? _jsx("span", { className: "niceeval-conversation-turn-verdict", children: turn.verdict }) : null] }), _jsx(ConversationEntries, { entries: turn.entries, locale: locale })] }));
111
111
  }
112
112
  function commandEvidenceTitle(command) {
113
- const title = command.classification === "failed" ? "FAILED COMMAND" : "NON-ZERO COMMAND · observed";
113
+ const title = command.classification === "succeeded" ? "COMMAND"
114
+ : command.classification === "failed" ? "FAILED COMMAND"
115
+ : "NON-ZERO COMMAND · observed";
114
116
  return `${title} · ${command.phase} · exit ${command.exitCode}${command.durationMs === undefined ? "" : ` · ${command.durationMs}ms`}`;
115
117
  }
116
118
  function CommandEvidenceCard({ command }) {
@@ -147,8 +149,8 @@ function assertCommandEvidenceContent(data) {
147
149
  throw dataShapeError("CommandEvidence", "commandEvidenceData", "CommandEvidenceContent", `"data.commands[${index}].display" must be a string`);
148
150
  if (typeof command.exitCode !== "number")
149
151
  throw dataShapeError("CommandEvidence", "commandEvidenceData", "CommandEvidenceContent", `"data.commands[${index}].exitCode" must be a number`);
150
- if (command.classification !== "observed" && command.classification !== "failed") {
151
- throw dataShapeError("CommandEvidence", "commandEvidenceData", "CommandEvidenceContent", `"data.commands[${index}].classification" must be observed | failed`);
152
+ if (command.classification !== "succeeded" && command.classification !== "observed" && command.classification !== "failed") {
153
+ throw dataShapeError("CommandEvidence", "commandEvidenceData", "CommandEvidenceContent", `"data.commands[${index}].classification" must be succeeded | observed | failed`);
152
154
  }
153
155
  if (command.durationMs !== undefined && typeof command.durationMs !== "number")
154
156
  throw dataShapeError("CommandEvidence", "commandEvidenceData", "CommandEvidenceContent", `"data.commands[${index}].durationMs" must be a number`);
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { COMPONENT_RAW_CHILDREN, COMPONENT_ROLE, defineComponent, } from "./tree.js";
3
3
  import { hrefForLocator } from "../components/shared.js";
4
4
  import { localeText, resolveLocalizedText } from "../model/locale.js";
@@ -10,7 +10,7 @@ import { datasetToTableContent, isDataset } from "../model/dataset.js";
10
10
  import { isMetricValue } from "../model/calculation.js";
11
11
  import { flattenTableContentForText, formatCellText, } from "./cell.js";
12
12
  import { MetricCellView } from "../components/cell.js";
13
- import { verdictMark } from "../model/format.js";
13
+ import { formatTimeDistance, missingText, verdictMark } from "../model/format.js";
14
14
  function childArray(children) {
15
15
  if (children === null || children === undefined || typeof children === "boolean")
16
16
  return [];
@@ -548,18 +548,34 @@ function renderCellWeb(cell, ctx) {
548
548
  switch (cell.kind) {
549
549
  case "notApplicable":
550
550
  return _jsx("span", { className: "niceeval-missing", children: MISSING_MARK });
551
- case "missing":
552
- return _jsx("span", { className: "niceeval-missing", children: formatCellText(cell, ctx.locale) });
551
+ case "missing": {
552
+ const reference = cell.reference;
553
+ let body;
554
+ if (reference) {
555
+ const mark = (_jsx("span", { className: "niceeval-locator-mark", "aria-hidden": "true", children: verdictMark(reference.verdict === "skipped" ? "skipped" : reference.verdict) }));
556
+ const distance = _jsx("span", { className: "niceeval-stale-distance", children: formatTimeDistance(reference.staleSinceMs, ctx.locale) });
557
+ const title = localeText(ctx.locale, "experimentList.staleReferenceTooltip");
558
+ const href = ctx.href(reference.locator);
559
+ body = href !== undefined ? (_jsxs("a", { className: "niceeval-locator niceeval-stale", href: href, title: title, children: [mark, reference.locator, distance] })) : (_jsxs("span", { className: "niceeval-locator niceeval-stale", title: title, children: [mark, reference.locator, distance] }));
560
+ }
561
+ else {
562
+ body = _jsx("span", { className: "niceeval-missing-reason", children: missingText(cell.code, ctx.locale) });
563
+ }
564
+ return (_jsxs("span", { className: "niceeval-missing", children: [body, cell.detail ? _jsx("small", { className: "niceeval-cell-detail", children: cell.detail }) : null] }));
565
+ }
553
566
  case "text":
554
567
  return (_jsxs("span", { className: "niceeval-cell-text", children: [_jsx("span", { children: cell.text }), cell.detail ? _jsx("small", { className: "niceeval-cell-detail", children: cell.detail }) : null] }));
555
568
  case "locator": {
556
569
  // 判定长在 locator 上:判定符与语义色同场,不靠颜色单独表意
557
570
  // (docs/feature/reports/components/summaries/experiment-table.md)。
558
571
  const verdict = cell.verdict;
559
- const className = verdict === undefined ? "niceeval-locator" : `niceeval-locator niceeval-verdict-${verdict}`;
572
+ const stale = cell.staleSinceMs !== undefined;
573
+ const className = cx("niceeval-locator", verdict !== undefined ? `niceeval-verdict-${verdict}` : undefined, stale ? "niceeval-stale" : undefined);
560
574
  const mark = verdict === undefined ? null : (_jsx("span", { className: "niceeval-locator-mark", "aria-hidden": "true", children: verdictMark(verdict === "skipped" ? "skipped" : verdict) }));
575
+ const distance = stale ? (_jsx("span", { className: "niceeval-stale-distance", children: formatTimeDistance(cell.staleSinceMs, ctx.locale) })) : null;
576
+ const title = stale ? localeText(ctx.locale, "experimentList.historicalTooltip") : undefined;
561
577
  const href = ctx.href(cell.locator);
562
- return href !== undefined ? (_jsxs("a", { className: className, href: href, children: [mark, cell.locator] })) : (_jsx("span", { className: className, children: formatCellText(cell, ctx.locale) }));
578
+ return href !== undefined ? (_jsxs("a", { className: className, href: href, title: title, children: [mark, cell.locator, distance] })) : (_jsxs("span", { className: className, title: title, children: [mark, cell.locator, distance] }));
563
579
  }
564
580
  case "summary":
565
581
  case "score":
@@ -570,11 +586,20 @@ function renderCellWeb(cell, ctx) {
570
586
  return (_jsxs("span", { className: "niceeval-verdict-tally", children: [parts.map((kind) => (_jsxs("span", { className: `niceeval-verdict-${kind}`, children: [cell.counts[kind], " ", localeText(ctx.locale, `verdict.${kind === "skipped" ? "skipped" : kind}`)] }, kind))), parts.length === 0 ? _jsx("span", { className: "niceeval-missing", children: MISSING_MARK }) : null] }));
571
587
  }
572
588
  const verdict = cell.verdict ?? "skipped";
589
+ const stale = cell.staleSinceMs !== undefined;
573
590
  // 判定符走 verdictMark 单源,与 locator 格同一张表(errored 是 `!`,不并到 `✗`)。
574
- return (_jsxs("span", { className: `niceeval-verdict niceeval-verdict-${verdict}`, children: [verdictMark(verdict === "skipped" ? "skipped" : verdict), " ", localeText(ctx.locale, `verdict.${verdict === "skipped" ? "skipped" : verdict}`)] }));
591
+ return (_jsxs("span", { className: cx("niceeval-verdict", `niceeval-verdict-${verdict}`, stale ? "niceeval-stale" : undefined), title: stale ? localeText(ctx.locale, "experimentList.historicalTooltip") : undefined, children: [verdictMark(verdict === "skipped" ? "skipped" : verdict), !cell.bare ? _jsxs(_Fragment, { children: [" ", localeText(ctx.locale, `verdict.${verdict === "skipped" ? "skipped" : verdict}`)] }) : null, stale ? _jsx("span", { className: "niceeval-stale-distance", children: formatTimeDistance(cell.staleSinceMs, ctx.locale) }) : null] }));
575
592
  }
576
593
  case "metric":
577
594
  return (_jsx(MetricCellView, { cell: cell.metric, href: ctx.showMeasureRefs === false ? undefined : ctx.href, locale: ctx.locale }));
595
+ case "composition": {
596
+ // web 面画成一条分段条,hover 一段给出它的名字与计数;段的业务含义住在投影里,
597
+ // 这里只按声明序给一条位置驱动的渐隐色阶(docs/feature/reports/components/primitives/table.md「构成格」)。
598
+ const total = cell.segments.reduce((sum, segment) => sum + segment.count, 0);
599
+ if (total === 0)
600
+ return _jsx("span", { className: "niceeval-missing", children: MISSING_MARK });
601
+ return (_jsx("span", { className: "niceeval-composition", children: cell.segments.map((segment, i) => segment.count > 0 ? (_jsx("span", { className: "niceeval-composition-segment", style: { width: `${(segment.count / total) * 100}%`, "--composition-step": i }, title: `${resolveLocalizedText(segment.label, ctx.locale)}: ${segment.count}` }, i)) : null) }));
602
+ }
578
603
  default: {
579
604
  const _e = cell;
580
605
  return _e;
@@ -92,8 +92,9 @@ export interface WebContext {
92
92
  /** chrome 文案的 locale;官方组件渲染面经上下文读取,宿主外默认 "en"。 */
93
93
  locale: ReportLocale;
94
94
  /**
95
- * 取本组件 `dimensions()` 声明的某个句柄在这一页的呈现面(含 `seriesSlot` / 色板下标 /
96
- * 形状变体)。查别的组件的句柄或没声明的句柄抛 `UndeclaredDimensionValueError`。
95
+ * 取本组件 `dimensions()` 声明的某个句柄在这一页的呈现面(label / color / series 的
96
+ * fill·stroke·marker 等可直接使用的值)。查别的组件的句柄或没声明的句柄抛
97
+ * `UndeclaredDimensionValueError`。
97
98
  */
98
99
  dimension(handle: string): PresentedDimension;
99
100
  }
@@ -152,6 +153,8 @@ export type ReportComponent<P extends object> = ((props: P) => ReactNode) & {
152
153
  */
153
154
  export interface PageDimensions {
154
155
  dimension(props: object, handle: string): PresentedDimension;
156
+ /** 维度 name → 值 → seriesSlot;测试与调试观察槽位分配,组件渲染不读槽号。 */
157
+ readonly slotsByDimension: ReadonlyMap<string, ReadonlyMap<string, number>>;
155
158
  }
156
159
  /** 宿主渲染前把这一页的分配结果挂到渲染上下文上(text 与 web 同一条通道)。 */
157
160
  export declare function withPageDimensions<C extends object>(ctx: C, plan: PageDimensions | undefined): C;
@@ -142,6 +142,7 @@ export function collectPageDimensions(node, pins = {}, face = "web") {
142
142
  visit(node);
143
143
  const plan = allocatePageDimensions(handles, pins, { face });
144
144
  return {
145
+ slotsByDimension: plan.slotsByDimension,
145
146
  dimension(props, handle) {
146
147
  const entry = byProps.get(props);
147
148
  const key = entry?.keys.get(handle);
@@ -1,8 +1,8 @@
1
1
  export { aggregate, agent, costUSD, dedupeLocators, durationMs, evalId, evidenceRow, experiment, isCalculation, isMetricValue, max, mean, metricValue, min, model, parseEvidenceRow, parseEvidenceRows, passRate, percentile, rollup, sum, tokens, totalScore, } from "./model/calculation.ts";
2
2
  export type { AggregateRow, AggregationSubject, Calculation, EvidenceRow, GroupFunction, MetricBasis, MetricFormat, MetricValue, Reducer, RollupOptions, } from "./model/calculation.ts";
3
- export { toAttemptAssertions, toAttemptFixPrompt, toAttemptListRows, toAttemptNotices, toAttemptRows, toAttemptSource, toAttemptSummary, toAttemptUsage, toConversationTurns, toDiffFiles, toEvalRows, toExperimentDetails, toExperimentRows, toHeroData, toRunNotices, toSampleFixPrompt, toSampleNotices, toSummaryItems, toTimelineNodes, toTraceNodes, } from "./model/conversions.ts";
3
+ export { toAttemptAssertions, toAttemptFacts, toAttemptFixPrompt, toAttemptListRows, toAttemptNotices, toAttemptRows, toAttemptSource, toAttemptSummary, toAttemptUsage, toConversationTurns, toDiffFiles, toEvalRows, toExperimentDetails, toExperimentRows, toHeroData, toRunNotices, toSampleFixPrompt, toSampleNotices, toSummaryItems, toTimelineNodes, toTraceNodes, } from "./model/conversions.ts";
4
4
  export { presentDimension, shortestUniqueLabels } from "./presentation.ts";
5
- export type { DimensionDeclaration, DimensionEncoding, PresentedDimension } from "./presentation.ts";
5
+ export type { ColorPresentation, DimensionDeclaration, DimensionEncoding, DimensionPresentation, FillSeriesPresentation, LabelPresentation, LineSeriesPresentation, PresentedDimension, ScatterSeriesPresentation, } from "./presentation.ts";
6
6
  export { flag, label, numericFlag, numericLabel, numericRunConfig, runConfig } from "./model/flag.ts";
7
7
  export { evaluationKindComposition } from "./model/evaluation-kind.ts";
8
8
  export { annotatedSourceResult, attemptDetailsResult, comparisonResult, conversationResult, diffResult, historyResult, stabilityResult, standardOverviewResult, timingResult, usageResult, } from "./tasks.ts";
@@ -22,7 +22,7 @@ export { Area, Bars, Callouts, Chart, Col, CommandEvidence, Conversation, CopyBl
22
22
  export type { AreaProps, BarsProps, BarsSort, EvidenceAxisKey, EvidenceDimensionKey, CalloutGroup, CalloutItem, CalloutLevel, CalloutsProps, ChartPresentation, ChartProps, ChartAxisBinding, ChartFieldBinding, ChartSeriesOverride, ColProps, ConversationContent, ConversationEntry, ConversationProps, ConversationTurn, CopyBlockContent, CopyBlockProps, DiffChange, DiffContent, DiffFile, DiffFileWindow, DiffViewProps, ExternalAreaProps, ExternalAxisKey, ExternalBarsProps, ExternalLineProps, ExternalScatterProps, CommandEvidenceContent, CommandEvidenceItem, CommandEvidenceProps, GridProps, LayoutProps, LineProps, MarkdownProps, PlainTableColumn, RowProps, ScatterProps, SectionProps, SeriesProps, SourceBlockContent, SourceCallContent, SourceContent, SourceLine, SourceViewProps, StatProps, StatTone, StyleProps, TabProps, TableProps, TabsProps, TextProps, WaterfallContent, WaterfallNode, WaterfallProps, WaterfallRow, } from "./definition/primitives.tsx";
23
23
  export type { Cell, VerdictCounts } from "./definition/cell.ts";
24
24
  export { formatCellText } from "./definition/cell.ts";
25
- export { formatAxisTick, formatInstant, formatMetricValue, missingText } from "./model/format.ts";
25
+ export { formatAxisTick, formatInstant, formatMetricValue, formatTimeDistance, missingText } from "./model/format.ts";
26
26
  export { stringWidth, padDisplay as padEnd, padStartDisplay as padStart, wrapDisplay as wrapText, indentBlock as indent, textBar as bar, joinColumns as columns, } from "./model/text-layout.ts";
27
27
  export type { ColumnAlign } from "./model/text-layout.ts";
28
28
  export { DEFAULT_REPORT_LOCALE, localizedTextEquals, resolveLocalizedText, resolveMetricLabel, } from "./model/locale.ts";
@@ -38,6 +38,6 @@ export { AttemptAssessment, AttemptDetails, AttemptSummary, } from "./components
38
38
  export type { AttemptDetailsProps } from "./components/attempt-detail/index.tsx";
39
39
  export { ExperimentDetails } from "./components/experiment-detail/index.tsx";
40
40
  export type { ExperimentDetailsProps } from "./components/experiment-detail/index.tsx";
41
- export type { Aggregator, AttemptListItem, AttemptLocator, BuiltInDimension, CopyFixPromptData, CustomDimension, DeltaCell, DeltaData, DimensionInput, DimensionOptions, DimensionRef, EvalListItem, ExperimentDetailsData, ExperimentListEvalRow, ExperimentListItem, FlagConditions, HeroData, NumericAxis, NumericAxisOptions, NumericRunConfigAxisOptions, ReportInput, RunConfigKey, SampleSummaryContent, SampleIssue, EvaluationKindComposition, SeriesInput, SnapshotDiagnosticsData, SnapshotDiagnosticsItem, TraceSpanSummary, TraceWaterfallRow, VerdictTally, } from "./model/types.ts";
42
- export type { AttemptAssertionsData, AttemptConversationData, AttemptConversationReply, AttemptConversationRound, AttemptDiagnosticsData, AttemptDiffData, AttemptErrorData, AttemptFixPromptData, AttemptSummaryData, AttemptTimelineData, AttemptTraceData, UsageTableData, } from "./model/types.ts";
41
+ export type { Aggregator, AttemptListItem, AttemptLocator, BuiltInDimension, CopyFixPromptData, CustomDimension, DeltaCell, DeltaData, DimensionInput, DimensionOptions, DimensionRef, EvalListItem, ExperimentDetailsData, ExperimentListEvalRow, ExperimentListItem, FlagConditions, HeroData, NumericAxis, NumericAxisOptions, NumericRunConfigAxisOptions, ReportInput, RunConfigKey, SampleSummaryContent, SampleIssue, EvaluationKindComposition, SeriesInput, SnapshotDiagnosticsData, SnapshotDiagnosticsItem, StaleConclusionReference, TraceSpanSummary, TraceWaterfallRow, VerdictTally, } from "./model/types.ts";
42
+ export type { AttemptAssertionsData, AttemptConversationData, AttemptConversationReply, AttemptConversationRound, AttemptDiagnosticsData, AttemptDiffData, AttemptErrorData, AttemptFactsData, AttemptFixPromptData, AttemptSummaryData, AttemptTimelineData, AttemptTraceData, UsageTableData, } from "./model/types.ts";
43
43
  export type { AttemptHandle, Record, Sample, Run } from "../record/types.ts";
@@ -8,7 +8,7 @@
8
8
  // 官方 Calculation(普通值作者模型);内部切片仍可用 AttemptMetric 字面量,不导出 defineMeasure。
9
9
  // 普通值计算内核
10
10
  export { aggregate, agent, costUSD, dedupeLocators, durationMs, evalId, evidenceRow, experiment, isCalculation, isMetricValue, max, mean, metricValue, min, model, parseEvidenceRow, parseEvidenceRows, passRate, percentile, rollup, sum, tokens, totalScore, } from "./model/calculation.js";
11
- export { toAttemptAssertions, toAttemptFixPrompt, toAttemptListRows, toAttemptNotices, toAttemptRows, toAttemptSource, toAttemptSummary, toAttemptUsage, toConversationTurns, toDiffFiles, toEvalRows, toExperimentDetails, toExperimentRows, toHeroData, toRunNotices, toSampleFixPrompt, toSampleNotices, toSummaryItems, toTimelineNodes, toTraceNodes, } from "./model/conversions.js";
11
+ export { toAttemptAssertions, toAttemptFacts, toAttemptFixPrompt, toAttemptListRows, toAttemptNotices, toAttemptRows, toAttemptSource, toAttemptSummary, toAttemptUsage, toConversationTurns, toDiffFiles, toEvalRows, toExperimentDetails, toExperimentRows, toHeroData, toRunNotices, toSampleFixPrompt, toSampleNotices, toSummaryItems, toTimelineNodes, toTraceNodes, } from "./model/conversions.js";
12
12
  export { presentDimension, shortestUniqueLabels } from "./presentation.js";
13
13
  export { flag, label, numericFlag, numericLabel, numericRunConfig, runConfig } from "./model/flag.js";
14
14
  export { evaluationKindComposition } from "./model/evaluation-kind.js";
@@ -24,7 +24,7 @@ export { defineComponent, createTextContext, renderNodeToText, resolveReportTree
24
24
  export { Area, Bars, Callouts, Chart, Col, CommandEvidence, Conversation, CopyBlock, DiffView, Grid, Line, Markdown, Row, Scatter, Section, Series, SourceView, Stat, Style, Tab, Table, Tabs, Text, Waterfall, applyBarsSortLimit, } from "./definition/primitives.js";
25
25
  export { formatCellText } from "./definition/cell.js";
26
26
  // 格式化与呈现工具箱(docs/feature/reports/library/presentation.md):渲染侧单点入口。
27
- export { formatAxisTick, formatInstant, formatMetricValue, missingText } from "./model/format.js";
27
+ export { formatAxisTick, formatInstant, formatMetricValue, formatTimeDistance, missingText } from "./model/format.js";
28
28
  // 文本排版工具箱:自定义组件的 text 面用的就是官方组件那把尺子。
29
29
  // 表格有 <Table> 承担,这里只给表以外的形态用 —— 尤其别拿 String.prototype.padEnd 对齐:
30
30
  // 它数 UTF-16 码元不数显示列宽,一带中文列就撕歪。renderAlignedRows 刻意不导出。
@@ -1,6 +1,6 @@
1
1
  import type { AttemptHandle, SampleCoverage, SampleIssue, Run } from "../../record/types.ts";
2
2
  import { type AttemptLocator } from "../../record/locator.ts";
3
- import type { Aggregator, DimensionInput, AttemptMetric, MetricValue, MetricColumn, NumericAxis, ReportInput, SeriesInput } from "./types.ts";
3
+ import type { Aggregator, DimensionInput, AttemptMetric, MetricValue, MetricColumn, NumericAxis, ReportInput, SeriesInput, StaleConclusionReference } from "./types.ts";
4
4
  import { type LocalizedText } from "./locale.ts";
5
5
  import { evalPrefixPredicate } from "../../shared/aggregate.ts";
6
6
  import type { JsonValue } from "../../shared/types.ts";
@@ -15,6 +15,10 @@ export declare function resolveInput(input: ReportInput): {
15
15
  attempts: readonly AttemptHandle[];
16
16
  issues: readonly SampleIssue[];
17
17
  coverage: readonly SampleCoverage[];
18
+ /** 同一总体内按 locator 去重的完整历史 attempt;裸 `Run[]` 没有挑选过程,取全部。 */
19
+ historyAttempts: readonly AttemptHandle[];
20
+ /** 是否只保留新执行的 attempt;裸 `Run[]` 没有这个口径,恒为 false。 */
21
+ fresh: boolean;
18
22
  };
19
23
  /**
20
24
  * 展平后的一条样本:`run` 始终是 attempt 的真实来源(= `attempt.run`,快照维度/
@@ -37,6 +41,15 @@ export declare function evalIdOf(item: Item): string;
37
41
  * 来源可能有多个,水位基准是其中 startedAt 最新的一个。
38
42
  */
39
43
  export declare function historicalOf(item: Item): boolean;
44
+ /** 一个 ISO 时刻距渲染时刻(`Date.now()`)的毫秒数,恒不小于 0。 */
45
+ export declare function msSince(iso: string): number;
46
+ /**
47
+ * 覆盖缺口 / 对照矩阵缺席格的「过期结论」参考(docs/feature/reports/components/summaries/
48
+ * experiment-table.md「覆盖缺口的两档占位行」、show/compare.md 同一套口径):候选已经是与
49
+ * 当前基准 configHash 不可比的那些,取其中最近一条;两个消费方(entity-lists 的占位行、
50
+ * DeltaTable 的缺席格)共用同一份「取最新」判据,不各自实现一遍。
51
+ */
52
+ export declare function staleReferenceOf(candidates: readonly AttemptHandle[]): StaleConclusionReference | undefined;
40
53
  /** 快照键:"<experimentId> @ <startedAt>"("run" 维度与手挑快照数组的对比用)。 */
41
54
  export declare function snapshotKeyOf(run: Run): string;
42
55
  /** 一组 Item 的 eval 全身份键:experimentId + eval id(聚合中的题级身份始终是这一对)。 */
@@ -22,10 +22,18 @@ const KEY_SEP = "\u0000";
22
22
  export function resolveInput(input) {
23
23
  if (Array.isArray(input)) {
24
24
  const runs = input;
25
- return { runs, attempts: runs.flatMap((s) => s.attempts), issues: [], coverage: [] };
25
+ const attempts = runs.flatMap((s) => s.attempts);
26
+ return { runs, attempts, issues: [], coverage: [], historyAttempts: attempts, fresh: false };
26
27
  }
27
28
  const scope = input;
28
- return { runs: scope.runs, attempts: scope.attempts, issues: scope.issues, coverage: scope.coverage };
29
+ return {
30
+ runs: scope.runs,
31
+ attempts: scope.attempts,
32
+ issues: scope.issues,
33
+ coverage: scope.coverage,
34
+ historyAttempts: scope.historyAttempts,
35
+ fresh: scope.fresh,
36
+ };
29
37
  }
30
38
  export function experimentIdOf(item) {
31
39
  return item.attempt.experimentId || item.run.experimentId;
@@ -42,6 +50,30 @@ export function evalIdOf(item) {
42
50
  export function historicalOf(item) {
43
51
  return item.attempt.carried || item.run.startedAt < item.watermark.startedAt;
44
52
  }
53
+ /** 一个 ISO 时刻距渲染时刻(`Date.now()`)的毫秒数,恒不小于 0。 */
54
+ export function msSince(iso) {
55
+ return Math.max(0, Date.now() - Date.parse(iso));
56
+ }
57
+ /**
58
+ * 覆盖缺口 / 对照矩阵缺席格的「过期结论」参考(docs/feature/reports/components/summaries/
59
+ * experiment-table.md「覆盖缺口的两档占位行」、show/compare.md 同一套口径):候选已经是与
60
+ * 当前基准 configHash 不可比的那些,取其中最近一条;两个消费方(entity-lists 的占位行、
61
+ * DeltaTable 的缺席格)共用同一份「取最新」判据,不各自实现一遍。
62
+ */
63
+ export function staleReferenceOf(candidates) {
64
+ if (candidates.length === 0)
65
+ return undefined;
66
+ const newest = candidates.reduce((a, b) => ((a.result.startedAt ?? "") >= (b.result.startedAt ?? "") ? a : b));
67
+ const startedAt = newest.result.startedAt;
68
+ if (!startedAt)
69
+ return undefined; // 无时刻的 legacy 落盘算不出时距,不伪造参考
70
+ return {
71
+ locator: newest.locator ??
72
+ encodeAttemptLocator({ runId: newest.run.runId, evalId: newest.evalId, attempt: newest.result.attempt }),
73
+ verdict: newest.result.verdict,
74
+ staleSinceMs: msSince(startedAt),
75
+ };
76
+ }
45
77
  /** 快照键:"<experimentId> @ <startedAt>"("run" 维度与手挑快照数组的对比用)。 */
46
78
  export function snapshotKeyOf(run) {
47
79
  return `${run.experimentId} @ ${run.startedAt}`;
@@ -37,3 +37,4 @@ export declare function toAttemptSource(attempt: AttemptEvidence): Promise<impor
37
37
  export declare function toAttemptAssertions(attempt: AttemptEvidence): Promise<import("../definition/cell.ts").TableContent | null>;
38
38
  export declare function toAttemptFixPrompt(attempt: AttemptEvidence): Promise<CopyBlockContent | null>;
39
39
  export declare function toAttemptUsage(attempt: AttemptEvidence): Promise<import("./types.ts").UsageTableData | null>;
40
+ export declare function toAttemptFacts(attempt: AttemptEvidence): Promise<import("./types.ts").AttemptFactsData | null>;
@@ -6,7 +6,7 @@ import { sampleSummary } from "../components/summaries/compute.js";
6
6
  import { runNoticesContent, sampleFixPromptContent, sampleNoticesContent, sampleTracesContent, } from "../components/site-components/projections.js";
7
7
  import { heroData } from "../components/site-components/compute.js";
8
8
  import { attemptAssertionsContent, attemptCommandEvidenceContent, attemptConversationContent, attemptDiffContent, attemptFixPromptContent, attemptNoticesContent, projectedSourceContent, attemptTimelineContent, } from "../components/attempt-detail/content.js";
9
- import { attemptAssertionsData, attemptCommandEvidenceData, attemptConversationData, attemptDiagnosticsData, attemptDiffData, attemptErrorData, attemptFixPromptData, attemptSummaryData, attemptTimelineData, usageTableData, } from "../components/attempt-detail/compute.js";
9
+ import { attemptAssertionsData, attemptCommandEvidenceData, attemptConversationData, attemptDiagnosticsData, attemptDiffData, attemptErrorData, attemptFactsData, attemptFixPromptData, attemptSummaryData, attemptTimelineData, usageTableData, } from "../components/attempt-detail/compute.js";
10
10
  /** Sample 挑选警告 → Callouts items。 */
11
11
  export function toSampleNotices(sample) {
12
12
  return sampleNoticesContent(sample);
@@ -83,3 +83,6 @@ export async function toAttemptFixPrompt(attempt) {
83
83
  export async function toAttemptUsage(attempt) {
84
84
  return usageTableData(attempt);
85
85
  }
86
+ export async function toAttemptFacts(attempt) {
87
+ return attemptFactsData(attempt);
88
+ }
@@ -68,12 +68,13 @@ export declare function formatReportDateTimeRange(fromIso: string, toIso: string
68
68
  to: string;
69
69
  };
70
70
  /**
71
- * 历史执行的紧凑时距("3d" / "2h" / "5m" / "10s"):自 `startedAt` 起算,渲染时刻由调用方
72
- * 传入(`nowIso` 缺省当前时刻)——粒度阈值复用 `gapParts`(与曾经的 stale-run message
73
- * 同一套单源,见 `results/select.ts`),只是这里的呈现是紧凑单字母,不是完整单词
74
- * (docs/feature/reports/library.md「时效标注」)。
71
+ * 一段时长(毫秒) → 当前 locale 的紧凑相对时距("12d" / "12 天")。按区间选粒度最大的单位
72
+ * 取整,结果恒不小于一个单位(docs/feature/reports/library/presentation.md「相对时距是数据,
73
+ * 不是文案」):不足 1 小时→分钟,不足 1 天→小时,不足 30 天→天,30 天及以上→月。
74
+ * `locator` 格与占位行参考的 `staleSinceMs` 都经这一个入口,不各写各的措辞;不加箭头、回环
75
+ * 之类的装饰记号——记号不携带时长,读者还要先学会它。
75
76
  */
76
- export declare function formatHistoricalGap(startedAtIso: string, nowIso?: string): string;
77
+ export declare function formatTimeDistance(ms: number, locale?: ReportLocale): string;
77
78
  /** passed / failed / errored / skipped 的判定符。 */
78
79
  export declare function verdictMark(verdict: Verdict): string;
79
80
  /**