agent_ui: No global thread history (#51362)
Before you mark this PR as ready for review, make sure that you have: - [x] Added a solid test coverage and/or screenshots from doing manual testing - [x] Done a self-review taking into account security and performance aspects - [x] Aligned any UI changes with the [UI checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) Release Notes: - N/A
This commit is contained in:
1 parent
81da953acf
commit
5ebdbe2aac
7 files changed
+332
-194
No files matched your search
@@ -9,42 +9,51 @@ use gpui::{AppContext, Context, Entity, EventEmitter, SharedString, Subscription
|
||||
use project::{AgentServerStore, AgentServersUpdated, Project};
|
||||
use watch::Receiver;
|
||||
|
||||
use crate::ExternalAgent;
|
||||
use crate::{ExternalAgent, ThreadHistory};
|
||||
use project::ExternalAgentServerName;
|
||||
|
||||
pub enum ConnectionEntry {
|
||||
pub enum AgentConnectionEntry {
|
||||
Connecting {
|
||||
connect_task: Shared<Task<Result<Rc<dyn AgentConnection>, LoadError>>>,
|
||||
},
|
||||
Connected {
|
||||
connection: Rc<dyn AgentConnection>,
|
||||
connect_task: Shared<Task<Result<AgentConnectedState, LoadError>>>,
|
||||
},
|
||||
Connected(AgentConnectedState),
|
||||
Error {
|
||||
error: LoadError,
|
||||
},
|
||||
}
|
||||
|
||||
impl ConnectionEntry {
|
||||
pub fn wait_for_connection(&self) -> Shared<Task<Result<Rc<dyn AgentConnection>, LoadError>>> {
|
||||
#[derive(Clone)]
|
||||
pub struct AgentConnectedState {
|
||||
pub connection: Rc<dyn AgentConnection>,
|
||||
pub history: Entity<ThreadHistory>,
|
||||
}
|
||||
|
||||
impl AgentConnectionEntry {
|
||||
pub fn wait_for_connection(&self) -> Shared<Task<Result<AgentConnectedState, LoadError>>> {
|
||||
match self {
|
||||
ConnectionEntry::Connecting { connect_task } => connect_task.clone(),
|
||||
ConnectionEntry::Connected { connection } => {
|
||||
Task::ready(Ok(connection.clone())).shared()
|
||||
}
|
||||
ConnectionEntry::Error { error } => Task::ready(Err(error.clone())).shared(),
|
||||
AgentConnectionEntry::Connecting { connect_task } => connect_task.clone(),
|
||||
AgentConnectionEntry::Connected(state) => Task::ready(Ok(state.clone())).shared(),
|
||||
AgentConnectionEntry::Error { error } => Task::ready(Err(error.clone())).shared(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn history(&self) -> Option<&Entity<ThreadHistory>> {
|
||||
match self {
|
||||
AgentConnectionEntry::Connected(state) => Some(&state.history),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ConnectionEntryEvent {
|
||||
pub enum AgentConnectionEntryEvent {
|
||||
NewVersionAvailable(SharedString),
|
||||
}
|
||||
|
||||
impl EventEmitter<ConnectionEntryEvent> for ConnectionEntry {}
|
||||
impl EventEmitter<AgentConnectionEntryEvent> for AgentConnectionEntry {}
|
||||
|
||||
pub struct AgentConnectionStore {
|
||||
project: Entity<Project>,
|
||||
entries: HashMap<ExternalAgent, Entity<ConnectionEntry>>,
|
||||
entries: HashMap<ExternalAgent, Entity<AgentConnectionEntry>>,
|
||||
_subscriptions: Vec<Subscription>,
|
||||
}
|
||||
|
||||
@@ -59,17 +68,21 @@ impl AgentConnectionStore {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn entry(&self, key: &ExternalAgent) -> Option<&Entity<AgentConnectionEntry>> {
|
||||
self.entries.get(key)
|
||||
}
|
||||
|
||||
pub fn request_connection(
|
||||
&mut self,
|
||||
key: ExternalAgent,
|
||||
server: Rc<dyn AgentServer>,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Entity<ConnectionEntry> {
|
||||
) -> Entity<AgentConnectionEntry> {
|
||||
self.entries.get(&key).cloned().unwrap_or_else(|| {
|
||||
let (mut new_version_rx, connect_task) = self.start_connection(server.clone(), cx);
|
||||
let connect_task = connect_task.shared();
|
||||
|
||||
let entry = cx.new(|_cx| ConnectionEntry::Connecting {
|
||||
let entry = cx.new(|_cx| AgentConnectionEntry::Connecting {
|
||||
connect_task: connect_task.clone(),
|
||||
});
|
||||
|
||||
@@ -79,18 +92,18 @@ impl AgentConnectionStore {
|
||||
let key = key.clone();
|
||||
let entry = entry.clone();
|
||||
async move |this, cx| match connect_task.await {
|
||||
Ok(connection) => {
|
||||
Ok(connected_state) => {
|
||||
entry.update(cx, |entry, cx| {
|
||||
if let ConnectionEntry::Connecting { .. } = entry {
|
||||
*entry = ConnectionEntry::Connected { connection };
|
||||
if let AgentConnectionEntry::Connecting { .. } = entry {
|
||||
*entry = AgentConnectionEntry::Connected(connected_state);
|
||||
cx.notify();
|
||||
}
|
||||
});
|
||||
}
|
||||
Err(error) => {
|
||||
entry.update(cx, |entry, cx| {
|
||||
if let ConnectionEntry::Connecting { .. } = entry {
|
||||
*entry = ConnectionEntry::Error { error };
|
||||
if let AgentConnectionEntry::Connecting { .. } = entry {
|
||||
*entry = AgentConnectionEntry::Error { error };
|
||||
cx.notify();
|
||||
}
|
||||
});
|
||||
@@ -106,7 +119,7 @@ impl AgentConnectionStore {
|
||||
while let Ok(version) = new_version_rx.recv().await {
|
||||
if let Some(version) = version {
|
||||
entry.update(cx, |_entry, cx| {
|
||||
cx.emit(ConnectionEntryEvent::NewVersionAvailable(
|
||||
cx.emit(AgentConnectionEntryEvent::NewVersionAvailable(
|
||||
version.clone().into(),
|
||||
));
|
||||
});
|
||||
@@ -143,7 +156,7 @@ impl AgentConnectionStore {
|
||||
cx: &mut Context<Self>,
|
||||
) -> (
|
||||
Receiver<Option<String>>,
|
||||
Task<Result<Rc<dyn AgentConnection>, LoadError>>,
|
||||
Task<Result<AgentConnectedState, LoadError>>,
|
||||
) {
|
||||
let (new_version_tx, new_version_rx) = watch::channel::<Option<String>>(None);
|
||||
|
||||
@@ -151,8 +164,14 @@ impl AgentConnectionStore {
|
||||
let delegate = AgentServerDelegate::new(agent_server_store, Some(new_version_tx));
|
||||
|
||||
let connect_task = server.connect(delegate, cx);
|
||||
let connect_task = cx.spawn(async move |_this, _cx| match connect_task.await {
|
||||
Ok(connection) => Ok(connection),
|
||||
let connect_task = cx.spawn(async move |_this, cx| match connect_task.await {
|
||||
Ok(connection) => cx.update(|cx| {
|
||||
let history = cx.new(|cx| ThreadHistory::new(connection.session_list(cx), cx));
|
||||
Ok(AgentConnectedState {
|
||||
connection,
|
||||
history,
|
||||
})
|
||||
}),
|
||||
Err(err) => match err.downcast::<LoadError>() {
|
||||
Ok(load_error) => Err(load_error),
|
||||
Err(err) => Err(LoadError::Other(SharedString::from(err.to_string()))),
|
||||
|
||||
+182
-111
@@ -29,8 +29,6 @@ use zed_actions::agent::{
|
||||
ResolveConflictedFilesWithAgent, ResolveConflictsWithAgent, ReviewBranchDiff,
|
||||
};
|
||||
|
||||
use crate::ManageProfiles;
|
||||
use crate::agent_connection_store::AgentConnectionStore;
|
||||
use crate::ui::{AcpOnboardingModal, ClaudeCodeOnboardingModal};
|
||||
use crate::{
|
||||
AddContextServer, AgentDiffPane, ConnectionView, CopyThreadToClipboard, Follow,
|
||||
@@ -48,12 +46,14 @@ use crate::{
|
||||
NewNativeAgentThreadFromSummary,
|
||||
};
|
||||
use crate::{
|
||||
ExpandMessageEditor, ThreadHistory, ThreadHistoryView, ThreadHistoryViewEvent,
|
||||
ExpandMessageEditor, ThreadHistoryView,
|
||||
text_thread_history::{TextThreadHistory, TextThreadHistoryEvent},
|
||||
};
|
||||
use crate::{ManageProfiles, ThreadHistoryViewEvent};
|
||||
use crate::{ThreadHistory, agent_connection_store::AgentConnectionStore};
|
||||
use agent_settings::AgentSettings;
|
||||
use ai_onboarding::AgentPanelOnboarding;
|
||||
use anyhow::{Result, anyhow};
|
||||
use anyhow::{Context as _, Result, anyhow};
|
||||
use assistant_slash_command::SlashCommandWorkingSet;
|
||||
use assistant_text_thread::{TextThread, TextThreadEvent, TextThreadSummary};
|
||||
use client::UserStore;
|
||||
@@ -621,9 +621,9 @@ fn build_conflicted_files_resolution_prompt(
|
||||
content
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
enum HistoryKind {
|
||||
AgentThreads,
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
enum History {
|
||||
AgentThreads { view: Entity<ThreadHistoryView> },
|
||||
TextThreads,
|
||||
}
|
||||
|
||||
@@ -639,7 +639,7 @@ enum ActiveView {
|
||||
_subscriptions: Vec<gpui::Subscription>,
|
||||
},
|
||||
History {
|
||||
kind: HistoryKind,
|
||||
history: History,
|
||||
},
|
||||
Configuration,
|
||||
}
|
||||
@@ -870,8 +870,6 @@ pub struct AgentPanel {
|
||||
project: Entity<Project>,
|
||||
fs: Arc<dyn Fs>,
|
||||
language_registry: Arc<LanguageRegistry>,
|
||||
acp_history: Entity<ThreadHistory>,
|
||||
acp_history_view: Entity<ThreadHistoryView>,
|
||||
text_thread_history: Entity<TextThreadHistory>,
|
||||
thread_store: Entity<ThreadStore>,
|
||||
text_thread_store: Entity<assistant_text_thread::TextThreadStore>,
|
||||
@@ -1081,26 +1079,9 @@ impl AgentPanel {
|
||||
cx.new(|cx| ContextServerRegistry::new(project.read(cx).context_server_store(), cx));
|
||||
|
||||
let thread_store = ThreadStore::global(cx);
|
||||
let acp_history = cx.new(|cx| ThreadHistory::new(None, cx));
|
||||
let acp_history_view = cx.new(|cx| ThreadHistoryView::new(acp_history.clone(), window, cx));
|
||||
let text_thread_history =
|
||||
cx.new(|cx| TextThreadHistory::new(text_thread_store.clone(), window, cx));
|
||||
cx.subscribe_in(
|
||||
&acp_history_view,
|
||||
window,
|
||||
|this, _, event, window, cx| match event {
|
||||
ThreadHistoryViewEvent::Open(thread) => {
|
||||
this.load_agent_thread(
|
||||
thread.session_id.clone(),
|
||||
thread.cwd.clone(),
|
||||
thread.title.clone(),
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
}
|
||||
},
|
||||
)
|
||||
.detach();
|
||||
|
||||
cx.subscribe_in(
|
||||
&text_thread_history,
|
||||
window,
|
||||
@@ -1120,15 +1101,18 @@ impl AgentPanel {
|
||||
window.defer(cx, move |window, cx| {
|
||||
let panel = weak_panel.clone();
|
||||
let agent_navigation_menu =
|
||||
ContextMenu::build_persistent(window, cx, move |mut menu, _window, cx| {
|
||||
ContextMenu::build_persistent(window, cx, move |mut menu, window, cx| {
|
||||
if let Some(panel) = panel.upgrade() {
|
||||
if let Some(kind) = panel.read(cx).history_kind_for_selected_agent(cx) {
|
||||
menu =
|
||||
Self::populate_recently_updated_menu_section(menu, panel, kind, cx);
|
||||
let view_all_label = match kind {
|
||||
HistoryKind::AgentThreads => "View All",
|
||||
HistoryKind::TextThreads => "View All Text Threads",
|
||||
if let Some(history) = panel
|
||||
.update(cx, |panel, cx| panel.history_for_selected_agent(window, cx))
|
||||
{
|
||||
let view_all_label = match history {
|
||||
History::AgentThreads { .. } => "View All",
|
||||
History::TextThreads => "View All Text Threads",
|
||||
};
|
||||
menu = Self::populate_recently_updated_menu_section(
|
||||
menu, panel, history, cx,
|
||||
);
|
||||
menu = menu.action(view_all_label, Box::new(OpenHistory));
|
||||
}
|
||||
}
|
||||
@@ -1222,8 +1206,6 @@ impl AgentPanel {
|
||||
zoomed: false,
|
||||
pending_serialization: None,
|
||||
onboarding,
|
||||
acp_history,
|
||||
acp_history_view,
|
||||
text_thread_history,
|
||||
thread_store,
|
||||
selected_agent: AgentType::default(),
|
||||
@@ -1288,8 +1270,8 @@ impl AgentPanel {
|
||||
&self.thread_store
|
||||
}
|
||||
|
||||
pub fn history(&self) -> &Entity<ThreadHistory> {
|
||||
&self.acp_history
|
||||
pub fn connection_store(&self) -> &Entity<AgentConnectionStore> {
|
||||
&self.connection_store
|
||||
}
|
||||
|
||||
pub fn open_thread(
|
||||
@@ -1353,27 +1335,41 @@ impl AgentPanel {
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let Some(thread) = self
|
||||
.acp_history
|
||||
.read(cx)
|
||||
.session_for_id(&action.from_session_id)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let agent = ExternalAgent::NativeAgent;
|
||||
|
||||
self.external_thread(
|
||||
Some(ExternalAgent::NativeAgent),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Some(AgentInitialContent::ThreadSummary {
|
||||
session_id: thread.session_id,
|
||||
title: thread.title,
|
||||
}),
|
||||
true,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
let server = agent.server(self.fs.clone(), self.thread_store.clone());
|
||||
let session_id = action.from_session_id.clone();
|
||||
|
||||
let entry = self.connection_store.update(cx, |store, cx| {
|
||||
store.request_connection(agent.clone(), server, cx)
|
||||
});
|
||||
let connect_task = entry.read(cx).wait_for_connection();
|
||||
|
||||
cx.spawn_in(window, async move |this, cx| {
|
||||
let history = connect_task.await?.history;
|
||||
this.update_in(cx, |this, window, cx| {
|
||||
let thread = history
|
||||
.read(cx)
|
||||
.session_for_id(&session_id)
|
||||
.context("Session not found")?;
|
||||
|
||||
this.external_thread(
|
||||
Some(agent),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Some(AgentInitialContent::ThreadSummary {
|
||||
session_id: thread.session_id,
|
||||
title: thread.title,
|
||||
}),
|
||||
true,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
anyhow::Ok(())
|
||||
})
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
}
|
||||
|
||||
fn new_text_thread(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
@@ -1554,13 +1550,52 @@ impl AgentPanel {
|
||||
})
|
||||
}
|
||||
|
||||
fn history_kind_for_selected_agent(&self, cx: &App) -> Option<HistoryKind> {
|
||||
match self.selected_agent {
|
||||
AgentType::NativeAgent => Some(HistoryKind::AgentThreads),
|
||||
AgentType::TextThread => Some(HistoryKind::TextThreads),
|
||||
AgentType::Custom { .. } => {
|
||||
if self.acp_history.read(cx).has_session_list() {
|
||||
Some(HistoryKind::AgentThreads)
|
||||
fn has_history_for_selected_agent(&self, cx: &App) -> bool {
|
||||
match &self.selected_agent {
|
||||
AgentType::TextThread | AgentType::NativeAgent => true,
|
||||
AgentType::Custom { name } => {
|
||||
let agent = ExternalAgent::Custom { name: name.clone() };
|
||||
self.connection_store
|
||||
.read(cx)
|
||||
.entry(&agent)
|
||||
.map_or(false, |entry| entry.read(cx).history().is_some())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn history_for_selected_agent(
|
||||
&self,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Option<History> {
|
||||
match &self.selected_agent {
|
||||
AgentType::TextThread => Some(History::TextThreads),
|
||||
AgentType::NativeAgent => {
|
||||
let history = self
|
||||
.connection_store
|
||||
.read(cx)
|
||||
.entry(&ExternalAgent::NativeAgent)?
|
||||
.read(cx)
|
||||
.history()?
|
||||
.clone();
|
||||
|
||||
Some(History::AgentThreads {
|
||||
view: self.create_thread_history_view(history, window, cx),
|
||||
})
|
||||
}
|
||||
AgentType::Custom { name } => {
|
||||
let agent = ExternalAgent::Custom { name: name.clone() };
|
||||
let history = self
|
||||
.connection_store
|
||||
.read(cx)
|
||||
.entry(&agent)?
|
||||
.read(cx)
|
||||
.history()?
|
||||
.clone();
|
||||
if history.read(cx).has_session_list() {
|
||||
Some(History::AgentThreads {
|
||||
view: self.create_thread_history_view(history, window, cx),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -1568,13 +1603,38 @@ impl AgentPanel {
|
||||
}
|
||||
}
|
||||
|
||||
fn create_thread_history_view(
|
||||
&self,
|
||||
history: Entity<ThreadHistory>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Entity<ThreadHistoryView> {
|
||||
let view = cx.new(|cx| ThreadHistoryView::new(history.clone(), window, cx));
|
||||
cx.subscribe_in(&view, window, |this, _, event, window, cx| match event {
|
||||
ThreadHistoryViewEvent::Open(thread) => {
|
||||
this.load_agent_thread(
|
||||
thread.session_id.clone(),
|
||||
thread.cwd.clone(),
|
||||
thread.title.clone(),
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
view
|
||||
}
|
||||
|
||||
fn open_history(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let Some(kind) = self.history_kind_for_selected_agent(cx) else {
|
||||
let Some(history) = self.history_for_selected_agent(window, cx) else {
|
||||
return;
|
||||
};
|
||||
|
||||
if let ActiveView::History { kind: active_kind } = self.active_view {
|
||||
if active_kind == kind {
|
||||
if let ActiveView::History {
|
||||
history: active_history,
|
||||
} = &self.active_view
|
||||
{
|
||||
if active_history == &history {
|
||||
if let Some(previous_view) = self.previous_view.take() {
|
||||
self.set_active_view(previous_view, true, window, cx);
|
||||
}
|
||||
@@ -1582,7 +1642,7 @@ impl AgentPanel {
|
||||
}
|
||||
}
|
||||
|
||||
self.set_active_view(ActiveView::History { kind }, true, window, cx);
|
||||
self.set_active_view(ActiveView::History { history }, true, window, cx);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
@@ -1655,7 +1715,7 @@ impl AgentPanel {
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
if self.history_kind_for_selected_agent(cx).is_none() {
|
||||
if !self.has_history_for_selected_agent(cx) {
|
||||
return;
|
||||
}
|
||||
self.agent_navigation_menu_handle.toggle(window, cx);
|
||||
@@ -2096,7 +2156,7 @@ impl AgentPanel {
|
||||
let was_in_agent_history = matches!(
|
||||
self.active_view,
|
||||
ActiveView::History {
|
||||
kind: HistoryKind::AgentThreads
|
||||
history: History::AgentThreads { .. }
|
||||
}
|
||||
);
|
||||
let current_is_uninitialized = matches!(self.active_view, ActiveView::Uninitialized);
|
||||
@@ -2154,16 +2214,13 @@ impl AgentPanel {
|
||||
}
|
||||
};
|
||||
|
||||
let is_in_agent_history = matches!(
|
||||
self.active_view,
|
||||
ActiveView::History {
|
||||
kind: HistoryKind::AgentThreads
|
||||
if let ActiveView::History { history } = &self.active_view {
|
||||
if !was_in_agent_history && let History::AgentThreads { view } = history {
|
||||
view.update(cx, |view, cx| {
|
||||
view.history()
|
||||
.update(cx, |history, cx| history.refresh_full_history(cx))
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
if !was_in_agent_history && is_in_agent_history {
|
||||
self.acp_history
|
||||
.update(cx, |history, cx| history.refresh_full_history(cx));
|
||||
}
|
||||
|
||||
if focus {
|
||||
@@ -2175,14 +2232,14 @@ impl AgentPanel {
|
||||
fn populate_recently_updated_menu_section(
|
||||
mut menu: ContextMenu,
|
||||
panel: Entity<Self>,
|
||||
kind: HistoryKind,
|
||||
history: History,
|
||||
cx: &mut Context<ContextMenu>,
|
||||
) -> ContextMenu {
|
||||
match kind {
|
||||
HistoryKind::AgentThreads => {
|
||||
let entries = panel
|
||||
match history {
|
||||
History::AgentThreads { view } => {
|
||||
let entries = view
|
||||
.read(cx)
|
||||
.acp_history
|
||||
.history()
|
||||
.read(cx)
|
||||
.sessions()
|
||||
.iter()
|
||||
@@ -2224,7 +2281,7 @@ impl AgentPanel {
|
||||
});
|
||||
}
|
||||
}
|
||||
HistoryKind::TextThreads => {
|
||||
History::TextThreads => {
|
||||
let entries = panel
|
||||
.read(cx)
|
||||
.text_thread_store
|
||||
@@ -2518,7 +2575,6 @@ impl AgentPanel {
|
||||
project,
|
||||
thread_store,
|
||||
self.prompt_store.clone(),
|
||||
self.acp_history.clone(),
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
@@ -3056,9 +3112,9 @@ impl Focusable for AgentPanel {
|
||||
match &self.active_view {
|
||||
ActiveView::Uninitialized => self.focus_handle.clone(),
|
||||
ActiveView::AgentThread { server_view, .. } => server_view.focus_handle(cx),
|
||||
ActiveView::History { kind } => match kind {
|
||||
HistoryKind::AgentThreads => self.acp_history_view.focus_handle(cx),
|
||||
HistoryKind::TextThreads => self.text_thread_history.focus_handle(cx),
|
||||
ActiveView::History { history: kind } => match kind {
|
||||
History::AgentThreads { view } => view.read(cx).focus_handle(cx),
|
||||
History::TextThreads => self.text_thread_history.focus_handle(cx),
|
||||
},
|
||||
ActiveView::TextThread {
|
||||
text_thread_editor, ..
|
||||
@@ -3292,10 +3348,10 @@ impl AgentPanel {
|
||||
.into_any_element(),
|
||||
}
|
||||
}
|
||||
ActiveView::History { kind } => {
|
||||
ActiveView::History { history: kind } => {
|
||||
let title = match kind {
|
||||
HistoryKind::AgentThreads => "History",
|
||||
HistoryKind::TextThreads => "Text Thread History",
|
||||
History::AgentThreads { .. } => "History",
|
||||
History::TextThreads => "Text Thread History",
|
||||
};
|
||||
Label::new(title).truncate().into_any_element()
|
||||
}
|
||||
@@ -4122,7 +4178,7 @@ impl AgentPanel {
|
||||
selected_agent.into_any_element()
|
||||
};
|
||||
|
||||
let show_history_menu = self.history_kind_for_selected_agent(cx).is_some();
|
||||
let show_history_menu = self.has_history_for_selected_agent(cx);
|
||||
let has_v2_flag = cx.has_flag::<AgentV2FeatureFlag>();
|
||||
let is_empty_state = !self.active_thread_has_messages(cx);
|
||||
|
||||
@@ -4402,6 +4458,14 @@ impl AgentPanel {
|
||||
return false;
|
||||
}
|
||||
|
||||
let has_configured_non_zed_providers = LanguageModelRegistry::read_global(cx)
|
||||
.visible_providers()
|
||||
.iter()
|
||||
.any(|provider| {
|
||||
provider.is_authenticated(cx)
|
||||
&& provider.id() != language_model::ZED_CLOUD_PROVIDER_ID
|
||||
});
|
||||
|
||||
match &self.active_view {
|
||||
ActiveView::Uninitialized | ActiveView::History { .. } | ActiveView::Configuration => {
|
||||
false
|
||||
@@ -4411,17 +4475,15 @@ impl AgentPanel {
|
||||
{
|
||||
false
|
||||
}
|
||||
_ => {
|
||||
let history_is_empty = self.acp_history.read(cx).is_empty();
|
||||
|
||||
let has_configured_non_zed_providers = LanguageModelRegistry::read_global(cx)
|
||||
.visible_providers()
|
||||
.iter()
|
||||
.any(|provider| {
|
||||
provider.is_authenticated(cx)
|
||||
&& provider.id() != language_model::ZED_CLOUD_PROVIDER_ID
|
||||
});
|
||||
|
||||
ActiveView::AgentThread { server_view } => {
|
||||
let history_is_empty = server_view
|
||||
.read(cx)
|
||||
.history()
|
||||
.is_none_or(|h| h.read(cx).is_empty());
|
||||
history_is_empty || !has_configured_non_zed_providers
|
||||
}
|
||||
ActiveView::TextThread { .. } => {
|
||||
let history_is_empty = self.text_thread_history.read(cx).is_empty();
|
||||
history_is_empty || !has_configured_non_zed_providers
|
||||
}
|
||||
}
|
||||
@@ -4803,9 +4865,9 @@ impl Render for AgentPanel {
|
||||
ActiveView::AgentThread { server_view, .. } => parent
|
||||
.child(server_view.clone())
|
||||
.child(self.render_drag_target(cx)),
|
||||
ActiveView::History { kind } => match kind {
|
||||
HistoryKind::AgentThreads => parent.child(self.acp_history_view.clone()),
|
||||
HistoryKind::TextThreads => parent.child(self.text_thread_history.clone()),
|
||||
ActiveView::History { history: kind } => match kind {
|
||||
History::AgentThreads { view } => parent.child(view.clone()),
|
||||
History::TextThreads => parent.child(self.text_thread_history.clone()),
|
||||
},
|
||||
ActiveView::TextThread {
|
||||
text_thread_editor,
|
||||
@@ -4910,17 +4972,26 @@ impl rules_library::InlineAssistDelegate for PromptLibraryInlineAssist {
|
||||
let Some(panel) = workspace.read(cx).panel::<AgentPanel>(cx) else {
|
||||
return;
|
||||
};
|
||||
let Some(history) = panel
|
||||
.read(cx)
|
||||
.connection_store()
|
||||
.read(cx)
|
||||
.entry(&crate::ExternalAgent::NativeAgent)
|
||||
.and_then(|s| s.read(cx).history())
|
||||
else {
|
||||
log::error!("No connection entry found for native agent");
|
||||
return;
|
||||
};
|
||||
let project = workspace.read(cx).project().downgrade();
|
||||
let panel = panel.read(cx);
|
||||
let thread_store = panel.thread_store().clone();
|
||||
let history = panel.history().downgrade();
|
||||
assistant.assist(
|
||||
prompt_editor,
|
||||
self.workspace.clone(),
|
||||
project,
|
||||
thread_store,
|
||||
None,
|
||||
history,
|
||||
history.downgrade(),
|
||||
initial_prompt,
|
||||
window,
|
||||
cx,
|
||||
|
||||
@@ -67,7 +67,9 @@ use super::entry_view_state::EntryViewState;
|
||||
use super::thread_history::ThreadHistory;
|
||||
use crate::ModeSelector;
|
||||
use crate::ModelSelectorPopover;
|
||||
use crate::agent_connection_store::{AgentConnectionStore, ConnectionEntryEvent};
|
||||
use crate::agent_connection_store::{
|
||||
AgentConnectedState, AgentConnectionEntryEvent, AgentConnectionStore,
|
||||
};
|
||||
use crate::agent_diff::AgentDiff;
|
||||
use crate::entry_view_state::{EntryViewEvent, ViewEvent};
|
||||
use crate::message_editor::{MessageEditor, MessageEditorEvent};
|
||||
@@ -314,7 +316,6 @@ pub struct ConnectionView {
|
||||
thread_store: Option<Entity<ThreadStore>>,
|
||||
prompt_store: Option<Entity<PromptStore>>,
|
||||
server_state: ServerState,
|
||||
history: Entity<ThreadHistory>,
|
||||
focus_handle: FocusHandle,
|
||||
notifications: Vec<WindowHandle<AgentNotification>>,
|
||||
notification_subscriptions: HashMap<WindowHandle<AgentNotification>, Vec<Subscription>>,
|
||||
@@ -418,6 +419,7 @@ pub struct ConnectedServerState {
|
||||
active_id: Option<acp::SessionId>,
|
||||
threads: HashMap<acp::SessionId, Entity<ThreadView>>,
|
||||
connection: Rc<dyn AgentConnection>,
|
||||
history: Entity<ThreadHistory>,
|
||||
conversation: Entity<Conversation>,
|
||||
_connection_entry_subscription: Subscription,
|
||||
}
|
||||
@@ -484,7 +486,6 @@ impl ConnectionView {
|
||||
project: Entity<Project>,
|
||||
thread_store: Option<Entity<ThreadStore>>,
|
||||
prompt_store: Option<Entity<PromptStore>>,
|
||||
history: Entity<ThreadHistory>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Self {
|
||||
@@ -537,7 +538,6 @@ impl ConnectionView {
|
||||
notifications: Vec::new(),
|
||||
notification_subscriptions: HashMap::default(),
|
||||
auth_task: None,
|
||||
history,
|
||||
_subscriptions: subscriptions,
|
||||
focus_handle: cx.focus_handle(),
|
||||
}
|
||||
@@ -660,7 +660,7 @@ impl ConnectionView {
|
||||
|
||||
let connection_entry_subscription =
|
||||
cx.subscribe(&connection_entry, |this, _entry, event, cx| match event {
|
||||
ConnectionEntryEvent::NewVersionAvailable(version) => {
|
||||
AgentConnectionEntryEvent::NewVersionAvailable(version) => {
|
||||
if let Some(thread) = this.active_thread() {
|
||||
thread.update(cx, |thread, cx| {
|
||||
thread.new_server_version_available = Some(version.clone());
|
||||
@@ -674,8 +674,11 @@ impl ConnectionView {
|
||||
|
||||
let load_session_id = resume_session_id.clone();
|
||||
let load_task = cx.spawn_in(window, async move |this, cx| {
|
||||
let connection = match connect_result.await {
|
||||
Ok(connection) => connection,
|
||||
let (connection, history) = match connect_result.await {
|
||||
Ok(AgentConnectedState {
|
||||
connection,
|
||||
history,
|
||||
}) => (connection, history),
|
||||
Err(err) => {
|
||||
this.update_in(cx, |this, window, cx| {
|
||||
this.handle_load_error(load_session_id.clone(), err, window, cx);
|
||||
@@ -764,6 +767,7 @@ impl ConnectionView {
|
||||
conversation.clone(),
|
||||
resumed_without_history,
|
||||
initial_content,
|
||||
history.clone(),
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
@@ -777,14 +781,6 @@ impl ConnectionView {
|
||||
}
|
||||
|
||||
let id = current.read(cx).thread.read(cx).session_id().clone();
|
||||
let session_list = if connection.supports_session_history() {
|
||||
connection.session_list(cx)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
this.history.update(cx, |history, cx| {
|
||||
history.set_session_list(session_list, cx);
|
||||
});
|
||||
this.set_server_state(
|
||||
ServerState::Connected(ConnectedServerState {
|
||||
connection,
|
||||
@@ -792,6 +788,7 @@ impl ConnectionView {
|
||||
active_id: Some(id.clone()),
|
||||
threads: HashMap::from_iter([(id, current)]),
|
||||
conversation,
|
||||
history,
|
||||
_connection_entry_subscription: connection_entry_subscription,
|
||||
}),
|
||||
cx,
|
||||
@@ -825,6 +822,7 @@ impl ConnectionView {
|
||||
conversation: Entity<Conversation>,
|
||||
resumed_without_history: bool,
|
||||
initial_content: Option<AgentInitialContent>,
|
||||
history: Entity<ThreadHistory>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Entity<ThreadView> {
|
||||
@@ -841,7 +839,7 @@ impl ConnectionView {
|
||||
self.workspace.clone(),
|
||||
self.project.downgrade(),
|
||||
self.thread_store.clone(),
|
||||
self.history.downgrade(),
|
||||
history.downgrade(),
|
||||
self.prompt_store.clone(),
|
||||
prompt_capabilities.clone(),
|
||||
available_commands.clone(),
|
||||
@@ -1008,7 +1006,7 @@ impl ConnectionView {
|
||||
resumed_without_history,
|
||||
self.project.downgrade(),
|
||||
self.thread_store.clone(),
|
||||
self.history.clone(),
|
||||
history,
|
||||
self.prompt_store.clone(),
|
||||
initial_content,
|
||||
subscriptions,
|
||||
@@ -1090,6 +1088,7 @@ impl ConnectionView {
|
||||
threads: HashMap::default(),
|
||||
connection,
|
||||
conversation: cx.new(|_cx| Conversation::default()),
|
||||
history: cx.new(|cx| ThreadHistory::new(None, cx)),
|
||||
_connection_entry_subscription: Subscription::new(|| {}),
|
||||
}),
|
||||
cx,
|
||||
@@ -1694,10 +1693,10 @@ impl ConnectionView {
|
||||
cx.spawn_in(window, async move |this, cx| {
|
||||
let subagent_thread = subagent_thread_task.await?;
|
||||
this.update_in(cx, |this, window, cx| {
|
||||
let conversation = this
|
||||
let Some((conversation, history)) = this
|
||||
.as_connected()
|
||||
.map(|connected| connected.conversation.clone());
|
||||
let Some(conversation) = conversation else {
|
||||
.map(|connected| (connected.conversation.clone(), connected.history.clone()))
|
||||
else {
|
||||
return;
|
||||
};
|
||||
conversation.update(cx, |conversation, cx| {
|
||||
@@ -1709,6 +1708,7 @@ impl ConnectionView {
|
||||
conversation,
|
||||
false,
|
||||
None,
|
||||
history,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
@@ -2215,9 +2215,11 @@ impl ConnectionView {
|
||||
let agent_name = self.agent.name();
|
||||
let workspace = self.workspace.clone();
|
||||
let project = self.project.downgrade();
|
||||
let history = self.history.downgrade();
|
||||
|
||||
let Some(thread) = self.active_thread() else {
|
||||
let Some(connected) = self.as_connected() else {
|
||||
return;
|
||||
};
|
||||
let history = connected.history.downgrade();
|
||||
let Some(thread) = connected.active_view() else {
|
||||
return;
|
||||
};
|
||||
let prompt_capabilities = thread.read(cx).prompt_capabilities.clone();
|
||||
@@ -2610,8 +2612,16 @@ impl ConnectionView {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn history(&self) -> Option<&Entity<ThreadHistory>> {
|
||||
self.as_connected().map(|c| &c.history)
|
||||
}
|
||||
|
||||
pub fn delete_history_entry(&mut self, session_id: &acp::SessionId, cx: &mut Context<Self>) {
|
||||
let task = self
|
||||
let Some(connected) = self.as_connected() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let task = connected
|
||||
.history
|
||||
.update(cx, |history, cx| history.delete_session(&session_id, cx));
|
||||
task.detach_and_log_err(cx);
|
||||
@@ -2900,8 +2910,6 @@ pub(crate) mod tests {
|
||||
let workspace = multi_workspace.read_with(cx, |mw, _| mw.workspace().clone());
|
||||
|
||||
let thread_store = cx.update(|_window, cx| cx.new(|cx| ThreadStore::new(cx)));
|
||||
// Create history without an initial session list - it will be set after connection
|
||||
let history = cx.update(|_window, cx| cx.new(|cx| ThreadHistory::new(None, cx)));
|
||||
let connection_store =
|
||||
cx.update(|_window, cx| cx.new(|cx| AgentConnectionStore::new(project.clone(), cx)));
|
||||
|
||||
@@ -2921,7 +2929,6 @@ pub(crate) mod tests {
|
||||
project,
|
||||
Some(thread_store),
|
||||
None,
|
||||
history.clone(),
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
@@ -2931,6 +2938,14 @@ pub(crate) mod tests {
|
||||
// Wait for connection to establish
|
||||
cx.run_until_parked();
|
||||
|
||||
let history = cx.update(|_window, cx| {
|
||||
thread_view
|
||||
.read(cx)
|
||||
.history()
|
||||
.expect("Missing history")
|
||||
.clone()
|
||||
});
|
||||
|
||||
// Initially empty because StubAgentConnection.session_list() returns None
|
||||
active_thread(&thread_view, cx).read_with(cx, |view, _cx| {
|
||||
assert_eq!(view.recent_history_entries.len(), 0);
|
||||
@@ -3007,7 +3022,6 @@ pub(crate) mod tests {
|
||||
let workspace = multi_workspace.read_with(cx, |mw, _| mw.workspace().clone());
|
||||
|
||||
let thread_store = cx.update(|_window, cx| cx.new(|cx| ThreadStore::new(cx)));
|
||||
let history = cx.update(|_window, cx| cx.new(|cx| ThreadHistory::new(None, cx)));
|
||||
let connection_store =
|
||||
cx.update(|_window, cx| cx.new(|cx| AgentConnectionStore::new(project.clone(), cx)));
|
||||
|
||||
@@ -3027,7 +3041,6 @@ pub(crate) mod tests {
|
||||
project,
|
||||
Some(thread_store),
|
||||
None,
|
||||
history,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
@@ -3066,7 +3079,6 @@ pub(crate) mod tests {
|
||||
let captured_cwd = connection.captured_cwd.clone();
|
||||
|
||||
let thread_store = cx.update(|_window, cx| cx.new(|cx| ThreadStore::new(cx)));
|
||||
let history = cx.update(|_window, cx| cx.new(|cx| ThreadHistory::new(None, cx)));
|
||||
let connection_store =
|
||||
cx.update(|_window, cx| cx.new(|cx| AgentConnectionStore::new(project.clone(), cx)));
|
||||
|
||||
@@ -3086,7 +3098,6 @@ pub(crate) mod tests {
|
||||
project,
|
||||
Some(thread_store),
|
||||
None,
|
||||
history,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
@@ -3123,7 +3134,6 @@ pub(crate) mod tests {
|
||||
let captured_cwd = connection.captured_cwd.clone();
|
||||
|
||||
let thread_store = cx.update(|_window, cx| cx.new(|cx| ThreadStore::new(cx)));
|
||||
let history = cx.update(|_window, cx| cx.new(|cx| ThreadHistory::new(None, cx)));
|
||||
let connection_store =
|
||||
cx.update(|_window, cx| cx.new(|cx| AgentConnectionStore::new(project.clone(), cx)));
|
||||
|
||||
@@ -3143,7 +3153,6 @@ pub(crate) mod tests {
|
||||
project,
|
||||
Some(thread_store),
|
||||
None,
|
||||
history,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
@@ -3180,7 +3189,6 @@ pub(crate) mod tests {
|
||||
let captured_cwd = connection.captured_cwd.clone();
|
||||
|
||||
let thread_store = cx.update(|_window, cx| cx.new(|cx| ThreadStore::new(cx)));
|
||||
let history = cx.update(|_window, cx| cx.new(|cx| ThreadHistory::new(None, cx)));
|
||||
let connection_store =
|
||||
cx.update(|_window, cx| cx.new(|cx| AgentConnectionStore::new(project.clone(), cx)));
|
||||
|
||||
@@ -3200,7 +3208,6 @@ pub(crate) mod tests {
|
||||
project,
|
||||
Some(thread_store),
|
||||
None,
|
||||
history,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
@@ -3498,7 +3505,6 @@ pub(crate) mod tests {
|
||||
|
||||
// Set up thread view in workspace 1
|
||||
let thread_store = cx.update(|_window, cx| cx.new(|cx| ThreadStore::new(cx)));
|
||||
let history = cx.update(|_window, cx| cx.new(|cx| ThreadHistory::new(None, cx)));
|
||||
let connection_store =
|
||||
cx.update(|_window, cx| cx.new(|cx| AgentConnectionStore::new(project1.clone(), cx)));
|
||||
|
||||
@@ -3519,7 +3525,6 @@ pub(crate) mod tests {
|
||||
project1.clone(),
|
||||
Some(thread_store),
|
||||
None,
|
||||
history,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
@@ -3676,7 +3681,8 @@ pub(crate) mod tests {
|
||||
agent: impl AgentServer + 'static,
|
||||
cx: &mut TestAppContext,
|
||||
) -> (Entity<ConnectionView>, &mut VisualTestContext) {
|
||||
let (thread_view, _history, cx) = setup_thread_view_with_history(agent, cx).await;
|
||||
let (thread_view, _history, cx) =
|
||||
setup_thread_view_with_history_and_initial_content(agent, None, cx).await;
|
||||
(thread_view, cx)
|
||||
}
|
||||
|
||||
@@ -3688,7 +3694,9 @@ pub(crate) mod tests {
|
||||
Entity<ThreadHistory>,
|
||||
&mut VisualTestContext,
|
||||
) {
|
||||
setup_thread_view_with_history_and_initial_content(agent, None, cx).await
|
||||
let (thread_view, history, cx) =
|
||||
setup_thread_view_with_history_and_initial_content(agent, None, cx).await;
|
||||
(thread_view, history.expect("Missing history"), cx)
|
||||
}
|
||||
|
||||
async fn setup_thread_view_with_initial_content(
|
||||
@@ -3708,7 +3716,7 @@ pub(crate) mod tests {
|
||||
cx: &mut TestAppContext,
|
||||
) -> (
|
||||
Entity<ConnectionView>,
|
||||
Entity<ThreadHistory>,
|
||||
Option<Entity<ThreadHistory>>,
|
||||
&mut VisualTestContext,
|
||||
) {
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
@@ -3718,18 +3726,19 @@ pub(crate) mod tests {
|
||||
let workspace = multi_workspace.read_with(cx, |mw, _| mw.workspace().clone());
|
||||
|
||||
let thread_store = cx.update(|_window, cx| cx.new(|cx| ThreadStore::new(cx)));
|
||||
let history = cx.update(|_window, cx| cx.new(|cx| ThreadHistory::new(None, cx)));
|
||||
let connection_store =
|
||||
cx.update(|_window, cx| cx.new(|cx| AgentConnectionStore::new(project.clone(), cx)));
|
||||
|
||||
let agent_key = ExternalAgent::Custom {
|
||||
name: "Test".into(),
|
||||
};
|
||||
|
||||
let thread_view = cx.update(|window, cx| {
|
||||
cx.new(|cx| {
|
||||
ConnectionView::new(
|
||||
Rc::new(agent),
|
||||
connection_store,
|
||||
ExternalAgent::Custom {
|
||||
name: "Test".into(),
|
||||
},
|
||||
connection_store.clone(),
|
||||
agent_key.clone(),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
@@ -3738,13 +3747,20 @@ pub(crate) mod tests {
|
||||
project,
|
||||
Some(thread_store),
|
||||
None,
|
||||
history.clone(),
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
});
|
||||
cx.run_until_parked();
|
||||
|
||||
let history = cx.update(|_window, cx| {
|
||||
connection_store
|
||||
.read(cx)
|
||||
.entry(&agent_key)
|
||||
.and_then(|e| e.read(cx).history().cloned())
|
||||
});
|
||||
|
||||
(thread_view, history, cx)
|
||||
}
|
||||
|
||||
@@ -4454,7 +4470,6 @@ pub(crate) mod tests {
|
||||
let workspace = multi_workspace.read_with(cx, |mw, _| mw.workspace().clone());
|
||||
|
||||
let thread_store = cx.update(|_window, cx| cx.new(|cx| ThreadStore::new(cx)));
|
||||
let history = cx.update(|_window, cx| cx.new(|cx| ThreadHistory::new(None, cx)));
|
||||
let connection_store =
|
||||
cx.update(|_window, cx| cx.new(|cx| AgentConnectionStore::new(project.clone(), cx)));
|
||||
|
||||
@@ -4475,7 +4490,6 @@ pub(crate) mod tests {
|
||||
project.clone(),
|
||||
Some(thread_store.clone()),
|
||||
None,
|
||||
history,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
|
||||
@@ -266,7 +266,7 @@ impl InlineAssistant {
|
||||
return;
|
||||
};
|
||||
|
||||
let configuration_error = || {
|
||||
let configuration_error = |cx| {
|
||||
let model_registry = LanguageModelRegistry::read_global(cx);
|
||||
model_registry.configuration_error(model_registry.inline_assistant_model(), cx)
|
||||
};
|
||||
@@ -278,7 +278,15 @@ impl InlineAssistant {
|
||||
|
||||
let prompt_store = agent_panel.prompt_store().as_ref().cloned();
|
||||
let thread_store = agent_panel.thread_store().clone();
|
||||
let history = agent_panel.history().downgrade();
|
||||
let Some(history) = agent_panel
|
||||
.connection_store()
|
||||
.read(cx)
|
||||
.entry(&crate::ExternalAgent::NativeAgent)
|
||||
.and_then(|s| s.read(cx).history().cloned())
|
||||
else {
|
||||
log::error!("No connection entry found for native agent");
|
||||
return;
|
||||
};
|
||||
|
||||
let handle_assist =
|
||||
|window: &mut Window, cx: &mut Context<Workspace>| match inline_assist_target {
|
||||
@@ -290,7 +298,7 @@ impl InlineAssistant {
|
||||
workspace.project().downgrade(),
|
||||
thread_store,
|
||||
prompt_store,
|
||||
history,
|
||||
history.downgrade(),
|
||||
action.prompt.clone(),
|
||||
window,
|
||||
cx,
|
||||
@@ -305,7 +313,7 @@ impl InlineAssistant {
|
||||
workspace.project().downgrade(),
|
||||
thread_store,
|
||||
prompt_store,
|
||||
history,
|
||||
history.downgrade(),
|
||||
action.prompt.clone(),
|
||||
window,
|
||||
cx,
|
||||
@@ -314,7 +322,7 @@ impl InlineAssistant {
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(error) = configuration_error() {
|
||||
if let Some(error) = configuration_error(cx) {
|
||||
if let ConfigurationError::ProviderNotAuthenticated(provider) = error {
|
||||
cx.spawn(async move |_, cx| {
|
||||
cx.update(|cx| provider.authenticate(cx)).await?;
|
||||
@@ -322,7 +330,7 @@ impl InlineAssistant {
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
|
||||
if configuration_error().is_none() {
|
||||
if configuration_error(cx).is_none() {
|
||||
handle_assist(window, cx);
|
||||
}
|
||||
} else {
|
||||
@@ -1969,7 +1977,16 @@ impl CodeActionProvider for AssistantCodeActionProvider {
|
||||
.panel::<AgentPanel>(cx)
|
||||
.context("missing agent panel")?
|
||||
.read(cx);
|
||||
anyhow::Ok((panel.thread_store().clone(), panel.history().downgrade()))
|
||||
|
||||
let history = panel
|
||||
.connection_store()
|
||||
.read(cx)
|
||||
.entry(&crate::ExternalAgent::NativeAgent)
|
||||
.and_then(|e| e.read(cx).history())
|
||||
.context("no history found for native agent")?
|
||||
.downgrade();
|
||||
|
||||
anyhow::Ok((panel.thread_store().clone(), history))
|
||||
})??;
|
||||
let editor = editor.upgrade().context("editor was released")?;
|
||||
let range = editor
|
||||
|
||||
@@ -116,6 +116,10 @@ impl TextThreadHistory {
|
||||
this
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.visible_items.is_empty()
|
||||
}
|
||||
|
||||
fn update_visible_items(&mut self, preserve_selected_item: bool, cx: &mut Context<Self>) {
|
||||
let entries = self.text_thread_store.update(cx, |store, _| {
|
||||
store.ordered_text_threads().cloned().collect::<Vec<_>>()
|
||||
|
||||
@@ -19,14 +19,23 @@ impl ThreadHistory {
|
||||
_refresh_task: Task::ready(()),
|
||||
_watch_task: None,
|
||||
};
|
||||
this.set_session_list(session_list, cx);
|
||||
this.set_session_list_impl(session_list, cx);
|
||||
this
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub fn set_session_list(
|
||||
&mut self,
|
||||
session_list: Option<Rc<dyn AgentSessionList>>,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.set_session_list_impl(session_list, cx);
|
||||
}
|
||||
|
||||
fn set_session_list_impl(
|
||||
&mut self,
|
||||
session_list: Option<Rc<dyn AgentSessionList>>,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
if let (Some(current), Some(next)) = (&self.session_list, &session_list)
|
||||
&& Rc::ptr_eq(current, next)
|
||||
|
||||
@@ -117,6 +117,10 @@ impl ThreadHistoryView {
|
||||
this
|
||||
}
|
||||
|
||||
pub fn history(&self) -> &Entity<ThreadHistory> {
|
||||
&self.history
|
||||
}
|
||||
|
||||
fn update_visible_items(&mut self, preserve_selected_item: bool, cx: &mut Context<Self>) {
|
||||
let entries = self.history.read(cx).sessions().to_vec();
|
||||
let new_list_items = if self.search_query.is_empty() {
|
||||
|
||||
Reference in new issue
Block a user