acp.rs

  1use std::path::Path;
  2
  3use crate::{Agent, AgentThread, AgentThreadEntry, AgentThreadSummary, ResponseEvent, ThreadId};
  4use agentic_coding_protocol as acp;
  5use anyhow::{Context as _, Result};
  6use async_trait::async_trait;
  7use futures::channel::mpsc::UnboundedReceiver;
  8use gpui::{AppContext, AsyncApp, Entity, Task};
  9use project::Project;
 10use smol::process::Child;
 11use util::ResultExt;
 12
 13pub struct AcpAgent {
 14    connection: acp::AgentConnection,
 15    _handler_task: Task<()>,
 16    _io_task: Task<()>,
 17}
 18
 19struct AcpClientDelegate {
 20    project: Entity<Project>,
 21    cx: AsyncApp,
 22    // sent_buffer_versions: HashMap<Entity<Buffer>, HashMap<u64, BufferSnapshot>>,
 23}
 24
 25#[async_trait(?Send)]
 26impl acp::Client for AcpClientDelegate {
 27    async fn read_file(&self, request: acp::ReadFileParams) -> Result<acp::ReadFileResponse> {
 28        let cx = &mut self.cx.clone();
 29        let buffer = self
 30            .project
 31            .update(cx, |project, cx| {
 32                let path = project
 33                    .project_path_for_absolute_path(Path::new(&request.path), cx)
 34                    .context("Failed to get project path")?;
 35                anyhow::Ok(project.open_buffer(path, cx))
 36            })??
 37            .await?;
 38
 39        buffer.update(cx, |buffer, _| acp::ReadFileResponse {
 40            content: buffer.text(),
 41            version: acp::FileVersion(0),
 42        })
 43    }
 44
 45    async fn glob_search(&self, request: acp::GlobSearchParams) -> Result<acp::GlobSearchResponse> {
 46        todo!()
 47    }
 48}
 49
 50impl AcpAgent {
 51    pub fn stdio(mut process: Child, project: Entity<Project>, cx: AsyncApp) -> Self {
 52        let stdin = process.stdin.take().expect("process didn't have stdin");
 53        let stdout = process.stdout.take().expect("process didn't have stdout");
 54
 55        let (connection, handler_fut, io_fut) = acp::AgentConnection::connect_to_agent(
 56            AcpClientDelegate {
 57                project,
 58                cx: cx.clone(),
 59            },
 60            stdin,
 61            stdout,
 62        );
 63
 64        let io_task = cx.background_spawn(async move {
 65            io_fut.await.log_err();
 66            process.status().await.log_err();
 67        });
 68
 69        Self {
 70            connection,
 71            _handler_task: cx.foreground_executor().spawn(handler_fut),
 72            _io_task: io_task,
 73        }
 74    }
 75}
 76
 77impl Agent for AcpAgent {
 78    type Thread = AcpAgentThread;
 79
 80    async fn threads(&self) -> Result<Vec<AgentThreadSummary>> {
 81        let threads = self.connection.request(acp::ListThreadsParams).await?;
 82        threads
 83            .threads
 84            .into_iter()
 85            .map(|thread| {
 86                Ok(AgentThreadSummary {
 87                    id: ThreadId(thread.id.0),
 88                    title: thread.title,
 89                    created_at: thread.created_at,
 90                })
 91            })
 92            .collect()
 93    }
 94
 95    async fn create_thread(&self) -> Result<Self::Thread> {
 96        todo!()
 97    }
 98
 99    async fn open_thread(&self, id: crate::ThreadId) -> Result<Self::Thread> {
100        todo!()
101    }
102}
103
104pub struct AcpAgentThread {}
105
106impl AgentThread for AcpAgentThread {
107    async fn entries(&self) -> Result<Vec<AgentThreadEntry>> {
108        todo!()
109    }
110
111    async fn send(
112        &self,
113        message: crate::Message,
114    ) -> Result<UnboundedReceiver<Result<ResponseEvent>>> {
115        todo!()
116    }
117}