From bfc0a3dd361458ed3b8d05f01a6903f65eb8d09c Mon Sep 17 00:00:00 2001 From: Ammar Date: Sat, 13 Dec 2025 20:24:42 -0600 Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=A4=96=20fix:=20treat=20default=20Con?= =?UTF-8?q?tinue=20as=20resume=20control?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/components/AIView.tsx | 2 +- src/browser/utils/chatCommands.test.ts | 29 ++++++++++++++++++++++++++ src/browser/utils/chatCommands.ts | 21 ++++++++++++++----- 3 files changed, 46 insertions(+), 6 deletions(-) diff --git a/src/browser/components/AIView.tsx b/src/browser/components/AIView.tsx index 728c77ad82..97dd8e9927 100644 --- a/src/browser/components/AIView.tsx +++ b/src/browser/components/AIView.tsx @@ -197,7 +197,7 @@ const AIViewInner: React.FC = ({ api, workspaceId, sendMessageOptions: pendingSendOptions, - continueMessage: { text: "Continue with the current task" }, + continueMessage: { text: "Continue" }, }); }, [api, workspaceId, pendingSendOptions]); diff --git a/src/browser/utils/chatCommands.test.ts b/src/browser/utils/chatCommands.test.ts index 9b79ec9530..8c10a954f9 100644 --- a/src/browser/utils/chatCommands.test.ts +++ b/src/browser/utils/chatCommands.test.ts @@ -166,6 +166,35 @@ describe("prepareCompactionMessage", () => { expect(metadata.parsed.continueMessage?.text).toBe("Continue with this"); }); + test("omits default resume text from compaction prompt", () => { + const sendMessageOptions = createBaseOptions(); + const { messageText, metadata } = prepareCompactionMessage({ + workspaceId: "ws-1", + continueMessage: { text: "Continue" }, + sendMessageOptions, + }); + + expect(messageText).not.toContain("The user wants to continue with: Continue"); + + if (metadata.type !== "compaction-request") { + throw new Error("Expected compaction metadata"); + } + + // Still queued for auto-send after compaction + expect(metadata.parsed.continueMessage?.text).toBe("Continue"); + }); + + test("includes non-default continue text in compaction prompt", () => { + const sendMessageOptions = createBaseOptions(); + const { messageText } = prepareCompactionMessage({ + workspaceId: "ws-1", + continueMessage: { text: "fix tests" }, + sendMessageOptions, + }); + + expect(messageText).toContain("The user wants to continue with: fix tests"); + }); + test("creates continueMessage when images are provided without text", () => { const sendMessageOptions = createBaseOptions(); const { metadata } = prepareCompactionMessage({ diff --git a/src/browser/utils/chatCommands.ts b/src/browser/utils/chatCommands.ts index d86cfa68c5..8f2c49324c 100644 --- a/src/browser/utils/chatCommands.ts +++ b/src/browser/utils/chatCommands.ts @@ -627,7 +627,21 @@ export function prepareCompactionMessage(options: CompactionOptions): { // Build compaction message with optional continue context let messageText = buildCompactionPrompt(targetWords); - if (options.continueMessage) { + const continueText = options.continueMessage?.text; + const hasImages = + options.continueMessage?.imageParts && options.continueMessage.imageParts.length > 0; + const hasReviews = options.continueMessage?.reviews && options.continueMessage.reviews.length > 0; + + // When the user didn't supply a follow-up message (we inject a default "Continue"), + // avoid polluting the compaction prompt with that control text. + // We still keep continueMessage in metadata so it will be auto-sent after compaction. + const isDefaultResume = + typeof continueText === "string" && + continueText.trim() === "Continue" && + !hasImages && + !hasReviews; + + if (options.continueMessage && !isDefaultResume) { messageText += `\n\nThe user wants to continue with: ${options.continueMessage.text}`; } @@ -636,10 +650,7 @@ export function prepareCompactionMessage(options: CompactionOptions): { // Create compaction metadata (will be stored in user message) // Only include continueMessage if there's text, images, or reviews to queue after compaction - const hasText = options.continueMessage?.text; - const hasImages = - options.continueMessage?.imageParts && options.continueMessage.imageParts.length > 0; - const hasReviews = options.continueMessage?.reviews && options.continueMessage.reviews.length > 0; + const hasText = continueText; const compactData: CompactionRequestData = { model: effectiveModel, maxOutputTokens: options.maxOutputTokens, From d7b1248d12503ba44a8865e609aec89c2628ac50 Mon Sep 17 00:00:00 2001 From: Ammar Date: Sat, 13 Dec 2025 20:30:13 -0600 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=A4=96=20docs:=20clarify=20compaction?= =?UTF-8?q?=20resume=20sentinel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/browser/components/AIView.tsx | 5 ++++- src/browser/utils/chatCommands.ts | 8 +++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/browser/components/AIView.tsx b/src/browser/components/AIView.tsx index 97dd8e9927..c7078bd27b 100644 --- a/src/browser/components/AIView.tsx +++ b/src/browser/components/AIView.tsx @@ -190,7 +190,10 @@ const AIViewInner: React.FC = ({ // Show warning when: shouldShowWarning flag is true AND not currently compacting const shouldShowCompactionWarning = !isCompacting && autoCompactionResult.shouldShowWarning; - // Handle force compaction callback - memoized to avoid effect re-runs + // Handle force compaction callback - memoized to avoid effect re-runs. + // We pass a default continueMessage of "Continue" as a resume sentinel so the backend can + // auto-send it after compaction. The compaction prompt builder special-cases this sentinel + // to avoid injecting it into the summarization request. const handleForceCompaction = useCallback(() => { if (!api) return; void executeCompaction({ diff --git a/src/browser/utils/chatCommands.ts b/src/browser/utils/chatCommands.ts index 8f2c49324c..0d2f925d54 100644 --- a/src/browser/utils/chatCommands.ts +++ b/src/browser/utils/chatCommands.ts @@ -632,9 +632,11 @@ export function prepareCompactionMessage(options: CompactionOptions): { options.continueMessage?.imageParts && options.continueMessage.imageParts.length > 0; const hasReviews = options.continueMessage?.reviews && options.continueMessage.reviews.length > 0; - // When the user didn't supply a follow-up message (we inject a default "Continue"), - // avoid polluting the compaction prompt with that control text. - // We still keep continueMessage in metadata so it will be auto-sent after compaction. + // continueMessage is a follow-up user message that will be auto-sent after compaction. + // For forced compaction (no explicit follow-up), we inject a short resume sentinel ("Continue"). + // Keep that sentinel out of the *compaction prompt* (summarization request), otherwise the model can + // misread it as a competing instruction. We still keep it in metadata so the backend resumes. + // Only treat it as the default resume when there's no other queued content (images/reviews). const isDefaultResume = typeof continueText === "string" && continueText.trim() === "Continue" &&