@deepseek-ai/dsh-subagent 0.2.1-alpha.1 → 0.2.1-alpha.2
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 +34 -34
- package/README.md +32 -26
- package/README.zh.md +32 -26
- package/lib/index.js +1436 -1304
- package/lib/typert.host.js +59 -48
- package/lib/typert.remote-client.js +2 -2
- package/lib/types/activation.d.ts +114 -0
- package/lib/types/activation.js +112 -0
- package/lib/types/catalog.d.ts +14 -6
- package/lib/types/catalog.js +34 -30
- package/lib/types/child-agent.d.ts +1 -3
- package/lib/types/child-agent.js +1 -3
- package/lib/types/continuation-messages.d.ts +4 -3
- package/lib/types/continuation-messages.js +13 -5
- package/lib/types/control-types.d.ts +7 -0
- package/lib/types/descriptor.d.ts +7 -46
- package/lib/types/descriptor.js +20 -21
- package/lib/types/index.d.ts +52 -51
- package/lib/types/index.js +75 -95
- package/lib/types/internal.d.ts +1 -1
- package/lib/types/lifecycle.d.ts +28 -58
- package/lib/types/lifecycle.js +9 -74
- package/lib/types/list-children.js +1 -1
- package/lib/types/manager.d.ts +247 -0
- package/lib/types/manager.js +1294 -0
- package/lib/types/out-of-process.d.ts +2 -28
- package/lib/types/out-of-process.js +3 -44
- package/lib/types/projection-types.d.ts +3 -0
- package/lib/types/structured.d.ts +49 -0
- package/lib/types/structured.js +130 -0
- package/lib/types/types.d.ts +57 -81
- package/lib/types/types.js +1 -1
- package/package.json +50 -49
- package/lib/types/continuation-activation.d.ts +0 -267
- package/lib/types/continuation-activation.js +0 -700
- package/lib/types/continuation.d.ts +0 -124
- package/lib/types/continuation.js +0 -455
- package/lib/types/inbox.d.ts +0 -43
- package/lib/types/inbox.js +0 -61
- package/lib/types/run-settlement.d.ts +0 -17
- package/lib/types/run-settlement.js +0 -70
package/lib/types/index.js
CHANGED
|
@@ -40,23 +40,22 @@ import { Remote, RemoteError, TypertRemoteService } from '@deepseek-ai/dsh-typer
|
|
|
40
40
|
import { rejectPrompt, validateControlRequest, } from "./control.js";
|
|
41
41
|
import { SubagentError } from "./error.js";
|
|
42
42
|
import { assertSubagentMaxDepth } from "./depth.js";
|
|
43
|
-
import { createActivationObserver, createLifecycleEmitter
|
|
44
|
-
import
|
|
43
|
+
import { createActivationObserver, createLifecycleEmitter } from "./lifecycle.js";
|
|
44
|
+
import SubagentManager from "./manager.js";
|
|
45
45
|
import { listChildren as listSubagentChildren, listDescendants as listSubagentDescendants } from "./list-children.js";
|
|
46
46
|
import { installSubagentArchiveAdmission } from "./archive-admission.js";
|
|
47
|
-
import { snapshotSubagentDescriptor } from "./descriptor.js";
|
|
48
47
|
import { subagentIdentityProjectionDefinition, subagentTimingProjectionDefinition } from "./projection.js";
|
|
49
|
-
import {
|
|
48
|
+
import { subagentCatalogProjectionDefinition } from "./catalog.js";
|
|
50
49
|
import { deliverSubagentPrompt } from "./internal.js";
|
|
51
50
|
export * from "./out-of-process.js";
|
|
52
51
|
export { AssistantOutputFold, finalAssistantOutput } from "./assistant-output.js";
|
|
53
52
|
export { SubagentRunId } from "./types.js";
|
|
54
53
|
export { foldSubagentDescriptor, snapshotSubagentDescriptor, SUBAGENT_DESCRIPTOR_VERSION, } from "./descriptor.js";
|
|
54
|
+
export { STRUCTURED_OUTPUT_TOOL } from "./structured.js";
|
|
55
55
|
export { SubagentError } from "./error.js";
|
|
56
|
-
export { settleRun } from "./run-settlement.js";
|
|
57
56
|
export { assertSubagentMaxDepth, delegationDepthOf } from "./depth.js";
|
|
58
57
|
export { appendDelegatedPolicyOverrides, applyChildComposition, captureDelegatedPolicyOverrides, childSessionMeta, parentAgentOptionsForDelegation, resolveChildAgentOptions, resolveChildDepth, SubagentDepthError, } from "./child-agent.js";
|
|
59
|
-
/** Named provider registry with
|
|
58
|
+
/** Named provider registry with managed activations, durable discovery, and local child messaging. */
|
|
60
59
|
let SubagentRuntime = (() => {
|
|
61
60
|
let _classSuper = TypertRemoteService;
|
|
62
61
|
let _instanceExtraInitializers = [];
|
|
@@ -76,8 +75,9 @@ let SubagentRuntime = (() => {
|
|
|
76
75
|
maxDepth: z.number().step(1).min(0).max(Number.MAX_SAFE_INTEGER).default(1).volatile(),
|
|
77
76
|
maxActiveSubagents: z.number().step(1).min(1).max(Number.MAX_SAFE_INTEGER).default(8).volatile(),
|
|
78
77
|
});
|
|
78
|
+
static inject = ['workingDirectory'];
|
|
79
79
|
providers = new Map();
|
|
80
|
-
|
|
80
|
+
manager;
|
|
81
81
|
/**
|
|
82
82
|
* The contained lifecycle-edge publisher. Built here because scoped dispatch
|
|
83
83
|
* keys its carrier by this exact service instance, whose own context filter
|
|
@@ -89,22 +89,25 @@ let SubagentRuntime = (() => {
|
|
|
89
89
|
this.config = config;
|
|
90
90
|
this.emitLifecycle = createLifecycleEmitter(this.ctx, parent => scopeTarget(this, parent));
|
|
91
91
|
ctx.inject(['agents'], (childCtx) => {
|
|
92
|
-
const manager = new
|
|
92
|
+
const manager = new SubagentManager(childCtx, {
|
|
93
|
+
startExternal: (name, request) => {
|
|
94
|
+
const provider = this.expectProvider(name);
|
|
95
|
+
return provider.start(request);
|
|
96
|
+
},
|
|
93
97
|
prepareContinuable: (name, request) => this.prepareContinuable(name, request),
|
|
94
98
|
observeActivation: (provider, childId, parent) => this.observeActivation(provider, childId, parent),
|
|
95
99
|
}, () => this.config.maxActiveSubagents.get());
|
|
96
|
-
this.
|
|
100
|
+
this.manager = manager;
|
|
97
101
|
childCtx.effect(() => () => {
|
|
98
102
|
/* v8 ignore else -- one injected binding owns the slot until its fiber disposes. */
|
|
99
|
-
if (this.
|
|
100
|
-
this.
|
|
101
|
-
}, 'subagents.
|
|
103
|
+
if (this.manager === manager)
|
|
104
|
+
this.manager = undefined;
|
|
105
|
+
}, 'subagents.managerBinding()');
|
|
102
106
|
});
|
|
103
107
|
ctx.inject(['sessionProjections'], (projectionCtx) => {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
projections.register(subagentIdentityProjectionDefinition);
|
|
108
|
+
projectionCtx.sessionProjections.register(subagentCatalogProjectionDefinition);
|
|
109
|
+
projectionCtx.sessionProjections.register(subagentTimingProjectionDefinition);
|
|
110
|
+
projectionCtx.sessionProjections.register(subagentIdentityProjectionDefinition);
|
|
108
111
|
});
|
|
109
112
|
// Archive admission: this runtime is the owner that knows which live
|
|
110
113
|
// children descend from a Session and how a parent stops them.
|
|
@@ -125,16 +128,36 @@ let SubagentRuntime = (() => {
|
|
|
125
128
|
return depth;
|
|
126
129
|
}
|
|
127
130
|
/**
|
|
128
|
-
* Establish
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
* @param spec - provider, delegation request, and caller cancellation.
|
|
133
|
-
* @returns the durable child id and the accepted prompt's message id.
|
|
134
|
-
* @throws when continuation services are unavailable or materialization fails.
|
|
131
|
+
* Establish a managed child with backend-specific execution capabilities.
|
|
132
|
+
* @param spec - task, backend, and result recipient.
|
|
133
|
+
* @returns identities, execution result, and exact-activation disposal. A reserved
|
|
134
|
+
* childId requires a local backend and guarantees an accepted messageId.
|
|
135
135
|
*/
|
|
136
|
-
async
|
|
137
|
-
|
|
136
|
+
async startActivation(spec) {
|
|
137
|
+
const provider = this.expectProvider(spec.provider);
|
|
138
|
+
if (spec.childId !== undefined && provider.prepareContinuable === undefined) {
|
|
139
|
+
throw new SubagentError('reserved child ids require a local backend', 'UNSUPPORTED_CAPABILITY');
|
|
140
|
+
}
|
|
141
|
+
assertSubagentMaxDepth(spec.request.maxDepth);
|
|
142
|
+
this.assertCapabilities(provider, { ...spec.request, signal: spec.signal });
|
|
143
|
+
const manager = this.requireManager();
|
|
144
|
+
if (this.ctx.get('agents')?.get(spec.request.parent.id) !== spec.request.parent) {
|
|
145
|
+
throw new SubagentError('subagent creation requires the exact live parent agent', 'UNAUTHORIZED');
|
|
146
|
+
}
|
|
147
|
+
if (spec.request.outputSchema !== undefined)
|
|
148
|
+
assertObjectJsonSchema(spec.request.outputSchema);
|
|
149
|
+
return provider.prepareContinuable === undefined
|
|
150
|
+
? manager.startExternal(spec)
|
|
151
|
+
: manager.startLocal(spec);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Join progressing descendants without cancelling them. Idle descendants whose
|
|
155
|
+
* inboxes require a later wake stay resident and do not delay host completion.
|
|
156
|
+
* @param parent - the exact parent whose descendant work is observed.
|
|
157
|
+
* @returns whether work was joined; hosts recheck parent idle after true.
|
|
158
|
+
*/
|
|
159
|
+
async waitForChildren(parent) {
|
|
160
|
+
return this.manager?.waitForChildren(parent) ?? false;
|
|
138
161
|
}
|
|
139
162
|
/**
|
|
140
163
|
* Steer one model-authored message to the sender's direct parent or direct
|
|
@@ -151,7 +174,7 @@ let SubagentRuntime = (() => {
|
|
|
151
174
|
* or the message was not admitted.
|
|
152
175
|
*/
|
|
153
176
|
async sendMessage(sender, targetId, content, options) {
|
|
154
|
-
return this.
|
|
177
|
+
return this.requireManager().sendMessage(sender, targetId, content, options);
|
|
155
178
|
}
|
|
156
179
|
/**
|
|
157
180
|
* Deliver one host-protocol message to a direct continuable child.
|
|
@@ -167,17 +190,17 @@ let SubagentRuntime = (() => {
|
|
|
167
190
|
*/
|
|
168
191
|
[deliverSubagentPrompt](parent, childId, content, source, signal, delivery) {
|
|
169
192
|
return delivery === 'steer'
|
|
170
|
-
? this.
|
|
171
|
-
: this.
|
|
193
|
+
? this.requireManager().steerPrompt(parent, childId, content, source, signal)
|
|
194
|
+
: this.requireManager().queuePrompt(parent, childId, content, source, signal);
|
|
172
195
|
}
|
|
173
196
|
/**
|
|
174
|
-
* Interrupt one live
|
|
197
|
+
* Interrupt one live child's current execution under a human parent
|
|
175
198
|
* address or an exact live ancestor Agent. Fire-and-return: the cancel
|
|
176
199
|
* signal is issued before this returns, but the target may keep running
|
|
177
200
|
* until it observes the signal. Unclaimed pending inbox work, the Activation,
|
|
178
201
|
* and published descendants are preserved; claimed work is not requeued.
|
|
179
|
-
* Once the interrupted
|
|
180
|
-
* queue.
|
|
202
|
+
* Once the interrupted Agent is idle, a waking send resumes the parked FIFO
|
|
203
|
+
* queue. External backends stop their single execution. An absent target
|
|
181
204
|
* is an accepted no-op, as is a manager-less composition, which cannot own a
|
|
182
205
|
* live Activation.
|
|
183
206
|
* @param targetSessionId - the durable child session id to interrupt.
|
|
@@ -186,37 +209,37 @@ let SubagentRuntime = (() => {
|
|
|
186
209
|
* live target.
|
|
187
210
|
*/
|
|
188
211
|
interrupt(targetSessionId, authority) {
|
|
189
|
-
this.
|
|
212
|
+
this.manager?.interrupt(targetSessionId, authority);
|
|
190
213
|
}
|
|
191
214
|
/**
|
|
192
|
-
* Close
|
|
215
|
+
* Close subagent admission below exact live parent Agents, stop only their
|
|
193
216
|
* visible descendant Activations synchronously, then await admitted scoped
|
|
194
217
|
* materializations and release those forests child-first. The scoped cutoff
|
|
195
218
|
* lasts until each exact parent leaves the registry; unrelated parent trees
|
|
196
219
|
* remain live.
|
|
197
220
|
* @param parents - exact host-owned parent Agents entering teardown.
|
|
198
|
-
* @returns once every retained descendant
|
|
221
|
+
* @returns once every retained descendant activation released its execution handle.
|
|
199
222
|
* @throws an aggregate error after all branches settle when any failed.
|
|
200
223
|
*/
|
|
201
|
-
async
|
|
202
|
-
const manager = this.
|
|
203
|
-
//
|
|
224
|
+
async drainDescendants(parents) {
|
|
225
|
+
const manager = this.manager;
|
|
226
|
+
// An absent activation manager cannot own materialized children.
|
|
204
227
|
if (manager === undefined)
|
|
205
228
|
return;
|
|
206
229
|
await manager.drainDescendants(parents);
|
|
207
230
|
}
|
|
208
231
|
/**
|
|
209
|
-
* Release selected resident
|
|
232
|
+
* Release selected resident direct children of one exact live
|
|
210
233
|
* parent. Other children of the same parent remain admitted and resident.
|
|
211
234
|
* Absent targets and a manager-less composition are accepted no-ops.
|
|
212
235
|
* @param parent - exact live direct parent authorizing the selected release.
|
|
213
236
|
* @param childIds - durable direct-child ids to release when resident.
|
|
214
|
-
* @returns once every selected
|
|
237
|
+
* @returns once every selected activation released its execution handle.
|
|
215
238
|
* @throws {SubagentError} `UNAUTHORIZED` when a resident target belongs to a
|
|
216
239
|
* different parent or the supplied parent identity is stale.
|
|
217
240
|
*/
|
|
218
|
-
async
|
|
219
|
-
const manager = this.
|
|
241
|
+
async drainChildren(parent, childIds) {
|
|
242
|
+
const manager = this.manager;
|
|
220
243
|
if (manager === undefined)
|
|
221
244
|
return;
|
|
222
245
|
await manager.drainChildren(parent, childIds);
|
|
@@ -236,7 +259,8 @@ let SubagentRuntime = (() => {
|
|
|
236
259
|
/**
|
|
237
260
|
* Recursively list reachable parent catalogs in stable pre-order, preserving
|
|
238
261
|
* each catalog's event order. Each row carries its catalog parent and depth;
|
|
239
|
-
* one-shot and unknown-mode children remain
|
|
262
|
+
* external children are leaves; one-shot and unknown-mode children remain
|
|
263
|
+
* traversal nodes. Unknown modes
|
|
240
264
|
* produce unsupported diagnostics. Unreadable child catalogs produce corrupt
|
|
241
265
|
* or unavailable diagnostics and stop only that branch. Root read failures,
|
|
242
266
|
* missing services or projections, and cancellation reject the whole listing.
|
|
@@ -338,12 +362,16 @@ let SubagentRuntime = (() => {
|
|
|
338
362
|
/**
|
|
339
363
|
* Register a provider under its name. Registration is effect-scoped and HMR
|
|
340
364
|
* safe; removing a provider blocks new starts but does not revoke runs that
|
|
341
|
-
* were already returned to their holders.
|
|
365
|
+
* were already returned to their holders. Providers without either execution
|
|
366
|
+
* method are rejected with UNSUPPORTED_CAPABILITY before registration.
|
|
342
367
|
* @param provider - the trusted provider implementation.
|
|
343
368
|
* @returns the exact Cordis effect disposer.
|
|
344
369
|
*/
|
|
345
370
|
registerProvider(provider) {
|
|
346
371
|
const name = provider.name;
|
|
372
|
+
if (provider.start === undefined && provider.prepareContinuable === undefined) {
|
|
373
|
+
throw new SubagentError(`subagent provider "${name}" must implement start or prepareContinuable`, 'UNSUPPORTED_CAPABILITY');
|
|
374
|
+
}
|
|
347
375
|
// oxlint-disable-next-line typescript/no-misused-promises -- synchronous disposer
|
|
348
376
|
return this.ctx.effect(function* () {
|
|
349
377
|
if (this.providers.has(name)) {
|
|
@@ -374,50 +402,6 @@ let SubagentRuntime = (() => {
|
|
|
374
402
|
list() {
|
|
375
403
|
return [...this.providers.keys()];
|
|
376
404
|
}
|
|
377
|
-
/**
|
|
378
|
-
* Establish a published child on the named provider. Capability and semantic
|
|
379
|
-
* checks run before delegation. Provider ownership lasts until its promise
|
|
380
|
-
* fulfills; a rejection therefore has no run for the caller to dispose and
|
|
381
|
-
* emits no run lifecycle events. Post-publication turn and infrastructure
|
|
382
|
-
* failures settle through the returned run.
|
|
383
|
-
* A catalog append failure disposes the run and handles its result rejection;
|
|
384
|
-
* the caller receives the catalog error even if disposal also fails.
|
|
385
|
-
* @param name - the provider to use.
|
|
386
|
-
* @param request - child label, prompt, parent, signal, and optional capabilities.
|
|
387
|
-
* @returns the published holder-owned run.
|
|
388
|
-
*/
|
|
389
|
-
async start(name, request) {
|
|
390
|
-
const provider = this.expectProvider(name);
|
|
391
|
-
this.assertCapabilities(provider, request);
|
|
392
|
-
assertSubagentMaxDepth(request.maxDepth);
|
|
393
|
-
if (request.outputSchema !== undefined)
|
|
394
|
-
assertObjectJsonSchema(request.outputSchema);
|
|
395
|
-
const descriptor = snapshotSubagentDescriptor({
|
|
396
|
-
mode: 'one-shot',
|
|
397
|
-
provider: name,
|
|
398
|
-
...request.label !== undefined ? { label: request.label } : {},
|
|
399
|
-
});
|
|
400
|
-
const resolved = { ...request, descriptor };
|
|
401
|
-
const run = await provider.start(resolved);
|
|
402
|
-
const child = run.localAgent?.session;
|
|
403
|
-
if (child !== undefined) {
|
|
404
|
-
try {
|
|
405
|
-
establishCatalogChild(request.parent.session, child.header, descriptor);
|
|
406
|
-
}
|
|
407
|
-
catch (error) {
|
|
408
|
-
// No caller receives this run; the catalog error owns the failed start.
|
|
409
|
-
void run.result.catch(() => undefined);
|
|
410
|
-
try {
|
|
411
|
-
await run.dispose();
|
|
412
|
-
}
|
|
413
|
-
catch (cleanupError) {
|
|
414
|
-
this.ctx.logger.warn(`subagent: disposal after catalog append failure also failed: ${String(cleanupError)}`);
|
|
415
|
-
}
|
|
416
|
-
throw error;
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
return observeRun(this.emitLifecycle, name, request.parent, run);
|
|
420
|
-
}
|
|
421
405
|
/**
|
|
422
406
|
* Resolve one provider's detached continuable-creation contribution. Method
|
|
423
407
|
* presence on the provider IS the capability, so a provider without it is
|
|
@@ -425,10 +409,6 @@ let SubagentRuntime = (() => {
|
|
|
425
409
|
*/
|
|
426
410
|
async prepareContinuable(name, request) {
|
|
427
411
|
const provider = this.expectProvider(name);
|
|
428
|
-
if (provider.prepareContinuable === undefined) {
|
|
429
|
-
throw new SubagentError(`subagent provider "${provider.name}" does not support continuable children `
|
|
430
|
-
+ '(no prepareContinuable capability)', 'UNSUPPORTED_CAPABILITY');
|
|
431
|
-
}
|
|
432
412
|
return provider.prepareContinuable(request);
|
|
433
413
|
}
|
|
434
414
|
/** Look up a provider for dispatch or fail loud. */
|
|
@@ -439,12 +419,12 @@ let SubagentRuntime = (() => {
|
|
|
439
419
|
}
|
|
440
420
|
return provider;
|
|
441
421
|
}
|
|
442
|
-
/** Resolve the
|
|
443
|
-
|
|
444
|
-
if (this.
|
|
422
|
+
/** Resolve the subagent manager or fail loud. */
|
|
423
|
+
requireManager() {
|
|
424
|
+
if (this.manager === undefined) {
|
|
445
425
|
throw new SubagentError('continuable subagents require the agents service', 'CONTINUATION_UNAVAILABLE');
|
|
446
426
|
}
|
|
447
|
-
return this.
|
|
427
|
+
return this.manager;
|
|
448
428
|
}
|
|
449
429
|
/**
|
|
450
430
|
* Build the lifecycle observer for one continuable Activation's residency
|
package/lib/types/internal.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ import type { ContentBlock, MessageId, MessageSource } from '@deepseek-ai/dsh-ll
|
|
|
8
8
|
import type { SessionId } from '@deepseek-ai/dsh-session';
|
|
9
9
|
import type { ToolDefinition } from '@deepseek-ai/dsh-tools';
|
|
10
10
|
import type SubagentRuntime from './index.ts';
|
|
11
|
-
import type { SubagentDelivery } from './
|
|
11
|
+
import type { SubagentDelivery } from './control-types.ts';
|
|
12
12
|
/** Process-stable identity carried only by the standard adjacent-Agent messaging tool. */
|
|
13
13
|
export declare const adjacentAgentSendMessageTool: unique symbol;
|
|
14
14
|
/**
|
package/lib/types/lifecycle.d.ts
CHANGED
|
@@ -1,69 +1,34 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Lifecycle-edge publication for
|
|
3
|
-
* the one-shot run observer, and the continuable Activation observer.
|
|
2
|
+
* Lifecycle-edge publication for managed subagent activations.
|
|
4
3
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* consumer-facing types; this module owns only the implementation and the
|
|
8
|
-
* package-private {@link ActivationObserver} the continuation manager consumes.
|
|
9
|
-
* Keeping the internal control interface out of the published surface is
|
|
10
|
-
* deliberate: the observer's `start`/`capture`/`settle` ordering is a contract
|
|
11
|
-
* between this module and one in-package caller, not something a plugin may
|
|
12
|
-
* depend on.
|
|
4
|
+
* Public event payloads live in `./types.ts`; the package-private observer
|
|
5
|
+
* retains only the identity shared by each start/end pair.
|
|
13
6
|
*
|
|
14
7
|
* @module @deepseek-ai/dsh-subagent/lifecycle
|
|
15
8
|
*/
|
|
16
9
|
import type { Context } from '@deepseek-ai/cordis';
|
|
17
10
|
import type { Agent } from '@deepseek-ai/dsh-agent';
|
|
18
|
-
import type {
|
|
19
|
-
import type {
|
|
20
|
-
import type { SubagentResult, SubagentRun, SubagentRunEndInfo, SubagentRunInfo } from './types.ts';
|
|
11
|
+
import type { SessionEvent, SessionId } from '@deepseek-ai/dsh-session';
|
|
12
|
+
import type { SubagentResult, SubagentRunEndInfo, SubagentRunInfo } from './types.ts';
|
|
21
13
|
/**
|
|
22
|
-
*
|
|
23
|
-
* edge and the manager's own parent delivery report it.
|
|
24
|
-
*/
|
|
25
|
-
export interface ActivationTerminal {
|
|
26
|
-
/** Why this epoch's last ordinary turn ended, or `error` when teardown failed. */
|
|
27
|
-
readonly stopReason: SubagentResult['stopReason'];
|
|
28
|
-
/** The epoch's final assistant content, absent when it produced none or failed. */
|
|
29
|
-
readonly output?: readonly ContentBlock[];
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Lifecycle observer for one Activation's residency epoch, so continuable
|
|
33
|
-
* children emit the same start/end pair as one-shot runs. Package-private: the
|
|
14
|
+
* Lifecycle observer for one activation's residency epoch. Package-private: the
|
|
34
15
|
* continuation manager is the only consumer, and its call ordering is an
|
|
35
16
|
* in-package contract rather than a published extension point.
|
|
36
17
|
*/
|
|
37
18
|
export interface ActivationObserver {
|
|
38
19
|
/**
|
|
39
20
|
* Publish the start edge once the epoch is resident.
|
|
40
|
-
* @param child - the resident
|
|
41
|
-
*/
|
|
42
|
-
start(child: Agent): void;
|
|
43
|
-
/**
|
|
44
|
-
* Snapshot the child-dependent terminal facts while the child is still
|
|
45
|
-
* registered, because handle disposal unregisters it and consumers resolve it
|
|
46
|
-
* to read the child's own log and scope.
|
|
47
|
-
* @param child - the quiescent child agent about to be released.
|
|
48
|
-
*/
|
|
49
|
-
capture(child: Agent): void;
|
|
50
|
-
/**
|
|
51
|
-
* Resolve the terminal facts {@link settle} will publish, without publishing
|
|
52
|
-
* them. The manager's parent delivery must run before the ownership release
|
|
53
|
-
* that lets the parent settle, which is earlier than the terminal edge; both
|
|
54
|
-
* therefore read one computation instead of restating the failure rule.
|
|
55
|
-
* @param failure - the teardown or durability failure, or `undefined` on success.
|
|
56
|
-
* @returns this epoch's stop reason and final assistant content.
|
|
21
|
+
* @param child - the resident local Agent, or undefined for an external execution.
|
|
57
22
|
*/
|
|
58
|
-
|
|
23
|
+
start(child?: Agent): void;
|
|
59
24
|
/**
|
|
60
25
|
* Publish the terminal edge exactly once, pairing this epoch's {@link start},
|
|
61
26
|
* after the disposal outcome is known. Called only for a resident epoch: a
|
|
62
27
|
* failure before residency publishes no edge, because inventing one would
|
|
63
28
|
* report a lifecycle the child never had.
|
|
64
|
-
* @param
|
|
29
|
+
* @param result - the captured result, with teardown failures applied.
|
|
65
30
|
*/
|
|
66
|
-
settle(
|
|
31
|
+
settle(result: SubagentResult): void;
|
|
67
32
|
}
|
|
68
33
|
/**
|
|
69
34
|
* Publish one lifecycle edge with per-listener exception containment. Run edges
|
|
@@ -90,19 +55,7 @@ export type LifecycleEmitter = {
|
|
|
90
55
|
*/
|
|
91
56
|
export declare function createLifecycleEmitter(ctx: Context, carrier: (parent: Agent) => object): LifecycleEmitter;
|
|
92
57
|
/**
|
|
93
|
-
*
|
|
94
|
-
* @param emit - the contained lifecycle emitter.
|
|
95
|
-
* @param provider - the provider that established the run.
|
|
96
|
-
* @param parent - the delegating parent keying scoped dispatch.
|
|
97
|
-
* @param run - the published run whose settlement closes the pair.
|
|
98
|
-
* @returns the same run, unchanged.
|
|
99
|
-
*/
|
|
100
|
-
export declare function observeRun(emit: LifecycleEmitter, provider: string, parent: Agent, run: SubagentRun): SubagentRun;
|
|
101
|
-
/**
|
|
102
|
-
* Build the observer for one continuable Activation's residency epoch. Observers
|
|
103
|
-
* see the same vocabulary as a one-shot run, so a child's start and settlement
|
|
104
|
-
* remain observable without exposing whether the manager materialized, woke, or
|
|
105
|
-
* cold-resumed it. Creation failure before residency emits no lifecycle edge.
|
|
58
|
+
* Build the observer for one local or external activation's residency epoch. Creation failure before residency emits no lifecycle edge.
|
|
106
59
|
* @param emit - the contained lifecycle emitter.
|
|
107
60
|
* @param provider - the provider name recorded in the durable descriptor.
|
|
108
61
|
* @param childId - the durable child session id.
|
|
@@ -110,4 +63,21 @@ export declare function observeRun(emit: LifecycleEmitter, provider: string, par
|
|
|
110
63
|
* @returns the observer whose edges this epoch publishes.
|
|
111
64
|
*/
|
|
112
65
|
export declare function createActivationObserver(emit: LifecycleEmitter, provider: string, childId: SessionId, parent: Agent): ActivationObserver;
|
|
66
|
+
/**
|
|
67
|
+
* Why this child's epoch ended, for the terminal lifecycle edge and the
|
|
68
|
+
* manager's own parent delivery. The child's own log is authoritative:
|
|
69
|
+
* teardown succeeding says nothing about whether the model errored, hit its
|
|
70
|
+
* token ceiling, or was cancelled, so deriving the reason from disposal would
|
|
71
|
+
* report failed work as completed.
|
|
72
|
+
*
|
|
73
|
+
* {@link foldConsumedWork} supplies both halves the raw turn sequence cannot:
|
|
74
|
+
* which turn accounts for the work this epoch consumed, and whether accepted
|
|
75
|
+
* work was cancelled after it without any turn opening over it. A recorded
|
|
76
|
+
* failure still wins over a cancellation — stopping a child that had already
|
|
77
|
+
* failed does not turn its failure into a cancellation.
|
|
78
|
+
* @param events - this epoch's own event suffix.
|
|
79
|
+
* @returns its terminal stop reason; `completed` only for an epoch that both
|
|
80
|
+
* closed cleanly and had nothing left to run.
|
|
81
|
+
*/
|
|
82
|
+
export declare function epochStopReason(events: readonly SessionEvent[]): SubagentResult['stopReason'];
|
|
113
83
|
//# sourceMappingURL=lifecycle.d.ts.map
|
package/lib/types/lifecycle.js
CHANGED
|
@@ -1,22 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Lifecycle-edge publication for
|
|
3
|
-
* the one-shot run observer, and the continuable Activation observer.
|
|
2
|
+
* Lifecycle-edge publication for managed subagent activations.
|
|
4
3
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* consumer-facing types; this module owns only the implementation and the
|
|
8
|
-
* package-private {@link ActivationObserver} the continuation manager consumes.
|
|
9
|
-
* Keeping the internal control interface out of the published surface is
|
|
10
|
-
* deliberate: the observer's `start`/`capture`/`settle` ordering is a contract
|
|
11
|
-
* between this module and one in-package caller, not something a plugin may
|
|
12
|
-
* depend on.
|
|
4
|
+
* Public event payloads live in `./types.ts`; the package-private observer
|
|
5
|
+
* retains only the identity shared by each start/end pair.
|
|
13
6
|
*
|
|
14
7
|
* @module @deepseek-ai/dsh-subagent/lifecycle
|
|
15
8
|
*/
|
|
16
9
|
import { randomUUID } from 'node:crypto';
|
|
17
10
|
import { foldConsumedWork } from '@deepseek-ai/dsh-agent';
|
|
18
|
-
import { SessionLogOffset } from '@deepseek-ai/dsh-session';
|
|
19
|
-
import { finalAssistantOutput } from "./assistant-output.js";
|
|
20
11
|
import { SubagentRunId } from "./types.js";
|
|
21
12
|
/**
|
|
22
13
|
* Build the contained lifecycle emitter this seam publishes every edge through.
|
|
@@ -46,40 +37,7 @@ export function createLifecycleEmitter(ctx, carrier) {
|
|
|
46
37
|
};
|
|
47
38
|
}
|
|
48
39
|
/**
|
|
49
|
-
*
|
|
50
|
-
* @param emit - the contained lifecycle emitter.
|
|
51
|
-
* @param provider - the provider that established the run.
|
|
52
|
-
* @param parent - the delegating parent keying scoped dispatch.
|
|
53
|
-
* @param run - the published run whose settlement closes the pair.
|
|
54
|
-
* @returns the same run, unchanged.
|
|
55
|
-
*/
|
|
56
|
-
export function observeRun(emit, provider, parent, run) {
|
|
57
|
-
const identity = {
|
|
58
|
-
runId: SubagentRunId(randomUUID()),
|
|
59
|
-
provider,
|
|
60
|
-
id: run.id,
|
|
61
|
-
local: run.localAgent !== undefined,
|
|
62
|
-
};
|
|
63
|
-
// Attach the terminal observer before dispatching start. Promise reactions
|
|
64
|
-
// still run after this synchronous start emission, preserving start → end.
|
|
65
|
-
void run.result.then((result) => {
|
|
66
|
-
emit('subagent/end', {
|
|
67
|
-
...identity,
|
|
68
|
-
stopReason: result.stopReason,
|
|
69
|
-
// Omit the field when no output exists, matching continuable epochs.
|
|
70
|
-
...result.output.length === 0 ? {} : { lastAssistantMessage: result.output },
|
|
71
|
-
}, parent);
|
|
72
|
-
}, () => {
|
|
73
|
-
emit('subagent/end', { ...identity, stopReason: 'error' }, parent);
|
|
74
|
-
});
|
|
75
|
-
emit('subagent/start', identity, parent);
|
|
76
|
-
return run;
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Build the observer for one continuable Activation's residency epoch. Observers
|
|
80
|
-
* see the same vocabulary as a one-shot run, so a child's start and settlement
|
|
81
|
-
* remain observable without exposing whether the manager materialized, woke, or
|
|
82
|
-
* cold-resumed it. Creation failure before residency emits no lifecycle edge.
|
|
40
|
+
* Build the observer for one local or external activation's residency epoch. Creation failure before residency emits no lifecycle edge.
|
|
83
41
|
* @param emit - the contained lifecycle emitter.
|
|
84
42
|
* @param provider - the provider name recorded in the durable descriptor.
|
|
85
43
|
* @param childId - the durable child session id.
|
|
@@ -87,40 +45,17 @@ export function observeRun(emit, provider, parent, run) {
|
|
|
87
45
|
* @returns the observer whose edges this epoch publishes.
|
|
88
46
|
*/
|
|
89
47
|
export function createActivationObserver(emit, provider, childId, parent) {
|
|
90
|
-
const identity = { runId: SubagentRunId(randomUUID()), provider, id: childId, local:
|
|
91
|
-
// A cold resume replays earlier turns, so this epoch's telemetry must come
|
|
92
|
-
// from the suffix it actually produced — never the whole session, which
|
|
93
|
-
// would report a previous epoch's answer when this one opened no turn.
|
|
94
|
-
let boundary = SessionLogOffset(0);
|
|
95
|
-
// Assigned by `capture()`, which the disposal path always runs before
|
|
96
|
-
// `settle()`; a resident epoch therefore always has its facts by then.
|
|
97
|
-
let captured = { stopReason: 'completed' };
|
|
98
|
-
// Teardown failure overrides the epoch's own outcome and withholds its
|
|
99
|
-
// output: an answer this harness could not durably release is not a result.
|
|
100
|
-
const terminal = (failure) => failure === undefined
|
|
101
|
-
? captured
|
|
102
|
-
: { stopReason: 'error' };
|
|
48
|
+
const identity = { runId: SubagentRunId(randomUUID()), provider, id: childId, local: false };
|
|
103
49
|
return {
|
|
104
50
|
start: (child) => {
|
|
105
|
-
|
|
51
|
+
identity.local = child !== undefined;
|
|
106
52
|
emit('subagent/start', identity, parent);
|
|
107
53
|
},
|
|
108
|
-
|
|
109
|
-
// oxlint-disable-next-line typescript/no-deprecated -- Existing Session history read; migration deferred.
|
|
110
|
-
const own = child.session.snapshotEvents(boundary);
|
|
111
|
-
const output = finalAssistantOutput(own);
|
|
112
|
-
captured = {
|
|
113
|
-
stopReason: epochStopReason(own),
|
|
114
|
-
...output === undefined ? {} : { output },
|
|
115
|
-
};
|
|
116
|
-
},
|
|
117
|
-
terminal,
|
|
118
|
-
settle: (failure) => {
|
|
119
|
-
const { stopReason, output } = terminal(failure);
|
|
54
|
+
settle: ({ stopReason, output }) => {
|
|
120
55
|
emit('subagent/end', {
|
|
121
56
|
...identity,
|
|
122
57
|
stopReason,
|
|
123
|
-
...output ===
|
|
58
|
+
...output.length === 0 ? {} : { lastAssistantMessage: output },
|
|
124
59
|
}, parent);
|
|
125
60
|
},
|
|
126
61
|
};
|
|
@@ -141,7 +76,7 @@ export function createActivationObserver(emit, provider, childId, parent) {
|
|
|
141
76
|
* @returns its terminal stop reason; `completed` only for an epoch that both
|
|
142
77
|
* closed cleanly and had nothing left to run.
|
|
143
78
|
*/
|
|
144
|
-
function epochStopReason(events) {
|
|
79
|
+
export function epochStopReason(events) {
|
|
145
80
|
const { end, droppedUnrun } = foldConsumedWork(events);
|
|
146
81
|
switch (end?.data.reason.kind) {
|
|
147
82
|
case 'max-tokens':
|
|
@@ -126,7 +126,7 @@ export async function listDescendants(ctx, rootSessionId, signal) {
|
|
|
126
126
|
visited.add(entry.id);
|
|
127
127
|
let children;
|
|
128
128
|
try {
|
|
129
|
-
children = await readChildren(entry.id);
|
|
129
|
+
children = entry.mode === 'external' ? [] : await readChildren(entry.id);
|
|
130
130
|
}
|
|
131
131
|
catch (error) {
|
|
132
132
|
if (error instanceof SubagentError)
|