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