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