diff --git a/Cargo.lock b/Cargo.lock
index 59e444f1f8..540e3039ef 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3,10 +3,9 @@
version = 4
[[package]]
-name = "acp"
+name = "acp_thread"
version = "0.1.0"
dependencies = [
- "agent_servers",
"agentic-coding-protocol",
"anyhow",
"assistant_tool",
@@ -21,6 +20,7 @@ dependencies = [
"language",
"markdown",
"project",
+ "serde",
"serde_json",
"settings",
"smol",
@@ -139,16 +139,29 @@ dependencies = [
name = "agent_servers"
version = "0.1.0"
dependencies = [
+ "acp_thread",
+ "agentic-coding-protocol",
"anyhow",
"collections",
+ "context_server",
+ "env_logger 0.11.8",
"futures 0.3.31",
"gpui",
+ "indoc",
+ "itertools 0.14.0",
+ "language",
+ "log",
"paths",
"project",
"schemars",
"serde",
+ "serde_json",
"settings",
+ "smol",
+ "tempfile",
+ "ui",
"util",
+ "watch",
"which 6.0.3",
"workspace-hack",
]
@@ -176,7 +189,7 @@ dependencies = [
name = "agent_ui"
version = "0.1.0"
dependencies = [
- "acp",
+ "acp_thread",
"agent",
"agent_servers",
"agent_settings",
@@ -3411,12 +3424,14 @@ dependencies = [
"futures 0.3.31",
"gpui",
"log",
+ "net",
"parking_lot",
"postage",
"schemars",
"serde",
"serde_json",
"smol",
+ "tempfile",
"url",
"util",
"workspace-hack",
@@ -10288,6 +10303,17 @@ dependencies = [
"uuid",
]
+[[package]]
+name = "nc"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "futures 0.3.31",
+ "net",
+ "smol",
+ "workspace-hack",
+]
+
[[package]]
name = "ndk"
version = "0.8.0"
@@ -20171,6 +20197,7 @@ dependencies = [
"menu",
"migrator",
"mimalloc",
+ "nc",
"nix 0.29.0",
"node_runtime",
"notifications",
diff --git a/Cargo.toml b/Cargo.toml
index afb47c006e..1c79f4c1c8 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -2,7 +2,7 @@
resolver = "2"
members = [
"crates/activity_indicator",
- "crates/acp",
+ "crates/acp_thread",
"crates/agent_ui",
"crates/agent",
"crates/agent_settings",
@@ -102,6 +102,7 @@ members = [
"crates/migrator",
"crates/mistral",
"crates/multi_buffer",
+ "crates/nc",
"crates/net",
"crates/node_runtime",
"crates/notifications",
@@ -219,7 +220,7 @@ edition = "2024"
# Workspace member crates
#
-acp = { path = "crates/acp" }
+acp_thread = { path = "crates/acp_thread" }
agent = { path = "crates/agent" }
activity_indicator = { path = "crates/activity_indicator" }
agent_ui = { path = "crates/agent_ui" }
@@ -317,6 +318,7 @@ menu = { path = "crates/menu" }
migrator = { path = "crates/migrator" }
mistral = { path = "crates/mistral" }
multi_buffer = { path = "crates/multi_buffer" }
+nc = { path = "crates/nc" }
net = { path = "crates/net" }
node_runtime = { path = "crates/node_runtime" }
notifications = { path = "crates/notifications" }
@@ -406,7 +408,7 @@ zlog_settings = { path = "crates/zlog_settings" }
# External crates
#
-agentic-coding-protocol = { version = "0.0.9" }
+agentic-coding-protocol = "0.0.9"
aho-corasick = "1.1"
alacritty_terminal = { git = "https://github.com/zed-industries/alacritty.git", branch = "add-hush-login-flag" }
any_vec = "0.14"
diff --git a/assets/icons/ai_claude.svg b/assets/icons/ai_claude.svg
new file mode 100644
index 0000000000..423a963eba
--- /dev/null
+++ b/assets/icons/ai_claude.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/keymaps/default-linux.json b/assets/keymaps/default-linux.json
index da4d79eca1..b52b6c614d 100644
--- a/assets/keymaps/default-linux.json
+++ b/assets/keymaps/default-linux.json
@@ -269,10 +269,10 @@
}
},
{
- "context": "AgentPanel && acp_thread",
+ "context": "AgentPanel && external_agent_thread",
"use_key_equivalents": true,
"bindings": {
- "ctrl-n": "agent::NewAcpThread",
+ "ctrl-n": "agent::NewExternalAgentThread",
"ctrl-alt-t": "agent::NewThread"
}
},
diff --git a/assets/keymaps/default-macos.json b/assets/keymaps/default-macos.json
index 962760098b..240b42fd1f 100644
--- a/assets/keymaps/default-macos.json
+++ b/assets/keymaps/default-macos.json
@@ -310,10 +310,10 @@
}
},
{
- "context": "AgentPanel && acp_thread",
+ "context": "AgentPanel && external_agent_thread",
"use_key_equivalents": true,
"bindings": {
- "cmd-n": "agent::NewAcpThread",
+ "cmd-n": "agent::NewExternalAgentThread",
"cmd-alt-t": "agent::NewThread"
}
},
diff --git a/crates/acp/Cargo.toml b/crates/acp_thread/Cargo.toml
similarity index 92%
rename from crates/acp/Cargo.toml
rename to crates/acp_thread/Cargo.toml
index 1570aeaef0..b44c25ccc9 100644
--- a/crates/acp/Cargo.toml
+++ b/crates/acp_thread/Cargo.toml
@@ -1,5 +1,5 @@
[package]
-name = "acp"
+name = "acp_thread"
version = "0.1.0"
edition.workspace = true
publish.workspace = true
@@ -9,15 +9,13 @@ license = "GPL-3.0-or-later"
workspace = true
[lib]
-path = "src/acp.rs"
+path = "src/acp_thread.rs"
doctest = false
[features]
test-support = ["gpui/test-support", "project/test-support"]
-gemini = []
[dependencies]
-agent_servers.workspace = true
agentic-coding-protocol.workspace = true
anyhow.workspace = true
assistant_tool.workspace = true
@@ -29,6 +27,8 @@ itertools.workspace = true
language.workspace = true
markdown.workspace = true
project.workspace = true
+serde.workspace = true
+serde_json.workspace = true
settings.workspace = true
smol.workspace = true
ui.workspace = true
@@ -41,7 +41,6 @@ env_logger.workspace = true
gpui = { workspace = true, "features" = ["test-support"] }
indoc.workspace = true
project = { workspace = true, "features" = ["test-support"] }
-serde_json.workspace = true
tempfile.workspace = true
util.workspace = true
settings.workspace = true
diff --git a/crates/acp/LICENSE-GPL b/crates/acp_thread/LICENSE-GPL
similarity index 100%
rename from crates/acp/LICENSE-GPL
rename to crates/acp_thread/LICENSE-GPL
diff --git a/crates/acp/src/acp.rs b/crates/acp_thread/src/acp_thread.rs
similarity index 75%
rename from crates/acp/src/acp.rs
rename to crates/acp_thread/src/acp_thread.rs
index a7e72b0c2d..1e3947351a 100644
--- a/crates/acp/src/acp.rs
+++ b/crates/acp_thread/src/acp_thread.rs
@@ -1,7 +1,12 @@
+mod connection;
+pub use connection::*;
+
pub use acp::ToolCallId;
-use agent_servers::AgentServer;
-use agentic_coding_protocol::{self as acp, ToolCallLocation, UserMessageChunk};
-use anyhow::{Context as _, Result, anyhow};
+use agentic_coding_protocol::{
+ self as acp, AgentRequest, ProtocolVersion, ToolCallConfirmationOutcome, ToolCallLocation,
+ UserMessageChunk,
+};
+use anyhow::{Context as _, Result};
use assistant_tool::ActionLog;
use buffer_diff::BufferDiff;
use editor::{Bias, MultiBuffer, PathKey};
@@ -97,7 +102,7 @@ pub struct AssistantMessage {
}
impl AssistantMessage {
- fn to_markdown(&self, cx: &App) -> String {
+ pub fn to_markdown(&self, cx: &App) -> String {
format!(
"## Assistant\n\n{}\n\n",
self.chunks
@@ -455,9 +460,8 @@ pub struct AcpThread {
action_log: Entity,
shared_buffers: HashMap, BufferSnapshot>,
send_task: Option>,
- connection: Arc,
+ connection: Arc,
child_status: Option>>,
- _io_task: Task<()>,
}
pub enum AcpThreadEvent {
@@ -476,7 +480,11 @@ pub enum ThreadStatus {
#[derive(Debug, Clone)]
pub enum LoadError {
- Unsupported { current_version: SharedString },
+ Unsupported {
+ error_message: SharedString,
+ upgrade_message: SharedString,
+ upgrade_command: String,
+ },
Exited(i32),
Other(SharedString),
}
@@ -484,13 +492,7 @@ pub enum LoadError {
impl Display for LoadError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
- LoadError::Unsupported { current_version } => {
- write!(
- f,
- "Your installed version of Gemini {} doesn't support the Agentic Coding Protocol (ACP).",
- current_version
- )
- }
+ LoadError::Unsupported { error_message, .. } => write!(f, "{}", error_message),
LoadError::Exited(status) => write!(f, "Server exited with status {}", status),
LoadError::Other(msg) => write!(f, "{}", msg),
}
@@ -500,75 +502,38 @@ impl Display for LoadError {
impl Error for LoadError {}
impl AcpThread {
- pub async fn spawn(
- server: impl AgentServer + 'static,
- root_dir: &Path,
+ pub fn new(
+ connection: impl AgentConnection + 'static,
+ title: SharedString,
+ child_status: Option>>,
project: Entity,
- cx: &mut AsyncApp,
- ) -> Result> {
- let command = match server.command(&project, cx).await {
- Ok(command) => command,
- Err(e) => return Err(anyhow!(LoadError::Other(format!("{e}").into()))),
- };
+ cx: &mut Context,
+ ) -> Self {
+ let action_log = cx.new(|_| ActionLog::new(project.clone()));
- let mut child = util::command::new_smol_command(&command.path)
- .args(command.args.iter())
- .current_dir(root_dir)
- .stdin(std::process::Stdio::piped())
- .stdout(std::process::Stdio::piped())
- .stderr(std::process::Stdio::inherit())
- .kill_on_drop(true)
- .spawn()?;
+ Self {
+ action_log,
+ shared_buffers: Default::default(),
+ entries: Default::default(),
+ title,
+ project,
+ send_task: None,
+ connection: Arc::new(connection),
+ child_status,
+ }
+ }
- let stdin = child.stdin.take().unwrap();
- let stdout = child.stdout.take().unwrap();
-
- cx.new(|cx| {
- let foreground_executor = cx.foreground_executor().clone();
-
- let (connection, io_fut) = acp::AgentConnection::connect_to_agent(
- AcpClientDelegate::new(cx.entity().downgrade(), cx.to_async()),
- stdin,
- stdout,
- move |fut| foreground_executor.spawn(fut).detach(),
- );
-
- let io_task = cx.background_spawn(async move {
- io_fut.await.log_err();
- });
-
- let child_status = cx.background_spawn(async move {
- match child.status().await {
- Err(e) => Err(anyhow!(e)),
- Ok(result) if result.success() => Ok(()),
- Ok(result) => {
- if let Some(version) = server.version(&command).await.log_err()
- && !version.supported
- {
- Err(anyhow!(LoadError::Unsupported {
- current_version: version.current_version
- }))
- } else {
- Err(anyhow!(LoadError::Exited(result.code().unwrap_or(-127))))
- }
- }
- }
- });
-
- let action_log = cx.new(|_| ActionLog::new(project.clone()));
-
- Self {
- action_log,
- shared_buffers: Default::default(),
- entries: Default::default(),
- title: "ACP Thread".into(),
- project,
- send_task: None,
- connection: Arc::new(connection),
- child_status: Some(child_status),
- _io_task: io_task,
- }
- })
+ /// Send a request to the agent and wait for a response.
+ pub fn request(
+ &self,
+ params: R,
+ ) -> impl use + Future