deepagentsdk 0.11.1 → 0.12.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.
Files changed (103) hide show
  1. package/dist/adapters/elements/index.cjs +324 -0
  2. package/dist/adapters/elements/index.cjs.map +1 -0
  3. package/dist/adapters/elements/index.d.cts +212 -0
  4. package/dist/adapters/elements/index.d.mts +212 -0
  5. package/dist/adapters/elements/index.mjs +320 -0
  6. package/dist/adapters/elements/index.mjs.map +1 -0
  7. package/dist/agent-CrH-He58.mjs +2974 -0
  8. package/dist/agent-CrH-He58.mjs.map +1 -0
  9. package/dist/agent-Cuks-Idh.cjs +3396 -0
  10. package/dist/agent-Cuks-Idh.cjs.map +1 -0
  11. package/dist/chunk-CbDLau6x.cjs +34 -0
  12. package/dist/cli/index.cjs +3162 -0
  13. package/dist/cli/index.cjs.map +1 -0
  14. package/dist/cli/index.d.cts +1 -0
  15. package/dist/cli/index.d.mts +1 -0
  16. package/dist/cli/index.mjs +3120 -0
  17. package/dist/cli/index.mjs.map +1 -0
  18. package/dist/file-saver-BJCqMIb5.mjs +655 -0
  19. package/dist/file-saver-BJCqMIb5.mjs.map +1 -0
  20. package/dist/file-saver-C6O2LAvg.cjs +679 -0
  21. package/dist/file-saver-C6O2LAvg.cjs.map +1 -0
  22. package/dist/index.cjs +1471 -0
  23. package/dist/index.cjs.map +1 -0
  24. package/dist/index.d.cts +1581 -0
  25. package/dist/index.d.mts +1581 -0
  26. package/dist/index.mjs +1371 -0
  27. package/dist/index.mjs.map +1 -0
  28. package/dist/load-79a2H4m0.cjs +163 -0
  29. package/dist/load-79a2H4m0.cjs.map +1 -0
  30. package/dist/load-94gjHorc.mjs +3 -0
  31. package/dist/load-B6CA5js_.mjs +142 -0
  32. package/dist/load-B6CA5js_.mjs.map +1 -0
  33. package/dist/load-C2qVmZMp.cjs +3 -0
  34. package/dist/types-4g9UvXal.d.mts +1151 -0
  35. package/dist/types-IulnvhFg.d.cts +1151 -0
  36. package/package.json +26 -12
  37. package/src/adapters/elements/index.ts +0 -27
  38. package/src/adapters/elements/messageAdapter.ts +0 -165
  39. package/src/adapters/elements/statusAdapter.ts +0 -39
  40. package/src/adapters/elements/types.ts +0 -97
  41. package/src/adapters/elements/useElementsAdapter.ts +0 -261
  42. package/src/agent.ts +0 -1258
  43. package/src/backends/composite.ts +0 -273
  44. package/src/backends/filesystem.ts +0 -692
  45. package/src/backends/index.ts +0 -22
  46. package/src/backends/local-sandbox.ts +0 -175
  47. package/src/backends/persistent.ts +0 -593
  48. package/src/backends/sandbox.ts +0 -510
  49. package/src/backends/state.ts +0 -244
  50. package/src/backends/utils.ts +0 -287
  51. package/src/checkpointer/file-saver.ts +0 -98
  52. package/src/checkpointer/index.ts +0 -5
  53. package/src/checkpointer/kv-saver.ts +0 -82
  54. package/src/checkpointer/memory-saver.ts +0 -82
  55. package/src/checkpointer/types.ts +0 -125
  56. package/src/cli/components/ApiKeyInput.tsx +0 -300
  57. package/src/cli/components/FilePreview.tsx +0 -237
  58. package/src/cli/components/Input.tsx +0 -277
  59. package/src/cli/components/Message.tsx +0 -93
  60. package/src/cli/components/ModelSelection.tsx +0 -338
  61. package/src/cli/components/SlashMenu.tsx +0 -101
  62. package/src/cli/components/StatusBar.tsx +0 -89
  63. package/src/cli/components/Subagent.tsx +0 -91
  64. package/src/cli/components/TodoList.tsx +0 -133
  65. package/src/cli/components/ToolApproval.tsx +0 -70
  66. package/src/cli/components/ToolCall.tsx +0 -144
  67. package/src/cli/components/ToolCallSummary.tsx +0 -175
  68. package/src/cli/components/Welcome.tsx +0 -75
  69. package/src/cli/components/index.ts +0 -24
  70. package/src/cli/hooks/index.ts +0 -12
  71. package/src/cli/hooks/useAgent.ts +0 -933
  72. package/src/cli/index.tsx +0 -1066
  73. package/src/cli/theme.ts +0 -205
  74. package/src/cli/utils/model-list.ts +0 -365
  75. package/src/constants/errors.ts +0 -29
  76. package/src/constants/limits.ts +0 -195
  77. package/src/index.ts +0 -176
  78. package/src/middleware/agent-memory.ts +0 -330
  79. package/src/prompts.ts +0 -196
  80. package/src/skills/index.ts +0 -2
  81. package/src/skills/load.ts +0 -191
  82. package/src/skills/types.ts +0 -53
  83. package/src/tools/execute.ts +0 -167
  84. package/src/tools/filesystem.ts +0 -418
  85. package/src/tools/index.ts +0 -39
  86. package/src/tools/subagent.ts +0 -443
  87. package/src/tools/todos.ts +0 -101
  88. package/src/tools/web.ts +0 -567
  89. package/src/types/backend.ts +0 -177
  90. package/src/types/core.ts +0 -220
  91. package/src/types/events.ts +0 -430
  92. package/src/types/index.ts +0 -94
  93. package/src/types/structured-output.ts +0 -43
  94. package/src/types/subagent.ts +0 -96
  95. package/src/types.ts +0 -22
  96. package/src/utils/approval.ts +0 -213
  97. package/src/utils/events.ts +0 -416
  98. package/src/utils/eviction.ts +0 -181
  99. package/src/utils/index.ts +0 -34
  100. package/src/utils/model-parser.ts +0 -38
  101. package/src/utils/patch-tool-calls.ts +0 -233
  102. package/src/utils/project-detection.ts +0 -32
  103. package/src/utils/summarization.ts +0 -254
package/dist/index.cjs ADDED
@@ -0,0 +1,1471 @@
1
+ const require_chunk = require('./chunk-CbDLau6x.cjs');
2
+ const require_agent = require('./agent-Cuks-Idh.cjs');
3
+ const require_file_saver = require('./file-saver-C6O2LAvg.cjs');
4
+ const require_load = require('./load-79a2H4m0.cjs');
5
+ let ai = require("ai");
6
+ let micromatch = require("micromatch");
7
+ micromatch = require_chunk.__toESM(micromatch);
8
+ let path = require("path");
9
+ path = require_chunk.__toESM(path);
10
+ let fs_promises = require("fs/promises");
11
+ fs_promises = require_chunk.__toESM(fs_promises);
12
+ let fs = require("fs");
13
+ fs = require_chunk.__toESM(fs);
14
+ let child_process = require("child_process");
15
+ let fast_glob = require("fast-glob");
16
+ fast_glob = require_chunk.__toESM(fast_glob);
17
+ let node_path = require("node:path");
18
+ node_path = require_chunk.__toESM(node_path);
19
+ let node_fs_promises = require("node:fs/promises");
20
+ node_fs_promises = require_chunk.__toESM(node_fs_promises);
21
+ let node_os = require("node:os");
22
+ node_os = require_chunk.__toESM(node_os);
23
+
24
+ //#region src/types/structured-output.ts
25
+ /**
26
+ * Type guard for checking if a result has structured output
27
+ */
28
+ function hasStructuredOutput(result) {
29
+ return result && typeof result === "object" && "output" in result;
30
+ }
31
+ /**
32
+ * Type guard for checking if an event has structured output
33
+ */
34
+ function eventHasStructuredOutput(event) {
35
+ return event && event.type === "done" && "output" in event;
36
+ }
37
+ /**
38
+ * Extract structured output from agent result with type safety
39
+ */
40
+ function getStructuredOutput(result) {
41
+ return hasStructuredOutput(result) ? result.output : void 0;
42
+ }
43
+ /**
44
+ * Extract structured output from event with type safety
45
+ */
46
+ function getEventOutput(event) {
47
+ return eventHasStructuredOutput(event) ? event.output : void 0;
48
+ }
49
+
50
+ //#endregion
51
+ //#region src/backends/filesystem.ts
52
+ /**
53
+ * FilesystemBackend: Read and write files directly from the filesystem.
54
+ */
55
+ const SUPPORTS_NOFOLLOW = fs.constants.O_NOFOLLOW !== void 0;
56
+ /**
57
+ * Backend that reads and writes files directly from the filesystem.
58
+ *
59
+ * Files are persisted to disk, making them available across agent invocations.
60
+ * This backend provides real file I/O operations with security checks to prevent
61
+ * directory traversal attacks.
62
+ *
63
+ * @example Basic usage
64
+ * ```typescript
65
+ * const backend = new FilesystemBackend({ rootDir: './workspace' });
66
+ * const agent = createDeepAgent({
67
+ * model: anthropic('claude-sonnet-4-20250514'),
68
+ * backend,
69
+ * });
70
+ * ```
71
+ *
72
+ * @example With custom options
73
+ * ```typescript
74
+ * const backend = new FilesystemBackend({
75
+ * rootDir: './my-project',
76
+ * virtualMode: false,
77
+ * maxFileSizeMb: 50, // Allow larger files
78
+ * });
79
+ * ```
80
+ */
81
+ var FilesystemBackend = class {
82
+ cwd;
83
+ virtualMode;
84
+ maxFileSizeBytes;
85
+ /**
86
+ * Create a new FilesystemBackend instance.
87
+ *
88
+ * @param options - Configuration options
89
+ * @param options.rootDir - Optional root directory for file operations (default: current working directory).
90
+ * All file paths are resolved relative to this directory.
91
+ * @param options.virtualMode - Optional flag for virtual mode (default: false).
92
+ * When true, files are stored in memory but paths are validated against filesystem.
93
+ * @param options.maxFileSizeMb - Optional maximum file size in MB (default: 10).
94
+ * Files larger than this will be rejected.
95
+ */
96
+ constructor(options = {}) {
97
+ const { rootDir, virtualMode = false, maxFileSizeMb = require_agent.MAX_FILE_SIZE_MB } = options;
98
+ this.cwd = rootDir ? path.resolve(rootDir) : process.cwd();
99
+ this.virtualMode = virtualMode;
100
+ this.maxFileSizeBytes = maxFileSizeMb * 1024 * 1024;
101
+ }
102
+ /**
103
+ * Resolve a file path with security checks.
104
+ */
105
+ resolvePath(key) {
106
+ if (this.virtualMode) {
107
+ const vpath = key.startsWith("/") ? key : "/" + key;
108
+ if (vpath.includes("..") || vpath.startsWith("~")) throw new Error("Path traversal not allowed");
109
+ const full = path.resolve(this.cwd, vpath.substring(1));
110
+ const relative = path.relative(this.cwd, full);
111
+ if (relative.startsWith("..") || path.isAbsolute(relative)) throw new Error(`Path: ${full} outside root directory: ${this.cwd}`);
112
+ return full;
113
+ }
114
+ if (path.isAbsolute(key)) return key;
115
+ return path.resolve(this.cwd, key);
116
+ }
117
+ /**
118
+ * List files and directories in the specified directory (non-recursive).
119
+ */
120
+ async lsInfo(dirPath) {
121
+ try {
122
+ const resolvedPath = this.resolvePath(dirPath);
123
+ if (!(await fs_promises.stat(resolvedPath)).isDirectory()) return [];
124
+ const entries = await fs_promises.readdir(resolvedPath, { withFileTypes: true });
125
+ const results = [];
126
+ const cwdStr = this.cwd.endsWith(path.sep) ? this.cwd : this.cwd + path.sep;
127
+ for (const entry of entries) {
128
+ const fullPath = path.join(resolvedPath, entry.name);
129
+ try {
130
+ const entryStat = await fs_promises.stat(fullPath);
131
+ const isFile = entryStat.isFile();
132
+ const isDir = entryStat.isDirectory();
133
+ if (!this.virtualMode) {
134
+ if (isFile) results.push({
135
+ path: fullPath,
136
+ is_dir: false,
137
+ size: entryStat.size,
138
+ modified_at: entryStat.mtime.toISOString()
139
+ });
140
+ else if (isDir) results.push({
141
+ path: fullPath + path.sep,
142
+ is_dir: true,
143
+ size: 0,
144
+ modified_at: entryStat.mtime.toISOString()
145
+ });
146
+ } else {
147
+ let relativePath;
148
+ if (fullPath.startsWith(cwdStr)) relativePath = fullPath.substring(cwdStr.length);
149
+ else if (fullPath.startsWith(this.cwd)) relativePath = fullPath.substring(this.cwd.length).replace(/^[/\\]/, "");
150
+ else relativePath = fullPath;
151
+ relativePath = relativePath.split(path.sep).join("/");
152
+ const virtPath = "/" + relativePath;
153
+ if (isFile) results.push({
154
+ path: virtPath,
155
+ is_dir: false,
156
+ size: entryStat.size,
157
+ modified_at: entryStat.mtime.toISOString()
158
+ });
159
+ else if (isDir) results.push({
160
+ path: virtPath + "/",
161
+ is_dir: true,
162
+ size: 0,
163
+ modified_at: entryStat.mtime.toISOString()
164
+ });
165
+ }
166
+ } catch {
167
+ continue;
168
+ }
169
+ }
170
+ results.sort((a, b) => a.path.localeCompare(b.path));
171
+ return results;
172
+ } catch {
173
+ return [];
174
+ }
175
+ }
176
+ /**
177
+ * Read file content with line numbers.
178
+ */
179
+ async read(filePath, offset = 0, limit = require_agent.DEFAULT_READ_LIMIT) {
180
+ try {
181
+ const resolvedPath = this.resolvePath(filePath);
182
+ let content;
183
+ if (SUPPORTS_NOFOLLOW) {
184
+ if (!(await fs_promises.stat(resolvedPath)).isFile()) return require_agent.FILE_NOT_FOUND(filePath);
185
+ const fd = await fs_promises.open(resolvedPath, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
186
+ try {
187
+ content = await fd.readFile({ encoding: "utf-8" });
188
+ } finally {
189
+ await fd.close();
190
+ }
191
+ } else {
192
+ const stat = await fs_promises.lstat(resolvedPath);
193
+ if (stat.isSymbolicLink()) return `Error: Symlinks are not allowed: ${filePath}`;
194
+ if (!stat.isFile()) return require_agent.FILE_NOT_FOUND(filePath);
195
+ content = await fs_promises.readFile(resolvedPath, "utf-8");
196
+ }
197
+ const emptyMsg = require_agent.checkEmptyContent(content);
198
+ if (emptyMsg) return emptyMsg;
199
+ const lines = content.split("\n");
200
+ const startIdx = offset;
201
+ const endIdx = Math.min(startIdx + limit, lines.length);
202
+ if (startIdx >= lines.length) return `Error: Line offset ${offset} exceeds file length (${lines.length} lines)`;
203
+ return require_agent.formatContentWithLineNumbers(lines.slice(startIdx, endIdx), startIdx + 1);
204
+ } catch (e) {
205
+ return `Error reading file '${filePath}': ${e.message}`;
206
+ }
207
+ }
208
+ /**
209
+ * Read file content as raw FileData.
210
+ */
211
+ async readRaw(filePath) {
212
+ const resolvedPath = this.resolvePath(filePath);
213
+ let content;
214
+ let stat;
215
+ if (SUPPORTS_NOFOLLOW) {
216
+ stat = await fs_promises.stat(resolvedPath);
217
+ if (!stat.isFile()) throw new Error(`File '${filePath}' not found`);
218
+ const fd = await fs_promises.open(resolvedPath, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
219
+ try {
220
+ content = await fd.readFile({ encoding: "utf-8" });
221
+ } finally {
222
+ await fd.close();
223
+ }
224
+ } else {
225
+ stat = await fs_promises.lstat(resolvedPath);
226
+ if (stat.isSymbolicLink()) throw new Error(`Symlinks are not allowed: ${filePath}`);
227
+ if (!stat.isFile()) throw new Error(`File '${filePath}' not found`);
228
+ content = await fs_promises.readFile(resolvedPath, "utf-8");
229
+ }
230
+ return {
231
+ content: content.split("\n"),
232
+ created_at: stat.ctime.toISOString(),
233
+ modified_at: stat.mtime.toISOString()
234
+ };
235
+ }
236
+ /**
237
+ * Create a new file with content.
238
+ */
239
+ async write(filePath, content) {
240
+ try {
241
+ const resolvedPath = this.resolvePath(filePath);
242
+ try {
243
+ if ((await fs_promises.lstat(resolvedPath)).isSymbolicLink()) return {
244
+ success: false,
245
+ error: `Cannot write to ${filePath} because it is a symlink. Symlinks are not allowed.`
246
+ };
247
+ return {
248
+ success: false,
249
+ error: require_agent.FILE_ALREADY_EXISTS(filePath)
250
+ };
251
+ } catch {}
252
+ await fs_promises.mkdir(path.dirname(resolvedPath), { recursive: true });
253
+ if (SUPPORTS_NOFOLLOW) {
254
+ const flags = fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_TRUNC | fs.constants.O_NOFOLLOW;
255
+ const fd = await fs_promises.open(resolvedPath, flags, 420);
256
+ try {
257
+ await fd.writeFile(content, "utf-8");
258
+ } finally {
259
+ await fd.close();
260
+ }
261
+ } else await fs_promises.writeFile(resolvedPath, content, "utf-8");
262
+ return {
263
+ success: true,
264
+ path: filePath
265
+ };
266
+ } catch (e) {
267
+ return {
268
+ success: false,
269
+ error: `Error writing file '${filePath}': ${e.message}`
270
+ };
271
+ }
272
+ }
273
+ /**
274
+ * Edit a file by replacing string occurrences.
275
+ */
276
+ async edit(filePath, oldString, newString, replaceAll = false) {
277
+ try {
278
+ const resolvedPath = this.resolvePath(filePath);
279
+ let content;
280
+ if (SUPPORTS_NOFOLLOW) {
281
+ if (!(await fs_promises.stat(resolvedPath)).isFile()) return {
282
+ success: false,
283
+ error: require_agent.FILE_NOT_FOUND(filePath)
284
+ };
285
+ const fd = await fs_promises.open(resolvedPath, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
286
+ try {
287
+ content = await fd.readFile({ encoding: "utf-8" });
288
+ } finally {
289
+ await fd.close();
290
+ }
291
+ } else {
292
+ const stat = await fs_promises.lstat(resolvedPath);
293
+ if (stat.isSymbolicLink()) return {
294
+ success: false,
295
+ error: `Error: Symlinks are not allowed: ${filePath}`
296
+ };
297
+ if (!stat.isFile()) return {
298
+ success: false,
299
+ error: require_agent.FILE_NOT_FOUND(filePath)
300
+ };
301
+ content = await fs_promises.readFile(resolvedPath, "utf-8");
302
+ }
303
+ const result = require_agent.performStringReplacement(content, oldString, newString, replaceAll);
304
+ if (typeof result === "string") return {
305
+ success: false,
306
+ error: result
307
+ };
308
+ const [newContent, occurrences] = result;
309
+ if (SUPPORTS_NOFOLLOW) {
310
+ const flags = fs.constants.O_WRONLY | fs.constants.O_TRUNC | fs.constants.O_NOFOLLOW;
311
+ const fd = await fs_promises.open(resolvedPath, flags);
312
+ try {
313
+ await fd.writeFile(newContent, "utf-8");
314
+ } finally {
315
+ await fd.close();
316
+ }
317
+ } else await fs_promises.writeFile(resolvedPath, newContent, "utf-8");
318
+ return {
319
+ success: true,
320
+ path: filePath,
321
+ occurrences
322
+ };
323
+ } catch (e) {
324
+ return {
325
+ success: false,
326
+ error: `Error editing file '${filePath}': ${e.message}`
327
+ };
328
+ }
329
+ }
330
+ /**
331
+ * Structured search results or error string for invalid input.
332
+ */
333
+ async grepRaw(pattern, dirPath = "/", glob$1 = null) {
334
+ try {
335
+ new RegExp(pattern);
336
+ } catch (e) {
337
+ return `Invalid regex pattern: ${e.message}`;
338
+ }
339
+ let baseFull;
340
+ try {
341
+ baseFull = this.resolvePath(dirPath || ".");
342
+ } catch {
343
+ return [];
344
+ }
345
+ try {
346
+ await fs_promises.stat(baseFull);
347
+ } catch {
348
+ return [];
349
+ }
350
+ let results = await this.ripgrepSearch(pattern, baseFull, glob$1);
351
+ if (results === null) results = await this.regexSearch(pattern, baseFull, glob$1);
352
+ const matches = [];
353
+ for (const [fpath, items] of Object.entries(results)) for (const [lineNum, lineText] of items) matches.push({
354
+ path: fpath,
355
+ line: lineNum,
356
+ text: lineText
357
+ });
358
+ return matches;
359
+ }
360
+ /**
361
+ * Try to use ripgrep for fast searching.
362
+ */
363
+ async ripgrepSearch(pattern, baseFull, includeGlob) {
364
+ return new Promise((resolve) => {
365
+ const args = ["--json"];
366
+ if (includeGlob) args.push("--glob", includeGlob);
367
+ args.push("--", pattern, baseFull);
368
+ const proc = (0, child_process.spawn)("rg", args, { timeout: 3e4 });
369
+ const results = {};
370
+ let output = "";
371
+ proc.stdout.on("data", (data) => {
372
+ output += data.toString();
373
+ });
374
+ proc.on("close", (code) => {
375
+ if (code !== 0 && code !== 1) {
376
+ resolve(null);
377
+ return;
378
+ }
379
+ for (const line of output.split("\n")) {
380
+ if (!line.trim()) continue;
381
+ try {
382
+ const data = JSON.parse(line);
383
+ if (data.type !== "match") continue;
384
+ const pdata = data.data || {};
385
+ const ftext = pdata.path?.text;
386
+ if (!ftext) continue;
387
+ let virtPath;
388
+ if (this.virtualMode) try {
389
+ const resolved = path.resolve(ftext);
390
+ const relative = path.relative(this.cwd, resolved);
391
+ if (relative.startsWith("..")) continue;
392
+ virtPath = "/" + relative.split(path.sep).join("/");
393
+ } catch {
394
+ continue;
395
+ }
396
+ else virtPath = ftext;
397
+ const ln = pdata.line_number;
398
+ const lt = pdata.lines?.text?.replace(/\n$/, "") || "";
399
+ if (ln === void 0) continue;
400
+ if (!results[virtPath]) results[virtPath] = [];
401
+ results[virtPath].push([ln, lt]);
402
+ } catch {
403
+ continue;
404
+ }
405
+ }
406
+ resolve(results);
407
+ });
408
+ proc.on("error", () => {
409
+ resolve(null);
410
+ });
411
+ });
412
+ }
413
+ /**
414
+ * Fallback regex search implementation.
415
+ */
416
+ async regexSearch(pattern, baseFull, includeGlob) {
417
+ let regex;
418
+ try {
419
+ regex = new RegExp(pattern);
420
+ } catch {
421
+ return {};
422
+ }
423
+ const results = {};
424
+ const files = await (0, fast_glob.default)("**/*", {
425
+ cwd: (await fs_promises.stat(baseFull)).isDirectory() ? baseFull : path.dirname(baseFull),
426
+ absolute: true,
427
+ onlyFiles: true,
428
+ dot: true
429
+ });
430
+ for (const fp of files) try {
431
+ if (includeGlob && !micromatch.default.isMatch(path.basename(fp), includeGlob)) continue;
432
+ if ((await fs_promises.stat(fp)).size > this.maxFileSizeBytes) continue;
433
+ const lines = (await fs_promises.readFile(fp, "utf-8")).split("\n");
434
+ for (let i = 0; i < lines.length; i++) {
435
+ const line = lines[i];
436
+ if (line && regex.test(line)) {
437
+ let virtPath;
438
+ if (this.virtualMode) try {
439
+ const relative = path.relative(this.cwd, fp);
440
+ if (relative.startsWith("..")) continue;
441
+ virtPath = "/" + relative.split(path.sep).join("/");
442
+ } catch {
443
+ continue;
444
+ }
445
+ else virtPath = fp;
446
+ if (!results[virtPath]) results[virtPath] = [];
447
+ results[virtPath].push([i + 1, line]);
448
+ }
449
+ }
450
+ } catch {
451
+ continue;
452
+ }
453
+ return results;
454
+ }
455
+ /**
456
+ * Structured glob matching returning FileInfo objects.
457
+ */
458
+ async globInfo(pattern, searchPath = "/") {
459
+ if (pattern.startsWith("/")) pattern = pattern.substring(1);
460
+ const resolvedSearchPath = searchPath === "/" ? this.cwd : this.resolvePath(searchPath);
461
+ try {
462
+ if (!(await fs_promises.stat(resolvedSearchPath)).isDirectory()) return [];
463
+ } catch {
464
+ return [];
465
+ }
466
+ const results = [];
467
+ try {
468
+ const matches = await (0, fast_glob.default)(pattern, {
469
+ cwd: resolvedSearchPath,
470
+ absolute: true,
471
+ onlyFiles: true,
472
+ dot: true
473
+ });
474
+ for (const matchedPath of matches) try {
475
+ const fileStat = await fs_promises.stat(matchedPath);
476
+ if (!fileStat.isFile()) continue;
477
+ const normalizedPath = matchedPath.split("/").join(path.sep);
478
+ if (!this.virtualMode) results.push({
479
+ path: normalizedPath,
480
+ is_dir: false,
481
+ size: fileStat.size,
482
+ modified_at: fileStat.mtime.toISOString()
483
+ });
484
+ else {
485
+ const cwdStr = this.cwd.endsWith(path.sep) ? this.cwd : this.cwd + path.sep;
486
+ let relativePath;
487
+ if (normalizedPath.startsWith(cwdStr)) relativePath = normalizedPath.substring(cwdStr.length);
488
+ else if (normalizedPath.startsWith(this.cwd)) relativePath = normalizedPath.substring(this.cwd.length).replace(/^[/\\]/, "");
489
+ else relativePath = normalizedPath;
490
+ relativePath = relativePath.split(path.sep).join("/");
491
+ const virt = "/" + relativePath;
492
+ results.push({
493
+ path: virt,
494
+ is_dir: false,
495
+ size: fileStat.size,
496
+ modified_at: fileStat.mtime.toISOString()
497
+ });
498
+ }
499
+ } catch {
500
+ continue;
501
+ }
502
+ } catch {}
503
+ results.sort((a, b) => a.path.localeCompare(b.path));
504
+ return results;
505
+ }
506
+ };
507
+
508
+ //#endregion
509
+ //#region src/backends/composite.ts
510
+ /**
511
+ * Backend that routes file operations to different backends based on path prefix.
512
+ *
513
+ * This enables hybrid storage strategies by routing files to different backends
514
+ * based on their path prefix. Useful for separating persistent and ephemeral storage,
515
+ * or using different storage backends for different types of files.
516
+ *
517
+ * @example Hybrid storage strategy
518
+ * ```typescript
519
+ * import { CompositeBackend, FilesystemBackend, StateBackend } from 'deepagentsdk';
520
+ *
521
+ * const state = { todos: [], files: {} };
522
+ * const backend = new CompositeBackend(
523
+ * new StateBackend(state), // Default: ephemeral storage
524
+ * {
525
+ * '/persistent/': new FilesystemBackend({ rootDir: './persistent' }), // Persistent files
526
+ * '/cache/': new StateBackend(state), // Cached files (ephemeral)
527
+ * }
528
+ * );
529
+ *
530
+ * const agent = createDeepAgent({
531
+ * model: anthropic('claude-sonnet-4-20250514'),
532
+ * backend,
533
+ * });
534
+ * ```
535
+ *
536
+ * @example Multiple persistent backends
537
+ * ```typescript
538
+ * const backend = new CompositeBackend(
539
+ * new FilesystemBackend({ rootDir: './default' }),
540
+ * {
541
+ * '/user-data/': new FilesystemBackend({ rootDir: './user-data' }),
542
+ * '/system/': new FilesystemBackend({ rootDir: './system' }),
543
+ * }
544
+ * );
545
+ * ```
546
+ */
547
+ var CompositeBackend = class {
548
+ defaultBackend;
549
+ routes;
550
+ sortedRoutes;
551
+ /**
552
+ * Create a new CompositeBackend instance.
553
+ *
554
+ * @param defaultBackend - Backend to use for paths that don't match any route prefix
555
+ * @param routes - Record mapping path prefixes to backends.
556
+ * Routes are matched by longest prefix first.
557
+ * Example: `{ '/persistent/': filesystemBackend, '/cache/': stateBackend }`
558
+ */
559
+ constructor(defaultBackend, routes) {
560
+ this.defaultBackend = defaultBackend;
561
+ this.routes = routes;
562
+ this.sortedRoutes = Object.entries(routes).sort((a, b) => b[0].length - a[0].length);
563
+ }
564
+ /**
565
+ * Determine which backend handles this key and strip prefix.
566
+ */
567
+ getBackendAndKey(key) {
568
+ for (const [prefix, backend] of this.sortedRoutes) if (key.startsWith(prefix)) {
569
+ const suffix = key.substring(prefix.length);
570
+ return [backend, suffix ? "/" + suffix : "/"];
571
+ }
572
+ return [this.defaultBackend, key];
573
+ }
574
+ /**
575
+ * List files and directories in the specified directory (non-recursive).
576
+ */
577
+ async lsInfo(path$1) {
578
+ for (const [routePrefix, backend] of this.sortedRoutes) if (path$1.startsWith(routePrefix.replace(/\/$/, ""))) {
579
+ const suffix = path$1.substring(routePrefix.length);
580
+ const searchPath = suffix ? "/" + suffix : "/";
581
+ const infos = await backend.lsInfo(searchPath);
582
+ const prefixed = [];
583
+ for (const fi of infos) prefixed.push({
584
+ ...fi,
585
+ path: routePrefix.slice(0, -1) + fi.path
586
+ });
587
+ return prefixed;
588
+ }
589
+ if (path$1 === "/") {
590
+ const results = [];
591
+ const defaultInfos = await this.defaultBackend.lsInfo(path$1);
592
+ results.push(...defaultInfos);
593
+ for (const [routePrefix] of this.sortedRoutes) results.push({
594
+ path: routePrefix,
595
+ is_dir: true,
596
+ size: 0,
597
+ modified_at: ""
598
+ });
599
+ results.sort((a, b) => a.path.localeCompare(b.path));
600
+ return results;
601
+ }
602
+ return await this.defaultBackend.lsInfo(path$1);
603
+ }
604
+ /**
605
+ * Read file content, routing to appropriate backend.
606
+ */
607
+ async read(filePath, offset = 0, limit = 2e3) {
608
+ const [backend, strippedKey] = this.getBackendAndKey(filePath);
609
+ return await backend.read(strippedKey, offset, limit);
610
+ }
611
+ /**
612
+ * Read file content as raw FileData.
613
+ */
614
+ async readRaw(filePath) {
615
+ const [backend, strippedKey] = this.getBackendAndKey(filePath);
616
+ return await backend.readRaw(strippedKey);
617
+ }
618
+ /**
619
+ * Structured search results or error string for invalid input.
620
+ */
621
+ async grepRaw(pattern, path$1 = "/", glob$1 = null) {
622
+ for (const [routePrefix, backend] of this.sortedRoutes) if (path$1.startsWith(routePrefix.replace(/\/$/, ""))) {
623
+ const suffix = path$1.substring(routePrefix.length);
624
+ const searchPath = suffix ? "/" + suffix : "/";
625
+ const raw = await backend.grepRaw(pattern, searchPath, glob$1);
626
+ if (typeof raw === "string") return raw;
627
+ return raw.map((m) => ({
628
+ ...m,
629
+ path: routePrefix.slice(0, -1) + m.path
630
+ }));
631
+ }
632
+ const allMatches = [];
633
+ const rawDefault = await this.defaultBackend.grepRaw(pattern, path$1, glob$1);
634
+ if (typeof rawDefault === "string") return rawDefault;
635
+ allMatches.push(...rawDefault);
636
+ for (const [routePrefix, backend] of Object.entries(this.routes)) {
637
+ const raw = await backend.grepRaw(pattern, "/", glob$1);
638
+ if (typeof raw === "string") return raw;
639
+ allMatches.push(...raw.map((m) => ({
640
+ ...m,
641
+ path: routePrefix.slice(0, -1) + m.path
642
+ })));
643
+ }
644
+ return allMatches;
645
+ }
646
+ /**
647
+ * Structured glob matching returning FileInfo objects.
648
+ */
649
+ async globInfo(pattern, path$1 = "/") {
650
+ const results = [];
651
+ for (const [routePrefix, backend] of this.sortedRoutes) if (path$1.startsWith(routePrefix.replace(/\/$/, ""))) {
652
+ const suffix = path$1.substring(routePrefix.length);
653
+ const searchPath = suffix ? "/" + suffix : "/";
654
+ return (await backend.globInfo(pattern, searchPath)).map((fi) => ({
655
+ ...fi,
656
+ path: routePrefix.slice(0, -1) + fi.path
657
+ }));
658
+ }
659
+ const defaultInfos = await this.defaultBackend.globInfo(pattern, path$1);
660
+ results.push(...defaultInfos);
661
+ for (const [routePrefix, backend] of Object.entries(this.routes)) {
662
+ const infos = await backend.globInfo(pattern, "/");
663
+ results.push(...infos.map((fi) => ({
664
+ ...fi,
665
+ path: routePrefix.slice(0, -1) + fi.path
666
+ })));
667
+ }
668
+ results.sort((a, b) => a.path.localeCompare(b.path));
669
+ return results;
670
+ }
671
+ /**
672
+ * Create a new file, routing to appropriate backend.
673
+ */
674
+ async write(filePath, content) {
675
+ const [backend, strippedKey] = this.getBackendAndKey(filePath);
676
+ return await backend.write(strippedKey, content);
677
+ }
678
+ /**
679
+ * Edit a file, routing to appropriate backend.
680
+ */
681
+ async edit(filePath, oldString, newString, replaceAll = false) {
682
+ const [backend, strippedKey] = this.getBackendAndKey(filePath);
683
+ return await backend.edit(strippedKey, oldString, newString, replaceAll);
684
+ }
685
+ };
686
+
687
+ //#endregion
688
+ //#region src/backends/persistent.ts
689
+ /**
690
+ * Simple in-memory implementation of KeyValueStore.
691
+ *
692
+ * Useful for testing or single-session persistence. Data is stored in a Map
693
+ * and does not persist across application restarts.
694
+ *
695
+ * @example Basic usage
696
+ * ```typescript
697
+ * const store = new InMemoryStore();
698
+ * const backend = new PersistentBackend({ store });
699
+ * ```
700
+ *
701
+ * @example For testing
702
+ * ```typescript
703
+ * const store = new InMemoryStore();
704
+ * // ... run tests ...
705
+ * store.clear(); // Clean up after tests
706
+ * ```
707
+ */
708
+ var InMemoryStore = class {
709
+ data = /* @__PURE__ */ new Map();
710
+ makeKey(namespace, key) {
711
+ return [...namespace, key].join(":");
712
+ }
713
+ parseKey(fullKey, namespace) {
714
+ const prefix = namespace.join(":") + ":";
715
+ if (fullKey.startsWith(prefix)) return fullKey.substring(prefix.length);
716
+ return null;
717
+ }
718
+ async get(namespace, key) {
719
+ return this.data.get(this.makeKey(namespace, key));
720
+ }
721
+ async put(namespace, key, value) {
722
+ this.data.set(this.makeKey(namespace, key), value);
723
+ }
724
+ async delete(namespace, key) {
725
+ this.data.delete(this.makeKey(namespace, key));
726
+ }
727
+ async list(namespace) {
728
+ const results = [];
729
+ const prefix = namespace.join(":") + ":";
730
+ for (const [fullKey, value] of this.data.entries()) if (fullKey.startsWith(prefix)) {
731
+ const key = fullKey.substring(prefix.length);
732
+ if (!key.includes(":")) results.push({
733
+ key,
734
+ value
735
+ });
736
+ }
737
+ return results;
738
+ }
739
+ /**
740
+ * Clear all data (useful for testing).
741
+ */
742
+ clear() {
743
+ this.data.clear();
744
+ }
745
+ /**
746
+ * Get the number of stored items.
747
+ */
748
+ size() {
749
+ return this.data.size;
750
+ }
751
+ };
752
+ /**
753
+ * Backend that stores files in a persistent key-value store.
754
+ *
755
+ * This provides cross-conversation file persistence that survives between agent sessions.
756
+ * Files are stored in the provided key-value store, allowing you to use any storage backend
757
+ * (Redis, SQLite, cloud storage, etc.) by implementing the `KeyValueStore` interface.
758
+ *
759
+ * @example Using InMemoryStore (for testing or single-session persistence)
760
+ * ```typescript
761
+ * import { createDeepAgent } from 'deepagentsdk';
762
+ * import { PersistentBackend, InMemoryStore } from 'deepagentsdk';
763
+ * import { anthropic } from '@ai-sdk/anthropic';
764
+ *
765
+ * const store = new InMemoryStore();
766
+ * const backend = new PersistentBackend({ store });
767
+ * const agent = createDeepAgent({
768
+ * model: anthropic('claude-sonnet-4-20250514'),
769
+ * backend,
770
+ * });
771
+ * ```
772
+ *
773
+ * @example With custom namespace for project isolation
774
+ * ```typescript
775
+ * import { createDeepAgent } from 'deepagentsdk';
776
+ * import { PersistentBackend, InMemoryStore } from 'deepagentsdk';
777
+ * import { anthropic } from '@ai-sdk/anthropic';
778
+ *
779
+ * const store = new InMemoryStore();
780
+ * const backend = new PersistentBackend({
781
+ * store,
782
+ * namespace: 'project-123', // Isolate files for this project
783
+ * });
784
+ * const agent = createDeepAgent({
785
+ * model: anthropic('claude-sonnet-4-20250514'),
786
+ * backend,
787
+ * });
788
+ * ```
789
+ *
790
+ * @example Custom KeyValueStore implementation (Redis)
791
+ * ```typescript
792
+ * import { createDeepAgent } from 'deepagentsdk';
793
+ * import { PersistentBackend, type KeyValueStore } from 'deepagentsdk';
794
+ * import { anthropic } from '@ai-sdk/anthropic';
795
+ * import { createClient } from 'redis';
796
+ *
797
+ * class RedisStore implements KeyValueStore {
798
+ * constructor(private redis: ReturnType<typeof createClient>) {}
799
+ *
800
+ * async get(namespace: string[], key: string) {
801
+ * const redisKey = [...namespace, key].join(':');
802
+ * const data = await this.redis.get(redisKey);
803
+ * return data ? JSON.parse(data) : undefined;
804
+ * }
805
+ *
806
+ * async put(namespace: string[], key: string, value: Record<string, unknown>) {
807
+ * const redisKey = [...namespace, key].join(':');
808
+ * await this.redis.set(redisKey, JSON.stringify(value));
809
+ * }
810
+ *
811
+ * async delete(namespace: string[], key: string) {
812
+ * const redisKey = [...namespace, key].join(':');
813
+ * await this.redis.del(redisKey);
814
+ * }
815
+ *
816
+ * async list(namespace: string[]) {
817
+ * const prefix = [...namespace].join(':') + ':';
818
+ * const keys = await this.redis.keys(prefix + '*');
819
+ * const results = [];
820
+ * for (const key of keys) {
821
+ * const data = await this.redis.get(key);
822
+ * if (data) {
823
+ * const relativeKey = key.substring(prefix.length);
824
+ * results.push({ key: relativeKey, value: JSON.parse(data) });
825
+ * }
826
+ * }
827
+ * return results;
828
+ * }
829
+ * }
830
+ *
831
+ * const redis = createClient();
832
+ * await redis.connect();
833
+ *
834
+ * const backend = new PersistentBackend({
835
+ * store: new RedisStore(redis),
836
+ * namespace: 'production'
837
+ * });
838
+ *
839
+ * const agent = createDeepAgent({
840
+ * model: anthropic('claude-sonnet-4-20250514'),
841
+ * backend,
842
+ * });
843
+ * ```
844
+ */
845
+ var PersistentBackend = class {
846
+ store;
847
+ namespacePrefix;
848
+ /**
849
+ * Create a new PersistentBackend instance.
850
+ *
851
+ * @param options - Configuration options
852
+ * @param options.store - The key-value store implementation to use
853
+ * @param options.namespace - Optional namespace prefix for file isolation
854
+ */
855
+ constructor(options) {
856
+ this.store = options.store;
857
+ this.namespacePrefix = options.namespace || "default";
858
+ }
859
+ /**
860
+ * Get the namespace for store operations.
861
+ */
862
+ getNamespace() {
863
+ return [this.namespacePrefix, "filesystem"];
864
+ }
865
+ /**
866
+ * Convert a store value to FileData format.
867
+ */
868
+ convertToFileData(value) {
869
+ if (!value.content || !Array.isArray(value.content) || typeof value.created_at !== "string" || typeof value.modified_at !== "string") throw new Error(`Store item does not contain valid FileData fields. Got keys: ${Object.keys(value).join(", ")}`);
870
+ return {
871
+ content: value.content,
872
+ created_at: value.created_at,
873
+ modified_at: value.modified_at
874
+ };
875
+ }
876
+ /**
877
+ * Convert FileData to a value suitable for store.put().
878
+ */
879
+ convertFromFileData(fileData) {
880
+ return {
881
+ content: fileData.content,
882
+ created_at: fileData.created_at,
883
+ modified_at: fileData.modified_at
884
+ };
885
+ }
886
+ /**
887
+ * List files and directories in the specified directory (non-recursive).
888
+ */
889
+ async lsInfo(path$1) {
890
+ const namespace = this.getNamespace();
891
+ const items = await this.store.list(namespace);
892
+ const infos = [];
893
+ const subdirs = /* @__PURE__ */ new Set();
894
+ const normalizedPath = path$1.endsWith("/") ? path$1 : path$1 + "/";
895
+ for (const item of items) {
896
+ const itemKey = item.key;
897
+ if (!itemKey.startsWith(normalizedPath)) continue;
898
+ const relative = itemKey.substring(normalizedPath.length);
899
+ if (relative.includes("/")) {
900
+ const subdirName = relative.split("/")[0];
901
+ subdirs.add(normalizedPath + subdirName + "/");
902
+ continue;
903
+ }
904
+ try {
905
+ const fd = this.convertToFileData(item.value);
906
+ const size = fd.content.join("\n").length;
907
+ infos.push({
908
+ path: itemKey,
909
+ is_dir: false,
910
+ size,
911
+ modified_at: fd.modified_at
912
+ });
913
+ } catch {
914
+ continue;
915
+ }
916
+ }
917
+ for (const subdir of Array.from(subdirs).sort()) infos.push({
918
+ path: subdir,
919
+ is_dir: true,
920
+ size: 0,
921
+ modified_at: ""
922
+ });
923
+ infos.sort((a, b) => a.path.localeCompare(b.path));
924
+ return infos;
925
+ }
926
+ /**
927
+ * Read file content with line numbers.
928
+ */
929
+ async read(filePath, offset = 0, limit = 2e3) {
930
+ try {
931
+ return require_agent.formatReadResponse(await this.readRaw(filePath), offset, limit);
932
+ } catch (e) {
933
+ return `Error: ${e.message}`;
934
+ }
935
+ }
936
+ /**
937
+ * Read file content as raw FileData.
938
+ */
939
+ async readRaw(filePath) {
940
+ const namespace = this.getNamespace();
941
+ const value = await this.store.get(namespace, filePath);
942
+ if (!value) throw new Error(`File '${filePath}' not found`);
943
+ return this.convertToFileData(value);
944
+ }
945
+ /**
946
+ * Create a new file with content.
947
+ */
948
+ async write(filePath, content) {
949
+ const namespace = this.getNamespace();
950
+ if (await this.store.get(namespace, filePath)) return {
951
+ success: false,
952
+ error: require_agent.FILE_ALREADY_EXISTS(filePath)
953
+ };
954
+ const fileData = require_agent.createFileData(content);
955
+ const storeValue = this.convertFromFileData(fileData);
956
+ await this.store.put(namespace, filePath, storeValue);
957
+ return {
958
+ success: true,
959
+ path: filePath
960
+ };
961
+ }
962
+ /**
963
+ * Edit a file by replacing string occurrences.
964
+ */
965
+ async edit(filePath, oldString, newString, replaceAll = false) {
966
+ const namespace = this.getNamespace();
967
+ const value = await this.store.get(namespace, filePath);
968
+ if (!value) return {
969
+ success: false,
970
+ error: require_agent.FILE_NOT_FOUND(filePath)
971
+ };
972
+ try {
973
+ const fileData = this.convertToFileData(value);
974
+ const result = require_agent.performStringReplacement(require_agent.fileDataToString(fileData), oldString, newString, replaceAll);
975
+ if (typeof result === "string") return {
976
+ success: false,
977
+ error: result
978
+ };
979
+ const [newContent, occurrences] = result;
980
+ const newFileData = require_agent.updateFileData(fileData, newContent);
981
+ const storeValue = this.convertFromFileData(newFileData);
982
+ await this.store.put(namespace, filePath, storeValue);
983
+ return {
984
+ success: true,
985
+ path: filePath,
986
+ occurrences
987
+ };
988
+ } catch (e) {
989
+ return {
990
+ success: false,
991
+ error: `Error: ${e.message}`
992
+ };
993
+ }
994
+ }
995
+ /**
996
+ * Structured search results or error string for invalid input.
997
+ */
998
+ async grepRaw(pattern, path$1 = "/", glob$1 = null) {
999
+ const namespace = this.getNamespace();
1000
+ const items = await this.store.list(namespace);
1001
+ const files = {};
1002
+ for (const item of items) try {
1003
+ files[item.key] = this.convertToFileData(item.value);
1004
+ } catch {
1005
+ continue;
1006
+ }
1007
+ return require_agent.grepMatchesFromFiles(files, pattern, path$1, glob$1);
1008
+ }
1009
+ /**
1010
+ * Structured glob matching returning FileInfo objects.
1011
+ */
1012
+ async globInfo(pattern, path$1 = "/") {
1013
+ const namespace = this.getNamespace();
1014
+ const items = await this.store.list(namespace);
1015
+ const files = {};
1016
+ for (const item of items) try {
1017
+ files[item.key] = this.convertToFileData(item.value);
1018
+ } catch {
1019
+ continue;
1020
+ }
1021
+ const result = require_agent.globSearchFiles(files, pattern, path$1);
1022
+ if (result === "No files found") return [];
1023
+ const paths = result.split("\n");
1024
+ const infos = [];
1025
+ for (const p of paths) {
1026
+ const fd = files[p];
1027
+ const size = fd ? fd.content.join("\n").length : 0;
1028
+ infos.push({
1029
+ path: p,
1030
+ is_dir: false,
1031
+ size,
1032
+ modified_at: fd?.modified_at || ""
1033
+ });
1034
+ }
1035
+ return infos;
1036
+ }
1037
+ /**
1038
+ * Delete a file.
1039
+ */
1040
+ async deleteFile(filePath) {
1041
+ const namespace = this.getNamespace();
1042
+ if (!await this.store.get(namespace, filePath)) return { error: `File '${filePath}' not found` };
1043
+ await this.store.delete(namespace, filePath);
1044
+ return {};
1045
+ }
1046
+ };
1047
+
1048
+ //#endregion
1049
+ //#region src/checkpointer/memory-saver.ts
1050
+ /**
1051
+ * In-memory checkpoint saver.
1052
+ *
1053
+ * Stores checkpoints in a Map. Data is lost when the process exits.
1054
+ * Useful for testing or single-session applications.
1055
+ *
1056
+ * @example
1057
+ * ```typescript
1058
+ * const saver = new MemorySaver();
1059
+ * const agent = createDeepAgent({
1060
+ * model: anthropic('claude-sonnet-4-20250514'),
1061
+ * checkpointer: saver,
1062
+ * });
1063
+ * ```
1064
+ */
1065
+ var MemorySaver = class {
1066
+ checkpoints = /* @__PURE__ */ new Map();
1067
+ namespace;
1068
+ constructor(options = {}) {
1069
+ this.namespace = options.namespace || "default";
1070
+ }
1071
+ getKey(threadId) {
1072
+ return `${this.namespace}:${threadId}`;
1073
+ }
1074
+ async save(checkpoint) {
1075
+ const key = this.getKey(checkpoint.threadId);
1076
+ this.checkpoints.set(key, {
1077
+ ...checkpoint,
1078
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1079
+ });
1080
+ }
1081
+ async load(threadId) {
1082
+ const key = this.getKey(threadId);
1083
+ return this.checkpoints.get(key);
1084
+ }
1085
+ async list() {
1086
+ const prefix = `${this.namespace}:`;
1087
+ const threadIds = [];
1088
+ for (const key of this.checkpoints.keys()) if (key.startsWith(prefix)) threadIds.push(key.substring(prefix.length));
1089
+ return threadIds;
1090
+ }
1091
+ async delete(threadId) {
1092
+ const key = this.getKey(threadId);
1093
+ this.checkpoints.delete(key);
1094
+ }
1095
+ async exists(threadId) {
1096
+ const key = this.getKey(threadId);
1097
+ return this.checkpoints.has(key);
1098
+ }
1099
+ /**
1100
+ * Clear all checkpoints (useful for testing).
1101
+ */
1102
+ clear() {
1103
+ this.checkpoints.clear();
1104
+ }
1105
+ /**
1106
+ * Get the number of stored checkpoints.
1107
+ */
1108
+ size() {
1109
+ return this.checkpoints.size;
1110
+ }
1111
+ };
1112
+
1113
+ //#endregion
1114
+ //#region src/checkpointer/kv-saver.ts
1115
+ /**
1116
+ * Checkpoint saver using KeyValueStore interface.
1117
+ *
1118
+ * This adapter allows using any KeyValueStore implementation (Redis,
1119
+ * database, cloud storage, etc.) for checkpoint storage.
1120
+ *
1121
+ * @example
1122
+ * ```typescript
1123
+ * import { InMemoryStore } from 'deepagentsdk';
1124
+ *
1125
+ * const store = new InMemoryStore();
1126
+ * const saver = new KeyValueStoreSaver({ store });
1127
+ * const agent = createDeepAgent({
1128
+ * model: anthropic('claude-sonnet-4-20250514'),
1129
+ * checkpointer: saver,
1130
+ * });
1131
+ * ```
1132
+ *
1133
+ * @example With Redis
1134
+ * ```typescript
1135
+ * const redisStore = new RedisStore(redisClient); // Your implementation
1136
+ * const saver = new KeyValueStoreSaver({ store: redisStore });
1137
+ * ```
1138
+ */
1139
+ var KeyValueStoreSaver = class {
1140
+ store;
1141
+ namespace;
1142
+ constructor(options) {
1143
+ this.store = options.store;
1144
+ this.namespace = [options.namespace || "default", "checkpoints"];
1145
+ }
1146
+ async save(checkpoint) {
1147
+ const data = {
1148
+ ...checkpoint,
1149
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1150
+ };
1151
+ await this.store.put(this.namespace, checkpoint.threadId, data);
1152
+ }
1153
+ async load(threadId) {
1154
+ const data = await this.store.get(this.namespace, threadId);
1155
+ if (!data) return;
1156
+ return data;
1157
+ }
1158
+ async list() {
1159
+ return (await this.store.list(this.namespace)).map((item) => item.key);
1160
+ }
1161
+ async delete(threadId) {
1162
+ await this.store.delete(this.namespace, threadId);
1163
+ }
1164
+ async exists(threadId) {
1165
+ return await this.store.get(this.namespace, threadId) !== void 0;
1166
+ }
1167
+ };
1168
+
1169
+ //#endregion
1170
+ //#region src/middleware/agent-memory.ts
1171
+ /**
1172
+ * Load agent memory from a file path.
1173
+ * Returns empty string if file doesn't exist or can't be read.
1174
+ */
1175
+ async function loadAgentMemory(filePath) {
1176
+ try {
1177
+ return (await node_fs_promises.readFile(filePath, "utf-8")).trim();
1178
+ } catch {
1179
+ return "";
1180
+ }
1181
+ }
1182
+ /**
1183
+ * Load all additional .md files from the agent's directory (excluding agent.md).
1184
+ * Returns an array of { filename, content } objects.
1185
+ */
1186
+ async function loadAdditionalMemoryFiles(dirPath) {
1187
+ try {
1188
+ const mdFiles = (await node_fs_promises.readdir(dirPath)).filter((f) => f.endsWith(".md") && f !== "agent.md");
1189
+ return (await Promise.all(mdFiles.map(async (filename) => {
1190
+ return {
1191
+ filename,
1192
+ content: await loadAgentMemory(node_path.join(dirPath, filename))
1193
+ };
1194
+ }))).filter((r) => r.content.length > 0);
1195
+ } catch {
1196
+ return [];
1197
+ }
1198
+ }
1199
+ /**
1200
+ * Build the memory section for the system prompt.
1201
+ * This comprehensive prompt teaches the agent how to use memory effectively.
1202
+ */
1203
+ function buildMemorySection(userMemory, projectMemory, additionalFiles, agentId, userMemoryPath, projectMemoryPath) {
1204
+ let sections = [];
1205
+ if (userMemory) sections.push(`# Agent Memory (User-Level)
1206
+
1207
+ The following is your persistent memory stored at ${userMemoryPath}:
1208
+
1209
+ ${userMemory}`);
1210
+ if (projectMemory && projectMemoryPath) sections.push(`# Agent Memory (Project-Level)
1211
+
1212
+ The following is project-specific context stored at ${projectMemoryPath}:
1213
+
1214
+ ${projectMemory}`);
1215
+ if (additionalFiles.length > 0) {
1216
+ const additionalSections = additionalFiles.map(({ filename, content }) => `## ${filename}
1217
+
1218
+ ${content}`);
1219
+ sections.push(`# Additional Context Files
1220
+
1221
+ ${additionalSections.join("\n\n")}`);
1222
+ }
1223
+ if (sections.length === 0) return "";
1224
+ return `
1225
+ <agent_memory>
1226
+ ${sections.join("\n\n---\n\n")}
1227
+
1228
+ ---
1229
+
1230
+ ## How to Use This Memory
1231
+
1232
+ **What is this?**
1233
+ - The content above is your persistent memory, stored in markdown files
1234
+ - **User-level memory** (${userMemoryPath}) contains your core personality, preferences, and cross-project context
1235
+ - **Project-level memory** ${projectMemoryPath ? `(${projectMemoryPath})` : "(not available)"} contains project-specific context and conventions
1236
+
1237
+ **When to read memory:**
1238
+ - You already have the memory content above in your context - no need to read the files unless you need to verify exact content
1239
+ - If you need to check current memory state or see if it's been updated, use \`read_file\` tool
1240
+
1241
+ **When to update memory:**
1242
+ - **User memory**: When you learn something important about the user's preferences, working style, or recurring patterns
1243
+ - **Project memory**: When you discover project-specific conventions, architecture decisions, or important context
1244
+ - **Additional files**: For specialized context that doesn't fit in agent.md (e.g., decision logs, architecture notes)
1245
+
1246
+ **How to update memory:**
1247
+ - Use the \`write_file\` or \`edit_file\` tools with the file paths shown above
1248
+ - Keep entries concise and relevant
1249
+ - Organize information clearly with markdown headings
1250
+ - Remove outdated information when updating
1251
+
1252
+ **Important guidelines:**
1253
+ - Memory is meant for long-term context, not temporary task tracking
1254
+ - Don't store information that's already in the codebase or documentation
1255
+ - Focus on insights, patterns, and preferences that aren't obvious from other sources
1256
+ - When in doubt, ask the user if something should be remembered
1257
+
1258
+ **Example use cases:**
1259
+ - User prefers TypeScript strict mode and comprehensive error handling
1260
+ - Project uses custom testing framework located in \`test-utils/\`
1261
+ - User wants all API responses to follow specific error format
1262
+ - Project has specific commit message conventions
1263
+ </agent_memory>
1264
+ `.trim();
1265
+ }
1266
+ /**
1267
+ * Create agent memory middleware for AI SDK v6.
1268
+ *
1269
+ * This middleware loads agent memory from:
1270
+ * 1. User-level: ~/.deepagents/{agentId}/agent.md (personality, preferences)
1271
+ * 2. Project-level: [git-root]/.deepagents/agent.md (project-specific context)
1272
+ * 3. Additional files: Any other .md files in the user-level directory
1273
+ *
1274
+ * The memory is injected into the system prompt before each model call, teaching
1275
+ * the agent when and how to read/update its own memory using filesystem tools.
1276
+ *
1277
+ * @param options - Configuration for agent memory
1278
+ * @param options.agentId - Unique identifier for the agent (e.g., "code-architect")
1279
+ * @param options.workingDirectory - Optional working directory for project detection (defaults to process.cwd())
1280
+ * @param options.userDeepagentsDir - Optional custom path for user-level .deepagents directory (defaults to ~/.deepagents)
1281
+ * @param options.requestProjectApproval - Optional callback to request approval before creating project .deepagents/ directory
1282
+ * @returns AI SDK v6 middleware
1283
+ *
1284
+ * @example Basic usage
1285
+ * ```typescript
1286
+ * import { createDeepAgent } from 'deepagentsdk';
1287
+ * import { createAgentMemoryMiddleware } from 'deepagentsdk/middleware';
1288
+ * import { anthropic } from '@ai-sdk/anthropic';
1289
+ *
1290
+ * const memoryMiddleware = createAgentMemoryMiddleware({
1291
+ * agentId: 'code-architect',
1292
+ * });
1293
+ *
1294
+ * const agent = createDeepAgent({
1295
+ * model: anthropic('claude-sonnet-4-5'),
1296
+ * middleware: memoryMiddleware,
1297
+ * });
1298
+ * ```
1299
+ *
1300
+ * @example With project approval callback
1301
+ * ```typescript
1302
+ * const memoryMiddleware = createAgentMemoryMiddleware({
1303
+ * agentId: 'code-architect',
1304
+ * requestProjectApproval: async (projectPath) => {
1305
+ * console.log(`Create .deepagents/ in ${projectPath}? (y/n)`);
1306
+ * // ... get user input
1307
+ * return userSaidYes;
1308
+ * }
1309
+ * });
1310
+ * ```
1311
+ *
1312
+ * @example With custom user directory path
1313
+ * ```typescript
1314
+ * const memoryMiddleware = createAgentMemoryMiddleware({
1315
+ * agentId: 'code-architect',
1316
+ * userDeepagentsDir: '/custom/path/.deepagents',
1317
+ * // Memory will be loaded from:
1318
+ * // - /custom/path/.deepagents/code-architect/agent.md
1319
+ * // - [git-root]/.deepagents/agent.md (project-level)
1320
+ * });
1321
+ * ```
1322
+ */
1323
+ function createAgentMemoryMiddleware(options) {
1324
+ const { agentId, workingDirectory, userDeepagentsDir, requestProjectApproval } = options;
1325
+ let memoryLoaded = false;
1326
+ let cachedMemorySection = "";
1327
+ return {
1328
+ specificationVersion: "v3",
1329
+ transformParams: async ({ params }) => {
1330
+ if (!memoryLoaded) {
1331
+ const workDir = workingDirectory || process.cwd();
1332
+ const baseUserDir = userDeepagentsDir || node_path.join(node_os.default.homedir(), ".deepagents");
1333
+ const userAgentDir = node_path.join(baseUserDir, agentId);
1334
+ const userMemoryPath = node_path.join(userAgentDir, "agent.md");
1335
+ const userMemory = await loadAgentMemory(userMemoryPath);
1336
+ if (!userMemory) try {
1337
+ await node_fs_promises.mkdir(userAgentDir, { recursive: true });
1338
+ } catch {}
1339
+ const additionalFiles = await loadAdditionalMemoryFiles(userAgentDir);
1340
+ let projectMemory = "";
1341
+ let projectMemoryPath = null;
1342
+ const gitRoot = await require_load.findGitRoot(workDir);
1343
+ if (gitRoot) {
1344
+ const projectDeepagentsDir = node_path.join(gitRoot, ".deepagents");
1345
+ projectMemoryPath = node_path.join(projectDeepagentsDir, "agent.md");
1346
+ try {
1347
+ await node_fs_promises.stat(projectDeepagentsDir);
1348
+ projectMemory = await loadAgentMemory(projectMemoryPath);
1349
+ } catch {
1350
+ if (requestProjectApproval) {
1351
+ if (await requestProjectApproval(gitRoot)) try {
1352
+ await node_fs_promises.mkdir(projectDeepagentsDir, { recursive: true });
1353
+ } catch {}
1354
+ }
1355
+ }
1356
+ }
1357
+ cachedMemorySection = buildMemorySection(userMemory, projectMemory, additionalFiles, agentId, userMemoryPath, projectMemoryPath);
1358
+ memoryLoaded = true;
1359
+ }
1360
+ if (cachedMemorySection) {
1361
+ const updatedPrompt = params.prompt.map((msg) => {
1362
+ if (msg.role === "system") return {
1363
+ ...msg,
1364
+ content: `${msg.content}\n\n${cachedMemorySection}`
1365
+ };
1366
+ return msg;
1367
+ });
1368
+ return {
1369
+ ...params,
1370
+ prompt: updatedPrompt
1371
+ };
1372
+ }
1373
+ return params;
1374
+ }
1375
+ };
1376
+ }
1377
+
1378
+ //#endregion
1379
+ exports.BASE_PROMPT = require_agent.BASE_PROMPT;
1380
+ exports.BaseSandbox = require_file_saver.BaseSandbox;
1381
+ exports.CompositeBackend = CompositeBackend;
1382
+ exports.DEFAULT_EVICTION_TOKEN_LIMIT = require_agent.DEFAULT_EVICTION_TOKEN_LIMIT;
1383
+ exports.DEFAULT_GENERAL_PURPOSE_DESCRIPTION = require_agent.DEFAULT_GENERAL_PURPOSE_DESCRIPTION;
1384
+ exports.DEFAULT_KEEP_MESSAGES = require_agent.DEFAULT_KEEP_MESSAGES;
1385
+ exports.DEFAULT_SUBAGENT_PROMPT = require_agent.DEFAULT_SUBAGENT_PROMPT;
1386
+ exports.DEFAULT_SUMMARIZATION_THRESHOLD = require_agent.DEFAULT_SUMMARIZATION_THRESHOLD;
1387
+ exports.DeepAgent = require_agent.DeepAgent;
1388
+ exports.EXECUTE_SYSTEM_PROMPT = require_agent.EXECUTE_SYSTEM_PROMPT;
1389
+ exports.FILESYSTEM_SYSTEM_PROMPT = require_agent.FILESYSTEM_SYSTEM_PROMPT;
1390
+ exports.FileSaver = require_file_saver.FileSaver;
1391
+ exports.FilesystemBackend = FilesystemBackend;
1392
+ exports.InMemoryStore = InMemoryStore;
1393
+ exports.KeyValueStoreSaver = KeyValueStoreSaver;
1394
+ exports.LocalSandbox = require_file_saver.LocalSandbox;
1395
+ exports.MemorySaver = MemorySaver;
1396
+ exports.PersistentBackend = PersistentBackend;
1397
+ exports.StateBackend = require_agent.StateBackend;
1398
+ exports.TASK_SYSTEM_PROMPT = require_agent.TASK_SYSTEM_PROMPT;
1399
+ exports.TODO_SYSTEM_PROMPT = require_agent.TODO_SYSTEM_PROMPT;
1400
+ Object.defineProperty(exports, 'ToolLoopAgent', {
1401
+ enumerable: true,
1402
+ get: function () {
1403
+ return ai.ToolLoopAgent;
1404
+ }
1405
+ });
1406
+ exports.createAgentMemoryMiddleware = createAgentMemoryMiddleware;
1407
+ exports.createDeepAgent = require_agent.createDeepAgent;
1408
+ exports.createEditFileTool = require_agent.createEditFileTool;
1409
+ exports.createExecuteTool = require_agent.createExecuteTool;
1410
+ exports.createExecuteToolFromBackend = require_agent.createExecuteToolFromBackend;
1411
+ exports.createFetchUrlTool = require_agent.createFetchUrlTool;
1412
+ exports.createFilesystemTools = require_agent.createFilesystemTools;
1413
+ exports.createGlobTool = require_agent.createGlobTool;
1414
+ exports.createGrepTool = require_agent.createGrepTool;
1415
+ exports.createHttpRequestTool = require_agent.createHttpRequestTool;
1416
+ exports.createLsTool = require_agent.createLsTool;
1417
+ exports.createReadFileTool = require_agent.createReadFileTool;
1418
+ exports.createSubagentTool = require_agent.createSubagentTool;
1419
+ exports.createTodosTool = require_agent.createTodosTool;
1420
+ exports.createToolResultWrapper = require_agent.createToolResultWrapper;
1421
+ exports.createWebSearchTool = require_agent.createWebSearchTool;
1422
+ exports.createWebTools = require_agent.createWebTools;
1423
+ exports.createWriteFileTool = require_agent.createWriteFileTool;
1424
+ exports.edit_file = require_agent.edit_file;
1425
+ exports.estimateMessagesTokens = require_agent.estimateMessagesTokens;
1426
+ exports.estimateTokens = require_agent.estimateTokens;
1427
+ exports.eventHasStructuredOutput = eventHasStructuredOutput;
1428
+ exports.evictToolResult = require_agent.evictToolResult;
1429
+ exports.execute = require_agent.execute;
1430
+ exports.fetch_url = require_agent.fetch_url;
1431
+ exports.findGitRoot = require_load.findGitRoot;
1432
+ exports.getEventOutput = getEventOutput;
1433
+ exports.getStructuredOutput = getStructuredOutput;
1434
+ exports.getTaskToolDescription = require_agent.getTaskToolDescription;
1435
+ exports.glob = require_agent.glob;
1436
+ exports.grep = require_agent.grep;
1437
+ exports.hasDanglingToolCalls = require_agent.hasDanglingToolCalls;
1438
+ exports.hasStructuredOutput = hasStructuredOutput;
1439
+ Object.defineProperty(exports, 'hasToolCall', {
1440
+ enumerable: true,
1441
+ get: function () {
1442
+ return ai.hasToolCall;
1443
+ }
1444
+ });
1445
+ exports.htmlToMarkdown = require_agent.htmlToMarkdown;
1446
+ exports.http_request = require_agent.http_request;
1447
+ exports.isSandboxBackend = require_agent.isSandboxBackend;
1448
+ exports.listSkills = require_load.listSkills;
1449
+ exports.ls = require_agent.ls;
1450
+ exports.needsSummarization = require_agent.needsSummarization;
1451
+ exports.parseSkillMetadata = require_load.parseSkillMetadata;
1452
+ exports.patchToolCalls = require_agent.patchToolCalls;
1453
+ exports.read_file = require_agent.read_file;
1454
+ exports.shouldEvict = require_agent.shouldEvict;
1455
+ Object.defineProperty(exports, 'stepCountIs', {
1456
+ enumerable: true,
1457
+ get: function () {
1458
+ return ai.stepCountIs;
1459
+ }
1460
+ });
1461
+ exports.summarizeIfNeeded = require_agent.summarizeIfNeeded;
1462
+ exports.web_search = require_agent.web_search;
1463
+ Object.defineProperty(exports, 'wrapLanguageModel', {
1464
+ enumerable: true,
1465
+ get: function () {
1466
+ return ai.wrapLanguageModel;
1467
+ }
1468
+ });
1469
+ exports.write_file = require_agent.write_file;
1470
+ exports.write_todos = require_agent.write_todos;
1471
+ //# sourceMappingURL=index.cjs.map