diff --git a/Cargo.lock b/Cargo.lock index 0815155ee6..6d15e873e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -263,9 +263,9 @@ dependencies = [ [[package]] name = "agentic-coding-protocol" -version = "0.0.5" +version = "0.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b962eee17ee3924870d9b9d28cc8b6dcb5421e4d4e81cd864226374a122ceed1" +checksum = "d1ac0351749af7bf53c65042ef69fefb9351aa8b7efa0a813d6281377605c37d" dependencies = [ "anyhow", "chrono", diff --git a/Cargo.toml b/Cargo.toml index aac12b7ff8..0a87bf35b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -404,7 +404,7 @@ zlog_settings = { path = "crates/zlog_settings" } # External crates # -agentic-coding-protocol = "0.0.5" +agentic-coding-protocol = "0.0.6" 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/src/acp.rs b/crates/acp/src/acp.rs index ce83618288..ddb7c50f7a 100644 --- a/crates/acp/src/acp.rs +++ b/crates/acp/src/acp.rs @@ -28,15 +28,15 @@ pub struct UserMessage { impl UserMessage { pub fn from_acp( - message: acp::UserMessage, + message: &acp::SendUserMessageParams, language_registry: Arc, cx: &mut App, ) -> Self { let mut md_source = String::new(); - for chunk in message.chunks { + for chunk in &message.chunks { match chunk { - UserMessageChunk::Text { chunk } => md_source.push_str(&chunk), + UserMessageChunk::Text { text } => md_source.push_str(&text), UserMessageChunk::Path { path } => { write!(&mut md_source, "{}", MentionPath(&path)).unwrap() } @@ -116,11 +116,12 @@ impl AssistantMessageChunk { cx: &mut App, ) -> Self { match chunk { - acp::AssistantMessageChunk::Text { chunk } => Self::Text { - chunk: cx.new(|cx| Markdown::new(chunk.into(), Some(language_registry), None, cx)), + acp::AssistantMessageChunk::Text { text } => Self::Text { + chunk: cx.new(|cx| Markdown::new(text.into(), Some(language_registry), None, cx)), }, - acp::AssistantMessageChunk::Thought { chunk } => Self::Thought { - chunk: cx.new(|cx| Markdown::new(chunk.into(), Some(language_registry), None, cx)), + acp::AssistantMessageChunk::Thought { thought } => Self::Thought { + chunk: cx + .new(|cx| Markdown::new(thought.into(), Some(language_registry), None, cx)), }, } } @@ -607,11 +608,11 @@ impl AcpThread { match (chunks.last_mut(), &chunk) { ( Some(AssistantMessageChunk::Text { chunk: old_chunk }), - acp::AssistantMessageChunk::Text { chunk: new_chunk }, + acp::AssistantMessageChunk::Text { text: new_chunk }, ) | ( Some(AssistantMessageChunk::Thought { chunk: old_chunk }), - acp::AssistantMessageChunk::Thought { chunk: new_chunk }, + acp::AssistantMessageChunk::Thought { thought: new_chunk }, ) => { old_chunk.update(cx, |old_chunk, cx| { old_chunk.append(&new_chunk, cx); @@ -813,16 +814,31 @@ impl AcpThread { async move { Ok(connection.request(acp::AuthenticateParams).await?) } } + #[cfg(test)] + pub fn send_raw( + &mut self, + message: &str, + cx: &mut Context, + ) -> BoxFuture<'static, Result<()>> { + self.send( + acp::SendUserMessageParams { + chunks: vec![acp::UserMessageChunk::Text { + text: message.to_string(), + }], + }, + cx, + ) + } + pub fn send( &mut self, - message: impl Into, + message: acp::SendUserMessageParams, cx: &mut Context, ) -> BoxFuture<'static, Result<()>> { let agent = self.connection.clone(); - let message = message.into(); self.push_entry( AgentThreadEntry::UserMessage(UserMessage::from_acp( - message.clone(), + &message, self.project.read(cx).languages().clone(), cx, )), @@ -835,7 +851,7 @@ impl AcpThread { self.send_task = Some(cx.spawn(async move |this, cx| { cancel.await.log_err(); - let result = agent.request(acp::SendUserMessageParams { message }).await; + let result = agent.request(message).await; tx.send(result).log_err(); this.update(cx, |this, _cx| this.send_task.take()).log_err(); })); @@ -1030,8 +1046,6 @@ mod tests { async fn test_thinking_concatenation(cx: &mut TestAppContext) { init_test(cx); - cx.executor().allow_parking(); - let fs = FakeFs::new(cx.executor()); let project = Project::test(fs, [], cx).await; let (thread, fake_server) = fake_acp_thread(project, cx); @@ -1042,7 +1056,7 @@ mod tests { .update(&mut cx, |server, _| { server.send_to_zed(acp::StreamAssistantMessageChunkParams { chunk: acp::AssistantMessageChunk::Thought { - chunk: "Thinking ".into(), + thought: "Thinking ".into(), }, }) })? @@ -1052,7 +1066,7 @@ mod tests { .update(&mut cx, |server, _| { server.send_to_zed(acp::StreamAssistantMessageChunkParams { chunk: acp::AssistantMessageChunk::Thought { - chunk: "hard!".into(), + thought: "hard!".into(), }, }) })? @@ -1064,7 +1078,7 @@ mod tests { }); thread - .update(cx, |thread, cx| thread.send("Hello from Zed!", cx)) + .update(cx, |thread, cx| thread.send_raw("Hello from Zed!", cx)) .await .unwrap(); @@ -1123,7 +1137,7 @@ mod tests { }); let request = thread.update(cx, |thread, cx| { - thread.send("Fetch https://example.com", cx) + thread.send_raw("Fetch https://example.com", cx) }); run_until_first_tool_call(&thread, cx).await; @@ -1197,7 +1211,7 @@ mod tests { let project = Project::test(fs, [], cx).await; let thread = gemini_acp_thread(project.clone(), "/private/tmp", cx).await; thread - .update(cx, |thread, cx| thread.send("Hello from Zed!", cx)) + .update(cx, |thread, cx| thread.send_raw("Hello from Zed!", cx)) .await .unwrap(); @@ -1235,11 +1249,17 @@ mod tests { thread .update(cx, |thread, cx| { thread.send( - acp::UserMessage { + acp::SendUserMessageParams { chunks: vec![ - "Read the file ".into(), - Path::new("foo.rs").into(), - " and tell me what the content of the println! is".into(), + acp::UserMessageChunk::Text { + text: "Read the file ".into(), + }, + acp::UserMessageChunk::Path { + path: Path::new("foo.rs").into(), + }, + acp::UserMessageChunk::Text { + text: " and tell me what the content of the println! is".into(), + }, ], }, cx, @@ -1283,7 +1303,7 @@ mod tests { let thread = gemini_acp_thread(project.clone(), "/private/tmp", cx).await; thread .update(cx, |thread, cx| { - thread.send( + thread.send_raw( "Read the '/private/tmp/foo' file and tell me what you see.", cx, ) @@ -1317,7 +1337,7 @@ mod tests { let project = Project::test(fs, [path!("/private/tmp").as_ref()], cx).await; let thread = gemini_acp_thread(project.clone(), "/private/tmp", cx).await; let full_turn = thread.update(cx, |thread, cx| { - thread.send(r#"Run `echo "Hello, world!"`"#, cx) + thread.send_raw(r#"Run `echo "Hello, world!"`"#, cx) }); run_until_first_tool_call(&thread, cx).await; @@ -1386,7 +1406,7 @@ mod tests { let project = Project::test(fs, [path!("/private/tmp").as_ref()], cx).await; let thread = gemini_acp_thread(project.clone(), "/private/tmp", cx).await; let full_turn = thread.update(cx, |thread, cx| { - thread.send(r#"Run `echo "Hello, world!"`"#, cx) + thread.send_raw(r#"Run `echo "Hello, world!"`"#, cx) }); let first_tool_call_ix = run_until_first_tool_call(&thread, cx).await; @@ -1427,7 +1447,7 @@ mod tests { thread .update(cx, |thread, cx| { - thread.send(r#"Stop running and say goodbye to me."#, cx) + thread.send_raw(r#"Stop running and say goodbye to me."#, cx) }) .await .unwrap(); diff --git a/crates/agent_ui/src/acp/thread_view.rs b/crates/agent_ui/src/acp/thread_view.rs index 18b8e0212a..2e3bf54837 100644 --- a/crates/agent_ui/src/acp/thread_view.rs +++ b/crates/agent_ui/src/acp/thread_view.rs @@ -53,7 +53,7 @@ pub struct AcpThreadView { auth_task: Option>, expanded_tool_calls: HashSet, expanded_thinking_blocks: HashSet<(usize, usize)>, - message_history: MessageHistory, + message_history: MessageHistory, } enum ThreadState { @@ -294,7 +294,7 @@ impl AcpThreadView { let crease_range = crease.range().to_offset(&snapshot.buffer_snapshot); if crease_range.start > ix { chunks.push(acp::UserMessageChunk::Text { - chunk: text[ix..crease_range.start].to_string(), + text: text[ix..crease_range.start].to_string(), }); } if let Some(abs_path) = project.read(cx).absolute_path(&project_path, cx) { @@ -307,7 +307,9 @@ impl AcpThreadView { if ix < text.len() { let last_chunk = text[ix..].trim(); if !last_chunk.is_empty() { - chunks.push(last_chunk.into()); + chunks.push(acp::UserMessageChunk::Text { + text: last_chunk.into(), + }); } } }) @@ -318,7 +320,7 @@ impl AcpThreadView { } let Some(thread) = self.thread() else { return }; - let message = acp::UserMessage { chunks }; + let message = acp::SendUserMessageParams { chunks }; let task = thread.update(cx, |thread, cx| thread.send(message.clone(), cx)); cx.spawn(async move |this, cx| { @@ -379,7 +381,7 @@ impl AcpThreadView { message_editor: Entity, mention_set: Arc>, project: Entity, - message: Option<&acp::UserMessage>, + message: Option<&acp::SendUserMessageParams>, window: &mut Window, cx: &mut Context, ) { @@ -398,7 +400,7 @@ impl AcpThreadView { for chunk in &message.chunks { match chunk { - acp::UserMessageChunk::Text { chunk } => { + acp::UserMessageChunk::Text { text: chunk } => { text.push_str(&chunk); } acp::UserMessageChunk::Path { path } => {