From 81218ba7a4bd763a946f79aa2358cb11a4dd4603 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Sat, 11 Jul 2026 13:39:01 +0200 Subject: [PATCH] fix(app): preserve History agent unarchive --- .../app/src/utils/navigate-to-agent/index.ts | 22 +++++++++++++++---- .../restore-archived-workspace.test.ts | 19 +++++++++++++++- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/packages/app/src/utils/navigate-to-agent/index.ts b/packages/app/src/utils/navigate-to-agent/index.ts index faf1afb042..c9a280ee90 100644 --- a/packages/app/src/utils/navigate-to-agent/index.ts +++ b/packages/app/src/utils/navigate-to-agent/index.ts @@ -64,10 +64,7 @@ function restoreArchivedWorkspace(serverId: string, agentId: string, workspaceId const store = useSessionStore.getState(); const session = store.sessions[serverId]; - // History carries restore intent explicitly. Workspace lifecycle is separate - // from agent lifecycle, so a closed non-archived agent can legitimately own an - // archived workspace. A present workspace or in-flight restore stays a no-op. - if (!session || session.workspaces.has(workspaceId)) { + if (!session) { return; } if (session.restoringWorkspaces.get(workspaceId) === "restoring") { @@ -81,6 +78,23 @@ function restoreArchivedWorkspace(serverId: string, agentId: string, workspaceId return; } + // Workspace and agent archive lifecycles are independent. A missing workspace + // must restore even when its agent survived unarchived; an archived agent must + // still reopen when its workspace survived. + if (session.workspaces.has(workspaceId)) { + const agent = session.agents.get(agentId) ?? session.agentDetails.get(agentId); + if (agent?.archivedAt) { + client.refreshAgent(agentId).catch((error) => { + console.error("[HistoryRestore] Failed to reopen archived agent", { + serverId, + agentId, + error, + }); + }); + } + return; + } + // COMPAT(worktreeRestore): added in v0.1.97, drop the gate when floor >= v0.1.97 // Single capability read for restore. An old daemon recreates nothing on // refresh_agent, so a gone directory would spin then flash a misleading diff --git a/packages/app/src/utils/navigate-to-agent/restore-archived-workspace.test.ts b/packages/app/src/utils/navigate-to-agent/restore-archived-workspace.test.ts index 845a330e6a..5b6659a154 100644 --- a/packages/app/src/utils/navigate-to-agent/restore-archived-workspace.test.ts +++ b/packages/app/src/utils/navigate-to-agent/restore-archived-workspace.test.ts @@ -269,12 +269,29 @@ describe("restoreArchivedWorkspace via navigateToAgent", () => { expect(status()).toBe("needs-host-upgrade"); }); - it("is a no-op when the workspace descriptor is already present", () => { + it("reopens an archived History agent when its workspace descriptor is already present", () => { refreshAgent.mockImplementation(() => new Promise(() => {})); useSessionStore.getState().mergeWorkspaces(SERVER_ID, [workspace()]); openFromHistory(); + expect(refreshAgent).toHaveBeenCalledTimes(1); + expect(refreshAgent).toHaveBeenCalledWith(AGENT_ID); + expect(status()).toBeNull(); + }); + + it("is a no-op when a non-archived workspace descriptor is already present", () => { + const store = useSessionStore.getState(); + store.setAgents(SERVER_ID, (prev) => { + const next = new Map(prev); + next.set(AGENT_ID, agent(null)); + return next; + }); + refreshAgent.mockImplementation(() => new Promise(() => {})); + store.mergeWorkspaces(SERVER_ID, [workspace()]); + + openFromHistory(); + expect(refreshAgent).not.toHaveBeenCalled(); }); });