feat(sdk): add subagent_model param to create_deep_agent#1369
Open
Harrison Chase (hwchase17) wants to merge 78 commits intomainfrom
Open
feat(sdk): add subagent_model param to create_deep_agent#1369Harrison Chase (hwchase17) wants to merge 78 commits intomainfrom
subagent_model param to create_deep_agent#1369Harrison Chase (hwchase17) wants to merge 78 commits intomainfrom
Conversation
…gent model override
libs/deepagents/deepagents/graph.py
Outdated
| system_prompt: str | SystemMessage | None = None, | ||
| middleware: Sequence[AgentMiddleware] = (), | ||
| subagents: list[SubAgent | CompiledSubAgent] | None = None, | ||
| subagent_model: str | BaseChatModel | None = None, |
Collaborator
There was a problem hiding this comment.
Do we have a way to specify t his through subagents instead?
General agent is forcing this arg to appear at top level of API, but it spreads subagent configuration into multiple locations
Fixes #1289 Pressing Escape while the slash-command or file-mention dropdown was open would skip the popup and immediately interrupt the running agent (or reject an approval). Gives the completion popup its own dismissal step in the `action_interrupt` priority chain. Before (Pressing `Esc` did not close the completion dropdown) : <img width="847" height="691" alt="Screenshot 2026-02-17 142557" src="https://github.com/user-attachments/assets/6309d245-3e87-43fe-951e-19e25d849eec" /> After (Pressing `Esc` cleanly hides the dropdown): <img width="844" height="688" alt="Screenshot 2026-02-17 145510" src="https://github.com/user-attachments/assets/ecc2ce6f-32ce-4056-8b1c-3e5565fe0a3e" /> --------- Co-authored-by: Mason Daugherty <mason@langchain.dev> Co-authored-by: Mason Daugherty <github@mdrxy.com>
Give the chat input box visual feedback when the user enters bash or command mode. The prompt character, border color, and prompt color all update to reflect the active mode. <img width="720" height="137" alt="Screenshot 19" src="https://github.com/user-attachments/assets/c949ef79-21bb-42b2-80b7-f6a82cb53192" /> <img width="716" height="327" alt="Screenshot 10" src="https://github.com/user-attachments/assets/1f3a91f2-9118-45b6-9313-d196f2d14c6f" /> <img width="713" height="138" alt="Screenshot 9" src="https://github.com/user-attachments/assets/9cc6ec3a-45bb-4854-9430-efde45b8aa94" />
Strip mode-trigger characters (`!`, `/`) from the text area after they switch the input mode, so users see clean input (e.g. `ls` instead of `!ls`) while the mode indicator badge communicates the active mode visually. <img width="298" height="127" alt="Screenshot 9" src="https://github.com/user-attachments/assets/369a67a9-181b-450e-b90c-47cb0dd70395" /> <img width="316" height="137" alt="Screenshot 8" src="https://github.com/user-attachments/assets/c4fc57cd-0123-4381-9fd1-645a5e4c2342" /> Also shows in history: <img width="587" height="437" alt="Screenshot" src="https://github.com/user-attachments/assets/e1f7afed-f0e2-4757-97c3-2599e849e3cf" />
The `/version` slash command and `--version` CLI flag previously only showed the `deepagents-cli` package version. Now both surfaces display the SDK (`deepagents`) version alongside the CLI version, making it easier to triage issues.
…ort (#1295) Rewrite `LocalContextMiddleware` to run environment detection as a bash script via `backend.execute()` instead of Python's `subprocess`/`pathlib` calls. The old implementation only worked locally — running detection through the backend means the same context-gathering logic works in remote sandboxes too. ## Changes - Replace all previous Python-based detection methods with a single bash script built from composable section functions - `LocalContextMiddleware` now requires a `backend` argument implementing the `_ExecutableBackend` protocol - Move middleware registration in `create_cli_agent` outside the local/remote branch so it activates for any backend that supports shell execution, gated by `isinstance(backend, _ExecutableBackend)` - Add new detection capabilities not in the old implementation: runtime version reporting (Python/Node versions), uncommitted change count, and `npm test` command detection via `package.json` scripts The loop detection middleware is ported from the Harbor benchmark harness where it ran across 445 TB2 trials, adapted for CLI's human-in-the-loop context. --------- Co-authored-by: Mason Daugherty <github@mdrxy.com>
) Fixes #1351 Fixes #1006 --- When tool results contain base64-encoded images (e.g. from screenshot or vision tools), the raw data would flood the terminal with thousands of characters of noise. This replaces those payloads with a compact `[Image: mime/type, ~NKB]` placeholder. Also fixes a separate issue where `_escape_markup` was doing a bracket replacement (`\[`, `\]`) that caused visible backslashes in tool output — now delegates to Rich's [`escape`](https://rich.readthedocs.io/en/latest/markup.html#escaping) which only targets actual markup patterns.
Switch the CLI's unit test runner from sequential to parallel execution via `pytest-xdist` (`-n auto`). Cuts total test time by ~75%
Restrict prompt history navigation to true input boundaries — up arrow only triggers at cursor position `(0, 0)`, down arrow only at the end of input. Previously, being anywhere on the first or last line was enough, which hijacked normal cursor movement while editing multi-character or multiline prompts. When already browsing history (`_in_history`), navigation is allowed from either boundary so cycling through entries still works naturally.
Add drag-and-drop image attachment to the CLI chat input. Dropping image files onto the terminal (or pasting their paths) encodes them as base64 and inserts `[image N]` placeholder tokens into the composer, treating each placeholder as an atomic unit for editing.
Defer heavy third-party imports out of module-level scope so the CLI loads faster. Every function that needs these libraries now imports them at call time. Also adds a fast-path version check – `deepagents -v` / `--version` now prints and exits before any heavy imports are loaded.
Add integration test suite to daytona
Add a small test suite for evals. These are more of "short-horizon" tasks to make sure that the agent can be efficient at specific tasks. if all the context is in place.
🤖 I have created a release *beep* *boop* --- ## [0.0.23](deepagents-cli==0.0.22...deepagents-cli==0.0.23) (2026-02-18) ### Features * **cli:** add drag-and-drop image attachment to chat input ([#1386](#1386)) ([cd3d89b](cd3d89b)) * **cli:** add skill deletion command ([#580](#580)) ([40a8d86](40a8d86)) * **cli:** add visual mode indicators to chat input ([#1371](#1371)) ([1ea6159](1ea6159)) * **cli:** dismiss completion dropdown on `esc` ([#1362](#1362)) ([961b7fc](961b7fc)) * **cli:** expand local context & implement via bash for sandbox support ([#1295](#1295)) ([de8bc7c](de8bc7c)) * **cli:** show sdk version alongside cli version ([#1378](#1378)) ([e99b4c8](e99b4c8)) * **cli:** strip mode-trigger prefix from chat input text ([#1373](#1373)) ([6879eff](6879eff)) ### Bug Fixes * **cli,sdk:** harden path hardening ([#918](#918)) ([fc34a14](fc34a14)) * **cli:** only navigate prompt history at input boundaries ([#1385](#1385)) ([6d82d6d](6d82d6d)) * **cli:** substitute image base64 for placeholder in result block ([#1381](#1381)) ([54f4d8e](54f4d8e)) ### Performance Improvements * **cli:** defer more heavy imports to speed up startup ([#1389](#1389)) ([4dd10d5](4dd10d5)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
parameterize by model
* Parametrizing the system message for exec vs. no exec. * In a separate PR we'll deal with the custom system message.
Add more evals
Fixes a rendering crash when shell commands contain literal `Rich`
tag-like text (for example `[/dim]`), which was being interpreted as
markup instead of plain text.
**Before**
`ApprovalMenu._get_command_display()` built a Rich-marked string by
interpolating raw command text:
```python
f"[bold #f59e0b]{command}[/bold #f59e0b]"
```
If `command` contained `[` or `]` tag sequences, Rich parsed them.
Example input:
```text
echo [/dim] [literal]
```
could raise:
```text
... closing tag '[/dim]' does not match any open tag
```
**After**
`command` (and truncated command) is escaped before insertion into
markup:
```python
f"[bold #f59e0b]{escape_markup(command)}[/bold #f59e0b]"
```
Result: Rich renders the command text literally, including bracket
sequences, with no parse error.
Replace hand-maintained help targets across all `Makefile`s The manual echo blocks were already drifting out of sync with actual targets
…1484) `BaseSandbox.ls_info()` and `_READ_COMMAND_TEMPLATE` interpolate `path`/`file_path` directly into Python code strings using raw `'{path}'` formatting. A crafted path containing single quotes can break out of the string literal and execute arbitrary code inside the sandbox. This applies the same base64-encoding pattern already used by `_GLOB_COMMAND_TEMPLATE`, `_WRITE_COMMAND_TEMPLATE`, and `_EDIT_COMMAND_TEMPLATE` in the same file. Fixes #1483 ## Areas requiring careful review - The `_READ_COMMAND_TEMPLATE` now takes `file_path_b64` instead of `file_path`, and `BaseSandbox.read()` encodes it before calling `.format()`. This is the same pattern as `glob_info()`. - The `ls_info()` inline command now encodes `path` the same way. - Two new tests verify that malicious paths do not appear in the generated command strings. ## How was this verified? - Ran `make format`, `make lint`, and `make test` from `libs/deepagents/` - Added `test_sandbox_ls_info_path_is_sanitized` and `test_sandbox_read_path_is_sanitized` to confirm malicious paths are not interpolated raw - Updated `test_read_command_template_format` to use the new base64 interface - Verified `write()`, `edit()`, `glob_info()`, and `grep_raw()` already use safe patterns (base64 or `shlex.quote()`) and are unchanged --------- Co-authored-by: Eugene Yurtsev <eyurtsev@gmail.com>
## Summary Fixed issue #1348 where glob patterns containing `**` silently return zero results when ripgrep is unavailable. ## Problem The `_python_search` fallback method had two bugs: 1. Used `fp.name` (filename only) instead of relative path for glob matching 2. Missing `GLOBSTAR` flag, so `**` wasn't treated as recursive wildcard ## Fix ```python # Before (broken): if include_glob and not wcglob.globmatch(fp.name, include_glob, flags=wcglob.BRACE): continue # After (fixed): if include_glob: rel_path = str(fp.relative_to(root)) if not wcglob.globmatch( rel_path, include_glob, flags=wcglob.BRACE | wcglob.GLOBSTAR ): continue ``` Now matches against relative path and adds GLOBSTAR flag for proper `**` matching. ## Testing - Reproduced the bug with test case from issue - Verified fix works with glob patterns like `**/*.py` AI assistance used for code review and implementation --------- Co-authored-by: Eugene Yurtsev <eyurtsev@gmail.com> Co-authored-by: Eugene Yurtsev <eugene@langchain.dev>
…tputs (#1482) ## Summary Fixes data loss when large `ToolMessage` content is evicted to `/large_tool_results/...`. Before this change, eviction rebuilt the message with only `content/tool_call_id/name`, which dropped runtime fields like `artifact`. This patch preserves the original message metadata when generating the evicted replacement message: - `artifact` - `id` - `status` - `additional_kwargs` - `response_metadata` ## Why this matters `artifact` is not model-facing text, but it is part of runtime semantics for downstream tooling, tracing, and replay/debug flows. Dropping it caused structured tool metadata to disappear after middleware processing. ## Tests Added regression coverage for both sync and async interception paths: - `test_intercept_long_toolmessage_preserves_artifact_and_metadata` - `test_store_backend_aintercept_large_tool_result_async` (extended) Local validation: - `uv run --all-groups ruff check deepagents/middleware/filesystem.py tests/unit_tests/test_middleware.py tests/unit_tests/backends/test_store_backend_async.py` - `uv run --all-groups ty check deepagents/middleware/filesystem.py` - `uv run --all-groups ty check tests/unit_tests/backends/test_store_backend_async.py` - `uv run --group test pytest -q tests/unit_tests/test_middleware.py -k "preserves_artifact_and_metadata or preserves_name"` - `uv run --group test pytest -q tests/unit_tests/backends/test_store_backend_async.py -k "aintercept_large_tool_result_async"` Additionally verified via `trace` that the new preservation lines are executed in both sync and async code paths. Closes #1450. --- AI assistance disclosure: AI support was limited to unit-test drafting, code review pass, and style/lint checks. Final code decisions and edits were manually validated.
## Summary - Refactors `_READ_COMMAND_TEMPLATE` to receive `file_path`, `offset`, and `limit` as a single base64-encoded JSON payload via stdin heredoc - Matches the pattern already used by `_WRITE_COMMAND_TEMPLATE` and `_EDIT_COMMAND_TEMPLATE` in the same file - Eliminates all raw parameter interpolation from the read command string Follow-up to reviewer feedback from @eyurtsev in #1484. Fixes #1499 ## Areas requiring careful review - The `_READ_COMMAND_TEMPLATE` now reads a JSON payload from stdin instead of using `.format()` placeholders for `file_path`, `offset`, and `limit` - `BaseSandbox.read()` now constructs and encodes the payload before formatting
Sydney Runkle (sydney-runkle)
approved these changes
Feb 25, 2026
Eugene Yurtsev (eyurtsev)
approved these changes
Feb 25, 2026
Collaborator
Eugene Yurtsev (eyurtsev)
left a comment
There was a problem hiding this comment.
It's fine for now, we will likely be able to support this later using a more general overrides argument. Mind documenting this in the api ref itself in create_deep_agents?
subagent_model param to create_deep_agent
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Allow users to override the default general-purpose subagent configuration in
create_deep_agentby passing a subagent with the name"general-purpose"in thesubagentslist.Previously, the built-in general-purpose subagent was always prepended, making it impossible to customize its model, system prompt, or other settings. Now, if a user-provided subagent shares the
"general-purpose"name, it replaces the default instead of duplicating it.Changes
libs/deepagents/deepagents/graph.py: Check whetherprocessed_subagentsalready contains a"general-purpose"entry before prepending the default spec. Also inlines the base agent prompt and adds a minor noqa annotation.libs/deepagents/tests/unit_tests/test_subagents.py: Addstest_general_purpose_subagent_overrideto verify that a user-supplied general-purpose subagent fully replaces the default.