@deepseek-ai/dsh-session-snapshot 0.1.2-alpha.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 DeepSeek
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,6 @@
1
+ # Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
2
+ # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
+ # after editing either side, bring the other along and re-record with:
4
+ # pnpm run verify-translation-pairing --write packages/test-support/session-snapshot/README.md
5
+ README.md: 9f1907af8b9e50f587bcc05b93d8e3730c8f272c
6
+ README.zh.md: 2d4290578a655df91eb60f63177c97fc5d82da6c
package/README.md ADDED
@@ -0,0 +1,169 @@
1
+ ---
2
+ description: "Session-log snapshot support for keyless profile tests: manifests, identity redaction, normalization, workspace checks, and protocol adapters."
3
+ kind: "package-library"
4
+ ---
5
+
6
+ # @deepseek-ai/dsh-session-snapshot
7
+
8
+ English | [中文](README.zh.md)
9
+
10
+ ## Summary
11
+
12
+ `dsh-session-snapshot` provides the shared support behind keyless recorded-session tests (`pnpm run test:snapshot`): closed manifests, typed identity redaction, normalization, workspace comparison, fixture guards, and protocol adapters for headless, SDK, ACP, and Web owners. The ACP adapter launches the tested profile as a real subprocess, drives a deterministic input script, and registers the complete record, replay, and refresh suite. Every scenario owns enough committed evidence to prove model-visible output and filesystem effects without trusting the agent's report. The package entry imports vitest and is therefore available only inside a vitest run.
13
+
14
+ ## Table of Contents
15
+
16
+ - [Use this package](#use-this-package)
17
+ - [Understand the implementation](#understand-the-implementation)
18
+ - [Further Exploration](#further-exploration)
19
+ - [Model Experience](#model-experience)
20
+ - [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
21
+ - [Dev Note](#dev-note)
22
+
23
+ -----
24
+
25
+ <a id="use-this-package"></a>
26
+ ## Use this package
27
+
28
+ This package turns a shipped profile scenario into a keyless snapshot suite: write a scenario table and a fixtures directory, call the matching adapter once, and the kit owns launching or composing the profile, driving the scenario, comparing normalized output, and guarding the committed fixtures.
29
+
30
+ ### Writing a snapshot suite
31
+
32
+ A consuming `*.snapshot.ts` is the scenario table plus one factory call. `AgentUnderTest` supplies absolute `binScript`, optional `libBinScript`, `configPath`, and `tsconfigPath` paths, because the subprocess cwd sits outside the repository:
33
+
34
+ ```ts
35
+ import { dirname, join } from 'node:path'
36
+ import { fileURLToPath } from 'node:url'
37
+ import {
38
+ defineAcpSnapshotSuite,
39
+ type Scenario,
40
+ type SnapshotSuiteOptions,
41
+ } from '@deepseek-ai/dsh-session-snapshot'
42
+
43
+ function snapshotMode(value: string | undefined): SnapshotSuiteOptions['mode'] {
44
+ switch (value) {
45
+ case undefined:
46
+ case '':
47
+ case 'replay': return 'replay'
48
+ case 'record': return 'record'
49
+ case 'refresh': return 'refresh'
50
+ default: throw new Error(`unknown DSH_SNAPSHOT mode: ${value}`)
51
+ }
52
+ }
53
+
54
+ const SCENARIOS: Scenario[] = [
55
+ { name: 'text-turn', hasModelTurn: true, recorded: true, pinsHeader: true },
56
+ ]
57
+
58
+ defineAcpSnapshotSuite({
59
+ agent: { // absolute paths, resolved from the suite's own location
60
+ binScript: fileURLToPath(new URL('../../../apps/cli/src/bin.ts', import.meta.url)),
61
+ configPath: fileURLToPath(new URL('../cordis.yml', import.meta.url)),
62
+ profile: 'acp',
63
+ tsconfigPath: fileURLToPath(new URL('../../../tsconfig.json', import.meta.url)),
64
+ },
65
+ snapshotsDir: join(dirname(fileURLToPath(import.meta.url)), 'snapshots'),
66
+ scenarios: SCENARIOS, // exactly one entry per header class sets pinsHeader
67
+ mode: snapshotMode(process.env.DSH_SNAPSHOT),
68
+ })
69
+ ```
70
+
71
+ Each recorded-session directory carries a closed `snapshot.yml` manifest plus its `session.jsonl` and contiguous `session.<n>.jsonl` child logs. The manifest names the scenario, shipped profile, composition/header class, recording source, and only the replay, platform, permission, environment, workspace, or input facts the completed session cannot reconstruct. The adapter registers expected-output, session-log, and optional `workspace.expected/` comparisons; guards reject orphan directories, missing files, absolute paths, malformed manifests, and platform-specific separators.
72
+
73
+ `normalizeSessionSnapshot` retains the complete session header and event payloads but omits ordinary `seq`/`time` and packed-row `seq0`/`time0` envelopes from committed fixtures after normalizing paths and scrubbing request headers. Replay synthesizes the envelopes in memory, while runtime persistence continues to write complete logs. Fixtures use canonical packed rows; the [temporary repository migrator](../../../scripts/migrate-packed-session-fixtures.ts) (`pnpm run migrate:packed-session-fixtures`) rewrites older layouts, and its [removal proposal](../../../.agents/notes/proposed/process/2026-07-26-remove-packed-session-fixture-migrator.md) owns its deletion.
74
+
75
+ ### Record, replay, and refresh
76
+
77
+ `pnpm run test:snapshot:record` calls the live LLM and rewrites recorded model fixtures; `pnpm run test:snapshot:refresh` stays keyless, runs the replay overlay, and rewrites stdout, comparable session-log expected outputs, and owned prompt and tool-schema sidecars from committed model scripts. Each composition owner keeps its replay patch beside its live patch; top-level `snapshots/` owns session-driven scenarios, while other expected outputs stay beside their owning package. [`dsh-llm-replay`](../llm-replay/README.md) serves the recorded streams selected through `DSH_SNAPSHOT_*` environment values.
78
+
79
+ ### Pinning request headers
80
+
81
+ A pin owns its generated `system-prompt.expected.md` or `tool-schemas.expected.json` sidecar by default; `systemPromptSource` and `toolSchemasSource` name another pin when the complete corresponding sequence is identical, so each distinct version is committed once. The pin's `session.jsonl` stores `"system":"{{system}}","tools":"{{tools}}"` while retaining config, reason, and any model-visible prefix. A child session whose own scope composes a different request declares it per fixture index with `pinsChildToolSchemas` and `pinsChildSystemPrompts`. A scenario that changes the request header mid-run declares `expectedHeaderChanges`.
82
+
83
+ ### Platform and composition variants
84
+
85
+ A scenario requiring a non-Windows host declares `posixOnly`, which skips its run test on Windows while the fixture guards keep covering its committed files everywhere; a scenario whose composition needs a usable `pwsh` declares `pwshOnly`. `workspaceParent` moves the generated child cwd outside the platform temp directory when temporary-directory grants are themselves under test; a scenario's committed `workspace/` is copied into that child first, then `prepareWorkspace` runs against the generated cwd before the agent starts. Default generated workspaces are stored in session fixtures as `{{cwd}}`, so platform temp roots and random basenames do not affect recordings.
86
+
87
+ ### What can go wrong
88
+
89
+ - **A fixture guard rejects the committed files** — orphan scenario dirs, missing files, multiple pins for one header class, duplicate sidecar content, unscrubbed JSONL headers, and malformed pinning headers all fail the suite before comparisons run.
90
+ - **The session harvest needs raw JSONL mode** — snapshot configs set the JSONL backend's `compression: 'none'`; compressed JSONL and SQLite compositions have no snapshot-harvest path.
91
+ - **Built mode needs current artifacts** — run `pnpm run build` before selecting `DSH_EXAMPLE_MODE=lib`; source mode remains the zero-build path.
92
+
93
+ -----
94
+
95
+ <a id="understand-the-implementation"></a>
96
+ ## Understand the implementation
97
+
98
+ <details>
99
+ <summary>Implementation internals — click to expand</summary>
100
+
101
+ This section explains the design of the kit; the observable behavior is fully covered in [Use this package](#use-this-package).
102
+
103
+ ### Design
104
+
105
+ The shared core owns manifests, workspace setup/comparison, typed identity mapping, normalizers, and fixture invariants. The ACP adapter adds four composable layers: launcher, scenario harness, normalizers, and suite factory. `launchAcpTestAgent` boots a source profile under tsx or a built `lib` profile under plain Node, connects the SDK client over a raw-byte stdout tee, collects session updates and stderr, fails closed on unhandled permission requests, and owns shutdown. `runScenario` drives ACP JSON-RPC stdio and harvests every persisted raw JSONL session log. The pure normalizers replace cwd paths and typed identities with stable tokens, zero times, expand physical provenance ranges, and scrub request-header bulk. `defineAcpSnapshotSuite` registers comparisons, fixture write-back, and the live uniformity guard.
106
+
107
+ ### Source map
108
+
109
+ | File | Role |
110
+ |---|---|
111
+ | [`src/launcher.ts`](src/launcher.ts) | Subprocess/client launcher and shutdown ownership |
112
+ | [`src/harness.ts`](src/harness.ts) | Scripted scenario driver and session-log harvest |
113
+ | [`src/manifest.ts`](src/manifest.ts) | Closed `snapshot.yml` schema, collection, and ownership rules |
114
+ | [`src/identity.ts`](src/identity.ts) | Typed first-seen identity tokenization across parent and child logs |
115
+ | [`src/normalize.ts`](src/normalize.ts) | Pure normalizers and scrubbing helpers |
116
+ | [`src/workspace.ts`](src/workspace.ts) | Scenario workspace setup and complete expected-state comparison |
117
+ | [`src/suite.ts`](src/suite.ts) | Scenario-table suite factory, fixture guards, record/refresh write-back |
118
+ | [`src/index.ts`](src/index.ts) | Package entry re-exporting the four layers |
119
+ | [`src/invariant.ts`](src/invariant.ts) | Invariant companion (no runtime invariant; consuming test suites exercise the kit) |
120
+
121
+ ### Data flow
122
+
123
+ A scenario runs the agent under the launcher, feeds it the input script through the harness, and captures stdout plus the persisted logs. The normalizers canonicalize those captures — ids to first-seen sequence, generated cwd to `{{cwd}}`, header bulk to `{{system}}`/`{{tools}}` — so recorded and fresh runs compare structurally. The factory then compares normalized stdout and re-persisted logs against committed fixtures, or writes them back in record/refresh mode, and its guards reject malformed or drifting fixtures before any comparison result is trusted.
124
+
125
+ </details>
126
+
127
+ -----
128
+
129
+ <a id="further-exploration"></a>
130
+ ## Further Exploration
131
+
132
+ Read these pages when the package-level contract is not enough. They move from the snapshot kit to the model fixture source, the launch mechanics, and the policy that requires the tier.
133
+
134
+ - [llm-replay](../llm-replay/README.md) — the keyless model fixture source replay mode consumes.
135
+ - [loader-smoke](../loader-smoke/README.md) — the mode-aware subprocess launch mechanics the launcher builds on.
136
+ - [Testing policy](../../../docs/testing.md) — the keyless snapshot tier, when it is required, and the fixture ownership rules.
137
+ - [Test-support group map](../README.md) — sibling harnesses and support packages.
138
+
139
+ -----
140
+
141
+ <a id="model-experience"></a>
142
+ ## Model Experience
143
+
144
+ None, as this test-only support records, normalizes, and compares profile sessions without changing the agent's assembled model request.
145
+
146
+ #### KV Cache effect
147
+
148
+ None; this package neither assembles nor sends a provider request.
149
+
150
+ ## Known Limitations and Deferred Work
151
+
152
+ <a id="known-limitations-and-deferred-work"></a>
153
+
154
+
155
+ These limits define when the kit needs special care. They are current package constraints, not a task backlog.
156
+
157
+ - **Session harvest requires raw JSONL mode** — `runScenario` collects persisted `.jsonl` logs, so snapshot configs set the JSONL backend's `compression: 'none'`; compressed JSONL and SQLite compositions have no snapshot-harvest path.
158
+ - **Built mode requires current artifacts** — run `pnpm run build` before selecting `DSH_EXAMPLE_MODE=lib`; source mode remains the zero-build path.
159
+ - **ACP remains for protocol behavior** — cancellation and permission round trips whose stimulus is the ACP client stay on that adapter; assembled one-shot and persistent-control behavior uses headless and SDK adapters.
160
+
161
+ <a id="dev-note"></a>
162
+ ### Dev Note
163
+
164
+ <details>
165
+ <summary>Working context for maintainers — click to expand</summary>
166
+
167
+ None.
168
+
169
+ </details>
package/README.zh.md ADDED
@@ -0,0 +1,169 @@
1
+ ---
2
+ description: "面向无密钥 profile 测试的 session-log 快照支持:manifest、身份脱敏、规范化、workspace 检查与协议适配器。"
3
+ kind: "package-library"
4
+ ---
5
+
6
+ # @deepseek-ai/dsh-session-snapshot
7
+
8
+ [English](README.md) | 中文
9
+
10
+ ## 概述
11
+
12
+ `dsh-session-snapshot` 提供无密钥已记录会话测试(`pnpm run test:snapshot`)背后的共享支持:封闭 manifest、类型化身份脱敏、规范化、workspace 比较、fixture 保护,以及 headless、SDK、ACP 与 Web owner 使用的协议适配器。ACP 适配器以真实子进程启动被测 profile,驱动确定性输入脚本,并注册完整的录制、回放与刷新套件。每个场景都提交足够证据来证明模型可见输出与文件系统效果,不依赖 agent 自述。包入口会导入 vitest,因此只能在 vitest 运行中使用。
13
+
14
+ ## 目录
15
+
16
+ - [使用本包](#use-this-package)
17
+ - [理解实现](#understand-the-implementation)
18
+ - [进一步探索](#further-exploration)
19
+ - [模型体验](#model-experience)
20
+ - [已知限制与延期工作](#known-limitations-and-deferred-work)
21
+ - [开发备注](#dev-note)
22
+
23
+ -----
24
+
25
+ <a id="use-this-package"></a>
26
+ ## 使用本包
27
+
28
+ 本包把随附 profile 场景变成无密钥快照套件:写一张场景表和一个 fixture 目录,调用一次匹配的适配器,工具包就负责启动或组合 profile、驱动场景、比较规范化输出并守护已提交的 fixture。
29
+
30
+ ### 编写快照套件
31
+
32
+ 消费方 `*.snapshot.ts` 就是场景表加一次工厂调用。`AgentUnderTest` 提供绝对 `binScript`、可选 `libBinScript`、`configPath` 与 `tsconfigPath` 路径,因为子进程 cwd 位于仓库之外:
33
+
34
+ ```ts
35
+ import { dirname, join } from 'node:path'
36
+ import { fileURLToPath } from 'node:url'
37
+ import {
38
+ defineAcpSnapshotSuite,
39
+ type Scenario,
40
+ type SnapshotSuiteOptions,
41
+ } from '@deepseek-ai/dsh-session-snapshot'
42
+
43
+ function snapshotMode(value: string | undefined): SnapshotSuiteOptions['mode'] {
44
+ switch (value) {
45
+ case undefined:
46
+ case '':
47
+ case 'replay': return 'replay'
48
+ case 'record': return 'record'
49
+ case 'refresh': return 'refresh'
50
+ default: throw new Error(`unknown DSH_SNAPSHOT mode: ${value}`)
51
+ }
52
+ }
53
+
54
+ const SCENARIOS: Scenario[] = [
55
+ { name: 'text-turn', hasModelTurn: true, recorded: true, pinsHeader: true },
56
+ ]
57
+
58
+ defineAcpSnapshotSuite({
59
+ agent: { // absolute paths, resolved from the suite's own location
60
+ binScript: fileURLToPath(new URL('../../../apps/cli/src/bin.ts', import.meta.url)),
61
+ configPath: fileURLToPath(new URL('../cordis.yml', import.meta.url)),
62
+ profile: 'acp',
63
+ tsconfigPath: fileURLToPath(new URL('../../../tsconfig.json', import.meta.url)),
64
+ },
65
+ snapshotsDir: join(dirname(fileURLToPath(import.meta.url)), 'snapshots'),
66
+ scenarios: SCENARIOS, // exactly one entry per header class sets pinsHeader
67
+ mode: snapshotMode(process.env.DSH_SNAPSHOT),
68
+ })
69
+ ```
70
+
71
+ 每个已记录会话目录携带封闭的 `snapshot.yml` manifest,以及自身的 `session.jsonl` 与连续的 `session.<n>.jsonl` 子会话日志。manifest 指名场景、随附 profile、组合/header 类别、录制来源,以及已完成会话无法重建的 replay、平台、权限、环境、workspace 或输入事实。适配器注册预期输出、会话日志与可选 `workspace.expected/` 比较;保护会拒绝遗留目录、缺失文件、绝对路径、畸形 manifest 与平台专用分隔符。
72
+
73
+ `normalizeSessionSnapshot` 在规范化路径并清理 request header 后,会保留完整会话 header 与事件 payload,但从已提交 fixture 中省略普通行的 `seq`/`time` 和打包行的 `seq0`/`time0` envelope。回放只在内存中合成这些 envelope,而运行时持久化仍写入完整日志。fixture 使用规范打包行;[临时仓库迁移器](../../../scripts/migrate-packed-session-fixtures.ts)(`pnpm run migrate:packed-session-fixtures`)会改写较旧的布局,由其[移除提案](../../../.agents/notes/proposed/process/2026-07-26-remove-packed-session-fixture-migrator.zh.md)负责删除该迁移器。
74
+
75
+ ### 录制、回放与刷新
76
+
77
+ `pnpm run test:snapshot:record` 调用在线 LLM(大语言模型),并重写已录制的模型 fixture;`pnpm run test:snapshot:refresh` 保持无密钥,运行回放 overlay,并从已提交模型脚本重写 stdout、可比较会话日志预期输出,以及各 pin 自有的提示词与工具 schema 伴随文件。每个组合 owner 把 replay patch 放在 live patch 旁;顶层 `snapshots/` 拥有会话驱动场景,其他预期输出留在其包 owner 旁。[`dsh-llm-replay`](../llm-replay/README.zh.md) 提供通过 `DSH_SNAPSHOT_*` 环境值选择的已记录流。
78
+
79
+ ### 固定请求 header
80
+
81
+ 每个 pin 默认拥有其生成的 `system-prompt.expected.md` 或 `tool-schemas.expected.json` 伴随文件;当完整的对应序列相同时,`systemPromptSource` 与 `toolSchemasSource` 指定另一个 pin 作为来源,因此每个不同版本只提交一次。该 pin 的 `session.jsonl` 存储 `"system":"{{system}}","tools":"{{tools}}"`,同时保留配置、原因与任何模型可见前缀。自身作用域组合出不同请求的子会话按 fixture 索引以 `pinsChildToolSchemas` 与 `pinsChildSystemPrompts` 单独声明。运行中改变请求 header 的场景声明 `expectedHeaderChanges`。
82
+
83
+ ### 平台与组合变体
84
+
85
+ 需要非 Windows 主机的场景声明 `posixOnly`,在 Windows 上跳过运行测试,但 fixture 保护仍在所有平台覆盖其已提交文件;组合需要可用 `pwsh` 的场景声明 `pwshOnly`。当临时目录授权自身待测时,`workspaceParent` 将生成子级 cwd 移出平台临时区域;场景签入的 `workspace/` 会先复制到该子级,随后 `prepareWorkspace` 在 agent 启动前针对生成 cwd 运行。默认生成的 workspace 在会话 fixture 中存储为 `{{cwd}}`,使平台临时根目录与随机 basename 不影响录制。
86
+
87
+ ### 可能出什么问题
88
+
89
+ - **fixture 保护拒绝已提交文件**——遗留场景目录、缺失文件、一个 header 类别包含多个 pin、重复的伴随文件内容、未擦除的 JSONL header 与格式错误的 pin header 都会在比较运行前使套件失败。
90
+ - **会话收集需要原始 JSONL mode**——快照配置使用 JSONL 后端的 `compression: 'none'`;压缩 JSONL 与 SQLite 组合没有快照收集路径。
91
+ - **构建 mode 需要当前产物**——选择 `DSH_EXAMPLE_MODE=lib` 前先运行 `pnpm run build`;源 mode 仍是零构建路径。
92
+
93
+ -----
94
+
95
+ <a id="understand-the-implementation"></a>
96
+ ## 理解实现
97
+
98
+ <details>
99
+ <summary>实现细节——点击展开</summary>
100
+
101
+ 本节解释工具包的设计;可观察行为已在[使用本包](#use-this-package)中完整说明。
102
+
103
+ ### 设计
104
+
105
+ 共享核心拥有 manifest、workspace 设置/比较、类型化身份映射、规范化器与 fixture 不变式。ACP 适配器增加四个可组合层:启动器、场景 harness、规范化器与套件工厂。`launchAcpTestAgent` 在 tsx 下启动源码 profile,或在普通 Node 下启动已构建 `lib` profile,通过原始字节 stdout tee 连接 SDK 客户端,收集会话更新与 stderr,默认拒绝未处理的权限请求,并负责关闭。`runScenario` 驱动 ACP JSON-RPC stdio,并收集每个持久化原始 JSONL 会话日志。纯规范化器把 cwd 路径与类型化身份变为稳定 token,将时间归零、展开物理来源区间,并擦除请求 header bulk。`defineAcpSnapshotSuite` 注册比较、fixture 回写与实时一致性保护。
106
+
107
+ ### 源码地图
108
+
109
+ | 文件 | 职责 |
110
+ |---|---|
111
+ | [`src/launcher.ts`](src/launcher.ts) | 子进程/客户端启动器与关闭所有权 |
112
+ | [`src/harness.ts`](src/harness.ts) | 脚本化场景驱动与会话日志收集 |
113
+ | [`src/manifest.ts`](src/manifest.ts) | 封闭 `snapshot.yml` schema、收集与归属规则 |
114
+ | [`src/identity.ts`](src/identity.ts) | 跨父子日志的类型化首次出现身份 token 化 |
115
+ | [`src/normalize.ts`](src/normalize.ts) | 纯规范化器与擦除辅助 |
116
+ | [`src/workspace.ts`](src/workspace.ts) | 场景 workspace 设置与完整预期状态比较 |
117
+ | [`src/suite.ts`](src/suite.ts) | 场景表套件工厂、fixture 保护、录制/刷新回写 |
118
+ | [`src/index.ts`](src/index.ts) | 再导出四个层的包入口 |
119
+ | [`src/invariant.ts`](src/invariant.ts) | 不变式伴生插件(无运行时不变式;消费它的测试套件会检验该工具包) |
120
+
121
+ ### 数据流
122
+
123
+ 场景在启动器下运行 agent,通过 harness 向它喂入输入脚本,并捕获 stdout 与持久化日志。规范化器把捕获内容规范化——id 转为首次出现序列、生成 cwd 转为 `{{cwd}}`、header bulk 转为 `{{system}}`/`{{tools}}`——使已录制与本次运行可以结构化比较。随后工厂把规范化 stdout 与重新持久化日志同已提交 fixture 比较,或在录制/刷新模式下回写它们;其保护在任何比较结果被采信之前就拒绝畸形或漂移的 fixture。
124
+
125
+ </details>
126
+
127
+ -----
128
+
129
+ <a id="further-exploration"></a>
130
+ ## 进一步探索
131
+
132
+ 当包级约定不够用时阅读以下页面。它们从快照工具包逐步进入模型 fixture 来源、启动机制与要求该层级存在的策略。
133
+
134
+ - [llm-replay](../llm-replay/README.zh.md)——回放模式消费的无密钥模型 fixture 来源。
135
+ - [loader-smoke](../loader-smoke/README.zh.md)——启动器所依赖的模式感知子进程启动机制。
136
+ - [测试策略](../../../docs/testing.zh.md)——无密钥快照层、其适用时机与 fixture 归属规则。
137
+ - [test-support 组地图](../README.zh.md)——兄弟 harness 与支持包。
138
+
139
+ -----
140
+
141
+ <a id="model-experience"></a>
142
+ ## 模型体验
143
+
144
+ 无。该测试专用支持会记录、规范化并比较 profile 会话,不会改变 agent 组装的模型请求。
145
+
146
+ #### KV Cache 影响
147
+
148
+ 无;本包既不组装也不发送提供方请求。
149
+
150
+ ## 已知限制与延期工作
151
+
152
+ <a id="known-limitations-and-deferred-work"></a>
153
+
154
+
155
+ 这些限制说明何时需要对该工具包特别小心。它们是当前包约束,不是任务积压。
156
+
157
+ - **会话收集需要原始 JSONL mode**——`runScenario` 收集持久化 `.jsonl` 日志,因此快照配置使用 JSONL 后端的 `compression: 'none'`;压缩 JSONL 与 SQLite 组合没有快照收集路径。
158
+ - **构建 mode 需要当前产物**——选择 `DSH_EXAMPLE_MODE=lib` 前先运行 `pnpm run build`;源 mode 仍是零构建路径。
159
+ - **ACP 继续覆盖协议行为**——刺激来自 ACP 客户端的取消与权限往返留在该适配器;组装式一次性行为与持久控制行为使用 headless 与 SDK 适配器。
160
+
161
+ <a id="dev-note"></a>
162
+ ### 开发备注
163
+
164
+ <details>
165
+ <summary>维护者的工作上下文——点击展开</summary>
166
+
167
+ 无。
168
+
169
+ </details>