Skip to content
Draft
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
28 changes: 24 additions & 4 deletions apps/vscode-e2e/src/suite/markdown-lists.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,12 @@ suite("Markdown List Rendering", function () {
})

const taskId = await api.startNewTask({
configuration: { mode: "ask", alwaysAllowModeSwitch: true, autoApprovalEnabled: true },
configuration: {
mode: "ask",
alwaysAllowModeSwitch: true,
autoApprovalEnabled: true,
alwaysAllowFollowupQuestions: true,
},
text: "Please show me an example of an unordered list with the following items: Apple, Banana, Orange",
})

Expand Down Expand Up @@ -57,7 +62,12 @@ suite("Markdown List Rendering", function () {
})

const taskId = await api.startNewTask({
configuration: { mode: "ask", alwaysAllowModeSwitch: true, autoApprovalEnabled: true },
configuration: {
mode: "ask",
alwaysAllowModeSwitch: true,
autoApprovalEnabled: true,
alwaysAllowFollowupQuestions: true,
},
text: "Please show me a numbered list with three steps: First step, Second step, Third step",
})

Expand Down Expand Up @@ -94,7 +104,12 @@ suite("Markdown List Rendering", function () {
})

const taskId = await api.startNewTask({
configuration: { mode: "ask", alwaysAllowModeSwitch: true, autoApprovalEnabled: true },
configuration: {
mode: "ask",
alwaysAllowModeSwitch: true,
autoApprovalEnabled: true,
alwaysAllowFollowupQuestions: true,
},
text: "Please create a nested list with 'Main item' having two sub-items: 'Sub-item A' and 'Sub-item B'",
})

Expand Down Expand Up @@ -146,7 +161,12 @@ suite("Markdown List Rendering", function () {
})

const taskId = await api.startNewTask({
configuration: { mode: "ask", alwaysAllowModeSwitch: true, autoApprovalEnabled: true },
configuration: {
mode: "ask",
alwaysAllowModeSwitch: true,
autoApprovalEnabled: true,
alwaysAllowFollowupQuestions: true,
},
text: "Please create a list that has both numbered items and bullet points, mixing ordered and unordered lists",
})

Expand Down
7 changes: 6 additions & 1 deletion apps/vscode-e2e/src/suite/modes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@ suite("Roo Code Modes", function () {
globalThis.api.on(RooCodeEventName.TaskModeSwitched, (_taskId, mode) => modes.push(mode))

const switchModesTaskId = await globalThis.api.startNewTask({
configuration: { mode: "code", alwaysAllowModeSwitch: true, autoApprovalEnabled: true },
configuration: {
mode: "code",
alwaysAllowModeSwitch: true,
autoApprovalEnabled: true,
alwaysAllowFollowupQuestions: true,
},
text: "For each of `architect`, `ask`, and `debug` use the `switch_mode` tool to switch to that mode.",
})

Expand Down
1 change: 1 addition & 0 deletions apps/vscode-e2e/src/suite/subtasks.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ suite.skip("Roo Code Subtasks", () => {
alwaysAllowModeSwitch: true,
alwaysAllowSubtasks: true,
autoApprovalEnabled: true,
alwaysAllowFollowupQuestions: true,
enableCheckpoints: false,
},
text:
Expand Down
7 changes: 6 additions & 1 deletion apps/vscode-e2e/src/suite/task.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,12 @@ suite("Roo Code Task", function () {
})

const taskId = await api.startNewTask({
configuration: { mode: "ask", alwaysAllowModeSwitch: true, autoApprovalEnabled: true },
configuration: {
mode: "ask",
alwaysAllowModeSwitch: true,
autoApprovalEnabled: true,
alwaysAllowFollowupQuestions: true,
},
text: "Hello world, what is your name? Respond with 'My name is ...'",
})

Expand Down
3 changes: 3 additions & 0 deletions packages/types/src/followup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ export interface FollowUpData {
question?: string
/** Array of suggested answers that the user can select */
suggest?: Array<SuggestionItem>
/** If true, hide the "Roo has a question" header in the UI */
hideHeader?: boolean
}

/**
Expand All @@ -36,6 +38,7 @@ export const suggestionItemSchema = z.object({
export const followUpDataSchema = z.object({
question: z.string().optional(),
suggest: z.array(suggestionItemSchema).optional(),
hideHeader: z.boolean().optional(),
})

export type FollowUpDataType = z.infer<typeof followUpDataSchema>

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 9 additions & 15 deletions src/core/prompts/sections/__tests__/tool-use.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@ import { TOOL_PROTOCOL } from "@roo-code/types"

describe("getSharedToolUseSection", () => {
describe("XML protocol", () => {
it("should include one tool per message requirement", () => {
it("should include step-by-step tool usage guidance", () => {
const section = getSharedToolUseSection(TOOL_PROTOCOL.XML)

expect(section).toContain("You must use exactly one tool per message")
expect(section).toContain("every assistant message must include a tool call")
expect(section).toContain("You use tools step-by-step to accomplish a given task")
})

it("should include XML formatting instructions", () => {
Expand All @@ -19,28 +18,23 @@ describe("getSharedToolUseSection", () => {
})

describe("native protocol", () => {
it("should include one tool per message requirement when experiment is disabled", () => {
it("should include single tool guidance when experiment is disabled", () => {
// No experiment flags passed (default: disabled)
const section = getSharedToolUseSection(TOOL_PROTOCOL.NATIVE)

expect(section).toContain("You must use exactly one tool call per assistant response")
expect(section).toContain("Do not call zero tools or more than one tool")
expect(section).toContain("When using tools, use exactly one tool call per assistant response")
})

it("should include one tool per message requirement when experiment is explicitly disabled", () => {
it("should include single tool guidance when experiment is explicitly disabled", () => {
const section = getSharedToolUseSection(TOOL_PROTOCOL.NATIVE, { multipleNativeToolCalls: false })

expect(section).toContain("You must use exactly one tool call per assistant response")
expect(section).toContain("Do not call zero tools or more than one tool")
expect(section).toContain("When using tools, use exactly one tool call per assistant response")
})

it("should NOT include one tool per message requirement when experiment is enabled", () => {
it("should include multiple tools guidance when experiment is enabled", () => {
const section = getSharedToolUseSection(TOOL_PROTOCOL.NATIVE, { multipleNativeToolCalls: true })

expect(section).not.toContain("You must use exactly one tool per message")
expect(section).not.toContain("every assistant message must include a tool call")
expect(section).toContain("You must call at least one tool per assistant response")
expect(section).toContain("Prefer calling as many tools as are reasonably needed")
expect(section).toContain("When using tools, prefer calling as many tools as are reasonably needed")
})

it("should include native tool-calling instructions", () => {
Expand All @@ -63,7 +57,7 @@ describe("getSharedToolUseSection", () => {
const section = getSharedToolUseSection()

expect(section).toContain("XML-style tags")
expect(section).toContain("You must use exactly one tool per message")
expect(section).toContain("You use tools step-by-step to accomplish a given task")
})
})
})
6 changes: 3 additions & 3 deletions src/core/prompts/sections/tool-use.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ export function getSharedToolUseSection(
)

const toolUseGuidance = isMultipleNativeToolCallsEnabled
? " You must call at least one tool per assistant response. Prefer calling as many tools as are reasonably needed in a single response to reduce back-and-forth and complete tasks faster."
: " You must use exactly one tool call per assistant response. Do not call zero tools or more than one tool in the same response."
? " When using tools, prefer calling as many tools as are reasonably needed in a single response to reduce back-and-forth and complete tasks faster."
: " When using tools, use exactly one tool call per assistant response."

return `====

Expand All @@ -28,7 +28,7 @@ You have access to a set of tools that are executed upon the user's approval. Us

TOOL USE

You have access to a set of tools that are executed upon the user's approval. You must use exactly one tool per message, and every assistant message must include a tool call. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you know how much this change impacts things?

You have access to a set of tools that are executed upon the user's approval. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.

# Tool Use Formatting

Expand Down
43 changes: 29 additions & 14 deletions src/core/task/Task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
type CreateTaskOptions,
type ModelInfo,
type ToolProtocol,
type FollowUpData,
RooCodeEventName,
TelemetryEventName,
TaskStatus,
Expand Down Expand Up @@ -3304,21 +3305,35 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
)

if (!didToolUse) {
// Increment consecutive no-tool-use counter
this.consecutiveNoToolUseCount++

// Only show error and count toward mistake limit after 2 consecutive failures
if (this.consecutiveNoToolUseCount >= 2) {
await this.say("error", "MODEL_NO_TOOLS_USED")
// Only count toward mistake limit after second consecutive failure
this.consecutiveMistakeCount++
}
// Reset counter when we get a response (even without tools)
this.consecutiveNoToolUseCount = 0

// Use the task's locked protocol for consistent behavior
this.userMessageContent.push({
type: "text",
text: formatResponse.noToolsUsed(this._taskToolProtocol ?? "xml"),
})
const state = await this.providerRef.deref()?.getState()

if (state?.autoApprovalEnabled && state?.alwaysAllowFollowupQuestions) {
// Auto-approval enabled: tell model to use a tool and continue
this.userMessageContent.push({
type: "text",
text: formatResponse.noToolsUsed(this._taskToolProtocol ?? "xml"),
})
} else {
// Auto-approval disabled: present message to user, wait for response
// Use the assistant's text content as the message (hide "has a question" header)
const followUpData: FollowUpData = { question: assistantMessage, hideHeader: true }
const { text, images } = await this.ask("followup", JSON.stringify(followUpData), false)
await this.say("user_feedback", text ?? "", images)

// formatResponse.toolResult can return string or array, handle both
const toolResult = formatResponse.toolResult(`<answer>\n${text}\n</answer>`, images)
if (typeof toolResult === "string") {
this.userMessageContent.push({
type: "text",
text: toolResult,
})
} else {
this.userMessageContent.push(...toolResult)
}
}
} else {
// Reset counter when tools are used successfully
this.consecutiveNoToolUseCount = 0
Expand Down
Loading
Loading