Skip to content

Commit 0f08867

Browse files
refactor: unify user content tags to <user_message> (#10723)
Co-authored-by: Roo Code <roomote@roocode.com>
1 parent ea62173 commit 0f08867

File tree

10 files changed

+44
-78
lines changed

10 files changed

+44
-78
lines changed

src/core/mentions/__tests__/processUserContentMentions.spec.ts

Lines changed: 22 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ describe("processUserContentMentions", () => {
3434
const userContent = [
3535
{
3636
type: "text" as const,
37-
text: "<task>Read file with limit</task>",
37+
text: "<user_message>Read file with limit</user_message>",
3838
},
3939
]
4040

@@ -48,7 +48,7 @@ describe("processUserContentMentions", () => {
4848
})
4949

5050
expect(parseMentions).toHaveBeenCalledWith(
51-
"<task>Read file with limit</task>",
51+
"<user_message>Read file with limit</user_message>",
5252
"/test",
5353
mockUrlContentFetcher,
5454
mockFileContextTracker,
@@ -64,7 +64,7 @@ describe("processUserContentMentions", () => {
6464
const userContent = [
6565
{
6666
type: "text" as const,
67-
text: "<task>Read file without limit</task>",
67+
text: "<user_message>Read file without limit</user_message>",
6868
},
6969
]
7070

@@ -77,7 +77,7 @@ describe("processUserContentMentions", () => {
7777
})
7878

7979
expect(parseMentions).toHaveBeenCalledWith(
80-
"<task>Read file without limit</task>",
80+
"<user_message>Read file without limit</user_message>",
8181
"/test",
8282
mockUrlContentFetcher,
8383
mockFileContextTracker,
@@ -93,7 +93,7 @@ describe("processUserContentMentions", () => {
9393
const userContent = [
9494
{
9595
type: "text" as const,
96-
text: "<task>Read unlimited lines</task>",
96+
text: "<user_message>Read unlimited lines</user_message>",
9797
},
9898
]
9999

@@ -107,7 +107,7 @@ describe("processUserContentMentions", () => {
107107
})
108108

109109
expect(parseMentions).toHaveBeenCalledWith(
110-
"<task>Read unlimited lines</task>",
110+
"<user_message>Read unlimited lines</user_message>",
111111
"/test",
112112
mockUrlContentFetcher,
113113
mockFileContextTracker,
@@ -121,11 +121,11 @@ describe("processUserContentMentions", () => {
121121
})
122122

123123
describe("content processing", () => {
124-
it("should process text blocks with <task> tags", async () => {
124+
it("should process text blocks with <user_message> tags", async () => {
125125
const userContent = [
126126
{
127127
type: "text" as const,
128-
text: "<task>Do something</task>",
128+
text: "<user_message>Do something</user_message>",
129129
},
130130
]
131131

@@ -139,35 +139,12 @@ describe("processUserContentMentions", () => {
139139
expect(parseMentions).toHaveBeenCalled()
140140
expect(result.content[0]).toEqual({
141141
type: "text",
142-
text: "parsed: <task>Do something</task>",
142+
text: "parsed: <user_message>Do something</user_message>",
143143
})
144144
expect(result.mode).toBeUndefined()
145145
})
146146

147-
it("should process text blocks with <feedback> tags", async () => {
148-
const userContent = [
149-
{
150-
type: "text" as const,
151-
text: "<feedback>Fix this issue</feedback>",
152-
},
153-
]
154-
155-
const result = await processUserContentMentions({
156-
userContent,
157-
cwd: "/test",
158-
urlContentFetcher: mockUrlContentFetcher,
159-
fileContextTracker: mockFileContextTracker,
160-
})
161-
162-
expect(parseMentions).toHaveBeenCalled()
163-
expect(result.content[0]).toEqual({
164-
type: "text",
165-
text: "parsed: <feedback>Fix this issue</feedback>",
166-
})
167-
expect(result.mode).toBeUndefined()
168-
})
169-
170-
it("should not process text blocks without task or feedback tags", async () => {
147+
it("should not process text blocks without user_message tags", async () => {
171148
const userContent = [
172149
{
173150
type: "text" as const,
@@ -192,7 +169,7 @@ describe("processUserContentMentions", () => {
192169
{
193170
type: "tool_result" as const,
194171
tool_use_id: "123",
195-
content: "<feedback>Tool feedback</feedback>",
172+
content: "<user_message>Tool feedback</user_message>",
196173
},
197174
]
198175

@@ -207,7 +184,7 @@ describe("processUserContentMentions", () => {
207184
expect(result.content[0]).toEqual({
208185
type: "tool_result",
209186
tool_use_id: "123",
210-
content: "parsed: <feedback>Tool feedback</feedback>",
187+
content: "parsed: <user_message>Tool feedback</user_message>",
211188
})
212189
expect(result.mode).toBeUndefined()
213190
})
@@ -220,7 +197,7 @@ describe("processUserContentMentions", () => {
220197
content: [
221198
{
222199
type: "text" as const,
223-
text: "<task>Array task</task>",
200+
text: "<user_message>Array task</user_message>",
224201
},
225202
{
226203
type: "text" as const,
@@ -244,7 +221,7 @@ describe("processUserContentMentions", () => {
244221
content: [
245222
{
246223
type: "text",
247-
text: "parsed: <task>Array task</task>",
224+
text: "parsed: <user_message>Array task</user_message>",
248225
},
249226
{
250227
type: "text",
@@ -259,7 +236,7 @@ describe("processUserContentMentions", () => {
259236
const userContent = [
260237
{
261238
type: "text" as const,
262-
text: "<task>First task</task>",
239+
text: "<user_message>First task</user_message>",
263240
},
264241
{
265242
type: "image" as const,
@@ -272,7 +249,7 @@ describe("processUserContentMentions", () => {
272249
{
273250
type: "tool_result" as const,
274251
tool_use_id: "456",
275-
content: "<feedback>Feedback</feedback>",
252+
content: "<user_message>Feedback</user_message>",
276253
},
277254
]
278255

@@ -288,13 +265,13 @@ describe("processUserContentMentions", () => {
288265
expect(result.content).toHaveLength(3)
289266
expect(result.content[0]).toEqual({
290267
type: "text",
291-
text: "parsed: <task>First task</task>",
268+
text: "parsed: <user_message>First task</user_message>",
292269
})
293270
expect(result.content[1]).toEqual(userContent[1]) // Image block unchanged
294271
expect(result.content[2]).toEqual({
295272
type: "tool_result",
296273
tool_use_id: "456",
297-
content: "parsed: <feedback>Feedback</feedback>",
274+
content: "parsed: <user_message>Feedback</user_message>",
298275
})
299276
expect(result.mode).toBeUndefined()
300277
})
@@ -305,7 +282,7 @@ describe("processUserContentMentions", () => {
305282
const userContent = [
306283
{
307284
type: "text" as const,
308-
text: "<task>Test default</task>",
285+
text: "<user_message>Test default</user_message>",
309286
},
310287
]
311288

@@ -317,7 +294,7 @@ describe("processUserContentMentions", () => {
317294
})
318295

319296
expect(parseMentions).toHaveBeenCalledWith(
320-
"<task>Test default</task>",
297+
"<user_message>Test default</user_message>",
321298
"/test",
322299
mockUrlContentFetcher,
323300
mockFileContextTracker,
@@ -333,7 +310,7 @@ describe("processUserContentMentions", () => {
333310
const userContent = [
334311
{
335312
type: "text" as const,
336-
text: "<task>Test explicit false</task>",
313+
text: "<user_message>Test explicit false</user_message>",
337314
},
338315
]
339316

@@ -346,7 +323,7 @@ describe("processUserContentMentions", () => {
346323
})
347324

348325
expect(parseMentions).toHaveBeenCalledWith(
349-
"<task>Test explicit false</task>",
326+
"<user_message>Test explicit false</user_message>",
350327
"/test",
351328
mockUrlContentFetcher,
352329
mockFileContextTracker,

src/core/mentions/processUserContentMentions.ts

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,20 +38,13 @@ export async function processUserContentMentions({
3838
// Process userContent array, which contains various block types:
3939
// TextBlockParam, ImageBlockParam, ToolUseBlockParam, and ToolResultBlockParam.
4040
// We need to apply parseMentions() to:
41-
// 1. All TextBlockParam's text (first user message with task)
41+
// 1. All TextBlockParam's text (first user message)
4242
// 2. ToolResultBlockParam's content/context text arrays if it contains
43-
// "<feedback>" (see formatToolDeniedFeedback, attemptCompletion,
44-
// executeCommand, and consecutiveMistakeCount >= 3) or "<answer>"
45-
// (see askFollowupQuestion), we place all user generated content in
46-
// these tags so they can effectively be used as markers for when we
47-
// should parse mentions).
43+
// "<user_message>" - we place all user generated content in this tag
44+
// so it can effectively be used as a marker for when we should parse mentions.
4845
const content = await Promise.all(
4946
userContent.map(async (block) => {
50-
const shouldProcessMentions = (text: string) =>
51-
text.includes("<task>") ||
52-
text.includes("<feedback>") ||
53-
text.includes("<answer>") ||
54-
text.includes("<user_message>")
47+
const shouldProcessMentions = (text: string) => text.includes("<user_message>")
5548

5649
if (block.type === "text") {
5750
if (shouldProcessMentions(block.text)) {

src/core/prompts/responses.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,22 +20,20 @@ export const formatResponse = {
2020
if (isNativeProtocol(protocol ?? TOOL_PROTOCOL.XML)) {
2121
return JSON.stringify({
2222
status: "denied",
23-
message: "The user denied this operation and provided the following feedback",
2423
feedback: feedback,
2524
})
2625
}
27-
return `The user denied this operation and provided the following feedback:\n<feedback>\n${feedback}\n</feedback>`
26+
return `The user denied this operation and responded with the message:\n<user_message>\n${feedback}\n</user_message>`
2827
},
2928

3029
toolApprovedWithFeedback: (feedback?: string, protocol?: ToolProtocol) => {
3130
if (isNativeProtocol(protocol ?? TOOL_PROTOCOL.XML)) {
3231
return JSON.stringify({
3332
status: "approved",
34-
message: "The user approved this operation and provided the following context",
3533
feedback: feedback,
3634
})
3735
}
38-
return `The user approved this operation and provided the following context:\n<feedback>\n${feedback}\n</feedback>`
36+
return `The user approved this operation and responded with the message:\n<user_message>\n${feedback}\n</user_message>`
3937
},
4038

4139
toolError: (error?: string, protocol?: ToolProtocol) => {
@@ -81,11 +79,10 @@ Otherwise, if you have not completed the task and do not need additional informa
8179
if (isNativeProtocol(protocol ?? TOOL_PROTOCOL.XML)) {
8280
return JSON.stringify({
8381
status: "guidance",
84-
message: "You seem to be having trouble proceeding",
8582
feedback: feedback,
8683
})
8784
}
88-
return `You seem to be having trouble proceeding. The user has provided the following feedback to help guide you:\n<feedback>\n${feedback}\n</feedback>`
85+
return `You seem to be having trouble proceeding. The user has provided the following feedback to help guide you:\n<user_message>\n${feedback}\n</user_message>`
8986
},
9087

9188
missingToolParameterError: (paramName: string, protocol?: ToolProtocol) => {

src/core/task/Task.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1918,7 +1918,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
19181918
await this.initiateTaskLoop([
19191919
{
19201920
type: "text",
1921-
text: `<task>\n${task}\n</task>`,
1921+
text: `<user_message>\n${task}\n</user_message>`,
19221922
},
19231923
...imageBlocks,
19241924
]).catch((error) => {
@@ -2197,7 +2197,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
21972197
if (responseText) {
21982198
newUserContent.push({
21992199
type: "text",
2200-
text: `\n\nNew instructions for task continuation:\n<user_message>\n${responseText}\n</user_message>`,
2200+
text: `<user_message>\n${responseText}\n</user_message>`,
22012201
})
22022202
}
22032203

src/core/task/__tests__/Task.spec.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -883,7 +883,7 @@ describe("Cline", () => {
883883
})
884884

885885
describe("processUserContentMentions", () => {
886-
it("should process mentions in task and feedback tags", async () => {
886+
it("should process mentions in user_message tags", async () => {
887887
const [cline, task] = Task.create({
888888
provider: mockProvider,
889889
apiConfiguration: mockApiConfig,
@@ -897,15 +897,15 @@ describe("Cline", () => {
897897
} as const,
898898
{
899899
type: "text",
900-
text: "<task>Text with 'some/path' (see below for file content) in task tags</task>",
900+
text: "<user_message>Text with 'some/path' (see below for file content) in user_message tags</user_message>",
901901
} as const,
902902
{
903903
type: "tool_result",
904904
tool_use_id: "test-id",
905905
content: [
906906
{
907907
type: "text",
908-
text: "<feedback>Check 'some/path' (see below for file content)</feedback>",
908+
text: "<user_message>Check 'some/path' (see below for file content)</user_message>",
909909
},
910910
],
911911
} as Anthropic.ToolResultBlockParam,
@@ -933,18 +933,18 @@ describe("Cline", () => {
933933
"Regular text with 'some/path' (see below for file content)",
934934
)
935935

936-
// Text within task tags should be processed
936+
// Text within user_message tags should be processed
937937
expect((processedContent[1] as Anthropic.TextBlockParam).text).toContain("processed:")
938938
expect((processedContent[1] as Anthropic.TextBlockParam).text).toContain(
939-
"<task>Text with 'some/path' (see below for file content) in task tags</task>",
939+
"<user_message>Text with 'some/path' (see below for file content) in user_message tags</user_message>",
940940
)
941941

942-
// Feedback tag content should be processed
942+
// user_message tag content should be processed
943943
const toolResult1 = processedContent[2] as Anthropic.ToolResultBlockParam
944944
const content1 = Array.isArray(toolResult1.content) ? toolResult1.content[0] : toolResult1.content
945945
expect((content1 as Anthropic.TextBlockParam).text).toContain("processed:")
946946
expect((content1 as Anthropic.TextBlockParam).text).toContain(
947-
"<feedback>Check 'some/path' (see below for file content)</feedback>",
947+
"<user_message>Check 'some/path' (see below for file content)</user_message>",
948948
)
949949

950950
// Regular tool result should not be processed

src/core/task/__tests__/task-tool-history.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ describe("Task Tool History Handling", () => {
292292
},
293293
{
294294
type: "text" as const,
295-
text: "Another message with <task> tags",
295+
text: "Another message with <user_message> tags",
296296
},
297297
{
298298
type: "tool_result" as const,

src/core/tools/AskFollowupQuestionTool.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ export class AskFollowupQuestionTool extends BaseTool<"ask_followup_question"> {
8686
task.consecutiveMistakeCount = 0
8787
const { text, images } = await task.ask("followup", JSON.stringify(follow_up_json), false)
8888
await task.say("user_feedback", text ?? "", images)
89-
pushToolResult(formatResponse.toolResult(`<answer>\n${text}\n</answer>`, images))
89+
pushToolResult(formatResponse.toolResult(`<user_message>\n${text}\n</user_message>`, images))
9090
} catch (error) {
9191
await handleError("asking question", error as Error)
9292
}

src/core/tools/AttemptCompletionTool.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ export class AttemptCompletionTool extends BaseTool<"attempt_completion"> {
150150
// User provided feedback - push tool result to continue the conversation
151151
await task.say("user_feedback", text ?? "", images)
152152

153-
const feedbackText = `The user has provided feedback on the results. Consider their input to continue the task, and then attempt completion again.\n<feedback>\n${text}\n</feedback>`
153+
const feedbackText = `<user_message>\n${text}\n</user_message>`
154154
pushToolResult(formatResponse.toolResult(feedbackText, images))
155155
} catch (error) {
156156
await handleError("inspecting site", error as Error)

src/core/tools/ExecuteCommandTool.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -340,8 +340,7 @@ export async function executeCommandInTerminal(
340340
[
341341
`Command is still running in terminal from '${terminal.getCurrentWorkingDirectory().toPosix()}'.`,
342342
result.length > 0 ? `Here's the output so far:\n${result}\n` : "\n",
343-
`The user provided the following feedback:`,
344-
`<feedback>\n${text}\n</feedback>`,
343+
`<user_message>\n${text}\n</user_message>`,
345344
].join("\n"),
346345
images,
347346
),

src/core/tools/__tests__/readFileTool.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,11 @@ vi.mock("../../prompts/responses", () => ({
9595
toolDenied: vi.fn(() => "The user denied this operation."),
9696
toolDeniedWithFeedback: vi.fn(
9797
(feedback?: string) =>
98-
`The user denied this operation and provided the following feedback:\n<feedback>\n${feedback}\n</feedback>`,
98+
`The user denied this operation and responded with the message:\n<user_message>\n${feedback}\n</user_message>`,
9999
),
100100
toolApprovedWithFeedback: vi.fn(
101101
(feedback?: string) =>
102-
`The user approved this operation and provided the following context:\n<feedback>\n${feedback}\n</feedback>`,
102+
`The user approved this operation and responded with the message:\n<user_message>\n${feedback}\n</user_message>`,
103103
),
104104
rooIgnoreError: vi.fn(
105105
(path: string) =>

0 commit comments

Comments
 (0)