diff --git a/crates/agent2/src/db.rs b/crates/agent2/src/db.rs index d40352f257..a7240df5c7 100644 --- a/crates/agent2/src/db.rs +++ b/crates/agent2/src/db.rs @@ -6,7 +6,7 @@ use anyhow::{Result, anyhow}; use chrono::{DateTime, Utc}; use collections::{HashMap, IndexMap}; use futures::{FutureExt, future::Shared}; -use gpui::{BackgroundExecutor, Global, ReadGlobal, Task}; +use gpui::{BackgroundExecutor, Global, Task}; use indoc::indoc; use parking_lot::Mutex; use serde::{Deserialize, Serialize}; @@ -15,7 +15,7 @@ use sqlez::{ connection::Connection, statement::Statement, }; -use std::{path::PathBuf, sync::Arc}; +use std::sync::Arc; use ui::{App, SharedString}; pub type DbMessage = crate::Message; @@ -221,6 +221,10 @@ pub(crate) struct ThreadsDatabase { connection: Arc>, } +struct GlobalThreadsDatabase(Shared, Arc>>>); + +impl Global for GlobalThreadsDatabase {} + impl ThreadsDatabase { fn connection(&self) -> Arc> { self.connection.clone() @@ -231,8 +235,11 @@ impl ThreadsDatabase { impl ThreadsDatabase { pub fn connect(cx: &mut App) -> Shared, Arc>>> { + if cx.has_global::() { + return cx.global::().0.clone(); + } let executor = cx.background_executor().clone(); - executor + let task = executor .spawn({ let executor = executor.clone(); async move { @@ -242,7 +249,10 @@ impl ThreadsDatabase { } } }) - .shared() + .shared(); + + cx.set_global(GlobalThreadsDatabase(task.clone())); + task } pub fn new(executor: BackgroundExecutor) -> Result { diff --git a/crates/agent_ui/src/acp.rs b/crates/agent_ui/src/acp.rs index c3fe90760e..efdeee9efd 100644 --- a/crates/agent_ui/src/acp.rs +++ b/crates/agent_ui/src/acp.rs @@ -8,5 +8,5 @@ mod thread_view; pub use model_selector::AcpModelSelector; pub use model_selector_popover::AcpModelSelectorPopover; -pub use thread_history::AcpThreadHistory; +pub use thread_history::{AcpThreadHistory, ThreadHistoryEvent}; pub use thread_view::AcpThreadView; diff --git a/crates/agent_ui/src/acp/thread_history.rs b/crates/agent_ui/src/acp/thread_history.rs index 4bf409a2af..b2fd1bfcb2 100644 --- a/crates/agent_ui/src/acp/thread_history.rs +++ b/crates/agent_ui/src/acp/thread_history.rs @@ -8,7 +8,7 @@ use chrono::{Datelike as _, Local, NaiveDate, TimeDelta}; use editor::{Editor, EditorEvent}; use fuzzy::{StringMatch, StringMatchCandidate}; use gpui::{ - App, Empty, Entity, FocusHandle, Focusable, ScrollStrategy, Stateful, Task, + App, Empty, Entity, EventEmitter, FocusHandle, Focusable, ScrollStrategy, Stateful, Task, UniformListScrollHandle, WeakEntity, Window, uniform_list, }; use project::Project; @@ -61,9 +61,14 @@ enum ListItemType { }, } +pub enum ThreadHistoryEvent { + Open(HistoryEntry), +} + +impl EventEmitter for AcpThreadHistory {} + impl AcpThreadHistory { pub(crate) fn new( - agent_panel: WeakEntity, project: &Entity, window: &mut Window, cx: &mut Context, @@ -117,7 +122,6 @@ impl AcpThreadHistory { let scrollbar_state = ScrollbarState::new(scroll_handle.clone()); let mut this = Self { - agent_panel, history_store, scroll_handle, selected_index: 0, @@ -429,28 +433,29 @@ impl AcpThreadHistory { ) } - fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context) { - self.confirm_entry(self.selected_index, window, cx); + fn confirm(&mut self, _: &menu::Confirm, _window: &mut Window, cx: &mut Context) { + self.confirm_entry(self.selected_index, cx); } - fn confirm_entry(&mut self, ix: usize, window: &mut Window, cx: &mut Context) { + fn confirm_entry(&mut self, ix: usize, cx: &mut Context) { let Some(entry) = self.get_match(ix) else { return; }; - let task_result = match entry { - HistoryEntry::Thread(thread) => { - self.agent_panel.update(cx, move |agent_panel, cx| todo!()) - } - HistoryEntry::Context(context) => { - self.agent_panel.update(cx, move |agent_panel, cx| { - agent_panel.open_saved_prompt_editor(context.path.clone(), window, cx) - }) - } - }; + cx.emit(ThreadHistoryEvent::Open(entry.clone())); + // let task_result = match entry { + // HistoryEntry::Thread(thread) => { + // self.agent_panel.update(cx, move |agent_panel, cx| todo!()) + // } + // HistoryEntry::Context(context) => { + // self.agent_panel.update(cx, move |agent_panel, cx| { + // agent_panel.open_saved_prompt_editor(context.path.clone(), window, cx) + // }) + // } + // }; - if let Some(task) = task_result.log_err() { - task.detach_and_log_err(cx); - }; + // if let Some(task) = task_result.log_err() { + // task.detach_and_log_err(cx); + // }; cx.notify(); } @@ -606,9 +611,9 @@ impl AcpThreadHistory { } else { None }) - .on_click(cx.listener(move |this, _, window, cx| { - this.confirm_entry(list_entry_ix, window, cx) - })), + .on_click( + cx.listener(move |this, _, _, cx| this.confirm_entry(list_entry_ix, cx)), + ), ) .into_any_element() } diff --git a/crates/agent_ui/src/acp/thread_view.rs b/crates/agent_ui/src/acp/thread_view.rs index af97f944e9..13a6fd1a21 100644 --- a/crates/agent_ui/src/acp/thread_view.rs +++ b/crates/agent_ui/src/acp/thread_view.rs @@ -1,6 +1,7 @@ use acp_thread::{ - AcpThread, AcpThreadEvent, AgentThreadEntry, AssistantMessage, AssistantMessageChunk, - LoadError, MentionUri, ThreadStatus, ToolCall, ToolCallContent, ToolCallStatus, UserMessageId, + AcpThread, AcpThreadEvent, AcpThreadMetadata, AgentThreadEntry, AssistantMessage, + AssistantMessageChunk, LoadError, MentionUri, ThreadStatus, ToolCall, ToolCallContent, + ToolCallStatus, UserMessageId, }; use acp_thread::{AgentConnection, Plan}; use action_log::ActionLog; @@ -136,6 +137,7 @@ impl AcpThreadView { project: Entity, thread_store: Entity, text_thread_store: Entity, + restore_thread: Option, window: &mut Window, cx: &mut Context, ) -> Self { diff --git a/crates/agent_ui/src/agent_panel.rs b/crates/agent_ui/src/agent_panel.rs index f54ea9b31a..7030df1596 100644 --- a/crates/agent_ui/src/agent_panel.rs +++ b/crates/agent_ui/src/agent_panel.rs @@ -4,13 +4,13 @@ use std::rc::Rc; use std::sync::Arc; use std::time::Duration; +use acp_thread::AcpThreadMetadata; use agent_servers::AgentServer; -use agent2::NativeAgentServer; +use agent2::history_store::HistoryEntry; use db::kvp::{Dismissable, KEY_VALUE_STORE}; use serde::{Deserialize, Serialize}; -use crate::NewExternalAgentThread; -use crate::acp::AcpThreadHistory; +use crate::acp::{AcpThreadHistory, ThreadHistoryEvent}; use crate::agent_diff::AgentDiffThread; use crate::{ AddContextServer, AgentDiffPane, ContinueThread, ContinueWithBurnMode, @@ -31,6 +31,7 @@ use crate::{ thread_history::{HistoryEntryElement, ThreadHistory}, ui::{AgentOnboardingModal, EndTrialUpsell}, }; +use crate::{ExternalAgent, NewExternalAgentThread}; use agent::{ Thread, ThreadError, ThreadEvent, ThreadId, ThreadSummary, TokenUsageRatio, context_store::ContextStore, @@ -121,7 +122,7 @@ pub fn init(cx: &mut App) { if let Some(panel) = workspace.panel::(cx) { workspace.focus_panel::(window, cx); panel.update(cx, |panel, cx| { - panel.new_external_thread(action.agent, window, cx) + panel.new_external_thread(action.agent, None, window, cx) }); } }) @@ -746,8 +747,26 @@ impl AgentPanel { ) }); - let acp_history = - cx.new(|cx| AcpThreadHistory::new(weak_self.clone(), &project, window, cx)); + let acp_history = cx.new(|cx| AcpThreadHistory::new(&project, window, cx)); + cx.subscribe_in( + &acp_history, + window, + |this, _, event, window, cx| match event { + ThreadHistoryEvent::Open(HistoryEntry::Thread(thread)) => { + let agent_choice = match thread.agent.0.as_ref() { + "Claude Code" => Some(ExternalAgent::ClaudeCode), + "Gemini" => Some(ExternalAgent::Gemini), + "Native Agent" => Some(ExternalAgent::NativeAgent), + _ => None, + }; + this.new_external_thread(agent_choice, Some(thread.clone()), window, cx); + } + ThreadHistoryEvent::Open(HistoryEntry::Context(thread)) => { + todo!() + } + }, + ) + .detach(); Self { active_view, @@ -962,6 +981,7 @@ impl AgentPanel { fn new_external_thread( &mut self, agent_choice: Option, + restore_thread: Option, window: &mut Window, cx: &mut Context, ) { @@ -1019,6 +1039,7 @@ impl AgentPanel { project, thread_store.clone(), text_thread_store.clone(), + restore_thread, window, cx, )