From 47691eee91ced308730a4b28e1e4236330b5828b Mon Sep 17 00:00:00 2001 From: "paseo-bot[bot]" <266920839+paseo-bot[bot]@users.noreply.github.com> Date: Fri, 6 Nov 2026 06:31:46 +0000 Subject: [PATCH] fix(app): keep rewound history in its own chat A restored fork snapshot belongs to the rewound agent. Give it an agent attachment scope so switching chats cannot send or consume that context elsewhere in the workspace. --- .../app/e2e/browser/assistant-fork-menu.spec.ts | 2 ++ packages/app/e2e/support/helpers/chat-history.ts | 10 ++++++++++ .../src/attachments/workspace-attachments-store.ts | 13 +++++++++++++ packages/app/src/panels/agent-panel.tsx | 12 ++++++++---- 4 files changed, 33 insertions(+), 4 deletions(-) diff --git a/packages/app/e2e/browser/assistant-fork-menu.spec.ts b/packages/app/e2e/browser/assistant-fork-menu.spec.ts index 0ec214eb48..cafc59d065 100644 --- a/packages/app/e2e/browser/assistant-fork-menu.spec.ts +++ b/packages/app/e2e/browser/assistant-fork-menu.spec.ts @@ -1,5 +1,6 @@ import { captureChatHistory, + expectRewoundHistoryIsPrivate, rewindForkPrompt, closeChatHistory, followChatHistorySource, @@ -113,6 +114,7 @@ test.describe("Assistant fork menu", () => { await openChatHistory(page, "sent", [initialPrompt]); await closeChatHistory(page); await rewindForkPrompt(page); + await expectRewoundHistoryIsPrivate(page, session.agentId, forkId); await openChatHistory(page, "draft", [initialPrompt]); await closeChatHistory(page); await submitMessage(page, prompt); diff --git a/packages/app/e2e/support/helpers/chat-history.ts b/packages/app/e2e/support/helpers/chat-history.ts index 99b49ba6ac..cf12316fe1 100644 --- a/packages/app/e2e/support/helpers/chat-history.ts +++ b/packages/app/e2e/support/helpers/chat-history.ts @@ -70,3 +70,13 @@ export async function rewindForkPrompt(page: Page) { await option.click(); await expect(option).not.toBeVisible(); } + +export async function expectRewoundHistoryIsPrivate(page: Page, sourceId: string, forkId: string) { + await page.getByTestId(`workspace-tab-agent_${sourceId}`).click(); + const history = page + .getByTestId("composer-chat-history-attachment-pill") + .filter({ visible: true }); + await expect(history).toHaveCount(0); + await page.getByTestId(`workspace-tab-agent_${forkId}`).click(); + await expect(history).toBeVisible(); +} diff --git a/packages/app/src/attachments/workspace-attachments-store.ts b/packages/app/src/attachments/workspace-attachments-store.ts index ea5b557208..8609cb8c02 100644 --- a/packages/app/src/attachments/workspace-attachments-store.ts +++ b/packages/app/src/attachments/workspace-attachments-store.ts @@ -178,6 +178,19 @@ export function useWorkspaceAttachmentScopeKey(input: WorkspaceAttachmentScope): }, [cwd, draftId, isDraftScope, serverId, workspaceId]); } +export function useAgentAttachmentScopeKey(input: { serverId: string; agentId: string }): string { + const { serverId, agentId } = input; + return useMemo( + () => + [ + "agent-attachments", + `server=${encodeScopePart(serverId)}`, + `agent=${encodeScopePart(agentId)}`, + ].join(":"), + [serverId, agentId], + ); +} + export function useDraftWorkspaceAttachmentScopeKey(draftId: string | null | undefined): string { const normalizedDraftId = useMemo(() => draftId?.trim() ?? "", [draftId]); return useMemo( diff --git a/packages/app/src/panels/agent-panel.tsx b/packages/app/src/panels/agent-panel.tsx index 48dd5ff89c..a29a18f69e 100644 --- a/packages/app/src/panels/agent-panel.tsx +++ b/packages/app/src/panels/agent-panel.tsx @@ -43,7 +43,10 @@ import { RewindComposerRestoreProvider } from "@/components/rewind/composer-rest import { useAgentDescriptor } from "@/agent-presentation"; import { useToastHost, type ToastApi, type ToastState } from "@/components/toast-host"; import type { WorkspaceComposerAttachment } from "@/attachments/types"; -import { useWorkspaceAttachmentScopeKey } from "@/attachments/workspace-attachments-store"; +import { + useWorkspaceAttachmentScopeKey, + useAgentAttachmentScopeKey, +} from "@/attachments/workspace-attachments-store"; import { COMPACT_CONTENT_WIDTH, useIsCompactFormFactor } from "@/constants/layout"; import { isNative, isWeb } from "@/constants/platform"; import { useAgentAttentionClear } from "@/hooks/use-agent-attention-clear"; @@ -1112,7 +1115,7 @@ const ChatAgentReadyContent = memo(function ChatAgentReadyContent({ const parentAgentId = useParentAgentId(serverId, agentId); // With a composer the parent is a pill above it; without one, the box takes the composer's place. const parentPillAgentId = showsComposer ? parentAgentId : null; - const rewindAttachmentScopeKey = useWorkspaceAttachmentScopeKey({ serverId, workspaceId, cwd }); + const rewindAttachmentScopeKey = useAgentAttachmentScopeKey({ serverId, agentId }); const rawAgentInputDraft = useAgentInputDraft({ draftKey: buildDraftStoreKey({ serverId, @@ -1543,9 +1546,10 @@ function ActiveAgentComposer({ cwd, workspaceId, }); + const agentAttachmentScopeKey = useAgentAttachmentScopeKey({ serverId, agentId }); const attachmentScopeKeys = useMemo( - () => [workspaceAttachmentScopeKey], - [workspaceAttachmentScopeKey], + () => [workspaceAttachmentScopeKey, agentAttachmentScopeKey], + [workspaceAttachmentScopeKey, agentAttachmentScopeKey], ); const handleOpenWorkspaceAttachment = useCallback( (attachment: WorkspaceComposerAttachment) => {