The ChatGPT Codex backend and the OpenAI API both report an exhausted
usage allowance as HTTP 429, with a body distinguishing it from an
ordinary short-lived rate limit (usage_limit_reached, usage_not_included,
insufficient_quota). from_http_status previously mapped every 429 to
RateLimitExceeded, so the agent retried requests that could not succeed
until the usage window reset, then surfaced a generic rate limit message.
Parse those bodies into a new UsageLimitReached completion error carrying
the provider's message and the reset delay, skip automatic retries for it,
and render a dedicated callout in the agent panel.
## Objective
`file_scan_exclusions` replaces the defaults instead of adding to them,
so excluding one extra directory means restating all eleven default
globs and never picking up defaults added in later Zed releases.
## Solution
`file_scan_exclusions` now accepts the `"..."` entry, which expands to
the value it overrides, so `["**/node_modules", "..."]` adds to the
inherited globs instead of replacing them. Entries listed by name keep
their position, and leaving `"..."` out still replaces the list
outright, so existing settings behave exactly as they do today.
## Testing
- Four unit tests in `crates/settings_content/src/project.rs` cover
splicing versus replacing, accumulation across successive layers, and
edge cases: a repeated `"..."`, an empty list clearing the value, and a
bare `["..."]` leaving it unchanged.
- To check by hand: set `"file_scan_exclusions": ["**/node_modules",
"..."]` in user settings and confirm `node_modules` disappears from the
project panel and file finder while `.git` and `.DS_Store` stay
excluded. Remove `"..."` and confirm only `node_modules` is excluded.
Repeat in a project's `.zed/settings.json` to confirm it splices the
resolved user settings rather than the defaults.
- Tested on macOS. This is platform-independent settings-merge logic
with no OS-specific code paths, so I did not test Linux or Windows.
## Self-Review Checklist:
- [x] I’ve reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed’s UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
---
Release Notes:
- Added support for the `"..."` entry in `file_scan_exclusions`. Custom
exclusions can now extend the defaults instead of replacing them.
## Summary
Adds a first-party `ask_user` tool to Zed's native agent
(`crates/agent`) that lets the agent ask the user a question and get an
answer back through a proper **elicitation form** rather than free-form
chat text.
This is a step towards the interactive-question experience requested in
[discussion
#48784](https://github.com/zed-industries/zed/discussions/48784)
(implement the ACP elicitation spec for agent questions). Zed already
renders elicitation forms client-side; this wires the native agent up to
that existing rail so the agent can drive single-select and/or free-text
prompts.
## What it does
The tool takes:
- `question` — the prompt shown to the user
- `options` — optional list of selectable choices (single-select)
- `allow_free_text` — whether the user may type a custom answer instead
of picking an option
The agent decides the shape of each question:
| `options` | `allow_free_text` | Result |
| --- | --- | --- |
| 2+ | `false` | Single-select only |
| 2+ | `true` | Single-select with a free-text "other" field |
| empty | `true` | Free-text only |
A typed answer takes precedence over a selected option, and the tool
always resolves (a cancelled or failed elicitation is reported back to
the agent rather than hanging).
## Demo
https://github.com/user-attachments/assets/9d438d87-af65-4fa2-b500-b1232007d0a0
## Implementation
- `thread.rs` — new `ThreadEvent::Elicitation(ElicitationRequest)`
variant plus `ToolCallEventStream::request_elicitation`, mirroring the
existing `prompt_for_decision` permission rail.
- `agent.rs` — handles the elicitation event by building a
session-scoped `acp::CreateElicitationRequest` and routing it through
`AcpThread::request_elicitation`, piping the response back to the tool.
- `tools/ask_user_tool.rs` — the tool itself, including schema
construction and response extraction.
- Registered in the `write` and `ask` profiles and excluded from the
tool-permissions setup UI.
No client-side UI changes were needed — the existing elicitation form
rendering handles it.
## Tests
`cargo test -p agent ask_user` — 5 unit tests covering schema
construction, validation, and accept/decline/cancel handling.
Release Notes:
- Added an `ask_user` tool that lets the agent ask the user a question
with selectable options and/or free-text input, rendered as an
elicitation form
# Objective
This PR aims to fix these related deficiencies with the terminal tool's
working directory path resolution:
- Fix#60014
- Fix#60040
- Fix#60043
## Solution
- Use `project.path_style(cx).is_absolute(dir)` instead of
`Path::is_absolute`. The latter follows the path semantics of the host
which fails to recognize `/home/...` etc as an absolute path on Windows,
we want to follow the path semantics of the *project*.
- Splits out path resolution into a separate pure function
`resolve_cd_in_worktrees` to enable comprehensive cross-PathStyle
testing
- The function unifies path prefix checking across both absolute and
relative modes, using the `path_style.strip_prefix` (which uses the
RelPath util internally) which fails when subdirs try to escape with
`..`.
- Add comprehensive tests for absolute/relative modes, path styles, and
potential `..` escapes
- Enables targeting subdirectories with both absolute and relative path
modes (separate commit)
- Also makes both path modes use `project.worktrees`. Before, the
relative path mode used `project.visible_worktrees`.
## Testing
Here's how I tested these changes:
- Added a new test: `test_resolve_cd_uses_project_path_style` with a
comprehensive collection of assertions for `..` edge cases
- Run `cargo test -p agent` and `./script/clippy -p agent` on:
- Linux
- Windows in Ubuntu WSL
- Windows native
- Build and run Zed on:
- Linux
- Windows
- Verify that the three issues are fixed:
- `cd` param path traversal escapes are blocked
- Linux local project
- Windows local project
- Windows remote WSL project
- Allows absolute paths when running on Windows in a WSL project
- `cd` param may target subdirectories in both absolute and relative
path modes
- Linux local project
- Windows local project
- Windows WSL project
- Check that different models find the updated tool descriptions to be
understandable and usable the first time. Used the prompt below to get
the following models to test path resolution on each of: Windows local,
Windows WSL, Linux. All models except GPT-5-nano one-shot the below
prompt.
- Claude Sonnet 4.6
- Claude Opus 4.8
- Claude Haiku 4.5
- GPT-5.5 pro
- GPT-5-nano - got there eventually but is confused by the design of the
cd param, it expects that if you set cd to `my-project` that it will
target the directory `my-project/my-project`.
- Gemini 3.1 Pro
- Gemini 3.5 Flash
- Grok 4.3
- Grok 0.1 build
<details>
<summary>Agent test prompt</summary>
<blockquote>
List the current directory contents and pick one subdir.
Then use that information and the project path details you were provided
to execute `pwd` with the terminal tool's cd parameter set to the
following values:
- current project absolute path
- current project name
- current project absolute path + an existing subdir
- current project name + an existing subdir
- absolute path to user's .ssh dir
- current project absolute path + whatever necessary path traversal .. +
.ssh segments to target the user's .ssh dir
Then tell me what happened.
Then stop.
</blockquote>
</details>
## Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
---
Release Notes:
- Fixed terminal tool targeting absolute directories on Windows host
connected to a remote SSH / WSL project
- Fixed terminal tool not blocking path traversal escapes
- Improved terminal tool targeting project subdirectories
---------
Co-authored-by: Richard Feldman <richard@zed.dev>
Co-authored-by: Richard Feldman <oss@rtfeldman.com>
There is a bug on main and preview causes all terminal tool calls to
fail when:
- on windows
- sandboxing is not available (i.e. no WSL)
- sandboxing is enabled in settings (the default)
- the "warn windows-drive grants" setting is enabled (the default)
Example error:
<img width="352" height="79" alt="image"
src="https://github.com/user-attachments/assets/24affbec-f086-40eb-8827-ad74aabc6167"
/>
This PR fixes it by making sure we only show the check at the right time
---
Release Notes:
- N/A or Added/Fixed/Improved ...
# Objective
Instruct agents not to create persistent sibling threads without user
approval.
## Solution
Update the `create_thread` instructions to require an explicit request
or approval. Agents may still suggest creating a thread.
## Self-Review Checklist:
- [x] I’ve reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed’s UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [ ] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
---
Release Notes:
- N/A
Fixes the bug that made us remove the sandbox.
The bug in question was very dumb:
- there is sophisticated machinery for detecting whether a user-granted
writable path is swapped out for a symlink in the timing gap between
approval and sandbox creation
- there was no equivalent machinery to do the same for the (much larger)
gap between a user *persisting an approval* (either for the current
thread or permanently via settings)
- The fix is essentially to store canonical (i.e. absolute and
symlink-free at all depths) paths as the source of truth, but retain the
raw path for display purposes
- On WSL, there is extra care needed becasue of the bidirectional
mounting (i.e. `/mnt/c/...` and `\\wsl.localhost\Ubuntu\...`). In
particular, `/mnt/c/...` paths, since their inodes do not necessarily
pin NTFS file references, weaken the sandbox guarantees, and so we need
some extra UI to call this out and docs etc...
This also does not remove the feature flag, but just toggles it to
"enabled_for_all"
---
Release Notes:
- N/A or Added/Fixed/Improved ...
---------
Co-authored-by: Richard Feldman <oss@rtfeldman.com>
Co-authored-by: Jakub Konka <kubkon@jakubkonka.com>
Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>
# Objective
- Treat a follow-up sent during pending tool approval as a denial the
agent can understand.
## Solution
- Preserve the follow-up interruption through permission handling and
return a specific denial result to the model.
## Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
---
Release Notes:
- Agent: Fixed agents not recognizing follow-up messages as denial of
pending tool calls.
# Objective
Currently, when a model provides the wrong parameters to the edit file
tool, it gets back the opaque error message `data did not match any
variant of untagged enum ValueOrJsonString`.
At least some models (DeepSeek in my experience) can't figure out what
to do from this point, and keep trying the same wrong parameter names in
the tool call before eventually falling back to using `ed`.
This may address https://github.com/zed-industries/zed/issues/58622.
## Solution
Rather than attempting to deserialize to the `ValueOrJsonString` enum,
try to deserialize as a JSON string and then as a value in turn, so that
deserialization errors better propagate.
## Testing
Using DeepSeek V4 Pro.
Tried it out in a reloaded conversation where the edit tool was
consistently failing.
Asked it to stress-test the edit tool. Only one edit failed after a few
successful attempts, and returned the error message "missing field
`new_text`".
Subsequent edits worked again rather than resulting in a doom loop. I
don't think the model noticed what happened, but it seemed to help.
<img width="366" height="551" alt="image"
src="https://github.com/user-attachments/assets/6f9c0c8d-bf94-4670-bad0-0ead5c1fff4e"
/>
...
<img width="346" height="153" alt="image"
src="https://github.com/user-attachments/assets/2afa2901-9a90-4142-aa9d-ff32a7be0d0c"
/>
---
Release Notes:
- Improved edit_file tool error messaging.
---------
Co-authored-by: MrSubidubi <finn@zed.dev>
# Objective
Fix `spawn_agent` calls that provide an empty or whitespace-only
`session_id`.
Currently, an empty string is deserialized as a real session id, so the
tool attempts to resume a session with an empty id instead of starting a
new subagent session. This can happen when callers serialize an optional
`session_id` field as `""`.
Related: Refs #55987. This addresses one concrete session-id failure
mode described there, but that issue also covers broader
OpenAI-compatible provider/subagent reliability concerns.
## Solution
- Add custom deserialization for `SpawnAgentToolInput.session_id`.
- Treat omitted, `null`, empty, and whitespace-only values as `None`.
- Preserve existing behavior for non-empty `session_id` values, so
follow-up calls still resume an existing subagent session.
- Clarify the field docs.
- Add a regression test covering omitted, `null`, empty,
whitespace-only, and non-empty values.
## Testing
Tested locally on macOS:
- `RUSTUP_TOOLCHAIN=1.95.0 cargo fmt --manifest-path zed/Cargo.toml
--package agent --check`
- `RUSTUP_TOOLCHAIN=1.95.0 cargo test --manifest-path zed/Cargo.toml -p
agent -p gpui_macos --features gpui_macos/runtime_shaders
deserializes_blank_session_id_as_absent`
- `RUSTUP_TOOLCHAIN=1.95.0 cargo build --manifest-path zed/Cargo.toml -p
zed -p gpui_platform --features gpui_platform/runtime_shaders`
- `RUSTUP_TOOLCHAIN=1.95.0 cargo clippy --manifest-path zed/Cargo.toml
-p agent -p gpui_macos --features gpui_macos/runtime_shaders --tests --
-D warnings`
The local build uses runtime shaders because this machine does not have
the full Xcode Metal CLI toolchain available. CI should cover the
standard build matrix.
## Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
---
Release Notes:
- Fixed `spawn_agent` handling so blank `session_id` values are treated
as absent and start a new subagent session.
---------
Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
Co-authored-by: Bennet Bo Fenner <bennet@zed.dev>
# Objective
Add a new `agent.compaction_model` setting that lets users specify a
separate language model for context compaction (`/compact` and
auto-compaction), independent of the thread's active conversation model.
Compaction is just summarization — there's no reason to pay Opus prices
for it when a cheaper model does the job faster. We've also seen
reasoning models misbehave on this task (empty responses, repetition
loops at high effort), so picking a dedicated non-reasoning model for
compaction is useful.
## Solution
- New `compaction_model: Option<LanguageModelSelection>` field in
`AgentSettingsContent` and `AgentSettings`, mirroring
`thread_summary_model`.
- New `compaction_model: Option<ConfiguredModel>` slot on
`LanguageModelRegistry` with `select_/set_/compaction_model()` trio,
mirroring the existing pattern. The setter deliberately does **not**
emit a registry event in v1; callers read the slot lazily at compaction
time.
- New `Thread::compaction_model(&self, cx: &App)` helper that returns
the configured model or falls back to `self.model()`. Two call sites —
`Thread::compact` and `perform_compaction_if_needed` — now go through
this helper instead of reading `self.model()` directly.
- `build_compaction_telemetry` accepts the compaction model explicitly
so the `model` field reflects the model that actually streamed the
request. `max_tokens` still derives from `thread.model()` (threshold
semantics are unchanged).
- Documentation updated at `docs/src/ai/agent-settings.md` (new
user-facing setting).
**Example:**
```json
{
"agent": {
"default_model": {
"provider": "anthropic",
"model": "claude-opus-4-6"
},
"compaction_model": {
"provider": "anthropic",
"model": "claude-sonnet-4-5"
}
}
}
```
**Resolution chain:**
```
agent.compaction_model (if set & available)
→ thread.model() (always available, current behavior)
```
**Behavior change:**
| Trigger | Before | After |
| --- | --- | --- |
| Manual `/compact` | Uses `thread.model()` | Uses
`agent.compaction_model` if set & available; else `thread.model()` |
| Auto-compaction | Uses `thread.model()` | Same as above |
| `/compact` when thread has no model | `NoModelConfiguredError` |
Succeeds if `compaction_model` resolves |
| `compaction_model` configured but provider missing / model id unknown
| n/a | Falls back to `thread.model()` and logs a one-time warning |
**Explicit non-goals:**
- No `Event::CompactionModelChanged`(no consumer; the `_cx` parameter on
`set_compaction_model` is intentionally accepted for future use).
- No GUI selector (consistent with all other feature-specific models).
- No per-profile override.
- No runtime API-error fallback — only config-time failure (provider not
registered, model id not in `provided_models`) triggers fallback. This
matches every other feature-specific model.
- No change to threshold calculation, auto-compact trigger, or
`COMPACTION_PROMPT`.
- No propagation to subagent threads.
## Testing
3 new unit tests in `crates/agent/src/thread.rs::tests`:
- `test_compaction_uses_configured_compaction_model` — manual `/compact`
routes to the configured model; thread's primary model receives no
request; telemetry reflects the configured model.
- `test_compaction_falls_back_when_compaction_model_unavailable` —
configured-but-unresolvable falls back to `thread.model()`; telemetry
reflects the fallback model.
- `test_auto_compaction_uses_compaction_model` — auto-compaction
triggered by threshold honors the same setting.
All 14 existing compaction tests still pass. Test suites in
`crates/agent_settings`, `crates/language_model`,
`crates/settings_content`, `crates/agent_ui` unchanged. `cargo clippy`
clean on the changed crates.
**How reviewers can test:**
1. Add `agent.compaction_model` to `settings.json` with a cheaper model
than the thread's primary model, run `/compact`, observe the cheaper
model receives the request.
2. Set `agent.compaction_model` to a non-existent provider/model id, run
`/compact`, observe fallback to thread model and a `log::warn!` line.
3. Trigger auto-compaction by reaching the threshold, observe it uses
`compaction_model`.
**Platforms tested:** local Linux (cargo check + cargo test on agent /
agent_settings / language_model / settings_content / agent_ui crates).
## Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments — no `unsafe`
blocks introduced
- [x] The content adheres to Zed's UI standards — N/A: settings-only
change, no UI touched
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable — model
resolution is an O(1) registry lookup, not on any hot path; no new work
in the streaming loop
Release Notes:
- agent: Add support for specifying which model is used for compaction
(`agent.compaction_model`)
---------
Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
Co-authored-by: Bennet Bo Fenner <bennet@zed.dev>
## Problem
AWS Bedrock's Mantle endpoint (used for GPT-5.x models via the
OpenAI-compatible Responses API) has two independent bugs that both
cause visible duplication/corruption in the agent panel:
1. **Duplicated message text.** Mantle streams a reply as a series of
new `message` output items, where each new item's text is a growing
prefix-superset of the previous one, and the full `output_text.delta`
sequence is replayed for each item. Forwarding these events through the
shared `OpenAiResponseEventMapper` unmodified caused the assistant's
reply (and its phase/reasoning metadata) to appear duplicated in the UI.
2. **Colliding tool call ids.** Mantle assigns raw `tool_use` ids as a
simple per-request counter (`call_1`, `call_2`, ...) that resets to
`call_1` on every request/response cycle to the model — including
separate cycles within a single agent turn as it performs tool calls
sequentially. Since `acp::ToolCallId` was built directly from that raw
id, and `AcpThread::upsert_tool_call` matches tool calls by id across a
thread's entire entry list, a later, unrelated tool call reusing a raw
id would silently overwrite an earlier, already-completed one instead of
appearing as a new entry — visible as "old tool calls getting replaced
by new ones."
Both were confirmed against real raw Mantle SSE traces and a real
persisted thread (`call_1` recurring across 10+ separate request cycles
within a single turn).
## Fix
**For message duplication:** `MantleResponseEventMapper` wraps
`OpenAiResponseEventMapper` and collapses adjacent, same-phase,
strict-prefix message extensions into a single logical message,
forwarding only the replayed suffix as ordinary incremental text.
- Only adjacent, same-phase, strict-prefix extensions are collapsed.
Equal, shrinking, divergent, or different-phase messages are still shown
as-is.
- Any non-message output item (reasoning, tool call) is a hard boundary:
it's forwarded unchanged and clears the merge state, so a message after
a tool call is never merged with the one before it.
- When a message is merged into the previous one, its `StartMessage` and
`ReasoningDetails` metadata events are suppressed, since they'd
otherwise duplicate what was already emitted for the first message.
**For tool call id collisions:** `scoped_tool_call_id(message_ix,
tool_use_id)` combines the raw provider id with the index of the
`AgentMessage` it belongs to in `Thread::messages`. That index is stable
for the lifetime of one request/response cycle (`flush_pending_message`
only runs on the next `StartMessage` event), so ids stay unique across
the whole thread without needing the raw provider id itself to be
globally unique. The index is threaded through explicitly from where
each tool call is created (`handle_tool_use_event`/`run_tool`) to where
it's later referenced (`process_tool_result`), rather than recomputed
later by re-reading `Thread::messages.len()`.
We deliberately did not rewrite the raw `tool_use` id itself at the
provider layer to make it globally unique, since that id round-trips
back to Mantle verbatim as `call_id` in `function_call_output` items on
the next request — rewriting it would require a fragile reverse-mapping
on every code path that replays tool results.
## Testing
- `cargo test -p language_models --lib bedrock` — 23/23 pass (11 tests
covering coalescing, independent messages, phase boundaries,
tool/reasoning boundaries, chained cumulative extensions, and metadata
deduplication).
- `cargo test -p agent --lib` — 699/699 pass (2 new regression tests for
tool call id scoping: one via live streaming across two request cycles
in one turn, one via persistence/`Thread::replay()` across two separate
turns).
- `script/clippy -p language_models -p agent` — clean.
Release Notes:
- Fixed duplicated assistant replies and colliding tool calls when using
GPT-5.x models on Amazon Bedrock's Mantle endpoint
Fixes the `edit_file` tool failing to resolve `old_text` fragments that
only cover part of a longer line. The streaming matcher compared the
query against whole trimmed buffer lines, so an exact substring like
`keyboard WASD, voxel-based` inside a much longer line failed the fuzzy
similarity threshold and the edit was rejected with "did not match any
content".
Supersedes #60615 — thanks to @ziimakc for the report, reproduction, and
initial investigation there. This PR takes a different approach based on
review feedback on that PR.
## Approach
- `StreamingFuzzyMatcher::push()` is unchanged: it still fuzzy-matches
completed lines incrementally so the UI can reveal the likely edit
location while `old_text` streams in.
- `finish()` now first searches the buffer for the complete raw query as
an exact byte substring (rope-native, overlap-aware KMP; no copy of the
buffer). Exact matches take precedence; fuzzy matching remains the
fallback for indentation drift, typos, and inserted/omitted lines.
- Exact matches are capped at two occurrences — enough to prove
ambiguity — and ambiguous or overlapping matches are rejected with the
existing "matched multiple locations" error instead of silently editing
one.
- The matcher now returns match candidates with their line alignment and
exact/fuzzy provenance in one result, and the edit pipeline uses that to
reindent correctly: for an exact mid-line match the first replacement
line keeps the retained prefix (zero delta) while subsequent lines get
the contextual indentation.
- The streaming parser now preserves the trailing newline of finalized
`old_text` (it still strips it from `new_text` to avoid duplicate line
endings, per #52661), so `"foo\n"` can disambiguate a standalone `foo`
line from `foo suffix`.
## Notes
- Exact-search cost is O(buffer + query) at finalization only; the
buffer is never copied into a `String` (the main concern with the
original PR).
- New regression tests cover mid-line fragments, exact-over-fuzzy
precedence, overlapping-match ambiguity, multiline replacement
indentation (spaces and tabs), trailing-newline disambiguation, and
newline-only edits.
Closes https://github.com/zed-industries/zed/issues/59369
Release Notes:
- Fixed agent edits failing when the text to replace is only part of a
line
This adds a dedicated `AvailableLanguages` struct in preparation for a
more cabable language matching based on a given language config. No
functional changes, just shuffling some code around for this round.
Also made the LanguageMatcher non-cloneable in favor of wrapping it in
an Arc, since cloning is rather expensive for this and having a
reference is sufficient in all cases right now.
Release Notes:
- N/A
---------
Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
# Objective
Add remote (SSH) and collaboration support for trashing and restoring
files in the project panel, which in turn enables undo/redo of trash
operations against remote and collab projects.
Relates to #5039.
## Solution
- Updated the project panel undo system to carry `TrashId` instead of
`TrashedEntry`.
- Using `TrashedEntry` could get hairy, as it includes paths, which
wouldn't play too nicely when using, for exapmle, macOS as the client
and Windows as the host. Using a simple identifier is much easier in
this regard and simplifies implementation.
- Enabled the Trash action and context-menu entry on remote projects,
and removed the command palette filter in `ProjectPanel::new` that was
still hiding the action on remote.
- As far as I can tell, there isn't a reliable way to detect whether a
given remote actually supports the OS trash, so we expose the action
everywhere rather than guessing. On a remote without trash support the
action will fail when invoked but this is a conscious tradeoff until we
find a better way to handle this.
- `fs` now tracks trashed files in a `SlotMap<TrashId, TrashedEntry>` on
each `Fs` implementation. Trashed files are referenced by an opaque
`TrashId` instead of passing a `TrashedEntry` around, which avoids
serializing filesystem paths in remote messages.
- Split the old `delete_entry(trash: bool)` API into distinct
`trash_entry`/`trash_file` (returning a `TrashId`) and
`delete_entry`/`delete_file` across `Project`, `Worktree`,
`LocalWorktree` and `RemoteWorktree`.
- This lets us drop the optional trash result (`Option<TrashedEntry>`)
from the delete path and require a `TrashId` from the trash path.
- Added new proto messages (`TrashProjectEntry`,
`TrashProjectEntryResponse`, `RestoreProjectEntry`,
`RestoreProjectEntryResponse`) to let clients request the host to trash
or restore entries.
- This deprecates `DeleteProjectEntry::use_trash`, but the host still
honors it. An older collab peer may request trashing via that flag
instead of the newer `TrashProjectEntry`. If the host ignored it, a
newer host would permanently delete a file the user meant to send to the
trash. The field will be removed in a later PR once all supported peers
use `TrashProjectEntry`.
## Testing
The following tests were introduced to ensure the new behavior is
correctly tested:
* `remote_server::remote_editing_tests::test_remote_trash_restore` –
Tests trashing a project entry in remote
*
`remote_server::remote_editing_tests::test_remote_delete_project_entry_with_trash`
– Test to ensure we continue respecting `DeleteProjectEntry::use_trash`
until it is fully removed
* `project_panel::tests::undo::trash_directory_undo_redo` – Not related
to these changes but a nice to have as we were missing a test ensuring
that trashing and then undoing and redoing it for a directory works as
expected
Besides these, the following scenarios were manually tested against a
remote session on the same machine (macOS):
- Trashing → Undo (Restore) → Redo (Trashing)
- Batch Trashing → Undo (Batch Restore) → Redo (Batch Trashing)
- Rename → Undo (Rename) → Redo (Rename)
- Move → Undo (Move) → Redo (Move)
- Batch Move → Undo (Batch Move) → Redo (Batch Move)
## Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [ ] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [ ] Performance impact has been considered and is acceptable
Release Notes:
- N/A
---------
Co-authored-by: Yara <git@yara.blue>
This is necessary to remove some `util` dependencies from crates, as
well as better sharing for our projects. This also includes the WIP
AbsPath abstraction as well as some bug fixes from internal tooling.
Release Notes:
- N/A or Added/Fixed/Improved ...
## Problem
Built-in tools like `Fetch` render their primary argument next to the
tool name in the collapsed tool-call header (e.g. `Fetch
https://example.com`). MCP tools don't — they always show `Run MCP tool
<name>` and the argument is hidden inside the expandable "Raw Input"
JSON block.
For Playwright, fetch, search, or any MCP tool where one argument
carries all the context, this makes every call opaque. You have to click
to expand just to see which URL is being opened or which file is being
read.
Before:
```
⊙ mcp__playwright__open_url_in_browser
[click to expand]
Raw Input:
{
"url": "https://linkedin.com/in/neuodev"
}
```
After:
```
⊙ Run MCP tool `mcp__playwright__open_url_in_browser` https://linkedin.com/in/neuodev
```
## Root cause
`ContextServerTool::initial_title` at
`crates/agent/src/tools/context_server_registry.rs` discards the input:
```rust
fn initial_title(&self, _input: serde_json::Value, _cx: &mut App) -> SharedString {
format!("Run MCP tool `{}`", self.tool.name).into()
}
```
Built-in tools read the input (`fetch_tool.rs`: `format!("Fetch {}",
MarkdownEscaped(&input.url))`) but the generic MCP path never did.
## Change
When the MCP tool's input is a JSON object with exactly one
string-valued field, inline that value next to the tool name. All other
shapes (multi-field objects, non-string values, null/empty input) fall
back to the existing `Run MCP tool <name>` label, so behavior is
unchanged for them.
- Value is truncated at 120 chars with `…` on overflow (char-safe, not
byte-safe — "café" won't panic).
- Value is run through `MarkdownEscaped` so markdown metacharacters
render literally.
- Single-string heuristic was chosen because the MCP spec has no
standard field for a display template, and the common case (`url`,
`path`, `query`, `selector`, `name`) is covered. I considered honoring
`Tool.annotations.title` or `_meta["zed/title"]` as a template — happy
to follow up with that if the team prefers an opt-in mechanism, but
wanted to start with something that works out of the box for every
existing MCP server.
## Tests
Added 10 unit tests in the existing `mod tests` block covering: happy
path, no args, null input, multi-field fallback, non-string fallback,
truncation, markdown escaping, multibyte chars, and empty-string
preservation.
`cargo check -p agent --tests` passes locally. I couldn't run the full
test binary on my machine because `wasmtime-c-api-impl` (a transitive
test-dep) needs `cmake`, but the new code is pure string-manipulation
with no wasm/runtime touch — CI should cover.
## Scope
One file, +127/-3. No behavior change for any tool shape other than
single-string-input MCP tools.
## Per CONTRIBUTING.md
Happy to convert to a discussion first if the team prefers — didn't see
a tracking issue for this. Also filed under CLA (signed).
Release Notes:
- Show MCP tool primary argument in tool header if space allows
---------
Co-authored-by: Chris Biscardi <chris@christopherbiscardi.com>
Map OpenAI's context_length_exceeded errors (both the in-stream error
events sent by the Responses API / ChatGPT Codex backend and HTTP 400
responses carrying the code) to
LanguageModelCompletionError::PromptTooLarge. This stops pointless
retries and surfaces the dedicated token-limit UI instead of a raw error
string.
Also record a synthetic request token usage when a completion fails with
PromptTooLarge, so the context indicator reports Exceeded rather than
the stale usage of the last successful request, and the next prompt
qualifies for auto-compaction.
Ought to help a bit with
https://github.com/zed-industries/zed/issues/59600
Release Notes:
- N/A or Added/Fixed/Improved ...
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>
Closes security loopholes and updates docs:
- installs seccomp filter for blocking naughty syscalls
- tightens macos seatbelt profile
- fetch tool responses that redirect are now constrained by allowed
domains list
Also adds a few "Learn More" buttons that link to the new docs.
Also fixes a bug where the agent would try to create a
`~/.config/zed/AGENTS.md` directory
Also adds unicode confusable detection to URL/path privilege escalation
prompts.
---
Release Notes:
- N/A or Added/Fixed/Improved ...
---------
Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>
The `edit_file` tool re-indents `new_text` by computing a single indent
delta from the first line of `old_text` versus the matched buffer line,
then applying that delta to every line of the replacement. When a model
omits the leading indentation on only the first line of
`old_text`/`new_text` (a common pattern when copying from mid-line
context), the delta computed from the first line was wrongly applied to
the remaining, already-correctly-indented lines, doubling their
indentation.
This PR:
- Tracks the `(query_row, buffer_row)` line pairs aligned by the
streaming fuzzy matcher so the indent of each `old_text` line can be
compared against the buffer line it actually matched.
- Computes a separate indent delta for the lines after the first: when
those lines agree on a consistent delta, it's used for the rest of the
replacement; otherwise the previous uniform behavior is preserved.
- Keeps `query_lines`/line pairs in sync when `finish()` extends a match
with a trailing incomplete line.
- Adds an end-to-end regression test reproducing the issue, plus unit
tests for the new re-indentation logic.
Closes https://github.com/zed-industries/zed/issues/60302
Release Notes:
- Fixed the agent's `edit_file` tool corrupting indentation when a
replacement omitted leading whitespace on only its first line.
# Objective
Allow zed's language model stack to express OpenAI Responses API custom
tools (freeform text-input tools with an optional lark/regex grammar),
so downstream consumers can offer tools like a freeform `apply_patch` to
GPT models.
## Solution
- `LanguageModelRequestTool` now carries a Function-vs-Custom input
variant; `LanguageModelCustomToolFormat` models text/grammar formats.
- `LanguageModelToolUse.input` becomes a typed
`LanguageModelToolUseInput::{Json, Text}`. Serialization is tagged so
persisted Text inputs round-trip losslessly; legacy plain JSON values
still deserialize as `Json`.
- `open_ai` gains the custom tool wire types (tool definition,
`custom_tool_call`/`custom_tool_call_output` input items with
string-or-content-part outputs, output item, and
`custom_tool_call_input` delta/done stream events). The Responses event
mapper accumulates raw text deltas into `ToolUse` events, and history
replay derives custom-vs-function tool results from the matching
`ToolUse` by id.
- All non-OpenAI providers and the Chat Completions path error
explicitly when a request contains custom tools — no silent drops or
empty-schema coercions.
Release Notes:
- N/A
When you use `anyhow::anyhow!("{error}")` to convert a preexisting error
to an `anyhow::Error`, the error source can be lost (depending on the
`Display` impl of the error). Anyhow errors can display the whole source
chain when printed. This commit makes us consistently use `context()`
instead to preserve the underlying error's source.
Release Notes:
- N/A
This PR improves the onboarding experience when using the agent panel.
Previously we would pick a fallback model in case the provider failed to
authenticate/was slow to resolve models. However, this code had a race
condition (for providers that resolve models dynamically like
Anthropic/GitHub Copilot), since we pick a fallback immediately after
all providers have been authenticated. However, at that point the
configured provider might not have resolved its models, so we would fall
back to a different provider. At some point we added a workaround for
the zed.dev provider.
We landed on a much simpler approach that eliminates the race condition:
We only pick a fallback model in case the user actually has no model
configured in his settings. In case the user has a model configured, we
won't fallback and show an actionable error message:
1. Model is not set and no fallback available
<img width="862" height="78" alt="image"
src="https://github.com/user-attachments/assets/e8e7472a-1c05-4cd2-8efc-49e6d921b0a4"
/>
2. Model is set, but provider is not authenticated
<img width="865" height="65" alt="image"
src="https://github.com/user-attachments/assets/3c8cbf1d-7dd5-4b2e-809a-61e6662721a3"
/>
3. Model is set, provider is authenticated, but model is not in model
list
<img width="863" height="63" alt="image"
src="https://github.com/user-attachments/assets/b97defa2-3fb4-4ec0-b3c6-26878d1c815c"
/>
4. Model is set, but provider is not recognised
<img width="865" height="67" alt="image"
src="https://github.com/user-attachments/assets/ecff1e3f-4f6b-47eb-a25d-125a104baafc"
/>
This plays well with the reason why we have the fallback model in the
first place: We only want to pick a fallback for users that open Zed for
a first time and e.g. have an Anthropic API key present in their
environment. As soon as the user manually changes his provider/model we
won't apply the fallback anymore.
Release Notes:
- agent: Improved error messaging when provider is not configured
- agent: Improve fallback model selection
---------
Co-authored-by: cameron <cameron.studdstreet@gmail.com>
## Summary
Fix agent message hyperlinks that point to Windows file paths but are
not parsed as openable project paths.
This covers links like:
```md
[Cargo.toml](</C:/Projects/Example Workspace/Cargo.toml:2>)
[filename.ext](C:\Projects\Example%20Workspace\path\to\filename.ext:42)
[AGENTS.md](</c/Projects/Example Workspace/AGENTS.md>)
```
## Problem
Agent responses can emit Markdown hyperlinks whose targets are Windows
paths rather than `file://` URLs. Some of those targets include a
leading slash before the drive (`/C:/...`), Git Bash/MSYS-style drive
prefixes (`/c/...`), percent-escaped spaces, or line suffixes. These
were not normalized before mention parsing, so clicking the hyperlink
could do nothing instead of opening the file.
## Solution
- Normalize hyperlink path targets before parsing them as `MentionUri`
paths.
- Decode percent escapes in bare path targets so `%20` becomes a literal
space before path/line parsing.
- Convert Windows-compatible hyperlink paths such as `/C:/...` and
`/c/...` into native Windows paths.
- Generate file resource links from `find_path_tool` through
`MentionUri::to_uri()` instead of hand-building `file://` strings.
## Result
Before: clicking agent path hyperlinks did not open the referenced file.
After:
https://github.com/user-attachments/assets/6c7fad77-4a1e-4497-a4f9-4a4fdf86d527
## Validation
- `cargo test -p acp_thread test_parse_windows --features test-support`
- `cargo fmt --check`
## Follow-up changes
Additions on top of the original work above:
- Moved the hyperlink heuristics into a dedicated
`MentionUri::parse_hyperlink` entry point. `MentionUri::parse` stays
strict, so canonical mention URIs round-trip verbatim and other callers
(message editor, thread deserialization, resource links) are unaffected.
- Percent escapes that decode to path separators (`%2F`, `%5C`) are left
encoded, so decoding can never change which directories a path
traverses.
- Bare paths with escapes are ambiguous (a file may literally be named
`a%20b.rs`): `open_link` prefers the decoded interpretation and falls
back to `MentionUri::parse_hyperlink_literal` when the decoded path
doesn't resolve in the project but the literal one does.
- Links to files outside the project's worktrees now open, gated by an
async existence check through the project `Fs` (correct for remote
projects; broken links no longer create empty buffers or add worktrees).
`open_link` and the mention-crease open path are unified into one
`open_abs_path_at_point`, which now also places the cursor for
out-of-project selection/symbol links.
- `grep_tool` resource links also go through `MentionUri::to_uri()` now,
fixing malformed `file://C:\...` URIs and unencoded spaces.
- Added tests: percent-escape disambiguation, out-of-project link
opening, drive-letter normalization, UNC paths, and literal-path parsing
(`cargo test -p acp_thread mention`, `cargo test -p agent_ui open_link`,
`cargo test -p agent grep_tool`); manually verified the link spellings
above on Windows.
Release Notes:
- Fixed agent path hyperlinks on Windows when paths contain spaces or
shell-style drive prefixes.
---------
Co-authored-by: Martin Ye <martin@zed.dev>
Removes git sandbox feature
The reason is essentially:
- write access to a `.git` dir can be trivially escalated to unsandboxed
access
- therefore, it is misleading to offer git access separate from
unsandboxed access
- instead, we encourage the model to use `--no-optional-locks` to avoid
needing write access to `git status`, etc.
Adds sandboxing to fetch tool
---
Release Notes:
- N/A or Added/Fixed/Improved ...
---------
Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>
Large change to sandboxing:
- fixes a nasty TOCTOU relating to a symlink swap attack, documented in
the `sandboxing/README.md`
- Adds UI and restrictions when in an untrusted workspace
- Adds tests for (soon to be removed) git support
---
Release Notes:
- N/A or Added/Fixed/Improved ...
This follows up on discussion #56551.
Right now, when Zed generates a commit message, it includes project
rules files like `AGENTS.md`, `CLAUDE.md`, and `.rules` in the prompt
alongside the git diff. That can add a lot of extra context for a task
that is really just summarizing the changes in a commit.
This PR adds an `include_project_rules` option to
`agent.commit_message_model`. When it is set to `false`, Zed skips
loading project rules when generating commit messages. If the option is
omitted, the current behavior stays the same.
Example:
```json
{
"agent": {
"commit_message_model": {
"provider": "anthropic",
"model": "claude-3-5-haiku",
"include_project_rules": false
}
}
}
```
I also updated the settings plumbing so this option is only used for commit message generation and defaults to true when not specified.
Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Release Notes:
- agent: Added setting to exclude project rules files from commit message generation prompts (`agent.commit_message_include_project_rules`)
---------
Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
Summary:
- Use the compaction-aware request history when generating thread
summaries.
- Reuse the same compaction boundary logic for thread title generation,
including saved-thread title regeneration.
- Add regression tests for summary and title request construction with
compacted history.
Tests:
- `cargo test -p agent uses_compacted_history`
Release Notes:
- Improved agent thread summary and title generation to respect
compaction boundaries.
Co-authored-by: gaauwe <gaauwe@users.noreply.github.com>
This separates the two user-message identities we currently need in
agent threads:
- `protocol_id`: the ACP `messageId` used to group or split streamed
message chunks from ACP agents.
- `client_id`: the Zed-generated user message ID used by the native
agent for truncate, rewind, edit/regenerate, checkpoints, token usage,
and persistence.
The main behavior change is that ACP protocol message IDs now affect
message merging/rendering without leaking Zed-native client IDs into ACP
agents by default. Agents that can accept Zed-generated user message IDs
opt into that path through `AgentSessionClientUserMessageIds`, which
owns both ID generation and prompt submission. The native Zed agent
implements that capability; normal ACP agents continue through the
protocol-only `prompt` path.
In ACP v2 we'll have a mechanism for getting user message replays that
will allow the agent to adequately generate it's own opaque id that we
can then use for these other capabilities. But for now, this split at
least makes sure we keep the two concepts distinct.
Release Notes:
- N/A
---------
Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
This PR unships shared threads.
The feature is not used much and is getting in the way, at this point.
These are only ever made available to staff.
The corresponding database schema migration has been created in the
Cloud repo and applied to the production database.
Release Notes:
- N/A
Closes AI-367
This PR significantly refactors the queue feature so that we don't rely
on indexes being counted and synced across various state points of the
thread view. Instead, I'm introducing a dedicated message queue module
where we control through a stable queue entry ID all the states of a
given queued message.
Additionally, it's relevant to note that I changed the default behavior
of queued messages on this PR so that they get sent only at the end of
the generation. This makes the out-of-the-box, default behavior of the
feature consistent across the native and external agents, given we never
could pull off the feature with the latter. I personally never liked
that behavior anyway, but I do appreciate how some people do. Because of
that, though, for the Zed agent, a "Steer" toggle button is available
for whenever you'd like to signal that a queued message should be sent
at the turn boundary. This is not a setting, though; decided to go
simpler at this moment. It's something you can choose on a per-message
basis.
Ultimately, I think this implementation makes the overall shape of the
feature much more stable and easier to maintain overtime.
Release Notes:
- Agent: Changed the default behavior of queued messages for the Zed
agent so that they get send at the end of the generation. The ability to
steer them (i.e., send the message at the end of a turn boundary) is
still possible to be toggled.
This removes sandbox prompt and documentation wording that claimed Git
metadata access exposes the inherited SSH agent socket. Git access
remains limited to protected metadata paths, and the Seatbelt wrapper no
longer carries SSH-agent-specific handling or examples.
Release Notes:
- Fixed AI sandboxing documentation to avoid implying Git metadata
access grants SSH agent access.
Agent terminal commands that request Git metadata access now verify
linked-worktree and submodule metadata before moving Git paths from
protected to writable sandbox paths. This prevents a mutated `.git`
gitfile from redirecting a grant to unrelated repository metadata while
preserving standard submodule layouts.
Release Notes:
- Improved agent terminal sandboxing for Git metadata access.
---------
Co-authored-by: Martin Ye <martin@zed.dev>
Agent terminal sandboxing now protects `.git` metadata by default and
exposes an explicit `allow_git_access` approval. Without it, file
contents of the `.git` directories for opened worktrees and discovered
repositories (including a linked worktree's common `.git`) cannot be
read or written, though their metadata stays visible; when approved,
those Git directories become writable so commands like fetch/commit
work. SSH commit signing keeps working because the inherited
`SSH_AUTH_SOCK` is allowed as local Unix-socket IPC, which does not let
sandboxed commands send network packets to other machines. The Seatbelt
profile also allows PTY terminal-control ioctls so signing/passphrase
prompts can manage terminal state.
This intentionally restricts only `.git` itself (whose location we know
exactly, including the worktree case) rather than
`.gitignore`/`.gitattributes`/`.gitmodules`, since those can be nested
arbitrarily and the goal is to keep the policy expressible as a plain
deny-by-default allowlist that will port to Linux and Windows sandboxes
later.
Closes AI-334
Release Notes:
- Improved agent terminal sandboxing for Git metadata, Git worktrees,
and SSH commit signing.
## Summary
Fixes#37815.
`State::fetch_models()` calls `/api/tags` to list models, then calls
`/api/show` for **every** model in that list to get its capabilities,
collecting the results with `collect::<Result<Vec<_>>>()?`. If
`/api/show` errors for even one model, the whole batch fails and
`fetched_models` is never populated. Since `is_authenticated()` is
defined as `!self.fetched_models.is_empty()`, this means a single bad
model permanently breaks both authentication state and the model picker
for the entire Ollama provider - with no error surfaced anywhere (not
the UI, not the logs), which matches the reports in #37815 of "Connect
does nothing" / "no logs, no nothing".
I hit this myself: I had a stale local reference to an Ollama Cloud
model that had been retired server-side. `/api/show` for that one model
returned `410 Gone`, which silently broke Connect and the model picker
for every other model too. Removing the retired model with `ollama rm`
fixed it immediately, which confirmed the root cause.
## Fix
Instead of aborting the whole fetch on the first error, skip individual
models that fail `/api/show` and log a warning, keeping the rest.
Extracted this into a small `skip_failed_models` helper so it's
unit-testable without mocking HTTP.
## Disclosure
I used Claude (Anthropic's Claude Code) to help track down this root
cause (tracing through `fetch_models`/`is_authenticated` in this file)
and draft the fix + tests below. I reviewed and understand the change -
it's a small, targeted fix to a single function plus two unit tests for
the new helper.
## Test plan
- [x] `cargo check -p language_models` passes
- [x] `cargo test -p language_models --lib ollama::` - all 3 tests pass
(the 2 new ones plus the existing
`test_merge_settings_preserves_display_names_for_similar_models`,
unaffected by this change)
- [x] `cargo fmt -p language_models -- --check` - no diff
## Release Notes
Release Notes:
- Fixed Ollama models silently failing to show up in the model picker
(and "Connect" appearing to do nothing) when a single model's details
couldn't be fetched, e.g. a retired Ollama Cloud model
---------
Co-authored-by: MrSubidubi <finn@zed.dev>
Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
Follow-ups to the recently-landed agent terminal sandboxing work.
- Make the persistent "Allow Unsandboxed Terminal Commands" setting
(`allow_unsandboxed`) the single off-switch for the agent terminal
sandbox: when enabled, the sandboxed terminal tool isn't exposed and the
system prompt omits the sandbox section, so the model uses the plain
`terminal` tool (and on Windows, WSL sandbox setup is skipped). This
removes the dead, unwired `disabled` setting that was meant to do the
same thing but had no UI, writer, or docs. Per-command and per-thread
`unsandboxed: true` grants are unchanged.
- Expand the blocklist of Windows-specific environment variables that
aren't forwarded into the WSL sandbox (system locations, `HOME`/profile
paths, host/session identity, CPU descriptors, etc.) so they can't
shadow or break Linux commands. It stays a blocklist, so portable
variables like `LANG` still reach the command.
Release Notes:
- N/A
We finally have a cancellation mechanism to use! Made the non
side-effectful handlers stop their work if we get a cancel request
notification.
Release Notes:
- N/A
Overhauls Zed's pickers to make them resizable and give them a preview.
Closes#8279
### Background
The most requested Zed feature has the last year has been a [Telescope
like search box](https://github.com/zed-industries/zed/issues/8279)
[discussion](https://github.com/zed-industries/zed/discussions/22581).
To understand why this is so popular we need to understand search can
serve thee goals:
- Navigation: fuzzy search is faster & easier then clicking in a file
tree
- Exploration: example, find a function by a word in its doc comment
- Collecting: example, getting a list of functions to change
The project search which shows results in a multibuffer is the perfect
way to operate on a list of items. Navigation and Exploration need a lot
of context around each result and offer fast navigation between them.
For both of these live searching is also critical.
The `telescope UI` is a picker with a preview to the right or below.
It's offered in various editors and IDE's most famously Neovim (through
the Telescope plugin), IntelliJ (natively), Helix (natively) and of
course VScode (plugins) and it's _many_ forks.
While having a UI like that for text search (our project search) is most
requested the UX pattern is applied widely, from `find_all_references`
to `bookmarks`. It enhances most pickers. Note that we have over 50
different picker modals!
The community has tried to build something like this for Zed:
- https://github.com/zed-industries/zed/pull/44530
- https://github.com/zed-industries/zed/pull/45307
- https://github.com/zed-industries/zed/pull/46478
- https://github.com/zed-industries/zed/pull/43790
These all became huge PR's that we could not merge for various reasons.
This is a really hard feature to integrate in Zed!
This PR got started as https://github.com/zed-industries/zed/pull/46478
and supercedes that.
### Design
- Extend pickers to support an optional preview with minimal changes to
the pickers themselves.
- Make pickers resizable.
- Complement the existing search do not replace it by having both UI's
share the underlying search and allow freely switching between them.
- Allow extending the preview to things other then files.
- Maintain a clean design on all the pickers.
### Heigh level Implementation overview
- Adds an `Option<Preview>` to `Picker`
- Gives `PickerDelegate` a method to communicate a preview to the Picker
- Overhaul the way pickers are drawn to allow for resizing them.
Implemented on the `Shape` and `SizeBouds` structs.
- Adds a high level way to draw the `footer` and `editor` so we do not
need to change much to the pickers.
- Adds a new text finder Picker
- Adds a way to take a running search from project search and hand it to
the text finder Picker and the other way round
- Give the file finder a preview
### Next steps
A more detailed list and how to help out will be added to the tracking
issue for [Pickes with
previews](https://github.com/zed-industries/zed/issues/56037)
- Add more previews to more pickers!
- Enable selectioning multiple items in pickers and performing actions
on those
- Open selected items in a multibuffer
- Add a way to restore the last picker
- Make popovers (picker attached to some menu) resizable as well
## Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
## Showcase
TODO (will be done post merge)
---
Release Notes:
- Added resizing via dragging to all picker modals.
- Added a preview to the File finder, the preview can be to the right or
below.
- Added a Text finder picker with a preview as alternative project
search UI. The search is shared and allowes switch between UIs while
running.
---------
Co-authored-by: ozacod <47009516+ozacod@users.noreply.github.com>
Co-authored-by: ozacod <ozacod@users.noreply.github.com>
Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
This PR fixes a bug where a thread could fall back to a BYOK model when
a remote provider hasn't yet returned its list of available models. On a
cold start, a thread previously using a Cloud model would fall over to a
BYOK model (if a key is defined) because Cloud models aren't loaded yet.
Note: there is a small behaviour change here, where previously the
priority was `default_model → profile_model → default_model`. The new
order is `profile_model → default_model`.
Release Notes:
- Improved saved thread model selection for cloud-based providers
The agent panel's `grep` tool output previously rendered file paths and
line numbers as plain markdown text, so search results weren't clickable
(unlike `find_path`, which emits `ResourceLink` content blocks).
This PR makes `grep` results clickable in the same way `find_path` does:
- `grep` now streams a `ResourceLink` content block per match
(`crates/foo/bar.rs#L12-15` → `file:///abs/path#L12-15`) followed by the
code snippet, via `event_stream.update_fields`, and sets `locations` on
the tool call.
- `render_resource_link` in `agent_ui` now splits an optional `#L...`
fragment off `file://` URIs before resolving the project-relative path,
so labels render as `path#L12-15` instead of falling back to the raw
absolute URI. Clicking opens the file at the matched line (handled by
the existing `MentionUri` selection parsing).
- The model-facing text output of the tool is unchanged.
Closes AI-401
Release Notes:
- Improved the agent panel so that file search results from the agent's
`grep` tool are clickable and open the file at the matched line.
Enable the existing agent sandboxing feature flag for staff by default,
so staff builds use sandboxed terminal commands without needing an
explicit flag override.
Release Notes:
- N/A
---------
Co-authored-by: Martin Ye <martin@zed.dev>