@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/types/index.js
CHANGED
|
@@ -41,13 +41,11 @@ import { Remote, RemoteError, TypertRemoteService, principalOf } from '@deepseek
|
|
|
41
41
|
import { rejectPrompt, validateControlRequest, } from "./control.js";
|
|
42
42
|
import { SubagentError } from "./error.js";
|
|
43
43
|
import { assertSubagentMaxDepth } from "./depth.js";
|
|
44
|
-
import {
|
|
44
|
+
import { createLifecycleEmitter } from "./lifecycle.js";
|
|
45
45
|
import SubagentManager from "./manager.js";
|
|
46
|
-
import {
|
|
47
|
-
import { installSubagentArchiveAdmission } from "./archive-admission.js";
|
|
46
|
+
import { listDescendants as listSubagentDescendants } from "./list-children.js";
|
|
48
47
|
import { subagentIdentityProjectionDefinition, subagentTimingProjectionDefinition } from "./projection.js";
|
|
49
48
|
import { subagentCatalogProjectionDefinition } from "./catalog.js";
|
|
50
|
-
import { deliverSubagentPrompt } from "./internal.js";
|
|
51
49
|
export * from "./out-of-process.js";
|
|
52
50
|
export { AssistantOutputFold, finalAssistantOutput } from "./assistant-output.js";
|
|
53
51
|
export { SubagentRunId } from "./types.js";
|
|
@@ -61,14 +59,14 @@ let SubagentRuntime = (() => {
|
|
|
61
59
|
let _classSuper = TypertRemoteService;
|
|
62
60
|
let _instanceExtraInitializers = [];
|
|
63
61
|
let _prompt_decorators;
|
|
64
|
-
let
|
|
62
|
+
let _interruptByUser_decorators;
|
|
65
63
|
return class SubagentRuntime extends _classSuper {
|
|
66
64
|
static {
|
|
67
65
|
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
68
|
-
_prompt_decorators = [Remote('prompt')];
|
|
69
|
-
|
|
66
|
+
_prompt_decorators = [Remote({ name: 'prompt', resources: { session: request => request.childSessionId } })];
|
|
67
|
+
_interruptByUser_decorators = [Remote({ name: 'interruptByUser', resources: { session: childSessionId => childSessionId } })];
|
|
70
68
|
__esDecorate(this, null, _prompt_decorators, { kind: "method", name: "prompt", static: false, private: false, access: { has: obj => "prompt" in obj, get: obj => obj.prompt }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
71
|
-
__esDecorate(this, null,
|
|
69
|
+
__esDecorate(this, null, _interruptByUser_decorators, { kind: "method", name: "interruptByUser", static: false, private: false, access: { has: obj => "interruptByUser" in obj, get: obj => obj.interruptByUser }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
72
70
|
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
73
71
|
}
|
|
74
72
|
config = __runInitializers(this, _instanceExtraInitializers);
|
|
@@ -90,14 +88,18 @@ let SubagentRuntime = (() => {
|
|
|
90
88
|
super(ctx, 'subagents');
|
|
91
89
|
this.config = config;
|
|
92
90
|
this.emitLifecycle = createLifecycleEmitter(this.ctx, parent => scopeTarget(this, parent));
|
|
91
|
+
}
|
|
92
|
+
async [Service.init]() {
|
|
93
|
+
const ctx = this.ctx;
|
|
94
|
+
await ctx.sessionProjections.register(subagentCatalogProjectionDefinition);
|
|
95
|
+
await ctx.sessionProjections.register(subagentTimingProjectionDefinition);
|
|
96
|
+
const identityProjection = await ctx.sessionProjections.register(subagentIdentityProjectionDefinition);
|
|
97
|
+
if (ctx.fiber.uid === null)
|
|
98
|
+
return;
|
|
93
99
|
ctx.inject(['agents'], (childCtx) => {
|
|
94
100
|
const manager = new SubagentManager(childCtx, {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
return provider.start(request);
|
|
98
|
-
},
|
|
99
|
-
prepareContinuable: (name, request) => this.prepareContinuable(name, request),
|
|
100
|
-
observeActivation: (provider, childId, parent) => this.observeActivation(provider, childId, parent),
|
|
101
|
+
identityOf: session => identityProjection.stateOf(session).identity,
|
|
102
|
+
emitLifecycle: this.emitLifecycle,
|
|
101
103
|
}, () => this.config.maxActiveSubagents.get(), () => this.config.delegatedApproval.get());
|
|
102
104
|
this.manager = manager;
|
|
103
105
|
childCtx.effect(() => () => {
|
|
@@ -106,15 +108,6 @@ let SubagentRuntime = (() => {
|
|
|
106
108
|
this.manager = undefined;
|
|
107
109
|
}, 'subagents.managerBinding()');
|
|
108
110
|
});
|
|
109
|
-
// Archive admission: this runtime is the owner that knows which live
|
|
110
|
-
// children descend from a Session and how a parent stops them.
|
|
111
|
-
ctx.inject(['agents'], (agentsCtx) => { installSubagentArchiveAdmission(agentsCtx); });
|
|
112
|
-
}
|
|
113
|
-
async [Service.init]() {
|
|
114
|
-
const { sessionProjections } = this.ctx;
|
|
115
|
-
await sessionProjections.register(subagentCatalogProjectionDefinition);
|
|
116
|
-
await sessionProjections.register(subagentTimingProjectionDefinition);
|
|
117
|
-
await sessionProjections.register(subagentIdentityProjectionDefinition);
|
|
118
111
|
}
|
|
119
112
|
/**
|
|
120
113
|
* Resolve a delegation tool's depth policy against the current user setting.
|
|
@@ -135,70 +128,72 @@ let SubagentRuntime = (() => {
|
|
|
135
128
|
* @param spec - task, backend, and result recipient.
|
|
136
129
|
* @returns identities, execution result, and exact-activation disposal. A reserved
|
|
137
130
|
* childId requires a local backend and guarantees an accepted messageId.
|
|
131
|
+
* @throws PROVIDER_CHANGED if the selected provider is removed or replaced before dispatch.
|
|
138
132
|
*/
|
|
139
133
|
async startActivation(spec) {
|
|
140
134
|
const provider = this.expectProvider(spec.provider);
|
|
141
135
|
if (spec.childId !== undefined && provider.prepareContinuable === undefined) {
|
|
142
136
|
throw new SubagentError('reserved child ids require a local backend', 'UNSUPPORTED_CAPABILITY');
|
|
143
137
|
}
|
|
144
|
-
assertSubagentMaxDepth(spec.
|
|
145
|
-
this.assertCapabilities(provider,
|
|
138
|
+
assertSubagentMaxDepth(spec.maxDepth);
|
|
139
|
+
this.assertCapabilities(provider, spec);
|
|
146
140
|
const manager = this.requireManager();
|
|
147
|
-
if (this.ctx.get('agents')?.get(spec.
|
|
141
|
+
if (this.ctx.get('agents')?.get(spec.parent.id) !== spec.parent) {
|
|
148
142
|
throw new SubagentError('subagent creation requires the exact live parent agent', 'UNAUTHORIZED');
|
|
149
143
|
}
|
|
150
|
-
if (spec.
|
|
151
|
-
assertObjectJsonSchema(spec.
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
144
|
+
if (spec.outputSchema !== undefined)
|
|
145
|
+
assertObjectJsonSchema(spec.outputSchema);
|
|
146
|
+
const prepare = provider.prepareContinuable?.bind(provider);
|
|
147
|
+
if (prepare !== undefined) {
|
|
148
|
+
return manager.startLocal(spec, (request) => {
|
|
149
|
+
this.assertProviderCurrent(provider);
|
|
150
|
+
return prepare(request);
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
// oxlint-disable-next-line typescript/no-non-null-assertion -- registerProvider requires start when prepareContinuable is absent.
|
|
154
|
+
const start = provider.start.bind(provider);
|
|
155
|
+
return manager.startExternal(spec, (request) => {
|
|
156
|
+
this.assertProviderCurrent(provider);
|
|
157
|
+
return start(request);
|
|
158
|
+
});
|
|
155
159
|
}
|
|
156
160
|
/**
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
* @
|
|
160
|
-
* @
|
|
161
|
+
* Resolve the root shared by an Agent and its other agents in its subagent tree.
|
|
162
|
+
* @param caller - exact live Agent whose subagent ancestry selects the tree.
|
|
163
|
+
* @returns the live root; ordinary Session forks lead independent trees.
|
|
164
|
+
* @throws when the caller or its runtime owner is unavailable.
|
|
161
165
|
*/
|
|
162
|
-
|
|
163
|
-
return this.
|
|
166
|
+
rootOf(caller) {
|
|
167
|
+
return this.requireManager().rootOf(caller);
|
|
164
168
|
}
|
|
165
169
|
/**
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
170
|
+
* Join progressing subagent tree work for its root. Idle members with parked input
|
|
171
|
+
* stay resident without delaying host completion. Ordinary members return false.
|
|
172
|
+
* @param root - exact live subagent tree root whose member work is awaited.
|
|
173
|
+
* @returns whether work was joined; hosts recheck root idle after true.
|
|
174
|
+
*/
|
|
175
|
+
async waitForTreeWork(root) {
|
|
176
|
+
return this.manager?.waitForTreeWork(root) ?? false;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Steer one model-authored message to another member of the same subagent tree.
|
|
180
|
+
* A running target receives it at its next step; an idle target starts a turn.
|
|
181
|
+
* A cold target resumes under its runtime subagent tree root without restoring
|
|
182
|
+
* its creation ancestors. Sender identity is rechecked before inbox acceptance; cancellation
|
|
183
|
+
* stops only pre-acceptance work and never retracts an accepted message.
|
|
184
|
+
* @param sender - exact live root or resident continuable Agent originating the message.
|
|
185
|
+
* @param targetId - another root or continuable member's durable Session id.
|
|
173
186
|
* @param content - model-authored content to deliver.
|
|
174
187
|
* @param options - caller cancellation before inbox acceptance.
|
|
175
188
|
* @returns the accepted message's inbox id.
|
|
176
|
-
* @throws when
|
|
177
|
-
* or the message was not admitted.
|
|
189
|
+
* @throws when the target is outside the tree, cannot resume, or rejects input.
|
|
178
190
|
*/
|
|
179
191
|
async sendMessage(sender, targetId, content, options) {
|
|
180
192
|
return this.requireManager().sendMessage(sender, targetId, content, options);
|
|
181
193
|
}
|
|
182
194
|
/**
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
* widening the public Service Definition or impersonating an Agent sender.
|
|
186
|
-
* @param parent - exact live direct parent authorizing delivery.
|
|
187
|
-
* @param childId - durable direct-child session id.
|
|
188
|
-
* @param content - host-authored content to deliver.
|
|
189
|
-
* @param source - durable host-protocol source descriptor.
|
|
190
|
-
* @param signal - caller cancellation before inbox acceptance.
|
|
191
|
-
* @param delivery - Queue as a distinct turn or Steer at the nearest step.
|
|
192
|
-
* @returns the accepted message's inbox id.
|
|
193
|
-
*/
|
|
194
|
-
[deliverSubagentPrompt](parent, childId, content, source, signal, delivery) {
|
|
195
|
-
return delivery === 'steer'
|
|
196
|
-
? this.requireManager().steerPrompt(parent, childId, content, source, signal)
|
|
197
|
-
: this.requireManager().queuePrompt(parent, childId, content, source, signal);
|
|
198
|
-
}
|
|
199
|
-
/**
|
|
200
|
-
* Interrupt one live child's current execution under a human parent
|
|
201
|
-
* address or an exact live ancestor Agent. Fire-and-return: the cancel
|
|
195
|
+
* Interrupt one live child's current execution under a trusted user
|
|
196
|
+
* operation or an exact live ancestor Agent. Fire-and-return: the cancel
|
|
202
197
|
* signal is issued before this returns, but the target may keep running
|
|
203
198
|
* until it observes the signal. Unclaimed pending inbox work, the Activation,
|
|
204
199
|
* and published descendants are preserved; claimed work is not requeued.
|
|
@@ -207,7 +202,7 @@ let SubagentRuntime = (() => {
|
|
|
207
202
|
* is an accepted no-op, as is a manager-less composition, which cannot own a
|
|
208
203
|
* live Activation.
|
|
209
204
|
* @param targetSessionId - the durable child session id to interrupt.
|
|
210
|
-
* @param authority -
|
|
205
|
+
* @param authority - a trusted user operation or exact live ancestor Agent.
|
|
211
206
|
* @throws {SubagentError} `UNAUTHORIZED` when the authority does not own the
|
|
212
207
|
* live target.
|
|
213
208
|
*/
|
|
@@ -215,13 +210,11 @@ let SubagentRuntime = (() => {
|
|
|
215
210
|
this.manager?.interrupt(targetSessionId, authority);
|
|
216
211
|
}
|
|
217
212
|
/**
|
|
218
|
-
* Close
|
|
219
|
-
*
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
* @param parents - exact host-owned parent Agents entering teardown.
|
|
224
|
-
* @returns once every retained descendant activation released its execution handle.
|
|
213
|
+
* Close admission and stop all instances in the selected subagent trees, then await
|
|
214
|
+
* pending startup and cleanup. Ordinary members are ignored. The cutoff lasts
|
|
215
|
+
* until each exact root leaves the registry; unrelated subagent trees remain live.
|
|
216
|
+
* @param parents - exact live subagent tree roots entering teardown.
|
|
217
|
+
* @returns once all admitted startup and member cleanup have settled.
|
|
225
218
|
* @throws an aggregate error after all branches settle when any failed.
|
|
226
219
|
*/
|
|
227
220
|
async drainDescendants(parents) {
|
|
@@ -231,34 +224,6 @@ let SubagentRuntime = (() => {
|
|
|
231
224
|
return;
|
|
232
225
|
await manager.drainDescendants(parents);
|
|
233
226
|
}
|
|
234
|
-
/**
|
|
235
|
-
* Release selected resident direct children of one exact live
|
|
236
|
-
* parent. Other children of the same parent remain admitted and resident.
|
|
237
|
-
* Absent targets and a manager-less composition are accepted no-ops.
|
|
238
|
-
* @param parent - exact live direct parent authorizing the selected release.
|
|
239
|
-
* @param childIds - durable direct-child ids to release when resident.
|
|
240
|
-
* @returns once every selected activation released its execution handle.
|
|
241
|
-
* @throws {SubagentError} `UNAUTHORIZED` when a resident target belongs to a
|
|
242
|
-
* different parent or the supplied parent identity is stale.
|
|
243
|
-
*/
|
|
244
|
-
async drainChildren(parent, childIds) {
|
|
245
|
-
const manager = this.manager;
|
|
246
|
-
if (manager === undefined)
|
|
247
|
-
return;
|
|
248
|
-
await manager.drainChildren(parent, childIds);
|
|
249
|
-
}
|
|
250
|
-
/**
|
|
251
|
-
* Read the parent's durable direct-child catalog without loading or resuming an Agent.
|
|
252
|
-
* The service owns and releases the live-preferred Session observation.
|
|
253
|
-
* @param parentSessionId - parent whose direct children are requested.
|
|
254
|
-
* @param signal - cancellation forwarded to the Session query.
|
|
255
|
-
* @returns catalog children in parent event order.
|
|
256
|
-
* @throws {@link SubagentError} when query or catalog projection is unavailable.
|
|
257
|
-
* @throws SessionQueryError when the parent cannot be read or the query is cancelled.
|
|
258
|
-
*/
|
|
259
|
-
listChildren(parentSessionId, signal) {
|
|
260
|
-
return listSubagentChildren(this.ctx, parentSessionId, signal);
|
|
261
|
-
}
|
|
262
227
|
/**
|
|
263
228
|
* Recursively list reachable parent catalogs in stable pre-order, preserving
|
|
264
229
|
* each catalog's event order. Each row carries its catalog parent and depth;
|
|
@@ -279,8 +244,8 @@ let SubagentRuntime = (() => {
|
|
|
279
244
|
return listSubagentDescendants(this.ctx, rootSessionId, signal);
|
|
280
245
|
}
|
|
281
246
|
/**
|
|
282
|
-
* Deliver one browser-authored message to a continuable child through
|
|
283
|
-
*
|
|
247
|
+
* Deliver one browser-authored message to a continuable child through its
|
|
248
|
+
* live subagent tree root, retaining the caller-minted request identity and
|
|
284
249
|
* validated browser zone on the accepted message. Success identifies the
|
|
285
250
|
* message the child's inbox accepted; later execution is independent of this
|
|
286
251
|
* call. Queue delivery targets a later turn; steer delivery targets the
|
|
@@ -288,16 +253,17 @@ let SubagentRuntime = (() => {
|
|
|
288
253
|
* Image parts are admitted and persisted through the attachment store
|
|
289
254
|
* before delivery, and the child's model must accept image input.
|
|
290
255
|
* Cold resume at capacity rejects with `subagent/delivery-unavailable`.
|
|
291
|
-
*
|
|
256
|
+
* Access is checked on the target Session before attachment admission or restoration.
|
|
257
|
+
* @param request - target id, delivery, minted identity, content, and optional browser zone.
|
|
292
258
|
* @param signal - carrier cancellation, owning the call until inbox acceptance.
|
|
293
259
|
* @returns the accepted message's inbox identity.
|
|
294
260
|
* @throws {RemoteError} `gateway/bad-request`, `subagent/attachment-invalid`,
|
|
295
|
-
* `subagent/invalid-time-zone`,
|
|
261
|
+
* `subagent/invalid-time-zone`,
|
|
296
262
|
* `subagent/not-resumable`, `subagent/unauthorized`,
|
|
297
263
|
* `subagent/delivery-unavailable`, `gateway/cancelled`, or `gateway/internal`.
|
|
298
264
|
*/
|
|
299
265
|
async prompt(request, signal) {
|
|
300
|
-
const {
|
|
266
|
+
const { childSessionId, clientTimeZone, delivery } = request;
|
|
301
267
|
validateControlRequest('subagent.prompt', request);
|
|
302
268
|
const canonicalTimeZone = clientTimeZone === undefined
|
|
303
269
|
? undefined
|
|
@@ -305,10 +271,6 @@ let SubagentRuntime = (() => {
|
|
|
305
271
|
if (clientTimeZone !== undefined && canonicalTimeZone === undefined) {
|
|
306
272
|
throw new RemoteError('subagent/invalid-time-zone', 'clientTimeZone must be UTC or a valid IANA Area/Location name', { value: clientTimeZone });
|
|
307
273
|
}
|
|
308
|
-
const parent = this.ctx.get('agents')?.get(parentSessionId);
|
|
309
|
-
if (parent === undefined) {
|
|
310
|
-
throw new RemoteError('subagent/parent-unavailable', `parent session "${parentSessionId}" is not live`, { parentSessionId });
|
|
311
|
-
}
|
|
312
274
|
const principal = principalOf(this.ctx.invocation);
|
|
313
275
|
const source = {
|
|
314
276
|
kind: 'user',
|
|
@@ -330,7 +292,7 @@ let SubagentRuntime = (() => {
|
|
|
330
292
|
content = await attachments.admitPromptContent(request.content);
|
|
331
293
|
}
|
|
332
294
|
return {
|
|
333
|
-
messageId: await this
|
|
295
|
+
messageId: await this.requireManager().deliverPrompt(childSessionId, content, { source, signal, delivery }),
|
|
334
296
|
};
|
|
335
297
|
}
|
|
336
298
|
catch (error) {
|
|
@@ -338,28 +300,20 @@ let SubagentRuntime = (() => {
|
|
|
338
300
|
}
|
|
339
301
|
}
|
|
340
302
|
/**
|
|
341
|
-
* Remote face of {@link interrupt}
|
|
342
|
-
* catalog, history, persistence, or parent Agent lookup runs
|
|
343
|
-
* primitive alone authorizes the address against the live Activation, which
|
|
344
|
-
* is what keeps a live child interruptible while its parent Agent is offline.
|
|
303
|
+
* Remote face of {@link interrupt}, authorized on the target Session by the Gateway.
|
|
304
|
+
* No catalog, history, persistence, or parent Agent lookup runs.
|
|
345
305
|
* Absent, idle, and already-completed targets are accepted no-ops there.
|
|
346
306
|
* @param childSessionId - durable child session id to interrupt.
|
|
347
|
-
* @param parentSessionId - durable direct parent whose authority is claimed.
|
|
348
|
-
* @param mode - required continuable-address discriminator.
|
|
349
307
|
* @returns acknowledgement that the cancel signal was admitted, not that the target is quiescent.
|
|
350
308
|
* @throws {RemoteError} `gateway/bad-request` for an empty id,
|
|
351
|
-
* `subagent/unauthorized` when the address does not own the live target,
|
|
352
309
|
* otherwise `gateway/internal`.
|
|
353
310
|
*/
|
|
354
|
-
|
|
355
|
-
validateControlRequest('subagent.interrupt', { childSessionId
|
|
311
|
+
interruptByUser(childSessionId) {
|
|
312
|
+
validateControlRequest('subagent.interrupt', { childSessionId });
|
|
356
313
|
try {
|
|
357
|
-
this.interrupt(childSessionId, { kind: 'user'
|
|
314
|
+
this.interrupt(childSessionId, { kind: 'user' });
|
|
358
315
|
}
|
|
359
316
|
catch (error) {
|
|
360
|
-
if (error instanceof SubagentError && error.code === 'UNAUTHORIZED') {
|
|
361
|
-
throw new RemoteError('subagent/unauthorized', 'subagent does not belong to this parent', { childSessionId }, { cause: error });
|
|
362
|
-
}
|
|
363
317
|
throw new RemoteError('gateway/internal', 'subagent interrupt failed', {}, { cause: error });
|
|
364
318
|
}
|
|
365
319
|
return { accepted: true };
|
|
@@ -400,21 +354,11 @@ let SubagentRuntime = (() => {
|
|
|
400
354
|
getProvider(name) {
|
|
401
355
|
return this.providers.get(name);
|
|
402
356
|
}
|
|
403
|
-
/**
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
return [...this.providers.keys()];
|
|
409
|
-
}
|
|
410
|
-
/**
|
|
411
|
-
* Resolve one provider's detached continuable-creation contribution. Method
|
|
412
|
-
* presence on the provider IS the capability, so a provider without it is
|
|
413
|
-
* rejected before the manager reserves any child resources.
|
|
414
|
-
*/
|
|
415
|
-
async prepareContinuable(name, request) {
|
|
416
|
-
const provider = this.expectProvider(name);
|
|
417
|
-
return provider.prepareContinuable(request);
|
|
357
|
+
/** A pending start must use the exact provider whose capabilities were checked. */
|
|
358
|
+
assertProviderCurrent(provider) {
|
|
359
|
+
if (this.providers.get(provider.name) !== provider) {
|
|
360
|
+
throw new SubagentError(`subagent provider "${provider.name}" changed during startup; retry the delegation`, 'PROVIDER_CHANGED');
|
|
361
|
+
}
|
|
418
362
|
}
|
|
419
363
|
/** Look up a provider for dispatch or fail loud. */
|
|
420
364
|
expectProvider(name) {
|
|
@@ -431,13 +375,6 @@ let SubagentRuntime = (() => {
|
|
|
431
375
|
}
|
|
432
376
|
return this.manager;
|
|
433
377
|
}
|
|
434
|
-
/**
|
|
435
|
-
* Build the lifecycle observer for one continuable Activation's residency
|
|
436
|
-
* epoch, so the manager publishes its edges without owning event dispatch.
|
|
437
|
-
*/
|
|
438
|
-
observeActivation(provider, childId, parent) {
|
|
439
|
-
return createActivationObserver(this.emitLifecycle, provider, childId, parent);
|
|
440
|
-
}
|
|
441
378
|
/** Reject the first requested capability that the provider lacks. */
|
|
442
379
|
assertCapabilities(provider, request) {
|
|
443
380
|
if (request.agentPreset !== undefined && (!provider.capabilities.agentPreset || provider.inheritsParentContext)) {
|
package/lib/types/lifecycle.d.ts
CHANGED
|
@@ -1,39 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Contained lifecycle notifications for subagents.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* The service owns scoped dispatch and listener containment; the manager
|
|
5
|
+
* supplies event payloads from each activation.
|
|
6
6
|
*
|
|
7
7
|
* @module @deepseek-ai/dsh-subagent/lifecycle
|
|
8
8
|
*/
|
|
9
9
|
import type { Context } from '@deepseek-ai/cordis';
|
|
10
10
|
import type { Agent } from '@deepseek-ai/dsh-agent';
|
|
11
|
-
import type { SessionEvent
|
|
11
|
+
import type { SessionEvent } from '@deepseek-ai/dsh-session';
|
|
12
12
|
import type { SubagentResult, SubagentRunEndInfo, SubagentRunInfo } from './types.ts';
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*/
|
|
18
|
-
export interface ActivationObserver {
|
|
19
|
-
/**
|
|
20
|
-
* Publish the start edge once the epoch is resident.
|
|
21
|
-
* @param child - the resident local Agent, or undefined for an external execution.
|
|
22
|
-
*/
|
|
23
|
-
start(child?: Agent): void;
|
|
24
|
-
/**
|
|
25
|
-
* Publish the terminal edge exactly once, pairing this epoch's {@link start},
|
|
26
|
-
* after the disposal outcome is known. Called only for a resident epoch: a
|
|
27
|
-
* failure before residency publishes no edge, because inventing one would
|
|
28
|
-
* report a lifecycle the child never had.
|
|
29
|
-
* @param result - the captured result, with teardown failures applied.
|
|
30
|
-
*/
|
|
31
|
-
settle(result: SubagentResult): void;
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Publish one lifecycle edge with per-listener exception containment. Run edges
|
|
35
|
-
* carry the delegating parent that keys scoped dispatch; provider removal has no
|
|
36
|
-
* parent carrier and reaches listeners unscoped.
|
|
14
|
+
* Publish one notification with per-listener exception containment. Run edges
|
|
15
|
+
* carry the subagent tree root that keys scoped dispatch. Provider removal
|
|
16
|
+
* reaches all scopes.
|
|
37
17
|
*
|
|
38
18
|
* The service owns this closure because scoped dispatch keys its carrier by the
|
|
39
19
|
* exact service instance, whose own context filter composes into the carrier;
|
|
@@ -50,19 +30,10 @@ export type LifecycleEmitter = {
|
|
|
50
30
|
* returned promise is logged without starving peer listeners, changing the run,
|
|
51
31
|
* or — for provider removal, which fires from a disposer — breaking teardown.
|
|
52
32
|
* @param ctx - the service's own context, owning dispatch and the logger.
|
|
53
|
-
* @param carrier - resolve the scoped dispatch carrier for one
|
|
54
|
-
* @returns the emitter
|
|
33
|
+
* @param carrier - resolve the scoped dispatch carrier for one subagent tree root.
|
|
34
|
+
* @returns the emitter the manager and provider registry publish through.
|
|
55
35
|
*/
|
|
56
36
|
export declare function createLifecycleEmitter(ctx: Context, carrier: (parent: Agent) => object): LifecycleEmitter;
|
|
57
|
-
/**
|
|
58
|
-
* Build the observer for one local or external activation's residency epoch. Creation failure before residency emits no lifecycle edge.
|
|
59
|
-
* @param emit - the contained lifecycle emitter.
|
|
60
|
-
* @param provider - the provider name recorded in the durable descriptor.
|
|
61
|
-
* @param childId - the durable child session id.
|
|
62
|
-
* @param parent - the exact live direct parent keying scoped dispatch.
|
|
63
|
-
* @returns the observer whose edges this epoch publishes.
|
|
64
|
-
*/
|
|
65
|
-
export declare function createActivationObserver(emit: LifecycleEmitter, provider: string, childId: SessionId, parent: Agent): ActivationObserver;
|
|
66
37
|
/**
|
|
67
38
|
* Why this child's epoch ended, for the terminal lifecycle edge and the
|
|
68
39
|
* manager's own parent delivery. The child's own log is authoritative:
|
package/lib/types/lifecycle.js
CHANGED
|
@@ -1,22 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Contained lifecycle notifications for subagents.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* The service owns scoped dispatch and listener containment; the manager
|
|
5
|
+
* supplies event payloads from each activation.
|
|
6
6
|
*
|
|
7
7
|
* @module @deepseek-ai/dsh-subagent/lifecycle
|
|
8
8
|
*/
|
|
9
|
-
import { randomUUID } from 'node:crypto';
|
|
10
9
|
import { foldConsumedWork } from '@deepseek-ai/dsh-agent';
|
|
11
|
-
import { SubagentRunId } from "./types.js";
|
|
12
10
|
/**
|
|
13
11
|
* Build the contained lifecycle emitter this seam publishes every edge through.
|
|
14
12
|
* Every listener is independently contained: a synchronous throw or a rejected
|
|
15
13
|
* returned promise is logged without starving peer listeners, changing the run,
|
|
16
14
|
* or — for provider removal, which fires from a disposer — breaking teardown.
|
|
17
15
|
* @param ctx - the service's own context, owning dispatch and the logger.
|
|
18
|
-
* @param carrier - resolve the scoped dispatch carrier for one
|
|
19
|
-
* @returns the emitter
|
|
16
|
+
* @param carrier - resolve the scoped dispatch carrier for one subagent tree root.
|
|
17
|
+
* @returns the emitter the manager and provider registry publish through.
|
|
20
18
|
*/
|
|
21
19
|
export function createLifecycleEmitter(ctx, carrier) {
|
|
22
20
|
return (name, info, parent) => {
|
|
@@ -36,30 +34,6 @@ export function createLifecycleEmitter(ctx, carrier) {
|
|
|
36
34
|
}
|
|
37
35
|
};
|
|
38
36
|
}
|
|
39
|
-
/**
|
|
40
|
-
* Build the observer for one local or external activation's residency epoch. Creation failure before residency emits no lifecycle edge.
|
|
41
|
-
* @param emit - the contained lifecycle emitter.
|
|
42
|
-
* @param provider - the provider name recorded in the durable descriptor.
|
|
43
|
-
* @param childId - the durable child session id.
|
|
44
|
-
* @param parent - the exact live direct parent keying scoped dispatch.
|
|
45
|
-
* @returns the observer whose edges this epoch publishes.
|
|
46
|
-
*/
|
|
47
|
-
export function createActivationObserver(emit, provider, childId, parent) {
|
|
48
|
-
const identity = { runId: SubagentRunId(randomUUID()), provider, id: childId, local: false };
|
|
49
|
-
return {
|
|
50
|
-
start: (child) => {
|
|
51
|
-
identity.local = child !== undefined;
|
|
52
|
-
emit('subagent/start', identity, parent);
|
|
53
|
-
},
|
|
54
|
-
settle: ({ stopReason, output }) => {
|
|
55
|
-
emit('subagent/end', {
|
|
56
|
-
...identity,
|
|
57
|
-
stopReason,
|
|
58
|
-
...output.length === 0 ? {} : { lastAssistantMessage: output },
|
|
59
|
-
}, parent);
|
|
60
|
-
},
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
37
|
/**
|
|
64
38
|
* Why this child's epoch ended, for the terminal lifecycle edge and the
|
|
65
39
|
* manager's own parent delivery. The child's own log is authoritative:
|