From 526a1a90230611dd81e99fd800278a8dbc6461c0 Mon Sep 17 00:00:00 2001 From: Nishitha M <32355027+imnishitha@users.noreply.github.com> Date: Thu, 2 Jul 2026 14:08:10 +0000 Subject: [PATCH 1/9] docs: document deepagents middleware override by name Co-authored-by: open-swe[bot] --- src/oss/deepagents/customization.mdx | 34 +++++++++++++++++++++++++--- src/oss/deepagents/subagents.mdx | 2 +- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/oss/deepagents/customization.mdx b/src/oss/deepagents/customization.mdx index f6ff435ce9..9f46568140 100644 --- a/src/oss/deepagents/customization.mdx +++ b/src/oss/deepagents/customization.mdx @@ -75,7 +75,7 @@ agent = create_deep_agent( | [`backend=`](#backends) | Filesystem backend (StateBackend by default) | | [`permissions=`](/oss/deepagents/permissions) | Path-level access control for the filesystem | | [`subagents=`](#subagents) | Custom subagents for delegated tasks | -| [`middleware=`](#middleware) | Extra middleware appended to the [default stack](#default-stack-main-agent) | +| [`middleware=`](#middleware) | Extra middleware merged into the [default stack](#default-stack-main-agent); an instance whose `.name` matches a default replaces it in place | | [`interrupt_on=`](#human-in-the-loop) | Pause before tool calls for human approval | | [`response_format=`](#structured-output) | Structured output schema | | [`state_schema=`](/oss/deepagents/context-engineering#custom-state-schema) | Custom graph state schema | @@ -350,7 +350,7 @@ agent = create_deep_agent( Deep Agents support any [middleware](/oss/langchain/middleware/overview), including the built-in middleware listed below, prebuilt middleware from LangChain, provider-specific middleware, and custom middleware you write yourself. :::python -Pass middleware to the `middleware` argument of `create_deep_agent`. Custom middleware is appended after @[`PatchToolCallsMiddleware`] in the [default stack](#default-stack-main-agent). +Pass middleware to the `middleware` argument of `create_deep_agent`. Each instance is merged into the [default stack](#default-stack-main-agent) by matching its `.name` against the defaults already in the stack: a match replaces the default instance in place, and anything that does not match is inserted after @[`PatchToolCallsMiddleware`]. See [Override a default middleware instance](#override-a-default-middleware-instance). ::: :::js @@ -372,7 +372,7 @@ From first to last: 5. @[`SummarizationMiddleware`]: Condenses message history to stay within context limits when conversations grow long (via @[create_summarization_middleware]). 6. @[`PatchToolCallsMiddleware`]: Repairs dangling tool calls in message history when a run resumes after an interruption or receives malformed tool-call arguments. Runs **before** Anthropic prompt caching and the tail stack below. 7. @[`AsyncSubAgentMiddleware`]: Only when you configure async subagents. -8. **Your middleware argument**: Optional middleware you pass as the `middleware` argument is appended here (after Patch, before the tail stack). +8. **Your middleware argument**: Optional middleware you pass as the `middleware` argument is merged here (after Patch, before the tail stack)—an instance whose `.name` matches one of the defaults above replaces that default in place instead of duplicating it; anything else lands here. See [Override a default middleware instance](#override-a-default-middleware-instance). 9. **Harness profile extras**: Provider-specific middleware from the resolved model profile, if any. 10. **Excluded-tool filtering**: When the harness profile lists excluded tools, middleware removes those tools from the agent. 11. **Prompt caching** (@[`AnthropicPromptCachingMiddleware`] and @[`BedrockPromptCachingMiddleware`]): Both are always registered and run **after** Patch and after your middleware so the cached prefix matches what is actually sent to the model. Each no-ops on models it does not support (`unsupported_model_behavior="ignore"`), so the Anthropic middleware applies on Anthropic models and the Bedrock middleware on AWS Bedrock models with cache support. @@ -503,6 +503,34 @@ Mutation in place, such as modifying `state.x` in `beforeAgent`, mutating a shar If you must use mutation in custom middleware, consider what happens when subagents, parallel tools, or concurrent agent invocations run at the same time. +### Override a default middleware instance + +:::python +Pass a middleware instance whose `.name` matches an entry in the [default stack](#default-stack-main-agent), such as @[`SummarizationMiddleware`], to replace that default in place instead of appending a duplicate: + +```python +from deepagents import create_deep_agent +from deepagents.backends import StateBackend +from deepagents.middleware import SummarizationMiddleware + +backend = StateBackend() +model = "openai:gpt-5.5" + +custom_summarization = SummarizationMiddleware( + model=model, + backend=backend, + summary_prompt="Your custom summary prompt here.", +) + +agent = create_deep_agent( + model=model, + middleware=[custom_summarization], # replaces the default SummarizationMiddleware +) +``` + +The same rule applies to the `middleware` field on a [`SubAgent`](/oss/deepagents/subagents#subagent-dictionary-based) dictionary, matched against that subagent's own default stack. Declarative subagents build their stacks independently, so a main-agent override does not carry over to them—pass the override directly in the subagent's `middleware` field instead. The auto-added general-purpose subagent is an exception: it inherits a main-agent override when the override's `.name` matches one of the general-purpose subagent's own default middleware slots. +::: + ### Interpreters Use [interpreters](/oss/deepagents/interpreters) to add an `eval` tool that runs JavaScript in a scoped QuickJS runtime. Interpreters are useful when the agent needs to compose tools programmatically, batch work, handle errors in code, or transform structured data without a full shell environment. diff --git a/src/oss/deepagents/subagents.mdx b/src/oss/deepagents/subagents.mdx index 2d78cc7339..edfd869918 100644 --- a/src/oss/deepagents/subagents.mdx +++ b/src/oss/deepagents/subagents.mdx @@ -90,7 +90,7 @@ Define subagents as dictionaries matching the @[`SubAgent`] spec with the follow | `system_prompt` | `str` | Required. Instructions for the subagent. Custom subagents must define their own. Include tool usage guidance and output format requirements.

Does not inherit from main agent. | | `tools` | `list[Callable]` | Optional. Tools the subagent can use. Keep this minimal and include only what's needed.

Inherits from main agent by default. When specified, overrides the inherited tools entirely. | | `model` | `str` \| `BaseChatModel` | Optional. Overrides the main agent's model. Omit to use the main agent's model.

Inherits from main agent by default. You can pass either a model identifier string like `'openai:gpt-5.5'` (using the `'provider:model'` format) or a LangChain chat model object (`init_chat_model("gpt-5.5")` or `ChatOpenAI(model="gpt-5.5")`). | -| `middleware` | `list[Middleware]` | Optional. Additional middleware for custom behavior, logging, or rate limiting.

Does not inherit from the main agent. Appended to the [default subagent stack](/oss/deepagents/customization#default-stack-synchronous-subagents). | +| `middleware` | `list[Middleware]` | Optional. Additional middleware for custom behavior, logging, or rate limiting.

Does not inherit from the main agent. Merged into the [default subagent stack](/oss/deepagents/customization#default-stack-synchronous-subagents): an instance whose `.name` matches a default replaces it in place, see [Override a default middleware instance](/oss/deepagents/customization#override-a-default-middleware-instance). | | `interrupt_on` | `dict[str, bool \| InterruptOnConfig]` | Optional. Configure [human-in-the-loop](/oss/deepagents/human-in-the-loop) for specific tools. Options:`True`, `False`, or an `InterruptOnConfig` with `allowed_decisions`. Requires checkpointer.

Inherits from main agent by default. Subagent value overrides the default. | | `skills` | `list[str]` | Optional. [Skills](/oss/deepagents/skills) source paths. When specified, the subagent will load skills from these directories (e.g., `["/skills/research/", "/skills/web-search/"]`). This allows subagents to have different skill sets than the main agent.

Does not inherit from main agent. Only the general-purpose subagent inherits the main agent's skills. When a subagent has skills, it runs its own independent @[`SkillsMiddleware`] instance. Skill state is fully isolated—a subagent's loaded skills are not visible to the parent, and vice versa. | | `response_format` | `ResponseFormat` | Optional. [Structured output](/oss/langchain/structured-output) schema for the subagent. When set, the parent receives the subagent's result as JSON instead of free-form text. Accepts Pydantic models, `ToolStrategy(...)`, `ProviderStrategy(...)`, or a raw schema type. See [Structured output](#structured-output). | From ab743cd31a6ca011d952ca14b819a61ba0cdf3ce Mon Sep 17 00:00:00 2001 From: Nishitha M <32355027+imnishitha@users.noreply.github.com> Date: Thu, 2 Jul 2026 15:00:41 +0000 Subject: [PATCH 2/9] docs: clarify middleware override inheritance for filesystem mw and subagents Co-authored-by: open-swe[bot] --- src/oss/deepagents/customization.mdx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/oss/deepagents/customization.mdx b/src/oss/deepagents/customization.mdx index 9f46568140..081abd2c64 100644 --- a/src/oss/deepagents/customization.mdx +++ b/src/oss/deepagents/customization.mdx @@ -528,7 +528,13 @@ agent = create_deep_agent( ) ``` -The same rule applies to the `middleware` field on a [`SubAgent`](/oss/deepagents/subagents#subagent-dictionary-based) dictionary, matched against that subagent's own default stack. Declarative subagents build their stacks independently, so a main-agent override does not carry over to them—pass the override directly in the subagent's `middleware` field instead. The auto-added general-purpose subagent is an exception: it inherits a main-agent override when the override's `.name` matches one of the general-purpose subagent's own default middleware slots. + +An override replaces the default instance entirely rather than merging with it, so you must construct the replacement with every parameter it needs. This matters most for @[`FilesystemMiddleware`]: overriding it means passing `backend` (and `permissions`, if you rely on them) directly to your custom instance, since it no longer inherits the values you passed to `create_deep_agent`'s own `backend=` and `permissions=` arguments. + + +The general-purpose subagent, which Deep Agents adds automatically, inherits the same middleware customization you pass to the main agent. + +Declarative subagents defined via `subagents=` do not inherit the main agent's middleware customization. Pass the override directly in that subagent's own [`middleware`](/oss/deepagents/subagents#subagent-dictionary-based) field to apply it there; that field is matched against the subagent's own default stack, the same way `middleware=` is matched against the main agent's. ::: ### Interpreters From ed9f70095fda5ca51a1c172f696cb3784ad61701 Mon Sep 17 00:00:00 2001 From: Nishitha M <32355027+imnishitha@users.noreply.github.com> Date: Thu, 2 Jul 2026 15:54:39 +0000 Subject: [PATCH 3/9] docs: document FilesystemMiddleware tools allowlist Co-authored-by: open-swe[bot] --- src/oss/deepagents/customization.mdx | 2 +- src/oss/deepagents/overview.mdx | 23 +++++++++++++++++++++++ src/oss/deepagents/subagents.mdx | 2 +- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/oss/deepagents/customization.mdx b/src/oss/deepagents/customization.mdx index 081abd2c64..74bff59d26 100644 --- a/src/oss/deepagents/customization.mdx +++ b/src/oss/deepagents/customization.mdx @@ -529,7 +529,7 @@ agent = create_deep_agent( ``` -An override replaces the default instance entirely rather than merging with it, so you must construct the replacement with every parameter it needs. This matters most for @[`FilesystemMiddleware`]: overriding it means passing `backend` (and `permissions`, if you rely on them) directly to your custom instance, since it no longer inherits the values you passed to `create_deep_agent`'s own `backend=` and `permissions=` arguments. +An override replaces the default instance entirely rather than merging with it, so you must construct the replacement with every parameter it needs. This matters most for @[`FilesystemMiddleware`]: overriding it means passing `backend` (and `permissions`, if you rely on them) directly to your custom instance, since it no longer inherits the values you passed to `create_deep_agent`'s own `backend=` and `permissions=` arguments. This is also how you restrict the tool surface itself—pass a `tools` allowlist to your custom @[`FilesystemMiddleware`] instance; see [Virtual filesystem access](/oss/deepagents/overview#virtual-filesystem-access) for the "Restricting filesystem tools" example. The general-purpose subagent, which Deep Agents adds automatically, inherits the same middleware customization you pass to the main agent. diff --git a/src/oss/deepagents/overview.mdx b/src/oss/deepagents/overview.mdx index 8a0a4f8832..2f4ff6ea02 100644 --- a/src/oss/deepagents/overview.mdx +++ b/src/oss/deepagents/overview.mdx @@ -326,6 +326,29 @@ The backends support the following file system operations: Removing @[`FilesystemMiddleware`] itself via `excluded_middleware` is intentionally rejected—it is required scaffolding in the [default middleware stack](/oss/deepagents/customization#default-stack-main-agent). Use `excluded_tools` to hide only the model-visible tool surface and leave the middleware in place. To remove the `task` tool, see [Running without subagents](/oss/deepagents/subagents#running-without-subagents). + + :::python + To expose only a subset of the filesystem tools listed above, instead of hiding them all, pass a `tools` allowlist to @[`FilesystemMiddleware`] and provide the instance through `middleware=`. Any built-in filesystem tool left out of the list is removed from both the model's tool list and the middleware's dynamic system prompt section. + + ```python + from deepagents import create_deep_agent + from deepagents.middleware import FilesystemMiddleware + + # Read-only agent: write_file, edit_file, delete, and execute are never shown + agent = create_deep_agent( + model="claude-sonnet-4-6", + middleware=[ + FilesystemMiddleware(backend=backend, tools=["read_file", "ls", "glob", "grep"]), + ], + ) + ``` + + `read_file` must always be included in the list—omitting it raises `ValueError` when the agent is created. The `execute` and `delete` tools are also dropped from the tool surface whenever the configured backend doesn't support them, whether or not you include them in `tools`. Custom tools you add through `create_deep_agent`'s own `tools=` argument are never affected by this allowlist. + + Passing your own @[`FilesystemMiddleware`] instance this way replaces the default one for the main agent (see [Override a default middleware instance](/oss/deepagents/customization#override-a-default-middleware-instance)), and the general-purpose subagent inherits the same restriction. Declarative subagents don't inherit it: include a `FilesystemMiddleware(tools=...)` instance in that subagent's own `middleware` field to restrict it independently. + ::: + + The virtual filesystem is used by several other harness capabilities such as skills, memory, code execution, and context management. You can also use the file system when building custom tools and middleware for Deep Agents. diff --git a/src/oss/deepagents/subagents.mdx b/src/oss/deepagents/subagents.mdx index edfd869918..dfebd6de09 100644 --- a/src/oss/deepagents/subagents.mdx +++ b/src/oss/deepagents/subagents.mdx @@ -90,7 +90,7 @@ Define subagents as dictionaries matching the @[`SubAgent`] spec with the follow | `system_prompt` | `str` | Required. Instructions for the subagent. Custom subagents must define their own. Include tool usage guidance and output format requirements.

Does not inherit from main agent. | | `tools` | `list[Callable]` | Optional. Tools the subagent can use. Keep this minimal and include only what's needed.

Inherits from main agent by default. When specified, overrides the inherited tools entirely. | | `model` | `str` \| `BaseChatModel` | Optional. Overrides the main agent's model. Omit to use the main agent's model.

Inherits from main agent by default. You can pass either a model identifier string like `'openai:gpt-5.5'` (using the `'provider:model'` format) or a LangChain chat model object (`init_chat_model("gpt-5.5")` or `ChatOpenAI(model="gpt-5.5")`). | -| `middleware` | `list[Middleware]` | Optional. Additional middleware for custom behavior, logging, or rate limiting.

Does not inherit from the main agent. Merged into the [default subagent stack](/oss/deepagents/customization#default-stack-synchronous-subagents): an instance whose `.name` matches a default replaces it in place, see [Override a default middleware instance](/oss/deepagents/customization#override-a-default-middleware-instance). | +| `middleware` | `list[Middleware]` | Optional. Additional middleware for custom behavior, logging, or rate limiting.

Does not inherit from the main agent. Merged into the [default subagent stack](/oss/deepagents/customization#default-stack-synchronous-subagents): an instance whose `.name` matches a default replaces it in place, see [Override a default middleware instance](/oss/deepagents/customization#override-a-default-middleware-instance). For example, include a @[`FilesystemMiddleware`] instance with a `tools` allowlist here to restrict the subagent's filesystem tools independently of the main agent—see the "Restricting filesystem tools" section under [Virtual filesystem access](/oss/deepagents/overview#virtual-filesystem-access). | | `interrupt_on` | `dict[str, bool \| InterruptOnConfig]` | Optional. Configure [human-in-the-loop](/oss/deepagents/human-in-the-loop) for specific tools. Options:`True`, `False`, or an `InterruptOnConfig` with `allowed_decisions`. Requires checkpointer.

Inherits from main agent by default. Subagent value overrides the default. | | `skills` | `list[str]` | Optional. [Skills](/oss/deepagents/skills) source paths. When specified, the subagent will load skills from these directories (e.g., `["/skills/research/", "/skills/web-search/"]`). This allows subagents to have different skill sets than the main agent.

Does not inherit from main agent. Only the general-purpose subagent inherits the main agent's skills. When a subagent has skills, it runs its own independent @[`SkillsMiddleware`] instance. Skill state is fully isolated—a subagent's loaded skills are not visible to the parent, and vice versa. | | `response_format` | `ResponseFormat` | Optional. [Structured output](/oss/langchain/structured-output) schema for the subagent. When set, the parent receives the subagent's result as JSON instead of free-form text. Accepts Pydantic models, `ToolStrategy(...)`, `ProviderStrategy(...)`, or a raw schema type. See [Structured output](#structured-output). | From c0a2914896e50edf524296775b16f4abe8f23915 Mon Sep 17 00:00:00 2001 From: Nishitha Madhu Date: Thu, 2 Jul 2026 13:56:30 -0400 Subject: [PATCH 4/9] rephrasing --- src/oss/deepagents/customization.mdx | 3 ++- src/oss/langchain/middleware/built-in.mdx | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/oss/deepagents/customization.mdx b/src/oss/deepagents/customization.mdx index 74bff59d26..27e4187754 100644 --- a/src/oss/deepagents/customization.mdx +++ b/src/oss/deepagents/customization.mdx @@ -529,7 +529,8 @@ agent = create_deep_agent( ``` -An override replaces the default instance entirely rather than merging with it, so you must construct the replacement with every parameter it needs. This matters most for @[`FilesystemMiddleware`]: overriding it means passing `backend` (and `permissions`, if you rely on them) directly to your custom instance, since it no longer inherits the values you passed to `create_deep_agent`'s own `backend=` and `permissions=` arguments. This is also how you restrict the tool surface itself—pass a `tools` allowlist to your custom @[`FilesystemMiddleware`] instance; see [Virtual filesystem access](/oss/deepagents/overview#virtual-filesystem-access) for the "Restricting filesystem tools" example. +An override **replaces** the default middleware instance, it is not merged with it. That means your replacement must be fully configured with any settings it needs. This is especially important for `FilesystemMiddleware`: if you override it, you must pass the `backend` (and `permissions`, if applicable) directly to your custom instance, since it won't inherit the `backend=` and `permissions=` passed to `create_deep_agent()`. To restrict the available filesystem tools, pass a `tools` allowlist to your custom @[`FilesystemMiddleware`] instance; see [Virtual filesystem access](/oss/deepagents/overview#virtual-filesystem-access) for the "Restricting filesystem tools" example. + The general-purpose subagent, which Deep Agents adds automatically, inherits the same middleware customization you pass to the main agent. diff --git a/src/oss/langchain/middleware/built-in.mdx b/src/oss/langchain/middleware/built-in.mdx index 795050d683..94f600548f 100644 --- a/src/oss/langchain/middleware/built-in.mdx +++ b/src/oss/langchain/middleware/built-in.mdx @@ -2597,7 +2597,8 @@ agent = create_agent( custom_tool_descriptions={ "ls": "Use the ls tool when...", "read_file": "Use the read_file tool to..." - } # Optional: Custom descriptions for filesystem tools + }, # Optional: Custom descriptions for filesystem tools + tools=["read_file", "ls", "glob", "grep"], # Optional: Allowlist restricting which filesystem tools are exposed ), ], ) From 842837850fb22548300a39edd69969b263454cca Mon Sep 17 00:00:00 2001 From: Nishitha Madhu Date: Thu, 2 Jul 2026 14:03:45 -0400 Subject: [PATCH 5/9] detail about the custom mw --- src/oss/deepagents/customization.mdx | 4 ++-- src/oss/deepagents/subagents.mdx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/oss/deepagents/customization.mdx b/src/oss/deepagents/customization.mdx index 27e4187754..e18f040768 100644 --- a/src/oss/deepagents/customization.mdx +++ b/src/oss/deepagents/customization.mdx @@ -75,7 +75,7 @@ agent = create_deep_agent( | [`backend=`](#backends) | Filesystem backend (StateBackend by default) | | [`permissions=`](/oss/deepagents/permissions) | Path-level access control for the filesystem | | [`subagents=`](#subagents) | Custom subagents for delegated tasks | -| [`middleware=`](#middleware) | Extra middleware merged into the [default stack](#default-stack-main-agent); an instance whose `.name` matches a default replaces it in place | +| [`middleware=`](#middleware) | Extra middleware merged into the [default stack](#default-stack-main-agent); an instance whose `.name` matches a default replaces it in place, anything else lands after the last core middleware entry and before the profile/prompt-caching/memory tail | | [`interrupt_on=`](#human-in-the-loop) | Pause before tool calls for human approval | | [`response_format=`](#structured-output) | Structured output schema | | [`state_schema=`](/oss/deepagents/context-engineering#custom-state-schema) | Custom graph state schema | @@ -506,7 +506,7 @@ If you must use mutation in custom middleware, consider what happens when subage ### Override a default middleware instance :::python -Pass a middleware instance whose `.name` matches an entry in the [default stack](#default-stack-main-agent), such as @[`SummarizationMiddleware`], to replace that default in place instead of appending a duplicate: +Pass a middleware instance whose `.name` matches an entry in the [default stack](#default-stack-main-agent), such as @[`SummarizationMiddleware`], to replace that default in place instead of appending a duplicate. Any middleware you pass whose `.name` does **not** match a default is not replaced, it lands after the last core middleware entry and before the profile/prompt-caching/memory tail. See [Default stack (main agent)](#default-stack-main-agent) for the full ordering. ```python from deepagents import create_deep_agent diff --git a/src/oss/deepagents/subagents.mdx b/src/oss/deepagents/subagents.mdx index dfebd6de09..88f2795f25 100644 --- a/src/oss/deepagents/subagents.mdx +++ b/src/oss/deepagents/subagents.mdx @@ -90,7 +90,7 @@ Define subagents as dictionaries matching the @[`SubAgent`] spec with the follow | `system_prompt` | `str` | Required. Instructions for the subagent. Custom subagents must define their own. Include tool usage guidance and output format requirements.

Does not inherit from main agent. | | `tools` | `list[Callable]` | Optional. Tools the subagent can use. Keep this minimal and include only what's needed.

Inherits from main agent by default. When specified, overrides the inherited tools entirely. | | `model` | `str` \| `BaseChatModel` | Optional. Overrides the main agent's model. Omit to use the main agent's model.

Inherits from main agent by default. You can pass either a model identifier string like `'openai:gpt-5.5'` (using the `'provider:model'` format) or a LangChain chat model object (`init_chat_model("gpt-5.5")` or `ChatOpenAI(model="gpt-5.5")`). | -| `middleware` | `list[Middleware]` | Optional. Additional middleware for custom behavior, logging, or rate limiting.

Does not inherit from the main agent. Merged into the [default subagent stack](/oss/deepagents/customization#default-stack-synchronous-subagents): an instance whose `.name` matches a default replaces it in place, see [Override a default middleware instance](/oss/deepagents/customization#override-a-default-middleware-instance). For example, include a @[`FilesystemMiddleware`] instance with a `tools` allowlist here to restrict the subagent's filesystem tools independently of the main agent—see the "Restricting filesystem tools" section under [Virtual filesystem access](/oss/deepagents/overview#virtual-filesystem-access). | +| `middleware` | `list[Middleware]` | Optional. Additional middleware for custom behavior, logging, or rate limiting.

Does not inherit from the main agent. Merged into the [default subagent stack](/oss/deepagents/customization#default-stack-synchronous-subagents): an instance whose `.name` matches a default replaces it in place, anything else lands after the last core middleware entry and before the profile/prompt-caching/memory tail, see [Override a default middleware instance](/oss/deepagents/customization#override-a-default-middleware-instance). For example, include a @[`FilesystemMiddleware`] instance with a `tools` allowlist here to restrict the subagent's filesystem tools independently of the main agent—see the "Restricting filesystem tools" section under [Virtual filesystem access](/oss/deepagents/overview#virtual-filesystem-access). | | `interrupt_on` | `dict[str, bool \| InterruptOnConfig]` | Optional. Configure [human-in-the-loop](/oss/deepagents/human-in-the-loop) for specific tools. Options:`True`, `False`, or an `InterruptOnConfig` with `allowed_decisions`. Requires checkpointer.

Inherits from main agent by default. Subagent value overrides the default. | | `skills` | `list[str]` | Optional. [Skills](/oss/deepagents/skills) source paths. When specified, the subagent will load skills from these directories (e.g., `["/skills/research/", "/skills/web-search/"]`). This allows subagents to have different skill sets than the main agent.

Does not inherit from main agent. Only the general-purpose subagent inherits the main agent's skills. When a subagent has skills, it runs its own independent @[`SkillsMiddleware`] instance. Skill state is fully isolated—a subagent's loaded skills are not visible to the parent, and vice versa. | | `response_format` | `ResponseFormat` | Optional. [Structured output](/oss/langchain/structured-output) schema for the subagent. When set, the parent receives the subagent's result as JSON instead of free-form text. Accepts Pydantic models, `ToolStrategy(...)`, `ProviderStrategy(...)`, or a raw schema type. See [Structured output](#structured-output). | From 17be7e04642dd4423ffc8014ff82d52c232b06aa Mon Sep 17 00:00:00 2001 From: Nishitha Madhu Date: Thu, 2 Jul 2026 15:44:36 -0400 Subject: [PATCH 6/9] call out --- src/oss/deepagents/customization.mdx | 4 ++++ src/oss/deepagents/overview.mdx | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/oss/deepagents/customization.mdx b/src/oss/deepagents/customization.mdx index e18f040768..d2921feebf 100644 --- a/src/oss/deepagents/customization.mdx +++ b/src/oss/deepagents/customization.mdx @@ -506,6 +506,10 @@ If you must use mutation in custom middleware, consider what happens when subage ### Override a default middleware instance :::python + +Overriding a default middleware by matching `.name` requires `deepagents` 0.7 alpha versions. + + Pass a middleware instance whose `.name` matches an entry in the [default stack](#default-stack-main-agent), such as @[`SummarizationMiddleware`], to replace that default in place instead of appending a duplicate. Any middleware you pass whose `.name` does **not** match a default is not replaced, it lands after the last core middleware entry and before the profile/prompt-caching/memory tail. See [Default stack (main agent)](#default-stack-main-agent) for the full ordering. ```python diff --git a/src/oss/deepagents/overview.mdx b/src/oss/deepagents/overview.mdx index 2f4ff6ea02..0537f0aaaf 100644 --- a/src/oss/deepagents/overview.mdx +++ b/src/oss/deepagents/overview.mdx @@ -328,6 +328,10 @@ The backends support the following file system operations: :::python + + The `tools` allowlist on `FilesystemMiddleware` require `deepagents` 0.7 alpha versions. + + To expose only a subset of the filesystem tools listed above, instead of hiding them all, pass a `tools` allowlist to @[`FilesystemMiddleware`] and provide the instance through `middleware=`. Any built-in filesystem tool left out of the list is removed from both the model's tool list and the middleware's dynamic system prompt section. ```python From 07933bc1661bd98ecfe30705c4ef90ad76163c3e Mon Sep 17 00:00:00 2001 From: Nishitha Madhu Date: Mon, 6 Jul 2026 11:48:11 -0400 Subject: [PATCH 7/9] address comments --- src/oss/deepagents/customization.mdx | 8 ++++---- src/oss/deepagents/overview.mdx | 8 ++++---- src/oss/deepagents/subagents.mdx | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/oss/deepagents/customization.mdx b/src/oss/deepagents/customization.mdx index d2921feebf..852c873f30 100644 --- a/src/oss/deepagents/customization.mdx +++ b/src/oss/deepagents/customization.mdx @@ -75,7 +75,7 @@ agent = create_deep_agent( | [`backend=`](#backends) | Filesystem backend (StateBackend by default) | | [`permissions=`](/oss/deepagents/permissions) | Path-level access control for the filesystem | | [`subagents=`](#subagents) | Custom subagents for delegated tasks | -| [`middleware=`](#middleware) | Extra middleware merged into the [default stack](#default-stack-main-agent); an instance whose `.name` matches a default replaces it in place, anything else lands after the last core middleware entry and before the profile/prompt-caching/memory tail | +| [`middleware=`](#middleware) | Extra middleware merged into the [default stack](#default-stack-main-agent); an instance whose `.name` matches a default replaces it in place, anything else lands after the last core middleware entry and before the profile, prompt-caching, and memory | | [`interrupt_on=`](#human-in-the-loop) | Pause before tool calls for human approval | | [`response_format=`](#structured-output) | Structured output schema | | [`state_schema=`](/oss/deepagents/context-engineering#custom-state-schema) | Custom graph state schema | @@ -372,7 +372,7 @@ From first to last: 5. @[`SummarizationMiddleware`]: Condenses message history to stay within context limits when conversations grow long (via @[create_summarization_middleware]). 6. @[`PatchToolCallsMiddleware`]: Repairs dangling tool calls in message history when a run resumes after an interruption or receives malformed tool-call arguments. Runs **before** Anthropic prompt caching and the tail stack below. 7. @[`AsyncSubAgentMiddleware`]: Only when you configure async subagents. -8. **Your middleware argument**: Optional middleware you pass as the `middleware` argument is merged here (after Patch, before the tail stack)—an instance whose `.name` matches one of the defaults above replaces that default in place instead of duplicating it; anything else lands here. See [Override a default middleware instance](#override-a-default-middleware-instance). +8. **Your middleware argument**: Optional middleware you pass as the `middleware` argument is merged after Patch but before the rest of the stack. An instance whose `.name` matches one of the defaults above replaces that default in place instead of duplicating it; anything else lands here. See [Override a default middleware instance](#override-a-default-middleware-instance). 9. **Harness profile extras**: Provider-specific middleware from the resolved model profile, if any. 10. **Excluded-tool filtering**: When the harness profile lists excluded tools, middleware removes those tools from the agent. 11. **Prompt caching** (@[`AnthropicPromptCachingMiddleware`] and @[`BedrockPromptCachingMiddleware`]): Both are always registered and run **after** Patch and after your middleware so the cached prefix matches what is actually sent to the model. Each no-ops on models it does not support (`unsupported_model_behavior="ignore"`), so the Anthropic middleware applies on Anthropic models and the Bedrock middleware on AWS Bedrock models with cache support. @@ -507,10 +507,10 @@ If you must use mutation in custom middleware, consider what happens when subage :::python -Overriding a default middleware by matching `.name` requires `deepagents` 0.7 alpha versions. +Overriding a default middleware by matching `.name` requires `deepagents` 0.7a3 versions or newer. -Pass a middleware instance whose `.name` matches an entry in the [default stack](#default-stack-main-agent), such as @[`SummarizationMiddleware`], to replace that default in place instead of appending a duplicate. Any middleware you pass whose `.name` does **not** match a default is not replaced, it lands after the last core middleware entry and before the profile/prompt-caching/memory tail. See [Default stack (main agent)](#default-stack-main-agent) for the full ordering. +Pass a middleware instance whose `.name` matches an entry in the [default stack](#default-stack-main-agent), such as @[`SummarizationMiddleware`], to replace that default in place instead of appending a duplicate. Any middleware you pass whose `.name` does **not** match a default is not replaced, it lands after the last core middleware entry and before the profile, prompt-caching, and memory. See [Default stack (main agent)](#default-stack-main-agent) for the full ordering. ```python from deepagents import create_deep_agent diff --git a/src/oss/deepagents/overview.mdx b/src/oss/deepagents/overview.mdx index 0537f0aaaf..bd75490e49 100644 --- a/src/oss/deepagents/overview.mdx +++ b/src/oss/deepagents/overview.mdx @@ -326,10 +326,10 @@ The backends support the following file system operations: Removing @[`FilesystemMiddleware`] itself via `excluded_middleware` is intentionally rejected—it is required scaffolding in the [default middleware stack](/oss/deepagents/customization#default-stack-main-agent). Use `excluded_tools` to hide only the model-visible tool surface and leave the middleware in place. To remove the `task` tool, see [Running without subagents](/oss/deepagents/subagents#running-without-subagents). +:::python - :::python - The `tools` allowlist on `FilesystemMiddleware` require `deepagents` 0.7 alpha versions. + The `tools` allowlist on `FilesystemMiddleware` requires `deepagents` 0.7a4 versions or newer. To expose only a subset of the filesystem tools listed above, instead of hiding them all, pass a `tools` allowlist to @[`FilesystemMiddleware`] and provide the instance through `middleware=`. Any built-in filesystem tool left out of the list is removed from both the model's tool list and the middleware's dynamic system prompt section. @@ -349,9 +349,9 @@ The backends support the following file system operations: `read_file` must always be included in the list—omitting it raises `ValueError` when the agent is created. The `execute` and `delete` tools are also dropped from the tool surface whenever the configured backend doesn't support them, whether or not you include them in `tools`. Custom tools you add through `create_deep_agent`'s own `tools=` argument are never affected by this allowlist. - Passing your own @[`FilesystemMiddleware`] instance this way replaces the default one for the main agent (see [Override a default middleware instance](/oss/deepagents/customization#override-a-default-middleware-instance)), and the general-purpose subagent inherits the same restriction. Declarative subagents don't inherit it: include a `FilesystemMiddleware(tools=...)` instance in that subagent's own `middleware` field to restrict it independently. - ::: + Passing your own @[`FilesystemMiddleware`] instance this way replaces the default one for the main agent and the general-purpose subagent inherits the same restriction. See [Override a default middleware instance](/oss/deepagents/customization#override-a-default-middleware-instance) for more information. Declarative subagents don't inherit it: include a `FilesystemMiddleware(tools=...)` instance in that subagent's own `middleware` field to restrict it independently. +::: The virtual filesystem is used by several other harness capabilities such as skills, memory, code execution, and context management. You can also use the file system when building custom tools and middleware for Deep Agents. diff --git a/src/oss/deepagents/subagents.mdx b/src/oss/deepagents/subagents.mdx index 88f2795f25..f457567242 100644 --- a/src/oss/deepagents/subagents.mdx +++ b/src/oss/deepagents/subagents.mdx @@ -90,7 +90,7 @@ Define subagents as dictionaries matching the @[`SubAgent`] spec with the follow | `system_prompt` | `str` | Required. Instructions for the subagent. Custom subagents must define their own. Include tool usage guidance and output format requirements.

Does not inherit from main agent. | | `tools` | `list[Callable]` | Optional. Tools the subagent can use. Keep this minimal and include only what's needed.

Inherits from main agent by default. When specified, overrides the inherited tools entirely. | | `model` | `str` \| `BaseChatModel` | Optional. Overrides the main agent's model. Omit to use the main agent's model.

Inherits from main agent by default. You can pass either a model identifier string like `'openai:gpt-5.5'` (using the `'provider:model'` format) or a LangChain chat model object (`init_chat_model("gpt-5.5")` or `ChatOpenAI(model="gpt-5.5")`). | -| `middleware` | `list[Middleware]` | Optional. Additional middleware for custom behavior, logging, or rate limiting.

Does not inherit from the main agent. Merged into the [default subagent stack](/oss/deepagents/customization#default-stack-synchronous-subagents): an instance whose `.name` matches a default replaces it in place, anything else lands after the last core middleware entry and before the profile/prompt-caching/memory tail, see [Override a default middleware instance](/oss/deepagents/customization#override-a-default-middleware-instance). For example, include a @[`FilesystemMiddleware`] instance with a `tools` allowlist here to restrict the subagent's filesystem tools independently of the main agent—see the "Restricting filesystem tools" section under [Virtual filesystem access](/oss/deepagents/overview#virtual-filesystem-access). | +| `middleware` | `list[Middleware]` | Optional. Additional middleware for custom behavior, logging, or rate limiting.

Does not inherit from the main agent. Merged into the [default subagent stack](/oss/deepagents/customization#default-stack-synchronous-subagents): an instance whose `.name` matches a default replaces it in place, anything else lands after the last core middleware entry and before profile, prompt-caching, and memory. See [Override a default middleware instance](/oss/deepagents/customization#override-a-default-middleware-instance). For example, include a @[`FilesystemMiddleware`] instance with a `tools` allowlist here to restrict the subagent's filesystem tools independently of the main agent. For more information, see the "Restricting filesystem tools" section under [Virtual filesystem access](/oss/deepagents/overview#virtual-filesystem-access). | | `interrupt_on` | `dict[str, bool \| InterruptOnConfig]` | Optional. Configure [human-in-the-loop](/oss/deepagents/human-in-the-loop) for specific tools. Options:`True`, `False`, or an `InterruptOnConfig` with `allowed_decisions`. Requires checkpointer.

Inherits from main agent by default. Subagent value overrides the default. | | `skills` | `list[str]` | Optional. [Skills](/oss/deepagents/skills) source paths. When specified, the subagent will load skills from these directories (e.g., `["/skills/research/", "/skills/web-search/"]`). This allows subagents to have different skill sets than the main agent.

Does not inherit from main agent. Only the general-purpose subagent inherits the main agent's skills. When a subagent has skills, it runs its own independent @[`SkillsMiddleware`] instance. Skill state is fully isolated—a subagent's loaded skills are not visible to the parent, and vice versa. | | `response_format` | `ResponseFormat` | Optional. [Structured output](/oss/langchain/structured-output) schema for the subagent. When set, the parent receives the subagent's result as JSON instead of free-form text. Accepts Pydantic models, `ToolStrategy(...)`, `ProviderStrategy(...)`, or a raw schema type. See [Structured output](#structured-output). | From d659cba61158a62bd79ff5a6661ce984d34b9a47 Mon Sep 17 00:00:00 2001 From: Nishitha M <32355027+imnishitha@users.noreply.github.com> Date: Tue, 7 Jul 2026 14:13:31 -0400 Subject: [PATCH 8/9] Apply suggestions from code review Co-authored-by: ccurme --- src/oss/deepagents/customization.mdx | 2 +- src/oss/deepagents/overview.mdx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/oss/deepagents/customization.mdx b/src/oss/deepagents/customization.mdx index ed18970e04..f2b59a3650 100644 --- a/src/oss/deepagents/customization.mdx +++ b/src/oss/deepagents/customization.mdx @@ -423,7 +423,7 @@ If you must use mutation in custom middleware, consider what happens when subage :::python -Overriding a default middleware by matching `.name` requires `deepagents` 0.7a3 versions or newer. +Overriding a default middleware by matching `.name` requires `deepagents>=0.7.0a3`. Pass a middleware instance whose `.name` matches an entry in the [default stack](#default-stack-main-agent), such as @[`SummarizationMiddleware`], to replace that default in place instead of appending a duplicate. Any middleware you pass whose `.name` does **not** match a default is not replaced, it lands after the last core middleware entry and before the profile, prompt-caching, and memory. See [Default stack (main agent)](#default-stack-main-agent) for the full ordering. diff --git a/src/oss/deepagents/overview.mdx b/src/oss/deepagents/overview.mdx index bd75490e49..13808bb769 100644 --- a/src/oss/deepagents/overview.mdx +++ b/src/oss/deepagents/overview.mdx @@ -329,7 +329,7 @@ The backends support the following file system operations: :::python - The `tools` allowlist on `FilesystemMiddleware` requires `deepagents` 0.7a4 versions or newer. + The `tools` allowlist on `FilesystemMiddleware` requires `deepagents>=0.7.0a4`. To expose only a subset of the filesystem tools listed above, instead of hiding them all, pass a `tools` allowlist to @[`FilesystemMiddleware`] and provide the instance through `middleware=`. Any built-in filesystem tool left out of the list is removed from both the model's tool list and the middleware's dynamic system prompt section. From d19bc8fbdd066499aca0fd2908e1f087a67d1cbd Mon Sep 17 00:00:00 2001 From: Nishitha Madhu Date: Tue, 7 Jul 2026 16:02:38 -0400 Subject: [PATCH 9/9] more examples --- src/oss/deepagents/customization.mdx | 103 +++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/src/oss/deepagents/customization.mdx b/src/oss/deepagents/customization.mdx index f2b59a3650..6dc8cbd814 100644 --- a/src/oss/deepagents/customization.mdx +++ b/src/oss/deepagents/customization.mdx @@ -456,6 +456,109 @@ An override **replaces** the default middleware instance, it is not merged with The general-purpose subagent, which Deep Agents adds automatically, inherits the same middleware customization you pass to the main agent. Declarative subagents defined via `subagents=` do not inherit the main agent's middleware customization. Pass the override directly in that subagent's own [`middleware`](/oss/deepagents/subagents#subagent-dictionary-based) field to apply it there; that field is matched against the subagent's own default stack, the same way `middleware=` is matched against the main agent's. + +#### Examples + + + + Override @[`SummarizationMiddleware`] with custom `trigger` and `keep` thresholds to compact conversation history earlier or later than the default, and control how many recent messages survive each compaction. + + ```python + from deepagents import create_deep_agent + from deepagents.backends import StateBackend + from deepagents.middleware import SummarizationMiddleware + + backend = StateBackend() + model = "anthropic:claude-sonnet-4-6" + + agent = create_deep_agent( + model=model, + middleware=[ + SummarizationMiddleware( + model=model, + backend=backend, + trigger=("tokens", 100000), # summarize once the conversation exceeds 100k tokens + keep=("messages", 20), # keep the most recent 20 messages verbatim + ), + ], + ) + ``` + + `trigger` also accepts `("fraction", ...)` for a percentage of the model's context window, and a list of thresholds combines them with OR semantics. See the @[`SummarizationMiddleware`] reference for the full set of options. + + + Override @[`AnthropicPromptCachingMiddleware`] to extend the cache lifetime beyond the default `5m` TTL, useful for agents with long gaps between turns. See [Prompt caching](/oss/deepagents/overview#prompt-caching) for how caching is applied by default. + + ```python + from deepagents import create_deep_agent + from langchain_anthropic.middleware import AnthropicPromptCachingMiddleware + + agent = create_deep_agent( + model="anthropic:claude-sonnet-4-6", + middleware=[ + AnthropicPromptCachingMiddleware(ttl="1h"), # replaces the default 5m TTL + ], + ) + ``` + + + Override @[`FilesystemMiddleware`] with a `system_prompt` to replace the filesystem-specific instructions it appends to the system prompt in place of the dynamically generated default. + + ```python + from deepagents import create_deep_agent + from deepagents.backends import StateBackend + from deepagents.middleware import FilesystemMiddleware + + backend = StateBackend() + + agent = create_deep_agent( + model="anthropic:claude-sonnet-4-6", + backend=backend, + middleware=[ + FilesystemMiddleware( + backend=backend, + system_prompt="Use the virtual filesystem to track long-running work. Write intermediate results to files instead of repeating them in messages.", + ), + ], + ) + ``` + + As with any @[`FilesystemMiddleware`] override, pass the same `backend` (and `permissions`, if applicable) used elsewhere, since the override is not merged with the default. + + + Override @[`SubAgentMiddleware`] with a `task_description` to replace the `task` tool's description, for example to steer the model on when to delegate. The override replaces the default stack outright, so redeclare the same `backend` and `subagents` passed to `create_deep_agent`. The auto-added [general-purpose subagent](/oss/deepagents/subagents#the-general-purpose-subagent) is not included unless you add an equivalent entry yourself. + + ```python + from deepagents import create_deep_agent + from deepagents.backends import StateBackend + from deepagents.middleware import SubAgentMiddleware + + backend = StateBackend() + model = "anthropic:claude-sonnet-4-6" + researcher = { + "name": "researcher", + "description": "Researches a topic and returns findings.", + "system_prompt": "You are a researcher.", + "model": model, + "tools": [search], + } + + agent = create_deep_agent( + model=model, + subagents=[researcher], + middleware=[ + SubAgentMiddleware( + backend=backend, + subagents=[researcher], + task_description="Delegate research to the `researcher` subagent only for multi-step lookups.", + ), + ], + ) + ``` + + `task_description` also supports an `available_agents` template placeholder that is filled in with the subagent name and description list; see the @[`SubAgentMiddleware`] reference for details. For a narrower change that only rewords the `task` tool description without replacing the subagent stack, use a [harness profile](/oss/deepagents/profiles#harness-profiles)'s `tool_description_overrides` instead; see [Profiles](#profiles). + + ::: ### Interpreters