@mystilleef/pi-subagent 0.5.0 → 0.7.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/README.md +1 -1
- package/package.json +13 -11
- package/src/agent/agent-cache.ts +258 -0
- package/src/agent/agents.ts +232 -0
- package/src/child/child-events.ts +119 -0
- package/src/{process.ts → child/process.ts} +268 -122
- package/src/{prompt-contract.ts → child/prompt-contract.ts} +2 -0
- package/src/index.ts +43 -24
- package/src/{jobs-command.ts → orchestration/jobs-command.ts} +2 -3
- package/src/{run.ts → orchestration/run.ts} +2 -2
- package/src/{subagent-orchestrator.ts → orchestration/subagent-orchestrator.ts} +246 -124
- package/src/{normalize.ts → output/normalize.ts} +35 -49
- package/src/{summary.ts → output/summary.ts} +4 -2
- package/src/{ui.ts → output/ui.ts} +45 -48
- package/src/{progress-state.ts → progress/progress-state.ts} +164 -52
- package/src/{progress.ts → progress/progress.ts} +21 -21
- package/src/{result-details.ts → progress/result-details.ts} +55 -17
- package/src/shared/instance-name.ts +117 -0
- package/src/{types.ts → shared/types.ts} +13 -2
- package/src/{utils.ts → shared/utils.ts} +4 -6
- package/src/agent-cache.ts +0 -41
- package/src/agents.ts +0 -178
- package/src/child-events.ts +0 -32
- package/src/instance-name.ts +0 -164
- /package/src/{termination.ts → child/termination.ts} +0 -0
- /package/src/{cancel-command.ts → orchestration/cancel-command.ts} +0 -0
- /package/src/{run-command.ts → orchestration/run-command.ts} +0 -0
- /package/src/{run-registry.ts → orchestration/run-registry.ts} +0 -0
package/src/index.ts
CHANGED
|
@@ -5,18 +5,19 @@ import type {
|
|
|
5
5
|
import {
|
|
6
6
|
getCachedAgentCompletions,
|
|
7
7
|
resetAgentDiscoveryCache,
|
|
8
|
-
} from "./agent-cache.js";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import { renderSubagentResultMessage } from "./run.js";
|
|
13
|
-
import { runCommandHandler } from "./run-command.js";
|
|
8
|
+
} from "./agent/agent-cache.js";
|
|
9
|
+
import { isDirectoryAsync } from "./agent/agents.js";
|
|
10
|
+
import { cancelSubagentCommandHandler } from "./orchestration/cancel-command.js";
|
|
11
|
+
import { jobsCommandHandler } from "./orchestration/jobs-command.js";
|
|
12
|
+
import { renderSubagentResultMessage } from "./orchestration/run.js";
|
|
13
|
+
import { runCommandHandler } from "./orchestration/run-command.js";
|
|
14
14
|
import {
|
|
15
|
-
|
|
15
|
+
formatSubagentToolResult,
|
|
16
16
|
SubagentParams,
|
|
17
17
|
startSubagentJob,
|
|
18
|
-
} from "./subagent-orchestrator.js";
|
|
19
|
-
import { renderSubagentCall,
|
|
18
|
+
} from "./orchestration/subagent-orchestrator.js";
|
|
19
|
+
import { renderSubagentCall, renderSubagentToolResult } from "./output/ui.js";
|
|
20
|
+
import { renderSubagentProgress } from "./progress/progress.js";
|
|
20
21
|
|
|
21
22
|
export { SubagentParams };
|
|
22
23
|
|
|
@@ -24,15 +25,40 @@ export function resetAgentCache() {
|
|
|
24
25
|
resetAgentDiscoveryCache();
|
|
25
26
|
}
|
|
26
27
|
|
|
28
|
+
function normalizeWorkspaceRoot(cwd: string | undefined): string | undefined {
|
|
29
|
+
if (typeof cwd !== "string") return undefined;
|
|
30
|
+
const root = cwd.trim();
|
|
31
|
+
return root.length > 0 ? root : undefined;
|
|
32
|
+
}
|
|
33
|
+
|
|
27
34
|
export default function registerSubagentExtension(pi: ExtensionAPI) {
|
|
35
|
+
let activeWorkspaceRoot: string | undefined;
|
|
36
|
+
const setActiveWorkspaceRoot = (
|
|
37
|
+
cwd: string | undefined,
|
|
38
|
+
fallback?: string,
|
|
39
|
+
) => {
|
|
40
|
+
activeWorkspaceRoot = normalizeWorkspaceRoot(cwd ?? fallback);
|
|
41
|
+
};
|
|
42
|
+
const getRunArgumentCompletions = async (prefix: string) => {
|
|
43
|
+
if (!activeWorkspaceRoot) return [];
|
|
44
|
+
if (!(await isDirectoryAsync(activeWorkspaceRoot))) return [];
|
|
45
|
+
return getCachedAgentCompletions(prefix, activeWorkspaceRoot);
|
|
46
|
+
};
|
|
47
|
+
pi.on("resources_discover", (event, ctx) => {
|
|
48
|
+
setActiveWorkspaceRoot(ctx.cwd, event.cwd);
|
|
49
|
+
});
|
|
50
|
+
pi.on("session_start", (_event, ctx) => {
|
|
51
|
+
setActiveWorkspaceRoot(ctx.cwd);
|
|
52
|
+
});
|
|
28
53
|
pi.registerMessageRenderer("subagent-progress", renderSubagentProgress);
|
|
29
54
|
pi.registerMessageRenderer("subagent-result", renderSubagentResultMessage);
|
|
30
55
|
pi.registerCommand("run", {
|
|
31
56
|
description: "Run a subagent directly: /run <agent> [task]",
|
|
32
|
-
getArgumentCompletions:
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
runCommandHandler(pi, ctx as ExtensionContext, args)
|
|
57
|
+
getArgumentCompletions: getRunArgumentCompletions,
|
|
58
|
+
handler: async (args, ctx) => {
|
|
59
|
+
setActiveWorkspaceRoot(ctx.cwd);
|
|
60
|
+
return runCommandHandler(pi, ctx as ExtensionContext, args);
|
|
61
|
+
},
|
|
36
62
|
});
|
|
37
63
|
pi.registerCommand("cancel-subagent", {
|
|
38
64
|
description:
|
|
@@ -48,28 +74,21 @@ export default function registerSubagentExtension(pi: ExtensionAPI) {
|
|
|
48
74
|
label: "Subagent",
|
|
49
75
|
description: "Delegate a task to a subagent with isolated context.",
|
|
50
76
|
parameters: SubagentParams,
|
|
51
|
-
async execute(_toolCallId, params, signal,
|
|
77
|
+
async execute(_toolCallId, params, signal, onUpdate, ctx) {
|
|
52
78
|
const result = await startSubagentJob(
|
|
53
79
|
pi,
|
|
54
80
|
ctx,
|
|
55
81
|
params,
|
|
56
82
|
signal ?? undefined,
|
|
83
|
+
onUpdate ?? undefined,
|
|
57
84
|
);
|
|
58
|
-
return
|
|
59
|
-
content: [
|
|
60
|
-
{
|
|
61
|
-
type: "text" as const,
|
|
62
|
-
text: formatStartJobStatus(params.agent, result),
|
|
63
|
-
},
|
|
64
|
-
],
|
|
65
|
-
details: result.makeDetails([]),
|
|
66
|
-
};
|
|
85
|
+
return formatSubagentToolResult(params.agent, result);
|
|
67
86
|
},
|
|
68
87
|
renderCall(args, theme, _context) {
|
|
69
88
|
return renderSubagentCall(args, theme);
|
|
70
89
|
},
|
|
71
90
|
renderResult(result, display, theme, _context) {
|
|
72
|
-
return
|
|
91
|
+
return renderSubagentToolResult(result, theme, display);
|
|
73
92
|
},
|
|
74
93
|
});
|
|
75
94
|
}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
|
|
2
|
+
import { renderRunsBoard } from "../output/ui.js";
|
|
3
3
|
import {
|
|
4
4
|
getAllProgressStates,
|
|
5
5
|
type SubagentProgressState,
|
|
6
|
-
} from "
|
|
6
|
+
} from "../progress/progress-state.js";
|
|
7
7
|
import { listRunJobs } from "./run-registry.js";
|
|
8
|
-
import { renderRunsBoard } from "./ui.js";
|
|
9
8
|
|
|
10
9
|
export async function jobsCommandHandler(
|
|
11
10
|
ctx: ExtensionCommandContext,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import {
|
|
1
|
+
import { renderSubagentResult, type SubagentTheme } from "../output/ui.js";
|
|
2
|
+
import type { SubagentDetails } from "../shared/types.js";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Pi message renderer adapter for `"subagent-result"` messages.
|
|
@@ -1,46 +1,48 @@
|
|
|
1
|
+
import type { AgentToolUpdateCallback } from "@earendil-works/pi-agent-core";
|
|
1
2
|
import { type Message, StringEnum } from "@earendil-works/pi-ai";
|
|
2
3
|
import type {
|
|
3
4
|
ExtensionAPI,
|
|
4
5
|
ExtensionContext,
|
|
5
6
|
} from "@earendil-works/pi-coding-agent";
|
|
6
7
|
import { type Static, Type } from "typebox";
|
|
7
|
-
import { getCachedAgentDiscovery } from "
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
} from "
|
|
14
|
-
import {
|
|
15
|
-
import { runSingleAgent } from "./process.js";
|
|
8
|
+
import { getCachedAgentDiscovery } from "../agent/agent-cache.js";
|
|
9
|
+
import type {
|
|
10
|
+
AgentConfig,
|
|
11
|
+
AgentScope,
|
|
12
|
+
ThinkingLevel,
|
|
13
|
+
} from "../agent/agents.js";
|
|
14
|
+
import { runSingleAgent, SubagentAbortError } from "../child/process.js";
|
|
15
|
+
import { formatSubagentResultForParent } from "../output/summary.js";
|
|
16
16
|
import {
|
|
17
17
|
cancelProgressState,
|
|
18
18
|
createProgressState,
|
|
19
19
|
failProgressState,
|
|
20
20
|
finalizeProgressState,
|
|
21
21
|
getProgressState,
|
|
22
|
-
} from "
|
|
22
|
+
} from "../progress/progress.js";
|
|
23
23
|
import {
|
|
24
24
|
createSubagentError,
|
|
25
25
|
getFeedbackSummaryText,
|
|
26
|
+
getLatestResult,
|
|
26
27
|
getResultDisplayText,
|
|
27
28
|
hasSubagentFailed,
|
|
28
29
|
patchProgressFromDetails,
|
|
29
30
|
sanitizeDetailsForDisplay,
|
|
30
|
-
} from "
|
|
31
|
+
} from "../progress/result-details.js";
|
|
32
|
+
import { generateSubagentInstanceName } from "../shared/instance-name.js";
|
|
33
|
+
import type {
|
|
34
|
+
OnUpdateCallback,
|
|
35
|
+
SingleResult,
|
|
36
|
+
SubagentDetails,
|
|
37
|
+
SubagentToolResult,
|
|
38
|
+
} from "../shared/types.js";
|
|
39
|
+
import { getSubagentDepth } from "../shared/utils.js";
|
|
31
40
|
import {
|
|
32
41
|
listRunJobs,
|
|
33
42
|
type RunJob,
|
|
34
43
|
registerRunJob,
|
|
35
44
|
removeRunJob,
|
|
36
45
|
} from "./run-registry.js";
|
|
37
|
-
import { formatSubagentResultForParent } from "./summary.js";
|
|
38
|
-
import type {
|
|
39
|
-
OnUpdateCallback,
|
|
40
|
-
SingleResult,
|
|
41
|
-
SubagentDetails,
|
|
42
|
-
SubagentToolResult,
|
|
43
|
-
} from "./types.js";
|
|
44
46
|
|
|
45
47
|
const AgentScopeSchema = StringEnum(["user", "project", "both"] as const, {
|
|
46
48
|
description:
|
|
@@ -75,6 +77,22 @@ type DetailsBuilder = (
|
|
|
75
77
|
options?: DetailsOptions,
|
|
76
78
|
) => SubagentDetails;
|
|
77
79
|
|
|
80
|
+
interface LifecycleContext {
|
|
81
|
+
pi: ExtensionAPI;
|
|
82
|
+
ctx: ExtensionContext;
|
|
83
|
+
requestId: string;
|
|
84
|
+
job: RunJob;
|
|
85
|
+
debug: boolean;
|
|
86
|
+
makeDetails: DetailsBuilder;
|
|
87
|
+
mergedSignal: AbortSignal;
|
|
88
|
+
agents: AgentConfig[];
|
|
89
|
+
agentName: string;
|
|
90
|
+
task: string;
|
|
91
|
+
parentModel: { provider: string; id: string } | undefined;
|
|
92
|
+
parentThinking: ThinkingLevel;
|
|
93
|
+
hostOnUpdate?: AgentToolUpdateCallback<SubagentDetails>;
|
|
94
|
+
}
|
|
95
|
+
|
|
78
96
|
function createDetailsBuilder(
|
|
79
97
|
agentScope: AgentScope,
|
|
80
98
|
projectAgentsDir: string | null,
|
|
@@ -99,37 +117,40 @@ function sanitizeResultDetails(
|
|
|
99
117
|
includeDebugMessages && (options?.includeMessages ?? true);
|
|
100
118
|
const { messages, termination, progress, stderr, usage, ...core } = result;
|
|
101
119
|
const { contextWindowTokens, ...usageBase } = usage;
|
|
102
|
-
|
|
103
120
|
const sanitized: Record<string, unknown> = {
|
|
104
121
|
...core,
|
|
105
122
|
stderr: includeDebugMessages ? stderr : "",
|
|
106
123
|
usage: { ...usageBase },
|
|
107
124
|
};
|
|
108
|
-
|
|
109
125
|
if (contextWindowTokens !== undefined) {
|
|
110
126
|
(sanitized.usage as Record<string, unknown>).contextWindowTokens =
|
|
111
127
|
contextWindowTokens;
|
|
112
128
|
}
|
|
113
|
-
|
|
114
129
|
if (progress !== undefined) {
|
|
115
|
-
const {
|
|
130
|
+
const {
|
|
131
|
+
activityText,
|
|
132
|
+
activeToolActivity,
|
|
133
|
+
lastToolPreview,
|
|
134
|
+
toolResultCompleted,
|
|
135
|
+
...progBase
|
|
136
|
+
} = progress;
|
|
116
137
|
sanitized.progress = {
|
|
117
138
|
toolCalls: progBase.toolCalls.map((tc) => ({
|
|
118
139
|
id: tc.id,
|
|
119
140
|
preview: tc.preview,
|
|
120
141
|
})),
|
|
121
142
|
...(activityText !== undefined && { activityText }),
|
|
143
|
+
...(activeToolActivity !== undefined && { activeToolActivity }),
|
|
122
144
|
...(lastToolPreview !== undefined && { lastToolPreview }),
|
|
145
|
+
...(toolResultCompleted !== undefined && { toolResultCompleted }),
|
|
123
146
|
};
|
|
124
147
|
}
|
|
125
|
-
|
|
126
148
|
if (includeMessages) {
|
|
127
149
|
sanitized.messages = options?.recentMessages
|
|
128
150
|
? [...options.recentMessages]
|
|
129
151
|
: messages !== undefined
|
|
130
152
|
? [...messages]
|
|
131
153
|
: undefined;
|
|
132
|
-
|
|
133
154
|
if (includeDebugMessages && termination !== undefined) {
|
|
134
155
|
const { cancelReason, terminationSignal, fallbackCause, ...termBase } =
|
|
135
156
|
termination;
|
|
@@ -141,7 +162,6 @@ function sanitizeResultDetails(
|
|
|
141
162
|
};
|
|
142
163
|
}
|
|
143
164
|
}
|
|
144
|
-
|
|
145
165
|
return sanitized as unknown as SingleResult;
|
|
146
166
|
}
|
|
147
167
|
|
|
@@ -184,93 +204,136 @@ export function emitCompletionAlert(
|
|
|
184
204
|
process.stdout.write("\x07");
|
|
185
205
|
}
|
|
186
206
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
207
|
+
function createCompletedToolResult(
|
|
208
|
+
content: string,
|
|
209
|
+
details: SubagentDetails,
|
|
210
|
+
): SubagentToolResult {
|
|
211
|
+
return {
|
|
212
|
+
content: [{ type: "text", text: content }],
|
|
213
|
+
details: { ...details, renderedByMessage: true },
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function finishLifecycleFailure(
|
|
218
|
+
lc: LifecycleContext,
|
|
219
|
+
errorMessage: string,
|
|
220
|
+
details: SubagentDetails,
|
|
221
|
+
): SubagentToolResult {
|
|
222
|
+
const displayDetails = sanitizeDetailsForDisplay(details, lc.debug);
|
|
223
|
+
if (lc.mergedSignal.aborted) {
|
|
224
|
+
cancelProgressState(lc.requestId, lc.job.cancelReason ?? errorMessage);
|
|
225
|
+
if (getSubagentDepth() > 0) {
|
|
226
|
+
sendSubagentResultMessage(lc.pi, "Canceled", displayDetails);
|
|
227
|
+
}
|
|
228
|
+
return createCompletedToolResult("Canceled", displayDetails);
|
|
229
|
+
}
|
|
230
|
+
failProgressState(lc.requestId, errorMessage);
|
|
231
|
+
lc.ctx.ui?.notify(errorMessage, "error");
|
|
232
|
+
const latestResult = getLatestResult(details);
|
|
233
|
+
const content = latestResult
|
|
234
|
+
? formatSubagentResultForParent(latestResult) || "(failed)"
|
|
235
|
+
: errorMessage;
|
|
236
|
+
sendSubagentResultMessage(lc.pi, content, displayDetails);
|
|
237
|
+
return createCompletedToolResult(content, displayDetails);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function finishLifecycleResult(
|
|
241
|
+
lc: LifecycleContext,
|
|
242
|
+
result: SingleResult,
|
|
243
|
+
): SubagentToolResult {
|
|
244
|
+
const details = lc.makeDetails([result]);
|
|
245
|
+
if (hasSubagentFailed(result)) {
|
|
246
|
+
return finishLifecycleFailure(
|
|
247
|
+
lc,
|
|
248
|
+
lc.mergedSignal.aborted ? "Aborted" : createSubagentError(result).message,
|
|
249
|
+
details,
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
const displayDetails = sanitizeDetailsForDisplay(details, lc.debug);
|
|
253
|
+
const content = formatSubagentResultForParent(result) || "(no output)";
|
|
254
|
+
const toolResult = createCompletedToolResult(content, displayDetails);
|
|
255
|
+
finalizeProgressState(lc.requestId, getFeedbackSummaryText(toolResult));
|
|
256
|
+
sendSubagentResultMessage(
|
|
257
|
+
lc.pi,
|
|
258
|
+
getResultDisplayText(toolResult),
|
|
259
|
+
displayDetails,
|
|
260
|
+
);
|
|
261
|
+
return toolResult;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function createPayloadFingerprint(payload: {
|
|
265
|
+
content: { type: string; text?: string }[];
|
|
266
|
+
details: SubagentDetails;
|
|
267
|
+
}): string {
|
|
268
|
+
const contentText = payload.content[0]?.text ?? "";
|
|
269
|
+
const latestResult = payload.details.results[0];
|
|
270
|
+
const activityText = latestResult?.progress?.activityText ?? "";
|
|
271
|
+
const toolCallIds =
|
|
272
|
+
[...new Set(latestResult?.progress?.toolCalls?.map((tc) => tc.id))]
|
|
273
|
+
.sort()
|
|
274
|
+
.join(",") ?? "";
|
|
275
|
+
const exitCode = latestResult?.exitCode ?? 0;
|
|
276
|
+
const stopReason = latestResult?.stopReason ?? "";
|
|
277
|
+
return `${contentText}|${activityText}|${toolCallIds}|${exitCode}|${stopReason}`;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
async function runSubagentLifecycle(
|
|
281
|
+
lc: LifecycleContext,
|
|
282
|
+
): Promise<SubagentToolResult> {
|
|
201
283
|
const seenToolCallIds = new Set<string>();
|
|
202
|
-
const requestProgressRender = createProgressRenderRequester(
|
|
284
|
+
const requestProgressRender = createProgressRenderRequester(
|
|
285
|
+
lc.ctx,
|
|
286
|
+
lc.requestId,
|
|
287
|
+
);
|
|
288
|
+
let lastDeliveredFingerprint: string | undefined;
|
|
203
289
|
const onUpdate: OnUpdateCallback = (result) => {
|
|
204
|
-
patchProgressFromDetails(requestId, result.details, seenToolCallIds);
|
|
290
|
+
patchProgressFromDetails(lc.requestId, result.details, seenToolCallIds);
|
|
205
291
|
requestProgressRender();
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
details: SubagentDetails,
|
|
211
|
-
) {
|
|
212
|
-
if (isAborted) {
|
|
213
|
-
cancelProgressState(requestId, job.cancelReason ?? errorMessage);
|
|
214
|
-
} else {
|
|
215
|
-
failProgressState(requestId, errorMessage);
|
|
216
|
-
ctx.ui?.notify(errorMessage, "error");
|
|
217
|
-
const content = details.results[0]
|
|
218
|
-
? formatSubagentResultForParent(details.results[0] as SingleResult) ||
|
|
219
|
-
"(failed)"
|
|
220
|
-
: errorMessage;
|
|
221
|
-
sendSubagentResultMessage(
|
|
222
|
-
pi,
|
|
223
|
-
content,
|
|
224
|
-
sanitizeDetailsForDisplay(details, debug),
|
|
292
|
+
if (lc.hostOnUpdate) {
|
|
293
|
+
const sanitizedDetails = sanitizeDetailsForDisplay(
|
|
294
|
+
result.details,
|
|
295
|
+
lc.debug,
|
|
225
296
|
);
|
|
297
|
+
const { renderedByMessage, ...partialDetails } = sanitizedDetails;
|
|
298
|
+
const payload = {
|
|
299
|
+
content: result.content,
|
|
300
|
+
details: partialDetails,
|
|
301
|
+
};
|
|
302
|
+
const fingerprint = createPayloadFingerprint(payload);
|
|
303
|
+
if (fingerprint !== lastDeliveredFingerprint) {
|
|
304
|
+
lastDeliveredFingerprint = fingerprint;
|
|
305
|
+
lc.hostOnUpdate(payload);
|
|
306
|
+
}
|
|
226
307
|
}
|
|
227
|
-
}
|
|
308
|
+
};
|
|
309
|
+
const timerTick = setInterval(requestProgressRender, 500);
|
|
228
310
|
try {
|
|
229
311
|
const result = await runSingleAgent(
|
|
230
|
-
ctx.cwd,
|
|
231
|
-
agents,
|
|
232
|
-
agentName,
|
|
233
|
-
task,
|
|
234
|
-
mergedSignal,
|
|
312
|
+
lc.ctx.cwd,
|
|
313
|
+
lc.agents,
|
|
314
|
+
lc.agentName,
|
|
315
|
+
lc.task,
|
|
316
|
+
lc.mergedSignal,
|
|
235
317
|
onUpdate,
|
|
236
|
-
makeDetails,
|
|
237
|
-
parentModel,
|
|
238
|
-
parentThinking,
|
|
318
|
+
lc.makeDetails,
|
|
319
|
+
lc.parentModel,
|
|
320
|
+
lc.parentThinking,
|
|
239
321
|
);
|
|
240
|
-
|
|
241
|
-
handleWorkerFailure(
|
|
242
|
-
mergedSignal.aborted ? "Aborted" : createSubagentError(result).message,
|
|
243
|
-
mergedSignal.aborted,
|
|
244
|
-
makeDetails([result]),
|
|
245
|
-
);
|
|
246
|
-
} else {
|
|
247
|
-
const toolResult: SubagentToolResult = {
|
|
248
|
-
content: [
|
|
249
|
-
{
|
|
250
|
-
type: "text" as const,
|
|
251
|
-
text: formatSubagentResultForParent(result) || "(no output)",
|
|
252
|
-
},
|
|
253
|
-
],
|
|
254
|
-
details: makeDetails([result]),
|
|
255
|
-
};
|
|
256
|
-
finalizeProgressState(requestId, getFeedbackSummaryText(toolResult));
|
|
257
|
-
sendSubagentResultMessage(
|
|
258
|
-
pi,
|
|
259
|
-
getResultDisplayText(toolResult),
|
|
260
|
-
sanitizeDetailsForDisplay(toolResult.details, debug),
|
|
261
|
-
);
|
|
262
|
-
}
|
|
322
|
+
return finishLifecycleResult(lc, result);
|
|
263
323
|
} catch (error) {
|
|
264
|
-
|
|
324
|
+
const abortResult =
|
|
325
|
+
error instanceof SubagentAbortError ? error.result : undefined;
|
|
326
|
+
return finishLifecycleFailure(
|
|
327
|
+
lc,
|
|
265
328
|
error instanceof Error ? error.message : String(error),
|
|
266
|
-
|
|
267
|
-
makeDetails([]),
|
|
329
|
+
abortResult ? lc.makeDetails([abortResult]) : lc.makeDetails([]),
|
|
268
330
|
);
|
|
269
331
|
} finally {
|
|
332
|
+
clearInterval(timerTick);
|
|
270
333
|
requestProgressRender();
|
|
271
|
-
removeRunJob(requestId);
|
|
334
|
+
removeRunJob(lc.requestId);
|
|
272
335
|
if (listRunJobs().length === 0) {
|
|
273
|
-
const state = getProgressState(requestId);
|
|
336
|
+
const state = getProgressState(lc.requestId);
|
|
274
337
|
if (state) {
|
|
275
338
|
emitCompletionAlert(state);
|
|
276
339
|
}
|
|
@@ -278,23 +341,43 @@ async function runSubagentWorker(
|
|
|
278
341
|
}
|
|
279
342
|
}
|
|
280
343
|
|
|
281
|
-
|
|
344
|
+
type StartJobResult =
|
|
282
345
|
| {
|
|
283
346
|
kind: "started";
|
|
284
347
|
requestId: string;
|
|
285
348
|
instanceName: string;
|
|
286
349
|
makeDetails: DetailsBuilder;
|
|
287
350
|
}
|
|
351
|
+
| { kind: "completed"; result: SubagentToolResult }
|
|
288
352
|
| { kind: "cancelled"; makeDetails: DetailsBuilder }
|
|
289
353
|
| { kind: "not_found"; makeDetails: DetailsBuilder };
|
|
290
354
|
|
|
291
|
-
|
|
355
|
+
type PrepareSubagentJobResult =
|
|
356
|
+
| {
|
|
357
|
+
kind: "ready";
|
|
358
|
+
lc: LifecycleContext;
|
|
359
|
+
instanceName: string;
|
|
360
|
+
requestProgressRender: () => void;
|
|
361
|
+
hostOnUpdate?: AgentToolUpdateCallback<SubagentDetails>;
|
|
362
|
+
}
|
|
363
|
+
| { kind: "not_found"; makeDetails: DetailsBuilder }
|
|
364
|
+
| { kind: "cancelled"; makeDetails: DetailsBuilder }
|
|
365
|
+
| { kind: "aborted"; makeDetails: DetailsBuilder };
|
|
366
|
+
|
|
367
|
+
export function formatSubagentToolResult(
|
|
292
368
|
agentName: string,
|
|
293
369
|
result: StartJobResult,
|
|
294
|
-
):
|
|
295
|
-
if (result.kind === "
|
|
296
|
-
|
|
297
|
-
|
|
370
|
+
): SubagentToolResult {
|
|
371
|
+
if (result.kind === "completed") return result.result;
|
|
372
|
+
let text: string;
|
|
373
|
+
if (result.kind === "not_found") text = `Unknown agent: "${agentName}"`;
|
|
374
|
+
else if (result.kind === "cancelled") text = "Canceled";
|
|
375
|
+
else
|
|
376
|
+
text = `Subagent ${agentName} ${result.instanceName} started (job: ${result.requestId})`;
|
|
377
|
+
return {
|
|
378
|
+
content: [{ type: "text", text }],
|
|
379
|
+
details: result.makeDetails([]),
|
|
380
|
+
};
|
|
298
381
|
}
|
|
299
382
|
|
|
300
383
|
function needsProjectAgentConfirmation(
|
|
@@ -316,14 +399,15 @@ function confirmProjectAgentRun(
|
|
|
316
399
|
);
|
|
317
400
|
}
|
|
318
401
|
|
|
319
|
-
|
|
402
|
+
async function prepareSubagentJob(
|
|
320
403
|
pi: ExtensionAPI,
|
|
321
404
|
ctx: ExtensionContext,
|
|
322
405
|
params: Static<typeof SubagentParams>,
|
|
323
406
|
hostSignal: AbortSignal | undefined,
|
|
324
|
-
|
|
407
|
+
hostOnUpdate?: AgentToolUpdateCallback<SubagentDetails>,
|
|
408
|
+
): Promise<PrepareSubagentJobResult> {
|
|
325
409
|
const agentScope: AgentScope = params.agentScope ?? "both";
|
|
326
|
-
const discovery = getCachedAgentDiscovery(ctx.cwd, agentScope);
|
|
410
|
+
const discovery = await getCachedAgentDiscovery(ctx.cwd, agentScope);
|
|
327
411
|
const agents = discovery.agents;
|
|
328
412
|
const debug = params.debug === true;
|
|
329
413
|
const makeDetails = createDetailsBuilder(
|
|
@@ -344,7 +428,7 @@ export async function startSubagentJob(
|
|
|
344
428
|
if (!confirmed) return { kind: "cancelled", makeDetails };
|
|
345
429
|
}
|
|
346
430
|
if (requested.source === "project") {
|
|
347
|
-
const userAgents =
|
|
431
|
+
const userAgents = await getCachedAgentDiscovery(ctx.cwd, "user");
|
|
348
432
|
const hasUserCollision = userAgents.agents.some(
|
|
349
433
|
(a) => a.name === requested.name,
|
|
350
434
|
);
|
|
@@ -387,32 +471,70 @@ export async function startSubagentJob(
|
|
|
387
471
|
details: { agent: params.agent, instanceName, requestId },
|
|
388
472
|
});
|
|
389
473
|
const requestProgressRender = createProgressRenderRequester(ctx, requestId);
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
474
|
+
if (mergedSignal.aborted) {
|
|
475
|
+
cancelStartedJob(job, job.cancelReason ?? "Aborted");
|
|
476
|
+
requestProgressRender();
|
|
477
|
+
return { kind: "aborted", makeDetails: makeStartedDetails };
|
|
478
|
+
}
|
|
479
|
+
return {
|
|
480
|
+
kind: "ready",
|
|
481
|
+
lc: {
|
|
397
482
|
pi,
|
|
398
483
|
ctx,
|
|
484
|
+
requestId,
|
|
485
|
+
job,
|
|
486
|
+
debug,
|
|
487
|
+
makeDetails: makeStartedDetails,
|
|
488
|
+
mergedSignal,
|
|
399
489
|
agents,
|
|
400
|
-
params.agent,
|
|
490
|
+
agentName: params.agent,
|
|
401
491
|
task,
|
|
402
|
-
debug,
|
|
403
492
|
parentModel,
|
|
404
493
|
parentThinking,
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
494
|
+
hostOnUpdate,
|
|
495
|
+
},
|
|
496
|
+
instanceName,
|
|
497
|
+
requestProgressRender,
|
|
498
|
+
hostOnUpdate,
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
export async function startSubagentJob(
|
|
503
|
+
pi: ExtensionAPI,
|
|
504
|
+
ctx: ExtensionContext,
|
|
505
|
+
params: Static<typeof SubagentParams>,
|
|
506
|
+
hostSignal: AbortSignal | undefined,
|
|
507
|
+
hostOnUpdate?: AgentToolUpdateCallback<SubagentDetails>,
|
|
508
|
+
): Promise<StartJobResult> {
|
|
509
|
+
const prepared = await prepareSubagentJob(
|
|
510
|
+
pi,
|
|
511
|
+
ctx,
|
|
512
|
+
params,
|
|
513
|
+
hostSignal,
|
|
514
|
+
hostOnUpdate,
|
|
515
|
+
);
|
|
516
|
+
if (prepared.kind !== "ready") {
|
|
517
|
+
if (prepared.kind === "aborted")
|
|
518
|
+
return { kind: "cancelled", makeDetails: prepared.makeDetails };
|
|
519
|
+
return prepared;
|
|
520
|
+
}
|
|
521
|
+
const { lc, instanceName, requestProgressRender } = prepared;
|
|
522
|
+
if (getSubagentDepth() > 0) {
|
|
523
|
+
const result = await runSubagentLifecycle(lc);
|
|
524
|
+
return { kind: "completed", result };
|
|
525
|
+
}
|
|
526
|
+
setImmediate(() => {
|
|
527
|
+
if (lc.mergedSignal.aborted) {
|
|
528
|
+
cancelStartedJob(lc.job, lc.job.cancelReason ?? "Aborted");
|
|
529
|
+
requestProgressRender();
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
runSubagentLifecycle(lc);
|
|
410
533
|
});
|
|
411
|
-
if (mergedSignal.aborted) return { kind: "cancelled", makeDetails };
|
|
412
534
|
return {
|
|
413
535
|
kind: "started",
|
|
414
|
-
requestId,
|
|
536
|
+
requestId: lc.requestId,
|
|
415
537
|
instanceName,
|
|
416
|
-
makeDetails:
|
|
538
|
+
makeDetails: lc.makeDetails,
|
|
417
539
|
};
|
|
418
540
|
}
|