@morlay/dsh-subagent 0.0.9 → 0.0.11-alpha.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.
package/src/index.ts CHANGED
@@ -27,32 +27,36 @@
27
27
  *
28
28
  * @module @deepseek-ai/dsh-subagent
29
29
  */
30
- import type { Volatile } from '@deepseek-ai/cordis'
30
+ import type { Volatile } from "@deepseek-ai/cordis";
31
31
 
32
- import { Context } from '@deepseek-ai/cordis'
33
- import z from '@deepseek-ai/schemastery'
34
- import type {} from '@deepseek-ai/dsh-working-directory'
35
- import type {} from '@deepseek-ai/dsh-attachment'
36
- import { scopeTarget } from '@deepseek-ai/dsh-scope'
37
- import type {} from '@deepseek-ai/dsh-subagent'
32
+ import { Context } from "@deepseek-ai/cordis";
33
+ import z from "@deepseek-ai/schemastery";
34
+ import type {} from "@deepseek-ai/dsh-working-directory";
35
+ import type {} from "@deepseek-ai/dsh-attachment";
36
+ import { scopeTarget } from "@deepseek-ai/dsh-scope";
37
+ import type {} from "@deepseek-ai/dsh-subagent";
38
38
  // 服务面声明(`ctx.agentPresets`)由它给:回报指引按会话挂的 preset 选文案。
39
- import type {} from '@deepseek-ai/dsh-agent-preset-registry'
40
- import { assertObjectJsonSchema } from '@deepseek-ai/dsh-tools'
41
- import type { ContentBlock, MessageId, MessageSource } from '@deepseek-ai/dsh-llm'
42
- import type { Agent } from '@deepseek-ai/dsh-agent'
43
- import type { SessionId } from '@deepseek-ai/dsh-session'
44
- import { canonicalClientTimeZone } from '@deepseek-ai/dsh-util-time'
45
- import { Remote, RemoteError, TypertRemoteService, principalOf, type Principal } from '@deepseek-ai/dsh-typert-protocol'
39
+ import type {} from "@deepseek-ai/dsh-agent-preset-registry";
40
+ import { assertObjectJsonSchema } from "@deepseek-ai/dsh-tools";
41
+ import type { ContentBlock, MessageId, MessageSource } from "@deepseek-ai/dsh-llm";
42
+ import type { Agent } from "@deepseek-ai/dsh-agent";
43
+ import type { SessionId } from "@deepseek-ai/dsh-session";
44
+ import { canonicalClientTimeZone } from "@deepseek-ai/dsh-util-time";
46
45
  import {
47
- rejectPrompt, validateControlRequest,
48
- } from './vendor/dsh-subagent/control.ts'
46
+ Remote,
47
+ RemoteError,
48
+ TypertRemoteService,
49
+ principalOf,
50
+ type Principal,
51
+ } from "@deepseek-ai/dsh-typert-protocol";
52
+ import { rejectPrompt, validateControlRequest } from "./vendor/dsh-subagent/control.ts";
49
53
  import type {
50
54
  SubagentDelivery,
51
55
  SubagentInterruptReceipt,
52
56
  SubagentPromptReceipt,
53
57
  SubagentPromptRequest,
54
58
  SubagentPromptRequestId,
55
- } from './vendor/dsh-subagent/control-types.ts'
59
+ } from "./vendor/dsh-subagent/control-types.ts";
56
60
  import type {
57
61
  ContinuableCreateRequest,
58
62
  ContinuableCreateSpec,
@@ -63,25 +67,37 @@ import type {
63
67
  SubagentProvider,
64
68
  SubagentSendMessageOptions,
65
69
  SubagentStartRequest,
66
- } from './vendor/dsh-subagent/types.ts'
67
- import { SubagentError } from './vendor/dsh-subagent/error.ts'
68
- import { assertSubagentMaxDepth } from './vendor/dsh-subagent/depth.ts'
69
- import { createActivationObserver, createLifecycleEmitter } from './vendor/dsh-subagent/lifecycle.ts'
70
- import type { ActivationObserver, LifecycleEmitter } from './vendor/dsh-subagent/lifecycle.ts'
71
- import SubagentManager from './manager.ts'
72
- import { installDelegationContext } from './delegation-context.ts'
73
- import { listChildren as listSubagentChildren, listDescendants as listSubagentDescendants } from './vendor/dsh-subagent/list-children.ts'
74
- import type { SubagentDescendantListEntry } from './vendor/dsh-subagent/list-children.ts'
75
- import { installSubagentArchiveAdmission } from './vendor/dsh-subagent/archive-admission.ts'
76
- import type { SubagentCatalogEntry } from './vendor/dsh-subagent/projection-types.ts'
77
- import { subagentIdentityProjectionDefinition, subagentTimingProjectionDefinition } from './vendor/dsh-subagent/projection.ts'
78
- import { subagentCatalogProjectionDefinition } from './vendor/dsh-subagent/catalog.ts'
79
- import { deliverSubagentPrompt } from './vendor/dsh-subagent/internal.ts'
70
+ } from "./vendor/dsh-subagent/types.ts";
71
+ import { SubagentError } from "./vendor/dsh-subagent/error.ts";
72
+ import { assertSubagentMaxDepth } from "./vendor/dsh-subagent/depth.ts";
73
+ import {
74
+ createActivationObserver,
75
+ createLifecycleEmitter,
76
+ } from "./vendor/dsh-subagent/lifecycle.ts";
77
+ import type { ActivationObserver, LifecycleEmitter } from "./vendor/dsh-subagent/lifecycle.ts";
78
+ import SubagentManager from "./manager.ts";
79
+ import { installDelegationContext } from "./delegation-context.ts";
80
+ import {
81
+ listChildren as listSubagentChildren,
82
+ listDescendants as listSubagentDescendants,
83
+ } from "./vendor/dsh-subagent/list-children.ts";
84
+ import type { SubagentDescendantListEntry } from "./vendor/dsh-subagent/list-children.ts";
85
+ import { installSubagentArchiveAdmission } from "./vendor/dsh-subagent/archive-admission.ts";
86
+ import type { SubagentCatalogEntry } from "./vendor/dsh-subagent/projection-types.ts";
87
+ import {
88
+ subagentIdentityProjectionDefinition,
89
+ subagentTimingProjectionDefinition,
90
+ } from "./vendor/dsh-subagent/projection.ts";
91
+ import { subagentCatalogProjectionDefinition } from "./vendor/dsh-subagent/catalog.ts";
92
+ import { deliverSubagentPrompt } from "./vendor/dsh-subagent/internal.ts";
80
93
 
81
- export type {} from './vendor/dsh-subagent/catalog.ts'
82
- export * from './vendor/dsh-subagent/out-of-process.ts'
83
- export { AssistantOutputFold, finalAssistantOutput } from './vendor/dsh-subagent/assistant-output.ts'
84
- export { SubagentRunId } from './vendor/dsh-subagent/types.ts'
94
+ export type {} from "./vendor/dsh-subagent/catalog.ts";
95
+ export * from "./vendor/dsh-subagent/out-of-process.ts";
96
+ export {
97
+ AssistantOutputFold,
98
+ finalAssistantOutput,
99
+ } from "./vendor/dsh-subagent/assistant-output.ts";
100
+ export { SubagentRunId } from "./vendor/dsh-subagent/types.ts";
85
101
  export type {
86
102
  ContinuableCreateRequest,
87
103
  ContinuableCreateSpec,
@@ -97,21 +113,21 @@ export type {
97
113
  ResolvedSubagentStartRequest,
98
114
  SubagentStopReason,
99
115
  SubagentStopReasonMap,
100
- } from './vendor/dsh-subagent/types.ts'
116
+ } from "./vendor/dsh-subagent/types.ts";
101
117
  export {
102
118
  foldSubagentDescriptor,
103
119
  snapshotSubagentDescriptor,
104
120
  SUBAGENT_DESCRIPTOR_VERSION,
105
- } from './vendor/dsh-subagent/descriptor.ts'
121
+ } from "./vendor/dsh-subagent/descriptor.ts";
106
122
  export type {
107
123
  ContinuableSubagentDescriptorData,
108
124
  ContinuableSubagentDescriptorInput,
109
125
  OneShotSubagentDescriptorData,
110
126
  SubagentDescriptorData,
111
- } from './vendor/dsh-subagent/descriptor.ts'
112
- export { STRUCTURED_OUTPUT_TOOL } from './vendor/dsh-subagent/structured.ts'
113
- export { SubagentError } from './vendor/dsh-subagent/error.ts'
114
- export { assertSubagentMaxDepth, delegationDepthOf } from './vendor/dsh-subagent/depth.ts'
127
+ } from "./vendor/dsh-subagent/descriptor.ts";
128
+ export { STRUCTURED_OUTPUT_TOOL } from "./vendor/dsh-subagent/structured.ts";
129
+ export { SubagentError } from "./vendor/dsh-subagent/error.ts";
130
+ export { assertSubagentMaxDepth, delegationDepthOf } from "./vendor/dsh-subagent/depth.ts";
115
131
  export {
116
132
  appendDelegatedPolicyOverrides,
117
133
  applyChildComposition,
@@ -121,13 +137,20 @@ export {
121
137
  resolveChildAgentOptions,
122
138
  resolveChildDepth,
123
139
  SubagentDepthError,
124
- } from './vendor/dsh-subagent/child-agent.ts'
125
- export type { ChildComposition, DelegatedPolicyOverrides } from './vendor/dsh-subagent/child-agent.ts'
126
- export type { AgentMessageSource, SubagentSettledMessageSource } from './continuation-messages.ts'
127
- export type * from './vendor/dsh-subagent/control-types.ts'
128
- export type { SubagentDescendantListEntry } from './vendor/dsh-subagent/list-children.ts'
129
- export type { SubagentRunEndInfo, SubagentRunInfo } from './vendor/dsh-subagent/types.ts'
130
- export type { SubagentCatalogEntry, SubagentIdentityProjection, SubagentTimingProjection } from './vendor/dsh-subagent/projection-types.ts'
140
+ } from "./vendor/dsh-subagent/child-agent.ts";
141
+ export type {
142
+ ChildComposition,
143
+ DelegatedPolicyOverrides,
144
+ } from "./vendor/dsh-subagent/child-agent.ts";
145
+ export type { AgentMessageSource, SubagentSettledMessageSource } from "./continuation-messages.ts";
146
+ export type * from "./vendor/dsh-subagent/control-types.ts";
147
+ export type { SubagentDescendantListEntry } from "./vendor/dsh-subagent/list-children.ts";
148
+ export type { SubagentRunEndInfo, SubagentRunInfo } from "./vendor/dsh-subagent/types.ts";
149
+ export type {
150
+ SubagentCatalogEntry,
151
+ SubagentIdentityProjection,
152
+ SubagentTimingProjection,
153
+ } from "./vendor/dsh-subagent/projection-types.ts";
131
154
 
132
155
  /**
133
156
  * Durable attribution of one browser-authored follow-up. The Session
@@ -137,73 +160,95 @@ export type { SubagentCatalogEntry, SubagentIdentityProjection, SubagentTimingPr
137
160
  * Client reconciles its optimistic prompt against.
138
161
  */
139
162
  interface BrowserPromptSource {
140
- readonly kind: 'user'
141
- readonly rpcId: SubagentPromptRequestId
142
- readonly clientTimeZone?: string
163
+ readonly kind: "user";
164
+ readonly rpcId: SubagentPromptRequestId;
165
+ readonly clientTimeZone?: string;
143
166
  /** Caller that submitted this follow-up, written only when it is not the owner; absent means the owner. */
144
- readonly principal?: Principal
167
+ readonly principal?: Principal;
145
168
  }
146
169
 
147
170
  /** Host configuration for continuable subagent capacity. */
148
171
  export interface Config {
149
172
  /** Maximum live children sharing uninterrupted continuable parent links; defaults to 8. */
150
- maxActiveSubagents: Volatile<number>
173
+ maxActiveSubagents: Volatile<number>;
151
174
  /** Default delegation depth for tools without an explicit limit; defaults to 1. */
152
- maxDepth: Volatile<number>
175
+ maxDepth: Volatile<number>;
153
176
  /**
154
177
  * Preset ids the Chinese return guidance is restricted to; empty (default) means no restriction.
155
178
  *
156
179
  * 不配 = 任意 preset 的会话都用本包的中文回报指引(官方四个 shipped preset 也在内,还有还没绑 preset 的会话)。
157
180
  * 要反过来只让某几份 preset 用中文,就在这里列出来。装配面,不是用户面——所以不进设置页(`.hidden()`)。
158
181
  */
159
- localizedReturnGuidancePresets: string[]
182
+ localizedReturnGuidancePresets: string[];
160
183
  }
161
184
 
162
185
  /** Named provider registry with managed activations, durable discovery, and local child messaging. */
163
186
  export class SubagentRuntime extends TypertRemoteService {
164
187
  static Config = z.object({
165
188
  maxDepth: z.number().step(1).min(0).max(Number.MAX_SAFE_INTEGER).default(1).volatile(),
166
- maxActiveSubagents: z.number().step(1).min(1).max(Number.MAX_SAFE_INTEGER).default(8).volatile(),
189
+ maxActiveSubagents: z
190
+ .number()
191
+ .step(1)
192
+ .min(1)
193
+ .max(Number.MAX_SAFE_INTEGER)
194
+ .default(8)
195
+ .volatile(),
167
196
  localizedReturnGuidancePresets: z.array(z.string()).default([]).hidden(),
168
- })
169
- static inject = ['workingDirectory']
170
- private providers = new Map<string, SubagentProvider>()
171
- private manager: SubagentManager | undefined
197
+ });
198
+ static inject = ["workingDirectory"];
199
+ private providers = new Map<string, SubagentProvider>();
200
+ private manager: SubagentManager | undefined;
172
201
  /**
173
202
  * The contained lifecycle-edge publisher. Built here because scoped dispatch
174
203
  * keys its carrier by this exact service instance, whose own context filter
175
204
  * composes into the carrier.
176
205
  */
177
- private readonly emitLifecycle: LifecycleEmitter
206
+ private readonly emitLifecycle: LifecycleEmitter;
178
207
 
179
- constructor(ctx: Context, private config: Config) {
180
- super(ctx, 'subagents')
208
+ constructor(
209
+ ctx: Context,
210
+ private config: Config,
211
+ ) {
212
+ super(ctx, "subagents");
181
213
  // 模型看到的委派范围说明换成中文(见 `./delegation-context.ts`)。
182
- installDelegationContext(ctx)
183
- this.emitLifecycle = createLifecycleEmitter(this.ctx, parent => scopeTarget(this, parent))
184
- ctx.inject(['agents'], (childCtx: Context) => {
185
- const manager = new SubagentManager(childCtx, {
186
- startExternal: (name, request) => {
187
- const provider = this.expectProvider(name) as SubagentProvider & Required<Pick<SubagentProvider, 'start'>>
188
- return provider.start(request)
214
+ installDelegationContext(ctx);
215
+ this.emitLifecycle = createLifecycleEmitter(this.ctx, (parent) => scopeTarget(this, parent));
216
+ ctx.inject(["agents"], (childCtx: Context) => {
217
+ const manager = new SubagentManager(
218
+ childCtx,
219
+ {
220
+ startExternal: (name, request) => {
221
+ const provider = this.expectProvider(name) as SubagentProvider &
222
+ Required<Pick<SubagentProvider, "start">>;
223
+ return provider.start(request);
224
+ },
225
+ prepareContinuable: (name, request) => this.prepareContinuable(name, request),
226
+ observeActivation: (provider, childId, parent) =>
227
+ this.observeActivation(provider, childId, parent),
228
+ },
229
+ () => this.config.maxActiveSubagents.get(),
230
+ () => this.config.localizedReturnGuidancePresets,
231
+ );
232
+ this.manager = manager;
233
+ childCtx.effect(
234
+ () => () => {
235
+ /* v8 ignore else -- one injected binding owns the slot until its fiber disposes. */
236
+ if (this.manager === manager) this.manager = undefined;
189
237
  },
190
- prepareContinuable: (name, request) => this.prepareContinuable(name, request),
191
- observeActivation: (provider, childId, parent) => this.observeActivation(provider, childId, parent),
192
- }, () => this.config.maxActiveSubagents.get(), () => this.config.localizedReturnGuidancePresets)
193
- this.manager = manager
194
- childCtx.effect(() => () => {
195
- /* v8 ignore else -- one injected binding owns the slot until its fiber disposes. */
196
- if (this.manager === manager) this.manager = undefined
197
- }, 'subagents.managerBinding()')
198
- })
199
- ctx.inject(['sessionProjections'], (projectionCtx) => {
200
- projectionCtx.sessionProjections.register(subagentCatalogProjectionDefinition)
201
- projectionCtx.sessionProjections.register(subagentTimingProjectionDefinition)
202
- projectionCtx.sessionProjections.register(subagentIdentityProjectionDefinition)
203
- })
238
+ "subagents.managerBinding()",
239
+ );
240
+ });
241
+ ctx.inject(["sessionProjections"], async (projectionCtx) => {
242
+ // 上游 0.2.1-rc.2 起 `register()` 是异步的(句柄随注册一起交付),注册面要 await。
243
+ await projectionCtx.sessionProjections.register(subagentCatalogProjectionDefinition);
244
+ await projectionCtx.sessionProjections.register(subagentTimingProjectionDefinition);
245
+ await projectionCtx.sessionProjections.register(subagentIdentityProjectionDefinition);
246
+ });
204
247
  // Archive admission: this runtime is the owner that knows which live
205
248
  // children descend from a Session and how a parent stops them.
206
- ctx.inject(['agents'], (agentsCtx: Context) => { installSubagentArchiveAdmission(agentsCtx) })
249
+ ctx.inject(["agents"], (agentsCtx: Context) => {
250
+ installSubagentArchiveAdmission(agentsCtx);
251
+ });
207
252
  }
208
253
 
209
254
  /**
@@ -211,12 +256,12 @@ export class SubagentRuntime extends TypertRemoteService {
211
256
  * @param configured - Explicit tool limit, or provider-managed for external delegation.
212
257
  * @returns The numeric limit, or undefined when the provider owns depth enforcement.
213
258
  */
214
- resolveMaxDepth(configured?: number | 'provider-managed'): number | undefined {
215
- if (configured === 'provider-managed') return undefined
216
- if (configured !== undefined) return configured
217
- const depth = this.config.maxDepth.get()
218
- assertSubagentMaxDepth(depth)
219
- return depth
259
+ resolveMaxDepth(configured?: number | "provider-managed"): number | undefined {
260
+ if (configured === "provider-managed") return undefined;
261
+ if (configured !== undefined) return configured;
262
+ const depth = this.config.maxDepth.get();
263
+ assertSubagentMaxDepth(depth);
264
+ return depth;
220
265
  }
221
266
 
222
267
  /**
@@ -224,14 +269,15 @@ export class SubagentRuntime extends TypertRemoteService {
224
269
  * @param spec - local task, reserved child id, and result recipient.
225
270
  * @returns activation with its accepted initial message id.
226
271
  */
227
- startActivation(spec: SubagentActivationSpec & { readonly childId: SessionId }):
228
- Promise<SubagentActivation & { readonly messageId: MessageId }>
272
+ startActivation(
273
+ spec: SubagentActivationSpec & { readonly childId: SessionId },
274
+ ): Promise<SubagentActivation & { readonly messageId: MessageId }>;
229
275
  /**
230
276
  * Start a local or external child.
231
277
  * @param spec - task, backend, and result recipient.
232
278
  * @returns activation with a message id only for local children.
233
279
  */
234
- startActivation(spec: SubagentActivationSpec): Promise<SubagentActivation>
280
+ startActivation(spec: SubagentActivationSpec): Promise<SubagentActivation>;
235
281
  /**
236
282
  * Establish a managed child with backend-specific execution capabilities.
237
283
  * @param spec - task, backend, and result recipient.
@@ -239,20 +285,26 @@ export class SubagentRuntime extends TypertRemoteService {
239
285
  * childId requires a local backend and guarantees an accepted messageId.
240
286
  */
241
287
  async startActivation(spec: SubagentActivationSpec): Promise<SubagentActivation> {
242
- const provider = this.expectProvider(spec.provider)
288
+ const provider = this.expectProvider(spec.provider);
243
289
  if (spec.childId !== undefined && provider.prepareContinuable === undefined) {
244
- throw new SubagentError('reserved child ids require a local backend', 'UNSUPPORTED_CAPABILITY')
290
+ throw new SubagentError(
291
+ "reserved child ids require a local backend",
292
+ "UNSUPPORTED_CAPABILITY",
293
+ );
245
294
  }
246
- assertSubagentMaxDepth(spec.request.maxDepth)
247
- this.assertCapabilities(provider, { ...spec.request, signal: spec.signal })
248
- const manager = this.requireManager()
249
- if (this.ctx.get('agents')?.get(spec.request.parent.id) !== spec.request.parent) {
250
- throw new SubagentError('subagent creation requires the exact live parent agent', 'UNAUTHORIZED')
295
+ assertSubagentMaxDepth(spec.request.maxDepth);
296
+ this.assertCapabilities(provider, { ...spec.request, signal: spec.signal });
297
+ const manager = this.requireManager();
298
+ if (this.ctx.get("agents")?.get(spec.request.parent.id) !== spec.request.parent) {
299
+ throw new SubagentError(
300
+ "subagent creation requires the exact live parent agent",
301
+ "UNAUTHORIZED",
302
+ );
251
303
  }
252
- if (spec.request.outputSchema !== undefined) assertObjectJsonSchema(spec.request.outputSchema)
304
+ if (spec.request.outputSchema !== undefined) assertObjectJsonSchema(spec.request.outputSchema);
253
305
  return provider.prepareContinuable === undefined
254
306
  ? manager.startExternal(spec)
255
- : manager.startLocal(spec)
307
+ : manager.startLocal(spec);
256
308
  }
257
309
 
258
310
  /**
@@ -262,7 +314,7 @@ export class SubagentRuntime extends TypertRemoteService {
262
314
  * @returns whether work was joined; hosts recheck parent idle after true.
263
315
  */
264
316
  async waitForChildren(parent: Agent): Promise<boolean> {
265
- return this.manager?.waitForChildren(parent) ?? false
317
+ return this.manager?.waitForChildren(parent) ?? false;
266
318
  }
267
319
 
268
320
  /**
@@ -285,7 +337,7 @@ export class SubagentRuntime extends TypertRemoteService {
285
337
  content: ContentBlock[],
286
338
  options: SubagentSendMessageOptions,
287
339
  ): Promise<MessageId> {
288
- return this.requireManager().sendMessage(sender, targetId, content, options)
340
+ return this.requireManager().sendMessage(sender, targetId, content, options);
289
341
  }
290
342
 
291
343
  /**
@@ -308,9 +360,9 @@ export class SubagentRuntime extends TypertRemoteService {
308
360
  signal: AbortSignal,
309
361
  delivery: SubagentDelivery,
310
362
  ): Promise<MessageId> {
311
- return delivery === 'steer'
363
+ return delivery === "steer"
312
364
  ? this.requireManager().steerPrompt(parent, childId, content, source, signal)
313
- : this.requireManager().queuePrompt(parent, childId, content, source, signal)
365
+ : this.requireManager().queuePrompt(parent, childId, content, source, signal);
314
366
  }
315
367
 
316
368
  /**
@@ -329,7 +381,7 @@ export class SubagentRuntime extends TypertRemoteService {
329
381
  * live target.
330
382
  */
331
383
  interrupt(targetSessionId: SessionId, authority: SubagentInterruptAuthority): void {
332
- this.manager?.interrupt(targetSessionId, authority)
384
+ this.manager?.interrupt(targetSessionId, authority);
333
385
  }
334
386
 
335
387
  /**
@@ -343,10 +395,10 @@ export class SubagentRuntime extends TypertRemoteService {
343
395
  * @throws an aggregate error after all branches settle when any failed.
344
396
  */
345
397
  async drainDescendants(parents: readonly Agent[]): Promise<void> {
346
- const manager = this.manager
398
+ const manager = this.manager;
347
399
  // An absent activation manager cannot own materialized children.
348
- if (manager === undefined) return
349
- await manager.drainDescendants(parents)
400
+ if (manager === undefined) return;
401
+ await manager.drainDescendants(parents);
350
402
  }
351
403
 
352
404
  /**
@@ -360,9 +412,9 @@ export class SubagentRuntime extends TypertRemoteService {
360
412
  * different parent or the supplied parent identity is stale.
361
413
  */
362
414
  async drainChildren(parent: Agent, childIds: readonly SessionId[]): Promise<void> {
363
- const manager = this.manager
364
- if (manager === undefined) return
365
- await manager.drainChildren(parent, childIds)
415
+ const manager = this.manager;
416
+ if (manager === undefined) return;
417
+ await manager.drainChildren(parent, childIds);
366
418
  }
367
419
 
368
420
  /**
@@ -375,7 +427,7 @@ export class SubagentRuntime extends TypertRemoteService {
375
427
  * @throws SessionQueryError when the parent cannot be read or the query is cancelled.
376
428
  */
377
429
  listChildren(parentSessionId: SessionId, signal?: AbortSignal): Promise<SubagentCatalogEntry[]> {
378
- return listSubagentChildren(this.ctx, parentSessionId, signal)
430
+ return listSubagentChildren(this.ctx, parentSessionId, signal);
379
431
  }
380
432
 
381
433
  /**
@@ -394,8 +446,11 @@ export class SubagentRuntime extends TypertRemoteService {
394
446
  * @throws {@link SubagentError} when listing dependencies are unavailable or the caller cancels.
395
447
  * @throws SessionQueryError when the root catalog cannot be read.
396
448
  */
397
- listDescendants(rootSessionId: SessionId, signal?: AbortSignal): Promise<SubagentDescendantListEntry[]> {
398
- return listSubagentDescendants(this.ctx, rootSessionId, signal)
449
+ listDescendants(
450
+ rootSessionId: SessionId,
451
+ signal?: AbortSignal,
452
+ ): Promise<SubagentDescendantListEntry[]> {
453
+ return listSubagentDescendants(this.ctx, rootSessionId, signal);
399
454
  }
400
455
 
401
456
  /**
@@ -416,45 +471,52 @@ export class SubagentRuntime extends TypertRemoteService {
416
471
  * `subagent/not-resumable`, `subagent/unauthorized`,
417
472
  * `subagent/delivery-unavailable`, `gateway/cancelled`, or `gateway/internal`.
418
473
  */
419
- @Remote('prompt')
420
- async prompt(request: SubagentPromptRequest, signal: AbortSignal): Promise<SubagentPromptReceipt> {
421
- const { parentSessionId, childSessionId, clientTimeZone, delivery } = request
422
- validateControlRequest('subagent.prompt', request)
423
- const canonicalTimeZone = clientTimeZone === undefined
424
- ? undefined
425
- : canonicalClientTimeZone(clientTimeZone)
474
+ @Remote("prompt")
475
+ async prompt(
476
+ request: SubagentPromptRequest,
477
+ signal: AbortSignal,
478
+ ): Promise<SubagentPromptReceipt> {
479
+ const { parentSessionId, childSessionId, clientTimeZone, delivery } = request;
480
+ validateControlRequest("subagent.prompt", request);
481
+ const canonicalTimeZone =
482
+ clientTimeZone === undefined ? undefined : canonicalClientTimeZone(clientTimeZone);
426
483
  if (clientTimeZone !== undefined && canonicalTimeZone === undefined) {
427
484
  throw new RemoteError(
428
- 'subagent/invalid-time-zone',
429
- 'clientTimeZone must be UTC or a valid IANA Area/Location name',
485
+ "subagent/invalid-time-zone",
486
+ "clientTimeZone must be UTC or a valid IANA Area/Location name",
430
487
  { value: clientTimeZone },
431
- )
488
+ );
432
489
  }
433
- const parent = this.ctx.get('agents')?.get(parentSessionId)
490
+ const parent = this.ctx.get("agents")?.get(parentSessionId);
434
491
  if (parent === undefined) {
435
492
  throw new RemoteError(
436
- 'subagent/parent-unavailable',
493
+ "subagent/parent-unavailable",
437
494
  `parent session "${parentSessionId}" is not live`,
438
495
  { parentSessionId },
439
- )
496
+ );
440
497
  }
441
- const principal = principalOf(this.ctx.invocation)
498
+ const principal = principalOf(this.ctx.invocation);
442
499
  const source: BrowserPromptSource = {
443
- kind: 'user',
500
+ kind: "user",
444
501
  rpcId: request.requestId,
445
502
  ...(canonicalTimeZone === undefined ? {} : { clientTimeZone: canonicalTimeZone }),
446
503
  ...(principal === undefined ? {} : { principal }),
447
- }
504
+ };
448
505
  try {
449
506
  // Admission precedes delivery: image parts become durable references
450
507
  // here, so the child inbox only ever accepts Host-persisted attachments.
451
- let content: ContentBlock[]
452
- if (request.content.every((part): part is { readonly type: 'text'; readonly text: string } => part.type === 'text')) {
453
- content = request.content.map(part => ({ type: 'text', text: part.text }))
508
+ let content: ContentBlock[];
509
+ if (
510
+ request.content.every(
511
+ (part): part is { readonly type: "text"; readonly text: string } => part.type === "text",
512
+ )
513
+ ) {
514
+ content = request.content.map((part) => ({ type: "text", text: part.text }));
454
515
  } else {
455
- const attachments = this.ctx.get('attachments')
456
- if (attachments === undefined) throw new Error('subagent image prompt requires an attachment store')
457
- content = await attachments.admitPromptContent(request.content)
516
+ const attachments = this.ctx.get("attachments");
517
+ if (attachments === undefined)
518
+ throw new Error("subagent image prompt requires an attachment store");
519
+ content = await attachments.admitPromptContent(request.content);
458
520
  }
459
521
  return {
460
522
  messageId: await this[deliverSubagentPrompt](
@@ -465,9 +527,9 @@ export class SubagentRuntime extends TypertRemoteService {
465
527
  signal,
466
528
  delivery,
467
529
  ),
468
- }
530
+ };
469
531
  } catch (error: unknown) {
470
- return rejectPrompt(error, childSessionId, signal)
532
+ return rejectPrompt(error, childSessionId, signal);
471
533
  }
472
534
  }
473
535
 
@@ -485,27 +547,27 @@ export class SubagentRuntime extends TypertRemoteService {
485
547
  * `subagent/unauthorized` when the address does not own the live target,
486
548
  * otherwise `gateway/internal`.
487
549
  */
488
- @Remote('interruptByParent')
550
+ @Remote("interruptByParent")
489
551
  interruptByParent(
490
552
  childSessionId: SessionId,
491
553
  parentSessionId: SessionId,
492
- mode: 'continuable',
554
+ mode: "continuable",
493
555
  ): SubagentInterruptReceipt {
494
- validateControlRequest('subagent.interrupt', { childSessionId, parentSessionId, mode })
556
+ validateControlRequest("subagent.interrupt", { childSessionId, parentSessionId, mode });
495
557
  try {
496
- this.interrupt(childSessionId, { kind: 'user', parentSessionId })
558
+ this.interrupt(childSessionId, { kind: "user", parentSessionId });
497
559
  } catch (error: unknown) {
498
- if (error instanceof SubagentError && error.code === 'UNAUTHORIZED') {
560
+ if (error instanceof SubagentError && error.code === "UNAUTHORIZED") {
499
561
  throw new RemoteError(
500
- 'subagent/unauthorized',
501
- 'subagent does not belong to this parent',
562
+ "subagent/unauthorized",
563
+ "subagent does not belong to this parent",
502
564
  { childSessionId },
503
565
  { cause: error },
504
- )
566
+ );
505
567
  }
506
- throw new RemoteError('gateway/internal', 'subagent interrupt failed', {}, { cause: error })
568
+ throw new RemoteError("gateway/internal", "subagent interrupt failed", {}, { cause: error });
507
569
  }
508
- return { accepted: true }
570
+ return { accepted: true };
509
571
  }
510
572
 
511
573
  /**
@@ -517,24 +579,33 @@ export class SubagentRuntime extends TypertRemoteService {
517
579
  * @returns the exact Cordis effect disposer.
518
580
  */
519
581
  registerProvider(provider: SubagentProvider): () => void {
520
- const name = provider.name
582
+ const name = provider.name;
521
583
  if (provider.start === undefined && provider.prepareContinuable === undefined) {
522
- throw new SubagentError(`subagent provider "${name}" must implement start or prepareContinuable`, 'UNSUPPORTED_CAPABILITY')
584
+ throw new SubagentError(
585
+ `subagent provider "${name}" must implement start or prepareContinuable`,
586
+ "UNSUPPORTED_CAPABILITY",
587
+ );
523
588
  }
524
589
  // oxlint-disable-next-line typescript/no-misused-promises -- synchronous disposer
525
- return this.ctx.effect(function* (this: SubagentRuntime) {
526
- if (this.providers.has(name)) {
527
- throw new SubagentError(`a subagent provider named "${name}" is already registered`, 'DUPLICATE_PROVIDER')
528
- }
529
- this.providers.set(name, provider)
530
- yield () => {
531
- this.providers.delete(name)
532
- this.emitLifecycle('subagent/provider-removed', name)
533
- }
534
- // A throwing added-listener unwinds the yielded rollback, matching the
535
- // repository's fail-loud registration semantics.
536
- this.ctx.emit('subagent/provider-added', provider)
537
- }.bind(this), 'subagents.registerProvider()')
590
+ return this.ctx.effect(
591
+ function* (this: SubagentRuntime) {
592
+ if (this.providers.has(name)) {
593
+ throw new SubagentError(
594
+ `a subagent provider named "${name}" is already registered`,
595
+ "DUPLICATE_PROVIDER",
596
+ );
597
+ }
598
+ this.providers.set(name, provider);
599
+ yield () => {
600
+ this.providers.delete(name);
601
+ this.emitLifecycle("subagent/provider-removed", name);
602
+ };
603
+ // A throwing added-listener unwinds the yielded rollback, matching the
604
+ // repository's fail-loud registration semantics.
605
+ this.ctx.emit("subagent/provider-added", provider);
606
+ }.bind(this),
607
+ "subagents.registerProvider()",
608
+ );
538
609
  }
539
610
 
540
611
  /**
@@ -543,7 +614,7 @@ export class SubagentRuntime extends TypertRemoteService {
543
614
  * @returns the provider, or undefined when absent.
544
615
  */
545
616
  getProvider(name: string): SubagentProvider | undefined {
546
- return this.providers.get(name)
617
+ return this.providers.get(name);
547
618
  }
548
619
 
549
620
  /**
@@ -551,7 +622,7 @@ export class SubagentRuntime extends TypertRemoteService {
551
622
  * @returns the registered names.
552
623
  */
553
624
  list(): string[] {
554
- return [...this.providers.keys()]
625
+ return [...this.providers.keys()];
555
626
  }
556
627
 
557
628
  /**
@@ -563,28 +634,29 @@ export class SubagentRuntime extends TypertRemoteService {
563
634
  name: string,
564
635
  request: ContinuableCreateRequest,
565
636
  ): Promise<ContinuableCreateSpec> {
566
- const provider = this.expectProvider(name) as SubagentProvider & Required<Pick<SubagentProvider, 'prepareContinuable'>>
567
- return provider.prepareContinuable(request)
637
+ const provider = this.expectProvider(name) as SubagentProvider &
638
+ Required<Pick<SubagentProvider, "prepareContinuable">>;
639
+ return provider.prepareContinuable(request);
568
640
  }
569
641
 
570
642
  /** Look up a provider for dispatch or fail loud. */
571
643
  private expectProvider(name: string): SubagentProvider {
572
- const provider = this.providers.get(name)
644
+ const provider = this.providers.get(name);
573
645
  if (provider === undefined) {
574
- throw new SubagentError(`no subagent provider registered for "${name}"`, 'NO_PROVIDER')
646
+ throw new SubagentError(`no subagent provider registered for "${name}"`, "NO_PROVIDER");
575
647
  }
576
- return provider
648
+ return provider;
577
649
  }
578
650
 
579
651
  /** Resolve the subagent manager or fail loud. */
580
652
  private requireManager(): SubagentManager {
581
653
  if (this.manager === undefined) {
582
654
  throw new SubagentError(
583
- 'continuable subagents require the agents service',
584
- 'CONTINUATION_UNAVAILABLE',
585
- )
655
+ "continuable subagents require the agents service",
656
+ "CONTINUATION_UNAVAILABLE",
657
+ );
586
658
  }
587
- return this.manager
659
+ return this.manager;
588
660
  }
589
661
 
590
662
  /**
@@ -596,32 +668,34 @@ export class SubagentRuntime extends TypertRemoteService {
596
668
  childId: SessionId,
597
669
  parent: Agent,
598
670
  ): ActivationObserver {
599
- return createActivationObserver(this.emitLifecycle, provider, childId, parent)
671
+ return createActivationObserver(this.emitLifecycle, provider, childId, parent);
600
672
  }
601
673
 
602
674
  /** Reject the first requested capability that the provider lacks. */
603
675
  private assertCapabilities(provider: SubagentProvider, request: SubagentStartRequest): void {
604
- if (request.agentPreset !== undefined && (!provider.capabilities.agentPreset || provider.inheritsParentContext)) {
676
+ if (
677
+ request.agentPreset !== undefined &&
678
+ (!provider.capabilities.agentPreset || provider.inheritsParentContext)
679
+ ) {
605
680
  throw new SubagentError(
606
681
  `subagent provider "${provider.name}" does not support independent agent preset selection`,
607
- 'UNSUPPORTED_CAPABILITY',
608
- )
682
+ "UNSUPPORTED_CAPABILITY",
683
+ );
609
684
  }
610
685
  const needs: { when: boolean; cap: keyof SubagentCapabilities }[] = [
611
- { when: request.agentOptions !== undefined, cap: 'agentOptions' },
612
- { when: request.outputSchema !== undefined, cap: 'outputSchema' },
613
- { when: request.maxDepth !== undefined, cap: 'depthLimit' },
614
- ]
686
+ { when: request.agentOptions !== undefined, cap: "agentOptions" },
687
+ { when: request.outputSchema !== undefined, cap: "outputSchema" },
688
+ { when: request.maxDepth !== undefined, cap: "depthLimit" },
689
+ ];
615
690
  for (const { when, cap } of needs) {
616
691
  if (when && !provider.capabilities[cap]) {
617
692
  throw new SubagentError(
618
693
  `subagent provider "${provider.name}" does not support the "${cap}" capability`,
619
- 'UNSUPPORTED_CAPABILITY',
620
- )
694
+ "UNSUPPORTED_CAPABILITY",
695
+ );
621
696
  }
622
697
  }
623
698
  }
624
-
625
699
  }
626
700
 
627
- export default SubagentRuntime
701
+ export default SubagentRuntime;