1use std::{error::Error, fmt, path::Path, rc::Rc};
2
3use agent_client_protocol as acp;
4use agentic_coding_protocol::{self as acp_old, AgentRequest};
5use anyhow::Result;
6use gpui::{AppContext, AsyncApp, Entity, Task};
7use project::Project;
8use ui::App;
9
10use crate::AcpThread;
11
12pub trait AgentConnection {
13 fn new_thread(
14 &self,
15 project: Entity<Project>,
16 cwd: &Path,
17 connection: Rc<dyn AgentConnection>,
18 cx: &mut AsyncApp,
19 ) -> Task<Result<Entity<AcpThread>>>;
20
21 fn authenticate(&self, cx: &mut App) -> Task<Result<()>>;
22
23 fn prompt(&self, params: acp::PromptToolArguments, cx: &mut App) -> Task<Result<()>>;
24
25 fn cancel(&self, cx: &mut App);
26}
27
28#[derive(Debug)]
29pub struct Unauthenticated;
30
31impl Error for Unauthenticated {}
32impl fmt::Display for Unauthenticated {
33 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34 write!(f, "Unauthenticated")
35 }
36}
37
38pub struct OldAcpAgentConnection {
39 pub connection: acp_old::AgentConnection,
40 pub child_status: Task<Result<()>>,
41}
42
43impl AgentConnection for OldAcpAgentConnection {
44 fn new_thread(
45 &self,
46 project: Entity<Project>,
47 _cwd: &Path,
48 connection: Rc<dyn AgentConnection>,
49 cx: &mut AsyncApp,
50 ) -> Task<Result<Entity<AcpThread>>> {
51 let task = self.connection.request_any(
52 acp_old::InitializeParams {
53 protocol_version: acp_old::ProtocolVersion::latest(),
54 }
55 .into_any(),
56 );
57 cx.spawn(async move |cx| {
58 let result = task.await?;
59 let result = acp_old::InitializeParams::response_from_any(result)?;
60
61 if !result.is_authenticated {
62 anyhow::bail!(Unauthenticated)
63 }
64
65 cx.update(|cx| {
66 let thread = cx.new(|cx| {
67 let session_id = acp::SessionId("acp-old-no-id".into());
68 AcpThread::new(connection, "Gemini".into(), project, session_id, cx)
69 });
70 thread
71 })
72 })
73 }
74
75 fn authenticate(&self, cx: &mut App) -> Task<Result<()>> {
76 let task = self
77 .connection
78 .request_any(acp_old::AuthenticateParams.into_any());
79 cx.foreground_executor().spawn(async move {
80 task.await?;
81 Ok(())
82 })
83 }
84
85 fn prompt(&self, params: acp::PromptToolArguments, cx: &mut App) -> Task<Result<()>> {
86 let chunks = params
87 .prompt
88 .into_iter()
89 .filter_map(|block| match block {
90 acp::ContentBlock::Text(text) => {
91 Some(acp_old::UserMessageChunk::Text { text: text.text })
92 }
93 acp::ContentBlock::ResourceLink(link) => Some(acp_old::UserMessageChunk::Path {
94 path: link.uri.into(),
95 }),
96 _ => None,
97 })
98 .collect();
99
100 let task = self
101 .connection
102 .request_any(acp_old::SendUserMessageParams { chunks }.into_any());
103 cx.foreground_executor().spawn(async move {
104 task.await?;
105 anyhow::Ok(())
106 })
107 }
108
109 fn cancel(&self, cx: &mut App) {
110 let task = self
111 .connection
112 .request_any(acp_old::CancelSendMessageParams.into_any());
113 cx.foreground_executor()
114 .spawn(async move {
115 task.await?;
116 anyhow::Ok(())
117 })
118 .detach_and_log_err(cx)
119 }
120}