From 812d52db2d753ae4c156ddfff084c3eabfbd5fb5 Mon Sep 17 00:00:00 2001 From: Hannes Rudolph Date: Thu, 13 Nov 2025 10:11:19 -0700 Subject: [PATCH 1/2] refactor(task): wrap initial user message in instead of to prevent focus drift after context-management Rationale: After a successful context-management event, framing the next user block as feedback reduces model focus drift. Mentions parsing already supports , and tool flows (attemptCompletion, responses) are aligned. No change to loop/persistence. --- src/core/task/Task.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index 08880d0ee02..de4ca15f0d2 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -1279,7 +1279,7 @@ export class Task extends EventEmitter implements TaskLike { await this.initiateTaskLoop([ { type: "text", - text: `\n${task}\n`, + text: `\n${task}\n`, }, ...imageBlocks, ]) From 7089e11c33b359ea19da7d7c1335273ebc04bf1d Mon Sep 17 00:00:00 2001 From: Hannes Rudolph Date: Thu, 13 Nov 2025 17:46:53 -0700 Subject: [PATCH 2/2] refactor(mentions): drop parsing; standardize on ; update tests --- .../processUserContentMentions.spec.ts | 34 +++++++++---------- .../mentions/processUserContentMentions.ts | 7 ++-- src/core/task/__tests__/Task.spec.ts | 4 +-- 3 files changed, 21 insertions(+), 24 deletions(-) diff --git a/src/core/mentions/__tests__/processUserContentMentions.spec.ts b/src/core/mentions/__tests__/processUserContentMentions.spec.ts index 13c225042d7..3e3bf42e362 100644 --- a/src/core/mentions/__tests__/processUserContentMentions.spec.ts +++ b/src/core/mentions/__tests__/processUserContentMentions.spec.ts @@ -31,7 +31,7 @@ describe("processUserContentMentions", () => { const userContent = [ { type: "text" as const, - text: "Read file with limit", + text: "Read file with limit", }, ] @@ -45,7 +45,7 @@ describe("processUserContentMentions", () => { }) expect(parseMentions).toHaveBeenCalledWith( - "Read file with limit", + "Read file with limit", "/test", mockUrlContentFetcher, mockFileContextTracker, @@ -61,7 +61,7 @@ describe("processUserContentMentions", () => { const userContent = [ { type: "text" as const, - text: "Read file without limit", + text: "Read file without limit", }, ] @@ -74,7 +74,7 @@ describe("processUserContentMentions", () => { }) expect(parseMentions).toHaveBeenCalledWith( - "Read file without limit", + "Read file without limit", "/test", mockUrlContentFetcher, mockFileContextTracker, @@ -90,7 +90,7 @@ describe("processUserContentMentions", () => { const userContent = [ { type: "text" as const, - text: "Read unlimited lines", + text: "Read unlimited lines", }, ] @@ -104,7 +104,7 @@ describe("processUserContentMentions", () => { }) expect(parseMentions).toHaveBeenCalledWith( - "Read unlimited lines", + "Read unlimited lines", "/test", mockUrlContentFetcher, mockFileContextTracker, @@ -118,11 +118,11 @@ describe("processUserContentMentions", () => { }) describe("content processing", () => { - it("should process text blocks with tags", async () => { + it("should process text blocks with tags", async () => { const userContent = [ { type: "text" as const, - text: "Do something", + text: "Do something", }, ] @@ -136,7 +136,7 @@ describe("processUserContentMentions", () => { expect(parseMentions).toHaveBeenCalled() expect(result[0]).toEqual({ type: "text", - text: "parsed: Do something", + text: "parsed: Do something", }) }) @@ -213,7 +213,7 @@ describe("processUserContentMentions", () => { content: [ { type: "text" as const, - text: "Array task", + text: "Array task", }, { type: "text" as const, @@ -237,7 +237,7 @@ describe("processUserContentMentions", () => { content: [ { type: "text", - text: "parsed: Array task", + text: "parsed: Array task", }, { type: "text", @@ -251,7 +251,7 @@ describe("processUserContentMentions", () => { const userContent = [ { type: "text" as const, - text: "First task", + text: "First task", }, { type: "image" as const, @@ -280,7 +280,7 @@ describe("processUserContentMentions", () => { expect(result).toHaveLength(3) expect(result[0]).toEqual({ type: "text", - text: "parsed: First task", + text: "parsed: First task", }) expect(result[1]).toEqual(userContent[1]) // Image block unchanged expect(result[2]).toEqual({ @@ -296,7 +296,7 @@ describe("processUserContentMentions", () => { const userContent = [ { type: "text" as const, - text: "Test default", + text: "Test default", }, ] @@ -308,7 +308,7 @@ describe("processUserContentMentions", () => { }) expect(parseMentions).toHaveBeenCalledWith( - "Test default", + "Test default", "/test", mockUrlContentFetcher, mockFileContextTracker, @@ -324,7 +324,7 @@ describe("processUserContentMentions", () => { const userContent = [ { type: "text" as const, - text: "Test explicit false", + text: "Test explicit false", }, ] @@ -337,7 +337,7 @@ describe("processUserContentMentions", () => { }) expect(parseMentions).toHaveBeenCalledWith( - "Test explicit false", + "Test explicit false", "/test", mockUrlContentFetcher, mockFileContextTracker, diff --git a/src/core/mentions/processUserContentMentions.ts b/src/core/mentions/processUserContentMentions.ts index 4bdb422d48b..a36dcc76b47 100644 --- a/src/core/mentions/processUserContentMentions.ts +++ b/src/core/mentions/processUserContentMentions.ts @@ -30,7 +30,7 @@ export async function processUserContentMentions({ // Process userContent array, which contains various block types: // TextBlockParam, ImageBlockParam, ToolUseBlockParam, and ToolResultBlockParam. // We need to apply parseMentions() to: - // 1. All TextBlockParam's text (first user message with task) + // 1. All TextBlockParam's text (first user message with feedback) // 2. ToolResultBlockParam's content/context text arrays if it contains // "" (see formatToolDeniedFeedback, attemptCompletion, // executeCommand, and consecutiveMistakeCount >= 3) or "" @@ -40,10 +40,7 @@ export async function processUserContentMentions({ return Promise.all( userContent.map(async (block) => { const shouldProcessMentions = (text: string) => - text.includes("") || - text.includes("") || - text.includes("") || - text.includes("") + text.includes("") || text.includes("") || text.includes("") if (block.type === "text") { if (shouldProcessMentions(block.text)) { diff --git a/src/core/task/__tests__/Task.spec.ts b/src/core/task/__tests__/Task.spec.ts index 36492eebc97..02e130dcef5 100644 --- a/src/core/task/__tests__/Task.spec.ts +++ b/src/core/task/__tests__/Task.spec.ts @@ -895,7 +895,7 @@ describe("Cline", () => { } as const, { type: "text", - text: "Text with 'some/path' (see below for file content) in task tags", + text: "Text with 'some/path' (see below for file content) in task tags", } as const, { type: "tool_result", @@ -934,7 +934,7 @@ describe("Cline", () => { // Text within task tags should be processed expect((processedContent[1] as Anthropic.TextBlockParam).text).toContain("processed:") expect((processedContent[1] as Anthropic.TextBlockParam).text).toContain( - "Text with 'some/path' (see below for file content) in task tags", + "Text with 'some/path' (see below for file content) in task tags", ) // Feedback tag content should be processed