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