agent: Remove unused `HistoryStore` (#40187)

Bennet Fenner created

Release Notes:

- N/A

Change summary

Cargo.lock                         |   1 
crates/agent/Cargo.toml            |   1 
crates/agent/src/agent.rs          |   1 
crates/agent/src/history_store.rs  | 253 --------------------------------
crates/agent_ui/src/agent_panel.rs |  53 +-----
5 files changed, 11 insertions(+), 298 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -161,7 +161,6 @@ dependencies = [
  "http_client",
  "icons",
  "indoc",
- "itertools 0.14.0",
  "language",
  "language_model",
  "log",

crates/agent/Cargo.toml 🔗

@@ -39,7 +39,6 @@ heed.workspace = true
 http_client.workspace = true
 icons.workspace = true
 indoc.workspace = true
-itertools.workspace = true
 language.workspace = true
 language_model.workspace = true
 log.workspace = true

crates/agent/src/agent.rs 🔗

@@ -2,7 +2,6 @@ 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;

crates/agent/src/history_store.rs 🔗

@@ -1,253 +0,0 @@
-use crate::{ThreadId, thread_store::SerializedThreadMetadata};
-use anyhow::{Context as _, Result};
-use assistant_context::SavedContextMetadata;
-use chrono::{DateTime, Utc};
-use gpui::{App, AsyncApp, Entity, SharedString, Task, prelude::*};
-use itertools::Itertools;
-use paths::contexts_dir;
-use serde::{Deserialize, Serialize};
-use std::{collections::VecDeque, path::Path, sync::Arc, time::Duration};
-use util::ResultExt as _;
-
-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);
-
-#[derive(Clone, Debug)]
-pub enum HistoryEntry {
-    Thread(SerializedThreadMetadata),
-    Context(SavedContextMetadata),
-}
-
-impl HistoryEntry {
-    pub fn updated_at(&self) -> DateTime<Utc> {
-        match self {
-            HistoryEntry::Thread(thread) => thread.updated_at,
-            HistoryEntry::Context(context) => context.mtime.to_utc(),
-        }
-    }
-
-    pub fn id(&self) -> HistoryEntryId {
-        match self {
-            HistoryEntry::Thread(thread) => HistoryEntryId::Thread(thread.id.clone()),
-            HistoryEntry::Context(context) => HistoryEntryId::Context(context.path.clone()),
-        }
-    }
-
-    pub fn title(&self) -> &SharedString {
-        match self {
-            HistoryEntry::Thread(thread) => &thread.summary,
-            HistoryEntry::Context(context) => &context.title,
-        }
-    }
-}
-
-/// Generic identifier for a history entry.
-#[derive(Clone, PartialEq, Eq, Debug)]
-pub enum HistoryEntryId {
-    Thread(ThreadId),
-    Context(Arc<Path>),
-}
-
-#[derive(Serialize, Deserialize)]
-enum SerializedRecentOpen {
-    Thread(String),
-    ContextName(String),
-    /// Old format which stores the full path
-    Context(String),
-}
-
-pub struct HistoryStore {
-    context_store: Entity<assistant_context::ContextStore>,
-    recently_opened_entries: VecDeque<HistoryEntryId>,
-    _subscriptions: Vec<gpui::Subscription>,
-    _save_recently_opened_entries_task: Task<()>,
-}
-
-impl HistoryStore {
-    pub fn new(
-        context_store: Entity<assistant_context::ContextStore>,
-        initial_recent_entries: impl IntoIterator<Item = HistoryEntryId>,
-        cx: &mut Context<Self>,
-    ) -> Self {
-        let subscriptions = vec![cx.observe(&context_store, |_, _, cx| cx.notify())];
-
-        cx.spawn(async move |this, cx| {
-            let entries = Self::load_recently_opened_entries(cx).await.log_err()?;
-            this.update(cx, |this, _| {
-                this.recently_opened_entries
-                    .extend(
-                        entries.into_iter().take(
-                            MAX_RECENTLY_OPENED_ENTRIES
-                                .saturating_sub(this.recently_opened_entries.len()),
-                        ),
-                    );
-            })
-            .ok()
-        })
-        .detach();
-
-        Self {
-            context_store,
-            recently_opened_entries: initial_recent_entries.into_iter().collect(),
-            _subscriptions: subscriptions,
-            _save_recently_opened_entries_task: Task::ready(()),
-        }
-    }
-
-    pub fn entries(&self, cx: &mut Context<Self>) -> Vec<HistoryEntry> {
-        let mut history_entries = Vec::new();
-
-        #[cfg(debug_assertions)]
-        if std::env::var("ZED_SIMULATE_NO_THREAD_HISTORY").is_ok() {
-            return history_entries;
-        }
-
-        history_entries.extend(
-            self.context_store
-                .read(cx)
-                .unordered_contexts()
-                .cloned()
-                .map(HistoryEntry::Context),
-        );
-
-        history_entries.sort_unstable_by_key(|entry| std::cmp::Reverse(entry.updated_at()));
-        history_entries
-    }
-
-    pub fn recent_entries(&self, limit: usize, cx: &mut Context<Self>) -> Vec<HistoryEntry> {
-        self.entries(cx).into_iter().take(limit).collect()
-    }
-
-    pub fn recently_opened_entries(&self, cx: &App) -> Vec<HistoryEntry> {
-        #[cfg(debug_assertions)]
-        if std::env::var("ZED_SIMULATE_NO_THREAD_HISTORY").is_ok() {
-            return Vec::new();
-        }
-
-        let context_entries =
-            self.context_store
-                .read(cx)
-                .unordered_contexts()
-                .flat_map(|context| {
-                    self.recently_opened_entries
-                        .iter()
-                        .enumerate()
-                        .flat_map(|(index, entry)| match entry {
-                            HistoryEntryId::Context(path) if &context.path == path => {
-                                Some((index, HistoryEntry::Context(context.clone())))
-                            }
-                            _ => None,
-                        })
-                });
-
-        context_entries
-            // optimization to halt iteration early
-            .take(self.recently_opened_entries.len())
-            .sorted_unstable_by_key(|(index, _)| *index)
-            .map(|(_, entry)| entry)
-            .collect()
-    }
-
-    fn save_recently_opened_entries(&mut self, cx: &mut Context<Self>) {
-        let serialized_entries = self
-            .recently_opened_entries
-            .iter()
-            .filter_map(|entry| match entry {
-                HistoryEntryId::Context(path) => path.file_name().map(|file| {
-                    SerializedRecentOpen::ContextName(file.to_string_lossy().into_owned())
-                }),
-                HistoryEntryId::Thread(id) => Some(SerializedRecentOpen::Thread(id.to_string())),
-            })
-            .collect::<Vec<_>>();
-
-        self._save_recently_opened_entries_task = cx.spawn(async move |_, cx| {
-            cx.background_executor()
-                .timer(SAVE_RECENTLY_OPENED_ENTRIES_DEBOUNCE)
-                .await;
-            cx.background_spawn(async move {
-                let path = paths::data_dir().join(NAVIGATION_HISTORY_PATH);
-                let content = serde_json::to_string(&serialized_entries)?;
-                std::fs::write(path, content)?;
-                anyhow::Ok(())
-            })
-            .await
-            .log_err();
-        });
-    }
-
-    fn load_recently_opened_entries(cx: &AsyncApp) -> Task<Result<Vec<HistoryEntryId>>> {
-        cx.background_spawn(async move {
-            let path = paths::data_dir().join(NAVIGATION_HISTORY_PATH);
-            let contents = match smol::fs::read_to_string(path).await {
-                Ok(it) => it,
-                Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
-                    return Ok(Vec::new());
-                }
-                Err(e) => {
-                    return Err(e)
-                        .context("deserializing persisted agent panel navigation history");
-                }
-            };
-            let entries = serde_json::from_str::<Vec<SerializedRecentOpen>>(&contents)
-                .context("deserializing persisted agent panel navigation history")?
-                .into_iter()
-                .take(MAX_RECENTLY_OPENED_ENTRIES)
-                .flat_map(|entry| match entry {
-                    SerializedRecentOpen::Thread(id) => {
-                        Some(HistoryEntryId::Thread(id.as_str().into()))
-                    }
-                    SerializedRecentOpen::ContextName(file_name) => Some(HistoryEntryId::Context(
-                        contexts_dir().join(file_name).into(),
-                    )),
-                    SerializedRecentOpen::Context(path) => {
-                        Path::new(&path).file_name().map(|file_name| {
-                            HistoryEntryId::Context(contexts_dir().join(file_name).into())
-                        })
-                    }
-                })
-                .collect::<Vec<_>>();
-            Ok(entries)
-        })
-    }
-
-    pub fn push_recently_opened_entry(&mut self, entry: HistoryEntryId, cx: &mut Context<Self>) {
-        self.recently_opened_entries
-            .retain(|old_entry| old_entry != &entry);
-        self.recently_opened_entries.push_front(entry);
-        self.recently_opened_entries
-            .truncate(MAX_RECENTLY_OPENED_ENTRIES);
-        self.save_recently_opened_entries(cx);
-    }
-
-    pub fn remove_recently_opened_thread(&mut self, id: ThreadId, cx: &mut Context<Self>) {
-        self.recently_opened_entries.retain(
-            |entry| !matches!(entry, HistoryEntryId::Thread(thread_id) if thread_id == &id),
-        );
-        self.save_recently_opened_entries(cx);
-    }
-
-    pub fn replace_recently_opened_text_thread(
-        &mut self,
-        old_path: &Path,
-        new_path: &Arc<Path>,
-        cx: &mut Context<Self>,
-    ) {
-        for entry in &mut self.recently_opened_entries {
-            match entry {
-                HistoryEntryId::Context(path) if path.as_ref() == old_path => {
-                    *entry = HistoryEntryId::Context(new_path.clone());
-                    break;
-                }
-                _ => {}
-            }
-        }
-        self.save_recently_opened_entries(cx);
-    }
-
-    pub fn remove_recently_opened_entry(&mut self, entry: &HistoryEntryId, cx: &mut Context<Self>) {
-        self.recently_opened_entries
-            .retain(|old_entry| old_entry != entry);
-        self.save_recently_opened_entries(cx);
-    }
-}

crates/agent_ui/src/agent_panel.rs 🔗

@@ -34,7 +34,6 @@ use crate::{
 };
 use agent::{
     context_store::ContextStore,
-    history_store::{HistoryEntryId, HistoryStore},
     thread_store::{TextThreadStore, ThreadStore},
 };
 use agent_settings::AgentSettings;
@@ -309,7 +308,6 @@ impl ActiveView {
 
     pub fn prompt_editor(
         context_editor: Entity<TextThreadEditor>,
-        history_store: Entity<HistoryStore>,
         acp_history_store: Entity<agent2::HistoryStore>,
         language_registry: Arc<LanguageRegistry>,
         window: &mut Window,
@@ -377,18 +375,6 @@ impl ActiveView {
                         })
                     }
                     ContextEvent::PathChanged { old_path, new_path } => {
-                        history_store.update(cx, |history_store, cx| {
-                            if let Some(old_path) = old_path {
-                                history_store
-                                    .replace_recently_opened_text_thread(old_path, new_path, cx);
-                            } else {
-                                history_store.push_recently_opened_entry(
-                                    HistoryEntryId::Context(new_path.clone()),
-                                    cx,
-                                );
-                            }
-                        });
-
                         acp_history_store.update(cx, |history_store, cx| {
                             if let Some(old_path) = old_path {
                                 history_store
@@ -430,7 +416,7 @@ pub struct AgentPanel {
     language_registry: Arc<LanguageRegistry>,
     thread_store: Entity<ThreadStore>,
     acp_history: Entity<AcpThreadHistory>,
-    acp_history_store: Entity<agent2::HistoryStore>,
+    history_store: Entity<agent2::HistoryStore>,
     context_store: Entity<TextThreadStore>,
     prompt_store: Option<Entity<PromptStore>>,
     inline_assist_context_store: Entity<ContextStore>,
@@ -438,7 +424,6 @@ pub struct AgentPanel {
     configuration_subscription: Option<Subscription>,
     active_view: ActiveView,
     previous_view: Option<ActiveView>,
-    history_store: Entity<HistoryStore>,
     new_thread_menu_handle: PopoverMenuHandle<ContextMenu>,
     agent_panel_menu_handle: PopoverMenuHandle<ContextMenu>,
     assistant_navigation_menu_handle: PopoverMenuHandle<ContextMenu>,
@@ -571,10 +556,8 @@ impl AgentPanel {
         let inline_assist_context_store =
             cx.new(|_cx| ContextStore::new(project.downgrade(), Some(thread_store.downgrade())));
 
-        let history_store = cx.new(|cx| HistoryStore::new(context_store.clone(), [], cx));
-
-        let acp_history_store = cx.new(|cx| agent2::HistoryStore::new(context_store.clone(), cx));
-        let acp_history = cx.new(|cx| AcpThreadHistory::new(acp_history_store.clone(), window, cx));
+        let history_store = cx.new(|cx| agent2::HistoryStore::new(context_store.clone(), cx));
+        let acp_history = cx.new(|cx| AcpThreadHistory::new(history_store.clone(), window, cx));
         cx.subscribe_in(
             &acp_history,
             window,
@@ -596,14 +579,12 @@ impl AgentPanel {
         )
         .detach();
 
-        cx.observe(&history_store, |_, _, cx| cx.notify()).detach();
-
         let panel_type = AgentSettings::get_global(cx).default_view;
         let active_view = match panel_type {
             DefaultView::Thread => ActiveView::native_agent(
                 fs.clone(),
                 prompt_store.clone(),
-                acp_history_store.clone(),
+                history_store.clone(),
                 project.clone(),
                 workspace.clone(),
                 window,
@@ -629,7 +610,6 @@ impl AgentPanel {
                 ActiveView::prompt_editor(
                     context_editor,
                     history_store.clone(),
-                    acp_history_store.clone(),
                     language_registry.clone(),
                     window,
                     cx,
@@ -695,7 +675,6 @@ impl AgentPanel {
             configuration_subscription: None,
             inline_assist_context_store,
             previous_view: None,
-            history_store: history_store.clone(),
             new_thread_menu_handle: PopoverMenuHandle::default(),
             agent_panel_menu_handle: PopoverMenuHandle::default(),
             assistant_navigation_menu_handle: PopoverMenuHandle::default(),
@@ -706,7 +685,7 @@ impl AgentPanel {
             pending_serialization: None,
             onboarding,
             acp_history,
-            acp_history_store,
+            history_store,
             selected_agent: AgentType::default(),
             loading: false,
         }
@@ -760,7 +739,7 @@ impl AgentPanel {
         cx: &mut Context<Self>,
     ) {
         let Some(thread) = self
-            .acp_history_store
+            .history_store
             .read(cx)
             .thread_from_session_id(&action.from_session_id)
         else {
@@ -809,7 +788,6 @@ impl AgentPanel {
             ActiveView::prompt_editor(
                 context_editor.clone(),
                 self.history_store.clone(),
-                self.acp_history_store.clone(),
                 self.language_registry.clone(),
                 window,
                 cx,
@@ -841,7 +819,7 @@ impl AgentPanel {
         }
 
         let loading = self.loading;
-        let history = self.acp_history_store.clone();
+        let history = self.history_store.clone();
 
         cx.spawn_in(window, async move |this, cx| {
             let ext_agent = match agent_choice {
@@ -902,7 +880,7 @@ impl AgentPanel {
                         summarize_thread,
                         workspace.clone(),
                         project,
-                        this.acp_history_store.clone(),
+                        this.history_store.clone(),
                         this.prompt_store.clone(),
                         window,
                         cx,
@@ -1000,7 +978,6 @@ impl AgentPanel {
             ActiveView::prompt_editor(
                 editor,
                 self.history_store.clone(),
-                self.acp_history_store.clone(),
                 self.language_registry.clone(),
                 window,
                 cx,
@@ -1264,11 +1241,6 @@ impl AgentPanel {
         match &new_view {
             ActiveView::TextThread { context_editor, .. } => {
                 self.history_store.update(cx, |store, cx| {
-                    if let Some(path) = context_editor.read(cx).context().read(cx).path() {
-                        store.push_recently_opened_entry(HistoryEntryId::Context(path.clone()), cx)
-                    }
-                });
-                self.acp_history_store.update(cx, |store, cx| {
                     if let Some(path) = context_editor.read(cx).context().read(cx).path() {
                         store.push_recently_opened_entry(
                             agent2::HistoryEntryId::TextThread(path.clone()),
@@ -1302,7 +1274,7 @@ impl AgentPanel {
     ) -> ContextMenu {
         let entries = panel
             .read(cx)
-            .acp_history_store
+            .history_store
             .read(cx)
             .recently_opened_entries(cx);
 
@@ -1347,7 +1319,7 @@ impl AgentPanel {
                     move |_window, cx| {
                         panel
                             .update(cx, |this, cx| {
-                                this.acp_history_store.update(cx, |history_store, cx| {
+                                this.history_store.update(cx, |history_store, cx| {
                                     history_store.remove_recently_opened_entry(&id, cx);
                                 });
                             })
@@ -2178,10 +2150,7 @@ impl AgentPanel {
                 false
             }
             _ => {
-                let history_is_empty = self.acp_history_store.read(cx).is_empty(cx)
-                    && self
-                        .history_store
-                        .update(cx, |store, cx| store.recent_entries(1, cx).is_empty());
+                let history_is_empty = self.history_store.read(cx).is_empty(cx);
 
                 let has_configured_non_zed_providers = LanguageModelRegistry::read_global(cx)
                     .providers()