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

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/lib/index.js CHANGED
@@ -1,11 +1,15 @@
1
- import { Service } from "@deepseek-ai/cordis";
1
+ import { AttachmentError, admitPromptContent } from "@deepseek-ai/dsh-attachment";
2
2
  import { scopeTarget } from "@deepseek-ai/dsh-scope";
3
3
  import { assertObjectJsonSchema } from "@deepseek-ai/dsh-tools";
4
- import { HarnessError, boundContextSummary, createUserMessage, errorChain } from "@deepseek-ai/dsh-llm";
4
+ import { canonicalClientTimeZone } from "@deepseek-ai/dsh-util-time";
5
+ import { Remote, RemoteError, TypertRemoteService } from "@deepseek-ai/dsh-typert-protocol";
6
+ import { z } from "zod";
7
+ import { HarnessError, ReasoningEffortId, boundContextSummary, contentHasImage, createUserMessage, errorChain } from "@deepseek-ai/dsh-llm";
5
8
  import { randomUUID } from "node:crypto";
6
9
  import { foldConsumedWork } from "@deepseek-ai/dsh-agent";
7
- import { Session, SessionId, snapshotJsonValue } from "@deepseek-ai/dsh-session";
8
- import { z } from "zod";
10
+ import { brandString } from "@deepseek-ai/dsh-brand";
11
+ import { snapshotJsonValue } from "@deepseek-ai/dsh-util-values";
12
+ import { Session } from "@deepseek-ai/dsh-session";
9
13
  import { accessSync, constants, statSync } from "node:fs";
10
14
  import { isAbsolute, resolve } from "node:path";
11
15
  //#region lib/types/error.js
@@ -22,6 +26,101 @@ var SubagentError = class extends HarnessError {
22
26
  }
23
27
  };
24
28
  //#endregion
29
+ //#region lib/types/control.js
30
+ /**
31
+ * Browser-facing subagent control assembly: the catalog view sampled against
32
+ * the live Agent registry, one browser zone's validation, and the stable
33
+ * failure codes the Remote surface answers with.
34
+ *
35
+ * @module @deepseek-ai/dsh-subagent
36
+ */
37
+ const SESSION_ID_SCHEMA = z.string().min(1);
38
+ const CONTROL_ID_SCHEMAS = {
39
+ "subagent.list": z.object({ parentSessionId: SESSION_ID_SCHEMA }),
40
+ "subagent.prompt": z.object({
41
+ parentSessionId: SESSION_ID_SCHEMA,
42
+ childSessionId: SESSION_ID_SCHEMA,
43
+ mode: z.literal("continuable")
44
+ }),
45
+ "subagent.interrupt": z.object({
46
+ parentSessionId: SESSION_ID_SCHEMA,
47
+ childSessionId: SESSION_ID_SCHEMA,
48
+ mode: z.literal("continuable")
49
+ })
50
+ };
51
+ /**
52
+ * Apply the subagent payload checks that are stricter than generated
53
+ * branded-string codecs.
54
+ * @param method - method name carried in the failure message.
55
+ * @param payload - decoded control fields to validate.
56
+ * @throws {RemoteError} `gateway/bad-request` with the original Zod issues.
57
+ */
58
+ function validateControlRequest(method, payload) {
59
+ const parsed = CONTROL_ID_SCHEMAS[method].safeParse(payload);
60
+ if (!parsed.success) throw new RemoteError("gateway/bad-request", `invalid payload for ${method}`, { issues: parsed.error.issues });
61
+ }
62
+ /**
63
+ * Project one durable listing onto the catalog view, replacing each row's
64
+ * store-derived activity with the live Agent driver's status and reporting
65
+ * whether the exact parent Agent is live. Without an Agent registry no driver
66
+ * runs at all, so every row is inactive and the parent is unavailable.
67
+ * @param ctx - Host context that may carry the Agent registry.
68
+ * @param parentSessionId - the listed parent.
69
+ * @param entries - the durable direct-child listing.
70
+ * @returns the catalog view answered to one browser.
71
+ */
72
+ function catalogView(ctx, parentSessionId, entries) {
73
+ const agents = ctx.get("agents");
74
+ return {
75
+ entries: entries.map((entry) => entry.kind === "child" ? {
76
+ ...entry,
77
+ activity: agents?.get(entry.id)?.status === "running" ? "running" : "inactive"
78
+ } : entry),
79
+ parentAvailable: agents?.get(parentSessionId) !== void 0
80
+ };
81
+ }
82
+ /**
83
+ * Refuse one catalog read while preserving cancellation and a missing
84
+ * projections registry as distinct failures.
85
+ * @param error - the thrown value.
86
+ * @param signal - the caller's cancellation.
87
+ * @returns Never — the refusal is thrown.
88
+ * @throws {RemoteError} always.
89
+ */
90
+ function rejectCatalogRead(error, signal) {
91
+ if (isCancellation(error, signal)) throw new RemoteError("gateway/cancelled", "subagent catalog read was cancelled", {}, { cause: error });
92
+ if (error instanceof SubagentError && error.code === "SUBAGENT_CONTROL_PROJECTIONS_UNAVAILABLE") throw new RemoteError("subagent/projections-unavailable", "subagent catalog is unavailable: this deployment does not mount the sessionProjections registry (load @deepseek-ai/dsh-session-projection)", {}, { cause: error });
93
+ throw new RemoteError("gateway/internal", "subagent catalog read failed", {}, { cause: error });
94
+ }
95
+ /**
96
+ * Refuse one continuation prompt without exposing provider detail: admission
97
+ * failures the caller can act on keep their own code, everything else is
98
+ * internal.
99
+ * @param error - the thrown value.
100
+ * @param childSessionId - the addressed child.
101
+ * @param signal - the caller's cancellation.
102
+ * @returns Never — the refusal is thrown.
103
+ * @throws {RemoteError} always.
104
+ */
105
+ function rejectPrompt(error, childSessionId, signal) {
106
+ if (isCancellation(error, signal)) throw new RemoteError("gateway/cancelled", "subagent prompt was cancelled", {}, { cause: error });
107
+ if (error instanceof AttachmentError) throw new RemoteError("subagent/attachment-invalid", error.message, { reason: error.code }, { cause: error });
108
+ if (error instanceof SubagentError) switch (error.code) {
109
+ case "MODEL_DOES_NOT_SUPPORT_IMAGES": throw new RemoteError("subagent/attachment-invalid", error.message, { reason: error.code }, { cause: error });
110
+ case "NOT_RESUMABLE": throw new RemoteError("subagent/not-resumable", "subagent cannot be resumed", { childSessionId }, { cause: error });
111
+ case "UNAUTHORIZED": throw new RemoteError("subagent/unauthorized", "subagent does not belong to this parent", { childSessionId }, { cause: error });
112
+ case "DRAINING":
113
+ case "ACTIVATION_CLOSING":
114
+ case "CONTINUATION_UNAVAILABLE":
115
+ case "PERSISTENCE_UNAVAILABLE": throw new RemoteError("subagent/delivery-unavailable", "subagent follow-up is temporarily unavailable", { childSessionId }, { cause: error });
116
+ default: break;
117
+ }
118
+ throw new RemoteError("gateway/internal", "subagent prompt failed", {}, { cause: error });
119
+ }
120
+ function isCancellation(error, signal) {
121
+ return signal.aborted || error instanceof SubagentError && error.code === "CANCELLED";
122
+ }
123
+ //#endregion
25
124
  //#region lib/types/depth.js
26
125
  /**
27
126
  * Delegation-depth accounting: the recursion budget a parent passes to its
@@ -325,7 +424,7 @@ function renderThrown(value) {
325
424
  * Supporting another composition input is a deliberate version change, never
326
425
  * an implicit extra field.
327
426
  */
328
- const SUBAGENT_DESCRIPTOR_VERSION = 2;
427
+ const SUBAGENT_DESCRIPTOR_VERSION = 3;
329
428
  const DESCRIPTOR_BASE_KEYS = [
330
429
  "version",
331
430
  "mode",
@@ -337,6 +436,7 @@ const CONTINUABLE_DESCRIPTOR_KEYS = new Set([
337
436
  ...DESCRIPTOR_BASE_KEYS,
338
437
  "agentProvider",
339
438
  "agentModel",
439
+ "agentReasoningEffort",
340
440
  "persona",
341
441
  "toolFilter"
342
442
  ]);
@@ -383,7 +483,7 @@ function parseSubagentDescriptor(value) {
383
483
  if (!isRecord(value)) throw new Error("persisted subagent descriptor payload must be an object");
384
484
  const version = value["version"];
385
485
  if (typeof version !== "number") throw new Error("persisted subagent descriptor version must be a number");
386
- if (version !== 2) return void 0;
486
+ if (version !== 3) return void 0;
387
487
  const mode = value["mode"];
388
488
  if (mode !== "one-shot" && mode !== "continuable") throw new Error("persisted subagent descriptor mode must be \"one-shot\" or \"continuable\"");
389
489
  assertKnownKeys(value, mode === "one-shot" ? ONE_SHOT_DESCRIPTOR_KEYS : CONTINUABLE_DESCRIPTOR_KEYS, "payload");
@@ -392,7 +492,7 @@ function parseSubagentDescriptor(value) {
392
492
  if (mode === "one-shot") {
393
493
  const label = optionalString(value, "label");
394
494
  return {
395
- version: 2,
495
+ version: 3,
396
496
  mode,
397
497
  provider,
398
498
  ...label !== void 0 ? { label } : {}
@@ -402,32 +502,35 @@ function parseSubagentDescriptor(value) {
402
502
  if (typeof label !== "string") throw new Error("persisted subagent descriptor label must be a string");
403
503
  const agentProvider = optionalString(value, "agentProvider");
404
504
  const agentModel = optionalString(value, "agentModel");
505
+ const agentReasoningEffort = optionalString(value, "agentReasoningEffort");
405
506
  const persona = optionalString(value, "persona");
406
507
  const toolFilter = Object.hasOwn(value, "toolFilter") ? parseToolFilter(value["toolFilter"]) : void 0;
407
508
  return {
408
- version: 2,
509
+ version: 3,
409
510
  mode,
410
511
  provider,
411
512
  label,
412
513
  ...agentProvider !== void 0 ? { agentProvider } : {},
413
514
  ...agentModel !== void 0 ? { agentModel } : {},
515
+ ...agentReasoningEffort !== void 0 ? { agentReasoningEffort } : {},
414
516
  ...persona !== void 0 ? { persona } : {},
415
517
  ...toolFilter !== void 0 ? { toolFilter } : {}
416
518
  };
417
519
  }
418
520
  function snapshotSubagentDescriptor(input) {
419
521
  const snapshot = snapshotJsonValue(input.mode === "one-shot" ? {
420
- version: 2,
522
+ version: 3,
421
523
  mode: input.mode,
422
524
  provider: input.provider,
423
525
  ...input.label !== void 0 ? { label: input.label } : {}
424
526
  } : {
425
- version: 2,
527
+ version: 3,
426
528
  mode: input.mode,
427
529
  provider: input.provider,
428
530
  label: input.label,
429
531
  ...input.agentProvider !== void 0 ? { agentProvider: input.agentProvider } : {},
430
532
  ...input.agentModel !== void 0 ? { agentModel: input.agentModel } : {},
533
+ ...input.agentReasoningEffort !== void 0 ? { agentReasoningEffort: input.agentReasoningEffort } : {},
431
534
  ...input.persona !== void 0 ? { persona: input.persona } : {},
432
535
  ...input.toolFilter !== void 0 ? { toolFilter: input.toolFilter } : {}
433
536
  });
@@ -490,25 +593,51 @@ function resolveChildDepth(parent, maxDepth) {
490
593
  return childDepth;
491
594
  }
492
595
  /**
493
- * Resolve the child's `AgentOptions`: the parent's provider/model/maxTokens
494
- * route unless the request overrides it, stamped with the child's own
495
- * delegation depth.
596
+ * Resolve the parent values inherited by a child. The latest request header
597
+ * owns provider, model, and reasoning effort after request-time selection;
598
+ * creation options remain the fallback before the first request and retain
599
+ * the configured output-token limit.
600
+ * @param parent - delegating parent Agent.
601
+ * @returns detached Agent options for child-option merging.
602
+ */
603
+ function parentAgentOptionsForDelegation(parent) {
604
+ const requestConfig = parent.session.requestHeader()?.config;
605
+ if (requestConfig === void 0) return { ...parent.options };
606
+ const { provider: _createdProvider, model: _createdModel, reasoningEffort: _createdReasoningEffort, ...createdOptions } = parent.options;
607
+ return {
608
+ ...createdOptions,
609
+ provider: requestConfig.provider,
610
+ model: requestConfig.model,
611
+ ...requestConfig.reasoningEffort === void 0 ? {} : { reasoningEffort: requestConfig.reasoningEffort }
612
+ };
613
+ }
614
+ /**
615
+ * Resolve the child's `AgentOptions`: the parent's provider/model,
616
+ * reasoning-effort, and maxTokens values unless the request overrides them,
617
+ * stamped with the child's own delegation depth. Changing the route without
618
+ * naming an effort clears the parent's route-owned effort so the selected
619
+ * model resolves its own default.
496
620
  * @param parent - the delegating parent whose route the child inherits.
497
621
  * @param requested - per-child overrides, if any.
498
622
  * @param childDepth - the resolved delegation depth to stamp.
499
623
  * @returns the resolved options for `ctx.agents.create()`.
500
624
  */
501
625
  function resolveChildAgentOptions(parent, requested, childDepth) {
502
- const parentProvider = parent.options.provider;
503
- const parentModel = parent.options.model;
504
- const parentMaxTokens = parent.options.maxTokens;
505
- return {
626
+ const parentOptions = parentAgentOptionsForDelegation(parent);
627
+ const parentProvider = parentOptions.provider;
628
+ const parentModel = parentOptions.model;
629
+ const parentReasoningEffort = parentOptions.reasoningEffort;
630
+ const parentMaxTokens = parentOptions.maxTokens;
631
+ const resolved = {
506
632
  ...parentProvider !== void 0 ? { provider: parentProvider } : {},
507
633
  ...parentModel !== void 0 ? { model: parentModel } : {},
634
+ ...parentReasoningEffort !== void 0 ? { reasoningEffort: parentReasoningEffort } : {},
508
635
  ...parentMaxTokens !== void 0 ? { maxTokens: parentMaxTokens } : {},
509
636
  ...requested,
510
637
  subagentDepth: childDepth
511
638
  };
639
+ if ((resolved.provider !== parentProvider || resolved.model !== parentModel) && requested?.reasoningEffort === void 0) delete resolved.reasoningEffort;
640
+ return resolved;
512
641
  }
513
642
  /**
514
643
  * Build the child session's durable creation metadata: the parent's workspace,
@@ -571,12 +700,12 @@ function applyChildComposition(childCtx, parent, composition) {
571
700
  childCtx.get("agentPresets")?.composeFrom(childCtx, parent.ctx);
572
701
  childCtx.systemPrompt.context({
573
702
  name: "subagent:delegation",
574
- order: 120,
703
+ order: childCtx.systemPrompt.getContextOrder("SUBAGENT_DELEGATION"),
575
704
  text: SUBAGENT_DELEGATION_CONTEXT
576
705
  });
577
706
  if (composition.persona !== void 0) childCtx.systemPrompt.section({
578
707
  name: "deployment:persona",
579
- order: 0,
708
+ order: childCtx.systemPrompt.getSectionOrder("DEPLOYMENT_PERSONA"),
580
709
  text: composition.persona
581
710
  });
582
711
  if (composition.toolFilter !== void 0) childCtx.tools.restrict(composition.toolFilter);
@@ -664,6 +793,64 @@ function seedDescriptorTurn(childId, seed, descriptor) {
664
793
  *
665
794
  * @module @deepseek-ai/dsh-subagent
666
795
  */
796
+ var __addDisposableResource$1 = function(env, value, async) {
797
+ if (value !== null && value !== void 0) {
798
+ if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
799
+ var dispose, inner;
800
+ if (async) {
801
+ if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
802
+ dispose = value[Symbol.asyncDispose];
803
+ }
804
+ if (dispose === void 0) {
805
+ if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
806
+ dispose = value[Symbol.dispose];
807
+ if (async) inner = dispose;
808
+ }
809
+ if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
810
+ if (inner) dispose = function() {
811
+ try {
812
+ inner.call(this);
813
+ } catch (e) {
814
+ return Promise.reject(e);
815
+ }
816
+ };
817
+ env.stack.push({
818
+ value,
819
+ dispose,
820
+ async
821
+ });
822
+ } else if (async) env.stack.push({ async: true });
823
+ return value;
824
+ };
825
+ var __disposeResources$1 = (function(SuppressedError) {
826
+ return function(env) {
827
+ function fail(e) {
828
+ env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
829
+ env.hasError = true;
830
+ }
831
+ var r, s = 0;
832
+ function next() {
833
+ while (r = env.stack.pop()) try {
834
+ if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
835
+ if (r.dispose) {
836
+ var result = r.dispose.call(r.value);
837
+ if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) {
838
+ fail(e);
839
+ return next();
840
+ });
841
+ } else s |= 1;
842
+ } catch (e) {
843
+ fail(e);
844
+ }
845
+ if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
846
+ if (env.hasError) throw env.error;
847
+ }
848
+ return next();
849
+ };
850
+ })(typeof SuppressedError === "function" ? SuppressedError : function(error, suppressed, message) {
851
+ var e = new Error(message);
852
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
853
+ });
667
854
  /**
668
855
  * Read one Activation's current disposal transaction. This indirection exists
669
856
  * because TypeScript would otherwise narrow repeated reads of the mutable field
@@ -774,17 +961,20 @@ var SubagentContinuationManager = class {
774
961
  this.assertAdmitting(parent);
775
962
  const persistence = this.requirePersistence();
776
963
  assertSubagentMaxDepth(request.maxDepth);
777
- const childId = spec.childId ?? SessionId(randomUUID());
964
+ const childId = spec.childId ?? brandString(randomUUID());
778
965
  this.assertChildIdAvailable(childId);
779
966
  const childDepth = resolveChildDepth(parent, request.maxDepth);
780
- const agentProvider = request.agentOptions?.provider ?? parent.options.provider;
781
- const agentModel = request.agentOptions?.model ?? parent.options.model;
967
+ const agentOptions = resolveChildAgentOptions(parent, request.agentOptions, childDepth);
968
+ const agentProvider = agentOptions.provider;
969
+ const agentModel = agentOptions.model;
970
+ const agentReasoningEffort = agentOptions.reasoningEffort;
782
971
  const descriptor = snapshotSubagentDescriptor({
783
972
  mode: "continuable",
784
973
  provider: spec.provider,
785
974
  label: spec.label,
786
975
  ...agentProvider !== void 0 ? { agentProvider } : {},
787
976
  ...agentModel !== void 0 ? { agentModel } : {},
977
+ ...agentReasoningEffort !== void 0 ? { agentReasoningEffort } : {},
788
978
  ...request.persona !== void 0 ? { persona: request.persona } : {},
789
979
  ...request.toolFilter !== void 0 ? { toolFilter: request.toolFilter } : {}
790
980
  });
@@ -820,7 +1010,7 @@ var SubagentContinuationManager = class {
820
1010
  meta: childSessionMeta(parent, childDepth, lineageSeedLength),
821
1011
  delegatedPolicies
822
1012
  },
823
- agentOptions: resolveChildAgentOptions(parent, request.agentOptions, childDepth),
1013
+ agentOptions,
824
1014
  composition: {
825
1015
  persona: request.persona,
826
1016
  toolFilter: request.toolFilter
@@ -858,11 +1048,19 @@ var SubagentContinuationManager = class {
858
1048
  const live = await this.locks.run(childId, async () => {
859
1049
  const activation = this.activations.get(childId);
860
1050
  if (activation === void 0) return this.coldResume(parent, childId, content, options);
1051
+ const disposal = activation.disposal;
861
1052
  /* v8 ignore next 3 -- the send-versus-dispose cutoff: reaching this arm needs a
862
1053
  * delivery to observe the transaction inside the same critical section that opened it,
863
1054
  * which no test can schedule deterministically. The behavior is covered end-to-end by
864
1055
  * "cold-resumes a delivery that lost the race with final disposal". */
865
- if (activation.disposal !== void 0) return activation.disposal.then(() => void 0, () => void 0);
1056
+ if (disposal !== void 0) return disposal.then(() => void 0, () => void 0);
1057
+ if (contentHasImage(content)) {
1058
+ await this.assertImageCapable(activation.handle.agent, options.signal);
1059
+ if (activation.disposal !== void 0) {
1060
+ await Promise.allSettled([activation.disposal]);
1061
+ return;
1062
+ }
1063
+ }
866
1064
  return this.submitAdmitted(activation, content, options.source, parent, options.signal);
867
1065
  });
868
1066
  /* v8 ignore start -- only the lost-cutoff arm above returns undefined, so only that
@@ -1135,48 +1333,61 @@ var SubagentContinuationManager = class {
1135
1333
  return "settled";
1136
1334
  }
1137
1335
  /**
1138
- * Cold-resume a persisted child: inspect and authorize its Session, fold the
1336
+ * Cold-resume a persisted child: retain and authorize its prepared Session, fold the
1139
1337
  * generic descriptor, create the Activation through `ctx.agents.resume()`,
1140
1338
  * and submit the waiting turn. This never dispatches through a subagent
1141
1339
  * provider — the persisted Session already holds the initial prefix and the
1142
1340
  * descriptor is the whole reconstruction input.
1143
1341
  */
1144
1342
  async coldResume(parent, childId, content, options) {
1145
- const persistence = this.requirePersistence();
1146
- let loaded;
1147
- try {
1148
- loaded = await persistence.inspect(childId, options.signal);
1149
- } catch (error) {
1150
- options.signal.throwIfAborted();
1151
- throw new SubagentError(`subagent "${childId}" is unavailable`, "NOT_RESUMABLE", { cause: error });
1152
- }
1153
- options.signal.throwIfAborted();
1154
- this.assertAdmitting(parent);
1155
- this.authorizeLineage(parent, childId, loaded.meta.parentSession);
1156
- const descriptor = foldSubagentDescriptor(loaded.events.slice(loaded.meta.seedLength ?? 0));
1157
- if (descriptor === void 0 || descriptor.mode !== "continuable") throw new SubagentError(`subagent "${childId}" has no supported continuation state and cannot be resumed; do not retry send_message with this id`, "NOT_RESUMABLE");
1158
- let activation;
1343
+ const env_1 = {
1344
+ stack: [],
1345
+ error: void 0,
1346
+ hasError: false
1347
+ };
1159
1348
  try {
1160
- activation = await this.materialize({
1161
- childId,
1162
- provider: descriptor.provider,
1163
- parent,
1164
- agentOptions: {
1165
- ...descriptor.agentProvider !== void 0 ? { provider: descriptor.agentProvider } : {},
1166
- ...descriptor.agentModel !== void 0 ? { model: descriptor.agentModel } : {}
1167
- },
1168
- composition: {
1169
- persona: descriptor.persona,
1170
- toolFilter: descriptor.toolFilter
1171
- },
1172
- signal: options.signal
1173
- });
1174
- } catch (error) {
1175
- options.signal.throwIfAborted();
1176
- if (error instanceof SubagentError) throw error;
1177
- throw new SubagentError(`subagent "${childId}" is unavailable`, "NOT_RESUMABLE", { cause: error });
1349
+ const query = this.requireSessionQuery();
1350
+ let observation;
1351
+ try {
1352
+ observation = await query.observeSession(childId, { signal: options.signal });
1353
+ } catch (error) {
1354
+ options.signal.throwIfAborted();
1355
+ throw new SubagentError(`subagent "${childId}" is unavailable`, "NOT_RESUMABLE", { cause: error });
1356
+ }
1357
+ const source = __addDisposableResource$1(env_1, observation, false);
1358
+ this.assertAdmitting(parent);
1359
+ this.authorizeLineage(parent, childId, source.header.parentSession);
1360
+ const descriptor = foldSubagentDescriptor(source.events.slice(source.header.seedLength ?? 0));
1361
+ if (descriptor === void 0 || descriptor.mode !== "continuable") throw new SubagentError(`subagent "${childId}" has no supported continuation state and cannot be resumed; do not retry send_message with this id`, "NOT_RESUMABLE");
1362
+ let activation;
1363
+ try {
1364
+ activation = await this.materialize({
1365
+ childId,
1366
+ provider: descriptor.provider,
1367
+ parent,
1368
+ agentOptions: {
1369
+ ...descriptor.agentProvider !== void 0 ? { provider: descriptor.agentProvider } : {},
1370
+ ...descriptor.agentModel !== void 0 ? { model: descriptor.agentModel } : {},
1371
+ ...descriptor.agentReasoningEffort !== void 0 ? { reasoningEffort: ReasoningEffortId(descriptor.agentReasoningEffort) } : {}
1372
+ },
1373
+ composition: {
1374
+ persona: descriptor.persona,
1375
+ toolFilter: descriptor.toolFilter
1376
+ },
1377
+ signal: options.signal
1378
+ });
1379
+ } catch (error) {
1380
+ options.signal.throwIfAborted();
1381
+ if (error instanceof SubagentError) throw error;
1382
+ throw new SubagentError(`subagent "${childId}" is unavailable`, "NOT_RESUMABLE", { cause: error });
1383
+ }
1384
+ return await this.submitMaterialized(activation, content, options.source, parent, options.signal);
1385
+ } catch (e_1) {
1386
+ env_1.error = e_1;
1387
+ env_1.hasError = true;
1388
+ } finally {
1389
+ __disposeResources$1(env_1);
1178
1390
  }
1179
- return this.submitMaterialized(activation, content, options.source, parent, options.signal);
1180
1391
  }
1181
1392
  /**
1182
1393
  * Submit to a freshly materialized Activation or roll it back completely.
@@ -1189,6 +1400,10 @@ var SubagentContinuationManager = class {
1189
1400
  */
1190
1401
  async submitMaterialized(activation, content, source, parent, signal) {
1191
1402
  try {
1403
+ if (contentHasImage(content)) {
1404
+ await this.assertImageCapable(activation.handle.agent, signal);
1405
+ if (activation.disposal !== void 0) throw new SubagentError(`subagent "${activation.childId}" is closing`, "ACTIVATION_CLOSING");
1406
+ }
1192
1407
  return this.submitAdmitted(activation, content, source, parent, signal);
1193
1408
  } catch (error) {
1194
1409
  /* v8 ignore next -- rollback disposal failures must not mask the
@@ -1198,6 +1413,28 @@ var SubagentContinuationManager = class {
1198
1413
  }
1199
1414
  }
1200
1415
  /**
1416
+ * Refuse image content addressed to a child whose model accepts text only.
1417
+ * Callers guard with `contentHasImage`, so text-only delivery never awaits.
1418
+ * The check runs inside the per-child delivery lock, before the message
1419
+ * exists, so a rejection leaves no partial user message. When the child's
1420
+ * route is not fixed by its options (a request-waterfall listener owns it)
1421
+ * or no LLM registry is composed, delivery proceeds and the LLM layer's
1422
+ * text-only projection replaces each image with its stable placeholder.
1423
+ * @param agent - the live or freshly materialized child agent.
1424
+ * @param signal - caller cancellation bounding the model-info read.
1425
+ * @throws {SubagentError} `MODEL_DOES_NOT_SUPPORT_IMAGES` when the child's resolved model declines image input.
1426
+ */
1427
+ async assertImageCapable(agent, signal) {
1428
+ const { provider, model } = agent.options;
1429
+ if (provider === void 0 || model === void 0) return;
1430
+ const llm = this.ctx.get("llm");
1431
+ /* v8 ignore next -- a deployment without the LLM registry serves no model
1432
+ * to refuse against; delivery then defers to the text-only projection. */
1433
+ if (llm === void 0) return;
1434
+ const info = await llm.resolveModelInfo(provider, model, signal);
1435
+ if (info.inputModalities !== void 0 && !info.inputModalities.includes("image")) throw new SubagentError(`Model "${model}" does not support image input.`, "MODEL_DOES_NOT_SUPPORT_IMAGES");
1436
+ }
1437
+ /**
1201
1438
  * Create or resume the child Agent through the private activation-owner
1202
1439
  * scope, install the handle in a fresh Activation, and register ownership on
1203
1440
  * a continuation-managed parent. Rejection leaves no Activation, no handle,
@@ -1541,6 +1778,12 @@ var SubagentContinuationManager = class {
1541
1778
  if (persistence === void 0) throw new SubagentError("continuable subagents require session persistence (load a dsh-session-persistence backend)", "PERSISTENCE_UNAVAILABLE");
1542
1779
  return persistence;
1543
1780
  }
1781
+ /** Resolve the Session query service used for cold child observations. */
1782
+ requireSessionQuery() {
1783
+ const query = this.ctx.get("sessionQuery");
1784
+ if (query === void 0) throw new SubagentError("continuable subagents require session query (load @deepseek-ai/dsh-session-query)", "CONTINUATION_UNAVAILABLE");
1785
+ return query;
1786
+ }
1544
1787
  };
1545
1788
  //#endregion
1546
1789
  //#region lib/types/activation-setup-registry.js
@@ -1674,14 +1917,14 @@ var SubagentActivationSetupRegistry = class {
1674
1917
  //#region lib/types/list-children.js
1675
1918
  /**
1676
1919
  * Read-only enumeration of durable subagent children and descendant trees
1677
- * straight from the live session store and optional session persistence — no
1678
- * query service. Candidates come from one live-preferred corpus; each child's
1679
- * mode/label is the registered `subagent` projection unit's value, resolved
1920
+ * through the Session query service. Candidates come from one live-preferred
1921
+ * corpus; each child's mode/label is the registered `subagent` projection
1922
+ * unit's value, resolved
1680
1923
  * down a three-rung ladder: the registry's watermark cache for a live child,
1681
1924
  * a durable projection-cache row when it serves an own-suffix identity (the
1682
- * seq gate), and one persistence inspection folded through the registry
1683
- * otherwise, validated against the enumerated lifecycle. The projection fold
1684
- * is the single classification authority — this module parses no descriptor
1925
+ * seq gate), and one shared Session observation otherwise, validated against
1926
+ * the enumerated lifecycle. The projection fold is the single classification
1927
+ * authority — this module parses no descriptor
1685
1928
  * itself. Absent persistence, enumeration is live-only: a cold child is
1686
1929
  * unreachable for resume anyway, so its absence is capability absence, not an
1687
1930
  * error. The module owns no catalog state and does not consult Activation,
@@ -1689,10 +1932,68 @@ var SubagentActivationSetupRegistry = class {
1689
1932
  *
1690
1933
  * @module @deepseek-ai/dsh-subagent
1691
1934
  */
1935
+ var __addDisposableResource = function(env, value, async) {
1936
+ if (value !== null && value !== void 0) {
1937
+ if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
1938
+ var dispose, inner;
1939
+ if (async) {
1940
+ if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
1941
+ dispose = value[Symbol.asyncDispose];
1942
+ }
1943
+ if (dispose === void 0) {
1944
+ if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
1945
+ dispose = value[Symbol.dispose];
1946
+ if (async) inner = dispose;
1947
+ }
1948
+ if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
1949
+ if (inner) dispose = function() {
1950
+ try {
1951
+ inner.call(this);
1952
+ } catch (e) {
1953
+ return Promise.reject(e);
1954
+ }
1955
+ };
1956
+ env.stack.push({
1957
+ value,
1958
+ dispose,
1959
+ async
1960
+ });
1961
+ } else if (async) env.stack.push({ async: true });
1962
+ return value;
1963
+ };
1964
+ var __disposeResources = (function(SuppressedError) {
1965
+ return function(env) {
1966
+ function fail(e) {
1967
+ env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
1968
+ env.hasError = true;
1969
+ }
1970
+ var r, s = 0;
1971
+ function next() {
1972
+ while (r = env.stack.pop()) try {
1973
+ if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
1974
+ if (r.dispose) {
1975
+ var result = r.dispose.call(r.value);
1976
+ if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) {
1977
+ fail(e);
1978
+ return next();
1979
+ });
1980
+ } else s |= 1;
1981
+ } catch (e) {
1982
+ fail(e);
1983
+ }
1984
+ if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
1985
+ if (env.hasError) throw env.error;
1986
+ }
1987
+ return next();
1988
+ };
1989
+ })(typeof SuppressedError === "function" ? SuppressedError : function(error, suppressed, message) {
1990
+ var e = new Error(message);
1991
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
1992
+ });
1692
1993
  /**
1693
- * Concurrent cold inspections per listing; a constant because it bounds one
1694
- * read-only scan of local media, not deployment behavior. Should a networked
1695
- * persistence backend appear, promote it to a validated `Config` field.
1994
+ * Concurrent cold observations per explicit catalog listing. Current Session
1995
+ * persistence providers are local; a networked provider must promote this to
1996
+ * a validated deployment setting.
1696
1997
  */
1697
1998
  const COLD_READ_CONCURRENCY = 4;
1698
1999
  /**
@@ -1700,9 +2001,8 @@ const COLD_READ_CONCURRENCY = 4;
1700
2001
  * live-preferred merge of `ctx.sessions` and optional session persistence,
1701
2002
  * serving each identity from the `subagent` projection unit: the registry's
1702
2003
  * watermark snapshot for a live child; for a cold one, a durable
1703
- * projection-cache row when it serves an own-suffix identity (the seq gate),
1704
- * else one bounded-concurrency persistence inspection folded through the
1705
- * registry.
2004
+ * projection-cache read when it serves an own-suffix identity (the seq gate),
2005
+ * else one bounded-concurrency shared Session observation.
1706
2006
  * @see SubagentRuntime.listChildren for the public cancellation and failure contract.
1707
2007
  * @param ctx - context carrying the session store, the projection registry,
1708
2008
  * optional persistence, and the optional projection cache.
@@ -1751,32 +2051,30 @@ async function prepareListing(ctx, signal) {
1751
2051
  const sessions = ctx.get("sessions");
1752
2052
  if (sessions === void 0) throw new SubagentError("listing subagents requires the session store (load @deepseek-ai/dsh-session)", "SUBAGENT_CONTROL_SESSION_STORE_UNAVAILABLE");
1753
2053
  assertListingNotCancelled(signal);
1754
- const persistence = ctx.get("sessionPersistence");
2054
+ const query = ctx.get("sessionQuery");
2055
+ if (query === void 0) throw new SubagentError("listing subagents requires the sessionQuery service (load @deepseek-ai/dsh-session-query)", "SUBAGENT_CONTROL_QUERY_UNAVAILABLE");
1755
2056
  const cache = ctx.get("sessionProjectionCache");
1756
- let persistedHeaders = [];
1757
- if (persistence !== void 0) {
1758
- try {
1759
- persistedHeaders = await persistence.list(signal);
1760
- } catch (error) {
1761
- assertListingNotCancelled(signal);
1762
- throw error;
1763
- }
2057
+ let records;
2058
+ try {
2059
+ records = await query.listSessions(signal);
2060
+ } catch (error) {
1764
2061
  assertListingNotCancelled(signal);
2062
+ throw error;
1765
2063
  }
2064
+ assertListingNotCancelled(signal);
1766
2065
  const corpus = /* @__PURE__ */ new Map();
1767
- for (const header of persistedHeaders) corpus.set(header.id, {
1768
- header,
1769
- live: void 0
1770
- });
1771
- for (const session of sessions.list()) corpus.set(session.header.id, {
1772
- header: session.header,
1773
- live: session
1774
- });
2066
+ for (const record of records) {
2067
+ const live = sessions.get(record.header.id);
2068
+ corpus.set(record.header.id, {
2069
+ header: live?.header ?? record.header,
2070
+ live
2071
+ });
2072
+ }
1775
2073
  const subagentParents = /* @__PURE__ */ new Set();
1776
2074
  for (const record of corpus.values()) if (record.header.origin === "subagent" && record.header.parentSession !== void 0) subagentParents.add(record.header.parentSession);
1777
2075
  return {
1778
2076
  projections,
1779
- persistence,
2077
+ query,
1780
2078
  cache,
1781
2079
  corpus,
1782
2080
  subagentParents
@@ -1784,7 +2082,7 @@ async function prepareListing(ctx, signal) {
1784
2082
  }
1785
2083
  /** Resolve projection-backed rows for aligned candidates with bounded cold reads. */
1786
2084
  async function resolveCandidateRows(candidates, listing, signal) {
1787
- const { projections, persistence, cache, subagentParents } = listing;
2085
+ const { projections, query, cache, subagentParents } = listing;
1788
2086
  const rows = Array.from({ length: candidates.length });
1789
2087
  const coldReads = [];
1790
2088
  candidates.forEach((candidate, index) => {
@@ -1798,7 +2096,7 @@ async function resolveCandidateRows(candidates, listing, signal) {
1798
2096
  }
1799
2097
  let identity;
1800
2098
  try {
1801
- identity = projections.snapshot(candidate.live).values.subagent;
2099
+ identity = projections.snapshot(candidate.live, ["subagent"]).values.subagent;
1802
2100
  } catch {
1803
2101
  rows[index] = {
1804
2102
  kind: "diagnostic",
@@ -1807,13 +2105,13 @@ async function resolveCandidateRows(candidates, listing, signal) {
1807
2105
  };
1808
2106
  return;
1809
2107
  }
1810
- if (identity === void 0 || identity === null) return;
2108
+ if (identity === void 0 || identity === null || identity.seq < (candidate.header.seedLength ?? 0)) return;
1811
2109
  rows[index] = childRow(childId, identity, "running", subagentParents.has(childId));
1812
2110
  });
1813
- if (persistence !== void 0 && coldReads.length > 0) {
2111
+ if (coldReads.length > 0) {
1814
2112
  const queue = [...coldReads];
1815
2113
  await Promise.all(Array.from({ length: Math.min(COLD_READ_CONCURRENCY, queue.length) }, async () => {
1816
- for (let job = queue.shift(); job !== void 0; job = queue.shift()) rows[job.index] = await resolveColdIdentity(persistence, projections, cache, job.header, subagentParents.has(job.header.id), signal);
2114
+ for (let job = queue.shift(); job !== void 0; job = queue.shift()) rows[job.index] = await resolveColdIdentity(query, cache, job.header, subagentParents.has(job.header.id), signal);
1817
2115
  }));
1818
2116
  }
1819
2117
  assertListingNotCancelled(signal);
@@ -1859,58 +2157,61 @@ function compareCorpusRecords(a, b) {
1859
2157
  /**
1860
2158
  * Resolve one cold candidate down the remaining ladder: a durable
1861
2159
  * projection-cache row when it serves an own-suffix identity (the seq gate),
1862
- * otherwise one persistence inspection folded through the projection
1863
- * registry (the same detached recipe the API proxy uses for detached session
1864
- * projections). A failed inspection is one transient `unavailable` row
1865
- * retried on the next listing; an inspection naming another lifecycle, and a
2160
+ * otherwise one shared Session observation. An absent or transiently failed
2161
+ * observation is one `unavailable` row retried on the next listing; an observation
2162
+ * source naming another lifecycle, and a
1866
2163
  * settled log the fold cannot identify — or that makes any registered unit
1867
2164
  * throw — are final, so they report `corrupt`.
1868
2165
  */
1869
- async function resolveColdIdentity(persistence, projections, cache, header, hasChildren, signal) {
1870
- const childId = header.id;
1871
- if (cache !== void 0) {
1872
- let cached;
2166
+ async function resolveColdIdentity(query, cache, header, hasChildren, signal) {
2167
+ const env_1 = {
2168
+ stack: [],
2169
+ error: void 0,
2170
+ hasError: false
2171
+ };
2172
+ try {
2173
+ const childId = header.id;
2174
+ if (cache !== void 0) {
2175
+ let cached;
2176
+ try {
2177
+ cached = cache.cachedSnapshot(header, ["subagent"])?.values.subagent;
2178
+ } catch {
2179
+ cached = void 0;
2180
+ }
2181
+ if (cached !== void 0 && cached !== null && cached.seq >= (header.seedLength ?? 0)) return childRow(childId, cached, "inactive", hasChildren);
2182
+ }
2183
+ assertListingNotCancelled(signal);
2184
+ let observation;
1873
2185
  try {
1874
- cached = cache.cachedSnapshot(header)?.values.subagent;
1875
- } catch {
1876
- cached = void 0;
2186
+ observation = await query.observeSession(childId, { ...signal === void 0 ? {} : { signal } });
2187
+ } catch (error) {
2188
+ assertListingNotCancelled(signal);
2189
+ return {
2190
+ kind: "diagnostic",
2191
+ id: childId,
2192
+ reason: sessionQueryCode(error) === "SESSION_QUERY_CORRUPT_SESSION" || sessionQueryCode(error) === "SESSION_QUERY_SOURCE_CONFLICT" ? "corrupt" : "unavailable"
2193
+ };
1877
2194
  }
1878
- if (cached !== void 0 && cached !== null && cached.seq >= (header.seedLength ?? 0)) return childRow(childId, cached, "inactive", hasChildren);
1879
- }
1880
- assertListingNotCancelled(signal);
1881
- let inspected;
1882
- try {
1883
- inspected = await persistence.inspect(childId, signal);
1884
- } catch {
2195
+ const ownedObservation = __addDisposableResource(env_1, observation, false);
1885
2196
  assertListingNotCancelled(signal);
1886
- return {
2197
+ if (!sameLifecycle(ownedObservation.header, header)) return {
1887
2198
  kind: "diagnostic",
1888
2199
  id: childId,
1889
- reason: "unavailable"
2200
+ reason: "corrupt"
1890
2201
  };
1891
- }
1892
- assertListingNotCancelled(signal);
1893
- if (!sameLifecycle(inspected.meta, header)) return {
1894
- kind: "diagnostic",
1895
- id: childId,
1896
- reason: "corrupt"
1897
- };
1898
- let identity;
1899
- try {
1900
- identity = projections.restore({}, inspected.events, 0).snapshot.values.subagent;
1901
- } catch {
1902
- return {
2202
+ const identity = ownedObservation.projections?.values.subagent;
2203
+ if (identity === void 0 || identity === null || identity.seq < (header.seedLength ?? 0)) return {
1903
2204
  kind: "diagnostic",
1904
2205
  id: childId,
1905
2206
  reason: "corrupt"
1906
2207
  };
2208
+ return childRow(childId, identity, "inactive", hasChildren);
2209
+ } catch (e_1) {
2210
+ env_1.error = e_1;
2211
+ env_1.hasError = true;
2212
+ } finally {
2213
+ __disposeResources(env_1);
1907
2214
  }
1908
- if (identity === void 0 || identity === null) return {
1909
- kind: "diagnostic",
1910
- id: childId,
1911
- reason: "corrupt"
1912
- };
1913
- return childRow(childId, identity, "inactive", hasChildren);
1914
2215
  }
1915
2216
  /** Materialize one served identity as its child row. */
1916
2217
  function childRow(id, identity, activity, hasChildren) {
@@ -1938,7 +2239,9 @@ const LIFECYCLE_WITNESS_KEYS = [
1938
2239
  "cwd",
1939
2240
  "parentSession",
1940
2241
  "seedLength",
1941
- "delegationDepth"
2242
+ "delegationDepth",
2243
+ "origin",
2244
+ "agentPreset"
1942
2245
  ];
1943
2246
  /** Whether an inspected log still belongs to the enumerated lifecycle. */
1944
2247
  function sameLifecycle(meta, expected) {
@@ -1948,6 +2251,9 @@ function sameLifecycle(meta, expected) {
1948
2251
  function assertListingNotCancelled(signal) {
1949
2252
  if (signal?.aborted) throw new SubagentError("subagent listing was cancelled", "CANCELLED");
1950
2253
  }
2254
+ function sessionQueryCode(error) {
2255
+ return error instanceof Error && "code" in error ? error.code : void 0;
2256
+ }
1951
2257
  //#endregion
1952
2258
  //#region lib/types/projection.js
1953
2259
  /**
@@ -2127,13 +2433,21 @@ function limitSubagentDiagnostic(diagnostic) {
2127
2433
  while ((bytes[prefixBytes] & 192) === 128) prefixBytes -= 1;
2128
2434
  return utf8Decoder.decode(bytes.subarray(0, prefixBytes)) + DIAGNOSTIC_TRUNCATION_SUFFIX;
2129
2435
  }
2436
+ /** Enforce the byte limit on a provider-returned diagnostic. */
2437
+ function normalizeSubagentDiagnostic(result) {
2438
+ return result.diagnostic === void 0 ? result : {
2439
+ ...result,
2440
+ diagnostic: limitSubagentDiagnostic(result.diagnostic)
2441
+ };
2442
+ }
2130
2443
  /**
2131
2444
  * The capability advertisement of an out-of-process backend: NONE. A child in
2132
2445
  * another process cannot honor parent-enforced start features
2133
- * (`outputSchema`/`maxDepth`/`toolFilter`/`persona`), so the service rejects a
2446
+ * (`agentOptions`/`outputSchema`/`maxDepth`/`toolFilter`/`persona`), so the service rejects a
2134
2447
  * request needing any of them before `start` runs — never accepted-then-ignored.
2135
2448
  */
2136
2449
  const NO_START_CAPABILITIES = Object.freeze({
2450
+ agentOptions: false,
2137
2451
  outputSchema: false,
2138
2452
  depthLimit: false,
2139
2453
  toolFilter: false,
@@ -2221,7 +2535,8 @@ function toError(value) {
2221
2535
  * rejects after publication. A normally completed or rejected attempt resolves
2222
2536
  * as `aborted` when cancellation already settled locally; another rejection is
2223
2537
  * flattened to `stopReason: 'error'` through the contained diagnostic sink.
2224
- * The abort listener is removed on every path.
2538
+ * Provider-returned diagnostics use the same byte limit. The abort listener is
2539
+ * removed on every path.
2225
2540
  * @param parts - the attempt, output snapshot, cancellation state, sink, and signal wiring.
2226
2541
  * @returns the terminal result (never a rejection).
2227
2542
  */
@@ -2231,7 +2546,7 @@ async function settleRunResult(parts) {
2231
2546
  return parts.cancelled() ? {
2232
2547
  output: parts.collectOutput(),
2233
2548
  stopReason: "aborted"
2234
- } : result;
2549
+ } : normalizeSubagentDiagnostic(result);
2235
2550
  } catch (error) {
2236
2551
  if (parts.cancelled()) return {
2237
2552
  output: parts.collectOutput(),
@@ -2293,8 +2608,10 @@ function failureDetail(result) {
2293
2608
  return result.diagnostic === void 0 ? stopReason : `${stopReason}; diagnostic: ${result.diagnostic}`;
2294
2609
  }
2295
2610
  /**
2296
- * Map a child result to the task outcome: completed carries final text,
2297
- * aborted is killed, and every other reason is failed without partial output.
2611
+ * Map a child result to the task outcome: completed carries final text, local
2612
+ * cancellation (`aborted` without a diagnostic) is killed, and provider-
2613
+ * diagnosed remote aborts plus every other reason are failed without partial
2614
+ * output.
2298
2615
  * @param result - child terminal result.
2299
2616
  * @returns outcome for the `ctx.jobs` registration.
2300
2617
  */
@@ -2304,7 +2621,10 @@ function runOutcome(result) {
2304
2621
  status: "completed",
2305
2622
  output: finalText(result.output)
2306
2623
  };
2307
- case "aborted": return { status: "killed" };
2624
+ case "aborted": return result.diagnostic === void 0 ? { status: "killed" } : {
2625
+ status: "failed",
2626
+ detail: failureDetail(result)
2627
+ };
2308
2628
  case "error":
2309
2629
  case "max-tokens":
2310
2630
  case "refusal": return {
@@ -2351,10 +2671,8 @@ async function settleRun(run) {
2351
2671
  * child before returning its run, so fulfillment is the single publication and
2352
2672
  * ownership-transfer boundary.
2353
2673
  *
2354
- * Unlike the bash seam (one executor per context, second load throws), MULTIPLE
2355
- * providers coexist here: each registers under a unique name and a caller picks
2356
- * one by name. The shape mirrors the LLM adapter registry
2357
- * (`LlmRuntime.registerAdapter`), not the single-service bash executor.
2674
+ * Multiple providers coexist: each registers under a unique name and callers
2675
+ * select one by name.
2358
2676
  *
2359
2677
  * This package owns the Service Definition role of the capability seam. Service Providers
2360
2678
  * (`@deepseek-ai/dsh-subagent-spawn-in-process`, `-fork`, `-acp`) and the model-facing
@@ -2377,299 +2695,481 @@ async function settleRun(run) {
2377
2695
  *
2378
2696
  * @module @deepseek-ai/dsh-subagent
2379
2697
  */
2698
+ var __runInitializers = function(thisArg, initializers, value) {
2699
+ var useValue = arguments.length > 2;
2700
+ for (var i = 0; i < initializers.length; i++) value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
2701
+ return useValue ? value : void 0;
2702
+ };
2703
+ var __esDecorate = function(ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
2704
+ function accept(f) {
2705
+ if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected");
2706
+ return f;
2707
+ }
2708
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
2709
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
2710
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
2711
+ var _, done = false;
2712
+ for (var i = decorators.length - 1; i >= 0; i--) {
2713
+ var context = {};
2714
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
2715
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
2716
+ context.addInitializer = function(f) {
2717
+ if (done) throw new TypeError("Cannot add initializers after decoration has completed");
2718
+ extraInitializers.push(accept(f || null));
2719
+ };
2720
+ var result = (0, decorators[i])(kind === "accessor" ? {
2721
+ get: descriptor.get,
2722
+ set: descriptor.set
2723
+ } : descriptor[key], context);
2724
+ if (kind === "accessor") {
2725
+ if (result === void 0) continue;
2726
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
2727
+ if (_ = accept(result.get)) descriptor.get = _;
2728
+ if (_ = accept(result.set)) descriptor.set = _;
2729
+ if (_ = accept(result.init)) initializers.unshift(_);
2730
+ } else if (_ = accept(result)) if (kind === "field") initializers.unshift(_);
2731
+ else descriptor[key] = _;
2732
+ }
2733
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
2734
+ done = true;
2735
+ };
2380
2736
  /** Named provider registry with one-shot runs, durable discovery, and continuable-child operations. */
2381
- var SubagentRuntime = class extends Service {
2382
- providers = /* @__PURE__ */ new Map();
2383
- continuations;
2384
- /** Deployment contributions composed into unpublished continuable children. */
2385
- setupRegistry = new SubagentActivationSetupRegistry();
2386
- /**
2387
- * The contained lifecycle-edge publisher. Built here because scoped dispatch
2388
- * keys its carrier by this exact service instance, whose own context filter
2389
- * composes into the carrier.
2390
- */
2391
- emitLifecycle;
2392
- constructor(ctx) {
2393
- super(ctx, "subagents");
2394
- this.emitLifecycle = createLifecycleEmitter(this.ctx, (parent) => scopeTarget(this, parent));
2395
- ctx.inject(["agents"], (childCtx) => {
2396
- const manager = new SubagentContinuationManager(childCtx, {
2397
- prepareContinuable: (name, request) => this.prepareContinuable(name, request),
2398
- observeActivation: (provider, childId, parent) => this.observeActivation(provider, childId, parent)
2399
- }, this.setupRegistry);
2400
- this.continuations = manager;
2401
- childCtx.effect(() => () => {
2402
- /* v8 ignore else -- one injected binding owns the slot until its fiber disposes. */
2403
- if (this.continuations === manager) this.continuations = void 0;
2404
- }, "subagents.continuationBinding()");
2405
- });
2406
- ctx.inject(["sessionProjections"], (projectionCtx) => {
2407
- projectionCtx.sessionProjections.register(subagentTimingProjectionDefinition);
2408
- projectionCtx.sessionProjections.register(subagentIdentityProjectionDefinition);
2409
- });
2410
- }
2411
- /**
2412
- * Establish one durable continuable child and deliver its initial prompt.
2413
- * Resolves when the child's inbox accepts that prompt, without waiting for the
2414
- * turn to start or for the message to reach the Session log; any earlier
2415
- * failure rejects with no ids and rolls back the child entirely.
2416
- * @param spec - provider, delegation request, and caller cancellation.
2417
- * @returns the durable child id and the accepted prompt's message id.
2418
- * @throws when continuation services are unavailable or materialization fails.
2419
- */
2420
- async startContinuable(spec) {
2421
- return this.requireContinuations().startContinuable(spec);
2422
- }
2423
- /**
2424
- * Deliver one later message to a continuable child as its next FIFO turn. A
2425
- * resident child's Agent inbox accepts it directly (waking a `waiting`
2426
- * Activation), while an absent one is cold-resumed from its persisted
2427
- * Session. The Agent inbox is the only queue, so every accepted message has
2428
- * one observable order.
2429
- * @param parent - the exact live direct parent authorizing this delivery.
2430
- * @param childId - durable child session id.
2431
- * @param content - user-role content to deliver.
2432
- * @param options - the message source fields and caller cancellation, which stops the
2433
- * operation only before inbox acceptance.
2434
- * @returns the accepted message's inbox id.
2435
- * @throws when continuation services are unavailable, parent authority is
2436
- * rejected, or the message was not admitted.
2437
- */
2438
- async followup(parent, childId, content, options) {
2439
- return this.requireContinuations().followup(parent, childId, content, options);
2440
- }
2441
- /**
2442
- * Interrupt one live continuable child's current turn under a human parent
2443
- * address or an exact live ancestor Agent. Fire-and-return: the cancel
2444
- * signal is issued before this returns, but the target may keep running
2445
- * until it observes the signal. Unclaimed pending inbox work, the Activation,
2446
- * and published descendants are preserved; claimed work is not requeued.
2447
- * Once the interrupted driver is idle, a waking send resumes the parked FIFO
2448
- * queue. An absent target — including a one-shot or unknown id —
2449
- * is an accepted no-op, as is a manager-less composition, which cannot own a
2450
- * live Activation.
2451
- * @param targetSessionId - the durable child session id to interrupt.
2452
- * @param authority - the human parent address or exact live ancestor Agent.
2453
- * @throws {SubagentError} `UNAUTHORIZED` when the authority does not own the
2454
- * live target.
2455
- */
2456
- interrupt(targetSessionId, authority) {
2457
- this.continuations?.interrupt(targetSessionId, authority);
2458
- }
2459
- /**
2460
- * Deliver selected content from one live continuable child to its durable
2461
- * direct parent. The child is the authority credential; callers cannot name a
2462
- * recipient. Reporting does not conclude the child's turn or Activation.
2463
- * @param child - exact live reporting child.
2464
- * @param content - selected model-facing content.
2465
- * @param options - parent scheduling and pre-acceptance cancellation.
2466
- * @returns the stable identity of the parent-accepted message.
2467
- * @throws when continuation services are unavailable, sender authorization
2468
- * fails, or the direct parent is not live.
2469
- */
2470
- async reportFrom(child, content, options) {
2471
- return this.requireContinuations().reportFrom(child, content, options);
2472
- }
2473
- /**
2474
- * Compose one deployment capability into every continuable child's
2475
- * unpublished creation context on fresh creation and cold resume. Grants wait
2476
- * for the next Activation; removing the contribution revokes every resident
2477
- * installation immediately.
2478
- * @param contribution - synchronous child-scope installer.
2479
- * @returns the exact Cordis effect disposer.
2480
- */
2481
- registerContinuableSetup(contribution) {
2482
- return this.ctx.effect(() => this.setupRegistry.register(contribution), "subagents.registerContinuableSetup()");
2483
- }
2484
- /**
2485
- * Close continuable admission below exact live parent Agents, stop only their
2486
- * visible descendant Activations synchronously, then await admitted scoped
2487
- * materializations and release those forests child-first. The scoped cutoff
2488
- * lasts until each exact parent leaves the registry; unrelated parent trees
2489
- * remain live.
2490
- * @param parents - exact host-owned parent Agents entering teardown.
2491
- * @returns once every retained descendant Activation released its `AgentHandle`.
2492
- * @throws an aggregate error after all branches settle when any failed.
2493
- */
2494
- async drainContinuableDescendants(parents) {
2495
- const manager = this.continuations;
2496
- if (manager === void 0) return;
2497
- await manager.drainDescendants(parents);
2498
- }
2499
- /**
2500
- * Release selected resident continuable direct children of one exact live
2501
- * parent. Other children of the same parent remain admitted and resident.
2502
- * Absent targets and a manager-less composition are accepted no-ops.
2503
- * @param parent - exact live direct parent authorizing the selected release.
2504
- * @param childIds - durable direct-child ids to release when resident.
2505
- * @returns once every selected Activation released its `AgentHandle`.
2506
- * @throws {SubagentError} `UNAUTHORIZED` when a resident target belongs to a
2507
- * different parent or the supplied parent identity is stale.
2508
- */
2509
- async drainContinuableChildren(parent, childIds) {
2510
- const manager = this.continuations;
2511
- if (manager === void 0) return;
2512
- await manager.drainChildren(parent, childIds);
2513
- }
2514
- /**
2515
- * Enumerate the parent's direct session-backed subagents without loading or
2516
- * resuming an Agent and without any query service: the listing merges the live
2517
- * session store with optional session persistence (live-preferred) and
2518
- * serves each child's durable mode/label from the registered `subagent`
2519
- * projection unit down a three-rung ladder — the registry's watermark
2520
- * snapshot for a live child; for a cold one, a durable projection-cache
2521
- * row when the optional cache serves an own-suffix identity (its `seq`
2522
- * gate proves the value postdates the fork seed, where a child's own
2523
- * descriptor is immutable once appended), else one persistence inspection
2524
- * folded through the registry. The
2525
- * projection fold is the single classification authority; per-child
2526
- * diagnostics relay a fold that served no identity or a failed inspection,
2527
- * never a list-time descriptor parse. Absent persistence, enumeration is
2528
- * live-only (a cold child cannot be resumed then either, so its absence is
2529
- * capability absence, not an error). This service consults no Agent
2530
- * registrations, Activations, or providers.
2531
- *
2532
- * Every persistence read receives `signal`, and the listing rechecks
2533
- * cancellation around each of those awaits. Read rejections that settle
2534
- * after an abort become a stable `SubagentError` with code `CANCELLED`.
2535
- * @param parentSessionId - parent session whose direct children are listed.
2536
- * @param signal - caller-owned cancellation forwarded to persistence reads
2537
- * and observed around every read await.
2538
- * @returns children and per-child diagnostics ordered by `createdAt`, then id.
2539
- * @throws {@link SubagentError} when the projection registry or the session
2540
- * store is not mounted, or the caller cancels the listing.
2541
- */
2542
- listChildren(parentSessionId, signal) {
2543
- return listChildren(this.ctx, parentSessionId, signal);
2544
- }
2545
- /**
2546
- * Enumerate the root's complete session-backed subagent tree in stable
2547
- * pre-order from one live-preferred corpus, without loading or resuming an
2548
- * Agent. Ordinary sessions and one-shot children remain traversal nodes so
2549
- * continuable descendants below them are discovered; each returned entry
2550
- * adds its durable `parentId` and root-relative `depth`. Identity resolution,
2551
- * diagnostics, optional persistence, and cancellation follow the same
2552
- * projection-backed contract as {@link listChildren}.
2553
- * @param rootSessionId - session whose complete descendant tree is listed.
2554
- * @param signal - caller-owned cancellation forwarded to persistence reads
2555
- * and observed around every read await.
2556
- * @returns children and per-candidate diagnostics with tree position, in
2557
- * stable pre-order.
2558
- * @throws {@link SubagentError} under the same conditions as {@link listChildren}.
2559
- */
2560
- listDescendants(rootSessionId, signal) {
2561
- return listDescendants(this.ctx, rootSessionId, signal);
2562
- }
2563
- /**
2564
- * Register a provider under its name. Registration is effect-scoped and HMR
2565
- * safe; removing a provider blocks new starts but does not revoke runs that
2566
- * were already returned to their holders.
2567
- * @param provider - the trusted provider implementation.
2568
- * @returns the exact Cordis effect disposer.
2569
- */
2570
- registerProvider(provider) {
2571
- const name = provider.name;
2572
- return this.ctx.effect(function* () {
2573
- if (this.providers.has(name)) throw new SubagentError(`a subagent provider named "${name}" is already registered`, "DUPLICATE_PROVIDER");
2574
- this.providers.set(name, provider);
2575
- yield () => {
2576
- this.providers.delete(name);
2577
- this.emitLifecycle("subagent/provider-removed", name);
2737
+ let SubagentRuntime = (() => {
2738
+ let _classSuper = TypertRemoteService;
2739
+ let _instanceExtraInitializers = [];
2740
+ let _remoteExportList_decorators;
2741
+ let _prompt_decorators;
2742
+ let _interruptByParent_decorators;
2743
+ return class SubagentRuntime extends _classSuper {
2744
+ static {
2745
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
2746
+ _remoteExportList_decorators = [Remote("list")];
2747
+ _prompt_decorators = [Remote("prompt")];
2748
+ _interruptByParent_decorators = [Remote("interruptByParent")];
2749
+ __esDecorate(this, null, _remoteExportList_decorators, {
2750
+ kind: "method",
2751
+ name: "remoteExportList",
2752
+ static: false,
2753
+ private: false,
2754
+ access: {
2755
+ has: (obj) => "remoteExportList" in obj,
2756
+ get: (obj) => obj.remoteExportList
2757
+ },
2758
+ metadata: _metadata
2759
+ }, null, _instanceExtraInitializers);
2760
+ __esDecorate(this, null, _prompt_decorators, {
2761
+ kind: "method",
2762
+ name: "prompt",
2763
+ static: false,
2764
+ private: false,
2765
+ access: {
2766
+ has: (obj) => "prompt" in obj,
2767
+ get: (obj) => obj.prompt
2768
+ },
2769
+ metadata: _metadata
2770
+ }, null, _instanceExtraInitializers);
2771
+ __esDecorate(this, null, _interruptByParent_decorators, {
2772
+ kind: "method",
2773
+ name: "interruptByParent",
2774
+ static: false,
2775
+ private: false,
2776
+ access: {
2777
+ has: (obj) => "interruptByParent" in obj,
2778
+ get: (obj) => obj.interruptByParent
2779
+ },
2780
+ metadata: _metadata
2781
+ }, null, _instanceExtraInitializers);
2782
+ if (_metadata) Object.defineProperty(this, Symbol.metadata, {
2783
+ enumerable: true,
2784
+ configurable: true,
2785
+ writable: true,
2786
+ value: _metadata
2787
+ });
2788
+ }
2789
+ providers = (__runInitializers(this, _instanceExtraInitializers), /* @__PURE__ */ new Map());
2790
+ continuations;
2791
+ /** Deployment contributions composed into unpublished continuable children. */
2792
+ setupRegistry = new SubagentActivationSetupRegistry();
2793
+ /**
2794
+ * The contained lifecycle-edge publisher. Built here because scoped dispatch
2795
+ * keys its carrier by this exact service instance, whose own context filter
2796
+ * composes into the carrier.
2797
+ */
2798
+ emitLifecycle;
2799
+ constructor(ctx) {
2800
+ super(ctx, "subagents");
2801
+ this.emitLifecycle = createLifecycleEmitter(this.ctx, (parent) => scopeTarget(this, parent));
2802
+ ctx.inject(["agents"], (childCtx) => {
2803
+ const manager = new SubagentContinuationManager(childCtx, {
2804
+ prepareContinuable: (name, request) => this.prepareContinuable(name, request),
2805
+ observeActivation: (provider, childId, parent) => this.observeActivation(provider, childId, parent)
2806
+ }, this.setupRegistry);
2807
+ this.continuations = manager;
2808
+ childCtx.effect(() => () => {
2809
+ /* v8 ignore else -- one injected binding owns the slot until its fiber disposes. */
2810
+ if (this.continuations === manager) this.continuations = void 0;
2811
+ }, "subagents.continuationBinding()");
2812
+ });
2813
+ ctx.inject(["sessionProjections"], (projectionCtx) => {
2814
+ projectionCtx.sessionProjections.register(subagentTimingProjectionDefinition);
2815
+ projectionCtx.sessionProjections.register(subagentIdentityProjectionDefinition);
2816
+ });
2817
+ }
2818
+ /**
2819
+ * Establish one durable continuable child and deliver its initial prompt.
2820
+ * Resolves when the child's inbox accepts that prompt, without waiting for the
2821
+ * turn to start or for the message to reach the Session log; any earlier
2822
+ * failure rejects with no ids and rolls back the child entirely.
2823
+ * @param spec - provider, delegation request, and caller cancellation.
2824
+ * @returns the durable child id and the accepted prompt's message id.
2825
+ * @throws when continuation services are unavailable or materialization fails.
2826
+ */
2827
+ async startContinuable(spec) {
2828
+ return this.requireContinuations().startContinuable(spec);
2829
+ }
2830
+ /**
2831
+ * Deliver one later message to a continuable child as its next FIFO turn. A
2832
+ * resident child's Agent inbox accepts it directly (waking a `waiting`
2833
+ * Activation), while an absent one is cold-resumed from its persisted
2834
+ * Session. The Agent inbox is the only queue, so every accepted message has
2835
+ * one observable order.
2836
+ * @param parent - the exact live direct parent authorizing this delivery.
2837
+ * @param childId - durable child session id.
2838
+ * @param content - user-role content to deliver.
2839
+ * @param options - the message source fields and caller cancellation, which stops the
2840
+ * operation only before inbox acceptance.
2841
+ * @returns the accepted message's inbox id.
2842
+ * @throws when continuation services are unavailable, parent authority is
2843
+ * rejected, or the message was not admitted.
2844
+ */
2845
+ async followup(parent, childId, content, options) {
2846
+ return this.requireContinuations().followup(parent, childId, content, options);
2847
+ }
2848
+ /**
2849
+ * Interrupt one live continuable child's current turn under a human parent
2850
+ * address or an exact live ancestor Agent. Fire-and-return: the cancel
2851
+ * signal is issued before this returns, but the target may keep running
2852
+ * until it observes the signal. Unclaimed pending inbox work, the Activation,
2853
+ * and published descendants are preserved; claimed work is not requeued.
2854
+ * Once the interrupted driver is idle, a waking send resumes the parked FIFO
2855
+ * queue. An absent target — including a one-shot or unknown id —
2856
+ * is an accepted no-op, as is a manager-less composition, which cannot own a
2857
+ * live Activation.
2858
+ * @param targetSessionId - the durable child session id to interrupt.
2859
+ * @param authority - the human parent address or exact live ancestor Agent.
2860
+ * @throws {SubagentError} `UNAUTHORIZED` when the authority does not own the
2861
+ * live target.
2862
+ */
2863
+ interrupt(targetSessionId, authority) {
2864
+ this.continuations?.interrupt(targetSessionId, authority);
2865
+ }
2866
+ /**
2867
+ * Deliver selected content from one live continuable child to its durable
2868
+ * direct parent. The child is the authority credential; callers cannot name a
2869
+ * recipient. Reporting does not conclude the child's turn or Activation.
2870
+ * @param child - exact live reporting child.
2871
+ * @param content - selected model-facing content.
2872
+ * @param options - parent scheduling and pre-acceptance cancellation.
2873
+ * @returns the stable identity of the parent-accepted message.
2874
+ * @throws when continuation services are unavailable, sender authorization
2875
+ * fails, or the direct parent is not live.
2876
+ */
2877
+ async reportFrom(child, content, options) {
2878
+ return this.requireContinuations().reportFrom(child, content, options);
2879
+ }
2880
+ /**
2881
+ * Compose one deployment capability into every continuable child's
2882
+ * unpublished creation context on fresh creation and cold resume. Grants wait
2883
+ * for the next Activation; removing the contribution revokes every resident
2884
+ * installation immediately.
2885
+ * @param contribution - synchronous child-scope installer.
2886
+ * @returns the exact Cordis effect disposer.
2887
+ */
2888
+ registerContinuableSetup(contribution) {
2889
+ return this.ctx.effect(() => this.setupRegistry.register(contribution), "subagents.registerContinuableSetup()");
2890
+ }
2891
+ /**
2892
+ * Close continuable admission below exact live parent Agents, stop only their
2893
+ * visible descendant Activations synchronously, then await admitted scoped
2894
+ * materializations and release those forests child-first. The scoped cutoff
2895
+ * lasts until each exact parent leaves the registry; unrelated parent trees
2896
+ * remain live.
2897
+ * @param parents - exact host-owned parent Agents entering teardown.
2898
+ * @returns once every retained descendant Activation released its `AgentHandle`.
2899
+ * @throws an aggregate error after all branches settle when any failed.
2900
+ */
2901
+ async drainContinuableDescendants(parents) {
2902
+ const manager = this.continuations;
2903
+ if (manager === void 0) return;
2904
+ await manager.drainDescendants(parents);
2905
+ }
2906
+ /**
2907
+ * Release selected resident continuable direct children of one exact live
2908
+ * parent. Other children of the same parent remain admitted and resident.
2909
+ * Absent targets and a manager-less composition are accepted no-ops.
2910
+ * @param parent - exact live direct parent authorizing the selected release.
2911
+ * @param childIds - durable direct-child ids to release when resident.
2912
+ * @returns once every selected Activation released its `AgentHandle`.
2913
+ * @throws {SubagentError} `UNAUTHORIZED` when a resident target belongs to a
2914
+ * different parent or the supplied parent identity is stale.
2915
+ */
2916
+ async drainContinuableChildren(parent, childIds) {
2917
+ const manager = this.continuations;
2918
+ if (manager === void 0) return;
2919
+ await manager.drainChildren(parent, childIds);
2920
+ }
2921
+ /**
2922
+ * Enumerate the parent's direct session-backed subagents without loading or
2923
+ * resuming an Agent. The Session query service supplies one live-preferred
2924
+ * corpus and shared point observations; the projection cache supplies
2925
+ * immutable descriptor hits without opening cold logs. The registered
2926
+ * `subagent` projection remains the sole mode/label classifier.
2927
+ *
2928
+ * Every query receives `signal`, and the listing rechecks cancellation
2929
+ * around each await. Read rejections that settle
2930
+ * after an abort become a stable `SubagentError` with code `CANCELLED`.
2931
+ * @param parentSessionId - parent session whose direct children are listed.
2932
+ * @param signal - caller-owned cancellation forwarded to Session queries
2933
+ * and observed around every read await.
2934
+ * @returns children and per-child diagnostics ordered by `createdAt`, then id.
2935
+ * @throws {@link SubagentError} when the projection registry or the session
2936
+ * store is not mounted, or the caller cancels the listing.
2937
+ */
2938
+ listChildren(parentSessionId, signal) {
2939
+ return listChildren(this.ctx, parentSessionId, signal);
2940
+ }
2941
+ /**
2942
+ * Enumerate the root's complete session-backed subagent tree in stable
2943
+ * pre-order from one live-preferred corpus, without loading or resuming an
2944
+ * Agent. Ordinary sessions and one-shot children remain traversal nodes so
2945
+ * continuable descendants below them are discovered; each returned entry
2946
+ * adds its durable `parentId` and root-relative `depth`. Identity resolution,
2947
+ * diagnostics, optional persistence, and cancellation follow the same
2948
+ * projection-backed contract as {@link listChildren}.
2949
+ * @param rootSessionId - session whose complete descendant tree is listed.
2950
+ * @param signal - caller-owned cancellation forwarded to persistence reads
2951
+ * and observed around every read await.
2952
+ * @returns children and per-candidate diagnostics with tree position, in
2953
+ * stable pre-order.
2954
+ * @throws {@link SubagentError} under the same conditions as {@link listChildren}.
2955
+ */
2956
+ listDescendants(rootSessionId, signal) {
2957
+ return listDescendants(this.ctx, rootSessionId, signal);
2958
+ }
2959
+ /**
2960
+ * Remote face of {@link listChildren} for one browser: the durable listing
2961
+ * plus live Agent activity and the delivery-time parent availability hint.
2962
+ * Parent availability is a hint; {@link prompt} performs the authoritative
2963
+ * check. Named apart from the provider-name {@link list}, which owns the
2964
+ * member.
2965
+ * @param parentSessionId - parent session whose direct children are listed.
2966
+ * @param signal - carrier cancellation forwarded to Session queries.
2967
+ * @returns the catalog view for that parent.
2968
+ * @throws {RemoteError} `gateway/bad-request` for an empty parent id,
2969
+ * `gateway/cancelled` for an aborted read, `subagent/projections-unavailable` when
2970
+ * the deployment has no projection registry, otherwise `gateway/internal`.
2971
+ */
2972
+ async remoteExportList(parentSessionId, signal) {
2973
+ validateControlRequest("subagent.list", { parentSessionId });
2974
+ try {
2975
+ return catalogView(this.ctx, parentSessionId, await this.listChildren(parentSessionId, signal));
2976
+ } catch (error) {
2977
+ return rejectCatalogRead(error, signal);
2978
+ }
2979
+ }
2980
+ /**
2981
+ * Deliver one browser-authored message to a continuable child through the
2982
+ * exact live direct parent, retaining the caller-minted request identity and
2983
+ * validated browser zone on the accepted message. Success identifies the
2984
+ * message the child's FIFO inbox accepted; later execution is independent of
2985
+ * this call.
2986
+ * Image parts are admitted and persisted through the attachment store
2987
+ * before delivery, and the child's model must accept image input.
2988
+ * @param request - durable address, minted identity, content, and optional browser zone.
2989
+ * @param signal - carrier cancellation, owning the call until inbox acceptance.
2990
+ * @returns the accepted message's inbox identity.
2991
+ * @throws {RemoteError} `gateway/bad-request`, `subagent/attachment-invalid`,
2992
+ * `subagent/invalid-time-zone`, `subagent/parent-unavailable`,
2993
+ * `subagent/not-resumable`, `subagent/unauthorized`,
2994
+ * `subagent/delivery-unavailable`, `gateway/cancelled`, or `gateway/internal`.
2995
+ */
2996
+ async prompt(request, signal) {
2997
+ const { parentSessionId, childSessionId, clientTimeZone } = request;
2998
+ validateControlRequest("subagent.prompt", request);
2999
+ const canonicalTimeZone = clientTimeZone === void 0 ? void 0 : canonicalClientTimeZone(clientTimeZone);
3000
+ if (clientTimeZone !== void 0 && canonicalTimeZone === void 0) throw new RemoteError("subagent/invalid-time-zone", "clientTimeZone must be UTC or a valid IANA Area/Location name", { value: clientTimeZone });
3001
+ const parent = this.ctx.get("agents")?.get(parentSessionId);
3002
+ if (parent === void 0) throw new RemoteError("subagent/parent-unavailable", `parent session "${parentSessionId}" is not live`, { parentSessionId });
3003
+ const source = {
3004
+ kind: "user",
3005
+ rpcId: request.requestId,
3006
+ ...canonicalTimeZone === void 0 ? {} : { clientTimeZone: canonicalTimeZone }
2578
3007
  };
2579
- this.ctx.emit("subagent/provider-added", provider);
2580
- }.bind(this), "subagents.registerProvider()");
2581
- }
2582
- /**
2583
- * Look up a provider by name.
2584
- * @param name - the provider name.
2585
- * @returns the provider, or undefined when absent.
2586
- */
2587
- getProvider(name) {
2588
- return this.providers.get(name);
2589
- }
2590
- /**
2591
- * List registered provider names in insertion order.
2592
- * @returns the registered names.
2593
- */
2594
- list() {
2595
- return [...this.providers.keys()];
2596
- }
2597
- /**
2598
- * Establish a published child on the named provider. Capability and semantic
2599
- * checks run before delegation. Provider ownership lasts until its promise
2600
- * fulfills; a rejection therefore has no run for the caller to dispose and
2601
- * emits no run lifecycle events. Post-publication turn and infrastructure
2602
- * failures settle through the returned run.
2603
- * @param name - the provider to use.
2604
- * @param request - child label, prompt, parent, signal, and optional capabilities.
2605
- * @returns the published holder-owned run.
2606
- */
2607
- async start(name, request) {
2608
- const provider = this.expectProvider(name);
2609
- this.assertCapabilities(provider, request);
2610
- assertSubagentMaxDepth(request.maxDepth);
2611
- if (request.outputSchema !== void 0) assertObjectJsonSchema(request.outputSchema);
2612
- const descriptor = snapshotSubagentDescriptor({
2613
- mode: "one-shot",
2614
- provider: name,
2615
- ...request.label !== void 0 ? { label: request.label } : {}
2616
- });
2617
- const resolved = {
2618
- ...request,
2619
- descriptor
2620
- };
2621
- return observeRun(this.emitLifecycle, name, request.parent, await provider.start(resolved));
2622
- }
2623
- /**
2624
- * Resolve one provider's detached continuable-creation contribution. Method
2625
- * presence on the provider IS the capability, so a provider without it is
2626
- * rejected before the manager reserves any child resources.
2627
- */
2628
- async prepareContinuable(name, request) {
2629
- const provider = this.expectProvider(name);
2630
- if (provider.prepareContinuable === void 0) throw new SubagentError(`subagent provider "${provider.name}" does not support continuable children (no prepareContinuable capability)`, "UNSUPPORTED_CAPABILITY");
2631
- return provider.prepareContinuable(request);
2632
- }
2633
- /** Look up a provider for dispatch or fail loud. */
2634
- expectProvider(name) {
2635
- const provider = this.providers.get(name);
2636
- if (provider === void 0) throw new SubagentError(`no subagent provider registered for "${name}"`, "NO_PROVIDER");
2637
- return provider;
2638
- }
2639
- /** Resolve the optional continuable-subagent manager or fail loud. */
2640
- requireContinuations() {
2641
- if (this.continuations === void 0) throw new SubagentError("continuable subagents require the agents service", "CONTINUATION_UNAVAILABLE");
2642
- return this.continuations;
2643
- }
2644
- /**
2645
- * Build the lifecycle observer for one continuable Activation's residency
2646
- * epoch, so the manager publishes its edges without owning event dispatch.
2647
- */
2648
- observeActivation(provider, childId, parent) {
2649
- return createActivationObserver(this.emitLifecycle, provider, childId, parent);
2650
- }
2651
- /** Reject the first requested capability that the provider lacks. */
2652
- assertCapabilities(provider, request) {
2653
- const needs = [
2654
- {
2655
- when: request.outputSchema !== void 0,
2656
- cap: "outputSchema"
2657
- },
2658
- {
2659
- when: request.maxDepth !== void 0,
2660
- cap: "depthLimit"
2661
- },
2662
- {
2663
- when: request.toolFilter !== void 0,
2664
- cap: "toolFilter"
2665
- },
2666
- {
2667
- when: request.persona !== void 0,
2668
- cap: "persona"
3008
+ try {
3009
+ let content;
3010
+ if (request.content.every((part) => part.type === "text")) content = request.content.map((part) => ({
3011
+ type: "text",
3012
+ text: part.text
3013
+ }));
3014
+ else {
3015
+ const attachments = this.ctx.get("attachments");
3016
+ if (attachments === void 0) throw new Error("subagent image prompt requires an attachment store");
3017
+ content = await admitPromptContent(attachments, request.content);
3018
+ }
3019
+ return { messageId: await this.followup(parent, childSessionId, content, {
3020
+ source,
3021
+ signal
3022
+ }) };
3023
+ } catch (error) {
3024
+ return rejectPrompt(error, childSessionId, signal);
2669
3025
  }
2670
- ];
2671
- for (const { when, cap } of needs) if (when && !provider.capabilities[cap]) throw new SubagentError(`subagent provider "${provider.name}" does not support the "${cap}" capability`, "UNSUPPORTED_CAPABILITY");
2672
- }
2673
- };
3026
+ }
3027
+ /**
3028
+ * Remote face of {@link interrupt} under one durable parent address. No
3029
+ * catalog, history, persistence, or parent Agent lookup runs: the core
3030
+ * primitive alone authorizes the address against the live Activation, which
3031
+ * is what keeps a live child interruptible while its parent Agent is offline.
3032
+ * Absent, idle, and already-completed targets are accepted no-ops there.
3033
+ * @param childSessionId - durable child session id to interrupt.
3034
+ * @param parentSessionId - durable direct parent whose authority is claimed.
3035
+ * @param mode - required continuable-address discriminator.
3036
+ * @returns acknowledgement that the cancel signal was admitted, not that the target is quiescent.
3037
+ * @throws {RemoteError} `gateway/bad-request` for an empty id,
3038
+ * `subagent/unauthorized` when the address does not own the live target,
3039
+ * otherwise `gateway/internal`.
3040
+ */
3041
+ interruptByParent(childSessionId, parentSessionId, mode) {
3042
+ validateControlRequest("subagent.interrupt", {
3043
+ childSessionId,
3044
+ parentSessionId,
3045
+ mode
3046
+ });
3047
+ try {
3048
+ this.interrupt(childSessionId, {
3049
+ kind: "user",
3050
+ parentSessionId
3051
+ });
3052
+ } catch (error) {
3053
+ if (error instanceof SubagentError && error.code === "UNAUTHORIZED") throw new RemoteError("subagent/unauthorized", "subagent does not belong to this parent", { childSessionId }, { cause: error });
3054
+ throw new RemoteError("gateway/internal", "subagent interrupt failed", {}, { cause: error });
3055
+ }
3056
+ return { accepted: true };
3057
+ }
3058
+ /**
3059
+ * Register a provider under its name. Registration is effect-scoped and HMR
3060
+ * safe; removing a provider blocks new starts but does not revoke runs that
3061
+ * were already returned to their holders.
3062
+ * @param provider - the trusted provider implementation.
3063
+ * @returns the exact Cordis effect disposer.
3064
+ */
3065
+ registerProvider(provider) {
3066
+ const name = provider.name;
3067
+ return this.ctx.effect(function* () {
3068
+ if (this.providers.has(name)) throw new SubagentError(`a subagent provider named "${name}" is already registered`, "DUPLICATE_PROVIDER");
3069
+ this.providers.set(name, provider);
3070
+ yield () => {
3071
+ this.providers.delete(name);
3072
+ this.emitLifecycle("subagent/provider-removed", name);
3073
+ };
3074
+ this.ctx.emit("subagent/provider-added", provider);
3075
+ }.bind(this), "subagents.registerProvider()");
3076
+ }
3077
+ /**
3078
+ * Look up a provider by name.
3079
+ * @param name - the provider name.
3080
+ * @returns the provider, or undefined when absent.
3081
+ */
3082
+ getProvider(name) {
3083
+ return this.providers.get(name);
3084
+ }
3085
+ /**
3086
+ * List registered provider names in insertion order.
3087
+ * @returns the registered names.
3088
+ */
3089
+ list() {
3090
+ return [...this.providers.keys()];
3091
+ }
3092
+ /**
3093
+ * Establish a published child on the named provider. Capability and semantic
3094
+ * checks run before delegation. Provider ownership lasts until its promise
3095
+ * fulfills; a rejection therefore has no run for the caller to dispose and
3096
+ * emits no run lifecycle events. Post-publication turn and infrastructure
3097
+ * failures settle through the returned run.
3098
+ * @param name - the provider to use.
3099
+ * @param request - child label, prompt, parent, signal, and optional capabilities.
3100
+ * @returns the published holder-owned run.
3101
+ */
3102
+ async start(name, request) {
3103
+ const provider = this.expectProvider(name);
3104
+ this.assertCapabilities(provider, request);
3105
+ assertSubagentMaxDepth(request.maxDepth);
3106
+ if (request.outputSchema !== void 0) assertObjectJsonSchema(request.outputSchema);
3107
+ const descriptor = snapshotSubagentDescriptor({
3108
+ mode: "one-shot",
3109
+ provider: name,
3110
+ ...request.label !== void 0 ? { label: request.label } : {}
3111
+ });
3112
+ const resolved = {
3113
+ ...request,
3114
+ descriptor
3115
+ };
3116
+ return observeRun(this.emitLifecycle, name, request.parent, await provider.start(resolved));
3117
+ }
3118
+ /**
3119
+ * Resolve one provider's detached continuable-creation contribution. Method
3120
+ * presence on the provider IS the capability, so a provider without it is
3121
+ * rejected before the manager reserves any child resources.
3122
+ */
3123
+ async prepareContinuable(name, request) {
3124
+ const provider = this.expectProvider(name);
3125
+ if (provider.prepareContinuable === void 0) throw new SubagentError(`subagent provider "${provider.name}" does not support continuable children (no prepareContinuable capability)`, "UNSUPPORTED_CAPABILITY");
3126
+ return provider.prepareContinuable(request);
3127
+ }
3128
+ /** Look up a provider for dispatch or fail loud. */
3129
+ expectProvider(name) {
3130
+ const provider = this.providers.get(name);
3131
+ if (provider === void 0) throw new SubagentError(`no subagent provider registered for "${name}"`, "NO_PROVIDER");
3132
+ return provider;
3133
+ }
3134
+ /** Resolve the optional continuable-subagent manager or fail loud. */
3135
+ requireContinuations() {
3136
+ if (this.continuations === void 0) throw new SubagentError("continuable subagents require the agents service", "CONTINUATION_UNAVAILABLE");
3137
+ return this.continuations;
3138
+ }
3139
+ /**
3140
+ * Build the lifecycle observer for one continuable Activation's residency
3141
+ * epoch, so the manager publishes its edges without owning event dispatch.
3142
+ */
3143
+ observeActivation(provider, childId, parent) {
3144
+ return createActivationObserver(this.emitLifecycle, provider, childId, parent);
3145
+ }
3146
+ /** Reject the first requested capability that the provider lacks. */
3147
+ assertCapabilities(provider, request) {
3148
+ const needs = [
3149
+ {
3150
+ when: request.agentOptions !== void 0,
3151
+ cap: "agentOptions"
3152
+ },
3153
+ {
3154
+ when: request.outputSchema !== void 0,
3155
+ cap: "outputSchema"
3156
+ },
3157
+ {
3158
+ when: request.maxDepth !== void 0,
3159
+ cap: "depthLimit"
3160
+ },
3161
+ {
3162
+ when: request.toolFilter !== void 0,
3163
+ cap: "toolFilter"
3164
+ },
3165
+ {
3166
+ when: request.persona !== void 0,
3167
+ cap: "persona"
3168
+ }
3169
+ ];
3170
+ for (const { when, cap } of needs) if (when && !provider.capabilities[cap]) throw new SubagentError(`subagent provider "${provider.name}" does not support the "${cap}" capability`, "UNSUPPORTED_CAPABILITY");
3171
+ }
3172
+ };
3173
+ })();
2674
3174
  //#endregion
2675
- export { AssistantOutputFold, NO_START_CAPABILITIES, SUBAGENT_DESCRIPTOR_VERSION, SubagentDepthError, SubagentError, SubagentRunId, SubagentRuntime, SubagentRuntime as default, appendDelegatedPolicyOverrides, applyChildComposition, assertPositiveFinite, assertSubagentMaxDepth, assertUsableCwd, captureDelegatedPolicyOverrides, childSessionMeta, delegationDepthOf, finalAssistantOutput, foldSubagentDescriptor, resolveChildAgentOptions, resolveChildCwd, resolveChildDepth, seedDescriptorTurn, settleRun, settleRunResult, snapshotSubagentDescriptor, subprocessRunHandle, validateConfiguredCwd };
3175
+ export { AssistantOutputFold, NO_START_CAPABILITIES, SUBAGENT_DESCRIPTOR_VERSION, SubagentDepthError, SubagentError, SubagentRunId, SubagentRuntime, SubagentRuntime as default, appendDelegatedPolicyOverrides, applyChildComposition, assertPositiveFinite, assertSubagentMaxDepth, assertUsableCwd, captureDelegatedPolicyOverrides, childSessionMeta, delegationDepthOf, finalAssistantOutput, foldSubagentDescriptor, parentAgentOptionsForDelegation, resolveChildAgentOptions, resolveChildCwd, resolveChildDepth, seedDescriptorTurn, settleRun, settleRunResult, snapshotSubagentDescriptor, subprocessRunHandle, validateConfiguredCwd };