Add Native Agent to UI and implement NativeAgentServer

- Created NativeAgentServer that implements AgentServer trait
- Added NativeAgent to ExternalAgent enum
- Added Native Agent option to both the + menu and empty state view
- Added necessary dependencies (agent_servers, ui) to agent2 crate
- Added agent2 dependency to agent_ui crate
- Temporarily removed feature flag check for testing
This commit is contained in:
Nathan Sobo 2025-08-02 09:17:26 -06:00
parent 4f2d6a9ea9
commit bc1f861d3f
7 changed files with 188 additions and 89 deletions

3
Cargo.lock generated
View file

@ -155,6 +155,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"acp_thread", "acp_thread",
"agent-client-protocol", "agent-client-protocol",
"agent_servers",
"anyhow", "anyhow",
"assistant_tool", "assistant_tool",
"assistant_tools", "assistant_tools",
@ -182,6 +183,7 @@ dependencies = [
"settings", "settings",
"smol", "smol",
"thiserror 2.0.12", "thiserror 2.0.12",
"ui",
"util", "util",
"uuid", "uuid",
"worktree", "worktree",
@ -250,6 +252,7 @@ dependencies = [
"acp_thread", "acp_thread",
"agent", "agent",
"agent-client-protocol", "agent-client-protocol",
"agent2",
"agent_servers", "agent_servers",
"agent_settings", "agent_settings",
"ai_onboarding", "ai_onboarding",

View file

@ -14,6 +14,7 @@ workspace = true
[dependencies] [dependencies]
acp_thread.workspace = true acp_thread.workspace = true
agent-client-protocol.workspace = true agent-client-protocol.workspace = true
agent_servers.workspace = true
anyhow.workspace = true anyhow.workspace = true
assistant_tool.workspace = true assistant_tool.workspace = true
assistant_tools.workspace = true assistant_tools.workspace = true
@ -36,6 +37,7 @@ serde_json.workspace = true
settings.workspace = true settings.workspace = true
smol.workspace = true smol.workspace = true
thiserror.workspace = true thiserror.workspace = true
ui.workspace = true
util.workspace = true util.workspace = true
uuid.workspace = true uuid.workspace = true
worktree.workspace = true worktree.workspace = true

View file

@ -1,4 +1,5 @@
mod agent; mod agent;
mod native_agent_server;
mod prompts; mod prompts;
mod templates; mod templates;
mod thread; mod thread;
@ -8,4 +9,5 @@ mod tools;
mod tests; mod tests;
pub use agent::*; pub use agent::*;
pub use native_agent_server::NativeAgentServer;
pub use thread::*; pub use thread::*;

View file

@ -0,0 +1,51 @@
use std::path::Path;
use std::rc::Rc;
use agent_servers::AgentServer;
use anyhow::Result;
use gpui::{App, AppContext, Entity, Task};
use project::Project;
use crate::{templates::Templates, NativeAgent, NativeAgentConnection};
#[derive(Clone)]
pub struct NativeAgentServer;
impl AgentServer for NativeAgentServer {
fn name(&self) -> &'static str {
"Native Agent"
}
fn empty_state_headline(&self) -> &'static str {
"Native Agent"
}
fn empty_state_message(&self) -> &'static str {
"How can I help you today?"
}
fn logo(&self) -> ui::IconName {
// Using the ZedAssistant icon as it's the native built-in agent
ui::IconName::ZedAssistant
}
fn connect(
&self,
_root_dir: &Path,
_project: &Entity<Project>,
cx: &mut App,
) -> Task<Result<Rc<dyn acp_thread::AgentConnection>>> {
cx.spawn(async move |cx| {
// Create templates (you might want to load these from files or resources)
let templates = Templates::new();
// Create the native agent
let agent = cx.update(|cx| cx.new(|_| NativeAgent::new(templates)))?;
// Create the connection wrapper
let connection = NativeAgentConnection(agent);
Ok(Rc::new(connection) as Rc<dyn acp_thread::AgentConnection>)
})
}
}

View file

@ -19,6 +19,7 @@ test-support = ["gpui/test-support", "language/test-support"]
acp_thread.workspace = true acp_thread.workspace = true
agent-client-protocol.workspace = true agent-client-protocol.workspace = true
agent.workspace = true agent.workspace = true
agent2.workspace = true
agent_servers.workspace = true agent_servers.workspace = true
agent_settings.workspace = true agent_settings.workspace = true
ai_onboarding.workspace = true ai_onboarding.workspace = true

View file

@ -1954,40 +1954,54 @@ impl AgentPanel {
this this
} }
}) })
.when(cx.has_flag::<feature_flags::AcpFeatureFlag>(), |this| { // Temporarily removed feature flag check for testing
this.separator() // .when(cx.has_flag::<feature_flags::AcpFeatureFlag>(), |this| {
.header("External Agents") // this
.item( .separator()
ContextMenuEntry::new("New Gemini Thread") .header("External Agents")
.icon(IconName::AiGemini) .item(
.icon_color(Color::Muted) ContextMenuEntry::new("New Gemini Thread")
.handler(move |window, cx| { .icon(IconName::AiGemini)
window.dispatch_action( .icon_color(Color::Muted)
NewExternalAgentThread { .handler(move |window, cx| {
agent: Some(crate::ExternalAgent::Gemini), window.dispatch_action(
} NewExternalAgentThread {
.boxed_clone(), agent: Some(crate::ExternalAgent::Gemini),
cx, }
); .boxed_clone(),
}), cx,
) );
.item( }),
ContextMenuEntry::new("New Claude Code Thread") )
.icon(IconName::AiClaude) .item(
.icon_color(Color::Muted) ContextMenuEntry::new("New Claude Code Thread")
.handler(move |window, cx| { .icon(IconName::AiClaude)
window.dispatch_action( .icon_color(Color::Muted)
NewExternalAgentThread { .handler(move |window, cx| {
agent: Some( window.dispatch_action(
crate::ExternalAgent::ClaudeCode, NewExternalAgentThread {
), agent: Some(crate::ExternalAgent::ClaudeCode),
} }
.boxed_clone(), .boxed_clone(),
cx, cx,
); );
}), }),
) )
}); .item(
ContextMenuEntry::new("New Native Agent Thread")
.icon(IconName::ZedAssistant)
.icon_color(Color::Muted)
.handler(move |window, cx| {
window.dispatch_action(
NewExternalAgentThread {
agent: Some(crate::ExternalAgent::NativeAgent),
}
.boxed_clone(),
cx,
);
}),
);
// });
menu menu
})) }))
} }
@ -2594,63 +2608,87 @@ impl AgentPanel {
), ),
), ),
) )
.when(cx.has_flag::<feature_flags::AcpFeatureFlag>(), |this| { // Temporarily removed feature flag check for testing
this.child( // .when(cx.has_flag::<feature_flags::AcpFeatureFlag>(), |this| {
h_flex() // this
.w_full() .child(
.gap_2() h_flex()
.child( .w_full()
NewThreadButton::new( .gap_2()
"new-gemini-thread-btn", .child(
"New Gemini Thread", NewThreadButton::new(
IconName::AiGemini, "new-gemini-thread-btn",
) "New Gemini Thread",
// .keybinding(KeyBinding::for_action_in( IconName::AiGemini,
// &OpenHistory,
// &self.focus_handle(cx),
// window,
// cx,
// ))
.on_click(
|window, cx| {
window.dispatch_action(
Box::new(NewExternalAgentThread {
agent: Some(
crate::ExternalAgent::Gemini,
),
}),
cx,
)
},
),
) )
.child( // .keybinding(KeyBinding::for_action_in(
NewThreadButton::new( // &OpenHistory,
"new-claude-thread-btn", // &self.focus_handle(cx),
"New Claude Code Thread", // window,
IconName::AiClaude, // cx,
) // ))
// .keybinding(KeyBinding::for_action_in( .on_click(
// &OpenHistory, |window, cx| {
// &self.focus_handle(cx), window.dispatch_action(
// window, Box::new(NewExternalAgentThread {
// cx, agent: Some(crate::ExternalAgent::Gemini),
// )) }),
.on_click( cx,
|window, cx| { )
window.dispatch_action( },
Box::new(NewExternalAgentThread {
agent: Some(
crate::ExternalAgent::ClaudeCode,
),
}),
cx,
)
},
),
), ),
) )
}), .child(
NewThreadButton::new(
"new-claude-thread-btn",
"New Claude Code Thread",
IconName::AiClaude,
)
// .keybinding(KeyBinding::for_action_in(
// &OpenHistory,
// &self.focus_handle(cx),
// window,
// cx,
// ))
.on_click(
|window, cx| {
window.dispatch_action(
Box::new(NewExternalAgentThread {
agent: Some(
crate::ExternalAgent::ClaudeCode,
),
}),
cx,
)
},
),
)
.child(
NewThreadButton::new(
"new-native-agent-thread-btn",
"New Native Agent Thread",
IconName::ZedAssistant,
)
// .keybinding(KeyBinding::for_action_in(
// &OpenHistory,
// &self.focus_handle(cx),
// window,
// cx,
// ))
.on_click(
|window, cx| {
window.dispatch_action(
Box::new(NewExternalAgentThread {
agent: Some(
crate::ExternalAgent::NativeAgent,
),
}),
cx,
)
},
),
),
), // })
) )
.when_some(configuration_error.as_ref(), |this, err| { .when_some(configuration_error.as_ref(), |this, err| {
this.child(self.render_configuration_error(err, &focus_handle, window, cx)) this.child(self.render_configuration_error(err, &focus_handle, window, cx))

View file

@ -150,6 +150,7 @@ enum ExternalAgent {
#[default] #[default]
Gemini, Gemini,
ClaudeCode, ClaudeCode,
NativeAgent,
} }
impl ExternalAgent { impl ExternalAgent {
@ -157,6 +158,7 @@ impl ExternalAgent {
match self { match self {
ExternalAgent::Gemini => Rc::new(agent_servers::Gemini), ExternalAgent::Gemini => Rc::new(agent_servers::Gemini),
ExternalAgent::ClaudeCode => Rc::new(agent_servers::ClaudeCode), ExternalAgent::ClaudeCode => Rc::new(agent_servers::ClaudeCode),
ExternalAgent::NativeAgent => Rc::new(agent2::NativeAgentServer),
} }
} }
} }