Deep Agents Code now offers an opt-in **Auto** approval mode. Auto
immediately runs a narrow, deterministic set of routine actions. For
other approval-gated actions, the active model checks whether they match
the user's literal request. It runs approved actions and blocks the
rest. After repeated denials or classifier failures, Auto sends the next
batch to the normal approval UI, then continues in Auto mode.
Auto is experimental and currently limited to interactive, local TUI
sessions using `FilesystemBackend`. Manual remains the default.
> [!WARNING]
> Auto is an authorization heuristic for a local coding agent. It is
**not** sandbox containment, an operating-system boundary, or a
guarantee that model-generated actions are safe.
## Enable Auto
1. Set `DEEPAGENTS_CODE_EXPERIMENTAL=1`.
2. Select Auto in one of these ways:
- In-app: use <kbd>Shift</kbd>+<kbd>Tab</kbd> or
<kbd>Ctrl</kbd>+<kbd>T</kbd> to toggle between Manual and Auto.
- At launch: pass `-y` or `--auto-approve`.
- By default: set `[startup].mode = "auto"` in
`~/.deepagents/config.toml`.
`-y` and `--auto-approve` now select Auto rather than unrestricted
execution. Users who want the previous unrestricted behavior must choose
`--yolo`. The removed `dangerously-auto` startup value is no longer
recognized and falls back to Manual.
---
## Why
The previous approval modes forced a binary choice:
- **Manual** preserved a human checkpoint, but repeatedly interrupted
routine coding work.
- **YOLO** removed that checkpoint entirely, including for actions that
exceeded the user's request (blanket approval).
Auto introduces a third policy: keep the low-friction path for clearly
routine work, but make authorization explicit for potentially
consequential actions.
| Mode | Policy | Entry points | Availability |
|---|---|---|---|
| **Manual** | Uses the normal human approval UI for gated actions |
Default; `[startup].mode = "manual"` | All interactive sessions |
| **Auto** | Uses narrow deterministic rules, then classifier review,
with human fallback after repeated denials or failures | `-y`,
`--auto-approve`, `[startup].mode = "auto"`, or the live keyboard toggle
| Experimental, local, unsandboxed TUI sessions |
| **YOLO** | Runs gated actions without review | `--yolo` or
`[startup].mode = "yolo"` | Interactive sessions after a one-time
acknowledgement |
## How Auto handles a proposed action
```mermaid
flowchart TD
A[Model proposes tool calls] --> B{Covered by the approval policy?}
B -->|No| X[Existing tool behavior is unchanged]
B -->|Yes| S{Is this thread still in Auto, with readable denial/failure history?}
S -->|No| J[Open the existing approval HITL widget]
S -->|Yes| C{Narrow deterministic allow?}
C -->|Yes| R[Execute without a classifier call]
C -->|No| D[Build one structured decision batch]
D --> H{Was this batch already reviewed, or do earlier denials/failures require human review?}
H -->|Yes| J
H -->|No| E[Active model reviews effects against user request/input]
E -->|Allow| R
E -->|Deny| Q{Total-denial threshold reached?}
Q -->|No| F[Return a sanitized error result]
Q -->|Yes| J
E -->|Unavailable or invalid| G[Return a compact unavailable result]
F --> I[Agent can revise its plan]
G --> I
J -->|Approve| R
J -->|Reject| K[Return a rejection result]
J -->|Switch to Manual| L[Persist Manual, then review the full gated batch]
R --> M[Reconcile the result and continue]
K --> M
I --> M
L --> M
```
In practice:
1. **Auto starts with Manual's existing human-in-the-loop (HITL)
rules.** It changes only how HITL-gated actions are reviewed.
- These built-in tools are not HITL-gated: `ls`, `read_file`, `glob`,
`grep`, `ask_user`, `get_goal`, `get_rubric`, and `update_goal`.
- MCP tools with valid read-only annotations, the optional `js_eval`
tool, and caller-supplied tools without HITL configuration continue to
run normally.
2. **Only narrow cases resolve locally.** The deterministic policy can
allow, never deny:
- A routine write such as `src/parser.py` can proceed based on its
resolved path and suffix; a sensitive target such as
`.github/workflows/ci.yml` goes to classifier review.
- A read-only Git command such as `git status` can proceed; a mutating
command such as `git commit -m change` goes to classifier review.
Explicit shell allow-list entries still apply after Auto removes broad
and wildcard rules.
- An MCP tool with a valid read-only annotation can proceed; tools
without read-only annotations, or with contradictory or malformed
annotations, go to classifier review.
<details>
<summary>How MCP read-only annotations are evaluated</summary>
MCP servers can attach standard `ToolAnnotations` when advertising a
tool. Deep Agents Code copies those annotations into the wrapped tool's
metadata and lets the tool bypass classifier review only when:
- `readOnlyHint` is the literal Boolean `true`;
- `destructiveHint` is absent, `null`, or `false`; and
- every supplied standard hint (`readOnlyHint`, `destructiveHint`,
`idempotentHint`, or `openWorldHint`) is a Boolean or `null`, rather
than a string or another type.
For example:
- `readOnlyHint: true, destructiveHint: false` proceeds without
classifier review.
- No `readOnlyHint` goes to review because the tool never explicitly
claimed to be read-only.
- `readOnlyHint: true, destructiveHint: true` goes to review because the
hints contradict each other.
- `readOnlyHint: true, destructiveHint: "false"` goes to review because
`"false"` is a string, not a Boolean.
In Auto, tools that do not pass this check enter the classifier batch.
In Manual, they use the normal approval UI; headless runtimes reject
them because they have no approval UI. The annotation is a
server-provided assertion that Deep Agents Code does not independently
verify.
</details>
3. **The classifier reviews all unresolved calls together.** Only
pre-expansion literal user text can authorize an action. Referenced
paths, summarized arguments, prior tool calls, and MCP metadata provide
effect context but cannot grant permission. Expanded files, assistant
prose, and tool results are excluded.
- **Example:** For the literal request `Research issue #123, update
@src/parser.py, and open a PR`, proposed `fetch_url` and `git push`
calls can be reviewed in one batch and receive separate decisions. The
referenced path and summarized call arguments help identify what each
action would affect, but the expanded file contents are not sent.
Instructions found in that file, assistant prose, or a prior tool result
therefore cannot widen the user's request or authorize another action.
4. **Auto revalidates before side effects.** The decision plan is bound
to the thread, mode, batch, and exact gated calls. Missing or invalid
state, a mode race, or a replay falls back to human review. Denied calls
are skipped and receive sanitized error results so the agent can revise
its plan.
- **Examples:** If the user switches to Manual while classifier review
is in progress, an earlier Auto decision cannot execute silently; the
normal approval UI opens instead. A plan associated with another thread,
a different set of tool-call IDs, missing decisions, or a previously
processed batch also requires human review. If Auto denies a proposed
`delete`, it returns an error result with that call's ID instead of
invoking the tool, allowing the agent to propose a safer next step.
## Limits and user-visible behavior
- The Manual approval menu can enable Auto for the current thread.
Threshold fallback can switch permanently to Manual or perform a one-off
review while leaving Auto enabled.
- Denials, classifier failures, and fallbacks appear as short sanitized
transcript events rather than raw classifier output.
- Auto requested without the experimental opt-in, or in a sandboxed
session, falls back to Manual with a warning. Explicit autonomous-mode
flags are rejected in non-interactive and ACP entry points.
- The active model is not an independent security authority. MCP
read-only annotations are trusted as a deliberate beta tradeoff.
- Parent-level Auto review does not cover actions performed inside
delegated subagents or broader explicitly configured PTC/`js_eval`
fan-out. Model providers and tracing backends may still observe
classifier inputs and outputs even though the TUI hides them.
<details>
<summary><strong>Implementation and reviewer details</strong></summary>
### Approval boundary and decision inputs
The following tools are HITL-gated:
- `execute`, `write_file`, `edit_file`, and `delete`;
- `web_search` and `fetch_url`;
- `task`;
- `start_async_task`, `update_async_task`, and `cancel_async_task`;
- `compact_conversation` when optional compaction approval is enabled;
and
- MCP tools without valid read-only annotations.
For deterministic writes and edits, the fast path uses only the resolved
target path and suffix; it does not inspect proposed content. Dependency
and lock files, sensitive names and locations, shell scripts,
unsupported file types, and paths outside the trusted root do not
qualify. Selected Git inspection commands must also pass shell-control
and outside-worktree path checks. Configured shell entries remain an
explicit user trust exception, but broad commands and wildcard entries
are ignored by Auto.
The classifier receives one typed batch containing:
- up to 20 client-attached, pre-expansion prompt rows, including literal
prompts, referenced path names, and turn IDs;
- trusted project-root and redacted-origin facts; and
- depth- and length-limited effect context from current arguments, prior
AI tool calls after the latest trusted prompt, and selected MCP
annotations.
Only `literal_user_text` is authorizing. Content-bearing write/edit
arguments are reduced to character counts. The classifier must return
exactly one unique decision for every unresolved tool-call ID; missing,
duplicated, unknown, or malformed decisions make the classifier
unavailable rather than partially authorizing the batch.
Before routing calls, Auto re-reads the live mode and validates its
private decision plan. Manual mode, invalid control state, mismatched
calls, or replayed batches require human review. Classifier review uses
a second call to the active session model with a 20-second timeout. Raw
classifier text and tool-call chunks are hidden from the transcript,
while usage accounting and tracing are retained.
Denial reasons are sanitized before persistence or display by removing
control characters, known credential values, credential-like
assignments, and URL credentials or query values. MCP tools without
valid read-only annotations are HITL-gated; headless runtimes reject
them because no approval UI is available.
### Failure and fallback behavior
| Condition | Result |
|---|---|
| Selected mode cannot be persisted | Persist Manual instead; if that
also fails, block graph execution |
| Classifier timeout, provider error, or invalid response | Block the
affected calls and return a compact unavailable result |
| Three consecutive policy denials in one user turn | Send the next
classifier-review-eligible calls to one-off human review |
| Two consecutive classifier-unavailable results | Send the next
classifier-review-eligible calls to one-off human review |
| A denial raises the thread's total denial count to 20 | Require human
review for that call immediately |
| Counter state cannot be read or reset before classification | Require
human review for every gated call in the batch |
| Counter state cannot be written after classification | Require human
review for classifier-path decisions; deterministic fast-path allows
remain eligible |
| Decision plan is missing, malformed, replayed, or bound to another
thread | Require Manual review |
| User chooses **Switch to Manual** during fallback | Persist Manual
before presenting the full gated batch for review |
### Python API and state compatibility
- Existing Boolean compatibility input retains its previous meaning:
`auto_approve=True` maps to YOLO, not Auto.
- Per-thread Store records persist the typed mode instead of a Boolean.
Missing, legacy, or malformed records fail closed.
- Startup and live mode changes take effect only after the Store write
succeeds.
- YOLO requires a versioned local acknowledgement and cannot be entered
through the live keyboard toggle.
### Reviewer guide
Suggested review order:
1. **Mode semantics and persistence** — `ApprovalMode`, Store payloads,
acknowledgement, startup resolution, and live mode writes.
2. **Authorization policy** — `AutoModeHITLMiddleware`, literal prompt
metadata, deterministic routing, structured classification,
sanitization, counters, and checkpoint validation.
3. **Graph boundary** — middleware replacement, the gated-tool
inventory, MCP annotation handling, headless rejection, PTC, and
delegated-subagent scope.
4. **Client behavior** — stream metadata, classifier-output filtering,
fallback interrupts, keyboard toggles, status presentation, and
transcript events.
Highest-risk invariants:
- every consequential tool in the supported boundary is gated or
explicitly outside scope;
- path-based writes and selected Git fast paths stay within their
documented checks;
- only literal user prompts can authorize classifier-reviewed effects;
- malformed output, Store failures, mode races, and checkpoint replay
fail closed; and
- hidden classifier chunks never create transcript content while usage
accounting remains intact.
</details>
Plugins are now available by default in deepagents-code without setting
`DEEPAGENTS_CODE_EXPERIMENTAL`.
This removes the flag gates from CLI and slash-command discovery, plugin
skills and MCP loading, and reload behavior while preserving the flag
for unrelated experimental features.
Made by [Open
SWE](https://openswe.vercel.app/agents/019f6c79-6211-734f-b05c-a5a3401c8a6b)
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Co-authored-by: Mason Daugherty <mason@langchain.dev>
Co-authored-by: Mason Daugherty <github@mdrxy.com>
`/goal` acceptance-criteria drafting now runs on the agent server and
can use conversation, repository, web, and explicitly read-only
configured MCP context while preserving resumable review state.
---
Previously, `/goal` proposals were drafted in the client process from
the explicit objective, so remote or sandbox repository context and
references to the current conversation were unavailable. This moves
proposal and amendment drafting into a nested agent in the main server
graph, where it can use recent conversation context, bounded read-only
repository search, `fetch_url`, optional web search, and configured MCP
tools explicitly annotated as read-only.
Repository context remains read-only and repository-rooted across
backends: local reads use a virtual project root, while sandbox reads
are constrained to the provider's declared working directory, including
canonical-path checks that reject symlink escapes.
Proposals are stored as pending checkpoint state rather than chat
messages, so review survives remote interrupts and resumes without
polluting the coding conversation. Each proposal is correlated with its
originating request, and criteria requests are cleared after success,
failure, or cancellation without clearing newer requests. Cancelling
criteria generation stops the server run without adding normal
chat-interruption messages. The client refreshes checkpoint state after
generation and reuses the existing accept, edit, and reject flow; model
profile overrides now cross the client/server boundary consistently.
In dcode local mode, offloaded large tool results previously lived
behind a virtual `CompositeBackend` route backed by a hidden `mkdtemp`
`FilesystemBackend`. Because `execute` always runs on the default
backend (never path-routed), the agent could not open those results with
`jq`/`grep`/`python` by the path it was handed — the
`/large_tool_results/` prefix wasn't a real inode, and the temp root was
intentionally hidden.
This points `CompositeBackend.artifacts_root` at a stable, hardened
per-user temp dir and drops the `/large_tool_results/` route, so
offloaded results fall through to the default `LocalShellBackend` at a
real path. The agent can now inspect them with `execute` using the exact
path the offload message hands it — no virtual-path translation, no
copy-out/in. Conversation history keeps a dedicated route to persistent
`~/.deepagents` storage so `/offload` archives survive restarts, and its
address stays stable across restarts because the artifacts root is
deterministic per user.
This uses the existing `artifacts_root` support on `CompositeBackend`
(already consumed by both the filesystem and summarization middleware),
so it is a dcode-only change with no SDK edit. The artifacts dir is
created `0o700` with an ownership/`S_ISDIR` guard and falls back to a
private unique dir if the predictable path is squatted; consolidating on
one per-user dir also removes the old unbounded `mkdtemp`-per-session
leak.
The rubric grader's read allow-list now derives its permitted prefix
from `artifacts_root` rather than a hardcoded `/large_tool_results/`.
Made by [Open
SWE](https://openswe.vercel.app/agents/e815e778-3188-4f2e-1068-e52819c024a7)
## References
- Plan:
https://openswe.vercel.app/agents/e815e778-3188-4f2e-1068-e52819c024a7/plan
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
`deepagents-code` now supports experimental plugin marketplaces with
namespaced skills and MCP servers. Enable with
`DEEPAGENTS_CODE_EXPERIMENTAL=1`.
Docs: https://github.com/langchain-ai/docs/pull/4905
---
`deepagents-code` can now manage experimental plugin marketplaces and
load installed plugins that contribute namespaced skills and MCP
servers. The new plugin manager establishes the `tui/modals`,
`tui/screens`, and `tui/widgets` UI convention, with colocated styling
and focused state/content modules.
- Supports local, GitHub, Git, and marketplace JSON sources.
- Adds install, enable, disable, uninstall, and safe marketplace removal
through `/plugins` and `dcode plugin`.
- Reloads plugin skills and plugin MCP configuration with `/reload` when
experimental mode is enabled.
- Namespaces plugin skills as `{plugin_id}:{skill_name}` through a
code-local `PluginSkillsMiddleware` adapter, so no SDK changes are
required.
Made by [Open
SWE](https://openswe.vercel.app/agents/019f5dea-0f1f-73fe-9803-32d1b13fbd4c)
---------
Signed-off-by: Johannes du Plessis <johannes@langchain.dev>
Co-authored-by: Alexander Olsen <aolsenjazz@gmail.com>
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Mason Daugherty <mason@langchain.dev>
Co-authored-by: open-swe[bot] <215916821+open-swe[bot]@users.noreply.github.com>
Co-authored-by: Mason Daugherty <github@mdrxy.com>
Depends on alpha release of new SDK changes
dcode's system prompt appears as though it was intended to replace the
base system prompt instead of augmenting it (some of the sections are
almost identical).
https://github.com/langchain-ai/deepagents/pull/4437 introduced a
mechanism to overwrite the base prompt. Here we apply it in dcode.
Review system prompt snapshot for effective changes.
---------
Co-authored-by: Mason Daugherty <mason@langchain.dev>
Co-authored-by: Mason Daugherty <github@mdrxy.com>
`/offload` now stores archived conversation history through the agent's
backend.
---
`/offload` previously summarized and saved conversation history in the
client process. In server and sandbox modes, that process does not own
the backend used by the agent: persistence could fail against a
read-only filesystem, and even a successfully written archive would not
be available to the agent through `read_file`.
This changes Deep Agents Code to run the existing `compact_conversation`
tool through the active agent instead. The command seeds a tool call
into the thread, approves the expected human-in-the-loop interrupt
because the user explicitly requested `/offload`, resumes the graph, and
reads the persisted summarization event back from server state. The
archive is therefore written through the agent's composite backend and
remains readable by the agent in local, server, and sandbox runs.
The old client-side `perform_offload` helper (and its `OffloadResult` /
`OffloadThresholdNotMet` / `OffloadModelError` result types) is removed,
as summarization and persistence now run through the agent. In local
mode, the `conversation_history` backend now roots under `~/.deepagents`
(via `_offload_fallback_root`, with a hardened private-temp fallback
when the home directory is not writable) instead of a throwaway
`tempfile.mkdtemp` directory, so offloaded history persists across
sessions.
The SDK's `compact_conversation` API is unchanged; this PR is confined
to Deep Agents Code.
Made by [Open
SWE](https://openswe.vercel.app/agents/1cbc308e-b411-2a09-bd6b-541e606ca4c5)
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Fixed `/goal` so completed objectives no longer grade later prompts,
while unfinished goals and completion requests that could not be saved
remain recoverable with clear guidance about what happens next.
---
`/goal` turns a plain-language objective into acceptance criteria and
keeps checking each follow-up turn until the work is done. This PR fixes
what happens when that lifecycle ends—or when grading or checkpoint
persistence fails.
Previously, a goal could remain active even after its completion was
approved. The next prompt would then still be graded against work that
was already finished.
Grader connection failures also looked like unmet criteria, and reaching
the grading iteration limit did not explain what was left to do.
Now, an approved completion clears the goal and its rubric after that
transition is saved, so later prompts start clean and `/goal show`
reports that no goal is set. The completion message and evidence remain
in the thread transcript. Blocked goals stay active because they still
need user input and should remain resumable.
If the completion cannot be saved, dcode restores the active goal and
its pending completion request instead of letting the UI diverge from
the thread checkpoint. The request is graded again on the next turn.
Grading is also more reliable and actionable:
- A transient mid-response transport failure retries the grader once
without rerunning the agent turn, so edits and tool calls are not
repeated.
- A persistent grader or infrastructure failure is identified as such
and leaves the unfinished goal intact for a safe retry.
- If grading reaches its iteration limit, dcode shows the unmet criteria
and how to resume, amend, or clear the goal. Headless runs show the same
criterion-level gaps.
## User stories and before/after examples
<details>
<summary>Completed goals stop grading unrelated follow-up
prompts</summary>
**User story:** As a user who has finished a goal, I want it cleared so
later prompts start clean and `/goal show` reflects that there is no
current goal.
**Before**
1. I complete “Add refresh-token support,” and dcode approves the
completion.
2. I ask, “Now explain how cache eviction works.”
3. dcode still grades that explanation against the refresh-token
acceptance criteria.
**After**
1. Once completion is approved and saved, dcode clears the goal, rubric,
status, and pending completion state.
2. “Now explain how cache eviction works” runs without the completed
goal attached.
3. `/goal show` reports “No goal set.” The earlier completion message
and evidence remain in the thread transcript.
</details>
<details>
<summary>A completion that cannot be saved remains safe to
retry</summary>
**User story:** As a user whose thread checkpoint is temporarily
unavailable, I want dcode’s displayed state to match the saved thread so
resuming cannot silently lose or misreport my goal.
**Before**
1. The grader approves completion, but saving the completed state fails.
2. dcode can look complete in the current session even though the thread
still contains an active goal and pending completion.
**After**
1. dcode restores the active goal, rubric, and pending completion
request in the UI.
2. It reports: “Goal completion could not be saved, so the goal remains
active and its completion request is still pending for retry.”
3. The completion request is graded again on the next turn.
</details>
<details>
<summary>A dropped grader response retries grading without repeating the
work</summary>
**User story:** As a user whose grader connection drops while reading a
response, I want dcode to retry the check without rerunning edits or
tool calls that already succeeded.
**Before**
1. The agent finishes the requested work.
2. The grader connection closes partway through its response.
3. The grading attempt ends even though the agent turn itself succeeded.
**After**
1. dcode retries the grader once for recognized mid-response transport
failures.
2. Only the grading subagent runs again; the task agent’s edits and tool
calls are not repeated.
3. If the retry succeeds, the normal rubric result is used.
</details>
<details>
<summary>Persistent grader failures are not presented as missing user
work</summary>
**User story:** As a user facing a grader or infrastructure outage, I
want dcode to identify the outage instead of telling me that my
implementation failed its criteria.
**Before**
A grader connection failure can look like an unmet rubric, leaving it
unclear whether the implementation or the grading service failed.
**After**
- The transcript identifies a persistent transport problem as
“Grader/infrastructure failure.”
- If the grader cannot evaluate the rubric, dcode says so explicitly
rather than reporting ordinary unmet criteria.
- The goal remains active. For an infrastructure failure, its completion
request also remains pending and is re-graded on the next turn.
</details>
<details>
<summary>Iteration limits show the remaining gaps and recovery
choices</summary>
**User story:** As a user whose goal reaches the grading limit, I want
to know what is still missing and how to continue.
**Before**
> ⚠ Acceptance criteria not satisfied (iteration limit reached)
The message does not identify the failing criteria or explain what
happens to the goal.
**After**
> ⚠ Iteration limit reached with unmet acceptance criteria
> ✗ Refresh-token tests — the expiration case is still failing
> The goal remains active. Continue with another prompt to resume or
retry, use `/goal <objective>` to amend it, or `/goal clear` to clear
it.
Interactive and headless runs both show criterion-level gaps, and the
unfinished goal remains available for the next action.
</details>
`deepagents-code` adds a `memory.auto_save` setting (env
`DEEPAGENTS_CODE_MEMORY_AUTO_SAVE` / `[memory].auto_save`, default on).
Disable it to keep loading memory into context while stopping the agent
from automatically writing learnings back to your `AGENTS.md` files.
---
Automatic memory saving in `deepagents-code` is prompt-driven:
`MemoryMiddleware` injects guidance telling the agent to proactively
persist learnings to the `AGENTS.md` sources. Until now the only switch
was `enable_memory`, which is all-or-nothing — turning it off also stops
memory from being loaded into context. This adds a way to keep loading
memory while turning the automatic saving off.
A new `memory.auto_save` option (env `DEEPAGENTS_CODE_MEMORY_AUTO_SAVE`,
`[memory].auto_save` in `config.toml`; defaults on) flips
`MemoryMiddleware` to a read-only prompt when disabled. The read-only
prompt keeps the trust/verification framing but drops the "proactively
persist learnings" guidance, so memory still informs the agent and
explicit saves (e.g. the `remember` skill) keep working — only
unprompted auto-saving stops.
To support this, the SDK gains a canonical
`MEMORY_READONLY_SYSTEM_PROMPT`, exported from `deepagents.middleware`
alongside `MEMORY_SYSTEM_PROMPT`.
Made by [Open
SWE](https://openswe.vercel.app/agents/825df1d1-b8e0-91c3-f7f6-dac1fcdd5c5e)
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
dcode no longer exposes the `write_todos` tool / todo-list middleware to
the agent or its subagents when `DEEPAGENTS_CODE_EXPERIMENTAL` is set.
---
`dcode` builds its agent with `create_deep_agent`, which always injects
the SDK's `TodoListMiddleware` (and its `write_todos` tool) and exposes
no parameter to disable it. Because `create_deep_agent`'s
`_apply_custom_middleware` merge replaces a default middleware in place
when a caller middleware shares its `.name`, we add a tool-less no-op
middleware named `TodoListMiddleware` and thread it into the main-agent
and subagent middleware lists — so the real middleware (and
`write_todos`) is dropped without any SDK change.
This is gated behind the new `DEEPAGENTS_CODE_EXPERIMENTAL` env var and
is a no-op when unset, so default behavior is unchanged. Kept
intentionally narrow: the todo prompt guidance and TUI rendering are
left in place (dead only when the flag is on).
Made by [Open
SWE](https://openswe.vercel.app/agents/c5e51661-6737-cd07-a93a-30847caeef06)
## References
- Plan:
https://openswe.vercel.app/agents/c5e51661-6737-cd07-a93a-30847caeef06/plan
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
The `system_prompt` parameter of `create_cli_agent` was documented only
as "override the default system prompt," which understates its effect.
Passing an explicit value replaces the auto-generated prompt *entirely*,
dropping all interpolated dynamic context (model identity, working
directory, sandbox-vs-local execution mode, skills path, and
interactive-vs-headless guidance), and rendering
`sandbox_type`/`interactive` inert for prompt purposes. This clarifies
the behavior in general terms — a reader does not need to know the
internal prompt-builder helper to understand the tradeoff.
Made by [Open
SWE](https://openswe.vercel.app/agents/25d7a0c1-a171-ddde-8532-4424d56578bd)
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Add `dcode tools list` (with `--json`) to show the tools available to
the agent, grouped by source.
---
Adds a `dcode tools list` verb under the existing `dcode tools` group
that prints the tools available to the agent, grouped by source
(built-in tools first, then per-server MCP tools), in an Amp-style
layout with a total-count header and a `--json` mode. Tools are
enumerated from the *real* tool objects the agent binds — the agent is
compiled with an offline placeholder model (no credentials or network)
and its bound tool node is read — so names/descriptions never drift from
what the model sees. MCP discovery is best-effort and skipped on
failure.
Made by [Open
SWE](https://openswe.vercel.app/agents/96e689b3-3bc7-3a04-7135-c2001b8ed058)
## References
- Plan:
https://openswe.vercel.app/agents/96e689b3-3bc7-3a04-7135-c2001b8ed058/plan
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Related: #4436
Fixed a credential leak where shell subprocesses spawned by Deep Agents
Code inherited the agent's internal LangSmith API key override instead
of the caller's original key. Subprocess environments now restore the
caller's `LANGSMITH_API_KEY` (and LangChain alias) to the value they had
before bootstrap, or remove the key entirely if the caller never set
one.
---
## Why
When a user launches Deep Agents Code with
`DEEPAGENTS_CODE_LANGSMITH_API_KEY` set, bootstrap bridges that prefixed
value onto the canonical `LANGSMITH_API_KEY` so the LangSmith SDK picks
it up for the agent's own tracing. This overwrites the caller's original
key in-process — fine for the agent itself, but the original value was
never saved. As a result, shell commands run by the agent (via
`execute`) inherited the override key, and the caller's own credential
was irrecoverable in that subprocess environment.
This is the same data-loss footgun that `restore_user_tracing_env`
already guards for tracing *flags*. The fix applies the same
save/restore pattern to tracing API *keys*: snapshot the caller's keys
during `_ensure_bootstrap`, then restore them (or drop the propagated
value if the caller had none) when preparing the shell subprocess
environment in `create_cli_agent`.
Deep Agents Code now records resume model state privately after
successful model calls, including model params, without exposing the
internal `effective_model` runtime context field in LangSmith traces.
```dcode -r 019f1a63-1fca-70a3-90bb-e94458e61f29```
---
Model resume bookkeeping now stays in private checkpoint state instead of being carried through runtime context as `effective_model`. The model middleware records the actual model request that completed, which also avoids persisting a requested override when server-side resolution falls back to the previous model.
## Changes
- Remove `effective_model` from `CLIContext` so it no longer appears as LangSmith trace metadata.
- Move `_model_spec` persistence into `ConfigurableModelMiddleware`, emitted after a successful model response using `ExtendedModelResponse` and a private state update.
- Persist `_model_params` alongside `_model_spec` so resumed sessions restore invocation params such as `temperature` or `max_tokens`.
- Keep subagent model middleware from writing parent-thread resume metadata.
- Leave `ResumeStateMiddleware` focused on `_context_tokens`, with model metadata owned by the middleware that resolves the actual request.
Closes#4404
Deep Agents Code now uses clearer acceptance-criteria grading messages,
lets the rubric grader inspect offloaded tool-result evidence referenced
in the transcript, defers the rubric iteration default to the SDK, and
adds interactive grader controls for goals and rubrics.
---
This improves the goal and rubric grading UX so the status messages
describe the acceptance criteria check rather than implying the rubric
text itself is wrong. When grading fails with `needs_revision`, the UI
now says the changes need revision. Iteration numbers are hidden unless
the user explicitly configured an iteration cap, keeping the default
`/goal` and `/rubric` flow less noisy.
Deep Agents Code no longer sets its own default rubric iteration cap. If
the user does not pass `--rubric-max-iterations`, dcode omits
`max_iterations` and lets `RubricMiddleware` use the SDK default.
Explicit `--rubric-max-iterations` values are still forwarded, and the
SDK now accepts any positive cap instead of enforcing the previous hard
upper bound.
The rubric grader also gets a narrow `read_file` tool that can only
inspect `/large_tool_results/` paths explicitly referenced in the
transcript. This gives the grader a way to verify offloaded evidence
without exposing broader filesystem access or waiting for an SDK-level
transcript reconstruction change.
The TUI now exposes `/rubric max-iterations <N|clear>` and `/goal
max-iterations <N|clear>` for interactive sessions. Changing the value
restarts the app-owned LangGraph server so the construction-time
`RubricMiddleware` setting takes effect. `/goal model
[provider:model|clear]` and `/goal max-iterations` are goal-first entry
points for the same shared grader settings surfaced by `/rubric model`
and `/rubric max-iterations`.
Adds rubric-driven acceptance criteria to Deep Agents Code and layers a
goal workflow on top of it.
A rubric is the explicit definition of done. Use it when you already
know the criteria the agent should satisfy before it considers the work
complete. Criteria can be sticky for the thread, one-shot for the next
turn, or loaded from a file. While the agent works, the TUI shows
grading lifecycle messages so the user can see when the work is being
checked, revised, satisfied, or stopped.
Usage examples:
```text
/rubric set tests pass; no unrelated files changed; help text is updated
/rubric next only change the auth callback; do not refactor unrelated code
/rubric file acceptance.md
/rubric show
/rubric clear
```
A goal is the objective-oriented workflow. Use it when you know the
outcome, but want `dcode` to propose acceptance criteria before
execution. `/goal <objective>` asks the model to draft criteria and
displays them in an inline review prompt. The user can accept the
criteria, edit them directly, reject them with feedback to regenerate,
or cancel. Accepted goal criteria become the sticky rubric for the
thread.
Usage examples:
```text
/goal add OAuth refresh handling
/goal show
/goal clear
```
The `--goal` CLI flag starts the same goal-review workflow when
launching the TUI. It is intentionally interactive: the generated
criteria must be reviewed before execution. After the user accepts the
proposal, the accepted goal is sent as the first task.
```bash
dcode --goal "add OAuth refresh handling"
```
`--goal` cannot be combined with `-n`, `-m`, `--skill`, or `--rubric`.
For non-interactive/headless runs, users should provide criteria
explicitly with `--rubric` instead:
```bash
dcode -n "implement OAuth refresh handling" --rubric "tests pass; no unrelated files changed"
dcode -n "implement OAuth refresh handling" --rubric @acceptance.md
```
Accepted goal/rubric state is persisted on the thread and restored on
resume. For example, a user can set `/goal add OAuth refresh handling`,
accept the proposed criteria, quit the TUI, and later resume the same
thread with the goal and criteria still active. That matters because the
acceptance criteria continue to guide future turns and remain visible in
`/goal show` instead of becoming hidden context that disappears between
sessions.
When the agent believes the goal is done, it does not get to declare
victory on its own. For example, after implementing OAuth refresh
handling, the agent can ask to mark the goal complete with evidence such
as "tests pass." `dcode` keeps that request pending until the rubric
check finishes. If the rubric still needs revision, the goal stays
active and the user sees why it was not completed. If the rubric is
satisfied, auto-approve mode records the completion automatically;
manual mode asks the user before changing the goal status. This keeps
the user's accepted criteria as the source of truth for whether the goal
is actually finished.
The active criteria and goal are also visible to the agent through
constrained tools. `get_rubric` lets the agent inspect the current
criteria and whether they came from a goal, a sticky rubric, or the
current invocation. `get_goal` lets it inspect the active objective,
status, criteria, and any prior note. `update_goal` lets it report when
it believes the goal is `complete` or `blocked` with evidence. The
constraints matter: the agent can read criteria and update progress, but
it cannot create, pause, resume, clear, or replace goals. Those
lifecycle actions stay user/system controlled so the model cannot
silently redefine or remove the user's objective.
Wires `deepagents-code`'s headless (`-n`) path to the SDK's
`RubricMiddleware` so a run can self-evaluate against caller-supplied
acceptance criteria and loop until satisfied. This is the
non-interactive vertical slice of the rubric/"acceptance criteria"
feature; the interactive TUI `/rubric` command + status indicator are
intentionally left as a follow-up.
New flags (all `-n`/piped-stdin only):
* `--rubric TEXT|@PATH` accepts literal criteria, or reads criteria from
`@path`. `@path` may be absolute, relative to the `dcode` process
working directory, or use `~` for the home directory.
* `--rubric-model MODEL` defaults to the main agent model.
* `--rubric-max-iterations N` controls grader iterations per attempt.
The rubric text is passed as graph `rubric` state; grader model +
max-iterations flow to the server subprocess via `ServerConfig`. Grader
lifecycle events are surfaced by subscribing to the `custom` stream and
rendering `rubric_evaluation_start/end` (⏳ grading / ✓ satisfied / ↻
needs revision / ⚠ max-iterations / failed), and the headless header
shows `Rubric: active`.
## Test Plan
- [ ] `dcode -n "implement X" --rubric "tests pass; minimal diff"`
against an SDK that ships `RubricMiddleware` loops the agent and renders
grading events
- [ ] `--rubric @rubric.md` resolves file contents from a relative path;
absolute paths and `~`-expanded paths are also supported
- [ ] `--rubric` without `-n` exits 2
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
`delete` now follows the same approval, display, tracking, and
memory-protection paths as other mutating file tools. This closes the
gap where destructive deletes could bypass file-operation previews and
managed memory protections.
## Changes
- Register `delete` as a write-capable tool for PTC auditing and add it
to the human approval interrupt map.
- Add delete-specific approval previews that show removed file content
as a unified diff when possible, or flag directories and unreadable
paths explicitly.
- Track completed delete operations in `FileOpTracker`, modeling
successful file deletion as an empty after-state so removed lines appear
in operation diffs.
- Extend `ManagedMemoryGuardMiddleware` to reject deletes of guarded
memory files, including parent-directory deletes that would remove a
managed memory block.
- Display `delete` calls consistently with other file tools in message
headers and tool labels.
Local dcode sessions now start with the JS interpreter and safe PTC
bridge available by default, while remote sandbox launches keep it
disabled unless explicitly requested. `langchain-quickjs` is now a core
dependency, so users no longer need to install the `quickjs` extra just
to use interpreter support.
Approval mode now has a live control path for remote `dcode` sessions
instead of relying only on the run-context snapshot captured at stream
start. That lets mid-run toggles take effect before the next gated tool
call, while failing closed to manual approval when the live state cannot
be synced or read.
## Changes
- **Live approval state:** Added `approval_mode_key`,
`approval_mode_payload`, `read_approval_mode_from_store`, and
`awrite_approval_mode` to store per-thread auto-approve state in the
LangGraph Store under a deterministic hashed thread key.
- **Interrupt decisions:** Updated `_should_interrupt_tool_call` to
prefer the live store value over stale context, so toggling from
auto-approve back to manual can interrupt subsequent gated tool calls in
the same active run.
- **TUI synchronization:** Updated
`DeepAgentsApp.action_toggle_auto_approve` and
`_on_auto_approve_enabled` to write the live approval mode whenever the
user toggles approval behavior.
- **Fail-closed behavior:** If manual approval cannot sync while an
agent is running, the app warns the user and cancels the active run
rather than allowing stale auto-approval to continue.
- **Remote support:** Added `RemoteAgent.aput_store_item` so remote
sessions can write unindexed LangGraph Store records used by the
server-side approval predicate.
- **Context reconstruction:** Preserved `auto_approve` and
`approval_mode_key` when dict-based runtime context is reconstructed
into `CLIContextSchema`.
- **Stream propagation:** Updated `execute_task_textual` to write live
approval state before each stream iteration and await async auto-approve
callbacks triggered from approval prompts.
The `deepagents-code` release failed because the published SDK now
returns `No files found` for empty filesystem listings. That means the
temporary `_FilesystemEmptyResultMiddleware` canary did its job: the
upstream behavior changed and the compatibility middleware is no longer
needed for the release artifact.\n\nThis removes the obsolete
`deepagents-code` middleware and its tests so the release check can pass
against the current pinned published SDK behavior.\n\nVerified
with:\n\n- `make -C libs/code lint`\n- `make -C libs/code test COV_ARGS=
PYTEST_EXTRA="--no-cov -q"`
LangSmith tracing can now be configured directly from `/auth` (and
`dcode auth set langsmith`), including enabling tracing, a temporary
opt-out via `DEEPAGENTS_CODE_LANGSMITH_TRACING=false`, and a custom
project name.
---
Wires LangSmith tracing into the `/auth` surface as a configurable
service, mirroring how Tavily is already handled. Storing a LangSmith
key via `/auth` persists it to the credential store, bridges it onto
`LANGSMITH_API_KEY`, and opts the user into tracing — so configuring
tracing no longer requires exporting environment variables before
launch.
Key behaviors:
- A stored key turns tracing on by default. An explicit falsy tracing
flag (most simply `DEEPAGENTS_CODE_LANGSMITH_TRACING=false`) is honored
as a non-destructive, session-scoped opt-out — pause tracing without
deleting the key. The TUI prompt surfaces this path.
- An optional project name (advanced field in the TUI prompt,
`--project` on `dcode auth set`) routes traces to a custom
`LANGSMITH_PROJECT`; the default stays `deepagents-code`.
- Services are now surfaced in `dcode auth list`/`status` (this also
fixes a pre-existing gap where Tavily was invisible to the CLI).
- Bootstrap reordered so a `/auth`-stored key is applied before
orphaned-tracing detection, keeping tracing alive instead of being
disabled.
Made by [Open
SWE](https://openswe.vercel.app/agents/2ba1485c-7c70-59b6-82bf-18302c214c6c)
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Fix the `/agents` picker listing the internal `bin` directory (managed
ripgrep install) as a selectable agent.
---
The `/agents` picker lists every real, non-dot subdirectory under
`~/.deepagents/`, filtering only symlinks and dot-prefixed names. The
managed ripgrep install dir (`~/.deepagents/bin`,
`managed_tools.BIN_DIR`) therefore showed up as a phantom `bin` agent.
This adds a reserved-name filter to `_is_agent_dir_entry`, sourced from
`BIN_DIR.name` to keep a single source of truth.
Made by [Open
SWE](https://openswe.vercel.app/agents/3cd7dda7-db49-2afd-b636-d678ecc65dc0)
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
`make test` in `libs/code` had started reporting warning noise from a
few dependency and test-helper paths, which made it harder to notice new
warnings in the unit suite.
This keeps the production `LocalShellBackend` construction explicit
about `virtual_mode=False`, and localizes expected dependency warnings
in the tests that intentionally instantiate beta or experimental
classes. It also avoids socket-guard warnings from test-only setup paths
by disabling unrelated MCP metadata preload in non-interactive tests and
passing explicit empty socket options when constructing the Codex model
under the socket guard.
Deep Agents Code no longer splits a turn into many runs when "approve
always" is enabled; gated tool interrupts are now suppressed at the
source.
---
When a user enables "approve always" in Deep Agents Code,
`HumanInTheLoopMiddleware` kept interrupting on every gated tool call
and the client auto-resolved each one. Each interrupt/resume cycle
starts a fresh agent run, so a single turn fragmented into many runs and
produced noisy, hard-to-parse traces.
The fix carries the auto-approve decision in run-scoped CLI context
(`CLIContext` / `CLIContextSchema.auto_approve`) rather than graph
state, and each `interrupt_on` config gains a `when` predicate
(`_should_interrupt_tool_call`) that reads `request.runtime.context`.
Once approve-always is in effect the predicate returns `False`, so the
middleware skips the interrupt entirely instead of pausing and being
patched on the client. The initial turn seeds the flag from session
settings (`app.py`), and `textual_adapter.py` refreshes it into context
on every stream iteration — so choosing "auto-approve all" mid-turn
propagates to the resuming stream and the remaining tool calls in that
same run also stop interrupting.
Auto-approve is sourced from run context rather than graph state for two
reasons: seeding state would require a first-turn `Command(update=...)`,
which the LangGraph API server rebuilds with `goto=None` and crashes
`_control_branch` on a fresh thread; and context is safer because the
model cannot self-approve by writing state. The predicate is fail-closed
— missing or malformed context still interrupts — and consumers accept
both a coerced `CLIContextSchema` (in-process) and a plain dict (over
the LangGraph API / RemoteGraph).
Made by [Open
SWE](https://openswe.vercel.app/agents/4b76ff9f-a307-5834-5471-a0d441f2a147)
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Co-authored-by: Sydney Runkle <54324534+sydney-runkle@users.noreply.github.com>
`/trace` no longer shows a scary "project not found" error before the
first run is traced, and the default `deepagents-code` tracing project
is now actually used when tracing is enabled with no project configured.
---
`/trace` surfaced LangSmith's raw `Project deepagents-code not found`
404 as an error, even though it only means no trace has been ingested
into that project yet (projects are auto-created lazily on first
ingest). We now classify that 404 as a dedicated
`LangSmithProjectNotFoundError` and show a friendly "created on first
trace" hint instead. We also default `LANGSMITH_PROJECT` to
`deepagents-code` at bootstrap when tracing is enabled but no project is
configured, so ingestion matches the name `get_langsmith_project_name()`
displays and looks up. We deliberately do **not** call `create_project`.
Made by [Open
SWE](https://openswe.vercel.app/agents/f2a5f732-0697-777e-0bb2-7f2fb0704f00)
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
## Summary
Bumps the `langchain-quickjs` dependency constraints to the current
`0.3.x` line in `libs/code` and `libs/evals`.
## Changes
- **`libs/code/pyproject.toml`**: bump the `test` dependency group
constraint from `>=0.2.0,<0.3.0` to `>=0.3.0,<0.4.0`, matching the
`quickjs` optional extra (which was already `>=0.3.0,<0.4.0`).
- **`libs/code/deepagents_code/agent.py`**: update a stale docstring
version reference (`>=0.1.2,<0.2.0` -> `>=0.3.0,<0.4.0`).
- **`libs/evals/pyproject.toml`**: bump `>=0.1.4` -> `>=0.3.0,<0.4.0`
(adds an upper bound, consistent with the rest of the repo).
- **Lockfiles**: re-ran `uv lock` in both libs. This also pulled
`quickjs-rs` `0.2.0` -> `0.2.3` transitively.
## Notes
`0.3.0` is the latest published version on PyPI. In both libs the
dependency resolves through the editable `../partners/quickjs` path
locally.
Closes#1424
---
Adds a `Sign in with ChatGPT` OAuth flow to `/auth` in `deepagents-code`
and a new `openai_codex` model provider backed by upstream's
`ChatOpenAICodex`. Codex-eligible models (e.g. `gpt-5.5`) now appear
under their own auth context in the `/model` switcher, separate from the
API-key-backed `openai` provider.
The OAuth heavy-lifting — PKCE, refresh-aware file-backed token store,
the loopback callback HTTP server — lives upstream in
`langchain_openai.chatgpt_oauth` (langchain-ai/langchain#37569).
The new `run_browser_login` composes upstream's PKCE / authorize-URL /
callback-wait / token-exchange primitives so the Textual modal can
surface the authorize URL inline *before* the loopback wait begins. The
upstream `login_chatgpt()` helper prints the URL to stdout, which is
invisible inside a Textual app. The flow mirrors the `/mcp` loopback
pattern: a `ThreadingHTTPServer` on a fixed port, PKCE plus state CSRF,
browser launch with a manual-URL fallback, and a cancellable progress
modal.
_Opened collaboratively by Mason Daugherty and open-swe._
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Co-authored-by: Mason Daugherty <61371264+mdrxy@users.noreply.github.com>
Co-authored-by: Mason Daugherty <github@mdrxy.com>
Co-authored-by: Mason Daugherty <mason@langchain.dev>
LangSmith trace metadata now separates dcode's selected agent name from
Agent Server's `assistant_id`, avoiding collisions with the UUID
assigned by LangSmith. Root dcode runs also pass the selected agent name
into `create_deep_agent`, so `lc_agent_name` is populated consistently
with LangChain's agent naming metadata.
## Changes
- Set `create_deep_agent(..., name=assistant_id)` in `create_cli_agent`
so root runs report the selected dcode agent through `lc_agent_name`.
- Replace dcode's custom `metadata["assistant_id"]` value with
`metadata["dcode_agent_name"]`, leaving LangSmith/Agent Server ownership
of `assistant_id` intact.
- Keep `metadata["agent_name"]` for compatibility while adding the more
explicit dcode-specific metadata key.
- Clarify `build_stream_config` docs so `assistant_id` is described as
the dcode agent identifier in this code path.
Restore inheritance of a launch-time `PYTHONPATH` into agent shell
commands.
---
`_SERVER_ENV_DENYLIST` stripped `PYTHONPATH` from the `langgraph dev`
server process, so agent `execute` shell commands no longer inherited a
user-provided launch environment (e.g. `PYTHONPATH=src dcode`), causing
`ModuleNotFoundError`. Remove `PYTHONPATH` from the denylist; project
`.env` injection stays blocked by `config._DOTENV_DENIED_ENV_KEYS`.
Made by [Open SWE](https://openswe.vercel.app)
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
The interpreter PTC allowlist (`interpreter_ptc`, set via
`--interpreter-tools` or `[interpreter].ptc`) lets `js_eval` call a
subset of the agent's tools by name. Resolution ran while the agent was
being built and validated the configured names against the tool list
handed to `create_cli_agent`. That list does **not** contain the Deep
Agents built-ins (`read_file`, `glob`, `grep`, `write_file`,
`edit_file`, `execute`, `write_todos`, `task`) — those are injected a
step later by SDK middleware. In server and non-interactive mode the
handed-in list is only `fetch_url`/`web_search`/MCP tools, so two things
broke:
- Naming a built-in (e.g. `--interpreter-tools read_file,grep,task`)
failed validation and **aborted server startup** with `Unknown tool
names: [...]` (exit code 3).
- `interpreter_ptc="safe"` intersected its read-only preset against that
list, matched none of its members, and silently resolved to an empty
allowlist — exposing nothing.
`CodeInterpreterMiddleware` already resolves PTC names against the live
runtime registry and silently drops names that match nothing, so
build-time validation was both unnecessary and wrong. This change stops
validating names at build time: `"safe"` resolves to its preset
directly, explicit names pass through to the middleware, and runtime
decides what is exposed. The SDK exposes no importable list of built-in
tool names, so there is deliberately no hardcoded mirror that could
drift.
While here, the `"safe"` preset can now be combined with explicit names
in a list — `--interpreter-tools safe,task` or `ptc = ["safe", "task"]`
— deduplicated with order preserved. `"all"` stays a standalone,
acknowledgement-gated sentinel (it can expose write/shell tools without
HITL prompts) and is now rejected early, with a clear message, if placed
inside a list — at the CLI, `config.toml`, and agent-build layers.
## Notes
- `"safe"` now genuinely exposes `read_file`/`glob`/`grep` in server and
non-interactive mode (previously empty). The preset stays locked to
read-only, non-HITL-gated tools, enforced by a test against the live
interrupt map.
- Build-time "unknown tool name" errors are gone; a mistyped explicit
name now silently exposes nothing (logged at debug). Recovering that
feedback at the layer that can distinguish a typo from an absent tool —
plus an "expose-all" sentinel so `interpreter_ptc="all"` can include the
SDK built-ins — is tracked in #3847.
- `interpreter_ptc="all"` enumeration is unchanged from `main`: still
scoped to the tools passed to `create_cli_agent`, since the runtime
built-ins are not enumerable at build time (see #3847).
Made by [Open SWE](https://openswe.vercel.app)
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
The coding agent now sees the active LangSmith tracing project name(s)
in its context when tracing is enabled.
---
`LocalContextMiddleware` already injects environment context (git,
runtimes, MCP servers) into the system prompt, but it said nothing about
LangSmith tracing. `deepagents-code` deliberately routes its *own*
traces to a dedicated project (`get_langsmith_project_name()`) while
preserving the user's original `LANGSMITH_PROJECT` for shell commands it
runs — so the agent had no way to know where to look for its traces.
This adds a `**LangSmith Tracing**` section listing both project *names*
(no network/URL lookup) so the agent can fetch the relevant runs via the
LangSmith MCP server or CLI. The section is omitted entirely when
tracing is disabled, and the second line collapses when the two projects
are identical.
Made by [Open SWE](https://openswe.vercel.app)
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Undo earlier `_ToolExceptionRecoveryMiddleware`.
Recoverable MCP tool failures now stay inside the `StructuredTool` error
handler instead of relying on global agent middleware. This keeps MCP
`isError` results, transient retry failures, and re-auth signals visible
to the model as `ToolMessage(status="error")` while avoiding broad
agent-level `ToolException` recovery.
## Changes
- Removed `_ToolExceptionRecoveryMiddleware` from the main agent and
implicit subagent middleware stacks, so agent-level tool execution no
longer catches every `ToolException`.
- Wired cached MCP tools to use adapter-owned `_handle_mcp_tool_error`
directly through `_handle_cached_mcp_tool_error`, with warning logs and
fallback text for recoverable wrapper failures the adapter does not
format.
- Simplified cached MCP result conversion to call
`_convert_call_tool_result`, leaving MCP `isError=True` handling in the
tool-local `handle_tool_error` callback.
- Updated MCP session failure behavior so repeated transient failures,
generic wrapper failures, and re-auth signals return error-status
`ToolMessage`s when invoked as tool calls.
Cached-session MCP tools now match `langchain-mcp-adapters` 0.3 behavior
for MCP execution errors: `CallToolResult(isError=True)` is returned to
the model as a failed tool result instead of escaping as a raised
`ToolException`. Transport, session, retry, and reauth failures keep
their existing exception paths, while the generic recovery middleware
remains documented as a compatibility fallback for
non-MCP/tool-originated `ToolException`s.
Protect the machine-managed onboarding-name block in the user
`AGENTS.md` from being overwritten by agent file edits.
---
The onboarding flow writes the user's preferred name into the user
`AGENTS.md` inside a block delimited by `deepagents:onboarding-name`
HTML-comment markers. `MemoryMiddleware` strips HTML comments before
injecting memory, so the model never sees those markers — yet the same
prompt tells it to `edit_file` that file to persist learnings, leaving
nothing to stop it from rewriting the managed block.
This adds `ManagedMemoryGuardMiddleware`, a deterministic
`wrap_tool_call` guard wired into the agent's memory stack. When
`write_file`/`edit_file` targets the guarded user `AGENTS.md` and
changes (or drops) the managed block, the model's other edits are kept
but the block is restored to its prior content via the existing
`_upsert_onboarding_name_memory`, and an error is returned so the model
learns the region is machine-managed. Edits that leave the block
untouched, reads, and writes to other files pass through unchanged.
<details>
<summary>Scenario where this applies</summary>
A user has completed onboarding, so their preferred name is stored in
the machine-managed block inside their user `AGENTS.md`.
Later, the agent tries to persist a new memory by calling `edit_file` on
that file. `ManagedMemoryGuardMiddleware` intercepts the tool call to
ensure the onboarding-name block is not changed. If the agent's edit
touches or removes that block, the middleware preserves the agent's
other edits, restores the onboarding-name block to its previous content,
and returns an error telling the agent not to modify the machine-managed
region.
</details>
Made by [Open SWE](https://openswe.vercel.app)
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Dependency floors are refreshed across the monorepo for newer
LangChain/LangSmith releases, provider integrations, pytest tooling,
`ruff`, and `ty`. The code changes are the compatibility work needed for
those newer lint and type checks, with small structural cleanups where
the tools needed clearer narrowing or less complex functions.
## Changes
- Raised runtime and test dependency minimums across SDK, CLI, Code,
ACP, evals, examples, and partner packages, including updated
LangChain/LangSmith/provider pins and current lint/test tooling
baselines.
- Reworked backend factory typing around `BACKEND_TYPES` with
`_resolve_backend`, widened file transfer error fields to support
backend-specific strings, and updated filesystem/summarization
middleware to use the shared resolver.
- Tightened `deepagents-code` typing for middleware stacks, MCP tool
filtering, update checks, and Textual compatibility patches, replacing
stale suppressions with `ty`-specific ignores where runtime behavior is
intentionally narrower than static types.
- Split large CLI command handlers into focused helpers such as
`_dispatch_command`, `_execute_agents_command`, and the MCP server
subcommand helpers while preserving existing command behavior.
- Updated Harbor LangSmith sandbox handling to rely on explicit
`NetworkPolicy` and `capabilities` instead of legacy property overrides,
with tests adjusted around the new capability checks.
- Applied lint/format/type-check cleanups across tests and examples so
the staged code passes the refreshed tooling expectations.
When an MCP tool returns an error, `langchain-mcp-adapters` raises a
`ToolException`, and LangGraph's default tool-error handling re-raises
plain `ToolException` rather than converting it — so a recoverable
tool-level failure (e.g. `langsmith_fetch_runs` reporting `no project
found`) would crash the entire agent run. A new middleware catches these
and hands the model an error message it can work around.
## Changes
- Add `_ToolExceptionRecoveryMiddleware`, which catches `ToolException`
from tool execution and returns an error `ToolMessage`
(`status="error"`) so the run continues. Only `ToolException` is caught
— every other exception propagates so genuine bugs still surface.
- Wire the middleware onto both the main agent and CLI subagents, placed
innermost in each stack so it wraps tool execution as tightly as
possible and only intercepts the tool's own failures, not sibling
middleware (which return error messages rather than raise).
- Log recovered failures at `WARNING` with `exc_info=True` so the
traceback is preserved for debugging.
- Fall back to a tool-named message (`"<tool> failed with no error
detail"`) when the exception carries no text, instead of an opaque
exception class name.
Fixes#2316
---
Runtime `/model` overrides should apply to subagents that inherit the
CLI-selected model, but not to subagents that intentionally pin their
own model. This adds `ConfigurableModelMiddleware` only to subagents
whose `AGENTS.md` omits `model:`, including the default
`general-purpose` subagent.
Examples:
- No `model:` in a subagent config: after `/model
anthropic:claude-opus-4-6`, that subagent follows the main session and
uses Opus.
- `model: anthropic:claude-haiku-4-5` in a subagent config: after the
same `/model` switch, that subagent keeps using Haiku because the model
was explicitly pinned.
Made by [Open SWE](https://openswe.vercel.app)
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Normalizes empty `ls` and `glob` tool outputs in deepagents-code so
agents see `No files found` instead of raw `[]` while preserving errors
and non-empty results.
_Opened collaboratively by Mason Daugherty and open-swe._
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Co-authored-by: Mason Daugherty <61371264+mdrxy@users.noreply.github.com>
Co-authored-by: Mason Daugherty <mason@langchain.dev>
Co-authored-by: Mason Daugherty <github@mdrxy.com>
Resumed threads now carry the model they last used, so `dcode -r` can
continue the conversation with the same provider/model instead of
silently falling back to the current default. Explicit `--model`
selections still take precedence, and resume-driven switches stay
session-only so they do not rewrite the user's saved model preferences.
Pin every `deepagents-code` instantiation of `FilesystemBackend` to
`virtual_mode=False` so the CLI keeps its current real-filesystem
semantics when the SDK flips the default. Also silences the pending
`DeprecationWarning` the SDK now emits when `virtual_mode` is left
unset.