@dsh-cc/subagent-task 0.5.0

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.
Files changed (49) hide show
  1. package/LICENSE +201 -0
  2. package/README.i18n.yaml +6 -0
  3. package/README.md +214 -0
  4. package/README.zh.md +100 -0
  5. package/lib/background-start.d.ts +207 -0
  6. package/lib/background-start.d.ts.map +1 -0
  7. package/lib/background-start.js +354 -0
  8. package/lib/background-start.js.map +1 -0
  9. package/lib/catalog.d.ts +99 -0
  10. package/lib/catalog.d.ts.map +1 -0
  11. package/lib/catalog.js +197 -0
  12. package/lib/catalog.js.map +1 -0
  13. package/lib/epoch-collector.d.ts +126 -0
  14. package/lib/epoch-collector.d.ts.map +1 -0
  15. package/lib/epoch-collector.js +243 -0
  16. package/lib/epoch-collector.js.map +1 -0
  17. package/lib/index.d.ts +62 -0
  18. package/lib/index.d.ts.map +1 -0
  19. package/lib/index.js +143 -0
  20. package/lib/index.js.map +1 -0
  21. package/lib/preload-tools.d.ts +66 -0
  22. package/lib/preload-tools.d.ts.map +1 -0
  23. package/lib/preload-tools.js +101 -0
  24. package/lib/preload-tools.js.map +1 -0
  25. package/lib/registry.d.ts +49 -0
  26. package/lib/registry.d.ts.map +1 -0
  27. package/lib/registry.js +66 -0
  28. package/lib/registry.js.map +1 -0
  29. package/lib/resume-capture.d.ts +107 -0
  30. package/lib/resume-capture.d.ts.map +1 -0
  31. package/lib/resume-capture.js +232 -0
  32. package/lib/resume-capture.js.map +1 -0
  33. package/lib/sanitize-filter.d.ts +27 -0
  34. package/lib/sanitize-filter.d.ts.map +1 -0
  35. package/lib/sanitize-filter.js +95 -0
  36. package/lib/sanitize-filter.js.map +1 -0
  37. package/lib/strip-instructions.d.ts +47 -0
  38. package/lib/strip-instructions.d.ts.map +1 -0
  39. package/lib/strip-instructions.js +77 -0
  40. package/lib/strip-instructions.js.map +1 -0
  41. package/lib/suppress-settled.d.ts +45 -0
  42. package/lib/suppress-settled.d.ts.map +1 -0
  43. package/lib/suppress-settled.js +80 -0
  44. package/lib/suppress-settled.js.map +1 -0
  45. package/lib/tool.d.ts +49 -0
  46. package/lib/tool.d.ts.map +1 -0
  47. package/lib/tool.js +247 -0
  48. package/lib/tool.js.map +1 -0
  49. package/package.json +78 -0
package/LICENSE ADDED
@@ -0,0 +1,201 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright [yyyy] [name of copyright owner]
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
@@ -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/subagent/task/README.md
5
+ README.md: 2f41225a5a429d06b1c499a0f764797183ce6ab4
6
+ README.zh.md: 5388f67d88f9e8bec717ab5cb353e35e962de8b1
package/README.md ADDED
@@ -0,0 +1,214 @@
1
+ # @dsh-cc/subagent-task
2
+
3
+ English | [中文](README.zh.md)
4
+
5
+ The Claude Code-compatible **Task tool** and **per-workspace subagent catalog** for the
6
+ DeepSeek Harness. It mounts:
7
+
8
+ - the `subagent_fork` tool (CC display name `Task`) with `subagent_type` dispatch over the
9
+ session workspace's `.claude/agents` definitions;
10
+ - the `Available subagents` system-prompt section, rendered per workspace;
11
+ - the reserved tool names (`subagent`, `workflow`) that keep disabled harness rows
12
+ restrictable;
13
+ - a pre-step strip listener that removes the harness `agent-instructions` workspace
14
+ baseline (CLAUDE.md / AGENTS.md) from delegated Task children.
15
+
16
+ The `ccModelRoutes` service (from `@dsh-cc/model-aliases`) supplies the spawn-time
17
+ alias resolver; when it is absent, every child inherits its parent's route (the builtin
18
+ fallback).
19
+
20
+ ## What it is
21
+
22
+ Claude Code's `Task` tool lets the main agent delegate to a named subagent
23
+ (`subagent_type`, e.g. `deep-reasoner`) defined in `.claude/agents`, and the runner loads
24
+ that agent's own system prompt, model, and tool restriction. Historically the DeepSeek
25
+ Harness only had a generic fork (`description`/`prompt`), so `subagent_type` dispatch was a
26
+ dead letter: the child only ever got the *hand-written role copy* the main model put in the
27
+ prompt, and the agent's `model: opus` alias never reached the backend route.
28
+
29
+ This package restores the real link. It discovers the CC `.claude/agents` definitions that
30
+ are visible from the **session's** working directory (not the host process cwd — a web host
31
+ serves many workspaces, so `~/.dsh/…` startup must still see `my-repo/.claude/agents`), and
32
+ turns the internal `subagent_fork` tool into a genuine subagent-type dispatcher.
33
+
34
+ ## How dispatch works
35
+
36
+ Given a `Task(subagent_type, description, prompt)` call from a CC preset session:
37
+
38
+ 1. **`subagent_type` omitted, blank, or `general-purpose`** → a **fresh spawn** of the
39
+ caller: the prompt text becomes the child's first user message, no definition
40
+ participates, no parent conversation is copied. Write a self-contained prompt.
41
+ 2. **`subagent_type` equal to the reserved sentinel `fork`** → a conversation-inheriting
42
+ **fork** of the caller (Claude Code's `subagent_type: "fork"`): completed parent turns
43
+ seed the child; no definition participates. The sentinel is reserved and wins over a
44
+ workspace file of the same name — `.claude/agents/fork.md` is unreachable.
45
+ 3. **A type that matches a definition** under the session cwd (`cwdOf` the assembling agent)
46
+ → a `spawn` with:
47
+ - `persona` = the definition's `systemPrompt` (delivered as the child's system segment);
48
+ - the task text as the child's **first user message**;
49
+ - `agentOptions` = the alias-resolved `{ provider?, model? }` from
50
+ `ctx.get('ccModelRoutes').resolve(def.model)` (only provider/model fields that resolve
51
+ to a value are forwarded, so per-field inheritance never breaks);
52
+ - `toolFilter` = the definition's `toolRestriction` (allow/deny), **sanitized** of tool
53
+ names this composition no longer registers;
54
+ - `maxDepth` = 3 (matches the harness default; configurable).
55
+ 4. **Any other type** (not found in the workspace) → an **error result** listing the
56
+ available types in this workspace (or noting the workspace defines none).
57
+
58
+ The dispatch is **foreground unless the child is explicitly or definition-pinned background**:
59
+ with `run_in_background` omitted, the run is foreground — the tool awaits the child to
60
+ completion, surfaces a non-`completed` stop reason as an error, and concatenates only the
61
+ `text` blocks of the child output — unless the definition pins `background: true`, in which
62
+ case the run backgrounds on omit. Explicit `run_in_background: false` wins over the pin and
63
+ forces foreground. An explicit `run_in_background: true` (or a pin on omit) starts the child
64
+ as a durable continuable background agent and returns promptly with its `agentId`.
65
+
66
+ ### Tool-restriction sanitization and reserved names
67
+
68
+ When a definition's frontmatter narrows tools (e.g. `tools: Read, Task`), the allow/deny
69
+ lists are forced through the CC→harness translation and then checked against the **live**
70
+ set of names the tools registry knows (`ctx.tools.view(callingAgent).restrictableNames` —
71
+ registered and reserved names, read at execute time against the calling agent's scope so
72
+ standing-scope MCP reservations are visible). A name the registry does not know is dropped
73
+ with a warning; there is no static legal-names set, so mounted MCP tools and any future
74
+ registered row are accepted without code churn:
75
+
76
+ - **MCP public names.** An exact `mcp__<server>__<tool>` entry is kept as written — it must
77
+ be the tool's public name, including the deterministic 12-hex identity-hash suffix when
78
+ normalization truncated or replaced the name.
79
+ - **Server-level MCP wildcards.** `mcp__<server>` and `mcp__<server>__*` both expand to
80
+ every mounted tool of that server (`mcp__<server>__` prefix), so frontmatter survives
81
+ servers publishing new tools without naming hashed entries.
82
+ - **A bare `mcp__`** (no server segment) is dropped with an invalid-wildcard warning.
83
+ - **Auto-`ToolSearch`.** If the filter carried an `allow` list, any kept allow name is an
84
+ MCP tool, and the `ToolSearch` tool is itself mounted (restrictable), `ToolSearch` is
85
+ appended (deduped) — otherwise the child would hold MCP names with no load path. When
86
+ `ToolSearch` is not mounted it is never injected.
87
+ - **Spawn-time preload of explicit deferred MCP names.** An explicit `tools:` entry that is
88
+ still deferred (not yet registered) is activated through the duck-typed `ctx.toolSearch`
89
+ seam at spawn, BEFORE the child starts, on BOTH dispatch paths (foreground collect and
90
+ background) — activation is process-global, so every admitting agent's schema grows after
91
+ the spawn. Server-level wildcard entries (`mcp__<server>`, `mcp__<server>__*`) are
92
+ restrict-only: their expansions are never preloaded. Names the sanitized deny list
93
+ excludes, or that sanitize dropped as unknown, are never activated; already-registered
94
+ (eager) names are skipped silently. Outcomes surface in the Task result text
95
+ (`Preloaded deferred tools for child: …`, plus `Not preloaded: <name> (<reason>)` for
96
+ denied/unknown activations); with no `toolSearch` service mounted the step degrades to a
97
+ single warning and the spawn proceeds normally.
98
+ - **Unmounted names are dropped** with the standard `dropping unknown tool name …`
99
+ warning — including MCP names of servers that are not mounted.
100
+ - **An allow-list that matches nothing is deny-all, loudly.** If the filter carried an
101
+ `allow` list and sanitization left zero names, the emitted filter is `{ allow: [] }`
102
+ (the child runs with zero tools) with a warning naming the dropped originals — omitting
103
+ `allow` would instead widen the child to every tool. An emptied `deny` list is simply
104
+ omitted.
105
+
106
+ The internal tool name `subagent_fork` is registered by this package, and
107
+ `ctx.tools.reserve('subagent')` / `reserve('workflow')` keep those names in the restrictable
108
+ universe without exposing visible definitions (the CC frontmatter `Task` translation is
109
+ `['subagent', 'subagent_fork']`, so `subagent` must remain legal even though the harness
110
+ spawn row is disabled; `workflow` is reserved for the deferred workflow row). Because
111
+ sanitization checks the live registry rather than a static list, these reserved names and
112
+ every static CC name (`read`, `bash`, …) survive only when they are actually
113
+ reserved/registered — which they are in the cc preset. A definition that omits both
114
+ `tools` and `disallowedTools` passes no `toolFilter`, so the child inherits the full
115
+ parent tool view (including MCP schemas).
116
+
117
+ ## Available subagents system-prompt section
118
+
119
+ A single global section (`cc:subagent-catalog`, order 110) serves every agent. Its text
120
+ callback receives the assembling agent through the assemble scope, derives that agent's cwd,
121
+ and renders:
122
+
123
+ ```
124
+ ## Available subagents
125
+
126
+ - deep-reasoner — reason through hard architecture and design problems
127
+ - fast-worker — execute a pre-approved mechanical plan
128
+
129
+ To delegate to one, pass its name as the `subagent_type` argument of the Task tool.
130
+ ```
131
+
132
+ Because the section text is composed synchronously but discovery is async, the first
133
+ assembly for an unknown workspace shows nothing, then `system-prompt/change` fires once
134
+ discovery lands and reassembly reveals the catalog. When a workspace defines no agents (or
135
+ there is no agent to scope to) the section renders an empty string and drops out of the
136
+ prompt. The catalog lists only file definitions — it deliberately does **not** enumerate seam
137
+ backend provider names (`fork`/`spawn`/`codex`/`claude-code`) as if they were addressable
138
+ agent types.
139
+
140
+ ## Workspace instructions on Task children
141
+
142
+ The harness `agent-instructions` plugin injects the workspace CLAUDE.md / AGENTS.md
143
+ baseline as an `agent-instructions`-sourced user message on every session — including
144
+ Task children. This package mounts an `agent/pre-step` listener that strips that baseline
145
+ from delegated children (`delegationDepth > 0`):
146
+
147
+ - A delegated child receives only the enter batch and pending inbox messages that are not
148
+ `agent-instructions`-sourced; its persona remains the agent-file `systemPrompt` (or the
149
+ deployment persona for `general-purpose`).
150
+ - Fork children still inherit any CLAUDE.md already in the parent seed — the listener only
151
+ skips a *fresh* child scan; parent history is never rewritten.
152
+ - This is an intentional deviation from Claude Code, whose custom subagents **do** load
153
+ CLAUDE.md (Explore/Plan in CC skip it). dsh-cc applies the skip to every Task child
154
+ because the dsh-cc repo CLAUDE.md is orchestrator policy, not a worker contract.
155
+ - Residual: the harness still reads the instruction files from disk on the child's behalf;
156
+ they just never enter the child's model-visible batch.
157
+
158
+ ## Mounting
159
+
160
+ Mounted by the `cc` preset's `tool-task` row (`@dsh-cc/subagent-task`) inside the
161
+ `cc-services` group, alongside `cc-model-routes` (`@dsh-cc/model-aliases`) which
162
+ supplies the alias resolver. The cc preset **disables** the harness `tool-subagent` and
163
+ `tool-subagent-fork` rows in favour of this tool so there is no double registration of the
164
+ `subagent_fork` name.
165
+
166
+ ## Known limits
167
+
168
+ - **Cold resume drops extra `agentOptions`.** Background (continuable) dispatch exists —
169
+ `run_in_background: true` or a `background: true` definition pin — but cold resume restores
170
+ `persona`/`toolFilter`/model route only and drops every other `agentOptions` field
171
+ (alias-stamped `reasoningEffort`, `maxTokens`). See the parity matrix for the full
172
+ background contract (drain on parent exit, no `outputFile`, fork + background rejected).
173
+ - **Process-level discovery cache.** The registry caches per workspace root for the process
174
+ lifetime and does not watch the filesystem. Editing a `.claude/agents` definition takes
175
+ effect on the next session for a workspace whose cache entry has not yet been created, and
176
+ on process restart otherwise. mtime-based invalidation is a follow-up.
177
+ - **No plugin-agent dispatch (v1).** Only file definitions under `.claude/agents` are
178
+ dispatched. Seam plugin agents (`AgentProvider`) are not addressed by `subagent_type` in v1
179
+ (their start contract does not carry the task text and their capability flags would reject
180
+ `maxDepth`) — see the parity matrix.
181
+ - **Reserved type names.** `general-purpose` and `fork` are sentinels, not file types. A
182
+ workspace file `.claude/agents/fork.md` is unreachable; `subagent_type: "fork"` always
183
+ means inherit completed parent turns.
184
+ - **Instruction files are still scanned.** The strip happens after the harness
185
+ `agent-instructions` plugin has read the workspace CLAUDE.md / AGENTS.md from disk and
186
+ injected them; this listener only keeps them out of the child's model-visible batch, so
187
+ the disk scan itself cannot be prevented without changing the harness. A fork child's
188
+ parent seed is never rewritten, so CLAUDE.md already in the seed is inherited.
189
+
190
+ ## API
191
+
192
+ - `apply(ctx)` — cordis plugin entry (plugin id `cc-subagent-task`); safe when either the
193
+ tools or the system-prompt seam is absent.
194
+ - `AgentRegistry` (`./registry`) — per-workspace definition cache (`ensure` / `list` /
195
+ `resolve`), lazily loading `loadClaudeCodeAgents(root)` (user layer + project layer,
196
+ project shadows user).
197
+ - `registerTaskTool` / `TASK_TOOL` (`./tool`) — register the `subagent_fork` Task tool.
198
+ - `mountAgentCatalog` / `CATALOG_SECTION_NAME` / `CATALOG_SECTION_ORDER` (`./catalog`) —
199
+ mount the `Available subagents` section.
200
+ - `mountStripWorkspaceInstructions` / `isDelegated` / `isAgentInstructions`
201
+ (`./strip-instructions`) — mount (or classify for) the pre-step strip of the harness
202
+ `agent-instructions` workspace baseline on delegated Task children.
203
+
204
+ ## Non-goals
205
+
206
+ - Treating omitted `run_in_background` as background as a session policy (the Claude Code
207
+ interactive omit=background rule); dsh-cc stays foreground on omit unless the definition
208
+ pins `background: true`.
209
+ - In-flight promotion of a running foreground Task to background (TUI Ctrl+B) — a follow-up,
210
+ not a limitation of the package's existence.
211
+ - Seam plugin-agent dispatch.
212
+ - CC frontmatter `permissionMode` / `isolation` / `memory` / `effort` projection onto the
213
+ child (the loader parses them, v1 does not consume them).
214
+ - `registerBaseAgents` in cc-shell (base-agent discovery moved here; see the cc-shell README).
package/README.zh.md ADDED
@@ -0,0 +1,100 @@
1
+ # @dsh-cc/subagent-task
2
+
3
+ [English](README.md) | 中文
4
+
5
+ 面向 DeepSeek Harness 的 **Claude Code 兼容 Task 工具** 与 **按工作区隔离的 subagent 目录**。它挂载:
6
+
7
+ - 内部工具名 `subagent_fork`(CC 显示名 `Task`),以 `subagent_type` 对会话工作区的 `.claude/agents` 定义做派发;
8
+ - `Available subagents` 系统提示词 section(**按工作区**渲染);
9
+ - 保留的工具名(`subagent`、`workflow`),使被禁用的 harness 行仍可被 restrict;
10
+ - 一个 pre-step 剥离监听器,把 harness `agent-instructions` 的工作区基线(CLAUDE.md / AGENTS.md)从被委派的 Task child 中移除。
11
+
12
+ `ccModelRoutes` 服务(来自 `@dsh-cc/model-aliases`)提供派发时的别名解析器;当其缺席时,每个 child 继承父路由(内置 fallback)。
13
+
14
+ ## 是什么
15
+
16
+ Claude Code 的 `Task` 工具允许主代理按 `subagent_type`(如 `deep-reasoner`)委派给 `.claude/agents` 里定义的具名 subagent,运行时加载该 agent 自己的 system prompt、model 与工具限制。历史上 DeepSeek Harness 只有通用 fork(`description`/`prompt`),因此 `subagent_type` 派发形同虚设:child 只能拿到主模型写在 prompt 里的**手写角色文字**,而该 agent 的 `model: opus` alias 也从未抵达后端路由。
17
+
18
+ 本包恢复真实链路。它发现从**会话**工作目录(而非宿主进程 cwd——web 宿主同时服务多个工作区,从 `~/.dsh/…` 启动也必须看到 `my-repo/.claude/agents`)可见的 CC `.claude/agents` 定义,并把内部 `subagent_fork` 工具变成一个真正的 subagent-type 派发器。
19
+
20
+ ## 派发机制
21
+
22
+ 给定 CC preset 会话中的一次 `Task(subagent_type, description, prompt)` 调用:
23
+
24
+ 1. **`subagent_type` 省略、空白或为 `general-purpose`** → **全新 spawn**:prompt 文本成为 child 的首条 user message,无定义参与,不拷贝父对话。prompt 必须自包含。
25
+ 2. **`subagent_type` 等于保留哨兵 `fork`** → 继承对话的 **fork**(Claude Code 的 `subagent_type: "fork"`):父已完成轮次作为 seed,无定义参与。哨兵优先于同名文件,`.claude/agents/fork.md` 不可达。
26
+ 3. **命中会话 cwd(`cwdOf` 组装 agent)下的定义** → 以 `spawn` 启动并携带:
27
+ - `persona` = 定义的 `systemPrompt`(作为 child 的系统段下发);
28
+ - 任务文本作为 child 的**首条 user message**;
29
+ - `agentOptions` = 来自 `ctx.get('ccModelRoutes').resolve(def.model)` 的别名解析结果 `{ provider?, model? }`(只透传解析到值的 provider/model 字段,绝不破坏按字段继承);
30
+ - `toolFilter` = 定义的 `toolRestriction`(allow/deny),**消毒**掉本组合已不再注册的工具名;
31
+ - `maxDepth` = 3(与 harness 默认一致;可配置)。
32
+ 4. **其它类型**(工作区内找不到)→ **报错结果**,附带本工作区可用类型清单(或说明本工作区未定义任何 agent)。
33
+
34
+ 派发规则是**前台,除非显式指定或定义钉死了后台**:`run_in_background` 缺省时运行是前台——工具等待 child 跑完,非 `completed` 的 stop reason 以错误浮出,child 输出只拼接 `text` 块——除非定义钉了 `background: true`,此时缺省即后台。显式 `run_in_background: false` 覆盖钉死,强制前台。显式 `run_in_background: true`(或钉死时缺省)则把 child 作为 durable continuable 后台 agent 启动,并立即返回其 `agentId`。
35
+
36
+ ### 工具限制消毒与保留名
37
+
38
+ 当定义 frontmatter 收窄工具(如 `tools: Read, Task`)时,allow/deny 清单会强制经过 CC→harness 翻译,再对照工具注册表**实时**已知的名集合(`ctx.tools.view(callingAgent).restrictableNames`——已注册与已保留名的并集,在 execute 时按调用 agent 的 scope 读取,因此 standing-scope 上的 MCP 保留名可见)做校验。注册表不认识的名会被丢弃并告警;这里没有静态合法名集合,因此已挂载的 MCP 工具和未来新增的注册行都无需改代码即可通过:
39
+
40
+ - **MCP 公开名。** 精确的 `mcp__<server>__<tool>` 原样保留——必须用该工具的公开名,包括名字被规范化截断/替换时追加的 12 位十六进制确定性 hash 后缀。
41
+ - **server 级 MCP 通配。** `mcp__<server>` 与 `mcp__<server>__*` 都会展开为该 server 已挂载的全部工具(`mcp__<server>__` 前缀),frontmatter 因此不必逐个点名带 hash 的条目也能在 server 发布新工具后继续生效。
42
+ - **裸 `mcp__`**(无 server 段)被丢弃并给出 invalid-wildcard 告警。
43
+ - **自动带上 `ToolSearch`。** 若过滤器带有 `allow` 清单、保留下来的 allow 名中有 MCP 工具、且 `ToolSearch` 工具本身已挂载(可限制),则追加 `ToolSearch`(去重)——否则 child 手握 MCP 名却没有任何加载路径。`ToolSearch` 未挂载时绝不注入。
44
+ - **spawn 时预激活显式列出的延后 MCP 名。** `tools:` 中显式点名、且仍处于延后(未注册)状态的 MCP 名,会在 spawn 时、child 启动之前,经 duck-typed `ctx.toolSearch` seam 激活——前台 collect 与后台两条派发路径都会执行,激活是进程全局的(spawn 之后每个可接纳该工具的 agent 的 schema 都会变大)。server 级通配(`mcp__<server>`、`mcp__<server>__*`)仅用于限制、绝不预激活;被消毒 deny 清单排除或被消毒丢弃的名永不激活,已注册(急加载)的名静默跳过。结果浮现在 Task 结果文本中(`Preloaded deferred tools for child: …`,以及 `Not preloaded: <名>(<原因>)`);未挂载 `toolSearch` 服务时只告警一次、照常 spawn。
45
+ - **未挂载的名被丢弃**,给出标准 `dropping unknown tool name …` 告警——包括未挂载 server 的 MCP 名。
46
+ - **匹配不到任何工具的 allow 清单 = 醒目的 deny-all。** 若过滤器带有 `allow` 清单而消毒后一个名都不剩,产出的过滤器是 `{ allow: [] }`(child 以零工具运行)并告警列出被丢弃的原始名——省略 `allow` 反而会把 child 放宽到全部工具。被清空的 `deny` 清单则直接省略。
47
+
48
+ 本包注册内部工具名 `subagent_fork`,并经 `ctx.tools.reserve('subagent')` / `reserve('workflow')` 把这些名放进可限制 universe 而不暴露可见定义(CC frontmatter `Task` 的翻译是 `['subagent', 'subagent_fork']`,故即使 harness spawn 行被禁,`subagent` 也必须保持合法;`workflow` 为延后的 workflow 行保留)。由于消毒对照的是实时注册表而非静态清单,这些保留名和每个静态 CC 名(`read`、`bash` 等)只有在真正被保留/注册时才会存活——在 cc preset 中它们正是如此。定义同时省略 `tools` 与 `disallowedTools` 时不传 `toolFilter`,child 继承父的完整工具面(含 MCP schema)。
49
+
50
+ ## Available subagents 系统提示词 section
51
+
52
+ 一个全局 section(`cc:subagent-catalog`,order 110)服务所有 agent。其 text 回调通过 assemble scope 拿到组装 agent,推导其 cwd,并渲染:
53
+
54
+ ```
55
+ ## Available subagents
56
+
57
+ - deep-reasoner — reason through hard architecture and design problems
58
+ - fast-worker — execute a pre-approved mechanical plan
59
+
60
+ To delegate to one, pass its name as the `subagent_type` argument of the Task tool.
61
+ ```
62
+
63
+ 由于 section 文本是同步组装的而发现是异步的,未知工作区的首次组装会显示空,随后 discovery 落地后触发 `system-prompt/change`,重组即显示目录。当工作区未定义任何 agent(或没有可 scope 的 agent)时,section 渲染空串并从提示词中消失。目录只列**文件定义**——刻意**不**把 seam 后端 provider 名(`fork`/`spawn`/`codex`/`claude-code`)当作可寻址的 agent 类型来枚举。
64
+
65
+ ## Task child 上的工作区指令
66
+
67
+ harness 的 `agent-instructions` 插件会在**每个**会话(包括 Task child)注入工作区 CLAUDE.md / AGENTS.md 基线,作为一条 `agent-instructions` 来源的 user message。本包挂载一个 `agent/pre-step` 监听器,为被委派的 child(`delegationDepth > 0`)剥离该基线:
68
+
69
+ - 被委派的 child 收到的 enter 批次与待处理 inbox 消息中不再包含 `agent-instructions` 来源的消息;其 persona 仍是 agent 文件的 `systemPrompt`(`general-purpose` 则是部署 persona)。
70
+ - fork child 仍继承父 seed 中已有的 CLAUDE.md——监听器只跳过*全新* child 的扫描,从不改写父历史。
71
+ - 这是对 Claude Code 的**有意偏离**:CC 的自定义 subagent **会**加载 CLAUDE.md(CC 的 Explore/Plan 则跳过)。dsh-cc 对所有 Task child 施加跳过,因为 dsh-cc 仓库的 CLAUDE.md 是编排者策略,而非 worker 契约。
72
+ - 残余: harness 仍会代 child 从磁盘读取指令文件,只是它们不再进入 child 的模型可见批次。
73
+
74
+ ## 挂载
75
+
76
+ 由 `cc` preset 的 `tool-task` 行(`@dsh-cc/subagent-task`)挂载在 `cc-services` 组内,旁边是提供别名解析器的 `cc-model-routes`(`@dsh-cc/model-aliases`)。cc preset **禁用** harness 的 `tool-subagent` 与 `tool-subagent-fork` 两行以改用本工具,避免 `subagent_fork` 名被重复注册。
77
+
78
+ ## 已知限制
79
+
80
+ - **冷恢复丢弃其余 `agentOptions`。** 后台(continuable)派发已存在——`run_in_background: true` 或定义钉 `background: true`——但冷恢复只还原 `persona`/`toolFilter`/模型路由,丢弃其余全部 `agentOptions` 字段(别名标记的 `reasoningEffort`、`maxTokens`)。完整后台契约(父退出 drain、无 `outputFile`、fork + 后台被拒)见 parity matrix。
81
+ - **进程级发现缓存。** 注册表按工作区 root 缓存整个进程生命周期,不监听文件系统。编辑 `.claude/agents` 定义:对缓存条目尚未创建的工作区在下次会话生效,否则在进程重启后生效。基于 mtime 的失效刷为 follow-up。
82
+ - **v1 不做插件 agent 派发。** 只派发 `.claude/agents` 下的文件定义。seam 插件 agent(`AgentProvider`)在 v1 不被 `subagent_type` 寻址(其 start 契约不携带任务正文,且 capability 标志会拒绝 `maxDepth`)——见 parity matrix。
83
+ - **保留类型名。** `general-purpose` 与 `fork` 是哨兵,不是文件类型。工作区文件 `.claude/agents/fork.md` 不可达;`subagent_type: "fork"` 永远表示继承父已完成轮次。
84
+ - **指令文件仍会被扫描。** 剥离发生在 harness `agent-instructions` 插件已从磁盘读取工作区 CLAUDE.md / AGENTS.md 并注入之后;本监听器只把它们挡在 child 的模型可见批次之外,不改 harness 就无法阻止磁盘扫描。fork child 的父 seed 从不改写,seed 中已有的 CLAUDE.md 会被继承。
85
+
86
+ ## API
87
+
88
+ - `apply(ctx)` — cordis 插件入口(插件 id `cc-subagent-task`);tools 或 system-prompt seam 任一缺席时也安全。
89
+ - `AgentRegistry`(`./registry`)— 按工作区的定义缓存(`ensure` / `list` / `resolve`),惰性加载 `loadClaudeCodeAgents(root)`(用户层 + 项目层,项目遮蔽用户)。
90
+ - `registerTaskTool` / `TASK_TOOL`(`./tool`)— 注册 `subagent_fork` Task 工具。
91
+ - `mountAgentCatalog` / `CATALOG_SECTION_NAME` / `CATALOG_SECTION_ORDER`(`./catalog`)— 挂载 `Available subagents` section。
92
+ - `mountStripWorkspaceInstructions` / `isDelegated` / `isAgentInstructions`(`./strip-instructions`)— 挂载(或为测试分类)pre-step 剥离监听器,把 harness `agent-instructions` 工作区基线从被委派的 Task child 中移除。
93
+
94
+ ## 非目标
95
+
96
+ - 把缺省的 `run_in_background` 视为后台的会话级策略(Claude Code 交互式的 omit=background 规则);dsh-cc 在缺省时保持前台,除非定义钉了 `background: true`。
97
+ - 正在运行的前台 Task 的在途转后台(TUI Ctrl+B)——follow-up,而非本包存在性上的限制。
98
+ - seam 插件 agent 派发。
99
+ - 把 CC frontmatter 的 `permissionMode` / `isolation` / `memory` / `effort` 投影到 child(loader 会解析,v1 不消费)。
100
+ - cc-shell 里的 `registerBaseAgents`(base agent 发现迁至此处;见 cc-shell README)。