diff --git a/autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatContainer/useAutoOpenArtifacts.test.ts b/autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatContainer/useAutoOpenArtifacts.test.ts index 8ff3046d55..0b730b29ac 100644 --- a/autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatContainer/useAutoOpenArtifacts.test.ts +++ b/autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatContainer/useAutoOpenArtifacts.test.ts @@ -88,4 +88,53 @@ describe("useAutoOpenArtifacts", () => { expect(s.activeArtifact?.id).toBe("c"); expect(s.history).toEqual([]); }); + + // SECRT-2254: "had agent panel open then went to profile then went to home + // and agent panel was still open". Nav-away unmounts the copilot page; if + // the panel state persists in the store, coming back re-renders it open. + it("closes the panel on unmount so nav-away → nav-back doesn't resurrect it (SECRT-2254)", () => { + useCopilotUIStore.getState().openArtifact(makeArtifact(A_ID, "a.txt")); + expect(useCopilotUIStore.getState().artifactPanel.isOpen).toBe(true); + + const { unmount } = renderHook(() => + useAutoOpenArtifacts({ sessionId: "s1" }), + ); + + act(() => { + unmount(); + }); + + const s = useCopilotUIStore.getState().artifactPanel; + expect(s.isOpen).toBe(false); + expect(s.activeArtifact).toBeNull(); + expect(s.history).toEqual([]); + }); + + // SECRT-2220: "keep closed by default" — a fresh mount (e.g. user returns to + // /copilot) must start with a closed panel even if the store somehow carries + // stale state from a prior life. + it("does not re-open a panel whose store state is stale on fresh mount (SECRT-2220)", () => { + // Simulate the store being left in an open state by a previous page life. + useCopilotUIStore.setState({ + artifactPanel: { + isOpen: true, + isMinimized: false, + isMaximized: false, + width: 600, + activeArtifact: makeArtifact(A_ID, "stale.txt"), + history: [], + }, + }); + + const { unmount } = renderHook(() => + useAutoOpenArtifacts({ sessionId: "s1" }), + ); + act(() => { + unmount(); + }); + + // Next mount of the page should see a clean store. + renderHook(() => useAutoOpenArtifacts({ sessionId: "s1" })); + expect(useCopilotUIStore.getState().artifactPanel.isOpen).toBe(false); + }); }); diff --git a/autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatContainer/useAutoOpenArtifacts.ts b/autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatContainer/useAutoOpenArtifacts.ts index a8b867009c..04ef7d2631 100644 --- a/autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatContainer/useAutoOpenArtifacts.ts +++ b/autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatContainer/useAutoOpenArtifacts.ts @@ -26,4 +26,13 @@ export function useAutoOpenArtifacts({ resetArtifactPanel(); } }, [sessionId, resetArtifactPanel]); + + // Reset on unmount so navigating away from /copilot (to /profile, /home, + // etc.) can't leave the panel open in the Zustand store, which would then + // render the panel re-open when the user returns. See SECRT-2254/2220. + useEffect(() => { + return () => { + resetArtifactPanel(); + }; + }, [resetArtifactPanel]); }