deepagents 1.2.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -4,10 +4,11 @@ import { AnnotationRoot } from "@langchain/langgraph";
4
4
  import { StructuredTool as StructuredTool$1 } from "@langchain/core/tools";
5
5
  import { BaseLanguageModel, LanguageModelLike } from "@langchain/core/language_models/base";
6
6
  import { BaseCheckpointSaver, BaseStore } from "@langchain/langgraph-checkpoint";
7
+ import { Runnable } from "@langchain/core/runnables";
7
8
  import { InteropZodObject } from "@langchain/core/utils/types";
8
9
 
9
10
  //#region src/backends/protocol.d.ts
10
-
11
+ type MaybePromise<T> = T | Promise<T>;
11
12
  /**
12
13
  * Structured file listing info.
13
14
  *
@@ -65,6 +66,8 @@ interface WriteResult {
65
66
  * External backends set null (already persisted to disk/S3/database/etc).
66
67
  */
67
68
  filesUpdate?: Record<string, FileData> | null;
69
+ /** Metadata for the write operation, attached to the ToolMessage */
70
+ metadata?: Record<string, unknown>;
68
71
  }
69
72
  /**
70
73
  * Result from backend edit operations.
@@ -85,6 +88,8 @@ interface EditResult {
85
88
  filesUpdate?: Record<string, FileData> | null;
86
89
  /** Number of replacements made, undefined on failure */
87
90
  occurrences?: number;
91
+ /** Metadata for the edit operation, attached to the ToolMessage */
92
+ metadata?: Record<string, unknown>;
88
93
  }
89
94
  /**
90
95
  * Protocol for pluggable memory backends (single, unified).
@@ -107,7 +112,7 @@ interface BackendProtocol {
107
112
  * @param path - Absolute path to directory
108
113
  * @returns List of FileInfo objects for files and directories directly in the directory
109
114
  */
110
- lsInfo(path: string): FileInfo[] | Promise<FileInfo[]>;
115
+ lsInfo(path: string): MaybePromise<FileInfo[]>;
111
116
  /**
112
117
  * Read file content with line numbers or an error string.
113
118
  *
@@ -116,14 +121,14 @@ interface BackendProtocol {
116
121
  * @param limit - Maximum number of lines to read, default 2000
117
122
  * @returns Formatted file content with line numbers, or error message
118
123
  */
119
- read(filePath: string, offset?: number, limit?: number): string | Promise<string>;
124
+ read(filePath: string, offset?: number, limit?: number): MaybePromise<string>;
120
125
  /**
121
126
  * Read file content as raw FileData.
122
127
  *
123
128
  * @param filePath - Absolute file path
124
129
  * @returns Raw file content as FileData
125
130
  */
126
- readRaw(filePath: string): FileData | Promise<FileData>;
131
+ readRaw(filePath: string): MaybePromise<FileData>;
127
132
  /**
128
133
  * Structured search results or error string for invalid input.
129
134
  *
@@ -134,7 +139,7 @@ interface BackendProtocol {
134
139
  * @param glob - Optional glob pattern to filter files (e.g., "*.py")
135
140
  * @returns List of GrepMatch objects or error string for invalid regex
136
141
  */
137
- grepRaw(pattern: string, path?: string | null, glob?: string | null): GrepMatch[] | string | Promise<GrepMatch[] | string>;
142
+ grepRaw(pattern: string, path?: string | null, glob?: string | null): MaybePromise<GrepMatch[] | string>;
138
143
  /**
139
144
  * Structured glob matching returning FileInfo objects.
140
145
  *
@@ -142,7 +147,7 @@ interface BackendProtocol {
142
147
  * @param path - Base path to search from (default: "/")
143
148
  * @returns List of FileInfo objects matching the pattern
144
149
  */
145
- globInfo(pattern: string, path?: string): FileInfo[] | Promise<FileInfo[]>;
150
+ globInfo(pattern: string, path?: string): MaybePromise<FileInfo[]>;
146
151
  /**
147
152
  * Create a new file.
148
153
  *
@@ -150,7 +155,7 @@ interface BackendProtocol {
150
155
  * @param content - File content as string
151
156
  * @returns WriteResult with error populated on failure
152
157
  */
153
- write(filePath: string, content: string): WriteResult | Promise<WriteResult>;
158
+ write(filePath: string, content: string): MaybePromise<WriteResult>;
154
159
  /**
155
160
  * Edit a file by replacing string occurrences.
156
161
  *
@@ -160,7 +165,7 @@ interface BackendProtocol {
160
165
  * @param replaceAll - If true, replace all occurrences (default: false)
161
166
  * @returns EditResult with error, path, filesUpdate, and occurrences
162
167
  */
163
- edit(filePath: string, oldString: string, newString: string, replaceAll?: boolean): EditResult | Promise<EditResult>;
168
+ edit(filePath: string, oldString: string, newString: string, replaceAll?: boolean): MaybePromise<EditResult>;
164
169
  }
165
170
  /**
166
171
  * State and store container for backend initialization.
@@ -198,15 +203,6 @@ interface StateAndStore {
198
203
  type BackendFactory = (stateAndStore: StateAndStore) => BackendProtocol;
199
204
  //#endregion
200
205
  //#region src/middleware/fs.d.ts
201
- type FilesystemEventResponse = {
202
- kind: "raw-contents";
203
- } | {
204
- kind: "metadata";
205
- data: Record<string, unknown>;
206
- };
207
- interface FilesystemEvents {
208
- onWrite?: (path: string, backend: BackendProtocol) => void | FilesystemEventResponse | Promise<void | FilesystemEventResponse>;
209
- }
210
206
  /**
211
207
  * Options for creating filesystem middleware.
212
208
  */
@@ -219,8 +215,6 @@ interface FilesystemMiddlewareOptions {
219
215
  customToolDescriptions?: Record<string, string> | null;
220
216
  /** Optional token limit before evicting a tool result to the filesystem (default: 20000 tokens, ~80KB) */
221
217
  toolTokenLimitBeforeEvict?: number | null;
222
- /** Filesystem events callbacks */
223
- events?: FilesystemEvents;
224
218
  }
225
219
  /**
226
220
  * Create filesystem middleware with all tools and features.
@@ -228,6 +222,17 @@ interface FilesystemMiddlewareOptions {
228
222
  declare function createFilesystemMiddleware(options?: FilesystemMiddlewareOptions): langchain0.AgentMiddleware<any, undefined, any>;
229
223
  //#endregion
230
224
  //#region src/middleware/subagents.d.ts
225
+ /**
226
+ * Type definitions for pre-compiled agents.
227
+ */
228
+ interface CompiledSubAgent {
229
+ /** The name of the agent */
230
+ name: string;
231
+ /** The description of the agent */
232
+ description: string;
233
+ /** The agent instance */
234
+ runnable: ReactAgent<any, any, any, any> | Runnable;
235
+ }
231
236
  /**
232
237
  * Type definitions for subagents
233
238
  */
@@ -260,7 +265,7 @@ interface SubAgentMiddlewareOptions {
260
265
  /** The tool configs for the default general-purpose subagent */
261
266
  defaultInterruptOn?: Record<string, boolean | InterruptOnConfig> | null;
262
267
  /** A list of additional subagents to provide to the agent */
263
- subagents?: Array<SubAgent>;
268
+ subagents?: (SubAgent | CompiledSubAgent)[];
264
269
  /** Full system prompt override */
265
270
  systemPrompt?: string | null;
266
271
  /** Whether to include the general-purpose agent */
@@ -627,7 +632,7 @@ interface CreateDeepAgentParams<ContextSchema extends AnnotationRoot<any> | Inte
627
632
  /** Custom middleware to apply after standard middleware */
628
633
  middleware?: AgentMiddleware[];
629
634
  /** List of subagent specifications for task delegation */
630
- subagents?: SubAgent[];
635
+ subagents?: (SubAgent | CompiledSubAgent)[];
631
636
  /** Structured output response format for the agent */
632
637
  responseFormat?: any;
633
638
  /** Optional schema for context (not persisted between invocations) */
@@ -667,4 +672,4 @@ interface CreateDeepAgentParams<ContextSchema extends AnnotationRoot<any> | Inte
667
672
  */
668
673
  declare function createDeepAgent<ContextSchema extends AnnotationRoot<any> | InteropZodObject = AnnotationRoot<any>>(params?: CreateDeepAgentParams<ContextSchema>): ReactAgent<any, any, ContextSchema, any>;
669
674
  //#endregion
670
- export { type BackendFactory, type BackendProtocol, CompositeBackend, type CreateDeepAgentParams, type EditResult, type FileData, type FileInfo, FilesystemBackend, type FilesystemMiddlewareOptions, type GrepMatch, StateBackend, StoreBackend, type SubAgent, type SubAgentMiddlewareOptions, type WriteResult, createDeepAgent, createFilesystemMiddleware, createPatchToolCallsMiddleware, createSubAgentMiddleware };
675
+ export { type BackendFactory, type BackendProtocol, type CompiledSubAgent, CompositeBackend, type CreateDeepAgentParams, type EditResult, type FileData, type FileInfo, FilesystemBackend, type FilesystemMiddlewareOptions, type GrepMatch, StateBackend, StoreBackend, type SubAgent, type SubAgentMiddlewareOptions, type WriteResult, createDeepAgent, createFilesystemMiddleware, createPatchToolCallsMiddleware, createSubAgentMiddleware };
package/dist/index.js CHANGED
@@ -405,6 +405,16 @@ function fileDataReducer(left, right) {
405
405
  return result;
406
406
  }
407
407
  /**
408
+ * Shared filesystem state schema.
409
+ * Defined at module level to ensure the same object identity is used across all agents,
410
+ * preventing "Channel already exists with different type" errors when multiple agents
411
+ * use createFilesystemMiddleware.
412
+ */
413
+ const FilesystemStateSchema = z.object({ files: withLangGraph(z.record(z.string(), FileDataSchema).default({}), { reducer: {
414
+ fn: fileDataReducer,
415
+ schema: z.record(z.string(), FileDataSchema.nullable())
416
+ } }) });
417
+ /**
408
418
  * Resolve backend from factory or instance.
409
419
  *
410
420
  * @param backend - Backend instance or factory function
@@ -414,12 +424,6 @@ function getBackend(backend, stateAndStore) {
414
424
  if (typeof backend === "function") return backend(stateAndStore);
415
425
  return backend;
416
426
  }
417
- /**
418
- * Helper to await if Promise, otherwise return value directly.
419
- */
420
- async function awaitIfPromise(value) {
421
- return value;
422
- }
423
427
  const FILESYSTEM_SYSTEM_PROMPT = `You have access to a virtual filesystem. All file paths must start with a /.
424
428
 
425
429
  - ls: list files in a directory (requires absolute path)
@@ -445,7 +449,7 @@ function createLsTool(backend, options) {
445
449
  store: config.store
446
450
  });
447
451
  const path$1 = input.path || "/";
448
- const infos = await awaitIfPromise(resolvedBackend.lsInfo(path$1));
452
+ const infos = await resolvedBackend.lsInfo(path$1);
449
453
  if (infos.length === 0) return `No files found in ${path$1}`;
450
454
  const lines = [];
451
455
  for (const info of infos) if (info.is_dir) lines.push(`${info.path} (directory)`);
@@ -471,7 +475,7 @@ function createReadFileTool(backend, options) {
471
475
  store: config.store
472
476
  });
473
477
  const { file_path, offset = 0, limit = 2e3 } = input;
474
- return await awaitIfPromise(resolvedBackend.read(file_path, offset, limit));
478
+ return await resolvedBackend.read(file_path, offset, limit);
475
479
  }, {
476
480
  name: "read_file",
477
481
  description: customDescription || READ_FILE_TOOL_DESCRIPTION,
@@ -486,7 +490,7 @@ function createReadFileTool(backend, options) {
486
490
  * Create write_file tool using backend.
487
491
  */
488
492
  function createWriteFileTool(backend, options) {
489
- const { customDescription, events } = options;
493
+ const { customDescription } = options;
490
494
  return tool(async (input, config) => {
491
495
  const resolvedBackend = getBackend(backend, {
492
496
  state: getCurrentTaskInput(config),
@@ -495,16 +499,11 @@ function createWriteFileTool(backend, options) {
495
499
  const { file_path, content } = input;
496
500
  const result = await resolvedBackend.write(file_path, content);
497
501
  if (result.error) return result.error;
498
- const resolved = await events?.onWrite?.(file_path, resolvedBackend) ?? void 0;
499
- const metadata = await (async () => {
500
- if (resolved?.kind === "metadata") return resolved.data;
501
- if (resolved?.kind === "raw-contents") return { ...await resolvedBackend.readRaw(file_path) };
502
- })();
503
502
  const message = new ToolMessage({
504
503
  content: `Successfully wrote to '${file_path}'`,
505
504
  tool_call_id: config.toolCall?.id,
506
505
  name: "write_file",
507
- metadata
506
+ metadata: result.metadata
508
507
  });
509
508
  if (result.filesUpdate) return new Command({ update: {
510
509
  files: result.filesUpdate,
@@ -524,25 +523,20 @@ function createWriteFileTool(backend, options) {
524
523
  * Create edit_file tool using backend.
525
524
  */
526
525
  function createEditFileTool(backend, options) {
527
- const { customDescription, events } = options;
526
+ const { customDescription } = options;
528
527
  return tool(async (input, config) => {
529
528
  const resolvedBackend = getBackend(backend, {
530
529
  state: getCurrentTaskInput(config),
531
530
  store: config.store
532
531
  });
533
532
  const { file_path, old_string, new_string, replace_all = false } = input;
534
- const result = await awaitIfPromise(resolvedBackend.edit(file_path, old_string, new_string, replace_all));
533
+ const result = await resolvedBackend.edit(file_path, old_string, new_string, replace_all);
535
534
  if (result.error) return result.error;
536
- const resolved = await events?.onWrite?.(file_path, resolvedBackend) ?? void 0;
537
- const metadata = await (async () => {
538
- if (resolved?.kind === "metadata") return resolved.data;
539
- if (resolved?.kind === "raw-contents") return { ...await resolvedBackend.readRaw(file_path) };
540
- })();
541
535
  const message = new ToolMessage({
542
536
  content: `Successfully replaced ${result.occurrences} occurrence(s) in '${file_path}'`,
543
537
  tool_call_id: config.toolCall?.id,
544
538
  name: "edit_file",
545
- metadata
539
+ metadata: result.metadata
546
540
  });
547
541
  if (result.filesUpdate) return new Command({ update: {
548
542
  files: result.filesUpdate,
@@ -571,7 +565,7 @@ function createGlobTool(backend, options) {
571
565
  store: config.store
572
566
  });
573
567
  const { pattern, path: path$1 = "/" } = input;
574
- const infos = await awaitIfPromise(resolvedBackend.globInfo(pattern, path$1));
568
+ const infos = await resolvedBackend.globInfo(pattern, path$1);
575
569
  if (infos.length === 0) return `No files found matching pattern '${pattern}'`;
576
570
  return infos.map((info) => info.path).join("\n");
577
571
  }, {
@@ -594,7 +588,7 @@ function createGrepTool(backend, options) {
594
588
  store: config.store
595
589
  });
596
590
  const { pattern, path: path$1 = "/", glob = null } = input;
597
- const result = await awaitIfPromise(resolvedBackend.grepRaw(pattern, path$1, glob));
591
+ const result = await resolvedBackend.grepRaw(pattern, path$1, glob);
598
592
  if (typeof result === "string") return result;
599
593
  if (result.length === 0) return `No matches found for pattern '${pattern}'`;
600
594
  const lines = [];
@@ -621,41 +615,19 @@ function createGrepTool(backend, options) {
621
615
  * Create filesystem middleware with all tools and features.
622
616
  */
623
617
  function createFilesystemMiddleware(options = {}) {
624
- const { backend = (stateAndStore) => new StateBackend(stateAndStore), systemPrompt: customSystemPrompt = null, customToolDescriptions = null, toolTokenLimitBeforeEvict = 2e4, events = void 0 } = options;
618
+ const { backend = (stateAndStore) => new StateBackend(stateAndStore), systemPrompt: customSystemPrompt = null, customToolDescriptions = null, toolTokenLimitBeforeEvict = 2e4 } = options;
625
619
  const systemPrompt = customSystemPrompt || FILESYSTEM_SYSTEM_PROMPT;
626
- const tools = [
627
- createLsTool(backend, {
628
- customDescription: customToolDescriptions?.ls ?? null,
629
- events
630
- }),
631
- createReadFileTool(backend, {
632
- customDescription: customToolDescriptions?.read_file ?? null,
633
- events
634
- }),
635
- createWriteFileTool(backend, {
636
- customDescription: customToolDescriptions?.write_file ?? null,
637
- events
638
- }),
639
- createEditFileTool(backend, {
640
- customDescription: customToolDescriptions?.edit_file ?? null,
641
- events
642
- }),
643
- createGlobTool(backend, {
644
- customDescription: customToolDescriptions?.glob ?? null,
645
- events
646
- }),
647
- createGrepTool(backend, {
648
- customDescription: customToolDescriptions?.grep ?? null,
649
- events
650
- })
651
- ];
652
620
  return createMiddleware({
653
621
  name: "FilesystemMiddleware",
654
- stateSchema: z.object({ files: withLangGraph(z.record(z.string(), FileDataSchema).default({}), { reducer: {
655
- fn: fileDataReducer,
656
- schema: z.record(z.string(), FileDataSchema.nullable())
657
- } }) }),
658
- tools,
622
+ stateSchema: FilesystemStateSchema,
623
+ tools: [
624
+ createLsTool(backend, { customDescription: customToolDescriptions?.ls }),
625
+ createReadFileTool(backend, { customDescription: customToolDescriptions?.read_file }),
626
+ createWriteFileTool(backend, { customDescription: customToolDescriptions?.write_file }),
627
+ createEditFileTool(backend, { customDescription: customToolDescriptions?.edit_file }),
628
+ createGlobTool(backend, { customDescription: customToolDescriptions?.glob }),
629
+ createGrepTool(backend, { customDescription: customToolDescriptions?.grep })
630
+ ],
659
631
  wrapModelCall: systemPrompt ? async (request, handler) => {
660
632
  const currentSystemPrompt = request.systemPrompt || "";
661
633
  const newSystemPrompt = currentSystemPrompt ? `${currentSystemPrompt}\n\n${systemPrompt}` : systemPrompt;
@@ -673,7 +645,7 @@ function createFilesystemMiddleware(options = {}) {
673
645
  store: request.config?.store
674
646
  });
675
647
  const evictPath = `/large_tool_results/${sanitizeToolCallId(request.toolCall?.id || msg.tool_call_id)}`;
676
- const writeResult = await awaitIfPromise(resolvedBackend.write(evictPath, msg.content));
648
+ const writeResult = await resolvedBackend.write(evictPath, msg.content);
677
649
  if (writeResult.error) return {
678
650
  message: msg,
679
651
  filesUpdate: null
@@ -919,15 +891,18 @@ function getSubagents(options) {
919
891
  }
920
892
  for (const agentParams of subagents) {
921
893
  subagentDescriptions.push(`- ${agentParams.name}: ${agentParams.description}`);
922
- const middleware = agentParams.middleware ? [...defaultSubagentMiddleware, ...agentParams.middleware] : [...defaultSubagentMiddleware];
923
- const interruptOn = agentParams.interruptOn || defaultInterruptOn;
924
- if (interruptOn) middleware.push(humanInTheLoopMiddleware({ interruptOn }));
925
- agents[agentParams.name] = createAgent({
926
- model: agentParams.model ?? defaultModel,
927
- systemPrompt: agentParams.systemPrompt,
928
- tools: agentParams.tools ?? defaultTools,
929
- middleware
930
- });
894
+ if ("runnable" in agentParams) agents[agentParams.name] = agentParams.runnable;
895
+ else {
896
+ const middleware = agentParams.middleware ? [...defaultSubagentMiddleware, ...agentParams.middleware] : [...defaultSubagentMiddleware];
897
+ const interruptOn = agentParams.interruptOn || defaultInterruptOn;
898
+ if (interruptOn) middleware.push(humanInTheLoopMiddleware({ interruptOn }));
899
+ agents[agentParams.name] = createAgent({
900
+ model: agentParams.model ?? defaultModel,
901
+ systemPrompt: agentParams.systemPrompt,
902
+ tools: agentParams.tools ?? defaultTools,
903
+ middleware
904
+ });
905
+ }
931
906
  }
932
907
  return {
933
908
  agents,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepagents",
3
- "version": "1.2.0",
3
+ "version": "1.3.1",
4
4
  "description": "Deep Agents - a library for building controllable AI agents with LangGraph",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",