niceeval 0.12.0 → 0.12.1-canary.22

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.
@@ -42,6 +42,13 @@ description: "按被测对象选择可运行的 NiceEval 示例:Agent Framewor
42
42
  - [查看在线报告](https://niceeval.com/showcase/memory)
43
43
  - [查看项目源码](https://github.com/CorrectRoadH/coding-agent-memory-evals)
44
44
 
45
+ ### Terminal-Bench
46
+
47
+ [terminal-bench](https://github.com/NiceEval/terminal-bench) 用 NiceEval 跑经过审核的 Harbor Terminal-Bench 题包,判分仍走原版 `run-tests.sh`,并把各 coding agent 与模型并排比较。
48
+
49
+ - [查看在线报告](https://niceeval.com/showcase/terminal-bench)
50
+ - [查看项目源码](https://github.com/NiceEval/terminal-bench)
51
+
45
52
  ## Examples 的收录边界
46
53
 
47
54
  - 单段 API 用法放进任务教程或 Reference。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "niceeval",
3
- "version": "0.12.0",
3
+ "version": "0.12.1-canary.22",
4
4
  "description": "Agent-native eval tool — eval agents, services, functions, and coding-agent fixtures",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -186,14 +186,24 @@ describe("acceptPreparedAttempt", () => {
186
186
  roots.push(root);
187
187
  const currentManifest = { algorithmVersion: 2, coverageVersion: 1, config: {}, source: {}, data: {} };
188
188
  const sources = [makeSource(root, { id: "e" }), makeSource(root, { id: "f" })];
189
+ // 模拟 prepareAcceptTarget 的真实形状:每条 locator 的 currentExperiment.selectedEvalIds
190
+ // 只有自己那一题(cliPatterns 单 id),sandboxPlansByEval 也只有自己。
189
191
  const prepared = await Promise.all(sources.map((source) => prepareAcceptedAttempt({
190
192
  recordRoot: root,
191
193
  source,
192
194
  pair: makePair({}, source.evalId),
193
195
  currentFingerprint: `current-${source.evalId}`,
194
196
  currentManifest,
195
- currentConfigHash: `config-${source.evalId}`,
196
- knownEvalIds: ["e", "f"],
197
+ currentConfigHash: "shared-config",
198
+ currentExperiment: {
199
+ attempts: 1,
200
+ earlyExit: true,
201
+ selectedEvalIds: [source.evalId],
202
+ sandboxLayer: {},
203
+ sandboxPlansByEval: { [source.evalId]: { plan: source.evalId } },
204
+ agentInstalls: [],
205
+ },
206
+ knownEvalIds: [source.evalId],
197
207
  now: () => "2026-01-02T00:00:00.000Z",
198
208
  })));
199
209
 
@@ -206,6 +216,22 @@ describe("acceptPreparedAttempt", () => {
206
216
  accepted.map((entry) => entry.sourceLocator),
207
217
  );
208
218
  expect(accepted[0]!.record.experiments[0]!.runs).toHaveLength(1);
219
+
220
+ // 快照级覆盖声明必须是整组,不能只剩 groupFirst 的单题——否则 currentSample / view 塌成 1 题。
221
+ const runMeta = JSON.parse(
222
+ await readFile(join(accepted[0]!.run.dir, "run.json"), "utf-8"),
223
+ ) as {
224
+ experiment?: { selectedEvalIds?: string[]; sandboxPlansByEval?: globalThis.Record<string, unknown> };
225
+ knownEvalIds?: string[];
226
+ };
227
+ expect(runMeta.experiment?.selectedEvalIds?.slice().sort()).toEqual(["e", "f"]);
228
+ expect(Object.keys(runMeta.experiment?.sandboxPlansByEval ?? {}).sort()).toEqual(["e", "f"]);
229
+ expect(runMeta.knownEvalIds?.slice().sort()).toEqual(["e", "f"]);
230
+
231
+ // 读面:currentSample 必须看到两条,不能按错误收窄的 selectedEvalIds 只留第一条。
232
+ const { currentSample } = await import("../sample/index.ts");
233
+ const sample = currentSample(accepted[0]!.record);
234
+ expect(sample.attempts.map((a) => a.evalId).sort()).toEqual(["e", "f"]);
209
235
  });
210
236
 
211
237
  it("批量 prepare 中任一条失败时不创建 snapshot", async () => {
@@ -520,7 +520,7 @@ export async function writeAcceptedAttempts(
520
520
  // 返回值必须按调用方传入的 preparedAttempts 顺序还原;分组打乱了处理顺序,用引用做索引。
521
521
  const locatorByPrepared = new Map<PreparedAcceptedAttempt, AttemptLocator>();
522
522
  for (const [experimentId, group] of groups) {
523
- // 快照级字段(agent/model/configHash/currentExperiment/name)按本组取,不再从全批 first 拿——
523
+ // 快照级字段(agent/model/configHash/name)按本组取,不再从全批 first 拿——
524
524
  // 否则跨 experiment 批次会把另一个 experiment 的身份写进这个 experiment 的 run.json。
525
525
  const groupFirst = group[0]!;
526
526
  // manifests 是这个 experiment 自己的袋子;不同 experiment 各自独立,不共享同一个对象,
@@ -529,15 +529,21 @@ export async function writeAcceptedAttempts(
529
529
  const knownEvalIds = new Set<string>();
530
530
  for (const prepared of group) {
531
531
  manifests[prepared.source.evalId] = prepared.currentManifest;
532
+ knownEvalIds.add(prepared.source.evalId);
532
533
  for (const evalId of prepared.knownEvalIds ?? []) knownEvalIds.add(evalId);
533
534
  }
535
+ // prepare 阶段每条 locator 单独按「只选中自己那一题」重算指纹,currentExperiment.selectedEvalIds
536
+ // 因此只有一个 id。快照级覆盖声明必须是本 experiment 组**全部**接受的题——currentSample
537
+ // 按 selectedEvalIds 过滤贡献范围,只写 groupFirst 会让批量 accept 的 view/show 塌成 1 题
538
+ // (MemoryBench: 36 条 result.json 在盘、首页 1/36)。
539
+ const experiment = experimentForAcceptedGroup(group);
534
540
  const snapshot = await writer.run({
535
541
  experimentId,
536
542
  agent: groupFirst.pair.run.agent.name,
537
543
  ...(groupFirst.pair.run.model !== undefined ? { model: groupFirst.pair.run.model } : {}),
538
544
  startedAt: now,
539
545
  configHash: groupFirst.currentConfigHash,
540
- ...(groupFirst.currentExperiment === undefined ? {} : { experiment: groupFirst.currentExperiment }),
546
+ ...(experiment === undefined ? {} : { experiment }),
541
547
  ...(knownEvalIds.size === 0 ? {} : { knownEvalIds: [...knownEvalIds] }),
542
548
  manifests,
543
549
  ...(groupFirst.name === undefined ? {} : { name: groupFirst.name }),
@@ -570,6 +576,39 @@ export async function writeAcceptedAttempts(
570
576
  });
571
577
  }
572
578
 
579
+ /**
580
+ * 把同 experiment 一批 accept 的覆盖声明合成快照级 `ExperimentRunInfo`。
581
+ * 单条 prepare 的 `currentExperiment.selectedEvalIds` / `sandboxPlansByEval` 只含自己那题
582
+ * (指纹重算需要);封口时必须扩成整组,否则 Sample 读面按 selectedEvalIds 过滤会丢掉其余题。
583
+ */
584
+ function experimentForAcceptedGroup(
585
+ group: readonly PreparedAcceptedAttempt[],
586
+ ): EvalResult["experiment"] | undefined {
587
+ const bases = group
588
+ .map((prepared) => prepared.currentExperiment)
589
+ .filter((experiment): experiment is NonNullable<typeof experiment> => experiment !== undefined);
590
+ const base = bases[0];
591
+ if (base === undefined) return undefined;
592
+
593
+ const selectedEvalIds = new Set<string>(base.selectedEvalIds);
594
+ const sandboxPlansByEval: globalThis.Record<string, JsonValue> = {
595
+ ...base.sandboxPlansByEval,
596
+ };
597
+ for (const prepared of group) {
598
+ selectedEvalIds.add(prepared.source.evalId);
599
+ const plans = prepared.currentExperiment?.sandboxPlansByEval;
600
+ if (plans === undefined) continue;
601
+ for (const [evalId, plan] of Object.entries(plans)) {
602
+ sandboxPlansByEval[evalId] = plan;
603
+ }
604
+ }
605
+ return {
606
+ ...base,
607
+ selectedEvalIds: [...selectedEvalIds],
608
+ sandboxPlansByEval,
609
+ };
610
+ }
611
+
573
612
  function acceptedResultFor(
574
613
  prepared: PreparedAcceptedAttempt,
575
614
  locator: AttemptLocator,