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}