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.
- package/dist/agents/types.d.ts +67 -5
- package/dist/context/types.d.ts +32 -12
- package/dist/i18n/en.d.ts +54 -0
- package/dist/i18n/zh-CN.d.ts +55 -1
- package/dist/o11y/types.d.ts +16 -2
- package/dist/report/aggregate.d.ts +5 -3
- package/dist/report/aggregate.js +32 -5
- package/dist/report/built-ins/experiment-comparison.d.ts +39 -1
- package/dist/report/built-ins/experiment-comparison.js +116 -10
- package/dist/report/built-ins/index.d.ts +1 -0
- package/dist/report/built-ins/index.js +1 -1
- package/dist/report/components.d.ts +8 -2
- package/dist/report/components.js +3 -3
- package/dist/report/compute.d.ts +11 -18
- package/dist/report/compute.js +54 -34
- package/dist/report/flag.d.ts +16 -1
- package/dist/report/flag.js +19 -1
- package/dist/report/format.d.ts +16 -8
- package/dist/report/format.js +27 -12
- package/dist/report/index.d.ts +4 -3
- package/dist/report/index.js +5 -4
- package/dist/report/locale.d.ts +11 -2
- package/dist/report/locale.js +23 -5
- package/dist/report/metrics.d.ts +13 -1
- package/dist/report/metrics.js +65 -14
- package/dist/report/primitives.d.ts +6 -0
- package/dist/report/react/AttemptList.d.ts +2 -2
- package/dist/report/react/AttemptList.js +5 -6
- package/dist/report/react/EvalList.d.ts +1 -1
- package/dist/report/react/EvalList.js +0 -0
- package/dist/report/react/ExperimentComparison.d.ts +8 -0
- package/dist/report/react/ExperimentComparison.js +11 -0
- package/dist/report/react/ExperimentList.d.ts +2 -1
- package/dist/report/react/ExperimentList.js +8 -10
- package/dist/report/react/MetricScatter.js +5 -11
- package/dist/report/react/chart-math.d.ts +23 -6
- package/dist/report/react/chart-math.js +71 -19
- package/dist/report/react/fixtures.d.ts +3 -3
- package/dist/report/react/fixtures.js +21 -14
- package/dist/report/report.d.ts +5 -1
- package/dist/report/report.js +6 -2
- package/dist/report/text/faces.d.ts +1 -1
- package/dist/report/text/faces.js +42 -41
- package/dist/report/text/table.js +36 -5
- package/dist/report/types.d.ts +39 -21
- package/dist/results/types.d.ts +11 -0
- package/dist/runner/feedback/sink.d.ts +110 -0
- package/dist/runner/types.d.ts +513 -22
- package/dist/sandbox/docker.d.ts +23 -2
- package/dist/sandbox/e2b.d.ts +15 -1
- package/dist/sandbox/errors.d.ts +30 -3
- package/dist/sandbox/io-retry.d.ts +17 -0
- package/dist/sandbox/registry.d.ts +2 -0
- package/dist/sandbox/resolve.d.ts +18 -5
- package/dist/sandbox/retry.d.ts +11 -1
- package/dist/sandbox/types.d.ts +39 -5
- package/dist/sandbox/vercel.d.ts +7 -1
- package/dist/scoring/coverage.d.ts +30 -0
- package/dist/scoring/display.d.ts +21 -0
- package/dist/scoring/display.js +120 -0
- package/dist/scoring/types.d.ts +103 -20
- package/dist/shared/aggregate.d.ts +1 -0
- package/dist/shared/aggregate.js +3 -3
- package/dist/shared/types.d.ts +28 -0
- package/dist/tty-line.d.ts +0 -4
- package/dist/util.d.ts +23 -0
- package/docs-site/zh/concepts/adapter.mdx +22 -4
- package/docs-site/zh/concepts/experiment.mdx +1 -1
- package/docs-site/zh/concepts/overview.mdx +6 -6
- package/docs-site/zh/guides/agent-feedback-loop.mdx +28 -26
- package/docs-site/zh/guides/authoring.mdx +33 -0
- package/docs-site/zh/guides/ci-integration.mdx +23 -12
- package/docs-site/zh/guides/connect-your-agent.mdx +29 -3
- package/docs-site/zh/guides/custom-reports.mdx +29 -34
- package/docs-site/zh/guides/dataset-fanout.mdx +25 -3
- package/docs-site/zh/guides/debug-sandbox.mdx +57 -0
- package/docs-site/zh/guides/debugging.mdx +210 -0
- package/docs-site/zh/guides/experiments.mdx +10 -3
- package/docs-site/zh/guides/official-adapters.mdx +26 -2
- package/docs-site/zh/guides/publish-report.mdx +30 -16
- package/docs-site/zh/guides/report-components.mdx +42 -30
- package/docs-site/zh/guides/reporters.mdx +2 -2
- package/docs-site/zh/guides/results-data.mdx +17 -9
- package/docs-site/zh/guides/runner.mdx +17 -7
- package/docs-site/zh/guides/sandbox-agent.mdx +56 -7
- package/docs-site/zh/guides/sandbox-providers.mdx +257 -9
- package/docs-site/zh/guides/scoring-guide.mdx +4 -4
- package/docs-site/zh/guides/viewing-results.mdx +79 -36
- package/docs-site/zh/guides/write-experiment.mdx +5 -3
- package/docs-site/zh/guides/write-send.mdx +17 -1
- package/docs-site/zh/index.mdx +1 -1
- package/docs-site/zh/reference/builtin-agents.mdx +27 -0
- package/docs-site/zh/reference/capabilities.mdx +2 -2
- package/docs-site/zh/reference/cli.mdx +33 -7
- package/docs-site/zh/reference/define-agent.mdx +57 -4
- package/docs-site/zh/reference/define-config.mdx +1 -1
- package/docs-site/zh/reference/define-eval.mdx +42 -9
- package/docs-site/zh/reference/expect.mdx +26 -1
- package/package.json +5 -1
- package/src/agents/ai-sdk-otel.test.ts +1 -0
- package/src/agents/ai-sdk.test.ts +3 -0
- package/src/agents/ai-sdk.ts +3 -0
- package/src/agents/bub-install-spec.test.ts +34 -0
- package/src/agents/bub-install-spec.ts +32 -0
- package/src/agents/bub.ts +31 -32
- package/src/agents/claude-code.test.ts +130 -9
- package/src/agents/claude-code.ts +76 -4
- package/src/agents/codex.test.ts +189 -40
- package/src/agents/codex.ts +155 -14
- package/src/agents/coding-cli-versions.test.ts +15 -0
- package/src/agents/coding-cli-versions.ts +3 -0
- package/src/agents/index.ts +11 -0
- package/src/agents/langgraph.test.ts +204 -0
- package/src/agents/langgraph.ts +495 -0
- package/src/agents/marketplace.ts +85 -0
- package/src/agents/native-config.test.ts +179 -0
- package/src/agents/native-config.ts +267 -0
- package/src/agents/openai-compat.test.ts +1 -0
- package/src/agents/openclaw.test.ts +31 -0
- package/src/agents/openclaw.ts +171 -0
- package/src/agents/plugin-config.test.ts +1 -0
- package/src/agents/sdk-streams.test.ts +79 -0
- package/src/agents/sdk-streams.ts +55 -10
- package/src/agents/skills.test.ts +1 -0
- package/src/agents/streaming.test.ts +3 -9
- package/src/agents/types.ts +68 -5
- package/src/agents/ui-message-stream.test.ts +3 -0
- package/src/cli.ts +411 -108
- package/src/context/context.test.ts +51 -12
- package/src/context/context.ts +161 -29
- package/src/context/session.test.ts +1 -0
- package/src/context/session.ts +114 -6
- package/src/context/types.ts +30 -12
- package/src/define.test.ts +13 -8
- package/src/define.ts +25 -4
- package/src/expect/index.ts +53 -23
- package/src/i18n/en.ts +64 -2
- package/src/i18n/zh-CN.ts +65 -3
- package/src/o11y/cost.test.ts +1 -0
- package/src/o11y/execution-tree.test.ts +1 -20
- package/src/o11y/otlp/mappers/claude-code.test.ts +1 -0
- package/src/o11y/otlp/parse.test.ts +1 -0
- package/src/o11y/otlp/turn-otel.test.ts +1 -0
- package/src/o11y/parsers/bub.test.ts +1 -0
- package/src/o11y/parsers/claude-code.test.ts +1 -34
- package/src/o11y/parsers/openclaw.test.ts +154 -0
- package/src/o11y/parsers/openclaw.ts +310 -0
- package/src/o11y/prices.json +746 -311
- package/src/o11y/tool-names.test.ts +1 -0
- package/src/o11y/types.ts +16 -2
- package/src/report/aggregate.ts +34 -5
- package/src/report/built-in-user-parity.test.tsx +110 -153
- package/src/report/built-ins/experiment-comparison.tsx +173 -13
- package/src/report/built-ins/index.ts +6 -1
- package/src/report/components.tsx +9 -3
- package/src/report/compute.ts +70 -40
- package/src/report/dual-render.test.tsx +194 -67
- package/src/report/flag.ts +30 -2
- package/src/report/format.ts +35 -11
- package/src/report/index.ts +22 -4
- package/src/report/locale.ts +25 -5
- package/src/report/metrics.ts +67 -14
- package/src/report/primitives.tsx +6 -0
- package/src/report/react/AttemptList.tsx +6 -31
- package/src/report/react/EvalList.tsx +0 -0
- package/src/report/react/ExperimentComparison.tsx +68 -0
- package/src/report/react/ExperimentList.tsx +15 -9
- package/src/report/react/MetricScatter.tsx +12 -14
- package/src/report/react/chart-math.test.ts +85 -0
- package/src/report/react/chart-math.ts +101 -22
- package/src/report/react/enhance.js +33 -1
- package/src/report/react/fixtures.ts +24 -17
- package/src/report/react/render.test.tsx +9 -64
- package/src/report/react/styles.css +73 -2
- package/src/report/report.test.ts +306 -98
- package/src/report/report.ts +6 -2
- package/src/report/text/faces.ts +47 -43
- package/src/report/text/table.ts +42 -5
- package/src/report/types.ts +41 -21
- package/src/results/annotated-source.test.ts +62 -9
- package/src/results/annotated-source.ts +64 -6
- package/src/results/attempt-evidence.test.ts +9 -7
- package/src/results/attempt-evidence.ts +15 -8
- package/src/results/attempt-source.ts +6 -3
- package/src/results/copy.ts +145 -55
- package/src/results/host-equivalence.test.ts +8 -6
- package/src/results/index.ts +2 -0
- package/src/results/locator.test.ts +1 -22
- package/src/results/open.ts +7 -1
- package/src/results/publish.ts +149 -0
- package/src/results/results.test.ts +85 -51
- package/src/results/truncate.ts +90 -0
- package/src/results/types.ts +7 -0
- package/src/results/writer.ts +31 -13
- package/src/runner/attempt.test.ts +138 -7
- package/src/runner/attempt.ts +603 -104
- package/src/runner/discover.test.ts +47 -0
- package/src/runner/discover.ts +36 -2
- package/src/runner/eval-source.test.ts +1 -27
- package/src/runner/feedback/agent.test.ts +504 -0
- package/src/runner/feedback/agent.ts +409 -0
- package/src/runner/feedback/ci.test.ts +562 -0
- package/src/runner/feedback/ci.ts +401 -0
- package/src/runner/feedback/coordinator.test.ts +317 -0
- package/src/runner/feedback/coordinator.ts +397 -0
- package/src/runner/feedback/failure.ts +40 -0
- package/src/runner/feedback/human.test.ts +616 -0
- package/src/runner/feedback/human.ts +535 -0
- package/src/runner/feedback/index.ts +66 -0
- package/src/runner/feedback/io.ts +78 -0
- package/src/runner/feedback/profile.test.ts +50 -0
- package/src/runner/feedback/profile.ts +58 -0
- package/src/runner/feedback/reducer.test.ts +395 -0
- package/src/runner/feedback/reducer.ts +260 -0
- package/src/runner/feedback/renderer.ts +82 -0
- package/src/runner/feedback/sink.ts +203 -0
- package/src/runner/feedback/testing.ts +106 -0
- package/src/runner/ledger.test.ts +230 -0
- package/src/runner/ledger.ts +329 -0
- package/src/runner/report.test.ts +128 -3
- package/src/runner/report.ts +33 -9
- package/src/runner/reporters/artifacts.ts +8 -2
- package/src/runner/reporters/braintrust.test.ts +8 -7
- package/src/runner/reporters/braintrust.ts +9 -2
- package/src/runner/reporters/index.ts +2 -2
- package/src/runner/reporters/json.test.ts +162 -0
- package/src/runner/reporters/json.ts +35 -8
- package/src/runner/reporters/shared.ts +1 -5
- package/src/runner/run.test.ts +760 -3
- package/src/runner/run.ts +242 -36
- package/src/runner/sandbox-prep.ts +3 -42
- package/src/runner/timing.ts +158 -0
- package/src/runner/types.ts +518 -22
- package/src/sandbox/checkpoint.test.ts +55 -0
- package/src/sandbox/checkpoint.ts +29 -8
- package/src/sandbox/cli-commands.ts +407 -0
- package/src/sandbox/docker.ts +115 -16
- package/src/sandbox/e2b-agent-template.test.ts +56 -0
- package/src/sandbox/e2b-agent-template.ts +94 -0
- package/src/sandbox/e2b.ts +74 -9
- package/src/sandbox/errors.ts +111 -4
- package/src/sandbox/index.ts +2 -0
- package/src/sandbox/io-retry.test.ts +58 -0
- package/src/sandbox/io-retry.ts +45 -0
- package/src/sandbox/keep-registry.test.ts +86 -0
- package/src/sandbox/keep-registry.ts +142 -0
- package/src/sandbox/keep.ts +178 -0
- package/src/sandbox/paths.test.ts +1 -0
- package/src/sandbox/paths.ts +19 -8
- package/src/sandbox/registry.ts +20 -3
- package/src/sandbox/resolve.ts +76 -11
- package/src/sandbox/retry.test.ts +70 -0
- package/src/sandbox/retry.ts +46 -4
- package/src/sandbox/types.ts +44 -6
- package/src/sandbox/vercel.ts +43 -20
- package/src/scoring/collector.ts +60 -17
- package/src/scoring/coverage.ts +95 -0
- package/src/scoring/diff.ts +81 -0
- package/src/scoring/display.test.ts +121 -0
- package/src/scoring/display.ts +133 -0
- package/src/scoring/evidence.test.ts +189 -0
- package/src/scoring/judge.test.ts +142 -0
- package/src/scoring/judge.ts +15 -18
- package/src/scoring/scoped.ts +217 -50
- package/src/scoring/types.ts +117 -20
- package/src/scoring/verdict.ts +16 -4
- package/src/shared/aggregate.ts +3 -2
- package/src/shared/types.ts +31 -0
- package/src/show/compose.ts +2 -2
- package/src/show/index.ts +21 -1
- package/src/show/render.ts +619 -104
- package/src/show/show.test.ts +235 -19
- package/src/tty-line.ts +8 -26
- package/src/util.test.ts +1 -0
- package/src/util.ts +41 -0
- package/src/view/app/components/AttemptModal.tsx +153 -2
- package/src/view/app/components/CodeView.tsx +32 -11
- package/src/view/app/components/CopyControls.tsx +2 -2
- package/src/view/app/i18n.ts +6 -0
- package/src/view/app/lib/attempt-route.test.ts +1 -0
- package/src/view/app/lib/verdict.ts +7 -9
- package/src/view/artifact-serving.test.ts +2 -1
- package/src/view/client-dist/app.css +1 -1
- package/src/view/client-dist/app.js +17 -17
- package/src/view/data.test.ts +1 -0
- package/src/view/data.ts +11 -1
- package/src/view/index.ts +11 -0
- package/src/view/server.ts +2 -0
- package/src/view/styles.css +3 -0
- package/src/view/view-report.test.ts +6 -5
- package/src/runner/reporters/console.ts +0 -70
- package/src/runner/reporters/live.test.ts +0 -56
- package/src/runner/reporters/live.ts +0 -247
- package/src/runner/reporters/quiet.test.ts +0 -66
- package/src/runner/reporters/quiet.ts +0 -49
- package/src/runner/reporters/table.ts +0 -277
package/dist/sandbox/docker.d.ts
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import type { Sandbox, CommandResult, CommandOptions, SandboxFile, SourceFiles, ReadSourceFilesOptions } from "../types.ts";
|
|
2
|
-
import type
|
|
2
|
+
import { type SandboxProvisionErrorKind } from "./errors.ts";
|
|
3
3
|
/**
|
|
4
4
|
* dockerode 对镜像拉取限流没有专门的错误类型;Docker Hub 429 体现在错误 message 里
|
|
5
5
|
* (如 "toomanyrequests: You have reached your pull rate limit")。
|
|
6
6
|
*/
|
|
7
7
|
export declare function classifyProvisionError(e: unknown): SandboxProvisionErrorKind;
|
|
8
|
+
/**
|
|
9
|
+
* Provisioning 重试前的对账:按 provision token 查询本地 daemon,查到的实例先销毁再重建
|
|
10
|
+
* (不做断线收养,重建比重连语义干净)。docker create 是对本地 daemon 的调用,歧义窗口极小,
|
|
11
|
+
* 这条主要兜 daemon 代理 / 远程 DOCKER_HOST 的场景。查询或销毁失败必须抛出——对账是重试的
|
|
12
|
+
* 硬前置,静默放行等于盲重试(见 docs/feature/sandbox/architecture.md);唯一的例外:
|
|
13
|
+
* 容器已不存在(404),视作对账完成。
|
|
14
|
+
*/
|
|
15
|
+
export declare function reconcileProvision(token: string): Promise<void>;
|
|
8
16
|
/** 创建 Docker 沙箱的选项。 */
|
|
9
17
|
export interface DockerSandboxOptions {
|
|
10
18
|
/** 单条命令超时(毫秒)。 */
|
|
@@ -13,6 +21,10 @@ export interface DockerSandboxOptions {
|
|
|
13
21
|
runtime?: "node20" | "node24";
|
|
14
22
|
/** 覆盖默认镜像(默认按 runtime 选 `node:*-slim`)。预制模板:烘焙好 agent CLI 的镜像名。 */
|
|
15
23
|
image?: string;
|
|
24
|
+
/** runner 绑定到 `sandbox.create` 的反馈句柄(镜像拉取进度走它);省略退回全局 sink。 */
|
|
25
|
+
feedback?: import("../types.ts").ScopedFeedback;
|
|
26
|
+
/** 一次性 provision token:写进容器 label,歧义类失败重试前按它对账(见 errors.ts 的两维分类)。 */
|
|
27
|
+
provisionToken?: string;
|
|
16
28
|
}
|
|
17
29
|
/**
|
|
18
30
|
* Docker 沙箱:为每次运行起一个隔离容器。
|
|
@@ -27,6 +39,8 @@ export declare class DockerSandbox implements Sandbox {
|
|
|
27
39
|
private timeout;
|
|
28
40
|
private runtime;
|
|
29
41
|
private image?;
|
|
42
|
+
private feedback?;
|
|
43
|
+
private provisionToken?;
|
|
30
44
|
constructor(options?: DockerSandboxOptions);
|
|
31
45
|
/** 创建并启动一个 Docker 沙箱。 */
|
|
32
46
|
static create(options?: DockerSandboxOptions): Promise<DockerSandbox>;
|
|
@@ -79,6 +93,13 @@ export declare class DockerSandbox implements Sandbox {
|
|
|
79
93
|
* 打成单文件 tar → putArchive 到目标目录,与 uploadFiles 同一机制但目标路径自由。
|
|
80
94
|
*/
|
|
81
95
|
uploadFile(destPath: string, content: Buffer): Promise<void>;
|
|
82
|
-
/**
|
|
96
|
+
/** 销毁容器:显式 stop + remove(创建时不带 AutoRemove,见 createContainer 的注释)。 */
|
|
83
97
|
stop(): Promise<void>;
|
|
98
|
+
/**
|
|
99
|
+
* 留存休眠(suspend):`docker stop`——文件系统落盘持久、不占内存、跨 daemon 重启存活。
|
|
100
|
+
* 不用 `docker pause`(内存驻留,daemon 重启即失)也不用 `docker commit`(引入第二种资源面)。
|
|
101
|
+
* 不属于中性 Sandbox 接口——「留下」是 runner 的调度决定,不是沙箱的能力;由 sandbox/keep.ts
|
|
102
|
+
* 在 sandbox/ 域内路由到这里。
|
|
103
|
+
*/
|
|
104
|
+
suspend(): Promise<void>;
|
|
84
105
|
}
|
package/dist/sandbox/e2b.d.ts
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import type { Sandbox, CommandResult, CommandOptions, SandboxFile, SourceFiles, ReadSourceFilesOptions } from "../types.ts";
|
|
2
|
-
import type
|
|
2
|
+
import { type SandboxProvisionErrorKind } from "./errors.ts";
|
|
3
3
|
/** e2b 的限流错误是 SDK 原生的 RateLimitError(HTTP 429 映射而来);见 resolve.ts 的 withProvisionRetry。 */
|
|
4
|
+
/**
|
|
5
|
+
* Provisioning 重试前的对账:按 metadata 里的 provision token 检索远端实例,查到即 kill。
|
|
6
|
+
* 检索或销毁失败必须抛出——对账是重试的硬前置,静默放行等于盲重试,会复制计费实例
|
|
7
|
+
* (见 docs/feature/sandbox/architecture.md「Provisioning 失败与重试」)。
|
|
8
|
+
* 唯一的例外:实例已不存在(NotFound),视作对账完成。
|
|
9
|
+
*/
|
|
10
|
+
export declare function reconcileProvision(token: string): Promise<void>;
|
|
4
11
|
export declare function classifyProvisionError(e: unknown): SandboxProvisionErrorKind;
|
|
5
12
|
export declare class E2BSandbox implements Sandbox {
|
|
6
13
|
readonly workdir = "/home/user/workspace";
|
|
@@ -13,6 +20,7 @@ export declare class E2BSandbox implements Sandbox {
|
|
|
13
20
|
timeout?: number;
|
|
14
21
|
runtime?: "node20" | "node24";
|
|
15
22
|
template?: string;
|
|
23
|
+
provisionToken?: string;
|
|
16
24
|
}): Promise<E2BSandbox>;
|
|
17
25
|
runCommand(cmd: string, args?: string[], opts?: CommandOptions): Promise<CommandResult>;
|
|
18
26
|
runShell(script: string, opts?: CommandOptions): Promise<CommandResult>;
|
|
@@ -26,6 +34,12 @@ export declare class E2BSandbox implements Sandbox {
|
|
|
26
34
|
ignore?: string[];
|
|
27
35
|
}): Promise<void>;
|
|
28
36
|
stop(): Promise<void>;
|
|
37
|
+
/**
|
|
38
|
+
* 留存休眠(suspend):e2b `pause`——文件系统与内存整体持久化,暂停期间停止计费,
|
|
39
|
+
* 现场无限期保留、可 resume 找回(没有自然过期时刻,注册表不写 expiresAt)。
|
|
40
|
+
* SDK 版本差异按能力探测(betaPause 是旧名),都没有则如实抛错(现场保持 alive)。
|
|
41
|
+
*/
|
|
42
|
+
suspend(): Promise<void>;
|
|
29
43
|
downloadFile(path: string): Promise<Buffer>;
|
|
30
44
|
uploadFile(path: string, content: Buffer): Promise<void>;
|
|
31
45
|
}
|
package/dist/sandbox/errors.d.ts
CHANGED
|
@@ -1,4 +1,31 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Provisioning 失败的两维分类(见 docs/feature/sandbox/architecture.md「Provisioning 失败与重试」):
|
|
3
|
+
* **性质**(瞬时 / 确定性)决定要不要重试,**后果**(远端是否可能已创建实例)决定能不能直接重试。
|
|
4
|
+
* - `rate_limit` / `rejected`:拒绝类——请求确定没被受理(限流、DNS/连接被拒/TLS 握手失败),
|
|
5
|
+
* 直接指数退避重试。
|
|
6
|
+
* - `ambiguous`:歧义类——请求可能已被受理、只是响应丢了(响应中途重置、请求超时、5xx),
|
|
7
|
+
* 远端可能有一台正在计费的实例;重试前必须对账(provider 提供检索通道时),否则第一次抛出。
|
|
8
|
+
* - `unknown`:确定性失败(模板不存在、凭据缺失、权限不足),第一次就抛,重试没有意义。
|
|
9
|
+
* 分类器偏向宽认瞬时:误判成确定性会白白判死可自愈的 attempt;反向只多花封顶的退避时间。
|
|
10
|
+
*/
|
|
11
|
+
export type SandboxProvisionErrorKind = "rate_limit" | "rejected" | "ambiguous" | "unknown";
|
|
12
|
+
/** 拒绝类(含限流):请求确定没被受理,直接退避重试。 */
|
|
13
|
+
export declare function isRejectedProvisionError(kind: SandboxProvisionErrorKind): boolean;
|
|
14
|
+
/** 按 kind 判断是否可能重试(歧义类还需对账通道,见 retry.ts);unknown 第一次就抛。 */
|
|
4
15
|
export declare function isRetryableProvisionError(kind: SandboxProvisionErrorKind): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* 与文件 IO 重试共用的保守瞬时分类兜底:provider 没认出的错误按形态落进拒绝类或歧义类。
|
|
18
|
+
* 连接建立失败(DNS / refused / TLS)= 拒绝类;响应中途重置 / 超时 / 5xx = 歧义类。
|
|
19
|
+
*/
|
|
20
|
+
export declare function classifyProvisionErrorFallback(error: unknown): SandboxProvisionErrorKind;
|
|
21
|
+
/**
|
|
22
|
+
* 已创建 Sandbox 上单次文件 IO 的中性错误分类。这里描述的是传输层瞬时故障,
|
|
23
|
+
* 不是文件不存在、权限不足、路径错误等确定性结果。
|
|
24
|
+
*/
|
|
25
|
+
export type SandboxIoErrorKind = "rate_limit" | "network" | "service_unavailable" | "unknown";
|
|
26
|
+
export declare function isRetryableSandboxIoError(kind: SandboxIoErrorKind): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* 内置 provider 与自定义 provider 共用的保守分类器。SDK 常把底层网络错误包在
|
|
29
|
+
* `cause` 中,因此最多沿 cause 链向下检查几层;Abort/沙箱终止明确不重试。
|
|
30
|
+
*/
|
|
31
|
+
export declare function classifySandboxIoError(error: unknown): SandboxIoErrorKind;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type SandboxIoErrorKind } from "./errors.ts";
|
|
2
|
+
export interface SandboxIoRetryOptions {
|
|
3
|
+
maxAttempts?: number;
|
|
4
|
+
baseDelayMs?: number;
|
|
5
|
+
classify?: (error: unknown) => SandboxIoErrorKind;
|
|
6
|
+
/** 内部可观察挂点;runner feedback 接入后由统一包装层注入。 */
|
|
7
|
+
onRetry?: (event: {
|
|
8
|
+
attempt: number;
|
|
9
|
+
delayMs: number;
|
|
10
|
+
kind: SandboxIoErrorKind;
|
|
11
|
+
error: unknown;
|
|
12
|
+
}) => void;
|
|
13
|
+
/** 仅供确定性单测注入。 */
|
|
14
|
+
random?: () => number;
|
|
15
|
+
sleep?: (ms: number) => Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
export declare function withSandboxIoRetry<T>(operation: () => Promise<T>, options?: SandboxIoRetryOptions): Promise<T>;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { Sandbox } from "../types.ts";
|
|
2
2
|
export declare function registerSandbox(sb: Sandbox): void;
|
|
3
|
+
/** 留存提交成功后把沙箱移出本次 run 的内存强清集合(不 stop——现场归持久注册表管理)。 */
|
|
4
|
+
export declare function unregisterSandbox(sb: Sandbox): void;
|
|
3
5
|
export declare function liveSandboxCount(): number;
|
|
4
6
|
/**
|
|
5
7
|
* 带超时地停单个沙箱。成功 / 失败 / 超时都从登记表移除(失败的靠 provider 过期兜底),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
|
-
import type { CustomSandboxSpec, Sandbox, SandboxOption, SandboxRuntime } from "../types.ts";
|
|
2
|
+
import type { CustomSandboxSpec, JsonValue, Sandbox, SandboxOption, SandboxRuntime, ScopedFeedback } from "../types.ts";
|
|
3
3
|
import { type ProvisionSlot } from "./retry.ts";
|
|
4
4
|
/** 归一化后的沙箱描述:确定的 provider + 各 provider 参数(只有对应 provider 用得上的会有值)。 */
|
|
5
5
|
export interface ResolvedSandbox {
|
|
@@ -25,16 +25,29 @@ export declare function resolveSandbox(opt: SandboxOption | undefined, runtimeDe
|
|
|
25
25
|
* vercel:sandbox session 有严格的并发限制,1 避免 429。
|
|
26
26
|
*/
|
|
27
27
|
export declare function sandboxRecommendedConcurrency(opt: SandboxOption | undefined): number;
|
|
28
|
-
/** 报告 / 日志用的简短标签:provider 名,带上区分性的参数(镜像 / 快照 / 模板)。 */
|
|
29
|
-
export declare function sandboxLabel(opt: SandboxOption | undefined): string;
|
|
30
28
|
/**
|
|
31
|
-
*
|
|
32
|
-
*
|
|
29
|
+
* ExperimentRunInfo.sandbox 的投影:provider 名 + 公开参数(镜像/快照/模板/runtime)+ 配置指纹。
|
|
30
|
+
* 参数只经这个投影落盘——token、凭据路径永不进来;defineSandbox 自定义 provider 未实现
|
|
31
|
+
* `publicConfig()` 时只落 provider 名(见 docs/feature/results/architecture.md)。
|
|
33
32
|
*/
|
|
33
|
+
export declare function sandboxRunInfo(opt: SandboxOption | undefined): {
|
|
34
|
+
provider: string;
|
|
35
|
+
params?: Record<string, JsonValue>;
|
|
36
|
+
fingerprint?: string;
|
|
37
|
+
} | undefined;
|
|
38
|
+
/** 报告 / 日志用的简短标签:provider 名,带上区分性的参数(镜像 / 快照 / 模板)。 */
|
|
39
|
+
export declare function sandboxLabel(opt: SandboxOption | undefined): string;
|
|
34
40
|
export declare function createSandbox(opts: {
|
|
35
41
|
sandbox?: SandboxOption;
|
|
36
42
|
timeout?: number;
|
|
37
43
|
runtime?: SandboxRuntime;
|
|
38
44
|
/** 调用方并发槽位的临时归还/收回,传给 withProvisionRetry 在退避睡眠期间释放(见 retry.ts)。 */
|
|
39
45
|
provisionSlot?: ProvisionSlot;
|
|
46
|
+
/** runner 绑定到 `sandbox.create` 阶段的反馈句柄;provider 的进度/诊断都走它。 */
|
|
47
|
+
feedback?: ScopedFeedback;
|
|
48
|
+
/**
|
|
49
|
+
* release 覆写(留存路径用):Scope 关闭时按调用方的 disposition 决定 stop 还是 suspend。
|
|
50
|
+
* 省略 = 恒 stopSandbox(默认销毁)。
|
|
51
|
+
*/
|
|
52
|
+
release?: (sb: Sandbox) => Promise<void>;
|
|
40
53
|
}): Effect.Effect<Sandbox, never, import("effect/Scope").Scope>;
|
package/dist/sandbox/retry.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type SandboxProvisionErrorKind } from "./errors.ts";
|
|
2
|
+
import type { ScopedFeedback } from "../types.ts";
|
|
2
3
|
/**
|
|
3
4
|
* 调用方(resolve.ts)持有的并发槽位的临时归还/收回。不认调用方用的是不是 Effect —— 只要求
|
|
4
5
|
* 两个 async 方法,保持这层 provider 无关。
|
|
@@ -7,4 +8,13 @@ export interface ProvisionSlot {
|
|
|
7
8
|
release(): Promise<void>;
|
|
8
9
|
reacquire(): Promise<void>;
|
|
9
10
|
}
|
|
10
|
-
export declare function withProvisionRetry<T>(create: () => Promise<T>, classify: (e: unknown) => SandboxProvisionErrorKind, slot?: ProvisionSlot
|
|
11
|
+
export declare function withProvisionRetry<T>(create: () => Promise<T>, classify: (e: unknown) => SandboxProvisionErrorKind, slot?: ProvisionSlot, feedback?: ScopedFeedback,
|
|
12
|
+
/**
|
|
13
|
+
* 对账钩子:按 provision token 检索远端、销毁可能已创建的实例。提供时**每次重试前都执行**,
|
|
14
|
+
* 不区分拒绝类还是歧义类——被重试的 create() 闭包跨多个请求,一个 429 可能来自实例已创建
|
|
15
|
+
* 之后的初始化请求,分类不保证远端没有实例。对账排在退避睡眠之后(紧跟失败发出的检索大概率
|
|
16
|
+
* 同样被限流);对账抛错即放弃重试、抛回原始 create 错误。没有对账通道的 provider 不传——
|
|
17
|
+
* 歧义类第一次抛出:宁可判死一个 attempt,不留一台计费的无主实例
|
|
18
|
+
* (见 docs/feature/sandbox/architecture.md)。
|
|
19
|
+
*/
|
|
20
|
+
reconcile?: () => Promise<void>): Promise<T>;
|
package/dist/sandbox/types.d.ts
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Cleanup, ScopedFeedback } from "../shared/types.ts";
|
|
2
2
|
export interface CommandResult {
|
|
3
3
|
stdout: string;
|
|
4
4
|
stderr: string;
|
|
5
5
|
exitCode: number;
|
|
6
|
+
/**
|
|
7
|
+
* 这次执行的命令摘要(有界、已脱敏,与时间树 command 节点同一份文案)。由执行面在最外层
|
|
8
|
+
* 公开调用处附加;直接从 provider 拿到的裸结果可能没有。断言失败时用作 evidence
|
|
9
|
+
* (`commandSucceeded()` 的「命令行本身」),消费方按可选字段读。
|
|
10
|
+
*/
|
|
11
|
+
command?: string;
|
|
6
12
|
}
|
|
7
13
|
export interface SandboxFile {
|
|
8
14
|
path: string;
|
|
@@ -71,14 +77,27 @@ export type SandboxRuntime = "node20" | "node24";
|
|
|
71
77
|
*/
|
|
72
78
|
export interface SandboxHooks<Self> {
|
|
73
79
|
/** 已挂载的 setup 钩子,按追加顺序保存(内部读取,一般用不到)。 */
|
|
74
|
-
readonly setupHooks?: readonly
|
|
80
|
+
readonly setupHooks?: readonly SandboxHook[];
|
|
75
81
|
/** 已挂载的 teardown 钩子,按追加顺序保存,执行时逆序(内部读取,一般用不到)。 */
|
|
76
|
-
readonly teardownHooks?: readonly
|
|
82
|
+
readonly teardownHooks?: readonly SandboxHook[];
|
|
77
83
|
/** 追加一个沙箱级 setup 钩子,返回新 spec;详细契约见 {@link SandboxHooks}。 */
|
|
78
|
-
setup(fn:
|
|
84
|
+
setup(fn: SandboxHook): Self;
|
|
79
85
|
/** 追加一个沙箱级 teardown 钩子,返回新 spec;详细契约见 {@link SandboxHooks}。 */
|
|
80
|
-
teardown(fn:
|
|
86
|
+
teardown(fn: SandboxHook): Self;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Sandbox hook 的窄上下文:只有 `experimentId`、`signal` 与作用域绑定的 `progress/diagnostic`,
|
|
90
|
+
* 不借用包含 session / model / telemetry 的完整 `AgentContext`
|
|
91
|
+
* (见 docs/feature/sandbox/library.md「环境层生命周期钩子」)。
|
|
92
|
+
*/
|
|
93
|
+
export interface SandboxHookContext extends ScopedFeedback {
|
|
94
|
+
/** 路径推导出的实验 id;不经 experiment 跑时是 undefined。跨 attempt 状态按它分区。 */
|
|
95
|
+
readonly experimentId?: string;
|
|
96
|
+
/** 本次 attempt 的中止信号。 */
|
|
97
|
+
readonly signal: AbortSignal;
|
|
81
98
|
}
|
|
99
|
+
/** 沙箱级生命周期钩子(`.setup()` / `.teardown()` 链式挂载);`setup` 可返回 cleanup 闭包。 */
|
|
100
|
+
export type SandboxHook = (sandbox: Sandbox, ctx: SandboxHookContext) => void | Cleanup | Promise<void | Cleanup>;
|
|
82
101
|
/**
|
|
83
102
|
* Sandbox 的「数据结构」定义 —— 与 agent 一样可带参数(见 docs/feature/sandbox/library.md)。
|
|
84
103
|
* 必须用工厂函数构造(`dockerSandbox()` / `vercelSandbox()` / `e2bSandbox()` / `defineSandbox()`),
|
|
@@ -113,10 +132,17 @@ export interface CustomSandboxSpec extends SandboxHooks<CustomSandboxSpec> {
|
|
|
113
132
|
readonly provider: string;
|
|
114
133
|
readonly runtime?: SandboxRuntime;
|
|
115
134
|
readonly recommendedConcurrency?: number;
|
|
135
|
+
/** `feedback` 绑定到 `sandbox.create` 阶段:分配实例 / 拉镜像 / 恢复 snapshot 的进度与诊断走它。 */
|
|
116
136
|
readonly create: (opts: {
|
|
117
137
|
timeout?: number;
|
|
118
138
|
runtime?: SandboxRuntime;
|
|
139
|
+
feedback: ScopedFeedback;
|
|
119
140
|
}) => Promise<Sandbox>;
|
|
141
|
+
/**
|
|
142
|
+
* 「哪些参数可发布」的投影,进结果快照的 ExperimentRunInfo.sandbox.params;
|
|
143
|
+
* 未实现时只落 provider 名。token、凭据路径永远不该出现在返回值里。
|
|
144
|
+
*/
|
|
145
|
+
readonly publicConfig?: () => Record<string, import("../shared/types.ts").JsonValue>;
|
|
120
146
|
}
|
|
121
147
|
export type SandboxSpec = DockerSandboxSpec | VercelSandboxSpec | E2BSandboxSpec | CustomSandboxSpec;
|
|
122
148
|
/** config / experiment 的 `sandbox` 字段:必须是工厂函数产出的 spec 数据结构;沙箱型 agent 不能省略。 */
|
|
@@ -133,6 +159,14 @@ export interface CommandOptions {
|
|
|
133
159
|
* 不支持的 provider 忽略)—— 日志怎么浮现是 provider 的事,adapter 只声明意图。
|
|
134
160
|
*/
|
|
135
161
|
stream?: boolean;
|
|
162
|
+
/**
|
|
163
|
+
* 命令 stdout 每到一块就调用一次。回调只用于运行中的短命反馈;完整 stdout 仍会原样
|
|
164
|
+
* 出现在返回的 `CommandResult` 里。provider 不支持真流时,至少会在命令结束后按完整
|
|
165
|
+
* stdout 调用一次,不能静默丢掉。
|
|
166
|
+
*/
|
|
167
|
+
onStdout?: (chunk: string) => void | Promise<void>;
|
|
168
|
+
/** `onStdout` 的 stderr 对应物;完整 stderr 仍保留在 `CommandResult`。 */
|
|
169
|
+
onStderr?: (chunk: string) => void | Promise<void>;
|
|
136
170
|
/**
|
|
137
171
|
* 以 root 跑本命令。默认 `false` —— 命令以沙箱的标准**非 root** 用户跑(agent 的自然环境)。
|
|
138
172
|
* 给 setup 阶段装系统依赖用(`apt-get install …`、`pip install --break-system-packages …`)。
|
package/dist/sandbox/vercel.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Sandbox, CommandResult, CommandOptions, SandboxFile, SourceFiles, ReadSourceFilesOptions } from "../types.ts";
|
|
2
|
-
import type
|
|
2
|
+
import { type SandboxProvisionErrorKind } from "./errors.ts";
|
|
3
3
|
/**
|
|
4
4
|
* vercel SDK 对单次 fetch 的 429 已有内部重试(见 @vercel/sandbox 的 with-retry.js,
|
|
5
5
|
* 5 次指数退避);这里再分类是为了给 create() 整体重试兜底——耗尽内部重试后仍返回 429
|
|
@@ -19,6 +19,7 @@ export declare class VercelSandbox implements Sandbox {
|
|
|
19
19
|
timeout?: number;
|
|
20
20
|
runtime?: "node20" | "node24";
|
|
21
21
|
snapshotId?: string;
|
|
22
|
+
feedback?: import("../types.ts").ScopedFeedback;
|
|
22
23
|
}): Promise<VercelSandbox>;
|
|
23
24
|
private rotateIfNeeded;
|
|
24
25
|
runCommand(cmd: string, args?: string[], opts?: CommandOptions): Promise<CommandResult>;
|
|
@@ -32,6 +33,11 @@ export declare class VercelSandbox implements Sandbox {
|
|
|
32
33
|
ignore?: string[];
|
|
33
34
|
}): Promise<void>;
|
|
34
35
|
stop(): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* 留存休眠(suspend):vercel `stop`——sandbox 默认持久,stop 保存文件系统,之后经
|
|
38
|
+
* `Sandbox.get` 恢复(SDK 原生能力);内存态不保留,唤醒后进程要重新启动。
|
|
39
|
+
*/
|
|
40
|
+
suspend(): Promise<void>;
|
|
35
41
|
downloadFile(path: string): Promise<Buffer>;
|
|
36
42
|
uploadFile(path: string, content: Buffer): Promise<void>;
|
|
37
43
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { CoverageStatus, EvidenceCoverage } from "../agents/types.ts";
|
|
2
|
+
/** 证据通道全集(EvidenceCoverage 的键)。 */
|
|
3
|
+
export declare const COVERAGE_CHANNELS: readonly ["events", "actions", "messages", "usage", "status", "data"];
|
|
4
|
+
export type CoverageChannel = (typeof COVERAGE_CHANNELS)[number];
|
|
5
|
+
/**
|
|
6
|
+
* 解析后的通道状态:未声明按 "unknown" 落地(不是 complete)。unknown 与 unavailable 在
|
|
7
|
+
* 消费侧同样保守;区别只在展示(unknown =「Adapter 没说」,unavailable =「Adapter 说了拿不到」)。
|
|
8
|
+
*/
|
|
9
|
+
export type ResolvedCoverageStatus = CoverageStatus | "unknown";
|
|
10
|
+
/** 解析后的单通道声明。 */
|
|
11
|
+
export interface ResolvedCoverageChannel {
|
|
12
|
+
status: ResolvedCoverageStatus;
|
|
13
|
+
reason?: string;
|
|
14
|
+
}
|
|
15
|
+
/** 全通道解析后的覆盖:每个通道必有一个状态(缺省 unknown)。 */
|
|
16
|
+
export type ResolvedCoverage = Readonly<Record<CoverageChannel, ResolvedCoverageChannel>>;
|
|
17
|
+
/**
|
|
18
|
+
* 官方 SDK 适配器用的「全通道 complete」常量:完整事件流、完整 steps/output、经过生命周期
|
|
19
|
+
* fixture 验证的 transcript 才可以声明它(见 docs/feature/adapters/architecture/evidence.md)。
|
|
20
|
+
*/
|
|
21
|
+
export declare const completeCoverage: EvidenceCoverage;
|
|
22
|
+
/** Agent 级声明 → 全通道解析(未声明通道 = unknown)。 */
|
|
23
|
+
export declare function resolveAgentCoverage(declared: EvidenceCoverage | undefined): ResolvedCoverage;
|
|
24
|
+
/**
|
|
25
|
+
* Turn 级降级:相对 base(Agent 默认)只能变差,不能升格——Turn 声明比 base 更好的状态
|
|
26
|
+
* 直接被 base 压住(min 语义天然满足「不能把 Agent 未声明的通道升格成 complete」)。
|
|
27
|
+
*/
|
|
28
|
+
export declare function downgradeCoverage(base: ResolvedCoverage, turn: EvidenceCoverage | undefined): ResolvedCoverage;
|
|
29
|
+
/** attempt / session 级聚合:各 turn 的最差值(unknown/unavailable < partial < complete)。 */
|
|
30
|
+
export declare function worstCoverage(list: readonly ResolvedCoverage[]): ResolvedCoverage;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { AssertionResult, PrimaryAssertionSummary, Verdict } from "./types.ts";
|
|
2
|
+
/** 摘要面的单值收口:折单行 + 240 字符上限。任何把断言事实放进「行」里的面共用这一条。 */
|
|
3
|
+
export declare function summaryText(value: string): string;
|
|
4
|
+
/**
|
|
5
|
+
* 按公开展示契约选择主失败断言:failed gate 优先;只有 soft 促成 failed verdict 时才取 soft;
|
|
6
|
+
* errored 且没有结构化 error 时可由第一条非 optional unavailable 解释。
|
|
7
|
+
*/
|
|
8
|
+
export declare function primaryAssertionSummary(assertions: readonly AssertionResult[], verdict: Verdict): PrimaryAssertionSummary | undefined;
|
|
9
|
+
/** 摘要的事实层;Human/Agent 用作第二行,表格可把它接在标题后。 */
|
|
10
|
+
export declare function assertionSummaryDetail(summary: PrimaryAssertionSummary): string | undefined;
|
|
11
|
+
/** Human/Agent 的至多两层文本。 */
|
|
12
|
+
export declare function assertionSummaryLines(summary: PrimaryAssertionSummary): [string] | [string, string];
|
|
13
|
+
/** 比较列表的单元格投影;无 group 时不重复 `gate:` 前缀。 */
|
|
14
|
+
export declare function compactAssertionSummary(summary: PrimaryAssertionSummary): string;
|
|
15
|
+
/**
|
|
16
|
+
* 单元格投影的宽度收口。空间不足时按解释力从低到高让位:先截语义标题、再截 matcher,
|
|
17
|
+
* `expected / received` 与 `+N more failures` 最后截——它们直接解释为什么红。
|
|
18
|
+
* maxChars 由渲染面按可用宽度给(如两行单元格 = 2 × 列宽);字符数口径与
|
|
19
|
+
* SUMMARY_TEXT_MAX_CHARS 一致,显示宽度的精确裁剪仍归渲染面。
|
|
20
|
+
*/
|
|
21
|
+
export declare function fitCompactAssertionSummary(summary: PrimaryAssertionSummary, maxChars: number): string;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// scoring 结果的摘要投影。这里仅决定「摘要面显示哪一条、显示哪些事实」;完整诊断面继续
|
|
2
|
+
// 消费 AssertionResult[],不复用这个有损投影。
|
|
3
|
+
/**
|
|
4
|
+
* Human/Agent 摘要是一条终端事实行,不是完整证据面。压成单行并设字符上限,避免 received
|
|
5
|
+
* 恰好是源码/工具输出时把多页内容灌进 scrollback;完整 AssertionResult 仍原样留给 show/view。
|
|
6
|
+
*/
|
|
7
|
+
const SUMMARY_TEXT_MAX_CHARS = 240;
|
|
8
|
+
/** 摘要面的单值收口:折单行 + 240 字符上限。任何把断言事实放进「行」里的面共用这一条。 */
|
|
9
|
+
export function summaryText(value) {
|
|
10
|
+
const singleLine = value.replace(/\s+/g, " ").trim();
|
|
11
|
+
return singleLine.length <= SUMMARY_TEXT_MAX_CHARS
|
|
12
|
+
? singleLine
|
|
13
|
+
: `${singleLine.slice(0, SUMMARY_TEXT_MAX_CHARS - 1)}…`;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* 按公开展示契约选择主失败断言:failed gate 优先;只有 soft 促成 failed verdict 时才取 soft;
|
|
17
|
+
* errored 且没有结构化 error 时可由第一条非 optional unavailable 解释。
|
|
18
|
+
*/
|
|
19
|
+
export function primaryAssertionSummary(assertions, verdict) {
|
|
20
|
+
if (verdict === "failed") {
|
|
21
|
+
const failedGates = assertions.filter((assertion) => assertion.outcome === "failed" && assertion.severity === "gate");
|
|
22
|
+
if (failedGates.length > 0)
|
|
23
|
+
return summaryOf(failedGates[0], failedGates.length - 1);
|
|
24
|
+
const failedSoft = assertions.filter((assertion) => assertion.outcome === "failed" && assertion.severity === "soft");
|
|
25
|
+
if (failedSoft.length > 0)
|
|
26
|
+
return summaryOf(failedSoft[0], failedSoft.length - 1);
|
|
27
|
+
}
|
|
28
|
+
if (verdict === "errored") {
|
|
29
|
+
const unavailable = assertions.filter((assertion) => assertion.outcome === "unavailable" && assertion.optional !== true);
|
|
30
|
+
if (unavailable.length > 0)
|
|
31
|
+
return summaryOf(unavailable[0], unavailable.length - 1);
|
|
32
|
+
}
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
function summaryOf(assertion, additionalFailures) {
|
|
36
|
+
const rawTitle = assertion.groupPath?.length ? assertion.groupPath.join(" > ") : assertion.name;
|
|
37
|
+
const rawMatcher = assertion.detail ?? assertion.name;
|
|
38
|
+
const title = summaryText(rawTitle);
|
|
39
|
+
return {
|
|
40
|
+
severity: assertion.severity,
|
|
41
|
+
assertion: title,
|
|
42
|
+
...(rawMatcher !== rawTitle ? { matcher: summaryText(rawMatcher) } : {}),
|
|
43
|
+
...(assertion.outcome === "unavailable"
|
|
44
|
+
? { reason: summaryText(assertion.reason) }
|
|
45
|
+
: {
|
|
46
|
+
...(assertion.expected !== undefined ? { expected: summaryText(assertion.expected) } : {}),
|
|
47
|
+
...(assertion.received !== undefined ? { received: summaryText(assertion.received) } : {}),
|
|
48
|
+
...(assertion.severity === "soft" || assertion.threshold !== undefined ? { score: assertion.score } : {}),
|
|
49
|
+
...(assertion.threshold !== undefined ? { threshold: assertion.threshold } : {}),
|
|
50
|
+
}),
|
|
51
|
+
additionalFailures,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/** 摘要的事实层;Human/Agent 用作第二行,表格可把它接在标题后。 */
|
|
55
|
+
export function assertionSummaryDetail(summary) {
|
|
56
|
+
const parts = [];
|
|
57
|
+
if (summary.matcher !== undefined)
|
|
58
|
+
parts.push(summary.matcher);
|
|
59
|
+
if (summary.expected !== undefined)
|
|
60
|
+
parts.push(`expected ${summary.expected}`);
|
|
61
|
+
if (summary.received !== undefined)
|
|
62
|
+
parts.push(`received ${summary.received}`);
|
|
63
|
+
if (summary.score !== undefined)
|
|
64
|
+
parts.push(`score ${summary.score}`);
|
|
65
|
+
if (summary.threshold !== undefined)
|
|
66
|
+
parts.push(`threshold ${summary.threshold}`);
|
|
67
|
+
if (summary.reason !== undefined)
|
|
68
|
+
parts.push(`reason ${summary.reason}`);
|
|
69
|
+
if (summary.additionalFailures > 0)
|
|
70
|
+
parts.push(`+${summary.additionalFailures} more failures`);
|
|
71
|
+
return parts.length > 0 ? parts.join(" · ") : undefined;
|
|
72
|
+
}
|
|
73
|
+
/** Human/Agent 的至多两层文本。 */
|
|
74
|
+
export function assertionSummaryLines(summary) {
|
|
75
|
+
const head = `${summary.severity}: ${summary.assertion}`;
|
|
76
|
+
const detail = assertionSummaryDetail(summary);
|
|
77
|
+
return detail === undefined ? [head] : [head, detail];
|
|
78
|
+
}
|
|
79
|
+
/** 比较列表的单元格投影;无 group 时不重复 `gate:` 前缀。 */
|
|
80
|
+
export function compactAssertionSummary(summary) {
|
|
81
|
+
const hasDistinctTitle = summary.matcher !== undefined;
|
|
82
|
+
const head = hasDistinctTitle ? `${summary.severity}: ${summary.assertion}` : summary.assertion;
|
|
83
|
+
const detail = assertionSummaryDetail(summary);
|
|
84
|
+
return detail === undefined ? head : `${head} · ${detail}`;
|
|
85
|
+
}
|
|
86
|
+
/** 收口用的截断:目标长度容不下时截到 target-1 并补 `…`。 */
|
|
87
|
+
function shrinkTo(text, target) {
|
|
88
|
+
return text.length <= target ? text : `${text.slice(0, Math.max(0, target - 1))}…`;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* 单元格投影的宽度收口。空间不足时按解释力从低到高让位:先截语义标题、再截 matcher,
|
|
92
|
+
* `expected / received` 与 `+N more failures` 最后截——它们直接解释为什么红。
|
|
93
|
+
* maxChars 由渲染面按可用宽度给(如两行单元格 = 2 × 列宽);字符数口径与
|
|
94
|
+
* SUMMARY_TEXT_MAX_CHARS 一致,显示宽度的精确裁剪仍归渲染面。
|
|
95
|
+
*/
|
|
96
|
+
export function fitCompactAssertionSummary(summary, maxChars) {
|
|
97
|
+
const budget = Math.max(24, Math.floor(maxChars));
|
|
98
|
+
let full = compactAssertionSummary(summary);
|
|
99
|
+
if (full.length <= budget)
|
|
100
|
+
return full;
|
|
101
|
+
const TITLE_FLOOR = 24;
|
|
102
|
+
let fitted = {
|
|
103
|
+
...summary,
|
|
104
|
+
assertion: shrinkTo(summary.assertion, Math.max(TITLE_FLOOR, summary.assertion.length - (full.length - budget))),
|
|
105
|
+
};
|
|
106
|
+
full = compactAssertionSummary(fitted);
|
|
107
|
+
if (full.length <= budget)
|
|
108
|
+
return full;
|
|
109
|
+
const MATCHER_FLOOR = 16;
|
|
110
|
+
if (fitted.matcher !== undefined) {
|
|
111
|
+
fitted = {
|
|
112
|
+
...fitted,
|
|
113
|
+
matcher: shrinkTo(fitted.matcher, Math.max(MATCHER_FLOOR, fitted.matcher.length - (full.length - budget))),
|
|
114
|
+
};
|
|
115
|
+
full = compactAssertionSummary(fitted);
|
|
116
|
+
if (full.length <= budget)
|
|
117
|
+
return full;
|
|
118
|
+
}
|
|
119
|
+
return shrinkTo(full, budget);
|
|
120
|
+
}
|