@@ -25,6 +25,7 @@ use zed_actions::agent::{
ResolveConflictsWithAgent, ReviewBranchDiff,
};
+use crate::thread_metadata_store::ThreadMetadataStore;
use crate::{
AddContextServer, AgentDiffPane, ConversationView, CopyThreadToClipboard, CycleStartThreadIn,
Follow, InlineAssistant, LoadThreadFromClipboard, NewThread, OpenActiveThreadAsMarkdown,
@@ -753,28 +754,21 @@ impl AgentPanel {
.as_ref()
.and_then(|p| p.last_active_thread.as_ref())
{
- if thread_info.agent_type.is_native() {
- let session_id = acp::SessionId::new(thread_info.session_id.clone());
- let load_result = cx.update(|_window, cx| {
- let thread_store = ThreadStore::global(cx);
- thread_store.update(cx, |store, cx| store.load_thread(session_id, cx))
- });
- let thread_exists = if let Ok(task) = load_result {
- task.await.ok().flatten().is_some()
- } else {
- false
- };
- if thread_exists {
- Some(thread_info)
- } else {
- log::warn!(
- "last active thread {} not found in database, skipping restoration",
- thread_info.session_id
- );
- None
- }
- } else {
+ let session_id = acp::SessionId::new(thread_info.session_id.clone());
+ let has_metadata = cx
+ .update(|_window, cx| {
+ let store = ThreadMetadataStore::global(cx);
+ store.read(cx).entry(&session_id).is_some()
+ })
+ .unwrap_or(false);
+ if has_metadata {
Some(thread_info)
+ } else {
+ log::warn!(
+ "last active thread {} has no metadata, skipping restoration",
+ thread_info.session_id
+ );
+ None
}
} else {
None
@@ -4305,6 +4299,8 @@ mod tests {
);
});
+ send_message(&panel_a, cx);
+
let agent_type_a = panel_a.read_with(cx, |panel, _cx| panel.selected_agent.clone());
// --- Set up workspace B: ClaudeCode, no active thread ---
@@ -4364,6 +4360,72 @@ mod tests {
});
}
+ #[gpui::test]
+ async fn test_non_native_thread_without_metadata_is_not_restored(cx: &mut TestAppContext) {
+ init_test(cx);
+ cx.update(|cx| {
+ cx.update_flags(true, vec!["agent-v2".to_string()]);
+ agent::ThreadStore::init_global(cx);
+ language_model::LanguageModelRegistry::test(cx);
+ });
+
+ let fs = FakeFs::new(cx.executor());
+ let project = Project::test(fs, [], cx).await;
+
+ let multi_workspace =
+ cx.add_window(|window, cx| MultiWorkspace::test_new(project.clone(), window, cx));
+
+ let workspace = multi_workspace
+ .read_with(cx, |multi_workspace, _cx| {
+ multi_workspace.workspace().clone()
+ })
+ .unwrap();
+
+ workspace.update(cx, |workspace, _cx| {
+ workspace.set_random_database_id();
+ });
+
+ let cx = &mut VisualTestContext::from_window(multi_workspace.into(), cx);
+
+ let panel = workspace.update_in(cx, |workspace, window, cx| {
+ cx.new(|cx| AgentPanel::new(workspace, None, window, cx))
+ });
+
+ panel.update_in(cx, |panel, window, cx| {
+ panel.open_external_thread_with_server(
+ Rc::new(StubAgentServer::default_response()),
+ window,
+ cx,
+ );
+ });
+
+ cx.run_until_parked();
+
+ panel.read_with(cx, |panel, cx| {
+ assert!(
+ panel.active_agent_thread(cx).is_some(),
+ "should have an active thread after connection"
+ );
+ });
+
+ // Serialize without ever sending a message, so no thread metadata exists.
+ panel.update(cx, |panel, cx| panel.serialize(cx));
+ cx.run_until_parked();
+
+ let async_cx = cx.update(|window, cx| window.to_async(cx));
+ let loaded = AgentPanel::load(workspace.downgrade(), async_cx)
+ .await
+ .expect("panel load should succeed");
+ cx.run_until_parked();
+
+ loaded.read_with(cx, |panel, _cx| {
+ assert!(
+ panel.active_conversation_view().is_none(),
+ "thread without metadata should not be restored"
+ );
+ });
+ }
+
/// Extracts the text from a Text content block, panicking if it's not Text.
fn expect_text_block(block: &acp::ContentBlock) -> &str {
match block {