diff --git a/docs/capability-seams.md b/docs/capability-seams.md
index 1aafbb2817..03239998b8 100644
--- a/docs/capability-seams.md
+++ b/docs/capability-seams.md
@@ -77,7 +77,6 @@ flowchart LR
pkg_subagent_spawn["subagent-spawn"]
pkg_subagent_fork["subagent-fork"]
pkg_subagent_acp["subagent-acp"]
- pkg_subagent_mock["subagent-mock"]
pkg_tasks["tasks"]
svc_tasks["ctx.tasks
Background task registry"]
pkg_tool_tasks["tool-tasks"]
@@ -129,7 +128,6 @@ flowchart LR
pkg_subagent --> svc_subagents
pkg_subagent_acp --> svc_subagents
pkg_subagent_fork --> svc_subagents
- pkg_subagent_mock --> svc_subagents
pkg_subagent_spawn --> svc_subagents
pkg_system_prompt --> svc_systemPrompt
pkg_tasks --> svc_tasks
@@ -224,7 +222,7 @@ flowchart LR
| `ctx.codeRuntime` | `seam` | [`code-runtime`](../packages/code-runtime/code-runtime) | [`code-runtime-worker`](../packages/code-runtime/code-runtime-worker) | [`tools`](../packages/core/tools) | - | Runs one model-written program against host-provided async bindings; backends differ by substrate and language (the tool registry consumes it for Code Mode). |
| `ctx.fs` | `seam` | [`fs`](../packages/fs/fs) | [`fs-local`](../packages/fs/fs-local) | [`tool-fs`](../packages/fs/tool-fs) | [`fs-policy`](../packages/fs/fs-policy) | tool-fs executes read/write/edit through ctx.fs; fs-policy contributes observed-state checks through the fs/* event gate. |
| `ctx.compact` | `seam` | [`compact`](../packages/compact/compact) | [`compact-basic`](../packages/compact/compact-basic) | [`compact-basic`](../packages/compact/compact-basic) | - | The basic backend currently consumes the pre-step event directly; a model-facing compact tool remains deferred. |
-| `ctx.subagents` | `seam` | [`subagent`](../packages/subagent/subagent) | [`subagent-spawn`](../packages/subagent/subagent-spawn), [`subagent-fork`](../packages/subagent/subagent-fork), [`subagent-acp`](../packages/subagent/subagent-acp), [`subagent-mock`](../packages/support/subagent-mock) | [`tool-subagent`](../packages/subagent/tool-subagent) | - | Providers implement transports; tool-subagent exposes one configured provider as a model-facing tool name. |
+| `ctx.subagents` | `seam` | [`subagent`](../packages/subagent/subagent) | [`subagent-spawn`](../packages/subagent/subagent-spawn), [`subagent-fork`](../packages/subagent/subagent-fork), [`subagent-acp`](../packages/subagent/subagent-acp) | [`tool-subagent`](../packages/subagent/tool-subagent) | - | Providers implement transports; tool-subagent exposes one configured provider as a model-facing tool name. |
| `ctx.tasks` | `core` | [`tasks`](../packages/tasks/tasks) | - | [`tool-bash`](../packages/bash/tool-bash), [`tool-subagent`](../packages/subagent/tool-subagent), [`tool-tasks`](../packages/tasks/tool-tasks) | - | Producers (tool-bash background commands, tool-subagent background delegations) register running work; tool-tasks is the model-facing control surface that reads, lists, and kills it. |
| `ctx.web` | `seam` | [`web`](../packages/web/web) | [`web-search-exa`](../packages/web/web-search-exa), [`web-search-perplexity`](../packages/web/web-search-perplexity), [`web-search-deepseek`](../packages/web/web-search-deepseek), [`web-fetch-local`](../packages/web/web-fetch-local) | [`tool-web`](../packages/web/tool-web) | - | Search and fetch providers register into one ctx.web seam; tool-web owns the stable model-facing names. |
| `ctx.spillStore` | `seam` | [`spill`](../packages/spill/spill) | [`spill-local`](../packages/spill/spill-local) | [`spill-policy`](../packages/spill/spill-policy) | - | The backend saves oversized tool text and returns a model-facing locator plus retrieval hint; spill-policy is the tools/post-execute consumer that decides when to spill. |
diff --git a/docs/config-catalog.md b/docs/config-catalog.md
index 30c448ba03..69704c2aa2 100644
--- a/docs/config-catalog.md
+++ b/docs/config-catalog.md
@@ -890,41 +890,6 @@ export interface Config {
Source: [`packages/subagent/subagent-fork/src/index.ts:25`](../packages/subagent/subagent-fork/src/index.ts)
-## `@deepseek-ai/dsh-subagent-mock`
-
-Requires: `subagents`
-
-```ts config-catalog
-/** Config for the mock provider; all optional with test-friendly defaults. */
-export interface Config {
- /** Registry name to register under. */
- name: string
- /** The text the scripted child "returns" as its final answer. */
- reply?: string
- /** The stop reason the run settles with. */
- stopReason?: SubagentStopReason
- /** Which start-time capabilities to advertise (default: all `true`). */
- capabilities?: Partial
- /**
- * The conversation-history descriptor to declare
- * ({@link SubagentProvider.inheritsParentContext}); default `false` (fresh
- * conversation). Set `true` to exercise seeded/fork wording in consumer
- * tests. This flag says nothing about tool, service, scope, or authority
- * inheritance.
- */
- inheritsParentContext?: boolean
- /**
- * Structured value surfaced when a request carries an `outputSchema` and the
- * `outputSchema` capability is on (default: `{ reply }`).
- */
- structured?: unknown
-}
-```
-
-Depends on: [`SubagentCapabilities`](../packages/subagent/subagent/src/index.ts) · [`SubagentStopReason`](../packages/subagent/subagent/src/index.ts)
-
-Source: [`packages/support/subagent-mock/src/index.ts:87`](../packages/support/subagent-mock/src/index.ts)
-
## `@deepseek-ai/dsh-subagent-spawn`
Requires: `subagents`
diff --git a/docs/module-graph.md b/docs/module-graph.md
index f49092f99d..afd4004673 100644
--- a/docs/module-graph.md
+++ b/docs/module-graph.md
@@ -100,7 +100,6 @@ flowchart TD
pkg_invariants["invariants"]
pkg_llm_replay["llm-replay"]
pkg_loader_smoke["loader-smoke"]
- pkg_subagent_mock["subagent-mock"]
end
subgraph group_ui["packages/ui"]
pkg_acp["acp"]
@@ -368,10 +367,6 @@ flowchart TD
pkg_hooks_claude --> pkg_session_persistence
pkg_hooks_claude --> pkg_subagent
pkg_hooks_claude --> pkg_tools
- pkg_subagent_mock --> pkg_agent
- pkg_subagent_mock --> pkg_llm
- pkg_subagent_mock --> pkg_session
- pkg_subagent_mock --> pkg_subagent
pkg_jsonrpc --> pkg_agent
pkg_jsonrpc --> pkg_llm
pkg_jsonrpc --> pkg_llm_deepseek
@@ -513,7 +508,6 @@ flowchart TD
| [`subagent-inprocess`](../packages/subagent/subagent-inprocess) | `subagent` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
| [`tool-subagent`](../packages/subagent/tool-subagent) | `subagent` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`subagent`](../packages/subagent/subagent), [`tasks`](../packages/tasks/tasks), [`tools`](../packages/core/tools) |
| [`hooks-claude`](../packages/hooks/hooks-claude) | `hooks` | [`agent`](../packages/core/agent), [`hook-protocol`](../packages/hooks/hook-protocol), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session-persistence/session-persistence), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools) |
-| [`subagent-mock`](../packages/support/subagent-mock) | `support` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent) |
| [`jsonrpc`](../packages/ui/jsonrpc) | `ui` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`llm-deepseek`](../packages/llm/llm-deepseek), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent) |
| [`stdio`](../packages/ui/stdio) | `ui` | [`agent`](../packages/core/agent), [`agent-loop`](../packages/core/agent-loop), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`user-interaction`](../packages/ui/user-interaction) |
| [`agent-spine-demo`](../packages/examples/agent-spine-demo) | `examples` | [`agent`](../packages/core/agent), [`agent-loop`](../packages/core/agent-loop), [`home`](../packages/util/home), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`skill`](../packages/skill/skill), [`skill-local`](../packages/skill/skill-local), [`system-prompt`](../packages/core/system-prompt), [`tasks`](../packages/tasks/tasks), [`tool-bash`](../packages/bash/tool-bash), [`tool-skill`](../packages/skill/tool-skill), [`tool-tasks`](../packages/tasks/tool-tasks), [`tools`](../packages/core/tools), [`workspace-context`](../packages/context/workspace-context) |
diff --git a/docs/rfc/INDEX.md b/docs/rfc/INDEX.md
index 48774d96d7..6bd83ad03a 100644
--- a/docs/rfc/INDEX.md
+++ b/docs/rfc/INDEX.md
@@ -22,7 +22,6 @@ Generated by `pnpm run gen-rfc-index` from the RFC tree — never edit by hand;
|---|---|
| [Prune dead public and result surface](proposed/simplification/2026-07-04-prune-dead-core-spine-surface.md) | 2026-07-04 |
| [Make JSON-RPC completion and transport directional](proposed/simplification/2026-07-19-make-jsonrpc-directional.md) | 2026-07-19 |
-| [Retire the standalone subagent mock package](proposed/simplification/2026-07-19-retire-subagent-mock-package.md) | 2026-07-19 |
### Architecture
@@ -116,6 +115,7 @@ Generated by `pnpm run gen-rfc-index` from the RFC tree — never edit by hand;
| [Drop unconsumed skill provider events](implemented/simplification/2026-07-12-drop-unconsumed-skill-provider-events.md) | 2026-07-12 |
| [Prune unused web seam fields](implemented/simplification/2026-07-12-prune-unused-web-seam-fields.md) | 2026-07-12 |
| [Simplify session-log representation](implemented/simplification/2026-07-12-simplify-session-log-representation.md) | 2026-07-12 |
+| [Retire the standalone subagent mock package](implemented/simplification/2026-07-19-retire-subagent-mock-package.md) | 2026-07-19 |
| [Use one surface manager per session](implemented/simplification/2026-07-19-use-one-session-surface-manager.md) | 2026-07-19 |
### Architecture
diff --git a/docs/rfc/implemented/feature/2026-06-21-subagent-capability-seam.md b/docs/rfc/implemented/feature/2026-06-21-subagent-capability-seam.md
index 11824bcf63..3f44940848 100644
--- a/docs/rfc/implemented/feature/2026-06-21-subagent-capability-seam.md
+++ b/docs/rfc/implemented/feature/2026-06-21-subagent-capability-seam.md
@@ -2,7 +2,7 @@
Status: implemented
-> The full seam is shipped: the `dsh-subagent` interface, the `dsh-subagent-mock` test backend, and the `dsh-tool-subagent` consumer; the two in-process backends (`dsh-subagent-spawn`, `dsh-subagent-fork`); the nested-agent snapshot infrastructure ([per-session snapshot replay](../testing/2026-06-22-subagent-snapshot-replay.md)); and the out-of-process `dsh-subagent-acp` backend ([its RFC](2026-06-22-acp-subagent-backend.md)).
+> The full seam is shipped: the `dsh-subagent` interface and `dsh-tool-subagent` consumer; the two in-process backends (`dsh-subagent-spawn`, `dsh-subagent-fork`); the nested-agent snapshot infrastructure ([per-session snapshot replay](../testing/2026-06-22-subagent-snapshot-replay.md)); and the out-of-process `dsh-subagent-acp` backend ([its RFC](2026-06-22-acp-subagent-backend.md)).
## Problem
@@ -32,7 +32,6 @@ A new package group `packages/subagent/`:
| `@deepseek-ai/dsh-subagent-spawn` | implementation: a fresh in-process child via `ctx.agents.create` |
| `@deepseek-ai/dsh-subagent-fork` | implementation: an in-process child seeded with a snapshot of the parent's log |
| `@deepseek-ai/dsh-subagent-acp` | implementation: an ACP client driving a configured child process |
-| `@deepseek-ai/dsh-subagent-mock` | support: a scripted provider for testing the seam through the real load path |
| `@deepseek-ai/dsh-tool-subagent` | consumer: the model-facing `subagent` tool over `ctx.subagents` |
### The primitive: async `start → SubagentRun`
@@ -62,7 +61,7 @@ Each subagent runs in its **own `Session`** (own id, `parentSession` lineage), p
## Testing
-The seam is tested through the real Cordis Loader/export path, which catches the export-shape failure described in [postmortem 0001](../../../postmortem/0001-acp-default-export-drops-inject.md). Registry tests cover reload safety, duplicate names, and start-time capability rejection; nested-agent scenarios replay keylessly through [per-session snapshot replay](../testing/2026-06-22-subagent-snapshot-replay.md); in-process backends also have real-loop unit tests and a with-key e2e.
+Registry and tool tests replace only the nondeterministic child boundary with a package-local scripted provider while exercising the real `SubagentService`, lifecycle, task integration, and model-facing tool. Provider and consumer export shapes retain their Loader regression coverage for the failure described in [postmortem 0001](../../../postmortem/0001-acp-default-export-drops-inject.md). Registry tests cover reload safety, duplicate names, and start-time capability rejection; nested-agent scenarios replay keylessly through [per-session snapshot replay](../testing/2026-06-22-subagent-snapshot-replay.md); in-process backends also have real-loop unit tests and a with-key e2e.
## Consequences
diff --git a/docs/rfc/proposed/simplification/2026-07-19-retire-subagent-mock-package.i18n.yaml b/docs/rfc/implemented/simplification/2026-07-19-retire-subagent-mock-package.i18n.yaml
similarity index 62%
rename from docs/rfc/proposed/simplification/2026-07-19-retire-subagent-mock-package.i18n.yaml
rename to docs/rfc/implemented/simplification/2026-07-19-retire-subagent-mock-package.i18n.yaml
index 37c3c8b508..b86d9405e0 100644
--- a/docs/rfc/proposed/simplification/2026-07-19-retire-subagent-mock-package.i18n.yaml
+++ b/docs/rfc/implemented/simplification/2026-07-19-retire-subagent-mock-package.i18n.yaml
@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
-2026-07-19-retire-subagent-mock-package.md: 2731d35448cbc4dc9db4c92579c35202a53eefdd
-2026-07-19-retire-subagent-mock-package.zh.md: 919c56502227157465277cc07c23fb7096763684
+2026-07-19-retire-subagent-mock-package.md: 47174d22eaf27c5a5509280793dd50d70c542d00
+2026-07-19-retire-subagent-mock-package.zh.md: bfad78e1912f9d5153196df3fea2494c88436a91
diff --git a/docs/rfc/implemented/simplification/2026-07-19-retire-subagent-mock-package.md b/docs/rfc/implemented/simplification/2026-07-19-retire-subagent-mock-package.md
new file mode 100644
index 0000000000..47174d22ea
--- /dev/null
+++ b/docs/rfc/implemented/simplification/2026-07-19-retire-subagent-mock-package.md
@@ -0,0 +1,34 @@
+# RFC: Retire the standalone subagent mock package
+
+Status: implemented
+
+English | [中文](2026-07-19-retire-subagent-mock-package.zh.md)
+
+## Problem
+
+`@deepseek-ai/dsh-subagent-mock` was a configurable test double packaged as a workspace plugin. Its only external consumers were the `tool-subagent` unit suite and the tool-catalog generator; no runtime package, example, snapshot configuration, or real provider loaded it.
+
+That narrow fixture carried a manifest, exports, peer and development dependencies, project references, package README obligations, Loader composition tests, module-graph membership, and documentation exceptions. The tool-catalog generator mounted it only to make production consumers register their schemas and never executed a child.
+
+## Decision
+
+The standalone package is deleted. Its scripted child behavior now lives in `packages/subagent/tool-subagent/tests/scripted-provider.ts`, where tests mount the real `SubagentService`, provider registry, tool implementation, and task runtime while replacing only the nondeterministic child boundary.
+
+The local fixture retains deterministic replies, structured results, stop reasons, cancellation before and after publication, conversation-inheritance descriptors, and effect-scoped disposal. Package-specific Schemastery and Loader-export tests disappear because the fixture is no longer a deployable plugin.
+
+The tool-catalog generator registers a minimal local `SubagentProvider` descriptor before mounting `ToolSubagent` or the workflow engine. The descriptor cannot start a child; it exists only to satisfy production load-time dependencies while harvesting schemas from the real consumers.
+
+Workspace project references, package dependencies, lockfile entries, graph metadata, support-package prose, config-catalog entries, and README gate exceptions no longer name the retired package.
+
+## Alternatives considered
+
+**Keep a reusable mock package for future tests.** Reuse never materialized outside one test file and one generator. A future second behavioral consumer can extract a shared fixture after its contract is known; pre-packaging it made test infrastructure look like a supported backend.
+
+**Generate subagent schemas without mounting production consumers.** Hand-constructing or importing schemas would weaken the catalog check that the real registry and tool composition expose the documented shape. A minimal provider descriptor preserves that check without carrying executable fake-backend behavior.
+
+## Consequences
+
+- The workspace has one fewer deployable package and no test-only node in the capability or module graphs.
+- `tool-subagent` tests retain foreground, background-task, lifecycle, cancellation, reply, stop-reason, and structured-result coverage through production services.
+- Tool-catalog output remains generated from production registrations and is byte-for-byte unchanged.
+- Runtime and example packages gain no dependency on test fixtures.
diff --git a/docs/rfc/implemented/simplification/2026-07-19-retire-subagent-mock-package.zh.md b/docs/rfc/implemented/simplification/2026-07-19-retire-subagent-mock-package.zh.md
new file mode 100644
index 0000000000..bfad78e191
--- /dev/null
+++ b/docs/rfc/implemented/simplification/2026-07-19-retire-subagent-mock-package.zh.md
@@ -0,0 +1,34 @@
+# RFC: 撤销独立的 subagent mock 包
+
+Status: implemented
+
+[English](2026-07-19-retire-subagent-mock-package.md) | 中文
+
+## 问题
+
+`@deepseek-ai/dsh-subagent-mock` 曾是一个以工作区插件形式发布的可配置测试替身。它仅有两个外部消费方:`tool-subagent` 单元测试和工具目录生成器;运行时包、示例、快照配置和真实提供方都不会加载它。
+
+这个用途狭窄的 fixture(测试前置数据)需要维护 manifest(元数据清单)、导出、对等依赖(peer dependency)与开发依赖、项目引用、包(package)README 契约、Loader 组合测试、模块图成员关系以及文档例外。工具目录生成器挂载它,只是为了让生产消费方注册 schema,并不会执行子 agent。
+
+## 决策
+
+删除独立包。脚本化子 agent 行为现位于 `packages/subagent/tool-subagent/tests/scripted-provider.ts`;测试挂载真实的 `SubagentService`、提供方注册表、工具实现和任务运行时,只替换具有不确定性的子 agent 边界。
+
+本地 fixture 保留确定性回复、结构化结果、停止原因、发布前后的取消、对话继承描述和作用域化的 dispose(资源释放)覆盖。由于 fixture 不再是可部署插件,删除包专用的 Schemastery 与 Loader 导出测试。
+
+工具目录生成器在挂载 `ToolSubagent` 或工作流引擎之前,注册一个最小本地 `SubagentProvider` 描述。该描述无法启动子 agent;它只用于满足生产消费方的加载时依赖,同时从真实消费方提取 schema。
+
+工作区项目引用、包依赖、锁文件条目、图元数据、支持包说明、配置目录条目和 README 门禁例外不再提及已撤销的包。
+
+## 备选方案
+
+**为未来测试保留可复用 mock 包。** 除一个测试文件和一个生成器外,复用需求始终没有出现。未来产生第二个行为消费方时,可以在共享契约明确后再提取 fixture;提前将其打包会使测试基础设施看起来像受支持的后端。
+
+**不挂载生产消费方,直接生成 subagent schema。** 手工构造或直接导入 schema,会削弱目录门禁对真实注册表与工具组合是否公开文档结构的校验。最小提供方描述能保留该校验,而无需携带可执行的虚假后端行为。
+
+## 影响
+
+- 工作区减少一个可部署包,能力图与模块图也不再包含测试专用节点。
+- `tool-subagent` 测试继续通过生产服务覆盖前台、后台任务、生命周期、取消、回复、停止原因和结构化结果。
+- 工具目录输出仍根据生产注册生成,并保持字节级一致。
+- 运行时包与示例包都不会依赖测试 fixture。
diff --git a/docs/rfc/proposed/simplification/2026-07-19-retire-subagent-mock-package.md b/docs/rfc/proposed/simplification/2026-07-19-retire-subagent-mock-package.md
deleted file mode 100644
index 2731d35448..0000000000
--- a/docs/rfc/proposed/simplification/2026-07-19-retire-subagent-mock-package.md
+++ /dev/null
@@ -1,35 +0,0 @@
-# RFC: Retire the standalone subagent mock package
-
-Status: proposed
-
-English | [中文](2026-07-19-retire-subagent-mock-package.zh.md)
-
-## Problem
-
-`@deepseek-ai/dsh-subagent-mock` is a configurable test double packaged as a workspace plugin. Its only external consumers are the `tool-subagent` unit suite and the tool-catalog generator. No runtime package, example, snapshot configuration, or real provider loads it.
-
-That narrow fixture carries a manifest, exports, peer and development dependencies, project references, package README obligations, Loader composition tests, module-graph membership, and documentation-gate exceptions. The tool-catalog generator mounts it only to make the real subagent tool register its schema; it never executes a child.
-
-## Proposal
-
-Delete `packages/support/subagent-mock`. Move the scripted provider behavior actually used by `tool-subagent` into a package-local test fixture while continuing to exercise the real `SubagentService`, provider registry, and tool implementation.
-
-Have the tool-catalog generator register the minimal provider descriptor required before mounting `ToolSubagent`. Remove the package references, manifest dependency, graph node, README allowlists, and mock-specific Loader tests.
-
-## Alternatives considered
-
-**Keep a reusable mock package for future tests.** Reuse has not materialized outside one test file and one generator. A future second consumer can extract a fixture once its shared contract is known; packaging all configurable reply, cancellation, result, and Loader behavior today makes test infrastructure look like a supported backend.
-
-**Generate the subagent schema without mounting the real tool.** Hand-constructing or importing the schema would weaken the catalog's check that the production registry and tool composition expose the documented shape. The generator should keep mounting the real service and tool with only the child boundary replaced.
-
-## Acceptance criteria
-
-- `packages/support/subagent-mock` and every workspace, graph, dependency, and documentation entry for it are removed.
-- `tool-subagent` tests retain every scripted reply, structured-result, cancellation, foreground/background, and task-integration case they currently exercise through the real service and tool.
-- Tool-catalog generation mounts the production subagent registry and tool with a minimal local provider and produces a byte-identical catalog.
-- No runtime or example package gains a dependency on test-only fixtures.
-- Focused subagent tests, catalog and graph generation, module-graph verification, build, hygiene, and the full pre-push suite pass.
-
-## Risks
-
-Relocating the fixture could accidentally replace too much production composition with a stub. The local fixture must implement only the nondeterministic child boundary; capability checks, lifecycle, task handling, and tool output remain under production code. Mock Loader and HMR coverage can disappear because no deployed composition consumes the package afterward.
diff --git a/docs/rfc/proposed/simplification/2026-07-19-retire-subagent-mock-package.zh.md b/docs/rfc/proposed/simplification/2026-07-19-retire-subagent-mock-package.zh.md
deleted file mode 100644
index 919c565022..0000000000
--- a/docs/rfc/proposed/simplification/2026-07-19-retire-subagent-mock-package.zh.md
+++ /dev/null
@@ -1,35 +0,0 @@
-# RFC: 撤销独立的 subagent mock 包
-
-Status: proposed
-
-[English](2026-07-19-retire-subagent-mock-package.md) | 中文
-
-## 问题
-
-`@deepseek-ai/dsh-subagent-mock` 是一个以工作区插件形式发布的可配置测试替身。它仅有两个外部消费方:`tool-subagent` 单元测试和工具目录生成器。运行时包、示例、快照配置和真实提供方都不会加载它。
-
-这个用途狭窄的 fixture(测试前置数据)需要维护 manifest(元数据清单)、导出、对等依赖(peer dependency)与开发依赖、项目引用、包(package)README 契约、Loader 组合测试、模块图成员关系以及文档门禁例外。工具目录生成器挂载它,只是为了让真实 subagent 工具注册 schema;生成器从不执行子 agent。
-
-## 提案
-
-删除 `packages/support/subagent-mock`。把 `tool-subagent` 实际使用的脚本化提供方行为移入该包的本地测试 fixture,同时继续测试真实的 `SubagentService`、提供方注册表和工具实现。
-
-工具目录生成器在挂载 `ToolSubagent` 前,只注册所需的最小提供方描述。删除该包的项目引用、manifest 依赖、图节点、README 允许列表和 mock 专用 Loader 测试。
-
-## 备选方案
-
-**为未来测试保留可复用 mock 包。** 除一个测试文件和一个生成器外,复用需求并未出现。未来产生第二个消费方时,可以在共享契约明确后再提取 fixture;当前把所有可配置回复、取消、结果与 Loader 行为打包,会使测试基础设施看起来像受支持的后端。
-
-**不挂载真实工具,直接生成 subagent schema。** 手工构造或直接导入 schema,会削弱目录生成器对生产注册表与工具组合是否公开文档结构的校验。生成器应继续挂载真实服务与工具,只替换不确定的子 agent 边界。
-
-## 验收标准
-
-- 删除 `packages/support/subagent-mock`,并移除其全部工作区、图、依赖和文档条目。
-- `tool-subagent` 测试保留当前通过真实服务与工具覆盖的全部脚本化回复、结构化结果、取消、前台与后台运行以及任务集成用例。
-- 工具目录生成器使用最小本地提供方挂载生产 subagent 注册表与工具,并生成字节级一致的目录。
-- 运行时包与示例包都不会新增对测试专用 fixture 的依赖。
-- 聚焦 subagent 测试、目录与图生成、模块图校验、构建、hygiene 和完整 pre-push 门禁全部通过。
-
-## 风险
-
-迁移 fixture 时,可能会误将过多生产组合替换成测试替身。本地 fixture 只能实现不确定的 subagent 边界;能力检查、生命周期、任务处理与工具输出仍由生产代码负责。由于之后不再有部署组合消费该包,可以删除 mock 的 Loader 与 HMR 覆盖。
diff --git a/docs/rfc/rejected/simplification/2026-07-04-prune-unimplemented-subagent-vocabulary.md b/docs/rfc/rejected/simplification/2026-07-04-prune-unimplemented-subagent-vocabulary.md
index 4c52d0b1a4..266a1d911d 100644
--- a/docs/rfc/rejected/simplification/2026-07-04-prune-unimplemented-subagent-vocabulary.md
+++ b/docs/rfc/rejected/simplification/2026-07-04-prune-unimplemented-subagent-vocabulary.md
@@ -1,19 +1,19 @@
# RFC: Prune the unimplemented subagent seam vocabulary
-Status: rejected — the deferred capability vocabulary (`outputSchema`/`structured`, `toolFilter`, `sendMessage`/`resume`) is intentionally reserved surface: the seam advertises the full intended contract ahead of its implementations by design, so providers and consumers grow into a stable shape rather than re-negotiating it per capability. The consumer-evidence analysis below stands as the record of what is currently unimplemented.
+Status: rejected — the deferred capability vocabulary (`outputSchema`/`structured`, `toolFilter`, `sendMessage`/`resume`) is intentionally reserved surface: the seam advertises the full intended contract ahead of its implementations by design, so providers and consumers grow into a stable shape rather than re-negotiating it per capability. The consumer-evidence analysis below records the decision-time state.
## Problem
The [subagent seam](../../implemented/feature/2026-06-21-subagent-capability-seam.md) shipped a two-tier capability design: start-time capability flags checked by the service, and optional runtime methods on `SubagentRun`. Three start-time features and both optional runtime methods have zero implementations and zero callers:
-- **`outputSchema`/`structured` and `toolFilter`** (`SubagentCapabilities`, `SubagentStartRequest`, `SubagentResult` in `packages/subagent/subagent/src/types.ts`): every real provider declares `outputSchema: false, toolFilter: false` (`packages/subagent/subagent-spawn/src/index.ts`, `packages/subagent/subagent-fork/src/index.ts`, `packages/subagent/subagent-acp/src/index.ts`); the sole production `ctx.subagents.start` caller (`packages/subagent/tool-subagent/src/index.ts`) builds `{ prompt, parent, signal?, agentOptions? }` and structurally cannot set either; `structured` is produced only by the test mock (`packages/support/subagent-mock`) for its own spec. The service's capability check carries two assert rows whose only exercisers are the rejection tests.
+- **`outputSchema`/`structured` and `toolFilter`** (`SubagentCapabilities`, `SubagentStartRequest`, `SubagentResult` in `packages/subagent/subagent/src/types.ts`): at the decision point, every real provider declared `outputSchema: false, toolFilter: false` (`packages/subagent/subagent-spawn/src/index.ts`, `packages/subagent/subagent-fork/src/index.ts`, `packages/subagent/subagent-acp/src/index.ts`); the sole production `ctx.subagents.start` caller (`packages/subagent/tool-subagent/src/index.ts`) built `{ prompt, parent, signal?, agentOptions? }` and structurally could not set either; `structured` appeared only in the scripted test fixture. The service's capability check carried two assert rows whose only exercisers were the rejection tests.
- **`SubagentRun.sendMessage` / `SubagentRun.resume`** (same file): implemented by NO provider — not even the mock; the spawn spec asserts their *absence*.
The only reason `dsh-subagent` depends on `dsh-tools` at all is `outputSchema`'s `SchemaSpec` type. Three subsequent subagent workstreams (per-session snapshot replay, the fork seed boundary, the ACP backend) landed around this surface without growing a single consumer.
## Proposal
-Remove `outputSchema`/`structured`, `toolFilter`, `sendMessage`, and `resume` from the seam; shrink `SubagentCapabilities` to `{ depthLimit }`; drop the two capability-assert rows, the all-false flags on the three providers, the mock's structured branch and its `capabilities`/`structured` config knobs, and the tests that exist to pin the removed surface (the two rejection rows, the spawn absence test, the mock structured specs). Drop the `dsh-tools` peer/dev dependency from `packages/subagent/subagent/package.json`. Update the [subagent.md](../../../core-data-structures/subagent.md) pastes and the type-equiv manifest, and the README rows in `packages/subagent/subagent`, `packages/subagent/subagent-spawn`, `packages/subagent/subagent-fork`, and `packages/support/subagent-mock`. The implementing PR amends the seam RFC's capability catalog per [implemented/AGENTS.md](../../implemented/AGENTS.md).
+Remove `outputSchema`/`structured`, `toolFilter`, `sendMessage`, and `resume` from the seam; shrink `SubagentCapabilities` to `{ depthLimit }`; drop the two capability-assert rows, the all-false flags on the three providers, the scripted fixture's structured branch and capability knobs, and the tests that exist to pin the removed surface. Drop the `dsh-tools` peer/dev dependency from `packages/subagent/subagent/package.json`. Update the [subagent.md](../../../core-data-structures/subagent.md) pastes and the type-equiv manifest, plus the affected provider READMEs. The implementing PR amends the seam RFC's capability catalog per [implemented/AGENTS.md](../../implemented/AGENTS.md).
**Keep** `depthLimit`/`maxDepth` and capability checks. The in-process backend enforces the limit, although the shipping tool does not yet set it. Recursion is a known seam risk, so the appropriate follow-up is to supply a tool default rather than delete working enforcement.
diff --git a/packages/subagent/README.md b/packages/subagent/README.md
index 62de4ffb08..9935233e85 100644
--- a/packages/subagent/README.md
+++ b/packages/subagent/README.md
@@ -12,6 +12,6 @@ The subagent seam: an agent delegating work to a child agent. Like the [bash](..
| `subagent-acp/` | Out-of-process backend: a child agent in a spawned subprocess, driven over ACP | (registers on `ctx.subagents`) |
| `tool-subagent/` | Model-facing `subagent` delegation tool over `ctx.subagents` | (registers on `ctx.tools`) |
-The interface lives at `subagent/subagent/`. The in-process `subagent-spawn` / `subagent-fork` backends share the `subagent-inprocess` driver (a library with no provider of its own — both depend on it, neither on the other), the out-of-process `subagent-acp` backend builds on the `subagent-subprocess` library (the credential env scrub, the dispose ladder, isolated config dirs) and ships alongside them here; the test-only `dsh-subagent-mock` (in [support](../support/README.md)) is separate. All **product** packages except the mock.
+The interface lives at `subagent/subagent/`. The in-process `subagent-spawn` / `subagent-fork` backends share the `subagent-inprocess` driver (a library with no provider of its own — both depend on it, neither on the other), and the out-of-process `subagent-acp` backend builds on the `subagent-subprocess` library (the credential env scrub, the dispose ladder, isolated config dirs). Tests replace only the child boundary with package-local fixtures.
The proposal and design rationale: [docs/rfc/implemented/feature/2026-06-21-subagent-capability-seam.md](../../docs/rfc/implemented/feature/2026-06-21-subagent-capability-seam.md).
diff --git a/packages/subagent/tool-subagent/package.json b/packages/subagent/tool-subagent/package.json
index c52f5a74dc..e2d4378743 100644
--- a/packages/subagent/tool-subagent/package.json
+++ b/packages/subagent/tool-subagent/package.json
@@ -37,7 +37,6 @@
"@deepseek-ai/dsh-agent": "workspace:^",
"@deepseek-ai/dsh-llm": "workspace:^",
"@deepseek-ai/dsh-subagent": "workspace:^",
- "@deepseek-ai/dsh-subagent-mock": "workspace:^",
"@deepseek-ai/dsh-system-prompt": "workspace:^",
"@deepseek-ai/dsh-tasks": "workspace:^",
"@deepseek-ai/dsh-tool-tasks": "workspace:^",
diff --git a/packages/subagent/tool-subagent/tests/scripted-provider.spec.ts b/packages/subagent/tool-subagent/tests/scripted-provider.spec.ts
new file mode 100644
index 0000000000..7365348410
--- /dev/null
+++ b/packages/subagent/tool-subagent/tests/scripted-provider.spec.ts
@@ -0,0 +1,98 @@
+import { describe, expect, it } from 'vitest'
+import { Context } from 'cordis'
+import { type Agent } from '@deepseek-ai/dsh-agent'
+import SubagentService, { type SubagentStartRequest } from '@deepseek-ai/dsh-subagent'
+import { SessionId } from '@deepseek-ai/dsh-session'
+import * as scripted from './scripted-provider.ts'
+
+/** A minimal parent; the scripted provider only reads its id. */
+function fakeParent(id = 'parent-1'): Agent {
+ return { id: SessionId(id) } as unknown as Agent
+}
+
+function baseRequest(over: Partial = {}): SubagentStartRequest {
+ return {
+ prompt: [{ type: 'text', text: 'task' }],
+ parent: fakeParent(),
+ signal: new AbortController().signal,
+ ...over,
+ }
+}
+
+async function mount(config: Partial = {}): Promise {
+ const ctx = new Context()
+ await ctx.plugin(SubagentService)
+ await scripted.mountScriptedProvider(ctx, { name: 'mock', ...config })
+ return ctx
+}
+
+describe('scripted subagent provider fixture', () => {
+ it('registers through the real service and returns the scripted reply', async () => {
+ const ctx = await mount({ reply: 'hello from fixture' })
+ expect(ctx.subagents.list()).toEqual(['mock'])
+
+ const run = await ctx.subagents.start('mock', baseRequest())
+ await expect(run.result).resolves.toEqual({
+ output: [{ type: 'text', text: 'hello from fixture' }],
+ structured: undefined,
+ stopReason: 'completed',
+ })
+ await run.dispose()
+ })
+
+ it('registers under a configurable name', async () => {
+ const ctx = await mount({ name: 'spawn' })
+ expect(ctx.subagents.list()).toEqual(['spawn'])
+ })
+
+ it('returns configured and default structured results', async () => {
+ const configured = await mount({ reply: 'r', structured: { answer: 42 } })
+ const schema = { type: 'object' as const, properties: { answer: { type: 'number' as const } } }
+ const configuredRun = await configured.subagents.start('mock', baseRequest({ outputSchema: schema }))
+ await expect(configuredRun.result).resolves.toMatchObject({ structured: { answer: 42 } })
+
+ const fallback = await mount({ reply: 'fallback reply' })
+ const fallbackRun = await fallback.subagents.start('mock', baseRequest({ outputSchema: schema }))
+ await expect(fallbackRun.result).resolves.toMatchObject({ structured: { reply: 'fallback reply' } })
+ })
+
+ it('omits structured output when no schema is requested', async () => {
+ const ctx = await mount({ capabilities: { outputSchema: false } })
+ const run = await ctx.subagents.start('mock', baseRequest())
+ expect(await run.result).not.toHaveProperty('structured')
+ })
+
+ it('honors configured and cancellation stop reasons', async () => {
+ const refused = await mount({ stopReason: 'refusal' })
+ const refusedRun = await refused.subagents.start('mock', baseRequest())
+ await expect(refusedRun.result).resolves.toMatchObject({ stopReason: 'refusal' })
+
+ const cancelled = await mount()
+ const controller = new AbortController()
+ const cancelledRun = await cancelled.subagents.start('mock', baseRequest({ signal: controller.signal }))
+ controller.abort()
+ await expect(cancelledRun.result).resolves.toMatchObject({ stopReason: 'aborted' })
+ })
+
+ it('rejects cancellation before or during asynchronous publication', async () => {
+ const ctx = await mount()
+ const alreadyAborted = new AbortController()
+ alreadyAborted.abort()
+ await expect(ctx.subagents.start('mock', baseRequest({ signal: alreadyAborted.signal })))
+ .rejects.toThrow('scripted subagent start aborted before publication')
+
+ const handoff = new AbortController()
+ const pending = ctx.subagents.start('mock', baseRequest({ signal: handoff.signal }))
+ handoff.abort()
+ await expect(pending).rejects.toThrow('scripted subagent start aborted before publication')
+ })
+
+ it('unregisters with its owning fixture fiber', async () => {
+ const ctx = new Context()
+ await ctx.plugin(SubagentService)
+ const fiber = await scripted.mountScriptedProvider(ctx, { name: 'mock' })
+ expect(ctx.subagents.list()).toEqual(['mock'])
+ await fiber.dispose()
+ expect(ctx.subagents.list()).toEqual([])
+ })
+})
diff --git a/packages/subagent/tool-subagent/tests/scripted-provider.ts b/packages/subagent/tool-subagent/tests/scripted-provider.ts
new file mode 100644
index 0000000000..01c0769cf8
--- /dev/null
+++ b/packages/subagent/tool-subagent/tests/scripted-provider.ts
@@ -0,0 +1,104 @@
+/** Package-local scripted child boundary for deterministic tool-subagent tests. */
+
+import type { Context } from 'cordis'
+import type { ContentBlock } from '@deepseek-ai/dsh-llm'
+import { SessionId } from '@deepseek-ai/dsh-session'
+import type {
+ SubagentCapabilities,
+ SubagentProvider,
+ SubagentResult,
+ SubagentRun,
+ SubagentStartRequest,
+ SubagentStopReason,
+} from '@deepseek-ai/dsh-subagent'
+
+const DEFAULT_CAPABILITIES: SubagentCapabilities = {
+ outputSchema: true,
+ depthLimit: true,
+ toolFilter: true,
+ persona: true,
+}
+
+/** Options for one scripted provider fixture. */
+export interface Config {
+ /** Registry name to register under. */
+ name: string
+ /** Final text returned by the scripted child. */
+ reply?: string
+ /** Terminal result reason. */
+ stopReason?: SubagentStopReason
+ /** Start-time features advertised by the provider. */
+ capabilities?: Partial
+ /** Whether tool descriptions say the child inherits completed turns. */
+ inheritsParentContext?: boolean
+ /** Structured value returned when the request asks for one. */
+ structured?: unknown
+}
+
+/** Scripted provider whose result aborts if its signal or disposer wins first. */
+class ScriptedSubagentProvider implements SubagentProvider {
+ readonly capabilities: SubagentCapabilities
+ readonly inheritsParentContext: boolean
+
+ constructor(
+ readonly name: string,
+ private readonly config: Config,
+ ) {
+ this.capabilities = { ...DEFAULT_CAPABILITIES, ...config.capabilities }
+ this.inheritsParentContext = config.inheritsParentContext ?? false
+ }
+
+ async start(request: SubagentStartRequest): Promise {
+ if (request.signal.aborted) throw new Error('scripted subagent start aborted before publication')
+ const reply = this.config.reply ?? 'scripted subagent reply'
+ const output: ContentBlock[] = [{ type: 'text', text: reply }]
+ const wantsStructured = request.outputSchema !== undefined && this.capabilities.outputSchema
+ const stopReason = this.config.stopReason ?? 'completed'
+ const state = { cancelled: false }
+ const onAbort = (): void => { state.cancelled = true }
+ request.signal.addEventListener('abort', onAbort, { once: true })
+ await Promise.resolve()
+ if (state.cancelled) {
+ request.signal.removeEventListener('abort', onAbort)
+ throw new Error('scripted subagent start aborted before publication')
+ }
+
+ const resultFor = (): SubagentResult => ({
+ output,
+ ...wantsStructured ? { structured: this.config.structured ?? { reply } } : {},
+ stopReason: state.cancelled ? 'aborted' : stopReason,
+ })
+ const result = new Promise((resolve) => {
+ setTimeout(() => { resolve(resultFor()) }, 0)
+ }).finally(() => {
+ request.signal.removeEventListener('abort', onAbort)
+ })
+
+ return {
+ id: SessionId(`scripted-subagent:${this.name}:${request.parent.id}`),
+ localAgent: undefined,
+ result,
+ dispose(): Promise {
+ state.cancelled = true
+ request.signal.removeEventListener('abort', onAbort)
+ return Promise.resolve()
+ },
+ }
+ }
+}
+
+/**
+ * Mount one scripted provider through an effect-scoped local plugin.
+ * @param ctx - context carrying the real subagent registry.
+ * @param config - scripted provider identity and outcome.
+ * @returns the fixture plugin's disposable fiber.
+ */
+export function mountScriptedProvider(ctx: Context, config: Config) {
+ return ctx.plugin({
+ name: 'scripted-subagent-provider',
+ inject: ['subagents'],
+ apply(pluginCtx: Context): void {
+ pluginCtx.subagents.registerProvider(new ScriptedSubagentProvider(config.name, config))
+ },
+ })
+}
diff --git a/packages/subagent/tool-subagent/tests/tool-subagent.spec.ts b/packages/subagent/tool-subagent/tests/tool-subagent.spec.ts
index 3bd42ae299..d88367ced5 100644
--- a/packages/subagent/tool-subagent/tests/tool-subagent.spec.ts
+++ b/packages/subagent/tool-subagent/tests/tool-subagent.spec.ts
@@ -9,18 +9,17 @@ import AgentRegistry from '@deepseek-ai/dsh-agent'
import SubagentService from '@deepseek-ai/dsh-subagent'
import TaskService from '@deepseek-ai/dsh-tasks'
import * as ToolTasks from '@deepseek-ai/dsh-tool-tasks'
-import * as mock from '@deepseek-ai/dsh-subagent-mock'
+import * as mock from './scripted-provider.ts'
import * as tool from '../src/index.ts'
import { runOutcome, settleRun } from '../src/index.ts'
import { SessionId } from '@deepseek-ai/dsh-session'
/**
* Drives the REAL plugin body: mounts `dsh-tool-subagent` on a real
- * `ToolRegistry` + `SubagentService`, with the real `dsh-subagent-mock` as the
- * backend, and invokes the registered `subagent` tool through
- * `ctx.tools.execute`. The mock is the genuine collaborator (we mock only the
- * "child agent", the expensive/non-deterministic boundary) — everything
- * downstream of the tool is the shipping code path.
+ * `ToolRegistry` + `SubagentService`, with a package-local scripted child
+ * boundary, and invokes the registered `subagent` tool through
+ * `ctx.tools.execute`. Everything downstream of the child boundary is the
+ * shipping code path.
*/
/** A minimal parent Agent — the tool reads `agent.id` for `parent`. */
@@ -33,7 +32,7 @@ async function setup(toolConfig: tool.Config, mockConfig: Partial =
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRegistry)
await ctx.plugin(SubagentService)
- await ctx.plugin(mock, { name: 'mock', ...mockConfig })
+ await mock.mountScriptedProvider(ctx, { name: 'mock', ...mockConfig })
await ctx.plugin(tool, toolConfig)
return ctx
}
@@ -131,8 +130,8 @@ describe('dsh-tool-subagent', () => {
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRegistry)
await ctx.plugin(SubagentService)
- await ctx.plugin(mock, { name: 'spawn', reply: 'from spawn' })
- await ctx.plugin(mock, { name: 'acp', reply: 'from acp' })
+ await mock.mountScriptedProvider(ctx, { name: 'spawn', reply: 'from spawn' })
+ await mock.mountScriptedProvider(ctx, { name: 'acp', reply: 'from acp' })
await ctx.plugin(tool, { provider: 'spawn', toolName: 'subagent' })
await ctx.plugin(tool, { provider: 'acp', toolName: 'subagent_acp' })
@@ -249,7 +248,7 @@ describe('dsh-tool-subagent', () => {
tool.apply(ctx, { provider: 'mock' })
expect(ctx.tools.schemas().some(s => s.name === 'subagent')).toBe(false)
// Backend arrives (as a delayed sibling fiber would): the tool appears.
- await ctx.plugin(mock, { name: 'mock', reply: 'late but fine' })
+ await mock.mountScriptedProvider(ctx, { name: 'mock', reply: 'late but fine' })
expect(ctx.tools.schemas().some(s => s.name === 'subagent')).toBe(true)
const result = await callSubagent(ctx, { description: 'd', prompt: 'p' })
expect(text(result)).toBe('late but fine')
@@ -260,7 +259,7 @@ describe('dsh-tool-subagent', () => {
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRegistry)
await ctx.plugin(SubagentService)
- const backend = await ctx.plugin(mock, { name: 'mock' }) // fresh conversation (descriptor: false)
+ const backend = await mock.mountScriptedProvider(ctx, { name: 'mock' }) // fresh conversation (descriptor: false)
await ctx.plugin(tool, { provider: 'mock' })
expect(ctx.tools.schemas().find(s => s.name === 'subagent')!.description).toContain('does not see this conversation')
@@ -270,7 +269,7 @@ describe('dsh-tool-subagent', () => {
// Backend reloads with a DIFFERENT conversation-history descriptor: the wording is re-derived
// from the fresh provider, not served stale from the first mount.
- await ctx.plugin(mock, { name: 'mock', inheritsParentContext: true })
+ await mock.mountScriptedProvider(ctx, { name: 'mock', inheritsParentContext: true })
expect(ctx.tools.schemas().find(s => s.name === 'subagent')!.description).toContain('inherits this conversation')
})
@@ -281,7 +280,7 @@ describe('dsh-tool-subagent', () => {
await ctx.plugin(SubagentService)
// Arm 1: a mounted tool dies with its plugin fiber; the provider survives.
- await ctx.plugin(mock, { name: 'mock' })
+ await mock.mountScriptedProvider(ctx, { name: 'mock' })
const mounted = await ctx.plugin(tool, { provider: 'mock' })
expect(ctx.tools.schemas().some(s => s.name === 'subagent')).toBe(true)
await mounted.dispose()
@@ -293,7 +292,7 @@ describe('dsh-tool-subagent', () => {
// live plugin owns (the zombie mount).
const waiting = await ctx.plugin(tool, { provider: 'later', toolName: 'subagent_later' })
await waiting.dispose()
- await ctx.plugin(mock, { name: 'later' })
+ await mock.mountScriptedProvider(ctx, { name: 'later' })
expect(ctx.tools.schemas().some(s => s.name === 'subagent_later')).toBe(false)
})
@@ -302,11 +301,11 @@ describe('dsh-tool-subagent', () => {
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRegistry)
await ctx.plugin(SubagentService)
- await ctx.plugin(mock, { name: 'mock' })
+ await mock.mountScriptedProvider(ctx, { name: 'mock' })
await ctx.plugin(tool, { provider: 'mock' })
// An unrelated provider registering (added-event with another name) and
// unregistering (removed-event with another name) must not touch the tool.
- const other = await ctx.plugin(mock, { name: 'other', inheritsParentContext: true })
+ const other = await mock.mountScriptedProvider(ctx, { name: 'other', inheritsParentContext: true })
expect(ctx.tools.schemas().filter(s => s.name === 'subagent')).toHaveLength(1)
expect(ctx.tools.schemas().find(s => s.name === 'subagent')!.description).toContain('does not see this conversation')
await other.dispose()
diff --git a/packages/support/README.md b/packages/support/README.md
index 0d991f07cf..433d6b3cdb 100644
--- a/packages/support/README.md
+++ b/packages/support/README.md
@@ -9,6 +9,5 @@ Packages that exist to serve development, testing, and the examples rather than
| `invariants/` | Runtime event-contract assertions for development diagnostics | (listens on `session/*`, `agent/*`) |
| `loader-smoke/` | Shared real-Loader subprocess harness for keyless example smokes | (library — imported by example e2e suites) |
| `llm-replay/` | Record/replay adapter: short-circuits `llm/stream` from a recorded session JSONL (keyless snapshot tests) | (listens on `llm/stream`) |
-| `subagent-mock/` | Scripted `SubagentProvider` for deterministic seam/tool tests | (registers on `ctx.subagents`) |
-`invariants` is development support but has no environment guard: it runs wherever registered, and the default `dsh-agent-spine-demo` bundle mounts it unconditionally. `agent-loop-testkit` centralizes the mandatory service spine for hand-built AgentLoop tests without owning their loop or scenario. `llm-replay` backs the demos and the snapshot test tier under the per-file coverage gate. `acp-snapshot` carries the ACP subprocess/client boundary plus the snapshot harness, normalizers, and suite machinery, while `loader-smoke` owns the parallel stdio/Loader process boundary used by keyless example e2e suites. `subagent-mock` exercises the real `ctx.subagents` load path without a model or child agent. A package graduates OUT of `support/` into a product group only when it gains documented product consumers.
+`invariants` is development support but has no environment guard: it runs wherever registered, and the default `dsh-agent-spine-demo` bundle mounts it unconditionally. `agent-loop-testkit` centralizes the mandatory service spine for hand-built AgentLoop tests without owning their loop or scenario. `llm-replay` backs the demos and the snapshot test tier under the per-file coverage gate. `acp-snapshot` carries the ACP subprocess/client boundary plus the snapshot harness, normalizers, and suite machinery, while `loader-smoke` owns the parallel stdio/Loader process boundary used by keyless example e2e suites. A package graduates OUT of `support/` into a product group only when it gains documented product consumers.
diff --git a/packages/support/subagent-mock/README.md b/packages/support/subagent-mock/README.md
deleted file mode 100644
index a879433b67..0000000000
--- a/packages/support/subagent-mock/README.md
+++ /dev/null
@@ -1,29 +0,0 @@
-# @deepseek-ai/dsh-subagent-mock
-
-A scripted `SubagentProvider` for testing the [subagent seam](../../subagent/subagent/README.md) without a model or a real child agent — the subagent analog of [`dsh-llm-replay`](../llm-replay/README.md).
-
-It lets a test drive `ctx.subagents` and the model-facing `dsh-tool-subagent` through the real Cordis loader/export path, exercising provider registration, async start, start-time capability validation, required-signal cancellation, `result`, `dispose`, and structured output deterministically and keylessly.
-
-## Usage
-
-Load it as a plugin (functional shape: `name`/`inject`/`Config`/`apply`, no default). Config (all optional):
-
-| Key | Default | Meaning |
-|---|---|---|
-| `name` | `mock` | Registry name to register the provider under. |
-| `reply` | `mock subagent reply` | The scripted child's final answer text. |
-| `stopReason` | `completed` | The stop reason `result` settles with. |
-| `capabilities` | all `true` | Which start-time capabilities (`outputSchema`, `depthLimit`, `toolFilter`, and `persona`) the provider advertises. |
-| `inheritsParentContext` | `false` | Conversation-history descriptor: `false` means fresh, while `true` exercises seeded/fork wording. It says nothing about tool, service, scope, or authority inheritance. |
-| `structured` | `{ reply }` | Structured value surfaced when a request carries an `outputSchema` and the capability is on. |
-
-Aborting the required request signal or disposing before `result` settles flips the stop reason to `aborted`, so both holder-facing cancellation paths are observable.
-
-## Model Experience
-
-Indirectly, through `dsh-tool-subagent`, which renders this test provider's configured reply or stop-reason error into the parent test history.
-
-## Known Limitations and Deferred Work
-
-- **Scripted provider only** — it does not run a model, create a child agent, or exercise real prompt/tool-loop behavior.
-- **One synthetic outcome per run** — it models no multi-turn, streaming, steering, resume, or subprocess transport behavior.
diff --git a/packages/support/subagent-mock/package.json b/packages/support/subagent-mock/package.json
deleted file mode 100644
index c9d0982f55..0000000000
--- a/packages/support/subagent-mock/package.json
+++ /dev/null
@@ -1,42 +0,0 @@
-{
- "name": "@deepseek-ai/dsh-subagent-mock",
- "description": "Scripted subagent provider for testing the subagent seam (keyless, deterministic)",
- "version": "0.0.1",
- "private": true,
- "type": "module",
- "main": "lib/index.js",
- "types": "lib/types/index.d.ts",
- "exports": {
- ".": {
- "types": "./lib/types/index.d.ts",
- "default": "./lib/index.js"
- },
- "./src/*": "./src/*",
- "./package.json": "./package.json"
- },
- "files": [
- "lib/index.js",
- "lib/types/**/*.d.ts",
- "lib/types/**/*.d.ts.map",
- "src"
- ],
- "license": "BSD-3-Clause",
- "peerDependencies": {
- "@deepseek-ai/dsh-agent": "^0.0.1",
- "@deepseek-ai/dsh-llm": "^0.0.1",
- "@deepseek-ai/dsh-session": "^0.0.1",
- "@deepseek-ai/dsh-subagent": "^0.0.1",
- "cordis": "^4.0.0-rc.7"
- },
- "dependencies": {
- "schemastery": "^3.18.0"
- },
- "devDependencies": {
- "@deepseek-ai/dsh-agent": "workspace:^",
- "@deepseek-ai/dsh-llm": "workspace:^",
- "@deepseek-ai/dsh-session": "workspace:^",
- "@deepseek-ai/dsh-subagent": "workspace:^",
- "@cordisjs/plugin-loader": "^1.0.0-rc.5",
- "cordis": "^4.0.0-rc.7"
- }
-}
diff --git a/packages/support/subagent-mock/src/index.ts b/packages/support/subagent-mock/src/index.ts
deleted file mode 100644
index 4ea30d166a..0000000000
--- a/packages/support/subagent-mock/src/index.ts
+++ /dev/null
@@ -1,127 +0,0 @@
-/**
- * Scripted, model-free subagent provider for deterministic coverage of registration,
- * capability checks, lifecycle, the model-facing tool, and structured results through the real
- * loader path. It is a named-export functional plugin; no default export.
- * @module @deepseek-ai/dsh-subagent-mock
- */
-
-import type { Context } from 'cordis'
-import z from 'schemastery'
-import type { ContentBlock } from '@deepseek-ai/dsh-llm'
-import { SessionId } from '@deepseek-ai/dsh-session'
-import type {
- SubagentCapabilities,
- SubagentProvider,
- SubagentResult,
- SubagentRun,
- SubagentStartRequest,
- SubagentStopReason,
-} from '@deepseek-ai/dsh-subagent'
-
-const STOP_REASONS = ['completed', 'aborted', 'error', 'max-tokens', 'refusal'] as const
-
-const DEFAULT_CAPS: SubagentCapabilities = { outputSchema: true, depthLimit: true, toolFilter: true, persona: true }
-
-/** Scripted provider whose configured result aborts if disposed or signalled first. */
-class MockSubagentProvider implements SubagentProvider {
- readonly capabilities: SubagentCapabilities
- readonly inheritsParentContext: boolean
-
- constructor(
- readonly name: string,
- private readonly config: Config,
- ) {
- this.capabilities = { ...DEFAULT_CAPS, ...config.capabilities }
- this.inheritsParentContext = config.inheritsParentContext ?? false
- }
-
- async start(request: SubagentStartRequest): Promise {
- if (request.signal.aborted) throw new Error('mock subagent start aborted before publication')
- const reply = this.config.reply ?? 'mock subagent reply'
- const output: ContentBlock[] = [{ type: 'text', text: reply }]
- const wantsStructured = request.outputSchema !== undefined && this.capabilities.outputSchema
- const baseStop: SubagentStopReason = this.config.stopReason ?? 'completed'
- const flags = { cancelled: false }
- const onAbort = (): void => { flags.cancelled = true }
- request.signal.addEventListener('abort', onAbort, { once: true })
- // Make publication genuinely asynchronous so a same-turn abort is still
- // a provider-owned startup failure rather than a returned live run.
- await Promise.resolve()
- if (flags.cancelled) {
- request.signal.removeEventListener('abort', onAbort)
- throw new Error('mock subagent start aborted before publication')
- }
-
- // A deterministic child id derived from the parent — no clock/random (both
- // banned in deterministic paths here, and unnecessary for a scripted run).
- const id = SessionId(`mock-subagent:${this.name}:${request.parent.id}`)
-
- const resultFor = (): SubagentResult => ({
- output,
- ...wantsStructured ? { structured: this.config.structured ?? { reply } } : {},
- stopReason: flags.cancelled ? 'aborted' : baseStop,
- })
-
- const result = new Promise((resolve) => {
- setTimeout(() => { resolve(resultFor()) }, 0)
- }).finally(() => {
- request.signal.removeEventListener('abort', onAbort)
- })
- return {
- id,
- localAgent: undefined,
- result,
- dispose(): Promise {
- flags.cancelled = true
- request.signal.removeEventListener('abort', onAbort)
- return Promise.resolve()
- },
- }
- }
-}
-
-export const name = 'subagent-mock'
-export const inject = ['subagents']
-
-/** Config for the mock provider; all optional with test-friendly defaults. */
-export interface Config {
- /** Registry name to register under. */
- name: string
- /** The text the scripted child "returns" as its final answer. */
- reply?: string
- /** The stop reason the run settles with. */
- stopReason?: SubagentStopReason
- /** Which start-time capabilities to advertise (default: all `true`). */
- capabilities?: Partial
- /**
- * The conversation-history descriptor to declare
- * ({@link SubagentProvider.inheritsParentContext}); default `false` (fresh
- * conversation). Set `true` to exercise seeded/fork wording in consumer
- * tests. This flag says nothing about tool, service, scope, or authority
- * inheritance.
- */
- inheritsParentContext?: boolean
- /**
- * Structured value surfaced when a request carries an `outputSchema` and the
- * `outputSchema` capability is on (default: `{ reply }`).
- */
- structured?: unknown
-}
-
-export const Config: z = z.object({
- name: z.string().default('mock'),
- reply: z.string(),
- stopReason: z.union(STOP_REASONS),
- capabilities: z.object({
- outputSchema: z.boolean(),
- depthLimit: z.boolean(),
- toolFilter: z.boolean(),
- persona: z.boolean(),
- }),
- inheritsParentContext: z.boolean(),
- structured: z.any(),
-})
-
-export function apply(ctx: Context, config: Config): void {
- ctx.subagents.registerProvider(new MockSubagentProvider(config.name, config))
-}
diff --git a/packages/support/subagent-mock/tests/subagent-mock.spec.ts b/packages/support/subagent-mock/tests/subagent-mock.spec.ts
deleted file mode 100644
index aec0fd7db1..0000000000
--- a/packages/support/subagent-mock/tests/subagent-mock.spec.ts
+++ /dev/null
@@ -1,122 +0,0 @@
-import { describe, expect, it } from 'vitest'
-import { Context } from 'cordis'
-import Loader from '@cordisjs/plugin-loader'
-import { type Agent } from '@deepseek-ai/dsh-agent'
-
-import SubagentService, { type SubagentStartRequest } from '@deepseek-ai/dsh-subagent'
-import * as mock from '../src/index.ts'
-import { SessionId } from '@deepseek-ai/dsh-session'
-
-/** A minimal parent — the mock provider only reads `parent.id`. */
-function fakeParent(id = 'parent-1'): Agent {
- return { id: SessionId(id) } as unknown as Agent
-}
-
-function baseRequest(over: Partial = {}): SubagentStartRequest {
- return { prompt: [{ type: 'text', text: 'task' }], parent: fakeParent(), signal: new AbortController().signal, ...over }
-}
-
-async function mount(config: Partial = {}): Promise {
- const ctx = new Context()
- await ctx.plugin(SubagentService)
- await ctx.plugin(mock, { name: 'mock', ...config })
- return ctx
-}
-
-describe('dsh-subagent-mock', () => {
- it('registers a provider on ctx.subagents and returns the scripted reply', async () => {
- const ctx = await mount({ reply: 'hello from mock' })
- expect(ctx.subagents.list()).toEqual(['mock'])
-
- const run = await ctx.subagents.start('mock', baseRequest())
- await expect(run.result).resolves.toEqual({
- output: [{ type: 'text', text: 'hello from mock' }],
- structured: undefined,
- stopReason: 'completed',
- })
- await run.dispose()
- })
-
- it('registers under a configurable name', async () => {
- const ctx = await mount({ name: 'spawn' })
- expect(ctx.subagents.list()).toEqual(['spawn'])
- })
-
- it('surfaces a structured result when the request carries an outputSchema', async () => {
- const ctx = await mount({ reply: 'r', structured: { answer: 42 } })
- const run = await ctx.subagents.start('mock', baseRequest({ outputSchema: { type: 'object', properties: { answer: { type: 'number' } } } }))
- await expect(run.result).resolves.toMatchObject({ structured: { answer: 42 } })
- })
-
- it('defaults structured output to { reply } when outputSchema is requested but no structured value is configured', async () => {
- const ctx = await mount({ reply: 'fallback reply' })
- const run = await ctx.subagents.start('mock', baseRequest({ outputSchema: { type: 'object', properties: { answer: { type: 'number' } } } }))
- await expect(run.result).resolves.toMatchObject({ structured: { reply: 'fallback reply' } })
- })
-
- it('omits structured output when outputSchema capability is off', async () => {
- const ctx = await mount({ capabilities: { outputSchema: false } })
- // The service rejects an outputSchema request against a no-cap provider, so
- // the structured path is only reachable when the cap is on; with it off and
- // no schema requested, the result has no structured field.
- const run = await ctx.subagents.start('mock', baseRequest())
- const result = await run.result
- expect(result).not.toHaveProperty('structured')
- })
-
- it('honors a configured stop reason', async () => {
- const ctx = await mount({ stopReason: 'refusal' })
- const run = await ctx.subagents.start('mock', baseRequest())
- await expect(run.result).resolves.toMatchObject({ stopReason: 'refusal' })
- })
-
- it('flips the stop reason to aborted when the signal fires before the result settles', async () => {
- const ctx = await mount()
- const controller = new AbortController()
- const run = await ctx.subagents.start('mock', baseRequest({ signal: controller.signal }))
- controller.abort()
- await expect(run.result).resolves.toMatchObject({ stopReason: 'aborted' })
- })
-
- it('rejects an already-aborted request before starting publication', async () => {
- const ctx = await mount()
- const controller = new AbortController()
- controller.abort()
-
- await expect(ctx.subagents.start('mock', baseRequest({ signal: controller.signal })))
- .rejects.toThrow('mock subagent start aborted before publication')
- })
-
- it('rejects when cancellation wins the asynchronous publication handoff', async () => {
- const ctx = await mount()
- const controller = new AbortController()
- const pending = ctx.subagents.start('mock', baseRequest({ signal: controller.signal }))
-
- controller.abort()
-
- await expect(pending).rejects.toThrow('mock subagent start aborted before publication')
- })
-
- it('unregisters the provider when the owning fiber is disposed (HMR safety)', async () => {
- const ctx = new Context()
- await ctx.plugin(SubagentService)
- const fiber = await ctx.plugin(mock, { name: 'mock' })
- expect(ctx.subagents.list()).toEqual(['mock'])
- await fiber.dispose()
- expect(ctx.subagents.list()).toEqual([])
- })
-
- it('has the namespace-plugin export shape (no stray default) so the Loader keeps name/inject/Config/apply', () => {
- // A default export would make Loader unwrap only that value and drop `inject`.
- expect('default' in mock).toBe(false)
- expect(mock.name).toBe('subagent-mock')
- expect(mock.inject).toEqual(['subagents'])
-
- const loader = Object.create(Loader.prototype) as Loader
- const unwrapped = loader.unwrapExports(mock) as Record
- expect(unwrapped).toBe(mock)
- expect(unwrapped.name).toBe('subagent-mock')
- expect(unwrapped.inject).toEqual(['subagents'])
- expect(typeof unwrapped.apply).toBe('function')
- })
-})
diff --git a/packages/support/subagent-mock/tsconfig.json b/packages/support/subagent-mock/tsconfig.json
deleted file mode 100644
index ccc9fa45ed..0000000000
--- a/packages/support/subagent-mock/tsconfig.json
+++ /dev/null
@@ -1,30 +0,0 @@
-{
- "extends": "../../../tsconfig.base.json",
- "compilerOptions": {
- "rootDir": "src",
- "outDir": "lib/types"
- },
- "include": [
- "src"
- ],
- "references": [
- {
- "path": "../../../vendor/cosmokit"
- },
- {
- "path": "../../../vendor/cordis"
- },
- {
- "path": "../../../vendor/schemastery"
- },
- {
- "path": "../../core/agent"
- },
- {
- "path": "../../llm/llm"
- },
- {
- "path": "../../subagent/subagent"
- }
- ]
-}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 47d998fedc..f67e60bd8f 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1640,9 +1640,6 @@ importers:
'@deepseek-ai/dsh-subagent':
specifier: workspace:^
version: link:../subagent
- '@deepseek-ai/dsh-subagent-mock':
- specifier: workspace:^
- version: link:../../support/subagent-mock
'@deepseek-ai/dsh-system-prompt':
specifier: workspace:^
version: link:../../core/system-prompt
@@ -1751,31 +1748,6 @@ importers:
specifier: ^4.0.0-rc.6
version: 4.0.0-rc.7(@cordisjs/plugin-include@1.0.4)(@cordisjs/plugin-loader@1.0.0-rc.5)
- packages/support/subagent-mock:
- dependencies:
- schemastery:
- specifier: ^3.18.0
- version: 3.18.0
- devDependencies:
- '@cordisjs/plugin-loader':
- specifier: ^1.0.0-rc.5
- version: 1.0.0-rc.5(cordis@4.0.0-rc.7)(node-addon-require-builtin@0.1.0)
- '@deepseek-ai/dsh-agent':
- specifier: workspace:^
- version: link:../../core/agent
- '@deepseek-ai/dsh-llm':
- specifier: workspace:^
- version: link:../../llm/llm
- '@deepseek-ai/dsh-session':
- specifier: workspace:^
- version: link:../../core/session
- '@deepseek-ai/dsh-subagent':
- specifier: workspace:^
- version: link:../../subagent/subagent
- cordis:
- specifier: ^4.0.0-rc.7
- version: 4.0.0-rc.7(@cordisjs/plugin-include@1.0.4)(@cordisjs/plugin-loader@1.0.0-rc.5)
-
packages/tasks/tasks:
devDependencies:
'@deepseek-ai/dsh-agent':
diff --git a/scripts/gen-doc-graphs.ts b/scripts/gen-doc-graphs.ts
index f6aaa6c2d9..5a4178bd56 100644
--- a/scripts/gen-doc-graphs.ts
+++ b/scripts/gen-doc-graphs.ts
@@ -245,7 +245,7 @@ const SERVICE_ROLES: ServiceRole[] = [
pkg: 'subagent',
title: 'Subagent provider registry',
mode: 'seam',
- implementations: ['subagent-spawn', 'subagent-fork', 'subagent-acp', 'subagent-mock'],
+ implementations: ['subagent-spawn', 'subagent-fork', 'subagent-acp'],
consumers: ['tool-subagent'],
note: 'Providers implement transports; tool-subagent exposes one configured provider as a model-facing tool name.',
},
diff --git a/scripts/gen-tool-catalog.ts b/scripts/gen-tool-catalog.ts
index f178f70e9f..ca69768227 100644
--- a/scripts/gen-tool-catalog.ts
+++ b/scripts/gen-tool-catalog.ts
@@ -19,7 +19,7 @@ import WebService from '@deepseek-ai/dsh-web'
import * as WebSearchExa from '@deepseek-ai/dsh-web-search-exa'
import * as WebFetchLocal from '@deepseek-ai/dsh-web-fetch-local'
import SubagentService from '@deepseek-ai/dsh-subagent'
-import * as SubagentMock from '@deepseek-ai/dsh-subagent-mock'
+import type { SubagentProvider } from '@deepseek-ai/dsh-subagent'
import SkillService from '@deepseek-ai/dsh-skill'
import * as SkillLocal from '@deepseek-ai/dsh-skill-local'
import TaskService from '@deepseek-ai/dsh-tasks'
@@ -39,6 +39,17 @@ import * as ToolWorkflow from '@deepseek-ai/dsh-tool-workflow'
const root = resolve(import.meta.dirname, '..')
const OUT = 'docs/tool-catalog.md'
+/** Register the descriptor needed to mount schema-producing consumers. */
+function registerCatalogSubagentProvider(ctx: Context, name: string): void {
+ const provider: SubagentProvider = {
+ name,
+ capabilities: { outputSchema: false, depthLimit: false, toolFilter: false, persona: false },
+ inheritsParentContext: false,
+ start: () => Promise.reject(new Error('tool-catalog provider cannot start a child')),
+ }
+ ctx.subagents.registerProvider(provider)
+}
+
/**
* Tool package plus its hand-maintained boot recipe. The caller mounts the
* prompt and registry; each recipe supplies only package-specific seams and
@@ -191,8 +202,7 @@ const TOOL_PACKAGES: ToolPackage[] = [
shippedNames: ['subagent', 'subagent_fork'],
async mount(ctx) {
await ctx.plugin(SubagentService)
- // Register a scripted provider under the name the tool delegates to.
- await ctx.plugin(SubagentMock, { name: 'mock' })
+ registerCatalogSubagentProvider(ctx, 'mock')
await ctx.plugin(ToolSubagent, { provider: 'mock' })
},
note:
@@ -234,7 +244,7 @@ const TOOL_PACKAGES: ToolPackage[] = [
// subagent provider to satisfy it. The schema does not depend on which
// provider backs the engine.
await ctx.plugin(SubagentService)
- await ctx.plugin(SubagentMock, { name: 'mock' })
+ registerCatalogSubagentProvider(ctx, 'mock')
await ctx.plugin(VmWorkflowEngine, { provider: 'mock' })
await ctx.plugin(ToolWorkflow)
},
diff --git a/scripts/verify-package-readme-model-experience.ts b/scripts/verify-package-readme-model-experience.ts
index 75fb2b75ad..35cd2dae1b 100644
--- a/scripts/verify-package-readme-model-experience.ts
+++ b/scripts/verify-package-readme-model-experience.ts
@@ -65,7 +65,6 @@ const SENTENCE_MODEL_EXPERIENCE: Readonly> = {
'packages/support/invariants': { kind: 'none', reason: 'The observer validates requests but never rewrites their context.' },
'packages/support/loader-smoke': { kind: 'none', reason: 'The test harness observes child-process streams without changing live requests.' },
'packages/support/llm-replay': { kind: 'none', reason: 'The keyless adapter invokes no provider model.' },
- 'packages/support/subagent-mock': { kind: 'indirect', reason: 'Only dsh-tool-subagent renders its configured test outcome.' },
'packages/tasks/tasks': { kind: 'indirect', reason: 'Producer and control-surface plugins own all model rendering over the task registry.' },
'packages/examples/acp-demo': { kind: 'indirect', reason: 'The app bundle delegates request composition to dsh-agent-spine-demo and dsh-acp.' },
'packages/ui/app-boot': { kind: 'indirect', reason: 'Only the loaded plugin tree contributes model context.' },
diff --git a/tsconfig.build.json b/tsconfig.build.json
index dd13e018c3..6f18011715 100644
--- a/tsconfig.build.json
+++ b/tsconfig.build.json
@@ -77,7 +77,6 @@
{ "path": "./packages/support/acp-snapshot" },
{ "path": "./packages/support/loader-smoke" },
{ "path": "./packages/subagent/subagent" },
- { "path": "./packages/support/subagent-mock" },
{ "path": "./packages/subagent/tool-subagent" },
{ "path": "./packages/subagent/subagent-inprocess" },
{ "path": "./packages/subagent/subagent-subprocess" },
diff --git a/tsconfig.json b/tsconfig.json
index 22451dc10f..d0b5692cf0 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -88,7 +88,6 @@
{ "path": "./packages/support/acp-snapshot" },
{ "path": "./packages/support/loader-smoke" },
{ "path": "./packages/subagent/subagent" },
- { "path": "./packages/support/subagent-mock" },
{ "path": "./packages/subagent/tool-subagent" },
{ "path": "./packages/subagent/subagent-inprocess" },
{ "path": "./packages/subagent/subagent-subprocess" },