wave-agent-sdk 0.0.7 → 0.0.10
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 +105 -24
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +438 -53
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/managers/aiManager.d.ts +18 -7
- package/dist/managers/aiManager.d.ts.map +1 -1
- package/dist/managers/aiManager.js +254 -142
- package/dist/managers/backgroundBashManager.d.ts.map +1 -1
- package/dist/managers/backgroundBashManager.js +11 -9
- package/dist/managers/hookManager.d.ts +6 -6
- package/dist/managers/hookManager.d.ts.map +1 -1
- package/dist/managers/hookManager.js +81 -39
- package/dist/managers/liveConfigManager.d.ts +95 -0
- package/dist/managers/liveConfigManager.d.ts.map +1 -0
- package/dist/managers/liveConfigManager.js +442 -0
- package/dist/managers/lspManager.d.ts +43 -0
- package/dist/managers/lspManager.d.ts.map +1 -0
- package/dist/managers/lspManager.js +326 -0
- package/dist/managers/messageManager.d.ts +41 -24
- package/dist/managers/messageManager.d.ts.map +1 -1
- package/dist/managers/messageManager.js +184 -73
- package/dist/managers/permissionManager.d.ts +66 -0
- package/dist/managers/permissionManager.d.ts.map +1 -0
- package/dist/managers/permissionManager.js +208 -0
- package/dist/managers/skillManager.d.ts +1 -0
- package/dist/managers/skillManager.d.ts.map +1 -1
- package/dist/managers/skillManager.js +2 -1
- package/dist/managers/slashCommandManager.d.ts.map +1 -1
- package/dist/managers/slashCommandManager.js +4 -2
- package/dist/managers/subagentManager.d.ts +42 -6
- package/dist/managers/subagentManager.d.ts.map +1 -1
- package/dist/managers/subagentManager.js +213 -62
- package/dist/managers/toolManager.d.ts +38 -1
- package/dist/managers/toolManager.d.ts.map +1 -1
- package/dist/managers/toolManager.js +66 -2
- package/dist/services/aiService.d.ts +15 -5
- package/dist/services/aiService.d.ts.map +1 -1
- package/dist/services/aiService.js +446 -77
- package/dist/services/configurationService.d.ts +116 -0
- package/dist/services/configurationService.d.ts.map +1 -0
- package/dist/services/configurationService.js +585 -0
- package/dist/services/fileWatcher.d.ts +69 -0
- package/dist/services/fileWatcher.d.ts.map +1 -0
- package/dist/services/fileWatcher.js +212 -0
- package/dist/services/hook.d.ts +5 -40
- package/dist/services/hook.d.ts.map +1 -1
- package/dist/services/hook.js +47 -109
- package/dist/services/jsonlHandler.d.ts +71 -0
- package/dist/services/jsonlHandler.d.ts.map +1 -0
- package/dist/services/jsonlHandler.js +236 -0
- package/dist/services/memory.d.ts.map +1 -1
- package/dist/services/memory.js +33 -11
- package/dist/services/session.d.ts +116 -52
- package/dist/services/session.d.ts.map +1 -1
- package/dist/services/session.js +415 -143
- package/dist/tools/bashTool.d.ts.map +1 -1
- package/dist/tools/bashTool.js +77 -17
- package/dist/tools/deleteFileTool.d.ts.map +1 -1
- package/dist/tools/deleteFileTool.js +27 -1
- package/dist/tools/editTool.d.ts.map +1 -1
- package/dist/tools/editTool.js +33 -8
- package/dist/tools/lspTool.d.ts +6 -0
- package/dist/tools/lspTool.d.ts.map +1 -0
- package/dist/tools/lspTool.js +589 -0
- package/dist/tools/multiEditTool.d.ts.map +1 -1
- package/dist/tools/multiEditTool.js +30 -10
- package/dist/tools/readTool.d.ts.map +1 -1
- package/dist/tools/readTool.js +113 -3
- package/dist/tools/skillTool.js +2 -2
- package/dist/tools/todoWriteTool.d.ts.map +1 -1
- package/dist/tools/todoWriteTool.js +23 -0
- package/dist/tools/types.d.ts +11 -8
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/tools/writeTool.d.ts.map +1 -1
- package/dist/tools/writeTool.js +30 -15
- package/dist/types/commands.d.ts +4 -1
- package/dist/types/commands.d.ts.map +1 -1
- package/dist/types/config.d.ts +4 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/configuration.d.ts +69 -0
- package/dist/types/configuration.d.ts.map +1 -0
- package/dist/types/configuration.js +8 -0
- package/dist/types/core.d.ts +45 -0
- package/dist/types/core.d.ts.map +1 -1
- package/dist/types/environment.d.ts +83 -0
- package/dist/types/environment.d.ts.map +1 -0
- package/dist/types/environment.js +21 -0
- package/dist/types/fileSearch.d.ts +5 -0
- package/dist/types/fileSearch.d.ts.map +1 -0
- package/dist/types/fileSearch.js +1 -0
- package/dist/types/hooks.d.ts +18 -3
- package/dist/types/hooks.d.ts.map +1 -1
- package/dist/types/hooks.js +8 -8
- package/dist/types/index.d.ts +7 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +7 -0
- package/dist/types/lsp.d.ts +90 -0
- package/dist/types/lsp.d.ts.map +1 -0
- package/dist/types/lsp.js +4 -0
- package/dist/types/messaging.d.ts +19 -12
- package/dist/types/messaging.d.ts.map +1 -1
- package/dist/types/permissions.d.ts +35 -0
- package/dist/types/permissions.d.ts.map +1 -0
- package/dist/types/permissions.js +12 -0
- package/dist/types/session.d.ts +15 -0
- package/dist/types/session.d.ts.map +1 -0
- package/dist/types/session.js +7 -0
- package/dist/types/skills.d.ts +1 -0
- package/dist/types/skills.d.ts.map +1 -1
- package/dist/types/tools.d.ts +35 -0
- package/dist/types/tools.d.ts.map +1 -0
- package/dist/types/tools.js +4 -0
- package/dist/utils/abortUtils.d.ts +34 -0
- package/dist/utils/abortUtils.d.ts.map +1 -0
- package/dist/utils/abortUtils.js +92 -0
- package/dist/utils/bashHistory.d.ts +4 -0
- package/dist/utils/bashHistory.d.ts.map +1 -1
- package/dist/utils/bashHistory.js +48 -30
- package/dist/utils/builtinSubagents.d.ts +7 -0
- package/dist/utils/builtinSubagents.d.ts.map +1 -0
- package/dist/utils/builtinSubagents.js +65 -0
- package/dist/utils/cacheControlUtils.d.ts +96 -0
- package/dist/utils/cacheControlUtils.d.ts.map +1 -0
- package/dist/utils/cacheControlUtils.js +324 -0
- package/dist/utils/commandPathResolver.d.ts +52 -0
- package/dist/utils/commandPathResolver.d.ts.map +1 -0
- package/dist/utils/commandPathResolver.js +145 -0
- package/dist/utils/configPaths.d.ts +85 -0
- package/dist/utils/configPaths.d.ts.map +1 -0
- package/dist/utils/configPaths.js +121 -0
- package/dist/utils/constants.d.ts +1 -13
- package/dist/utils/constants.d.ts.map +1 -1
- package/dist/utils/constants.js +2 -14
- package/dist/utils/convertMessagesForAPI.d.ts +2 -1
- package/dist/utils/convertMessagesForAPI.d.ts.map +1 -1
- package/dist/utils/convertMessagesForAPI.js +39 -18
- package/dist/utils/customCommands.d.ts.map +1 -1
- package/dist/utils/customCommands.js +66 -21
- package/dist/utils/fileSearch.d.ts +14 -0
- package/dist/utils/fileSearch.d.ts.map +1 -0
- package/dist/utils/fileSearch.js +88 -0
- package/dist/utils/fileUtils.d.ts +27 -0
- package/dist/utils/fileUtils.d.ts.map +1 -0
- package/dist/utils/fileUtils.js +145 -0
- package/dist/utils/globalLogger.d.ts +88 -0
- package/dist/utils/globalLogger.d.ts.map +1 -0
- package/dist/utils/globalLogger.js +120 -0
- package/dist/utils/largeOutputHandler.d.ts +15 -0
- package/dist/utils/largeOutputHandler.d.ts.map +1 -0
- package/dist/utils/largeOutputHandler.js +40 -0
- package/dist/utils/markdownParser.d.ts.map +1 -1
- package/dist/utils/markdownParser.js +1 -17
- package/dist/utils/mcpUtils.d.ts.map +1 -1
- package/dist/utils/mcpUtils.js +25 -3
- package/dist/utils/messageOperations.d.ts +20 -18
- package/dist/utils/messageOperations.d.ts.map +1 -1
- package/dist/utils/messageOperations.js +30 -38
- package/dist/utils/pathEncoder.d.ts +108 -0
- package/dist/utils/pathEncoder.d.ts.map +1 -0
- package/dist/utils/pathEncoder.js +279 -0
- package/dist/utils/subagentParser.d.ts +2 -2
- package/dist/utils/subagentParser.d.ts.map +1 -1
- package/dist/utils/subagentParser.js +12 -8
- package/dist/utils/tokenCalculation.d.ts +26 -0
- package/dist/utils/tokenCalculation.d.ts.map +1 -0
- package/dist/utils/tokenCalculation.js +36 -0
- package/dist/utils/tokenEstimator.d.ts +39 -0
- package/dist/utils/tokenEstimator.d.ts.map +1 -0
- package/dist/utils/tokenEstimator.js +55 -0
- package/package.json +6 -6
- package/src/agent.ts +586 -78
- package/src/index.ts +4 -0
- package/src/managers/aiManager.ts +341 -192
- package/src/managers/backgroundBashManager.ts +11 -9
- package/src/managers/hookManager.ts +102 -54
- package/src/managers/liveConfigManager.ts +634 -0
- package/src/managers/lspManager.ts +434 -0
- package/src/managers/messageManager.ts +258 -121
- package/src/managers/permissionManager.ts +276 -0
- package/src/managers/skillManager.ts +3 -1
- package/src/managers/slashCommandManager.ts +5 -3
- package/src/managers/subagentManager.ts +295 -76
- package/src/managers/toolManager.ts +95 -3
- package/src/services/aiService.ts +656 -84
- package/src/services/configurationService.ts +762 -0
- package/src/services/fileWatcher.ts +300 -0
- package/src/services/hook.ts +54 -144
- package/src/services/jsonlHandler.ts +303 -0
- package/src/services/memory.ts +34 -11
- package/src/services/session.ts +522 -173
- package/src/tools/bashTool.ts +94 -20
- package/src/tools/deleteFileTool.ts +38 -1
- package/src/tools/editTool.ts +44 -9
- package/src/tools/lspTool.ts +760 -0
- package/src/tools/multiEditTool.ts +41 -11
- package/src/tools/readTool.ts +127 -3
- package/src/tools/skillTool.ts +2 -2
- package/src/tools/todoWriteTool.ts +33 -1
- package/src/tools/types.ts +15 -9
- package/src/tools/writeTool.ts +43 -16
- package/src/types/commands.ts +6 -1
- package/src/types/config.ts +5 -0
- package/src/types/configuration.ts +73 -0
- package/src/types/core.ts +55 -0
- package/src/types/environment.ts +104 -0
- package/src/types/fileSearch.ts +4 -0
- package/src/types/hooks.ts +32 -16
- package/src/types/index.ts +7 -0
- package/src/types/lsp.ts +96 -0
- package/src/types/messaging.ts +21 -14
- package/src/types/permissions.ts +48 -0
- package/src/types/session.ts +20 -0
- package/src/types/skills.ts +1 -0
- package/src/types/tools.ts +38 -0
- package/src/utils/abortUtils.ts +118 -0
- package/src/utils/bashHistory.ts +55 -31
- package/src/utils/builtinSubagents.ts +71 -0
- package/src/utils/cacheControlUtils.ts +475 -0
- package/src/utils/commandPathResolver.ts +189 -0
- package/src/utils/configPaths.ts +163 -0
- package/src/utils/constants.ts +2 -17
- package/src/utils/convertMessagesForAPI.ts +44 -18
- package/src/utils/customCommands.ts +90 -22
- package/src/utils/fileSearch.ts +107 -0
- package/src/utils/fileUtils.ts +160 -0
- package/src/utils/globalLogger.ts +128 -0
- package/src/utils/largeOutputHandler.ts +55 -0
- package/src/utils/markdownParser.ts +1 -19
- package/src/utils/mcpUtils.ts +34 -3
- package/src/utils/messageOperations.ts +47 -53
- package/src/utils/pathEncoder.ts +394 -0
- package/src/utils/subagentParser.ts +13 -9
- package/src/utils/tokenCalculation.ts +43 -0
- package/src/utils/tokenEstimator.ts +68 -0
- package/dist/utils/configResolver.d.ts +0 -38
- package/dist/utils/configResolver.d.ts.map +0 -1
- package/dist/utils/configResolver.js +0 -106
- package/src/utils/configResolver.ts +0 -142
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFile, writeFile } from "fs/promises";
|
|
2
|
+
import { logger } from "../utils/globalLogger.js";
|
|
2
3
|
import type { ToolPlugin, ToolResult, ToolContext } from "./types.js";
|
|
3
4
|
import { resolvePath, getDisplayPath } from "../utils/path.js";
|
|
4
|
-
import { diffLines } from "diff";
|
|
5
5
|
|
|
6
6
|
interface EditOperation {
|
|
7
7
|
old_string: string;
|
|
@@ -149,7 +149,7 @@ export const multiEditTool: ToolPlugin = {
|
|
|
149
149
|
if (edits[0] && edits[0].old_string === "") {
|
|
150
150
|
originalContent = "";
|
|
151
151
|
isNewFile = true;
|
|
152
|
-
|
|
152
|
+
logger.debug(`Creating new file: ${resolvedPath}`);
|
|
153
153
|
} else {
|
|
154
154
|
return {
|
|
155
155
|
success: false,
|
|
@@ -181,7 +181,7 @@ export const multiEditTool: ToolPlugin = {
|
|
|
181
181
|
return {
|
|
182
182
|
success: false,
|
|
183
183
|
content: "",
|
|
184
|
-
error: `Edit operation ${i + 1}: old_string not found in current content
|
|
184
|
+
error: `Edit operation ${i + 1}: old_string not found in current content`,
|
|
185
185
|
};
|
|
186
186
|
}
|
|
187
187
|
|
|
@@ -216,6 +216,42 @@ export const multiEditTool: ToolPlugin = {
|
|
|
216
216
|
}
|
|
217
217
|
}
|
|
218
218
|
|
|
219
|
+
// Permission check after validation but before real operation
|
|
220
|
+
if (
|
|
221
|
+
context.permissionManager &&
|
|
222
|
+
context.permissionMode &&
|
|
223
|
+
context.permissionMode !== "bypassPermissions"
|
|
224
|
+
) {
|
|
225
|
+
if (context.permissionManager.isRestrictedTool("MultiEdit")) {
|
|
226
|
+
try {
|
|
227
|
+
const permissionContext = context.permissionManager.createContext(
|
|
228
|
+
"MultiEdit",
|
|
229
|
+
context.permissionMode,
|
|
230
|
+
context.canUseToolCallback,
|
|
231
|
+
{ file_path: filePath, edits },
|
|
232
|
+
);
|
|
233
|
+
const permissionResult =
|
|
234
|
+
await context.permissionManager.checkPermission(
|
|
235
|
+
permissionContext,
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
if (permissionResult.behavior === "deny") {
|
|
239
|
+
return {
|
|
240
|
+
success: false,
|
|
241
|
+
content: "",
|
|
242
|
+
error: `MultiEdit operation denied by user, reason: ${permissionResult.message || "No reason provided"}`,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
} catch {
|
|
246
|
+
return {
|
|
247
|
+
success: false,
|
|
248
|
+
content: "",
|
|
249
|
+
error: "Permission check failed",
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
219
255
|
// Write file
|
|
220
256
|
try {
|
|
221
257
|
await writeFile(resolvedPath, currentContent, "utf-8");
|
|
@@ -227,30 +263,24 @@ export const multiEditTool: ToolPlugin = {
|
|
|
227
263
|
};
|
|
228
264
|
}
|
|
229
265
|
|
|
230
|
-
// Generate diff information
|
|
231
|
-
const diffResult = diffLines(originalContent, currentContent);
|
|
232
|
-
|
|
233
266
|
const shortResult = isNewFile
|
|
234
267
|
? `Created file with ${edits.length} operations`
|
|
235
268
|
: `Applied ${edits.length} edits`;
|
|
236
269
|
|
|
237
270
|
const detailedContent = `${shortResult}\n\nOperations performed:\n${appliedEdits.map((edit, i) => `${i + 1}. ${edit}`).join("\n")}`;
|
|
238
271
|
|
|
239
|
-
|
|
272
|
+
logger.debug(`MultiEdit tool: ${shortResult}`);
|
|
240
273
|
|
|
241
274
|
return {
|
|
242
275
|
success: true,
|
|
243
276
|
content: detailedContent,
|
|
244
277
|
shortResult,
|
|
245
278
|
filePath: resolvedPath,
|
|
246
|
-
originalContent,
|
|
247
|
-
newContent: currentContent,
|
|
248
|
-
diffResult,
|
|
249
279
|
};
|
|
250
280
|
} catch (error) {
|
|
251
281
|
const errorMessage =
|
|
252
282
|
error instanceof Error ? error.message : String(error);
|
|
253
|
-
|
|
283
|
+
logger.error(`MultiEdit tool error: ${errorMessage}`);
|
|
254
284
|
return {
|
|
255
285
|
success: false,
|
|
256
286
|
content: "",
|
package/src/tools/readTool.ts
CHANGED
|
@@ -1,10 +1,129 @@
|
|
|
1
|
-
import { readFile } from "fs/promises";
|
|
1
|
+
import { readFile, stat } from "fs/promises";
|
|
2
|
+
import { extname } from "path";
|
|
3
|
+
import { logger } from "../utils/globalLogger.js";
|
|
2
4
|
import type { ToolPlugin, ToolResult, ToolContext } from "./types.js";
|
|
3
5
|
import { resolvePath, getDisplayPath } from "../utils/path.js";
|
|
4
6
|
import {
|
|
5
7
|
isBinaryDocument,
|
|
6
8
|
getBinaryDocumentError,
|
|
7
9
|
} from "../utils/fileFormat.js";
|
|
10
|
+
import { convertImageToBase64 } from "../utils/messageOperations.js";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Supported image file extensions
|
|
14
|
+
*/
|
|
15
|
+
const SUPPORTED_IMAGE_EXTENSIONS = [
|
|
16
|
+
"png",
|
|
17
|
+
"jpeg",
|
|
18
|
+
"jpg",
|
|
19
|
+
"gif",
|
|
20
|
+
"webp",
|
|
21
|
+
] as const;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Check if a file path represents an image file
|
|
25
|
+
* @param filePath - Path to the file
|
|
26
|
+
* @returns true if the file is a supported image format
|
|
27
|
+
*/
|
|
28
|
+
function isImageFile(filePath: string): boolean {
|
|
29
|
+
const ext = extname(filePath).toLowerCase().substring(1);
|
|
30
|
+
return (SUPPORTED_IMAGE_EXTENSIONS as readonly string[]).includes(ext);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Validate image file size
|
|
35
|
+
* @param filePath - Path to the image file
|
|
36
|
+
* @param maxSizeBytes - Maximum allowed file size in bytes (default: 20MB)
|
|
37
|
+
* @returns Promise<boolean> - true if file size is within limit
|
|
38
|
+
*/
|
|
39
|
+
async function validateImageFileSize(
|
|
40
|
+
filePath: string,
|
|
41
|
+
maxSizeBytes: number = 20 * 1024 * 1024,
|
|
42
|
+
): Promise<boolean> {
|
|
43
|
+
try {
|
|
44
|
+
const stats = await stat(filePath);
|
|
45
|
+
return stats.size <= maxSizeBytes;
|
|
46
|
+
} catch {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Get MIME type for image file based on extension
|
|
53
|
+
* @param filePath - Path to the image file
|
|
54
|
+
* @returns MIME type string
|
|
55
|
+
*/
|
|
56
|
+
function getImageMimeType(filePath: string): string {
|
|
57
|
+
const ext = extname(filePath).toLowerCase().substring(1);
|
|
58
|
+
switch (ext) {
|
|
59
|
+
case "png":
|
|
60
|
+
return "image/png";
|
|
61
|
+
case "jpg":
|
|
62
|
+
case "jpeg":
|
|
63
|
+
return "image/jpeg";
|
|
64
|
+
case "gif":
|
|
65
|
+
return "image/gif";
|
|
66
|
+
case "webp":
|
|
67
|
+
return "image/webp";
|
|
68
|
+
default:
|
|
69
|
+
return "image/png"; // Default fallback
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Process an image file and return ToolResult with image data
|
|
75
|
+
* @param filePath - Path to the image file
|
|
76
|
+
* @param context - Tool execution context
|
|
77
|
+
* @returns Promise<ToolResult> with image data
|
|
78
|
+
*/
|
|
79
|
+
async function processImageFile(
|
|
80
|
+
filePath: string,
|
|
81
|
+
context: ToolContext,
|
|
82
|
+
): Promise<ToolResult> {
|
|
83
|
+
try {
|
|
84
|
+
// Resolve path
|
|
85
|
+
const actualFilePath = filePath.startsWith("/")
|
|
86
|
+
? filePath
|
|
87
|
+
: resolvePath(filePath, context.workdir);
|
|
88
|
+
|
|
89
|
+
// Validate file size
|
|
90
|
+
const isValidSize = await validateImageFileSize(actualFilePath);
|
|
91
|
+
if (!isValidSize) {
|
|
92
|
+
const stats = await stat(actualFilePath);
|
|
93
|
+
const sizeMB = (stats.size / (1024 * 1024)).toFixed(2);
|
|
94
|
+
return {
|
|
95
|
+
success: false,
|
|
96
|
+
content: "",
|
|
97
|
+
error: `Image file exceeds 20MB limit (actual: ${sizeMB}MB)`,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Convert image to base64
|
|
102
|
+
const imageDataUrl = convertImageToBase64(actualFilePath);
|
|
103
|
+
const mimeType = getImageMimeType(actualFilePath);
|
|
104
|
+
|
|
105
|
+
// Extract base64 data from data URL (remove data:image/type;base64, prefix)
|
|
106
|
+
const base64Data = imageDataUrl.split(",")[1] || "";
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
success: true,
|
|
110
|
+
content: `Image file processed: ${getDisplayPath(filePath, context.workdir)}\nFormat: ${mimeType}\nSize: Available for AI processing`,
|
|
111
|
+
shortResult: `Image processed (${mimeType})`,
|
|
112
|
+
images: [
|
|
113
|
+
{
|
|
114
|
+
data: base64Data,
|
|
115
|
+
mediaType: mimeType,
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
};
|
|
119
|
+
} catch (error) {
|
|
120
|
+
return {
|
|
121
|
+
success: false,
|
|
122
|
+
content: "",
|
|
123
|
+
error: `Failed to process image: ${error instanceof Error ? error.message : String(error)}`,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
}
|
|
8
127
|
|
|
9
128
|
/**
|
|
10
129
|
* Read Tool Plugin - Read file content
|
|
@@ -16,7 +135,7 @@ export const readTool: ToolPlugin = {
|
|
|
16
135
|
function: {
|
|
17
136
|
name: "Read",
|
|
18
137
|
description:
|
|
19
|
-
"Reads a file from the local filesystem. You can access any file directly by using this tool.\nAssume this tool is able to read all files on the machine. If the User provides a path to a file assume that path is valid. It is okay to read a file that does not exist; an error will be returned.\n\nUsage:\n- The file_path parameter must be an absolute path, not a relative path\n- By default, it reads up to 2000 lines starting from the beginning of the file\n- You can optionally specify a line offset and limit (especially handy for long files), but it's recommended to read the whole file by not providing these parameters\n- Any lines longer than 2000 characters will be truncated\n- Results are returned using cat -n format, with line numbers starting at 1\n- This tool allows
|
|
138
|
+
"Reads a file from the local filesystem. You can access any file directly by using this tool.\nAssume this tool is able to read all files on the machine. If the User provides a path to a file assume that path is valid. It is okay to read a file that does not exist; an error will be returned.\n\nUsage:\n- The file_path parameter must be an absolute path, not a relative path\n- By default, it reads up to 2000 lines starting from the beginning of the file\n- You can optionally specify a line offset and limit (especially handy for long files), but it's recommended to read the whole file by not providing these parameters\n- Any lines longer than 2000 characters will be truncated\n- Results are returned using cat -n format, with line numbers starting at 1\n- This tool allows Agent to read images (eg PNG, JPG, etc). When reading an image file the contents are presented visually as Agent is a multimodal LLM.\n- This tool can read Jupyter notebooks (.ipynb files) and returns all cells with their outputs, combining code, text, and visualizations.\n- You have the capability to call multiple tools in a single response. It is always better to speculatively read multiple files as a batch that are potentially useful.\n- You will regularly be asked to read screenshots. If the user provides a path to a screenshot ALWAYS use this tool to view the file at the path. This tool will work with all temporary file paths like /var/folders/123/abc/T/TemporaryItems/NSIRD_screencaptureui_ZfB1tD/Screenshot.png\n- If you read a file that exists but has empty contents you will receive a system reminder warning in place of file contents.\n- Binary document formats (PDF, DOC, DOCX, XLS, XLSX, PPT, PPTX) are not supported and will return an error.",
|
|
20
139
|
parameters: {
|
|
21
140
|
type: "object",
|
|
22
141
|
properties: {
|
|
@@ -64,6 +183,11 @@ export const readTool: ToolPlugin = {
|
|
|
64
183
|
};
|
|
65
184
|
}
|
|
66
185
|
|
|
186
|
+
// Check if this is an image file
|
|
187
|
+
if (isImageFile(filePath)) {
|
|
188
|
+
return processImageFile(filePath, context);
|
|
189
|
+
}
|
|
190
|
+
|
|
67
191
|
try {
|
|
68
192
|
// Note: New Read tool requires absolute paths, so we don't use resolvePath
|
|
69
193
|
// But for compatibility, if it's not an absolute path, we still try to resolve
|
|
@@ -75,7 +199,7 @@ export const readTool: ToolPlugin = {
|
|
|
75
199
|
|
|
76
200
|
// Check if file is empty
|
|
77
201
|
if (fileContent.length === 0) {
|
|
78
|
-
|
|
202
|
+
logger.warn(`File ${filePath} exists but has empty contents`);
|
|
79
203
|
return {
|
|
80
204
|
success: true,
|
|
81
205
|
content:
|
package/src/tools/skillTool.ts
CHANGED
|
@@ -23,11 +23,11 @@ export function createSkillTool(skillManager: SkillManager): ToolPlugin {
|
|
|
23
23
|
};
|
|
24
24
|
|
|
25
25
|
return {
|
|
26
|
-
name: "
|
|
26
|
+
name: "Skill",
|
|
27
27
|
config: {
|
|
28
28
|
type: "function",
|
|
29
29
|
function: {
|
|
30
|
-
name: "
|
|
30
|
+
name: "Skill",
|
|
31
31
|
description: getToolDescription(),
|
|
32
32
|
parameters: {
|
|
33
33
|
type: "object",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ToolPlugin, ToolResult } from "./types.js";
|
|
1
|
+
import type { ToolPlugin, ToolResult, ToolContext } from "./types.js";
|
|
2
2
|
|
|
3
3
|
interface TodoItem {
|
|
4
4
|
content: string;
|
|
@@ -101,6 +101,38 @@ When in doubt, use this tool. Being proactive with task management demonstrates
|
|
|
101
101
|
},
|
|
102
102
|
},
|
|
103
103
|
|
|
104
|
+
formatCompactParams: (
|
|
105
|
+
params: Record<string, unknown>,
|
|
106
|
+
context: ToolContext,
|
|
107
|
+
) => {
|
|
108
|
+
void context; // Context not needed for this tool
|
|
109
|
+
try {
|
|
110
|
+
const { todos } = params as { todos?: TodoItem[] };
|
|
111
|
+
|
|
112
|
+
// Handle invalid or missing tasks array
|
|
113
|
+
if (!todos || !Array.isArray(todos)) {
|
|
114
|
+
return "invalid todos";
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Handle empty task list
|
|
118
|
+
if (todos.length === 0) {
|
|
119
|
+
return "0 tasks";
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Count completed tasks
|
|
123
|
+
const completedCount = todos.filter(
|
|
124
|
+
(todo) => todo && todo.status === "completed",
|
|
125
|
+
).length;
|
|
126
|
+
const totalCount = todos.length;
|
|
127
|
+
|
|
128
|
+
// Format with proper singular/plural
|
|
129
|
+
const taskWord = totalCount === 1 ? "task" : "tasks";
|
|
130
|
+
return `${completedCount}/${totalCount} ${taskWord}`;
|
|
131
|
+
} catch {
|
|
132
|
+
return "invalid todos";
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
|
|
104
136
|
execute: async (args: Record<string, unknown>): Promise<ToolResult> => {
|
|
105
137
|
try {
|
|
106
138
|
// Validate arguments
|
package/src/tools/types.ts
CHANGED
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { ChatCompletionFunctionTool } from "openai/resources.js";
|
|
6
|
+
import type {
|
|
7
|
+
PermissionMode,
|
|
8
|
+
PermissionCallback,
|
|
9
|
+
} from "../types/permissions.js";
|
|
6
10
|
|
|
7
11
|
export interface ToolPlugin {
|
|
8
12
|
name: string;
|
|
@@ -23,15 +27,7 @@ export interface ToolResult {
|
|
|
23
27
|
error?: string;
|
|
24
28
|
// Short output, used to display summary information in collapsed state
|
|
25
29
|
shortResult?: string;
|
|
26
|
-
//
|
|
27
|
-
originalContent?: string;
|
|
28
|
-
newContent?: string;
|
|
29
|
-
diffResult?: Array<{
|
|
30
|
-
count?: number;
|
|
31
|
-
value: string;
|
|
32
|
-
added?: boolean;
|
|
33
|
-
removed?: boolean;
|
|
34
|
-
}>;
|
|
30
|
+
// File path for operations that affect files
|
|
35
31
|
filePath?: string;
|
|
36
32
|
// Image data, for supporting multimedia content
|
|
37
33
|
images?: Array<{
|
|
@@ -44,4 +40,14 @@ export interface ToolContext {
|
|
|
44
40
|
abortSignal?: AbortSignal;
|
|
45
41
|
backgroundBashManager?: import("../managers/backgroundBashManager.js").BackgroundBashManager;
|
|
46
42
|
workdir: string;
|
|
43
|
+
/** Permission mode for this tool execution */
|
|
44
|
+
permissionMode?: PermissionMode;
|
|
45
|
+
/** Custom permission callback */
|
|
46
|
+
canUseToolCallback?: PermissionCallback;
|
|
47
|
+
/** Permission manager instance for permission checks */
|
|
48
|
+
permissionManager?: import("../managers/permissionManager.js").PermissionManager;
|
|
49
|
+
/** MCP manager instance for calling MCP tools */
|
|
50
|
+
mcpManager?: import("../managers/mcpManager.js").McpManager;
|
|
51
|
+
/** LSP manager instance for code intelligence */
|
|
52
|
+
lspManager?: import("../types/lsp.js").ILspManager;
|
|
47
53
|
}
|
package/src/tools/writeTool.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
2
2
|
import { dirname } from "path";
|
|
3
|
+
import { logger } from "../utils/globalLogger.js";
|
|
3
4
|
import type { ToolPlugin, ToolResult, ToolContext } from "./types.js";
|
|
4
5
|
import { resolvePath, getDisplayPath } from "../utils/path.js";
|
|
5
|
-
import { diffLines } from "diff";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* File Write Tool Plugin
|
|
@@ -14,7 +14,7 @@ export const writeTool: ToolPlugin = {
|
|
|
14
14
|
function: {
|
|
15
15
|
name: "Write",
|
|
16
16
|
description:
|
|
17
|
-
"Writes a file to the local filesystem.\n\nUsage:\n- This tool will overwrite the existing file if there is one at the provided path.\n- If this is an existing file, you MUST use the Read tool first to read the file's contents. This tool will fail if you did not read the file first.\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.",
|
|
17
|
+
"Writes a file to the local filesystem.\n\nUsage:\n- This tool will overwrite the existing file if there is one at the provided path.\n- If this is an existing file, you MUST use the Read tool first to read the file's contents. This tool will fail if you did not read the file first.\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\n- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\n- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.\n- IMPORTANT: Always provide file_path parameter before content parameter when calling this tool.",
|
|
18
18
|
parameters: {
|
|
19
19
|
type: "object",
|
|
20
20
|
properties: {
|
|
@@ -79,9 +79,6 @@ export const writeTool: ToolPlugin = {
|
|
|
79
79
|
content: `File ${filePath} already has the same content, no changes needed`,
|
|
80
80
|
shortResult: "No changes needed",
|
|
81
81
|
filePath: resolvedPath,
|
|
82
|
-
originalContent,
|
|
83
|
-
newContent: content,
|
|
84
|
-
diffResult: [],
|
|
85
82
|
};
|
|
86
83
|
}
|
|
87
84
|
|
|
@@ -95,9 +92,45 @@ export const writeTool: ToolPlugin = {
|
|
|
95
92
|
mkdirError instanceof Error &&
|
|
96
93
|
!mkdirError.message.includes("EEXIST")
|
|
97
94
|
) {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
95
|
+
logger.warn(
|
|
96
|
+
`Failed to create directory ${fileDir}: ${mkdirError.message}`,
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Permission check after validation but before real operation
|
|
102
|
+
if (
|
|
103
|
+
context.permissionManager &&
|
|
104
|
+
context.permissionMode &&
|
|
105
|
+
context.permissionMode !== "bypassPermissions"
|
|
106
|
+
) {
|
|
107
|
+
if (context.permissionManager.isRestrictedTool("Write")) {
|
|
108
|
+
try {
|
|
109
|
+
const permissionContext = context.permissionManager.createContext(
|
|
110
|
+
"Write",
|
|
111
|
+
context.permissionMode,
|
|
112
|
+
context.canUseToolCallback,
|
|
113
|
+
{ file_path: filePath, content },
|
|
114
|
+
);
|
|
115
|
+
const permissionResult =
|
|
116
|
+
await context.permissionManager.checkPermission(
|
|
117
|
+
permissionContext,
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
if (permissionResult.behavior === "deny") {
|
|
121
|
+
return {
|
|
122
|
+
success: false,
|
|
123
|
+
content: "",
|
|
124
|
+
error: `Write operation denied by user, reason: ${permissionResult.message || "No reason provided"}`,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
} catch {
|
|
128
|
+
return {
|
|
129
|
+
success: false,
|
|
130
|
+
content: "",
|
|
131
|
+
error: "Permission check failed",
|
|
132
|
+
};
|
|
133
|
+
}
|
|
101
134
|
}
|
|
102
135
|
}
|
|
103
136
|
|
|
@@ -112,30 +145,24 @@ export const writeTool: ToolPlugin = {
|
|
|
112
145
|
};
|
|
113
146
|
}
|
|
114
147
|
|
|
115
|
-
// Generate diff information
|
|
116
|
-
const diffResult = diffLines(originalContent, content);
|
|
117
|
-
|
|
118
148
|
const shortResult = isExistingFile ? "File overwritten" : "File created";
|
|
119
149
|
|
|
120
150
|
const lines = content.split("\n").length;
|
|
121
151
|
const chars = content.length;
|
|
122
152
|
const detailedContent = `${shortResult} (${lines} lines, ${chars} characters)`;
|
|
123
153
|
|
|
124
|
-
|
|
154
|
+
logger.debug(`Write tool: ${shortResult}`);
|
|
125
155
|
|
|
126
156
|
return {
|
|
127
157
|
success: true,
|
|
128
158
|
content: detailedContent,
|
|
129
159
|
shortResult,
|
|
130
160
|
filePath: resolvedPath,
|
|
131
|
-
originalContent,
|
|
132
|
-
newContent: content,
|
|
133
|
-
diffResult,
|
|
134
161
|
};
|
|
135
162
|
} catch (error) {
|
|
136
163
|
const errorMessage =
|
|
137
164
|
error instanceof Error ? error.message : String(error);
|
|
138
|
-
|
|
165
|
+
logger.error(`Write tool error: ${errorMessage}`);
|
|
139
166
|
return {
|
|
140
167
|
success: false,
|
|
141
168
|
content: "",
|
package/src/types/commands.ts
CHANGED
|
@@ -11,7 +11,6 @@ export interface SlashCommand {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export interface CustomSlashCommandConfig {
|
|
14
|
-
allowedTools?: string[];
|
|
15
14
|
model?: string;
|
|
16
15
|
description?: string;
|
|
17
16
|
}
|
|
@@ -23,4 +22,10 @@ export interface CustomSlashCommand {
|
|
|
23
22
|
filePath: string;
|
|
24
23
|
content: string;
|
|
25
24
|
config?: CustomSlashCommandConfig;
|
|
25
|
+
|
|
26
|
+
// Nested command support
|
|
27
|
+
namespace?: string; // Parent directory for nested commands (e.g., "openspec")
|
|
28
|
+
isNested: boolean; // Whether command is in a subdirectory
|
|
29
|
+
depth: number; // 0 = root, 1 = nested
|
|
30
|
+
segments: string[]; // Path components for ID generation (e.g., ["openspec", "apply"])
|
|
26
31
|
}
|
package/src/types/config.ts
CHANGED
|
@@ -3,9 +3,14 @@
|
|
|
3
3
|
* Dependencies: None
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import OpenAI from "openai";
|
|
7
|
+
|
|
6
8
|
export interface GatewayConfig {
|
|
7
9
|
apiKey: string;
|
|
8
10
|
baseURL: string;
|
|
11
|
+
defaultHeaders?: Record<string, string>;
|
|
12
|
+
fetchOptions?: OpenAI["fetchOptions"];
|
|
13
|
+
fetch?: OpenAI["fetch"];
|
|
9
14
|
}
|
|
10
15
|
|
|
11
16
|
export interface ModelConfig {
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration Management Types
|
|
3
|
+
*
|
|
4
|
+
* Types for centralized configuration loading and validation services.
|
|
5
|
+
* These support the refactored configuration architecture that separates
|
|
6
|
+
* configuration management from hook execution.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { WaveConfiguration } from "./hooks.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Result of configuration loading operations with detailed status information
|
|
13
|
+
*/
|
|
14
|
+
export interface ConfigurationLoadResult {
|
|
15
|
+
/** The loaded configuration, or null if loading failed */
|
|
16
|
+
configuration: WaveConfiguration | null;
|
|
17
|
+
/** Whether the loading operation was successful */
|
|
18
|
+
success: boolean;
|
|
19
|
+
/** Error message if loading failed */
|
|
20
|
+
error?: string;
|
|
21
|
+
/** Path of the successfully loaded file */
|
|
22
|
+
sourcePath?: string;
|
|
23
|
+
/** Non-critical warnings during loading */
|
|
24
|
+
warnings: string[];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Result of configuration validation operations
|
|
29
|
+
*/
|
|
30
|
+
export interface ValidationResult {
|
|
31
|
+
/** Whether the configuration is valid */
|
|
32
|
+
isValid: boolean;
|
|
33
|
+
/** Critical errors that prevent configuration use */
|
|
34
|
+
errors: string[];
|
|
35
|
+
/** Non-critical warnings about the configuration */
|
|
36
|
+
warnings: string[];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Configuration file paths organized by category
|
|
41
|
+
*/
|
|
42
|
+
export interface ConfigurationPaths {
|
|
43
|
+
/** User-specific configuration file paths in priority order */
|
|
44
|
+
userPaths: string[];
|
|
45
|
+
/** Project-specific configuration file paths in priority order */
|
|
46
|
+
projectPaths: string[];
|
|
47
|
+
/** All configuration paths combined */
|
|
48
|
+
allPaths: string[];
|
|
49
|
+
/** Only the paths that actually exist on the filesystem */
|
|
50
|
+
existingPaths: string[];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Options for configuring the ConfigurationService
|
|
55
|
+
*/
|
|
56
|
+
export interface ConfigurationServiceOptions {
|
|
57
|
+
/** Working directory for resolving project configurations */
|
|
58
|
+
workdir: string;
|
|
59
|
+
/** Optional logger for configuration operations */
|
|
60
|
+
logger?: Logger;
|
|
61
|
+
/** Whether to enable validation during loading (default: true) */
|
|
62
|
+
enableValidation?: boolean;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Minimal logger interface for configuration services
|
|
67
|
+
*/
|
|
68
|
+
interface Logger {
|
|
69
|
+
error: (...args: unknown[]) => void;
|
|
70
|
+
warn: (...args: unknown[]) => void;
|
|
71
|
+
info: (...args: unknown[]) => void;
|
|
72
|
+
debug: (...args: unknown[]) => void;
|
|
73
|
+
}
|
package/src/types/core.ts
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* Dependencies: None (foundation layer)
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import type { CompletionUsage } from "openai/resources";
|
|
7
|
+
|
|
6
8
|
/**
|
|
7
9
|
* Logger interface definition
|
|
8
10
|
* Compatible with OpenAI package Logger interface
|
|
@@ -24,6 +26,59 @@ export interface Usage {
|
|
|
24
26
|
total_tokens: number; // Sum of prompt + completion tokens
|
|
25
27
|
model?: string; // Model used for the operation (e.g., "gpt-4", "gpt-3.5-turbo")
|
|
26
28
|
operation_type?: "agent" | "compress"; // Type of operation that generated usage
|
|
29
|
+
|
|
30
|
+
// Cache-related tokens (Claude models only)
|
|
31
|
+
cache_read_input_tokens?: number; // Tokens read from cache
|
|
32
|
+
cache_creation_input_tokens?: number; // Tokens used to create cache entries
|
|
33
|
+
cache_creation?: {
|
|
34
|
+
ephemeral_5m_input_tokens: number; // Tokens cached for 5 minutes
|
|
35
|
+
ephemeral_1h_input_tokens: number; // Tokens cached for 1 hour
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Enhanced usage metrics including Claude cache information
|
|
41
|
+
* Backward compatible with standard OpenAI CompletionUsage
|
|
42
|
+
*/
|
|
43
|
+
export interface ClaudeUsage extends CompletionUsage {
|
|
44
|
+
// Standard OpenAI usage fields (inherited)
|
|
45
|
+
prompt_tokens: number;
|
|
46
|
+
completion_tokens: number;
|
|
47
|
+
total_tokens: number;
|
|
48
|
+
|
|
49
|
+
// Claude-specific cache extensions
|
|
50
|
+
/**
|
|
51
|
+
* Number of tokens read from existing cache
|
|
52
|
+
* Indicates cost savings from cache hits
|
|
53
|
+
*/
|
|
54
|
+
cache_read_input_tokens?: number;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Number of tokens used to create new cache entries
|
|
58
|
+
* Investment in future cache hits
|
|
59
|
+
*/
|
|
60
|
+
cache_creation_input_tokens?: number;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Detailed breakdown of cache creation by duration
|
|
64
|
+
*/
|
|
65
|
+
cache_creation?: {
|
|
66
|
+
/** Tokens cached for 5 minute duration */
|
|
67
|
+
ephemeral_5m_input_tokens: number;
|
|
68
|
+
/** Tokens cached for 1 hour duration */
|
|
69
|
+
ephemeral_1h_input_tokens: number;
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Represents a diff change for tool parameter-based diff display
|
|
75
|
+
* Contains the old content and new content for comparison
|
|
76
|
+
*/
|
|
77
|
+
export interface Change {
|
|
78
|
+
/** The original content (empty string for additions) */
|
|
79
|
+
oldContent: string;
|
|
80
|
+
/** The new content (empty string for deletions) */
|
|
81
|
+
newContent: string;
|
|
27
82
|
}
|
|
28
83
|
|
|
29
84
|
export class ConfigurationError extends Error {
|