@fishwithcat/dsh-subagent 0.2.2-alpha.2 → 0.2.2-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.i18n.yaml +39 -24
- package/README.md +41 -20
- package/README.zh.md +41 -20
- package/lib/index.js +573 -872
- package/lib/typert.host.js +64 -120
- package/lib/typert.remote-client.d.ts +3 -3
- package/lib/typert.remote-client.js +17 -63
- package/lib/types/activation.d.ts +15 -25
- package/lib/types/activation.js +1 -1
- package/lib/types/archive-admission.d.ts +11 -4
- package/lib/types/archive-admission.js +39 -115
- package/lib/types/child-agent.d.ts +7 -17
- package/lib/types/child-agent.js +6 -4
- package/lib/types/continuation-messages.d.ts +8 -7
- package/lib/types/continuation-messages.js +12 -14
- package/lib/types/control-types.d.ts +7 -28
- package/lib/types/control.d.ts +0 -4
- package/lib/types/control.js +1 -5
- package/lib/types/descriptor.d.ts +1 -1
- package/lib/types/descriptor.js +1 -1
- package/lib/types/index.d.ts +46 -95
- package/lib/types/index.js +83 -146
- package/lib/types/lifecycle.d.ts +9 -38
- package/lib/types/lifecycle.js +5 -31
- package/lib/types/manager.d.ts +74 -109
- package/lib/types/manager.js +394 -568
- package/lib/types/projection-types.d.ts +14 -6
- package/lib/types/projection.d.ts +2 -2
- package/lib/types/structured.d.ts +1 -1
- package/lib/types/structured.js +2 -2
- package/lib/types/types.d.ts +16 -28
- package/lib/types/types.js +2 -4
- package/package.json +53 -57
- package/lib/types/internal.d.ts +0 -58
- package/lib/types/internal.js +0 -58
package/lib/index.js
CHANGED
|
@@ -7,8 +7,8 @@ import { Remote, RemoteError, TypertRemoteService, principalOf } from "@deepseek
|
|
|
7
7
|
import { AttachmentError } from "@deepseek-ai/dsh-attachment";
|
|
8
8
|
import { z as z$1 } from "zod";
|
|
9
9
|
import { HarnessError, ReasoningEffortId, boundContextSummary, contentHasImage, createUserMessage, errorChain, joinAssistantStreamText } from "@deepseek-ai/dsh-llm";
|
|
10
|
-
import { randomUUID } from "node:crypto";
|
|
11
10
|
import { foldConsumedWork } from "@deepseek-ai/dsh-agent";
|
|
11
|
+
import { randomUUID } from "node:crypto";
|
|
12
12
|
import { isAbsolute, resolve } from "node:path";
|
|
13
13
|
import { brandString } from "@deepseek-ai/dsh-brand";
|
|
14
14
|
import { SessionLogOffset, SessionSeq } from "@deepseek-ai/dsh-session";
|
|
@@ -39,16 +39,10 @@ var SubagentError = class extends HarnessError {
|
|
|
39
39
|
const SESSION_ID_SCHEMA = z$1.string().min(1);
|
|
40
40
|
const CONTROL_ID_SCHEMAS = {
|
|
41
41
|
"subagent.prompt": z$1.object({
|
|
42
|
-
parentSessionId: SESSION_ID_SCHEMA,
|
|
43
42
|
childSessionId: SESSION_ID_SCHEMA,
|
|
44
|
-
mode: z$1.literal("continuable"),
|
|
45
43
|
delivery: z$1.enum(["queue", "steer"])
|
|
46
44
|
}),
|
|
47
|
-
"subagent.interrupt": z$1.object({
|
|
48
|
-
parentSessionId: SESSION_ID_SCHEMA,
|
|
49
|
-
childSessionId: SESSION_ID_SCHEMA,
|
|
50
|
-
mode: z$1.literal("continuable")
|
|
51
|
-
})
|
|
45
|
+
"subagent.interrupt": z$1.object({ childSessionId: SESSION_ID_SCHEMA })
|
|
52
46
|
};
|
|
53
47
|
/**
|
|
54
48
|
* Apply the subagent payload checks that are stricter than generated
|
|
@@ -77,7 +71,7 @@ function rejectPrompt(error, childSessionId, signal) {
|
|
|
77
71
|
if (error instanceof SubagentError) switch (error.code) {
|
|
78
72
|
case "MODEL_DOES_NOT_SUPPORT_IMAGES": throw new RemoteError("subagent/attachment-invalid", error.message, { reason: error.code }, { cause: error });
|
|
79
73
|
case "NOT_RESUMABLE": throw new RemoteError("subagent/not-resumable", "subagent cannot be resumed", { childSessionId }, { cause: error });
|
|
80
|
-
case "UNAUTHORIZED": throw new RemoteError("subagent/unauthorized", "subagent
|
|
74
|
+
case "UNAUTHORIZED": throw new RemoteError("subagent/unauthorized", "subagent membership in the active tree could not be verified", { childSessionId }, { cause: error });
|
|
81
75
|
case "DRAINING":
|
|
82
76
|
case "ACTIVATION_CLOSING":
|
|
83
77
|
case "ACTIVATION_LIMIT_REACHED":
|
|
@@ -122,32 +116,12 @@ function assertSubagentMaxDepth(maxDepth) {
|
|
|
122
116
|
if (maxDepth !== void 0 && (typeof maxDepth !== "number" || !Number.isSafeInteger(maxDepth) || maxDepth < 0 || Object.is(maxDepth, -0))) throw new TypeError("subagent maxDepth must be a non-negative safe integer");
|
|
123
117
|
}
|
|
124
118
|
//#endregion
|
|
125
|
-
//#region lib/types/types.js
|
|
126
|
-
/**
|
|
127
|
-
* The seam's consumer-facing contracts: request, result, and capability types
|
|
128
|
-
* for {@link SubagentProvider}, plus the `subagent/start` and `subagent/end`
|
|
129
|
-
* payloads that plugins and hosts observe. Internal control interfaces belong
|
|
130
|
-
* with their implementation — the lifecycle observer in `./lifecycle.ts`, the
|
|
131
|
-
* manager host in `./manager.ts` — so this module stays the published
|
|
132
|
-
* surface rather than a bag of everything type-shaped.
|
|
133
|
-
*
|
|
134
|
-
* @module @deepseek-ai/dsh-subagent/types
|
|
135
|
-
*/
|
|
136
|
-
/**
|
|
137
|
-
* Brand a string as a {@link SubagentRunId}.
|
|
138
|
-
* @param id - the raw run id.
|
|
139
|
-
* @returns the same string, branded.
|
|
140
|
-
*/
|
|
141
|
-
function SubagentRunId(id) {
|
|
142
|
-
return id;
|
|
143
|
-
}
|
|
144
|
-
//#endregion
|
|
145
119
|
//#region lib/types/lifecycle.js
|
|
146
120
|
/**
|
|
147
|
-
*
|
|
121
|
+
* Contained lifecycle notifications for subagents.
|
|
148
122
|
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
123
|
+
* The service owns scoped dispatch and listener containment; the manager
|
|
124
|
+
* supplies event payloads from each activation.
|
|
151
125
|
*
|
|
152
126
|
* @module @deepseek-ai/dsh-subagent/lifecycle
|
|
153
127
|
*/
|
|
@@ -157,8 +131,8 @@ function SubagentRunId(id) {
|
|
|
157
131
|
* returned promise is logged without starving peer listeners, changing the run,
|
|
158
132
|
* or — for provider removal, which fires from a disposer — breaking teardown.
|
|
159
133
|
* @param ctx - the service's own context, owning dispatch and the logger.
|
|
160
|
-
* @param carrier - resolve the scoped dispatch carrier for one
|
|
161
|
-
* @returns the emitter
|
|
134
|
+
* @param carrier - resolve the scoped dispatch carrier for one subagent tree root.
|
|
135
|
+
* @returns the emitter the manager and provider registry publish through.
|
|
162
136
|
*/
|
|
163
137
|
function createLifecycleEmitter(ctx, carrier) {
|
|
164
138
|
return (name, info, parent) => {
|
|
@@ -178,35 +152,6 @@ function createLifecycleEmitter(ctx, carrier) {
|
|
|
178
152
|
};
|
|
179
153
|
}
|
|
180
154
|
/**
|
|
181
|
-
* Build the observer for one local or external activation's residency epoch. Creation failure before residency emits no lifecycle edge.
|
|
182
|
-
* @param emit - the contained lifecycle emitter.
|
|
183
|
-
* @param provider - the provider name recorded in the durable descriptor.
|
|
184
|
-
* @param childId - the durable child session id.
|
|
185
|
-
* @param parent - the exact live direct parent keying scoped dispatch.
|
|
186
|
-
* @returns the observer whose edges this epoch publishes.
|
|
187
|
-
*/
|
|
188
|
-
function createActivationObserver(emit, provider, childId, parent) {
|
|
189
|
-
const identity = {
|
|
190
|
-
runId: SubagentRunId(randomUUID()),
|
|
191
|
-
provider,
|
|
192
|
-
id: childId,
|
|
193
|
-
local: false
|
|
194
|
-
};
|
|
195
|
-
return {
|
|
196
|
-
start: (child) => {
|
|
197
|
-
identity.local = child !== void 0;
|
|
198
|
-
emit("subagent/start", identity, parent);
|
|
199
|
-
},
|
|
200
|
-
settle: ({ stopReason, output }) => {
|
|
201
|
-
emit("subagent/end", {
|
|
202
|
-
...identity,
|
|
203
|
-
stopReason,
|
|
204
|
-
...output.length === 0 ? {} : { lastAssistantMessage: output }
|
|
205
|
-
}, parent);
|
|
206
|
-
}
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
/**
|
|
210
155
|
* Why this child's epoch ended, for the terminal lifecycle edge and the
|
|
211
156
|
* manager's own parent delivery. The child's own log is authoritative:
|
|
212
157
|
* teardown succeeding says nothing about whether the model errored, hit its
|
|
@@ -371,10 +316,11 @@ const SUBAGENT_DELEGATION_CONTEXT = "You are a delegated subagent: your permissi
|
|
|
371
316
|
const SUBAGENT_DELEGATION_INTERACTIVE_CONTEXT = "You are a delegated subagent: your permission scope was fixed when you were started and cannot be widened from inside this session. An operation that needs access beyond that scope may request approval from the user, who can allow or reject it. Do not retry a rejected or denied operation; state the limitation in your reply so the delegating agent can handle it.";
|
|
372
317
|
/**
|
|
373
318
|
* Compose a child before publication and restore historical descriptor overrides.
|
|
374
|
-
* Explicit selection mounts the named preset
|
|
375
|
-
*
|
|
319
|
+
* Explicit selection mounts the named preset. Omission joins a supplied creator's
|
|
320
|
+
* current generation; a restored child without a saved preset uses host composition.
|
|
321
|
+
* Registrations belong to the child's scope.
|
|
376
322
|
* @param childCtx - the child agent's scoped creation context.
|
|
377
|
-
* @param parent -
|
|
323
|
+
* @param parent - creator for fresh delegation; absent during target-only restoration.
|
|
378
324
|
* @param composition - independent preset and any overrides read from a historical descriptor.
|
|
379
325
|
* @param approvalPolicy - the child's effective approval policy, selecting its delegation statement.
|
|
380
326
|
* @returns A promise when mounting an independent preset; otherwise completes synchronously.
|
|
@@ -387,7 +333,7 @@ function applyChildComposition(childCtx, parent, composition, approvalPolicy) {
|
|
|
387
333
|
applyChildOverrides(childCtx, composition, approvalPolicy);
|
|
388
334
|
});
|
|
389
335
|
}
|
|
390
|
-
childCtx.get("agentPresets")?.composeFrom(childCtx, parent.ctx);
|
|
336
|
+
if (parent !== void 0) childCtx.get("agentPresets")?.composeFrom(childCtx, parent.ctx);
|
|
391
337
|
applyChildOverrides(childCtx, composition, approvalPolicy);
|
|
392
338
|
}
|
|
393
339
|
function applyChildOverrides(childCtx, composition, approvalPolicy) {
|
|
@@ -453,7 +399,7 @@ function appendDelegatedPolicyOverrides(childSession, overrides) {
|
|
|
453
399
|
*
|
|
454
400
|
* @module @deepseek-ai/dsh-subagent/continuation-messages
|
|
455
401
|
*/
|
|
456
|
-
/** Build durable attribution for one
|
|
402
|
+
/** Build durable attribution for one Agent message. */
|
|
457
403
|
function agentMessageSource(sender) {
|
|
458
404
|
return {
|
|
459
405
|
kind: "agent-message",
|
|
@@ -462,7 +408,7 @@ function agentMessageSource(sender) {
|
|
|
462
408
|
};
|
|
463
409
|
}
|
|
464
410
|
/**
|
|
465
|
-
* Build the model-visible and durable representation of one
|
|
411
|
+
* Build the model-visible and durable representation of one Agent message.
|
|
466
412
|
* @param sender - exact live Agent that authored the message.
|
|
467
413
|
* @param content - model-visible message blocks supplied by the sender.
|
|
468
414
|
* @returns the durable user-message representation delivered to the recipient.
|
|
@@ -477,16 +423,14 @@ function createAgentMessage(sender, content) {
|
|
|
477
423
|
});
|
|
478
424
|
}
|
|
479
425
|
/**
|
|
480
|
-
* Append
|
|
481
|
-
* @param parentId - durable parent session id named in the guidance.
|
|
426
|
+
* Append automatic result-delivery guidance to a parent-delivered child's initial task.
|
|
482
427
|
* @param prompt - initial model-visible task blocks.
|
|
483
|
-
* @returns task blocks followed by the
|
|
428
|
+
* @returns task blocks followed by the result-delivery guidance.
|
|
484
429
|
*/
|
|
485
|
-
function
|
|
486
|
-
const encodedParentId = JSON.stringify(parentId);
|
|
430
|
+
function withResultDeliveryGuidance(prompt) {
|
|
487
431
|
return [...prompt, {
|
|
488
432
|
type: "text",
|
|
489
|
-
text:
|
|
433
|
+
text: "Your final answer is delivered automatically if your parent is still online. Include the result and relevant evidence in that answer; the parent does not receive your full transcript, tool output, or reasoning. If your last action is a message to your parent, the completion notice omits your final answer; include the result and relevant evidence in that message."
|
|
490
434
|
}];
|
|
491
435
|
}
|
|
492
436
|
/**
|
|
@@ -516,8 +460,8 @@ function settlementSummary(childId, stopReason, continuable) {
|
|
|
516
460
|
* @param childId - durable child session id named in the notice.
|
|
517
461
|
* @param terminal - recorded terminal state for the settled Activation.
|
|
518
462
|
* @param continuable - whether the child can accept another task.
|
|
519
|
-
* @param omitClosingText - replace the closing text
|
|
520
|
-
*
|
|
463
|
+
* @param omitClosingText - replace the closing text when the child's last action
|
|
464
|
+
* was a message to its direct parent.
|
|
521
465
|
* @returns the durable user-message representation delivered to the parent.
|
|
522
466
|
*/
|
|
523
467
|
function createSettlementMessage(childId, terminal, continuable = true, omitClosingText = false) {
|
|
@@ -552,11 +496,84 @@ function createSettlementMessage(childId, terminal, continuable = true, omitClos
|
|
|
552
496
|
kind: "subagent-settled",
|
|
553
497
|
form: "notice",
|
|
554
498
|
summary: boundContextSummary(summary),
|
|
499
|
+
stopReason: terminal.stopReason,
|
|
555
500
|
senderSessionId: childId
|
|
556
501
|
}
|
|
557
502
|
});
|
|
558
503
|
}
|
|
559
504
|
//#endregion
|
|
505
|
+
//#region lib/types/archive-admission.js
|
|
506
|
+
/**
|
|
507
|
+
* The `subagent` family of the Workspace registry's archive admission: which
|
|
508
|
+
* subagent descendants of a Session are still inside a turn, and how they
|
|
509
|
+
* stop when the Session is archived with its work. Archived ancestors also
|
|
510
|
+
* block later model steps, including when their Sessions have been released.
|
|
511
|
+
*
|
|
512
|
+
* @module @deepseek-ai/dsh-subagent
|
|
513
|
+
*/
|
|
514
|
+
/**
|
|
515
|
+
* Answer `workspace/session-activity` with the running subagent descendants of
|
|
516
|
+
* the asked Session, and `workspace/session-stop` by cancelling each of them
|
|
517
|
+
* as their parent would. Reject subsequent steps beneath archived ancestors.
|
|
518
|
+
* All listeners live as long as `ctx`'s fiber.
|
|
519
|
+
* @param ctx - context carrying the Agent registry.
|
|
520
|
+
* @param residentAncestors - verified creation ancestors for an exact managed Agent, absent before publication or for unmanaged Agents.
|
|
521
|
+
* @param identityOf - ready service-owned descriptor identity reader; absent or invalid descriptors yield no identity.
|
|
522
|
+
*/
|
|
523
|
+
function installSubagentArchiveAdmission(ctx, residentAncestors, identityOf) {
|
|
524
|
+
const ancestorsOf = (agent) => residentAncestors(agent) ?? headerAncestors(ctx, agent);
|
|
525
|
+
const runningDescendants = (sessionId) => ctx.agents.list().filter((agent) => agent.status === "running" && ancestorsOf(agent).includes(sessionId));
|
|
526
|
+
ctx.on("workspace/session-activity", async ({ sessionId }, next) => {
|
|
527
|
+
const running = runningDescendants(sessionId);
|
|
528
|
+
const rest = await next();
|
|
529
|
+
if (running.length === 0) return rest;
|
|
530
|
+
return [{
|
|
531
|
+
kind: "subagent",
|
|
532
|
+
items: running.map((child) => describe(child, identityOf(child.session)))
|
|
533
|
+
}, ...rest];
|
|
534
|
+
});
|
|
535
|
+
ctx.on("workspace/session-stop", ({ sessionId }) => {
|
|
536
|
+
for (const child of runningDescendants(sessionId)) try {
|
|
537
|
+
child.cancel({ kind: "parent" });
|
|
538
|
+
} catch (error) {
|
|
539
|
+
ctx.logger.warn(`subagent: cancelling "${child.id}" for an archived Session failed: ${String(error)}`);
|
|
540
|
+
}
|
|
541
|
+
});
|
|
542
|
+
ctx.on("agent/pre-step", ({ agent }, next) => {
|
|
543
|
+
const archived = ctx.get("workspaceRegistry")?.archivedSessionIds;
|
|
544
|
+
return archived !== void 0 && ancestorsOf(agent).some((id) => archived.includes(id)) ? Promise.resolve({ kind: "reject" }) : next();
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Header-only ancestry for Agents without a published Activation. Include an
|
|
549
|
+
* offline direct creator; ordinary forks stop the chain and cycles terminate.
|
|
550
|
+
*/
|
|
551
|
+
function headerAncestors(ctx, agent) {
|
|
552
|
+
const ancestors = [];
|
|
553
|
+
const visited = new Set([agent.id]);
|
|
554
|
+
let header = agent.session.header;
|
|
555
|
+
while (header.origin === "subagent" && header.parentSession !== void 0 && !visited.has(header.parentSession)) {
|
|
556
|
+
const parentId = header.parentSession;
|
|
557
|
+
ancestors.push(parentId);
|
|
558
|
+
visited.add(parentId);
|
|
559
|
+
const parent = ctx.agents.get(parentId)?.session;
|
|
560
|
+
if (parent === void 0) break;
|
|
561
|
+
header = parent.header;
|
|
562
|
+
}
|
|
563
|
+
return ancestors;
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* Report the child's current projected label only when its descriptor belongs
|
|
567
|
+
* to the child's own log; inherited, absent, or invalid identities yield the id alone.
|
|
568
|
+
*/
|
|
569
|
+
function describe(child, identity) {
|
|
570
|
+
const label = identity !== void 0 && child.session.isOwnSeq(identity.seq) ? identity.label : void 0;
|
|
571
|
+
return label === void 0 ? { id: child.id } : {
|
|
572
|
+
id: child.id,
|
|
573
|
+
label
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
//#endregion
|
|
560
577
|
//#region lib/types/descriptor.js
|
|
561
578
|
/**
|
|
562
579
|
* The durable subagent-child descriptor: the versioned, model-hidden
|
|
@@ -574,7 +591,7 @@ function createSettlementMessage(childId, terminal, continuable = true, omitClos
|
|
|
574
591
|
* to one activation's result contract rather than durable child composition.
|
|
575
592
|
* Per-activation knobs such as `maxTokens` are omitted for the same reason as
|
|
576
593
|
* `outputSchema`: they budget one activation. Cold resume requires the exact
|
|
577
|
-
* live
|
|
594
|
+
* live subagent tree root for authorization but reconstructs child options only from the
|
|
578
595
|
* durable descriptor, so it neither restores the prior budget nor inherits
|
|
579
596
|
* the parent's current one; the resumed route's defaults apply instead.
|
|
580
597
|
*
|
|
@@ -852,28 +869,23 @@ function establishExternalCatalogChild(parent, childId, label) {
|
|
|
852
869
|
});
|
|
853
870
|
}
|
|
854
871
|
//#endregion
|
|
855
|
-
//#region lib/types/
|
|
872
|
+
//#region lib/types/types.js
|
|
856
873
|
/**
|
|
857
|
-
*
|
|
858
|
-
*
|
|
859
|
-
*
|
|
874
|
+
* The seam's consumer-facing contracts: request, result, and capability types
|
|
875
|
+
* for {@link SubagentProvider}, plus the `subagent/start` and `subagent/end`
|
|
876
|
+
* payloads that plugins and hosts observe. Internal control interfaces, such
|
|
877
|
+
* as the manager host, belong with their implementation.
|
|
878
|
+
*
|
|
879
|
+
* @module @deepseek-ai/dsh-subagent/types
|
|
860
880
|
*/
|
|
861
|
-
/** Process-stable identity carried only by the standard adjacent-Agent messaging tool. */
|
|
862
|
-
const adjacentAgentSendMessageTool = Symbol.for("dsh.subagent.adjacentAgentSendMessageTool");
|
|
863
881
|
/**
|
|
864
|
-
*
|
|
865
|
-
* @param
|
|
866
|
-
* @returns
|
|
882
|
+
* Brand a string as a {@link SubagentRunId}.
|
|
883
|
+
* @param id - the raw run id.
|
|
884
|
+
* @returns the same string, branded.
|
|
867
885
|
*/
|
|
868
|
-
function
|
|
869
|
-
return
|
|
886
|
+
function SubagentRunId(id) {
|
|
887
|
+
return id;
|
|
870
888
|
}
|
|
871
|
-
/**
|
|
872
|
-
* Process-stable symbol-keyed host delivery shared by the bundled runtime
|
|
873
|
-
* entry and this unbundled internal subpath.
|
|
874
|
-
* @internal
|
|
875
|
-
*/
|
|
876
|
-
const deliverSubagentPrompt = Symbol.for("dsh.subagent.deliverPrompt");
|
|
877
889
|
//#endregion
|
|
878
890
|
//#region lib/types/structured.js
|
|
879
891
|
/**
|
|
@@ -901,7 +913,7 @@ const STRUCTURED_OUTPUT_INSTRUCTION = `When you have your final answer, you MUST
|
|
|
901
913
|
* @param childCtx - the child agent's scope context (`setup`'s argument).
|
|
902
914
|
* @param schema - the trusted, already-asserted schema subset to enforce (see
|
|
903
915
|
* `assertObjectJsonSchema` in dsh-tools).
|
|
904
|
-
* @param canComplete - whether accepted input
|
|
916
|
+
* @param canComplete - whether accepted input has been processed.
|
|
905
917
|
* @returns the attachment handle (read `captured()` after the child settles).
|
|
906
918
|
*/
|
|
907
919
|
function attachStructuredRuntime(childCtx, schema, canComplete) {
|
|
@@ -943,7 +955,7 @@ function attachStructuredRuntime(childCtx, schema, canComplete) {
|
|
|
943
955
|
execute(args, exec) {
|
|
944
956
|
const violations = validateJsonSchemaValue(schema, args);
|
|
945
957
|
if (violations.length > 0) throw new ToolArgsError(violations);
|
|
946
|
-
if (!canComplete()) throw new Error("
|
|
958
|
+
if (!canComplete()) throw new Error("Process pending messages before submitting structured output.");
|
|
947
959
|
staged.set(exec, { value: args });
|
|
948
960
|
exec.concludeTurn();
|
|
949
961
|
return Promise.resolve({ recorded: true });
|
|
@@ -1053,7 +1065,7 @@ function finalAssistantOutput(events) {
|
|
|
1053
1065
|
/** Execution-period records, capacity slots, child serialization, and local output capture.
|
|
1054
1066
|
* @module @deepseek-ai/dsh-subagent/activation
|
|
1055
1067
|
*/
|
|
1056
|
-
/** Process-local
|
|
1068
|
+
/** Process-local capacity shared by every member of a subagent tree. */
|
|
1057
1069
|
var ActivationPool = class {
|
|
1058
1070
|
slots = /* @__PURE__ */ new Set();
|
|
1059
1071
|
/**
|
|
@@ -1159,11 +1171,11 @@ var ChildLock = class {
|
|
|
1159
1171
|
/**
|
|
1160
1172
|
* Owns subagent startup, message admission, cold resume, and execution lifetimes.
|
|
1161
1173
|
* One child lock orders input acceptance and closure; one activation map owns
|
|
1162
|
-
* capacity,
|
|
1174
|
+
* capacity, runtime ownership, and resource release across all providers.
|
|
1163
1175
|
*
|
|
1164
1176
|
* @module @deepseek-ai/dsh-subagent/manager
|
|
1165
1177
|
*/
|
|
1166
|
-
var __addDisposableResource$
|
|
1178
|
+
var __addDisposableResource$1 = function(env, value, async) {
|
|
1167
1179
|
if (value !== null && value !== void 0) {
|
|
1168
1180
|
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
|
1169
1181
|
var dispose, inner;
|
|
@@ -1192,7 +1204,7 @@ var __addDisposableResource$2 = function(env, value, async) {
|
|
|
1192
1204
|
} else if (async) env.stack.push({ async: true });
|
|
1193
1205
|
return value;
|
|
1194
1206
|
};
|
|
1195
|
-
var __disposeResources$
|
|
1207
|
+
var __disposeResources$1 = (function(SuppressedError) {
|
|
1196
1208
|
return function(env) {
|
|
1197
1209
|
function fail(e) {
|
|
1198
1210
|
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
|
|
@@ -1241,18 +1253,13 @@ var SubagentManager = class {
|
|
|
1241
1253
|
locks = new ChildLock();
|
|
1242
1254
|
/** Structural Cordis owner of every Activation handle. */
|
|
1243
1255
|
ownerCtx;
|
|
1244
|
-
/**
|
|
1245
|
-
|
|
1246
|
-
* observed under each root. Entries remain until that exact root leaves the
|
|
1247
|
-
* Agent registry, closing admission throughout its host's teardown without
|
|
1248
|
-
* poisoning a later same-id replacement.
|
|
1249
|
-
*/
|
|
1250
|
-
closingScopes = /* @__PURE__ */ new Map();
|
|
1256
|
+
/** Exact subagent tree roots whose host teardown has closed admission. */
|
|
1257
|
+
closingRoots = /* @__PURE__ */ new Set();
|
|
1251
1258
|
draining = false;
|
|
1252
1259
|
/**
|
|
1253
1260
|
* Build one manager inside the service's Agent-injected context.
|
|
1254
1261
|
* @param ctx - context providing Agents, Sessions, and teardown ownership.
|
|
1255
|
-
* @param host -
|
|
1262
|
+
* @param host - descriptor reads and lifecycle publication owned by the service.
|
|
1256
1263
|
* @param maxActiveSubagents - the current configured child capacity.
|
|
1257
1264
|
* @param delegatedApproval - the configured approval policy seed for each local child.
|
|
1258
1265
|
*/
|
|
@@ -1263,8 +1270,12 @@ var SubagentManager = class {
|
|
|
1263
1270
|
this.delegatedApproval = delegatedApproval;
|
|
1264
1271
|
const scope = ctx.plugin(function activationOwner() {});
|
|
1265
1272
|
this.ownerCtx = scope.ctx;
|
|
1273
|
+
installSubagentArchiveAdmission(ctx, (agent) => {
|
|
1274
|
+
const activation = this.resident.get(agent.id);
|
|
1275
|
+
return activation !== void 0 && activationAgent(activation) === agent ? activation.ancestors : void 0;
|
|
1276
|
+
}, (session) => host.identityOf(session));
|
|
1266
1277
|
ctx.on("agent/disposed", ({ agent }) => {
|
|
1267
|
-
this.
|
|
1278
|
+
this.closingRoots.delete(agent);
|
|
1268
1279
|
});
|
|
1269
1280
|
ctx.effect(function* () {
|
|
1270
1281
|
yield scope.dispose;
|
|
@@ -1274,7 +1285,7 @@ var SubagentManager = class {
|
|
|
1274
1285
|
/**
|
|
1275
1286
|
* Interrupt one live continuable child's current turn under the supplied authority.
|
|
1276
1287
|
* @param targetSessionId - the durable child session id to interrupt.
|
|
1277
|
-
* @param authority -
|
|
1288
|
+
* @param authority - a trusted user operation or exact live ancestor Agent.
|
|
1278
1289
|
*/
|
|
1279
1290
|
interrupt(targetSessionId, authority) {
|
|
1280
1291
|
if (authority.kind === "ancestor") {
|
|
@@ -1284,9 +1295,7 @@ var SubagentManager = class {
|
|
|
1284
1295
|
}
|
|
1285
1296
|
const activation = this.resident.get(targetSessionId);
|
|
1286
1297
|
if (activation === void 0) return;
|
|
1287
|
-
if (authority.kind === "
|
|
1288
|
-
if (activation.parent.id !== authority.parentSessionId) throw new SubagentError(`subagent "${targetSessionId}" belongs to another parent session`, "UNAUTHORIZED");
|
|
1289
|
-
} else if (!activation.ancestry.has(authority.agent)) throw new SubagentError(`subagent "${targetSessionId}" is not a live descendant of agent "${authority.agent.id}"`, "UNAUTHORIZED");
|
|
1298
|
+
if (authority.kind === "ancestor" && (this.rootOf(authority.agent) !== activation.root || !activation.ancestors.includes(authority.agent.id))) throw new SubagentError(`subagent "${targetSessionId}" is not a live descendant of agent "${authority.agent.id}"`, "UNAUTHORIZED");
|
|
1290
1299
|
if (activation.closing !== void 0) return;
|
|
1291
1300
|
const kind = authority.kind === "user" ? "user" : "parent";
|
|
1292
1301
|
if (activation.kind === "local") activation.handle.agent.cancel({ kind }, { keepInbox: true });
|
|
@@ -1297,28 +1306,25 @@ var SubagentManager = class {
|
|
|
1297
1306
|
}
|
|
1298
1307
|
/**
|
|
1299
1308
|
* Close admission, await every already-admitted materialization through
|
|
1300
|
-
* publication or rollback, then dispose
|
|
1301
|
-
* child-first.
|
|
1309
|
+
* publication or rollback, then dispose every agent in the subagent tree.
|
|
1302
1310
|
*/
|
|
1303
1311
|
async drain() {
|
|
1304
1312
|
this.draining = true;
|
|
1305
1313
|
for (const pending of this.materializations) pending.controller.abort();
|
|
1306
1314
|
await Promise.all([...this.materializations].map((materialization) => materialization.settled));
|
|
1307
|
-
|
|
1308
|
-
for (const activation of this.resident.values()) for (const child of activation.ownedChildren) owned.add(child);
|
|
1309
|
-
const roots = [...this.resident.values()].filter((activation) => !owned.has(activation.childId));
|
|
1310
|
-
await this.disposeRoots(roots, "activation(s)");
|
|
1315
|
+
await this.disposeRoots([...this.resident.values()], "activation(s)");
|
|
1311
1316
|
}
|
|
1312
1317
|
/**
|
|
1313
|
-
* Join progressing
|
|
1314
|
-
* @param
|
|
1315
|
-
* @returns whether work was joined and the host must recheck
|
|
1318
|
+
* Join progressing subagent tree work for its root, leaving idle parked inboxes resident.
|
|
1319
|
+
* @param root - exact live subagent tree root; ordinary members return false.
|
|
1320
|
+
* @returns whether work was joined and the host must recheck root completion.
|
|
1316
1321
|
*/
|
|
1317
|
-
async
|
|
1322
|
+
async waitForTreeWork(root) {
|
|
1323
|
+
if (this.rootOf(root) !== root) return false;
|
|
1318
1324
|
let observed = false;
|
|
1319
1325
|
while (true) {
|
|
1320
|
-
const pending = [...this.materializations].filter((item) => item.
|
|
1321
|
-
const children = [...this.resident.values()].filter((item) =>
|
|
1326
|
+
const pending = [...this.materializations].filter((item) => item.root === root);
|
|
1327
|
+
const children = [...this.resident.values()].filter((item) => item.root === root);
|
|
1322
1328
|
observed ||= children.some((item) => item.kind === "local" && item.handle.agent.status !== "idle");
|
|
1323
1329
|
await Promise.all(children.flatMap((item) => item.kind === "local" ? [item.handle.agent.whenIdle()] : []));
|
|
1324
1330
|
const waits = pending.map((item) => item.settled);
|
|
@@ -1331,10 +1337,6 @@ var SubagentManager = class {
|
|
|
1331
1337
|
waits.push(item.handle.agent.whenIdle());
|
|
1332
1338
|
continue;
|
|
1333
1339
|
}
|
|
1334
|
-
if (item.ownedChildren.size > 0) {
|
|
1335
|
-
if ([...item.ownedChildren].some((id) => !this.resident.has(id))) waits.push(item.poke.promise);
|
|
1336
|
-
continue;
|
|
1337
|
-
}
|
|
1338
1340
|
const inbox = item.handle.agent.inbox;
|
|
1339
1341
|
if (item.failureAt === void 0 && (inbox.nextTurn.length > 0 || inbox.nextStep.length > 0)) continue;
|
|
1340
1342
|
waits.push(Promise.race([item.result.promise.catch(() => void 0), item.poke.promise]));
|
|
@@ -1344,114 +1346,73 @@ var SubagentManager = class {
|
|
|
1344
1346
|
await Promise.all(waits);
|
|
1345
1347
|
continue;
|
|
1346
1348
|
}
|
|
1347
|
-
if ([...this.materializations].some((item) => item.
|
|
1349
|
+
if ([...this.materializations].some((item) => item.root === root && !pending.includes(item)) || [...this.resident.values()].some((item) => item.root === root && !children.includes(item))) continue;
|
|
1348
1350
|
return observed;
|
|
1349
1351
|
}
|
|
1350
1352
|
}
|
|
1351
1353
|
/**
|
|
1352
|
-
* Stop the
|
|
1353
|
-
* @param parents - exact live roots whose
|
|
1354
|
+
* Stop every member of the selected subagent trees; ordinary members do not close a subagent tree.
|
|
1355
|
+
* @param parents - exact live subagent tree roots whose instances must stop.
|
|
1354
1356
|
*/
|
|
1355
1357
|
async drainDescendants(parents) {
|
|
1356
|
-
const roots = new Set(parents.filter((
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
const
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
if (owners.length === 0) continue;
|
|
1365
|
-
targets.push(activation);
|
|
1366
|
-
for (const owner of owners) {
|
|
1367
|
-
const members = this.closingMembers(owner);
|
|
1368
|
-
if (child !== void 0) members.add(child);
|
|
1369
|
-
for (const agent of lineage) members.add(agent);
|
|
1370
|
-
}
|
|
1371
|
-
}
|
|
1372
|
-
const materializations = [...this.materializations].filter((materialization) => {
|
|
1373
|
-
const owners = [...roots].filter((root) => materialization.lineage.includes(root));
|
|
1374
|
-
for (const owner of owners) {
|
|
1375
|
-
const members = this.closingMembers(owner);
|
|
1376
|
-
for (const agent of materialization.lineage) members.add(agent);
|
|
1377
|
-
}
|
|
1378
|
-
if (owners.length > 0) materialization.controller.abort();
|
|
1379
|
-
return owners.length > 0;
|
|
1380
|
-
});
|
|
1381
|
-
const ownedTargets = /* @__PURE__ */ new Set();
|
|
1382
|
-
for (const activation of targets) for (const child of activation.ownedChildren) ownedTargets.add(child);
|
|
1383
|
-
const targetRoots = targets.filter((activation) => !ownedTargets.has(activation.childId));
|
|
1384
|
-
for (const activation of targets) this.dispose(activation).catch(() => void 0);
|
|
1385
|
-
await Promise.all(materializations.map((materialization) => materialization.settled));
|
|
1386
|
-
await this.disposeRoots(targetRoots, "scoped activation(s)");
|
|
1387
|
-
}
|
|
1388
|
-
/**
|
|
1389
|
-
* Release selected resident direct children of one exact live parent.
|
|
1390
|
-
* @param parent - exact live direct parent authorizing the selected release.
|
|
1391
|
-
* @param childIds - durable direct-child ids to release when resident.
|
|
1392
|
-
*/
|
|
1393
|
-
async drainChildren(parent, childIds) {
|
|
1394
|
-
if (this.ctx.agents.get(parent.id) !== parent) throw new SubagentError("selected child teardown requires the exact live parent agent", "UNAUTHORIZED");
|
|
1395
|
-
const targets = [];
|
|
1396
|
-
for (const childId of new Set(childIds)) {
|
|
1397
|
-
const activation = this.resident.get(childId);
|
|
1398
|
-
if (activation === void 0) continue;
|
|
1399
|
-
if (activation.parent.id !== parent.id || !activation.ancestry.has(parent)) throw new SubagentError(`subagent "${childId}" is not a direct child of agent "${parent.id}"`, "UNAUTHORIZED");
|
|
1400
|
-
targets.push(activation);
|
|
1401
|
-
}
|
|
1402
|
-
for (const activation of targets) this.dispose(activation).catch(() => void 0);
|
|
1403
|
-
await this.disposeRoots(targets, "selected activation(s)");
|
|
1358
|
+
const roots = new Set(parents.filter((root) => this.ctx.agents.get(root.id) === root && this.rootOf(root) === root));
|
|
1359
|
+
for (const root of roots) this.closingRoots.add(root);
|
|
1360
|
+
const pending = [...this.materializations].filter((item) => roots.has(item.root));
|
|
1361
|
+
for (const item of pending) item.controller.abort();
|
|
1362
|
+
const targets = [...this.resident.values()].filter((item) => roots.has(item.root));
|
|
1363
|
+
const disposal = this.disposeRoots(targets, "scoped activation(s)");
|
|
1364
|
+
const [disposed] = await Promise.allSettled([disposal, ...pending.map((item) => item.settled)]);
|
|
1365
|
+
if (disposed.status === "rejected") throw disposed.reason;
|
|
1404
1366
|
}
|
|
1405
1367
|
/**
|
|
1406
1368
|
* Start one continuable background child and resolve at initial inbox acceptance.
|
|
1407
1369
|
* Every earlier failure disposes any created handle and rolls back Activation
|
|
1408
|
-
* and
|
|
1370
|
+
* and its capacity slot without returning either id.
|
|
1409
1371
|
* @param spec - provider, delegation request, and caller cancellation.
|
|
1372
|
+
* @param prepare - creation contribution bound to the checked provider.
|
|
1410
1373
|
* @returns the durable child id and accepted initial prompt message id.
|
|
1411
1374
|
*/
|
|
1412
|
-
async startLocal(spec) {
|
|
1413
|
-
const
|
|
1414
|
-
const parent = request.parent;
|
|
1375
|
+
async startLocal(spec, prepare) {
|
|
1376
|
+
const parent = spec.parent;
|
|
1415
1377
|
this.assertAdmitting(parent);
|
|
1416
|
-
const
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
const
|
|
1437
|
-
const prepared = await this.host.prepareContinuable(spec.provider, {
|
|
1378
|
+
const ancestry = this.ancestryForChild(parent);
|
|
1379
|
+
return this.trackStartup(ancestry.root, spec.signal, async (signal) => {
|
|
1380
|
+
const persistence = spec.delivery === "caller" ? this.ctx.get("sessionPersistence") : this.requirePersistence();
|
|
1381
|
+
const childId = spec.childId ?? brandString(randomUUID());
|
|
1382
|
+
this.assertChildIdAvailable(childId);
|
|
1383
|
+
const childDepth = resolveChildDepth(parent, spec.maxDepth);
|
|
1384
|
+
const agentOptions = resolveChildAgentOptions(parent, spec.agentOptions, childDepth);
|
|
1385
|
+
const agentProvider = agentOptions.provider;
|
|
1386
|
+
const agentModel = agentOptions.model;
|
|
1387
|
+
const agentReasoningEffort = agentOptions.reasoningEffort;
|
|
1388
|
+
const descriptor = snapshotSubagentDescriptor({
|
|
1389
|
+
mode: "continuable",
|
|
1390
|
+
provider: spec.provider,
|
|
1391
|
+
label: spec.label,
|
|
1392
|
+
...agentProvider !== void 0 ? { agentProvider } : {},
|
|
1393
|
+
...agentModel !== void 0 ? { agentModel } : {},
|
|
1394
|
+
...agentReasoningEffort !== void 0 ? { agentReasoningEffort } : {}
|
|
1395
|
+
});
|
|
1396
|
+
const delegatedPolicies = captureDelegatedPolicyOverrides(parent, this.delegatedApproval());
|
|
1397
|
+
const cwd = await this.resolveDirectory(spec, signal);
|
|
1398
|
+
const prepared = await prepare({
|
|
1438
1399
|
sessionId: childId,
|
|
1439
1400
|
cwd,
|
|
1440
1401
|
parent,
|
|
1441
|
-
signal
|
|
1402
|
+
signal
|
|
1442
1403
|
});
|
|
1443
|
-
|
|
1404
|
+
signal.throwIfAborted();
|
|
1444
1405
|
this.assertAdmitting(parent);
|
|
1445
1406
|
const inheritedEventCount = SessionLogOffset(prepared.seed?.length ?? 0);
|
|
1446
1407
|
const seed = prepared.seed;
|
|
1447
1408
|
let established;
|
|
1448
1409
|
const messageId = await this.locks.run(childId, async () => {
|
|
1449
|
-
|
|
1410
|
+
signal.throwIfAborted();
|
|
1450
1411
|
this.assertAdmitting(parent);
|
|
1451
1412
|
this.assertChildIdAvailable(childId);
|
|
1452
1413
|
if (spec.childId !== void 0) {
|
|
1453
|
-
const persisted = await persistence?.stat(childId, { signal
|
|
1454
|
-
|
|
1414
|
+
const persisted = await persistence?.stat(childId, { signal });
|
|
1415
|
+
signal.throwIfAborted();
|
|
1455
1416
|
this.assertAdmitting(parent);
|
|
1456
1417
|
this.assertChildIdAvailable(childId);
|
|
1457
1418
|
if (persisted !== void 0) throw new SubagentError(`subagent "${childId}" already exists`, "DUPLICATE_CHILD");
|
|
@@ -1460,29 +1421,33 @@ var SubagentManager = class {
|
|
|
1460
1421
|
kind: "local",
|
|
1461
1422
|
childId,
|
|
1462
1423
|
provider: spec.provider,
|
|
1463
|
-
|
|
1424
|
+
...ancestry,
|
|
1464
1425
|
create: {
|
|
1426
|
+
parent,
|
|
1465
1427
|
cwd,
|
|
1466
1428
|
seed,
|
|
1467
|
-
meta: childSessionMeta(parent, childDepth, prepared.seed !== void 0,
|
|
1429
|
+
meta: childSessionMeta(parent, childDepth, prepared.seed !== void 0, spec.agentPreset),
|
|
1468
1430
|
inheritedEventCount,
|
|
1469
1431
|
delegatedPolicies,
|
|
1470
1432
|
descriptor
|
|
1471
1433
|
},
|
|
1472
1434
|
agentOptions,
|
|
1473
|
-
composition: { agentPreset:
|
|
1474
|
-
signal
|
|
1435
|
+
composition: { agentPreset: spec.agentPreset },
|
|
1436
|
+
signal,
|
|
1475
1437
|
delivery: spec.delivery,
|
|
1476
|
-
outputSchema: spec.
|
|
1438
|
+
outputSchema: spec.outputSchema
|
|
1477
1439
|
});
|
|
1478
1440
|
established = activation;
|
|
1479
|
-
const
|
|
1480
|
-
|
|
1481
|
-
return await this.submitMaterialized(activation, spec.delivery !== "caller" && isAdjacentAgentSendMessageTool(this.ctx.get("tools")?.get("send_message", child)) ? withContinuableReturnGuidance(parent.id, request.prompt) : request.prompt, {
|
|
1441
|
+
const childHeader = requireLocalActivation(activation).handle.agent.session.header;
|
|
1442
|
+
return await this.submitMaterialized(activation, spec.delivery !== "caller" ? withResultDeliveryGuidance(spec.prompt) : spec.prompt, {
|
|
1482
1443
|
source: { kind: "user" },
|
|
1483
|
-
signal
|
|
1484
|
-
delivery: "queue"
|
|
1485
|
-
|
|
1444
|
+
signal,
|
|
1445
|
+
delivery: "queue",
|
|
1446
|
+
authorize: () => {
|
|
1447
|
+
this.assertAdmitting(parent);
|
|
1448
|
+
}
|
|
1449
|
+
}, ancestry.root, () => {
|
|
1450
|
+
this.assertAdmitting(parent);
|
|
1486
1451
|
establishCatalogChild(parent.session, childHeader, descriptor);
|
|
1487
1452
|
});
|
|
1488
1453
|
});
|
|
@@ -1491,54 +1456,49 @@ var SubagentManager = class {
|
|
|
1491
1456
|
childId,
|
|
1492
1457
|
messageId
|
|
1493
1458
|
};
|
|
1494
|
-
}
|
|
1495
|
-
releaseHold();
|
|
1496
|
-
throw error;
|
|
1497
|
-
}
|
|
1459
|
+
});
|
|
1498
1460
|
}
|
|
1499
1461
|
/**
|
|
1500
1462
|
* Start an external backend under activation ownership.
|
|
1501
1463
|
* @param spec - task, provider, cancellation, and delivery policy.
|
|
1464
|
+
* @param start - execution bound to the checked provider.
|
|
1502
1465
|
* @returns the accepted activation and its result/disposal capabilities.
|
|
1503
1466
|
*/
|
|
1504
|
-
async startExternal(spec) {
|
|
1505
|
-
const
|
|
1467
|
+
async startExternal(spec, start) {
|
|
1468
|
+
const { provider, delivery, childId: _childId, ...request } = spec;
|
|
1469
|
+
const parent = request.parent;
|
|
1506
1470
|
this.assertAdmitting(parent);
|
|
1507
|
-
const
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
this.ctx.logger.warn(`subagent "${activation.childId}" admission rollback failed: ${failureMessage(cleanupError)}`);
|
|
1471
|
+
const ancestry = this.ancestryForChild(parent);
|
|
1472
|
+
return this.trackStartup(ancestry.root, spec.signal, async (signal) => {
|
|
1473
|
+
let activation;
|
|
1474
|
+
try {
|
|
1475
|
+
const cwd = await this.resolveDirectory(spec, signal);
|
|
1476
|
+
activation = await this.materialize({
|
|
1477
|
+
kind: "external",
|
|
1478
|
+
provider,
|
|
1479
|
+
...ancestry,
|
|
1480
|
+
signal,
|
|
1481
|
+
delivery,
|
|
1482
|
+
start: (signal) => start({
|
|
1483
|
+
...request,
|
|
1484
|
+
cwd,
|
|
1485
|
+
signal
|
|
1486
|
+
})
|
|
1487
|
+
});
|
|
1488
|
+
this.assertAdmitting(parent);
|
|
1489
|
+
signal.throwIfAborted();
|
|
1490
|
+
if (delivery === "parent") establishExternalCatalogChild(parent.session, activation.childId, spec.label);
|
|
1491
|
+
this.accept(activation);
|
|
1492
|
+
return this.receipt(activation);
|
|
1493
|
+
} catch (error) {
|
|
1494
|
+
if (activation !== void 0) try {
|
|
1495
|
+
await this.dispose(activation);
|
|
1496
|
+
} catch (cleanupError) {
|
|
1497
|
+
this.ctx.logger.warn(`subagent "${activation.childId}" admission rollback failed: ${failureMessage(cleanupError)}`);
|
|
1498
|
+
}
|
|
1499
|
+
throw error;
|
|
1537
1500
|
}
|
|
1538
|
-
|
|
1539
|
-
} finally {
|
|
1540
|
-
releaseHold();
|
|
1541
|
-
}
|
|
1501
|
+
});
|
|
1542
1502
|
}
|
|
1543
1503
|
/** Capture the explicit or inherited directory while the parent still owns startup. */
|
|
1544
1504
|
async resolveDirectory(request, signal) {
|
|
@@ -1547,60 +1507,125 @@ var SubagentManager = class {
|
|
|
1547
1507
|
return cwd;
|
|
1548
1508
|
}
|
|
1549
1509
|
/**
|
|
1550
|
-
*
|
|
1551
|
-
*
|
|
1552
|
-
*
|
|
1553
|
-
* @
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1510
|
+
* Resolve the live subagent tree root from runtime ownership, independent of creator residency.
|
|
1511
|
+
* @param caller - exact live Agent selecting the subagent tree.
|
|
1512
|
+
* @returns the live subagent tree root.
|
|
1513
|
+
* @throws when the caller has no active runtime owner.
|
|
1514
|
+
*/
|
|
1515
|
+
rootOf(caller) {
|
|
1516
|
+
if (this.ctx.agents.get(caller.id) !== caller) throw new SubagentError("subagent tree access requires the exact live caller", "UNAUTHORIZED");
|
|
1517
|
+
const identity = this.host.identityOf(caller.session);
|
|
1518
|
+
const delegated = caller.session.header.origin === "subagent" || identity != null && caller.session.isOwnSeq(identity.seq);
|
|
1519
|
+
const activation = this.resident.get(caller.id);
|
|
1520
|
+
if (activation !== void 0 && activationAgent(activation) === caller && this.ctx.agents.get(activation.root.id) === activation.root) return activation.root;
|
|
1521
|
+
if (delegated || activation !== void 0) throw new SubagentError("agent has no active runtime owner", "UNAUTHORIZED");
|
|
1522
|
+
return caller;
|
|
1523
|
+
}
|
|
1524
|
+
/** Capture creation ancestry separately from the root that owns the new instance. */
|
|
1525
|
+
ancestryForChild(creator) {
|
|
1526
|
+
return {
|
|
1527
|
+
root: this.rootOf(creator),
|
|
1528
|
+
ancestors: [...this.resident.get(creator.id)?.ancestors ?? [], creator.id]
|
|
1529
|
+
};
|
|
1530
|
+
}
|
|
1531
|
+
/**
|
|
1532
|
+
* Deliver to another agent in the subagent tree, restoring only the target when absent.
|
|
1533
|
+
* @param sender - exact live root or resident continuable Agent.
|
|
1534
|
+
* @param targetId - another member's durable Session id.
|
|
1535
|
+
* @param content - model-authored message blocks.
|
|
1536
|
+
* @param options - cancellation before inbox acceptance.
|
|
1537
|
+
* @returns the accepted inbox message id.
|
|
1558
1538
|
*/
|
|
1559
1539
|
async sendMessage(sender, targetId, content, options) {
|
|
1560
|
-
|
|
1540
|
+
options.signal.throwIfAborted();
|
|
1541
|
+
const root = this.rootOf(sender);
|
|
1542
|
+
if (sender.id === targetId) throw new SubagentError("send_message requires another agent in the subagent tree", "UNAUTHORIZED");
|
|
1561
1543
|
this.assertAdmitting(sender);
|
|
1562
|
-
const
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
return this.sendToParent(senderActivation, sender, content);
|
|
1566
|
-
}
|
|
1567
|
-
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");
|
|
1568
|
-
return this.deliverToChild(sender, targetId, content, {
|
|
1544
|
+
const message = createAgentMessage(sender, content);
|
|
1545
|
+
const activation = this.resident.get(sender.id);
|
|
1546
|
+
return this.deliverToMember(root, targetId, [...message.content], {
|
|
1569
1547
|
signal: options.signal,
|
|
1570
|
-
delivery: "steer"
|
|
1548
|
+
delivery: "steer",
|
|
1549
|
+
source: message.source,
|
|
1550
|
+
authorize: () => {
|
|
1551
|
+
this.assertAdmitting(sender);
|
|
1552
|
+
},
|
|
1553
|
+
onAccepted: () => {
|
|
1554
|
+
if (activation?.kind === "local" && activation.ancestors.at(-1) === targetId) activation.omitClosingText = true;
|
|
1555
|
+
}
|
|
1571
1556
|
});
|
|
1572
1557
|
}
|
|
1573
|
-
/**
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1558
|
+
/** Resolve the live owner through verified creation edges without activating ancestors. */
|
|
1559
|
+
async resolveCreationAncestry(source, signal, root) {
|
|
1560
|
+
const ancestors = [];
|
|
1561
|
+
const seen = new Set([source.header.id]);
|
|
1562
|
+
let { header, projections, inheritedEventCount } = source;
|
|
1563
|
+
while (true) {
|
|
1564
|
+
const env_1 = {
|
|
1565
|
+
stack: [],
|
|
1566
|
+
error: void 0,
|
|
1567
|
+
hasError: false
|
|
1568
|
+
};
|
|
1569
|
+
try {
|
|
1570
|
+
signal.throwIfAborted();
|
|
1571
|
+
const parentId = header.parentSession;
|
|
1572
|
+
const identity = projections?.values.subagent;
|
|
1573
|
+
if (parentId === void 0 || identity == null || identity.seq < inheritedEventCount) break;
|
|
1574
|
+
if (seen.has(parentId)) throw new SubagentError("cyclic subagent creation ancestry", "UNAUTHORIZED");
|
|
1575
|
+
seen.add(parentId);
|
|
1576
|
+
const parent = __addDisposableResource$1(env_1, await this.observeContinuation(parentId, signal), false);
|
|
1577
|
+
if (!parent.projections?.values.subagentCatalog?.some((entry) => entry.id === header.id)) throw new SubagentError("subagent is absent from its creator catalog", "UNAUTHORIZED");
|
|
1578
|
+
ancestors.unshift(parentId);
|
|
1579
|
+
if (parentId === root?.id) return {
|
|
1580
|
+
root,
|
|
1581
|
+
ancestors
|
|
1582
|
+
};
|
|
1583
|
+
({header, projections, inheritedEventCount} = parent);
|
|
1584
|
+
} catch (e_1) {
|
|
1585
|
+
env_1.error = e_1;
|
|
1586
|
+
env_1.hasError = true;
|
|
1587
|
+
} finally {
|
|
1588
|
+
__disposeResources$1(env_1);
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
if (root === void 0 && ancestors.length > 0 && header.origin !== "subagent") {
|
|
1592
|
+
const owner = this.ctx.agents.get(header.id);
|
|
1593
|
+
if (owner === void 0 || this.rootOf(owner) !== owner) throw new SubagentError("subagent tree root is not live", "CONTINUATION_UNAVAILABLE");
|
|
1594
|
+
return {
|
|
1595
|
+
root: owner,
|
|
1596
|
+
ancestors
|
|
1597
|
+
};
|
|
1598
|
+
}
|
|
1599
|
+
throw new SubagentError(`agent "${source.header.id}" is not a member of this subagent tree`, "UNAUTHORIZED");
|
|
1588
1600
|
}
|
|
1589
1601
|
/**
|
|
1590
|
-
*
|
|
1591
|
-
*
|
|
1592
|
-
* @param childId - durable
|
|
1602
|
+
* Deliver one host-authored prompt through the target's live subagent tree root.
|
|
1603
|
+
* Remote callers must be authorized for the target Session before invoking this operation.
|
|
1604
|
+
* @param childId - durable continuable subagent session id.
|
|
1593
1605
|
* @param content - model-visible prompt blocks.
|
|
1594
|
-
* @param
|
|
1595
|
-
* @param signal - caller cancellation before inbox acceptance.
|
|
1606
|
+
* @param options - inbox destination, durable attribution, and cancellation before acceptance.
|
|
1596
1607
|
* @returns the accepted durable message id.
|
|
1597
1608
|
*/
|
|
1598
|
-
async
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1609
|
+
async deliverPrompt(childId, content, options) {
|
|
1610
|
+
options.signal.throwIfAborted();
|
|
1611
|
+
let root = this.resident.get(childId)?.root;
|
|
1612
|
+
if (root === void 0) {
|
|
1613
|
+
const env_2 = {
|
|
1614
|
+
stack: [],
|
|
1615
|
+
error: void 0,
|
|
1616
|
+
hasError: false
|
|
1617
|
+
};
|
|
1618
|
+
try {
|
|
1619
|
+
const source = __addDisposableResource$1(env_2, await this.observeContinuation(childId, options.signal), false);
|
|
1620
|
+
({root} = await this.resolveCreationAncestry(source, options.signal));
|
|
1621
|
+
} catch (e_2) {
|
|
1622
|
+
env_2.error = e_2;
|
|
1623
|
+
env_2.hasError = true;
|
|
1624
|
+
} finally {
|
|
1625
|
+
__disposeResources$1(env_2);
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
return this.deliverToMember(root, childId, content, options);
|
|
1604
1629
|
}
|
|
1605
1630
|
/** Admit local input synchronously before any teardown can begin. */
|
|
1606
1631
|
deliver(activation, message, delivery) {
|
|
@@ -1618,32 +1643,6 @@ var SubagentManager = class {
|
|
|
1618
1643
|
if (this.resident.has(childId) || this.ctx.agents.get(childId) !== void 0 || this.ctx.get("sessions")?.get(childId) !== void 0) throw new SubagentError(`subagent "${childId}" already exists`, "DUPLICATE_CHILD");
|
|
1619
1644
|
}
|
|
1620
1645
|
/**
|
|
1621
|
-
* Pre-register `childId` in a continuation-managed parent's owned set so the
|
|
1622
|
-
* parent cannot settle while a caller is still establishing or resuming that
|
|
1623
|
-
* child. Returns a releaser for the failure path; it removes only a hold
|
|
1624
|
-
* this call added, and leaves ownership in place once a live Activation for
|
|
1625
|
-
* the child exists.
|
|
1626
|
-
* @param parent - the live direct parent the operation is admitted under.
|
|
1627
|
-
* @param childId - the durable child the operation addresses.
|
|
1628
|
-
* @returns the failure-path releaser; a no-op when nothing was added.
|
|
1629
|
-
*/
|
|
1630
|
-
holdOwnership(parent, childId) {
|
|
1631
|
-
const parentActivation = this.resident.get(parent.id);
|
|
1632
|
-
if (parentActivation === void 0 || activationAgent(parentActivation) !== parent) return () => {};
|
|
1633
|
-
if (parentActivation.closing !== void 0) throw new SubagentError(`subagent parent "${parent.id}" is being disposed; the child was not established`, "ACTIVATION_CLOSING");
|
|
1634
|
-
if (parentActivation.ownedChildren.has(childId)) return () => {};
|
|
1635
|
-
parentActivation.ownedChildren.add(childId);
|
|
1636
|
-
return () => {
|
|
1637
|
-
const live = this.resident.get(childId);
|
|
1638
|
-
/* v8 ignore next 4 -- reaching this arm needs another delivery to establish the child
|
|
1639
|
-
* between this operation's failure and its releaser running, which no test can schedule
|
|
1640
|
-
* deterministically: the ownership edge then belongs to that live Activation, so the
|
|
1641
|
-
* conservative keep leaves it for finishDisposal's releaseOwnership. */
|
|
1642
|
-
if (live !== void 0 && live.closing === void 0) return;
|
|
1643
|
-
if (parentActivation.ownedChildren.delete(childId)) this.wake(parentActivation);
|
|
1644
|
-
};
|
|
1645
|
-
}
|
|
1646
|
-
/**
|
|
1647
1646
|
* Apply activation admission to a resident parent before delivering input.
|
|
1648
1647
|
* @param parent - exact live Agent receiving the message.
|
|
1649
1648
|
* @param message - durable user message to deliver.
|
|
@@ -1663,27 +1662,16 @@ var SubagentManager = class {
|
|
|
1663
1662
|
else parent.followup(message);
|
|
1664
1663
|
}
|
|
1665
1664
|
/**
|
|
1666
|
-
* Reject new
|
|
1667
|
-
* @param agent - exact live
|
|
1665
|
+
* Reject new work from closing members or subagent trees.
|
|
1666
|
+
* @param agent - exact live caller whose instance and subagent tree must admit work.
|
|
1668
1667
|
*/
|
|
1669
1668
|
assertAdmitting(agent) {
|
|
1670
1669
|
const closing = this.closingTeardownFor(agent);
|
|
1671
|
-
if (closing === void 0)
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
* @param parent - exact live Agent claiming direct-parent authority.
|
|
1677
|
-
* @param childId - durable child session id addressed by the operation.
|
|
1678
|
-
* @param parentSession - durable direct-parent id recorded by the child.
|
|
1679
|
-
*/
|
|
1680
|
-
authorizeLineage(parent, childId, parentSession) {
|
|
1681
|
-
this.assertLiveParent(parent, childId);
|
|
1682
|
-
if (parentSession !== parent.id) throw new SubagentError(`subagent "${childId}" belongs to another parent session`, "UNAUTHORIZED");
|
|
1683
|
-
}
|
|
1684
|
-
/** Reject parent authority after the live registry entry changes. */
|
|
1685
|
-
assertLiveParent(parent, childId) {
|
|
1686
|
-
if (this.ctx.agents.get(parent.id) !== parent) throw new SubagentError(`subagent "${childId}" delivery requires the exact live parent agent`, "UNAUTHORIZED");
|
|
1670
|
+
if (closing === void 0) {
|
|
1671
|
+
if (this.resident.get(agent.id)?.closing !== void 0) throw new SubagentError(`subagent "${agent.id}" is closing`, "ACTIVATION_CLOSING");
|
|
1672
|
+
return;
|
|
1673
|
+
}
|
|
1674
|
+
throw new SubagentError(closing === "manager" ? "subagents are draining; the operation was not admitted" : `subagent tree "${closing.id}" is draining; the operation was not admitted`, "DRAINING");
|
|
1687
1675
|
}
|
|
1688
1676
|
/**
|
|
1689
1677
|
* Create or resume one child Agent and publish its Activation.
|
|
@@ -1691,25 +1679,28 @@ var SubagentManager = class {
|
|
|
1691
1679
|
* @returns the published process-local Activation.
|
|
1692
1680
|
*/
|
|
1693
1681
|
materialize(inputs) {
|
|
1694
|
-
this.assertAdmitting(inputs.
|
|
1682
|
+
this.assertAdmitting(inputs.root);
|
|
1695
1683
|
inputs.signal.throwIfAborted();
|
|
1696
|
-
const
|
|
1697
|
-
|
|
1698
|
-
|
|
1684
|
+
const releaseSlot = this.rootPool(inputs.root).reserve(this.maxActiveSubagents());
|
|
1685
|
+
return this.materializeTracked(inputs, releaseSlot).catch((error) => {
|
|
1686
|
+
releaseSlot();
|
|
1687
|
+
throw error;
|
|
1688
|
+
});
|
|
1689
|
+
}
|
|
1690
|
+
/** Keep subagent tree shutdown and host waits joined to preparation, publication, and rollback. */
|
|
1691
|
+
trackStartup(root, signal, start) {
|
|
1692
|
+
this.assertAdmitting(root);
|
|
1693
|
+
signal.throwIfAborted();
|
|
1699
1694
|
const settled = Promise.withResolvers();
|
|
1700
1695
|
const controller = new AbortController();
|
|
1701
1696
|
const materialization = {
|
|
1702
|
-
|
|
1697
|
+
root,
|
|
1703
1698
|
settled: settled.promise,
|
|
1704
1699
|
controller
|
|
1705
1700
|
};
|
|
1706
1701
|
this.materializations.add(materialization);
|
|
1707
|
-
return
|
|
1708
|
-
|
|
1709
|
-
signal: AbortSignal.any([inputs.signal, controller.signal])
|
|
1710
|
-
}, lineage, pool, releaseSlot).catch((error) => {
|
|
1711
|
-
releaseSlot();
|
|
1712
|
-
if (error instanceof Error && error.name === "AbortError") this.assertAdmitting(inputs.parent);
|
|
1702
|
+
return start(AbortSignal.any([signal, controller.signal])).catch((error) => {
|
|
1703
|
+
if (error instanceof Error && error.name === "AbortError") this.assertAdmitting(root);
|
|
1713
1704
|
throw error;
|
|
1714
1705
|
}).finally(() => {
|
|
1715
1706
|
this.materializations.delete(materialization);
|
|
@@ -1744,35 +1735,13 @@ var SubagentManager = class {
|
|
|
1744
1735
|
}))).filter((failure) => failure !== void 0);
|
|
1745
1736
|
if (reasons.length > 0) throw new SubagentError(`subagent teardown failed for ${reasons.length} ${failureSubject}: ` + reasons.map((reason) => failureMessage(reason)).join("; "), "ACTIVATION_TEARDOWN_FAILED");
|
|
1746
1737
|
}
|
|
1747
|
-
/** Return the
|
|
1748
|
-
closingMembers(root) {
|
|
1749
|
-
const existing = this.closingScopes.get(root);
|
|
1750
|
-
if (existing !== void 0) return existing;
|
|
1751
|
-
const members = /* @__PURE__ */ new Set();
|
|
1752
|
-
this.closingScopes.set(root, members);
|
|
1753
|
-
return members;
|
|
1754
|
-
}
|
|
1755
|
-
/** Return the exact currently resolvable ancestry from `agent` upward. */
|
|
1756
|
-
liveLineage(agent) {
|
|
1757
|
-
const lineage = [agent];
|
|
1758
|
-
const seen = new Set([agent.id]);
|
|
1759
|
-
let parentSession = agent.session.header.parentSession;
|
|
1760
|
-
while (parentSession !== void 0) {
|
|
1761
|
-
const parent = this.ctx.agents.get(parentSession);
|
|
1762
|
-
if (parent === void 0 || seen.has(parent.id)) break;
|
|
1763
|
-
lineage.push(parent);
|
|
1764
|
-
seen.add(parent.id);
|
|
1765
|
-
parentSession = parent.session.header.parentSession;
|
|
1766
|
-
}
|
|
1767
|
-
return lineage;
|
|
1768
|
-
}
|
|
1769
|
-
/** Return the teardown that closed continuable admission for this agent's lineage. */
|
|
1738
|
+
/** Return the subagent tree teardown that closed admission for this exact Agent. */
|
|
1770
1739
|
closingTeardownFor(agent) {
|
|
1771
1740
|
if (this.draining) return "manager";
|
|
1772
|
-
const
|
|
1773
|
-
|
|
1741
|
+
const root = this.rootOf(agent);
|
|
1742
|
+
return this.closingRoots.has(root) ? root : void 0;
|
|
1774
1743
|
}
|
|
1775
|
-
/**
|
|
1744
|
+
/** Share one capacity pool across every instance in a subagent tree. */
|
|
1776
1745
|
rootPool(parent) {
|
|
1777
1746
|
let pool = this.rootPools.get(parent);
|
|
1778
1747
|
if (pool === void 0) {
|
|
@@ -1782,9 +1751,8 @@ var SubagentManager = class {
|
|
|
1782
1751
|
return pool;
|
|
1783
1752
|
}
|
|
1784
1753
|
/** Perform one tracked materialization through publication or rollback. */
|
|
1785
|
-
async materializeTracked(inputs,
|
|
1786
|
-
const { provider,
|
|
1787
|
-
let childId = inputs.childId;
|
|
1754
|
+
async materializeTracked(inputs, releaseSlot) {
|
|
1755
|
+
const { provider, root } = inputs;
|
|
1788
1756
|
inputs.signal.throwIfAborted();
|
|
1789
1757
|
let resource;
|
|
1790
1758
|
if (inputs.kind === "external") {
|
|
@@ -1801,9 +1769,8 @@ var SubagentManager = class {
|
|
|
1801
1769
|
controller
|
|
1802
1770
|
};
|
|
1803
1771
|
run.result.catch(() => void 0);
|
|
1804
|
-
childId = run.id;
|
|
1805
1772
|
try {
|
|
1806
|
-
this.assertChildIdAvailable(
|
|
1773
|
+
this.assertChildIdAvailable(run.id);
|
|
1807
1774
|
} catch (error) {
|
|
1808
1775
|
await run.dispose();
|
|
1809
1776
|
throw error;
|
|
@@ -1812,11 +1779,11 @@ var SubagentManager = class {
|
|
|
1812
1779
|
inputs.signal.removeEventListener("abort", cancelStartup);
|
|
1813
1780
|
}
|
|
1814
1781
|
} else {
|
|
1815
|
-
const { create } = inputs;
|
|
1782
|
+
const { create, childId } = inputs;
|
|
1816
1783
|
let structured;
|
|
1817
1784
|
const setup = async (childCtx, child) => {
|
|
1818
1785
|
const approvalPolicy = create?.delegatedPolicies.approvalPolicy ?? childCtx.get("approval")?.effectivePolicy(child.session);
|
|
1819
|
-
if (inputs.composition.agentPreset !== void 0) await applyChildComposition(childCtx, parent, inputs.composition, approvalPolicy);
|
|
1786
|
+
if (inputs.composition.agentPreset !== void 0) await applyChildComposition(childCtx, create?.parent, inputs.composition, approvalPolicy);
|
|
1820
1787
|
if (create !== void 0) {
|
|
1821
1788
|
if (inputs.composition.agentPreset !== void 0) child.session.append("agent-preset/selected", { agentPreset: inputs.composition.agentPreset });
|
|
1822
1789
|
child.session.append("subagent/descriptor", create.descriptor);
|
|
@@ -1825,18 +1792,16 @@ var SubagentManager = class {
|
|
|
1825
1792
|
if (workingDirectory === void 0) throw new Error("local subagents require the working-directory service");
|
|
1826
1793
|
await workingDirectory.set(child, create.cwd, inputs.signal);
|
|
1827
1794
|
}
|
|
1828
|
-
if (inputs.composition.agentPreset === void 0) await applyChildComposition(childCtx, parent, inputs.composition, approvalPolicy);
|
|
1829
|
-
if (inputs.outputSchema !== void 0) structured = attachStructuredRuntime(childCtx, inputs.outputSchema, () =>
|
|
1795
|
+
if (inputs.composition.agentPreset === void 0) await applyChildComposition(childCtx, create?.parent, inputs.composition, approvalPolicy);
|
|
1796
|
+
if (inputs.outputSchema !== void 0) structured = attachStructuredRuntime(childCtx, inputs.outputSchema, () => child.inbox.nextStep.length === 0 && child.inbox.nextTurn.length === 0);
|
|
1830
1797
|
};
|
|
1831
1798
|
const handle = create === void 0 ? await this.ownerCtx.agents.resume({
|
|
1832
1799
|
resumeSessionId: childId,
|
|
1833
|
-
parentAgent: parent,
|
|
1834
1800
|
agentOptions: inputs.agentOptions,
|
|
1835
1801
|
signal: inputs.signal,
|
|
1836
1802
|
setup
|
|
1837
1803
|
}) : await this.ownerCtx.agents.create({
|
|
1838
1804
|
sessionId: childId,
|
|
1839
|
-
parentAgent: parent,
|
|
1840
1805
|
meta: create.meta,
|
|
1841
1806
|
...create.seed === void 0 ? {} : { seed: create.seed },
|
|
1842
1807
|
inheritedEventCount: create.inheritedEventCount,
|
|
@@ -1854,28 +1819,25 @@ var SubagentManager = class {
|
|
|
1854
1819
|
omitClosingText: false
|
|
1855
1820
|
};
|
|
1856
1821
|
}
|
|
1857
|
-
const
|
|
1822
|
+
const childId = resource.kind === "local" ? resource.handle.agent.id : resource.run.id;
|
|
1858
1823
|
const activation = {
|
|
1859
|
-
pool,
|
|
1860
1824
|
releaseSlot,
|
|
1861
1825
|
childId,
|
|
1826
|
+
runId: SubagentRunId(randomUUID()),
|
|
1827
|
+
provider,
|
|
1862
1828
|
...resource,
|
|
1863
|
-
|
|
1829
|
+
root,
|
|
1830
|
+
ancestors: inputs.ancestors,
|
|
1864
1831
|
delivery: inputs.delivery ?? "parent",
|
|
1865
1832
|
result: Promise.withResolvers(),
|
|
1866
1833
|
closing: void 0,
|
|
1867
|
-
|
|
1868
|
-
ownedChildren: /* @__PURE__ */ new Set(),
|
|
1869
|
-
observer,
|
|
1870
|
-
announced: false
|
|
1834
|
+
accepted: false
|
|
1871
1835
|
};
|
|
1872
1836
|
activation.result.promise.catch(() => void 0);
|
|
1873
1837
|
this.resident.set(childId, activation);
|
|
1874
1838
|
try {
|
|
1875
|
-
this.assertAdmitting(
|
|
1839
|
+
this.assertAdmitting(root);
|
|
1876
1840
|
inputs.signal.throwIfAborted();
|
|
1877
|
-
if (this.ctx.agents.get(parent.id) !== parent) throw new SubagentError("subagent parent is no longer live", "UNAUTHORIZED");
|
|
1878
|
-
this.acquireOwnership(parent, childId);
|
|
1879
1841
|
const wakeSettlement = () => {
|
|
1880
1842
|
this.wake(activation);
|
|
1881
1843
|
};
|
|
@@ -1891,7 +1853,7 @@ var SubagentManager = class {
|
|
|
1891
1853
|
if (event.type === "tool/call") activation.omitClosingText = false;
|
|
1892
1854
|
});
|
|
1893
1855
|
}
|
|
1894
|
-
|
|
1856
|
+
this.host.emitLifecycle("subagent/start", this.runInfo(activation), root);
|
|
1895
1857
|
} catch (error) {
|
|
1896
1858
|
/* v8 ignore next -- rollback failure must not mask the admission failure
|
|
1897
1859
|
* that prevented this operation from returning an accepted message id. */
|
|
@@ -1900,13 +1862,19 @@ var SubagentManager = class {
|
|
|
1900
1862
|
}
|
|
1901
1863
|
return activation;
|
|
1902
1864
|
}
|
|
1903
|
-
/**
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1865
|
+
/** Identity shared by one residency epoch's start/end pair, including cold resumes. */
|
|
1866
|
+
runInfo(activation) {
|
|
1867
|
+
return {
|
|
1868
|
+
runId: activation.runId,
|
|
1869
|
+
provider: activation.provider,
|
|
1870
|
+
parentSessionId: activation.ancestors.at(-1),
|
|
1871
|
+
id: activation.childId,
|
|
1872
|
+
local: activation.kind === "local"
|
|
1873
|
+
};
|
|
1874
|
+
}
|
|
1875
|
+
/** Record acceptance and begin settlement observation before returning the receipt. */
|
|
1876
|
+
accept(activation) {
|
|
1877
|
+
activation.accepted = true;
|
|
1910
1878
|
if (activation.kind === "local") this.watchLocalSettlement(activation);
|
|
1911
1879
|
else {
|
|
1912
1880
|
const finish = () => this.close(activation, () => this.finishDisposal(activation, false));
|
|
@@ -1923,23 +1891,10 @@ var SubagentManager = class {
|
|
|
1923
1891
|
} finally {
|
|
1924
1892
|
this.resident.delete(activation.childId);
|
|
1925
1893
|
activation.releaseSlot();
|
|
1926
|
-
this.releaseOwnership(activation.childId);
|
|
1927
1894
|
activation.result.reject(error);
|
|
1928
1895
|
}
|
|
1929
1896
|
});
|
|
1930
1897
|
}
|
|
1931
|
-
/** Register the child in a continuation-managed parent's owned set. */
|
|
1932
|
-
acquireOwnership(parent, childId) {
|
|
1933
|
-
const parentActivation = this.resident.get(parent.id);
|
|
1934
|
-
if (parentActivation === void 0) return;
|
|
1935
|
-
if (parentActivation.closing !== void 0) throw new SubagentError(`subagent parent "${parent.id}" is being disposed; the child was not established`, "ACTIVATION_CLOSING");
|
|
1936
|
-
parentActivation.ownedChildren.add(childId);
|
|
1937
|
-
this.wake(parentActivation);
|
|
1938
|
-
}
|
|
1939
|
-
/** Remove one child from its live owner's set and let that owner re-check settlement. */
|
|
1940
|
-
releaseOwnership(childId) {
|
|
1941
|
-
for (const candidate of this.resident.values()) if (candidate.ownedChildren.delete(childId)) this.wake(candidate);
|
|
1942
|
-
}
|
|
1943
1898
|
/** Let a settlement watcher re-check residency after relevant state changes. */
|
|
1944
1899
|
wake(activation) {
|
|
1945
1900
|
if (activation.kind === "external") return;
|
|
@@ -1994,16 +1949,15 @@ var SubagentManager = class {
|
|
|
1994
1949
|
reportTeardownFailure(activation, error) {
|
|
1995
1950
|
this.ctx.logger.warn(`subagent "${activation.childId}" activation teardown failed: ${failureMessage(error)}`);
|
|
1996
1951
|
}
|
|
1997
|
-
/** Check pending input
|
|
1952
|
+
/** Check pending input before closing admission. */
|
|
1998
1953
|
settlementState(activation, observation) {
|
|
1999
1954
|
if (activation.closing !== void 0) return "closed";
|
|
2000
1955
|
if (activation.poke !== observation) return "retry";
|
|
2001
1956
|
const inbox = activation.handle.agent.inbox;
|
|
2002
|
-
if (activation.ownedChildren.size > 0) return "wait";
|
|
2003
1957
|
if (activation.failureAt === void 0 && (inbox.nextTurn.length > 0 || inbox.nextStep.length > 0)) return "wait";
|
|
2004
1958
|
return "ready";
|
|
2005
1959
|
}
|
|
2006
|
-
/**
|
|
1960
|
+
/** Stop and release one instance without affecting its creation descendants. */
|
|
2007
1961
|
async finishDisposal(activation, stop) {
|
|
2008
1962
|
this.wake(activation);
|
|
2009
1963
|
const { childId } = activation;
|
|
@@ -2027,22 +1981,7 @@ var SubagentManager = class {
|
|
|
2027
1981
|
} else {
|
|
2028
1982
|
if (stop) {
|
|
2029
1983
|
activation.handle.agent.cancel({ kind: "parent" });
|
|
2030
|
-
|
|
2031
|
-
const children = [...activation.ownedChildren].map((child) => this.resident.get(child)).filter((child) => child !== void 0);
|
|
2032
|
-
const pending = [...this.materializations].filter((item) => item.lineage.includes(activation.handle.agent));
|
|
2033
|
-
const childDisposals = children.map((child) => this.dispose(child));
|
|
2034
|
-
for (const item of pending) item.controller.abort();
|
|
2035
|
-
childDisposals.push(...pending.map((item) => item.settled));
|
|
2036
|
-
const reasons = (await Promise.all(childDisposals.map(async (disposal) => {
|
|
2037
|
-
try {
|
|
2038
|
-
await disposal;
|
|
2039
|
-
return;
|
|
2040
|
-
} catch (error) {
|
|
2041
|
-
return error;
|
|
2042
|
-
}
|
|
2043
|
-
}))).filter((reason) => reason !== void 0);
|
|
2044
|
-
if (reasons.length > 0) failures.push(new SubagentError(`subagent "${childId}" child teardown failed: ${reasons.map((reason) => failureMessage(reason)).join("; ")}`, "ACTIVATION_TEARDOWN_FAILED"));
|
|
2045
|
-
await idle;
|
|
1984
|
+
await activation.handle.agent.whenIdle();
|
|
2046
1985
|
await this.flushFinalState(activation);
|
|
2047
1986
|
}
|
|
2048
1987
|
result = captureLocalResult(activation);
|
|
@@ -2067,18 +2006,22 @@ var SubagentManager = class {
|
|
|
2067
2006
|
stopReason: "error"
|
|
2068
2007
|
};
|
|
2069
2008
|
this.notifySettlement(activation, result);
|
|
2070
|
-
this.
|
|
2071
|
-
|
|
2009
|
+
this.host.emitLifecycle("subagent/end", {
|
|
2010
|
+
...this.runInfo(activation),
|
|
2011
|
+
stopReason: terminal.stopReason,
|
|
2012
|
+
...terminal.output.length === 0 ? {} : { lastAssistantMessage: terminal.output }
|
|
2013
|
+
}, activation.root);
|
|
2072
2014
|
if (resultFailure === void 0) activation.result.resolve(result);
|
|
2073
2015
|
else activation.result.reject(resultFailure.error);
|
|
2074
2016
|
if (failure !== void 0) throw failure;
|
|
2075
2017
|
}
|
|
2076
2018
|
/** Deliver the captured execution result to the durable direct parent after cleanup. */
|
|
2077
2019
|
notifySettlement(activation, terminal) {
|
|
2078
|
-
if (!activation.
|
|
2020
|
+
if (!activation.accepted || activation.delivery === "caller") return;
|
|
2079
2021
|
try {
|
|
2080
|
-
const
|
|
2081
|
-
|
|
2022
|
+
const parentId = activation.ancestors.at(-1);
|
|
2023
|
+
const parent = this.ctx.agents.get(parentId);
|
|
2024
|
+
if (parent === void 0 || this.rootOf(parent) !== activation.root) return;
|
|
2082
2025
|
const message = createSettlementMessage(activation.childId, terminal, activation.kind === "local", activation.kind === "local" && activation.omitClosingText);
|
|
2083
2026
|
if (this.closingTeardownFor(parent) !== void 0) {
|
|
2084
2027
|
parent.inject(message);
|
|
@@ -2105,124 +2048,100 @@ var SubagentManager = class {
|
|
|
2105
2048
|
dispose: () => this.dispose(activation)
|
|
2106
2049
|
};
|
|
2107
2050
|
}
|
|
2108
|
-
/**
|
|
2109
|
-
async
|
|
2110
|
-
this.assertAdmitting(parent);
|
|
2111
|
-
const releaseHold = this.holdOwnership(parent, childId);
|
|
2112
|
-
try {
|
|
2113
|
-
return await this.deliverFollowup(parent, childId, content, options);
|
|
2114
|
-
} catch (error) {
|
|
2115
|
-
releaseHold();
|
|
2116
|
-
throw error;
|
|
2117
|
-
}
|
|
2118
|
-
}
|
|
2119
|
-
/** The delivery loop behind {@link deliverToChild}, run under the parent hold. */
|
|
2120
|
-
async deliverFollowup(parent, childId, content, options) {
|
|
2051
|
+
/** Serialize target restoration and inbox admission; closure is awaited outside the lock. */
|
|
2052
|
+
async deliverToMember(root, childId, content, options) {
|
|
2121
2053
|
while (true) {
|
|
2122
|
-
|
|
2054
|
+
this.assertAdmitting(root);
|
|
2055
|
+
options.signal.throwIfAborted();
|
|
2056
|
+
const result = await this.locks.run(childId, async () => {
|
|
2057
|
+
if (childId === root.id) {
|
|
2058
|
+
if (contentHasImage(content)) await this.assertImageCapable(root, options.signal);
|
|
2059
|
+
options.signal.throwIfAborted();
|
|
2060
|
+
options.authorize?.();
|
|
2061
|
+
this.assertAdmitting(root);
|
|
2062
|
+
const message = createUserMessage({
|
|
2063
|
+
content,
|
|
2064
|
+
source: options.source
|
|
2065
|
+
});
|
|
2066
|
+
this.sendWaking(root, message, options.delivery);
|
|
2067
|
+
options.onAccepted?.();
|
|
2068
|
+
return { messageId: message.id };
|
|
2069
|
+
}
|
|
2123
2070
|
const activation = this.resident.get(childId);
|
|
2124
|
-
if (activation === void 0)
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
/* v8 ignore next 3 -- the send-versus-dispose cutoff needs a delivery to
|
|
2128
|
-
* observe the transaction inside the same critical section that opened it. */
|
|
2129
|
-
if (disposal !== void 0) return disposal.then(() => void 0, () => void 0);
|
|
2130
|
-
if (contentHasImage(content)) {
|
|
2131
|
-
await this.assertImageCapable(requireLocalActivation(activation).handle.agent, options.signal);
|
|
2132
|
-
if (activation.closing !== void 0) {
|
|
2133
|
-
await Promise.allSettled([activation.closing]);
|
|
2134
|
-
return;
|
|
2135
|
-
}
|
|
2071
|
+
if (activation === void 0) {
|
|
2072
|
+
const restored = await this.restoreTarget(root, childId, options.signal);
|
|
2073
|
+
return { messageId: await this.submitMaterialized(restored, content, options, root) };
|
|
2136
2074
|
}
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2075
|
+
if (activation.root !== root) throw new SubagentError("target belongs to another subagent tree", "UNAUTHORIZED");
|
|
2076
|
+
requireLocalActivation(activation);
|
|
2077
|
+
if (activation.closing === void 0 && contentHasImage(content)) await this.assertImageCapable(requireLocalActivation(activation).handle.agent, options.signal);
|
|
2078
|
+
if (activation.closing !== void 0) return { closing: activation.closing };
|
|
2079
|
+
return { messageId: this.submitAdmitted(activation, content, options, root) };
|
|
2140
2080
|
});
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
this.assertAdmitting(parent);
|
|
2144
|
-
options.signal.throwIfAborted();
|
|
2081
|
+
if ("messageId" in result) return result.messageId;
|
|
2082
|
+
await Promise.allSettled([result.closing]);
|
|
2145
2083
|
}
|
|
2146
2084
|
}
|
|
2147
|
-
/**
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
* transaction between exact-agent authorization and this no-await span. */
|
|
2151
|
-
if (activation.closing !== void 0) throw new SubagentError(`subagent "${sender.id}" activation is being disposed; the message was not delivered`, "ACTIVATION_CLOSING");
|
|
2152
|
-
const parent = this.ctx.agents.get(activation.parent.id);
|
|
2153
|
-
if (parent !== activation.parent) throw new SubagentError("direct parent is not live; the message was not delivered", "PARENT_UNAVAILABLE");
|
|
2154
|
-
const message = createAgentMessage(sender, content);
|
|
2155
|
-
this.sendAgentMessage(parent, message);
|
|
2156
|
-
activation.omitClosingText = true;
|
|
2157
|
-
return message.id;
|
|
2158
|
-
}
|
|
2159
|
-
/** Send one Agent message while translating only the target's own rejection. */
|
|
2160
|
-
sendAgentMessage(parent, message) {
|
|
2085
|
+
/** Read continuation history, preserving cancellation and reporting unavailable storage. */
|
|
2086
|
+
async observeContinuation(childId, signal) {
|
|
2087
|
+
const query = this.requireSessionQuery();
|
|
2161
2088
|
try {
|
|
2162
|
-
|
|
2089
|
+
return await query.observeSession(childId, { signal });
|
|
2163
2090
|
} catch (error) {
|
|
2164
|
-
|
|
2091
|
+
signal.throwIfAborted();
|
|
2092
|
+
throw new SubagentError(`subagent "${childId}" is unavailable`, "NOT_RESUMABLE", { cause: error });
|
|
2165
2093
|
}
|
|
2166
2094
|
}
|
|
2167
|
-
/**
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
hasError: false
|
|
2176
|
-
};
|
|
2177
|
-
try {
|
|
2178
|
-
const query = this.requireSessionQuery();
|
|
2179
|
-
let observation;
|
|
2180
|
-
try {
|
|
2181
|
-
observation = await query.observeSession(childId, { signal: options.signal });
|
|
2182
|
-
} catch (error) {
|
|
2183
|
-
options.signal.throwIfAborted();
|
|
2184
|
-
throw new SubagentError(`subagent "${childId}" is unavailable`, "NOT_RESUMABLE", { cause: error });
|
|
2185
|
-
}
|
|
2186
|
-
const source = __addDisposableResource$2(env_1, observation, false);
|
|
2187
|
-
this.assertAdmitting(parent);
|
|
2188
|
-
this.authorizeLineage(parent, childId, source.header.parentSession);
|
|
2189
|
-
const ownEvents = source.events.slice(source.inheritedEventCount);
|
|
2190
|
-
const descriptor = foldSubagentDescriptor(ownEvents);
|
|
2191
|
-
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");
|
|
2192
|
-
let activation;
|
|
2095
|
+
/** Reconstruct only the target under its subagent tree root; the caller owns acceptance or rollback. */
|
|
2096
|
+
async restoreTarget(root, childId, signal) {
|
|
2097
|
+
return this.trackStartup(root, signal, async (signal) => {
|
|
2098
|
+
const env_3 = {
|
|
2099
|
+
stack: [],
|
|
2100
|
+
error: void 0,
|
|
2101
|
+
hasError: false
|
|
2102
|
+
};
|
|
2193
2103
|
try {
|
|
2194
|
-
const
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2104
|
+
const source = __addDisposableResource$1(env_3, await this.observeContinuation(childId, signal), false);
|
|
2105
|
+
this.assertAdmitting(root);
|
|
2106
|
+
const descriptor = foldSubagentDescriptor(source.events.slice(source.inheritedEventCount));
|
|
2107
|
+
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");
|
|
2108
|
+
const { ancestors } = await this.resolveCreationAncestry(source, signal, root);
|
|
2109
|
+
let activation;
|
|
2110
|
+
try {
|
|
2111
|
+
const projectedPreset = source.projections?.values.agentPreset;
|
|
2112
|
+
if (projectedPreset === void 0 && source.events.some((event) => event.type === "agent-preset/selected")) throw new SubagentError(`subagent "${childId}" has no agent preset projection for restoration`, "NOT_RESUMABLE");
|
|
2113
|
+
const agentPreset = projectedPreset ?? source.header.agentPreset;
|
|
2114
|
+
activation = await this.materialize({
|
|
2115
|
+
kind: "local",
|
|
2116
|
+
childId,
|
|
2117
|
+
provider: descriptor.provider,
|
|
2118
|
+
root,
|
|
2119
|
+
ancestors,
|
|
2120
|
+
agentOptions: {
|
|
2121
|
+
...descriptor.agentProvider !== void 0 ? { provider: descriptor.agentProvider } : {},
|
|
2122
|
+
...descriptor.agentModel !== void 0 ? { model: descriptor.agentModel } : {},
|
|
2123
|
+
...descriptor.agentReasoningEffort !== void 0 ? { reasoningEffort: ReasoningEffortId(descriptor.agentReasoningEffort) } : {}
|
|
2124
|
+
},
|
|
2125
|
+
composition: {
|
|
2126
|
+
agentPreset: agentPreset ?? void 0,
|
|
2127
|
+
persona: descriptor.persona,
|
|
2128
|
+
toolFilter: descriptor.toolFilter
|
|
2129
|
+
},
|
|
2130
|
+
signal
|
|
2131
|
+
});
|
|
2132
|
+
} catch (error) {
|
|
2133
|
+
signal.throwIfAborted();
|
|
2134
|
+
if (error instanceof SubagentError) throw error;
|
|
2135
|
+
throw new SubagentError(`subagent "${childId}" is unavailable`, "NOT_RESUMABLE", { cause: error });
|
|
2136
|
+
}
|
|
2137
|
+
return activation;
|
|
2138
|
+
} catch (e_3) {
|
|
2139
|
+
env_3.error = e_3;
|
|
2140
|
+
env_3.hasError = true;
|
|
2141
|
+
} finally {
|
|
2142
|
+
__disposeResources$1(env_3);
|
|
2218
2143
|
}
|
|
2219
|
-
|
|
2220
|
-
} catch (e_1) {
|
|
2221
|
-
env_1.error = e_1;
|
|
2222
|
-
env_1.hasError = true;
|
|
2223
|
-
} finally {
|
|
2224
|
-
__disposeResources$2(env_1);
|
|
2225
|
-
}
|
|
2144
|
+
});
|
|
2226
2145
|
}
|
|
2227
2146
|
/** Admit a materialized child, commit its creation fact, and release it on failure. */
|
|
2228
2147
|
async submitMaterialized(activation, content, options, parent, commit) {
|
|
@@ -2233,7 +2152,7 @@ var SubagentManager = class {
|
|
|
2233
2152
|
}
|
|
2234
2153
|
const messageId = this.submitAdmitted(activation, content, options, parent);
|
|
2235
2154
|
commit?.();
|
|
2236
|
-
this.
|
|
2155
|
+
this.accept(activation);
|
|
2237
2156
|
return messageId;
|
|
2238
2157
|
} catch (error) {
|
|
2239
2158
|
try {
|
|
@@ -2246,19 +2165,19 @@ var SubagentManager = class {
|
|
|
2246
2165
|
}
|
|
2247
2166
|
/** Build and submit one message across the final synchronous admission cutoff. */
|
|
2248
2167
|
submitAdmitted(activation, content, options, parent) {
|
|
2249
|
-
const message =
|
|
2168
|
+
const message = createUserMessage({
|
|
2250
2169
|
content,
|
|
2251
2170
|
source: options.source
|
|
2252
2171
|
});
|
|
2253
2172
|
options.signal.throwIfAborted();
|
|
2173
|
+
options.authorize?.();
|
|
2254
2174
|
this.assertAdmitting(parent);
|
|
2255
|
-
this.authorizeLineage(parent, activation.childId, activation.parent.id);
|
|
2256
|
-
this.acquireOwnership(parent, activation.childId);
|
|
2257
2175
|
try {
|
|
2258
2176
|
this.deliver(activation, message, options.delivery);
|
|
2259
2177
|
} finally {
|
|
2260
2178
|
this.wake(activation);
|
|
2261
2179
|
}
|
|
2180
|
+
options.onAccepted?.();
|
|
2262
2181
|
return message.id;
|
|
2263
2182
|
}
|
|
2264
2183
|
/** Refuse image content for a child whose fixed model accepts text only. */
|
|
@@ -2284,15 +2203,6 @@ var SubagentManager = class {
|
|
|
2284
2203
|
return query;
|
|
2285
2204
|
}
|
|
2286
2205
|
};
|
|
2287
|
-
/** Creation-time selection precedes the own descriptor; historical forks can select after it. */
|
|
2288
|
-
function hasIndependentPresetSelection(events) {
|
|
2289
|
-
for (const event of events) {
|
|
2290
|
-
if (event.type === "subagent/descriptor") return false;
|
|
2291
|
-
if (event.type === "agent-preset/selected") return true;
|
|
2292
|
-
}
|
|
2293
|
-
/* v8 ignore next -- coldResume rejects logs without a supported own descriptor before this lookup. */
|
|
2294
|
-
return false;
|
|
2295
|
-
}
|
|
2296
2206
|
//#endregion
|
|
2297
2207
|
//#region lib/types/list-children.js
|
|
2298
2208
|
/**
|
|
@@ -2300,7 +2210,7 @@ function hasIndependentPresetSelection(events) {
|
|
|
2300
2210
|
* Each catalog read releases its Session observation before the next branch.
|
|
2301
2211
|
* @module @deepseek-ai/dsh-subagent
|
|
2302
2212
|
*/
|
|
2303
|
-
var __addDisposableResource
|
|
2213
|
+
var __addDisposableResource = function(env, value, async) {
|
|
2304
2214
|
if (value !== null && value !== void 0) {
|
|
2305
2215
|
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
|
2306
2216
|
var dispose, inner;
|
|
@@ -2329,7 +2239,7 @@ var __addDisposableResource$1 = function(env, value, async) {
|
|
|
2329
2239
|
} else if (async) env.stack.push({ async: true });
|
|
2330
2240
|
return value;
|
|
2331
2241
|
};
|
|
2332
|
-
var __disposeResources
|
|
2242
|
+
var __disposeResources = (function(SuppressedError) {
|
|
2333
2243
|
return function(env) {
|
|
2334
2244
|
function fail(e) {
|
|
2335
2245
|
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
|
|
@@ -2375,14 +2285,14 @@ async function listChildren(ctx, parentSessionId, signal) {
|
|
|
2375
2285
|
try {
|
|
2376
2286
|
const query = ctx.get("sessionQuery");
|
|
2377
2287
|
if (query === void 0) throw new SubagentError("listing subagents requires the sessionQuery service (load @deepseek-ai/dsh-session-query)", "SUBAGENT_CONTROL_QUERY_UNAVAILABLE");
|
|
2378
|
-
const entries = __addDisposableResource
|
|
2288
|
+
const entries = __addDisposableResource(env_1, await query.observeSession(parentSessionId, { ...signal === void 0 ? {} : { signal } }), false).projections?.values.subagentCatalog;
|
|
2379
2289
|
if (entries === void 0) throw new SubagentError("listing subagents requires the registered subagentCatalog projection", "SUBAGENT_CONTROL_PROJECTIONS_UNAVAILABLE");
|
|
2380
2290
|
return entries;
|
|
2381
2291
|
} catch (e_1) {
|
|
2382
2292
|
env_1.error = e_1;
|
|
2383
2293
|
env_1.hasError = true;
|
|
2384
2294
|
} finally {
|
|
2385
|
-
__disposeResources
|
|
2295
|
+
__disposeResources(env_1);
|
|
2386
2296
|
}
|
|
2387
2297
|
}
|
|
2388
2298
|
/**
|
|
@@ -2465,159 +2375,6 @@ function assertListingNotCancelled(signal) {
|
|
|
2465
2375
|
if (signal?.aborted) throw new SubagentError("subagent listing was cancelled", "CANCELLED");
|
|
2466
2376
|
}
|
|
2467
2377
|
//#endregion
|
|
2468
|
-
//#region lib/types/archive-admission.js
|
|
2469
|
-
/**
|
|
2470
|
-
* The `subagent` family of the Workspace registry's archive admission: which
|
|
2471
|
-
* subagent descendants of a Session are still inside a turn, and how they
|
|
2472
|
-
* stop when the Session is archived with its work.
|
|
2473
|
-
*
|
|
2474
|
-
* @module @deepseek-ai/dsh-subagent
|
|
2475
|
-
*/
|
|
2476
|
-
var __addDisposableResource = function(env, value, async) {
|
|
2477
|
-
if (value !== null && value !== void 0) {
|
|
2478
|
-
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
|
2479
|
-
var dispose, inner;
|
|
2480
|
-
if (async) {
|
|
2481
|
-
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
|
|
2482
|
-
dispose = value[Symbol.asyncDispose];
|
|
2483
|
-
}
|
|
2484
|
-
if (dispose === void 0) {
|
|
2485
|
-
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
|
|
2486
|
-
dispose = value[Symbol.dispose];
|
|
2487
|
-
if (async) inner = dispose;
|
|
2488
|
-
}
|
|
2489
|
-
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
|
|
2490
|
-
if (inner) dispose = function() {
|
|
2491
|
-
try {
|
|
2492
|
-
inner.call(this);
|
|
2493
|
-
} catch (e) {
|
|
2494
|
-
return Promise.reject(e);
|
|
2495
|
-
}
|
|
2496
|
-
};
|
|
2497
|
-
env.stack.push({
|
|
2498
|
-
value,
|
|
2499
|
-
dispose,
|
|
2500
|
-
async
|
|
2501
|
-
});
|
|
2502
|
-
} else if (async) env.stack.push({ async: true });
|
|
2503
|
-
return value;
|
|
2504
|
-
};
|
|
2505
|
-
var __disposeResources = (function(SuppressedError) {
|
|
2506
|
-
return function(env) {
|
|
2507
|
-
function fail(e) {
|
|
2508
|
-
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
|
|
2509
|
-
env.hasError = true;
|
|
2510
|
-
}
|
|
2511
|
-
var r, s = 0;
|
|
2512
|
-
function next() {
|
|
2513
|
-
while (r = env.stack.pop()) try {
|
|
2514
|
-
if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
|
|
2515
|
-
if (r.dispose) {
|
|
2516
|
-
var result = r.dispose.call(r.value);
|
|
2517
|
-
if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) {
|
|
2518
|
-
fail(e);
|
|
2519
|
-
return next();
|
|
2520
|
-
});
|
|
2521
|
-
} else s |= 1;
|
|
2522
|
-
} catch (e) {
|
|
2523
|
-
fail(e);
|
|
2524
|
-
}
|
|
2525
|
-
if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
|
|
2526
|
-
if (env.hasError) throw env.error;
|
|
2527
|
-
}
|
|
2528
|
-
return next();
|
|
2529
|
-
};
|
|
2530
|
-
})(typeof SuppressedError === "function" ? SuppressedError : function(error, suppressed, message) {
|
|
2531
|
-
var e = new Error(message);
|
|
2532
|
-
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
2533
|
-
});
|
|
2534
|
-
/**
|
|
2535
|
-
* Answer `workspace/session-activity` with the running subagent descendants of
|
|
2536
|
-
* the asked Session, and `workspace/session-stop` by cancelling each of them
|
|
2537
|
-
* as their parent would. Both listeners live as long as `ctx`'s fiber.
|
|
2538
|
-
* @param ctx - context carrying the Agent registry; the Session query service is optional and only supplies labels.
|
|
2539
|
-
*/
|
|
2540
|
-
function installSubagentArchiveAdmission(ctx) {
|
|
2541
|
-
ctx.on("workspace/session-activity", async ({ sessionId }, next) => {
|
|
2542
|
-
const running = runningDescendants(ctx, sessionId);
|
|
2543
|
-
const rest = await next();
|
|
2544
|
-
if (running.length === 0) return rest;
|
|
2545
|
-
return [{
|
|
2546
|
-
kind: "subagent",
|
|
2547
|
-
items: await Promise.all(running.map((child) => describe(ctx, child)))
|
|
2548
|
-
}, ...rest];
|
|
2549
|
-
});
|
|
2550
|
-
ctx.on("workspace/session-stop", ({ sessionId }) => {
|
|
2551
|
-
for (const child of runningDescendants(ctx, sessionId)) try {
|
|
2552
|
-
child.cancel({ kind: "parent" });
|
|
2553
|
-
} catch (error) {
|
|
2554
|
-
ctx.logger.warn(`subagent: cancelling "${child.id}" for an archived Session failed: ${String(error)}`);
|
|
2555
|
-
}
|
|
2556
|
-
});
|
|
2557
|
-
}
|
|
2558
|
-
/**
|
|
2559
|
-
* Live subagent descendants inside a turn, by durable lineage: a child whose
|
|
2560
|
-
* header names its parent and carries the subagent origin this package
|
|
2561
|
-
* records, at any depth. A fork shares the lineage field without the origin
|
|
2562
|
-
* and is an independent conversation, so it never holds its source. Lineage
|
|
2563
|
-
* is read as data, so a damaged header chain that loops is visited once.
|
|
2564
|
-
*/
|
|
2565
|
-
function runningDescendants(ctx, rootId) {
|
|
2566
|
-
const childrenOf = /* @__PURE__ */ new Map();
|
|
2567
|
-
for (const agent of ctx.agents.list()) {
|
|
2568
|
-
const { parentSession, origin } = agent.session.header;
|
|
2569
|
-
if (parentSession === void 0 || origin !== "subagent") continue;
|
|
2570
|
-
const siblings = childrenOf.get(parentSession) ?? [];
|
|
2571
|
-
siblings.push(agent);
|
|
2572
|
-
childrenOf.set(parentSession, siblings);
|
|
2573
|
-
}
|
|
2574
|
-
const running = [];
|
|
2575
|
-
const pending = [rootId];
|
|
2576
|
-
const visited = /* @__PURE__ */ new Set();
|
|
2577
|
-
while (pending.length > 0) {
|
|
2578
|
-
const parentId = pending.shift();
|
|
2579
|
-
if (visited.has(parentId)) continue;
|
|
2580
|
-
visited.add(parentId);
|
|
2581
|
-
for (const child of childrenOf.get(parentId) ?? []) {
|
|
2582
|
-
if (child.status === "running") running.push(child);
|
|
2583
|
-
pending.push(child.id);
|
|
2584
|
-
}
|
|
2585
|
-
}
|
|
2586
|
-
return running;
|
|
2587
|
-
}
|
|
2588
|
-
/**
|
|
2589
|
-
* The child's activity item: its id, plus the durable creation label its
|
|
2590
|
-
* descriptor carries, read through a live Session observation. Without the
|
|
2591
|
-
* Session query service, or with a descriptor that is absent or unreadable,
|
|
2592
|
-
* the item names the child by id alone.
|
|
2593
|
-
*/
|
|
2594
|
-
async function describe(ctx, child) {
|
|
2595
|
-
const query = ctx.get("sessionQuery");
|
|
2596
|
-
if (query === void 0) return { id: child.id };
|
|
2597
|
-
try {
|
|
2598
|
-
const env_1 = {
|
|
2599
|
-
stack: [],
|
|
2600
|
-
error: void 0,
|
|
2601
|
-
hasError: false
|
|
2602
|
-
};
|
|
2603
|
-
try {
|
|
2604
|
-
const observation = __addDisposableResource(env_1, await query.observeSession(child.id, { projectionMode: "none" }), false);
|
|
2605
|
-
const label = foldSubagentDescriptor(observation.events.slice(observation.inheritedEventCount))?.label;
|
|
2606
|
-
return label === void 0 ? { id: child.id } : {
|
|
2607
|
-
id: child.id,
|
|
2608
|
-
label
|
|
2609
|
-
};
|
|
2610
|
-
} catch (e_1) {
|
|
2611
|
-
env_1.error = e_1;
|
|
2612
|
-
env_1.hasError = true;
|
|
2613
|
-
} finally {
|
|
2614
|
-
__disposeResources(env_1);
|
|
2615
|
-
}
|
|
2616
|
-
} catch {
|
|
2617
|
-
return { id: child.id };
|
|
2618
|
-
}
|
|
2619
|
-
}
|
|
2620
|
-
//#endregion
|
|
2621
2378
|
//#region lib/types/projection.js
|
|
2622
2379
|
/**
|
|
2623
2380
|
* Pure session projections for subagent identity (mode/label) and active-turn
|
|
@@ -2969,12 +2726,18 @@ let SubagentRuntime = (() => {
|
|
|
2969
2726
|
let _classSuper = TypertRemoteService;
|
|
2970
2727
|
let _instanceExtraInitializers = [];
|
|
2971
2728
|
let _prompt_decorators;
|
|
2972
|
-
let
|
|
2729
|
+
let _interruptByUser_decorators;
|
|
2973
2730
|
return class SubagentRuntime extends _classSuper {
|
|
2974
2731
|
static {
|
|
2975
2732
|
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
2976
|
-
_prompt_decorators = [Remote(
|
|
2977
|
-
|
|
2733
|
+
_prompt_decorators = [Remote({
|
|
2734
|
+
name: "prompt",
|
|
2735
|
+
resources: { session: (request) => request.childSessionId }
|
|
2736
|
+
})];
|
|
2737
|
+
_interruptByUser_decorators = [Remote({
|
|
2738
|
+
name: "interruptByUser",
|
|
2739
|
+
resources: { session: (childSessionId) => childSessionId }
|
|
2740
|
+
})];
|
|
2978
2741
|
__esDecorate(this, null, _prompt_decorators, {
|
|
2979
2742
|
kind: "method",
|
|
2980
2743
|
name: "prompt",
|
|
@@ -2986,14 +2749,14 @@ let SubagentRuntime = (() => {
|
|
|
2986
2749
|
},
|
|
2987
2750
|
metadata: _metadata
|
|
2988
2751
|
}, null, _instanceExtraInitializers);
|
|
2989
|
-
__esDecorate(this, null,
|
|
2752
|
+
__esDecorate(this, null, _interruptByUser_decorators, {
|
|
2990
2753
|
kind: "method",
|
|
2991
|
-
name: "
|
|
2754
|
+
name: "interruptByUser",
|
|
2992
2755
|
static: false,
|
|
2993
2756
|
private: false,
|
|
2994
2757
|
access: {
|
|
2995
|
-
has: (obj) => "
|
|
2996
|
-
get: (obj) => obj.
|
|
2758
|
+
has: (obj) => "interruptByUser" in obj,
|
|
2759
|
+
get: (obj) => obj.interruptByUser
|
|
2997
2760
|
},
|
|
2998
2761
|
metadata: _metadata
|
|
2999
2762
|
}, null, _instanceExtraInitializers);
|
|
@@ -3023,13 +2786,17 @@ let SubagentRuntime = (() => {
|
|
|
3023
2786
|
super(ctx, "subagents");
|
|
3024
2787
|
this.config = config;
|
|
3025
2788
|
this.emitLifecycle = createLifecycleEmitter(this.ctx, (parent) => scopeTarget(this, parent));
|
|
2789
|
+
}
|
|
2790
|
+
async [Service.init]() {
|
|
2791
|
+
const ctx = this.ctx;
|
|
2792
|
+
await ctx.sessionProjections.register(subagentCatalogProjectionDefinition);
|
|
2793
|
+
await ctx.sessionProjections.register(subagentTimingProjectionDefinition);
|
|
2794
|
+
const identityProjection = await ctx.sessionProjections.register(subagentIdentityProjectionDefinition);
|
|
2795
|
+
if (ctx.fiber.uid === null) return;
|
|
3026
2796
|
ctx.inject(["agents"], (childCtx) => {
|
|
3027
2797
|
const manager = new SubagentManager(childCtx, {
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
},
|
|
3031
|
-
prepareContinuable: (name, request) => this.prepareContinuable(name, request),
|
|
3032
|
-
observeActivation: (provider, childId, parent) => this.observeActivation(provider, childId, parent)
|
|
2798
|
+
identityOf: (session) => identityProjection.stateOf(session).identity,
|
|
2799
|
+
emitLifecycle: this.emitLifecycle
|
|
3033
2800
|
}, () => this.config.maxActiveSubagents.get(), () => this.config.delegatedApproval.get());
|
|
3034
2801
|
this.manager = manager;
|
|
3035
2802
|
childCtx.effect(() => () => {
|
|
@@ -3037,15 +2804,6 @@ let SubagentRuntime = (() => {
|
|
|
3037
2804
|
if (this.manager === manager) this.manager = void 0;
|
|
3038
2805
|
}, "subagents.managerBinding()");
|
|
3039
2806
|
});
|
|
3040
|
-
ctx.inject(["agents"], (agentsCtx) => {
|
|
3041
|
-
installSubagentArchiveAdmission(agentsCtx);
|
|
3042
|
-
});
|
|
3043
|
-
}
|
|
3044
|
-
async [Service.init]() {
|
|
3045
|
-
const { sessionProjections } = this.ctx;
|
|
3046
|
-
await sessionProjections.register(subagentCatalogProjectionDefinition);
|
|
3047
|
-
await sessionProjections.register(subagentTimingProjectionDefinition);
|
|
3048
|
-
await sessionProjections.register(subagentIdentityProjectionDefinition);
|
|
3049
2807
|
}
|
|
3050
2808
|
/**
|
|
3051
2809
|
* Resolve a delegation tool's depth policy against the current user setting.
|
|
@@ -3064,64 +2822,64 @@ let SubagentRuntime = (() => {
|
|
|
3064
2822
|
* @param spec - task, backend, and result recipient.
|
|
3065
2823
|
* @returns identities, execution result, and exact-activation disposal. A reserved
|
|
3066
2824
|
* childId requires a local backend and guarantees an accepted messageId.
|
|
2825
|
+
* @throws PROVIDER_CHANGED if the selected provider is removed or replaced before dispatch.
|
|
3067
2826
|
*/
|
|
3068
2827
|
async startActivation(spec) {
|
|
3069
2828
|
const provider = this.expectProvider(spec.provider);
|
|
3070
2829
|
if (spec.childId !== void 0 && provider.prepareContinuable === void 0) throw new SubagentError("reserved child ids require a local backend", "UNSUPPORTED_CAPABILITY");
|
|
3071
|
-
assertSubagentMaxDepth(spec.
|
|
3072
|
-
this.assertCapabilities(provider,
|
|
3073
|
-
...spec.request,
|
|
3074
|
-
signal: spec.signal
|
|
3075
|
-
});
|
|
2830
|
+
assertSubagentMaxDepth(spec.maxDepth);
|
|
2831
|
+
this.assertCapabilities(provider, spec);
|
|
3076
2832
|
const manager = this.requireManager();
|
|
3077
|
-
if (this.ctx.get("agents")?.get(spec.
|
|
3078
|
-
if (spec.
|
|
3079
|
-
|
|
2833
|
+
if (this.ctx.get("agents")?.get(spec.parent.id) !== spec.parent) throw new SubagentError("subagent creation requires the exact live parent agent", "UNAUTHORIZED");
|
|
2834
|
+
if (spec.outputSchema !== void 0) assertObjectJsonSchema(spec.outputSchema);
|
|
2835
|
+
const prepare = provider.prepareContinuable?.bind(provider);
|
|
2836
|
+
if (prepare !== void 0) return manager.startLocal(spec, (request) => {
|
|
2837
|
+
this.assertProviderCurrent(provider);
|
|
2838
|
+
return prepare(request);
|
|
2839
|
+
});
|
|
2840
|
+
const start = provider.start.bind(provider);
|
|
2841
|
+
return manager.startExternal(spec, (request) => {
|
|
2842
|
+
this.assertProviderCurrent(provider);
|
|
2843
|
+
return start(request);
|
|
2844
|
+
});
|
|
2845
|
+
}
|
|
2846
|
+
/**
|
|
2847
|
+
* Resolve the root shared by an Agent and its other agents in its subagent tree.
|
|
2848
|
+
* @param caller - exact live Agent whose subagent ancestry selects the tree.
|
|
2849
|
+
* @returns the live root; ordinary Session forks lead independent trees.
|
|
2850
|
+
* @throws when the caller or its runtime owner is unavailable.
|
|
2851
|
+
*/
|
|
2852
|
+
rootOf(caller) {
|
|
2853
|
+
return this.requireManager().rootOf(caller);
|
|
3080
2854
|
}
|
|
3081
2855
|
/**
|
|
3082
|
-
* Join progressing
|
|
3083
|
-
*
|
|
3084
|
-
* @param
|
|
3085
|
-
* @returns whether work was joined; hosts recheck
|
|
2856
|
+
* Join progressing subagent tree work for its root. Idle members with parked input
|
|
2857
|
+
* stay resident without delaying host completion. Ordinary members return false.
|
|
2858
|
+
* @param root - exact live subagent tree root whose member work is awaited.
|
|
2859
|
+
* @returns whether work was joined; hosts recheck root idle after true.
|
|
3086
2860
|
*/
|
|
3087
|
-
async
|
|
3088
|
-
return this.manager?.
|
|
2861
|
+
async waitForTreeWork(root) {
|
|
2862
|
+
return this.manager?.waitForTreeWork(root) ?? false;
|
|
3089
2863
|
}
|
|
3090
2864
|
/**
|
|
3091
|
-
* Steer one model-authored message to
|
|
3092
|
-
*
|
|
3093
|
-
*
|
|
3094
|
-
*
|
|
3095
|
-
*
|
|
3096
|
-
* @param sender - exact live
|
|
3097
|
-
* @param targetId -
|
|
2865
|
+
* Steer one model-authored message to another member of the same subagent tree.
|
|
2866
|
+
* A running target receives it at its next step; an idle target starts a turn.
|
|
2867
|
+
* A cold target resumes under its runtime subagent tree root without restoring
|
|
2868
|
+
* its creation ancestors. Sender identity is rechecked before inbox acceptance; cancellation
|
|
2869
|
+
* stops only pre-acceptance work and never retracts an accepted message.
|
|
2870
|
+
* @param sender - exact live root or resident continuable Agent originating the message.
|
|
2871
|
+
* @param targetId - another root or continuable member's durable Session id.
|
|
3098
2872
|
* @param content - model-authored content to deliver.
|
|
3099
2873
|
* @param options - caller cancellation before inbox acceptance.
|
|
3100
2874
|
* @returns the accepted message's inbox id.
|
|
3101
|
-
* @throws when
|
|
3102
|
-
* or the message was not admitted.
|
|
2875
|
+
* @throws when the target is outside the tree, cannot resume, or rejects input.
|
|
3103
2876
|
*/
|
|
3104
2877
|
async sendMessage(sender, targetId, content, options) {
|
|
3105
2878
|
return this.requireManager().sendMessage(sender, targetId, content, options);
|
|
3106
2879
|
}
|
|
3107
2880
|
/**
|
|
3108
|
-
*
|
|
3109
|
-
*
|
|
3110
|
-
* widening the public Service Definition or impersonating an Agent sender.
|
|
3111
|
-
* @param parent - exact live direct parent authorizing delivery.
|
|
3112
|
-
* @param childId - durable direct-child session id.
|
|
3113
|
-
* @param content - host-authored content to deliver.
|
|
3114
|
-
* @param source - durable host-protocol source descriptor.
|
|
3115
|
-
* @param signal - caller cancellation before inbox acceptance.
|
|
3116
|
-
* @param delivery - Queue as a distinct turn or Steer at the nearest step.
|
|
3117
|
-
* @returns the accepted message's inbox id.
|
|
3118
|
-
*/
|
|
3119
|
-
[deliverSubagentPrompt](parent, childId, content, source, signal, delivery) {
|
|
3120
|
-
return delivery === "steer" ? this.requireManager().steerPrompt(parent, childId, content, source, signal) : this.requireManager().queuePrompt(parent, childId, content, source, signal);
|
|
3121
|
-
}
|
|
3122
|
-
/**
|
|
3123
|
-
* Interrupt one live child's current execution under a human parent
|
|
3124
|
-
* address or an exact live ancestor Agent. Fire-and-return: the cancel
|
|
2881
|
+
* Interrupt one live child's current execution under a trusted user
|
|
2882
|
+
* operation or an exact live ancestor Agent. Fire-and-return: the cancel
|
|
3125
2883
|
* signal is issued before this returns, but the target may keep running
|
|
3126
2884
|
* until it observes the signal. Unclaimed pending inbox work, the Activation,
|
|
3127
2885
|
* and published descendants are preserved; claimed work is not requeued.
|
|
@@ -3130,7 +2888,7 @@ let SubagentRuntime = (() => {
|
|
|
3130
2888
|
* is an accepted no-op, as is a manager-less composition, which cannot own a
|
|
3131
2889
|
* live Activation.
|
|
3132
2890
|
* @param targetSessionId - the durable child session id to interrupt.
|
|
3133
|
-
* @param authority -
|
|
2891
|
+
* @param authority - a trusted user operation or exact live ancestor Agent.
|
|
3134
2892
|
* @throws {SubagentError} `UNAUTHORIZED` when the authority does not own the
|
|
3135
2893
|
* live target.
|
|
3136
2894
|
*/
|
|
@@ -3138,13 +2896,11 @@ let SubagentRuntime = (() => {
|
|
|
3138
2896
|
this.manager?.interrupt(targetSessionId, authority);
|
|
3139
2897
|
}
|
|
3140
2898
|
/**
|
|
3141
|
-
* Close
|
|
3142
|
-
*
|
|
3143
|
-
*
|
|
3144
|
-
*
|
|
3145
|
-
*
|
|
3146
|
-
* @param parents - exact host-owned parent Agents entering teardown.
|
|
3147
|
-
* @returns once every retained descendant activation released its execution handle.
|
|
2899
|
+
* Close admission and stop all instances in the selected subagent trees, then await
|
|
2900
|
+
* pending startup and cleanup. Ordinary members are ignored. The cutoff lasts
|
|
2901
|
+
* until each exact root leaves the registry; unrelated subagent trees remain live.
|
|
2902
|
+
* @param parents - exact live subagent tree roots entering teardown.
|
|
2903
|
+
* @returns once all admitted startup and member cleanup have settled.
|
|
3148
2904
|
* @throws an aggregate error after all branches settle when any failed.
|
|
3149
2905
|
*/
|
|
3150
2906
|
async drainDescendants(parents) {
|
|
@@ -3153,33 +2909,6 @@ let SubagentRuntime = (() => {
|
|
|
3153
2909
|
await manager.drainDescendants(parents);
|
|
3154
2910
|
}
|
|
3155
2911
|
/**
|
|
3156
|
-
* Release selected resident direct children of one exact live
|
|
3157
|
-
* parent. Other children of the same parent remain admitted and resident.
|
|
3158
|
-
* Absent targets and a manager-less composition are accepted no-ops.
|
|
3159
|
-
* @param parent - exact live direct parent authorizing the selected release.
|
|
3160
|
-
* @param childIds - durable direct-child ids to release when resident.
|
|
3161
|
-
* @returns once every selected activation released its execution handle.
|
|
3162
|
-
* @throws {SubagentError} `UNAUTHORIZED` when a resident target belongs to a
|
|
3163
|
-
* different parent or the supplied parent identity is stale.
|
|
3164
|
-
*/
|
|
3165
|
-
async drainChildren(parent, childIds) {
|
|
3166
|
-
const manager = this.manager;
|
|
3167
|
-
if (manager === void 0) return;
|
|
3168
|
-
await manager.drainChildren(parent, childIds);
|
|
3169
|
-
}
|
|
3170
|
-
/**
|
|
3171
|
-
* Read the parent's durable direct-child catalog without loading or resuming an Agent.
|
|
3172
|
-
* The service owns and releases the live-preferred Session observation.
|
|
3173
|
-
* @param parentSessionId - parent whose direct children are requested.
|
|
3174
|
-
* @param signal - cancellation forwarded to the Session query.
|
|
3175
|
-
* @returns catalog children in parent event order.
|
|
3176
|
-
* @throws {@link SubagentError} when query or catalog projection is unavailable.
|
|
3177
|
-
* @throws SessionQueryError when the parent cannot be read or the query is cancelled.
|
|
3178
|
-
*/
|
|
3179
|
-
listChildren(parentSessionId, signal) {
|
|
3180
|
-
return listChildren(this.ctx, parentSessionId, signal);
|
|
3181
|
-
}
|
|
3182
|
-
/**
|
|
3183
2912
|
* Recursively list reachable parent catalogs in stable pre-order, preserving
|
|
3184
2913
|
* each catalog's event order. Each row carries its catalog parent and depth;
|
|
3185
2914
|
* external children are leaves; one-shot and unknown-mode children remain
|
|
@@ -3199,8 +2928,8 @@ let SubagentRuntime = (() => {
|
|
|
3199
2928
|
return listDescendants(this.ctx, rootSessionId, signal);
|
|
3200
2929
|
}
|
|
3201
2930
|
/**
|
|
3202
|
-
* Deliver one browser-authored message to a continuable child through
|
|
3203
|
-
*
|
|
2931
|
+
* Deliver one browser-authored message to a continuable child through its
|
|
2932
|
+
* live subagent tree root, retaining the caller-minted request identity and
|
|
3204
2933
|
* validated browser zone on the accepted message. Success identifies the
|
|
3205
2934
|
* message the child's inbox accepted; later execution is independent of this
|
|
3206
2935
|
* call. Queue delivery targets a later turn; steer delivery targets the
|
|
@@ -3208,21 +2937,20 @@ let SubagentRuntime = (() => {
|
|
|
3208
2937
|
* Image parts are admitted and persisted through the attachment store
|
|
3209
2938
|
* before delivery, and the child's model must accept image input.
|
|
3210
2939
|
* Cold resume at capacity rejects with `subagent/delivery-unavailable`.
|
|
3211
|
-
*
|
|
2940
|
+
* Access is checked on the target Session before attachment admission or restoration.
|
|
2941
|
+
* @param request - target id, delivery, minted identity, content, and optional browser zone.
|
|
3212
2942
|
* @param signal - carrier cancellation, owning the call until inbox acceptance.
|
|
3213
2943
|
* @returns the accepted message's inbox identity.
|
|
3214
2944
|
* @throws {RemoteError} `gateway/bad-request`, `subagent/attachment-invalid`,
|
|
3215
|
-
* `subagent/invalid-time-zone`,
|
|
2945
|
+
* `subagent/invalid-time-zone`,
|
|
3216
2946
|
* `subagent/not-resumable`, `subagent/unauthorized`,
|
|
3217
2947
|
* `subagent/delivery-unavailable`, `gateway/cancelled`, or `gateway/internal`.
|
|
3218
2948
|
*/
|
|
3219
2949
|
async prompt(request, signal) {
|
|
3220
|
-
const {
|
|
2950
|
+
const { childSessionId, clientTimeZone, delivery } = request;
|
|
3221
2951
|
validateControlRequest("subagent.prompt", request);
|
|
3222
2952
|
const canonicalTimeZone = clientTimeZone === void 0 ? void 0 : canonicalClientTimeZone(clientTimeZone);
|
|
3223
2953
|
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 });
|
|
3224
|
-
const parent = this.ctx.get("agents")?.get(parentSessionId);
|
|
3225
|
-
if (parent === void 0) throw new RemoteError("subagent/parent-unavailable", `parent session "${parentSessionId}" is not live`, { parentSessionId });
|
|
3226
2954
|
const principal = principalOf(this.ctx.invocation);
|
|
3227
2955
|
const source = {
|
|
3228
2956
|
kind: "user",
|
|
@@ -3241,38 +2969,29 @@ let SubagentRuntime = (() => {
|
|
|
3241
2969
|
if (attachments === void 0) throw new Error("subagent image prompt requires an attachment store");
|
|
3242
2970
|
content = await attachments.admitPromptContent(request.content);
|
|
3243
2971
|
}
|
|
3244
|
-
return { messageId: await this
|
|
2972
|
+
return { messageId: await this.requireManager().deliverPrompt(childSessionId, content, {
|
|
2973
|
+
source,
|
|
2974
|
+
signal,
|
|
2975
|
+
delivery
|
|
2976
|
+
}) };
|
|
3245
2977
|
} catch (error) {
|
|
3246
2978
|
return rejectPrompt(error, childSessionId, signal);
|
|
3247
2979
|
}
|
|
3248
2980
|
}
|
|
3249
2981
|
/**
|
|
3250
|
-
* Remote face of {@link interrupt}
|
|
3251
|
-
* catalog, history, persistence, or parent Agent lookup runs
|
|
3252
|
-
* primitive alone authorizes the address against the live Activation, which
|
|
3253
|
-
* is what keeps a live child interruptible while its parent Agent is offline.
|
|
2982
|
+
* Remote face of {@link interrupt}, authorized on the target Session by the Gateway.
|
|
2983
|
+
* No catalog, history, persistence, or parent Agent lookup runs.
|
|
3254
2984
|
* Absent, idle, and already-completed targets are accepted no-ops there.
|
|
3255
2985
|
* @param childSessionId - durable child session id to interrupt.
|
|
3256
|
-
* @param parentSessionId - durable direct parent whose authority is claimed.
|
|
3257
|
-
* @param mode - required continuable-address discriminator.
|
|
3258
2986
|
* @returns acknowledgement that the cancel signal was admitted, not that the target is quiescent.
|
|
3259
2987
|
* @throws {RemoteError} `gateway/bad-request` for an empty id,
|
|
3260
|
-
* `subagent/unauthorized` when the address does not own the live target,
|
|
3261
2988
|
* otherwise `gateway/internal`.
|
|
3262
2989
|
*/
|
|
3263
|
-
|
|
3264
|
-
validateControlRequest("subagent.interrupt", {
|
|
3265
|
-
childSessionId,
|
|
3266
|
-
parentSessionId,
|
|
3267
|
-
mode
|
|
3268
|
-
});
|
|
2990
|
+
interruptByUser(childSessionId) {
|
|
2991
|
+
validateControlRequest("subagent.interrupt", { childSessionId });
|
|
3269
2992
|
try {
|
|
3270
|
-
this.interrupt(childSessionId, {
|
|
3271
|
-
kind: "user",
|
|
3272
|
-
parentSessionId
|
|
3273
|
-
});
|
|
2993
|
+
this.interrupt(childSessionId, { kind: "user" });
|
|
3274
2994
|
} catch (error) {
|
|
3275
|
-
if (error instanceof SubagentError && error.code === "UNAUTHORIZED") throw new RemoteError("subagent/unauthorized", "subagent does not belong to this parent", { childSessionId }, { cause: error });
|
|
3276
2995
|
throw new RemoteError("gateway/internal", "subagent interrupt failed", {}, { cause: error });
|
|
3277
2996
|
}
|
|
3278
2997
|
return { accepted: true };
|
|
@@ -3306,20 +3025,9 @@ let SubagentRuntime = (() => {
|
|
|
3306
3025
|
getProvider(name) {
|
|
3307
3026
|
return this.providers.get(name);
|
|
3308
3027
|
}
|
|
3309
|
-
/**
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
*/
|
|
3313
|
-
list() {
|
|
3314
|
-
return [...this.providers.keys()];
|
|
3315
|
-
}
|
|
3316
|
-
/**
|
|
3317
|
-
* Resolve one provider's detached continuable-creation contribution. Method
|
|
3318
|
-
* presence on the provider IS the capability, so a provider without it is
|
|
3319
|
-
* rejected before the manager reserves any child resources.
|
|
3320
|
-
*/
|
|
3321
|
-
async prepareContinuable(name, request) {
|
|
3322
|
-
return this.expectProvider(name).prepareContinuable(request);
|
|
3028
|
+
/** A pending start must use the exact provider whose capabilities were checked. */
|
|
3029
|
+
assertProviderCurrent(provider) {
|
|
3030
|
+
if (this.providers.get(provider.name) !== provider) throw new SubagentError(`subagent provider "${provider.name}" changed during startup; retry the delegation`, "PROVIDER_CHANGED");
|
|
3323
3031
|
}
|
|
3324
3032
|
/** Look up a provider for dispatch or fail loud. */
|
|
3325
3033
|
expectProvider(name) {
|
|
@@ -3332,13 +3040,6 @@ let SubagentRuntime = (() => {
|
|
|
3332
3040
|
if (this.manager === void 0) throw new SubagentError("continuable subagents require the agents service", "CONTINUATION_UNAVAILABLE");
|
|
3333
3041
|
return this.manager;
|
|
3334
3042
|
}
|
|
3335
|
-
/**
|
|
3336
|
-
* Build the lifecycle observer for one continuable Activation's residency
|
|
3337
|
-
* epoch, so the manager publishes its edges without owning event dispatch.
|
|
3338
|
-
*/
|
|
3339
|
-
observeActivation(provider, childId, parent) {
|
|
3340
|
-
return createActivationObserver(this.emitLifecycle, provider, childId, parent);
|
|
3341
|
-
}
|
|
3342
3043
|
/** Reject the first requested capability that the provider lacks. */
|
|
3343
3044
|
assertCapabilities(provider, request) {
|
|
3344
3045
|
if (request.agentPreset !== void 0 && (!provider.capabilities.agentPreset || provider.inheritsParentContext)) throw new SubagentError(`subagent provider "${provider.name}" does not support independent agent preset selection`, "UNSUPPORTED_CAPABILITY");
|