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