wave-agent-sdk 0.6.1 → 0.6.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/dist/agent.d.ts +0 -5
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +3 -67
- 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/managers/aiManager.d.ts.map +1 -1
- package/dist/managers/aiManager.js +9 -25
- package/dist/managers/backgroundTaskManager.d.ts.map +1 -1
- package/dist/managers/backgroundTaskManager.js +4 -0
- package/dist/managers/messageManager.d.ts +0 -18
- package/dist/managers/messageManager.d.ts.map +1 -1
- package/dist/managers/messageManager.js +1 -36
- package/dist/managers/reversionManager.d.ts.map +1 -1
- package/dist/managers/reversionManager.js +23 -2
- package/dist/managers/slashCommandManager.js +1 -1
- package/dist/managers/subagentManager.d.ts +4 -14
- package/dist/managers/subagentManager.d.ts.map +1 -1
- package/dist/managers/subagentManager.js +35 -109
- package/dist/{constants/prompts.d.ts → prompts/index.d.ts} +7 -8
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/{constants/prompts.js → prompts/index.js} +127 -11
- package/dist/services/aiService.js +1 -1
- package/dist/services/taskManager.d.ts +5 -1
- package/dist/services/taskManager.d.ts.map +1 -1
- package/dist/services/taskManager.js +6 -0
- package/dist/tools/bashTool.d.ts.map +1 -1
- package/dist/tools/bashTool.js +34 -29
- package/dist/tools/exitPlanMode.js +1 -1
- package/dist/tools/taskManagementTools.d.ts.map +1 -1
- package/dist/tools/taskManagementTools.js +8 -0
- package/dist/tools/taskTool.d.ts.map +1 -1
- package/dist/tools/taskTool.js +32 -2
- package/dist/tools/types.d.ts +2 -0
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/types/messaging.d.ts +1 -11
- package/dist/types/messaging.d.ts.map +1 -1
- package/dist/types/processes.d.ts +5 -0
- package/dist/types/processes.d.ts.map +1 -1
- package/dist/utils/builtinSubagents.d.ts.map +1 -1
- package/dist/utils/builtinSubagents.js +28 -50
- 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 +1 -23
- package/dist/utils/messageOperations.d.ts.map +1 -1
- package/dist/utils/messageOperations.js +0 -49
- package/package.json +1 -1
- package/src/agent.ts +3 -89
- package/src/constants/subagents.ts +4 -0
- package/src/managers/aiManager.ts +9 -25
- package/src/managers/backgroundTaskManager.ts +5 -0
- package/src/managers/messageManager.ts +0 -80
- package/src/managers/reversionManager.ts +26 -2
- package/src/managers/slashCommandManager.ts +1 -1
- package/src/managers/subagentManager.ts +42 -145
- package/src/{constants/prompts.ts → prompts/index.ts} +136 -13
- package/src/services/aiService.ts +1 -1
- package/src/services/taskManager.ts +8 -1
- package/src/tools/bashTool.ts +35 -30
- package/src/tools/exitPlanMode.ts +1 -1
- package/src/tools/taskManagementTools.ts +18 -0
- package/src/tools/taskTool.ts +39 -1
- package/src/tools/types.ts +2 -0
- package/src/types/messaging.ts +0 -12
- package/src/types/processes.ts +5 -0
- package/src/utils/builtinSubagents.ts +41 -51
- package/src/utils/editUtils.ts +2 -6
- package/src/utils/gitUtils.ts +24 -0
- package/src/utils/messageOperations.ts +1 -93
- package/dist/constants/prompts.d.ts.map +0 -1
|
@@ -8,20 +8,19 @@ import type {
|
|
|
8
8
|
ModelConfig,
|
|
9
9
|
Usage,
|
|
10
10
|
} from "../types/index.js";
|
|
11
|
-
import type { SessionData } from "../services/session.js";
|
|
12
11
|
import { AIManager } from "./aiManager.js";
|
|
13
12
|
import { MessageManager } from "./messageManager.js";
|
|
14
13
|
import { ToolManager } from "./toolManager.js";
|
|
15
14
|
import { HookManager } from "./hookManager.js";
|
|
16
|
-
import {
|
|
17
|
-
UserMessageParams,
|
|
18
|
-
type AgentToolBlockUpdateParams,
|
|
19
|
-
} from "../utils/messageOperations.js";
|
|
20
15
|
import {
|
|
21
16
|
addConsolidatedAbortListener,
|
|
22
17
|
createAbortPromise,
|
|
23
18
|
} from "../utils/abortUtils.js";
|
|
24
19
|
import { BackgroundTaskManager } from "./backgroundTaskManager.js";
|
|
20
|
+
import {
|
|
21
|
+
UserMessageParams,
|
|
22
|
+
type AgentToolBlockUpdateParams,
|
|
23
|
+
} from "../utils/messageOperations.js";
|
|
25
24
|
|
|
26
25
|
export interface SubagentManagerCallbacks {
|
|
27
26
|
// Granular subagent message callbacks (015-subagent-message-callbacks)
|
|
@@ -66,14 +65,15 @@ export interface SubagentInstance {
|
|
|
66
65
|
toolManager: ToolManager;
|
|
67
66
|
status: "initializing" | "active" | "completed" | "error" | "aborted";
|
|
68
67
|
messages: Message[];
|
|
68
|
+
lastTools: string[]; // Track last two tool names
|
|
69
69
|
subagentType: string; // Store the subagent type for hook context
|
|
70
70
|
backgroundTaskId?: string; // ID of the background task if transitioned
|
|
71
|
+
onUpdate?: () => void; // Optional callback for real-time updates
|
|
71
72
|
}
|
|
72
73
|
|
|
73
74
|
export interface SubagentManagerOptions {
|
|
74
75
|
workdir: string;
|
|
75
76
|
parentToolManager: ToolManager;
|
|
76
|
-
parentMessageManager: MessageManager;
|
|
77
77
|
taskManager: import("../services/taskManager.js").TaskManager;
|
|
78
78
|
callbacks?: SubagentManagerCallbacks; // Use SubagentManagerCallbacks instead of parentCallbacks
|
|
79
79
|
logger?: Logger;
|
|
@@ -93,7 +93,6 @@ export class SubagentManager {
|
|
|
93
93
|
|
|
94
94
|
private workdir: string;
|
|
95
95
|
private parentToolManager: ToolManager;
|
|
96
|
-
private parentMessageManager: MessageManager;
|
|
97
96
|
private taskManager: import("../services/taskManager.js").TaskManager;
|
|
98
97
|
private callbacks?: SubagentManagerCallbacks; // Use SubagentManagerCallbacks instead of parentCallbacks
|
|
99
98
|
private logger?: Logger;
|
|
@@ -109,7 +108,6 @@ export class SubagentManager {
|
|
|
109
108
|
constructor(options: SubagentManagerOptions) {
|
|
110
109
|
this.workdir = options.workdir;
|
|
111
110
|
this.parentToolManager = options.parentToolManager;
|
|
112
|
-
this.parentMessageManager = options.parentMessageManager;
|
|
113
111
|
this.taskManager = options.taskManager;
|
|
114
112
|
this.callbacks = options.callbacks; // Store SubagentManagerCallbacks
|
|
115
113
|
this.logger = options.logger;
|
|
@@ -176,6 +174,7 @@ export class SubagentManager {
|
|
|
176
174
|
subagent_type: string;
|
|
177
175
|
},
|
|
178
176
|
runInBackground?: boolean,
|
|
177
|
+
onUpdate?: () => void,
|
|
179
178
|
): Promise<SubagentInstance> {
|
|
180
179
|
if (!this.parentToolManager) {
|
|
181
180
|
throw new Error(
|
|
@@ -248,22 +247,13 @@ export class SubagentManager {
|
|
|
248
247
|
toolManager,
|
|
249
248
|
status: "initializing",
|
|
250
249
|
messages: [],
|
|
250
|
+
lastTools: [], // Initialize lastTools
|
|
251
251
|
subagentType: parameters.subagent_type, // Store the subagent type
|
|
252
|
+
onUpdate,
|
|
252
253
|
};
|
|
253
254
|
|
|
254
255
|
this.instances.set(subagentId, instance);
|
|
255
256
|
|
|
256
|
-
// Create subagent block in parent message manager
|
|
257
|
-
this.parentMessageManager.addSubagentBlock(
|
|
258
|
-
subagentId,
|
|
259
|
-
configuration.name,
|
|
260
|
-
messageManager.getSessionId(),
|
|
261
|
-
configuration,
|
|
262
|
-
"active",
|
|
263
|
-
parameters,
|
|
264
|
-
runInBackground,
|
|
265
|
-
);
|
|
266
|
-
|
|
267
257
|
return instance;
|
|
268
258
|
}
|
|
269
259
|
|
|
@@ -287,9 +277,6 @@ export class SubagentManager {
|
|
|
287
277
|
|
|
288
278
|
// Set status to active and update parent
|
|
289
279
|
this.updateInstanceStatus(instance.subagentId, "active");
|
|
290
|
-
this.parentMessageManager.updateSubagentBlock(instance.subagentId, {
|
|
291
|
-
status: "active",
|
|
292
|
-
});
|
|
293
280
|
|
|
294
281
|
if (runInBackground && this.backgroundTaskManager) {
|
|
295
282
|
const taskId = this.backgroundTaskManager.generateId();
|
|
@@ -303,7 +290,11 @@ export class SubagentManager {
|
|
|
303
290
|
description: instance.configuration.description,
|
|
304
291
|
stdout: "",
|
|
305
292
|
stderr: "",
|
|
306
|
-
|
|
293
|
+
subagentId: instance.subagentId,
|
|
294
|
+
onStop: () => {
|
|
295
|
+
instance.aiManager.abortAIMessage();
|
|
296
|
+
this.cleanupInstance(instance.subagentId);
|
|
297
|
+
},
|
|
307
298
|
});
|
|
308
299
|
|
|
309
300
|
instance.backgroundTaskId = taskId;
|
|
@@ -341,9 +332,6 @@ export class SubagentManager {
|
|
|
341
332
|
return await this.internalExecute(instance, prompt, abortSignal);
|
|
342
333
|
} catch (error) {
|
|
343
334
|
this.updateInstanceStatus(instance.subagentId, "error");
|
|
344
|
-
this.parentMessageManager.updateSubagentBlock(instance.subagentId, {
|
|
345
|
-
status: "error",
|
|
346
|
-
});
|
|
347
335
|
throw error;
|
|
348
336
|
}
|
|
349
337
|
}
|
|
@@ -369,16 +357,15 @@ export class SubagentManager {
|
|
|
369
357
|
description: instance.configuration.description,
|
|
370
358
|
stdout: "",
|
|
371
359
|
stderr: "",
|
|
372
|
-
|
|
360
|
+
subagentId: instance.subagentId,
|
|
361
|
+
onStop: () => {
|
|
362
|
+
instance.aiManager.abortAIMessage();
|
|
363
|
+
this.cleanupInstance(instance.subagentId);
|
|
364
|
+
},
|
|
373
365
|
});
|
|
374
366
|
|
|
375
367
|
instance.backgroundTaskId = taskId;
|
|
376
368
|
|
|
377
|
-
// Update parent message manager to reflect background status
|
|
378
|
-
this.parentMessageManager.updateSubagentBlock(subagentId, {
|
|
379
|
-
runInBackground: true,
|
|
380
|
-
});
|
|
381
|
-
|
|
382
369
|
return taskId;
|
|
383
370
|
}
|
|
384
371
|
|
|
@@ -395,9 +382,8 @@ export class SubagentManager {
|
|
|
395
382
|
() => {
|
|
396
383
|
// Update status to aborted
|
|
397
384
|
this.updateInstanceStatus(instance.subagentId, "aborted");
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
});
|
|
385
|
+
// Cleanup instance immediately on abort
|
|
386
|
+
this.cleanupInstance(instance.subagentId);
|
|
401
387
|
},
|
|
402
388
|
() => {
|
|
403
389
|
// Abort the AI execution
|
|
@@ -474,9 +460,6 @@ export class SubagentManager {
|
|
|
474
460
|
|
|
475
461
|
// Update status to completed and update parent
|
|
476
462
|
this.updateInstanceStatus(instance.subagentId, "completed");
|
|
477
|
-
this.parentMessageManager.updateSubagentBlock(instance.subagentId, {
|
|
478
|
-
status: "completed",
|
|
479
|
-
});
|
|
480
463
|
|
|
481
464
|
// If this was transitioned to background, update the background task
|
|
482
465
|
if (instance.backgroundTaskId && this.backgroundTaskManager) {
|
|
@@ -580,99 +563,6 @@ export class SubagentManager {
|
|
|
580
563
|
this.instances.clear();
|
|
581
564
|
}
|
|
582
565
|
|
|
583
|
-
/**
|
|
584
|
-
* Restore subagent instances from saved session data
|
|
585
|
-
* This method is called during agent initialization to restore previous subagent states
|
|
586
|
-
*/
|
|
587
|
-
async restoreSubagentSessions(
|
|
588
|
-
subagentSessions: Array<{
|
|
589
|
-
sessionData: SessionData;
|
|
590
|
-
subagentId: string;
|
|
591
|
-
configuration: SubagentConfiguration;
|
|
592
|
-
}>,
|
|
593
|
-
): Promise<void> {
|
|
594
|
-
for (const { sessionData, subagentId, configuration } of subagentSessions) {
|
|
595
|
-
try {
|
|
596
|
-
// Use the configuration from the SubagentBlock
|
|
597
|
-
|
|
598
|
-
// Create the subagent instance without executing - just restore the state
|
|
599
|
-
|
|
600
|
-
// Create MessageManager for the restored subagent
|
|
601
|
-
const subagentCallbacks = this.createSubagentCallbacks(subagentId);
|
|
602
|
-
const messageManager = new MessageManager({
|
|
603
|
-
callbacks: subagentCallbacks,
|
|
604
|
-
workdir: this.workdir,
|
|
605
|
-
logger: this.logger,
|
|
606
|
-
sessionType: "subagent",
|
|
607
|
-
subagentType: configuration.name, // Use configuration name for restored sessions
|
|
608
|
-
memoryRuleManager: this.memoryRuleManager,
|
|
609
|
-
});
|
|
610
|
-
|
|
611
|
-
// Use the parent tool manager
|
|
612
|
-
const toolManager = this.parentToolManager;
|
|
613
|
-
|
|
614
|
-
// Determine model to use
|
|
615
|
-
let modelToUse: string;
|
|
616
|
-
const parentModelConfig = this.getModelConfig();
|
|
617
|
-
|
|
618
|
-
if (!configuration.model || configuration.model === "inherit") {
|
|
619
|
-
modelToUse = parentModelConfig.agentModel;
|
|
620
|
-
} else if (configuration.model === "fastModel") {
|
|
621
|
-
modelToUse = parentModelConfig.fastModel;
|
|
622
|
-
} else {
|
|
623
|
-
modelToUse = configuration.model;
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
// Create AIManager for the restored subagent
|
|
627
|
-
const aiManager = new AIManager({
|
|
628
|
-
messageManager,
|
|
629
|
-
toolManager,
|
|
630
|
-
taskManager: this.taskManager,
|
|
631
|
-
logger: this.logger,
|
|
632
|
-
workdir: this.workdir,
|
|
633
|
-
systemPrompt: configuration.systemPrompt,
|
|
634
|
-
subagentType: configuration.name, // Use configuration name as subagent type for restored instances
|
|
635
|
-
hookManager: this.hookManager,
|
|
636
|
-
permissionManager: this.parentToolManager.getPermissionManager(),
|
|
637
|
-
getGatewayConfig: this.getGatewayConfig,
|
|
638
|
-
getModelConfig: () => ({
|
|
639
|
-
...parentModelConfig,
|
|
640
|
-
agentModel: modelToUse,
|
|
641
|
-
}),
|
|
642
|
-
getMaxInputTokens: this.getMaxInputTokens,
|
|
643
|
-
getLanguage: this.getLanguage,
|
|
644
|
-
callbacks: {
|
|
645
|
-
onUsageAdded: this.onUsageAdded,
|
|
646
|
-
},
|
|
647
|
-
});
|
|
648
|
-
|
|
649
|
-
// Create restored instance
|
|
650
|
-
const instance: SubagentInstance = {
|
|
651
|
-
subagentId,
|
|
652
|
-
configuration,
|
|
653
|
-
aiManager,
|
|
654
|
-
messageManager,
|
|
655
|
-
toolManager,
|
|
656
|
-
status: "completed", // Restored sessions are considered completed
|
|
657
|
-
messages: sessionData.messages,
|
|
658
|
-
subagentType: configuration.name, // Use configuration name as subagent type for restored instances
|
|
659
|
-
};
|
|
660
|
-
|
|
661
|
-
// IMPORTANT: Store instance in map BEFORE calling setMessages
|
|
662
|
-
// This ensures the callback can find the instance
|
|
663
|
-
this.instances.set(subagentId, instance);
|
|
664
|
-
|
|
665
|
-
messageManager.initializeFromSession(sessionData);
|
|
666
|
-
} catch (error) {
|
|
667
|
-
this.logger?.warn(
|
|
668
|
-
`Failed to restore subagent session ${subagentId}:`,
|
|
669
|
-
error,
|
|
670
|
-
);
|
|
671
|
-
// Continue with other sessions even if one fails
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
|
|
676
566
|
/**
|
|
677
567
|
* Create subagent callbacks for a specific subagent ID
|
|
678
568
|
* Extracted to reuse in both create and restore flows
|
|
@@ -715,6 +605,20 @@ export class SubagentManager {
|
|
|
715
605
|
},
|
|
716
606
|
|
|
717
607
|
onToolBlockUpdated: (params: AgentToolBlockUpdateParams) => {
|
|
608
|
+
// Track last two tool names when a tool starts running
|
|
609
|
+
if (params.stage === "running" && params.name) {
|
|
610
|
+
const instance = this.instances.get(subagentId);
|
|
611
|
+
if (instance) {
|
|
612
|
+
// Add to lastTools if it's different from the last one or we want to show duplicates
|
|
613
|
+
// Based on "ToolA, ToolB" requirement, we'll just keep the last two
|
|
614
|
+
instance.lastTools.push(params.name);
|
|
615
|
+
if (instance.lastTools.length > 2) {
|
|
616
|
+
instance.lastTools.shift();
|
|
617
|
+
}
|
|
618
|
+
instance.onUpdate?.();
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
718
622
|
// Forward tool block updates to parent via SubagentManager callbacks
|
|
719
623
|
if (this.callbacks?.onSubagentToolBlockUpdated) {
|
|
720
624
|
this.callbacks.onSubagentToolBlockUpdated(subagentId, params);
|
|
@@ -726,6 +630,8 @@ export class SubagentManager {
|
|
|
726
630
|
const instance = this.instances.get(subagentId);
|
|
727
631
|
if (instance) {
|
|
728
632
|
instance.messages = messages;
|
|
633
|
+
// Trigger the onUpdate callback if provided
|
|
634
|
+
instance.onUpdate?.();
|
|
729
635
|
// Forward subagent message changes to parent via callbacks
|
|
730
636
|
if (this.callbacks?.onSubagentMessagesChange) {
|
|
731
637
|
this.callbacks.onSubagentMessagesChange(subagentId, messages);
|
|
@@ -733,26 +639,17 @@ export class SubagentManager {
|
|
|
733
639
|
}
|
|
734
640
|
},
|
|
735
641
|
|
|
736
|
-
onSessionIdChange: (newSessionId: string) => {
|
|
737
|
-
// Update the subagent block with the new session ID
|
|
738
|
-
this.parentMessageManager.updateSubagentBlock(subagentId, {
|
|
739
|
-
sessionId: newSessionId,
|
|
740
|
-
});
|
|
741
|
-
},
|
|
742
|
-
|
|
743
642
|
onLatestTotalTokensChange: (tokens: number) => {
|
|
643
|
+
const instance = this.instances.get(subagentId);
|
|
644
|
+
if (instance) {
|
|
645
|
+
// Trigger the onUpdate callback if provided
|
|
646
|
+
instance.onUpdate?.();
|
|
647
|
+
}
|
|
744
648
|
// Forward latest total tokens to parent via SubagentManager callbacks
|
|
745
649
|
if (this.callbacks?.onSubagentLatestTotalTokensChange) {
|
|
746
650
|
this.callbacks.onSubagentLatestTotalTokensChange(subagentId, tokens);
|
|
747
651
|
}
|
|
748
652
|
},
|
|
749
|
-
|
|
750
|
-
onFileHistoryBlockAdded: (
|
|
751
|
-
snapshots: import("../types/reversion.js").FileSnapshot[],
|
|
752
|
-
) => {
|
|
753
|
-
// Forward file history blocks to parent MessageManager
|
|
754
|
-
this.parentMessageManager.addFileHistoryBlock(snapshots);
|
|
755
|
-
},
|
|
756
653
|
};
|
|
757
654
|
}
|
|
758
655
|
}
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
+
import * as os from "node:os";
|
|
1
2
|
import { ToolPlugin } from "../tools/types.js";
|
|
3
|
+
import { isGitRepository } from "../utils/gitUtils.js";
|
|
4
|
+
import {
|
|
5
|
+
EXPLORE_SUBAGENT_TYPE,
|
|
6
|
+
PLAN_SUBAGENT_TYPE,
|
|
7
|
+
} from "../constants/subagents.js";
|
|
2
8
|
import {
|
|
3
9
|
ASK_USER_QUESTION_TOOL_NAME,
|
|
4
10
|
BASH_TOOL_NAME,
|
|
@@ -11,7 +17,8 @@ import {
|
|
|
11
17
|
TASK_UPDATE_TOOL_NAME,
|
|
12
18
|
TASK_LIST_TOOL_NAME,
|
|
13
19
|
WRITE_TOOL_NAME,
|
|
14
|
-
|
|
20
|
+
EXIT_PLAN_MODE_TOOL_NAME,
|
|
21
|
+
} from "../constants/tools.js";
|
|
15
22
|
|
|
16
23
|
export const BASE_SYSTEM_PROMPT = `You are an interactive CLI tool that helps users with software engineering tasks. Use the instructions below and the tools available to you to assist the user.
|
|
17
24
|
|
|
@@ -23,8 +30,9 @@ The user will primarily request you perform software engineering tasks. This inc
|
|
|
23
30
|
- Don't add features, refactor code, or make "improvements" beyond what was asked. A bug fix doesn't need surrounding code cleaned up. A simple feature doesn't need extra configurability. Don't add docstrings, comments, or type annotations to code you didn't change. Only add comments where the logic isn't self-evident.
|
|
24
31
|
- Don't add error handling, fallbacks, or validation for scenarios that can't happen. Trust internal code and framework guarantees. Only validate at system boundaries (user input, external APIs). Don't use feature flags or backwards-compatibility shims when you can just change the code.
|
|
25
32
|
- Don't create helpers, utilities, or abstractions for one-time operations. Don't design for hypothetical future requirements. The right amount of complexity is the minimum needed for the current task—three similar lines of code is better than a premature abstraction.
|
|
26
|
-
- Avoid backwards-compatibility hacks like renaming unused \`_vars\`, re-exporting types, adding \`// removed\` comments for removed code, etc. If something is unused, delete it completely
|
|
33
|
+
- Avoid backwards-compatibility hacks like renaming unused \`_vars\`, re-exporting types, adding \`// removed\` comments for removed code, etc. If something is unused, delete it completely.`;
|
|
27
34
|
|
|
35
|
+
export const TOOL_POLICY = `
|
|
28
36
|
# Tool usage policy
|
|
29
37
|
- You can call multiple tools in a single response. If you intend to call multiple tools and there are no dependencies between them, make all independent tool calls in parallel. Maximize use of parallel tool calls where possible to increase efficiency.
|
|
30
38
|
- However, if some tool calls depend on previous calls to inform dependent values, do NOT call these tools in parallel and instead call them sequentially. For instance, if one operation must complete before another starts, run these operations sequentially instead. Never use placeholders or guess missing parameters in tool calls.
|
|
@@ -39,17 +47,90 @@ It is critical that you mark tasks as completed as soon as you are done with a t
|
|
|
39
47
|
export function buildPlanModePrompt(
|
|
40
48
|
planFilePath: string,
|
|
41
49
|
planExists: boolean,
|
|
50
|
+
isSubagent: boolean = false,
|
|
42
51
|
): string {
|
|
43
52
|
const planFileInfo = planExists
|
|
44
53
|
? `A plan file already exists at ${planFilePath}. You can read it and make incremental edits using the ${EDIT_TOOL_NAME} tool if you need to.`
|
|
45
54
|
: `No plan file exists yet. You should create your plan at ${planFilePath} using the ${WRITE_TOOL_NAME} tool if you need to.`;
|
|
46
55
|
|
|
47
|
-
|
|
56
|
+
if (isSubagent) {
|
|
57
|
+
return `Plan mode is active. The user indicated that they do not want you to execute yet -- you MUST NOT make any edits, 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 (for example, to make edits). Instead, you should:
|
|
58
|
+
|
|
59
|
+
## Plan File Info:
|
|
60
|
+
${planFileInfo}
|
|
61
|
+
You 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.
|
|
62
|
+
Answer the user's query comprehensively, using the ${ASK_USER_QUESTION_TOOL_NAME} tool if you need to ask the user clarifying questions. If you do use the ${ASK_USER_QUESTION_TOOL_NAME}, make sure to ask all clarifying questions you need to fully understand the user's intent before proceeding.`;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return `Plan 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.
|
|
48
66
|
|
|
49
67
|
## Plan File Info:
|
|
50
68
|
${planFileInfo}
|
|
51
69
|
You 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.
|
|
52
|
-
|
|
70
|
+
|
|
71
|
+
## Plan Workflow
|
|
72
|
+
|
|
73
|
+
### Phase 1: Initial Understanding
|
|
74
|
+
Goal: Gain a comprehensive understanding of the user's request by reading through code and asking them questions. Critical: In this phase you should only use the Task subagent type with subagent_type=${EXPLORE_SUBAGENT_TYPE}.
|
|
75
|
+
|
|
76
|
+
1. Focus on understanding the user's request and the code associated with their request. Actively search for existing functions, utilities, and patterns that can be reused — avoid proposing new code when suitable implementations already exist.
|
|
77
|
+
|
|
78
|
+
2. **Launch up to 3 ${EXPLORE_SUBAGENT_TYPE} agents IN PARALLEL** (single message, multiple tool calls) to efficiently explore the codebase.
|
|
79
|
+
- Use 1 agent when the task is isolated to known files, the user provided specific file paths, or you're making a small targeted change.
|
|
80
|
+
- Use multiple agents when: the scope is uncertain, multiple areas of the codebase are involved, or you need to understand existing patterns before planning.
|
|
81
|
+
- Quality over quantity - 3 agents maximum, but you should try to use the minimum number of agents necessary (usually just 1)
|
|
82
|
+
- If using multiple agents: Provide each agent with a specific search focus or area to explore. Example: One agent searches for existing implementations, another explores related components, a third investigating testing patterns
|
|
83
|
+
|
|
84
|
+
### Phase 2: Design
|
|
85
|
+
Goal: Design an implementation approach.
|
|
86
|
+
|
|
87
|
+
Launch Task agent(s) with subagent_type=${PLAN_SUBAGENT_TYPE} to design the implementation based on the user's intent and your exploration results from Phase 1.
|
|
88
|
+
|
|
89
|
+
You can launch up to 3 agent(s) in parallel.
|
|
90
|
+
|
|
91
|
+
**Guidelines:**
|
|
92
|
+
- **Default**: Launch at least 1 Plan agent for most tasks - it helps validate your understanding and consider alternatives
|
|
93
|
+
- **Skip agents**: Only for truly trivial tasks (typo fixes, single-line changes, simple renames)
|
|
94
|
+
- **Multiple agents**: Use up to 3 agents for complex tasks that benefit from different perspectives
|
|
95
|
+
|
|
96
|
+
Examples of when to use multiple agents:
|
|
97
|
+
- The task touches multiple parts of the codebase
|
|
98
|
+
- It's a large refactor or architectural change
|
|
99
|
+
- There are many edge cases to consider
|
|
100
|
+
- You'd benefit from exploring different approaches
|
|
101
|
+
|
|
102
|
+
Example perspectives by task type:
|
|
103
|
+
- New feature: simplicity vs performance vs maintainability
|
|
104
|
+
- Bug fix: root cause vs workaround vs prevention
|
|
105
|
+
- Refactoring: minimal change vs clean architecture
|
|
106
|
+
|
|
107
|
+
In the agent prompt:
|
|
108
|
+
- Provide comprehensive background context from Phase 1 exploration including filenames and code path traces
|
|
109
|
+
- Describe requirements and constraints
|
|
110
|
+
- Request a detailed implementation plan
|
|
111
|
+
|
|
112
|
+
### Phase 3: Review
|
|
113
|
+
Goal: Review the plan(s) from Phase 2 and ensure alignment with the user's intentions.
|
|
114
|
+
1. Read the critical files identified by agents to deepen your understanding
|
|
115
|
+
2. Ensure that the plans align with the user's original request
|
|
116
|
+
3. Use ${ASK_USER_QUESTION_TOOL_NAME} to clarify any remaining questions with the user
|
|
117
|
+
|
|
118
|
+
### Phase 4: Final Plan
|
|
119
|
+
Goal: Write your final plan to the plan file (the only file you can edit).
|
|
120
|
+
- Begin with a **Context** section: explain why this change is being made — the problem or need it addresses, what prompted it, and the intended outcome
|
|
121
|
+
- Include only your recommended approach, not all alternatives
|
|
122
|
+
- Ensure that the plan file is concise enough to scan quickly, but detailed enough to execute effectively
|
|
123
|
+
- Include the paths of critical files to be modified
|
|
124
|
+
- Reference existing functions and utilities you found that should be reused, with their file paths
|
|
125
|
+
- Include a verification section describing how to test the changes end-to-end (run the code, use MCP tools, run tests)
|
|
126
|
+
|
|
127
|
+
### Phase 5: Call ${EXIT_PLAN_MODE_TOOL_NAME}
|
|
128
|
+
At the very end of your turn, once you have asked the user questions and are happy with your final plan file - you should always call ${EXIT_PLAN_MODE_TOOL_NAME} to indicate to the user that you are done planning.
|
|
129
|
+
This is critical - your turn should only end with either using the ${ASK_USER_QUESTION_TOOL_NAME} tool OR calling ${EXIT_PLAN_MODE_TOOL_NAME}. Do not stop unless it's for these 2 reasons
|
|
130
|
+
|
|
131
|
+
**Important:** Use ${ASK_USER_QUESTION_TOOL_NAME} ONLY to clarify requirements or choose between approaches. Use ${EXIT_PLAN_MODE_TOOL_NAME} to request plan approval. Do NOT ask about plan approval in any other way - no text questions, no AskUserQuestion. Phrases like "Is this plan okay?", "Should I proceed?", "How does this plan look?", "Any changes before we start?", or similar MUST use ${EXIT_PLAN_MODE_TOOL_NAME}.
|
|
132
|
+
|
|
133
|
+
NOTE: At any point in time through this workflow you should feel free to ask the user questions or clarifications using the ${ASK_USER_QUESTION_TOOL_NAME} tool. Don't make large assumptions about user intent. The goal is to present a well researched plan to the user, and tie any loose ends before implementation begins.`;
|
|
53
134
|
}
|
|
54
135
|
|
|
55
136
|
export const DEFAULT_SYSTEM_PROMPT = BASE_SYSTEM_PROMPT;
|
|
@@ -84,6 +165,44 @@ Guidelines:
|
|
|
84
165
|
- In your final response always share relevant file names and code snippets. Any file paths you return in your response MUST be absolute. Do NOT use relative paths.
|
|
85
166
|
- For clear communication, avoid using emojis.`;
|
|
86
167
|
|
|
168
|
+
export const EXPLORE_SUBAGENT_SYSTEM_PROMPT = `You are a file search specialist. You excel at thoroughly navigating and exploring codebases.
|
|
169
|
+
|
|
170
|
+
=== CRITICAL: READ-ONLY MODE - NO FILE MODIFICATIONS ===
|
|
171
|
+
This is a READ-ONLY exploration task. You are STRICTLY PROHIBITED from:
|
|
172
|
+
- Creating new files (no Write, touch, or file creation of any kind)
|
|
173
|
+
- Modifying existing files (no Edit operations)
|
|
174
|
+
- Deleting files (no rm or deletion)
|
|
175
|
+
- Moving or copying files (no mv or cp)
|
|
176
|
+
- Creating temporary files anywhere, including /tmp
|
|
177
|
+
- Using redirect operators (>, >>, |) or heredocs to write to files
|
|
178
|
+
- Running ANY commands that change system state
|
|
179
|
+
|
|
180
|
+
Your role is EXCLUSIVELY to search and analyze existing code. You do NOT have access to file editing tools - attempting to edit files will fail.
|
|
181
|
+
|
|
182
|
+
Your strengths:
|
|
183
|
+
- Rapidly finding files using glob patterns
|
|
184
|
+
- Searching code and text with powerful regex patterns
|
|
185
|
+
- Reading and analyzing file contents
|
|
186
|
+
- Using Language Server Protocol (LSP) for deep code intelligence (definitions, references, etc.)
|
|
187
|
+
|
|
188
|
+
Guidelines:
|
|
189
|
+
- Use ${GLOB_TOOL_NAME} for broad file pattern matching
|
|
190
|
+
- Use ${GREP_TOOL_NAME} for searching file contents with regex
|
|
191
|
+
- Use ${READ_TOOL_NAME} when you know the specific file path you need to read
|
|
192
|
+
- Use LSP for code intelligence features like finding definitions, references, implementations, and symbols. This is especially useful for understanding complex code relationships.
|
|
193
|
+
- Use ${BASH_TOOL_NAME} ONLY for read-only operations (ls, git status, git log, git diff, find, cat, head, tail)
|
|
194
|
+
- NEVER use ${BASH_TOOL_NAME} for: mkdir, touch, rm, cp, mv, git add, git commit, npm install, pip install, or any file creation/modification
|
|
195
|
+
- Adapt your search approach based on the thoroughness level specified by the caller
|
|
196
|
+
- Return file paths as absolute paths in your final response
|
|
197
|
+
- For clear communication, avoid using emojis
|
|
198
|
+
- Communicate your final report directly as a regular message - do NOT attempt to create files
|
|
199
|
+
|
|
200
|
+
NOTE: You are meant to be a fast agent that returns output as quickly as possible. In order to achieve this you must:
|
|
201
|
+
- Make efficient use of the tools that you have at your disposal: be smart about how you search for files and implementations
|
|
202
|
+
- Wherever possible you should try to spawn multiple parallel tool calls for grepping and reading files
|
|
203
|
+
|
|
204
|
+
Complete the user's search request efficiently and report your findings clearly.`;
|
|
205
|
+
|
|
87
206
|
export const PLAN_SUBAGENT_SYSTEM_PROMPT = `You are a software architect and planning specialist. Your role is to explore the codebase and design implementation plans.
|
|
88
207
|
|
|
89
208
|
=== CRITICAL: READ-ONLY MODE - NO FILE MODIFICATIONS ===
|
|
@@ -186,12 +305,9 @@ export function buildSystemPrompt(
|
|
|
186
305
|
tools: ToolPlugin[],
|
|
187
306
|
options: {
|
|
188
307
|
workdir?: string;
|
|
189
|
-
isGitRepo?: string;
|
|
190
|
-
platform?: string;
|
|
191
|
-
osVersion?: string;
|
|
192
|
-
today?: string;
|
|
193
308
|
memory?: string;
|
|
194
309
|
language?: string;
|
|
310
|
+
isSubagent?: boolean;
|
|
195
311
|
planMode?: {
|
|
196
312
|
planFilePath: string;
|
|
197
313
|
planExists: boolean;
|
|
@@ -210,6 +326,8 @@ export function buildSystemPrompt(
|
|
|
210
326
|
prompt += TASK_MANAGEMENT_POLICY;
|
|
211
327
|
}
|
|
212
328
|
|
|
329
|
+
prompt += `\n\n${TOOL_POLICY}`;
|
|
330
|
+
|
|
213
331
|
for (const tool of tools) {
|
|
214
332
|
if (tool.prompt) {
|
|
215
333
|
prompt += tool.prompt();
|
|
@@ -221,19 +339,24 @@ export function buildSystemPrompt(
|
|
|
221
339
|
}
|
|
222
340
|
|
|
223
341
|
if (options.planMode) {
|
|
224
|
-
prompt += `\n\n${buildPlanModePrompt(options.planMode.planFilePath, options.planMode.planExists)}`;
|
|
342
|
+
prompt += `\n\n${buildPlanModePrompt(options.planMode.planFilePath, options.planMode.planExists, options.isSubagent)}`;
|
|
225
343
|
}
|
|
226
344
|
|
|
227
345
|
if (options.workdir) {
|
|
346
|
+
const isGitRepo = isGitRepository(options.workdir);
|
|
347
|
+
const platform = os.platform();
|
|
348
|
+
const osVersion = `${os.type()} ${os.release()}`;
|
|
349
|
+
const today = new Date().toISOString().split("T")[0];
|
|
350
|
+
|
|
228
351
|
prompt += `
|
|
229
352
|
|
|
230
353
|
Here is useful information about the environment you are running in:
|
|
231
354
|
<env>
|
|
232
355
|
Working directory: ${options.workdir}
|
|
233
|
-
Is directory a git repo: ${
|
|
234
|
-
Platform: ${
|
|
235
|
-
OS Version: ${
|
|
236
|
-
Today's date: ${
|
|
356
|
+
Is directory a git repo: ${isGitRepo}
|
|
357
|
+
Platform: ${platform}
|
|
358
|
+
OS Version: ${osVersion}
|
|
359
|
+
Today's date: ${today}
|
|
237
360
|
</env>
|
|
238
361
|
`;
|
|
239
362
|
}
|
|
@@ -21,7 +21,7 @@ import * as os from "os";
|
|
|
21
21
|
import * as fs from "fs";
|
|
22
22
|
import * as path from "path";
|
|
23
23
|
|
|
24
|
-
import { COMPRESS_MESSAGES_SYSTEM_PROMPT } from "../
|
|
24
|
+
import { COMPRESS_MESSAGES_SYSTEM_PROMPT } from "../prompts/index.js";
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
27
|
* Interface for debug data saved during 400 errors
|
|
@@ -27,7 +27,7 @@ export class TaskManager extends EventEmitter {
|
|
|
27
27
|
return join(this.baseDir, this.taskListId);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
public getTaskPath(taskId: string): string {
|
|
31
31
|
return join(this.getSessionDir(), `${taskId}.json`);
|
|
32
32
|
}
|
|
33
33
|
|
|
@@ -185,4 +185,11 @@ export class TaskManager extends EventEmitter {
|
|
|
185
185
|
|
|
186
186
|
return (Math.max(...ids) + 1).toString();
|
|
187
187
|
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Refreshes the task list by re-reading tasks from disk and emitting a change event.
|
|
191
|
+
*/
|
|
192
|
+
public async refreshTasks(): Promise<void> {
|
|
193
|
+
this.emit("tasksChange", this.taskListId);
|
|
194
|
+
}
|
|
188
195
|
}
|
package/src/tools/bashTool.ts
CHANGED
|
@@ -24,7 +24,34 @@ export const bashTool: ToolPlugin = {
|
|
|
24
24
|
type: "function",
|
|
25
25
|
function: {
|
|
26
26
|
name: BASH_TOOL_NAME,
|
|
27
|
-
description:
|
|
27
|
+
description: "Run shell command",
|
|
28
|
+
parameters: {
|
|
29
|
+
type: "object",
|
|
30
|
+
properties: {
|
|
31
|
+
command: {
|
|
32
|
+
type: "string",
|
|
33
|
+
description: "The command to execute",
|
|
34
|
+
},
|
|
35
|
+
timeout: {
|
|
36
|
+
type: "number",
|
|
37
|
+
description: "Optional timeout in milliseconds (max 600000)",
|
|
38
|
+
},
|
|
39
|
+
description: {
|
|
40
|
+
type: "string",
|
|
41
|
+
description:
|
|
42
|
+
"Clear, concise description of what this command does in 5-10 words.",
|
|
43
|
+
},
|
|
44
|
+
run_in_background: {
|
|
45
|
+
type: "boolean",
|
|
46
|
+
description: `Set to true to run this command in the background. Use ${TASK_OUTPUT_TOOL_NAME} to read the output later.`,
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
required: ["command"],
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
prompt: () => `
|
|
54
|
+
Executes a given bash command in a persistent shell session with optional timeout, ensuring proper handling and security measures.
|
|
28
55
|
|
|
29
56
|
IMPORTANT: This tool is for terminal operations like git, npm, docker, etc. DO NOT use it for file operations (reading, writing, editing, searching, finding files) - use the specialized tools for this instead.
|
|
30
57
|
|
|
@@ -69,33 +96,7 @@ Usage notes:
|
|
|
69
96
|
<bad-example>
|
|
70
97
|
cd /foo/bar && pytest tests
|
|
71
98
|
</bad-example>
|
|
72
|
-
|
|
73
|
-
parameters: {
|
|
74
|
-
type: "object",
|
|
75
|
-
properties: {
|
|
76
|
-
command: {
|
|
77
|
-
type: "string",
|
|
78
|
-
description: "The command to execute",
|
|
79
|
-
},
|
|
80
|
-
timeout: {
|
|
81
|
-
type: "number",
|
|
82
|
-
description: "Optional timeout in milliseconds (max 600000)",
|
|
83
|
-
},
|
|
84
|
-
description: {
|
|
85
|
-
type: "string",
|
|
86
|
-
description:
|
|
87
|
-
"Clear, concise description of what this command does in 5-10 words.",
|
|
88
|
-
},
|
|
89
|
-
run_in_background: {
|
|
90
|
-
type: "boolean",
|
|
91
|
-
description: `Set to true to run this command in the background. Use ${TASK_OUTPUT_TOOL_NAME} to read the output later.`,
|
|
92
|
-
},
|
|
93
|
-
},
|
|
94
|
-
required: ["command"],
|
|
95
|
-
},
|
|
96
|
-
},
|
|
97
|
-
},
|
|
98
|
-
prompt: () => `
|
|
99
|
+
|
|
99
100
|
- Reserve bash tools exclusively for actual system commands and terminal operations that require shell execution. NEVER use bash echo or other command-line tools to communicate thoughts, explanations, or instructions to the user. Output all communication directly in your response text instead.
|
|
100
101
|
- When making multiple bash tool calls, you MUST send a single message with multiple tools calls to run the calls in parallel. For example, if you need to run "git status" and "git diff", send a single message with two tool calls in parallel.`,
|
|
101
102
|
execute: async (
|
|
@@ -306,13 +307,17 @@ Usage notes:
|
|
|
306
307
|
|
|
307
308
|
child.stdout?.on("data", (data) => {
|
|
308
309
|
if (!isAborted && !isBackgrounded) {
|
|
309
|
-
|
|
310
|
+
const chunk = stripAnsiColors(data.toString());
|
|
311
|
+
outputBuffer += chunk;
|
|
312
|
+
context.onShortResultUpdate?.(chunk.trim().split("\n").pop() || "");
|
|
310
313
|
}
|
|
311
314
|
});
|
|
312
315
|
|
|
313
316
|
child.stderr?.on("data", (data) => {
|
|
314
317
|
if (!isAborted && !isBackgrounded) {
|
|
315
|
-
|
|
318
|
+
const chunk = stripAnsiColors(data.toString());
|
|
319
|
+
errorBuffer += chunk;
|
|
320
|
+
context.onShortResultUpdate?.(chunk.trim().split("\n").pop() || "");
|
|
316
321
|
}
|
|
317
322
|
});
|
|
318
323
|
|
|
@@ -93,7 +93,7 @@ Ensure your plan is complete and unambiguous:
|
|
|
93
93
|
if (permissionResult.behavior === "deny") {
|
|
94
94
|
return {
|
|
95
95
|
success: false,
|
|
96
|
-
content: permissionResult.message || "Plan rejected by user"
|
|
96
|
+
content: `User feedback: ${permissionResult.message || "Plan rejected by user"}. Please update your proposal accordingly.`,
|
|
97
97
|
error: permissionResult.message ? undefined : "Plan rejected by user",
|
|
98
98
|
};
|
|
99
99
|
}
|