fourmis-agents-sdk 0.2.6 → 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.
@@ -0,0 +1,319 @@
1
+ // @bun
2
+ var __defProp = Object.defineProperty;
3
+ var __export = (target, all) => {
4
+ for (var name in all)
5
+ __defProp(target, name, {
6
+ get: all[name],
7
+ enumerable: true,
8
+ configurable: true,
9
+ set: (newValue) => all[name] = () => newValue
10
+ });
11
+ };
12
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
13
+ var __require = import.meta.require;
14
+
15
+ // src/memory/memory-handler.ts
16
+ import { readdir, stat, readFile, writeFile, rm, rename, mkdir } from "fs/promises";
17
+ import { join, resolve, relative } from "path";
18
+ import { existsSync } from "fs";
19
+ function createMemoryHandler(memoryDir) {
20
+ const absMemoryDir = resolve(memoryDir);
21
+ function resolvePath(logicalPath) {
22
+ let cleaned = logicalPath;
23
+ if (cleaned.startsWith("/memories")) {
24
+ cleaned = cleaned.slice("/memories".length);
25
+ }
26
+ if (cleaned.startsWith("/")) {
27
+ cleaned = cleaned.slice(1);
28
+ }
29
+ if (cleaned.includes("..") || cleaned.includes("%2e") || cleaned.includes("%2E")) {
30
+ throw new Error(`Path traversal detected: ${logicalPath}`);
31
+ }
32
+ const absPath = cleaned === "" ? absMemoryDir : resolve(absMemoryDir, cleaned);
33
+ const rel = relative(absMemoryDir, absPath);
34
+ if (rel.startsWith("..") || resolve(absPath) !== absPath && !absPath.startsWith(absMemoryDir)) {
35
+ throw new Error(`Path traversal detected: ${logicalPath}`);
36
+ }
37
+ return absPath;
38
+ }
39
+ function toLogicalPath(absPath) {
40
+ const rel = relative(absMemoryDir, absPath);
41
+ return rel === "" ? "/memories" : `/memories/${rel}`;
42
+ }
43
+ function formatSize(bytes) {
44
+ if (bytes < 1024)
45
+ return `${bytes}`;
46
+ if (bytes < 1024 * 1024)
47
+ return `${(bytes / 1024).toFixed(1)}K`;
48
+ return `${(bytes / (1024 * 1024)).toFixed(1)}M`;
49
+ }
50
+ async function listDir(dirPath, depth = 0) {
51
+ const lines = [];
52
+ const dirStat = await stat(dirPath);
53
+ if (depth === 0) {
54
+ lines.push(`${formatSize(dirStat.size)} ${toLogicalPath(dirPath)}`);
55
+ }
56
+ if (depth >= 2)
57
+ return lines;
58
+ try {
59
+ const entries = await readdir(dirPath, { withFileTypes: true });
60
+ for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
61
+ if (entry.name.startsWith(".") || entry.name === "node_modules")
62
+ continue;
63
+ const entryPath = join(dirPath, entry.name);
64
+ const entryStat = await stat(entryPath);
65
+ lines.push(`${formatSize(entryStat.size)} ${toLogicalPath(entryPath)}`);
66
+ if (entry.isDirectory()) {
67
+ const subLines = await listDir(entryPath, depth + 1);
68
+ lines.push(...subLines.slice(depth === 0 ? 0 : 0));
69
+ }
70
+ }
71
+ } catch {}
72
+ return lines;
73
+ }
74
+ function formatFileContent(content, viewRange) {
75
+ const lines = content.split(`
76
+ `);
77
+ if (lines.length > 999999) {
78
+ throw new Error(`File exceeds maximum line limit of 999,999 lines.`);
79
+ }
80
+ let start = 0;
81
+ let end = lines.length;
82
+ if (viewRange && viewRange.length >= 2) {
83
+ start = Math.max(0, viewRange[0] - 1);
84
+ end = Math.min(lines.length, viewRange[1]);
85
+ }
86
+ const formatted = [];
87
+ for (let i = start;i < end; i++) {
88
+ const lineNum = String(i + 1).padStart(6, " ");
89
+ formatted.push(`${lineNum} ${lines[i]}`);
90
+ }
91
+ return formatted.join(`
92
+ `);
93
+ }
94
+ async function handleView(cmd) {
95
+ const absPath = resolvePath(cmd.path);
96
+ if (!existsSync(absPath)) {
97
+ return `The path ${cmd.path} does not exist. Please provide a valid path.`;
98
+ }
99
+ const s = await stat(absPath);
100
+ if (s.isDirectory()) {
101
+ const lines = await listDir(absPath);
102
+ return `Here're the files and directories up to 2 levels deep in ${cmd.path}, excluding hidden items and node_modules:
103
+ ${lines.join(`
104
+ `)}`;
105
+ }
106
+ const content = await readFile(absPath, "utf-8");
107
+ const formatted = formatFileContent(content, cmd.view_range);
108
+ return `Here's the content of ${cmd.path} with line numbers:
109
+ ${formatted}`;
110
+ }
111
+ async function handleCreate(cmd) {
112
+ const absPath = resolvePath(cmd.path);
113
+ if (existsSync(absPath)) {
114
+ return `Error: File ${cmd.path} already exists`;
115
+ }
116
+ const parentDir = resolve(absPath, "..");
117
+ await mkdir(parentDir, { recursive: true });
118
+ await writeFile(absPath, cmd.file_text, "utf-8");
119
+ return `File created successfully at: ${cmd.path}`;
120
+ }
121
+ async function handleStrReplace(cmd) {
122
+ const absPath = resolvePath(cmd.path);
123
+ if (!existsSync(absPath)) {
124
+ return `Error: The path ${cmd.path} does not exist. Please provide a valid path.`;
125
+ }
126
+ const s = await stat(absPath);
127
+ if (s.isDirectory()) {
128
+ return `Error: The path ${cmd.path} does not exist. Please provide a valid path.`;
129
+ }
130
+ const content = await readFile(absPath, "utf-8");
131
+ const lines = content.split(`
132
+ `);
133
+ const matchingLines = [];
134
+ let searchPos = 0;
135
+ let occurrences = 0;
136
+ while (true) {
137
+ const idx = content.indexOf(cmd.old_str, searchPos);
138
+ if (idx === -1)
139
+ break;
140
+ occurrences++;
141
+ const lineNum = content.substring(0, idx).split(`
142
+ `).length;
143
+ matchingLines.push(lineNum);
144
+ searchPos = idx + cmd.old_str.length;
145
+ }
146
+ if (occurrences === 0) {
147
+ return `No replacement was performed, old_str \`${cmd.old_str}\` did not appear verbatim in ${cmd.path}.`;
148
+ }
149
+ if (occurrences > 1) {
150
+ return `No replacement was performed. Multiple occurrences of old_str \`${cmd.old_str}\` in lines: ${matchingLines.join(", ")}. Please ensure it is unique`;
151
+ }
152
+ const newContent = content.replace(cmd.old_str, cmd.new_str);
153
+ await writeFile(absPath, newContent, "utf-8");
154
+ const newLines = newContent.split(`
155
+ `);
156
+ const replaceLine = matchingLines[0];
157
+ const snippetStart = Math.max(0, replaceLine - 3);
158
+ const snippetEnd = Math.min(newLines.length, replaceLine + 3);
159
+ const snippet = newLines.slice(snippetStart, snippetEnd).map((line, i) => `${String(snippetStart + i + 1).padStart(6, " ")} ${line}`).join(`
160
+ `);
161
+ return `The memory file has been edited.
162
+ ${snippet}`;
163
+ }
164
+ async function handleInsert(cmd) {
165
+ const absPath = resolvePath(cmd.path);
166
+ if (!existsSync(absPath)) {
167
+ return `Error: The path ${cmd.path} does not exist`;
168
+ }
169
+ const s = await stat(absPath);
170
+ if (s.isDirectory()) {
171
+ return `Error: The path ${cmd.path} does not exist`;
172
+ }
173
+ const content = await readFile(absPath, "utf-8");
174
+ const lines = content.split(`
175
+ `);
176
+ if (cmd.insert_line < 0 || cmd.insert_line > lines.length) {
177
+ return `Error: Invalid \`insert_line\` parameter: ${cmd.insert_line}. It should be within the range of lines of the file: [0, ${lines.length}]`;
178
+ }
179
+ const insertLines = cmd.insert_text.split(`
180
+ `);
181
+ lines.splice(cmd.insert_line, 0, ...insertLines);
182
+ await writeFile(absPath, lines.join(`
183
+ `), "utf-8");
184
+ return `The file ${cmd.path} has been edited.`;
185
+ }
186
+ async function handleDelete(cmd) {
187
+ const absPath = resolvePath(cmd.path);
188
+ if (!existsSync(absPath)) {
189
+ return `Error: The path ${cmd.path} does not exist`;
190
+ }
191
+ await rm(absPath, { recursive: true, force: true });
192
+ return `Successfully deleted ${cmd.path}`;
193
+ }
194
+ async function handleRename(cmd) {
195
+ const oldAbs = resolvePath(cmd.old_path);
196
+ const newAbs = resolvePath(cmd.new_path);
197
+ if (!existsSync(oldAbs)) {
198
+ return `Error: The path ${cmd.old_path} does not exist`;
199
+ }
200
+ if (existsSync(newAbs)) {
201
+ return `Error: The destination ${cmd.new_path} already exists`;
202
+ }
203
+ const parentDir = resolve(newAbs, "..");
204
+ await mkdir(parentDir, { recursive: true });
205
+ await rename(oldAbs, newAbs);
206
+ return `Successfully renamed ${cmd.old_path} to ${cmd.new_path}`;
207
+ }
208
+ async function execute(cmd) {
209
+ if (!existsSync(absMemoryDir)) {
210
+ await mkdir(absMemoryDir, { recursive: true });
211
+ }
212
+ switch (cmd.command) {
213
+ case "view":
214
+ return handleView(cmd);
215
+ case "create":
216
+ return handleCreate(cmd);
217
+ case "str_replace":
218
+ return handleStrReplace(cmd);
219
+ case "insert":
220
+ return handleInsert(cmd);
221
+ case "delete":
222
+ return handleDelete(cmd);
223
+ case "rename":
224
+ return handleRename(cmd);
225
+ default:
226
+ return `Error: Unknown command: ${cmd.command}`;
227
+ }
228
+ }
229
+ return { execute, resolvePath, toLogicalPath };
230
+ }
231
+ // src/memory/index.ts
232
+ function createNativeMemoryTool(config) {
233
+ const handler = createMemoryHandler(config.path);
234
+ return {
235
+ definition: { type: "memory_20250818", name: "memory" },
236
+ execute: (cmd) => handler.execute(cmd)
237
+ };
238
+ }
239
+ function createMemoryTool(config) {
240
+ const handler = createMemoryHandler(config.path);
241
+ return {
242
+ name: "memory",
243
+ description: `Manage persistent memory files. Supports 6 commands:
244
+ ` + `- view: Show directory listing or file contents (path, optional view_range)
245
+ ` + `- create: Create a new file (path, file_text)
246
+ ` + `- str_replace: Replace text in a file (path, old_str, new_str)
247
+ ` + `- insert: Insert text at a line number (path, insert_line, insert_text)
248
+ ` + `- delete: Delete a file or directory (path)
249
+ ` + `- rename: Rename/move a file or directory (old_path, new_path)
250
+
251
+ ` + `All paths should start with /memories/. Example: /memories/notes.txt
252
+
253
+ ` + "IMPORTANT: Always view your memory directory before starting any task.",
254
+ inputSchema: {
255
+ type: "object",
256
+ properties: {
257
+ command: {
258
+ type: "string",
259
+ enum: ["view", "create", "str_replace", "insert", "delete", "rename"],
260
+ description: "The memory operation to perform"
261
+ },
262
+ path: {
263
+ type: "string",
264
+ description: "Path to the file or directory (starts with /memories/)"
265
+ },
266
+ file_text: {
267
+ type: "string",
268
+ description: "Content for the 'create' command"
269
+ },
270
+ old_str: {
271
+ type: "string",
272
+ description: "Text to find for 'str_replace' command"
273
+ },
274
+ new_str: {
275
+ type: "string",
276
+ description: "Replacement text for 'str_replace' command"
277
+ },
278
+ insert_line: {
279
+ type: "number",
280
+ description: "Line number for 'insert' command"
281
+ },
282
+ insert_text: {
283
+ type: "string",
284
+ description: "Text to insert for 'insert' command"
285
+ },
286
+ old_path: {
287
+ type: "string",
288
+ description: "Source path for 'rename' command"
289
+ },
290
+ new_path: {
291
+ type: "string",
292
+ description: "Destination path for 'rename' command"
293
+ },
294
+ view_range: {
295
+ type: "array",
296
+ items: { type: "number" },
297
+ description: "Optional [start, end] line range for 'view' command"
298
+ }
299
+ },
300
+ required: ["command"]
301
+ },
302
+ async execute(input) {
303
+ try {
304
+ const cmd = input;
305
+ const result = await handler.execute(cmd);
306
+ const isError = result.startsWith("Error:");
307
+ return { content: result, isError };
308
+ } catch (err) {
309
+ const message = err instanceof Error ? err.message : String(err);
310
+ return { content: `Error: ${message}`, isError: true };
311
+ }
312
+ }
313
+ };
314
+ }
315
+ export {
316
+ createNativeMemoryTool,
317
+ createMemoryTool,
318
+ createMemoryHandler
319
+ };
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Memory handler — file-based memory storage with path traversal protection.
3
+ *
4
+ * Implements all 6 memory tool operations:
5
+ * - view: Show directory contents or file contents
6
+ * - create: Create a new file
7
+ * - str_replace: Replace text in a file
8
+ * - insert: Insert text at a specific line
9
+ * - delete: Delete a file or directory
10
+ * - rename: Rename/move a file or directory
11
+ *
12
+ * All paths are sandboxed within the configured memory directory.
13
+ */
14
+ export type MemoryCommand = {
15
+ command: "view";
16
+ path: string;
17
+ view_range?: number[];
18
+ } | {
19
+ command: "create";
20
+ path: string;
21
+ file_text: string;
22
+ } | {
23
+ command: "str_replace";
24
+ path: string;
25
+ old_str: string;
26
+ new_str: string;
27
+ } | {
28
+ command: "insert";
29
+ path: string;
30
+ insert_line: number;
31
+ insert_text: string;
32
+ } | {
33
+ command: "delete";
34
+ path: string;
35
+ } | {
36
+ command: "rename";
37
+ old_path: string;
38
+ new_path: string;
39
+ };
40
+ /**
41
+ * Create a memory handler bound to a specific directory.
42
+ * All operations are sandboxed within this directory.
43
+ */
44
+ export declare function createMemoryHandler(memoryDir: string): {
45
+ execute: (cmd: MemoryCommand) => Promise<string>;
46
+ resolvePath: (logicalPath: string) => string;
47
+ toLogicalPath: (absPath: string) => string;
48
+ };
49
+ //# sourceMappingURL=memory-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-handler.d.ts","sourceRoot":"","sources":["../../src/memory/memory-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAMH,MAAM,MAAM,aAAa,GACrB;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,GACxD;IAAE,OAAO,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACtD;IAAE,OAAO,EAAE,aAAa,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC1E;IAAE,OAAO,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAC7E;IAAE,OAAO,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACnC;IAAE,OAAO,EAAE,QAAQ,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9D;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM;mBA0QvB,aAAa,KAAG,OAAO,CAAC,MAAM,CAAC;+BAlQzB,MAAM,KAAG,MAAM;6BA6BjB,MAAM,KAAG,MAAM;EA8PhD"}
@@ -0,0 +1,233 @@
1
+ // @bun
2
+ var __defProp = Object.defineProperty;
3
+ var __export = (target, all) => {
4
+ for (var name in all)
5
+ __defProp(target, name, {
6
+ get: all[name],
7
+ enumerable: true,
8
+ configurable: true,
9
+ set: (newValue) => all[name] = () => newValue
10
+ });
11
+ };
12
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
13
+ var __require = import.meta.require;
14
+
15
+ // src/memory/memory-handler.ts
16
+ import { readdir, stat, readFile, writeFile, rm, rename, mkdir } from "fs/promises";
17
+ import { join, resolve, relative } from "path";
18
+ import { existsSync } from "fs";
19
+ function createMemoryHandler(memoryDir) {
20
+ const absMemoryDir = resolve(memoryDir);
21
+ function resolvePath(logicalPath) {
22
+ let cleaned = logicalPath;
23
+ if (cleaned.startsWith("/memories")) {
24
+ cleaned = cleaned.slice("/memories".length);
25
+ }
26
+ if (cleaned.startsWith("/")) {
27
+ cleaned = cleaned.slice(1);
28
+ }
29
+ if (cleaned.includes("..") || cleaned.includes("%2e") || cleaned.includes("%2E")) {
30
+ throw new Error(`Path traversal detected: ${logicalPath}`);
31
+ }
32
+ const absPath = cleaned === "" ? absMemoryDir : resolve(absMemoryDir, cleaned);
33
+ const rel = relative(absMemoryDir, absPath);
34
+ if (rel.startsWith("..") || resolve(absPath) !== absPath && !absPath.startsWith(absMemoryDir)) {
35
+ throw new Error(`Path traversal detected: ${logicalPath}`);
36
+ }
37
+ return absPath;
38
+ }
39
+ function toLogicalPath(absPath) {
40
+ const rel = relative(absMemoryDir, absPath);
41
+ return rel === "" ? "/memories" : `/memories/${rel}`;
42
+ }
43
+ function formatSize(bytes) {
44
+ if (bytes < 1024)
45
+ return `${bytes}`;
46
+ if (bytes < 1024 * 1024)
47
+ return `${(bytes / 1024).toFixed(1)}K`;
48
+ return `${(bytes / (1024 * 1024)).toFixed(1)}M`;
49
+ }
50
+ async function listDir(dirPath, depth = 0) {
51
+ const lines = [];
52
+ const dirStat = await stat(dirPath);
53
+ if (depth === 0) {
54
+ lines.push(`${formatSize(dirStat.size)} ${toLogicalPath(dirPath)}`);
55
+ }
56
+ if (depth >= 2)
57
+ return lines;
58
+ try {
59
+ const entries = await readdir(dirPath, { withFileTypes: true });
60
+ for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
61
+ if (entry.name.startsWith(".") || entry.name === "node_modules")
62
+ continue;
63
+ const entryPath = join(dirPath, entry.name);
64
+ const entryStat = await stat(entryPath);
65
+ lines.push(`${formatSize(entryStat.size)} ${toLogicalPath(entryPath)}`);
66
+ if (entry.isDirectory()) {
67
+ const subLines = await listDir(entryPath, depth + 1);
68
+ lines.push(...subLines.slice(depth === 0 ? 0 : 0));
69
+ }
70
+ }
71
+ } catch {}
72
+ return lines;
73
+ }
74
+ function formatFileContent(content, viewRange) {
75
+ const lines = content.split(`
76
+ `);
77
+ if (lines.length > 999999) {
78
+ throw new Error(`File exceeds maximum line limit of 999,999 lines.`);
79
+ }
80
+ let start = 0;
81
+ let end = lines.length;
82
+ if (viewRange && viewRange.length >= 2) {
83
+ start = Math.max(0, viewRange[0] - 1);
84
+ end = Math.min(lines.length, viewRange[1]);
85
+ }
86
+ const formatted = [];
87
+ for (let i = start;i < end; i++) {
88
+ const lineNum = String(i + 1).padStart(6, " ");
89
+ formatted.push(`${lineNum} ${lines[i]}`);
90
+ }
91
+ return formatted.join(`
92
+ `);
93
+ }
94
+ async function handleView(cmd) {
95
+ const absPath = resolvePath(cmd.path);
96
+ if (!existsSync(absPath)) {
97
+ return `The path ${cmd.path} does not exist. Please provide a valid path.`;
98
+ }
99
+ const s = await stat(absPath);
100
+ if (s.isDirectory()) {
101
+ const lines = await listDir(absPath);
102
+ return `Here're the files and directories up to 2 levels deep in ${cmd.path}, excluding hidden items and node_modules:
103
+ ${lines.join(`
104
+ `)}`;
105
+ }
106
+ const content = await readFile(absPath, "utf-8");
107
+ const formatted = formatFileContent(content, cmd.view_range);
108
+ return `Here's the content of ${cmd.path} with line numbers:
109
+ ${formatted}`;
110
+ }
111
+ async function handleCreate(cmd) {
112
+ const absPath = resolvePath(cmd.path);
113
+ if (existsSync(absPath)) {
114
+ return `Error: File ${cmd.path} already exists`;
115
+ }
116
+ const parentDir = resolve(absPath, "..");
117
+ await mkdir(parentDir, { recursive: true });
118
+ await writeFile(absPath, cmd.file_text, "utf-8");
119
+ return `File created successfully at: ${cmd.path}`;
120
+ }
121
+ async function handleStrReplace(cmd) {
122
+ const absPath = resolvePath(cmd.path);
123
+ if (!existsSync(absPath)) {
124
+ return `Error: The path ${cmd.path} does not exist. Please provide a valid path.`;
125
+ }
126
+ const s = await stat(absPath);
127
+ if (s.isDirectory()) {
128
+ return `Error: The path ${cmd.path} does not exist. Please provide a valid path.`;
129
+ }
130
+ const content = await readFile(absPath, "utf-8");
131
+ const lines = content.split(`
132
+ `);
133
+ const matchingLines = [];
134
+ let searchPos = 0;
135
+ let occurrences = 0;
136
+ while (true) {
137
+ const idx = content.indexOf(cmd.old_str, searchPos);
138
+ if (idx === -1)
139
+ break;
140
+ occurrences++;
141
+ const lineNum = content.substring(0, idx).split(`
142
+ `).length;
143
+ matchingLines.push(lineNum);
144
+ searchPos = idx + cmd.old_str.length;
145
+ }
146
+ if (occurrences === 0) {
147
+ return `No replacement was performed, old_str \`${cmd.old_str}\` did not appear verbatim in ${cmd.path}.`;
148
+ }
149
+ if (occurrences > 1) {
150
+ return `No replacement was performed. Multiple occurrences of old_str \`${cmd.old_str}\` in lines: ${matchingLines.join(", ")}. Please ensure it is unique`;
151
+ }
152
+ const newContent = content.replace(cmd.old_str, cmd.new_str);
153
+ await writeFile(absPath, newContent, "utf-8");
154
+ const newLines = newContent.split(`
155
+ `);
156
+ const replaceLine = matchingLines[0];
157
+ const snippetStart = Math.max(0, replaceLine - 3);
158
+ const snippetEnd = Math.min(newLines.length, replaceLine + 3);
159
+ const snippet = newLines.slice(snippetStart, snippetEnd).map((line, i) => `${String(snippetStart + i + 1).padStart(6, " ")} ${line}`).join(`
160
+ `);
161
+ return `The memory file has been edited.
162
+ ${snippet}`;
163
+ }
164
+ async function handleInsert(cmd) {
165
+ const absPath = resolvePath(cmd.path);
166
+ if (!existsSync(absPath)) {
167
+ return `Error: The path ${cmd.path} does not exist`;
168
+ }
169
+ const s = await stat(absPath);
170
+ if (s.isDirectory()) {
171
+ return `Error: The path ${cmd.path} does not exist`;
172
+ }
173
+ const content = await readFile(absPath, "utf-8");
174
+ const lines = content.split(`
175
+ `);
176
+ if (cmd.insert_line < 0 || cmd.insert_line > lines.length) {
177
+ return `Error: Invalid \`insert_line\` parameter: ${cmd.insert_line}. It should be within the range of lines of the file: [0, ${lines.length}]`;
178
+ }
179
+ const insertLines = cmd.insert_text.split(`
180
+ `);
181
+ lines.splice(cmd.insert_line, 0, ...insertLines);
182
+ await writeFile(absPath, lines.join(`
183
+ `), "utf-8");
184
+ return `The file ${cmd.path} has been edited.`;
185
+ }
186
+ async function handleDelete(cmd) {
187
+ const absPath = resolvePath(cmd.path);
188
+ if (!existsSync(absPath)) {
189
+ return `Error: The path ${cmd.path} does not exist`;
190
+ }
191
+ await rm(absPath, { recursive: true, force: true });
192
+ return `Successfully deleted ${cmd.path}`;
193
+ }
194
+ async function handleRename(cmd) {
195
+ const oldAbs = resolvePath(cmd.old_path);
196
+ const newAbs = resolvePath(cmd.new_path);
197
+ if (!existsSync(oldAbs)) {
198
+ return `Error: The path ${cmd.old_path} does not exist`;
199
+ }
200
+ if (existsSync(newAbs)) {
201
+ return `Error: The destination ${cmd.new_path} already exists`;
202
+ }
203
+ const parentDir = resolve(newAbs, "..");
204
+ await mkdir(parentDir, { recursive: true });
205
+ await rename(oldAbs, newAbs);
206
+ return `Successfully renamed ${cmd.old_path} to ${cmd.new_path}`;
207
+ }
208
+ async function execute(cmd) {
209
+ if (!existsSync(absMemoryDir)) {
210
+ await mkdir(absMemoryDir, { recursive: true });
211
+ }
212
+ switch (cmd.command) {
213
+ case "view":
214
+ return handleView(cmd);
215
+ case "create":
216
+ return handleCreate(cmd);
217
+ case "str_replace":
218
+ return handleStrReplace(cmd);
219
+ case "insert":
220
+ return handleInsert(cmd);
221
+ case "delete":
222
+ return handleDelete(cmd);
223
+ case "rename":
224
+ return handleRename(cmd);
225
+ default:
226
+ return `Error: Unknown command: ${cmd.command}`;
227
+ }
228
+ }
229
+ return { execute, resolvePath, toLogicalPath };
230
+ }
231
+ export {
232
+ createMemoryHandler
233
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../src/providers/anthropic.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EACV,eAAe,EACf,WAAW,EACX,SAAS,EACT,eAAe,EAIhB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAiB9C,qBAAa,gBAAiB,YAAW,eAAe;IACtD,IAAI,SAAe;IACnB,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,SAAS,CAAU;gBAEf,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE;IAwBpD,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,cAAc,CAAC,SAAS,CAAC;IA+H5D,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,MAAM;IAIvD,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAIvC,eAAe,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO;IAgBlD,OAAO,CAAC,eAAe;IAiCvB,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,aAAa;CActB"}
1
+ {"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../src/providers/anthropic.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EACV,eAAe,EACf,WAAW,EACX,SAAS,EACT,eAAe,EAIhB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAiB9C,qBAAa,gBAAiB,YAAW,eAAe;IACtD,IAAI,SAAe;IACnB,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,SAAS,CAAU;gBAEf,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE;IAwBpD,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,cAAc,CAAC,SAAS,CAAC;IAmJ5D,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,MAAM;IAIvD,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAIvC,eAAe,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO;IAgBlD,OAAO,CAAC,eAAe;IAiCvB,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,aAAa;CActB"}
@@ -278,15 +278,29 @@ class AnthropicAdapter {
278
278
  } else if (request.systemPrompt) {
279
279
  params.system = request.systemPrompt;
280
280
  }
281
+ const allTools = [];
281
282
  if (tools && tools.length > 0) {
282
- params.tools = tools;
283
+ allTools.push(...tools);
284
+ }
285
+ if (request.nativeTools && request.nativeTools.length > 0) {
286
+ allTools.push(...request.nativeTools);
287
+ }
288
+ if (allTools.length > 0) {
289
+ params.tools = allTools;
283
290
  }
284
291
  if (request.temperature !== undefined) {
285
292
  params.temperature = request.temperature;
286
293
  }
287
- const stream = this.client.messages.stream(params, {
294
+ const hasMemoryTool = request.nativeTools?.some((t) => t.type === "memory_20250818");
295
+ const requestOptions = {
288
296
  signal: request.signal
289
- });
297
+ };
298
+ if (hasMemoryTool) {
299
+ requestOptions.headers = {
300
+ "anthropic-beta": "context-management-2025-06-27"
301
+ };
302
+ }
303
+ const stream = this.client.messages.stream(params, requestOptions);
290
304
  const toolInputBuffers = new Map;
291
305
  for await (const event of stream) {
292
306
  switch (event.type) {
@@ -657,15 +657,29 @@ class AnthropicAdapter {
657
657
  } else if (request.systemPrompt) {
658
658
  params.system = request.systemPrompt;
659
659
  }
660
+ const allTools = [];
660
661
  if (tools && tools.length > 0) {
661
- params.tools = tools;
662
+ allTools.push(...tools);
663
+ }
664
+ if (request.nativeTools && request.nativeTools.length > 0) {
665
+ allTools.push(...request.nativeTools);
666
+ }
667
+ if (allTools.length > 0) {
668
+ params.tools = allTools;
662
669
  }
663
670
  if (request.temperature !== undefined) {
664
671
  params.temperature = request.temperature;
665
672
  }
666
- const stream = this.client.messages.stream(params, {
673
+ const hasMemoryTool = request.nativeTools?.some((t) => t.type === "memory_20250818");
674
+ const requestOptions = {
667
675
  signal: request.signal
668
- });
676
+ };
677
+ if (hasMemoryTool) {
678
+ requestOptions.headers = {
679
+ "anthropic-beta": "context-management-2025-06-27"
680
+ };
681
+ }
682
+ const stream = this.client.messages.stream(params, requestOptions);
669
683
  const toolInputBuffers = new Map;
670
684
  for await (const event of stream) {
671
685
  switch (event.type) {
@@ -39,6 +39,12 @@ export type ChatRequest = {
39
39
  temperature?: number;
40
40
  thinkingBudget?: number;
41
41
  signal?: AbortSignal;
42
+ /**
43
+ * Provider-specific native tools (e.g. Anthropic's memory_20250818).
44
+ * These bypass the normal tool definition format and are passed directly
45
+ * to the provider's API alongside converted regular tools.
46
+ */
47
+ nativeTools?: unknown[];
42
48
  };
43
49
  export type StopReason = "end_turn" | "tool_use" | "max_tokens" | "stop_sequence";
44
50
  export type ChatChunk = {