@deepseek-ai/dsh-subagent 0.1.1-rc.2 → 0.1.2-alpha.2

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.
@@ -4,10 +4,8 @@
4
4
  * child before returning its run, so fulfillment is the single publication and
5
5
  * ownership-transfer boundary.
6
6
  *
7
- * Unlike the bash seam (one executor per context, second load throws), MULTIPLE
8
- * providers coexist here: each registers under a unique name and a caller picks
9
- * one by name. The shape mirrors the LLM adapter registry
10
- * (`LlmRuntime.registerAdapter`), not the single-service bash executor.
7
+ * Multiple providers coexist: each registers under a unique name and callers
8
+ * select one by name.
11
9
  *
12
10
  * This package owns the Service Definition role of the capability seam. Service Providers
13
11
  * (`@deepseek-ai/dsh-subagent-spawn-in-process`, `-fork`, `-acp`) and the model-facing
@@ -30,9 +28,45 @@
30
28
  *
31
29
  * @module @deepseek-ai/dsh-subagent
32
30
  */
33
- import { Service } from '@deepseek-ai/cordis';
31
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
32
+ var useValue = arguments.length > 2;
33
+ for (var i = 0; i < initializers.length; i++) {
34
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
35
+ }
36
+ return useValue ? value : void 0;
37
+ };
38
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
39
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
40
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
41
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
42
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
43
+ var _, done = false;
44
+ for (var i = decorators.length - 1; i >= 0; i--) {
45
+ var context = {};
46
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
47
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
48
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
49
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
50
+ if (kind === "accessor") {
51
+ if (result === void 0) continue;
52
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
53
+ if (_ = accept(result.get)) descriptor.get = _;
54
+ if (_ = accept(result.set)) descriptor.set = _;
55
+ if (_ = accept(result.init)) initializers.unshift(_);
56
+ }
57
+ else if (_ = accept(result)) {
58
+ if (kind === "field") initializers.unshift(_);
59
+ else descriptor[key] = _;
60
+ }
61
+ }
62
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
63
+ done = true;
64
+ };
34
65
  import { scopeTarget } from '@deepseek-ai/dsh-scope';
35
66
  import { assertObjectJsonSchema } from '@deepseek-ai/dsh-tools';
67
+ import { canonicalClientTimeZone } from '@deepseek-ai/dsh-util-time';
68
+ import { Remote, RemoteError, TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
69
+ import { admitPromptContent, catalogView, rejectCatalogRead, rejectPrompt, validateControlRequest, } from "./control.js";
36
70
  import { SubagentError } from "./error.js";
37
71
  import { assertSubagentMaxDepth } from "./depth.js";
38
72
  import { createActivationObserver, createLifecycleEmitter, observeRun } from "./lifecycle.js";
@@ -49,307 +83,404 @@ export { seedDescriptorTurn } from "./descriptor-seed.js";
49
83
  export { SubagentError } from "./error.js";
50
84
  export { settleRun } from "./run-settlement.js";
51
85
  export { assertSubagentMaxDepth, delegationDepthOf } from "./depth.js";
52
- export { appendDelegatedPolicyOverrides, applyChildComposition, captureDelegatedPolicyOverrides, childSessionMeta, resolveChildAgentOptions, resolveChildDepth, SubagentDepthError, } from "./child-agent.js";
86
+ export { appendDelegatedPolicyOverrides, applyChildComposition, captureDelegatedPolicyOverrides, childSessionMeta, parentAgentOptionsForDelegation, resolveChildAgentOptions, resolveChildDepth, SubagentDepthError, } from "./child-agent.js";
53
87
  /** Named provider registry with one-shot runs, durable discovery, and continuable-child operations. */
54
- export class SubagentRuntime extends Service {
55
- providers = new Map();
56
- continuations;
57
- /** Deployment contributions composed into unpublished continuable children. */
58
- setupRegistry = new SubagentActivationSetupRegistry();
59
- /**
60
- * The contained lifecycle-edge publisher. Built here because scoped dispatch
61
- * keys its carrier by this exact service instance, whose own context filter
62
- * composes into the carrier.
63
- */
64
- emitLifecycle;
65
- constructor(ctx) {
66
- super(ctx, 'subagents');
67
- this.emitLifecycle = createLifecycleEmitter(this.ctx, parent => scopeTarget(this, parent));
68
- ctx.inject(['agents'], (childCtx) => {
69
- const manager = new SubagentContinuationManager(childCtx, {
70
- prepareContinuable: (name, request) => this.prepareContinuable(name, request),
71
- observeActivation: (provider, childId, parent) => this.observeActivation(provider, childId, parent),
72
- }, this.setupRegistry);
73
- this.continuations = manager;
74
- childCtx.effect(() => () => {
75
- /* v8 ignore else -- one injected binding owns the slot until its fiber disposes. */
76
- if (this.continuations === manager)
77
- this.continuations = undefined;
78
- }, 'subagents.continuationBinding()');
79
- });
80
- ctx.inject(['sessionProjections'], (projectionCtx) => {
81
- projectionCtx.sessionProjections.register(subagentTimingProjectionDefinition);
82
- projectionCtx.sessionProjections.register(subagentIdentityProjectionDefinition);
83
- });
84
- }
85
- /**
86
- * Establish one durable continuable child and deliver its initial prompt.
87
- * Resolves when the child's inbox accepts that prompt, without waiting for the
88
- * turn to start or for the message to reach the Session log; any earlier
89
- * failure rejects with no ids and rolls back the child entirely.
90
- * @param spec - provider, delegation request, and caller cancellation.
91
- * @returns the durable child id and the accepted prompt's message id.
92
- * @throws when continuation services are unavailable or materialization fails.
93
- */
94
- async startContinuable(spec) {
95
- return this.requireContinuations().startContinuable(spec);
96
- }
97
- /**
98
- * Deliver one later message to a continuable child as its next FIFO turn. A
99
- * resident child's Agent inbox accepts it directly (waking a `waiting`
100
- * Activation), while an absent one is cold-resumed from its persisted
101
- * Session. The Agent inbox is the only queue, so every accepted message has
102
- * one observable order.
103
- * @param parent - the exact live direct parent authorizing this delivery.
104
- * @param childId - durable child session id.
105
- * @param content - user-role content to deliver.
106
- * @param options - the message source fields and caller cancellation, which stops the
107
- * operation only before inbox acceptance.
108
- * @returns the accepted message's inbox id.
109
- * @throws when continuation services are unavailable, parent authority is
110
- * rejected, or the message was not admitted.
111
- */
112
- async followup(parent, childId, content, options) {
113
- return this.requireContinuations().followup(parent, childId, content, options);
114
- }
115
- /**
116
- * Interrupt one live continuable child's current turn under a human parent
117
- * address or an exact live ancestor Agent. Fire-and-return: the cancel
118
- * signal is issued before this returns, but the target may keep running
119
- * until it observes the signal. Unclaimed pending inbox work, the Activation,
120
- * and published descendants are preserved; claimed work is not requeued.
121
- * Once the interrupted driver is idle, a waking send resumes the parked FIFO
122
- * queue. An absent target — including a one-shot or unknown id —
123
- * is an accepted no-op, as is a manager-less composition, which cannot own a
124
- * live Activation.
125
- * @param targetSessionId - the durable child session id to interrupt.
126
- * @param authority - the human parent address or exact live ancestor Agent.
127
- * @throws {SubagentError} `UNAUTHORIZED` when the authority does not own the
128
- * live target.
129
- */
130
- interrupt(targetSessionId, authority) {
131
- this.continuations?.interrupt(targetSessionId, authority);
132
- }
133
- /**
134
- * Deliver selected content from one live continuable child to its durable
135
- * direct parent. The child is the authority credential; callers cannot name a
136
- * recipient. Reporting does not conclude the child's turn or Activation.
137
- * @param child - exact live reporting child.
138
- * @param content - selected model-facing content.
139
- * @param options - parent scheduling and pre-acceptance cancellation.
140
- * @returns the stable identity of the parent-accepted message.
141
- * @throws when continuation services are unavailable, sender authorization
142
- * fails, or the direct parent is not live.
143
- */
144
- async reportFrom(child, content, options) {
145
- return this.requireContinuations().reportFrom(child, content, options);
146
- }
147
- /**
148
- * Compose one deployment capability into every continuable child's
149
- * unpublished creation context on fresh creation and cold resume. Grants wait
150
- * for the next Activation; removing the contribution revokes every resident
151
- * installation immediately.
152
- * @param contribution - synchronous child-scope installer.
153
- * @returns the exact Cordis effect disposer.
154
- */
155
- registerContinuableSetup(contribution) {
156
- // oxlint-disable-next-line typescript/no-misused-promises -- synchronous cleanup; direct return preserves disposer identity
157
- return this.ctx.effect(() => this.setupRegistry.register(contribution), 'subagents.registerContinuableSetup()');
158
- }
159
- /**
160
- * Close continuable admission below exact live parent Agents, stop only their
161
- * visible descendant Activations synchronously, then await admitted scoped
162
- * materializations and release those forests child-first. The scoped cutoff
163
- * lasts until each exact parent leaves the registry; unrelated parent trees
164
- * remain live.
165
- * @param parents - exact host-owned parent Agents entering teardown.
166
- * @returns once every retained descendant Activation released its `AgentHandle`.
167
- * @throws an aggregate error after all branches settle when any failed.
168
- */
169
- async drainContinuableDescendants(parents) {
170
- const manager = this.continuations;
171
- // Absent continuation services means nothing was ever materialized.
172
- if (manager === undefined)
173
- return;
174
- await manager.drainDescendants(parents);
175
- }
176
- /**
177
- * Release selected resident continuable direct children of one exact live
178
- * parent. Other children of the same parent remain admitted and resident.
179
- * Absent targets and a manager-less composition are accepted no-ops.
180
- * @param parent - exact live direct parent authorizing the selected release.
181
- * @param childIds - durable direct-child ids to release when resident.
182
- * @returns once every selected Activation released its `AgentHandle`.
183
- * @throws {SubagentError} `UNAUTHORIZED` when a resident target belongs to a
184
- * different parent or the supplied parent identity is stale.
185
- */
186
- async drainContinuableChildren(parent, childIds) {
187
- const manager = this.continuations;
188
- if (manager === undefined)
189
- return;
190
- await manager.drainChildren(parent, childIds);
191
- }
192
- /**
193
- * Enumerate the parent's direct session-backed subagents without loading or
194
- * resuming an Agent and without any query service: the listing merges the live
195
- * session store with optional session persistence (live-preferred) and
196
- * serves each child's durable mode/label from the registered `subagent`
197
- * projection unit down a three-rung ladder — the registry's watermark
198
- * snapshot for a live child; for a cold one, a durable projection-cache
199
- * row when the optional cache serves an own-suffix identity (its `seq`
200
- * gate proves the value postdates the fork seed, where a child's own
201
- * descriptor is immutable once appended), else one persistence inspection
202
- * folded through the registry. The
203
- * projection fold is the single classification authority; per-child
204
- * diagnostics relay a fold that served no identity or a failed inspection,
205
- * never a list-time descriptor parse. Absent persistence, enumeration is
206
- * live-only (a cold child cannot be resumed then either, so its absence is
207
- * capability absence, not an error). This service consults no Agent
208
- * registrations, Activations, or providers.
209
- *
210
- * Every persistence read receives `signal`, and the listing rechecks
211
- * cancellation around each of those awaits. Read rejections that settle
212
- * after an abort become a stable `SubagentError` with code `CANCELLED`.
213
- * @param parentSessionId - parent session whose direct children are listed.
214
- * @param signal - caller-owned cancellation forwarded to persistence reads
215
- * and observed around every read await.
216
- * @returns children and per-child diagnostics ordered by `createdAt`, then id.
217
- * @throws {@link SubagentError} when the projection registry or the session
218
- * store is not mounted, or the caller cancels the listing.
219
- */
220
- listChildren(parentSessionId, signal) {
221
- return listSubagentChildren(this.ctx, parentSessionId, signal);
222
- }
223
- /**
224
- * Enumerate the root's complete session-backed subagent tree in stable
225
- * pre-order from one live-preferred corpus, without loading or resuming an
226
- * Agent. Ordinary sessions and one-shot children remain traversal nodes so
227
- * continuable descendants below them are discovered; each returned entry
228
- * adds its durable `parentId` and root-relative `depth`. Identity resolution,
229
- * diagnostics, optional persistence, and cancellation follow the same
230
- * projection-backed contract as {@link listChildren}.
231
- * @param rootSessionId - session whose complete descendant tree is listed.
232
- * @param signal - caller-owned cancellation forwarded to persistence reads
233
- * and observed around every read await.
234
- * @returns children and per-candidate diagnostics with tree position, in
235
- * stable pre-order.
236
- * @throws {@link SubagentError} under the same conditions as {@link listChildren}.
237
- */
238
- listDescendants(rootSessionId, signal) {
239
- return listSubagentDescendants(this.ctx, rootSessionId, signal);
240
- }
241
- /**
242
- * Register a provider under its name. Registration is effect-scoped and HMR
243
- * safe; removing a provider blocks new starts but does not revoke runs that
244
- * were already returned to their holders.
245
- * @param provider - the trusted provider implementation.
246
- * @returns the exact Cordis effect disposer.
247
- */
248
- registerProvider(provider) {
249
- const name = provider.name;
250
- // oxlint-disable-next-line typescript/no-misused-promises -- synchronous cleanup; direct return preserves disposer identity
251
- return this.ctx.effect(function* () {
252
- if (this.providers.has(name)) {
253
- throw new SubagentError(`a subagent provider named "${name}" is already registered`, 'DUPLICATE_PROVIDER');
88
+ let SubagentRuntime = (() => {
89
+ let _classSuper = TypertRemoteService;
90
+ let _instanceExtraInitializers = [];
91
+ let _remoteExportList_decorators;
92
+ let _prompt_decorators;
93
+ let _interruptByParent_decorators;
94
+ return class SubagentRuntime extends _classSuper {
95
+ static {
96
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
97
+ _remoteExportList_decorators = [Remote('list')];
98
+ _prompt_decorators = [Remote('prompt')];
99
+ _interruptByParent_decorators = [Remote('interruptByParent')];
100
+ __esDecorate(this, null, _remoteExportList_decorators, { kind: "method", name: "remoteExportList", static: false, private: false, access: { has: obj => "remoteExportList" in obj, get: obj => obj.remoteExportList }, metadata: _metadata }, null, _instanceExtraInitializers);
101
+ __esDecorate(this, null, _prompt_decorators, { kind: "method", name: "prompt", static: false, private: false, access: { has: obj => "prompt" in obj, get: obj => obj.prompt }, metadata: _metadata }, null, _instanceExtraInitializers);
102
+ __esDecorate(this, null, _interruptByParent_decorators, { kind: "method", name: "interruptByParent", static: false, private: false, access: { has: obj => "interruptByParent" in obj, get: obj => obj.interruptByParent }, metadata: _metadata }, null, _instanceExtraInitializers);
103
+ if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
104
+ }
105
+ providers = (__runInitializers(this, _instanceExtraInitializers), new Map());
106
+ continuations;
107
+ /** Deployment contributions composed into unpublished continuable children. */
108
+ setupRegistry = new SubagentActivationSetupRegistry();
109
+ /**
110
+ * The contained lifecycle-edge publisher. Built here because scoped dispatch
111
+ * keys its carrier by this exact service instance, whose own context filter
112
+ * composes into the carrier.
113
+ */
114
+ emitLifecycle;
115
+ constructor(ctx) {
116
+ super(ctx, 'subagents');
117
+ this.emitLifecycle = createLifecycleEmitter(this.ctx, parent => scopeTarget(this, parent));
118
+ ctx.inject(['agents'], (childCtx) => {
119
+ const manager = new SubagentContinuationManager(childCtx, {
120
+ prepareContinuable: (name, request) => this.prepareContinuable(name, request),
121
+ observeActivation: (provider, childId, parent) => this.observeActivation(provider, childId, parent),
122
+ }, this.setupRegistry);
123
+ this.continuations = manager;
124
+ childCtx.effect(() => () => {
125
+ /* v8 ignore else -- one injected binding owns the slot until its fiber disposes. */
126
+ if (this.continuations === manager)
127
+ this.continuations = undefined;
128
+ }, 'subagents.continuationBinding()');
129
+ });
130
+ ctx.inject(['sessionProjections'], (projectionCtx) => {
131
+ projectionCtx.sessionProjections.register(subagentTimingProjectionDefinition);
132
+ projectionCtx.sessionProjections.register(subagentIdentityProjectionDefinition);
133
+ });
134
+ }
135
+ /**
136
+ * Establish one durable continuable child and deliver its initial prompt.
137
+ * Resolves when the child's inbox accepts that prompt, without waiting for the
138
+ * turn to start or for the message to reach the Session log; any earlier
139
+ * failure rejects with no ids and rolls back the child entirely.
140
+ * @param spec - provider, delegation request, and caller cancellation.
141
+ * @returns the durable child id and the accepted prompt's message id.
142
+ * @throws when continuation services are unavailable or materialization fails.
143
+ */
144
+ async startContinuable(spec) {
145
+ return this.requireContinuations().startContinuable(spec);
146
+ }
147
+ /**
148
+ * Deliver one later message to a continuable child as its next FIFO turn. A
149
+ * resident child's Agent inbox accepts it directly (waking a `waiting`
150
+ * Activation), while an absent one is cold-resumed from its persisted
151
+ * Session. The Agent inbox is the only queue, so every accepted message has
152
+ * one observable order.
153
+ * @param parent - the exact live direct parent authorizing this delivery.
154
+ * @param childId - durable child session id.
155
+ * @param content - user-role content to deliver.
156
+ * @param options - the message source fields and caller cancellation, which stops the
157
+ * operation only before inbox acceptance.
158
+ * @returns the accepted message's inbox id.
159
+ * @throws when continuation services are unavailable, parent authority is
160
+ * rejected, or the message was not admitted.
161
+ */
162
+ async followup(parent, childId, content, options) {
163
+ return this.requireContinuations().followup(parent, childId, content, options);
164
+ }
165
+ /**
166
+ * Interrupt one live continuable child's current turn under a human parent
167
+ * address or an exact live ancestor Agent. Fire-and-return: the cancel
168
+ * signal is issued before this returns, but the target may keep running
169
+ * until it observes the signal. Unclaimed pending inbox work, the Activation,
170
+ * and published descendants are preserved; claimed work is not requeued.
171
+ * Once the interrupted driver is idle, a waking send resumes the parked FIFO
172
+ * queue. An absent target — including a one-shot or unknown id —
173
+ * is an accepted no-op, as is a manager-less composition, which cannot own a
174
+ * live Activation.
175
+ * @param targetSessionId - the durable child session id to interrupt.
176
+ * @param authority - the human parent address or exact live ancestor Agent.
177
+ * @throws {SubagentError} `UNAUTHORIZED` when the authority does not own the
178
+ * live target.
179
+ */
180
+ interrupt(targetSessionId, authority) {
181
+ this.continuations?.interrupt(targetSessionId, authority);
182
+ }
183
+ /**
184
+ * Deliver selected content from one live continuable child to its durable
185
+ * direct parent. The child is the authority credential; callers cannot name a
186
+ * recipient. Reporting does not conclude the child's turn or Activation.
187
+ * @param child - exact live reporting child.
188
+ * @param content - selected model-facing content.
189
+ * @param options - parent scheduling and pre-acceptance cancellation.
190
+ * @returns the stable identity of the parent-accepted message.
191
+ * @throws when continuation services are unavailable, sender authorization
192
+ * fails, or the direct parent is not live.
193
+ */
194
+ async reportFrom(child, content, options) {
195
+ return this.requireContinuations().reportFrom(child, content, options);
196
+ }
197
+ /**
198
+ * Compose one deployment capability into every continuable child's
199
+ * unpublished creation context on fresh creation and cold resume. Grants wait
200
+ * for the next Activation; removing the contribution revokes every resident
201
+ * installation immediately.
202
+ * @param contribution - synchronous child-scope installer.
203
+ * @returns the exact Cordis effect disposer.
204
+ */
205
+ registerContinuableSetup(contribution) {
206
+ // oxlint-disable-next-line typescript/no-misused-promises -- synchronous disposer
207
+ return this.ctx.effect(() => this.setupRegistry.register(contribution), 'subagents.registerContinuableSetup()');
208
+ }
209
+ /**
210
+ * Close continuable admission below exact live parent Agents, stop only their
211
+ * visible descendant Activations synchronously, then await admitted scoped
212
+ * materializations and release those forests child-first. The scoped cutoff
213
+ * lasts until each exact parent leaves the registry; unrelated parent trees
214
+ * remain live.
215
+ * @param parents - exact host-owned parent Agents entering teardown.
216
+ * @returns once every retained descendant Activation released its `AgentHandle`.
217
+ * @throws an aggregate error after all branches settle when any failed.
218
+ */
219
+ async drainContinuableDescendants(parents) {
220
+ const manager = this.continuations;
221
+ // Absent continuation services means nothing was ever materialized.
222
+ if (manager === undefined)
223
+ return;
224
+ await manager.drainDescendants(parents);
225
+ }
226
+ /**
227
+ * Release selected resident continuable direct children of one exact live
228
+ * parent. Other children of the same parent remain admitted and resident.
229
+ * Absent targets and a manager-less composition are accepted no-ops.
230
+ * @param parent - exact live direct parent authorizing the selected release.
231
+ * @param childIds - durable direct-child ids to release when resident.
232
+ * @returns once every selected Activation released its `AgentHandle`.
233
+ * @throws {SubagentError} `UNAUTHORIZED` when a resident target belongs to a
234
+ * different parent or the supplied parent identity is stale.
235
+ */
236
+ async drainContinuableChildren(parent, childIds) {
237
+ const manager = this.continuations;
238
+ if (manager === undefined)
239
+ return;
240
+ await manager.drainChildren(parent, childIds);
241
+ }
242
+ /**
243
+ * Enumerate the parent's direct session-backed subagents without loading or
244
+ * resuming an Agent. The Session query service supplies one live-preferred
245
+ * corpus and shared point observations; the projection cache supplies
246
+ * immutable descriptor hits without opening cold logs. The registered
247
+ * `subagent` projection remains the sole mode/label classifier.
248
+ *
249
+ * Every query receives `signal`, and the listing rechecks cancellation
250
+ * around each await. Read rejections that settle
251
+ * after an abort become a stable `SubagentError` with code `CANCELLED`.
252
+ * @param parentSessionId - parent session whose direct children are listed.
253
+ * @param signal - caller-owned cancellation forwarded to Session queries
254
+ * and observed around every read await.
255
+ * @returns children and per-child diagnostics ordered by `createdAt`, then id.
256
+ * @throws {@link SubagentError} when the projection registry or the session
257
+ * store is not mounted, or the caller cancels the listing.
258
+ */
259
+ listChildren(parentSessionId, signal) {
260
+ return listSubagentChildren(this.ctx, parentSessionId, signal);
261
+ }
262
+ /**
263
+ * Enumerate the root's complete session-backed subagent tree in stable
264
+ * pre-order from one live-preferred corpus, without loading or resuming an
265
+ * Agent. Ordinary sessions and one-shot children remain traversal nodes so
266
+ * continuable descendants below them are discovered; each returned entry
267
+ * adds its durable `parentId` and root-relative `depth`. Identity resolution,
268
+ * diagnostics, optional persistence, and cancellation follow the same
269
+ * projection-backed contract as {@link listChildren}.
270
+ * @param rootSessionId - session whose complete descendant tree is listed.
271
+ * @param signal - caller-owned cancellation forwarded to persistence reads
272
+ * and observed around every read await.
273
+ * @returns children and per-candidate diagnostics with tree position, in
274
+ * stable pre-order.
275
+ * @throws {@link SubagentError} under the same conditions as {@link listChildren}.
276
+ */
277
+ listDescendants(rootSessionId, signal) {
278
+ return listSubagentDescendants(this.ctx, rootSessionId, signal);
279
+ }
280
+ /**
281
+ * Remote face of {@link listChildren} for one browser: the durable listing
282
+ * plus live Agent activity and the delivery-time parent availability hint.
283
+ * Parent availability is a hint; {@link prompt} performs the authoritative
284
+ * check. Named apart from the provider-name {@link list}, which owns the
285
+ * member.
286
+ * @param parentSessionId - parent session whose direct children are listed.
287
+ * @param signal - carrier cancellation forwarded to Session queries.
288
+ * @returns the catalog view for that parent.
289
+ * @throws {RemoteError} `gateway/bad-request` for an empty parent id,
290
+ * `gateway/cancelled` for an aborted read, `subagent/projections-unavailable` when
291
+ * the deployment has no projection registry, otherwise `gateway/internal`.
292
+ */
293
+ async remoteExportList(parentSessionId, signal) {
294
+ validateControlRequest('subagent.list', { parentSessionId });
295
+ try {
296
+ return catalogView(this.ctx, parentSessionId, await this.listChildren(parentSessionId, signal));
297
+ }
298
+ catch (error) {
299
+ return rejectCatalogRead(error, signal);
254
300
  }
255
- this.providers.set(name, provider);
256
- yield () => {
257
- this.providers.delete(name);
258
- this.emitLifecycle('subagent/provider-removed', name);
301
+ }
302
+ /**
303
+ * Deliver one browser-authored message to a continuable child through the
304
+ * exact live direct parent, retaining the caller-minted request identity and
305
+ * validated browser zone on the accepted message. Success identifies the
306
+ * message the child's FIFO inbox accepted; later execution is independent of
307
+ * this call.
308
+ * @param request - durable address, minted identity, content, and optional browser zone.
309
+ * @param signal - carrier cancellation, owning the call until inbox acceptance.
310
+ * @returns the accepted message's inbox identity.
311
+ * @throws {RemoteError} `gateway/bad-request`, `subagent/attachment-unsupported`,
312
+ * `subagent/invalid-time-zone`, `subagent/parent-unavailable`,
313
+ * `subagent/not-resumable`, `subagent/unauthorized`,
314
+ * `subagent/delivery-unavailable`, `gateway/cancelled`, or `gateway/internal`.
315
+ */
316
+ async prompt(request, signal) {
317
+ const { parentSessionId, childSessionId, clientTimeZone } = request;
318
+ validateControlRequest('subagent.prompt', request);
319
+ const content = admitPromptContent(childSessionId, request.content);
320
+ const canonicalTimeZone = clientTimeZone === undefined
321
+ ? undefined
322
+ : canonicalClientTimeZone(clientTimeZone);
323
+ if (clientTimeZone !== undefined && canonicalTimeZone === undefined) {
324
+ throw new RemoteError('subagent/invalid-time-zone', 'clientTimeZone must be UTC or a valid IANA Area/Location name', { value: clientTimeZone });
325
+ }
326
+ const parent = this.ctx.get('agents')?.get(parentSessionId);
327
+ if (parent === undefined) {
328
+ throw new RemoteError('subagent/parent-unavailable', `parent session "${parentSessionId}" is not live`, { parentSessionId });
329
+ }
330
+ const source = {
331
+ kind: 'user',
332
+ rpcId: request.requestId,
333
+ ...(canonicalTimeZone === undefined ? {} : { clientTimeZone: canonicalTimeZone }),
259
334
  };
260
- // A throwing added-listener unwinds the yielded rollback, matching the
261
- // repository's fail-loud registration semantics.
262
- this.ctx.emit('subagent/provider-added', provider);
263
- }.bind(this), 'subagents.registerProvider()');
264
- }
265
- /**
266
- * Look up a provider by name.
267
- * @param name - the provider name.
268
- * @returns the provider, or undefined when absent.
269
- */
270
- getProvider(name) {
271
- return this.providers.get(name);
272
- }
273
- /**
274
- * List registered provider names in insertion order.
275
- * @returns the registered names.
276
- */
277
- list() {
278
- return [...this.providers.keys()];
279
- }
280
- /**
281
- * Establish a published child on the named provider. Capability and semantic
282
- * checks run before delegation. Provider ownership lasts until its promise
283
- * fulfills; a rejection therefore has no run for the caller to dispose and
284
- * emits no run lifecycle events. Post-publication turn and infrastructure
285
- * failures settle through the returned run.
286
- * @param name - the provider to use.
287
- * @param request - child label, prompt, parent, signal, and optional capabilities.
288
- * @returns the published holder-owned run.
289
- */
290
- async start(name, request) {
291
- const provider = this.expectProvider(name);
292
- this.assertCapabilities(provider, request);
293
- assertSubagentMaxDepth(request.maxDepth);
294
- if (request.outputSchema !== undefined)
295
- assertObjectJsonSchema(request.outputSchema);
296
- const descriptor = snapshotSubagentDescriptor({
297
- mode: 'one-shot',
298
- provider: name,
299
- ...request.label !== undefined ? { label: request.label } : {},
300
- });
301
- const resolved = { ...request, descriptor };
302
- return observeRun(this.emitLifecycle, name, request.parent, await provider.start(resolved));
303
- }
304
- /**
305
- * Resolve one provider's detached continuable-creation contribution. Method
306
- * presence on the provider IS the capability, so a provider without it is
307
- * rejected before the manager reserves any child resources.
308
- */
309
- async prepareContinuable(name, request) {
310
- const provider = this.expectProvider(name);
311
- if (provider.prepareContinuable === undefined) {
312
- throw new SubagentError(`subagent provider "${provider.name}" does not support continuable children `
313
- + '(no prepareContinuable capability)', 'UNSUPPORTED_CAPABILITY');
335
+ try {
336
+ return { messageId: await this.followup(parent, childSessionId, content, { source, signal }) };
337
+ }
338
+ catch (error) {
339
+ return rejectPrompt(error, childSessionId, signal);
340
+ }
314
341
  }
315
- return provider.prepareContinuable(request);
316
- }
317
- /** Look up a provider for dispatch or fail loud. */
318
- expectProvider(name) {
319
- const provider = this.providers.get(name);
320
- if (provider === undefined) {
321
- throw new SubagentError(`no subagent provider registered for "${name}"`, 'NO_PROVIDER');
342
+ /**
343
+ * Remote face of {@link interrupt} under one durable parent address. No
344
+ * catalog, history, persistence, or parent Agent lookup runs: the core
345
+ * primitive alone authorizes the address against the live Activation, which
346
+ * is what keeps a live child interruptible while its parent Agent is offline.
347
+ * Absent, idle, and already-completed targets are accepted no-ops there.
348
+ * @param childSessionId - durable child session id to interrupt.
349
+ * @param parentSessionId - durable direct parent whose authority is claimed.
350
+ * @param mode - required continuable-address discriminator.
351
+ * @returns acknowledgement that the cancel signal was admitted, not that the target is quiescent.
352
+ * @throws {RemoteError} `gateway/bad-request` for an empty id,
353
+ * `subagent/unauthorized` when the address does not own the live target,
354
+ * otherwise `gateway/internal`.
355
+ */
356
+ interruptByParent(childSessionId, parentSessionId, mode) {
357
+ validateControlRequest('subagent.interrupt', { childSessionId, parentSessionId, mode });
358
+ try {
359
+ this.interrupt(childSessionId, { kind: 'user', parentSessionId });
360
+ }
361
+ catch (error) {
362
+ if (error instanceof SubagentError && error.code === 'UNAUTHORIZED') {
363
+ throw new RemoteError('subagent/unauthorized', 'subagent does not belong to this parent', { childSessionId }, { cause: error });
364
+ }
365
+ throw new RemoteError('gateway/internal', 'subagent interrupt failed', {}, { cause: error });
366
+ }
367
+ return { accepted: true };
322
368
  }
323
- return provider;
324
- }
325
- /** Resolve the optional continuable-subagent manager or fail loud. */
326
- requireContinuations() {
327
- if (this.continuations === undefined) {
328
- throw new SubagentError('continuable subagents require the agents service', 'CONTINUATION_UNAVAILABLE');
369
+ /**
370
+ * Register a provider under its name. Registration is effect-scoped and HMR
371
+ * safe; removing a provider blocks new starts but does not revoke runs that
372
+ * were already returned to their holders.
373
+ * @param provider - the trusted provider implementation.
374
+ * @returns the exact Cordis effect disposer.
375
+ */
376
+ registerProvider(provider) {
377
+ const name = provider.name;
378
+ // oxlint-disable-next-line typescript/no-misused-promises -- synchronous disposer
379
+ return this.ctx.effect(function* () {
380
+ if (this.providers.has(name)) {
381
+ throw new SubagentError(`a subagent provider named "${name}" is already registered`, 'DUPLICATE_PROVIDER');
382
+ }
383
+ this.providers.set(name, provider);
384
+ yield () => {
385
+ this.providers.delete(name);
386
+ this.emitLifecycle('subagent/provider-removed', name);
387
+ };
388
+ // A throwing added-listener unwinds the yielded rollback, matching the
389
+ // repository's fail-loud registration semantics.
390
+ this.ctx.emit('subagent/provider-added', provider);
391
+ }.bind(this), 'subagents.registerProvider()');
329
392
  }
330
- return this.continuations;
331
- }
332
- /**
333
- * Build the lifecycle observer for one continuable Activation's residency
334
- * epoch, so the manager publishes its edges without owning event dispatch.
335
- */
336
- observeActivation(provider, childId, parent) {
337
- return createActivationObserver(this.emitLifecycle, provider, childId, parent);
338
- }
339
- /** Reject the first requested capability that the provider lacks. */
340
- assertCapabilities(provider, request) {
341
- const needs = [
342
- { when: request.outputSchema !== undefined, cap: 'outputSchema' },
343
- { when: request.maxDepth !== undefined, cap: 'depthLimit' },
344
- { when: request.toolFilter !== undefined, cap: 'toolFilter' },
345
- { when: request.persona !== undefined, cap: 'persona' },
346
- ];
347
- for (const { when, cap } of needs) {
348
- if (when && !provider.capabilities[cap]) {
349
- throw new SubagentError(`subagent provider "${provider.name}" does not support the "${cap}" capability`, 'UNSUPPORTED_CAPABILITY');
393
+ /**
394
+ * Look up a provider by name.
395
+ * @param name - the provider name.
396
+ * @returns the provider, or undefined when absent.
397
+ */
398
+ getProvider(name) {
399
+ return this.providers.get(name);
400
+ }
401
+ /**
402
+ * List registered provider names in insertion order.
403
+ * @returns the registered names.
404
+ */
405
+ list() {
406
+ return [...this.providers.keys()];
407
+ }
408
+ /**
409
+ * Establish a published child on the named provider. Capability and semantic
410
+ * checks run before delegation. Provider ownership lasts until its promise
411
+ * fulfills; a rejection therefore has no run for the caller to dispose and
412
+ * emits no run lifecycle events. Post-publication turn and infrastructure
413
+ * failures settle through the returned run.
414
+ * @param name - the provider to use.
415
+ * @param request - child label, prompt, parent, signal, and optional capabilities.
416
+ * @returns the published holder-owned run.
417
+ */
418
+ async start(name, request) {
419
+ const provider = this.expectProvider(name);
420
+ this.assertCapabilities(provider, request);
421
+ assertSubagentMaxDepth(request.maxDepth);
422
+ if (request.outputSchema !== undefined)
423
+ assertObjectJsonSchema(request.outputSchema);
424
+ const descriptor = snapshotSubagentDescriptor({
425
+ mode: 'one-shot',
426
+ provider: name,
427
+ ...request.label !== undefined ? { label: request.label } : {},
428
+ });
429
+ const resolved = { ...request, descriptor };
430
+ return observeRun(this.emitLifecycle, name, request.parent, await provider.start(resolved));
431
+ }
432
+ /**
433
+ * Resolve one provider's detached continuable-creation contribution. Method
434
+ * presence on the provider IS the capability, so a provider without it is
435
+ * rejected before the manager reserves any child resources.
436
+ */
437
+ async prepareContinuable(name, request) {
438
+ const provider = this.expectProvider(name);
439
+ if (provider.prepareContinuable === undefined) {
440
+ throw new SubagentError(`subagent provider "${provider.name}" does not support continuable children `
441
+ + '(no prepareContinuable capability)', 'UNSUPPORTED_CAPABILITY');
350
442
  }
443
+ return provider.prepareContinuable(request);
351
444
  }
352
- }
353
- }
445
+ /** Look up a provider for dispatch or fail loud. */
446
+ expectProvider(name) {
447
+ const provider = this.providers.get(name);
448
+ if (provider === undefined) {
449
+ throw new SubagentError(`no subagent provider registered for "${name}"`, 'NO_PROVIDER');
450
+ }
451
+ return provider;
452
+ }
453
+ /** Resolve the optional continuable-subagent manager or fail loud. */
454
+ requireContinuations() {
455
+ if (this.continuations === undefined) {
456
+ throw new SubagentError('continuable subagents require the agents service', 'CONTINUATION_UNAVAILABLE');
457
+ }
458
+ return this.continuations;
459
+ }
460
+ /**
461
+ * Build the lifecycle observer for one continuable Activation's residency
462
+ * epoch, so the manager publishes its edges without owning event dispatch.
463
+ */
464
+ observeActivation(provider, childId, parent) {
465
+ return createActivationObserver(this.emitLifecycle, provider, childId, parent);
466
+ }
467
+ /** Reject the first requested capability that the provider lacks. */
468
+ assertCapabilities(provider, request) {
469
+ const needs = [
470
+ { when: request.agentOptions !== undefined, cap: 'agentOptions' },
471
+ { when: request.outputSchema !== undefined, cap: 'outputSchema' },
472
+ { when: request.maxDepth !== undefined, cap: 'depthLimit' },
473
+ { when: request.toolFilter !== undefined, cap: 'toolFilter' },
474
+ { when: request.persona !== undefined, cap: 'persona' },
475
+ ];
476
+ for (const { when, cap } of needs) {
477
+ if (when && !provider.capabilities[cap]) {
478
+ throw new SubagentError(`subagent provider "${provider.name}" does not support the "${cap}" capability`, 'UNSUPPORTED_CAPABILITY');
479
+ }
480
+ }
481
+ }
482
+ };
483
+ })();
484
+ export { SubagentRuntime };
354
485
  export default SubagentRuntime;
355
486
  //# sourceMappingURL=index.js.map