From a06741397d15f5226a9f77b22305e1b74a4b825f Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 19 Mar 2026 13:41:50 +0000 Subject: [PATCH] fix: add clear separator between metadata and user message Problem: When a user message is passed to the LLM, metadata blocks (sender info, conversation info, etc.) are joined with the actual user content using only double newlines. This causes the LLM to sometimes misinterpret the structure, treating metadata as the entire message. Solution: Insert a visible "---\n**User Message:**" separator between metadata and user content. Both sanitizer functions strip it after removing metadata blocks so it never appears in user-facing surfaces. Changes: - get-reply-run.ts: inject separator when metadata is present - strip-inbound-meta.ts: strip separator after metadata removal in both stripInboundMetadata and stripLeadingInboundMetadata - strip-inbound-meta.test.ts: add separator stripping tests --- src/auto-reply/reply/get-reply-run.ts | 4 +++- .../reply/strip-inbound-meta.test.ts | 22 +++++++++++++++++++ src/auto-reply/reply/strip-inbound-meta.ts | 13 +++++++++-- 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/auto-reply/reply/get-reply-run.ts b/src/auto-reply/reply/get-reply-run.ts index c8451fd88f6..defebe86799 100644 --- a/src/auto-reply/reply/get-reply-run.ts +++ b/src/auto-reply/reply/get-reply-run.ts @@ -307,7 +307,9 @@ export async function runPreparedReply( ); const baseBodyForPrompt = isBareSessionReset ? baseBodyFinal - : [inboundUserContext, baseBodyFinal].filter(Boolean).join("\n\n"); + : inboundUserContext + ? `${inboundUserContext}\n\n---\n**User Message:**\n${baseBodyFinal}` + : baseBodyFinal; const baseBodyTrimmed = baseBodyForPrompt.trim(); const hasMediaAttachment = Boolean( sessionCtx.MediaPath || (sessionCtx.MediaPaths && sessionCtx.MediaPaths.length > 0), diff --git a/src/auto-reply/reply/strip-inbound-meta.test.ts b/src/auto-reply/reply/strip-inbound-meta.test.ts index 9bdb20edcee..62b3c8e029c 100644 --- a/src/auto-reply/reply/strip-inbound-meta.test.ts +++ b/src/auto-reply/reply/strip-inbound-meta.test.ts @@ -146,6 +146,28 @@ Hello`; }); }); +describe("user-message separator stripping", () => { + it("strips separator after metadata removal", () => { + const input = `${CONV_BLOCK}\n\n---\n**User Message:**\nHello world`; + expect(stripInboundMetadata(input)).toBe("Hello world"); + }); + + it("strips separator with multiple metadata blocks", () => { + const input = `${CONV_BLOCK}\n\n${SENDER_BLOCK}\n\n---\n**User Message:**\nHello world`; + expect(stripInboundMetadata(input)).toBe("Hello world"); + }); + + it("preserves separator when no metadata blocks are present", () => { + const input = `---\n**User Message:**\nHello world`; + expect(stripInboundMetadata(input)).toBe(input); + }); + + it("preserves user content starting with --- when it is not the separator", () => { + const input = `${CONV_BLOCK}\n\n---\nSome regular content`; + expect(stripInboundMetadata(input)).toBe("---\nSome regular content"); + }); +}); + describe("extractInboundSenderLabel", () => { it("returns the sender label block when present", () => { const input = `${CONV_BLOCK}\n\n${SENDER_BLOCK}\n\nHello from user`; diff --git a/src/auto-reply/reply/strip-inbound-meta.ts b/src/auto-reply/reply/strip-inbound-meta.ts index 80e12a3fc20..089955bf47d 100644 --- a/src/auto-reply/reply/strip-inbound-meta.ts +++ b/src/auto-reply/reply/strip-inbound-meta.ts @@ -184,7 +184,16 @@ export function stripInboundMetadata(text: string): string { result.push(line); } - return result.join("\n").replace(/^\n+/, "").replace(/\n+$/, ""); + return stripUserMessageSeparator(result.join("\n").replace(/^\n+/, "").replace(/\n+$/, "")); +} + +/** + * Removes the `---\n**User Message:**` separator injected between metadata and + * user content. Called after metadata blocks have already been stripped so the + * separator (if any) is now a leading artefact in the remaining text. + */ +function stripUserMessageSeparator(text: string): string { + return text.replace(/^[\t ]*---[\t ]*\n[\t ]*\*\*User Message:\*\*[\t ]*\n?/, ""); } export function stripLeadingInboundMetadata(text: string): string { @@ -232,7 +241,7 @@ export function stripLeadingInboundMetadata(text: string): string { } const strippedRemainder = stripTrailingUntrustedContextSuffix(lines.slice(index)); - return strippedRemainder.join("\n"); + return stripUserMessageSeparator(strippedRemainder.join("\n")); } export function extractInboundSenderLabel(text: string): string | null {