From 39de188584f3fd3d966290dfe8577d44f231c4df Mon Sep 17 00:00:00 2001 From: Ben Brandt Date: Thu, 25 Sep 2025 13:53:36 +0200 Subject: [PATCH] acp: Use ACP error types in read_text_file (#38863) - Map path lookup and internal failures to acp::Error - Return INVALID_PARAMS for reads beyond EOF Release Notes: - acp: Return more informative error types from `read_text_file` to agents --- Cargo.lock | 4 +- Cargo.toml | 2 +- crates/acp_thread/src/acp_thread.rs | 61 +++++++++++++++++++++++------ 3 files changed, 52 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 85bb007c8a943ac4b685cdd961535151d3038a71..6ad4619df11f7a0b52f7579099df1f9a03526520 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -195,9 +195,9 @@ dependencies = [ [[package]] name = "agent-client-protocol" -version = "0.4.0" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2526e80463b9742afed4829aedd6ae5632d6db778c6cc1fecb80c960c3521b" +checksum = "3aaa2bd05a2401887945f8bfd70026e90bc3cf96c62ab9eba2779835bf21dc60" dependencies = [ "anyhow", "async-broadcast", diff --git a/Cargo.toml b/Cargo.toml index 96a1fc588f03162ba10ed60c52353a1a710b3783..0ebdbe15936b086e7c84f8506b285fd5a7c6394f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -437,7 +437,7 @@ zlog_settings = { path = "crates/zlog_settings" } # External crates # -agent-client-protocol = { version = "0.4.0", features = ["unstable"] } +agent-client-protocol = { version = "0.4.3", features = ["unstable"] } aho-corasick = "1.1" alacritty_terminal = { git = "https://github.com/zed-industries/alacritty.git", branch = "add-hush-login-flag" } any_vec = "0.14" diff --git a/crates/acp_thread/src/acp_thread.rs b/crates/acp_thread/src/acp_thread.rs index 1f863ce9cbea495f90f0de325bbb8de6bbc163fe..0e9cc921cd4a2f6961cd140d974cd612aeabbe0d 100644 --- a/crates/acp_thread/src/acp_thread.rs +++ b/crates/acp_thread/src/acp_thread.rs @@ -1780,20 +1780,26 @@ impl AcpThread { limit: Option, reuse_shared_snapshot: bool, cx: &mut Context, - ) -> Task> { + ) -> Task> { // Args are 1-based, move to 0-based let line = line.unwrap_or_default().saturating_sub(1); let limit = limit.unwrap_or(u32::MAX); let project = self.project.clone(); let action_log = self.action_log.clone(); cx.spawn(async move |this, cx| { - let load = project.update(cx, |project, cx| { - let path = project - .project_path_for_absolute_path(&path, cx) - .context("invalid path")?; - anyhow::Ok(project.open_buffer(path, cx)) - }); - let buffer = load??.await?; + let load = project + .update(cx, |project, cx| { + let path = project + .project_path_for_absolute_path(&path, cx) + .ok_or_else(|| { + acp::Error::resource_not_found(Some(path.display().to_string())) + })?; + Ok(project.open_buffer(path, cx)) + }) + .map_err(|e| acp::Error::internal_error().with_data(e.to_string())) + .flatten()?; + + let buffer = load.await?; let snapshot = if reuse_shared_snapshot { this.read_with(cx, |this, _| { @@ -1823,11 +1829,11 @@ impl AcpThread { let start_position = Point::new(line, 0); if start_position > max_point { - anyhow::bail!( + return Err(acp::Error::invalid_params().with_data(format!( "Attempting to read beyond the end of the file, line {}:{}", max_point.row + 1, max_point.column - ); + ))); } let start = snapshot.anchor_before(start_position); @@ -2461,7 +2467,7 @@ mod tests { assert_eq!( err.to_string(), - "Attempting to read beyond the end of the file, line 5:0" + "Invalid params: \"Attempting to read beyond the end of the file, line 5:0\"" ); } @@ -2536,9 +2542,40 @@ mod tests { assert_eq!( err.to_string(), - "Attempting to read beyond the end of the file, line 1:0" + "Invalid params: \"Attempting to read beyond the end of the file, line 1:0\"" ); } + #[gpui::test] + async fn test_reading_non_existing_file(cx: &mut TestAppContext) { + init_test(cx); + + let fs = FakeFs::new(cx.executor()); + fs.insert_tree(path!("/tmp"), json!({})).await; + let project = Project::test(fs.clone(), [], cx).await; + project + .update(cx, |project, cx| { + project.find_or_create_worktree(path!("/tmp"), true, cx) + }) + .await + .unwrap(); + + let connection = Rc::new(FakeAgentConnection::new()); + + let thread = cx + .update(|cx| connection.new_thread(project, Path::new(path!("/tmp")), cx)) + .await + .unwrap(); + + // Out of project file + let err = thread + .update(cx, |thread, cx| { + thread.read_text_file(path!("/foo").into(), None, None, false, cx) + }) + .await + .unwrap_err(); + + assert_eq!(err.code, acp::ErrorCode::RESOURCE_NOT_FOUND.code); + } #[gpui::test] async fn test_succeeding_canceled_toolcall(cx: &mut TestAppContext) {