Skip to content

fix(sdk): improve grep literal guidance and sandbox glob routing - #4168

Merged
Mason Daugherty (mdrxy) merged 17 commits into
mainfrom
mdrxy/sdk/grep-literal-regex-hint
Jul 2, 2026
Merged

fix(sdk): improve grep literal guidance and sandbox glob routing#4168
Mason Daugherty (mdrxy) merged 17 commits into
mainfrom
mdrxy/sdk/grep-literal-regex-hint

Conversation

@mdrxy

@mdrxy Mason Daugherty (mdrxy) commented Jun 23, 2026

Copy link
Copy Markdown
Member
  • grep now warns when a no-match pattern looks like a regex, since the tool only matches literal text.
  • The grep tool description and parameter docs now state up front that the pattern is literal, with explicit do-nots and a pointer to execute + rg for genuine regex.
  • Slash-containing globs (e.g. src/**/*.py) now work in sandbox backends — previously they silently returned zero results because GNU grep --include only matches basenames.
  • The grep tool description is now reconciled at request time against the backend's actual execute capability, so the rg fallback line appears only when execute is available.
  • Three new retrieval evals verify that a model recovers after a regex-style grep miss.
  • Default grep and glob timeouts are halved (grep 30s → 15s, glob 10s → 5s backend / 20s → 10s middleware) so bad patterns and large trees fail faster.

The grep tool matches literal text — FilesystemBackend shells out to ripgrep with -F, and the in-memory backends do a plain substring check. Models routinely reach for regex anyway (| alternation, .*, escapes like \.), which is matched verbatim and silently returns "No matches found" even when the content exists. This PR adds two complementary, non-breaking mitigations: a sharper tool description that sets expectations before the call, and a post-call hint that steers the model back to literal searches after a miss.

It also fixes a sandbox backend bug where slash-containing globs silently returned nothing, makes the grep description self-adjust to the backend's execute capability at request time, and halves the default wall-clock timeouts so that a pathological pattern or a huge tree fails faster.

User-facing behavior changes

  • The grep tool description now leads with "LITERAL text pattern across files (NOT regex)" and lists explicit do-nots: no | alternation, no .* wildcards, no \. escapes. When the execute tool is active for the backend, it also points to execute with rg for genuine regex needs; that line is dropped automatically when execute is unavailable, so the description never suggests a tool the model can't call.

    • Example: a model that previously called grep(pattern="foo|bar") and got "No matches found" should now read the tool description and run two separate calls — grep(pattern="foo") and grep(pattern="bar") — instead.
  • When grep returns no matches and the pattern carries strong regex signals, the tool response now appends a one-line hint:

    Note: grep matches literal text, not regex, so characters like `|`, `.*`, and `\.` are searched verbatim. Search for the literal text you need instead; for `|` alternation, run a separate search per alternative.
    

    The hint stays backend-agnostic (it never names the execute tool, which is not present in every configuration) and only appears on a genuine no-match result. If the pattern happens to match literally (e.g. a file contains a = b|c), or if matches existed but were all removed by read permissions, no hint is shown.

  • The glob parameter description on GrepSchema now clarifies that it is an in-tool file filter (not a call to the separate glob tool), spells out the matching semantics (a pattern without / matches the file name at any depth; a pattern containing / matches the search-root-relative path, e.g. src/**/*.py), and notes that brace expansion (e.g. *.{ts,tsx}) is not supported on all backends — the wcmatch-backed backends expand braces, but the Context Hub backend treats them literally, so running a separate search per extension is the reliable choice.

  • The output_mode parameter description now spells out the exact output shape for each mode instead of a terse one-liner: files_with_matches returns newline-separated paths, content returns matching lines grouped by file under a <path>: header with indented <line_number>: <line text> lines (no surrounding context), and count returns one <path>: <match_count> line per file.

  • Sandbox backends (BaseSandbox) now route slash-containing globs through an in-process Python glob instead of GNU grep --include. Previously, a pattern like src/**/*.py was passed to grep --include=src/**/*.py, which only matches basenames and silently returned zero results. The new _GREP_PATH_GLOB_TEMPLATE resolves the glob relative to the search root, reads matching files directly, and emits the same path\0line_num:text record structure that _parse_grep_output already consumes, so the rest of the pipeline is unchanged. Basename-only globs (no /) continue through GNU grep for speed.

  • The grep tool description is now a placeholder that is reconciled at request time. _create_grep_tool provisions the description assuming execute is available (so the rg fallback line is present). _filter_unsupported_tools_and_apply_prompt then checks whether execute is actually active for the backend and swaps in the without-execute variant via _with_filtered_grep_description when it isn't. The static description on self.tools is only provisional until a request runs. The unsupported-tools logic was extracted into _unsupported_tools_and_execution_state and _tool_name helpers to support this.

  • Default filesystem tool timeouts are halved:

    • grep sync phase: 30s → 15s
    • grep async wrapper: 65s → 35s (wraps two sync phases plus headroom)
    • glob backend walk: 10s → 5s
    • glob middleware deadline: 20s → 10s

    The layering invariants are preserved: the backend glob budget stays below the middleware deadline (5s < 10s), and the async grep timeout stays above two sync phases (35s > 30s). read_file has no timeout (single direct read, no search or tree walk) and is unchanged.

How regex detection works

Detection is deliberately conservative so common literal code searches don't trigger false hints. The heuristic flags:

  • | — alternation, the most common regex reflex
  • .* or .+ — wildcard quantifiers
  • Escaped metacharacters or character classes: \. , \w, \d, \s, \b, etc.

Bare ., (, ), [, ], ?, ^, $ are intentionally not flagged because they appear routinely in literal code searches:

def __init__(self):    # ( ) : are literal
self.tools              # . is literal
arr[0]                  # [ ] are literal

Motivation

A silent "No matches found" for a regex-style pattern is a common failure mode: the content exists, the model just asked for it the wrong way and gets no signal that the tool doesn't speak regex. The tool description fix addresses the problem before the call by making the literal-only contract unmistakable. The hint addresses it after the call by catching the model when it still reaches for regex, steering it toward separate literal searches instead of burning calls on regex variants. (The rg escape hatch lives in the always-visible tool description rather than the hint, so the hint stays useful even on backends without an execute tool.)

The sandbox glob fix addresses a quieter but equally confusible failure: a model that passes glob="src/**/*.py" to grep on a sandbox backend got an empty result with no indication that the pattern was the problem — the files existed and the pattern was valid, but GNU grep --include only matches basenames. The in-process glob route makes slash-containing patterns work as documented.

The dynamic description swap ensures the rg fallback line never appears for a backend that can't serve execute, preventing the model from being directed to a tool it can't call.

The timeout reduction addresses latency: the original values (30s grep, 20s glob) were chosen independently based on each operation's cost profile. Halving them lets the model receive a "narrow your search" signal sooner, reducing wasted wall-clock time on pathological patterns or huge directory trees.

The grep tool matches literal text (ripgrep -F / substring), but models
routinely pass regex (alternation, .*, escapes) and get a bare "No matches
found", masking that the content exists. Sharpen the tool description to
forbid regex and, on an empty result whose pattern carries regex signals,
append a hint pointing at separate literal searches or execute+rg.

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 feature New feature/enhancement or request for one internal User is a member of the `langchain-ai` GitHub organization size: S 50-199 LOC labels Jun 23, 2026
@codspeed-hq

codspeed-hq Bot commented Jun 23, 2026

Copy link
Copy Markdown

Merging this PR will improve performance by 24.37%

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

Performance Changes

Mode Benchmark BASE HEAD Efficiency
WallTime test_filesystem_init 490.4 µs 394.3 µs +24.37%

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-literal-regex-hint (f5e9de3) with main (20107ed)

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.

@mdrxy
Mason Daugherty (mdrxy) marked this pull request as ready for review June 23, 2026 14:41

@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: No issues found

Open SWE reviewed this PR and found no potential bugs to report.

Open in WebView Open SWE trace

…mpt snapshots

Add hillclimb retrieval evals covering the three regex signals the new
literal-grep hint detects (`|` alternation, `.*` wildcard, escaped `\.`),
asserting the agent still finds and reports the right files. Refresh the
stale system-prompt tool snapshots for the updated GREP_TOOL_DESCRIPTION
and regenerate EVAL_CATALOG.md.

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
@open-swe
open-swe Bot requested a review from vivek (vtrivedy) as a code owner June 23, 2026 21:55
@github-actions github-actions Bot added evals Evaluation suite and Harbor integration size: M 200-499 LOC and removed size: S 50-199 LOC labels Jun 23, 2026
@github-actions

github-actions Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

ℹ️ PR scope/file mismatch acknowledged via the allow-scope-mismatch label.

Title scope(s): sdk

Touched package dir(s) not covered by those scopes:

  • package label evals from libs/evals/

Remove the label to re-enable the block.

The grep `glob` param is a filename glob (NOT regex) and an in-tool filter,
not a call to the separate glob tool; say so explicitly. Replace the vague
`output_mode` description (which implied 'content' included surrounding
context) with the exact returned shapes per mode. Share the wording across
the schema and both tool signatures and refresh prompt snapshots.

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
@mdrxy Mason Daugherty (mdrxy) added the allow-scope-mismatch Bypass single scope requirement on PRs label Jun 23, 2026

@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/middleware/filesystem.py Outdated
Comment thread libs/deepagents/deepagents/middleware/filesystem.py Outdated
Comment thread libs/deepagents/deepagents/backends/utils.py Outdated
Mason Daugherty (mdrxy) and others added 2 commits July 2, 2026 15:01
Resolve conflicts and address review feedback:
- Drop redundant inline Annotated grep params (schema descriptions come
  from GrepSchema via args_schema).
- Privatize looks_like_regex -> _looks_like_regex (internal helper).
- Remove unnecessary default on _format_grep_tool_result pattern arg.
- Drop unsupported '*.{ts,tsx}' brace-glob example from grep glob
  description; note brace alternation is not supported across backends.
- Regenerate EVAL_CATALOG.md and refresh prompt snapshots.

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>

@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/middleware/filesystem.py Outdated
…s backends

The glob parameter description claimed brace alternation is "not supported
across backends." In reality, the wcmatch-backed backends (FilesystemBackend,
SandboxBackend, and the in-memory helpers) enable BRACE expansion via
 / ; only the Context Hub
backend (which uses fnmatch) treats braces literally.

Reword to "not supported on all backends" with "run a separate search per
extension for reliable results" so the guidance is accurate without
overstating the limitation.
@github-actions github-actions Bot added size: L 500-999 LOC and removed size: M 200-499 LOC labels Jul 2, 2026

@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/middleware/filesystem.py
Mason Daugherty (mdrxy) and others added 4 commits July 2, 2026 16:17
DEFAULT_GREP_TIMEOUT: 30s -> 15s (per sync phase)
ASYNC_GREP_TIMEOUT: 65s -> 35s (wraps 2 sync phases + headroom)
_DEFAULT_GLOB_TIMEOUT: 10s -> 5s (backend walk budget)
GLOB_TIMEOUT: 20s -> 10s (middleware outer deadline)

The layering invariants still hold:
- _DEFAULT_GLOB_TIMEOUT (5) < GLOB_TIMEOUT (10)
- ASYNC_GREP_TIMEOUT (35) > 2 * DEFAULT_GREP_TIMEOUT (30)
Comment thread libs/deepagents/deepagents/backends/protocol.py
@github-actions github-actions Bot added size: XL 1000+ LOC and removed size: L 500-999 LOC labels Jul 2, 2026
@mdrxy Mason Daugherty (mdrxy) changed the title feat(sdk): warn when literal grep pattern looks like regex fix(sdk): warn when literal grep pattern looks like regex Jul 2, 2026
@github-actions github-actions Bot added fix A bug fix (PATCH) and removed feature New feature/enhancement or request for one labels Jul 2, 2026
@mdrxy Mason Daugherty (mdrxy) changed the title fix(sdk): warn when literal grep pattern looks like regex fix(sdk): improve grep literal guidance and sandbox glob routing Jul 2, 2026
These evals pass deterministically across all tested models (100%
correctness, 4 models × 3 trials × 3 cases = 36/36), with perfect
step_ratio and tool_call_ratio. They test a concrete SDK feature (the
literal-grep regex hint) and behave as regression gates, not progress
signals.
@mdrxy

Copy link
Copy Markdown
Member Author

Eval results — grep regex-recovery (baseline tier)

Ran the three grep regex-recovery evals (| alternation, .* wildcard, \. escaped metachar) against four models, 3 trials each. All 36 runs passed — 100% correctness with perfect step and tool-call ratios.

Model Correctness Solve Rate Step Ratio Tool Call Ratio Median Duration
claude-sonnet-4-5 1.00 ±0.00 0.568 ±0.028 1.00 ±0.00 1.00 ±0.00 3.72s
gpt-5.5 1.00 ±0.00 0.640 ±0.093 1.00 ±0.00 1.00 ±0.00 3.07s
glm-5p2 (fireworks) 1.00 ±0.00 0.648 ±0.112 1.00 ±0.00 1.08 ±0.14 3.48s
claude-opus-4-8 1.00 ±0.00 0.483 ±0.027 1.00 ±0.00 1.00 ±0.00 4.09s

Every model recovers after a regex-style grep miss, finds the right files, and reports the correct paths. The minor tool-call-ratio variance on glm-5p2 (1.08) reflects an occasional extra call that still succeeds.

Tier reclassification

Moved these three evals from hillclimbbaseline in f5e9de354. They pass deterministically across all tested models with perfect step/tool-call ratios — they behave as regression gates for the literal-grep regex hint feature, not progress signals.

@mdrxy
Mason Daugherty (mdrxy) merged commit b1dbf5e into main Jul 2, 2026
64 of 65 checks passed
@mdrxy
Mason Daugherty (mdrxy) deleted the mdrxy/sdk/grep-literal-regex-hint branch July 2, 2026 23:04
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
…gchain-ai#4168)

* `grep` now warns when a no-match pattern looks like a regex, since the
tool only matches literal text.
* The `grep` tool description and parameter docs now state up front that
the pattern is literal, with explicit do-nots and a pointer to `execute`
+ `rg` for genuine regex.
* Slash-containing globs (e.g. `src/**/*.py`) now work in sandbox
backends — previously they silently returned zero results because GNU
`grep --include` only matches basenames.
* The `grep` tool description is now reconciled at request time against
the backend's actual `execute` capability, so the `rg` fallback line
appears only when `execute` is available.
* Three new retrieval evals verify that a model recovers after a
regex-style grep miss.
* Default `grep` and `glob` timeouts are halved (grep 30s → 15s, glob
10s → 5s backend / 20s → 10s middleware) so bad patterns and large trees
fail faster.


---

The `grep` tool matches *literal* text — `FilesystemBackend` shells out
to ripgrep with `-F`, and the in-memory backends do a plain substring
check. Models routinely reach for regex anyway (`|` alternation, `.*`,
escapes like `\.`), which is matched verbatim and silently returns "No
matches found" even when the content exists. This PR adds two
complementary, non-breaking mitigations: a sharper tool description that
sets expectations before the call, and a post-call hint that steers the
model back to literal searches after a miss.

It also fixes a sandbox backend bug where slash-containing globs
silently returned nothing, makes the grep description self-adjust to the
backend's `execute` capability at request time, and halves the default
wall-clock timeouts so that a pathological pattern or a huge tree fails
faster.

### User-facing behavior changes

* The `grep` tool description now leads with "LITERAL text pattern
across files (NOT regex)" and lists explicit do-nots: no `|`
alternation, no `.*` wildcards, no `\.` escapes. When the `execute` tool
is active for the backend, it also points to `execute` with `rg` for
genuine regex needs; that line is dropped automatically when `execute`
is unavailable, so the description never suggests a tool the model can't
call.
+ Example: a model that previously called `grep(pattern="foo|bar")` and
got "No matches found" should now read the tool description and run two
separate calls — `grep(pattern="foo")` and `grep(pattern="bar")` —
instead.
* When `grep` returns no matches *and* the pattern carries strong regex
signals, the tool response now appends a one-line hint:
  ```
Note: grep matches literal text, not regex, so characters like `|`,
`.*`, and `\.` are searched verbatim. Search for the literal text you
need instead; for `|` alternation, run a separate search per
alternative.
  ```
The hint stays backend-agnostic (it never names the `execute` tool,
which is not present in every configuration) and only appears on a
genuine no-match result. If the pattern happens to match literally (e.g.
a file contains `a = b|c`), or if matches existed but were all removed
by read permissions, no hint is shown.
* The `glob` parameter description on `GrepSchema` now clarifies that it
is an in-tool file filter (not a call to the separate `glob` tool),
spells out the matching semantics (a pattern without `/` matches the
file name at any depth; a pattern containing `/` matches the
search-root-relative path, e.g. `src/**/*.py`), and notes that brace
expansion (e.g. `*.{ts,tsx}`) is not supported on all backends — the
wcmatch-backed backends expand braces, but the Context Hub backend
treats them literally, so running a separate search per extension is the
reliable choice.
* The `output_mode` parameter description now spells out the exact
output shape for each mode instead of a terse one-liner:
`files_with_matches` returns newline-separated paths, `content` returns
matching lines grouped by file under a `<path>:` header with indented
`<line_number>: <line text>` lines (no surrounding context), and `count`
returns one `<path>: <match_count>` line per file.
* Sandbox backends (`BaseSandbox`) now route slash-containing globs
through an in-process Python glob instead of GNU `grep --include`.
Previously, a pattern like `src/**/*.py` was passed to `grep
--include=src/**/*.py`, which only matches basenames and silently
returned zero results. The new `_GREP_PATH_GLOB_TEMPLATE` resolves the
glob relative to the search root, reads matching files directly, and
emits the same `path\0line_num:text` record structure that
`_parse_grep_output` already consumes, so the rest of the pipeline is
unchanged. Basename-only globs (no `/`) continue through GNU `grep` for
speed.
* The `grep` tool description is now a *placeholder* that is reconciled
at request time. `_create_grep_tool` provisions the description assuming
`execute` is available (so the `rg` fallback line is present).
`_filter_unsupported_tools_and_apply_prompt` then checks whether
`execute` is actually active for the backend and swaps in the
without-`execute` variant via `_with_filtered_grep_description` when it
isn't. The static description on `self.tools` is only provisional until
a request runs. The unsupported-tools logic was extracted into
`_unsupported_tools_and_execution_state` and `_tool_name` helpers to
support this.
* Default filesystem tool timeouts are halved:
  - `grep` sync phase: 30s → 15s
- `grep` async wrapper: 65s → 35s (wraps two sync phases plus headroom)
  - `glob` backend walk: 10s → 5s
  - `glob` middleware deadline: 20s → 10s

The layering invariants are preserved: the backend glob budget stays
below the middleware deadline (5s < 10s), and the async grep timeout
stays above two sync phases (35s > 30s). `read_file` has no timeout
(single direct read, no search or tree walk) and is unchanged.

<details>
<summary>How regex detection works</summary>

Detection is deliberately conservative so common literal code searches
don't trigger false hints. The heuristic flags:

- `|` — alternation, the most common regex reflex
- `.*` or `.+` — wildcard quantifiers
- Escaped metacharacters or character classes: `\.` , `\w`, `\d`, `\s`,
`\b`, etc.

Bare `.`, `(`, `)`, `[`, `]`, `?`, `^`, `$` are intentionally *not*
flagged because they appear routinely in literal code searches:

```
def __init__(self):    # ( ) : are literal
self.tools              # . is literal
arr[0]                  # [ ] are literal
```

</details>

### Motivation

A silent "No matches found" for a regex-style pattern is a common
failure mode: the content exists, the model just asked for it the wrong
way and gets no signal that the tool doesn't speak regex. The tool
description fix addresses the problem before the call by making the
literal-only contract unmistakable. The hint addresses it after the call
by catching the model when it still reaches for regex, steering it
toward separate literal searches instead of burning calls on regex
variants. (The `rg` escape hatch lives in the always-visible tool
description rather than the hint, so the hint stays useful even on
backends without an `execute` tool.)

The sandbox glob fix addresses a quieter but equally confusible failure:
a model that passes `glob="src/**/*.py"` to `grep` on a sandbox backend
got an empty result with no indication that the pattern was the problem
— the files existed and the pattern was valid, but GNU `grep --include`
only matches basenames. The in-process glob route makes slash-containing
patterns work as documented.

The dynamic description swap ensures the `rg` fallback line never
appears for a backend that can't serve `execute`, preventing the model
from being directed to a tool it can't call.

The timeout reduction addresses latency: the original values (30s grep,
20s glob) were chosen independently based on each operation's cost
profile. Halving them lets the model receive a "narrow your search"
signal sooner, reducing wasted wall-clock time on pathological patterns
or huge directory trees.

---------

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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

allow-scope-mismatch Bypass single scope requirement on PRs deepagents Related to the `deepagents` SDK / agent harness evals Evaluation suite and Harbor integration fix A bug fix (PATCH) internal User is a member of the `langchain-ai` GitHub organization size: XL 1000+ LOC

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants