wave-agent-sdk 0.8.2 → 0.8.4
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.js +2 -2
- package/dist/constants/tools.d.ts +1 -2
- package/dist/constants/tools.d.ts.map +1 -1
- package/dist/constants/tools.js +1 -2
- package/dist/managers/permissionManager.d.ts.map +1 -1
- package/dist/managers/permissionManager.js +49 -36
- package/dist/managers/pluginManager.d.ts.map +1 -1
- package/dist/managers/pluginManager.js +28 -1
- package/dist/managers/subagentManager.d.ts +3 -3
- package/dist/managers/subagentManager.d.ts.map +1 -1
- package/dist/managers/subagentManager.js +13 -12
- package/dist/managers/toolManager.d.ts +2 -2
- package/dist/managers/toolManager.d.ts.map +1 -1
- package/dist/managers/toolManager.js +4 -6
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/index.js +3 -3
- package/dist/tools/agentTool.d.ts +6 -0
- package/dist/tools/agentTool.d.ts.map +1 -0
- package/dist/tools/{taskTool.js → agentTool.js} +42 -30
- package/dist/tools/grepTool.js +2 -2
- package/dist/tools/skillTool.js +2 -2
- package/dist/tools/types.d.ts +1 -1
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/utils/bashParser.d.ts +4 -0
- package/dist/utils/bashParser.d.ts.map +1 -1
- package/dist/utils/bashParser.js +39 -2
- package/dist/utils/builtinSubagents.d.ts.map +1 -1
- package/dist/utils/builtinSubagents.js +1 -3
- package/dist/utils/gitUtils.d.ts.map +1 -1
- package/dist/utils/gitUtils.js +47 -1
- package/dist/utils/path.d.ts +0 -10
- package/dist/utils/path.d.ts.map +1 -1
- package/dist/utils/path.js +0 -61
- package/package.json +2 -5
- package/src/agent.ts +2 -2
- package/src/constants/tools.ts +1 -2
- package/src/managers/permissionManager.ts +61 -44
- package/src/managers/pluginManager.ts +39 -1
- package/src/managers/subagentManager.ts +13 -12
- package/src/managers/toolManager.ts +4 -6
- package/src/prompts/index.ts +3 -2
- package/src/tools/{taskTool.ts → agentTool.ts} +42 -30
- package/src/tools/grepTool.ts +2 -2
- package/src/tools/skillTool.ts +2 -2
- package/src/tools/types.ts +1 -1
- package/src/utils/bashParser.ts +50 -2
- package/src/utils/builtinSubagents.ts +0 -3
- package/src/utils/gitUtils.ts +48 -1
- package/src/utils/path.ts +0 -62
- package/dist/tools/lsTool.d.ts +0 -6
- package/dist/tools/lsTool.d.ts.map +0 -1
- package/dist/tools/lsTool.js +0 -175
- package/dist/tools/taskTool.d.ts +0 -6
- package/dist/tools/taskTool.d.ts.map +0 -1
- package/src/tools/lsTool.ts +0 -212
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
splitBashCommand,
|
|
21
21
|
stripEnvVars,
|
|
22
22
|
stripRedirections,
|
|
23
|
+
hasWriteRedirections,
|
|
23
24
|
getSmartPrefix,
|
|
24
25
|
DANGEROUS_COMMANDS,
|
|
25
26
|
} from "../utils/bashParser.js";
|
|
@@ -29,7 +30,6 @@ import {
|
|
|
29
30
|
EDIT_TOOL_NAME,
|
|
30
31
|
WRITE_TOOL_NAME,
|
|
31
32
|
READ_TOOL_NAME,
|
|
32
|
-
LS_TOOL_NAME,
|
|
33
33
|
} from "../constants/tools.js";
|
|
34
34
|
import { Container } from "../utils/container.js";
|
|
35
35
|
import { ConfigurationService } from "../services/configurationService.js";
|
|
@@ -464,6 +464,9 @@ export class PermissionManager {
|
|
|
464
464
|
const parts = splitBashCommand(command);
|
|
465
465
|
|
|
466
466
|
const isDangerous = parts.some((part) => {
|
|
467
|
+
if (hasWriteRedirections(part)) {
|
|
468
|
+
return true;
|
|
469
|
+
}
|
|
467
470
|
const processedPart = stripRedirections(stripEnvVars(part));
|
|
468
471
|
const commandMatch = processedPart.match(/^(\w+)(\s+.*)?$/);
|
|
469
472
|
if (commandMatch) {
|
|
@@ -523,7 +526,17 @@ export class PermissionManager {
|
|
|
523
526
|
// Handle Bash command rules
|
|
524
527
|
if (toolName === BASH_TOOL_NAME) {
|
|
525
528
|
const command = String(context.toolInput?.command || "");
|
|
526
|
-
const
|
|
529
|
+
const hasWriteInPattern = hasWriteRedirections(pattern);
|
|
530
|
+
const hasWriteInCommand = hasWriteRedirections(command);
|
|
531
|
+
|
|
532
|
+
// If the command has write redirections, it must match a pattern that also has write redirections
|
|
533
|
+
if (hasWriteInCommand && !hasWriteInPattern) {
|
|
534
|
+
return false;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
const processedPart = hasWriteInPattern
|
|
538
|
+
? stripEnvVars(command)
|
|
539
|
+
: stripRedirections(stripEnvVars(command));
|
|
527
540
|
// For Bash commands, we want '*' to match everything including slashes and spaces
|
|
528
541
|
// minimatch's default behavior for '*' is to not match across directory separators
|
|
529
542
|
// We use a regex to replace '*' with '.*' (match anything)
|
|
@@ -536,12 +549,7 @@ export class PermissionManager {
|
|
|
536
549
|
}
|
|
537
550
|
|
|
538
551
|
// Handle path-based rules (e.g., "Read(**/*.env)")
|
|
539
|
-
const pathTools = [
|
|
540
|
-
READ_TOOL_NAME,
|
|
541
|
-
WRITE_TOOL_NAME,
|
|
542
|
-
EDIT_TOOL_NAME,
|
|
543
|
-
LS_TOOL_NAME,
|
|
544
|
-
];
|
|
552
|
+
const pathTools = [READ_TOOL_NAME, WRITE_TOOL_NAME, EDIT_TOOL_NAME];
|
|
545
553
|
if (pathTools.includes(toolName)) {
|
|
546
554
|
const targetPath = (context.toolInput?.file_path ||
|
|
547
555
|
context.toolInput?.path) as string | undefined;
|
|
@@ -561,6 +569,7 @@ export class PermissionManager {
|
|
|
561
569
|
const isAllowedByRuleList = (
|
|
562
570
|
ctx: ToolPermissionContext,
|
|
563
571
|
rules: string[],
|
|
572
|
+
isDefaultRules: boolean = false,
|
|
564
573
|
) => {
|
|
565
574
|
if (ctx.toolName === BASH_TOOL_NAME && ctx.toolInput?.command) {
|
|
566
575
|
const command = String(ctx.toolInput.command);
|
|
@@ -570,53 +579,60 @@ export class PermissionManager {
|
|
|
570
579
|
const workdir = ctx.toolInput?.workdir as string | undefined;
|
|
571
580
|
|
|
572
581
|
return parts.every((part) => {
|
|
582
|
+
const hasWrite = hasWriteRedirections(part);
|
|
573
583
|
const processedPart = stripRedirections(stripEnvVars(part));
|
|
574
584
|
|
|
575
585
|
// Check for safe commands
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
if (cmd
|
|
583
|
-
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
if (workdir) {
|
|
587
|
-
// For cd and ls, check paths
|
|
588
|
-
const pathArgs =
|
|
589
|
-
(args.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g) || []).filter(
|
|
590
|
-
(arg) => !arg.startsWith("-"),
|
|
591
|
-
) || [];
|
|
592
|
-
|
|
593
|
-
if (pathArgs.length === 0) {
|
|
594
|
-
// cd or ls without arguments operates on current dir (workdir)
|
|
586
|
+
if (!hasWrite) {
|
|
587
|
+
const commandMatch = processedPart.match(/^(\w+)(\s+.*)?$/);
|
|
588
|
+
if (commandMatch) {
|
|
589
|
+
const cmd = commandMatch[1];
|
|
590
|
+
const args = commandMatch[2]?.trim() || "";
|
|
591
|
+
|
|
592
|
+
if (SAFE_COMMANDS.includes(cmd)) {
|
|
593
|
+
if (cmd === "pwd" || cmd === "true" || cmd === "false") {
|
|
595
594
|
return true;
|
|
596
595
|
}
|
|
597
596
|
|
|
598
|
-
|
|
599
|
-
//
|
|
600
|
-
const
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
597
|
+
if (workdir) {
|
|
598
|
+
// For cd and ls, check paths
|
|
599
|
+
const pathArgs =
|
|
600
|
+
(args.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g) || []).filter(
|
|
601
|
+
(arg) => !arg.startsWith("-"),
|
|
602
|
+
) || [];
|
|
603
|
+
|
|
604
|
+
if (pathArgs.length === 0) {
|
|
605
|
+
// cd or ls without arguments operates on current dir (workdir)
|
|
606
|
+
return true;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
const allPathsSafe = pathArgs.every((pathArg) => {
|
|
610
|
+
// Remove quotes if present
|
|
611
|
+
const cleanPath = pathArg.replace(/^['"](.*)['"]$/, "$1");
|
|
612
|
+
const { isInside } = this.isInsideSafeZone(
|
|
613
|
+
cleanPath,
|
|
614
|
+
workdir,
|
|
615
|
+
);
|
|
616
|
+
return isInside;
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
if (allPathsSafe) {
|
|
620
|
+
return true;
|
|
621
|
+
}
|
|
610
622
|
}
|
|
611
623
|
}
|
|
612
624
|
}
|
|
613
625
|
}
|
|
614
626
|
|
|
615
627
|
// Check if this specific part is allowed by any rule
|
|
628
|
+
if (hasWrite && isDefaultRules) {
|
|
629
|
+
return false;
|
|
630
|
+
}
|
|
631
|
+
|
|
616
632
|
// We create a temporary context with just this part of the command
|
|
617
633
|
const partContext = {
|
|
618
634
|
...ctx,
|
|
619
|
-
toolInput: { ...ctx.toolInput, command:
|
|
635
|
+
toolInput: { ...ctx.toolInput, command: part },
|
|
620
636
|
};
|
|
621
637
|
const allowedByRule = rules.some((rule) => {
|
|
622
638
|
return this.matchesRule(partContext, rule);
|
|
@@ -643,7 +659,7 @@ export class PermissionManager {
|
|
|
643
659
|
}
|
|
644
660
|
|
|
645
661
|
// Check default allowed rules
|
|
646
|
-
return isAllowedByRuleList(context, DEFAULT_ALLOWED_RULES);
|
|
662
|
+
return isAllowedByRuleList(context, DEFAULT_ALLOWED_RULES, true);
|
|
647
663
|
}
|
|
648
664
|
|
|
649
665
|
/**
|
|
@@ -659,13 +675,14 @@ export class PermissionManager {
|
|
|
659
675
|
const rules: string[] = [];
|
|
660
676
|
|
|
661
677
|
for (const part of parts) {
|
|
678
|
+
const hasWrite = hasWriteRedirections(part);
|
|
662
679
|
const processedPart = stripRedirections(stripEnvVars(part));
|
|
663
680
|
|
|
664
681
|
// Check for safe commands
|
|
665
682
|
const commandMatch = processedPart.match(/^(\w+)(\s+.*)?$/);
|
|
666
683
|
let isSafe = false;
|
|
667
684
|
|
|
668
|
-
if (commandMatch) {
|
|
685
|
+
if (commandMatch && !hasWrite) {
|
|
669
686
|
const cmd = commandMatch[1];
|
|
670
687
|
const args = commandMatch[2]?.trim() || "";
|
|
671
688
|
|
|
@@ -724,11 +741,11 @@ export class PermissionManager {
|
|
|
724
741
|
}
|
|
725
742
|
}
|
|
726
743
|
|
|
727
|
-
const smartPrefix = getSmartPrefix(processedPart);
|
|
744
|
+
const smartPrefix = hasWrite ? null : getSmartPrefix(processedPart);
|
|
728
745
|
if (smartPrefix) {
|
|
729
746
|
rules.push(`Bash(${smartPrefix}*)`);
|
|
730
747
|
} else {
|
|
731
|
-
rules.push(`Bash(${processedPart})`);
|
|
748
|
+
rules.push(`Bash(${hasWrite ? stripEnvVars(part) : processedPart})`);
|
|
732
749
|
}
|
|
733
750
|
}
|
|
734
751
|
}
|
|
@@ -73,7 +73,45 @@ export class PluginManager {
|
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
const marketplaceService = new MarketplaceService();
|
|
76
|
-
|
|
76
|
+
let installedRegistry = await marketplaceService.getInstalledPlugins();
|
|
77
|
+
const knownMarketplaces = await marketplaceService.listMarketplaces();
|
|
78
|
+
|
|
79
|
+
// Identify missing enabled plugins and auto-install them if marketplace is known
|
|
80
|
+
for (const pluginId of Object.keys(this.enabledPlugins)) {
|
|
81
|
+
if (this.enabledPlugins[pluginId] !== true) continue;
|
|
82
|
+
|
|
83
|
+
const [name, marketplaceName] = pluginId.split("@");
|
|
84
|
+
if (!name || !marketplaceName) continue;
|
|
85
|
+
|
|
86
|
+
const isInstalled = installedRegistry.plugins.some(
|
|
87
|
+
(p) => p.name === name && p.marketplace === marketplaceName,
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
if (!isInstalled) {
|
|
91
|
+
const isMarketplaceKnown = knownMarketplaces.some(
|
|
92
|
+
(m) => m.name === marketplaceName,
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
if (isMarketplaceKnown) {
|
|
96
|
+
logger?.info(`Auto-installing missing plugin: ${pluginId}`);
|
|
97
|
+
try {
|
|
98
|
+
await marketplaceService.installPlugin(pluginId);
|
|
99
|
+
} catch (installError) {
|
|
100
|
+
logger?.error(
|
|
101
|
+
`Failed to auto-install plugin ${pluginId}:`,
|
|
102
|
+
installError,
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
} else {
|
|
106
|
+
logger?.warn(
|
|
107
|
+
`Plugin ${pluginId} is enabled but marketplace ${marketplaceName} is unknown. Skipping auto-install.`,
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Refresh registry after potential auto-installs
|
|
114
|
+
installedRegistry = await marketplaceService.getInstalledPlugins();
|
|
77
115
|
|
|
78
116
|
for (const p of installedRegistry.plugins) {
|
|
79
117
|
const pluginId = `${p.name}@${p.marketplace}`;
|
|
@@ -9,6 +9,7 @@ import type {
|
|
|
9
9
|
import { AIManager } from "./aiManager.js";
|
|
10
10
|
import { MessageManager } from "./messageManager.js";
|
|
11
11
|
import { ToolManager } from "./toolManager.js";
|
|
12
|
+
import { AGENT_TOOL_NAME } from "../constants/tools.js";
|
|
12
13
|
import {
|
|
13
14
|
addConsolidatedAbortListener,
|
|
14
15
|
createAbortPromise,
|
|
@@ -287,12 +288,12 @@ export class SubagentManager {
|
|
|
287
288
|
}
|
|
288
289
|
|
|
289
290
|
/**
|
|
290
|
-
* Execute
|
|
291
|
+
* Execute agent using subagent instance
|
|
291
292
|
*
|
|
292
|
-
* IMPORTANT: This method automatically filters out the
|
|
293
|
+
* IMPORTANT: This method automatically filters out the Agent tool from allowedTools
|
|
293
294
|
* to prevent subagents from spawning other subagents (infinite recursion protection)
|
|
294
295
|
*/
|
|
295
|
-
async
|
|
296
|
+
async executeAgent(
|
|
296
297
|
instance: SubagentInstance,
|
|
297
298
|
prompt: string,
|
|
298
299
|
abortSignal?: AbortSignal,
|
|
@@ -301,7 +302,7 @@ export class SubagentManager {
|
|
|
301
302
|
try {
|
|
302
303
|
// Check if already aborted before starting
|
|
303
304
|
if (abortSignal?.aborted) {
|
|
304
|
-
throw new Error("
|
|
305
|
+
throw new Error("Agent was aborted before execution started");
|
|
305
306
|
}
|
|
306
307
|
|
|
307
308
|
// Set status to active and update parent
|
|
@@ -433,17 +434,17 @@ export class SubagentManager {
|
|
|
433
434
|
// Add the user's prompt as a message
|
|
434
435
|
instance.messageManager.addUserMessage({ content: prompt });
|
|
435
436
|
|
|
436
|
-
// Create enabled tools list - always exclude
|
|
437
|
+
// Create enabled tools list - always exclude Agent tool to prevent subagent recursion
|
|
437
438
|
// Use instance.configuration.tools if provided, otherwise fallback to all tools
|
|
438
439
|
let enabledTools = instance.configuration.tools;
|
|
439
440
|
|
|
440
|
-
// Always filter out the
|
|
441
|
+
// Always filter out the Agent tool to prevent subagents from creating sub-subagents
|
|
441
442
|
if (enabledTools) {
|
|
442
|
-
enabledTools = enabledTools.filter((tool) => tool !==
|
|
443
|
+
enabledTools = enabledTools.filter((tool) => tool !== AGENT_TOOL_NAME);
|
|
443
444
|
} else {
|
|
444
|
-
// If no tools specified, get all tools except
|
|
445
|
+
// If no tools specified, get all tools except Agent
|
|
445
446
|
const allTools = instance.toolManager.list().map((tool) => tool.name);
|
|
446
|
-
enabledTools = allTools.filter((tool) => tool !==
|
|
447
|
+
enabledTools = allTools.filter((tool) => tool !== AGENT_TOOL_NAME);
|
|
447
448
|
}
|
|
448
449
|
|
|
449
450
|
// Execute the AI request with tool restrictions
|
|
@@ -476,7 +477,7 @@ export class SubagentManager {
|
|
|
476
477
|
if (abortSignal && !instance.backgroundTaskId) {
|
|
477
478
|
await Promise.race([
|
|
478
479
|
executeAI,
|
|
479
|
-
createAbortPromise(abortSignal, "
|
|
480
|
+
createAbortPromise(abortSignal, "Agent was aborted"),
|
|
480
481
|
]);
|
|
481
482
|
} else {
|
|
482
483
|
await executeAI;
|
|
@@ -510,7 +511,7 @@ export class SubagentManager {
|
|
|
510
511
|
const task = backgroundTaskManager.getTask(instance.backgroundTaskId);
|
|
511
512
|
if (task) {
|
|
512
513
|
task.status = "completed";
|
|
513
|
-
task.stdout = response || "
|
|
514
|
+
task.stdout = response || "Agent completed with no text response";
|
|
514
515
|
task.endTime = Date.now();
|
|
515
516
|
if (task.startTime) {
|
|
516
517
|
task.runtime = task.endTime - task.startTime;
|
|
@@ -518,7 +519,7 @@ export class SubagentManager {
|
|
|
518
519
|
}
|
|
519
520
|
}
|
|
520
521
|
|
|
521
|
-
return response || "
|
|
522
|
+
return response || "Agent completed with no text response";
|
|
522
523
|
} catch (error) {
|
|
523
524
|
const backgroundTaskManager = this.container.has("BackgroundTaskManager")
|
|
524
525
|
? this.container.get<BackgroundTaskManager>("BackgroundTaskManager")
|
|
@@ -9,10 +9,9 @@ import { askUserQuestionTool } from "../tools/askUserQuestion.js";
|
|
|
9
9
|
// New tools
|
|
10
10
|
import { globTool } from "../tools/globTool.js";
|
|
11
11
|
import { grepTool } from "../tools/grepTool.js";
|
|
12
|
-
import { lsTool } from "../tools/lsTool.js";
|
|
13
12
|
import { readTool } from "../tools/readTool.js";
|
|
14
13
|
import { lspTool } from "../tools/lspTool.js";
|
|
15
|
-
import {
|
|
14
|
+
import { agentTool } from "../tools/agentTool.js";
|
|
16
15
|
import { skillTool } from "../tools/skillTool.js";
|
|
17
16
|
import {
|
|
18
17
|
taskCreateTool,
|
|
@@ -78,10 +77,10 @@ class ToolManager {
|
|
|
78
77
|
*
|
|
79
78
|
* This method can be called multiple times safely. When called without dependencies,
|
|
80
79
|
* it registers basic tools (Bash, Read, Write, TaskCreate, etc.). When called with
|
|
81
|
-
* dependencies, it also registers tools that require managers (
|
|
80
|
+
* dependencies, it also registers tools that require managers (Agent, Skill).
|
|
82
81
|
*
|
|
83
82
|
* @param deps Optional dependencies for advanced tools
|
|
84
|
-
* @param deps.subagentManager SubagentManager instance for
|
|
83
|
+
* @param deps.subagentManager SubagentManager instance for Agent tool
|
|
85
84
|
* @param deps.skillManager SkillManager instance for Skill tool
|
|
86
85
|
*
|
|
87
86
|
* @example
|
|
@@ -107,10 +106,9 @@ class ToolManager {
|
|
|
107
106
|
askUserQuestionTool,
|
|
108
107
|
globTool,
|
|
109
108
|
grepTool,
|
|
110
|
-
lsTool,
|
|
111
109
|
readTool,
|
|
112
110
|
lspTool,
|
|
113
|
-
|
|
111
|
+
agentTool,
|
|
114
112
|
skillTool,
|
|
115
113
|
taskCreateTool,
|
|
116
114
|
taskGetTool,
|
package/src/prompts/index.ts
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
READ_TOOL_NAME,
|
|
15
15
|
WRITE_TOOL_NAME,
|
|
16
16
|
EXIT_PLAN_MODE_TOOL_NAME,
|
|
17
|
+
AGENT_TOOL_NAME,
|
|
17
18
|
} from "../constants/tools.js";
|
|
18
19
|
|
|
19
20
|
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.
|
|
@@ -61,7 +62,7 @@ You should build your plan incrementally by writing to or editing this file. NOT
|
|
|
61
62
|
## Plan Workflow
|
|
62
63
|
|
|
63
64
|
### Phase 1: Initial Understanding
|
|
64
|
-
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
|
|
65
|
+
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 ${AGENT_TOOL_NAME} tool with subagent_type=${EXPLORE_SUBAGENT_TYPE}.
|
|
65
66
|
|
|
66
67
|
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.
|
|
67
68
|
|
|
@@ -74,7 +75,7 @@ Goal: Gain a comprehensive understanding of the user's request by reading throug
|
|
|
74
75
|
### Phase 2: Design
|
|
75
76
|
Goal: Design an implementation approach.
|
|
76
77
|
|
|
77
|
-
Launch
|
|
78
|
+
Launch 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.
|
|
78
79
|
|
|
79
80
|
You can launch up to 3 agent(s) in parallel.
|
|
80
81
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ToolPlugin, ToolResult, ToolContext } from "./types.js";
|
|
2
2
|
import { EXPLORE_SUBAGENT_TYPE } from "../constants/subagents.js";
|
|
3
|
-
import {
|
|
3
|
+
import { AGENT_TOOL_NAME } from "../constants/tools.js";
|
|
4
4
|
import type { SubagentConfiguration } from "../utils/subagentParser.js";
|
|
5
5
|
import {
|
|
6
6
|
countToolBlocks,
|
|
@@ -8,16 +8,16 @@ import {
|
|
|
8
8
|
} from "../utils/messageOperations.js";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
11
|
+
* Agent tool plugin for launching specialized agents to handle complex tasks
|
|
12
12
|
*/
|
|
13
|
-
export const
|
|
14
|
-
name:
|
|
13
|
+
export const agentTool: ToolPlugin = {
|
|
14
|
+
name: AGENT_TOOL_NAME,
|
|
15
15
|
config: {
|
|
16
16
|
type: "function" as const,
|
|
17
17
|
function: {
|
|
18
|
-
name:
|
|
18
|
+
name: AGENT_TOOL_NAME,
|
|
19
19
|
description:
|
|
20
|
-
"
|
|
20
|
+
"Launch a new agent to handle complex, multi-step tasks autonomously.",
|
|
21
21
|
parameters: {
|
|
22
22
|
type: "object",
|
|
23
23
|
properties: {
|
|
@@ -47,19 +47,31 @@ export const taskTool: ToolPlugin = {
|
|
|
47
47
|
prompt: (args?: { availableSubagents?: SubagentConfiguration[] }) => {
|
|
48
48
|
const subagentList = args?.availableSubagents
|
|
49
49
|
? args.availableSubagents
|
|
50
|
-
.map((config) =>
|
|
50
|
+
.map((config) => {
|
|
51
|
+
let toolsStr = "";
|
|
52
|
+
if (config.tools && config.tools.length > 0) {
|
|
53
|
+
toolsStr = ` (Tools: ${config.tools.join(", ")})`;
|
|
54
|
+
} else {
|
|
55
|
+
toolsStr = " (Tools: *)";
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return `- ${config.name}: ${config.description}${toolsStr}`;
|
|
59
|
+
})
|
|
51
60
|
.join("\n")
|
|
52
61
|
: "";
|
|
53
62
|
|
|
54
|
-
return `
|
|
55
|
-
|
|
63
|
+
return `Launch a new agent to handle complex, multi-step tasks autonomously.
|
|
64
|
+
|
|
65
|
+
The Agent tool launches specialized agents (subprocesses) that autonomously handle complex tasks. Each agent type has specific capabilities and tools available to it.
|
|
66
|
+
|
|
67
|
+
Available agent types and the tools they have access to:
|
|
68
|
+
${subagentList || "No agents configured"}
|
|
56
69
|
|
|
57
|
-
|
|
58
|
-
${subagentList || "No subagents configured"}
|
|
70
|
+
When using the Agent tool, you must specify a subagent_type parameter to select which agent type to use.
|
|
59
71
|
|
|
60
|
-
- When doing file search, prefer to use the ${
|
|
61
|
-
- You should proactively use the ${
|
|
62
|
-
- VERY IMPORTANT: When exploring the codebase to gather context or to answer a question that is not a needle query for a specific file/class/function, it is CRITICAL that you use the ${
|
|
72
|
+
- When doing file search, prefer to use the ${AGENT_TOOL_NAME} tool in order to reduce context usage.
|
|
73
|
+
- You should proactively use the ${AGENT_TOOL_NAME} tool with specialized agents when the task at hand matches the agent's description.
|
|
74
|
+
- VERY IMPORTANT: When exploring the codebase to gather context or to answer a question that is not a needle query for a specific file/class/function, it is CRITICAL that you use the ${AGENT_TOOL_NAME} tool with subagent_type=${EXPLORE_SUBAGENT_TYPE} instead of running search commands directly.`;
|
|
63
75
|
},
|
|
64
76
|
|
|
65
77
|
execute: async (
|
|
@@ -72,7 +84,7 @@ ${subagentList || "No subagents configured"}
|
|
|
72
84
|
success: false,
|
|
73
85
|
content: "",
|
|
74
86
|
error: "Subagent manager not available in tool context",
|
|
75
|
-
shortResult: "
|
|
87
|
+
shortResult: "Agent delegation failed",
|
|
76
88
|
};
|
|
77
89
|
}
|
|
78
90
|
|
|
@@ -87,7 +99,7 @@ ${subagentList || "No subagents configured"}
|
|
|
87
99
|
success: false,
|
|
88
100
|
content: "",
|
|
89
101
|
error: "description parameter is required and must be a string",
|
|
90
|
-
shortResult: "
|
|
102
|
+
shortResult: "Agent delegation failed",
|
|
91
103
|
};
|
|
92
104
|
}
|
|
93
105
|
|
|
@@ -96,7 +108,7 @@ ${subagentList || "No subagents configured"}
|
|
|
96
108
|
success: false,
|
|
97
109
|
content: "",
|
|
98
110
|
error: "prompt parameter is required and must be a string",
|
|
99
|
-
shortResult: "
|
|
111
|
+
shortResult: "Agent delegation failed",
|
|
100
112
|
};
|
|
101
113
|
}
|
|
102
114
|
|
|
@@ -105,7 +117,7 @@ ${subagentList || "No subagents configured"}
|
|
|
105
117
|
success: false,
|
|
106
118
|
content: "",
|
|
107
119
|
error: "subagent_type parameter is required and must be a string",
|
|
108
|
-
shortResult: "
|
|
120
|
+
shortResult: "Agent delegation failed",
|
|
109
121
|
};
|
|
110
122
|
}
|
|
111
123
|
|
|
@@ -121,14 +133,14 @@ ${subagentList || "No subagents configured"}
|
|
|
121
133
|
return {
|
|
122
134
|
success: false,
|
|
123
135
|
content: "",
|
|
124
|
-
error: `No
|
|
125
|
-
shortResult: "
|
|
136
|
+
error: `No agent found matching "${subagent_type}". Available agents: ${availableNames || "none"}`,
|
|
137
|
+
shortResult: "Agent not found",
|
|
126
138
|
};
|
|
127
139
|
}
|
|
128
140
|
|
|
129
141
|
let isBackgrounded = false;
|
|
130
142
|
|
|
131
|
-
// Create subagent instance and execute
|
|
143
|
+
// Create subagent instance and execute agent
|
|
132
144
|
const instance = await subagentManager.createInstance(
|
|
133
145
|
configuration,
|
|
134
146
|
{
|
|
@@ -173,8 +185,8 @@ ${subagentList || "No subagents configured"}
|
|
|
173
185
|
await subagentManager.backgroundInstance(instance.subagentId);
|
|
174
186
|
resolve({
|
|
175
187
|
success: true,
|
|
176
|
-
content: "
|
|
177
|
-
shortResult: "
|
|
188
|
+
content: "Agent backgrounded",
|
|
189
|
+
shortResult: "Agent backgrounded",
|
|
178
190
|
isManuallyBackgrounded: true,
|
|
179
191
|
});
|
|
180
192
|
},
|
|
@@ -182,7 +194,7 @@ ${subagentList || "No subagents configured"}
|
|
|
182
194
|
}
|
|
183
195
|
|
|
184
196
|
try {
|
|
185
|
-
const result = await subagentManager.
|
|
197
|
+
const result = await subagentManager.executeAgent(
|
|
186
198
|
instance,
|
|
187
199
|
prompt,
|
|
188
200
|
context.abortSignal,
|
|
@@ -196,13 +208,13 @@ ${subagentList || "No subagents configured"}
|
|
|
196
208
|
if (run_in_background) {
|
|
197
209
|
resolve({
|
|
198
210
|
success: true,
|
|
199
|
-
content: `
|
|
200
|
-
shortResult: `
|
|
211
|
+
content: `Agent started in background with ID: ${result}`,
|
|
212
|
+
shortResult: `Agent started in background: ${result}`,
|
|
201
213
|
});
|
|
202
214
|
return;
|
|
203
215
|
}
|
|
204
216
|
|
|
205
|
-
// Cleanup subagent instance after
|
|
217
|
+
// Cleanup subagent instance after agent completion
|
|
206
218
|
subagentManager.cleanupInstance(instance.subagentId);
|
|
207
219
|
|
|
208
220
|
const messages = instance.messageManager.getMessages();
|
|
@@ -213,14 +225,14 @@ ${subagentList || "No subagents configured"}
|
|
|
213
225
|
resolve({
|
|
214
226
|
success: true,
|
|
215
227
|
content: result,
|
|
216
|
-
shortResult: `
|
|
228
|
+
shortResult: `Agent completed${summary ? ` ${summary}` : ""}`,
|
|
217
229
|
});
|
|
218
230
|
} catch (error) {
|
|
219
231
|
if (!isBackgrounded) {
|
|
220
232
|
resolve({
|
|
221
233
|
success: false,
|
|
222
234
|
content: "",
|
|
223
|
-
error: `
|
|
235
|
+
error: `Agent delegation failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
224
236
|
shortResult: "Delegation error",
|
|
225
237
|
});
|
|
226
238
|
}
|
|
@@ -237,7 +249,7 @@ ${subagentList || "No subagents configured"}
|
|
|
237
249
|
return {
|
|
238
250
|
success: false,
|
|
239
251
|
content: "",
|
|
240
|
-
error: `
|
|
252
|
+
error: `Agent delegation failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
241
253
|
shortResult: "Delegation error",
|
|
242
254
|
};
|
|
243
255
|
}
|
package/src/tools/grepTool.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { getDisplayPath } from "../utils/path.js";
|
|
|
6
6
|
import {
|
|
7
7
|
GREP_TOOL_NAME,
|
|
8
8
|
BASH_TOOL_NAME,
|
|
9
|
-
|
|
9
|
+
AGENT_TOOL_NAME,
|
|
10
10
|
} from "../constants/tools.js";
|
|
11
11
|
|
|
12
12
|
/**
|
|
@@ -94,7 +94,7 @@ export const grepTool: ToolPlugin = {
|
|
|
94
94
|
- Supports full regex syntax (e.g., "log.*Error", "function\\s+\\w+")
|
|
95
95
|
- Filter files with glob parameter (e.g., "*.js", "**/*.tsx") or type parameter (e.g., "js", "py", "rust")
|
|
96
96
|
- Output modes: "content" shows matching lines, "files_with_matches" shows only file paths (default), "count" shows match counts
|
|
97
|
-
- Use ${
|
|
97
|
+
- Use ${AGENT_TOOL_NAME} tool for open-ended searches requiring multiple rounds
|
|
98
98
|
- Pattern syntax: Uses ripgrep (not grep) - literal braces need escaping (use \`interface\\{\\}\` to find \`interface{}\` in Go code)
|
|
99
99
|
- Multiline matching: By default patterns match within single lines only. For cross-line patterns like \`struct \\{[\\s\\S]*?field\`, use \`multiline: true\`
|
|
100
100
|
`,
|
package/src/tools/skillTool.ts
CHANGED
|
@@ -155,14 +155,14 @@ export const skillTool: ToolPlugin = {
|
|
|
155
155
|
);
|
|
156
156
|
|
|
157
157
|
try {
|
|
158
|
-
const result = await subagentManager.
|
|
158
|
+
const result = await subagentManager.executeAgent(
|
|
159
159
|
instance,
|
|
160
160
|
skillResult.content,
|
|
161
161
|
context.abortSignal,
|
|
162
162
|
false,
|
|
163
163
|
);
|
|
164
164
|
|
|
165
|
-
// Cleanup subagent instance after
|
|
165
|
+
// Cleanup subagent instance after agent completion
|
|
166
166
|
subagentManager.cleanupInstance(instance.subagentId);
|
|
167
167
|
|
|
168
168
|
const messages = instance.messageManager.getMessages();
|
package/src/tools/types.ts
CHANGED
|
@@ -73,7 +73,7 @@ export interface ToolContext {
|
|
|
73
73
|
foregroundTaskManager?: import("../types/processes.js").IForegroundTaskManager;
|
|
74
74
|
/** Task manager instance for task management */
|
|
75
75
|
taskManager: import("../services/taskManager.js").TaskManager;
|
|
76
|
-
/** Subagent manager instance for
|
|
76
|
+
/** Subagent manager instance for agent delegation */
|
|
77
77
|
subagentManager?: import("../managers/subagentManager.js").SubagentManager;
|
|
78
78
|
/** Skill manager instance for skill invocation */
|
|
79
79
|
skillManager?: import("../managers/skillManager.js").SkillManager;
|