niceeval 0.5.1 → 0.5.3

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 (64) hide show
  1. package/docs-site/zh/concepts/adapter.mdx +6 -6
  2. package/docs-site/zh/concepts/experiment.mdx +6 -6
  3. package/docs-site/zh/concepts/overview.mdx +1 -1
  4. package/docs-site/zh/concepts/tier.mdx +6 -6
  5. package/docs-site/zh/guides/ci-integration.mdx +2 -0
  6. package/docs-site/zh/guides/connect-your-agent.mdx +7 -7
  7. package/docs-site/zh/guides/custom-reports.mdx +7 -7
  8. package/docs-site/zh/guides/experiments.mdx +3 -3
  9. package/docs-site/zh/guides/publish-report.mdx +91 -0
  10. package/docs-site/zh/guides/report-components.mdx +4 -4
  11. package/docs-site/zh/guides/reporters.mdx +1 -1
  12. package/docs-site/zh/guides/results-data.mdx +1 -1
  13. package/docs-site/zh/guides/runner.mdx +1 -1
  14. package/docs-site/zh/guides/sandbox-agent.mdx +2 -2
  15. package/docs-site/zh/guides/viewing-results.mdx +3 -19
  16. package/docs-site/zh/guides/write-experiment.mdx +9 -9
  17. package/docs-site/zh/guides/write-send.mdx +6 -6
  18. package/docs-site/zh/index.mdx +1 -1
  19. package/docs-site/zh/introduction.mdx +1 -1
  20. package/docs-site/zh/reference/builtin-agents.mdx +4 -0
  21. package/docs-site/zh/reference/cli.mdx +10 -6
  22. package/docs-site/zh/reference/define-agent.mdx +7 -3
  23. package/docs-site/zh/reference/define-config.mdx +3 -1
  24. package/docs-site/zh/reference/define-eval.mdx +9 -3
  25. package/docs-site/zh/reference/events.mdx +4 -0
  26. package/docs-site/zh/reference/expect.mdx +4 -0
  27. package/package.json +3 -2
  28. package/src/agents/ai-sdk.test.ts +1 -1
  29. package/src/agents/ai-sdk.ts +2 -2
  30. package/src/agents/claude-code.ts +1 -1
  31. package/src/agents/codex.ts +2 -2
  32. package/src/agents/streaming.test.ts +1 -1
  33. package/src/agents/types.ts +2 -2
  34. package/src/agents/ui-message-stream.test.ts +1 -1
  35. package/src/cli.ts +53 -15
  36. package/src/context/context.test.ts +1 -1
  37. package/src/context/context.ts +3 -3
  38. package/src/context/session.ts +2 -2
  39. package/src/context/types.ts +2 -2
  40. package/src/i18n/en.ts +2 -2
  41. package/src/i18n/zh-CN.ts +2 -2
  42. package/src/report/aggregate.ts +16 -16
  43. package/src/report/components.tsx +1 -1
  44. package/src/report/compute.ts +7 -7
  45. package/src/report/dual-face.test.tsx +1 -1
  46. package/src/report/{param.ts → flag.ts} +6 -6
  47. package/src/report/index.ts +3 -3
  48. package/src/report/react/MetricLine.tsx +1 -1
  49. package/src/report/react/fixtures.ts +1 -1
  50. package/src/report/report.test.ts +17 -17
  51. package/src/report/types.ts +11 -11
  52. package/src/results/results.test.ts +2 -2
  53. package/src/runner/attempt.ts +3 -3
  54. package/src/runner/fingerprint.ts +1 -1
  55. package/src/runner/reporters/braintrust.test.ts +2 -2
  56. package/src/runner/reporters/braintrust.ts +3 -3
  57. package/src/runner/run.ts +1 -1
  58. package/src/runner/types.ts +8 -8
  59. package/src/view/app/css.d.ts +3 -0
  60. package/src/view/app/i18n.ts +3 -3
  61. package/src/view/app/lib/rows.ts +4 -4
  62. package/src/view/client-dist/app.js +2 -2
  63. package/src/view/data.test.ts +25 -1
  64. package/src/view/data.ts +27 -3
@@ -56,6 +56,8 @@ description: "NiceEval 内置的 claude-code、codex、bub 适配器分别做到
56
56
 
57
57
  {/* GENERATED:BEGIN builtin-agent-config */}
58
58
 
59
+ {/* 本区块由 pnpm docs:reference 从源码注释生成,勿手改;要改文案,改对应源码的 TSDoc/JSDoc(映射见 scripts/generate-reference.ts) */}
60
+
59
61
  ### `ClaudeCodeConfig`
60
62
 
61
63
  #### `apiKey`
@@ -196,6 +198,8 @@ export default uiMessageStreamAgent({
196
198
 
197
199
  {/* GENERATED:BEGIN ui-message-stream-options */}
198
200
 
201
+ {/* 本区块由 pnpm docs:reference 从源码注释生成,勿手改;要改文案,改对应源码的 TSDoc/JSDoc(映射见 scripts/generate-reference.ts) */}
202
+
199
203
  #### `name`
200
204
 
201
205
  ```ts
@@ -4,7 +4,7 @@ sidebarTitle: "命令行"
4
4
  description: "NiceEval CLI 参考:exp、init、list、clean 和 view 命令,以及 experiment、eval 过滤、sandbox、并发、预算和 JUnit CI 输出。"
5
5
  ---
6
6
 
7
- [NiceEval](https://niceeval.com/) CLI 是发现、运行和查看 eval 的入口。实际执行 eval 时采用 experiment-first 模型:**`exp` 选择可签入的运行配置**,experiment 后的位置参数才按 eval ID 前缀过滤。agent、model 和 params 写在 `experiments/`,不靠临时 CLI 参数。
7
+ [NiceEval](https://niceeval.com/) CLI 是发现、运行和查看 eval 的入口。实际执行 eval 时采用 experiment-first 模型:**`exp` 选择可签入的运行配置**,experiment 后的位置参数才按 eval ID 前缀过滤。agent、model 和 flags 写在 `experiments/`,不靠临时 CLI 参数。
8
8
 
9
9
  ## 命令
10
10
 
@@ -56,10 +56,12 @@ npx niceeval exp compare-models weather
56
56
 
57
57
  ## 常用 flags
58
58
 
59
- 下表由 CLI 的 flag 解析表生成。**没有**用于选择 sandbox 后端的 CLI flag:sandbox 后端只能写在代码里——在 experiment(`defineExperiment`)里设置 `sandbox`,或者在 `niceeval.config.ts`(`defineConfig`)里设置作为项目级兜底,值来自 `niceeval/sandbox` 的 `dockerSandbox()` / `vercelSandbox()` / `e2bSandbox()`;在 CLI 上尝试传入 `--sandbox` 会直接报错并以非零状态退出。`--watch` / `--json` 是文档曾接受、尚未实现的 flag,容忍解析(no-op)但不接任何行为,同样不在下表出现。
59
+ 下表由 CLI 的 flag 解析表生成,表外的 flag 一律按未知 flag 报错并以非零状态退出。**没有**用于选择 sandbox 后端的 CLI flag:sandbox 后端只能写在代码里——在 experiment(`defineExperiment`)里设置 `sandbox`,或者在 `niceeval.config.ts`(`defineConfig`)里设置作为项目级兜底,值来自 `niceeval/sandbox` 的 `dockerSandbox()` / `vercelSandbox()` / `e2bSandbox()`。
60
60
 
61
61
  {/* GENERATED:BEGIN cli-flags */}
62
62
 
63
+ {/* 本区块由 pnpm docs:reference 从源码注释生成,勿手改;要改文案,改对应源码的 TSDoc/JSDoc(映射见 scripts/generate-reference.ts) */}
64
+
63
65
  | Flag | 类型 | 说明 |
64
66
  |---|---|---|
65
67
  | `--agent` | string | experiment 运行不支持该 flag。要换 agent,请在 `experiments/` 下新增或复制一个配置文件。 |
@@ -70,16 +72,17 @@ npx niceeval exp compare-models weather
70
72
  | `--budget` | number | 整次运行的预算上限(美元)。 |
71
73
  | `--tag` | string | 只运行带有该 tag 的 eval(见 `defineEval` 的 `tags`)。 |
72
74
  | `--junit` | string | 额外写一份 JUnit XML 报告到指定路径,供 CI 消费。 |
75
+ | `--json` | string | 额外写一份 JSON 结果(`RunSummary` 原样序列化)到指定路径,供 CI 或下游脚本消费。 |
73
76
  | `--out` | string | `view` 命令专用:把结果查看器静态导出到指定目录。 |
74
77
  | `--port` | number | `view` 命令专用:指定本地服务器监听端口。 |
75
78
  | `--transcript` | boolean | `show` 命令专用:渲染单个 eval 的完整对话与工具调用(证据切面)。 |
76
79
  | `--trace` | boolean | `show` 命令专用:渲染单个 eval 的 trace 瀑布文本版(证据切面)。 |
77
80
  | `--diff` | boolean | `show` 命令专用:sandbox 里的文件改动摘要;`--diff=<文件路径>` 看单个文件的完整改动(路径必须 `=` 连写)。 |
78
81
  | `--history` | boolean | `show` 命令专用:跨 run 时间轴,只列真实执行;与 `--report` 互斥。 |
79
- | `--experiment` | string | `show` 命令专用:选集只留该实验。 |
82
+ | `--experiment` | string | `show` / `view` 命令专用:选集只留该实验。 |
80
83
  | `--attempt` | number | `show` 命令专用:指定详情 / 证据切面看第几次 attempt(与展示一致的 1 计序号)。 |
81
- | `--run` | string | `show` 命令专用:钉死看某一个结果目录(历史 run 或 `copySnapshots` 产物)。 |
82
- | `--report` | string | `show` 命令专用:把默认榜单整槽换成你的报告文件(默认导出 `defineReport(...)`)。 |
84
+ | `--run` | string | `show` / `view` 命令专用:钉死看某一个结果目录(历史 run 或 `copySnapshots` 产物)。 |
85
+ | `--report` | string | `show` / `view` 命令专用:把默认榜单整槽换成你的报告文件(默认导出 `defineReport(...)`)。 |
83
86
  | `--dry` | boolean | 只打印本次会匹配到的 eval × 运行配置,不实际执行。 |
84
87
  | `--quiet` | boolean | 关闭控制台 / live 进度输出(reporter 仍会写 artifacts)。 |
85
88
  | `--force` | boolean | 忽略上次运行结果,不跳过已通过的 (experiment, eval) 组合,强制全部重跑。 |
@@ -87,6 +90,7 @@ npx niceeval exp compare-models weather
87
90
  | `--early-exit` / `--no-early-exit` | boolean | 某个 eval 的一次 attempt 通过后,停止该 eval 剩余的 attempts。 |
88
91
  | `--open` / `--no-open` | boolean | `view` 命令专用:启动后自动打开浏览器(默认行为)。 |
89
92
  | `--help` | boolean | 打印用法说明并退出。 |
93
+ | `--version` | boolean | 打印 niceeval 的版本号并退出。 |
90
94
 
91
95
  {/* GENERATED:END cli-flags */}
92
96
 
@@ -105,7 +109,7 @@ npx niceeval exp compare-models
105
109
  npx niceeval exp compare-models weather-tool
106
110
  ```
107
111
 
108
- 运行命名 experiment,用矩阵比较 agents、models 或 params。第二个参数开始是 eval ID 前缀过滤。
112
+ 运行命名 experiment,用矩阵比较 agents、models 或 flags。第二个参数开始是 eval ID 前缀过滤。
109
113
 
110
114
  ## `view`
111
115
 
@@ -80,6 +80,8 @@ export default defineExperiment({
80
80
 
81
81
  {/* GENERATED:BEGIN agent-def */}
82
82
 
83
+ {/* 本区块由 pnpm docs:reference 从源码注释生成,勿手改;要改文案,改对应源码的 TSDoc/JSDoc(映射见 scripts/generate-reference.ts) */}
84
+
83
85
  ### `RemoteAgentDef`
84
86
 
85
87
  #### `name`
@@ -214,13 +216,13 @@ readonly reasoningEffort?: string;
214
216
 
215
217
  模型推理努力程度;归属同 model——实验决定,省略时不覆盖 agent 原生默认。
216
218
 
217
- #### `params`
219
+ #### `flags`
218
220
 
219
221
  ```ts
220
- readonly params: Readonly<Record<string, unknown>>;
222
+ readonly flags: Readonly<Record<string, unknown>>;
221
223
  ```
222
224
 
223
- experiment 的 `params` 字段原样透传,内容和结构完全由 experiment 作者自定义
225
+ experiment 的 `flags` 字段原样透传,内容和结构完全由 experiment 作者自定义
224
226
  (如 `{ webResearch: true }`、`{ systemPrompt: "..." }`)。adapter 按自己的约定
225
227
  读取其中的字段;框架本身不解释、不校验它的内容。命名特意避开 CLI 解析出的
226
228
  `flag`(跑法层面的 --timeout/--budget 等),两者是不相关的概念。
@@ -280,6 +282,8 @@ log(msg: string): void;
280
282
 
281
283
  {/* GENERATED:BEGIN sandbox-methods */}
282
284
 
285
+ {/* 本区块由 pnpm docs:reference 从源码注释生成,勿手改;要改文案,改对应源码的 TSDoc/JSDoc(映射见 scripts/generate-reference.ts) */}
286
+
283
287
  ### `Sandbox`
284
288
 
285
289
  #### `workdir`
@@ -4,7 +4,7 @@ sidebarTitle: "defineConfig"
4
4
  description: "defineConfig 参考:judge、reporters、并发、超时和 sandbox 默认值。"
5
5
  ---
6
6
 
7
- `defineConfig` 从根目录的 `niceeval.config.ts` 默认导出,只放项目级默认值。agent、model、params、runs 和实验预算写在 `experiments/` 下的 `defineExperiment` 文件里。
7
+ `defineConfig` 从根目录的 `niceeval.config.ts` 默认导出,只放项目级默认值。agent、model、flags、runs 和实验预算写在 `experiments/` 下的 `defineExperiment` 文件里。
8
8
 
9
9
  ```ts
10
10
  import { defineConfig } from "niceeval";
@@ -20,6 +20,8 @@ export default defineConfig({
20
20
 
21
21
  {/* GENERATED:BEGIN config-fields */}
22
22
 
23
+ {/* 本区块由 pnpm docs:reference 从源码注释生成,勿手改;要改文案,改对应源码的 TSDoc/JSDoc(映射见 scripts/generate-reference.ts) */}
24
+
23
25
  #### `name`
24
26
 
25
27
  ```ts
@@ -26,6 +26,8 @@ export default defineEval({
26
26
 
27
27
  {/* GENERATED:BEGIN defineeval-options */}
28
28
 
29
+ {/* 本区块由 pnpm docs:reference 从源码注释生成,勿手改;要改文案,改对应源码的 TSDoc/JSDoc(映射见 scripts/generate-reference.ts) */}
30
+
29
31
  #### `id`
30
32
 
31
33
  ```ts
@@ -108,6 +110,8 @@ eval 主体:拿到 TestContext,驱动对话 / 沙箱操作并就地断言。
108
110
 
109
111
  {/* GENERATED:BEGIN test-context */}
110
112
 
113
+ {/* 本区块由 pnpm docs:reference 从源码注释生成,勿手改;要改文案,改对应源码的 TSDoc/JSDoc(映射见 scripts/generate-reference.ts) */}
114
+
111
115
  #### `send`
112
116
 
113
117
  ```ts
@@ -207,13 +211,13 @@ readonly reasoningEffort?: string;
207
211
 
208
212
  本次 attempt 的推理努力程度(如 "low"/"medium"/"high",取值由 adapter/模型决定)。
209
213
 
210
- #### `params`
214
+ #### `flags`
211
215
 
212
216
  ```ts
213
- readonly params: Readonly<Record<string, unknown>>;
217
+ readonly flags: Readonly<Record<string, unknown>>;
214
218
  ```
215
219
 
216
- 本次 attempt 生效的 params(experiment.params 与 CLI flag 合并后的只读视图)。
220
+ 本次 attempt 生效的实验 flags(experiment.flags 的只读视图;实验条件,非命令行开关)。
217
221
 
218
222
  #### `log`
219
223
 
@@ -440,6 +444,8 @@ t.judge.autoevals.summarizes(sourceText, { on: t.reply }).atLeast(0.7);
440
444
 
441
445
  {/* GENERATED:BEGIN turn-handle */}
442
446
 
447
+ {/* 本区块由 pnpm docs:reference 从源码注释生成,勿手改;要改文案,改对应源码的 TSDoc/JSDoc(映射见 scripts/generate-reference.ts) */}
448
+
443
449
  #### `events`
444
450
 
445
451
  ```ts
@@ -23,6 +23,8 @@ interface Turn {
23
23
 
24
24
  {/* GENERATED:BEGIN usage-fields */}
25
25
 
26
+ {/* 本区块由 pnpm docs:reference 从源码注释生成,勿手改;要改文案,改对应源码的 TSDoc/JSDoc(映射见 scripts/generate-reference.ts) */}
27
+
26
28
  #### `inputTokens`
27
29
 
28
30
  ```ts
@@ -81,6 +83,8 @@ token 用量或 OTel span 反推得到)。存在时优先于按价格表(`define
81
83
 
82
84
  {/* GENERATED:BEGIN stream-events */}
83
85
 
86
+ {/* 本区块由 pnpm docs:reference 从源码注释生成,勿手改;要改文案,改对应源码的 TSDoc/JSDoc(映射见 scripts/generate-reference.ts) */}
87
+
84
88
  #### `message`
85
89
 
86
90
  ```ts
@@ -29,6 +29,8 @@ t.require(turn.status, equals("completed"));
29
29
 
30
30
  {/* GENERATED:BEGIN expect-matchers */}
31
31
 
32
+ {/* 本区块由 pnpm docs:reference 从源码注释生成,勿手改;要改文案,改对应源码的 TSDoc/JSDoc(映射见 scripts/generate-reference.ts) */}
33
+
32
34
  #### `includes`
33
35
 
34
36
  ```ts
@@ -150,6 +152,8 @@ t.check(t.reply, includes("nice to have").atLeast(0.7));
150
152
 
151
153
  {/* GENERATED:BEGIN value-assertion */}
152
154
 
155
+ {/* 本区块由 pnpm docs:reference 从源码注释生成,勿手改;要改文案,改对应源码的 TSDoc/JSDoc(映射见 scripts/generate-reference.ts) */}
156
+
153
157
  #### `name`
154
158
 
155
159
  ```ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "niceeval",
3
- "version": "0.5.1",
3
+ "version": "0.5.3",
4
4
  "description": "Agent-native eval tool — eval agents, services, functions, and coding-agent fixtures",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -100,7 +100,8 @@
100
100
  "shiki": "^4.3.0",
101
101
  "tailwind-merge": "^3.6.0",
102
102
  "tailwindcss": "^4.3.1",
103
- "typescript": "^5.6.0",
103
+ "typescript": "npm:@typescript/typescript6@^6.0.2",
104
+ "@typescript/native": "npm:typescript@^7.0.2",
104
105
  "vite": "^8.1.0",
105
106
  "vitest": "^4.1.9"
106
107
  },
@@ -454,7 +454,7 @@ function fakeCtx(opts: { id?: string } = {}): AgentContext {
454
454
  return {
455
455
  signal: new AbortController().signal,
456
456
  model: undefined,
457
- params: {},
457
+ flags: {},
458
458
  sandbox: undefined as unknown as AgentContext["sandbox"],
459
459
  session,
460
460
  log: () => {},
@@ -367,7 +367,7 @@ export interface AiSdkGenerateContext<M = unknown> {
367
367
  /** 实验钉的推理努力程度(ctx.reasoningEffort);省略 → 用应用自己的默认。应用自己决定怎么塞进 providerOptions(如 OpenAI 的 reasoningEffort)。 */
368
368
  readonly reasoningEffort?: string;
369
369
  readonly signal: AbortSignal;
370
- readonly params: Readonly<Record<string, unknown>>;
370
+ readonly flags: Readonly<Record<string, unknown>>;
371
371
  /**
372
372
  * 配了 `tracing`(如 `aiSdkOtel()`)才有:直接放进 generateText / streamText 的
373
373
  * `telemetry` 选项。OTel provider、per-attempt 端点绑定和轮末 flush 都由工厂做,
@@ -505,7 +505,7 @@ export function aiSdkAgent<M = unknown>(options: AiSdkAgentOptions<M>): Agent {
505
505
  model: ctx.model,
506
506
  reasoningEffort: ctx.reasoningEffort,
507
507
  signal: ctx.signal,
508
- params: ctx.params,
508
+ flags: ctx.flags,
509
509
  telemetry: otel?.settings,
510
510
  });
511
511
  } catch (error) {
@@ -94,7 +94,7 @@ export function claudeCodeAgent(config?: ClaudeCodeConfig): Agent {
94
94
  const args = ["--print", "--dangerously-skip-permissions"];
95
95
  if (ctx.model) args.push("--model", ctx.model);
96
96
  if (config?.maxTurns != null) args.push("--max-turns", String(config.maxTurns));
97
- if (ctx.params.webResearch) args.push("--allowedTools", "WebSearch,WebFetch");
97
+ if (ctx.flags.webResearch) args.push("--allowedTools", "WebSearch,WebFetch");
98
98
  if (ctx.session.id) args.push("--resume", ctx.session.id);
99
99
  args.push(input.text);
100
100
 
@@ -9,7 +9,7 @@ import type { Agent, McpServer } from "../types.ts";
9
9
  //
10
10
  // 连接方式:在沙箱里 spawn `codex exec --json`,stdout JSONL → parseCodex → 标准事件流。
11
11
  // 配置:鉴权本地(config / env),模型交给实验(ctx.model),推理努力程度经 ctx.reasoningEffort
12
- // (兼容旧的 ctx.params.effort),其余参数经 ctx.params
12
+ // (兼容旧的 ctx.flags.effort),其余参数经 ctx.flags
13
13
  // ───────────────────────────────────────────────────────────────────────────
14
14
 
15
15
  export interface CodexConfig {
@@ -44,7 +44,7 @@ export function codexAgent(config?: CodexConfig): Agent {
44
44
  // model 归属:实验决定(ctx.model);省略时不写 model 行,交给 codex CLI 原生默认,
45
45
  // 不在 adapter 里硬编码一个会过期的模型名。
46
46
  const modelLine = ctx.model ? `model = "${ctx.model}"\n` : "";
47
- const effort = ctx.reasoningEffort ?? (ctx.params.effort as string | undefined) ?? "medium";
47
+ const effort = ctx.reasoningEffort ?? (ctx.flags.effort as string | undefined) ?? "medium";
48
48
  const base = getBaseUrl();
49
49
 
50
50
  if (base) {
@@ -9,7 +9,7 @@ import { createAgentSession } from "../context/session.ts";
9
9
  function lineCtx(): AgentContext {
10
10
  return {
11
11
  signal: new AbortController().signal,
12
- params: {},
12
+ flags: {},
13
13
  session: createAgentSession(),
14
14
  sandbox: undefined as never,
15
15
  log() {},
@@ -155,12 +155,12 @@ export interface AgentContext {
155
155
  /** 模型推理努力程度;归属同 model——实验决定,省略时不覆盖 agent 原生默认。 */
156
156
  readonly reasoningEffort?: string;
157
157
  /**
158
- * experiment 的 `params` 字段原样透传,内容和结构完全由 experiment 作者自定义
158
+ * experiment 的 `flags` 字段原样透传,内容和结构完全由 experiment 作者自定义
159
159
  * (如 `{ webResearch: true }`、`{ systemPrompt: "..." }`)。adapter 按自己的约定
160
160
  * 读取其中的字段;框架本身不解释、不校验它的内容。命名特意避开 CLI 解析出的
161
161
  * `flag`(跑法层面的 --timeout/--budget 等),两者是不相关的概念。
162
162
  */
163
- readonly params: Readonly<Record<string, unknown>>;
163
+ readonly flags: Readonly<Record<string, unknown>>;
164
164
  /** 仅沙箱型 agent 有(运行器按 --sandbox 备好)。 */
165
165
  readonly sandbox: Sandbox;
166
166
  readonly session: AgentSession;
@@ -73,7 +73,7 @@ function ctx(overrides: Partial<{ model: string }> = {}): AgentContext {
73
73
  return {
74
74
  signal: new AbortController().signal,
75
75
  model: overrides.model,
76
- params: {},
76
+ flags: {},
77
77
  sandbox: undefined as never,
78
78
  // 同一个 ctx 重复用 = 同一条会话线(续接,同一个 ctx.session);新造 = 新线。
79
79
  session: createAgentSession(),
package/src/cli.ts CHANGED
@@ -17,7 +17,7 @@ import { stopAllSandboxes, liveSandboxCount } from "./sandbox/registry.ts";
17
17
  import { evalLevelStats } from "./shared/outcome.ts";
18
18
  import { sandboxRecommendedConcurrency } from "./sandbox/resolve.ts";
19
19
  import { Console as ConsoleReporter } from "./runner/reporters/console.ts";
20
- import { JUnit } from "./runner/reporters/json.ts";
20
+ import { Json, JUnit } from "./runner/reporters/json.ts";
21
21
  import { Live as LiveReporter, type LiveRow } from "./runner/reporters/live.ts";
22
22
  import { Artifacts as ArtifactsReporter } from "./runner/reporters/artifacts.ts";
23
23
  import {
@@ -60,10 +60,12 @@ interface Flags {
60
60
  budget?: number;
61
61
  tag?: string;
62
62
  junit?: string;
63
+ json?: string;
63
64
  open?: boolean;
64
65
  out?: string;
65
66
  port?: number;
66
67
  help: boolean;
68
+ version: boolean;
67
69
  // ── show 专属(位置参数仍是 eval id 前缀;这些 flag 选「怎么看」)──
68
70
  transcript: boolean;
69
71
  trace: boolean;
@@ -79,43 +81,70 @@ interface Flags {
79
81
 
80
82
  // 表驱动的 flag 定义(node:util parseArgs)。--no-x 显式声明,不依赖 allowNegative(需 Node 20.14+,
81
83
  // engines 是 >=18)。未知 flag 由 strict 模式报清晰错误,不再静默吞掉后面的位置参数。
84
+ //
85
+ // 每个 flag 的 JSDoc 就是它在 docs-site/zh/reference/cli.mdx flag 表里的说明,由
86
+ // scripts/generate-reference.ts 提取渲染——改 flag 语义时改这里的注释即可,不用碰生成脚本。
87
+ // 负向 flag(no-x)与正向 flag 合并成一行展示,不需要单独写 JSDoc。
82
88
  const FLAG_OPTIONS = {
89
+ /** experiment 运行不支持该 flag。要换 agent,请在 `experiments/` 下新增或复制一个配置文件。 */
83
90
  agent: { type: "string" },
91
+ /** experiment 运行不支持该 flag。要换模型,请新增或复制一个 experiment 文件并修改 `model`。 */
84
92
  model: { type: "string" },
93
+ /** 每个 eval 运行多少次,常用于 pass@N。 */
85
94
  runs: { type: "string" },
95
+ /** 设置同时运行的 eval 数量。 */
86
96
  "max-concurrency": { type: "string" },
97
+ /** 单个 attempt 的超时时间,单位毫秒。 */
87
98
  timeout: { type: "string" },
99
+ /** 整次运行的预算上限(美元)。 */
88
100
  budget: { type: "string" },
101
+ /** 只运行带有该 tag 的 eval(见 `defineEval` 的 `tags`)。 */
89
102
  tag: { type: "string" },
103
+ /** 额外写一份 JUnit XML 报告到指定路径,供 CI 消费。 */
90
104
  junit: { type: "string" },
105
+ /** 额外写一份 JSON 结果(`RunSummary` 原样序列化)到指定路径,供 CI 或下游脚本消费。 */
106
+ json: { type: "string" },
107
+ /** `view` 命令专用:把结果查看器静态导出到指定目录。 */
91
108
  out: { type: "string" },
109
+ /** `view` 命令专用:指定本地服务器监听端口。 */
92
110
  port: { type: "string" },
93
- // --sandbox 已移除(sandbox 归 config/experiment);留着解析是为了给出迁移提示而非「未知 flag」。
94
- sandbox: { type: "string" },
95
- // --watch / --json:文档曾接受、尚未实现的 flag。容忍(no-op)而不是硬报「未知 flag」,
96
- // 免得照旧文档写的脚本直接崩;真正实现前不接任何行为。
97
- watch: { type: "boolean" },
98
- json: { type: "boolean" },
99
111
  // show 的证据切面 / 时间轴 / 报告装载(docs-site/zh/guides/viewing-results.mdx)。
112
+ /** `show` 命令专用:渲染单个 eval 的完整对话与工具调用(证据切面)。 */
100
113
  transcript: { type: "boolean" },
114
+ /** `show` 命令专用:渲染单个 eval 的 trace 瀑布文本版(证据切面)。 */
101
115
  trace: { type: "boolean" },
102
116
  // --diff 是布尔;--diff=<路径> 在 parseArgs 前预扫成 diffPath(路径必须 = 连写,
103
117
  // 空格形式的下一个 token 仍是位置参数 = eval id 前缀,与文档一致)。
118
+ /** `show` 命令专用:sandbox 里的文件改动摘要;`--diff=<文件路径>` 看单个文件的完整改动(路径必须 `=` 连写)。 */
104
119
  diff: { type: "boolean" },
120
+ /** `show` 命令专用:跨 run 时间轴,只列真实执行;与 `--report` 互斥。 */
105
121
  history: { type: "boolean" },
122
+ /** `show` / `view` 命令专用:选集只留该实验。 */
106
123
  experiment: { type: "string" },
124
+ /** `show` 命令专用:指定详情 / 证据切面看第几次 attempt(与展示一致的 1 计序号)。 */
107
125
  attempt: { type: "string" },
126
+ /** `show` / `view` 命令专用:钉死看某一个结果目录(历史 run 或 `copySnapshots` 产物)。 */
108
127
  run: { type: "string" },
128
+ /** `show` / `view` 命令专用:把默认榜单整槽换成你的报告文件(默认导出 `defineReport(...)`)。 */
109
129
  report: { type: "string" },
130
+ /** 只打印本次会匹配到的 eval × 运行配置,不实际执行。 */
110
131
  dry: { type: "boolean" },
132
+ /** 关闭控制台 / live 进度输出(reporter 仍会写 artifacts)。 */
111
133
  quiet: { type: "boolean" },
134
+ /** 忽略上次运行结果,不跳过已通过的 (experiment, eval) 组合,强制全部重跑。 */
112
135
  force: { type: "boolean" },
136
+ /** CI 中推荐使用:让软阈值(`soft`)失败也计入整条 eval 的 outcome。 */
113
137
  strict: { type: "boolean" },
138
+ /** 某个 eval 的一次 attempt 通过后,停止该 eval 剩余的 attempts。 */
114
139
  "early-exit": { type: "boolean" },
115
140
  "no-early-exit": { type: "boolean" },
141
+ /** `view` 命令专用:启动后自动打开浏览器(默认行为)。 */
116
142
  open: { type: "boolean" },
117
143
  "no-open": { type: "boolean" },
144
+ /** 打印用法说明并退出。 */
118
145
  help: { type: "boolean", short: "h" },
146
+ /** 打印 niceeval 的版本号并退出。 */
147
+ version: { type: "boolean", short: "v" },
119
148
  } as const;
120
149
 
121
150
  function numberFlag(name: string, raw: string | undefined): number | undefined {
@@ -153,11 +182,6 @@ function parseArgs(argv: string[]): { command: string; positionals: string[]; fl
153
182
  process.exit(1);
154
183
  }
155
184
 
156
- if (values.sandbox !== undefined) {
157
- process.stderr.write(t("cli.sandboxFlagRemoved"));
158
- process.exit(1);
159
- }
160
-
161
185
  // 第一个位置参数若是已知命令,则为命令;其余是 eval id 前缀 / view 输入。
162
186
  const commands = new Set(["exp", "show", "list", "view", "clean", "init", "watch", "run"]);
163
187
  let command = "run";
@@ -176,6 +200,7 @@ function parseArgs(argv: string[]): { command: string; positionals: string[]; fl
176
200
  budget: numberFlag("budget", values.budget as string | undefined),
177
201
  tag: values.tag as string | undefined,
178
202
  junit: values.junit as string | undefined,
203
+ json: values.json as string | undefined,
179
204
  out: values.out as string | undefined,
180
205
  port: numberFlag("port", values.port as string | undefined),
181
206
  dry: values.dry === true,
@@ -185,6 +210,7 @@ function parseArgs(argv: string[]): { command: string; positionals: string[]; fl
185
210
  earlyExit: values["no-early-exit"] === true ? false : values["early-exit"] === true ? true : undefined,
186
211
  open: values["no-open"] === true ? false : values.open === true ? true : undefined,
187
212
  help: values.help === true,
213
+ version: values.version === true,
188
214
  transcript: values.transcript === true,
189
215
  trace: values.trace === true,
190
216
  diff: values.diff === true && diffPath === undefined,
@@ -324,17 +350,28 @@ function evalsFilterFromExperiment(
324
350
  return (id) => expFilter(id) && patternFilter(id);
325
351
  }
326
352
 
353
+ /** package.json 的 version 字段;-v/--version 直接回显这个号。 */
354
+ async function packageVersion(): Promise<string> {
355
+ const raw = await readFile(new URL("../package.json", import.meta.url), "utf-8");
356
+ return (JSON.parse(raw) as { version: string }).version;
357
+ }
358
+
327
359
  async function main(): Promise<void> {
328
360
  const cwd = process.cwd();
329
361
  await loadDotenv(cwd);
330
362
  const { command, positionals, flags } = parseArgs(process.argv.slice(2));
331
363
 
332
- // --help 不需要 config,先于一切命令处理。
364
+ // --help / --version 不需要 config,先于一切命令处理。
333
365
  if (flags.help) {
334
366
  process.stdout.write(t("cli.help"));
335
367
  process.exit(0);
336
368
  }
337
369
 
370
+ if (flags.version) {
371
+ process.stdout.write(`${await packageVersion()}\n`);
372
+ process.exit(0);
373
+ }
374
+
338
375
  if (command === "view") {
339
376
  // 位置参数 = eval id 前缀(收窄报告槽选集);存在的文件路径 = 单文件模式;
340
377
  // 结果目录经 --run 递入;--report 整槽替换报告槽(与 show --report 吃同一个文件)。
@@ -435,7 +472,7 @@ async function main(): Promise<void> {
435
472
  agent: exp.agent,
436
473
  model: exp.model,
437
474
  reasoningEffort: exp.reasoningEffort,
438
- params: exp.params ?? {},
475
+ flags: exp.flags ?? {},
439
476
  runs: flags.runs ?? envNumber("NICEEVAL_RUNS") ?? exp.runs ?? 1,
440
477
  earlyExit: flags.earlyExit ?? exp.earlyExit ?? true,
441
478
  sandbox: exp.sandbox ?? config.sandbox,
@@ -451,7 +488,7 @@ async function main(): Promise<void> {
451
488
  }
452
489
  } else {
453
490
  // 裸 run / `niceeval <eval>` 不再执行。运行配置必须来自 experiments/,
454
- // 这样 agent/model/params/runs/budget 与结果聚合都有可签入的身份。
491
+ // 这样 agent/model/flags/runs/budget 与结果聚合都有可签入的身份。
455
492
  const experiments = await discoverExperiments(cwd);
456
493
  const asExp = experiments.filter((e) =>
457
494
  positionals.some((p) => e.group === p || e.id === p || e.id.startsWith(p + "/")),
@@ -512,6 +549,7 @@ async function main(): Promise<void> {
512
549
  const artifacts = ArtifactsReporter();
513
550
  reporters.push(artifacts);
514
551
  if (flags.junit) reporters.push(JUnit(flags.junit));
552
+ if (flags.json) reporters.push(Json(flags.json));
515
553
  reporters.push(...(config.reporters ?? []));
516
554
 
517
555
  // Ctrl+C / kill 的三级响应,核心目标:任何情况下都不留下孤儿沙箱。
@@ -57,7 +57,7 @@ function makeContext(agent: Agent, sandbox = fakeSandbox(), evalBaseDir?: string
57
57
  return createEvalContext({
58
58
  agent,
59
59
  sandbox,
60
- params: {},
60
+ flags: {},
61
61
  signal: new AbortController().signal,
62
62
  log: () => {},
63
63
  judge: undefined,
@@ -61,7 +61,7 @@ export interface ContextDeps {
61
61
  sandbox: Sandbox;
62
62
  model?: string;
63
63
  reasoningEffort?: string;
64
- params: Record<string, unknown>;
64
+ flags: Record<string, unknown>;
65
65
  signal: AbortSignal;
66
66
  log(msg: string): void;
67
67
  judge: JudgeConfig | undefined;
@@ -92,7 +92,7 @@ export function createEvalContext(deps: ContextDeps): { context: TestContext; st
92
92
  sandbox: deps.sandbox,
93
93
  model: deps.model,
94
94
  reasoningEffort: deps.reasoningEffort,
95
- params: deps.params,
95
+ flags: deps.flags,
96
96
  signal: deps.signal,
97
97
  log: deps.log,
98
98
  telemetry: deps.telemetry,
@@ -284,7 +284,7 @@ export function createEvalContext(deps: ContextDeps): { context: TestContext; st
284
284
  signal: deps.signal,
285
285
  model: deps.model,
286
286
  reasoningEffort: deps.reasoningEffort,
287
- params: deps.params,
287
+ flags: deps.flags,
288
288
  log: deps.log,
289
289
  skip: (reason: string) => {
290
290
  if (reason.trim().length === 0) throw new Error(t("context.skipEmpty"));
@@ -88,7 +88,7 @@ export interface SessionDeps {
88
88
  sandbox: Sandbox;
89
89
  model?: string;
90
90
  reasoningEffort?: string;
91
- params: Record<string, unknown>;
91
+ flags: Record<string, unknown>;
92
92
  signal: AbortSignal;
93
93
  log(msg: string): void;
94
94
  /** tracing agent 的 OTLP 端点(经 send ctx 透给 adapter,用于注入导出 env)。 */
@@ -138,7 +138,7 @@ export class SessionManager {
138
138
  signal: this.deps.signal,
139
139
  model: this.deps.model,
140
140
  reasoningEffort: this.deps.reasoningEffort,
141
- params: this.deps.params,
141
+ flags: this.deps.flags,
142
142
  sandbox: this.deps.sandbox,
143
143
  session,
144
144
  telemetry: this.deps.telemetry,
@@ -274,8 +274,8 @@ export interface TestContext {
274
274
  readonly model?: string;
275
275
  /** 本次 attempt 的推理努力程度(如 "low"/"medium"/"high",取值由 adapter/模型决定)。 */
276
276
  readonly reasoningEffort?: string;
277
- /** 本次 attempt 生效的 params(experiment.params 与 CLI flag 合并后的只读视图)。 */
278
- readonly params: Readonly<Record<string, unknown>>;
277
+ /** 本次 attempt 生效的实验 flags(experiment.flags 的只读视图;实验条件,非命令行开关)。 */
278
+ readonly flags: Readonly<Record<string, unknown>>;
279
279
  /** 打一行调试日志;有 live 进度回调时走该回调,否则落到 stderr,不出现在最终结果里。 */
280
280
  log(msg: string): void;
281
281
  /** 立即中止本 eval 并标记为 skipped(outcome / EvalResult.skipReason),reason 不能为空。 */
package/src/i18n/en.ts CHANGED
@@ -59,7 +59,7 @@ export const en = {
59
59
  "Flags:\n" +
60
60
  " --runs n --max-concurrency n --timeout ms --budget usd --tag t\n" +
61
61
  " --early-exit / --no-early-exit --strict --force --dry --quiet\n" +
62
- " --junit path --out dir --port n --open / --no-open -h, --help\n\n" +
62
+ " --junit path --json path --out dir --port n --open / --no-open -h, --help -v, --version\n\n" +
63
63
  "Positional args only select which evals to run (id prefixes); which agent and\n" +
64
64
  "how to run come from experiments/ + flags. Env overrides (flag > env > config):\n" +
65
65
  " NICEEVAL_RUNS NICEEVAL_MAX_CONCURRENCY NICEEVAL_TIMEOUT NICEEVAL_BUDGET\n",
@@ -94,10 +94,10 @@ export const en = {
94
94
  " Docs: node_modules/niceeval/docs-site/zh/guides/write-experiment.mdx\n",
95
95
  "cli.run.experimentRequiredHint": "Hint: \"{{pattern}}\" is an experiment{{kind}}; you probably meant: niceeval exp {{pattern}}\n",
96
96
  "cli.run.experimentRequiredKnown": "Discovered experiments: {{experiments}}\n",
97
- "cli.sandboxFlagRemoved": "`--sandbox` is not a CLI flag. Set `sandbox` in the experiment (or `niceeval.config.ts` as a project-wide fallback) to dockerSandbox() / vercelSandbox() / e2bSandbox() (import from \"niceeval/sandbox\").\n",
98
97
  "cli.unimplemented": "Command \"{{command}}\" is not implemented yet (MVP).\n",
99
98
  "cli.view.exportedDir": "Exported static report site: {{out}} (serve the whole directory with any static host; opening index.html via file:// cannot fetch artifacts)\n",
100
99
  "cli.view.incompatible": "{{dir}}: written by niceeval {{producer}} (schemaVersion {{schemaVersion}}); this CLI reads schemaVersion {{supported}}.\nRun `{{command}}` to view it.\n",
100
+ "cli.view.noResults": "No results found under {{root}}. Run `niceeval exp` first, then `niceeval view`.\n",
101
101
  "cli.view.incompatibleForeign": "{{dir}}: written by {{name}} {{version}} (schemaVersion {{schemaVersion}}); this CLI reads schemaVersion {{supported}}.\nOpen this report with the tool that produced it.\n",
102
102
  "cli.view.url": "niceeval view: {{url}}\n",
103
103
  "context.capabilityMissing":
package/src/i18n/zh-CN.ts CHANGED
@@ -55,7 +55,7 @@ export const zhCN = {
55
55
  "标志:\n" +
56
56
  " --runs n --max-concurrency n --timeout ms --budget usd --tag t\n" +
57
57
  " --early-exit / --no-early-exit --strict --force --dry --quiet\n" +
58
- " --junit path --out dir --port n --open / --no-open -h, --help\n\n" +
58
+ " --junit path --json path --out dir --port n --open / --no-open -h, --help -v, --version\n\n" +
59
59
  "位置参数只选「跑哪些 eval」(id 前缀);对着哪个 agent、怎么跑来自 experiments/ 与\n" +
60
60
  "标志。环境变量覆盖(标志 > 环境变量 > config):\n" +
61
61
  " NICEEVAL_RUNS NICEEVAL_MAX_CONCURRENCY NICEEVAL_TIMEOUT NICEEVAL_BUDGET\n",
@@ -91,10 +91,10 @@ export const zhCN = {
91
91
  " 文档:node_modules/niceeval/docs-site/zh/guides/write-experiment.mdx\n",
92
92
  "cli.run.experimentRequiredHint": "提示:\"{{pattern}}\" 是实验{{kind}},你大概想跑:niceeval exp {{pattern}}\n",
93
93
  "cli.run.experimentRequiredKnown": "已发现实验:{{experiments}}\n",
94
- "cli.sandboxFlagRemoved": "`--sandbox` 不是 CLI flag。请在 experiment(或 niceeval.config.ts 做全项目兜底)里把 sandbox 设成 dockerSandbox() / vercelSandbox() / e2bSandbox()(从 \"niceeval/sandbox\" 导入)。\n",
95
94
  "cli.unimplemented": "命令 \"{{command}}\" 暂未实现(MVP)。\n",
96
95
  "cli.view.exportedDir": "已导出静态查看站:{{out}}(整个目录可直接静态托管;本地打开 {{out}}/index.html 需经 http 服务,file:// 下工件 fetch 不可用)\n",
97
96
  "cli.view.incompatible": "{{dir}}: 由 niceeval {{producer}} 写入(schemaVersion {{schemaVersion}}),当前 CLI 只读 schemaVersion {{supported}}。\n运行 `{{command}}` 查看这份报告。\n",
97
+ "cli.view.noResults": "{{root}} 下没有结果。先 `niceeval exp` 跑一轮,再 `niceeval view`。\n",
98
98
  "cli.view.incompatibleForeign": "{{dir}}: 由 {{name}} {{version}} 写入(schemaVersion {{schemaVersion}}),当前 CLI 只读 schemaVersion {{supported}}。\n请用写出它的那个工具查看这份报告。\n",
99
99
  "cli.view.url": "niceeval view: {{url}}\n",
100
100
  "context.capabilityMissing":