@toddzheng024/dscode-bundle 0.4.0 → 0.5.0
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/THIRD_PARTY_NOTICES.md +15 -0
- package/cordis.patch.yml +3 -3
- package/package.json +7 -1
- package/plugins/clipboard-image/clipboard-image.swift +56 -0
- package/plugins/clipboard-image/index.mjs +70 -0
- package/plugins/code-review/git.mjs +95 -0
- package/plugins/code-review/index.mjs +88 -0
- package/plugins/dscode/index.mjs +17 -3
- package/plugins/session-metrics/index.mjs +6 -2
- package/plugins/session-metrics/rate.mjs +62 -0
- package/plugins/session-metrics/view.mjs +15 -9
- package/plugins/tui-tools/doctor-cli.mjs +22 -0
- package/plugins/tui-tools/doctor.mjs +141 -0
- package/plugins/tui-tools/index.mjs +14 -6
- package/plugins/ultra/policy.mjs +12 -1
- package/plugins/worktree-subagent/worktree.mjs +50 -0
- package/presets/dscode/agent.cordis.yml +7 -2
- package/vendor/deepseek/index.js +12 -2
- package/vendor/subagent/index.js +29 -9
- package/vendor/subagent-core/LICENSE +21 -0
- package/vendor/subagent-core/index.js +3232 -0
- package/vendor/subagent-core/invariant.js +76 -0
- package/vendor/subagent-core/typert.host.d.ts +3 -0
- package/vendor/subagent-core/typert.host.js +964 -0
- package/vendor/subagent-core/typert.remote-client.d.ts +27 -0
- package/vendor/subagent-core/typert.remote-client.js +160 -0
- package/vendor/subagent-core/types/assistant-output.d.ts +48 -0
- package/vendor/subagent-core/types/assistant-output.js +73 -0
- package/vendor/subagent-core/types/catalog.d.ts +77 -0
- package/vendor/subagent-core/types/catalog.js +103 -0
- package/vendor/subagent-core/types/child-agent.d.ts +150 -0
- package/vendor/subagent-core/types/child-agent.js +207 -0
- package/vendor/subagent-core/types/client.d.ts +8 -0
- package/vendor/subagent-core/types/client.js +7 -0
- package/vendor/subagent-core/types/continuation-activation.d.ts +251 -0
- package/vendor/subagent-core/types/continuation-activation.js +663 -0
- package/vendor/subagent-core/types/continuation-messages.d.ts +62 -0
- package/vendor/subagent-core/types/continuation-messages.js +102 -0
- package/vendor/subagent-core/types/continuation.d.ts +124 -0
- package/vendor/subagent-core/types/continuation.js +455 -0
- package/vendor/subagent-core/types/control-types.d.ts +146 -0
- package/vendor/subagent-core/types/control-types.js +9 -0
- package/vendor/subagent-core/types/control.d.ts +71 -0
- package/vendor/subagent-core/types/control.js +116 -0
- package/vendor/subagent-core/types/depth.d.ts +31 -0
- package/vendor/subagent-core/types/depth.js +39 -0
- package/vendor/subagent-core/types/descriptor.d.ts +144 -0
- package/vendor/subagent-core/types/descriptor.js +193 -0
- package/vendor/subagent-core/types/error.d.ts +11 -0
- package/vendor/subagent-core/types/error.js +14 -0
- package/vendor/subagent-core/types/inbox.d.ts +43 -0
- package/vendor/subagent-core/types/inbox.js +61 -0
- package/vendor/subagent-core/types/index.d.ts +316 -0
- package/vendor/subagent-core/types/index.js +511 -0
- package/vendor/subagent-core/types/internal.d.ts +58 -0
- package/vendor/subagent-core/types/internal.js +58 -0
- package/vendor/subagent-core/types/invariant.d.ts +13 -0
- package/vendor/subagent-core/types/invariant.js +91 -0
- package/vendor/subagent-core/types/lifecycle.d.ts +113 -0
- package/vendor/subagent-core/types/lifecycle.js +178 -0
- package/vendor/subagent-core/types/list-children.d.ts +65 -0
- package/vendor/subagent-core/types/list-children.js +381 -0
- package/vendor/subagent-core/types/out-of-process.d.ts +118 -0
- package/vendor/subagent-core/types/out-of-process.js +219 -0
- package/vendor/subagent-core/types/projection-types.d.ts +74 -0
- package/vendor/subagent-core/types/projection-types.js +7 -0
- package/vendor/subagent-core/types/projection.d.ts +95 -0
- package/vendor/subagent-core/types/projection.js +151 -0
- package/vendor/subagent-core/types/run-settlement.d.ts +17 -0
- package/vendor/subagent-core/types/run-settlement.js +70 -0
- package/vendor/subagent-core/types/types.d.ts +377 -0
- package/vendor/subagent-core/types/types.js +19 -0
- package/vendor/subagent-driver/LICENSE +21 -0
- package/vendor/subagent-driver/index.js +255 -0
- package/vendor/subagent-driver/types/index.d.ts +33 -0
- package/vendor/subagent-driver/types/structured.d.ts +42 -0
- package/vendor/subagent-fork/LICENSE +21 -0
- package/vendor/subagent-fork/index.js +61 -0
- package/vendor/subagent-fork/types/index.d.ts +20 -0
- package/vendor/subagent-spawn/LICENSE +21 -0
- package/vendor/subagent-spawn/index.js +45 -0
- package/vendor/subagent-spawn/types/index.d.ts +19 -0
- package/vendor/terminal/LICENSE +21 -0
- package/vendor/terminal/index.js +1013 -0
- package/vendor/terminal/types/config.d.ts +71 -0
- package/vendor/terminal/types/index.d.ts +36 -0
- package/vendor/terminal/types/sanitize.d.ts +47 -0
- package/vendor/terminal/types/session.d.ts +87 -0
- package/vendor/tui/dscode-clipboard-image/clipboard-image.swift +56 -0
- package/vendor/tui/dscode-clipboard-image/index.mjs +70 -0
- package/vendor/tui/index.mjs +301 -78
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Continuable-subagent orchestration behind `ctx.subagents`: stable child ids,
|
|
3
|
+
* descriptor persistence, provider preparation, cold resume, authorization,
|
|
4
|
+
* and message routing. {@link ContinuableActivationRegistry} owns the mutable
|
|
5
|
+
* process-local Activation graph and its settlement and disposal lifecycle.
|
|
6
|
+
*
|
|
7
|
+
* A continuable child has one durable Session and at most one process-local
|
|
8
|
+
* Activation. The Agent inbox is the only turn queue, so this manager owns
|
|
9
|
+
* durable orchestration while the Agent loop owns all turn ordering and
|
|
10
|
+
* execution. No continuable path creates a Task or an intermediate
|
|
11
|
+
* result-bearing wrapper.
|
|
12
|
+
*
|
|
13
|
+
* @module @deepseek-ai/dsh-subagent
|
|
14
|
+
*/
|
|
15
|
+
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
|
|
16
|
+
if (value !== null && value !== void 0) {
|
|
17
|
+
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
|
18
|
+
var dispose, inner;
|
|
19
|
+
if (async) {
|
|
20
|
+
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
|
|
21
|
+
dispose = value[Symbol.asyncDispose];
|
|
22
|
+
}
|
|
23
|
+
if (dispose === void 0) {
|
|
24
|
+
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
|
|
25
|
+
dispose = value[Symbol.dispose];
|
|
26
|
+
if (async) inner = dispose;
|
|
27
|
+
}
|
|
28
|
+
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
|
|
29
|
+
if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
|
|
30
|
+
env.stack.push({ value: value, dispose: dispose, async: async });
|
|
31
|
+
}
|
|
32
|
+
else if (async) {
|
|
33
|
+
env.stack.push({ async: true });
|
|
34
|
+
}
|
|
35
|
+
return value;
|
|
36
|
+
};
|
|
37
|
+
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
|
|
38
|
+
return function (env) {
|
|
39
|
+
function fail(e) {
|
|
40
|
+
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
|
|
41
|
+
env.hasError = true;
|
|
42
|
+
}
|
|
43
|
+
var r, s = 0;
|
|
44
|
+
function next() {
|
|
45
|
+
while (r = env.stack.pop()) {
|
|
46
|
+
try {
|
|
47
|
+
if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
|
|
48
|
+
if (r.dispose) {
|
|
49
|
+
var result = r.dispose.call(r.value);
|
|
50
|
+
if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
|
|
51
|
+
}
|
|
52
|
+
else s |= 1;
|
|
53
|
+
}
|
|
54
|
+
catch (e) {
|
|
55
|
+
fail(e);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
|
|
59
|
+
if (env.hasError) throw env.error;
|
|
60
|
+
}
|
|
61
|
+
return next();
|
|
62
|
+
};
|
|
63
|
+
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
64
|
+
var e = new Error(message);
|
|
65
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
66
|
+
});
|
|
67
|
+
import { randomUUID } from 'node:crypto';
|
|
68
|
+
import { brandString } from '@deepseek-ai/dsh-brand';
|
|
69
|
+
import { ReasoningEffortId, contentHasImage, createUserMessage } from '@deepseek-ai/dsh-llm';
|
|
70
|
+
import { SessionLogOffset } from '@deepseek-ai/dsh-session';
|
|
71
|
+
import { childSessionMeta, captureDelegatedPolicyOverrides, resolveChildAgentOptions, resolveChildDepth, } from "./child-agent.js";
|
|
72
|
+
import { ContinuableActivationRegistry, } from "./continuation-activation.js";
|
|
73
|
+
import { createAgentMessage, withContinuableReturnGuidance, } from "./continuation-messages.js";
|
|
74
|
+
import { assertSubagentMaxDepth } from "./depth.js";
|
|
75
|
+
import { foldSubagentDescriptor, snapshotSubagentDescriptor } from "./descriptor.js";
|
|
76
|
+
import { establishCatalogChild } from "./catalog.js";
|
|
77
|
+
import { SubagentError } from "./error.js";
|
|
78
|
+
import { isAdjacentAgentSendMessageTool } from "./internal.js";
|
|
79
|
+
/**
|
|
80
|
+
* The continuable-subagent orchestration service behind `ctx.subagents`. Tool
|
|
81
|
+
* schema and host adapters are consumers of this one contract; foreground
|
|
82
|
+
* one-shot delegation keeps calling `ctx.subagents.start()` and never enters
|
|
83
|
+
* this lifecycle.
|
|
84
|
+
*/
|
|
85
|
+
export class SubagentContinuationManager {
|
|
86
|
+
ctx;
|
|
87
|
+
host;
|
|
88
|
+
activations;
|
|
89
|
+
constructor(ctx, host) {
|
|
90
|
+
this.ctx = ctx;
|
|
91
|
+
this.host = host;
|
|
92
|
+
this.activations = new ContinuableActivationRegistry(ctx, (provider, childId, parent) => host.observeActivation(provider, childId, parent));
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Start one continuable background child and resolve at initial inbox acceptance.
|
|
96
|
+
* Every earlier failure disposes any created handle and rolls back Activation
|
|
97
|
+
* and parent ownership without returning either id.
|
|
98
|
+
* @param spec - provider, delegation request, and caller cancellation.
|
|
99
|
+
* @returns the durable child id and accepted initial prompt message id.
|
|
100
|
+
*/
|
|
101
|
+
async startContinuable(spec) {
|
|
102
|
+
const request = spec.request;
|
|
103
|
+
const parent = request.parent;
|
|
104
|
+
this.activations.assertAdmitting(parent);
|
|
105
|
+
const persistence = this.requirePersistence();
|
|
106
|
+
assertSubagentMaxDepth(request.maxDepth);
|
|
107
|
+
const childId = spec.childId ?? brandString(randomUUID());
|
|
108
|
+
this.activations.assertChildIdAvailable(childId);
|
|
109
|
+
const childDepth = resolveChildDepth(parent, request.maxDepth);
|
|
110
|
+
// Snapshot before any await: invalid descriptor JSON rejects the call
|
|
111
|
+
// before a child exists, and the detached value is what reaches the log.
|
|
112
|
+
const agentOptions = resolveChildAgentOptions(parent, request.agentOptions, childDepth);
|
|
113
|
+
const agentProvider = agentOptions.provider;
|
|
114
|
+
const agentModel = agentOptions.model;
|
|
115
|
+
const agentReasoningEffort = agentOptions.reasoningEffort;
|
|
116
|
+
const descriptor = snapshotSubagentDescriptor({
|
|
117
|
+
mode: 'continuable',
|
|
118
|
+
provider: spec.provider,
|
|
119
|
+
label: spec.label,
|
|
120
|
+
...agentProvider !== undefined ? { agentProvider } : {},
|
|
121
|
+
...agentModel !== undefined ? { agentModel } : {},
|
|
122
|
+
...agentReasoningEffort !== undefined ? { agentReasoningEffort } : {},
|
|
123
|
+
...request.persona !== undefined ? { persona: request.persona } : {},
|
|
124
|
+
...request.toolFilter !== undefined ? { toolFilter: request.toolFilter } : {},
|
|
125
|
+
});
|
|
126
|
+
// Capture before the first await: a later parent switch belongs to the
|
|
127
|
+
// parent's future, not to this child.
|
|
128
|
+
const delegatedPolicies = captureDelegatedPolicyOverrides(parent);
|
|
129
|
+
// An idle continuation-managed parent must not settle while a caller is
|
|
130
|
+
// still creating its child. A turn-scoped delegation does not need this,
|
|
131
|
+
// but the service is also callable outside a turn.
|
|
132
|
+
const releaseHold = this.activations.holdOwnership(parent, childId);
|
|
133
|
+
try {
|
|
134
|
+
const prepared = await this.host.prepareContinuable(spec.provider, {
|
|
135
|
+
sessionId: childId,
|
|
136
|
+
parent,
|
|
137
|
+
signal: spec.signal,
|
|
138
|
+
});
|
|
139
|
+
spec.signal.throwIfAborted();
|
|
140
|
+
this.activations.assertAdmitting(parent);
|
|
141
|
+
const inheritedEventCount = SessionLogOffset(prepared.seed?.length ?? 0);
|
|
142
|
+
const seed = prepared.seed;
|
|
143
|
+
const messageId = await this.activations.locks.run(childId, async () => {
|
|
144
|
+
spec.signal.throwIfAborted();
|
|
145
|
+
this.activations.assertAdmitting(parent);
|
|
146
|
+
this.activations.assertChildIdAvailable(childId);
|
|
147
|
+
if (spec.childId !== undefined) {
|
|
148
|
+
const persisted = await persistence.stat(childId, { signal: spec.signal });
|
|
149
|
+
spec.signal.throwIfAborted();
|
|
150
|
+
this.activations.assertAdmitting(parent);
|
|
151
|
+
this.activations.assertChildIdAvailable(childId);
|
|
152
|
+
if (persisted !== undefined) {
|
|
153
|
+
throw new SubagentError(`subagent "${childId}" already exists`, 'DUPLICATE_CHILD');
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
const activation = await this.activations.materialize({
|
|
157
|
+
childId,
|
|
158
|
+
provider: spec.provider,
|
|
159
|
+
parent,
|
|
160
|
+
create: {
|
|
161
|
+
seed,
|
|
162
|
+
meta: childSessionMeta(parent, childDepth, prepared.seed !== undefined),
|
|
163
|
+
inheritedEventCount,
|
|
164
|
+
delegatedPolicies,
|
|
165
|
+
descriptor,
|
|
166
|
+
},
|
|
167
|
+
agentOptions,
|
|
168
|
+
composition: { persona: request.persona, toolFilter: request.toolFilter },
|
|
169
|
+
signal: spec.signal,
|
|
170
|
+
});
|
|
171
|
+
const childHeader = activation.handle.agent.session.header;
|
|
172
|
+
return await this.submitMaterialized(activation, isAdjacentAgentSendMessageTool(this.ctx.get('tools')?.get('send_message', activation.handle.agent))
|
|
173
|
+
? withContinuableReturnGuidance(parent.id, request.prompt)
|
|
174
|
+
: request.prompt, { source: { kind: 'user' }, signal: spec.signal, delivery: 'queue' }, parent, () => { establishCatalogChild(parent.session, childHeader, descriptor); });
|
|
175
|
+
});
|
|
176
|
+
return { childId, messageId };
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
releaseHold();
|
|
180
|
+
throw error;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Deliver one model-authored message to a direct continuable child or to the
|
|
185
|
+
* sender's direct parent. A missing direct child cold-resumes through the
|
|
186
|
+
* ordinary continuation lifecycle.
|
|
187
|
+
* @param sender - exact live Agent authorizing and originating the message.
|
|
188
|
+
* @param targetId - durable direct-parent or direct-child session id.
|
|
189
|
+
* @param content - model-authored content to deliver.
|
|
190
|
+
* @param options - caller cancellation before acceptance.
|
|
191
|
+
* @returns the accepted message's inbox id.
|
|
192
|
+
*/
|
|
193
|
+
async sendMessage(sender, targetId, content, options) {
|
|
194
|
+
if (this.ctx.agents.get(sender.id) !== sender) {
|
|
195
|
+
throw new SubagentError('message delivery requires the exact live sender agent', 'UNAUTHORIZED');
|
|
196
|
+
}
|
|
197
|
+
this.activations.assertAdmitting(sender);
|
|
198
|
+
const senderActivation = this.activations.get(sender.id);
|
|
199
|
+
if (senderActivation !== undefined
|
|
200
|
+
&& senderActivation.handle.agent === sender
|
|
201
|
+
&& senderActivation.parentSession === targetId) {
|
|
202
|
+
options.signal.throwIfAborted();
|
|
203
|
+
return this.sendToParent(senderActivation, sender, content);
|
|
204
|
+
}
|
|
205
|
+
if (sender.session.header.parentSession === targetId) {
|
|
206
|
+
throw new SubagentError(`agent "${sender.id}" is not a resident continuable child and cannot send to parent "${targetId}"`, 'UNAUTHORIZED');
|
|
207
|
+
}
|
|
208
|
+
return this.deliverToChild(sender, targetId, content, {
|
|
209
|
+
signal: options.signal,
|
|
210
|
+
delivery: 'steer',
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Queue one human-authored prompt as a distinct direct-child turn.
|
|
215
|
+
* @param parent - exact live direct parent authorizing delivery.
|
|
216
|
+
* @param childId - durable direct-child session id.
|
|
217
|
+
* @param content - model-visible prompt blocks.
|
|
218
|
+
* @param source - durable attribution for the human prompt.
|
|
219
|
+
* @param signal - caller cancellation before inbox acceptance.
|
|
220
|
+
* @returns the accepted durable message id.
|
|
221
|
+
*/
|
|
222
|
+
async queuePrompt(parent, childId, content, source, signal) {
|
|
223
|
+
return this.deliverToChild(parent, childId, content, { source, signal, delivery: 'queue' });
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Steer one host-authored prompt to a direct continuable child.
|
|
227
|
+
* @param parent - exact live direct parent authorizing delivery.
|
|
228
|
+
* @param childId - durable direct-child session id.
|
|
229
|
+
* @param content - model-visible prompt blocks.
|
|
230
|
+
* @param source - durable attribution for the host prompt.
|
|
231
|
+
* @param signal - caller cancellation before inbox acceptance.
|
|
232
|
+
* @returns the accepted durable message id.
|
|
233
|
+
*/
|
|
234
|
+
async steerPrompt(parent, childId, content, source, signal) {
|
|
235
|
+
return this.deliverToChild(parent, childId, content, { source, signal, delivery: 'steer' });
|
|
236
|
+
}
|
|
237
|
+
/** Route one parent-originated delivery through residency and cold resume. */
|
|
238
|
+
async deliverToChild(parent, childId, content, options) {
|
|
239
|
+
this.activations.assertAdmitting(parent);
|
|
240
|
+
const releaseHold = this.activations.holdOwnership(parent, childId);
|
|
241
|
+
try {
|
|
242
|
+
return await this.deliverFollowup(parent, childId, content, options);
|
|
243
|
+
}
|
|
244
|
+
catch (error) {
|
|
245
|
+
releaseHold();
|
|
246
|
+
throw error;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
/** The delivery loop behind {@link deliverToChild}, run under the parent hold. */
|
|
250
|
+
async deliverFollowup(parent, childId, content, options) {
|
|
251
|
+
while (true) {
|
|
252
|
+
const live = await this.activations.locks.run(childId, async () => {
|
|
253
|
+
const activation = this.activations.get(childId);
|
|
254
|
+
if (activation === undefined)
|
|
255
|
+
return this.coldResume(parent, childId, content, options);
|
|
256
|
+
const disposal = activation.inbox.closing;
|
|
257
|
+
/* v8 ignore next 3 -- the send-versus-dispose cutoff needs a delivery to
|
|
258
|
+
* observe the transaction inside the same critical section that opened it. */
|
|
259
|
+
if (disposal !== undefined) {
|
|
260
|
+
return disposal.then(() => undefined, () => undefined);
|
|
261
|
+
}
|
|
262
|
+
if (contentHasImage(content)) {
|
|
263
|
+
await this.assertImageCapable(activation.handle.agent, options.signal);
|
|
264
|
+
if (activation.inbox.closing !== undefined) {
|
|
265
|
+
await Promise.allSettled([activation.inbox.closing]);
|
|
266
|
+
return undefined;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
const messageId = this.submitAdmitted(activation, content, options, parent);
|
|
270
|
+
activation.announced = true;
|
|
271
|
+
return messageId;
|
|
272
|
+
});
|
|
273
|
+
/* v8 ignore start -- only a delivery that lost the disposal cutoff retries. */
|
|
274
|
+
if (live !== undefined)
|
|
275
|
+
return live;
|
|
276
|
+
this.activations.assertAdmitting(parent);
|
|
277
|
+
options.signal.throwIfAborted();
|
|
278
|
+
/* v8 ignore stop */
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Interrupt one live continuable child's current turn. Admission is
|
|
283
|
+
* synchronous and the cancellation effect is asynchronous. An absent or
|
|
284
|
+
* already-closing target is an accepted no-op after authority checks.
|
|
285
|
+
* @param targetSessionId - the durable child session id to interrupt.
|
|
286
|
+
* @param authority - the human parent address or exact live ancestor Agent.
|
|
287
|
+
*/
|
|
288
|
+
interrupt(targetSessionId, authority) {
|
|
289
|
+
this.activations.interrupt(targetSessionId, authority);
|
|
290
|
+
}
|
|
291
|
+
/** Deliver one resident continuable child's message to its live direct parent. */
|
|
292
|
+
sendToParent(activation, sender, content) {
|
|
293
|
+
/* v8 ignore next 6 -- only synchronous re-entrant teardown can open this
|
|
294
|
+
* transaction between exact-agent authorization and this no-await span. */
|
|
295
|
+
if (activation.inbox.closing !== undefined) {
|
|
296
|
+
throw new SubagentError(`subagent "${sender.id}" activation is being disposed; the message was not delivered`, 'ACTIVATION_CLOSING');
|
|
297
|
+
}
|
|
298
|
+
const parent = this.ctx.agents.get(activation.parentSession);
|
|
299
|
+
if (parent === undefined) {
|
|
300
|
+
throw new SubagentError('direct parent is not live; the message was not delivered', 'PARENT_UNAVAILABLE');
|
|
301
|
+
}
|
|
302
|
+
const message = createAgentMessage(sender, content);
|
|
303
|
+
this.sendAgentMessage(parent, message);
|
|
304
|
+
return message.id;
|
|
305
|
+
}
|
|
306
|
+
/** Send one Agent message while translating only the target's own rejection. */
|
|
307
|
+
sendAgentMessage(parent, message) {
|
|
308
|
+
try {
|
|
309
|
+
this.activations.sendWaking(parent, message, 'steer');
|
|
310
|
+
}
|
|
311
|
+
catch (error) {
|
|
312
|
+
throw new SubagentError('direct parent is not live; the message was not delivered', 'PARENT_UNAVAILABLE', { cause: error });
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
/** Close manager-wide admission and release every live Activation. */
|
|
316
|
+
async drain() {
|
|
317
|
+
await this.activations.drain();
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Stop only the continuable descendants of exact live host-owned parents.
|
|
321
|
+
* @param parents - exact live roots whose continuable descendants must stop.
|
|
322
|
+
*/
|
|
323
|
+
async drainDescendants(parents) {
|
|
324
|
+
await this.activations.drainDescendants(parents);
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Release selected resident direct children of one exact live parent.
|
|
328
|
+
* @param parent - exact live direct parent authorizing the selected release.
|
|
329
|
+
* @param childIds - durable direct-child ids to release when resident.
|
|
330
|
+
*/
|
|
331
|
+
async drainChildren(parent, childIds) {
|
|
332
|
+
await this.activations.drainChildren(parent, childIds);
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Cold-resume a persisted child and submit the waiting turn. The descriptor
|
|
336
|
+
* supplies every reconstruction input; no subagent provider is dispatched.
|
|
337
|
+
*/
|
|
338
|
+
async coldResume(parent, childId, content, options) {
|
|
339
|
+
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
340
|
+
try {
|
|
341
|
+
const query = this.requireSessionQuery();
|
|
342
|
+
let observation;
|
|
343
|
+
try {
|
|
344
|
+
observation = await query.observeSession(childId, {
|
|
345
|
+
signal: options.signal,
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
catch (error) {
|
|
349
|
+
options.signal.throwIfAborted();
|
|
350
|
+
throw new SubagentError(`subagent "${childId}" is unavailable`, 'NOT_RESUMABLE', { cause: error });
|
|
351
|
+
}
|
|
352
|
+
const source = __addDisposableResource(env_1, observation, false);
|
|
353
|
+
this.activations.assertAdmitting(parent);
|
|
354
|
+
this.activations.authorizeLineage(parent, childId, source.header.parentSession);
|
|
355
|
+
const descriptor = foldSubagentDescriptor(source.events.slice(source.inheritedEventCount));
|
|
356
|
+
if (descriptor === undefined || descriptor.mode !== 'continuable') {
|
|
357
|
+
throw new SubagentError(`subagent "${childId}" has no supported continuation state and cannot be resumed; choose a different target`, 'NOT_RESUMABLE');
|
|
358
|
+
}
|
|
359
|
+
let activation;
|
|
360
|
+
try {
|
|
361
|
+
activation = await this.activations.materialize({
|
|
362
|
+
childId,
|
|
363
|
+
provider: descriptor.provider,
|
|
364
|
+
parent,
|
|
365
|
+
agentOptions: {
|
|
366
|
+
...descriptor.agentProvider !== undefined ? { provider: descriptor.agentProvider } : {},
|
|
367
|
+
...descriptor.agentModel !== undefined ? { model: descriptor.agentModel } : {},
|
|
368
|
+
...descriptor.agentReasoningEffort !== undefined
|
|
369
|
+
? { reasoningEffort: ReasoningEffortId(descriptor.agentReasoningEffort) }
|
|
370
|
+
: {},
|
|
371
|
+
},
|
|
372
|
+
composition: { persona: descriptor.persona, toolFilter: descriptor.toolFilter },
|
|
373
|
+
signal: options.signal,
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
catch (error) {
|
|
377
|
+
options.signal.throwIfAborted();
|
|
378
|
+
if (error instanceof SubagentError)
|
|
379
|
+
throw error;
|
|
380
|
+
throw new SubagentError(`subagent "${childId}" is unavailable`, 'NOT_RESUMABLE', { cause: error });
|
|
381
|
+
}
|
|
382
|
+
return await this.submitMaterialized(activation, content, options, parent);
|
|
383
|
+
}
|
|
384
|
+
catch (e_1) {
|
|
385
|
+
env_1.error = e_1;
|
|
386
|
+
env_1.hasError = true;
|
|
387
|
+
}
|
|
388
|
+
finally {
|
|
389
|
+
__disposeResources(env_1);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
/** Admit a materialized child, commit its creation fact, and release it on failure. */
|
|
393
|
+
async submitMaterialized(activation, content, options, parent, commit) {
|
|
394
|
+
try {
|
|
395
|
+
if (contentHasImage(content)) {
|
|
396
|
+
await this.assertImageCapable(activation.handle.agent, options.signal);
|
|
397
|
+
if (activation.inbox.closing !== undefined) {
|
|
398
|
+
throw new SubagentError(`subagent "${activation.childId}" is closing`, 'ACTIVATION_CLOSING');
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
const messageId = this.submitAdmitted(activation, content, options, parent);
|
|
402
|
+
commit?.();
|
|
403
|
+
activation.announced = true;
|
|
404
|
+
return messageId;
|
|
405
|
+
}
|
|
406
|
+
catch (error) {
|
|
407
|
+
try {
|
|
408
|
+
await this.activations.dispose(activation);
|
|
409
|
+
}
|
|
410
|
+
catch (cleanupError) {
|
|
411
|
+
this.ctx.logger.warn(`subagent continuation: disposal after admission or catalog append failure also failed: ${String(cleanupError)}`);
|
|
412
|
+
}
|
|
413
|
+
throw error;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
/** Build and submit one message across the final synchronous admission cutoff. */
|
|
417
|
+
submitAdmitted(activation, content, options, parent) {
|
|
418
|
+
const message = options.source === undefined
|
|
419
|
+
? createAgentMessage(parent, content)
|
|
420
|
+
: createUserMessage({ content, source: options.source });
|
|
421
|
+
return this.activations.submitAdmitted(activation, message, options.delivery, parent, options.signal);
|
|
422
|
+
}
|
|
423
|
+
/** Refuse image content for a child whose fixed model accepts text only. */
|
|
424
|
+
async assertImageCapable(agent, signal) {
|
|
425
|
+
const { provider, model } = agent.options;
|
|
426
|
+
if (provider === undefined || model === undefined)
|
|
427
|
+
return;
|
|
428
|
+
const llm = this.ctx.get('llm');
|
|
429
|
+
/* v8 ignore next -- without an LLM registry, delivery defers to projection. */
|
|
430
|
+
if (llm === undefined)
|
|
431
|
+
return;
|
|
432
|
+
const info = await llm.resolveModelInfo(provider, model, signal);
|
|
433
|
+
if (info.inputModalities !== undefined && !info.inputModalities.includes('image')) {
|
|
434
|
+
throw new SubagentError(`Model "${model}" does not support image input.`, 'MODEL_DOES_NOT_SUPPORT_IMAGES');
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
/** Resolve the persistence service continuable children require, or fail loud. */
|
|
438
|
+
requirePersistence() {
|
|
439
|
+
const persistence = this.ctx.get('sessionPersistence');
|
|
440
|
+
if (persistence === undefined) {
|
|
441
|
+
throw new SubagentError('continuable subagents require session persistence (load a dsh-session-persistence backend)', 'PERSISTENCE_UNAVAILABLE');
|
|
442
|
+
}
|
|
443
|
+
return persistence;
|
|
444
|
+
}
|
|
445
|
+
/** Resolve the Session query service used for cold child observations. */
|
|
446
|
+
requireSessionQuery() {
|
|
447
|
+
const query = this.ctx.get('sessionQuery');
|
|
448
|
+
if (query === undefined) {
|
|
449
|
+
throw new SubagentError('continuable subagents require session query (load @deepseek-ai/dsh-session-query)', 'CONTINUATION_UNAVAILABLE');
|
|
450
|
+
}
|
|
451
|
+
return query;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
export default SubagentContinuationManager;
|
|
455
|
+
//# sourceMappingURL=continuation.js.map
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-safe subagent catalog and control vocabulary: the durable direct-child
|
|
3
|
+
* row both the listing and the browser catalog answer with, plus the
|
|
4
|
+
* browser-facing control surface's prompt, receipts, and failures.
|
|
5
|
+
*
|
|
6
|
+
* @module @deepseek-ai/dsh-subagent/control-types
|
|
7
|
+
*/
|
|
8
|
+
import type { PromptContentPart } from '@deepseek-ai/dsh-attachment/types';
|
|
9
|
+
import type { Branded } from '@deepseek-ai/dsh-brand';
|
|
10
|
+
import type { MessageId } from '@deepseek-ai/dsh-llm/brand';
|
|
11
|
+
import type { SessionId } from '@deepseek-ai/dsh-session/types';
|
|
12
|
+
/**
|
|
13
|
+
* Client-minted identity of one browser prompt, persisted on the exact accepted
|
|
14
|
+
* message. It carries the Session Controller's `session-request-id` brand so a
|
|
15
|
+
* subagent prompt and an ordinary Session prompt share one identity
|
|
16
|
+
* vocabulary; that package depends on this one, so the brand is spelled here
|
|
17
|
+
* rather than imported.
|
|
18
|
+
*/
|
|
19
|
+
export type SubagentPromptRequestId = Branded<'session-request-id'>;
|
|
20
|
+
/**
|
|
21
|
+
* One durable direct-child row, ordered by header `createdAt` with ties broken
|
|
22
|
+
* on id. Only a candidate whose durable header has `origin: 'subagent'` is
|
|
23
|
+
* interpreted. A served `subagent` projection value produces a `child`; a
|
|
24
|
+
* settled candidate whose fold served no identity produces a `diagnostic`; a
|
|
25
|
+
* running candidate without one is omitted — its descriptor may not be
|
|
26
|
+
* appended yet (the creation window). Diagnostics relay the projection fold's
|
|
27
|
+
* outcome or a failed read, never a per-child event scan, and never expose
|
|
28
|
+
* model-hidden descriptor content.
|
|
29
|
+
*/
|
|
30
|
+
export type SubagentListEntry = {
|
|
31
|
+
readonly kind: 'child';
|
|
32
|
+
/** The durable child session id, stable across Activations. */
|
|
33
|
+
readonly id: SessionId;
|
|
34
|
+
/**
|
|
35
|
+
* Whether the child is live at the moment its reader sampled it: the
|
|
36
|
+
* durable listing reads the Session store (`running` means the logical
|
|
37
|
+
* record is resident, `inactive` that it exists only in persistence),
|
|
38
|
+
* while the browser catalog re-samples the child's Agent driver. Neither
|
|
39
|
+
* encodes a durable outcome, and a continuable child may still reject
|
|
40
|
+
* delivery as an ownership conflict.
|
|
41
|
+
*/
|
|
42
|
+
readonly activity: 'running' | 'inactive';
|
|
43
|
+
/** Whether a direct descendant has durable `origin: 'subagent'`. */
|
|
44
|
+
readonly hasChildren: boolean;
|
|
45
|
+
} & ({
|
|
46
|
+
/** A terminal one-shot child. */
|
|
47
|
+
readonly mode: 'one-shot';
|
|
48
|
+
/** Optional durable creation label from the child's descriptor. */
|
|
49
|
+
readonly label?: string;
|
|
50
|
+
} | {
|
|
51
|
+
/** A resumable conversation. */
|
|
52
|
+
readonly mode: 'continuable';
|
|
53
|
+
/** Durable creation label from the child's descriptor. */
|
|
54
|
+
readonly label: string;
|
|
55
|
+
}) | {
|
|
56
|
+
readonly kind: 'diagnostic';
|
|
57
|
+
/** The candidate's session id. */
|
|
58
|
+
readonly id: SessionId;
|
|
59
|
+
/**
|
|
60
|
+
* Why the candidate has no `child` row: `corrupt` for a settled candidate
|
|
61
|
+
* whose projection fold served no identity (a missing, malformed, or
|
|
62
|
+
* unrecognized-version descriptor — deliberately undistinguished), and
|
|
63
|
+
* for any candidate whose log makes a registered unit's fold or schema
|
|
64
|
+
* throw (deterministic data damage, contained per child); `unavailable`
|
|
65
|
+
* when the candidate's Session observation was absent or transiently
|
|
66
|
+
* unreadable (retried on the next listing). `unsupported` is never produced; it remains in the
|
|
67
|
+
* union for consumers that route on it.
|
|
68
|
+
*/
|
|
69
|
+
readonly reason: 'corrupt' | 'unsupported' | 'unavailable';
|
|
70
|
+
};
|
|
71
|
+
/** Complete direct-child catalog plus the delivery-time parent availability hint. */
|
|
72
|
+
export interface SubagentCatalog {
|
|
73
|
+
readonly entries: readonly SubagentListEntry[];
|
|
74
|
+
readonly parentAvailable: boolean;
|
|
75
|
+
}
|
|
76
|
+
/** Durable parent/child address that selects subagent transport in the client. */
|
|
77
|
+
export type SubagentAddress = {
|
|
78
|
+
readonly parentSessionId: SessionId;
|
|
79
|
+
readonly childSessionId: SessionId;
|
|
80
|
+
} & ({
|
|
81
|
+
readonly mode: 'one-shot';
|
|
82
|
+
} | {
|
|
83
|
+
readonly mode: 'continuable';
|
|
84
|
+
});
|
|
85
|
+
/** One human message addressed to a continuable direct child. */
|
|
86
|
+
export interface SubagentPromptRequest {
|
|
87
|
+
/** Identity persisted on the accepted message, minted before the call. */
|
|
88
|
+
readonly requestId: SubagentPromptRequestId;
|
|
89
|
+
readonly parentSessionId: SessionId;
|
|
90
|
+
readonly childSessionId: SessionId;
|
|
91
|
+
/** Required discriminator retained from the browser control address. */
|
|
92
|
+
readonly mode: 'continuable';
|
|
93
|
+
/** Whether this message queues a later turn or targets the nearest step. */
|
|
94
|
+
readonly delivery: 'queue' | 'steer';
|
|
95
|
+
/**
|
|
96
|
+
* Browser prompt parts delivered as the child's user message. The Host
|
|
97
|
+
* admits and persists image parts before delivery, so the wire never
|
|
98
|
+
* carries a durable attachment reference the caller could fabricate.
|
|
99
|
+
*/
|
|
100
|
+
readonly content: readonly PromptContentPart[];
|
|
101
|
+
/** Optional browser zone sampled for this exact human prompt. */
|
|
102
|
+
readonly clientTimeZone?: string;
|
|
103
|
+
}
|
|
104
|
+
/** Inbox identity returned once the continuation accepts one human message. */
|
|
105
|
+
export interface SubagentPromptReceipt {
|
|
106
|
+
readonly messageId: MessageId;
|
|
107
|
+
}
|
|
108
|
+
/** Uniform acknowledgement that one interrupt request was admitted. */
|
|
109
|
+
export interface SubagentInterruptReceipt {
|
|
110
|
+
readonly accepted: true;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Failure details the control surface answers with. Catalog reads, prompts,
|
|
114
|
+
* and interrupts share this vocabulary with the Client Remote result.
|
|
115
|
+
*/
|
|
116
|
+
declare module '@deepseek-ai/dsh-typert-protocol' {
|
|
117
|
+
interface RemoteErrorDetailsMap {
|
|
118
|
+
/** A browser-supplied zone is neither UTC nor a canonical IANA name. */
|
|
119
|
+
'subagent/invalid-time-zone': {
|
|
120
|
+
readonly value: string;
|
|
121
|
+
};
|
|
122
|
+
/** No live Agent carries the addressed parent session. */
|
|
123
|
+
'subagent/parent-unavailable': {
|
|
124
|
+
readonly parentSessionId: SessionId;
|
|
125
|
+
};
|
|
126
|
+
/** The addressed child cannot take a continuation. */
|
|
127
|
+
'subagent/not-resumable': {
|
|
128
|
+
readonly childSessionId: SessionId;
|
|
129
|
+
};
|
|
130
|
+
/** The claimed parent does not own the addressed child. */
|
|
131
|
+
'subagent/unauthorized': {
|
|
132
|
+
readonly childSessionId: SessionId;
|
|
133
|
+
};
|
|
134
|
+
/** Image admission or model image-capability refusal. */
|
|
135
|
+
'subagent/attachment-invalid': {
|
|
136
|
+
readonly reason: string;
|
|
137
|
+
};
|
|
138
|
+
/** The child exists but its inbox cannot admit the message now. */
|
|
139
|
+
'subagent/delivery-unavailable': {
|
|
140
|
+
readonly childSessionId: SessionId;
|
|
141
|
+
};
|
|
142
|
+
/** The deployment mounts no session-projection registry. */
|
|
143
|
+
'subagent/projections-unavailable': {};
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=control-types.d.ts.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-safe subagent catalog and control vocabulary: the durable direct-child
|
|
3
|
+
* row both the listing and the browser catalog answer with, plus the
|
|
4
|
+
* browser-facing control surface's prompt, receipts, and failures.
|
|
5
|
+
*
|
|
6
|
+
* @module @deepseek-ai/dsh-subagent/control-types
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=control-types.js.map
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-facing subagent control assembly: the catalog view sampled against
|
|
3
|
+
* the live Agent registry, one browser zone's validation, and the stable
|
|
4
|
+
* failure codes the Remote surface answers with.
|
|
5
|
+
*
|
|
6
|
+
* @module @deepseek-ai/dsh-subagent
|
|
7
|
+
*/
|
|
8
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
9
|
+
import type { SessionId } from '@deepseek-ai/dsh-session';
|
|
10
|
+
import { z } from 'zod';
|
|
11
|
+
import type { SubagentCatalog, SubagentListEntry } from './control-types.ts';
|
|
12
|
+
declare const CONTROL_ID_SCHEMAS: {
|
|
13
|
+
readonly 'subagent.list': z.ZodObject<{
|
|
14
|
+
parentSessionId: z.ZodString;
|
|
15
|
+
}, z.core.$strip>;
|
|
16
|
+
readonly 'subagent.prompt': z.ZodObject<{
|
|
17
|
+
parentSessionId: z.ZodString;
|
|
18
|
+
childSessionId: z.ZodString;
|
|
19
|
+
mode: z.ZodLiteral<"continuable">;
|
|
20
|
+
delivery: z.ZodEnum<{
|
|
21
|
+
queue: "queue";
|
|
22
|
+
steer: "steer";
|
|
23
|
+
}>;
|
|
24
|
+
}, z.core.$strip>;
|
|
25
|
+
readonly 'subagent.interrupt': z.ZodObject<{
|
|
26
|
+
parentSessionId: z.ZodString;
|
|
27
|
+
childSessionId: z.ZodString;
|
|
28
|
+
mode: z.ZodLiteral<"continuable">;
|
|
29
|
+
}, z.core.$strip>;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Apply the subagent payload checks that are stricter than generated
|
|
33
|
+
* branded-string codecs.
|
|
34
|
+
* @param method - method name carried in the failure message.
|
|
35
|
+
* @param payload - decoded control fields to validate.
|
|
36
|
+
* @throws {RemoteError} `gateway/bad-request` with the original Zod issues.
|
|
37
|
+
*/
|
|
38
|
+
export declare function validateControlRequest(method: keyof typeof CONTROL_ID_SCHEMAS, payload: unknown): void;
|
|
39
|
+
/**
|
|
40
|
+
* Project one durable listing onto the catalog view, replacing each row's
|
|
41
|
+
* store-derived activity with the live Agent driver's status and reporting
|
|
42
|
+
* whether the exact parent Agent is live. Without an Agent registry no driver
|
|
43
|
+
* runs at all, so every row is inactive and the parent is unavailable.
|
|
44
|
+
* @param ctx - Host context that may carry the Agent registry.
|
|
45
|
+
* @param parentSessionId - the listed parent.
|
|
46
|
+
* @param entries - the durable direct-child listing.
|
|
47
|
+
* @returns the catalog view answered to one browser.
|
|
48
|
+
*/
|
|
49
|
+
export declare function catalogView(ctx: Context, parentSessionId: SessionId, entries: readonly SubagentListEntry[]): SubagentCatalog;
|
|
50
|
+
/**
|
|
51
|
+
* Refuse one catalog read while preserving cancellation and a missing
|
|
52
|
+
* projections registry as distinct failures.
|
|
53
|
+
* @param error - the thrown value.
|
|
54
|
+
* @param signal - the caller's cancellation.
|
|
55
|
+
* @returns Never — the refusal is thrown.
|
|
56
|
+
* @throws {RemoteError} always.
|
|
57
|
+
*/
|
|
58
|
+
export declare function rejectCatalogRead(error: unknown, signal: AbortSignal): never;
|
|
59
|
+
/**
|
|
60
|
+
* Refuse one continuation prompt without exposing provider detail: admission
|
|
61
|
+
* failures the caller can act on keep their own code, everything else is
|
|
62
|
+
* internal.
|
|
63
|
+
* @param error - the thrown value.
|
|
64
|
+
* @param childSessionId - the addressed child.
|
|
65
|
+
* @param signal - the caller's cancellation.
|
|
66
|
+
* @returns Never — the refusal is thrown.
|
|
67
|
+
* @throws {RemoteError} always.
|
|
68
|
+
*/
|
|
69
|
+
export declare function rejectPrompt(error: unknown, childSessionId: SessionId, signal: AbortSignal): never;
|
|
70
|
+
export {};
|
|
71
|
+
//# sourceMappingURL=control.d.ts.map
|