wave-agent-sdk 0.4.0 → 0.5.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/dist/agent.d.ts +28 -5
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +54 -37
- package/dist/constants/tools.d.ts +2 -2
- package/dist/constants/tools.js +2 -2
- package/dist/managers/MemoryRuleManager.js +1 -1
- package/dist/managers/aiManager.d.ts +3 -3
- package/dist/managers/aiManager.d.ts.map +1 -1
- package/dist/managers/aiManager.js +3 -4
- package/dist/managers/backgroundBashManager.d.ts.map +1 -1
- package/dist/managers/backgroundBashManager.js +1 -0
- package/dist/managers/backgroundTaskManager.d.ts +35 -0
- package/dist/managers/backgroundTaskManager.d.ts.map +1 -0
- package/dist/managers/backgroundTaskManager.js +249 -0
- package/dist/managers/foregroundTaskManager.d.ts +9 -0
- package/dist/managers/foregroundTaskManager.d.ts.map +1 -0
- package/dist/managers/foregroundTaskManager.js +20 -0
- package/dist/managers/liveConfigManager.d.ts +1 -1
- package/dist/managers/lspManager.d.ts.map +1 -1
- package/dist/managers/lspManager.js +3 -1
- package/dist/managers/messageManager.d.ts +12 -2
- package/dist/managers/messageManager.d.ts.map +1 -1
- package/dist/managers/messageManager.js +36 -2
- package/dist/managers/permissionManager.d.ts.map +1 -1
- package/dist/managers/permissionManager.js +1 -7
- package/dist/managers/pluginManager.d.ts.map +1 -1
- package/dist/managers/pluginManager.js +3 -2
- package/dist/managers/slashCommandManager.d.ts +3 -0
- package/dist/managers/slashCommandManager.d.ts.map +1 -1
- package/dist/managers/slashCommandManager.js +1 -0
- package/dist/managers/subagentManager.d.ts +11 -2
- package/dist/managers/subagentManager.d.ts.map +1 -1
- package/dist/managers/subagentManager.js +137 -39
- package/dist/managers/toolManager.d.ts +7 -1
- package/dist/managers/toolManager.d.ts.map +1 -1
- package/dist/managers/toolManager.js +9 -3
- package/dist/services/GitService.d.ts.map +1 -1
- package/dist/services/GitService.js +6 -2
- package/dist/services/MarketplaceService.d.ts +2 -2
- package/dist/services/MarketplaceService.d.ts.map +1 -1
- package/dist/services/MarketplaceService.js +18 -11
- package/dist/services/MemoryRuleService.d.ts +1 -1
- package/dist/services/MemoryRuleService.d.ts.map +1 -1
- package/dist/services/MemoryRuleService.js +13 -2
- package/dist/services/memory.js +1 -1
- package/dist/tools/bashTool.d.ts +0 -8
- package/dist/tools/bashTool.d.ts.map +1 -1
- package/dist/tools/bashTool.js +44 -172
- package/dist/tools/editTool.d.ts.map +1 -1
- package/dist/tools/editTool.js +6 -5
- package/dist/tools/multiEditTool.d.ts.map +1 -1
- package/dist/tools/multiEditTool.js +7 -6
- package/dist/tools/taskOutputTool.d.ts +3 -0
- package/dist/tools/taskOutputTool.d.ts.map +1 -0
- package/dist/tools/taskOutputTool.js +149 -0
- package/dist/tools/taskStopTool.d.ts +3 -0
- package/dist/tools/taskStopTool.d.ts.map +1 -0
- package/dist/tools/taskStopTool.js +65 -0
- package/dist/tools/taskTool.d.ts.map +1 -1
- package/dist/tools/taskTool.js +105 -63
- package/dist/tools/types.d.ts +3 -0
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/types/marketplace.d.ts +1 -0
- package/dist/types/marketplace.d.ts.map +1 -1
- package/dist/types/messaging.d.ts +1 -0
- package/dist/types/messaging.d.ts.map +1 -1
- package/dist/types/processes.d.ts +24 -4
- package/dist/types/processes.d.ts.map +1 -1
- package/dist/utils/editUtils.d.ts +2 -11
- package/dist/utils/editUtils.d.ts.map +1 -1
- package/dist/utils/editUtils.js +52 -79
- package/dist/utils/messageOperations.d.ts +3 -1
- package/dist/utils/messageOperations.d.ts.map +1 -1
- package/dist/utils/messageOperations.js +5 -1
- package/package.json +5 -5
- package/src/agent.ts +73 -45
- package/src/constants/tools.ts +2 -2
- package/src/managers/MemoryRuleManager.ts +1 -1
- package/src/managers/aiManager.ts +6 -9
- package/src/managers/backgroundBashManager.ts +1 -0
- package/src/managers/backgroundTaskManager.ts +306 -0
- package/src/managers/foregroundTaskManager.ts +26 -0
- package/src/managers/lspManager.ts +3 -1
- package/src/managers/messageManager.ts +48 -2
- package/src/managers/permissionManager.ts +1 -7
- package/src/managers/pluginManager.ts +4 -3
- package/src/managers/slashCommandManager.ts +4 -0
- package/src/managers/subagentManager.ts +167 -35
- package/src/managers/toolManager.ts +16 -4
- package/src/services/GitService.ts +6 -2
- package/src/services/MarketplaceService.ts +30 -12
- package/src/services/MemoryRuleService.ts +18 -6
- package/src/services/memory.ts +1 -1
- package/src/tools/bashTool.ts +59 -196
- package/src/tools/editTool.ts +6 -17
- package/src/tools/multiEditTool.ts +7 -18
- package/src/tools/taskOutputTool.ts +174 -0
- package/src/tools/taskStopTool.ts +72 -0
- package/src/tools/taskTool.ts +130 -74
- package/src/tools/types.ts +3 -0
- package/src/types/marketplace.ts +1 -0
- package/src/types/messaging.ts +1 -0
- package/src/types/processes.ts +33 -4
- package/src/utils/editUtils.ts +65 -103
- package/src/utils/messageOperations.ts +7 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { minimatch } from "minimatch";
|
|
2
|
+
import * as path from "node:path";
|
|
2
3
|
import { parseFrontmatter } from "../utils/markdownParser.js";
|
|
3
4
|
import type { MemoryRule, MemoryRuleMetadata } from "../types/memoryRule.js";
|
|
4
5
|
|
|
@@ -45,15 +46,26 @@ export class MemoryRuleService {
|
|
|
45
46
|
/**
|
|
46
47
|
* Determines if a rule matches any of the given file paths using minimatch.
|
|
47
48
|
*/
|
|
48
|
-
isRuleActive(
|
|
49
|
+
isRuleActive(
|
|
50
|
+
rule: MemoryRule,
|
|
51
|
+
filesInContext: string[],
|
|
52
|
+
workdir?: string,
|
|
53
|
+
): boolean {
|
|
49
54
|
if (!rule.metadata.paths || rule.metadata.paths.length === 0) {
|
|
50
55
|
return true;
|
|
51
56
|
}
|
|
52
57
|
|
|
53
|
-
return filesInContext.some((filePath) =>
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
)
|
|
57
|
-
|
|
58
|
+
return filesInContext.some((filePath) => {
|
|
59
|
+
// Normalize path relative to workdir if it's an absolute path
|
|
60
|
+
let normalizedPath = filePath;
|
|
61
|
+
if (workdir && path.isAbsolute(filePath)) {
|
|
62
|
+
normalizedPath = path.relative(workdir, filePath);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return rule.metadata.paths!.some((pattern) => {
|
|
66
|
+
const isMatch = minimatch(normalizedPath, pattern, { dot: true });
|
|
67
|
+
return isMatch;
|
|
68
|
+
});
|
|
69
|
+
});
|
|
58
70
|
}
|
|
59
71
|
}
|
package/src/services/memory.ts
CHANGED
package/src/tools/bashTool.ts
CHANGED
|
@@ -4,8 +4,7 @@ import { stripAnsiColors } from "../utils/stringUtils.js";
|
|
|
4
4
|
import type { ToolPlugin, ToolResult, ToolContext } from "./types.js";
|
|
5
5
|
import {
|
|
6
6
|
BASH_TOOL_NAME,
|
|
7
|
-
|
|
8
|
-
KILL_BASH_TOOL_NAME,
|
|
7
|
+
TASK_OUTPUT_TOOL_NAME,
|
|
9
8
|
GLOB_TOOL_NAME,
|
|
10
9
|
GREP_TOOL_NAME,
|
|
11
10
|
READ_TOOL_NAME,
|
|
@@ -89,7 +88,7 @@ Usage notes:
|
|
|
89
88
|
},
|
|
90
89
|
run_in_background: {
|
|
91
90
|
type: "boolean",
|
|
92
|
-
description: `Set to true to run this command in the background. Use ${
|
|
91
|
+
description: `Set to true to run this command in the background. Use ${TASK_OUTPUT_TOOL_NAME} to read the output later.`,
|
|
93
92
|
},
|
|
94
93
|
},
|
|
95
94
|
required: ["command"],
|
|
@@ -164,20 +163,20 @@ Usage notes:
|
|
|
164
163
|
|
|
165
164
|
if (runInBackground) {
|
|
166
165
|
// Background execution
|
|
167
|
-
const
|
|
168
|
-
if (!
|
|
166
|
+
const backgroundTaskManager = context?.backgroundTaskManager;
|
|
167
|
+
if (!backgroundTaskManager) {
|
|
169
168
|
return {
|
|
170
169
|
success: false,
|
|
171
170
|
content: "",
|
|
172
|
-
error: "Background
|
|
171
|
+
error: "Background task manager not available",
|
|
173
172
|
};
|
|
174
173
|
}
|
|
175
174
|
|
|
176
|
-
const
|
|
175
|
+
const { id: taskId } = backgroundTaskManager.startShell(command, timeout);
|
|
177
176
|
return {
|
|
178
177
|
success: true,
|
|
179
|
-
content: `Command started in background with ID: ${
|
|
180
|
-
shortResult: `Background process ${
|
|
178
|
+
content: `Command started in background with ID: ${taskId}. Use TaskOutput tool with task_id="${taskId}" to monitor output.`,
|
|
179
|
+
shortResult: `Background process ${taskId} started`,
|
|
181
180
|
};
|
|
182
181
|
}
|
|
183
182
|
|
|
@@ -195,6 +194,41 @@ Usage notes:
|
|
|
195
194
|
let outputBuffer = "";
|
|
196
195
|
let errorBuffer = "";
|
|
197
196
|
let isAborted = false;
|
|
197
|
+
let isBackgrounded = false;
|
|
198
|
+
|
|
199
|
+
const foregroundTaskId = `bash_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
200
|
+
|
|
201
|
+
// Register as foreground task
|
|
202
|
+
if (context.foregroundTaskManager && command) {
|
|
203
|
+
context.foregroundTaskManager.registerForegroundTask({
|
|
204
|
+
id: foregroundTaskId,
|
|
205
|
+
backgroundHandler: async () => {
|
|
206
|
+
isBackgrounded = true;
|
|
207
|
+
if (timeoutHandle) {
|
|
208
|
+
clearTimeout(timeoutHandle);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const backgroundTaskManager = context.backgroundTaskManager;
|
|
212
|
+
if (backgroundTaskManager) {
|
|
213
|
+
const taskId = backgroundTaskManager.adoptProcess(
|
|
214
|
+
child,
|
|
215
|
+
command,
|
|
216
|
+
outputBuffer,
|
|
217
|
+
errorBuffer,
|
|
218
|
+
);
|
|
219
|
+
resolve({
|
|
220
|
+
success: true,
|
|
221
|
+
content: `Command moved to background with ID: ${taskId}. Use TaskOutput tool with task_id="${taskId}" to monitor output.`,
|
|
222
|
+
shortResult: `Process ${taskId} backgrounded`,
|
|
223
|
+
});
|
|
224
|
+
} else {
|
|
225
|
+
handleAbort(
|
|
226
|
+
"Failed to background: Background task manager not available",
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
}
|
|
198
232
|
|
|
199
233
|
// Set up timeout
|
|
200
234
|
let timeoutHandle: NodeJS.Timeout | undefined;
|
|
@@ -267,19 +301,25 @@ Usage notes:
|
|
|
267
301
|
}
|
|
268
302
|
|
|
269
303
|
child.stdout?.on("data", (data) => {
|
|
270
|
-
if (!isAborted) {
|
|
304
|
+
if (!isAborted && !isBackgrounded) {
|
|
271
305
|
outputBuffer += stripAnsiColors(data.toString());
|
|
272
306
|
}
|
|
273
307
|
});
|
|
274
308
|
|
|
275
309
|
child.stderr?.on("data", (data) => {
|
|
276
|
-
if (!isAborted) {
|
|
310
|
+
if (!isAborted && !isBackgrounded) {
|
|
277
311
|
errorBuffer += stripAnsiColors(data.toString());
|
|
278
312
|
}
|
|
279
313
|
});
|
|
280
314
|
|
|
281
315
|
child.on("exit", (code) => {
|
|
282
|
-
if (
|
|
316
|
+
if (context.foregroundTaskManager) {
|
|
317
|
+
context.foregroundTaskManager.unregisterForegroundTask(
|
|
318
|
+
foregroundTaskId,
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (!isAborted && !isBackgrounded) {
|
|
283
323
|
if (timeoutHandle) {
|
|
284
324
|
clearTimeout(timeoutHandle);
|
|
285
325
|
}
|
|
@@ -309,7 +349,13 @@ Usage notes:
|
|
|
309
349
|
});
|
|
310
350
|
|
|
311
351
|
child.on("error", (error) => {
|
|
312
|
-
if (
|
|
352
|
+
if (context.foregroundTaskManager) {
|
|
353
|
+
context.foregroundTaskManager.unregisterForegroundTask(
|
|
354
|
+
foregroundTaskId,
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (!isAborted && !isBackgrounded) {
|
|
313
359
|
if (timeoutHandle) {
|
|
314
360
|
clearTimeout(timeoutHandle);
|
|
315
361
|
}
|
|
@@ -334,186 +380,3 @@ Usage notes:
|
|
|
334
380
|
return `${command}${runInBackground ? " (background)" : ""}`;
|
|
335
381
|
},
|
|
336
382
|
};
|
|
337
|
-
|
|
338
|
-
/**
|
|
339
|
-
* BashOutput tool - retrieves output from background bash shells
|
|
340
|
-
*/
|
|
341
|
-
export const bashOutputTool: ToolPlugin = {
|
|
342
|
-
name: BASH_OUTPUT_TOOL_NAME,
|
|
343
|
-
config: {
|
|
344
|
-
type: "function",
|
|
345
|
-
function: {
|
|
346
|
-
name: BASH_OUTPUT_TOOL_NAME,
|
|
347
|
-
description:
|
|
348
|
-
"Retrieves output from a running or completed background bash shell",
|
|
349
|
-
parameters: {
|
|
350
|
-
type: "object",
|
|
351
|
-
properties: {
|
|
352
|
-
bash_id: {
|
|
353
|
-
type: "string",
|
|
354
|
-
description:
|
|
355
|
-
"The ID of the background shell to retrieve output from",
|
|
356
|
-
},
|
|
357
|
-
filter: {
|
|
358
|
-
type: "string",
|
|
359
|
-
description:
|
|
360
|
-
"Optional regular expression to filter the output lines. Only lines matching this regex will be included in the result. Any lines that do not match will no longer be available to read.",
|
|
361
|
-
},
|
|
362
|
-
},
|
|
363
|
-
required: ["bash_id"],
|
|
364
|
-
},
|
|
365
|
-
},
|
|
366
|
-
},
|
|
367
|
-
execute: async (
|
|
368
|
-
args: Record<string, unknown>,
|
|
369
|
-
context: ToolContext,
|
|
370
|
-
): Promise<ToolResult> => {
|
|
371
|
-
const bashId = args.bash_id as string;
|
|
372
|
-
const filter = args.filter as string | undefined;
|
|
373
|
-
|
|
374
|
-
if (!bashId || typeof bashId !== "string") {
|
|
375
|
-
return {
|
|
376
|
-
success: false,
|
|
377
|
-
content: "",
|
|
378
|
-
error: "bash_id parameter is required and must be a string",
|
|
379
|
-
};
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
const backgroundBashManager = context?.backgroundBashManager;
|
|
383
|
-
if (!backgroundBashManager) {
|
|
384
|
-
return {
|
|
385
|
-
success: false,
|
|
386
|
-
content: "",
|
|
387
|
-
error: "Background bash manager not available",
|
|
388
|
-
};
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
const output = backgroundBashManager.getOutput(bashId, filter);
|
|
392
|
-
if (!output) {
|
|
393
|
-
return {
|
|
394
|
-
success: false,
|
|
395
|
-
content: "",
|
|
396
|
-
error: `Background shell with ID ${bashId} not found`,
|
|
397
|
-
};
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
const shell = backgroundBashManager.getShell(bashId);
|
|
401
|
-
if (!shell) {
|
|
402
|
-
return {
|
|
403
|
-
success: false,
|
|
404
|
-
content: "",
|
|
405
|
-
error: `Background shell with ID ${bashId} not found`,
|
|
406
|
-
};
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
let content = "";
|
|
410
|
-
if (output.stdout) {
|
|
411
|
-
content += stripAnsiColors(output.stdout);
|
|
412
|
-
}
|
|
413
|
-
if (output.stderr) {
|
|
414
|
-
content += (content ? "\n" : "") + stripAnsiColors(output.stderr);
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
const finalContent = content || "No output available";
|
|
418
|
-
const processedContent =
|
|
419
|
-
finalContent.length > MAX_OUTPUT_LENGTH
|
|
420
|
-
? finalContent.substring(0, MAX_OUTPUT_LENGTH) +
|
|
421
|
-
"\n\n... (output truncated)"
|
|
422
|
-
: finalContent;
|
|
423
|
-
|
|
424
|
-
return {
|
|
425
|
-
success: true,
|
|
426
|
-
content: processedContent,
|
|
427
|
-
shortResult: `${bashId}: ${output.status}${shell.exitCode !== undefined ? ` (${shell.exitCode})` : ""}`,
|
|
428
|
-
error: undefined,
|
|
429
|
-
};
|
|
430
|
-
},
|
|
431
|
-
formatCompactParams: (params: Record<string, unknown>) => {
|
|
432
|
-
const bashId = params.bash_id as string;
|
|
433
|
-
const filter = params.filter as string | undefined;
|
|
434
|
-
return filter ? `${bashId} filtered: ${filter}` : bashId;
|
|
435
|
-
},
|
|
436
|
-
};
|
|
437
|
-
|
|
438
|
-
/**
|
|
439
|
-
* KillBash tool - kills a running background bash shell
|
|
440
|
-
*/
|
|
441
|
-
export const killBashTool: ToolPlugin = {
|
|
442
|
-
name: KILL_BASH_TOOL_NAME,
|
|
443
|
-
config: {
|
|
444
|
-
type: "function",
|
|
445
|
-
function: {
|
|
446
|
-
name: KILL_BASH_TOOL_NAME,
|
|
447
|
-
description: "Kills a running background bash shell by its ID",
|
|
448
|
-
parameters: {
|
|
449
|
-
type: "object",
|
|
450
|
-
properties: {
|
|
451
|
-
shell_id: {
|
|
452
|
-
type: "string",
|
|
453
|
-
description: "The ID of the background shell to kill",
|
|
454
|
-
},
|
|
455
|
-
},
|
|
456
|
-
required: ["shell_id"],
|
|
457
|
-
},
|
|
458
|
-
},
|
|
459
|
-
},
|
|
460
|
-
execute: async (
|
|
461
|
-
args: Record<string, unknown>,
|
|
462
|
-
context: ToolContext,
|
|
463
|
-
): Promise<ToolResult> => {
|
|
464
|
-
const shellId = args.shell_id as string;
|
|
465
|
-
|
|
466
|
-
if (!shellId || typeof shellId !== "string") {
|
|
467
|
-
return {
|
|
468
|
-
success: false,
|
|
469
|
-
content: "",
|
|
470
|
-
error: "shell_id parameter is required and must be a string",
|
|
471
|
-
};
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
const backgroundBashManager = context?.backgroundBashManager;
|
|
475
|
-
if (!backgroundBashManager) {
|
|
476
|
-
return {
|
|
477
|
-
success: false,
|
|
478
|
-
content: "",
|
|
479
|
-
error: "Background bash manager not available",
|
|
480
|
-
};
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
const shell = backgroundBashManager.getShell(shellId);
|
|
484
|
-
if (!shell) {
|
|
485
|
-
return {
|
|
486
|
-
success: false,
|
|
487
|
-
content: "",
|
|
488
|
-
error: `Background shell with ID ${shellId} not found`,
|
|
489
|
-
};
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
if (shell.status !== "running") {
|
|
493
|
-
return {
|
|
494
|
-
success: false,
|
|
495
|
-
content: "",
|
|
496
|
-
error: `Background shell ${shellId} is not running (status: ${shell.status})`,
|
|
497
|
-
};
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
const killed = backgroundBashManager.killShell(shellId);
|
|
501
|
-
if (killed) {
|
|
502
|
-
return {
|
|
503
|
-
success: true,
|
|
504
|
-
content: `Background shell ${shellId} has been killed`,
|
|
505
|
-
shortResult: `Killed ${shellId}`,
|
|
506
|
-
};
|
|
507
|
-
} else {
|
|
508
|
-
return {
|
|
509
|
-
success: false,
|
|
510
|
-
content: "",
|
|
511
|
-
error: `Failed to kill background shell ${shellId}`,
|
|
512
|
-
};
|
|
513
|
-
}
|
|
514
|
-
},
|
|
515
|
-
formatCompactParams: (params: Record<string, unknown>) => {
|
|
516
|
-
const shellId = params.shell_id as string;
|
|
517
|
-
return shellId;
|
|
518
|
-
},
|
|
519
|
-
};
|
package/src/tools/editTool.ts
CHANGED
|
@@ -2,11 +2,7 @@ import { readFile, writeFile } from "fs/promises";
|
|
|
2
2
|
import { logger } from "../utils/globalLogger.js";
|
|
3
3
|
import type { ToolPlugin, ToolResult, ToolContext } from "./types.js";
|
|
4
4
|
import { resolvePath, getDisplayPath } from "../utils/path.js";
|
|
5
|
-
import {
|
|
6
|
-
findIndentationInsensitiveMatch,
|
|
7
|
-
escapeRegExp,
|
|
8
|
-
saveEditErrorSnapshot,
|
|
9
|
-
} from "../utils/editUtils.js";
|
|
5
|
+
import { escapeRegExp, analyzeEditMismatch } from "../utils/editUtils.js";
|
|
10
6
|
import { EDIT_TOOL_NAME, READ_TOOL_NAME } from "../constants/tools.js";
|
|
11
7
|
|
|
12
8
|
/**
|
|
@@ -115,23 +111,16 @@ export const editTool: ToolPlugin = {
|
|
|
115
111
|
};
|
|
116
112
|
}
|
|
117
113
|
|
|
118
|
-
// Check if old_string exists
|
|
119
|
-
const matchedOldString =
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
);
|
|
114
|
+
// Check if old_string exists
|
|
115
|
+
const matchedOldString = originalContent.includes(oldString)
|
|
116
|
+
? oldString
|
|
117
|
+
: null;
|
|
123
118
|
|
|
124
119
|
if (!matchedOldString) {
|
|
125
|
-
await saveEditErrorSnapshot(
|
|
126
|
-
resolvedPath,
|
|
127
|
-
oldString,
|
|
128
|
-
originalContent,
|
|
129
|
-
EDIT_TOOL_NAME,
|
|
130
|
-
);
|
|
131
120
|
return {
|
|
132
121
|
success: false,
|
|
133
122
|
content: "",
|
|
134
|
-
error:
|
|
123
|
+
error: analyzeEditMismatch(originalContent, oldString),
|
|
135
124
|
};
|
|
136
125
|
}
|
|
137
126
|
|
|
@@ -2,11 +2,7 @@ import { readFile, writeFile } from "fs/promises";
|
|
|
2
2
|
import { logger } from "../utils/globalLogger.js";
|
|
3
3
|
import type { ToolPlugin, ToolResult, ToolContext } from "./types.js";
|
|
4
4
|
import { resolvePath, getDisplayPath } from "../utils/path.js";
|
|
5
|
-
import {
|
|
6
|
-
findIndentationInsensitiveMatch,
|
|
7
|
-
escapeRegExp,
|
|
8
|
-
saveEditErrorSnapshot,
|
|
9
|
-
} from "../utils/editUtils.js";
|
|
5
|
+
import { escapeRegExp, analyzeEditMismatch } from "../utils/editUtils.js";
|
|
10
6
|
import {
|
|
11
7
|
MULTI_EDIT_TOOL_NAME,
|
|
12
8
|
EDIT_TOOL_NAME,
|
|
@@ -185,23 +181,16 @@ export const multiEditTool: ToolPlugin = {
|
|
|
185
181
|
continue;
|
|
186
182
|
}
|
|
187
183
|
|
|
188
|
-
// Check if old_string exists
|
|
189
|
-
const matchedOldString =
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
);
|
|
184
|
+
// Check if old_string exists
|
|
185
|
+
const matchedOldString = currentContent.includes(edit.old_string)
|
|
186
|
+
? edit.old_string
|
|
187
|
+
: null;
|
|
193
188
|
|
|
194
189
|
if (!matchedOldString) {
|
|
195
|
-
await saveEditErrorSnapshot(
|
|
196
|
-
resolvedPath,
|
|
197
|
-
edit.old_string,
|
|
198
|
-
currentContent,
|
|
199
|
-
MULTI_EDIT_TOOL_NAME,
|
|
200
|
-
);
|
|
201
190
|
return {
|
|
202
191
|
success: false,
|
|
203
192
|
content: "",
|
|
204
|
-
error: `Edit operation ${i + 1}: old_string
|
|
193
|
+
error: `Edit operation ${i + 1}: ${analyzeEditMismatch(currentContent, edit.old_string)}`,
|
|
205
194
|
};
|
|
206
195
|
}
|
|
207
196
|
|
|
@@ -209,7 +198,7 @@ export const multiEditTool: ToolPlugin = {
|
|
|
209
198
|
|
|
210
199
|
if (replaceAll) {
|
|
211
200
|
// Replace all matches
|
|
212
|
-
const regex = new RegExp(escapeRegExp(
|
|
201
|
+
const regex = new RegExp(escapeRegExp(edit.old_string), "g");
|
|
213
202
|
currentContent = currentContent.replace(regex, edit.new_string);
|
|
214
203
|
replacementCount = (currentContent.match(regex) || []).length;
|
|
215
204
|
appliedEdits.push(
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { TASK_OUTPUT_TOOL_NAME } from "../constants/tools.js";
|
|
2
|
+
import { ToolContext, ToolPlugin, ToolResult } from "./types.js";
|
|
3
|
+
import { stripAnsiColors } from "../utils/stringUtils.js";
|
|
4
|
+
|
|
5
|
+
const MAX_OUTPUT_LENGTH = 30000;
|
|
6
|
+
|
|
7
|
+
export const taskOutputTool: ToolPlugin = {
|
|
8
|
+
name: TASK_OUTPUT_TOOL_NAME,
|
|
9
|
+
config: {
|
|
10
|
+
type: "function",
|
|
11
|
+
function: {
|
|
12
|
+
name: TASK_OUTPUT_TOOL_NAME,
|
|
13
|
+
description:
|
|
14
|
+
"Retrieves output from a running or completed background task",
|
|
15
|
+
parameters: {
|
|
16
|
+
type: "object",
|
|
17
|
+
properties: {
|
|
18
|
+
task_id: {
|
|
19
|
+
type: "string",
|
|
20
|
+
description:
|
|
21
|
+
"The ID of the background task to retrieve output from",
|
|
22
|
+
},
|
|
23
|
+
filter: {
|
|
24
|
+
type: "string",
|
|
25
|
+
description:
|
|
26
|
+
"Optional regular expression to filter the output lines.",
|
|
27
|
+
},
|
|
28
|
+
block: {
|
|
29
|
+
type: "boolean",
|
|
30
|
+
description:
|
|
31
|
+
"If true, wait for the task to complete before returning output. If false, return current output immediately.",
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
required: ["task_id"],
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
execute: async (
|
|
39
|
+
args: Record<string, unknown>,
|
|
40
|
+
context: ToolContext,
|
|
41
|
+
): Promise<ToolResult> => {
|
|
42
|
+
const taskId = args.task_id as string;
|
|
43
|
+
const filter = args.filter as string | undefined;
|
|
44
|
+
const block = args.block as boolean | undefined;
|
|
45
|
+
|
|
46
|
+
if (!taskId || typeof taskId !== "string") {
|
|
47
|
+
return {
|
|
48
|
+
success: false,
|
|
49
|
+
content: "",
|
|
50
|
+
error: "task_id parameter is required and must be a string",
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const backgroundTaskManager = context?.backgroundTaskManager;
|
|
55
|
+
if (!backgroundTaskManager) {
|
|
56
|
+
return {
|
|
57
|
+
success: false,
|
|
58
|
+
content: "",
|
|
59
|
+
error: "Background task manager not available",
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const getResult = () => {
|
|
64
|
+
const output = backgroundTaskManager.getOutput(taskId, filter);
|
|
65
|
+
if (!output) return null;
|
|
66
|
+
|
|
67
|
+
let content = "";
|
|
68
|
+
if (output.stdout) {
|
|
69
|
+
content += stripAnsiColors(output.stdout);
|
|
70
|
+
}
|
|
71
|
+
if (output.stderr) {
|
|
72
|
+
content += (content ? "\n" : "") + stripAnsiColors(output.stderr);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const finalContent = content || "No output available";
|
|
76
|
+
const processedContent =
|
|
77
|
+
finalContent.length > MAX_OUTPUT_LENGTH
|
|
78
|
+
? finalContent.substring(0, MAX_OUTPUT_LENGTH) +
|
|
79
|
+
"\n\n... (output truncated)"
|
|
80
|
+
: finalContent;
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
success: true,
|
|
84
|
+
content: processedContent,
|
|
85
|
+
shortResult: `${taskId}: ${output.status}`,
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
if (block) {
|
|
90
|
+
// Polling for completion
|
|
91
|
+
return new Promise((resolve) => {
|
|
92
|
+
let timeoutHandle: NodeJS.Timeout | null = null;
|
|
93
|
+
let isAborted = false;
|
|
94
|
+
|
|
95
|
+
const cleanup = () => {
|
|
96
|
+
if (timeoutHandle) {
|
|
97
|
+
clearTimeout(timeoutHandle);
|
|
98
|
+
timeoutHandle = null;
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const onAbort = () => {
|
|
103
|
+
isAborted = true;
|
|
104
|
+
cleanup();
|
|
105
|
+
resolve({
|
|
106
|
+
success: false,
|
|
107
|
+
content: "",
|
|
108
|
+
error: "Task output retrieval was aborted",
|
|
109
|
+
});
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
if (context.abortSignal) {
|
|
113
|
+
if (context.abortSignal.aborted) {
|
|
114
|
+
onAbort();
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
context.abortSignal.addEventListener("abort", onAbort, {
|
|
118
|
+
once: true,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const check = () => {
|
|
123
|
+
if (isAborted) return;
|
|
124
|
+
|
|
125
|
+
const task = backgroundTaskManager.getTask(taskId);
|
|
126
|
+
if (!task) {
|
|
127
|
+
if (context.abortSignal) {
|
|
128
|
+
context.abortSignal.removeEventListener("abort", onAbort);
|
|
129
|
+
}
|
|
130
|
+
resolve({
|
|
131
|
+
success: false,
|
|
132
|
+
content: "",
|
|
133
|
+
error: `Task with ID ${taskId} not found`,
|
|
134
|
+
});
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (task.status !== "running") {
|
|
139
|
+
if (context.abortSignal) {
|
|
140
|
+
context.abortSignal.removeEventListener("abort", onAbort);
|
|
141
|
+
}
|
|
142
|
+
const result = getResult();
|
|
143
|
+
resolve(
|
|
144
|
+
result || {
|
|
145
|
+
success: false,
|
|
146
|
+
content: "",
|
|
147
|
+
error: "Task not found",
|
|
148
|
+
},
|
|
149
|
+
);
|
|
150
|
+
} else {
|
|
151
|
+
timeoutHandle = setTimeout(check, 500);
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
check();
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const result = getResult();
|
|
159
|
+
if (!result) {
|
|
160
|
+
return {
|
|
161
|
+
success: false,
|
|
162
|
+
content: "",
|
|
163
|
+
error: `Task with ID ${taskId} not found`,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return result;
|
|
168
|
+
},
|
|
169
|
+
formatCompactParams: (params: Record<string, unknown>) => {
|
|
170
|
+
const taskId = params.task_id as string;
|
|
171
|
+
const block = params.block as boolean;
|
|
172
|
+
return `${taskId}${block ? " (blocking)" : ""}`;
|
|
173
|
+
},
|
|
174
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { TASK_STOP_TOOL_NAME } from "../constants/tools.js";
|
|
2
|
+
import { ToolContext, ToolPlugin, ToolResult } from "./types.js";
|
|
3
|
+
|
|
4
|
+
export const taskStopTool: ToolPlugin = {
|
|
5
|
+
name: TASK_STOP_TOOL_NAME,
|
|
6
|
+
config: {
|
|
7
|
+
type: "function",
|
|
8
|
+
function: {
|
|
9
|
+
name: TASK_STOP_TOOL_NAME,
|
|
10
|
+
description: "Stops a running background task",
|
|
11
|
+
parameters: {
|
|
12
|
+
type: "object",
|
|
13
|
+
properties: {
|
|
14
|
+
task_id: {
|
|
15
|
+
type: "string",
|
|
16
|
+
description: "The ID of the background task to stop",
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
required: ["task_id"],
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
execute: async (
|
|
24
|
+
args: Record<string, unknown>,
|
|
25
|
+
context: ToolContext,
|
|
26
|
+
): Promise<ToolResult> => {
|
|
27
|
+
const taskId = args.task_id as string;
|
|
28
|
+
|
|
29
|
+
if (!taskId || typeof taskId !== "string") {
|
|
30
|
+
return {
|
|
31
|
+
success: false,
|
|
32
|
+
content: "",
|
|
33
|
+
error: "task_id parameter is required and must be a string",
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const backgroundTaskManager = context?.backgroundTaskManager;
|
|
38
|
+
if (!backgroundTaskManager) {
|
|
39
|
+
return {
|
|
40
|
+
success: false,
|
|
41
|
+
content: "",
|
|
42
|
+
error: "Background task manager not available",
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const stopped = backgroundTaskManager.stopTask(taskId);
|
|
47
|
+
if (stopped) {
|
|
48
|
+
return {
|
|
49
|
+
success: true,
|
|
50
|
+
content: `Task ${taskId} has been stopped`,
|
|
51
|
+
shortResult: `Stopped ${taskId}`,
|
|
52
|
+
};
|
|
53
|
+
} else {
|
|
54
|
+
const task = backgroundTaskManager.getTask(taskId);
|
|
55
|
+
if (!task) {
|
|
56
|
+
return {
|
|
57
|
+
success: false,
|
|
58
|
+
content: "",
|
|
59
|
+
error: `Task with ID ${taskId} not found`,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
success: false,
|
|
64
|
+
content: "",
|
|
65
|
+
error: `Failed to stop task ${taskId} (status: ${task.status})`,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
formatCompactParams: (params: Record<string, unknown>) => {
|
|
70
|
+
return params.task_id as string;
|
|
71
|
+
},
|
|
72
|
+
};
|