@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
@@ -0,0 +1,243 @@
1
+ /**
2
+ * The dsh-cc epoch collector: inline first-epoch collection of a continuable
3
+ * subagent child with zero harness changes (normative design:
4
+ * `docs/plans/2026-09-10-epoch-collector-dsh-cc.md`).
5
+ *
6
+ * One shared `subagent/start` + `subagent/end` listener pair and one watch
7
+ * map (`Map<childId, { runId?, resolve }>`) serve every collector of the
8
+ * process. The reservation is placed BEFORE `startContinuable` so an
9
+ * immediate settle cannot fall between start and subscription; the runId is
10
+ * captured from the child's first `subagent/start` and the end event is
11
+ * matched by runId (a cold-resumed later epoch has a new runId and never
12
+ * satisfies a stale watcher). The shared listeners are disposed when the map
13
+ * empties.
14
+ *
15
+ * This module is the ONE-FILE swap seam (§7): when upstream later ships the
16
+ * collectable continuable handle, only this file's `collectFirstEpoch`
17
+ * implementation is replaced — the Task tool and TUI surfaces are untouched.
18
+ *
19
+ * @module @dsh-cc/subagent-task/epoch-collector
20
+ */
21
+ /** The one shared watch map (per-process singleton, §3 "Parallel collects"). */
22
+ const watches = new Map();
23
+ /** The bus the shared listener pair is currently subscribed to. */
24
+ let watchedBus;
25
+ /** Disposer of the shared listener pair on {@link watchedBus}. */
26
+ let disposeWatchers;
27
+ function ensureWatchers(bus) {
28
+ if (disposeWatchers !== undefined && watchedBus === bus)
29
+ return;
30
+ // A collect on a different bus (never happens in production, where every
31
+ // collector shares the one cordis context) re-subscribes on the new bus.
32
+ if (disposeWatchers !== undefined)
33
+ disposeWatchers();
34
+ const offStart = bus.on('subagent/start', info => {
35
+ const entry = watches.get(String(info.id));
36
+ if (entry !== undefined && entry.runId === undefined)
37
+ entry.runId = String(info.runId);
38
+ });
39
+ const offEnd = bus.on('subagent/end', info => {
40
+ const runId = String(info.runId);
41
+ for (const [childId, entry] of watches) {
42
+ if (entry.runId !== runId)
43
+ continue;
44
+ watches.delete(childId);
45
+ const output = info.lastAssistantMessage;
46
+ entry.resolve({
47
+ stopReason: String(info.stopReason),
48
+ ...(output !== undefined ? { output } : {}),
49
+ });
50
+ if (watches.size === 0 && disposeWatchers !== undefined)
51
+ disposeWatchers();
52
+ return;
53
+ }
54
+ });
55
+ watchedBus = bus;
56
+ disposeWatchers = () => {
57
+ offStart?.();
58
+ offEnd?.();
59
+ watchedBus = undefined;
60
+ };
61
+ }
62
+ function release(childId) {
63
+ watches.delete(childId);
64
+ if (watches.size === 0 && disposeWatchers !== undefined)
65
+ disposeWatchers();
66
+ }
67
+ /** Instrumentation for tests and diagnostics: live watch entries. */
68
+ export function epochWatchSize() {
69
+ return watches.size;
70
+ }
71
+ // ── Duplicate-notice suppression bookkeeping (§5) ─────────────────────────
72
+ /**
73
+ * The pop-once "collected" set: senderSessionIds whose `subagent-settled`
74
+ * notice must be dropped because the epoch was consumed inline. Entries are
75
+ * popped by the suppression pre-step listener on first delivery, so a later
76
+ * epoch of the same child delivers normally.
77
+ */
78
+ const collectedForSuppression = new Set();
79
+ /** Mark a child's settlement notice for drop (done at collect reservation). */
80
+ export function markCollectedForSuppression(childId) {
81
+ collectedForSuppression.add(childId);
82
+ }
83
+ /**
84
+ * Un-mark a child (a promoted child is never suppressed — its notice flows
85
+ * normally; Slice 3's `promote()` calls this).
86
+ */
87
+ export function releaseCollectedForSuppression(childId) {
88
+ collectedForSuppression.delete(childId);
89
+ }
90
+ /** Whether a child's settlement notice is currently marked for drop. */
91
+ export function isCollectedForSuppression(childId) {
92
+ return collectedForSuppression.has(childId);
93
+ }
94
+ const registrations = new Map();
95
+ /** Registry key: parentSessionId + toolCallToken. */
96
+ export function collectorKey(parentSessionId, toolCallToken) {
97
+ return `${parentSessionId}\u0000${toolCallToken}`;
98
+ }
99
+ export function registerCollector(key, handle) {
100
+ registrations.set(key, handle);
101
+ }
102
+ export function unregisterCollector(key) {
103
+ registrations.delete(key);
104
+ }
105
+ export function collectorFor(key) {
106
+ return registrations.get(key);
107
+ }
108
+ /**
109
+ * All live registrations of ONE parent session (the TUI busy-branch Ctrl+B
110
+ * query, F9): every armed collect whose compound key starts with the session
111
+ * prefix. A promoted/settled/aborted collect unregisters itself, so an
112
+ * armed entry here is exactly a promotable foreground wait.
113
+ */
114
+ export function collectorsForSession(parentSessionId) {
115
+ const prefix = `${parentSessionId}\u0000`;
116
+ const found = [];
117
+ for (const [key, handle] of registrations) {
118
+ if (key.startsWith(prefix))
119
+ found.push(handle);
120
+ }
121
+ return found;
122
+ }
123
+ /** Instrumentation for tests and diagnostics: live registration count. */
124
+ export function registeredCollectorCount() {
125
+ return registrations.size;
126
+ }
127
+ /**
128
+ * Collect a continuable child's first epoch inline. Never awaits the parent's
129
+ * inbound messages — only bus events (§3 deadlock rule).
130
+ */
131
+ export async function collectFirstEpoch(deps) {
132
+ const { bus, childId, agent, signal, subagents, start } = deps;
133
+ markCollectedForSuppression(childId);
134
+ let resolveEntry;
135
+ const entry = { resolve: terminal => resolveEntry(terminal) };
136
+ const epochPromise = new Promise(resolve => {
137
+ resolveEntry = resolve;
138
+ });
139
+ let interrupted = false;
140
+ let promoted = false;
141
+ let settled = false;
142
+ let raceAbort;
143
+ const abortPromise = new Promise(resolve => {
144
+ raceAbort = resolve;
145
+ });
146
+ let resolvePromoted;
147
+ const promotedPromise = new Promise(resolve => {
148
+ resolvePromoted = resolve;
149
+ });
150
+ // The promotion-registry registration (§6): registered BEFORE start, so a
151
+ // Ctrl+B fired during the still-in-flight `startContinuable` finds the
152
+ // armed handle; unregistered on EVERY resolution path (settle, abort,
153
+ // promote, start-throw) — a resolved collect is never promotable.
154
+ const registrationKey = deps.parentSessionId !== undefined && deps.toolCallToken !== undefined
155
+ ? collectorKey(deps.parentSessionId, deps.toolCallToken)
156
+ : undefined;
157
+ const registration = {
158
+ childId,
159
+ promote() {
160
+ // Idempotent: a collect already resolved (settled) or already promoted
161
+ // never re-releases, re-un-suppresses, or re-resolves.
162
+ if (settled || promoted)
163
+ return;
164
+ promoted = true;
165
+ // Release the watch: the runId-matched `subagent/end` arriving later
166
+ // resolves nothing — the epoch is no longer awaited (§6).
167
+ release(childId);
168
+ // The promoted child's eventual settlement notice flows NORMALLY
169
+ // (exactly-once, un-suppressed).
170
+ releaseCollectedForSuppression(childId);
171
+ resolvePromoted();
172
+ },
173
+ abort() {
174
+ if (settled || interrupted)
175
+ return;
176
+ onAbort();
177
+ },
178
+ };
179
+ const onAbort = () => {
180
+ if (interrupted)
181
+ return;
182
+ interrupted = true;
183
+ // Interrupt exactly once (M5); an absent/settled target is an accepted
184
+ // no-op, and an admission throw must never block the prompt resolve.
185
+ if (subagents?.interrupt !== undefined) {
186
+ try {
187
+ subagents.interrupt(childId, { kind: 'ancestor', agent });
188
+ }
189
+ catch {
190
+ // Degraded: the prompt resolve below is still prompt and observable.
191
+ }
192
+ }
193
+ raceAbort?.({ kind: 'aborted', stopReason: 'aborted' });
194
+ };
195
+ signal.addEventListener('abort', onAbort, { once: true });
196
+ // Reserve BEFORE start (§8 race register).
197
+ watches.set(childId, entry);
198
+ ensureWatchers(bus);
199
+ if (registrationKey !== undefined)
200
+ registerCollector(registrationKey, registration);
201
+ const finish = () => {
202
+ settled = true;
203
+ if (registrationKey !== undefined)
204
+ unregisterCollector(registrationKey);
205
+ signal.removeEventListener('abort', onAbort);
206
+ };
207
+ try {
208
+ await start();
209
+ }
210
+ catch (error) {
211
+ finish();
212
+ release(childId);
213
+ releaseCollectedForSuppression(childId);
214
+ throw error;
215
+ }
216
+ if (signal.aborted)
217
+ onAbort();
218
+ // Pre-acceptance promotion (§6): a promote() that fired while
219
+ // `startContinuable` was still in flight resolves the tool call AS SOON AS
220
+ // start resolves — the child is accepted, its id durable, the epoch never
221
+ // awaited. The watch was already released by promote(); the settled notice
222
+ // was already un-suppressed.
223
+ if (promoted) {
224
+ finish();
225
+ return { kind: 'promoted' };
226
+ }
227
+ const outcome = await Promise.race([
228
+ epochPromise.then((terminal) => ({ kind: 'epoch', ...terminal })),
229
+ abortPromise,
230
+ promotedPromise.then(() => ({ kind: 'promoted' })),
231
+ ]);
232
+ finish();
233
+ if (outcome.kind === 'aborted') {
234
+ // Keep the watch entry armed ONLY to drive suppression bookkeeping: the
235
+ // child's real `subagent/end` arrives later and releases the entry.
236
+ return outcome;
237
+ }
238
+ // Epoch end already released the entry (and disposed the shared listeners
239
+ // when the map emptied); the suppression mark stays until the pop-once
240
+ // listener consumes the duplicated settled notice.
241
+ return outcome;
242
+ }
243
+ //# sourceMappingURL=epoch-collector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"epoch-collector.js","sourceRoot":"","sources":["../src/epoch-collector.ts"],"names":[],"mappings":"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"}
package/lib/index.d.ts ADDED
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Claude Code-compatible Task tool and per-workspace subagent catalog for the
3
+ * DeepSeek Harness. Mounts:
4
+ * - the `subagent_fork` tool (CC display name `Task`) with `subagent_type`
5
+ * dispatch over the session workspace's `.claude/agents` definitions;
6
+ * - the `Available subagents` system-prompt section rendered per workspace;
7
+ * - the reserved tool names that keep disabled harness rows restrictable;
8
+ * - a pre-step strip listener that removes the harness `agent-instructions`
9
+ * workspace baseline from delegated children so each child keeps its own
10
+ * persona instead of also loading the parent's CLAUDE.md / AGENTS.md.
11
+ *
12
+ * The `ccModelRoutes` service (from `@dsh-cc/model-aliases`) supplies
13
+ * the spawn-time alias resolver; when absent, every child inherits its
14
+ * parent's route (the builtin fallback).
15
+ *
16
+ * @module @dsh-cc/subagent-task
17
+ */
18
+ import type { Context } from '@deepseek-ai/cordis';
19
+ import { type ResumePinsConfig } from './resume-capture.ts';
20
+ export { AgentRegistry } from './registry.ts';
21
+ export { registerTaskTool, TASK_TOOL, MAX_LIVE_CONTINUABLE_CHILDREN, CLAUDE_CODE_DISABLE_BACKGROUND_TASKS, backgroundTasksDisabled, } from './tool.ts';
22
+ export { collectFirstEpoch, collectorFor, collectorKey, registerCollector, unregisterCollector, collectorsForSession, registeredCollectorCount, markCollectedForSuppression, releaseCollectedForSuppression, isCollectedForSuppression, } from './epoch-collector.ts';
23
+ export type { CollectorRegistration, EpochOutcome, EpochTerminal, } from './epoch-collector.ts';
24
+ export { mountAgentCatalog } from './catalog.ts';
25
+ export { mountStripWorkspaceInstructions, isDelegated, isAgentInstructions, } from './strip-instructions.ts';
26
+ export { isSubagentSettledNotice, mountSettledNoticeSuppression, } from './suppress-settled.ts';
27
+ export type { ResumePinsConfig, CaptureInput } from './resume-capture.ts';
28
+ export { SpawnPinCapture, overlayRoute, probeWorkspace } from './resume-capture.ts';
29
+ /** Cordis plugin id. */
30
+ export declare const name = "cc-subagent-task";
31
+ /** Section name for the background-subagent contract. */
32
+ export declare const BACKGROUND_SECTION_NAME = "cc:subagent-background";
33
+ export declare const BACKGROUND_SECTION_TEXT: string;
34
+ /**
35
+ * Register the static background-loop system-prompt section (same contract the
36
+ * Task tool description teaches, stated once so it survives description
37
+ * trimming). No-op when the system-prompt seam is absent.
38
+ * @param ctx - the plug context.
39
+ * @returns the section disposer, or undefined when the seam is absent.
40
+ */
41
+ export declare function mountBackgroundSection(ctx: Context): (() => void) | undefined;
42
+ /** Plugin configuration. */
43
+ export interface TaskPluginConfig {
44
+ /**
45
+ * Spawn-time resume-pin capture (plan §4.3/§4.5) for continuable background
46
+ * children. Absent (default) → zero behavior change: no pin writes, no
47
+ * preflight, identical spawns.
48
+ */
49
+ readonly resumePins?: ResumePinsConfig;
50
+ }
51
+ /**
52
+ * Mount the Task tool, the agents catalog, and the workspace-instruction
53
+ * strip. Safe when either the tools or the system-prompt seam is absent
54
+ * (the corresponding mount skips); the pre-step listener only needs
55
+ * `ctx.on`, so it mounts regardless.
56
+ * @param ctx - the plug context.
57
+ * @param config - plugin configuration; omitted `resumePins` still arms capture
58
+ * when the resume-pins plugin's `resumePinStore` service is mounted (plan
59
+ * §4.10); with neither, capture is disabled.
60
+ */
61
+ export declare function apply(ctx: Context, config?: TaskPluginConfig): void;
62
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAOlD,OAAO,EAAmB,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAO5E,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,EACL,gBAAgB,EAChB,SAAS,EACT,6BAA6B,EAC7B,oCAAoC,EACpC,uBAAuB,GACxB,MAAM,WAAW,CAAA;AAClB,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,YAAY,EACZ,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,EACpB,wBAAwB,EACxB,2BAA2B,EAC3B,8BAA8B,EAC9B,yBAAyB,GAC1B,MAAM,sBAAsB,CAAA;AAC7B,YAAY,EACV,qBAAqB,EACrB,YAAY,EACZ,aAAa,GACd,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAChD,OAAO,EACL,+BAA+B,EAC/B,WAAW,EACX,mBAAmB,GACpB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACL,uBAAuB,EACvB,6BAA6B,GAC9B,MAAM,uBAAuB,CAAA;AAC9B,YAAY,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AACzE,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAEnF,wBAAwB;AACxB,eAAO,MAAM,IAAI,qBAAqB,CAAA;AAEtC,yDAAyD;AACzD,eAAO,MAAM,uBAAuB,2BAA2B,CAAA;AAK/D,eAAO,MAAM,uBAAuB,QAwBxB,CAAA;AAEZ;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,OAAO,GAAG,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAU7E;AAED,4BAA4B;AAC5B,MAAM,WAAW,gBAAgB;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,gBAAgB,CAAA;CACvC;AAED;;;;;;;;;GASG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,GAAE,gBAAqB,GAAG,IAAI,CAuBvE"}
package/lib/index.js ADDED
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Claude Code-compatible Task tool and per-workspace subagent catalog for the
3
+ * DeepSeek Harness. Mounts:
4
+ * - the `subagent_fork` tool (CC display name `Task`) with `subagent_type`
5
+ * dispatch over the session workspace's `.claude/agents` definitions;
6
+ * - the `Available subagents` system-prompt section rendered per workspace;
7
+ * - the reserved tool names that keep disabled harness rows restrictable;
8
+ * - a pre-step strip listener that removes the harness `agent-instructions`
9
+ * workspace baseline from delegated children so each child keeps its own
10
+ * persona instead of also loading the parent's CLAUDE.md / AGENTS.md.
11
+ *
12
+ * The `ccModelRoutes` service (from `@dsh-cc/model-aliases`) supplies
13
+ * the spawn-time alias resolver; when absent, every child inherits its
14
+ * parent's route (the builtin fallback).
15
+ *
16
+ * @module @dsh-cc/subagent-task
17
+ */
18
+ import { PinStore } from '@dsh-cc/subagent-resume-pins';
19
+ import { collectorFor, collectorsForSession, registeredCollectorCount, } from "./epoch-collector.js";
20
+ import { SpawnPinCapture } from "./resume-capture.js";
21
+ import { AgentRegistry } from "./registry.js";
22
+ import { registerTaskTool } from "./tool.js";
23
+ import { mountSettledNoticeSuppression } from "./suppress-settled.js";
24
+ import { mountAgentCatalog } from "./catalog.js";
25
+ import { mountStripWorkspaceInstructions } from "./strip-instructions.js";
26
+ export { AgentRegistry } from "./registry.js";
27
+ export { registerTaskTool, TASK_TOOL, MAX_LIVE_CONTINUABLE_CHILDREN, CLAUDE_CODE_DISABLE_BACKGROUND_TASKS, backgroundTasksDisabled, } from "./tool.js";
28
+ export { collectFirstEpoch, collectorFor, collectorKey, registerCollector, unregisterCollector, collectorsForSession, registeredCollectorCount, markCollectedForSuppression, releaseCollectedForSuppression, isCollectedForSuppression, } from "./epoch-collector.js";
29
+ export { mountAgentCatalog } from "./catalog.js";
30
+ export { mountStripWorkspaceInstructions, isDelegated, isAgentInstructions, } from "./strip-instructions.js";
31
+ export { isSubagentSettledNotice, mountSettledNoticeSuppression, } from "./suppress-settled.js";
32
+ export { SpawnPinCapture, overlayRoute, probeWorkspace } from "./resume-capture.js";
33
+ /** Cordis plugin id. */
34
+ export const name = 'cc-subagent-task';
35
+ /** Section name for the background-subagent contract. */
36
+ export const BACKGROUND_SECTION_NAME = 'cc:subagent-background';
37
+ /** Order slot beside the catalog section (tool guidance owns 100–199). */
38
+ const BACKGROUND_SECTION_ORDER = 112;
39
+ export const BACKGROUND_SECTION_TEXT = [
40
+ '## Background subagents',
41
+ '',
42
+ '- Heuristic: if this turn\'s answer to the human depends on the child, omit `run_in_background`',
43
+ ' (foreground — the call waits for the final text). If the human can keep talking while the',
44
+ ' child works, pass `run_in_background: true`: the call returns promptly with a durable',
45
+ ' `agentId` once the child accepts its first turn. Synthesize on the wake; do not poll.',
46
+ '- A definition with `background: true` backgrounds on omit: the call returns',
47
+ ' `{ status: \'async_launched\' }` immediately and the real result arrives later as a wake',
48
+ ' message — there is NO inline result to use, so never compose on one. Pass',
49
+ ' `run_in_background: false` when this turn needs that child\'s result: it forces',
50
+ ' synchronous collection. Explicit true/false always win over the pin.',
51
+ '- A background child\'s report — or its finish notice when it ends without reporting — arrives',
52
+ ' as a waking message; do not poll.',
53
+ '- Control the child by that id: `list_agents` for status, `send_message` to continue the same',
54
+ ' conversation (only the agent that started the child may continue it), `interrupt_agent` to',
55
+ ' stop its current turn.',
56
+ '- A foreground wait may be user-promoted (Ctrl+B) to background while it runs: if a tool result',
57
+ ' carries `status: \'async_launched\'` with `backgroundedByUser: true`, treat it exactly like a',
58
+ ' background launch — the result arrives as a later wake; do not poll.',
59
+ '- `subagent_type: "fork"` cannot run in the background (upstream harness issue #2124); use a',
60
+ ' plain background spawn instead.',
61
+ '- Exiting your session drains a background child\'s in-flight turn; its persisted session',
62
+ ' survives and cold-resumes on the next `send_message`.',
63
+ ].join('\n');
64
+ /**
65
+ * Register the static background-loop system-prompt section (same contract the
66
+ * Task tool description teaches, stated once so it survives description
67
+ * trimming). No-op when the system-prompt seam is absent.
68
+ * @param ctx - the plug context.
69
+ * @returns the section disposer, or undefined when the seam is absent.
70
+ */
71
+ export function mountBackgroundSection(ctx) {
72
+ const seam = ctx.get('systemPrompt');
73
+ if (seam === undefined)
74
+ return undefined;
75
+ return seam.section({
76
+ name: BACKGROUND_SECTION_NAME,
77
+ order: BACKGROUND_SECTION_ORDER,
78
+ text: BACKGROUND_SECTION_TEXT,
79
+ });
80
+ }
81
+ /**
82
+ * Mount the Task tool, the agents catalog, and the workspace-instruction
83
+ * strip. Safe when either the tools or the system-prompt seam is absent
84
+ * (the corresponding mount skips); the pre-step listener only needs
85
+ * `ctx.on`, so it mounts regardless.
86
+ * @param ctx - the plug context.
87
+ * @param config - plugin configuration; omitted `resumePins` still arms capture
88
+ * when the resume-pins plugin's `resumePinStore` service is mounted (plan
89
+ * §4.10); with neither, capture is disabled.
90
+ */
91
+ export function apply(ctx, config = {}) {
92
+ const registry = new AgentRegistry();
93
+ const pins = config.resumePins;
94
+ // Durability ordering (plan §4.6): when the resume-pins plugin is mounted
95
+ // its `resumePinStore` is THE store — gate, overlay, and capture must share
96
+ // one cache. Capture only falls back to its own config store/pinsRoot when
97
+ // no plugin-provided store exists (keeps the standalone config path working).
98
+ const sharedStore = ctx.get('resumePinStore');
99
+ // Production wiring (plan §4.10): the preset mounts the resume-pins plugin
100
+ // ahead of this row, so its service store arms capture with NO extra Task
101
+ // config. Explicit `resumePins` config still wins for standalone consumers.
102
+ const capture = pins === undefined
103
+ ? sharedStore !== undefined
104
+ ? new SpawnPinCapture(ctx, sharedStore)
105
+ : undefined
106
+ : new SpawnPinCapture(ctx, sharedStore ?? pins.store ?? new PinStore(pins.pinsRoot));
107
+ registerTaskTool(ctx, registry, capture);
108
+ mountAgentCatalog(ctx, registry);
109
+ mountBackgroundSection(ctx);
110
+ mountStripWorkspaceInstructions(ctx);
111
+ mountSettledNoticeSuppression(ctx);
112
+ publishCollectorRegistry(ctx);
113
+ }
114
+ /**
115
+ * Publish the Slice 3 promotion registry (collector doc §6) as the ROOT-realm
116
+ * `ccCollectorRegistry` service so the TUI busy-branch — a host-plane sibling
117
+ * that cannot resolve realm-interior mounts — queries the SAME live
118
+ * registration map the Task tool's collect path populates. Mirrors the
119
+ * command-agents `ccAgents` publication (CcPlugins pattern): first
120
+ * publication provides the name; a reclaim after an unload takes the slot
121
+ * back via `set`; the unload effect clears it so the TUI degrades to
122
+ * "nothing promotable" instead of holding a dead registry.
123
+ * @param ctx - the plug context.
124
+ */
125
+ function publishCollectorRegistry(ctx) {
126
+ const root = ctx.root;
127
+ const registryService = {
128
+ collectorFor,
129
+ collectorsForSession,
130
+ registeredCollectorCount,
131
+ };
132
+ if (root.get('ccCollectorRegistry', false) === undefined) {
133
+ root.provide('ccCollectorRegistry', registryService);
134
+ }
135
+ else {
136
+ root.set('ccCollectorRegistry', registryService);
137
+ }
138
+ ctx.effect(() => () => {
139
+ if (root.get('ccCollectorRegistry', false) === registryService)
140
+ root.set('ccCollectorRegistry', undefined);
141
+ }, 'cc-subagent-task: clear host-realm ccCollectorRegistry publication on unload');
142
+ }
143
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAA;AACvD,OAAO,EACL,YAAY,EACZ,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,eAAe,EAAyB,MAAM,qBAAqB,CAAA;AAC5E,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAA;AAC5C,OAAO,EAAE,6BAA6B,EAAE,MAAM,uBAAuB,CAAA;AACrE,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAChD,OAAO,EAAE,+BAA+B,EAAE,MAAM,yBAAyB,CAAA;AAEzE,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,EACL,gBAAgB,EAChB,SAAS,EACT,6BAA6B,EAC7B,oCAAoC,EACpC,uBAAuB,GACxB,MAAM,WAAW,CAAA;AAClB,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,YAAY,EACZ,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,EACpB,wBAAwB,EACxB,2BAA2B,EAC3B,8BAA8B,EAC9B,yBAAyB,GAC1B,MAAM,sBAAsB,CAAA;AAM7B,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAChD,OAAO,EACL,+BAA+B,EAC/B,WAAW,EACX,mBAAmB,GACpB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACL,uBAAuB,EACvB,6BAA6B,GAC9B,MAAM,uBAAuB,CAAA;AAE9B,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAEnF,wBAAwB;AACxB,MAAM,CAAC,MAAM,IAAI,GAAG,kBAAkB,CAAA;AAEtC,yDAAyD;AACzD,MAAM,CAAC,MAAM,uBAAuB,GAAG,wBAAwB,CAAA;AAE/D,0EAA0E;AAC1E,MAAM,wBAAwB,GAAG,GAAG,CAAA;AAEpC,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC,yBAAyB;IACzB,EAAE;IACF,iGAAiG;IACjG,6FAA6F;IAC7F,yFAAyF;IACzF,yFAAyF;IACzF,8EAA8E;IAC9E,4FAA4F;IAC5F,6EAA6E;IAC7E,mFAAmF;IACnF,wEAAwE;IACxE,gGAAgG;IAChG,qCAAqC;IACrC,+FAA+F;IAC/F,8FAA8F;IAC9F,0BAA0B;IAC1B,iGAAiG;IACjG,iGAAiG;IACjG,wEAAwE;IACxE,8FAA8F;IAC9F,mCAAmC;IACnC,2FAA2F;IAC3F,yDAAyD;CAC1D,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AAEZ;;;;;;GAMG;AACH,MAAM,UAAU,sBAAsB,CAAC,GAAY;IACjD,MAAM,IAAI,GAAG,GAAG,CAAC,GAAG,CAAC,cAAc,CAEtB,CAAA;IACb,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,SAAS,CAAA;IACxC,OAAO,IAAI,CAAC,OAAO,CAAC;QAClB,IAAI,EAAE,uBAAuB;QAC7B,KAAK,EAAE,wBAAwB;QAC/B,IAAI,EAAE,uBAAuB;KAC9B,CAAC,CAAA;AACJ,CAAC;AAYD;;;;;;;;;GASG;AACH,MAAM,UAAU,KAAK,CAAC,GAAY,EAAE,SAA2B,EAAE;IAC/D,MAAM,QAAQ,GAAG,IAAI,aAAa,EAAE,CAAA;IACpC,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,CAAA;IAC9B,0EAA0E;IAC1E,4EAA4E;IAC5E,2EAA2E;IAC3E,8EAA8E;IAC9E,MAAM,WAAW,GAAG,GAAG,CAAC,GAAG,CAAC,gBAAgB,CAAyB,CAAA;IACrE,2EAA2E;IAC3E,0EAA0E;IAC1E,4EAA4E;IAC5E,MAAM,OAAO,GACX,IAAI,KAAK,SAAS;QAChB,CAAC,CAAC,WAAW,KAAK,SAAS;YACzB,CAAC,CAAC,IAAI,eAAe,CAAC,GAAG,EAAE,WAAW,CAAC;YACvC,CAAC,CAAC,SAAS;QACb,CAAC,CAAC,IAAI,eAAe,CAAC,GAAG,EAAE,WAAW,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAA;IACxF,gBAAgB,CAAC,GAAG,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAA;IACxC,iBAAiB,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAA;IAChC,sBAAsB,CAAC,GAAG,CAAC,CAAA;IAC3B,+BAA+B,CAAC,GAAG,CAAC,CAAA;IACpC,6BAA6B,CAAC,GAAG,CAAC,CAAA;IAClC,wBAAwB,CAAC,GAAG,CAAC,CAAA;AAC/B,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAS,wBAAwB,CAAC,GAAY;IAC5C,MAAM,IAAI,GAAG,GAAG,CAAC,IAIhB,CAAA;IACD,MAAM,eAAe,GAAG;QACtB,YAAY;QACZ,oBAAoB;QACpB,wBAAwB;KACzB,CAAA;IACD,IAAI,IAAI,CAAC,GAAG,CAAC,qBAAqB,EAAE,KAAK,CAAC,KAAK,SAAS,EAAE,CAAC;QACzD,IAAI,CAAC,OAAO,CAAC,qBAAqB,EAAE,eAAe,CAAC,CAAA;IACtD,CAAC;SAAM,CAAC;QACN,IAAI,CAAC,GAAG,CAAC,qBAAqB,EAAE,eAAe,CAAC,CAAA;IAClD,CAAC;IACD,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE;QACpB,IAAI,IAAI,CAAC,GAAG,CAAC,qBAAqB,EAAE,KAAK,CAAC,KAAK,eAAe;YAAE,IAAI,CAAC,GAAG,CAAC,qBAAqB,EAAE,SAAS,CAAC,CAAA;IAC5G,CAAC,EAAE,8EAA8E,CAAC,CAAA;AACpF,CAAC"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Spawn-time pre-activation of allow-listed deferred MCP tools for a Task
3
+ * child. A definition frontmatter naming an explicit deferred MCP tool
4
+ * (`mcp__<server>__<tool>`) leaves the child with a reserved-but-unregistered
5
+ * name; this module activates each such name through the duck-typed
6
+ * `ctx.toolSearch` seam BEFORE the child starts, so the tool is registered —
7
+ * process-globally — by the time the child's first tool assembly runs.
8
+ *
9
+ * Duck-typed so `src/` never imports `@dsh-cc/tool-search` — the
10
+ * package is a test-only devDependency; production stays pluggable (mirrors
11
+ * `packages/mcp/mcp-client/src/defer.ts`).
12
+ *
13
+ * @module @dsh-cc/subagent-task/preload-tools
14
+ */
15
+ import type { Agent } from '@deepseek-ai/dsh-agent';
16
+ import type { ToolRestriction } from '@dsh-cc/claude-code-agents';
17
+ /** The structural subset of the `ctx.toolSearch` seam this module needs. */
18
+ export interface ToolSearchActivateSeam {
19
+ activate(name: string, scope?: unknown): {
20
+ status: 'loaded' | 'already-loaded' | 'denied' | 'unknown';
21
+ name: string;
22
+ reason?: string;
23
+ };
24
+ }
25
+ /** A structural view of the tools registry used to skip registered names. */
26
+ interface ToolsView {
27
+ get(name: string): unknown;
28
+ }
29
+ /** The outcome of one preload pass: what loaded and what did not (with why). */
30
+ export interface PreloadSummary {
31
+ preloaded: string[];
32
+ notices: string[];
33
+ }
34
+ /**
35
+ * Pre-activate the explicit deferred MCP names a definition's `tools:` allows.
36
+ *
37
+ * Selection rule: iterate the RAW allow entries. An entry is a candidate iff
38
+ * it is an explicit exact name (not a wildcard form) that survived into the
39
+ * SANITIZED allow list, is not excluded by the sanitized deny list, and is not
40
+ * already registered (a registered name is eager — activation is pointless).
41
+ * Each candidate is activated for the CALLING agent scope; `loaded` and
42
+ * `already-loaded` are silent successes, `denied`/`unknown` collect a notice
43
+ * and warn. No-op when there is no raw allow list, no calling agent, no
44
+ * sanitized allow list, or (single warn) no toolSearch seam.
45
+ */
46
+ export declare function preloadDeferredFilterTools(opts: {
47
+ /** The definition's RAW toolRestriction (undefined → no-op). */
48
+ raw?: ToolRestriction | undefined;
49
+ /** The SANITIZED toolFilter the child will actually receive. */
50
+ sanitized?: ToolRestriction | undefined;
51
+ /** The duck-typed `ctx.toolSearch` seam (undefined → single warn + no-op). */
52
+ toolSearch?: ToolSearchActivateSeam | undefined;
53
+ /** The calling agent (`exec.agent`, undefined → no-op). */
54
+ agent?: Agent | undefined;
55
+ /** The duck-typed tools view (`ctx.tools`) used to skip registered names. */
56
+ tools?: ToolsView | undefined;
57
+ /** The warn sink (the context logger). */
58
+ warn: (message: string) => void;
59
+ }): PreloadSummary;
60
+ /**
61
+ * Render the preload summary as the Task result-text lines. Empty string when
62
+ * nothing happened (nothing preloaded and no notices).
63
+ */
64
+ export declare function renderPreloadLines(summary: PreloadSummary): string;
65
+ export {};
66
+ //# sourceMappingURL=preload-tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preload-tools.d.ts","sourceRoot":"","sources":["../src/preload-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAA;AACnD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAA;AAKjE,4EAA4E;AAC5E,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG;QACvC,MAAM,EAAE,QAAQ,GAAG,gBAAgB,GAAG,QAAQ,GAAG,SAAS,CAAA;QAC1D,IAAI,EAAE,MAAM,CAAA;QACZ,MAAM,CAAC,EAAE,MAAM,CAAA;KAChB,CAAA;CACF;AAED,6EAA6E;AAC7E,UAAU,SAAS;IACjB,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;CAC3B;AAED,gFAAgF;AAChF,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB,OAAO,EAAE,MAAM,EAAE,CAAA;CAClB;AAcD;;;;;;;;;;;GAWG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE;IAC/C,gEAAgE;IAChE,GAAG,CAAC,EAAE,eAAe,GAAG,SAAS,CAAA;IACjC,gEAAgE;IAChE,SAAS,CAAC,EAAE,eAAe,GAAG,SAAS,CAAA;IACvC,8EAA8E;IAC9E,UAAU,CAAC,EAAE,sBAAsB,GAAG,SAAS,CAAA;IAC/C,2DAA2D;IAC3D,KAAK,CAAC,EAAE,KAAK,GAAG,SAAS,CAAA;IACzB,6EAA6E;IAC7E,KAAK,CAAC,EAAE,SAAS,GAAG,SAAS,CAAA;IAC7B,0CAA0C;IAC1C,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CAChC,GAAG,cAAc,CAuCjB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM,CASlE"}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Spawn-time pre-activation of allow-listed deferred MCP tools for a Task
3
+ * child. A definition frontmatter naming an explicit deferred MCP tool
4
+ * (`mcp__<server>__<tool>`) leaves the child with a reserved-but-unregistered
5
+ * name; this module activates each such name through the duck-typed
6
+ * `ctx.toolSearch` seam BEFORE the child starts, so the tool is registered —
7
+ * process-globally — by the time the child's first tool assembly runs.
8
+ *
9
+ * Duck-typed so `src/` never imports `@dsh-cc/tool-search` — the
10
+ * package is a test-only devDependency; production stays pluggable (mirrors
11
+ * `packages/mcp/mcp-client/src/defer.ts`).
12
+ *
13
+ * @module @dsh-cc/subagent-task/preload-tools
14
+ */
15
+ /** The MCP public-name prefix every bridged MCP tool carries on `ctx.tools`. */
16
+ const MCP_PUBLIC_PREFIX = 'mcp__';
17
+ /**
18
+ * Decide whether a RAW allow entry is a wildcard form (`mcp__<server>` bare,
19
+ * `mcp__<anything>__*` postfix, or the bare `mcp__`). Wildcards are
20
+ * restrict-only: their expansions are preloaded by name, never via this
21
+ * module (the expansion set is the sanitize step's business).
22
+ */
23
+ function isWildcardEntry(name) {
24
+ if (!name.startsWith(MCP_PUBLIC_PREFIX))
25
+ return false;
26
+ const rest = name.slice(MCP_PUBLIC_PREFIX.length);
27
+ return rest.length === 0 || rest.endsWith('__*') || !rest.includes('__');
28
+ }
29
+ /**
30
+ * Pre-activate the explicit deferred MCP names a definition's `tools:` allows.
31
+ *
32
+ * Selection rule: iterate the RAW allow entries. An entry is a candidate iff
33
+ * it is an explicit exact name (not a wildcard form) that survived into the
34
+ * SANITIZED allow list, is not excluded by the sanitized deny list, and is not
35
+ * already registered (a registered name is eager — activation is pointless).
36
+ * Each candidate is activated for the CALLING agent scope; `loaded` and
37
+ * `already-loaded` are silent successes, `denied`/`unknown` collect a notice
38
+ * and warn. No-op when there is no raw allow list, no calling agent, no
39
+ * sanitized allow list, or (single warn) no toolSearch seam.
40
+ */
41
+ export function preloadDeferredFilterTools(opts) {
42
+ const { raw, sanitized, toolSearch, agent, tools, warn } = opts;
43
+ if (raw?.allow === undefined || raw.allow.length === 0)
44
+ return { preloaded: [], notices: [] };
45
+ if (agent === undefined)
46
+ return { preloaded: [], notices: [] };
47
+ if (toolSearch === undefined) {
48
+ warn('cc-task: no toolSearch service is mounted, so deferred MCP tools named explicitly in a '
49
+ + 'subagent toolFilter cannot be pre-activated at spawn; they stay searchable-only');
50
+ return { preloaded: [], notices: [] };
51
+ }
52
+ const sanitizedAllow = sanitized?.allow;
53
+ if (sanitizedAllow === undefined)
54
+ return { preloaded: [], notices: [] };
55
+ const sanitizedDeny = sanitized?.deny;
56
+ const preloaded = [];
57
+ const notices = [];
58
+ for (const name of raw.allow) {
59
+ // Deferred tools live on the toolSearch registry, which in this
60
+ // composition only ever holds MCP tools; a non-MCP harness name (read,
61
+ // bash, reserved rows) can never be a deferred entry, and probing it
62
+ // would only farm spurious "unknown" notices.
63
+ if (!name.startsWith(MCP_PUBLIC_PREFIX))
64
+ continue;
65
+ if (isWildcardEntry(name))
66
+ continue;
67
+ if (!sanitizedAllow.includes(name))
68
+ continue;
69
+ if (sanitizedDeny !== undefined && sanitizedDeny.includes(name))
70
+ continue;
71
+ if (tools !== undefined && tools.get(name) !== undefined)
72
+ continue;
73
+ const outcome = toolSearch.activate(name, agent);
74
+ if (outcome.status === 'loaded' || outcome.status === 'already-loaded') {
75
+ preloaded.push(name);
76
+ continue;
77
+ }
78
+ const reason = outcome.status === 'unknown'
79
+ ? 'unknown to the toolSearch registry'
80
+ : outcome.reason?.replace(/^"[^"]*" is /, '');
81
+ const notice = reason !== undefined ? `${name} (${reason})` : `${name}`;
82
+ notices.push(notice);
83
+ warn(`cc-task: could not pre-activate deferred tool "${name}" for a subagent: ${notice}`);
84
+ }
85
+ return { preloaded, notices };
86
+ }
87
+ /**
88
+ * Render the preload summary as the Task result-text lines. Empty string when
89
+ * nothing happened (nothing preloaded and no notices).
90
+ */
91
+ export function renderPreloadLines(summary) {
92
+ const lines = [];
93
+ if (summary.preloaded.length > 0) {
94
+ lines.push(`Preloaded deferred tools for child: ${summary.preloaded.join(', ')}`);
95
+ }
96
+ if (summary.notices.length > 0) {
97
+ lines.push(`Not preloaded: ${summary.notices.join(', ')}`);
98
+ }
99
+ return lines.join('\n');
100
+ }
101
+ //# sourceMappingURL=preload-tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preload-tools.js","sourceRoot":"","sources":["../src/preload-tools.ts"],"names":[],"mappings":"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"}