@morlay/dsh-subagent 0.0.9 → 0.0.11-alpha.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/dist/index.d.mts +86 -129
- package/dist/index.mjs +43 -49
- package/dist/rows.d.mts +1 -3
- package/dist/rows.mjs +1 -2
- package/package.json +28 -28
- package/src/continuation-messages.ts +73 -59
- package/src/index.ts +263 -189
- package/src/manager.ts +900 -716
- package/src/vendor/dsh-subagent/activation.ts +81 -89
- package/src/vendor/dsh-subagent/archive-admission.ts +38 -45
- package/src/vendor/dsh-subagent/assistant-output.ts +17 -19
- package/src/vendor/dsh-subagent/catalog.ts +79 -111
- package/src/vendor/dsh-subagent/child-agent.ts +83 -92
- package/src/vendor/dsh-subagent/continuation-messages.ts +64 -68
- package/src/vendor/dsh-subagent/control-types.ts +71 -68
- package/src/vendor/dsh-subagent/control.ts +39 -55
- package/src/vendor/dsh-subagent/depth.ts +14 -18
- package/src/vendor/dsh-subagent/descriptor.ts +107 -113
- package/src/vendor/dsh-subagent/error.ts +3 -3
- package/src/vendor/dsh-subagent/index.ts +193 -254
- package/src/vendor/dsh-subagent/internal.ts +17 -21
- package/src/vendor/dsh-subagent/lifecycle.ts +48 -51
- package/src/vendor/dsh-subagent/list-children.ts +57 -65
- package/src/vendor/dsh-subagent/manager.ts +726 -862
- package/src/vendor/dsh-subagent/out-of-process.ts +59 -61
- package/src/vendor/dsh-subagent/projection-types.ts +40 -38
- package/src/vendor/dsh-subagent/projection.ts +85 -107
- package/src/vendor/dsh-subagent/structured.ts +48 -57
- package/src/vendor/dsh-subagent/types.ts +68 -68
package/src/index.ts
CHANGED
|
@@ -27,32 +27,36 @@
|
|
|
27
27
|
*
|
|
28
28
|
* @module @deepseek-ai/dsh-subagent
|
|
29
29
|
*/
|
|
30
|
-
import type { Volatile } from
|
|
30
|
+
import type { Volatile } from "@deepseek-ai/cordis";
|
|
31
31
|
|
|
32
|
-
import { Context } from
|
|
33
|
-
import z from
|
|
34
|
-
import type {} from
|
|
35
|
-
import type {} from
|
|
36
|
-
import { scopeTarget } from
|
|
37
|
-
import type {} from
|
|
32
|
+
import { Context } from "@deepseek-ai/cordis";
|
|
33
|
+
import z from "@deepseek-ai/schemastery";
|
|
34
|
+
import type {} from "@deepseek-ai/dsh-working-directory";
|
|
35
|
+
import type {} from "@deepseek-ai/dsh-attachment";
|
|
36
|
+
import { scopeTarget } from "@deepseek-ai/dsh-scope";
|
|
37
|
+
import type {} from "@deepseek-ai/dsh-subagent";
|
|
38
38
|
// 服务面声明(`ctx.agentPresets`)由它给:回报指引按会话挂的 preset 选文案。
|
|
39
|
-
import type {} from
|
|
40
|
-
import { assertObjectJsonSchema } from
|
|
41
|
-
import type { ContentBlock, MessageId, MessageSource } from
|
|
42
|
-
import type { Agent } from
|
|
43
|
-
import type { SessionId } from
|
|
44
|
-
import { canonicalClientTimeZone } from
|
|
45
|
-
import { Remote, RemoteError, TypertRemoteService, principalOf, type Principal } from '@deepseek-ai/dsh-typert-protocol'
|
|
39
|
+
import type {} from "@deepseek-ai/dsh-agent-preset-registry";
|
|
40
|
+
import { assertObjectJsonSchema } from "@deepseek-ai/dsh-tools";
|
|
41
|
+
import type { ContentBlock, MessageId, MessageSource } from "@deepseek-ai/dsh-llm";
|
|
42
|
+
import type { Agent } from "@deepseek-ai/dsh-agent";
|
|
43
|
+
import type { SessionId } from "@deepseek-ai/dsh-session";
|
|
44
|
+
import { canonicalClientTimeZone } from "@deepseek-ai/dsh-util-time";
|
|
46
45
|
import {
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
Remote,
|
|
47
|
+
RemoteError,
|
|
48
|
+
TypertRemoteService,
|
|
49
|
+
principalOf,
|
|
50
|
+
type Principal,
|
|
51
|
+
} from "@deepseek-ai/dsh-typert-protocol";
|
|
52
|
+
import { rejectPrompt, validateControlRequest } from "./vendor/dsh-subagent/control.ts";
|
|
49
53
|
import type {
|
|
50
54
|
SubagentDelivery,
|
|
51
55
|
SubagentInterruptReceipt,
|
|
52
56
|
SubagentPromptReceipt,
|
|
53
57
|
SubagentPromptRequest,
|
|
54
58
|
SubagentPromptRequestId,
|
|
55
|
-
} from
|
|
59
|
+
} from "./vendor/dsh-subagent/control-types.ts";
|
|
56
60
|
import type {
|
|
57
61
|
ContinuableCreateRequest,
|
|
58
62
|
ContinuableCreateSpec,
|
|
@@ -63,25 +67,37 @@ import type {
|
|
|
63
67
|
SubagentProvider,
|
|
64
68
|
SubagentSendMessageOptions,
|
|
65
69
|
SubagentStartRequest,
|
|
66
|
-
} from
|
|
67
|
-
import { SubagentError } from
|
|
68
|
-
import { assertSubagentMaxDepth } from
|
|
69
|
-
import {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
import {
|
|
74
|
-
import
|
|
75
|
-
import {
|
|
76
|
-
import
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
70
|
+
} from "./vendor/dsh-subagent/types.ts";
|
|
71
|
+
import { SubagentError } from "./vendor/dsh-subagent/error.ts";
|
|
72
|
+
import { assertSubagentMaxDepth } from "./vendor/dsh-subagent/depth.ts";
|
|
73
|
+
import {
|
|
74
|
+
createActivationObserver,
|
|
75
|
+
createLifecycleEmitter,
|
|
76
|
+
} from "./vendor/dsh-subagent/lifecycle.ts";
|
|
77
|
+
import type { ActivationObserver, LifecycleEmitter } from "./vendor/dsh-subagent/lifecycle.ts";
|
|
78
|
+
import SubagentManager from "./manager.ts";
|
|
79
|
+
import { installDelegationContext } from "./delegation-context.ts";
|
|
80
|
+
import {
|
|
81
|
+
listChildren as listSubagentChildren,
|
|
82
|
+
listDescendants as listSubagentDescendants,
|
|
83
|
+
} from "./vendor/dsh-subagent/list-children.ts";
|
|
84
|
+
import type { SubagentDescendantListEntry } from "./vendor/dsh-subagent/list-children.ts";
|
|
85
|
+
import { installSubagentArchiveAdmission } from "./vendor/dsh-subagent/archive-admission.ts";
|
|
86
|
+
import type { SubagentCatalogEntry } from "./vendor/dsh-subagent/projection-types.ts";
|
|
87
|
+
import {
|
|
88
|
+
subagentIdentityProjectionDefinition,
|
|
89
|
+
subagentTimingProjectionDefinition,
|
|
90
|
+
} from "./vendor/dsh-subagent/projection.ts";
|
|
91
|
+
import { subagentCatalogProjectionDefinition } from "./vendor/dsh-subagent/catalog.ts";
|
|
92
|
+
import { deliverSubagentPrompt } from "./vendor/dsh-subagent/internal.ts";
|
|
80
93
|
|
|
81
|
-
export type {} from
|
|
82
|
-
export * from
|
|
83
|
-
export {
|
|
84
|
-
|
|
94
|
+
export type {} from "./vendor/dsh-subagent/catalog.ts";
|
|
95
|
+
export * from "./vendor/dsh-subagent/out-of-process.ts";
|
|
96
|
+
export {
|
|
97
|
+
AssistantOutputFold,
|
|
98
|
+
finalAssistantOutput,
|
|
99
|
+
} from "./vendor/dsh-subagent/assistant-output.ts";
|
|
100
|
+
export { SubagentRunId } from "./vendor/dsh-subagent/types.ts";
|
|
85
101
|
export type {
|
|
86
102
|
ContinuableCreateRequest,
|
|
87
103
|
ContinuableCreateSpec,
|
|
@@ -97,21 +113,21 @@ export type {
|
|
|
97
113
|
ResolvedSubagentStartRequest,
|
|
98
114
|
SubagentStopReason,
|
|
99
115
|
SubagentStopReasonMap,
|
|
100
|
-
} from
|
|
116
|
+
} from "./vendor/dsh-subagent/types.ts";
|
|
101
117
|
export {
|
|
102
118
|
foldSubagentDescriptor,
|
|
103
119
|
snapshotSubagentDescriptor,
|
|
104
120
|
SUBAGENT_DESCRIPTOR_VERSION,
|
|
105
|
-
} from
|
|
121
|
+
} from "./vendor/dsh-subagent/descriptor.ts";
|
|
106
122
|
export type {
|
|
107
123
|
ContinuableSubagentDescriptorData,
|
|
108
124
|
ContinuableSubagentDescriptorInput,
|
|
109
125
|
OneShotSubagentDescriptorData,
|
|
110
126
|
SubagentDescriptorData,
|
|
111
|
-
} from
|
|
112
|
-
export { STRUCTURED_OUTPUT_TOOL } from
|
|
113
|
-
export { SubagentError } from
|
|
114
|
-
export { assertSubagentMaxDepth, delegationDepthOf } from
|
|
127
|
+
} from "./vendor/dsh-subagent/descriptor.ts";
|
|
128
|
+
export { STRUCTURED_OUTPUT_TOOL } from "./vendor/dsh-subagent/structured.ts";
|
|
129
|
+
export { SubagentError } from "./vendor/dsh-subagent/error.ts";
|
|
130
|
+
export { assertSubagentMaxDepth, delegationDepthOf } from "./vendor/dsh-subagent/depth.ts";
|
|
115
131
|
export {
|
|
116
132
|
appendDelegatedPolicyOverrides,
|
|
117
133
|
applyChildComposition,
|
|
@@ -121,13 +137,20 @@ export {
|
|
|
121
137
|
resolveChildAgentOptions,
|
|
122
138
|
resolveChildDepth,
|
|
123
139
|
SubagentDepthError,
|
|
124
|
-
} from
|
|
125
|
-
export type {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
export type {
|
|
130
|
-
export type
|
|
140
|
+
} from "./vendor/dsh-subagent/child-agent.ts";
|
|
141
|
+
export type {
|
|
142
|
+
ChildComposition,
|
|
143
|
+
DelegatedPolicyOverrides,
|
|
144
|
+
} from "./vendor/dsh-subagent/child-agent.ts";
|
|
145
|
+
export type { AgentMessageSource, SubagentSettledMessageSource } from "./continuation-messages.ts";
|
|
146
|
+
export type * from "./vendor/dsh-subagent/control-types.ts";
|
|
147
|
+
export type { SubagentDescendantListEntry } from "./vendor/dsh-subagent/list-children.ts";
|
|
148
|
+
export type { SubagentRunEndInfo, SubagentRunInfo } from "./vendor/dsh-subagent/types.ts";
|
|
149
|
+
export type {
|
|
150
|
+
SubagentCatalogEntry,
|
|
151
|
+
SubagentIdentityProjection,
|
|
152
|
+
SubagentTimingProjection,
|
|
153
|
+
} from "./vendor/dsh-subagent/projection-types.ts";
|
|
131
154
|
|
|
132
155
|
/**
|
|
133
156
|
* Durable attribution of one browser-authored follow-up. The Session
|
|
@@ -137,73 +160,95 @@ export type { SubagentCatalogEntry, SubagentIdentityProjection, SubagentTimingPr
|
|
|
137
160
|
* Client reconciles its optimistic prompt against.
|
|
138
161
|
*/
|
|
139
162
|
interface BrowserPromptSource {
|
|
140
|
-
readonly kind:
|
|
141
|
-
readonly rpcId: SubagentPromptRequestId
|
|
142
|
-
readonly clientTimeZone?: string
|
|
163
|
+
readonly kind: "user";
|
|
164
|
+
readonly rpcId: SubagentPromptRequestId;
|
|
165
|
+
readonly clientTimeZone?: string;
|
|
143
166
|
/** Caller that submitted this follow-up, written only when it is not the owner; absent means the owner. */
|
|
144
|
-
readonly principal?: Principal
|
|
167
|
+
readonly principal?: Principal;
|
|
145
168
|
}
|
|
146
169
|
|
|
147
170
|
/** Host configuration for continuable subagent capacity. */
|
|
148
171
|
export interface Config {
|
|
149
172
|
/** Maximum live children sharing uninterrupted continuable parent links; defaults to 8. */
|
|
150
|
-
maxActiveSubagents: Volatile<number
|
|
173
|
+
maxActiveSubagents: Volatile<number>;
|
|
151
174
|
/** Default delegation depth for tools without an explicit limit; defaults to 1. */
|
|
152
|
-
maxDepth: Volatile<number
|
|
175
|
+
maxDepth: Volatile<number>;
|
|
153
176
|
/**
|
|
154
177
|
* Preset ids the Chinese return guidance is restricted to; empty (default) means no restriction.
|
|
155
178
|
*
|
|
156
179
|
* 不配 = 任意 preset 的会话都用本包的中文回报指引(官方四个 shipped preset 也在内,还有还没绑 preset 的会话)。
|
|
157
180
|
* 要反过来只让某几份 preset 用中文,就在这里列出来。装配面,不是用户面——所以不进设置页(`.hidden()`)。
|
|
158
181
|
*/
|
|
159
|
-
localizedReturnGuidancePresets: string[]
|
|
182
|
+
localizedReturnGuidancePresets: string[];
|
|
160
183
|
}
|
|
161
184
|
|
|
162
185
|
/** Named provider registry with managed activations, durable discovery, and local child messaging. */
|
|
163
186
|
export class SubagentRuntime extends TypertRemoteService {
|
|
164
187
|
static Config = z.object({
|
|
165
188
|
maxDepth: z.number().step(1).min(0).max(Number.MAX_SAFE_INTEGER).default(1).volatile(),
|
|
166
|
-
maxActiveSubagents: z
|
|
189
|
+
maxActiveSubagents: z
|
|
190
|
+
.number()
|
|
191
|
+
.step(1)
|
|
192
|
+
.min(1)
|
|
193
|
+
.max(Number.MAX_SAFE_INTEGER)
|
|
194
|
+
.default(8)
|
|
195
|
+
.volatile(),
|
|
167
196
|
localizedReturnGuidancePresets: z.array(z.string()).default([]).hidden(),
|
|
168
|
-
})
|
|
169
|
-
static inject = [
|
|
170
|
-
private providers = new Map<string, SubagentProvider>()
|
|
171
|
-
private manager: SubagentManager | undefined
|
|
197
|
+
});
|
|
198
|
+
static inject = ["workingDirectory"];
|
|
199
|
+
private providers = new Map<string, SubagentProvider>();
|
|
200
|
+
private manager: SubagentManager | undefined;
|
|
172
201
|
/**
|
|
173
202
|
* The contained lifecycle-edge publisher. Built here because scoped dispatch
|
|
174
203
|
* keys its carrier by this exact service instance, whose own context filter
|
|
175
204
|
* composes into the carrier.
|
|
176
205
|
*/
|
|
177
|
-
private readonly emitLifecycle: LifecycleEmitter
|
|
206
|
+
private readonly emitLifecycle: LifecycleEmitter;
|
|
178
207
|
|
|
179
|
-
constructor(
|
|
180
|
-
|
|
208
|
+
constructor(
|
|
209
|
+
ctx: Context,
|
|
210
|
+
private config: Config,
|
|
211
|
+
) {
|
|
212
|
+
super(ctx, "subagents");
|
|
181
213
|
// 模型看到的委派范围说明换成中文(见 `./delegation-context.ts`)。
|
|
182
|
-
installDelegationContext(ctx)
|
|
183
|
-
this.emitLifecycle = createLifecycleEmitter(this.ctx, parent => scopeTarget(this, parent))
|
|
184
|
-
ctx.inject([
|
|
185
|
-
const manager = new SubagentManager(
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
214
|
+
installDelegationContext(ctx);
|
|
215
|
+
this.emitLifecycle = createLifecycleEmitter(this.ctx, (parent) => scopeTarget(this, parent));
|
|
216
|
+
ctx.inject(["agents"], (childCtx: Context) => {
|
|
217
|
+
const manager = new SubagentManager(
|
|
218
|
+
childCtx,
|
|
219
|
+
{
|
|
220
|
+
startExternal: (name, request) => {
|
|
221
|
+
const provider = this.expectProvider(name) as SubagentProvider &
|
|
222
|
+
Required<Pick<SubagentProvider, "start">>;
|
|
223
|
+
return provider.start(request);
|
|
224
|
+
},
|
|
225
|
+
prepareContinuable: (name, request) => this.prepareContinuable(name, request),
|
|
226
|
+
observeActivation: (provider, childId, parent) =>
|
|
227
|
+
this.observeActivation(provider, childId, parent),
|
|
228
|
+
},
|
|
229
|
+
() => this.config.maxActiveSubagents.get(),
|
|
230
|
+
() => this.config.localizedReturnGuidancePresets,
|
|
231
|
+
);
|
|
232
|
+
this.manager = manager;
|
|
233
|
+
childCtx.effect(
|
|
234
|
+
() => () => {
|
|
235
|
+
/* v8 ignore else -- one injected binding owns the slot until its fiber disposes. */
|
|
236
|
+
if (this.manager === manager) this.manager = undefined;
|
|
189
237
|
},
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
})
|
|
199
|
-
ctx.inject(['sessionProjections'], (projectionCtx) => {
|
|
200
|
-
projectionCtx.sessionProjections.register(subagentCatalogProjectionDefinition)
|
|
201
|
-
projectionCtx.sessionProjections.register(subagentTimingProjectionDefinition)
|
|
202
|
-
projectionCtx.sessionProjections.register(subagentIdentityProjectionDefinition)
|
|
203
|
-
})
|
|
238
|
+
"subagents.managerBinding()",
|
|
239
|
+
);
|
|
240
|
+
});
|
|
241
|
+
ctx.inject(["sessionProjections"], async (projectionCtx) => {
|
|
242
|
+
// 上游 0.2.1-rc.2 起 `register()` 是异步的(句柄随注册一起交付),注册面要 await。
|
|
243
|
+
await projectionCtx.sessionProjections.register(subagentCatalogProjectionDefinition);
|
|
244
|
+
await projectionCtx.sessionProjections.register(subagentTimingProjectionDefinition);
|
|
245
|
+
await projectionCtx.sessionProjections.register(subagentIdentityProjectionDefinition);
|
|
246
|
+
});
|
|
204
247
|
// Archive admission: this runtime is the owner that knows which live
|
|
205
248
|
// children descend from a Session and how a parent stops them.
|
|
206
|
-
ctx.inject([
|
|
249
|
+
ctx.inject(["agents"], (agentsCtx: Context) => {
|
|
250
|
+
installSubagentArchiveAdmission(agentsCtx);
|
|
251
|
+
});
|
|
207
252
|
}
|
|
208
253
|
|
|
209
254
|
/**
|
|
@@ -211,12 +256,12 @@ export class SubagentRuntime extends TypertRemoteService {
|
|
|
211
256
|
* @param configured - Explicit tool limit, or provider-managed for external delegation.
|
|
212
257
|
* @returns The numeric limit, or undefined when the provider owns depth enforcement.
|
|
213
258
|
*/
|
|
214
|
-
resolveMaxDepth(configured?: number |
|
|
215
|
-
if (configured ===
|
|
216
|
-
if (configured !== undefined) return configured
|
|
217
|
-
const depth = this.config.maxDepth.get()
|
|
218
|
-
assertSubagentMaxDepth(depth)
|
|
219
|
-
return depth
|
|
259
|
+
resolveMaxDepth(configured?: number | "provider-managed"): number | undefined {
|
|
260
|
+
if (configured === "provider-managed") return undefined;
|
|
261
|
+
if (configured !== undefined) return configured;
|
|
262
|
+
const depth = this.config.maxDepth.get();
|
|
263
|
+
assertSubagentMaxDepth(depth);
|
|
264
|
+
return depth;
|
|
220
265
|
}
|
|
221
266
|
|
|
222
267
|
/**
|
|
@@ -224,14 +269,15 @@ export class SubagentRuntime extends TypertRemoteService {
|
|
|
224
269
|
* @param spec - local task, reserved child id, and result recipient.
|
|
225
270
|
* @returns activation with its accepted initial message id.
|
|
226
271
|
*/
|
|
227
|
-
startActivation(
|
|
228
|
-
|
|
272
|
+
startActivation(
|
|
273
|
+
spec: SubagentActivationSpec & { readonly childId: SessionId },
|
|
274
|
+
): Promise<SubagentActivation & { readonly messageId: MessageId }>;
|
|
229
275
|
/**
|
|
230
276
|
* Start a local or external child.
|
|
231
277
|
* @param spec - task, backend, and result recipient.
|
|
232
278
|
* @returns activation with a message id only for local children.
|
|
233
279
|
*/
|
|
234
|
-
startActivation(spec: SubagentActivationSpec): Promise<SubagentActivation
|
|
280
|
+
startActivation(spec: SubagentActivationSpec): Promise<SubagentActivation>;
|
|
235
281
|
/**
|
|
236
282
|
* Establish a managed child with backend-specific execution capabilities.
|
|
237
283
|
* @param spec - task, backend, and result recipient.
|
|
@@ -239,20 +285,26 @@ export class SubagentRuntime extends TypertRemoteService {
|
|
|
239
285
|
* childId requires a local backend and guarantees an accepted messageId.
|
|
240
286
|
*/
|
|
241
287
|
async startActivation(spec: SubagentActivationSpec): Promise<SubagentActivation> {
|
|
242
|
-
const provider = this.expectProvider(spec.provider)
|
|
288
|
+
const provider = this.expectProvider(spec.provider);
|
|
243
289
|
if (spec.childId !== undefined && provider.prepareContinuable === undefined) {
|
|
244
|
-
throw new SubagentError(
|
|
290
|
+
throw new SubagentError(
|
|
291
|
+
"reserved child ids require a local backend",
|
|
292
|
+
"UNSUPPORTED_CAPABILITY",
|
|
293
|
+
);
|
|
245
294
|
}
|
|
246
|
-
assertSubagentMaxDepth(spec.request.maxDepth)
|
|
247
|
-
this.assertCapabilities(provider, { ...spec.request, signal: spec.signal })
|
|
248
|
-
const manager = this.requireManager()
|
|
249
|
-
if (this.ctx.get(
|
|
250
|
-
throw new SubagentError(
|
|
295
|
+
assertSubagentMaxDepth(spec.request.maxDepth);
|
|
296
|
+
this.assertCapabilities(provider, { ...spec.request, signal: spec.signal });
|
|
297
|
+
const manager = this.requireManager();
|
|
298
|
+
if (this.ctx.get("agents")?.get(spec.request.parent.id) !== spec.request.parent) {
|
|
299
|
+
throw new SubagentError(
|
|
300
|
+
"subagent creation requires the exact live parent agent",
|
|
301
|
+
"UNAUTHORIZED",
|
|
302
|
+
);
|
|
251
303
|
}
|
|
252
|
-
if (spec.request.outputSchema !== undefined) assertObjectJsonSchema(spec.request.outputSchema)
|
|
304
|
+
if (spec.request.outputSchema !== undefined) assertObjectJsonSchema(spec.request.outputSchema);
|
|
253
305
|
return provider.prepareContinuable === undefined
|
|
254
306
|
? manager.startExternal(spec)
|
|
255
|
-
: manager.startLocal(spec)
|
|
307
|
+
: manager.startLocal(spec);
|
|
256
308
|
}
|
|
257
309
|
|
|
258
310
|
/**
|
|
@@ -262,7 +314,7 @@ export class SubagentRuntime extends TypertRemoteService {
|
|
|
262
314
|
* @returns whether work was joined; hosts recheck parent idle after true.
|
|
263
315
|
*/
|
|
264
316
|
async waitForChildren(parent: Agent): Promise<boolean> {
|
|
265
|
-
return this.manager?.waitForChildren(parent) ?? false
|
|
317
|
+
return this.manager?.waitForChildren(parent) ?? false;
|
|
266
318
|
}
|
|
267
319
|
|
|
268
320
|
/**
|
|
@@ -285,7 +337,7 @@ export class SubagentRuntime extends TypertRemoteService {
|
|
|
285
337
|
content: ContentBlock[],
|
|
286
338
|
options: SubagentSendMessageOptions,
|
|
287
339
|
): Promise<MessageId> {
|
|
288
|
-
return this.requireManager().sendMessage(sender, targetId, content, options)
|
|
340
|
+
return this.requireManager().sendMessage(sender, targetId, content, options);
|
|
289
341
|
}
|
|
290
342
|
|
|
291
343
|
/**
|
|
@@ -308,9 +360,9 @@ export class SubagentRuntime extends TypertRemoteService {
|
|
|
308
360
|
signal: AbortSignal,
|
|
309
361
|
delivery: SubagentDelivery,
|
|
310
362
|
): Promise<MessageId> {
|
|
311
|
-
return delivery ===
|
|
363
|
+
return delivery === "steer"
|
|
312
364
|
? this.requireManager().steerPrompt(parent, childId, content, source, signal)
|
|
313
|
-
: this.requireManager().queuePrompt(parent, childId, content, source, signal)
|
|
365
|
+
: this.requireManager().queuePrompt(parent, childId, content, source, signal);
|
|
314
366
|
}
|
|
315
367
|
|
|
316
368
|
/**
|
|
@@ -329,7 +381,7 @@ export class SubagentRuntime extends TypertRemoteService {
|
|
|
329
381
|
* live target.
|
|
330
382
|
*/
|
|
331
383
|
interrupt(targetSessionId: SessionId, authority: SubagentInterruptAuthority): void {
|
|
332
|
-
this.manager?.interrupt(targetSessionId, authority)
|
|
384
|
+
this.manager?.interrupt(targetSessionId, authority);
|
|
333
385
|
}
|
|
334
386
|
|
|
335
387
|
/**
|
|
@@ -343,10 +395,10 @@ export class SubagentRuntime extends TypertRemoteService {
|
|
|
343
395
|
* @throws an aggregate error after all branches settle when any failed.
|
|
344
396
|
*/
|
|
345
397
|
async drainDescendants(parents: readonly Agent[]): Promise<void> {
|
|
346
|
-
const manager = this.manager
|
|
398
|
+
const manager = this.manager;
|
|
347
399
|
// An absent activation manager cannot own materialized children.
|
|
348
|
-
if (manager === undefined) return
|
|
349
|
-
await manager.drainDescendants(parents)
|
|
400
|
+
if (manager === undefined) return;
|
|
401
|
+
await manager.drainDescendants(parents);
|
|
350
402
|
}
|
|
351
403
|
|
|
352
404
|
/**
|
|
@@ -360,9 +412,9 @@ export class SubagentRuntime extends TypertRemoteService {
|
|
|
360
412
|
* different parent or the supplied parent identity is stale.
|
|
361
413
|
*/
|
|
362
414
|
async drainChildren(parent: Agent, childIds: readonly SessionId[]): Promise<void> {
|
|
363
|
-
const manager = this.manager
|
|
364
|
-
if (manager === undefined) return
|
|
365
|
-
await manager.drainChildren(parent, childIds)
|
|
415
|
+
const manager = this.manager;
|
|
416
|
+
if (manager === undefined) return;
|
|
417
|
+
await manager.drainChildren(parent, childIds);
|
|
366
418
|
}
|
|
367
419
|
|
|
368
420
|
/**
|
|
@@ -375,7 +427,7 @@ export class SubagentRuntime extends TypertRemoteService {
|
|
|
375
427
|
* @throws SessionQueryError when the parent cannot be read or the query is cancelled.
|
|
376
428
|
*/
|
|
377
429
|
listChildren(parentSessionId: SessionId, signal?: AbortSignal): Promise<SubagentCatalogEntry[]> {
|
|
378
|
-
return listSubagentChildren(this.ctx, parentSessionId, signal)
|
|
430
|
+
return listSubagentChildren(this.ctx, parentSessionId, signal);
|
|
379
431
|
}
|
|
380
432
|
|
|
381
433
|
/**
|
|
@@ -394,8 +446,11 @@ export class SubagentRuntime extends TypertRemoteService {
|
|
|
394
446
|
* @throws {@link SubagentError} when listing dependencies are unavailable or the caller cancels.
|
|
395
447
|
* @throws SessionQueryError when the root catalog cannot be read.
|
|
396
448
|
*/
|
|
397
|
-
listDescendants(
|
|
398
|
-
|
|
449
|
+
listDescendants(
|
|
450
|
+
rootSessionId: SessionId,
|
|
451
|
+
signal?: AbortSignal,
|
|
452
|
+
): Promise<SubagentDescendantListEntry[]> {
|
|
453
|
+
return listSubagentDescendants(this.ctx, rootSessionId, signal);
|
|
399
454
|
}
|
|
400
455
|
|
|
401
456
|
/**
|
|
@@ -416,45 +471,52 @@ export class SubagentRuntime extends TypertRemoteService {
|
|
|
416
471
|
* `subagent/not-resumable`, `subagent/unauthorized`,
|
|
417
472
|
* `subagent/delivery-unavailable`, `gateway/cancelled`, or `gateway/internal`.
|
|
418
473
|
*/
|
|
419
|
-
@Remote(
|
|
420
|
-
async prompt(
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
474
|
+
@Remote("prompt")
|
|
475
|
+
async prompt(
|
|
476
|
+
request: SubagentPromptRequest,
|
|
477
|
+
signal: AbortSignal,
|
|
478
|
+
): Promise<SubagentPromptReceipt> {
|
|
479
|
+
const { parentSessionId, childSessionId, clientTimeZone, delivery } = request;
|
|
480
|
+
validateControlRequest("subagent.prompt", request);
|
|
481
|
+
const canonicalTimeZone =
|
|
482
|
+
clientTimeZone === undefined ? undefined : canonicalClientTimeZone(clientTimeZone);
|
|
426
483
|
if (clientTimeZone !== undefined && canonicalTimeZone === undefined) {
|
|
427
484
|
throw new RemoteError(
|
|
428
|
-
|
|
429
|
-
|
|
485
|
+
"subagent/invalid-time-zone",
|
|
486
|
+
"clientTimeZone must be UTC or a valid IANA Area/Location name",
|
|
430
487
|
{ value: clientTimeZone },
|
|
431
|
-
)
|
|
488
|
+
);
|
|
432
489
|
}
|
|
433
|
-
const parent = this.ctx.get(
|
|
490
|
+
const parent = this.ctx.get("agents")?.get(parentSessionId);
|
|
434
491
|
if (parent === undefined) {
|
|
435
492
|
throw new RemoteError(
|
|
436
|
-
|
|
493
|
+
"subagent/parent-unavailable",
|
|
437
494
|
`parent session "${parentSessionId}" is not live`,
|
|
438
495
|
{ parentSessionId },
|
|
439
|
-
)
|
|
496
|
+
);
|
|
440
497
|
}
|
|
441
|
-
const principal = principalOf(this.ctx.invocation)
|
|
498
|
+
const principal = principalOf(this.ctx.invocation);
|
|
442
499
|
const source: BrowserPromptSource = {
|
|
443
|
-
kind:
|
|
500
|
+
kind: "user",
|
|
444
501
|
rpcId: request.requestId,
|
|
445
502
|
...(canonicalTimeZone === undefined ? {} : { clientTimeZone: canonicalTimeZone }),
|
|
446
503
|
...(principal === undefined ? {} : { principal }),
|
|
447
|
-
}
|
|
504
|
+
};
|
|
448
505
|
try {
|
|
449
506
|
// Admission precedes delivery: image parts become durable references
|
|
450
507
|
// here, so the child inbox only ever accepts Host-persisted attachments.
|
|
451
|
-
let content: ContentBlock[]
|
|
452
|
-
if (
|
|
453
|
-
|
|
508
|
+
let content: ContentBlock[];
|
|
509
|
+
if (
|
|
510
|
+
request.content.every(
|
|
511
|
+
(part): part is { readonly type: "text"; readonly text: string } => part.type === "text",
|
|
512
|
+
)
|
|
513
|
+
) {
|
|
514
|
+
content = request.content.map((part) => ({ type: "text", text: part.text }));
|
|
454
515
|
} else {
|
|
455
|
-
const attachments = this.ctx.get(
|
|
456
|
-
if (attachments === undefined)
|
|
457
|
-
|
|
516
|
+
const attachments = this.ctx.get("attachments");
|
|
517
|
+
if (attachments === undefined)
|
|
518
|
+
throw new Error("subagent image prompt requires an attachment store");
|
|
519
|
+
content = await attachments.admitPromptContent(request.content);
|
|
458
520
|
}
|
|
459
521
|
return {
|
|
460
522
|
messageId: await this[deliverSubagentPrompt](
|
|
@@ -465,9 +527,9 @@ export class SubagentRuntime extends TypertRemoteService {
|
|
|
465
527
|
signal,
|
|
466
528
|
delivery,
|
|
467
529
|
),
|
|
468
|
-
}
|
|
530
|
+
};
|
|
469
531
|
} catch (error: unknown) {
|
|
470
|
-
return rejectPrompt(error, childSessionId, signal)
|
|
532
|
+
return rejectPrompt(error, childSessionId, signal);
|
|
471
533
|
}
|
|
472
534
|
}
|
|
473
535
|
|
|
@@ -485,27 +547,27 @@ export class SubagentRuntime extends TypertRemoteService {
|
|
|
485
547
|
* `subagent/unauthorized` when the address does not own the live target,
|
|
486
548
|
* otherwise `gateway/internal`.
|
|
487
549
|
*/
|
|
488
|
-
@Remote(
|
|
550
|
+
@Remote("interruptByParent")
|
|
489
551
|
interruptByParent(
|
|
490
552
|
childSessionId: SessionId,
|
|
491
553
|
parentSessionId: SessionId,
|
|
492
|
-
mode:
|
|
554
|
+
mode: "continuable",
|
|
493
555
|
): SubagentInterruptReceipt {
|
|
494
|
-
validateControlRequest(
|
|
556
|
+
validateControlRequest("subagent.interrupt", { childSessionId, parentSessionId, mode });
|
|
495
557
|
try {
|
|
496
|
-
this.interrupt(childSessionId, { kind:
|
|
558
|
+
this.interrupt(childSessionId, { kind: "user", parentSessionId });
|
|
497
559
|
} catch (error: unknown) {
|
|
498
|
-
if (error instanceof SubagentError && error.code ===
|
|
560
|
+
if (error instanceof SubagentError && error.code === "UNAUTHORIZED") {
|
|
499
561
|
throw new RemoteError(
|
|
500
|
-
|
|
501
|
-
|
|
562
|
+
"subagent/unauthorized",
|
|
563
|
+
"subagent does not belong to this parent",
|
|
502
564
|
{ childSessionId },
|
|
503
565
|
{ cause: error },
|
|
504
|
-
)
|
|
566
|
+
);
|
|
505
567
|
}
|
|
506
|
-
throw new RemoteError(
|
|
568
|
+
throw new RemoteError("gateway/internal", "subagent interrupt failed", {}, { cause: error });
|
|
507
569
|
}
|
|
508
|
-
return { accepted: true }
|
|
570
|
+
return { accepted: true };
|
|
509
571
|
}
|
|
510
572
|
|
|
511
573
|
/**
|
|
@@ -517,24 +579,33 @@ export class SubagentRuntime extends TypertRemoteService {
|
|
|
517
579
|
* @returns the exact Cordis effect disposer.
|
|
518
580
|
*/
|
|
519
581
|
registerProvider(provider: SubagentProvider): () => void {
|
|
520
|
-
const name = provider.name
|
|
582
|
+
const name = provider.name;
|
|
521
583
|
if (provider.start === undefined && provider.prepareContinuable === undefined) {
|
|
522
|
-
throw new SubagentError(
|
|
584
|
+
throw new SubagentError(
|
|
585
|
+
`subagent provider "${name}" must implement start or prepareContinuable`,
|
|
586
|
+
"UNSUPPORTED_CAPABILITY",
|
|
587
|
+
);
|
|
523
588
|
}
|
|
524
589
|
// oxlint-disable-next-line typescript/no-misused-promises -- synchronous disposer
|
|
525
|
-
return this.ctx.effect(
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
590
|
+
return this.ctx.effect(
|
|
591
|
+
function* (this: SubagentRuntime) {
|
|
592
|
+
if (this.providers.has(name)) {
|
|
593
|
+
throw new SubagentError(
|
|
594
|
+
`a subagent provider named "${name}" is already registered`,
|
|
595
|
+
"DUPLICATE_PROVIDER",
|
|
596
|
+
);
|
|
597
|
+
}
|
|
598
|
+
this.providers.set(name, provider);
|
|
599
|
+
yield () => {
|
|
600
|
+
this.providers.delete(name);
|
|
601
|
+
this.emitLifecycle("subagent/provider-removed", name);
|
|
602
|
+
};
|
|
603
|
+
// A throwing added-listener unwinds the yielded rollback, matching the
|
|
604
|
+
// repository's fail-loud registration semantics.
|
|
605
|
+
this.ctx.emit("subagent/provider-added", provider);
|
|
606
|
+
}.bind(this),
|
|
607
|
+
"subagents.registerProvider()",
|
|
608
|
+
);
|
|
538
609
|
}
|
|
539
610
|
|
|
540
611
|
/**
|
|
@@ -543,7 +614,7 @@ export class SubagentRuntime extends TypertRemoteService {
|
|
|
543
614
|
* @returns the provider, or undefined when absent.
|
|
544
615
|
*/
|
|
545
616
|
getProvider(name: string): SubagentProvider | undefined {
|
|
546
|
-
return this.providers.get(name)
|
|
617
|
+
return this.providers.get(name);
|
|
547
618
|
}
|
|
548
619
|
|
|
549
620
|
/**
|
|
@@ -551,7 +622,7 @@ export class SubagentRuntime extends TypertRemoteService {
|
|
|
551
622
|
* @returns the registered names.
|
|
552
623
|
*/
|
|
553
624
|
list(): string[] {
|
|
554
|
-
return [...this.providers.keys()]
|
|
625
|
+
return [...this.providers.keys()];
|
|
555
626
|
}
|
|
556
627
|
|
|
557
628
|
/**
|
|
@@ -563,28 +634,29 @@ export class SubagentRuntime extends TypertRemoteService {
|
|
|
563
634
|
name: string,
|
|
564
635
|
request: ContinuableCreateRequest,
|
|
565
636
|
): Promise<ContinuableCreateSpec> {
|
|
566
|
-
const provider = this.expectProvider(name) as SubagentProvider &
|
|
567
|
-
|
|
637
|
+
const provider = this.expectProvider(name) as SubagentProvider &
|
|
638
|
+
Required<Pick<SubagentProvider, "prepareContinuable">>;
|
|
639
|
+
return provider.prepareContinuable(request);
|
|
568
640
|
}
|
|
569
641
|
|
|
570
642
|
/** Look up a provider for dispatch or fail loud. */
|
|
571
643
|
private expectProvider(name: string): SubagentProvider {
|
|
572
|
-
const provider = this.providers.get(name)
|
|
644
|
+
const provider = this.providers.get(name);
|
|
573
645
|
if (provider === undefined) {
|
|
574
|
-
throw new SubagentError(`no subagent provider registered for "${name}"`,
|
|
646
|
+
throw new SubagentError(`no subagent provider registered for "${name}"`, "NO_PROVIDER");
|
|
575
647
|
}
|
|
576
|
-
return provider
|
|
648
|
+
return provider;
|
|
577
649
|
}
|
|
578
650
|
|
|
579
651
|
/** Resolve the subagent manager or fail loud. */
|
|
580
652
|
private requireManager(): SubagentManager {
|
|
581
653
|
if (this.manager === undefined) {
|
|
582
654
|
throw new SubagentError(
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
)
|
|
655
|
+
"continuable subagents require the agents service",
|
|
656
|
+
"CONTINUATION_UNAVAILABLE",
|
|
657
|
+
);
|
|
586
658
|
}
|
|
587
|
-
return this.manager
|
|
659
|
+
return this.manager;
|
|
588
660
|
}
|
|
589
661
|
|
|
590
662
|
/**
|
|
@@ -596,32 +668,34 @@ export class SubagentRuntime extends TypertRemoteService {
|
|
|
596
668
|
childId: SessionId,
|
|
597
669
|
parent: Agent,
|
|
598
670
|
): ActivationObserver {
|
|
599
|
-
return createActivationObserver(this.emitLifecycle, provider, childId, parent)
|
|
671
|
+
return createActivationObserver(this.emitLifecycle, provider, childId, parent);
|
|
600
672
|
}
|
|
601
673
|
|
|
602
674
|
/** Reject the first requested capability that the provider lacks. */
|
|
603
675
|
private assertCapabilities(provider: SubagentProvider, request: SubagentStartRequest): void {
|
|
604
|
-
if (
|
|
676
|
+
if (
|
|
677
|
+
request.agentPreset !== undefined &&
|
|
678
|
+
(!provider.capabilities.agentPreset || provider.inheritsParentContext)
|
|
679
|
+
) {
|
|
605
680
|
throw new SubagentError(
|
|
606
681
|
`subagent provider "${provider.name}" does not support independent agent preset selection`,
|
|
607
|
-
|
|
608
|
-
)
|
|
682
|
+
"UNSUPPORTED_CAPABILITY",
|
|
683
|
+
);
|
|
609
684
|
}
|
|
610
685
|
const needs: { when: boolean; cap: keyof SubagentCapabilities }[] = [
|
|
611
|
-
{ when: request.agentOptions !== undefined, cap:
|
|
612
|
-
{ when: request.outputSchema !== undefined, cap:
|
|
613
|
-
{ when: request.maxDepth !== undefined, cap:
|
|
614
|
-
]
|
|
686
|
+
{ when: request.agentOptions !== undefined, cap: "agentOptions" },
|
|
687
|
+
{ when: request.outputSchema !== undefined, cap: "outputSchema" },
|
|
688
|
+
{ when: request.maxDepth !== undefined, cap: "depthLimit" },
|
|
689
|
+
];
|
|
615
690
|
for (const { when, cap } of needs) {
|
|
616
691
|
if (when && !provider.capabilities[cap]) {
|
|
617
692
|
throw new SubagentError(
|
|
618
693
|
`subagent provider "${provider.name}" does not support the "${cap}" capability`,
|
|
619
|
-
|
|
620
|
-
)
|
|
694
|
+
"UNSUPPORTED_CAPABILITY",
|
|
695
|
+
);
|
|
621
696
|
}
|
|
622
697
|
}
|
|
623
698
|
}
|
|
624
|
-
|
|
625
699
|
}
|
|
626
700
|
|
|
627
|
-
export default SubagentRuntime
|
|
701
|
+
export default SubagentRuntime;
|