1mod acp;
2mod active_thread;
3mod agent_configuration;
4mod agent_diff;
5mod agent_model_selector;
6mod agent_panel;
7mod buffer_codegen;
8mod burn_mode_tooltip;
9mod context_picker;
10mod context_server_configuration;
11mod context_strip;
12mod debug;
13mod inline_assistant;
14mod inline_prompt_editor;
15mod language_model_selector;
16mod message_editor;
17mod profile_selector;
18mod slash_command;
19mod slash_command_picker;
20mod slash_command_settings;
21mod terminal_codegen;
22mod terminal_inline_assistant;
23mod text_thread_editor;
24mod thread_history;
25mod tool_compatibility;
26mod ui;
27
28use std::rc::Rc;
29use std::sync::Arc;
30
31use agent::{Thread, ThreadId};
32use agent_settings::{AgentProfileId, AgentSettings, LanguageModelSelection};
33use assistant_slash_command::SlashCommandRegistry;
34use client::Client;
35use command_palette_hooks::CommandPaletteFilter;
36use feature_flags::FeatureFlagAppExt as _;
37use fs::Fs;
38use gpui::{Action, App, Entity, actions};
39use language::LanguageRegistry;
40use language_model::{
41 ConfiguredModel, LanguageModel, LanguageModelId, LanguageModelProviderId, LanguageModelRegistry,
42};
43use project::DisableAiSettings;
44use prompt_store::PromptBuilder;
45use schemars::JsonSchema;
46use serde::{Deserialize, Serialize};
47use settings::{Settings as _, SettingsStore};
48use std::any::TypeId;
49
50pub use crate::active_thread::ActiveThread;
51use crate::agent_configuration::{ConfigureContextServerModal, ManageProfilesModal};
52pub use crate::agent_panel::{AgentPanel, ConcreteAssistantPanelDelegate};
53pub use crate::inline_assistant::InlineAssistant;
54use crate::slash_command_settings::SlashCommandSettings;
55pub use agent_diff::{AgentDiffPane, AgentDiffToolbar};
56pub use text_thread_editor::{AgentPanelDelegate, TextThreadEditor};
57pub use ui::preview::{all_agent_previews, get_agent_preview};
58use zed_actions;
59
60actions!(
61 agent,
62 [
63 /// Creates a new text-based conversation thread.
64 NewTextThread,
65 /// Toggles the context picker interface for adding files, symbols, or other context.
66 ToggleContextPicker,
67 /// Toggles the menu to create new agent threads.
68 ToggleNewThreadMenu,
69 /// Toggles the navigation menu for switching between threads and views.
70 ToggleNavigationMenu,
71 /// Toggles the options menu for agent settings and preferences.
72 ToggleOptionsMenu,
73 /// Deletes the recently opened thread from history.
74 DeleteRecentlyOpenThread,
75 /// Toggles the profile selector for switching between agent profiles.
76 ToggleProfileSelector,
77 /// Removes all added context from the current conversation.
78 RemoveAllContext,
79 /// Expands the message editor to full size.
80 ExpandMessageEditor,
81 /// Opens the conversation history view.
82 OpenHistory,
83 /// Adds a context server to the configuration.
84 AddContextServer,
85 /// Removes the currently selected thread.
86 RemoveSelectedThread,
87 /// Starts a chat conversation with follow-up enabled.
88 ChatWithFollow,
89 /// Cycles to the next inline assist suggestion.
90 CycleNextInlineAssist,
91 /// Cycles to the previous inline assist suggestion.
92 CyclePreviousInlineAssist,
93 /// Moves focus up in the interface.
94 FocusUp,
95 /// Moves focus down in the interface.
96 FocusDown,
97 /// Moves focus left in the interface.
98 FocusLeft,
99 /// Moves focus right in the interface.
100 FocusRight,
101 /// Removes the currently focused context item.
102 RemoveFocusedContext,
103 /// Accepts the suggested context item.
104 AcceptSuggestedContext,
105 /// Opens the active thread as a markdown file.
106 OpenActiveThreadAsMarkdown,
107 /// Opens the agent diff view to review changes.
108 OpenAgentDiff,
109 /// Keeps the current suggestion or change.
110 Keep,
111 /// Rejects the current suggestion or change.
112 Reject,
113 /// Rejects all suggestions or changes.
114 RejectAll,
115 /// Keeps all suggestions or changes.
116 KeepAll,
117 /// Follows the agent's suggestions.
118 Follow,
119 /// Resets the trial upsell notification.
120 ResetTrialUpsell,
121 /// Resets the trial end upsell notification.
122 ResetTrialEndUpsell,
123 /// Continues the current thread.
124 ContinueThread,
125 /// Continues the thread with burn mode enabled.
126 ContinueWithBurnMode,
127 /// Toggles burn mode for faster responses.
128 ToggleBurnMode,
129 ]
130);
131
132/// Creates a new conversation thread, optionally based on an existing thread.
133#[derive(Default, Clone, PartialEq, Deserialize, JsonSchema, Action)]
134#[action(namespace = agent)]
135#[serde(deny_unknown_fields)]
136pub struct NewThread {
137 #[serde(default)]
138 from_thread_id: Option<ThreadId>,
139}
140
141/// Creates a new external agent conversation thread.
142#[derive(Default, Clone, PartialEq, Deserialize, JsonSchema, Action)]
143#[action(namespace = agent)]
144#[serde(deny_unknown_fields)]
145pub struct NewExternalAgentThread {
146 /// Which agent to use for the conversation.
147 agent: Option<ExternalAgent>,
148}
149
150#[derive(Default, Clone, Copy, PartialEq, Serialize, Deserialize, JsonSchema)]
151#[serde(rename_all = "snake_case")]
152enum ExternalAgent {
153 #[default]
154 Gemini,
155 ClaudeCode,
156 NativeAgent,
157}
158
159impl ExternalAgent {
160 pub fn server(&self, fs: Arc<dyn fs::Fs>) -> Rc<dyn agent_servers::AgentServer> {
161 match self {
162 ExternalAgent::Gemini => Rc::new(agent_servers::Gemini),
163 ExternalAgent::ClaudeCode => Rc::new(agent_servers::ClaudeCode),
164 ExternalAgent::NativeAgent => Rc::new(agent2::NativeAgentServer::new(fs)),
165 }
166 }
167}
168
169/// Opens the profile management interface for configuring agent tools and settings.
170#[derive(PartialEq, Clone, Default, Debug, Deserialize, JsonSchema, Action)]
171#[action(namespace = agent)]
172#[serde(deny_unknown_fields)]
173pub struct ManageProfiles {
174 #[serde(default)]
175 pub customize_tools: Option<AgentProfileId>,
176}
177
178impl ManageProfiles {
179 pub fn customize_tools(profile_id: AgentProfileId) -> Self {
180 Self {
181 customize_tools: Some(profile_id),
182 }
183 }
184}
185
186#[derive(Clone)]
187pub(crate) enum ModelUsageContext {
188 Thread(Entity<Thread>),
189 InlineAssistant,
190}
191
192impl ModelUsageContext {
193 pub fn configured_model(&self, cx: &App) -> Option<ConfiguredModel> {
194 match self {
195 Self::Thread(thread) => thread.read(cx).configured_model(),
196 Self::InlineAssistant => {
197 LanguageModelRegistry::read_global(cx).inline_assistant_model()
198 }
199 }
200 }
201
202 pub fn language_model(&self, cx: &App) -> Option<Arc<dyn LanguageModel>> {
203 self.configured_model(cx)
204 .map(|configured_model| configured_model.model)
205 }
206}
207
208/// Initializes the `agent` crate.
209pub fn init(
210 fs: Arc<dyn Fs>,
211 client: Arc<Client>,
212 prompt_builder: Arc<PromptBuilder>,
213 language_registry: Arc<LanguageRegistry>,
214 is_eval: bool,
215 cx: &mut App,
216) {
217 AgentSettings::register(cx);
218 SlashCommandSettings::register(cx);
219
220 assistant_context::init(client.clone(), cx);
221 rules_library::init(cx);
222 if !is_eval {
223 // Initializing the language model from the user settings messes with the eval, so we only initialize them when
224 // we're not running inside of the eval.
225 init_language_model_settings(cx);
226 }
227 assistant_slash_command::init(cx);
228 agent::init(cx);
229 agent_panel::init(cx);
230 context_server_configuration::init(language_registry.clone(), fs.clone(), cx);
231 TextThreadEditor::init(cx);
232
233 register_slash_commands(cx);
234 inline_assistant::init(
235 fs.clone(),
236 prompt_builder.clone(),
237 client.telemetry().clone(),
238 cx,
239 );
240 terminal_inline_assistant::init(
241 fs.clone(),
242 prompt_builder.clone(),
243 client.telemetry().clone(),
244 cx,
245 );
246 indexed_docs::init(cx);
247 cx.observe_new(move |workspace, window, cx| {
248 ConfigureContextServerModal::register(workspace, language_registry.clone(), window, cx)
249 })
250 .detach();
251 cx.observe_new(ManageProfilesModal::register).detach();
252
253 // Update command palette filter based on AI settings
254 update_command_palette_filter(cx);
255
256 // Watch for settings changes
257 cx.observe_global::<SettingsStore>(|app_cx| {
258 // When settings change, update the command palette filter
259 update_command_palette_filter(app_cx);
260 })
261 .detach();
262}
263
264fn update_command_palette_filter(cx: &mut App) {
265 let disable_ai = DisableAiSettings::get_global(cx).disable_ai;
266 CommandPaletteFilter::update_global(cx, |filter, _| {
267 if disable_ai {
268 filter.hide_namespace("agent");
269 filter.hide_namespace("assistant");
270 filter.hide_namespace("copilot");
271 filter.hide_namespace("supermaven");
272 filter.hide_namespace("zed_predict_onboarding");
273 filter.hide_namespace("edit_prediction");
274
275 use editor::actions::{
276 AcceptEditPrediction, AcceptPartialEditPrediction, NextEditPrediction,
277 PreviousEditPrediction, ShowEditPrediction, ToggleEditPrediction,
278 };
279 let edit_prediction_actions = [
280 TypeId::of::<AcceptEditPrediction>(),
281 TypeId::of::<AcceptPartialEditPrediction>(),
282 TypeId::of::<ShowEditPrediction>(),
283 TypeId::of::<NextEditPrediction>(),
284 TypeId::of::<PreviousEditPrediction>(),
285 TypeId::of::<ToggleEditPrediction>(),
286 ];
287 filter.hide_action_types(&edit_prediction_actions);
288 filter.hide_action_types(&[TypeId::of::<zed_actions::OpenZedPredictOnboarding>()]);
289 } else {
290 filter.show_namespace("agent");
291 filter.show_namespace("assistant");
292 filter.show_namespace("copilot");
293 filter.show_namespace("zed_predict_onboarding");
294
295 filter.show_namespace("edit_prediction");
296
297 use editor::actions::{
298 AcceptEditPrediction, AcceptPartialEditPrediction, NextEditPrediction,
299 PreviousEditPrediction, ShowEditPrediction, ToggleEditPrediction,
300 };
301 let edit_prediction_actions = [
302 TypeId::of::<AcceptEditPrediction>(),
303 TypeId::of::<AcceptPartialEditPrediction>(),
304 TypeId::of::<ShowEditPrediction>(),
305 TypeId::of::<NextEditPrediction>(),
306 TypeId::of::<PreviousEditPrediction>(),
307 TypeId::of::<ToggleEditPrediction>(),
308 ];
309 filter.show_action_types(edit_prediction_actions.iter());
310
311 filter
312 .show_action_types([TypeId::of::<zed_actions::OpenZedPredictOnboarding>()].iter());
313 }
314 });
315}
316
317fn init_language_model_settings(cx: &mut App) {
318 update_active_language_model_from_settings(cx);
319
320 cx.observe_global::<SettingsStore>(update_active_language_model_from_settings)
321 .detach();
322 cx.subscribe(
323 &LanguageModelRegistry::global(cx),
324 |_, event: &language_model::Event, cx| match event {
325 language_model::Event::ProviderStateChanged
326 | language_model::Event::AddedProvider(_)
327 | language_model::Event::RemovedProvider(_) => {
328 update_active_language_model_from_settings(cx);
329 }
330 _ => {}
331 },
332 )
333 .detach();
334}
335
336fn update_active_language_model_from_settings(cx: &mut App) {
337 let settings = AgentSettings::get_global(cx);
338
339 fn to_selected_model(selection: &LanguageModelSelection) -> language_model::SelectedModel {
340 language_model::SelectedModel {
341 provider: LanguageModelProviderId::from(selection.provider.0.clone()),
342 model: LanguageModelId::from(selection.model.clone()),
343 }
344 }
345
346 let default = settings.default_model.as_ref().map(to_selected_model);
347 let inline_assistant = settings
348 .inline_assistant_model
349 .as_ref()
350 .map(to_selected_model);
351 let commit_message = settings
352 .commit_message_model
353 .as_ref()
354 .map(to_selected_model);
355 let thread_summary = settings
356 .thread_summary_model
357 .as_ref()
358 .map(to_selected_model);
359 let inline_alternatives = settings
360 .inline_alternatives
361 .iter()
362 .map(to_selected_model)
363 .collect::<Vec<_>>();
364
365 LanguageModelRegistry::global(cx).update(cx, |registry, cx| {
366 registry.select_default_model(default.as_ref(), cx);
367 registry.select_inline_assistant_model(inline_assistant.as_ref(), cx);
368 registry.select_commit_message_model(commit_message.as_ref(), cx);
369 registry.select_thread_summary_model(thread_summary.as_ref(), cx);
370 registry.select_inline_alternative_models(inline_alternatives, cx);
371 });
372}
373
374fn register_slash_commands(cx: &mut App) {
375 let slash_command_registry = SlashCommandRegistry::global(cx);
376
377 slash_command_registry.register_command(assistant_slash_commands::FileSlashCommand, true);
378 slash_command_registry.register_command(assistant_slash_commands::DeltaSlashCommand, true);
379 slash_command_registry.register_command(assistant_slash_commands::OutlineSlashCommand, true);
380 slash_command_registry.register_command(assistant_slash_commands::TabSlashCommand, true);
381 slash_command_registry
382 .register_command(assistant_slash_commands::CargoWorkspaceSlashCommand, true);
383 slash_command_registry.register_command(assistant_slash_commands::PromptSlashCommand, true);
384 slash_command_registry.register_command(assistant_slash_commands::SelectionCommand, true);
385 slash_command_registry.register_command(assistant_slash_commands::DefaultSlashCommand, false);
386 slash_command_registry.register_command(assistant_slash_commands::NowSlashCommand, false);
387 slash_command_registry
388 .register_command(assistant_slash_commands::DiagnosticsSlashCommand, true);
389 slash_command_registry.register_command(assistant_slash_commands::FetchSlashCommand, true);
390
391 cx.observe_flag::<assistant_slash_commands::StreamingExampleSlashCommandFeatureFlag, _>({
392 let slash_command_registry = slash_command_registry.clone();
393 move |is_enabled, _cx| {
394 if is_enabled {
395 slash_command_registry.register_command(
396 assistant_slash_commands::StreamingExampleSlashCommand,
397 false,
398 );
399 }
400 }
401 })
402 .detach();
403
404 update_slash_commands_from_settings(cx);
405 cx.observe_global::<SettingsStore>(update_slash_commands_from_settings)
406 .detach();
407}
408
409fn update_slash_commands_from_settings(cx: &mut App) {
410 let slash_command_registry = SlashCommandRegistry::global(cx);
411 let settings = SlashCommandSettings::get_global(cx);
412
413 if settings.docs.enabled {
414 slash_command_registry.register_command(assistant_slash_commands::DocsSlashCommand, true);
415 } else {
416 slash_command_registry.unregister_command(assistant_slash_commands::DocsSlashCommand);
417 }
418
419 if settings.cargo_workspace.enabled {
420 slash_command_registry
421 .register_command(assistant_slash_commands::CargoWorkspaceSlashCommand, true);
422 } else {
423 slash_command_registry
424 .unregister_command(assistant_slash_commands::CargoWorkspaceSlashCommand);
425 }
426}