agent: Remove open tool (#56295)

At this point, this tool rarely gets called and the agent can likely
figure out how to call these itself.

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:

- Removed open tool from built-in Agent tools.
This commit is contained in:
Ben Brandt 2026-05-11 10:58:03 +02:00 committed by GitHub
parent 7bdcb61722
commit db6039d815
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 19 additions and 292 deletions

1
Cargo.lock generated
View file

@ -183,7 +183,6 @@ dependencies = [
"language_models",
"log",
"lsp",
"open",
"parking_lot",
"paths",
"pretty_assertions",

View file

@ -1117,15 +1117,12 @@
"get_code_actions": true,
"go_to_definition": true,
"list_directory": true,
"project_notifications": false,
"move_path": true,
"rename_symbol": true,
"read_file": true,
"open": true,
"grep": true,
"spawn_agent": true,
"terminal": true,
"thinking": true,
"update_plan": true,
"search_web": true,
},
@ -1138,16 +1135,13 @@
"diagnostics": true,
"fetch": true,
"list_directory": true,
"project_notifications": false,
"find_path": true,
"find_references": true,
"get_code_actions": true,
"go_to_definition": true,
"read_file": true,
"open": true,
"grep": true,
"spawn_agent": true,
"thinking": true,
"update_plan": true,
"search_web": true,
},

View file

@ -46,7 +46,6 @@ language.workspace = true
language_model.workspace = true
language_models.workspace = true
log.workspace = true
open.workspace = true
parking_lot.workspace = true
paths.workspace = true
project.workspace = true

View file

@ -2,9 +2,9 @@ use crate::{
ApplyCodeActionTool, CodeActionStore, ContextServerRegistry, CopyPathTool, CreateDirectoryTool,
DbLanguageModel, DbThread, DeletePathTool, DiagnosticsTool, EditFileTool, FetchTool,
FindPathTool, FindReferencesTool, GetCodeActionsTool, GoToDefinitionTool, GrepTool,
ListDirectoryTool, MovePathTool, OpenTool, ProjectSnapshot, ReadFileTool, RenameTool,
SpawnAgentTool, SystemPromptTemplate, Templates, TerminalTool, ToolPermissionDecision,
UpdatePlanTool, WebSearchTool, WriteFileTool, decide_permission_from_settings,
ListDirectoryTool, MovePathTool, ProjectSnapshot, ReadFileTool, RenameTool, SpawnAgentTool,
SystemPromptTemplate, Templates, TerminalTool, ToolPermissionDecision, UpdatePlanTool,
WebSearchTool, WriteFileTool, decide_permission_from_settings,
};
use acp_thread::{MentionUri, UserMessageId};
use action_log::ActionLog;
@ -1607,7 +1607,6 @@ impl Thread {
self.add_tool(GrepTool::new(self.project.clone()));
self.add_tool(ListDirectoryTool::new(self.project.clone()));
self.add_tool(MovePathTool::new(self.project.clone()));
self.add_tool(OpenTool::new(self.project.clone()));
if cx.has_flag::<UpdatePlanToolFeatureFlag>() {
self.add_tool(UpdatePlanTool);
}

View file

@ -16,7 +16,6 @@ mod go_to_definition_tool;
mod grep_tool;
mod list_directory_tool;
mod move_path_tool;
mod open_tool;
mod read_file_tool;
mod rename_tool;
mod spawn_agent_tool;
@ -72,7 +71,6 @@ pub use go_to_definition_tool::*;
pub use grep_tool::*;
pub use list_directory_tool::*;
pub use move_path_tool::*;
pub use open_tool::*;
pub use read_file_tool::*;
pub use rename_tool::*;
pub use spawn_agent_tool::*;
@ -166,7 +164,6 @@ tools! {
GrepTool,
ListDirectoryTool,
MovePathTool,
OpenTool,
ReadFileTool,
RenameTool,
SpawnAgentTool,

View file

@ -1,227 +0,0 @@
use super::tool_permissions::{
ResolvedProjectPath, authorize_symlink_access, canonicalize_worktree_roots,
resolve_project_path,
};
use crate::{AgentTool, ToolInput};
use agent_client_protocol::schema as acp;
use futures::FutureExt as _;
use gpui::{App, AppContext as _, Entity, SharedString, Task};
use project::Project;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::{path::PathBuf, sync::Arc};
use util::markdown::MarkdownEscaped;
/// This tool opens a file or URL with the default application associated with it on the user's operating system:
///
/// - On macOS, it's equivalent to the `open` command
/// - On Windows, it's equivalent to `start`
/// - On Linux, it uses something like `xdg-open`, `gio open`, `gnome-open`, `kde-open`, `wslview` as appropriate
///
/// For example, it can open a web browser with a URL, open a PDF file with the default PDF viewer, etc.
///
/// You MUST ONLY use this tool when the user has explicitly requested opening something. You MUST NEVER assume that the user would like for you to use this tool.
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub struct OpenToolInput {
/// The path or URL to open with the default application.
path_or_url: String,
}
pub struct OpenTool {
project: Entity<Project>,
}
impl OpenTool {
pub fn new(project: Entity<Project>) -> Self {
Self { project }
}
}
impl AgentTool for OpenTool {
type Input = OpenToolInput;
type Output = String;
const NAME: &'static str = "open";
fn kind() -> acp::ToolKind {
acp::ToolKind::Execute
}
fn initial_title(
&self,
input: Result<Self::Input, serde_json::Value>,
_cx: &mut App,
) -> SharedString {
if let Ok(input) = input {
format!("Open `{}`", MarkdownEscaped(&input.path_or_url)).into()
} else {
"Open file or URL".into()
}
}
fn run(
self: Arc<Self>,
input: ToolInput<Self::Input>,
event_stream: crate::ToolCallEventStream,
cx: &mut App,
) -> Task<Result<Self::Output, Self::Output>> {
let project = self.project.clone();
cx.spawn(async move |cx| {
let input = input.recv().await.map_err(|e| e.to_string())?;
// If path_or_url turns out to be a path in the project, make it absolute.
let (abs_path, initial_title) = cx.update(|cx| {
let abs_path = to_absolute_path(&input.path_or_url, project.clone(), cx);
let initial_title = self.initial_title(Ok(input.clone()), cx);
(abs_path, initial_title)
});
let fs = project.read_with(cx, |project, _cx| project.fs().clone());
let canonical_roots = canonicalize_worktree_roots(&project, &fs, cx).await;
// Symlink escape authorization replaces (rather than supplements)
// the normal tool-permission prompt. The symlink prompt already
// requires explicit user approval with the canonical target shown,
// which is strictly more security-relevant than a generic confirm.
let symlink_escape = project.read_with(cx, |project, cx| {
match resolve_project_path(
project,
PathBuf::from(&input.path_or_url),
&canonical_roots,
cx,
) {
Ok(ResolvedProjectPath::SymlinkEscape {
canonical_target, ..
}) => Some(canonical_target),
_ => None,
}
});
let authorize = if let Some(canonical_target) = symlink_escape {
cx.update(|cx| {
authorize_symlink_access(
Self::NAME,
&input.path_or_url,
&canonical_target,
&event_stream,
cx,
)
})
} else {
cx.update(|cx| {
let context = crate::ToolPermissionContext::new(
Self::NAME,
vec![input.path_or_url.clone()],
);
event_stream.authorize(initial_title, context, cx)
})
};
futures::select! {
result = authorize.fuse() => result.map_err(|e| e.to_string())?,
_ = event_stream.cancelled_by_user().fuse() => {
return Err("Open cancelled by user".to_string());
}
}
let path_or_url = input.path_or_url.clone();
cx.background_spawn(async move {
match abs_path {
Some(path) => open::that(path),
None => open::that(path_or_url),
}
.map_err(|e| format!("Failed to open URL or file path: {e}"))
})
.await?;
Ok(format!("Successfully opened {}", input.path_or_url))
})
}
}
fn to_absolute_path(
potential_path: &str,
project: Entity<Project>,
cx: &mut App,
) -> Option<PathBuf> {
let project = project.read(cx);
project
.find_project_path(PathBuf::from(potential_path), cx)
.and_then(|project_path| project.absolute_path(&project_path, cx))
}
#[cfg(test)]
mod tests {
use super::*;
use gpui::TestAppContext;
use project::{FakeFs, Project};
use settings::SettingsStore;
use std::path::Path;
use tempfile::TempDir;
#[gpui::test]
async fn test_to_absolute_path(cx: &mut TestAppContext) {
init_test(cx);
let temp_dir = TempDir::new().expect("Failed to create temp directory");
let temp_path = temp_dir.path().to_string_lossy().into_owned();
let fs = FakeFs::new(cx.executor());
fs.insert_tree(
&temp_path,
serde_json::json!({
"src": {
"main.rs": "fn main() {}",
"lib.rs": "pub fn lib_fn() {}"
},
"docs": {
"readme.md": "# Project Documentation"
}
}),
)
.await;
// Use the temp_path as the root directory, not just its filename
let project = Project::test(fs.clone(), [temp_dir.path()], cx).await;
// Test cases where the function should return Some
cx.update(|cx| {
// Project-relative paths should return Some
// Create paths using the last segment of the temp path to simulate a project-relative path
let root_dir_name = Path::new(&temp_path)
.file_name()
.unwrap_or_else(|| std::ffi::OsStr::new("temp"))
.to_string_lossy();
assert!(
to_absolute_path(&format!("{root_dir_name}/src/main.rs"), project.clone(), cx)
.is_some(),
"Failed to resolve main.rs path"
);
assert!(
to_absolute_path(
&format!("{root_dir_name}/docs/readme.md",),
project.clone(),
cx,
)
.is_some(),
"Failed to resolve readme.md path"
);
// External URL should return None
let result = to_absolute_path("https://example.com", project.clone(), cx);
assert_eq!(result, None, "External URLs should return None");
// Path outside project
let result = to_absolute_path("../invalid/path", project.clone(), cx);
assert_eq!(result, None, "Paths outside the project should return None");
});
}
fn init_test(cx: &mut TestAppContext) {
cx.update(|cx| {
let settings_store = SettingsStore::test(cx);
cx.set_global(settings_store);
});
}
}

View file

@ -52,22 +52,10 @@ Searches file contents across the project using regular expressions, preferred f
Lists files and directories in a given path, providing an overview of filesystem contents.
### `now` {#now}
Returns the current date and time.
### `open` {#open}
Opens a file or URL with the default application associated with it on the user's operating system.
### `read_file` {#read-file}
Reads the content of a specified file in the project, allowing access to file contents.
### `thinking` {#thinking}
Allows the Agent to work through problems, brainstorm ideas, or plan without executing actions, useful for complex problem-solving.
### `search_web` {#search-web}
Searches the web for information, providing results with snippets and links from relevant web pages, useful for accessing real-time information.
@ -94,13 +82,9 @@ Edits files by replacing specific text with new content.
Moves or renames a file or directory in the project, performing a rename if only the filename differs.
### `restore_file_from_disk` {#restore-file}
### `write_file` {#write-file}
Discards unsaved changes in open buffers by reloading file contents from disk. Useful for resetting files to their on-disk state before retrying an edit.
### `save_file` {#save-file}
Saves files that have unsaved changes. Used when files need to be saved before further edits can be made.
Creates a new file or overwrites an existing file with completely new contents.
### `terminal` {#terminal}
@ -108,7 +92,7 @@ Executes shell commands and returns the combined output, creating a new shell pr
## Other Tools {#other-tools}
### `subagent` {#subagent}
### `spawn_agent` {#spawn-agent}
Spawns a subagent with its own context window to perform a delegated task. Useful for running parallel investigations, completing self-contained tasks, or performing research where only the outcome matters. Each subagent has access to the same tools as the parent agent.

View file

@ -110,7 +110,6 @@ As an example, [the Dagger team suggests](https://container-use.com/agent-integr
"list_directory": false,
"diagnostics": false,
"read_file": false,
"open": false,
"move_path": false,
"grep": false,
"edit_file": false,

View file

@ -43,18 +43,17 @@ The `tool_permissions` setting lets you customize tool permissions by specifying
## Supported Tools
| Tool | Input Matched Against |
| ------------------------ | ---------------------------- |
| `terminal` | The shell command string |
| `edit_file` | The file path |
| `delete_path` | The path being deleted |
| `move_path` | Source and destination paths |
| `copy_path` | Source and destination paths |
| `create_directory` | The directory path |
| `restore_file_from_disk` | The file paths |
| `save_file` | The file paths |
| `fetch` | The URL |
| `search_web` | The search query |
| Tool | Input Matched Against |
| ------------------ | ---------------------------- |
| `terminal` | The shell command string |
| `edit_file` | The file path |
| `write_file` | The file path |
| `delete_path` | The path being deleted |
| `move_path` | Source and destination paths |
| `copy_path` | Source and destination paths |
| `create_directory` | The directory path |
| `fetch` | The URL |
| `search_web` | The search query |
For MCP tools, use the format `mcp:<server>:<tool_name>`.
For example, a tool called `create_issue` on a server called `github` would be `mcp:github:create_issue`.

View file

@ -41,22 +41,10 @@ Searches file contents across the project using regular expressions, preferred f
Lists files and directories in a given path, providing an overview of filesystem contents.
### `now`
Returns the current date and time.
### `open`
Opens a file or URL with the default application associated with it on the user's operating system.
### `read_file`
Reads the content of a specified file in the project, allowing access to file contents.
### `thinking`
Allows the Agent to work through problems, brainstorm ideas, or plan without executing actions, useful for complex problem-solving.
### `search_web`
Searches the web for information, providing results with snippets and links from relevant web pages, useful for accessing real-time information.
@ -89,13 +77,9 @@ Edits files by replacing specific text with new content.
Moves or renames a file or directory in the project, performing a rename if only the filename differs.
### `restore_file_from_disk`
### `write_file`
Discards unsaved changes in open buffers by reloading file contents from disk. Useful for resetting files to their on-disk state before retrying an edit.
### `save_file`
Saves files that have unsaved changes. Used when files need to be saved before further edits can be made.
Creates a new file or overwrites an existing file with completely new contents.
### `terminal`