Roo-Code/src/core/tools/newTaskTool.ts
Matt Rubens 8dd99a0254
Support config policies that require use of cloud (#4762)
* Support config policies that require use of cloud

* Switch to vitest
2025-06-16 18:31:22 -04:00

109 lines
3 KiB
TypeScript

import delay from "delay"
import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools"
import { Task } from "../task/Task"
import { defaultModeSlug, getModeBySlug } from "../../shared/modes"
import { formatResponse } from "../prompts/responses"
import { t } from "../../i18n"
export async function newTaskTool(
cline: Task,
block: ToolUse,
askApproval: AskApproval,
handleError: HandleError,
pushToolResult: PushToolResult,
removeClosingTag: RemoveClosingTag,
) {
const mode: string | undefined = block.params.mode
const message: string | undefined = block.params.message
try {
if (block.partial) {
const partialMessage = JSON.stringify({
tool: "newTask",
mode: removeClosingTag("mode", mode),
message: removeClosingTag("message", message),
})
await cline.ask("tool", partialMessage, block.partial).catch(() => {})
return
} else {
if (!mode) {
cline.consecutiveMistakeCount++
cline.recordToolError("new_task")
pushToolResult(await cline.sayAndCreateMissingParamError("new_task", "mode"))
return
}
if (!message) {
cline.consecutiveMistakeCount++
cline.recordToolError("new_task")
pushToolResult(await cline.sayAndCreateMissingParamError("new_task", "message"))
return
}
cline.consecutiveMistakeCount = 0
// Un-escape one level of backslashes before '@' for hierarchical subtasks
// Un-escape one level: \\@ -> \@ (removes one backslash for hierarchical subtasks)
const unescapedMessage = message.replace(/\\\\@/g, "\\@")
// Verify the mode exists
const targetMode = getModeBySlug(mode, (await cline.providerRef.deref()?.getState())?.customModes)
if (!targetMode) {
pushToolResult(formatResponse.toolError(`Invalid mode: ${mode}`))
return
}
const toolMessage = JSON.stringify({
tool: "newTask",
mode: targetMode.name,
content: message,
})
const didApprove = await askApproval("tool", toolMessage)
if (!didApprove) {
return
}
const provider = cline.providerRef.deref()
if (!provider) {
return
}
if (cline.enableCheckpoints) {
cline.checkpointSave(true)
}
// Preserve the current mode so we can resume with it later.
cline.pausedModeSlug = (await provider.getState()).mode ?? defaultModeSlug
// Switch mode first, then create new task instance.
await provider.handleModeSwitch(mode)
// Delay to allow mode change to take effect before next tool is executed.
await delay(500)
const newCline = await provider.initClineWithTask(unescapedMessage, undefined, cline)
if (!newCline) {
pushToolResult(t("tools:newTask.errors.policy_restriction"))
return
}
cline.emit("taskSpawned", newCline.taskId)
pushToolResult(`Successfully created new task in ${targetMode.name} mode with message: ${unescapedMessage}`)
// Set the isPaused flag to true so the parent
// task can wait for the sub-task to finish.
cline.isPaused = true
cline.emit("taskPaused")
return
}
} catch (error) {
await handleError("creating new task", error)
return
}
}