Rebuild recently opened threads for ACP (#36531)
Closes #ISSUE Release Notes: - N/A
This commit is contained in:
parent
88c4a5ca49
commit
88754a70f7
7 changed files with 109 additions and 63 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -206,6 +206,7 @@ dependencies = [
|
||||||
"collections",
|
"collections",
|
||||||
"context_server",
|
"context_server",
|
||||||
"ctor",
|
"ctor",
|
||||||
|
"db",
|
||||||
"editor",
|
"editor",
|
||||||
"env_logger 0.11.8",
|
"env_logger 0.11.8",
|
||||||
"fs",
|
"fs",
|
||||||
|
|
|
@ -26,6 +26,7 @@ chrono.workspace = true
|
||||||
cloud_llm_client.workspace = true
|
cloud_llm_client.workspace = true
|
||||||
collections.workspace = true
|
collections.workspace = true
|
||||||
context_server.workspace = true
|
context_server.workspace = true
|
||||||
|
db.workspace = true
|
||||||
fs.workspace = true
|
fs.workspace = true
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
git.workspace = true
|
git.workspace = true
|
||||||
|
|
|
@ -974,7 +974,7 @@ mod tests {
|
||||||
.await;
|
.await;
|
||||||
let project = Project::test(fs.clone(), [], cx).await;
|
let project = Project::test(fs.clone(), [], cx).await;
|
||||||
let context_store = cx.new(|cx| assistant_context::ContextStore::fake(project.clone(), cx));
|
let context_store = cx.new(|cx| assistant_context::ContextStore::fake(project.clone(), cx));
|
||||||
let history_store = cx.new(|cx| HistoryStore::new(context_store, [], cx));
|
let history_store = cx.new(|cx| HistoryStore::new(context_store, cx));
|
||||||
let agent = NativeAgent::new(
|
let agent = NativeAgent::new(
|
||||||
project.clone(),
|
project.clone(),
|
||||||
history_store,
|
history_store,
|
||||||
|
@ -1032,7 +1032,7 @@ mod tests {
|
||||||
fs.insert_tree("/", json!({ "a": {} })).await;
|
fs.insert_tree("/", json!({ "a": {} })).await;
|
||||||
let project = Project::test(fs.clone(), [], cx).await;
|
let project = Project::test(fs.clone(), [], cx).await;
|
||||||
let context_store = cx.new(|cx| assistant_context::ContextStore::fake(project.clone(), cx));
|
let context_store = cx.new(|cx| assistant_context::ContextStore::fake(project.clone(), cx));
|
||||||
let history_store = cx.new(|cx| HistoryStore::new(context_store, [], cx));
|
let history_store = cx.new(|cx| HistoryStore::new(context_store, cx));
|
||||||
let connection = NativeAgentConnection(
|
let connection = NativeAgentConnection(
|
||||||
NativeAgent::new(
|
NativeAgent::new(
|
||||||
project.clone(),
|
project.clone(),
|
||||||
|
@ -1088,7 +1088,7 @@ mod tests {
|
||||||
let project = Project::test(fs.clone(), [], cx).await;
|
let project = Project::test(fs.clone(), [], cx).await;
|
||||||
|
|
||||||
let context_store = cx.new(|cx| assistant_context::ContextStore::fake(project.clone(), cx));
|
let context_store = cx.new(|cx| assistant_context::ContextStore::fake(project.clone(), cx));
|
||||||
let history_store = cx.new(|cx| HistoryStore::new(context_store, [], cx));
|
let history_store = cx.new(|cx| HistoryStore::new(context_store, cx));
|
||||||
|
|
||||||
// Create the agent and connection
|
// Create the agent and connection
|
||||||
let agent = NativeAgent::new(
|
let agent = NativeAgent::new(
|
||||||
|
|
|
@ -3,6 +3,7 @@ use agent_client_protocol as acp;
|
||||||
use anyhow::{Context as _, Result, anyhow};
|
use anyhow::{Context as _, Result, anyhow};
|
||||||
use assistant_context::SavedContextMetadata;
|
use assistant_context::SavedContextMetadata;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
|
use db::kvp::KEY_VALUE_STORE;
|
||||||
use gpui::{App, AsyncApp, Entity, SharedString, Task, prelude::*};
|
use gpui::{App, AsyncApp, Entity, SharedString, Task, prelude::*};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use paths::contexts_dir;
|
use paths::contexts_dir;
|
||||||
|
@ -11,7 +12,7 @@ use std::{collections::VecDeque, path::Path, sync::Arc, time::Duration};
|
||||||
use util::ResultExt as _;
|
use util::ResultExt as _;
|
||||||
|
|
||||||
const MAX_RECENTLY_OPENED_ENTRIES: usize = 6;
|
const MAX_RECENTLY_OPENED_ENTRIES: usize = 6;
|
||||||
const NAVIGATION_HISTORY_PATH: &str = "agent-navigation-history.json";
|
const RECENTLY_OPENED_THREADS_KEY: &str = "recent-agent-threads";
|
||||||
const SAVE_RECENTLY_OPENED_ENTRIES_DEBOUNCE: Duration = Duration::from_millis(50);
|
const SAVE_RECENTLY_OPENED_ENTRIES_DEBOUNCE: Duration = Duration::from_millis(50);
|
||||||
|
|
||||||
const DEFAULT_TITLE: &SharedString = &SharedString::new_static("New Thread");
|
const DEFAULT_TITLE: &SharedString = &SharedString::new_static("New Thread");
|
||||||
|
@ -53,12 +54,10 @@ pub enum HistoryEntryId {
|
||||||
TextThread(Arc<Path>),
|
TextThread(Arc<Path>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
enum SerializedRecentOpen {
|
enum SerializedRecentOpen {
|
||||||
Thread(String),
|
AcpThread(String),
|
||||||
ContextName(String),
|
TextThread(String),
|
||||||
/// Old format which stores the full path
|
|
||||||
Context(String),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct HistoryStore {
|
pub struct HistoryStore {
|
||||||
|
@ -72,29 +71,26 @@ pub struct HistoryStore {
|
||||||
impl HistoryStore {
|
impl HistoryStore {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
context_store: Entity<assistant_context::ContextStore>,
|
context_store: Entity<assistant_context::ContextStore>,
|
||||||
initial_recent_entries: impl IntoIterator<Item = HistoryEntryId>,
|
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let subscriptions = vec![cx.observe(&context_store, |_, _, cx| cx.notify())];
|
let subscriptions = vec![cx.observe(&context_store, |_, _, cx| cx.notify())];
|
||||||
|
|
||||||
cx.spawn(async move |this, cx| {
|
cx.spawn(async move |this, cx| {
|
||||||
let entries = Self::load_recently_opened_entries(cx).await.log_err()?;
|
let entries = Self::load_recently_opened_entries(cx).await;
|
||||||
this.update(cx, |this, _| {
|
this.update(cx, |this, cx| {
|
||||||
this.recently_opened_entries
|
if let Some(entries) = entries.log_err() {
|
||||||
.extend(
|
this.recently_opened_entries = entries;
|
||||||
entries.into_iter().take(
|
}
|
||||||
MAX_RECENTLY_OPENED_ENTRIES
|
|
||||||
.saturating_sub(this.recently_opened_entries.len()),
|
this.reload(cx);
|
||||||
),
|
|
||||||
);
|
|
||||||
})
|
})
|
||||||
.ok()
|
.ok();
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
context_store,
|
context_store,
|
||||||
recently_opened_entries: initial_recent_entries.into_iter().collect(),
|
recently_opened_entries: VecDeque::default(),
|
||||||
threads: Vec::default(),
|
threads: Vec::default(),
|
||||||
_subscriptions: subscriptions,
|
_subscriptions: subscriptions,
|
||||||
_save_recently_opened_entries_task: Task::ready(()),
|
_save_recently_opened_entries_task: Task::ready(()),
|
||||||
|
@ -134,6 +130,18 @@ impl HistoryStore {
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
this.update(cx, |this, cx| {
|
this.update(cx, |this, cx| {
|
||||||
|
if this.recently_opened_entries.len() < MAX_RECENTLY_OPENED_ENTRIES {
|
||||||
|
for thread in threads
|
||||||
|
.iter()
|
||||||
|
.take(MAX_RECENTLY_OPENED_ENTRIES - this.recently_opened_entries.len())
|
||||||
|
.rev()
|
||||||
|
{
|
||||||
|
this.push_recently_opened_entry(
|
||||||
|
HistoryEntryId::AcpThread(thread.id.clone()),
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
this.threads = threads;
|
this.threads = threads;
|
||||||
cx.notify();
|
cx.notify();
|
||||||
})
|
})
|
||||||
|
@ -162,6 +170,16 @@ impl HistoryStore {
|
||||||
history_entries
|
history_entries
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self, cx: &App) -> bool {
|
||||||
|
self.threads.is_empty()
|
||||||
|
&& self
|
||||||
|
.context_store
|
||||||
|
.read(cx)
|
||||||
|
.unordered_contexts()
|
||||||
|
.next()
|
||||||
|
.is_none()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn recent_entries(&self, limit: usize, cx: &mut Context<Self>) -> Vec<HistoryEntry> {
|
pub fn recent_entries(&self, limit: usize, cx: &mut Context<Self>) -> Vec<HistoryEntry> {
|
||||||
self.entries(cx).into_iter().take(limit).collect()
|
self.entries(cx).into_iter().take(limit).collect()
|
||||||
}
|
}
|
||||||
|
@ -215,58 +233,44 @@ impl HistoryStore {
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|entry| match entry {
|
.filter_map(|entry| match entry {
|
||||||
HistoryEntryId::TextThread(path) => path.file_name().map(|file| {
|
HistoryEntryId::TextThread(path) => path.file_name().map(|file| {
|
||||||
SerializedRecentOpen::ContextName(file.to_string_lossy().to_string())
|
SerializedRecentOpen::TextThread(file.to_string_lossy().to_string())
|
||||||
}),
|
}),
|
||||||
HistoryEntryId::AcpThread(id) => Some(SerializedRecentOpen::Thread(id.to_string())),
|
HistoryEntryId::AcpThread(id) => {
|
||||||
|
Some(SerializedRecentOpen::AcpThread(id.to_string()))
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
self._save_recently_opened_entries_task = cx.spawn(async move |_, cx| {
|
self._save_recently_opened_entries_task = cx.spawn(async move |_, cx| {
|
||||||
|
let content = serde_json::to_string(&serialized_entries).unwrap();
|
||||||
cx.background_executor()
|
cx.background_executor()
|
||||||
.timer(SAVE_RECENTLY_OPENED_ENTRIES_DEBOUNCE)
|
.timer(SAVE_RECENTLY_OPENED_ENTRIES_DEBOUNCE)
|
||||||
.await;
|
.await;
|
||||||
cx.background_spawn(async move {
|
KEY_VALUE_STORE
|
||||||
let path = paths::data_dir().join(NAVIGATION_HISTORY_PATH);
|
.write_kvp(RECENTLY_OPENED_THREADS_KEY.to_owned(), content)
|
||||||
let content = serde_json::to_string(&serialized_entries)?;
|
.await
|
||||||
std::fs::write(path, content)?;
|
.log_err();
|
||||||
anyhow::Ok(())
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
.log_err();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_recently_opened_entries(cx: &AsyncApp) -> Task<Result<Vec<HistoryEntryId>>> {
|
fn load_recently_opened_entries(cx: &AsyncApp) -> Task<Result<VecDeque<HistoryEntryId>>> {
|
||||||
cx.background_spawn(async move {
|
cx.background_spawn(async move {
|
||||||
let path = paths::data_dir().join(NAVIGATION_HISTORY_PATH);
|
let json = KEY_VALUE_STORE
|
||||||
let contents = match smol::fs::read_to_string(path).await {
|
.read_kvp(RECENTLY_OPENED_THREADS_KEY)?
|
||||||
Ok(it) => it,
|
.unwrap_or("[]".to_string());
|
||||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
|
let entries = serde_json::from_str::<Vec<SerializedRecentOpen>>(&json)
|
||||||
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")?
|
.context("deserializing persisted agent panel navigation history")?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.take(MAX_RECENTLY_OPENED_ENTRIES)
|
.take(MAX_RECENTLY_OPENED_ENTRIES)
|
||||||
.flat_map(|entry| match entry {
|
.flat_map(|entry| match entry {
|
||||||
SerializedRecentOpen::Thread(id) => Some(HistoryEntryId::AcpThread(
|
SerializedRecentOpen::AcpThread(id) => Some(HistoryEntryId::AcpThread(
|
||||||
acp::SessionId(id.as_str().into()),
|
acp::SessionId(id.as_str().into()),
|
||||||
)),
|
)),
|
||||||
SerializedRecentOpen::ContextName(file_name) => Some(
|
SerializedRecentOpen::TextThread(file_name) => Some(
|
||||||
HistoryEntryId::TextThread(contexts_dir().join(file_name).into()),
|
HistoryEntryId::TextThread(contexts_dir().join(file_name).into()),
|
||||||
),
|
),
|
||||||
SerializedRecentOpen::Context(path) => {
|
|
||||||
Path::new(&path).file_name().map(|file_name| {
|
|
||||||
HistoryEntryId::TextThread(contexts_dir().join(file_name).into())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect();
|
||||||
Ok(entries)
|
Ok(entries)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1414,7 +1414,7 @@ async fn test_agent_connection(cx: &mut TestAppContext) {
|
||||||
let project = Project::test(fake_fs.clone(), [Path::new("/test")], cx).await;
|
let project = Project::test(fake_fs.clone(), [Path::new("/test")], cx).await;
|
||||||
let cwd = Path::new("/test");
|
let cwd = Path::new("/test");
|
||||||
let context_store = cx.new(|cx| assistant_context::ContextStore::fake(project.clone(), cx));
|
let context_store = cx.new(|cx| assistant_context::ContextStore::fake(project.clone(), cx));
|
||||||
let history_store = cx.new(|cx| HistoryStore::new(context_store, [], cx));
|
let history_store = cx.new(|cx| HistoryStore::new(context_store, cx));
|
||||||
|
|
||||||
// Create agent and connection
|
// Create agent and connection
|
||||||
let agent = NativeAgent::new(
|
let agent = NativeAgent::new(
|
||||||
|
|
|
@ -9,7 +9,7 @@ use agent::{TextThreadStore, ThreadStore};
|
||||||
use agent_client_protocol::{self as acp};
|
use agent_client_protocol::{self as acp};
|
||||||
use agent_servers::{AgentServer, ClaudeCode};
|
use agent_servers::{AgentServer, ClaudeCode};
|
||||||
use agent_settings::{AgentProfileId, AgentSettings, CompletionMode, NotifyWhenAgentWaiting};
|
use agent_settings::{AgentProfileId, AgentSettings, CompletionMode, NotifyWhenAgentWaiting};
|
||||||
use agent2::DbThreadMetadata;
|
use agent2::{DbThreadMetadata, HistoryEntryId, HistoryStore};
|
||||||
use anyhow::bail;
|
use anyhow::bail;
|
||||||
use audio::{Audio, Sound};
|
use audio::{Audio, Sound};
|
||||||
use buffer_diff::BufferDiff;
|
use buffer_diff::BufferDiff;
|
||||||
|
@ -111,6 +111,7 @@ pub struct AcpThreadView {
|
||||||
workspace: WeakEntity<Workspace>,
|
workspace: WeakEntity<Workspace>,
|
||||||
project: Entity<Project>,
|
project: Entity<Project>,
|
||||||
thread_state: ThreadState,
|
thread_state: ThreadState,
|
||||||
|
history_store: Entity<HistoryStore>,
|
||||||
entry_view_state: Entity<EntryViewState>,
|
entry_view_state: Entity<EntryViewState>,
|
||||||
message_editor: Entity<MessageEditor>,
|
message_editor: Entity<MessageEditor>,
|
||||||
model_selector: Option<Entity<AcpModelSelectorPopover>>,
|
model_selector: Option<Entity<AcpModelSelectorPopover>>,
|
||||||
|
@ -159,6 +160,7 @@ impl AcpThreadView {
|
||||||
resume_thread: Option<DbThreadMetadata>,
|
resume_thread: Option<DbThreadMetadata>,
|
||||||
workspace: WeakEntity<Workspace>,
|
workspace: WeakEntity<Workspace>,
|
||||||
project: Entity<Project>,
|
project: Entity<Project>,
|
||||||
|
history_store: Entity<HistoryStore>,
|
||||||
thread_store: Entity<ThreadStore>,
|
thread_store: Entity<ThreadStore>,
|
||||||
text_thread_store: Entity<TextThreadStore>,
|
text_thread_store: Entity<TextThreadStore>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
|
@ -223,6 +225,7 @@ impl AcpThreadView {
|
||||||
plan_expanded: false,
|
plan_expanded: false,
|
||||||
editor_expanded: false,
|
editor_expanded: false,
|
||||||
terminal_expanded: true,
|
terminal_expanded: true,
|
||||||
|
history_store,
|
||||||
_subscriptions: subscriptions,
|
_subscriptions: subscriptions,
|
||||||
_cancel_task: None,
|
_cancel_task: None,
|
||||||
}
|
}
|
||||||
|
@ -260,7 +263,7 @@ impl AcpThreadView {
|
||||||
let result = if let Some(native_agent) = connection
|
let result = if let Some(native_agent) = connection
|
||||||
.clone()
|
.clone()
|
||||||
.downcast::<agent2::NativeAgentConnection>()
|
.downcast::<agent2::NativeAgentConnection>()
|
||||||
&& let Some(resume) = resume_thread
|
&& let Some(resume) = resume_thread.clone()
|
||||||
{
|
{
|
||||||
cx.update(|_, cx| {
|
cx.update(|_, cx| {
|
||||||
native_agent
|
native_agent
|
||||||
|
@ -313,6 +316,15 @@ impl AcpThreadView {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if let Some(resume) = resume_thread {
|
||||||
|
this.history_store.update(cx, |history, cx| {
|
||||||
|
history.push_recently_opened_entry(
|
||||||
|
HistoryEntryId::AcpThread(resume.id),
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
AgentDiff::set_active_thread(&workspace, thread.clone(), window, cx);
|
AgentDiff::set_active_thread(&workspace, thread.clone(), window, cx);
|
||||||
|
|
||||||
this.model_selector =
|
this.model_selector =
|
||||||
|
@ -555,9 +567,15 @@ impl AcpThreadView {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
fn send(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
if let Some(thread) = self.thread()
|
let Some(thread) = self.thread() else { return };
|
||||||
&& thread.read(cx).status() != ThreadStatus::Idle
|
self.history_store.update(cx, |history, cx| {
|
||||||
{
|
history.push_recently_opened_entry(
|
||||||
|
HistoryEntryId::AcpThread(thread.read(cx).session_id().clone()),
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
if thread.read(cx).status() != ThreadStatus::Idle {
|
||||||
self.stop_current_and_send_new_message(window, cx);
|
self.stop_current_and_send_new_message(window, cx);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -3942,6 +3960,7 @@ pub(crate) mod tests {
|
||||||
use acp_thread::StubAgentConnection;
|
use acp_thread::StubAgentConnection;
|
||||||
use agent::{TextThreadStore, ThreadStore};
|
use agent::{TextThreadStore, ThreadStore};
|
||||||
use agent_client_protocol::SessionId;
|
use agent_client_protocol::SessionId;
|
||||||
|
use assistant_context::ContextStore;
|
||||||
use editor::EditorSettings;
|
use editor::EditorSettings;
|
||||||
use fs::FakeFs;
|
use fs::FakeFs;
|
||||||
use gpui::{EventEmitter, SemanticVersion, TestAppContext, VisualTestContext};
|
use gpui::{EventEmitter, SemanticVersion, TestAppContext, VisualTestContext};
|
||||||
|
@ -4079,6 +4098,10 @@ pub(crate) mod tests {
|
||||||
cx.update(|_window, cx| cx.new(|cx| ThreadStore::fake(project.clone(), cx)));
|
cx.update(|_window, cx| cx.new(|cx| ThreadStore::fake(project.clone(), cx)));
|
||||||
let text_thread_store =
|
let text_thread_store =
|
||||||
cx.update(|_window, cx| cx.new(|cx| TextThreadStore::fake(project.clone(), cx)));
|
cx.update(|_window, cx| cx.new(|cx| TextThreadStore::fake(project.clone(), cx)));
|
||||||
|
let context_store =
|
||||||
|
cx.update(|_window, cx| cx.new(|cx| ContextStore::fake(project.clone(), cx)));
|
||||||
|
let history_store =
|
||||||
|
cx.update(|_window, cx| cx.new(|cx| HistoryStore::new(context_store, cx)));
|
||||||
|
|
||||||
let thread_view = cx.update(|window, cx| {
|
let thread_view = cx.update(|window, cx| {
|
||||||
cx.new(|cx| {
|
cx.new(|cx| {
|
||||||
|
@ -4087,6 +4110,7 @@ pub(crate) mod tests {
|
||||||
None,
|
None,
|
||||||
workspace.downgrade(),
|
workspace.downgrade(),
|
||||||
project,
|
project,
|
||||||
|
history_store,
|
||||||
thread_store.clone(),
|
thread_store.clone(),
|
||||||
text_thread_store.clone(),
|
text_thread_store.clone(),
|
||||||
window,
|
window,
|
||||||
|
@ -4283,6 +4307,10 @@ pub(crate) mod tests {
|
||||||
cx.update(|_window, cx| cx.new(|cx| ThreadStore::fake(project.clone(), cx)));
|
cx.update(|_window, cx| cx.new(|cx| ThreadStore::fake(project.clone(), cx)));
|
||||||
let text_thread_store =
|
let text_thread_store =
|
||||||
cx.update(|_window, cx| cx.new(|cx| TextThreadStore::fake(project.clone(), cx)));
|
cx.update(|_window, cx| cx.new(|cx| TextThreadStore::fake(project.clone(), cx)));
|
||||||
|
let context_store =
|
||||||
|
cx.update(|_window, cx| cx.new(|cx| ContextStore::fake(project.clone(), cx)));
|
||||||
|
let history_store =
|
||||||
|
cx.update(|_window, cx| cx.new(|cx| HistoryStore::new(context_store, cx)));
|
||||||
|
|
||||||
let connection = Rc::new(StubAgentConnection::new());
|
let connection = Rc::new(StubAgentConnection::new());
|
||||||
let thread_view = cx.update(|window, cx| {
|
let thread_view = cx.update(|window, cx| {
|
||||||
|
@ -4292,6 +4320,7 @@ pub(crate) mod tests {
|
||||||
None,
|
None,
|
||||||
workspace.downgrade(),
|
workspace.downgrade(),
|
||||||
project.clone(),
|
project.clone(),
|
||||||
|
history_store.clone(),
|
||||||
thread_store.clone(),
|
thread_store.clone(),
|
||||||
text_thread_store.clone(),
|
text_thread_store.clone(),
|
||||||
window,
|
window,
|
||||||
|
|
|
@ -648,8 +648,7 @@ impl AgentPanel {
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
let acp_history_store =
|
let acp_history_store = cx.new(|cx| agent2::HistoryStore::new(context_store.clone(), cx));
|
||||||
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 acp_history = cx.new(|cx| AcpThreadHistory::new(acp_history_store.clone(), window, cx));
|
||||||
cx.subscribe_in(
|
cx.subscribe_in(
|
||||||
&acp_history,
|
&acp_history,
|
||||||
|
@ -1073,6 +1072,7 @@ impl AgentPanel {
|
||||||
resume_thread,
|
resume_thread,
|
||||||
workspace.clone(),
|
workspace.clone(),
|
||||||
project,
|
project,
|
||||||
|
this.acp_history_store.clone(),
|
||||||
thread_store.clone(),
|
thread_store.clone(),
|
||||||
text_thread_store.clone(),
|
text_thread_store.clone(),
|
||||||
window,
|
window,
|
||||||
|
@ -1609,6 +1609,14 @@ impl AgentPanel {
|
||||||
if let Some(path) = context_editor.read(cx).context().read(cx).path() {
|
if let Some(path) = context_editor.read(cx).context().read(cx).path() {
|
||||||
store.push_recently_opened_entry(HistoryEntryId::Context(path.clone()), cx)
|
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()),
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
ActiveView::ExternalAgentThread { .. } => {}
|
ActiveView::ExternalAgentThread { .. } => {}
|
||||||
|
@ -2763,9 +2771,12 @@ impl AgentPanel {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let history_is_empty = self
|
let history_is_empty = if cx.has_flag::<AcpFeatureFlag>() {
|
||||||
.history_store
|
self.acp_history_store.read(cx).is_empty(cx)
|
||||||
.update(cx, |store, cx| store.recent_entries(1, cx).is_empty());
|
} else {
|
||||||
|
self.history_store
|
||||||
|
.update(cx, |store, cx| store.recent_entries(1, cx).is_empty())
|
||||||
|
};
|
||||||
|
|
||||||
let has_configured_non_zed_providers = LanguageModelRegistry::read_global(cx)
|
let has_configured_non_zed_providers = LanguageModelRegistry::read_global(cx)
|
||||||
.providers()
|
.providers()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue