deepagents 1.0.0 → 1.1.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.
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
- MIT License
1
+ The MIT License
2
2
 
3
- Copyright (c) 2025 Oleksandr Shevtsov
3
+ Copyright (c) LangChain, Inc.
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -9,13 +9,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
9
  copies of the Software, and to permit persons to whom the Software is
10
10
  furnished to do so, subject to the following conditions:
11
11
 
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
14
 
15
15
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
16
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
17
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
18
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
package/README.md CHANGED
@@ -1,151 +1,555 @@
1
- ## This is an unofficial port of the [hwchase17/deepagents](https://github.com/hwchase17/deepagents) Python package to TypeScript
2
-
3
1
  # 🧠🤖Deep Agents
4
2
 
5
3
  Using an LLM to call tools in a loop is the simplest form of an agent.
6
- This architecture, however, can yield agents that are shallow and fail to plan and act over longer, more complex tasks.
7
- Applications like “Deep Research”, "Manus", and “Claude Code” have gotten around this limitation by implementing a combination of four things:
4
+ This architecture, however, can yield agents that are "shallow" and fail to plan and act over longer, more complex tasks.
5
+
6
+ Applications like "Deep Research", "Manus", and "Claude Code" have gotten around this limitation by implementing a combination of four things:
8
7
  a **planning tool**, **sub agents**, access to a **file system**, and a **detailed prompt**.
9
8
 
10
- <img src="deep_agents.png" alt="deep agent" width="600"/>
9
+ > 💡 **Tip:** Looking for the Python version of this package? See [here: langchain-ai/deepagents](https://github.com/langchain-ai/deepagents)
10
+
11
+ <!-- TODO: Add image here -->
11
12
 
12
- `deepagents` is a Typescript package that implements these in a general purpose way so that you can easily create a Deep Agent for your application.
13
+ `deepagents` is a TypeScript package that implements these in a general purpose way so that you can easily create a Deep Agent for your application.
13
14
 
14
15
  **Acknowledgements: This project was primarily inspired by Claude Code, and initially was largely an attempt to see what made Claude Code general purpose, and make it even more so.**
15
16
 
16
17
  ## Installation
17
18
 
18
19
  ```bash
20
+ # npm
19
21
  npm install deepagents
22
+
23
+ # yarn
24
+ yarn add deepagents
25
+
26
+ # pnpm
27
+ pnpm add deepagents
20
28
  ```
21
29
 
22
30
  ## Usage
23
31
 
24
- > NB! This part of documentation is not fully migrated
32
+ (To run the example below, you will need to `npm install @langchain/tavily`).
25
33
 
26
- See [src/examples/research/agent.ts](src/examples/research/agent.ts) for a more complex example.
34
+ Make sure to set `TAVILY_API_KEY` in your environment. You can generate one [here](https://www.tavily.com/).
35
+
36
+ ```typescript
37
+ import { tool } from "langchain";
38
+ import { TavilySearch } from "@langchain/tavily";
39
+ import { createDeepAgent } from "deepagents";
40
+ import { z } from "zod";
41
+
42
+ // Web search tool
43
+ const internetSearch = tool(
44
+ async ({
45
+ query,
46
+ maxResults = 5,
47
+ topic = "general",
48
+ includeRawContent = false,
49
+ }: {
50
+ query: string;
51
+ maxResults?: number;
52
+ topic?: "general" | "news" | "finance";
53
+ includeRawContent?: boolean;
54
+ }) => {
55
+ const tavilySearch = new TavilySearch({
56
+ maxResults,
57
+ tavilyApiKey: process.env.TAVILY_API_KEY,
58
+ includeRawContent,
59
+ topic,
60
+ });
61
+ return await tavilySearch._call({ query });
62
+ },
63
+ {
64
+ name: "internet_search",
65
+ description: "Run a web search",
66
+ schema: z.object({
67
+ query: z.string().describe("The search query"),
68
+ maxResults: z
69
+ .number()
70
+ .optional()
71
+ .default(5)
72
+ .describe("Maximum number of results to return"),
73
+ topic: z
74
+ .enum(["general", "news", "finance"])
75
+ .optional()
76
+ .default("general")
77
+ .describe("Search topic category"),
78
+ includeRawContent: z
79
+ .boolean()
80
+ .optional()
81
+ .default(false)
82
+ .describe("Whether to include raw content"),
83
+ }),
84
+ },
85
+ );
86
+
87
+ // System prompt to steer the agent to be an expert researcher
88
+ const researchInstructions = `You are an expert researcher. Your job is to conduct thorough research, and then write a polished report.
89
+
90
+ You have access to an internet search tool as your primary means of gathering information.
91
+
92
+ ## \`internet_search\`
93
+
94
+ Use this to run an internet search for a given query. You can specify the max number of results to return, the topic, and whether raw content should be included.
95
+ `;
96
+
97
+ // Create the deep agent
98
+ const agent = createDeepAgent({
99
+ tools: [internetSearch],
100
+ systemPrompt: researchInstructions,
101
+ });
102
+
103
+ // Invoke the agent
104
+ const result = await agent.invoke({
105
+ messages: [{ role: "user", content: "What is langgraph?" }],
106
+ });
107
+ ```
108
+
109
+ See [examples/research/research-agent.ts](examples/research/research-agent.ts) for a more complex example.
27
110
 
28
111
  The agent created with `createDeepAgent` is just a LangGraph graph - so you can interact with it (streaming, human-in-the-loop, memory, studio)
29
112
  in the same way you would any LangGraph agent.
30
113
 
31
- ## Creating a custom deep agent
114
+ ## Core Capabilities
32
115
 
33
- There is an option argument with three properties you can pass to `createDeepAgent` to create your own custom deep agent.
116
+ **Planning & Task Decomposition**
34
117
 
35
- ### `tools` (Required)
118
+ Deep Agents include a built-in `write_todos` tool that enables agents to break down complex tasks into discrete steps, track progress, and adapt plans as new information emerges.
36
119
 
37
- This should be a list of functions or LangChain `@tool` objects.
38
- The agent (and any subagents) will have access to these tools.
120
+ **Context Management**
39
121
 
40
- ### `instructions` (Required)
122
+ File system tools (`ls`, `read_file`, `write_file`, `edit_file`, `glob`, `grep`) allow agents to offload large context to memory, preventing context window overflow and enabling work with variable-length tool results.
41
123
 
42
- This will serve as part of the prompt of the deep agent.
43
- Note that there is a [built in system prompt](#built-in-prompt) as well, so this is not the _entire_ prompt the agent will see.
124
+ **Subagent Spawning**
44
125
 
45
- ### `subagents` (Optional)
126
+ A built-in `task` tool enables agents to spawn specialized subagents for context isolation. This keeps the main agent's context clean while still going deep on specific subtasks.
46
127
 
47
- This can be used to specify any custom subagents this deep agent will have access to.
48
- You can read more about why you would want to use subagents [here](#sub-agents)
49
- `subagents` should be a list of dictionaries, where each dictionary follow this schema:
128
+ **Long-term Memory**
50
129
 
51
- ```js
52
- export interface SubAgent {
53
- name: string;
54
- description: string;
55
- prompt: string;
56
- tools?: string[];
57
- }
130
+ Extend agents with persistent memory across threads using LangGraph's Store. Agents can save and retrieve information from previous conversations.
131
+
132
+ ## Customizing Deep Agents
133
+
134
+ There are several parameters you can pass to `createDeepAgent` to create your own custom deep agent.
135
+
136
+ ### `model`
137
+
138
+ By default, `deepagents` uses `"claude-sonnet-4-5-20250929"`. You can customize this by passing any [LangChain model object](https://js.langchain.com/docs/integrations/chat/).
139
+
140
+ ```typescript
141
+ import { ChatAnthropic } from "@langchain/anthropic";
142
+ import { ChatOpenAI } from "@langchain/openai";
143
+ import { createDeepAgent } from "deepagents";
144
+
145
+ // Using Anthropic
146
+ const agent = createDeepAgent({
147
+ model: new ChatAnthropic({
148
+ model: "claude-sonnet-4-20250514",
149
+ temperature: 0,
150
+ }),
151
+ });
152
+
153
+ // Using OpenAI
154
+ const agent2 = createDeepAgent({
155
+ model: new ChatOpenAI({
156
+ model: "gpt-5",
157
+ temperature: 0,
158
+ }),
159
+ });
58
160
  ```
59
161
 
60
- - **name**: This is the name of the subagent, and how the main agent will call the subagent
61
- - **description**: This is the description of the subagent that is shown to the main agent
62
- - **prompt**: This is the prompt used for the subagent
63
- - **tools**: This is the list of tools that the subagent has access to. By default will have access to all tools passed in, as well as all built-in tools.
162
+ ### `systemPrompt`
64
163
 
65
- To use it looks like:
164
+ Deep Agents come with a built-in system prompt. This is relatively detailed prompt that is heavily based on and inspired by [attempts](https://github.com/kn1026/cc/blob/main/claudecode.md) to [replicate](https://github.com/asgeirtj/system_prompts_leaks/blob/main/Anthropic/claude-code.md)
165
+ Claude Code's system prompt. It was made more general purpose than Claude Code's system prompt. The default prompt contains detailed instructions for how to use the built-in planning tool, file system tools, and sub agents.
66
166
 
67
- ```js
68
- import { createDeepAgent, SubAgent } from "deepagents";
167
+ Each deep agent tailored to a use case should include a custom system prompt specific to that use case as well. The importance of prompting for creating a successful deep agent cannot be overstated.
168
+
169
+ ```typescript
170
+ import { createDeepAgent } from "deepagents";
171
+
172
+ const researchInstructions = `You are an expert researcher. Your job is to conduct thorough research, and then write a polished report.`;
173
+
174
+ const agent = createDeepAgent({
175
+ systemPrompt: researchInstructions,
176
+ });
177
+ ```
178
+
179
+ ### `tools`
180
+
181
+ Just like with tool-calling agents, you can provide a deep agent with a set of tools that it has access to.
182
+
183
+ ```typescript
184
+ import { tool } from "langchain";
185
+ import { TavilySearch } from "@langchain/tavily";
186
+ import { createDeepAgent } from "deepagents";
187
+ import { z } from "zod";
188
+
189
+ const internetSearch = tool(
190
+ async ({
191
+ query,
192
+ maxResults = 5,
193
+ topic = "general",
194
+ includeRawContent = false,
195
+ }: {
196
+ query: string;
197
+ maxResults?: number;
198
+ topic?: "general" | "news" | "finance";
199
+ includeRawContent?: boolean;
200
+ }) => {
201
+ const tavilySearch = new TavilySearch({
202
+ maxResults,
203
+ tavilyApiKey: process.env.TAVILY_API_KEY,
204
+ includeRawContent,
205
+ topic,
206
+ });
207
+ return await tavilySearch._call({ query });
208
+ },
209
+ {
210
+ name: "internet_search",
211
+ description: "Run a web search",
212
+ schema: z.object({
213
+ query: z.string().describe("The search query"),
214
+ maxResults: z.number().optional().default(5),
215
+ topic: z
216
+ .enum(["general", "news", "finance"])
217
+ .optional()
218
+ .default("general"),
219
+ includeRawContent: z.boolean().optional().default(false),
220
+ }),
221
+ },
222
+ );
69
223
 
70
- const researchSubAgent: SubAgent = {
71
- name: "research-agent",
72
- description: "Used to research more in depth questions...",
73
- prompt: subResearchPrompt,
74
- tools: ["internet_search"],
75
- };
76
224
  const agent = createDeepAgent({
77
- instructions: researchInstructions,
78
- subagents: [researchSubAgent],
79
- tools: [internetSearch],
225
+ tools: [internetSearch],
80
226
  });
81
227
  ```
82
228
 
83
- ### `model` (Optional)
229
+ ### `middleware`
84
230
 
85
- By default, `deepagents` will use local LM Studio. If you want to use a different model,
86
- you can pass a [LangChain model object](https://js.langchain.com/docs/integrations/chat/).
231
+ `createDeepAgent` is implemented with middleware that can be customized. You can provide additional middleware to extend functionality, add tools, or implement custom hooks.
87
232
 
88
- ## Deep Agent Details
233
+ ```typescript
234
+ import { tool } from "langchain";
235
+ import { createDeepAgent } from "deepagents";
236
+ import type { AgentMiddleware } from "langchain";
237
+ import { z } from "zod";
238
+
239
+ const getWeather = tool(
240
+ async ({ city }: { city: string }) => {
241
+ return `The weather in ${city} is sunny.`;
242
+ },
243
+ {
244
+ name: "get_weather",
245
+ description: "Get the weather in a city.",
246
+ schema: z.object({
247
+ city: z.string().describe("The city to get weather for"),
248
+ }),
249
+ },
250
+ );
251
+
252
+ const getTemperature = tool(
253
+ async ({ city }: { city: string }) => {
254
+ return `The temperature in ${city} is 70 degrees Fahrenheit.`;
255
+ },
256
+ {
257
+ name: "get_temperature",
258
+ description: "Get the temperature in a city.",
259
+ schema: z.object({
260
+ city: z.string().describe("The city to get temperature for"),
261
+ }),
262
+ },
263
+ );
264
+
265
+ class WeatherMiddleware implements AgentMiddleware {
266
+ tools = [getWeather, getTemperature];
267
+ }
89
268
 
90
- The below components are built into `deepagents` and helps make it work for deep tasks off-the-shelf.
269
+ const agent = createDeepAgent({
270
+ model: "claude-sonnet-4-20250514",
271
+ middleware: [new WeatherMiddleware()],
272
+ });
273
+ ```
91
274
 
92
- ### System Prompt
275
+ ### `subagents`
93
276
 
94
- `deepagents` comes with a [built-in system prompt](src/prompts.ts). This is relatively detailed prompt that is heavily based on and inspired by [attempts](https://github.com/kn1026/cc/blob/main/claudecode.md) to [replicate](https://github.com/asgeirtj/system_prompts_leaks/blob/main/Anthropic/claude-code.md)
95
- Claude Code's system prompt. It was made more general purpose than Claude Code's system prompt.
96
- This contains detailed instructions for how to use the built-in planning tool, file system tools, and sub agents.
97
- Note that part of this system prompt [can be customized](#promptprefix--required-)
277
+ A main feature of Deep Agents is their ability to spawn subagents. You can specify custom subagents that your agent can hand off work to in the subagents parameter. Sub agents are useful for context quarantine (to help not pollute the overall context of the main agent) as well as custom instructions.
98
278
 
99
- Without this default system prompt - the agent would not be nearly as successful at going as it is.
100
- The importance of prompting for creating a "deep" agent cannot be understated.
279
+ `subagents` should be a list of objects that follow the `SubAgent` interface:
101
280
 
102
- ### Planing Tool
281
+ ```typescript
282
+ interface SubAgent {
283
+ name: string;
284
+ description: string;
285
+ systemPrompt: string;
286
+ tools?: StructuredTool[];
287
+ model?: LanguageModelLike | string;
288
+ middleware?: AgentMiddleware[];
289
+ interruptOn?: Record<string, boolean | InterruptOnConfig>;
290
+ }
291
+ ```
103
292
 
104
- `deepagents` comes with a built-in planning tool. This planning tool is very simple and is based on ClaudeCode's TodoWrite tool.
105
- This tool doesn't actually do anything - it is just a way for the agent to come up with a plan, and then have that in the context to help keep it on track.
293
+ **SubAgent fields:**
294
+
295
+ - **name**: This is the name of the subagent, and how the main agent will call the subagent
296
+ - **description**: This is the description of the subagent that is shown to the main agent
297
+ - **systemPrompt**: This is the prompt used for the subagent
298
+ - **tools**: This is the list of tools that the subagent has access to.
299
+ - **model**: Optional model name or model instance.
300
+ - **middleware**: Additional middleware to attach to the subagent. See [here](https://docs.langchain.com/oss/typescript/langchain/middleware) for an introduction into middleware and how it works with createAgent.
301
+ - **interruptOn**: A custom interrupt config that specifies human-in-the-loop interactions for your tools.
302
+
303
+ #### Using SubAgent
304
+
305
+ ```typescript
306
+ import { tool } from "langchain";
307
+ import { TavilySearch } from "@langchain/tavily";
308
+ import { createDeepAgent, type SubAgent } from "deepagents";
309
+ import { z } from "zod";
310
+
311
+ const internetSearch = tool(
312
+ async ({
313
+ query,
314
+ maxResults = 5,
315
+ topic = "general",
316
+ includeRawContent = false,
317
+ }: {
318
+ query: string;
319
+ maxResults?: number;
320
+ topic?: "general" | "news" | "finance";
321
+ includeRawContent?: boolean;
322
+ }) => {
323
+ const tavilySearch = new TavilySearch({
324
+ maxResults,
325
+ tavilyApiKey: process.env.TAVILY_API_KEY,
326
+ includeRawContent,
327
+ topic,
328
+ });
329
+ return await tavilySearch._call({ query });
330
+ },
331
+ {
332
+ name: "internet_search",
333
+ description: "Run a web search",
334
+ schema: z.object({
335
+ query: z.string(),
336
+ maxResults: z.number().optional().default(5),
337
+ topic: z
338
+ .enum(["general", "news", "finance"])
339
+ .optional()
340
+ .default("general"),
341
+ includeRawContent: z.boolean().optional().default(false),
342
+ }),
343
+ },
344
+ );
345
+
346
+ const researchSubagent: SubAgent = {
347
+ name: "research-agent",
348
+ description: "Used to research more in depth questions",
349
+ systemPrompt: "You are a great researcher",
350
+ tools: [internetSearch],
351
+ model: "gpt-4o", // Optional override, defaults to main agent model
352
+ };
106
353
 
107
- ### File System Tools
354
+ const subagents = [researchSubagent];
108
355
 
109
- `deepagents` comes with four built-in file system tools: `ls`, `edit_file`, `read_file`, `write_file`.
110
- These do not actually use a file system - rather, they mock out a file system using LangGraph's State object.
111
- This means you can easily run many of these agents on the same machine without worrying that they will edit the same underlying files.
356
+ const agent = createDeepAgent({
357
+ model: "claude-sonnet-4-20250514",
358
+ subagents: subagents,
359
+ });
360
+ ```
361
+
362
+ ### `interruptOn`
112
363
 
113
- Right now the "file system" will only be one level deep (no sub directories).
364
+ A common reality for agents is that some tool operations may be sensitive and require human approval before execution. Deep Agents supports human-in-the-loop workflows through LangGraph's interrupt capabilities. You can configure which tools require approval using a checkpointer.
114
365
 
115
- These files can be passed in (and also retrieved) by using the `files` key in the LangGraph State object.
366
+ These tool configs are passed to our prebuilt [HITL middleware](https://docs.langchain.com/oss/typescript/langchain/middleware#human-in-the-loop) so that the agent pauses execution and waits for feedback from the user before executing configured tools.
116
367
 
117
- ```js
368
+ ```typescript
369
+ import { tool } from "langchain";
118
370
  import { createDeepAgent } from "deepagents";
371
+ import { z } from "zod";
372
+
373
+ const getWeather = tool(
374
+ async ({ city }: { city: string }) => {
375
+ return `The weather in ${city} is sunny.`;
376
+ },
377
+ {
378
+ name: "get_weather",
379
+ description: "Get the weather in a city.",
380
+ schema: z.object({
381
+ city: z.string(),
382
+ }),
383
+ },
384
+ );
119
385
 
120
386
  const agent = createDeepAgent({
121
- instructions: researchInstructions,
122
- subagents: [critiqueSubAgent, researchSubAgent],
123
- tools: [internetSearch],
387
+ model: "claude-sonnet-4-20250514",
388
+ tools: [getWeather],
389
+ interruptOn: {
390
+ get_weather: {
391
+ allowedDecisions: ["approve", "edit", "reject"],
392
+ },
393
+ },
124
394
  });
395
+ ```
125
396
 
126
- const result = await agent.invoke({
127
- messages: [new HumanMessage("what is langchain?")],
128
- files: [{"README.md": "# Deep Agents\n\nThis is a README file for the deep agents example."}],
397
+ ### `backend`
398
+
399
+ Deep Agents use backends to manage file system operations and memory storage. You can configure different backends depending on your needs:
400
+
401
+ ```typescript
402
+ import {
403
+ createDeepAgent,
404
+ StateBackend,
405
+ StoreBackend,
406
+ FilesystemBackend,
407
+ CompositeBackend,
408
+ } from "deepagents";
409
+ import { MemorySaver } from "@langchain/langgraph";
410
+ import { InMemoryStore } from "@langchain/langgraph-checkpoint";
411
+
412
+ // Default: StateBackend (in-memory, ephemeral)
413
+ const agent1 = createDeepAgent({
414
+ // No backend specified - uses StateBackend by default
415
+ });
416
+
417
+ // StoreBackend: Persistent storage using LangGraph Store
418
+ const agent2 = createDeepAgent({
419
+ backend: (config) => new StoreBackend(config),
420
+ store: new InMemoryStore(), // Provide a store
421
+ checkpointer: new MemorySaver(), // Optional: for conversation persistence
129
422
  });
130
423
 
131
- # Access any files afterwards like this
132
- result.files
424
+ // FilesystemBackend: Store files on actual filesystem
425
+ const agent3 = createDeepAgent({
426
+ backend: (config) => new FilesystemBackend({ rootDir: "./agent-workspace" }),
427
+ });
428
+
429
+ // CompositeBackend: Combine multiple backends
430
+ const agent4 = createDeepAgent({
431
+ backend: (config) =>
432
+ new CompositeBackend({
433
+ state: new StateBackend(config),
434
+ store: config.store ? new StoreBackend(config) : undefined,
435
+ }),
436
+ store: new InMemoryStore(),
437
+ checkpointer: new MemorySaver(),
438
+ });
133
439
  ```
134
440
 
135
- ### Sub Agents
441
+ See [examples/backends/](examples/backends/) for detailed examples of each backend type.
442
+
443
+ ## Deep Agents Middleware
444
+
445
+ Deep Agents are built with a modular middleware architecture. As a reminder, Deep Agents have access to:
136
446
 
137
- `deepagents` comes with the built-in ability to call sub agents (based on Claude Code).
138
- It has access to a `general-purpose` subagent at all times - this is a subagent with the same instructions as the main agent and all the tools that is has access to.
139
- You can also specify [custom sub agents](#subagents--optional-) with their own instructions and tools.
447
+ - A planning tool
448
+ - A filesystem for storing context and long-term memories
449
+ - The ability to spawn subagents
140
450
 
141
- Sub agents are useful for ["context quarantine"](https://www.dbreunig.com/2025/06/26/how-to-fix-your-context.html#context-quarantine) (to help not pollute the overall context of the main agent)
142
- as well as custom instructions.
451
+ Each of these features is implemented as separate middleware. When you create a deep agent with `createDeepAgent`, we automatically attach **todoListMiddleware**, **FilesystemMiddleware** and **SubAgentMiddleware** to your agent.
143
452
 
144
- ## Roadmap
453
+ Middleware is a composable concept, and you can choose to add as many or as few middleware to an agent depending on your use case. That means that you can also use any of the aforementioned middleware independently!
145
454
 
146
- - [ ] Allow users to customize full system prompt
147
- - [ ] Code cleanliness (type hinting, docstrings, formating)
148
- - [ ] Allow for more of a robust virtual filesystem
149
- - [ ] Create an example of a deep coding agent built on top of this
150
- - [ ] Benchmark the example of [deep research agent](src/examples/research/agent.ts)
151
- - [ ] Add human-in-the-loop support for tools
455
+ ### TodoListMiddleware
456
+
457
+ Planning is integral to solving complex problems. If you've used claude code recently, you'll notice how it writes out a To-Do list before tackling complex, multi-part tasks. You'll also notice how it can adapt and update this To-Do list on the fly as more information comes in.
458
+
459
+ **todoListMiddleware** provides your agent with a tool specifically for updating this To-Do list. Before, and while it executes a multi-part task, the agent is prompted to use the write_todos tool to keep track of what its doing, and what still needs to be done.
460
+
461
+ ```typescript
462
+ import { createAgent, todoListMiddleware } from "langchain";
463
+
464
+ // todoListMiddleware is included by default in createDeepAgent
465
+ // You can customize it if building a custom agent
466
+ const agent = createAgent({
467
+ model: "claude-sonnet-4-20250514",
468
+ middleware: [
469
+ todoListMiddleware({
470
+ // Optional: Custom addition to the system prompt
471
+ systemPrompt: "Use the write_todos tool to...",
472
+ }),
473
+ ],
474
+ });
475
+ ```
476
+
477
+ ### FilesystemMiddleware
478
+
479
+ Context engineering is one of the main challenges in building effective agents. This can be particularly hard when using tools that can return variable length results (ex. web_search, rag), as long ToolResults can quickly fill up your context window.
480
+
481
+ **FilesystemMiddleware** provides tools to your agent to interact with both short-term and long-term memory:
482
+
483
+ - **ls**: List the files in your filesystem
484
+ - **read_file**: Read an entire file, or a certain number of lines from a file
485
+ - **write_file**: Write a new file to your filesystem
486
+ - **edit_file**: Edit an existing file in your filesystem
487
+ - **glob**: Find files matching a pattern
488
+ - **grep**: Search for text within files
489
+
490
+ ```typescript
491
+ import { createAgent } from "langchain";
492
+ import { createFilesystemMiddleware } from "deepagents";
493
+
494
+ // FilesystemMiddleware is included by default in createDeepAgent
495
+ // You can customize it if building a custom agent
496
+ const agent = createAgent({
497
+ model: "claude-sonnet-4-20250514",
498
+ middleware: [
499
+ createFilesystemMiddleware({
500
+ backend: ..., // Optional: customize storage backend
501
+ systemPrompt: "Write to the filesystem when...", // Optional custom system prompt override
502
+ customToolDescriptions: {
503
+ ls: "Use the ls tool when...",
504
+ read_file: "Use the read_file tool to...",
505
+ }, // Optional: Custom descriptions for filesystem tools
506
+ }),
507
+ ],
508
+ });
509
+ ```
510
+
511
+ ### SubAgentMiddleware
512
+
513
+ Handing off tasks to subagents is a great way to isolate context, keeping the context window of the main (supervisor) agent clean while still going deep on a task. The subagents middleware allows you supply subagents through a task tool.
514
+
515
+ A subagent is defined with a name, description, system prompt, and tools. You can also provide a subagent with a custom model, or with additional middleware. This can be particularly useful when you want to give the subagent an additional state key to share with the main agent.
516
+
517
+ ```typescript
518
+ import { tool } from "langchain";
519
+ import { createAgent } from "langchain";
520
+ import { createSubAgentMiddleware, type SubAgent } from "deepagents";
521
+ import { z } from "zod";
522
+
523
+ const getWeather = tool(
524
+ async ({ city }: { city: string }) => {
525
+ return `The weather in ${city} is sunny.`;
526
+ },
527
+ {
528
+ name: "get_weather",
529
+ description: "Get the weather in a city.",
530
+ schema: z.object({
531
+ city: z.string(),
532
+ }),
533
+ },
534
+ );
535
+
536
+ const weatherSubagent: SubAgent = {
537
+ name: "weather",
538
+ description: "This subagent can get weather in cities.",
539
+ systemPrompt: "Use the get_weather tool to get the weather in a city.",
540
+ tools: [getWeather],
541
+ model: "gpt-4o",
542
+ middleware: [],
543
+ };
544
+
545
+ const agent = createAgent({
546
+ model: "claude-sonnet-4-20250514",
547
+ middleware: [
548
+ createSubAgentMiddleware({
549
+ defaultModel: "claude-sonnet-4-20250514",
550
+ defaultTools: [],
551
+ subagents: [weatherSubagent],
552
+ }),
553
+ ],
554
+ });
555
+ ```