wave-agent-sdk 0.10.4 → 0.11.1
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/builtin/skills/init/SKILL.md +26 -0
- package/builtin/skills/loop/SKILL.md +53 -0
- package/builtin/skills/settings/ENV.md +64 -0
- package/builtin/skills/settings/HOOKS.md +94 -0
- package/builtin/skills/settings/MCP.md +55 -0
- package/builtin/skills/settings/MEMORY_RULES.md +60 -0
- package/{dist/builtin-skills → builtin/skills}/settings/SKILL.md +23 -16
- package/builtin/skills/settings/SKILLS.md +63 -0
- package/builtin/skills/settings/SUBAGENTS.md +60 -0
- package/builtin/subagents/bash.md +18 -0
- package/builtin/subagents/explore.md +42 -0
- package/builtin/subagents/general-purpose.md +20 -0
- package/builtin/subagents/plan.md +55 -0
- package/dist/agent.d.ts +8 -6
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +12 -9
- package/dist/constants/tools.d.ts +3 -0
- package/dist/constants/tools.d.ts.map +1 -1
- package/dist/constants/tools.js +3 -0
- 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 +0 -2
- package/dist/managers/aiManager.d.ts.map +1 -1
- package/dist/managers/aiManager.js +53 -14
- package/dist/managers/cronManager.d.ts +19 -0
- package/dist/managers/cronManager.d.ts.map +1 -0
- package/dist/managers/cronManager.js +124 -0
- package/dist/managers/hookManager.d.ts.map +1 -1
- package/dist/managers/hookManager.js +21 -13
- package/dist/managers/liveConfigManager.js +1 -1
- package/dist/managers/mcpManager.d.ts +1 -1
- package/dist/managers/mcpManager.d.ts.map +1 -1
- package/dist/managers/mcpManager.js +10 -2
- package/dist/managers/messageManager.d.ts +0 -1
- package/dist/managers/messageManager.d.ts.map +1 -1
- package/dist/managers/permissionManager.d.ts +27 -7
- package/dist/managers/permissionManager.d.ts.map +1 -1
- package/dist/managers/permissionManager.js +119 -14
- package/dist/managers/slashCommandManager.d.ts.map +1 -1
- package/dist/managers/slashCommandManager.js +7 -12
- package/dist/managers/subagentManager.d.ts +3 -0
- package/dist/managers/subagentManager.d.ts.map +1 -1
- package/dist/managers/subagentManager.js +10 -17
- package/dist/managers/toolManager.d.ts +1 -1
- package/dist/managers/toolManager.d.ts.map +1 -1
- package/dist/managers/toolManager.js +28 -4
- package/dist/prompts/index.d.ts +0 -5
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/index.js +1 -136
- package/dist/services/configurationService.d.ts.map +1 -1
- package/dist/services/configurationService.js +8 -7
- package/dist/services/hook.d.ts.map +1 -1
- package/dist/services/hook.js +3 -10
- package/dist/services/initializationService.js +2 -2
- package/dist/services/jsonlHandler.d.ts.map +1 -1
- package/dist/services/jsonlHandler.js +3 -0
- package/dist/services/reversionService.d.ts +2 -2
- package/dist/services/reversionService.d.ts.map +1 -1
- package/dist/services/reversionService.js +3 -3
- package/dist/services/session.d.ts.map +1 -1
- package/dist/services/session.js +18 -11
- package/dist/tools/agentTool.js +1 -1
- package/dist/tools/bashTool.d.ts.map +1 -1
- package/dist/tools/bashTool.js +5 -5
- package/dist/tools/cronCreateTool.d.ts +3 -0
- package/dist/tools/cronCreateTool.d.ts.map +1 -0
- package/dist/tools/cronCreateTool.js +59 -0
- package/dist/tools/cronDeleteTool.d.ts +3 -0
- package/dist/tools/cronDeleteTool.d.ts.map +1 -0
- package/dist/tools/cronDeleteTool.js +38 -0
- package/dist/tools/cronListTool.d.ts +3 -0
- package/dist/tools/cronListTool.d.ts.map +1 -0
- package/dist/tools/cronListTool.js +30 -0
- package/dist/tools/skillTool.d.ts +0 -3
- package/dist/tools/skillTool.d.ts.map +1 -1
- package/dist/tools/skillTool.js +4 -3
- package/dist/tools/taskOutputTool.d.ts.map +1 -1
- package/dist/tools/taskOutputTool.js +15 -8
- package/dist/tools/types.d.ts +2 -0
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/types/agent.d.ts +10 -0
- package/dist/types/agent.d.ts.map +1 -1
- package/dist/types/configuration.d.ts +1 -1
- package/dist/types/configuration.d.ts.map +1 -1
- package/dist/types/cron.d.ts +10 -0
- package/dist/types/cron.d.ts.map +1 -0
- package/dist/types/cron.js +1 -0
- package/dist/types/hooks.d.ts +1 -5
- package/dist/types/hooks.d.ts.map +1 -1
- package/dist/types/hooks.js +1 -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 +1 -1
- package/dist/types/messaging.d.ts.map +1 -1
- package/dist/utils/configPaths.d.ts +4 -0
- package/dist/utils/configPaths.d.ts.map +1 -1
- package/dist/utils/configPaths.js +11 -9
- package/dist/utils/containerSetup.d.ts.map +1 -1
- package/dist/utils/containerSetup.js +40 -13
- package/dist/utils/fileSearch.d.ts.map +1 -1
- package/dist/utils/fileSearch.js +7 -1
- package/dist/utils/mcpUtils.d.ts +2 -2
- package/dist/utils/mcpUtils.d.ts.map +1 -1
- package/dist/utils/mcpUtils.js +1 -5
- package/dist/utils/subagentParser.d.ts.map +1 -1
- package/dist/utils/subagentParser.js +14 -4
- package/package.json +4 -2
- package/src/agent.ts +17 -12
- package/src/constants/tools.ts +3 -0
- package/src/index.ts +1 -0
- package/src/managers/aiManager.ts +72 -24
- package/src/managers/cronManager.ts +167 -0
- package/src/managers/hookManager.ts +27 -13
- package/src/managers/liveConfigManager.ts +2 -2
- package/src/managers/mcpManager.ts +23 -2
- package/src/managers/messageManager.ts +0 -6
- package/src/managers/permissionManager.ts +154 -18
- package/src/managers/slashCommandManager.ts +7 -14
- package/src/managers/subagentManager.ts +15 -19
- package/src/managers/toolManager.ts +37 -4
- package/src/prompts/index.ts +0 -144
- package/src/services/configurationService.ts +8 -7
- package/src/services/hook.ts +5 -11
- package/src/services/initializationService.ts +3 -3
- package/src/services/jsonlHandler.ts +4 -0
- package/src/services/reversionService.ts +9 -4
- package/src/services/session.ts +19 -12
- package/src/tools/agentTool.ts +1 -1
- package/src/tools/bashTool.ts +6 -5
- package/src/tools/cronCreateTool.ts +73 -0
- package/src/tools/cronDeleteTool.ts +47 -0
- package/src/tools/cronListTool.ts +38 -0
- package/src/tools/skillTool.ts +6 -4
- package/src/tools/taskOutputTool.ts +14 -8
- package/src/tools/types.ts +2 -0
- package/src/types/agent.ts +10 -0
- package/src/types/configuration.ts +1 -1
- package/src/types/cron.ts +9 -0
- package/src/types/hooks.ts +5 -9
- package/src/types/index.ts +1 -0
- package/src/types/messaging.ts +1 -1
- package/src/utils/configPaths.ts +12 -10
- package/src/utils/containerSetup.ts +50 -16
- package/src/utils/fileSearch.ts +7 -1
- package/src/utils/mcpUtils.ts +2 -5
- package/src/utils/subagentParser.ts +16 -6
- package/dist/builtin-skills/settings/HOOKS.md +0 -95
- package/dist/utils/builtinSubagents.d.ts +0 -7
- package/dist/utils/builtinSubagents.d.ts.map +0 -1
- package/dist/utils/builtinSubagents.js +0 -94
- package/src/builtin-skills/settings/HOOKS.md +0 -95
- package/src/builtin-skills/settings/SKILL.md +0 -86
- package/src/utils/builtinSubagents.ts +0 -122
|
@@ -347,6 +347,7 @@ export class McpManager {
|
|
|
347
347
|
async executeMcpTool(
|
|
348
348
|
toolName: string,
|
|
349
349
|
args: Record<string, unknown>,
|
|
350
|
+
context?: ToolContext,
|
|
350
351
|
): Promise<{
|
|
351
352
|
success: boolean;
|
|
352
353
|
content: string;
|
|
@@ -360,6 +361,23 @@ export class McpManager {
|
|
|
360
361
|
);
|
|
361
362
|
}
|
|
362
363
|
|
|
364
|
+
// Permission check
|
|
365
|
+
if (context?.permissionManager) {
|
|
366
|
+
const permissionContext = context.permissionManager.createContext(
|
|
367
|
+
toolName,
|
|
368
|
+
context.permissionMode || "default",
|
|
369
|
+
context.canUseToolCallback,
|
|
370
|
+
args,
|
|
371
|
+
context.toolCallId,
|
|
372
|
+
);
|
|
373
|
+
|
|
374
|
+
const decision =
|
|
375
|
+
await context.permissionManager.checkPermission(permissionContext);
|
|
376
|
+
if (decision.behavior === "deny") {
|
|
377
|
+
throw new Error(decision.message || "Permission denied");
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
363
381
|
const parts = toolName.split("__");
|
|
364
382
|
if (parts.length < 3) {
|
|
365
383
|
throw new Error(
|
|
@@ -479,8 +497,11 @@ export class McpManager {
|
|
|
479
497
|
const plugin = createMcpToolPlugin(
|
|
480
498
|
tool,
|
|
481
499
|
server.name,
|
|
482
|
-
(
|
|
483
|
-
|
|
500
|
+
(
|
|
501
|
+
name: string,
|
|
502
|
+
args: Record<string, unknown>,
|
|
503
|
+
context?: ToolContext,
|
|
504
|
+
) => this.executeMcpTool(name, args, context),
|
|
484
505
|
);
|
|
485
506
|
mcpTools.set(plugin.name, plugin);
|
|
486
507
|
}
|
|
@@ -45,12 +45,6 @@ export interface MessageManagerCallbacks {
|
|
|
45
45
|
onErrorBlockAdded?: (error: string) => void;
|
|
46
46
|
onCompressBlockAdded?: (content: string) => void;
|
|
47
47
|
onCompressionStateChange?: (isCompressing: boolean) => void;
|
|
48
|
-
onMemoryBlockAdded?: (
|
|
49
|
-
content: string,
|
|
50
|
-
success: boolean,
|
|
51
|
-
type: "project" | "user",
|
|
52
|
-
storagePath: string,
|
|
53
|
-
) => void;
|
|
54
48
|
// Bang callback
|
|
55
49
|
onAddBangMessage?: (command: string) => void;
|
|
56
50
|
onUpdateBangMessage?: (command: string, output: string) => void;
|
|
@@ -92,12 +92,16 @@ const DEFAULT_ALLOWED_RULES = [
|
|
|
92
92
|
import { logger } from "../utils/globalLogger.js";
|
|
93
93
|
|
|
94
94
|
export interface PermissionManagerOptions {
|
|
95
|
-
/** Configured
|
|
96
|
-
|
|
95
|
+
/** Configured permission mode from settings */
|
|
96
|
+
configuredPermissionMode?: PermissionMode;
|
|
97
97
|
/** Allowed rules from settings */
|
|
98
98
|
allowedRules?: string[];
|
|
99
99
|
/** Denied rules from settings */
|
|
100
100
|
deniedRules?: string[];
|
|
101
|
+
/** Instance-specific allowed rules (from AgentOptions) */
|
|
102
|
+
instanceAllowedRules?: string[];
|
|
103
|
+
/** Instance-specific denied rules (from AgentOptions) */
|
|
104
|
+
instanceDeniedRules?: string[];
|
|
101
105
|
/** Additional directories considered part of the Safe Zone */
|
|
102
106
|
additionalDirectories?: string[];
|
|
103
107
|
/** The main working directory */
|
|
@@ -109,61 +113,70 @@ export interface PermissionManagerOptions {
|
|
|
109
113
|
}
|
|
110
114
|
|
|
111
115
|
export class PermissionManager {
|
|
112
|
-
private
|
|
116
|
+
private configuredPermissionMode?: PermissionMode;
|
|
113
117
|
private allowedRules: string[] = [];
|
|
114
118
|
private deniedRules: string[] = [];
|
|
119
|
+
private instanceAllowedRules: string[] = [];
|
|
120
|
+
private instanceDeniedRules: string[] = [];
|
|
115
121
|
private temporaryRules: string[] = [];
|
|
116
122
|
private additionalDirectories: string[] = [];
|
|
117
123
|
private systemAdditionalDirectories: string[] = [];
|
|
118
124
|
private workdir?: string;
|
|
119
125
|
private planFilePath?: string;
|
|
120
|
-
private
|
|
126
|
+
private worktreeName?: string;
|
|
127
|
+
private mainRepoRoot?: string;
|
|
128
|
+
private onConfiguredPermissionModeChange?: (mode: PermissionMode) => void;
|
|
121
129
|
private _logger?: Logger;
|
|
122
130
|
|
|
123
131
|
constructor(
|
|
124
132
|
private container: Container,
|
|
125
133
|
options: PermissionManagerOptions = {},
|
|
126
134
|
) {
|
|
127
|
-
this.
|
|
135
|
+
this.configuredPermissionMode = options.configuredPermissionMode;
|
|
128
136
|
this.allowedRules = options.allowedRules || [];
|
|
129
137
|
this.deniedRules = options.deniedRules || [];
|
|
138
|
+
this.instanceAllowedRules = options.instanceAllowedRules || [];
|
|
139
|
+
this.instanceDeniedRules = options.instanceDeniedRules || [];
|
|
130
140
|
this.workdir = options.workdir;
|
|
131
141
|
this.planFilePath = options.planFilePath;
|
|
132
142
|
this._logger = options.logger;
|
|
133
143
|
this.updateAdditionalDirectories(options.additionalDirectories || []);
|
|
144
|
+
|
|
145
|
+
this.worktreeName = this.container.get<string>("WorktreeName");
|
|
146
|
+
this.mainRepoRoot = this.container.get<string>("MainRepoRoot");
|
|
134
147
|
}
|
|
135
148
|
|
|
136
149
|
/**
|
|
137
150
|
* Set a callback to be notified when the effective permission mode changes due to configuration updates
|
|
138
151
|
*/
|
|
139
|
-
public
|
|
152
|
+
public setOnConfiguredPermissionModeChange(
|
|
140
153
|
callback: (mode: PermissionMode) => void,
|
|
141
154
|
): void {
|
|
142
|
-
this.
|
|
155
|
+
this.onConfiguredPermissionModeChange = callback;
|
|
143
156
|
}
|
|
144
157
|
|
|
145
158
|
/**
|
|
146
159
|
* Update the configured default mode (e.g., when configuration reloads)
|
|
147
160
|
*/
|
|
148
|
-
|
|
161
|
+
updateConfiguredPermissionMode(permissionMode?: PermissionMode): void {
|
|
149
162
|
const oldEffectiveMode = this.getCurrentEffectiveMode();
|
|
150
163
|
|
|
151
|
-
this.
|
|
164
|
+
this.configuredPermissionMode = permissionMode;
|
|
152
165
|
|
|
153
166
|
const newEffectiveMode = this.getCurrentEffectiveMode();
|
|
154
167
|
if (
|
|
155
168
|
oldEffectiveMode !== newEffectiveMode &&
|
|
156
|
-
this.
|
|
169
|
+
this.onConfiguredPermissionModeChange
|
|
157
170
|
) {
|
|
158
|
-
this.
|
|
171
|
+
this.onConfiguredPermissionModeChange(newEffectiveMode);
|
|
159
172
|
}
|
|
160
173
|
}
|
|
161
174
|
|
|
162
175
|
/**
|
|
163
176
|
* Get the configured default mode
|
|
164
177
|
*/
|
|
165
|
-
public
|
|
166
|
-
return this.
|
|
178
|
+
public getConfiguredPermissionMode(): PermissionMode | undefined {
|
|
179
|
+
return this.configuredPermissionMode;
|
|
167
180
|
}
|
|
168
181
|
|
|
169
182
|
/**
|
|
@@ -180,6 +193,20 @@ export class PermissionManager {
|
|
|
180
193
|
return [...this.deniedRules];
|
|
181
194
|
}
|
|
182
195
|
|
|
196
|
+
/**
|
|
197
|
+
* Get all instance-specific allowed rules
|
|
198
|
+
*/
|
|
199
|
+
public getInstanceAllowedRules(): string[] {
|
|
200
|
+
return [...this.instanceAllowedRules];
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Get all instance-specific denied rules
|
|
205
|
+
*/
|
|
206
|
+
public getInstanceDeniedRules(): string[] {
|
|
207
|
+
return [...this.instanceDeniedRules];
|
|
208
|
+
}
|
|
209
|
+
|
|
183
210
|
/**
|
|
184
211
|
* Get all additional directories
|
|
185
212
|
*/
|
|
@@ -325,8 +352,8 @@ export class PermissionManager {
|
|
|
325
352
|
}
|
|
326
353
|
|
|
327
354
|
// Use configured default mode if available
|
|
328
|
-
if (this.
|
|
329
|
-
return this.
|
|
355
|
+
if (this.configuredPermissionMode !== undefined) {
|
|
356
|
+
return this.configuredPermissionMode;
|
|
330
357
|
}
|
|
331
358
|
|
|
332
359
|
// Fall back to system default
|
|
@@ -340,6 +367,54 @@ export class PermissionManager {
|
|
|
340
367
|
async checkPermission(
|
|
341
368
|
context: ToolPermissionContext,
|
|
342
369
|
): Promise<PermissionDecision> {
|
|
370
|
+
// 0. Check instance-specific denied rules first - Deny always takes precedence
|
|
371
|
+
for (const rule of this.instanceDeniedRules) {
|
|
372
|
+
if (this.matchesRule(context, rule)) {
|
|
373
|
+
logger?.warn("Permission denied by instance rule", {
|
|
374
|
+
toolName: context.toolName,
|
|
375
|
+
rule,
|
|
376
|
+
});
|
|
377
|
+
return {
|
|
378
|
+
behavior: "deny",
|
|
379
|
+
message: `Access to tool '${context.toolName}' is explicitly denied by instance rule: ${rule}`,
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// 0. Check worktree safety for Write and Edit tools
|
|
385
|
+
if (
|
|
386
|
+
this.worktreeName &&
|
|
387
|
+
this.mainRepoRoot &&
|
|
388
|
+
this.workdir &&
|
|
389
|
+
(context.toolName === WRITE_TOOL_NAME ||
|
|
390
|
+
context.toolName === EDIT_TOOL_NAME)
|
|
391
|
+
) {
|
|
392
|
+
const targetPath = context.toolInput?.file_path as string | undefined;
|
|
393
|
+
if (targetPath) {
|
|
394
|
+
const absoluteTargetPath = path.resolve(this.workdir, targetPath);
|
|
395
|
+
const isInsideMainRepo = isPathInside(
|
|
396
|
+
absoluteTargetPath,
|
|
397
|
+
this.mainRepoRoot,
|
|
398
|
+
);
|
|
399
|
+
const isInsideWorktree = isPathInside(absoluteTargetPath, this.workdir);
|
|
400
|
+
|
|
401
|
+
// If it's inside the main repo but NOT inside the current worktree
|
|
402
|
+
if (isInsideMainRepo && !isInsideWorktree) {
|
|
403
|
+
logger?.warn("Worktree safety violation", {
|
|
404
|
+
toolName: context.toolName,
|
|
405
|
+
targetPath,
|
|
406
|
+
worktreeName: this.worktreeName,
|
|
407
|
+
mainRepoRoot: this.mainRepoRoot,
|
|
408
|
+
workdir: this.workdir,
|
|
409
|
+
});
|
|
410
|
+
return {
|
|
411
|
+
behavior: "deny",
|
|
412
|
+
message: `Access denied: You are currently in a worktree session ("${this.worktreeName}"). Modifying files in the main repository (outside the worktree) is not allowed. Please only modify files within the worktree directory: ${this.workdir}`,
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
343
418
|
// 0. Check denied rules first - Deny always takes precedence
|
|
344
419
|
for (const rule of this.deniedRules) {
|
|
345
420
|
if (this.matchesRule(context, rule)) {
|
|
@@ -359,7 +434,7 @@ export class PermissionManager {
|
|
|
359
434
|
return { behavior: "allow" };
|
|
360
435
|
}
|
|
361
436
|
|
|
362
|
-
// 1.1 If acceptEdits mode, allow Edit, Write
|
|
437
|
+
// 1.1 If acceptEdits mode, allow Edit, Write, and mkdir in safe zone
|
|
363
438
|
if (context.permissionMode === "acceptEdits") {
|
|
364
439
|
const autoAcceptedTools = [EDIT_TOOL_NAME, WRITE_TOOL_NAME];
|
|
365
440
|
if (autoAcceptedTools.includes(context.toolName)) {
|
|
@@ -387,6 +462,40 @@ export class PermissionManager {
|
|
|
387
462
|
}
|
|
388
463
|
}
|
|
389
464
|
}
|
|
465
|
+
|
|
466
|
+
// Special case for mkdir in Bash tool
|
|
467
|
+
if (context.toolName === BASH_TOOL_NAME && context.toolInput?.command) {
|
|
468
|
+
const command = String(context.toolInput.command).trim();
|
|
469
|
+
if (command.startsWith("mkdir ")) {
|
|
470
|
+
const parts = splitBashCommand(command);
|
|
471
|
+
// Check if it's a simple mkdir command (first part is mkdir)
|
|
472
|
+
if (parts.length === 1) {
|
|
473
|
+
const processedPart = stripEnvVars(parts[0]);
|
|
474
|
+
if (processedPart.startsWith("mkdir ")) {
|
|
475
|
+
const args = processedPart.slice(6).trim();
|
|
476
|
+
const pathArgs =
|
|
477
|
+
(args.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g) || []).filter(
|
|
478
|
+
(arg) => !arg.startsWith("-"),
|
|
479
|
+
) || [];
|
|
480
|
+
|
|
481
|
+
if (pathArgs.length > 0) {
|
|
482
|
+
const allPathsSafe = pathArgs.every((pathArg) => {
|
|
483
|
+
const cleanPath = pathArg.replace(/^['"](.*)['"]$/, "$1");
|
|
484
|
+
const { isInside } = this.isInsideSafeZone(
|
|
485
|
+
cleanPath,
|
|
486
|
+
context.toolInput?.workdir as string | undefined,
|
|
487
|
+
);
|
|
488
|
+
return isInside;
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
if (allPathsSafe) {
|
|
492
|
+
return { behavior: "allow" };
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
390
499
|
}
|
|
391
500
|
|
|
392
501
|
// 1.2 Check if tool call is allowed by persistent or temporary rules
|
|
@@ -475,7 +584,27 @@ export class PermissionManager {
|
|
|
475
584
|
* Determine if a tool requires permission checks based on its name
|
|
476
585
|
*/
|
|
477
586
|
isRestrictedTool(toolName: string): boolean {
|
|
478
|
-
return (
|
|
587
|
+
return (
|
|
588
|
+
(RESTRICTED_TOOLS as readonly string[]).includes(toolName) ||
|
|
589
|
+
toolName.startsWith("mcp__")
|
|
590
|
+
);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Check if a tool is completely denied by name in instance or global rules
|
|
595
|
+
*/
|
|
596
|
+
public isToolDenied(toolName: string): boolean {
|
|
597
|
+
// Check instance-specific denied rules
|
|
598
|
+
if (this.instanceDeniedRules.includes(toolName)) {
|
|
599
|
+
return true;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
// Check global denied rules
|
|
603
|
+
if (this.deniedRules.includes(toolName)) {
|
|
604
|
+
return true;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
return false;
|
|
479
608
|
}
|
|
480
609
|
|
|
481
610
|
/**
|
|
@@ -497,6 +626,8 @@ export class PermissionManager {
|
|
|
497
626
|
const processedPart = stripRedirections(stripEnvVars(parts[0]));
|
|
498
627
|
suggestedPrefix = getSmartPrefix(processedPart) ?? undefined;
|
|
499
628
|
}
|
|
629
|
+
} else if (toolName.startsWith("mcp__")) {
|
|
630
|
+
suggestedPrefix = toolName;
|
|
500
631
|
}
|
|
501
632
|
|
|
502
633
|
const context: ToolPermissionContext = {
|
|
@@ -742,7 +873,12 @@ export class PermissionManager {
|
|
|
742
873
|
return rules.some((rule) => this.matchesRule(ctx, rule));
|
|
743
874
|
};
|
|
744
875
|
|
|
745
|
-
// Check
|
|
876
|
+
// Check instance-specific allowed rules first
|
|
877
|
+
if (isAllowedByRuleList(context, this.instanceAllowedRules)) {
|
|
878
|
+
return true;
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
// Check temporary rules
|
|
746
882
|
if (isAllowedByRuleList(context, this.temporaryRules)) {
|
|
747
883
|
return true;
|
|
748
884
|
}
|
|
@@ -16,7 +16,6 @@ import {
|
|
|
16
16
|
replaceBashCommandsWithOutput,
|
|
17
17
|
executeBashCommands,
|
|
18
18
|
} from "../utils/markdownParser.js";
|
|
19
|
-
import { INIT_PROMPT } from "../prompts/index.js";
|
|
20
19
|
import type { SkillManager } from "./skillManager.js";
|
|
21
20
|
import type { SkillMetadata } from "../types/skills.js";
|
|
22
21
|
|
|
@@ -73,21 +72,15 @@ export class SlashCommandManager {
|
|
|
73
72
|
}
|
|
74
73
|
|
|
75
74
|
private initializeBuiltinCommands(): void {
|
|
76
|
-
// Register built-in
|
|
75
|
+
// Register built-in clear command
|
|
77
76
|
this.registerCommand({
|
|
78
|
-
id: "
|
|
79
|
-
name: "
|
|
80
|
-
description:
|
|
81
|
-
"Initialize repository for AI agents by generating AGENTS.md",
|
|
77
|
+
id: "clear",
|
|
78
|
+
name: "clear",
|
|
79
|
+
description: "Clear conversation history and reset session",
|
|
82
80
|
handler: async () => {
|
|
83
|
-
|
|
84
|
-
this.messageManager.
|
|
85
|
-
|
|
86
|
-
customCommandContent: INIT_PROMPT,
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
// Execute the AI conversation with the init prompt
|
|
90
|
-
await this.aiManager.sendAIMessage();
|
|
81
|
+
this.aiManager.abortAIMessage();
|
|
82
|
+
this.messageManager.clearMessages();
|
|
83
|
+
await this.taskManager.syncWithSession();
|
|
91
84
|
},
|
|
92
85
|
});
|
|
93
86
|
}
|
|
@@ -80,6 +80,7 @@ export interface SubagentManagerOptions {
|
|
|
80
80
|
workdir: string;
|
|
81
81
|
callbacks?: SubagentManagerCallbacks; // Use SubagentManagerCallbacks instead of parentCallbacks
|
|
82
82
|
onUsageAdded?: (usage: Usage) => void;
|
|
83
|
+
stream: boolean;
|
|
83
84
|
}
|
|
84
85
|
|
|
85
86
|
export class SubagentManager {
|
|
@@ -90,12 +91,14 @@ export class SubagentManager {
|
|
|
90
91
|
private callbacks?: SubagentManagerCallbacks; // Use SubagentManagerCallbacks instead of parentCallbacks
|
|
91
92
|
private onUsageAdded?: (usage: Usage) => void;
|
|
92
93
|
private container: Container;
|
|
94
|
+
private stream: boolean;
|
|
93
95
|
|
|
94
96
|
constructor(container: Container, options: SubagentManagerOptions) {
|
|
95
97
|
this.container = container;
|
|
96
98
|
this.workdir = options.workdir;
|
|
97
99
|
this.callbacks = options.callbacks; // Store SubagentManagerCallbacks
|
|
98
100
|
this.onUsageAdded = options.onUsageAdded;
|
|
101
|
+
this.stream = options.stream;
|
|
99
102
|
}
|
|
100
103
|
|
|
101
104
|
private get configurationService(): ConfigurationService {
|
|
@@ -155,6 +158,7 @@ export class SubagentManager {
|
|
|
155
158
|
subagent_type: string;
|
|
156
159
|
allowedTools?: string[];
|
|
157
160
|
model?: string;
|
|
161
|
+
stream?: boolean;
|
|
158
162
|
},
|
|
159
163
|
runInBackground?: boolean,
|
|
160
164
|
onUpdate?: () => void,
|
|
@@ -178,10 +182,16 @@ export class SubagentManager {
|
|
|
178
182
|
this.container.get<PermissionManager>("PermissionManager");
|
|
179
183
|
const subagentPermissionManager = new PermissionManager(subagentContainer, {
|
|
180
184
|
workdir: this.workdir,
|
|
181
|
-
|
|
182
|
-
parentPermissionManager?.
|
|
185
|
+
configuredPermissionMode:
|
|
186
|
+
parentPermissionManager?.getConfiguredPermissionMode(),
|
|
183
187
|
allowedRules: parentPermissionManager?.getAllowedRules(),
|
|
184
188
|
deniedRules: parentPermissionManager?.getDeniedRules(),
|
|
189
|
+
instanceAllowedRules:
|
|
190
|
+
parentPermissionManager?.getInstanceAllowedRules?.(),
|
|
191
|
+
instanceDeniedRules: [
|
|
192
|
+
...(parentPermissionManager?.getInstanceDeniedRules?.() || []),
|
|
193
|
+
AGENT_TOOL_NAME, // Always deny Agent tool in subagents to prevent recursion
|
|
194
|
+
],
|
|
185
195
|
additionalDirectories:
|
|
186
196
|
parentPermissionManager?.getAdditionalDirectories(),
|
|
187
197
|
planFilePath: parentPermissionManager?.getPlanFilePath(),
|
|
@@ -222,6 +232,7 @@ export class SubagentManager {
|
|
|
222
232
|
systemPrompt: configuration.systemPrompt,
|
|
223
233
|
subagentType: parameters.subagent_type, // Pass subagent type for hook context
|
|
224
234
|
modelOverride: parameters.model || configuration.model, // Pass model override
|
|
235
|
+
stream: parameters.stream ?? this.stream, // Pass streaming mode flag
|
|
225
236
|
callbacks: {
|
|
226
237
|
onUsageAdded: this.onUsageAdded,
|
|
227
238
|
},
|
|
@@ -410,24 +421,9 @@ export class SubagentManager {
|
|
|
410
421
|
// Add the user's prompt as a message
|
|
411
422
|
instance.messageManager.addUserMessage({ content: prompt });
|
|
412
423
|
|
|
413
|
-
//
|
|
414
|
-
// Use instance.configuration.tools if provided, otherwise fallback to all tools
|
|
415
|
-
let enabledTools = instance.configuration.tools;
|
|
416
|
-
|
|
417
|
-
// Always filter out the Agent tool to prevent subagents from creating sub-subagents
|
|
418
|
-
if (enabledTools) {
|
|
419
|
-
enabledTools = enabledTools.filter((tool) => tool !== AGENT_TOOL_NAME);
|
|
420
|
-
} else {
|
|
421
|
-
// If no tools specified, get all tools except Agent
|
|
422
|
-
const allTools = instance.toolManager.list().map((tool) => tool.name);
|
|
423
|
-
enabledTools = allTools.filter((tool) => tool !== AGENT_TOOL_NAME);
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
// Execute the AI request with tool restrictions
|
|
424
|
+
// Execute the AI request
|
|
427
425
|
// The AIManager will handle abort signals through its own abort controllers
|
|
428
|
-
const executeAI = instance.aiManager.sendAIMessage(
|
|
429
|
-
tools: enabledTools,
|
|
430
|
-
});
|
|
426
|
+
const executeAI = instance.aiManager.sendAIMessage();
|
|
431
427
|
|
|
432
428
|
// If we have an abort signal, race against it using utilities to prevent listener accumulation
|
|
433
429
|
if (abortSignal && !instance.backgroundTaskId) {
|
|
@@ -6,6 +6,9 @@ import { editTool } from "../tools/editTool.js";
|
|
|
6
6
|
import { writeTool } from "../tools/writeTool.js";
|
|
7
7
|
import { exitPlanModeTool } from "../tools/exitPlanMode.js";
|
|
8
8
|
import { askUserQuestionTool } from "../tools/askUserQuestion.js";
|
|
9
|
+
import { cronCreateTool } from "../tools/cronCreateTool.js";
|
|
10
|
+
import { cronDeleteTool } from "../tools/cronDeleteTool.js";
|
|
11
|
+
import { cronListTool } from "../tools/cronListTool.js";
|
|
9
12
|
// New tools
|
|
10
13
|
import { globTool } from "../tools/globTool.js";
|
|
11
14
|
import { grepTool } from "../tools/grepTool.js";
|
|
@@ -114,6 +117,9 @@ class ToolManager {
|
|
|
114
117
|
taskGetTool,
|
|
115
118
|
taskUpdateTool,
|
|
116
119
|
taskListTool,
|
|
120
|
+
cronCreateTool,
|
|
121
|
+
cronDeleteTool,
|
|
122
|
+
cronListTool,
|
|
117
123
|
];
|
|
118
124
|
|
|
119
125
|
for (const tool of builtInTools) {
|
|
@@ -124,9 +130,17 @@ class ToolManager {
|
|
|
124
130
|
}
|
|
125
131
|
|
|
126
132
|
/**
|
|
127
|
-
* Check if a tool should be enabled based on tools configuration
|
|
133
|
+
* Check if a tool should be enabled based on tools configuration and permission rules
|
|
128
134
|
*/
|
|
129
135
|
private shouldEnableTool(name: string): boolean {
|
|
136
|
+
const permissionManager =
|
|
137
|
+
this.container.get<PermissionManager>("PermissionManager");
|
|
138
|
+
|
|
139
|
+
// If tool is explicitly denied by name in permission rules, filter it out
|
|
140
|
+
if (permissionManager?.isToolDenied(name)) {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
|
|
130
144
|
if (!this.tools) {
|
|
131
145
|
return true;
|
|
132
146
|
}
|
|
@@ -194,6 +208,11 @@ class ToolManager {
|
|
|
194
208
|
skillManager: this.container.has("SkillManager")
|
|
195
209
|
? this.container.get<SkillManager>("SkillManager")
|
|
196
210
|
: undefined,
|
|
211
|
+
cronManager: this.container.has("CronManager")
|
|
212
|
+
? this.container.get<import("./cronManager.js").CronManager>(
|
|
213
|
+
"CronManager",
|
|
214
|
+
)
|
|
215
|
+
: undefined,
|
|
197
216
|
sessionId: context.sessionId,
|
|
198
217
|
toolCallId: context.toolCallId,
|
|
199
218
|
};
|
|
@@ -241,8 +260,14 @@ class ToolManager {
|
|
|
241
260
|
}
|
|
242
261
|
|
|
243
262
|
list(): ToolPlugin[] {
|
|
244
|
-
const
|
|
245
|
-
|
|
263
|
+
const permissionManager =
|
|
264
|
+
this.container.get<PermissionManager>("PermissionManager");
|
|
265
|
+
const builtInTools = Array.from(this.toolsRegistry.values()).filter(
|
|
266
|
+
(tool) => !permissionManager?.isToolDenied(tool.name),
|
|
267
|
+
);
|
|
268
|
+
const mcpTools = this.mcpManager
|
|
269
|
+
.getMcpToolPlugins()
|
|
270
|
+
.filter((tool) => !permissionManager?.isToolDenied(tool.name));
|
|
246
271
|
return [...builtInTools, ...mcpTools];
|
|
247
272
|
}
|
|
248
273
|
|
|
@@ -251,9 +276,15 @@ class ToolManager {
|
|
|
251
276
|
availableSkills?: SkillMetadata[];
|
|
252
277
|
workdir?: string;
|
|
253
278
|
}): ChatCompletionFunctionTool[] {
|
|
279
|
+
const permissionManager =
|
|
280
|
+
this.container.get<PermissionManager>("PermissionManager");
|
|
254
281
|
const effectivePermissionMode = this.getPermissionMode();
|
|
255
282
|
const builtInToolsConfig = Array.from(this.toolsRegistry.values())
|
|
256
283
|
.filter((tool) => {
|
|
284
|
+
// If tool is explicitly denied by name in permission rules, filter it out
|
|
285
|
+
if (permissionManager?.isToolDenied(tool.name)) {
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
257
288
|
if (effectivePermissionMode === "bypassPermissions") {
|
|
258
289
|
if (tool.name === "ExitPlanMode" || tool.name === "AskUserQuestion") {
|
|
259
290
|
return false;
|
|
@@ -278,7 +309,9 @@ class ToolManager {
|
|
|
278
309
|
}
|
|
279
310
|
return config;
|
|
280
311
|
});
|
|
281
|
-
const mcpToolsConfig = this.mcpManager
|
|
312
|
+
const mcpToolsConfig = this.mcpManager
|
|
313
|
+
.getMcpToolsConfig()
|
|
314
|
+
.filter((tool) => !permissionManager?.isToolDenied(tool.function.name));
|
|
282
315
|
return [...builtInToolsConfig, ...mcpToolsConfig];
|
|
283
316
|
}
|
|
284
317
|
|