wave-agent-sdk 0.5.0 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent.d.ts +14 -11
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +64 -151
- package/dist/constants/subagents.d.ts +5 -0
- package/dist/constants/subagents.d.ts.map +1 -0
- package/dist/constants/subagents.js +4 -0
- package/dist/constants/tools.d.ts +4 -1
- package/dist/constants/tools.d.ts.map +1 -1
- package/dist/constants/tools.js +4 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/managers/aiManager.d.ts +2 -5
- package/dist/managers/aiManager.d.ts.map +1 -1
- package/dist/managers/aiManager.js +43 -48
- package/dist/managers/backgroundTaskManager.d.ts.map +1 -1
- package/dist/managers/backgroundTaskManager.js +63 -53
- package/dist/managers/foregroundTaskManager.d.ts.map +1 -1
- package/dist/managers/foregroundTaskManager.js +3 -2
- package/dist/managers/mcpManager.d.ts.map +1 -1
- package/dist/managers/messageManager.d.ts +13 -27
- package/dist/managers/messageManager.d.ts.map +1 -1
- package/dist/managers/messageManager.js +94 -89
- package/dist/managers/permissionManager.d.ts.map +1 -1
- package/dist/managers/permissionManager.js +25 -15
- package/dist/managers/planManager.d.ts +1 -1
- package/dist/managers/planManager.d.ts.map +1 -1
- package/dist/managers/planManager.js +2 -2
- package/dist/managers/reversionManager.d.ts.map +1 -1
- package/dist/managers/reversionManager.js +23 -2
- package/dist/managers/slashCommandManager.d.ts +3 -0
- package/dist/managers/slashCommandManager.d.ts.map +1 -1
- package/dist/managers/slashCommandManager.js +8 -3
- package/dist/managers/subagentManager.d.ts +8 -14
- package/dist/managers/subagentManager.d.ts.map +1 -1
- package/dist/managers/subagentManager.js +46 -112
- package/dist/managers/toolManager.d.ts +11 -0
- package/dist/managers/toolManager.d.ts.map +1 -1
- package/dist/managers/toolManager.js +20 -2
- package/dist/{constants/prompts.d.ts → prompts/index.d.ts} +17 -15
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +309 -0
- package/dist/services/aiService.d.ts +0 -1
- package/dist/services/aiService.d.ts.map +1 -1
- package/dist/services/aiService.js +4 -140
- package/dist/services/memory.d.ts +0 -3
- package/dist/services/memory.d.ts.map +1 -1
- package/dist/services/memory.js +0 -59
- package/dist/services/session.d.ts +15 -1
- package/dist/services/session.d.ts.map +1 -1
- package/dist/services/session.js +57 -1
- package/dist/services/taskManager.d.ts +25 -0
- package/dist/services/taskManager.d.ts.map +1 -0
- package/dist/services/taskManager.js +164 -0
- package/dist/tools/askUserQuestion.d.ts.map +1 -1
- package/dist/tools/askUserQuestion.js +39 -25
- package/dist/tools/bashTool.d.ts.map +1 -1
- package/dist/tools/bashTool.js +13 -11
- package/dist/tools/editTool.d.ts.map +1 -1
- package/dist/tools/editTool.js +2 -1
- package/dist/tools/exitPlanMode.d.ts.map +1 -1
- package/dist/tools/exitPlanMode.js +26 -2
- package/dist/tools/globTool.d.ts.map +1 -1
- package/dist/tools/globTool.js +8 -2
- package/dist/tools/grepTool.d.ts.map +1 -1
- package/dist/tools/grepTool.js +17 -6
- package/dist/tools/lsTool.d.ts.map +1 -1
- package/dist/tools/lsTool.js +3 -1
- package/dist/tools/readTool.d.ts.map +1 -1
- package/dist/tools/readTool.js +16 -1
- package/dist/tools/taskManagementTools.d.ts +6 -0
- package/dist/tools/taskManagementTools.d.ts.map +1 -0
- package/dist/tools/taskManagementTools.js +461 -0
- package/dist/tools/taskOutputTool.d.ts.map +1 -1
- package/dist/tools/taskOutputTool.js +32 -8
- package/dist/tools/taskStopTool.d.ts.map +1 -1
- package/dist/tools/taskStopTool.js +7 -1
- package/dist/tools/taskTool.d.ts.map +1 -1
- package/dist/tools/taskTool.js +37 -2
- package/dist/tools/types.d.ts +11 -0
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/tools/writeTool.d.ts.map +1 -1
- package/dist/tools/writeTool.js +9 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -0
- package/dist/types/messaging.d.ts +2 -18
- package/dist/types/messaging.d.ts.map +1 -1
- package/dist/types/processes.d.ts +16 -6
- package/dist/types/processes.d.ts.map +1 -1
- package/dist/types/tasks.d.ts +13 -0
- package/dist/types/tasks.d.ts.map +1 -0
- package/dist/types/tasks.js +1 -0
- package/dist/types/tools.d.ts +4 -1
- package/dist/types/tools.d.ts.map +1 -1
- package/dist/utils/builtinSubagents.d.ts.map +1 -1
- package/dist/utils/builtinSubagents.js +59 -44
- package/dist/utils/cacheControlUtils.d.ts.map +1 -1
- package/dist/utils/cacheControlUtils.js +18 -12
- package/dist/utils/constants.d.ts +0 -4
- package/dist/utils/constants.d.ts.map +1 -1
- package/dist/utils/constants.js +0 -4
- package/dist/utils/convertMessagesForAPI.js +2 -2
- package/dist/utils/editUtils.d.ts.map +1 -1
- package/dist/utils/editUtils.js +2 -2
- package/dist/utils/gitUtils.d.ts +7 -0
- package/dist/utils/gitUtils.d.ts.map +1 -0
- package/dist/utils/gitUtils.js +24 -0
- package/dist/utils/messageOperations.d.ts +3 -58
- package/dist/utils/messageOperations.d.ts.map +1 -1
- package/dist/utils/messageOperations.js +4 -146
- package/dist/utils/nameGenerator.d.ts +1 -1
- package/dist/utils/nameGenerator.d.ts.map +1 -1
- package/dist/utils/nameGenerator.js +19 -3
- package/package.json +1 -1
- package/src/agent.ts +86 -183
- package/src/constants/subagents.ts +4 -0
- package/src/constants/tools.ts +4 -1
- package/src/index.ts +1 -0
- package/src/managers/aiManager.ts +63 -70
- package/src/managers/backgroundTaskManager.ts +58 -54
- package/src/managers/foregroundTaskManager.ts +3 -2
- package/src/managers/mcpManager.ts +6 -3
- package/src/managers/messageManager.ts +126 -142
- package/src/managers/permissionManager.ts +32 -21
- package/src/managers/planManager.ts +2 -2
- package/src/managers/reversionManager.ts +26 -2
- package/src/managers/slashCommandManager.ts +12 -3
- package/src/managers/subagentManager.ts +60 -144
- package/src/managers/toolManager.ts +32 -2
- package/src/prompts/index.ts +366 -0
- package/src/services/aiService.ts +3 -145
- package/src/services/memory.ts +0 -72
- package/src/services/session.ts +73 -0
- package/src/services/taskManager.ts +195 -0
- package/src/tools/askUserQuestion.ts +51 -29
- package/src/tools/bashTool.ts +15 -17
- package/src/tools/editTool.ts +3 -1
- package/src/tools/exitPlanMode.ts +27 -3
- package/src/tools/globTool.ts +10 -2
- package/src/tools/grepTool.ts +17 -6
- package/src/tools/lsTool.ts +3 -1
- package/src/tools/readTool.ts +17 -1
- package/src/tools/taskManagementTools.ts +516 -0
- package/src/tools/taskOutputTool.ts +34 -12
- package/src/tools/taskStopTool.ts +7 -1
- package/src/tools/taskTool.ts +45 -1
- package/src/tools/types.ts +12 -0
- package/src/tools/writeTool.ts +9 -2
- package/src/types/index.ts +1 -0
- package/src/types/messaging.ts +1 -21
- package/src/types/processes.ts +18 -7
- package/src/types/tasks.ts +13 -0
- package/src/types/tools.ts +4 -1
- package/src/utils/builtinSubagents.ts +81 -45
- package/src/utils/cacheControlUtils.ts +26 -18
- package/src/utils/constants.ts +0 -5
- package/src/utils/convertMessagesForAPI.ts +2 -2
- package/src/utils/editUtils.ts +2 -6
- package/src/utils/gitUtils.ts +24 -0
- package/src/utils/messageOperations.ts +6 -229
- package/src/utils/nameGenerator.ts +20 -3
- package/dist/constants/prompts.d.ts.map +0 -1
- package/dist/constants/prompts.js +0 -118
- package/dist/tools/todoWriteTool.d.ts +0 -6
- package/dist/tools/todoWriteTool.d.ts.map +0 -1
- package/dist/tools/todoWriteTool.js +0 -220
- package/src/constants/prompts.ts +0 -155
- package/src/tools/todoWriteTool.ts +0 -257
|
@@ -3,7 +3,6 @@ import {
|
|
|
3
3
|
compressMessages,
|
|
4
4
|
type CallAgentOptions,
|
|
5
5
|
} from "../services/aiService.js";
|
|
6
|
-
import { getMessagesToCompress } from "../utils/messageOperations.js";
|
|
7
6
|
import { convertMessagesForAPI } from "../utils/convertMessagesForAPI.js";
|
|
8
7
|
import { calculateComprehensiveTotalTokens } from "../utils/tokenCalculation.js";
|
|
9
8
|
import * as fs from "node:fs/promises";
|
|
@@ -21,10 +20,7 @@ import { ChatCompletionMessageFunctionToolCall } from "openai/resources.js";
|
|
|
21
20
|
import type { HookManager } from "./hookManager.js";
|
|
22
21
|
import type { ExtendedHookExecutionContext } from "../types/hooks.js";
|
|
23
22
|
import type { PermissionManager } from "./permissionManager.js";
|
|
24
|
-
import {
|
|
25
|
-
DEFAULT_SYSTEM_PROMPT,
|
|
26
|
-
buildSystemPrompt,
|
|
27
|
-
} from "../constants/prompts.js";
|
|
23
|
+
import { buildSystemPrompt } from "../prompts/index.js";
|
|
28
24
|
|
|
29
25
|
export interface AIManagerCallbacks {
|
|
30
26
|
onCompressionStateChange?: (isCompressing: boolean) => void;
|
|
@@ -34,6 +30,7 @@ export interface AIManagerCallbacks {
|
|
|
34
30
|
export interface AIManagerOptions {
|
|
35
31
|
messageManager: MessageManager;
|
|
36
32
|
toolManager: ToolManager;
|
|
33
|
+
taskManager: import("../services/taskManager.js").TaskManager;
|
|
37
34
|
logger?: Logger;
|
|
38
35
|
backgroundTaskManager?: BackgroundTaskManager;
|
|
39
36
|
hookManager?: HookManager;
|
|
@@ -60,6 +57,7 @@ export class AIManager {
|
|
|
60
57
|
private logger?: Logger;
|
|
61
58
|
private toolManager: ToolManager;
|
|
62
59
|
private messageManager: MessageManager;
|
|
60
|
+
private taskManager: import("../services/taskManager.js").TaskManager;
|
|
63
61
|
private backgroundTaskManager?: BackgroundTaskManager;
|
|
64
62
|
private hookManager?: HookManager;
|
|
65
63
|
private reversionManager?: import("./reversionManager.js").ReversionManager;
|
|
@@ -79,6 +77,7 @@ export class AIManager {
|
|
|
79
77
|
constructor(options: AIManagerOptions) {
|
|
80
78
|
this.messageManager = options.messageManager;
|
|
81
79
|
this.toolManager = options.toolManager;
|
|
80
|
+
this.taskManager = options.taskManager;
|
|
82
81
|
this.backgroundTaskManager = options.backgroundTaskManager;
|
|
83
82
|
this.hookManager = options.hookManager;
|
|
84
83
|
this.reversionManager = options.reversionManager;
|
|
@@ -159,15 +158,6 @@ export class AIManager {
|
|
|
159
158
|
this.setIsLoading(false);
|
|
160
159
|
}
|
|
161
160
|
|
|
162
|
-
/**
|
|
163
|
-
* Abort the AI recursion loop immediately.
|
|
164
|
-
* This is used when a tool is backgrounded via Ctrl-B, even if no foreground task was active.
|
|
165
|
-
*/
|
|
166
|
-
public abortRecursion(): void {
|
|
167
|
-
this.logger?.info("Aborting AI recursion loop");
|
|
168
|
-
this.abortAIMessage();
|
|
169
|
-
}
|
|
170
|
-
|
|
171
161
|
// Helper method to generate compactParams
|
|
172
162
|
private generateCompactParams(
|
|
173
163
|
toolName: string,
|
|
@@ -180,6 +170,7 @@ export class AIManager {
|
|
|
180
170
|
if (toolPlugin?.formatCompactParams) {
|
|
181
171
|
const context: ToolContext = {
|
|
182
172
|
workdir: this.workdir,
|
|
173
|
+
taskManager: this.taskManager,
|
|
183
174
|
};
|
|
184
175
|
return toolPlugin.formatCompactParams(toolArgs, context);
|
|
185
176
|
}
|
|
@@ -213,9 +204,7 @@ export class AIManager {
|
|
|
213
204
|
);
|
|
214
205
|
|
|
215
206
|
// Check if messages need compression
|
|
216
|
-
const
|
|
217
|
-
this.messageManager.getMessages(),
|
|
218
|
-
);
|
|
207
|
+
const messagesToCompress = this.messageManager.getMessages();
|
|
219
208
|
|
|
220
209
|
// If there are messages to compress, perform compression
|
|
221
210
|
if (messagesToCompress.length > 0) {
|
|
@@ -248,7 +237,6 @@ export class AIManager {
|
|
|
248
237
|
|
|
249
238
|
// Execute message reconstruction and sessionId update after compression
|
|
250
239
|
this.messageManager.compressMessagesAndUpdateSession(
|
|
251
|
-
insertIndex,
|
|
252
240
|
compressionResult.content,
|
|
253
241
|
compressionUsage,
|
|
254
242
|
);
|
|
@@ -305,6 +293,9 @@ export class AIManager {
|
|
|
305
293
|
return;
|
|
306
294
|
}
|
|
307
295
|
|
|
296
|
+
// Save session in each recursion to ensure message persistence
|
|
297
|
+
await this.messageManager.saveSession();
|
|
298
|
+
|
|
308
299
|
// Only create new AbortControllers for the initial call (recursionDepth === 0)
|
|
309
300
|
// For recursive calls, reuse existing controllers to maintain abort signal
|
|
310
301
|
let abortController: AbortController;
|
|
@@ -319,14 +310,10 @@ export class AIManager {
|
|
|
319
310
|
this.toolAbortController = toolAbortController;
|
|
320
311
|
} else {
|
|
321
312
|
// Reuse existing controllers for recursive calls
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
toolAbortController = this.toolAbortController || new AbortController();
|
|
313
|
+
abortController = this.abortController!;
|
|
314
|
+
toolAbortController = this.toolAbortController!;
|
|
325
315
|
}
|
|
326
316
|
|
|
327
|
-
// Save session in each recursion to ensure message persistence
|
|
328
|
-
await this.messageManager.saveSession();
|
|
329
|
-
|
|
330
317
|
// Only set loading state for the initial call
|
|
331
318
|
if (recursionDepth === 0) {
|
|
332
319
|
this.setIsLoading(true);
|
|
@@ -354,17 +341,14 @@ export class AIManager {
|
|
|
354
341
|
this.getModelConfig().permissionMode,
|
|
355
342
|
);
|
|
356
343
|
const toolsConfig = this.getFilteredToolsConfig(tools);
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
344
|
+
const toolNames = new Set(toolsConfig.map((t) => t.function.name));
|
|
345
|
+
const filteredToolPlugins = this.toolManager
|
|
346
|
+
.getTools()
|
|
347
|
+
.filter((t) => toolNames.has(t.name));
|
|
361
348
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
const languagePrompt = `\n\n# Language\nAlways respond in ${language}. Technical terms (e.g., code, tool names, file paths) should remain in their original language or English where appropriate.`;
|
|
366
|
-
effectiveSystemPrompt = (effectiveSystemPrompt || "") + languagePrompt;
|
|
367
|
-
}
|
|
349
|
+
let planModeOptions:
|
|
350
|
+
| { planFilePath: string; planExists: boolean }
|
|
351
|
+
| undefined;
|
|
368
352
|
|
|
369
353
|
if (currentMode === "plan") {
|
|
370
354
|
const planFilePath = this.permissionManager?.getPlanFilePath();
|
|
@@ -376,10 +360,7 @@ export class AIManager {
|
|
|
376
360
|
} catch {
|
|
377
361
|
planExists = false;
|
|
378
362
|
}
|
|
379
|
-
|
|
380
|
-
const reminder = `\n\nPlan mode is active. The user indicated that they do not want you to execute yet -- you MUST NOT make any edits (with the exception of the plan file mentioned below), run any non-readonly tools (including changing configs or making commits), or otherwise make any changes to the system. This supercedes any other instructions you have received.\n\n## Plan File Info:\n${planExists ? `A plan file already exists at ${planFilePath}. You can read it and make incremental edits using the Edit tool if you need to.` : `No plan file exists yet. You should create your plan at ${planFilePath} using the Write tool if you need to.`}\nYou should build your plan incrementally by writing to or editing this file. NOTE that this is the only file you are allowed to edit - other than this you are only allowed to take READ-ONLY actions. You may also use the AskUserQuestion tool to gather requirements or clarify intent before finalizing your plan.`;
|
|
381
|
-
|
|
382
|
-
effectiveSystemPrompt = (effectiveSystemPrompt || "") + reminder;
|
|
363
|
+
planModeOptions = { planFilePath, planExists };
|
|
383
364
|
}
|
|
384
365
|
}
|
|
385
366
|
|
|
@@ -390,11 +371,20 @@ export class AIManager {
|
|
|
390
371
|
messages: recentMessages,
|
|
391
372
|
sessionId: this.messageManager.getSessionId(),
|
|
392
373
|
abortSignal: abortController.signal,
|
|
393
|
-
memory: combinedMemory, // Pass combined memory content
|
|
394
374
|
workdir: this.workdir, // Pass working directory
|
|
395
375
|
tools: toolsConfig, // Pass filtered tool configuration
|
|
396
376
|
model: model, // Use passed model
|
|
397
|
-
systemPrompt:
|
|
377
|
+
systemPrompt: buildSystemPrompt(
|
|
378
|
+
this.systemPrompt,
|
|
379
|
+
filteredToolPlugins,
|
|
380
|
+
{
|
|
381
|
+
workdir: this.workdir,
|
|
382
|
+
memory: combinedMemory,
|
|
383
|
+
language: this.getLanguage(),
|
|
384
|
+
isSubagent: !!this.subagentType,
|
|
385
|
+
planMode: planModeOptions,
|
|
386
|
+
},
|
|
387
|
+
), // Pass custom system prompt
|
|
398
388
|
maxTokens: maxTokens, // Pass max tokens override
|
|
399
389
|
};
|
|
400
390
|
|
|
@@ -628,6 +618,15 @@ export class AIManager {
|
|
|
628
618
|
backgroundTaskManager: this.backgroundTaskManager,
|
|
629
619
|
workdir: this.workdir,
|
|
630
620
|
messageId: this.messageManager.getMessages().slice(-1)[0]?.id,
|
|
621
|
+
sessionId: this.messageManager.getSessionId(),
|
|
622
|
+
taskManager: this.taskManager,
|
|
623
|
+
onShortResultUpdate: (shortResult: string) => {
|
|
624
|
+
this.messageManager.updateToolBlock({
|
|
625
|
+
id: toolId,
|
|
626
|
+
shortResult,
|
|
627
|
+
stage: "running", // Keep it in running stage while updating shortResult
|
|
628
|
+
});
|
|
629
|
+
},
|
|
631
630
|
};
|
|
632
631
|
|
|
633
632
|
// Execute tool
|
|
@@ -637,23 +636,6 @@ export class AIManager {
|
|
|
637
636
|
context,
|
|
638
637
|
);
|
|
639
638
|
|
|
640
|
-
// Check if the tool was backgrounded via Ctrl-B
|
|
641
|
-
// If it was backgrounded, we should abort the AI recursion
|
|
642
|
-
if (
|
|
643
|
-
toolResult.success &&
|
|
644
|
-
toolResult.content.includes(
|
|
645
|
-
"Command was manually backgrounded by user",
|
|
646
|
-
)
|
|
647
|
-
) {
|
|
648
|
-
this.logger?.info(
|
|
649
|
-
`Tool ${toolName} was backgrounded via Ctrl-B, aborting AI recursion`,
|
|
650
|
-
);
|
|
651
|
-
// Use abortAIMessage directly instead of abortRecursion to avoid double logging
|
|
652
|
-
// and ensure we don't trigger the "Request was aborted" error block
|
|
653
|
-
this.abortAIMessage();
|
|
654
|
-
return;
|
|
655
|
-
}
|
|
656
|
-
|
|
657
639
|
// Update message state - tool execution completed
|
|
658
640
|
this.messageManager.updateToolBlock({
|
|
659
641
|
id: toolId,
|
|
@@ -666,6 +648,7 @@ export class AIManager {
|
|
|
666
648
|
stage: "end",
|
|
667
649
|
name: toolName,
|
|
668
650
|
shortResult: toolResult.shortResult,
|
|
651
|
+
isManuallyBackgrounded: toolResult.isManuallyBackgrounded,
|
|
669
652
|
});
|
|
670
653
|
|
|
671
654
|
// Execute PostToolUse hooks after successful tool completion
|
|
@@ -690,6 +673,7 @@ export class AIManager {
|
|
|
690
673
|
stage: "end",
|
|
691
674
|
name: toolName,
|
|
692
675
|
compactParams,
|
|
676
|
+
isManuallyBackgrounded: false,
|
|
693
677
|
});
|
|
694
678
|
}
|
|
695
679
|
},
|
|
@@ -721,7 +705,25 @@ export class AIManager {
|
|
|
721
705
|
const isCurrentlyAborted =
|
|
722
706
|
abortController.signal.aborted || toolAbortController.signal.aborted;
|
|
723
707
|
|
|
724
|
-
if
|
|
708
|
+
// Check if all tools were manually backgrounded
|
|
709
|
+
const lastMessage =
|
|
710
|
+
this.messageManager.getMessages()[
|
|
711
|
+
this.messageManager.getMessages().length - 1
|
|
712
|
+
];
|
|
713
|
+
const toolBlocks =
|
|
714
|
+
lastMessage?.blocks.filter(
|
|
715
|
+
(block): block is import("../types/messaging.js").ToolBlock =>
|
|
716
|
+
block.type === "tool",
|
|
717
|
+
) || [];
|
|
718
|
+
const hasBackgrounded =
|
|
719
|
+
toolBlocks.length > 0 &&
|
|
720
|
+
toolBlocks.some((block) => block.isManuallyBackgrounded);
|
|
721
|
+
|
|
722
|
+
if (hasBackgrounded) {
|
|
723
|
+
this.logger?.info(
|
|
724
|
+
"Some tools were manually backgrounded, stopping recursion.",
|
|
725
|
+
);
|
|
726
|
+
} else if (!isCurrentlyAborted) {
|
|
725
727
|
// Recursively call AI service, increment recursion depth, and pass same configuration
|
|
726
728
|
await this.sendAIMessage({
|
|
727
729
|
recursionDepth: recursionDepth + 1,
|
|
@@ -733,18 +735,9 @@ export class AIManager {
|
|
|
733
735
|
}
|
|
734
736
|
}
|
|
735
737
|
} catch (error) {
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
abortController.signal.aborted || toolAbortController.signal.aborted;
|
|
740
|
-
|
|
741
|
-
if (isCurrentlyAborted) {
|
|
742
|
-
this.logger?.info("AI message processing was aborted");
|
|
743
|
-
} else {
|
|
744
|
-
this.messageManager.addErrorBlock(
|
|
745
|
-
error instanceof Error ? error.message : "Unknown error occurred",
|
|
746
|
-
);
|
|
747
|
-
}
|
|
738
|
+
this.messageManager.addErrorBlock(
|
|
739
|
+
error instanceof Error ? error.message : "Unknown error occurred",
|
|
740
|
+
);
|
|
748
741
|
} finally {
|
|
749
742
|
// Only execute cleanup and hooks for the initial call
|
|
750
743
|
if (recursionDepth === 0) {
|
|
@@ -69,6 +69,26 @@ export class BackgroundTaskManager {
|
|
|
69
69
|
status: "running",
|
|
70
70
|
stdout: "",
|
|
71
71
|
stderr: "",
|
|
72
|
+
onStop: () => {
|
|
73
|
+
try {
|
|
74
|
+
if (child.pid) {
|
|
75
|
+
process.kill(-child.pid, "SIGTERM");
|
|
76
|
+
setTimeout(() => {
|
|
77
|
+
if (child.pid && !child.killed) {
|
|
78
|
+
try {
|
|
79
|
+
process.kill(-child.pid, "SIGKILL");
|
|
80
|
+
} catch (error) {
|
|
81
|
+
logger.error("Failed to force kill process:", error);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}, 1000);
|
|
85
|
+
} else {
|
|
86
|
+
child.kill("SIGTERM");
|
|
87
|
+
}
|
|
88
|
+
} catch {
|
|
89
|
+
child.kill("SIGTERM");
|
|
90
|
+
}
|
|
91
|
+
},
|
|
72
92
|
};
|
|
73
93
|
|
|
74
94
|
this.tasks.set(id, shell);
|
|
@@ -155,6 +175,26 @@ export class BackgroundTaskManager {
|
|
|
155
175
|
status: "running",
|
|
156
176
|
stdout: initialStdout,
|
|
157
177
|
stderr: initialStderr,
|
|
178
|
+
onStop: () => {
|
|
179
|
+
try {
|
|
180
|
+
if (child.pid) {
|
|
181
|
+
process.kill(-child.pid, "SIGTERM");
|
|
182
|
+
setTimeout(() => {
|
|
183
|
+
if (child.pid && !child.killed) {
|
|
184
|
+
try {
|
|
185
|
+
process.kill(-child.pid, "SIGKILL");
|
|
186
|
+
} catch (error) {
|
|
187
|
+
logger.error("Failed to force kill process:", error);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}, 1000);
|
|
191
|
+
} else {
|
|
192
|
+
child.kill("SIGTERM");
|
|
193
|
+
}
|
|
194
|
+
} catch {
|
|
195
|
+
child.kill("SIGTERM");
|
|
196
|
+
}
|
|
197
|
+
},
|
|
158
198
|
};
|
|
159
199
|
|
|
160
200
|
this.tasks.set(id, shell);
|
|
@@ -232,65 +272,29 @@ export class BackgroundTaskManager {
|
|
|
232
272
|
return false;
|
|
233
273
|
}
|
|
234
274
|
|
|
235
|
-
if (task.
|
|
236
|
-
const shell = task as BackgroundShell;
|
|
275
|
+
if (task.onStop) {
|
|
237
276
|
try {
|
|
238
|
-
|
|
239
|
-
if (
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
setTimeout(() => {
|
|
244
|
-
if (
|
|
245
|
-
shell.status === "running" &&
|
|
246
|
-
shell.process.pid &&
|
|
247
|
-
!shell.process.killed
|
|
248
|
-
) {
|
|
249
|
-
try {
|
|
250
|
-
process.kill(-shell.process.pid, "SIGKILL");
|
|
251
|
-
} catch (error) {
|
|
252
|
-
logger.error("Failed to force kill process:", error);
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
}, 1000);
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
shell.status = "killed";
|
|
259
|
-
shell.endTime = Date.now();
|
|
260
|
-
shell.runtime = shell.endTime - shell.startTime;
|
|
261
|
-
this.notifyTasksChange();
|
|
262
|
-
return true;
|
|
263
|
-
} catch {
|
|
264
|
-
// Fallback to direct process kill
|
|
265
|
-
try {
|
|
266
|
-
shell.process.kill("SIGTERM");
|
|
267
|
-
setTimeout(() => {
|
|
268
|
-
if (!shell.process.killed) {
|
|
269
|
-
shell.process.kill("SIGKILL");
|
|
270
|
-
}
|
|
271
|
-
}, 1000);
|
|
272
|
-
shell.status = "killed";
|
|
273
|
-
shell.endTime = Date.now();
|
|
274
|
-
shell.runtime = shell.endTime - shell.startTime;
|
|
275
|
-
this.notifyTasksChange();
|
|
276
|
-
return true;
|
|
277
|
-
} catch (directKillError) {
|
|
278
|
-
logger.error("Failed to kill child process:", directKillError);
|
|
279
|
-
return false;
|
|
277
|
+
const result = task.onStop();
|
|
278
|
+
if (result instanceof Promise) {
|
|
279
|
+
result.catch((error) => {
|
|
280
|
+
logger.error("Error in background task onStop callback:", error);
|
|
281
|
+
});
|
|
280
282
|
}
|
|
283
|
+
} catch (error) {
|
|
284
|
+
logger.error("Error in background task onStop callback:", error);
|
|
281
285
|
}
|
|
282
|
-
} else if (task.type === "subagent") {
|
|
283
|
-
// Subagent termination logic will be handled by aborting the AI loop
|
|
284
|
-
// which is already managed by the SubagentManager and AIManager.
|
|
285
|
-
// Here we just update the status.
|
|
286
|
-
task.status = "killed";
|
|
287
|
-
task.endTime = Date.now();
|
|
288
|
-
task.runtime = task.endTime - task.startTime;
|
|
289
|
-
this.notifyTasksChange();
|
|
290
|
-
return true;
|
|
291
286
|
}
|
|
292
287
|
|
|
293
|
-
|
|
288
|
+
// If it's a subagent task, we should also notify the subagent manager to cleanup
|
|
289
|
+
// However, to avoid circular dependency, we rely on the onStop callback
|
|
290
|
+
// which is already set to instance.aiManager.abortAIMessage()
|
|
291
|
+
// The subagentManager.cleanupInstance will be called by the tool or by status change.
|
|
292
|
+
|
|
293
|
+
task.status = "killed";
|
|
294
|
+
task.endTime = Date.now();
|
|
295
|
+
task.runtime = task.endTime - task.startTime;
|
|
296
|
+
this.notifyTasksChange();
|
|
297
|
+
return true;
|
|
294
298
|
}
|
|
295
299
|
|
|
296
300
|
public cleanup(): void {
|
|
@@ -14,8 +14,9 @@ export class ForegroundTaskManager implements IForegroundTaskManager {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
public async backgroundCurrentTask(): Promise<void> {
|
|
17
|
-
const
|
|
18
|
-
|
|
17
|
+
const tasks = [...this.activeForegroundTasks].reverse();
|
|
18
|
+
this.activeForegroundTasks = [];
|
|
19
|
+
for (const task of tasks) {
|
|
19
20
|
await task.backgroundHandler();
|
|
20
21
|
}
|
|
21
22
|
}
|
|
@@ -3,7 +3,7 @@ import { join } from "path";
|
|
|
3
3
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
4
4
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
5
5
|
import { ChatCompletionFunctionTool } from "openai/resources.js";
|
|
6
|
-
import { createMcpToolPlugin, findToolServer } from "
|
|
6
|
+
import { createMcpToolPlugin, findToolServer } from "../utils/mcpUtils.js";
|
|
7
7
|
import type { ToolPlugin, ToolResult, ToolContext } from "../tools/types.js";
|
|
8
8
|
import type {
|
|
9
9
|
Logger,
|
|
@@ -476,8 +476,11 @@ export class McpManager {
|
|
|
476
476
|
const server = findToolServer(tool.name, servers);
|
|
477
477
|
|
|
478
478
|
if (server) {
|
|
479
|
-
const plugin = createMcpToolPlugin(
|
|
480
|
-
|
|
479
|
+
const plugin = createMcpToolPlugin(
|
|
480
|
+
tool,
|
|
481
|
+
server.name,
|
|
482
|
+
(name: string, args: Record<string, unknown>) =>
|
|
483
|
+
this.executeMcpTool(name, args),
|
|
481
484
|
);
|
|
482
485
|
mcpTools.set(plugin.name, plugin);
|
|
483
486
|
}
|