@deepseek-ai/dsh-subagent 0.1.2-alpha.2 → 0.1.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 +2 -2
- package/README.md +1 -1
- package/README.zh.md +1 -1
- package/lib/index.js +52 -29
- package/lib/typert.host.js +14 -105
- package/lib/typert.remote-client.js +8 -91
- package/lib/types/continuation.d.ts +13 -0
- package/lib/types/continuation.js +51 -3
- package/lib/types/control-types.d.ts +11 -27
- package/lib/types/control.d.ts +1 -17
- package/lib/types/control.js +6 -24
- package/lib/types/index.d.ts +3 -1
- package/lib/types/index.js +17 -3
- package/package.json +44 -44
package/README.i18n.yaml
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
3
|
# after editing either side, bring the other along and re-record with:
|
|
4
4
|
# pnpm run verify-translation-pairing --write packages/subagent/subagent/README.md
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: f13de661015f3aa59b776273376f07653f6bbba5
|
|
6
|
+
README.zh.md: 42923504ed11b8eff7c0bdcd9d0c9505d741f42e
|
package/README.md
CHANGED
|
@@ -48,7 +48,7 @@ One-shot children run once and settle with a single result, plus an optional str
|
|
|
48
48
|
|
|
49
49
|
### Following up, interrupting, and discovering
|
|
50
50
|
|
|
51
|
-
Continuable children answer follow-up messages as their next turns, and the parent can interrupt a running turn or list its children at any time. Discovery covers both shapes: the service lists direct children and the full descendant tree — mode, activity, and lineage — reading live session state and optional persistence, without loading any child.
|
|
51
|
+
Continuable children answer follow-up messages as their next turns, and the parent can interrupt a running turn or list its children at any time. A browser continuation prompt may carry image parts: the Host admits and persists each image batch through the attachment store before the child inbox accepts the message, and refuses delivery when the child's declared model does not accept image input. Discovery covers both shapes: the service lists direct children and the full descendant tree — mode, activity, and lineage — reading live session state and optional persistence, without loading any child.
|
|
52
52
|
|
|
53
53
|
### Failure and recovery
|
|
54
54
|
|
package/README.zh.md
CHANGED
|
@@ -48,7 +48,7 @@ kind: "package-reference"
|
|
|
48
48
|
|
|
49
49
|
### 后续消息、中断与发现
|
|
50
50
|
|
|
51
|
-
可继续子 agent
|
|
51
|
+
可继续子 agent 把后续消息作为下一个轮次回答,父级随时可以中断运行中的轮次或列举自己的子级。浏览器发出的继续执行 prompt 可以携带图片部分:Host 先通过附件存储完成整批图片的准入与持久化,子级 inbox 才接受这条消息;当子级声明的模型不接受图片输入时拒绝投递。发现覆盖两种形态:服务列举直接子级与完整后代树——模式、活动状态与血缘——直接读取在线会话状态与可选持久化,不加载任何子 agent。
|
|
52
52
|
|
|
53
53
|
### 失败与恢复
|
|
54
54
|
|
package/lib/index.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { AttachmentError, admitPromptContent } from "@deepseek-ai/dsh-attachment";
|
|
1
2
|
import { scopeTarget } from "@deepseek-ai/dsh-scope";
|
|
2
3
|
import { assertObjectJsonSchema } from "@deepseek-ai/dsh-tools";
|
|
3
4
|
import { canonicalClientTimeZone } from "@deepseek-ai/dsh-util-time";
|
|
4
5
|
import { Remote, RemoteError, TypertRemoteService } from "@deepseek-ai/dsh-typert-protocol";
|
|
5
6
|
import { z } from "zod";
|
|
6
|
-
import { HarnessError, ReasoningEffortId, boundContextSummary, createUserMessage, errorChain } from "@deepseek-ai/dsh-llm";
|
|
7
|
+
import { HarnessError, ReasoningEffortId, boundContextSummary, contentHasImage, createUserMessage, errorChain } from "@deepseek-ai/dsh-llm";
|
|
7
8
|
import { randomUUID } from "node:crypto";
|
|
8
9
|
import { foldConsumedWork } from "@deepseek-ai/dsh-agent";
|
|
9
10
|
import { brandString } from "@deepseek-ai/dsh-brand";
|
|
@@ -59,31 +60,6 @@ function validateControlRequest(method, payload) {
|
|
|
59
60
|
if (!parsed.success) throw new RemoteError("gateway/bad-request", `invalid payload for ${method}`, { issues: parsed.error.issues });
|
|
60
61
|
}
|
|
61
62
|
/**
|
|
62
|
-
* Admit the content one continuation may deliver, refusing every image.
|
|
63
|
-
*
|
|
64
|
-
* The blocks become the child's user message verbatim, and this surface admits
|
|
65
|
-
* no attachment: nothing here registers encoded bytes with the attachment
|
|
66
|
-
* service, so an image would reach the child as a reference nothing resolves.
|
|
67
|
-
* The wire accepts the encoded upload so this refusal — not a Client that
|
|
68
|
-
* strips the block — is what the caller is answered with. Other block types
|
|
69
|
-
* still cross unnarrowed.
|
|
70
|
-
* @param childSessionId - the addressed child, named by the refusal.
|
|
71
|
-
* @param content - blocks the caller asked to deliver.
|
|
72
|
-
* @returns the admitted blocks, in order, as the durable content vocabulary.
|
|
73
|
-
* @throws {RemoteError} `subagent/attachment-unsupported` when any block is an image.
|
|
74
|
-
*/
|
|
75
|
-
function admitPromptContent(childSessionId, content) {
|
|
76
|
-
const admitted = [];
|
|
77
|
-
for (const block of content) {
|
|
78
|
-
if (block.type === "image") throw new RemoteError("subagent/attachment-unsupported", "subagent continuation does not accept images", {
|
|
79
|
-
childSessionId,
|
|
80
|
-
reason: "SUBAGENT_IMAGE_UNSUPPORTED"
|
|
81
|
-
});
|
|
82
|
-
admitted.push(block);
|
|
83
|
-
}
|
|
84
|
-
return admitted;
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
63
|
* Project one durable listing onto the catalog view, replacing each row's
|
|
88
64
|
* store-derived activity with the live Agent driver's status and reporting
|
|
89
65
|
* whether the exact parent Agent is live. Without an Agent registry no driver
|
|
@@ -128,7 +104,9 @@ function rejectCatalogRead(error, signal) {
|
|
|
128
104
|
*/
|
|
129
105
|
function rejectPrompt(error, childSessionId, signal) {
|
|
130
106
|
if (isCancellation(error, signal)) throw new RemoteError("gateway/cancelled", "subagent prompt was cancelled", {}, { cause: error });
|
|
107
|
+
if (error instanceof AttachmentError) throw new RemoteError("subagent/attachment-invalid", error.message, { reason: error.code }, { cause: error });
|
|
131
108
|
if (error instanceof SubagentError) switch (error.code) {
|
|
109
|
+
case "MODEL_DOES_NOT_SUPPORT_IMAGES": throw new RemoteError("subagent/attachment-invalid", error.message, { reason: error.code }, { cause: error });
|
|
132
110
|
case "NOT_RESUMABLE": throw new RemoteError("subagent/not-resumable", "subagent cannot be resumed", { childSessionId }, { cause: error });
|
|
133
111
|
case "UNAUTHORIZED": throw new RemoteError("subagent/unauthorized", "subagent does not belong to this parent", { childSessionId }, { cause: error });
|
|
134
112
|
case "DRAINING":
|
|
@@ -1070,11 +1048,19 @@ var SubagentContinuationManager = class {
|
|
|
1070
1048
|
const live = await this.locks.run(childId, async () => {
|
|
1071
1049
|
const activation = this.activations.get(childId);
|
|
1072
1050
|
if (activation === void 0) return this.coldResume(parent, childId, content, options);
|
|
1051
|
+
const disposal = activation.disposal;
|
|
1073
1052
|
/* v8 ignore next 3 -- the send-versus-dispose cutoff: reaching this arm needs a
|
|
1074
1053
|
* delivery to observe the transaction inside the same critical section that opened it,
|
|
1075
1054
|
* which no test can schedule deterministically. The behavior is covered end-to-end by
|
|
1076
1055
|
* "cold-resumes a delivery that lost the race with final disposal". */
|
|
1077
|
-
if (
|
|
1056
|
+
if (disposal !== void 0) return disposal.then(() => void 0, () => void 0);
|
|
1057
|
+
if (contentHasImage(content)) {
|
|
1058
|
+
await this.assertImageCapable(activation.handle.agent, options.signal);
|
|
1059
|
+
if (activation.disposal !== void 0) {
|
|
1060
|
+
await Promise.allSettled([activation.disposal]);
|
|
1061
|
+
return;
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1078
1064
|
return this.submitAdmitted(activation, content, options.source, parent, options.signal);
|
|
1079
1065
|
});
|
|
1080
1066
|
/* v8 ignore start -- only the lost-cutoff arm above returns undefined, so only that
|
|
@@ -1414,6 +1400,10 @@ var SubagentContinuationManager = class {
|
|
|
1414
1400
|
*/
|
|
1415
1401
|
async submitMaterialized(activation, content, source, parent, signal) {
|
|
1416
1402
|
try {
|
|
1403
|
+
if (contentHasImage(content)) {
|
|
1404
|
+
await this.assertImageCapable(activation.handle.agent, signal);
|
|
1405
|
+
if (activation.disposal !== void 0) throw new SubagentError(`subagent "${activation.childId}" is closing`, "ACTIVATION_CLOSING");
|
|
1406
|
+
}
|
|
1417
1407
|
return this.submitAdmitted(activation, content, source, parent, signal);
|
|
1418
1408
|
} catch (error) {
|
|
1419
1409
|
/* v8 ignore next -- rollback disposal failures must not mask the
|
|
@@ -1423,6 +1413,28 @@ var SubagentContinuationManager = class {
|
|
|
1423
1413
|
}
|
|
1424
1414
|
}
|
|
1425
1415
|
/**
|
|
1416
|
+
* Refuse image content addressed to a child whose model accepts text only.
|
|
1417
|
+
* Callers guard with `contentHasImage`, so text-only delivery never awaits.
|
|
1418
|
+
* The check runs inside the per-child delivery lock, before the message
|
|
1419
|
+
* exists, so a rejection leaves no partial user message. When the child's
|
|
1420
|
+
* route is not fixed by its options (a request-waterfall listener owns it)
|
|
1421
|
+
* or no LLM registry is composed, delivery proceeds and the LLM layer's
|
|
1422
|
+
* text-only projection replaces each image with its stable placeholder.
|
|
1423
|
+
* @param agent - the live or freshly materialized child agent.
|
|
1424
|
+
* @param signal - caller cancellation bounding the model-info read.
|
|
1425
|
+
* @throws {SubagentError} `MODEL_DOES_NOT_SUPPORT_IMAGES` when the child's resolved model declines image input.
|
|
1426
|
+
*/
|
|
1427
|
+
async assertImageCapable(agent, signal) {
|
|
1428
|
+
const { provider, model } = agent.options;
|
|
1429
|
+
if (provider === void 0 || model === void 0) return;
|
|
1430
|
+
const llm = this.ctx.get("llm");
|
|
1431
|
+
/* v8 ignore next -- a deployment without the LLM registry serves no model
|
|
1432
|
+
* to refuse against; delivery then defers to the text-only projection. */
|
|
1433
|
+
if (llm === void 0) return;
|
|
1434
|
+
const info = await llm.resolveModelInfo(provider, model, signal);
|
|
1435
|
+
if (info.inputModalities !== void 0 && !info.inputModalities.includes("image")) throw new SubagentError(`Model "${model}" does not support image input.`, "MODEL_DOES_NOT_SUPPORT_IMAGES");
|
|
1436
|
+
}
|
|
1437
|
+
/**
|
|
1426
1438
|
* Create or resume the child Agent through the private activation-owner
|
|
1427
1439
|
* scope, install the handle in a fresh Activation, and register ownership on
|
|
1428
1440
|
* a continuation-managed parent. Rejection leaves no Activation, no handle,
|
|
@@ -2971,10 +2983,12 @@ let SubagentRuntime = (() => {
|
|
|
2971
2983
|
* validated browser zone on the accepted message. Success identifies the
|
|
2972
2984
|
* message the child's FIFO inbox accepted; later execution is independent of
|
|
2973
2985
|
* this call.
|
|
2986
|
+
* Image parts are admitted and persisted through the attachment store
|
|
2987
|
+
* before delivery, and the child's model must accept image input.
|
|
2974
2988
|
* @param request - durable address, minted identity, content, and optional browser zone.
|
|
2975
2989
|
* @param signal - carrier cancellation, owning the call until inbox acceptance.
|
|
2976
2990
|
* @returns the accepted message's inbox identity.
|
|
2977
|
-
* @throws {RemoteError} `gateway/bad-request`, `subagent/attachment-
|
|
2991
|
+
* @throws {RemoteError} `gateway/bad-request`, `subagent/attachment-invalid`,
|
|
2978
2992
|
* `subagent/invalid-time-zone`, `subagent/parent-unavailable`,
|
|
2979
2993
|
* `subagent/not-resumable`, `subagent/unauthorized`,
|
|
2980
2994
|
* `subagent/delivery-unavailable`, `gateway/cancelled`, or `gateway/internal`.
|
|
@@ -2982,7 +2996,6 @@ let SubagentRuntime = (() => {
|
|
|
2982
2996
|
async prompt(request, signal) {
|
|
2983
2997
|
const { parentSessionId, childSessionId, clientTimeZone } = request;
|
|
2984
2998
|
validateControlRequest("subagent.prompt", request);
|
|
2985
|
-
const content = admitPromptContent(childSessionId, request.content);
|
|
2986
2999
|
const canonicalTimeZone = clientTimeZone === void 0 ? void 0 : canonicalClientTimeZone(clientTimeZone);
|
|
2987
3000
|
if (clientTimeZone !== void 0 && canonicalTimeZone === void 0) throw new RemoteError("subagent/invalid-time-zone", "clientTimeZone must be UTC or a valid IANA Area/Location name", { value: clientTimeZone });
|
|
2988
3001
|
const parent = this.ctx.get("agents")?.get(parentSessionId);
|
|
@@ -2993,6 +3006,16 @@ let SubagentRuntime = (() => {
|
|
|
2993
3006
|
...canonicalTimeZone === void 0 ? {} : { clientTimeZone: canonicalTimeZone }
|
|
2994
3007
|
};
|
|
2995
3008
|
try {
|
|
3009
|
+
let content;
|
|
3010
|
+
if (request.content.every((part) => part.type === "text")) content = request.content.map((part) => ({
|
|
3011
|
+
type: "text",
|
|
3012
|
+
text: part.text
|
|
3013
|
+
}));
|
|
3014
|
+
else {
|
|
3015
|
+
const attachments = this.ctx.get("attachments");
|
|
3016
|
+
if (attachments === void 0) throw new Error("subagent image prompt requires an attachment store");
|
|
3017
|
+
content = await admitPromptContent(attachments, request.content);
|
|
3018
|
+
}
|
|
2996
3019
|
return { messageId: await this.followup(parent, childSessionId, content, {
|
|
2997
3020
|
source,
|
|
2998
3021
|
signal
|
package/lib/typert.host.js
CHANGED
|
@@ -1,37 +1,6 @@
|
|
|
1
1
|
/* Generated by @deepseek-ai/dsh-typert-generator from FaceModel — do not edit. */
|
|
2
2
|
import { z } from 'zod'
|
|
3
3
|
|
|
4
|
-
const ToolResultBlockRemoteCodec$schema = z.object({
|
|
5
|
-
'type': z.literal("tool-result"),
|
|
6
|
-
'toolCallId': z.intersection(z.string(), z.unknown()),
|
|
7
|
-
'content': z.array(z.union([z.object({
|
|
8
|
-
'type': z.literal("text"),
|
|
9
|
-
'text': z.string(),
|
|
10
|
-
}), z.object({
|
|
11
|
-
'type': z.literal("image"),
|
|
12
|
-
'attachment': z.object({
|
|
13
|
-
'attachmentId': z.intersection(z.string(), z.unknown()),
|
|
14
|
-
'mediaType': z.union([z.literal("image/png"), z.literal("image/jpeg"), z.literal("image/webp"), z.literal("image/gif")]),
|
|
15
|
-
'bytes': z.number(),
|
|
16
|
-
'width': z.number(),
|
|
17
|
-
'height': z.number(),
|
|
18
|
-
'name': z.string().optional(),
|
|
19
|
-
'originalDimensions': z.object({
|
|
20
|
-
'width': z.number(),
|
|
21
|
-
'height': z.number(),
|
|
22
|
-
}).optional(),
|
|
23
|
-
}),
|
|
24
|
-
}), z.object({
|
|
25
|
-
'type': z.literal("reasoning"),
|
|
26
|
-
'text': z.string(),
|
|
27
|
-
}), z.object({
|
|
28
|
-
'type': z.literal("tool-call"),
|
|
29
|
-
'id': z.intersection(z.string(), z.unknown()),
|
|
30
|
-
'name': z.string(),
|
|
31
|
-
'arguments': z.string(),
|
|
32
|
-
}), z.lazy(() => ToolResultBlockRemoteCodec$schema)])),
|
|
33
|
-
'isError': z.boolean().optional(),
|
|
34
|
-
})
|
|
35
4
|
const _deepseek_ai_dsh_subagent_subagents_interruptByParent_parameter_0$schema = z.intersection(z.string(), z.unknown())
|
|
36
5
|
const _deepseek_ai_dsh_subagent_subagents_interruptByParent_parameter_1$schema = z.intersection(z.string(), z.unknown())
|
|
37
6
|
const _deepseek_ai_dsh_subagent_subagents_interruptByParent_parameter_2$schema = z.literal("continuable")
|
|
@@ -69,65 +38,13 @@ const _deepseek_ai_dsh_subagent_subagents_prompt_parameter_0$schema = z.object({
|
|
|
69
38
|
'childSessionId': z.intersection(z.string(), z.unknown()).readonly(),
|
|
70
39
|
'mode': z.literal("continuable").readonly(),
|
|
71
40
|
'content': z.array(z.union([z.object({
|
|
72
|
-
'type': z.literal("text"),
|
|
73
|
-
'text': z.string(),
|
|
74
|
-
}), z.object({
|
|
75
|
-
'type': z.literal("image"),
|
|
76
|
-
'attachment': z.object({
|
|
77
|
-
'attachmentId': z.intersection(z.string(), z.unknown()),
|
|
78
|
-
'mediaType': z.union([z.literal("image/png"), z.literal("image/jpeg"), z.literal("image/webp"), z.literal("image/gif")]),
|
|
79
|
-
'bytes': z.number(),
|
|
80
|
-
'width': z.number(),
|
|
81
|
-
'height': z.number(),
|
|
82
|
-
'name': z.string().optional(),
|
|
83
|
-
'originalDimensions': z.object({
|
|
84
|
-
'width': z.number(),
|
|
85
|
-
'height': z.number(),
|
|
86
|
-
}).optional(),
|
|
87
|
-
}),
|
|
88
|
-
}), z.object({
|
|
89
|
-
'type': z.literal("reasoning"),
|
|
90
|
-
'text': z.string(),
|
|
91
|
-
}), z.object({
|
|
92
|
-
'type': z.literal("tool-call"),
|
|
93
|
-
'id': z.intersection(z.string(), z.unknown()),
|
|
94
|
-
'name': z.string(),
|
|
95
|
-
'arguments': z.string(),
|
|
96
|
-
}), z.object({
|
|
97
|
-
'type': z.literal("tool-result"),
|
|
98
|
-
'toolCallId': z.intersection(z.string(), z.unknown()),
|
|
99
|
-
'content': z.array(z.union([z.object({
|
|
100
|
-
'type': z.literal("text"),
|
|
101
|
-
'text': z.string(),
|
|
102
|
-
}), z.object({
|
|
103
|
-
'type': z.literal("image"),
|
|
104
|
-
'attachment': z.object({
|
|
105
|
-
'attachmentId': z.intersection(z.string(), z.unknown()),
|
|
106
|
-
'mediaType': z.union([z.literal("image/png"), z.literal("image/jpeg"), z.literal("image/webp"), z.literal("image/gif")]),
|
|
107
|
-
'bytes': z.number(),
|
|
108
|
-
'width': z.number(),
|
|
109
|
-
'height': z.number(),
|
|
110
|
-
'name': z.string().optional(),
|
|
111
|
-
'originalDimensions': z.object({
|
|
112
|
-
'width': z.number(),
|
|
113
|
-
'height': z.number(),
|
|
114
|
-
}).optional(),
|
|
115
|
-
}),
|
|
116
|
-
}), z.object({
|
|
117
|
-
'type': z.literal("reasoning"),
|
|
118
|
-
'text': z.string(),
|
|
119
|
-
}), z.object({
|
|
120
|
-
'type': z.literal("tool-call"),
|
|
121
|
-
'id': z.intersection(z.string(), z.unknown()),
|
|
122
|
-
'name': z.string(),
|
|
123
|
-
'arguments': z.string(),
|
|
124
|
-
}), z.lazy(() => ToolResultBlockRemoteCodec$schema)])),
|
|
125
|
-
'isError': z.boolean().optional(),
|
|
41
|
+
'type': z.literal("text").readonly(),
|
|
42
|
+
'text': z.string().readonly(),
|
|
126
43
|
}), z.object({
|
|
127
44
|
'type': z.literal("image").readonly(),
|
|
128
|
-
'mediaType': z.union([z.literal("image/png"), z.literal("image/jpeg"), z.literal("image/webp"), z.literal("image/gif")]),
|
|
129
|
-
'data': z.string(),
|
|
130
|
-
'name': z.string().optional(),
|
|
45
|
+
'mediaType': z.union([z.literal("image/png"), z.literal("image/jpeg"), z.literal("image/webp"), z.literal("image/gif")]).readonly(),
|
|
46
|
+
'data': z.string().readonly(),
|
|
47
|
+
'name': z.string().readonly().optional(),
|
|
131
48
|
})])).readonly(),
|
|
132
49
|
'clientTimeZone': z.string().readonly().optional(),
|
|
133
50
|
})
|
|
@@ -184,7 +101,7 @@ export const TYPERT = {
|
|
|
184
101
|
typeSymbol: '@deepseek-ai/dsh-subagent/client#SubagentInterruptReceipt',
|
|
185
102
|
schema: _deepseek_ai_dsh_subagent_subagents_interruptByParent_result$schema,
|
|
186
103
|
},
|
|
187
|
-
sourceLocation: {"file":"packages/subagent/subagent/src/index.ts","line":
|
|
104
|
+
sourceLocation: {"file":"packages/subagent/subagent/src/index.ts","line":491,"column":3},
|
|
188
105
|
},
|
|
189
106
|
{
|
|
190
107
|
id: '@deepseek-ai/dsh-subagent#subagents/list',
|
|
@@ -211,7 +128,7 @@ export const TYPERT = {
|
|
|
211
128
|
typeSymbol: '@deepseek-ai/dsh-subagent/client#SubagentCatalog',
|
|
212
129
|
schema: _deepseek_ai_dsh_subagent_subagents_list_result$schema,
|
|
213
130
|
},
|
|
214
|
-
sourceLocation: {"file":"packages/subagent/subagent/src/index.ts","line":
|
|
131
|
+
sourceLocation: {"file":"packages/subagent/subagent/src/index.ts","line":407,"column":9},
|
|
215
132
|
},
|
|
216
133
|
{
|
|
217
134
|
id: '@deepseek-ai/dsh-subagent#subagents/prompt',
|
|
@@ -237,7 +154,7 @@ export const TYPERT = {
|
|
|
237
154
|
typeSymbol: '@deepseek-ai/dsh-subagent/client#SubagentPromptReceipt',
|
|
238
155
|
schema: _deepseek_ai_dsh_subagent_subagents_prompt_result$schema,
|
|
239
156
|
},
|
|
240
|
-
sourceLocation: {"file":"packages/subagent/subagent/src/index.ts","line":
|
|
157
|
+
sourceLocation: {"file":"packages/subagent/subagent/src/index.ts","line":433,"column":9},
|
|
241
158
|
},
|
|
242
159
|
],
|
|
243
160
|
model: {
|
|
@@ -325,7 +242,7 @@ export const TYPERT = {
|
|
|
325
242
|
"name": "prompt",
|
|
326
243
|
"signature": "@Remote('prompt') async prompt(request: SubagentPromptRequest, signal: AbortSignal): Promise<SubagentPromptReceipt>",
|
|
327
244
|
"summary": "Deliver one browser-authored message to a continuable child through the exact live direct parent, retaining the caller-minted request identity and validated browser zone on the accepted message.",
|
|
328
|
-
"jsDoc": "/**\n * Deliver one browser-authored message to a continuable child through the\n * exact live direct parent, retaining the caller-minted request identity and\n * validated browser zone on the accepted message. Success identifies the\n * message the child's FIFO inbox accepted; later execution is independent of\n * this call.\n * @param request - durable address, minted identity, content, and optional browser zone.\n * @param signal - carrier cancellation, owning the call until inbox acceptance.\n * @returns the accepted message's inbox identity.\n * @throws {RemoteError} `gateway/bad-request`, `subagent/attachment-
|
|
245
|
+
"jsDoc": "/**\n * Deliver one browser-authored message to a continuable child through the\n * exact live direct parent, retaining the caller-minted request identity and\n * validated browser zone on the accepted message. Success identifies the\n * message the child's FIFO inbox accepted; later execution is independent of\n * this call.\n * Image parts are admitted and persisted through the attachment store\n * before delivery, and the child's model must accept image input.\n * @param request - durable address, minted identity, content, and optional browser zone.\n * @param signal - carrier cancellation, owning the call until inbox acceptance.\n * @returns the accepted message's inbox identity.\n * @throws {RemoteError} `gateway/bad-request`, `subagent/attachment-invalid`,\n * `subagent/invalid-time-zone`, `subagent/parent-unavailable`,\n * `subagent/not-resumable`, `subagent/unauthorized`,\n * `subagent/delivery-unavailable`, `gateway/cancelled`, or `gateway/internal`.\n */"
|
|
329
246
|
},
|
|
330
247
|
{
|
|
331
248
|
"kind": "method",
|
|
@@ -476,14 +393,6 @@ export const TYPERT = {
|
|
|
476
393
|
"name": "CoordinatorMessageSource",
|
|
477
394
|
"declaration": "export interface CoordinatorMessageSource {\n readonly kind: 'coordinator';\n readonly form: 'relay';\n readonly senderSessionId: SessionId;\n}"
|
|
478
395
|
},
|
|
479
|
-
{
|
|
480
|
-
"name": "EncodedImageAttachment",
|
|
481
|
-
"declaration": "export interface EncodedImageAttachment {\n mediaType: ImageMediaType;\n data: string;\n name?: string;\n}"
|
|
482
|
-
},
|
|
483
|
-
{
|
|
484
|
-
"name": "EncodedImagePromptBlock",
|
|
485
|
-
"declaration": "export interface EncodedImagePromptBlock extends EncodedImageAttachment {\n readonly type: 'image';\n}"
|
|
486
|
-
},
|
|
487
396
|
{
|
|
488
397
|
"name": "EpochHeader",
|
|
489
398
|
"declaration": "export interface EpochHeader {\n config: LlmCallConfig;\n adapterDefaults?: LlmCallConfigAdapterDefaults;\n system?: string;\n tools?: ToolSchema[];\n}"
|
|
@@ -616,6 +525,10 @@ export const TYPERT = {
|
|
|
616
525
|
"name": "OneShotSubagentDescriptorData",
|
|
617
526
|
"declaration": "export interface OneShotSubagentDescriptorData extends SubagentDescriptorBase {\n readonly mode: 'one-shot';\n readonly label?: string;\n}"
|
|
618
527
|
},
|
|
528
|
+
{
|
|
529
|
+
"name": "PromptContentPart",
|
|
530
|
+
"declaration": "export type PromptContentPart = { readonly type: 'text'; readonly text: string; } | { readonly type: 'image'; readonly mediaType: ImageMediaType; readonly data: string; readonly name?: string; };"
|
|
531
|
+
},
|
|
619
532
|
{
|
|
620
533
|
"name": "ProviderRequestId",
|
|
621
534
|
"declaration": "export type ProviderRequestId = Branded<'ProviderRequestId'>;"
|
|
@@ -752,17 +665,13 @@ export const TYPERT = {
|
|
|
752
665
|
"name": "SubagentListEntry",
|
|
753
666
|
"declaration": "export type SubagentListEntry = { readonly kind: 'child'; readonly id: SessionId; readonly activity: 'running' | 'inactive'; readonly hasChildren: boolean; } & ({ readonly mode: 'one-shot'; readonly label?: string; } | { readonly mode: 'continuable'; readonly label: string; }) | { readonly kind: 'diagnostic'; readonly id: SessionId; readonly reason: 'corrupt' | 'unsupported' | 'unavailable'; };"
|
|
754
667
|
},
|
|
755
|
-
{
|
|
756
|
-
"name": "SubagentPromptContentPart",
|
|
757
|
-
"declaration": "export type SubagentPromptContentPart = ContentBlock | EncodedImagePromptBlock;"
|
|
758
|
-
},
|
|
759
668
|
{
|
|
760
669
|
"name": "SubagentPromptReceipt",
|
|
761
670
|
"declaration": "export interface SubagentPromptReceipt {\n readonly messageId: MessageId;\n}"
|
|
762
671
|
},
|
|
763
672
|
{
|
|
764
673
|
"name": "SubagentPromptRequest",
|
|
765
|
-
"declaration": "export interface SubagentPromptRequest {\n readonly requestId: SubagentPromptRequestId;\n readonly parentSessionId: SessionId;\n readonly childSessionId: SessionId;\n readonly mode: 'continuable';\n readonly content: readonly
|
|
674
|
+
"declaration": "export interface SubagentPromptRequest {\n readonly requestId: SubagentPromptRequestId;\n readonly parentSessionId: SessionId;\n readonly childSessionId: SessionId;\n readonly mode: 'continuable';\n readonly content: readonly PromptContentPart[];\n readonly clientTimeZone?: string;\n}"
|
|
766
675
|
},
|
|
767
676
|
{
|
|
768
677
|
"name": "SubagentPromptRequestId",
|
|
@@ -1,37 +1,6 @@
|
|
|
1
1
|
/* Generated by @deepseek-ai/dsh-typert-generator from the Host FaceModel — do not edit. */
|
|
2
2
|
import { z } from 'zod'
|
|
3
3
|
|
|
4
|
-
const ToolResultBlockRemoteCodec$schema = z.object({
|
|
5
|
-
'type': z.literal("tool-result"),
|
|
6
|
-
'toolCallId': z.intersection(z.string(), z.unknown()),
|
|
7
|
-
'content': z.array(z.union([z.object({
|
|
8
|
-
'type': z.literal("text"),
|
|
9
|
-
'text': z.string(),
|
|
10
|
-
}), z.object({
|
|
11
|
-
'type': z.literal("image"),
|
|
12
|
-
'attachment': z.object({
|
|
13
|
-
'attachmentId': z.intersection(z.string(), z.unknown()),
|
|
14
|
-
'mediaType': z.union([z.literal("image/png"), z.literal("image/jpeg"), z.literal("image/webp"), z.literal("image/gif")]),
|
|
15
|
-
'bytes': z.number(),
|
|
16
|
-
'width': z.number(),
|
|
17
|
-
'height': z.number(),
|
|
18
|
-
'name': z.string().optional(),
|
|
19
|
-
'originalDimensions': z.object({
|
|
20
|
-
'width': z.number(),
|
|
21
|
-
'height': z.number(),
|
|
22
|
-
}).optional(),
|
|
23
|
-
}),
|
|
24
|
-
}), z.object({
|
|
25
|
-
'type': z.literal("reasoning"),
|
|
26
|
-
'text': z.string(),
|
|
27
|
-
}), z.object({
|
|
28
|
-
'type': z.literal("tool-call"),
|
|
29
|
-
'id': z.intersection(z.string(), z.unknown()),
|
|
30
|
-
'name': z.string(),
|
|
31
|
-
'arguments': z.string(),
|
|
32
|
-
}), z.lazy(() => ToolResultBlockRemoteCodec$schema)])),
|
|
33
|
-
'isError': z.boolean().optional(),
|
|
34
|
-
})
|
|
35
4
|
const _deepseek_ai_dsh_subagent_subagents_interruptByParent_parameter_0$schema = z.intersection(z.string(), z.unknown())
|
|
36
5
|
const _deepseek_ai_dsh_subagent_subagents_interruptByParent_parameter_1$schema = z.intersection(z.string(), z.unknown())
|
|
37
6
|
const _deepseek_ai_dsh_subagent_subagents_interruptByParent_parameter_2$schema = z.literal("continuable")
|
|
@@ -69,65 +38,13 @@ const _deepseek_ai_dsh_subagent_subagents_prompt_parameter_0$schema = z.object({
|
|
|
69
38
|
'childSessionId': z.intersection(z.string(), z.unknown()).readonly(),
|
|
70
39
|
'mode': z.literal("continuable").readonly(),
|
|
71
40
|
'content': z.array(z.union([z.object({
|
|
72
|
-
'type': z.literal("text"),
|
|
73
|
-
'text': z.string(),
|
|
74
|
-
}), z.object({
|
|
75
|
-
'type': z.literal("image"),
|
|
76
|
-
'attachment': z.object({
|
|
77
|
-
'attachmentId': z.intersection(z.string(), z.unknown()),
|
|
78
|
-
'mediaType': z.union([z.literal("image/png"), z.literal("image/jpeg"), z.literal("image/webp"), z.literal("image/gif")]),
|
|
79
|
-
'bytes': z.number(),
|
|
80
|
-
'width': z.number(),
|
|
81
|
-
'height': z.number(),
|
|
82
|
-
'name': z.string().optional(),
|
|
83
|
-
'originalDimensions': z.object({
|
|
84
|
-
'width': z.number(),
|
|
85
|
-
'height': z.number(),
|
|
86
|
-
}).optional(),
|
|
87
|
-
}),
|
|
88
|
-
}), z.object({
|
|
89
|
-
'type': z.literal("reasoning"),
|
|
90
|
-
'text': z.string(),
|
|
91
|
-
}), z.object({
|
|
92
|
-
'type': z.literal("tool-call"),
|
|
93
|
-
'id': z.intersection(z.string(), z.unknown()),
|
|
94
|
-
'name': z.string(),
|
|
95
|
-
'arguments': z.string(),
|
|
96
|
-
}), z.object({
|
|
97
|
-
'type': z.literal("tool-result"),
|
|
98
|
-
'toolCallId': z.intersection(z.string(), z.unknown()),
|
|
99
|
-
'content': z.array(z.union([z.object({
|
|
100
|
-
'type': z.literal("text"),
|
|
101
|
-
'text': z.string(),
|
|
102
|
-
}), z.object({
|
|
103
|
-
'type': z.literal("image"),
|
|
104
|
-
'attachment': z.object({
|
|
105
|
-
'attachmentId': z.intersection(z.string(), z.unknown()),
|
|
106
|
-
'mediaType': z.union([z.literal("image/png"), z.literal("image/jpeg"), z.literal("image/webp"), z.literal("image/gif")]),
|
|
107
|
-
'bytes': z.number(),
|
|
108
|
-
'width': z.number(),
|
|
109
|
-
'height': z.number(),
|
|
110
|
-
'name': z.string().optional(),
|
|
111
|
-
'originalDimensions': z.object({
|
|
112
|
-
'width': z.number(),
|
|
113
|
-
'height': z.number(),
|
|
114
|
-
}).optional(),
|
|
115
|
-
}),
|
|
116
|
-
}), z.object({
|
|
117
|
-
'type': z.literal("reasoning"),
|
|
118
|
-
'text': z.string(),
|
|
119
|
-
}), z.object({
|
|
120
|
-
'type': z.literal("tool-call"),
|
|
121
|
-
'id': z.intersection(z.string(), z.unknown()),
|
|
122
|
-
'name': z.string(),
|
|
123
|
-
'arguments': z.string(),
|
|
124
|
-
}), z.lazy(() => ToolResultBlockRemoteCodec$schema)])),
|
|
125
|
-
'isError': z.boolean().optional(),
|
|
41
|
+
'type': z.literal("text").readonly(),
|
|
42
|
+
'text': z.string().readonly(),
|
|
126
43
|
}), z.object({
|
|
127
44
|
'type': z.literal("image").readonly(),
|
|
128
|
-
'mediaType': z.union([z.literal("image/png"), z.literal("image/jpeg"), z.literal("image/webp"), z.literal("image/gif")]),
|
|
129
|
-
'data': z.string(),
|
|
130
|
-
'name': z.string().optional(),
|
|
45
|
+
'mediaType': z.union([z.literal("image/png"), z.literal("image/jpeg"), z.literal("image/webp"), z.literal("image/gif")]).readonly(),
|
|
46
|
+
'data': z.string().readonly(),
|
|
47
|
+
'name': z.string().readonly().optional(),
|
|
131
48
|
})])).readonly(),
|
|
132
49
|
'clientTimeZone': z.string().readonly().optional(),
|
|
133
50
|
})
|
|
@@ -181,7 +98,7 @@ export const TYPERT_REMOTE = {
|
|
|
181
98
|
typeSymbol: '@deepseek-ai/dsh-subagent/client#SubagentInterruptReceipt',
|
|
182
99
|
schema: _deepseek_ai_dsh_subagent_subagents_interruptByParent_result$schema,
|
|
183
100
|
},
|
|
184
|
-
sourceLocation: {"file":"packages/subagent/subagent/src/index.ts","line":
|
|
101
|
+
sourceLocation: {"file":"packages/subagent/subagent/src/index.ts","line":491,"column":3},
|
|
185
102
|
},
|
|
186
103
|
{
|
|
187
104
|
id: '@deepseek-ai/dsh-subagent#subagents/list',
|
|
@@ -208,7 +125,7 @@ export const TYPERT_REMOTE = {
|
|
|
208
125
|
typeSymbol: '@deepseek-ai/dsh-subagent/client#SubagentCatalog',
|
|
209
126
|
schema: _deepseek_ai_dsh_subagent_subagents_list_result$schema,
|
|
210
127
|
},
|
|
211
|
-
sourceLocation: {"file":"packages/subagent/subagent/src/index.ts","line":
|
|
128
|
+
sourceLocation: {"file":"packages/subagent/subagent/src/index.ts","line":407,"column":9},
|
|
212
129
|
},
|
|
213
130
|
{
|
|
214
131
|
id: '@deepseek-ai/dsh-subagent#subagents/prompt',
|
|
@@ -234,7 +151,7 @@ export const TYPERT_REMOTE = {
|
|
|
234
151
|
typeSymbol: '@deepseek-ai/dsh-subagent/client#SubagentPromptReceipt',
|
|
235
152
|
schema: _deepseek_ai_dsh_subagent_subagents_prompt_result$schema,
|
|
236
153
|
},
|
|
237
|
-
sourceLocation: {"file":"packages/subagent/subagent/src/index.ts","line":
|
|
154
|
+
sourceLocation: {"file":"packages/subagent/subagent/src/index.ts","line":433,"column":9},
|
|
238
155
|
},
|
|
239
156
|
],
|
|
240
157
|
}
|
|
@@ -340,6 +340,19 @@ export declare class SubagentContinuationManager {
|
|
|
340
340
|
* @returns the accepted inbox message id.
|
|
341
341
|
*/
|
|
342
342
|
private submitMaterialized;
|
|
343
|
+
/**
|
|
344
|
+
* Refuse image content addressed to a child whose model accepts text only.
|
|
345
|
+
* Callers guard with `contentHasImage`, so text-only delivery never awaits.
|
|
346
|
+
* The check runs inside the per-child delivery lock, before the message
|
|
347
|
+
* exists, so a rejection leaves no partial user message. When the child's
|
|
348
|
+
* route is not fixed by its options (a request-waterfall listener owns it)
|
|
349
|
+
* or no LLM registry is composed, delivery proceeds and the LLM layer's
|
|
350
|
+
* text-only projection replaces each image with its stable placeholder.
|
|
351
|
+
* @param agent - the live or freshly materialized child agent.
|
|
352
|
+
* @param signal - caller cancellation bounding the model-info read.
|
|
353
|
+
* @throws {SubagentError} `MODEL_DOES_NOT_SUPPORT_IMAGES` when the child's resolved model declines image input.
|
|
354
|
+
*/
|
|
355
|
+
private assertImageCapable;
|
|
343
356
|
/**
|
|
344
357
|
* Create or resume the child Agent through the private activation-owner
|
|
345
358
|
* scope, install the handle in a fresh Activation, and register ownership on
|
|
@@ -74,7 +74,7 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
|
|
|
74
74
|
});
|
|
75
75
|
import { randomUUID } from 'node:crypto';
|
|
76
76
|
import { brandString } from '@deepseek-ai/dsh-brand';
|
|
77
|
-
import { ReasoningEffortId, boundContextSummary, createUserMessage, errorChain } from '@deepseek-ai/dsh-llm';
|
|
77
|
+
import { ReasoningEffortId, boundContextSummary, contentHasImage, createUserMessage, errorChain } from '@deepseek-ai/dsh-llm';
|
|
78
78
|
import { foldSubagentDescriptor, snapshotSubagentDescriptor } from "./descriptor.js";
|
|
79
79
|
import { appendDelegatedPolicyOverrides, applyChildComposition, captureDelegatedPolicyOverrides, childSessionMeta, resolveChildAgentOptions, resolveChildDepth, } from "./child-agent.js";
|
|
80
80
|
import { assertSubagentMaxDepth } from "./depth.js";
|
|
@@ -297,12 +297,25 @@ export class SubagentContinuationManager {
|
|
|
297
297
|
return this.coldResume(parent, childId, content, options);
|
|
298
298
|
// A delivery that arrives after the disposal transaction began must not
|
|
299
299
|
// reach a handle being torn down; wait for release, then cold-resume.
|
|
300
|
+
const disposal = activation.disposal;
|
|
300
301
|
/* v8 ignore next 3 -- the send-versus-dispose cutoff: reaching this arm needs a
|
|
301
302
|
* delivery to observe the transaction inside the same critical section that opened it,
|
|
302
303
|
* which no test can schedule deterministically. The behavior is covered end-to-end by
|
|
303
304
|
* "cold-resumes a delivery that lost the race with final disposal". */
|
|
304
|
-
if (
|
|
305
|
-
return
|
|
305
|
+
if (disposal !== undefined) {
|
|
306
|
+
return disposal.then(() => undefined, () => undefined);
|
|
307
|
+
}
|
|
308
|
+
// Text-only delivery stays await-free, so the disposal-cutoff check
|
|
309
|
+
// above and the submit share one critical window. The image path
|
|
310
|
+
// awaits a capability read, so it re-checks the cutoff afterwards; a
|
|
311
|
+
// disposal that began during the read is waited out and retried like
|
|
312
|
+
// one observed on entry.
|
|
313
|
+
if (contentHasImage(content)) {
|
|
314
|
+
await this.assertImageCapable(activation.handle.agent, options.signal);
|
|
315
|
+
if (activation.disposal !== undefined) {
|
|
316
|
+
await Promise.allSettled([activation.disposal]);
|
|
317
|
+
return undefined;
|
|
318
|
+
}
|
|
306
319
|
}
|
|
307
320
|
return this.submitAdmitted(activation, content, options.source, parent, options.signal);
|
|
308
321
|
});
|
|
@@ -742,6 +755,15 @@ export class SubagentContinuationManager {
|
|
|
742
755
|
*/
|
|
743
756
|
async submitMaterialized(activation, content, source, parent, signal) {
|
|
744
757
|
try {
|
|
758
|
+
if (contentHasImage(content)) {
|
|
759
|
+
// The capability read awaits with the activation already published, so
|
|
760
|
+
// the disposal cutoff is re-checked before the submit; a drain that
|
|
761
|
+
// began during the read turns into a clean closing rejection.
|
|
762
|
+
await this.assertImageCapable(activation.handle.agent, signal);
|
|
763
|
+
if (activation.disposal !== undefined) {
|
|
764
|
+
throw new SubagentError(`subagent "${activation.childId}" is closing`, 'ACTIVATION_CLOSING');
|
|
765
|
+
}
|
|
766
|
+
}
|
|
745
767
|
return this.submitAdmitted(activation, content, source, parent, signal);
|
|
746
768
|
}
|
|
747
769
|
catch (error) {
|
|
@@ -751,6 +773,32 @@ export class SubagentContinuationManager {
|
|
|
751
773
|
throw error;
|
|
752
774
|
}
|
|
753
775
|
}
|
|
776
|
+
/**
|
|
777
|
+
* Refuse image content addressed to a child whose model accepts text only.
|
|
778
|
+
* Callers guard with `contentHasImage`, so text-only delivery never awaits.
|
|
779
|
+
* The check runs inside the per-child delivery lock, before the message
|
|
780
|
+
* exists, so a rejection leaves no partial user message. When the child's
|
|
781
|
+
* route is not fixed by its options (a request-waterfall listener owns it)
|
|
782
|
+
* or no LLM registry is composed, delivery proceeds and the LLM layer's
|
|
783
|
+
* text-only projection replaces each image with its stable placeholder.
|
|
784
|
+
* @param agent - the live or freshly materialized child agent.
|
|
785
|
+
* @param signal - caller cancellation bounding the model-info read.
|
|
786
|
+
* @throws {SubagentError} `MODEL_DOES_NOT_SUPPORT_IMAGES` when the child's resolved model declines image input.
|
|
787
|
+
*/
|
|
788
|
+
async assertImageCapable(agent, signal) {
|
|
789
|
+
const { provider, model } = agent.options;
|
|
790
|
+
if (provider === undefined || model === undefined)
|
|
791
|
+
return;
|
|
792
|
+
const llm = this.ctx.get('llm');
|
|
793
|
+
/* v8 ignore next -- a deployment without the LLM registry serves no model
|
|
794
|
+
* to refuse against; delivery then defers to the text-only projection. */
|
|
795
|
+
if (llm === undefined)
|
|
796
|
+
return;
|
|
797
|
+
const info = await llm.resolveModelInfo(provider, model, signal);
|
|
798
|
+
if (info.inputModalities !== undefined && !info.inputModalities.includes('image')) {
|
|
799
|
+
throw new SubagentError(`Model "${model}" does not support image input.`, 'MODEL_DOES_NOT_SUPPORT_IMAGES');
|
|
800
|
+
}
|
|
801
|
+
}
|
|
754
802
|
/**
|
|
755
803
|
* Create or resume the child Agent through the private activation-owner
|
|
756
804
|
* scope, install the handle in a fresh Activation, and register ownership on
|
|
@@ -5,10 +5,9 @@
|
|
|
5
5
|
*
|
|
6
6
|
* @module @deepseek-ai/dsh-subagent/control-types
|
|
7
7
|
*/
|
|
8
|
-
import type {
|
|
8
|
+
import type { PromptContentPart } from '@deepseek-ai/dsh-attachment/types';
|
|
9
9
|
import type { Branded } from '@deepseek-ai/dsh-brand';
|
|
10
10
|
import type { MessageId } from '@deepseek-ai/dsh-llm/brand';
|
|
11
|
-
import type { ContentBlock } from '@deepseek-ai/dsh-llm/types';
|
|
12
11
|
import type { SessionId } from '@deepseek-ai/dsh-session/types';
|
|
13
12
|
/**
|
|
14
13
|
* Client-minted identity of one browser prompt, persisted on the exact accepted
|
|
@@ -83,19 +82,6 @@ export type SubagentAddress = {
|
|
|
83
82
|
} | {
|
|
84
83
|
readonly mode: 'continuable';
|
|
85
84
|
});
|
|
86
|
-
/**
|
|
87
|
-
* One browser-encoded upload as the Session prompt wire carries it: the shared
|
|
88
|
-
* attachment vocabulary under the content-block tag.
|
|
89
|
-
*/
|
|
90
|
-
export interface EncodedImagePromptBlock extends EncodedImageAttachment {
|
|
91
|
-
readonly type: 'image';
|
|
92
|
-
}
|
|
93
|
-
/**
|
|
94
|
-
* One block a browser prompt may carry. The encoded upload is accepted by the
|
|
95
|
-
* wire and refused by the Host, so the Client narrows nothing: a caller that
|
|
96
|
-
* attaches an image is answered, not silently stripped.
|
|
97
|
-
*/
|
|
98
|
-
export type SubagentPromptContentPart = ContentBlock | EncodedImagePromptBlock;
|
|
99
85
|
/** One human message addressed to a continuable direct child. */
|
|
100
86
|
export interface SubagentPromptRequest {
|
|
101
87
|
/** Identity persisted on the accepted message, minted before the call. */
|
|
@@ -104,8 +90,12 @@ export interface SubagentPromptRequest {
|
|
|
104
90
|
readonly childSessionId: SessionId;
|
|
105
91
|
/** Required discriminator retained from the browser control address. */
|
|
106
92
|
readonly mode: 'continuable';
|
|
107
|
-
/**
|
|
108
|
-
|
|
93
|
+
/**
|
|
94
|
+
* Browser prompt parts delivered as the child's user message. The Host
|
|
95
|
+
* admits and persists image parts before delivery, so the wire never
|
|
96
|
+
* carries a durable attachment reference the caller could fabricate.
|
|
97
|
+
*/
|
|
98
|
+
readonly content: readonly PromptContentPart[];
|
|
109
99
|
/** Optional browser zone sampled for this exact human prompt. */
|
|
110
100
|
readonly clientTimeZone?: string;
|
|
111
101
|
}
|
|
@@ -118,10 +108,8 @@ export interface SubagentInterruptReceipt {
|
|
|
118
108
|
readonly accepted: true;
|
|
119
109
|
}
|
|
120
110
|
/**
|
|
121
|
-
* Failure details the control surface answers with.
|
|
122
|
-
*
|
|
123
|
-
* `subagent/not-resumable` and `subagent/delivery-unavailable` for a one-shot
|
|
124
|
-
* address it refuses before the call, so both planes read one vocabulary.
|
|
111
|
+
* Failure details the control surface answers with. Catalog reads, prompts,
|
|
112
|
+
* and interrupts share this vocabulary with the Client Remote result.
|
|
125
113
|
*/
|
|
126
114
|
declare module '@deepseek-ai/dsh-typert-protocol' {
|
|
127
115
|
interface RemoteErrorDetailsMap {
|
|
@@ -141,12 +129,8 @@ declare module '@deepseek-ai/dsh-typert-protocol' {
|
|
|
141
129
|
'subagent/unauthorized': {
|
|
142
130
|
readonly childSessionId: SessionId;
|
|
143
131
|
};
|
|
144
|
-
/**
|
|
145
|
-
|
|
146
|
-
* for the caller's copy, as the Session prompt's attachment refusals do.
|
|
147
|
-
*/
|
|
148
|
-
'subagent/attachment-unsupported': {
|
|
149
|
-
readonly childSessionId: SessionId;
|
|
132
|
+
/** Image admission or model image-capability refusal. */
|
|
133
|
+
'subagent/attachment-invalid': {
|
|
150
134
|
readonly reason: string;
|
|
151
135
|
};
|
|
152
136
|
/** The child exists but its inbox cannot admit the message now. */
|
package/lib/types/control.d.ts
CHANGED
|
@@ -6,10 +6,9 @@
|
|
|
6
6
|
* @module @deepseek-ai/dsh-subagent
|
|
7
7
|
*/
|
|
8
8
|
import type { Context } from '@deepseek-ai/cordis';
|
|
9
|
-
import type { ContentBlock } from '@deepseek-ai/dsh-llm';
|
|
10
9
|
import type { SessionId } from '@deepseek-ai/dsh-session';
|
|
11
10
|
import { z } from 'zod';
|
|
12
|
-
import type { SubagentCatalog, SubagentListEntry
|
|
11
|
+
import type { SubagentCatalog, SubagentListEntry } from './control-types.ts';
|
|
13
12
|
declare const CONTROL_ID_SCHEMAS: {
|
|
14
13
|
readonly 'subagent.list': z.ZodObject<{
|
|
15
14
|
parentSessionId: z.ZodString;
|
|
@@ -33,21 +32,6 @@ declare const CONTROL_ID_SCHEMAS: {
|
|
|
33
32
|
* @throws {RemoteError} `gateway/bad-request` with the original Zod issues.
|
|
34
33
|
*/
|
|
35
34
|
export declare function validateControlRequest(method: keyof typeof CONTROL_ID_SCHEMAS, payload: unknown): void;
|
|
36
|
-
/**
|
|
37
|
-
* Admit the content one continuation may deliver, refusing every image.
|
|
38
|
-
*
|
|
39
|
-
* The blocks become the child's user message verbatim, and this surface admits
|
|
40
|
-
* no attachment: nothing here registers encoded bytes with the attachment
|
|
41
|
-
* service, so an image would reach the child as a reference nothing resolves.
|
|
42
|
-
* The wire accepts the encoded upload so this refusal — not a Client that
|
|
43
|
-
* strips the block — is what the caller is answered with. Other block types
|
|
44
|
-
* still cross unnarrowed.
|
|
45
|
-
* @param childSessionId - the addressed child, named by the refusal.
|
|
46
|
-
* @param content - blocks the caller asked to deliver.
|
|
47
|
-
* @returns the admitted blocks, in order, as the durable content vocabulary.
|
|
48
|
-
* @throws {RemoteError} `subagent/attachment-unsupported` when any block is an image.
|
|
49
|
-
*/
|
|
50
|
-
export declare function admitPromptContent(childSessionId: SessionId, content: readonly SubagentPromptContentPart[]): ContentBlock[];
|
|
51
35
|
/**
|
|
52
36
|
* Project one durable listing onto the catalog view, replacing each row's
|
|
53
37
|
* store-derived activity with the live Agent driver's status and reporting
|
package/lib/types/control.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* @module @deepseek-ai/dsh-subagent
|
|
7
7
|
*/
|
|
8
|
+
import { AttachmentError } from '@deepseek-ai/dsh-attachment';
|
|
8
9
|
import { RemoteError } from '@deepseek-ai/dsh-typert-protocol';
|
|
9
10
|
import { z } from 'zod';
|
|
10
11
|
import { SubagentError } from "./error.js";
|
|
@@ -35,30 +36,6 @@ export function validateControlRequest(method, payload) {
|
|
|
35
36
|
throw new RemoteError('gateway/bad-request', `invalid payload for ${method}`, { issues: parsed.error.issues });
|
|
36
37
|
}
|
|
37
38
|
}
|
|
38
|
-
/**
|
|
39
|
-
* Admit the content one continuation may deliver, refusing every image.
|
|
40
|
-
*
|
|
41
|
-
* The blocks become the child's user message verbatim, and this surface admits
|
|
42
|
-
* no attachment: nothing here registers encoded bytes with the attachment
|
|
43
|
-
* service, so an image would reach the child as a reference nothing resolves.
|
|
44
|
-
* The wire accepts the encoded upload so this refusal — not a Client that
|
|
45
|
-
* strips the block — is what the caller is answered with. Other block types
|
|
46
|
-
* still cross unnarrowed.
|
|
47
|
-
* @param childSessionId - the addressed child, named by the refusal.
|
|
48
|
-
* @param content - blocks the caller asked to deliver.
|
|
49
|
-
* @returns the admitted blocks, in order, as the durable content vocabulary.
|
|
50
|
-
* @throws {RemoteError} `subagent/attachment-unsupported` when any block is an image.
|
|
51
|
-
*/
|
|
52
|
-
export function admitPromptContent(childSessionId, content) {
|
|
53
|
-
const admitted = [];
|
|
54
|
-
for (const block of content) {
|
|
55
|
-
if (block.type === 'image') {
|
|
56
|
-
throw new RemoteError('subagent/attachment-unsupported', 'subagent continuation does not accept images', { childSessionId, reason: 'SUBAGENT_IMAGE_UNSUPPORTED' });
|
|
57
|
-
}
|
|
58
|
-
admitted.push(block);
|
|
59
|
-
}
|
|
60
|
-
return admitted;
|
|
61
|
-
}
|
|
62
39
|
/**
|
|
63
40
|
* Project one durable listing onto the catalog view, replacing each row's
|
|
64
41
|
* store-derived activity with the live Agent driver's status and reporting
|
|
@@ -109,8 +86,13 @@ export function rejectPrompt(error, childSessionId, signal) {
|
|
|
109
86
|
if (isCancellation(error, signal)) {
|
|
110
87
|
throw new RemoteError('gateway/cancelled', 'subagent prompt was cancelled', {}, { cause: error });
|
|
111
88
|
}
|
|
89
|
+
if (error instanceof AttachmentError) {
|
|
90
|
+
throw new RemoteError('subagent/attachment-invalid', error.message, { reason: error.code }, { cause: error });
|
|
91
|
+
}
|
|
112
92
|
if (error instanceof SubagentError) {
|
|
113
93
|
switch (error.code) {
|
|
94
|
+
case 'MODEL_DOES_NOT_SUPPORT_IMAGES':
|
|
95
|
+
throw new RemoteError('subagent/attachment-invalid', error.message, { reason: error.code }, { cause: error });
|
|
114
96
|
case 'NOT_RESUMABLE':
|
|
115
97
|
throw new RemoteError('subagent/not-resumable', 'subagent cannot be resumed', { childSessionId }, { cause: error });
|
|
116
98
|
case 'UNAUTHORIZED':
|
package/lib/types/index.d.ts
CHANGED
|
@@ -248,10 +248,12 @@ export declare class SubagentRuntime extends TypertRemoteService {
|
|
|
248
248
|
* validated browser zone on the accepted message. Success identifies the
|
|
249
249
|
* message the child's FIFO inbox accepted; later execution is independent of
|
|
250
250
|
* this call.
|
|
251
|
+
* Image parts are admitted and persisted through the attachment store
|
|
252
|
+
* before delivery, and the child's model must accept image input.
|
|
251
253
|
* @param request - durable address, minted identity, content, and optional browser zone.
|
|
252
254
|
* @param signal - carrier cancellation, owning the call until inbox acceptance.
|
|
253
255
|
* @returns the accepted message's inbox identity.
|
|
254
|
-
* @throws {RemoteError} `gateway/bad-request`, `subagent/attachment-
|
|
256
|
+
* @throws {RemoteError} `gateway/bad-request`, `subagent/attachment-invalid`,
|
|
255
257
|
* `subagent/invalid-time-zone`, `subagent/parent-unavailable`,
|
|
256
258
|
* `subagent/not-resumable`, `subagent/unauthorized`,
|
|
257
259
|
* `subagent/delivery-unavailable`, `gateway/cancelled`, or `gateway/internal`.
|
package/lib/types/index.js
CHANGED
|
@@ -62,11 +62,12 @@ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn,
|
|
|
62
62
|
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
63
63
|
done = true;
|
|
64
64
|
};
|
|
65
|
+
import { admitPromptContent } from '@deepseek-ai/dsh-attachment';
|
|
65
66
|
import { scopeTarget } from '@deepseek-ai/dsh-scope';
|
|
66
67
|
import { assertObjectJsonSchema } from '@deepseek-ai/dsh-tools';
|
|
67
68
|
import { canonicalClientTimeZone } from '@deepseek-ai/dsh-util-time';
|
|
68
69
|
import { Remote, RemoteError, TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
|
|
69
|
-
import {
|
|
70
|
+
import { catalogView, rejectCatalogRead, rejectPrompt, validateControlRequest, } from "./control.js";
|
|
70
71
|
import { SubagentError } from "./error.js";
|
|
71
72
|
import { assertSubagentMaxDepth } from "./depth.js";
|
|
72
73
|
import { createActivationObserver, createLifecycleEmitter, observeRun } from "./lifecycle.js";
|
|
@@ -305,10 +306,12 @@ let SubagentRuntime = (() => {
|
|
|
305
306
|
* validated browser zone on the accepted message. Success identifies the
|
|
306
307
|
* message the child's FIFO inbox accepted; later execution is independent of
|
|
307
308
|
* this call.
|
|
309
|
+
* Image parts are admitted and persisted through the attachment store
|
|
310
|
+
* before delivery, and the child's model must accept image input.
|
|
308
311
|
* @param request - durable address, minted identity, content, and optional browser zone.
|
|
309
312
|
* @param signal - carrier cancellation, owning the call until inbox acceptance.
|
|
310
313
|
* @returns the accepted message's inbox identity.
|
|
311
|
-
* @throws {RemoteError} `gateway/bad-request`, `subagent/attachment-
|
|
314
|
+
* @throws {RemoteError} `gateway/bad-request`, `subagent/attachment-invalid`,
|
|
312
315
|
* `subagent/invalid-time-zone`, `subagent/parent-unavailable`,
|
|
313
316
|
* `subagent/not-resumable`, `subagent/unauthorized`,
|
|
314
317
|
* `subagent/delivery-unavailable`, `gateway/cancelled`, or `gateway/internal`.
|
|
@@ -316,7 +319,6 @@ let SubagentRuntime = (() => {
|
|
|
316
319
|
async prompt(request, signal) {
|
|
317
320
|
const { parentSessionId, childSessionId, clientTimeZone } = request;
|
|
318
321
|
validateControlRequest('subagent.prompt', request);
|
|
319
|
-
const content = admitPromptContent(childSessionId, request.content);
|
|
320
322
|
const canonicalTimeZone = clientTimeZone === undefined
|
|
321
323
|
? undefined
|
|
322
324
|
: canonicalClientTimeZone(clientTimeZone);
|
|
@@ -333,6 +335,18 @@ let SubagentRuntime = (() => {
|
|
|
333
335
|
...(canonicalTimeZone === undefined ? {} : { clientTimeZone: canonicalTimeZone }),
|
|
334
336
|
};
|
|
335
337
|
try {
|
|
338
|
+
// Admission precedes delivery: image parts become durable references
|
|
339
|
+
// here, so the child inbox only ever accepts Host-persisted attachments.
|
|
340
|
+
let content;
|
|
341
|
+
if (request.content.every((part) => part.type === 'text')) {
|
|
342
|
+
content = request.content.map(part => ({ type: 'text', text: part.text }));
|
|
343
|
+
}
|
|
344
|
+
else {
|
|
345
|
+
const attachments = this.ctx.get('attachments');
|
|
346
|
+
if (attachments === undefined)
|
|
347
|
+
throw new Error('subagent image prompt requires an attachment store');
|
|
348
|
+
content = await admitPromptContent(attachments, request.content);
|
|
349
|
+
}
|
|
336
350
|
return { messageId: await this.followup(parent, childSessionId, content, { source, signal }) };
|
|
337
351
|
}
|
|
338
352
|
catch (error) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-ai/dsh-subagent",
|
|
3
3
|
"description": "Abstract subagent seam (ctx.subagents): named-provider registry for delegating to child agents",
|
|
4
|
-
"version": "0.1.2-alpha.
|
|
4
|
+
"version": "0.1.2-alpha.3",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -50,30 +50,30 @@
|
|
|
50
50
|
"license": "MIT",
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"zod": "^4.4.3",
|
|
53
|
-
"@deepseek-ai/dsh-brand": "^0.1.2-alpha.
|
|
54
|
-
"@deepseek-ai/dsh-util-values": "^0.1.2-alpha.
|
|
53
|
+
"@deepseek-ai/dsh-brand": "^0.1.2-alpha.3",
|
|
54
|
+
"@deepseek-ai/dsh-util-values": "^0.1.2-alpha.3"
|
|
55
55
|
},
|
|
56
56
|
"peerDependencies": {
|
|
57
57
|
"@deepseek-ai/cordis": "^4.0.2",
|
|
58
|
-
"@deepseek-ai/dsh-agent": "^0.1.2-alpha.
|
|
59
|
-
"@deepseek-ai/dsh-agent-presets": "^0.1.2-alpha.
|
|
60
|
-
"@deepseek-ai/dsh-attachment": "^0.1.2-alpha.
|
|
61
|
-
"@deepseek-ai/dsh-invariants": "^0.1.2-alpha.
|
|
62
|
-
"@deepseek-ai/dsh-jobs": "^0.1.2-alpha.
|
|
63
|
-
"@deepseek-ai/dsh-llm": "^0.1.2-alpha.
|
|
64
|
-
"@deepseek-ai/dsh-sandbox": "^0.1.2-alpha.
|
|
65
|
-
"@deepseek-ai/dsh-sandbox-policy": "^0.1.2-alpha.
|
|
66
|
-
"@deepseek-ai/dsh-scope": "^0.1.2-alpha.
|
|
67
|
-
"@deepseek-ai/dsh-session": "^0.1.2-alpha.
|
|
68
|
-
"@deepseek-ai/dsh-session-
|
|
69
|
-
"@deepseek-ai/dsh-session-projection-cache": "^0.1.2-alpha.
|
|
70
|
-
"@deepseek-ai/dsh-
|
|
71
|
-
"@deepseek-ai/dsh-
|
|
72
|
-
"@deepseek-ai/dsh-
|
|
73
|
-
"@deepseek-ai/dsh-
|
|
74
|
-
"@deepseek-ai/dsh-session-
|
|
75
|
-
"@deepseek-ai/dsh-
|
|
76
|
-
"@deepseek-ai/dsh-
|
|
58
|
+
"@deepseek-ai/dsh-agent": "^0.1.2-alpha.3",
|
|
59
|
+
"@deepseek-ai/dsh-agent-presets": "^0.1.2-alpha.3",
|
|
60
|
+
"@deepseek-ai/dsh-attachment": "^0.1.2-alpha.3",
|
|
61
|
+
"@deepseek-ai/dsh-invariants": "^0.1.2-alpha.3",
|
|
62
|
+
"@deepseek-ai/dsh-jobs": "^0.1.2-alpha.3",
|
|
63
|
+
"@deepseek-ai/dsh-llm": "^0.1.2-alpha.3",
|
|
64
|
+
"@deepseek-ai/dsh-sandbox": "^0.1.2-alpha.3",
|
|
65
|
+
"@deepseek-ai/dsh-sandbox-policy": "^0.1.2-alpha.3",
|
|
66
|
+
"@deepseek-ai/dsh-scope": "^0.1.2-alpha.3",
|
|
67
|
+
"@deepseek-ai/dsh-session": "^0.1.2-alpha.3",
|
|
68
|
+
"@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.3",
|
|
69
|
+
"@deepseek-ai/dsh-session-projection-cache": "^0.1.2-alpha.3",
|
|
70
|
+
"@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.3",
|
|
71
|
+
"@deepseek-ai/dsh-tools": "^0.1.2-alpha.3",
|
|
72
|
+
"@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.3",
|
|
73
|
+
"@deepseek-ai/dsh-user-approval": "^0.1.2-alpha.3",
|
|
74
|
+
"@deepseek-ai/dsh-session-persistence": "^0.1.2-alpha.3",
|
|
75
|
+
"@deepseek-ai/dsh-util-time": "^0.1.2-alpha.3",
|
|
76
|
+
"@deepseek-ai/dsh-session-query": "^0.1.2-alpha.3"
|
|
77
77
|
},
|
|
78
78
|
"peerDependenciesMeta": {
|
|
79
79
|
"@deepseek-ai/dsh-agent-presets": {
|
|
@@ -106,27 +106,27 @@
|
|
|
106
106
|
},
|
|
107
107
|
"devDependencies": {
|
|
108
108
|
"@deepseek-ai/cordis": "^4.0.2",
|
|
109
|
-
"@deepseek-ai/dsh-agent": "^0.1.2-alpha.
|
|
110
|
-
"@deepseek-ai/dsh-
|
|
111
|
-
"@deepseek-ai/dsh-
|
|
112
|
-
"@deepseek-ai/dsh-
|
|
113
|
-
"@deepseek-ai/dsh-
|
|
114
|
-
"@deepseek-ai/dsh-llm": "^0.1.2-alpha.
|
|
115
|
-
"@deepseek-ai/dsh-sandbox": "^0.1.2-alpha.
|
|
116
|
-
"@deepseek-ai/dsh-sandbox-policy": "^0.1.2-alpha.
|
|
117
|
-
"@deepseek-ai/dsh-scope": "^0.1.2-alpha.
|
|
118
|
-
"@deepseek-ai/dsh-session": "^0.1.2-alpha.
|
|
119
|
-
"@deepseek-ai/dsh-session-
|
|
120
|
-
"@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.
|
|
121
|
-
"@deepseek-ai/dsh-session-
|
|
122
|
-
"@deepseek-ai/dsh-storage
|
|
123
|
-
"@deepseek-ai/dsh-storage-
|
|
124
|
-
"@deepseek-ai/dsh-
|
|
125
|
-
"@deepseek-ai/dsh-
|
|
126
|
-
"@deepseek-ai/dsh-
|
|
127
|
-
"@deepseek-ai/dsh-
|
|
128
|
-
"@deepseek-ai/dsh-
|
|
129
|
-
"@deepseek-ai/dsh-
|
|
130
|
-
"@deepseek-ai/dsh-session-
|
|
109
|
+
"@deepseek-ai/dsh-agent": "^0.1.2-alpha.3",
|
|
110
|
+
"@deepseek-ai/dsh-agent-presets": "^0.1.2-alpha.3",
|
|
111
|
+
"@deepseek-ai/dsh-attachment": "^0.1.2-alpha.3",
|
|
112
|
+
"@deepseek-ai/dsh-jobs": "^0.1.2-alpha.3",
|
|
113
|
+
"@deepseek-ai/dsh-invariants": "^0.1.2-alpha.3",
|
|
114
|
+
"@deepseek-ai/dsh-llm": "^0.1.2-alpha.3",
|
|
115
|
+
"@deepseek-ai/dsh-sandbox": "^0.1.2-alpha.3",
|
|
116
|
+
"@deepseek-ai/dsh-sandbox-policy": "^0.1.2-alpha.3",
|
|
117
|
+
"@deepseek-ai/dsh-scope": "^0.1.2-alpha.3",
|
|
118
|
+
"@deepseek-ai/dsh-session": "^0.1.2-alpha.3",
|
|
119
|
+
"@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.3",
|
|
120
|
+
"@deepseek-ai/dsh-session-projection-cache": "^0.1.2-alpha.3",
|
|
121
|
+
"@deepseek-ai/dsh-session-query": "^0.1.2-alpha.3",
|
|
122
|
+
"@deepseek-ai/dsh-storage": "^0.1.2-alpha.3",
|
|
123
|
+
"@deepseek-ai/dsh-storage-domain": "^0.1.2-alpha.3",
|
|
124
|
+
"@deepseek-ai/dsh-storage-json": "^0.1.2-alpha.3",
|
|
125
|
+
"@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.3",
|
|
126
|
+
"@deepseek-ai/dsh-tools": "^0.1.2-alpha.3",
|
|
127
|
+
"@deepseek-ai/dsh-typert-protocol": "^0.1.2-alpha.3",
|
|
128
|
+
"@deepseek-ai/dsh-user-approval": "^0.1.2-alpha.3",
|
|
129
|
+
"@deepseek-ai/dsh-util-time": "^0.1.2-alpha.3",
|
|
130
|
+
"@deepseek-ai/dsh-session-persistence": "^0.1.2-alpha.3"
|
|
131
131
|
}
|
|
132
132
|
}
|