Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions src/browser/components/AIView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -190,14 +190,17 @@ const AIViewInner: React.FC<AIViewProps> = ({
// 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({
api,
workspaceId,
sendMessageOptions: pendingSendOptions,
continueMessage: { text: "Continue with the current task" },
continueMessage: { text: "Continue" },
});
}, [api, workspaceId, pendingSendOptions]);

Expand Down
29 changes: 29 additions & 0 deletions src/browser/utils/chatCommands.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down
23 changes: 18 additions & 5 deletions src/browser/utils/chatCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -627,7 +627,23 @@ 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;

// 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" &&
!hasImages &&
!hasReviews;

if (options.continueMessage && !isDefaultResume) {
messageText += `\n\nThe user wants to continue with: ${options.continueMessage.text}`;
}

Expand All @@ -636,10 +652,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,
Expand Down