fix: rename new_task mode param to mode_slug for consistency with switch_mode

The new_task tool used "mode" as its parameter name while switch_mode
used "mode_slug" for the same concept (a mode slug). This inconsistency
caused LLMs to confuse the parameter names between the two tools,
leading to errors like "Missing value for required parameter mode_slug"
when using new_task.

Renamed the new_task parameter from "mode" to "mode_slug" to match
switch_mode, eliminating the source of LLM confusion.

Fixes #11861
This commit is contained in:
Roo Code 2026-03-16 20:05:56 +00:00
parent 44fd975b17
commit 6d8d4dd7f6
8 changed files with 48 additions and 48 deletions

View file

@ -241,7 +241,7 @@ describe("History resume delegation - parent metadata transitions", () => {
type: "tool_use",
name: "new_task",
id: "toolu_abc123",
input: { mode: "code", message: "Do something" },
input: { mode_slug: "code", message: "Do something" },
},
],
ts: 50,

View file

@ -628,9 +628,9 @@ export class NativeToolCallParser {
break
case "new_task":
if (partialArgs.mode !== undefined || partialArgs.message !== undefined) {
if (partialArgs.mode_slug !== undefined || partialArgs.message !== undefined) {
nativeArgs = {
mode: partialArgs.mode,
mode_slug: partialArgs.mode_slug,
message: partialArgs.message,
todos: partialArgs.todos,
}
@ -977,9 +977,9 @@ export class NativeToolCallParser {
break
case "new_task":
if (args.mode !== undefined && args.message !== undefined) {
if (args.mode_slug !== undefined && args.message !== undefined) {
nativeArgs = {
mode: args.mode,
mode_slug: args.mode_slug,
message: args.message,
todos: args.todos,
} as NativeArgsFor<TName>

View file

@ -372,7 +372,7 @@ export async function presentAssistantMessage(cline: Task) {
case "update_todo_list":
return `[${block.name}]`
case "new_task": {
const mode = block.params.mode ?? defaultModeSlug
const mode = block.params.mode_slug ?? defaultModeSlug
const message = block.params.message ?? "(no message)"
const modeName = getModeBySlug(mode, customModes)?.name ?? mode
return `[${block.name} in ${modeName} mode: '${message}']`

View file

@ -19,7 +19,7 @@ export default {
parameters: {
type: "object",
properties: {
mode: {
mode_slug: {
type: "string",
description: MODE_PARAMETER_DESCRIPTION,
},
@ -32,7 +32,7 @@ export default {
description: TODOS_PARAMETER_DESCRIPTION,
},
},
required: ["mode", "message", "todos"],
required: ["mode_slug", "message", "todos"],
additionalProperties: false,
},
},

View file

@ -61,7 +61,7 @@ describe("new_task Tool Isolation Enforcement", () => {
type: "tool_use",
id: "toolu_new_task_1",
name: "new_task",
input: { mode: "code", message: "Do something" },
input: { mode_slug: "code", message: "Do something" },
},
]
@ -83,7 +83,7 @@ describe("new_task Tool Isolation Enforcement", () => {
type: "tool_use",
id: "toolu_new_task_1",
name: "new_task",
input: { mode: "code", message: "Do something" },
input: { mode_slug: "code", message: "Do something" },
},
]
@ -123,7 +123,7 @@ describe("new_task Tool Isolation Enforcement", () => {
type: "tool_use",
id: "toolu_new_task_1",
name: "new_task",
input: { mode: "code", message: "Do something" },
input: { mode_slug: "code", message: "Do something" },
},
{
type: "tool_use",
@ -146,7 +146,7 @@ describe("new_task Tool Isolation Enforcement", () => {
type: "tool_use",
id: "toolu_new_task_1",
name: "new_task",
input: { mode: "code", message: "Do something" },
input: { mode_slug: "code", message: "Do something" },
},
{
type: "tool_use",
@ -173,7 +173,7 @@ describe("new_task Tool Isolation Enforcement", () => {
type: "tool_use",
id: "toolu_new_task_1",
name: "new_task",
input: { mode: "code", message: "Do something" },
input: { mode_slug: "code", message: "Do something" },
},
{
type: "tool_use",
@ -219,7 +219,7 @@ describe("new_task Tool Isolation Enforcement", () => {
type: "tool_use",
id: "toolu_new_task_1",
name: "new_task",
input: { mode: "code", message: "Do something" },
input: { mode_slug: "code", message: "Do something" },
},
{
type: "tool_use",
@ -253,7 +253,7 @@ describe("new_task Tool Isolation Enforcement", () => {
type: "tool_use",
id: "toolu_new_task_1",
name: "new_task",
input: { mode: "code", message: "Do something" },
input: { mode_slug: "code", message: "Do something" },
},
{
type: "tool_use",
@ -284,7 +284,7 @@ describe("new_task Tool Isolation Enforcement", () => {
type: "tool_use",
id: "toolu_new_task_1",
name: "new_task",
input: { mode: "code", message: "Do something" },
input: { mode_slug: "code", message: "Do something" },
},
{
type: "text",
@ -316,7 +316,7 @@ describe("new_task Tool Isolation Enforcement", () => {
type: "tool_use",
id: "toolu_new_task_1",
name: "new_task",
input: { mode: "code", message: "Do something" },
input: { mode_slug: "code", message: "Do something" },
},
// Simulating a malformed tool without ID (shouldn't happen, but defensive)
{
@ -345,13 +345,13 @@ describe("new_task Tool Isolation Enforcement", () => {
type: "tool_use",
id: "toolu_new_task_1",
name: "new_task",
input: { mode: "code", message: "First task" },
input: { mode_slug: "code", message: "First task" },
},
{
type: "tool_use",
id: "toolu_new_task_2",
name: "new_task",
input: { mode: "debug", message: "Second task" },
input: { mode_slug: "debug", message: "Second task" },
},
]
@ -375,7 +375,7 @@ describe("new_task Tool Isolation Enforcement", () => {
type: "tool_use",
id: "toolu_new_task_1",
name: "new_task",
input: { mode: "code", message: "Do something" },
input: { mode_slug: "code", message: "Do something" },
},
{
type: "tool_use",
@ -397,7 +397,7 @@ describe("new_task Tool Isolation Enforcement", () => {
type: "tool_use",
id: "toolu_new_task_1",
name: "new_task",
input: { mode: "code", message: "Do something" },
input: { mode_slug: "code", message: "Do something" },
},
{
type: "tool_use",

View file

@ -12,7 +12,7 @@ import { BaseTool, ToolCallbacks } from "./BaseTool"
import type { ToolUse } from "../../shared/tools"
interface NewTaskParams {
mode: string
mode_slug: string
message: string
todos?: string
}
@ -21,16 +21,16 @@ export class NewTaskTool extends BaseTool<"new_task"> {
readonly name = "new_task" as const
async execute(params: NewTaskParams, task: Task, callbacks: ToolCallbacks): Promise<void> {
const { mode, message, todos } = params
const { mode_slug, message, todos } = params
const { askApproval, handleError, pushToolResult } = callbacks
try {
// Validate required parameters.
if (!mode) {
if (!mode_slug) {
task.consecutiveMistakeCount++
task.recordToolError("new_task")
task.didToolFailInCurrentTurn = true
pushToolResult(await task.sayAndCreateMissingParamError("new_task", "mode"))
pushToolResult(await task.sayAndCreateMissingParamError("new_task", "mode_slug"))
return
}
@ -89,10 +89,10 @@ export class NewTaskTool extends BaseTool<"new_task"> {
const unescapedMessage = message.replace(/\\\\@/g, "\\@")
// Verify the mode exists
const targetMode = getModeBySlug(mode, state?.customModes)
const targetMode = getModeBySlug(mode_slug, state?.customModes)
if (!targetMode) {
pushToolResult(formatResponse.toolError(`Invalid mode: ${mode}`))
pushToolResult(formatResponse.toolError(`Invalid mode: ${mode_slug}`))
return
}
@ -114,7 +114,7 @@ export class NewTaskTool extends BaseTool<"new_task"> {
parentTaskId: task.taskId,
message: unescapedMessage,
initialTodos: todoItems,
mode,
mode: mode_slug,
})
// Reflect delegation in tool result (no pause/unpause, no wait)
@ -127,7 +127,7 @@ export class NewTaskTool extends BaseTool<"new_task"> {
}
override async handlePartial(task: Task, block: ToolUse<"new_task">): Promise<void> {
const mode: string | undefined = block.params.mode
const mode: string | undefined = block.params.mode_slug
const message: string | undefined = block.params.message
const todos: string | undefined = block.params.todos

View file

@ -117,7 +117,7 @@ const withNativeArgs = (block: ToolUse<"new_task">): ToolUse<"new_task"> => ({
// These tests intentionally exercise missing-param behavior, so we allow undefined
// values and let the tool's runtime validation handle it.
nativeArgs: {
mode: block.params.mode,
mode_slug: block.params.mode_slug,
message: block.params.message,
todos: block.params.todos,
} as unknown as NativeToolArgs["new_task"],
@ -148,7 +148,7 @@ describe("newTaskTool", () => {
type: "tool_use", // Add required 'type' property
name: "new_task", // Correct property name
params: {
mode: "code",
mode_slug: "code",
message: "Review this: \\\\@file1.txt and also \\\\\\\\@file2.txt", // Input with \\@ and \\\\@
todos: "[ ] First task\n[ ] Second task",
},
@ -183,7 +183,7 @@ describe("newTaskTool", () => {
type: "tool_use", // Add required 'type' property
name: "new_task", // Correct property name
params: {
mode: "code",
mode_slug: "code",
message: "This is already unescaped: \\@file1.txt",
todos: "[ ] Test todo",
},
@ -208,7 +208,7 @@ describe("newTaskTool", () => {
type: "tool_use", // Add required 'type' property
name: "new_task", // Correct property name
params: {
mode: "code",
mode_slug: "code",
message: "A normal mention @file1.txt",
todos: "[ ] Test todo",
},
@ -233,7 +233,7 @@ describe("newTaskTool", () => {
type: "tool_use", // Add required 'type' property
name: "new_task", // Correct property name
params: {
mode: "code",
mode_slug: "code",
message: "Mix: @file0.txt, \\@file1.txt, \\\\@file2.txt, \\\\\\\\@file3.txt",
todos: "[ ] Test todo",
},
@ -258,7 +258,7 @@ describe("newTaskTool", () => {
type: "tool_use",
name: "new_task",
params: {
mode: "code",
mode_slug: "code",
message: "Test message",
// todos missing - should work for backward compatibility
},
@ -288,7 +288,7 @@ describe("newTaskTool", () => {
type: "tool_use",
name: "new_task",
params: {
mode: "code",
mode_slug: "code",
message: "Test message with todos",
todos: "[ ] First task\n[ ] Second task",
},
@ -332,7 +332,7 @@ describe("newTaskTool", () => {
pushToolResult: mockPushToolResult,
})
expect(mockSayAndCreateMissingParamError).toHaveBeenCalledWith("new_task", "mode")
expect(mockSayAndCreateMissingParamError).toHaveBeenCalledWith("new_task", "mode_slug")
expect(mockCline.consecutiveMistakeCount).toBe(1)
expect(mockCline.recordToolError).toHaveBeenCalledWith("new_task")
})
@ -342,7 +342,7 @@ describe("newTaskTool", () => {
type: "tool_use",
name: "new_task",
params: {
mode: "code",
mode_slug: "code",
// message missing
todos: "[ ] Test todo",
},
@ -365,7 +365,7 @@ describe("newTaskTool", () => {
type: "tool_use",
name: "new_task",
params: {
mode: "code",
mode_slug: "code",
message: "Test message",
todos: "[ ] Pending task\n[x] Completed task\n[-] In progress task",
},
@ -401,7 +401,7 @@ describe("newTaskTool", () => {
type: "tool_use",
name: "new_task",
params: {
mode: "code",
mode_slug: "code",
message: "Test message",
// todos missing - should work when setting is disabled
},
@ -437,7 +437,7 @@ describe("newTaskTool", () => {
type: "tool_use",
name: "new_task",
params: {
mode: "code",
mode_slug: "code",
message: "Test message",
// todos missing - should error when setting is enabled
},
@ -473,7 +473,7 @@ describe("newTaskTool", () => {
type: "tool_use",
name: "new_task",
params: {
mode: "code",
mode_slug: "code",
message: "Test message",
todos: "[ ] First task\n[ ] Second task",
},
@ -515,7 +515,7 @@ describe("newTaskTool", () => {
type: "tool_use",
name: "new_task",
params: {
mode: "code",
mode_slug: "code",
message: "Test message",
todos: "", // Empty string should be accepted
},
@ -550,7 +550,7 @@ describe("newTaskTool", () => {
type: "tool_use",
name: "new_task",
params: {
mode: "code",
mode_slug: "code",
message: "Test message",
},
partial: false,
@ -583,7 +583,7 @@ describe("newTaskTool", () => {
type: "tool_use",
name: "new_task",
params: {
mode: "code",
mode_slug: "code",
message: "Test message",
},
partial: false,
@ -640,7 +640,7 @@ describe("newTaskTool delegation flow", () => {
type: "tool_use",
name: "new_task",
params: {
mode: "code",
mode_slug: "code",
message: "Do something",
// no todos -> should default to []
},

View file

@ -102,7 +102,7 @@ export type NativeToolArgs = {
edit_file: { file_path: string; old_string: string; new_string: string; expected_replacements?: number }
apply_patch: { patch: string }
list_files: { path: string; recursive?: boolean }
new_task: { mode: string; message: string; todos?: string }
new_task: { mode_slug: string; message: string; todos?: string }
ask_followup_question: {
question: string
follow_up: Array<{ text: string; mode?: string }>
@ -240,7 +240,7 @@ export interface SwitchModeToolUse extends ToolUse<"switch_mode"> {
export interface NewTaskToolUse extends ToolUse<"new_task"> {
name: "new_task"
params: Partial<Pick<Record<ToolParamName, string>, "mode" | "message" | "todos">>
params: Partial<Pick<Record<ToolParamName, string>, "mode_slug" | "message" | "todos">>
}
export interface RunSlashCommandToolUse extends ToolUse<"run_slash_command"> {