Related #4786
When `dcode` starts with MCP servers enabled, it loads the code used to
connect to those servers. Loading the optional authentication support
for the first time can perform filesystem work. Previously, that work
happened on the async server's event loop, where blocking calls are
rejected, so startup could fail with:
BlockingError: Blocking call to os.getcwd
`dcode` now loads MCP authentication support in a worker thread before
the server needs it. This keeps the potentially blocking work away from
the event loop so the runtime no longer rejects it during startup.
---
#4786 addressed the immediate failure by limiting `filelock` to versions
before 3.30. This follow-up hardens `dcode` against the broader problem
instead of relying only on one dependency version. The version limit
remains in place for now.
If the optional authentication support cannot be loaded, the failure is
still reported only for an MCP server that needs it; it does not prevent
unrelated or stdio MCP servers from loading.
A startup smoke test uses the same blocking-call detector as the runtime
to verify that first-time authentication loading happens in a worker
thread, so CI catches regressions.
Marketplace entries in the plugin manager now have spacing between them,
matching the plugins list.
---
The marketplaces tab rendered entries back-to-back, while the plugins
list (`_plugin_options`) inserts a disabled spacer `Option` between
rows. This applies the same disabled-spacer pattern between marketplace
rows so both surfaces are visually consistent. The spacers are disabled,
so keyboard navigation still skips over them.
Made by [Open
SWE](https://openswe.vercel.app/agents/045a43e2-f199-47e8-a7f0-ffb5dbff5762)
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Approval prompts no longer suggest enabling Auto mode when it is
unavailable (experimental opt-in off).
---
The HITL approval widget always offered "Enable Auto for this thread
(a)" even when Auto mode is ineligible (e.g.
`DEEPAGENTS_CODE_EXPERIMENTAL` is off or a sandbox is active), where
choosing it only surfaced the "Auto is available only in the opt-in
local TUI beta" warning and did nothing. `ApprovalMenu` now takes
`auto_mode_eligible` (passed from `App._auto_mode_eligible`) and omits
the Auto option when it can't be enabled, dynamically renumbering
options and quick keys. A live Auto fallback still shows its
Switch-to-Manual affordance.
Made by [Open
SWE](https://openswe.vercel.app/agents/6c5eb368-e615-f9d7-b8d0-bbce9ebaa3ff)
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Auto mode no longer raises `BlockingError` while resolving proposed file
paths on the LangGraph server event loop.
---
Auto mode still uses canonical, symlink-aware `Path.resolve()` before
deciding whether a write stays inside the worktree. The async planning
path now runs the complete deterministic policy check in a worker
thread, so current and future filesystem-backed policy checks cannot
block the event loop.
Regression coverage drives `awrap_model_call()` with the original
absolute `/tmp/langchain-groq-reasoning-model-pr.md` target and verifies
resolution happens off the event-loop thread. It also confirms routine
in-worktree writes remain deterministic allows and symlink escapes still
require classifier review.
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>
When quitting waits for unfinished work, `deepagents-code` now shows a
brief "Finishing pending work before exit…" toast so the pause reads as
intentional rather than a hang.
---
`DeepAgentsApp.exit()` only defers teardown when it must wait for an
in-flight agent worker or pending fire-and-forget hooks. That wait was
previously silent, so a user pressing quit could think the app had
frozen. This adds a single toast on exactly that deferred path.
The notification is emitted before scheduling the `_graceful_exit`
coroutine. Because scheduling returns control to the event loop and
teardown is deferred behind at least one `await`, Textual's message pump
gets an opportunity to render the toast before `super().exit()` runs.
Immediate/idle exits skip this branch entirely, no delay is introduced,
and the existing two-second grace period is untouched. A repeated/forced
exit hits the existing force-quit guard before reaching the notify, so
the toast is never duplicated (including when both wait conditions
apply).
Made by [Open
SWE](https://openswe.vercel.app/agents/26e6d24a-3180-4334-b655-4075767ae45b)
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
`dcode config list`/`dcode config ls` are removed; use `dcode config
show` (add `--verbose` to see all option keys).
---
`config list`/`config ls` duplicated `config show`, so this removes both
parser aliases and makes `config show` the sole command for displaying
effective config values and their sources. Drops `list_mode`, the
`config list` JSON label/catalog special-casing, backward-compat
comments, and now-dead paths; `config show --json` keeps effective-only
fields (catalog folded in only with `--verbose`) and always labels the
payload `"config show"`. User-facing suggestions now point at `config
show`/`config show --verbose`. Backward compatibility is intentionally
not preserved.
### Breaking change
`config list --json` was the machine-readable catalog endpoint (its JSON
envelope `command` field was `"config list"` and it always folded in
catalog fields). It is gone: the command no longer parses, and the
catalog fields are now available via `config show --verbose --json`
(payload labeled `"config show"`). Any machine consumer of `config list
--json` must migrate.
Made by [Open
SWE](https://openswe.vercel.app/agents/7351f382-9468-b4dc-2df4-03eaf75ef12f)
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Typing in the chat input is no longer blocked while the `/restart`
command respawns the server.
---
Running `/restart` froze the chat input until the server finished
respawning. `_handle_restart_command` awaited the multi-second
`_restart_server_manual` (`server_proc.restart()`) directly on the
Textual message pump, so key events stopped being forwarded and the chat
input was "blocked". This is the same class of bug fixed for the MCP
viewer `Ctrl+R` reconnect path in #4753 — the fix runs the respawn as a
detached `asyncio.create_task` (mirroring the MCP viewer/force-reconnect
paths), keeping the pump free so keystrokes stay live and queued
messages drain once `ServerReady` fires.
Made by [Open
SWE](https://openswe.vercel.app/agents/a27e2ef4-b392-c236-f410-c1b9ebc533aa)
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Editable installs conflated three distinct version facts, so `/version`,
`--version`, and `doctor` could silently report a single number that hid
stale metadata or a broken SDK dependency pin. Version diagnostics now
separate the live source version, the installed distribution metadata
version, and the `deepagents` requirement that `deepagents-code`
declares — reporting the source version as primary for editable
installs, showing the installed metadata alongside it when they drift,
and flagging an actionable mismatch when the installed SDK metadata does
not satisfy the declared requirement.
---
A new structured `DistributionVersion`/`VersionReport` representation in
`extras_info` is the single source of truth these surfaces render.
`resolve_sdk_version()` is preserved as a thin compatibility wrapper so
existing callers are untouched.
- `/version`, `--version`, and `doctor` render the same facts: an
editable SDK is now clearly identified (not just editable
`deepagents-code`), source/metadata drift shows both versions, and an
unsatisfied requirement (compared via `packaging.Requirement`) is
surfaced as `required by deepagents-code: <version> — mismatch`.
- `doctor` marks a dependency-requirement mismatch unhealthy, while
treating source/metadata drift as informational since it is normal while
editing SDK source.
- Normal non-editable output is unchanged when all versions agree, and
version collection stays fully offline and best-effort (no
`uv`/`pip`/PyPI, no subprocess).
- No package versions or dependency pins were changed.
Two existing `doctor` integration tests now isolate the SDK requirement
check so they stay deterministic regardless of the workspace's
intentional prerelease pin; the mismatch and drift paths have dedicated
new coverage.
Made by [Open
SWE](https://openswe.vercel.app/agents/1145d1a5-ba8a-abbb-e5b7-039e9c2a5b04)
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Fixed dcode TUI `task` elapsed timers resetting when a nested subagent
reached a human-in-the-loop approval checkpoint.
---
A long-running `task [general-purpose]` row's elapsed timer reset to
zero whenever a nested subagent hit a HITL approval checkpoint. The
interrupt handler paused/resumed *every* tracked tool row, and
`ToolCallMessage.pause_running()`/`set_running()` clear and reassign
`_start_time` — so an unrelated nested child's (or sibling subagent's)
approval restarted the outer `task` timers. This scopes pause/resume to
only the rows an interrupt actually owns (matched by tool name + args
via the new `_interrupt_owned_tool_rows` helper). Untracked nested-child
interrupts now own no outer row, so outer and sibling `task` timers stay
monotonic and completed `Took …` durations reflect full execution time.
A tool awaiting its own initial approval still pauses then resumes as
before.
Made by [Open
SWE](https://openswe.vercel.app/agents/5c0ae962-c16b-6e04-ada4-d4d76826adb9)
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
The `/version` command's update-available line hard-coded `uv tool
install -U deepagents-code`, which is stale guidance now that dcode
auto-updates on relaunch and offers `/update` and `dcode update`. It now
points at the relaunch/auto-update path when auto-update is enabled, and
at `/update` or `dcode update` otherwise.
---
Made by [Open
SWE](https://openswe.vercel.app/agents/a8b54b7c-cc2f-e6cc-a034-61a8578b8a04)
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Fixed `/goal` criteria being discarded when clearing the completed
criteria request from the thread failed.
---
Follow-up to #4784. When a YOLO `/goal` criteria run succeeds, cleanup
clears the `goal_criteria_request` marker before syncing but ignored a
failed clear (checkpoint write failure). A stale marker still naming the
just-completed request then made the sync
(`_sync_goal_rubric_state_from_thread`) and restore
(`_restore_goal_rubric_state`) paths strip the freshly generated
`_pending_goal_*` payload, so no review/YOLO acceptance mounted and the
criteria were silently lost. The marker now supersedes a proposal only
when it names a *different* request than the pending proposal's request
id.
Made by [Open
SWE](https://openswe.vercel.app/agents/6f2018c7-ffa3-ac12-4dab-8eed1142e4c4)
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Reduce noisy full-traceback DEBUG logs when an MCP server is skipped
because it requires authentication; the actionable warning and a concise
debug line remain.
---
When an MCP server is skipped during discovery because it needs
authentication — either a token-refresh (`reauth`) failure or an RFC
9728 `401` OAuth challenge — `deepagents-code` logged the entire anyio
`ExceptionGroup` / httpx traceback at DEBUG via `exc_info`, on top of
the actionable WARNING. For these already-classified, expected outcomes
the traceback adds no diagnostic value and just floods debug output.
These two recognized branches now emit a concise single-line DEBUG
breadcrumb (the exception class name only). The full traceback is still
logged for genuinely unknown discovery errors (the `else` branch) and
for classifier failures, so real anomalies stay debuggable. The
user-facing WARNING is unchanged apart from dropping the now-inaccurate
"the original error is in debug logs" note from the reauth message.
Made by [Open
SWE](https://openswe.vercel.app/agents/a5ab799b-1747-0d65-16b6-ee0ccccee0c4)
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
A long `task` tool description is rendered on a dim line truncated at
120 characters, but the full text was previously unreachable. This makes
the truncated preview expandable: clicking the description region or
pressing Ctrl+O toggles between the truncated and full description,
mirroring the collapsible command/code affordance on `execute`/`js_eval`
rows.
---
The `task` description now owns Ctrl+O when it is truncated (like an
expandable command/code block), while any expandable tool output stays
reachable by clicking its own region. A "click or Ctrl+O to
expand/collapse" hint is shown beside a truncated description.
<details>
<summary>Test plan</summary>
- Added `TestToolCallMessageTaskDescription` covering expandability
detection, the toggle swap, and click routing.
- Added an app-level Ctrl+O routing test that prefers a truncated `task`
description over expandable output.
- Updated existing Ctrl+O routing tests to account for the new
higher-priority branch.
</details>
Made by [Open
SWE](https://openswe.vercel.app/agents/f5d64d3e-e6e2-d1cf-f52c-0ecbd8829ef4)
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
In the installed plugin details view, the `Status: ✓ Enabled` line was
rendered `dim` like every other status line. This styles it with the
`$success` theme color so a successful load reads as green, matching the
semantic coloring used elsewhere in the TUI. Other status lines
(error/disabled/pending, and the secondary MCP restart note) stay `dim`.
Made by [Open
SWE](https://openswe.vercel.app/agents/d3174852-cfc9-43f5-f431-56993468deaf)
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Fixed the `dcode` chat input deleting a collapsed `[Pasted text #N]` (or
`[image N]`) placeholder when backspacing the line break on the line
below it; the line break is now removed while the placeholder is
preserved.
---
In the `dcode` chat input, pasting large text collapses it into a
`[Pasted text #N]` placeholder. Pressing Enter to add a newline and then
backspacing from the start of that new line deleted the entire
placeholder instead of just rejoining the lines.
The cause is in `_find_placeholder_span`: its backwards-delete branch
treats any whitespace immediately after a placeholder as an
auto-inserted trailing separator and swallows the whole token with it.
Since `str.isspace()` also matches a newline, backspacing a line break
below a placeholder removed the placeholder. Restricting that branch to
a literal space (the separator actually auto-inserted after media
placeholders) lets a newline fall through to the default backspace,
which removes only the line break and leaves the cursor at the end of
the placeholder line. The same fix is applied to the shared base text
area used by the inline prompts.
Made by [Open
SWE](https://openswe.vercel.app/agents/79b11ee1-f326-668a-e659-c990811f4e29)
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
`ls` and `write_todos` results in the `deepagents-code` TUI now align
flush under the output-gutter glyph, matching other tools, instead of
being indented an extra four spaces.
---
The `ls` and `write_todos` renderers in the terminal UI prefixed every
row with four hardcoded spaces, so their entries rendered at a 4-space
hanging indent under the `⎿` output-gutter glyph. Every other tool
formatter (`grep`, `glob`, `read_file`, `execute`) sits flush after that
gutter, and the gutter layout already owns alignment for soft-wrapped
lines — so the extra pad just produced an inconsistent gap between the
glyph and the first item. This emits bare rows for both formatters and
also drops the matching indent from the todo stats header and
empty-state, so the whole block stays flush and consistent. The todo
continuation-line hang-indent auto-adjusts since it derives from the
status-label width.
Made by [Open
SWE](https://openswe.vercel.app/agents/c5dec078-214f-79a6-2417-4b0014e0944e)
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Deep Agents Code now emits a `DEBUG` log when a higher-precedence skill
overrides another skill with the same name, aiding skill-resolution
debugging.
---
Skill sources (built-in, plugin, global, project) are merged by
precedence and a higher-precedence skill silently replaces a
lower-precedence one with the same name. This override is intentional
and unchanged; this PR makes it diagnosable by routing both
skill-discovery merge points — the CLI `skills list` loader
(`skills/load.py`) and the runtime `PluginSkillsMiddleware` — through a
shared `merge_skill` helper that emits a `DEBUG` log on each effective
replacement, recording the skill name plus the previous and replacement
source paths and labels.
Made by [Open
SWE](https://openswe.vercel.app/agents/5f97f80c-bed1-0fa5-d0b1-ce67060df37c)
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
When an Ollama daemon is down, discovery no longer probes it twice or
emits duplicate, out-of-order debug logs at startup — it probes and logs
"not detected" once per reload.
---
Ollama discovery runs from two startup callers — `get_available_models`
and `get_model_profiles` — that both call
`_get_ollama_installed_models`. Empty results were never cached, so an
unreachable daemon was probed twice per reload and logged `Ollama daemon
not detected ...` twice. Because the second `not detected` (from
`get_model_profiles`) landed right after the first pass's `discovery
returned no models` line, the two appeared out of order even though each
individual pass logs in the correct order.
This tracks endpoints that fail the TCP presence preflight in
`_ollama_unreachable_endpoints` and negatively-caches their empty
result, so the probe and log happen once. A *reachable* daemon that
simply reports no models is still left uncached, preserving recovery of
a later `ollama pull` without `/reload` (covered by the existing
`test_empty_installed_model_discovery_not_cached`).
Made by [Open
SWE](https://openswe.vercel.app/agents/7b64e449-2a7e-f258-17a1-5ffe933608a3)
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Fix the live tool-group progress line in `dcode` so it counts only the
tools still running instead of every tool in the step.
---
The collapsed tool-group summary's live progress line summarizes the
whole assistant step instead of what is actually still running. Because
it aggregates every tool in the group, finished calls keep being
reported as in progress — e.g. it shows `Running 1 shell command,
running 2 agents…` even after the shell command and one of the agents
have completed.
This builds the present-tense line from only the still-pending members,
so finished calls drop out as they complete. The past-tense line (shown
once everything finishes) continues to summarize every tool that ran.
The cached present phrasing now rebuilds whenever the set of running
members changes, not just when the group grows.
Made by [Open
SWE](https://openswe.vercel.app/agents/25ed04be-b8ed-97c9-a600-d24d80c02162)
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Clearing the Debug Console (`Ctrl+\`) traceback/log tail with `Ctrl+L`
now persists across closing and reopening the console for the process
lifetime, instead of re-populating from the in-memory buffer on the next
open.
---
The console modal is recreated fresh on every open and, on mount,
replays everything still retained in the always-on in-memory ring
buffer. `Ctrl+L` (`action_clear_view`) was deliberately a view-only
clear that advanced only the current instance's render cursor, so
closing and reopening replayed the buffer and the "cleared" records came
back.
This adds a process-lifetime "cleared upto" cursor owned by the app.
`DebugConsoleScreen` gains keyword-only `cleared_upto` (seeds its render
cursor so a prior clear is honored on open) and `on_clear` (reports the
new cursor when `Ctrl+L` fires). `DeepAgentsApp` stores the cursor in
`_debug_console_cleared_upto`, seeds each new console from it, and
persists updates via the callback.
The change stays strictly view-only: the shared ring buffer is never
mutated, other diagnostics are unaffected, and records logged *after* a
clear still appear. Clearing is by emission time, matching the existing
in-session semantics.
Made by [Open
SWE](https://openswe.vercel.app/agents/58c9cc66-afbe-b468-729d-375b731bbf71)
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
`/version` output (and incognito shell output) is now selectable and
copyable.
---
The output from the `/version` command could not be selected or copied.
Those tables — and incognito shell output — render through
`AppMessage(markdown=True)`, which produced a raw Rich renderable.
Textual only supports text selection over `Content`/`Text` visuals, so a
`RichVisual` carries none of the per-cell offset metadata selection
relies on and its text is neither highlightable nor copyable.
This renders the muted markdown to segments and rebuilds it as
`Content`, laid out to the widget's current width, so the
tables/rules/emphasis look the same but the text is now selectable and
copyable. Trailing padding is trimmed per line so copies stay clean.
Made by [Open
SWE](https://openswe.vercel.app/agents/f705adb2-3ebb-25a2-7ebd-ddce94887917)
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
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>
Fixes MCP OAuth token refresh crashing tool calls under `langgraph dev`
with a `NodeCancelledError`.
---
OAuth refresh previously performed token-file work synchronously.
BlockBuster could interrupt that work, cancel session initialization,
and leave its `AsyncExitStack` to be finalized from another task. Users
then saw errors such as:
Attempted to exit cancel scope in a different task than it was entered
in
Token-file I/O now runs in worker threads. Writes are serialized, token
and expiry values are read from one snapshot, and cancellation waits for
token writes and refresh-lock acquisition or release to finish before
propagating. Partial MCP sessions are closed by the task that created
them without hiding the original failure.
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Debug Console (`Ctrl+\`) thread ID is now click-to-copy and shows a
clickable `(langsmith)` trace link.
---
The in-app Debug Console (`Ctrl+\`) now lets you click the Thread ID
snapshot row to copy it to the clipboard, and appends a clickable inline
`(langsmith)` trace link next to it once the thread's LangSmith URL
resolves. The URL lookup runs in a background worker with a short
timeout (mirroring the welcome banner), so it never blocks opening the
overlay and degrades to no link on failure.
Made by [Open
SWE](https://openswe.vercel.app/agents/7f7451e4-5850-04d6-b76e-ef6dea6300a4)
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Auto-approve ("YOLO") runs are now labeled with
`dcode_auto_approve=True` in LangSmith trace metadata, making them
filterable.
---
Auto-approve ("YOLO") mode — the `Shift+Tab` toggle / `--auto-approve` —
sets `interrupt_on = {}` so tools run with no HITL gate, but nothing
recorded that state in LangSmith traces, so YOLO runs weren't
filterable. The `coding-agent-v1` contract's `approval_policy` key can't
help here: it's scoped to root/interrupted runs and would leak onto
every descendant run (and fail contract validation) if stamped
trace-wide.
This adds a non-contract diagnostic `dcode_auto_approve=True` metadata
key in `build_stream_config`, mirroring the existing
`dcode_experimental` pattern (only stamped when active). Interactive
turns pass `session_state.auto_approve`; headless turns pass the
resolved `use_auto_approve` (which reflects "tools run without HITL
because shell is unrestricted or disabled" — the same `auto_approve`
flag the graph consumes). Metadata is sampled once at turn start, so a
mid-turn toggle doesn't relabel that turn's trace.
New keyword-only `auto_approve` param defaults to `False`, so the change
is additive and backward-compatible.
Made by [Open
SWE](https://openswe.vercel.app/agents/31881cb9-2c33-7505-1b1a-fd453e84ea5a)
## References
- Plan:
https://openswe.vercel.app/agents/31881cb9-2c33-7505-1b1a-fd453e84ea5a/plan
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Make the Deep Agents Code unit tests hermetic so they no longer depend
on the developer's real environment, `~/.deepagents/.env`, or the
checkout-path length.
Ambient MCP trust vars
(`DEEPAGENTS_CODE_DANGEROUSLY_ENABLE_PROJECT_MCP_SERVERS` and friends),
`DEEPAGENTS_CODE_DEBUG_NOTIFICATIONS`, and
`DEEPAGENTS_CODE_EXPERIMENTAL` can be loaded from the developer's global
dotenv at `deepagents_code.config` import time — before fixtures run —
and `/reload` rereads that global dotenv and restores them. The
dangerous MCP allowlist intentionally replaces scoped TOML approvals
(breaking trust-list/selective-project-trust assertions), the debug var
changes notification suppression, and a restored experimental flag
breaks the `/reload` plugin-summary test. Separately,
`TestBranchDisplay::test_short_branch_name_not_truncated` let a long
real CWD consume the status bar, leaving the branch widget zero width.
Approach (test-only; no production behavior changes):
- New autouse fixture redirects `config._GLOBAL_DOTENV_PATH` to a
nonexistent `tmp_path` file so dotenv rereads (e.g. `/reload`) are
inert.
- New autouse fixture clears `DEEPAGENTS_CODE_DEBUG_NOTIFICATIONS`; the
existing MCP-trust fixture already removes the trust vars (docstring
expanded to explain why).
- The short-branch test now hides the CWD via `HIDE_CWD`, consistent
with neighboring branch-display tests, so path length can't starve the
widget.
Tests that intentionally exercise dotenv loading already set
`_GLOBAL_DOTENV_PATH` and their inputs explicitly in the test body
(after the isolation fixtures), so they are unaffected.
Made by [Open
SWE](https://openswe.vercel.app/agents/234e9ee9-f18a-e812-4187-aae8190e4711)
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
The splash tip above the chat input is now dismissed when the TUI is
launched with an initial prompt (`-m`), skill (`--skill`), or goal
(`--goal`), matching interactive submissions.
---
When launching the TUI with `dcode -m "msg"`, the splash tip above the
chat input stayed visible, unlike a normal interactive prompt which
clears it.
The tip was only dismissed inside `_submit_input`, the shared path for
interactive and external prompts. Startup submissions from `-m` (as well
as `--skill` and `--goal`) run through `_submit_initial_submission`
instead, which dispatches directly to `_handle_user_message` /
`_invoke_skill` / `_handle_goal_command` and never dismissed the tip.
This adds a `_dismiss_startup_tip()` call at the top of
`_submit_initial_submission` so all startup submission paths match
interactive behavior.
Made by [Open
SWE](https://openswe.vercel.app/agents/2d2a8fdc-c71c-58c6-287a-6d4109d24bbb)
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
## Description
Marketplace detail controls ignored Tab and Shift+Tab because those
bindings only attempted to switch tabs. Cycle enabled detail options
with wraparound while preserving existing list-tab and Escape behavior.
## Release Note
Marketplace detail controls now support visible, predictable Tab and
Shift+Tab keyboard navigation.
## Test Plan
- [x] Navigate marketplace details forward, backward, and through
wraparound with a Textual pilot test.
Made by [Open
SWE](https://openswe.vercel.app/agents/019f6783-f3db-727b-ad05-65934c0a9505)
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Closes
[DCD-49](https://linear.app/langchain/issue/DCD-49/plugins-fix-installed-component-discovery-and-status-messaging)
Plugin manager status and component messaging now reflect what
deepagents-code actually loads, and Enabled only appears after
`/reload`.
---
Plugins that only ship `agents/`/`commands/`/`hooks` (e.g.
`pr-review-toolkit`) now report those as unsupported instead of “no
components discovered.” Discover preview and installed status
distinguish pending `/reload`, enabled, and error.
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Mason Daugherty <github@mdrxy.com>
Co-authored-by: Mason Daugherty <mason@langchain.dev>
Closes
[DCD-48](https://linear.app/langchain/issue/DCD-48/plugins-improve-empty-marketplace-onboarding-and-list-polish)
`/plugins` empty Discover state now links straight to adding a
marketplace, with clearer marketplace error and enabled-status styling.
---
Make `/plugins` easier to onboard and a bit clearer to scan:
- empty Discover state offers a direct Add marketplace action
- marketplaces list gets a divider and styled load-error labels
- plugin rows use per-chip styling; enabled stays bold on highlight and
`$success` in details
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Mason Daugherty <github@mdrxy.com>
YOLO mode now automatically accepts successfully generated `/goal`
acceptance criteria, including amendments, regenerated proposals, and
proposals restored from a thread.
---
When someone creates, amends, or regenerates a `/goal` while YOLO mode
is enabled, dcode still generates acceptance criteria but now accepts a
successful proposal without interrupting the workflow with an
interactive review.
Automatic acceptance reads the live per-thread approval mode and follows
the same persistence and continuation path as selecting “Accept proposed
criteria.” Turning YOLO on also resolves a proposal already awaiting
review, while turning it off before generation finishes preserves the
manual review. Failed, cancelled, rejected, stale, and superseded
proposals remain fail-closed, and review widgets, futures, tasks, and
focus are cleaned up when automatic acceptance takes over.
Manual goal review and standalone `/rubric` behavior are unchanged. The
transcript explains when YOLO accepted criteria automatically, and
`/goal show` continues to expose the accepted objective and criteria.
Accepted `/goal` objectives now automatically transition to `complete`
when their current goal-backed work turn finishes with a satisfied
acceptance-criteria grade. Completed goals remain visible through `/goal
show` but no longer affect later ordinary turns.
---
An accepted `/goal` should finish when its own final rubric grade says
every criterion is satisfied. Previously the agent also had to call
`update_goal(status="complete", ...)`, manual mode requested another
approval, and successful completion removed the goal record entirely.
That made completion depend on approval mode and left `/goal show` with
nothing to report.
Completion now requires a validated grading event observed live during
the current goal-backed work turn, a matching grading-run identifier in
the checkpoint, and a turn that finishes without aborting. Criteria
generation, thread restoration, standalone `/rubric` grading, unrelated
or ungraded turns, stale persisted grader state, and unknown grader
verdicts cannot complete a goal. If a satisfied grade arrives before an
interrupted turn, the goal remains active and the UI explains that it
will be checked again on the next turn.
Completion is persisted transactionally; if that write fails, the active
goal and any agent-provided note remain available for a later retry.
Completed goals retain their objective, acceptance criteria, status, and
completion note while no longer driving subsequent work or grading.
Agents may still call `update_goal(status="complete", ...)` to provide a
completion note, but the call is optional. Manual and YOLO modes use the
same completion path without a completion approval prompt.
`Ctrl+X` now opens the active `/goal` criteria or rejection-feedback
field in the configured external editor.
---
When a user was editing proposed goal criteria or entering regeneration
feedback, `Ctrl+X` still opened the unrelated chat draft because the
priority app binding always targeted the composer. This moved focus out
of the review and made external-editor composition unusable in both
goal-review modes.
`Ctrl+X` now detects the active goal-review field and opens its complete
logical value, including expanded collapsed-paste content, in `$VISUAL`
or `$EDITOR`. Saved text returns to the same field without submitting;
cancellation or failure preserves the existing value and focus. Hidden,
stale, detached, or unfocused goal editors fall through to the unchanged
chat-input behavior.
`/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.
Deleting a thread now also removes its offloaded conversation history
from `~/.deepagents`.
---
Deleting a thread through the `/threads` switcher (and the `threads
delete` CLI) only removed the thread's SQLite checkpoints, leaving its
offloaded conversation-history archive orphaned under
`~/.deepagents/conversation_history/{thread_id}.md`. Over time these
files accumulate for threads the user believes are gone.
`delete_thread` now also removes the per-thread archive via a new
`delete_offloaded_history` helper in `offload.py`, which resolves the
same offload root used when writing history. The helper guards against a
crafted `thread_id` escaping the archive directory and is a no-op when
no archive exists (e.g. server/sandbox mode, where history lives on the
sandbox backend).
Made by [Open
SWE](https://openswe.vercel.app/agents/1ca83aca-3d44-5887-c9c2-3ea96c440136)
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Fixed the `/mcp` viewer Ctrl+R reconnect blocking keyboard input until
the server finished reconnecting.
---
Pressing Ctrl+R inside the `/mcp` viewer to reconnect froze the chat
input until the reconnect finished. The handler scheduled
`_reconnect_from_viewer_safe` with `call_later`, which awaits the
coroutine inside the Textual app message pump; the multi-second
`server_proc.restart()` then stalled the pump so key events were no
longer forwarded. Running the reconnect as a detached
`asyncio.create_task` (mirroring the force-reconnect confirm path) keeps
the pump free, so input stays responsive and typed messages queue and
drain once the server is ready.
Made by [Open
SWE](https://openswe.vercel.app/agents/b1d98f23-e279-dc41-4f4f-ce0818900274)
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
`test_cli_agent_summarizes` exercises the production local
conversation-history backend, whose fallback location is the user's
`~/.deepagents` directory. The test's generated 720 KB payload therefore
survived each run and polluted real user state.
Patch the test helper's offload root to a directory under `tmp_path` and
assert that the archive is created there. Pytest now removes the
artifact with the rest of the test directory without changing production
persistence behavior.
Verified with the full `test_end_to_end.py` unit test file, Ruff, and a
before/after comparison confirming the real conversation-history
directory remains unchanged.
Updates the deepagents-code sandbox integration suite to match the
current `write()` contract: writing to an existing file replaces its
contents rather than returning an error. This keeps the Python test
expectation aligned with the overwrite behavior covered by the
corresponding deepagentsjs standard tests.
## Changes
- Rename the existing-file write test to describe overwrite behavior.
- Assert that the second write succeeds and that the replacement content
is persisted.
- Update the sandbox operations module overview to include overwriting
existing files.
`dcode` now surfaces the tool calls that require approval instead of
hiding them in a collapsed group.
---
Approval-blocked tool calls could remain hidden inside the collapsed
live group, leaving only a generic batch prompt. Finalize the active
group at the approval boundary and detach pending rows while preserving
single-command duplicate suppression.
Made by [Open
SWE](https://openswe.vercel.app/agents/3f2b94f6-67ee-d5d4-d2a5-250adeacac5e)
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
The project MCP server approval prompt now offers an "always allow"
option that saves approved servers to `config.toml`, with an
all/custom/none picker so you choose exactly which servers to persist.
---
The project-level MCP server approval prompt (`dcode` startup) only
offered per-session, fingerprint-based trust. This adds an "always
allow" choice (`a`/`always`) that persists approved servers to
`[mcp].enabled_project_servers` in the user-level `config.toml`, so they
load by name on future runs without re-prompting — even if the config
content changes.
Rather than presuming the whole list, "always allow" now lets you choose
*which* servers to save via an `[a]ll / [c]ustom / [n]one` menu.
`custom` reprints the servers numbered and accepts a `1,3`-style
selection; a single prompted server skips the menu. The writer only ever
touches the user's home `config.toml` (never a repo file), preserving
the boundary that a committed `.mcp.json` can't self-approve.
---
## ⚠️ Breaking change (migration)
This branch evolved past the original design above. As shipped, "always
allow" persists **fingerprint-scoped** approvals under
`[mcp].enabled_project_server_approvals` (bound to project root +
server-definition fingerprint, so a changed command/URL re-prompts) via
an arrow-key checkbox picker — not the flat, name-based
`[mcp].enabled_project_servers` list or the `all/custom/none` text menu
described above.
Consequences for anyone who adopted the previous per-server trust
feature (`[mcp].enabled_project_servers`, shipped in `0.1.33`/`0.1.34`):
- The legacy `[mcp].enabled_project_servers` TOML key is now **ignored**
(a debug-only log warns; the key is preserved on write, not deleted).
- The env override `DEEPAGENTS_CODE_ENABLED_PROJECT_MCP_SERVERS` is
renamed to `DEEPAGENTS_CODE_DANGEROUSLY_ENABLE_PROJECT_MCP_SERVERS`
(still name-based and project-agnostic — the explicit escape hatch).
- **Interactive `dcode`:** affected servers move back to the approval
prompt; re-approve once with `a`. Safe, mildly annoying.
- **Non-interactive / `dcode mcp login`:** affected servers silently
stop loading (no prompt in these surfaces). Re-run `dcode` in the
project to persist a scoped approval, or set
`DEEPAGENTS_CODE_DANGEROUSLY_ENABLE_PROJECT_MCP_SERVERS` to keep the old
name-based behavior.
This is fail-closed (no server loads with *less* scrutiny than before),
so it is a safety-preserving break.
Made by [Open
SWE](https://openswe.vercel.app/agents/6cb9d698-5a31-5568-306a-9ff7a3f4356b)
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Closes#4524
`dcode` updates that require a prerelease SDK dependency no longer allow
unrelated optional providers to install prerelease versions, avoiding
failed upgrades from unbuildable release candidates.
---
Stable `deepagents-code` releases can intentionally require an exact
prerelease hard dependency (e.g. `deepagents==0.7.0a7`). The updater
handled this by adding a global `--prerelease allow`, which widens the
resolver's candidate set for *every* package — so an unrelated optional
provider could float to an unbuildable prerelease. That is what caused
#4524 (a `litellm` RC failing to build on Python 3.14).
`perform_upgrade()` now identifies the mandatory, unconditional, exact
prerelease pins from the target release's PyPI metadata, writes them to
a temporary uv constraints file, and runs with `--constraints <file>
--prerelease if-necessary-or-explicit`. uv then admits only the named
prerelease (e.g. the SDK alpha) while unrelated optional dependencies
stay on their stable releases. The exact dcode target pin, installed
extras, receipt `--with` packages, interpreter selection, and
transactional uv-tool behavior are all preserved. The explicit dcode
prerelease-*channel* path (installed prerelease or `--prerelease`) is
unchanged and still uses `--prerelease allow`.
The marker-agnostic boolean cache (`release_requires_prereleases`) is
redesigned into a structured, versioned per-version pin cache
(`release_prerelease_pins`); the legacy boolean key is never read as
authoritative. `release_requires_prereleases()` is kept as a
backward-compatible boolean wrapper for the display-only callers in
`app.py`/`main.py`. The requirement classifier is deliberately limited
to unconditional exact pins — marker-bearing pins (extra-gated or
interpreter/platform-gated) are ignored rather than evaluated against a
possibly-wrong interpreter; this limit is documented in code and tests.
Constraint-file lifecycle is a context manager: the file lives for the
full subprocess invocation and is removed in `finally`, tolerating
cleanup errors without masking the install result. A
constraint-generation failure leaves the existing install untouched and
returns actionable output rather than silently reverting to a global
allow.
Audit of other receipt-aware/global-allow paths (scoped follow-ups, not
fixed here to avoid broad refactoring and offline-install regressions):
- `install_extra` / `install_package` build commands unconditionally use
`--prerelease allow`; they pin the current version, so converting them
safely needs the same targeted-constraint plumbing plus offline-fallback
handling.
- `dependency_refresh` only adds `--prerelease allow` when following the
prerelease channel, so it does not exhibit the metadata-driven
global-allow bug for stable installs (it fails safe instead).
- `scripts/install.sh` still defaults `DEEPAGENTS_CODE_PRERELEASE=allow`
for fresh installs; porting the targeted strategy to bash is a larger
installer change with its own tests.
Made by [Open
SWE](https://openswe.vercel.app/agents/90a3f170-5a1a-ed8b-3c37-d97d6021c97d)
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
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>
Pasting large multi-line text into the chat input now shows a toast
noting the paste was collapsed and can be pasted again to expand it
inline.
---
Pasting a large, multi-line clipboard payload into the chat input
collapses it into a compact `[Pasted text #N]` placeholder. That
behavior is discoverable only if you already know that pasting the
identical content a second time expands it inline. This surfaces a short
toast at the moment of collapse so the affordance is visible.
The notification fires only when a *new* placeholder is inserted —
repeat-paste expansion of an existing placeholder stays silent — and it
routes through the single collapse funnel
(`ChatInput._collapse_and_insert_paste`), so bracketed pastes, paste
bursts, and app-level paste routing all get it. The message is a static
literal sent with `markup=False`.
Made by [Open
SWE](https://openswe.vercel.app/agents/100a1495-af57-cbd8-abbb-5f682db12646)
---------
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>