wave-agent-sdk 0.9.7 → 0.10.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/core/session.d.ts +1 -1
- package/dist/core/session.d.ts.map +1 -1
- package/dist/core/session.js +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/managers/aiManager.d.ts.map +1 -1
- package/dist/managers/aiManager.js +1 -0
- package/dist/managers/backgroundTaskManager.d.ts.map +1 -1
- package/dist/managers/backgroundTaskManager.js +59 -6
- package/dist/managers/messageManager.d.ts +1 -6
- package/dist/managers/messageManager.d.ts.map +1 -1
- package/dist/managers/messageManager.js +0 -6
- package/dist/managers/permissionManager.d.ts +1 -1
- package/dist/managers/permissionManager.d.ts.map +1 -1
- package/dist/managers/permissionManager.js +17 -9
- package/dist/managers/skillManager.d.ts.map +1 -1
- package/dist/managers/skillManager.js +13 -1
- package/dist/managers/slashCommandManager.d.ts.map +1 -1
- package/dist/managers/slashCommandManager.js +0 -1
- package/dist/managers/subagentManager.d.ts +2 -0
- package/dist/managers/subagentManager.d.ts.map +1 -1
- package/dist/managers/subagentManager.js +22 -0
- package/dist/managers/toolManager.d.ts.map +1 -1
- package/dist/managers/toolManager.js +1 -0
- package/dist/services/session.d.ts +7 -0
- package/dist/services/session.d.ts.map +1 -1
- package/dist/services/session.js +33 -0
- package/dist/tools/agentTool.d.ts.map +1 -1
- package/dist/tools/agentTool.js +7 -3
- package/dist/tools/askUserQuestion.d.ts.map +1 -1
- package/dist/tools/askUserQuestion.js +8 -1
- package/dist/tools/bashTool.d.ts.map +1 -1
- package/dist/tools/bashTool.js +7 -3
- package/dist/tools/editTool.d.ts.map +1 -1
- package/dist/tools/editTool.js +1 -1
- package/dist/tools/exitPlanMode.d.ts.map +1 -1
- package/dist/tools/exitPlanMode.js +1 -1
- package/dist/tools/globTool.d.ts.map +1 -1
- package/dist/tools/globTool.js +9 -48
- package/dist/tools/grepTool.d.ts.map +1 -1
- package/dist/tools/grepTool.js +0 -6
- package/dist/tools/types.d.ts +2 -0
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/tools/writeTool.d.ts.map +1 -1
- package/dist/tools/writeTool.js +1 -1
- package/dist/types/permissions.d.ts +2 -0
- package/dist/types/permissions.d.ts.map +1 -1
- package/dist/types/processes.d.ts +4 -0
- package/dist/types/processes.d.ts.map +1 -1
- package/dist/utils/bashParser.d.ts.map +1 -1
- package/dist/utils/bashParser.js +50 -0
- package/dist/utils/fileSearch.d.ts.map +1 -1
- package/dist/utils/fileSearch.js +0 -5
- package/dist/utils/openaiClient.d.ts.map +1 -1
- package/dist/utils/openaiClient.js +24 -7
- package/package.json +1 -1
- package/src/core/session.ts +1 -0
- package/src/index.ts +0 -1
- package/src/managers/aiManager.ts +1 -0
- package/src/managers/backgroundTaskManager.ts +62 -6
- package/src/managers/messageManager.ts +1 -9
- package/src/managers/permissionManager.ts +21 -8
- package/src/managers/skillManager.ts +11 -1
- package/src/managers/slashCommandManager.ts +0 -1
- package/src/managers/subagentManager.ts +31 -0
- package/src/managers/toolManager.ts +1 -0
- package/src/services/session.ts +41 -0
- package/src/tools/agentTool.ts +9 -3
- package/src/tools/askUserQuestion.ts +10 -0
- package/src/tools/bashTool.ts +7 -2
- package/src/tools/editTool.ts +1 -0
- package/src/tools/exitPlanMode.ts +1 -0
- package/src/tools/globTool.ts +9 -61
- package/src/tools/grepTool.ts +0 -7
- package/src/tools/types.ts +2 -0
- package/src/tools/writeTool.ts +1 -0
- package/src/types/permissions.ts +2 -0
- package/src/types/processes.ts +4 -0
- package/src/utils/bashParser.ts +54 -0
- package/src/utils/fileSearch.ts +0 -5
- package/src/utils/openaiClient.ts +23 -7
- package/dist/utils/fileFilter.d.ts +0 -15
- package/dist/utils/fileFilter.d.ts.map +0 -1
- package/dist/utils/fileFilter.js +0 -35
- package/src/utils/fileFilter.ts +0 -39
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { spawn, type ChildProcess } from "child_process";
|
|
2
|
+
import * as os from "os";
|
|
3
|
+
import * as fs from "fs";
|
|
4
|
+
import * as path from "path";
|
|
2
5
|
import { BackgroundTask, BackgroundShell } from "../types/processes.js";
|
|
3
6
|
import { stripAnsiColors } from "../utils/stringUtils.js";
|
|
4
7
|
import { logger } from "../utils/globalLogger.js";
|
|
@@ -64,6 +67,10 @@ export class BackgroundTaskManager {
|
|
|
64
67
|
},
|
|
65
68
|
});
|
|
66
69
|
|
|
70
|
+
// Create log file
|
|
71
|
+
const logPath = path.join(os.tmpdir(), `wave-task-${id}.log`);
|
|
72
|
+
const logStream = fs.createWriteStream(logPath, { flags: "a" });
|
|
73
|
+
|
|
67
74
|
const shell: BackgroundShell = {
|
|
68
75
|
id,
|
|
69
76
|
type: "shell",
|
|
@@ -73,8 +80,10 @@ export class BackgroundTaskManager {
|
|
|
73
80
|
status: "running",
|
|
74
81
|
stdout: "",
|
|
75
82
|
stderr: "",
|
|
83
|
+
outputPath: logPath,
|
|
76
84
|
onStop: () => {
|
|
77
85
|
try {
|
|
86
|
+
logStream.end();
|
|
78
87
|
if (child.pid) {
|
|
79
88
|
process.kill(-child.pid, "SIGTERM");
|
|
80
89
|
setTimeout(() => {
|
|
@@ -109,12 +118,20 @@ export class BackgroundTaskManager {
|
|
|
109
118
|
}
|
|
110
119
|
|
|
111
120
|
const onStdout = (data: Buffer | string) => {
|
|
112
|
-
|
|
121
|
+
const stripped = stripAnsiColors(data.toString());
|
|
122
|
+
shell.stdout += stripped;
|
|
123
|
+
if (logStream.writable) {
|
|
124
|
+
logStream.write(stripped);
|
|
125
|
+
}
|
|
113
126
|
this.notifyTasksChange();
|
|
114
127
|
};
|
|
115
128
|
|
|
116
129
|
const onStderr = (data: Buffer | string) => {
|
|
117
|
-
|
|
130
|
+
const stripped = stripAnsiColors(data.toString());
|
|
131
|
+
shell.stderr += stripped;
|
|
132
|
+
if (logStream.writable) {
|
|
133
|
+
logStream.write(stripped);
|
|
134
|
+
}
|
|
118
135
|
this.notifyTasksChange();
|
|
119
136
|
};
|
|
120
137
|
|
|
@@ -122,6 +139,9 @@ export class BackgroundTaskManager {
|
|
|
122
139
|
if (timeoutHandle) {
|
|
123
140
|
clearTimeout(timeoutHandle);
|
|
124
141
|
}
|
|
142
|
+
if (logStream.writable) {
|
|
143
|
+
logStream.end();
|
|
144
|
+
}
|
|
125
145
|
shell.status = code === 0 ? "completed" : "failed";
|
|
126
146
|
shell.exitCode = code ?? 0;
|
|
127
147
|
shell.endTime = Date.now();
|
|
@@ -133,8 +153,13 @@ export class BackgroundTaskManager {
|
|
|
133
153
|
if (timeoutHandle) {
|
|
134
154
|
clearTimeout(timeoutHandle);
|
|
135
155
|
}
|
|
156
|
+
const stripped = `\nProcess error: ${stripAnsiColors(error.message)}`;
|
|
136
157
|
shell.status = "failed";
|
|
137
|
-
shell.stderr +=
|
|
158
|
+
shell.stderr += stripped;
|
|
159
|
+
if (logStream.writable) {
|
|
160
|
+
logStream.write(stripped);
|
|
161
|
+
logStream.end();
|
|
162
|
+
}
|
|
138
163
|
shell.exitCode = 1;
|
|
139
164
|
shell.endTime = Date.now();
|
|
140
165
|
shell.runtime = shell.endTime - startTime;
|
|
@@ -154,6 +179,7 @@ export class BackgroundTaskManager {
|
|
|
154
179
|
if (timeoutHandle) {
|
|
155
180
|
clearTimeout(timeoutHandle);
|
|
156
181
|
}
|
|
182
|
+
logStream.end();
|
|
157
183
|
this.tasks.delete(id);
|
|
158
184
|
this.notifyTasksChange();
|
|
159
185
|
};
|
|
@@ -170,6 +196,18 @@ export class BackgroundTaskManager {
|
|
|
170
196
|
const id = this.generateId();
|
|
171
197
|
const startTime = Date.now();
|
|
172
198
|
|
|
199
|
+
// Create log file
|
|
200
|
+
const logPath = path.join(os.tmpdir(), `wave-task-${id}.log`);
|
|
201
|
+
const logStream = fs.createWriteStream(logPath, { flags: "a" });
|
|
202
|
+
|
|
203
|
+
// Write initial output to log file
|
|
204
|
+
if (initialStdout) {
|
|
205
|
+
logStream.write(stripAnsiColors(initialStdout));
|
|
206
|
+
}
|
|
207
|
+
if (initialStderr) {
|
|
208
|
+
logStream.write(stripAnsiColors(initialStderr));
|
|
209
|
+
}
|
|
210
|
+
|
|
173
211
|
const shell: BackgroundShell = {
|
|
174
212
|
id,
|
|
175
213
|
type: "shell",
|
|
@@ -179,8 +217,10 @@ export class BackgroundTaskManager {
|
|
|
179
217
|
status: "running",
|
|
180
218
|
stdout: initialStdout,
|
|
181
219
|
stderr: initialStderr,
|
|
220
|
+
outputPath: logPath,
|
|
182
221
|
onStop: () => {
|
|
183
222
|
try {
|
|
223
|
+
logStream.end();
|
|
184
224
|
if (child.pid) {
|
|
185
225
|
process.kill(-child.pid, "SIGTERM");
|
|
186
226
|
setTimeout(() => {
|
|
@@ -205,16 +245,27 @@ export class BackgroundTaskManager {
|
|
|
205
245
|
this.notifyTasksChange();
|
|
206
246
|
|
|
207
247
|
child.stdout?.on("data", (data) => {
|
|
208
|
-
|
|
248
|
+
const stripped = stripAnsiColors(data.toString());
|
|
249
|
+
shell.stdout += stripped;
|
|
250
|
+
if (logStream.writable) {
|
|
251
|
+
logStream.write(stripped);
|
|
252
|
+
}
|
|
209
253
|
this.notifyTasksChange();
|
|
210
254
|
});
|
|
211
255
|
|
|
212
256
|
child.stderr?.on("data", (data) => {
|
|
213
|
-
|
|
257
|
+
const stripped = stripAnsiColors(data.toString());
|
|
258
|
+
shell.stderr += stripped;
|
|
259
|
+
if (logStream.writable) {
|
|
260
|
+
logStream.write(stripped);
|
|
261
|
+
}
|
|
214
262
|
this.notifyTasksChange();
|
|
215
263
|
});
|
|
216
264
|
|
|
217
265
|
child.on("exit", (code) => {
|
|
266
|
+
if (logStream.writable) {
|
|
267
|
+
logStream.end();
|
|
268
|
+
}
|
|
218
269
|
shell.status = code === 0 ? "completed" : "failed";
|
|
219
270
|
shell.exitCode = code ?? 0;
|
|
220
271
|
shell.endTime = Date.now();
|
|
@@ -223,8 +274,13 @@ export class BackgroundTaskManager {
|
|
|
223
274
|
});
|
|
224
275
|
|
|
225
276
|
child.on("error", (error) => {
|
|
277
|
+
const stripped = `\nProcess error: ${stripAnsiColors(error.message)}`;
|
|
226
278
|
shell.status = "failed";
|
|
227
|
-
shell.stderr +=
|
|
279
|
+
shell.stderr += stripped;
|
|
280
|
+
if (logStream.writable) {
|
|
281
|
+
logStream.write(stripped);
|
|
282
|
+
logStream.end();
|
|
283
|
+
}
|
|
228
284
|
shell.exitCode = 1;
|
|
229
285
|
shell.endTime = Date.now();
|
|
230
286
|
shell.runtime = shell.endTime - startTime;
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
type AgentToolBlockUpdateParams,
|
|
13
13
|
generateMessageId,
|
|
14
14
|
} from "../utils/messageOperations.js";
|
|
15
|
-
import type { Message, Usage
|
|
15
|
+
import type { Message, Usage } from "../types/index.js";
|
|
16
16
|
import { join } from "path";
|
|
17
17
|
import {
|
|
18
18
|
appendMessages,
|
|
@@ -55,7 +55,6 @@ export interface MessageManagerCallbacks {
|
|
|
55
55
|
onAddBangMessage?: (command: string) => void;
|
|
56
56
|
onUpdateBangMessage?: (command: string, output: string) => void;
|
|
57
57
|
onCompleteBangMessage?: (command: string, exitCode: number) => void;
|
|
58
|
-
onSlashCommandsChange?: (commands: SlashCommand[]) => void;
|
|
59
58
|
onInfoBlockAdded?: (content: string) => void;
|
|
60
59
|
// Rewind callbacks
|
|
61
60
|
onShowRewind?: () => void;
|
|
@@ -297,13 +296,6 @@ export class MessageManager {
|
|
|
297
296
|
this.callbacks.onShowRewind?.();
|
|
298
297
|
}
|
|
299
298
|
|
|
300
|
-
/**
|
|
301
|
-
* Trigger slash commands change callback
|
|
302
|
-
*/
|
|
303
|
-
public triggerSlashCommandsChange(commands: SlashCommand[]): void {
|
|
304
|
-
this.callbacks.onSlashCommandsChange?.(commands);
|
|
305
|
-
}
|
|
306
|
-
|
|
307
299
|
// Initialize state from session data
|
|
308
300
|
public initializeFromSession(sessionData: SessionData): void {
|
|
309
301
|
this.setSessionId(sessionData.id);
|
|
@@ -69,6 +69,7 @@ const DEFAULT_ALLOWED_RULES = [
|
|
|
69
69
|
"Bash(whoami*)",
|
|
70
70
|
"Bash(date*)",
|
|
71
71
|
"Bash(uptime*)",
|
|
72
|
+
"Bash(wc -l*)",
|
|
72
73
|
];
|
|
73
74
|
|
|
74
75
|
import { logger } from "../utils/globalLogger.js";
|
|
@@ -457,6 +458,7 @@ export class PermissionManager {
|
|
|
457
458
|
permissionMode: PermissionMode,
|
|
458
459
|
callback?: PermissionCallback,
|
|
459
460
|
toolInput?: Record<string, unknown>,
|
|
461
|
+
toolCallId?: string,
|
|
460
462
|
): ToolPermissionContext {
|
|
461
463
|
let suggestedPrefix: string | undefined;
|
|
462
464
|
if (toolName === BASH_TOOL_NAME && toolInput?.command) {
|
|
@@ -475,6 +477,7 @@ export class PermissionManager {
|
|
|
475
477
|
canUseToolCallback: callback,
|
|
476
478
|
toolInput,
|
|
477
479
|
suggestedPrefix,
|
|
480
|
+
toolCallId,
|
|
478
481
|
};
|
|
479
482
|
|
|
480
483
|
// Set hidePersistentOption for out-of-bounds file operations
|
|
@@ -512,8 +515,8 @@ export class PermissionManager {
|
|
|
512
515
|
return true;
|
|
513
516
|
}
|
|
514
517
|
|
|
515
|
-
// Check out-of-bounds for cd
|
|
516
|
-
if (cmd === "cd"
|
|
518
|
+
// Check out-of-bounds for cd
|
|
519
|
+
if (cmd === "cd") {
|
|
517
520
|
const pathArgs =
|
|
518
521
|
(args.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g) || []).filter(
|
|
519
522
|
(arg) => !arg.startsWith("-"),
|
|
@@ -642,19 +645,24 @@ export class PermissionManager {
|
|
|
642
645
|
const args = commandMatch[2]?.trim() || "";
|
|
643
646
|
|
|
644
647
|
if (SAFE_COMMANDS.includes(cmd)) {
|
|
645
|
-
if (
|
|
648
|
+
if (
|
|
649
|
+
cmd === "pwd" ||
|
|
650
|
+
cmd === "true" ||
|
|
651
|
+
cmd === "false" ||
|
|
652
|
+
cmd === "ls"
|
|
653
|
+
) {
|
|
646
654
|
return true;
|
|
647
655
|
}
|
|
648
656
|
|
|
649
657
|
if (workdir) {
|
|
650
|
-
// For cd
|
|
658
|
+
// For cd, check paths
|
|
651
659
|
const pathArgs =
|
|
652
660
|
(args.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g) || []).filter(
|
|
653
661
|
(arg) => !arg.startsWith("-"),
|
|
654
662
|
) || [];
|
|
655
663
|
|
|
656
664
|
if (pathArgs.length === 0) {
|
|
657
|
-
// cd
|
|
665
|
+
// cd without arguments operates on current dir (workdir)
|
|
658
666
|
return true;
|
|
659
667
|
}
|
|
660
668
|
|
|
@@ -739,10 +747,15 @@ export class PermissionManager {
|
|
|
739
747
|
const args = commandMatch[2]?.trim() || "";
|
|
740
748
|
|
|
741
749
|
if (SAFE_COMMANDS.includes(cmd)) {
|
|
742
|
-
if (
|
|
750
|
+
if (
|
|
751
|
+
cmd === "pwd" ||
|
|
752
|
+
cmd === "true" ||
|
|
753
|
+
cmd === "false" ||
|
|
754
|
+
cmd === "ls"
|
|
755
|
+
) {
|
|
743
756
|
isSafe = true;
|
|
744
757
|
} else {
|
|
745
|
-
// For cd
|
|
758
|
+
// For cd, check paths
|
|
746
759
|
const pathArgs =
|
|
747
760
|
(args.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g) || []).filter(
|
|
748
761
|
(arg) => !arg.startsWith("-"),
|
|
@@ -775,7 +788,7 @@ export class PermissionManager {
|
|
|
775
788
|
continue;
|
|
776
789
|
}
|
|
777
790
|
|
|
778
|
-
if (cmd === "cd"
|
|
791
|
+
if (cmd === "cd") {
|
|
779
792
|
const pathArgs =
|
|
780
793
|
(args.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g) || []).filter(
|
|
781
794
|
(arg) => !arg.startsWith("-"),
|
|
@@ -243,8 +243,18 @@ export class SkillManager {
|
|
|
243
243
|
const entries = await readdir(skillsPath, { withFileTypes: true });
|
|
244
244
|
|
|
245
245
|
for (const entry of entries) {
|
|
246
|
+
const fullPath = join(skillsPath, entry.name);
|
|
246
247
|
if (entry.isDirectory()) {
|
|
247
|
-
directories.push(
|
|
248
|
+
directories.push(fullPath);
|
|
249
|
+
} else if (entry.isSymbolicLink()) {
|
|
250
|
+
try {
|
|
251
|
+
const s = await stat(fullPath);
|
|
252
|
+
if (s.isDirectory()) {
|
|
253
|
+
directories.push(fullPath);
|
|
254
|
+
}
|
|
255
|
+
} catch {
|
|
256
|
+
// Ignore broken symlinks or other errors
|
|
257
|
+
}
|
|
248
258
|
}
|
|
249
259
|
}
|
|
250
260
|
} catch {
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from "crypto";
|
|
2
|
+
import * as os from "os";
|
|
3
|
+
import * as fs from "fs";
|
|
4
|
+
import * as path from "path";
|
|
2
5
|
import type { SubagentConfiguration } from "../utils/subagentParser.js";
|
|
3
6
|
import type { Message, Usage } from "../types/index.js";
|
|
4
7
|
import { AIManager } from "./aiManager.js";
|
|
@@ -70,6 +73,7 @@ export interface SubagentInstance {
|
|
|
70
73
|
backgroundTaskId?: string; // ID of the background task if transitioned
|
|
71
74
|
onUpdate?: () => void; // Optional callback for real-time updates
|
|
72
75
|
model?: string; // Optional model override
|
|
76
|
+
logStream?: fs.WriteStream; // Optional log stream for background tasks
|
|
73
77
|
}
|
|
74
78
|
|
|
75
79
|
export interface SubagentManagerOptions {
|
|
@@ -274,6 +278,11 @@ export class SubagentManager {
|
|
|
274
278
|
const taskId = backgroundTaskManager.generateId();
|
|
275
279
|
const startTime = Date.now();
|
|
276
280
|
|
|
281
|
+
// Create log file
|
|
282
|
+
const logPath = path.join(os.tmpdir(), `wave-subagent-${taskId}.log`);
|
|
283
|
+
const logStream = fs.createWriteStream(logPath, { flags: "a" });
|
|
284
|
+
instance.logStream = logStream;
|
|
285
|
+
|
|
277
286
|
backgroundTaskManager.addTask({
|
|
278
287
|
id: taskId,
|
|
279
288
|
type: "subagent",
|
|
@@ -282,8 +291,10 @@ export class SubagentManager {
|
|
|
282
291
|
description: instance.description,
|
|
283
292
|
stdout: "",
|
|
284
293
|
stderr: "",
|
|
294
|
+
outputPath: logPath,
|
|
285
295
|
subagentId: instance.subagentId,
|
|
286
296
|
onStop: () => {
|
|
297
|
+
instance.logStream?.end();
|
|
287
298
|
instance.aiManager.abortAIMessage();
|
|
288
299
|
this.cleanupInstance(instance.subagentId);
|
|
289
300
|
},
|
|
@@ -345,6 +356,11 @@ export class SubagentManager {
|
|
|
345
356
|
const taskId = backgroundTaskManager.generateId();
|
|
346
357
|
const startTime = Date.now();
|
|
347
358
|
|
|
359
|
+
// Create log file
|
|
360
|
+
const logPath = path.join(os.tmpdir(), `wave-subagent-${taskId}.log`);
|
|
361
|
+
const logStream = fs.createWriteStream(logPath, { flags: "a" });
|
|
362
|
+
instance.logStream = logStream;
|
|
363
|
+
|
|
348
364
|
backgroundTaskManager.addTask({
|
|
349
365
|
id: taskId,
|
|
350
366
|
type: "subagent",
|
|
@@ -353,8 +369,10 @@ export class SubagentManager {
|
|
|
353
369
|
description: instance.description,
|
|
354
370
|
stdout: "",
|
|
355
371
|
stderr: "",
|
|
372
|
+
outputPath: logPath,
|
|
356
373
|
subagentId: instance.subagentId,
|
|
357
374
|
onStop: () => {
|
|
375
|
+
instance.logStream?.end();
|
|
358
376
|
instance.aiManager.abortAIMessage();
|
|
359
377
|
this.cleanupInstance(instance.subagentId);
|
|
360
378
|
},
|
|
@@ -446,6 +464,7 @@ export class SubagentManager {
|
|
|
446
464
|
|
|
447
465
|
// If this was transitioned to background, update the background task
|
|
448
466
|
if (instance.backgroundTaskId && backgroundTaskManager) {
|
|
467
|
+
instance.logStream?.end();
|
|
449
468
|
const task = backgroundTaskManager.getTask(instance.backgroundTaskId);
|
|
450
469
|
if (task) {
|
|
451
470
|
task.status = "completed";
|
|
@@ -465,6 +484,7 @@ export class SubagentManager {
|
|
|
465
484
|
|
|
466
485
|
// If this was transitioned to background, update the background task with error
|
|
467
486
|
if (instance.backgroundTaskId && backgroundTaskManager) {
|
|
487
|
+
instance.logStream?.end();
|
|
468
488
|
const task = backgroundTaskManager.getTask(instance.backgroundTaskId);
|
|
469
489
|
if (task) {
|
|
470
490
|
task.status = "failed";
|
|
@@ -599,6 +619,17 @@ export class SubagentManager {
|
|
|
599
619
|
instance.lastTools.shift();
|
|
600
620
|
}
|
|
601
621
|
instance.onUpdate?.();
|
|
622
|
+
|
|
623
|
+
// Log tool execution to file
|
|
624
|
+
if (instance.logStream) {
|
|
625
|
+
const compactParams = (params.parameters || "{}").substring(
|
|
626
|
+
0,
|
|
627
|
+
100,
|
|
628
|
+
);
|
|
629
|
+
instance.logStream.write(
|
|
630
|
+
`[${new Date().toISOString()}] Running tool: ${params.name} with params: ${compactParams}${compactParams.length >= 100 ? "..." : ""}\n`,
|
|
631
|
+
);
|
|
632
|
+
}
|
|
602
633
|
}
|
|
603
634
|
}
|
|
604
635
|
|
package/src/services/session.ts
CHANGED
|
@@ -798,6 +798,47 @@ export async function getFirstMessageContent(
|
|
|
798
798
|
}
|
|
799
799
|
}
|
|
800
800
|
|
|
801
|
+
/**
|
|
802
|
+
* Delete a session
|
|
803
|
+
* @param sessionId - UUID session identifier
|
|
804
|
+
* @param workdir - Working directory for the session
|
|
805
|
+
* @param sessionType - Type of session ("main" or "subagent", defaults to "main")
|
|
806
|
+
*/
|
|
807
|
+
export async function deleteSession(
|
|
808
|
+
sessionId: string,
|
|
809
|
+
workdir: string,
|
|
810
|
+
sessionType: "main" | "subagent" = "main",
|
|
811
|
+
): Promise<void> {
|
|
812
|
+
const filePath = await generateSessionFilePath(
|
|
813
|
+
sessionId,
|
|
814
|
+
workdir,
|
|
815
|
+
sessionType,
|
|
816
|
+
);
|
|
817
|
+
try {
|
|
818
|
+
await fs.unlink(filePath);
|
|
819
|
+
} catch (error) {
|
|
820
|
+
// Ignore if file doesn't exist
|
|
821
|
+
if ((error as NodeJS.ErrnoException).code !== "ENOENT") {
|
|
822
|
+
throw error;
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
// Also remove from index
|
|
827
|
+
try {
|
|
828
|
+
const encoder = new PathEncoder();
|
|
829
|
+
const projectDir = await encoder.getProjectDirectory(workdir, SESSION_DIR);
|
|
830
|
+
const indexPath = join(projectDir.encodedPath, SESSION_INDEX_FILENAME);
|
|
831
|
+
const indexContent = await fs.readFile(indexPath, "utf8");
|
|
832
|
+
const index = JSON.parse(indexContent) as SessionIndex;
|
|
833
|
+
if (index.sessions[sessionId]) {
|
|
834
|
+
delete index.sessions[sessionId];
|
|
835
|
+
await fs.writeFile(indexPath, JSON.stringify(index, null, 2), "utf8");
|
|
836
|
+
}
|
|
837
|
+
} catch {
|
|
838
|
+
// Ignore index errors
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
|
|
801
842
|
/**
|
|
802
843
|
* Truncate content to a maximum length, adding ellipsis if truncated
|
|
803
844
|
* @param content - The content to truncate
|
package/src/tools/agentTool.ts
CHANGED
|
@@ -182,10 +182,14 @@ When using the Agent tool, you must specify a subagent_type parameter to select
|
|
|
182
182
|
id: instance.subagentId,
|
|
183
183
|
backgroundHandler: async () => {
|
|
184
184
|
isBackgrounded = true;
|
|
185
|
-
await subagentManager.backgroundInstance(
|
|
185
|
+
const taskId = await subagentManager.backgroundInstance(
|
|
186
|
+
instance.subagentId,
|
|
187
|
+
);
|
|
188
|
+
const task = context.backgroundTaskManager?.getTask(taskId);
|
|
189
|
+
const outputPath = task?.outputPath;
|
|
186
190
|
resolve({
|
|
187
191
|
success: true,
|
|
188
|
-
content:
|
|
192
|
+
content: `Agent backgrounded with ID: ${taskId}.${outputPath ? ` Real-time output: ${outputPath}` : ""}`,
|
|
189
193
|
shortResult: "Agent backgrounded",
|
|
190
194
|
isManuallyBackgrounded: true,
|
|
191
195
|
});
|
|
@@ -206,9 +210,11 @@ When using the Agent tool, you must specify a subagent_type parameter to select
|
|
|
206
210
|
}
|
|
207
211
|
|
|
208
212
|
if (run_in_background) {
|
|
213
|
+
const task = context.backgroundTaskManager?.getTask(result);
|
|
214
|
+
const outputPath = task?.outputPath;
|
|
209
215
|
resolve({
|
|
210
216
|
success: true,
|
|
211
|
-
content: `Agent started in background with ID: ${result}`,
|
|
217
|
+
content: `Agent started in background with ID: ${result}.${outputPath ? ` Real-time output: ${outputPath}` : ""}`,
|
|
212
218
|
shortResult: `Agent started in background: ${result}`,
|
|
213
219
|
});
|
|
214
220
|
return;
|
|
@@ -106,6 +106,15 @@ Plan mode note: In plan mode, use this tool to clarify requirements or choose be
|
|
|
106
106
|
metadata,
|
|
107
107
|
} = args as unknown as AskUserQuestionInput;
|
|
108
108
|
|
|
109
|
+
if (!questions || !Array.isArray(questions) || questions.length === 0) {
|
|
110
|
+
return {
|
|
111
|
+
success: false,
|
|
112
|
+
content: "",
|
|
113
|
+
error:
|
|
114
|
+
"The 'questions' parameter is missing or empty. Please use the correct schema: { questions: [{ question, header, options, multiSelect? }] }",
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
109
118
|
if (!context.permissionManager) {
|
|
110
119
|
throw new Error(
|
|
111
120
|
`Permission manager is required for ${ASK_USER_QUESTION_TOOL_NAME} tool`,
|
|
@@ -117,6 +126,7 @@ Plan mode note: In plan mode, use this tool to clarify requirements or choose be
|
|
|
117
126
|
context.permissionMode || "default",
|
|
118
127
|
context.canUseToolCallback,
|
|
119
128
|
{ questions, answers: existingAnswers, metadata },
|
|
129
|
+
context.toolCallId,
|
|
120
130
|
);
|
|
121
131
|
permissionContext.hidePersistentOption = true; // Always hide persistent option for questions
|
|
122
132
|
|
package/src/tools/bashTool.ts
CHANGED
|
@@ -145,6 +145,7 @@ Usage notes:
|
|
|
145
145
|
timeout,
|
|
146
146
|
workdir: context.workdir,
|
|
147
147
|
},
|
|
148
|
+
context.toolCallId,
|
|
148
149
|
);
|
|
149
150
|
const permissionResult =
|
|
150
151
|
await context.permissionManager.checkPermission(permissionContext);
|
|
@@ -177,9 +178,11 @@ Usage notes:
|
|
|
177
178
|
}
|
|
178
179
|
|
|
179
180
|
const { id: taskId } = backgroundTaskManager.startShell(command, timeout);
|
|
181
|
+
const task = backgroundTaskManager.getTask(taskId);
|
|
182
|
+
const outputPath = task?.outputPath;
|
|
180
183
|
return {
|
|
181
184
|
success: true,
|
|
182
|
-
content: `Command started in background with ID: ${taskId}
|
|
185
|
+
content: `Command started in background with ID: ${taskId}.${outputPath ? ` Real-time output: ${outputPath}` : ` Use TaskOutput tool with task_id="${taskId}" to monitor output.`}`,
|
|
183
186
|
shortResult: `Background process ${taskId} started`,
|
|
184
187
|
};
|
|
185
188
|
}
|
|
@@ -220,9 +223,11 @@ Usage notes:
|
|
|
220
223
|
outputBuffer,
|
|
221
224
|
errorBuffer,
|
|
222
225
|
);
|
|
226
|
+
const task = backgroundTaskManager.getTask(taskId);
|
|
227
|
+
const outputPath = task?.outputPath;
|
|
223
228
|
resolve({
|
|
224
229
|
success: true,
|
|
225
|
-
content: `Command moved to background with ID: ${taskId}
|
|
230
|
+
content: `Command moved to background with ID: ${taskId}.${outputPath ? ` Real-time output: ${outputPath}` : ""}`,
|
|
226
231
|
shortResult: `Process ${taskId} backgrounded`,
|
|
227
232
|
isManuallyBackgrounded: true,
|
|
228
233
|
});
|
package/src/tools/editTool.ts
CHANGED
package/src/tools/globTool.ts
CHANGED
|
@@ -1,70 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { rgPath } from "../utils/ripgrep.js";
|
|
1
|
+
import { glob } from "glob";
|
|
3
2
|
import { stat } from "fs/promises";
|
|
4
3
|
import type { ToolPlugin, ToolResult, ToolContext } from "./types.js";
|
|
5
4
|
import { resolvePath, getDisplayPath } from "../utils/path.js";
|
|
6
|
-
import { getAllIgnorePatterns } from "../utils/fileFilter.js";
|
|
7
5
|
import { GLOB_TOOL_NAME } from "../constants/tools.js";
|
|
8
6
|
|
|
9
7
|
/**
|
|
10
8
|
* Maximum number of files returned by glob tool
|
|
11
9
|
*/
|
|
12
|
-
const MAX_GLOB_RESULTS =
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Execute ripgrep to find files matching a pattern
|
|
16
|
-
*/
|
|
17
|
-
async function runRipgrep(pattern: string, workdir: string): Promise<string[]> {
|
|
18
|
-
if (!rgPath) {
|
|
19
|
-
throw new Error("ripgrep is not available");
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const ignorePatterns = getAllIgnorePatterns();
|
|
23
|
-
const rgArgs = ["--files", "--color=never", "--hidden", "--glob", pattern];
|
|
24
|
-
|
|
25
|
-
for (const ignorePattern of ignorePatterns) {
|
|
26
|
-
rgArgs.push("--glob", `!${ignorePattern}`);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return new Promise<string[]>((resolve, reject) => {
|
|
30
|
-
const child = spawn(rgPath, rgArgs, {
|
|
31
|
-
cwd: workdir,
|
|
32
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
let stdout = "";
|
|
36
|
-
let stderr = "";
|
|
37
|
-
|
|
38
|
-
child.stdout?.on("data", (data) => {
|
|
39
|
-
stdout += data.toString();
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
child.stderr?.on("data", (data) => {
|
|
43
|
-
stderr += data.toString();
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
child.on("close", (code) => {
|
|
47
|
-
if (code !== 0 && code !== 1) {
|
|
48
|
-
reject(
|
|
49
|
-
new Error(
|
|
50
|
-
`ripgrep failed with code ${code}: ${stderr || "Unknown error"}`,
|
|
51
|
-
),
|
|
52
|
-
);
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
const files = stdout
|
|
56
|
-
.trim()
|
|
57
|
-
.split("\n")
|
|
58
|
-
.filter((f) => f.length > 0)
|
|
59
|
-
.map((f) => f.replace(/\\/g, "/")); // Normalize to forward slashes
|
|
60
|
-
resolve(files);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
child.on("error", (err) => {
|
|
64
|
-
reject(err);
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
}
|
|
10
|
+
const MAX_GLOB_RESULTS = 100;
|
|
68
11
|
|
|
69
12
|
/**
|
|
70
13
|
* Glob Tool Plugin - Fast file pattern matching
|
|
@@ -122,8 +65,13 @@ export const globTool: ToolPlugin = {
|
|
|
122
65
|
? resolvePath(searchPath, context.workdir)
|
|
123
66
|
: context.workdir;
|
|
124
67
|
|
|
125
|
-
// Execute glob search using
|
|
126
|
-
const matches = await
|
|
68
|
+
// Execute glob search using glob package
|
|
69
|
+
const matches = await glob(pattern, {
|
|
70
|
+
cwd: workdir,
|
|
71
|
+
nodir: true,
|
|
72
|
+
dot: true,
|
|
73
|
+
ignore: ["**/.git/**"],
|
|
74
|
+
});
|
|
127
75
|
|
|
128
76
|
if (matches.length === 0) {
|
|
129
77
|
return {
|
package/src/tools/grepTool.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { ToolPlugin, ToolResult, ToolContext } from "./types.js";
|
|
2
2
|
import { spawn } from "child_process";
|
|
3
|
-
import { getAllIgnorePatterns } from "../utils/fileFilter.js";
|
|
4
3
|
import { rgPath } from "../utils/ripgrep.js";
|
|
5
4
|
import { getDisplayPath } from "../utils/path.js";
|
|
6
5
|
import {
|
|
@@ -188,12 +187,6 @@ export const grepTool: ToolPlugin = {
|
|
|
188
187
|
rgArgs.push("--glob", globPattern);
|
|
189
188
|
}
|
|
190
189
|
|
|
191
|
-
// Get common ignore rules
|
|
192
|
-
const ignorePatterns = getAllIgnorePatterns();
|
|
193
|
-
for (const exclude of ignorePatterns) {
|
|
194
|
-
rgArgs.push("--glob", `!${exclude}`);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
190
|
// Add search pattern - use -e parameter to avoid patterns starting with - being mistaken as command line options
|
|
198
191
|
rgArgs.push("-e", pattern);
|
|
199
192
|
|
package/src/tools/types.ts
CHANGED
|
@@ -79,6 +79,8 @@ export interface ToolContext {
|
|
|
79
79
|
skillManager?: import("../managers/skillManager.js").SkillManager;
|
|
80
80
|
/** Current session ID */
|
|
81
81
|
sessionId?: string;
|
|
82
|
+
/** The ID of the current tool call */
|
|
83
|
+
toolCallId?: string;
|
|
82
84
|
/** Callback to update the short result of the current tool block */
|
|
83
85
|
onShortResultUpdate?: (shortResult: string) => void;
|
|
84
86
|
}
|