niceeval 0.6.1 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (296) hide show
  1. package/dist/agents/types.d.ts +67 -5
  2. package/dist/context/types.d.ts +32 -12
  3. package/dist/i18n/en.d.ts +54 -0
  4. package/dist/i18n/zh-CN.d.ts +55 -1
  5. package/dist/o11y/types.d.ts +16 -2
  6. package/dist/report/aggregate.d.ts +5 -3
  7. package/dist/report/aggregate.js +32 -5
  8. package/dist/report/built-ins/experiment-comparison.d.ts +39 -1
  9. package/dist/report/built-ins/experiment-comparison.js +116 -10
  10. package/dist/report/built-ins/index.d.ts +1 -0
  11. package/dist/report/built-ins/index.js +1 -1
  12. package/dist/report/components.d.ts +8 -2
  13. package/dist/report/components.js +3 -3
  14. package/dist/report/compute.d.ts +11 -18
  15. package/dist/report/compute.js +54 -34
  16. package/dist/report/flag.d.ts +16 -1
  17. package/dist/report/flag.js +19 -1
  18. package/dist/report/format.d.ts +16 -8
  19. package/dist/report/format.js +27 -12
  20. package/dist/report/index.d.ts +4 -3
  21. package/dist/report/index.js +5 -4
  22. package/dist/report/locale.d.ts +11 -2
  23. package/dist/report/locale.js +23 -5
  24. package/dist/report/metrics.d.ts +13 -1
  25. package/dist/report/metrics.js +65 -14
  26. package/dist/report/primitives.d.ts +6 -0
  27. package/dist/report/react/AttemptList.d.ts +2 -2
  28. package/dist/report/react/AttemptList.js +5 -6
  29. package/dist/report/react/EvalList.d.ts +1 -1
  30. package/dist/report/react/EvalList.js +0 -0
  31. package/dist/report/react/ExperimentComparison.d.ts +8 -0
  32. package/dist/report/react/ExperimentComparison.js +11 -0
  33. package/dist/report/react/ExperimentList.d.ts +2 -1
  34. package/dist/report/react/ExperimentList.js +8 -10
  35. package/dist/report/react/MetricScatter.js +5 -11
  36. package/dist/report/react/chart-math.d.ts +23 -6
  37. package/dist/report/react/chart-math.js +71 -19
  38. package/dist/report/react/fixtures.d.ts +3 -3
  39. package/dist/report/react/fixtures.js +21 -14
  40. package/dist/report/report.d.ts +5 -1
  41. package/dist/report/report.js +6 -2
  42. package/dist/report/text/faces.d.ts +1 -1
  43. package/dist/report/text/faces.js +42 -41
  44. package/dist/report/text/table.js +36 -5
  45. package/dist/report/types.d.ts +39 -21
  46. package/dist/results/types.d.ts +11 -0
  47. package/dist/runner/feedback/sink.d.ts +110 -0
  48. package/dist/runner/types.d.ts +513 -22
  49. package/dist/sandbox/docker.d.ts +23 -2
  50. package/dist/sandbox/e2b.d.ts +15 -1
  51. package/dist/sandbox/errors.d.ts +30 -3
  52. package/dist/sandbox/io-retry.d.ts +17 -0
  53. package/dist/sandbox/registry.d.ts +2 -0
  54. package/dist/sandbox/resolve.d.ts +18 -5
  55. package/dist/sandbox/retry.d.ts +11 -1
  56. package/dist/sandbox/types.d.ts +39 -5
  57. package/dist/sandbox/vercel.d.ts +7 -1
  58. package/dist/scoring/coverage.d.ts +30 -0
  59. package/dist/scoring/display.d.ts +21 -0
  60. package/dist/scoring/display.js +120 -0
  61. package/dist/scoring/types.d.ts +103 -20
  62. package/dist/shared/aggregate.d.ts +1 -0
  63. package/dist/shared/aggregate.js +3 -3
  64. package/dist/shared/types.d.ts +28 -0
  65. package/dist/tty-line.d.ts +0 -4
  66. package/dist/util.d.ts +23 -0
  67. package/docs-site/zh/concepts/adapter.mdx +22 -4
  68. package/docs-site/zh/concepts/experiment.mdx +1 -1
  69. package/docs-site/zh/concepts/overview.mdx +6 -6
  70. package/docs-site/zh/guides/agent-feedback-loop.mdx +28 -26
  71. package/docs-site/zh/guides/authoring.mdx +33 -0
  72. package/docs-site/zh/guides/ci-integration.mdx +23 -12
  73. package/docs-site/zh/guides/connect-your-agent.mdx +29 -3
  74. package/docs-site/zh/guides/custom-reports.mdx +29 -34
  75. package/docs-site/zh/guides/dataset-fanout.mdx +25 -3
  76. package/docs-site/zh/guides/debug-sandbox.mdx +57 -0
  77. package/docs-site/zh/guides/debugging.mdx +210 -0
  78. package/docs-site/zh/guides/experiments.mdx +10 -3
  79. package/docs-site/zh/guides/official-adapters.mdx +26 -2
  80. package/docs-site/zh/guides/publish-report.mdx +30 -16
  81. package/docs-site/zh/guides/report-components.mdx +42 -30
  82. package/docs-site/zh/guides/reporters.mdx +2 -2
  83. package/docs-site/zh/guides/results-data.mdx +17 -9
  84. package/docs-site/zh/guides/runner.mdx +17 -7
  85. package/docs-site/zh/guides/sandbox-agent.mdx +56 -7
  86. package/docs-site/zh/guides/sandbox-providers.mdx +257 -9
  87. package/docs-site/zh/guides/scoring-guide.mdx +4 -4
  88. package/docs-site/zh/guides/viewing-results.mdx +79 -36
  89. package/docs-site/zh/guides/write-experiment.mdx +5 -3
  90. package/docs-site/zh/guides/write-send.mdx +17 -1
  91. package/docs-site/zh/index.mdx +1 -1
  92. package/docs-site/zh/reference/builtin-agents.mdx +27 -0
  93. package/docs-site/zh/reference/capabilities.mdx +2 -2
  94. package/docs-site/zh/reference/cli.mdx +33 -7
  95. package/docs-site/zh/reference/define-agent.mdx +57 -4
  96. package/docs-site/zh/reference/define-config.mdx +1 -1
  97. package/docs-site/zh/reference/define-eval.mdx +42 -9
  98. package/docs-site/zh/reference/expect.mdx +26 -1
  99. package/package.json +5 -1
  100. package/src/agents/ai-sdk-otel.test.ts +1 -0
  101. package/src/agents/ai-sdk.test.ts +3 -0
  102. package/src/agents/ai-sdk.ts +3 -0
  103. package/src/agents/bub-install-spec.test.ts +34 -0
  104. package/src/agents/bub-install-spec.ts +32 -0
  105. package/src/agents/bub.ts +31 -32
  106. package/src/agents/claude-code.test.ts +130 -9
  107. package/src/agents/claude-code.ts +76 -4
  108. package/src/agents/codex.test.ts +189 -40
  109. package/src/agents/codex.ts +155 -14
  110. package/src/agents/coding-cli-versions.test.ts +15 -0
  111. package/src/agents/coding-cli-versions.ts +3 -0
  112. package/src/agents/index.ts +11 -0
  113. package/src/agents/langgraph.test.ts +204 -0
  114. package/src/agents/langgraph.ts +495 -0
  115. package/src/agents/marketplace.ts +85 -0
  116. package/src/agents/native-config.test.ts +179 -0
  117. package/src/agents/native-config.ts +267 -0
  118. package/src/agents/openai-compat.test.ts +1 -0
  119. package/src/agents/openclaw.test.ts +31 -0
  120. package/src/agents/openclaw.ts +171 -0
  121. package/src/agents/plugin-config.test.ts +1 -0
  122. package/src/agents/sdk-streams.test.ts +79 -0
  123. package/src/agents/sdk-streams.ts +55 -10
  124. package/src/agents/skills.test.ts +1 -0
  125. package/src/agents/streaming.test.ts +3 -9
  126. package/src/agents/types.ts +68 -5
  127. package/src/agents/ui-message-stream.test.ts +3 -0
  128. package/src/cli.ts +411 -108
  129. package/src/context/context.test.ts +51 -12
  130. package/src/context/context.ts +161 -29
  131. package/src/context/session.test.ts +1 -0
  132. package/src/context/session.ts +114 -6
  133. package/src/context/types.ts +30 -12
  134. package/src/define.test.ts +13 -8
  135. package/src/define.ts +25 -4
  136. package/src/expect/index.ts +53 -23
  137. package/src/i18n/en.ts +64 -2
  138. package/src/i18n/zh-CN.ts +65 -3
  139. package/src/o11y/cost.test.ts +1 -0
  140. package/src/o11y/execution-tree.test.ts +1 -20
  141. package/src/o11y/otlp/mappers/claude-code.test.ts +1 -0
  142. package/src/o11y/otlp/parse.test.ts +1 -0
  143. package/src/o11y/otlp/turn-otel.test.ts +1 -0
  144. package/src/o11y/parsers/bub.test.ts +1 -0
  145. package/src/o11y/parsers/claude-code.test.ts +1 -34
  146. package/src/o11y/parsers/openclaw.test.ts +154 -0
  147. package/src/o11y/parsers/openclaw.ts +310 -0
  148. package/src/o11y/prices.json +746 -311
  149. package/src/o11y/tool-names.test.ts +1 -0
  150. package/src/o11y/types.ts +16 -2
  151. package/src/report/aggregate.ts +34 -5
  152. package/src/report/built-in-user-parity.test.tsx +110 -153
  153. package/src/report/built-ins/experiment-comparison.tsx +173 -13
  154. package/src/report/built-ins/index.ts +6 -1
  155. package/src/report/components.tsx +9 -3
  156. package/src/report/compute.ts +70 -40
  157. package/src/report/dual-render.test.tsx +194 -67
  158. package/src/report/flag.ts +30 -2
  159. package/src/report/format.ts +35 -11
  160. package/src/report/index.ts +22 -4
  161. package/src/report/locale.ts +25 -5
  162. package/src/report/metrics.ts +67 -14
  163. package/src/report/primitives.tsx +6 -0
  164. package/src/report/react/AttemptList.tsx +6 -31
  165. package/src/report/react/EvalList.tsx +0 -0
  166. package/src/report/react/ExperimentComparison.tsx +68 -0
  167. package/src/report/react/ExperimentList.tsx +15 -9
  168. package/src/report/react/MetricScatter.tsx +12 -14
  169. package/src/report/react/chart-math.test.ts +85 -0
  170. package/src/report/react/chart-math.ts +101 -22
  171. package/src/report/react/enhance.js +33 -1
  172. package/src/report/react/fixtures.ts +24 -17
  173. package/src/report/react/render.test.tsx +9 -64
  174. package/src/report/react/styles.css +73 -2
  175. package/src/report/report.test.ts +306 -98
  176. package/src/report/report.ts +6 -2
  177. package/src/report/text/faces.ts +47 -43
  178. package/src/report/text/table.ts +42 -5
  179. package/src/report/types.ts +41 -21
  180. package/src/results/annotated-source.test.ts +62 -9
  181. package/src/results/annotated-source.ts +64 -6
  182. package/src/results/attempt-evidence.test.ts +9 -7
  183. package/src/results/attempt-evidence.ts +15 -8
  184. package/src/results/attempt-source.ts +6 -3
  185. package/src/results/copy.ts +145 -55
  186. package/src/results/host-equivalence.test.ts +8 -6
  187. package/src/results/index.ts +2 -0
  188. package/src/results/locator.test.ts +1 -22
  189. package/src/results/open.ts +7 -1
  190. package/src/results/publish.ts +149 -0
  191. package/src/results/results.test.ts +85 -51
  192. package/src/results/truncate.ts +90 -0
  193. package/src/results/types.ts +7 -0
  194. package/src/results/writer.ts +31 -13
  195. package/src/runner/attempt.test.ts +138 -7
  196. package/src/runner/attempt.ts +603 -104
  197. package/src/runner/discover.test.ts +47 -0
  198. package/src/runner/discover.ts +36 -2
  199. package/src/runner/eval-source.test.ts +1 -27
  200. package/src/runner/feedback/agent.test.ts +504 -0
  201. package/src/runner/feedback/agent.ts +409 -0
  202. package/src/runner/feedback/ci.test.ts +562 -0
  203. package/src/runner/feedback/ci.ts +401 -0
  204. package/src/runner/feedback/coordinator.test.ts +317 -0
  205. package/src/runner/feedback/coordinator.ts +397 -0
  206. package/src/runner/feedback/failure.ts +40 -0
  207. package/src/runner/feedback/human.test.ts +616 -0
  208. package/src/runner/feedback/human.ts +535 -0
  209. package/src/runner/feedback/index.ts +66 -0
  210. package/src/runner/feedback/io.ts +78 -0
  211. package/src/runner/feedback/profile.test.ts +50 -0
  212. package/src/runner/feedback/profile.ts +58 -0
  213. package/src/runner/feedback/reducer.test.ts +395 -0
  214. package/src/runner/feedback/reducer.ts +260 -0
  215. package/src/runner/feedback/renderer.ts +82 -0
  216. package/src/runner/feedback/sink.ts +203 -0
  217. package/src/runner/feedback/testing.ts +106 -0
  218. package/src/runner/ledger.test.ts +230 -0
  219. package/src/runner/ledger.ts +329 -0
  220. package/src/runner/report.test.ts +128 -3
  221. package/src/runner/report.ts +33 -9
  222. package/src/runner/reporters/artifacts.ts +8 -2
  223. package/src/runner/reporters/braintrust.test.ts +8 -7
  224. package/src/runner/reporters/braintrust.ts +9 -2
  225. package/src/runner/reporters/index.ts +2 -2
  226. package/src/runner/reporters/json.test.ts +162 -0
  227. package/src/runner/reporters/json.ts +35 -8
  228. package/src/runner/reporters/shared.ts +1 -5
  229. package/src/runner/run.test.ts +760 -3
  230. package/src/runner/run.ts +242 -36
  231. package/src/runner/sandbox-prep.ts +3 -42
  232. package/src/runner/timing.ts +158 -0
  233. package/src/runner/types.ts +518 -22
  234. package/src/sandbox/checkpoint.test.ts +55 -0
  235. package/src/sandbox/checkpoint.ts +29 -8
  236. package/src/sandbox/cli-commands.ts +407 -0
  237. package/src/sandbox/docker.ts +115 -16
  238. package/src/sandbox/e2b-agent-template.test.ts +56 -0
  239. package/src/sandbox/e2b-agent-template.ts +94 -0
  240. package/src/sandbox/e2b.ts +74 -9
  241. package/src/sandbox/errors.ts +111 -4
  242. package/src/sandbox/index.ts +2 -0
  243. package/src/sandbox/io-retry.test.ts +58 -0
  244. package/src/sandbox/io-retry.ts +45 -0
  245. package/src/sandbox/keep-registry.test.ts +86 -0
  246. package/src/sandbox/keep-registry.ts +142 -0
  247. package/src/sandbox/keep.ts +178 -0
  248. package/src/sandbox/paths.test.ts +1 -0
  249. package/src/sandbox/paths.ts +19 -8
  250. package/src/sandbox/registry.ts +20 -3
  251. package/src/sandbox/resolve.ts +76 -11
  252. package/src/sandbox/retry.test.ts +70 -0
  253. package/src/sandbox/retry.ts +46 -4
  254. package/src/sandbox/types.ts +44 -6
  255. package/src/sandbox/vercel.ts +43 -20
  256. package/src/scoring/collector.ts +60 -17
  257. package/src/scoring/coverage.ts +95 -0
  258. package/src/scoring/diff.ts +81 -0
  259. package/src/scoring/display.test.ts +121 -0
  260. package/src/scoring/display.ts +133 -0
  261. package/src/scoring/evidence.test.ts +189 -0
  262. package/src/scoring/judge.test.ts +142 -0
  263. package/src/scoring/judge.ts +15 -18
  264. package/src/scoring/scoped.ts +217 -50
  265. package/src/scoring/types.ts +117 -20
  266. package/src/scoring/verdict.ts +16 -4
  267. package/src/shared/aggregate.ts +3 -2
  268. package/src/shared/types.ts +31 -0
  269. package/src/show/compose.ts +2 -2
  270. package/src/show/index.ts +21 -1
  271. package/src/show/render.ts +619 -104
  272. package/src/show/show.test.ts +235 -19
  273. package/src/tty-line.ts +8 -26
  274. package/src/util.test.ts +1 -0
  275. package/src/util.ts +41 -0
  276. package/src/view/app/components/AttemptModal.tsx +153 -2
  277. package/src/view/app/components/CodeView.tsx +32 -11
  278. package/src/view/app/components/CopyControls.tsx +2 -2
  279. package/src/view/app/i18n.ts +6 -0
  280. package/src/view/app/lib/attempt-route.test.ts +1 -0
  281. package/src/view/app/lib/verdict.ts +7 -9
  282. package/src/view/artifact-serving.test.ts +2 -1
  283. package/src/view/client-dist/app.css +1 -1
  284. package/src/view/client-dist/app.js +17 -17
  285. package/src/view/data.test.ts +1 -0
  286. package/src/view/data.ts +11 -1
  287. package/src/view/index.ts +11 -0
  288. package/src/view/server.ts +2 -0
  289. package/src/view/styles.css +3 -0
  290. package/src/view/view-report.test.ts +6 -5
  291. package/src/runner/reporters/console.ts +0 -70
  292. package/src/runner/reporters/live.test.ts +0 -56
  293. package/src/runner/reporters/live.ts +0 -247
  294. package/src/runner/reporters/quiet.test.ts +0 -66
  295. package/src/runner/reporters/quiet.ts +0 -49
  296. package/src/runner/reporters/table.ts +0 -277
@@ -1,10 +1,16 @@
1
1
  import type { Severity, SourceLoc } from "../shared/types.ts";
2
2
  import type { DerivedFacts, StreamEvent, Usage } from "../o11y/types.ts";
3
- /** 值断言(expect 匹配器)。纯函数 score + 可链式改严重度 / 阈值。 */
3
+ import type { ResolvedCoverage } from "./coverage.ts";
4
+ export type { CoverageChannel, ResolvedCoverage, ResolvedCoverageChannel, ResolvedCoverageStatus, } from "./coverage.ts";
5
+ /** 值断言(expect 匹配器)。纯函数 score + 可链式改严重度 / 阈值 / optional。 */
4
6
  export interface ValueAssertion {
5
7
  readonly name: string;
6
8
  readonly severity: Severity;
7
9
  readonly threshold?: number;
10
+ /** `.optional()` 链过的标记:评不了只记 unavailable,不把 attempt 拖成 errored。 */
11
+ readonly isOptional?: boolean;
12
+ /** 期望条件的有界文本描述(如 `contains "Brooklyn"`),失败时进 AssertionResult.expected。 */
13
+ readonly expected?: string;
8
14
  score(value: unknown): number | Promise<number>;
9
15
  /** 转成硬门槛断言:未达阈值(省略 threshold 则按 score > 0 判定)整条 eval 判为 failed。返回新实例,不改原对象。 */
10
16
  gate(threshold?: number): ValueAssertion;
@@ -13,34 +19,76 @@ export interface ValueAssertion {
13
19
  * `--strict` 运行下,软阈值失败也会把整条 eval 的 verdict 计为 failed。返回新实例,不改原对象。
14
20
  */
15
21
  atLeast(threshold: number): ValueAssertion;
22
+ /**
23
+ * 允许这条断言证据缺席:评不了时只记录 `outcome: "unavailable"`,不影响判定。
24
+ * 与 severity 正交(severity 说影不影响质量判定,optional 说证据允不允许缺席)。返回新实例,不改原对象。
25
+ */
26
+ optional(): ValueAssertion;
16
27
  }
17
- /** 收集到 collector 里的一条断言记录(评估前)。 */
18
- export interface AssertionSpec {
28
+ /**
29
+ * 断言记录的公共字段(见 docs/feature/scoring/architecture.md「断言记录」——字段契约的单点定义)。
30
+ */
31
+ export interface AssertionBase {
32
+ /** 断言标题:t.group 内是该断言自己的摘要,组外是 matcher 摘要或 judge 问题;show/view 失败行的标题。 */
19
33
  name: string;
34
+ /** 所属分组路径:外层在前的 t.group 标题数组;无分组省略。纯报告用,不影响判定。 */
35
+ groupPath?: string[];
20
36
  severity: Severity;
21
- threshold?: number;
22
- /** 延迟评估:final 时拿到完整运行结果再算分。 */
23
- evaluate(ctx: ScoringContext): Promise<number> | number;
37
+ /** 作者用 .optional() 显式允许该断言缺席;只改变 unavailable 的折叠方式(见 Severity 与 Verdict),不改变 severity 语义。 */
38
+ optional?: true;
39
+ /** matcher / judge 摘要,如 `equals(4)`、`closedQA("…")`;与 name 分开,供 show/view 同时展示分组标题与检查方式。 */
40
+ detail?: string;
41
+ /** 断言在 eval 源码中的调用点,`--eval` 把结果标回源码行的锚。 */
42
+ loc?: SourceLoc;
24
43
  }
25
- /** 断言评估完的结果(进判定 / 报告)。 */
26
- export interface AssertionResult {
27
- name: string;
28
- severity: Severity;
29
- threshold?: number;
44
+ /**
45
+ * 断言评估完的结果(进判定 / 报告)。判别键是 `outcome`——`unavailable` 是没有分数的独立态,
46
+ * 普通聚合代码按 `outcome` 分支就不可能把证据缺口算成零分。判定只消费
47
+ * `severity` / `outcome` / `optional` / `score` / `threshold`。
48
+ */
49
+ export type AssertionResult = (AssertionBase & {
50
+ outcome: "passed" | "failed";
51
+ /** 归一化得分:值断言 0/1,judge 等打分断言 0..1。 */
30
52
  score: number;
31
- passed: boolean;
32
- detail?: string;
33
- /** 这条分数是看着什么材料算出来的(judge 收到的输入,或 t.check 失败时实际被检查的值)。view 展开排查「为什么是这个分」,默认不展示。 */
53
+ /** soft 断言的 .atLeast(x) 阈值;没有设阈值则省略。 */
54
+ threshold?: number;
55
+ /** 失败证据摘要:期望值的有界文本预览,供 show/view 直接展示。 */
56
+ expected?: string;
57
+ /** 失败证据摘要:实际值的有界文本预览。 */
58
+ received?: string;
59
+ /** 这条分数看着什么材料算出(judge 输入或被检查值预览);view 展开排查用,默认不展示。 */
34
60
  evidence?: string;
35
- /** 所属分组(t.group 标题)。纯报告用,不影响 passed/score。 */
36
- group?: string;
37
- /** 断言在 eval 源码里的调用点(栈回溯抠出);view 把判定叠回这一行。 */
38
- loc?: SourceLoc;
61
+ }) | (AssertionBase & {
62
+ outcome: "unavailable";
63
+ /** 机器可读原因,如 "judge-model-unresolved"、"coverage:actions=partial"。 */
64
+ reason: string;
65
+ });
66
+ /**
67
+ * 摘要面从完整 `AssertionResult[]` 选出的一条主失败断言。它只负责展示,不参与 verdict;
68
+ * `show @locator` / view Attempt 详情仍读取完整断言数组。字段保持结构化,使 CI 不必解析
69
+ * Human 的 `gate: …` 文案。
70
+ */
71
+ export interface PrimaryAssertionSummary {
72
+ severity: Severity;
73
+ /** `groupPath.join(" > ")`,无 group 时回退到断言 name。 */
74
+ assertion: string;
75
+ /** `detail ?? name`;与 assertion 相同时省略,避免重复。 */
76
+ matcher?: string;
77
+ expected?: string;
78
+ received?: string;
79
+ score?: number;
80
+ threshold?: number;
81
+ /** unavailable 断言的结构化原因。 */
82
+ reason?: string;
83
+ /** 同类因果失败中除主失败外的条数。 */
84
+ additionalFailures: number;
39
85
  }
40
86
  /** eval 作者拿到的可链式句柄(t.judge.autoevals.closedQA(...).atLeast(0.7))。 */
41
87
  export interface AssertionHandle {
42
88
  atLeast(threshold: number): AssertionHandle;
43
89
  gate(threshold?: number): AssertionHandle;
90
+ /** 允许这条断言证据缺席:unavailable 只保留在记录里,不影响判定(见 Severity 与 Verdict)。 */
91
+ optional(): AssertionHandle;
44
92
  }
45
93
  /** scoped / judge 断言在 final 评估时拿到的运行结果。 */
46
94
  export interface ScoringContext {
@@ -50,6 +98,8 @@ export interface ScoringContext {
50
98
  readonly scripts: Record<string, ScriptResult>;
51
99
  readonly usage: Usage;
52
100
  readonly status: "completed" | "failed" | "waiting";
101
+ /** 当前作用域(turn / session / attempt)解析后的证据覆盖;断言按它做三值折叠(见 scoped.ts)。 */
102
+ readonly coverage: ResolvedCoverage;
53
103
  /** 读沙箱里某文件的最终内容(judge / file 断言用)。 */
54
104
  readFile(path: string): Promise<string | undefined>;
55
105
  }
@@ -57,9 +107,42 @@ export interface ScriptResult {
57
107
  success: boolean;
58
108
  output: string;
59
109
  }
110
+ /** diff.json 的落盘形状:按时序的窗口数组(逐窗口 delta 序列,不做跨窗口压缩)。 */
111
+ export type DiffArtifact = DiffWindow[];
112
+ export interface DiffWindow {
113
+ /** send 窗口标签,与时间树 turn 节点、--execution 轮次同源(如 "s1/t2")。 */
114
+ window: string;
115
+ /** 该窗口内 agent 改动的文件;窗口内没有 workspace 变化时窗口仍落一条、changes 为空对象。 */
116
+ changes: Record<string, WindowChange>;
117
+ }
118
+ export interface WindowChange {
119
+ status: "added" | "modified" | "deleted";
120
+ /** 窗口开始时的内容;added 无此字段。 */
121
+ before?: string;
122
+ /** 窗口结束时的内容;deleted 无此字段。 */
123
+ after?: string;
124
+ /** 二进制文件不内联内容,只记字节数。 */
125
+ binary?: {
126
+ beforeBytes?: number;
127
+ afterBytes?: number;
128
+ };
129
+ }
130
+ /** 读取面在窗口序列之上派生的文件级视图(派生物可随时重算,不落盘)。 */
131
+ export interface DiffFileSummary {
132
+ /** 净效果:首个触及窗口的起点 vs 最后触及窗口的终点;"none" = 动过但净无变化(创建又删除、改回原样)。 */
133
+ net: "added" | "modified" | "deleted" | "none";
134
+ /** 触及该文件的窗口标签,按时序。 */
135
+ windows: string[];
136
+ binary?: true;
137
+ }
138
+ /** agent 归因 diff 的消费视图:窗口序列(落盘事实)+ 派生的文件级摘要与终态读取。 */
60
139
  export interface DiffData {
61
- generatedFiles: Record<string, string>;
62
- deletedFiles: string[];
140
+ /** 落盘事实,原样。 */
141
+ windows: DiffWindow[];
142
+ /** 派生:每个被 agent 触及的文件一条。 */
143
+ files: Record<string, DiffFileSummary>;
144
+ /** 该文件最后一个触及窗口结束时的内容;净删除或从未触及返回 undefined。t.sandbox.diff.get 同一语义。 */
145
+ get(path: string): string | undefined;
63
146
  }
64
147
  export type Verdict = "passed" | "failed" | "errored" | "skipped";
65
148
  export interface JudgeConfig {
@@ -22,6 +22,7 @@ export declare function experimentGroupOf(experimentId: string): string | undefi
22
22
  export declare function evalPrefixPredicate(evals?: string | string[]): (id: string) => boolean;
23
23
  /** 无 experimentId 时的兜底标签。 */
24
24
  export declare function fallbackExperimentLabel(result: {
25
+ experimentId?: string;
25
26
  experiment?: ExperimentRunInfo;
26
27
  agent: string;
27
28
  model?: string;
@@ -1,4 +1,4 @@
1
- // CLI 表格(runner/reporters/table.ts)与 view 榜单(view/aggregate.ts)共用的聚合小工具。
1
+ // report 聚合(report/aggregate.ts)与 view 榜单(view/app/lib/rows.ts)共用的聚合小工具。
2
2
  // 实验标签推导、token/成本求和、verdict 排序各只有一份 —— 否则同一个实验在终端和网页上
3
3
  // 会显示成两个名字 / 两组数。保持环境无关(纯函数,只 type import)。
4
4
  /** 明细行排序:失败最靠前(failed > errored > skipped > passed 的紧急程度)。 */
@@ -47,8 +47,8 @@ export function evalPrefixPredicate(evals) {
47
47
  }
48
48
  /** 无 experimentId 时的兜底标签。 */
49
49
  export function fallbackExperimentLabel(result) {
50
- if (result.experiment?.id)
51
- return displayExperimentName(result.experiment.id) ?? result.experiment.id;
50
+ if (result.experimentId)
51
+ return displayExperimentName(result.experimentId) ?? result.experimentId;
52
52
  if (result.model)
53
53
  return `${result.agent}/${result.model}`;
54
54
  return result.agent || "ad hoc run";
@@ -20,6 +20,34 @@ export interface SourceArtifact {
20
20
  }
21
21
  /** 通用清理闭包(setup 返回值 / teardown 的形状),异步同步皆可,统一在 finally 里执行。 */
22
22
  export type Cleanup = () => Promise<void> | void;
23
+ /** `ScopedFeedback.progress` 的入参:此刻正在做什么(短命状态,可被后续更新覆盖)。 */
24
+ export interface ProgressUpdate {
25
+ message: string;
26
+ current?: number;
27
+ total?: number;
28
+ }
29
+ /** `ScopedFeedback.diagnostic` 的入参:运行结束后仍应保留的问题(永久事件)。 */
30
+ export interface DiagnosticInput {
31
+ code: string;
32
+ level: "warning" | "error";
33
+ message: string;
34
+ data?: Readonly<Record<string, JsonValue>>;
35
+ /** 并发 attempt 产生同一问题时的去重键;相同 key 折叠成一条并累计次数。 */
36
+ dedupeKey?: string;
37
+ }
38
+ /**
39
+ * 作用域反馈 API(见 docs/feature/experiments/library.md「生命周期代码怎样向这次运行反馈」):
40
+ * sandbox provider、sandbox hook、eval 与 Agent Adapter 从 runner 注入的上下文获得同一套入口。
41
+ * - `progress` 是短命状态:Human profile 更新 active 行,Agent/CI 不逐条打印,不进最终结果;
42
+ * - `diagnostic` 是永久事件:进 Human/Agent/CI 的永久输出流并落进 attempt 的 diagnostics;
43
+ * 即使 level 为 "error" 也不自动改变 verdict(要 errored 抛异常,要 failed 用断言)。
44
+ * 两个方法都不接受 phase / scope / 颜色 / 输出流——runner 知道当前回调属于哪个生命周期阶段,
45
+ * 调用方不能冒充其它阶段。
46
+ */
47
+ export interface ScopedFeedback {
48
+ progress(update: ProgressUpdate): void;
49
+ diagnostic(input: DiagnosticInput): void;
50
+ }
23
51
  /**
24
52
  * 可本地化文案:纯字符串,或按 locale 代码(如 "en"、"zh-CN")映射多语言。
25
53
  * view 按当前界面语言挑一条,挑不到回退到 en / 第一条。
@@ -1,6 +1,2 @@
1
- /** live.ts 用来订阅"即将有一行独立诊断消息落地"。返回取消订阅函数。 */
2
- export declare function onBeforeExternalTerminalWrite(fn: () => void): () => void;
3
- /** 任何要绕开 Reporter 直接往终端打一行独立诊断消息的地方,写之前都先调用这个。 */
4
- export declare function beforeExternalTerminalWrite(): void;
5
1
  /** process.stderr.write 的替代:文本需自带换行(沿用现有 i18n 字符串的约定)。 */
6
2
  export declare function writeStderrLine(text: string): void;
package/dist/util.d.ts CHANGED
@@ -10,6 +10,29 @@ export declare function stripComments(code: string): string;
10
10
  * `e instanceof Error ? e.message : String(e)`——那样用户永远看不出错误发生在哪一行。
11
11
  */
12
12
  export declare function formatThrown(e: unknown): string;
13
+ /**
14
+ * 截到第一个换行为止。`formatThrown()` 优先带完整 `.stack`(含本地绝对文件路径的多行调用栈)
15
+ * 给需要按 file:line 定位问题的落盘产物(`EvalResult.error`、`niceeval show`);但机器消费的
16
+ * 单行 envelope(`FailureNotice.reason`、reporter 失败诊断的 `message`……)只要「一层可行动摘要」
17
+ * ——`Error.stack` 的第一行恒为 `name: message`,不含栈帧,直接满足这个要求。完整栈仍然原样
18
+ * 留在调用方各自的落盘字段里,这个函数只负责第二次、更短的那份表达,不是唯一出口。
19
+ */
20
+ export declare function firstLine(text: string): string;
21
+ /**
22
+ * 把 catch 到的 e 拆成 `AttemptError` 的三段:`message`(一层原因,Error.message,不带 name 前缀、
23
+ * 不拼 stack)、`stack`(完整多行栈,供 `niceeval show` 展开)、`cause`(沿 `e.cause` 取一层结构化
24
+ * 摘要 `{ name?, code?, message }`)。Node 的 `Error.stack` 不展开 cause 链,所以 cause 只能从
25
+ * `e.cause` 单独取。非 Error 值只给 message。
26
+ */
27
+ export declare function describeError(e: unknown): {
28
+ message: string;
29
+ stack?: string;
30
+ cause?: {
31
+ name?: string;
32
+ code?: string;
33
+ message: string;
34
+ };
35
+ };
13
36
  /** 零填充到 4 位(数据集扇出的 id:sql/0000)。 */
14
37
  export declare function pad4(n: number): string;
15
38
  /**
@@ -37,6 +37,7 @@ export function webAgent(options: WebAgentOptions): Agent {
37
37
  return defineAgent({
38
38
  name: "web-agent",
39
39
  async send(input, ctx) {
40
+ ctx.progress({ message: "等待 Web Agent" });
40
41
  const response = await fetch(`${baseUrl}/api/turn`, {
41
42
  method: "POST",
42
43
  headers: {
@@ -54,6 +55,12 @@ export function webAgent(options: WebAgentOptions): Agent {
54
55
  });
55
56
 
56
57
  if (!response.ok) {
58
+ ctx.diagnostic({
59
+ code: "agent-http-error",
60
+ level: "error",
61
+ message: `Web Agent 返回 HTTP ${response.status}`,
62
+ data: { status: response.status },
63
+ });
57
64
  return {
58
65
  status: "failed",
59
66
  events: [{ type: "error", message: `HTTP ${response.status}` }],
@@ -107,10 +114,12 @@ interface Agent {
107
114
  }
108
115
  ```
109
116
 
117
+ `progress` 是运行中的短期状态,不落盘;`diagnostic` 是需要运行后回顾的有界 warning/error,会随 Attempt 保存。它们都不能指定 lifecycle phase,也不会自动改变 `Turn.status`。连接失败或响应无法解析时抛出异常;runner 会把它记录为 `errored`,并在终端给出可下钻的 locator。
118
+
110
119
  `send` 是唯一要实现的函数。它的签名里只有三个类型,分开看。
111
120
 
112
121
  <Note>
113
- 这里的 `setup` / `teardown` 是 Agent 自己"怎么连自己"的私事(装 CLI、写鉴权配置)。按实验变化的环境准备(装某个实验专属的二进制、预热、跨 attempt 保存状态)不写在 Agent 上,挂在 `sandbox` 字段那个 Sandbox spec 自己的 `.setup()` / `.teardown()` 链式方法上,见 [Sandbox 后端 · 环境钩子](/zh/guides/sandbox-providers#环境钩子)。
122
+ 这里的 `setup` / `teardown` 是 Agent 自己"怎么连自己"的私事(装 CLI、写鉴权配置)。按实验变化的环境准备(装某个实验专属的二进制、预热、跨 attempt 保存状态)不写在 Agent 上,挂在 `sandbox` 字段那个 Sandbox spec 自己的 `.setup()` / `.teardown()` 链式方法上,见 [沙箱 provider · 环境钩子](/zh/guides/sandbox-providers#环境钩子)。
114
123
  </Note>
115
124
 
116
125
  ### 传入:`TurnInput`
@@ -182,10 +191,17 @@ Adapter 侧的对应义务:按 `requestId` 把裁决交回应用(不要按
182
191
 
183
192
  ```ts
184
193
  interface AgentContext {
185
- // 每轮都在的三个
194
+ // 每轮都在的反馈与运行上下文
186
195
  readonly signal: AbortSignal; // 运行器的超时与取消:透传给你发出的每个请求
187
196
  readonly session: AgentSession; // 本条会话线的状态槽
188
- log(msg: string): void; // 写进本轮日志,niceeval view 里可见
197
+ progress(update: { message: string; current?: number; total?: number }): void;
198
+ diagnostic(input: {
199
+ code: string;
200
+ level: "warning" | "error";
201
+ message: string;
202
+ data?: Readonly<Record<string, JsonValue>>;
203
+ dedupeKey?: string;
204
+ }): void;
189
205
 
190
206
  // 按接入档位出现的三个(见「接入分三个 Tier」)
191
207
  readonly model?: string; // Tier 1 的模型对比:experiment.model 透传;应用接口收模型选择就转发
@@ -214,7 +230,9 @@ interface AgentSession {
214
230
  }
215
231
  ```
216
232
 
217
- `ctx` 里没有要你检查的标志位。三个档位字段都是"透传"语义:`model`、`flags` experiment 声明、运行器原样递过来,Adapter 只负责随请求转发给应用,不解释它们的含义;`telemetry` 仅在配置了 OTel 接入时出现,`send` 里只需要把 `headers` spread 进请求头——接收端点在 `defineConfig` 里固定、由应用启动时指向,不从这里传,见[OTel 接入](/zh/guides/connect-otel)。`experimentId` 是路径推导出的稳定标识,典型用途是在 Sandbox 的环境钩子里按实验隔离跨 attempt 的状态(缓存目录名、沙箱快照 tag 按它分区),见 [Sandbox 后端 · 环境钩子](/zh/guides/sandbox-providers#环境钩子)。
233
+ Runner Agent `setup`、每次 `send` `teardown` 分别绑定 lifecycle scope。Adapter 只报告当前回调内部的 progress/diagnostic,不能传 phase、颜色或输出流。`progress` 不落盘;diagnostic 会进入 Attempt `result.json`。无法继续时抛错,由 runner 保存结构化 error 并把 Attempt 标为 `errored`。
234
+
235
+ `ctx` 里没有要你检查的标志位。三个档位字段都是"透传"语义:`model`、`flags` 由 experiment 声明、运行器原样递过来,Adapter 只负责随请求转发给应用,不解释它们的含义;`telemetry` 仅在配置了 OTel 接入时出现,`send` 里只需要把 `headers` spread 进请求头——接收端点在 `defineConfig` 里固定、由应用启动时指向,不从这里传,见[OTel 接入](/zh/guides/connect-otel)。`experimentId` 是路径推导出的稳定标识,典型用途是在 Sandbox 的环境钩子里按实验隔离跨 attempt 的状态(缓存目录名、沙箱快照 tag 按它分区),见 [沙箱 provider · 环境钩子](/zh/guides/sandbox-providers#环境钩子)。
218
236
 
219
237
  `session` 是本条会话线的自有状态,[NiceEval](https://niceeval.com/) 对它只承诺一件事:**同一条会话线的每次 `send` 拿到同一个 `ctx.session`,新会话线(eval 的第一轮,或 `t.newSession()` 之后)拿到一个全新的。** 会话续接(`id`/`capture`、`history`)和 HITL 停轮现场(`hold`/`take`)的存取器都在它上面,"第一轮"就是新会话线的自然形态——`id` 是 `undefined`、`history.get()` 是空数组,没有要判断的分支;`state` 是这些存取器之外的逃生舱,框架从不往里写数据。
220
238
 
@@ -25,7 +25,7 @@ export default defineExperiment({
25
25
  - `model` / `flags`:透传语义。[NiceEval](https://niceeval.com/) 不解释它们的含义,原样经 `ctx` 递给 Adapter,由 Adapter 随请求转发、应用按需切换——这正是 [Tier](/zh/concepts/tier) 里模型对比(Tier 1)和 feature A/B(Tier 3)的通道。
26
26
  - `runs`、`budget`、并发、`sandbox` 等运行参数:怎么跑、跑多少。完整字段见[写实验](/zh/guides/write-experiment)。
27
27
 
28
- Experiment 是纯配置数据,没有 `setup` / `teardown` 这类生命周期字段。要按实验准备环境(装二进制、预热、跨 attempt 存取状态),挂在 `sandbox` 字段的 spec 上——`dockerSandbox()` 等工厂返回的对象可以链 `.setup()` / `.teardown()`,见 [Sandbox 后端 · 环境钩子](/zh/guides/sandbox-providers#环境钩子)。
28
+ Experiment 是纯配置数据,没有 `setup` / `teardown` 这类生命周期字段。要按实验准备环境(装二进制、预热、跨 attempt 存取状态),挂在 `sandbox` 字段的 spec 上——`dockerSandbox()` 等工厂返回的对象可以链 `.setup()` / `.teardown()`,见 [沙箱 provider · 环境钩子](/zh/guides/sandbox-providers#环境钩子)。
29
29
 
30
30
  ## 矩阵对比
31
31
 
@@ -1,10 +1,10 @@
1
1
  ---
2
2
  title: "NiceEval 架构:evals、agents 与 sandboxes"
3
3
  sidebarTitle: "概览"
4
- description: "理解 NiceEval、Adapter 和 Sandbox backend 如何配合,用统一 API 评估任意 AI agent。"
4
+ description: "理解 NiceEval、Adapter 和 Sandbox Provider 如何配合,用统一 API 评估任意 AI Agent。"
5
5
  ---
6
6
 
7
- [NiceEval](https://niceeval.com/) 的核心设计是把“评测逻辑”与“如何连接被测对象”分开。[NiceEval](https://niceeval.com/) 负责发现、调度、评分和报告;Adapter 负责调用被测系统;Sandbox backend 负责隔离文件系统。
7
+ [NiceEval](https://niceeval.com/) 的核心设计是把“评测逻辑”与“如何连接被测对象”分开。[NiceEval](https://niceeval.com/) 负责发现、调度、评分和报告;Adapter 负责调用被测系统;Sandbox Provider 负责隔离文件系统。
8
8
 
9
9
  ## 四层架构
10
10
 
@@ -15,7 +15,7 @@ NiceEval
15
15
 
16
16
  Adapter
17
17
 
18
- Subject under test / Sandbox backend
18
+ Subject under test / Sandbox Provider
19
19
  ```
20
20
 
21
21
  ## NiceEval 负责什么
@@ -53,12 +53,12 @@ Subject under test / Sandbox backend
53
53
 
54
54
  <Tabs>
55
55
  <Tab title="Docker">
56
- 本地容器后端,适合开发和 CI 中的 coding-agent eval。
56
+ 本地容器 Provider,适合开发和 CI 中的 coding-agent eval。
57
57
  </Tab>
58
58
  <Tab title="Vercel Sandbox">
59
- 云端 sandbox 后端,适合更强隔离或更大的运行资源。
59
+ 云端 Sandbox Provider,适合更强隔离或更大的运行资源。
60
60
  </Tab>
61
- <Tab title="第三方后端">
61
+ <Tab title="第三方 Provider">
62
62
  只要实现 `Sandbox` 接口,就可以接入其他沙箱服务。
63
63
  </Tab>
64
64
  </Tabs>
@@ -38,7 +38,7 @@ AI 通常按任务选择这些入口:
38
38
  <Steps>
39
39
  <Step title="运行实验">
40
40
  ```bash
41
- npx niceeval exp local
41
+ npx niceeval exp local --output agent
42
42
  ```
43
43
 
44
44
  先读取退出码:`0` 表示所有 Eval 通过;`1` 表示至少一个 Eval 失败或出错;`2` 表示 NiceEval 自身未能完成运行。退出码决定是否继续,控制台文本用于定位原因。
@@ -59,7 +59,7 @@ AI 通常按任务选择这些入口:
59
59
 
60
60
  `--execution` 合并 AI 输出与 trace:标准事件流提供消息、thinking、tool call/result 和 Skill load;OTel 在能够关联时给同一节点补开始时间、耗时、父子关系和错误状态。没有 OTel 时步骤仍完整,只不显示时间。
61
61
 
62
- 不带证据 flag 时,`show @<id>` 是失败诊断首页。它先列出失败断言的 group、matcher、expected、received、原因和源码位置,再给执行与文件变化摘要。AI 应该先读这一页;只有需要回答“为什么产生这个值”时,才继续打开对应证据。
62
+ 不带证据 flag 时,`show @<id>` 是失败诊断首页。它先列出失败断言的 group、matcher、expected、received、原因和源码位置,再给执行、生命周期阶段耗时与文件变化摘要。AI 应该先读这一页;只有需要回答“为什么产生这个值”时,才继续打开对应证据。
63
63
 
64
64
  ```text
65
65
  $ niceeval show @1k2m9qtr
@@ -89,20 +89,16 @@ AI 通常按任务选择这些入口:
89
89
  source: evals/memory/swelancer-manager-proposals.eval.ts:40:11
90
90
 
91
91
  execution: 3 events · 0 skill loads · 0 tool calls · 1 AI messages
92
- timing: OTel spans recorded for this attempt see --execution for per-step timing.
93
- agent run ▕████████████████████▏ 41.2s
94
- ├─ inference ▕█████░░░░░░░░░░░░░░░▏ 10.1s · "布鲁克林今天大约 24°C,晴。"
95
- ├─ inference ▕░░░░░█████████░░░░░░▏ 18.3s
96
- └─ inference ▕░░░░░░░░░░░░░░██████▏ 12.4s
97
- no tool calls
92
+ timing: eval.run 40.4s · scoring.evaluate 0.5s · teardown +0.2s
98
93
 
99
94
  changes · diff unavailable
100
95
  reason: this Attempt did not produce workspace file changes
101
96
 
102
- full eval source: …/weather/brooklyn/a2/eval-source.ts
97
+ full eval source: …/weather/brooklyn/a2/sources.json
103
98
  available:
104
99
  niceeval show @1k2m9qtr --eval
105
100
  niceeval show @1k2m9qtr --execution
101
+ niceeval show @1k2m9qtr --timing
106
102
  ```
107
103
 
108
104
  `--execution` 把 AI 消息、Skill load、工具调用和工具结果排成一棵执行树。它只展示 Agent 可理解的事件;没有关联到这些事件的 SDK / runtime span 不逐行输出,只报告省略数量并保留 `trace.json` 路径。下面的 Attempt 有 OTel,所以能关联的节点同时带相对时间与耗时:
@@ -175,9 +171,10 @@ AI 通常按任务选择这些入口:
175
171
 
176
172
  | 要回答的问题 | 入口 | 输出必须包含 |
177
173
  | --- | --- | --- |
178
- | 快速判断一次 Attempt 发生了什么 | `niceeval show @<id>` | Eval 断言、执行步骤、可选 OTel 时间、diff 摘要及各块可用性 |
174
+ | 快速判断一次 Attempt 发生了什么 | `niceeval show @<id>` | Eval 断言、执行步骤、生命周期阶段耗时摘要、diff 摘要及各块可用性 |
179
175
  | Eval 实际检查了什么,哪条 gate / soft 为什么通过或失败 | `niceeval show @<id> --eval` | 运行时 Eval 源码、源码哈希、断言所在行、严重度、分数与原因 |
180
- | AI 做了什么、调用了什么、时间花在哪里 | `niceeval show @<id> --execution` | 消息、thinking、Skill load、工具调用与结果;有 OTel 时在同一节点显示时间、父子关系和错误状态 |
176
+ | AI 做了什么、调用了什么 | `niceeval show @<id> --execution` | 消息、thinking、Skill load、工具调用与结果;有 OTel 时在同一节点附时间、父子关系和错误状态 |
177
+ | 整个 Attempt 的时间花在哪里 | `niceeval show @<id> --timing` | lifecycle → hook/turn → shell → OTel 的统一时间树;出错的 Attempt 标出已知的最深失败节点 |
181
178
  | Sandbox 工作区文件变成什么 | `niceeval show @<id> --diff` | 文件摘要、增删行数、具体补丁和原始 diff 路径;无文件工作区时明确 unavailable |
182
179
  </Step>
183
180
  <Step title="提出假设并修改">
@@ -195,7 +192,7 @@ AI 通常按任务选择这些入口:
195
192
  </Step>
196
193
  <Step title="局部重跑并验证假设">
197
194
  ```bash
198
- npx niceeval exp local weather/brooklyn --force
195
+ npx niceeval exp local weather/brooklyn --output agent --force
199
196
  npx niceeval show weather/brooklyn
200
197
  ```
201
198
 
@@ -203,7 +200,7 @@ AI 通常按任务选择这些入口:
203
200
  </Step>
204
201
  <Step title="全量确认没有回归">
205
202
  ```bash
206
- npx niceeval exp local --force
203
+ npx niceeval exp local --output agent --force
207
204
  npx niceeval show
208
205
  ```
209
206
 
@@ -213,19 +210,22 @@ AI 通常按任务选择这些入口:
213
210
 
214
211
  ## AI 应该从输出里读什么
215
212
 
216
- `exp` 运行结束后会给出三类信息:失败摘要、结果统计和结果快照目录。典型输出如下:
213
+ `--output agent` 运行中只向 stderr 追加低频 checkpoint(存活信号,不是结果数据源),结束时向 stdout 打印一个有界 handoff block——这才是 AI 应该解析的部分:
217
214
 
218
215
  ```text
219
- Failing:
220
- weather/brooklyn · @1k2m9qtr
221
- gate calledTool("get_weather"): tool was never called
222
-
223
- Results: 14 passed, 1 failed, 0 errored, 0 skipped
224
- Structured results: .niceeval/local/2026-07-09T10-00-00-000Z-x1f2/
225
- (snapshot.json + attempt 的 result.json / events.json / trace.json / diff.json)
216
+ NICEEVAL RESULT failed
217
+ summary: 14 passed, 1 failed, 0 errored (0 reused)
218
+ snapshots:
219
+ - .niceeval/local/2026-07-09T10-00-00-000Z-x1f2/
220
+ failures:
221
+ - @1k2m9qtr weather/brooklyn [local]
222
+ gate: tool was never called
223
+ next:
224
+ niceeval show @1k2m9qtr
225
+ niceeval show @1k2m9qtr --execution
226
226
  ```
227
227
 
228
- AI 应先从失败项选中 Attempt locator,再按证据位执行 `niceeval show @<id>` 或对应证据 flag,不要从头解析运行期间不断刷新的进度行。需要机器读取时,结果快照是事实来源:
228
+ AI 应先从 `failures` 选中 Attempt locator,再按证据位执行 `next` 给出的 `niceeval show @<id>` 或对应证据 flag,不要解析运行期间 stderr 上低频追加的 checkpoint 行——那些只用于判断进程是否存活。失败条数超过上限(默认 5 条)时,handoff 只展开前几条并给出总数,完整清单读结果快照。需要机器读取时,结果快照是事实来源:
229
229
 
230
230
  ```text
231
231
  .niceeval/<experiment>/<快照>/
@@ -238,7 +238,7 @@ AI 应先从失败项选中 Attempt locator,再按证据位执行 `niceeval sh
238
238
  ```
239
239
 
240
240
  - `snapshot.json` 记录实验身份、运行配置、格式版本和时间。
241
- - `result.json` 记录该 Attempt 的判定、断言、错误和用量。
241
+ - `result.json` 记录该 Attempt 的判定、断言、结构化错误、diagnostics 和用量;瞬时 progress 不落盘。
242
242
  - `events.json` 是对话与工具调用事件,`trace.json` 是调用链,`diff.json` 是 Sandbox 文件变化。
243
243
  - 某类证据不存在时,对应文件不会生成。先以 `show` 的提示为准,不要假设每个目录都有全部文件。
244
244
 
@@ -268,12 +268,14 @@ AI 应先从失败项选中 Attempt locator,再按证据位执行 `niceeval sh
268
268
 
269
269
  ```text
270
270
  读取 node_modules/niceeval/INDEX.md,再按索引读取与任务有关的文档。
271
- 运行 npx niceeval exp local,并根据退出码和失败摘要决定下一步。
271
+ 运行 npx niceeval exp local --output agent,并根据退出码和失败 locator 决定下一步。
272
272
  对每个失败的 Eval,从报告选择一个 Attempt locator;再运行 niceeval show @<id>
273
- 并按问题选择默认 Eval 源码面、--execution 或 --diff。写出失败原因的假设,并判断应该修改被测程序、
273
+ 并按问题选择 --eval、--execution、--timing 或 --diff;--timing 从 lifecycle 展开 setup/teardown
274
+ hook、shell 命令、每轮 send 与可关联的 OTel model/tool,回答整个 Attempt 的时间花在哪里。
275
+ 写出失败原因的假设,并判断应该修改被测程序、
274
276
  Eval,还是实验环境。修改后用 --force 重跑对应 Eval,比较新的判定和证据。
275
277
  同一问题连续三轮没有新证据或改善时停止并汇报,不要靠放宽断言碰绿。
276
- 全部局部失败清零后,用 npx niceeval exp local --force 全量验证;退出码 0 才完成。
278
+ 全部局部失败清零后,用 npx niceeval exp local --output agent --force 全量验证;退出码 0 才完成。
277
279
  ```
278
280
 
279
281
  真实 Agent 的运行可能产生费用。实验阶段可以加 `--budget <美元>` 限制本轮累计成本;预算只能限制单次命令,不能替代上面的停止条件。
@@ -18,6 +18,7 @@ export default defineEval({
18
18
  reporters?: Reporter[];
19
19
  timeoutMs?: number;
20
20
  metadata?: Record<string, unknown>;
21
+ async setup(sandbox, ctx) { /* task fixture + progress/diagnostic */ },
21
22
  async test(t) { /* interactions + assertions */ },
22
23
  });
23
24
  ```
@@ -111,6 +112,38 @@ export default defineEval({
111
112
 
112
113
  详见 [Fixtures](/zh/guides/fixtures)。
113
114
 
115
+ ## 从 Eval 报告长步骤和诊断
116
+
117
+ `setup` 用于这条 Eval 的任务夹具。第二个参数绑定到 eval setup 阶段;`test(t)` 里的反馈绑定到 eval run 阶段:
118
+
119
+ ```ts
120
+ export default defineEval({
121
+ async setup(sandbox, ctx) {
122
+ ctx.progress({ message: "安装 fixture 依赖" });
123
+ await sandbox.runCommand("npm", ["install"]);
124
+ },
125
+
126
+ async test(t) {
127
+ t.progress({ message: "上传隐藏测试", current: 1, total: 2 });
128
+ await t.sandbox.uploadDirectory("../fixtures/project");
129
+
130
+ const preflight = await inspectFixture();
131
+ if (preflight.usedFallback) {
132
+ t.diagnostic({
133
+ code: "fixture-check-degraded",
134
+ level: "warning",
135
+ message: "Fixture 预检使用了备用检查器",
136
+ data: { checker: preflight.checker },
137
+ });
138
+ }
139
+
140
+ await t.send("完成任务");
141
+ },
142
+ });
143
+ ```
144
+
145
+ `progress` 只更新运行中的短期状态,不进入结果。`diagnostic` 会写进当前 Attempt 的 `result.json`,但不会代替断言或自动改变判定:业务结论仍用 `t.check` / `t.require` / gate;基础设施无法继续时抛出异常。
146
+
114
147
  ## 命名约定
115
148
 
116
149
  <CardGroup cols={2}>
@@ -11,9 +11,15 @@ Evals 应该和测试一样进入 CI。它们能在 PR 阶段发现 agent 行为
11
11
  默认情况下,只要存在失败的 gate,[NiceEval](https://niceeval.com/) 将以非零状态码退出。CI 中通常使用 `--strict`,让失败更明确。
12
12
 
13
13
  ```bash
14
- npx niceeval exp ci --strict
14
+ NICEEVAL_LANG=en npx niceeval exp ci \
15
+ --output ci \
16
+ --strict \
17
+ --json .niceeval/ci-summary.json \
18
+ --junit .niceeval/junit.xml
15
19
  ```
16
20
 
21
+ CI profile 不输出 ANSI、spinner 或动态表格。日志使用单一有序 stdout 流,只追加 start、低频 heartbeat、失败/错误、diagnostic 和最终 result;通过的 Attempt 不逐条打印。
22
+
17
23
  ## GitHub Actions 示例
18
24
 
19
25
  ```yaml
@@ -32,8 +38,12 @@ jobs:
32
38
  node-version: 22
33
39
  cache: npm
34
40
  - run: npm ci
35
- - run: npx niceeval exp ci --strict
41
+ - run: >-
42
+ npx niceeval exp ci --output ci --strict
43
+ --json .niceeval/ci-summary.json
44
+ --junit .niceeval/junit.xml
36
45
  env:
46
+ NICEEVAL_LANG: en
37
47
  OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
38
48
  ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
39
49
  ```
@@ -52,18 +62,19 @@ jobs:
52
62
  </Step>
53
63
  </Steps>
54
64
 
55
- ## JUnit reporter
56
-
57
- ```ts
58
- import { defineConfig } from "niceeval";
59
- import { Console, JUnit } from "niceeval/reporters";
65
+ ## JSON、JUnit 和结构化错误
60
66
 
61
- export default defineConfig({
62
- reporters: [Console(), JUnit(".niceeval/junit.xml")],
63
- });
67
+ ```text
68
+ niceeval: start total=24 configs=3 concurrency=10 reused=18
69
+ niceeval: errored locator=@12h8m4k1 eval=fixtures/button experiment=ci/codex phase=sandbox.create reason="E2B sandbox allocation failed after 5 attempts"
70
+ niceeval: result=failed passed=23 failed=0 errored=1 reused=18 duration=128s
71
+ niceeval: json=.niceeval/ci-summary.json
72
+ niceeval: junit=.niceeval/junit.xml
64
73
  ```
65
74
 
66
- CI 可以上传 JUnit XML,让失败出现在测试报告 UI 里。想把结果同时上报到 Braintrust 这类实验平台,见 [Reporter 上报](./reporters)。
75
+ 退出码是第一层红绿信号;JSON、JUnit 和结果快照是完整机器接口,日志行只用于搜索和 annotation。`errored` 行带 locator、eval/experiment 身份、已知时的正式 phase,以及一层 `reason` 摘要。详细 cause、stack 和 diagnostics 保存在 Attempt 的 `result.json`,可在保留 artifact 后运行 `niceeval show @<locator>` 回顾。
76
+
77
+ CLI 显式要求的 JSON/JUnit 和默认 results artifact 都是 required 输出:写入失败必须让 job 判红,不能只留 warning 后退出 0。想把结果同时上报到 Braintrust 这类实验平台,见 [Reporter 上报](./reporters)。
67
78
 
68
79
  ## 只检查发现
69
80
 
@@ -80,7 +91,7 @@ npx niceeval list
80
91
  ## 控制并发
81
92
 
82
93
  ```bash
83
- npx niceeval exp ci --max-concurrency 2
94
+ npx niceeval exp ci --output ci --max-concurrency 2
84
95
  ```
85
96
 
86
97
  标准 GitHub-hosted runner 上,sandbox eval 并发不宜过高。远程 HTTP eval 可以按服务限流能力调高。