diff --git a/Cargo.lock b/Cargo.lock index d089d533dcc3d2f632ce459767dba03d35833e65..18bd13d9ca124d69c3d856f56d56e7c5e4f3ada3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -86,7 +86,7 @@ dependencies = [ "rand 0.8.5", "ref-cast", "rope", - "schemars", + "schemars 0.8.22", "serde", "serde_json", "settings", @@ -109,12 +109,18 @@ dependencies = [ name = "agent2" version = "0.1.0" dependencies = [ + "agentic-coding-protocol", "anyhow", + "async-trait", "chrono", + "collections", "futures 0.3.31", "gpui", + "language", "project", "serde_json", + "settings", + "smol", "util", "uuid", "workspace-hack", @@ -137,7 +143,7 @@ dependencies = [ "ollama", "open_ai", "paths", - "schemars", + "schemars 0.8.22", "serde", "serde_json", "serde_json_lenient", @@ -203,7 +209,7 @@ dependencies = [ "release_channel", "rope", "rules_library", - "schemars", + "schemars 0.8.22", "search", "serde", "serde_json", @@ -232,6 +238,20 @@ dependencies = [ "zed_llm_client", ] +[[package]] +name = "agentic-coding-protocol" +version = "0.0.1" +dependencies = [ + "anyhow", + "async-trait", + "chrono", + "futures 0.3.31", + "parking_lot", + "schemars 1.0.1", + "serde", + "serde_json", +] + [[package]] name = "ahash" version = "0.7.8" @@ -428,7 +448,7 @@ dependencies = [ "chrono", "futures 0.3.31", "http_client", - "schemars", + "schemars 0.8.22", "serde", "serde_json", "strum 0.27.1", @@ -755,7 +775,7 @@ dependencies = [ "regex", "reqwest_client", "rust-embed", - "schemars", + "schemars 0.8.22", "serde", "serde_json", "settings", @@ -1216,7 +1236,7 @@ dependencies = [ "log", "paths", "release_channel", - "schemars", + "schemars 0.8.22", "serde", "serde_json", "settings", @@ -1926,7 +1946,7 @@ dependencies = [ "aws-sdk-bedrockruntime", "aws-smithy-types", "futures 0.3.31", - "schemars", + "schemars 0.8.22", "serde", "serde_json", "strum 0.27.1", @@ -2440,7 +2460,7 @@ dependencies = [ "log", "postage", "project", - "schemars", + "schemars 0.8.22", "serde", "serde_derive", "settings", @@ -2896,7 +2916,7 @@ dependencies = [ "release_channel", "rpc", "rustls-pki-types", - "schemars", + "schemars 0.8.22", "serde", "serde_json", "settings", @@ -3171,7 +3191,7 @@ dependencies = [ "release_channel", "rich_text", "rpc", - "schemars", + "schemars 0.8.22", "serde", "serde_derive", "serde_json", @@ -3360,7 +3380,7 @@ dependencies = [ "log", "parking_lot", "postage", - "schemars", + "schemars 0.8.22", "serde", "serde_json", "smol", @@ -4135,7 +4155,7 @@ dependencies = [ "parking_lot", "paths", "proto", - "schemars", + "schemars 0.8.22", "serde", "serde_json", "settings", @@ -4155,7 +4175,7 @@ name = "dap-types" version = "0.0.1" source = "git+https://github.com/zed-industries/dap-types?rev=b40956a7f4d1939da67429d941389ee306a3a308#b40956a7f4d1939da67429d941389ee306a3a308" dependencies = [ - "schemars", + "schemars 0.8.22", "serde", "serde_json", ] @@ -4380,7 +4400,7 @@ dependencies = [ "anyhow", "futures 0.3.31", "http_client", - "schemars", + "schemars 0.8.22", "serde", "serde_json", "workspace-hack", @@ -4833,7 +4853,7 @@ dependencies = [ "rand 0.8.5", "release_channel", "rpc", - "schemars", + "schemars 0.8.22", "serde", "serde_json", "settings", @@ -5307,7 +5327,7 @@ dependencies = [ "release_channel", "remote", "reqwest_client", - "schemars", + "schemars 0.8.22", "semantic_version", "serde", "serde_json", @@ -5508,7 +5528,7 @@ dependencies = [ "picker", "pretty_assertions", "project", - "schemars", + "schemars 0.8.22", "search", "serde", "serde_derive", @@ -6169,7 +6189,7 @@ dependencies = [ "pretty_assertions", "regex", "rope", - "schemars", + "schemars 0.8.22", "serde", "serde_json", "smol", @@ -6211,7 +6231,7 @@ dependencies = [ "indoc", "pretty_assertions", "regex", - "schemars", + "schemars 0.8.22", "serde", "serde_json", "settings", @@ -6254,7 +6274,7 @@ dependencies = [ "postage", "pretty_assertions", "project", - "schemars", + "schemars 0.8.22", "serde", "serde_derive", "serde_json", @@ -7091,7 +7111,7 @@ dependencies = [ "menu", "project", "rope", - "schemars", + "schemars 0.8.22", "serde", "serde_json", "settings", @@ -7112,7 +7132,7 @@ dependencies = [ "anyhow", "futures 0.3.31", "http_client", - "schemars", + "schemars 0.8.22", "serde", "serde_json", "strum 0.27.1", @@ -7213,7 +7233,7 @@ dependencies = [ "reqwest_client", "resvg", "scap", - "schemars", + "schemars 0.8.22", "seahash", "semantic_version", "serde", @@ -8121,7 +8141,7 @@ dependencies = [ "language", "log", "project", - "schemars", + "schemars 0.8.22", "serde", "settings", "theme", @@ -8678,7 +8698,7 @@ dependencies = [ "editor", "gpui", "log", - "schemars", + "schemars 0.8.22", "serde", "settings", "shellexpand 2.1.2", @@ -8873,7 +8893,7 @@ dependencies = [ "rand 0.8.5", "regex", "rpc", - "schemars", + "schemars 0.8.22", "serde", "serde_json", "settings", @@ -8941,7 +8961,7 @@ dependencies = [ "log", "parking_lot", "proto", - "schemars", + "schemars 0.8.22", "serde", "serde_json", "smol", @@ -8987,7 +9007,7 @@ dependencies = [ "project", "proto", "release_channel", - "schemars", + "schemars 0.8.22", "serde", "serde_json", "settings", @@ -9083,7 +9103,7 @@ dependencies = [ "regex", "rope", "rust-embed", - "schemars", + "schemars 0.8.22", "serde", "serde_json", "serde_json_lenient", @@ -9474,7 +9494,7 @@ dependencies = [ "anyhow", "futures 0.3.31", "http_client", - "schemars", + "schemars 0.8.22", "serde", "serde_json", "workspace-hack", @@ -9580,7 +9600,7 @@ dependencies = [ "parking_lot", "postage", "release_channel", - "schemars", + "schemars 0.8.22", "serde", "serde_json", "smol", @@ -10039,7 +10059,7 @@ dependencies = [ "anyhow", "futures 0.3.31", "http_client", - "schemars", + "schemars 0.8.22", "serde", "serde_json", "strum 0.27.1", @@ -10822,7 +10842,7 @@ dependencies = [ "anyhow", "futures 0.3.31", "http_client", - "schemars", + "schemars 0.8.22", "serde", "serde_json", "workspace-hack", @@ -10893,7 +10913,7 @@ dependencies = [ "anyhow", "futures 0.3.31", "http_client", - "schemars", + "schemars 0.8.22", "serde", "serde_json", "strum 0.27.1", @@ -10907,7 +10927,7 @@ dependencies = [ "anyhow", "futures 0.3.31", "http_client", - "schemars", + "schemars 0.8.22", "serde", "serde_json", "workspace-hack", @@ -11083,7 +11103,7 @@ dependencies = [ "outline", "pretty_assertions", "project", - "schemars", + "schemars 0.8.22", "search", "serde", "serde_json", @@ -11856,7 +11876,7 @@ dependencies = [ "env_logger 0.11.8", "gpui", "menu", - "schemars", + "schemars 0.8.22", "serde", "serde_json", "ui", @@ -12285,7 +12305,7 @@ dependencies = [ "release_channel", "remote", "rpc", - "schemars", + "schemars 0.8.22", "serde", "serde_json", "settings", @@ -12328,7 +12348,7 @@ dependencies = [ "menu", "pretty_assertions", "project", - "schemars", + "schemars 0.8.22", "search", "serde", "serde_derive", @@ -12984,7 +13004,7 @@ dependencies = [ "project", "release_channel", "remote", - "schemars", + "schemars 0.8.22", "serde", "serde_json", "settings", @@ -13172,7 +13192,7 @@ dependencies = [ "prost 0.9.0", "release_channel", "rpc", - "schemars", + "schemars 0.8.22", "serde", "serde_json", "shlex", @@ -13287,7 +13307,7 @@ dependencies = [ "picker", "project", "runtimelib", - "schemars", + "schemars 0.8.22", "serde", "serde_json", "settings", @@ -14057,7 +14077,7 @@ dependencies = [ "anyhow", "clap", "env_logger 0.11.8", - "schemars", + "schemars 0.8.22", "serde", "serde_json", "theme", @@ -14072,7 +14092,21 @@ checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" dependencies = [ "dyn-clone", "indexmap", - "schemars_derive", + "schemars_derive 0.8.22", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe8c9d1c68d67dd9f97ecbc6f932b60eb289c5dbddd8aa1405484a8fd2fcd984" +dependencies = [ + "chrono", + "dyn-clone", + "ref-cast", + "schemars_derive 1.0.1", "serde", "serde_json", ] @@ -14089,6 +14123,18 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "schemars_derive" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ca9fcb757952f8e8629b9ab066fc62da523c46c2b247b1708a3be06dd82530b" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 2.0.101", +] + [[package]] name = "scoped-tls" version = "1.0.1" @@ -14261,7 +14307,7 @@ dependencies = [ "language", "menu", "project", - "schemars", + "schemars 0.8.22", "serde", "serde_json", "settings", @@ -14562,7 +14608,7 @@ dependencies = [ "pretty_assertions", "release_channel", "rust-embed", - "schemars", + "schemars 0.8.22", "serde", "serde_derive", "serde_json", @@ -14586,7 +14632,7 @@ dependencies = [ "fs", "gpui", "log", - "schemars", + "schemars 0.8.22", "serde", "settings", "theme", @@ -14890,7 +14936,7 @@ dependencies = [ "indoc", "parking_lot", "paths", - "schemars", + "schemars 0.8.22", "serde", "serde_json_lenient", "snippet", @@ -15726,7 +15772,7 @@ dependencies = [ "menu", "picker", "project", - "schemars", + "schemars 0.8.22", "serde", "serde_json", "settings", @@ -15807,7 +15853,7 @@ dependencies = [ "parking_lot", "pretty_assertions", "proto", - "schemars", + "schemars 0.8.22", "serde", "serde_json", "serde_json_lenient", @@ -15913,7 +15959,7 @@ dependencies = [ "rand 0.8.5", "regex", "release_channel", - "schemars", + "schemars 0.8.22", "serde", "serde_derive", "settings", @@ -15960,7 +16006,7 @@ dependencies = [ "project", "rand 0.8.5", "regex", - "schemars", + "schemars 0.8.22", "search", "serde", "serde_json", @@ -16015,7 +16061,7 @@ dependencies = [ "palette", "parking_lot", "refineable", - "schemars", + "schemars 0.8.22", "serde", "serde_derive", "serde_json", @@ -16302,7 +16348,7 @@ dependencies = [ "project", "remote", "rpc", - "schemars", + "schemars 0.8.22", "serde", "settings", "smallvec", @@ -17480,7 +17526,7 @@ dependencies = [ "project_panel", "regex", "release_channel", - "schemars", + "schemars 0.8.22", "search", "serde", "serde_derive", @@ -18333,7 +18379,7 @@ dependencies = [ "language", "picker", "project", - "schemars", + "schemars 0.8.22", "serde", "settings", "telemetry", @@ -19375,7 +19421,7 @@ dependencies = [ "postage", "project", "remote", - "schemars", + "schemars 0.8.22", "serde", "serde_json", "session", @@ -19607,7 +19653,7 @@ dependencies = [ "pretty_assertions", "rand 0.8.5", "rpc", - "schemars", + "schemars 0.8.22", "serde", "serde_json", "settings", @@ -20069,7 +20115,7 @@ name = "zed_actions" version = "0.1.0" dependencies = [ "gpui", - "schemars", + "schemars 0.8.22", "serde", "uuid", "workspace-hack", @@ -20396,7 +20442,7 @@ version = "0.1.0" dependencies = [ "anyhow", "gpui", - "schemars", + "schemars 0.8.22", "serde", "settings", "workspace-hack", diff --git a/crates/agent2/Cargo.toml b/crates/agent2/Cargo.toml index b61acffb7817af658f50e31460bf17690011a1ec..2b2404171619ecfe6a0e68626dfaaf8678f6db44 100644 --- a/crates/agent2/Cargo.toml +++ b/crates/agent2/Cargo.toml @@ -15,18 +15,27 @@ doctest = false [features] test-support = [ "gpui/test-support", + "project/test-support", ] [dependencies] anyhow.workspace = true +async-trait.workspace = true +collections.workspace = true chrono.workspace = true futures.workspace = true +language.workspace = true gpui.workspace = true project.workspace = true +smol.workspace = true uuid.workspace = true workspace-hack.workspace = true +util.workspace = true +agentic-coding-protocol = { path = "../../../agentic-coding-protocol" } [dev-dependencies] gpui = { workspace = true, "features" = ["test-support"] } +project = { workspace = true, "features" = ["test-support"] } serde_json.workspace = true util.workspace = true +settings.workspace = true diff --git a/crates/agent2/src/acp.rs b/crates/agent2/src/acp.rs new file mode 100644 index 0000000000000000000000000000000000000000..6ee02fd0d1971748551dba717d966458095108c2 --- /dev/null +++ b/crates/agent2/src/acp.rs @@ -0,0 +1,105 @@ +use crate::{Agent, AgentThread, AgentThreadEntry, AgentThreadSummary, ResponseEvent, ThreadId}; +use agentic_coding_protocol as acp; +use anyhow::{Context as _, Result}; +use async_trait::async_trait; +use futures::channel::mpsc::UnboundedReceiver; +use gpui::{AppContext, AsyncApp, Entity, Task}; +use project::Project; +use smol::process::Child; +use util::ResultExt; + +pub struct AcpAgent { + connection: acp::Connection, + _handler_task: Task<()>, + _io_task: Task<()>, +} + +struct AcpClientDelegate { + project: Entity, + cx: AsyncApp, + // sent_buffer_versions: HashMap, HashMap>, +} + +#[async_trait] +impl acp::Client for AcpClientDelegate { + async fn read_file(&self, request: acp::ReadFileParams) -> Result { + let cx = &mut self.cx.clone(); + let buffer = self + .project + .update(cx, |project, cx| { + let path = project + .project_path_for_absolute_path(request.path, cx) + .context("Failed to get project path")?; + project.open_buffer(path, cx) + })? + .await?; + + anyhow::Ok(buffer.update(cx, |buffer, cx| acp::ReadFileResponse { + content: buffer.text(), + // todo! + version: 0, + })) + } +} + +impl AcpAgent { + pub fn stdio(process: Child, project: Entity, cx: AsyncApp) -> Self { + let stdin = process.stdin.expect("process didn't have stdin"); + let stdout = process.stdout.expect("process didn't have stdout"); + + let (connection, handler_fut, io_fut) = + acp::Connection::client_to_agent(AcpClientDelegate { project, cx }, stdin, stdout); + + let io_task = cx.background_spawn(async move { + io_fut.await.log_err(); + }); + + Self { + connection, + _handler_task: cx.foreground_executor().spawn(handler_fut), + _io_task: io_task, + } + } +} + +impl Agent for AcpAgent { + type Thread = AcpAgentThread; + + async fn threads(&self) -> Result> { + let threads = self.connection.request(acp::ListThreadsParams).await?; + threads + .threads + .into_iter() + .map(|thread| { + Ok(AgentThreadSummary { + id: ThreadId(thread.id.0), + title: thread.title, + created_at: thread.created_at, + }) + }) + .collect() + } + + async fn create_thread(&self) -> Result { + todo!() + } + + async fn open_thread(&self, id: crate::ThreadId) -> Result { + todo!() + } +} + +struct AcpAgentThread {} + +impl AgentThread for AcpAgentThread { + async fn entries(&self) -> Result> { + todo!() + } + + async fn send( + &self, + message: crate::Message, + ) -> Result>> { + todo!() + } +} diff --git a/crates/agent2/src/agent2.rs b/crates/agent2/src/agent2.rs index 782232cf07c3bc063c7eccf6fa78d636fe196a22..188411bbad8e8f1dda7995d26464e3777551f653 100644 --- a/crates/agent2/src/agent2.rs +++ b/crates/agent2/src/agent2.rs @@ -1,3 +1,5 @@ +mod acp; + use anyhow::{Result, anyhow}; use chrono::{DateTime, Utc}; use futures::{ @@ -9,7 +11,6 @@ use futures::{ use gpui::{AppContext, AsyncApp, Context, Entity, Task, WeakEntity}; use project::Project; use std::{future, ops::Range, path::PathBuf, pin::pin, sync::Arc}; -use uuid::Uuid; pub trait Agent: 'static { type Thread: AgentThread; @@ -53,7 +54,7 @@ impl ReadFileRequest { } } -pub struct ThreadId(Uuid); +pub struct ThreadId(String); pub struct FileVersion(u64); @@ -363,14 +364,27 @@ impl Thread { #[cfg(test)] mod tests { use super::*; + use agentic_coding_protocol::Client; use gpui::{BackgroundExecutor, TestAppContext}; use project::FakeFs; use serde_json::json; - use std::path::Path; + use settings::SettingsStore; + use smol::process::Child; + use std::env; use util::path; + fn init_test(cx: &mut TestAppContext) { + cx.update(|cx| { + let settings_store = SettingsStore::test(cx); + cx.set_global(settings_store); + Project::init_settings(cx); + }); + } + #[gpui::test] async fn test_basic(cx: &mut TestAppContext) { + init_test(cx); + cx.executor().allow_parking(); let fs = FakeFs::new(cx.executor()); @@ -380,19 +394,43 @@ mod tests { ) .await; let project = Project::test(fs, [path!("/test").as_ref()], cx).await; - let agent = GeminiAgent::start("~/gemini-cli/change-me.js", &cx.executor()) - .await - .unwrap(); + let agent = GeminiAgent::start(&cx.executor()).await.unwrap(); let thread_store = ThreadStore::load(Arc::new(agent), project, &mut cx.to_async()) .await .unwrap(); } - struct GeminiAgent {} + struct TestClient; + + #[async_trait] + impl Client for TestClient { + async fn read_file(&self, _request: ReadFileParams) -> Result { + Ok(ReadFileResponse { + version: FileVersion(0), + content: "the content".into(), + }) + } + } + + struct GeminiAgent { + child: Child, + _task: Task<()>, + } impl GeminiAgent { - pub fn start(path: impl AsRef, executor: &BackgroundExecutor) -> Task> { - executor.spawn(async move { Ok(GeminiAgent {}) }) + pub fn start(executor: &BackgroundExecutor) -> Task> { + executor.spawn(async move { + // todo! + let child = util::command::new_smol_command("node") + .arg("../gemini-cli/packages/cli") + .arg("--acp") + .env("GEMINI_API_KEY", env::var("GEMINI_API_KEY").unwrap()) + .kill_on_drop(true) + .spawn() + .unwrap(); + + Ok(GeminiAgent { child }) + }) } }