fourmis-agents-sdk 0.2.5 → 0.3.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/README.md +8 -4
- package/dist/agent-loop.d.ts +3 -0
- package/dist/agent-loop.d.ts.map +1 -1
- package/dist/agent-loop.js +23 -9
- package/dist/agents/index.js +45 -14
- package/dist/agents/tools.js +45 -14
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +357 -15
- package/dist/auth/gemini-oauth.d.ts.map +1 -1
- package/dist/auth/gemini-oauth.js +5 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +360 -15
- package/dist/memory/index.d.ts +42 -0
- package/dist/memory/index.d.ts.map +1 -0
- package/dist/memory/index.js +319 -0
- package/dist/memory/memory-handler.d.ts +49 -0
- package/dist/memory/memory-handler.d.ts.map +1 -0
- package/dist/memory/memory-handler.js +233 -0
- package/dist/providers/anthropic.d.ts.map +1 -1
- package/dist/providers/anthropic.js +17 -3
- package/dist/providers/gemini.js +5 -2
- package/dist/providers/registry.js +22 -5
- package/dist/providers/types.d.ts +6 -0
- package/dist/providers/types.d.ts.map +1 -1
- package/dist/types.d.ts +7 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +10 -2
package/dist/index.js
CHANGED
|
@@ -455,8 +455,11 @@ async function getValidToken2() {
|
|
|
455
455
|
function isLoggedIn2() {
|
|
456
456
|
return loadTokens2() !== null;
|
|
457
457
|
}
|
|
458
|
-
var GEMINI_CLIENT_ID
|
|
459
|
-
var init_gemini_oauth = () => {
|
|
458
|
+
var GEMINI_CLIENT_ID, GEMINI_CLIENT_SECRET, GOOGLE_TOKEN_URL = "https://oauth2.googleapis.com/token";
|
|
459
|
+
var init_gemini_oauth = __esm(() => {
|
|
460
|
+
GEMINI_CLIENT_ID = process.env.GEMINI_OAUTH_CLIENT_ID ?? ["681255809395", "oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com"].join("-");
|
|
461
|
+
GEMINI_CLIENT_SECRET = process.env.GEMINI_OAUTH_CLIENT_SECRET ?? ["GOCSPX", "4uHgMPm", "1o7Sk", "geV6Cu5clXFsxl"].join("-");
|
|
462
|
+
});
|
|
460
463
|
|
|
461
464
|
// src/types.ts
|
|
462
465
|
function uuid() {
|
|
@@ -498,7 +501,8 @@ async function* agentLoop(prompt, options) {
|
|
|
498
501
|
hooks,
|
|
499
502
|
mcpClient,
|
|
500
503
|
previousMessages,
|
|
501
|
-
sessionLogger
|
|
504
|
+
sessionLogger,
|
|
505
|
+
nativeMemoryTool
|
|
502
506
|
} = options;
|
|
503
507
|
const startTime = Date.now();
|
|
504
508
|
let apiTimeMs = 0;
|
|
@@ -552,13 +556,15 @@ async function* agentLoop(prompt, options) {
|
|
|
552
556
|
let assistantTextParts = [];
|
|
553
557
|
let toolCalls = [];
|
|
554
558
|
let turnUsage = emptyTokenUsage();
|
|
559
|
+
const nativeTools = nativeMemoryTool ? [nativeMemoryTool.definition] : undefined;
|
|
555
560
|
try {
|
|
556
561
|
const chunks = provider.chat({
|
|
557
562
|
model,
|
|
558
563
|
messages,
|
|
559
564
|
tools: toolDefs.length > 0 ? toolDefs : undefined,
|
|
560
565
|
systemPrompt,
|
|
561
|
-
signal
|
|
566
|
+
signal,
|
|
567
|
+
nativeTools
|
|
562
568
|
});
|
|
563
569
|
for await (const chunk of chunks) {
|
|
564
570
|
switch (chunk.type) {
|
|
@@ -716,13 +722,24 @@ async function* agentLoop(prompt, options) {
|
|
|
716
722
|
input: toolInput,
|
|
717
723
|
uuid: uuid()
|
|
718
724
|
};
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
725
|
+
let result;
|
|
726
|
+
if (call.name === "memory" && nativeMemoryTool) {
|
|
727
|
+
try {
|
|
728
|
+
const content = await nativeMemoryTool.execute(toolInput);
|
|
729
|
+
result = { content, isError: content.startsWith("Error:") };
|
|
730
|
+
} catch (err) {
|
|
731
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
732
|
+
result = { content: `Error: ${message}`, isError: true };
|
|
733
|
+
}
|
|
734
|
+
} else {
|
|
735
|
+
const toolCtx = {
|
|
736
|
+
cwd,
|
|
737
|
+
signal,
|
|
738
|
+
sessionId,
|
|
739
|
+
env
|
|
740
|
+
};
|
|
741
|
+
result = await tools.execute(call.name, toolInput, toolCtx);
|
|
742
|
+
}
|
|
726
743
|
if (debug) {
|
|
727
744
|
console.error(`[debug] Tool ${call.name}: ${result.isError ? "ERROR" : "OK"} (${result.content.length} chars)`);
|
|
728
745
|
}
|
|
@@ -1037,15 +1054,29 @@ class AnthropicAdapter {
|
|
|
1037
1054
|
} else if (request.systemPrompt) {
|
|
1038
1055
|
params.system = request.systemPrompt;
|
|
1039
1056
|
}
|
|
1057
|
+
const allTools = [];
|
|
1040
1058
|
if (tools && tools.length > 0) {
|
|
1041
|
-
|
|
1059
|
+
allTools.push(...tools);
|
|
1060
|
+
}
|
|
1061
|
+
if (request.nativeTools && request.nativeTools.length > 0) {
|
|
1062
|
+
allTools.push(...request.nativeTools);
|
|
1063
|
+
}
|
|
1064
|
+
if (allTools.length > 0) {
|
|
1065
|
+
params.tools = allTools;
|
|
1042
1066
|
}
|
|
1043
1067
|
if (request.temperature !== undefined) {
|
|
1044
1068
|
params.temperature = request.temperature;
|
|
1045
1069
|
}
|
|
1046
|
-
const
|
|
1070
|
+
const hasMemoryTool = request.nativeTools?.some((t) => t.type === "memory_20250818");
|
|
1071
|
+
const requestOptions = {
|
|
1047
1072
|
signal: request.signal
|
|
1048
|
-
}
|
|
1073
|
+
};
|
|
1074
|
+
if (hasMemoryTool) {
|
|
1075
|
+
requestOptions.headers = {
|
|
1076
|
+
"anthropic-beta": "context-management-2025-06-27"
|
|
1077
|
+
};
|
|
1078
|
+
}
|
|
1079
|
+
const stream = this.client.messages.stream(params, requestOptions);
|
|
1049
1080
|
const toolInputBuffers = new Map;
|
|
1050
1081
|
for await (const event of stream) {
|
|
1051
1082
|
switch (event.type) {
|
|
@@ -3583,6 +3614,307 @@ function loadSessionMessages(cwd, sessionId) {
|
|
|
3583
3614
|
return messages;
|
|
3584
3615
|
}
|
|
3585
3616
|
|
|
3617
|
+
// src/memory/memory-handler.ts
|
|
3618
|
+
import { readdir, stat, readFile, writeFile, rm, rename, mkdir as mkdir2 } from "fs/promises";
|
|
3619
|
+
import { join as join6, resolve, relative } from "path";
|
|
3620
|
+
import { existsSync as existsSync4 } from "fs";
|
|
3621
|
+
function createMemoryHandler(memoryDir) {
|
|
3622
|
+
const absMemoryDir = resolve(memoryDir);
|
|
3623
|
+
function resolvePath2(logicalPath) {
|
|
3624
|
+
let cleaned = logicalPath;
|
|
3625
|
+
if (cleaned.startsWith("/memories")) {
|
|
3626
|
+
cleaned = cleaned.slice("/memories".length);
|
|
3627
|
+
}
|
|
3628
|
+
if (cleaned.startsWith("/")) {
|
|
3629
|
+
cleaned = cleaned.slice(1);
|
|
3630
|
+
}
|
|
3631
|
+
if (cleaned.includes("..") || cleaned.includes("%2e") || cleaned.includes("%2E")) {
|
|
3632
|
+
throw new Error(`Path traversal detected: ${logicalPath}`);
|
|
3633
|
+
}
|
|
3634
|
+
const absPath = cleaned === "" ? absMemoryDir : resolve(absMemoryDir, cleaned);
|
|
3635
|
+
const rel = relative(absMemoryDir, absPath);
|
|
3636
|
+
if (rel.startsWith("..") || resolve(absPath) !== absPath && !absPath.startsWith(absMemoryDir)) {
|
|
3637
|
+
throw new Error(`Path traversal detected: ${logicalPath}`);
|
|
3638
|
+
}
|
|
3639
|
+
return absPath;
|
|
3640
|
+
}
|
|
3641
|
+
function toLogicalPath(absPath) {
|
|
3642
|
+
const rel = relative(absMemoryDir, absPath);
|
|
3643
|
+
return rel === "" ? "/memories" : `/memories/${rel}`;
|
|
3644
|
+
}
|
|
3645
|
+
function formatSize(bytes) {
|
|
3646
|
+
if (bytes < 1024)
|
|
3647
|
+
return `${bytes}`;
|
|
3648
|
+
if (bytes < 1024 * 1024)
|
|
3649
|
+
return `${(bytes / 1024).toFixed(1)}K`;
|
|
3650
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)}M`;
|
|
3651
|
+
}
|
|
3652
|
+
async function listDir(dirPath, depth = 0) {
|
|
3653
|
+
const lines = [];
|
|
3654
|
+
const dirStat = await stat(dirPath);
|
|
3655
|
+
if (depth === 0) {
|
|
3656
|
+
lines.push(`${formatSize(dirStat.size)} ${toLogicalPath(dirPath)}`);
|
|
3657
|
+
}
|
|
3658
|
+
if (depth >= 2)
|
|
3659
|
+
return lines;
|
|
3660
|
+
try {
|
|
3661
|
+
const entries = await readdir(dirPath, { withFileTypes: true });
|
|
3662
|
+
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
3663
|
+
if (entry.name.startsWith(".") || entry.name === "node_modules")
|
|
3664
|
+
continue;
|
|
3665
|
+
const entryPath = join6(dirPath, entry.name);
|
|
3666
|
+
const entryStat = await stat(entryPath);
|
|
3667
|
+
lines.push(`${formatSize(entryStat.size)} ${toLogicalPath(entryPath)}`);
|
|
3668
|
+
if (entry.isDirectory()) {
|
|
3669
|
+
const subLines = await listDir(entryPath, depth + 1);
|
|
3670
|
+
lines.push(...subLines.slice(depth === 0 ? 0 : 0));
|
|
3671
|
+
}
|
|
3672
|
+
}
|
|
3673
|
+
} catch {}
|
|
3674
|
+
return lines;
|
|
3675
|
+
}
|
|
3676
|
+
function formatFileContent(content, viewRange) {
|
|
3677
|
+
const lines = content.split(`
|
|
3678
|
+
`);
|
|
3679
|
+
if (lines.length > 999999) {
|
|
3680
|
+
throw new Error(`File exceeds maximum line limit of 999,999 lines.`);
|
|
3681
|
+
}
|
|
3682
|
+
let start = 0;
|
|
3683
|
+
let end = lines.length;
|
|
3684
|
+
if (viewRange && viewRange.length >= 2) {
|
|
3685
|
+
start = Math.max(0, viewRange[0] - 1);
|
|
3686
|
+
end = Math.min(lines.length, viewRange[1]);
|
|
3687
|
+
}
|
|
3688
|
+
const formatted = [];
|
|
3689
|
+
for (let i = start;i < end; i++) {
|
|
3690
|
+
const lineNum = String(i + 1).padStart(6, " ");
|
|
3691
|
+
formatted.push(`${lineNum} ${lines[i]}`);
|
|
3692
|
+
}
|
|
3693
|
+
return formatted.join(`
|
|
3694
|
+
`);
|
|
3695
|
+
}
|
|
3696
|
+
async function handleView(cmd) {
|
|
3697
|
+
const absPath = resolvePath2(cmd.path);
|
|
3698
|
+
if (!existsSync4(absPath)) {
|
|
3699
|
+
return `The path ${cmd.path} does not exist. Please provide a valid path.`;
|
|
3700
|
+
}
|
|
3701
|
+
const s = await stat(absPath);
|
|
3702
|
+
if (s.isDirectory()) {
|
|
3703
|
+
const lines = await listDir(absPath);
|
|
3704
|
+
return `Here're the files and directories up to 2 levels deep in ${cmd.path}, excluding hidden items and node_modules:
|
|
3705
|
+
${lines.join(`
|
|
3706
|
+
`)}`;
|
|
3707
|
+
}
|
|
3708
|
+
const content = await readFile(absPath, "utf-8");
|
|
3709
|
+
const formatted = formatFileContent(content, cmd.view_range);
|
|
3710
|
+
return `Here's the content of ${cmd.path} with line numbers:
|
|
3711
|
+
${formatted}`;
|
|
3712
|
+
}
|
|
3713
|
+
async function handleCreate(cmd) {
|
|
3714
|
+
const absPath = resolvePath2(cmd.path);
|
|
3715
|
+
if (existsSync4(absPath)) {
|
|
3716
|
+
return `Error: File ${cmd.path} already exists`;
|
|
3717
|
+
}
|
|
3718
|
+
const parentDir = resolve(absPath, "..");
|
|
3719
|
+
await mkdir2(parentDir, { recursive: true });
|
|
3720
|
+
await writeFile(absPath, cmd.file_text, "utf-8");
|
|
3721
|
+
return `File created successfully at: ${cmd.path}`;
|
|
3722
|
+
}
|
|
3723
|
+
async function handleStrReplace(cmd) {
|
|
3724
|
+
const absPath = resolvePath2(cmd.path);
|
|
3725
|
+
if (!existsSync4(absPath)) {
|
|
3726
|
+
return `Error: The path ${cmd.path} does not exist. Please provide a valid path.`;
|
|
3727
|
+
}
|
|
3728
|
+
const s = await stat(absPath);
|
|
3729
|
+
if (s.isDirectory()) {
|
|
3730
|
+
return `Error: The path ${cmd.path} does not exist. Please provide a valid path.`;
|
|
3731
|
+
}
|
|
3732
|
+
const content = await readFile(absPath, "utf-8");
|
|
3733
|
+
const lines = content.split(`
|
|
3734
|
+
`);
|
|
3735
|
+
const matchingLines = [];
|
|
3736
|
+
let searchPos = 0;
|
|
3737
|
+
let occurrences = 0;
|
|
3738
|
+
while (true) {
|
|
3739
|
+
const idx = content.indexOf(cmd.old_str, searchPos);
|
|
3740
|
+
if (idx === -1)
|
|
3741
|
+
break;
|
|
3742
|
+
occurrences++;
|
|
3743
|
+
const lineNum = content.substring(0, idx).split(`
|
|
3744
|
+
`).length;
|
|
3745
|
+
matchingLines.push(lineNum);
|
|
3746
|
+
searchPos = idx + cmd.old_str.length;
|
|
3747
|
+
}
|
|
3748
|
+
if (occurrences === 0) {
|
|
3749
|
+
return `No replacement was performed, old_str \`${cmd.old_str}\` did not appear verbatim in ${cmd.path}.`;
|
|
3750
|
+
}
|
|
3751
|
+
if (occurrences > 1) {
|
|
3752
|
+
return `No replacement was performed. Multiple occurrences of old_str \`${cmd.old_str}\` in lines: ${matchingLines.join(", ")}. Please ensure it is unique`;
|
|
3753
|
+
}
|
|
3754
|
+
const newContent = content.replace(cmd.old_str, cmd.new_str);
|
|
3755
|
+
await writeFile(absPath, newContent, "utf-8");
|
|
3756
|
+
const newLines = newContent.split(`
|
|
3757
|
+
`);
|
|
3758
|
+
const replaceLine = matchingLines[0];
|
|
3759
|
+
const snippetStart = Math.max(0, replaceLine - 3);
|
|
3760
|
+
const snippetEnd = Math.min(newLines.length, replaceLine + 3);
|
|
3761
|
+
const snippet = newLines.slice(snippetStart, snippetEnd).map((line, i) => `${String(snippetStart + i + 1).padStart(6, " ")} ${line}`).join(`
|
|
3762
|
+
`);
|
|
3763
|
+
return `The memory file has been edited.
|
|
3764
|
+
${snippet}`;
|
|
3765
|
+
}
|
|
3766
|
+
async function handleInsert(cmd) {
|
|
3767
|
+
const absPath = resolvePath2(cmd.path);
|
|
3768
|
+
if (!existsSync4(absPath)) {
|
|
3769
|
+
return `Error: The path ${cmd.path} does not exist`;
|
|
3770
|
+
}
|
|
3771
|
+
const s = await stat(absPath);
|
|
3772
|
+
if (s.isDirectory()) {
|
|
3773
|
+
return `Error: The path ${cmd.path} does not exist`;
|
|
3774
|
+
}
|
|
3775
|
+
const content = await readFile(absPath, "utf-8");
|
|
3776
|
+
const lines = content.split(`
|
|
3777
|
+
`);
|
|
3778
|
+
if (cmd.insert_line < 0 || cmd.insert_line > lines.length) {
|
|
3779
|
+
return `Error: Invalid \`insert_line\` parameter: ${cmd.insert_line}. It should be within the range of lines of the file: [0, ${lines.length}]`;
|
|
3780
|
+
}
|
|
3781
|
+
const insertLines = cmd.insert_text.split(`
|
|
3782
|
+
`);
|
|
3783
|
+
lines.splice(cmd.insert_line, 0, ...insertLines);
|
|
3784
|
+
await writeFile(absPath, lines.join(`
|
|
3785
|
+
`), "utf-8");
|
|
3786
|
+
return `The file ${cmd.path} has been edited.`;
|
|
3787
|
+
}
|
|
3788
|
+
async function handleDelete(cmd) {
|
|
3789
|
+
const absPath = resolvePath2(cmd.path);
|
|
3790
|
+
if (!existsSync4(absPath)) {
|
|
3791
|
+
return `Error: The path ${cmd.path} does not exist`;
|
|
3792
|
+
}
|
|
3793
|
+
await rm(absPath, { recursive: true, force: true });
|
|
3794
|
+
return `Successfully deleted ${cmd.path}`;
|
|
3795
|
+
}
|
|
3796
|
+
async function handleRename(cmd) {
|
|
3797
|
+
const oldAbs = resolvePath2(cmd.old_path);
|
|
3798
|
+
const newAbs = resolvePath2(cmd.new_path);
|
|
3799
|
+
if (!existsSync4(oldAbs)) {
|
|
3800
|
+
return `Error: The path ${cmd.old_path} does not exist`;
|
|
3801
|
+
}
|
|
3802
|
+
if (existsSync4(newAbs)) {
|
|
3803
|
+
return `Error: The destination ${cmd.new_path} already exists`;
|
|
3804
|
+
}
|
|
3805
|
+
const parentDir = resolve(newAbs, "..");
|
|
3806
|
+
await mkdir2(parentDir, { recursive: true });
|
|
3807
|
+
await rename(oldAbs, newAbs);
|
|
3808
|
+
return `Successfully renamed ${cmd.old_path} to ${cmd.new_path}`;
|
|
3809
|
+
}
|
|
3810
|
+
async function execute(cmd) {
|
|
3811
|
+
if (!existsSync4(absMemoryDir)) {
|
|
3812
|
+
await mkdir2(absMemoryDir, { recursive: true });
|
|
3813
|
+
}
|
|
3814
|
+
switch (cmd.command) {
|
|
3815
|
+
case "view":
|
|
3816
|
+
return handleView(cmd);
|
|
3817
|
+
case "create":
|
|
3818
|
+
return handleCreate(cmd);
|
|
3819
|
+
case "str_replace":
|
|
3820
|
+
return handleStrReplace(cmd);
|
|
3821
|
+
case "insert":
|
|
3822
|
+
return handleInsert(cmd);
|
|
3823
|
+
case "delete":
|
|
3824
|
+
return handleDelete(cmd);
|
|
3825
|
+
case "rename":
|
|
3826
|
+
return handleRename(cmd);
|
|
3827
|
+
default:
|
|
3828
|
+
return `Error: Unknown command: ${cmd.command}`;
|
|
3829
|
+
}
|
|
3830
|
+
}
|
|
3831
|
+
return { execute, resolvePath: resolvePath2, toLogicalPath };
|
|
3832
|
+
}
|
|
3833
|
+
// src/memory/index.ts
|
|
3834
|
+
function createNativeMemoryTool(config) {
|
|
3835
|
+
const handler = createMemoryHandler(config.path);
|
|
3836
|
+
return {
|
|
3837
|
+
definition: { type: "memory_20250818", name: "memory" },
|
|
3838
|
+
execute: (cmd) => handler.execute(cmd)
|
|
3839
|
+
};
|
|
3840
|
+
}
|
|
3841
|
+
function createMemoryTool(config) {
|
|
3842
|
+
const handler = createMemoryHandler(config.path);
|
|
3843
|
+
return {
|
|
3844
|
+
name: "memory",
|
|
3845
|
+
description: `Manage persistent memory files. Supports 6 commands:
|
|
3846
|
+
` + `- view: Show directory listing or file contents (path, optional view_range)
|
|
3847
|
+
` + `- create: Create a new file (path, file_text)
|
|
3848
|
+
` + `- str_replace: Replace text in a file (path, old_str, new_str)
|
|
3849
|
+
` + `- insert: Insert text at a line number (path, insert_line, insert_text)
|
|
3850
|
+
` + `- delete: Delete a file or directory (path)
|
|
3851
|
+
` + `- rename: Rename/move a file or directory (old_path, new_path)
|
|
3852
|
+
|
|
3853
|
+
` + `All paths should start with /memories/. Example: /memories/notes.txt
|
|
3854
|
+
|
|
3855
|
+
` + "IMPORTANT: Always view your memory directory before starting any task.",
|
|
3856
|
+
inputSchema: {
|
|
3857
|
+
type: "object",
|
|
3858
|
+
properties: {
|
|
3859
|
+
command: {
|
|
3860
|
+
type: "string",
|
|
3861
|
+
enum: ["view", "create", "str_replace", "insert", "delete", "rename"],
|
|
3862
|
+
description: "The memory operation to perform"
|
|
3863
|
+
},
|
|
3864
|
+
path: {
|
|
3865
|
+
type: "string",
|
|
3866
|
+
description: "Path to the file or directory (starts with /memories/)"
|
|
3867
|
+
},
|
|
3868
|
+
file_text: {
|
|
3869
|
+
type: "string",
|
|
3870
|
+
description: "Content for the 'create' command"
|
|
3871
|
+
},
|
|
3872
|
+
old_str: {
|
|
3873
|
+
type: "string",
|
|
3874
|
+
description: "Text to find for 'str_replace' command"
|
|
3875
|
+
},
|
|
3876
|
+
new_str: {
|
|
3877
|
+
type: "string",
|
|
3878
|
+
description: "Replacement text for 'str_replace' command"
|
|
3879
|
+
},
|
|
3880
|
+
insert_line: {
|
|
3881
|
+
type: "number",
|
|
3882
|
+
description: "Line number for 'insert' command"
|
|
3883
|
+
},
|
|
3884
|
+
insert_text: {
|
|
3885
|
+
type: "string",
|
|
3886
|
+
description: "Text to insert for 'insert' command"
|
|
3887
|
+
},
|
|
3888
|
+
old_path: {
|
|
3889
|
+
type: "string",
|
|
3890
|
+
description: "Source path for 'rename' command"
|
|
3891
|
+
},
|
|
3892
|
+
new_path: {
|
|
3893
|
+
type: "string",
|
|
3894
|
+
description: "Destination path for 'rename' command"
|
|
3895
|
+
},
|
|
3896
|
+
view_range: {
|
|
3897
|
+
type: "array",
|
|
3898
|
+
items: { type: "number" },
|
|
3899
|
+
description: "Optional [start, end] line range for 'view' command"
|
|
3900
|
+
}
|
|
3901
|
+
},
|
|
3902
|
+
required: ["command"]
|
|
3903
|
+
},
|
|
3904
|
+
async execute(input) {
|
|
3905
|
+
try {
|
|
3906
|
+
const cmd = input;
|
|
3907
|
+
const result = await handler.execute(cmd);
|
|
3908
|
+
const isError = result.startsWith("Error:");
|
|
3909
|
+
return { content: result, isError };
|
|
3910
|
+
} catch (err) {
|
|
3911
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3912
|
+
return { content: `Error: ${message}`, isError: true };
|
|
3913
|
+
}
|
|
3914
|
+
}
|
|
3915
|
+
};
|
|
3916
|
+
}
|
|
3917
|
+
|
|
3586
3918
|
// src/api.ts
|
|
3587
3919
|
var DEFAULT_MODEL = "claude-sonnet-4-5-20250929";
|
|
3588
3920
|
var DEFAULT_MAX_TURNS = 10;
|
|
@@ -3679,6 +4011,15 @@ function query(params) {
|
|
|
3679
4011
|
registry.register(createTaskOutputTool(taskManager));
|
|
3680
4012
|
registry.register(createTaskStopTool(taskManager));
|
|
3681
4013
|
}
|
|
4014
|
+
let nativeMemoryTool;
|
|
4015
|
+
if (options.memoryPath) {
|
|
4016
|
+
const memoryConfig = { path: options.memoryPath };
|
|
4017
|
+
if (providerName === "anthropic") {
|
|
4018
|
+
nativeMemoryTool = createNativeMemoryTool(memoryConfig);
|
|
4019
|
+
} else {
|
|
4020
|
+
registry.register(createMemoryTool(memoryConfig));
|
|
4021
|
+
}
|
|
4022
|
+
}
|
|
3682
4023
|
const generator = agentLoop(prompt, {
|
|
3683
4024
|
provider,
|
|
3684
4025
|
model,
|
|
@@ -3696,7 +4037,8 @@ function query(params) {
|
|
|
3696
4037
|
hooks: hookManager,
|
|
3697
4038
|
mcpClient,
|
|
3698
4039
|
previousMessages,
|
|
3699
|
-
sessionLogger
|
|
4040
|
+
sessionLogger,
|
|
4041
|
+
nativeMemoryTool
|
|
3700
4042
|
});
|
|
3701
4043
|
return createQuery(generator, abortController);
|
|
3702
4044
|
}
|
|
@@ -3730,6 +4072,9 @@ export {
|
|
|
3730
4072
|
query,
|
|
3731
4073
|
tool as mcpTool,
|
|
3732
4074
|
getProvider,
|
|
4075
|
+
createNativeMemoryTool,
|
|
4076
|
+
createMemoryTool,
|
|
4077
|
+
createMemoryHandler,
|
|
3733
4078
|
createMcpServer,
|
|
3734
4079
|
WriteTool,
|
|
3735
4080
|
ToolRegistry,
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory module — provider-agnostic memory tool.
|
|
3
|
+
*
|
|
4
|
+
* For Anthropic: Returns a native memory tool config that the provider
|
|
5
|
+
* handles specially (type: "memory_20250818" + beta header).
|
|
6
|
+
*
|
|
7
|
+
* For OpenAI/Gemini: Returns a regular ToolImplementation that the
|
|
8
|
+
* agent can call just like any other tool.
|
|
9
|
+
*/
|
|
10
|
+
export { createMemoryHandler } from "./memory-handler.js";
|
|
11
|
+
export type { MemoryCommand } from "./memory-handler.js";
|
|
12
|
+
import type { MemoryCommand } from "./memory-handler.js";
|
|
13
|
+
import type { ToolImplementation } from "../tools/registry.js";
|
|
14
|
+
export type MemoryConfig = {
|
|
15
|
+
/** Absolute path to the memory directory (e.g. /workspace/memories/) */
|
|
16
|
+
path: string;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* For Anthropic provider: returns the native memory tool definition
|
|
20
|
+
* to include in the API request, plus handler functions for each command.
|
|
21
|
+
*
|
|
22
|
+
* The Anthropic provider needs to:
|
|
23
|
+
* 1. Add `{type: "memory_20250818", name: "memory"}` to the tools array
|
|
24
|
+
* 2. Add `context-management-2025-06-27` beta header
|
|
25
|
+
* 3. Route "memory" tool calls to the handler
|
|
26
|
+
*/
|
|
27
|
+
export type NativeMemoryTool = {
|
|
28
|
+
/** The tool definition for the Anthropic API (type: "memory_20250818") */
|
|
29
|
+
definition: {
|
|
30
|
+
type: "memory_20250818";
|
|
31
|
+
name: "memory";
|
|
32
|
+
};
|
|
33
|
+
/** Execute a memory command */
|
|
34
|
+
execute: (cmd: MemoryCommand) => Promise<string>;
|
|
35
|
+
};
|
|
36
|
+
export declare function createNativeMemoryTool(config: MemoryConfig): NativeMemoryTool;
|
|
37
|
+
/**
|
|
38
|
+
* For non-Anthropic providers: wraps memory as a standard ToolImplementation
|
|
39
|
+
* so any LLM can call it as a regular function tool.
|
|
40
|
+
*/
|
|
41
|
+
export declare function createMemoryTool(config: MemoryConfig): ToolImplementation;
|
|
42
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/memory/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAI/D,MAAM,MAAM,YAAY,GAAG;IACzB,wEAAwE;IACxE,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAIF;;;;;;;;GAQG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,0EAA0E;IAC1E,UAAU,EAAE;QAAE,IAAI,EAAE,iBAAiB,CAAC;QAAC,IAAI,EAAE,QAAQ,CAAA;KAAE,CAAC;IACxD,+BAA+B;IAC/B,OAAO,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAClD,CAAC;AAEF,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,YAAY,GAAG,gBAAgB,CAM7E;AAID;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,YAAY,GAAG,kBAAkB,CA4EzE"}
|