@deepseek-ai/dsh-subagent 0.0.1-rc.1

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 (41) hide show
  1. package/LICENSE +28 -0
  2. package/README.i18n.yaml +6 -0
  3. package/README.md +132 -0
  4. package/README.zh.md +132 -0
  5. package/lib/index.js +2392 -0
  6. package/lib/invariant.js +76 -0
  7. package/lib/types/activation-setup-registry.d.ts +57 -0
  8. package/lib/types/activation-setup-registry.js +148 -0
  9. package/lib/types/child-agent.d.ts +139 -0
  10. package/lib/types/child-agent.js +169 -0
  11. package/lib/types/client.d.ts +7 -0
  12. package/lib/types/client.js +7 -0
  13. package/lib/types/continuation.d.ts +375 -0
  14. package/lib/types/continuation.js +951 -0
  15. package/lib/types/depth.d.ts +31 -0
  16. package/lib/types/depth.js +39 -0
  17. package/lib/types/descriptor-seed.d.ts +21 -0
  18. package/lib/types/descriptor-seed.js +24 -0
  19. package/lib/types/descriptor.d.ts +139 -0
  20. package/lib/types/descriptor.js +189 -0
  21. package/lib/types/error.d.ts +11 -0
  22. package/lib/types/error.js +14 -0
  23. package/lib/types/index.d.ts +278 -0
  24. package/lib/types/index.js +338 -0
  25. package/lib/types/invariant.d.ts +13 -0
  26. package/lib/types/invariant.js +91 -0
  27. package/lib/types/lifecycle.d.ts +93 -0
  28. package/lib/types/lifecycle.js +169 -0
  29. package/lib/types/list-children.d.ts +112 -0
  30. package/lib/types/list-children.js +316 -0
  31. package/lib/types/out-of-process.d.ts +115 -0
  32. package/lib/types/out-of-process.js +181 -0
  33. package/lib/types/projection-types.d.ts +60 -0
  34. package/lib/types/projection-types.js +7 -0
  35. package/lib/types/projection.d.ts +48 -0
  36. package/lib/types/projection.js +135 -0
  37. package/lib/types/run-settlement.d.ts +17 -0
  38. package/lib/types/run-settlement.js +59 -0
  39. package/lib/types/types.d.ts +293 -0
  40. package/lib/types/types.js +19 -0
  41. package/package.json +106 -0
@@ -0,0 +1,91 @@
1
+ /** Package-owned subagent registry and lifecycle invariants. @module @deepseek-ai/dsh-subagent/invariant */
2
+ const PACKAGE_NAME = '@deepseek-ai/dsh-subagent';
3
+ /** Cordis companion plugin name. */
4
+ export const name = 'subagent-invariant';
5
+ /** Service required before the companion can reserve package ownership. */
6
+ export const inject = ['invariants'];
7
+ /** Assert that a terminal lifecycle payload matches its start identity. */
8
+ function validateRunEnd(start, end, fail) {
9
+ if (start.provider !== end.provider || start.id !== end.id || start.local !== end.local) {
10
+ fail(`subagent/end identity diverges from subagent/start for run ${JSON.stringify(end.runId)}`);
11
+ }
12
+ }
13
+ /** Install provider-registry and start/end pairing checks. */
14
+ const install = Object.assign((ctx, fail) => {
15
+ const providers = new Set(ctx.subagents.list());
16
+ const runs = new Map();
17
+ const stagedProviders = new WeakSet();
18
+ const stagedRemovals = new Set();
19
+ const stagedStarts = new WeakSet();
20
+ const stagedEnds = new WeakSet();
21
+ ctx.on('internal/dispatch', (_mode, eventName, args) => {
22
+ if (eventName === 'subagent/provider-added') {
23
+ const provider = args[0];
24
+ if (provider.name.length === 0)
25
+ fail('subagent provider names must be non-empty');
26
+ if (providers.has(provider.name))
27
+ fail(`subagent/provider-added repeated ${JSON.stringify(provider.name)}`);
28
+ stagedProviders.add(provider);
29
+ return;
30
+ }
31
+ if (eventName === 'subagent/provider-removed') {
32
+ const providerName = args[0];
33
+ if (!providers.has(providerName))
34
+ fail(`subagent/provider-removed names unknown provider ${JSON.stringify(providerName)}`);
35
+ stagedRemovals.add(providerName);
36
+ return;
37
+ }
38
+ if (eventName === 'subagent/start') {
39
+ const info = args[0];
40
+ // Provider availability is an admission-time relationship. A published
41
+ // one-shot run may outlive provider removal, and a cold-resumed Activation
42
+ // records the initial provider name without dispatching through it.
43
+ if (info.provider.length === 0 || String(info.runId).length === 0 || String(info.id).length === 0) {
44
+ fail('subagent/start provider, runId, and child id must be non-empty');
45
+ }
46
+ if (runs.has(info.runId))
47
+ fail(`subagent/start repeated run id ${JSON.stringify(info.runId)}`);
48
+ stagedStarts.add(info);
49
+ return;
50
+ }
51
+ if (eventName !== 'subagent/end')
52
+ return;
53
+ const info = args[0];
54
+ const start = runs.get(info.runId);
55
+ if (start === undefined)
56
+ fail(`subagent/end has no matching subagent/start for run ${JSON.stringify(info.runId)}`);
57
+ validateRunEnd(start, info, fail);
58
+ stagedEnds.add(info);
59
+ }, { global: true });
60
+ ctx.on('subagent/provider-added', (provider) => {
61
+ /* v8 ignore next -- internal/dispatch stages the same provider object */
62
+ if (!stagedProviders.delete(provider))
63
+ return;
64
+ providers.add(provider.name);
65
+ }, { global: true });
66
+ ctx.on('subagent/provider-removed', (providerName) => {
67
+ /* v8 ignore next -- internal/dispatch stages the same provider name */
68
+ if (!stagedRemovals.delete(providerName))
69
+ return;
70
+ providers.delete(providerName);
71
+ }, { global: true });
72
+ ctx.on('subagent/start', (info) => {
73
+ /* v8 ignore next -- internal/dispatch stages the same lifecycle object */
74
+ if (!stagedStarts.delete(info))
75
+ return;
76
+ runs.set(info.runId, info);
77
+ }, { global: true });
78
+ ctx.on('subagent/end', (info) => {
79
+ /* v8 ignore next -- internal/dispatch stages the same lifecycle object */
80
+ if (!stagedEnds.delete(info))
81
+ return;
82
+ runs.delete(info.runId);
83
+ }, { global: true });
84
+ }, { inject: ['subagents'] });
85
+ /**
86
+ * Register the subagent invariant companion.
87
+ * @param ctx - Cordis context carrying the invariant service.
88
+ * @returns the installed registration's disposer after setup succeeds.
89
+ */
90
+ export const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
91
+ //# sourceMappingURL=invariant.js.map
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Lifecycle-edge publication for both subagent shapes: the contained emitter,
3
+ * the one-shot run observer, and the continuable Activation observer.
4
+ *
5
+ * The public payload contracts ({@link SubagentRunInfo},
6
+ * {@link SubagentRunEndInfo}) live in `./types.ts` with the rest of the seam's
7
+ * consumer-facing types; this module owns only the implementation and the
8
+ * package-private {@link ActivationObserver} the continuation manager consumes.
9
+ * Keeping the internal control interface out of the published surface is
10
+ * deliberate: the observer's `start`/`capture`/`settle` ordering is a contract
11
+ * between this module and one in-package caller, not something a plugin may
12
+ * depend on.
13
+ *
14
+ * @module @deepseek-ai/dsh-subagent/lifecycle
15
+ */
16
+ import type { Context } from '@deepseek-ai/cordis';
17
+ import type { Agent } from '@deepseek-ai/dsh-agent';
18
+ import type { SessionId } from '@deepseek-ai/dsh-session';
19
+ import type { SubagentRun, SubagentRunEndInfo, SubagentRunInfo } from './types.ts';
20
+ /**
21
+ * Lifecycle observer for one Activation's residency epoch, so continuable
22
+ * children emit the same start/end pair as one-shot runs. Package-private: the
23
+ * continuation manager is the only consumer, and its call ordering is an
24
+ * in-package contract rather than a published extension point.
25
+ */
26
+ export interface ActivationObserver {
27
+ /**
28
+ * Publish the start edge once the epoch is resident.
29
+ * @param child - the resident child agent, whose log suffix bounds this epoch.
30
+ */
31
+ start(child: Agent): void;
32
+ /**
33
+ * Snapshot the child-dependent terminal facts while the child is still
34
+ * registered, because handle disposal unregisters it and consumers resolve it
35
+ * to read the child's own log and scope.
36
+ * @param child - the quiescent child agent about to be released.
37
+ */
38
+ capture(child: Agent): void;
39
+ /**
40
+ * Publish the terminal edge exactly once, pairing this epoch's {@link start},
41
+ * after the disposal outcome is known. Called only for a resident epoch: a
42
+ * failure before residency publishes no edge, because inventing one would
43
+ * report a lifecycle the child never had.
44
+ * @param failure - the teardown or durability failure, or `undefined` on success.
45
+ */
46
+ settle(failure: unknown): void;
47
+ }
48
+ /**
49
+ * Publish one lifecycle edge with per-listener exception containment. Run edges
50
+ * carry the delegating parent that keys scoped dispatch; provider removal has no
51
+ * parent carrier and reaches listeners unscoped.
52
+ *
53
+ * The service owns this closure because scoped dispatch keys its carrier by the
54
+ * exact service instance, whose own context filter composes into the carrier;
55
+ * a narrowed stand-in would silently change scope filtering.
56
+ */
57
+ export type LifecycleEmitter = {
58
+ (name: 'subagent/start', info: SubagentRunInfo, parent: Agent): void;
59
+ (name: 'subagent/end', info: SubagentRunEndInfo, parent: Agent): void;
60
+ (name: 'subagent/provider-removed', info: string): void;
61
+ };
62
+ /**
63
+ * Build the contained lifecycle emitter this seam publishes every edge through.
64
+ * Every listener is independently contained: a synchronous throw or a rejected
65
+ * returned promise is logged without starving peer listeners, changing the run,
66
+ * or — for provider removal, which fires from a disposer — breaking teardown.
67
+ * @param ctx - the service's own context, owning dispatch and the logger.
68
+ * @param carrier - resolve the scoped dispatch carrier for one delegating parent.
69
+ * @returns the emitter both observers and the provider registry publish through.
70
+ */
71
+ export declare function createLifecycleEmitter(ctx: Context, carrier: (parent: Agent) => object): LifecycleEmitter;
72
+ /**
73
+ * Emit the start/end lifecycle pair for one accepted one-shot run.
74
+ * @param emit - the contained lifecycle emitter.
75
+ * @param provider - the provider that established the run.
76
+ * @param parent - the delegating parent keying scoped dispatch.
77
+ * @param run - the published run whose settlement closes the pair.
78
+ * @returns the same run, unchanged.
79
+ */
80
+ export declare function observeRun(emit: LifecycleEmitter, provider: string, parent: Agent, run: SubagentRun): SubagentRun;
81
+ /**
82
+ * Build the observer for one continuable Activation's residency epoch. Observers
83
+ * see the same vocabulary as a one-shot run, so a child's start and settlement
84
+ * remain observable without exposing whether the manager materialized, woke, or
85
+ * cold-resumed it. Creation failure before residency emits no lifecycle edge.
86
+ * @param emit - the contained lifecycle emitter.
87
+ * @param provider - the provider name recorded in the durable descriptor.
88
+ * @param childId - the durable child session id.
89
+ * @param parent - the exact live direct parent keying scoped dispatch.
90
+ * @returns the observer whose edges this epoch publishes.
91
+ */
92
+ export declare function createActivationObserver(emit: LifecycleEmitter, provider: string, childId: SessionId, parent: Agent): ActivationObserver;
93
+ //# sourceMappingURL=lifecycle.d.ts.map
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Lifecycle-edge publication for both subagent shapes: the contained emitter,
3
+ * the one-shot run observer, and the continuable Activation observer.
4
+ *
5
+ * The public payload contracts ({@link SubagentRunInfo},
6
+ * {@link SubagentRunEndInfo}) live in `./types.ts` with the rest of the seam's
7
+ * consumer-facing types; this module owns only the implementation and the
8
+ * package-private {@link ActivationObserver} the continuation manager consumes.
9
+ * Keeping the internal control interface out of the published surface is
10
+ * deliberate: the observer's `start`/`capture`/`settle` ordering is a contract
11
+ * between this module and one in-package caller, not something a plugin may
12
+ * depend on.
13
+ *
14
+ * @module @deepseek-ai/dsh-subagent/lifecycle
15
+ */
16
+ import { randomUUID } from 'node:crypto';
17
+ import { findLastMessageTurnEnd } from '@deepseek-ai/dsh-session';
18
+ import { SubagentRunId } from "./types.js";
19
+ /**
20
+ * Build the contained lifecycle emitter this seam publishes every edge through.
21
+ * Every listener is independently contained: a synchronous throw or a rejected
22
+ * returned promise is logged without starving peer listeners, changing the run,
23
+ * or — for provider removal, which fires from a disposer — breaking teardown.
24
+ * @param ctx - the service's own context, owning dispatch and the logger.
25
+ * @param carrier - resolve the scoped dispatch carrier for one delegating parent.
26
+ * @returns the emitter both observers and the provider registry publish through.
27
+ */
28
+ export function createLifecycleEmitter(ctx, carrier) {
29
+ return (name, info, parent) => {
30
+ const dispatchArgs = parent === undefined
31
+ ? [name, info]
32
+ : [carrier(parent), name, info];
33
+ for (const callback of ctx.events.dispatch('emit', dispatchArgs)) {
34
+ try {
35
+ const returned = callback(info);
36
+ void Promise.resolve(returned).catch((error) => {
37
+ ctx.logger.warn(`subagent: ${name} listener rejected: ${renderThrown(error)}`);
38
+ });
39
+ }
40
+ catch (error) {
41
+ ctx.logger.warn(`subagent: ${name} listener threw: ${renderThrown(error)}`);
42
+ }
43
+ }
44
+ };
45
+ }
46
+ /**
47
+ * Emit the start/end lifecycle pair for one accepted one-shot run.
48
+ * @param emit - the contained lifecycle emitter.
49
+ * @param provider - the provider that established the run.
50
+ * @param parent - the delegating parent keying scoped dispatch.
51
+ * @param run - the published run whose settlement closes the pair.
52
+ * @returns the same run, unchanged.
53
+ */
54
+ export function observeRun(emit, provider, parent, run) {
55
+ const identity = {
56
+ runId: SubagentRunId(randomUUID()),
57
+ provider,
58
+ id: run.id,
59
+ local: run.localAgent !== undefined,
60
+ };
61
+ // Attach the terminal observer before dispatching start. Promise reactions
62
+ // still run after this synchronous start emission, preserving start → end.
63
+ void run.result.then((result) => {
64
+ emit('subagent/end', {
65
+ ...identity,
66
+ stopReason: result.stopReason,
67
+ lastAssistantMessage: result.output,
68
+ }, parent);
69
+ }, () => {
70
+ emit('subagent/end', { ...identity, stopReason: 'error' }, parent);
71
+ });
72
+ emit('subagent/start', identity, parent);
73
+ return run;
74
+ }
75
+ /**
76
+ * Build the observer for one continuable Activation's residency epoch. Observers
77
+ * see the same vocabulary as a one-shot run, so a child's start and settlement
78
+ * remain observable without exposing whether the manager materialized, woke, or
79
+ * cold-resumed it. Creation failure before residency emits no lifecycle edge.
80
+ * @param emit - the contained lifecycle emitter.
81
+ * @param provider - the provider name recorded in the durable descriptor.
82
+ * @param childId - the durable child session id.
83
+ * @param parent - the exact live direct parent keying scoped dispatch.
84
+ * @returns the observer whose edges this epoch publishes.
85
+ */
86
+ export function createActivationObserver(emit, provider, childId, parent) {
87
+ const identity = { runId: SubagentRunId(randomUUID()), provider, id: childId, local: true };
88
+ // A cold resume replays earlier turns, so this epoch's telemetry must come
89
+ // from the suffix it actually produced — never the whole session, which
90
+ // would report a previous epoch's answer when this one opened no turn.
91
+ let boundary = 0;
92
+ // Assigned by `capture()`, which the disposal path always runs before
93
+ // `settle()`; a resident epoch therefore always has its facts by then.
94
+ let captured = {
95
+ stopReason: 'completed',
96
+ };
97
+ return {
98
+ start: (child) => {
99
+ boundary = child.session.events.length;
100
+ emit('subagent/start', identity, parent);
101
+ },
102
+ capture: (child) => {
103
+ const own = child.session.events.slice(boundary);
104
+ const output = lastAssistantOutput(own);
105
+ captured = {
106
+ stopReason: epochStopReason(own),
107
+ ...output === undefined ? {} : { output },
108
+ };
109
+ },
110
+ settle: (failure) => {
111
+ const output = failure === undefined ? captured.output : undefined;
112
+ emit('subagent/end', {
113
+ ...identity,
114
+ stopReason: failure === undefined ? captured.stopReason : 'error',
115
+ ...output === undefined ? {} : { lastAssistantMessage: output },
116
+ }, parent);
117
+ },
118
+ };
119
+ }
120
+ /**
121
+ * Why this child's last ordinary turn ended, for the terminal lifecycle edge.
122
+ * The child's own `turn/end` is authoritative: teardown succeeding says nothing
123
+ * about whether the model errored, hit its token ceiling, or was cancelled, so
124
+ * deriving the reason from disposal would report failed work as completed.
125
+ * @param events - this epoch's own event suffix.
126
+ * @returns its terminal stop reason; `completed` when no ordinary turn closed.
127
+ */
128
+ function epochStopReason(events) {
129
+ const reason = findLastMessageTurnEnd(events)?.data.reason;
130
+ // No ordinary turn closed, so nothing failed either.
131
+ if (reason === undefined)
132
+ return 'completed';
133
+ switch (reason.kind) {
134
+ case 'max-tokens':
135
+ return 'max-tokens';
136
+ case 'aborted':
137
+ case 'interrupted':
138
+ return 'aborted';
139
+ case 'error':
140
+ return 'error';
141
+ case 'completed':
142
+ return 'completed';
143
+ /* v8 ignore next 3 -- `TurnEndReason` is merge-extensible, so this arm needs a
144
+ * backend that adds a variant; treating an unnameable reason as success would
145
+ * report failed work as completed. */
146
+ default:
147
+ return 'error';
148
+ }
149
+ }
150
+ /**
151
+ * The child's last assistant message content, for one Activation's terminal
152
+ * lifecycle edge. Absent when no assistant message reached the log.
153
+ * @param events - this epoch's own event suffix.
154
+ * @returns its final assistant content, or `undefined` when it produced none.
155
+ */
156
+ function lastAssistantOutput(events) {
157
+ const message = events.findLast((event) => event.type === 'assistant/message');
158
+ return message?.data.message.content;
159
+ }
160
+ /** Render any listener-thrown value without letting coercion escape containment. */
161
+ function renderThrown(value) {
162
+ try {
163
+ return value instanceof Error ? `${value.name}: ${value.message}` : String(value);
164
+ }
165
+ catch {
166
+ return '<unrenderable thrown value>';
167
+ }
168
+ }
169
+ //# sourceMappingURL=lifecycle.js.map
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Read-only enumeration of durable subagent children and descendant trees
3
+ * straight from the live session store and optional session persistence — no
4
+ * query service. Candidates come from one live-preferred corpus; each child's
5
+ * mode/label is the registered `subagent` projection unit's value, resolved
6
+ * down a three-rung ladder: the registry's watermark cache for a live child,
7
+ * a durable projection-cache row when it serves an own-suffix identity (the
8
+ * seq gate), and one persistence inspection folded through the registry
9
+ * otherwise, validated against the enumerated lifecycle. The projection fold
10
+ * is the single classification authority — this module parses no descriptor
11
+ * itself. Absent persistence, enumeration is live-only: a cold child is
12
+ * unreachable for resume anyway, so its absence is capability absence, not an
13
+ * error. The module owns no catalog state and does not consult Activation,
14
+ * Agent-registry, continuation-manager, or provider state.
15
+ *
16
+ * @module @deepseek-ai/dsh-subagent
17
+ */
18
+ import type { Context } from '@deepseek-ai/cordis';
19
+ import type { SessionId } from '@deepseek-ai/dsh-session';
20
+ /**
21
+ * One entry of a {@link listChildren} result, ordered by header `createdAt`
22
+ * with ties broken on id. Only a candidate whose durable header has
23
+ * `origin: 'subagent'` is interpreted. A served `subagent` projection value
24
+ * produces a `child`; a settled candidate whose fold served no identity
25
+ * produces a `diagnostic`; a running candidate without one is omitted — its
26
+ * descriptor may not be appended yet (the creation window). Diagnostics
27
+ * relay the projection fold's outcome or a failed read, never a per-child
28
+ * event scan, and never expose model-hidden descriptor content.
29
+ */
30
+ export type SubagentListEntry = {
31
+ readonly kind: 'child';
32
+ /** The durable child session id, stable across Activations. */
33
+ readonly id: SessionId;
34
+ /**
35
+ * Store snapshot activity: `running` means the logical record is live in
36
+ * `ctx.sessions`; `inactive` means it exists only in persistence. Neither
37
+ * encodes a durable outcome, and a continuable child may still reject
38
+ * delivery as an ownership conflict.
39
+ */
40
+ readonly activity: 'running' | 'inactive';
41
+ /** Whether a direct descendant has durable `origin: 'subagent'`. */
42
+ readonly hasChildren: boolean;
43
+ } & ({
44
+ /** A terminal one-shot child. */
45
+ readonly mode: 'one-shot';
46
+ /** Optional durable creation label from the child's descriptor. */
47
+ readonly label?: string;
48
+ } | {
49
+ /** A resumable conversation. */
50
+ readonly mode: 'continuable';
51
+ /** Durable creation label from the child's descriptor. */
52
+ readonly label: string;
53
+ }) | {
54
+ readonly kind: 'diagnostic';
55
+ /** The candidate's session id. */
56
+ readonly id: SessionId;
57
+ /**
58
+ * Why the candidate has no `child` row: `corrupt` for a settled candidate
59
+ * whose projection fold served no identity (a missing, malformed, or
60
+ * unrecognized-version descriptor — deliberately undistinguished), and
61
+ * for any candidate whose log makes a registered unit's fold or schema
62
+ * throw (deterministic data damage, contained per child); `unavailable`
63
+ * when the candidate's persistence inspection failed (retried on the
64
+ * next listing). `unsupported` is never produced; it remains in the
65
+ * union for consumers that route on it.
66
+ */
67
+ readonly reason: 'corrupt' | 'unsupported' | 'unavailable';
68
+ };
69
+ /**
70
+ * One entry of a descendant listing: the interpreted subagent facts plus its
71
+ * position in the complete session tree. `parentId` is the durable direct
72
+ * parent from the enumerated header, and `depth` counts edges from the root.
73
+ */
74
+ export type SubagentDescendantListEntry = SubagentListEntry & {
75
+ /** Durable direct parent of this candidate in the enumerated tree. */
76
+ readonly parentId: SessionId;
77
+ /** Edge distance from the requested root; direct children are `1`. */
78
+ readonly depth: number;
79
+ };
80
+ /**
81
+ * Enumerate one parent's origin-classified direct children from the
82
+ * live-preferred merge of `ctx.sessions` and optional session persistence,
83
+ * serving each identity from the `subagent` projection unit: the registry's
84
+ * watermark snapshot for a live child; for a cold one, a durable
85
+ * projection-cache row when it serves an own-suffix identity (the seq gate),
86
+ * else one bounded-concurrency persistence inspection folded through the
87
+ * registry.
88
+ * @see SubagentService.listChildren for the public cancellation and failure contract.
89
+ * @param ctx - context carrying the session store, the projection registry,
90
+ * optional persistence, and the optional projection cache.
91
+ * @param parentSessionId - parent session whose direct children are listed.
92
+ * @param signal - caller-owned cancellation observed around every persistence read.
93
+ * @returns children and per-child diagnostics ordered by `createdAt`, then id.
94
+ * @throws {@link SubagentError} when the projection registry or the session
95
+ * store is not mounted, or the caller cancels the listing.
96
+ */
97
+ export declare function listChildren(ctx: Context, parentSessionId: SessionId, signal?: AbortSignal): Promise<SubagentListEntry[]>;
98
+ /**
99
+ * Enumerate every session-backed subagent below one root in stable pre-order.
100
+ * Ordinary sessions and one-shot children remain traversal nodes, so a
101
+ * continuable child below either is still discovered. Classification uses the
102
+ * same projection-backed runtime as {@link listChildren}; no Agent is loaded or
103
+ * resumed.
104
+ * @see SubagentService.listDescendants for the public cancellation and failure contract.
105
+ * @param ctx - context carrying the session store, projection registry, and optional persistence/cache.
106
+ * @param rootSessionId - session whose complete descendant tree is listed.
107
+ * @param signal - caller-owned cancellation observed around every persistence read.
108
+ * @returns interpreted subagents with durable direct-parent and root-relative depth.
109
+ * @throws {@link SubagentError} under the same conditions as {@link listChildren}.
110
+ */
111
+ export declare function listDescendants(ctx: Context, rootSessionId: SessionId, signal?: AbortSignal): Promise<SubagentDescendantListEntry[]>;
112
+ //# sourceMappingURL=list-children.d.ts.map