mirror of
https://github.com/zed-industries/zed.git
synced 2026-05-31 19:05:00 +07:00
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:
parent
7bdcb61722
commit
db6039d815
10 changed files with 19 additions and 292 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -183,7 +183,6 @@ dependencies = [
|
|||
"language_models",
|
||||
"log",
|
||||
"lsp",
|
||||
"open",
|
||||
"parking_lot",
|
||||
"paths",
|
||||
"pretty_assertions",
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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`.
|
||||
|
|
|
|||
|
|
@ -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`
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue