Skip to content

fix(sdk): unify grep include-glob semantics across backends - #3936

Merged
Mason Daugherty (mdrxy) merged 5 commits into
mainfrom
mdrxy/sdk/grep-include-glob-semantics
Jul 2, 2026
Merged

fix(sdk): unify grep include-glob semantics across backends#3936
Mason Daugherty (mdrxy) merged 5 commits into
mainfrom
mdrxy/sdk/grep-include-glob-semantics

Conversation

@mdrxy

Copy link
Copy Markdown
Member

Closes #3922


The same grep(..., glob=...) call returned different files depending on the backend and whether ripgrep was installed. The in-memory StateBackend/StoreBackend path applied the glob to only the basename, so directory patterns like src/**/*.py and even **/*.py silently matched nothing. The FilesystemBackend Python fallback applied the glob to the root-relative path, so slashless patterns like *.py missed nested files. Since StateBackend is the default in create_deep_agent and the tool docs advertise recursive globs, agents could wrongly conclude files do not exist.

This introduces a single shared matcher, compile_grep_include_glob, that mirrors ripgrep's include-glob behavior: a pattern without a / matches the basename at any depth (*.py matches /src/app/main.py), while a pattern containing a / matches the path relative to the grep search root with ** support (src/**/*.py matches /src/app/main.py). It is used by both grep_matches_from_files and the FilesystemBackend Python fallback; the ripgrep code path is unchanged. No public APIs, result schemas, or literal-search semantics change.

Regression tests cover the src/**/*.py, **/*.py, *.py, and *.md matrix plus search-root relativity for both the in-memory and (ripgrep-stubbed) Python fallback paths.

Made by Open SWE

The same `grep(..., glob=...)` call previously matched different files
depending on the backend and whether ripgrep was installed. The in-memory
path matched the glob against only the basename (so `src/**/*.py` and
`**/*.py` matched nothing), while the FilesystemBackend Python fallback
matched against the root-relative path (so slashless `*.py` missed nested
files).

Add `compile_grep_include_glob` implementing one ripgrep-like contract:
slashless patterns match the basename at any depth, while path-containing
patterns match relative to the search root with `**` support. Use it in
`grep_matches_from_files` and the FilesystemBackend Python fallback.

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
@github-actions github-actions Bot added deepagents Related to the `deepagents` SDK / agent harness fix A bug fix (PATCH) internal User is a member of the `langchain-ai` GitHub organization size: S 50-199 LOC labels Jun 12, 2026
@mdrxy
Mason Daugherty (mdrxy) marked this pull request as ready for review June 13, 2026 00:39

@open-swe open-swe Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Open SWE Review found 1 potential issue.

Open in WebView Open SWE trace

Comment thread libs/deepagents/deepagents/backends/utils.py
Mason Daugherty (mdrxy) and others added 4 commits June 24, 2026 17:46
Resolve import conflict in backends/utils.py and update
test_file_format.py to use the renamed compile_grep_include_glob.

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
@mdrxy
Mason Daugherty (mdrxy) merged commit feab6e0 into main Jul 2, 2026
61 checks passed
@mdrxy
Mason Daugherty (mdrxy) deleted the mdrxy/sdk/grep-include-glob-semantics branch July 2, 2026 06:59
@codspeed-hq

codspeed-hq Bot commented Jul 2, 2026

Copy link
Copy Markdown

Merging this PR will improve performance by 26.33%

⚡ 1 improved benchmark
✅ 20 untouched benchmarks
⏩ 79 skipped benchmarks1

Performance Changes

Mode Benchmark BASE HEAD Efficiency
WallTime test_filesystem_init 490.4 µs 388.2 µs +26.33%

Tip

Curious why this is faster? Comment @codspeedbot explain why this is faster on this PR, or directly use the CodSpeed MCP with your agent.


Comparing mdrxy/sdk/grep-include-glob-semantics (7dfb10c) with main (b0d92c0)

Open in CodSpeed

Footnotes

  1. 79 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

Mason Daugherty (mdrxy) added a commit that referenced this pull request Jul 29, 2026
> [!CAUTION]
> Merging this PR will automatically publish to **PyPI** and create a
**GitHub release**.

For the full release process, see
[`.github/RELEASING.md`](https://github.com/langchain-ai/deepagents/blob/main/.github/RELEASING.md).

---

_Release notes preview: keep this section in sync with the package
`CHANGELOG.md`. Publish reads the merged CHANGELOG via `release.yml`,
not this PR description — keep them aligned anyway so the PR stays an
accurate historical record for reviewers and anyone returning later._

---


##
[0.7.0](deepagents==0.6.12...deepagents==0.7.0)
(2026-07-29)

See [the
docs](https://docs.langchain.com/oss/python/releases/changelog#deepagents-v0-7-0)
for curated release notes.

### ⚠ BREAKING CHANGES

* `create_deep_agent` no longer includes `TodoListMiddleware` by
default, the `write_todos` tool, `todos` state channel, and
todo-planning prompt are now absent. Pass
`middleware=[TodoListMiddleware()]` to restore them on the main agent;
add it to each `SubAgent`'s middleware to restore them there.
([#4929](#4929))
([9340518](9340518))
* Default agent prompts are now lean: the authored base prompt is empty,
and tool-usage prose that duplicates tool schemas is trimmed.
`BASE_AGENT_PROMPT` is deprecated (removal in `deepagents==0.9.0`) but
remains importable and still returns the previous authored prompt
verbatim; pass it as
`create_deep_agent(system_prompt=BASE_AGENT_PROMPT)` to restore the old
behavior.
([#4859](#4859))
([#4979](#4979))
([a8d1b32](a8d1b32))
([d9f54fc](d9f54fc))
* The built-in tool-usage prompt constants `TASK_SYSTEM_PROMPT`,
`ASYNC_TASK_SYSTEM_PROMPT`, `SUMMARIZATION_SYSTEM_PROMPT`,
`FILESYSTEM_SYSTEM_PROMPT`, and `EXECUTION_SYSTEM_PROMPT` are removed,
and the `system_prompt` default on `SubAgentMiddleware`,
`AsyncSubAgentMiddleware`, `SummarizationToolMiddleware`, and
`create_summarization_tool_middleware` is now `None`, which injects no
prose. Pass your own string to restore prompt text.
([#4859](#4859))
([a8d1b32](a8d1b32))
* `FilesystemBackend` and `LocalShellBackend` now default to
`virtual_mode=True`. Filesystem paths are anchored under `root_dir`,
`..` traversal is rejected, and paths resolving outside `root_dir` raise
`ValueError`. Previously an unspecified `virtual_mode` emitted a
deprecation warning and fell back to `False`, where absolute host paths
were used as-is and `..` could escape `root_dir`. Pass
`virtual_mode=False` explicitly to restore the old filesystem behavior.
([#4541](#4541))
([540a0fa](540a0fa))
* Agents now see a destructive, recursive `delete` filesystem tool
whenever the backend supports it, and filesystem permissions classify
`delete` as a write operation — so an existing rule allowing writes to a
path also authorizes recursively deleting that subtree unless a narrower
deny or interrupt rule covers the target. Because recursive deletes
affect descendants, deny and interrupt checks use bulk path overlap
instead of exact-path matching. To keep the previous behavior, add a
deny or interrupt rule, or omit `delete` from
`FilesystemMiddleware(tools=...)`. Missing paths return a not-found
error, `CompositeBackend` reports an unsupported-operation error when a
routed sub-backend cannot delete, and the tool is hidden from the model
entirely when the backend itself does not implement it.
([#3659](#3659))
([#3691](#3691))
([#3765](#3765))
([#3851](#3851))
([f2a21ec](f2a21ec))
* `write_file` can now create a file if it is missing and replaces it
entirely if it already exists, instead of returning a file-exists error.
The `write_file` tool description no longer requires reading the file
first. There is no "create-only" compatibility mode. Workflows, prompts,
tests, or guardrails that relied on the file-exists error to force
`edit_file` usage or to protect existing content must omit `write_file`,
add explicit permission or interrupt rules, or use `edit_file` where
preserving existing content matters.
([#4109](#4109))
([2506fcc](2506fcc))
* Removed deprecated backend compatibility shims. Callers must pass
concrete `BackendProtocol` instances (not factories), configure
`StoreBackend` with an explicit `namespace`, and use the current `ls` /
`glob` / `grep` / `ReadResult` APIs.
([#4541](#4541))
([540a0fa](540a0fa))
* The deprecated `files_update` attribute and constructor keyword are
removed from `WriteResult` and `EditResult`. Custom backends must stop
passing `files_update=`, and callers must stop reading
`result.files_update`; state writes are emitted directly by
`StateBackend`.
([#4541](#4541))
([540a0fa](540a0fa))
* Removed the deprecated `BackendProtocol` methods `ls_info`,
`als_info`, `glob_info`, `aglob_info`, `grep_raw`, and `agrep_raw`. Use
`ls` / `glob` / `grep` and their async counterparts.
([#4541](#4541))
([540a0fa](540a0fa))
* `SummarizationMiddleware(history_path_prefix=...)` was removed and now
raises `TypeError`. Configure `CompositeBackend(artifacts_root=...)`
instead.
([#4541](#4541))
([540a0fa](540a0fa))
* Agent-facing `ls` and `glob` tool output now renders empty results as
`No files found` instead of `[]`; direct backend APIs continue to return
structured empty `LsResult` and `GlobResult` values. Callers that parse
tool output should update those checks.
([#3709](#3709))
([efafd1e](efafd1e))
* `read_file` no longer renders raw text with a fixed-width `cat
-n`-style line-number gutter and tab separator. Line and continuation
markers are dynamically aligned and separated from source content by two
spaces, and the `LINE_NUMBER_WIDTH` constant is removed from
`deepagents.backends.utils` and `deepagents.middleware.filesystem`.
Callers that parse raw tool output should update those parsers.
([#4561](#4561))
([cf057b4](cf057b4))

### Features

* Custom middleware passed to `create_deep_agent(..., middleware=[...])`
can replace a default middleware instance when `.name` matches, so
defaults such as `SummarizationMiddleware` can be overridden without
also excluding the built-in instance.
([#4251](#4251))
([90c8472](90c8472))
* `FilesystemMiddleware(tools=[...])` accepts a keyword-only allowlist
of built-in filesystem tools, typed by the newly exported `FsToolName`
literal (`"ls"`, `"read_file"`, `"write_file"`, `"edit_file"`,
`"delete"`, `"glob"`, `"grep"`, `"execute"`); pass `"all"` or omit the
argument to keep every tool. A list must include `"read_file"` or the
constructor raises `ValueError`. Omitted built-in tools are
non-executable, and custom user tools are unaffected.
([#4325](#4325))
([#4698](#4698))
([704a70d](704a70d))
([9709525](9709525))
* Shorten LLM-facing descriptions for the `task` tool and filesystem
tools (`read_file`, `grep`, `edit_file`, `glob`, `execute`).
([#5009](#5009))
([761f5f0](761f5f0))
* `GrepResult` and `GlobResult` now carry a `truncated` flag so
supporting backends can return valid partial results when a match cap or
backend deadline is reached; agent-facing tool output adds a note
telling the model to narrow the search. `FilesystemBackend` returns
partial `grep` and `glob` results on its backend timeout rather than
erroring, while other backend or middleware timeouts may still return
errors. Its `glob` also gains brace expansion such as `*.{py,md}`
(already supported by the state and store backends).
([#4063](#4063))
([ef591e7](ef591e7))
* The agent-facing `grep` match cap is configurable:
`FilesystemMiddleware(grep_max_count=...)` sets the default (`1000`;
`None` disables it) and the model can override it per call through the
tool's new `max_count` argument. `grep` / `agrep` on `BackendProtocol`
and all built-in backends accept a keyword-only `max_count`. Local
ripgrep output is streamed and terminated once the cap is reached.
Direct `FilesystemBackend.grep()` callers can request surrounding lines
with keyword-only `context_lines`.
([#4570](#4570))
([#4706](#4706))
([8e86f5e](8e86f5e))
([65230df](65230df))
* Paginated built-in `read_file` responses report the returned
source-line range and next `offset`; total and remaining line counts are
included when the backend knows the file length. Resume offsets remain
safe when sandbox or middleware limits shorten the visible page.
([#4540](#4540))
([8321194](8321194))
* Optional video frame extraction for `read_file`, enabled by the new
`deepagents[video]` extra. Video files are sampled into JPEG frames,
with `offset` and `limit` interpreted as seconds. Without the extra,
existing generic video/file content-block behavior remains.
([#4094](#4094))
([b927147](b927147))
* `FilesystemMiddleware` can capture oversized `execute` tool output
directly inside the sandbox artifact path on compatible, opted-in
`BaseSandbox` implementations to reduce round trips; `LangSmithSandbox`
opts in by default.
([#4230](#4230))
([02f5bd7](02f5bd7))
* Automatically enable Fireworks prompt-cache session affinity when a
compatible `langchain-fireworks` installation is available.
([#4598](#4598))
([5d878bf](5d878bf))
* Add a built-in NVIDIA Nemotron 3 Ultra harness profile and NVIDIA NIM
app-origin attribution.
([#4192](#4192))
([#4455](#4455))
([d5a60ec](d5a60ec))
([4cb4749](4cb4749))
* `RubricMiddleware` now accepts any positive `max_iterations` cap
instead of enforcing a hard upper bound.
([#4405](#4405))
([d6692a7](d6692a7))

### Bug Fixes

* Keep fields marked with `PrivateStateAttr`, including fields declared
through `create_deep_agent(state_schema=...)`, out of subagent inputs
and returned parent-state updates.
([#4587](#4587))
([a4662c0](a4662c0))
* Preserve `ContextT` through the `create_deep_agent(...,
middleware=[...])` type annotation so type checkers accept context-aware
middleware when a matching `context_schema` is passed.
([#4055](#4055))
([7be76c7](7be76c7))
* Accept YAML list values as well as comma-separated strings for skill
`allowed-tools` frontmatter, and make skill truncation warnings
actionable with field name, path, length, configured limit, and impact.
([#4140](#4140))
([#4141](#4141))
([d62534c](d62534c))
([2f5f5b8](2f5f5b8))
* Align filesystem instructions with the tools that remain after
allowlist and backend-capability filtering, so agents no longer
reference hidden `grep`/`glob` tools or prohibit equivalent shell search
when dedicated search tools are unavailable.
([#4920](#4920))
([#4921](#4921))
([d3650c7](d3650c7))
([b65cc00](b65cc00))
* Propagate default-backend failures from `CompositeBackend.ls("/")` and
`CompositeBackend.als("/")` instead of returning successful route-only
listings.
([#4925](#4925))
([4c3b166](4c3b166))
* Correct `CompositeBackend.glob` / `CompositeBackend.aglob` routing so
explicit default-backend paths such as `/tools` do not also return files
from routed backends such as `/memories`.
([#4531](#4531))
([cbdb0a7](cbdb0a7))
* Propagate default- and routed-backend failures from root
`CompositeBackend.glob(..., path=None)` / `aglob(..., path=None)` and
`path="/"` searches instead of returning incomplete successful results.
([#4063](#4063))
([ef591e7](ef591e7))
* Constrain sandbox `glob` and slash-pattern `grep` searches to their
declared search root by treating leading `/` as search-root-relative,
rejecting `..` traversal segments, and filtering symlink-resolved
matches outside the root.
([#4588](#4588))
([c6c7213](c6c7213))
* Unify `grep(..., glob=...)` include-glob semantics across filesystem
and in-memory backends: basename patterns like `*.py` match at any
depth, and slash-containing patterns like `src/**/*.py` match relative
paths consistently.
([#3936](#3936))
([feab6e0](feab6e0))
* Improve agent-facing `grep` descriptions and no-match hints to steer
regex-looking patterns toward literal searches, route slash-containing
sandbox include-globs correctly, and shorten default search timeouts so
bad patterns and huge trees return guidance faster.
([#4168](#4168))
([b1dbf5e](b1dbf5e))
* Align sandbox delete behavior with other backends by returning
not-found errors for missing paths, and avoid over-blocking unrelated
sibling deletes when deny rules use glob patterns.
([#4321](#4321))
([d77496b](d77496b))
* Improve rubric grader failure diagnostics with configured model,
structured-output strategy, and integer HTTP status when available.
([#4938](#4938))
([#4967](#4967))
([f51d3a0](f51d3a0))
([bca70aa](bca70aa))
* Emit `max_iterations_reached` as the terminal `RubricMiddleware`
status when the iteration cap is exhausted, instead of a final
`needs_revision` event that will not loop.
([#4406](#4406))
([a51c8d2](a51c8d2))
* Handle missing async subagent URLs consistently in `check_async_task`
and `cancel_async_task`.
([#3967](#3967))
([b0d92c0](b0d92c0))

### Performance Improvements

* Run LangSmith sandbox commands over the async client.
([#5061](#5061))
([0d08747](0d08747))

---

_End release notes preview._

---

> [!NOTE]
> A **New Contributors** section is appended to the GitHub release notes
automatically at publish time (see [Release
Pipeline](https://github.com/langchain-ai/deepagents/blob/main/.github/RELEASING.md#release-pipeline),
step 2).

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Mason Daugherty <github@mdrxy.com>
Marcelo5444 pushed a commit to Marcelo5444/deepagents that referenced this pull request Jul 30, 2026
…ain-ai#3936)

Closes langchain-ai#3922

---

The same `grep(..., glob=...)` call returned different files depending
on the backend and whether ripgrep was installed. The in-memory
`StateBackend`/`StoreBackend` path applied the glob to only the
basename, so directory patterns like `src/**/*.py` and even `**/*.py`
silently matched nothing. The `FilesystemBackend` Python fallback
applied the glob to the root-relative path, so slashless patterns like
`*.py` missed nested files. Since `StateBackend` is the default in
`create_deep_agent` and the tool docs advertise recursive globs, agents
could wrongly conclude files do not exist.

This introduces a single shared matcher, `compile_grep_include_glob`,
that mirrors ripgrep's include-glob behavior: a pattern without a `/`
matches the basename at any depth (`*.py` matches `/src/app/main.py`),
while a pattern containing a `/` matches the path relative to the grep
search root with `**` support (`src/**/*.py` matches
`/src/app/main.py`). It is used by both `grep_matches_from_files` and
the `FilesystemBackend` Python fallback; the ripgrep code path is
unchanged. No public APIs, result schemas, or literal-search semantics
change.

Regression tests cover the `src/**/*.py`, `**/*.py`, `*.py`, and `*.md`
matrix plus search-root relativity for both the in-memory and
(ripgrep-stubbed) Python fallback paths.

Made by [Open SWE](https://openswe.vercel.app)

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Marcelo5444 pushed a commit to Marcelo5444/deepagents that referenced this pull request Jul 30, 2026
> [!CAUTION]
> Merging this PR will automatically publish to **PyPI** and create a
**GitHub release**.

For the full release process, see
[`.github/RELEASING.md`](https://github.com/langchain-ai/deepagents/blob/main/.github/RELEASING.md).

---

_Release notes preview: keep this section in sync with the package
`CHANGELOG.md`. Publish reads the merged CHANGELOG via `release.yml`,
not this PR description — keep them aligned anyway so the PR stays an
accurate historical record for reviewers and anyone returning later._

---


##
[0.7.0](langchain-ai/deepagents@deepagents==0.6.12...deepagents==0.7.0)
(2026-07-29)

See [the
docs](https://docs.langchain.com/oss/python/releases/changelog#deepagents-v0-7-0)
for curated release notes.

### ⚠ BREAKING CHANGES

* `create_deep_agent` no longer includes `TodoListMiddleware` by
default, the `write_todos` tool, `todos` state channel, and
todo-planning prompt are now absent. Pass
`middleware=[TodoListMiddleware()]` to restore them on the main agent;
add it to each `SubAgent`'s middleware to restore them there.
([langchain-ai#4929](langchain-ai#4929))
([9340518](langchain-ai@9340518))
* Default agent prompts are now lean: the authored base prompt is empty,
and tool-usage prose that duplicates tool schemas is trimmed.
`BASE_AGENT_PROMPT` is deprecated (removal in `deepagents==0.9.0`) but
remains importable and still returns the previous authored prompt
verbatim; pass it as
`create_deep_agent(system_prompt=BASE_AGENT_PROMPT)` to restore the old
behavior.
([langchain-ai#4859](langchain-ai#4859))
([langchain-ai#4979](langchain-ai#4979))
([a8d1b32](langchain-ai@a8d1b32))
([d9f54fc](langchain-ai@d9f54fc))
* The built-in tool-usage prompt constants `TASK_SYSTEM_PROMPT`,
`ASYNC_TASK_SYSTEM_PROMPT`, `SUMMARIZATION_SYSTEM_PROMPT`,
`FILESYSTEM_SYSTEM_PROMPT`, and `EXECUTION_SYSTEM_PROMPT` are removed,
and the `system_prompt` default on `SubAgentMiddleware`,
`AsyncSubAgentMiddleware`, `SummarizationToolMiddleware`, and
`create_summarization_tool_middleware` is now `None`, which injects no
prose. Pass your own string to restore prompt text.
([langchain-ai#4859](langchain-ai#4859))
([a8d1b32](langchain-ai@a8d1b32))
* `FilesystemBackend` and `LocalShellBackend` now default to
`virtual_mode=True`. Filesystem paths are anchored under `root_dir`,
`..` traversal is rejected, and paths resolving outside `root_dir` raise
`ValueError`. Previously an unspecified `virtual_mode` emitted a
deprecation warning and fell back to `False`, where absolute host paths
were used as-is and `..` could escape `root_dir`. Pass
`virtual_mode=False` explicitly to restore the old filesystem behavior.
([langchain-ai#4541](langchain-ai#4541))
([540a0fa](langchain-ai@540a0fa))
* Agents now see a destructive, recursive `delete` filesystem tool
whenever the backend supports it, and filesystem permissions classify
`delete` as a write operation — so an existing rule allowing writes to a
path also authorizes recursively deleting that subtree unless a narrower
deny or interrupt rule covers the target. Because recursive deletes
affect descendants, deny and interrupt checks use bulk path overlap
instead of exact-path matching. To keep the previous behavior, add a
deny or interrupt rule, or omit `delete` from
`FilesystemMiddleware(tools=...)`. Missing paths return a not-found
error, `CompositeBackend` reports an unsupported-operation error when a
routed sub-backend cannot delete, and the tool is hidden from the model
entirely when the backend itself does not implement it.
([langchain-ai#3659](langchain-ai#3659))
([langchain-ai#3691](langchain-ai#3691))
([langchain-ai#3765](langchain-ai#3765))
([langchain-ai#3851](langchain-ai#3851))
([f2a21ec](langchain-ai@f2a21ec))
* `write_file` can now create a file if it is missing and replaces it
entirely if it already exists, instead of returning a file-exists error.
The `write_file` tool description no longer requires reading the file
first. There is no "create-only" compatibility mode. Workflows, prompts,
tests, or guardrails that relied on the file-exists error to force
`edit_file` usage or to protect existing content must omit `write_file`,
add explicit permission or interrupt rules, or use `edit_file` where
preserving existing content matters.
([langchain-ai#4109](langchain-ai#4109))
([2506fcc](langchain-ai@2506fcc))
* Removed deprecated backend compatibility shims. Callers must pass
concrete `BackendProtocol` instances (not factories), configure
`StoreBackend` with an explicit `namespace`, and use the current `ls` /
`glob` / `grep` / `ReadResult` APIs.
([langchain-ai#4541](langchain-ai#4541))
([540a0fa](langchain-ai@540a0fa))
* The deprecated `files_update` attribute and constructor keyword are
removed from `WriteResult` and `EditResult`. Custom backends must stop
passing `files_update=`, and callers must stop reading
`result.files_update`; state writes are emitted directly by
`StateBackend`.
([langchain-ai#4541](langchain-ai#4541))
([540a0fa](langchain-ai@540a0fa))
* Removed the deprecated `BackendProtocol` methods `ls_info`,
`als_info`, `glob_info`, `aglob_info`, `grep_raw`, and `agrep_raw`. Use
`ls` / `glob` / `grep` and their async counterparts.
([langchain-ai#4541](langchain-ai#4541))
([540a0fa](langchain-ai@540a0fa))
* `SummarizationMiddleware(history_path_prefix=...)` was removed and now
raises `TypeError`. Configure `CompositeBackend(artifacts_root=...)`
instead.
([langchain-ai#4541](langchain-ai#4541))
([540a0fa](langchain-ai@540a0fa))
* Agent-facing `ls` and `glob` tool output now renders empty results as
`No files found` instead of `[]`; direct backend APIs continue to return
structured empty `LsResult` and `GlobResult` values. Callers that parse
tool output should update those checks.
([langchain-ai#3709](langchain-ai#3709))
([efafd1e](langchain-ai@efafd1e))
* `read_file` no longer renders raw text with a fixed-width `cat
-n`-style line-number gutter and tab separator. Line and continuation
markers are dynamically aligned and separated from source content by two
spaces, and the `LINE_NUMBER_WIDTH` constant is removed from
`deepagents.backends.utils` and `deepagents.middleware.filesystem`.
Callers that parse raw tool output should update those parsers.
([langchain-ai#4561](langchain-ai#4561))
([cf057b4](langchain-ai@cf057b4))

### Features

* Custom middleware passed to `create_deep_agent(..., middleware=[...])`
can replace a default middleware instance when `.name` matches, so
defaults such as `SummarizationMiddleware` can be overridden without
also excluding the built-in instance.
([langchain-ai#4251](langchain-ai#4251))
([90c8472](langchain-ai@90c8472))
* `FilesystemMiddleware(tools=[...])` accepts a keyword-only allowlist
of built-in filesystem tools, typed by the newly exported `FsToolName`
literal (`"ls"`, `"read_file"`, `"write_file"`, `"edit_file"`,
`"delete"`, `"glob"`, `"grep"`, `"execute"`); pass `"all"` or omit the
argument to keep every tool. A list must include `"read_file"` or the
constructor raises `ValueError`. Omitted built-in tools are
non-executable, and custom user tools are unaffected.
([langchain-ai#4325](langchain-ai#4325))
([langchain-ai#4698](langchain-ai#4698))
([704a70d](langchain-ai@704a70d))
([9709525](langchain-ai@9709525))
* Shorten LLM-facing descriptions for the `task` tool and filesystem
tools (`read_file`, `grep`, `edit_file`, `glob`, `execute`).
([langchain-ai#5009](langchain-ai#5009))
([761f5f0](langchain-ai@761f5f0))
* `GrepResult` and `GlobResult` now carry a `truncated` flag so
supporting backends can return valid partial results when a match cap or
backend deadline is reached; agent-facing tool output adds a note
telling the model to narrow the search. `FilesystemBackend` returns
partial `grep` and `glob` results on its backend timeout rather than
erroring, while other backend or middleware timeouts may still return
errors. Its `glob` also gains brace expansion such as `*.{py,md}`
(already supported by the state and store backends).
([langchain-ai#4063](langchain-ai#4063))
([ef591e7](langchain-ai@ef591e7))
* The agent-facing `grep` match cap is configurable:
`FilesystemMiddleware(grep_max_count=...)` sets the default (`1000`;
`None` disables it) and the model can override it per call through the
tool's new `max_count` argument. `grep` / `agrep` on `BackendProtocol`
and all built-in backends accept a keyword-only `max_count`. Local
ripgrep output is streamed and terminated once the cap is reached.
Direct `FilesystemBackend.grep()` callers can request surrounding lines
with keyword-only `context_lines`.
([langchain-ai#4570](langchain-ai#4570))
([langchain-ai#4706](langchain-ai#4706))
([8e86f5e](langchain-ai@8e86f5e))
([65230df](langchain-ai@65230df))
* Paginated built-in `read_file` responses report the returned
source-line range and next `offset`; total and remaining line counts are
included when the backend knows the file length. Resume offsets remain
safe when sandbox or middleware limits shorten the visible page.
([langchain-ai#4540](langchain-ai#4540))
([8321194](langchain-ai@8321194))
* Optional video frame extraction for `read_file`, enabled by the new
`deepagents[video]` extra. Video files are sampled into JPEG frames,
with `offset` and `limit` interpreted as seconds. Without the extra,
existing generic video/file content-block behavior remains.
([langchain-ai#4094](langchain-ai#4094))
([b927147](langchain-ai@b927147))
* `FilesystemMiddleware` can capture oversized `execute` tool output
directly inside the sandbox artifact path on compatible, opted-in
`BaseSandbox` implementations to reduce round trips; `LangSmithSandbox`
opts in by default.
([langchain-ai#4230](langchain-ai#4230))
([02f5bd7](langchain-ai@02f5bd7))
* Automatically enable Fireworks prompt-cache session affinity when a
compatible `langchain-fireworks` installation is available.
([langchain-ai#4598](langchain-ai#4598))
([5d878bf](langchain-ai@5d878bf))
* Add a built-in NVIDIA Nemotron 3 Ultra harness profile and NVIDIA NIM
app-origin attribution.
([langchain-ai#4192](langchain-ai#4192))
([langchain-ai#4455](langchain-ai#4455))
([d5a60ec](langchain-ai@d5a60ec))
([4cb4749](langchain-ai@4cb4749))
* `RubricMiddleware` now accepts any positive `max_iterations` cap
instead of enforcing a hard upper bound.
([langchain-ai#4405](langchain-ai#4405))
([d6692a7](langchain-ai@d6692a7))

### Bug Fixes

* Keep fields marked with `PrivateStateAttr`, including fields declared
through `create_deep_agent(state_schema=...)`, out of subagent inputs
and returned parent-state updates.
([langchain-ai#4587](langchain-ai#4587))
([a4662c0](langchain-ai@a4662c0))
* Preserve `ContextT` through the `create_deep_agent(...,
middleware=[...])` type annotation so type checkers accept context-aware
middleware when a matching `context_schema` is passed.
([langchain-ai#4055](langchain-ai#4055))
([7be76c7](langchain-ai@7be76c7))
* Accept YAML list values as well as comma-separated strings for skill
`allowed-tools` frontmatter, and make skill truncation warnings
actionable with field name, path, length, configured limit, and impact.
([langchain-ai#4140](langchain-ai#4140))
([langchain-ai#4141](langchain-ai#4141))
([d62534c](langchain-ai@d62534c))
([2f5f5b8](langchain-ai@2f5f5b8))
* Align filesystem instructions with the tools that remain after
allowlist and backend-capability filtering, so agents no longer
reference hidden `grep`/`glob` tools or prohibit equivalent shell search
when dedicated search tools are unavailable.
([langchain-ai#4920](langchain-ai#4920))
([langchain-ai#4921](langchain-ai#4921))
([d3650c7](langchain-ai@d3650c7))
([b65cc00](langchain-ai@b65cc00))
* Propagate default-backend failures from `CompositeBackend.ls("/")` and
`CompositeBackend.als("/")` instead of returning successful route-only
listings.
([langchain-ai#4925](langchain-ai#4925))
([4c3b166](langchain-ai@4c3b166))
* Correct `CompositeBackend.glob` / `CompositeBackend.aglob` routing so
explicit default-backend paths such as `/tools` do not also return files
from routed backends such as `/memories`.
([langchain-ai#4531](langchain-ai#4531))
([cbdb0a7](langchain-ai@cbdb0a7))
* Propagate default- and routed-backend failures from root
`CompositeBackend.glob(..., path=None)` / `aglob(..., path=None)` and
`path="/"` searches instead of returning incomplete successful results.
([langchain-ai#4063](langchain-ai#4063))
([ef591e7](langchain-ai@ef591e7))
* Constrain sandbox `glob` and slash-pattern `grep` searches to their
declared search root by treating leading `/` as search-root-relative,
rejecting `..` traversal segments, and filtering symlink-resolved
matches outside the root.
([langchain-ai#4588](langchain-ai#4588))
([c6c7213](langchain-ai@c6c7213))
* Unify `grep(..., glob=...)` include-glob semantics across filesystem
and in-memory backends: basename patterns like `*.py` match at any
depth, and slash-containing patterns like `src/**/*.py` match relative
paths consistently.
([langchain-ai#3936](langchain-ai#3936))
([feab6e0](langchain-ai@feab6e0))
* Improve agent-facing `grep` descriptions and no-match hints to steer
regex-looking patterns toward literal searches, route slash-containing
sandbox include-globs correctly, and shorten default search timeouts so
bad patterns and huge trees return guidance faster.
([langchain-ai#4168](langchain-ai#4168))
([b1dbf5e](langchain-ai@b1dbf5e))
* Align sandbox delete behavior with other backends by returning
not-found errors for missing paths, and avoid over-blocking unrelated
sibling deletes when deny rules use glob patterns.
([langchain-ai#4321](langchain-ai#4321))
([d77496b](langchain-ai@d77496b))
* Improve rubric grader failure diagnostics with configured model,
structured-output strategy, and integer HTTP status when available.
([langchain-ai#4938](langchain-ai#4938))
([langchain-ai#4967](langchain-ai#4967))
([f51d3a0](langchain-ai@f51d3a0))
([bca70aa](langchain-ai@bca70aa))
* Emit `max_iterations_reached` as the terminal `RubricMiddleware`
status when the iteration cap is exhausted, instead of a final
`needs_revision` event that will not loop.
([langchain-ai#4406](langchain-ai#4406))
([a51c8d2](langchain-ai@a51c8d2))
* Handle missing async subagent URLs consistently in `check_async_task`
and `cancel_async_task`.
([langchain-ai#3967](langchain-ai#3967))
([b0d92c0](langchain-ai@b0d92c0))

### Performance Improvements

* Run LangSmith sandbox commands over the async client.
([langchain-ai#5061](langchain-ai#5061))
([0d08747](langchain-ai@0d08747))

---

_End release notes preview._

---

> [!NOTE]
> A **New Contributors** section is appended to the GitHub release notes
automatically at publish time (see [Release
Pipeline](https://github.com/langchain-ai/deepagents/blob/main/.github/RELEASING.md#release-pipeline),
step 2).

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Mason Daugherty <github@mdrxy.com>
Mason Daugherty (mdrxy) added a commit that referenced this pull request Jul 30, 2026
> [!CAUTION]
> Merging this PR will automatically publish to **PyPI** and create a
**GitHub release**.

For the full release process, see
[`.github/RELEASING.md`](https://github.com/langchain-ai/deepagents/blob/main/.github/RELEASING.md).

---

_Release notes preview: keep this section in sync with the package
`CHANGELOG.md`. Publish reads the merged CHANGELOG via `release.yml`,
not this PR description — keep them aligned anyway so the PR stays an
accurate historical record for reviewers and anyone returning later._

---


##
[0.7.0](deepagents==0.6.12...deepagents==0.7.0)
(2026-07-29)

See [the
docs](https://docs.langchain.com/oss/python/releases/changelog#deepagents-v0-7-0)
for curated release notes.

### ⚠ BREAKING CHANGES

* `create_deep_agent` no longer includes `TodoListMiddleware` by
default, the `write_todos` tool, `todos` state channel, and
todo-planning prompt are now absent. Pass
`middleware=[TodoListMiddleware()]` to restore them on the main agent;
add it to each `SubAgent`'s middleware to restore them there.
([#4929](#4929))
([9340518](9340518))
* Default agent prompts are now lean: the authored base prompt is empty,
and tool-usage prose that duplicates tool schemas is trimmed.
`BASE_AGENT_PROMPT` is deprecated (removal in `deepagents==0.9.0`) but
remains importable and still returns the previous authored prompt
verbatim; pass it as
`create_deep_agent(system_prompt=BASE_AGENT_PROMPT)` to restore the old
behavior.
([#4859](#4859))
([#4979](#4979))
([a8d1b32](a8d1b32))
([d9f54fc](d9f54fc))
* The built-in tool-usage prompt constants `TASK_SYSTEM_PROMPT`,
`ASYNC_TASK_SYSTEM_PROMPT`, `SUMMARIZATION_SYSTEM_PROMPT`,
`FILESYSTEM_SYSTEM_PROMPT`, and `EXECUTION_SYSTEM_PROMPT` are removed,
and the `system_prompt` default on `SubAgentMiddleware`,
`AsyncSubAgentMiddleware`, `SummarizationToolMiddleware`, and
`create_summarization_tool_middleware` is now `None`, which injects no
prose. Pass your own string to restore prompt text.
([#4859](#4859))
([a8d1b32](a8d1b32))
* `FilesystemBackend` and `LocalShellBackend` now default to
`virtual_mode=True`. Filesystem paths are anchored under `root_dir`,
`..` traversal is rejected, and paths resolving outside `root_dir` raise
`ValueError`. Previously an unspecified `virtual_mode` emitted a
deprecation warning and fell back to `False`, where absolute host paths
were used as-is and `..` could escape `root_dir`. Pass
`virtual_mode=False` explicitly to restore the old filesystem behavior.
([#4541](#4541))
([540a0fa](540a0fa))
* Agents now see a destructive, recursive `delete` filesystem tool
whenever the backend supports it, and filesystem permissions classify
`delete` as a write operation — so an existing rule allowing writes to a
path also authorizes recursively deleting that subtree unless a narrower
deny or interrupt rule covers the target. Because recursive deletes
affect descendants, deny and interrupt checks use bulk path overlap
instead of exact-path matching. To keep the previous behavior, add a
deny or interrupt rule, or omit `delete` from
`FilesystemMiddleware(tools=...)`. Missing paths return a not-found
error, `CompositeBackend` reports an unsupported-operation error when a
routed sub-backend cannot delete, and the tool is hidden from the model
entirely when the backend itself does not implement it.
([#3659](#3659))
([#3691](#3691))
([#3765](#3765))
([#3851](#3851))
([f2a21ec](f2a21ec))
* `write_file` can now create a file if it is missing and replaces it
entirely if it already exists, instead of returning a file-exists error.
The `write_file` tool description no longer requires reading the file
first. There is no "create-only" compatibility mode. Workflows, prompts,
tests, or guardrails that relied on the file-exists error to force
`edit_file` usage or to protect existing content must omit `write_file`,
add explicit permission or interrupt rules, or use `edit_file` where
preserving existing content matters.
([#4109](#4109))
([2506fcc](2506fcc))
* Removed deprecated backend compatibility shims. Callers must pass
concrete `BackendProtocol` instances (not factories), configure
`StoreBackend` with an explicit `namespace`, and use the current `ls` /
`glob` / `grep` / `ReadResult` APIs.
([#4541](#4541))
([540a0fa](540a0fa))
* The deprecated `files_update` attribute and constructor keyword are
removed from `WriteResult` and `EditResult`. Custom backends must stop
passing `files_update=`, and callers must stop reading
`result.files_update`; state writes are emitted directly by
`StateBackend`.
([#4541](#4541))
([540a0fa](540a0fa))
* Removed the deprecated `BackendProtocol` methods `ls_info`,
`als_info`, `glob_info`, `aglob_info`, `grep_raw`, and `agrep_raw`. Use
`ls` / `glob` / `grep` and their async counterparts.
([#4541](#4541))
([540a0fa](540a0fa))
* `SummarizationMiddleware(history_path_prefix=...)` was removed and now
raises `TypeError`. Configure `CompositeBackend(artifacts_root=...)`
instead.
([#4541](#4541))
([540a0fa](540a0fa))
* Agent-facing `ls` and `glob` tool output now renders empty results as
`No files found` instead of `[]`; direct backend APIs continue to return
structured empty `LsResult` and `GlobResult` values. Callers that parse
tool output should update those checks.
([#3709](#3709))
([efafd1e](efafd1e))
* `read_file` no longer renders raw text with a fixed-width `cat
-n`-style line-number gutter and tab separator. Line and continuation
markers are dynamically aligned and separated from source content by two
spaces, and the `LINE_NUMBER_WIDTH` constant is removed from
`deepagents.backends.utils` and `deepagents.middleware.filesystem`.
Callers that parse raw tool output should update those parsers.
([#4561](#4561))
([cf057b4](cf057b4))

### Features

* Custom middleware passed to `create_deep_agent(..., middleware=[...])`
can replace a default middleware instance when `.name` matches, so
defaults such as `SummarizationMiddleware` can be overridden without
also excluding the built-in instance.
([#4251](#4251))
([90c8472](90c8472))
* `FilesystemMiddleware(tools=[...])` accepts a keyword-only allowlist
of built-in filesystem tools, typed by the newly exported `FsToolName`
literal (`"ls"`, `"read_file"`, `"write_file"`, `"edit_file"`,
`"delete"`, `"glob"`, `"grep"`, `"execute"`); pass `"all"` or omit the
argument to keep every tool. A list must include `"read_file"` or the
constructor raises `ValueError`. Omitted built-in tools are
non-executable, and custom user tools are unaffected.
([#4325](#4325))
([#4698](#4698))
([704a70d](704a70d))
([9709525](9709525))
* Shorten LLM-facing descriptions for the `task` tool and filesystem
tools (`read_file`, `grep`, `edit_file`, `glob`, `execute`).
([#5009](#5009))
([761f5f0](761f5f0))
* `GrepResult` and `GlobResult` now carry a `truncated` flag so
supporting backends can return valid partial results when a match cap or
backend deadline is reached; agent-facing tool output adds a note
telling the model to narrow the search. `FilesystemBackend` returns
partial `grep` and `glob` results on its backend timeout rather than
erroring, while other backend or middleware timeouts may still return
errors. Its `glob` also gains brace expansion such as `*.{py,md}`
(already supported by the state and store backends).
([#4063](#4063))
([ef591e7](ef591e7))
* The agent-facing `grep` match cap is configurable:
`FilesystemMiddleware(grep_max_count=...)` sets the default (`1000`;
`None` disables it) and the model can override it per call through the
tool's new `max_count` argument. `grep` / `agrep` on `BackendProtocol`
and all built-in backends accept a keyword-only `max_count`. Local
ripgrep output is streamed and terminated once the cap is reached.
Direct `FilesystemBackend.grep()` callers can request surrounding lines
with keyword-only `context_lines`.
([#4570](#4570))
([#4706](#4706))
([8e86f5e](8e86f5e))
([65230df](65230df))
* Paginated built-in `read_file` responses report the returned
source-line range and next `offset`; total and remaining line counts are
included when the backend knows the file length. Resume offsets remain
safe when sandbox or middleware limits shorten the visible page.
([#4540](#4540))
([8321194](8321194))
* Optional video frame extraction for `read_file`, enabled by the new
`deepagents[video]` extra. Video files are sampled into JPEG frames,
with `offset` and `limit` interpreted as seconds. Without the extra,
existing generic video/file content-block behavior remains.
([#4094](#4094))
([b927147](b927147))
* `FilesystemMiddleware` can capture oversized `execute` tool output
directly inside the sandbox artifact path on compatible, opted-in
`BaseSandbox` implementations to reduce round trips; `LangSmithSandbox`
opts in by default.
([#4230](#4230))
([02f5bd7](02f5bd7))
* Automatically enable Fireworks prompt-cache session affinity when a
compatible `langchain-fireworks` installation is available.
([#4598](#4598))
([5d878bf](5d878bf))
* Add a built-in NVIDIA Nemotron 3 Ultra harness profile and NVIDIA NIM
app-origin attribution.
([#4192](#4192))
([#4455](#4455))
([d5a60ec](d5a60ec))
([4cb4749](4cb4749))
* `RubricMiddleware` now accepts any positive `max_iterations` cap
instead of enforcing a hard upper bound.
([#4405](#4405))
([d6692a7](d6692a7))

### Bug Fixes

* Keep fields marked with `PrivateStateAttr`, including fields declared
through `create_deep_agent(state_schema=...)`, out of subagent inputs
and returned parent-state updates.
([#4587](#4587))
([a4662c0](a4662c0))
* Preserve `ContextT` through the `create_deep_agent(...,
middleware=[...])` type annotation so type checkers accept context-aware
middleware when a matching `context_schema` is passed.
([#4055](#4055))
([7be76c7](7be76c7))
* Accept YAML list values as well as comma-separated strings for skill
`allowed-tools` frontmatter, and make skill truncation warnings
actionable with field name, path, length, configured limit, and impact.
([#4140](#4140))
([#4141](#4141))
([d62534c](d62534c))
([2f5f5b8](2f5f5b8))
* Align filesystem instructions with the tools that remain after
allowlist and backend-capability filtering, so agents no longer
reference hidden `grep`/`glob` tools or prohibit equivalent shell search
when dedicated search tools are unavailable.
([#4920](#4920))
([#4921](#4921))
([d3650c7](d3650c7))
([b65cc00](b65cc00))
* Propagate default-backend failures from `CompositeBackend.ls("/")` and
`CompositeBackend.als("/")` instead of returning successful route-only
listings.
([#4925](#4925))
([4c3b166](4c3b166))
* Correct `CompositeBackend.glob` / `CompositeBackend.aglob` routing so
explicit default-backend paths such as `/tools` do not also return files
from routed backends such as `/memories`.
([#4531](#4531))
([cbdb0a7](cbdb0a7))
* Propagate default- and routed-backend failures from root
`CompositeBackend.glob(..., path=None)` / `aglob(..., path=None)` and
`path="/"` searches instead of returning incomplete successful results.
([#4063](#4063))
([ef591e7](ef591e7))
* Constrain sandbox `glob` and slash-pattern `grep` searches to their
declared search root by treating leading `/` as search-root-relative,
rejecting `..` traversal segments, and filtering symlink-resolved
matches outside the root.
([#4588](#4588))
([c6c7213](c6c7213))
* Unify `grep(..., glob=...)` include-glob semantics across filesystem
and in-memory backends: basename patterns like `*.py` match at any
depth, and slash-containing patterns like `src/**/*.py` match relative
paths consistently.
([#3936](#3936))
([feab6e0](feab6e0))
* Improve agent-facing `grep` descriptions and no-match hints to steer
regex-looking patterns toward literal searches, route slash-containing
sandbox include-globs correctly, and shorten default search timeouts so
bad patterns and huge trees return guidance faster.
([#4168](#4168))
([b1dbf5e](b1dbf5e))
* Align sandbox delete behavior with other backends by returning
not-found errors for missing paths, and avoid over-blocking unrelated
sibling deletes when deny rules use glob patterns.
([#4321](#4321))
([d77496b](d77496b))
* Improve rubric grader failure diagnostics with configured model,
structured-output strategy, and integer HTTP status when available.
([#4938](#4938))
([#4967](#4967))
([f51d3a0](f51d3a0))
([bca70aa](bca70aa))
* Emit `max_iterations_reached` as the terminal `RubricMiddleware`
status when the iteration cap is exhausted, instead of a final
`needs_revision` event that will not loop.
([#4406](#4406))
([a51c8d2](a51c8d2))
* Handle missing async subagent URLs consistently in `check_async_task`
and `cancel_async_task`.
([#3967](#3967))
([b0d92c0](b0d92c0))

### Performance Improvements

* Run LangSmith sandbox commands over the async client.
([#5061](#5061))
([0d08747](0d08747))

---

_End release notes preview._

---

> [!NOTE]
> A **New Contributors** section is appended to the GitHub release notes
automatically at publish time (see [Release
Pipeline](https://github.com/langchain-ai/deepagents/blob/main/.github/RELEASING.md#release-pipeline),
step 2).

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Mason Daugherty <github@mdrxy.com>
Mason Daugherty (mdrxy) added a commit that referenced this pull request Aug 17, 2026
)

Fixes #4978

`glob("*.py")` now finds files in subdirectories on every backend. This
also aligns `glob()` with the `grep` include-glob rules from #3936.

Related: #3936, #4063.

---

Backends did not agree on what a bare pattern such as `*.py` means:

- `FilesystemBackend` matched the file name at any depth.
- `StoreBackend` and `StateBackend` matched top-level files only.
- `SandboxBackend` matched top-level files only and skipped files under
hidden directories.

A caller that switched backends got different results for the same
pattern, and an agent that expected the recursive `FilesystemBackend`
behavior silently found fewer files on the others. The `grep`
include-glob already had one shared contract (#3936); `glob()` did not.

## The rules

This PR gives `glob()` the same contract as the `grep` include-glob on
every backend:

- A pattern with no `/` matches the file name at any depth. `*.py`
matches `src/app/main.py` and `.hidden/x.py`.
- A pattern with a `/` matches the path relative to the search root and
supports `**`.
- A leading `/` anchors the pattern to the search root. `/*.py` matches
top-level files only.
- A name that starts with a dot matches only if that pattern segment
also starts with a dot, and `**` does not go into dot-directories. So
`*.yml` matches *more* files than `**/*.yml`.

The in-process backends use the shared matcher,
`compile_grep_include_glob`; the Sandbox backend mirrors its contract
remotely. If the matcher refuses a pattern (for example, brace expansion
past its limit), it raises `ValueError` and the backend returns it as
`GlobResult.error`.

## Sandbox backend

The sandbox cannot call the shared matcher directly. Its remote script
applies the same rules over `os.walk`, including hidden directories. A
20-case differential test compares the remote matcher with
`FilesystemBackend.glob`, which uses `compile_grep_include_glob`, across
the cases where the two underlying libraries disagree: negated character
classes, nested braces, empty path segments, trailing slashes, and case
sensitivity.

A bare pattern now walks the whole tree instead of listing one
directory, so the script must not run unbounded. These limits keep it
contained and make partial results visible:

- A 5-second time budget, checked per directory and within large
directories, and a cap on the number of matches. If the script stops
early, the result is marked `truncated` so it is never shown as
complete. A list clipped in transport is now also marked `truncated`.
- `**` matching is memoized, so a pattern such as `**/*/**/*` cannot run
exponentially.
- A directory the script cannot read is reported instead of quietly
missing from the results.
- Every failure returns a structured error code. Output the host cannot
parse is an error, not an empty result.
- Brace expansion is capped and returns `pattern_too_broad`, which is
distinct from `invalid_pattern` (used for `..` traversal).

## What changes for callers

This is a bug fix, but some changes are visible to callers:

1. `Store`, `State`, and `Sandbox` bare patterns now match files in
subdirectories. This is the intended fix. Code that assumed `*.py` meant
top-level only will now see nested paths.
2. On `Filesystem`, a pattern that contains a `/` no longer gets an
implicit `**/` prefix, and `/*.py` is top-level only.
3. A bare `*` does not match a name that starts with a dot. Use `.*` or
`.github/**` for those. A non-dot file under a hidden directory still
matches `*.py` on every backend.
4. `SandboxBackend.glob` returns absolute paths and regular files only,
matching `FilesystemBackend.glob`. It used to return
search-root-relative paths and include directories. Relative paths also
bypassed `deny` permission rules, because those rules match absolute
patterns.
5. `CompositeBackend` keeps an anchored pattern anchored when it strips
the route prefix. `/memories/*.md` stays top-level within the route
instead of matching at any depth.
6. A pattern the matcher refuses (for example, brace expansion past its
limit) raises `ValueError` and is returned as `GlobResult.error` from
every backend. Before, a private `wcmatch` exception reached the caller.

<details>
<summary>1 — Bare patterns: before and after</summary>

`glob("*.py")` with `a.py` at the root and `sub/b.py` nested:

- **Before:** `StoreBackend` and `StateBackend` returned `a.py` only.
`FilesystemBackend` returned both.
- **After:** every backend returns `a.py` and `sub/b.py`.

</details>

<details>
<summary>2 — Leading <code>/</code> anchors: before and after</summary>

`glob("/*.py")` with `a.py` at the root and `sub/b.py` nested:

- **Before:** `ContextHubBackend` returned both, because its matcher let
`*` cross `/`.
- **After:** every backend returns `a.py` only.

</details>

<details>
<summary>3 — Dot names: before and after</summary>

`glob("*.yml")` with `.github/workflows/ci.yml`:

- **Before:** matched on `Filesystem` and `ContextHub` (both crossed
dot-directories); not on `Store`, `State`, or `Sandbox` (top-level
only).
- **After:** still matches everywhere — a bare pattern matches the
basename at any depth. The dot rule applies once the pattern has a `/`:
`**/*.yml` no longer matches, because `.github` starts with a dot and
the pattern segment does not. Use `.github/**` to match it.

</details>

<details>
<summary>4 — Sandbox paths and permission rules: before and
after</summary>

`glob("*.py")` from search root `/workspace`, with a `deny` rule on
`/workspace/secrets/**`:

- **Before:** the sandbox returned `secrets/key.py` relative to the
search root. The `deny` rule did not match it (the rule needs an
absolute path), so the file was not filtered out.
- **After:** the sandbox returns `/workspace/secrets/key.py`, so the
`deny` rule matches and the file is filtered out.

</details>

<details>
<summary>5 — Routed patterns: before and after</summary>

`glob("/memories/*.md")` against a route mounted at `/memories/`, with
`note.md` at the route root and `sub/nested.md` deeper:

- **Before:** stripping the route prefix turned the pattern into the
basename pattern `*.md`, which matched `note.md` and `sub/nested.md`.
- **After:** stripping keeps the anchor, so the pattern becomes `/*.md`
and matches `note.md` only.

</details>

<details>
<summary>6 — Invalid patterns: before and after</summary>

A pattern whose brace expansion is too large (for example, many `{a,b}`
groups):

- **Before:** the backend raised a private `wcmatch` exception to the
caller.
- **After:** the matcher raises `ValueError`, and every backend returns
it as `GlobResult.error`.

</details>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

deepagents Related to the `deepagents` SDK / agent harness fix A bug fix (PATCH) internal User is a member of the `langchain-ai` GitHub organization size: S 50-199 LOC

Projects

None yet

Development

Successfully merging this pull request may close these issues.

grep glob filter has different semantics on every backend - **/*.py (the documented example) matches nothing on the default StateBackend

1 participant