1use std::{any::Any, path::Path, rc::Rc, sync::Arc};
2
3use agent_servers::{AgentServer, AgentServerDelegate};
4use anyhow::Result;
5use collections::HashMap;
6use fs::Fs;
7use gpui::{App, Entity, SharedString, Task};
8use prompt_store::PromptStore;
9
10use crate::{HistoryStore, NativeAgent, NativeAgentConnection, templates::Templates};
11
12#[derive(Clone)]
13pub struct NativeAgentServer {
14 fs: Arc<dyn Fs>,
15 history: Entity<HistoryStore>,
16}
17
18impl NativeAgentServer {
19 pub fn new(fs: Arc<dyn Fs>, history: Entity<HistoryStore>) -> Self {
20 Self { fs, history }
21 }
22}
23
24impl AgentServer for NativeAgentServer {
25 fn telemetry_id(&self) -> &'static str {
26 "zed"
27 }
28
29 fn name(&self) -> SharedString {
30 "Zed Agent".into()
31 }
32
33 fn logo(&self) -> ui::IconName {
34 ui::IconName::ZedAgent
35 }
36
37 fn local_login_commands(&self) -> Vec<String> {
38 vec![]
39 }
40
41 fn remote_login_commands(&self) -> Vec<String> {
42 vec![]
43 }
44
45 fn local_logout_commands(&self) -> Vec<String> {
46 vec![]
47 }
48
49 fn remote_logout_commands(&self) -> Vec<String> {
50 vec![]
51 }
52
53 fn connect(
54 &self,
55 _root_dir: Option<&Path>,
56 delegate: AgentServerDelegate,
57 cx: &mut App,
58 ) -> Task<
59 Result<(
60 Rc<dyn acp_thread::AgentConnection>,
61 HashMap<String, task::SpawnInTerminal>,
62 )>,
63 > {
64 log::debug!(
65 "NativeAgentServer::connect called for path: {:?}",
66 _root_dir
67 );
68 let project = delegate.project().clone();
69 let fs = self.fs.clone();
70 let history = self.history.clone();
71 let prompt_store = PromptStore::global(cx);
72 cx.spawn(async move |cx| {
73 log::debug!("Creating templates for native agent");
74 let templates = Templates::new();
75 let prompt_store = prompt_store.await?;
76
77 log::debug!("Creating native agent entity");
78 let agent =
79 NativeAgent::new(project, history, templates, Some(prompt_store), fs, cx).await?;
80
81 // Create the connection wrapper
82 let connection = NativeAgentConnection(agent);
83 log::debug!("NativeAgentServer connection established successfully");
84
85 Ok((
86 Rc::new(connection) as Rc<dyn acp_thread::AgentConnection>,
87 HashMap::default(),
88 ))
89 })
90 }
91
92 fn into_any(self: Rc<Self>) -> Rc<dyn Any> {
93 self
94 }
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100
101 use assistant_context::ContextStore;
102 use gpui::AppContext;
103
104 agent_servers::e2e_tests::common_e2e_tests!(
105 async |fs, project, cx| {
106 let auth = cx.update(|cx| {
107 prompt_store::init(cx);
108 terminal::init(cx);
109
110 let registry = language_model::LanguageModelRegistry::read_global(cx);
111 let auth = registry
112 .provider(&language_model::ANTHROPIC_PROVIDER_ID)
113 .unwrap()
114 .authenticate(cx);
115
116 cx.spawn(async move |_| auth.await)
117 });
118
119 auth.await.unwrap();
120
121 cx.update(|cx| {
122 let registry = language_model::LanguageModelRegistry::global(cx);
123
124 registry.update(cx, |registry, cx| {
125 registry.select_default_model(
126 Some(&language_model::SelectedModel {
127 provider: language_model::ANTHROPIC_PROVIDER_ID,
128 model: language_model::LanguageModelId("claude-sonnet-4-latest".into()),
129 }),
130 cx,
131 );
132 });
133 });
134
135 let history = cx.update(|cx| {
136 let context_store = cx.new(move |cx| ContextStore::fake(project.clone(), cx));
137 cx.new(move |cx| HistoryStore::new(context_store, cx))
138 });
139
140 NativeAgentServer::new(fs.clone(), history)
141 },
142 allow_option_id = "allow"
143 );
144}