@xneog/dsh-subagent 0.1.0 → 0.1.2-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.i18n.yaml +2 -2
- package/README.md +108 -76
- package/README.zh.md +112 -80
- package/lib/index.js +1252 -696
- package/lib/typert.host.d.ts +3 -0
- package/lib/typert.host.js +911 -0
- package/lib/typert.remote-client.d.ts +27 -0
- package/lib/typert.remote-client.js +159 -0
- package/lib/types/child-agent.d.ts +16 -5
- package/lib/types/child-agent.js +51 -13
- package/lib/types/client.d.ts +2 -1
- package/lib/types/client.js +1 -1
- package/lib/types/continuation.d.ts +100 -72
- package/lib/types/continuation.js +427 -163
- package/lib/types/control-types.d.ts +144 -0
- package/lib/types/control-types.js +9 -0
- package/lib/types/control.d.ts +67 -0
- package/lib/types/control.js +115 -0
- package/lib/types/descriptor-seed.d.ts +1 -1
- package/lib/types/descriptor-seed.js +1 -1
- package/lib/types/descriptor.d.ts +6 -1
- package/lib/types/descriptor.js +6 -2
- package/lib/types/index.d.ts +103 -68
- package/lib/types/index.js +437 -286
- package/lib/types/internal.d.ts +59 -0
- package/lib/types/internal.js +58 -0
- package/lib/types/lifecycle.js +4 -3
- package/lib/types/list-children.d.ts +12 -59
- package/lib/types/list-children.js +166 -101
- package/lib/types/out-of-process.d.ts +5 -2
- package/lib/types/out-of-process.js +42 -4
- package/lib/types/projection-types.d.ts +4 -3
- package/lib/types/projection.d.ts +55 -8
- package/lib/types/projection.js +33 -17
- package/lib/types/run-settlement.js +17 -6
- package/lib/types/types.d.ts +25 -0
- package/package.json +67 -37
- package/lib/types/activation-setup-registry.d.ts +0 -57
- package/lib/types/activation-setup-registry.js +0 -148
package/lib/index.js
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AttachmentError, admitPromptContent } from "@xneog/dsh-attachment";
|
|
2
2
|
import { scopeTarget } from "@xneog/dsh-scope";
|
|
3
3
|
import { assertObjectJsonSchema } from "@xneog/dsh-tools";
|
|
4
|
-
import {
|
|
4
|
+
import { canonicalClientTimeZone } from "@xneog/dsh-util-time";
|
|
5
|
+
import { Remote, RemoteError, TypertRemoteService } from "@xneog/dsh-typert-protocol";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import { HarnessError, ReasoningEffortId, boundContextSummary, contentHasImage, createUserMessage, errorChain } from "@xneog/dsh-llm";
|
|
5
8
|
import { randomUUID } from "node:crypto";
|
|
6
9
|
import { foldConsumedWork } from "@xneog/dsh-agent";
|
|
7
|
-
import { Session,
|
|
8
|
-
import {
|
|
10
|
+
import { Session, SessionLogOffset, SessionSeq } from "@xneog/dsh-session";
|
|
11
|
+
import { brandString } from "@xneog/dsh-brand";
|
|
12
|
+
import { snapshotJsonValue } from "@xneog/dsh-util-values";
|
|
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 @xneog/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 @xneog/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
|
|
@@ -228,16 +327,16 @@ function createActivationObserver(emit, provider, childId, parent) {
|
|
|
228
327
|
id: childId,
|
|
229
328
|
local: true
|
|
230
329
|
};
|
|
231
|
-
let boundary = 0;
|
|
330
|
+
let boundary = SessionLogOffset(0);
|
|
232
331
|
let captured = { stopReason: "completed" };
|
|
233
332
|
const terminal = (failure) => failure === void 0 ? captured : { stopReason: "error" };
|
|
234
333
|
return {
|
|
235
334
|
start: (child) => {
|
|
236
|
-
boundary = child.session.
|
|
335
|
+
boundary = child.session.seq;
|
|
237
336
|
emit("subagent/start", identity, parent);
|
|
238
337
|
},
|
|
239
338
|
capture: (child) => {
|
|
240
|
-
const own = child.session.
|
|
339
|
+
const own = child.session.snapshotEvents(boundary);
|
|
241
340
|
const output = finalAssistantOutput(own);
|
|
242
341
|
captured = {
|
|
243
342
|
stopReason: epochStopReason(own),
|
|
@@ -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 =
|
|
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 !==
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
494
|
-
*
|
|
495
|
-
*
|
|
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
|
|
503
|
-
const
|
|
504
|
-
const
|
|
505
|
-
|
|
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,
|
|
@@ -524,19 +653,19 @@ function resolveChildAgentOptions(parent, requested, childDepth) {
|
|
|
524
653
|
* child never had.
|
|
525
654
|
* @param parent - the delegating parent agent.
|
|
526
655
|
* @param childDepth - the resolved delegation depth to persist.
|
|
527
|
-
* @param
|
|
656
|
+
* @param isSeeded - whether this child inherits a parent-log prefix, including an explicitly empty one.
|
|
528
657
|
* @returns the `meta` for `ctx.agents.create()`.
|
|
529
658
|
*/
|
|
530
|
-
function childSessionMeta(parent, childDepth,
|
|
659
|
+
function childSessionMeta(parent, childDepth, isSeeded) {
|
|
531
660
|
const parentHeader = parent.session.header;
|
|
532
661
|
const agentPreset = parent.ctx.get("agentPresets")?.composedPreset(parent.ctx);
|
|
533
662
|
return {
|
|
534
663
|
...parentHeader.cwd !== void 0 ? { cwd: parentHeader.cwd } : {},
|
|
535
664
|
...agentPreset === void 0 ? {} : { agentPreset },
|
|
536
665
|
parentSession: parentHeader.id,
|
|
666
|
+
isSeeded,
|
|
537
667
|
origin: "subagent",
|
|
538
|
-
delegationDepth: childDepth
|
|
539
|
-
...lineageSeedLength > 0 ? { seedLength: lineageSeedLength } : {}
|
|
668
|
+
delegationDepth: childDepth
|
|
540
669
|
};
|
|
541
670
|
}
|
|
542
671
|
/**
|
|
@@ -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:
|
|
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:
|
|
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);
|
|
@@ -638,9 +767,32 @@ function appendDelegatedPolicyOverrides(childSession, overrides) {
|
|
|
638
767
|
function seedDescriptorTurn(childId, seed, descriptor) {
|
|
639
768
|
const staged = Session.create(childId, seed);
|
|
640
769
|
staged.append("subagent/descriptor", descriptor);
|
|
641
|
-
return
|
|
770
|
+
return staged.snapshotEvents();
|
|
642
771
|
}
|
|
643
772
|
//#endregion
|
|
773
|
+
//#region lib/types/internal.js
|
|
774
|
+
/**
|
|
775
|
+
* Continuation integration markers and host adapters outside the public
|
|
776
|
+
* Service Definition and model-facing Agent messaging contract.
|
|
777
|
+
* @module @xneog/dsh-subagent/internal
|
|
778
|
+
*/
|
|
779
|
+
/** Process-stable identity carried only by the standard adjacent-Agent messaging tool. */
|
|
780
|
+
const adjacentAgentSendMessageTool = Symbol.for("dsh.subagent.adjacentAgentSendMessageTool");
|
|
781
|
+
/**
|
|
782
|
+
* Test whether one visible definition is the standard adjacent-Agent messaging tool.
|
|
783
|
+
* @param definition - the scope-resolved `send_message` candidate.
|
|
784
|
+
* @returns whether the definition carries the internal standard-tool identity.
|
|
785
|
+
*/
|
|
786
|
+
function isAdjacentAgentSendMessageTool(definition) {
|
|
787
|
+
return definition !== void 0 && definition[adjacentAgentSendMessageTool] === true;
|
|
788
|
+
}
|
|
789
|
+
/**
|
|
790
|
+
* Process-stable symbol-keyed host delivery shared by the bundled runtime
|
|
791
|
+
* entry and this unbundled internal subpath.
|
|
792
|
+
* @internal
|
|
793
|
+
*/
|
|
794
|
+
const deliverSubagentPrompt = Symbol.for("dsh.subagent.deliverPrompt");
|
|
795
|
+
//#endregion
|
|
644
796
|
//#region lib/types/continuation.js
|
|
645
797
|
/**
|
|
646
798
|
* Internal continuable-subagent manager: stable child ids, descriptor
|
|
@@ -664,6 +816,64 @@ function seedDescriptorTurn(childId, seed, descriptor) {
|
|
|
664
816
|
*
|
|
665
817
|
* @module @xneog/dsh-subagent
|
|
666
818
|
*/
|
|
819
|
+
var __addDisposableResource$1 = function(env, value, async) {
|
|
820
|
+
if (value !== null && value !== void 0) {
|
|
821
|
+
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
|
822
|
+
var dispose, inner;
|
|
823
|
+
if (async) {
|
|
824
|
+
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
|
|
825
|
+
dispose = value[Symbol.asyncDispose];
|
|
826
|
+
}
|
|
827
|
+
if (dispose === void 0) {
|
|
828
|
+
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
|
|
829
|
+
dispose = value[Symbol.dispose];
|
|
830
|
+
if (async) inner = dispose;
|
|
831
|
+
}
|
|
832
|
+
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
|
|
833
|
+
if (inner) dispose = function() {
|
|
834
|
+
try {
|
|
835
|
+
inner.call(this);
|
|
836
|
+
} catch (e) {
|
|
837
|
+
return Promise.reject(e);
|
|
838
|
+
}
|
|
839
|
+
};
|
|
840
|
+
env.stack.push({
|
|
841
|
+
value,
|
|
842
|
+
dispose,
|
|
843
|
+
async
|
|
844
|
+
});
|
|
845
|
+
} else if (async) env.stack.push({ async: true });
|
|
846
|
+
return value;
|
|
847
|
+
};
|
|
848
|
+
var __disposeResources$1 = (function(SuppressedError) {
|
|
849
|
+
return function(env) {
|
|
850
|
+
function fail(e) {
|
|
851
|
+
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
|
|
852
|
+
env.hasError = true;
|
|
853
|
+
}
|
|
854
|
+
var r, s = 0;
|
|
855
|
+
function next() {
|
|
856
|
+
while (r = env.stack.pop()) try {
|
|
857
|
+
if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
|
|
858
|
+
if (r.dispose) {
|
|
859
|
+
var result = r.dispose.call(r.value);
|
|
860
|
+
if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) {
|
|
861
|
+
fail(e);
|
|
862
|
+
return next();
|
|
863
|
+
});
|
|
864
|
+
} else s |= 1;
|
|
865
|
+
} catch (e) {
|
|
866
|
+
fail(e);
|
|
867
|
+
}
|
|
868
|
+
if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
|
|
869
|
+
if (env.hasError) throw env.error;
|
|
870
|
+
}
|
|
871
|
+
return next();
|
|
872
|
+
};
|
|
873
|
+
})(typeof SuppressedError === "function" ? SuppressedError : function(error, suppressed, message) {
|
|
874
|
+
var e = new Error(message);
|
|
875
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
876
|
+
});
|
|
667
877
|
/**
|
|
668
878
|
* Read one Activation's current disposal transaction. This indirection exists
|
|
669
879
|
* because TypeScript would otherwise narrow repeated reads of the mutable field
|
|
@@ -674,6 +884,32 @@ function seedDescriptorTurn(childId, seed, descriptor) {
|
|
|
674
884
|
function disposalOf(activation) {
|
|
675
885
|
return activation.disposal;
|
|
676
886
|
}
|
|
887
|
+
/** Build durable attribution for one adjacent-Agent message. */
|
|
888
|
+
function agentMessageSource(sender) {
|
|
889
|
+
return {
|
|
890
|
+
kind: "agent-message",
|
|
891
|
+
form: "relay",
|
|
892
|
+
senderSessionId: sender.id
|
|
893
|
+
};
|
|
894
|
+
}
|
|
895
|
+
/** Build the model-visible and durable representation of one adjacent-Agent message. */
|
|
896
|
+
function agentMessage(sender, content) {
|
|
897
|
+
return createUserMessage({
|
|
898
|
+
content: [{
|
|
899
|
+
type: "text",
|
|
900
|
+
text: `Agent ${sender.id} sent a message:`
|
|
901
|
+
}, ...content],
|
|
902
|
+
source: agentMessageSource(sender)
|
|
903
|
+
});
|
|
904
|
+
}
|
|
905
|
+
/** Append adjacent-Agent return guidance to a continuable child's initial task. */
|
|
906
|
+
function continuableInitialPrompt(parentId, prompt) {
|
|
907
|
+
const encodedParentId = JSON.stringify(parentId);
|
|
908
|
+
return [...prompt, {
|
|
909
|
+
type: "text",
|
|
910
|
+
text: `Your parent agent id is ${encodedParentId}. Before you finish, send your result to that agent with send_message({ agent_id: ${encodedParentId}, message: "<self-contained result>" }). The parent shares your workspace but does not automatically receive your transcript, tool output, or reasoning. Send earlier messages as well when a finding changes what the parent should do next; sending a message does not end your turn.`
|
|
911
|
+
}];
|
|
912
|
+
}
|
|
677
913
|
/**
|
|
678
914
|
* One line telling a parent that a background child is finished and why, in
|
|
679
915
|
* the parent's own task vocabulary.
|
|
@@ -723,7 +959,6 @@ var ChildLock = class {
|
|
|
723
959
|
var SubagentContinuationManager = class {
|
|
724
960
|
ctx;
|
|
725
961
|
host;
|
|
726
|
-
setupRegistry;
|
|
727
962
|
/** Child session id → its live Activation. Process-local, never durable. */
|
|
728
963
|
activations = /* @__PURE__ */ new Map();
|
|
729
964
|
/** Materializations admitted before drain, tracked through publication or rollback. */
|
|
@@ -739,10 +974,9 @@ var SubagentContinuationManager = class {
|
|
|
739
974
|
*/
|
|
740
975
|
closingScopes = /* @__PURE__ */ new Map();
|
|
741
976
|
draining = false;
|
|
742
|
-
constructor(ctx, host
|
|
977
|
+
constructor(ctx, host) {
|
|
743
978
|
this.ctx = ctx;
|
|
744
979
|
this.host = host;
|
|
745
|
-
this.setupRegistry = setupRegistry;
|
|
746
980
|
const scope = ctx.plugin(function activationOwner() {});
|
|
747
981
|
this.ownerCtx = scope.ctx;
|
|
748
982
|
ctx.on("agent/disposed", ({ agent }) => {
|
|
@@ -772,83 +1006,202 @@ var SubagentContinuationManager = class {
|
|
|
772
1006
|
const request = spec.request;
|
|
773
1007
|
const parent = request.parent;
|
|
774
1008
|
this.assertAdmitting(parent);
|
|
775
|
-
this.requirePersistence();
|
|
1009
|
+
const persistence = this.requirePersistence();
|
|
776
1010
|
assertSubagentMaxDepth(request.maxDepth);
|
|
777
|
-
const childId =
|
|
1011
|
+
const childId = spec.childId ?? brandString(randomUUID());
|
|
1012
|
+
this.assertChildIdAvailable(childId);
|
|
778
1013
|
const childDepth = resolveChildDepth(parent, request.maxDepth);
|
|
779
|
-
const
|
|
780
|
-
const
|
|
1014
|
+
const agentOptions = resolveChildAgentOptions(parent, request.agentOptions, childDepth);
|
|
1015
|
+
const agentProvider = agentOptions.provider;
|
|
1016
|
+
const agentModel = agentOptions.model;
|
|
1017
|
+
const agentReasoningEffort = agentOptions.reasoningEffort;
|
|
781
1018
|
const descriptor = snapshotSubagentDescriptor({
|
|
782
1019
|
mode: "continuable",
|
|
783
1020
|
provider: spec.provider,
|
|
784
1021
|
label: spec.label,
|
|
785
1022
|
...agentProvider !== void 0 ? { agentProvider } : {},
|
|
786
1023
|
...agentModel !== void 0 ? { agentModel } : {},
|
|
1024
|
+
...agentReasoningEffort !== void 0 ? { agentReasoningEffort } : {},
|
|
787
1025
|
...request.persona !== void 0 ? { persona: request.persona } : {},
|
|
788
1026
|
...request.toolFilter !== void 0 ? { toolFilter: request.toolFilter } : {}
|
|
789
1027
|
});
|
|
790
1028
|
const delegatedPolicies = captureDelegatedPolicyOverrides(parent);
|
|
791
|
-
const
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
childId,
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
parent
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
1029
|
+
const releaseHold = this.holdOwnership(parent, childId);
|
|
1030
|
+
try {
|
|
1031
|
+
const prepared = await this.host.prepareContinuable(spec.provider, {
|
|
1032
|
+
sessionId: childId,
|
|
1033
|
+
parent,
|
|
1034
|
+
signal: spec.signal
|
|
1035
|
+
});
|
|
1036
|
+
spec.signal.throwIfAborted();
|
|
1037
|
+
this.assertAdmitting(parent);
|
|
1038
|
+
const inheritedEventCount = SessionLogOffset(prepared.seed?.length ?? 0);
|
|
1039
|
+
const seed = seedDescriptorTurn(childId, prepared.seed, descriptor);
|
|
1040
|
+
return {
|
|
1041
|
+
childId,
|
|
1042
|
+
messageId: await this.locks.run(childId, async () => {
|
|
1043
|
+
spec.signal.throwIfAborted();
|
|
1044
|
+
this.assertAdmitting(parent);
|
|
1045
|
+
this.assertChildIdAvailable(childId);
|
|
1046
|
+
if (spec.childId !== void 0) {
|
|
1047
|
+
const persisted = await persistence.stat(childId, { signal: spec.signal });
|
|
1048
|
+
spec.signal.throwIfAborted();
|
|
1049
|
+
this.assertAdmitting(parent);
|
|
1050
|
+
this.assertChildIdAvailable(childId);
|
|
1051
|
+
if (persisted !== void 0) throw new SubagentError(`subagent "${childId}" already exists`, "DUPLICATE_CHILD");
|
|
1052
|
+
}
|
|
1053
|
+
const activation = await this.materialize({
|
|
1054
|
+
childId,
|
|
1055
|
+
provider: spec.provider,
|
|
1056
|
+
parent,
|
|
1057
|
+
create: {
|
|
1058
|
+
seed,
|
|
1059
|
+
meta: childSessionMeta(parent, childDepth, prepared.seed !== void 0),
|
|
1060
|
+
inheritedEventCount,
|
|
1061
|
+
delegatedPolicies
|
|
1062
|
+
},
|
|
1063
|
+
agentOptions,
|
|
1064
|
+
composition: {
|
|
1065
|
+
persona: request.persona,
|
|
1066
|
+
toolFilter: request.toolFilter
|
|
1067
|
+
},
|
|
1068
|
+
signal: spec.signal
|
|
1069
|
+
});
|
|
1070
|
+
return this.submitMaterialized(activation, isAdjacentAgentSendMessageTool(this.ctx.get("tools")?.get("send_message", activation.handle.agent)) ? continuableInitialPrompt(parent.id, request.prompt) : request.prompt, {
|
|
1071
|
+
source: { kind: "user" },
|
|
1072
|
+
signal: spec.signal,
|
|
1073
|
+
delivery: "queue"
|
|
1074
|
+
}, parent);
|
|
1075
|
+
})
|
|
1076
|
+
};
|
|
1077
|
+
} catch (error) {
|
|
1078
|
+
releaseHold();
|
|
1079
|
+
throw error;
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
/**
|
|
1083
|
+
* Pre-register `childId` in a continuation-managed parent's owned set so the
|
|
1084
|
+
* parent cannot settle while a caller is still establishing or resuming that
|
|
1085
|
+
* child. Returns a releaser for the failure path; it removes only a hold
|
|
1086
|
+
* this call added, and leaves ownership in place once a live Activation for
|
|
1087
|
+
* the child exists (an admitted delivery owns it from then on). A parent
|
|
1088
|
+
* without an Activation needs no hold: only this manager settles parents.
|
|
1089
|
+
* @param parent - the live direct parent the operation is admitted under.
|
|
1090
|
+
* @param childId - the durable child the operation addresses.
|
|
1091
|
+
* @returns the failure-path releaser; a no-op when nothing was added.
|
|
1092
|
+
* @throws {SubagentError} `ACTIVATION_CLOSING` when the parent's own
|
|
1093
|
+
* disposal transaction is already open.
|
|
1094
|
+
*/
|
|
1095
|
+
holdOwnership(parent, childId) {
|
|
1096
|
+
const parentActivation = this.activations.get(parent.id);
|
|
1097
|
+
if (parentActivation === void 0 || parentActivation.handle.agent !== parent) return () => {};
|
|
1098
|
+
if (parentActivation.disposal !== void 0) throw new SubagentError(`subagent parent "${parent.id}" is being disposed; the child was not established`, "ACTIVATION_CLOSING");
|
|
1099
|
+
if (parentActivation.ownedChildren.has(childId)) return () => {};
|
|
1100
|
+
parentActivation.ownedChildren.add(childId);
|
|
1101
|
+
return () => {
|
|
1102
|
+
const live = this.activations.get(childId);
|
|
1103
|
+
/* v8 ignore next 4 -- reaching this arm needs another delivery to establish the child
|
|
1104
|
+
* between this operation's failure and its releaser running, which no test can schedule
|
|
1105
|
+
* deterministically: the ownership edge then belongs to that live Activation, so the
|
|
1106
|
+
* conservative keep leaves it for finishDisposal's releaseOwnership. */
|
|
1107
|
+
if (live !== void 0 && live.disposal === void 0) return;
|
|
1108
|
+
if (parentActivation.ownedChildren.delete(childId)) this.wake(parentActivation);
|
|
821
1109
|
};
|
|
822
1110
|
}
|
|
1111
|
+
/** Reject one child identity already owned by a live Agent or Session. */
|
|
1112
|
+
assertChildIdAvailable(childId) {
|
|
1113
|
+
if (this.ctx.agents.get(childId) !== void 0 || this.ctx.get("sessions")?.get(childId) !== void 0) throw new SubagentError(`subagent "${childId}" already exists`, "DUPLICATE_CHILD");
|
|
1114
|
+
}
|
|
823
1115
|
/**
|
|
824
|
-
* Deliver one
|
|
825
|
-
*
|
|
826
|
-
*
|
|
827
|
-
*
|
|
828
|
-
*
|
|
829
|
-
*
|
|
830
|
-
*
|
|
831
|
-
*
|
|
832
|
-
*
|
|
833
|
-
* @param parent - the exact live direct parent authorizing this delivery.
|
|
834
|
-
* @param childId - the durable child session id.
|
|
835
|
-
* @param content - the user-role content to deliver.
|
|
836
|
-
* @param options - the message source fields and caller cancellation.
|
|
1116
|
+
* Deliver one model-authored message to a direct continuable child or to the
|
|
1117
|
+
* sender's direct parent. Both directions use Steer: a running target admits
|
|
1118
|
+
* the message at its nearest step boundary, while an idle target starts a
|
|
1119
|
+
* turn. A missing direct child cold-resumes through the ordinary continuation
|
|
1120
|
+
* lifecycle. The caller signal owns the operation only until inbox acceptance.
|
|
1121
|
+
* @param sender - exact live Agent authorizing and originating the message.
|
|
1122
|
+
* @param targetId - durable direct-parent or direct-child session id.
|
|
1123
|
+
* @param content - model-authored content to deliver.
|
|
1124
|
+
* @param options - caller cancellation before acceptance.
|
|
837
1125
|
* @returns the accepted message's inbox id.
|
|
838
|
-
* @throws when
|
|
1126
|
+
* @throws when adjacency, availability, or admission rejects delivery.
|
|
839
1127
|
*/
|
|
840
|
-
async
|
|
1128
|
+
async sendMessage(sender, targetId, content, options) {
|
|
1129
|
+
if (this.ctx.agents.get(sender.id) !== sender) throw new SubagentError("message delivery requires the exact live sender agent", "UNAUTHORIZED");
|
|
1130
|
+
this.assertAdmitting(sender);
|
|
1131
|
+
const senderActivation = this.activations.get(sender.id);
|
|
1132
|
+
if (senderActivation !== void 0 && senderActivation.handle.agent === sender && senderActivation.parentSession === targetId) {
|
|
1133
|
+
options.signal.throwIfAborted();
|
|
1134
|
+
return this.sendToParent(senderActivation, sender, content);
|
|
1135
|
+
}
|
|
1136
|
+
if (sender.session.header.parentSession === targetId) throw new SubagentError(`agent "${sender.id}" is not a resident continuable child and cannot send to parent "${targetId}"`, "UNAUTHORIZED");
|
|
1137
|
+
return this.deliverToChild(sender, targetId, content, {
|
|
1138
|
+
signal: options.signal,
|
|
1139
|
+
delivery: "steer"
|
|
1140
|
+
});
|
|
1141
|
+
}
|
|
1142
|
+
/**
|
|
1143
|
+
* Queue one human-authored prompt as a distinct direct-child turn.
|
|
1144
|
+
* @param parent - exact live direct parent authorizing delivery.
|
|
1145
|
+
* @param childId - durable direct-child session id.
|
|
1146
|
+
* @param content - human-authored content to deliver.
|
|
1147
|
+
* @param source - durable host-protocol provenance.
|
|
1148
|
+
* @param signal - caller cancellation before inbox acceptance.
|
|
1149
|
+
* @returns the accepted message's inbox id.
|
|
1150
|
+
*/
|
|
1151
|
+
async queuePrompt(parent, childId, content, source, signal) {
|
|
1152
|
+
return this.deliverToChild(parent, childId, content, {
|
|
1153
|
+
source,
|
|
1154
|
+
signal,
|
|
1155
|
+
delivery: "queue"
|
|
1156
|
+
});
|
|
1157
|
+
}
|
|
1158
|
+
/**
|
|
1159
|
+
* Steer one host-authored prompt to a direct continuable child.
|
|
1160
|
+
* @param parent - exact live direct parent authorizing delivery.
|
|
1161
|
+
* @param childId - durable direct-child session id.
|
|
1162
|
+
* @param content - host-authored content to deliver.
|
|
1163
|
+
* @param source - durable host-protocol provenance.
|
|
1164
|
+
* @param signal - caller cancellation before inbox acceptance.
|
|
1165
|
+
* @returns the accepted message's inbox id.
|
|
1166
|
+
*/
|
|
1167
|
+
async steerPrompt(parent, childId, content, source, signal) {
|
|
1168
|
+
return this.deliverToChild(parent, childId, content, {
|
|
1169
|
+
source,
|
|
1170
|
+
signal,
|
|
1171
|
+
delivery: "steer"
|
|
1172
|
+
});
|
|
1173
|
+
}
|
|
1174
|
+
/** Route one parent-originated delivery through residency and cold resume. */
|
|
1175
|
+
async deliverToChild(parent, childId, content, options) {
|
|
841
1176
|
this.assertAdmitting(parent);
|
|
1177
|
+
const releaseHold = this.holdOwnership(parent, childId);
|
|
1178
|
+
try {
|
|
1179
|
+
return await this.deliverFollowup(parent, childId, content, options);
|
|
1180
|
+
} catch (error) {
|
|
1181
|
+
releaseHold();
|
|
1182
|
+
throw error;
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
/** The delivery loop behind {@link deliverToChild}, run under the parent hold. */
|
|
1186
|
+
async deliverFollowup(parent, childId, content, options) {
|
|
842
1187
|
while (true) {
|
|
843
1188
|
const live = await this.locks.run(childId, async () => {
|
|
844
1189
|
const activation = this.activations.get(childId);
|
|
845
1190
|
if (activation === void 0) return this.coldResume(parent, childId, content, options);
|
|
1191
|
+
const disposal = activation.disposal;
|
|
846
1192
|
/* v8 ignore next 3 -- the send-versus-dispose cutoff: reaching this arm needs a
|
|
847
1193
|
* delivery to observe the transaction inside the same critical section that opened it,
|
|
848
1194
|
* which no test can schedule deterministically. The behavior is covered end-to-end by
|
|
849
1195
|
* "cold-resumes a delivery that lost the race with final disposal". */
|
|
850
|
-
if (
|
|
851
|
-
|
|
1196
|
+
if (disposal !== void 0) return disposal.then(() => void 0, () => void 0);
|
|
1197
|
+
if (contentHasImage(content)) {
|
|
1198
|
+
await this.assertImageCapable(activation.handle.agent, options.signal);
|
|
1199
|
+
if (activation.disposal !== void 0) {
|
|
1200
|
+
await Promise.allSettled([activation.disposal]);
|
|
1201
|
+
return;
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
return this.submitAdmitted(activation, content, options, parent);
|
|
852
1205
|
});
|
|
853
1206
|
/* v8 ignore start -- only the lost-cutoff arm above returns undefined, so only that
|
|
854
1207
|
* race reaches the retry below, which then cold-resumes a new Activation. */
|
|
@@ -892,66 +1245,24 @@ var SubagentContinuationManager = class {
|
|
|
892
1245
|
if (activation.disposal !== void 0) return;
|
|
893
1246
|
activation.handle.agent.cancel(authority.kind === "user" ? { kind: "user" } : { kind: "parent" }, { keepInbox: true });
|
|
894
1247
|
}
|
|
895
|
-
/**
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
* live, or continuation admission is closing.
|
|
906
|
-
*/
|
|
907
|
-
async reportFrom(child, content, options) {
|
|
908
|
-
options.signal.throwIfAborted();
|
|
909
|
-
this.assertAdmitting(child);
|
|
910
|
-
const activation = this.authorizeReporter(child);
|
|
911
|
-
const parent = this.resolveReportParent(child);
|
|
912
|
-
return this.deliverReport(activation, parent, content, options.delivery);
|
|
913
|
-
}
|
|
914
|
-
/** Authorize only the exact Agent of one resident Activation. */
|
|
915
|
-
authorizeReporter(child) {
|
|
916
|
-
const activation = this.activations.get(child.id);
|
|
917
|
-
if (activation === void 0 || activation.handle.agent !== child) throw new SubagentError(`agent "${child.id}" is not a live continuable subagent and cannot report`, "UNAUTHORIZED");
|
|
918
|
-
/* v8 ignore next 6 -- only a synchronous re-entrant disposer can open this
|
|
919
|
-
* transaction between exact-agent authorization and this no-await cutoff. */
|
|
920
|
-
if (activation.disposal !== void 0) throw new SubagentError(`subagent "${child.id}" activation is being disposed; the report was not delivered`, "ACTIVATION_CLOSING");
|
|
921
|
-
return activation;
|
|
922
|
-
}
|
|
923
|
-
/** Resolve the reporting child's live direct parent from durable lineage. */
|
|
924
|
-
resolveReportParent(child) {
|
|
925
|
-
const parentId = child.session.header.parentSession;
|
|
926
|
-
/* v8 ignore next -- every continuation-managed child has direct-parent metadata. */
|
|
927
|
-
const parent = parentId === void 0 ? void 0 : this.ctx.agents.get(parentId);
|
|
928
|
-
if (parent === void 0) throw new SubagentError("direct parent is not live; report was not delivered", "PARENT_UNAVAILABLE");
|
|
929
|
-
return parent;
|
|
930
|
-
}
|
|
931
|
-
/** Deliver one framed report through the selected parent scheduling preset. */
|
|
932
|
-
deliverReport(activation, parent, content, delivery) {
|
|
933
|
-
const message = createUserMessage({
|
|
934
|
-
content: [{
|
|
935
|
-
type: "text",
|
|
936
|
-
text: `Background subagent ${activation.childId} reported:`
|
|
937
|
-
}, ...content],
|
|
938
|
-
source: {
|
|
939
|
-
kind: "subagent-report",
|
|
940
|
-
form: "relay",
|
|
941
|
-
senderSessionId: activation.childId
|
|
942
|
-
}
|
|
943
|
-
});
|
|
944
|
-
if (delivery === "wakeup") this.sendWaking(parent, message, () => {
|
|
945
|
-
this.sendReport(parent, message, delivery);
|
|
1248
|
+
/** Deliver one resident continuable child's message to its live direct parent. */
|
|
1249
|
+
sendToParent(activation, sender, content) {
|
|
1250
|
+
/* v8 ignore next 6 -- only synchronous re-entrant teardown can open this
|
|
1251
|
+
* transaction between exact-agent authorization and this no-await span. */
|
|
1252
|
+
if (activation.disposal !== void 0) throw new SubagentError(`subagent "${sender.id}" activation is being disposed; the message was not delivered`, "ACTIVATION_CLOSING");
|
|
1253
|
+
const parent = this.ctx.agents.get(activation.parentSession);
|
|
1254
|
+
if (parent === void 0) throw new SubagentError("direct parent is not live; the message was not delivered", "PARENT_UNAVAILABLE");
|
|
1255
|
+
const message = agentMessage(sender, content);
|
|
1256
|
+
this.sendWaking(parent, message, () => {
|
|
1257
|
+
this.sendAgentMessage(parent, message);
|
|
946
1258
|
});
|
|
947
|
-
else this.sendReport(parent, message, delivery);
|
|
948
1259
|
return message.id;
|
|
949
1260
|
}
|
|
950
1261
|
/**
|
|
951
1262
|
* Perform one waking send to a parent, accounted against that parent's own
|
|
952
1263
|
* Activation when it has one. Registering the id before the send is what
|
|
953
1264
|
* keeps a continuation-managed parent from being judged quiescent in the
|
|
954
|
-
* window between
|
|
1265
|
+
* window between a waking send and the microtask that admits it.
|
|
955
1266
|
* @param parent - the exact live parent receiving the waking message.
|
|
956
1267
|
* @param message - the message whose id is accounted.
|
|
957
1268
|
* @param send - the synchronous waking send to perform.
|
|
@@ -961,13 +1272,12 @@ var SubagentContinuationManager = class {
|
|
|
961
1272
|
if (parentActivation !== void 0 && parentActivation.handle.agent === parent) this.admitWaking(parentActivation, message.id, send);
|
|
962
1273
|
else send();
|
|
963
1274
|
}
|
|
964
|
-
/** Send one
|
|
965
|
-
|
|
1275
|
+
/** Send one Agent message while translating only the target's own rejection. */
|
|
1276
|
+
sendAgentMessage(parent, message) {
|
|
966
1277
|
try {
|
|
967
|
-
|
|
968
|
-
else parent.inject(message);
|
|
1278
|
+
parent.steer(message);
|
|
969
1279
|
} catch (error) {
|
|
970
|
-
throw new SubagentError("direct parent is not live;
|
|
1280
|
+
throw new SubagentError("direct parent is not live; the message was not delivered", "PARENT_UNAVAILABLE", { cause: error });
|
|
971
1281
|
}
|
|
972
1282
|
}
|
|
973
1283
|
/**
|
|
@@ -1027,6 +1337,28 @@ var SubagentContinuationManager = class {
|
|
|
1027
1337
|
await Promise.all(materializations.map((materialization) => materialization.settled));
|
|
1028
1338
|
await this.disposeRoots(targetRoots, "scoped activation(s)");
|
|
1029
1339
|
}
|
|
1340
|
+
/**
|
|
1341
|
+
* Release selected resident direct children of one exact live parent without
|
|
1342
|
+
* closing admission for the parent's other continuable children. Owned
|
|
1343
|
+
* descendants are released recursively through the same lifecycle.
|
|
1344
|
+
* @param parent - exact live direct parent authorizing the selected release.
|
|
1345
|
+
* @param childIds - durable direct-child ids to release when resident.
|
|
1346
|
+
* @returns once every selected Activation released its handle.
|
|
1347
|
+
* @throws {SubagentError} `UNAUTHORIZED` when a resident target is not the
|
|
1348
|
+
* parent's direct continuable child or the parent identity is stale.
|
|
1349
|
+
*/
|
|
1350
|
+
async drainChildren(parent, childIds) {
|
|
1351
|
+
if (this.ctx.agents.get(parent.id) !== parent) throw new SubagentError("selected child teardown requires the exact live parent agent", "UNAUTHORIZED");
|
|
1352
|
+
const targets = [];
|
|
1353
|
+
for (const childId of new Set(childIds)) {
|
|
1354
|
+
const activation = this.activations.get(childId);
|
|
1355
|
+
if (activation === void 0) continue;
|
|
1356
|
+
if (activation.parentSession !== parent.id || !activation.ancestry.has(parent)) throw new SubagentError(`subagent "${childId}" is not a direct child of agent "${parent.id}"`, "UNAUTHORIZED");
|
|
1357
|
+
targets.push(activation);
|
|
1358
|
+
}
|
|
1359
|
+
for (const activation of targets) this.dispose(activation).catch(() => void 0);
|
|
1360
|
+
await this.disposeRoots(targets, "selected activation(s)");
|
|
1361
|
+
}
|
|
1030
1362
|
/** Dispose independent roots and report every branch failure after all settle. */
|
|
1031
1363
|
async disposeRoots(roots, failureSubject) {
|
|
1032
1364
|
const reasons = (await Promise.all(roots.map(async (activation) => {
|
|
@@ -1098,61 +1430,77 @@ var SubagentContinuationManager = class {
|
|
|
1098
1430
|
return "settled";
|
|
1099
1431
|
}
|
|
1100
1432
|
/**
|
|
1101
|
-
* Cold-resume a persisted child:
|
|
1433
|
+
* Cold-resume a persisted child: retain and authorize its prepared Session, fold the
|
|
1102
1434
|
* generic descriptor, create the Activation through `ctx.agents.resume()`,
|
|
1103
1435
|
* and submit the waiting turn. This never dispatches through a subagent
|
|
1104
1436
|
* provider — the persisted Session already holds the initial prefix and the
|
|
1105
1437
|
* descriptor is the whole reconstruction input.
|
|
1106
1438
|
*/
|
|
1107
1439
|
async coldResume(parent, childId, content, options) {
|
|
1108
|
-
const
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
}
|
|
1113
|
-
options.signal.throwIfAborted();
|
|
1114
|
-
throw new SubagentError(`subagent "${childId}" is unavailable`, "NOT_RESUMABLE", { cause: error });
|
|
1115
|
-
}
|
|
1116
|
-
options.signal.throwIfAborted();
|
|
1117
|
-
this.assertAdmitting(parent);
|
|
1118
|
-
this.authorizeLineage(parent, childId, loaded.meta.parentSession);
|
|
1119
|
-
const descriptor = foldSubagentDescriptor(loaded.events.slice(loaded.meta.seedLength ?? 0));
|
|
1120
|
-
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");
|
|
1121
|
-
let activation;
|
|
1440
|
+
const env_1 = {
|
|
1441
|
+
stack: [],
|
|
1442
|
+
error: void 0,
|
|
1443
|
+
hasError: false
|
|
1444
|
+
};
|
|
1122
1445
|
try {
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1446
|
+
const query = this.requireSessionQuery();
|
|
1447
|
+
let observation;
|
|
1448
|
+
try {
|
|
1449
|
+
observation = await query.observeSession(childId, { signal: options.signal });
|
|
1450
|
+
} catch (error) {
|
|
1451
|
+
options.signal.throwIfAborted();
|
|
1452
|
+
throw new SubagentError(`subagent "${childId}" is unavailable`, "NOT_RESUMABLE", { cause: error });
|
|
1453
|
+
}
|
|
1454
|
+
const source = __addDisposableResource$1(env_1, observation, false);
|
|
1455
|
+
this.assertAdmitting(parent);
|
|
1456
|
+
this.authorizeLineage(parent, childId, source.header.parentSession);
|
|
1457
|
+
const descriptor = foldSubagentDescriptor(source.events.slice(source.inheritedEventCount));
|
|
1458
|
+
if (descriptor === void 0 || descriptor.mode !== "continuable") throw new SubagentError(`subagent "${childId}" has no supported continuation state and cannot be resumed; choose a different target`, "NOT_RESUMABLE");
|
|
1459
|
+
let activation;
|
|
1460
|
+
try {
|
|
1461
|
+
activation = await this.materialize({
|
|
1462
|
+
childId,
|
|
1463
|
+
provider: descriptor.provider,
|
|
1464
|
+
parent,
|
|
1465
|
+
agentOptions: {
|
|
1466
|
+
...descriptor.agentProvider !== void 0 ? { provider: descriptor.agentProvider } : {},
|
|
1467
|
+
...descriptor.agentModel !== void 0 ? { model: descriptor.agentModel } : {},
|
|
1468
|
+
...descriptor.agentReasoningEffort !== void 0 ? { reasoningEffort: ReasoningEffortId(descriptor.agentReasoningEffort) } : {}
|
|
1469
|
+
},
|
|
1470
|
+
composition: {
|
|
1471
|
+
persona: descriptor.persona,
|
|
1472
|
+
toolFilter: descriptor.toolFilter
|
|
1473
|
+
},
|
|
1474
|
+
signal: options.signal
|
|
1475
|
+
});
|
|
1476
|
+
} catch (error) {
|
|
1477
|
+
options.signal.throwIfAborted();
|
|
1478
|
+
if (error instanceof SubagentError) throw error;
|
|
1479
|
+
throw new SubagentError(`subagent "${childId}" is unavailable`, "NOT_RESUMABLE", { cause: error });
|
|
1480
|
+
}
|
|
1481
|
+
return await this.submitMaterialized(activation, content, options, parent);
|
|
1482
|
+
} catch (e_1) {
|
|
1483
|
+
env_1.error = e_1;
|
|
1484
|
+
env_1.hasError = true;
|
|
1485
|
+
} finally {
|
|
1486
|
+
__disposeResources$1(env_1);
|
|
1141
1487
|
}
|
|
1142
|
-
return this.submitMaterialized(activation, content, options.source, parent, options.signal);
|
|
1143
1488
|
}
|
|
1144
1489
|
/**
|
|
1145
1490
|
* Submit to a freshly materialized Activation or roll it back completely.
|
|
1146
1491
|
* @param activation - the just-published Activation to admit or release.
|
|
1147
1492
|
* @param content - the initial or resumed message content.
|
|
1148
|
-
* @param
|
|
1493
|
+
* @param options - durable source, scheduling, and pre-acceptance cancellation.
|
|
1149
1494
|
* @param parent - the live direct parent authorizing admission.
|
|
1150
|
-
* @param signal - caller cancellation owning admission until acceptance.
|
|
1151
1495
|
* @returns the accepted inbox message id.
|
|
1152
1496
|
*/
|
|
1153
|
-
async submitMaterialized(activation, content,
|
|
1497
|
+
async submitMaterialized(activation, content, options, parent) {
|
|
1154
1498
|
try {
|
|
1155
|
-
|
|
1499
|
+
if (contentHasImage(content)) {
|
|
1500
|
+
await this.assertImageCapable(activation.handle.agent, options.signal);
|
|
1501
|
+
if (activation.disposal !== void 0) throw new SubagentError(`subagent "${activation.childId}" is closing`, "ACTIVATION_CLOSING");
|
|
1502
|
+
}
|
|
1503
|
+
return this.submitAdmitted(activation, content, options, parent);
|
|
1156
1504
|
} catch (error) {
|
|
1157
1505
|
/* v8 ignore next -- rollback disposal failures must not mask the
|
|
1158
1506
|
* pre-acceptance signal, drain, or lifecycle failure. */
|
|
@@ -1161,6 +1509,28 @@ var SubagentContinuationManager = class {
|
|
|
1161
1509
|
}
|
|
1162
1510
|
}
|
|
1163
1511
|
/**
|
|
1512
|
+
* Refuse image content addressed to a child whose model accepts text only.
|
|
1513
|
+
* Callers guard with `contentHasImage`, so text-only delivery never awaits.
|
|
1514
|
+
* The check runs inside the per-child delivery lock, before the message
|
|
1515
|
+
* exists, so a rejection leaves no partial user message. When the child's
|
|
1516
|
+
* route is not fixed by its options (a request-waterfall listener owns it)
|
|
1517
|
+
* or no LLM registry is composed, delivery proceeds and the LLM layer's
|
|
1518
|
+
* text-only projection replaces each image with its stable placeholder.
|
|
1519
|
+
* @param agent - the live or freshly materialized child agent.
|
|
1520
|
+
* @param signal - caller cancellation bounding the model-info read.
|
|
1521
|
+
* @throws {SubagentError} `MODEL_DOES_NOT_SUPPORT_IMAGES` when the child's resolved model declines image input.
|
|
1522
|
+
*/
|
|
1523
|
+
async assertImageCapable(agent, signal) {
|
|
1524
|
+
const { provider, model } = agent.options;
|
|
1525
|
+
if (provider === void 0 || model === void 0) return;
|
|
1526
|
+
const llm = this.ctx.get("llm");
|
|
1527
|
+
/* v8 ignore next -- a deployment without the LLM registry serves no model
|
|
1528
|
+
* to refuse against; delivery then defers to the text-only projection. */
|
|
1529
|
+
if (llm === void 0) return;
|
|
1530
|
+
const info = await llm.resolveModelInfo(provider, model, signal);
|
|
1531
|
+
if (info.inputModalities !== void 0 && !info.inputModalities.includes("image")) throw new SubagentError(`Model "${model}" does not support image input.`, "MODEL_DOES_NOT_SUPPORT_IMAGES");
|
|
1532
|
+
}
|
|
1533
|
+
/**
|
|
1164
1534
|
* Create or resume the child Agent through the private activation-owner
|
|
1165
1535
|
* scope, install the handle in a fresh Activation, and register ownership on
|
|
1166
1536
|
* a continuation-managed parent. Rejection leaves no Activation, no handle,
|
|
@@ -1191,7 +1561,6 @@ var SubagentContinuationManager = class {
|
|
|
1191
1561
|
const setup = (childCtx) => {
|
|
1192
1562
|
if (create !== void 0) appendDelegatedPolicyOverrides(childCtx.agent.session, create.delegatedPolicies);
|
|
1193
1563
|
applyChildComposition(childCtx, parent, inputs.composition);
|
|
1194
|
-
return this.setupRegistry.apply(childCtx);
|
|
1195
1564
|
};
|
|
1196
1565
|
const observer = this.host.observeActivation(provider, childId, parent);
|
|
1197
1566
|
const handle = create === void 0 ? await this.ownerCtx.agents.resume({
|
|
@@ -1203,6 +1572,7 @@ var SubagentContinuationManager = class {
|
|
|
1203
1572
|
sessionId: childId,
|
|
1204
1573
|
meta: create.meta,
|
|
1205
1574
|
seed: create.seed,
|
|
1575
|
+
inheritedEventCount: create.inheritedEventCount,
|
|
1206
1576
|
agentOptions: inputs.agentOptions,
|
|
1207
1577
|
signal: inputs.signal,
|
|
1208
1578
|
setup
|
|
@@ -1284,14 +1654,15 @@ var SubagentContinuationManager = class {
|
|
|
1284
1654
|
* inbox id. Acceptance is the operation's success boundary; the manager owns
|
|
1285
1655
|
* the Activation independently afterwards.
|
|
1286
1656
|
*/
|
|
1287
|
-
submit(activation, content,
|
|
1657
|
+
submit(activation, content, options, parent) {
|
|
1288
1658
|
this.acquireOwnership(parent, activation.childId);
|
|
1289
|
-
const message = createUserMessage({
|
|
1659
|
+
const message = options.source === void 0 ? agentMessage(parent, content) : createUserMessage({
|
|
1290
1660
|
content,
|
|
1291
|
-
source
|
|
1661
|
+
source: options.source
|
|
1292
1662
|
});
|
|
1293
1663
|
const accepted = this.admitWaking(activation, message.id, () => {
|
|
1294
|
-
activation.handle.agent.
|
|
1664
|
+
if (options.delivery === "steer") activation.handle.agent.steer(message);
|
|
1665
|
+
else activation.handle.agent.followup(message);
|
|
1295
1666
|
});
|
|
1296
1667
|
activation.announced = true;
|
|
1297
1668
|
return accepted;
|
|
@@ -1319,14 +1690,14 @@ var SubagentContinuationManager = class {
|
|
|
1319
1690
|
* manager drain, or Activation disposal that wins before this synchronous
|
|
1320
1691
|
* span rejects without inbox acceptance.
|
|
1321
1692
|
*/
|
|
1322
|
-
submitAdmitted(activation, content,
|
|
1323
|
-
signal.throwIfAborted();
|
|
1693
|
+
submitAdmitted(activation, content, options, parent) {
|
|
1694
|
+
options.signal.throwIfAborted();
|
|
1324
1695
|
this.assertAdmitting(parent);
|
|
1325
1696
|
/* v8 ignore next 6 -- only a synchronous re-entrant disposer can change
|
|
1326
1697
|
* this field between the caller's live check and this no-await boundary. */
|
|
1327
1698
|
if (disposalOf(activation) !== void 0) throw new SubagentError(`subagent "${activation.childId}" activation is being disposed; the message was not accepted`, "ACTIVATION_CLOSING");
|
|
1328
1699
|
this.authorizeLineage(parent, activation.childId, activation.handle.agent.session.header.parentSession);
|
|
1329
|
-
return this.submit(activation, content,
|
|
1700
|
+
return this.submit(activation, content, options, parent);
|
|
1330
1701
|
}
|
|
1331
1702
|
/**
|
|
1332
1703
|
* Authorize one operation against the durable direct-parent lineage. Other
|
|
@@ -1504,147 +1875,26 @@ var SubagentContinuationManager = class {
|
|
|
1504
1875
|
if (persistence === void 0) throw new SubagentError("continuable subagents require session persistence (load a dsh-session-persistence backend)", "PERSISTENCE_UNAVAILABLE");
|
|
1505
1876
|
return persistence;
|
|
1506
1877
|
}
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
* child's unpublished creation context.
|
|
1513
|
-
*
|
|
1514
|
-
* A contribution grants a child-scoped capability without teaching the
|
|
1515
|
-
* continuation manager which capabilities exist. The manager owns residency;
|
|
1516
|
-
* this registry owns the join between plugin lifetime, unpublished setup, and
|
|
1517
|
-
* Activation disposal, so no installation outlives either owner and no removed
|
|
1518
|
-
* contribution can be installed after revocation reports completion.
|
|
1519
|
-
*
|
|
1520
|
-
* @module @xneog/dsh-subagent/activation-setup-registry
|
|
1521
|
-
*/
|
|
1522
|
-
/** Re-read mutable removal state after a contribution may have revoked itself. */
|
|
1523
|
-
function isRemoved(registration) {
|
|
1524
|
-
return registration.removed;
|
|
1525
|
-
}
|
|
1526
|
-
/**
|
|
1527
|
-
* Owns continuable-child setup registrations, installations, rollback, child
|
|
1528
|
-
* cleanup, and immediate live revocation.
|
|
1529
|
-
*/
|
|
1530
|
-
var SubagentActivationSetupRegistry = class {
|
|
1531
|
-
/** Live contributions in installation order. */
|
|
1532
|
-
registrations = /* @__PURE__ */ new Set();
|
|
1533
|
-
/** Child context to its live installations. */
|
|
1534
|
-
byChild = /* @__PURE__ */ new Map();
|
|
1535
|
-
/**
|
|
1536
|
-
* Register one contribution.
|
|
1537
|
-
* @param contribution - synchronous child-scope installer.
|
|
1538
|
-
* @returns an idempotent registration undo.
|
|
1539
|
-
* @throws after attempting every installation when any disposer fails.
|
|
1540
|
-
*/
|
|
1541
|
-
register(contribution) {
|
|
1542
|
-
const registration = {
|
|
1543
|
-
contribution,
|
|
1544
|
-
removed: false,
|
|
1545
|
-
installations: /* @__PURE__ */ new Set()
|
|
1546
|
-
};
|
|
1547
|
-
this.registrations.add(registration);
|
|
1548
|
-
return () => {
|
|
1549
|
-
if (registration.removed) return;
|
|
1550
|
-
registration.removed = true;
|
|
1551
|
-
this.registrations.delete(registration);
|
|
1552
|
-
this.releaseAll([...registration.installations], "contribution removal");
|
|
1553
|
-
};
|
|
1554
|
-
}
|
|
1555
|
-
/**
|
|
1556
|
-
* Install every live contribution into one unpublished child context.
|
|
1557
|
-
* @param childCtx - the child's unpublished scoped context.
|
|
1558
|
-
* @returns the provisioning commit consumed at Agent publication.
|
|
1559
|
-
*/
|
|
1560
|
-
apply(childCtx) {
|
|
1561
|
-
const state = {
|
|
1562
|
-
installations: [],
|
|
1563
|
-
invalidated: false
|
|
1564
|
-
};
|
|
1565
|
-
try {
|
|
1566
|
-
for (const registration of [...this.registrations]) {
|
|
1567
|
-
/* v8 ignore next -- only a synchronous re-entrant revocation of an
|
|
1568
|
-
* already-snapshotted registration reaches this guard. */
|
|
1569
|
-
if (registration.removed) continue;
|
|
1570
|
-
const installation = {
|
|
1571
|
-
registration,
|
|
1572
|
-
childCtx,
|
|
1573
|
-
dispose: registration.contribution(childCtx),
|
|
1574
|
-
released: false,
|
|
1575
|
-
transaction: state
|
|
1576
|
-
};
|
|
1577
|
-
registration.installations.add(installation);
|
|
1578
|
-
state.installations.push(installation);
|
|
1579
|
-
let indexed = this.byChild.get(childCtx);
|
|
1580
|
-
if (indexed === void 0) {
|
|
1581
|
-
indexed = /* @__PURE__ */ new Set();
|
|
1582
|
-
this.byChild.set(childCtx, indexed);
|
|
1583
|
-
}
|
|
1584
|
-
indexed.add(installation);
|
|
1585
|
-
if (isRemoved(registration)) this.release(installation);
|
|
1586
|
-
}
|
|
1587
|
-
} catch (error) {
|
|
1588
|
-
try {
|
|
1589
|
-
this.releaseAll([...state.installations], "setup rollback");
|
|
1590
|
-
} catch (releaseFailure) {}
|
|
1591
|
-
throw error;
|
|
1592
|
-
}
|
|
1593
|
-
childCtx.effect(() => () => {
|
|
1594
|
-
this.releaseChild(childCtx);
|
|
1595
|
-
}, "subagents.activationSetup()");
|
|
1596
|
-
return { commit: () => {
|
|
1597
|
-
if (state.invalidated) throw new SubagentError("a continuable-subagent setup contribution was revoked while this child was being built; the child was not established", "ACTIVATION_SETUP_REVOKED");
|
|
1598
|
-
for (const installation of state.installations) installation.transaction = void 0;
|
|
1599
|
-
} };
|
|
1600
|
-
}
|
|
1601
|
-
/** Release every remaining installation owned by one disposed child scope. */
|
|
1602
|
-
releaseChild(childCtx) {
|
|
1603
|
-
const indexed = this.byChild.get(childCtx) ?? [];
|
|
1604
|
-
this.releaseAll([...indexed], "child scope disposal");
|
|
1605
|
-
}
|
|
1606
|
-
/**
|
|
1607
|
-
* Release a batch completely before reporting disposer failures.
|
|
1608
|
-
* @param installations - records to release.
|
|
1609
|
-
* @param during - operation name for diagnostics.
|
|
1610
|
-
*/
|
|
1611
|
-
releaseAll(installations, during) {
|
|
1612
|
-
const failures = [];
|
|
1613
|
-
for (const installation of installations) try {
|
|
1614
|
-
this.release(installation);
|
|
1615
|
-
} catch (error) {
|
|
1616
|
-
failures.push(error);
|
|
1617
|
-
}
|
|
1618
|
-
if (failures.length === 0) return;
|
|
1619
|
-
throw new SubagentError(`continuable-subagent setup ${during} failed to release ${failures.length} installation(s): ` + failures.map((failure) => errorChain(failure)).join("; "), "ACTIVATION_SETUP_RELEASE_FAILED");
|
|
1620
|
-
}
|
|
1621
|
-
/** Drop one installation from both indices and dispose it exactly once. */
|
|
1622
|
-
release(installation) {
|
|
1623
|
-
if (installation.released) return;
|
|
1624
|
-
installation.released = true;
|
|
1625
|
-
installation.registration.installations.delete(installation);
|
|
1626
|
-
const indexed = this.byChild.get(installation.childCtx);
|
|
1627
|
-
/* v8 ignore next 4 -- every live installation is indexed until this method removes it. */
|
|
1628
|
-
if (indexed !== void 0) {
|
|
1629
|
-
indexed.delete(installation);
|
|
1630
|
-
if (indexed.size === 0) this.byChild.delete(installation.childCtx);
|
|
1631
|
-
}
|
|
1632
|
-
if (installation.transaction !== void 0) installation.transaction.invalidated = true;
|
|
1633
|
-
installation.dispose();
|
|
1878
|
+
/** Resolve the Session query service used for cold child observations. */
|
|
1879
|
+
requireSessionQuery() {
|
|
1880
|
+
const query = this.ctx.get("sessionQuery");
|
|
1881
|
+
if (query === void 0) throw new SubagentError("continuable subagents require session query (load @xneog/dsh-session-query)", "CONTINUATION_UNAVAILABLE");
|
|
1882
|
+
return query;
|
|
1634
1883
|
}
|
|
1635
1884
|
};
|
|
1636
1885
|
//#endregion
|
|
1637
1886
|
//#region lib/types/list-children.js
|
|
1638
1887
|
/**
|
|
1639
1888
|
* Read-only enumeration of durable subagent children and descendant trees
|
|
1640
|
-
*
|
|
1641
|
-
*
|
|
1642
|
-
*
|
|
1889
|
+
* through the Session query service. Candidates come from one live-preferred
|
|
1890
|
+
* corpus; each child's mode/label is the registered `subagent` projection
|
|
1891
|
+
* unit's value, resolved
|
|
1643
1892
|
* down a three-rung ladder: the registry's watermark cache for a live child,
|
|
1644
|
-
*
|
|
1645
|
-
*
|
|
1646
|
-
*
|
|
1647
|
-
* is the single classification
|
|
1893
|
+
* an unseeded durable projection-cache row, and one shared Session observation
|
|
1894
|
+
* otherwise. A seeded header deliberately lacks its exact inherited cut, so
|
|
1895
|
+
* it takes the body-bearing observation path before classifying an identity.
|
|
1896
|
+
* The projection fold is the single classification
|
|
1897
|
+
* authority — this module parses no descriptor
|
|
1648
1898
|
* itself. Absent persistence, enumeration is live-only: a cold child is
|
|
1649
1899
|
* unreachable for resume anyway, so its absence is capability absence, not an
|
|
1650
1900
|
* error. The module owns no catalog state and does not consult Activation,
|
|
@@ -1652,10 +1902,68 @@ var SubagentActivationSetupRegistry = class {
|
|
|
1652
1902
|
*
|
|
1653
1903
|
* @module @xneog/dsh-subagent
|
|
1654
1904
|
*/
|
|
1905
|
+
var __addDisposableResource = function(env, value, async) {
|
|
1906
|
+
if (value !== null && value !== void 0) {
|
|
1907
|
+
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
|
1908
|
+
var dispose, inner;
|
|
1909
|
+
if (async) {
|
|
1910
|
+
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
|
|
1911
|
+
dispose = value[Symbol.asyncDispose];
|
|
1912
|
+
}
|
|
1913
|
+
if (dispose === void 0) {
|
|
1914
|
+
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
|
|
1915
|
+
dispose = value[Symbol.dispose];
|
|
1916
|
+
if (async) inner = dispose;
|
|
1917
|
+
}
|
|
1918
|
+
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
|
|
1919
|
+
if (inner) dispose = function() {
|
|
1920
|
+
try {
|
|
1921
|
+
inner.call(this);
|
|
1922
|
+
} catch (e) {
|
|
1923
|
+
return Promise.reject(e);
|
|
1924
|
+
}
|
|
1925
|
+
};
|
|
1926
|
+
env.stack.push({
|
|
1927
|
+
value,
|
|
1928
|
+
dispose,
|
|
1929
|
+
async
|
|
1930
|
+
});
|
|
1931
|
+
} else if (async) env.stack.push({ async: true });
|
|
1932
|
+
return value;
|
|
1933
|
+
};
|
|
1934
|
+
var __disposeResources = (function(SuppressedError) {
|
|
1935
|
+
return function(env) {
|
|
1936
|
+
function fail(e) {
|
|
1937
|
+
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
|
|
1938
|
+
env.hasError = true;
|
|
1939
|
+
}
|
|
1940
|
+
var r, s = 0;
|
|
1941
|
+
function next() {
|
|
1942
|
+
while (r = env.stack.pop()) try {
|
|
1943
|
+
if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
|
|
1944
|
+
if (r.dispose) {
|
|
1945
|
+
var result = r.dispose.call(r.value);
|
|
1946
|
+
if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) {
|
|
1947
|
+
fail(e);
|
|
1948
|
+
return next();
|
|
1949
|
+
});
|
|
1950
|
+
} else s |= 1;
|
|
1951
|
+
} catch (e) {
|
|
1952
|
+
fail(e);
|
|
1953
|
+
}
|
|
1954
|
+
if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
|
|
1955
|
+
if (env.hasError) throw env.error;
|
|
1956
|
+
}
|
|
1957
|
+
return next();
|
|
1958
|
+
};
|
|
1959
|
+
})(typeof SuppressedError === "function" ? SuppressedError : function(error, suppressed, message) {
|
|
1960
|
+
var e = new Error(message);
|
|
1961
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
1962
|
+
});
|
|
1655
1963
|
/**
|
|
1656
|
-
* Concurrent cold
|
|
1657
|
-
*
|
|
1658
|
-
*
|
|
1964
|
+
* Concurrent cold observations per explicit catalog listing. Current Session
|
|
1965
|
+
* persistence providers are local; a networked provider must promote this to
|
|
1966
|
+
* a validated deployment setting.
|
|
1659
1967
|
*/
|
|
1660
1968
|
const COLD_READ_CONCURRENCY = 4;
|
|
1661
1969
|
/**
|
|
@@ -1663,9 +1971,8 @@ const COLD_READ_CONCURRENCY = 4;
|
|
|
1663
1971
|
* live-preferred merge of `ctx.sessions` and optional session persistence,
|
|
1664
1972
|
* serving each identity from the `subagent` projection unit: the registry's
|
|
1665
1973
|
* watermark snapshot for a live child; for a cold one, a durable
|
|
1666
|
-
* projection-cache
|
|
1667
|
-
*
|
|
1668
|
-
* registry.
|
|
1974
|
+
* projection-cache read for an unseeded lifecycle, else one bounded-concurrency
|
|
1975
|
+
* shared Session observation carrying the exact inherited cut.
|
|
1669
1976
|
* @see SubagentRuntime.listChildren for the public cancellation and failure contract.
|
|
1670
1977
|
* @param ctx - context carrying the session store, the projection registry,
|
|
1671
1978
|
* optional persistence, and the optional projection cache.
|
|
@@ -1714,32 +2021,30 @@ async function prepareListing(ctx, signal) {
|
|
|
1714
2021
|
const sessions = ctx.get("sessions");
|
|
1715
2022
|
if (sessions === void 0) throw new SubagentError("listing subagents requires the session store (load @xneog/dsh-session)", "SUBAGENT_CONTROL_SESSION_STORE_UNAVAILABLE");
|
|
1716
2023
|
assertListingNotCancelled(signal);
|
|
1717
|
-
const
|
|
2024
|
+
const query = ctx.get("sessionQuery");
|
|
2025
|
+
if (query === void 0) throw new SubagentError("listing subagents requires the sessionQuery service (load @xneog/dsh-session-query)", "SUBAGENT_CONTROL_QUERY_UNAVAILABLE");
|
|
1718
2026
|
const cache = ctx.get("sessionProjectionCache");
|
|
1719
|
-
let
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
} catch (error) {
|
|
1724
|
-
assertListingNotCancelled(signal);
|
|
1725
|
-
throw error;
|
|
1726
|
-
}
|
|
2027
|
+
let records;
|
|
2028
|
+
try {
|
|
2029
|
+
records = await query.listSessions(signal);
|
|
2030
|
+
} catch (error) {
|
|
1727
2031
|
assertListingNotCancelled(signal);
|
|
2032
|
+
throw error;
|
|
1728
2033
|
}
|
|
2034
|
+
assertListingNotCancelled(signal);
|
|
1729
2035
|
const corpus = /* @__PURE__ */ new Map();
|
|
1730
|
-
for (const
|
|
1731
|
-
header
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
});
|
|
2036
|
+
for (const record of records) {
|
|
2037
|
+
const live = sessions.get(record.header.id);
|
|
2038
|
+
corpus.set(record.header.id, {
|
|
2039
|
+
header: live?.header ?? record.header,
|
|
2040
|
+
live
|
|
2041
|
+
});
|
|
2042
|
+
}
|
|
1738
2043
|
const subagentParents = /* @__PURE__ */ new Set();
|
|
1739
2044
|
for (const record of corpus.values()) if (record.header.origin === "subagent" && record.header.parentSession !== void 0) subagentParents.add(record.header.parentSession);
|
|
1740
2045
|
return {
|
|
1741
2046
|
projections,
|
|
1742
|
-
|
|
2047
|
+
query,
|
|
1743
2048
|
cache,
|
|
1744
2049
|
corpus,
|
|
1745
2050
|
subagentParents
|
|
@@ -1747,7 +2052,7 @@ async function prepareListing(ctx, signal) {
|
|
|
1747
2052
|
}
|
|
1748
2053
|
/** Resolve projection-backed rows for aligned candidates with bounded cold reads. */
|
|
1749
2054
|
async function resolveCandidateRows(candidates, listing, signal) {
|
|
1750
|
-
const { projections,
|
|
2055
|
+
const { projections, query, cache, subagentParents } = listing;
|
|
1751
2056
|
const rows = Array.from({ length: candidates.length });
|
|
1752
2057
|
const coldReads = [];
|
|
1753
2058
|
candidates.forEach((candidate, index) => {
|
|
@@ -1761,7 +2066,7 @@ async function resolveCandidateRows(candidates, listing, signal) {
|
|
|
1761
2066
|
}
|
|
1762
2067
|
let identity;
|
|
1763
2068
|
try {
|
|
1764
|
-
identity = projections.snapshot(candidate.live).values.subagent;
|
|
2069
|
+
identity = projections.snapshot(candidate.live, ["subagent"]).values.subagent;
|
|
1765
2070
|
} catch {
|
|
1766
2071
|
rows[index] = {
|
|
1767
2072
|
kind: "diagnostic",
|
|
@@ -1770,13 +2075,13 @@ async function resolveCandidateRows(candidates, listing, signal) {
|
|
|
1770
2075
|
};
|
|
1771
2076
|
return;
|
|
1772
2077
|
}
|
|
1773
|
-
if (identity === void 0 || identity === null) return;
|
|
2078
|
+
if (identity === void 0 || identity === null || !candidate.live.isOwnSeq(identity.seq)) return;
|
|
1774
2079
|
rows[index] = childRow(childId, identity, "running", subagentParents.has(childId));
|
|
1775
2080
|
});
|
|
1776
|
-
if (
|
|
2081
|
+
if (coldReads.length > 0) {
|
|
1777
2082
|
const queue = [...coldReads];
|
|
1778
2083
|
await Promise.all(Array.from({ length: Math.min(COLD_READ_CONCURRENCY, queue.length) }, async () => {
|
|
1779
|
-
for (let job = queue.shift(); job !== void 0; job = queue.shift()) rows[job.index] = await resolveColdIdentity(
|
|
2084
|
+
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);
|
|
1780
2085
|
}));
|
|
1781
2086
|
}
|
|
1782
2087
|
assertListingNotCancelled(signal);
|
|
@@ -1820,60 +2125,62 @@ function compareCorpusRecords(a, b) {
|
|
|
1820
2125
|
return a.header.createdAt - b.header.createdAt || a.header.id.localeCompare(b.header.id);
|
|
1821
2126
|
}
|
|
1822
2127
|
/**
|
|
1823
|
-
* Resolve one cold candidate down the remaining ladder:
|
|
1824
|
-
* projection-cache row
|
|
1825
|
-
*
|
|
1826
|
-
*
|
|
1827
|
-
* projections). A failed inspection is one transient `unavailable` row
|
|
1828
|
-
* retried on the next listing; an inspection naming another lifecycle, and a
|
|
2128
|
+
* Resolve one cold candidate down the remaining ladder: an unseeded durable
|
|
2129
|
+
* projection-cache row, otherwise one shared Session observation. An absent or transiently failed
|
|
2130
|
+
* observation is one `unavailable` row retried on the next listing; an observation
|
|
2131
|
+
* source naming another lifecycle, and a
|
|
1829
2132
|
* settled log the fold cannot identify — or that makes any registered unit
|
|
1830
2133
|
* throw — are final, so they report `corrupt`.
|
|
1831
2134
|
*/
|
|
1832
|
-
async function resolveColdIdentity(
|
|
1833
|
-
const
|
|
1834
|
-
|
|
1835
|
-
|
|
2135
|
+
async function resolveColdIdentity(query, cache, header, hasChildren, signal) {
|
|
2136
|
+
const env_1 = {
|
|
2137
|
+
stack: [],
|
|
2138
|
+
error: void 0,
|
|
2139
|
+
hasError: false
|
|
2140
|
+
};
|
|
2141
|
+
try {
|
|
2142
|
+
const childId = header.id;
|
|
2143
|
+
if (cache !== void 0 && !header.isSeeded) {
|
|
2144
|
+
let cached;
|
|
2145
|
+
try {
|
|
2146
|
+
cached = cache.cachedSnapshot(header, SessionLogOffset(0), ["subagent"])?.values.subagent;
|
|
2147
|
+
} catch {
|
|
2148
|
+
cached = void 0;
|
|
2149
|
+
}
|
|
2150
|
+
if (cached !== void 0 && cached !== null) return childRow(childId, cached, "inactive", hasChildren);
|
|
2151
|
+
}
|
|
2152
|
+
assertListingNotCancelled(signal);
|
|
2153
|
+
let observation;
|
|
1836
2154
|
try {
|
|
1837
|
-
|
|
1838
|
-
} catch {
|
|
1839
|
-
|
|
2155
|
+
observation = await query.observeSession(childId, { ...signal === void 0 ? {} : { signal } });
|
|
2156
|
+
} catch (error) {
|
|
2157
|
+
assertListingNotCancelled(signal);
|
|
2158
|
+
return {
|
|
2159
|
+
kind: "diagnostic",
|
|
2160
|
+
id: childId,
|
|
2161
|
+
reason: sessionQueryCode(error) === "SESSION_QUERY_CORRUPT_SESSION" || sessionQueryCode(error) === "SESSION_QUERY_SOURCE_CONFLICT" ? "corrupt" : "unavailable"
|
|
2162
|
+
};
|
|
1840
2163
|
}
|
|
1841
|
-
|
|
1842
|
-
}
|
|
1843
|
-
assertListingNotCancelled(signal);
|
|
1844
|
-
let inspected;
|
|
1845
|
-
try {
|
|
1846
|
-
inspected = await persistence.inspect(childId, signal);
|
|
1847
|
-
} catch {
|
|
2164
|
+
const ownedObservation = __addDisposableResource(env_1, observation, false);
|
|
1848
2165
|
assertListingNotCancelled(signal);
|
|
1849
|
-
return {
|
|
2166
|
+
if (!sameLifecycle(ownedObservation.header, header)) return {
|
|
1850
2167
|
kind: "diagnostic",
|
|
1851
2168
|
id: childId,
|
|
1852
|
-
reason: "
|
|
2169
|
+
reason: "corrupt"
|
|
1853
2170
|
};
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
if (!sameLifecycle(inspected.meta, header)) return {
|
|
1857
|
-
kind: "diagnostic",
|
|
1858
|
-
id: childId,
|
|
1859
|
-
reason: "corrupt"
|
|
1860
|
-
};
|
|
1861
|
-
let identity;
|
|
1862
|
-
try {
|
|
1863
|
-
identity = projections.restore({}, inspected.events, 0).snapshot.values.subagent;
|
|
1864
|
-
} catch {
|
|
1865
|
-
return {
|
|
2171
|
+
const identity = ownedObservation.projections?.values.subagent;
|
|
2172
|
+
if (identity === void 0 || identity === null || identity.seq < ownedObservation.inheritedEventCount) return {
|
|
1866
2173
|
kind: "diagnostic",
|
|
1867
2174
|
id: childId,
|
|
1868
2175
|
reason: "corrupt"
|
|
1869
2176
|
};
|
|
2177
|
+
return childRow(childId, identity, "inactive", hasChildren);
|
|
2178
|
+
} catch (e_1) {
|
|
2179
|
+
env_1.error = e_1;
|
|
2180
|
+
env_1.hasError = true;
|
|
2181
|
+
} finally {
|
|
2182
|
+
__disposeResources(env_1);
|
|
1870
2183
|
}
|
|
1871
|
-
if (identity === void 0 || identity === null) return {
|
|
1872
|
-
kind: "diagnostic",
|
|
1873
|
-
id: childId,
|
|
1874
|
-
reason: "corrupt"
|
|
1875
|
-
};
|
|
1876
|
-
return childRow(childId, identity, "inactive", hasChildren);
|
|
1877
2184
|
}
|
|
1878
2185
|
/** Materialize one served identity as its child row. */
|
|
1879
2186
|
function childRow(id, identity, activity, hasChildren) {
|
|
@@ -1900,8 +2207,10 @@ const LIFECYCLE_WITNESS_KEYS = [
|
|
|
1900
2207
|
"createdAt",
|
|
1901
2208
|
"cwd",
|
|
1902
2209
|
"parentSession",
|
|
1903
|
-
"
|
|
1904
|
-
"delegationDepth"
|
|
2210
|
+
"isSeeded",
|
|
2211
|
+
"delegationDepth",
|
|
2212
|
+
"origin",
|
|
2213
|
+
"agentPreset"
|
|
1905
2214
|
];
|
|
1906
2215
|
/** Whether an inspected log still belongs to the enumerated lifecycle. */
|
|
1907
2216
|
function sameLifecycle(meta, expected) {
|
|
@@ -1911,6 +2220,28 @@ function sameLifecycle(meta, expected) {
|
|
|
1911
2220
|
function assertListingNotCancelled(signal) {
|
|
1912
2221
|
if (signal?.aborted) throw new SubagentError("subagent listing was cancelled", "CANCELLED");
|
|
1913
2222
|
}
|
|
2223
|
+
function sessionQueryCode(error) {
|
|
2224
|
+
return error instanceof Error && "code" in error ? error.code : void 0;
|
|
2225
|
+
}
|
|
2226
|
+
//#endregion
|
|
2227
|
+
//#region lib/types/projection.js
|
|
2228
|
+
/**
|
|
2229
|
+
* Pure session projections for subagent identity (mode/label) and active-turn
|
|
2230
|
+
* duration.
|
|
2231
|
+
*
|
|
2232
|
+
* @module @xneog/dsh-subagent/projection
|
|
2233
|
+
*/
|
|
2234
|
+
const activeIntervalSchema = z.object({
|
|
2235
|
+
since: z.number().int().nonnegative(),
|
|
2236
|
+
through: z.number().int().nonnegative()
|
|
2237
|
+
}).strict();
|
|
2238
|
+
const projectionSchema = z.object({
|
|
2239
|
+
settledMs: z.number().int().nonnegative(),
|
|
2240
|
+
active: activeIntervalSchema.optional()
|
|
2241
|
+
}).strict().transform(({ settledMs, active }) => ({
|
|
2242
|
+
settledMs,
|
|
2243
|
+
...active === void 0 ? {} : { active }
|
|
2244
|
+
}));
|
|
1914
2245
|
/**
|
|
1915
2246
|
* Fold turn boundaries around the child's own durable descriptor.
|
|
1916
2247
|
*
|
|
@@ -1921,12 +2252,11 @@ function assertListingNotCancelled(signal) {
|
|
|
1921
2252
|
*/
|
|
1922
2253
|
const subagentTimingProjectionDefinition = {
|
|
1923
2254
|
key: "subagentTiming",
|
|
1924
|
-
|
|
2255
|
+
stateSchema: z.object({
|
|
1925
2256
|
settledMs: z.number().int().nonnegative(),
|
|
1926
|
-
active:
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
}).strict().optional()
|
|
2257
|
+
active: activeIntervalSchema.optional(),
|
|
2258
|
+
pendingTurnStart: z.number().int().nonnegative().optional(),
|
|
2259
|
+
descriptorSeen: z.boolean()
|
|
1930
2260
|
}).strict(),
|
|
1931
2261
|
init: () => ({
|
|
1932
2262
|
descriptorSeen: false,
|
|
@@ -1976,21 +2306,26 @@ const subagentTimingProjectionDefinition = {
|
|
|
1976
2306
|
}
|
|
1977
2307
|
};
|
|
1978
2308
|
},
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
2309
|
+
wire: {
|
|
2310
|
+
viewSchema: projectionSchema,
|
|
2311
|
+
view: (state) => ({
|
|
2312
|
+
settledMs: state.settledMs,
|
|
2313
|
+
...state.active === void 0 ? {} : { active: state.active }
|
|
2314
|
+
})
|
|
2315
|
+
},
|
|
1983
2316
|
stateVersion: 2
|
|
1984
2317
|
};
|
|
1985
|
-
const
|
|
2318
|
+
const identityValueSchema = z.discriminatedUnion("mode", [z.object({
|
|
1986
2319
|
mode: z.literal("one-shot"),
|
|
1987
2320
|
label: z.string().optional(),
|
|
1988
|
-
seq: z.number().int().nonnegative()
|
|
2321
|
+
seq: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER).transform(SessionSeq)
|
|
1989
2322
|
}).strict(), z.object({
|
|
1990
2323
|
mode: z.literal("continuable"),
|
|
1991
2324
|
label: z.string(),
|
|
1992
|
-
seq: z.number().int().nonnegative()
|
|
1993
|
-
}).strict()])
|
|
2325
|
+
seq: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER).transform(SessionSeq)
|
|
2326
|
+
}).strict()]);
|
|
2327
|
+
const identitySchema = identityValueSchema.nullable();
|
|
2328
|
+
const identityStateSchema = z.object({ identity: identityValueSchema.optional() }).strict();
|
|
1994
2329
|
/** Interpret one `subagent/descriptor` event's identity; no value when the payload cannot be trusted. */
|
|
1995
2330
|
function descriptorIdentity(event) {
|
|
1996
2331
|
let descriptor;
|
|
@@ -2023,14 +2358,17 @@ function descriptorIdentity(event) {
|
|
|
2023
2358
|
*/
|
|
2024
2359
|
const subagentIdentityProjectionDefinition = {
|
|
2025
2360
|
key: "subagent",
|
|
2026
|
-
|
|
2361
|
+
stateSchema: identityStateSchema,
|
|
2027
2362
|
init: () => ({}),
|
|
2028
2363
|
apply: (state, event) => {
|
|
2029
2364
|
if (event.type !== "subagent/descriptor") return state;
|
|
2030
2365
|
const identity = descriptorIdentity(event);
|
|
2031
2366
|
return identity === void 0 ? {} : { identity };
|
|
2032
2367
|
},
|
|
2033
|
-
|
|
2368
|
+
wire: {
|
|
2369
|
+
viewSchema: identitySchema,
|
|
2370
|
+
view: (state) => state.identity ?? null
|
|
2371
|
+
},
|
|
2034
2372
|
stateVersion: 2
|
|
2035
2373
|
};
|
|
2036
2374
|
//#endregion
|
|
@@ -2047,13 +2385,38 @@ const subagentIdentityProjectionDefinition = {
|
|
|
2047
2385
|
*
|
|
2048
2386
|
* @module @xneog/dsh-subagent/out-of-process
|
|
2049
2387
|
*/
|
|
2388
|
+
/** Maximum UTF-8 size of {@link SubagentResult.diagnostic}. */
|
|
2389
|
+
const MAX_SUBAGENT_DIAGNOSTIC_BYTES = 4096;
|
|
2390
|
+
const DIAGNOSTIC_TRUNCATION_SUFFIX = "\n[diagnostic truncated]";
|
|
2391
|
+
const utf8Encoder = new TextEncoder();
|
|
2392
|
+
const utf8Decoder = new TextDecoder();
|
|
2393
|
+
/**
|
|
2394
|
+
* Limit provider-authored failure detail without splitting a UTF-8 sequence.
|
|
2395
|
+
* @param diagnostic - safe diagnostic text produced by the provider.
|
|
2396
|
+
* @returns the original text, or a visibly truncated value within the limit.
|
|
2397
|
+
*/
|
|
2398
|
+
function limitSubagentDiagnostic(diagnostic) {
|
|
2399
|
+
const bytes = utf8Encoder.encode(diagnostic);
|
|
2400
|
+
if (bytes.byteLength <= MAX_SUBAGENT_DIAGNOSTIC_BYTES) return diagnostic;
|
|
2401
|
+
let prefixBytes = MAX_SUBAGENT_DIAGNOSTIC_BYTES - utf8Encoder.encode(DIAGNOSTIC_TRUNCATION_SUFFIX).byteLength;
|
|
2402
|
+
while ((bytes[prefixBytes] & 192) === 128) prefixBytes -= 1;
|
|
2403
|
+
return utf8Decoder.decode(bytes.subarray(0, prefixBytes)) + DIAGNOSTIC_TRUNCATION_SUFFIX;
|
|
2404
|
+
}
|
|
2405
|
+
/** Enforce the byte limit on a provider-returned diagnostic. */
|
|
2406
|
+
function normalizeSubagentDiagnostic(result) {
|
|
2407
|
+
return result.diagnostic === void 0 ? result : {
|
|
2408
|
+
...result,
|
|
2409
|
+
diagnostic: limitSubagentDiagnostic(result.diagnostic)
|
|
2410
|
+
};
|
|
2411
|
+
}
|
|
2050
2412
|
/**
|
|
2051
2413
|
* The capability advertisement of an out-of-process backend: NONE. A child in
|
|
2052
2414
|
* another process cannot honor parent-enforced start features
|
|
2053
|
-
* (`outputSchema`/`maxDepth`/`toolFilter`/`persona`), so the service rejects a
|
|
2415
|
+
* (`agentOptions`/`outputSchema`/`maxDepth`/`toolFilter`/`persona`), so the service rejects a
|
|
2054
2416
|
* request needing any of them before `start` runs — never accepted-then-ignored.
|
|
2055
2417
|
*/
|
|
2056
2418
|
const NO_START_CAPABILITIES = Object.freeze({
|
|
2419
|
+
agentOptions: false,
|
|
2057
2420
|
outputSchema: false,
|
|
2058
2421
|
depthLimit: false,
|
|
2059
2422
|
toolFilter: false,
|
|
@@ -2141,7 +2504,8 @@ function toError(value) {
|
|
|
2141
2504
|
* rejects after publication. A normally completed or rejected attempt resolves
|
|
2142
2505
|
* as `aborted` when cancellation already settled locally; another rejection is
|
|
2143
2506
|
* flattened to `stopReason: 'error'` through the contained diagnostic sink.
|
|
2144
|
-
* The abort listener is
|
|
2507
|
+
* Provider-returned diagnostics use the same byte limit. The abort listener is
|
|
2508
|
+
* removed on every path.
|
|
2145
2509
|
* @param parts - the attempt, output snapshot, cancellation state, sink, and signal wiring.
|
|
2146
2510
|
* @returns the terminal result (never a rejection).
|
|
2147
2511
|
*/
|
|
@@ -2151,7 +2515,7 @@ async function settleRunResult(parts) {
|
|
|
2151
2515
|
return parts.cancelled() ? {
|
|
2152
2516
|
output: parts.collectOutput(),
|
|
2153
2517
|
stopReason: "aborted"
|
|
2154
|
-
} : result;
|
|
2518
|
+
} : normalizeSubagentDiagnostic(result);
|
|
2155
2519
|
} catch (error) {
|
|
2156
2520
|
if (parts.cancelled()) return {
|
|
2157
2521
|
output: parts.collectOutput(),
|
|
@@ -2160,8 +2524,11 @@ async function settleRunResult(parts) {
|
|
|
2160
2524
|
try {
|
|
2161
2525
|
parts.onError?.(toError(error), "error");
|
|
2162
2526
|
} catch {}
|
|
2527
|
+
const collected = parts.collectDiagnostic?.();
|
|
2528
|
+
const diagnostic = collected === void 0 ? void 0 : limitSubagentDiagnostic(collected);
|
|
2163
2529
|
return {
|
|
2164
2530
|
output: parts.collectOutput(),
|
|
2531
|
+
...diagnostic === void 0 ? {} : { diagnostic },
|
|
2165
2532
|
stopReason: "error"
|
|
2166
2533
|
};
|
|
2167
2534
|
} finally {
|
|
@@ -2204,9 +2571,16 @@ function subprocessRunHandle(parts) {
|
|
|
2204
2571
|
function finalText(blocks) {
|
|
2205
2572
|
return blocks.filter((block) => block.type === "text").map((block) => block.text).join("");
|
|
2206
2573
|
}
|
|
2574
|
+
/** Render a failed stop reason with optional provider-authored detail. */
|
|
2575
|
+
function failureDetail(result) {
|
|
2576
|
+
const stopReason = result.stopReason;
|
|
2577
|
+
return result.diagnostic === void 0 ? stopReason : `${stopReason}; diagnostic: ${result.diagnostic}`;
|
|
2578
|
+
}
|
|
2207
2579
|
/**
|
|
2208
|
-
* Map a child result to the task outcome: completed carries final text,
|
|
2209
|
-
* aborted is killed, and
|
|
2580
|
+
* Map a child result to the task outcome: completed carries final text, local
|
|
2581
|
+
* cancellation (`aborted` without a diagnostic) is killed, and provider-
|
|
2582
|
+
* diagnosed remote aborts plus every other reason are failed without partial
|
|
2583
|
+
* output.
|
|
2210
2584
|
* @param result - child terminal result.
|
|
2211
2585
|
* @returns outcome for the `ctx.jobs` registration.
|
|
2212
2586
|
*/
|
|
@@ -2216,16 +2590,19 @@ function runOutcome(result) {
|
|
|
2216
2590
|
status: "completed",
|
|
2217
2591
|
output: finalText(result.output)
|
|
2218
2592
|
};
|
|
2219
|
-
case "aborted": return { status: "killed" }
|
|
2593
|
+
case "aborted": return result.diagnostic === void 0 ? { status: "killed" } : {
|
|
2594
|
+
status: "failed",
|
|
2595
|
+
detail: failureDetail(result)
|
|
2596
|
+
};
|
|
2220
2597
|
case "error":
|
|
2221
2598
|
case "max-tokens":
|
|
2222
2599
|
case "refusal": return {
|
|
2223
2600
|
status: "failed",
|
|
2224
|
-
detail: result
|
|
2601
|
+
detail: failureDetail(result)
|
|
2225
2602
|
};
|
|
2226
2603
|
default: return {
|
|
2227
2604
|
status: "failed",
|
|
2228
|
-
detail:
|
|
2605
|
+
detail: failureDetail(result)
|
|
2229
2606
|
};
|
|
2230
2607
|
}
|
|
2231
2608
|
}
|
|
@@ -2263,10 +2640,8 @@ async function settleRun(run) {
|
|
|
2263
2640
|
* child before returning its run, so fulfillment is the single publication and
|
|
2264
2641
|
* ownership-transfer boundary.
|
|
2265
2642
|
*
|
|
2266
|
-
*
|
|
2267
|
-
*
|
|
2268
|
-
* one by name. The shape mirrors the LLM adapter registry
|
|
2269
|
-
* (`LlmRuntime.registerAdapter`), not the single-service bash executor.
|
|
2643
|
+
* Multiple providers coexist: each registers under a unique name and callers
|
|
2644
|
+
* select one by name.
|
|
2270
2645
|
*
|
|
2271
2646
|
* This package owns the Service Definition role of the capability seam. Service Providers
|
|
2272
2647
|
* (`@xneog/dsh-subagent-spawn-in-process`, `-fork`, `-acp`) and the model-facing
|
|
@@ -2274,8 +2649,8 @@ async function settleRun(run) {
|
|
|
2274
2649
|
*
|
|
2275
2650
|
* Public operations express caller intent: `start` returns one published owned
|
|
2276
2651
|
* one-shot run, `startContinuable` establishes a durable continuable child, and
|
|
2277
|
-
* `
|
|
2278
|
-
* resident. Continuable children never become a {@link SubagentRun}: the
|
|
2652
|
+
* `sendMessage` steers between adjacent Agents without exposing whether a child
|
|
2653
|
+
* is resident. Continuable children never become a {@link SubagentRun}: the
|
|
2279
2654
|
* continuation manager holds their `AgentHandle` directly and orders every turn
|
|
2280
2655
|
* through the child's own inbox, so providers contribute only the detached
|
|
2281
2656
|
* creation spec and see no handle, turn, or teardown. Child and descendant
|
|
@@ -2289,284 +2664,465 @@ async function settleRun(run) {
|
|
|
2289
2664
|
*
|
|
2290
2665
|
* @module @xneog/dsh-subagent
|
|
2291
2666
|
*/
|
|
2667
|
+
var __runInitializers = function(thisArg, initializers, value) {
|
|
2668
|
+
var useValue = arguments.length > 2;
|
|
2669
|
+
for (var i = 0; i < initializers.length; i++) value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
2670
|
+
return useValue ? value : void 0;
|
|
2671
|
+
};
|
|
2672
|
+
var __esDecorate = function(ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
2673
|
+
function accept(f) {
|
|
2674
|
+
if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected");
|
|
2675
|
+
return f;
|
|
2676
|
+
}
|
|
2677
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
2678
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
2679
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
2680
|
+
var _, done = false;
|
|
2681
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
2682
|
+
var context = {};
|
|
2683
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
2684
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
2685
|
+
context.addInitializer = function(f) {
|
|
2686
|
+
if (done) throw new TypeError("Cannot add initializers after decoration has completed");
|
|
2687
|
+
extraInitializers.push(accept(f || null));
|
|
2688
|
+
};
|
|
2689
|
+
var result = (0, decorators[i])(kind === "accessor" ? {
|
|
2690
|
+
get: descriptor.get,
|
|
2691
|
+
set: descriptor.set
|
|
2692
|
+
} : descriptor[key], context);
|
|
2693
|
+
if (kind === "accessor") {
|
|
2694
|
+
if (result === void 0) continue;
|
|
2695
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
2696
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
2697
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
2698
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
2699
|
+
} else if (_ = accept(result)) if (kind === "field") initializers.unshift(_);
|
|
2700
|
+
else descriptor[key] = _;
|
|
2701
|
+
}
|
|
2702
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
2703
|
+
done = true;
|
|
2704
|
+
};
|
|
2292
2705
|
/** Named provider registry with one-shot runs, durable discovery, and continuable-child operations. */
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2706
|
+
let SubagentRuntime = (() => {
|
|
2707
|
+
let _classSuper = TypertRemoteService;
|
|
2708
|
+
let _instanceExtraInitializers = [];
|
|
2709
|
+
let _remoteExportList_decorators;
|
|
2710
|
+
let _prompt_decorators;
|
|
2711
|
+
let _interruptByParent_decorators;
|
|
2712
|
+
return class SubagentRuntime extends _classSuper {
|
|
2713
|
+
static {
|
|
2714
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
2715
|
+
_remoteExportList_decorators = [Remote("list")];
|
|
2716
|
+
_prompt_decorators = [Remote("prompt")];
|
|
2717
|
+
_interruptByParent_decorators = [Remote("interruptByParent")];
|
|
2718
|
+
__esDecorate(this, null, _remoteExportList_decorators, {
|
|
2719
|
+
kind: "method",
|
|
2720
|
+
name: "remoteExportList",
|
|
2721
|
+
static: false,
|
|
2722
|
+
private: false,
|
|
2723
|
+
access: {
|
|
2724
|
+
has: (obj) => "remoteExportList" in obj,
|
|
2725
|
+
get: (obj) => obj.remoteExportList
|
|
2726
|
+
},
|
|
2727
|
+
metadata: _metadata
|
|
2728
|
+
}, null, _instanceExtraInitializers);
|
|
2729
|
+
__esDecorate(this, null, _prompt_decorators, {
|
|
2730
|
+
kind: "method",
|
|
2731
|
+
name: "prompt",
|
|
2732
|
+
static: false,
|
|
2733
|
+
private: false,
|
|
2734
|
+
access: {
|
|
2735
|
+
has: (obj) => "prompt" in obj,
|
|
2736
|
+
get: (obj) => obj.prompt
|
|
2737
|
+
},
|
|
2738
|
+
metadata: _metadata
|
|
2739
|
+
}, null, _instanceExtraInitializers);
|
|
2740
|
+
__esDecorate(this, null, _interruptByParent_decorators, {
|
|
2741
|
+
kind: "method",
|
|
2742
|
+
name: "interruptByParent",
|
|
2743
|
+
static: false,
|
|
2744
|
+
private: false,
|
|
2745
|
+
access: {
|
|
2746
|
+
has: (obj) => "interruptByParent" in obj,
|
|
2747
|
+
get: (obj) => obj.interruptByParent
|
|
2748
|
+
},
|
|
2749
|
+
metadata: _metadata
|
|
2750
|
+
}, null, _instanceExtraInitializers);
|
|
2751
|
+
if (_metadata) Object.defineProperty(this, Symbol.metadata, {
|
|
2752
|
+
enumerable: true,
|
|
2753
|
+
configurable: true,
|
|
2754
|
+
writable: true,
|
|
2755
|
+
value: _metadata
|
|
2756
|
+
});
|
|
2757
|
+
}
|
|
2758
|
+
providers = (__runInitializers(this, _instanceExtraInitializers), /* @__PURE__ */ new Map());
|
|
2759
|
+
continuations;
|
|
2760
|
+
/**
|
|
2761
|
+
* The contained lifecycle-edge publisher. Built here because scoped dispatch
|
|
2762
|
+
* keys its carrier by this exact service instance, whose own context filter
|
|
2763
|
+
* composes into the carrier.
|
|
2764
|
+
*/
|
|
2765
|
+
emitLifecycle;
|
|
2766
|
+
constructor(ctx) {
|
|
2767
|
+
super(ctx, "subagents");
|
|
2768
|
+
this.emitLifecycle = createLifecycleEmitter(this.ctx, (parent) => scopeTarget(this, parent));
|
|
2769
|
+
ctx.inject(["agents"], (childCtx) => {
|
|
2770
|
+
const manager = new SubagentContinuationManager(childCtx, {
|
|
2771
|
+
prepareContinuable: (name, request) => this.prepareContinuable(name, request),
|
|
2772
|
+
observeActivation: (provider, childId, parent) => this.observeActivation(provider, childId, parent)
|
|
2773
|
+
});
|
|
2774
|
+
this.continuations = manager;
|
|
2775
|
+
childCtx.effect(() => () => {
|
|
2776
|
+
/* v8 ignore else -- one injected binding owns the slot until its fiber disposes. */
|
|
2777
|
+
if (this.continuations === manager) this.continuations = void 0;
|
|
2778
|
+
}, "subagents.continuationBinding()");
|
|
2779
|
+
});
|
|
2780
|
+
ctx.inject(["sessionProjections"], (projectionCtx) => {
|
|
2781
|
+
projectionCtx.sessionProjections.register(subagentTimingProjectionDefinition);
|
|
2782
|
+
projectionCtx.sessionProjections.register(subagentIdentityProjectionDefinition);
|
|
2783
|
+
});
|
|
2784
|
+
}
|
|
2785
|
+
/**
|
|
2786
|
+
* Establish one durable continuable child and deliver its initial prompt.
|
|
2787
|
+
* Resolves when the child's inbox accepts that prompt, without waiting for the
|
|
2788
|
+
* turn to start or for the message to reach the Session log; any earlier
|
|
2789
|
+
* failure rejects with no ids and rolls back the child entirely.
|
|
2790
|
+
* @param spec - provider, delegation request, and caller cancellation.
|
|
2791
|
+
* @returns the durable child id and the accepted prompt's message id.
|
|
2792
|
+
* @throws when continuation services are unavailable or materialization fails.
|
|
2793
|
+
*/
|
|
2794
|
+
async startContinuable(spec) {
|
|
2795
|
+
return this.requireContinuations().startContinuable(spec);
|
|
2796
|
+
}
|
|
2797
|
+
/**
|
|
2798
|
+
* Steer one model-authored message to the sender's direct parent or direct
|
|
2799
|
+
* continuable child. A running target admits it at the nearest step boundary;
|
|
2800
|
+
* an idle target starts a turn, and an absent direct child cold-resumes from
|
|
2801
|
+
* persistence. The service derives durable sender attribution from the exact
|
|
2802
|
+
* live sender. Caller cancellation stops only pre-acceptance work.
|
|
2803
|
+
* @param sender - exact live Agent authorizing and originating the message.
|
|
2804
|
+
* @param targetId - durable direct-parent or direct-child session id.
|
|
2805
|
+
* @param content - model-authored content to deliver.
|
|
2806
|
+
* @param options - caller cancellation before inbox acceptance.
|
|
2807
|
+
* @returns the accepted message's inbox id.
|
|
2808
|
+
* @throws when continuation services are unavailable, adjacency is rejected,
|
|
2809
|
+
* or the message was not admitted.
|
|
2810
|
+
*/
|
|
2811
|
+
async sendMessage(sender, targetId, content, options) {
|
|
2812
|
+
return this.requireContinuations().sendMessage(sender, targetId, content, options);
|
|
2813
|
+
}
|
|
2814
|
+
/**
|
|
2815
|
+
* Deliver one host-protocol message to a direct continuable child.
|
|
2816
|
+
* Symbol-keyed so host adapters can preserve their own provenance without
|
|
2817
|
+
* widening the public Service Definition or impersonating an Agent sender.
|
|
2818
|
+
* @param parent - exact live direct parent authorizing delivery.
|
|
2819
|
+
* @param childId - durable direct-child session id.
|
|
2820
|
+
* @param content - host-authored content to deliver.
|
|
2821
|
+
* @param source - durable host-protocol provenance.
|
|
2822
|
+
* @param signal - caller cancellation before inbox acceptance.
|
|
2823
|
+
* @param delivery - Queue as a distinct turn or Steer at the nearest step.
|
|
2824
|
+
* @returns the accepted message's inbox id.
|
|
2825
|
+
*/
|
|
2826
|
+
[deliverSubagentPrompt](parent, childId, content, source, signal, delivery) {
|
|
2827
|
+
return delivery === "steer" ? this.requireContinuations().steerPrompt(parent, childId, content, source, signal) : this.requireContinuations().queuePrompt(parent, childId, content, source, signal);
|
|
2828
|
+
}
|
|
2829
|
+
/**
|
|
2830
|
+
* Interrupt one live continuable child's current turn under a human parent
|
|
2831
|
+
* address or an exact live ancestor Agent. Fire-and-return: the cancel
|
|
2832
|
+
* signal is issued before this returns, but the target may keep running
|
|
2833
|
+
* until it observes the signal. Unclaimed pending inbox work, the Activation,
|
|
2834
|
+
* and published descendants are preserved; claimed work is not requeued.
|
|
2835
|
+
* Once the interrupted driver is idle, a waking send resumes the parked FIFO
|
|
2836
|
+
* queue. An absent target — including a one-shot or unknown id —
|
|
2837
|
+
* is an accepted no-op, as is a manager-less composition, which cannot own a
|
|
2838
|
+
* live Activation.
|
|
2839
|
+
* @param targetSessionId - the durable child session id to interrupt.
|
|
2840
|
+
* @param authority - the human parent address or exact live ancestor Agent.
|
|
2841
|
+
* @throws {SubagentError} `UNAUTHORIZED` when the authority does not own the
|
|
2842
|
+
* live target.
|
|
2843
|
+
*/
|
|
2844
|
+
interrupt(targetSessionId, authority) {
|
|
2845
|
+
this.continuations?.interrupt(targetSessionId, authority);
|
|
2846
|
+
}
|
|
2847
|
+
/**
|
|
2848
|
+
* Close continuable admission below exact live parent Agents, stop only their
|
|
2849
|
+
* visible descendant Activations synchronously, then await admitted scoped
|
|
2850
|
+
* materializations and release those forests child-first. The scoped cutoff
|
|
2851
|
+
* lasts until each exact parent leaves the registry; unrelated parent trees
|
|
2852
|
+
* remain live.
|
|
2853
|
+
* @param parents - exact host-owned parent Agents entering teardown.
|
|
2854
|
+
* @returns once every retained descendant Activation released its `AgentHandle`.
|
|
2855
|
+
* @throws an aggregate error after all branches settle when any failed.
|
|
2856
|
+
*/
|
|
2857
|
+
async drainContinuableDescendants(parents) {
|
|
2858
|
+
const manager = this.continuations;
|
|
2859
|
+
if (manager === void 0) return;
|
|
2860
|
+
await manager.drainDescendants(parents);
|
|
2861
|
+
}
|
|
2862
|
+
/**
|
|
2863
|
+
* Release selected resident continuable direct children of one exact live
|
|
2864
|
+
* parent. Other children of the same parent remain admitted and resident.
|
|
2865
|
+
* Absent targets and a manager-less composition are accepted no-ops.
|
|
2866
|
+
* @param parent - exact live direct parent authorizing the selected release.
|
|
2867
|
+
* @param childIds - durable direct-child ids to release when resident.
|
|
2868
|
+
* @returns once every selected Activation released its `AgentHandle`.
|
|
2869
|
+
* @throws {SubagentError} `UNAUTHORIZED` when a resident target belongs to a
|
|
2870
|
+
* different parent or the supplied parent identity is stale.
|
|
2871
|
+
*/
|
|
2872
|
+
async drainContinuableChildren(parent, childIds) {
|
|
2873
|
+
const manager = this.continuations;
|
|
2874
|
+
if (manager === void 0) return;
|
|
2875
|
+
await manager.drainChildren(parent, childIds);
|
|
2876
|
+
}
|
|
2877
|
+
/**
|
|
2878
|
+
* Enumerate the parent's direct session-backed subagents without loading or
|
|
2879
|
+
* resuming an Agent. The Session query service supplies one live-preferred
|
|
2880
|
+
* corpus and shared point observations; the projection cache supplies
|
|
2881
|
+
* immutable descriptor hits without opening cold logs. The registered
|
|
2882
|
+
* `subagent` projection remains the sole mode/label classifier.
|
|
2883
|
+
*
|
|
2884
|
+
* Every query receives `signal`, and the listing rechecks cancellation
|
|
2885
|
+
* around each await. Read rejections that settle
|
|
2886
|
+
* after an abort become a stable `SubagentError` with code `CANCELLED`.
|
|
2887
|
+
* @param parentSessionId - parent session whose direct children are listed.
|
|
2888
|
+
* @param signal - caller-owned cancellation forwarded to Session queries
|
|
2889
|
+
* and observed around every read await.
|
|
2890
|
+
* @returns children and per-child diagnostics ordered by `createdAt`, then id.
|
|
2891
|
+
* @throws {@link SubagentError} when the projection registry or the session
|
|
2892
|
+
* store is not mounted, or the caller cancels the listing.
|
|
2893
|
+
*/
|
|
2894
|
+
listChildren(parentSessionId, signal) {
|
|
2895
|
+
return listChildren(this.ctx, parentSessionId, signal);
|
|
2896
|
+
}
|
|
2897
|
+
/**
|
|
2898
|
+
* Enumerate the root's complete session-backed subagent tree in stable
|
|
2899
|
+
* pre-order from one live-preferred corpus, without loading or resuming an
|
|
2900
|
+
* Agent. Ordinary sessions and one-shot children remain traversal nodes so
|
|
2901
|
+
* continuable descendants below them are discovered; each returned entry
|
|
2902
|
+
* adds its durable `parentId` and root-relative `depth`. Identity resolution,
|
|
2903
|
+
* diagnostics, optional persistence, and cancellation follow the same
|
|
2904
|
+
* projection-backed contract as {@link listChildren}.
|
|
2905
|
+
* @param rootSessionId - session whose complete descendant tree is listed.
|
|
2906
|
+
* @param signal - caller-owned cancellation forwarded to persistence reads
|
|
2907
|
+
* and observed around every read await.
|
|
2908
|
+
* @returns children and per-candidate diagnostics with tree position, in
|
|
2909
|
+
* stable pre-order.
|
|
2910
|
+
* @throws {@link SubagentError} under the same conditions as {@link listChildren}.
|
|
2911
|
+
*/
|
|
2912
|
+
listDescendants(rootSessionId, signal) {
|
|
2913
|
+
return listDescendants(this.ctx, rootSessionId, signal);
|
|
2914
|
+
}
|
|
2915
|
+
/**
|
|
2916
|
+
* Remote face of {@link listChildren} for one browser: the durable listing
|
|
2917
|
+
* plus live Agent activity and the delivery-time parent availability hint.
|
|
2918
|
+
* Parent availability is a hint; {@link prompt} performs the authoritative
|
|
2919
|
+
* check. Named apart from the provider-name {@link list}, which owns the
|
|
2920
|
+
* member.
|
|
2921
|
+
* @param parentSessionId - parent session whose direct children are listed.
|
|
2922
|
+
* @param signal - carrier cancellation forwarded to Session queries.
|
|
2923
|
+
* @returns the catalog view for that parent.
|
|
2924
|
+
* @throws {RemoteError} `gateway/bad-request` for an empty parent id,
|
|
2925
|
+
* `gateway/cancelled` for an aborted read, `subagent/projections-unavailable` when
|
|
2926
|
+
* the deployment has no projection registry, otherwise `gateway/internal`.
|
|
2927
|
+
*/
|
|
2928
|
+
async remoteExportList(parentSessionId, signal) {
|
|
2929
|
+
validateControlRequest("subagent.list", { parentSessionId });
|
|
2930
|
+
try {
|
|
2931
|
+
return catalogView(this.ctx, parentSessionId, await this.listChildren(parentSessionId, signal));
|
|
2932
|
+
} catch (error) {
|
|
2933
|
+
return rejectCatalogRead(error, signal);
|
|
2934
|
+
}
|
|
2935
|
+
}
|
|
2936
|
+
/**
|
|
2937
|
+
* Deliver one browser-authored message to a continuable child through the
|
|
2938
|
+
* exact live direct parent, retaining the caller-minted request identity and
|
|
2939
|
+
* validated browser zone on the accepted message. Success identifies the
|
|
2940
|
+
* message the child's FIFO inbox accepted; later execution is independent of
|
|
2941
|
+
* this call.
|
|
2942
|
+
* Image parts are admitted and persisted through the attachment store
|
|
2943
|
+
* before delivery, and the child's model must accept image input.
|
|
2944
|
+
* @param request - durable address, minted identity, content, and optional browser zone.
|
|
2945
|
+
* @param signal - carrier cancellation, owning the call until inbox acceptance.
|
|
2946
|
+
* @returns the accepted message's inbox identity.
|
|
2947
|
+
* @throws {RemoteError} `gateway/bad-request`, `subagent/attachment-invalid`,
|
|
2948
|
+
* `subagent/invalid-time-zone`, `subagent/parent-unavailable`,
|
|
2949
|
+
* `subagent/not-resumable`, `subagent/unauthorized`,
|
|
2950
|
+
* `subagent/delivery-unavailable`, `gateway/cancelled`, or `gateway/internal`.
|
|
2951
|
+
*/
|
|
2952
|
+
async prompt(request, signal) {
|
|
2953
|
+
const { parentSessionId, childSessionId, clientTimeZone } = request;
|
|
2954
|
+
validateControlRequest("subagent.prompt", request);
|
|
2955
|
+
const canonicalTimeZone = clientTimeZone === void 0 ? void 0 : canonicalClientTimeZone(clientTimeZone);
|
|
2956
|
+
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 });
|
|
2957
|
+
const parent = this.ctx.get("agents")?.get(parentSessionId);
|
|
2958
|
+
if (parent === void 0) throw new RemoteError("subagent/parent-unavailable", `parent session "${parentSessionId}" is not live`, { parentSessionId });
|
|
2959
|
+
const source = {
|
|
2960
|
+
kind: "user",
|
|
2961
|
+
rpcId: request.requestId,
|
|
2962
|
+
...canonicalTimeZone === void 0 ? {} : { clientTimeZone: canonicalTimeZone }
|
|
2475
2963
|
};
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
*/
|
|
2491
|
-
list() {
|
|
2492
|
-
return [...this.providers.keys()];
|
|
2493
|
-
}
|
|
2494
|
-
/**
|
|
2495
|
-
* Establish a published child on the named provider. Capability and semantic
|
|
2496
|
-
* checks run before delegation. Provider ownership lasts until its promise
|
|
2497
|
-
* fulfills; a rejection therefore has no run for the caller to dispose and
|
|
2498
|
-
* emits no run lifecycle events. Post-publication turn and infrastructure
|
|
2499
|
-
* failures settle through the returned run.
|
|
2500
|
-
* @param name - the provider to use.
|
|
2501
|
-
* @param request - child label, prompt, parent, signal, and optional capabilities.
|
|
2502
|
-
* @returns the published holder-owned run.
|
|
2503
|
-
*/
|
|
2504
|
-
async start(name, request) {
|
|
2505
|
-
const provider = this.expectProvider(name);
|
|
2506
|
-
this.assertCapabilities(provider, request);
|
|
2507
|
-
assertSubagentMaxDepth(request.maxDepth);
|
|
2508
|
-
if (request.outputSchema !== void 0) assertObjectJsonSchema(request.outputSchema);
|
|
2509
|
-
const descriptor = snapshotSubagentDescriptor({
|
|
2510
|
-
mode: "one-shot",
|
|
2511
|
-
provider: name,
|
|
2512
|
-
...request.label !== void 0 ? { label: request.label } : {}
|
|
2513
|
-
});
|
|
2514
|
-
const resolved = {
|
|
2515
|
-
...request,
|
|
2516
|
-
descriptor
|
|
2517
|
-
};
|
|
2518
|
-
return observeRun(this.emitLifecycle, name, request.parent, await provider.start(resolved));
|
|
2519
|
-
}
|
|
2520
|
-
/**
|
|
2521
|
-
* Resolve one provider's detached continuable-creation contribution. Method
|
|
2522
|
-
* presence on the provider IS the capability, so a provider without it is
|
|
2523
|
-
* rejected before the manager reserves any child resources.
|
|
2524
|
-
*/
|
|
2525
|
-
async prepareContinuable(name, request) {
|
|
2526
|
-
const provider = this.expectProvider(name);
|
|
2527
|
-
if (provider.prepareContinuable === void 0) throw new SubagentError(`subagent provider "${provider.name}" does not support continuable children (no prepareContinuable capability)`, "UNSUPPORTED_CAPABILITY");
|
|
2528
|
-
return provider.prepareContinuable(request);
|
|
2529
|
-
}
|
|
2530
|
-
/** Look up a provider for dispatch or fail loud. */
|
|
2531
|
-
expectProvider(name) {
|
|
2532
|
-
const provider = this.providers.get(name);
|
|
2533
|
-
if (provider === void 0) throw new SubagentError(`no subagent provider registered for "${name}"`, "NO_PROVIDER");
|
|
2534
|
-
return provider;
|
|
2535
|
-
}
|
|
2536
|
-
/** Resolve the optional continuable-subagent manager or fail loud. */
|
|
2537
|
-
requireContinuations() {
|
|
2538
|
-
if (this.continuations === void 0) throw new SubagentError("continuable subagents require the agents service", "CONTINUATION_UNAVAILABLE");
|
|
2539
|
-
return this.continuations;
|
|
2540
|
-
}
|
|
2541
|
-
/**
|
|
2542
|
-
* Build the lifecycle observer for one continuable Activation's residency
|
|
2543
|
-
* epoch, so the manager publishes its edges without owning event dispatch.
|
|
2544
|
-
*/
|
|
2545
|
-
observeActivation(provider, childId, parent) {
|
|
2546
|
-
return createActivationObserver(this.emitLifecycle, provider, childId, parent);
|
|
2547
|
-
}
|
|
2548
|
-
/** Reject the first requested capability that the provider lacks. */
|
|
2549
|
-
assertCapabilities(provider, request) {
|
|
2550
|
-
const needs = [
|
|
2551
|
-
{
|
|
2552
|
-
when: request.outputSchema !== void 0,
|
|
2553
|
-
cap: "outputSchema"
|
|
2554
|
-
},
|
|
2555
|
-
{
|
|
2556
|
-
when: request.maxDepth !== void 0,
|
|
2557
|
-
cap: "depthLimit"
|
|
2558
|
-
},
|
|
2559
|
-
{
|
|
2560
|
-
when: request.toolFilter !== void 0,
|
|
2561
|
-
cap: "toolFilter"
|
|
2562
|
-
},
|
|
2563
|
-
{
|
|
2564
|
-
when: request.persona !== void 0,
|
|
2565
|
-
cap: "persona"
|
|
2964
|
+
try {
|
|
2965
|
+
let content;
|
|
2966
|
+
if (request.content.every((part) => part.type === "text")) content = request.content.map((part) => ({
|
|
2967
|
+
type: "text",
|
|
2968
|
+
text: part.text
|
|
2969
|
+
}));
|
|
2970
|
+
else {
|
|
2971
|
+
const attachments = this.ctx.get("attachments");
|
|
2972
|
+
if (attachments === void 0) throw new Error("subagent image prompt requires an attachment store");
|
|
2973
|
+
content = await admitPromptContent(attachments, request.content);
|
|
2974
|
+
}
|
|
2975
|
+
return { messageId: await this[deliverSubagentPrompt](parent, childSessionId, content, source, signal, "queue") };
|
|
2976
|
+
} catch (error) {
|
|
2977
|
+
return rejectPrompt(error, childSessionId, signal);
|
|
2566
2978
|
}
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2979
|
+
}
|
|
2980
|
+
/**
|
|
2981
|
+
* Remote face of {@link interrupt} under one durable parent address. No
|
|
2982
|
+
* catalog, history, persistence, or parent Agent lookup runs: the core
|
|
2983
|
+
* primitive alone authorizes the address against the live Activation, which
|
|
2984
|
+
* is what keeps a live child interruptible while its parent Agent is offline.
|
|
2985
|
+
* Absent, idle, and already-completed targets are accepted no-ops there.
|
|
2986
|
+
* @param childSessionId - durable child session id to interrupt.
|
|
2987
|
+
* @param parentSessionId - durable direct parent whose authority is claimed.
|
|
2988
|
+
* @param mode - required continuable-address discriminator.
|
|
2989
|
+
* @returns acknowledgement that the cancel signal was admitted, not that the target is quiescent.
|
|
2990
|
+
* @throws {RemoteError} `gateway/bad-request` for an empty id,
|
|
2991
|
+
* `subagent/unauthorized` when the address does not own the live target,
|
|
2992
|
+
* otherwise `gateway/internal`.
|
|
2993
|
+
*/
|
|
2994
|
+
interruptByParent(childSessionId, parentSessionId, mode) {
|
|
2995
|
+
validateControlRequest("subagent.interrupt", {
|
|
2996
|
+
childSessionId,
|
|
2997
|
+
parentSessionId,
|
|
2998
|
+
mode
|
|
2999
|
+
});
|
|
3000
|
+
try {
|
|
3001
|
+
this.interrupt(childSessionId, {
|
|
3002
|
+
kind: "user",
|
|
3003
|
+
parentSessionId
|
|
3004
|
+
});
|
|
3005
|
+
} catch (error) {
|
|
3006
|
+
if (error instanceof SubagentError && error.code === "UNAUTHORIZED") throw new RemoteError("subagent/unauthorized", "subagent does not belong to this parent", { childSessionId }, { cause: error });
|
|
3007
|
+
throw new RemoteError("gateway/internal", "subagent interrupt failed", {}, { cause: error });
|
|
3008
|
+
}
|
|
3009
|
+
return { accepted: true };
|
|
3010
|
+
}
|
|
3011
|
+
/**
|
|
3012
|
+
* Register a provider under its name. Registration is effect-scoped and HMR
|
|
3013
|
+
* safe; removing a provider blocks new starts but does not revoke runs that
|
|
3014
|
+
* were already returned to their holders.
|
|
3015
|
+
* @param provider - the trusted provider implementation.
|
|
3016
|
+
* @returns the exact Cordis effect disposer.
|
|
3017
|
+
*/
|
|
3018
|
+
registerProvider(provider) {
|
|
3019
|
+
const name = provider.name;
|
|
3020
|
+
return this.ctx.effect(function* () {
|
|
3021
|
+
if (this.providers.has(name)) throw new SubagentError(`a subagent provider named "${name}" is already registered`, "DUPLICATE_PROVIDER");
|
|
3022
|
+
this.providers.set(name, provider);
|
|
3023
|
+
yield () => {
|
|
3024
|
+
this.providers.delete(name);
|
|
3025
|
+
this.emitLifecycle("subagent/provider-removed", name);
|
|
3026
|
+
};
|
|
3027
|
+
this.ctx.emit("subagent/provider-added", provider);
|
|
3028
|
+
}.bind(this), "subagents.registerProvider()");
|
|
3029
|
+
}
|
|
3030
|
+
/**
|
|
3031
|
+
* Look up a provider by name.
|
|
3032
|
+
* @param name - the provider name.
|
|
3033
|
+
* @returns the provider, or undefined when absent.
|
|
3034
|
+
*/
|
|
3035
|
+
getProvider(name) {
|
|
3036
|
+
return this.providers.get(name);
|
|
3037
|
+
}
|
|
3038
|
+
/**
|
|
3039
|
+
* List registered provider names in insertion order.
|
|
3040
|
+
* @returns the registered names.
|
|
3041
|
+
*/
|
|
3042
|
+
list() {
|
|
3043
|
+
return [...this.providers.keys()];
|
|
3044
|
+
}
|
|
3045
|
+
/**
|
|
3046
|
+
* Establish a published child on the named provider. Capability and semantic
|
|
3047
|
+
* checks run before delegation. Provider ownership lasts until its promise
|
|
3048
|
+
* fulfills; a rejection therefore has no run for the caller to dispose and
|
|
3049
|
+
* emits no run lifecycle events. Post-publication turn and infrastructure
|
|
3050
|
+
* failures settle through the returned run.
|
|
3051
|
+
* @param name - the provider to use.
|
|
3052
|
+
* @param request - child label, prompt, parent, signal, and optional capabilities.
|
|
3053
|
+
* @returns the published holder-owned run.
|
|
3054
|
+
*/
|
|
3055
|
+
async start(name, request) {
|
|
3056
|
+
const provider = this.expectProvider(name);
|
|
3057
|
+
this.assertCapabilities(provider, request);
|
|
3058
|
+
assertSubagentMaxDepth(request.maxDepth);
|
|
3059
|
+
if (request.outputSchema !== void 0) assertObjectJsonSchema(request.outputSchema);
|
|
3060
|
+
const descriptor = snapshotSubagentDescriptor({
|
|
3061
|
+
mode: "one-shot",
|
|
3062
|
+
provider: name,
|
|
3063
|
+
...request.label !== void 0 ? { label: request.label } : {}
|
|
3064
|
+
});
|
|
3065
|
+
const resolved = {
|
|
3066
|
+
...request,
|
|
3067
|
+
descriptor
|
|
3068
|
+
};
|
|
3069
|
+
return observeRun(this.emitLifecycle, name, request.parent, await provider.start(resolved));
|
|
3070
|
+
}
|
|
3071
|
+
/**
|
|
3072
|
+
* Resolve one provider's detached continuable-creation contribution. Method
|
|
3073
|
+
* presence on the provider IS the capability, so a provider without it is
|
|
3074
|
+
* rejected before the manager reserves any child resources.
|
|
3075
|
+
*/
|
|
3076
|
+
async prepareContinuable(name, request) {
|
|
3077
|
+
const provider = this.expectProvider(name);
|
|
3078
|
+
if (provider.prepareContinuable === void 0) throw new SubagentError(`subagent provider "${provider.name}" does not support continuable children (no prepareContinuable capability)`, "UNSUPPORTED_CAPABILITY");
|
|
3079
|
+
return provider.prepareContinuable(request);
|
|
3080
|
+
}
|
|
3081
|
+
/** Look up a provider for dispatch or fail loud. */
|
|
3082
|
+
expectProvider(name) {
|
|
3083
|
+
const provider = this.providers.get(name);
|
|
3084
|
+
if (provider === void 0) throw new SubagentError(`no subagent provider registered for "${name}"`, "NO_PROVIDER");
|
|
3085
|
+
return provider;
|
|
3086
|
+
}
|
|
3087
|
+
/** Resolve the optional continuable-subagent manager or fail loud. */
|
|
3088
|
+
requireContinuations() {
|
|
3089
|
+
if (this.continuations === void 0) throw new SubagentError("continuable subagents require the agents service", "CONTINUATION_UNAVAILABLE");
|
|
3090
|
+
return this.continuations;
|
|
3091
|
+
}
|
|
3092
|
+
/**
|
|
3093
|
+
* Build the lifecycle observer for one continuable Activation's residency
|
|
3094
|
+
* epoch, so the manager publishes its edges without owning event dispatch.
|
|
3095
|
+
*/
|
|
3096
|
+
observeActivation(provider, childId, parent) {
|
|
3097
|
+
return createActivationObserver(this.emitLifecycle, provider, childId, parent);
|
|
3098
|
+
}
|
|
3099
|
+
/** Reject the first requested capability that the provider lacks. */
|
|
3100
|
+
assertCapabilities(provider, request) {
|
|
3101
|
+
const needs = [
|
|
3102
|
+
{
|
|
3103
|
+
when: request.agentOptions !== void 0,
|
|
3104
|
+
cap: "agentOptions"
|
|
3105
|
+
},
|
|
3106
|
+
{
|
|
3107
|
+
when: request.outputSchema !== void 0,
|
|
3108
|
+
cap: "outputSchema"
|
|
3109
|
+
},
|
|
3110
|
+
{
|
|
3111
|
+
when: request.maxDepth !== void 0,
|
|
3112
|
+
cap: "depthLimit"
|
|
3113
|
+
},
|
|
3114
|
+
{
|
|
3115
|
+
when: request.toolFilter !== void 0,
|
|
3116
|
+
cap: "toolFilter"
|
|
3117
|
+
},
|
|
3118
|
+
{
|
|
3119
|
+
when: request.persona !== void 0,
|
|
3120
|
+
cap: "persona"
|
|
3121
|
+
}
|
|
3122
|
+
];
|
|
3123
|
+
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");
|
|
3124
|
+
}
|
|
3125
|
+
};
|
|
3126
|
+
})();
|
|
2571
3127
|
//#endregion
|
|
2572
|
-
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 };
|
|
3128
|
+
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 };
|