acp.rs

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