wave-agent-sdk 0.0.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/README.md +32 -0
- package/dist/agent.d.ts +96 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +286 -0
- package/dist/hooks/executor.d.ts +56 -0
- package/dist/hooks/executor.d.ts.map +1 -0
- package/dist/hooks/executor.js +312 -0
- package/dist/hooks/index.d.ts +17 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +14 -0
- package/dist/hooks/manager.d.ts +90 -0
- package/dist/hooks/manager.d.ts.map +1 -0
- package/dist/hooks/manager.js +395 -0
- package/dist/hooks/matcher.d.ts +49 -0
- package/dist/hooks/matcher.d.ts.map +1 -0
- package/dist/hooks/matcher.js +147 -0
- package/dist/hooks/settings.d.ts +46 -0
- package/dist/hooks/settings.d.ts.map +1 -0
- package/dist/hooks/settings.js +100 -0
- package/dist/hooks/types.d.ts +80 -0
- package/dist/hooks/types.d.ts.map +1 -0
- package/dist/hooks/types.js +59 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/managers/aiManager.d.ts +61 -0
- package/dist/managers/aiManager.d.ts.map +1 -0
- package/dist/managers/aiManager.js +415 -0
- package/dist/managers/backgroundBashManager.d.ts +27 -0
- package/dist/managers/backgroundBashManager.d.ts.map +1 -0
- package/dist/managers/backgroundBashManager.js +166 -0
- package/dist/managers/bashManager.d.ts +20 -0
- package/dist/managers/bashManager.d.ts.map +1 -0
- package/dist/managers/bashManager.js +66 -0
- package/dist/managers/mcpManager.d.ts +63 -0
- package/dist/managers/mcpManager.d.ts.map +1 -0
- package/dist/managers/mcpManager.js +378 -0
- package/dist/managers/messageManager.d.ts +85 -0
- package/dist/managers/messageManager.d.ts.map +1 -0
- package/dist/managers/messageManager.js +265 -0
- package/dist/managers/skillManager.d.ts +59 -0
- package/dist/managers/skillManager.d.ts.map +1 -0
- package/dist/managers/skillManager.js +317 -0
- package/dist/managers/slashCommandManager.d.ts +77 -0
- package/dist/managers/slashCommandManager.d.ts.map +1 -0
- package/dist/managers/slashCommandManager.js +208 -0
- package/dist/managers/toolManager.d.ts +23 -0
- package/dist/managers/toolManager.d.ts.map +1 -0
- package/dist/managers/toolManager.js +79 -0
- package/dist/services/aiService.d.ts +28 -0
- package/dist/services/aiService.d.ts.map +1 -0
- package/dist/services/aiService.js +180 -0
- package/dist/services/memory.d.ts +8 -0
- package/dist/services/memory.d.ts.map +1 -0
- package/dist/services/memory.js +128 -0
- package/dist/services/session.d.ts +54 -0
- package/dist/services/session.d.ts.map +1 -0
- package/dist/services/session.js +196 -0
- package/dist/tools/bashTool.d.ts +14 -0
- package/dist/tools/bashTool.d.ts.map +1 -0
- package/dist/tools/bashTool.js +351 -0
- package/dist/tools/deleteFileTool.d.ts +6 -0
- package/dist/tools/deleteFileTool.d.ts.map +1 -0
- package/dist/tools/deleteFileTool.js +67 -0
- package/dist/tools/editTool.d.ts +6 -0
- package/dist/tools/editTool.d.ts.map +1 -0
- package/dist/tools/editTool.js +168 -0
- package/dist/tools/globTool.d.ts +6 -0
- package/dist/tools/globTool.d.ts.map +1 -0
- package/dist/tools/globTool.js +113 -0
- package/dist/tools/grepTool.d.ts +6 -0
- package/dist/tools/grepTool.d.ts.map +1 -0
- package/dist/tools/grepTool.js +268 -0
- package/dist/tools/lsTool.d.ts +6 -0
- package/dist/tools/lsTool.d.ts.map +1 -0
- package/dist/tools/lsTool.js +160 -0
- package/dist/tools/multiEditTool.d.ts +6 -0
- package/dist/tools/multiEditTool.d.ts.map +1 -0
- package/dist/tools/multiEditTool.js +222 -0
- package/dist/tools/readTool.d.ts +6 -0
- package/dist/tools/readTool.d.ts.map +1 -0
- package/dist/tools/readTool.js +136 -0
- package/dist/tools/types.d.ts +35 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +4 -0
- package/dist/tools/writeTool.d.ts +6 -0
- package/dist/tools/writeTool.d.ts.map +1 -0
- package/dist/tools/writeTool.js +138 -0
- package/dist/types.d.ts +212 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +13 -0
- package/dist/utils/bashHistory.d.ts +46 -0
- package/dist/utils/bashHistory.d.ts.map +1 -0
- package/dist/utils/bashHistory.js +236 -0
- package/dist/utils/commandArgumentParser.d.ts +34 -0
- package/dist/utils/commandArgumentParser.d.ts.map +1 -0
- package/dist/utils/commandArgumentParser.js +123 -0
- package/dist/utils/constants.d.ts +27 -0
- package/dist/utils/constants.d.ts.map +1 -0
- package/dist/utils/constants.js +28 -0
- package/dist/utils/convertMessagesForAPI.d.ts +9 -0
- package/dist/utils/convertMessagesForAPI.d.ts.map +1 -0
- package/dist/utils/convertMessagesForAPI.js +189 -0
- package/dist/utils/customCommands.d.ts +14 -0
- package/dist/utils/customCommands.d.ts.map +1 -0
- package/dist/utils/customCommands.js +71 -0
- package/dist/utils/fileFilter.d.ts +26 -0
- package/dist/utils/fileFilter.d.ts.map +1 -0
- package/dist/utils/fileFilter.js +177 -0
- package/dist/utils/markdownParser.d.ts +27 -0
- package/dist/utils/markdownParser.d.ts.map +1 -0
- package/dist/utils/markdownParser.js +109 -0
- package/dist/utils/mcpUtils.d.ts +24 -0
- package/dist/utils/mcpUtils.d.ts.map +1 -0
- package/dist/utils/mcpUtils.js +51 -0
- package/dist/utils/messageOperations.d.ts +118 -0
- package/dist/utils/messageOperations.d.ts.map +1 -0
- package/dist/utils/messageOperations.js +334 -0
- package/dist/utils/path.d.ts +25 -0
- package/dist/utils/path.d.ts.map +1 -0
- package/dist/utils/path.js +109 -0
- package/dist/utils/skillParser.d.ts +18 -0
- package/dist/utils/skillParser.d.ts.map +1 -0
- package/dist/utils/skillParser.js +147 -0
- package/dist/utils/stringUtils.d.ts +13 -0
- package/dist/utils/stringUtils.d.ts.map +1 -0
- package/dist/utils/stringUtils.js +44 -0
- package/package.json +51 -0
- package/src/agent.ts +405 -0
- package/src/hooks/executor.ts +440 -0
- package/src/hooks/index.ts +52 -0
- package/src/hooks/manager.ts +618 -0
- package/src/hooks/matcher.ts +187 -0
- package/src/hooks/settings.ts +129 -0
- package/src/hooks/types.ts +169 -0
- package/src/index.ts +24 -0
- package/src/managers/aiManager.ts +573 -0
- package/src/managers/backgroundBashManager.ts +203 -0
- package/src/managers/bashManager.ts +97 -0
- package/src/managers/mcpManager.ts +493 -0
- package/src/managers/messageManager.ts +415 -0
- package/src/managers/skillManager.ts +404 -0
- package/src/managers/slashCommandManager.ts +293 -0
- package/src/managers/toolManager.ts +106 -0
- package/src/services/aiService.ts +252 -0
- package/src/services/memory.ts +149 -0
- package/src/services/session.ts +265 -0
- package/src/tools/bashTool.ts +402 -0
- package/src/tools/deleteFileTool.ts +81 -0
- package/src/tools/editTool.ts +192 -0
- package/src/tools/globTool.ts +135 -0
- package/src/tools/grepTool.ts +326 -0
- package/src/tools/lsTool.ts +187 -0
- package/src/tools/multiEditTool.ts +268 -0
- package/src/tools/readTool.ts +165 -0
- package/src/tools/types.ts +47 -0
- package/src/tools/writeTool.ts +163 -0
- package/src/types.ts +260 -0
- package/src/utils/bashHistory.ts +303 -0
- package/src/utils/commandArgumentParser.ts +153 -0
- package/src/utils/constants.ts +37 -0
- package/src/utils/convertMessagesForAPI.ts +236 -0
- package/src/utils/customCommands.ts +85 -0
- package/src/utils/fileFilter.ts +202 -0
- package/src/utils/markdownParser.ts +156 -0
- package/src/utils/mcpUtils.ts +81 -0
- package/src/utils/messageOperations.ts +506 -0
- package/src/utils/path.ts +118 -0
- package/src/utils/skillParser.ts +188 -0
- package/src/utils/stringUtils.ts +50 -0
|
@@ -0,0 +1,506 @@
|
|
|
1
|
+
import type { Message } from "../types.js";
|
|
2
|
+
import { readFileSync } from "fs";
|
|
3
|
+
import { extname } from "path";
|
|
4
|
+
import { ChatCompletionMessageFunctionToolCall } from "openai/resources.js";
|
|
5
|
+
|
|
6
|
+
// Parameter interfaces for message operations
|
|
7
|
+
export interface AddUserMessageParams {
|
|
8
|
+
messages: Message[];
|
|
9
|
+
content: string;
|
|
10
|
+
images?: Array<{ path: string; mimeType: string }>;
|
|
11
|
+
customCommandBlock?: {
|
|
12
|
+
type: "custom_command";
|
|
13
|
+
commandName: string;
|
|
14
|
+
content: string;
|
|
15
|
+
originalInput?: string;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface UpdateToolBlockParams {
|
|
20
|
+
messages: Message[];
|
|
21
|
+
id: string;
|
|
22
|
+
parameters: string;
|
|
23
|
+
result?: string;
|
|
24
|
+
success?: boolean;
|
|
25
|
+
error?: string;
|
|
26
|
+
isRunning?: boolean;
|
|
27
|
+
name?: string;
|
|
28
|
+
shortResult?: string;
|
|
29
|
+
images?: Array<{ data: string; mediaType?: string }>;
|
|
30
|
+
compactParams?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Agent specific interfaces (without messages parameter)
|
|
34
|
+
export interface AgentToolBlockUpdateParams {
|
|
35
|
+
toolId: string;
|
|
36
|
+
args?: string;
|
|
37
|
+
result?: string;
|
|
38
|
+
success?: boolean;
|
|
39
|
+
error?: string;
|
|
40
|
+
isRunning?: boolean;
|
|
41
|
+
name?: string;
|
|
42
|
+
shortResult?: string;
|
|
43
|
+
compactParams?: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface AddDiffBlockParams {
|
|
47
|
+
messages: Message[];
|
|
48
|
+
path: string;
|
|
49
|
+
diffResult: Array<{ value: string; added?: boolean; removed?: boolean }>;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface AddErrorBlockParams {
|
|
53
|
+
messages: Message[];
|
|
54
|
+
error: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface AddMemoryBlockParams {
|
|
58
|
+
messages: Message[];
|
|
59
|
+
content: string;
|
|
60
|
+
isSuccess: boolean;
|
|
61
|
+
memoryType?: "project" | "user";
|
|
62
|
+
storagePath?: string;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface AddCommandOutputParams {
|
|
66
|
+
messages: Message[];
|
|
67
|
+
command: string;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface UpdateCommandOutputParams {
|
|
71
|
+
messages: Message[];
|
|
72
|
+
command: string;
|
|
73
|
+
output: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface CompleteCommandParams {
|
|
77
|
+
messages: Message[];
|
|
78
|
+
command: string;
|
|
79
|
+
exitCode: number;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Extract text content from user messages in the messages array
|
|
84
|
+
*/
|
|
85
|
+
export const extractUserInputHistory = (messages: Message[]): string[] => {
|
|
86
|
+
return messages
|
|
87
|
+
.filter((message) => message.role === "user")
|
|
88
|
+
.map((message) => {
|
|
89
|
+
// Extract all text block content and merge
|
|
90
|
+
const textBlocks = message.blocks.filter(
|
|
91
|
+
(block) => block.type === "text",
|
|
92
|
+
);
|
|
93
|
+
return textBlocks
|
|
94
|
+
.map((block) => block.content)
|
|
95
|
+
.join(" ")
|
|
96
|
+
.trim();
|
|
97
|
+
})
|
|
98
|
+
.filter((text) => text.length > 0); // Filter out empty text
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Convert image file path to base64 format
|
|
103
|
+
* @param imagePath Image file path
|
|
104
|
+
* @returns base64 format image data URL
|
|
105
|
+
*/
|
|
106
|
+
export const convertImageToBase64 = (imagePath: string): string => {
|
|
107
|
+
try {
|
|
108
|
+
const imageBuffer = readFileSync(imagePath);
|
|
109
|
+
const ext = extname(imagePath).toLowerCase().substring(1);
|
|
110
|
+
|
|
111
|
+
// Determine MIME type based on file extension
|
|
112
|
+
let mimeType = "image/png"; // Default
|
|
113
|
+
switch (ext) {
|
|
114
|
+
case "jpg":
|
|
115
|
+
case "jpeg":
|
|
116
|
+
mimeType = "image/jpeg";
|
|
117
|
+
break;
|
|
118
|
+
case "png":
|
|
119
|
+
mimeType = "image/png";
|
|
120
|
+
break;
|
|
121
|
+
case "gif":
|
|
122
|
+
mimeType = "image/gif";
|
|
123
|
+
break;
|
|
124
|
+
case "webp":
|
|
125
|
+
mimeType = "image/webp";
|
|
126
|
+
break;
|
|
127
|
+
case "bmp":
|
|
128
|
+
mimeType = "image/bmp";
|
|
129
|
+
break;
|
|
130
|
+
default:
|
|
131
|
+
mimeType = "image/png";
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const base64String = imageBuffer.toString("base64");
|
|
135
|
+
return `data:${mimeType};base64,${base64String}`;
|
|
136
|
+
} catch {
|
|
137
|
+
// logger.error(`Failed to convert image to base64: ${imagePath}`, error);
|
|
138
|
+
// Return an error placeholder or throw error
|
|
139
|
+
return `data:image/png;base64,`; // Empty base64, avoid program crash
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
// Add user message
|
|
144
|
+
export const addUserMessageToMessages = ({
|
|
145
|
+
messages,
|
|
146
|
+
content,
|
|
147
|
+
images,
|
|
148
|
+
customCommandBlock,
|
|
149
|
+
}: AddUserMessageParams): Message[] => {
|
|
150
|
+
const blocks: Message["blocks"] = [];
|
|
151
|
+
|
|
152
|
+
// If there's a custom command block, use it instead of text content
|
|
153
|
+
if (customCommandBlock) {
|
|
154
|
+
blocks.push(customCommandBlock);
|
|
155
|
+
} else {
|
|
156
|
+
blocks.push({ type: "text", content });
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// If there are images, add image block
|
|
160
|
+
if (images && images.length > 0) {
|
|
161
|
+
const imageUrls = images.map((img) => img.path);
|
|
162
|
+
blocks.push({
|
|
163
|
+
type: "image",
|
|
164
|
+
imageUrls,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const userMessage: Message = {
|
|
169
|
+
role: "user",
|
|
170
|
+
blocks,
|
|
171
|
+
};
|
|
172
|
+
return [...messages, userMessage];
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
// Add assistant message (support one-time addition of answer and tool calls)
|
|
176
|
+
export const addAssistantMessageToMessages = (
|
|
177
|
+
messages: Message[],
|
|
178
|
+
content?: string,
|
|
179
|
+
toolCalls?: ChatCompletionMessageFunctionToolCall[],
|
|
180
|
+
): Message[] => {
|
|
181
|
+
const blocks: Message["blocks"] = [];
|
|
182
|
+
|
|
183
|
+
// If there's answer content, add text block
|
|
184
|
+
if (content) {
|
|
185
|
+
blocks.push({ type: "text", content: content });
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// If there are tool calls, add tool blocks
|
|
189
|
+
if (toolCalls && toolCalls.length > 0) {
|
|
190
|
+
toolCalls.forEach((toolCall) => {
|
|
191
|
+
blocks.push({
|
|
192
|
+
type: "tool",
|
|
193
|
+
parameters: toolCall.function.arguments || "",
|
|
194
|
+
result: "",
|
|
195
|
+
id: toolCall.id || "",
|
|
196
|
+
name: toolCall.function?.name || "",
|
|
197
|
+
isRunning: false,
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const initialAssistantMessage: Message = {
|
|
203
|
+
role: "assistant",
|
|
204
|
+
blocks,
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
return [...messages, initialAssistantMessage];
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
// Update File Operation Block of the last assistant message
|
|
211
|
+
export const addDiffBlockToMessage = ({
|
|
212
|
+
messages,
|
|
213
|
+
path,
|
|
214
|
+
diffResult,
|
|
215
|
+
}: AddDiffBlockParams): Message[] => {
|
|
216
|
+
const newMessages = [...messages];
|
|
217
|
+
// Find the last assistant message
|
|
218
|
+
for (let i = newMessages.length - 1; i >= 0; i--) {
|
|
219
|
+
if (newMessages[i].role === "assistant") {
|
|
220
|
+
// Directly add diff block instead of replacing existing blocks
|
|
221
|
+
newMessages[i].blocks.push({
|
|
222
|
+
type: "diff",
|
|
223
|
+
path: path,
|
|
224
|
+
diffResult: diffResult,
|
|
225
|
+
});
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return newMessages;
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
// Update Tool Block of the last assistant message
|
|
233
|
+
export const updateToolBlockInMessage = ({
|
|
234
|
+
messages,
|
|
235
|
+
id,
|
|
236
|
+
parameters,
|
|
237
|
+
result,
|
|
238
|
+
success,
|
|
239
|
+
error,
|
|
240
|
+
isRunning,
|
|
241
|
+
name,
|
|
242
|
+
shortResult,
|
|
243
|
+
images,
|
|
244
|
+
compactParams,
|
|
245
|
+
}: UpdateToolBlockParams): Message[] => {
|
|
246
|
+
const newMessages = [...messages];
|
|
247
|
+
// Find the last assistant message
|
|
248
|
+
for (let i = newMessages.length - 1; i >= 0; i--) {
|
|
249
|
+
if (newMessages[i].role === "assistant") {
|
|
250
|
+
const toolBlockIndex = newMessages[i].blocks.findIndex(
|
|
251
|
+
(block) => block.type === "tool" && block.id === id,
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
if (toolBlockIndex !== -1) {
|
|
255
|
+
const toolBlock = newMessages[i].blocks[toolBlockIndex];
|
|
256
|
+
if (toolBlock.type === "tool") {
|
|
257
|
+
toolBlock.parameters = parameters;
|
|
258
|
+
if (result !== undefined) toolBlock.result = result;
|
|
259
|
+
if (shortResult !== undefined) toolBlock.shortResult = shortResult;
|
|
260
|
+
toolBlock.images = images; // Add image data update
|
|
261
|
+
if (success !== undefined) toolBlock.success = success;
|
|
262
|
+
if (error !== undefined) toolBlock.error = error;
|
|
263
|
+
if (isRunning !== undefined) toolBlock.isRunning = isRunning;
|
|
264
|
+
if (compactParams !== undefined)
|
|
265
|
+
toolBlock.compactParams = compactParams;
|
|
266
|
+
}
|
|
267
|
+
} else if (result !== undefined) {
|
|
268
|
+
// If existing block not found, create new one
|
|
269
|
+
newMessages[i].blocks.push({
|
|
270
|
+
type: "tool",
|
|
271
|
+
parameters: parameters,
|
|
272
|
+
result: result,
|
|
273
|
+
shortResult: shortResult,
|
|
274
|
+
images: images, // Add image data
|
|
275
|
+
id: id,
|
|
276
|
+
name: name || "unknown",
|
|
277
|
+
success: success,
|
|
278
|
+
error: error,
|
|
279
|
+
isRunning: isRunning ?? false,
|
|
280
|
+
compactParams: compactParams,
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
break;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return newMessages;
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
// Add Error Block to the last assistant message
|
|
290
|
+
export const addErrorBlockToMessage = ({
|
|
291
|
+
messages,
|
|
292
|
+
error,
|
|
293
|
+
}: AddErrorBlockParams): Message[] => {
|
|
294
|
+
const newMessages = [...messages];
|
|
295
|
+
// Find the last assistant message
|
|
296
|
+
let assistantMessageFound = false;
|
|
297
|
+
for (let i = newMessages.length - 1; i >= 0; i--) {
|
|
298
|
+
if (newMessages[i].role === "assistant") {
|
|
299
|
+
newMessages[i].blocks = [
|
|
300
|
+
...newMessages[i].blocks,
|
|
301
|
+
{
|
|
302
|
+
type: "error",
|
|
303
|
+
content: error,
|
|
304
|
+
},
|
|
305
|
+
];
|
|
306
|
+
assistantMessageFound = true;
|
|
307
|
+
break;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// If no assistant message found, create a new assistant message with only error block
|
|
312
|
+
if (!assistantMessageFound) {
|
|
313
|
+
newMessages.push({
|
|
314
|
+
role: "assistant",
|
|
315
|
+
blocks: [
|
|
316
|
+
{
|
|
317
|
+
type: "error",
|
|
318
|
+
content: error,
|
|
319
|
+
},
|
|
320
|
+
],
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return newMessages;
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
// Add Memory Block as new assistant message
|
|
328
|
+
export const addMemoryBlockToMessage = ({
|
|
329
|
+
messages,
|
|
330
|
+
content,
|
|
331
|
+
isSuccess,
|
|
332
|
+
memoryType,
|
|
333
|
+
storagePath,
|
|
334
|
+
}: AddMemoryBlockParams): Message[] => {
|
|
335
|
+
const newMessages = [...messages];
|
|
336
|
+
|
|
337
|
+
// Create new assistant message containing MemoryBlock
|
|
338
|
+
const memoryMessage: Message = {
|
|
339
|
+
role: "assistant",
|
|
340
|
+
blocks: [
|
|
341
|
+
{
|
|
342
|
+
type: "memory",
|
|
343
|
+
content,
|
|
344
|
+
isSuccess,
|
|
345
|
+
memoryType,
|
|
346
|
+
storagePath,
|
|
347
|
+
},
|
|
348
|
+
],
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
// Add to end of message list
|
|
352
|
+
newMessages.push(memoryMessage);
|
|
353
|
+
return newMessages;
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Count valid blocks from the end
|
|
358
|
+
* Only text, image, and tool type blocks are counted
|
|
359
|
+
* @param messages Message array
|
|
360
|
+
* @param targetCount Number of valid blocks to count
|
|
361
|
+
* @returns { messageIndex: number, blockCount: number } Message index and actual counted block count
|
|
362
|
+
*/
|
|
363
|
+
export const countValidBlocksFromEnd = (
|
|
364
|
+
messages: Message[],
|
|
365
|
+
targetCount: number,
|
|
366
|
+
): { messageIndex: number; blockCount: number } => {
|
|
367
|
+
let validBlockCount = 0;
|
|
368
|
+
|
|
369
|
+
// Iterate messages from end to beginning
|
|
370
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
371
|
+
const message = messages[i];
|
|
372
|
+
|
|
373
|
+
// Iterate through all blocks of current message
|
|
374
|
+
for (const block of message.blocks) {
|
|
375
|
+
// Only count valid block types
|
|
376
|
+
if (
|
|
377
|
+
block.type === "text" ||
|
|
378
|
+
block.type === "image" ||
|
|
379
|
+
block.type === "tool"
|
|
380
|
+
) {
|
|
381
|
+
validBlockCount++;
|
|
382
|
+
|
|
383
|
+
// If target count reached, return current message index
|
|
384
|
+
if (validBlockCount >= targetCount) {
|
|
385
|
+
return { messageIndex: i, blockCount: validBlockCount };
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// If target count not reached, return index 0
|
|
392
|
+
return { messageIndex: 0, blockCount: validBlockCount };
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Get messages to be compressed and insertion position
|
|
397
|
+
* @param messages Message array
|
|
398
|
+
* @param keepLastCount Keep the last few valid blocks uncompressed
|
|
399
|
+
* @returns { messagesToCompress: Message[], insertIndex: number }
|
|
400
|
+
*/
|
|
401
|
+
export const getMessagesToCompress = (
|
|
402
|
+
messages: Message[],
|
|
403
|
+
keepLastCount: number = 7,
|
|
404
|
+
): { messagesToCompress: Message[]; insertIndex: number } => {
|
|
405
|
+
// Calculate message position to keep from end to beginning
|
|
406
|
+
const { messageIndex } = countValidBlocksFromEnd(messages, keepLastCount);
|
|
407
|
+
|
|
408
|
+
// Find the last message containing compression block
|
|
409
|
+
let lastCompressIndex = -1;
|
|
410
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
411
|
+
const hasCompressBlock = messages[i].blocks.some(
|
|
412
|
+
(block) => block.type === "compress",
|
|
413
|
+
);
|
|
414
|
+
if (hasCompressBlock) {
|
|
415
|
+
lastCompressIndex = i;
|
|
416
|
+
break;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Determine compression start position
|
|
421
|
+
// If compression block exists, start from compression block position (include compression block)
|
|
422
|
+
// If no compression block, start from beginning
|
|
423
|
+
const startIndex = lastCompressIndex >= 0 ? lastCompressIndex : 0;
|
|
424
|
+
|
|
425
|
+
// Messages to compress are all messages from start position to before calculated position
|
|
426
|
+
const messagesToCompress = messages.slice(startIndex, messageIndex);
|
|
427
|
+
|
|
428
|
+
// Change insertion position to negative number, indicating position from end
|
|
429
|
+
const insertIndex = messageIndex - messages.length;
|
|
430
|
+
|
|
431
|
+
return { messagesToCompress, insertIndex };
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
// Add command output block to message list
|
|
435
|
+
export const addCommandOutputMessage = ({
|
|
436
|
+
messages,
|
|
437
|
+
command,
|
|
438
|
+
}: AddCommandOutputParams): Message[] => {
|
|
439
|
+
const outputMessage: Message = {
|
|
440
|
+
role: "assistant",
|
|
441
|
+
blocks: [
|
|
442
|
+
{
|
|
443
|
+
type: "command_output",
|
|
444
|
+
command,
|
|
445
|
+
output: "",
|
|
446
|
+
isRunning: true,
|
|
447
|
+
exitCode: null,
|
|
448
|
+
},
|
|
449
|
+
],
|
|
450
|
+
};
|
|
451
|
+
|
|
452
|
+
return [...messages, outputMessage];
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
// Update output content of command output block
|
|
456
|
+
export const updateCommandOutputInMessage = ({
|
|
457
|
+
messages,
|
|
458
|
+
command,
|
|
459
|
+
output,
|
|
460
|
+
}: UpdateCommandOutputParams): Message[] => {
|
|
461
|
+
const newMessages = [...messages];
|
|
462
|
+
// Find the last assistant message with a command_output block for this command
|
|
463
|
+
for (let i = newMessages.length - 1; i >= 0; i--) {
|
|
464
|
+
const msg = newMessages[i];
|
|
465
|
+
if (msg.role === "assistant") {
|
|
466
|
+
const commandBlock = msg.blocks.find(
|
|
467
|
+
(block) =>
|
|
468
|
+
block.type === "command_output" &&
|
|
469
|
+
block.command === command &&
|
|
470
|
+
block.isRunning,
|
|
471
|
+
);
|
|
472
|
+
if (commandBlock && commandBlock.type === "command_output") {
|
|
473
|
+
commandBlock.output = output.trim();
|
|
474
|
+
break;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
return newMessages;
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
// Complete command execution, update exit status
|
|
482
|
+
export const completeCommandInMessage = ({
|
|
483
|
+
messages,
|
|
484
|
+
command,
|
|
485
|
+
exitCode,
|
|
486
|
+
}: CompleteCommandParams): Message[] => {
|
|
487
|
+
const newMessages = [...messages];
|
|
488
|
+
// Find the last assistant message with a command_output block for this command
|
|
489
|
+
for (let i = newMessages.length - 1; i >= 0; i--) {
|
|
490
|
+
const msg = newMessages[i];
|
|
491
|
+
if (msg.role === "assistant") {
|
|
492
|
+
const commandBlock = msg.blocks.find(
|
|
493
|
+
(block) =>
|
|
494
|
+
block.type === "command_output" &&
|
|
495
|
+
block.command === command &&
|
|
496
|
+
block.isRunning,
|
|
497
|
+
);
|
|
498
|
+
if (commandBlock && commandBlock.type === "command_output") {
|
|
499
|
+
commandBlock.isRunning = false;
|
|
500
|
+
commandBlock.exitCode = exitCode;
|
|
501
|
+
break;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
return newMessages;
|
|
506
|
+
};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { resolve } from "path";
|
|
2
|
+
import { homedir } from "os";
|
|
3
|
+
import { relative } from "path";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Handle paths, supporting ~ expansion
|
|
7
|
+
* @param filePath File path
|
|
8
|
+
* @param workdir Working directory
|
|
9
|
+
* @returns Resolved absolute path
|
|
10
|
+
*/
|
|
11
|
+
export function resolvePath(filePath: string, workdir: string): string {
|
|
12
|
+
// If the path starts with ~, replace it with the user's home directory
|
|
13
|
+
if (filePath.startsWith("~/")) {
|
|
14
|
+
return resolve(homedir(), filePath.slice(2));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// If the path starts with ~ but has no slash, it means it's the home directory
|
|
18
|
+
if (filePath === "~") {
|
|
19
|
+
return homedir();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// For other paths, resolve using the specified working directory
|
|
23
|
+
return resolve(workdir, filePath);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Binary file extension list
|
|
28
|
+
*/
|
|
29
|
+
export const binaryExtensions = [
|
|
30
|
+
// Image files
|
|
31
|
+
"png",
|
|
32
|
+
"jpg",
|
|
33
|
+
"jpeg",
|
|
34
|
+
"gif",
|
|
35
|
+
"bmp",
|
|
36
|
+
"ico",
|
|
37
|
+
"webp",
|
|
38
|
+
"svg",
|
|
39
|
+
"sketch",
|
|
40
|
+
// Audio files
|
|
41
|
+
"mp3",
|
|
42
|
+
"wav",
|
|
43
|
+
"ogg",
|
|
44
|
+
"aac",
|
|
45
|
+
// Video files
|
|
46
|
+
"mp4",
|
|
47
|
+
"webm",
|
|
48
|
+
"avi",
|
|
49
|
+
"mov",
|
|
50
|
+
// Document files
|
|
51
|
+
"pdf",
|
|
52
|
+
"doc",
|
|
53
|
+
"docx",
|
|
54
|
+
"xls",
|
|
55
|
+
"xlsx",
|
|
56
|
+
"ppt",
|
|
57
|
+
"pptx",
|
|
58
|
+
// Compressed files
|
|
59
|
+
"zip",
|
|
60
|
+
"rar",
|
|
61
|
+
"7z",
|
|
62
|
+
"tar",
|
|
63
|
+
"gz",
|
|
64
|
+
// Font files
|
|
65
|
+
"ttf",
|
|
66
|
+
"otf",
|
|
67
|
+
"woff",
|
|
68
|
+
"woff2",
|
|
69
|
+
"eot",
|
|
70
|
+
// Other binary files
|
|
71
|
+
"exe",
|
|
72
|
+
"dll",
|
|
73
|
+
"so",
|
|
74
|
+
"dylib",
|
|
75
|
+
"bin",
|
|
76
|
+
] as const;
|
|
77
|
+
/**
|
|
78
|
+
* Check if a file is a binary file
|
|
79
|
+
* @param filename File name
|
|
80
|
+
* @returns Whether it is a binary file
|
|
81
|
+
*/
|
|
82
|
+
export const isBinary = (filename: string): boolean => {
|
|
83
|
+
const parts = filename.split(".");
|
|
84
|
+
const ext = parts.length > 1 ? parts.pop()?.toLowerCase() || "" : "";
|
|
85
|
+
return binaryExtensions.includes(ext as (typeof binaryExtensions)[number]);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Get relative path for display, use relative path if shorter and not in parent directory
|
|
90
|
+
* @param filePath Absolute path
|
|
91
|
+
* @param workdir Working directory
|
|
92
|
+
* @returns Path for display (relative or absolute)
|
|
93
|
+
*/
|
|
94
|
+
export function getDisplayPath(filePath: string, workdir: string): string {
|
|
95
|
+
if (!filePath) {
|
|
96
|
+
return filePath;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
try {
|
|
100
|
+
const relativePath = relative(workdir, filePath);
|
|
101
|
+
|
|
102
|
+
// If the relative path is empty (i.e., the paths are the same), return "."
|
|
103
|
+
if (relativePath === "") {
|
|
104
|
+
return ".";
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// If the relative path is shorter than the absolute path and does not start with .. (not in parent directory), use the relative path
|
|
108
|
+
if (
|
|
109
|
+
relativePath.length < filePath.length &&
|
|
110
|
+
!relativePath.startsWith("..")
|
|
111
|
+
) {
|
|
112
|
+
return relativePath;
|
|
113
|
+
}
|
|
114
|
+
} catch {
|
|
115
|
+
// If calculating the relative path fails, keep the original path
|
|
116
|
+
}
|
|
117
|
+
return filePath;
|
|
118
|
+
}
|