@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,99 @@
1
+ /**
2
+ * The per-agent "Available subagents" system-prompt section.
3
+ *
4
+ * A single global section (mount once on the root context) serves every
5
+ * agent: the text callback receives the assembling agent through
6
+ * `AssembleContext.scope` (the agent loop passes `scope: agent`) and renders
7
+ * that agent's workspace `.claude/agents` definitions. Because `text()` is a
8
+ * synchronous callback it cannot `await` discovery, so the section renders
9
+ * from a snapshot cache that a fire-and-forget `registry.ensure(cwd)` populates
10
+ * in the background. An `system-prompt/assemble` waterfall listener
11
+ * reconciles the placeholder in the same assembly: for a scope whose
12
+ * snapshot has not landed it joins the in-flight discovery (bounded), stores
13
+ * the snapshot, and re-renders the section with the same render function, so
14
+ * the FIRST assembly already carries the real catalog and no later request
15
+ * sees a different prefix; on timeout the placeholder ships and the
16
+ * `system-prompt/change` path lands the catalog on a later assembly. When a
17
+ * workspace defines no agents of its own (or there is no agent to scope to)
18
+ * the section still lists the bundled agents, or renders an empty string when
19
+ * there is nothing to scope to.
20
+ *
21
+ * @module @dsh-cc/subagent-task/catalog
22
+ */
23
+ import type { Context } from '@deepseek-ai/cordis';
24
+ import type { AgentDefinition } from '@dsh-cc/claude-code-agents';
25
+ import type { AgentRegistry } from './registry.ts';
26
+ /** Default order slot for the catalog section (tool guidance owns 100–199). */
27
+ export declare const CATALOG_SECTION_ORDER = 110;
28
+ /** The section's unique name. */
29
+ export declare const CATALOG_SECTION_NAME = "cc:subagent-catalog";
30
+ /**
31
+ * Owner of the cached catalog section text. Holds one sorted definition list
32
+ * per workspace root so the synchronous section provider can compose the text,
33
+ * and fills each root's snapshot from a background `registry.ensure`.
34
+ */
35
+ export declare class AgentCatalogSection {
36
+ private readonly ctx;
37
+ private readonly registry;
38
+ /** Sorted definitions per workspace root, populated once discovery lands. */
39
+ private readonly snapshot;
40
+ /** Roots whose background discovery has already been kicked off. */
41
+ private readonly seen;
42
+ /**
43
+ * Create a catalog cache holder bound to a registry.
44
+ * @param ctx - the host context whose `system-prompt` seam and
45
+ * `system-prompt/change` channel drive refresh.
46
+ * @param registry - the per-workspace definition cache to load from.
47
+ */
48
+ constructor(ctx: Context, registry: AgentRegistry);
49
+ /** Register the catalog section plus its assemble-waterfall reconciliation.
50
+ *
51
+ * The seam is fetched with `ctx.get` rather than the `ctx.systemPrompt`
52
+ * property: cordis strict mode rejects property access for services the
53
+ * plugin never declared via `inject`, and a throw here fails the whole
54
+ * preset mount (every session in the preset dies at creation).
55
+ *
56
+ * The waterfall listener removes the first-assembly placeholder jitter:
57
+ * `systemPrompt.assemble()` runs BEFORE the agent pre-step, so the section
58
+ * text callback cannot await discovery — but the waterfall can. After the
59
+ * base assembly, a scope whose snapshot has not landed yet joins the
60
+ * in-flight `registry.ensure` (bounded by {@linkcode READINESS_BUDGET_MS}),
61
+ * stores the snapshot itself, and re-renders the section with the same
62
+ * render function the section callback uses, so the first assembly already
63
+ * carries the real catalog and request 2 of the first turn keeps its
64
+ * prefix. Timeout or failure keeps the placeholder (the background
65
+ * `system-prompt/change` path still lands it on a later assembly).
66
+ * @returns the exact Cordis effect disposer for the section and listener. */
67
+ start(): () => void;
68
+ /**
69
+ * Compose the catalog text for the agent behind an assemble scope.
70
+ * Kicks the root's background discovery on first sight; renders from the
71
+ * snapshot until it lands. Returns '' (section drops out) when there is no
72
+ * agent or the snapshot holds no definitions.
73
+ */
74
+ private render;
75
+ /**
76
+ * Kick background discovery for one workspace root exactly once. When it
77
+ * lands, the snapshot is populated and `system-prompt/change` fires so the
78
+ * next assembly renders the catalog. Failures are swallowed: a missing or
79
+ * unreadable agents directory simply leaves an empty snapshot (the section
80
+ * stays absent, which is itself the correct "no agents" rendering).
81
+ * @param root - the workspace root.
82
+ */
83
+ private ensureDefs;
84
+ }
85
+ /**
86
+ * Mount the single global catalog section.
87
+ * @param ctx - the plug context carrying the `systemPrompt` seam.
88
+ * @param registry - the per-workspace definition cache.
89
+ * @returns the exact Cordis effect disposer, or undefined when the seam is absent.
90
+ */
91
+ export declare function mountAgentCatalog(ctx: Context, registry: AgentRegistry): (() => void) | undefined;
92
+ /**
93
+ * Compose the section text from the sorted definitions. Returns '' when empty
94
+ * so the section contributes nothing to the prompt.
95
+ * @param defs - the sorted definitions to list.
96
+ * @returns the rendered catalog, or '' when no definitions are available.
97
+ */
98
+ export declare function renderCatalog(defs: readonly AgentDefinition[]): string;
99
+ //# sourceMappingURL=catalog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAElD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAA;AAEjE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAElD,+EAA+E;AAC/E,eAAO,MAAM,qBAAqB,MAAM,CAAA;AAExC,iCAAiC;AACjC,eAAO,MAAM,oBAAoB,wBAAwB,CAAA;AAoCzD;;;;GAIG;AACH,qBAAa,mBAAmB;IAa5B,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAb3B,6EAA6E;IAC7E,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgD;IACzE,oEAAoE;IACpE,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAoB;IAEzC;;;;;OAKG;gBAEgB,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE,aAAa;IAG1C;;;;;;;;;;;;;;;;;iFAiB6E;IAC7E,KAAK,IAAI,MAAM,IAAI;IA0CnB;;;;;OAKG;IACH,OAAO,CAAC,MAAM;IAQd;;;;;;;OAOG;IACH,OAAO,CAAC,UAAU;CAcnB;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE,aAAa,GACtB,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAG1B;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,SAAS,eAAe,EAAE,GAAG,MAAM,CAUtE"}
package/lib/catalog.js ADDED
@@ -0,0 +1,197 @@
1
+ /**
2
+ * The per-agent "Available subagents" system-prompt section.
3
+ *
4
+ * A single global section (mount once on the root context) serves every
5
+ * agent: the text callback receives the assembling agent through
6
+ * `AssembleContext.scope` (the agent loop passes `scope: agent`) and renders
7
+ * that agent's workspace `.claude/agents` definitions. Because `text()` is a
8
+ * synchronous callback it cannot `await` discovery, so the section renders
9
+ * from a snapshot cache that a fire-and-forget `registry.ensure(cwd)` populates
10
+ * in the background. An `system-prompt/assemble` waterfall listener
11
+ * reconciles the placeholder in the same assembly: for a scope whose
12
+ * snapshot has not landed it joins the in-flight discovery (bounded), stores
13
+ * the snapshot, and re-renders the section with the same render function, so
14
+ * the FIRST assembly already carries the real catalog and no later request
15
+ * sees a different prefix; on timeout the placeholder ships and the
16
+ * `system-prompt/change` path lands the catalog on a later assembly. When a
17
+ * workspace defines no agents of its own (or there is no agent to scope to)
18
+ * the section still lists the bundled agents, or renders an empty string when
19
+ * there is nothing to scope to.
20
+ *
21
+ * @module @dsh-cc/subagent-task/catalog
22
+ */
23
+ import { cwdOf } from '@dsh-cc/memory';
24
+ /** Default order slot for the catalog section (tool guidance owns 100–199). */
25
+ export const CATALOG_SECTION_ORDER = 110;
26
+ /** The section's unique name. */
27
+ export const CATALOG_SECTION_NAME = 'cc:subagent-catalog';
28
+ /**
29
+ * How long the assemble waterfall may wait for a workspace's in-flight
30
+ * discovery before giving up and shipping the placeholder. Bounded so a slow
31
+ * or wedged scan delays the first request by at most this much; the
32
+ * background `system-prompt/change` path remains the fallback.
33
+ */
34
+ const READINESS_BUDGET_MS = 500;
35
+ /**
36
+ * Join `promise`, but reject after `ms` milliseconds either way. The loser
37
+ * keeps running in the background (its eventual rejection is contained); the
38
+ * caller gets a rejection to degrade on.
39
+ */
40
+ function withinBudget(promise, ms) {
41
+ let timer;
42
+ const budget = new Promise((_resolve, reject) => {
43
+ timer = setTimeout(() => reject(new Error(`readiness budget of ${ms}ms expired`)), ms);
44
+ });
45
+ budget.catch(() => { });
46
+ return Promise.race([promise, budget]).finally(() => clearTimeout(timer));
47
+ }
48
+ /**
49
+ * Extract the assembling agent from an `AssembleContext`. The agent loop
50
+ * assembles with `scope: agent` (a runtime contract; `ScopeKey` is opaque),
51
+ * so the scope IS the agent whenever a session drives the assembly.
52
+ */
53
+ function agentFromScope(scope) {
54
+ if (typeof scope === 'object' && scope !== null && 'session' in scope) {
55
+ return scope;
56
+ }
57
+ return undefined;
58
+ }
59
+ /**
60
+ * Owner of the cached catalog section text. Holds one sorted definition list
61
+ * per workspace root so the synchronous section provider can compose the text,
62
+ * and fills each root's snapshot from a background `registry.ensure`.
63
+ */
64
+ export class AgentCatalogSection {
65
+ ctx;
66
+ registry;
67
+ /** Sorted definitions per workspace root, populated once discovery lands. */
68
+ snapshot = new Map();
69
+ /** Roots whose background discovery has already been kicked off. */
70
+ seen = new Set();
71
+ /**
72
+ * Create a catalog cache holder bound to a registry.
73
+ * @param ctx - the host context whose `system-prompt` seam and
74
+ * `system-prompt/change` channel drive refresh.
75
+ * @param registry - the per-workspace definition cache to load from.
76
+ */
77
+ constructor(ctx, registry) {
78
+ this.ctx = ctx;
79
+ this.registry = registry;
80
+ }
81
+ /** Register the catalog section plus its assemble-waterfall reconciliation.
82
+ *
83
+ * The seam is fetched with `ctx.get` rather than the `ctx.systemPrompt`
84
+ * property: cordis strict mode rejects property access for services the
85
+ * plugin never declared via `inject`, and a throw here fails the whole
86
+ * preset mount (every session in the preset dies at creation).
87
+ *
88
+ * The waterfall listener removes the first-assembly placeholder jitter:
89
+ * `systemPrompt.assemble()` runs BEFORE the agent pre-step, so the section
90
+ * text callback cannot await discovery — but the waterfall can. After the
91
+ * base assembly, a scope whose snapshot has not landed yet joins the
92
+ * in-flight `registry.ensure` (bounded by {@linkcode READINESS_BUDGET_MS}),
93
+ * stores the snapshot itself, and re-renders the section with the same
94
+ * render function the section callback uses, so the first assembly already
95
+ * carries the real catalog and request 2 of the first turn keeps its
96
+ * prefix. Timeout or failure keeps the placeholder (the background
97
+ * `system-prompt/change` path still lands it on a later assembly).
98
+ * @returns the exact Cordis effect disposer for the section and listener. */
99
+ start() {
100
+ const seam = this.ctx.get('systemPrompt');
101
+ const disposeSection = seam === undefined
102
+ ? () => { }
103
+ : seam.section({
104
+ name: CATALOG_SECTION_NAME,
105
+ order: CATALOG_SECTION_ORDER,
106
+ text: (context) => this.render(context.scope),
107
+ });
108
+ const disposeListener = this.ctx.on('system-prompt/assemble', async (_assembly, context, next) => {
109
+ const result = await next();
110
+ const agent = agentFromScope(context.scope);
111
+ if (agent === undefined)
112
+ return result;
113
+ const root = cwdOf(agent);
114
+ if (this.snapshot.has(root))
115
+ return result;
116
+ try {
117
+ const defs = await withinBudget(this.registry.ensure(root), READINESS_BUDGET_MS);
118
+ this.snapshot.set(root, [...defs.values()].sort((a, b) => a.agentType.localeCompare(b.agentType)));
119
+ }
120
+ catch (error) {
121
+ this.ctx.logger.warn(`subagent catalog: discovery for ${root} did not land within ${READINESS_BUDGET_MS}ms of the first assembly: ${String(error)}; keeping the placeholder`);
122
+ return result;
123
+ }
124
+ return {
125
+ ...result,
126
+ sections: result.sections.map(section => section.name === CATALOG_SECTION_NAME
127
+ ? { ...section, text: this.render(agent) }
128
+ : section),
129
+ };
130
+ });
131
+ return () => {
132
+ disposeListener();
133
+ disposeSection();
134
+ };
135
+ }
136
+ /**
137
+ * Compose the catalog text for the agent behind an assemble scope.
138
+ * Kicks the root's background discovery on first sight; renders from the
139
+ * snapshot until it lands. Returns '' (section drops out) when there is no
140
+ * agent or the snapshot holds no definitions.
141
+ */
142
+ render(scope) {
143
+ const agent = agentFromScope(scope);
144
+ if (agent === undefined)
145
+ return '';
146
+ const root = cwdOf(agent);
147
+ this.ensureDefs(root);
148
+ return renderCatalog(this.snapshot.get(root) ?? []);
149
+ }
150
+ /**
151
+ * Kick background discovery for one workspace root exactly once. When it
152
+ * lands, the snapshot is populated and `system-prompt/change` fires so the
153
+ * next assembly renders the catalog. Failures are swallowed: a missing or
154
+ * unreadable agents directory simply leaves an empty snapshot (the section
155
+ * stays absent, which is itself the correct "no agents" rendering).
156
+ * @param root - the workspace root.
157
+ */
158
+ ensureDefs(root) {
159
+ if (this.seen.has(root))
160
+ return;
161
+ this.seen.add(root);
162
+ void this.registry.ensure(root).then(defs => {
163
+ this.snapshot.set(root, [...defs.values()].sort((a, b) => a.agentType.localeCompare(b.agentType)));
164
+ this.ctx.emit('system-prompt/change');
165
+ }, () => { });
166
+ }
167
+ }
168
+ /**
169
+ * Mount the single global catalog section.
170
+ * @param ctx - the plug context carrying the `systemPrompt` seam.
171
+ * @param registry - the per-workspace definition cache.
172
+ * @returns the exact Cordis effect disposer, or undefined when the seam is absent.
173
+ */
174
+ export function mountAgentCatalog(ctx, registry) {
175
+ if (ctx.get('systemPrompt') === undefined)
176
+ return undefined;
177
+ return new AgentCatalogSection(ctx, registry).start();
178
+ }
179
+ /**
180
+ * Compose the section text from the sorted definitions. Returns '' when empty
181
+ * so the section contributes nothing to the prompt.
182
+ * @param defs - the sorted definitions to list.
183
+ * @returns the rendered catalog, or '' when no definitions are available.
184
+ */
185
+ export function renderCatalog(defs) {
186
+ if (defs.length === 0)
187
+ return '';
188
+ const lines = [
189
+ '## Available subagents',
190
+ '',
191
+ ...defs.map(def => `- ${def.agentType} — ${def.whenToUse}`),
192
+ '',
193
+ 'To delegate to one, pass its name as the `subagent_type` argument of the Task tool.',
194
+ ];
195
+ return lines.join('\n');
196
+ }
197
+ //# sourceMappingURL=catalog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog.js","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,126 @@
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
+ import type { Agent } from '@deepseek-ai/dsh-agent';
22
+ /** One output content block of the child's closing message. */
23
+ export interface EpochOutputBlock {
24
+ type: string;
25
+ text?: string;
26
+ }
27
+ /** The child's terminal as observed on `subagent/end`. */
28
+ export interface EpochTerminal {
29
+ stopReason: string;
30
+ output?: readonly EpochOutputBlock[];
31
+ }
32
+ /**
33
+ * The collect race outcome: the epoch's real terminal, or the prompt
34
+ * synthetic `aborted` (abort never awaits child quiescence — §4).
35
+ */
36
+ export type EpochOutcome = ({
37
+ kind: 'epoch';
38
+ } & EpochTerminal) | {
39
+ kind: 'aborted';
40
+ stopReason: 'aborted';
41
+ } | {
42
+ kind: 'promoted';
43
+ };
44
+ /** Duck-typed interrupt surface on the subagents seam (harness `interrupt`). */
45
+ export interface SubagentsInterruptLike {
46
+ /**
47
+ * Interrupt a live child's current turn. Admission is synchronous; an
48
+ * absent/settled target is an accepted no-op. Absent on a seam without the
49
+ * capability → the collector degrades to a non-interrupting prompt resolve.
50
+ */
51
+ interrupt?(childId: string, authority: {
52
+ kind: 'ancestor';
53
+ agent: Agent;
54
+ }): void;
55
+ }
56
+ /** Duck-typed event bus (cordis `ctx.on`) the lifecycle events arrive on. */
57
+ export interface EpochEventBus {
58
+ on(event: string, listener: (info: Record<string, unknown>) => void): (() => void) | void;
59
+ }
60
+ /** Instrumentation for tests and diagnostics: live watch entries. */
61
+ export declare function epochWatchSize(): number;
62
+ /** Mark a child's settlement notice for drop (done at collect reservation). */
63
+ export declare function markCollectedForSuppression(childId: string): void;
64
+ /**
65
+ * Un-mark a child (a promoted child is never suppressed — its notice flows
66
+ * normally; Slice 3's `promote()` calls this).
67
+ */
68
+ export declare function releaseCollectedForSuppression(childId: string): void;
69
+ /** Whether a child's settlement notice is currently marked for drop. */
70
+ export declare function isCollectedForSuppression(childId: string): boolean;
71
+ /** A live foreground collect the TUI can promote (Ctrl+B) or abort. */
72
+ export interface CollectorRegistration {
73
+ childId: string;
74
+ /** Release the wait to background: un-suppress + resolve `async_launched`. */
75
+ promote(): void;
76
+ /** Interrupt exactly once + prompt synthetic resolve (§4). */
77
+ abort(): void;
78
+ }
79
+ /** Registry key: parentSessionId + toolCallToken. */
80
+ export declare function collectorKey(parentSessionId: string, toolCallToken: string): string;
81
+ export declare function registerCollector(key: string, handle: CollectorRegistration): void;
82
+ export declare function unregisterCollector(key: string): void;
83
+ export declare function collectorFor(key: string): CollectorRegistration | undefined;
84
+ /**
85
+ * All live registrations of ONE parent session (the TUI busy-branch Ctrl+B
86
+ * query, F9): every armed collect whose compound key starts with the session
87
+ * prefix. A promoted/settled/aborted collect unregisters itself, so an
88
+ * armed entry here is exactly a promotable foreground wait.
89
+ */
90
+ export declare function collectorsForSession(parentSessionId: string): CollectorRegistration[];
91
+ /** Instrumentation for tests and diagnostics: live registration count. */
92
+ export declare function registeredCollectorCount(): number;
93
+ export interface CollectFirstEpochDeps {
94
+ /** The cordis context (bus) the subagent lifecycle events arrive on. */
95
+ bus: EpochEventBus;
96
+ /** The preallocated durable child id the watch is keyed by. */
97
+ childId: string;
98
+ /** The calling agent — the interrupt authority credential. */
99
+ agent: Agent;
100
+ /** The tool call's signal; abort maps to the §4 semantics. */
101
+ signal: AbortSignal;
102
+ /** The subagents seam, probed for `interrupt` (M5). */
103
+ subagents?: SubagentsInterruptLike;
104
+ /**
105
+ * Performs the actual `startContinuable` call. The reservation is placed
106
+ * BEFORE this runs (subscribe-before-start, §8); a throw from it releases
107
+ * the reservation and the suppression mark (tombstone parity lives in the
108
+ * caller, mirroring `startBackground`).
109
+ */
110
+ start: () => Promise<void>;
111
+ /**
112
+ * The collecting parent session id — half of the promotion-registry key
113
+ * (§6). Both this and {@link CollectFirstEpochDeps.toolCallToken} must be
114
+ * provided for the collect to register itself as promotable; without them
115
+ * the collect is a plain Slice 2 collect (never discoverable by the TUI).
116
+ */
117
+ parentSessionId?: string;
118
+ /** The tool-call token — the other half of the registry key (§6). */
119
+ toolCallToken?: string;
120
+ }
121
+ /**
122
+ * Collect a continuable child's first epoch inline. Never awaits the parent's
123
+ * inbound messages — only bus events (§3 deadlock rule).
124
+ */
125
+ export declare function collectFirstEpoch(deps: CollectFirstEpochDeps): Promise<EpochOutcome>;
126
+ //# sourceMappingURL=epoch-collector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"epoch-collector.d.ts","sourceRoot":"","sources":["../src/epoch-collector.ts"],"names":[],"mappings":"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"}