agent_ui: Skip restoring external threads without metadata (#52978)
Make sure we don't try and load a thread we don't have metadata for and therefore is unlikely to exist Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - N/A
This commit is contained in:
1 parent
f31bfc9173
commit
3dec65d9a9
1 file changed
+83
-21
@@ -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 {
|
||||
|
||||
Reference in new issue
Block a user