wave-agent-sdk 0.4.0 → 0.5.0
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 +59 -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 +8 -3
- package/dist/managers/aiManager.d.ts.map +1 -1
- package/dist/managers/aiManager.js +35 -9
- 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 +141 -35
- 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 +52 -174
- 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 +79 -45
- package/src/constants/tools.ts +2 -2
- package/src/managers/MemoryRuleManager.ts +1 -1
- package/src/managers/aiManager.ts +50 -17
- 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 +171 -31
- 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 +73 -200
- 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
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import { spawn, type ChildProcess } from "child_process";
|
|
2
|
+
import { BackgroundTask, BackgroundShell } from "../types/processes.js";
|
|
3
|
+
import { stripAnsiColors } from "../utils/stringUtils.js";
|
|
4
|
+
import { logger } from "../utils/globalLogger.js";
|
|
5
|
+
|
|
6
|
+
export interface BackgroundTaskManagerCallbacks {
|
|
7
|
+
onTasksChange?: (tasks: BackgroundTask[]) => void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface BackgroundTaskManagerOptions {
|
|
11
|
+
callbacks?: BackgroundTaskManagerCallbacks;
|
|
12
|
+
workdir: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class BackgroundTaskManager {
|
|
16
|
+
private tasks = new Map<string, BackgroundTask>();
|
|
17
|
+
private nextId = 1;
|
|
18
|
+
private callbacks: BackgroundTaskManagerCallbacks;
|
|
19
|
+
private workdir: string;
|
|
20
|
+
|
|
21
|
+
constructor(options: BackgroundTaskManagerOptions) {
|
|
22
|
+
this.callbacks = options.callbacks || {};
|
|
23
|
+
this.workdir = options.workdir;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
private notifyTasksChange(): void {
|
|
27
|
+
this.callbacks.onTasksChange?.(Array.from(this.tasks.values()));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public generateId(): string {
|
|
31
|
+
return `task_${this.nextId++}`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public addTask(task: BackgroundTask): void {
|
|
35
|
+
this.tasks.set(task.id, task);
|
|
36
|
+
this.notifyTasksChange();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public getTask(id: string): BackgroundTask | undefined {
|
|
40
|
+
return this.tasks.get(id);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public getAllTasks(): BackgroundTask[] {
|
|
44
|
+
return Array.from(this.tasks.values());
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
public startShell(
|
|
48
|
+
command: string,
|
|
49
|
+
timeout?: number,
|
|
50
|
+
): { id: string; child: ChildProcess; detach: () => void } {
|
|
51
|
+
const id = this.generateId();
|
|
52
|
+
const startTime = Date.now();
|
|
53
|
+
|
|
54
|
+
const child = spawn(command, {
|
|
55
|
+
shell: true,
|
|
56
|
+
stdio: "pipe",
|
|
57
|
+
cwd: this.workdir,
|
|
58
|
+
env: {
|
|
59
|
+
...process.env,
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const shell: BackgroundShell = {
|
|
64
|
+
id,
|
|
65
|
+
type: "shell",
|
|
66
|
+
process: child,
|
|
67
|
+
command,
|
|
68
|
+
startTime,
|
|
69
|
+
status: "running",
|
|
70
|
+
stdout: "",
|
|
71
|
+
stderr: "",
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
this.tasks.set(id, shell);
|
|
75
|
+
this.notifyTasksChange();
|
|
76
|
+
|
|
77
|
+
// Set up timeout if specified
|
|
78
|
+
let timeoutHandle: NodeJS.Timeout | undefined;
|
|
79
|
+
if (timeout && timeout > 0) {
|
|
80
|
+
timeoutHandle = setTimeout(() => {
|
|
81
|
+
if (shell.status === "running") {
|
|
82
|
+
this.stopTask(id);
|
|
83
|
+
}
|
|
84
|
+
}, timeout);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const onStdout = (data: Buffer | string) => {
|
|
88
|
+
shell.stdout += stripAnsiColors(data.toString());
|
|
89
|
+
this.notifyTasksChange();
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const onStderr = (data: Buffer | string) => {
|
|
93
|
+
shell.stderr += stripAnsiColors(data.toString());
|
|
94
|
+
this.notifyTasksChange();
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const onExit = (code: number | null) => {
|
|
98
|
+
if (timeoutHandle) {
|
|
99
|
+
clearTimeout(timeoutHandle);
|
|
100
|
+
}
|
|
101
|
+
shell.status = code === 0 ? "completed" : "failed";
|
|
102
|
+
shell.exitCode = code ?? 0;
|
|
103
|
+
shell.endTime = Date.now();
|
|
104
|
+
shell.runtime = shell.endTime - startTime;
|
|
105
|
+
this.notifyTasksChange();
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const onError = (error: Error) => {
|
|
109
|
+
if (timeoutHandle) {
|
|
110
|
+
clearTimeout(timeoutHandle);
|
|
111
|
+
}
|
|
112
|
+
shell.status = "failed";
|
|
113
|
+
shell.stderr += `\nProcess error: ${stripAnsiColors(error.message)}`;
|
|
114
|
+
shell.exitCode = 1;
|
|
115
|
+
shell.endTime = Date.now();
|
|
116
|
+
shell.runtime = shell.endTime - startTime;
|
|
117
|
+
this.notifyTasksChange();
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
child.stdout?.on("data", onStdout);
|
|
121
|
+
child.stderr?.on("data", onStderr);
|
|
122
|
+
child.on("exit", onExit);
|
|
123
|
+
child.on("error", onError);
|
|
124
|
+
|
|
125
|
+
const detach = () => {
|
|
126
|
+
child.stdout?.off("data", onStdout);
|
|
127
|
+
child.stderr?.off("data", onStderr);
|
|
128
|
+
child.off("exit", onExit);
|
|
129
|
+
child.off("error", onError);
|
|
130
|
+
if (timeoutHandle) {
|
|
131
|
+
clearTimeout(timeoutHandle);
|
|
132
|
+
}
|
|
133
|
+
this.tasks.delete(id);
|
|
134
|
+
this.notifyTasksChange();
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
return { id, child, detach };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
public adoptProcess(
|
|
141
|
+
child: ChildProcess,
|
|
142
|
+
command: string,
|
|
143
|
+
initialStdout: string = "",
|
|
144
|
+
initialStderr: string = "",
|
|
145
|
+
): string {
|
|
146
|
+
const id = this.generateId();
|
|
147
|
+
const startTime = Date.now();
|
|
148
|
+
|
|
149
|
+
const shell: BackgroundShell = {
|
|
150
|
+
id,
|
|
151
|
+
type: "shell",
|
|
152
|
+
process: child,
|
|
153
|
+
command,
|
|
154
|
+
startTime,
|
|
155
|
+
status: "running",
|
|
156
|
+
stdout: initialStdout,
|
|
157
|
+
stderr: initialStderr,
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
this.tasks.set(id, shell);
|
|
161
|
+
this.notifyTasksChange();
|
|
162
|
+
|
|
163
|
+
child.stdout?.on("data", (data) => {
|
|
164
|
+
shell.stdout += stripAnsiColors(data.toString());
|
|
165
|
+
this.notifyTasksChange();
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
child.stderr?.on("data", (data) => {
|
|
169
|
+
shell.stderr += stripAnsiColors(data.toString());
|
|
170
|
+
this.notifyTasksChange();
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
child.on("exit", (code) => {
|
|
174
|
+
shell.status = code === 0 ? "completed" : "failed";
|
|
175
|
+
shell.exitCode = code ?? 0;
|
|
176
|
+
shell.endTime = Date.now();
|
|
177
|
+
shell.runtime = shell.endTime - startTime;
|
|
178
|
+
this.notifyTasksChange();
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
child.on("error", (error) => {
|
|
182
|
+
shell.status = "failed";
|
|
183
|
+
shell.stderr += `\nProcess error: ${stripAnsiColors(error.message)}`;
|
|
184
|
+
shell.exitCode = 1;
|
|
185
|
+
shell.endTime = Date.now();
|
|
186
|
+
shell.runtime = shell.endTime - startTime;
|
|
187
|
+
this.notifyTasksChange();
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
return id;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
public getOutput(
|
|
194
|
+
id: string,
|
|
195
|
+
filter?: string,
|
|
196
|
+
): { stdout: string; stderr: string; status: string } | null {
|
|
197
|
+
const task = this.tasks.get(id);
|
|
198
|
+
if (!task) {
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
let stdout = task.stdout;
|
|
203
|
+
let stderr = task.stderr;
|
|
204
|
+
|
|
205
|
+
// Apply regex filter if provided
|
|
206
|
+
if (filter) {
|
|
207
|
+
try {
|
|
208
|
+
const regex = new RegExp(filter);
|
|
209
|
+
stdout = stdout
|
|
210
|
+
.split("\n")
|
|
211
|
+
.filter((line) => regex.test(line))
|
|
212
|
+
.join("\n");
|
|
213
|
+
stderr = stderr
|
|
214
|
+
.split("\n")
|
|
215
|
+
.filter((line) => regex.test(line))
|
|
216
|
+
.join("\n");
|
|
217
|
+
} catch (error) {
|
|
218
|
+
logger.warn(`Invalid filter regex: ${filter}`, error);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return {
|
|
223
|
+
stdout,
|
|
224
|
+
stderr,
|
|
225
|
+
status: task.status,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
public stopTask(id: string): boolean {
|
|
230
|
+
const task = this.tasks.get(id);
|
|
231
|
+
if (!task || task.status !== "running") {
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (task.type === "shell") {
|
|
236
|
+
const shell = task as BackgroundShell;
|
|
237
|
+
try {
|
|
238
|
+
// Try to kill process group first
|
|
239
|
+
if (shell.process.pid) {
|
|
240
|
+
process.kill(-shell.process.pid, "SIGTERM");
|
|
241
|
+
|
|
242
|
+
// Force kill after timeout
|
|
243
|
+
setTimeout(() => {
|
|
244
|
+
if (
|
|
245
|
+
shell.status === "running" &&
|
|
246
|
+
shell.process.pid &&
|
|
247
|
+
!shell.process.killed
|
|
248
|
+
) {
|
|
249
|
+
try {
|
|
250
|
+
process.kill(-shell.process.pid, "SIGKILL");
|
|
251
|
+
} catch (error) {
|
|
252
|
+
logger.error("Failed to force kill process:", error);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}, 1000);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
shell.status = "killed";
|
|
259
|
+
shell.endTime = Date.now();
|
|
260
|
+
shell.runtime = shell.endTime - shell.startTime;
|
|
261
|
+
this.notifyTasksChange();
|
|
262
|
+
return true;
|
|
263
|
+
} catch {
|
|
264
|
+
// Fallback to direct process kill
|
|
265
|
+
try {
|
|
266
|
+
shell.process.kill("SIGTERM");
|
|
267
|
+
setTimeout(() => {
|
|
268
|
+
if (!shell.process.killed) {
|
|
269
|
+
shell.process.kill("SIGKILL");
|
|
270
|
+
}
|
|
271
|
+
}, 1000);
|
|
272
|
+
shell.status = "killed";
|
|
273
|
+
shell.endTime = Date.now();
|
|
274
|
+
shell.runtime = shell.endTime - shell.startTime;
|
|
275
|
+
this.notifyTasksChange();
|
|
276
|
+
return true;
|
|
277
|
+
} catch (directKillError) {
|
|
278
|
+
logger.error("Failed to kill child process:", directKillError);
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
} else if (task.type === "subagent") {
|
|
283
|
+
// Subagent termination logic will be handled by aborting the AI loop
|
|
284
|
+
// which is already managed by the SubagentManager and AIManager.
|
|
285
|
+
// Here we just update the status.
|
|
286
|
+
task.status = "killed";
|
|
287
|
+
task.endTime = Date.now();
|
|
288
|
+
task.runtime = task.endTime - task.startTime;
|
|
289
|
+
this.notifyTasksChange();
|
|
290
|
+
return true;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return false;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
public cleanup(): void {
|
|
297
|
+
// Kill all running tasks
|
|
298
|
+
for (const [id, task] of this.tasks) {
|
|
299
|
+
if (task.status === "running") {
|
|
300
|
+
this.stopTask(id);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
this.tasks.clear();
|
|
304
|
+
this.notifyTasksChange();
|
|
305
|
+
}
|
|
306
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ForegroundTask, IForegroundTaskManager } from "../types/processes.js";
|
|
2
|
+
|
|
3
|
+
export class ForegroundTaskManager implements IForegroundTaskManager {
|
|
4
|
+
private activeForegroundTasks: ForegroundTask[] = [];
|
|
5
|
+
|
|
6
|
+
public registerForegroundTask(task: ForegroundTask): void {
|
|
7
|
+
this.activeForegroundTasks.push(task);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
public unregisterForegroundTask(id: string): void {
|
|
11
|
+
this.activeForegroundTasks = this.activeForegroundTasks.filter(
|
|
12
|
+
(t) => t.id !== id,
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
public async backgroundCurrentTask(): Promise<void> {
|
|
17
|
+
const task = this.activeForegroundTasks.pop();
|
|
18
|
+
if (task) {
|
|
19
|
+
await task.backgroundHandler();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public hasActiveTasks(): boolean {
|
|
24
|
+
return this.activeForegroundTasks.length > 0;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -418,7 +418,9 @@ export class LspManager implements ILspManager {
|
|
|
418
418
|
await this.sendRequest(lspProc, "shutdown", {}, timeout);
|
|
419
419
|
await this.sendNotification(lspProc, "exit", {});
|
|
420
420
|
// Give it a moment to exit
|
|
421
|
-
|
|
421
|
+
if (timeout > 100) {
|
|
422
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
423
|
+
}
|
|
422
424
|
} catch (error) {
|
|
423
425
|
this.logger?.debug(
|
|
424
426
|
`Failed to gracefully shutdown LSP for ${language}: ${error}`,
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
SESSION_DIR,
|
|
27
27
|
} from "../services/session.js";
|
|
28
28
|
import { ChatCompletionMessageFunctionToolCall } from "openai/resources.js";
|
|
29
|
+
import type { MemoryRuleManager } from "./MemoryRuleManager.js";
|
|
29
30
|
import { pathEncoder } from "../utils/pathEncoder.js";
|
|
30
31
|
|
|
31
32
|
export interface MessageManagerCallbacks {
|
|
@@ -57,6 +58,7 @@ export interface MessageManagerCallbacks {
|
|
|
57
58
|
onUpdateCommandOutputMessage?: (command: string, output: string) => void;
|
|
58
59
|
onCompleteCommandMessage?: (command: string, exitCode: number) => void;
|
|
59
60
|
onSlashCommandsChange?: (commands: SlashCommand[]) => void;
|
|
61
|
+
onInfoBlockAdded?: (content: string) => void;
|
|
60
62
|
// Rewind callbacks
|
|
61
63
|
onShowRewind?: () => void;
|
|
62
64
|
// Subagent callbacks
|
|
@@ -80,6 +82,7 @@ export interface MessageManagerOptions {
|
|
|
80
82
|
logger?: Logger;
|
|
81
83
|
sessionType?: "main" | "subagent";
|
|
82
84
|
subagentType?: string;
|
|
85
|
+
memoryRuleManager?: MemoryRuleManager;
|
|
83
86
|
}
|
|
84
87
|
|
|
85
88
|
export class MessageManager {
|
|
@@ -95,6 +98,7 @@ export class MessageManager {
|
|
|
95
98
|
private transcriptPath: string; // Cached transcript path
|
|
96
99
|
private savedMessageCount: number; // Track how many messages have been saved to prevent duplication
|
|
97
100
|
private filesInContext: Set<string> = new Set(); // Track files mentioned in the conversation
|
|
101
|
+
private memoryRuleManager?: MemoryRuleManager;
|
|
98
102
|
private sessionType: "main" | "subagent";
|
|
99
103
|
private subagentType?: string;
|
|
100
104
|
|
|
@@ -110,6 +114,7 @@ export class MessageManager {
|
|
|
110
114
|
this.savedMessageCount = 0; // Initialize saved message count tracker
|
|
111
115
|
this.sessionType = options.sessionType || "main";
|
|
112
116
|
this.subagentType = options.subagentType;
|
|
117
|
+
this.memoryRuleManager = options.memoryRuleManager;
|
|
113
118
|
|
|
114
119
|
// Compute and cache the transcript path
|
|
115
120
|
this.transcriptPath = this.computeTranscriptPath();
|
|
@@ -151,6 +156,30 @@ export class MessageManager {
|
|
|
151
156
|
return this.transcriptPath;
|
|
152
157
|
}
|
|
153
158
|
|
|
159
|
+
/**
|
|
160
|
+
* Get combined memory content (project memory + user memory + modular rules)
|
|
161
|
+
*/
|
|
162
|
+
public async getCombinedMemory(): Promise<string> {
|
|
163
|
+
const memory = await import("../services/memory.js");
|
|
164
|
+
let combined = await memory.getCombinedMemoryContent(this.workdir);
|
|
165
|
+
|
|
166
|
+
if (this.memoryRuleManager) {
|
|
167
|
+
const filesInContext = this.getFilesInContext();
|
|
168
|
+
const activeRules = this.memoryRuleManager.getActiveRules(filesInContext);
|
|
169
|
+
if (activeRules.length > 0) {
|
|
170
|
+
this.logger?.debug(
|
|
171
|
+
`Active modular rules (${activeRules.length}): ${activeRules.map((r) => r.id).join(", ")}`,
|
|
172
|
+
);
|
|
173
|
+
if (combined) {
|
|
174
|
+
combined += "\n\n";
|
|
175
|
+
}
|
|
176
|
+
combined += activeRules.map((r) => r.content).join("\n\n");
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return combined;
|
|
181
|
+
}
|
|
182
|
+
|
|
154
183
|
/**
|
|
155
184
|
* Compute the transcript path using cached encoded workdir
|
|
156
185
|
* Called during construction and when sessionId changes
|
|
@@ -400,6 +429,18 @@ export class MessageManager {
|
|
|
400
429
|
this.callbacks.onErrorBlockAdded?.(error);
|
|
401
430
|
}
|
|
402
431
|
|
|
432
|
+
public addInfoBlock(content: string): void {
|
|
433
|
+
const lastMessage = this.messages[this.messages.length - 1];
|
|
434
|
+
if (lastMessage && lastMessage.role === "assistant") {
|
|
435
|
+
lastMessage.blocks.push({
|
|
436
|
+
type: "info",
|
|
437
|
+
content,
|
|
438
|
+
} as unknown as import("../types/index.js").MessageBlock);
|
|
439
|
+
this.setMessages([...this.messages]);
|
|
440
|
+
this.callbacks.onInfoBlockAdded?.(content);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
403
444
|
/**
|
|
404
445
|
* Compress messages and update session, delete compressed messages, only keep compressed messages and subsequent messages
|
|
405
446
|
*/
|
|
@@ -494,12 +535,13 @@ export class MessageManager {
|
|
|
494
535
|
subagentName: string,
|
|
495
536
|
sessionId: string,
|
|
496
537
|
configuration: SubagentConfiguration,
|
|
497
|
-
status: "active" | "completed" | "error" = "active",
|
|
538
|
+
status: "active" | "completed" | "error" | "aborted" = "active",
|
|
498
539
|
parameters: {
|
|
499
540
|
description: string;
|
|
500
541
|
prompt: string;
|
|
501
542
|
subagent_type: string;
|
|
502
543
|
},
|
|
544
|
+
runInBackground?: boolean,
|
|
503
545
|
): void {
|
|
504
546
|
const params: AddSubagentBlockParams = {
|
|
505
547
|
messages: this.messages,
|
|
@@ -508,6 +550,7 @@ export class MessageManager {
|
|
|
508
550
|
sessionId,
|
|
509
551
|
status,
|
|
510
552
|
configuration,
|
|
553
|
+
runInBackground,
|
|
511
554
|
};
|
|
512
555
|
const updatedMessages = addSubagentBlockToMessage(params);
|
|
513
556
|
this.setMessages(updatedMessages);
|
|
@@ -519,6 +562,7 @@ export class MessageManager {
|
|
|
519
562
|
updates: Partial<{
|
|
520
563
|
status: "active" | "completed" | "error" | "aborted";
|
|
521
564
|
sessionId: string;
|
|
565
|
+
runInBackground: boolean;
|
|
522
566
|
}>,
|
|
523
567
|
): void {
|
|
524
568
|
const updatedMessages = updateSubagentBlockInMessage(
|
|
@@ -532,7 +576,9 @@ export class MessageManager {
|
|
|
532
576
|
subagentId,
|
|
533
577
|
status: updates.status || "active",
|
|
534
578
|
};
|
|
535
|
-
|
|
579
|
+
if (updates.status) {
|
|
580
|
+
this.callbacks.onSubAgentBlockUpdated?.(params.subagentId, params.status);
|
|
581
|
+
}
|
|
536
582
|
}
|
|
537
583
|
|
|
538
584
|
/**
|
|
@@ -237,13 +237,7 @@ export class PermissionManager {
|
|
|
237
237
|
* Get the current effective permission mode for tool execution context
|
|
238
238
|
*/
|
|
239
239
|
getCurrentEffectiveMode(cliPermissionMode?: PermissionMode): PermissionMode {
|
|
240
|
-
|
|
241
|
-
this.logger?.debug("getCurrentEffectiveMode", {
|
|
242
|
-
cliPermissionMode,
|
|
243
|
-
configuredDefaultMode: this.configuredDefaultMode,
|
|
244
|
-
resolvedMode: mode,
|
|
245
|
-
});
|
|
246
|
-
return mode;
|
|
240
|
+
return this.resolveEffectivePermissionMode(cliPermissionMode);
|
|
247
241
|
}
|
|
248
242
|
|
|
249
243
|
/**
|
|
@@ -149,9 +149,7 @@ export class PluginManager {
|
|
|
149
149
|
* @param configs Array of plugin configurations
|
|
150
150
|
*/
|
|
151
151
|
async loadPlugins(configs: PluginConfig[]): Promise<void> {
|
|
152
|
-
// Load
|
|
153
|
-
await this.loadInstalledPlugins();
|
|
154
|
-
|
|
152
|
+
// Load plugins from configuration (e.g. --plugin-dir) first to give them higher priority
|
|
155
153
|
for (const config of configs) {
|
|
156
154
|
if (config.type !== "local") {
|
|
157
155
|
this.logger?.warn(`Unsupported plugin type: ${config.type}`);
|
|
@@ -164,6 +162,9 @@ export class PluginManager {
|
|
|
164
162
|
|
|
165
163
|
await this.loadSinglePlugin(absolutePath);
|
|
166
164
|
}
|
|
165
|
+
|
|
166
|
+
// Load installed plugins from marketplace
|
|
167
|
+
await this.loadInstalledPlugins();
|
|
167
168
|
}
|
|
168
169
|
|
|
169
170
|
/**
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { MessageManager } from "./messageManager.js";
|
|
2
2
|
import type { AIManager } from "./aiManager.js";
|
|
3
|
+
import type { BackgroundTaskManager } from "./backgroundTaskManager.js";
|
|
3
4
|
import type {
|
|
4
5
|
SlashCommand,
|
|
5
6
|
CustomSlashCommand,
|
|
@@ -26,6 +27,7 @@ const execAsync = promisify(exec);
|
|
|
26
27
|
export interface SlashCommandManagerOptions {
|
|
27
28
|
messageManager: MessageManager;
|
|
28
29
|
aiManager: AIManager;
|
|
30
|
+
backgroundTaskManager: BackgroundTaskManager;
|
|
29
31
|
workdir: string;
|
|
30
32
|
logger?: Logger;
|
|
31
33
|
}
|
|
@@ -35,12 +37,14 @@ export class SlashCommandManager {
|
|
|
35
37
|
private customCommands = new Map<string, CustomSlashCommand>();
|
|
36
38
|
private messageManager: MessageManager;
|
|
37
39
|
private aiManager: AIManager;
|
|
40
|
+
private backgroundTaskManager: BackgroundTaskManager;
|
|
38
41
|
private workdir: string;
|
|
39
42
|
private logger?: Logger;
|
|
40
43
|
|
|
41
44
|
constructor(options: SlashCommandManagerOptions) {
|
|
42
45
|
this.messageManager = options.messageManager;
|
|
43
46
|
this.aiManager = options.aiManager;
|
|
47
|
+
this.backgroundTaskManager = options.backgroundTaskManager;
|
|
44
48
|
this.workdir = options.workdir;
|
|
45
49
|
this.logger = options.logger;
|
|
46
50
|
|