@deepseek-ai/dsh-loader-smoke 0.1.1-rc.2 → 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/README.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write packages/test-support/loader-smoke/README.md
5
- README.md: e5a33beb95f4e5940364cf309c8ea5fea60686f1
6
- README.zh.md: 32c7af5bef050d6d46e91783468bdd0aa66e5ca0
5
+ README.md: 7e080273fd4ac9cb2745f6c722d3ed17057af208
6
+ README.zh.md: 3973a0602a6451c64f4d145240a4e6f9a3a7f5d7
package/README.md CHANGED
@@ -1,15 +1,101 @@
1
- # `@deepseek-ai/dsh-loader-smoke`
1
+ ---
2
+ description: "Shared subprocess and direct-agent harness for keyless example smoke tests, for test authors booting real Loader compositions."
3
+ kind: "package-library"
4
+ ---
5
+
6
+ # @deepseek-ai/dsh-loader-smoke
2
7
 
3
8
  English | [中文](README.zh.md)
4
9
 
5
- Shared subprocess harness for tests that boot an app and `cordis.yml` through the Cordis Loader. `resolveExampleLaunch` selects local `src` mode (tsx and root tsconfig paths) or CI `lib` mode (plain Node and package exports) from an explicit mode or `DSH_EXAMPLE_MODE`.
10
+ ## Summary
11
+
12
+ `dsh-loader-smoke` runs a real application bin and its `cordis.yml` through the Cordis Loader inside an isolated temporary directory, capturing stdout and stderr, so a smoke test exercises the true composition path — plugin loading, service wiring, and the agent loop — rather than a hand-built test context. `runFixtureTurn` drives one task through the composition's single root agent and returns the final assistant text and accumulated token usage. The package also provides the mode-aware launch resolver (`src` under tsx for zero-build dev, built `lib` under plain Node for CI) shared by package-local subprocess harnesses. It is support-tier test infrastructure, not a product API.
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 boots an application fixture the way an installed consumer would and lets a test watch the result: choose source or built mode, launch the bin with its config from an isolated cwd, and either wait for a clean exit or drive one task through the root agent.
29
+
30
+ ### Booting an application fixture
31
+
32
+ `runLoaderSmoke` takes bin and config paths, optional complete bin arguments, environment overrides, stdin, pre-run setup, and pre-cleanup inspection. It owns the isolated cwd, DSH homes, diagnostics, deadline, termination, EOF, and cleanup, and returns both streams after a zero exit or rejects with both streams on failure:
33
+
34
+ ```text
35
+ const result = await runLoaderSmoke({
36
+ label: 'acp-agent',
37
+ tempDirPrefix: 'acp-smoke-',
38
+ binScript: '/abs/path/to/src/bin.ts',
39
+ configPath: '/abs/path/to/cordis.yml',
40
+ tsconfigPath: '/abs/path/to/tsconfig.json',
41
+ })
42
+ ```
43
+
44
+ Set `expectedExitCode` when the scenario pins a designed failure surface — a one-shot turn ending in an error result — and a run that exits any other way, including succeeding, still fails the smoke.
45
+
46
+ ### Driving a fixture turn
47
+
48
+ `runFixtureTurn(ctx, options)` drives one task through exactly one configured root agent: it waits for the task to reach the durable inbox, forwards canonical events to your observer, flushes the session, and returns the final assistant text plus accumulated usage. Example-local drivers keep configuration, rendering, and assertion ownership.
49
+
50
+ ### Source or built mode
51
+
52
+ `resolveExampleLaunch` picks the artifact an example bin boots from. `src` mode runs the bin under tsx with `TSX_TSCONFIG_PATH` set, so workspace imports resolve through the tsconfig `paths` map — the zero-build dev path. `lib` mode runs the built `lib/` bin under plain Node, so bare package plugins resolve through real package `exports`, exactly as an installed consumer resolves them. The mode comes from an explicit value or `DSH_EXAMPLE_MODE` (CI sets `lib`, dev leaves it unset); anything else fails loud.
53
+
54
+ ### What can go wrong
55
+
56
+ - **The process never exits** — the smoke enforces a deadline and reports the captured streams in the failure; a faulty fixture that spawns its own process tree can outlive the smoke and needs external cleanup.
57
+ - **Built mode needs a prior build** — run `pnpm run build` before selecting `DSH_EXAMPLE_MODE=lib`; the owning package manifest must also declare every package named by the config.
58
+ - **Captured output is bounded by execa's default 100 MB `maxBuffer`** — a runaway child is terminated at that ceiling rather than at a smoke-chosen budget.
59
+
60
+ -----
6
61
 
7
- `runLoaderSmoke` accepts bin and config paths, optional complete bin arguments, environment overrides, stdin, pre-run setup, and pre-cleanup inspection. It owns the isolated cwd, DSH homes, diagnostics, deadline, termination, EOF, and cleanup; it returns both streams after a zero exit and rejects with both streams on failure.
62
+ <a id="understand-the-implementation"></a>
63
+ ## Understand the implementation
8
64
 
9
- `runFixtureTurn` drives one task through exactly one configured root agent, forwards canonical events after that task reaches the durable inbox, flushes the session, and returns the final assistant text plus accumulated usage. Example-local drivers retain configuration, rendering, and assertion ownership.
65
+ <details>
66
+ <summary>Implementation internals — click to expand</summary>
10
67
 
11
- This is support-tier test infrastructure, not product API.
68
+ This section explains the design of the harness; the observable behavior is fully covered in [Use this package](#use-this-package).
12
69
 
70
+ ### Design
71
+
72
+ The harness is built on one separation: the smoke runs in a child process under an isolated world, and the test process only observes and asserts. `runLoaderSmoke` creates a temporary cwd, prepares world state there, spawns the resolved bin with isolated DSH homes (`DSH_HOME`, `DSH_AGENTS_HOME` under the temp cwd), closes stdin immediately, and awaits a clean exit within the deadline before inspecting and cleaning up on every outcome. `runFixtureTurn` stays in-process: it looks up the composition's single root agent, follows the task from its durable inbox receipt through whole-agent idle, sums per-step usage, and flushes the session before returning.
73
+
74
+ ### Source map
75
+
76
+ | File | Role |
77
+ |---|---|
78
+ | [`src/index.ts`](src/index.ts) | Mode resolver, `runLoaderSmoke` subprocess harness, options and result types |
79
+ | [`src/agent-turn.ts`](src/agent-turn.ts) | `runFixtureTurn` direct-agent driver and result envelope |
80
+ | [`src/invariant.ts`](src/invariant.ts) | Invariant companion (no runtime invariant; consuming test suites exercise the harness) |
81
+
82
+ </details>
83
+
84
+ -----
85
+
86
+ <a id="further-exploration"></a>
87
+ ## Further Exploration
88
+
89
+ Read these pages when the package-level contract is not enough. They move from the harness to the composition it boots and the fixtures it serves.
90
+
91
+ - [llm-replay](../llm-replay/README.md) — the keyless model fixture smoke compositions mount to run without a provider key.
92
+ - [Agent package](../../core/agent/README.md) — the root agent `runFixtureTurn` drives.
93
+ - [Testing policy](../../../docs/testing.md) — the keyless snapshot and smoke tiers.
94
+ - [Test-support group map](../README.md) — sibling harnesses and support packages.
95
+
96
+ -----
97
+
98
+ <a id="model-experience"></a>
13
99
  ## Model Experience
14
100
 
15
101
  None, as the test harness submits only the consuming test's ordinary user task and delegates prompt and tool composition to the loaded tree.
@@ -20,6 +106,21 @@ None beyond the loaded tree; the helper neither changes the request prefix nor r
20
106
 
21
107
  ## Known Limitations and Deferred Work
22
108
 
23
- - **Built mode requires a prior build** — the config must also resolve every named package upward through `examples/node_modules`.
109
+ <a id="known-limitations-and-deferred-work"></a>
110
+
111
+
112
+ These limits define when the harness needs special care. They are current package constraints, not a task backlog.
113
+
114
+ - **Built mode requires a prior build** — the owning package manifest must also declare every package named by the config.
24
115
  - **Captured stdout and stderr are bounded only by execa's default 100 MB `maxBuffer`** — a runaway child is terminated at that ceiling rather than at a smoke-chosen budget.
25
116
  - **Timeout kills only the direct child** — a process tree spawned by a faulty fixture can outlive the smoke and needs external cleanup.
117
+
118
+ <a id="dev-note"></a>
119
+ ### Dev Note
120
+
121
+ <details>
122
+ <summary>Working context for maintainers — click to expand</summary>
123
+
124
+ None.
125
+
126
+ </details>
package/README.zh.md CHANGED
@@ -1,25 +1,126 @@
1
- # `@deepseek-ai/dsh-loader-smoke`
1
+ ---
2
+ description: "面向无密钥示例冒烟测试的共享子进程与直接 agent harness,供测试作者启动真实 Loader 组合。"
3
+ kind: "package-library"
4
+ ---
5
+
6
+ # @deepseek-ai/dsh-loader-smoke
2
7
 
3
8
  [English](README.md) | 中文
4
9
 
5
- 用于测试通过 Cordis Loader 启动应用和 `cordis.yml` 的共享子进程 harness。`resolveExampleLaunch` 选择本地 `src` mode(tsx 和根 tsconfig 路径)或 CI `lib` mode(普通 Node 和包导出);选择依据为显式 mode 或 `DSH_EXAMPLE_MODE`。
10
+ ## 概述
11
+
12
+ `dsh-loader-smoke` 在隔离的临时目录中通过 Cordis Loader 运行真实的应用可执行文件及其 `cordis.yml`,捕获 stdout 与 stderr,使冒烟测试检验真实的组合路径——插件加载、服务接线与 agent loop(智能体循环)——而非手工搭建的测试上下文。`runFixtureTurn` 让一项任务通过组合中的唯一根 agent(智能体),并返回最终 assistant 文本与累计 token 用量。本包还为包内子进程 harness 提供共享的模式感知启动解析器(`src` 模式经 tsx,零构建开发路径;`lib` 模式经普通 Node 运行已构建产物,供 CI 使用)。它是支持层测试基础设施,而非产品 API。
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
+ 本包以已安装消费方的方式启动应用 fixture,并让测试观察结果:选择源模式或构建模式,从隔离 cwd 用其配置启动可执行文件,然后要么等待干净退出,要么让一项任务通过根 agent。
29
+
30
+ ### 启动应用 fixture
31
+
32
+ `runLoaderSmoke` 接受可执行文件与配置路径、可选的完整可执行文件参数、环境覆盖、标准输入、运行前准备与清理前检查。它负责隔离工作目录、DSH 主目录、诊断、截止时间、终止、EOF 与清理;进程以零状态退出后返回两个流,失败时则拒绝并附带两个流:
33
+
34
+ ```text
35
+ const result = await runLoaderSmoke({
36
+ label: 'acp-agent',
37
+ tempDirPrefix: 'acp-smoke-',
38
+ binScript: '/abs/path/to/src/bin.ts',
39
+ configPath: '/abs/path/to/cordis.yml',
40
+ tsconfigPath: '/abs/path/to/tsconfig.json',
41
+ })
42
+ ```
43
+
44
+ 当场景固定一个设计好的失败面——例如一次性轮次以错误结果结束——时设置 `expectedExitCode`;以任何其他方式退出(包括成功退出)都会使冒烟测试失败。
45
+
46
+ ### 驱动 fixture 轮次
47
+
48
+ `runFixtureTurn(ctx, options)` 让一项任务通过恰好一个已配置的根 agent:它等待任务进入持久收件箱,把规范事件转发给你的观察器,刷写会话,并返回最终 assistant 文本与累计用量。示例本地的 driver 继续负责配置、渲染与断言。
49
+
50
+ ### 源模式或构建模式
51
+
52
+ `resolveExampleLaunch` 选择示例可执行文件从哪个产物启动。`src` 模式在 tsx 下运行可执行文件并设置 `TSX_TSCONFIG_PATH`,使工作区导入通过 tsconfig `paths` 映射解析——这是零构建开发路径。`lib` 模式在普通 Node 下运行构建后的 `lib/` 可执行文件,使裸包插件通过真实包 `exports` 解析,与已安装消费方的解析方式完全一致。模式来自显式值或 `DSH_EXAMPLE_MODE`(CI 设置 `lib`,开发时保持未设置);其他任何值都会明确报错。
53
+
54
+ ### 可能出什么问题
55
+
56
+ - **进程永不退出**——冒烟测试强制执行截止时间,并在失败信息中报告捕获的流;生成自身进程树的有故障 fixture(测试前置数据)可能比冒烟测试存活更久,需要外部清理。
57
+ - **构建模式需要事先构建**——选择 `DSH_EXAMPLE_MODE=lib` 前先运行 `pnpm run build`;拥有该配置的包 manifest 还必须声明配置中点名的每个包。
58
+ - **捕获输出受 execa 默认 100 MB `maxBuffer` 约束**——失控子进程在该上限处被终止,而不是在冒烟测试自选的预算处。
59
+
60
+ -----
6
61
 
7
- `runLoaderSmoke` 接受可执行文件路径和配置路径、可选的完整可执行文件参数、环境变量覆盖、标准输入、运行前准备和清理前检查。它负责隔离工作目录、DSH 主目录、诊断、截止时间、终止、EOF 和清理;进程以零状态退出后返回两个流,失败时则返回拒绝并附带两个流。
62
+ <a id="understand-the-implementation"></a>
63
+ ## 理解实现
8
64
 
9
- `runFixtureTurn` 通过恰好一个已配置的根 agent(智能体)驱动一项任务,在该任务进入持久收件箱后转发规范事件,刷写会话,并返回最终 assistant 文本和累计用量。示例本地 driver 继续负责配置、渲染和断言。
65
+ <details>
66
+ <summary>实现细节——点击展开</summary>
10
67
 
11
- 这是支持层测试基础设施,而非产品 API。
68
+ 本节解释 harness 的设计;可观察行为已在[使用本包](#use-this-package)中完整说明。
12
69
 
70
+ ### 设计
71
+
72
+ harness 建立在一个分离之上:冒烟测试在隔离世界中的子进程里运行,测试进程只观察与断言。`runLoaderSmoke` 创建临时 cwd、在那里准备世界状态、以隔离的 DSH 主目录(临时 cwd 下的 `DSH_HOME`、`DSH_AGENTS_HOME`)spawn 解析出的可执行文件、立即关闭 stdin,并在截止时间内等待干净退出,然后在每种结果下都执行检查与清理。`runFixtureTurn` 停留在进程内:它查找组合中的唯一根 agent,跟踪任务从持久收件箱接收到整个 agent 完全停稳,汇总每步用量,并在返回前刷写会话。
73
+
74
+ ### 源码地图
75
+
76
+ | 文件 | 职责 |
77
+ |---|---|
78
+ | [`src/index.ts`](src/index.ts) | 模式解析器、`runLoaderSmoke` 子进程 harness、选项与结果类型 |
79
+ | [`src/agent-turn.ts`](src/agent-turn.ts) | `runFixtureTurn` 直接 agent driver 与结果信封 |
80
+ | [`src/invariant.ts`](src/invariant.ts) | 不变式伴生插件(无运行时不变式;消费它的测试套件会检验该 harness) |
81
+
82
+ </details>
83
+
84
+ -----
85
+
86
+ <a id="further-exploration"></a>
87
+ ## 进一步探索
88
+
89
+ 当包级约定不够用时阅读以下页面。它们从 harness 逐步进入它启动的组合以及它所服务的 fixture。
90
+
91
+ - [llm-replay](../llm-replay/README.zh.md)——冒烟组合为无密钥运行而挂载的模型 fixture 来源。
92
+ - [Agent 包](../../core/agent/README.zh.md)——`runFixtureTurn` 驱动的根 agent。
93
+ - [测试策略](../../../docs/testing.zh.md)——无密钥快照与冒烟层级。
94
+ - [test-support 组地图](../README.zh.md)——兄弟 harness 与支持包。
95
+
96
+ -----
97
+
98
+ <a id="model-experience"></a>
13
99
  ## 模型体验
14
100
 
15
- 无,因为测试 harness 仅提交调用方测试的普通用户任务,并将提示词和工具组装交由已加载的树负责。
101
+ 无,因为测试 harness 仅提交调用方测试的普通用户任务,并将提示词与工具组装交由已加载的树负责。
16
102
 
17
103
  #### KV Cache 影响
18
104
 
19
105
  除已加载树本身的影响外,无其他影响;该 helper 既不更改请求前缀,也不跨运行保留状态。
20
106
 
21
- ## 已知限制与暂缓事项
107
+ ## 已知限制与延期工作
108
+
109
+ <a id="known-limitations-and-deferred-work"></a>
110
+
111
+
112
+ 这些限制说明何时需要对该 harness 特别小心。它们是当前包约束,不是任务积压。
113
+
114
+ - **构建模式需要事先构建**——拥有该配置的包 manifest 还必须声明配置中点名的每个包。
115
+ - **捕获的 stdout 与 stderr 仅受 execa 默认 100 MB `maxBuffer` 约束**——失控子进程在该上限处被终止,而不是在冒烟测试自选的预算处。
116
+ - **超时只终止直接子进程**——有故障的 fixture spawn 的进程树可能比冒烟测试存活更久,需要外部清理。
117
+
118
+ <a id="dev-note"></a>
119
+ ### 开发备注
120
+
121
+ <details>
122
+ <summary>维护者的工作上下文——点击展开</summary>
123
+
124
+ 无。
22
125
 
23
- - **构建模式需要事先构建**:配置还必须能够通过 `examples/node_modules` 向上解析每个命名包。
24
- - **捕获的 stdout 和 stderr 仅受 execa 默认 100 MB `maxBuffer` 约束**:失控子进程会在该上限处被终止,而不是在冒烟测试自选的预算处。
25
- - **超时只终止直接子进程**:有故障的 fixture(测试前置数据)spawn 的进程树可能比冒烟测试存活更久,需要外部清理。
126
+ </details>
package/lib/index.js CHANGED
@@ -140,7 +140,7 @@ function resolveExampleLaunch(options) {
140
140
  const env = { ...options.env };
141
141
  if (mode === "src") {
142
142
  if (options.tsconfigPath === void 0) throw new Error("resolveExampleLaunch: 'src' mode needs tsconfigPath for the workspace paths map.");
143
- const tsxLoader = import.meta.resolve("tsx");
143
+ const tsxLoader = options.sourceImport === "tsx/esm" ? import.meta.resolve("tsx/esm") : import.meta.resolve("tsx");
144
144
  env.TSX_TSCONFIG_PATH = options.tsconfigPath;
145
145
  return {
146
146
  command: process.execPath,
@@ -167,7 +167,7 @@ function resolveExampleLaunch(options) {
167
167
  * @returns captured stdout and stderr after a zero exit.
168
168
  */
169
169
  async function runLoaderSmoke(options) {
170
- const cwd = await mkdtemp(join(tmpdir(), options.tempDirPrefix));
170
+ const cwd = await mkdtemp(join(options.tempDirParent ?? tmpdir(), options.tempDirPrefix));
171
171
  const processTimeoutMs = options.processTimeoutMs ?? DEFAULT_PROCESS_TIMEOUT_MS;
172
172
  try {
173
173
  await options.prepare?.(cwd);
@@ -37,6 +37,8 @@ export interface ExampleLaunchOptions {
37
37
  readonly mode?: ExampleMode;
38
38
  /** Absolute repo tsconfig whose `paths` map resolves unbuilt workspace imports. Required in `src` mode, ignored in `lib`. */
39
39
  readonly tsconfigPath?: string;
40
+ /** Select the ESM-only tsx hook instead of the generic loader. */
41
+ readonly sourceImport?: 'tsx/esm';
40
42
  /** Extra environment entries the mode-specific ones layer over; the caller then merges the result over `process.env`. */
41
43
  readonly env?: NodeJS.ProcessEnv;
42
44
  }
@@ -69,6 +71,8 @@ export interface LoaderSmokeOptions {
69
71
  readonly label: string;
70
72
  /** Prefix for the isolated temporary process cwd. */
71
73
  readonly tempDirPrefix: string;
74
+ /** Existing parent for the generated cwd; defaults to the platform temporary directory. */
75
+ readonly tempDirParent?: string;
72
76
  /** Absolute app-bin source path (`<pkg>/src/bin.ts`); the `lib` bin is derived from it. */
73
77
  readonly binScript: string;
74
78
  /** Explicit plain-Node entry for `lib` mode; intended for test fixtures outside a package `src/` tree. */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-loader-smoke",
3
3
  "description": "Shared subprocess and direct-agent harness for keyless real-Loader example smoke tests",
4
- "version": "0.1.1-rc.2",
4
+ "version": "0.1.2-alpha.2",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -36,17 +36,18 @@
36
36
  "tsx": "^4.22.4"
37
37
  },
38
38
  "peerDependencies": {
39
- "@deepseek-ai/dsh-agent": "^0.1.1-rc.2",
40
- "@deepseek-ai/dsh-session": "^0.1.1-rc.2",
41
- "@deepseek-ai/cordis": "^4.0.1",
42
- "@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
43
- "@deepseek-ai/dsh-llm": "^0.1.1-rc.2"
39
+ "@deepseek-ai/cordis": "^4.0.2",
40
+ "@deepseek-ai/dsh-agent": "^0.1.2-alpha.2",
41
+ "@deepseek-ai/dsh-llm": "^0.1.2-alpha.2",
42
+ "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.2",
43
+ "@deepseek-ai/dsh-session": "^0.1.2-alpha.2"
44
44
  },
45
45
  "devDependencies": {
46
- "@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
47
- "@deepseek-ai/dsh-llm": "^0.1.1-rc.2",
48
- "@deepseek-ai/dsh-session": "^0.1.1-rc.2",
49
- "@deepseek-ai/cordis": "^4.0.1",
50
- "@deepseek-ai/dsh-agent": "^0.1.1-rc.2"
46
+ "@deepseek-ai/dsh-agent": "^0.1.2-alpha.2",
47
+ "@deepseek-ai/dsh-app-boot": "^0.1.2-alpha.2",
48
+ "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.2",
49
+ "@deepseek-ai/cordis": "^4.0.2",
50
+ "@deepseek-ai/dsh-llm": "^0.1.2-alpha.2",
51
+ "@deepseek-ai/dsh-session": "^0.1.2-alpha.2"
51
52
  }
52
53
  }