niceeval 0.6.1 → 0.7.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 (378) hide show
  1. package/INDEX.md +23 -23
  2. package/README.zh.md +6 -6
  3. package/dist/agents/types.d.ts +69 -7
  4. package/dist/context/types.d.ts +32 -12
  5. package/dist/i18n/en.d.ts +54 -0
  6. package/dist/i18n/zh-CN.d.ts +57 -3
  7. package/dist/o11y/types.d.ts +16 -2
  8. package/dist/report/aggregate.d.ts +32 -24
  9. package/dist/report/aggregate.js +158 -50
  10. package/dist/report/built-in/index.d.ts +2 -0
  11. package/dist/report/built-in/index.js +8 -0
  12. package/dist/report/components.d.ts +93 -160
  13. package/dist/report/components.js +377 -114
  14. package/dist/report/compute.d.ts +87 -81
  15. package/dist/report/compute.js +597 -417
  16. package/dist/report/flag.d.ts +32 -6
  17. package/dist/report/flag.js +92 -4
  18. package/dist/report/format.d.ts +19 -11
  19. package/dist/report/format.js +30 -13
  20. package/dist/report/index.d.ts +16 -16
  21. package/dist/report/index.js +20 -21
  22. package/dist/report/load.js +3 -2
  23. package/dist/report/locale.d.ts +57 -33
  24. package/dist/report/locale.js +122 -56
  25. package/dist/report/metrics.d.ts +23 -4
  26. package/dist/report/metrics.js +110 -25
  27. package/dist/report/primitives.d.ts +48 -15
  28. package/dist/report/primitives.js +135 -26
  29. package/dist/report/react/AttemptList.d.ts +9 -7
  30. package/dist/report/react/AttemptList.js +17 -10
  31. package/dist/report/react/DeltaTable.js +19 -18
  32. package/dist/report/react/EvalList.d.ts +4 -4
  33. package/dist/report/react/EvalList.js +0 -0
  34. package/dist/report/react/ExperimentComparison.d.ts +10 -0
  35. package/dist/report/react/ExperimentComparison.js +12 -0
  36. package/dist/report/react/ExperimentList.d.ts +4 -3
  37. package/dist/report/react/ExperimentList.js +17 -18
  38. package/dist/report/react/MetricBars.js +5 -4
  39. package/dist/report/react/MetricLine.js +12 -5
  40. package/dist/report/react/MetricMatrix.js +1 -1
  41. package/dist/report/react/MetricScatter.js +59 -28
  42. package/dist/report/react/MetricTable.js +2 -12
  43. package/dist/report/react/ScopeSummary.d.ts +10 -0
  44. package/dist/report/react/ScopeSummary.js +17 -0
  45. package/dist/report/react/Scoreboard.js +6 -6
  46. package/dist/report/react/cell.js +2 -2
  47. package/dist/report/react/chart-math.d.ts +23 -6
  48. package/dist/report/react/chart-math.js +71 -19
  49. package/dist/report/react/fixtures.d.ts +5 -9
  50. package/dist/report/react/fixtures.js +110 -147
  51. package/dist/report/react/index.d.ts +15 -5
  52. package/dist/report/react/index.js +18 -7
  53. package/dist/report/report.d.ts +137 -16
  54. package/dist/report/report.js +259 -28
  55. package/dist/report/text/faces.d.ts +17 -19
  56. package/dist/report/text/faces.js +253 -184
  57. package/dist/report/text/plot.js +1 -1
  58. package/dist/report/text/table.js +38 -7
  59. package/dist/report/tree.d.ts +90 -40
  60. package/dist/report/tree.js +252 -94
  61. package/dist/report/types.d.ts +247 -284
  62. package/dist/report/types.js +4 -3
  63. package/dist/report/web.d.ts +21 -5
  64. package/dist/report/web.js +42 -16
  65. package/dist/results/select.d.ts +38 -16
  66. package/dist/results/select.js +73 -25
  67. package/dist/results/types.d.ts +49 -14
  68. package/dist/runner/feedback/sink.d.ts +110 -0
  69. package/dist/runner/types.d.ts +513 -22
  70. package/dist/sandbox/docker.d.ts +23 -2
  71. package/dist/sandbox/e2b.d.ts +15 -1
  72. package/dist/sandbox/errors.d.ts +30 -3
  73. package/dist/sandbox/io-retry.d.ts +17 -0
  74. package/dist/sandbox/registry.d.ts +2 -0
  75. package/dist/sandbox/resolve.d.ts +18 -5
  76. package/dist/sandbox/retry.d.ts +11 -1
  77. package/dist/sandbox/types.d.ts +39 -5
  78. package/dist/sandbox/vercel.d.ts +7 -1
  79. package/dist/scoring/coverage.d.ts +30 -0
  80. package/dist/scoring/display.d.ts +21 -0
  81. package/dist/scoring/display.js +120 -0
  82. package/dist/scoring/types.d.ts +103 -20
  83. package/dist/shared/aggregate.d.ts +4 -2
  84. package/dist/shared/aggregate.js +8 -7
  85. package/dist/shared/types.d.ts +28 -0
  86. package/dist/tty-line.d.ts +0 -4
  87. package/dist/util.d.ts +23 -0
  88. package/docs-site/zh/README.md +44 -0
  89. package/docs-site/zh/examples/ai-agent-application.mdx +63 -0
  90. package/docs-site/zh/examples/coding-agent-extensions.mdx +57 -0
  91. package/docs-site/zh/examples/index.mdx +50 -0
  92. package/docs-site/zh/{concepts → explanation}/adapter.mdx +31 -13
  93. package/docs-site/zh/{concepts → explanation}/assert.mdx +7 -7
  94. package/docs-site/zh/{concepts → explanation}/drive.mdx +8 -8
  95. package/docs-site/zh/{concepts → explanation}/evals.mdx +4 -4
  96. package/docs-site/zh/{concepts → explanation}/experiment.mdx +8 -8
  97. package/docs-site/zh/{concepts → explanation}/hitl.mdx +8 -8
  98. package/docs-site/zh/{concepts → explanation}/judge.mdx +5 -5
  99. package/docs-site/zh/{concepts → explanation}/overview.mdx +11 -11
  100. package/docs-site/zh/{guides → explanation}/runner.mdx +18 -8
  101. package/docs-site/zh/{concepts → explanation}/tier.mdx +6 -6
  102. package/docs-site/zh/{guides → how-to}/agent-feedback-loop.mdx +35 -33
  103. package/docs-site/zh/{guides → how-to}/authoring.mdx +35 -2
  104. package/docs-site/zh/{guides → how-to}/ci-integration.mdx +23 -12
  105. package/docs-site/zh/{guides → how-to}/connect-otel.mdx +6 -6
  106. package/docs-site/zh/{guides → how-to}/connect-your-agent.mdx +47 -21
  107. package/docs-site/zh/{guides → how-to}/custom-reports.mdx +34 -39
  108. package/docs-site/zh/{guides → how-to}/dataset-fanout.mdx +25 -3
  109. package/docs-site/zh/{guides → how-to}/experiments.mdx +12 -5
  110. package/docs-site/zh/how-to/publish-report.mdx +105 -0
  111. package/docs-site/zh/{guides → how-to}/reporters.mdx +2 -2
  112. package/docs-site/zh/{guides → how-to}/sandbox-agent.mdx +56 -7
  113. package/docs-site/zh/how-to/sandbox-providers.mdx +350 -0
  114. package/docs-site/zh/{guides → how-to}/scoring-guide.mdx +4 -4
  115. package/docs-site/zh/{guides → how-to}/viewing-results.mdx +82 -39
  116. package/docs-site/zh/{guides → how-to}/write-experiment.mdx +6 -4
  117. package/docs-site/zh/{guides → how-to}/write-send.mdx +30 -14
  118. package/docs-site/zh/index.mdx +24 -26
  119. package/docs-site/zh/introduction.mdx +8 -8
  120. package/docs-site/zh/reference/builtin-agents.mdx +32 -5
  121. package/docs-site/zh/reference/capabilities.mdx +8 -8
  122. package/docs-site/zh/reference/cli.mdx +40 -12
  123. package/docs-site/zh/reference/define-agent.mdx +58 -5
  124. package/docs-site/zh/reference/define-config.mdx +1 -1
  125. package/docs-site/zh/reference/define-eval.mdx +42 -9
  126. package/docs-site/zh/reference/events.mdx +3 -3
  127. package/docs-site/zh/reference/expect.mdx +26 -1
  128. package/docs-site/zh/{guides → reference}/official-adapters.mdx +32 -8
  129. package/docs-site/zh/{guides → reference}/report-components.mdx +45 -33
  130. package/docs-site/zh/{guides → reference}/results-data.mdx +21 -13
  131. package/docs-site/zh/troubleshooting/debug-sandbox.mdx +57 -0
  132. package/docs-site/zh/troubleshooting/debugging.mdx +212 -0
  133. package/docs-site/zh/{quickstart.mdx → tutorials/quickstart.mdx} +5 -17
  134. package/package.json +10 -2
  135. package/src/agents/ai-sdk-otel.test.ts +1 -0
  136. package/src/agents/ai-sdk.test.ts +3 -0
  137. package/src/agents/ai-sdk.ts +3 -0
  138. package/src/agents/bub-install-spec.test.ts +34 -0
  139. package/src/agents/bub-install-spec.ts +32 -0
  140. package/src/agents/bub.ts +31 -32
  141. package/src/agents/claude-code.test.ts +130 -9
  142. package/src/agents/claude-code.ts +76 -4
  143. package/src/agents/codex.test.ts +189 -40
  144. package/src/agents/codex.ts +155 -14
  145. package/src/agents/coding-cli-versions.test.ts +15 -0
  146. package/src/agents/coding-cli-versions.ts +3 -0
  147. package/src/agents/index.ts +13 -2
  148. package/src/agents/langgraph.test.ts +204 -0
  149. package/src/agents/langgraph.ts +495 -0
  150. package/src/agents/marketplace.ts +85 -0
  151. package/src/agents/native-config.test.ts +179 -0
  152. package/src/agents/native-config.ts +267 -0
  153. package/src/agents/openai-compat.test.ts +1 -0
  154. package/src/agents/openai-compat.ts +1 -1
  155. package/src/agents/openclaw.test.ts +31 -0
  156. package/src/agents/openclaw.ts +171 -0
  157. package/src/agents/plugin-config.test.ts +1 -0
  158. package/src/agents/sdk-streams.test.ts +79 -0
  159. package/src/agents/sdk-streams.ts +55 -10
  160. package/src/agents/skills.test.ts +1 -0
  161. package/src/agents/streaming.test.ts +3 -9
  162. package/src/agents/streaming.ts +2 -2
  163. package/src/agents/types.ts +71 -8
  164. package/src/agents/ui-message-stream.test.ts +3 -0
  165. package/src/cli.ts +446 -124
  166. package/src/context/context.test.ts +51 -12
  167. package/src/context/context.ts +162 -30
  168. package/src/context/session.test.ts +2 -1
  169. package/src/context/session.ts +115 -7
  170. package/src/context/types.ts +30 -12
  171. package/src/define.test.ts +13 -8
  172. package/src/define.ts +25 -4
  173. package/src/expect/index.ts +53 -23
  174. package/src/i18n/en.ts +81 -17
  175. package/src/i18n/zh-CN.ts +80 -17
  176. package/src/o11y/cost.test.ts +1 -0
  177. package/src/o11y/execution-tree.test.ts +1 -20
  178. package/src/o11y/otlp/mappers/claude-code.test.ts +1 -0
  179. package/src/o11y/otlp/parse.test.ts +1 -0
  180. package/src/o11y/otlp/turn-otel.test.ts +1 -0
  181. package/src/o11y/parsers/bub.test.ts +1 -0
  182. package/src/o11y/parsers/claude-code.test.ts +1 -34
  183. package/src/o11y/parsers/openclaw.test.ts +154 -0
  184. package/src/o11y/parsers/openclaw.ts +310 -0
  185. package/src/o11y/prices.json +746 -311
  186. package/src/o11y/tool-names.test.ts +1 -0
  187. package/src/o11y/types.ts +16 -2
  188. package/src/report/aggregate.ts +178 -61
  189. package/src/report/built-in/index.tsx +9 -0
  190. package/src/report/components.tsx +625 -279
  191. package/src/report/compute.ts +723 -491
  192. package/src/report/dual-render.test.tsx +741 -1024
  193. package/src/report/flag.ts +104 -12
  194. package/src/report/format.ts +32 -12
  195. package/src/report/index.ts +119 -46
  196. package/src/report/load.ts +3 -2
  197. package/src/report/locale.ts +136 -65
  198. package/src/report/metrics.ts +108 -25
  199. package/src/report/primitives.tsx +196 -45
  200. package/src/report/react/AttemptList.tsx +30 -43
  201. package/src/report/react/DeltaTable.tsx +63 -45
  202. package/src/report/react/EvalList.tsx +0 -0
  203. package/src/report/react/ExperimentComparison.tsx +73 -0
  204. package/src/report/react/ExperimentList.tsx +50 -32
  205. package/src/report/react/MetricBars.tsx +5 -4
  206. package/src/report/react/MetricLine.tsx +13 -8
  207. package/src/report/react/MetricMatrix.tsx +2 -2
  208. package/src/report/react/MetricScatter.tsx +86 -34
  209. package/src/report/react/MetricTable.tsx +4 -76
  210. package/src/report/react/ScopeSummary.tsx +86 -0
  211. package/src/report/react/Scoreboard.tsx +28 -10
  212. package/src/report/react/cell.tsx +2 -2
  213. package/src/report/react/chart-math.test.ts +85 -0
  214. package/src/report/react/chart-math.ts +101 -22
  215. package/src/report/react/enhance.js +89 -5
  216. package/src/report/react/fixtures.ts +114 -154
  217. package/src/report/react/index.tsx +24 -39
  218. package/src/report/react/render.test.tsx +138 -158
  219. package/src/report/react/styles.css +243 -82
  220. package/src/report/report.test.ts +779 -841
  221. package/src/report/report.ts +423 -41
  222. package/src/report/text/faces.ts +290 -193
  223. package/src/report/text/plot.ts +1 -1
  224. package/src/report/text/table.ts +44 -7
  225. package/src/report/tree.ts +362 -104
  226. package/src/report/types.ts +261 -271
  227. package/src/report/web.ts +63 -20
  228. package/src/results/annotated-source.test.ts +62 -9
  229. package/src/results/annotated-source.ts +64 -6
  230. package/src/results/attempt-evidence.test.ts +13 -11
  231. package/src/results/attempt-evidence.ts +20 -13
  232. package/src/results/attempt-source.ts +6 -3
  233. package/src/results/copy.ts +150 -60
  234. package/src/results/host-equivalence.test.ts +34 -20
  235. package/src/results/index.ts +12 -4
  236. package/src/results/locator.test.ts +1 -22
  237. package/src/results/open.ts +15 -5
  238. package/src/results/publish.ts +149 -0
  239. package/src/results/results.test.ts +89 -54
  240. package/src/results/select.ts +104 -34
  241. package/src/results/truncate.ts +90 -0
  242. package/src/results/types.ts +43 -14
  243. package/src/results/writer.ts +31 -13
  244. package/src/runner/attempt.test.ts +138 -7
  245. package/src/runner/attempt.ts +603 -104
  246. package/src/runner/discover.test.ts +47 -0
  247. package/src/runner/discover.ts +36 -2
  248. package/src/runner/eval-source.test.ts +1 -27
  249. package/src/runner/feedback/agent.test.ts +504 -0
  250. package/src/runner/feedback/agent.ts +409 -0
  251. package/src/runner/feedback/ci.test.ts +562 -0
  252. package/src/runner/feedback/ci.ts +401 -0
  253. package/src/runner/feedback/coordinator.test.ts +317 -0
  254. package/src/runner/feedback/coordinator.ts +397 -0
  255. package/src/runner/feedback/failure.ts +40 -0
  256. package/src/runner/feedback/human.test.ts +616 -0
  257. package/src/runner/feedback/human.ts +535 -0
  258. package/src/runner/feedback/index.ts +66 -0
  259. package/src/runner/feedback/io.ts +78 -0
  260. package/src/runner/feedback/profile.test.ts +50 -0
  261. package/src/runner/feedback/profile.ts +58 -0
  262. package/src/runner/feedback/reducer.test.ts +395 -0
  263. package/src/runner/feedback/reducer.ts +260 -0
  264. package/src/runner/feedback/renderer.ts +82 -0
  265. package/src/runner/feedback/sink.ts +203 -0
  266. package/src/runner/feedback/testing.ts +106 -0
  267. package/src/runner/ledger.test.ts +230 -0
  268. package/src/runner/ledger.ts +329 -0
  269. package/src/runner/report.test.ts +128 -3
  270. package/src/runner/report.ts +33 -9
  271. package/src/runner/reporters/artifacts.ts +8 -2
  272. package/src/runner/reporters/braintrust.test.ts +8 -7
  273. package/src/runner/reporters/braintrust.ts +9 -2
  274. package/src/runner/reporters/index.ts +2 -2
  275. package/src/runner/reporters/json.test.ts +162 -0
  276. package/src/runner/reporters/json.ts +35 -8
  277. package/src/runner/reporters/shared.ts +1 -5
  278. package/src/runner/run.test.ts +760 -3
  279. package/src/runner/run.ts +243 -37
  280. package/src/runner/sandbox-prep.ts +3 -42
  281. package/src/runner/timing.ts +158 -0
  282. package/src/runner/types.ts +518 -22
  283. package/src/sandbox/checkpoint.test.ts +55 -0
  284. package/src/sandbox/checkpoint.ts +29 -8
  285. package/src/sandbox/cli-commands.ts +407 -0
  286. package/src/sandbox/docker.ts +115 -16
  287. package/src/sandbox/e2b-agent-template.test.ts +56 -0
  288. package/src/sandbox/e2b-agent-template.ts +94 -0
  289. package/src/sandbox/e2b.ts +74 -9
  290. package/src/sandbox/errors.ts +111 -4
  291. package/src/sandbox/index.ts +2 -0
  292. package/src/sandbox/io-retry.test.ts +58 -0
  293. package/src/sandbox/io-retry.ts +45 -0
  294. package/src/sandbox/keep-registry.test.ts +86 -0
  295. package/src/sandbox/keep-registry.ts +142 -0
  296. package/src/sandbox/keep.ts +178 -0
  297. package/src/sandbox/paths.test.ts +1 -0
  298. package/src/sandbox/paths.ts +19 -8
  299. package/src/sandbox/registry.ts +20 -3
  300. package/src/sandbox/resolve.ts +76 -11
  301. package/src/sandbox/retry.test.ts +70 -0
  302. package/src/sandbox/retry.ts +46 -4
  303. package/src/sandbox/types.ts +44 -6
  304. package/src/sandbox/vercel.ts +43 -20
  305. package/src/scoring/collector.ts +60 -17
  306. package/src/scoring/coverage.ts +95 -0
  307. package/src/scoring/diff.ts +81 -0
  308. package/src/scoring/display.test.ts +121 -0
  309. package/src/scoring/display.ts +133 -0
  310. package/src/scoring/evidence.test.ts +189 -0
  311. package/src/scoring/judge.test.ts +142 -0
  312. package/src/scoring/judge.ts +15 -18
  313. package/src/scoring/scoped.ts +217 -50
  314. package/src/scoring/types.ts +117 -20
  315. package/src/scoring/verdict.ts +16 -4
  316. package/src/shared/aggregate.ts +8 -6
  317. package/src/shared/types.ts +31 -0
  318. package/src/show/compose.ts +50 -67
  319. package/src/show/index.ts +127 -56
  320. package/src/show/render.ts +662 -131
  321. package/src/show/report-host.test.ts +188 -0
  322. package/src/show/report-host.ts +375 -0
  323. package/src/show/show.test.ts +320 -54
  324. package/src/tty-line.ts +8 -26
  325. package/src/util.test.ts +1 -0
  326. package/src/util.ts +41 -0
  327. package/src/view/app/App.test.tsx +69 -0
  328. package/src/view/app/App.tsx +144 -48
  329. package/src/view/app/components/AttemptModal.tsx +423 -11
  330. package/src/view/app/components/CodeView.tsx +41 -14
  331. package/src/view/app/components/CopyControls.tsx +2 -2
  332. package/src/view/app/i18n.ts +37 -17
  333. package/src/view/app/lib/attempt-route.test.ts +1 -0
  334. package/src/view/app/lib/verdict.ts +7 -9
  335. package/src/view/app/main.tsx +13 -8
  336. package/src/view/app/pages/{RunsPage.tsx → AttemptsPage.tsx} +6 -6
  337. package/src/view/app/types.ts +4 -1
  338. package/src/view/artifact-serving.test.ts +2 -1
  339. package/src/view/client-dist/app.css +1 -1
  340. package/src/view/client-dist/app.js +17 -17
  341. package/src/view/data.test.ts +10 -3
  342. package/src/view/data.ts +155 -49
  343. package/src/view/index.ts +56 -41
  344. package/src/view/server.ts +37 -15
  345. package/src/view/shared/types.ts +34 -5
  346. package/src/view/styles.css +227 -0
  347. package/src/view/view-report.test.ts +167 -62
  348. package/dist/report/built-ins/experiment-comparison.d.ts +0 -1
  349. package/dist/report/built-ins/experiment-comparison.js +0 -13
  350. package/dist/report/built-ins/index.d.ts +0 -1
  351. package/dist/report/built-ins/index.js +0 -2
  352. package/dist/report/react/GroupSummary.d.ts +0 -8
  353. package/dist/report/react/GroupSummary.js +0 -8
  354. package/dist/report/react/RunOverview.d.ts +0 -8
  355. package/dist/report/react/RunOverview.js +0 -12
  356. package/docs-site/zh/example/ai-agent-application.mdx +0 -152
  357. package/docs-site/zh/example/claude-code-codex-plugin.mdx +0 -167
  358. package/docs-site/zh/example/claude-code-codex-skill.mdx +0 -152
  359. package/docs-site/zh/example/showcase.mdx +0 -39
  360. package/docs-site/zh/guides/publish-report.mdx +0 -91
  361. package/docs-site/zh/guides/sandbox-providers.mdx +0 -102
  362. package/src/report/built-in-user-parity.test.tsx +0 -640
  363. package/src/report/built-ins/experiment-comparison.tsx +0 -19
  364. package/src/report/built-ins/index.ts +0 -2
  365. package/src/report/react/GroupSummary.tsx +0 -66
  366. package/src/report/react/RunOverview.tsx +0 -109
  367. package/src/runner/reporters/console.ts +0 -70
  368. package/src/runner/reporters/live.test.ts +0 -56
  369. package/src/runner/reporters/live.ts +0 -247
  370. package/src/runner/reporters/quiet.test.ts +0 -66
  371. package/src/runner/reporters/quiet.ts +0 -49
  372. package/src/runner/reporters/table.ts +0 -277
  373. /package/docs-site/zh/{example/tier1-ai-sdk-v7.mdx → examples/integrations/ai-sdk-v7.mdx} +0 -0
  374. /package/docs-site/zh/{example/tier1-claude-sdk.mdx → examples/integrations/claude-sdk.mdx} +0 -0
  375. /package/docs-site/zh/{example/tier1-codex-sdk.mdx → examples/integrations/codex-sdk.mdx} +0 -0
  376. /package/docs-site/zh/{example/tier1-langgraph.mdx → examples/integrations/langgraph.mdx} +0 -0
  377. /package/docs-site/zh/{example/tier1-pi-sdk.mdx → examples/integrations/pi-sdk.mdx} +0 -0
  378. /package/docs-site/zh/{guides → how-to}/fixtures.mdx +0 -0
@@ -1,14 +1,51 @@
1
- // 官方组件 chrome 文案的 locale 字典(en / zh-CN 两份)。
1
+ // 官方组件 chrome 文案的 locale 字典与 LocalizedText 解析。
2
+ // ReportLocale 是开放的 BCP 47 标签(数据协议不封语言上限);官方内置文案与
3
+ // MetricCell.display 生成面当前覆盖 en / zh-CN,其它 locale 走 LocalizedText 回退规则。
2
4
  // 只覆盖组件自带的固定文案(verdict 词、缺数据、覆盖率角标、注脚、占位符等);
3
- // 数据本身(display、维度键、warnings 的 message)不经这里。
4
- // 刻意不 import src/i18n/(CLI 专用字典,locale 来源与 key 面完全不同);
5
- // zh-CN 译法与 src/view/app/i18n.ts 现有词保持一致(成功率/平均耗时/预估成本…)。
5
+ // 维度键、warnings 的 message 不经这里。
6
+ // 刻意不 import src/i18n/(CLI 专用字典,locale 来源与 key 面完全不同)
6
7
 
7
- /** 报告渲染的 locale;默认 "en"(text 面缺省即 en,`niceeval show` 输出不变)。 */
8
- export type ReportLocale = "en" | "zh-CN";
8
+ import type { LocalizedText } from "../types.ts";
9
+
10
+ export type { LocalizedText };
11
+
12
+ /** 报告渲染的 locale(BCP 47 标签,开放);默认 "en"(`niceeval show` 缺省输出不变)。 */
13
+ export type ReportLocale = string;
9
14
 
10
15
  export const DEFAULT_REPORT_LOCALE: ReportLocale = "en";
11
16
 
17
+ /** MetricCell.display 生成面覆盖的 locale 全集;其它 locale 按 LocalizedText 回退取 en。 */
18
+ export const DISPLAY_LOCALES: readonly ReportLocale[] = ["en", "zh-CN"];
19
+
20
+ /**
21
+ * LocalizedText 的确定回退:取当前 locale;缺失时取 en;仍缺失时取按 locale 键字典序的
22
+ * 第一个非空值。对象没有任何非空值时报错,不渲染空文案
23
+ * (docs/feature/reports/library/shell.md「行为约束」)。
24
+ */
25
+ export function resolveLocalizedText(text: LocalizedText, locale: ReportLocale): string {
26
+ if (typeof text === "string") return text;
27
+ const direct = text[locale];
28
+ if (direct !== undefined && direct !== "") return direct;
29
+ const en = text.en;
30
+ if (en !== undefined && en !== "") return en;
31
+ for (const key of Object.keys(text).sort()) {
32
+ const value = text[key];
33
+ if (value !== undefined && value !== "") return value;
34
+ }
35
+ throw new Error(
36
+ "LocalizedText object has no non-empty value. Provide at least one locale entry, e.g. { en: \"…\" }.",
37
+ );
38
+ }
39
+
40
+ /** LocalizedText 按字段值深相等(键顺序无关;标题回退链的「唯一且相同」判定用)。 */
41
+ export function localizedTextEquals(a: LocalizedText, b: LocalizedText): boolean {
42
+ if (typeof a === "string" || typeof b === "string") return a === b;
43
+ const keysA = Object.keys(a).sort();
44
+ const keysB = Object.keys(b).sort();
45
+ if (keysA.length !== keysB.length) return false;
46
+ return keysA.every((key, i) => key === keysB[i] && a[key] === b[key]);
47
+ }
48
+
12
49
  const en = {
13
50
  "verdict.passed": "passed",
14
51
  "verdict.failed": "failed",
@@ -20,6 +57,7 @@ const en = {
20
57
  "cell.measuredTitle": "{samples}/{total} attempts measured",
21
58
  "cell.noneMeasurableTitle": "0/{total} attempts measurable",
22
59
  "cell.coverageTitle": "coverage {samples}/{total}: this metric is null for the remaining attempts",
60
+ "cell.missingValue": "(missing)",
23
61
 
24
62
  "table.higherBetter": "higher is better",
25
63
  "table.lowerBetter": "lower is better",
@@ -34,43 +72,50 @@ const en = {
34
72
  "table.attempt": "attempt",
35
73
  "experimentList.experiment": "Experiment",
36
74
  "experimentList.avgDuration": "Avg duration",
37
- "experimentList.passRate": "Pass rate",
75
+ "experimentList.passRate": "End-to-end pass rate",
38
76
  "experimentList.tokens": "Tokens",
39
- "experimentList.estimatedCost": "Est. cost",
77
+ "experimentList.cost": "Cost",
40
78
  "experimentList.result": "Result",
41
79
  "experimentList.status": "Status",
42
80
  "experimentList.evalAttempt": "Eval / Attempt",
43
81
  "experimentList.duration": "Duration",
44
- "experimentList.cost": "Cost",
45
82
  "experimentList.filterPlaceholder": "Filter experiments…",
46
83
  "experimentList.defaultModel": "default",
47
84
  "experimentList.flags": "Flags",
85
+ "entityList.average": "{value} avg",
86
+ "entityList.moreFailures.one": "+{n} more failure",
87
+ "entityList.moreFailures.other": "+{n} more failures",
48
88
  /** <Table> 压到下限仍放不下时,从右侧丢列并如实报数。 */
49
89
  "table.columnsHidden.one": "({n} more column not shown)",
50
90
  "table.columnsHidden.other": "({n} more columns not shown)",
51
91
 
52
- "overview.snapshots": "Snapshots",
53
- "overview.evals": "Evals",
54
- "overview.attempts": "attempts",
55
- "overview.passRate": "Pass rate",
56
- "overview.totalCost": "Total cost",
57
- "overview.totalDuration": "Total duration",
58
- "overview.source": "Source: {n} snapshots",
92
+ /** ScopeSummary 的 KPI 标签。 */
93
+ "scopeSummary.experiments": "Experiments",
94
+ "scopeSummary.evals": "Evals",
95
+ "scopeSummary.attempts": "attempts",
96
+ "scopeSummary.passRate": "End-to-end pass rate",
97
+ "scopeSummary.totalCost": "Total cost",
98
+ "scopeSummary.range": "{from} {to}",
99
+ "scopeSummary.votesEval": "eval verdicts",
100
+ "scopeSummary.votesAttempt": "attempt verdicts",
59
101
  "overview.experiments.one": "{n} experiment",
60
102
  "overview.experiments.other": "{n} experiments",
61
103
  "overview.evalsCount": "{n} evals",
62
104
  "overview.attemptsCount": "{n} attempts",
105
+ "overview.evals": "Evals",
106
+ "overview.passRate": "End-to-end pass rate",
107
+ "overview.totalCost": "Cost",
63
108
 
64
- /** GroupSummary 的 KPI dt 标签(其余字段复用 verdict.* / overview.* 已有 key)。 */
65
- "groupSummary.experiments": "Experiments",
66
-
67
- "composedFrom.one": "composed from {n} run",
68
- "composedFrom.other": "composed from {n} runs",
69
- "latestRun": "latest {run}",
109
+ "experimentComparison.groups": "Experiment groups",
110
+ "experimentComparison.group": "Experiment group",
111
+ "experimentComparison.results": "Eval results",
112
+ "experimentComparison.lastRun": "Last run",
113
+ "experimentComparison.selectGroup": "Select experiment group {group}",
114
+ "experimentComparison.commandsHead": "Group details:",
115
+ "experimentComparison.empty": "No experiment groups",
70
116
 
71
- "scatter.betterHint": "better ↗",
117
+ /** 方向提示唯一文案:轴向已随 better 反正,「更好」恒指向右上(两轴都声明 better 时才显示)。 */
72
118
  "scatter.betterUpperRight": "better → upper right",
73
- "scatter.axisReversed": "(axis reversed: right = better)",
74
119
  /** 0 个可画点:x/y 指标没有可用数据。 */
75
120
  "scatter.noData": "No data to plot {x} × {y}",
76
121
  "pointsMissing.one": "{n} point missing data",
@@ -80,22 +125,28 @@ const en = {
80
125
  "attemptList.truncated": "and {n} more not shown",
81
126
  "attemptList.truncatedText": "({n} more not shown)",
82
127
  "attemptList.score": "score {score}",
128
+ "attemptList.unavailable": "unavailable",
83
129
  "attemptList.details": "details",
84
130
 
85
131
  "scoreboard.total": "Total",
86
132
  "scoreboard.totalText": "total",
87
- "scoreboard.missing.one": "{n} eval missing, scored 0",
88
- "scoreboard.missing.other": "{n} evals missing, scored 0",
89
- "scoreboard.missingText": "({n} missing)",
133
+ "scoreboard.notRun.one": "{n} eval not run, scored 0",
134
+ "scoreboard.notRun.other": "{n} evals not run, scored 0",
135
+ "scoreboard.unscorable.one": "{n} eval unscorable, scored 0",
136
+ "scoreboard.unscorable.other": "{n} evals unscorable, scored 0",
137
+ "scoreboard.notRunText": "({n} not run)",
138
+ "scoreboard.unscorableText": "({n} unscorable)",
139
+ "scoreboard.ignored.one": "{n} eval outside the question set ignored",
140
+ "scoreboard.ignored.other": "{n} evals outside the question set ignored",
90
141
  "scoreboard.weights": "weights:",
91
142
  "scoreboard.allWeights": "all evals ×1",
92
143
  "scoreboard.othersWeight": "others ×1",
93
- "scoreboard.subjectTitle": "{evals} evals, weighted {earned} of {possible}",
144
+ "scoreboard.subjectTitle": "{questions} evals, weighted {earned} of {possible}",
94
145
 
95
146
  "delta.pairHeader": "pair (A → B)",
147
+ "delta.empty": "{experiments} experiments, 0 comparable pairs",
96
148
 
97
- "board.currentVerdicts": "Current verdicts",
98
- "board.failing": "Failing:",
149
+ "tabs.tab": "Tab",
99
150
  } as const;
100
151
 
101
152
  export type ReportMessageKey = keyof typeof en;
@@ -110,6 +161,7 @@ const zhCN: Record<ReportMessageKey, string> = {
110
161
  "cell.measuredTitle": "{samples}/{total} 次 attempt 测得",
111
162
  "cell.noneMeasurableTitle": "0/{total} 次 attempt 可测",
112
163
  "cell.coverageTitle": "覆盖率 {samples}/{total}:其余 attempt 测不了这个指标",
164
+ "cell.missingValue": "(missing)",
113
165
 
114
166
  "table.higherBetter": "越高越好",
115
167
  "table.lowerBetter": "越低越好",
@@ -123,41 +175,47 @@ const zhCN: Record<ReportMessageKey, string> = {
123
175
  "table.attempt": "Attempt",
124
176
  "experimentList.experiment": "实验",
125
177
  "experimentList.avgDuration": "平均耗时",
126
- "experimentList.passRate": "成功率",
178
+ "experimentList.passRate": "端到端成功率",
127
179
  "experimentList.tokens": "Tokens",
128
- "experimentList.estimatedCost": "预估成本",
180
+ "experimentList.cost": "成本",
129
181
  "experimentList.result": "结果",
130
182
  "experimentList.status": "状态",
131
183
  "experimentList.evalAttempt": "题目 / Attempt",
132
184
  "experimentList.duration": "耗时",
133
- "experimentList.cost": "成本",
134
185
  "experimentList.filterPlaceholder": "筛选实验…",
135
186
  "experimentList.defaultModel": "默认",
136
187
  "experimentList.flags": "Flags",
188
+ "entityList.average": "平均 {value}",
189
+ "entityList.moreFailures.one": "+{n} 条其它失败",
190
+ "entityList.moreFailures.other": "+{n} 条其它失败",
137
191
  "table.columnsHidden.one": "(还有 {n} 列未列出)",
138
192
  "table.columnsHidden.other": "(还有 {n} 列未列出)",
139
193
 
140
- "overview.snapshots": "快照",
141
- "overview.evals": "题目",
142
- "overview.attempts": "尝试",
143
- "overview.passRate": "通过率",
144
- "overview.totalCost": "总成本",
145
- "overview.totalDuration": "总耗时",
146
- "overview.source": "数据来源:{n} 个快照",
194
+ "scopeSummary.experiments": "实验数",
195
+ "scopeSummary.evals": "题目",
196
+ "scopeSummary.attempts": "次 attempt",
197
+ "scopeSummary.passRate": "端到端成功率",
198
+ "scopeSummary.totalCost": "总成本",
199
+ "scopeSummary.range": "{from} – {to}",
200
+ "scopeSummary.votesEval": "Eval 计票",
201
+ "scopeSummary.votesAttempt": "Attempt 计票",
147
202
  "overview.experiments.one": "{n} 个实验",
148
203
  "overview.experiments.other": "{n} 个实验",
149
204
  "overview.evalsCount": "{n} 道题",
150
205
  "overview.attemptsCount": "{n} 次 attempt",
206
+ "overview.evals": "Eval",
207
+ "overview.passRate": "端到端成功率",
208
+ "overview.totalCost": "成本",
151
209
 
152
- "groupSummary.experiments": "实验数",
210
+ "experimentComparison.groups": "实验组",
211
+ "experimentComparison.group": "实验组",
212
+ "experimentComparison.results": "Eval 结果",
213
+ "experimentComparison.lastRun": "最后运行",
214
+ "experimentComparison.selectGroup": "选择实验组 {group}",
215
+ "experimentComparison.commandsHead": "查看组内详情:",
216
+ "experimentComparison.empty": "没有实验组",
153
217
 
154
- "composedFrom.one": "合成自 {n} 个 run",
155
- "composedFrom.other": "合成自 {n} 个 run",
156
- "latestRun": "最新 {run}",
157
-
158
- "scatter.betterHint": "更好 ↗",
159
- "scatter.betterUpperRight": "更好 → 右上",
160
- "scatter.axisReversed": "(轴反向:右 = 更好)",
218
+ "scatter.betterUpperRight": "越靠右上越好",
161
219
  "scatter.noData": "{x} × {y} 没有可绘制的数据",
162
220
  "pointsMissing.one": "{n} 个点缺数据",
163
221
  "pointsMissing.other": "{n} 个点缺数据",
@@ -166,30 +224,36 @@ const zhCN: Record<ReportMessageKey, string> = {
166
224
  "attemptList.truncated": "还有 {n} 条未列出",
167
225
  "attemptList.truncatedText": "(还有 {n} 条未列出)",
168
226
  "attemptList.score": "得分 {score}",
227
+ "attemptList.unavailable": "评不了",
169
228
  "attemptList.details": "详情",
170
229
 
171
230
  "scoreboard.total": "总分",
172
231
  "scoreboard.totalText": "总分",
173
- "scoreboard.missing.one": "{n} 道题缺席,按 0 计",
174
- "scoreboard.missing.other": "{n} 道题缺席,按 0 计",
175
- "scoreboard.missingText": "(缺 {n} 题)",
232
+ "scoreboard.notRun.one": "{n} 道题没跑,按 0 计",
233
+ "scoreboard.notRun.other": "{n} 道题没跑,按 0 计",
234
+ "scoreboard.unscorable.one": "{n} 道题测不了,按 0 计",
235
+ "scoreboard.unscorable.other": "{n} 道题测不了,按 0 计",
236
+ "scoreboard.notRunText": "(没跑 {n} 题)",
237
+ "scoreboard.unscorableText": "(测不了 {n} 题)",
238
+ "scoreboard.ignored.one": "题集之外的 {n} 道题已忽略",
239
+ "scoreboard.ignored.other": "题集之外的 {n} 道题已忽略",
176
240
  "scoreboard.weights": "权重:",
177
241
  "scoreboard.allWeights": "全部题 ×1",
178
242
  "scoreboard.othersWeight": "其余 ×1",
179
- "scoreboard.subjectTitle": "{evals} 道题,加权得分 {earned}/{possible}",
243
+ "scoreboard.subjectTitle": "{questions} 道题,加权得分 {earned}/{possible}",
180
244
 
181
245
  "delta.pairHeader": "对比 (A → B)",
246
+ "delta.empty": "{experiments} 个实验、0 个可配对",
182
247
 
183
- "board.currentVerdicts": "现刻判定",
184
- "board.failing": "失败清单:",
248
+ "tabs.tab": "Tab",
185
249
  };
186
250
 
187
- const dictionaries: Record<ReportLocale, Record<ReportMessageKey, string>> = {
251
+ const dictionaries: Record<string, Record<ReportMessageKey, string>> = {
188
252
  en,
189
253
  "zh-CN": zhCN,
190
254
  };
191
255
 
192
- /** 查字典 + 简单插值({name} 占位符)。缺 locale 回退 en。 */
256
+ /** 查字典 + 简单插值({name} 占位符)。内置词典未覆盖的 locale 回退 en。 */
193
257
  export function localeText(
194
258
  locale: ReportLocale,
195
259
  key: ReportMessageKey,
@@ -205,25 +269,32 @@ export function localeText(
205
269
  /** 带单复数的计数文案:n === 1 用 `<base>.one`,其余 `<base>.other`(zh-CN 两键同值)。 */
206
270
  export function countText(
207
271
  locale: ReportLocale,
208
- base: "overview.experiments" | "composedFrom" | "pointsMissing" | "scoreboard.missing" | "table.columnsHidden",
272
+ base:
273
+ | "overview.experiments"
274
+ | "pointsMissing"
275
+ | "scoreboard.notRun"
276
+ | "scoreboard.unscorable"
277
+ | "scoreboard.ignored"
278
+ | "entityList.moreFailures"
279
+ | "table.columnsHidden",
209
280
  n: number,
210
281
  ): string {
211
282
  return localeText(locale, `${base}.${n === 1 ? "one" : "other"}` as ReportMessageKey, { n });
212
283
  }
213
284
 
214
- /** 指标 label 的可本地化形态:字符串,或按 locale 给的字典(缺项回退 en,再回退任一非空值)。 */
215
- export type LocalizedLabel = string | Partial<Record<ReportLocale, string>>;
216
-
217
285
  /**
218
- * 按 locale 解析指标 label:字符串原样;字典取当前 locale,缺项回退 en,
219
- * 再回退字典里任一值;全空回退 fallback(= metric.name)。渲染面(web / text)共用。
286
+ * 按 locale 解析指标 / 列 label:字符串原样;字典按 LocalizedText 回退规则取值;
287
+ * undefined 回退 fallback(= metric.name)。渲染面(web / text)共用。
220
288
  */
221
289
  export function resolveMetricLabel(
222
- label: LocalizedLabel | undefined,
290
+ label: LocalizedText | undefined,
223
291
  locale: ReportLocale,
224
292
  fallback: string,
225
293
  ): string {
226
294
  if (label === undefined) return fallback;
227
- if (typeof label === "string") return label;
228
- return label[locale] ?? label.en ?? Object.values(label).find((v) => v !== undefined) ?? fallback;
295
+ try {
296
+ return resolveLocalizedText(label, locale);
297
+ } catch {
298
+ return fallback;
299
+ }
229
300
  }
@@ -1,20 +1,25 @@
1
1
  // defineMetric 与内置指标。
2
2
  //
3
3
  // null ≠ 0:null = 此 attempt 测不了这个指标(不进聚合);0 = 测了,结果是零(照常进)。
4
- // 哪个 verdict 落哪边必须显式表态,内置指标按 docs/feature/reports/library.md「内置指标」的表格:
4
+ // 哪个 verdict 落哪边必须显式表态,内置指标按 docs/feature/reports/library.md「内置指标」的表格。
5
+ // 三个通过率指标把「Agent 答错」与「基建没跑起来」拆开,不互相伪装:
5
6
  //
6
- // 指标(name) skipped errored failed passed better
7
- // passRate(pass-rate) null 0 0 1 higher
8
- // examScore(exam-score) null 0 0 soft 均分 higher
9
- // durationMs(duration) null 实测 实测 实测 lower
10
- // tokens(tokens) null 实测;无 usage→null 同左 同左 lower
11
- // costUSD(cost) null 同上 同左 同左 lower
12
- // turns(turns) null 实测;o11y 缺失→null 同左 同左 lower
7
+ // 指标(name) skipped errored failed passed better
8
+ // taskPassRate(task-pass-rate) null null 0 1 higher
9
+ // executionReliability(execution-reliability) null 0 1 1 higher
10
+ // endToEndPassRate(end-to-end-pass-rate) null 0 0 1 higher
11
+ // examScore(exam-score) null 0 0 soft 均分 higher
12
+ // durationMs(duration) null 实测 实测 实测 lower
13
+ // tokens(tokens) null 实测;无 usage→null 同左 同左 lower
14
+ // costUSD(cost) null 同上 同左 同左 lower
15
+ // assistantTurns(assistant-turns) null 实测;o11y 缺失→null 同左 同左 lower
16
+ // repeatedFailedCommands(repeated-failed-commands) null 实测;o11y 缺失→null 同左 同左 lower
13
17
  //
14
- // 两档指标(docs/feature/reports/library.md「内置指标」):以上除 turns 外全部只读 attempt.result
15
- // 的瘦身字段——任何 producer、任何 copySnapshots artifacts 选择都算得出,内置报告
16
- // ExperimentComparison 只用这一档。turns attempt.o11y()(懒加载 artifact),发布时若
17
- // o11y 没随行就诚实渲染缺数据「—」,不算 0——报告作者自己摆时心里要有这根弦,内置报告不用它。
18
+ // 两档指标(docs/feature/reports/library/metrics.md「内置指标」):以上除 assistantTurns
19
+ // repeatedFailedCommands 外全部只读 attempt.result 的瘦身字段——任何 producer、任何
20
+ // copySnapshots artifacts 选择都算得出,内置报告 ExperimentComparison 只用这一档。
21
+ // 后两个读 attempt.o11y()(懒加载 artifact),发布时若 o11y 没随行就诚实渲染缺数据「—」,
22
+ // 不算 0——报告作者自己摆时心里要有这根弦,内置报告不用它们。
18
23
 
19
24
  import type { EvalResult } from "../types.ts";
20
25
  import type { Metric } from "./types.ts";
@@ -39,10 +44,58 @@ export function attemptCostUSD(result: EvalResult): number | null {
39
44
  return result.usage?.costUSD ?? result.estimatedCostUSD ?? null;
40
45
  }
41
46
 
42
- export const passRate = defineMetric({
43
- name: "pass-rate",
44
- label: { en: "Pass rate", "zh-CN": "成功率" },
45
- description: "Share of evals that passed (skipped attempts excluded).",
47
+ /**
48
+ * 条件答题质量:passed = 1,failed = 0,errored 记 null 不进分母。
49
+ * 这是「已形成可信判定」条件下的诊断指标,不能简称默认成功率
50
+ * (docs/feature/reports/library.md「内置指标」)
51
+ */
52
+ export const taskPassRate = defineMetric({
53
+ name: "task-pass-rate",
54
+ label: { en: "Task pass rate", "zh-CN": "可判定任务通过率" },
55
+ description: "Conditional task quality among attempts that formed a trustworthy verdict: passed = 1, failed = 0; errored is null.",
56
+ better: "higher",
57
+ unit: "%",
58
+ value(a) {
59
+ switch (a.result.verdict) {
60
+ case "passed":
61
+ return 1;
62
+ case "failed":
63
+ return 0;
64
+ default:
65
+ // errored = 没形成可信判定 → null 不进这个条件指标;skipped 同为 null。
66
+ return null;
67
+ }
68
+ },
69
+ });
70
+
71
+ /** 执行可靠性:跑到可判定(passed / failed)= 1,errored = 0;skipped → null。 */
72
+ export const executionReliability = defineMetric({
73
+ name: "execution-reliability",
74
+ label: { en: "Execution reliability", "zh-CN": "执行可靠性" },
75
+ description: "Execution reliability: reached a trustworthy verdict (passed / failed) = 1, errored = 0.",
76
+ better: "higher",
77
+ unit: "%",
78
+ value(a) {
79
+ switch (a.result.verdict) {
80
+ case "passed":
81
+ case "failed":
82
+ return 1;
83
+ case "errored":
84
+ return 0;
85
+ default:
86
+ return null; // skipped
87
+ }
88
+ },
89
+ });
90
+
91
+ /**
92
+ * 端到端合成:passed = 1,failed / errored = 0;哪边拖累用
93
+ * taskPassRate / executionReliability 拆开看。
94
+ */
95
+ export const endToEndPassRate = defineMetric({
96
+ name: "end-to-end-pass-rate",
97
+ label: { en: "End-to-end pass rate", "zh-CN": "端到端成功率" },
98
+ description: "End-to-end composite: passed = 1, failed / errored = 0. Split blame with taskPassRate and executionReliability.",
46
99
  better: "higher",
47
100
  unit: "%",
48
101
  value: (a) =>
@@ -62,9 +115,11 @@ export const examScore = defineMetric({
62
115
  // 字面实现会让条件空真成立、崩溃反而得满分 —— 交白卷是 0 分,不是缺数据,更不是满分。
63
116
  // failed 同理得 0:--strict 下被翻成 failed 的哪怕 soft 分不低也是 0(报告不重新判卷)。
64
117
  if (verdict !== "passed") return 0;
65
- const soft = assertions.filter((x) => x.severity === "soft");
118
+ // unavailable 没有分数:不计入均分分母(评不了 0 分;非 optional 的 unavailable
119
+ // 早已把 verdict 拖成 errored,走不到这个分支)。
120
+ const soft = assertions.filter((x) => x.severity === "soft" && x.outcome !== "unavailable");
66
121
  if (soft.length === 0) return 1;
67
- return soft.reduce((sum, x) => sum + x.score, 0) / soft.length;
122
+ return soft.reduce((sum, x) => sum + (x.outcome === "unavailable" ? 0 : x.score), 0) / soft.length;
68
123
  },
69
124
  });
70
125
 
@@ -95,7 +150,7 @@ export const tokens = defineMetric({
95
150
 
96
151
  export const costUSD = defineMetric({
97
152
  name: "cost",
98
- label: { en: "Cost", "zh-CN": "预估成本" },
153
+ label: { en: "Cost", "zh-CN": "成本" },
99
154
  description: "USD cost per attempt (gateway-measured beats estimated).",
100
155
  better: "lower",
101
156
  unit: "$",
@@ -103,14 +158,15 @@ export const costUSD = defineMetric({
103
158
  });
104
159
 
105
160
  /**
106
- * 唯一读 artifact(o11y,懒加载)的内置指标——其余五个只读瘦身字段。
161
+ * artifact(o11y,懒加载)的内置指标之一——其余只读瘦身字段。
107
162
  * 发布时若该 attempt 没带 o11y(如 copySnapshots 的 artifacts 选项漏了它),
108
- * value 如实返回 null,渲染成「—」,不冒充 0——见 docs/feature/reports/library.md「内置指标」。
163
+ * value 如实返回 null,渲染成「—」,不冒充 0。名字带限定词:o11y 事件流中的 assistant
164
+ * turn 数与 `t.send` 的 `s<session>/t<turn>` 轮次是两个计数。
109
165
  */
110
- export const turns = defineMetric({
111
- name: "turns",
112
- label: { en: "Turns", "zh-CN": "轮次" },
113
- description: "Total agent turns (assistant replies) per attempt. Reads o11y — “—” if not published alongside this attempt.",
166
+ export const assistantTurns = defineMetric({
167
+ name: "assistant-turns",
168
+ label: { en: "Assistant turns", "zh-CN": "Assistant 轮次" },
169
+ description: "Assistant turns in the o11y event stream per attempt. Reads o11y — “—” if not published alongside this attempt.",
114
170
  better: "lower",
115
171
  unit: "turns",
116
172
  async value(a) {
@@ -119,3 +175,30 @@ export const turns = defineMetric({
119
175
  return o11y?.totalTurns ?? null;
120
176
  },
121
177
  });
178
+
179
+ /**
180
+ * 同一 attempt 内同一条 shell 命令的重复失败数:每条命令失败 n 次(n > 1)记 n − 1,求和。
181
+ * 成功执行与只失败一次的命令不计。回答 agent 是否在反复撞同一个已知失败的命令。
182
+ * 读 o11y.json;skipped 与缺 o11y 返回 null(docs/feature/reports/library/metrics.md「内置指标」)。
183
+ */
184
+ export const repeatedFailedCommands = defineMetric({
185
+ name: "repeated-failed-commands",
186
+ label: { en: "Repeated failed commands", "zh-CN": "重复失败命令" },
187
+ description: "Per attempt: for each shell command failing n > 1 times, count n − 1, summed. Reads o11y — “—” if not published alongside this attempt.",
188
+ better: "lower",
189
+ unit: "cmds",
190
+ async value(a) {
191
+ if (a.result.verdict === "skipped") return null;
192
+ const o11y = await a.o11y();
193
+ if (!o11y) return null;
194
+ const failures = new Map<string, number>();
195
+ for (const entry of o11y.shellCommands) {
196
+ const failed = entry.success === false || (entry.success === undefined && entry.exitCode !== undefined && entry.exitCode !== 0);
197
+ if (!failed) continue;
198
+ failures.set(entry.command, (failures.get(entry.command) ?? 0) + 1);
199
+ }
200
+ let repeated = 0;
201
+ for (const n of failures.values()) if (n > 1) repeated += n - 1;
202
+ return repeated;
203
+ },
204
+ });