diff --git a/Cargo.lock b/Cargo.lock index 0bbde0bdfddb0b11b715bce230cb82cb4c74cb0f..2fa19c12df90ad026223020beb3772ec5215fc80 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -216,9 +216,9 @@ dependencies = [ [[package]] name = "agent-client-protocol" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e639d6b544ad39f5b4e05802db5eb04e1518284eb05fda1839931003e0244c8" +checksum = "c2ffe7d502c1e451aafc5aff655000f84d09c9af681354ac0012527009b1af13" dependencies = [ "agent-client-protocol-schema", "anyhow", @@ -233,15 +233,16 @@ dependencies = [ [[package]] name = "agent-client-protocol-schema" -version = "0.9.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f182f5e14bef8232b239719bd99166bb11e986c08fc211f28e392f880d3093ba" +checksum = "8af81cc2d5c3f9c04f73db452efd058333735ba9d51c2cf7ef33c9fee038e7e6" dependencies = [ "anyhow", "derive_more 2.0.1", "schemars", "serde", "serde_json", + "strum 0.27.2", ] [[package]] @@ -8147,7 +8148,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" dependencies = [ "equivalent", - "hashbrown 0.15.5", + "hashbrown 0.16.1", "serde", "serde_core", ] diff --git a/Cargo.toml b/Cargo.toml index be78357b2515b12acad808f436cf7359877b5418..2d203499c208d7ad3f366296b227807b496bf8ca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -443,7 +443,7 @@ ztracing_macro = { path = "crates/ztracing_macro" } # External crates # -agent-client-protocol = { version = "=0.8.0", features = ["unstable"] } +agent-client-protocol = { version = "=0.9.0", features = ["unstable"] } aho-corasick = "1.1" alacritty_terminal = "0.25.1-rc1" any_vec = "0.14" diff --git a/crates/acp_thread/src/acp_thread.rs b/crates/acp_thread/src/acp_thread.rs index 9c7590ccd6c5871c4db72b89eff344b3eca877a7..b96ef1d898086a0b4b9336a21d1d8369fea4ad6c 100644 --- a/crates/acp_thread/src/acp_thread.rs +++ b/crates/acp_thread/src/acp_thread.rs @@ -2929,7 +2929,7 @@ mod tests { .await .unwrap_err(); - assert_eq!(err.code, acp::ErrorCode::RESOURCE_NOT_FOUND.code); + assert_eq!(err.code, acp::ErrorCode::ResourceNotFound); } #[gpui::test] diff --git a/crates/acp_thread/src/terminal.rs b/crates/acp_thread/src/terminal.rs index fb9115650d1277e7e9982bfc851d8df142f048ad..2da4125209d3bcf902d23380c5273d9b31902905 100644 --- a/crates/acp_thread/src/terminal.rs +++ b/crates/acp_thread/src/terminal.rs @@ -75,15 +75,9 @@ impl Terminal { let exit_status = exit_status.map(portable_pty::ExitStatus::from); - let mut status = acp::TerminalExitStatus::new(); - - if let Some(exit_status) = exit_status.as_ref() { - status = status.exit_code(exit_status.exit_code()); - if let Some(signal) = exit_status.signal() { - status = status.signal(signal); - } - } - status + acp::TerminalExitStatus::new() + .exit_code(exit_status.as_ref().map(|e| e.exit_code())) + .signal(exit_status.and_then(|e| e.signal().map(ToOwned::to_owned))) }) .shared(), } @@ -105,19 +99,17 @@ impl Terminal { pub fn current_output(&self, cx: &App) -> acp::TerminalOutputResponse { if let Some(output) = self.output.as_ref() { - let mut exit_status = acp::TerminalExitStatus::new(); - if let Some(status) = output.exit_status.map(portable_pty::ExitStatus::from) { - exit_status = exit_status.exit_code(status.exit_code()); - if let Some(signal) = status.signal() { - exit_status = exit_status.signal(signal); - } - } + let exit_status = output.exit_status.map(portable_pty::ExitStatus::from); acp::TerminalOutputResponse::new( output.content.clone(), output.original_content_len > output.content.len(), ) - .exit_status(exit_status) + .exit_status( + acp::TerminalExitStatus::new() + .exit_code(exit_status.as_ref().map(|e| e.exit_code())) + .signal(exit_status.and_then(|e| e.signal().map(ToOwned::to_owned))), + ) } else { let (current_content, original_len) = self.truncated_output(cx); let truncated = current_content.len() < original_len; diff --git a/crates/agent/src/tests/mod.rs b/crates/agent/src/tests/mod.rs index 5948200dd796a336cbccbc1644c3bb200960de51..9ff870353279635957cd2b84f418f881c3444aa2 100644 --- a/crates/agent/src/tests/mod.rs +++ b/crates/agent/src/tests/mod.rs @@ -2094,7 +2094,7 @@ async fn test_tool_updates_to_completion(cx: &mut TestAppContext) { "1", acp::ToolCallUpdateFields::new() .status(acp::ToolCallStatus::Completed) - .raw_output("Finished thinking.".into()) + .raw_output("Finished thinking.") ) ); } diff --git a/crates/agent/src/thread.rs b/crates/agent/src/thread.rs index da95c4294757a23960d6c5c78aa905e63834debb..4aabf8069bc3380b6908187b28517f99a9548f26 100644 --- a/crates/agent/src/thread.rs +++ b/crates/agent/src/thread.rs @@ -766,20 +766,22 @@ impl Thread { .log_err(); } - let mut fields = acp::ToolCallUpdateFields::new().status(tool_result.as_ref().map_or( - acp::ToolCallStatus::Failed, - |result| { - if result.is_error { - acp::ToolCallStatus::Failed - } else { - acp::ToolCallStatus::Completed - } - }, - )); - if let Some(output) = output { - fields = fields.raw_output(output); - } - stream.update_tool_call_fields(&tool_use.id, fields); + stream.update_tool_call_fields( + &tool_use.id, + acp::ToolCallUpdateFields::new() + .status( + tool_result + .as_ref() + .map_or(acp::ToolCallStatus::Failed, |result| { + if result.is_error { + acp::ToolCallStatus::Failed + } else { + acp::ToolCallStatus::Completed + } + }), + ) + .raw_output(output), + ); } pub fn from_db( @@ -1259,15 +1261,16 @@ impl Thread { while let Some(tool_result) = tool_results.next().await { log::debug!("Tool finished {:?}", tool_result); - let mut fields = acp::ToolCallUpdateFields::new().status(if tool_result.is_error { - acp::ToolCallStatus::Failed - } else { - acp::ToolCallStatus::Completed - }); - if let Some(output) = &tool_result.output { - fields = fields.raw_output(output.clone()); - } - event_stream.update_tool_call_fields(&tool_result.tool_use_id, fields); + event_stream.update_tool_call_fields( + &tool_result.tool_use_id, + acp::ToolCallUpdateFields::new() + .status(if tool_result.is_error { + acp::ToolCallStatus::Failed + } else { + acp::ToolCallStatus::Completed + }) + .raw_output(tool_result.output.clone()), + ); this.update(cx, |this, _cx| { this.pending_message() .tool_results @@ -1545,7 +1548,7 @@ impl Thread { event_stream.update_tool_call_fields( &tool_use.id, acp::ToolCallUpdateFields::new() - .title(title) + .title(title.as_str()) .kind(kind) .raw_input(tool_use.input.clone()), ); @@ -2461,7 +2464,7 @@ impl ToolCallEventStream { ToolCallAuthorization { tool_call: acp::ToolCallUpdate::new( self.tool_use_id.to_string(), - acp::ToolCallUpdateFields::new().title(title), + acp::ToolCallUpdateFields::new().title(title.into()), ), options: vec![ acp::PermissionOption::new( diff --git a/crates/agent/src/tools/edit_file_tool.rs b/crates/agent/src/tools/edit_file_tool.rs index cbe96a6b20d6e325beb9aedb6cf6d2eca1df171a..0ab99426e2e9645adf3f837d21c28dc285ab6ea2 100644 --- a/crates/agent/src/tools/edit_file_tool.rs +++ b/crates/agent/src/tools/edit_file_tool.rs @@ -384,11 +384,7 @@ impl AgentTool for EditFileTool { range.start.to_point(&buffer.snapshot()).row }).ok(); if let Some(abs_path) = abs_path.clone() { - let mut location = ToolCallLocation::new(abs_path); - if let Some(line) = line { - location = location.line(line); - } - event_stream.update_fields(ToolCallUpdateFields::new().locations(vec![location])); + event_stream.update_fields(ToolCallUpdateFields::new().locations(vec![ToolCallLocation::new(abs_path).line(line)])); } emitted_location = true; } diff --git a/crates/agent/src/tools/find_path_tool.rs b/crates/agent/src/tools/find_path_tool.rs index 3c34f14c3a78f0fa8a6ee6794ef2567fe13d5d3c..2a33b14b4c87d87154e2aa1ee25363b397189f89 100644 --- a/crates/agent/src/tools/find_path_tool.rs +++ b/crates/agent/src/tools/find_path_tool.rs @@ -138,7 +138,7 @@ impl AgentTool for FindPathTool { )), )) }) - .collect(), + .collect::>(), ), ); diff --git a/crates/agent/src/tools/read_file_tool.rs b/crates/agent/src/tools/read_file_tool.rs index 5b19bf36ee3a0949910d217880e2e95c49f021fc..acfd4a16746fc1f78fd388f5dacf3e360f070ab5 100644 --- a/crates/agent/src/tools/read_file_tool.rs +++ b/crates/agent/src/tools/read_file_tool.rs @@ -152,12 +152,11 @@ impl AgentTool for ReadFileTool { } let file_path = input.path.clone(); - let mut location = acp::ToolCallLocation::new(&abs_path); - if let Some(line) = input.start_line { - location = location.line(line.saturating_sub(1)); - } - event_stream.update_fields(ToolCallUpdateFields::new().locations(vec![location])); + event_stream.update_fields(ToolCallUpdateFields::new().locations(vec![ + acp::ToolCallLocation::new(&abs_path) + .line(input.start_line.map(|line| line.saturating_sub(1))), + ])); if image_store::is_image_file(&self.project, &project_path, cx) { return cx.spawn(async move |cx| { diff --git a/crates/agent/src/tools/web_search_tool.rs b/crates/agent/src/tools/web_search_tool.rs index d78b692126f62d6ed7fd00f585618ab6b6ba55e2..eb4ebacea2a8e48d6efa9032f46b336ca30c39b6 100644 --- a/crates/agent/src/tools/web_search_tool.rs +++ b/crates/agent/src/tools/web_search_tool.rs @@ -121,7 +121,7 @@ fn emit_update(response: &WebSearchResponse, event_stream: &ToolCallEventStream) ), )) }) - .collect(), + .collect::>(), ), ); } diff --git a/crates/agent_servers/src/acp.rs b/crates/agent_servers/src/acp.rs index f035e981919deb2fa15069866507abd8be0ac209..153357a79afdaeeb4bf4c9e2b48bee32245ba2ef 100644 --- a/crates/agent_servers/src/acp.rs +++ b/crates/agent_servers/src/acp.rs @@ -173,10 +173,6 @@ impl AcpConnection { }); })?; - let mut client_info = acp::Implementation::new("zed", version); - if let Some(release_channel) = release_channel { - client_info = client_info.title(release_channel); - } let response = connection .initialize( acp::InitializeRequest::new(acp::ProtocolVersion::V1) @@ -192,7 +188,10 @@ impl AcpConnection { ("terminal-auth".into(), true.into()), ])), ) - .client_info(client_info), + .client_info( + acp::Implementation::new("zed", version) + .title(release_channel.map(ToOwned::to_owned)), + ), ) .await?; @@ -302,10 +301,10 @@ impl AgentConnection for AcpConnection { .new_session(acp::NewSessionRequest::new(cwd).mcp_servers(mcp_servers)) .await .map_err(|err| { - if err.code == acp::ErrorCode::AUTH_REQUIRED.code { + if err.code == acp::ErrorCode::AuthRequired { let mut error = AuthRequired::new(); - if err.message != acp::ErrorCode::AUTH_REQUIRED.message { + if err.message != acp::ErrorCode::AuthRequired.to_string() { error = error.with_description(err.message); } @@ -467,11 +466,11 @@ impl AgentConnection for AcpConnection { match result { Ok(response) => Ok(response), Err(err) => { - if err.code == acp::ErrorCode::AUTH_REQUIRED.code { + if err.code == acp::ErrorCode::AuthRequired { return Err(anyhow!(acp::Error::auth_required())); } - if err.code != ErrorCode::INTERNAL_ERROR.code { + if err.code != ErrorCode::InternalError { anyhow::bail!(err) } @@ -838,13 +837,18 @@ impl acp::Client for ClientDelegate { if let Some(term_exit) = meta.get("terminal_exit") { if let Some(id_str) = term_exit.get("terminal_id").and_then(|v| v.as_str()) { let terminal_id = acp::TerminalId::new(id_str); - let mut status = acp::TerminalExitStatus::new(); - if let Some(code) = term_exit.get("exit_code").and_then(|v| v.as_u64()) { - status = status.exit_code(code as u32) - } - if let Some(signal) = term_exit.get("signal").and_then(|v| v.as_str()) { - status = status.signal(signal); - } + let status = acp::TerminalExitStatus::new() + .exit_code( + term_exit + .get("exit_code") + .and_then(|v| v.as_u64()) + .map(|i| i as u32), + ) + .signal( + term_exit + .get("signal") + .and_then(|v| v.as_str().map(|s| s.to_string())), + ); let _ = session.thread.update(&mut self.cx.clone(), |thread, cx| { thread.on_terminal_provider_event( diff --git a/crates/agent_ui/src/acp/message_editor.rs b/crates/agent_ui/src/acp/message_editor.rs index 875dc495cea710d1df950b47b328042bbda4a287..225a68b0718b5e2d8c7dc8cbc0725aec9d48b551 100644 --- a/crates/agent_ui/src/acp/message_editor.rs +++ b/crates/agent_ui/src/acp/message_editor.rs @@ -417,13 +417,12 @@ impl MessageEditor { )) } } - Mention::Image(mention_image) => { - let mut image = acp::ImageContent::new( + Mention::Image(mention_image) => acp::ContentBlock::Image( + acp::ImageContent::new( mention_image.data.clone(), mention_image.format.mime_type(), - ); - - if let Some(uri) = match uri { + ) + .uri(match uri { MentionUri::File { .. } => Some(uri.to_uri().to_string()), MentionUri::PastedImage => None, other => { @@ -433,11 +432,8 @@ impl MessageEditor { ); None } - } { - image = image.uri(uri) - }; - acp::ContentBlock::Image(image) - } + }), + ), Mention::Link => acp::ContentBlock::ResourceLink( acp::ResourceLink::new(uri.name(), uri.to_uri().to_string()), ), diff --git a/crates/agent_ui/src/acp/thread_view.rs b/crates/agent_ui/src/acp/thread_view.rs index c917a48ad5bac67e7dcdef94dbace97b26843404..7b2954c2edafa6b610efe654c8c1368f04a374b6 100644 --- a/crates/agent_ui/src/acp/thread_view.rs +++ b/crates/agent_ui/src/acp/thread_view.rs @@ -100,7 +100,7 @@ impl ThreadError { { Self::ModelRequestLimitReached(error.plan) } else if let Some(acp_error) = error.downcast_ref::() - && acp_error.code == acp::ErrorCode::AUTH_REQUIRED.code + && acp_error.code == acp::ErrorCode::AuthRequired { Self::AuthenticationRequired(acp_error.message.clone().into()) } else { @@ -6243,7 +6243,7 @@ pub(crate) mod tests { StubAgentConnection::new().with_permission_requests(HashMap::from_iter([( tool_call_id, vec![acp::PermissionOption::new( - "1".into(), + "1", "Allow", acp::PermissionOptionKind::AllowOnce, )],