wave-agent-sdk 0.5.0 → 0.6.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 +14 -6
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +65 -88
- package/dist/constants/prompts.d.ts +18 -14
- package/dist/constants/prompts.d.ts.map +1 -1
- package/dist/constants/prompts.js +130 -54
- package/dist/constants/tools.d.ts +4 -1
- package/dist/constants/tools.d.ts.map +1 -1
- package/dist/constants/tools.js +4 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/managers/aiManager.d.ts +2 -5
- package/dist/managers/aiManager.d.ts.map +1 -1
- package/dist/managers/aiManager.js +59 -48
- package/dist/managers/backgroundTaskManager.d.ts.map +1 -1
- package/dist/managers/backgroundTaskManager.js +59 -53
- package/dist/managers/foregroundTaskManager.d.ts.map +1 -1
- package/dist/managers/foregroundTaskManager.js +3 -2
- package/dist/managers/mcpManager.d.ts.map +1 -1
- package/dist/managers/messageManager.d.ts +14 -10
- package/dist/managers/messageManager.d.ts.map +1 -1
- package/dist/managers/messageManager.js +102 -62
- package/dist/managers/permissionManager.d.ts.map +1 -1
- package/dist/managers/permissionManager.js +25 -15
- package/dist/managers/planManager.d.ts +1 -1
- package/dist/managers/planManager.d.ts.map +1 -1
- package/dist/managers/planManager.js +2 -2
- package/dist/managers/slashCommandManager.d.ts +3 -0
- package/dist/managers/slashCommandManager.d.ts.map +1 -1
- package/dist/managers/slashCommandManager.js +7 -2
- package/dist/managers/subagentManager.d.ts +4 -0
- package/dist/managers/subagentManager.d.ts.map +1 -1
- package/dist/managers/subagentManager.js +22 -14
- package/dist/managers/toolManager.d.ts +11 -0
- package/dist/managers/toolManager.d.ts.map +1 -1
- package/dist/managers/toolManager.js +20 -2
- package/dist/services/aiService.d.ts +0 -1
- package/dist/services/aiService.d.ts.map +1 -1
- package/dist/services/aiService.js +4 -140
- package/dist/services/memory.d.ts +0 -3
- package/dist/services/memory.d.ts.map +1 -1
- package/dist/services/memory.js +0 -59
- package/dist/services/session.d.ts +15 -1
- package/dist/services/session.d.ts.map +1 -1
- package/dist/services/session.js +57 -1
- package/dist/services/taskManager.d.ts +21 -0
- package/dist/services/taskManager.d.ts.map +1 -0
- package/dist/services/taskManager.js +158 -0
- package/dist/tools/askUserQuestion.d.ts.map +1 -1
- package/dist/tools/askUserQuestion.js +39 -25
- package/dist/tools/bashTool.d.ts.map +1 -1
- package/dist/tools/bashTool.js +7 -9
- package/dist/tools/editTool.d.ts.map +1 -1
- package/dist/tools/editTool.js +2 -1
- package/dist/tools/exitPlanMode.d.ts.map +1 -1
- package/dist/tools/exitPlanMode.js +25 -1
- package/dist/tools/globTool.d.ts.map +1 -1
- package/dist/tools/globTool.js +8 -2
- package/dist/tools/grepTool.d.ts.map +1 -1
- package/dist/tools/grepTool.js +17 -6
- package/dist/tools/lsTool.d.ts.map +1 -1
- package/dist/tools/lsTool.js +3 -1
- package/dist/tools/readTool.d.ts.map +1 -1
- package/dist/tools/readTool.js +16 -1
- package/dist/tools/taskManagementTools.d.ts +6 -0
- package/dist/tools/taskManagementTools.d.ts.map +1 -0
- package/dist/tools/taskManagementTools.js +453 -0
- package/dist/tools/taskOutputTool.d.ts.map +1 -1
- package/dist/tools/taskOutputTool.js +32 -8
- package/dist/tools/taskStopTool.d.ts.map +1 -1
- package/dist/tools/taskStopTool.js +7 -1
- package/dist/tools/taskTool.d.ts.map +1 -1
- package/dist/tools/taskTool.js +6 -1
- package/dist/tools/types.d.ts +9 -0
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/tools/writeTool.d.ts.map +1 -1
- package/dist/tools/writeTool.js +9 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -0
- package/dist/types/messaging.d.ts +2 -8
- package/dist/types/messaging.d.ts.map +1 -1
- package/dist/types/processes.d.ts +11 -6
- package/dist/types/processes.d.ts.map +1 -1
- package/dist/types/tasks.d.ts +13 -0
- package/dist/types/tasks.d.ts.map +1 -0
- package/dist/types/tasks.js +1 -0
- package/dist/types/tools.d.ts +4 -1
- package/dist/types/tools.d.ts.map +1 -1
- package/dist/utils/builtinSubagents.d.ts.map +1 -1
- package/dist/utils/builtinSubagents.js +38 -1
- package/dist/utils/cacheControlUtils.d.ts.map +1 -1
- package/dist/utils/cacheControlUtils.js +18 -12
- package/dist/utils/constants.d.ts +0 -4
- package/dist/utils/constants.d.ts.map +1 -1
- package/dist/utils/constants.js +0 -4
- package/dist/utils/convertMessagesForAPI.js +2 -2
- package/dist/utils/messageOperations.d.ts +2 -35
- package/dist/utils/messageOperations.d.ts.map +1 -1
- package/dist/utils/messageOperations.js +4 -97
- package/dist/utils/nameGenerator.d.ts +1 -1
- package/dist/utils/nameGenerator.d.ts.map +1 -1
- package/dist/utils/nameGenerator.js +19 -3
- package/package.json +1 -1
- package/src/agent.ts +90 -101
- package/src/constants/prompts.ts +156 -65
- package/src/constants/tools.ts +4 -1
- package/src/index.ts +1 -0
- package/src/managers/aiManager.ts +79 -70
- package/src/managers/backgroundTaskManager.ts +53 -54
- package/src/managers/foregroundTaskManager.ts +3 -2
- package/src/managers/mcpManager.ts +6 -3
- package/src/managers/messageManager.ts +137 -73
- package/src/managers/permissionManager.ts +32 -21
- package/src/managers/planManager.ts +2 -2
- package/src/managers/slashCommandManager.ts +11 -2
- package/src/managers/subagentManager.ts +33 -14
- package/src/managers/toolManager.ts +32 -2
- package/src/services/aiService.ts +3 -145
- package/src/services/memory.ts +0 -72
- package/src/services/session.ts +73 -0
- package/src/services/taskManager.ts +188 -0
- package/src/tools/askUserQuestion.ts +51 -29
- package/src/tools/bashTool.ts +9 -15
- package/src/tools/editTool.ts +3 -1
- package/src/tools/exitPlanMode.ts +26 -2
- package/src/tools/globTool.ts +10 -2
- package/src/tools/grepTool.ts +17 -6
- package/src/tools/lsTool.ts +3 -1
- package/src/tools/readTool.ts +17 -1
- package/src/tools/taskManagementTools.ts +498 -0
- package/src/tools/taskOutputTool.ts +34 -12
- package/src/tools/taskStopTool.ts +7 -1
- package/src/tools/taskTool.ts +7 -1
- package/src/tools/types.ts +10 -0
- package/src/tools/writeTool.ts +9 -2
- package/src/types/index.ts +1 -0
- package/src/types/messaging.ts +1 -9
- package/src/types/processes.ts +13 -7
- package/src/types/tasks.ts +13 -0
- package/src/types/tools.ts +4 -1
- package/src/utils/builtinSubagents.ts +47 -1
- package/src/utils/cacheControlUtils.ts +26 -18
- package/src/utils/constants.ts +0 -5
- package/src/utils/convertMessagesForAPI.ts +2 -2
- package/src/utils/messageOperations.ts +5 -136
- package/src/utils/nameGenerator.ts +20 -3
- package/dist/tools/todoWriteTool.d.ts +0 -6
- package/dist/tools/todoWriteTool.d.ts.map +0 -1
- package/dist/tools/todoWriteTool.js +0 -220
- package/src/tools/todoWriteTool.ts +0 -257
|
@@ -69,6 +69,26 @@ export class BackgroundTaskManager {
|
|
|
69
69
|
status: "running",
|
|
70
70
|
stdout: "",
|
|
71
71
|
stderr: "",
|
|
72
|
+
onStop: () => {
|
|
73
|
+
try {
|
|
74
|
+
if (child.pid) {
|
|
75
|
+
process.kill(-child.pid, "SIGTERM");
|
|
76
|
+
setTimeout(() => {
|
|
77
|
+
if (child.pid && !child.killed) {
|
|
78
|
+
try {
|
|
79
|
+
process.kill(-child.pid, "SIGKILL");
|
|
80
|
+
} catch (error) {
|
|
81
|
+
logger.error("Failed to force kill process:", error);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}, 1000);
|
|
85
|
+
} else {
|
|
86
|
+
child.kill("SIGTERM");
|
|
87
|
+
}
|
|
88
|
+
} catch {
|
|
89
|
+
child.kill("SIGTERM");
|
|
90
|
+
}
|
|
91
|
+
},
|
|
72
92
|
};
|
|
73
93
|
|
|
74
94
|
this.tasks.set(id, shell);
|
|
@@ -155,6 +175,26 @@ export class BackgroundTaskManager {
|
|
|
155
175
|
status: "running",
|
|
156
176
|
stdout: initialStdout,
|
|
157
177
|
stderr: initialStderr,
|
|
178
|
+
onStop: () => {
|
|
179
|
+
try {
|
|
180
|
+
if (child.pid) {
|
|
181
|
+
process.kill(-child.pid, "SIGTERM");
|
|
182
|
+
setTimeout(() => {
|
|
183
|
+
if (child.pid && !child.killed) {
|
|
184
|
+
try {
|
|
185
|
+
process.kill(-child.pid, "SIGKILL");
|
|
186
|
+
} catch (error) {
|
|
187
|
+
logger.error("Failed to force kill process:", error);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}, 1000);
|
|
191
|
+
} else {
|
|
192
|
+
child.kill("SIGTERM");
|
|
193
|
+
}
|
|
194
|
+
} catch {
|
|
195
|
+
child.kill("SIGTERM");
|
|
196
|
+
}
|
|
197
|
+
},
|
|
158
198
|
};
|
|
159
199
|
|
|
160
200
|
this.tasks.set(id, shell);
|
|
@@ -232,65 +272,24 @@ export class BackgroundTaskManager {
|
|
|
232
272
|
return false;
|
|
233
273
|
}
|
|
234
274
|
|
|
235
|
-
if (task.
|
|
236
|
-
const shell = task as BackgroundShell;
|
|
275
|
+
if (task.onStop) {
|
|
237
276
|
try {
|
|
238
|
-
|
|
239
|
-
if (
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
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;
|
|
277
|
+
const result = task.onStop();
|
|
278
|
+
if (result instanceof Promise) {
|
|
279
|
+
result.catch((error) => {
|
|
280
|
+
logger.error("Error in background task onStop callback:", error);
|
|
281
|
+
});
|
|
280
282
|
}
|
|
283
|
+
} catch (error) {
|
|
284
|
+
logger.error("Error in background task onStop callback:", error);
|
|
281
285
|
}
|
|
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
286
|
}
|
|
292
287
|
|
|
293
|
-
|
|
288
|
+
task.status = "killed";
|
|
289
|
+
task.endTime = Date.now();
|
|
290
|
+
task.runtime = task.endTime - task.startTime;
|
|
291
|
+
this.notifyTasksChange();
|
|
292
|
+
return true;
|
|
294
293
|
}
|
|
295
294
|
|
|
296
295
|
public cleanup(): void {
|
|
@@ -14,8 +14,9 @@ export class ForegroundTaskManager implements IForegroundTaskManager {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
public async backgroundCurrentTask(): Promise<void> {
|
|
17
|
-
const
|
|
18
|
-
|
|
17
|
+
const tasks = [...this.activeForegroundTasks].reverse();
|
|
18
|
+
this.activeForegroundTasks = [];
|
|
19
|
+
for (const task of tasks) {
|
|
19
20
|
await task.backgroundHandler();
|
|
20
21
|
}
|
|
21
22
|
}
|
|
@@ -3,7 +3,7 @@ import { join } from "path";
|
|
|
3
3
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
4
4
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
5
5
|
import { ChatCompletionFunctionTool } from "openai/resources.js";
|
|
6
|
-
import { createMcpToolPlugin, findToolServer } from "
|
|
6
|
+
import { createMcpToolPlugin, findToolServer } from "../utils/mcpUtils.js";
|
|
7
7
|
import type { ToolPlugin, ToolResult, ToolContext } from "../tools/types.js";
|
|
8
8
|
import type {
|
|
9
9
|
Logger,
|
|
@@ -476,8 +476,11 @@ export class McpManager {
|
|
|
476
476
|
const server = findToolServer(tool.name, servers);
|
|
477
477
|
|
|
478
478
|
if (server) {
|
|
479
|
-
const plugin = createMcpToolPlugin(
|
|
480
|
-
|
|
479
|
+
const plugin = createMcpToolPlugin(
|
|
480
|
+
tool,
|
|
481
|
+
server.name,
|
|
482
|
+
(name: string, args: Record<string, unknown>) =>
|
|
483
|
+
this.executeMcpTool(name, args),
|
|
481
484
|
);
|
|
482
485
|
mcpTools.set(plugin.name, plugin);
|
|
483
486
|
}
|
|
@@ -3,7 +3,6 @@ import {
|
|
|
3
3
|
updateToolBlockInMessage,
|
|
4
4
|
addErrorBlockToMessage,
|
|
5
5
|
addUserMessageToMessages,
|
|
6
|
-
extractUserInputHistory,
|
|
7
6
|
addCommandOutputMessage,
|
|
8
7
|
updateCommandOutputInMessage,
|
|
9
8
|
completeCommandInMessage,
|
|
@@ -33,7 +32,6 @@ export interface MessageManagerCallbacks {
|
|
|
33
32
|
onMessagesChange?: (messages: Message[]) => void;
|
|
34
33
|
onSessionIdChange?: (sessionId: string) => void;
|
|
35
34
|
onLatestTotalTokensChange?: (latestTotalTokens: number) => void;
|
|
36
|
-
onUserInputHistoryChange?: (history: string[]) => void;
|
|
37
35
|
onUsagesChange?: (usages: Usage[]) => void;
|
|
38
36
|
// Incremental callback
|
|
39
37
|
onUserMessageAdded?: (params: UserMessageParams) => void;
|
|
@@ -45,7 +43,7 @@ export interface MessageManagerCallbacks {
|
|
|
45
43
|
onAssistantReasoningUpdated?: (chunk: string, accumulated: string) => void;
|
|
46
44
|
onToolBlockUpdated?: (params: AgentToolBlockUpdateParams) => void;
|
|
47
45
|
onErrorBlockAdded?: (error: string) => void;
|
|
48
|
-
onCompressBlockAdded?: (
|
|
46
|
+
onCompressBlockAdded?: (content: string) => void;
|
|
49
47
|
onCompressionStateChange?: (isCompressing: boolean) => void;
|
|
50
48
|
onMemoryBlockAdded?: (
|
|
51
49
|
content: string,
|
|
@@ -74,6 +72,10 @@ export interface MessageManagerCallbacks {
|
|
|
74
72
|
subagentId: string,
|
|
75
73
|
status: "active" | "completed" | "error" | "aborted",
|
|
76
74
|
) => void;
|
|
75
|
+
onFileHistoryBlockAdded?: (
|
|
76
|
+
snapshots: import("../types/reversion.js").FileSnapshot[],
|
|
77
|
+
) => void;
|
|
78
|
+
onSubagentTaskStopRequested?: (subagentId: string) => void;
|
|
77
79
|
}
|
|
78
80
|
|
|
79
81
|
export interface MessageManagerOptions {
|
|
@@ -88,9 +90,10 @@ export interface MessageManagerOptions {
|
|
|
88
90
|
export class MessageManager {
|
|
89
91
|
// Private state properties
|
|
90
92
|
private sessionId: string;
|
|
93
|
+
private rootSessionId: string;
|
|
94
|
+
private parentSessionId?: string;
|
|
91
95
|
private messages: Message[];
|
|
92
96
|
private latestTotalTokens: number;
|
|
93
|
-
private userInputHistory: string[];
|
|
94
97
|
private workdir: string;
|
|
95
98
|
private encodedWorkdir: string; // Cached encoded workdir
|
|
96
99
|
private logger?: Logger; // Add optional logger property
|
|
@@ -104,9 +107,9 @@ export class MessageManager {
|
|
|
104
107
|
|
|
105
108
|
constructor(options: MessageManagerOptions) {
|
|
106
109
|
this.sessionId = generateSessionId();
|
|
110
|
+
this.rootSessionId = this.sessionId;
|
|
107
111
|
this.messages = [];
|
|
108
112
|
this.latestTotalTokens = 0;
|
|
109
|
-
this.userInputHistory = [];
|
|
110
113
|
this.workdir = options.workdir;
|
|
111
114
|
this.encodedWorkdir = pathEncoder.encodeSync(this.workdir); // Cache encoded workdir
|
|
112
115
|
this.callbacks = options.callbacks;
|
|
@@ -125,6 +128,14 @@ export class MessageManager {
|
|
|
125
128
|
return this.sessionId;
|
|
126
129
|
}
|
|
127
130
|
|
|
131
|
+
public getRootSessionId(): string {
|
|
132
|
+
return this.rootSessionId;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
public getParentSessionId(): string | undefined {
|
|
136
|
+
return this.parentSessionId;
|
|
137
|
+
}
|
|
138
|
+
|
|
128
139
|
public getMessages(): Message[] {
|
|
129
140
|
return [...this.messages];
|
|
130
141
|
}
|
|
@@ -133,10 +144,6 @@ export class MessageManager {
|
|
|
133
144
|
return this.latestTotalTokens;
|
|
134
145
|
}
|
|
135
146
|
|
|
136
|
-
public getUserInputHistory(): string[] {
|
|
137
|
-
return [...this.userInputHistory];
|
|
138
|
-
}
|
|
139
|
-
|
|
140
147
|
public getWorkdir(): string {
|
|
141
148
|
return this.workdir;
|
|
142
149
|
}
|
|
@@ -247,6 +254,8 @@ export class MessageManager {
|
|
|
247
254
|
unsavedMessages, // Only append new messages
|
|
248
255
|
this.workdir,
|
|
249
256
|
this.sessionType,
|
|
257
|
+
this.rootSessionId,
|
|
258
|
+
this.parentSessionId,
|
|
250
259
|
);
|
|
251
260
|
|
|
252
261
|
// Update the saved message count
|
|
@@ -264,18 +273,13 @@ export class MessageManager {
|
|
|
264
273
|
}
|
|
265
274
|
}
|
|
266
275
|
|
|
267
|
-
public setUserInputHistory(userInputHistory: string[]): void {
|
|
268
|
-
this.userInputHistory = [...userInputHistory];
|
|
269
|
-
this.callbacks.onUserInputHistoryChange?.(this.userInputHistory);
|
|
270
|
-
}
|
|
271
|
-
|
|
272
276
|
/**
|
|
273
|
-
* Clear messages
|
|
277
|
+
* Clear messages
|
|
274
278
|
*/
|
|
275
279
|
public clearMessages(): void {
|
|
276
280
|
this.setMessages([]);
|
|
277
|
-
this.setUserInputHistory([]);
|
|
278
281
|
this.setSessionId(generateSessionId());
|
|
282
|
+
this.rootSessionId = this.sessionId;
|
|
279
283
|
this.setlatestTotalTokens(0);
|
|
280
284
|
this.savedMessageCount = 0; // Reset saved message count
|
|
281
285
|
}
|
|
@@ -297,36 +301,17 @@ export class MessageManager {
|
|
|
297
301
|
// Initialize state from session data
|
|
298
302
|
public initializeFromSession(sessionData: SessionData): void {
|
|
299
303
|
this.setSessionId(sessionData.id);
|
|
304
|
+
this.rootSessionId = sessionData.rootSessionId || sessionData.id;
|
|
305
|
+
this.parentSessionId = sessionData.parentSessionId;
|
|
300
306
|
this.setMessages([...sessionData.messages]);
|
|
301
307
|
this.updateFilesInContext(sessionData.messages);
|
|
302
308
|
this.setlatestTotalTokens(sessionData.metadata.latestTotalTokens);
|
|
303
309
|
|
|
304
|
-
// Extract user input history from session messages
|
|
305
|
-
this.setUserInputHistory(extractUserInputHistory(sessionData.messages));
|
|
306
|
-
|
|
307
310
|
// Set saved message count to the number of loaded messages since they're already saved
|
|
308
311
|
// This must be done after setSessionId which resets it to 0
|
|
309
312
|
this.savedMessageCount = sessionData.messages.length;
|
|
310
313
|
}
|
|
311
314
|
|
|
312
|
-
// Add to input history
|
|
313
|
-
public addToInputHistory(input: string): void {
|
|
314
|
-
// Avoid adding duplicate inputs
|
|
315
|
-
if (
|
|
316
|
-
this.userInputHistory.length > 0 &&
|
|
317
|
-
this.userInputHistory[this.userInputHistory.length - 1] === input
|
|
318
|
-
) {
|
|
319
|
-
return;
|
|
320
|
-
}
|
|
321
|
-
// Limit history records, keep the latest 100
|
|
322
|
-
this.setUserInputHistory([...this.userInputHistory, input].slice(-100));
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
// Clear input history
|
|
326
|
-
public clearInputHistory(): void {
|
|
327
|
-
this.setUserInputHistory([]);
|
|
328
|
-
}
|
|
329
|
-
|
|
330
315
|
// Encapsulated message operation functions
|
|
331
316
|
public addUserMessage(params: UserMessageParams): void {
|
|
332
317
|
const newMessages = addUserMessageToMessages({
|
|
@@ -402,17 +387,7 @@ export class MessageManager {
|
|
|
402
387
|
public updateToolBlock(params: AgentToolBlockUpdateParams): void {
|
|
403
388
|
const newMessages = updateToolBlockInMessage({
|
|
404
389
|
messages: this.messages,
|
|
405
|
-
|
|
406
|
-
parameters: params.parameters,
|
|
407
|
-
result: params.result,
|
|
408
|
-
success: params.success,
|
|
409
|
-
error: params.error,
|
|
410
|
-
stage: params.stage,
|
|
411
|
-
name: params.name,
|
|
412
|
-
shortResult: params.shortResult,
|
|
413
|
-
images: params.images,
|
|
414
|
-
compactParams: params.compactParams,
|
|
415
|
-
parametersChunk: params.parametersChunk,
|
|
390
|
+
...params,
|
|
416
391
|
});
|
|
417
392
|
this.setMessages(newMessages);
|
|
418
393
|
this.callbacks.onToolBlockUpdated?.(params);
|
|
@@ -442,14 +417,14 @@ export class MessageManager {
|
|
|
442
417
|
}
|
|
443
418
|
|
|
444
419
|
/**
|
|
445
|
-
* Compress messages and update session, delete compressed messages, only keep compressed messages and
|
|
420
|
+
* Compress messages and update session, delete compressed messages, only keep compressed messages and last 3 messages
|
|
446
421
|
*/
|
|
447
422
|
public compressMessagesAndUpdateSession(
|
|
448
|
-
insertIndex: number,
|
|
449
423
|
compressedContent: string,
|
|
450
424
|
usage?: Usage,
|
|
451
425
|
): void {
|
|
452
|
-
|
|
426
|
+
// Get last 3 messages to preserve
|
|
427
|
+
const lastThreeMessages = this.messages.slice(-3);
|
|
453
428
|
|
|
454
429
|
// Create compressed message
|
|
455
430
|
const compressMessage: Message = {
|
|
@@ -464,24 +439,24 @@ export class MessageManager {
|
|
|
464
439
|
...(usage && { usage }),
|
|
465
440
|
};
|
|
466
441
|
|
|
467
|
-
//
|
|
468
|
-
const
|
|
469
|
-
insertIndex < 0 ? currentMessages.length + insertIndex : insertIndex;
|
|
442
|
+
// Build new message array: keep the compressed message and last 3 messages
|
|
443
|
+
const newMessages: Message[] = [compressMessage, ...lastThreeMessages];
|
|
470
444
|
|
|
471
|
-
//
|
|
472
|
-
const
|
|
473
|
-
compressMessage,
|
|
474
|
-
...currentMessages.slice(actualIndex),
|
|
475
|
-
];
|
|
476
|
-
|
|
477
|
-
// Update sessionId
|
|
445
|
+
// Update sessionId and parentSessionId
|
|
446
|
+
const oldSessionId = this.sessionId;
|
|
478
447
|
this.setSessionId(generateSessionId());
|
|
448
|
+
this.parentSessionId = oldSessionId;
|
|
449
|
+
|
|
450
|
+
// Trigger task list update if this is the main session to ensure continuity
|
|
451
|
+
if (this.sessionType === "main") {
|
|
452
|
+
this.callbacks.onSessionIdChange?.(this.sessionId);
|
|
453
|
+
}
|
|
479
454
|
|
|
480
455
|
// Set new message list
|
|
481
456
|
this.setMessages(newMessages);
|
|
482
457
|
|
|
483
|
-
// Trigger compression callback
|
|
484
|
-
this.callbacks.onCompressBlockAdded?.(
|
|
458
|
+
// Trigger compression callback
|
|
459
|
+
this.callbacks.onCompressBlockAdded?.(compressedContent);
|
|
485
460
|
}
|
|
486
461
|
|
|
487
462
|
public addFileHistoryBlock(
|
|
@@ -496,6 +471,7 @@ export class MessageManager {
|
|
|
496
471
|
snapshots,
|
|
497
472
|
} as unknown as import("../types/index.js").MessageBlock);
|
|
498
473
|
this.setMessages([...this.messages]);
|
|
474
|
+
this.callbacks.onFileHistoryBlockAdded?.(snapshots);
|
|
499
475
|
}
|
|
500
476
|
}
|
|
501
477
|
|
|
@@ -703,6 +679,14 @@ export class MessageManager {
|
|
|
703
679
|
this.setMessages(newMessages);
|
|
704
680
|
}
|
|
705
681
|
|
|
682
|
+
public async getFullMessageThread(): Promise<{
|
|
683
|
+
messages: Message[];
|
|
684
|
+
sessionIds: string[];
|
|
685
|
+
}> {
|
|
686
|
+
const { loadFullMessageThread } = await import("../services/session.js");
|
|
687
|
+
return loadFullMessageThread(this.sessionId, this.workdir);
|
|
688
|
+
}
|
|
689
|
+
|
|
706
690
|
/**
|
|
707
691
|
* Truncate history to a specific index and revert file changes.
|
|
708
692
|
* @param index - The index of the user message to truncate to.
|
|
@@ -712,30 +696,110 @@ export class MessageManager {
|
|
|
712
696
|
index: number,
|
|
713
697
|
reversionManager?: import("./reversionManager.js").ReversionManager,
|
|
714
698
|
): Promise<void> {
|
|
715
|
-
|
|
699
|
+
const { messages, sessionIds } = await this.getFullMessageThread();
|
|
700
|
+
|
|
701
|
+
if (index < 0 || index >= messages.length) {
|
|
716
702
|
throw new Error(`Invalid message index: ${index}`);
|
|
717
703
|
}
|
|
718
704
|
|
|
719
|
-
//
|
|
720
|
-
|
|
705
|
+
// Find which session the index belongs to
|
|
706
|
+
let targetSessionId = this.sessionId;
|
|
707
|
+
let targetIndexInSession = index;
|
|
708
|
+
|
|
709
|
+
// We need to be careful here because loadFullMessageThread might have removed "compress" blocks
|
|
710
|
+
// Let's re-calculate based on the actual messages returned.
|
|
711
|
+
// Actually, it's easier to just load sessions one by one again or keep track of counts.
|
|
712
|
+
|
|
713
|
+
// For simplicity, let's assume we want to truncate the WHOLE thread.
|
|
714
|
+
// If the index is in a previous session, we need to:
|
|
715
|
+
// 1. Load that session.
|
|
716
|
+
// 2. Truncate it.
|
|
717
|
+
// 3. Make it the current session.
|
|
718
|
+
// 4. Delete/Invalidate subsequent sessions.
|
|
719
|
+
|
|
720
|
+
// To correctly map 'index' to a session, we need to know the message count of each session
|
|
721
|
+
// as they appear in the concatenated 'messages' array.
|
|
722
|
+
|
|
723
|
+
let remainingIndex = index;
|
|
724
|
+
const { loadSessionFromJsonl } = await import("../services/session.js");
|
|
725
|
+
|
|
726
|
+
for (const sid of sessionIds) {
|
|
727
|
+
const sessionData = await loadSessionFromJsonl(sid, this.workdir);
|
|
728
|
+
if (!sessionData) continue;
|
|
729
|
+
|
|
730
|
+
const sessionMessages = sessionData.messages;
|
|
731
|
+
// If this is not the first session in the thread, it might have a compress block at the start
|
|
732
|
+
// that was removed in getFullMessageThread.
|
|
733
|
+
const hasCompressBlock = sessionMessages[0]?.blocks.some(
|
|
734
|
+
(b) => b.type === "compress",
|
|
735
|
+
);
|
|
736
|
+
const effectiveMessages =
|
|
737
|
+
hasCompressBlock && sid !== sessionIds[0]
|
|
738
|
+
? sessionMessages.slice(1)
|
|
739
|
+
: sessionMessages;
|
|
740
|
+
|
|
741
|
+
if (remainingIndex < effectiveMessages.length) {
|
|
742
|
+
targetSessionId = sid;
|
|
743
|
+
targetIndexInSession = hasCompressBlock
|
|
744
|
+
? remainingIndex + 1
|
|
745
|
+
: remainingIndex;
|
|
746
|
+
break;
|
|
747
|
+
}
|
|
748
|
+
remainingIndex -= effectiveMessages.length;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
// Load the target session to perform truncation
|
|
752
|
+
const targetSessionData = await loadSessionFromJsonl(
|
|
753
|
+
targetSessionId,
|
|
754
|
+
this.workdir,
|
|
755
|
+
);
|
|
756
|
+
if (!targetSessionData)
|
|
757
|
+
throw new Error(`Target session ${targetSessionId} not found`);
|
|
758
|
+
|
|
759
|
+
// Identify messages to be removed (from the whole thread)
|
|
760
|
+
const messagesToRemove = messages.slice(index);
|
|
721
761
|
const messageIdsToRemove = messagesToRemove
|
|
722
762
|
.map((m) => m.id as string)
|
|
723
763
|
.filter((id) => !!id);
|
|
724
764
|
|
|
725
765
|
// Revert file changes if manager is provided
|
|
726
766
|
if (reversionManager && messageIdsToRemove.length > 0) {
|
|
727
|
-
await reversionManager.revertTo(messageIdsToRemove,
|
|
767
|
+
await reversionManager.revertTo(messageIdsToRemove, messages);
|
|
728
768
|
}
|
|
729
769
|
|
|
730
|
-
// Truncate messages in
|
|
731
|
-
const
|
|
732
|
-
|
|
770
|
+
// Truncate messages in the target session
|
|
771
|
+
const newMessagesInSession = targetSessionData.messages.slice(
|
|
772
|
+
0,
|
|
773
|
+
targetIndexInSession,
|
|
774
|
+
);
|
|
775
|
+
|
|
776
|
+
// Identify subagent tasks to stop
|
|
777
|
+
for (const message of messagesToRemove) {
|
|
778
|
+
for (const block of message.blocks) {
|
|
779
|
+
if (block.type === "subagent" && block.subagentId) {
|
|
780
|
+
this.callbacks.onSubagentTaskStopRequested?.(block.subagentId);
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
// Update target session file
|
|
786
|
+
this.sessionId = targetSessionId;
|
|
787
|
+
this.rootSessionId = targetSessionData.rootSessionId || targetSessionId;
|
|
788
|
+
this.parentSessionId = targetSessionData.parentSessionId;
|
|
789
|
+
this.transcriptPath = this.computeTranscriptPath();
|
|
790
|
+
|
|
791
|
+
await this.rewriteSessionFile(newMessagesInSession);
|
|
733
792
|
|
|
734
|
-
// Update
|
|
735
|
-
|
|
793
|
+
// Update in-memory messages to the truncated session messages
|
|
794
|
+
// We do NOT include ancestor messages here to avoid exceeding context limits.
|
|
795
|
+
// The 'compress' block at the start of the session (if any) already summarizes them.
|
|
796
|
+
this.setMessages(newMessagesInSession);
|
|
736
797
|
|
|
737
798
|
// Update saved message count
|
|
738
|
-
this.savedMessageCount =
|
|
799
|
+
this.savedMessageCount = newMessagesInSession.length;
|
|
800
|
+
|
|
801
|
+
// Notify session ID change if it changed
|
|
802
|
+
this.callbacks.onSessionIdChange?.(this.sessionId);
|
|
739
803
|
}
|
|
740
804
|
|
|
741
805
|
/**
|
|
@@ -323,40 +323,30 @@ export class PermissionManager {
|
|
|
323
323
|
workdir,
|
|
324
324
|
);
|
|
325
325
|
if (!isInside) {
|
|
326
|
-
this.logger?.
|
|
327
|
-
"File operation outside the Safe Zone in acceptEdits mode",
|
|
326
|
+
this.logger?.info(
|
|
327
|
+
"File operation outside the Safe Zone in acceptEdits mode, falling back to manual confirmation",
|
|
328
328
|
{
|
|
329
329
|
toolName: context.toolName,
|
|
330
330
|
targetPath,
|
|
331
331
|
resolvedPath,
|
|
332
332
|
},
|
|
333
333
|
);
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
334
|
+
// Fall through to normal permission check flow to trigger confirmation prompt
|
|
335
|
+
} else {
|
|
336
|
+
this.logger?.debug(
|
|
337
|
+
"Permission automatically accepted for tool in acceptEdits mode",
|
|
338
|
+
{
|
|
339
|
+
toolName: context.toolName,
|
|
340
|
+
},
|
|
341
|
+
);
|
|
342
|
+
return { behavior: "allow" };
|
|
338
343
|
}
|
|
339
344
|
}
|
|
340
|
-
|
|
341
|
-
this.logger?.debug(
|
|
342
|
-
"Permission automatically accepted for tool in acceptEdits mode",
|
|
343
|
-
{
|
|
344
|
-
toolName: context.toolName,
|
|
345
|
-
},
|
|
346
|
-
);
|
|
347
|
-
return { behavior: "allow" };
|
|
348
345
|
}
|
|
349
346
|
}
|
|
350
347
|
|
|
351
348
|
// 1.3 If plan mode, allow Read-only tools and Edit/Write for plan file
|
|
352
349
|
if (context.permissionMode === "plan") {
|
|
353
|
-
if (context.toolName === BASH_TOOL_NAME) {
|
|
354
|
-
return {
|
|
355
|
-
behavior: "deny",
|
|
356
|
-
message: "Bash commands are not allowed in plan mode.",
|
|
357
|
-
};
|
|
358
|
-
}
|
|
359
|
-
|
|
360
350
|
const writeTools = [
|
|
361
351
|
EDIT_TOOL_NAME,
|
|
362
352
|
MULTI_EDIT_TOOL_NAME,
|
|
@@ -492,6 +482,27 @@ export class PermissionManager {
|
|
|
492
482
|
suggestedPrefix,
|
|
493
483
|
};
|
|
494
484
|
|
|
485
|
+
// Set hidePersistentOption for out-of-bounds file operations
|
|
486
|
+
const fileTools = [
|
|
487
|
+
EDIT_TOOL_NAME,
|
|
488
|
+
MULTI_EDIT_TOOL_NAME,
|
|
489
|
+
DELETE_FILE_TOOL_NAME,
|
|
490
|
+
WRITE_TOOL_NAME,
|
|
491
|
+
];
|
|
492
|
+
if (fileTools.includes(toolName)) {
|
|
493
|
+
const targetPath = (toolInput?.file_path || toolInput?.target_file) as
|
|
494
|
+
| string
|
|
495
|
+
| undefined;
|
|
496
|
+
const workdir = toolInput?.workdir as string | undefined;
|
|
497
|
+
|
|
498
|
+
if (targetPath) {
|
|
499
|
+
const { isInside } = this.isInsideSafeZone(targetPath, workdir);
|
|
500
|
+
if (!isInside) {
|
|
501
|
+
context.hidePersistentOption = true;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
495
506
|
// Set hidePersistentOption for dangerous or out-of-bounds bash commands
|
|
496
507
|
if (toolName === BASH_TOOL_NAME && toolInput?.command) {
|
|
497
508
|
const command = String(toolInput.command);
|
|
@@ -17,7 +17,7 @@ export class PlanManager {
|
|
|
17
17
|
/**
|
|
18
18
|
* Ensures the plan directory exists and generates a new plan file path with a random name
|
|
19
19
|
*/
|
|
20
|
-
public async getOrGeneratePlanFilePath(): Promise<{
|
|
20
|
+
public async getOrGeneratePlanFilePath(seed?: string): Promise<{
|
|
21
21
|
path: string;
|
|
22
22
|
name: string;
|
|
23
23
|
}> {
|
|
@@ -30,7 +30,7 @@ export class PlanManager {
|
|
|
30
30
|
);
|
|
31
31
|
throw error;
|
|
32
32
|
}
|
|
33
|
-
const name = generateRandomName();
|
|
33
|
+
const name = generateRandomName(seed);
|
|
34
34
|
const filePath = path.join(this.planDir, `${name}.md`);
|
|
35
35
|
this.logger?.info(`Generated plan file path: ${filePath}`);
|
|
36
36
|
return { path: filePath, name };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { MessageManager } from "./messageManager.js";
|
|
2
2
|
import type { AIManager } from "./aiManager.js";
|
|
3
3
|
import type { BackgroundTaskManager } from "./backgroundTaskManager.js";
|
|
4
|
+
import type { TaskManager } from "../services/taskManager.js";
|
|
4
5
|
import type {
|
|
5
6
|
SlashCommand,
|
|
6
7
|
CustomSlashCommand,
|
|
@@ -28,6 +29,7 @@ export interface SlashCommandManagerOptions {
|
|
|
28
29
|
messageManager: MessageManager;
|
|
29
30
|
aiManager: AIManager;
|
|
30
31
|
backgroundTaskManager: BackgroundTaskManager;
|
|
32
|
+
taskManager: TaskManager;
|
|
31
33
|
workdir: string;
|
|
32
34
|
logger?: Logger;
|
|
33
35
|
}
|
|
@@ -38,6 +40,7 @@ export class SlashCommandManager {
|
|
|
38
40
|
private messageManager: MessageManager;
|
|
39
41
|
private aiManager: AIManager;
|
|
40
42
|
private backgroundTaskManager: BackgroundTaskManager;
|
|
43
|
+
private taskManager: TaskManager;
|
|
41
44
|
private workdir: string;
|
|
42
45
|
private logger?: Logger;
|
|
43
46
|
|
|
@@ -45,6 +48,7 @@ export class SlashCommandManager {
|
|
|
45
48
|
this.messageManager = options.messageManager;
|
|
46
49
|
this.aiManager = options.aiManager;
|
|
47
50
|
this.backgroundTaskManager = options.backgroundTaskManager;
|
|
51
|
+
this.taskManager = options.taskManager;
|
|
48
52
|
this.workdir = options.workdir;
|
|
49
53
|
this.logger = options.logger;
|
|
50
54
|
|
|
@@ -61,8 +65,13 @@ export class SlashCommandManager {
|
|
|
61
65
|
handler: () => {
|
|
62
66
|
// Clear chat messages
|
|
63
67
|
this.messageManager.clearMessages();
|
|
64
|
-
|
|
65
|
-
|
|
68
|
+
|
|
69
|
+
// Reset task list if WAVE_TASK_LIST_ID is not set
|
|
70
|
+
if (!process.env.WAVE_TASK_LIST_ID) {
|
|
71
|
+
const newTaskListId = this.messageManager.getRootSessionId();
|
|
72
|
+
this.taskManager.setTaskListId(newTaskListId);
|
|
73
|
+
this.taskManager.emit("tasksChange", newTaskListId);
|
|
74
|
+
}
|
|
66
75
|
},
|
|
67
76
|
});
|
|
68
77
|
|