From 2283ec5de26feedecf5f1f75d76c367636623263 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 23 Jun 2025 18:00:28 -0700 Subject: [PATCH] Extract an agent_ui crate from agent (#33284) This PR moves the UI-dependent logic in the `agent` crate into its own crate, `agent_ui`. The remaining `agent` crate no longer depends on `editor`, `picker`, `ui`, `workspace`, etc. This has compile time benefits, but the main motivation is to isolate our core agentic logic, so that we can make agents more pluggable/configurable. Release Notes: - N/A --- Cargo.lock | 121 +++++-- Cargo.toml | 2 + crates/agent/Cargo.toml | 40 +-- crates/agent/src/agent.rs | 307 +----------------- crates/agent/src/agent_profile.rs | 7 +- crates/agent/src/context.rs | 84 +---- crates/agent/src/context_server_tool.rs | 2 +- crates/agent/src/context_store.rs | 73 ++++- crates/agent/src/history_store.rs | 16 +- crates/agent/src/thread.rs | 64 ++-- crates/agent/src/thread_store.rs | 38 ++- crates/agent/src/tool_use.rs | 19 +- crates/agent_ui/Cargo.toml | 107 ++++++ crates/agent_ui/LICENSE-GPL | 1 + .../{agent => agent_ui}/src/active_thread.rs | 34 +- .../src/agent_configuration.rs | 0 .../configure_context_server_modal.rs | 0 .../manage_profiles_modal.rs | 2 +- .../profile_modal_header.rs | 0 .../src/agent_configuration/tool_picker.rs | 0 crates/{agent => agent_ui}/src/agent_diff.rs | 6 +- .../src/agent_model_selector.rs | 0 crates/{agent => agent_ui}/src/agent_panel.rs | 62 ++-- crates/agent_ui/src/agent_ui.rs | 285 ++++++++++++++++ .../{agent => agent_ui}/src/buffer_codegen.rs | 9 +- .../{agent => agent_ui}/src/context_picker.rs | 10 +- .../src/context_picker/completion_provider.rs | 11 +- .../context_picker/fetch_context_picker.rs | 2 +- .../src/context_picker/file_context_picker.rs | 2 +- .../context_picker/rules_context_picker.rs | 4 +- .../context_picker/symbol_context_picker.rs | 4 +- .../context_picker/thread_context_picker.rs | 8 +- .../src/context_server_configuration.rs | 0 .../{agent => agent_ui}/src/context_strip.rs | 72 +--- crates/{agent => agent_ui}/src/debug.rs | 0 .../src/inline_assistant.rs | 36 +- .../src/inline_prompt_editor.rs | 9 +- .../{agent => agent_ui}/src/message_editor.rs | 95 +++++- .../src/profile_selector.rs | 13 +- .../src/slash_command_settings.rs | 0 .../src/terminal_codegen.rs | 0 .../src/terminal_inline_assistant.rs | 8 +- .../{agent => agent_ui}/src/thread_history.rs | 10 +- .../src/tool_compatibility.rs | 6 +- crates/{agent => agent_ui}/src/ui.rs | 0 .../src/ui/agent_notification.rs | 0 .../src/ui/animated_label.rs | 0 .../src/ui/context_pill.rs | 2 +- .../src/ui/max_mode_tooltip.rs | 0 .../src/ui/onboarding_modal.rs | 0 crates/{agent => agent_ui}/src/ui/preview.rs | 0 .../src/ui/preview/agent_preview.rs | 0 .../src/ui/preview/usage_callouts.rs | 0 crates/{agent => agent_ui}/src/ui/upsell.rs | 0 crates/eval/Cargo.toml | 1 + crates/eval/src/eval.rs | 2 +- crates/eval/src/example.rs | 2 +- crates/zed/Cargo.toml | 1 + crates/zed/src/main.rs | 2 +- crates/zed/src/zed.rs | 12 +- crates/zed/src/zed/component_preview.rs | 23 +- .../preview_support/active_thread.rs | 3 +- 62 files changed, 865 insertions(+), 752 deletions(-) create mode 100644 crates/agent_ui/Cargo.toml create mode 120000 crates/agent_ui/LICENSE-GPL rename crates/{agent => agent_ui}/src/active_thread.rs (99%) rename crates/{agent => agent_ui}/src/agent_configuration.rs (100%) rename crates/{agent => agent_ui}/src/agent_configuration/configure_context_server_modal.rs (100%) rename crates/{agent => agent_ui}/src/agent_configuration/manage_profiles_modal.rs (99%) rename crates/{agent => agent_ui}/src/agent_configuration/manage_profiles_modal/profile_modal_header.rs (100%) rename crates/{agent => agent_ui}/src/agent_configuration/tool_picker.rs (100%) rename crates/{agent => agent_ui}/src/agent_diff.rs (99%) rename crates/{agent => agent_ui}/src/agent_model_selector.rs (100%) rename crates/{agent => agent_ui}/src/agent_panel.rs (98%) create mode 100644 crates/agent_ui/src/agent_ui.rs rename crates/{agent => agent_ui}/src/buffer_codegen.rs (99%) rename crates/{agent => agent_ui}/src/context_picker.rs (99%) rename crates/{agent => agent_ui}/src/context_picker/completion_provider.rs (99%) rename crates/{agent => agent_ui}/src/context_picker/fetch_context_picker.rs (99%) rename crates/{agent => agent_ui}/src/context_picker/file_context_picker.rs (99%) rename crates/{agent => agent_ui}/src/context_picker/rules_context_picker.rs (98%) rename crates/{agent => agent_ui}/src/context_picker/symbol_context_picker.rs (99%) rename crates/{agent => agent_ui}/src/context_picker/thread_context_picker.rs (98%) rename crates/{agent => agent_ui}/src/context_server_configuration.rs (100%) rename crates/{agent => agent_ui}/src/context_strip.rs (93%) rename crates/{agent => agent_ui}/src/debug.rs (100%) rename crates/{agent => agent_ui}/src/inline_assistant.rs (98%) rename crates/{agent => agent_ui}/src/inline_prompt_editor.rs (99%) rename crates/{agent => agent_ui}/src/message_editor.rs (96%) rename crates/{agent => agent_ui}/src/profile_selector.rs (99%) rename crates/{agent => agent_ui}/src/slash_command_settings.rs (100%) rename crates/{agent => agent_ui}/src/terminal_codegen.rs (100%) rename crates/{agent => agent_ui}/src/terminal_inline_assistant.rs (99%) rename crates/{agent => agent_ui}/src/thread_history.rs (99%) rename crates/{agent => agent_ui}/src/tool_compatibility.rs (98%) rename crates/{agent => agent_ui}/src/ui.rs (100%) rename crates/{agent => agent_ui}/src/ui/agent_notification.rs (100%) rename crates/{agent => agent_ui}/src/ui/animated_label.rs (100%) rename crates/{agent => agent_ui}/src/ui/context_pill.rs (99%) rename crates/{agent => agent_ui}/src/ui/max_mode_tooltip.rs (100%) rename crates/{agent => agent_ui}/src/ui/onboarding_modal.rs (100%) rename crates/{agent => agent_ui}/src/ui/preview.rs (100%) rename crates/{agent => agent_ui}/src/ui/preview/agent_preview.rs (100%) rename crates/{agent => agent_ui}/src/ui/preview/usage_callouts.rs (100%) rename crates/{agent => agent_ui}/src/ui/upsell.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 28e46d730a..a4c34e87c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -56,49 +56,28 @@ dependencies = [ "agent_settings", "anyhow", "assistant_context_editor", - "assistant_slash_command", - "assistant_slash_commands", "assistant_tool", "assistant_tools", - "audio", - "buffer_diff", "chrono", "client", "collections", "component", "context_server", "convert_case 0.8.0", - "db", - "editor", - "extension", - "extension_host", "feature_flags", - "file_icons", "fs", "futures 0.3.31", - "fuzzy", "git", "gpui", "heed", - "html_to_markdown", "http_client", - "indexed_docs", + "icons", "indoc", - "inventory", "itertools 0.14.0", - "jsonschema", "language", "language_model", "log", - "lsp", - "markdown", - "menu", - "multi_buffer", - "notifications", - "ordered-float 2.10.1", - "parking_lot", "paths", - "picker", "postage", "pretty_assertions", "project", @@ -106,35 +85,22 @@ dependencies = [ "proto", "rand 0.8.5", "ref-cast", - "release_channel", "rope", - "rules_library", "schemars", - "search", "serde", "serde_json", - "serde_json_lenient", "settings", "smol", "sqlez", - "streaming_diff", "telemetry", - "telemetry_events", - "terminal", - "terminal_view", "text", "theme", "thiserror 2.0.12", "time", - "time_format", - "ui", - "urlencoding", "util", "uuid", - "watch", "workspace", "workspace-hack", - "zed_actions", "zed_llm_client", "zstd", ] @@ -165,6 +131,89 @@ dependencies = [ "zed_llm_client", ] +[[package]] +name = "agent_ui" +version = "0.1.0" +dependencies = [ + "agent", + "agent_settings", + "anyhow", + "assistant_context_editor", + "assistant_slash_command", + "assistant_slash_commands", + "assistant_tool", + "assistant_tools", + "audio", + "buffer_diff", + "chrono", + "client", + "collections", + "component", + "context_server", + "db", + "editor", + "extension", + "extension_host", + "feature_flags", + "file_icons", + "fs", + "futures 0.3.31", + "fuzzy", + "gpui", + "html_to_markdown", + "http_client", + "indexed_docs", + "indoc", + "inventory", + "itertools 0.14.0", + "jsonschema", + "language", + "language_model", + "log", + "lsp", + "markdown", + "menu", + "multi_buffer", + "notifications", + "ordered-float 2.10.1", + "parking_lot", + "paths", + "picker", + "pretty_assertions", + "project", + "prompt_store", + "proto", + "rand 0.8.5", + "release_channel", + "rope", + "rules_library", + "schemars", + "search", + "serde", + "serde_json", + "serde_json_lenient", + "settings", + "smol", + "streaming_diff", + "telemetry", + "telemetry_events", + "terminal", + "terminal_view", + "text", + "theme", + "time", + "time_format", + "ui", + "urlencoding", + "util", + "uuid", + "watch", + "workspace", + "workspace-hack", + "zed_actions", + "zed_llm_client", +] + [[package]] name = "ahash" version = "0.7.8" @@ -5062,6 +5111,7 @@ version = "0.1.0" dependencies = [ "agent", "agent_settings", + "agent_ui", "anyhow", "assistant_tool", "assistant_tools", @@ -19868,6 +19918,7 @@ dependencies = [ "activity_indicator", "agent", "agent_settings", + "agent_ui", "anyhow", "ashpd", "askpass", diff --git a/Cargo.toml b/Cargo.toml index 161a0096cd..55ee14485c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ resolver = "2" members = [ "crates/activity_indicator", + "crates/agent_ui", "crates/agent", "crates/agent_settings", "crates/anthropic", @@ -214,6 +215,7 @@ edition = "2024" activity_indicator = { path = "crates/activity_indicator" } agent = { path = "crates/agent" } +agent_ui = { path = "crates/agent_ui" } agent_settings = { path = "crates/agent_settings" } ai = { path = "crates/ai" } anthropic = { path = "crates/anthropic" } diff --git a/crates/agent/Cargo.toml b/crates/agent/Cargo.toml index 69edd5a189..bedd5506b9 100644 --- a/crates/agent/Cargo.toml +++ b/crates/agent/Cargo.toml @@ -22,93 +22,57 @@ test-support = [ agent_settings.workspace = true anyhow.workspace = true assistant_context_editor.workspace = true -assistant_slash_command.workspace = true -assistant_slash_commands.workspace = true assistant_tool.workspace = true -audio.workspace = true -buffer_diff.workspace = true chrono.workspace = true client.workspace = true collections.workspace = true component.workspace = true context_server.workspace = true convert_case.workspace = true -db.workspace = true -editor.workspace = true -extension.workspace = true -extension_host.workspace = true feature_flags.workspace = true -file_icons.workspace = true fs.workspace = true futures.workspace = true -fuzzy.workspace = true git.workspace = true gpui.workspace = true heed.workspace = true -html_to_markdown.workspace = true +icons.workspace = true indoc.workspace = true http_client.workspace = true -indexed_docs.workspace = true -inventory.workspace = true itertools.workspace = true -jsonschema.workspace = true language.workspace = true language_model.workspace = true log.workspace = true -lsp.workspace = true -markdown.workspace = true -menu.workspace = true -multi_buffer.workspace = true -notifications.workspace = true -ordered-float.workspace = true -parking_lot.workspace = true paths.workspace = true -picker.workspace = true postage.workspace = true project.workspace = true prompt_store.workspace = true proto.workspace = true ref-cast.workspace = true -release_channel.workspace = true rope.workspace = true -rules_library.workspace = true schemars.workspace = true -search.workspace = true serde.workspace = true serde_json.workspace = true -serde_json_lenient.workspace = true settings.workspace = true smol.workspace = true sqlez.workspace = true -streaming_diff.workspace = true telemetry.workspace = true -telemetry_events.workspace = true -terminal.workspace = true -terminal_view.workspace = true text.workspace = true theme.workspace = true thiserror.workspace = true time.workspace = true -time_format.workspace = true -ui.workspace = true -urlencoding.workspace = true util.workspace = true uuid.workspace = true -watch.workspace = true workspace-hack.workspace = true -workspace.workspace = true -zed_actions.workspace = true zed_llm_client.workspace = true zstd.workspace = true [dev-dependencies] assistant_tools.workspace = true -buffer_diff = { workspace = true, features = ["test-support"] } -editor = { workspace = true, features = ["test-support"] } gpui = { workspace = true, "features" = ["test-support"] } indoc.workspace = true language = { workspace = true, "features" = ["test-support"] } language_model = { workspace = true, "features" = ["test-support"] } pretty_assertions.workspace = true project = { workspace = true, features = ["test-support"] } +workspace = { workspace = true, features = ["test-support"] } rand.workspace = true diff --git a/crates/agent/src/agent.rs b/crates/agent/src/agent.rs index ff108e06cb..7e3590f05d 100644 --- a/crates/agent/src/agent.rs +++ b/crates/agent/src/agent.rs @@ -1,297 +1,20 @@ -mod active_thread; -mod agent_configuration; -mod agent_diff; -mod agent_model_selector; -mod agent_panel; -mod agent_profile; -mod buffer_codegen; -mod context; -mod context_picker; -mod context_server_configuration; -mod context_server_tool; -mod context_store; -mod context_strip; -mod debug; -mod history_store; -mod inline_assistant; -mod inline_prompt_editor; -mod message_editor; -mod profile_selector; -mod slash_command_settings; -mod terminal_codegen; -mod terminal_inline_assistant; -mod thread; -mod thread_history; -mod thread_store; -mod tool_compatibility; -mod tool_use; -mod ui; +pub mod agent_profile; +pub mod context; +pub mod context_server_tool; +pub mod context_store; +pub mod history_store; +pub mod thread; +pub mod thread_store; +pub mod tool_use; -use std::sync::Arc; - -use agent_settings::{AgentProfileId, AgentSettings, LanguageModelSelection}; -use assistant_slash_command::SlashCommandRegistry; -use client::Client; -use feature_flags::FeatureFlagAppExt as _; -use fs::Fs; -use gpui::{App, Entity, actions, impl_actions}; -use language::LanguageRegistry; -use language_model::{ - ConfiguredModel, LanguageModel, LanguageModelId, LanguageModelProviderId, LanguageModelRegistry, -}; -use prompt_store::PromptBuilder; -use schemars::JsonSchema; -use serde::Deserialize; -use settings::{Settings as _, SettingsStore}; -use thread::ThreadId; - -pub use crate::active_thread::ActiveThread; -use crate::agent_configuration::{ConfigureContextServerModal, ManageProfilesModal}; -pub use crate::agent_panel::{AgentPanel, ConcreteAssistantPanelDelegate}; -pub use crate::context::{ContextLoadResult, LoadedContext}; -pub use crate::inline_assistant::InlineAssistant; -use crate::slash_command_settings::SlashCommandSettings; -pub use crate::thread::{Message, MessageSegment, Thread, ThreadEvent}; -pub use crate::thread_store::{SerializedThread, TextThreadStore, ThreadStore}; -pub use agent_diff::{AgentDiffPane, AgentDiffToolbar}; +pub use context::{AgentContext, ContextId, ContextLoadResult}; pub use context_store::ContextStore; -pub use ui::preview::{all_agent_previews, get_agent_preview}; +pub use thread::{ + LastRestoreCheckpoint, Message, MessageCrease, MessageId, MessageSegment, Thread, ThreadError, + ThreadEvent, ThreadFeedback, ThreadId, ThreadSummary, TokenUsageRatio, +}; +pub use thread_store::{SerializedThread, TextThreadStore, ThreadStore}; -actions!( - agent, - [ - NewTextThread, - ToggleContextPicker, - ToggleNavigationMenu, - ToggleOptionsMenu, - DeleteRecentlyOpenThread, - ToggleProfileSelector, - RemoveAllContext, - ExpandMessageEditor, - OpenHistory, - AddContextServer, - RemoveSelectedThread, - Chat, - ChatWithFollow, - CycleNextInlineAssist, - CyclePreviousInlineAssist, - FocusUp, - FocusDown, - FocusLeft, - FocusRight, - RemoveFocusedContext, - AcceptSuggestedContext, - OpenActiveThreadAsMarkdown, - OpenAgentDiff, - Keep, - Reject, - RejectAll, - KeepAll, - Follow, - ResetTrialUpsell, - ResetTrialEndUpsell, - ContinueThread, - ContinueWithBurnMode, - ToggleBurnMode, - ] -); - -#[derive(Default, Clone, PartialEq, Deserialize, JsonSchema)] -pub struct NewThread { - #[serde(default)] - from_thread_id: Option, -} - -#[derive(PartialEq, Clone, Default, Debug, Deserialize, JsonSchema)] -pub struct ManageProfiles { - #[serde(default)] - pub customize_tools: Option, -} - -impl ManageProfiles { - pub fn customize_tools(profile_id: AgentProfileId) -> Self { - Self { - customize_tools: Some(profile_id), - } - } -} - -impl_actions!(agent, [NewThread, ManageProfiles]); - -#[derive(Clone)] -pub(crate) enum ModelUsageContext { - Thread(Entity), - InlineAssistant, -} - -impl ModelUsageContext { - pub fn configured_model(&self, cx: &App) -> Option { - match self { - Self::Thread(thread) => thread.read(cx).configured_model(), - Self::InlineAssistant => { - LanguageModelRegistry::read_global(cx).inline_assistant_model() - } - } - } - - pub fn language_model(&self, cx: &App) -> Option> { - self.configured_model(cx) - .map(|configured_model| configured_model.model) - } -} - -/// Initializes the `agent` crate. -pub fn init( - fs: Arc, - client: Arc, - prompt_builder: Arc, - language_registry: Arc, - is_eval: bool, - cx: &mut App, -) { - AgentSettings::register(cx); - SlashCommandSettings::register(cx); - - assistant_context_editor::init(client.clone(), cx); - rules_library::init(cx); - if !is_eval { - // Initializing the language model from the user settings messes with the eval, so we only initialize them when - // we're not running inside of the eval. - init_language_model_settings(cx); - } - assistant_slash_command::init(cx); +pub fn init(cx: &mut gpui::App) { thread_store::init(cx); - agent_panel::init(cx); - context_server_configuration::init(language_registry.clone(), fs.clone(), cx); - - register_slash_commands(cx); - inline_assistant::init( - fs.clone(), - prompt_builder.clone(), - client.telemetry().clone(), - cx, - ); - terminal_inline_assistant::init( - fs.clone(), - prompt_builder.clone(), - client.telemetry().clone(), - cx, - ); - indexed_docs::init(cx); - cx.observe_new(move |workspace, window, cx| { - ConfigureContextServerModal::register(workspace, language_registry.clone(), window, cx) - }) - .detach(); - cx.observe_new(ManageProfilesModal::register).detach(); -} - -fn init_language_model_settings(cx: &mut App) { - update_active_language_model_from_settings(cx); - - cx.observe_global::(update_active_language_model_from_settings) - .detach(); - cx.subscribe( - &LanguageModelRegistry::global(cx), - |_, event: &language_model::Event, cx| match event { - language_model::Event::ProviderStateChanged - | language_model::Event::AddedProvider(_) - | language_model::Event::RemovedProvider(_) => { - update_active_language_model_from_settings(cx); - } - _ => {} - }, - ) - .detach(); -} - -fn update_active_language_model_from_settings(cx: &mut App) { - let settings = AgentSettings::get_global(cx); - - fn to_selected_model(selection: &LanguageModelSelection) -> language_model::SelectedModel { - language_model::SelectedModel { - provider: LanguageModelProviderId::from(selection.provider.0.clone()), - model: LanguageModelId::from(selection.model.clone()), - } - } - - let default = to_selected_model(&settings.default_model); - let inline_assistant = settings - .inline_assistant_model - .as_ref() - .map(to_selected_model); - let commit_message = settings - .commit_message_model - .as_ref() - .map(to_selected_model); - let thread_summary = settings - .thread_summary_model - .as_ref() - .map(to_selected_model); - let inline_alternatives = settings - .inline_alternatives - .iter() - .map(to_selected_model) - .collect::>(); - - LanguageModelRegistry::global(cx).update(cx, |registry, cx| { - registry.select_default_model(Some(&default), cx); - registry.select_inline_assistant_model(inline_assistant.as_ref(), cx); - registry.select_commit_message_model(commit_message.as_ref(), cx); - registry.select_thread_summary_model(thread_summary.as_ref(), cx); - registry.select_inline_alternative_models(inline_alternatives, cx); - }); -} - -fn register_slash_commands(cx: &mut App) { - let slash_command_registry = SlashCommandRegistry::global(cx); - - slash_command_registry.register_command(assistant_slash_commands::FileSlashCommand, true); - slash_command_registry.register_command(assistant_slash_commands::DeltaSlashCommand, true); - slash_command_registry.register_command(assistant_slash_commands::OutlineSlashCommand, true); - slash_command_registry.register_command(assistant_slash_commands::TabSlashCommand, true); - slash_command_registry - .register_command(assistant_slash_commands::CargoWorkspaceSlashCommand, true); - slash_command_registry.register_command(assistant_slash_commands::PromptSlashCommand, true); - slash_command_registry.register_command(assistant_slash_commands::SelectionCommand, true); - slash_command_registry.register_command(assistant_slash_commands::DefaultSlashCommand, false); - slash_command_registry.register_command(assistant_slash_commands::NowSlashCommand, false); - slash_command_registry - .register_command(assistant_slash_commands::DiagnosticsSlashCommand, true); - slash_command_registry.register_command(assistant_slash_commands::FetchSlashCommand, true); - - cx.observe_flag::({ - let slash_command_registry = slash_command_registry.clone(); - move |is_enabled, _cx| { - if is_enabled { - slash_command_registry.register_command( - assistant_slash_commands::StreamingExampleSlashCommand, - false, - ); - } - } - }) - .detach(); - - update_slash_commands_from_settings(cx); - cx.observe_global::(update_slash_commands_from_settings) - .detach(); -} - -fn update_slash_commands_from_settings(cx: &mut App) { - let slash_command_registry = SlashCommandRegistry::global(cx); - let settings = SlashCommandSettings::get_global(cx); - - if settings.docs.enabled { - slash_command_registry.register_command(assistant_slash_commands::DocsSlashCommand, true); - } else { - slash_command_registry.unregister_command(assistant_slash_commands::DocsSlashCommand); - } - - if settings.cargo_workspace.enabled { - slash_command_registry - .register_command(assistant_slash_commands::CargoWorkspaceSlashCommand, true); - } else { - slash_command_registry - .unregister_command(assistant_slash_commands::CargoWorkspaceSlashCommand); - } } diff --git a/crates/agent/src/agent_profile.rs b/crates/agent/src/agent_profile.rs index 5cd69bd324..c27a534a56 100644 --- a/crates/agent/src/agent_profile.rs +++ b/crates/agent/src/agent_profile.rs @@ -5,9 +5,8 @@ use assistant_tool::{Tool, ToolSource, ToolWorkingSet}; use collections::IndexMap; use convert_case::{Case, Casing}; use fs::Fs; -use gpui::{App, Entity}; +use gpui::{App, Entity, SharedString}; use settings::{Settings, update_settings_file}; -use ui::SharedString; use util::ResultExt; #[derive(Clone, Debug, Eq, PartialEq)] @@ -108,11 +107,11 @@ mod tests { use agent_settings::ContextServerPreset; use assistant_tool::ToolRegistry; use collections::IndexMap; + use gpui::SharedString; use gpui::{AppContext, TestAppContext}; use http_client::FakeHttpClient; use project::Project; use settings::{Settings, SettingsStore}; - use ui::SharedString; use super::*; @@ -302,7 +301,7 @@ mod tests { unimplemented!() } - fn icon(&self) -> ui::IconName { + fn icon(&self) -> icons::IconName { unimplemented!() } diff --git a/crates/agent/src/context.rs b/crates/agent/src/context.rs index aaf613ea5f..bfbea476f6 100644 --- a/crates/agent/src/context.rs +++ b/crates/agent/src/context.rs @@ -1,30 +1,25 @@ -use std::fmt::{self, Display, Formatter, Write as _}; -use std::hash::{Hash, Hasher}; -use std::path::PathBuf; -use std::{ops::Range, path::Path, sync::Arc}; - +use crate::thread::Thread; use assistant_context_editor::AssistantContext; use assistant_tool::outline; -use collections::{HashMap, HashSet}; -use editor::display_map::CreaseId; -use editor::{Addon, Editor}; +use collections::HashSet; use futures::future; use futures::{FutureExt, future::Shared}; -use gpui::{App, AppContext as _, Entity, SharedString, Subscription, Task}; +use gpui::{App, AppContext as _, ElementId, Entity, SharedString, Task}; +use icons::IconName; use language::{Buffer, ParseStatus}; use language_model::{LanguageModelImage, LanguageModelRequestMessage, MessageContent}; use project::{Project, ProjectEntryId, ProjectPath, Worktree}; use prompt_store::{PromptStore, UserPromptId}; use ref_cast::RefCast; use rope::Point; +use std::fmt::{self, Display, Formatter, Write as _}; +use std::hash::{Hash, Hasher}; +use std::path::PathBuf; +use std::{ops::Range, path::Path, sync::Arc}; use text::{Anchor, OffsetRangeExt as _}; -use ui::{Context, ElementId, IconName}; use util::markdown::MarkdownCodeBlock; use util::{ResultExt as _, post_inc}; -use crate::context_store::{ContextStore, ContextStoreEvent}; -use crate::thread::Thread; - pub const RULES_ICON: IconName = IconName::Context; pub enum ContextKind { @@ -1117,69 +1112,6 @@ impl Hash for AgentContextKey { } } -#[derive(Default)] -pub struct ContextCreasesAddon { - creases: HashMap>, - _subscription: Option, -} - -impl Addon for ContextCreasesAddon { - fn to_any(&self) -> &dyn std::any::Any { - self - } - - fn to_any_mut(&mut self) -> Option<&mut dyn std::any::Any> { - Some(self) - } -} - -impl ContextCreasesAddon { - pub fn new() -> Self { - Self { - creases: HashMap::default(), - _subscription: None, - } - } - - pub fn add_creases( - &mut self, - context_store: &Entity, - key: AgentContextKey, - creases: impl IntoIterator, - cx: &mut Context, - ) { - self.creases.entry(key).or_default().extend(creases); - self._subscription = Some(cx.subscribe( - &context_store, - |editor, _, event, cx| match event { - ContextStoreEvent::ContextRemoved(key) => { - let Some(this) = editor.addon_mut::() else { - return; - }; - let (crease_ids, replacement_texts): (Vec<_>, Vec<_>) = this - .creases - .remove(key) - .unwrap_or_default() - .into_iter() - .unzip(); - let ranges = editor - .remove_creases(crease_ids, cx) - .into_iter() - .map(|(_, range)| range) - .collect::>(); - editor.unfold_ranges(&ranges, false, false, cx); - editor.edit(ranges.into_iter().zip(replacement_texts), cx); - cx.notify(); - } - }, - )) - } - - pub fn into_inner(self) -> HashMap> { - self.creases - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/crates/agent/src/context_server_tool.rs b/crates/agent/src/context_server_tool.rs index 17571fca04..da7de1e312 100644 --- a/crates/agent/src/context_server_tool.rs +++ b/crates/agent/src/context_server_tool.rs @@ -4,9 +4,9 @@ use anyhow::{Result, anyhow, bail}; use assistant_tool::{ActionLog, Tool, ToolResult, ToolSource}; use context_server::{ContextServerId, types}; use gpui::{AnyWindowHandle, App, Entity, Task}; +use icons::IconName; use language_model::{LanguageModel, LanguageModelRequest, LanguageModelToolSchemaFormat}; use project::{Project, context_server_store::ContextServerStore}; -use ui::IconName; pub struct ContextServerTool { store: Entity, diff --git a/crates/agent/src/context_store.rs b/crates/agent/src/context_store.rs index f4697d9eb4..3e43e4dd2a 100644 --- a/crates/agent/src/context_store.rs +++ b/crates/agent/src/context_store.rs @@ -1,7 +1,12 @@ -use std::ops::Range; -use std::path::{Path, PathBuf}; -use std::sync::Arc; - +use crate::{ + context::{ + AgentContextHandle, AgentContextKey, ContextId, ContextKind, DirectoryContextHandle, + FetchedUrlContext, FileContextHandle, ImageContext, RulesContextHandle, + SelectionContextHandle, SymbolContextHandle, TextThreadContextHandle, ThreadContextHandle, + }, + thread::{MessageId, Thread, ThreadId}, + thread_store::ThreadStore, +}; use anyhow::{Context as _, Result, anyhow}; use assistant_context_editor::AssistantContext; use collections::{HashSet, IndexSet}; @@ -9,20 +14,15 @@ use futures::{self, FutureExt}; use gpui::{App, Context, Entity, EventEmitter, Image, SharedString, Task, WeakEntity}; use language::{Buffer, File as _}; use language_model::LanguageModelImage; -use project::image_store::is_image_file; -use project::{Project, ProjectItem, ProjectPath, Symbol}; +use project::{Project, ProjectItem, ProjectPath, Symbol, image_store::is_image_file}; use prompt_store::UserPromptId; use ref_cast::RefCast as _; -use text::{Anchor, OffsetRangeExt}; - -use crate::ThreadStore; -use crate::context::{ - AgentContextHandle, AgentContextKey, ContextId, DirectoryContextHandle, FetchedUrlContext, - FileContextHandle, ImageContext, RulesContextHandle, SelectionContextHandle, - SymbolContextHandle, TextThreadContextHandle, ThreadContextHandle, +use std::{ + ops::Range, + path::{Path, PathBuf}, + sync::Arc, }; -use crate::context_strip::SuggestedContext; -use crate::thread::{MessageId, Thread, ThreadId}; +use text::{Anchor, OffsetRangeExt}; pub struct ContextStore { project: WeakEntity, @@ -561,6 +561,49 @@ impl ContextStore { } } +#[derive(Clone)] +pub enum SuggestedContext { + File { + name: SharedString, + icon_path: Option, + buffer: WeakEntity, + }, + Thread { + name: SharedString, + thread: WeakEntity, + }, + TextThread { + name: SharedString, + context: WeakEntity, + }, +} + +impl SuggestedContext { + pub fn name(&self) -> &SharedString { + match self { + Self::File { name, .. } => name, + Self::Thread { name, .. } => name, + Self::TextThread { name, .. } => name, + } + } + + pub fn icon_path(&self) -> Option { + match self { + Self::File { icon_path, .. } => icon_path.clone(), + Self::Thread { .. } => None, + Self::TextThread { .. } => None, + } + } + + pub fn kind(&self) -> ContextKind { + match self { + Self::File { .. } => ContextKind::File, + Self::Thread { .. } => ContextKind::Thread, + Self::TextThread { .. } => ContextKind::TextThread, + } + } +} + pub enum FileInclusion { Direct, InDirectory { full_path: PathBuf }, diff --git a/crates/agent/src/history_store.rs b/crates/agent/src/history_store.rs index 61fc430ddb..47dbd894df 100644 --- a/crates/agent/src/history_store.rs +++ b/crates/agent/src/history_store.rs @@ -1,21 +1,17 @@ -use std::{collections::VecDeque, path::Path, sync::Arc}; - +use crate::{ + ThreadId, + thread_store::{SerializedThreadMetadata, ThreadStore}, +}; use anyhow::{Context as _, Result}; use assistant_context_editor::SavedContextMetadata; use chrono::{DateTime, Utc}; -use gpui::{AsyncApp, Entity, SharedString, Task, prelude::*}; +use gpui::{App, AsyncApp, Entity, SharedString, Task, prelude::*}; use itertools::Itertools; use paths::contexts_dir; use serde::{Deserialize, Serialize}; -use std::time::Duration; -use ui::App; +use std::{collections::VecDeque, path::Path, sync::Arc, time::Duration}; use util::ResultExt as _; -use crate::{ - thread::ThreadId, - thread_store::{SerializedThreadMetadata, ThreadStore}, -}; - const MAX_RECENTLY_OPENED_ENTRIES: usize = 6; const NAVIGATION_HISTORY_PATH: &str = "agent-navigation-history.json"; const SAVE_RECENTLY_OPENED_ENTRIES_DEBOUNCE: Duration = Duration::from_millis(50); diff --git a/crates/agent/src/thread.rs b/crates/agent/src/thread.rs index e3080fd0ad..7a08de7a0b 100644 --- a/crates/agent/src/thread.rs +++ b/crates/agent/src/thread.rs @@ -1,22 +1,25 @@ -use std::io::Write; -use std::ops::Range; -use std::sync::Arc; -use std::time::Instant; - +use crate::{ + agent_profile::AgentProfile, + context::{AgentContext, AgentContextHandle, ContextLoadResult, LoadedContext}, + thread_store::{ + SerializedCrease, SerializedLanguageModel, SerializedMessage, SerializedMessageSegment, + SerializedThread, SerializedToolResult, SerializedToolUse, SharedProjectContext, + ThreadStore, + }, + tool_use::{PendingToolUse, ToolUse, ToolUseMetadata, ToolUseState}, +}; use agent_settings::{AgentProfileId, AgentSettings, CompletionMode}; use anyhow::{Result, anyhow}; use assistant_tool::{ActionLog, AnyToolCard, Tool, ToolWorkingSet}; use chrono::{DateTime, Utc}; use client::{ModelRequestUsage, RequestUsage}; use collections::{HashMap, HashSet}; -use editor::display_map::CreaseMetadata; use feature_flags::{self, FeatureFlagAppExt}; -use futures::future::Shared; -use futures::{FutureExt, StreamExt as _}; +use futures::{FutureExt, StreamExt as _, future::Shared}; use git::repository::DiffType; use gpui::{ AnyWindowHandle, App, AppContext, AsyncApp, Context, Entity, EventEmitter, SharedString, Task, - WeakEntity, + WeakEntity, Window, }; use language_model::{ ConfiguredModel, LanguageModel, LanguageModelCompletionError, LanguageModelCompletionEvent, @@ -27,29 +30,21 @@ use language_model::{ TokenUsage, }; use postage::stream::Stream as _; -use project::Project; -use project::git_store::{GitStore, GitStoreCheckpoint, RepositoryState}; +use project::{ + Project, + git_store::{GitStore, GitStoreCheckpoint, RepositoryState}, +}; use prompt_store::{ModelContext, PromptBuilder}; use proto::Plan; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use settings::Settings; +use std::{io::Write, ops::Range, sync::Arc, time::Instant}; use thiserror::Error; -use ui::Window; use util::{ResultExt as _, post_inc}; - use uuid::Uuid; use zed_llm_client::{CompletionIntent, CompletionRequestStatus, UsageLimit}; -use crate::ThreadStore; -use crate::agent_profile::AgentProfile; -use crate::context::{AgentContext, AgentContextHandle, ContextLoadResult, LoadedContext}; -use crate::thread_store::{ - SerializedCrease, SerializedLanguageModel, SerializedMessage, SerializedMessageSegment, - SerializedThread, SerializedToolResult, SerializedToolUse, SharedProjectContext, -}; -use crate::tool_use::{PendingToolUse, ToolUse, ToolUseMetadata, ToolUseState}; - #[derive( Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Serialize, Deserialize, JsonSchema, )] @@ -98,13 +93,18 @@ impl MessageId { fn post_inc(&mut self) -> Self { Self(post_inc(&mut self.0)) } + + pub fn as_usize(&self) -> usize { + self.0 + } } /// Stored information that can be used to resurrect a context crease when creating an editor for a past message. #[derive(Clone, Debug)] pub struct MessageCrease { pub range: Range, - pub metadata: CreaseMetadata, + pub icon_path: SharedString, + pub label: SharedString, /// None for a deserialized message, Some otherwise. pub context: Option, } @@ -540,10 +540,8 @@ impl Thread { .into_iter() .map(|crease| MessageCrease { range: crease.start..crease.end, - metadata: CreaseMetadata { - icon_path: crease.icon_path, - label: crease.label, - }, + icon_path: crease.icon_path, + label: crease.label, context: None, }) .collect(), @@ -1170,8 +1168,8 @@ impl Thread { .map(|crease| SerializedCrease { start: crease.range.start, end: crease.range.end, - icon_path: crease.metadata.icon_path.clone(), - label: crease.metadata.label.clone(), + icon_path: crease.icon_path.clone(), + label: crease.label.clone(), }) .collect(), is_hidden: message.is_hidden, @@ -2997,11 +2995,13 @@ fn resolve_tool_name_conflicts(tools: &[Arc]) -> Vec<(String, Arc, prompt_store: Option>, - inline_assist_context_store: Entity, + inline_assist_context_store: Entity, configuration: Option>, configuration_subscription: Option, local_timezone: UtcOffset, @@ -490,18 +494,10 @@ impl AgentPanel { let workspace = workspace.weak_handle(); let weak_self = cx.entity().downgrade(); - let message_editor_context_store = cx.new(|_cx| { - crate::context_store::ContextStore::new( - project.downgrade(), - Some(thread_store.downgrade()), - ) - }); - let inline_assist_context_store = cx.new(|_cx| { - crate::context_store::ContextStore::new( - project.downgrade(), - Some(thread_store.downgrade()), - ) - }); + let message_editor_context_store = + cx.new(|_cx| ContextStore::new(project.downgrade(), Some(thread_store.downgrade()))); + let inline_assist_context_store = + cx.new(|_cx| ContextStore::new(project.downgrade(), Some(thread_store.downgrade()))); let message_editor = cx.new(|cx| { MessageEditor::new( @@ -708,9 +704,7 @@ impl AgentPanel { &self.prompt_store } - pub(crate) fn inline_assist_context_store( - &self, - ) -> &Entity { + pub(crate) fn inline_assist_context_store(&self) -> &Entity { &self.inline_assist_context_store } @@ -742,7 +736,7 @@ impl AgentPanel { self.set_active_view(thread_view, window, cx); let context_store = cx.new(|_cx| { - crate::context_store::ContextStore::new( + ContextStore::new( self.project.downgrade(), Some(self.thread_store.downgrade()), ) @@ -990,7 +984,7 @@ impl AgentPanel { let thread_view = ActiveView::thread(thread.clone(), window, cx); self.set_active_view(thread_view, window, cx); let context_store = cx.new(|_cx| { - crate::context_store::ContextStore::new( + ContextStore::new( self.project.downgrade(), Some(self.thread_store.downgrade()), ) diff --git a/crates/agent_ui/src/agent_ui.rs b/crates/agent_ui/src/agent_ui.rs new file mode 100644 index 0000000000..eee0ab1993 --- /dev/null +++ b/crates/agent_ui/src/agent_ui.rs @@ -0,0 +1,285 @@ +mod active_thread; +mod agent_configuration; +mod agent_diff; +mod agent_model_selector; +mod agent_panel; +mod buffer_codegen; +mod context_picker; +mod context_server_configuration; +mod context_strip; +mod debug; +mod inline_assistant; +mod inline_prompt_editor; +mod message_editor; +mod profile_selector; +mod slash_command_settings; +mod terminal_codegen; +mod terminal_inline_assistant; +mod thread_history; +mod tool_compatibility; +mod ui; + +use std::sync::Arc; + +use agent::{Thread, ThreadId}; +use agent_settings::{AgentProfileId, AgentSettings, LanguageModelSelection}; +use assistant_slash_command::SlashCommandRegistry; +use client::Client; +use feature_flags::FeatureFlagAppExt as _; +use fs::Fs; +use gpui::{App, Entity, actions, impl_actions}; +use language::LanguageRegistry; +use language_model::{ + ConfiguredModel, LanguageModel, LanguageModelId, LanguageModelProviderId, LanguageModelRegistry, +}; +use prompt_store::PromptBuilder; +use schemars::JsonSchema; +use serde::Deserialize; +use settings::{Settings as _, SettingsStore}; + +pub use crate::active_thread::ActiveThread; +use crate::agent_configuration::{ConfigureContextServerModal, ManageProfilesModal}; +pub use crate::agent_panel::{AgentPanel, ConcreteAssistantPanelDelegate}; +pub use crate::inline_assistant::InlineAssistant; +use crate::slash_command_settings::SlashCommandSettings; +pub use agent_diff::{AgentDiffPane, AgentDiffToolbar}; +pub use ui::preview::{all_agent_previews, get_agent_preview}; + +actions!( + agent, + [ + NewTextThread, + ToggleContextPicker, + ToggleNavigationMenu, + ToggleOptionsMenu, + DeleteRecentlyOpenThread, + ToggleProfileSelector, + RemoveAllContext, + ExpandMessageEditor, + OpenHistory, + AddContextServer, + RemoveSelectedThread, + Chat, + ChatWithFollow, + CycleNextInlineAssist, + CyclePreviousInlineAssist, + FocusUp, + FocusDown, + FocusLeft, + FocusRight, + RemoveFocusedContext, + AcceptSuggestedContext, + OpenActiveThreadAsMarkdown, + OpenAgentDiff, + Keep, + Reject, + RejectAll, + KeepAll, + Follow, + ResetTrialUpsell, + ResetTrialEndUpsell, + ContinueThread, + ContinueWithBurnMode, + ToggleBurnMode, + ] +); + +#[derive(Default, Clone, PartialEq, Deserialize, JsonSchema)] +pub struct NewThread { + #[serde(default)] + from_thread_id: Option, +} + +#[derive(PartialEq, Clone, Default, Debug, Deserialize, JsonSchema)] +pub struct ManageProfiles { + #[serde(default)] + pub customize_tools: Option, +} + +impl ManageProfiles { + pub fn customize_tools(profile_id: AgentProfileId) -> Self { + Self { + customize_tools: Some(profile_id), + } + } +} + +impl_actions!(agent, [NewThread, ManageProfiles]); + +#[derive(Clone)] +pub(crate) enum ModelUsageContext { + Thread(Entity), + InlineAssistant, +} + +impl ModelUsageContext { + pub fn configured_model(&self, cx: &App) -> Option { + match self { + Self::Thread(thread) => thread.read(cx).configured_model(), + Self::InlineAssistant => { + LanguageModelRegistry::read_global(cx).inline_assistant_model() + } + } + } + + pub fn language_model(&self, cx: &App) -> Option> { + self.configured_model(cx) + .map(|configured_model| configured_model.model) + } +} + +/// Initializes the `agent` crate. +pub fn init( + fs: Arc, + client: Arc, + prompt_builder: Arc, + language_registry: Arc, + is_eval: bool, + cx: &mut App, +) { + AgentSettings::register(cx); + SlashCommandSettings::register(cx); + + assistant_context_editor::init(client.clone(), cx); + rules_library::init(cx); + if !is_eval { + // Initializing the language model from the user settings messes with the eval, so we only initialize them when + // we're not running inside of the eval. + init_language_model_settings(cx); + } + assistant_slash_command::init(cx); + agent::init(cx); + agent_panel::init(cx); + context_server_configuration::init(language_registry.clone(), fs.clone(), cx); + + register_slash_commands(cx); + inline_assistant::init( + fs.clone(), + prompt_builder.clone(), + client.telemetry().clone(), + cx, + ); + terminal_inline_assistant::init( + fs.clone(), + prompt_builder.clone(), + client.telemetry().clone(), + cx, + ); + indexed_docs::init(cx); + cx.observe_new(move |workspace, window, cx| { + ConfigureContextServerModal::register(workspace, language_registry.clone(), window, cx) + }) + .detach(); + cx.observe_new(ManageProfilesModal::register).detach(); +} + +fn init_language_model_settings(cx: &mut App) { + update_active_language_model_from_settings(cx); + + cx.observe_global::(update_active_language_model_from_settings) + .detach(); + cx.subscribe( + &LanguageModelRegistry::global(cx), + |_, event: &language_model::Event, cx| match event { + language_model::Event::ProviderStateChanged + | language_model::Event::AddedProvider(_) + | language_model::Event::RemovedProvider(_) => { + update_active_language_model_from_settings(cx); + } + _ => {} + }, + ) + .detach(); +} + +fn update_active_language_model_from_settings(cx: &mut App) { + let settings = AgentSettings::get_global(cx); + + fn to_selected_model(selection: &LanguageModelSelection) -> language_model::SelectedModel { + language_model::SelectedModel { + provider: LanguageModelProviderId::from(selection.provider.0.clone()), + model: LanguageModelId::from(selection.model.clone()), + } + } + + let default = to_selected_model(&settings.default_model); + let inline_assistant = settings + .inline_assistant_model + .as_ref() + .map(to_selected_model); + let commit_message = settings + .commit_message_model + .as_ref() + .map(to_selected_model); + let thread_summary = settings + .thread_summary_model + .as_ref() + .map(to_selected_model); + let inline_alternatives = settings + .inline_alternatives + .iter() + .map(to_selected_model) + .collect::>(); + + LanguageModelRegistry::global(cx).update(cx, |registry, cx| { + registry.select_default_model(Some(&default), cx); + registry.select_inline_assistant_model(inline_assistant.as_ref(), cx); + registry.select_commit_message_model(commit_message.as_ref(), cx); + registry.select_thread_summary_model(thread_summary.as_ref(), cx); + registry.select_inline_alternative_models(inline_alternatives, cx); + }); +} + +fn register_slash_commands(cx: &mut App) { + let slash_command_registry = SlashCommandRegistry::global(cx); + + slash_command_registry.register_command(assistant_slash_commands::FileSlashCommand, true); + slash_command_registry.register_command(assistant_slash_commands::DeltaSlashCommand, true); + slash_command_registry.register_command(assistant_slash_commands::OutlineSlashCommand, true); + slash_command_registry.register_command(assistant_slash_commands::TabSlashCommand, true); + slash_command_registry + .register_command(assistant_slash_commands::CargoWorkspaceSlashCommand, true); + slash_command_registry.register_command(assistant_slash_commands::PromptSlashCommand, true); + slash_command_registry.register_command(assistant_slash_commands::SelectionCommand, true); + slash_command_registry.register_command(assistant_slash_commands::DefaultSlashCommand, false); + slash_command_registry.register_command(assistant_slash_commands::NowSlashCommand, false); + slash_command_registry + .register_command(assistant_slash_commands::DiagnosticsSlashCommand, true); + slash_command_registry.register_command(assistant_slash_commands::FetchSlashCommand, true); + + cx.observe_flag::({ + let slash_command_registry = slash_command_registry.clone(); + move |is_enabled, _cx| { + if is_enabled { + slash_command_registry.register_command( + assistant_slash_commands::StreamingExampleSlashCommand, + false, + ); + } + } + }) + .detach(); + + update_slash_commands_from_settings(cx); + cx.observe_global::(update_slash_commands_from_settings) + .detach(); +} + +fn update_slash_commands_from_settings(cx: &mut App) { + let slash_command_registry = SlashCommandRegistry::global(cx); + let settings = SlashCommandSettings::get_global(cx); + + if settings.docs.enabled { + slash_command_registry.register_command(assistant_slash_commands::DocsSlashCommand, true); + } else { + slash_command_registry.unregister_command(assistant_slash_commands::DocsSlashCommand); + } + + if settings.cargo_workspace.enabled { + slash_command_registry + .register_command(assistant_slash_commands::CargoWorkspaceSlashCommand, true); + } else { + slash_command_registry + .unregister_command(assistant_slash_commands::CargoWorkspaceSlashCommand); + } +} diff --git a/crates/agent/src/buffer_codegen.rs b/crates/agent_ui/src/buffer_codegen.rs similarity index 99% rename from crates/agent/src/buffer_codegen.rs rename to crates/agent_ui/src/buffer_codegen.rs index e566ea9d86..f3919a958f 100644 --- a/crates/agent/src/buffer_codegen.rs +++ b/crates/agent_ui/src/buffer_codegen.rs @@ -1,6 +1,8 @@ -use crate::context::ContextLoadResult; use crate::inline_prompt_editor::CodegenStatus; -use crate::{context::load_context, context_store::ContextStore}; +use agent::{ + ContextStore, + context::{ContextLoadResult, load_context}, +}; use agent_settings::AgentSettings; use anyhow::{Context as _, Result}; use client::telemetry::Telemetry; @@ -18,8 +20,7 @@ use language_model::{ use multi_buffer::MultiBufferRow; use parking_lot::Mutex; use project::Project; -use prompt_store::PromptBuilder; -use prompt_store::PromptStore; +use prompt_store::{PromptBuilder, PromptStore}; use rope::Rope; use smol::future::FutureExt; use std::{ diff --git a/crates/agent/src/context_picker.rs b/crates/agent_ui/src/context_picker.rs similarity index 99% rename from crates/agent/src/context_picker.rs rename to crates/agent_ui/src/context_picker.rs index 336fee056b..9136307517 100644 --- a/crates/agent/src/context_picker.rs +++ b/crates/agent_ui/src/context_picker.rs @@ -37,10 +37,12 @@ use uuid::Uuid; use workspace::{Workspace, notifications::NotifyResultExt}; use crate::AgentPanel; -use crate::context::RULES_ICON; -use crate::context_store::ContextStore; -use crate::thread::ThreadId; -use crate::thread_store::{TextThreadStore, ThreadStore}; +use agent::{ + ThreadId, + context::RULES_ICON, + context_store::ContextStore, + thread_store::{TextThreadStore, ThreadStore}, +}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum ContextPickerEntry { diff --git a/crates/agent/src/context_picker/completion_provider.rs b/crates/agent_ui/src/context_picker/completion_provider.rs similarity index 99% rename from crates/agent/src/context_picker/completion_provider.rs rename to crates/agent_ui/src/context_picker/completion_provider.rs index 67e9f801fc..4c05206748 100644 --- a/crates/agent/src/context_picker/completion_provider.rs +++ b/crates/agent_ui/src/context_picker/completion_provider.rs @@ -3,6 +3,7 @@ use std::path::{Path, PathBuf}; use std::sync::Arc; use std::sync::atomic::AtomicBool; +use agent::context_store::ContextStore; use anyhow::Result; use editor::{CompletionProvider, Editor, ExcerptId, ToOffset as _}; use file_icons::FileIcons; @@ -20,10 +21,11 @@ use ui::prelude::*; use util::ResultExt as _; use workspace::Workspace; -use crate::Thread; -use crate::context::{AgentContextHandle, AgentContextKey, ContextCreasesAddon, RULES_ICON}; -use crate::context_store::ContextStore; -use crate::thread_store::{TextThreadStore, ThreadStore}; +use agent::{ + Thread, + context::{AgentContextHandle, AgentContextKey, RULES_ICON}, + thread_store::{TextThreadStore, ThreadStore}, +}; use super::fetch_context_picker::fetch_url_content; use super::file_context_picker::{FileMatch, search_files}; @@ -35,6 +37,7 @@ use super::{ ContextPickerAction, ContextPickerEntry, ContextPickerMode, MentionLink, RecentEntry, available_context_picker_entries, recent_context_picker_entries, selection_ranges, }; +use crate::message_editor::ContextCreasesAddon; pub(crate) enum Match { File(FileMatch), diff --git a/crates/agent/src/context_picker/fetch_context_picker.rs b/crates/agent_ui/src/context_picker/fetch_context_picker.rs similarity index 99% rename from crates/agent/src/context_picker/fetch_context_picker.rs rename to crates/agent_ui/src/context_picker/fetch_context_picker.rs index bd32e44f9b..8ff68a8365 100644 --- a/crates/agent/src/context_picker/fetch_context_picker.rs +++ b/crates/agent_ui/src/context_picker/fetch_context_picker.rs @@ -2,6 +2,7 @@ use std::cell::RefCell; use std::rc::Rc; use std::sync::Arc; +use agent::context_store::ContextStore; use anyhow::{Context as _, Result, bail}; use futures::AsyncReadExt as _; use gpui::{App, DismissEvent, Entity, FocusHandle, Focusable, Task, WeakEntity}; @@ -12,7 +13,6 @@ use ui::{Context, ListItem, Window, prelude::*}; use workspace::Workspace; use crate::context_picker::ContextPicker; -use crate::context_store::ContextStore; pub struct FetchContextPicker { picker: Entity>, diff --git a/crates/agent/src/context_picker/file_context_picker.rs b/crates/agent_ui/src/context_picker/file_context_picker.rs similarity index 99% rename from crates/agent/src/context_picker/file_context_picker.rs rename to crates/agent_ui/src/context_picker/file_context_picker.rs index be00644163..eaf9ed16d6 100644 --- a/crates/agent/src/context_picker/file_context_picker.rs +++ b/crates/agent_ui/src/context_picker/file_context_picker.rs @@ -14,7 +14,7 @@ use util::ResultExt as _; use workspace::Workspace; use crate::context_picker::ContextPicker; -use crate::context_store::{ContextStore, FileInclusion}; +use agent::context_store::{ContextStore, FileInclusion}; pub struct FileContextPicker { picker: Entity>, diff --git a/crates/agent/src/context_picker/rules_context_picker.rs b/crates/agent_ui/src/context_picker/rules_context_picker.rs similarity index 98% rename from crates/agent/src/context_picker/rules_context_picker.rs rename to crates/agent_ui/src/context_picker/rules_context_picker.rs index ef4676e4c3..8ce821cfaa 100644 --- a/crates/agent/src/context_picker/rules_context_picker.rs +++ b/crates/agent_ui/src/context_picker/rules_context_picker.rs @@ -7,9 +7,9 @@ use prompt_store::{PromptId, PromptStore, UserPromptId}; use ui::{ListItem, prelude::*}; use util::ResultExt as _; -use crate::context::RULES_ICON; use crate::context_picker::ContextPicker; -use crate::context_store::{self, ContextStore}; +use agent::context::RULES_ICON; +use agent::context_store::{self, ContextStore}; pub struct RulesContextPicker { picker: Entity>, diff --git a/crates/agent/src/context_picker/symbol_context_picker.rs b/crates/agent_ui/src/context_picker/symbol_context_picker.rs similarity index 99% rename from crates/agent/src/context_picker/symbol_context_picker.rs rename to crates/agent_ui/src/context_picker/symbol_context_picker.rs index f031353628..05e77deece 100644 --- a/crates/agent/src/context_picker/symbol_context_picker.rs +++ b/crates/agent_ui/src/context_picker/symbol_context_picker.rs @@ -14,9 +14,9 @@ use ui::{ListItem, prelude::*}; use util::ResultExt as _; use workspace::Workspace; -use crate::context::AgentContextHandle; use crate::context_picker::ContextPicker; -use crate::context_store::ContextStore; +use agent::context::AgentContextHandle; +use agent::context_store::ContextStore; pub struct SymbolContextPicker { picker: Entity>, diff --git a/crates/agent/src/context_picker/thread_context_picker.rs b/crates/agent_ui/src/context_picker/thread_context_picker.rs similarity index 98% rename from crates/agent/src/context_picker/thread_context_picker.rs rename to crates/agent_ui/src/context_picker/thread_context_picker.rs index ee9608a8d8..cb2e97a493 100644 --- a/crates/agent/src/context_picker/thread_context_picker.rs +++ b/crates/agent_ui/src/context_picker/thread_context_picker.rs @@ -9,9 +9,11 @@ use picker::{Picker, PickerDelegate}; use ui::{ListItem, prelude::*}; use crate::context_picker::ContextPicker; -use crate::context_store::{self, ContextStore}; -use crate::thread::ThreadId; -use crate::thread_store::{TextThreadStore, ThreadStore}; +use agent::{ + ThreadId, + context_store::{self, ContextStore}, + thread_store::{TextThreadStore, ThreadStore}, +}; pub struct ThreadContextPicker { picker: Entity>, diff --git a/crates/agent/src/context_server_configuration.rs b/crates/agent_ui/src/context_server_configuration.rs similarity index 100% rename from crates/agent/src/context_server_configuration.rs rename to crates/agent_ui/src/context_server_configuration.rs diff --git a/crates/agent/src/context_strip.rs b/crates/agent_ui/src/context_strip.rs similarity index 93% rename from crates/agent/src/context_strip.rs rename to crates/agent_ui/src/context_strip.rs index 2de2dcd024..b3890613dc 100644 --- a/crates/agent/src/context_strip.rs +++ b/crates/agent_ui/src/context_strip.rs @@ -1,7 +1,15 @@ -use std::path::Path; -use std::rc::Rc; - -use assistant_context_editor::AssistantContext; +use crate::{ + AcceptSuggestedContext, AgentPanel, FocusDown, FocusLeft, FocusRight, FocusUp, + ModelUsageContext, RemoveAllContext, RemoveFocusedContext, ToggleContextPicker, + context_picker::ContextPicker, + ui::{AddedContext, ContextPill}, +}; +use agent::context_store::SuggestedContext; +use agent::{ + context::AgentContextHandle, + context_store::ContextStore, + thread_store::{TextThreadStore, ThreadStore}, +}; use collections::HashSet; use editor::Editor; use file_icons::FileIcons; @@ -10,22 +18,11 @@ use gpui::{ Subscription, WeakEntity, }; use itertools::Itertools; -use language::Buffer; use project::ProjectItem; +use std::{path::Path, rc::Rc}; use ui::{PopoverMenu, PopoverMenuHandle, Tooltip, prelude::*}; use workspace::Workspace; -use crate::context::{AgentContextHandle, ContextKind}; -use crate::context_picker::ContextPicker; -use crate::context_store::ContextStore; -use crate::thread::Thread; -use crate::thread_store::{TextThreadStore, ThreadStore}; -use crate::ui::{AddedContext, ContextPill}; -use crate::{ - AcceptSuggestedContext, AgentPanel, FocusDown, FocusLeft, FocusRight, FocusUp, - ModelUsageContext, RemoveAllContext, RemoveFocusedContext, ToggleContextPicker, -}; - pub struct ContextStrip { context_store: Entity, context_picker: Entity, @@ -575,46 +572,3 @@ pub enum SuggestContextKind { File, Thread, } - -#[derive(Clone)] -pub enum SuggestedContext { - File { - name: SharedString, - icon_path: Option, - buffer: WeakEntity, - }, - Thread { - name: SharedString, - thread: WeakEntity, - }, - TextThread { - name: SharedString, - context: WeakEntity, - }, -} - -impl SuggestedContext { - pub fn name(&self) -> &SharedString { - match self { - Self::File { name, .. } => name, - Self::Thread { name, .. } => name, - Self::TextThread { name, .. } => name, - } - } - - pub fn icon_path(&self) -> Option { - match self { - Self::File { icon_path, .. } => icon_path.clone(), - Self::Thread { .. } => None, - Self::TextThread { .. } => None, - } - } - - pub fn kind(&self) -> ContextKind { - match self { - Self::File { .. } => ContextKind::File, - Self::Thread { .. } => ContextKind::Thread, - Self::TextThread { .. } => ContextKind::TextThread, - } - } -} diff --git a/crates/agent/src/debug.rs b/crates/agent_ui/src/debug.rs similarity index 100% rename from crates/agent/src/debug.rs rename to crates/agent_ui/src/debug.rs diff --git a/crates/agent/src/inline_assistant.rs b/crates/agent_ui/src/inline_assistant.rs similarity index 98% rename from crates/agent/src/inline_assistant.rs rename to crates/agent_ui/src/inline_assistant.rs index 6b7ebb061a..6e77e764a5 100644 --- a/crates/agent/src/inline_assistant.rs +++ b/crates/agent_ui/src/inline_assistant.rs @@ -4,18 +4,27 @@ use std::ops::Range; use std::rc::Rc; use std::sync::Arc; +use crate::{ + AgentPanel, + buffer_codegen::{BufferCodegen, CodegenAlternative, CodegenEvent}, + inline_prompt_editor::{CodegenStatus, InlineAssistId, PromptEditor, PromptEditorEvent}, + terminal_inline_assistant::TerminalInlineAssistant, +}; +use agent::{ + context_store::ContextStore, + thread_store::{TextThreadStore, ThreadStore}, +}; use agent_settings::AgentSettings; use anyhow::{Context as _, Result}; use client::telemetry::Telemetry; use collections::{HashMap, HashSet, VecDeque, hash_map}; -use editor::display_map::EditorMargins; use editor::{ Anchor, AnchorRangeExt, CodeActionProvider, Editor, EditorEvent, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset as _, ToPoint, actions::SelectAll, display_map::{ - BlockContext, BlockPlacement, BlockProperties, BlockStyle, CustomBlockId, RenderBlock, - ToDisplayPoint, + BlockContext, BlockPlacement, BlockProperties, BlockStyle, CustomBlockId, EditorMargins, + RenderBlock, ToDisplayPoint, }, }; use fs::Fs; @@ -24,16 +33,13 @@ use gpui::{ WeakEntity, Window, point, }; use language::{Buffer, Point, Selection, TransactionId}; -use language_model::ConfigurationError; -use language_model::ConfiguredModel; -use language_model::{LanguageModelRegistry, report_assistant_event}; +use language_model::{ + ConfigurationError, ConfiguredModel, LanguageModelRegistry, report_assistant_event, +}; use multi_buffer::MultiBufferRow; use parking_lot::Mutex; -use project::LspAction; -use project::Project; -use project::{CodeAction, ProjectTransaction}; -use prompt_store::PromptBuilder; -use prompt_store::PromptStore; +use project::{CodeAction, LspAction, Project, ProjectTransaction}; +use prompt_store::{PromptBuilder, PromptStore}; use settings::{Settings, SettingsStore}; use telemetry_events::{AssistantEventData, AssistantKind, AssistantPhase}; use terminal_view::{TerminalView, terminal_panel::TerminalPanel}; @@ -43,14 +49,6 @@ use util::{RangeExt, ResultExt, maybe}; use workspace::{ItemHandle, Toast, Workspace, dock::Panel, notifications::NotificationId}; use zed_actions::agent::OpenConfiguration; -use crate::AgentPanel; -use crate::buffer_codegen::{BufferCodegen, CodegenAlternative, CodegenEvent}; -use crate::context_store::ContextStore; -use crate::inline_prompt_editor::{CodegenStatus, InlineAssistId, PromptEditor, PromptEditorEvent}; -use crate::terminal_inline_assistant::TerminalInlineAssistant; -use crate::thread_store::TextThreadStore; -use crate::thread_store::ThreadStore; - pub fn init( fs: Arc, prompt_builder: Arc, diff --git a/crates/agent/src/inline_prompt_editor.rs b/crates/agent_ui/src/inline_prompt_editor.rs similarity index 99% rename from crates/agent/src/inline_prompt_editor.rs rename to crates/agent_ui/src/inline_prompt_editor.rs index 81912c82ef..5d486cdd8b 100644 --- a/crates/agent/src/inline_prompt_editor.rs +++ b/crates/agent_ui/src/inline_prompt_editor.rs @@ -1,14 +1,15 @@ use crate::agent_model_selector::AgentModelSelector; use crate::buffer_codegen::BufferCodegen; -use crate::context::ContextCreasesAddon; use crate::context_picker::{ContextPicker, ContextPickerCompletionProvider}; -use crate::context_store::ContextStore; use crate::context_strip::{ContextStrip, ContextStripEvent, SuggestContextKind}; -use crate::message_editor::{extract_message_creases, insert_message_creases}; +use crate::message_editor::{ContextCreasesAddon, extract_message_creases, insert_message_creases}; use crate::terminal_codegen::TerminalCodegen; -use crate::thread_store::{TextThreadStore, ThreadStore}; use crate::{CycleNextInlineAssist, CyclePreviousInlineAssist, ModelUsageContext}; use crate::{RemoveAllContext, ToggleContextPicker}; +use agent::{ + context_store::ContextStore, + thread_store::{TextThreadStore, ThreadStore}, +}; use assistant_context_editor::language_model_selector::ToggleModelSelector; use client::ErrorExt; use collections::VecDeque; diff --git a/crates/agent/src/message_editor.rs b/crates/agent_ui/src/message_editor.rs similarity index 96% rename from crates/agent/src/message_editor.rs rename to crates/agent_ui/src/message_editor.rs index cc20f18e4f..dabc98a5dd 100644 --- a/crates/agent/src/message_editor.rs +++ b/crates/agent_ui/src/message_editor.rs @@ -3,21 +3,25 @@ use std::rc::Rc; use std::sync::Arc; use crate::agent_model_selector::AgentModelSelector; -use crate::context::{AgentContextKey, ContextCreasesAddon, ContextLoadResult, load_context}; use crate::tool_compatibility::{IncompatibleToolsState, IncompatibleToolsTooltip}; use crate::ui::{ MaxModeTooltip, preview::{AgentPreview, UsageCallout}, }; +use agent::{ + context::{AgentContextKey, ContextLoadResult, load_context}, + context_store::ContextStoreEvent, +}; use agent_settings::{AgentSettings, CompletionMode}; use assistant_context_editor::language_model_selector::ToggleModelSelector; use buffer_diff::BufferDiff; use client::UserStore; use collections::{HashMap, HashSet}; use editor::actions::{MoveUp, Paste}; +use editor::display_map::CreaseId; use editor::{ - AnchorRangeExt, ContextMenuOptions, ContextMenuPlacement, Editor, EditorElement, EditorEvent, - EditorMode, EditorStyle, MultiBuffer, + Addon, AnchorRangeExt, ContextMenuOptions, ContextMenuPlacement, Editor, EditorElement, + EditorEvent, EditorMode, EditorStyle, MultiBuffer, }; use file_icons::FileIcons; use fs::Fs; @@ -46,16 +50,18 @@ use workspace::{CollaboratorId, Workspace}; use zed_llm_client::CompletionIntent; use crate::context_picker::{ContextPicker, ContextPickerCompletionProvider, crease_for_mention}; -use crate::context_store::ContextStore; use crate::context_strip::{ContextStrip, ContextStripEvent, SuggestContextKind}; use crate::profile_selector::ProfileSelector; -use crate::thread::{MessageCrease, Thread, TokenUsageRatio}; -use crate::thread_store::{TextThreadStore, ThreadStore}; use crate::{ ActiveThread, AgentDiffPane, Chat, ChatWithFollow, ExpandMessageEditor, Follow, KeepAll, ModelUsageContext, NewThread, OpenAgentDiff, RejectAll, RemoveAllContext, ToggleBurnMode, ToggleContextPicker, ToggleProfileSelector, register_agent_preview, }; +use agent::{ + MessageCrease, Thread, TokenUsageRatio, + context_store::ContextStore, + thread_store::{TextThreadStore, ThreadStore}, +}; #[derive(RegisterComponent)] pub struct MessageEditor { @@ -1466,6 +1472,69 @@ impl MessageEditor { } } +#[derive(Default)] +pub struct ContextCreasesAddon { + creases: HashMap>, + _subscription: Option, +} + +impl Addon for ContextCreasesAddon { + fn to_any(&self) -> &dyn std::any::Any { + self + } + + fn to_any_mut(&mut self) -> Option<&mut dyn std::any::Any> { + Some(self) + } +} + +impl ContextCreasesAddon { + pub fn new() -> Self { + Self { + creases: HashMap::default(), + _subscription: None, + } + } + + pub fn add_creases( + &mut self, + context_store: &Entity, + key: AgentContextKey, + creases: impl IntoIterator, + cx: &mut Context, + ) { + self.creases.entry(key).or_default().extend(creases); + self._subscription = Some(cx.subscribe( + &context_store, + |editor, _, event, cx| match event { + ContextStoreEvent::ContextRemoved(key) => { + let Some(this) = editor.addon_mut::() else { + return; + }; + let (crease_ids, replacement_texts): (Vec<_>, Vec<_>) = this + .creases + .remove(key) + .unwrap_or_default() + .into_iter() + .unzip(); + let ranges = editor + .remove_creases(crease_ids, cx) + .into_iter() + .map(|(_, range)| range) + .collect::>(); + editor.unfold_ranges(&ranges, false, false, cx); + editor.edit(ranges.into_iter().zip(replacement_texts), cx); + cx.notify(); + } + }, + )) + } + + pub fn into_inner(self) -> HashMap> { + self.creases + } +} + pub fn extract_message_creases( editor: &mut Editor, cx: &mut Context<'_, Editor>, @@ -1504,8 +1573,9 @@ pub fn extract_message_creases( let context = contexts_by_crease_id.remove(&id); MessageCrease { range, - metadata, context, + label: metadata.label, + icon_path: metadata.icon_path, } }) .collect() @@ -1577,8 +1647,8 @@ pub fn insert_message_creases( let start = buffer_snapshot.anchor_after(crease.range.start); let end = buffer_snapshot.anchor_before(crease.range.end); crease_for_mention( - crease.metadata.label.clone(), - crease.metadata.icon_path.clone(), + crease.label.clone(), + crease.icon_path.clone(), start..end, cx.weak_entity(), ) @@ -1590,12 +1660,7 @@ pub fn insert_message_creases( for (crease, id) in message_creases.iter().zip(ids) { if let Some(context) = crease.context.as_ref() { let key = AgentContextKey(context.clone()); - addon.add_creases( - context_store, - key, - vec![(id, crease.metadata.label.clone())], - cx, - ); + addon.add_creases(context_store, key, vec![(id, crease.label.clone())], cx); } } } diff --git a/crates/agent/src/profile_selector.rs b/crates/agent_ui/src/profile_selector.rs similarity index 99% rename from crates/agent/src/profile_selector.rs rename to crates/agent_ui/src/profile_selector.rs index 7a42e45fa4..ddcb44d46b 100644 --- a/crates/agent/src/profile_selector.rs +++ b/crates/agent_ui/src/profile_selector.rs @@ -1,20 +1,19 @@ -use std::sync::Arc; - +use crate::{ManageProfiles, ToggleProfileSelector}; +use agent::{ + Thread, + agent_profile::{AgentProfile, AvailableProfiles}, +}; use agent_settings::{AgentDockPosition, AgentProfileId, AgentSettings, builtin_profiles}; use fs::Fs; use gpui::{Action, Empty, Entity, FocusHandle, Subscription, prelude::*}; use language_model::LanguageModelRegistry; use settings::{Settings as _, SettingsStore, update_settings_file}; +use std::sync::Arc; use ui::{ ContextMenu, ContextMenuEntry, DocumentationSide, PopoverMenu, PopoverMenuHandle, Tooltip, prelude::*, }; -use crate::{ - ManageProfiles, Thread, ToggleProfileSelector, - agent_profile::{AgentProfile, AvailableProfiles}, -}; - pub struct ProfileSelector { profiles: AvailableProfiles, fs: Arc, diff --git a/crates/agent/src/slash_command_settings.rs b/crates/agent_ui/src/slash_command_settings.rs similarity index 100% rename from crates/agent/src/slash_command_settings.rs rename to crates/agent_ui/src/slash_command_settings.rs diff --git a/crates/agent/src/terminal_codegen.rs b/crates/agent_ui/src/terminal_codegen.rs similarity index 100% rename from crates/agent/src/terminal_codegen.rs rename to crates/agent_ui/src/terminal_codegen.rs diff --git a/crates/agent/src/terminal_inline_assistant.rs b/crates/agent_ui/src/terminal_inline_assistant.rs similarity index 99% rename from crates/agent/src/terminal_inline_assistant.rs rename to crates/agent_ui/src/terminal_inline_assistant.rs index 993cfd2fbd..162b45413f 100644 --- a/crates/agent/src/terminal_inline_assistant.rs +++ b/crates/agent_ui/src/terminal_inline_assistant.rs @@ -1,10 +1,12 @@ -use crate::context::load_context; -use crate::context_store::ContextStore; use crate::inline_prompt_editor::{ CodegenStatus, PromptEditor, PromptEditorEvent, TerminalInlineAssistId, }; use crate::terminal_codegen::{CLEAR_INPUT, CodegenEvent, TerminalCodegen}; -use crate::thread_store::{TextThreadStore, ThreadStore}; +use agent::{ + context::load_context, + context_store::ContextStore, + thread_store::{TextThreadStore, ThreadStore}, +}; use agent_settings::AgentSettings; use anyhow::{Context as _, Result}; use client::telemetry::Telemetry; diff --git a/crates/agent/src/thread_history.rs b/crates/agent_ui/src/thread_history.rs similarity index 99% rename from crates/agent/src/thread_history.rs rename to crates/agent_ui/src/thread_history.rs index dd77dd7cb1..a2ee816f73 100644 --- a/crates/agent/src/thread_history.rs +++ b/crates/agent_ui/src/thread_history.rs @@ -1,7 +1,5 @@ -use std::fmt::Display; -use std::ops::Range; -use std::sync::Arc; - +use crate::{AgentPanel, RemoveSelectedThread}; +use agent::history_store::{HistoryEntry, HistoryStore}; use chrono::{Datelike as _, Local, NaiveDate, TimeDelta}; use editor::{Editor, EditorEvent}; use fuzzy::{StringMatch, StringMatchCandidate}; @@ -9,6 +7,7 @@ use gpui::{ App, ClickEvent, Empty, Entity, FocusHandle, Focusable, ScrollStrategy, Stateful, Task, UniformListScrollHandle, WeakEntity, Window, uniform_list, }; +use std::{fmt::Display, ops::Range, sync::Arc}; use time::{OffsetDateTime, UtcOffset}; use ui::{ HighlightedLabel, IconButtonShape, ListItem, ListItemSpacing, Scrollbar, ScrollbarState, @@ -16,9 +15,6 @@ use ui::{ }; use util::ResultExt; -use crate::history_store::{HistoryEntry, HistoryStore}; -use crate::{AgentPanel, RemoveSelectedThread}; - pub struct ThreadHistory { agent_panel: WeakEntity, history_store: Entity, diff --git a/crates/agent/src/tool_compatibility.rs b/crates/agent_ui/src/tool_compatibility.rs similarity index 98% rename from crates/agent/src/tool_compatibility.rs rename to crates/agent_ui/src/tool_compatibility.rs index 6193b0929d..936612e556 100644 --- a/crates/agent/src/tool_compatibility.rs +++ b/crates/agent_ui/src/tool_compatibility.rs @@ -1,13 +1,11 @@ -use std::sync::Arc; - +use agent::{Thread, ThreadEvent}; use assistant_tool::{Tool, ToolSource}; use collections::HashMap; use gpui::{App, Context, Entity, IntoElement, Render, Subscription, Window}; use language_model::{LanguageModel, LanguageModelToolSchemaFormat}; +use std::sync::Arc; use ui::prelude::*; -use crate::{Thread, ThreadEvent}; - pub struct IncompatibleToolsState { cache: HashMap>>, thread: Entity, diff --git a/crates/agent/src/ui.rs b/crates/agent_ui/src/ui.rs similarity index 100% rename from crates/agent/src/ui.rs rename to crates/agent_ui/src/ui.rs diff --git a/crates/agent/src/ui/agent_notification.rs b/crates/agent_ui/src/ui/agent_notification.rs similarity index 100% rename from crates/agent/src/ui/agent_notification.rs rename to crates/agent_ui/src/ui/agent_notification.rs diff --git a/crates/agent/src/ui/animated_label.rs b/crates/agent_ui/src/ui/animated_label.rs similarity index 100% rename from crates/agent/src/ui/animated_label.rs rename to crates/agent_ui/src/ui/animated_label.rs diff --git a/crates/agent/src/ui/context_pill.rs b/crates/agent_ui/src/ui/context_pill.rs similarity index 99% rename from crates/agent/src/ui/context_pill.rs rename to crates/agent_ui/src/ui/context_pill.rs index 1abdd8fb8d..5dd57de244 100644 --- a/crates/agent/src/ui/context_pill.rs +++ b/crates/agent_ui/src/ui/context_pill.rs @@ -12,7 +12,7 @@ use prompt_store::PromptStore; use rope::Point; use ui::{IconButtonShape, Tooltip, prelude::*, tooltip_container}; -use crate::context::{ +use agent::context::{ AgentContext, AgentContextHandle, ContextId, ContextKind, DirectoryContext, DirectoryContextHandle, FetchedUrlContext, FileContext, FileContextHandle, ImageContext, ImageStatus, RulesContext, RulesContextHandle, SelectionContext, SelectionContextHandle, diff --git a/crates/agent/src/ui/max_mode_tooltip.rs b/crates/agent_ui/src/ui/max_mode_tooltip.rs similarity index 100% rename from crates/agent/src/ui/max_mode_tooltip.rs rename to crates/agent_ui/src/ui/max_mode_tooltip.rs diff --git a/crates/agent/src/ui/onboarding_modal.rs b/crates/agent_ui/src/ui/onboarding_modal.rs similarity index 100% rename from crates/agent/src/ui/onboarding_modal.rs rename to crates/agent_ui/src/ui/onboarding_modal.rs diff --git a/crates/agent/src/ui/preview.rs b/crates/agent_ui/src/ui/preview.rs similarity index 100% rename from crates/agent/src/ui/preview.rs rename to crates/agent_ui/src/ui/preview.rs diff --git a/crates/agent/src/ui/preview/agent_preview.rs b/crates/agent_ui/src/ui/preview/agent_preview.rs similarity index 100% rename from crates/agent/src/ui/preview/agent_preview.rs rename to crates/agent_ui/src/ui/preview/agent_preview.rs diff --git a/crates/agent/src/ui/preview/usage_callouts.rs b/crates/agent_ui/src/ui/preview/usage_callouts.rs similarity index 100% rename from crates/agent/src/ui/preview/usage_callouts.rs rename to crates/agent_ui/src/ui/preview/usage_callouts.rs diff --git a/crates/agent/src/ui/upsell.rs b/crates/agent_ui/src/ui/upsell.rs similarity index 100% rename from crates/agent/src/ui/upsell.rs rename to crates/agent_ui/src/ui/upsell.rs diff --git a/crates/eval/Cargo.toml b/crates/eval/Cargo.toml index 1e1e3d16e4..7ecba7c1ec 100644 --- a/crates/eval/Cargo.toml +++ b/crates/eval/Cargo.toml @@ -19,6 +19,7 @@ path = "src/explorer.rs" [dependencies] agent.workspace = true +agent_ui.workspace = true agent_settings.workspace = true anyhow.workspace = true assistant_tool.workspace = true diff --git a/crates/eval/src/eval.rs b/crates/eval/src/eval.rs index 93c3618409..e5132b0f33 100644 --- a/crates/eval/src/eval.rs +++ b/crates/eval/src/eval.rs @@ -423,7 +423,7 @@ pub fn init(cx: &mut App) -> Arc { terminal_view::init(cx); let stdout_is_a_pty = false; let prompt_builder = PromptBuilder::load(fs.clone(), stdout_is_a_pty, cx); - agent::init( + agent_ui::init( fs.clone(), client.clone(), prompt_builder.clone(), diff --git a/crates/eval/src/example.rs b/crates/eval/src/example.rs index 85af49e339..09770364cb 100644 --- a/crates/eval/src/example.rs +++ b/crates/eval/src/example.rs @@ -100,7 +100,7 @@ impl ExampleContext { pub fn new( meta: ExampleMetadata, log_prefix: String, - agent_thread: Entity, + agent_thread: Entity, model: Arc, app: AsyncApp, ) -> Self { diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index d8296c94f6..c6b65eeb0b 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -21,6 +21,7 @@ path = "src/main.rs" [dependencies] activity_indicator.workspace = true agent.workspace = true +agent_ui.workspace = true agent_settings.workspace = true anyhow.workspace = true askpass.workspace = true diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 90b8e3e0b7..1f95d938a5 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -531,7 +531,7 @@ pub fn main() { cx, ); let prompt_builder = PromptBuilder::load(app_state.fs.clone(), stdout_is_a_pty(), cx); - agent::init( + agent_ui::init( app_state.fs.clone(), app_state.client.clone(), prompt_builder.clone(), diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 44c88eb469..b9e6523f73 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -9,7 +9,7 @@ mod quick_action_bar; #[cfg(target_os = "windows")] pub(crate) mod windows_only_instance; -use agent::AgentDiffToolbar; +use agent_ui::AgentDiffToolbar; use anyhow::Context as _; pub use app_menus::*; use assets::Assets; @@ -515,7 +515,7 @@ fn initialize_panels( let is_assistant2_enabled = !cfg!(test); let agent_panel = if is_assistant2_enabled { let agent_panel = - agent::AgentPanel::load(workspace_handle.clone(), prompt_builder, cx.clone()) + agent_ui::AgentPanel::load(workspace_handle.clone(), prompt_builder, cx.clone()) .await?; Some(agent_panel) @@ -536,13 +536,13 @@ fn initialize_panels( // Once we ship `assistant2` we can push this back down into `agent::agent_panel::init`. if is_assistant2_enabled { ::set_global( - Arc::new(agent::ConcreteAssistantPanelDelegate), + Arc::new(agent_ui::ConcreteAssistantPanelDelegate), cx, ); workspace - .register_action(agent::AgentPanel::toggle_focus) - .register_action(agent::InlineAssistant::inline_assist); + .register_action(agent_ui::AgentPanel::toggle_focus) + .register_action(agent_ui::InlineAssistant::inline_assist); } })?; @@ -4320,7 +4320,7 @@ mod tests { web_search::init(cx); web_search_providers::init(app_state.client.clone(), cx); let prompt_builder = PromptBuilder::load(app_state.fs.clone(), false, cx); - agent::init( + agent_ui::init( app_state.fs.clone(), app_state.client.clone(), prompt_builder.clone(), diff --git a/crates/zed/src/zed/component_preview.rs b/crates/zed/src/zed/component_preview.rs index 1d9cd26854..c32248cbe0 100644 --- a/crates/zed/src/zed/component_preview.rs +++ b/crates/zed/src/zed/component_preview.rs @@ -5,20 +5,14 @@ mod persistence; mod preview_support; -use std::ops::Range; -use std::sync::Arc; - -use std::iter::Iterator; - -use agent::{ActiveThread, TextThreadStore, ThreadStore}; +use agent::{TextThreadStore, ThreadStore}; +use agent_ui::ActiveThread; use client::UserStore; +use collections::HashMap; use component::{ComponentId, ComponentMetadata, ComponentStatus, components}; use gpui::{ App, Entity, EventEmitter, FocusHandle, Focusable, Task, WeakEntity, Window, list, prelude::*, }; - -use collections::HashMap; - use gpui::{ListState, ScrollHandle, ScrollStrategy, UniformListScrollHandle}; use languages::LanguageRegistry; use notifications::status_toast::{StatusToast, ToastIcon}; @@ -27,11 +21,14 @@ use preview_support::active_thread::{ load_preview_text_thread_store, load_preview_thread_store, static_active_thread, }; use project::Project; +use std::{iter::Iterator, ops::Range, sync::Arc}; use ui::{ButtonLike, Divider, HighlightedLabel, ListItem, ListSubHeader, Tooltip, prelude::*}; use ui_input::SingleLineInput; use util::ResultExt as _; -use workspace::{AppState, ItemId, SerializableItem, delete_unloaded_items}; -use workspace::{Item, Workspace, WorkspaceId, item::ItemEvent}; +use workspace::{ + AppState, Item, ItemId, SerializableItem, Workspace, WorkspaceId, delete_unloaded_items, + item::ItemEvent, +}; pub fn init(app_state: Arc, cx: &mut App) { workspace::register_serializable_item::(cx); @@ -642,7 +639,7 @@ impl ComponentPreview { // Check if the component's scope is Agent if scope == ComponentScope::Agent { if let Some(active_thread) = self.active_thread.clone() { - if let Some(element) = agent::get_agent_preview( + if let Some(element) = agent_ui::get_agent_preview( &component.id(), self.workspace.clone(), active_thread, @@ -1140,7 +1137,7 @@ impl ComponentPreviewPage { fn render_preview(&self, window: &mut Window, cx: &mut App) -> impl IntoElement { // Try to get agent preview first if we have an active thread let maybe_agent_preview = if let Some(active_thread) = self.active_thread.as_ref() { - agent::get_agent_preview( + agent_ui::get_agent_preview( &self.component.id(), self.workspace.clone(), active_thread.clone(), diff --git a/crates/zed/src/zed/component_preview/preview_support/active_thread.rs b/crates/zed/src/zed/component_preview/preview_support/active_thread.rs index 97b75cdf32..825744572d 100644 --- a/crates/zed/src/zed/component_preview/preview_support/active_thread.rs +++ b/crates/zed/src/zed/component_preview/preview_support/active_thread.rs @@ -1,4 +1,5 @@ -use agent::{ActiveThread, ContextStore, MessageSegment, TextThreadStore, ThreadStore}; +use agent::{ContextStore, MessageSegment, TextThreadStore, ThreadStore}; +use agent_ui::ActiveThread; use anyhow::{Result, anyhow}; use assistant_tool::ToolWorkingSet; use gpui::{AppContext, AsyncApp, Entity, Task, WeakEntity};