evoltagent 1.1.3 → 1.1.4
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 +1541 -228
- package/dist/index.js +2483 -442
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -279,9 +279,96 @@ function escapeRegExp(string) {
|
|
|
279
279
|
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
280
280
|
}
|
|
281
281
|
|
|
282
|
+
// src/utils/deprecated.ts
|
|
283
|
+
function deprecated(options = {}) {
|
|
284
|
+
return function(target, propertyKey, descriptor) {
|
|
285
|
+
const original = descriptor.value;
|
|
286
|
+
descriptor.value = function(...args) {
|
|
287
|
+
const parts = [`${propertyKey} is deprecated`];
|
|
288
|
+
if (options.version) {
|
|
289
|
+
parts.push(`will be removed in ${options.version}`);
|
|
290
|
+
}
|
|
291
|
+
if (options.replacement) {
|
|
292
|
+
parts.push(`use ${options.replacement} instead`);
|
|
293
|
+
}
|
|
294
|
+
console.warn(parts.join(", "));
|
|
295
|
+
return original.apply(this, args);
|
|
296
|
+
};
|
|
297
|
+
return descriptor;
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// src/utils/readImage.ts
|
|
302
|
+
import * as fs from "fs";
|
|
303
|
+
import * as path from "path";
|
|
304
|
+
var MIME_TYPES = {
|
|
305
|
+
".jpg": "image/jpeg",
|
|
306
|
+
".jpeg": "image/jpeg",
|
|
307
|
+
".png": "image/png",
|
|
308
|
+
".gif": "image/gif",
|
|
309
|
+
".webp": "image/webp"
|
|
310
|
+
};
|
|
311
|
+
var SUPPORTED_FORMATS = /* @__PURE__ */ new Set([".jpg", ".jpeg", ".png", ".gif", ".webp"]);
|
|
312
|
+
function getMimeType(pathOrUrl) {
|
|
313
|
+
const cleanPath = pathOrUrl.split("?")[0];
|
|
314
|
+
const ext = path.extname(cleanPath).toLowerCase();
|
|
315
|
+
return MIME_TYPES[ext] || "image/jpeg";
|
|
316
|
+
}
|
|
317
|
+
function isSupportedImageFile(imagePath) {
|
|
318
|
+
const cleanPath = imagePath.split("?")[0];
|
|
319
|
+
const ext = path.extname(cleanPath).toLowerCase();
|
|
320
|
+
return SUPPORTED_FORMATS.has(ext);
|
|
321
|
+
}
|
|
322
|
+
async function fetchUrl(url) {
|
|
323
|
+
const response = await fetch(url, {
|
|
324
|
+
signal: AbortSignal.timeout(3e4)
|
|
325
|
+
});
|
|
326
|
+
if (!response.ok) {
|
|
327
|
+
throw new Error(`Failed to fetch image from ${url}: ${response.statusText}`);
|
|
328
|
+
}
|
|
329
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
330
|
+
return Buffer.from(arrayBuffer);
|
|
331
|
+
}
|
|
332
|
+
function readImage(imagePath) {
|
|
333
|
+
const mimeType = getMimeType(imagePath);
|
|
334
|
+
let content;
|
|
335
|
+
if (imagePath.startsWith("http://") || imagePath.startsWith("https://")) {
|
|
336
|
+
throw new Error("Synchronous URL fetching is not supported. Use areadImage instead.");
|
|
337
|
+
} else {
|
|
338
|
+
if (!fs.existsSync(imagePath)) {
|
|
339
|
+
throw new Error(`Image file not found: ${imagePath}`);
|
|
340
|
+
}
|
|
341
|
+
content = fs.readFileSync(imagePath);
|
|
342
|
+
}
|
|
343
|
+
const b64Content = content.toString("base64");
|
|
344
|
+
const dataUrl = `data:${mimeType};base64,${b64Content}`;
|
|
345
|
+
return {
|
|
346
|
+
type: "image_url",
|
|
347
|
+
image_url: { url: dataUrl }
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
async function areadImage(imagePath) {
|
|
351
|
+
const mimeType = getMimeType(imagePath);
|
|
352
|
+
let content;
|
|
353
|
+
if (imagePath.startsWith("http://") || imagePath.startsWith("https://")) {
|
|
354
|
+
content = await fetchUrl(imagePath);
|
|
355
|
+
} else {
|
|
356
|
+
if (!fs.existsSync(imagePath)) {
|
|
357
|
+
throw new Error(`Image file not found: ${imagePath}`);
|
|
358
|
+
}
|
|
359
|
+
content = await fs.promises.readFile(imagePath);
|
|
360
|
+
}
|
|
361
|
+
const b64Content = content.toString("base64");
|
|
362
|
+
const dataUrl = `data:${mimeType};base64,${b64Content}`;
|
|
363
|
+
return {
|
|
364
|
+
type: "image_url",
|
|
365
|
+
image_url: { url: dataUrl }
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
|
|
282
369
|
// src/utils/logger.ts
|
|
283
370
|
import * as winston from "winston";
|
|
284
|
-
import * as
|
|
371
|
+
import * as fs2 from "fs";
|
|
285
372
|
import * as dotenv from "dotenv";
|
|
286
373
|
dotenv.config();
|
|
287
374
|
var getDisableLog = () => {
|
|
@@ -474,7 +561,7 @@ var streamLogger = {
|
|
|
474
561
|
if (!getDisableLog()) {
|
|
475
562
|
process.stdout.write(message);
|
|
476
563
|
if (process.env.LOG_OUTPUT) {
|
|
477
|
-
|
|
564
|
+
fs2.appendFileSync(process.env.LOG_OUTPUT, message);
|
|
478
565
|
}
|
|
479
566
|
}
|
|
480
567
|
},
|
|
@@ -485,7 +572,7 @@ var streamLogger = {
|
|
|
485
572
|
if (!getDisableLog()) {
|
|
486
573
|
process.stderr.write(message);
|
|
487
574
|
if (process.env.LOG_OUTPUT) {
|
|
488
|
-
|
|
575
|
+
fs2.appendFileSync(process.env.LOG_OUTPUT, message);
|
|
489
576
|
}
|
|
490
577
|
}
|
|
491
578
|
},
|
|
@@ -496,7 +583,7 @@ var streamLogger = {
|
|
|
496
583
|
if (!getDisableLog()) {
|
|
497
584
|
process.stdout.write(message + "\n");
|
|
498
585
|
if (process.env.LOG_OUTPUT) {
|
|
499
|
-
|
|
586
|
+
fs2.appendFileSync(process.env.LOG_OUTPUT, message + "\n");
|
|
500
587
|
}
|
|
501
588
|
}
|
|
502
589
|
},
|
|
@@ -504,7 +591,7 @@ var streamLogger = {
|
|
|
504
591
|
if (!getDisableLog()) {
|
|
505
592
|
process.stdout.write(message);
|
|
506
593
|
if (process.env.LOG_OUTPUT) {
|
|
507
|
-
|
|
594
|
+
fs2.appendFileSync(process.env.LOG_OUTPUT, message);
|
|
508
595
|
}
|
|
509
596
|
}
|
|
510
597
|
}
|
|
@@ -671,18 +758,18 @@ function createMcpConnection(config2) {
|
|
|
671
758
|
}
|
|
672
759
|
|
|
673
760
|
// src/configs/configLoader.ts
|
|
674
|
-
import * as
|
|
675
|
-
import * as
|
|
761
|
+
import * as fs3 from "fs";
|
|
762
|
+
import * as path2 from "path";
|
|
676
763
|
import * as yaml from "yaml";
|
|
677
764
|
function getConfigPath() {
|
|
678
|
-
return
|
|
765
|
+
return path2.join(process.env.HOME || "~", ".evolt", "config.yaml");
|
|
679
766
|
}
|
|
680
767
|
function loadModelConfig(modelName) {
|
|
681
768
|
const configPath = process.env.EVOLT_CONFIG_PATH || getConfigPath();
|
|
682
769
|
let configData = {};
|
|
683
770
|
try {
|
|
684
|
-
if (
|
|
685
|
-
const fileContent =
|
|
771
|
+
if (fs3.existsSync(configPath)) {
|
|
772
|
+
const fileContent = fs3.readFileSync(configPath, "utf8");
|
|
686
773
|
configData = yaml.parse(fileContent);
|
|
687
774
|
} else {
|
|
688
775
|
logger.warn(`Config file not found at ${configPath}, using defaults`);
|
|
@@ -808,62 +895,62 @@ var LOG_LEVELS = {
|
|
|
808
895
|
};
|
|
809
896
|
|
|
810
897
|
// src/configs/paths.ts
|
|
811
|
-
import * as
|
|
812
|
-
import * as
|
|
898
|
+
import * as path3 from "path";
|
|
899
|
+
import * as fs4 from "fs";
|
|
813
900
|
function getWorkspaceDir() {
|
|
814
|
-
return process.env.EVOLT_WORKSPACE ||
|
|
901
|
+
return process.env.EVOLT_WORKSPACE || path3.join(process.cwd(), "workspace");
|
|
815
902
|
}
|
|
816
903
|
function getConfigDir() {
|
|
817
|
-
return process.env.EVOLT_CONFIG_DIR ||
|
|
904
|
+
return process.env.EVOLT_CONFIG_DIR || path3.join(process.cwd(), "config");
|
|
818
905
|
}
|
|
819
906
|
function getLogsDir() {
|
|
820
|
-
return process.env.EVOLT_LOGS_DIR ||
|
|
907
|
+
return process.env.EVOLT_LOGS_DIR || path3.join(getWorkspaceDir(), "logs");
|
|
821
908
|
}
|
|
822
909
|
function getCacheDir() {
|
|
823
|
-
return process.env.EVOLT_CACHE_DIR ||
|
|
910
|
+
return process.env.EVOLT_CACHE_DIR || path3.join(getWorkspaceDir(), "cache");
|
|
824
911
|
}
|
|
825
912
|
function getSkillsDir() {
|
|
826
|
-
return process.env.EVOLT_SKILLS_DIR ||
|
|
913
|
+
return process.env.EVOLT_SKILLS_DIR || path3.join(getWorkspaceDir(), "skills");
|
|
827
914
|
}
|
|
828
915
|
function ensureDir(dirPath) {
|
|
829
|
-
if (!
|
|
830
|
-
|
|
916
|
+
if (!fs4.existsSync(dirPath)) {
|
|
917
|
+
fs4.mkdirSync(dirPath, { recursive: true });
|
|
831
918
|
}
|
|
832
919
|
}
|
|
833
920
|
function getWorkspacePath(relativePath) {
|
|
834
921
|
const workspaceDir = getWorkspaceDir();
|
|
835
922
|
ensureDir(workspaceDir);
|
|
836
|
-
return
|
|
923
|
+
return path3.join(workspaceDir, relativePath);
|
|
837
924
|
}
|
|
838
925
|
function getConfigPath2(relativePath) {
|
|
839
926
|
const configDir = getConfigDir();
|
|
840
927
|
ensureDir(configDir);
|
|
841
|
-
return
|
|
928
|
+
return path3.join(configDir, relativePath);
|
|
842
929
|
}
|
|
843
930
|
function isInWorkspace(filePath) {
|
|
844
931
|
const workspaceDir = getWorkspaceDir();
|
|
845
|
-
const absolutePath =
|
|
846
|
-
return absolutePath.startsWith(
|
|
932
|
+
const absolutePath = path3.resolve(filePath);
|
|
933
|
+
return absolutePath.startsWith(path3.resolve(workspaceDir));
|
|
847
934
|
}
|
|
848
935
|
function normalizePath(filePath) {
|
|
849
|
-
return
|
|
936
|
+
return path3.normalize(filePath).replace(/\\/g, "/");
|
|
850
937
|
}
|
|
851
938
|
function getFileExtension(filePath) {
|
|
852
|
-
return
|
|
939
|
+
return path3.extname(filePath).toLowerCase();
|
|
853
940
|
}
|
|
854
941
|
function fileExists(filePath) {
|
|
855
942
|
try {
|
|
856
|
-
return
|
|
943
|
+
return fs4.existsSync(filePath);
|
|
857
944
|
} catch {
|
|
858
945
|
return false;
|
|
859
946
|
}
|
|
860
947
|
}
|
|
861
948
|
function getTempFilePath(prefix = "evolt") {
|
|
862
|
-
const tempDir =
|
|
949
|
+
const tempDir = path3.join(getCacheDir(), "temp");
|
|
863
950
|
ensureDir(tempDir);
|
|
864
951
|
const timestamp = Date.now();
|
|
865
952
|
const random = Math.random().toString(36).substring(2, 8);
|
|
866
|
-
return
|
|
953
|
+
return path3.join(tempDir, `${prefix}_${timestamp}_${random}.tmp`);
|
|
867
954
|
}
|
|
868
955
|
var WORKSPACE_DIR = getWorkspaceDir();
|
|
869
956
|
var SKILLS_DIR = getSkillsDir();
|
|
@@ -1388,8 +1475,8 @@ var CommandLineTool = class {
|
|
|
1388
1475
|
try {
|
|
1389
1476
|
const workDir = cwd || process.cwd();
|
|
1390
1477
|
try {
|
|
1391
|
-
const
|
|
1392
|
-
if (!
|
|
1478
|
+
const fs14 = await import("fs");
|
|
1479
|
+
if (!fs14.existsSync(workDir)) {
|
|
1393
1480
|
return `Working directory does not exist: ${workDir}`;
|
|
1394
1481
|
}
|
|
1395
1482
|
} catch (error) {
|
|
@@ -1409,9 +1496,9 @@ var CommandLineTool = class {
|
|
|
1409
1496
|
const processId = manager.registerBackgroundProcess(childProcess, command, workDir);
|
|
1410
1497
|
logger.debug(`Register background process: Command: ${command}
|
|
1411
1498
|
Process ID: ${processId}`);
|
|
1412
|
-
return new Promise((
|
|
1499
|
+
return new Promise((resolve5) => {
|
|
1413
1500
|
const timeout = setTimeout(async () => {
|
|
1414
|
-
|
|
1501
|
+
resolve5(
|
|
1415
1502
|
`COMMAND: ${command}
|
|
1416
1503
|
PROCESS ID: ${processId}
|
|
1417
1504
|
STATUS: The process may still be executing (waited 5 seconds, not completed)
|
|
@@ -1440,11 +1527,11 @@ ${stdoutText}`);
|
|
|
1440
1527
|
${stderrText}`);
|
|
1441
1528
|
}
|
|
1442
1529
|
resultParts.push(`EXIT CODE: ${code}`);
|
|
1443
|
-
|
|
1530
|
+
resolve5(resultParts.join("\n\n"));
|
|
1444
1531
|
});
|
|
1445
1532
|
childProcess.on("error", (error) => {
|
|
1446
1533
|
clearTimeout(timeout);
|
|
1447
|
-
|
|
1534
|
+
resolve5(`COMMAND: ${command}
|
|
1448
1535
|
ERROR: ${error.message}`);
|
|
1449
1536
|
});
|
|
1450
1537
|
});
|
|
@@ -1543,22 +1630,22 @@ CommandLineTool = __decorateClass([
|
|
|
1543
1630
|
], CommandLineTool);
|
|
1544
1631
|
|
|
1545
1632
|
// src/tools/fileTool.ts
|
|
1546
|
-
import * as
|
|
1633
|
+
import * as fs5 from "fs/promises";
|
|
1547
1634
|
import * as pathModule from "path";
|
|
1548
1635
|
var FileEditor = class {
|
|
1549
|
-
async read(
|
|
1636
|
+
async read(path10, lineRange = "all") {
|
|
1550
1637
|
try {
|
|
1551
|
-
await
|
|
1638
|
+
await fs5.access(path10);
|
|
1552
1639
|
} catch {
|
|
1553
|
-
throw new ToolExecutionError(`File does not exist: ${
|
|
1640
|
+
throw new ToolExecutionError(`File does not exist: ${path10}`);
|
|
1554
1641
|
}
|
|
1555
1642
|
try {
|
|
1556
1643
|
let content;
|
|
1557
1644
|
try {
|
|
1558
|
-
content = await
|
|
1645
|
+
content = await fs5.readFile(path10, "utf-8");
|
|
1559
1646
|
} catch (error) {
|
|
1560
1647
|
if (error.code === "ENCODING_NOT_SUPPORTED" || error.message.includes("encoding")) {
|
|
1561
|
-
const buffer = await
|
|
1648
|
+
const buffer = await fs5.readFile(path10);
|
|
1562
1649
|
content = buffer.toString("latin1");
|
|
1563
1650
|
} else {
|
|
1564
1651
|
throw error;
|
|
@@ -1577,11 +1664,11 @@ var FileEditor = class {
|
|
|
1577
1664
|
throw new ToolExecutionError(`Line range is invalid: ${lineRange}`);
|
|
1578
1665
|
}
|
|
1579
1666
|
const selectedLines = lines.slice(start, end);
|
|
1580
|
-
return `Lines ${start + 1} to ${end} of file ${
|
|
1667
|
+
return `Lines ${start + 1} to ${end} of file ${path10}:
|
|
1581
1668
|
` + selectedLines.join("\n");
|
|
1582
1669
|
} catch (error) {
|
|
1583
1670
|
throw new ToolExecutionError(
|
|
1584
|
-
`Invalid line range format when reading file ${
|
|
1671
|
+
`Invalid line range format when reading file ${path10}: ${lineRange}, error: ${error.message}`
|
|
1585
1672
|
);
|
|
1586
1673
|
}
|
|
1587
1674
|
} else {
|
|
@@ -1593,11 +1680,11 @@ var FileEditor = class {
|
|
|
1593
1680
|
if (lineNum >= lines.length) {
|
|
1594
1681
|
throw new ToolExecutionError(`Line number ${lineNum + 1} exceeds file length ${lines.length}`);
|
|
1595
1682
|
}
|
|
1596
|
-
return `Line ${lineNum + 1} of file ${
|
|
1683
|
+
return `Line ${lineNum + 1} of file ${path10}:
|
|
1597
1684
|
` + lines[lineNum];
|
|
1598
1685
|
} catch (error) {
|
|
1599
1686
|
throw new ToolExecutionError(
|
|
1600
|
-
`Invalid line number format when reading file ${
|
|
1687
|
+
`Invalid line number format when reading file ${path10}: ${lineRange}, error: ${error.message}`
|
|
1601
1688
|
);
|
|
1602
1689
|
}
|
|
1603
1690
|
}
|
|
@@ -1605,30 +1692,30 @@ var FileEditor = class {
|
|
|
1605
1692
|
if (error instanceof ToolExecutionError) {
|
|
1606
1693
|
throw error;
|
|
1607
1694
|
}
|
|
1608
|
-
throw new ToolExecutionError(`Error reading file ${
|
|
1695
|
+
throw new ToolExecutionError(`Error reading file ${path10}: ${error.message}`);
|
|
1609
1696
|
}
|
|
1610
1697
|
}
|
|
1611
|
-
async write(
|
|
1698
|
+
async write(path10, content) {
|
|
1612
1699
|
try {
|
|
1613
|
-
const dirPath = pathModule.dirname(
|
|
1700
|
+
const dirPath = pathModule.dirname(path10);
|
|
1614
1701
|
if (dirPath) {
|
|
1615
|
-
await
|
|
1702
|
+
await fs5.mkdir(dirPath, { recursive: true });
|
|
1616
1703
|
}
|
|
1617
|
-
await
|
|
1704
|
+
await fs5.writeFile(path10, content, "utf-8");
|
|
1618
1705
|
const bytesWritten = Buffer.from(content, "utf-8").length;
|
|
1619
|
-
return `Successfully wrote content to file ${
|
|
1706
|
+
return `Successfully wrote content to file ${path10}, wrote ${bytesWritten} bytes`;
|
|
1620
1707
|
} catch (error) {
|
|
1621
1708
|
if (error.code === "EACCES") {
|
|
1622
|
-
throw new ToolExecutionError(`No write permission: ${
|
|
1709
|
+
throw new ToolExecutionError(`No write permission: ${path10}`);
|
|
1623
1710
|
}
|
|
1624
|
-
throw new ToolExecutionError(`Error writing to file ${
|
|
1711
|
+
throw new ToolExecutionError(`Error writing to file ${path10}: ${error.message}`);
|
|
1625
1712
|
}
|
|
1626
1713
|
}
|
|
1627
|
-
async find(
|
|
1714
|
+
async find(path10, pattern) {
|
|
1628
1715
|
try {
|
|
1629
|
-
await
|
|
1716
|
+
await fs5.access(path10);
|
|
1630
1717
|
} catch {
|
|
1631
|
-
throw new ToolExecutionError(`File does not exist: ${
|
|
1718
|
+
throw new ToolExecutionError(`File does not exist: ${path10}`);
|
|
1632
1719
|
}
|
|
1633
1720
|
let compiledPattern;
|
|
1634
1721
|
try {
|
|
@@ -1640,10 +1727,10 @@ var FileEditor = class {
|
|
|
1640
1727
|
try {
|
|
1641
1728
|
let content;
|
|
1642
1729
|
try {
|
|
1643
|
-
content = await
|
|
1730
|
+
content = await fs5.readFile(path10, "utf-8");
|
|
1644
1731
|
} catch (error) {
|
|
1645
1732
|
if (error.code === "ENCODING_NOT_SUPPORTED" || error.message.includes("encoding")) {
|
|
1646
|
-
const buffer = await
|
|
1733
|
+
const buffer = await fs5.readFile(path10);
|
|
1647
1734
|
content = buffer.toString("latin1");
|
|
1648
1735
|
} else {
|
|
1649
1736
|
throw error;
|
|
@@ -1665,23 +1752,23 @@ var FileEditor = class {
|
|
|
1665
1752
|
}
|
|
1666
1753
|
}
|
|
1667
1754
|
} catch (error) {
|
|
1668
|
-
throw new ToolExecutionError(`Error reading file ${
|
|
1755
|
+
throw new ToolExecutionError(`Error reading file ${path10}: ${error.message}`);
|
|
1669
1756
|
}
|
|
1670
1757
|
if (matches.length === 0) {
|
|
1671
|
-
return `No content matching pattern '${pattern}' found in file ${
|
|
1758
|
+
return `No content matching pattern '${pattern}' found in file ${path10}`;
|
|
1672
1759
|
}
|
|
1673
|
-
const resultLines = [`Found ${matches.length} matches in file ${
|
|
1760
|
+
const resultLines = [`Found ${matches.length} matches in file ${path10}:`];
|
|
1674
1761
|
for (const match of matches) {
|
|
1675
1762
|
resultLines.push(`Line ${match.lineNumber}: ${match.match} (position ${match.startPos}-${match.endPos})`);
|
|
1676
1763
|
resultLines.push(` Full line content: ${match.lineContent}`);
|
|
1677
1764
|
}
|
|
1678
1765
|
return resultLines.join("\n");
|
|
1679
1766
|
}
|
|
1680
|
-
async findAndReplace(
|
|
1767
|
+
async findAndReplace(path10, pattern, replacement) {
|
|
1681
1768
|
try {
|
|
1682
|
-
await
|
|
1769
|
+
await fs5.access(path10);
|
|
1683
1770
|
} catch {
|
|
1684
|
-
throw new ToolExecutionError(`File does not exist: ${
|
|
1771
|
+
throw new ToolExecutionError(`File does not exist: ${path10}`);
|
|
1685
1772
|
}
|
|
1686
1773
|
let compiledPattern;
|
|
1687
1774
|
try {
|
|
@@ -1692,17 +1779,17 @@ var FileEditor = class {
|
|
|
1692
1779
|
let content;
|
|
1693
1780
|
try {
|
|
1694
1781
|
try {
|
|
1695
|
-
content = await
|
|
1782
|
+
content = await fs5.readFile(path10, "utf-8");
|
|
1696
1783
|
} catch (error) {
|
|
1697
1784
|
if (error.code === "ENCODING_NOT_SUPPORTED" || error.message.includes("encoding")) {
|
|
1698
|
-
const buffer = await
|
|
1785
|
+
const buffer = await fs5.readFile(path10);
|
|
1699
1786
|
content = buffer.toString("latin1");
|
|
1700
1787
|
} else {
|
|
1701
1788
|
throw error;
|
|
1702
1789
|
}
|
|
1703
1790
|
}
|
|
1704
1791
|
} catch (error) {
|
|
1705
|
-
throw new ToolExecutionError(`Error reading file ${
|
|
1792
|
+
throw new ToolExecutionError(`Error reading file ${path10}: ${error.message}`);
|
|
1706
1793
|
}
|
|
1707
1794
|
const newContent = content.replace(compiledPattern, replacement);
|
|
1708
1795
|
const replacementCount = (content.match(compiledPattern) || []).length;
|
|
@@ -1712,30 +1799,30 @@ var FileEditor = class {
|
|
|
1712
1799
|
const newLines = newContent.split("\n");
|
|
1713
1800
|
modifiedLines = originalLines.filter((line, index) => line !== newLines[index]).length;
|
|
1714
1801
|
try {
|
|
1715
|
-
await
|
|
1802
|
+
await fs5.writeFile(path10, newContent, "utf-8");
|
|
1716
1803
|
} catch (error) {
|
|
1717
1804
|
if (error.code === "EACCES") {
|
|
1718
|
-
throw new ToolExecutionError(`No write permission: ${
|
|
1805
|
+
throw new ToolExecutionError(`No write permission: ${path10}`);
|
|
1719
1806
|
}
|
|
1720
|
-
throw new ToolExecutionError(`Error writing to file ${
|
|
1807
|
+
throw new ToolExecutionError(`Error writing to file ${path10}: ${error.message}`);
|
|
1721
1808
|
}
|
|
1722
1809
|
}
|
|
1723
|
-
return `In file ${
|
|
1810
|
+
return `In file ${path10}, found and replaced pattern '${pattern}' with '${replacement}', successfully replaced ${replacementCount} occurrences, modified ${modifiedLines} lines`;
|
|
1724
1811
|
}
|
|
1725
|
-
async insert(
|
|
1812
|
+
async insert(path10, content, line) {
|
|
1726
1813
|
try {
|
|
1727
|
-
await
|
|
1814
|
+
await fs5.access(path10);
|
|
1728
1815
|
} catch {
|
|
1729
|
-
throw new ToolExecutionError(`File does not exist: ${
|
|
1816
|
+
throw new ToolExecutionError(`File does not exist: ${path10}`);
|
|
1730
1817
|
}
|
|
1731
1818
|
let lines;
|
|
1732
1819
|
try {
|
|
1733
1820
|
let fileContent;
|
|
1734
1821
|
try {
|
|
1735
|
-
fileContent = await
|
|
1822
|
+
fileContent = await fs5.readFile(path10, "utf-8");
|
|
1736
1823
|
} catch (error) {
|
|
1737
1824
|
if (error.code === "ENCODING_NOT_SUPPORTED" || error.message.includes("encoding")) {
|
|
1738
|
-
const buffer = await
|
|
1825
|
+
const buffer = await fs5.readFile(path10);
|
|
1739
1826
|
fileContent = buffer.toString("latin1");
|
|
1740
1827
|
} else {
|
|
1741
1828
|
throw error;
|
|
@@ -1743,27 +1830,27 @@ var FileEditor = class {
|
|
|
1743
1830
|
}
|
|
1744
1831
|
lines = fileContent.split("\n");
|
|
1745
1832
|
} catch (error) {
|
|
1746
|
-
throw new ToolExecutionError(`Error reading file ${
|
|
1833
|
+
throw new ToolExecutionError(`Error reading file ${path10}: ${error.message}`);
|
|
1747
1834
|
}
|
|
1748
1835
|
let actionDesc;
|
|
1749
1836
|
if (line === void 0) {
|
|
1750
1837
|
lines.push(content);
|
|
1751
|
-
actionDesc = `Appended content to end of file ${
|
|
1838
|
+
actionDesc = `Appended content to end of file ${path10}`;
|
|
1752
1839
|
} else {
|
|
1753
1840
|
if (!(0 < line && line <= lines.length + 1)) {
|
|
1754
|
-
throw new ToolExecutionError(`Line number ${line} exceeds file ${
|
|
1841
|
+
throw new ToolExecutionError(`Line number ${line} exceeds file ${path10} bounds (1 to ${lines.length + 1})`);
|
|
1755
1842
|
}
|
|
1756
1843
|
lines.splice(line - 1, 0, content);
|
|
1757
|
-
actionDesc = `Inserted content at line ${line} of file ${
|
|
1844
|
+
actionDesc = `Inserted content at line ${line} of file ${path10}`;
|
|
1758
1845
|
}
|
|
1759
1846
|
try {
|
|
1760
|
-
await
|
|
1847
|
+
await fs5.writeFile(path10, lines.join("\n"), "utf-8");
|
|
1761
1848
|
return actionDesc;
|
|
1762
1849
|
} catch (error) {
|
|
1763
1850
|
if (error.code === "EACCES") {
|
|
1764
|
-
throw new ToolExecutionError(`No write permission: ${
|
|
1851
|
+
throw new ToolExecutionError(`No write permission: ${path10}`);
|
|
1765
1852
|
}
|
|
1766
|
-
throw new ToolExecutionError(`Error writing to file ${
|
|
1853
|
+
throw new ToolExecutionError(`Error writing to file ${path10}: ${error.message}`);
|
|
1767
1854
|
}
|
|
1768
1855
|
}
|
|
1769
1856
|
};
|
|
@@ -2031,8 +2118,8 @@ ReflectTool = __decorateClass([
|
|
|
2031
2118
|
], ReflectTool);
|
|
2032
2119
|
|
|
2033
2120
|
// src/tools/skills.ts
|
|
2034
|
-
import * as
|
|
2035
|
-
import * as
|
|
2121
|
+
import * as fs6 from "fs";
|
|
2122
|
+
import * as path4 from "path";
|
|
2036
2123
|
var SkillsTool = class {
|
|
2037
2124
|
/**
|
|
2038
2125
|
* Skills directory
|
|
@@ -2051,8 +2138,8 @@ var SkillsTool = class {
|
|
|
2051
2138
|
* str: The content of the skill description file
|
|
2052
2139
|
*/
|
|
2053
2140
|
async readSkillDescription(name) {
|
|
2054
|
-
const skillsPath =
|
|
2055
|
-
if (!
|
|
2141
|
+
const skillsPath = path4.join(this.skillsDir, name, "SKILL.md");
|
|
2142
|
+
if (!fs6.existsSync(skillsPath)) {
|
|
2056
2143
|
throw new ToolExecutionError(`Skill description file not found: ${skillsPath}`);
|
|
2057
2144
|
}
|
|
2058
2145
|
const fileEditor = new FileEditor();
|
|
@@ -2065,14 +2152,14 @@ var SkillsTool = class {
|
|
|
2065
2152
|
* List the skills in the skills root directory
|
|
2066
2153
|
*/
|
|
2067
2154
|
async listSkills() {
|
|
2068
|
-
if (!
|
|
2155
|
+
if (!fs6.existsSync(this.skillsDir)) {
|
|
2069
2156
|
throw new ToolExecutionError(`Skills path not found: ${this.skillsDir}`);
|
|
2070
2157
|
}
|
|
2071
2158
|
const skillsDict = {};
|
|
2072
|
-
const items =
|
|
2159
|
+
const items = fs6.readdirSync(this.skillsDir);
|
|
2073
2160
|
for (const skillName of items) {
|
|
2074
|
-
const skillPath =
|
|
2075
|
-
if (!
|
|
2161
|
+
const skillPath = path4.join(this.skillsDir, skillName, "SKILL.md");
|
|
2162
|
+
if (!fs6.existsSync(skillPath)) {
|
|
2076
2163
|
continue;
|
|
2077
2164
|
}
|
|
2078
2165
|
const fileEditor = new FileEditor();
|
|
@@ -2104,8 +2191,8 @@ SkillsTool = __decorateClass([
|
|
|
2104
2191
|
|
|
2105
2192
|
// src/tools/gitTool.ts
|
|
2106
2193
|
import { exec } from "child_process";
|
|
2107
|
-
import * as
|
|
2108
|
-
import * as
|
|
2194
|
+
import * as fs7 from "fs";
|
|
2195
|
+
import * as path5 from "path";
|
|
2109
2196
|
function quote(str) {
|
|
2110
2197
|
if (/^[a-zA-Z0-9_\-.\/]+$/.test(str)) {
|
|
2111
2198
|
return str;
|
|
@@ -2657,12 +2744,12 @@ var GitTool = class {
|
|
|
2657
2744
|
*/
|
|
2658
2745
|
async _executeGitCommand(command, cwd, timeout = 3e4, config2) {
|
|
2659
2746
|
const workDir = cwd || process.cwd();
|
|
2660
|
-
if (!
|
|
2747
|
+
if (!fs7.existsSync(workDir)) {
|
|
2661
2748
|
throw new ToolExecutionError(`\u5DE5\u4F5C\u76EE\u5F55\u4E0D\u5B58\u5728: ${workDir}`);
|
|
2662
2749
|
}
|
|
2663
|
-
const gitDir =
|
|
2750
|
+
const gitDir = path5.join(workDir, ".git");
|
|
2664
2751
|
const isInitOrClone = command.includes("clone") || command.includes("init");
|
|
2665
|
-
if (!
|
|
2752
|
+
if (!fs7.existsSync(gitDir) && !isInitOrClone) {
|
|
2666
2753
|
throw new ToolExecutionError(`\u5F53\u524D\u76EE\u5F55\u4E0D\u662F git \u4ED3\u5E93: ${workDir}`);
|
|
2667
2754
|
}
|
|
2668
2755
|
let gitCmd = "git";
|
|
@@ -2672,7 +2759,7 @@ var GitTool = class {
|
|
|
2672
2759
|
}
|
|
2673
2760
|
}
|
|
2674
2761
|
gitCmd += ` ${command}`;
|
|
2675
|
-
return new Promise((
|
|
2762
|
+
return new Promise((resolve5, reject) => {
|
|
2676
2763
|
exec(gitCmd, { cwd: workDir, timeout, encoding: "utf-8" }, (error, stdout, stderr) => {
|
|
2677
2764
|
const stdoutText = stdout ? stdout.trim() : "";
|
|
2678
2765
|
const stderrText = stderr ? stderr.trim() : "";
|
|
@@ -2685,7 +2772,7 @@ var GitTool = class {
|
|
|
2685
2772
|
reject(new ToolExecutionError(`Git \u547D\u4EE4\u6267\u884C\u5931\u8D25: ${errorMsg}`));
|
|
2686
2773
|
return;
|
|
2687
2774
|
}
|
|
2688
|
-
|
|
2775
|
+
resolve5(stdoutText || "\u547D\u4EE4\u6267\u884C\u6210\u529F\uFF0C\u65E0\u8F93\u51FA");
|
|
2689
2776
|
});
|
|
2690
2777
|
});
|
|
2691
2778
|
}
|
|
@@ -2863,98 +2950,782 @@ GitTool = __decorateClass([
|
|
|
2863
2950
|
tools(gitToolConfig)
|
|
2864
2951
|
], GitTool);
|
|
2865
2952
|
|
|
2866
|
-
// src/
|
|
2867
|
-
import
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2953
|
+
// src/schemas/message.ts
|
|
2954
|
+
import * as fs8 from "fs";
|
|
2955
|
+
var _Message = class _Message {
|
|
2956
|
+
role;
|
|
2957
|
+
content;
|
|
2958
|
+
images;
|
|
2959
|
+
// OpenAI vision API format image content
|
|
2960
|
+
imagesContent;
|
|
2961
|
+
// Message tag for wrapping content
|
|
2962
|
+
tag;
|
|
2963
|
+
// Function calling / OpenAI tool call message information
|
|
2964
|
+
isFunctionCall;
|
|
2965
|
+
toolCallId;
|
|
2966
|
+
toolName;
|
|
2967
|
+
// Legacy type field (kept for compatibility)
|
|
2968
|
+
type;
|
|
2969
|
+
constructor(role, content, images, type) {
|
|
2970
|
+
this.role = role;
|
|
2971
|
+
this.content = content;
|
|
2972
|
+
this.images = images;
|
|
2973
|
+
this.type = type;
|
|
2974
|
+
this.imagesContent = [];
|
|
2975
|
+
this.tag = "";
|
|
2976
|
+
this.isFunctionCall = false;
|
|
2977
|
+
this.toolCallId = "";
|
|
2978
|
+
this.toolName = "";
|
|
2979
|
+
}
|
|
2980
|
+
/**
|
|
2981
|
+
* Create message from user input
|
|
2982
|
+
*/
|
|
2983
|
+
static fromUserMsg(content, images) {
|
|
2984
|
+
return new _Message("user", content, images);
|
|
2985
|
+
}
|
|
2986
|
+
/**
|
|
2987
|
+
* Create message from assistant response
|
|
2988
|
+
*/
|
|
2989
|
+
static fromAssistantMsg(content) {
|
|
2990
|
+
return new _Message("assistant", content);
|
|
2991
|
+
}
|
|
2992
|
+
/**
|
|
2993
|
+
* Create system message
|
|
2994
|
+
*/
|
|
2995
|
+
static fromSystemMsg(content) {
|
|
2996
|
+
return new _Message("system", content);
|
|
2997
|
+
}
|
|
2998
|
+
/**
|
|
2999
|
+
* Check if message is truthy based on content
|
|
3000
|
+
*/
|
|
3001
|
+
isTruthy() {
|
|
3002
|
+
if (typeof this.content === "string") {
|
|
3003
|
+
return Boolean(this.content.trim());
|
|
2882
3004
|
}
|
|
2883
|
-
|
|
3005
|
+
return false;
|
|
2884
3006
|
}
|
|
2885
3007
|
/**
|
|
2886
|
-
*
|
|
3008
|
+
* Format the content with pre/post content and optional tag wrapping
|
|
2887
3009
|
*/
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
3010
|
+
_formatForContent(preContent = "", postContent = "") {
|
|
3011
|
+
if (this.tag) {
|
|
3012
|
+
return `${preContent}<${this.tag}>
|
|
3013
|
+
${this.content}
|
|
3014
|
+
</${this.tag}>${postContent}`.trim();
|
|
3015
|
+
}
|
|
3016
|
+
return `${preContent}${this.content}${postContent}`.trim();
|
|
3017
|
+
}
|
|
3018
|
+
/**
|
|
3019
|
+
* Ensure text content is not empty to avoid API errors
|
|
3020
|
+
*/
|
|
3021
|
+
static _ensureNonEmptyText(text) {
|
|
3022
|
+
return text.trim() ? text : " ";
|
|
3023
|
+
}
|
|
3024
|
+
/**
|
|
3025
|
+
* Format message for OpenAI API
|
|
3026
|
+
*
|
|
3027
|
+
* This is the recommended method replacing toChatMessage.
|
|
3028
|
+
*
|
|
3029
|
+
* @param preContent - Content to prepend
|
|
3030
|
+
* @param postContent - Content to append
|
|
3031
|
+
* @returns Formatted message object for API
|
|
3032
|
+
*/
|
|
3033
|
+
formatForApi(preContent = "", postContent = "") {
|
|
3034
|
+
const role = ["user", "assistant", "system", "tool"].includes(this.role) ? this.role : "user";
|
|
3035
|
+
const functionCallingMeta = { tool_call_id: this.toolCallId, name: this.toolName };
|
|
3036
|
+
if (!this.imagesContent || this.imagesContent.length === 0) {
|
|
3037
|
+
const msg2 = {
|
|
3038
|
+
role,
|
|
3039
|
+
content: this._formatForContent(preContent, postContent)
|
|
3040
|
+
};
|
|
3041
|
+
return this.isFunctionCall ? { ...msg2, ...functionCallingMeta } : msg2;
|
|
3042
|
+
}
|
|
3043
|
+
let formattedText = this._formatForContent(preContent, postContent);
|
|
3044
|
+
formattedText = _Message._ensureNonEmptyText(formattedText);
|
|
3045
|
+
const content = [{ type: "text", text: formattedText }];
|
|
3046
|
+
content.push(...this.imagesContent);
|
|
3047
|
+
const msg = { role, content };
|
|
3048
|
+
return this.isFunctionCall ? { ...msg, ...functionCallingMeta } : msg;
|
|
3049
|
+
}
|
|
3050
|
+
/**
|
|
3051
|
+
* Convert to plain object for API calls
|
|
3052
|
+
*/
|
|
3053
|
+
toObject() {
|
|
3054
|
+
const obj = {
|
|
3055
|
+
role: this.role,
|
|
3056
|
+
content: this.content
|
|
3057
|
+
};
|
|
3058
|
+
if (this.images) {
|
|
3059
|
+
obj.images = this.images;
|
|
3060
|
+
}
|
|
3061
|
+
if (this.type) {
|
|
3062
|
+
obj.type = this.type;
|
|
3063
|
+
}
|
|
3064
|
+
if (this.tag) {
|
|
3065
|
+
obj.tag = this.tag;
|
|
3066
|
+
}
|
|
3067
|
+
if (this.imagesContent && this.imagesContent.length > 0) {
|
|
3068
|
+
obj.images_content = this.imagesContent;
|
|
3069
|
+
}
|
|
3070
|
+
if (this.isFunctionCall) {
|
|
3071
|
+
obj.is_function_call = this.isFunctionCall;
|
|
3072
|
+
obj.tool_call_id = this.toolCallId;
|
|
3073
|
+
obj.tool_name = this.toolName;
|
|
3074
|
+
}
|
|
3075
|
+
return obj;
|
|
3076
|
+
}
|
|
3077
|
+
/**
|
|
3078
|
+
* Convert to dictionary representation
|
|
3079
|
+
*/
|
|
3080
|
+
toDict() {
|
|
3081
|
+
return {
|
|
3082
|
+
role: this.role,
|
|
3083
|
+
content: this.content,
|
|
3084
|
+
tag: this.tag || "",
|
|
3085
|
+
images_content: this.imagesContent || [],
|
|
3086
|
+
is_function_call: this.isFunctionCall || false,
|
|
3087
|
+
tool_call_id: this.toolCallId || "",
|
|
3088
|
+
tool_name: this.toolName || ""
|
|
3089
|
+
};
|
|
3090
|
+
}
|
|
3091
|
+
/**
|
|
3092
|
+
* Check if message contains images
|
|
3093
|
+
*/
|
|
3094
|
+
hasImages() {
|
|
3095
|
+
return !!this.images && (Array.isArray(this.images) ? this.images.length > 0 : true) || !!this.imagesContent && this.imagesContent.length > 0;
|
|
3096
|
+
}
|
|
3097
|
+
/**
|
|
3098
|
+
* Get message content length
|
|
3099
|
+
*/
|
|
3100
|
+
getContentLength() {
|
|
3101
|
+
return this.content.length;
|
|
3102
|
+
}
|
|
3103
|
+
/**
|
|
3104
|
+
* Convert to string representation
|
|
3105
|
+
*/
|
|
3106
|
+
toString() {
|
|
3107
|
+
const parts = [];
|
|
3108
|
+
parts.push(`role=${this.role}`);
|
|
3109
|
+
if (this.content) {
|
|
3110
|
+
const truncatedContent = this.content.length > 100 ? this.content.slice(0, 100) + "..." : this.content;
|
|
3111
|
+
parts.push(`content=${truncatedContent}`);
|
|
3112
|
+
}
|
|
3113
|
+
if (this.tag) {
|
|
3114
|
+
parts.push(`tag=${this.tag}`);
|
|
3115
|
+
}
|
|
3116
|
+
if (this.imagesContent && this.imagesContent.length > 0) {
|
|
3117
|
+
const imagePaths = [];
|
|
3118
|
+
for (const img of this.imagesContent) {
|
|
3119
|
+
if (img && img.image_url && img.image_url.url) {
|
|
3120
|
+
const url = img.image_url.url;
|
|
3121
|
+
if (!url.startsWith("data:")) {
|
|
3122
|
+
imagePaths.push(url);
|
|
3123
|
+
}
|
|
3124
|
+
}
|
|
3125
|
+
}
|
|
3126
|
+
if (imagePaths.length > 0) {
|
|
3127
|
+
parts.push(`images_paths=[${imagePaths.join(", ")}]`);
|
|
3128
|
+
}
|
|
3129
|
+
}
|
|
3130
|
+
return `Message(${parts.join(", ")})`;
|
|
3131
|
+
}
|
|
3132
|
+
/**
|
|
3133
|
+
* Merge two messages into one
|
|
3134
|
+
*
|
|
3135
|
+
* @param other - Message to merge with
|
|
3136
|
+
* @returns New merged Message
|
|
3137
|
+
*/
|
|
3138
|
+
merge(other) {
|
|
3139
|
+
if (this.role !== other.role) {
|
|
3140
|
+
throw new Error("Messages must have the same role to merge");
|
|
3141
|
+
}
|
|
3142
|
+
if (this.isFunctionCall !== other.isFunctionCall) {
|
|
3143
|
+
throw new Error("Messages must have the same isFunctionCall to merge");
|
|
3144
|
+
}
|
|
3145
|
+
if (this.toolCallId !== other.toolCallId) {
|
|
3146
|
+
throw new Error("Messages must have the same toolCallId to merge");
|
|
3147
|
+
}
|
|
3148
|
+
if (this.toolName !== other.toolName) {
|
|
3149
|
+
throw new Error("Messages must have the same toolName to merge");
|
|
3150
|
+
}
|
|
3151
|
+
let selfContent = this.content;
|
|
3152
|
+
let otherContent = other.content;
|
|
3153
|
+
let resultTag = this.tag;
|
|
3154
|
+
if (this.tag !== other.tag) {
|
|
3155
|
+
selfContent = this.tag ? `<${this.tag}>
|
|
3156
|
+
${this.content}
|
|
3157
|
+
</${this.tag}>` : this.content;
|
|
3158
|
+
otherContent = other.tag ? `<${other.tag}>
|
|
3159
|
+
${other.content}
|
|
3160
|
+
</${other.tag}>` : other.content;
|
|
3161
|
+
resultTag = "";
|
|
3162
|
+
}
|
|
3163
|
+
const merged = new _Message(this.role, `${selfContent}
|
|
3164
|
+
${otherContent}`);
|
|
3165
|
+
merged.tag = resultTag;
|
|
3166
|
+
merged.imagesContent = [...this.imagesContent || [], ...other.imagesContent || []];
|
|
3167
|
+
merged.isFunctionCall = this.isFunctionCall;
|
|
3168
|
+
merged.toolCallId = this.toolCallId;
|
|
3169
|
+
merged.toolName = this.toolName;
|
|
3170
|
+
return merged;
|
|
3171
|
+
}
|
|
3172
|
+
async toChatMessage() {
|
|
3173
|
+
const role = this.role;
|
|
3174
|
+
if (!this.hasImages()) {
|
|
3175
|
+
return { role, content: this.content };
|
|
3176
|
+
}
|
|
3177
|
+
const content = [{ type: "text", text: this.content }];
|
|
3178
|
+
if (this.images) {
|
|
3179
|
+
const imageArray = Array.isArray(this.images) ? this.images : [this.images];
|
|
3180
|
+
for (const img of imageArray) {
|
|
3181
|
+
let base64Data;
|
|
3182
|
+
if (img.startsWith("http://") || img.startsWith("https://")) {
|
|
3183
|
+
base64Data = await this.encodeHttpImage(img);
|
|
3184
|
+
} else if (fs8.existsSync(img)) {
|
|
3185
|
+
base64Data = await this.encodeLocalFile(img);
|
|
3186
|
+
} else {
|
|
3187
|
+
base64Data = img;
|
|
3188
|
+
}
|
|
3189
|
+
const mimeType = this.getMimeType(img);
|
|
3190
|
+
content.push({
|
|
3191
|
+
type: "image_url",
|
|
3192
|
+
image_url: { url: `data:${mimeType};base64,${base64Data}` }
|
|
2904
3193
|
});
|
|
2905
|
-
|
|
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: 60000,
|
|
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}`);
|
|
3194
|
+
}
|
|
2922
3195
|
}
|
|
3196
|
+
if (this.imagesContent && this.imagesContent.length > 0) {
|
|
3197
|
+
content.push(...this.imagesContent);
|
|
3198
|
+
}
|
|
3199
|
+
return { role, content };
|
|
2923
3200
|
}
|
|
2924
3201
|
/**
|
|
2925
|
-
*
|
|
3202
|
+
* Get MIME type from file path or URL
|
|
2926
3203
|
*/
|
|
2927
|
-
|
|
2928
|
-
const
|
|
2929
|
-
const
|
|
3204
|
+
getMimeType(path10) {
|
|
3205
|
+
const ext = path10.toLowerCase().split(".").pop();
|
|
3206
|
+
const mimeTypes = {
|
|
3207
|
+
png: "image/png",
|
|
3208
|
+
jpg: "image/jpeg",
|
|
3209
|
+
jpeg: "image/jpeg",
|
|
3210
|
+
gif: "image/gif",
|
|
3211
|
+
webp: "image/webp",
|
|
3212
|
+
bmp: "image/bmp"
|
|
3213
|
+
};
|
|
3214
|
+
return mimeTypes[ext || ""] || "image/jpeg";
|
|
3215
|
+
}
|
|
3216
|
+
/**
|
|
3217
|
+
* Encode local file to base64
|
|
3218
|
+
*/
|
|
3219
|
+
async encodeLocalFile(filePath) {
|
|
3220
|
+
const fileBuffer = await fs8.promises.readFile(filePath);
|
|
3221
|
+
return fileBuffer.toString("base64");
|
|
3222
|
+
}
|
|
3223
|
+
/**
|
|
3224
|
+
* Fetch and encode HTTP image to base64
|
|
3225
|
+
*/
|
|
3226
|
+
async encodeHttpImage(url) {
|
|
3227
|
+
const response = await fetch(url);
|
|
3228
|
+
if (!response.ok) {
|
|
3229
|
+
throw new Error(`Failed to fetch image from ${url}: ${response.statusText}`);
|
|
3230
|
+
}
|
|
3231
|
+
const buffer = await response.arrayBuffer();
|
|
3232
|
+
return Buffer.from(buffer).toString("base64");
|
|
3233
|
+
}
|
|
3234
|
+
};
|
|
3235
|
+
__decorateClass([
|
|
3236
|
+
deprecated({ version: "0.2.2", replacement: "formatForApi" })
|
|
3237
|
+
], _Message.prototype, "toChatMessage", 1);
|
|
3238
|
+
var Message = _Message;
|
|
3239
|
+
|
|
3240
|
+
// src/tools/imageTool.ts
|
|
3241
|
+
var ImageTool = class {
|
|
3242
|
+
/**
|
|
3243
|
+
* Read and analyze an image (JPEG, JPG, PNG, GIF, WebP) from image path or URL.
|
|
3244
|
+
*
|
|
3245
|
+
* Note: SVG format is not supported by the AI model.
|
|
3246
|
+
*
|
|
3247
|
+
* @param imagePath - Image path or URL
|
|
3248
|
+
* @param instruction - Instruction for analyzing the image
|
|
3249
|
+
* @returns Message containing image content in base64 format
|
|
3250
|
+
*/
|
|
3251
|
+
async readImage(imagePath, instruction = "") {
|
|
2930
3252
|
try {
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
3253
|
+
if (!isSupportedImageFile(imagePath)) {
|
|
3254
|
+
logger.warn(`Unsupported image format: ${imagePath}. Only JPEG, PNG, GIF, and WebP are supported.`);
|
|
3255
|
+
const errorMsg = new Message(
|
|
3256
|
+
"user",
|
|
3257
|
+
`Error: Unsupported image format '${imagePath}'. Only JPEG, PNG, GIF, and WebP are supported by the AI model.`
|
|
3258
|
+
);
|
|
3259
|
+
return errorMsg;
|
|
3260
|
+
}
|
|
3261
|
+
const imageContent = await areadImage(imagePath);
|
|
3262
|
+
let content;
|
|
3263
|
+
if (instruction) {
|
|
3264
|
+
content = `Please analyze the image from ${imagePath} according to the following instruction:
|
|
3265
|
+
|
|
3266
|
+
${instruction}
|
|
3267
|
+
|
|
3268
|
+
Please provide a detailed analysis of the image content based on this instruction.`;
|
|
3269
|
+
} else {
|
|
3270
|
+
content = `Please analyze and describe the image from ${imagePath} in detail. Include information about:
|
|
3271
|
+
- What is shown in the image
|
|
3272
|
+
- Key elements, objects, or features
|
|
3273
|
+
- Colors, composition, and visual characteristics
|
|
3274
|
+
- Any text or symbols present
|
|
3275
|
+
- Overall context and meaning`;
|
|
2940
3276
|
}
|
|
3277
|
+
const msg = new Message("user", content);
|
|
3278
|
+
msg.imagesContent = [imageContent];
|
|
3279
|
+
msg.tag = "image_content";
|
|
3280
|
+
return msg;
|
|
2941
3281
|
} catch (error) {
|
|
2942
|
-
|
|
2943
|
-
throw error;
|
|
3282
|
+
return new Message("user", `Error reading image ${imagePath}: ${error}`);
|
|
2944
3283
|
}
|
|
2945
3284
|
}
|
|
2946
3285
|
/**
|
|
2947
|
-
*
|
|
3286
|
+
* Read images (JPEG, JPG, PNG, GIF, WebP) from image paths or URLs.
|
|
3287
|
+
*
|
|
3288
|
+
* Note: SVG format is not supported by the AI model and will be automatically filtered out.
|
|
3289
|
+
*
|
|
3290
|
+
* @param imagePaths - List of image paths or URLs
|
|
3291
|
+
* @param instruction - Instruction for reading the images
|
|
3292
|
+
* @returns Message containing multiple image contents in base64 format
|
|
2948
3293
|
*/
|
|
2949
|
-
async
|
|
2950
|
-
if (!this.openaiClient) {
|
|
2951
|
-
throw new ModelError("OpenAI client not initialized");
|
|
2952
|
-
}
|
|
3294
|
+
async readImages(imagePaths, instruction = "") {
|
|
2953
3295
|
try {
|
|
2954
|
-
const
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
3296
|
+
const supportedPaths = imagePaths.filter((path10) => isSupportedImageFile(path10));
|
|
3297
|
+
const unsupportedPaths = imagePaths.filter((path10) => !isSupportedImageFile(path10));
|
|
3298
|
+
if (unsupportedPaths.length > 0) {
|
|
3299
|
+
logger.warn(
|
|
3300
|
+
`Skipping unsupported image formats (only JPEG/PNG/GIF/WebP are supported): ${unsupportedPaths.join(", ")}`
|
|
3301
|
+
);
|
|
3302
|
+
}
|
|
3303
|
+
if (supportedPaths.length === 0) {
|
|
3304
|
+
return new Message(
|
|
3305
|
+
"user",
|
|
3306
|
+
`No supported image formats found in the provided list. Only JPEG, PNG, GIF, and WebP are supported. Unsupported files: ${unsupportedPaths.join(", ")}`
|
|
3307
|
+
);
|
|
3308
|
+
}
|
|
3309
|
+
const imageContents = await Promise.all(
|
|
3310
|
+
supportedPaths.map((imagePath) => areadImage(imagePath))
|
|
3311
|
+
);
|
|
3312
|
+
const contentParts = [`Successfully loaded ${supportedPaths.length} image(s): ${supportedPaths.join(", ")}`];
|
|
3313
|
+
if (unsupportedPaths.length > 0) {
|
|
3314
|
+
contentParts.push(`Skipped ${unsupportedPaths.length} unsupported file(s): ${unsupportedPaths.join(", ")}`);
|
|
3315
|
+
}
|
|
3316
|
+
const msg = new Message("user", contentParts.join("\n"));
|
|
3317
|
+
msg.imagesContent = imageContents;
|
|
3318
|
+
return msg;
|
|
3319
|
+
} catch (error) {
|
|
3320
|
+
return new Message("user", `Error reading images ${imagePaths.join(", ")}: ${error}`);
|
|
3321
|
+
}
|
|
3322
|
+
}
|
|
3323
|
+
};
|
|
3324
|
+
ImageTool = __decorateClass([
|
|
3325
|
+
tools({
|
|
3326
|
+
readImage: {
|
|
3327
|
+
description: "Read and analyze an image (JPEG, JPG, PNG, GIF, WebP) from image path or URL. Note: SVG format is not supported by the AI model.",
|
|
3328
|
+
params: [
|
|
3329
|
+
{ name: "imagePath", type: "string", description: "Image path or URL" },
|
|
3330
|
+
{ name: "instruction", type: "string", description: "Instruction for analyzing the image", optional: true }
|
|
3331
|
+
],
|
|
3332
|
+
returns: { type: "Message", description: "Message containing image content in base64 format" }
|
|
3333
|
+
},
|
|
3334
|
+
readImages: {
|
|
3335
|
+
description: "Read images (JPEG, JPG, PNG, GIF, WebP) from image paths or URLs. Note: SVG format is not supported by the AI model and will be automatically filtered out.",
|
|
3336
|
+
params: [
|
|
3337
|
+
{ name: "imagePaths", type: "array", description: "List of image paths or URLs" },
|
|
3338
|
+
{ name: "instruction", type: "string", description: "Instruction for reading the images", optional: true }
|
|
3339
|
+
],
|
|
3340
|
+
returns: { type: "Message", description: "Message containing multiple image contents in base64 format" }
|
|
3341
|
+
}
|
|
3342
|
+
})
|
|
3343
|
+
], ImageTool);
|
|
3344
|
+
|
|
3345
|
+
// src/tools/patchTool.ts
|
|
3346
|
+
import * as fs9 from "fs";
|
|
3347
|
+
import * as path6 from "path";
|
|
3348
|
+
import { spawn as spawn2 } from "child_process";
|
|
3349
|
+
var PatchTool = class {
|
|
3350
|
+
/**
|
|
3351
|
+
* Write patch content to file
|
|
3352
|
+
*
|
|
3353
|
+
* @param patchPath - Path to the patch file
|
|
3354
|
+
* @param patchContent - Content of the patch in unified diff format
|
|
3355
|
+
* @returns Success message
|
|
3356
|
+
*/
|
|
3357
|
+
async writePatchFile(patchPath, patchContent) {
|
|
3358
|
+
let warningMsg = "";
|
|
3359
|
+
if (!patchPath.endsWith(".patch")) {
|
|
3360
|
+
patchPath = `${patchPath}.patch`;
|
|
3361
|
+
warningMsg = `Warning: Patch path does not end with '.patch', automatically appended '.patch' to ${patchPath}`;
|
|
3362
|
+
}
|
|
3363
|
+
const dirPath = path6.dirname(patchPath);
|
|
3364
|
+
if (dirPath) {
|
|
3365
|
+
await fs9.promises.mkdir(dirPath, { recursive: true });
|
|
3366
|
+
}
|
|
3367
|
+
patchContent = patchContent.trim();
|
|
3368
|
+
if (patchContent.startsWith("```")) {
|
|
3369
|
+
const newlineIndex = patchContent.indexOf("\n");
|
|
3370
|
+
patchContent = newlineIndex !== -1 ? patchContent.slice(newlineIndex + 1) : patchContent.slice(3);
|
|
3371
|
+
}
|
|
3372
|
+
if (patchContent.endsWith("```")) {
|
|
3373
|
+
patchContent = patchContent.slice(0, -3).trimEnd();
|
|
3374
|
+
}
|
|
3375
|
+
patchContent = patchContent.trim();
|
|
3376
|
+
if (patchContent && patchContent.split("\n")[0].startsWith("diff --git")) {
|
|
3377
|
+
patchContent = this._convertGitDiffToUnifiedDiff(patchContent);
|
|
3378
|
+
}
|
|
3379
|
+
this._validatePatchFormat(patchContent);
|
|
3380
|
+
if (fs9.existsSync(patchPath)) {
|
|
3381
|
+
const baseName = path6.basename(patchPath, ".patch");
|
|
3382
|
+
const oldPatchPath = path6.join(path6.dirname(patchPath), `${baseName}1.patch`);
|
|
3383
|
+
await fs9.promises.rename(patchPath, oldPatchPath);
|
|
3384
|
+
}
|
|
3385
|
+
await fs9.promises.writeFile(patchPath, patchContent, "utf-8");
|
|
3386
|
+
const bytesWritten = Buffer.from(patchContent, "utf-8").length;
|
|
3387
|
+
const resultMsg = `Successfully wrote patch to ${patchPath}, wrote ${bytesWritten} bytes`;
|
|
3388
|
+
return warningMsg ? `${resultMsg}
|
|
3389
|
+
${warningMsg}` : resultMsg;
|
|
3390
|
+
}
|
|
3391
|
+
/**
|
|
3392
|
+
* Apply a patch to a file
|
|
3393
|
+
*
|
|
3394
|
+
* @param patchPath - Path to the patch file
|
|
3395
|
+
* @param patchContent - Optional patch content to write before applying
|
|
3396
|
+
* @returns Success message
|
|
3397
|
+
*/
|
|
3398
|
+
async applyPatch(patchPath, patchContent) {
|
|
3399
|
+
let warningMsg = "";
|
|
3400
|
+
if (!patchPath.endsWith(".patch")) {
|
|
3401
|
+
patchPath = `${patchPath}.patch`;
|
|
3402
|
+
warningMsg = `Warning: Patch path does not end with '.patch', automatically appended '.patch' to ${patchPath}`;
|
|
3403
|
+
}
|
|
3404
|
+
const patchFileAbs = path6.resolve(patchPath);
|
|
3405
|
+
if (patchContent === void 0) {
|
|
3406
|
+
if (!fs9.existsSync(patchFileAbs)) {
|
|
3407
|
+
throw new Error(`Patch file does not exist: ${patchPath}`);
|
|
3408
|
+
}
|
|
3409
|
+
patchContent = await fs9.promises.readFile(patchFileAbs, "utf-8");
|
|
3410
|
+
}
|
|
3411
|
+
const workDir = this._determineWorkDir(patchFileAbs, patchContent);
|
|
3412
|
+
let writeMsg;
|
|
3413
|
+
if (patchContent !== void 0) {
|
|
3414
|
+
writeMsg = await this.writePatchFile(patchPath, patchContent);
|
|
3415
|
+
}
|
|
3416
|
+
const applyMsg = await this._applyPatch(patchFileAbs, workDir);
|
|
3417
|
+
if (writeMsg) {
|
|
3418
|
+
return `${writeMsg}. ${applyMsg}.`;
|
|
3419
|
+
}
|
|
3420
|
+
return `Successfully applied existing patch file ${patchPath}. ${applyMsg}.
|
|
3421
|
+
${warningMsg}`;
|
|
3422
|
+
}
|
|
3423
|
+
/**
|
|
3424
|
+
* Validate patch format
|
|
3425
|
+
*/
|
|
3426
|
+
_validatePatchFormat(patchContent) {
|
|
3427
|
+
if (!patchContent.trim()) {
|
|
3428
|
+
throw new Error("Patch content is empty.");
|
|
3429
|
+
}
|
|
3430
|
+
const lines = patchContent.split("\n");
|
|
3431
|
+
const hasMinusLine = lines.slice(0, 50).some((line) => line.startsWith("--- "));
|
|
3432
|
+
const hasPlusLine = lines.slice(0, 50).some((line) => line.startsWith("+++ "));
|
|
3433
|
+
const hasHunkHeader = lines.slice(0, 100).some((line) => /^@@ -\d+,\d+ \+\d+,\d+ @@/.test(line));
|
|
3434
|
+
if (!(hasMinusLine && hasPlusLine)) {
|
|
3435
|
+
throw new Error(
|
|
3436
|
+
"Patch does not appear to be in standard unified diff format. Expected lines starting with '--- ' and '+++ '."
|
|
3437
|
+
);
|
|
3438
|
+
}
|
|
3439
|
+
if (!hasHunkHeader) {
|
|
3440
|
+
throw new Error(
|
|
3441
|
+
"Patch does not contain valid hunk headers (lines starting with '@@'). Example: '@@ -1,2 +1,2 @@'"
|
|
3442
|
+
);
|
|
3443
|
+
}
|
|
3444
|
+
}
|
|
3445
|
+
/**
|
|
3446
|
+
* Convert git diff format to unified diff format
|
|
3447
|
+
*/
|
|
3448
|
+
_convertGitDiffToUnifiedDiff(patchContent) {
|
|
3449
|
+
const lines = patchContent.split("\n");
|
|
3450
|
+
const convertedLines = [];
|
|
3451
|
+
const skipPrefixes = [
|
|
3452
|
+
"diff --git",
|
|
3453
|
+
"index ",
|
|
3454
|
+
"new file mode",
|
|
3455
|
+
"old file mode",
|
|
3456
|
+
"deleted file mode",
|
|
3457
|
+
"rename from",
|
|
3458
|
+
"rename to",
|
|
3459
|
+
"similarity index",
|
|
3460
|
+
"copy from",
|
|
3461
|
+
"copy to"
|
|
3462
|
+
];
|
|
3463
|
+
for (const line of lines) {
|
|
3464
|
+
if (skipPrefixes.some((prefix) => line.startsWith(prefix))) {
|
|
3465
|
+
continue;
|
|
3466
|
+
}
|
|
3467
|
+
if (line.startsWith("--- a/")) {
|
|
3468
|
+
const filePath = line.slice(6).split(" ")[0];
|
|
3469
|
+
convertedLines.push(`--- ${filePath}`);
|
|
3470
|
+
} else if (line.startsWith("+++ b/")) {
|
|
3471
|
+
const filePath = line.slice(6).split(" ")[0];
|
|
3472
|
+
convertedLines.push(`+++ ${filePath}`);
|
|
3473
|
+
} else {
|
|
3474
|
+
convertedLines.push(line);
|
|
3475
|
+
}
|
|
3476
|
+
}
|
|
3477
|
+
return convertedLines.join("\n");
|
|
3478
|
+
}
|
|
3479
|
+
/**
|
|
3480
|
+
* Extract target file path from patch content
|
|
3481
|
+
*/
|
|
3482
|
+
_extractTargetFile(patchContent) {
|
|
3483
|
+
for (const line of patchContent.split("\n")) {
|
|
3484
|
+
if (line.startsWith("+++ b/")) {
|
|
3485
|
+
return line.slice(6).split(" ")[0];
|
|
3486
|
+
} else if (line.startsWith("+++ ") && !line.startsWith("+++ /dev/null")) {
|
|
3487
|
+
return line.slice(4).split(" ")[0];
|
|
3488
|
+
}
|
|
3489
|
+
}
|
|
3490
|
+
return null;
|
|
3491
|
+
}
|
|
3492
|
+
/**
|
|
3493
|
+
* Determine working directory for applying patch
|
|
3494
|
+
*/
|
|
3495
|
+
_determineWorkDir(patchFileAbs, patchContent) {
|
|
3496
|
+
const patchDir = path6.dirname(patchFileAbs) || process.cwd();
|
|
3497
|
+
const defaultWorkDir = path6.resolve(patchDir);
|
|
3498
|
+
const targetFile = this._extractTargetFile(patchContent);
|
|
3499
|
+
if (!targetFile || targetFile === "/dev/null") {
|
|
3500
|
+
return defaultWorkDir;
|
|
3501
|
+
}
|
|
3502
|
+
const searchDirs = [process.cwd(), patchDir];
|
|
3503
|
+
for (const searchDir of searchDirs) {
|
|
3504
|
+
const candidate = path6.join(searchDir, targetFile);
|
|
3505
|
+
if (fs9.existsSync(candidate)) {
|
|
3506
|
+
return path6.dirname(candidate);
|
|
3507
|
+
}
|
|
3508
|
+
}
|
|
3509
|
+
return defaultWorkDir;
|
|
3510
|
+
}
|
|
3511
|
+
/**
|
|
3512
|
+
* Apply patch using patch command or manual fallback
|
|
3513
|
+
*/
|
|
3514
|
+
async _applyPatch(patchFileAbs, workDir) {
|
|
3515
|
+
try {
|
|
3516
|
+
const result = await this._executePatchCommand(patchFileAbs, workDir);
|
|
3517
|
+
if (result.exitCode === 0) {
|
|
3518
|
+
return `Successfully applied patch using patch command${result.stdout ? ": " + result.stdout : ""}`;
|
|
3519
|
+
}
|
|
3520
|
+
} catch (error) {
|
|
3521
|
+
logger.debug(`Patch command failed: ${error}`);
|
|
3522
|
+
}
|
|
3523
|
+
const manualResult = await this._applyPatchManually(patchFileAbs, workDir);
|
|
3524
|
+
if (manualResult) {
|
|
3525
|
+
return manualResult;
|
|
3526
|
+
}
|
|
3527
|
+
throw new Error(
|
|
3528
|
+
`Failed to apply patch ${patchFileAbs}. Working directory: ${workDir}. Both patch command and manual application failed. Please check: 1) patch format is correct, 2) target file exists, 3) line numbers match the file content.`
|
|
3529
|
+
);
|
|
3530
|
+
}
|
|
3531
|
+
/**
|
|
3532
|
+
* Execute patch command
|
|
3533
|
+
*/
|
|
3534
|
+
_executePatchCommand(patchFileAbs, workDir) {
|
|
3535
|
+
return new Promise((resolve5, reject) => {
|
|
3536
|
+
const patchProcess = spawn2("patch", ["--batch", "-p1", "-i", patchFileAbs], {
|
|
3537
|
+
cwd: workDir
|
|
3538
|
+
});
|
|
3539
|
+
let stdout = "";
|
|
3540
|
+
let stderr = "";
|
|
3541
|
+
patchProcess.stdout.on("data", (data) => {
|
|
3542
|
+
stdout += data.toString();
|
|
3543
|
+
});
|
|
3544
|
+
patchProcess.stderr.on("data", (data) => {
|
|
3545
|
+
stderr += data.toString();
|
|
3546
|
+
});
|
|
3547
|
+
patchProcess.on("close", (code) => {
|
|
3548
|
+
resolve5({
|
|
3549
|
+
exitCode: code || 0,
|
|
3550
|
+
stdout: stdout.trim(),
|
|
3551
|
+
stderr: stderr.trim()
|
|
3552
|
+
});
|
|
3553
|
+
});
|
|
3554
|
+
patchProcess.on("error", (error) => {
|
|
3555
|
+
reject(error);
|
|
3556
|
+
});
|
|
3557
|
+
});
|
|
3558
|
+
}
|
|
3559
|
+
/**
|
|
3560
|
+
* Manually apply patch by parsing unified diff format
|
|
3561
|
+
*/
|
|
3562
|
+
async _applyPatchManually(patchFileAbs, workDir) {
|
|
3563
|
+
try {
|
|
3564
|
+
const patchContent = await fs9.promises.readFile(patchFileAbs, "utf-8");
|
|
3565
|
+
const targetFile = this._extractTargetFile(patchContent);
|
|
3566
|
+
if (!targetFile || targetFile === "/dev/null") {
|
|
3567
|
+
return null;
|
|
3568
|
+
}
|
|
3569
|
+
const targetPath = path6.join(workDir, targetFile);
|
|
3570
|
+
const targetDir = path6.dirname(targetPath);
|
|
3571
|
+
if (!fs9.existsSync(targetDir)) {
|
|
3572
|
+
await fs9.promises.mkdir(targetDir, { recursive: true });
|
|
3573
|
+
}
|
|
3574
|
+
let fileLines = [];
|
|
3575
|
+
if (fs9.existsSync(targetPath)) {
|
|
3576
|
+
const content = await fs9.promises.readFile(targetPath, "utf-8");
|
|
3577
|
+
fileLines = content.split("\n");
|
|
3578
|
+
}
|
|
3579
|
+
const resultLines = [...fileLines];
|
|
3580
|
+
const lines = patchContent.split("\n");
|
|
3581
|
+
let inHunk = false;
|
|
3582
|
+
let oldLineNum = 0;
|
|
3583
|
+
let newLineNum = 0;
|
|
3584
|
+
const skipPrefixes = ["diff --git", "new file mode", "index ", "---", "+++"];
|
|
3585
|
+
for (const line of lines) {
|
|
3586
|
+
const hunkMatch = line.match(/^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/);
|
|
3587
|
+
if (hunkMatch) {
|
|
3588
|
+
inHunk = true;
|
|
3589
|
+
oldLineNum = parseInt(hunkMatch[1], 10) - 1;
|
|
3590
|
+
newLineNum = parseInt(hunkMatch[3], 10) - 1;
|
|
3591
|
+
continue;
|
|
3592
|
+
}
|
|
3593
|
+
if (skipPrefixes.some((prefix) => line.startsWith(prefix))) {
|
|
3594
|
+
continue;
|
|
3595
|
+
}
|
|
3596
|
+
if (!inHunk) {
|
|
3597
|
+
continue;
|
|
3598
|
+
}
|
|
3599
|
+
if (line.startsWith(" ")) {
|
|
3600
|
+
if (oldLineNum < resultLines.length) {
|
|
3601
|
+
oldLineNum++;
|
|
3602
|
+
}
|
|
3603
|
+
newLineNum++;
|
|
3604
|
+
} else if (line.startsWith("-")) {
|
|
3605
|
+
if (oldLineNum < resultLines.length) {
|
|
3606
|
+
resultLines.splice(oldLineNum, 1);
|
|
3607
|
+
}
|
|
3608
|
+
} else if (line.startsWith("+")) {
|
|
3609
|
+
resultLines.splice(newLineNum, 0, line.slice(1));
|
|
3610
|
+
newLineNum++;
|
|
3611
|
+
}
|
|
3612
|
+
}
|
|
3613
|
+
await fs9.promises.writeFile(targetPath, resultLines.join("\n"), "utf-8");
|
|
3614
|
+
return "Successfully applied patch manually by parsing and applying changes";
|
|
3615
|
+
} catch (error) {
|
|
3616
|
+
logger.debug(`Manual patch application failed: ${error}`);
|
|
3617
|
+
return null;
|
|
3618
|
+
}
|
|
3619
|
+
}
|
|
3620
|
+
};
|
|
3621
|
+
PatchTool = __decorateClass([
|
|
3622
|
+
tools({
|
|
3623
|
+
writePatchFile: {
|
|
3624
|
+
description: "Write patch content to file. IMPORTANT: Always read the target file first before generating a patch. Use correct line numbers based on actual file content. Never use @@ -0,0 format unless file is truly empty.",
|
|
3625
|
+
params: [
|
|
3626
|
+
{ name: "patchPath", type: "string", description: "Path to the patch file. Will automatically append .patch suffix if not present." },
|
|
3627
|
+
{ name: "patchContent", type: "string", description: "Content of the patch in unified diff format. Must start with --- a/file_path and include +++ b/file_path lines." }
|
|
3628
|
+
],
|
|
3629
|
+
returns: { type: "string", description: "Success message with bytes written" }
|
|
3630
|
+
},
|
|
3631
|
+
applyPatch: {
|
|
3632
|
+
description: "Apply a patch to a file. The patch should be generated using writePatchFile first. IMPORTANT: Ensure patch line numbers match the actual file content to avoid duplicate content.",
|
|
3633
|
+
params: [
|
|
3634
|
+
{ name: "patchPath", type: "string", description: "Path to the patch file. Will automatically append .patch suffix if not present." },
|
|
3635
|
+
{ name: "patchContent", type: "string", description: "Optional patch content to write before applying", optional: true }
|
|
3636
|
+
],
|
|
3637
|
+
returns: { type: "string", description: "Success message describing the applied patch" }
|
|
3638
|
+
}
|
|
3639
|
+
})
|
|
3640
|
+
], PatchTool);
|
|
3641
|
+
|
|
3642
|
+
// src/core/agent.ts
|
|
3643
|
+
import { v4 as uuidv4 } from "uuid";
|
|
3644
|
+
import * as path7 from "path";
|
|
3645
|
+
import * as fs10 from "fs";
|
|
3646
|
+
|
|
3647
|
+
// src/core/model.ts
|
|
3648
|
+
import OpenAI from "openai";
|
|
3649
|
+
import { GoogleGenerativeAI } from "@google/generative-ai";
|
|
3650
|
+
var Model = class {
|
|
3651
|
+
modelName;
|
|
3652
|
+
config;
|
|
3653
|
+
openaiClient;
|
|
3654
|
+
anthropicClient;
|
|
3655
|
+
geminiClient;
|
|
3656
|
+
constructor(model) {
|
|
3657
|
+
if (typeof model === "string" || model === void 0) {
|
|
3658
|
+
this.modelName = model || "deepseek";
|
|
3659
|
+
this.config = loadModelConfig(this.modelName);
|
|
3660
|
+
} else {
|
|
3661
|
+
this.config = model;
|
|
3662
|
+
this.modelName = model.model || "deepseek";
|
|
3663
|
+
}
|
|
3664
|
+
this._initializeClient();
|
|
3665
|
+
}
|
|
3666
|
+
/**
|
|
3667
|
+
* Initialize the appropriate SDK client based on provider
|
|
3668
|
+
*/
|
|
3669
|
+
_initializeClient() {
|
|
3670
|
+
const provider = this.config.provider.toLowerCase();
|
|
3671
|
+
switch (provider) {
|
|
3672
|
+
case "openai":
|
|
3673
|
+
case "anthropic":
|
|
3674
|
+
this.openaiClient = new OpenAI({
|
|
3675
|
+
apiKey: this.config.apiKey || process.env.OPENAI_API_KEY,
|
|
3676
|
+
baseURL: this.config.baseUrl,
|
|
3677
|
+
timeout: 6e4
|
|
3678
|
+
});
|
|
3679
|
+
break;
|
|
3680
|
+
case "deepseek":
|
|
3681
|
+
this.openaiClient = new OpenAI({
|
|
3682
|
+
apiKey: this.config.apiKey || process.env.DEEPSEEK_API_KEY,
|
|
3683
|
+
baseURL: this.config.baseUrl || "https://api.deepseek.com",
|
|
3684
|
+
timeout: 6e4
|
|
3685
|
+
});
|
|
3686
|
+
break;
|
|
3687
|
+
case "gemini":
|
|
3688
|
+
const apiKey = this.config.apiKey || process.env.GEMINI_API_KEY || "";
|
|
3689
|
+
this.geminiClient = new GoogleGenerativeAI(apiKey);
|
|
3690
|
+
break;
|
|
3691
|
+
default:
|
|
3692
|
+
throw new ModelError(`Unsupported provider: ${provider}`);
|
|
3693
|
+
}
|
|
3694
|
+
}
|
|
3695
|
+
/**
|
|
3696
|
+
* Asynchronous chat completion
|
|
3697
|
+
*/
|
|
3698
|
+
async achat(messages, tools2, stream = true) {
|
|
3699
|
+
const provider = this.config.provider.toLowerCase();
|
|
3700
|
+
const useStream = stream !== void 0 ? stream : !!this.config.stream;
|
|
3701
|
+
try {
|
|
3702
|
+
switch (provider) {
|
|
3703
|
+
case "openai":
|
|
3704
|
+
case "deepseek":
|
|
3705
|
+
case "anthropic":
|
|
3706
|
+
return await this._callOpenAICompatible(messages, tools2, useStream);
|
|
3707
|
+
case "gemini":
|
|
3708
|
+
return await this._callGemini(messages, tools2, useStream);
|
|
3709
|
+
default:
|
|
3710
|
+
throw new ModelError(`Unsupported provider: ${provider}`);
|
|
3711
|
+
}
|
|
3712
|
+
} catch (error) {
|
|
3713
|
+
logger.error(`Model call failed: ${error}`);
|
|
3714
|
+
throw error;
|
|
3715
|
+
}
|
|
3716
|
+
}
|
|
3717
|
+
/**
|
|
3718
|
+
* Call OpenAI-compatible API (OpenAI, DeepSeek, etc.)
|
|
3719
|
+
*/
|
|
3720
|
+
async _callOpenAICompatible(messages, tools2, stream = false) {
|
|
3721
|
+
if (!this.openaiClient) {
|
|
3722
|
+
throw new ModelError("OpenAI client not initialized");
|
|
3723
|
+
}
|
|
3724
|
+
try {
|
|
3725
|
+
const params = {
|
|
3726
|
+
model: this.config.model || "gpt-3.5-turbo",
|
|
3727
|
+
messages,
|
|
3728
|
+
temperature: this.config.temperature || 0.7,
|
|
2958
3729
|
max_tokens: this.config.maxOutputTokens || 1024,
|
|
2959
3730
|
top_p: this.config.topP || 0.9,
|
|
2960
3731
|
stream
|
|
@@ -3345,200 +4116,69 @@ Arguments: `);
|
|
|
3345
4116
|
}
|
|
3346
4117
|
};
|
|
3347
4118
|
|
|
3348
|
-
// src/
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
constructor(
|
|
3356
|
-
this.
|
|
3357
|
-
this.
|
|
3358
|
-
this.
|
|
3359
|
-
|
|
4119
|
+
// src/runtime/memory/messageHistory.ts
|
|
4120
|
+
var MessageHistory = class {
|
|
4121
|
+
messages = [];
|
|
4122
|
+
systemPrompt;
|
|
4123
|
+
model;
|
|
4124
|
+
contextWindowTokens;
|
|
4125
|
+
currentTokens = 0;
|
|
4126
|
+
constructor(model, system, contextWindowTokens) {
|
|
4127
|
+
this.model = model;
|
|
4128
|
+
this.systemPrompt = system;
|
|
4129
|
+
this.contextWindowTokens = contextWindowTokens;
|
|
4130
|
+
if (system) {
|
|
4131
|
+
const systemMessage = new Message("system", system);
|
|
4132
|
+
this.messages.push(systemMessage);
|
|
4133
|
+
this.currentTokens += this.estimateTokens(system);
|
|
4134
|
+
}
|
|
4135
|
+
}
|
|
4136
|
+
addMessage(arg1, arg2) {
|
|
4137
|
+
let message;
|
|
4138
|
+
if (arg1 instanceof Message) {
|
|
4139
|
+
message = arg1;
|
|
4140
|
+
} else if (typeof arg1 === "string") {
|
|
4141
|
+
message = new Message(arg1, arg2);
|
|
4142
|
+
} else if (typeof arg1 === "object" && arg1 !== null) {
|
|
4143
|
+
this.messages.push(arg1);
|
|
4144
|
+
const contentStr = arg1.content || JSON.stringify(arg1);
|
|
4145
|
+
this.currentTokens += this.estimateTokens(contentStr);
|
|
4146
|
+
return;
|
|
4147
|
+
} else {
|
|
4148
|
+
throw new Error(`Invalid message format: ${arg1}`);
|
|
4149
|
+
}
|
|
4150
|
+
this.messages.push(message);
|
|
4151
|
+
this.currentTokens += this.estimateTokens(message.content);
|
|
3360
4152
|
}
|
|
3361
4153
|
/**
|
|
3362
|
-
*
|
|
4154
|
+
* Get all messages
|
|
3363
4155
|
*/
|
|
3364
|
-
|
|
3365
|
-
return
|
|
4156
|
+
getMessages() {
|
|
4157
|
+
return [...this.messages];
|
|
3366
4158
|
}
|
|
3367
4159
|
/**
|
|
3368
|
-
*
|
|
4160
|
+
* Get messages formatted for API calls
|
|
4161
|
+
* Async version to handle base64 image encoding
|
|
3369
4162
|
*/
|
|
3370
|
-
|
|
3371
|
-
|
|
4163
|
+
async formatForApi() {
|
|
4164
|
+
const results = [];
|
|
4165
|
+
for (const msg of this.messages) {
|
|
4166
|
+
if (msg instanceof Message) {
|
|
4167
|
+
if (msg.hasImages()) {
|
|
4168
|
+
results.push(await msg.toChatMessage());
|
|
4169
|
+
} else {
|
|
4170
|
+
results.push(msg.toObject());
|
|
4171
|
+
}
|
|
4172
|
+
} else if (typeof msg.toObject === "function") {
|
|
4173
|
+
results.push(msg.toObject());
|
|
4174
|
+
} else {
|
|
4175
|
+
results.push(msg);
|
|
4176
|
+
}
|
|
4177
|
+
}
|
|
4178
|
+
return results;
|
|
3372
4179
|
}
|
|
3373
4180
|
/**
|
|
3374
|
-
*
|
|
3375
|
-
*/
|
|
3376
|
-
static fromSystemMsg(content) {
|
|
3377
|
-
return new _Message("system", content);
|
|
3378
|
-
}
|
|
3379
|
-
/**
|
|
3380
|
-
* Convert to plain object for API calls
|
|
3381
|
-
*/
|
|
3382
|
-
toObject() {
|
|
3383
|
-
const obj = {
|
|
3384
|
-
role: this.role,
|
|
3385
|
-
content: this.content
|
|
3386
|
-
};
|
|
3387
|
-
if (this.images) {
|
|
3388
|
-
obj.images = this.images;
|
|
3389
|
-
}
|
|
3390
|
-
if (this.type) {
|
|
3391
|
-
obj.type = this.type;
|
|
3392
|
-
}
|
|
3393
|
-
return obj;
|
|
3394
|
-
}
|
|
3395
|
-
/**
|
|
3396
|
-
* Check if message contains images
|
|
3397
|
-
*/
|
|
3398
|
-
hasImages() {
|
|
3399
|
-
return !!this.images && (Array.isArray(this.images) ? this.images.length > 0 : true);
|
|
3400
|
-
}
|
|
3401
|
-
/**
|
|
3402
|
-
* Get message content length
|
|
3403
|
-
*/
|
|
3404
|
-
getContentLength() {
|
|
3405
|
-
return this.content.length;
|
|
3406
|
-
}
|
|
3407
|
-
/**
|
|
3408
|
-
* Convert to string representation
|
|
3409
|
-
*/
|
|
3410
|
-
toString() {
|
|
3411
|
-
return `${this.role}: ${this.content}`;
|
|
3412
|
-
}
|
|
3413
|
-
/**
|
|
3414
|
-
* Convert to OpenAI Vision API format with base64-encoded images
|
|
3415
|
-
* This method handles:
|
|
3416
|
-
* - HTTP/HTTPS URLs: Downloads and converts to base64
|
|
3417
|
-
* - Local file paths: Reads file and converts to base64
|
|
3418
|
-
* - Already base64 strings: Passes through
|
|
3419
|
-
*/
|
|
3420
|
-
async toChatMessage() {
|
|
3421
|
-
const role = this.role;
|
|
3422
|
-
if (!this.hasImages()) {
|
|
3423
|
-
return { role, content: this.content };
|
|
3424
|
-
}
|
|
3425
|
-
const content = [{ type: "text", text: this.content }];
|
|
3426
|
-
const imageArray = Array.isArray(this.images) ? this.images : [this.images];
|
|
3427
|
-
for (const img of imageArray) {
|
|
3428
|
-
let base64Data;
|
|
3429
|
-
if (img.startsWith("http://") || img.startsWith("https://")) {
|
|
3430
|
-
base64Data = await this.encodeHttpImage(img);
|
|
3431
|
-
} else if (fs7.existsSync(img)) {
|
|
3432
|
-
base64Data = await this.encodeLocalFile(img);
|
|
3433
|
-
} else {
|
|
3434
|
-
base64Data = img;
|
|
3435
|
-
}
|
|
3436
|
-
const mimeType = this.getMimeType(img);
|
|
3437
|
-
content.push({
|
|
3438
|
-
type: "image_url",
|
|
3439
|
-
image_url: { url: `data:${mimeType};base64,${base64Data}` }
|
|
3440
|
-
});
|
|
3441
|
-
}
|
|
3442
|
-
return { role, content };
|
|
3443
|
-
}
|
|
3444
|
-
/**
|
|
3445
|
-
* Get MIME type from file path or URL
|
|
3446
|
-
*/
|
|
3447
|
-
getMimeType(path5) {
|
|
3448
|
-
const ext = path5.toLowerCase().split(".").pop();
|
|
3449
|
-
const mimeTypes = {
|
|
3450
|
-
png: "image/png",
|
|
3451
|
-
jpg: "image/jpeg",
|
|
3452
|
-
jpeg: "image/jpeg",
|
|
3453
|
-
gif: "image/gif",
|
|
3454
|
-
webp: "image/webp",
|
|
3455
|
-
bmp: "image/bmp"
|
|
3456
|
-
};
|
|
3457
|
-
return mimeTypes[ext || ""] || "image/jpeg";
|
|
3458
|
-
}
|
|
3459
|
-
/**
|
|
3460
|
-
* Encode local file to base64
|
|
3461
|
-
*/
|
|
3462
|
-
async encodeLocalFile(filePath) {
|
|
3463
|
-
const fileBuffer = await fs7.promises.readFile(filePath);
|
|
3464
|
-
return fileBuffer.toString("base64");
|
|
3465
|
-
}
|
|
3466
|
-
/**
|
|
3467
|
-
* Fetch and encode HTTP image to base64
|
|
3468
|
-
*/
|
|
3469
|
-
async encodeHttpImage(url) {
|
|
3470
|
-
const response = await fetch(url);
|
|
3471
|
-
if (!response.ok) {
|
|
3472
|
-
throw new Error(`Failed to fetch image from ${url}: ${response.statusText}`);
|
|
3473
|
-
}
|
|
3474
|
-
const buffer = await response.arrayBuffer();
|
|
3475
|
-
return Buffer.from(buffer).toString("base64");
|
|
3476
|
-
}
|
|
3477
|
-
};
|
|
3478
|
-
|
|
3479
|
-
// src/memory/messageHistory.ts
|
|
3480
|
-
var MessageHistory = class {
|
|
3481
|
-
messages = [];
|
|
3482
|
-
systemPrompt;
|
|
3483
|
-
model;
|
|
3484
|
-
contextWindowTokens;
|
|
3485
|
-
currentTokens = 0;
|
|
3486
|
-
constructor(model, system, contextWindowTokens) {
|
|
3487
|
-
this.model = model;
|
|
3488
|
-
this.systemPrompt = system;
|
|
3489
|
-
this.contextWindowTokens = contextWindowTokens;
|
|
3490
|
-
if (system) {
|
|
3491
|
-
const systemMessage = new Message("system", system);
|
|
3492
|
-
this.messages.push(systemMessage);
|
|
3493
|
-
this.currentTokens += this.estimateTokens(system);
|
|
3494
|
-
}
|
|
3495
|
-
}
|
|
3496
|
-
addMessage(arg1, arg2) {
|
|
3497
|
-
let message;
|
|
3498
|
-
if (arg1 instanceof Message) {
|
|
3499
|
-
message = arg1;
|
|
3500
|
-
} else if (typeof arg1 === "string") {
|
|
3501
|
-
message = new Message(arg1, arg2);
|
|
3502
|
-
} else if (typeof arg1 === "object" && arg1 !== null) {
|
|
3503
|
-
this.messages.push(arg1);
|
|
3504
|
-
const contentStr = arg1.content || JSON.stringify(arg1);
|
|
3505
|
-
this.currentTokens += this.estimateTokens(contentStr);
|
|
3506
|
-
return;
|
|
3507
|
-
} else {
|
|
3508
|
-
throw new Error(`Invalid message format: ${arg1}`);
|
|
3509
|
-
}
|
|
3510
|
-
this.messages.push(message);
|
|
3511
|
-
this.currentTokens += this.estimateTokens(message.content);
|
|
3512
|
-
}
|
|
3513
|
-
/**
|
|
3514
|
-
* Get all messages
|
|
3515
|
-
*/
|
|
3516
|
-
getMessages() {
|
|
3517
|
-
return [...this.messages];
|
|
3518
|
-
}
|
|
3519
|
-
/**
|
|
3520
|
-
* Get messages formatted for API calls
|
|
3521
|
-
* Async version to handle base64 image encoding
|
|
3522
|
-
*/
|
|
3523
|
-
async formatForApi() {
|
|
3524
|
-
const results = [];
|
|
3525
|
-
for (const msg of this.messages) {
|
|
3526
|
-
if (msg instanceof Message) {
|
|
3527
|
-
if (msg.hasImages()) {
|
|
3528
|
-
results.push(await msg.toChatMessage());
|
|
3529
|
-
} else {
|
|
3530
|
-
results.push(msg.toObject());
|
|
3531
|
-
}
|
|
3532
|
-
} else if (typeof msg.toObject === "function") {
|
|
3533
|
-
results.push(msg.toObject());
|
|
3534
|
-
} else {
|
|
3535
|
-
results.push(msg);
|
|
3536
|
-
}
|
|
3537
|
-
}
|
|
3538
|
-
return results;
|
|
3539
|
-
}
|
|
3540
|
-
/**
|
|
3541
|
-
* Update system prompt
|
|
4181
|
+
* Update system prompt
|
|
3542
4182
|
*/
|
|
3543
4183
|
updateSystem(system) {
|
|
3544
4184
|
this.messages = this.messages.filter((msg) => {
|
|
@@ -3795,7 +4435,7 @@ var ToolcallManager = class {
|
|
|
3795
4435
|
return;
|
|
3796
4436
|
}
|
|
3797
4437
|
while (this.semaphore.available <= 0) {
|
|
3798
|
-
await new Promise((
|
|
4438
|
+
await new Promise((resolve5) => setTimeout(resolve5, 100));
|
|
3799
4439
|
}
|
|
3800
4440
|
this.semaphore.available--;
|
|
3801
4441
|
try {
|
|
@@ -3885,7 +4525,7 @@ background process observation: ${backgroundResult.content}`;
|
|
|
3885
4525
|
async observe(waitAll = false, timeout = null) {
|
|
3886
4526
|
await this.execute();
|
|
3887
4527
|
if (this.tasks.size > 0 && !waitAll) {
|
|
3888
|
-
await new Promise((
|
|
4528
|
+
await new Promise((resolve5) => setTimeout(resolve5, 1e3));
|
|
3889
4529
|
}
|
|
3890
4530
|
if (waitAll && this.tasks.size > 0) {
|
|
3891
4531
|
if (timeout) {
|
|
@@ -3959,7 +4599,7 @@ background process observation: ${backgroundResult.content}`;
|
|
|
3959
4599
|
*/
|
|
3960
4600
|
async monitorProcess(processId, process2) {
|
|
3961
4601
|
try {
|
|
3962
|
-
await new Promise((
|
|
4602
|
+
await new Promise((resolve5, reject) => {
|
|
3963
4603
|
process2.on("exit", (code) => {
|
|
3964
4604
|
if (this.backgroundProcesses.has(processId)) {
|
|
3965
4605
|
const command = this.backgroundProcesses.get(processId).command;
|
|
@@ -3969,7 +4609,7 @@ background process observation: ${backgroundResult.content}`;
|
|
|
3969
4609
|
if (this.monitorTasks.has(processId)) {
|
|
3970
4610
|
this.monitorTasks.delete(processId);
|
|
3971
4611
|
}
|
|
3972
|
-
|
|
4612
|
+
resolve5();
|
|
3973
4613
|
});
|
|
3974
4614
|
process2.on("error", (error) => {
|
|
3975
4615
|
reject(error);
|
|
@@ -4021,17 +4661,17 @@ PID: ${info.pid}`;
|
|
|
4021
4661
|
logger.info(`\u7EC8\u6B62\u540E\u53F0\u8FDB\u7A0B: ${command} (PID: ${process2.pid})`);
|
|
4022
4662
|
}
|
|
4023
4663
|
try {
|
|
4024
|
-
await new Promise((
|
|
4664
|
+
await new Promise((resolve5) => {
|
|
4025
4665
|
const timeoutId = setTimeout(() => {
|
|
4026
4666
|
if (!force) {
|
|
4027
4667
|
process2.kill("SIGKILL");
|
|
4028
4668
|
logger.warn(`\u540E\u53F0\u8FDB\u7A0B\u672A\u54CD\u5E94\u7EC8\u6B62\u4FE1\u53F7\uFF0C\u5DF2\u5F3A\u5236\u6740\u6B7B: ${command}`);
|
|
4029
4669
|
}
|
|
4030
|
-
|
|
4670
|
+
resolve5();
|
|
4031
4671
|
}, 5e3);
|
|
4032
4672
|
process2.on("exit", () => {
|
|
4033
4673
|
clearTimeout(timeoutId);
|
|
4034
|
-
|
|
4674
|
+
resolve5();
|
|
4035
4675
|
});
|
|
4036
4676
|
});
|
|
4037
4677
|
} catch (error) {
|
|
@@ -4115,69 +4755,48 @@ PID: ${info.pid}`;
|
|
|
4115
4755
|
};
|
|
4116
4756
|
|
|
4117
4757
|
// src/prompts/tools.ts
|
|
4118
|
-
var
|
|
4758
|
+
var REFLECT_OUTPUT_FORMAT_PROMPT = `**Reflection Prompt: Output Format Error**
|
|
4759
|
+
Your output does not follow the required XML format:
|
|
4760
|
+
<OutputFormat>
|
|
4761
|
+
<ToolName.method_name><arg1_name>value1</arg1_name><arg2_name>value2</arg2_name></ToolName.method_name>
|
|
4762
|
+
</OutputFormat>
|
|
4119
4763
|
|
|
4120
|
-
|
|
4121
|
-
|
|
4764
|
+
Your erroneous output:
|
|
4765
|
+
{output_text}
|
|
4122
4766
|
|
|
4123
|
-
|
|
4124
|
-
<Generative Tasks Purpose>Create Something New</Generative Tasks Purpose>
|
|
4767
|
+
Please analyze the format discrepancies and re-output, ensuring:
|
|
4125
4768
|
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
</Generative Tasks Definition>
|
|
4769
|
+
- Use the correct XML tag structure
|
|
4770
|
+
- All tags are properly closed
|
|
4771
|
+
- Parameter values are placed within the corresponding tags
|
|
4130
4772
|
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
You may interleave tool calls with reasoning or explanations as needed:
|
|
4135
|
-
|
|
4136
|
-
[Your reasoning or explanation here...]
|
|
4137
|
-
<ToolName.method_name><args_name1>args_value1</args_name1><args_name2>args_value2</args_name2>...</ToolName.method_name>
|
|
4138
|
-
|
|
4139
|
-
[Additional thoughts or context...]
|
|
4140
|
-
<ToolName2.method_name2><args_name1>args_value1</args_name1><args_name2>args_value2</args_name2>...</ToolName2.method_name2>
|
|
4141
|
-
...
|
|
4142
|
-
</Generative Tasks Output Format>
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
### Analytical Tasks Output Format
|
|
4146
|
-
<Analytical Tasks Purpose>Understand, Diagnose & Evaluate Existing Information</Analytical Tasks Purpose>
|
|
4147
|
-
|
|
4148
|
-
<Analytical Tasks Definition>
|
|
4149
|
-
Analytical tasks focus on understanding, decomposing, diagnosing, and evaluating existing information.
|
|
4150
|
-
They include requirement analysis, code and system comprehension, quality evaluation, debugging and root-cause analysis, and data or behavioral analysis.
|
|
4151
|
-
Such tasks produce structured insights and decisions that guide subsequent generative or operational steps.
|
|
4152
|
-
</Analytical Tasks Definition>
|
|
4153
|
-
|
|
4154
|
-
<Analytical Tasks Output Format>
|
|
4155
|
-
You should fellow the format below, replace the <ToolName.method_name> and <args_name> with the actual name.
|
|
4156
|
-
|
|
4157
|
-
Thoughts: ...
|
|
4158
|
-
ToolCall: <ToolName.method_name><args_name1>args_value1</args_name1><args_name2>args_value2</args_name2>...</ToolName.method_name>
|
|
4159
|
-
// stop ToolCall here, and wait for the Observation of the ToolCall.
|
|
4773
|
+
Re-output:
|
|
4774
|
+
`;
|
|
4775
|
+
var OUTPUT_FORMAT_PROMPT = `<OutputFormat>
|
|
4160
4776
|
|
|
4161
|
-
|
|
4777
|
+
You should use one tool or multiple tools (depends on your task), follow the format below, replace the <ToolName.method_name> and <args_name> with the actual tool name.
|
|
4162
4778
|
|
|
4163
|
-
|
|
4779
|
+
Thought: ...
|
|
4780
|
+
Action: <ToolName1.method_name1><args_name1>args_value1</args_name1><args_name2>args_value2</args_name2>...</ToolName1.method_name1>
|
|
4164
4781
|
|
|
4165
|
-
|
|
4782
|
+
**YOUR OUTPUT MUST INCLUDE THE ACTUAL <args_name> AND <args_value>!!!**
|
|
4783
|
+
**If the task is completed, simply return the completion information like <TaskCompletion>your_completion_information</TaskCompletion>, No any tool call should be included.**
|
|
4784
|
+
</OutputFormat>
|
|
4785
|
+
`;
|
|
4786
|
+
var TOOLS_PROMPT = `
|
|
4787
|
+
## Tools And Tool Calls Format
|
|
4166
4788
|
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
The focus is on executing operations rather than generating or analyzing content.
|
|
4170
|
-
</Operational Tasks Definition>
|
|
4789
|
+
Tools are represented by a predefined XML-like syntax structure. This structure encapsulates parameter lists within tags such as \`<ToolName.method_name>\`.
|
|
4790
|
+
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.
|
|
4171
4791
|
|
|
4172
|
-
|
|
4173
|
-
|
|
4792
|
+
### Available Tools
|
|
4793
|
+
{available_tools}
|
|
4174
4794
|
|
|
4175
|
-
|
|
4176
|
-
|
|
4795
|
+
### Description of Available Tools
|
|
4796
|
+
{desc_of_tools}
|
|
4177
4797
|
|
|
4178
|
-
|
|
4179
|
-
|
|
4180
|
-
</System Tools OutputFormat>
|
|
4798
|
+
### Output Format
|
|
4799
|
+
{output_format}
|
|
4181
4800
|
`;
|
|
4182
4801
|
var SYSTEM_TOOLS_PROMPT = `
|
|
4183
4802
|
## System Tools And System Tools Call Format
|
|
@@ -4195,7 +4814,7 @@ By identifying and matching keywords inside these tags, the system automatically
|
|
|
4195
4814
|
{outputFormat}
|
|
4196
4815
|
`;
|
|
4197
4816
|
|
|
4198
|
-
// src/agent.ts
|
|
4817
|
+
// src/core/agent.ts
|
|
4199
4818
|
var Agent = class {
|
|
4200
4819
|
name;
|
|
4201
4820
|
profile;
|
|
@@ -4211,6 +4830,14 @@ var Agent = class {
|
|
|
4211
4830
|
toolcallManagerPoolSize;
|
|
4212
4831
|
postProcessor;
|
|
4213
4832
|
useFunctionCalling;
|
|
4833
|
+
// New properties from Python 0.2.2
|
|
4834
|
+
agentId;
|
|
4835
|
+
workspaceDir;
|
|
4836
|
+
executor;
|
|
4837
|
+
autoShutdownExecutor;
|
|
4838
|
+
parallelExecution;
|
|
4839
|
+
observeTimeout;
|
|
4840
|
+
skills;
|
|
4214
4841
|
constructor(config2) {
|
|
4215
4842
|
const {
|
|
4216
4843
|
name,
|
|
@@ -4220,18 +4847,38 @@ var Agent = class {
|
|
|
4220
4847
|
subAgents = [],
|
|
4221
4848
|
mcpServerNames = [],
|
|
4222
4849
|
modelConfig,
|
|
4850
|
+
udfModelName,
|
|
4223
4851
|
verbose = false,
|
|
4224
4852
|
useFunctionCalling = false,
|
|
4225
4853
|
postProcessor = null,
|
|
4226
|
-
toolcallManagerPoolSize = 5
|
|
4854
|
+
toolcallManagerPoolSize = 5,
|
|
4855
|
+
// New parameters from Python 0.2.2
|
|
4856
|
+
agentId,
|
|
4857
|
+
workspaceDir = "",
|
|
4858
|
+
executor = null,
|
|
4859
|
+
autoShutdownExecutor = false,
|
|
4860
|
+
parallelExecution = false,
|
|
4861
|
+
observeTimeout = 60,
|
|
4862
|
+
skills = []
|
|
4227
4863
|
} = config2;
|
|
4228
4864
|
this.name = name;
|
|
4229
4865
|
this.profile = profile;
|
|
4230
4866
|
this.verbose = verbose;
|
|
4231
|
-
this.modelConfig = modelConfig || "deepseek";
|
|
4867
|
+
this.modelConfig = udfModelName || modelConfig || "deepseek";
|
|
4232
4868
|
this.subAgents = subAgents || [];
|
|
4233
4869
|
this.postProcessor = postProcessor;
|
|
4234
4870
|
this.useFunctionCalling = useFunctionCalling;
|
|
4871
|
+
this.agentId = agentId || uuidv4();
|
|
4872
|
+
this.workspaceDir = workspaceDir ? path7.resolve(workspaceDir) : "";
|
|
4873
|
+
this.executor = executor;
|
|
4874
|
+
this.autoShutdownExecutor = autoShutdownExecutor;
|
|
4875
|
+
this.parallelExecution = parallelExecution;
|
|
4876
|
+
this.observeTimeout = observeTimeout;
|
|
4877
|
+
this.skills = skills;
|
|
4878
|
+
if (this.workspaceDir && !fs10.existsSync(this.workspaceDir)) {
|
|
4879
|
+
fs10.mkdirSync(this.workspaceDir, { recursive: true });
|
|
4880
|
+
logger.debug(`Created workspace directory for agent ${this.name}: ${this.workspaceDir}`);
|
|
4881
|
+
}
|
|
4235
4882
|
const agentToolNames = registerAgentAsTool(this.subAgents, Boolean(this.verbose));
|
|
4236
4883
|
this.tools = [...tools2, ...agentToolNames];
|
|
4237
4884
|
this.mcpServerNames = mcpServerNames;
|
|
@@ -4500,6 +5147,14 @@ var Agent = class {
|
|
|
4500
5147
|
throw error;
|
|
4501
5148
|
} finally {
|
|
4502
5149
|
await stack.close();
|
|
5150
|
+
if (this.autoShutdownExecutor && this.executor) {
|
|
5151
|
+
try {
|
|
5152
|
+
await this.executor.shutdown({ wait: true });
|
|
5153
|
+
logger.debug(`Executor shutdown completed for agent ${this.name}`);
|
|
5154
|
+
} catch (error) {
|
|
5155
|
+
logger.warn(`Error during executor shutdown: ${error}`);
|
|
5156
|
+
}
|
|
5157
|
+
}
|
|
4503
5158
|
}
|
|
4504
5159
|
}
|
|
4505
5160
|
/**
|
|
@@ -4553,21 +5208,192 @@ var Agent = class {
|
|
|
4553
5208
|
getSubAgents() {
|
|
4554
5209
|
return [...this.subAgents];
|
|
4555
5210
|
}
|
|
5211
|
+
/**
|
|
5212
|
+
* Get agent ID
|
|
5213
|
+
*/
|
|
5214
|
+
getAgentId() {
|
|
5215
|
+
return this.agentId;
|
|
5216
|
+
}
|
|
5217
|
+
/**
|
|
5218
|
+
* Get workspace directory
|
|
5219
|
+
*/
|
|
5220
|
+
getWorkspaceDir() {
|
|
5221
|
+
return this.workspaceDir;
|
|
5222
|
+
}
|
|
5223
|
+
/**
|
|
5224
|
+
* Get parallel execution setting
|
|
5225
|
+
*/
|
|
5226
|
+
getParallelExecution() {
|
|
5227
|
+
return this.parallelExecution;
|
|
5228
|
+
}
|
|
5229
|
+
/**
|
|
5230
|
+
* Get observe timeout setting
|
|
5231
|
+
*/
|
|
5232
|
+
getObserveTimeout() {
|
|
5233
|
+
return this.observeTimeout;
|
|
5234
|
+
}
|
|
5235
|
+
/**
|
|
5236
|
+
* Get skills
|
|
5237
|
+
*/
|
|
5238
|
+
getSkills() {
|
|
5239
|
+
return [...this.skills];
|
|
5240
|
+
}
|
|
5241
|
+
/**
|
|
5242
|
+
* Get executor
|
|
5243
|
+
*/
|
|
5244
|
+
getExecutor() {
|
|
5245
|
+
return this.executor;
|
|
5246
|
+
}
|
|
5247
|
+
/**
|
|
5248
|
+
* Set executor
|
|
5249
|
+
*/
|
|
5250
|
+
setExecutor(executor) {
|
|
5251
|
+
this.executor = executor;
|
|
5252
|
+
}
|
|
5253
|
+
/**
|
|
5254
|
+
* Get chat history message
|
|
5255
|
+
*/
|
|
5256
|
+
get chatHistoryMessage() {
|
|
5257
|
+
return this.history;
|
|
5258
|
+
}
|
|
5259
|
+
};
|
|
5260
|
+
|
|
5261
|
+
// src/schemas/feedback.ts
|
|
5262
|
+
var Feedback = class _Feedback {
|
|
5263
|
+
/**
|
|
5264
|
+
* Whether the test/evaluation passed
|
|
5265
|
+
*/
|
|
5266
|
+
isPassing;
|
|
5267
|
+
/**
|
|
5268
|
+
* Detailed feedback message
|
|
5269
|
+
*/
|
|
5270
|
+
feedback;
|
|
5271
|
+
constructor(isPassing = false, feedback) {
|
|
5272
|
+
this.isPassing = isPassing;
|
|
5273
|
+
this.feedback = feedback || new Message("user", "No feedback provided. Please check feedback settings.");
|
|
5274
|
+
this._validate();
|
|
5275
|
+
}
|
|
5276
|
+
/**
|
|
5277
|
+
* Validate the feedback
|
|
5278
|
+
*/
|
|
5279
|
+
_validate() {
|
|
5280
|
+
if (!this.feedback.content) {
|
|
5281
|
+
throw new Error("Feedback content cannot be empty");
|
|
5282
|
+
}
|
|
5283
|
+
if (this.feedback.tag !== void 0) {
|
|
5284
|
+
if (!this.feedback.tag) {
|
|
5285
|
+
this.feedback.tag = "feedback";
|
|
5286
|
+
} else if (!this.feedback.tag.toLowerCase().includes("feedback")) {
|
|
5287
|
+
this.feedback.tag = `feedback_${this.feedback.tag}`;
|
|
5288
|
+
}
|
|
5289
|
+
}
|
|
5290
|
+
}
|
|
5291
|
+
/**
|
|
5292
|
+
* Convert feedback to a Message, optionally with pre/post content
|
|
5293
|
+
*
|
|
5294
|
+
* @param preContent - Content to prepend to the feedback
|
|
5295
|
+
* @param postContent - Content to append to the feedback
|
|
5296
|
+
* @returns Combined Message
|
|
5297
|
+
*/
|
|
5298
|
+
toMessage(preContent = "", postContent = "") {
|
|
5299
|
+
const feedbackMsg = new Message(
|
|
5300
|
+
this.feedback.role,
|
|
5301
|
+
this.feedback.content,
|
|
5302
|
+
this.feedback.images,
|
|
5303
|
+
this.feedback.type
|
|
5304
|
+
);
|
|
5305
|
+
if (this.feedback.tag !== void 0) {
|
|
5306
|
+
feedbackMsg.tag = this.feedback.tag;
|
|
5307
|
+
}
|
|
5308
|
+
let result = feedbackMsg;
|
|
5309
|
+
if (preContent) {
|
|
5310
|
+
const preMsg = new Message("user", preContent);
|
|
5311
|
+
result = this._mergeMessages(preMsg, result);
|
|
5312
|
+
}
|
|
5313
|
+
if (postContent) {
|
|
5314
|
+
const postMsg = new Message("user", postContent);
|
|
5315
|
+
result = this._mergeMessages(result, postMsg);
|
|
5316
|
+
}
|
|
5317
|
+
return result;
|
|
5318
|
+
}
|
|
5319
|
+
/**
|
|
5320
|
+
* Merge two messages into one
|
|
5321
|
+
*/
|
|
5322
|
+
_mergeMessages(first, second) {
|
|
5323
|
+
const merged = new Message(
|
|
5324
|
+
first.role,
|
|
5325
|
+
`${first.content}
|
|
5326
|
+
${second.content}`,
|
|
5327
|
+
first.images || second.images,
|
|
5328
|
+
first.type || second.type
|
|
5329
|
+
);
|
|
5330
|
+
if (second.tag !== void 0) {
|
|
5331
|
+
merged.tag = second.tag;
|
|
5332
|
+
}
|
|
5333
|
+
return merged;
|
|
5334
|
+
}
|
|
5335
|
+
/**
|
|
5336
|
+
* Format feedback for API calls
|
|
5337
|
+
*
|
|
5338
|
+
* @param preContent - Content to prepend
|
|
5339
|
+
* @param postContent - Content to append
|
|
5340
|
+
* @returns Formatted object for API
|
|
5341
|
+
*/
|
|
5342
|
+
formatForApi(preContent = "", postContent = "") {
|
|
5343
|
+
const message = this.toMessage(preContent, postContent);
|
|
5344
|
+
return message.toObject();
|
|
5345
|
+
}
|
|
5346
|
+
/**
|
|
5347
|
+
* Create a passing feedback
|
|
5348
|
+
*/
|
|
5349
|
+
static pass(content) {
|
|
5350
|
+
return new _Feedback(true, new Message("user", content));
|
|
5351
|
+
}
|
|
5352
|
+
/**
|
|
5353
|
+
* Create a failing feedback
|
|
5354
|
+
*/
|
|
5355
|
+
static fail(content) {
|
|
5356
|
+
return new _Feedback(false, new Message("user", content));
|
|
5357
|
+
}
|
|
4556
5358
|
};
|
|
4557
5359
|
|
|
4558
|
-
// src/
|
|
4559
|
-
var
|
|
5360
|
+
// src/runtime/orchestrator/base.ts
|
|
5361
|
+
var InstructionType = /* @__PURE__ */ ((InstructionType2) => {
|
|
5362
|
+
InstructionType2["VALID"] = "valid";
|
|
5363
|
+
InstructionType2["QUIT"] = "quit";
|
|
5364
|
+
InstructionType2["SEND_TO_ALL"] = "sendToAll";
|
|
5365
|
+
InstructionType2["NO_AGENT_NAME"] = "noAgentName";
|
|
5366
|
+
InstructionType2["NO_AVAILABLE_AGENT_NAME"] = "noAvailableAgentName";
|
|
5367
|
+
return InstructionType2;
|
|
5368
|
+
})(InstructionType || {});
|
|
5369
|
+
var BaseOrchestrator = class {
|
|
4560
5370
|
/**
|
|
4561
|
-
* List of agents in the
|
|
5371
|
+
* List of agents in the orchestrator
|
|
4562
5372
|
*/
|
|
4563
5373
|
agents = [];
|
|
4564
5374
|
/**
|
|
4565
5375
|
* Assign skills to agent with their names
|
|
4566
5376
|
*/
|
|
4567
5377
|
agentSkills = {};
|
|
4568
|
-
|
|
5378
|
+
/**
|
|
5379
|
+
* Whether to always wait for human input
|
|
5380
|
+
*/
|
|
5381
|
+
alwaysWaitHumanInput = false;
|
|
5382
|
+
/**
|
|
5383
|
+
* Maximum number of rounds for agent execution
|
|
5384
|
+
*/
|
|
5385
|
+
maxRounds = 100;
|
|
5386
|
+
constructor(options = {}) {
|
|
5387
|
+
const {
|
|
5388
|
+
agents = [],
|
|
5389
|
+
agentSkills = {},
|
|
5390
|
+
alwaysWaitHumanInput = false,
|
|
5391
|
+
maxRounds = 100
|
|
5392
|
+
} = options;
|
|
4569
5393
|
this.agents = agents;
|
|
4570
5394
|
this.agentSkills = agentSkills;
|
|
5395
|
+
this.alwaysWaitHumanInput = alwaysWaitHumanInput;
|
|
5396
|
+
this.maxRounds = maxRounds;
|
|
4571
5397
|
this.setAgentSkills();
|
|
4572
5398
|
}
|
|
4573
5399
|
/**
|
|
@@ -4637,12 +5463,21 @@ var BaseEnvironment = class {
|
|
|
4637
5463
|
}
|
|
4638
5464
|
return ["noAvailableAgentName" /* NO_AVAILABLE_AGENT_NAME */, instruction];
|
|
4639
5465
|
}
|
|
5466
|
+
/**
|
|
5467
|
+
* Shutdown all agent executors
|
|
5468
|
+
*/
|
|
5469
|
+
async shutdownAllExecutors(options) {
|
|
5470
|
+
const wait = options?.wait ?? true;
|
|
5471
|
+
for (const agent of this.agents) {
|
|
5472
|
+
}
|
|
5473
|
+
logger.debug("All agent executors shutdown");
|
|
5474
|
+
}
|
|
4640
5475
|
/**
|
|
4641
5476
|
* Run with a single goal until completion (non-interactive mode)
|
|
4642
5477
|
*/
|
|
4643
5478
|
async runGoal(goal) {
|
|
4644
5479
|
if (this.agents.length === 0) {
|
|
4645
|
-
logger.error("No agents in the
|
|
5480
|
+
logger.error("No agents in the orchestrator.");
|
|
4646
5481
|
return;
|
|
4647
5482
|
}
|
|
4648
5483
|
const [instructionType, processedInstruction] = this.postProcessInstruction(
|
|
@@ -4673,36 +5508,40 @@ var BaseEnvironment = class {
|
|
|
4673
5508
|
}
|
|
4674
5509
|
}
|
|
4675
5510
|
/**
|
|
4676
|
-
* Run the
|
|
5511
|
+
* Run the orchestrator (interactive mode)
|
|
4677
5512
|
*/
|
|
4678
|
-
async run() {
|
|
5513
|
+
async run(instruction) {
|
|
5514
|
+
if (instruction) {
|
|
5515
|
+
await this.runGoal(instruction);
|
|
5516
|
+
return;
|
|
5517
|
+
}
|
|
4679
5518
|
const readline = await import("readline");
|
|
4680
5519
|
const rl = readline.createInterface({
|
|
4681
5520
|
input: process.stdin,
|
|
4682
5521
|
output: process.stdout
|
|
4683
5522
|
});
|
|
4684
5523
|
const question = (prompt) => {
|
|
4685
|
-
return new Promise((
|
|
4686
|
-
rl.question(prompt,
|
|
5524
|
+
return new Promise((resolve5) => {
|
|
5525
|
+
rl.question(prompt, resolve5);
|
|
4687
5526
|
});
|
|
4688
5527
|
};
|
|
4689
5528
|
try {
|
|
4690
5529
|
while (true) {
|
|
4691
5530
|
try {
|
|
4692
|
-
let
|
|
5531
|
+
let userInstruction;
|
|
4693
5532
|
if (this.agents.length === 0) {
|
|
4694
|
-
logger.error("No agents in the
|
|
5533
|
+
logger.error("No agents in the orchestrator.");
|
|
4695
5534
|
break;
|
|
4696
5535
|
} else if (this.agents.length === 1) {
|
|
4697
|
-
|
|
5536
|
+
userInstruction = await question("Enter your instruction (or '/q' to quit): ");
|
|
4698
5537
|
} else {
|
|
4699
5538
|
const agentNames = this.agents.map((agent) => agent.name).join(", ");
|
|
4700
|
-
|
|
5539
|
+
userInstruction = await question(
|
|
4701
5540
|
`Enter your instruction with '@agent_name' (or '/q' to quit), available agents: ${agentNames}: `
|
|
4702
5541
|
);
|
|
4703
5542
|
}
|
|
4704
5543
|
const [instructionType, processedInstruction] = this.postProcessInstruction(
|
|
4705
|
-
|
|
5544
|
+
userInstruction,
|
|
4706
5545
|
this.agents.map((agent) => agent.name)
|
|
4707
5546
|
);
|
|
4708
5547
|
if (instructionType === "quit" /* QUIT */) {
|
|
@@ -4728,16 +5567,18 @@ var BaseEnvironment = class {
|
|
|
4728
5567
|
}
|
|
4729
5568
|
}
|
|
4730
5569
|
};
|
|
5570
|
+
var BaseEnvironment = BaseOrchestrator;
|
|
4731
5571
|
|
|
4732
|
-
// src/
|
|
4733
|
-
import * as
|
|
4734
|
-
var
|
|
5572
|
+
// src/runtime/orchestrator/coding.ts
|
|
5573
|
+
import * as fs11 from "fs";
|
|
5574
|
+
var CodingOrchestrator = class extends BaseOrchestrator {
|
|
4735
5575
|
/**
|
|
4736
5576
|
* Workspace directory
|
|
4737
5577
|
*/
|
|
4738
5578
|
workspaceDir = "";
|
|
4739
|
-
constructor(
|
|
4740
|
-
|
|
5579
|
+
constructor(options = {}) {
|
|
5580
|
+
const { workspaceDir = "", ...baseOptions } = options;
|
|
5581
|
+
super(baseOptions);
|
|
4741
5582
|
this.workspaceDir = workspaceDir;
|
|
4742
5583
|
this.setWorkspaceDir();
|
|
4743
5584
|
}
|
|
@@ -4748,8 +5589,8 @@ var CodingEnvironment = class extends BaseEnvironment {
|
|
|
4748
5589
|
if (!this.workspaceDir) {
|
|
4749
5590
|
this.workspaceDir = WORKSPACE_DIR;
|
|
4750
5591
|
}
|
|
4751
|
-
if (!
|
|
4752
|
-
|
|
5592
|
+
if (!fs11.existsSync(this.workspaceDir)) {
|
|
5593
|
+
fs11.mkdirSync(this.workspaceDir, { recursive: true });
|
|
4753
5594
|
} else {
|
|
4754
5595
|
logger.debug(`Workspace directory already exists: ${this.workspaceDir}`);
|
|
4755
5596
|
}
|
|
@@ -4768,32 +5609,1206 @@ Your workspace directory is: <workspace>${this.workspaceDir}</workspace>
|
|
|
4768
5609
|
}
|
|
4769
5610
|
}
|
|
4770
5611
|
};
|
|
5612
|
+
var CodingEnvironment = CodingOrchestrator;
|
|
5613
|
+
|
|
5614
|
+
// src/runtime/orchestrator/reflexion.ts
|
|
5615
|
+
var CRITIC_SYSTEM_PROMPT = `You are a critic agent specialized in {taskType} tasks.
|
|
5616
|
+
|
|
5617
|
+
Your role is to:
|
|
5618
|
+
1. Analyze the implementation provided
|
|
5619
|
+
2. Identify issues and areas for improvement
|
|
5620
|
+
3. Provide constructive feedback in the form of {feedbackType}
|
|
5621
|
+
4. Suggest specific improvements
|
|
5622
|
+
|
|
5623
|
+
Be thorough but constructive in your feedback.`;
|
|
5624
|
+
var ACTOR_SYSTEM_PROMPT = `You are an actor agent specialized in {taskType} tasks.
|
|
5625
|
+
|
|
5626
|
+
Your role is to:
|
|
5627
|
+
1. Review the feedback and self-reflection
|
|
5628
|
+
2. Improve the implementation based on the suggestions
|
|
5629
|
+
3. Produce a better version of the code/output
|
|
5630
|
+
|
|
5631
|
+
Focus on addressing all issues mentioned in the feedback.`;
|
|
5632
|
+
var ReflexionOrchestrator = class {
|
|
5633
|
+
task;
|
|
5634
|
+
taskType;
|
|
5635
|
+
feedbackType;
|
|
5636
|
+
environment;
|
|
5637
|
+
criticAgentConfig;
|
|
5638
|
+
actorAgentConfig;
|
|
5639
|
+
maxIterations;
|
|
5640
|
+
constructor(options) {
|
|
5641
|
+
this.task = options.task;
|
|
5642
|
+
this.taskType = options.taskType;
|
|
5643
|
+
this.feedbackType = options.feedbackType;
|
|
5644
|
+
this.environment = options.environment;
|
|
5645
|
+
this.maxIterations = options.maxIterations ?? 3;
|
|
5646
|
+
this.criticAgentConfig = {
|
|
5647
|
+
name: "CriticAgent",
|
|
5648
|
+
profile: "You are an AI assistant.",
|
|
5649
|
+
tools: ["CommandLineTool.execute", "FileEditor.write", "FileEditor.read"],
|
|
5650
|
+
udfModelName: "deepseek",
|
|
5651
|
+
workspaceDir: "",
|
|
5652
|
+
fewShot: "",
|
|
5653
|
+
verbose: false,
|
|
5654
|
+
saveSelfReflection: false,
|
|
5655
|
+
postContent: "",
|
|
5656
|
+
...options.criticAgentConfig
|
|
5657
|
+
};
|
|
5658
|
+
this.actorAgentConfig = {
|
|
5659
|
+
name: "ActorAgent",
|
|
5660
|
+
profile: "You are an AI engineer.",
|
|
5661
|
+
tools: ["CommandLineTool.execute", "FileEditor.write", "ThinkTool.execute"],
|
|
5662
|
+
udfModelName: "deepseek",
|
|
5663
|
+
workspaceDir: "",
|
|
5664
|
+
fewShot: "",
|
|
5665
|
+
verbose: false,
|
|
5666
|
+
...options.actorAgentConfig
|
|
5667
|
+
};
|
|
5668
|
+
}
|
|
5669
|
+
/**
|
|
5670
|
+
* Create system prompt for critic agent
|
|
5671
|
+
*/
|
|
5672
|
+
createCriticSystemPrompt() {
|
|
5673
|
+
return CRITIC_SYSTEM_PROMPT.replace("{taskType}", this.taskType).replace("{feedbackType}", this.feedbackType);
|
|
5674
|
+
}
|
|
5675
|
+
/**
|
|
5676
|
+
* Create system prompt for actor agent
|
|
5677
|
+
*/
|
|
5678
|
+
createActorSystemPrompt() {
|
|
5679
|
+
return ACTOR_SYSTEM_PROMPT.replace("{taskType}", this.taskType).replace("{feedbackType}", this.feedbackType);
|
|
5680
|
+
}
|
|
5681
|
+
/**
|
|
5682
|
+
* Build history message for critic agent
|
|
5683
|
+
*/
|
|
5684
|
+
buildCriticHistoryMessage(impl, feedback) {
|
|
5685
|
+
const preContent = this.criticAgentConfig.workspaceDir ? `Your workspace directory is: ${this.criticAgentConfig.workspaceDir}
|
|
5686
|
+
|
|
5687
|
+
` : "";
|
|
5688
|
+
const postContent = (this.criticAgentConfig.postContent || "") + "\nDo not change the impl file, just use tools to find the correct way and give the self-reflection.";
|
|
5689
|
+
return [feedback.toMessage(preContent, postContent)];
|
|
5690
|
+
}
|
|
5691
|
+
/**
|
|
5692
|
+
* Build history message for actor agent
|
|
5693
|
+
*/
|
|
5694
|
+
buildActorHistoryMessage(feedback, selfReflection, improvedImplPath) {
|
|
5695
|
+
const messages = [];
|
|
5696
|
+
if (this.actorAgentConfig.fewShot) {
|
|
5697
|
+
messages.push(new Message("user", this.actorAgentConfig.fewShot));
|
|
5698
|
+
}
|
|
5699
|
+
messages.push(feedback);
|
|
5700
|
+
messages.push(new Message(
|
|
5701
|
+
"assistant",
|
|
5702
|
+
`<self_reflection>
|
|
5703
|
+
${selfReflection}
|
|
5704
|
+
</self_reflection>`
|
|
5705
|
+
));
|
|
5706
|
+
messages.push(new Message(
|
|
5707
|
+
"user",
|
|
5708
|
+
`Use ThinkTool to recap the feedback and self-reflection, think how to improve the previous impl, and use FileEditor tools to write your improved implementation, and saved it to the file \`${improvedImplPath}\`.`
|
|
5709
|
+
));
|
|
5710
|
+
return messages;
|
|
5711
|
+
}
|
|
5712
|
+
/**
|
|
5713
|
+
* Run the reflexion loop
|
|
5714
|
+
*/
|
|
5715
|
+
async run() {
|
|
5716
|
+
const actor = new Agent({
|
|
5717
|
+
name: this.actorAgentConfig.name || "ActorAgent",
|
|
5718
|
+
profile: this.actorAgentConfig.profile || "You are an AI engineer.",
|
|
5719
|
+
system: this.createActorSystemPrompt(),
|
|
5720
|
+
tools: this.actorAgentConfig.tools,
|
|
5721
|
+
modelConfig: this.actorAgentConfig.udfModelName,
|
|
5722
|
+
verbose: this.actorAgentConfig.verbose
|
|
5723
|
+
});
|
|
5724
|
+
const critic = new Agent({
|
|
5725
|
+
name: this.criticAgentConfig.name || "CriticAgent",
|
|
5726
|
+
profile: this.criticAgentConfig.profile || "You are an AI assistant.",
|
|
5727
|
+
system: this.createCriticSystemPrompt(),
|
|
5728
|
+
tools: this.criticAgentConfig.tools,
|
|
5729
|
+
modelConfig: this.criticAgentConfig.udfModelName,
|
|
5730
|
+
verbose: this.criticAgentConfig.verbose
|
|
5731
|
+
});
|
|
5732
|
+
let currentRound = 0;
|
|
5733
|
+
let feedback = await this.environment.step(this.environment.initStepKwargs);
|
|
5734
|
+
let finalOutput = "";
|
|
5735
|
+
while (!feedback.isPassing && currentRound < this.maxIterations) {
|
|
5736
|
+
logger.debug(`Feedback: ${feedback.feedback.content}`);
|
|
5737
|
+
const criticHistoryMsgs = this.buildCriticHistoryMessage(
|
|
5738
|
+
this.environment.improvedImpl,
|
|
5739
|
+
feedback
|
|
5740
|
+
);
|
|
5741
|
+
let selfReflection = await critic.run(
|
|
5742
|
+
criticHistoryMsgs[0].content
|
|
5743
|
+
);
|
|
5744
|
+
if (selfReflection.includes("<TaskCompletion>")) {
|
|
5745
|
+
selfReflection = selfReflection.replace(/<TaskCompletion>/g, "<self-reflection>").replace(/<\/TaskCompletion>/g, "</self-reflection>").trim();
|
|
5746
|
+
}
|
|
5747
|
+
logger.info(`Critic agent self-reflection: ${selfReflection}`);
|
|
5748
|
+
const actorHistoryMsgs = this.buildActorHistoryMessage(
|
|
5749
|
+
criticHistoryMsgs[criticHistoryMsgs.length - 1],
|
|
5750
|
+
selfReflection,
|
|
5751
|
+
this.environment.improvedImpl
|
|
5752
|
+
);
|
|
5753
|
+
let improvedImplSummary = await actor.run(
|
|
5754
|
+
actorHistoryMsgs[actorHistoryMsgs.length - 1].content
|
|
5755
|
+
);
|
|
5756
|
+
if (improvedImplSummary.includes("<TaskCompletion>")) {
|
|
5757
|
+
improvedImplSummary = improvedImplSummary.replace(/<TaskCompletion>/g, "<improved-impl-summary>").replace(/<\/TaskCompletion>/g, "</improved-impl-summary>").trim();
|
|
5758
|
+
}
|
|
5759
|
+
logger.info(`Actor agent improved impl summary: ${improvedImplSummary}`);
|
|
5760
|
+
finalOutput = improvedImplSummary;
|
|
5761
|
+
feedback = await this.environment.step(this.environment.improvedStepKwargs);
|
|
5762
|
+
currentRound++;
|
|
5763
|
+
}
|
|
5764
|
+
logger.info(`Reflection task completed in ${currentRound} rounds`);
|
|
5765
|
+
try {
|
|
5766
|
+
await this.environment.shutdown();
|
|
5767
|
+
logger.info("Environment shutdown completed");
|
|
5768
|
+
} catch (error) {
|
|
5769
|
+
logger.warn(`Error during environment shutdown: ${error}`);
|
|
5770
|
+
}
|
|
5771
|
+
return {
|
|
5772
|
+
success: feedback.isPassing,
|
|
5773
|
+
iterations: currentRound,
|
|
5774
|
+
finalOutput
|
|
5775
|
+
};
|
|
5776
|
+
}
|
|
5777
|
+
};
|
|
5778
|
+
|
|
5779
|
+
// src/runtime/environment/base.ts
|
|
5780
|
+
import * as fs12 from "fs";
|
|
5781
|
+
import * as path8 from "path";
|
|
5782
|
+
var Environment = class {
|
|
5783
|
+
/**
|
|
5784
|
+
* Arguments for the step method (initial implementation)
|
|
5785
|
+
*/
|
|
5786
|
+
initStepKwargs = {};
|
|
5787
|
+
/**
|
|
5788
|
+
* Arguments for the step method (improved implementation)
|
|
5789
|
+
*/
|
|
5790
|
+
improvedStepKwargs = {};
|
|
5791
|
+
/**
|
|
5792
|
+
* Path to the initial implementation file
|
|
5793
|
+
*/
|
|
5794
|
+
initImpl;
|
|
5795
|
+
/**
|
|
5796
|
+
* Path to the improved implementation file
|
|
5797
|
+
*/
|
|
5798
|
+
improvedImpl;
|
|
5799
|
+
constructor(options) {
|
|
5800
|
+
this.initImpl = path8.resolve(options.initImpl);
|
|
5801
|
+
this.improvedImpl = path8.resolve(options.improvedImpl);
|
|
5802
|
+
this.initStepKwargs = options.initStepKwargs || {};
|
|
5803
|
+
this.improvedStepKwargs = options.improvedStepKwargs || {};
|
|
5804
|
+
this._initImprovedImpl();
|
|
5805
|
+
}
|
|
5806
|
+
/**
|
|
5807
|
+
* Initialize the improved implementation by copying from init
|
|
5808
|
+
*/
|
|
5809
|
+
_initImprovedImpl() {
|
|
5810
|
+
if (fs12.existsSync(this.initImpl)) {
|
|
5811
|
+
if (fs12.existsSync(this.improvedImpl)) {
|
|
5812
|
+
const stats = fs12.statSync(this.improvedImpl);
|
|
5813
|
+
if (stats.isDirectory()) {
|
|
5814
|
+
fs12.rmSync(this.improvedImpl, { recursive: true });
|
|
5815
|
+
} else {
|
|
5816
|
+
fs12.unlinkSync(this.improvedImpl);
|
|
5817
|
+
}
|
|
5818
|
+
}
|
|
5819
|
+
fs12.copyFileSync(this.initImpl, this.improvedImpl);
|
|
5820
|
+
}
|
|
5821
|
+
}
|
|
5822
|
+
/**
|
|
5823
|
+
* Check if the improved implementation exists
|
|
5824
|
+
*/
|
|
5825
|
+
hasImprovedImpl() {
|
|
5826
|
+
return fs12.existsSync(this.improvedImpl);
|
|
5827
|
+
}
|
|
5828
|
+
/**
|
|
5829
|
+
* Shutdown the environment
|
|
5830
|
+
*
|
|
5831
|
+
* Called when the evaluation is complete or interrupted.
|
|
5832
|
+
* Override to clean up resources.
|
|
5833
|
+
*/
|
|
5834
|
+
async shutdown() {
|
|
5835
|
+
}
|
|
5836
|
+
/**
|
|
5837
|
+
* Evaluate the environment on a benchmark
|
|
5838
|
+
*
|
|
5839
|
+
* TODO: Add benchmark support for self-evolving
|
|
5840
|
+
*
|
|
5841
|
+
* @returns Whether the benchmark passed
|
|
5842
|
+
*/
|
|
5843
|
+
async bench() {
|
|
5844
|
+
throw new Error("bench() is not implemented");
|
|
5845
|
+
}
|
|
5846
|
+
};
|
|
5847
|
+
|
|
5848
|
+
// src/runtime/executors/base.ts
|
|
5849
|
+
function createGeneratedToolcall(options) {
|
|
5850
|
+
const toolCallId = options.toolCallId || `call_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
5851
|
+
return {
|
|
5852
|
+
toolName: options.toolName,
|
|
5853
|
+
toolArguments: options.toolArguments,
|
|
5854
|
+
toolCallId,
|
|
5855
|
+
source: options.source || "chat",
|
|
5856
|
+
isSuccess: true,
|
|
5857
|
+
rawContentFromLlm: options.rawContentFromLlm,
|
|
5858
|
+
idempotencyKey: `${options.toolName}-${JSON.stringify(options.toolArguments)}-${toolCallId}`
|
|
5859
|
+
};
|
|
5860
|
+
}
|
|
5861
|
+
function createExecutedToolcall(metadata, isSuccess, result) {
|
|
5862
|
+
return {
|
|
5863
|
+
metadata,
|
|
5864
|
+
isSuccess,
|
|
5865
|
+
result
|
|
5866
|
+
};
|
|
5867
|
+
}
|
|
5868
|
+
|
|
5869
|
+
// src/runtime/executors/utils.ts
|
|
5870
|
+
async function _executeSingleTool(generatedToolcall, toolStore) {
|
|
5871
|
+
const toolStores = Array.isArray(toolStore) ? toolStore : [toolStore];
|
|
5872
|
+
if (!generatedToolcall.isSuccess) {
|
|
5873
|
+
return createExecutedToolcall(
|
|
5874
|
+
generatedToolcall,
|
|
5875
|
+
false,
|
|
5876
|
+
`Tool extraction failed: ${generatedToolcall.failedReason || "Unknown"}`
|
|
5877
|
+
);
|
|
5878
|
+
}
|
|
5879
|
+
if (generatedToolcall.toolName.toLowerCase() === "taskcompletion") {
|
|
5880
|
+
return createExecutedToolcall(
|
|
5881
|
+
generatedToolcall,
|
|
5882
|
+
true,
|
|
5883
|
+
"The task has been completed."
|
|
5884
|
+
);
|
|
5885
|
+
}
|
|
5886
|
+
try {
|
|
5887
|
+
for (const ts of toolStores) {
|
|
5888
|
+
if (ts.hasTool && ts.hasTool(generatedToolcall.toolName)) {
|
|
5889
|
+
const toolDef = ts.getTool(generatedToolcall.toolName);
|
|
5890
|
+
if (!toolDef) {
|
|
5891
|
+
continue;
|
|
5892
|
+
}
|
|
5893
|
+
const toolExecute = toolDef.execute;
|
|
5894
|
+
if (!toolExecute) {
|
|
5895
|
+
return createExecutedToolcall(
|
|
5896
|
+
generatedToolcall,
|
|
5897
|
+
false,
|
|
5898
|
+
`Tool '${generatedToolcall.toolName}' has no execute function`
|
|
5899
|
+
);
|
|
5900
|
+
}
|
|
5901
|
+
const result = await toolExecute(generatedToolcall.toolArguments);
|
|
5902
|
+
const resultValue = result instanceof Message ? result : String(result);
|
|
5903
|
+
return createExecutedToolcall(
|
|
5904
|
+
generatedToolcall,
|
|
5905
|
+
true,
|
|
5906
|
+
resultValue
|
|
5907
|
+
);
|
|
5908
|
+
}
|
|
5909
|
+
}
|
|
5910
|
+
return createExecutedToolcall(
|
|
5911
|
+
generatedToolcall,
|
|
5912
|
+
false,
|
|
5913
|
+
`Tool '${generatedToolcall.toolName}' not found in tool_store. Please check the tool name.`
|
|
5914
|
+
);
|
|
5915
|
+
} catch (error) {
|
|
5916
|
+
const errorMsg = `Error executing tool: ${error instanceof Error ? error.message : String(error)}`;
|
|
5917
|
+
logger.error(errorMsg);
|
|
5918
|
+
return createExecutedToolcall(
|
|
5919
|
+
generatedToolcall,
|
|
5920
|
+
false,
|
|
5921
|
+
errorMsg
|
|
5922
|
+
);
|
|
5923
|
+
}
|
|
5924
|
+
}
|
|
5925
|
+
async function executeToolsSequential(toolcalls, toolStore) {
|
|
5926
|
+
const results = [];
|
|
5927
|
+
for (const toolcall of toolcalls) {
|
|
5928
|
+
const result = await _executeSingleTool(toolcall, toolStore);
|
|
5929
|
+
results.push(result);
|
|
5930
|
+
}
|
|
5931
|
+
return results;
|
|
5932
|
+
}
|
|
5933
|
+
async function executeToolsParallel(toolcalls, toolStore, maxConcurrency = 5) {
|
|
5934
|
+
const results = [];
|
|
5935
|
+
const pending = [];
|
|
5936
|
+
let runningCount = 0;
|
|
5937
|
+
for (const toolcall of toolcalls) {
|
|
5938
|
+
while (runningCount >= maxConcurrency) {
|
|
5939
|
+
await Promise.race(pending);
|
|
5940
|
+
}
|
|
5941
|
+
runningCount++;
|
|
5942
|
+
const promise = (async () => {
|
|
5943
|
+
try {
|
|
5944
|
+
const result = await _executeSingleTool(toolcall, toolStore);
|
|
5945
|
+
results.push(result);
|
|
5946
|
+
} finally {
|
|
5947
|
+
runningCount--;
|
|
5948
|
+
}
|
|
5949
|
+
})();
|
|
5950
|
+
pending.push(promise);
|
|
5951
|
+
}
|
|
5952
|
+
await Promise.all(pending);
|
|
5953
|
+
return results;
|
|
5954
|
+
}
|
|
5955
|
+
|
|
5956
|
+
// src/runtime/executors/localExecutor.ts
|
|
5957
|
+
import { v4 as uuidv42 } from "uuid";
|
|
5958
|
+
var _currentExecutor = null;
|
|
5959
|
+
function getCurrentExecutor() {
|
|
5960
|
+
return _currentExecutor;
|
|
5961
|
+
}
|
|
5962
|
+
function setCurrentExecutor(executor) {
|
|
5963
|
+
_currentExecutor = executor;
|
|
5964
|
+
}
|
|
5965
|
+
var LocalToolExecutor = class {
|
|
5966
|
+
poolSize;
|
|
5967
|
+
toolStores;
|
|
5968
|
+
// Concurrency control
|
|
5969
|
+
semaphore;
|
|
5970
|
+
// Task management
|
|
5971
|
+
pendingTasks = [];
|
|
5972
|
+
runningTasks = /* @__PURE__ */ new Set();
|
|
5973
|
+
finishedResults = [];
|
|
5974
|
+
successTasks = /* @__PURE__ */ new Map();
|
|
5975
|
+
failedTasks = /* @__PURE__ */ new Map();
|
|
5976
|
+
// Statistics
|
|
5977
|
+
totalSubmitted = 0;
|
|
5978
|
+
totalFinished = 0;
|
|
5979
|
+
totalFailed = 0;
|
|
5980
|
+
// Lifecycle state
|
|
5981
|
+
started = false;
|
|
5982
|
+
isShutdown = false;
|
|
5983
|
+
// Background process management
|
|
5984
|
+
backgroundProcesses = /* @__PURE__ */ new Map();
|
|
5985
|
+
constructor(poolSize = 5, toolStores = []) {
|
|
5986
|
+
this.poolSize = poolSize;
|
|
5987
|
+
this.toolStores = toolStores;
|
|
5988
|
+
this.semaphore = { available: poolSize };
|
|
5989
|
+
}
|
|
5990
|
+
// ---- Lifecycle Methods ----
|
|
5991
|
+
async start() {
|
|
5992
|
+
if (this.started) {
|
|
5993
|
+
logger.warn("Executor already started");
|
|
5994
|
+
return;
|
|
5995
|
+
}
|
|
5996
|
+
this.semaphore = { available: this.poolSize };
|
|
5997
|
+
this.started = true;
|
|
5998
|
+
this.isShutdown = false;
|
|
5999
|
+
logger.info(`LocalToolExecutor started with max concurrency: ${this.poolSize}`);
|
|
6000
|
+
}
|
|
6001
|
+
async shutdown(options) {
|
|
6002
|
+
const wait = options?.wait ?? true;
|
|
6003
|
+
if (this.isShutdown) {
|
|
6004
|
+
logger.warn("Executor already shutdown");
|
|
6005
|
+
return;
|
|
6006
|
+
}
|
|
6007
|
+
this.isShutdown = true;
|
|
6008
|
+
if (wait && this.runningTasks.size > 0) {
|
|
6009
|
+
logger.info(`Waiting for ${this.runningTasks.size} running tasks to complete...`);
|
|
6010
|
+
await Promise.allSettled(Array.from(this.runningTasks));
|
|
6011
|
+
logger.info("All tasks completed");
|
|
6012
|
+
}
|
|
6013
|
+
if (this.backgroundProcesses.size > 0) {
|
|
6014
|
+
logger.info("Cleaning up background processes...");
|
|
6015
|
+
await this.cleanupBackgroundProcesses();
|
|
6016
|
+
}
|
|
6017
|
+
this.started = false;
|
|
6018
|
+
logger.info("LocalToolExecutor shutdown");
|
|
6019
|
+
}
|
|
6020
|
+
// ---- Execution Methods ----
|
|
6021
|
+
async submit(toolcall) {
|
|
6022
|
+
if (!this.started) {
|
|
6023
|
+
throw new Error("Executor not started. Call start() first.");
|
|
6024
|
+
}
|
|
6025
|
+
if (this.isShutdown) {
|
|
6026
|
+
throw new Error("Executor already shutdown");
|
|
6027
|
+
}
|
|
6028
|
+
this.totalSubmitted++;
|
|
6029
|
+
const task = this._executeToolcall(toolcall);
|
|
6030
|
+
this.runningTasks.add(task);
|
|
6031
|
+
task.finally(() => {
|
|
6032
|
+
this.runningTasks.delete(task);
|
|
6033
|
+
});
|
|
6034
|
+
logger.debug(`Submitted tool call: ${toolcall.toolName}`);
|
|
6035
|
+
}
|
|
6036
|
+
async submitMany(toolcalls, options) {
|
|
6037
|
+
if (!this.started) {
|
|
6038
|
+
throw new Error("Executor not started. Call start() first.");
|
|
6039
|
+
}
|
|
6040
|
+
const toolcallList = Array.from(toolcalls);
|
|
6041
|
+
if (toolcallList.length === 0) {
|
|
6042
|
+
return;
|
|
6043
|
+
}
|
|
6044
|
+
const parallel = options?.parallel ?? true;
|
|
6045
|
+
if (parallel) {
|
|
6046
|
+
for (const toolcall of toolcallList) {
|
|
6047
|
+
await this.submit(toolcall);
|
|
6048
|
+
}
|
|
6049
|
+
logger.debug(`Submitted ${toolcallList.length} tool calls in parallel`);
|
|
6050
|
+
} else {
|
|
6051
|
+
logger.debug(`Starting sequential execution of ${toolcallList.length} tool calls`);
|
|
6052
|
+
for (const toolcall of toolcallList) {
|
|
6053
|
+
await this.submit(toolcall);
|
|
6054
|
+
while (this.runningTasks.size > 0) {
|
|
6055
|
+
await new Promise((resolve5) => setTimeout(resolve5, 100));
|
|
6056
|
+
}
|
|
6057
|
+
}
|
|
6058
|
+
logger.debug("Sequential execution completed");
|
|
6059
|
+
}
|
|
6060
|
+
}
|
|
6061
|
+
// ---- Observation Methods ----
|
|
6062
|
+
async observe(options) {
|
|
6063
|
+
const wait = options?.wait ?? false;
|
|
6064
|
+
const timeout = options?.timeout;
|
|
6065
|
+
const maxItems = options?.maxItems;
|
|
6066
|
+
if (wait && this.finishedResults.length === 0 && this.runningTasks.size > 0) {
|
|
6067
|
+
const startTime = Date.now();
|
|
6068
|
+
while (this.finishedResults.length === 0 && this.runningTasks.size > 0) {
|
|
6069
|
+
if (timeout !== void 0) {
|
|
6070
|
+
const elapsed = (Date.now() - startTime) / 1e3;
|
|
6071
|
+
if (elapsed >= timeout) {
|
|
6072
|
+
logger.warn(`Observe timeout (${timeout}s), no results yet`);
|
|
6073
|
+
break;
|
|
6074
|
+
}
|
|
6075
|
+
}
|
|
6076
|
+
await new Promise((resolve5) => setTimeout(resolve5, 100));
|
|
6077
|
+
}
|
|
6078
|
+
}
|
|
6079
|
+
const results = [];
|
|
6080
|
+
let count = 0;
|
|
6081
|
+
while (this.finishedResults.length > 0 && (maxItems === void 0 || count < maxItems)) {
|
|
6082
|
+
results.push(this.finishedResults.shift());
|
|
6083
|
+
count++;
|
|
6084
|
+
}
|
|
6085
|
+
if (results.length > 0) {
|
|
6086
|
+
logger.debug(`Returning ${results.length} execution results`);
|
|
6087
|
+
}
|
|
6088
|
+
return results;
|
|
6089
|
+
}
|
|
6090
|
+
async waitAll() {
|
|
6091
|
+
if (this.runningTasks.size > 0) {
|
|
6092
|
+
logger.debug(`Waiting for ${this.runningTasks.size} tasks to complete...`);
|
|
6093
|
+
await Promise.allSettled(Array.from(this.runningTasks));
|
|
6094
|
+
logger.info("All tasks completed");
|
|
6095
|
+
}
|
|
6096
|
+
}
|
|
6097
|
+
// ---- Status Methods ----
|
|
6098
|
+
status() {
|
|
6099
|
+
return {
|
|
6100
|
+
pending: this.pendingTasks.length,
|
|
6101
|
+
running: this.runningTasks.size,
|
|
6102
|
+
finished: this.successTasks.size,
|
|
6103
|
+
failed: this.failedTasks.size,
|
|
6104
|
+
totalSubmitted: this.totalSubmitted,
|
|
6105
|
+
isRunning: this.started && !this.isShutdown
|
|
6106
|
+
};
|
|
6107
|
+
}
|
|
6108
|
+
clear() {
|
|
6109
|
+
const cleared = this.finishedResults.length;
|
|
6110
|
+
this.finishedResults = [];
|
|
6111
|
+
this.successTasks.clear();
|
|
6112
|
+
this.failedTasks.clear();
|
|
6113
|
+
if (cleared > 0) {
|
|
6114
|
+
logger.debug(`Cleared ${cleared} execution results`);
|
|
6115
|
+
}
|
|
6116
|
+
}
|
|
6117
|
+
// ---- Private Methods ----
|
|
6118
|
+
async _executeToolcall(toolcall) {
|
|
6119
|
+
if (this.successTasks.has(toolcall.idempotencyKey) || this.failedTasks.has(toolcall.idempotencyKey)) {
|
|
6120
|
+
logger.debug(`Tool call already executed: ${toolcall.toolName}, using cached result`);
|
|
6121
|
+
const result = this.successTasks.get(toolcall.idempotencyKey) || this.failedTasks.get(toolcall.idempotencyKey);
|
|
6122
|
+
if (result) {
|
|
6123
|
+
this.finishedResults.push(result);
|
|
6124
|
+
}
|
|
6125
|
+
return;
|
|
6126
|
+
}
|
|
6127
|
+
if (!toolcall.isSuccess) {
|
|
6128
|
+
logger.warn(`Tool call extraction failed: ${toolcall.toolName}, reason: ${toolcall.failedReason || "Unknown"}`);
|
|
6129
|
+
const result = createExecutedToolcall(
|
|
6130
|
+
toolcall,
|
|
6131
|
+
false,
|
|
6132
|
+
`Tool extraction failed: ${toolcall.failedReason || "Unknown"}`
|
|
6133
|
+
);
|
|
6134
|
+
this.finishedResults.push(result);
|
|
6135
|
+
this.totalFailed++;
|
|
6136
|
+
this.failedTasks.set(toolcall.idempotencyKey, result);
|
|
6137
|
+
return;
|
|
6138
|
+
}
|
|
6139
|
+
while (this.semaphore.available <= 0) {
|
|
6140
|
+
await new Promise((resolve5) => setTimeout(resolve5, 100));
|
|
6141
|
+
}
|
|
6142
|
+
this.semaphore.available--;
|
|
6143
|
+
try {
|
|
6144
|
+
const previousExecutor = _currentExecutor;
|
|
6145
|
+
_currentExecutor = this;
|
|
6146
|
+
try {
|
|
6147
|
+
logger.debug(`Starting execution: ${toolcall.toolName}(${JSON.stringify(toolcall.toolArguments)})`);
|
|
6148
|
+
const executedToolcall = await _executeSingleTool(toolcall, this.toolStores);
|
|
6149
|
+
if (executedToolcall.isSuccess) {
|
|
6150
|
+
logger.debug(`Execution successful: ${executedToolcall.metadata.toolName}`);
|
|
6151
|
+
this.totalFinished++;
|
|
6152
|
+
this.successTasks.set(toolcall.idempotencyKey, executedToolcall);
|
|
6153
|
+
} else {
|
|
6154
|
+
logger.warn(
|
|
6155
|
+
`Execution failed: ${executedToolcall.metadata.toolName}, status: ${executedToolcall.isSuccess}, reason: ${executedToolcall.result}`
|
|
6156
|
+
);
|
|
6157
|
+
this.totalFailed++;
|
|
6158
|
+
this.failedTasks.set(toolcall.idempotencyKey, executedToolcall);
|
|
6159
|
+
}
|
|
6160
|
+
this.finishedResults.push(executedToolcall);
|
|
6161
|
+
} finally {
|
|
6162
|
+
_currentExecutor = previousExecutor;
|
|
6163
|
+
}
|
|
6164
|
+
} catch (error) {
|
|
6165
|
+
logger.error(`Exception executing tool ${toolcall.toolName}: ${error}`);
|
|
6166
|
+
const result = createExecutedToolcall(
|
|
6167
|
+
toolcall,
|
|
6168
|
+
false,
|
|
6169
|
+
`Execution exception: ${error instanceof Error ? error.message : String(error)}`
|
|
6170
|
+
);
|
|
6171
|
+
this.finishedResults.push(result);
|
|
6172
|
+
this.failedTasks.set(toolcall.idempotencyKey, result);
|
|
6173
|
+
this.totalFailed++;
|
|
6174
|
+
} finally {
|
|
6175
|
+
this.semaphore.available++;
|
|
6176
|
+
}
|
|
6177
|
+
}
|
|
6178
|
+
// ---- Background Process Management ----
|
|
6179
|
+
registerBackgroundProcess(process2, command, cwd) {
|
|
6180
|
+
const processId = uuidv42().slice(0, 8);
|
|
6181
|
+
this.backgroundProcesses.set(processId, {
|
|
6182
|
+
process: process2,
|
|
6183
|
+
command,
|
|
6184
|
+
cwd,
|
|
6185
|
+
pid: process2.pid || 0,
|
|
6186
|
+
status: "running"
|
|
6187
|
+
});
|
|
6188
|
+
logger.debug(`Registered background process: ${processId}, PID: ${process2.pid}, Command: ${command}`);
|
|
6189
|
+
return processId;
|
|
6190
|
+
}
|
|
6191
|
+
listBackgroundProcesses() {
|
|
6192
|
+
if (this.backgroundProcesses.size === 0) {
|
|
6193
|
+
return "No running background processes";
|
|
6194
|
+
}
|
|
6195
|
+
const lines = [`Total ${this.backgroundProcesses.size} background processes:
|
|
6196
|
+
`];
|
|
6197
|
+
for (const [processId, info] of this.backgroundProcesses) {
|
|
6198
|
+
const status = info.process.exitCode !== null ? "completed" : "running";
|
|
6199
|
+
lines.push(
|
|
6200
|
+
`- Process ID: ${processId}
|
|
6201
|
+
PID: ${info.pid}
|
|
6202
|
+
Command: ${info.command}
|
|
6203
|
+
Working Dir: ${info.cwd}
|
|
6204
|
+
Status: ${status}`
|
|
6205
|
+
);
|
|
6206
|
+
if (info.process.exitCode !== null) {
|
|
6207
|
+
lines.push(` Exit Code: ${info.process.exitCode}`);
|
|
6208
|
+
}
|
|
6209
|
+
}
|
|
6210
|
+
return lines.join("\n");
|
|
6211
|
+
}
|
|
6212
|
+
async stopBackgroundProcess(processId, force = false) {
|
|
6213
|
+
if (!this.backgroundProcesses.has(processId)) {
|
|
6214
|
+
return `Process ${processId} not found`;
|
|
6215
|
+
}
|
|
6216
|
+
const info = this.backgroundProcesses.get(processId);
|
|
6217
|
+
const process2 = info.process;
|
|
6218
|
+
if (process2.exitCode !== null) {
|
|
6219
|
+
return `Process ${processId} already terminated (exit code: ${process2.exitCode})`;
|
|
6220
|
+
}
|
|
6221
|
+
try {
|
|
6222
|
+
if (force) {
|
|
6223
|
+
process2.kill("SIGKILL");
|
|
6224
|
+
logger.info(`Force killed process ${process2.pid} (Process ID: ${processId})`);
|
|
6225
|
+
} else {
|
|
6226
|
+
process2.kill("SIGTERM");
|
|
6227
|
+
logger.info(`Sent SIGTERM to process ${process2.pid} (Process ID: ${processId})`);
|
|
6228
|
+
}
|
|
6229
|
+
await new Promise((resolve5) => {
|
|
6230
|
+
const timeout = setTimeout(() => {
|
|
6231
|
+
if (process2.exitCode === null && !force) {
|
|
6232
|
+
process2.kill("SIGKILL");
|
|
6233
|
+
logger.warn(`Process ${process2.pid} did not respond to SIGTERM, force killed`);
|
|
6234
|
+
}
|
|
6235
|
+
resolve5();
|
|
6236
|
+
}, 5e3);
|
|
6237
|
+
process2.on("exit", () => {
|
|
6238
|
+
clearTimeout(timeout);
|
|
6239
|
+
resolve5();
|
|
6240
|
+
});
|
|
6241
|
+
});
|
|
6242
|
+
info.status = "stopped";
|
|
6243
|
+
return `Process ${processId} stopped`;
|
|
6244
|
+
} catch (error) {
|
|
6245
|
+
logger.error(`Error stopping process ${processId}: ${error}`);
|
|
6246
|
+
return `Failed to stop process ${processId}: ${error}`;
|
|
6247
|
+
}
|
|
6248
|
+
}
|
|
6249
|
+
async cleanupBackgroundProcesses() {
|
|
6250
|
+
if (this.backgroundProcesses.size === 0) {
|
|
6251
|
+
return "No background processes to clean up";
|
|
6252
|
+
}
|
|
6253
|
+
const total = this.backgroundProcesses.size;
|
|
6254
|
+
let cleaned = 0;
|
|
6255
|
+
const errors = [];
|
|
6256
|
+
for (const processId of Array.from(this.backgroundProcesses.keys())) {
|
|
6257
|
+
const info = this.backgroundProcesses.get(processId);
|
|
6258
|
+
const process2 = info.process;
|
|
6259
|
+
if (process2.exitCode === null) {
|
|
6260
|
+
try {
|
|
6261
|
+
process2.kill("SIGTERM");
|
|
6262
|
+
await new Promise((resolve5) => {
|
|
6263
|
+
const timeout = setTimeout(() => {
|
|
6264
|
+
if (process2.exitCode === null) {
|
|
6265
|
+
process2.kill("SIGKILL");
|
|
6266
|
+
}
|
|
6267
|
+
resolve5();
|
|
6268
|
+
}, 3e3);
|
|
6269
|
+
process2.on("exit", () => {
|
|
6270
|
+
clearTimeout(timeout);
|
|
6271
|
+
resolve5();
|
|
6272
|
+
});
|
|
6273
|
+
});
|
|
6274
|
+
} catch (error) {
|
|
6275
|
+
errors.push(`Process ${processId}: ${error}`);
|
|
6276
|
+
continue;
|
|
6277
|
+
}
|
|
6278
|
+
}
|
|
6279
|
+
this.backgroundProcesses.delete(processId);
|
|
6280
|
+
cleaned++;
|
|
6281
|
+
}
|
|
6282
|
+
if (errors.length > 0) {
|
|
6283
|
+
return `Cleaned ${cleaned}/${total} processes. Errors:
|
|
6284
|
+
${errors.join("\n")}`;
|
|
6285
|
+
}
|
|
6286
|
+
return `Successfully cleaned ${cleaned} background processes`;
|
|
6287
|
+
}
|
|
6288
|
+
};
|
|
6289
|
+
|
|
6290
|
+
// src/runtime/state/store.ts
|
|
6291
|
+
function createStateRecord(sessionId, agentName, type, data) {
|
|
6292
|
+
return {
|
|
6293
|
+
sessionId,
|
|
6294
|
+
agentName,
|
|
6295
|
+
timestamp: Date.now() / 1e3,
|
|
6296
|
+
type,
|
|
6297
|
+
data
|
|
6298
|
+
};
|
|
6299
|
+
}
|
|
6300
|
+
|
|
6301
|
+
// src/runtime/state/jsonlStore.ts
|
|
6302
|
+
import * as fs13 from "fs";
|
|
6303
|
+
import * as path9 from "path";
|
|
6304
|
+
import * as os from "os";
|
|
6305
|
+
var JsonlAgentStateStore = class {
|
|
6306
|
+
storageDir;
|
|
6307
|
+
maxSessions;
|
|
6308
|
+
sessionFileMap = /* @__PURE__ */ new Map();
|
|
6309
|
+
/**
|
|
6310
|
+
* Create a new JSONL state store
|
|
6311
|
+
*
|
|
6312
|
+
* @param storageDir - Directory to store JSONL files. Defaults to ~/.evolt/state/
|
|
6313
|
+
* @param maxSessions - Maximum number of session files to keep. Older sessions are automatically cleaned up.
|
|
6314
|
+
*/
|
|
6315
|
+
constructor(storageDir, maxSessions = 3) {
|
|
6316
|
+
this.storageDir = storageDir || path9.join(os.homedir(), ".evolt", "state");
|
|
6317
|
+
this.maxSessions = maxSessions;
|
|
6318
|
+
if (!fs13.existsSync(this.storageDir)) {
|
|
6319
|
+
fs13.mkdirSync(this.storageDir, { recursive: true });
|
|
6320
|
+
}
|
|
6321
|
+
this._loadSessionMap();
|
|
6322
|
+
}
|
|
6323
|
+
/**
|
|
6324
|
+
* Load existing session_id to file mappings from disk
|
|
6325
|
+
*/
|
|
6326
|
+
_loadSessionMap() {
|
|
6327
|
+
try {
|
|
6328
|
+
const files = fs13.readdirSync(this.storageDir);
|
|
6329
|
+
for (const file of files) {
|
|
6330
|
+
if (file.endsWith(".jsonl")) {
|
|
6331
|
+
const sessionId = path9.basename(file, ".jsonl");
|
|
6332
|
+
this.sessionFileMap.set(sessionId, path9.join(this.storageDir, file));
|
|
6333
|
+
}
|
|
6334
|
+
}
|
|
6335
|
+
} catch (error) {
|
|
6336
|
+
logger.warn(`Failed to load session map: ${error}`);
|
|
6337
|
+
}
|
|
6338
|
+
}
|
|
6339
|
+
/**
|
|
6340
|
+
* Get existing file path for session or create a new one
|
|
6341
|
+
*/
|
|
6342
|
+
_getOrCreateFilePath(sessionId) {
|
|
6343
|
+
if (this.sessionFileMap.has(sessionId)) {
|
|
6344
|
+
return this.sessionFileMap.get(sessionId);
|
|
6345
|
+
}
|
|
6346
|
+
const filePath = path9.join(this.storageDir, `${sessionId}.jsonl`);
|
|
6347
|
+
this.sessionFileMap.set(sessionId, filePath);
|
|
6348
|
+
logger.debug(`Created new session file: ${filePath} for session=${sessionId}`);
|
|
6349
|
+
this._cleanupOldSessions();
|
|
6350
|
+
return filePath;
|
|
6351
|
+
}
|
|
6352
|
+
/**
|
|
6353
|
+
* Cleanup old session files to maintain max_sessions limit
|
|
6354
|
+
*/
|
|
6355
|
+
_cleanupOldSessions() {
|
|
6356
|
+
try {
|
|
6357
|
+
const files = fs13.readdirSync(this.storageDir).filter((f) => f.endsWith(".jsonl")).map((f) => path9.join(this.storageDir, f));
|
|
6358
|
+
const filesWithTs = [];
|
|
6359
|
+
for (const filePath of files) {
|
|
6360
|
+
const timestamp = this._getLatestTimestamp(filePath);
|
|
6361
|
+
filesWithTs.push({ path: filePath, timestamp });
|
|
6362
|
+
}
|
|
6363
|
+
filesWithTs.sort((a, b) => b.timestamp - a.timestamp);
|
|
6364
|
+
let deleted = 0;
|
|
6365
|
+
for (let i = this.maxSessions; i < filesWithTs.length; i++) {
|
|
6366
|
+
const filePath = filesWithTs[i].path;
|
|
6367
|
+
try {
|
|
6368
|
+
const sessionId = path9.basename(filePath, ".jsonl");
|
|
6369
|
+
this.sessionFileMap.delete(sessionId);
|
|
6370
|
+
fs13.unlinkSync(filePath);
|
|
6371
|
+
deleted++;
|
|
6372
|
+
logger.debug(`Deleted old session file: ${filePath}`);
|
|
6373
|
+
} catch (error) {
|
|
6374
|
+
logger.warn(`Failed to delete ${filePath}: ${error}`);
|
|
6375
|
+
}
|
|
6376
|
+
}
|
|
6377
|
+
if (deleted > 0) {
|
|
6378
|
+
logger.info(`Cleaned up ${deleted} old session files`);
|
|
6379
|
+
}
|
|
6380
|
+
return deleted;
|
|
6381
|
+
} catch (error) {
|
|
6382
|
+
logger.error(`Failed to cleanup old sessions: ${error}`);
|
|
6383
|
+
return 0;
|
|
6384
|
+
}
|
|
6385
|
+
}
|
|
6386
|
+
/**
|
|
6387
|
+
* Get the latest record timestamp from a session file
|
|
6388
|
+
*/
|
|
6389
|
+
_getLatestTimestamp(filePath) {
|
|
6390
|
+
try {
|
|
6391
|
+
if (!fs13.existsSync(filePath)) {
|
|
6392
|
+
return 0;
|
|
6393
|
+
}
|
|
6394
|
+
const content = fs13.readFileSync(filePath, "utf-8");
|
|
6395
|
+
const lines = content.trim().split("\n").filter((l) => l.trim());
|
|
6396
|
+
if (lines.length === 0) {
|
|
6397
|
+
return 0;
|
|
6398
|
+
}
|
|
6399
|
+
const lastLine = lines[lines.length - 1];
|
|
6400
|
+
try {
|
|
6401
|
+
const record = JSON.parse(lastLine);
|
|
6402
|
+
return record.timestamp || 0;
|
|
6403
|
+
} catch {
|
|
6404
|
+
return 0;
|
|
6405
|
+
}
|
|
6406
|
+
} catch (error) {
|
|
6407
|
+
return 0;
|
|
6408
|
+
}
|
|
6409
|
+
}
|
|
6410
|
+
async append(sessionId, agentName, record) {
|
|
6411
|
+
const filePath = this._getOrCreateFilePath(sessionId);
|
|
6412
|
+
const fullRecord = {
|
|
6413
|
+
sessionId,
|
|
6414
|
+
agentName,
|
|
6415
|
+
...record
|
|
6416
|
+
};
|
|
6417
|
+
const line = JSON.stringify(fullRecord) + "\n";
|
|
6418
|
+
await fs13.promises.appendFile(filePath, line, "utf-8");
|
|
6419
|
+
logger.debug(`Appended record to session ${sessionId}: type=${record.type}`);
|
|
6420
|
+
}
|
|
6421
|
+
async getAll(sessionId, agentName) {
|
|
6422
|
+
const filePath = this.sessionFileMap.get(sessionId);
|
|
6423
|
+
if (!filePath || !fs13.existsSync(filePath)) {
|
|
6424
|
+
return [];
|
|
6425
|
+
}
|
|
6426
|
+
try {
|
|
6427
|
+
const content = await fs13.promises.readFile(filePath, "utf-8");
|
|
6428
|
+
const lines = content.trim().split("\n").filter((l) => l.trim());
|
|
6429
|
+
const records = [];
|
|
6430
|
+
for (const line of lines) {
|
|
6431
|
+
try {
|
|
6432
|
+
const record = JSON.parse(line);
|
|
6433
|
+
if (record.agentName === agentName) {
|
|
6434
|
+
records.push(record);
|
|
6435
|
+
}
|
|
6436
|
+
} catch {
|
|
6437
|
+
logger.warn(`Failed to parse record line: ${line}`);
|
|
6438
|
+
}
|
|
6439
|
+
}
|
|
6440
|
+
records.sort((a, b) => a.timestamp - b.timestamp);
|
|
6441
|
+
return records;
|
|
6442
|
+
} catch (error) {
|
|
6443
|
+
logger.error(`Failed to read session file: ${error}`);
|
|
6444
|
+
return [];
|
|
6445
|
+
}
|
|
6446
|
+
}
|
|
6447
|
+
async clear(sessionId, agentName) {
|
|
6448
|
+
const filePath = this.sessionFileMap.get(sessionId);
|
|
6449
|
+
if (!filePath || !fs13.existsSync(filePath)) {
|
|
6450
|
+
return;
|
|
6451
|
+
}
|
|
6452
|
+
try {
|
|
6453
|
+
const content = await fs13.promises.readFile(filePath, "utf-8");
|
|
6454
|
+
const lines = content.trim().split("\n").filter((l) => l.trim());
|
|
6455
|
+
const remainingLines = [];
|
|
6456
|
+
for (const line of lines) {
|
|
6457
|
+
try {
|
|
6458
|
+
const record = JSON.parse(line);
|
|
6459
|
+
if (record.agentName !== agentName) {
|
|
6460
|
+
remainingLines.push(line);
|
|
6461
|
+
}
|
|
6462
|
+
} catch {
|
|
6463
|
+
remainingLines.push(line);
|
|
6464
|
+
}
|
|
6465
|
+
}
|
|
6466
|
+
await fs13.promises.writeFile(filePath, remainingLines.join("\n") + "\n", "utf-8");
|
|
6467
|
+
logger.debug(`Cleared records for agent ${agentName} in session ${sessionId}`);
|
|
6468
|
+
} catch (error) {
|
|
6469
|
+
logger.error(`Failed to clear records: ${error}`);
|
|
6470
|
+
}
|
|
6471
|
+
}
|
|
6472
|
+
exportJson(sessionId, agentName, filename) {
|
|
6473
|
+
const filePath = this.sessionFileMap.get(sessionId);
|
|
6474
|
+
if (!filePath || !fs13.existsSync(filePath)) {
|
|
6475
|
+
fs13.writeFileSync(filename, "[]", "utf-8");
|
|
6476
|
+
return;
|
|
6477
|
+
}
|
|
6478
|
+
const content = fs13.readFileSync(filePath, "utf-8");
|
|
6479
|
+
const lines = content.trim().split("\n").filter((l) => l.trim());
|
|
6480
|
+
const records = [];
|
|
6481
|
+
for (const line of lines) {
|
|
6482
|
+
try {
|
|
6483
|
+
const record = JSON.parse(line);
|
|
6484
|
+
if (record.agentName === agentName) {
|
|
6485
|
+
records.push(record);
|
|
6486
|
+
}
|
|
6487
|
+
} catch {
|
|
6488
|
+
}
|
|
6489
|
+
}
|
|
6490
|
+
fs13.writeFileSync(filename, JSON.stringify(records, null, 2), "utf-8");
|
|
6491
|
+
logger.info(`Exported ${records.length} records to ${filename}`);
|
|
6492
|
+
}
|
|
6493
|
+
importJson(filename) {
|
|
6494
|
+
if (!fs13.existsSync(filename)) {
|
|
6495
|
+
logger.warn(`Import file not found: ${filename}`);
|
|
6496
|
+
return;
|
|
6497
|
+
}
|
|
6498
|
+
const content = fs13.readFileSync(filename, "utf-8");
|
|
6499
|
+
const records = JSON.parse(content);
|
|
6500
|
+
const bySession = /* @__PURE__ */ new Map();
|
|
6501
|
+
for (const record of records) {
|
|
6502
|
+
if (!bySession.has(record.sessionId)) {
|
|
6503
|
+
bySession.set(record.sessionId, []);
|
|
6504
|
+
}
|
|
6505
|
+
bySession.get(record.sessionId).push(record);
|
|
6506
|
+
}
|
|
6507
|
+
for (const [sessionId, sessionRecords] of bySession) {
|
|
6508
|
+
const filePath = this._getOrCreateFilePath(sessionId);
|
|
6509
|
+
const lines = sessionRecords.map((r) => JSON.stringify(r));
|
|
6510
|
+
fs13.appendFileSync(filePath, lines.join("\n") + "\n", "utf-8");
|
|
6511
|
+
}
|
|
6512
|
+
logger.info(`Imported ${records.length} records from ${filename}`);
|
|
6513
|
+
}
|
|
6514
|
+
async deleteLastN(sessionId, agentName, n) {
|
|
6515
|
+
const filePath = this.sessionFileMap.get(sessionId);
|
|
6516
|
+
if (!filePath || !fs13.existsSync(filePath)) {
|
|
6517
|
+
return 0;
|
|
6518
|
+
}
|
|
6519
|
+
try {
|
|
6520
|
+
const content = await fs13.promises.readFile(filePath, "utf-8");
|
|
6521
|
+
const lines = content.trim().split("\n").filter((l) => l.trim());
|
|
6522
|
+
const agentRecordIndices = [];
|
|
6523
|
+
const parsedRecords = [];
|
|
6524
|
+
for (let i = 0; i < lines.length; i++) {
|
|
6525
|
+
const line = lines[i];
|
|
6526
|
+
try {
|
|
6527
|
+
const record = JSON.parse(line);
|
|
6528
|
+
parsedRecords.push({ line, record });
|
|
6529
|
+
if (record.agentName === agentName) {
|
|
6530
|
+
agentRecordIndices.push(i);
|
|
6531
|
+
}
|
|
6532
|
+
} catch {
|
|
6533
|
+
parsedRecords.push({ line, record: null });
|
|
6534
|
+
}
|
|
6535
|
+
}
|
|
6536
|
+
const indicesToDelete = new Set(agentRecordIndices.slice(-n));
|
|
6537
|
+
const deleted = indicesToDelete.size;
|
|
6538
|
+
const remainingLines = parsedRecords.filter((_, i) => !indicesToDelete.has(i)).map((r) => r.line);
|
|
6539
|
+
await fs13.promises.writeFile(filePath, remainingLines.join("\n") + "\n", "utf-8");
|
|
6540
|
+
logger.debug(`Deleted ${deleted} records for agent ${agentName} in session ${sessionId}`);
|
|
6541
|
+
return deleted;
|
|
6542
|
+
} catch (error) {
|
|
6543
|
+
logger.error(`Failed to delete records: ${error}`);
|
|
6544
|
+
return 0;
|
|
6545
|
+
}
|
|
6546
|
+
}
|
|
6547
|
+
async sessionExists(sessionId) {
|
|
6548
|
+
const filePath = this.sessionFileMap.get(sessionId);
|
|
6549
|
+
return filePath !== void 0 && fs13.existsSync(filePath);
|
|
6550
|
+
}
|
|
6551
|
+
listSessions() {
|
|
6552
|
+
const sessions = [];
|
|
6553
|
+
for (const [sessionId, filePath] of this.sessionFileMap) {
|
|
6554
|
+
if (!fs13.existsSync(filePath)) {
|
|
6555
|
+
continue;
|
|
6556
|
+
}
|
|
6557
|
+
try {
|
|
6558
|
+
const content = fs13.readFileSync(filePath, "utf-8");
|
|
6559
|
+
const lines = content.trim().split("\n").filter((l) => l.trim());
|
|
6560
|
+
if (lines.length === 0) {
|
|
6561
|
+
continue;
|
|
6562
|
+
}
|
|
6563
|
+
let firstInstruction;
|
|
6564
|
+
const agents = /* @__PURE__ */ new Set();
|
|
6565
|
+
let timestamp = 0;
|
|
6566
|
+
for (const line of lines) {
|
|
6567
|
+
try {
|
|
6568
|
+
const record = JSON.parse(line);
|
|
6569
|
+
agents.add(record.agentName);
|
|
6570
|
+
if (timestamp === 0) {
|
|
6571
|
+
timestamp = record.timestamp;
|
|
6572
|
+
}
|
|
6573
|
+
if (!firstInstruction && record.type === "instruction") {
|
|
6574
|
+
firstInstruction = record.data.content?.slice(0, 100);
|
|
6575
|
+
}
|
|
6576
|
+
} catch {
|
|
6577
|
+
}
|
|
6578
|
+
}
|
|
6579
|
+
sessions.push({
|
|
6580
|
+
sessionId,
|
|
6581
|
+
timestamp,
|
|
6582
|
+
firstInstruction,
|
|
6583
|
+
agents: Array.from(agents)
|
|
6584
|
+
});
|
|
6585
|
+
} catch (error) {
|
|
6586
|
+
logger.warn(`Failed to read session file ${filePath}: ${error}`);
|
|
6587
|
+
}
|
|
6588
|
+
}
|
|
6589
|
+
sessions.sort((a, b) => b.timestamp - a.timestamp);
|
|
6590
|
+
return sessions;
|
|
6591
|
+
}
|
|
6592
|
+
async getAgentsInSession(sessionId) {
|
|
6593
|
+
const filePath = this.sessionFileMap.get(sessionId);
|
|
6594
|
+
if (!filePath || !fs13.existsSync(filePath)) {
|
|
6595
|
+
return [];
|
|
6596
|
+
}
|
|
6597
|
+
try {
|
|
6598
|
+
const content = await fs13.promises.readFile(filePath, "utf-8");
|
|
6599
|
+
const lines = content.trim().split("\n").filter((l) => l.trim());
|
|
6600
|
+
const agents = /* @__PURE__ */ new Set();
|
|
6601
|
+
for (const line of lines) {
|
|
6602
|
+
try {
|
|
6603
|
+
const record = JSON.parse(line);
|
|
6604
|
+
agents.add(record.agentName);
|
|
6605
|
+
} catch {
|
|
6606
|
+
}
|
|
6607
|
+
}
|
|
6608
|
+
return Array.from(agents);
|
|
6609
|
+
} catch (error) {
|
|
6610
|
+
logger.error(`Failed to read session file: ${error}`);
|
|
6611
|
+
return [];
|
|
6612
|
+
}
|
|
6613
|
+
}
|
|
6614
|
+
};
|
|
6615
|
+
|
|
6616
|
+
// src/runtime/state/context.ts
|
|
6617
|
+
import { v4 as uuidv43 } from "uuid";
|
|
6618
|
+
var _sessionId = null;
|
|
6619
|
+
var _sessionIdExplicit = false;
|
|
6620
|
+
var _skipExecutorRestore = false;
|
|
6621
|
+
function useSessionId(sessionId, options) {
|
|
6622
|
+
if (_sessionId !== null) {
|
|
6623
|
+
throw new Error(
|
|
6624
|
+
`Session ID already set to '${_sessionId}'. Cannot change to '${sessionId}'. Call resetSessionId() first.`
|
|
6625
|
+
);
|
|
6626
|
+
}
|
|
6627
|
+
_sessionId = sessionId;
|
|
6628
|
+
_sessionIdExplicit = true;
|
|
6629
|
+
_skipExecutorRestore = options?.skipExecutorRestore ?? false;
|
|
6630
|
+
}
|
|
6631
|
+
function getOrCreateSessionId() {
|
|
6632
|
+
if (_sessionId === null) {
|
|
6633
|
+
_sessionId = uuidv43();
|
|
6634
|
+
_sessionIdExplicit = false;
|
|
6635
|
+
}
|
|
6636
|
+
return _sessionId;
|
|
6637
|
+
}
|
|
6638
|
+
function getSessionId() {
|
|
6639
|
+
return _sessionId;
|
|
6640
|
+
}
|
|
6641
|
+
function isNewSession() {
|
|
6642
|
+
return !_sessionIdExplicit;
|
|
6643
|
+
}
|
|
6644
|
+
function resetSessionId() {
|
|
6645
|
+
_sessionId = null;
|
|
6646
|
+
_sessionIdExplicit = false;
|
|
6647
|
+
_skipExecutorRestore = false;
|
|
6648
|
+
}
|
|
6649
|
+
function shouldSkipExecutorRestore() {
|
|
6650
|
+
return _skipExecutorRestore;
|
|
6651
|
+
}
|
|
6652
|
+
var _globalStore = null;
|
|
6653
|
+
function enableStateStore(store, options) {
|
|
6654
|
+
if (store !== void 0) {
|
|
6655
|
+
_globalStore = store;
|
|
6656
|
+
} else if (options?.backend === "jsonl" || !options?.backend) {
|
|
6657
|
+
_globalStore = new JsonlAgentStateStore(options?.storageDir);
|
|
6658
|
+
} else {
|
|
6659
|
+
_globalStore = null;
|
|
6660
|
+
}
|
|
6661
|
+
return _globalStore;
|
|
6662
|
+
}
|
|
6663
|
+
function getGlobalStore() {
|
|
6664
|
+
return _globalStore;
|
|
6665
|
+
}
|
|
6666
|
+
function isPersistenceEnabled() {
|
|
6667
|
+
return _globalStore !== null;
|
|
6668
|
+
}
|
|
6669
|
+
var _agentName = null;
|
|
6670
|
+
function setAgentName(agentName) {
|
|
6671
|
+
_agentName = agentName;
|
|
6672
|
+
}
|
|
6673
|
+
function getAgentName() {
|
|
6674
|
+
return _agentName;
|
|
6675
|
+
}
|
|
6676
|
+
function resetAgentName() {
|
|
6677
|
+
_agentName = null;
|
|
6678
|
+
}
|
|
6679
|
+
|
|
6680
|
+
// src/runtime/state/decorator.ts
|
|
6681
|
+
function persistentState(recordType) {
|
|
6682
|
+
return function(target, propertyKey, descriptor) {
|
|
6683
|
+
const originalMethod = descriptor.value;
|
|
6684
|
+
descriptor.value = async function(...args) {
|
|
6685
|
+
const result = await originalMethod.apply(this, args);
|
|
6686
|
+
const sessionId = getOrCreateSessionId();
|
|
6687
|
+
const agentName = getAgentName();
|
|
6688
|
+
const store = getGlobalStore();
|
|
6689
|
+
if (store !== null && agentName) {
|
|
6690
|
+
let currentType = recordType;
|
|
6691
|
+
const data = _serializeForPersistence(recordType, args, result);
|
|
6692
|
+
try {
|
|
6693
|
+
await store.append(sessionId, agentName, {
|
|
6694
|
+
type: currentType,
|
|
6695
|
+
data,
|
|
6696
|
+
timestamp: Date.now() / 1e3
|
|
6697
|
+
});
|
|
6698
|
+
logger.debug(`Persisted ${currentType} record for session=${sessionId}, agent=${agentName}`);
|
|
6699
|
+
} catch (error) {
|
|
6700
|
+
logger.error(`Failed to persist state: ${error}`);
|
|
6701
|
+
}
|
|
6702
|
+
}
|
|
6703
|
+
return result;
|
|
6704
|
+
};
|
|
6705
|
+
return descriptor;
|
|
6706
|
+
};
|
|
6707
|
+
}
|
|
6708
|
+
function _serializeForPersistence(recordType, args, result) {
|
|
6709
|
+
if (recordType === "message") {
|
|
6710
|
+
const message = args[0];
|
|
6711
|
+
if (message !== void 0) {
|
|
6712
|
+
if (typeof message.toDict === "function") {
|
|
6713
|
+
return { message: message.toDict() };
|
|
6714
|
+
} else if (typeof message.toObject === "function") {
|
|
6715
|
+
return { message: message.toObject() };
|
|
6716
|
+
} else if (typeof message === "object") {
|
|
6717
|
+
return { message };
|
|
6718
|
+
}
|
|
6719
|
+
}
|
|
6720
|
+
return { args: String(args) };
|
|
6721
|
+
} else if (recordType === "tool_call") {
|
|
6722
|
+
const toolcalls = [];
|
|
6723
|
+
const toolcallsArg = args[0];
|
|
6724
|
+
if (toolcallsArg !== void 0 && Symbol.iterator in Object(toolcallsArg)) {
|
|
6725
|
+
for (const tc of toolcallsArg) {
|
|
6726
|
+
if (typeof tc.toDict === "function") {
|
|
6727
|
+
toolcalls.push(tc.toDict());
|
|
6728
|
+
} else if (typeof tc === "object") {
|
|
6729
|
+
toolcalls.push(tc);
|
|
6730
|
+
}
|
|
6731
|
+
}
|
|
6732
|
+
}
|
|
6733
|
+
return { toolcalls };
|
|
6734
|
+
}
|
|
6735
|
+
return {};
|
|
6736
|
+
}
|
|
6737
|
+
function agentAutoRestore(target, propertyKey, descriptor) {
|
|
6738
|
+
const originalMethod = descriptor.value;
|
|
6739
|
+
descriptor.value = async function(instruction, images) {
|
|
6740
|
+
const inst = instruction || "";
|
|
6741
|
+
const sessionId = getOrCreateSessionId();
|
|
6742
|
+
const agentName = this.name;
|
|
6743
|
+
setAgentName(agentName);
|
|
6744
|
+
try {
|
|
6745
|
+
const result = await originalMethod.call(this, inst, images);
|
|
6746
|
+
const store = getGlobalStore();
|
|
6747
|
+
if (store !== null && typeof result === "string") {
|
|
6748
|
+
try {
|
|
6749
|
+
await store.append(sessionId, agentName, {
|
|
6750
|
+
type: "completion",
|
|
6751
|
+
data: { response: result },
|
|
6752
|
+
timestamp: Date.now() / 1e3
|
|
6753
|
+
});
|
|
6754
|
+
logger.debug(`Recorded completion for session=${sessionId}, agent=${agentName}`);
|
|
6755
|
+
} catch (error) {
|
|
6756
|
+
logger.error(`Failed to record completion: ${error}`);
|
|
6757
|
+
}
|
|
6758
|
+
}
|
|
6759
|
+
return result;
|
|
6760
|
+
} catch (error) {
|
|
6761
|
+
logger.error(`Agent ${agentName} run error: ${error}`);
|
|
6762
|
+
throw error;
|
|
6763
|
+
} finally {
|
|
6764
|
+
resetAgentName();
|
|
6765
|
+
if (this.autoShutdownExecutor && this.executor) {
|
|
6766
|
+
await this.executor.shutdown({ wait: true });
|
|
6767
|
+
}
|
|
6768
|
+
}
|
|
6769
|
+
};
|
|
6770
|
+
return descriptor;
|
|
6771
|
+
}
|
|
4771
6772
|
export {
|
|
4772
6773
|
Agent,
|
|
4773
6774
|
ApiTool,
|
|
4774
6775
|
BaseEnvironment,
|
|
6776
|
+
BaseOrchestrator,
|
|
4775
6777
|
CodingEnvironment,
|
|
6778
|
+
CodingOrchestrator,
|
|
4776
6779
|
CommandLineTool,
|
|
4777
6780
|
DEFAULT_CONFIG,
|
|
4778
6781
|
DEFAULT_SETTINGS,
|
|
4779
6782
|
ENV_VARS,
|
|
4780
6783
|
ERROR_MESSAGES,
|
|
6784
|
+
Environment,
|
|
4781
6785
|
EvoltError,
|
|
4782
6786
|
ExtendStateMachineTool,
|
|
6787
|
+
Feedback,
|
|
4783
6788
|
FileEditor,
|
|
4784
6789
|
FunctionCallingStore,
|
|
4785
6790
|
GitTool,
|
|
6791
|
+
ImageTool,
|
|
6792
|
+
InstructionType,
|
|
6793
|
+
JsonlAgentStateStore,
|
|
4786
6794
|
LOG_LEVELS,
|
|
6795
|
+
LocalToolExecutor,
|
|
4787
6796
|
MESSAGE_ROLES,
|
|
4788
6797
|
Message,
|
|
4789
6798
|
MessageHistory,
|
|
4790
6799
|
Model,
|
|
4791
6800
|
ModelError,
|
|
6801
|
+
OUTPUT_FORMAT_PROMPT,
|
|
6802
|
+
PatchTool,
|
|
6803
|
+
REFLECT_OUTPUT_FORMAT_PROMPT,
|
|
4792
6804
|
ReflectTool,
|
|
6805
|
+
ReflexionOrchestrator,
|
|
4793
6806
|
Reply2HumanTool,
|
|
4794
6807
|
SKILLS_DIR,
|
|
6808
|
+
SYSTEM_TOOLS_PROMPT,
|
|
4795
6809
|
SkillsTool,
|
|
4796
6810
|
SystemToolStore,
|
|
6811
|
+
TOOLS_PROMPT,
|
|
4797
6812
|
TOOL_CALL_TYPES,
|
|
4798
6813
|
TOOL_CONSTANTS,
|
|
4799
6814
|
ThinkTool,
|
|
@@ -4803,13 +6818,28 @@ export {
|
|
|
4803
6818
|
UserToolStore,
|
|
4804
6819
|
WORKSPACE_DIR,
|
|
4805
6820
|
WriteUIDesignDocument,
|
|
6821
|
+
_executeSingleTool,
|
|
6822
|
+
agentAutoRestore,
|
|
6823
|
+
areadImage,
|
|
6824
|
+
createExecutedToolcall,
|
|
6825
|
+
createGeneratedToolcall,
|
|
6826
|
+
createStateRecord,
|
|
6827
|
+
deprecated,
|
|
6828
|
+
enableStateStore,
|
|
4806
6829
|
ensureDir,
|
|
6830
|
+
executeToolsParallel,
|
|
6831
|
+
executeToolsSequential,
|
|
4807
6832
|
fileExists,
|
|
6833
|
+
getAgentName,
|
|
4808
6834
|
getCacheDir,
|
|
4809
6835
|
getConfigDir,
|
|
4810
6836
|
getConfigPath2 as getConfigPath,
|
|
6837
|
+
getCurrentExecutor,
|
|
4811
6838
|
getFileExtension,
|
|
6839
|
+
getGlobalStore,
|
|
4812
6840
|
getLogsDir,
|
|
6841
|
+
getOrCreateSessionId,
|
|
6842
|
+
getSessionId,
|
|
4813
6843
|
getSettings,
|
|
4814
6844
|
getSkillsDir,
|
|
4815
6845
|
getTempFilePath,
|
|
@@ -4817,12 +6847,23 @@ export {
|
|
|
4817
6847
|
getWorkspacePath,
|
|
4818
6848
|
initializeSettings,
|
|
4819
6849
|
isInWorkspace,
|
|
6850
|
+
isNewSession,
|
|
6851
|
+
isPersistenceEnabled,
|
|
6852
|
+
isSupportedImageFile,
|
|
4820
6853
|
loadModelConfig,
|
|
4821
6854
|
logger_default as logger,
|
|
4822
6855
|
normalizePath,
|
|
6856
|
+
persistentState,
|
|
6857
|
+
readImage,
|
|
4823
6858
|
registerAgentAsTool,
|
|
6859
|
+
resetAgentName,
|
|
6860
|
+
resetSessionId,
|
|
6861
|
+
setAgentName,
|
|
6862
|
+
setCurrentExecutor,
|
|
4824
6863
|
settings,
|
|
6864
|
+
shouldSkipExecutorRestore,
|
|
4825
6865
|
tools,
|
|
4826
|
-
updateSettings
|
|
6866
|
+
updateSettings,
|
|
6867
|
+
useSessionId
|
|
4827
6868
|
};
|
|
4828
6869
|
//# sourceMappingURL=index.js.map
|