@khorsheed/dsh-local-agent 0.2.0-rc.1 → 0.2.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/CHANGELOG.md +31 -0
- package/README.en.md +23 -164
- package/README.i18n.yaml +2 -2
- package/README.md +23 -164
- package/lib/client.js +23707 -4768
- package/lib/index.js +827 -62
- package/lib/member-bridge.js +195 -25
- package/lib/tool-factory.js +52 -19
- package/lib/typert.host.js +1440 -353
- package/lib/typert.remote-client.d.ts +43 -3
- package/lib/typert.remote-client.js +973 -335
- package/lib/types/client/FamilyCard.d.ts +37 -0
- package/lib/types/client/FamilyCard.js +55 -0
- package/lib/types/client/HarnessModelPicker.d.ts +21 -1
- package/lib/types/client/HarnessModelPicker.js +31 -3
- package/lib/types/client/MemberLiveNode.js +38 -4
- package/lib/types/client/ModelListSection.d.ts +49 -0
- package/lib/types/client/ModelListSection.js +132 -0
- package/lib/types/client/ProviderAuthBlock.d.ts +10 -2
- package/lib/types/client/ProviderAuthBlock.js +13 -6
- package/lib/types/client/auth-status.d.ts +6 -0
- package/lib/types/client/auth-status.js +8 -0
- package/lib/types/client/harness-cards.d.ts +72 -0
- package/lib/types/client/harness-cards.js +57 -0
- package/lib/types/client/icons.d.ts +2 -1
- package/lib/types/client/icons.js +1 -0
- package/lib/types/client/index.d.ts +32 -7
- package/lib/types/client/index.js +66 -9
- package/lib/types/client/locales.d.ts +25 -1
- package/lib/types/client/locales.js +50 -2
- package/lib/types/client/model-directory-scope.d.ts +14 -0
- package/lib/types/client/model-directory-scope.js +20 -0
- package/lib/types/gateway.d.ts +49 -1
- package/lib/types/gateway.js +89 -0
- package/lib/types/index.d.ts +91 -38
- package/lib/types/index.js +190 -58
- package/lib/types/live-stream.d.ts +11 -0
- package/lib/types/live-stream.js +26 -0
- package/lib/types/member-bridge.d.ts +11 -1
- package/lib/types/member-bridge.js +81 -13
- package/lib/types/member-channel.d.ts +44 -1
- package/lib/types/member-channel.js +65 -2
- package/lib/types/member-controls.d.ts +8 -0
- package/lib/types/member-controls.js +15 -2
- package/lib/types/mode-skills.d.ts +18 -0
- package/lib/types/mode-skills.js +56 -0
- package/lib/types/model-list.d.ts +55 -0
- package/lib/types/model-list.js +101 -0
- package/lib/types/preset-tool-host.d.ts +70 -0
- package/lib/types/preset-tool-host.js +210 -0
- package/lib/types/tool-factory.d.ts +9 -3
- package/lib/types/tool-factory.js +55 -18
- package/lib/types/types.d.ts +62 -5
- package/package.json +27 -23
- package/lib/invariant.js +0 -85
- package/lib/types/invariant.d.ts +0 -16
- package/lib/types/invariant.js +0 -105
package/lib/member-bridge.js
CHANGED
|
@@ -21,15 +21,22 @@ const MEMBER_BRIDGE_TOKEN_ENV = "DSH_MEMBER_TOKEN";
|
|
|
21
21
|
* it bare: `node <this file>`, with the socket path and token in its
|
|
22
22
|
* environment (written into the CLI's scoped MCP config by the provider).
|
|
23
23
|
* Covers the MCP handshake subset a client needs: `initialize`,
|
|
24
|
-
* `notifications/initialized`, `ping`, `tools/list`, `tools/call`.
|
|
24
|
+
* `notifications/initialized`, `ping`, `tools/list`, `tools/call`. A member
|
|
25
|
+
* with a mode also gets that preset's plugin tools: `tools/list` asks the
|
|
26
|
+
* host for them and any other tool name is forwarded as a mode-tool call.
|
|
25
27
|
* @module @khorsheed/dsh-local-agent/member-bridge
|
|
26
28
|
*/
|
|
27
29
|
/** The one tool this server exposes. */
|
|
28
30
|
const MEMBER_MESSAGE_TOOL = "member_message";
|
|
29
31
|
const TOOL_DESCRIPTION = [
|
|
30
|
-
"
|
|
31
|
-
"`to` is the
|
|
32
|
-
"
|
|
32
|
+
"Send a message to another member of this room/session group.",
|
|
33
|
+
"`to` is the member name (or its dsh child session id).",
|
|
34
|
+
"kind \"task\" (default) hands it work: in a room, its final reply is reported back to you automatically, citing the message id — do not poll.",
|
|
35
|
+
"kind \"notify\" is an FYI and is never reported back. Pass inReplyTo (e.g. m12) when answering a message.",
|
|
36
|
+
"Never sleep or poll for the result (room_read included): end your turn, the report arrives on its own.",
|
|
37
|
+
"Writing \"@name\" in a reply reaches nobody; only this tool does.",
|
|
38
|
+
"Optional note: what you will wait for before acting on the result; optional task: \"new\" or a task number from room_read.",
|
|
39
|
+
"The receipt is returned verbatim: sent #m<id> … / pending-confirm … / busy / an error reason."
|
|
33
40
|
].join(" ");
|
|
34
41
|
const TOOL_SCHEMA = {
|
|
35
42
|
type: "object",
|
|
@@ -40,7 +47,24 @@ const TOOL_SCHEMA = {
|
|
|
40
47
|
},
|
|
41
48
|
text: {
|
|
42
49
|
type: "string",
|
|
43
|
-
description: "The
|
|
50
|
+
description: "The message text, delivered with your provenance."
|
|
51
|
+
},
|
|
52
|
+
kind: {
|
|
53
|
+
type: "string",
|
|
54
|
+
enum: ["task", "notify"],
|
|
55
|
+
description: "task (default): the reply is reported back to you; notify: FYI only."
|
|
56
|
+
},
|
|
57
|
+
inReplyTo: {
|
|
58
|
+
type: "string",
|
|
59
|
+
description: "Optional id of the message this answers, e.g. m12."
|
|
60
|
+
},
|
|
61
|
+
note: {
|
|
62
|
+
type: "string",
|
|
63
|
+
description: "Optional: what you will wait for before acting on the result (the room uses it to decide when to hand you the report)."
|
|
64
|
+
},
|
|
65
|
+
task: {
|
|
66
|
+
type: "string",
|
|
67
|
+
description: "Optional: \"new\" or a task number from room_read; omit to stay in the task you are working on."
|
|
44
68
|
}
|
|
45
69
|
},
|
|
46
70
|
required: ["to", "text"],
|
|
@@ -62,7 +86,7 @@ const ROOM_TOOLS = [
|
|
|
62
86
|
},
|
|
63
87
|
{
|
|
64
88
|
name: "room_read",
|
|
65
|
-
description: "Read your room roster, coordinator, deliveries, recent outcomes and available harness providers. Identity comes from the host.",
|
|
89
|
+
description: "Read your room: roster with roles, coordinator, tasks with their numbers, deliveries, recent outcomes and available harness providers. Identity comes from the host. Use it to look around, not to wait for a result.",
|
|
66
90
|
inputSchema: {
|
|
67
91
|
type: "object",
|
|
68
92
|
properties: {},
|
|
@@ -77,27 +101,128 @@ const ROOM_TOOLS = [
|
|
|
77
101
|
properties: {
|
|
78
102
|
provider: { type: "string" },
|
|
79
103
|
name: { type: "string" },
|
|
80
|
-
|
|
104
|
+
note: {
|
|
105
|
+
type: "string",
|
|
106
|
+
description: "What you will wait for before acting on the first task result."
|
|
107
|
+
},
|
|
108
|
+
role: {
|
|
109
|
+
type: "string",
|
|
110
|
+
description: "Short public role (≤30 chars) the other members see, e.g. 代码审查."
|
|
111
|
+
},
|
|
112
|
+
instructions: {
|
|
113
|
+
type: "string",
|
|
114
|
+
description: "Private instructions only this member reads."
|
|
115
|
+
},
|
|
81
116
|
cwd: { type: "string" },
|
|
82
117
|
model: { type: "string" },
|
|
83
|
-
firstTask: { type: "string" }
|
|
118
|
+
firstTask: { type: "string" },
|
|
119
|
+
mode: {
|
|
120
|
+
type: "string",
|
|
121
|
+
description: "The member's dsh mode (a host preset id, e.g. dsh-gamedev). Omit to keep this room's own mode; \"none\" for only its own CLI tools."
|
|
122
|
+
}
|
|
84
123
|
},
|
|
85
124
|
required: ["provider", "name"],
|
|
86
125
|
additionalProperties: false
|
|
87
126
|
}
|
|
88
127
|
},
|
|
128
|
+
{
|
|
129
|
+
name: "ask_user_question",
|
|
130
|
+
description: "Ask the human questions they answer by picking options (or typing), instead of writing the questions into your reply. Use it whenever you need the human to decide or fill in something before you go on: give each question 2-4 short options with a one-line description each. The call waits for the answer and returns {answers:[{id, selected:[labels], custom?}]}; if nobody answers it fails — then go on with your best judgement and say what you assumed.",
|
|
131
|
+
inputSchema: {
|
|
132
|
+
type: "object",
|
|
133
|
+
properties: { questions: {
|
|
134
|
+
type: "array",
|
|
135
|
+
description: "At most 4 questions.",
|
|
136
|
+
items: {
|
|
137
|
+
type: "object",
|
|
138
|
+
properties: {
|
|
139
|
+
id: {
|
|
140
|
+
type: "string",
|
|
141
|
+
description: "Short unique id, echoed in the answer."
|
|
142
|
+
},
|
|
143
|
+
question: {
|
|
144
|
+
type: "string",
|
|
145
|
+
description: "The full question."
|
|
146
|
+
},
|
|
147
|
+
header: {
|
|
148
|
+
type: "string",
|
|
149
|
+
description: "Optional 2-6 word topic chip."
|
|
150
|
+
},
|
|
151
|
+
options: {
|
|
152
|
+
type: "array",
|
|
153
|
+
items: {
|
|
154
|
+
type: "object",
|
|
155
|
+
properties: {
|
|
156
|
+
label: { type: "string" },
|
|
157
|
+
description: { type: "string" }
|
|
158
|
+
},
|
|
159
|
+
required: ["label"]
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
multi_select: {
|
|
163
|
+
type: "boolean",
|
|
164
|
+
description: "True when several options may be picked."
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
required: ["id", "question"]
|
|
168
|
+
}
|
|
169
|
+
} },
|
|
170
|
+
required: ["questions"],
|
|
171
|
+
additionalProperties: false
|
|
172
|
+
}
|
|
173
|
+
},
|
|
89
174
|
{
|
|
90
175
|
name: "room_message",
|
|
91
|
-
description: "
|
|
176
|
+
description: "Send a room member a message by name (same as member_message). kind \"task\" (default): its reply comes back to you as a report citing the message id; kind \"notify\": FYI only. Do not poll.",
|
|
92
177
|
inputSchema: {
|
|
93
178
|
type: "object",
|
|
94
179
|
properties: {
|
|
95
180
|
member: { type: "string" },
|
|
96
|
-
text: { type: "string" }
|
|
181
|
+
text: { type: "string" },
|
|
182
|
+
kind: {
|
|
183
|
+
type: "string",
|
|
184
|
+
enum: ["task", "notify"]
|
|
185
|
+
},
|
|
186
|
+
inReplyTo: { type: "string" },
|
|
187
|
+
note: {
|
|
188
|
+
type: "string",
|
|
189
|
+
description: "What you will wait for before acting on the result."
|
|
190
|
+
},
|
|
191
|
+
task: {
|
|
192
|
+
type: "string",
|
|
193
|
+
description: "\"new\" or a task number; omit to stay in your current task."
|
|
194
|
+
}
|
|
97
195
|
},
|
|
98
196
|
required: ["member", "text"],
|
|
99
197
|
additionalProperties: false
|
|
100
198
|
}
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
name: "room_help",
|
|
202
|
+
description: "Ask another session outside this room for help — typically the one that built what you are stuck on. It works in its own session (a busy one queues you); its final answer plus a short handoff comes back to you on its own, so end your turn after asking and do not poll. Name the session by title, id or the @ mention the human wrote; a session that is another room goes to its coordinator, or to the member you name. The first ask of a session the human did not @ in the room waits for their approval. To answer its question or ask more, call again with the help id and no session.",
|
|
203
|
+
inputSchema: {
|
|
204
|
+
type: "object",
|
|
205
|
+
properties: {
|
|
206
|
+
session: {
|
|
207
|
+
type: "string",
|
|
208
|
+
description: "The session to ask (title, id, or @ mention). Omit when continuing a thread."
|
|
209
|
+
},
|
|
210
|
+
member: {
|
|
211
|
+
type: "string",
|
|
212
|
+
description: "When the session is another room: the member to ask. Omit to let its coordinator decide."
|
|
213
|
+
},
|
|
214
|
+
help: {
|
|
215
|
+
type: "string",
|
|
216
|
+
description: "The help id of an existing thread."
|
|
217
|
+
},
|
|
218
|
+
text: {
|
|
219
|
+
type: "string",
|
|
220
|
+
description: "What you need, self-contained: the helper does not see this room."
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
required: ["text"],
|
|
224
|
+
additionalProperties: false
|
|
225
|
+
}
|
|
101
226
|
}
|
|
102
227
|
];
|
|
103
228
|
async function callRoomCommand(env, name, args) {
|
|
@@ -116,6 +241,35 @@ async function callRoomCommand(env, name, args) {
|
|
|
116
241
|
return toolText(`Room command unavailable: ${String(error)}`, true);
|
|
117
242
|
}
|
|
118
243
|
}
|
|
244
|
+
/** The member's mode tools (the preset's plugin tools), empty when it has none or the host is unreachable. */
|
|
245
|
+
async function listModeTools(env) {
|
|
246
|
+
if (env.socket === void 0 || env.token === void 0) return [];
|
|
247
|
+
try {
|
|
248
|
+
const outcome = await callHost(env.socket, {
|
|
249
|
+
token: env.token,
|
|
250
|
+
modeTools: { list: true }
|
|
251
|
+
});
|
|
252
|
+
return outcome.ok && Array.isArray(outcome.tools) ? outcome.tools : [];
|
|
253
|
+
} catch {
|
|
254
|
+
return [];
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
/** Run one mode tool on the host; the host's MCP result passes through. */
|
|
258
|
+
async function callModeTool(env, name, args) {
|
|
259
|
+
if (env.socket === void 0 || env.token === void 0) return toolText("member channel is not configured for this run", true);
|
|
260
|
+
try {
|
|
261
|
+
const outcome = await callHost(env.socket, {
|
|
262
|
+
token: env.token,
|
|
263
|
+
modeTools: { call: {
|
|
264
|
+
name,
|
|
265
|
+
arguments: args
|
|
266
|
+
} }
|
|
267
|
+
});
|
|
268
|
+
return outcome.ok && outcome.result !== void 0 ? outcome.result : toolText(outcome.error ?? `Tool ${name} failed`, true);
|
|
269
|
+
} catch (error) {
|
|
270
|
+
return toolText(`Mode tool unavailable: ${String(error)}`, true);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
119
273
|
/** One socket round trip: send the request line, read the one-line response. */
|
|
120
274
|
function callHost(socket, payload) {
|
|
121
275
|
return new Promise((resolve, reject) => {
|
|
@@ -163,7 +317,11 @@ async function callMemberMessage(env, args) {
|
|
|
163
317
|
const outcome = await callHost(env.socket, {
|
|
164
318
|
token: env.token,
|
|
165
319
|
to: args.to,
|
|
166
|
-
text: args.text
|
|
320
|
+
text: args.text,
|
|
321
|
+
...args.kind === "task" || args.kind === "notify" ? { kind: args.kind } : {},
|
|
322
|
+
...typeof args.inReplyTo === "string" ? { inReplyTo: args.inReplyTo } : {},
|
|
323
|
+
...typeof args.note === "string" ? { note: args.note } : {},
|
|
324
|
+
...typeof args.task === "string" ? { task: args.task } : {}
|
|
167
325
|
});
|
|
168
326
|
if (outcome.ok) return toolText(outcome.receipt ?? "sent");
|
|
169
327
|
return toolText(outcome.error ?? "member channel rejected the message", true);
|
|
@@ -229,15 +387,19 @@ function serveMemberBridge(env, input, output) {
|
|
|
229
387
|
});
|
|
230
388
|
return;
|
|
231
389
|
case "tools/list":
|
|
232
|
-
write({
|
|
390
|
+
listModeTools(env).then((modeTools) => write({
|
|
233
391
|
jsonrpc: "2.0",
|
|
234
392
|
id: id ?? null,
|
|
235
|
-
result: { tools: [
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
393
|
+
result: { tools: [
|
|
394
|
+
{
|
|
395
|
+
name: MEMBER_MESSAGE_TOOL,
|
|
396
|
+
description: TOOL_DESCRIPTION,
|
|
397
|
+
inputSchema: TOOL_SCHEMA
|
|
398
|
+
},
|
|
399
|
+
...ROOM_TOOLS,
|
|
400
|
+
...modeTools.filter((tool) => tool["name"] !== "member_message" && !ROOM_TOOLS.some((room) => room.name === tool["name"]))
|
|
401
|
+
] }
|
|
402
|
+
}));
|
|
241
403
|
return;
|
|
242
404
|
case "tools/call": {
|
|
243
405
|
const params = request.params;
|
|
@@ -250,14 +412,22 @@ function serveMemberBridge(env, input, output) {
|
|
|
250
412
|
return;
|
|
251
413
|
}
|
|
252
414
|
if (params?.name !== "member_message") {
|
|
253
|
-
|
|
415
|
+
if (typeof params?.name !== "string") {
|
|
416
|
+
write({
|
|
417
|
+
jsonrpc: "2.0",
|
|
418
|
+
id: id ?? null,
|
|
419
|
+
error: {
|
|
420
|
+
code: -32602,
|
|
421
|
+
message: "tools/call requires a tool name"
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
callModeTool(env, params.name, params.arguments ?? {}).then((result) => write({
|
|
254
427
|
jsonrpc: "2.0",
|
|
255
428
|
id: id ?? null,
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
message: `unknown tool ${String(params?.name)}`
|
|
259
|
-
}
|
|
260
|
-
});
|
|
429
|
+
result
|
|
430
|
+
}));
|
|
261
431
|
return;
|
|
262
432
|
}
|
|
263
433
|
callMemberMessage(env, params.arguments ?? {}).then((result) => {
|
|
@@ -295,4 +465,4 @@ if (isMain()) serveMemberBridge({
|
|
|
295
465
|
token: process.env[MEMBER_BRIDGE_TOKEN_ENV]
|
|
296
466
|
}, process.stdin, process.stdout);
|
|
297
467
|
//#endregion
|
|
298
|
-
export { MEMBER_MESSAGE_TOOL, callMemberMessage, callRoomCommand, serveMemberBridge };
|
|
468
|
+
export { MEMBER_MESSAGE_TOOL, callMemberMessage, callModeTool, callRoomCommand, listModeTools, serveMemberBridge };
|
package/lib/tool-factory.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
1
2
|
import z from "@deepseek-ai/schemastery";
|
|
3
|
+
import { SessionId } from "@deepseek-ai/dsh-session";
|
|
2
4
|
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
3
5
|
//#region lib/types/tool-factory.js
|
|
4
6
|
/**
|
|
@@ -17,9 +19,15 @@ import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
|
17
19
|
* The tool instead carries the handle as a first-class `resume` parameter and
|
|
18
20
|
* resolves it through the localAgent service, which rejects any handle that
|
|
19
21
|
* does not name a delegation recorded for THIS parent session and provider.
|
|
20
|
-
* Continuation rounds still go through `ctx.subagents.
|
|
21
|
-
* events (`subagent/start`/`subagent/end`) and
|
|
22
|
-
* unchanged.
|
|
22
|
+
* Continuation rounds still go through `ctx.subagents.startActivation()` with
|
|
23
|
+
* caller delivery, so lifecycle events (`subagent/start`/`subagent/end`) and
|
|
24
|
+
* the standard 子代理 surface are unchanged.
|
|
25
|
+
*
|
|
26
|
+
* Host 0.2.1 note: `startActivation` rejects a provider run id that names a
|
|
27
|
+
* live Session (DUPLICATE_CHILD) — and every family provider's child session
|
|
28
|
+
* IS live. Providers therefore publish per-execution run ids, and the durable
|
|
29
|
+
* dsh child session id is minted HERE (the tool), staged on the delegation
|
|
30
|
+
* intent, and re-attached to the run adapter the rest of the family tracks.
|
|
23
31
|
*
|
|
24
32
|
* This module used to ship as the standalone companion package
|
|
25
33
|
* `@khorsheed/dsh-local-agent-tool-subagent` (0.1.x); the family fold moved it
|
|
@@ -84,8 +92,10 @@ function stopReasonError(result) {
|
|
|
84
92
|
/**
|
|
85
93
|
* Fuse a caller-owned controller with the tool execution's signal so EITHER
|
|
86
94
|
* abort cancels the run — the same shape the facade's tracked controller
|
|
87
|
-
* uses. The
|
|
88
|
-
*
|
|
95
|
+
* uses. The fused signal reaches `startActivation`'s `spec.signal`, which
|
|
96
|
+
* owns cancellation only until publication (host 0.2.1); the published
|
|
97
|
+
* activation's `dispose()` is the post-publication channel, wired below in
|
|
98
|
+
* the exec body and in the registry's stop lever.
|
|
89
99
|
*/
|
|
90
100
|
function fusedSignal(controller, caller) {
|
|
91
101
|
return caller === void 0 ? controller.signal : AbortSignal.any([controller.signal, caller]);
|
|
@@ -205,7 +215,11 @@ function mountSubagentDelegationTool(ctx, config, owner) {
|
|
|
205
215
|
provider: config.provider,
|
|
206
216
|
parentSessionId
|
|
207
217
|
});
|
|
208
|
-
|
|
218
|
+
const childSessionId = resumeHandle ?? randomUUID();
|
|
219
|
+
if (resumeHandle === void 0) ctx.localAgent.stageDelegationIntent(parentSessionId, config.provider, {
|
|
220
|
+
kind: "fresh",
|
|
221
|
+
childSessionId
|
|
222
|
+
});
|
|
209
223
|
else if (resume !== void 0) {
|
|
210
224
|
await ctx.localAgent.ensureChildLive?.(resumeHandle);
|
|
211
225
|
ctx.localAgent.stageDelegationIntent(parentSessionId, config.provider, {
|
|
@@ -214,27 +228,46 @@ function mountSubagentDelegationTool(ctx, config, owner) {
|
|
|
214
228
|
cliSessionId: resume.cliSessionId
|
|
215
229
|
});
|
|
216
230
|
}
|
|
217
|
-
const request = {
|
|
218
|
-
label: args.description,
|
|
219
|
-
prompt: [{
|
|
220
|
-
type: "text",
|
|
221
|
-
text: args.prompt
|
|
222
|
-
}],
|
|
223
|
-
parent
|
|
224
|
-
};
|
|
225
231
|
const controller = new AbortController();
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
|
|
232
|
+
const activation = await ctx.subagents.startActivation({
|
|
233
|
+
provider: config.provider,
|
|
234
|
+
label: args.description,
|
|
235
|
+
request: {
|
|
236
|
+
prompt: [{
|
|
237
|
+
type: "text",
|
|
238
|
+
text: args.prompt
|
|
239
|
+
}],
|
|
240
|
+
parent
|
|
241
|
+
},
|
|
242
|
+
signal: fusedSignal(controller, exec.signal),
|
|
243
|
+
delivery: "caller"
|
|
229
244
|
});
|
|
245
|
+
const dispose = () => activation.dispose();
|
|
246
|
+
const onExecAbort = () => {
|
|
247
|
+
dispose().catch((error) => {
|
|
248
|
+
ctx.logger.warn(`subagent tool: execution-signal disposal of ${childSessionId} failed: ${String(error)}`);
|
|
249
|
+
});
|
|
250
|
+
};
|
|
251
|
+
exec.signal.addEventListener("abort", onExecAbort, { once: true });
|
|
252
|
+
activation.result.then(() => exec.signal.removeEventListener("abort", onExecAbort), () => exec.signal.removeEventListener("abort", onExecAbort));
|
|
253
|
+
const run = {
|
|
254
|
+
id: SessionId(childSessionId),
|
|
255
|
+
result: activation.result,
|
|
256
|
+
dispose
|
|
257
|
+
};
|
|
230
258
|
const registry = ctx.localAgent;
|
|
231
|
-
if (typeof registry.trackDelegationRun === "function") registry.trackDelegationRun(
|
|
259
|
+
if (typeof registry.trackDelegationRun === "function") registry.trackDelegationRun(childSessionId, run, () => {
|
|
260
|
+
controller.abort();
|
|
261
|
+
dispose().catch((error) => {
|
|
262
|
+
ctx.logger.warn(`subagent tool: stop disposal of ${childSessionId} failed: ${String(error)}`);
|
|
263
|
+
});
|
|
264
|
+
});
|
|
232
265
|
const settled = await settleForegroundRun(run);
|
|
233
266
|
if (resume === void 0) return {
|
|
234
267
|
...settled,
|
|
235
268
|
output: [...settled.output, {
|
|
236
269
|
type: "text",
|
|
237
|
-
text: `${RESUME_HINT_PREFIX}${
|
|
270
|
+
text: `${RESUME_HINT_PREFIX}${childSessionId}"`
|
|
238
271
|
}]
|
|
239
272
|
};
|
|
240
273
|
return settled;
|