diff --git a/site/src/pages/AgentsPage/AgentsPage.tsx b/site/src/pages/AgentsPage/AgentsPage.tsx index 8a3be3c1a1..6fc13be1ba 100644 --- a/site/src/pages/AgentsPage/AgentsPage.tsx +++ b/site/src/pages/AgentsPage/AgentsPage.tsx @@ -33,7 +33,10 @@ import { emptyInputStorageKey } from "./components/AgentCreateForm"; import { maybePlayChime } from "./components/AgentDetail/useAgentChime"; import { useAgentsPageKeybindings } from "./hooks/useAgentsPageKeybindings"; import { useAgentsPWA } from "./hooks/useAgentsPWA"; -import { resolveArchiveAndDeleteAction } from "./utils/agentWorkspaceUtils"; +import { + resolveArchiveAndDeleteAction, + shouldNavigateAfterArchive, +} from "./utils/agentWorkspaceUtils"; import { getModelOptionsFromCatalog } from "./utils/modelOptions"; import type { ChatDetailError } from "./utils/usageLimitMessage"; @@ -262,7 +265,27 @@ const AgentsPage: FC = () => { archiveAndDeleteMutation.mutate( { chatId, workspaceId }, { - onSettled: () => navigate("/agents"), + onSettled: () => { + const activeChatId = activeChatIDRef.current; + if ( + shouldNavigateAfterArchive( + activeChatId, + chatId, + // Read root_chat_id from the per-chat + // cache, which survives WebSocket eviction + // of sub-agents (only the parent's chatKey + // is removed). Must be read at settle time + // so it reflects the user's current location. + activeChatId + ? queryClient.getQueryData( + chatKey(activeChatId), + )?.root_chat_id + : undefined, + ) + ) { + navigate("/agents"); + } + }, }, ); } else { @@ -274,10 +297,23 @@ const AgentsPage: FC = () => { }; const handleConfirmArchiveAndDelete = () => { if (pendingArchiveAndDelete && !isArchiving) { + const { chatId: archivedChatId } = pendingArchiveAndDelete; archiveAndDeleteMutation.mutate(pendingArchiveAndDelete, { onSettled: () => { setPendingArchiveAndDelete(null); - navigate("/agents"); + const activeChatId = activeChatIDRef.current; + if ( + shouldNavigateAfterArchive( + activeChatId, + archivedChatId, + activeChatId + ? queryClient.getQueryData(chatKey(activeChatId)) + ?.root_chat_id + : undefined, + ) + ) { + navigate("/agents"); + } }, }); } diff --git a/site/src/pages/AgentsPage/utils/agentWorkspaceUtils.test.ts b/site/src/pages/AgentsPage/utils/agentWorkspaceUtils.test.ts index b7b342e604..3ba42b31b8 100644 --- a/site/src/pages/AgentsPage/utils/agentWorkspaceUtils.test.ts +++ b/site/src/pages/AgentsPage/utils/agentWorkspaceUtils.test.ts @@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest"; import { isWorkspaceAutoCreated, resolveArchiveAndDeleteAction, + shouldNavigateAfterArchive, } from "./agentWorkspaceUtils"; describe("isWorkspaceAutoCreated", () => { @@ -80,3 +81,58 @@ describe("resolveArchiveAndDeleteAction", () => { ).rejects.toThrow("not found"); }); }); + +describe("shouldNavigateAfterArchive", () => { + it.each([ + { + name: "user still viewing archived chat", + activeChatId: "abc-123", + archivedChatId: "abc-123", + expected: true, + }, + { + name: "user navigated to a different chat", + activeChatId: "xyz-456", + archivedChatId: "abc-123", + expected: false, + }, + { + name: "user navigated to /agents root (no active chat)", + activeChatId: undefined, + archivedChatId: "abc-123", + expected: false, + }, + { + name: "user viewing sub-agent of archived parent", + activeChatId: "sub-agent-1", + archivedChatId: "parent-abc", + activeRootChatId: "parent-abc", + expected: true, + }, + { + name: "user viewing sub-agent of a different parent", + activeChatId: "sub-agent-1", + archivedChatId: "parent-abc", + activeRootChatId: "parent-other", + expected: false, + }, + { + name: "root chat ID not available (cache cleared)", + activeChatId: "sub-agent-1", + archivedChatId: "parent-abc", + activeRootChatId: undefined, + expected: false, + }, + ])( + "$name → $expected", + ({ activeChatId, archivedChatId, activeRootChatId, expected }) => { + expect( + shouldNavigateAfterArchive( + activeChatId, + archivedChatId, + activeRootChatId, + ), + ).toBe(expected); + }, + ); +}); diff --git a/site/src/pages/AgentsPage/utils/agentWorkspaceUtils.ts b/site/src/pages/AgentsPage/utils/agentWorkspaceUtils.ts index 00c0476b8b..a8377c64f4 100644 --- a/site/src/pages/AgentsPage/utils/agentWorkspaceUtils.ts +++ b/site/src/pages/AgentsPage/utils/agentWorkspaceUtils.ts @@ -12,6 +12,26 @@ export function isWorkspaceAutoCreated( return new Date(workspaceCreatedAt) >= new Date(chatCreatedAt); } +/** + * Returns whether the browser should navigate to /agents after an + * archive-and-delete mutation settles. Navigation is appropriate + * when the user is still viewing the archived chat or one of its + * sub-agents; if they already navigated elsewhere the redirect + * would be disruptive. + */ +export function shouldNavigateAfterArchive( + activeChatId: string | undefined, + archivedChatId: string, + activeRootChatId?: string, +): boolean { + if (activeChatId === archivedChatId) return true; + // The active chat is a sub-agent rooted at the archived parent. + if (activeRootChatId != null && activeRootChatId === archivedChatId) { + return true; + } + return false; +} + /** * Resolves whether an archive-and-delete action should proceed * immediately or require user confirmation. Fetches the workspace