evoltagent 1.1.1 → 1.1.2
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/index.d.ts +1052 -0
- package/dist/index.js +4829 -0
- package/dist/index.js.map +1 -0
- package/package.json +4 -2
package/dist/index.js
ADDED
|
@@ -0,0 +1,4829 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
4
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
5
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
6
|
+
if (decorator = decorators[i])
|
|
7
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
8
|
+
if (kind && result) __defProp(target, key, result);
|
|
9
|
+
return result;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// src/utils/connections.ts
|
|
13
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
14
|
+
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
15
|
+
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
16
|
+
|
|
17
|
+
// src/schemas/toolCall.ts
|
|
18
|
+
import { randomUUID } from "crypto";
|
|
19
|
+
var Toolcall = class {
|
|
20
|
+
name;
|
|
21
|
+
input;
|
|
22
|
+
isExtractedSuccess;
|
|
23
|
+
failedExtractedReason;
|
|
24
|
+
executedState;
|
|
25
|
+
executedContent;
|
|
26
|
+
toolCallId;
|
|
27
|
+
type;
|
|
28
|
+
rawContentFromLlm;
|
|
29
|
+
constructor(config2) {
|
|
30
|
+
this.name = config2.name;
|
|
31
|
+
this.input = config2.input || {};
|
|
32
|
+
this.isExtractedSuccess = config2.isExtractedSuccess ?? true;
|
|
33
|
+
this.failedExtractedReason = config2.failedExtractedReason;
|
|
34
|
+
this.executedState = config2.executedState || "pending";
|
|
35
|
+
this.executedContent = config2.executedContent;
|
|
36
|
+
this.toolCallId = config2.toolCallId || randomUUID();
|
|
37
|
+
this.type = config2.type || "system";
|
|
38
|
+
this.rawContentFromLlm = config2.rawContentFromLlm;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Toolcall extraction result description
|
|
42
|
+
*/
|
|
43
|
+
extractedResult() {
|
|
44
|
+
if (this.type === "TaskCompletion") {
|
|
45
|
+
return this.rawContentFromLlm || "";
|
|
46
|
+
}
|
|
47
|
+
if (this.type === "user") {
|
|
48
|
+
if (!this.isExtractedSuccess) {
|
|
49
|
+
return [
|
|
50
|
+
{
|
|
51
|
+
role: "assistant",
|
|
52
|
+
tool_calls: [
|
|
53
|
+
{
|
|
54
|
+
id: `user_tool_${this.toolCallId}`,
|
|
55
|
+
type: "function",
|
|
56
|
+
function: {
|
|
57
|
+
name: this.name,
|
|
58
|
+
arguments: this.rawContentFromLlm
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
role: "tool",
|
|
65
|
+
tool_call_id: `user_tool_${this.toolCallId}`,
|
|
66
|
+
name: this.name,
|
|
67
|
+
content: this.failedExtractedReason || "Unknown reason"
|
|
68
|
+
}
|
|
69
|
+
];
|
|
70
|
+
}
|
|
71
|
+
logger.debug(`User Toolcall: ${this.name}(${JSON.stringify(this.input)})`);
|
|
72
|
+
return {
|
|
73
|
+
role: "assistant",
|
|
74
|
+
tool_calls: [
|
|
75
|
+
{
|
|
76
|
+
id: `user_tool_${this.toolCallId}`,
|
|
77
|
+
type: "function",
|
|
78
|
+
function: {
|
|
79
|
+
name: this.name,
|
|
80
|
+
arguments: JSON.stringify(this.input)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
if (!this.isExtractedSuccess) {
|
|
87
|
+
return `Toolcall ${this.name} failed to extract: ${this.failedExtractedReason || "Unknown reason"}`;
|
|
88
|
+
}
|
|
89
|
+
if (this.name) {
|
|
90
|
+
const inputStr = Object.entries(this.input).map(([k, v]) => `<${k}>${v}</${k}>`).join("");
|
|
91
|
+
return `<${this.name}>${inputStr}</${this.name}>`;
|
|
92
|
+
}
|
|
93
|
+
return `Invalid Toolcall: name ${this.name}, arguments ${JSON.stringify(this.input)}.`;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Toolcall execution result: Feedback to LLM
|
|
97
|
+
*/
|
|
98
|
+
executedResult() {
|
|
99
|
+
if (this.type === "TaskCompletion") {
|
|
100
|
+
return "The task has been completed.";
|
|
101
|
+
}
|
|
102
|
+
if (this.type === "user") {
|
|
103
|
+
if (!this.isExtractedSuccess) {
|
|
104
|
+
return "";
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
role: "tool",
|
|
108
|
+
tool_call_id: this.toolCallId,
|
|
109
|
+
// Use the original tool_call_id without prefix
|
|
110
|
+
name: this.name,
|
|
111
|
+
content: (this.executedContent || "No execution result found.").trim()
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
if (this.name.startsWith("ThinkTool.execute") || this.name.startsWith("TodoListTool.write")) {
|
|
115
|
+
return (this.executedContent || "No thinking result found.").trim();
|
|
116
|
+
}
|
|
117
|
+
let toolcallDescription = "";
|
|
118
|
+
let observation = "";
|
|
119
|
+
if (this.name.startsWith("FileEditor.write")) {
|
|
120
|
+
toolcallDescription = `FileEditor.write(${this.input.path})`;
|
|
121
|
+
} else {
|
|
122
|
+
toolcallDescription = `${this.name}(${JSON.stringify(this.input)})`;
|
|
123
|
+
}
|
|
124
|
+
observation += `Executed content: ${(this.executedContent || "None").trim()}
|
|
125
|
+
`;
|
|
126
|
+
return `Toolcall: ${toolcallDescription}
|
|
127
|
+
Observation: ${observation}`;
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
// src/utils/toolUtil.ts
|
|
132
|
+
function isWriteJsonFile(argumentsTxt, toolName) {
|
|
133
|
+
if (!toolName.startsWith("FileEditor.") && !toolName.startsWith("ApiTool.")) {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
const pattern = /\.json\s*<\/(path|filePath|apiFilePath)>/;
|
|
137
|
+
return pattern.test(argumentsTxt);
|
|
138
|
+
}
|
|
139
|
+
function unescapeHtmlEntities(str) {
|
|
140
|
+
let result = str;
|
|
141
|
+
result = result.replace(/&#(\d+);/g, (_, dec) => String.fromCharCode(parseInt(dec, 10)));
|
|
142
|
+
result = result.replace(/&#x([0-9a-fA-F]+);/g, (_, hex) => String.fromCharCode(parseInt(hex, 16)));
|
|
143
|
+
result = result.replace(/&(?:quot|amp|lt|gt|apos);/g, (match) => {
|
|
144
|
+
const entities = {
|
|
145
|
+
""": '"',
|
|
146
|
+
"&": "&",
|
|
147
|
+
"<": "<",
|
|
148
|
+
">": ">",
|
|
149
|
+
"'": "'"
|
|
150
|
+
};
|
|
151
|
+
return entities[match] || match;
|
|
152
|
+
});
|
|
153
|
+
return result;
|
|
154
|
+
}
|
|
155
|
+
function convertStrToObject(txt, toolName, argNames) {
|
|
156
|
+
const result = {};
|
|
157
|
+
const startTag = `<${toolName}>`;
|
|
158
|
+
const endTag = `</${toolName}>`;
|
|
159
|
+
const toolPattern = new RegExp(`${escapeRegExp(startTag)}(.*?)${escapeRegExp(endTag)}`, "s");
|
|
160
|
+
const toolMatch = toolPattern.exec(txt);
|
|
161
|
+
if (!toolMatch) {
|
|
162
|
+
return result;
|
|
163
|
+
}
|
|
164
|
+
const innerContent = toolMatch[1];
|
|
165
|
+
for (const argName of argNames) {
|
|
166
|
+
const argStartTag = `<${argName}>`;
|
|
167
|
+
const argEndTag = `</${argName}>`;
|
|
168
|
+
const argPattern = new RegExp(`${escapeRegExp(argStartTag)}(.*?)${escapeRegExp(argEndTag)}`, "s");
|
|
169
|
+
const argMatch = argPattern.exec(innerContent);
|
|
170
|
+
if (argMatch) {
|
|
171
|
+
let value = argMatch[1].trim();
|
|
172
|
+
value = unescapeHtmlEntities(value);
|
|
173
|
+
if (isWriteJsonFile(txt, toolName)) {
|
|
174
|
+
result[argName] = value;
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
try {
|
|
178
|
+
const parsedValue = JSON.parse(value);
|
|
179
|
+
result[argName] = parsedValue;
|
|
180
|
+
} catch {
|
|
181
|
+
result[argName] = value;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return result;
|
|
186
|
+
}
|
|
187
|
+
function extractToolcallsFromStr(txt, toolStore) {
|
|
188
|
+
const matches = [];
|
|
189
|
+
for (const toolName of Object.keys(toolStore)) {
|
|
190
|
+
const pattern = new RegExp(`<${escapeRegExp(toolName)}>(.*?)</${escapeRegExp(toolName)}>`, "gs");
|
|
191
|
+
let match;
|
|
192
|
+
while ((match = pattern.exec(txt)) !== null) {
|
|
193
|
+
const argumentsTxt = match[1].trim();
|
|
194
|
+
if (!isWriteJsonFile(argumentsTxt, toolName) && argumentsTxt.startsWith("{") && argumentsTxt.endsWith("}")) {
|
|
195
|
+
matches.push([
|
|
196
|
+
match.index,
|
|
197
|
+
new Toolcall({
|
|
198
|
+
name: toolName,
|
|
199
|
+
input: {},
|
|
200
|
+
isExtractedSuccess: false,
|
|
201
|
+
type: "system",
|
|
202
|
+
rawContentFromLlm: txt
|
|
203
|
+
})
|
|
204
|
+
]);
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
const rawInput = `<${toolName}>${argumentsTxt}</${toolName}>`;
|
|
208
|
+
let pythonObjectInput = {};
|
|
209
|
+
if (rawInput) {
|
|
210
|
+
const argNames = toolStore[toolName].argNames || [];
|
|
211
|
+
pythonObjectInput = convertStrToObject(rawInput, toolName, argNames);
|
|
212
|
+
}
|
|
213
|
+
matches.push([
|
|
214
|
+
match.index,
|
|
215
|
+
new Toolcall({
|
|
216
|
+
name: toolName,
|
|
217
|
+
input: pythonObjectInput,
|
|
218
|
+
type: "system"
|
|
219
|
+
})
|
|
220
|
+
]);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
matches.sort((a, b) => a[0] - b[0]);
|
|
224
|
+
return matches.map(([, tc]) => tc);
|
|
225
|
+
}
|
|
226
|
+
async function executeSingleTool(toolcall, toolStore) {
|
|
227
|
+
if (!Array.isArray(toolStore)) {
|
|
228
|
+
toolStore = [toolStore];
|
|
229
|
+
}
|
|
230
|
+
if (!toolcall.isExtractedSuccess) {
|
|
231
|
+
return toolcall;
|
|
232
|
+
}
|
|
233
|
+
try {
|
|
234
|
+
if (toolcall.name && !toolcall.name.startsWith("FileEditor.") && !toolcall.name.startsWith("ThinkTool.")) {
|
|
235
|
+
logger.info(`Executing Tool ${toolcall.name} with arguments: ${JSON.stringify(toolcall.input)}`);
|
|
236
|
+
}
|
|
237
|
+
for (const ts of toolStore) {
|
|
238
|
+
let toolCall;
|
|
239
|
+
let argNames = [];
|
|
240
|
+
if (typeof ts.hasTool === "function" && ts.hasTool(toolcall.name)) {
|
|
241
|
+
const toolDesc = ts.getTool(toolcall.name);
|
|
242
|
+
toolCall = toolDesc ? toolDesc.execute : void 0;
|
|
243
|
+
argNames = toolDesc ? toolDesc.argNames : [];
|
|
244
|
+
} else if (toolcall.name in ts) {
|
|
245
|
+
toolCall = ts[toolcall.name].execute;
|
|
246
|
+
argNames = ts[toolcall.name].argNames || [];
|
|
247
|
+
}
|
|
248
|
+
if (toolCall) {
|
|
249
|
+
let result;
|
|
250
|
+
if (typeof toolCall === "function") {
|
|
251
|
+
if (argNames && argNames.length > 0) {
|
|
252
|
+
const args = argNames.map((name) => toolcall.input[name]);
|
|
253
|
+
result = await toolCall(...args);
|
|
254
|
+
} else {
|
|
255
|
+
result = await toolCall(toolcall.input);
|
|
256
|
+
}
|
|
257
|
+
} else {
|
|
258
|
+
logger.error(`Tool ${toolcall.name} is not callable`);
|
|
259
|
+
toolcall.executedContent = `Tool '${toolcall.name}' is not callable`;
|
|
260
|
+
toolcall.executedState = "failed";
|
|
261
|
+
return toolcall;
|
|
262
|
+
}
|
|
263
|
+
toolcall.executedContent = String(result);
|
|
264
|
+
toolcall.executedState = "success";
|
|
265
|
+
return toolcall;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
toolcall.executedContent = `Tool '${toolcall.name}' not found in tool_store. Please check the tool name.`;
|
|
269
|
+
toolcall.executedState = "failed";
|
|
270
|
+
return toolcall;
|
|
271
|
+
} catch (error) {
|
|
272
|
+
const errorMsg = `Error executing tool: ${error}`;
|
|
273
|
+
toolcall.executedContent = errorMsg;
|
|
274
|
+
toolcall.executedState = "failed";
|
|
275
|
+
return toolcall;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
function escapeRegExp(string) {
|
|
279
|
+
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// src/utils/logger.ts
|
|
283
|
+
import * as winston from "winston";
|
|
284
|
+
import * as fs from "fs";
|
|
285
|
+
import * as dotenv from "dotenv";
|
|
286
|
+
dotenv.config();
|
|
287
|
+
var getDisableLog = () => {
|
|
288
|
+
return process.env.DISABLE_LOG == "true" || !("DISABLE_LOG" in process.env);
|
|
289
|
+
};
|
|
290
|
+
var winstonLevels = {
|
|
291
|
+
error: 0,
|
|
292
|
+
warn: 1,
|
|
293
|
+
info: 2,
|
|
294
|
+
http: 3,
|
|
295
|
+
verbose: 4,
|
|
296
|
+
debug: 5,
|
|
297
|
+
silly: 6
|
|
298
|
+
};
|
|
299
|
+
var getLogLevel = () => {
|
|
300
|
+
const envLevel = process.env.LOG_LEVEL?.toLowerCase() || "error";
|
|
301
|
+
const levelMapping = {
|
|
302
|
+
fatal: "error",
|
|
303
|
+
trace: "debug"
|
|
304
|
+
};
|
|
305
|
+
return levelMapping[envLevel] || envLevel;
|
|
306
|
+
};
|
|
307
|
+
var loguruFormat = winston.format.combine(
|
|
308
|
+
winston.format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }),
|
|
309
|
+
winston.format.errors({ stack: true }),
|
|
310
|
+
winston.format.printf(({ timestamp, level, message, stack, ...meta }) => {
|
|
311
|
+
const formattedLevel = level.toUpperCase().padEnd(7);
|
|
312
|
+
const caller = meta.caller || meta[0]?.caller;
|
|
313
|
+
const location = caller ? `${String(caller).padEnd(35)} | ` : "";
|
|
314
|
+
const msg = stack || message;
|
|
315
|
+
return `${timestamp} | ${formattedLevel} | ${location}${msg}`;
|
|
316
|
+
})
|
|
317
|
+
);
|
|
318
|
+
var loguruFormatColored = winston.format.combine(
|
|
319
|
+
winston.format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }),
|
|
320
|
+
winston.format.errors({ stack: true }),
|
|
321
|
+
winston.format.printf(({ timestamp, level, message, stack, ...meta }) => {
|
|
322
|
+
const levelUpper = level.toUpperCase().padEnd(7);
|
|
323
|
+
let coloredLevel = levelUpper;
|
|
324
|
+
if (level === "error") {
|
|
325
|
+
coloredLevel = `\x1B[31m${levelUpper}\x1B[39m`;
|
|
326
|
+
} else if (level === "warn") {
|
|
327
|
+
coloredLevel = `\x1B[33m${levelUpper}\x1B[39m`;
|
|
328
|
+
} else if (level === "info") {
|
|
329
|
+
coloredLevel = `\x1B[36m${levelUpper}\x1B[39m`;
|
|
330
|
+
} else if (level === "debug") {
|
|
331
|
+
coloredLevel = `\x1B[34m${levelUpper}\x1B[39m`;
|
|
332
|
+
}
|
|
333
|
+
const caller = meta.caller || meta[0]?.caller;
|
|
334
|
+
const location = caller ? `\x1B[90m${String(caller).padEnd(35)}\x1B[39m | ` : "";
|
|
335
|
+
const msg = stack || message;
|
|
336
|
+
return `${timestamp} | ${coloredLevel} | ${location}${msg}`;
|
|
337
|
+
})
|
|
338
|
+
);
|
|
339
|
+
var createLoggerTransports = () => {
|
|
340
|
+
if (getDisableLog()) {
|
|
341
|
+
return [];
|
|
342
|
+
}
|
|
343
|
+
const output = process.env.LOG_OUTPUT;
|
|
344
|
+
if (!output || output === "console") {
|
|
345
|
+
return [
|
|
346
|
+
new winston.transports.Console({
|
|
347
|
+
level: getLogLevel(),
|
|
348
|
+
format: loguruFormatColored,
|
|
349
|
+
stderrLevels: ["error"]
|
|
350
|
+
// Only error goes to stderr
|
|
351
|
+
})
|
|
352
|
+
];
|
|
353
|
+
} else if (output === "stdout") {
|
|
354
|
+
return [
|
|
355
|
+
new winston.transports.Stream({
|
|
356
|
+
stream: process.stdout,
|
|
357
|
+
format: loguruFormat
|
|
358
|
+
})
|
|
359
|
+
];
|
|
360
|
+
} else if (output === "stderr") {
|
|
361
|
+
return [
|
|
362
|
+
new winston.transports.Stream({
|
|
363
|
+
stream: process.stderr,
|
|
364
|
+
format: loguruFormat
|
|
365
|
+
})
|
|
366
|
+
];
|
|
367
|
+
} else {
|
|
368
|
+
return [
|
|
369
|
+
new winston.transports.Console({
|
|
370
|
+
level: getLogLevel(),
|
|
371
|
+
format: loguruFormatColored,
|
|
372
|
+
stderrLevels: ["error"]
|
|
373
|
+
// Only error goes to stderr
|
|
374
|
+
}),
|
|
375
|
+
new winston.transports.File({
|
|
376
|
+
filename: output,
|
|
377
|
+
format: loguruFormat
|
|
378
|
+
})
|
|
379
|
+
];
|
|
380
|
+
}
|
|
381
|
+
};
|
|
382
|
+
var logger2 = winston.createLogger({
|
|
383
|
+
levels: winstonLevels,
|
|
384
|
+
level: getLogLevel(),
|
|
385
|
+
transports: createLoggerTransports()
|
|
386
|
+
});
|
|
387
|
+
winston.addColors({
|
|
388
|
+
error: "red",
|
|
389
|
+
warn: "yellow",
|
|
390
|
+
info: "cyan",
|
|
391
|
+
http: "green",
|
|
392
|
+
verbose: "blue",
|
|
393
|
+
debug: "blue",
|
|
394
|
+
silly: "magenta"
|
|
395
|
+
});
|
|
396
|
+
function captureCallerInfo() {
|
|
397
|
+
const stack = new Error().stack;
|
|
398
|
+
if (!stack) return "";
|
|
399
|
+
const lines = stack.split("\n");
|
|
400
|
+
for (let i = 3; i < lines.length; i++) {
|
|
401
|
+
const line = lines[i];
|
|
402
|
+
const match = line.match(/\(([^)]+):(\d+):\d+\)/) || line.match(/at\s+([^()\s]+):(\d+):\d+/);
|
|
403
|
+
if (match && match[1]) {
|
|
404
|
+
const fullPath = match[1];
|
|
405
|
+
const lineNum = match[2];
|
|
406
|
+
if (fullPath.includes("node_modules") || fullPath.endsWith("logger.ts") || fullPath.endsWith("logger.js")) {
|
|
407
|
+
continue;
|
|
408
|
+
}
|
|
409
|
+
const workspaceRoot = process.cwd();
|
|
410
|
+
let displayPath = fullPath;
|
|
411
|
+
if (fullPath.startsWith(workspaceRoot)) {
|
|
412
|
+
displayPath = fullPath.substring(workspaceRoot.length + 1);
|
|
413
|
+
} else {
|
|
414
|
+
const parts = fullPath.split(/[/\\]/);
|
|
415
|
+
displayPath = parts[parts.length - 1];
|
|
416
|
+
}
|
|
417
|
+
return `${displayPath}:${lineNum}`;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
return "";
|
|
421
|
+
}
|
|
422
|
+
var dummyLogger = {
|
|
423
|
+
error: () => {
|
|
424
|
+
},
|
|
425
|
+
warn: () => {
|
|
426
|
+
},
|
|
427
|
+
info: () => {
|
|
428
|
+
},
|
|
429
|
+
debug: () => {
|
|
430
|
+
},
|
|
431
|
+
http: () => {
|
|
432
|
+
},
|
|
433
|
+
verbose: () => {
|
|
434
|
+
},
|
|
435
|
+
silly: () => {
|
|
436
|
+
}
|
|
437
|
+
};
|
|
438
|
+
var enhancedLogger = {
|
|
439
|
+
error: (message, ...meta) => {
|
|
440
|
+
const caller = captureCallerInfo();
|
|
441
|
+
logger2.error(message, { ...meta, caller });
|
|
442
|
+
},
|
|
443
|
+
warn: (message, ...meta) => {
|
|
444
|
+
const caller = captureCallerInfo();
|
|
445
|
+
logger2.warn(message, { ...meta, caller });
|
|
446
|
+
},
|
|
447
|
+
info: (message, ...meta) => {
|
|
448
|
+
const caller = captureCallerInfo();
|
|
449
|
+
logger2.info(message, { ...meta, caller });
|
|
450
|
+
},
|
|
451
|
+
debug: (message, ...meta) => {
|
|
452
|
+
const caller = captureCallerInfo();
|
|
453
|
+
logger2.debug(message, { ...meta, caller });
|
|
454
|
+
},
|
|
455
|
+
http: (message, ...meta) => {
|
|
456
|
+
const caller = captureCallerInfo();
|
|
457
|
+
logger2.http(message, { ...meta, caller });
|
|
458
|
+
},
|
|
459
|
+
verbose: (message, ...meta) => {
|
|
460
|
+
const caller = captureCallerInfo();
|
|
461
|
+
logger2.verbose(message, { ...meta, caller });
|
|
462
|
+
},
|
|
463
|
+
silly: (message, ...meta) => {
|
|
464
|
+
const caller = captureCallerInfo();
|
|
465
|
+
logger2.silly(message, { ...meta, caller });
|
|
466
|
+
}
|
|
467
|
+
};
|
|
468
|
+
var streamLogger = {
|
|
469
|
+
/**
|
|
470
|
+
* Write content directly to stdout without formatting
|
|
471
|
+
* Used for streaming output where content comes in chunks
|
|
472
|
+
*/
|
|
473
|
+
info: (message) => {
|
|
474
|
+
if (!getDisableLog()) {
|
|
475
|
+
process.stdout.write(message);
|
|
476
|
+
if (process.env.LOG_OUTPUT) {
|
|
477
|
+
fs.appendFileSync(process.env.LOG_OUTPUT, message);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
},
|
|
481
|
+
/**
|
|
482
|
+
* Write error content to stderr
|
|
483
|
+
*/
|
|
484
|
+
error: (message) => {
|
|
485
|
+
if (!getDisableLog()) {
|
|
486
|
+
process.stderr.write(message);
|
|
487
|
+
if (process.env.LOG_OUTPUT) {
|
|
488
|
+
fs.appendFileSync(process.env.LOG_OUTPUT, message);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
},
|
|
492
|
+
/**
|
|
493
|
+
* Write content with newline
|
|
494
|
+
*/
|
|
495
|
+
log: (message) => {
|
|
496
|
+
if (!getDisableLog()) {
|
|
497
|
+
process.stdout.write(message + "\n");
|
|
498
|
+
if (process.env.LOG_OUTPUT) {
|
|
499
|
+
fs.appendFileSync(process.env.LOG_OUTPUT, message + "\n");
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
},
|
|
503
|
+
debug: (message) => {
|
|
504
|
+
if (!getDisableLog()) {
|
|
505
|
+
process.stdout.write(message);
|
|
506
|
+
if (process.env.LOG_OUTPUT) {
|
|
507
|
+
fs.appendFileSync(process.env.LOG_OUTPUT, message);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
};
|
|
512
|
+
var logger_default = { enhancedLogger, dummyLogger, streamLogger, getDisableLog };
|
|
513
|
+
|
|
514
|
+
// src/utils/index.ts
|
|
515
|
+
var logger = logger_default.getDisableLog() ? logger_default.dummyLogger : logger_default.enhancedLogger;
|
|
516
|
+
var streamLogger2 = logger_default.getDisableLog() ? logger_default.dummyLogger : logger_default.streamLogger;
|
|
517
|
+
if (process.env.LOG_LEVEL) {
|
|
518
|
+
logger.info(`LOG_LEVEL: ${process.env.LOG_LEVEL}`);
|
|
519
|
+
} else {
|
|
520
|
+
logger.info("LOG_LEVEL is not set");
|
|
521
|
+
}
|
|
522
|
+
if (process.env.LOG_OUTPUT) {
|
|
523
|
+
logger.info(`LOG_OUTPUT: ${process.env.LOG_OUTPUT}`);
|
|
524
|
+
}
|
|
525
|
+
if (process.env.ENABLE_LOG === "true") {
|
|
526
|
+
logger.info("ENABLE_LOG is set to true");
|
|
527
|
+
} else {
|
|
528
|
+
logger.info("ENABLE_LOG is not set");
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// src/utils/connections.ts
|
|
532
|
+
var AsyncExitStack = class {
|
|
533
|
+
stack = [];
|
|
534
|
+
/**
|
|
535
|
+
* Enter a context manager and add it to the stack
|
|
536
|
+
*/
|
|
537
|
+
async enterContext(context) {
|
|
538
|
+
const result = await context.enter();
|
|
539
|
+
this.stack.push(context);
|
|
540
|
+
return result;
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* Close all context managers in reverse order
|
|
544
|
+
*/
|
|
545
|
+
async close() {
|
|
546
|
+
while (this.stack.length > 0) {
|
|
547
|
+
const context = this.stack.pop();
|
|
548
|
+
if (context) {
|
|
549
|
+
try {
|
|
550
|
+
await context.exit(null, null, null);
|
|
551
|
+
} catch (error) {
|
|
552
|
+
logger.error("Error closing context:", error);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
/**
|
|
558
|
+
* Alias for close to match Python's close method if needed,
|
|
559
|
+
* though Python uses __aexit__ usually.
|
|
560
|
+
*/
|
|
561
|
+
async aclose() {
|
|
562
|
+
await this.close();
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Push a callback to be called when closing
|
|
566
|
+
*/
|
|
567
|
+
push(callback) {
|
|
568
|
+
const contextManager = {
|
|
569
|
+
enter: async () => {
|
|
570
|
+
},
|
|
571
|
+
exit: async (exc_type, exc_val, exc_tb) => {
|
|
572
|
+
await callback();
|
|
573
|
+
}
|
|
574
|
+
};
|
|
575
|
+
this.stack.push(contextManager);
|
|
576
|
+
}
|
|
577
|
+
};
|
|
578
|
+
var MCPConnection = class {
|
|
579
|
+
session = null;
|
|
580
|
+
transport = null;
|
|
581
|
+
async enter() {
|
|
582
|
+
this.transport = await this.createTransport();
|
|
583
|
+
this.session = new Client(
|
|
584
|
+
{
|
|
585
|
+
name: "evoltagent-client",
|
|
586
|
+
version: "1.0.0"
|
|
587
|
+
},
|
|
588
|
+
{
|
|
589
|
+
capabilities: {
|
|
590
|
+
// Client capabilities
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
);
|
|
594
|
+
await this.session.connect(this.transport);
|
|
595
|
+
return this;
|
|
596
|
+
}
|
|
597
|
+
async exit(exc_type, exc_val, exc_tb) {
|
|
598
|
+
try {
|
|
599
|
+
if (this.session) {
|
|
600
|
+
await this.session.close();
|
|
601
|
+
}
|
|
602
|
+
} catch (e) {
|
|
603
|
+
logger.error(`Error during cleanup: ${e}`);
|
|
604
|
+
} finally {
|
|
605
|
+
this.session = null;
|
|
606
|
+
this.transport = null;
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
async listTools() {
|
|
610
|
+
if (!this.session) throw new Error("Session not initialized");
|
|
611
|
+
const result = await this.session.listTools();
|
|
612
|
+
return result.tools;
|
|
613
|
+
}
|
|
614
|
+
async callTool(toolName, argumentsDict) {
|
|
615
|
+
if (!this.session) throw new Error("Session not initialized");
|
|
616
|
+
return await this.session.callTool({
|
|
617
|
+
name: toolName,
|
|
618
|
+
arguments: argumentsDict
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
};
|
|
622
|
+
var MCPConnectionStdio = class extends MCPConnection {
|
|
623
|
+
command;
|
|
624
|
+
args;
|
|
625
|
+
env;
|
|
626
|
+
constructor(command, args = [], env) {
|
|
627
|
+
super();
|
|
628
|
+
this.command = command;
|
|
629
|
+
this.args = args;
|
|
630
|
+
this.env = env;
|
|
631
|
+
}
|
|
632
|
+
async createTransport() {
|
|
633
|
+
return new StdioClientTransport({
|
|
634
|
+
command: this.command,
|
|
635
|
+
args: this.args,
|
|
636
|
+
env: this.env
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
};
|
|
640
|
+
var MCPConnectionSSE = class extends MCPConnection {
|
|
641
|
+
url;
|
|
642
|
+
headers;
|
|
643
|
+
constructor(url, headers) {
|
|
644
|
+
super();
|
|
645
|
+
this.url = url;
|
|
646
|
+
this.headers = headers;
|
|
647
|
+
}
|
|
648
|
+
async createTransport() {
|
|
649
|
+
return new SSEClientTransport(new URL(this.url), {
|
|
650
|
+
eventSourceInit: {
|
|
651
|
+
// headers: this.headers, // TypeScript doesn't like headers in EventSourceInit
|
|
652
|
+
}
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
};
|
|
656
|
+
function createMcpConnection(config2) {
|
|
657
|
+
const connType = (config2.type || "stdio").toLowerCase();
|
|
658
|
+
if (connType === "stdio") {
|
|
659
|
+
if (!config2.command) {
|
|
660
|
+
throw new Error("Command is required for STDIO connections");
|
|
661
|
+
}
|
|
662
|
+
return new MCPConnectionStdio(config2.command, config2.args || [], config2.env);
|
|
663
|
+
} else if (connType === "sse") {
|
|
664
|
+
if (!config2.url) {
|
|
665
|
+
throw new Error("URL is required for SSE connections");
|
|
666
|
+
}
|
|
667
|
+
return new MCPConnectionSSE(config2.url, config2.headers);
|
|
668
|
+
} else {
|
|
669
|
+
throw new Error(`Unsupported connection type: ${connType}`);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
// src/configs/configLoader.ts
|
|
674
|
+
import * as fs2 from "fs";
|
|
675
|
+
import * as path from "path";
|
|
676
|
+
import * as yaml from "yaml";
|
|
677
|
+
function getConfigPath() {
|
|
678
|
+
return path.join(process.env.HOME || "~", ".evolt", "config.yaml");
|
|
679
|
+
}
|
|
680
|
+
function loadModelConfig(modelName) {
|
|
681
|
+
const configPath = process.env.EVOLT_CONFIG_PATH || getConfigPath();
|
|
682
|
+
let configData = {};
|
|
683
|
+
try {
|
|
684
|
+
if (fs2.existsSync(configPath)) {
|
|
685
|
+
const fileContent = fs2.readFileSync(configPath, "utf8");
|
|
686
|
+
configData = yaml.parse(fileContent);
|
|
687
|
+
} else {
|
|
688
|
+
logger.warn(`Config file not found at ${configPath}, using defaults`);
|
|
689
|
+
configData = getDefaultConfig();
|
|
690
|
+
}
|
|
691
|
+
} catch (error) {
|
|
692
|
+
logger.warn(`Failed to load config from ${configPath}, using defaults:`, error);
|
|
693
|
+
configData = getDefaultConfig();
|
|
694
|
+
}
|
|
695
|
+
const models = configData.models || {};
|
|
696
|
+
const defaultModel = modelName || configData.defaultModel || "deepseek";
|
|
697
|
+
const modelConfig = models[defaultModel] || models.deepseek || getDefaultModelConfig();
|
|
698
|
+
const params = modelConfig.params || {};
|
|
699
|
+
return {
|
|
700
|
+
provider: modelConfig.provider || "openai",
|
|
701
|
+
model: modelConfig.model || "gpt-3.5-turbo",
|
|
702
|
+
contextWindowTokens: modelConfig.contextWindowTokens || 4096,
|
|
703
|
+
maxOutputTokens: params.maxCompletionTokens || params.maxTokens || modelConfig.maxOutputTokens || 1024,
|
|
704
|
+
temperature: params.temperature || modelConfig.temperature || 0.7,
|
|
705
|
+
topP: modelConfig.topP || 0.9,
|
|
706
|
+
apiKey: modelConfig.apiKey,
|
|
707
|
+
baseUrl: modelConfig.baseUrl,
|
|
708
|
+
...modelConfig
|
|
709
|
+
};
|
|
710
|
+
}
|
|
711
|
+
function getDefaultConfig() {
|
|
712
|
+
return {
|
|
713
|
+
defaultModel: "deepseek",
|
|
714
|
+
models: {
|
|
715
|
+
deepseek: getDefaultModelConfig(),
|
|
716
|
+
openai: {
|
|
717
|
+
provider: "openai",
|
|
718
|
+
model: "gpt-3.5-turbo",
|
|
719
|
+
contextWindowTokens: 4096,
|
|
720
|
+
maxOutputTokens: 1024,
|
|
721
|
+
temperature: 0.7,
|
|
722
|
+
topP: 0.9
|
|
723
|
+
},
|
|
724
|
+
anthropic: {
|
|
725
|
+
provider: "anthropic",
|
|
726
|
+
model: "claude-3-sonnet-20240229",
|
|
727
|
+
contextWindowTokens: 2e5,
|
|
728
|
+
maxOutputTokens: 4096,
|
|
729
|
+
temperature: 0.7,
|
|
730
|
+
topP: 0.9
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
function getDefaultModelConfig() {
|
|
736
|
+
return {
|
|
737
|
+
provider: "deepseek",
|
|
738
|
+
model: "deepseek-chat",
|
|
739
|
+
contextWindowTokens: 32768,
|
|
740
|
+
maxOutputTokens: 4096,
|
|
741
|
+
temperature: 0.7,
|
|
742
|
+
topP: 0.9,
|
|
743
|
+
apiKey: process.env.DEEPSEEK_API_KEY,
|
|
744
|
+
baseUrl: process.env.DEEPSEEK_BASE_URL || "https://api.deepseek.com/v1"
|
|
745
|
+
};
|
|
746
|
+
}
|
|
747
|
+
function convertTypeNameToOpenai(typeName) {
|
|
748
|
+
const typeMap = {
|
|
749
|
+
str: "string",
|
|
750
|
+
string: "string",
|
|
751
|
+
int: "integer",
|
|
752
|
+
integer: "integer",
|
|
753
|
+
float: "number",
|
|
754
|
+
number: "number",
|
|
755
|
+
bool: "boolean",
|
|
756
|
+
boolean: "boolean",
|
|
757
|
+
list: "array",
|
|
758
|
+
array: "array",
|
|
759
|
+
dict: "object",
|
|
760
|
+
object: "object"
|
|
761
|
+
};
|
|
762
|
+
return typeMap[typeName.toLowerCase()] || "string";
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// src/configs/constants.ts
|
|
766
|
+
var DEFAULT_CONFIG = {
|
|
767
|
+
MODEL: "deepseek",
|
|
768
|
+
PROVIDER: "deepseek",
|
|
769
|
+
CONTEXT_WINDOW: 32768,
|
|
770
|
+
MAX_OUTPUT_TOKENS: 4096,
|
|
771
|
+
TEMPERATURE: 0.7,
|
|
772
|
+
TOP_P: 0.9
|
|
773
|
+
};
|
|
774
|
+
var TOOL_CONSTANTS = {
|
|
775
|
+
SYSTEM_TOOL_PREFIX: "SystemTool.",
|
|
776
|
+
USER_TOOL_PREFIX: "UserTool.",
|
|
777
|
+
AGENT_TOOL_PREFIX: "Agent.",
|
|
778
|
+
MCP_TOOL_PREFIX: "MCP."
|
|
779
|
+
};
|
|
780
|
+
var MESSAGE_ROLES = {
|
|
781
|
+
SYSTEM: "system",
|
|
782
|
+
USER: "user",
|
|
783
|
+
ASSISTANT: "assistant"
|
|
784
|
+
};
|
|
785
|
+
var TOOL_CALL_TYPES = {
|
|
786
|
+
SYSTEM: "system",
|
|
787
|
+
USER: "user"
|
|
788
|
+
};
|
|
789
|
+
var ENV_VARS = {
|
|
790
|
+
EVOLT_CONFIG_PATH: "EVOLT_CONFIG_PATH",
|
|
791
|
+
DEEPSEEK_API_KEY: "DEEPSEEK_API_KEY",
|
|
792
|
+
DEEPSEEK_BASE_URL: "DEEPSEEK_BASE_URL",
|
|
793
|
+
OPENAI_API_KEY: "OPENAI_API_KEY",
|
|
794
|
+
ANTHROPIC_API_KEY: "ANTHROPIC_API_KEY"
|
|
795
|
+
};
|
|
796
|
+
var ERROR_MESSAGES = {
|
|
797
|
+
CONFIG_LOAD_FAILED: "Failed to load configuration",
|
|
798
|
+
MODEL_NOT_FOUND: "Model configuration not found",
|
|
799
|
+
TOOL_NOT_REGISTERED: "Tool not registered in tool store",
|
|
800
|
+
INVALID_MESSAGE_FORMAT: "Invalid message format",
|
|
801
|
+
TOOL_EXECUTION_FAILED: "Tool execution failed"
|
|
802
|
+
};
|
|
803
|
+
var LOG_LEVELS = {
|
|
804
|
+
DEBUG: 0,
|
|
805
|
+
INFO: 1,
|
|
806
|
+
WARNING: 2,
|
|
807
|
+
ERROR: 3
|
|
808
|
+
};
|
|
809
|
+
|
|
810
|
+
// src/configs/paths.ts
|
|
811
|
+
import * as path2 from "path";
|
|
812
|
+
import * as fs3 from "fs";
|
|
813
|
+
function getWorkspaceDir() {
|
|
814
|
+
return process.env.EVOLT_WORKSPACE || path2.join(process.cwd(), "workspace");
|
|
815
|
+
}
|
|
816
|
+
function getConfigDir() {
|
|
817
|
+
return process.env.EVOLT_CONFIG_DIR || path2.join(process.cwd(), "config");
|
|
818
|
+
}
|
|
819
|
+
function getLogsDir() {
|
|
820
|
+
return process.env.EVOLT_LOGS_DIR || path2.join(getWorkspaceDir(), "logs");
|
|
821
|
+
}
|
|
822
|
+
function getCacheDir() {
|
|
823
|
+
return process.env.EVOLT_CACHE_DIR || path2.join(getWorkspaceDir(), "cache");
|
|
824
|
+
}
|
|
825
|
+
function getSkillsDir() {
|
|
826
|
+
return process.env.EVOLT_SKILLS_DIR || path2.join(getWorkspaceDir(), "skills");
|
|
827
|
+
}
|
|
828
|
+
function ensureDir(dirPath) {
|
|
829
|
+
if (!fs3.existsSync(dirPath)) {
|
|
830
|
+
fs3.mkdirSync(dirPath, { recursive: true });
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
function getWorkspacePath(relativePath) {
|
|
834
|
+
const workspaceDir = getWorkspaceDir();
|
|
835
|
+
ensureDir(workspaceDir);
|
|
836
|
+
return path2.join(workspaceDir, relativePath);
|
|
837
|
+
}
|
|
838
|
+
function getConfigPath2(relativePath) {
|
|
839
|
+
const configDir = getConfigDir();
|
|
840
|
+
ensureDir(configDir);
|
|
841
|
+
return path2.join(configDir, relativePath);
|
|
842
|
+
}
|
|
843
|
+
function isInWorkspace(filePath) {
|
|
844
|
+
const workspaceDir = getWorkspaceDir();
|
|
845
|
+
const absolutePath = path2.resolve(filePath);
|
|
846
|
+
return absolutePath.startsWith(path2.resolve(workspaceDir));
|
|
847
|
+
}
|
|
848
|
+
function normalizePath(filePath) {
|
|
849
|
+
return path2.normalize(filePath).replace(/\\/g, "/");
|
|
850
|
+
}
|
|
851
|
+
function getFileExtension(filePath) {
|
|
852
|
+
return path2.extname(filePath).toLowerCase();
|
|
853
|
+
}
|
|
854
|
+
function fileExists(filePath) {
|
|
855
|
+
try {
|
|
856
|
+
return fs3.existsSync(filePath);
|
|
857
|
+
} catch {
|
|
858
|
+
return false;
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
function getTempFilePath(prefix = "evolt") {
|
|
862
|
+
const tempDir = path2.join(getCacheDir(), "temp");
|
|
863
|
+
ensureDir(tempDir);
|
|
864
|
+
const timestamp = Date.now();
|
|
865
|
+
const random = Math.random().toString(36).substring(2, 8);
|
|
866
|
+
return path2.join(tempDir, `${prefix}_${timestamp}_${random}.tmp`);
|
|
867
|
+
}
|
|
868
|
+
var WORKSPACE_DIR = getWorkspaceDir();
|
|
869
|
+
var SKILLS_DIR = getSkillsDir();
|
|
870
|
+
|
|
871
|
+
// src/configs/settings.ts
|
|
872
|
+
var DEFAULT_SETTINGS = {
|
|
873
|
+
logLevel: LOG_LEVELS.INFO,
|
|
874
|
+
verbose: false,
|
|
875
|
+
debug: false,
|
|
876
|
+
workspace: process.env.EVOLT_WORKSPACE || "./workspace",
|
|
877
|
+
maxRetries: 3,
|
|
878
|
+
timeout: 3e4,
|
|
879
|
+
// 30 seconds
|
|
880
|
+
enableCache: true,
|
|
881
|
+
enableTelemetry: false
|
|
882
|
+
};
|
|
883
|
+
var SettingsManager = class {
|
|
884
|
+
settings = { ...DEFAULT_SETTINGS };
|
|
885
|
+
/**
|
|
886
|
+
* Update settings
|
|
887
|
+
*/
|
|
888
|
+
update(newSettings) {
|
|
889
|
+
this.settings = { ...this.settings, ...newSettings };
|
|
890
|
+
}
|
|
891
|
+
/**
|
|
892
|
+
* Get current settings
|
|
893
|
+
*/
|
|
894
|
+
get() {
|
|
895
|
+
return { ...this.settings };
|
|
896
|
+
}
|
|
897
|
+
/**
|
|
898
|
+
* Reset to default settings
|
|
899
|
+
*/
|
|
900
|
+
reset() {
|
|
901
|
+
this.settings = { ...DEFAULT_SETTINGS };
|
|
902
|
+
}
|
|
903
|
+
/**
|
|
904
|
+
* Get specific setting value
|
|
905
|
+
*/
|
|
906
|
+
getValue(key) {
|
|
907
|
+
return this.settings[key];
|
|
908
|
+
}
|
|
909
|
+
/**
|
|
910
|
+
* Set specific setting value
|
|
911
|
+
*/
|
|
912
|
+
setValue(key, value) {
|
|
913
|
+
this.settings[key] = value;
|
|
914
|
+
}
|
|
915
|
+
};
|
|
916
|
+
var settings = new SettingsManager();
|
|
917
|
+
function initializeSettings() {
|
|
918
|
+
const envSettings = {};
|
|
919
|
+
const logLevelEnv = process.env.EVOLT_LOG_LEVEL;
|
|
920
|
+
if (logLevelEnv) {
|
|
921
|
+
const levelMap = {
|
|
922
|
+
debug: LOG_LEVELS.DEBUG,
|
|
923
|
+
info: LOG_LEVELS.INFO,
|
|
924
|
+
warning: LOG_LEVELS.WARNING,
|
|
925
|
+
error: LOG_LEVELS.ERROR
|
|
926
|
+
};
|
|
927
|
+
const normalizedLevel = logLevelEnv.toLowerCase();
|
|
928
|
+
envSettings.logLevel = levelMap[normalizedLevel] ?? LOG_LEVELS.INFO;
|
|
929
|
+
}
|
|
930
|
+
if (process.env.EVOLT_VERBOSE === "true" || process.env.EVOLT_VERBOSE === "1") {
|
|
931
|
+
envSettings.verbose = true;
|
|
932
|
+
}
|
|
933
|
+
if (process.env.EVOLT_DEBUG === "true" || process.env.EVOLT_DEBUG === "1") {
|
|
934
|
+
envSettings.debug = true;
|
|
935
|
+
envSettings.logLevel = LOG_LEVELS.DEBUG;
|
|
936
|
+
}
|
|
937
|
+
if (process.env.EVOLT_WORKSPACE) {
|
|
938
|
+
envSettings.workspace = process.env.EVOLT_WORKSPACE;
|
|
939
|
+
}
|
|
940
|
+
if (process.env.EVOLT_MAX_RETRIES) {
|
|
941
|
+
const maxRetries = parseInt(process.env.EVOLT_MAX_RETRIES, 10);
|
|
942
|
+
if (!isNaN(maxRetries)) {
|
|
943
|
+
envSettings.maxRetries = maxRetries;
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
if (process.env.EVOLT_TIMEOUT) {
|
|
947
|
+
const timeout = parseInt(process.env.EVOLT_TIMEOUT, 10);
|
|
948
|
+
if (!isNaN(timeout)) {
|
|
949
|
+
envSettings.timeout = timeout;
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
if (process.env.EVOLT_ENABLE_CACHE === "false" || process.env.EVOLT_ENABLE_CACHE === "0") {
|
|
953
|
+
envSettings.enableCache = false;
|
|
954
|
+
}
|
|
955
|
+
if (process.env.EVOLT_ENABLE_TELEMETRY === "true" || process.env.EVOLT_ENABLE_TELEMETRY === "1") {
|
|
956
|
+
envSettings.enableTelemetry = true;
|
|
957
|
+
}
|
|
958
|
+
settings.update(envSettings);
|
|
959
|
+
}
|
|
960
|
+
function getSettings() {
|
|
961
|
+
return settings.get();
|
|
962
|
+
}
|
|
963
|
+
function updateSettings(newSettings) {
|
|
964
|
+
settings.update(newSettings);
|
|
965
|
+
}
|
|
966
|
+
initializeSettings();
|
|
967
|
+
|
|
968
|
+
// src/configs/mcpConfig.ts
|
|
969
|
+
var MCP_SERVERS_CONFIG = {
|
|
970
|
+
playwright: {
|
|
971
|
+
type: "stdio",
|
|
972
|
+
command: "npx",
|
|
973
|
+
args: ["-y", "@playwright/mcp@latest"]
|
|
974
|
+
},
|
|
975
|
+
filesystem: {
|
|
976
|
+
type: "stdio",
|
|
977
|
+
command: "npx",
|
|
978
|
+
args: ["-y", "@modelcontextprotocol/server-filesystem", "."]
|
|
979
|
+
}
|
|
980
|
+
};
|
|
981
|
+
|
|
982
|
+
// src/tools/toolStore.ts
|
|
983
|
+
async function mcpTool(connection, toolName, args) {
|
|
984
|
+
try {
|
|
985
|
+
const argumentsDict = typeof args === "object" ? args : {};
|
|
986
|
+
const result = await connection.callTool(toolName, argumentsDict);
|
|
987
|
+
if (result && result.content) {
|
|
988
|
+
for (const item of result.content) {
|
|
989
|
+
if (item.type === "text") {
|
|
990
|
+
return item.text;
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
return "No text content in tool response";
|
|
995
|
+
} catch (e) {
|
|
996
|
+
return `Error executing ${toolName}: ${e}`;
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
var ToolStore = class {
|
|
1000
|
+
tools = {};
|
|
1001
|
+
/**
|
|
1002
|
+
* Add a tool to the store
|
|
1003
|
+
*/
|
|
1004
|
+
addTool(name, desc, execute, argNames, serverName, inputSchema) {
|
|
1005
|
+
if (name in this.tools) {
|
|
1006
|
+
logger.warn(`Tool ${name} already exists in store.`);
|
|
1007
|
+
return;
|
|
1008
|
+
}
|
|
1009
|
+
this.tools[name] = {
|
|
1010
|
+
desc,
|
|
1011
|
+
execute,
|
|
1012
|
+
argNames,
|
|
1013
|
+
serverName,
|
|
1014
|
+
inputSchema
|
|
1015
|
+
};
|
|
1016
|
+
}
|
|
1017
|
+
/**
|
|
1018
|
+
* Add MCP tools to the store
|
|
1019
|
+
*/
|
|
1020
|
+
async addMcpTools(agentName, serverName, stack) {
|
|
1021
|
+
const config2 = MCP_SERVERS_CONFIG[serverName];
|
|
1022
|
+
if (!config2) {
|
|
1023
|
+
logger.warn(`No MCP server config found for ${serverName}, agent: ${agentName}.`);
|
|
1024
|
+
return;
|
|
1025
|
+
}
|
|
1026
|
+
let numMcpTools = 0;
|
|
1027
|
+
try {
|
|
1028
|
+
const connection = createMcpConnection(config2);
|
|
1029
|
+
if (stack && typeof stack.enterContext === "function") {
|
|
1030
|
+
await stack.enterContext(connection);
|
|
1031
|
+
} else {
|
|
1032
|
+
logger.warn("No AsyncExitStack provided for MCP connection, connection might leak.");
|
|
1033
|
+
await connection.enter();
|
|
1034
|
+
}
|
|
1035
|
+
const toolDefinitions = await connection.listTools();
|
|
1036
|
+
for (const toolInfo of toolDefinitions) {
|
|
1037
|
+
if (toolInfo && toolInfo.name && toolInfo.inputSchema) {
|
|
1038
|
+
const toolDesc = toolInfo.description || `tool: ${toolInfo.name}`;
|
|
1039
|
+
const executeFn = async (args) => {
|
|
1040
|
+
return await mcpTool(connection, toolInfo.name, args);
|
|
1041
|
+
};
|
|
1042
|
+
const existingTool = this.getTool(toolInfo.name);
|
|
1043
|
+
if (existingTool && existingTool.agentName !== agentName) {
|
|
1044
|
+
logger.warn(`Tool ${toolInfo.name} already exists in store for different agent.`);
|
|
1045
|
+
continue;
|
|
1046
|
+
}
|
|
1047
|
+
this.addTool(
|
|
1048
|
+
toolInfo.name,
|
|
1049
|
+
toolDesc,
|
|
1050
|
+
executeFn,
|
|
1051
|
+
[],
|
|
1052
|
+
// Empty argNames implies expecting a single object argument (the dict)
|
|
1053
|
+
serverName,
|
|
1054
|
+
toolInfo.inputSchema
|
|
1055
|
+
);
|
|
1056
|
+
const tool = this.getTool(toolInfo.name);
|
|
1057
|
+
if (tool) {
|
|
1058
|
+
tool.agentName = agentName;
|
|
1059
|
+
}
|
|
1060
|
+
numMcpTools++;
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
logger.info(`Loaded ${numMcpTools} MCP tools from ${serverName}, config: ${JSON.stringify(config2)}.`);
|
|
1064
|
+
} catch (e) {
|
|
1065
|
+
logger.error(`Error setting up MCP server ${serverName}, config: ${JSON.stringify(config2)}: ${e}`);
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
/**
|
|
1069
|
+
* Get MCP tools schemas for a specific agent and server
|
|
1070
|
+
*/
|
|
1071
|
+
getMcpToolsSchemas(agentName, serverName, provider) {
|
|
1072
|
+
const toolcallSchemas = [];
|
|
1073
|
+
for (const name of this.listTools()) {
|
|
1074
|
+
const tool = this.getTool(name);
|
|
1075
|
+
if (!tool) continue;
|
|
1076
|
+
if (serverName && tool.serverName !== serverName) {
|
|
1077
|
+
continue;
|
|
1078
|
+
}
|
|
1079
|
+
if (tool.agentName && tool.agentName !== agentName) {
|
|
1080
|
+
continue;
|
|
1081
|
+
}
|
|
1082
|
+
if (tool.inputSchema) {
|
|
1083
|
+
toolcallSchemas.push(this.toToolSchema(name, tool, provider));
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
return toolcallSchemas;
|
|
1087
|
+
}
|
|
1088
|
+
/**
|
|
1089
|
+
* Convert tool to schema format for specific provider
|
|
1090
|
+
*/
|
|
1091
|
+
toToolSchema(name, tool, provider = "openai") {
|
|
1092
|
+
if (provider === "openai") {
|
|
1093
|
+
return {
|
|
1094
|
+
type: "function",
|
|
1095
|
+
function: {
|
|
1096
|
+
name,
|
|
1097
|
+
description: tool.desc,
|
|
1098
|
+
parameters: tool.inputSchema
|
|
1099
|
+
}
|
|
1100
|
+
};
|
|
1101
|
+
} else if (provider === "anthropic") {
|
|
1102
|
+
return {
|
|
1103
|
+
name,
|
|
1104
|
+
description: tool.desc,
|
|
1105
|
+
input_schema: tool.inputSchema
|
|
1106
|
+
};
|
|
1107
|
+
}
|
|
1108
|
+
return {};
|
|
1109
|
+
}
|
|
1110
|
+
/**
|
|
1111
|
+
* Get tool schema by name (Python: get_toolcall_schema)
|
|
1112
|
+
*/
|
|
1113
|
+
getToolcallSchema(toolName, provider = "openai") {
|
|
1114
|
+
const tool = this.getTool(toolName);
|
|
1115
|
+
if (!tool) {
|
|
1116
|
+
return {};
|
|
1117
|
+
}
|
|
1118
|
+
return this.toToolSchema(toolName, tool, provider);
|
|
1119
|
+
}
|
|
1120
|
+
/**
|
|
1121
|
+
* Get tool by name
|
|
1122
|
+
*/
|
|
1123
|
+
getTool(name) {
|
|
1124
|
+
return this.tools[name];
|
|
1125
|
+
}
|
|
1126
|
+
/**
|
|
1127
|
+
* Check if tool exists
|
|
1128
|
+
*/
|
|
1129
|
+
hasTool(name) {
|
|
1130
|
+
return name in this.tools;
|
|
1131
|
+
}
|
|
1132
|
+
/**
|
|
1133
|
+
* Get all tool names (Python: list_tools)
|
|
1134
|
+
*/
|
|
1135
|
+
listTools() {
|
|
1136
|
+
return Object.keys(this.tools);
|
|
1137
|
+
}
|
|
1138
|
+
/**
|
|
1139
|
+
* Convert all tools to schema format
|
|
1140
|
+
*/
|
|
1141
|
+
toDict(provider = "openai") {
|
|
1142
|
+
const toolSchemas = [];
|
|
1143
|
+
for (const [name, tool] of Object.entries(this.tools)) {
|
|
1144
|
+
if (tool.inputSchema) {
|
|
1145
|
+
toolSchemas.push(this.toToolSchema(name, tool, provider));
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
return toolSchemas;
|
|
1149
|
+
}
|
|
1150
|
+
/**
|
|
1151
|
+
* Get number of tools in store
|
|
1152
|
+
*/
|
|
1153
|
+
get length() {
|
|
1154
|
+
return Object.keys(this.tools).length;
|
|
1155
|
+
}
|
|
1156
|
+
/**
|
|
1157
|
+
* Check if tool exists (Python __contains__ equivalent)
|
|
1158
|
+
*/
|
|
1159
|
+
contains(name) {
|
|
1160
|
+
return this.hasTool(name);
|
|
1161
|
+
}
|
|
1162
|
+
/**
|
|
1163
|
+
* Get tool by index operator (Python __getitem__ equivalent)
|
|
1164
|
+
*/
|
|
1165
|
+
getItem(name) {
|
|
1166
|
+
const tool = this.getTool(name);
|
|
1167
|
+
if (!tool) {
|
|
1168
|
+
throw new Error(`Tool ${name} not found in store.`);
|
|
1169
|
+
}
|
|
1170
|
+
return tool;
|
|
1171
|
+
}
|
|
1172
|
+
/**
|
|
1173
|
+
* Get all tool entries
|
|
1174
|
+
*/
|
|
1175
|
+
items() {
|
|
1176
|
+
return Object.entries(this.tools);
|
|
1177
|
+
}
|
|
1178
|
+
/**
|
|
1179
|
+
* Get all tool names (alias for listTools)
|
|
1180
|
+
*/
|
|
1181
|
+
keys() {
|
|
1182
|
+
return this.listTools();
|
|
1183
|
+
}
|
|
1184
|
+
};
|
|
1185
|
+
var SystemToolStore = new ToolStore();
|
|
1186
|
+
var FunctionCallingStore = new ToolStore();
|
|
1187
|
+
var UserToolStore = FunctionCallingStore;
|
|
1188
|
+
|
|
1189
|
+
// src/tools/toolRegister.ts
|
|
1190
|
+
function buildToolDescription(fullToolName, config2) {
|
|
1191
|
+
let toolDesc = `
|
|
1192
|
+
<${fullToolName}>
|
|
1193
|
+
<${fullToolName}.description> ${config2.description}
|
|
1194
|
+
`;
|
|
1195
|
+
if (config2.params) {
|
|
1196
|
+
for (const param of config2.params) {
|
|
1197
|
+
toolDesc += `<argument name="${param.name}" type="${param.type}"> ${param.description} </argument>
|
|
1198
|
+
`;
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
if (config2.returns) {
|
|
1202
|
+
toolDesc += `<returns type="${config2.returns.type}"> ${config2.returns.description} </returns>
|
|
1203
|
+
`;
|
|
1204
|
+
}
|
|
1205
|
+
toolDesc += `</${fullToolName}.description>
|
|
1206
|
+
`;
|
|
1207
|
+
if (config2.examples && config2.examples.length > 0) {
|
|
1208
|
+
for (const example of config2.examples) {
|
|
1209
|
+
toolDesc += `<${fullToolName}.example>
|
|
1210
|
+
${example}
|
|
1211
|
+
</${fullToolName}.example>
|
|
1212
|
+
`;
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
toolDesc += `</${fullToolName}>`;
|
|
1216
|
+
return toolDesc;
|
|
1217
|
+
}
|
|
1218
|
+
function buildInputSchema(config2) {
|
|
1219
|
+
const inputSchema = {
|
|
1220
|
+
type: "object",
|
|
1221
|
+
properties: {},
|
|
1222
|
+
required: []
|
|
1223
|
+
};
|
|
1224
|
+
if (config2.params) {
|
|
1225
|
+
for (const param of config2.params) {
|
|
1226
|
+
inputSchema.properties[param.name] = {
|
|
1227
|
+
type: convertTypeNameToOpenai(param.type),
|
|
1228
|
+
description: param.description
|
|
1229
|
+
};
|
|
1230
|
+
if (!param.optional) {
|
|
1231
|
+
inputSchema.required.push(param.name);
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
return inputSchema;
|
|
1236
|
+
}
|
|
1237
|
+
function tools(config2) {
|
|
1238
|
+
return function(target) {
|
|
1239
|
+
const instance = new target();
|
|
1240
|
+
for (const [methodName, methodConfig] of Object.entries(config2)) {
|
|
1241
|
+
const method = instance[methodName];
|
|
1242
|
+
if (!method || typeof method !== "function") {
|
|
1243
|
+
throw new Error(`Tool ${target.name}.${methodName} not found`);
|
|
1244
|
+
}
|
|
1245
|
+
const execute = method.bind(instance);
|
|
1246
|
+
const argNames = methodConfig.params?.map((p) => p.name) || [];
|
|
1247
|
+
const systemToolName = `${target.name}.${methodName}`;
|
|
1248
|
+
const systemToolDesc = buildToolDescription(systemToolName, methodConfig);
|
|
1249
|
+
SystemToolStore.addTool(systemToolName, systemToolDesc, execute, argNames, target.name);
|
|
1250
|
+
const userToolName = `${target.name}-${methodName}`;
|
|
1251
|
+
const userToolDesc = `
|
|
1252
|
+
Tool \`${userToolName}\` function is: ${methodConfig.description}
|
|
1253
|
+
`;
|
|
1254
|
+
const inputSchema = buildInputSchema(methodConfig);
|
|
1255
|
+
FunctionCallingStore.addTool(userToolName, userToolDesc, execute, argNames, target.name, inputSchema);
|
|
1256
|
+
logger.debug(`Registered unified tool: ${systemToolName} / ${userToolName}`);
|
|
1257
|
+
}
|
|
1258
|
+
return target;
|
|
1259
|
+
};
|
|
1260
|
+
}
|
|
1261
|
+
function registerAgentAsTool(agents, verbose = false) {
|
|
1262
|
+
if (!agents) {
|
|
1263
|
+
return [];
|
|
1264
|
+
}
|
|
1265
|
+
const agentList = Array.isArray(agents) ? agents : [agents];
|
|
1266
|
+
const registeredAgentNames = [];
|
|
1267
|
+
for (const agent of agentList) {
|
|
1268
|
+
if (!agent || typeof agent !== "object") {
|
|
1269
|
+
logger.warn(`Invalid agent: ${agent}, skipping`);
|
|
1270
|
+
continue;
|
|
1271
|
+
}
|
|
1272
|
+
const agentName = `Agent.${agent.name}`;
|
|
1273
|
+
if (SystemToolStore.hasTool(agentName)) {
|
|
1274
|
+
logger.debug(`Agent ${agent.name} already registered as ${agentName}, change agent name`);
|
|
1275
|
+
registeredAgentNames.push(agentName);
|
|
1276
|
+
continue;
|
|
1277
|
+
}
|
|
1278
|
+
const toolDesc = `Delegate tasks to sub-agent ${agent.name}.
|
|
1279
|
+
|
|
1280
|
+
Profile: ${agent.profile}
|
|
1281
|
+
|
|
1282
|
+
Args:
|
|
1283
|
+
instruction (str): Detailed task description or instruction for the agent.
|
|
1284
|
+
Be specific about what you want the agent to accomplish.
|
|
1285
|
+
|
|
1286
|
+
Returns:
|
|
1287
|
+
str: The agent's execution result, including analysis, outputs, or conclusions.
|
|
1288
|
+
|
|
1289
|
+
Examples:
|
|
1290
|
+
<${agentName}>
|
|
1291
|
+
<instruction>
|
|
1292
|
+
Please analyze this data and provide insights on trends and patterns.
|
|
1293
|
+
</instruction>
|
|
1294
|
+
</${agentName}>
|
|
1295
|
+
`;
|
|
1296
|
+
const agentExecute = async (instruction) => {
|
|
1297
|
+
try {
|
|
1298
|
+
const response = await agent.run(instruction);
|
|
1299
|
+
if (typeof response === "string") {
|
|
1300
|
+
return response;
|
|
1301
|
+
}
|
|
1302
|
+
if (Array.isArray(response)) {
|
|
1303
|
+
return response.map((r) => String(r)).join("\n");
|
|
1304
|
+
}
|
|
1305
|
+
return JSON.stringify(response);
|
|
1306
|
+
} catch (error) {
|
|
1307
|
+
logger.error(`Error executing agent ${agent.name}:`, error);
|
|
1308
|
+
return `Error: ${error instanceof Error ? error.message : String(error)}`;
|
|
1309
|
+
}
|
|
1310
|
+
};
|
|
1311
|
+
SystemToolStore.addTool(agentName, toolDesc, agentExecute, ["instruction"], agent.name);
|
|
1312
|
+
registeredAgentNames.push(agentName);
|
|
1313
|
+
if (verbose) {
|
|
1314
|
+
logger.info(`Registered agent as tool: ${agentName}`);
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
if (registeredAgentNames.length > 0) {
|
|
1318
|
+
logger.debug(`Successfully registered ${registeredAgentNames.length} Agent tool(s): ${registeredAgentNames.join(", ")}`);
|
|
1319
|
+
}
|
|
1320
|
+
return registeredAgentNames;
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
// src/tools/think.ts
|
|
1324
|
+
var ThinkTool = class {
|
|
1325
|
+
async execute(thought) {
|
|
1326
|
+
return "Thinking complete!";
|
|
1327
|
+
}
|
|
1328
|
+
};
|
|
1329
|
+
ThinkTool = __decorateClass([
|
|
1330
|
+
tools({
|
|
1331
|
+
execute: {
|
|
1332
|
+
description: "Use the tool to think about something when complex reasoning.",
|
|
1333
|
+
params: [
|
|
1334
|
+
{
|
|
1335
|
+
name: "thought",
|
|
1336
|
+
type: "str",
|
|
1337
|
+
description: "The thought to think about."
|
|
1338
|
+
}
|
|
1339
|
+
],
|
|
1340
|
+
returns: {
|
|
1341
|
+
type: "str",
|
|
1342
|
+
description: "The complete thought result."
|
|
1343
|
+
},
|
|
1344
|
+
examples: [
|
|
1345
|
+
"Good example:\n<ThinkTool.execute><thought> your thought here </thought></ThinkTool.execute>",
|
|
1346
|
+
'Bad example:\n<ThinkTool.execute>{"thought":"your thought here"}</ThinkTool.execute>'
|
|
1347
|
+
]
|
|
1348
|
+
}
|
|
1349
|
+
})
|
|
1350
|
+
], ThinkTool);
|
|
1351
|
+
|
|
1352
|
+
// src/tools/cmdTool.ts
|
|
1353
|
+
import { spawn } from "child_process";
|
|
1354
|
+
|
|
1355
|
+
// src/types.ts
|
|
1356
|
+
var EvoltError = class extends Error {
|
|
1357
|
+
constructor(message) {
|
|
1358
|
+
super(message);
|
|
1359
|
+
this.name = "EvoltError";
|
|
1360
|
+
}
|
|
1361
|
+
};
|
|
1362
|
+
var ToolExecutionError = class extends EvoltError {
|
|
1363
|
+
constructor(message) {
|
|
1364
|
+
super(message);
|
|
1365
|
+
this.name = "ToolExecutionError";
|
|
1366
|
+
}
|
|
1367
|
+
};
|
|
1368
|
+
var ModelError = class extends EvoltError {
|
|
1369
|
+
constructor(message) {
|
|
1370
|
+
super(message);
|
|
1371
|
+
this.name = "ModelError";
|
|
1372
|
+
}
|
|
1373
|
+
};
|
|
1374
|
+
|
|
1375
|
+
// src/tools/context.ts
|
|
1376
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
1377
|
+
var toolcallManagerContext = new AsyncLocalStorage();
|
|
1378
|
+
function getCurrentManager() {
|
|
1379
|
+
return toolcallManagerContext.getStore() || null;
|
|
1380
|
+
}
|
|
1381
|
+
function runWithManager(manager, fn) {
|
|
1382
|
+
return toolcallManagerContext.run(manager, fn);
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
// src/tools/cmdTool.ts
|
|
1386
|
+
var CommandLineTool = class {
|
|
1387
|
+
async execute(command, cwd, env) {
|
|
1388
|
+
try {
|
|
1389
|
+
const workDir = cwd || process.cwd();
|
|
1390
|
+
try {
|
|
1391
|
+
const fs9 = await import("fs");
|
|
1392
|
+
if (!fs9.existsSync(workDir)) {
|
|
1393
|
+
return `Working directory does not exist: ${workDir}`;
|
|
1394
|
+
}
|
|
1395
|
+
} catch (error) {
|
|
1396
|
+
return `Cannot access working directory: ${workDir}`;
|
|
1397
|
+
}
|
|
1398
|
+
const execEnv = { ...process.env, ...env };
|
|
1399
|
+
const childProcess = spawn(command, {
|
|
1400
|
+
shell: true,
|
|
1401
|
+
cwd: workDir,
|
|
1402
|
+
env: execEnv,
|
|
1403
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
1404
|
+
});
|
|
1405
|
+
const manager = getCurrentManager();
|
|
1406
|
+
if (!manager) {
|
|
1407
|
+
throw new ToolExecutionError("Cannot get ToolcallManager, please execute this tool through ToolcallManager");
|
|
1408
|
+
}
|
|
1409
|
+
const processId = manager.registerBackgroundProcess(childProcess, command, workDir);
|
|
1410
|
+
logger.debug(`Register background process: Command: ${command}
|
|
1411
|
+
Process ID: ${processId}`);
|
|
1412
|
+
return new Promise((resolve2) => {
|
|
1413
|
+
const timeout = setTimeout(async () => {
|
|
1414
|
+
resolve2(
|
|
1415
|
+
`COMMAND: ${command}
|
|
1416
|
+
PROCESS ID: ${processId}
|
|
1417
|
+
STATUS: The process may still be executing (waited 5 seconds, not completed)
|
|
1418
|
+
TIP: Please use the list command later to check the process status, or wait for the process to complete and then check the result`
|
|
1419
|
+
);
|
|
1420
|
+
}, 5e3);
|
|
1421
|
+
let stdout = "";
|
|
1422
|
+
let stderr = "";
|
|
1423
|
+
childProcess.stdout?.on("data", (data) => {
|
|
1424
|
+
stdout += data.toString();
|
|
1425
|
+
});
|
|
1426
|
+
childProcess.stderr?.on("data", (data) => {
|
|
1427
|
+
stderr += data.toString();
|
|
1428
|
+
});
|
|
1429
|
+
childProcess.on("close", (code) => {
|
|
1430
|
+
clearTimeout(timeout);
|
|
1431
|
+
const stdoutText = stdout.trim();
|
|
1432
|
+
const stderrText = stderr.trim();
|
|
1433
|
+
const resultParts = [`COMMAND: ${command}`];
|
|
1434
|
+
if (stdoutText) {
|
|
1435
|
+
resultParts.push(`STDOUT:
|
|
1436
|
+
${stdoutText}`);
|
|
1437
|
+
}
|
|
1438
|
+
if (stderrText) {
|
|
1439
|
+
resultParts.push(`STDERR:
|
|
1440
|
+
${stderrText}`);
|
|
1441
|
+
}
|
|
1442
|
+
resultParts.push(`EXIT CODE: ${code}`);
|
|
1443
|
+
resolve2(resultParts.join("\n\n"));
|
|
1444
|
+
});
|
|
1445
|
+
childProcess.on("error", (error) => {
|
|
1446
|
+
clearTimeout(timeout);
|
|
1447
|
+
resolve2(`COMMAND: ${command}
|
|
1448
|
+
ERROR: ${error.message}`);
|
|
1449
|
+
});
|
|
1450
|
+
});
|
|
1451
|
+
} catch (error) {
|
|
1452
|
+
throw new ToolExecutionError(`Error executing background process '${command}': ${error.message}`);
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
/**
|
|
1456
|
+
* List all background processes.
|
|
1457
|
+
*
|
|
1458
|
+
* Returns:
|
|
1459
|
+
* str: The natural language description of the background process list
|
|
1460
|
+
*/
|
|
1461
|
+
async list() {
|
|
1462
|
+
const manager = getCurrentManager();
|
|
1463
|
+
if (!manager) {
|
|
1464
|
+
throw new ToolExecutionError("Cannot get ToolcallManager, please execute this tool through ToolcallManager");
|
|
1465
|
+
}
|
|
1466
|
+
const [status, result] = manager.listBackgroundProcesses();
|
|
1467
|
+
return result;
|
|
1468
|
+
}
|
|
1469
|
+
/**
|
|
1470
|
+
* Stop the specified background process.
|
|
1471
|
+
*
|
|
1472
|
+
* Args:
|
|
1473
|
+
* process_id (str): The process ID to stop
|
|
1474
|
+
* force (bool): Whether to force kill the process (using SIGKILL)
|
|
1475
|
+
*
|
|
1476
|
+
* Returns:
|
|
1477
|
+
* str: Natural language description of the operation result
|
|
1478
|
+
*/
|
|
1479
|
+
async stop(processId, force = false) {
|
|
1480
|
+
const manager = getCurrentManager();
|
|
1481
|
+
if (!manager) {
|
|
1482
|
+
throw new ToolExecutionError("Cannot get ToolcallManager, please execute this tool through ToolcallManager");
|
|
1483
|
+
}
|
|
1484
|
+
return await manager.stopBackgroundProcess(processId, force);
|
|
1485
|
+
}
|
|
1486
|
+
/**
|
|
1487
|
+
* Clean up all background processes.
|
|
1488
|
+
*
|
|
1489
|
+
* Returns:
|
|
1490
|
+
* str: The natural language description of the cleanup result
|
|
1491
|
+
*/
|
|
1492
|
+
async cleanup() {
|
|
1493
|
+
const manager = getCurrentManager();
|
|
1494
|
+
if (!manager) {
|
|
1495
|
+
throw new ToolExecutionError("Cannot get ToolcallManager, please execute this tool through ToolcallManager");
|
|
1496
|
+
}
|
|
1497
|
+
return await manager.cleanupBackgroundProcesses();
|
|
1498
|
+
}
|
|
1499
|
+
};
|
|
1500
|
+
CommandLineTool = __decorateClass([
|
|
1501
|
+
tools({
|
|
1502
|
+
execute: {
|
|
1503
|
+
description: "Execute bash command in background (non-blocking, returns result).",
|
|
1504
|
+
params: [
|
|
1505
|
+
{ name: "command", type: "str", description: "The bash command to execute" },
|
|
1506
|
+
{
|
|
1507
|
+
name: "cwd",
|
|
1508
|
+
type: "Optional[str]",
|
|
1509
|
+
description: "Working directory, uses current directory if None",
|
|
1510
|
+
optional: true
|
|
1511
|
+
},
|
|
1512
|
+
{
|
|
1513
|
+
name: "env",
|
|
1514
|
+
type: "Optional[Dict[str, str]]",
|
|
1515
|
+
description: "Environment variables dictionary, uses current environment if None",
|
|
1516
|
+
optional: true
|
|
1517
|
+
}
|
|
1518
|
+
],
|
|
1519
|
+
returns: { type: "str", description: "Background process startup information, including process ID and PID" }
|
|
1520
|
+
},
|
|
1521
|
+
list: {
|
|
1522
|
+
description: "List all background processes.",
|
|
1523
|
+
returns: { type: "str", description: "Information about all background processes" }
|
|
1524
|
+
},
|
|
1525
|
+
stop: {
|
|
1526
|
+
description: "Stop the specified background process.",
|
|
1527
|
+
params: [
|
|
1528
|
+
{ name: "processId", type: "str", description: "The process ID to stop" },
|
|
1529
|
+
{
|
|
1530
|
+
name: "force",
|
|
1531
|
+
type: "bool",
|
|
1532
|
+
description: "Whether to force kill the process (using SIGKILL)",
|
|
1533
|
+
optional: true
|
|
1534
|
+
}
|
|
1535
|
+
],
|
|
1536
|
+
returns: { type: "str", description: "Result information of stopping the process" }
|
|
1537
|
+
},
|
|
1538
|
+
cleanup: {
|
|
1539
|
+
description: "Clean up all background processes.",
|
|
1540
|
+
returns: { type: "str", description: "Cleanup result information" }
|
|
1541
|
+
}
|
|
1542
|
+
})
|
|
1543
|
+
], CommandLineTool);
|
|
1544
|
+
|
|
1545
|
+
// src/tools/fileTool.ts
|
|
1546
|
+
import * as fs4 from "fs/promises";
|
|
1547
|
+
import * as pathModule from "path";
|
|
1548
|
+
var FileEditor = class {
|
|
1549
|
+
async read(path5, lineRange = "all") {
|
|
1550
|
+
try {
|
|
1551
|
+
await fs4.access(path5);
|
|
1552
|
+
} catch {
|
|
1553
|
+
throw new ToolExecutionError(`File does not exist: ${path5}`);
|
|
1554
|
+
}
|
|
1555
|
+
try {
|
|
1556
|
+
let content;
|
|
1557
|
+
try {
|
|
1558
|
+
content = await fs4.readFile(path5, "utf-8");
|
|
1559
|
+
} catch (error) {
|
|
1560
|
+
if (error.code === "ENCODING_NOT_SUPPORTED" || error.message.includes("encoding")) {
|
|
1561
|
+
const buffer = await fs4.readFile(path5);
|
|
1562
|
+
content = buffer.toString("latin1");
|
|
1563
|
+
} else {
|
|
1564
|
+
throw error;
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
const lines = content.split("\n");
|
|
1568
|
+
if (lineRange === "all") {
|
|
1569
|
+
return content;
|
|
1570
|
+
}
|
|
1571
|
+
if (lineRange.includes("-")) {
|
|
1572
|
+
try {
|
|
1573
|
+
const [startStr, endStr] = lineRange.split("-", 2);
|
|
1574
|
+
const start = parseInt(startStr) - 1;
|
|
1575
|
+
const end = parseInt(endStr);
|
|
1576
|
+
if (start < 0 || end <= start) {
|
|
1577
|
+
throw new ToolExecutionError(`Line range is invalid: ${lineRange}`);
|
|
1578
|
+
}
|
|
1579
|
+
const selectedLines = lines.slice(start, end);
|
|
1580
|
+
return `Lines ${start + 1} to ${end} of file ${path5}:
|
|
1581
|
+
` + selectedLines.join("\n");
|
|
1582
|
+
} catch (error) {
|
|
1583
|
+
throw new ToolExecutionError(
|
|
1584
|
+
`Invalid line range format when reading file ${path5}: ${lineRange}, error: ${error.message}`
|
|
1585
|
+
);
|
|
1586
|
+
}
|
|
1587
|
+
} else {
|
|
1588
|
+
try {
|
|
1589
|
+
const lineNum = parseInt(lineRange) - 1;
|
|
1590
|
+
if (lineNum < 0) {
|
|
1591
|
+
throw new ToolExecutionError(`Line number is less than 0: ${lineRange}`);
|
|
1592
|
+
}
|
|
1593
|
+
if (lineNum >= lines.length) {
|
|
1594
|
+
throw new ToolExecutionError(`Line number ${lineNum + 1} exceeds file length ${lines.length}`);
|
|
1595
|
+
}
|
|
1596
|
+
return `Line ${lineNum + 1} of file ${path5}:
|
|
1597
|
+
` + lines[lineNum];
|
|
1598
|
+
} catch (error) {
|
|
1599
|
+
throw new ToolExecutionError(
|
|
1600
|
+
`Invalid line number format when reading file ${path5}: ${lineRange}, error: ${error.message}`
|
|
1601
|
+
);
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
} catch (error) {
|
|
1605
|
+
if (error instanceof ToolExecutionError) {
|
|
1606
|
+
throw error;
|
|
1607
|
+
}
|
|
1608
|
+
throw new ToolExecutionError(`Error reading file ${path5}: ${error.message}`);
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
async write(path5, content) {
|
|
1612
|
+
try {
|
|
1613
|
+
const dirPath = pathModule.dirname(path5);
|
|
1614
|
+
if (dirPath) {
|
|
1615
|
+
await fs4.mkdir(dirPath, { recursive: true });
|
|
1616
|
+
}
|
|
1617
|
+
await fs4.writeFile(path5, content, "utf-8");
|
|
1618
|
+
const bytesWritten = Buffer.from(content, "utf-8").length;
|
|
1619
|
+
return `Successfully wrote content to file ${path5}, wrote ${bytesWritten} bytes`;
|
|
1620
|
+
} catch (error) {
|
|
1621
|
+
if (error.code === "EACCES") {
|
|
1622
|
+
throw new ToolExecutionError(`No write permission: ${path5}`);
|
|
1623
|
+
}
|
|
1624
|
+
throw new ToolExecutionError(`Error writing to file ${path5}: ${error.message}`);
|
|
1625
|
+
}
|
|
1626
|
+
}
|
|
1627
|
+
async find(path5, pattern) {
|
|
1628
|
+
try {
|
|
1629
|
+
await fs4.access(path5);
|
|
1630
|
+
} catch {
|
|
1631
|
+
throw new ToolExecutionError(`File does not exist: ${path5}`);
|
|
1632
|
+
}
|
|
1633
|
+
let compiledPattern;
|
|
1634
|
+
try {
|
|
1635
|
+
compiledPattern = new RegExp(pattern, "g");
|
|
1636
|
+
} catch (error) {
|
|
1637
|
+
throw new ToolExecutionError(`Invalid regex pattern: ${pattern}, error: ${error.message}`);
|
|
1638
|
+
}
|
|
1639
|
+
const matches = [];
|
|
1640
|
+
try {
|
|
1641
|
+
let content;
|
|
1642
|
+
try {
|
|
1643
|
+
content = await fs4.readFile(path5, "utf-8");
|
|
1644
|
+
} catch (error) {
|
|
1645
|
+
if (error.code === "ENCODING_NOT_SUPPORTED" || error.message.includes("encoding")) {
|
|
1646
|
+
const buffer = await fs4.readFile(path5);
|
|
1647
|
+
content = buffer.toString("latin1");
|
|
1648
|
+
} else {
|
|
1649
|
+
throw error;
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
const lines = content.split("\n");
|
|
1653
|
+
for (let lineNumber = 0; lineNumber < lines.length; lineNumber++) {
|
|
1654
|
+
const line = lines[lineNumber];
|
|
1655
|
+
let match;
|
|
1656
|
+
compiledPattern.lastIndex = 0;
|
|
1657
|
+
while ((match = compiledPattern.exec(line)) !== null) {
|
|
1658
|
+
matches.push({
|
|
1659
|
+
lineNumber: lineNumber + 1,
|
|
1660
|
+
lineContent: line,
|
|
1661
|
+
match: match[0],
|
|
1662
|
+
startPos: match.index,
|
|
1663
|
+
endPos: match.index + match[0].length
|
|
1664
|
+
});
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1667
|
+
} catch (error) {
|
|
1668
|
+
throw new ToolExecutionError(`Error reading file ${path5}: ${error.message}`);
|
|
1669
|
+
}
|
|
1670
|
+
if (matches.length === 0) {
|
|
1671
|
+
return `No content matching pattern '${pattern}' found in file ${path5}`;
|
|
1672
|
+
}
|
|
1673
|
+
const resultLines = [`Found ${matches.length} matches in file ${path5}:`];
|
|
1674
|
+
for (const match of matches) {
|
|
1675
|
+
resultLines.push(`Line ${match.lineNumber}: ${match.match} (position ${match.startPos}-${match.endPos})`);
|
|
1676
|
+
resultLines.push(` Full line content: ${match.lineContent}`);
|
|
1677
|
+
}
|
|
1678
|
+
return resultLines.join("\n");
|
|
1679
|
+
}
|
|
1680
|
+
async findAndReplace(path5, pattern, replacement) {
|
|
1681
|
+
try {
|
|
1682
|
+
await fs4.access(path5);
|
|
1683
|
+
} catch {
|
|
1684
|
+
throw new ToolExecutionError(`File does not exist: ${path5}`);
|
|
1685
|
+
}
|
|
1686
|
+
let compiledPattern;
|
|
1687
|
+
try {
|
|
1688
|
+
compiledPattern = new RegExp(pattern, "g");
|
|
1689
|
+
} catch (error) {
|
|
1690
|
+
throw new ToolExecutionError(`Invalid regex pattern ${pattern}, error: ${error.message}`);
|
|
1691
|
+
}
|
|
1692
|
+
let content;
|
|
1693
|
+
try {
|
|
1694
|
+
try {
|
|
1695
|
+
content = await fs4.readFile(path5, "utf-8");
|
|
1696
|
+
} catch (error) {
|
|
1697
|
+
if (error.code === "ENCODING_NOT_SUPPORTED" || error.message.includes("encoding")) {
|
|
1698
|
+
const buffer = await fs4.readFile(path5);
|
|
1699
|
+
content = buffer.toString("latin1");
|
|
1700
|
+
} else {
|
|
1701
|
+
throw error;
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
} catch (error) {
|
|
1705
|
+
throw new ToolExecutionError(`Error reading file ${path5}: ${error.message}`);
|
|
1706
|
+
}
|
|
1707
|
+
const newContent = content.replace(compiledPattern, replacement);
|
|
1708
|
+
const replacementCount = (content.match(compiledPattern) || []).length;
|
|
1709
|
+
let modifiedLines = 0;
|
|
1710
|
+
if (replacementCount > 0) {
|
|
1711
|
+
const originalLines = content.split("\n");
|
|
1712
|
+
const newLines = newContent.split("\n");
|
|
1713
|
+
modifiedLines = originalLines.filter((line, index) => line !== newLines[index]).length;
|
|
1714
|
+
try {
|
|
1715
|
+
await fs4.writeFile(path5, newContent, "utf-8");
|
|
1716
|
+
} catch (error) {
|
|
1717
|
+
if (error.code === "EACCES") {
|
|
1718
|
+
throw new ToolExecutionError(`No write permission: ${path5}`);
|
|
1719
|
+
}
|
|
1720
|
+
throw new ToolExecutionError(`Error writing to file ${path5}: ${error.message}`);
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1723
|
+
return `In file ${path5}, found and replaced pattern '${pattern}' with '${replacement}', successfully replaced ${replacementCount} occurrences, modified ${modifiedLines} lines`;
|
|
1724
|
+
}
|
|
1725
|
+
async insert(path5, content, line) {
|
|
1726
|
+
try {
|
|
1727
|
+
await fs4.access(path5);
|
|
1728
|
+
} catch {
|
|
1729
|
+
throw new ToolExecutionError(`File does not exist: ${path5}`);
|
|
1730
|
+
}
|
|
1731
|
+
let lines;
|
|
1732
|
+
try {
|
|
1733
|
+
let fileContent;
|
|
1734
|
+
try {
|
|
1735
|
+
fileContent = await fs4.readFile(path5, "utf-8");
|
|
1736
|
+
} catch (error) {
|
|
1737
|
+
if (error.code === "ENCODING_NOT_SUPPORTED" || error.message.includes("encoding")) {
|
|
1738
|
+
const buffer = await fs4.readFile(path5);
|
|
1739
|
+
fileContent = buffer.toString("latin1");
|
|
1740
|
+
} else {
|
|
1741
|
+
throw error;
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
lines = fileContent.split("\n");
|
|
1745
|
+
} catch (error) {
|
|
1746
|
+
throw new ToolExecutionError(`Error reading file ${path5}: ${error.message}`);
|
|
1747
|
+
}
|
|
1748
|
+
let actionDesc;
|
|
1749
|
+
if (line === void 0) {
|
|
1750
|
+
lines.push(content);
|
|
1751
|
+
actionDesc = `Appended content to end of file ${path5}`;
|
|
1752
|
+
} else {
|
|
1753
|
+
if (!(0 < line && line <= lines.length + 1)) {
|
|
1754
|
+
throw new ToolExecutionError(`Line number ${line} exceeds file ${path5} bounds (1 to ${lines.length + 1})`);
|
|
1755
|
+
}
|
|
1756
|
+
lines.splice(line - 1, 0, content);
|
|
1757
|
+
actionDesc = `Inserted content at line ${line} of file ${path5}`;
|
|
1758
|
+
}
|
|
1759
|
+
try {
|
|
1760
|
+
await fs4.writeFile(path5, lines.join("\n"), "utf-8");
|
|
1761
|
+
return actionDesc;
|
|
1762
|
+
} catch (error) {
|
|
1763
|
+
if (error.code === "EACCES") {
|
|
1764
|
+
throw new ToolExecutionError(`No write permission: ${path5}`);
|
|
1765
|
+
}
|
|
1766
|
+
throw new ToolExecutionError(`Error writing to file ${path5}: ${error.message}`);
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
};
|
|
1770
|
+
FileEditor = __decorateClass([
|
|
1771
|
+
tools({
|
|
1772
|
+
read: {
|
|
1773
|
+
description: "Read the content of only one file. **Read one file at a time**.",
|
|
1774
|
+
params: [
|
|
1775
|
+
{ name: "path", type: "string", description: "The path of only one file, must include workspace directory" },
|
|
1776
|
+
{
|
|
1777
|
+
name: "lineRange",
|
|
1778
|
+
type: "string",
|
|
1779
|
+
description: "Line range, format 'start-end' or 'start'. If 'all', reads entire file.",
|
|
1780
|
+
optional: true
|
|
1781
|
+
}
|
|
1782
|
+
],
|
|
1783
|
+
returns: { type: "string", description: "The content of the file" }
|
|
1784
|
+
},
|
|
1785
|
+
write: {
|
|
1786
|
+
description: "Write content to a file.",
|
|
1787
|
+
params: [
|
|
1788
|
+
{ name: "path", type: "string", description: "File path, must include workspace directory" },
|
|
1789
|
+
{ name: "content", type: "string", description: "Content to write" }
|
|
1790
|
+
],
|
|
1791
|
+
returns: { type: "string", description: "Natural language description of operation result" }
|
|
1792
|
+
},
|
|
1793
|
+
find: {
|
|
1794
|
+
description: "Find content matching specified pattern in file. **Folder searching is not supported.**",
|
|
1795
|
+
params: [
|
|
1796
|
+
{ name: "path", type: "string", description: "File path, must include workspace directory" },
|
|
1797
|
+
{ name: "pattern", type: "string", description: "Regex pattern to search for" }
|
|
1798
|
+
],
|
|
1799
|
+
returns: { type: "string", description: "Natural language description of search results" }
|
|
1800
|
+
},
|
|
1801
|
+
findAndReplace: {
|
|
1802
|
+
description: "Find and replace content matching specified pattern in file. **Folder searching is not supported.**",
|
|
1803
|
+
params: [
|
|
1804
|
+
{ name: "path", type: "string", description: "File path, must include workspace directory" },
|
|
1805
|
+
{
|
|
1806
|
+
name: "pattern",
|
|
1807
|
+
type: "string",
|
|
1808
|
+
description: 'Regex pattern to search for. Escape special chars [ ] ( ) . * + ? $ ^ | \\ with backslash. Example: CSS class "pt-[30]" \u2192 use pattern "pt-\\[30\\]"'
|
|
1809
|
+
},
|
|
1810
|
+
{ name: "replacement", type: "string", description: "Replacement text" }
|
|
1811
|
+
],
|
|
1812
|
+
returns: { type: "string", description: "Natural language description of operation result" }
|
|
1813
|
+
},
|
|
1814
|
+
insert: {
|
|
1815
|
+
description: "Insert content at specified line in file. **Folder searching is not supported.**",
|
|
1816
|
+
params: [
|
|
1817
|
+
{ name: "path", type: "string", description: "File path, must include workspace directory" },
|
|
1818
|
+
{ name: "content", type: "string", description: "Content to insert" },
|
|
1819
|
+
{ name: "line", type: "integer", description: "Line number to insert at. If None, appends to end of file.", optional: true }
|
|
1820
|
+
],
|
|
1821
|
+
returns: { type: "string", description: "Natural language description of operation result" }
|
|
1822
|
+
}
|
|
1823
|
+
})
|
|
1824
|
+
], FileEditor);
|
|
1825
|
+
|
|
1826
|
+
// src/tools/esmTool.ts
|
|
1827
|
+
var ExtendStateMachineTool = class {
|
|
1828
|
+
async writeEsm(esmContent, filepath) {
|
|
1829
|
+
try {
|
|
1830
|
+
const fileEditor = new FileEditor();
|
|
1831
|
+
await fileEditor.write(filepath, esmContent);
|
|
1832
|
+
return `Successfully wrote the ESM content to the yaml file ${filepath}`;
|
|
1833
|
+
} catch (error) {
|
|
1834
|
+
throw new ToolExecutionError(`Failed to write the ESM content to the yaml file ${filepath}: ${error.message}`);
|
|
1835
|
+
}
|
|
1836
|
+
}
|
|
1837
|
+
};
|
|
1838
|
+
ExtendStateMachineTool = __decorateClass([
|
|
1839
|
+
tools({
|
|
1840
|
+
writeEsm: {
|
|
1841
|
+
description: "Write the yaml format ExtendStateMachine(ESM) content to the yaml file",
|
|
1842
|
+
params: [
|
|
1843
|
+
{
|
|
1844
|
+
name: "esmContent",
|
|
1845
|
+
type: "str",
|
|
1846
|
+
description: "The yaml format ESM content to write. follow the format of the ESM content."
|
|
1847
|
+
},
|
|
1848
|
+
{
|
|
1849
|
+
name: "filepath",
|
|
1850
|
+
type: "str",
|
|
1851
|
+
description: "The path to write the yaml format ESM content to."
|
|
1852
|
+
}
|
|
1853
|
+
],
|
|
1854
|
+
returns: {
|
|
1855
|
+
type: "str",
|
|
1856
|
+
description: "Successfully wrote the ESM content to the yaml file {filepath}"
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
})
|
|
1860
|
+
], ExtendStateMachineTool);
|
|
1861
|
+
|
|
1862
|
+
// src/tools/apiTool.ts
|
|
1863
|
+
var ApiTool = class {
|
|
1864
|
+
async write(apiFilePath, apiContent) {
|
|
1865
|
+
const fileEditor = new FileEditor();
|
|
1866
|
+
await fileEditor.write(apiFilePath, apiContent);
|
|
1867
|
+
return `Write api file ${apiFilePath} complete!`;
|
|
1868
|
+
}
|
|
1869
|
+
async generateGolangFilesFromApi(apiFilePath, dir) {
|
|
1870
|
+
const cmdTool = new CommandLineTool();
|
|
1871
|
+
await cmdTool.execute(`goctl api go -api ${apiFilePath} -dir ${dir}`);
|
|
1872
|
+
return `Generate golang files from ${apiFilePath} complete! The files are in ${dir}`;
|
|
1873
|
+
}
|
|
1874
|
+
};
|
|
1875
|
+
ApiTool = __decorateClass([
|
|
1876
|
+
tools({
|
|
1877
|
+
write: {
|
|
1878
|
+
description: "Use the tool to write api file based on goctl.",
|
|
1879
|
+
params: [
|
|
1880
|
+
{
|
|
1881
|
+
name: "apiFilePath",
|
|
1882
|
+
type: "str",
|
|
1883
|
+
description: "The api file path to write."
|
|
1884
|
+
},
|
|
1885
|
+
{
|
|
1886
|
+
name: "apiContent",
|
|
1887
|
+
type: "str",
|
|
1888
|
+
description: "The api content to write."
|
|
1889
|
+
}
|
|
1890
|
+
],
|
|
1891
|
+
returns: { type: "str", description: "The complete api file result." },
|
|
1892
|
+
examples: [
|
|
1893
|
+
'Good example:\n<ApiTool.write><apiFilePath>to/your/workspace/your_api_file_name.api</apiFilePath><apiContent>type ( Request { Name string `path:"name,options=[you,me]"` } Response { Message string `json:"message"` } ) service greet-api { @handler GreetHandler get /greet/from/:name (Request) returns (Response) }</apiContent></ApiTool.write>'
|
|
1894
|
+
]
|
|
1895
|
+
},
|
|
1896
|
+
generateGolangFilesFromApi: {
|
|
1897
|
+
description: "Use the tool to generate golang files from api file.",
|
|
1898
|
+
params: [
|
|
1899
|
+
{
|
|
1900
|
+
name: "apiFilePath",
|
|
1901
|
+
type: "str",
|
|
1902
|
+
description: "The api file path to generate golang files."
|
|
1903
|
+
},
|
|
1904
|
+
{
|
|
1905
|
+
name: "dir",
|
|
1906
|
+
type: "str",
|
|
1907
|
+
description: "The directory to generate golang files."
|
|
1908
|
+
}
|
|
1909
|
+
],
|
|
1910
|
+
returns: { type: "str", description: "The complete golang files result." }
|
|
1911
|
+
}
|
|
1912
|
+
})
|
|
1913
|
+
], ApiTool);
|
|
1914
|
+
|
|
1915
|
+
// src/tools/reply2human.ts
|
|
1916
|
+
var Reply2HumanTool = class {
|
|
1917
|
+
async reply(reply) {
|
|
1918
|
+
return "Reply complete!";
|
|
1919
|
+
}
|
|
1920
|
+
};
|
|
1921
|
+
Reply2HumanTool = __decorateClass([
|
|
1922
|
+
tools({
|
|
1923
|
+
reply: {
|
|
1924
|
+
description: "Emit the reply text in a unified and formatted manner.",
|
|
1925
|
+
params: [{ name: "reply", type: "str", description: "Raw reply text to be sent to the user." }],
|
|
1926
|
+
returns: { type: "str", description: "The complete, formatted reply." }
|
|
1927
|
+
}
|
|
1928
|
+
})
|
|
1929
|
+
], Reply2HumanTool);
|
|
1930
|
+
|
|
1931
|
+
// src/tools/todoList.ts
|
|
1932
|
+
var TodoListTool = class {
|
|
1933
|
+
async write(projectName, todoList, projectDir) {
|
|
1934
|
+
const content = `# ${projectName}
|
|
1935
|
+
## Todo List
|
|
1936
|
+
${todoList}`;
|
|
1937
|
+
const fileEditor = new FileEditor();
|
|
1938
|
+
await fileEditor.write(`${projectDir}/todo.md`, content);
|
|
1939
|
+
return `Write todo list file for project ${projectName} complete! The file is in ${projectDir}/todo.md`;
|
|
1940
|
+
}
|
|
1941
|
+
};
|
|
1942
|
+
TodoListTool = __decorateClass([
|
|
1943
|
+
tools({
|
|
1944
|
+
write: {
|
|
1945
|
+
description: "Use the tool to write todo list file for project.",
|
|
1946
|
+
params: [
|
|
1947
|
+
{ name: "projectName", type: "str", description: "The project name to write todo list file." },
|
|
1948
|
+
{ name: "todoList", type: "str", description: "The todo list to write." },
|
|
1949
|
+
{ name: "projectDir", type: "str", description: "The project directory to write todo list file." }
|
|
1950
|
+
],
|
|
1951
|
+
returns: { type: "str", description: "The complete todo list file result." },
|
|
1952
|
+
examples: [
|
|
1953
|
+
"Good example:\n<TodoListTool.write><projectName>your project name here</projectName><todoList>- [ ] Write todo list file for project\n- [ ] Write todo list file for project</todoList><projectDir>your project directory here</projectDir></TodoListTool.write>"
|
|
1954
|
+
]
|
|
1955
|
+
}
|
|
1956
|
+
})
|
|
1957
|
+
], TodoListTool);
|
|
1958
|
+
|
|
1959
|
+
// src/tools/designUI.ts
|
|
1960
|
+
var WriteUIDesignDocument = class {
|
|
1961
|
+
async write(uiDesignDocumentFileDir, elements, composites, pages, functions, styles) {
|
|
1962
|
+
const fileEditor = new FileEditor();
|
|
1963
|
+
await fileEditor.write(`${uiDesignDocumentFileDir}/AtomicElements.json`, JSON.stringify(elements, null, 2));
|
|
1964
|
+
await fileEditor.write(`${uiDesignDocumentFileDir}/Composites.json`, JSON.stringify(composites, null, 2));
|
|
1965
|
+
await fileEditor.write(`${uiDesignDocumentFileDir}/Pages.json`, JSON.stringify(pages, null, 2));
|
|
1966
|
+
await fileEditor.write(`${uiDesignDocumentFileDir}/Functions.json`, JSON.stringify(functions, null, 2));
|
|
1967
|
+
await fileEditor.write(`${uiDesignDocumentFileDir}/Styles.json`, JSON.stringify(styles, null, 2));
|
|
1968
|
+
return `Write UI Design Document files ${uiDesignDocumentFileDir} complete!`;
|
|
1969
|
+
}
|
|
1970
|
+
};
|
|
1971
|
+
WriteUIDesignDocument = __decorateClass([
|
|
1972
|
+
tools({
|
|
1973
|
+
write: {
|
|
1974
|
+
description: "Use the tool to write UI Design Document files.",
|
|
1975
|
+
params: [
|
|
1976
|
+
{
|
|
1977
|
+
name: "uiDesignDocumentFileDir",
|
|
1978
|
+
type: "str",
|
|
1979
|
+
description: "The UI Design Document file directory to write."
|
|
1980
|
+
},
|
|
1981
|
+
{
|
|
1982
|
+
name: "elements",
|
|
1983
|
+
type: "List[Dict]",
|
|
1984
|
+
description: "The elements to write."
|
|
1985
|
+
},
|
|
1986
|
+
{
|
|
1987
|
+
name: "composites",
|
|
1988
|
+
type: "List[Dict]",
|
|
1989
|
+
description: "The composites to write."
|
|
1990
|
+
},
|
|
1991
|
+
{ name: "pages", type: "List[Dict]", description: "The pages to write." },
|
|
1992
|
+
{
|
|
1993
|
+
name: "functions",
|
|
1994
|
+
type: "List[Dict]",
|
|
1995
|
+
description: "The functions to write."
|
|
1996
|
+
},
|
|
1997
|
+
{
|
|
1998
|
+
name: "styles",
|
|
1999
|
+
type: "List[Dict]",
|
|
2000
|
+
description: "The styles to write."
|
|
2001
|
+
}
|
|
2002
|
+
],
|
|
2003
|
+
returns: {
|
|
2004
|
+
type: "str",
|
|
2005
|
+
description: "The complete UI Design Document files result."
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
})
|
|
2009
|
+
], WriteUIDesignDocument);
|
|
2010
|
+
|
|
2011
|
+
// src/tools/reflect.ts
|
|
2012
|
+
var ReflectTool = class {
|
|
2013
|
+
async reflect(state) {
|
|
2014
|
+
return "Reflected complete!";
|
|
2015
|
+
}
|
|
2016
|
+
/**
|
|
2017
|
+
* Set the reflection for agent to see.
|
|
2018
|
+
*/
|
|
2019
|
+
setReflection(reflection) {
|
|
2020
|
+
return `<ReflectTool.setReflection><reflection>${reflection}</reflection></ReflectTool.setReflection>`;
|
|
2021
|
+
}
|
|
2022
|
+
};
|
|
2023
|
+
ReflectTool = __decorateClass([
|
|
2024
|
+
tools({
|
|
2025
|
+
reflect: {
|
|
2026
|
+
description: "Use the tool to reflect on the state.",
|
|
2027
|
+
params: [{ name: "state", type: "str", description: "The state to reflect on." }],
|
|
2028
|
+
returns: { type: "str", description: "The reflected result." }
|
|
2029
|
+
}
|
|
2030
|
+
})
|
|
2031
|
+
], ReflectTool);
|
|
2032
|
+
|
|
2033
|
+
// src/tools/skills.ts
|
|
2034
|
+
import * as fs5 from "fs";
|
|
2035
|
+
import * as path3 from "path";
|
|
2036
|
+
var SkillsTool = class {
|
|
2037
|
+
/**
|
|
2038
|
+
* Skills directory
|
|
2039
|
+
*/
|
|
2040
|
+
skillsDir;
|
|
2041
|
+
constructor(skillsDir = SKILLS_DIR) {
|
|
2042
|
+
this.skillsDir = skillsDir;
|
|
2043
|
+
}
|
|
2044
|
+
/**
|
|
2045
|
+
* Read the content of the skill description file.
|
|
2046
|
+
*
|
|
2047
|
+
* Args:
|
|
2048
|
+
* name (str): The name of the skill
|
|
2049
|
+
*
|
|
2050
|
+
* Returns:
|
|
2051
|
+
* str: The content of the skill description file
|
|
2052
|
+
*/
|
|
2053
|
+
async readSkillDescription(name) {
|
|
2054
|
+
const skillsPath = path3.join(this.skillsDir, name, "SKILL.md");
|
|
2055
|
+
if (!fs5.existsSync(skillsPath)) {
|
|
2056
|
+
throw new ToolExecutionError(`Skill description file not found: ${skillsPath}`);
|
|
2057
|
+
}
|
|
2058
|
+
const fileEditor = new FileEditor();
|
|
2059
|
+
const skillDescription = await fileEditor.read(skillsPath);
|
|
2060
|
+
const formattedDescription = `<${name}.SkillDescription>${skillDescription}</${name}.SkillDescription>`;
|
|
2061
|
+
logger.debug(formattedDescription);
|
|
2062
|
+
return formattedDescription;
|
|
2063
|
+
}
|
|
2064
|
+
/**
|
|
2065
|
+
* List the skills in the skills root directory
|
|
2066
|
+
*/
|
|
2067
|
+
async listSkills() {
|
|
2068
|
+
if (!fs5.existsSync(this.skillsDir)) {
|
|
2069
|
+
throw new ToolExecutionError(`Skills path not found: ${this.skillsDir}`);
|
|
2070
|
+
}
|
|
2071
|
+
const skillsDict = {};
|
|
2072
|
+
const items = fs5.readdirSync(this.skillsDir);
|
|
2073
|
+
for (const skillName of items) {
|
|
2074
|
+
const skillPath = path3.join(this.skillsDir, skillName, "SKILL.md");
|
|
2075
|
+
if (!fs5.existsSync(skillPath)) {
|
|
2076
|
+
continue;
|
|
2077
|
+
}
|
|
2078
|
+
const fileEditor = new FileEditor();
|
|
2079
|
+
try {
|
|
2080
|
+
const skillContent = await fileEditor.read(skillPath, "1-3");
|
|
2081
|
+
skillsDict[skillName] = skillContent;
|
|
2082
|
+
} catch (error) {
|
|
2083
|
+
logger.warn(`Failed to read skill ${skillName}:`, error);
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
const skillsInfo = "<SkillsInformation>" + Object.entries(skillsDict).map(([skillName, content]) => `<${skillName}> ${content} </${skillName}>`).join("\n") + "</SkillsInformation>";
|
|
2087
|
+
logger.debug(skillsInfo);
|
|
2088
|
+
return skillsInfo;
|
|
2089
|
+
}
|
|
2090
|
+
};
|
|
2091
|
+
SkillsTool = __decorateClass([
|
|
2092
|
+
tools({
|
|
2093
|
+
readSkillDescription: {
|
|
2094
|
+
description: "Read the content of the skill description file.",
|
|
2095
|
+
params: [{ name: "name", type: "str", description: "The name of the skill" }],
|
|
2096
|
+
returns: { type: "str", description: "The content of the skill description file" }
|
|
2097
|
+
},
|
|
2098
|
+
listSkills: {
|
|
2099
|
+
description: "List the skills in the skills root directory",
|
|
2100
|
+
returns: { type: "str", description: "The list of skills with their descriptions" }
|
|
2101
|
+
}
|
|
2102
|
+
})
|
|
2103
|
+
], SkillsTool);
|
|
2104
|
+
|
|
2105
|
+
// src/tools/gitTool.ts
|
|
2106
|
+
import { exec } from "child_process";
|
|
2107
|
+
import * as fs6 from "fs";
|
|
2108
|
+
import * as path4 from "path";
|
|
2109
|
+
function quote(str) {
|
|
2110
|
+
if (/^[a-zA-Z0-9_\-.\/]+$/.test(str)) {
|
|
2111
|
+
return str;
|
|
2112
|
+
}
|
|
2113
|
+
return `'${str.replace(/'/g, "'\\''")}'`;
|
|
2114
|
+
}
|
|
2115
|
+
var gitToolConfig = {
|
|
2116
|
+
init: {
|
|
2117
|
+
description: "Initialize a git repository.",
|
|
2118
|
+
params: [
|
|
2119
|
+
{
|
|
2120
|
+
name: "cwd",
|
|
2121
|
+
type: "string",
|
|
2122
|
+
description: "Working directory, defaults to current directory if None",
|
|
2123
|
+
optional: true
|
|
2124
|
+
},
|
|
2125
|
+
{
|
|
2126
|
+
name: "bare",
|
|
2127
|
+
type: "boolean",
|
|
2128
|
+
description: "Create a bare repository",
|
|
2129
|
+
optional: true
|
|
2130
|
+
},
|
|
2131
|
+
{
|
|
2132
|
+
name: "initialBranch",
|
|
2133
|
+
type: "string",
|
|
2134
|
+
description: "Initial branch name",
|
|
2135
|
+
optional: true
|
|
2136
|
+
},
|
|
2137
|
+
{
|
|
2138
|
+
name: "config",
|
|
2139
|
+
type: "object",
|
|
2140
|
+
description: "Git config options {key: value}",
|
|
2141
|
+
optional: true
|
|
2142
|
+
}
|
|
2143
|
+
],
|
|
2144
|
+
returns: { type: "string", description: "Initialization result" }
|
|
2145
|
+
},
|
|
2146
|
+
status: {
|
|
2147
|
+
description: "View git repository status, showing changes in working directory and staging area.",
|
|
2148
|
+
params: [
|
|
2149
|
+
{
|
|
2150
|
+
name: "cwd",
|
|
2151
|
+
type: "string",
|
|
2152
|
+
description: "Working directory, defaults to current directory if None",
|
|
2153
|
+
optional: true
|
|
2154
|
+
},
|
|
2155
|
+
{
|
|
2156
|
+
name: "config",
|
|
2157
|
+
type: "object",
|
|
2158
|
+
description: "Git config options {key: value}",
|
|
2159
|
+
optional: true
|
|
2160
|
+
}
|
|
2161
|
+
],
|
|
2162
|
+
returns: { type: "string", description: "Output of git status" }
|
|
2163
|
+
},
|
|
2164
|
+
add: {
|
|
2165
|
+
description: "Add files to staging area.",
|
|
2166
|
+
params: [
|
|
2167
|
+
{
|
|
2168
|
+
name: "files",
|
|
2169
|
+
type: "string",
|
|
2170
|
+
description: 'File paths to add, can be single file, multiple files (space separated), "." (all files) or path patterns'
|
|
2171
|
+
},
|
|
2172
|
+
{
|
|
2173
|
+
name: "cwd",
|
|
2174
|
+
type: "string",
|
|
2175
|
+
description: "Working directory, defaults to current directory if None",
|
|
2176
|
+
optional: true
|
|
2177
|
+
},
|
|
2178
|
+
{
|
|
2179
|
+
name: "config",
|
|
2180
|
+
type: "object",
|
|
2181
|
+
description: "Git config options {key: value}",
|
|
2182
|
+
optional: true
|
|
2183
|
+
}
|
|
2184
|
+
],
|
|
2185
|
+
returns: { type: "string", description: "Operation result description" }
|
|
2186
|
+
},
|
|
2187
|
+
commit: {
|
|
2188
|
+
description: "Commit changes in staging area.",
|
|
2189
|
+
params: [
|
|
2190
|
+
{ name: "message", type: "string", description: "Commit message" },
|
|
2191
|
+
{
|
|
2192
|
+
name: "cwd",
|
|
2193
|
+
type: "string",
|
|
2194
|
+
description: "Working directory, defaults to current directory if None",
|
|
2195
|
+
optional: true
|
|
2196
|
+
},
|
|
2197
|
+
{
|
|
2198
|
+
name: "allowEmpty",
|
|
2199
|
+
type: "boolean",
|
|
2200
|
+
description: "Whether to allow empty commits, default False",
|
|
2201
|
+
optional: true
|
|
2202
|
+
},
|
|
2203
|
+
{
|
|
2204
|
+
name: "config",
|
|
2205
|
+
type: "object",
|
|
2206
|
+
description: "Git config options {key: value}",
|
|
2207
|
+
optional: true
|
|
2208
|
+
}
|
|
2209
|
+
],
|
|
2210
|
+
returns: {
|
|
2211
|
+
type: "string",
|
|
2212
|
+
description: "Commit result, including commit hash and change statistics"
|
|
2213
|
+
}
|
|
2214
|
+
},
|
|
2215
|
+
push: {
|
|
2216
|
+
description: "Push local commits to remote repository.",
|
|
2217
|
+
params: [
|
|
2218
|
+
{
|
|
2219
|
+
name: "remote",
|
|
2220
|
+
type: "string",
|
|
2221
|
+
description: "Remote repository name, defaults to origin",
|
|
2222
|
+
optional: true
|
|
2223
|
+
},
|
|
2224
|
+
{
|
|
2225
|
+
name: "branch",
|
|
2226
|
+
type: "string",
|
|
2227
|
+
description: "Branch name to push, defaults to current branch",
|
|
2228
|
+
optional: true
|
|
2229
|
+
},
|
|
2230
|
+
{
|
|
2231
|
+
name: "cwd",
|
|
2232
|
+
type: "string",
|
|
2233
|
+
description: "Working directory, defaults to current directory if None",
|
|
2234
|
+
optional: true
|
|
2235
|
+
},
|
|
2236
|
+
{
|
|
2237
|
+
name: "force",
|
|
2238
|
+
type: "boolean",
|
|
2239
|
+
description: "Whether to force push, default False",
|
|
2240
|
+
optional: true
|
|
2241
|
+
},
|
|
2242
|
+
{
|
|
2243
|
+
name: "config",
|
|
2244
|
+
type: "object",
|
|
2245
|
+
description: "Git config options {key: value}",
|
|
2246
|
+
optional: true
|
|
2247
|
+
}
|
|
2248
|
+
],
|
|
2249
|
+
returns: { type: "string", description: "Push result description" }
|
|
2250
|
+
},
|
|
2251
|
+
pull: {
|
|
2252
|
+
description: "Pull latest changes from remote repository.",
|
|
2253
|
+
params: [
|
|
2254
|
+
{
|
|
2255
|
+
name: "remote",
|
|
2256
|
+
type: "string",
|
|
2257
|
+
description: "Remote repository name, defaults to origin",
|
|
2258
|
+
optional: true
|
|
2259
|
+
},
|
|
2260
|
+
{
|
|
2261
|
+
name: "branch",
|
|
2262
|
+
type: "string",
|
|
2263
|
+
description: "Branch name to pull, defaults to current branch",
|
|
2264
|
+
optional: true
|
|
2265
|
+
},
|
|
2266
|
+
{
|
|
2267
|
+
name: "cwd",
|
|
2268
|
+
type: "string",
|
|
2269
|
+
description: "Working directory, defaults to current directory if None",
|
|
2270
|
+
optional: true
|
|
2271
|
+
},
|
|
2272
|
+
{
|
|
2273
|
+
name: "config",
|
|
2274
|
+
type: "object",
|
|
2275
|
+
description: "Git config options {key: value}",
|
|
2276
|
+
optional: true
|
|
2277
|
+
}
|
|
2278
|
+
],
|
|
2279
|
+
returns: { type: "string", description: "Pull result description" }
|
|
2280
|
+
},
|
|
2281
|
+
checkout: {
|
|
2282
|
+
description: "Switch branch or checkout files.",
|
|
2283
|
+
params: [
|
|
2284
|
+
{
|
|
2285
|
+
name: "target",
|
|
2286
|
+
type: "string",
|
|
2287
|
+
description: "Target branch name or file path"
|
|
2288
|
+
},
|
|
2289
|
+
{
|
|
2290
|
+
name: "cwd",
|
|
2291
|
+
type: "string",
|
|
2292
|
+
description: "Working directory, defaults to current directory if None",
|
|
2293
|
+
optional: true
|
|
2294
|
+
},
|
|
2295
|
+
{
|
|
2296
|
+
name: "createBranch",
|
|
2297
|
+
type: "boolean",
|
|
2298
|
+
description: "Whether to create new branch if target doesn't exist, default True",
|
|
2299
|
+
optional: true
|
|
2300
|
+
},
|
|
2301
|
+
{
|
|
2302
|
+
name: "config",
|
|
2303
|
+
type: "object",
|
|
2304
|
+
description: "Git config options {key: value}",
|
|
2305
|
+
optional: true
|
|
2306
|
+
}
|
|
2307
|
+
],
|
|
2308
|
+
returns: { type: "string", description: "Checkout result description" }
|
|
2309
|
+
},
|
|
2310
|
+
branch: {
|
|
2311
|
+
description: "List all branches or create/delete branch.",
|
|
2312
|
+
params: [
|
|
2313
|
+
{
|
|
2314
|
+
name: "name",
|
|
2315
|
+
type: "string",
|
|
2316
|
+
description: "Branch name, lists all branches if None",
|
|
2317
|
+
optional: true
|
|
2318
|
+
},
|
|
2319
|
+
{
|
|
2320
|
+
name: "cwd",
|
|
2321
|
+
type: "string",
|
|
2322
|
+
description: "Working directory, defaults to current directory if None",
|
|
2323
|
+
optional: true
|
|
2324
|
+
},
|
|
2325
|
+
{
|
|
2326
|
+
name: "delete",
|
|
2327
|
+
type: "boolean",
|
|
2328
|
+
description: "Whether to delete branch, default False",
|
|
2329
|
+
optional: true
|
|
2330
|
+
},
|
|
2331
|
+
{
|
|
2332
|
+
name: "config",
|
|
2333
|
+
type: "object",
|
|
2334
|
+
description: "Git config options {key: value}",
|
|
2335
|
+
optional: true
|
|
2336
|
+
}
|
|
2337
|
+
],
|
|
2338
|
+
returns: { type: "string", description: "Branch operation result" }
|
|
2339
|
+
},
|
|
2340
|
+
log: {
|
|
2341
|
+
description: "View commit history.",
|
|
2342
|
+
params: [
|
|
2343
|
+
{
|
|
2344
|
+
name: "cwd",
|
|
2345
|
+
type: "string",
|
|
2346
|
+
description: "Working directory, defaults to current directory if None",
|
|
2347
|
+
optional: true
|
|
2348
|
+
},
|
|
2349
|
+
{
|
|
2350
|
+
name: "maxCount",
|
|
2351
|
+
type: "integer",
|
|
2352
|
+
description: "Maximum number of commits to show",
|
|
2353
|
+
optional: true
|
|
2354
|
+
},
|
|
2355
|
+
{
|
|
2356
|
+
name: "oneline",
|
|
2357
|
+
type: "boolean",
|
|
2358
|
+
description: "Whether to show in oneline format, default False",
|
|
2359
|
+
optional: true
|
|
2360
|
+
},
|
|
2361
|
+
{
|
|
2362
|
+
name: "filePath",
|
|
2363
|
+
type: "string",
|
|
2364
|
+
description: "View commit history for specific file",
|
|
2365
|
+
optional: true
|
|
2366
|
+
},
|
|
2367
|
+
{
|
|
2368
|
+
name: "config",
|
|
2369
|
+
type: "object",
|
|
2370
|
+
description: "Git config options {key: value}",
|
|
2371
|
+
optional: true
|
|
2372
|
+
}
|
|
2373
|
+
],
|
|
2374
|
+
returns: { type: "string", description: "Commit history" }
|
|
2375
|
+
},
|
|
2376
|
+
diff: {
|
|
2377
|
+
description: "View file differences.",
|
|
2378
|
+
params: [
|
|
2379
|
+
{
|
|
2380
|
+
name: "cwd",
|
|
2381
|
+
type: "string",
|
|
2382
|
+
description: "Working directory, defaults to current directory if None",
|
|
2383
|
+
optional: true
|
|
2384
|
+
},
|
|
2385
|
+
{
|
|
2386
|
+
name: "staged",
|
|
2387
|
+
type: "boolean",
|
|
2388
|
+
description: "Whether to view staged changes, default False",
|
|
2389
|
+
optional: true
|
|
2390
|
+
},
|
|
2391
|
+
{
|
|
2392
|
+
name: "filePath",
|
|
2393
|
+
type: "string",
|
|
2394
|
+
description: "View diff for specific file",
|
|
2395
|
+
optional: true
|
|
2396
|
+
},
|
|
2397
|
+
{
|
|
2398
|
+
name: "commit1",
|
|
2399
|
+
type: "string",
|
|
2400
|
+
description: "First commit hash or branch name",
|
|
2401
|
+
optional: true
|
|
2402
|
+
},
|
|
2403
|
+
{
|
|
2404
|
+
name: "commit2",
|
|
2405
|
+
type: "string",
|
|
2406
|
+
description: "Second commit hash or branch name",
|
|
2407
|
+
optional: true
|
|
2408
|
+
},
|
|
2409
|
+
{
|
|
2410
|
+
name: "config",
|
|
2411
|
+
type: "object",
|
|
2412
|
+
description: "Git config options {key: value}",
|
|
2413
|
+
optional: true
|
|
2414
|
+
}
|
|
2415
|
+
],
|
|
2416
|
+
returns: { type: "string", description: "Diff content" }
|
|
2417
|
+
},
|
|
2418
|
+
remote: {
|
|
2419
|
+
description: "Manage remote repositories.",
|
|
2420
|
+
params: [
|
|
2421
|
+
{
|
|
2422
|
+
name: "action",
|
|
2423
|
+
type: "string",
|
|
2424
|
+
description: 'Action type: "list", "add", "remove", "set-url", default "list"',
|
|
2425
|
+
optional: true
|
|
2426
|
+
},
|
|
2427
|
+
{
|
|
2428
|
+
name: "name",
|
|
2429
|
+
type: "string",
|
|
2430
|
+
description: 'Remote repository name, usually "origin"',
|
|
2431
|
+
optional: true
|
|
2432
|
+
},
|
|
2433
|
+
{
|
|
2434
|
+
name: "url",
|
|
2435
|
+
type: "string",
|
|
2436
|
+
description: "Remote repository URL (for add or set-url)",
|
|
2437
|
+
optional: true
|
|
2438
|
+
},
|
|
2439
|
+
{
|
|
2440
|
+
name: "cwd",
|
|
2441
|
+
type: "string",
|
|
2442
|
+
description: "Working directory, defaults to current directory if None",
|
|
2443
|
+
optional: true
|
|
2444
|
+
},
|
|
2445
|
+
{
|
|
2446
|
+
name: "config",
|
|
2447
|
+
type: "object",
|
|
2448
|
+
description: "Git config options {key: value}",
|
|
2449
|
+
optional: true
|
|
2450
|
+
}
|
|
2451
|
+
],
|
|
2452
|
+
returns: { type: "string", description: "Operation result" }
|
|
2453
|
+
},
|
|
2454
|
+
clone: {
|
|
2455
|
+
description: "Clone remote repository to local.",
|
|
2456
|
+
params: [
|
|
2457
|
+
{ name: "url", type: "string", description: "Remote repository URL" },
|
|
2458
|
+
{
|
|
2459
|
+
name: "directory",
|
|
2460
|
+
type: "string",
|
|
2461
|
+
description: "Local directory name, defaults to repository name if None",
|
|
2462
|
+
optional: true
|
|
2463
|
+
},
|
|
2464
|
+
{
|
|
2465
|
+
name: "cwd",
|
|
2466
|
+
type: "string",
|
|
2467
|
+
description: "Working directory, defaults to current directory if None",
|
|
2468
|
+
optional: true
|
|
2469
|
+
},
|
|
2470
|
+
{
|
|
2471
|
+
name: "config",
|
|
2472
|
+
type: "object",
|
|
2473
|
+
description: "Git config options {key: value}",
|
|
2474
|
+
optional: true
|
|
2475
|
+
}
|
|
2476
|
+
],
|
|
2477
|
+
returns: { type: "string", description: "Clone result description" }
|
|
2478
|
+
},
|
|
2479
|
+
fetch: {
|
|
2480
|
+
description: "Fetch latest changes from remote without merging.",
|
|
2481
|
+
params: [
|
|
2482
|
+
{
|
|
2483
|
+
name: "remote",
|
|
2484
|
+
type: "string",
|
|
2485
|
+
description: "Remote repository name, defaults to origin",
|
|
2486
|
+
optional: true
|
|
2487
|
+
},
|
|
2488
|
+
{
|
|
2489
|
+
name: "cwd",
|
|
2490
|
+
type: "string",
|
|
2491
|
+
description: "Working directory, defaults to current directory if None",
|
|
2492
|
+
optional: true
|
|
2493
|
+
},
|
|
2494
|
+
{
|
|
2495
|
+
name: "config",
|
|
2496
|
+
type: "object",
|
|
2497
|
+
description: "Git config options {key: value}",
|
|
2498
|
+
optional: true
|
|
2499
|
+
}
|
|
2500
|
+
],
|
|
2501
|
+
returns: { type: "string", description: "Fetch result description" }
|
|
2502
|
+
},
|
|
2503
|
+
merge: {
|
|
2504
|
+
description: "Merge specified branch into current branch.",
|
|
2505
|
+
params: [
|
|
2506
|
+
{ name: "branch", type: "string", description: "Branch name to merge" },
|
|
2507
|
+
{
|
|
2508
|
+
name: "cwd",
|
|
2509
|
+
type: "string",
|
|
2510
|
+
description: "Working directory, defaults to current directory if None",
|
|
2511
|
+
optional: true
|
|
2512
|
+
},
|
|
2513
|
+
{
|
|
2514
|
+
name: "config",
|
|
2515
|
+
type: "object",
|
|
2516
|
+
description: "Git config options {key: value}",
|
|
2517
|
+
optional: true
|
|
2518
|
+
}
|
|
2519
|
+
],
|
|
2520
|
+
returns: { type: "string", description: "Merge result description" }
|
|
2521
|
+
},
|
|
2522
|
+
show: {
|
|
2523
|
+
description: "Show detailed information about git objects (commits, tags, trees, etc).",
|
|
2524
|
+
params: [
|
|
2525
|
+
{
|
|
2526
|
+
name: "object",
|
|
2527
|
+
type: "string",
|
|
2528
|
+
description: "Object to show (commit hash, tag, branch, etc.), defaults to HEAD",
|
|
2529
|
+
optional: true
|
|
2530
|
+
},
|
|
2531
|
+
{
|
|
2532
|
+
name: "cwd",
|
|
2533
|
+
type: "string",
|
|
2534
|
+
description: "Working directory",
|
|
2535
|
+
optional: true
|
|
2536
|
+
},
|
|
2537
|
+
{
|
|
2538
|
+
name: "stat",
|
|
2539
|
+
type: "boolean",
|
|
2540
|
+
description: "Show status info",
|
|
2541
|
+
optional: true
|
|
2542
|
+
},
|
|
2543
|
+
{
|
|
2544
|
+
name: "nameOnly",
|
|
2545
|
+
type: "boolean",
|
|
2546
|
+
description: "Show only filenames",
|
|
2547
|
+
optional: true
|
|
2548
|
+
},
|
|
2549
|
+
{
|
|
2550
|
+
name: "format",
|
|
2551
|
+
type: "string",
|
|
2552
|
+
description: "Custom format string",
|
|
2553
|
+
optional: true
|
|
2554
|
+
},
|
|
2555
|
+
{
|
|
2556
|
+
name: "config",
|
|
2557
|
+
type: "object",
|
|
2558
|
+
description: "Git config options {key: value}",
|
|
2559
|
+
optional: true
|
|
2560
|
+
}
|
|
2561
|
+
],
|
|
2562
|
+
returns: { type: "string", description: "Object details" }
|
|
2563
|
+
},
|
|
2564
|
+
tag: {
|
|
2565
|
+
description: "Create, list or delete tags.",
|
|
2566
|
+
params: [
|
|
2567
|
+
{
|
|
2568
|
+
name: "name",
|
|
2569
|
+
type: "string",
|
|
2570
|
+
description: "Tag name, lists all tags if None",
|
|
2571
|
+
optional: true
|
|
2572
|
+
},
|
|
2573
|
+
{
|
|
2574
|
+
name: "cwd",
|
|
2575
|
+
type: "string",
|
|
2576
|
+
description: "Working directory",
|
|
2577
|
+
optional: true
|
|
2578
|
+
},
|
|
2579
|
+
{
|
|
2580
|
+
name: "message",
|
|
2581
|
+
type: "string",
|
|
2582
|
+
description: "Tag message",
|
|
2583
|
+
optional: true
|
|
2584
|
+
},
|
|
2585
|
+
{
|
|
2586
|
+
name: "delete",
|
|
2587
|
+
type: "boolean",
|
|
2588
|
+
description: "Delete tag",
|
|
2589
|
+
optional: true
|
|
2590
|
+
},
|
|
2591
|
+
{
|
|
2592
|
+
name: "listAll",
|
|
2593
|
+
type: "boolean",
|
|
2594
|
+
description: "List all tags",
|
|
2595
|
+
optional: true
|
|
2596
|
+
},
|
|
2597
|
+
{
|
|
2598
|
+
name: "annotate",
|
|
2599
|
+
type: "boolean",
|
|
2600
|
+
description: "Create annotated tag",
|
|
2601
|
+
optional: true
|
|
2602
|
+
},
|
|
2603
|
+
{
|
|
2604
|
+
name: "config",
|
|
2605
|
+
type: "object",
|
|
2606
|
+
description: "Git config options {key: value}",
|
|
2607
|
+
optional: true
|
|
2608
|
+
}
|
|
2609
|
+
],
|
|
2610
|
+
returns: { type: "string", description: "Tag operation result" }
|
|
2611
|
+
},
|
|
2612
|
+
revert: {
|
|
2613
|
+
description: "Revert existing commits.",
|
|
2614
|
+
params: [
|
|
2615
|
+
{
|
|
2616
|
+
name: "commit",
|
|
2617
|
+
type: "string",
|
|
2618
|
+
description: "Commit hash to revert"
|
|
2619
|
+
},
|
|
2620
|
+
{
|
|
2621
|
+
name: "cwd",
|
|
2622
|
+
type: "string",
|
|
2623
|
+
description: "Working directory",
|
|
2624
|
+
optional: true
|
|
2625
|
+
},
|
|
2626
|
+
{
|
|
2627
|
+
name: "noCommit",
|
|
2628
|
+
type: "boolean",
|
|
2629
|
+
description: "Revert changes but do not commit",
|
|
2630
|
+
optional: true
|
|
2631
|
+
},
|
|
2632
|
+
{
|
|
2633
|
+
name: "noEdit",
|
|
2634
|
+
type: "boolean",
|
|
2635
|
+
description: "Do not edit commit message, default True",
|
|
2636
|
+
optional: true
|
|
2637
|
+
},
|
|
2638
|
+
{
|
|
2639
|
+
name: "mainline",
|
|
2640
|
+
type: "integer",
|
|
2641
|
+
description: "Mainline parent number for merge commits",
|
|
2642
|
+
optional: true
|
|
2643
|
+
},
|
|
2644
|
+
{
|
|
2645
|
+
name: "config",
|
|
2646
|
+
type: "object",
|
|
2647
|
+
description: "Git config options {key: value}",
|
|
2648
|
+
optional: true
|
|
2649
|
+
}
|
|
2650
|
+
],
|
|
2651
|
+
returns: { type: "string", description: "Revert result" }
|
|
2652
|
+
}
|
|
2653
|
+
};
|
|
2654
|
+
var GitTool = class {
|
|
2655
|
+
/**
|
|
2656
|
+
* Helper method to execute git commands.
|
|
2657
|
+
*/
|
|
2658
|
+
async _executeGitCommand(command, cwd, timeout = 3e4, config2) {
|
|
2659
|
+
const workDir = cwd || process.cwd();
|
|
2660
|
+
if (!fs6.existsSync(workDir)) {
|
|
2661
|
+
throw new ToolExecutionError(`\u5DE5\u4F5C\u76EE\u5F55\u4E0D\u5B58\u5728: ${workDir}`);
|
|
2662
|
+
}
|
|
2663
|
+
const gitDir = path4.join(workDir, ".git");
|
|
2664
|
+
const isInitOrClone = command.includes("clone") || command.includes("init");
|
|
2665
|
+
if (!fs6.existsSync(gitDir) && !isInitOrClone) {
|
|
2666
|
+
throw new ToolExecutionError(`\u5F53\u524D\u76EE\u5F55\u4E0D\u662F git \u4ED3\u5E93: ${workDir}`);
|
|
2667
|
+
}
|
|
2668
|
+
let gitCmd = "git";
|
|
2669
|
+
if (config2) {
|
|
2670
|
+
for (const [key, value] of Object.entries(config2)) {
|
|
2671
|
+
gitCmd += ` -c ${key}=${quote(value)}`;
|
|
2672
|
+
}
|
|
2673
|
+
}
|
|
2674
|
+
gitCmd += ` ${command}`;
|
|
2675
|
+
return new Promise((resolve2, reject) => {
|
|
2676
|
+
exec(gitCmd, { cwd: workDir, timeout, encoding: "utf-8" }, (error, stdout, stderr) => {
|
|
2677
|
+
const stdoutText = stdout ? stdout.trim() : "";
|
|
2678
|
+
const stderrText = stderr ? stderr.trim() : "";
|
|
2679
|
+
if (error) {
|
|
2680
|
+
if (error.signal === "SIGTERM") {
|
|
2681
|
+
reject(new ToolExecutionError(`Git \u547D\u4EE4\u6267\u884C\u8D85\u65F6\uFF08\u8D85\u8FC7 ${timeout / 1e3} \u79D2\uFF09`));
|
|
2682
|
+
return;
|
|
2683
|
+
}
|
|
2684
|
+
const errorMsg = stderrText || stdoutText || error.message;
|
|
2685
|
+
reject(new ToolExecutionError(`Git \u547D\u4EE4\u6267\u884C\u5931\u8D25: ${errorMsg}`));
|
|
2686
|
+
return;
|
|
2687
|
+
}
|
|
2688
|
+
resolve2(stdoutText || "\u547D\u4EE4\u6267\u884C\u6210\u529F\uFF0C\u65E0\u8F93\u51FA");
|
|
2689
|
+
});
|
|
2690
|
+
});
|
|
2691
|
+
}
|
|
2692
|
+
/**
|
|
2693
|
+
* Get current branch name.
|
|
2694
|
+
*/
|
|
2695
|
+
async _getCurrentBranch(cwd, config2) {
|
|
2696
|
+
const result = await this._executeGitCommand("rev-parse --abbrev-ref HEAD", cwd, 3e4, config2);
|
|
2697
|
+
return result.trim();
|
|
2698
|
+
}
|
|
2699
|
+
async init(cwd, bare = false, initialBranch, config2) {
|
|
2700
|
+
let command = "init";
|
|
2701
|
+
if (bare) command += " --bare";
|
|
2702
|
+
if (initialBranch) command += ` --initial-branch=${initialBranch}`;
|
|
2703
|
+
const result = await this._executeGitCommand(command, cwd, 3e4, config2);
|
|
2704
|
+
return `Git \u4ED3\u5E93\u521D\u59CB\u5316\u5B8C\u6210
|
|
2705
|
+
${result}`;
|
|
2706
|
+
}
|
|
2707
|
+
async status(cwd, config2) {
|
|
2708
|
+
return await this._executeGitCommand("status", cwd, 3e4, config2);
|
|
2709
|
+
}
|
|
2710
|
+
async add(files, cwd, config2) {
|
|
2711
|
+
const result = await this._executeGitCommand(`add ${files}`, cwd, 3e4, config2);
|
|
2712
|
+
return `\u6210\u529F\u5C06\u6587\u4EF6\u6DFB\u52A0\u5230\u6682\u5B58\u533A: ${files}
|
|
2713
|
+
${result}`;
|
|
2714
|
+
}
|
|
2715
|
+
async commit(message, cwd, allowEmpty = false, config2) {
|
|
2716
|
+
const allowEmptyFlag = allowEmpty ? " --allow-empty" : "";
|
|
2717
|
+
const result = await this._executeGitCommand(`commit -m ${quote(message)}${allowEmptyFlag}`, cwd, 3e4, config2);
|
|
2718
|
+
return `\u63D0\u4EA4\u6210\u529F
|
|
2719
|
+
${result}`;
|
|
2720
|
+
}
|
|
2721
|
+
async push(remote = "origin", branch, cwd, force = false, config2) {
|
|
2722
|
+
const targetBranch = branch || await this._getCurrentBranch(cwd, config2);
|
|
2723
|
+
const forceFlag = force ? " --force" : "";
|
|
2724
|
+
const result = await this._executeGitCommand(`push${forceFlag} ${remote} ${targetBranch}`, cwd, 6e4, config2);
|
|
2725
|
+
return `\u6210\u529F\u63A8\u9001\u5230 ${remote}/${targetBranch}
|
|
2726
|
+
${result}`;
|
|
2727
|
+
}
|
|
2728
|
+
async pull(remote = "origin", branch, cwd, config2) {
|
|
2729
|
+
const targetBranch = branch || await this._getCurrentBranch(cwd, config2);
|
|
2730
|
+
const result = await this._executeGitCommand(`pull ${remote} ${targetBranch}`, cwd, 6e4, config2);
|
|
2731
|
+
return `\u6210\u529F\u4ECE ${remote}/${targetBranch} \u62C9\u53D6\u66F4\u6539
|
|
2732
|
+
${result}`;
|
|
2733
|
+
}
|
|
2734
|
+
async checkout(target, cwd, createBranch = true, config2) {
|
|
2735
|
+
const createFlag = createBranch ? " -b" : "";
|
|
2736
|
+
const result = await this._executeGitCommand(`checkout${createFlag} ${target}`, cwd, 3e4, config2);
|
|
2737
|
+
return `\u6210\u529F\u5207\u6362\u5230 ${target}
|
|
2738
|
+
${result}`;
|
|
2739
|
+
}
|
|
2740
|
+
async branch(name, cwd, deleteBranch = false, config2) {
|
|
2741
|
+
if (!name) {
|
|
2742
|
+
const result = await this._executeGitCommand("branch -a", cwd, 3e4, config2);
|
|
2743
|
+
return `\u6240\u6709\u5206\u652F\u5217\u8868:
|
|
2744
|
+
${result}`;
|
|
2745
|
+
} else if (deleteBranch) {
|
|
2746
|
+
const result = await this._executeGitCommand(`branch -d ${name}`, cwd, 3e4, config2);
|
|
2747
|
+
return `\u6210\u529F\u5220\u9664\u5206\u652F ${name}
|
|
2748
|
+
${result}`;
|
|
2749
|
+
} else {
|
|
2750
|
+
const result = await this._executeGitCommand(`branch ${name}`, cwd, 3e4, config2);
|
|
2751
|
+
return `\u6210\u529F\u521B\u5EFA\u5206\u652F ${name}
|
|
2752
|
+
${result}`;
|
|
2753
|
+
}
|
|
2754
|
+
}
|
|
2755
|
+
async log(cwd, maxCount, oneline = false, filePath, config2) {
|
|
2756
|
+
let command = "log";
|
|
2757
|
+
if (oneline) command += " --oneline";
|
|
2758
|
+
if (maxCount) command += ` -${maxCount}`;
|
|
2759
|
+
if (filePath) command += ` -- ${filePath}`;
|
|
2760
|
+
const result = await this._executeGitCommand(command, cwd, 3e4, config2);
|
|
2761
|
+
return `\u63D0\u4EA4\u5386\u53F2:
|
|
2762
|
+
${result}`;
|
|
2763
|
+
}
|
|
2764
|
+
async diff(cwd, staged = false, filePath, commit1, commit2, config2) {
|
|
2765
|
+
let command = "diff";
|
|
2766
|
+
if (staged) command += " --staged";
|
|
2767
|
+
if (commit1 && commit2) {
|
|
2768
|
+
command += ` ${commit1} ${commit2}`;
|
|
2769
|
+
} else if (commit1) {
|
|
2770
|
+
command += ` ${commit1}`;
|
|
2771
|
+
}
|
|
2772
|
+
if (filePath) command += ` -- ${filePath}`;
|
|
2773
|
+
const result = await this._executeGitCommand(command, cwd, 3e4, config2);
|
|
2774
|
+
return `\u5DEE\u5F02\u5185\u5BB9:
|
|
2775
|
+
${result}`;
|
|
2776
|
+
}
|
|
2777
|
+
async remote(action = "list", name, url, cwd, config2) {
|
|
2778
|
+
if (action === "list") {
|
|
2779
|
+
const result = await this._executeGitCommand("remote -v", cwd, 3e4, config2);
|
|
2780
|
+
return `\u8FDC\u7A0B\u4ED3\u5E93\u5217\u8868:
|
|
2781
|
+
${result}`;
|
|
2782
|
+
} else if (action === "add") {
|
|
2783
|
+
if (!name || !url) throw new ToolExecutionError("\u6DFB\u52A0\u8FDC\u7A0B\u4ED3\u5E93\u9700\u8981\u63D0\u4F9B name \u548C url \u53C2\u6570");
|
|
2784
|
+
const result = await this._executeGitCommand(`remote add ${name} ${url}`, cwd, 3e4, config2);
|
|
2785
|
+
return `\u6210\u529F\u6DFB\u52A0\u8FDC\u7A0B\u4ED3\u5E93 ${name}: ${url}
|
|
2786
|
+
${result}`;
|
|
2787
|
+
} else if (action === "remove") {
|
|
2788
|
+
if (!name) throw new ToolExecutionError("\u5220\u9664\u8FDC\u7A0B\u4ED3\u5E93\u9700\u8981\u63D0\u4F9B name \u53C2\u6570");
|
|
2789
|
+
const result = await this._executeGitCommand(`remote remove ${name}`, cwd, 3e4, config2);
|
|
2790
|
+
return `\u6210\u529F\u5220\u9664\u8FDC\u7A0B\u4ED3\u5E93 ${name}
|
|
2791
|
+
${result}`;
|
|
2792
|
+
} else if (action === "set-url") {
|
|
2793
|
+
if (!name || !url) throw new ToolExecutionError("\u8BBE\u7F6E\u8FDC\u7A0B\u4ED3\u5E93 URL \u9700\u8981\u63D0\u4F9B name \u548C url \u53C2\u6570");
|
|
2794
|
+
const result = await this._executeGitCommand(`remote set-url ${name} ${url}`, cwd, 3e4, config2);
|
|
2795
|
+
return `\u6210\u529F\u8BBE\u7F6E\u8FDC\u7A0B\u4ED3\u5E93 ${name} \u7684 URL \u4E3A {url}
|
|
2796
|
+
${result}`;
|
|
2797
|
+
} else {
|
|
2798
|
+
throw new ToolExecutionError(`\u4E0D\u652F\u6301\u7684\u64CD\u4F5C\u7C7B\u578B: ${action}\uFF0C\u652F\u6301\u7684\u64CD\u4F5C: list, add, remove, set-url`);
|
|
2799
|
+
}
|
|
2800
|
+
}
|
|
2801
|
+
async clone(url, directory, cwd, config2) {
|
|
2802
|
+
const workDir = cwd || process.cwd();
|
|
2803
|
+
let command = `clone ${url}`;
|
|
2804
|
+
if (directory) command += ` ${directory}`;
|
|
2805
|
+
const result = await this._executeGitCommand(command, workDir, 12e4, config2);
|
|
2806
|
+
return `\u6210\u529F\u514B\u9686\u4ED3\u5E93 ${url}
|
|
2807
|
+
${result}`;
|
|
2808
|
+
}
|
|
2809
|
+
async fetch(remote = "origin", cwd, config2) {
|
|
2810
|
+
const result = await this._executeGitCommand(`fetch ${remote}`, cwd, 6e4, config2);
|
|
2811
|
+
return `\u6210\u529F\u4ECE ${remote} \u83B7\u53D6\u66F4\u65B0
|
|
2812
|
+
${result}`;
|
|
2813
|
+
}
|
|
2814
|
+
async merge(branch, cwd, config2) {
|
|
2815
|
+
const noFfFlag = "";
|
|
2816
|
+
const result = await this._executeGitCommand(`merge${noFfFlag} ${branch}`, cwd, 3e4, config2);
|
|
2817
|
+
return `\u6210\u529F\u5408\u5E76\u5206\u652F ${branch}
|
|
2818
|
+
${result}`;
|
|
2819
|
+
}
|
|
2820
|
+
async show(object, cwd, stat = false, nameOnly = false, format2, config2) {
|
|
2821
|
+
let command = "show";
|
|
2822
|
+
if (object) command += ` ${object}`;
|
|
2823
|
+
if (stat) command += " --stat";
|
|
2824
|
+
if (nameOnly) command += " --name-only";
|
|
2825
|
+
if (format2) command += ` --format=${format2}`;
|
|
2826
|
+
const result = await this._executeGitCommand(command, cwd, 3e4, config2);
|
|
2827
|
+
return `\u663E\u793A\u5BF9\u8C61\u4FE1\u606F:
|
|
2828
|
+
${result}`;
|
|
2829
|
+
}
|
|
2830
|
+
async tag(name, cwd, message, deleteTag = false, listAll = false, annotate = false, config2) {
|
|
2831
|
+
if (!name) {
|
|
2832
|
+
let command = "tag";
|
|
2833
|
+
if (listAll) command += " -l";
|
|
2834
|
+
const result = await this._executeGitCommand(command, cwd, 3e4, config2);
|
|
2835
|
+
return `\u6807\u7B7E\u5217\u8868:
|
|
2836
|
+
${result}`;
|
|
2837
|
+
} else if (deleteTag) {
|
|
2838
|
+
const result = await this._executeGitCommand(`tag -d ${name}`, cwd, 3e4, config2);
|
|
2839
|
+
return `\u6210\u529F\u5220\u9664\u6807\u7B7E ${name}
|
|
2840
|
+
${result}`;
|
|
2841
|
+
} else {
|
|
2842
|
+
let command = "tag";
|
|
2843
|
+
if (annotate || message) command += " -a";
|
|
2844
|
+
if (message) command += ` -m ${quote(message)}`;
|
|
2845
|
+
command += ` ${name}`;
|
|
2846
|
+
const result = await this._executeGitCommand(command, cwd, 3e4, config2);
|
|
2847
|
+
return `\u6210\u529F\u521B\u5EFA\u6807\u7B7E ${name}
|
|
2848
|
+
${result}`;
|
|
2849
|
+
}
|
|
2850
|
+
}
|
|
2851
|
+
async revert(commit, cwd, noCommit = false, noEdit = true, mainline, config2) {
|
|
2852
|
+
let command = "revert";
|
|
2853
|
+
if (noCommit) command += " --no-commit";
|
|
2854
|
+
if (noEdit) command += " --no-edit";
|
|
2855
|
+
if (mainline) command += ` -m ${mainline}`;
|
|
2856
|
+
command += ` ${commit}`;
|
|
2857
|
+
const result = await this._executeGitCommand(command, cwd, 3e4, config2);
|
|
2858
|
+
return `\u6210\u529F\u64A4\u9500\u63D0\u4EA4 ${commit}
|
|
2859
|
+
${result}`;
|
|
2860
|
+
}
|
|
2861
|
+
};
|
|
2862
|
+
GitTool = __decorateClass([
|
|
2863
|
+
tools(gitToolConfig)
|
|
2864
|
+
], GitTool);
|
|
2865
|
+
|
|
2866
|
+
// src/model.ts
|
|
2867
|
+
import OpenAI from "openai";
|
|
2868
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
2869
|
+
import { GoogleGenerativeAI } from "@google/generative-ai";
|
|
2870
|
+
var Model = class {
|
|
2871
|
+
modelName;
|
|
2872
|
+
config;
|
|
2873
|
+
openaiClient;
|
|
2874
|
+
anthropicClient;
|
|
2875
|
+
geminiClient;
|
|
2876
|
+
constructor(model) {
|
|
2877
|
+
if (typeof model === "string" || model === void 0) {
|
|
2878
|
+
this.modelName = model || "deepseek";
|
|
2879
|
+
this.config = loadModelConfig(this.modelName);
|
|
2880
|
+
} else {
|
|
2881
|
+
this.config = model;
|
|
2882
|
+
this.modelName = model.model || "deepseek";
|
|
2883
|
+
}
|
|
2884
|
+
this._initializeClient();
|
|
2885
|
+
}
|
|
2886
|
+
/**
|
|
2887
|
+
* Initialize the appropriate SDK client based on provider
|
|
2888
|
+
*/
|
|
2889
|
+
_initializeClient() {
|
|
2890
|
+
const provider = this.config.provider.toLowerCase();
|
|
2891
|
+
switch (provider) {
|
|
2892
|
+
case "openai":
|
|
2893
|
+
this.openaiClient = new OpenAI({
|
|
2894
|
+
apiKey: this.config.apiKey || process.env.OPENAI_API_KEY,
|
|
2895
|
+
baseURL: this.config.baseUrl,
|
|
2896
|
+
timeout: 6e4
|
|
2897
|
+
});
|
|
2898
|
+
break;
|
|
2899
|
+
case "deepseek":
|
|
2900
|
+
this.openaiClient = new OpenAI({
|
|
2901
|
+
apiKey: this.config.apiKey || process.env.DEEPSEEK_API_KEY,
|
|
2902
|
+
baseURL: this.config.baseUrl || "https://api.deepseek.com",
|
|
2903
|
+
timeout: 6e4
|
|
2904
|
+
});
|
|
2905
|
+
break;
|
|
2906
|
+
case "anthropic":
|
|
2907
|
+
if (this.config.baseUrl.endsWith("/v1") || this.config.baseUrl.endsWith("v1/")) {
|
|
2908
|
+
this.config.baseUrl = this.config.baseUrl.slice(0, -3);
|
|
2909
|
+
}
|
|
2910
|
+
this.anthropicClient = new Anthropic({
|
|
2911
|
+
apiKey: this.config.apiKey || process.env.ANTHROPIC_API_KEY,
|
|
2912
|
+
baseURL: this.config.baseUrl,
|
|
2913
|
+
timeout: 6e4
|
|
2914
|
+
});
|
|
2915
|
+
break;
|
|
2916
|
+
case "gemini":
|
|
2917
|
+
const apiKey = this.config.apiKey || process.env.GEMINI_API_KEY || "";
|
|
2918
|
+
this.geminiClient = new GoogleGenerativeAI(apiKey);
|
|
2919
|
+
break;
|
|
2920
|
+
default:
|
|
2921
|
+
throw new ModelError(`Unsupported provider: ${provider}`);
|
|
2922
|
+
}
|
|
2923
|
+
}
|
|
2924
|
+
/**
|
|
2925
|
+
* Asynchronous chat completion
|
|
2926
|
+
*/
|
|
2927
|
+
async achat(messages, tools2, stream = true) {
|
|
2928
|
+
const provider = this.config.provider.toLowerCase();
|
|
2929
|
+
const useStream = stream !== void 0 ? stream : !!this.config.stream;
|
|
2930
|
+
try {
|
|
2931
|
+
switch (provider) {
|
|
2932
|
+
case "openai":
|
|
2933
|
+
case "deepseek":
|
|
2934
|
+
return await this._callOpenAICompatible(messages, tools2, useStream);
|
|
2935
|
+
case "anthropic":
|
|
2936
|
+
return await this._callAnthropic(messages, tools2, useStream);
|
|
2937
|
+
case "gemini":
|
|
2938
|
+
return await this._callGemini(messages, tools2, useStream);
|
|
2939
|
+
default:
|
|
2940
|
+
throw new ModelError(`Unsupported provider: ${provider}`);
|
|
2941
|
+
}
|
|
2942
|
+
} catch (error) {
|
|
2943
|
+
logger.error(`Model call failed: ${error}`);
|
|
2944
|
+
throw error;
|
|
2945
|
+
}
|
|
2946
|
+
}
|
|
2947
|
+
/**
|
|
2948
|
+
* Call OpenAI-compatible API (OpenAI, DeepSeek, etc.)
|
|
2949
|
+
*/
|
|
2950
|
+
async _callOpenAICompatible(messages, tools2, stream = false) {
|
|
2951
|
+
if (!this.openaiClient) {
|
|
2952
|
+
throw new ModelError("OpenAI client not initialized");
|
|
2953
|
+
}
|
|
2954
|
+
try {
|
|
2955
|
+
const params = {
|
|
2956
|
+
model: this.config.model || "gpt-3.5-turbo",
|
|
2957
|
+
messages,
|
|
2958
|
+
temperature: this.config.temperature || 0.7,
|
|
2959
|
+
max_tokens: this.config.maxOutputTokens || 1024,
|
|
2960
|
+
top_p: this.config.topP || 0.9,
|
|
2961
|
+
stream
|
|
2962
|
+
};
|
|
2963
|
+
if (tools2 && tools2.length > 0) {
|
|
2964
|
+
params.tools = tools2;
|
|
2965
|
+
params.tool_choice = "auto";
|
|
2966
|
+
}
|
|
2967
|
+
if (stream) {
|
|
2968
|
+
const streamResponse = await this.openaiClient.chat.completions.create({
|
|
2969
|
+
...params,
|
|
2970
|
+
stream: true
|
|
2971
|
+
});
|
|
2972
|
+
let fullContent = "";
|
|
2973
|
+
let toolCallsMap = {};
|
|
2974
|
+
for await (const chunk of streamResponse) {
|
|
2975
|
+
const delta = chunk.choices[0]?.delta;
|
|
2976
|
+
if (!delta) continue;
|
|
2977
|
+
if (delta.content) {
|
|
2978
|
+
streamLogger2.info(delta.content);
|
|
2979
|
+
fullContent += delta.content;
|
|
2980
|
+
}
|
|
2981
|
+
if (delta.tool_calls) {
|
|
2982
|
+
for (const tc of delta.tool_calls) {
|
|
2983
|
+
const index = tc.index;
|
|
2984
|
+
if (!toolCallsMap[index]) {
|
|
2985
|
+
toolCallsMap[index] = {
|
|
2986
|
+
id: tc.id || "",
|
|
2987
|
+
type: "function",
|
|
2988
|
+
function: {
|
|
2989
|
+
name: tc.function?.name || "",
|
|
2990
|
+
arguments: tc.function?.arguments || ""
|
|
2991
|
+
}
|
|
2992
|
+
};
|
|
2993
|
+
if (tc.function?.name) {
|
|
2994
|
+
streamLogger2.info(`
|
|
2995
|
+
Tool Call: ${tc.function.name}
|
|
2996
|
+
Arguments: `);
|
|
2997
|
+
}
|
|
2998
|
+
} else {
|
|
2999
|
+
if (tc.function?.arguments) {
|
|
3000
|
+
toolCallsMap[index].function.arguments += tc.function.arguments;
|
|
3001
|
+
streamLogger2.info(tc.function.arguments);
|
|
3002
|
+
}
|
|
3003
|
+
}
|
|
3004
|
+
}
|
|
3005
|
+
}
|
|
3006
|
+
}
|
|
3007
|
+
streamLogger2.info("\n");
|
|
3008
|
+
const toolCalls = Object.values(toolCallsMap);
|
|
3009
|
+
const hasContent = fullContent.trim().length > 0;
|
|
3010
|
+
const hasToolCalls = toolCalls.length > 0;
|
|
3011
|
+
if (hasToolCalls) {
|
|
3012
|
+
return [
|
|
3013
|
+
{
|
|
3014
|
+
type: "user",
|
|
3015
|
+
extractedResult: () => ({
|
|
3016
|
+
role: "assistant",
|
|
3017
|
+
content: fullContent,
|
|
3018
|
+
tool_calls: toolCalls
|
|
3019
|
+
})
|
|
3020
|
+
}
|
|
3021
|
+
];
|
|
3022
|
+
}
|
|
3023
|
+
return [
|
|
3024
|
+
{
|
|
3025
|
+
type: "system",
|
|
3026
|
+
extractedResult: () => fullContent,
|
|
3027
|
+
rawContentFromLlm: fullContent
|
|
3028
|
+
}
|
|
3029
|
+
];
|
|
3030
|
+
} else {
|
|
3031
|
+
const response = await this.openaiClient.chat.completions.create(params);
|
|
3032
|
+
if (!response.choices || response.choices.length === 0) {
|
|
3033
|
+
throw new ModelError("No choices returned from OpenAI API");
|
|
3034
|
+
}
|
|
3035
|
+
const message = response.choices[0].message;
|
|
3036
|
+
const messageContent = message.content || "";
|
|
3037
|
+
const hasToolCalls = message.tool_calls && message.tool_calls.length > 0;
|
|
3038
|
+
if (hasToolCalls) {
|
|
3039
|
+
return [
|
|
3040
|
+
{
|
|
3041
|
+
type: "user",
|
|
3042
|
+
extractedResult: () => ({
|
|
3043
|
+
role: "assistant",
|
|
3044
|
+
content: messageContent,
|
|
3045
|
+
tool_calls: message.tool_calls
|
|
3046
|
+
})
|
|
3047
|
+
}
|
|
3048
|
+
];
|
|
3049
|
+
}
|
|
3050
|
+
return [
|
|
3051
|
+
{
|
|
3052
|
+
type: "system",
|
|
3053
|
+
extractedResult: () => messageContent,
|
|
3054
|
+
rawContentFromLlm: messageContent
|
|
3055
|
+
}
|
|
3056
|
+
];
|
|
3057
|
+
}
|
|
3058
|
+
} catch (error) {
|
|
3059
|
+
const errorMsg = error.message || "Unknown error";
|
|
3060
|
+
throw new ModelError(`OpenAI-compatible API call failed: ${errorMsg}, model config: ${JSON.stringify(this.config)}`);
|
|
3061
|
+
}
|
|
3062
|
+
}
|
|
3063
|
+
/**
|
|
3064
|
+
* Call Anthropic API
|
|
3065
|
+
*/
|
|
3066
|
+
async _callAnthropic(messages, tools2, stream = false) {
|
|
3067
|
+
if (!this.anthropicClient) {
|
|
3068
|
+
throw new ModelError("Anthropic client not initialized");
|
|
3069
|
+
}
|
|
3070
|
+
try {
|
|
3071
|
+
const systemMessage = messages.find((m) => m.role === "system");
|
|
3072
|
+
const nonSystemMessages = messages.filter((m) => m.role !== "system");
|
|
3073
|
+
const params = {
|
|
3074
|
+
model: this.config.model || "claude-3-sonnet-20240229",
|
|
3075
|
+
messages: nonSystemMessages,
|
|
3076
|
+
max_tokens: this.config.maxOutputTokens || 4096,
|
|
3077
|
+
temperature: this.config.temperature || 0.7,
|
|
3078
|
+
top_p: this.config.topP || 0.9,
|
|
3079
|
+
stream
|
|
3080
|
+
};
|
|
3081
|
+
if (systemMessage) {
|
|
3082
|
+
params.system = systemMessage.content;
|
|
3083
|
+
}
|
|
3084
|
+
if (tools2 && tools2.length > 0) {
|
|
3085
|
+
params.tools = tools2.map((tool) => ({
|
|
3086
|
+
name: tool.function?.name || tool.name,
|
|
3087
|
+
description: tool.function?.description || tool.description,
|
|
3088
|
+
input_schema: tool.function?.parameters || tool.input_schema
|
|
3089
|
+
}));
|
|
3090
|
+
}
|
|
3091
|
+
if (stream) {
|
|
3092
|
+
const streamResponse = await this.anthropicClient.messages.stream({
|
|
3093
|
+
...params,
|
|
3094
|
+
stream: true
|
|
3095
|
+
});
|
|
3096
|
+
let fullContent = "";
|
|
3097
|
+
let toolUses = [];
|
|
3098
|
+
for await (const event of streamResponse) {
|
|
3099
|
+
if (event.type === "content_block_start") {
|
|
3100
|
+
if (event.content_block.type === "tool_use") {
|
|
3101
|
+
const toolUse = event.content_block;
|
|
3102
|
+
streamLogger2.info(`
|
|
3103
|
+
Tool Call: ${toolUse.name}
|
|
3104
|
+
Arguments: `);
|
|
3105
|
+
}
|
|
3106
|
+
} else if (event.type === "content_block_delta") {
|
|
3107
|
+
if (event.delta.type === "text_delta") {
|
|
3108
|
+
streamLogger2.info(event.delta.text);
|
|
3109
|
+
fullContent += event.delta.text;
|
|
3110
|
+
} else if (event.delta.type === "input_json_delta") {
|
|
3111
|
+
streamLogger2.info(event.delta.partial_json);
|
|
3112
|
+
}
|
|
3113
|
+
} else if (event.type === "message_delta") {
|
|
3114
|
+
}
|
|
3115
|
+
}
|
|
3116
|
+
streamLogger2.info("\n");
|
|
3117
|
+
const hasContent = fullContent.trim().length > 0;
|
|
3118
|
+
const hasToolUses = toolUses.length > 0;
|
|
3119
|
+
return [
|
|
3120
|
+
{
|
|
3121
|
+
type: "system",
|
|
3122
|
+
extractedResult: () => fullContent || toolUses,
|
|
3123
|
+
rawContentFromLlm: fullContent
|
|
3124
|
+
}
|
|
3125
|
+
];
|
|
3126
|
+
} else {
|
|
3127
|
+
const response = await this.anthropicClient.messages.create(params);
|
|
3128
|
+
if (!response.content || response.content.length === 0) {
|
|
3129
|
+
throw new ModelError("No content returned from Anthropic API");
|
|
3130
|
+
}
|
|
3131
|
+
const toolUse = response.content.find((c) => c.type === "tool_use");
|
|
3132
|
+
if (toolUse) {
|
|
3133
|
+
return [
|
|
3134
|
+
{
|
|
3135
|
+
type: "system",
|
|
3136
|
+
extractedResult: () => [toolUse]
|
|
3137
|
+
}
|
|
3138
|
+
];
|
|
3139
|
+
}
|
|
3140
|
+
const textContent = response.content.find((c) => c.type === "text");
|
|
3141
|
+
const messageContent = textContent?.text || "";
|
|
3142
|
+
return [
|
|
3143
|
+
{
|
|
3144
|
+
type: "system",
|
|
3145
|
+
extractedResult: () => messageContent,
|
|
3146
|
+
rawContentFromLlm: messageContent
|
|
3147
|
+
}
|
|
3148
|
+
];
|
|
3149
|
+
}
|
|
3150
|
+
} catch (error) {
|
|
3151
|
+
const errorMsg = error.message || "Unknown error";
|
|
3152
|
+
throw new ModelError(`Anthropic API call failed: ${errorMsg}`);
|
|
3153
|
+
}
|
|
3154
|
+
}
|
|
3155
|
+
/**
|
|
3156
|
+
* Call Google Gemini API
|
|
3157
|
+
*/
|
|
3158
|
+
async _callGemini(messages, tools2, stream = false) {
|
|
3159
|
+
if (!this.geminiClient) {
|
|
3160
|
+
throw new ModelError("Gemini client not initialized");
|
|
3161
|
+
}
|
|
3162
|
+
try {
|
|
3163
|
+
const modelName = this.config.model || "gemini-pro";
|
|
3164
|
+
const model = this.geminiClient.getGenerativeModel({
|
|
3165
|
+
model: modelName,
|
|
3166
|
+
generationConfig: {
|
|
3167
|
+
temperature: this.config.temperature || 0.7,
|
|
3168
|
+
maxOutputTokens: this.config.maxOutputTokens || 2048,
|
|
3169
|
+
topP: this.config.topP || 0.9
|
|
3170
|
+
}
|
|
3171
|
+
});
|
|
3172
|
+
const contents = this._convertMessagesToGeminiFormat(messages);
|
|
3173
|
+
if (stream) {
|
|
3174
|
+
const result = await model.generateContentStream({ contents });
|
|
3175
|
+
let fullContent = "";
|
|
3176
|
+
let functionCalls = [];
|
|
3177
|
+
for await (const chunk of result.stream) {
|
|
3178
|
+
try {
|
|
3179
|
+
const chunkText = await chunk.text();
|
|
3180
|
+
if (chunkText) {
|
|
3181
|
+
streamLogger2.info(chunkText);
|
|
3182
|
+
fullContent += chunkText;
|
|
3183
|
+
}
|
|
3184
|
+
} catch (e) {
|
|
3185
|
+
}
|
|
3186
|
+
const candidates = chunk.candidates;
|
|
3187
|
+
if (candidates && candidates[0]?.content?.parts) {
|
|
3188
|
+
for (const part of candidates[0].content.parts) {
|
|
3189
|
+
if (part.functionCall) {
|
|
3190
|
+
functionCalls.push(part.functionCall);
|
|
3191
|
+
}
|
|
3192
|
+
}
|
|
3193
|
+
}
|
|
3194
|
+
}
|
|
3195
|
+
streamLogger2.info("\n");
|
|
3196
|
+
const hasContent = fullContent.trim().length > 0;
|
|
3197
|
+
const hasFunctionCalls = functionCalls.length > 0;
|
|
3198
|
+
if (hasFunctionCalls) {
|
|
3199
|
+
const toolCalls = functionCalls.map((fc, index) => ({
|
|
3200
|
+
id: `call_${Date.now()}_${index}`,
|
|
3201
|
+
type: "function",
|
|
3202
|
+
function: {
|
|
3203
|
+
name: fc.name,
|
|
3204
|
+
arguments: JSON.stringify(fc.args)
|
|
3205
|
+
}
|
|
3206
|
+
}));
|
|
3207
|
+
return [
|
|
3208
|
+
{
|
|
3209
|
+
type: "user",
|
|
3210
|
+
extractedResult: () => ({
|
|
3211
|
+
role: "assistant",
|
|
3212
|
+
content: fullContent,
|
|
3213
|
+
tool_calls: toolCalls
|
|
3214
|
+
})
|
|
3215
|
+
}
|
|
3216
|
+
];
|
|
3217
|
+
}
|
|
3218
|
+
return [
|
|
3219
|
+
{
|
|
3220
|
+
type: "system",
|
|
3221
|
+
extractedResult: () => fullContent,
|
|
3222
|
+
rawContentFromLlm: fullContent
|
|
3223
|
+
}
|
|
3224
|
+
];
|
|
3225
|
+
} else {
|
|
3226
|
+
const result = await model.generateContent({ contents });
|
|
3227
|
+
const response = result.response;
|
|
3228
|
+
if (!response.candidates || response.candidates.length === 0) {
|
|
3229
|
+
throw new ModelError("No candidates returned from Gemini API");
|
|
3230
|
+
}
|
|
3231
|
+
const candidate = response.candidates[0];
|
|
3232
|
+
const content = candidate.content;
|
|
3233
|
+
if (!content || !content.parts || content.parts.length === 0) {
|
|
3234
|
+
throw new ModelError("No content parts returned from Gemini API");
|
|
3235
|
+
}
|
|
3236
|
+
const functionCalls = content.parts.filter((part) => part.functionCall);
|
|
3237
|
+
const hasFunctionCalls = functionCalls.length > 0;
|
|
3238
|
+
if (hasFunctionCalls) {
|
|
3239
|
+
const toolCalls = functionCalls.map((part, index) => ({
|
|
3240
|
+
id: `call_${Date.now()}_${index}`,
|
|
3241
|
+
type: "function",
|
|
3242
|
+
function: {
|
|
3243
|
+
name: part.functionCall.name,
|
|
3244
|
+
arguments: JSON.stringify(part.functionCall.args)
|
|
3245
|
+
}
|
|
3246
|
+
}));
|
|
3247
|
+
const textParts2 = content.parts.filter((part) => part.text);
|
|
3248
|
+
const textContent2 = textParts2.map((part) => part.text).join("");
|
|
3249
|
+
return [
|
|
3250
|
+
{
|
|
3251
|
+
type: "user",
|
|
3252
|
+
extractedResult: () => ({
|
|
3253
|
+
role: "assistant",
|
|
3254
|
+
content: textContent2,
|
|
3255
|
+
tool_calls: toolCalls
|
|
3256
|
+
})
|
|
3257
|
+
}
|
|
3258
|
+
];
|
|
3259
|
+
}
|
|
3260
|
+
const textParts = content.parts.filter((part) => part.text);
|
|
3261
|
+
const textContent = textParts.map((part) => part.text).join("");
|
|
3262
|
+
return [
|
|
3263
|
+
{
|
|
3264
|
+
type: "system",
|
|
3265
|
+
extractedResult: () => textContent,
|
|
3266
|
+
rawContentFromLlm: textContent
|
|
3267
|
+
}
|
|
3268
|
+
];
|
|
3269
|
+
}
|
|
3270
|
+
} catch (error) {
|
|
3271
|
+
const errorMsg = error.message || "Unknown error";
|
|
3272
|
+
throw new ModelError(`Gemini API call failed: ${errorMsg}`);
|
|
3273
|
+
}
|
|
3274
|
+
}
|
|
3275
|
+
/**
|
|
3276
|
+
* Convert OpenAI format messages to Gemini format
|
|
3277
|
+
*/
|
|
3278
|
+
_convertMessagesToGeminiFormat(messages) {
|
|
3279
|
+
const contents = [];
|
|
3280
|
+
for (const message of messages) {
|
|
3281
|
+
if (message.role === "system") {
|
|
3282
|
+
contents.push({
|
|
3283
|
+
role: "user",
|
|
3284
|
+
parts: [{ text: message.content }]
|
|
3285
|
+
});
|
|
3286
|
+
} else if (message.role === "user") {
|
|
3287
|
+
contents.push({
|
|
3288
|
+
role: "user",
|
|
3289
|
+
parts: [{ text: message.content }]
|
|
3290
|
+
});
|
|
3291
|
+
} else if (message.role === "assistant") {
|
|
3292
|
+
const parts = [];
|
|
3293
|
+
if (message.content) {
|
|
3294
|
+
parts.push({ text: message.content });
|
|
3295
|
+
}
|
|
3296
|
+
if (message.tool_calls) {
|
|
3297
|
+
for (const toolCall of message.tool_calls) {
|
|
3298
|
+
parts.push({
|
|
3299
|
+
functionCall: {
|
|
3300
|
+
name: toolCall.function.name,
|
|
3301
|
+
args: JSON.parse(toolCall.function.arguments)
|
|
3302
|
+
}
|
|
3303
|
+
});
|
|
3304
|
+
}
|
|
3305
|
+
}
|
|
3306
|
+
contents.push({
|
|
3307
|
+
role: "model",
|
|
3308
|
+
parts
|
|
3309
|
+
});
|
|
3310
|
+
} else if (message.role === "tool") {
|
|
3311
|
+
contents.push({
|
|
3312
|
+
role: "function",
|
|
3313
|
+
parts: [
|
|
3314
|
+
{
|
|
3315
|
+
functionResponse: {
|
|
3316
|
+
name: message.name,
|
|
3317
|
+
response: {
|
|
3318
|
+
content: message.content
|
|
3319
|
+
}
|
|
3320
|
+
}
|
|
3321
|
+
}
|
|
3322
|
+
]
|
|
3323
|
+
});
|
|
3324
|
+
}
|
|
3325
|
+
}
|
|
3326
|
+
return contents;
|
|
3327
|
+
}
|
|
3328
|
+
/**
|
|
3329
|
+
* Get model configuration
|
|
3330
|
+
*/
|
|
3331
|
+
getConfig() {
|
|
3332
|
+
return { ...this.config };
|
|
3333
|
+
}
|
|
3334
|
+
/**
|
|
3335
|
+
* Get model name
|
|
3336
|
+
*/
|
|
3337
|
+
getName() {
|
|
3338
|
+
return this.modelName;
|
|
3339
|
+
}
|
|
3340
|
+
/**
|
|
3341
|
+
* Check if model supports tool calling
|
|
3342
|
+
*/
|
|
3343
|
+
supportsToolCalling() {
|
|
3344
|
+
const supportedModels = ["gpt-3.5-turbo", "gpt-4", "claude-3", "deepseek-chat", "gemini-pro", "gemini-1.5"];
|
|
3345
|
+
return supportedModels.some((model) => this.config.model.includes(model));
|
|
3346
|
+
}
|
|
3347
|
+
};
|
|
3348
|
+
|
|
3349
|
+
// src/schemas/message.ts
|
|
3350
|
+
import * as fs7 from "fs";
|
|
3351
|
+
var Message = class _Message {
|
|
3352
|
+
role;
|
|
3353
|
+
content;
|
|
3354
|
+
images;
|
|
3355
|
+
type;
|
|
3356
|
+
constructor(role, content, images, type) {
|
|
3357
|
+
this.role = role;
|
|
3358
|
+
this.content = content;
|
|
3359
|
+
this.images = images;
|
|
3360
|
+
this.type = type;
|
|
3361
|
+
}
|
|
3362
|
+
/**
|
|
3363
|
+
* Create message from user input
|
|
3364
|
+
*/
|
|
3365
|
+
static fromUserMsg(content, images) {
|
|
3366
|
+
return new _Message("user", content, images);
|
|
3367
|
+
}
|
|
3368
|
+
/**
|
|
3369
|
+
* Create message from assistant response
|
|
3370
|
+
*/
|
|
3371
|
+
static fromAssistantMsg(content) {
|
|
3372
|
+
return new _Message("assistant", content);
|
|
3373
|
+
}
|
|
3374
|
+
/**
|
|
3375
|
+
* Create system message
|
|
3376
|
+
*/
|
|
3377
|
+
static fromSystemMsg(content) {
|
|
3378
|
+
return new _Message("system", content);
|
|
3379
|
+
}
|
|
3380
|
+
/**
|
|
3381
|
+
* Convert to plain object for API calls
|
|
3382
|
+
*/
|
|
3383
|
+
toObject() {
|
|
3384
|
+
const obj = {
|
|
3385
|
+
role: this.role,
|
|
3386
|
+
content: this.content
|
|
3387
|
+
};
|
|
3388
|
+
if (this.images) {
|
|
3389
|
+
obj.images = this.images;
|
|
3390
|
+
}
|
|
3391
|
+
if (this.type) {
|
|
3392
|
+
obj.type = this.type;
|
|
3393
|
+
}
|
|
3394
|
+
return obj;
|
|
3395
|
+
}
|
|
3396
|
+
/**
|
|
3397
|
+
* Check if message contains images
|
|
3398
|
+
*/
|
|
3399
|
+
hasImages() {
|
|
3400
|
+
return !!this.images && (Array.isArray(this.images) ? this.images.length > 0 : true);
|
|
3401
|
+
}
|
|
3402
|
+
/**
|
|
3403
|
+
* Get message content length
|
|
3404
|
+
*/
|
|
3405
|
+
getContentLength() {
|
|
3406
|
+
return this.content.length;
|
|
3407
|
+
}
|
|
3408
|
+
/**
|
|
3409
|
+
* Convert to string representation
|
|
3410
|
+
*/
|
|
3411
|
+
toString() {
|
|
3412
|
+
return `${this.role}: ${this.content}`;
|
|
3413
|
+
}
|
|
3414
|
+
/**
|
|
3415
|
+
* Convert to OpenAI Vision API format with base64-encoded images
|
|
3416
|
+
* This method handles:
|
|
3417
|
+
* - HTTP/HTTPS URLs: Downloads and converts to base64
|
|
3418
|
+
* - Local file paths: Reads file and converts to base64
|
|
3419
|
+
* - Already base64 strings: Passes through
|
|
3420
|
+
*/
|
|
3421
|
+
async toChatMessage() {
|
|
3422
|
+
const role = this.role;
|
|
3423
|
+
if (!this.hasImages()) {
|
|
3424
|
+
return { role, content: this.content };
|
|
3425
|
+
}
|
|
3426
|
+
const content = [{ type: "text", text: this.content }];
|
|
3427
|
+
const imageArray = Array.isArray(this.images) ? this.images : [this.images];
|
|
3428
|
+
for (const img of imageArray) {
|
|
3429
|
+
let base64Data;
|
|
3430
|
+
if (img.startsWith("http://") || img.startsWith("https://")) {
|
|
3431
|
+
base64Data = await this.encodeHttpImage(img);
|
|
3432
|
+
} else if (fs7.existsSync(img)) {
|
|
3433
|
+
base64Data = await this.encodeLocalFile(img);
|
|
3434
|
+
} else {
|
|
3435
|
+
base64Data = img;
|
|
3436
|
+
}
|
|
3437
|
+
const mimeType = this.getMimeType(img);
|
|
3438
|
+
content.push({
|
|
3439
|
+
type: "image_url",
|
|
3440
|
+
image_url: { url: `data:${mimeType};base64,${base64Data}` }
|
|
3441
|
+
});
|
|
3442
|
+
}
|
|
3443
|
+
return { role, content };
|
|
3444
|
+
}
|
|
3445
|
+
/**
|
|
3446
|
+
* Get MIME type from file path or URL
|
|
3447
|
+
*/
|
|
3448
|
+
getMimeType(path5) {
|
|
3449
|
+
const ext = path5.toLowerCase().split(".").pop();
|
|
3450
|
+
const mimeTypes = {
|
|
3451
|
+
png: "image/png",
|
|
3452
|
+
jpg: "image/jpeg",
|
|
3453
|
+
jpeg: "image/jpeg",
|
|
3454
|
+
gif: "image/gif",
|
|
3455
|
+
webp: "image/webp",
|
|
3456
|
+
bmp: "image/bmp"
|
|
3457
|
+
};
|
|
3458
|
+
return mimeTypes[ext || ""] || "image/jpeg";
|
|
3459
|
+
}
|
|
3460
|
+
/**
|
|
3461
|
+
* Encode local file to base64
|
|
3462
|
+
*/
|
|
3463
|
+
async encodeLocalFile(filePath) {
|
|
3464
|
+
const fileBuffer = await fs7.promises.readFile(filePath);
|
|
3465
|
+
return fileBuffer.toString("base64");
|
|
3466
|
+
}
|
|
3467
|
+
/**
|
|
3468
|
+
* Fetch and encode HTTP image to base64
|
|
3469
|
+
*/
|
|
3470
|
+
async encodeHttpImage(url) {
|
|
3471
|
+
const response = await fetch(url);
|
|
3472
|
+
if (!response.ok) {
|
|
3473
|
+
throw new Error(`Failed to fetch image from ${url}: ${response.statusText}`);
|
|
3474
|
+
}
|
|
3475
|
+
const buffer = await response.arrayBuffer();
|
|
3476
|
+
return Buffer.from(buffer).toString("base64");
|
|
3477
|
+
}
|
|
3478
|
+
};
|
|
3479
|
+
|
|
3480
|
+
// src/memory/messageHistory.ts
|
|
3481
|
+
var MessageHistory = class {
|
|
3482
|
+
messages = [];
|
|
3483
|
+
systemPrompt;
|
|
3484
|
+
model;
|
|
3485
|
+
contextWindowTokens;
|
|
3486
|
+
currentTokens = 0;
|
|
3487
|
+
constructor(model, system, contextWindowTokens) {
|
|
3488
|
+
this.model = model;
|
|
3489
|
+
this.systemPrompt = system;
|
|
3490
|
+
this.contextWindowTokens = contextWindowTokens;
|
|
3491
|
+
if (system) {
|
|
3492
|
+
const systemMessage = new Message("system", system);
|
|
3493
|
+
this.messages.push(systemMessage);
|
|
3494
|
+
this.currentTokens += this.estimateTokens(system);
|
|
3495
|
+
}
|
|
3496
|
+
}
|
|
3497
|
+
addMessage(arg1, arg2) {
|
|
3498
|
+
let message;
|
|
3499
|
+
if (arg1 instanceof Message) {
|
|
3500
|
+
message = arg1;
|
|
3501
|
+
} else if (typeof arg1 === "string") {
|
|
3502
|
+
message = new Message(arg1, arg2);
|
|
3503
|
+
} else if (typeof arg1 === "object" && arg1 !== null) {
|
|
3504
|
+
this.messages.push(arg1);
|
|
3505
|
+
const contentStr = arg1.content || JSON.stringify(arg1);
|
|
3506
|
+
this.currentTokens += this.estimateTokens(contentStr);
|
|
3507
|
+
return;
|
|
3508
|
+
} else {
|
|
3509
|
+
throw new Error(`Invalid message format: ${arg1}`);
|
|
3510
|
+
}
|
|
3511
|
+
this.messages.push(message);
|
|
3512
|
+
this.currentTokens += this.estimateTokens(message.content);
|
|
3513
|
+
}
|
|
3514
|
+
/**
|
|
3515
|
+
* Get all messages
|
|
3516
|
+
*/
|
|
3517
|
+
getMessages() {
|
|
3518
|
+
return [...this.messages];
|
|
3519
|
+
}
|
|
3520
|
+
/**
|
|
3521
|
+
* Get messages formatted for API calls
|
|
3522
|
+
* Async version to handle base64 image encoding
|
|
3523
|
+
*/
|
|
3524
|
+
async formatForApi() {
|
|
3525
|
+
const results = [];
|
|
3526
|
+
for (const msg of this.messages) {
|
|
3527
|
+
if (msg instanceof Message) {
|
|
3528
|
+
if (msg.hasImages()) {
|
|
3529
|
+
results.push(await msg.toChatMessage());
|
|
3530
|
+
} else {
|
|
3531
|
+
results.push(msg.toObject());
|
|
3532
|
+
}
|
|
3533
|
+
} else if (typeof msg.toObject === "function") {
|
|
3534
|
+
results.push(msg.toObject());
|
|
3535
|
+
} else {
|
|
3536
|
+
results.push(msg);
|
|
3537
|
+
}
|
|
3538
|
+
}
|
|
3539
|
+
return results;
|
|
3540
|
+
}
|
|
3541
|
+
/**
|
|
3542
|
+
* Update system prompt
|
|
3543
|
+
*/
|
|
3544
|
+
updateSystem(system) {
|
|
3545
|
+
this.messages = this.messages.filter((msg) => {
|
|
3546
|
+
if (msg instanceof Message) {
|
|
3547
|
+
return msg.role !== "system";
|
|
3548
|
+
}
|
|
3549
|
+
return msg.role !== "system";
|
|
3550
|
+
});
|
|
3551
|
+
this.currentTokens = 0;
|
|
3552
|
+
if (system) {
|
|
3553
|
+
const systemMessage = new Message("system", system);
|
|
3554
|
+
this.messages.unshift(systemMessage);
|
|
3555
|
+
this.currentTokens += this.estimateTokens(system);
|
|
3556
|
+
}
|
|
3557
|
+
for (const message of this.messages) {
|
|
3558
|
+
if (message instanceof Message) {
|
|
3559
|
+
if (message.role !== "system") {
|
|
3560
|
+
this.currentTokens += this.estimateTokens(message.content);
|
|
3561
|
+
}
|
|
3562
|
+
} else {
|
|
3563
|
+
const contentStr = message.content || JSON.stringify(message);
|
|
3564
|
+
this.currentTokens += this.estimateTokens(contentStr);
|
|
3565
|
+
}
|
|
3566
|
+
}
|
|
3567
|
+
}
|
|
3568
|
+
/**
|
|
3569
|
+
* Truncate history to fit context window
|
|
3570
|
+
* Preserves tool call chains: assistant(with tool_calls) → tool messages
|
|
3571
|
+
*/
|
|
3572
|
+
truncate() {
|
|
3573
|
+
if (this.currentTokens <= this.contextWindowTokens) {
|
|
3574
|
+
return;
|
|
3575
|
+
}
|
|
3576
|
+
let systemMessageIndex = -1;
|
|
3577
|
+
for (let i2 = 0; i2 < this.messages.length; i2++) {
|
|
3578
|
+
const msg = this.messages[i2];
|
|
3579
|
+
if (msg instanceof Message && msg.role === "system" || msg.role === "system") {
|
|
3580
|
+
systemMessageIndex = i2;
|
|
3581
|
+
break;
|
|
3582
|
+
}
|
|
3583
|
+
}
|
|
3584
|
+
const systemMessage = systemMessageIndex >= 0 ? this.messages[systemMessageIndex] : null;
|
|
3585
|
+
const otherMessages = this.messages.filter((msg, index) => index !== systemMessageIndex);
|
|
3586
|
+
const toolCallDependencies = /* @__PURE__ */ new Map();
|
|
3587
|
+
for (let i2 = 0; i2 < otherMessages.length; i2++) {
|
|
3588
|
+
const msg = otherMessages[i2];
|
|
3589
|
+
const role = msg instanceof Message ? msg.role : msg.role;
|
|
3590
|
+
if (role === "assistant" && !(msg instanceof Message)) {
|
|
3591
|
+
const toolCalls = msg.tool_calls;
|
|
3592
|
+
if (toolCalls && Array.isArray(toolCalls)) {
|
|
3593
|
+
for (const tc of toolCalls) {
|
|
3594
|
+
if (tc.id) {
|
|
3595
|
+
toolCallDependencies.set(tc.id, i2);
|
|
3596
|
+
}
|
|
3597
|
+
}
|
|
3598
|
+
}
|
|
3599
|
+
}
|
|
3600
|
+
}
|
|
3601
|
+
const mustKeepIndices = /* @__PURE__ */ new Set();
|
|
3602
|
+
for (let i2 = 0; i2 < otherMessages.length; i2++) {
|
|
3603
|
+
const msg = otherMessages[i2];
|
|
3604
|
+
const role = msg instanceof Message ? msg.role : msg.role;
|
|
3605
|
+
if (role === "tool" && !(msg instanceof Message)) {
|
|
3606
|
+
const toolCallId = msg.tool_call_id;
|
|
3607
|
+
if (toolCallId) {
|
|
3608
|
+
const assistantIndex = toolCallDependencies.get(toolCallId);
|
|
3609
|
+
if (assistantIndex !== void 0) {
|
|
3610
|
+
mustKeepIndices.add(assistantIndex);
|
|
3611
|
+
mustKeepIndices.add(i2);
|
|
3612
|
+
}
|
|
3613
|
+
}
|
|
3614
|
+
}
|
|
3615
|
+
}
|
|
3616
|
+
let i = 0;
|
|
3617
|
+
while (this.currentTokens > this.contextWindowTokens && i < otherMessages.length) {
|
|
3618
|
+
if (mustKeepIndices.has(i)) {
|
|
3619
|
+
i++;
|
|
3620
|
+
continue;
|
|
3621
|
+
}
|
|
3622
|
+
const msg = otherMessages[i];
|
|
3623
|
+
const role = msg instanceof Message ? msg.role : msg.role;
|
|
3624
|
+
let hasDependentToolMessages = false;
|
|
3625
|
+
if (role === "assistant" && !(msg instanceof Message)) {
|
|
3626
|
+
const toolCalls = msg.tool_calls;
|
|
3627
|
+
if (toolCalls && Array.isArray(toolCalls)) {
|
|
3628
|
+
for (const tc of toolCalls) {
|
|
3629
|
+
if (tc.id) {
|
|
3630
|
+
for (let j = i + 1; j < otherMessages.length; j++) {
|
|
3631
|
+
const laterMsg = otherMessages[j];
|
|
3632
|
+
const laterRole = laterMsg instanceof Message ? laterMsg.role : laterMsg.role;
|
|
3633
|
+
if (laterRole === "tool" && !(laterMsg instanceof Message)) {
|
|
3634
|
+
const laterToolCallId = laterMsg.tool_call_id;
|
|
3635
|
+
if (laterToolCallId === tc.id) {
|
|
3636
|
+
hasDependentToolMessages = true;
|
|
3637
|
+
break;
|
|
3638
|
+
}
|
|
3639
|
+
}
|
|
3640
|
+
}
|
|
3641
|
+
}
|
|
3642
|
+
}
|
|
3643
|
+
}
|
|
3644
|
+
}
|
|
3645
|
+
if (hasDependentToolMessages) {
|
|
3646
|
+
i++;
|
|
3647
|
+
continue;
|
|
3648
|
+
}
|
|
3649
|
+
const removed = otherMessages.splice(i, 1)[0];
|
|
3650
|
+
if (removed) {
|
|
3651
|
+
let contentStr = "";
|
|
3652
|
+
if (removed instanceof Message) {
|
|
3653
|
+
contentStr = removed.content;
|
|
3654
|
+
} else {
|
|
3655
|
+
contentStr = removed.content || JSON.stringify(removed);
|
|
3656
|
+
}
|
|
3657
|
+
this.currentTokens -= this.estimateTokens(contentStr);
|
|
3658
|
+
const newMustKeepIndices = /* @__PURE__ */ new Set();
|
|
3659
|
+
for (const idx of mustKeepIndices) {
|
|
3660
|
+
if (idx > i) {
|
|
3661
|
+
newMustKeepIndices.add(idx - 1);
|
|
3662
|
+
} else if (idx < i) {
|
|
3663
|
+
newMustKeepIndices.add(idx);
|
|
3664
|
+
}
|
|
3665
|
+
}
|
|
3666
|
+
mustKeepIndices.clear();
|
|
3667
|
+
for (const idx of newMustKeepIndices) {
|
|
3668
|
+
mustKeepIndices.add(idx);
|
|
3669
|
+
}
|
|
3670
|
+
}
|
|
3671
|
+
}
|
|
3672
|
+
this.messages = [];
|
|
3673
|
+
if (systemMessage) {
|
|
3674
|
+
this.messages.push(systemMessage);
|
|
3675
|
+
}
|
|
3676
|
+
this.messages.push(...otherMessages);
|
|
3677
|
+
}
|
|
3678
|
+
/**
|
|
3679
|
+
* Clear all messages except system
|
|
3680
|
+
*/
|
|
3681
|
+
clear() {
|
|
3682
|
+
let systemMessage;
|
|
3683
|
+
for (const msg of this.messages) {
|
|
3684
|
+
if (msg instanceof Message && msg.role === "system" || msg.role === "system") {
|
|
3685
|
+
systemMessage = msg;
|
|
3686
|
+
break;
|
|
3687
|
+
}
|
|
3688
|
+
}
|
|
3689
|
+
this.messages = systemMessage ? [systemMessage] : [];
|
|
3690
|
+
if (systemMessage) {
|
|
3691
|
+
if (systemMessage instanceof Message) {
|
|
3692
|
+
this.currentTokens = this.estimateTokens(systemMessage.content);
|
|
3693
|
+
} else {
|
|
3694
|
+
this.currentTokens = this.estimateTokens(systemMessage.content || JSON.stringify(systemMessage));
|
|
3695
|
+
}
|
|
3696
|
+
} else {
|
|
3697
|
+
this.currentTokens = 0;
|
|
3698
|
+
}
|
|
3699
|
+
}
|
|
3700
|
+
/**
|
|
3701
|
+
* Get the last message
|
|
3702
|
+
*/
|
|
3703
|
+
getLastMessage() {
|
|
3704
|
+
return this.messages.length > 0 ? this.messages[this.messages.length - 1] : null;
|
|
3705
|
+
}
|
|
3706
|
+
/**
|
|
3707
|
+
* Get message count
|
|
3708
|
+
*/
|
|
3709
|
+
getMessageCount() {
|
|
3710
|
+
return this.messages.length;
|
|
3711
|
+
}
|
|
3712
|
+
/**
|
|
3713
|
+
* Get formatted context usage information
|
|
3714
|
+
*/
|
|
3715
|
+
get formattedContextUsage() {
|
|
3716
|
+
const usagePercent = this.currentTokens / this.contextWindowTokens * 100;
|
|
3717
|
+
return `Tokens: ${this.currentTokens}/${this.contextWindowTokens} (${usagePercent.toFixed(1)}%)`;
|
|
3718
|
+
}
|
|
3719
|
+
/**
|
|
3720
|
+
* Estimate tokens for text (simplified implementation)
|
|
3721
|
+
*/
|
|
3722
|
+
estimateTokens(text) {
|
|
3723
|
+
if (!text) return 0;
|
|
3724
|
+
return Math.ceil(text.length / 4);
|
|
3725
|
+
}
|
|
3726
|
+
/**
|
|
3727
|
+
* Get conversation as string
|
|
3728
|
+
*/
|
|
3729
|
+
toString() {
|
|
3730
|
+
return this.messages.map((msg) => {
|
|
3731
|
+
if (msg instanceof Message) {
|
|
3732
|
+
return msg.toString();
|
|
3733
|
+
}
|
|
3734
|
+
return JSON.stringify(msg);
|
|
3735
|
+
}).join("\n");
|
|
3736
|
+
}
|
|
3737
|
+
};
|
|
3738
|
+
|
|
3739
|
+
// src/tools/toolcallManager.ts
|
|
3740
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
3741
|
+
var ToolcallManager = class {
|
|
3742
|
+
poolSize;
|
|
3743
|
+
semaphore;
|
|
3744
|
+
tasks = /* @__PURE__ */ new Set();
|
|
3745
|
+
todo = /* @__PURE__ */ new Map();
|
|
3746
|
+
done = /* @__PURE__ */ new Map();
|
|
3747
|
+
failed = /* @__PURE__ */ new Map();
|
|
3748
|
+
toolStores;
|
|
3749
|
+
executedResults = [];
|
|
3750
|
+
// Background process management
|
|
3751
|
+
backgroundProcesses = /* @__PURE__ */ new Map();
|
|
3752
|
+
monitorTasks = /* @__PURE__ */ new Map();
|
|
3753
|
+
constructor(poolSize = 5, toolStore = []) {
|
|
3754
|
+
this.poolSize = poolSize;
|
|
3755
|
+
this.semaphore = { available: poolSize };
|
|
3756
|
+
this.toolStores = toolStore;
|
|
3757
|
+
}
|
|
3758
|
+
/**
|
|
3759
|
+
* Get executed results
|
|
3760
|
+
*/
|
|
3761
|
+
getExecutedResults() {
|
|
3762
|
+
return Array.from(this.done.values()).concat(Array.from(this.failed.values()));
|
|
3763
|
+
}
|
|
3764
|
+
/**
|
|
3765
|
+
* Clear executed results
|
|
3766
|
+
*/
|
|
3767
|
+
clear() {
|
|
3768
|
+
this.todo.clear();
|
|
3769
|
+
this.done.clear();
|
|
3770
|
+
this.failed.clear();
|
|
3771
|
+
this.executedResults = [];
|
|
3772
|
+
}
|
|
3773
|
+
/**
|
|
3774
|
+
* Add tool calls to be executed
|
|
3775
|
+
*/
|
|
3776
|
+
addToolcall(toolCall) {
|
|
3777
|
+
if (Array.isArray(toolCall)) {
|
|
3778
|
+
toolCall.forEach((tc) => {
|
|
3779
|
+
this.todo.set(tc.toolCallId, tc);
|
|
3780
|
+
});
|
|
3781
|
+
logger.debug(`ToolcallManager: Added ${toolCall.length} toolcalls`);
|
|
3782
|
+
} else {
|
|
3783
|
+
this.todo.set(toolCall.toolCallId, toolCall);
|
|
3784
|
+
logger.debug(`ToolcallManager: Added toolcall: ${toolCall.name}`);
|
|
3785
|
+
}
|
|
3786
|
+
}
|
|
3787
|
+
/**
|
|
3788
|
+
* Execute single task with pool control
|
|
3789
|
+
*/
|
|
3790
|
+
async executeWithPool(toolcall) {
|
|
3791
|
+
if (!toolcall || !toolcall.name) {
|
|
3792
|
+
return;
|
|
3793
|
+
}
|
|
3794
|
+
if (!toolcall.isExtractedSuccess && toolcall.type === "user") {
|
|
3795
|
+
this.failed.set(toolcall.toolCallId, toolcall);
|
|
3796
|
+
return;
|
|
3797
|
+
}
|
|
3798
|
+
while (this.semaphore.available <= 0) {
|
|
3799
|
+
await new Promise((resolve2) => setTimeout(resolve2, 100));
|
|
3800
|
+
}
|
|
3801
|
+
this.semaphore.available--;
|
|
3802
|
+
try {
|
|
3803
|
+
toolcall.executedState = "running";
|
|
3804
|
+
let toolcallInputStr = JSON.stringify(toolcall.input);
|
|
3805
|
+
if (toolcallInputStr.length > 200) {
|
|
3806
|
+
toolcallInputStr = toolcallInputStr.substring(0, 100) + " ...(truncated by 100 characters)";
|
|
3807
|
+
}
|
|
3808
|
+
const toolcallDescription = `${toolcall.name}(${toolcallInputStr})`;
|
|
3809
|
+
logger.debug(`\u5F00\u59CB\u6267\u884C\u4EFB\u52A1: ${toolcallDescription}`);
|
|
3810
|
+
const result = await runWithManager(this, () => executeSingleTool(toolcall, this.toolStores));
|
|
3811
|
+
const obs = result.executedResult();
|
|
3812
|
+
if (typeof obs === "object" && "content" in obs) {
|
|
3813
|
+
logger.debug(`Observation of ${toolcall.name}, tool_call_id: ${toolcall.toolCallId}:
|
|
3814
|
+
${obs.content}`);
|
|
3815
|
+
} else {
|
|
3816
|
+
logger.debug(`Observation of ${toolcall.name}:
|
|
3817
|
+
${obs}`);
|
|
3818
|
+
}
|
|
3819
|
+
if (result.executedState === "success") {
|
|
3820
|
+
logger.debug(`\u4EFB\u52A1\u6267\u884C\u6210\u529F: ${toolcallDescription}`);
|
|
3821
|
+
this.done.set(toolcall.toolCallId, result);
|
|
3822
|
+
} else if (result.executedState === "failed") {
|
|
3823
|
+
logger.error(`\u4EFB\u52A1\u6267\u884C\u5931\u8D25: ${toolcallDescription}, \u9519\u8BEF: ${result.executedContent || "Unknown error"}`);
|
|
3824
|
+
this.failed.set(toolcall.toolCallId, result);
|
|
3825
|
+
} else if (result.executedState === "running") {
|
|
3826
|
+
logger.warn(`\u4EFB\u52A1\u6267\u884C\u4E2D: ${toolcallDescription}`);
|
|
3827
|
+
} else {
|
|
3828
|
+
logger.warn(`\u4EFB\u52A1\u6267\u884C\u72B6\u6001\u672A\u77E5: ${toolcallDescription}`);
|
|
3829
|
+
}
|
|
3830
|
+
const [backgroundProcessStatus, backgroundResult] = this.listBackgroundProcesses();
|
|
3831
|
+
if (backgroundProcessStatus === "running" /* RUNNING */) {
|
|
3832
|
+
if (typeof obs === "object" && "content" in obs) {
|
|
3833
|
+
const newObs = { ...obs };
|
|
3834
|
+
if (typeof backgroundResult === "string") {
|
|
3835
|
+
newObs.content = `${newObs.content}
|
|
3836
|
+
|
|
3837
|
+
${backgroundResult}`;
|
|
3838
|
+
} else if (typeof backgroundResult === "object") {
|
|
3839
|
+
newObs.content = `${newObs.content}
|
|
3840
|
+
|
|
3841
|
+
background process observation: ${backgroundResult.content}`;
|
|
3842
|
+
}
|
|
3843
|
+
this.executedResults.push(newObs);
|
|
3844
|
+
} else if (typeof obs === "string") {
|
|
3845
|
+
if (typeof backgroundResult === "string") {
|
|
3846
|
+
this.executedResults.push(obs + "\n\n" + backgroundResult);
|
|
3847
|
+
} else {
|
|
3848
|
+
this.executedResults.push(obs + "\n\nbackground process observation: " + backgroundResult.content);
|
|
3849
|
+
}
|
|
3850
|
+
} else {
|
|
3851
|
+
this.executedResults.push(obs);
|
|
3852
|
+
}
|
|
3853
|
+
} else {
|
|
3854
|
+
this.executedResults.push(obs);
|
|
3855
|
+
}
|
|
3856
|
+
} catch (error) {
|
|
3857
|
+
logger.error(`\u4EFB\u52A1\u6267\u884C\u5F02\u5E38: ${toolcall.name}, \u9519\u8BEF: ${error}`);
|
|
3858
|
+
toolcall.executedState = "failed";
|
|
3859
|
+
toolcall.executedContent = String(error);
|
|
3860
|
+
this.failed.set(toolcall.toolCallId, toolcall);
|
|
3861
|
+
this.executedResults.push(toolcall.executedResult());
|
|
3862
|
+
} finally {
|
|
3863
|
+
this.semaphore.available++;
|
|
3864
|
+
}
|
|
3865
|
+
}
|
|
3866
|
+
/**
|
|
3867
|
+
* Execute all pending tasks (background execution, returns immediately)
|
|
3868
|
+
*/
|
|
3869
|
+
async execute() {
|
|
3870
|
+
const executionPromises = [];
|
|
3871
|
+
for (const [toolcallId, toolcall] of this.todo) {
|
|
3872
|
+
this.todo.delete(toolcallId);
|
|
3873
|
+
const executionPromise = this.executeWithPool(toolcall);
|
|
3874
|
+
this.tasks.add(executionPromise);
|
|
3875
|
+
executionPromise.finally(() => {
|
|
3876
|
+
this.tasks.delete(executionPromise);
|
|
3877
|
+
});
|
|
3878
|
+
executionPromises.push(executionPromise);
|
|
3879
|
+
}
|
|
3880
|
+
await Promise.all(executionPromises);
|
|
3881
|
+
return this.executedResults;
|
|
3882
|
+
}
|
|
3883
|
+
/**
|
|
3884
|
+
* Observe execution results
|
|
3885
|
+
*/
|
|
3886
|
+
async observe(waitAll = false, timeout = null) {
|
|
3887
|
+
await this.execute();
|
|
3888
|
+
if (this.tasks.size > 0 && !waitAll) {
|
|
3889
|
+
await new Promise((resolve2) => setTimeout(resolve2, 1e3));
|
|
3890
|
+
}
|
|
3891
|
+
if (waitAll && this.tasks.size > 0) {
|
|
3892
|
+
if (timeout) {
|
|
3893
|
+
try {
|
|
3894
|
+
await Promise.race([
|
|
3895
|
+
Promise.all(Array.from(this.tasks)),
|
|
3896
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error(`Timeout after ${timeout}ms`)), timeout * 1e3))
|
|
3897
|
+
]);
|
|
3898
|
+
} catch (error) {
|
|
3899
|
+
logger.warn(`\u7B49\u5F85\u4EFB\u52A1\u5B8C\u6210\u8D85\u65F6 (${timeout}\u79D2)\uFF0C\u5F53\u524D\u8FD8\u6709 ${this.tasks.size} \u4E2A\u4EFB\u52A1\u5728\u8FD0\u884C`);
|
|
3900
|
+
}
|
|
3901
|
+
} else {
|
|
3902
|
+
await Promise.all(Array.from(this.tasks));
|
|
3903
|
+
}
|
|
3904
|
+
}
|
|
3905
|
+
if (this.executedResults.length === 0) {
|
|
3906
|
+
return "";
|
|
3907
|
+
}
|
|
3908
|
+
const results = [...this.executedResults];
|
|
3909
|
+
this.clear();
|
|
3910
|
+
return results;
|
|
3911
|
+
}
|
|
3912
|
+
/**
|
|
3913
|
+
* Wait for all tasks to complete
|
|
3914
|
+
*/
|
|
3915
|
+
async waitAll() {
|
|
3916
|
+
if (this.tasks.size > 0) {
|
|
3917
|
+
await Promise.all(Array.from(this.tasks));
|
|
3918
|
+
logger.info("\u6240\u6709\u4EFB\u52A1\u5DF2\u5B8C\u6210");
|
|
3919
|
+
}
|
|
3920
|
+
}
|
|
3921
|
+
/**
|
|
3922
|
+
* Get current status
|
|
3923
|
+
*/
|
|
3924
|
+
getStatus() {
|
|
3925
|
+
return {
|
|
3926
|
+
pool_size: this.poolSize,
|
|
3927
|
+
running_tasks: this.tasks.size,
|
|
3928
|
+
pending: this.todo.size,
|
|
3929
|
+
done: this.done.size,
|
|
3930
|
+
failed: this.failed.size,
|
|
3931
|
+
background_processes: this.backgroundProcesses.size,
|
|
3932
|
+
monitor_tasks: this.monitorTasks.size
|
|
3933
|
+
};
|
|
3934
|
+
}
|
|
3935
|
+
/**
|
|
3936
|
+
* Get pending tool call count
|
|
3937
|
+
*/
|
|
3938
|
+
getPendingCount() {
|
|
3939
|
+
return this.todo.size;
|
|
3940
|
+
}
|
|
3941
|
+
// Background process management methods
|
|
3942
|
+
/**
|
|
3943
|
+
* Register a background process
|
|
3944
|
+
*/
|
|
3945
|
+
registerBackgroundProcess(process2, command, cwd) {
|
|
3946
|
+
const processId = randomUUID2();
|
|
3947
|
+
this.backgroundProcesses.set(processId, {
|
|
3948
|
+
process: process2,
|
|
3949
|
+
command,
|
|
3950
|
+
pid: process2.pid,
|
|
3951
|
+
cwd
|
|
3952
|
+
});
|
|
3953
|
+
const monitorTask = this.monitorProcess(processId, process2);
|
|
3954
|
+
this.monitorTasks.set(processId, monitorTask);
|
|
3955
|
+
logger.info(`\u6CE8\u518C\u540E\u53F0\u8FDB\u7A0B: ${command} (PID: ${process2.pid}, ID: ${processId})`);
|
|
3956
|
+
return processId;
|
|
3957
|
+
}
|
|
3958
|
+
/**
|
|
3959
|
+
* Monitor process until it ends
|
|
3960
|
+
*/
|
|
3961
|
+
async monitorProcess(processId, process2) {
|
|
3962
|
+
try {
|
|
3963
|
+
await new Promise((resolve2, reject) => {
|
|
3964
|
+
process2.on("exit", (code) => {
|
|
3965
|
+
if (this.backgroundProcesses.has(processId)) {
|
|
3966
|
+
const command = this.backgroundProcesses.get(processId).command;
|
|
3967
|
+
this.backgroundProcesses.delete(processId);
|
|
3968
|
+
logger.info(`\u540E\u53F0\u8FDB\u7A0B\u5DF2\u7ED3\u675F: ${command} (PID: ${process2.pid}, \u8FD4\u56DE\u7801: ${code})`);
|
|
3969
|
+
}
|
|
3970
|
+
if (this.monitorTasks.has(processId)) {
|
|
3971
|
+
this.monitorTasks.delete(processId);
|
|
3972
|
+
}
|
|
3973
|
+
resolve2();
|
|
3974
|
+
});
|
|
3975
|
+
process2.on("error", (error) => {
|
|
3976
|
+
reject(error);
|
|
3977
|
+
});
|
|
3978
|
+
});
|
|
3979
|
+
} catch (error) {
|
|
3980
|
+
logger.error(`\u76D1\u63A7\u8FDB\u7A0B ${processId} \u65F6\u53D1\u751F\u9519\u8BEF: ${error}`);
|
|
3981
|
+
if (this.monitorTasks.has(processId)) {
|
|
3982
|
+
this.monitorTasks.delete(processId);
|
|
3983
|
+
}
|
|
3984
|
+
}
|
|
3985
|
+
}
|
|
3986
|
+
/**
|
|
3987
|
+
* Observe process stdout and stderr
|
|
3988
|
+
*/
|
|
3989
|
+
async observeProcess(processId, timeout = 5) {
|
|
3990
|
+
if (!this.backgroundProcesses.has(processId)) {
|
|
3991
|
+
return `\u672A\u627E\u5230\u8FDB\u7A0BID: ${processId}`;
|
|
3992
|
+
}
|
|
3993
|
+
const info = this.backgroundProcesses.get(processId);
|
|
3994
|
+
const process2 = info.process;
|
|
3995
|
+
return `\u8FDB\u7A0BID: ${processId}
|
|
3996
|
+
\u547D\u4EE4: ${info.command}
|
|
3997
|
+
PID: ${info.pid}`;
|
|
3998
|
+
}
|
|
3999
|
+
/**
|
|
4000
|
+
* Stop specified background process
|
|
4001
|
+
*/
|
|
4002
|
+
async stopBackgroundProcess(processId, force = false) {
|
|
4003
|
+
if (!this.backgroundProcesses.has(processId)) {
|
|
4004
|
+
return `\u672A\u627E\u5230\u8FDB\u7A0BID: ${processId}`;
|
|
4005
|
+
}
|
|
4006
|
+
try {
|
|
4007
|
+
const info = this.backgroundProcesses.get(processId);
|
|
4008
|
+
if (!info) {
|
|
4009
|
+
return `\u8FDB\u7A0BID ${processId} \u5DF2\u88AB\u79FB\u9664`;
|
|
4010
|
+
}
|
|
4011
|
+
const process2 = info.process;
|
|
4012
|
+
const command = info.command;
|
|
4013
|
+
if (process2.exitCode !== null) {
|
|
4014
|
+
return `\u8FDB\u7A0B\u5DF2\u7ECF\u7ED3\u675F (\u8FD4\u56DE\u7801: ${process2.exitCode})
|
|
4015
|
+
\u547D\u4EE4: ${command}`;
|
|
4016
|
+
}
|
|
4017
|
+
if (force) {
|
|
4018
|
+
process2.kill("SIGKILL");
|
|
4019
|
+
logger.info(`\u5F3A\u5236\u6740\u6B7B\u540E\u53F0\u8FDB\u7A0B: ${command} (PID: ${process2.pid})`);
|
|
4020
|
+
} else {
|
|
4021
|
+
process2.kill("SIGTERM");
|
|
4022
|
+
logger.info(`\u7EC8\u6B62\u540E\u53F0\u8FDB\u7A0B: ${command} (PID: ${process2.pid})`);
|
|
4023
|
+
}
|
|
4024
|
+
try {
|
|
4025
|
+
await new Promise((resolve2) => {
|
|
4026
|
+
const timeoutId = setTimeout(() => {
|
|
4027
|
+
if (!force) {
|
|
4028
|
+
process2.kill("SIGKILL");
|
|
4029
|
+
logger.warn(`\u540E\u53F0\u8FDB\u7A0B\u672A\u54CD\u5E94\u7EC8\u6B62\u4FE1\u53F7\uFF0C\u5DF2\u5F3A\u5236\u6740\u6B7B: ${command}`);
|
|
4030
|
+
}
|
|
4031
|
+
resolve2();
|
|
4032
|
+
}, 5e3);
|
|
4033
|
+
process2.on("exit", () => {
|
|
4034
|
+
clearTimeout(timeoutId);
|
|
4035
|
+
resolve2();
|
|
4036
|
+
});
|
|
4037
|
+
});
|
|
4038
|
+
} catch (error) {
|
|
4039
|
+
logger.error(`\u7B49\u5F85\u8FDB\u7A0B\u7ED3\u675F\u5931\u8D25: ${error}`);
|
|
4040
|
+
}
|
|
4041
|
+
if (this.monitorTasks.has(processId)) {
|
|
4042
|
+
const task = this.monitorTasks.get(processId);
|
|
4043
|
+
if (task) {
|
|
4044
|
+
this.monitorTasks.delete(processId);
|
|
4045
|
+
}
|
|
4046
|
+
}
|
|
4047
|
+
this.backgroundProcesses.delete(processId);
|
|
4048
|
+
return `\u540E\u53F0\u8FDB\u7A0B\u5DF2\u505C\u6B62
|
|
4049
|
+
\u8FDB\u7A0BID: ${processId}
|
|
4050
|
+
\u547D\u4EE4: ${command}
|
|
4051
|
+
\u8FD4\u56DE\u7801: ${process2.exitCode}`;
|
|
4052
|
+
} catch (error) {
|
|
4053
|
+
logger.error(`\u505C\u6B62\u540E\u53F0\u8FDB\u7A0B ${processId} \u65F6\u53D1\u751F\u9519\u8BEF: ${error}`);
|
|
4054
|
+
throw error;
|
|
4055
|
+
}
|
|
4056
|
+
}
|
|
4057
|
+
/**
|
|
4058
|
+
* Clean up all background processes
|
|
4059
|
+
*/
|
|
4060
|
+
async cleanupBackgroundProcesses() {
|
|
4061
|
+
if (this.backgroundProcesses.size === 0) {
|
|
4062
|
+
return "\u6CA1\u6709\u9700\u8981\u6E05\u7406\u7684\u540E\u53F0\u8FDB\u7A0B";
|
|
4063
|
+
}
|
|
4064
|
+
const count = this.backgroundProcesses.size;
|
|
4065
|
+
const processIds = Array.from(this.backgroundProcesses.keys());
|
|
4066
|
+
const results = [];
|
|
4067
|
+
for (const processId of processIds) {
|
|
4068
|
+
try {
|
|
4069
|
+
const result = await this.stopBackgroundProcess(processId);
|
|
4070
|
+
results.push(result);
|
|
4071
|
+
} catch (error) {
|
|
4072
|
+
logger.error(`\u6E05\u7406\u540E\u53F0\u8FDB\u7A0B ${processId} \u65F6\u53D1\u751F\u9519\u8BEF: ${error}`);
|
|
4073
|
+
results.push(`\u6E05\u7406\u8FDB\u7A0B ${processId} \u5931\u8D25: ${error}`);
|
|
4074
|
+
}
|
|
4075
|
+
}
|
|
4076
|
+
return `\u5DF2\u6E05\u7406 ${count} \u4E2A\u540E\u53F0\u8FDB\u7A0B:
|
|
4077
|
+
` + results.join("\n---\n");
|
|
4078
|
+
}
|
|
4079
|
+
/**
|
|
4080
|
+
* List all background processes
|
|
4081
|
+
*/
|
|
4082
|
+
listBackgroundProcesses() {
|
|
4083
|
+
if (this.backgroundProcesses.size === 0) {
|
|
4084
|
+
return ["idle" /* IDLE */, "\u5F53\u524D\u6CA1\u6709\u8FD0\u884C\u7684\u540E\u53F0\u8FDB\u7A0B\u3002"];
|
|
4085
|
+
}
|
|
4086
|
+
const finishedIds = [];
|
|
4087
|
+
for (const [processId, info] of this.backgroundProcesses) {
|
|
4088
|
+
if (info.process.exitCode !== null) {
|
|
4089
|
+
finishedIds.push(processId);
|
|
4090
|
+
}
|
|
4091
|
+
}
|
|
4092
|
+
for (const processId of finishedIds) {
|
|
4093
|
+
this.backgroundProcesses.delete(processId);
|
|
4094
|
+
}
|
|
4095
|
+
if (this.backgroundProcesses.size === 0) {
|
|
4096
|
+
return ["idle" /* IDLE */, "\u5F53\u524D\u6CA1\u6709\u8FD0\u884C\u7684\u540E\u53F0\u8FDB\u7A0B\u3002"];
|
|
4097
|
+
}
|
|
4098
|
+
const result = `\u5F53\u524D\u6709 ${this.backgroundProcesses.size} \u4E2A\u540E\u53F0\u8FDB\u7A0B:
|
|
4099
|
+
` + Array.from(this.backgroundProcesses.values()).map((process2) => `PID: ${process2.pid}, Command: ${process2.command}
|
|
4100
|
+
`).join("");
|
|
4101
|
+
return ["running" /* RUNNING */, result];
|
|
4102
|
+
}
|
|
4103
|
+
/**
|
|
4104
|
+
* Async context manager entry
|
|
4105
|
+
*/
|
|
4106
|
+
async enter() {
|
|
4107
|
+
return this;
|
|
4108
|
+
}
|
|
4109
|
+
/**
|
|
4110
|
+
* Async context manager exit, clean up all resources
|
|
4111
|
+
*/
|
|
4112
|
+
async exit() {
|
|
4113
|
+
await this.cleanupBackgroundProcesses();
|
|
4114
|
+
await this.waitAll();
|
|
4115
|
+
}
|
|
4116
|
+
};
|
|
4117
|
+
|
|
4118
|
+
// src/prompts/tools.ts
|
|
4119
|
+
var OUTPUT_FORMAT_PROMPT = `<System Tools OutputFormat>
|
|
4120
|
+
|
|
4121
|
+
We have three types of tasks: Generative Tasks, Analytical Tasks, and Operational Tasks.
|
|
4122
|
+
You should choose the appropriate task type based on the task description.
|
|
4123
|
+
|
|
4124
|
+
#### Generative Tasks Output Format
|
|
4125
|
+
<Generative Tasks Purpose>Create Something New</Generative Tasks Purpose>
|
|
4126
|
+
|
|
4127
|
+
<Generative Tasks Definition>
|
|
4128
|
+
These tasks focus on creating new artifacts from scratch, such as text, code, designs, or configurations.
|
|
4129
|
+
The model synthesizes information and produces original outputs.
|
|
4130
|
+
</Generative Tasks Definition>
|
|
4131
|
+
|
|
4132
|
+
<Generative Tasks Output Format>
|
|
4133
|
+
Output a sequence of tool calls in the format below. Replace <ToolName.method_name> and <args_name> with actual tool and parameter names.
|
|
4134
|
+
|
|
4135
|
+
You may interleave tool calls with reasoning or explanations as needed:
|
|
4136
|
+
|
|
4137
|
+
[Your reasoning or explanation here...]
|
|
4138
|
+
<ToolName.method_name><args_name1>args_value1</args_name1><args_name2>args_value2</args_name2>...</ToolName.method_name>
|
|
4139
|
+
|
|
4140
|
+
[Additional thoughts or context...]
|
|
4141
|
+
<ToolName2.method_name2><args_name1>args_value1</args_name1><args_name2>args_value2</args_name2>...</ToolName2.method_name2>
|
|
4142
|
+
...
|
|
4143
|
+
</Generative Tasks Output Format>
|
|
4144
|
+
|
|
4145
|
+
|
|
4146
|
+
### Analytical Tasks Output Format
|
|
4147
|
+
<Analytical Tasks Purpose>Understand, Diagnose & Evaluate Existing Information</Analytical Tasks Purpose>
|
|
4148
|
+
|
|
4149
|
+
<Analytical Tasks Definition>
|
|
4150
|
+
Analytical tasks focus on understanding, decomposing, diagnosing, and evaluating existing information.
|
|
4151
|
+
They include requirement analysis, code and system comprehension, quality evaluation, debugging and root-cause analysis, and data or behavioral analysis.
|
|
4152
|
+
Such tasks produce structured insights and decisions that guide subsequent generative or operational steps.
|
|
4153
|
+
</Analytical Tasks Definition>
|
|
4154
|
+
|
|
4155
|
+
<Analytical Tasks Output Format>
|
|
4156
|
+
You should fellow the format below, replace the <ToolName.method_name> and <args_name> with the actual name.
|
|
4157
|
+
|
|
4158
|
+
Thoughts: ...
|
|
4159
|
+
ToolCall: <ToolName.method_name><args_name1>args_value1</args_name1><args_name2>args_value2</args_name2>...</ToolName.method_name>
|
|
4160
|
+
// stop ToolCall here, and wait for the Observation of the ToolCall.
|
|
4161
|
+
|
|
4162
|
+
</Analytical Tasks Output Format>
|
|
4163
|
+
|
|
4164
|
+
### Operational Tasks Output Format
|
|
4165
|
+
|
|
4166
|
+
<Operational Tasks Purpose>Act & Operate Tools</Operational Tasks Purpose>
|
|
4167
|
+
|
|
4168
|
+
<Operational Tasks Definition>
|
|
4169
|
+
These tasks involve performing concrete actions through tools, systems, or commands.
|
|
4170
|
+
The focus is on executing operations rather than generating or analyzing content.
|
|
4171
|
+
</Operational Tasks Definition>
|
|
4172
|
+
|
|
4173
|
+
<Operational Tasks Output Format>
|
|
4174
|
+
You should follow the format below, replace the <ToolName.method_name> and <args_name> with the actual name.
|
|
4175
|
+
|
|
4176
|
+
<ToolName.method_name><args_name1>args_value1</args_name1><args_name2>args_value2</args_name2>...</ToolName.method_name>
|
|
4177
|
+
</Operational Tasks Output Format>
|
|
4178
|
+
|
|
4179
|
+
**YOUR OUTPUT MUST INCLUDE THE ACTUAL <args_name> AND <args_value>!!!**
|
|
4180
|
+
**If the task is completed, simply return the completion information like <TaskCompletion>your_completion_information</TaskCompletion>, No any tool call should be included.**
|
|
4181
|
+
</System Tools OutputFormat>
|
|
4182
|
+
`;
|
|
4183
|
+
var SYSTEM_TOOLS_PROMPT = `
|
|
4184
|
+
## System Tools And System Tools Call Format
|
|
4185
|
+
|
|
4186
|
+
System Tools are represented by a predefined XML-like syntax structure. This structure encapsulates parameter lists within tags such as \`<ToolName.method_name>\`.
|
|
4187
|
+
By identifying and matching keywords inside these tags, the system automatically parses the target tool name and its corresponding input parameter values to execute subsequent tool calls.
|
|
4188
|
+
|
|
4189
|
+
### Available System Tools
|
|
4190
|
+
{availableSystemTools}
|
|
4191
|
+
|
|
4192
|
+
### Description of Available System Tools
|
|
4193
|
+
{descOfSystemTools}
|
|
4194
|
+
|
|
4195
|
+
### System Tools Output Format
|
|
4196
|
+
{outputFormat}
|
|
4197
|
+
`;
|
|
4198
|
+
|
|
4199
|
+
// src/agent.ts
|
|
4200
|
+
var Agent = class {
|
|
4201
|
+
name;
|
|
4202
|
+
profile;
|
|
4203
|
+
system;
|
|
4204
|
+
tools;
|
|
4205
|
+
functionCallingTools = [];
|
|
4206
|
+
mcpServerNames;
|
|
4207
|
+
modelConfig;
|
|
4208
|
+
verbose;
|
|
4209
|
+
model;
|
|
4210
|
+
history;
|
|
4211
|
+
subAgents = [];
|
|
4212
|
+
toolcallManagerPoolSize;
|
|
4213
|
+
postProcessor;
|
|
4214
|
+
useFunctionCalling;
|
|
4215
|
+
constructor(config2) {
|
|
4216
|
+
const {
|
|
4217
|
+
name,
|
|
4218
|
+
profile,
|
|
4219
|
+
system,
|
|
4220
|
+
tools: tools2 = [],
|
|
4221
|
+
subAgents = [],
|
|
4222
|
+
mcpServerNames = [],
|
|
4223
|
+
modelConfig,
|
|
4224
|
+
verbose = false,
|
|
4225
|
+
useFunctionCalling = false,
|
|
4226
|
+
postProcessor = null,
|
|
4227
|
+
toolcallManagerPoolSize = 5
|
|
4228
|
+
} = config2;
|
|
4229
|
+
this.name = name;
|
|
4230
|
+
this.profile = profile;
|
|
4231
|
+
this.verbose = verbose;
|
|
4232
|
+
this.modelConfig = modelConfig || "deepseek";
|
|
4233
|
+
this.subAgents = subAgents || [];
|
|
4234
|
+
this.postProcessor = postProcessor;
|
|
4235
|
+
this.useFunctionCalling = useFunctionCalling;
|
|
4236
|
+
const agentToolNames = registerAgentAsTool(this.subAgents, Boolean(this.verbose));
|
|
4237
|
+
this.tools = [...tools2, ...agentToolNames];
|
|
4238
|
+
this.mcpServerNames = mcpServerNames;
|
|
4239
|
+
const systemPrompt = system || profile || "";
|
|
4240
|
+
this.system = this.tools.length > 0 ? this.setSystem(systemPrompt, this.tools) : systemPrompt;
|
|
4241
|
+
this.model = new Model(this.modelConfig);
|
|
4242
|
+
const modelNameForHistory = typeof this.modelConfig === "string" ? this.modelConfig : this.modelConfig.model || "deepseek";
|
|
4243
|
+
this.history = new MessageHistory(modelNameForHistory, this.system, this.model.getConfig().contextWindowTokens);
|
|
4244
|
+
if (this.useFunctionCalling) {
|
|
4245
|
+
this.functionCallingTools = this.setFunctionCallingTools(this.tools);
|
|
4246
|
+
}
|
|
4247
|
+
if (this.verbose) {
|
|
4248
|
+
logger.info(`
|
|
4249
|
+
========== [${this.name}] System Prompt ==========`);
|
|
4250
|
+
logger.info(this.system);
|
|
4251
|
+
logger.info(`========================================
|
|
4252
|
+
`);
|
|
4253
|
+
if (typeof this.verbose === "number" && this.verbose > 1) {
|
|
4254
|
+
logger.info(`[${this.name}] Tools: ${this.tools.join(", ")}`);
|
|
4255
|
+
logger.info("");
|
|
4256
|
+
}
|
|
4257
|
+
}
|
|
4258
|
+
this.toolcallManagerPoolSize = toolcallManagerPoolSize;
|
|
4259
|
+
}
|
|
4260
|
+
/**
|
|
4261
|
+
* Get the current system prompt
|
|
4262
|
+
*/
|
|
4263
|
+
get systemPrompt() {
|
|
4264
|
+
return this.system;
|
|
4265
|
+
}
|
|
4266
|
+
/**
|
|
4267
|
+
* Set the system prompt and update history
|
|
4268
|
+
*/
|
|
4269
|
+
set systemPrompt(value) {
|
|
4270
|
+
this.system = value;
|
|
4271
|
+
if (this.history) {
|
|
4272
|
+
this.history.updateSystem(value);
|
|
4273
|
+
}
|
|
4274
|
+
}
|
|
4275
|
+
/**
|
|
4276
|
+
* Set the system prompt with system tools descriptions
|
|
4277
|
+
* @private
|
|
4278
|
+
*/
|
|
4279
|
+
setSystem(system, systemTools) {
|
|
4280
|
+
let toolDescriptions = "";
|
|
4281
|
+
for (const tool of systemTools) {
|
|
4282
|
+
if (SystemToolStore.hasTool(tool)) {
|
|
4283
|
+
const toolDesc = SystemToolStore.getTool(tool);
|
|
4284
|
+
toolDescriptions += (toolDesc?.desc || "") + "\n\n";
|
|
4285
|
+
}
|
|
4286
|
+
}
|
|
4287
|
+
if (systemTools.length > 0) {
|
|
4288
|
+
const availableSystemTools = systemTools.map((t) => `- ${t}`).join("\n");
|
|
4289
|
+
return system + "\n" + SYSTEM_TOOLS_PROMPT.replace("{availableSystemTools}", availableSystemTools).replace("{descOfSystemTools}", toolDescriptions).replace("{outputFormat}", OUTPUT_FORMAT_PROMPT);
|
|
4290
|
+
}
|
|
4291
|
+
return system;
|
|
4292
|
+
}
|
|
4293
|
+
/**
|
|
4294
|
+
* Set up function calling tools for OpenAI-style function calling
|
|
4295
|
+
* @private
|
|
4296
|
+
*/
|
|
4297
|
+
setFunctionCallingTools(tools2) {
|
|
4298
|
+
const openaiToolCalls = [];
|
|
4299
|
+
for (let tool of tools2) {
|
|
4300
|
+
tool = tool.replace(".", "-");
|
|
4301
|
+
if (!FunctionCallingStore.hasTool(tool)) {
|
|
4302
|
+
logger.warn(`Tool ${tool} not registered in FunctionCallingStore.`);
|
|
4303
|
+
} else {
|
|
4304
|
+
const toolSchema = FunctionCallingStore.getToolcallSchema(tool, "openai");
|
|
4305
|
+
if (toolSchema && Object.keys(toolSchema).length > 0) {
|
|
4306
|
+
openaiToolCalls.push(toolSchema);
|
|
4307
|
+
}
|
|
4308
|
+
}
|
|
4309
|
+
}
|
|
4310
|
+
return openaiToolCalls;
|
|
4311
|
+
}
|
|
4312
|
+
/**
|
|
4313
|
+
* Agent loop - processes user input and handles tool calls
|
|
4314
|
+
* @private
|
|
4315
|
+
*/
|
|
4316
|
+
async _agentLoop(instruction, images) {
|
|
4317
|
+
const instructionMsg = Message.fromUserMsg(instruction, images);
|
|
4318
|
+
this.history.addMessage(instructionMsg);
|
|
4319
|
+
if (this.verbose) {
|
|
4320
|
+
logger.info(`
|
|
4321
|
+
[${this.name}] Received: ${instruction}`);
|
|
4322
|
+
}
|
|
4323
|
+
while (true) {
|
|
4324
|
+
this.history.truncate();
|
|
4325
|
+
if (typeof this.verbose === "number" && this.verbose > 2) {
|
|
4326
|
+
logger.debug(
|
|
4327
|
+
`History raw messages before extract toolcall messages:
|
|
4328
|
+
${JSON.stringify(await this.history.formatForApi())}`
|
|
4329
|
+
);
|
|
4330
|
+
}
|
|
4331
|
+
const response = await this.model.achat(
|
|
4332
|
+
await this.history.formatForApi(),
|
|
4333
|
+
this.useFunctionCalling ? this.functionCallingTools : void 0
|
|
4334
|
+
);
|
|
4335
|
+
const systemToolcallResponses = response.filter((r) => r.type === "system").map((r) => r.extractedResult()).filter((r) => typeof r === "string");
|
|
4336
|
+
if (systemToolcallResponses.length > 0) {
|
|
4337
|
+
const systemToolcallResponse = systemToolcallResponses.join("\n");
|
|
4338
|
+
this.history.addMessage("assistant", systemToolcallResponse);
|
|
4339
|
+
}
|
|
4340
|
+
const userToolcallMessages = response.filter((r) => r.type === "user").map((r) => r.extractedResult());
|
|
4341
|
+
if (userToolcallMessages.length > 0) {
|
|
4342
|
+
const userToolcallMessagesFlattened = [];
|
|
4343
|
+
for (const item of userToolcallMessages) {
|
|
4344
|
+
if (Array.isArray(item)) {
|
|
4345
|
+
userToolcallMessagesFlattened.push(...item);
|
|
4346
|
+
} else {
|
|
4347
|
+
userToolcallMessagesFlattened.push(item);
|
|
4348
|
+
}
|
|
4349
|
+
}
|
|
4350
|
+
for (const toolcall of userToolcallMessagesFlattened) {
|
|
4351
|
+
this.history.addMessage(toolcall);
|
|
4352
|
+
}
|
|
4353
|
+
}
|
|
4354
|
+
if (typeof this.verbose === "number" && this.verbose > 1) {
|
|
4355
|
+
const messages = await this.history.formatForApi();
|
|
4356
|
+
const last3 = messages.slice(-3);
|
|
4357
|
+
logger.debug(`History raw messages after extract toolcall messages (last 3):
|
|
4358
|
+
${JSON.stringify(last3)}`);
|
|
4359
|
+
}
|
|
4360
|
+
logger.debug(`History usage: ${this.history.formattedContextUsage}`);
|
|
4361
|
+
const toolcallManager = new ToolcallManager(this.toolcallManagerPoolSize, [SystemToolStore, FunctionCallingStore]);
|
|
4362
|
+
const allToolCalls = [];
|
|
4363
|
+
const systemToolMap = {};
|
|
4364
|
+
const systemToolNames = SystemToolStore.listTools();
|
|
4365
|
+
for (const name of systemToolNames) {
|
|
4366
|
+
const tool = SystemToolStore.getTool(name);
|
|
4367
|
+
if (tool) {
|
|
4368
|
+
systemToolMap[name] = tool;
|
|
4369
|
+
}
|
|
4370
|
+
}
|
|
4371
|
+
for (const r of response) {
|
|
4372
|
+
const result = r.extractedResult();
|
|
4373
|
+
if (typeof result === "string") {
|
|
4374
|
+
if (result.trim().length > 0) {
|
|
4375
|
+
const extracted = extractToolcallsFromStr(result, systemToolMap);
|
|
4376
|
+
allToolCalls.push(...extracted);
|
|
4377
|
+
}
|
|
4378
|
+
} else if (typeof result === "object" && result !== null) {
|
|
4379
|
+
if (!Array.isArray(result) && result.role === "assistant" && result.tool_calls && Array.isArray(result.tool_calls)) {
|
|
4380
|
+
for (const tc of result.tool_calls) {
|
|
4381
|
+
if (tc.type === "function" && tc.function) {
|
|
4382
|
+
let args = {};
|
|
4383
|
+
try {
|
|
4384
|
+
args = JSON.parse(tc.function.arguments);
|
|
4385
|
+
} catch (e) {
|
|
4386
|
+
logger.warn(`Failed to parse arguments for tool ${tc.function.name}: ${tc.function.arguments}`);
|
|
4387
|
+
args = { raw_args: tc.function.arguments };
|
|
4388
|
+
}
|
|
4389
|
+
allToolCalls.push(
|
|
4390
|
+
new Toolcall({
|
|
4391
|
+
name: tc.function.name,
|
|
4392
|
+
input: args,
|
|
4393
|
+
toolCallId: tc.id,
|
|
4394
|
+
type: "user",
|
|
4395
|
+
rawContentFromLlm: tc.function.arguments
|
|
4396
|
+
})
|
|
4397
|
+
);
|
|
4398
|
+
}
|
|
4399
|
+
}
|
|
4400
|
+
} else if (Array.isArray(result)) {
|
|
4401
|
+
for (const item of result) {
|
|
4402
|
+
if (item.type === "function" && item.function) {
|
|
4403
|
+
let args = {};
|
|
4404
|
+
try {
|
|
4405
|
+
args = JSON.parse(item.function.arguments);
|
|
4406
|
+
} catch (e) {
|
|
4407
|
+
logger.warn(`Failed to parse arguments for tool ${item.function.name}: ${item.function.arguments}`);
|
|
4408
|
+
args = { raw_args: item.function.arguments };
|
|
4409
|
+
}
|
|
4410
|
+
allToolCalls.push(
|
|
4411
|
+
new Toolcall({
|
|
4412
|
+
name: item.function.name,
|
|
4413
|
+
input: args,
|
|
4414
|
+
toolCallId: item.id,
|
|
4415
|
+
type: "user",
|
|
4416
|
+
rawContentFromLlm: item.function.arguments
|
|
4417
|
+
})
|
|
4418
|
+
);
|
|
4419
|
+
} else if (item.type === "tool_use") {
|
|
4420
|
+
allToolCalls.push(
|
|
4421
|
+
new Toolcall({
|
|
4422
|
+
name: item.name,
|
|
4423
|
+
input: item.input,
|
|
4424
|
+
toolCallId: item.id,
|
|
4425
|
+
type: "user"
|
|
4426
|
+
})
|
|
4427
|
+
);
|
|
4428
|
+
}
|
|
4429
|
+
}
|
|
4430
|
+
}
|
|
4431
|
+
}
|
|
4432
|
+
}
|
|
4433
|
+
if (allToolCalls.length > 0) {
|
|
4434
|
+
toolcallManager.addToolcall(allToolCalls);
|
|
4435
|
+
const obs = await toolcallManager.observe(true, 60);
|
|
4436
|
+
if (Array.isArray(obs)) {
|
|
4437
|
+
for (const item of obs) {
|
|
4438
|
+
if (typeof item === "object") {
|
|
4439
|
+
this.history.addMessage(item);
|
|
4440
|
+
} else if (typeof item === "string") {
|
|
4441
|
+
this.history.addMessage("user", item);
|
|
4442
|
+
}
|
|
4443
|
+
}
|
|
4444
|
+
} else if (typeof obs === "string") {
|
|
4445
|
+
this.history.addMessage("user", obs);
|
|
4446
|
+
} else if (typeof obs === "object") {
|
|
4447
|
+
this.history.addMessage(obs);
|
|
4448
|
+
} else {
|
|
4449
|
+
logger.warn(`Unknown observation type: ${typeof obs}`);
|
|
4450
|
+
}
|
|
4451
|
+
} else {
|
|
4452
|
+
for (const r of response) {
|
|
4453
|
+
if (r.type === "system") {
|
|
4454
|
+
const result = r.extractedResult();
|
|
4455
|
+
if (typeof result === "string") {
|
|
4456
|
+
const cleanResult = result.replace(/<TaskCompletion>/g, "").replace(/<\/TaskCompletion>/g, "").trim();
|
|
4457
|
+
return cleanResult;
|
|
4458
|
+
}
|
|
4459
|
+
}
|
|
4460
|
+
}
|
|
4461
|
+
return "";
|
|
4462
|
+
}
|
|
4463
|
+
}
|
|
4464
|
+
}
|
|
4465
|
+
/**
|
|
4466
|
+
* Run the agent with given instruction
|
|
4467
|
+
*/
|
|
4468
|
+
async run(instruction, images) {
|
|
4469
|
+
const stack = new AsyncExitStack();
|
|
4470
|
+
try {
|
|
4471
|
+
for (const serverName of this.mcpServerNames) {
|
|
4472
|
+
await FunctionCallingStore.addMcpTools?.(this.name, serverName, stack);
|
|
4473
|
+
const mcpTools = FunctionCallingStore.getMcpToolsSchemas?.(this.name, serverName, "openai") || [];
|
|
4474
|
+
if (this.verbose) {
|
|
4475
|
+
logger.info(`[${this.name}] Loaded MCP tools from ${serverName}: ${mcpTools.length} tools found.`);
|
|
4476
|
+
if (typeof this.verbose === "number" && this.verbose > 1) {
|
|
4477
|
+
logger.info(`[${this.name}] MCP tools sample: ${JSON.stringify(mcpTools).substring(0, 200)} ...`);
|
|
4478
|
+
}
|
|
4479
|
+
}
|
|
4480
|
+
if (this.useFunctionCalling) {
|
|
4481
|
+
this.functionCallingTools.push(...mcpTools);
|
|
4482
|
+
}
|
|
4483
|
+
}
|
|
4484
|
+
const responseText = await this._agentLoop(instruction, images);
|
|
4485
|
+
if (this.postProcessor) {
|
|
4486
|
+
try {
|
|
4487
|
+
return await this.postProcessor(responseText);
|
|
4488
|
+
} catch (error) {
|
|
4489
|
+
logger.error(`Post-processor error in agent ${this.name}:`, error);
|
|
4490
|
+
throw new Error(`Post-processor failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
4491
|
+
}
|
|
4492
|
+
}
|
|
4493
|
+
return responseText;
|
|
4494
|
+
} catch (error) {
|
|
4495
|
+
logger.error(`Agent ${this.name} execution error:`);
|
|
4496
|
+
logger.error(error);
|
|
4497
|
+
if (error instanceof Error) {
|
|
4498
|
+
logger.error(`Error message: ${error.message}`);
|
|
4499
|
+
logger.error(`Error stack: ${error.stack}`);
|
|
4500
|
+
}
|
|
4501
|
+
throw error;
|
|
4502
|
+
} finally {
|
|
4503
|
+
await stack.close();
|
|
4504
|
+
}
|
|
4505
|
+
}
|
|
4506
|
+
/**
|
|
4507
|
+
* Get agent profile
|
|
4508
|
+
*/
|
|
4509
|
+
getProfile() {
|
|
4510
|
+
return this.profile;
|
|
4511
|
+
}
|
|
4512
|
+
/**
|
|
4513
|
+
* Get tools
|
|
4514
|
+
*/
|
|
4515
|
+
getTools() {
|
|
4516
|
+
return [...this.tools];
|
|
4517
|
+
}
|
|
4518
|
+
/**
|
|
4519
|
+
* Get function calling tools schemas
|
|
4520
|
+
*/
|
|
4521
|
+
getFunctionCallingTools() {
|
|
4522
|
+
return [...this.functionCallingTools];
|
|
4523
|
+
}
|
|
4524
|
+
/**
|
|
4525
|
+
* Get MCP server names
|
|
4526
|
+
*/
|
|
4527
|
+
getMcpServerNames() {
|
|
4528
|
+
return [...this.mcpServerNames];
|
|
4529
|
+
}
|
|
4530
|
+
/**
|
|
4531
|
+
* Get model config (string or ModelConfig object)
|
|
4532
|
+
*/
|
|
4533
|
+
getModelConfig() {
|
|
4534
|
+
return this.modelConfig;
|
|
4535
|
+
}
|
|
4536
|
+
/**
|
|
4537
|
+
* Get model name (for backward compatibility)
|
|
4538
|
+
*/
|
|
4539
|
+
getModelName() {
|
|
4540
|
+
if (typeof this.modelConfig === "string") {
|
|
4541
|
+
return this.modelConfig;
|
|
4542
|
+
}
|
|
4543
|
+
return this.modelConfig.model || "deepseek";
|
|
4544
|
+
}
|
|
4545
|
+
/**
|
|
4546
|
+
* Get verbose setting
|
|
4547
|
+
*/
|
|
4548
|
+
getVerbose() {
|
|
4549
|
+
return this.verbose;
|
|
4550
|
+
}
|
|
4551
|
+
/**
|
|
4552
|
+
* Get sub-agents
|
|
4553
|
+
*/
|
|
4554
|
+
getSubAgents() {
|
|
4555
|
+
return [...this.subAgents];
|
|
4556
|
+
}
|
|
4557
|
+
};
|
|
4558
|
+
|
|
4559
|
+
// src/environment/base.ts
|
|
4560
|
+
var BaseEnvironment = class {
|
|
4561
|
+
/**
|
|
4562
|
+
* List of agents in the environment
|
|
4563
|
+
*/
|
|
4564
|
+
agents = [];
|
|
4565
|
+
/**
|
|
4566
|
+
* Assign skills to agent with their names
|
|
4567
|
+
*/
|
|
4568
|
+
agentSkills = {};
|
|
4569
|
+
constructor(agents = [], agentSkills = {}) {
|
|
4570
|
+
this.agents = agents;
|
|
4571
|
+
this.agentSkills = agentSkills;
|
|
4572
|
+
this.setAgentSkills();
|
|
4573
|
+
}
|
|
4574
|
+
/**
|
|
4575
|
+
* Set agent skills after initialization
|
|
4576
|
+
*/
|
|
4577
|
+
setAgentSkills() {
|
|
4578
|
+
if (this.agents.length === 0 || Object.keys(this.agentSkills).length === 0) {
|
|
4579
|
+
return;
|
|
4580
|
+
}
|
|
4581
|
+
const skiller = new SkillsTool(SKILLS_DIR);
|
|
4582
|
+
if ("all" in this.agentSkills) {
|
|
4583
|
+
for (const agent of this.agents) {
|
|
4584
|
+
this.agentSkills[agent.name] = this.agentSkills["all"];
|
|
4585
|
+
}
|
|
4586
|
+
delete this.agentSkills["all"];
|
|
4587
|
+
}
|
|
4588
|
+
logger.debug(`Assigning skills to agents: ${JSON.stringify(this.agentSkills)}`);
|
|
4589
|
+
for (const [agentName, skillNames] of Object.entries(this.agentSkills)) {
|
|
4590
|
+
for (const agent of this.agents) {
|
|
4591
|
+
if (agent.name === agentName) {
|
|
4592
|
+
const skillDescriptions = [];
|
|
4593
|
+
(async () => {
|
|
4594
|
+
for (const skillName of skillNames) {
|
|
4595
|
+
try {
|
|
4596
|
+
const skillDescription2 = await skiller.readSkillDescription(skillName);
|
|
4597
|
+
skillDescriptions.push(skillDescription2);
|
|
4598
|
+
} catch (error) {
|
|
4599
|
+
logger.warn(`Failed to read skill description for ${skillName}:`, error);
|
|
4600
|
+
}
|
|
4601
|
+
}
|
|
4602
|
+
const skillDescription = "\n--- <SKILLS START> ---\n" + skillDescriptions.join("\n-----\n") + "\n--- <SKILLS END> ---\n";
|
|
4603
|
+
agent.systemPrompt = agent.systemPrompt + skillDescription;
|
|
4604
|
+
})();
|
|
4605
|
+
}
|
|
4606
|
+
}
|
|
4607
|
+
}
|
|
4608
|
+
}
|
|
4609
|
+
/**
|
|
4610
|
+
* Check if instruction has agent name
|
|
4611
|
+
*/
|
|
4612
|
+
hasAgentName(instruction) {
|
|
4613
|
+
if (!instruction.includes("@")) {
|
|
4614
|
+
return false;
|
|
4615
|
+
}
|
|
4616
|
+
for (const agent of this.agents) {
|
|
4617
|
+
if (instruction.includes(`@${agent.name}`)) {
|
|
4618
|
+
return true;
|
|
4619
|
+
}
|
|
4620
|
+
}
|
|
4621
|
+
return false;
|
|
4622
|
+
}
|
|
4623
|
+
/**
|
|
4624
|
+
* Post process instruction
|
|
4625
|
+
*/
|
|
4626
|
+
postProcessInstruction(instruction, agentNames) {
|
|
4627
|
+
if (["/q", "exit", "quit"].includes(instruction.toLowerCase()) || agentNames.length === 0) {
|
|
4628
|
+
return ["quit" /* QUIT */, instruction];
|
|
4629
|
+
}
|
|
4630
|
+
if (agentNames.length === 1) {
|
|
4631
|
+
return ["valid" /* VALID */, `${instruction}@${agentNames[0]}`];
|
|
4632
|
+
}
|
|
4633
|
+
if (instruction.includes("@all")) {
|
|
4634
|
+
return ["sendToAll" /* SEND_TO_ALL */, instruction];
|
|
4635
|
+
}
|
|
4636
|
+
if (this.hasAgentName(instruction)) {
|
|
4637
|
+
return ["valid" /* VALID */, instruction];
|
|
4638
|
+
}
|
|
4639
|
+
return ["noAvailableAgentName" /* NO_AVAILABLE_AGENT_NAME */, instruction];
|
|
4640
|
+
}
|
|
4641
|
+
/**
|
|
4642
|
+
* Run with a single goal until completion (non-interactive mode)
|
|
4643
|
+
*/
|
|
4644
|
+
async runGoal(goal) {
|
|
4645
|
+
if (this.agents.length === 0) {
|
|
4646
|
+
logger.error("No agents in the environment.");
|
|
4647
|
+
return;
|
|
4648
|
+
}
|
|
4649
|
+
const [instructionType, processedInstruction] = this.postProcessInstruction(
|
|
4650
|
+
goal,
|
|
4651
|
+
this.agents.map((agent) => agent.name)
|
|
4652
|
+
);
|
|
4653
|
+
if (instructionType === "quit" /* QUIT */) {
|
|
4654
|
+
logger.info("Invalid goal instruction.");
|
|
4655
|
+
return;
|
|
4656
|
+
}
|
|
4657
|
+
if (instructionType === "noAvailableAgentName" /* NO_AVAILABLE_AGENT_NAME */) {
|
|
4658
|
+
logger.warn("No available agent name in instruction. Please provide instruction with '@agent_name'.");
|
|
4659
|
+
return;
|
|
4660
|
+
}
|
|
4661
|
+
logger.info("Goal:", goal);
|
|
4662
|
+
logger.info("Processing goal...\n");
|
|
4663
|
+
for (const agent of this.agents) {
|
|
4664
|
+
if (instructionType === "sendToAll" /* SEND_TO_ALL */ || processedInstruction.includes(`@${agent.name}`)) {
|
|
4665
|
+
try {
|
|
4666
|
+
const response = await agent.run(processedInstruction);
|
|
4667
|
+
logger.info(`
|
|
4668
|
+
> Agent ${agent.name} completed the goal.`);
|
|
4669
|
+
logger.info(`Final response: ${response}`);
|
|
4670
|
+
} catch (error) {
|
|
4671
|
+
logger.error(`Error running agent ${agent.name}:`, error);
|
|
4672
|
+
}
|
|
4673
|
+
}
|
|
4674
|
+
}
|
|
4675
|
+
}
|
|
4676
|
+
/**
|
|
4677
|
+
* Run the environment (interactive mode)
|
|
4678
|
+
*/
|
|
4679
|
+
async run() {
|
|
4680
|
+
const readline = await import("readline");
|
|
4681
|
+
const rl = readline.createInterface({
|
|
4682
|
+
input: process.stdin,
|
|
4683
|
+
output: process.stdout
|
|
4684
|
+
});
|
|
4685
|
+
const question = (prompt) => {
|
|
4686
|
+
return new Promise((resolve2) => {
|
|
4687
|
+
rl.question(prompt, resolve2);
|
|
4688
|
+
});
|
|
4689
|
+
};
|
|
4690
|
+
try {
|
|
4691
|
+
while (true) {
|
|
4692
|
+
try {
|
|
4693
|
+
let instruction;
|
|
4694
|
+
if (this.agents.length === 0) {
|
|
4695
|
+
logger.error("No agents in the environment.");
|
|
4696
|
+
break;
|
|
4697
|
+
} else if (this.agents.length === 1) {
|
|
4698
|
+
instruction = await question("Enter your instruction (or '/q' to quit): ");
|
|
4699
|
+
} else {
|
|
4700
|
+
const agentNames = this.agents.map((agent) => agent.name).join(", ");
|
|
4701
|
+
instruction = await question(
|
|
4702
|
+
`Enter your instruction with '@agent_name' (or '/q' to quit), available agents: ${agentNames}: `
|
|
4703
|
+
);
|
|
4704
|
+
}
|
|
4705
|
+
const [instructionType, processedInstruction] = this.postProcessInstruction(
|
|
4706
|
+
instruction,
|
|
4707
|
+
this.agents.map((agent) => agent.name)
|
|
4708
|
+
);
|
|
4709
|
+
if (instructionType === "quit" /* QUIT */) {
|
|
4710
|
+
logger.info("Quitting...");
|
|
4711
|
+
break;
|
|
4712
|
+
} else if (instructionType === "noAvailableAgentName" /* NO_AVAILABLE_AGENT_NAME */) {
|
|
4713
|
+
logger.warn("No available agent name in instruction. Please enter your instruction with '@agent_name'.");
|
|
4714
|
+
continue;
|
|
4715
|
+
}
|
|
4716
|
+
logger.info("Processing your request...");
|
|
4717
|
+
for (const agent of this.agents) {
|
|
4718
|
+
if (instructionType === "sendToAll" /* SEND_TO_ALL */ || processedInstruction.includes(`@${agent.name}`)) {
|
|
4719
|
+
const response = await agent.run(processedInstruction);
|
|
4720
|
+
logger.info(`Agent ${agent.name} response:`, response);
|
|
4721
|
+
}
|
|
4722
|
+
}
|
|
4723
|
+
} catch (error) {
|
|
4724
|
+
logger.error("Error processing instruction:", error);
|
|
4725
|
+
}
|
|
4726
|
+
}
|
|
4727
|
+
} finally {
|
|
4728
|
+
rl.close();
|
|
4729
|
+
}
|
|
4730
|
+
}
|
|
4731
|
+
};
|
|
4732
|
+
|
|
4733
|
+
// src/environment/coding.ts
|
|
4734
|
+
import * as fs8 from "fs";
|
|
4735
|
+
var CodingEnvironment = class extends BaseEnvironment {
|
|
4736
|
+
/**
|
|
4737
|
+
* Workspace directory
|
|
4738
|
+
*/
|
|
4739
|
+
workspaceDir = "";
|
|
4740
|
+
constructor(agents = [], agentSkills = {}, workspaceDir = "") {
|
|
4741
|
+
super(agents, agentSkills);
|
|
4742
|
+
this.workspaceDir = workspaceDir;
|
|
4743
|
+
this.setWorkspaceDir();
|
|
4744
|
+
}
|
|
4745
|
+
/**
|
|
4746
|
+
* Set workspace directory after initialization
|
|
4747
|
+
*/
|
|
4748
|
+
setWorkspaceDir() {
|
|
4749
|
+
if (!this.workspaceDir) {
|
|
4750
|
+
this.workspaceDir = WORKSPACE_DIR;
|
|
4751
|
+
}
|
|
4752
|
+
if (!fs8.existsSync(this.workspaceDir)) {
|
|
4753
|
+
fs8.mkdirSync(this.workspaceDir, { recursive: true });
|
|
4754
|
+
} else {
|
|
4755
|
+
logger.debug(`Workspace directory already exists: ${this.workspaceDir}`);
|
|
4756
|
+
}
|
|
4757
|
+
for (const agent of this.agents) {
|
|
4758
|
+
if (!agent.systemPrompt.includes(this.workspaceDir)) {
|
|
4759
|
+
agent.systemPrompt = agent.systemPrompt + `
|
|
4760
|
+
|
|
4761
|
+
Your workspace directory is: <workspace>${this.workspaceDir}</workspace>
|
|
4762
|
+
|
|
4763
|
+
**Note**: your any output files must be saved in ${this.workspaceDir}`;
|
|
4764
|
+
}
|
|
4765
|
+
}
|
|
4766
|
+
logger.debug(`Setting workspace directory: ${this.workspaceDir}`);
|
|
4767
|
+
for (const agent of this.agents) {
|
|
4768
|
+
logger.debug(`Agent ${agent.name} system: ${agent.systemPrompt}`);
|
|
4769
|
+
}
|
|
4770
|
+
}
|
|
4771
|
+
};
|
|
4772
|
+
export {
|
|
4773
|
+
Agent,
|
|
4774
|
+
ApiTool,
|
|
4775
|
+
BaseEnvironment,
|
|
4776
|
+
CodingEnvironment,
|
|
4777
|
+
CommandLineTool,
|
|
4778
|
+
DEFAULT_CONFIG,
|
|
4779
|
+
DEFAULT_SETTINGS,
|
|
4780
|
+
ENV_VARS,
|
|
4781
|
+
ERROR_MESSAGES,
|
|
4782
|
+
EvoltError,
|
|
4783
|
+
ExtendStateMachineTool,
|
|
4784
|
+
FileEditor,
|
|
4785
|
+
FunctionCallingStore,
|
|
4786
|
+
GitTool,
|
|
4787
|
+
LOG_LEVELS,
|
|
4788
|
+
MESSAGE_ROLES,
|
|
4789
|
+
Message,
|
|
4790
|
+
MessageHistory,
|
|
4791
|
+
Model,
|
|
4792
|
+
ModelError,
|
|
4793
|
+
ReflectTool,
|
|
4794
|
+
Reply2HumanTool,
|
|
4795
|
+
SKILLS_DIR,
|
|
4796
|
+
SkillsTool,
|
|
4797
|
+
SystemToolStore,
|
|
4798
|
+
TOOL_CALL_TYPES,
|
|
4799
|
+
TOOL_CONSTANTS,
|
|
4800
|
+
ThinkTool,
|
|
4801
|
+
TodoListTool,
|
|
4802
|
+
ToolExecutionError,
|
|
4803
|
+
Toolcall,
|
|
4804
|
+
UserToolStore,
|
|
4805
|
+
WORKSPACE_DIR,
|
|
4806
|
+
WriteUIDesignDocument,
|
|
4807
|
+
ensureDir,
|
|
4808
|
+
fileExists,
|
|
4809
|
+
getCacheDir,
|
|
4810
|
+
getConfigDir,
|
|
4811
|
+
getConfigPath2 as getConfigPath,
|
|
4812
|
+
getFileExtension,
|
|
4813
|
+
getLogsDir,
|
|
4814
|
+
getSettings,
|
|
4815
|
+
getSkillsDir,
|
|
4816
|
+
getTempFilePath,
|
|
4817
|
+
getWorkspaceDir,
|
|
4818
|
+
getWorkspacePath,
|
|
4819
|
+
initializeSettings,
|
|
4820
|
+
isInWorkspace,
|
|
4821
|
+
loadModelConfig,
|
|
4822
|
+
logger_default as logger,
|
|
4823
|
+
normalizePath,
|
|
4824
|
+
registerAgentAsTool,
|
|
4825
|
+
settings,
|
|
4826
|
+
tools,
|
|
4827
|
+
updateSettings
|
|
4828
|
+
};
|
|
4829
|
+
//# sourceMappingURL=index.js.map
|