@lov3kaizen/agentsea-core 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +199 -85
- package/dist/index.mjs +193 -79
- package/package.json +2 -2
package/dist/index.d.mts
CHANGED
|
@@ -198,6 +198,8 @@ declare class AnthropicProvider implements LLMProvider {
|
|
|
198
198
|
streamResponse(messages: Message[], config: ProviderConfig): AsyncIterable<LLMStreamChunk>;
|
|
199
199
|
parseToolCalls(response: LLMResponse): ToolCall[];
|
|
200
200
|
private convertMessages;
|
|
201
|
+
private ensureToolUse;
|
|
202
|
+
private toolResultBlock;
|
|
201
203
|
}
|
|
202
204
|
|
|
203
205
|
declare class OpenAIProvider implements LLMProvider {
|
package/dist/index.d.ts
CHANGED
|
@@ -198,6 +198,8 @@ declare class AnthropicProvider implements LLMProvider {
|
|
|
198
198
|
streamResponse(messages: Message[], config: ProviderConfig): AsyncIterable<LLMStreamChunk>;
|
|
199
199
|
parseToolCalls(response: LLMResponse): ToolCall[];
|
|
200
200
|
private convertMessages;
|
|
201
|
+
private ensureToolUse;
|
|
202
|
+
private toolResultBlock;
|
|
201
203
|
}
|
|
202
204
|
|
|
203
205
|
declare class OpenAIProvider implements LLMProvider {
|
package/dist/index.js
CHANGED
|
@@ -743,7 +743,7 @@ var ToolRegistry = class {
|
|
|
743
743
|
* Sleep helper
|
|
744
744
|
*/
|
|
745
745
|
sleep(ms) {
|
|
746
|
-
return new Promise((
|
|
746
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
747
747
|
}
|
|
748
748
|
};
|
|
749
749
|
|
|
@@ -778,7 +778,49 @@ var calculatorTool = {
|
|
|
778
778
|
};
|
|
779
779
|
|
|
780
780
|
// src/tools/built-in/http-request.tool.ts
|
|
781
|
+
var import_promises = require("dns/promises");
|
|
782
|
+
var import_net = require("net");
|
|
781
783
|
var import_zod2 = require("zod");
|
|
784
|
+
function isPrivateIp(ip) {
|
|
785
|
+
const mapped = ip.match(/^::ffff:(\d+\.\d+\.\d+\.\d+)$/i);
|
|
786
|
+
const addr = mapped ? mapped[1] : ip;
|
|
787
|
+
if ((0, import_net.isIP)(addr) === 4) {
|
|
788
|
+
const [a, b] = addr.split(".").map(Number);
|
|
789
|
+
return a === 0 || // 0.0.0.0/8
|
|
790
|
+
a === 10 || // 10.0.0.0/8 (private)
|
|
791
|
+
a === 127 || // 127.0.0.0/8 (loopback)
|
|
792
|
+
a === 169 && b === 254 || // 169.254.0.0/16 (link-local incl. cloud metadata)
|
|
793
|
+
a === 172 && b >= 16 && b <= 31 || // 172.16.0.0/12 (private)
|
|
794
|
+
a === 192 && b === 168 || // 192.168.0.0/16 (private)
|
|
795
|
+
a === 100 && b >= 64 && b <= 127 || // 100.64.0.0/10 (CGNAT)
|
|
796
|
+
a >= 224;
|
|
797
|
+
}
|
|
798
|
+
const lower = ip.toLowerCase();
|
|
799
|
+
return lower === "::1" || // loopback
|
|
800
|
+
lower === "::" || // unspecified
|
|
801
|
+
lower.startsWith("fe80:") || // link-local
|
|
802
|
+
lower.startsWith("fc") || // unique-local fc00::/7
|
|
803
|
+
lower.startsWith("fd");
|
|
804
|
+
}
|
|
805
|
+
async function assertPublicUrl(rawUrl) {
|
|
806
|
+
let parsed;
|
|
807
|
+
try {
|
|
808
|
+
parsed = new URL(rawUrl);
|
|
809
|
+
} catch {
|
|
810
|
+
throw new Error("Invalid URL");
|
|
811
|
+
}
|
|
812
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
813
|
+
throw new Error(`Blocked URL scheme: ${parsed.protocol}`);
|
|
814
|
+
}
|
|
815
|
+
const host = parsed.hostname;
|
|
816
|
+
if (host === "localhost") {
|
|
817
|
+
throw new Error("Blocked request to localhost");
|
|
818
|
+
}
|
|
819
|
+
const candidates = (0, import_net.isIP)(host) ? [host] : (await (0, import_promises.lookup)(host, { all: true })).map((r) => r.address);
|
|
820
|
+
if (candidates.some(isPrivateIp)) {
|
|
821
|
+
throw new Error(`Blocked request to non-public address for host "${host}"`);
|
|
822
|
+
}
|
|
823
|
+
}
|
|
782
824
|
var httpRequestTool = {
|
|
783
825
|
name: "http_request",
|
|
784
826
|
description: "Make HTTP requests to external APIs. Supports GET, POST, PUT, DELETE methods.",
|
|
@@ -790,6 +832,7 @@ var httpRequestTool = {
|
|
|
790
832
|
timeout: import_zod2.z.number().optional().default(1e4).describe("Request timeout in milliseconds")
|
|
791
833
|
}),
|
|
792
834
|
execute: async (params) => {
|
|
835
|
+
await assertPublicUrl(params.url);
|
|
793
836
|
const controller = new AbortController();
|
|
794
837
|
const timeoutId = setTimeout(
|
|
795
838
|
() => controller.abort(),
|
|
@@ -844,8 +887,21 @@ var httpRequestTool = {
|
|
|
844
887
|
|
|
845
888
|
// src/tools/built-in/file-operations.tool.ts
|
|
846
889
|
var import_fs = require("fs");
|
|
847
|
-
var
|
|
890
|
+
var import_path2 = require("path");
|
|
848
891
|
var import_zod3 = require("zod");
|
|
892
|
+
|
|
893
|
+
// src/tools/built-in/path-guard.ts
|
|
894
|
+
var import_path = require("path");
|
|
895
|
+
function resolveWithinRoot(inputPath) {
|
|
896
|
+
const root = (0, import_path.resolve)(process.env.AGENTSEA_FILE_ROOT || process.cwd());
|
|
897
|
+
const resolved = (0, import_path.resolve)(root, inputPath);
|
|
898
|
+
if (resolved !== root && !resolved.startsWith(root + import_path.sep)) {
|
|
899
|
+
throw new Error(`Path escapes the allowed root directory: ${inputPath}`);
|
|
900
|
+
}
|
|
901
|
+
return resolved;
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
// src/tools/built-in/file-operations.tool.ts
|
|
849
905
|
var fileReadTool = {
|
|
850
906
|
name: "file_read",
|
|
851
907
|
description: "Read contents of a file from the file system",
|
|
@@ -855,11 +911,12 @@ var fileReadTool = {
|
|
|
855
911
|
}),
|
|
856
912
|
execute: async (params) => {
|
|
857
913
|
try {
|
|
914
|
+
const safePath = resolveWithinRoot(params.path);
|
|
858
915
|
const content = await import_fs.promises.readFile(
|
|
859
|
-
|
|
916
|
+
safePath,
|
|
860
917
|
params.encoding
|
|
861
918
|
);
|
|
862
|
-
const stats = await import_fs.promises.stat(
|
|
919
|
+
const stats = await import_fs.promises.stat(safePath);
|
|
863
920
|
return {
|
|
864
921
|
content,
|
|
865
922
|
size: stats.size,
|
|
@@ -885,23 +942,24 @@ var fileWriteTool = {
|
|
|
885
942
|
}),
|
|
886
943
|
execute: async (params) => {
|
|
887
944
|
try {
|
|
945
|
+
const safePath = resolveWithinRoot(params.path);
|
|
888
946
|
if (params.append) {
|
|
889
947
|
await import_fs.promises.appendFile(
|
|
890
|
-
|
|
948
|
+
safePath,
|
|
891
949
|
params.content,
|
|
892
950
|
params.encoding
|
|
893
951
|
);
|
|
894
952
|
} else {
|
|
895
953
|
await import_fs.promises.writeFile(
|
|
896
|
-
|
|
954
|
+
safePath,
|
|
897
955
|
params.content,
|
|
898
956
|
params.encoding
|
|
899
957
|
);
|
|
900
958
|
}
|
|
901
|
-
const stats = await import_fs.promises.stat(
|
|
959
|
+
const stats = await import_fs.promises.stat(safePath);
|
|
902
960
|
return {
|
|
903
961
|
success: true,
|
|
904
|
-
path:
|
|
962
|
+
path: safePath,
|
|
905
963
|
size: stats.size,
|
|
906
964
|
modified: stats.mtime
|
|
907
965
|
};
|
|
@@ -922,10 +980,11 @@ var fileListTool = {
|
|
|
922
980
|
}),
|
|
923
981
|
execute: async (params) => {
|
|
924
982
|
try {
|
|
925
|
-
const
|
|
983
|
+
const safePath = resolveWithinRoot(params.path);
|
|
984
|
+
const items = await import_fs.promises.readdir(safePath, { withFileTypes: true });
|
|
926
985
|
const results = [];
|
|
927
986
|
for (const item of items) {
|
|
928
|
-
const fullPath = (0,
|
|
987
|
+
const fullPath = (0, import_path2.join)(safePath, item.name);
|
|
929
988
|
const stats = await import_fs.promises.stat(fullPath);
|
|
930
989
|
results.push({
|
|
931
990
|
name: item.name,
|
|
@@ -1444,7 +1503,7 @@ async function pollExecutionStatus(executionId, apiKey, baseUrl, maxAttempts = 3
|
|
|
1444
1503
|
return execution;
|
|
1445
1504
|
}
|
|
1446
1505
|
if (attempt < maxAttempts - 1) {
|
|
1447
|
-
await new Promise((
|
|
1506
|
+
await new Promise((resolve2) => setTimeout(resolve2, intervalMs));
|
|
1448
1507
|
}
|
|
1449
1508
|
}
|
|
1450
1509
|
throw new Error(
|
|
@@ -1459,10 +1518,10 @@ var MAX_OUTPUT_BYTES = 100 * 1024;
|
|
|
1459
1518
|
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
1460
1519
|
var MAX_TIMEOUT_MS = 12e4;
|
|
1461
1520
|
var DANGEROUS_PATTERNS = [
|
|
1462
|
-
/\brm\s+-[^\s]*r[^\s]*f[^\s]*\s
|
|
1463
|
-
// rm -rf /
|
|
1464
|
-
/\brm\s+-[^\s]*f[^\s]*r[^\s]*\s
|
|
1465
|
-
// rm -fr /
|
|
1521
|
+
/\brm\s+-[^\s]*r[^\s]*f[^\s]*\s+\/(\s|\*|$)/,
|
|
1522
|
+
// rm -rf / , / * , /...
|
|
1523
|
+
/\brm\s+-[^\s]*f[^\s]*r[^\s]*\s+\/(\s|\*|$)/,
|
|
1524
|
+
// rm -fr / , / * , /...
|
|
1466
1525
|
/\bmkfs\b/,
|
|
1467
1526
|
// mkfs (format disk)
|
|
1468
1527
|
/:(){ :\|:& };:/,
|
|
@@ -1478,7 +1537,7 @@ var DANGEROUS_PATTERNS = [
|
|
|
1478
1537
|
];
|
|
1479
1538
|
var shellExecuteTool = {
|
|
1480
1539
|
name: "shell_execute",
|
|
1481
|
-
description: "Execute a shell command and return stdout/stderr.
|
|
1540
|
+
description: "Execute a shell command and return stdout/stderr. A best-effort blocklist rejects a few obviously destructive commands, but this is NOT a security boundary \u2014 only enable this tool for trusted callers and prefer a sandboxed host. Non-zero exit codes return results (not errors) since tools like grep exit 1 on no matches.",
|
|
1482
1541
|
parameters: import_zod7.z.object({
|
|
1483
1542
|
command: import_zod7.z.string().describe("The shell command to execute"),
|
|
1484
1543
|
cwd: import_zod7.z.string().optional().describe(
|
|
@@ -1555,13 +1614,14 @@ var codeEditTool = {
|
|
|
1555
1614
|
}),
|
|
1556
1615
|
execute: async (params) => {
|
|
1557
1616
|
try {
|
|
1558
|
-
const
|
|
1617
|
+
const safePath = resolveWithinRoot(params.path);
|
|
1618
|
+
const content = await import_fs2.promises.readFile(safePath, "utf8");
|
|
1559
1619
|
if (params.oldString === "") {
|
|
1560
1620
|
const newContent2 = params.newString + content;
|
|
1561
|
-
await import_fs2.promises.writeFile(
|
|
1621
|
+
await import_fs2.promises.writeFile(safePath, newContent2, "utf8");
|
|
1562
1622
|
return {
|
|
1563
1623
|
success: true,
|
|
1564
|
-
path:
|
|
1624
|
+
path: safePath,
|
|
1565
1625
|
replacements: 1,
|
|
1566
1626
|
message: "Content inserted at beginning of file"
|
|
1567
1627
|
};
|
|
@@ -1585,10 +1645,10 @@ var codeEditTool = {
|
|
|
1585
1645
|
);
|
|
1586
1646
|
}
|
|
1587
1647
|
const newContent = content.split(params.oldString).join(params.newString);
|
|
1588
|
-
await import_fs2.promises.writeFile(
|
|
1648
|
+
await import_fs2.promises.writeFile(safePath, newContent, "utf8");
|
|
1589
1649
|
return {
|
|
1590
1650
|
success: true,
|
|
1591
|
-
path:
|
|
1651
|
+
path: safePath,
|
|
1592
1652
|
replacements: count,
|
|
1593
1653
|
message: `Replaced ${count} occurrence(s)`
|
|
1594
1654
|
};
|
|
@@ -1665,7 +1725,7 @@ var globTool = {
|
|
|
1665
1725
|
|
|
1666
1726
|
// src/tools/built-in/grep.tool.ts
|
|
1667
1727
|
var import_fs4 = require("fs");
|
|
1668
|
-
var
|
|
1728
|
+
var import_path3 = require("path");
|
|
1669
1729
|
var import_zod10 = require("zod");
|
|
1670
1730
|
var MAX_FILE_SIZE = 5 * 1024 * 1024;
|
|
1671
1731
|
var DEFAULT_IGNORE_DIRS = /* @__PURE__ */ new Set([
|
|
@@ -1685,7 +1745,7 @@ async function walkDir(dir, includePattern) {
|
|
|
1685
1745
|
for (const entry of entries) {
|
|
1686
1746
|
if (DEFAULT_IGNORE_DIRS.has(entry.name)) continue;
|
|
1687
1747
|
if (entry.name.startsWith(".") && entry.name !== ".env.example") continue;
|
|
1688
|
-
const fullPath = (0,
|
|
1748
|
+
const fullPath = (0, import_path3.join)(dir, entry.name);
|
|
1689
1749
|
if (entry.isDirectory()) {
|
|
1690
1750
|
const subResults = await walkDir(fullPath, includePattern);
|
|
1691
1751
|
results.push(...subResults);
|
|
@@ -1761,7 +1821,7 @@ var grepTool = {
|
|
|
1761
1821
|
);
|
|
1762
1822
|
for (const match of matches) {
|
|
1763
1823
|
if (allMatches.length >= params.maxResults) break;
|
|
1764
|
-
match.file = stat.isFile() ? match.file : (0,
|
|
1824
|
+
match.file = stat.isFile() ? match.file : (0, import_path3.relative)(searchPath, match.file);
|
|
1765
1825
|
allMatches.push(match);
|
|
1766
1826
|
}
|
|
1767
1827
|
} catch {
|
|
@@ -1787,7 +1847,7 @@ var import_child_process2 = require("child_process");
|
|
|
1787
1847
|
var import_zod11 = require("zod");
|
|
1788
1848
|
var GIT_TIMEOUT_MS = 3e4;
|
|
1789
1849
|
function gitExec(args, cwd) {
|
|
1790
|
-
return (0, import_child_process2.
|
|
1850
|
+
return (0, import_child_process2.execFileSync)("git", args, {
|
|
1791
1851
|
cwd: cwd || process.cwd(),
|
|
1792
1852
|
timeout: GIT_TIMEOUT_MS,
|
|
1793
1853
|
encoding: "utf8",
|
|
@@ -1802,8 +1862,8 @@ var gitStatusTool = {
|
|
|
1802
1862
|
}),
|
|
1803
1863
|
execute: (params) => {
|
|
1804
1864
|
try {
|
|
1805
|
-
const output = gitExec("status --porcelain", params.cwd);
|
|
1806
|
-
const branch = gitExec("branch --show-current", params.cwd);
|
|
1865
|
+
const output = gitExec(["status", "--porcelain"], params.cwd);
|
|
1866
|
+
const branch = gitExec(["branch", "--show-current"], params.cwd);
|
|
1807
1867
|
const staged = [];
|
|
1808
1868
|
const unstaged = [];
|
|
1809
1869
|
const untracked = [];
|
|
@@ -1845,9 +1905,9 @@ var gitDiffTool = {
|
|
|
1845
1905
|
}),
|
|
1846
1906
|
execute: (params) => {
|
|
1847
1907
|
try {
|
|
1848
|
-
|
|
1849
|
-
if (params.staged) args
|
|
1850
|
-
if (params.path) args
|
|
1908
|
+
const args = ["diff"];
|
|
1909
|
+
if (params.staged) args.push("--cached");
|
|
1910
|
+
if (params.path) args.push("--", params.path);
|
|
1851
1911
|
const output = gitExec(args, params.cwd);
|
|
1852
1912
|
return Promise.resolve({
|
|
1853
1913
|
diff: output,
|
|
@@ -1870,8 +1930,7 @@ var gitAddTool = {
|
|
|
1870
1930
|
}),
|
|
1871
1931
|
execute: (params) => {
|
|
1872
1932
|
try {
|
|
1873
|
-
|
|
1874
|
-
gitExec(`add ${escapedPaths}`, params.cwd);
|
|
1933
|
+
gitExec(["add", "--", ...params.paths], params.cwd);
|
|
1875
1934
|
return Promise.resolve({
|
|
1876
1935
|
success: true,
|
|
1877
1936
|
added: params.paths
|
|
@@ -1893,8 +1952,7 @@ var gitCommitTool = {
|
|
|
1893
1952
|
}),
|
|
1894
1953
|
execute: (params) => {
|
|
1895
1954
|
try {
|
|
1896
|
-
const
|
|
1897
|
-
const output = gitExec(`commit -m '${safeMessage}'`, params.cwd);
|
|
1955
|
+
const output = gitExec(["commit", "-m", params.message], params.cwd);
|
|
1898
1956
|
return Promise.resolve({
|
|
1899
1957
|
success: true,
|
|
1900
1958
|
output
|
|
@@ -1918,13 +1976,13 @@ var gitLogTool = {
|
|
|
1918
1976
|
}),
|
|
1919
1977
|
execute: (params) => {
|
|
1920
1978
|
try {
|
|
1921
|
-
|
|
1979
|
+
const args = ["log", `-${params.maxCount}`];
|
|
1922
1980
|
if (params.oneline) {
|
|
1923
|
-
args
|
|
1981
|
+
args.push("--oneline");
|
|
1924
1982
|
} else {
|
|
1925
|
-
args
|
|
1983
|
+
args.push("--format=%H%n%an%n%ae%n%ai%n%s%n---");
|
|
1926
1984
|
}
|
|
1927
|
-
if (params.path) args
|
|
1985
|
+
if (params.path) args.push("--", params.path);
|
|
1928
1986
|
const output = gitExec(args, params.cwd);
|
|
1929
1987
|
if (params.oneline) {
|
|
1930
1988
|
const commits = output.split("\n").filter(Boolean).map((line) => {
|
|
@@ -1957,8 +2015,8 @@ var gitBranchTool = {
|
|
|
1957
2015
|
try {
|
|
1958
2016
|
switch (params.action) {
|
|
1959
2017
|
case "list": {
|
|
1960
|
-
const output = gitExec("branch -a", params.cwd);
|
|
1961
|
-
const current = gitExec("branch --show-current", params.cwd);
|
|
2018
|
+
const output = gitExec(["branch", "-a"], params.cwd);
|
|
2019
|
+
const current = gitExec(["branch", "--show-current"], params.cwd);
|
|
1962
2020
|
const branches = output.split("\n").filter(Boolean).map((b) => b.replace(/^\*?\s+/, "").trim());
|
|
1963
2021
|
return Promise.resolve({ branches, current });
|
|
1964
2022
|
}
|
|
@@ -1966,14 +2024,20 @@ var gitBranchTool = {
|
|
|
1966
2024
|
if (!params.name) {
|
|
1967
2025
|
throw new Error("Branch name is required for create action");
|
|
1968
2026
|
}
|
|
1969
|
-
|
|
2027
|
+
if (params.name.startsWith("-")) {
|
|
2028
|
+
throw new Error("Invalid branch name");
|
|
2029
|
+
}
|
|
2030
|
+
gitExec(["branch", params.name], params.cwd);
|
|
1970
2031
|
return Promise.resolve({ success: true, created: params.name });
|
|
1971
2032
|
}
|
|
1972
2033
|
case "switch": {
|
|
1973
2034
|
if (!params.name) {
|
|
1974
2035
|
throw new Error("Branch name is required for switch action");
|
|
1975
2036
|
}
|
|
1976
|
-
|
|
2037
|
+
if (params.name.startsWith("-")) {
|
|
2038
|
+
throw new Error("Invalid branch name");
|
|
2039
|
+
}
|
|
2040
|
+
gitExec(["checkout", params.name], params.cwd);
|
|
1977
2041
|
return Promise.resolve({ success: true, switched: params.name });
|
|
1978
2042
|
}
|
|
1979
2043
|
}
|
|
@@ -2278,17 +2342,28 @@ var AnthropicProvider = class {
|
|
|
2278
2342
|
continue;
|
|
2279
2343
|
}
|
|
2280
2344
|
if (message.role === "tool") {
|
|
2281
|
-
const
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2345
|
+
const toolUseId = message.toolCallId || "";
|
|
2346
|
+
const resultBlock = this.toolResultBlock(toolUseId, message.content);
|
|
2347
|
+
const prev = converted[converted.length - 1];
|
|
2348
|
+
if (prev && prev.role === "user" && Array.isArray(prev.content) && prev.content.some((b) => b.type === "tool_result")) {
|
|
2349
|
+
const assistant = converted[converted.length - 2];
|
|
2350
|
+
if (assistant && assistant.role === "assistant") {
|
|
2351
|
+
assistant.content = this.ensureToolUse(
|
|
2352
|
+
assistant.content,
|
|
2353
|
+
toolUseId,
|
|
2354
|
+
message.name
|
|
2355
|
+
);
|
|
2291
2356
|
}
|
|
2357
|
+
prev.content.push(resultBlock);
|
|
2358
|
+
} else if (prev && prev.role === "assistant") {
|
|
2359
|
+
prev.content = this.ensureToolUse(
|
|
2360
|
+
prev.content,
|
|
2361
|
+
toolUseId,
|
|
2362
|
+
message.name
|
|
2363
|
+
);
|
|
2364
|
+
converted.push({ role: "user", content: [resultBlock] });
|
|
2365
|
+
} else {
|
|
2366
|
+
converted.push({ role: "user", content: [resultBlock] });
|
|
2292
2367
|
}
|
|
2293
2368
|
continue;
|
|
2294
2369
|
}
|
|
@@ -2299,6 +2374,20 @@ var AnthropicProvider = class {
|
|
|
2299
2374
|
}
|
|
2300
2375
|
return converted;
|
|
2301
2376
|
}
|
|
2377
|
+
/**
|
|
2378
|
+
* Ensure an assistant message's content is a block array that includes a
|
|
2379
|
+
* `tool_use` block with the given id, backfilling one if missing.
|
|
2380
|
+
*/
|
|
2381
|
+
ensureToolUse(content, id, name) {
|
|
2382
|
+
const blocks = typeof content === "string" ? content ? [{ type: "text", text: content }] : [] : [...content];
|
|
2383
|
+
if (!blocks.some((b) => b.type === "tool_use" && b.id === id)) {
|
|
2384
|
+
blocks.push({ type: "tool_use", id, name: name || "tool", input: {} });
|
|
2385
|
+
}
|
|
2386
|
+
return blocks;
|
|
2387
|
+
}
|
|
2388
|
+
toolResultBlock(toolUseId, content) {
|
|
2389
|
+
return { type: "tool_result", tool_use_id: toolUseId, content };
|
|
2390
|
+
}
|
|
2302
2391
|
};
|
|
2303
2392
|
|
|
2304
2393
|
// src/providers/openai.ts
|
|
@@ -3560,7 +3649,7 @@ var Workflow = class {
|
|
|
3560
3649
|
if (attempt < maxAttempts && initialDelayMs > 0) {
|
|
3561
3650
|
const raw = backoff === "linear" ? initialDelayMs * attempt : initialDelayMs * 2 ** (attempt - 1);
|
|
3562
3651
|
await new Promise(
|
|
3563
|
-
(
|
|
3652
|
+
(resolve2) => setTimeout(resolve2, Math.min(raw, maxDelayMs))
|
|
3564
3653
|
);
|
|
3565
3654
|
}
|
|
3566
3655
|
}
|
|
@@ -4152,7 +4241,7 @@ var RateLimiter = class {
|
|
|
4152
4241
|
* Sleep helper
|
|
4153
4242
|
*/
|
|
4154
4243
|
sleep(ms) {
|
|
4155
|
-
return new Promise((
|
|
4244
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
4156
4245
|
}
|
|
4157
4246
|
};
|
|
4158
4247
|
var SlidingWindowRateLimiter = class {
|
|
@@ -4384,7 +4473,7 @@ var StdioTransport = class extends import_events.EventEmitter {
|
|
|
4384
4473
|
connected = false;
|
|
4385
4474
|
buffer = "";
|
|
4386
4475
|
async connect() {
|
|
4387
|
-
return new Promise((
|
|
4476
|
+
return new Promise((resolve2, reject) => {
|
|
4388
4477
|
try {
|
|
4389
4478
|
this.process = (0, import_child_process3.spawn)(this.command, this.args, {
|
|
4390
4479
|
env: { ...process.env, ...this.env },
|
|
@@ -4407,7 +4496,7 @@ var StdioTransport = class extends import_events.EventEmitter {
|
|
|
4407
4496
|
});
|
|
4408
4497
|
this.connected = true;
|
|
4409
4498
|
this.emit("connect");
|
|
4410
|
-
|
|
4499
|
+
resolve2();
|
|
4411
4500
|
} catch (error) {
|
|
4412
4501
|
reject(error);
|
|
4413
4502
|
}
|
|
@@ -4455,7 +4544,7 @@ var SSETransport = class extends import_events.EventEmitter {
|
|
|
4455
4544
|
eventSource = null;
|
|
4456
4545
|
connected = false;
|
|
4457
4546
|
async connect() {
|
|
4458
|
-
return new Promise((
|
|
4547
|
+
return new Promise((resolve2, reject) => {
|
|
4459
4548
|
try {
|
|
4460
4549
|
if (typeof EventSource === "undefined") {
|
|
4461
4550
|
throw new Error(
|
|
@@ -4466,7 +4555,7 @@ var SSETransport = class extends import_events.EventEmitter {
|
|
|
4466
4555
|
this.eventSource.onopen = () => {
|
|
4467
4556
|
this.connected = true;
|
|
4468
4557
|
this.emit("connect");
|
|
4469
|
-
|
|
4558
|
+
resolve2();
|
|
4470
4559
|
};
|
|
4471
4560
|
this.eventSource.onmessage = (event) => {
|
|
4472
4561
|
try {
|
|
@@ -4677,9 +4766,9 @@ var MCPClient = class extends import_events2.EventEmitter {
|
|
|
4677
4766
|
if (!this.transport || !this.transport.isConnected()) {
|
|
4678
4767
|
throw new Error("Not connected to MCP server");
|
|
4679
4768
|
}
|
|
4680
|
-
return new Promise((
|
|
4769
|
+
return new Promise((resolve2, reject) => {
|
|
4681
4770
|
const id = message.id;
|
|
4682
|
-
this.pendingRequests.set(id, { resolve, reject });
|
|
4771
|
+
this.pendingRequests.set(id, { resolve: resolve2, reject });
|
|
4683
4772
|
try {
|
|
4684
4773
|
this.transport.send(message);
|
|
4685
4774
|
} catch (error) {
|
|
@@ -5814,7 +5903,7 @@ Respond naturally while ensuring you gather the required information.`;
|
|
|
5814
5903
|
|
|
5815
5904
|
// src/voice/voice-agent.ts
|
|
5816
5905
|
var import_fs5 = require("fs");
|
|
5817
|
-
var
|
|
5906
|
+
var import_path4 = require("path");
|
|
5818
5907
|
var VoiceAgent = class {
|
|
5819
5908
|
agent;
|
|
5820
5909
|
sttProvider;
|
|
@@ -5978,10 +6067,10 @@ var VoiceAgent = class {
|
|
|
5978
6067
|
exportConversation(outputDir) {
|
|
5979
6068
|
for (let i = 0; i < this.conversationHistory.length; i++) {
|
|
5980
6069
|
const message = this.conversationHistory[i];
|
|
5981
|
-
const textPath = (0,
|
|
6070
|
+
const textPath = (0, import_path4.join)(outputDir, `${i}-${message.role}.txt`);
|
|
5982
6071
|
(0, import_fs5.writeFileSync)(textPath, message.text);
|
|
5983
6072
|
if (message.audio) {
|
|
5984
|
-
const audioPath = (0,
|
|
6073
|
+
const audioPath = (0, import_path4.join)(outputDir, `${i}-${message.role}.mp3`);
|
|
5985
6074
|
(0, import_fs5.writeFileSync)(audioPath, message.audio);
|
|
5986
6075
|
}
|
|
5987
6076
|
}
|
|
@@ -6202,9 +6291,9 @@ var OpenAIWhisperProvider = class {
|
|
|
6202
6291
|
var import_child_process4 = require("child_process");
|
|
6203
6292
|
var import_fs7 = require("fs");
|
|
6204
6293
|
var import_os = require("os");
|
|
6205
|
-
var
|
|
6294
|
+
var import_path5 = require("path");
|
|
6206
6295
|
var import_util = require("util");
|
|
6207
|
-
var
|
|
6296
|
+
var execFileAsync = (0, import_util.promisify)(import_child_process4.execFile);
|
|
6208
6297
|
var LocalWhisperProvider = class {
|
|
6209
6298
|
whisperPath;
|
|
6210
6299
|
modelPath;
|
|
@@ -6220,7 +6309,7 @@ var LocalWhisperProvider = class {
|
|
|
6220
6309
|
let isTemporary = false;
|
|
6221
6310
|
try {
|
|
6222
6311
|
if (Buffer.isBuffer(audio)) {
|
|
6223
|
-
audioPath = (0,
|
|
6312
|
+
audioPath = (0, import_path5.join)((0, import_os.tmpdir)(), `audio-${Date.now()}.wav`);
|
|
6224
6313
|
(0, import_fs7.writeFileSync)(audioPath, audio);
|
|
6225
6314
|
isTemporary = true;
|
|
6226
6315
|
} else {
|
|
@@ -6230,16 +6319,23 @@ var LocalWhisperProvider = class {
|
|
|
6230
6319
|
throw new Error(`Audio file not found: ${audioPath}`);
|
|
6231
6320
|
}
|
|
6232
6321
|
const model = config?.model || "base";
|
|
6233
|
-
const
|
|
6322
|
+
const languageArgs = config?.language ? ["--language", config.language] : [];
|
|
6234
6323
|
const outputFormat = config?.responseFormat || "txt";
|
|
6235
|
-
let
|
|
6324
|
+
let args;
|
|
6236
6325
|
if (this.whisperPath.includes("whisper.cpp")) {
|
|
6237
6326
|
const modelFile = this.modelPath || `ggml-${model}.bin`;
|
|
6238
|
-
|
|
6327
|
+
args = ["-m", modelFile, ...languageArgs, "-otxt", audioPath];
|
|
6239
6328
|
} else {
|
|
6240
|
-
|
|
6241
|
-
|
|
6242
|
-
|
|
6329
|
+
args = [
|
|
6330
|
+
audioPath,
|
|
6331
|
+
"--model",
|
|
6332
|
+
model,
|
|
6333
|
+
...languageArgs,
|
|
6334
|
+
"--output_format",
|
|
6335
|
+
outputFormat
|
|
6336
|
+
];
|
|
6337
|
+
}
|
|
6338
|
+
const { stdout } = await execFileAsync(this.whisperPath, args, {
|
|
6243
6339
|
maxBuffer: 10 * 1024 * 1024
|
|
6244
6340
|
// 10MB buffer
|
|
6245
6341
|
});
|
|
@@ -6281,7 +6377,7 @@ var LocalWhisperProvider = class {
|
|
|
6281
6377
|
*/
|
|
6282
6378
|
async isInstalled() {
|
|
6283
6379
|
try {
|
|
6284
|
-
await
|
|
6380
|
+
await execFileAsync(this.whisperPath, ["--help"]);
|
|
6285
6381
|
return true;
|
|
6286
6382
|
} catch {
|
|
6287
6383
|
return false;
|
|
@@ -6587,9 +6683,9 @@ var ElevenLabsTTSProvider = class {
|
|
|
6587
6683
|
var import_child_process5 = require("child_process");
|
|
6588
6684
|
var import_fs8 = require("fs");
|
|
6589
6685
|
var import_os2 = require("os");
|
|
6590
|
-
var
|
|
6686
|
+
var import_path6 = require("path");
|
|
6591
6687
|
var import_util2 = require("util");
|
|
6592
|
-
var
|
|
6688
|
+
var execFileAsync2 = (0, import_util2.promisify)(import_child_process5.execFile);
|
|
6593
6689
|
var PiperTTSProvider = class {
|
|
6594
6690
|
piperPath;
|
|
6595
6691
|
modelPath;
|
|
@@ -6603,26 +6699,40 @@ var PiperTTSProvider = class {
|
|
|
6603
6699
|
* Synthesize text to speech
|
|
6604
6700
|
*/
|
|
6605
6701
|
async synthesize(text, config) {
|
|
6702
|
+
const outputPath = (0, import_path6.join)((0, import_os2.tmpdir)(), `speech-${Date.now()}.wav`);
|
|
6606
6703
|
try {
|
|
6607
|
-
const outputPath = (0, import_path5.join)((0, import_os2.tmpdir)(), `speech-${Date.now()}.wav`);
|
|
6608
6704
|
const model = this.modelPath || config?.model;
|
|
6609
6705
|
if (!model) {
|
|
6610
6706
|
throw new Error("Model path is required for Piper TTS");
|
|
6611
6707
|
}
|
|
6612
6708
|
const modelConfig = this.configPath || model.replace(".onnx", ".json");
|
|
6613
|
-
const
|
|
6614
|
-
|
|
6615
|
-
|
|
6616
|
-
|
|
6617
|
-
|
|
6618
|
-
|
|
6709
|
+
const args = [
|
|
6710
|
+
"--model",
|
|
6711
|
+
model,
|
|
6712
|
+
"--config",
|
|
6713
|
+
modelConfig,
|
|
6714
|
+
"--output_file",
|
|
6715
|
+
outputPath
|
|
6716
|
+
];
|
|
6717
|
+
await new Promise((resolve2, reject) => {
|
|
6718
|
+
const child = (0, import_child_process5.spawn)(this.piperPath, args, {
|
|
6719
|
+
stdio: ["pipe", "ignore", "pipe"]
|
|
6720
|
+
});
|
|
6721
|
+
let stderr = "";
|
|
6722
|
+
child.stderr?.on("data", (chunk) => {
|
|
6723
|
+
stderr += String(chunk);
|
|
6724
|
+
});
|
|
6725
|
+
child.on("error", reject);
|
|
6726
|
+
child.on("close", (code) => {
|
|
6727
|
+
if (code === 0) resolve2();
|
|
6728
|
+
else reject(new Error(`piper exited with code ${code}: ${stderr}`));
|
|
6729
|
+
});
|
|
6730
|
+
child.stdin?.end(text);
|
|
6619
6731
|
});
|
|
6620
6732
|
if (!(0, import_fs8.existsSync)(outputPath)) {
|
|
6621
6733
|
throw new Error("Piper failed to generate audio file");
|
|
6622
6734
|
}
|
|
6623
6735
|
const audio = (0, import_fs8.readFileSync)(outputPath);
|
|
6624
|
-
(0, import_fs8.unlinkSync)(outputPath);
|
|
6625
|
-
(0, import_fs8.unlinkSync)(textPath);
|
|
6626
6736
|
return {
|
|
6627
6737
|
audio,
|
|
6628
6738
|
format: "wav",
|
|
@@ -6632,6 +6742,10 @@ var PiperTTSProvider = class {
|
|
|
6632
6742
|
throw new Error(
|
|
6633
6743
|
`Piper TTS synthesis failed: ${error instanceof Error ? error.message : String(error)}`
|
|
6634
6744
|
);
|
|
6745
|
+
} finally {
|
|
6746
|
+
if ((0, import_fs8.existsSync)(outputPath)) {
|
|
6747
|
+
(0, import_fs8.unlinkSync)(outputPath);
|
|
6748
|
+
}
|
|
6635
6749
|
}
|
|
6636
6750
|
}
|
|
6637
6751
|
/**
|
|
@@ -6645,7 +6759,7 @@ var PiperTTSProvider = class {
|
|
|
6645
6759
|
*/
|
|
6646
6760
|
async isInstalled() {
|
|
6647
6761
|
try {
|
|
6648
|
-
await
|
|
6762
|
+
await execFileAsync2(this.piperPath, ["--version"]);
|
|
6649
6763
|
return true;
|
|
6650
6764
|
} catch {
|
|
6651
6765
|
return false;
|
package/dist/index.mjs
CHANGED
|
@@ -731,7 +731,7 @@ var ToolRegistry = class {
|
|
|
731
731
|
* Sleep helper
|
|
732
732
|
*/
|
|
733
733
|
sleep(ms) {
|
|
734
|
-
return new Promise((
|
|
734
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
735
735
|
}
|
|
736
736
|
};
|
|
737
737
|
|
|
@@ -766,7 +766,49 @@ var calculatorTool = {
|
|
|
766
766
|
};
|
|
767
767
|
|
|
768
768
|
// src/tools/built-in/http-request.tool.ts
|
|
769
|
+
import { lookup } from "dns/promises";
|
|
770
|
+
import { isIP } from "net";
|
|
769
771
|
import { z as z2 } from "zod";
|
|
772
|
+
function isPrivateIp(ip) {
|
|
773
|
+
const mapped = ip.match(/^::ffff:(\d+\.\d+\.\d+\.\d+)$/i);
|
|
774
|
+
const addr = mapped ? mapped[1] : ip;
|
|
775
|
+
if (isIP(addr) === 4) {
|
|
776
|
+
const [a, b] = addr.split(".").map(Number);
|
|
777
|
+
return a === 0 || // 0.0.0.0/8
|
|
778
|
+
a === 10 || // 10.0.0.0/8 (private)
|
|
779
|
+
a === 127 || // 127.0.0.0/8 (loopback)
|
|
780
|
+
a === 169 && b === 254 || // 169.254.0.0/16 (link-local incl. cloud metadata)
|
|
781
|
+
a === 172 && b >= 16 && b <= 31 || // 172.16.0.0/12 (private)
|
|
782
|
+
a === 192 && b === 168 || // 192.168.0.0/16 (private)
|
|
783
|
+
a === 100 && b >= 64 && b <= 127 || // 100.64.0.0/10 (CGNAT)
|
|
784
|
+
a >= 224;
|
|
785
|
+
}
|
|
786
|
+
const lower = ip.toLowerCase();
|
|
787
|
+
return lower === "::1" || // loopback
|
|
788
|
+
lower === "::" || // unspecified
|
|
789
|
+
lower.startsWith("fe80:") || // link-local
|
|
790
|
+
lower.startsWith("fc") || // unique-local fc00::/7
|
|
791
|
+
lower.startsWith("fd");
|
|
792
|
+
}
|
|
793
|
+
async function assertPublicUrl(rawUrl) {
|
|
794
|
+
let parsed;
|
|
795
|
+
try {
|
|
796
|
+
parsed = new URL(rawUrl);
|
|
797
|
+
} catch {
|
|
798
|
+
throw new Error("Invalid URL");
|
|
799
|
+
}
|
|
800
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
801
|
+
throw new Error(`Blocked URL scheme: ${parsed.protocol}`);
|
|
802
|
+
}
|
|
803
|
+
const host = parsed.hostname;
|
|
804
|
+
if (host === "localhost") {
|
|
805
|
+
throw new Error("Blocked request to localhost");
|
|
806
|
+
}
|
|
807
|
+
const candidates = isIP(host) ? [host] : (await lookup(host, { all: true })).map((r) => r.address);
|
|
808
|
+
if (candidates.some(isPrivateIp)) {
|
|
809
|
+
throw new Error(`Blocked request to non-public address for host "${host}"`);
|
|
810
|
+
}
|
|
811
|
+
}
|
|
770
812
|
var httpRequestTool = {
|
|
771
813
|
name: "http_request",
|
|
772
814
|
description: "Make HTTP requests to external APIs. Supports GET, POST, PUT, DELETE methods.",
|
|
@@ -778,6 +820,7 @@ var httpRequestTool = {
|
|
|
778
820
|
timeout: z2.number().optional().default(1e4).describe("Request timeout in milliseconds")
|
|
779
821
|
}),
|
|
780
822
|
execute: async (params) => {
|
|
823
|
+
await assertPublicUrl(params.url);
|
|
781
824
|
const controller = new AbortController();
|
|
782
825
|
const timeoutId = setTimeout(
|
|
783
826
|
() => controller.abort(),
|
|
@@ -834,6 +877,19 @@ var httpRequestTool = {
|
|
|
834
877
|
import { promises as fs } from "fs";
|
|
835
878
|
import { join } from "path";
|
|
836
879
|
import { z as z3 } from "zod";
|
|
880
|
+
|
|
881
|
+
// src/tools/built-in/path-guard.ts
|
|
882
|
+
import { resolve, sep } from "path";
|
|
883
|
+
function resolveWithinRoot(inputPath) {
|
|
884
|
+
const root = resolve(process.env.AGENTSEA_FILE_ROOT || process.cwd());
|
|
885
|
+
const resolved = resolve(root, inputPath);
|
|
886
|
+
if (resolved !== root && !resolved.startsWith(root + sep)) {
|
|
887
|
+
throw new Error(`Path escapes the allowed root directory: ${inputPath}`);
|
|
888
|
+
}
|
|
889
|
+
return resolved;
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
// src/tools/built-in/file-operations.tool.ts
|
|
837
893
|
var fileReadTool = {
|
|
838
894
|
name: "file_read",
|
|
839
895
|
description: "Read contents of a file from the file system",
|
|
@@ -843,11 +899,12 @@ var fileReadTool = {
|
|
|
843
899
|
}),
|
|
844
900
|
execute: async (params) => {
|
|
845
901
|
try {
|
|
902
|
+
const safePath = resolveWithinRoot(params.path);
|
|
846
903
|
const content = await fs.readFile(
|
|
847
|
-
|
|
904
|
+
safePath,
|
|
848
905
|
params.encoding
|
|
849
906
|
);
|
|
850
|
-
const stats = await fs.stat(
|
|
907
|
+
const stats = await fs.stat(safePath);
|
|
851
908
|
return {
|
|
852
909
|
content,
|
|
853
910
|
size: stats.size,
|
|
@@ -873,23 +930,24 @@ var fileWriteTool = {
|
|
|
873
930
|
}),
|
|
874
931
|
execute: async (params) => {
|
|
875
932
|
try {
|
|
933
|
+
const safePath = resolveWithinRoot(params.path);
|
|
876
934
|
if (params.append) {
|
|
877
935
|
await fs.appendFile(
|
|
878
|
-
|
|
936
|
+
safePath,
|
|
879
937
|
params.content,
|
|
880
938
|
params.encoding
|
|
881
939
|
);
|
|
882
940
|
} else {
|
|
883
941
|
await fs.writeFile(
|
|
884
|
-
|
|
942
|
+
safePath,
|
|
885
943
|
params.content,
|
|
886
944
|
params.encoding
|
|
887
945
|
);
|
|
888
946
|
}
|
|
889
|
-
const stats = await fs.stat(
|
|
947
|
+
const stats = await fs.stat(safePath);
|
|
890
948
|
return {
|
|
891
949
|
success: true,
|
|
892
|
-
path:
|
|
950
|
+
path: safePath,
|
|
893
951
|
size: stats.size,
|
|
894
952
|
modified: stats.mtime
|
|
895
953
|
};
|
|
@@ -910,10 +968,11 @@ var fileListTool = {
|
|
|
910
968
|
}),
|
|
911
969
|
execute: async (params) => {
|
|
912
970
|
try {
|
|
913
|
-
const
|
|
971
|
+
const safePath = resolveWithinRoot(params.path);
|
|
972
|
+
const items = await fs.readdir(safePath, { withFileTypes: true });
|
|
914
973
|
const results = [];
|
|
915
974
|
for (const item of items) {
|
|
916
|
-
const fullPath = join(
|
|
975
|
+
const fullPath = join(safePath, item.name);
|
|
917
976
|
const stats = await fs.stat(fullPath);
|
|
918
977
|
results.push({
|
|
919
978
|
name: item.name,
|
|
@@ -1432,7 +1491,7 @@ async function pollExecutionStatus(executionId, apiKey, baseUrl, maxAttempts = 3
|
|
|
1432
1491
|
return execution;
|
|
1433
1492
|
}
|
|
1434
1493
|
if (attempt < maxAttempts - 1) {
|
|
1435
|
-
await new Promise((
|
|
1494
|
+
await new Promise((resolve2) => setTimeout(resolve2, intervalMs));
|
|
1436
1495
|
}
|
|
1437
1496
|
}
|
|
1438
1497
|
throw new Error(
|
|
@@ -1447,10 +1506,10 @@ var MAX_OUTPUT_BYTES = 100 * 1024;
|
|
|
1447
1506
|
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
1448
1507
|
var MAX_TIMEOUT_MS = 12e4;
|
|
1449
1508
|
var DANGEROUS_PATTERNS = [
|
|
1450
|
-
/\brm\s+-[^\s]*r[^\s]*f[^\s]*\s
|
|
1451
|
-
// rm -rf /
|
|
1452
|
-
/\brm\s+-[^\s]*f[^\s]*r[^\s]*\s
|
|
1453
|
-
// rm -fr /
|
|
1509
|
+
/\brm\s+-[^\s]*r[^\s]*f[^\s]*\s+\/(\s|\*|$)/,
|
|
1510
|
+
// rm -rf / , / * , /...
|
|
1511
|
+
/\brm\s+-[^\s]*f[^\s]*r[^\s]*\s+\/(\s|\*|$)/,
|
|
1512
|
+
// rm -fr / , / * , /...
|
|
1454
1513
|
/\bmkfs\b/,
|
|
1455
1514
|
// mkfs (format disk)
|
|
1456
1515
|
/:(){ :\|:& };:/,
|
|
@@ -1466,7 +1525,7 @@ var DANGEROUS_PATTERNS = [
|
|
|
1466
1525
|
];
|
|
1467
1526
|
var shellExecuteTool = {
|
|
1468
1527
|
name: "shell_execute",
|
|
1469
|
-
description: "Execute a shell command and return stdout/stderr.
|
|
1528
|
+
description: "Execute a shell command and return stdout/stderr. A best-effort blocklist rejects a few obviously destructive commands, but this is NOT a security boundary \u2014 only enable this tool for trusted callers and prefer a sandboxed host. Non-zero exit codes return results (not errors) since tools like grep exit 1 on no matches.",
|
|
1470
1529
|
parameters: z7.object({
|
|
1471
1530
|
command: z7.string().describe("The shell command to execute"),
|
|
1472
1531
|
cwd: z7.string().optional().describe(
|
|
@@ -1543,13 +1602,14 @@ var codeEditTool = {
|
|
|
1543
1602
|
}),
|
|
1544
1603
|
execute: async (params) => {
|
|
1545
1604
|
try {
|
|
1546
|
-
const
|
|
1605
|
+
const safePath = resolveWithinRoot(params.path);
|
|
1606
|
+
const content = await fs2.readFile(safePath, "utf8");
|
|
1547
1607
|
if (params.oldString === "") {
|
|
1548
1608
|
const newContent2 = params.newString + content;
|
|
1549
|
-
await fs2.writeFile(
|
|
1609
|
+
await fs2.writeFile(safePath, newContent2, "utf8");
|
|
1550
1610
|
return {
|
|
1551
1611
|
success: true,
|
|
1552
|
-
path:
|
|
1612
|
+
path: safePath,
|
|
1553
1613
|
replacements: 1,
|
|
1554
1614
|
message: "Content inserted at beginning of file"
|
|
1555
1615
|
};
|
|
@@ -1573,10 +1633,10 @@ var codeEditTool = {
|
|
|
1573
1633
|
);
|
|
1574
1634
|
}
|
|
1575
1635
|
const newContent = content.split(params.oldString).join(params.newString);
|
|
1576
|
-
await fs2.writeFile(
|
|
1636
|
+
await fs2.writeFile(safePath, newContent, "utf8");
|
|
1577
1637
|
return {
|
|
1578
1638
|
success: true,
|
|
1579
|
-
path:
|
|
1639
|
+
path: safePath,
|
|
1580
1640
|
replacements: count,
|
|
1581
1641
|
message: `Replaced ${count} occurrence(s)`
|
|
1582
1642
|
};
|
|
@@ -1771,11 +1831,11 @@ var grepTool = {
|
|
|
1771
1831
|
};
|
|
1772
1832
|
|
|
1773
1833
|
// src/tools/built-in/git.tool.ts
|
|
1774
|
-
import {
|
|
1834
|
+
import { execFileSync } from "child_process";
|
|
1775
1835
|
import { z as z11 } from "zod";
|
|
1776
1836
|
var GIT_TIMEOUT_MS = 3e4;
|
|
1777
1837
|
function gitExec(args, cwd) {
|
|
1778
|
-
return
|
|
1838
|
+
return execFileSync("git", args, {
|
|
1779
1839
|
cwd: cwd || process.cwd(),
|
|
1780
1840
|
timeout: GIT_TIMEOUT_MS,
|
|
1781
1841
|
encoding: "utf8",
|
|
@@ -1790,8 +1850,8 @@ var gitStatusTool = {
|
|
|
1790
1850
|
}),
|
|
1791
1851
|
execute: (params) => {
|
|
1792
1852
|
try {
|
|
1793
|
-
const output = gitExec("status --porcelain", params.cwd);
|
|
1794
|
-
const branch = gitExec("branch --show-current", params.cwd);
|
|
1853
|
+
const output = gitExec(["status", "--porcelain"], params.cwd);
|
|
1854
|
+
const branch = gitExec(["branch", "--show-current"], params.cwd);
|
|
1795
1855
|
const staged = [];
|
|
1796
1856
|
const unstaged = [];
|
|
1797
1857
|
const untracked = [];
|
|
@@ -1833,9 +1893,9 @@ var gitDiffTool = {
|
|
|
1833
1893
|
}),
|
|
1834
1894
|
execute: (params) => {
|
|
1835
1895
|
try {
|
|
1836
|
-
|
|
1837
|
-
if (params.staged) args
|
|
1838
|
-
if (params.path) args
|
|
1896
|
+
const args = ["diff"];
|
|
1897
|
+
if (params.staged) args.push("--cached");
|
|
1898
|
+
if (params.path) args.push("--", params.path);
|
|
1839
1899
|
const output = gitExec(args, params.cwd);
|
|
1840
1900
|
return Promise.resolve({
|
|
1841
1901
|
diff: output,
|
|
@@ -1858,8 +1918,7 @@ var gitAddTool = {
|
|
|
1858
1918
|
}),
|
|
1859
1919
|
execute: (params) => {
|
|
1860
1920
|
try {
|
|
1861
|
-
|
|
1862
|
-
gitExec(`add ${escapedPaths}`, params.cwd);
|
|
1921
|
+
gitExec(["add", "--", ...params.paths], params.cwd);
|
|
1863
1922
|
return Promise.resolve({
|
|
1864
1923
|
success: true,
|
|
1865
1924
|
added: params.paths
|
|
@@ -1881,8 +1940,7 @@ var gitCommitTool = {
|
|
|
1881
1940
|
}),
|
|
1882
1941
|
execute: (params) => {
|
|
1883
1942
|
try {
|
|
1884
|
-
const
|
|
1885
|
-
const output = gitExec(`commit -m '${safeMessage}'`, params.cwd);
|
|
1943
|
+
const output = gitExec(["commit", "-m", params.message], params.cwd);
|
|
1886
1944
|
return Promise.resolve({
|
|
1887
1945
|
success: true,
|
|
1888
1946
|
output
|
|
@@ -1906,13 +1964,13 @@ var gitLogTool = {
|
|
|
1906
1964
|
}),
|
|
1907
1965
|
execute: (params) => {
|
|
1908
1966
|
try {
|
|
1909
|
-
|
|
1967
|
+
const args = ["log", `-${params.maxCount}`];
|
|
1910
1968
|
if (params.oneline) {
|
|
1911
|
-
args
|
|
1969
|
+
args.push("--oneline");
|
|
1912
1970
|
} else {
|
|
1913
|
-
args
|
|
1971
|
+
args.push("--format=%H%n%an%n%ae%n%ai%n%s%n---");
|
|
1914
1972
|
}
|
|
1915
|
-
if (params.path) args
|
|
1973
|
+
if (params.path) args.push("--", params.path);
|
|
1916
1974
|
const output = gitExec(args, params.cwd);
|
|
1917
1975
|
if (params.oneline) {
|
|
1918
1976
|
const commits = output.split("\n").filter(Boolean).map((line) => {
|
|
@@ -1945,8 +2003,8 @@ var gitBranchTool = {
|
|
|
1945
2003
|
try {
|
|
1946
2004
|
switch (params.action) {
|
|
1947
2005
|
case "list": {
|
|
1948
|
-
const output = gitExec("branch -a", params.cwd);
|
|
1949
|
-
const current = gitExec("branch --show-current", params.cwd);
|
|
2006
|
+
const output = gitExec(["branch", "-a"], params.cwd);
|
|
2007
|
+
const current = gitExec(["branch", "--show-current"], params.cwd);
|
|
1950
2008
|
const branches = output.split("\n").filter(Boolean).map((b) => b.replace(/^\*?\s+/, "").trim());
|
|
1951
2009
|
return Promise.resolve({ branches, current });
|
|
1952
2010
|
}
|
|
@@ -1954,14 +2012,20 @@ var gitBranchTool = {
|
|
|
1954
2012
|
if (!params.name) {
|
|
1955
2013
|
throw new Error("Branch name is required for create action");
|
|
1956
2014
|
}
|
|
1957
|
-
|
|
2015
|
+
if (params.name.startsWith("-")) {
|
|
2016
|
+
throw new Error("Invalid branch name");
|
|
2017
|
+
}
|
|
2018
|
+
gitExec(["branch", params.name], params.cwd);
|
|
1958
2019
|
return Promise.resolve({ success: true, created: params.name });
|
|
1959
2020
|
}
|
|
1960
2021
|
case "switch": {
|
|
1961
2022
|
if (!params.name) {
|
|
1962
2023
|
throw new Error("Branch name is required for switch action");
|
|
1963
2024
|
}
|
|
1964
|
-
|
|
2025
|
+
if (params.name.startsWith("-")) {
|
|
2026
|
+
throw new Error("Invalid branch name");
|
|
2027
|
+
}
|
|
2028
|
+
gitExec(["checkout", params.name], params.cwd);
|
|
1965
2029
|
return Promise.resolve({ success: true, switched: params.name });
|
|
1966
2030
|
}
|
|
1967
2031
|
}
|
|
@@ -2266,17 +2330,28 @@ var AnthropicProvider = class {
|
|
|
2266
2330
|
continue;
|
|
2267
2331
|
}
|
|
2268
2332
|
if (message.role === "tool") {
|
|
2269
|
-
const
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2333
|
+
const toolUseId = message.toolCallId || "";
|
|
2334
|
+
const resultBlock = this.toolResultBlock(toolUseId, message.content);
|
|
2335
|
+
const prev = converted[converted.length - 1];
|
|
2336
|
+
if (prev && prev.role === "user" && Array.isArray(prev.content) && prev.content.some((b) => b.type === "tool_result")) {
|
|
2337
|
+
const assistant = converted[converted.length - 2];
|
|
2338
|
+
if (assistant && assistant.role === "assistant") {
|
|
2339
|
+
assistant.content = this.ensureToolUse(
|
|
2340
|
+
assistant.content,
|
|
2341
|
+
toolUseId,
|
|
2342
|
+
message.name
|
|
2343
|
+
);
|
|
2279
2344
|
}
|
|
2345
|
+
prev.content.push(resultBlock);
|
|
2346
|
+
} else if (prev && prev.role === "assistant") {
|
|
2347
|
+
prev.content = this.ensureToolUse(
|
|
2348
|
+
prev.content,
|
|
2349
|
+
toolUseId,
|
|
2350
|
+
message.name
|
|
2351
|
+
);
|
|
2352
|
+
converted.push({ role: "user", content: [resultBlock] });
|
|
2353
|
+
} else {
|
|
2354
|
+
converted.push({ role: "user", content: [resultBlock] });
|
|
2280
2355
|
}
|
|
2281
2356
|
continue;
|
|
2282
2357
|
}
|
|
@@ -2287,6 +2362,20 @@ var AnthropicProvider = class {
|
|
|
2287
2362
|
}
|
|
2288
2363
|
return converted;
|
|
2289
2364
|
}
|
|
2365
|
+
/**
|
|
2366
|
+
* Ensure an assistant message's content is a block array that includes a
|
|
2367
|
+
* `tool_use` block with the given id, backfilling one if missing.
|
|
2368
|
+
*/
|
|
2369
|
+
ensureToolUse(content, id, name) {
|
|
2370
|
+
const blocks = typeof content === "string" ? content ? [{ type: "text", text: content }] : [] : [...content];
|
|
2371
|
+
if (!blocks.some((b) => b.type === "tool_use" && b.id === id)) {
|
|
2372
|
+
blocks.push({ type: "tool_use", id, name: name || "tool", input: {} });
|
|
2373
|
+
}
|
|
2374
|
+
return blocks;
|
|
2375
|
+
}
|
|
2376
|
+
toolResultBlock(toolUseId, content) {
|
|
2377
|
+
return { type: "tool_result", tool_use_id: toolUseId, content };
|
|
2378
|
+
}
|
|
2290
2379
|
};
|
|
2291
2380
|
|
|
2292
2381
|
// src/providers/openai.ts
|
|
@@ -3550,7 +3639,7 @@ var Workflow = class {
|
|
|
3550
3639
|
if (attempt < maxAttempts && initialDelayMs > 0) {
|
|
3551
3640
|
const raw = backoff === "linear" ? initialDelayMs * attempt : initialDelayMs * 2 ** (attempt - 1);
|
|
3552
3641
|
await new Promise(
|
|
3553
|
-
(
|
|
3642
|
+
(resolve2) => setTimeout(resolve2, Math.min(raw, maxDelayMs))
|
|
3554
3643
|
);
|
|
3555
3644
|
}
|
|
3556
3645
|
}
|
|
@@ -4142,7 +4231,7 @@ var RateLimiter = class {
|
|
|
4142
4231
|
* Sleep helper
|
|
4143
4232
|
*/
|
|
4144
4233
|
sleep(ms) {
|
|
4145
|
-
return new Promise((
|
|
4234
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
4146
4235
|
}
|
|
4147
4236
|
};
|
|
4148
4237
|
var SlidingWindowRateLimiter = class {
|
|
@@ -4374,7 +4463,7 @@ var StdioTransport = class extends EventEmitter {
|
|
|
4374
4463
|
connected = false;
|
|
4375
4464
|
buffer = "";
|
|
4376
4465
|
async connect() {
|
|
4377
|
-
return new Promise((
|
|
4466
|
+
return new Promise((resolve2, reject) => {
|
|
4378
4467
|
try {
|
|
4379
4468
|
this.process = spawn(this.command, this.args, {
|
|
4380
4469
|
env: { ...process.env, ...this.env },
|
|
@@ -4397,7 +4486,7 @@ var StdioTransport = class extends EventEmitter {
|
|
|
4397
4486
|
});
|
|
4398
4487
|
this.connected = true;
|
|
4399
4488
|
this.emit("connect");
|
|
4400
|
-
|
|
4489
|
+
resolve2();
|
|
4401
4490
|
} catch (error) {
|
|
4402
4491
|
reject(error);
|
|
4403
4492
|
}
|
|
@@ -4445,7 +4534,7 @@ var SSETransport = class extends EventEmitter {
|
|
|
4445
4534
|
eventSource = null;
|
|
4446
4535
|
connected = false;
|
|
4447
4536
|
async connect() {
|
|
4448
|
-
return new Promise((
|
|
4537
|
+
return new Promise((resolve2, reject) => {
|
|
4449
4538
|
try {
|
|
4450
4539
|
if (typeof EventSource === "undefined") {
|
|
4451
4540
|
throw new Error(
|
|
@@ -4456,7 +4545,7 @@ var SSETransport = class extends EventEmitter {
|
|
|
4456
4545
|
this.eventSource.onopen = () => {
|
|
4457
4546
|
this.connected = true;
|
|
4458
4547
|
this.emit("connect");
|
|
4459
|
-
|
|
4548
|
+
resolve2();
|
|
4460
4549
|
};
|
|
4461
4550
|
this.eventSource.onmessage = (event) => {
|
|
4462
4551
|
try {
|
|
@@ -4667,9 +4756,9 @@ var MCPClient = class extends EventEmitter2 {
|
|
|
4667
4756
|
if (!this.transport || !this.transport.isConnected()) {
|
|
4668
4757
|
throw new Error("Not connected to MCP server");
|
|
4669
4758
|
}
|
|
4670
|
-
return new Promise((
|
|
4759
|
+
return new Promise((resolve2, reject) => {
|
|
4671
4760
|
const id = message.id;
|
|
4672
|
-
this.pendingRequests.set(id, { resolve, reject });
|
|
4761
|
+
this.pendingRequests.set(id, { resolve: resolve2, reject });
|
|
4673
4762
|
try {
|
|
4674
4763
|
this.transport.send(message);
|
|
4675
4764
|
} catch (error) {
|
|
@@ -6189,12 +6278,12 @@ var OpenAIWhisperProvider = class {
|
|
|
6189
6278
|
};
|
|
6190
6279
|
|
|
6191
6280
|
// src/voice/stt/local-whisper.ts
|
|
6192
|
-
import {
|
|
6281
|
+
import { execFile } from "child_process";
|
|
6193
6282
|
import { writeFileSync as writeFileSync2, unlinkSync, existsSync } from "fs";
|
|
6194
6283
|
import { tmpdir } from "os";
|
|
6195
6284
|
import { join as join4 } from "path";
|
|
6196
6285
|
import { promisify } from "util";
|
|
6197
|
-
var
|
|
6286
|
+
var execFileAsync = promisify(execFile);
|
|
6198
6287
|
var LocalWhisperProvider = class {
|
|
6199
6288
|
whisperPath;
|
|
6200
6289
|
modelPath;
|
|
@@ -6220,16 +6309,23 @@ var LocalWhisperProvider = class {
|
|
|
6220
6309
|
throw new Error(`Audio file not found: ${audioPath}`);
|
|
6221
6310
|
}
|
|
6222
6311
|
const model = config?.model || "base";
|
|
6223
|
-
const
|
|
6312
|
+
const languageArgs = config?.language ? ["--language", config.language] : [];
|
|
6224
6313
|
const outputFormat = config?.responseFormat || "txt";
|
|
6225
|
-
let
|
|
6314
|
+
let args;
|
|
6226
6315
|
if (this.whisperPath.includes("whisper.cpp")) {
|
|
6227
6316
|
const modelFile = this.modelPath || `ggml-${model}.bin`;
|
|
6228
|
-
|
|
6317
|
+
args = ["-m", modelFile, ...languageArgs, "-otxt", audioPath];
|
|
6229
6318
|
} else {
|
|
6230
|
-
|
|
6231
|
-
|
|
6232
|
-
|
|
6319
|
+
args = [
|
|
6320
|
+
audioPath,
|
|
6321
|
+
"--model",
|
|
6322
|
+
model,
|
|
6323
|
+
...languageArgs,
|
|
6324
|
+
"--output_format",
|
|
6325
|
+
outputFormat
|
|
6326
|
+
];
|
|
6327
|
+
}
|
|
6328
|
+
const { stdout } = await execFileAsync(this.whisperPath, args, {
|
|
6233
6329
|
maxBuffer: 10 * 1024 * 1024
|
|
6234
6330
|
// 10MB buffer
|
|
6235
6331
|
});
|
|
@@ -6271,7 +6367,7 @@ var LocalWhisperProvider = class {
|
|
|
6271
6367
|
*/
|
|
6272
6368
|
async isInstalled() {
|
|
6273
6369
|
try {
|
|
6274
|
-
await
|
|
6370
|
+
await execFileAsync(this.whisperPath, ["--help"]);
|
|
6275
6371
|
return true;
|
|
6276
6372
|
} catch {
|
|
6277
6373
|
return false;
|
|
@@ -6574,12 +6670,12 @@ var ElevenLabsTTSProvider = class {
|
|
|
6574
6670
|
};
|
|
6575
6671
|
|
|
6576
6672
|
// src/voice/tts/piper-tts.ts
|
|
6577
|
-
import {
|
|
6578
|
-
import {
|
|
6673
|
+
import { execFile as execFile2, spawn as spawn2 } from "child_process";
|
|
6674
|
+
import { readFileSync, unlinkSync as unlinkSync2, existsSync as existsSync2 } from "fs";
|
|
6579
6675
|
import { tmpdir as tmpdir2 } from "os";
|
|
6580
6676
|
import { join as join5 } from "path";
|
|
6581
6677
|
import { promisify as promisify2 } from "util";
|
|
6582
|
-
var
|
|
6678
|
+
var execFileAsync2 = promisify2(execFile2);
|
|
6583
6679
|
var PiperTTSProvider = class {
|
|
6584
6680
|
piperPath;
|
|
6585
6681
|
modelPath;
|
|
@@ -6593,26 +6689,40 @@ var PiperTTSProvider = class {
|
|
|
6593
6689
|
* Synthesize text to speech
|
|
6594
6690
|
*/
|
|
6595
6691
|
async synthesize(text, config) {
|
|
6692
|
+
const outputPath = join5(tmpdir2(), `speech-${Date.now()}.wav`);
|
|
6596
6693
|
try {
|
|
6597
|
-
const outputPath = join5(tmpdir2(), `speech-${Date.now()}.wav`);
|
|
6598
6694
|
const model = this.modelPath || config?.model;
|
|
6599
6695
|
if (!model) {
|
|
6600
6696
|
throw new Error("Model path is required for Piper TTS");
|
|
6601
6697
|
}
|
|
6602
6698
|
const modelConfig = this.configPath || model.replace(".onnx", ".json");
|
|
6603
|
-
const
|
|
6604
|
-
|
|
6605
|
-
|
|
6606
|
-
|
|
6607
|
-
|
|
6608
|
-
|
|
6699
|
+
const args = [
|
|
6700
|
+
"--model",
|
|
6701
|
+
model,
|
|
6702
|
+
"--config",
|
|
6703
|
+
modelConfig,
|
|
6704
|
+
"--output_file",
|
|
6705
|
+
outputPath
|
|
6706
|
+
];
|
|
6707
|
+
await new Promise((resolve2, reject) => {
|
|
6708
|
+
const child = spawn2(this.piperPath, args, {
|
|
6709
|
+
stdio: ["pipe", "ignore", "pipe"]
|
|
6710
|
+
});
|
|
6711
|
+
let stderr = "";
|
|
6712
|
+
child.stderr?.on("data", (chunk) => {
|
|
6713
|
+
stderr += String(chunk);
|
|
6714
|
+
});
|
|
6715
|
+
child.on("error", reject);
|
|
6716
|
+
child.on("close", (code) => {
|
|
6717
|
+
if (code === 0) resolve2();
|
|
6718
|
+
else reject(new Error(`piper exited with code ${code}: ${stderr}`));
|
|
6719
|
+
});
|
|
6720
|
+
child.stdin?.end(text);
|
|
6609
6721
|
});
|
|
6610
6722
|
if (!existsSync2(outputPath)) {
|
|
6611
6723
|
throw new Error("Piper failed to generate audio file");
|
|
6612
6724
|
}
|
|
6613
6725
|
const audio = readFileSync(outputPath);
|
|
6614
|
-
unlinkSync2(outputPath);
|
|
6615
|
-
unlinkSync2(textPath);
|
|
6616
6726
|
return {
|
|
6617
6727
|
audio,
|
|
6618
6728
|
format: "wav",
|
|
@@ -6622,6 +6732,10 @@ var PiperTTSProvider = class {
|
|
|
6622
6732
|
throw new Error(
|
|
6623
6733
|
`Piper TTS synthesis failed: ${error instanceof Error ? error.message : String(error)}`
|
|
6624
6734
|
);
|
|
6735
|
+
} finally {
|
|
6736
|
+
if (existsSync2(outputPath)) {
|
|
6737
|
+
unlinkSync2(outputPath);
|
|
6738
|
+
}
|
|
6625
6739
|
}
|
|
6626
6740
|
}
|
|
6627
6741
|
/**
|
|
@@ -6635,7 +6749,7 @@ var PiperTTSProvider = class {
|
|
|
6635
6749
|
*/
|
|
6636
6750
|
async isInstalled() {
|
|
6637
6751
|
try {
|
|
6638
|
-
await
|
|
6752
|
+
await execFileAsync2(this.piperPath, ["--version"]);
|
|
6639
6753
|
return true;
|
|
6640
6754
|
} catch {
|
|
6641
6755
|
return false;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lov3kaizen/agentsea-core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "AgentSea - Unite and orchestrate AI agents. A production-ready ADK for building agentic AI applications with multi-provider support.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"winston": "^3.19.0",
|
|
26
26
|
"ioredis": "^5.11.1",
|
|
27
27
|
"marked": "^12.0.2",
|
|
28
|
-
"@lov3kaizen/agentsea-types": "1.
|
|
28
|
+
"@lov3kaizen/agentsea-types": "1.2.0"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@types/node": "^20.19.0",
|