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.
This commit is contained in:
paseo-bot[bot]
2026-11-06 06:31:46 +00:00
parent 15c8795cc6
commit 47691eee91
4 changed files with 33 additions and 4 deletions
@@ -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);
@@ -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();
}
@@ -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(
+8 -4
View File
@@ -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) => {