connection.rs

  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}