niceeval 0.11.1 → 0.11.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.
- package/INDEX.md +1 -0
- package/README.md +4 -2
- package/README.zh.md +4 -2
- package/dist/agents/types.d.ts +2 -2
- package/dist/o11y/types.d.ts +3 -3
- package/dist/report/assets/colors.d.ts +1 -1
- package/dist/report/assets/colors.js +1 -1
- package/dist/report/components/attempt-detail/AttemptSource.js +1 -2
- package/dist/report/components/attempt-detail/compute.d.ts +4 -4
- package/dist/report/components/attempt-detail/compute.js +6 -6
- package/dist/report/components/attempt-detail/faces.d.ts +1 -1
- package/dist/report/components/attempt-detail/faces.js +3 -3
- package/dist/report/components/entity-lists/AttemptList.d.ts +3 -3
- package/dist/report/components/entity-lists/AttemptList.js +3 -3
- package/dist/report/components/entity-lists/ExperimentList.js +2 -2
- package/dist/report/components/entity-lists/compute.js +1 -1
- package/dist/report/components/entity-lists/faces.js +1 -1
- package/dist/report/components/entity-lists/index.d.ts +1 -1
- package/dist/report/components/entity-lists/index.js +1 -1
- package/dist/report/components/metric-views/chart-math.d.ts +2 -2
- package/dist/report/components/metric-views/chart-math.js +2 -2
- package/dist/report/components/metric-views/compute.d.ts +2 -3
- package/dist/report/components/metric-views/compute.js +3 -4
- package/dist/report/components/shared.d.ts +1 -1
- package/dist/report/components/site-components/compute.d.ts +5 -5
- package/dist/report/components/site-components/compute.js +6 -6
- package/dist/report/components/site-components/index.d.ts +7 -7
- package/dist/report/components/site-components/index.js +7 -7
- package/dist/report/components/site-components/scope-warnings.d.ts +1 -1
- package/dist/report/components/site-components/scope-warnings.js +3 -3
- package/dist/report/components/site-components/snapshot-diagnostics.js +1 -1
- package/dist/report/components/summaries/compute.d.ts +1 -1
- package/dist/report/components/summaries/compute.js +2 -2
- package/dist/report/components/summaries/faces.js +1 -1
- package/dist/report/components/summaries/index.d.ts +1 -1
- package/dist/report/components/summaries/index.js +1 -1
- package/dist/report/index.js +1 -1
- package/dist/report/model/aggregate.d.ts +2 -2
- package/dist/report/model/aggregate.js +2 -2
- package/dist/report/model/format.d.ts +2 -2
- package/dist/report/model/format.js +2 -2
- package/dist/report/model/metrics.js +3 -3
- package/dist/report/model/types.d.ts +11 -12
- package/dist/results/locator.js +1 -1
- package/dist/results/select.d.ts +4 -4
- package/dist/results/select.js +5 -5
- package/dist/results/types.d.ts +8 -8
- package/dist/runner/types.d.ts +14 -14
- package/dist/sandbox/resolve.d.ts +1 -1
- package/dist/sandbox/types.d.ts +1 -1
- package/docs-site/zh/reference/builtin-agents.mdx +21 -0
- package/docs-site/zh/reference/define-agent.mdx +1 -1
- package/docs-site/zh/reference/report-components.mdx +156 -74
- package/docs-site/zh/tutorials/custom-reports.mdx +112 -22
- package/docs-site/zh/tutorials/sandbox-agent.mdx +3 -4
- package/docs-site/zh/tutorials/sandbox-providers.mdx +15 -15
- package/docs-site/zh/tutorials/theming.mdx +169 -0
- package/package.json +2 -1
- package/src/agents/ai-sdk.test.ts +1 -1
- package/src/agents/bub-install-spec.ts +26 -8
- package/src/agents/bub.ts +40 -15
- package/src/agents/builtin.ts +11 -1
- package/src/agents/coding-cli-versions.ts +67 -0
- package/src/agents/hermes.ts +235 -0
- package/src/agents/index.ts +4 -0
- package/src/agents/langgraph.test.ts +1 -1
- package/src/agents/manifest.ts +1 -1
- package/src/agents/openai-compat.test.ts +1 -1
- package/src/agents/openclaw.ts +103 -24
- package/src/agents/opencode.ts +183 -0
- package/src/agents/sdk-streams.test.ts +1 -1
- package/src/agents/shared.ts +13 -2
- package/src/agents/types.ts +2 -2
- package/src/context/session.test.ts +1 -1
- package/src/context/session.ts +1 -1
- package/src/o11y/cost.ts +1 -1
- package/src/o11y/parsers/bub.test.ts +1 -1
- package/src/o11y/parsers/bub.ts +1 -1
- package/src/o11y/parsers/codex.test.ts +1 -1
- package/src/o11y/parsers/codex.ts +1 -1
- package/src/o11y/parsers/hermes.ts +198 -0
- package/src/o11y/parsers/openclaw.ts +25 -3
- package/src/o11y/parsers/opencode.ts +295 -0
- package/src/o11y/types.ts +3 -3
- package/src/report/assets/colors.ts +1 -1
- package/src/report/assets/styles.css +17 -15
- package/src/report/components/attempt-detail/AttemptAssertions.tsx +1 -1
- package/src/report/components/attempt-detail/AttemptConversation.tsx +1 -1
- package/src/report/components/attempt-detail/AttemptDiagnostics.tsx +1 -1
- package/src/report/components/attempt-detail/AttemptDiff.tsx +1 -1
- package/src/report/components/attempt-detail/AttemptError.tsx +1 -1
- package/src/report/components/attempt-detail/AttemptFixPrompt.tsx +1 -1
- package/src/report/components/attempt-detail/AttemptSource.tsx +2 -3
- package/src/report/components/attempt-detail/AttemptSummary.tsx +1 -1
- package/src/report/components/attempt-detail/AttemptTimeline.tsx +1 -1
- package/src/report/components/attempt-detail/AttemptTrace.tsx +1 -1
- package/src/report/components/attempt-detail/UsageTable.tsx +1 -1
- package/src/report/components/attempt-detail/attempt-components.test.tsx +1 -1
- package/src/report/components/attempt-detail/compute.ts +6 -6
- package/src/report/components/attempt-detail/faces.ts +3 -3
- package/src/report/components/attempt-detail/index.tsx +1 -1
- package/src/report/components/entity-lists/AttemptList.tsx +3 -3
- package/src/report/components/entity-lists/ExperimentList.tsx +2 -2
- package/src/report/components/entity-lists/compute.ts +1 -1
- package/src/report/components/entity-lists/faces.ts +1 -1
- package/src/report/components/entity-lists/index.tsx +1 -1
- package/src/report/components/metric-views/DeltaTable.tsx +1 -2
- package/src/report/components/metric-views/StabilityMatrix.tsx +1 -1
- package/src/report/components/metric-views/chart-math.test.ts +1 -1
- package/src/report/components/metric-views/chart-math.ts +2 -2
- package/src/report/components/metric-views/compute.ts +3 -4
- package/src/report/components/shared.ts +1 -1
- package/src/report/components/site-components/CopyFixPrompt.tsx +1 -1
- package/src/report/components/site-components/HeroCard.tsx +1 -1
- package/src/report/components/site-components/PoweredBy.tsx +1 -1
- package/src/report/components/site-components/ScopeWarnings.tsx +1 -1
- package/src/report/components/site-components/SnapshotDiagnostics.tsx +1 -1
- package/src/report/components/site-components/TraceWaterfall.tsx +1 -1
- package/src/report/components/site-components/compute.ts +6 -6
- package/src/report/components/site-components/index.tsx +7 -7
- package/src/report/components/site-components/scope-warnings.ts +3 -3
- package/src/report/components/site-components/snapshot-diagnostics.ts +1 -1
- package/src/report/components/summaries/ScopeSummary.tsx +2 -2
- package/src/report/components/summaries/compute.ts +2 -2
- package/src/report/components/summaries/faces.ts +1 -1
- package/src/report/components/summaries/index.tsx +1 -1
- package/src/report/index.ts +1 -1
- package/src/report/model/aggregate.ts +2 -2
- package/src/report/model/format.ts +2 -2
- package/src/report/model/metrics.ts +3 -3
- package/src/report/model/types.ts +12 -13
- package/src/results/annotated-source.test.ts +1 -1
- package/src/results/attempt-evidence.test.ts +1 -1
- package/src/results/copy.ts +3 -3
- package/src/results/format.ts +3 -3
- package/src/results/host-equivalence.test.ts +6 -6
- package/src/results/index.ts +1 -1
- package/src/results/locator.test.ts +2 -2
- package/src/results/locator.ts +1 -1
- package/src/results/open.ts +2 -2
- package/src/results/publish.ts +1 -1
- package/src/results/results.test.ts +6 -323
- package/src/results/select.test.ts +438 -0
- package/src/results/select.ts +6 -6
- package/src/results/skipped-notice.ts +1 -1
- package/src/results/truncate.ts +2 -2
- package/src/results/types.ts +9 -9
- package/src/results/writer.ts +4 -4
- package/src/runner/attempt.test.ts +2 -2
- package/src/runner/attempt.ts +6 -6
- package/src/runner/eval-source.test.ts +1 -1
- package/src/runner/eval-source.ts +1 -1
- package/src/runner/reporters/artifacts.ts +1 -1
- package/src/runner/run.ts +2 -2
- package/src/runner/timing.ts +1 -1
- package/src/runner/types.ts +14 -14
- package/src/sandbox/docker-agent-image.ts +36 -0
- package/src/sandbox/e2b-agent-template.test.ts +6 -21
- package/src/sandbox/e2b-agent-template.ts +44 -11
- package/src/sandbox/index.ts +8 -0
- package/src/sandbox/official-baselines.test.ts +175 -0
- package/src/sandbox/resolve.ts +1 -1
- package/src/sandbox/types.ts +1 -1
- package/src/scoring/diff.ts +1 -1
- package/src/scoring/types.ts +1 -1
- package/src/shared/facts.ts +1 -1
- package/src/show/index.ts +1 -1
- package/src/show/render.ts +1 -1
- package/src/show/show.test.ts +3 -3
- package/src/view/app/App.tsx +3 -1
- package/src/view/client-dist/app.css +1 -1
- package/src/view/client-dist/app.js +1 -1
- package/src/view/data.test.ts +2 -2
- package/src/view/data.ts +1 -1
- package/src/view/site-base.test.ts +51 -0
- package/src/view/site.ts +16 -0
- package/src/view/styles.css +1 -2
- package/src/view/template.html +1 -0
- package/src/view/view-report.test.ts +1 -1
package/src/runner/attempt.ts
CHANGED
|
@@ -204,7 +204,7 @@ export function runAttemptEffect(
|
|
|
204
204
|
// 非零 Sandbox 命令证据的累加器(与 diagnostics/facts 同一种「共享容器」模式):
|
|
205
205
|
// withCommandTiming 在 CommandResult 交还调用方之前把每条非零退出命令 push 进来,
|
|
206
206
|
// runAttemptBody 的 finally 与本函数的超时/中断兜底分支都读同一个数组引用
|
|
207
|
-
// (见 docs/feature/
|
|
207
|
+
// (见 docs/feature/record/architecture.md「commandsjson」「证据在 CommandResult 返回
|
|
208
208
|
// 调用方之前写入内存」)。
|
|
209
209
|
const commands: FailedCommandEvidence[] = [];
|
|
210
210
|
const recordDiagnostic = (input: DiagnosticInput) => {
|
|
@@ -1068,7 +1068,7 @@ async function runAttemptBody(
|
|
|
1068
1068
|
const durationMs = Date.now() - t0;
|
|
1069
1069
|
const o11y = buildO11ySummary(events);
|
|
1070
1070
|
// 实测成本(网关带回)优先,缺则按 model + 用量查价格表估算(见 o11y/cost.ts)。
|
|
1071
|
-
// 权威唯一在 result.json 的 estimatedCostUSD;o11y.json 只留行为计数(见 docs/feature/
|
|
1071
|
+
// 权威唯一在 result.json 的 estimatedCostUSD;o11y.json 只留行为计数(见 docs/feature/record/architecture.md「o11y.json」)。
|
|
1072
1072
|
const cost = usage.costUSD ?? estimateCost(run.model, usage, config.pricing);
|
|
1073
1073
|
|
|
1074
1074
|
// 收 test 引用到的 eval 源码(按 send / 断言的 loc 去重),供 view 渲染代码视图。
|
|
@@ -1090,7 +1090,7 @@ async function runAttemptBody(
|
|
|
1090
1090
|
scoring: evalDef.scoring ?? "pass",
|
|
1091
1091
|
// 只在计分制 eval 上落 scoreEntries(t.score 直接给分记录);通过制 eval 的 t 上没有
|
|
1092
1092
|
// t.score,collector.scoreEntries 恒为空数组,省略即等价于空数组
|
|
1093
|
-
// (见 docs/feature/
|
|
1093
|
+
// (见 docs/feature/record/architecture.md「result.json」)。
|
|
1094
1094
|
...(evalDef.scoring === "points" ? { scoreEntries: state.collector.scoreEntries } : {}),
|
|
1095
1095
|
usage,
|
|
1096
1096
|
estimatedCostUSD: cost,
|
|
@@ -1129,7 +1129,7 @@ async function runAttemptBody(
|
|
|
1129
1129
|
return value;
|
|
1130
1130
|
} finally {
|
|
1131
1131
|
// 收尾段一律在 finally 跑(主链成败都执行),不改判定,各自兜错(diagnostic)、各自计时
|
|
1132
|
-
// (不计入 durationMs 口径,见 docs/feature/
|
|
1132
|
+
// (不计入 durationMs 口径,见 docs/feature/record/architecture.md)。执行序与 LifecyclePhase
|
|
1133
1133
|
// 闭集声明一致:eval.teardown → agent.teardown → sandbox.teardown;各段可独立标 failed。
|
|
1134
1134
|
// 沙箱 stop / 接收器 close 不在这里 —— 由 runAttemptEffect 的 Scope 在本函数返回后回收,
|
|
1135
1135
|
// 并经 finalizer 计成 sandbox.stop。触发规则统一是「同层 setup 时点走到过」(setup / test
|
|
@@ -1216,7 +1216,7 @@ function teardownDiagnostic(phase: LifecyclePhase, e: unknown): DiagnosticRecord
|
|
|
1216
1216
|
* (有界脱敏摘要 + exitCode;env 值与 stdout/stderr 不进入时间树)。Proxy 只拦这两个方法,
|
|
1217
1217
|
* provider 内部 `this.runCommand(...)` 转调不经过它——不形成重复节点。非零退出的命令额外
|
|
1218
1218
|
* 把完整 `CommandResult` 登记进 `commands` 累加器,登记发生在把结果交还调用方**之前**
|
|
1219
|
-
* (docs/feature/
|
|
1219
|
+
* (docs/feature/record/architecture.md「commandsjson」);登记不改变 `runCommand` 的
|
|
1220
1220
|
* 返回/抛错语义,调用方可以处理非零退出并继续,证据仍保留。只记录成功拿到 `CommandResult`
|
|
1221
1221
|
* 且 `exitCode !== 0` 的情形——`fn()` 本身抛错(如传输失败)不产出 `CommandResult`,不在这
|
|
1222
1222
|
* 条证据线里,那类失败只落进时间树的 `failed` 标记。
|
|
@@ -1309,7 +1309,7 @@ async function collectSources(
|
|
|
1309
1309
|
return out;
|
|
1310
1310
|
}
|
|
1311
1311
|
|
|
1312
|
-
/** 解析后运行配置的穷尽投影(ExperimentRunInfo,见 docs/feature/
|
|
1312
|
+
/** 解析后运行配置的穷尽投影(ExperimentRunInfo,见 docs/feature/record/architecture.md):
|
|
1313
1313
|
* agent/model 只在快照顶层,这里不复制;sandbox 只经 provider 的公开参数投影落盘。 */
|
|
1314
1314
|
export function experimentRunInfo(run: AgentRun, configSandbox?: Config["sandbox"]): EvalResult["experiment"] {
|
|
1315
1315
|
return {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// cases: docs/engineering/testing/unit/experiments-runner.md
|
|
2
|
-
// captureEvalSource 的单测(定稿见 docs/feature/
|
|
2
|
+
// captureEvalSource 的单测(定稿见 docs/feature/record/architecture.md「sources.json」)。
|
|
3
3
|
// 覆盖:哈希确定性、path 相对 root 计算、CRLF/BOM 归一化行为、与 results/source-hash.ts
|
|
4
4
|
// 算法保持一致(两处哈希必须逐字节相同,见 eval-source.ts 顶部注释)。
|
|
5
5
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Eval 源码捕获:discovery 时读一次 eval 定义文件、归一化、算 SHA-256(定稿见
|
|
2
|
-
// docs/concepts.md「标注 Eval 源码」、docs/feature/
|
|
2
|
+
// docs/concepts.md「标注 Eval 源码」、docs/feature/record/architecture.md「sources.json」)。
|
|
3
3
|
//
|
|
4
4
|
// 目标形态是"发现时捕获,同一快照内相同内容只存一份"——本函数只做捕获这一步(读 + 归一化 +
|
|
5
5
|
// 哈希),不碰去重存储、不写 result.json、不改 discoverEvals()。它的产物形状是为了让后续
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
//
|
|
3
3
|
// 本文件是 niceeval/results 写入面(createResultsWriter)的薄壳:订阅 reporter 事件按
|
|
4
4
|
// experimentId 路由转手调 writer,自己不持有任何布局知识(实验目录、快照目录、attempt
|
|
5
|
-
// 路径清洗、大字段拆分、瘦身、版本元数据全在库内)。落盘格式见 docs/feature/
|
|
5
|
+
// 路径清洗、大字段拆分、瘦身、版本元数据全在库内)。落盘格式见 docs/feature/record/architecture.md:
|
|
6
6
|
// 每个 experiment 一个实验目录,目录下按时间戳开快照,快照内每 eval-attempt 一个文件夹,
|
|
7
7
|
// 重数据分文件,snapshot.json 只留快照元数据,view 展开某条 trace 时再按需 fetch 它的 trace.json。
|
|
8
8
|
|
package/src/runner/run.ts
CHANGED
|
@@ -159,7 +159,7 @@ export async function runEvals(opts: RunOptions): Promise<InvocationSummary> {
|
|
|
159
159
|
for (const run of opts.agentRuns) {
|
|
160
160
|
// selectedEvalIds 已由 CLI 在构造 AgentRun 时对候选 eval 各求值一次算好(见
|
|
161
161
|
// eval-selection.ts 的 resolveExperimentEvals());这里只按 resolved id 取 eval,
|
|
162
|
-
// 不重新调用用户谓词(见 docs/feature/
|
|
162
|
+
// 不重新调用用户谓词(见 docs/feature/record/architecture.md「selectedEvalIds」)。
|
|
163
163
|
const evals = selectedEvalsForRun(opts.evals, run);
|
|
164
164
|
for (let i = 0; i < run.runs; i++) {
|
|
165
165
|
for (const evalDef of evals) {
|
|
@@ -470,7 +470,7 @@ export async function runEvals(opts: RunOptions): Promise<InvocationSummary> {
|
|
|
470
470
|
const experimentCompletedAt = new Map<string, string>();
|
|
471
471
|
// experiment 作用域 ctx.fact() 的累加器(与 experimentDiagnostics 同一种「按 experimentId 分桶」
|
|
472
472
|
// 模式):同一实验内后写覆盖先写,与 completedAt 同批在快照封口补写进 SnapshotMeta.facts
|
|
473
|
-
// (见 docs/feature/
|
|
473
|
+
// (见 docs/feature/record/architecture.md#facts运行事实)。没有 experimentId 的裸 run 没有
|
|
474
474
|
// Snapshot 可挂,调用直接丢弃(与 recordExperimentDiagnostic 同一条纪律)。
|
|
475
475
|
const experimentFacts = new Map<string, Record<string, FactValue>>();
|
|
476
476
|
const recordExperimentFact = (experimentId: string | undefined, key: string, value: FactValue): void => {
|
package/src/runner/timing.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// attempt 级阶段计时:LifecyclePhase 主链/收尾段的耗时 + 阶段内时间树(hook / turn / command)。
|
|
2
|
-
// 契约见 docs/feature/
|
|
2
|
+
// 契约见 docs/feature/record/architecture.md「result.json」的 PhaseTiming / TimingNode:
|
|
3
3
|
// 数组顺序即执行顺序;没执行的阶段不写 0 值条目;收尾段不计入 durationMs 口径但照常计时;
|
|
4
4
|
// 结果封口发生在 Effect Scope release 完成之后(sandbox.stop 也向这个 recorder 写入)。
|
|
5
5
|
|
package/src/runner/types.ts
CHANGED
|
@@ -23,7 +23,7 @@ import type { AttemptLocator } from "../results/locator.ts";
|
|
|
23
23
|
/**
|
|
24
24
|
* 解析后运行配置的**穷尽可序列化投影**——记录这次运行实际生效的值,不是原始 `ExperimentDef`
|
|
25
25
|
* (函数与 hooks 无法忠实落盘,存「原样」只能存谎)。`model` 与 `agent` 只在快照顶层存在,
|
|
26
|
-
* 这里不复制(见 docs/feature/
|
|
26
|
+
* 这里不复制(见 docs/feature/record/architecture.md「snapshot.json」)。
|
|
27
27
|
*/
|
|
28
28
|
export interface ExperimentRunInfo {
|
|
29
29
|
description?: string;
|
|
@@ -53,7 +53,7 @@ export interface SandboxRunInfo {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
/**
|
|
56
|
-
* 一次 attempt 的生命周期词表——**全仓唯一一套**(见 docs/feature/
|
|
56
|
+
* 一次 attempt 的生命周期词表——**全仓唯一一套**(见 docs/feature/record/architecture.md
|
|
57
57
|
* 「result.json」)。计时(`phases[].name`)、错误归因(`error.phase`)、诊断归属
|
|
58
58
|
* (`diagnostics[].phase`)、live 展示与 agent/ci envelope 的 `phase=` 都使用这同一个闭集,
|
|
59
59
|
* 不存在第二套词表。phase 是 runner 对真实 lifecycle 的单方面投影,不是 adapter / sandbox
|
|
@@ -84,7 +84,7 @@ export type LifecyclePhase =
|
|
|
84
84
|
| "sandbox.suspend" // 留存提交后 provider 把现场转入休眠(docker stop / e2b pause)
|
|
85
85
|
| "sandbox.stop"; // provider 销毁沙箱;与 sandbox.suspend 同一 attempt 互斥
|
|
86
86
|
|
|
87
|
-
/** TimingNode 的种类(见 docs/feature/
|
|
87
|
+
/** TimingNode 的种类(见 docs/feature/record/architecture.md「result.json」)。 */
|
|
88
88
|
export type TimingNodeKind = "hook" | "turn" | "command" | "provider" | "operation";
|
|
89
89
|
|
|
90
90
|
/**
|
|
@@ -120,7 +120,7 @@ export interface TimingNode {
|
|
|
120
120
|
};
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
/** Runner 阶段计时,按执行顺序;只记录实际发生的阶段(见 docs/feature/
|
|
123
|
+
/** Runner 阶段计时,按执行顺序;只记录实际发生的阶段(见 docs/feature/record/architecture.md)。 */
|
|
124
124
|
export interface PhaseTiming {
|
|
125
125
|
name: LifecyclePhase;
|
|
126
126
|
/** 阶段耗时;失败阶段计到抛错或超时中断时。 */
|
|
@@ -132,7 +132,7 @@ export interface PhaseTiming {
|
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
/**
|
|
135
|
-
* `commands.json` 的一条落盘记录(见 docs/feature/
|
|
135
|
+
* `commands.json` 的一条落盘记录(见 docs/feature/record/architecture.md「commandsjson」):
|
|
136
136
|
* 公开 `Sandbox.runCommand()` / `runShell()` 的最外层调用返回非零 `exitCode` 时,Runner 在
|
|
137
137
|
* `CommandResult` 交还调用方**之前**登记的完整证据——Eval 后续即使只把 `.slice(-N)` 拼进
|
|
138
138
|
* 异常消息,这份证据仍然完整。只记非零退出;成功命令的输出不进第二份 artifact,provider 内部
|
|
@@ -156,7 +156,7 @@ export interface FailedCommandEvidence {
|
|
|
156
156
|
export type CommandsArtifact = FailedCommandEvidence[];
|
|
157
157
|
|
|
158
158
|
/**
|
|
159
|
-
* 使 attempt 无法正常完成的唯一致命执行错误(见 docs/feature/
|
|
159
|
+
* 使 attempt 无法正常完成的唯一致命执行错误(见 docs/feature/record/architecture.md 的
|
|
160
160
|
* `AttemptError`)。`message` 是人可读的一层原因(不拼整份 SDK response);完整 stack 单放
|
|
161
161
|
* `stack`,`niceeval show @locator` 首页展开、终端即时反馈不整段打印。榜单只显示 `message`。
|
|
162
162
|
*/
|
|
@@ -174,7 +174,7 @@ export interface AttemptError {
|
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
/**
|
|
177
|
-
* 不一定改变 verdict、但运行后仍需回顾的有界诊断(见 docs/feature/
|
|
177
|
+
* 不一定改变 verdict、但运行后仍需回顾的有界诊断(见 docs/feature/record/architecture.md 的
|
|
178
178
|
* `DiagnosticRecord`)。`level` 表达消息严重度,不是 verdict 的别名 —— passed / failed / errored
|
|
179
179
|
* 任一 verdict 都可以带 cleanup / teardown 诊断。与运行级的 `DiagnosticNotice` 不同,这条挂在单个
|
|
180
180
|
* attempt 结果上、随 `result.json` 落盘。
|
|
@@ -217,7 +217,7 @@ export interface EvalResult {
|
|
|
217
217
|
/**
|
|
218
218
|
* 题型:`defineEval` → `"pass"`,`defineScoreEval` → `"points"`,定义期事实,与
|
|
219
219
|
* `EvalDescriptor.scoring` 同源。省略等价于 `"pass"`——兼容此字段引入前写入的落盘与未声明它的
|
|
220
|
-
* 第三方 harness(见 docs/feature/
|
|
220
|
+
* 第三方 harness(见 docs/feature/record/architecture.md「result.json」)。
|
|
221
221
|
*/
|
|
222
222
|
scoring?: EvalScoring;
|
|
223
223
|
/**
|
|
@@ -234,10 +234,10 @@ export interface EvalResult {
|
|
|
234
234
|
/**
|
|
235
235
|
* sandbox hook / agent setup·send·teardown 经 `ctx.fact()` 上报的运行事实(同 attempt 内
|
|
236
236
|
* 后写覆盖先写)。中性环境观测,不参与 verdict / 评分 / 指纹。见
|
|
237
|
-
* docs/feature/
|
|
237
|
+
* docs/feature/record/architecture.md#facts运行事实。
|
|
238
238
|
*/
|
|
239
239
|
facts?: Record<string, string | number | boolean>;
|
|
240
|
-
/** Runner 阶段计时,按执行顺序;只记录实际发生的阶段(见 docs/feature/
|
|
240
|
+
/** Runner 阶段计时,按执行顺序;只记录实际发生的阶段(见 docs/feature/record/architecture.md)。 */
|
|
241
241
|
phases?: PhaseTiming[];
|
|
242
242
|
skipReason?: string;
|
|
243
243
|
events?: StreamEvent[];
|
|
@@ -268,7 +268,7 @@ export interface EvalResult {
|
|
|
268
268
|
artifactBase?: string;
|
|
269
269
|
/**
|
|
270
270
|
* writer 实际写出的按需 artifact 词干列表(词表与全部横切属性单源在
|
|
271
|
-
* docs/feature/
|
|
271
|
+
* docs/feature/record/architecture.md「证据 registry」,如 ["commands", "events", "sources"])。
|
|
272
272
|
* 省略等价于空列表;携带条目原样携带。读取面的懒加载语义(缺失返回 null)独立成立,
|
|
273
273
|
* 本字段只服务「不 stat 磁盘就知道有什么」的消费方。
|
|
274
274
|
*/
|
|
@@ -278,7 +278,7 @@ export interface EvalResult {
|
|
|
278
278
|
/** `snapshot.json` 的格式标记;把 niceeval 报告和其它工具的同名文件区分开。 */
|
|
279
279
|
export const RESULTS_FORMAT = "niceeval.results";
|
|
280
280
|
/**
|
|
281
|
-
* 结果格式版本,只在破坏兼容读取时递增;读取器只认相同版本。见 docs/feature/
|
|
281
|
+
* 结果格式版本,只在破坏兼容读取时递增;读取器只认相同版本。见 docs/feature/record/architecture.md。
|
|
282
282
|
* `5`(见 memory 的 attempt-locator-and-source-dedup 条目)= result.json 新增 `locator` 字段;
|
|
283
283
|
* `sources.json` 从逐 attempt 内联全量内容改为「attempt 级引用 + 快照级 `sources/<sha256>.json`
|
|
284
284
|
* 去重仓库」,`AttemptHandle.sources()` 的公开返回形状不变(仍是 `SourceArtifact[] | null`)。
|
|
@@ -297,7 +297,7 @@ export const RESULTS_FORMAT = "niceeval.results";
|
|
|
297
297
|
*/
|
|
298
298
|
export const RESULTS_SCHEMA_VERSION = 9;
|
|
299
299
|
|
|
300
|
-
/** 一次 Invocation 的纯运行时内存聚合(reporter 契约用);落盘格式契约在 niceeval/results 的 SnapshotMeta / AttemptRecord,见 docs/feature/
|
|
300
|
+
/** 一次 Invocation 的纯运行时内存聚合(reporter 契约用);落盘格式契约在 niceeval/results 的 SnapshotMeta / AttemptRecord,见 docs/feature/record/architecture.md。不携带顶层 `agent`/`model`——一次 Invocation 可能横跨多个 `(agent, model, flags)` 配置,塞一个顶层单值只能代表其中一份配置;需要时从 `results` 里逐条 `EvalResult.agent`/`.model` 去重派生。 */
|
|
301
301
|
export interface InvocationSummary {
|
|
302
302
|
/** 项目名(来自 config.name),透传给 `niceeval view` 顶部 hero 显示。 */
|
|
303
303
|
name?: LocalizedText;
|
|
@@ -498,7 +498,7 @@ export interface ExperimentHookContext extends ScopedFeedback {
|
|
|
498
498
|
* 第三条反馈通道:上报整场实验的环境观测,与 `completedAt` 同批在快照封口补写进
|
|
499
499
|
* `SnapshotMeta.facts`。key 匹配 `[a-z0-9._-]{1,64}`,value 是标量;同 key 后写覆盖先写,
|
|
500
500
|
* 非法 key 或非标量 value 抛错。不影响判定,不参与 verdict / 评分 / 指纹。形状与归属语义见
|
|
501
|
-
* docs/feature/
|
|
501
|
+
* docs/feature/record/architecture.md#facts运行事实。`niceeval exp --teardown` 的独立收尾
|
|
502
502
|
* 路径不派发 attempt、不落任何 Snapshot,没有 `SnapshotMeta.facts` 可写——该路径下这个方法仍然
|
|
503
503
|
* 校验入参(非法 key / 非标量 value 照样抛错),校验通过后丢弃写入(no-op:诚实优于
|
|
504
504
|
* 静默——非法调用照样报错、不被这条路径悄悄吞掉,但也不假装有地方落盘),见 cli.ts 的
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type CodingAgentBaseline,
|
|
3
|
+
agentBaselineVersionTag,
|
|
4
|
+
} from "../agents/coding-cli-versions.ts";
|
|
5
|
+
|
|
6
|
+
/** 每个 Agent 的 NiceEval 公共 Docker Hub repository(不含 tag)。 */
|
|
7
|
+
export const NICEEVAL_DOCKER_IMAGE_NAME: Record<CodingAgentBaseline, string> = {
|
|
8
|
+
"claude-code": "niceeval/claude-code",
|
|
9
|
+
codex: "niceeval/codex",
|
|
10
|
+
bub: "niceeval/bub",
|
|
11
|
+
opencode: "niceeval/opencode",
|
|
12
|
+
hermes: "niceeval/hermes",
|
|
13
|
+
openclaw: "niceeval/openclaw",
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Fully pinned reference to a NiceEval official coding-agent Docker image.
|
|
18
|
+
*
|
|
19
|
+
* tag 就是[基线版本号](../agents/coding-cli-versions.ts)`<Agent 版本>-r<配方修订>`:
|
|
20
|
+
* 镜像的配方与版本常量在同一个 commit 里,Docker 侧的发布由配方变更触发的 CI 完成,
|
|
21
|
+
* 因此引用直接由版本常量派生,不另存一份易漂移的镜像 tag。
|
|
22
|
+
*/
|
|
23
|
+
export function niceevalDockerImage(agent: CodingAgentBaseline): string {
|
|
24
|
+
return `${NICEEVAL_DOCKER_IMAGE_NAME[agent]}:${agentBaselineVersionTag(agent)}`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* NiceEval 官方公共 Docker 镜像:每个值是完整、版本钉死的引用,直接交给 `dockerSandbox({ image })`,
|
|
29
|
+
* 或写进项目 Dockerfile 的 `FROM` 继续叠加依赖。
|
|
30
|
+
*/
|
|
31
|
+
export const NICEEVAL_CLAUDE_CODE_DOCKER_IMAGE = niceevalDockerImage("claude-code");
|
|
32
|
+
export const NICEEVAL_CODEX_DOCKER_IMAGE = niceevalDockerImage("codex");
|
|
33
|
+
export const NICEEVAL_BUB_DOCKER_IMAGE = niceevalDockerImage("bub");
|
|
34
|
+
export const NICEEVAL_OPENCODE_DOCKER_IMAGE = niceevalDockerImage("opencode");
|
|
35
|
+
export const NICEEVAL_HERMES_DOCKER_IMAGE = niceevalDockerImage("hermes");
|
|
36
|
+
export const NICEEVAL_OPENCLAW_DOCKER_IMAGE = niceevalDockerImage("openclaw");
|
|
@@ -4,14 +4,12 @@ import { describe, expect, it } from "vitest";
|
|
|
4
4
|
import {
|
|
5
5
|
BUB_INSTALL_MARKER,
|
|
6
6
|
DEFAULT_BUB_OTEL_PLUGIN,
|
|
7
|
-
|
|
7
|
+
DEFAULT_BUB_REQUIREMENT,
|
|
8
8
|
} from "../agents/bub-install-spec.ts";
|
|
9
9
|
import {
|
|
10
10
|
E2B_NODE_TOOL_PREFIX,
|
|
11
11
|
E2B_OFFICIAL_AGENT_TEMPLATES,
|
|
12
|
-
|
|
13
|
-
NICEEVAL_CLAUDE_CODE_E2B_TEMPLATE,
|
|
14
|
-
NICEEVAL_CODEX_E2B_TEMPLATE,
|
|
12
|
+
NICEEVAL_E2B_TEMPLATE_NAME,
|
|
15
13
|
e2bCodingAgentTemplate,
|
|
16
14
|
verifyE2BNodeToolContract,
|
|
17
15
|
} from "./e2b-agent-template.ts";
|
|
@@ -26,22 +24,7 @@ async function runSteps(template: Parameters<typeof Template.toJSON>[0]) {
|
|
|
26
24
|
.map((step) => ({ command: step.args[0]!, user: step.args[1] }));
|
|
27
25
|
}
|
|
28
26
|
|
|
29
|
-
const publicTemplates = {
|
|
30
|
-
"claude-code": NICEEVAL_CLAUDE_CODE_E2B_TEMPLATE,
|
|
31
|
-
codex: NICEEVAL_CODEX_E2B_TEMPLATE,
|
|
32
|
-
bub: NICEEVAL_BUB_E2B_TEMPLATE,
|
|
33
|
-
} as const;
|
|
34
|
-
|
|
35
27
|
describe("e2bCodingAgentTemplate", () => {
|
|
36
|
-
it("exports complete public refs pinned to one verified release", () => {
|
|
37
|
-
const releases = Object.values(publicTemplates).map((template) => template.split(":").at(-1));
|
|
38
|
-
|
|
39
|
-
expect(new Set(releases).size).toBe(1);
|
|
40
|
-
for (const template of Object.values(publicTemplates)) {
|
|
41
|
-
expect(template).toMatch(/^correctroads-default-team\/.+:v\d+\.\d+\.\d+$/);
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
|
|
45
28
|
it.each([
|
|
46
29
|
["claude-code", "claude"],
|
|
47
30
|
["codex", "codex"],
|
|
@@ -49,7 +32,7 @@ describe("e2bCodingAgentTemplate", () => {
|
|
|
49
32
|
const json = JSON.parse(await Template.toJSON(e2bCodingAgentTemplate(agent)));
|
|
50
33
|
expect(json.fromTemplate).toBe(base);
|
|
51
34
|
expect(E2B_OFFICIAL_AGENT_TEMPLATES[agent]).toBe(base);
|
|
52
|
-
expect(
|
|
35
|
+
expect(NICEEVAL_E2B_TEMPLATE_NAME[agent]).toContain(`/niceeval-${agent}`);
|
|
53
36
|
expect(JSON.stringify(json)).toContain(
|
|
54
37
|
agent === "claude-code" ? "claude.ai/install.sh" : "npm install -g",
|
|
55
38
|
);
|
|
@@ -97,7 +80,9 @@ describe("e2bCodingAgentTemplate", () => {
|
|
|
97
80
|
bubPythonPackages: ["bub-plugin-memory==1.3.0"],
|
|
98
81
|
}));
|
|
99
82
|
// pin 的单源在 bub-install-spec.ts;这里只证明 spec → 模板 recipe 的传播,不复刻 pin 值。
|
|
100
|
-
|
|
83
|
+
// requirement 必须经 override 文件落进 recipe:少了它,构建会拉 Bub 主干而不是钉死的版本。
|
|
84
|
+
expect(json).toContain(DEFAULT_BUB_REQUIREMENT);
|
|
85
|
+
expect(json).toContain("--overrides");
|
|
101
86
|
expect(json).toContain(DEFAULT_BUB_OTEL_PLUGIN.split("@").at(-1)!.split("#")[0]!);
|
|
102
87
|
expect(json).toContain(BUB_INSTALL_MARKER.split("/").at(-1)!);
|
|
103
88
|
expect(json).toContain("bub-plugin-memory==1.3.0");
|
|
@@ -2,15 +2,22 @@ import { Template, type TemplateBuilder } from "e2b";
|
|
|
2
2
|
import {
|
|
3
3
|
BUB_INSTALL_MARKER,
|
|
4
4
|
DEFAULT_BUB_OTEL_PLUGIN,
|
|
5
|
-
|
|
5
|
+
DEFAULT_BUB_REQUIREMENT,
|
|
6
6
|
bubInstallHash,
|
|
7
7
|
normalizeBubPackages,
|
|
8
8
|
} from "../agents/bub-install-spec.ts";
|
|
9
9
|
import {
|
|
10
10
|
DEFAULT_CLAUDE_CODE_CLI_VERSION,
|
|
11
11
|
DEFAULT_CODEX_CLI_VERSION,
|
|
12
|
+
agentBaselineVersionTag,
|
|
12
13
|
} from "../agents/coding-cli-versions.ts";
|
|
13
14
|
|
|
15
|
+
/**
|
|
16
|
+
* 已发布(或即将发布进台账)的 E2B 公共基线 Agent。
|
|
17
|
+
*
|
|
18
|
+
* 是 `CodingAgentBaseline` 的子集:OpenCode / Hermes / OpenClaw 只有 Docker 公共镜像,
|
|
19
|
+
* E2B 模板尚未进 `sandbox/e2b/published.json`,不能提前导出指向空制品的常量。
|
|
20
|
+
*/
|
|
14
21
|
export type E2BCodingAgent = "claude-code" | "codex" | "bub";
|
|
15
22
|
|
|
16
23
|
export interface E2BCodingAgentTemplateOptions {
|
|
@@ -24,25 +31,49 @@ export const E2B_OFFICIAL_AGENT_TEMPLATES = {
|
|
|
24
31
|
codex: "codex",
|
|
25
32
|
} as const;
|
|
26
33
|
|
|
34
|
+
/** NiceEval 公共 E2B baseline 所在的 Team namespace;跨 Team 引用必须带它。 */
|
|
35
|
+
export const NICEEVAL_E2B_TEAM = "correctroads-default-team";
|
|
36
|
+
|
|
37
|
+
/** 每个 Agent 的公共 E2B template 名字(不含 tag)。 */
|
|
38
|
+
export const NICEEVAL_E2B_TEMPLATE_NAME: Record<E2BCodingAgent, string> = {
|
|
39
|
+
"claude-code": `${NICEEVAL_E2B_TEAM}/niceeval-claude-code`,
|
|
40
|
+
codex: `${NICEEVAL_E2B_TEAM}/niceeval-codex`,
|
|
41
|
+
bub: `${NICEEVAL_E2B_TEAM}/niceeval-bub`,
|
|
42
|
+
};
|
|
43
|
+
|
|
27
44
|
/**
|
|
28
|
-
*
|
|
45
|
+
* 各 Agent 当前已发布并完成启动校验的公共 template tag。
|
|
29
46
|
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
47
|
+
* E2B 的发布是维护者手动动作(需要 `e2b auth login`),所以这里记的是**已发布事实**,
|
|
48
|
+
* 不是源码配方派生值:构建脚本按 `agentBaselineVersionTag()` 打 tag,发布并真机验证后,
|
|
49
|
+
* 同一批改动更新这张表与 `sandbox/e2b/published.json`。两者与源码版本常量的一致性由
|
|
50
|
+
* `e2b-agent-template.test.ts` 守护——版本常量走在发布前面时测试红,常量不会先指向不存在的制品。
|
|
33
51
|
*/
|
|
34
|
-
const
|
|
52
|
+
const PUBLISHED_E2B_BASELINE_TAG: Record<E2BCodingAgent, string> = {
|
|
53
|
+
"claude-code": "v0.6.1",
|
|
54
|
+
codex: "v0.6.1",
|
|
55
|
+
bub: "v0.6.1",
|
|
56
|
+
};
|
|
35
57
|
|
|
36
58
|
/**
|
|
37
|
-
* NiceEval 官方公共 E2B baseline
|
|
59
|
+
* NiceEval 官方公共 E2B baseline:每个值已经是完整、版本钉死、跨 Team 的 template ref。
|
|
38
60
|
* 直接交给 `e2bSandbox({ template })`,或交给 E2B `Template().fromTemplate(...)` 继续派生。
|
|
39
61
|
*/
|
|
40
62
|
export const NICEEVAL_CLAUDE_CODE_E2B_TEMPLATE =
|
|
41
|
-
|
|
63
|
+
`${NICEEVAL_E2B_TEMPLATE_NAME["claude-code"]}:${PUBLISHED_E2B_BASELINE_TAG["claude-code"]}`;
|
|
42
64
|
export const NICEEVAL_CODEX_E2B_TEMPLATE =
|
|
43
|
-
|
|
65
|
+
`${NICEEVAL_E2B_TEMPLATE_NAME.codex}:${PUBLISHED_E2B_BASELINE_TAG.codex}`;
|
|
44
66
|
export const NICEEVAL_BUB_E2B_TEMPLATE =
|
|
45
|
-
|
|
67
|
+
`${NICEEVAL_E2B_TEMPLATE_NAME.bub}:${PUBLISHED_E2B_BASELINE_TAG.bub}`;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Build tag for the next official E2B baseline of this agent: `<agent version>-r<recipe revision>`.
|
|
71
|
+
*
|
|
72
|
+
* 构建脚本用它打 tag,而不是用 niceeval 自己的 release——制品的版本位是它装的那个 Agent 的版本。
|
|
73
|
+
*/
|
|
74
|
+
export function e2bBaselineBuildTag(agent: E2BCodingAgent): string {
|
|
75
|
+
return agentBaselineVersionTag(agent);
|
|
76
|
+
}
|
|
46
77
|
|
|
47
78
|
function shellQuote(value: string): string {
|
|
48
79
|
return `'${value.replaceAll("'", `'"'"'`)}'`;
|
|
@@ -136,7 +167,9 @@ export function e2bCodingAgentTemplate(
|
|
|
136
167
|
.runCmd("curl -LsSf https://astral.sh/uv/install.sh | sh", { user: E2B_RUN_USER })
|
|
137
168
|
.runCmd(
|
|
138
169
|
[
|
|
139
|
-
`
|
|
170
|
+
// override 把 `bub` 钉成一个 PyPI 版本:OTel 插件所在 workspace 把 bub 声明成 git
|
|
171
|
+
// 依赖,不覆盖的话构建会拉到 Bub 主干,模板里装的版本随构建时间漂移。
|
|
172
|
+
`printf '%s\\n' ${shellQuote(DEFAULT_BUB_REQUIREMENT)} > ${overrideFile}`,
|
|
140
173
|
`$HOME/.local/bin/uv tool install --python 3.12 --prerelease allow bub --overrides ${overrideFile} --with ${shellQuote(DEFAULT_BUB_OTEL_PLUGIN)}${withPackages}`,
|
|
141
174
|
`mkdir -p $(dirname ${marker}) && printf '%s' ${shellQuote(installHash)} > ${marker}`,
|
|
142
175
|
],
|
package/src/sandbox/index.ts
CHANGED
|
@@ -4,6 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
export { dockerSandbox, vercelSandbox, e2bSandbox, localSandbox, defineSandbox } from "../define.ts";
|
|
6
6
|
export { createCheckpoint, restoreCheckpoint } from "./checkpoint.ts";
|
|
7
|
+
export {
|
|
8
|
+
NICEEVAL_BUB_DOCKER_IMAGE,
|
|
9
|
+
NICEEVAL_CLAUDE_CODE_DOCKER_IMAGE,
|
|
10
|
+
NICEEVAL_CODEX_DOCKER_IMAGE,
|
|
11
|
+
NICEEVAL_HERMES_DOCKER_IMAGE,
|
|
12
|
+
NICEEVAL_OPENCLAW_DOCKER_IMAGE,
|
|
13
|
+
NICEEVAL_OPENCODE_DOCKER_IMAGE,
|
|
14
|
+
} from "./docker-agent-image.ts";
|
|
7
15
|
|
|
8
16
|
export type {
|
|
9
17
|
Sandbox,
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
// cases: docs/engineering/testing/unit/sandbox.md
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import { describe, expect, it } from "vitest";
|
|
4
|
+
import {
|
|
5
|
+
AGENT_BASELINE_RECIPE_REVISION,
|
|
6
|
+
AGENT_BASELINE_VERSION,
|
|
7
|
+
DEFAULT_BUB_VERSION,
|
|
8
|
+
DEFAULT_CLAUDE_CODE_CLI_VERSION,
|
|
9
|
+
DEFAULT_CODEX_CLI_VERSION,
|
|
10
|
+
DEFAULT_HERMES_CLI_VERSION,
|
|
11
|
+
DEFAULT_OPENCLAW_CLI_VERSION,
|
|
12
|
+
DEFAULT_OPENCODE_CLI_VERSION,
|
|
13
|
+
type CodingAgentBaseline,
|
|
14
|
+
agentBaselineVersionTag,
|
|
15
|
+
} from "../agents/coding-cli-versions.ts";
|
|
16
|
+
import {
|
|
17
|
+
BUB_INSTALL_MARKER,
|
|
18
|
+
DEFAULT_BUB_OTEL_PLUGIN,
|
|
19
|
+
DEFAULT_BUB_REQUIREMENT,
|
|
20
|
+
bubInstallHash,
|
|
21
|
+
bubRequirement,
|
|
22
|
+
} from "../agents/bub-install-spec.ts";
|
|
23
|
+
import {
|
|
24
|
+
type E2BCodingAgent,
|
|
25
|
+
NICEEVAL_BUB_E2B_TEMPLATE,
|
|
26
|
+
NICEEVAL_CLAUDE_CODE_E2B_TEMPLATE,
|
|
27
|
+
NICEEVAL_CODEX_E2B_TEMPLATE,
|
|
28
|
+
NICEEVAL_E2B_TEMPLATE_NAME,
|
|
29
|
+
e2bBaselineBuildTag,
|
|
30
|
+
} from "./e2b-agent-template.ts";
|
|
31
|
+
import {
|
|
32
|
+
NICEEVAL_BUB_DOCKER_IMAGE,
|
|
33
|
+
NICEEVAL_CLAUDE_CODE_DOCKER_IMAGE,
|
|
34
|
+
NICEEVAL_CODEX_DOCKER_IMAGE,
|
|
35
|
+
NICEEVAL_DOCKER_IMAGE_NAME,
|
|
36
|
+
NICEEVAL_HERMES_DOCKER_IMAGE,
|
|
37
|
+
NICEEVAL_OPENCLAW_DOCKER_IMAGE,
|
|
38
|
+
NICEEVAL_OPENCODE_DOCKER_IMAGE,
|
|
39
|
+
} from "./docker-agent-image.ts";
|
|
40
|
+
|
|
41
|
+
const DOCKER_AGENTS = [
|
|
42
|
+
"claude-code",
|
|
43
|
+
"codex",
|
|
44
|
+
"bub",
|
|
45
|
+
"opencode",
|
|
46
|
+
"hermes",
|
|
47
|
+
"openclaw",
|
|
48
|
+
] as const satisfies readonly CodingAgentBaseline[];
|
|
49
|
+
|
|
50
|
+
const E2B_AGENTS = ["claude-code", "codex", "bub"] as const satisfies readonly E2BCodingAgent[];
|
|
51
|
+
|
|
52
|
+
const e2bTemplates: Record<E2BCodingAgent, string> = {
|
|
53
|
+
"claude-code": NICEEVAL_CLAUDE_CODE_E2B_TEMPLATE,
|
|
54
|
+
codex: NICEEVAL_CODEX_E2B_TEMPLATE,
|
|
55
|
+
bub: NICEEVAL_BUB_E2B_TEMPLATE,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const dockerImages: Record<CodingAgentBaseline, string> = {
|
|
59
|
+
"claude-code": NICEEVAL_CLAUDE_CODE_DOCKER_IMAGE,
|
|
60
|
+
codex: NICEEVAL_CODEX_DOCKER_IMAGE,
|
|
61
|
+
bub: NICEEVAL_BUB_DOCKER_IMAGE,
|
|
62
|
+
opencode: NICEEVAL_OPENCODE_DOCKER_IMAGE,
|
|
63
|
+
hermes: NICEEVAL_HERMES_DOCKER_IMAGE,
|
|
64
|
+
openclaw: NICEEVAL_OPENCLAW_DOCKER_IMAGE,
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
interface PublishedTemplate {
|
|
68
|
+
name: string;
|
|
69
|
+
versionTag: string;
|
|
70
|
+
agentVersion: string;
|
|
71
|
+
installMarker?: string;
|
|
72
|
+
templateId: string;
|
|
73
|
+
buildId: string;
|
|
74
|
+
/** 源码配方已前进到这个 tag、制品尚未发布时填;值必须就是配方派生出的 tag。 */
|
|
75
|
+
supersededBy?: string;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const ledger = JSON.parse(
|
|
79
|
+
await readFile(new URL("../../sandbox/e2b/published.json", import.meta.url), "utf8"),
|
|
80
|
+
) as { templates: Record<E2BCodingAgent, PublishedTemplate> };
|
|
81
|
+
|
|
82
|
+
const dockerfile = await readFile(
|
|
83
|
+
new URL("../../sandbox/docker/Dockerfile", import.meta.url),
|
|
84
|
+
"utf8",
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
describe("official coding-agent baselines", () => {
|
|
88
|
+
it.each(DOCKER_AGENTS)("versions the %s Docker baseline by the agent it ships, not by niceeval", (agent) => {
|
|
89
|
+
// 版本位是被装的那个 Agent 的版本;-r 位是 NiceEval 配方自己的修订号。
|
|
90
|
+
expect(agentBaselineVersionTag(agent)).toBe(
|
|
91
|
+
`${AGENT_BASELINE_VERSION[agent]}-r${AGENT_BASELINE_RECIPE_REVISION[agent]}`,
|
|
92
|
+
);
|
|
93
|
+
expect(dockerImages[agent]).toBe(
|
|
94
|
+
`${NICEEVAL_DOCKER_IMAGE_NAME[agent]}:${agentBaselineVersionTag(agent)}`,
|
|
95
|
+
);
|
|
96
|
+
// 接受 semver(`0.144.1-r2`)与 OpenClaw calver(`2026.7.1-2-r1`)。
|
|
97
|
+
expect(agentBaselineVersionTag(agent)).toMatch(/^\d+(\.\d+)+(?:-\d+)?-r\d+$/);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it.each(E2B_AGENTS)("keeps the %s E2B build tag on the same recipe version as Docker", (agent) => {
|
|
101
|
+
// 同一个 Agent 的两份制品共用一个版本号:一个版本号 = 一套基线配方。
|
|
102
|
+
expect(e2bBaselineBuildTag(agent)).toBe(agentBaselineVersionTag(agent));
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it("takes the version position from the same constants the runtime fallback installs", () => {
|
|
106
|
+
expect(AGENT_BASELINE_VERSION).toEqual({
|
|
107
|
+
"claude-code": DEFAULT_CLAUDE_CODE_CLI_VERSION,
|
|
108
|
+
codex: DEFAULT_CODEX_CLI_VERSION,
|
|
109
|
+
bub: DEFAULT_BUB_VERSION,
|
|
110
|
+
opencode: DEFAULT_OPENCODE_CLI_VERSION,
|
|
111
|
+
hermes: DEFAULT_HERMES_CLI_VERSION,
|
|
112
|
+
openclaw: DEFAULT_OPENCLAW_CLI_VERSION,
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it.each(E2B_AGENTS)("points the exported %s E2B ref at a published template", (agent) => {
|
|
117
|
+
const published = ledger.templates[agent];
|
|
118
|
+
|
|
119
|
+
// 具名常量只能指向台账里真实存在的制品:发布是维护者手动动作,常量不能先跑到发布前面。
|
|
120
|
+
expect(published.name).toBe(NICEEVAL_E2B_TEMPLATE_NAME[agent]);
|
|
121
|
+
expect(e2bTemplates[agent]).toBe(`${published.name}:${published.versionTag}`);
|
|
122
|
+
// 台账记的是那份制品里 Agent 的版本。源码把版本位往前推却没发布,只有一条合法出路:
|
|
123
|
+
// 在台账里写下待发布的 tag(supersededBy)。默不作声地分叉不行——那正是「常量指着装了
|
|
124
|
+
// 旧 Agent 的制品」而全绿的形态。
|
|
125
|
+
if (published.supersededBy === undefined) {
|
|
126
|
+
expect(published.agentVersion).toBe(AGENT_BASELINE_VERSION[agent]);
|
|
127
|
+
} else {
|
|
128
|
+
expect(published.supersededBy).toBe(agentBaselineVersionTag(agent));
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("keeps the published Bub template's install fingerprint in sync with the recipe", () => {
|
|
133
|
+
const published = ledger.templates.bub;
|
|
134
|
+
|
|
135
|
+
// 换 pin 必然换指纹:预装环境的 marker 对不上时 Adapter 回退完整安装,所以旧制品仍可用,
|
|
136
|
+
// 但台账必须承认它已被取代。
|
|
137
|
+
if (published.supersededBy === undefined) {
|
|
138
|
+
expect(published.installMarker).toBe(bubInstallHash([]));
|
|
139
|
+
} else {
|
|
140
|
+
expect(published.installMarker).not.toBe(bubInstallHash([]));
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it("keeps the Dockerfile's pinned versions in sync with the source constants", () => {
|
|
145
|
+
// Dockerfile 不能 import TypeScript,漂移只在真实构建时暴露,所以逐个比对。
|
|
146
|
+
expect(dockerfile).toContain(`ARG CODEX_VERSION=${DEFAULT_CODEX_CLI_VERSION}`);
|
|
147
|
+
expect(dockerfile).toContain(`ARG CLAUDE_CODE_VERSION=${DEFAULT_CLAUDE_CODE_CLI_VERSION}`);
|
|
148
|
+
expect(dockerfile).toContain(`ARG OPENCODE_VERSION=${DEFAULT_OPENCODE_CLI_VERSION}`);
|
|
149
|
+
expect(dockerfile).toContain(`ARG OPENCLAW_VERSION=${DEFAULT_OPENCLAW_CLI_VERSION}`);
|
|
150
|
+
expect(dockerfile).toContain(`ARG HERMES_VERSION=${DEFAULT_HERMES_CLI_VERSION}`);
|
|
151
|
+
expect(dockerfile).toContain(DEFAULT_BUB_OTEL_PLUGIN.replace(/^git\+/, ""));
|
|
152
|
+
expect(dockerfile).toContain(`'${bubInstallHash([])}' > $HOME/${BUB_INSTALL_MARKER}`);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it("puts both Bub pins into the install fingerprint", () => {
|
|
156
|
+
const baseline = bubInstallHash([]);
|
|
157
|
+
|
|
158
|
+
// 预装环境靠 marker 命中。版本或插件换了指纹却不变,就会在配方已变时继续命中旧环境,
|
|
159
|
+
// 装到上一代 Bub 而全程无声。
|
|
160
|
+
expect(bubInstallHash([], bubRequirement("0.3.9"))).not.toBe(baseline);
|
|
161
|
+
expect(bubInstallHash([], DEFAULT_BUB_REQUIREMENT, `${DEFAULT_BUB_OTEL_PLUGIN}x`)).not.toBe(baseline);
|
|
162
|
+
expect(bubRequirement("0.3.9")).toBe("bub==0.3.9");
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it("keeps the Docker bub override file in sync with the pinned recipe", async () => {
|
|
166
|
+
const override = await readFile(
|
|
167
|
+
new URL("../../sandbox/docker/bub-override.txt", import.meta.url),
|
|
168
|
+
"utf8",
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
// 这份 override 不是遗留物:插件所在 workspace 把 bub 声明成 git 依赖,少了它构建会拉
|
|
172
|
+
// Bub 主干,镜像里的版本随构建时间漂移。
|
|
173
|
+
expect(override.trim()).toBe(DEFAULT_BUB_REQUIREMENT);
|
|
174
|
+
});
|
|
175
|
+
});
|
package/src/sandbox/resolve.ts
CHANGED
|
@@ -76,7 +76,7 @@ export function sandboxRecommendedConcurrency(opt: SandboxOption | undefined): n
|
|
|
76
76
|
/**
|
|
77
77
|
* ExperimentRunInfo.sandbox 的投影:provider 名 + 公开参数(镜像/快照/模板/runtime)+ 配置指纹。
|
|
78
78
|
* 参数只经这个投影落盘——token、凭据路径永不进来;defineSandbox 自定义 provider 未实现
|
|
79
|
-
* `publicConfig()` 时只落 provider 名(见 docs/feature/
|
|
79
|
+
* `publicConfig()` 时只落 provider 名(见 docs/feature/record/architecture.md)。
|
|
80
80
|
*/
|
|
81
81
|
export function sandboxRunInfo(
|
|
82
82
|
opt: SandboxOption | undefined,
|
package/src/sandbox/types.ts
CHANGED
|
@@ -79,7 +79,7 @@ export interface SandboxHookContext extends ScopedFeedback {
|
|
|
79
79
|
* 第三条反馈通道:上报本次运行的中性环境观测,落进 `AttemptRecord.facts`。key 匹配
|
|
80
80
|
* `[a-z0-9._-]{1,64}`,value 是标量;同 key 后写覆盖先写,非法 key 或非标量 value 抛错。
|
|
81
81
|
* 不影响判定,不参与 verdict / 评分 / 指纹。形状与归属语义见
|
|
82
|
-
* docs/feature/
|
|
82
|
+
* docs/feature/record/architecture.md#facts运行事实。
|
|
83
83
|
*/
|
|
84
84
|
fact(key: string, value: string | number | boolean): void;
|
|
85
85
|
}
|
package/src/scoring/diff.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// agent 归因 diff 的派生视图:从落盘事实(DiffWindow[])派生文件级摘要与终态读取。
|
|
2
|
-
// 派生物可随时重算、不落盘——符合「聚合在消费方」铁律(docs/feature/
|
|
2
|
+
// 派生物可随时重算、不落盘——符合「聚合在消费方」铁律(docs/feature/record/architecture.md「diff.json」)。
|
|
3
3
|
|
|
4
4
|
import type { DiffArtifact, DiffData, DiffFileSummary, DiffWindow, WindowChange } from "./types.ts";
|
|
5
5
|
|