mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-05 08:10:14 +00:00
feat: introduce subagent functionality with tool integration
- Added new tool parameters for subagent including "description" and "subagent_type". - Implemented filtering logic to include/exclude the subagent tool based on experiment settings. - Enhanced localization for subagent tool in multiple languages. - Updated UI components to display subagent status and progress. - Introduced subagent-specific tool restrictions in the backend logic. This commit lays the groundwork for running subagents in the background for research or sub-tasks, enhancing the overall functionality of the toolset.
This commit is contained in:
parent
7118a14188
commit
0ead5bb238
61 changed files with 1735 additions and 7 deletions
|
|
@ -6,7 +6,13 @@ import type { Keys, Equals, AssertEqual } from "./type-fu.js"
|
|||
* ExperimentId
|
||||
*/
|
||||
|
||||
export const experimentIds = ["preventFocusDisruption", "imageGeneration", "runSlashCommand", "customTools"] as const
|
||||
export const experimentIds = [
|
||||
"preventFocusDisruption",
|
||||
"imageGeneration",
|
||||
"runSlashCommand",
|
||||
"customTools",
|
||||
"subagent",
|
||||
] as const
|
||||
|
||||
export const experimentIdsSchema = z.enum(experimentIds)
|
||||
|
||||
|
|
@ -21,6 +27,7 @@ export const experimentsSchema = z.object({
|
|||
imageGeneration: z.boolean().optional(),
|
||||
runSlashCommand: z.boolean().optional(),
|
||||
customTools: z.boolean().optional(),
|
||||
subagent: z.boolean().optional(),
|
||||
})
|
||||
|
||||
export type Experiments = z.infer<typeof experimentsSchema>
|
||||
|
|
|
|||
|
|
@ -183,6 +183,7 @@ export type ClineSay = z.infer<typeof clineSaySchema>
|
|||
export const toolProgressStatusSchema = z.object({
|
||||
icon: z.string().optional(),
|
||||
text: z.string().optional(),
|
||||
spin: z.boolean().optional(),
|
||||
})
|
||||
|
||||
export type ToolProgressStatus = z.infer<typeof toolProgressStatusSchema>
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ export const toolNames = [
|
|||
"skill",
|
||||
"generate_image",
|
||||
"custom_tool",
|
||||
"subagent",
|
||||
] as const
|
||||
|
||||
export const toolNamesSchema = z.enum(toolNames)
|
||||
|
|
|
|||
|
|
@ -782,6 +782,8 @@ export interface ClineSayTool {
|
|||
| "runSlashCommand"
|
||||
| "updateTodoList"
|
||||
| "skill"
|
||||
| "subagentRunning"
|
||||
| "subagentCompleted"
|
||||
path?: string
|
||||
// For readCommandOutput
|
||||
readStart?: number
|
||||
|
|
@ -839,6 +841,13 @@ export interface ClineSayTool {
|
|||
description?: string
|
||||
// Properties for skill tool
|
||||
skill?: string
|
||||
// Properties for subagent tool (subagentRunning / subagentCompleted)
|
||||
currentTask?: string
|
||||
result?: string
|
||||
error?: string
|
||||
/** When set (e.g. CANCELLED), webview shows t(messageKey) instead of result/error. */
|
||||
resultCode?: string
|
||||
messageKey?: string
|
||||
}
|
||||
|
||||
export interface ClineAskUseMcpServer {
|
||||
|
|
|
|||
|
|
@ -637,6 +637,20 @@ export class NativeToolCallParser {
|
|||
}
|
||||
break
|
||||
|
||||
case "subagent":
|
||||
if (
|
||||
partialArgs.description !== undefined ||
|
||||
partialArgs.prompt !== undefined ||
|
||||
partialArgs.subagent_type !== undefined
|
||||
) {
|
||||
nativeArgs = {
|
||||
description: partialArgs.description,
|
||||
prompt: partialArgs.prompt,
|
||||
subagent_type: partialArgs.subagent_type,
|
||||
}
|
||||
}
|
||||
break
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
|
@ -911,6 +925,21 @@ export class NativeToolCallParser {
|
|||
}
|
||||
break
|
||||
|
||||
case "subagent":
|
||||
if (
|
||||
args.description !== undefined &&
|
||||
args.prompt !== undefined &&
|
||||
args.subagent_type !== undefined &&
|
||||
(args.subagent_type === "general" || args.subagent_type === "explore")
|
||||
) {
|
||||
nativeArgs = {
|
||||
description: args.description,
|
||||
prompt: args.prompt,
|
||||
subagent_type: args.subagent_type,
|
||||
} as NativeArgsFor<TName>
|
||||
}
|
||||
break
|
||||
|
||||
case "use_mcp_tool":
|
||||
if (args.server_name !== undefined && args.tool_name !== undefined) {
|
||||
nativeArgs = {
|
||||
|
|
|
|||
|
|
@ -291,6 +291,56 @@ describe("NativeToolCallParser", () => {
|
|||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("subagent tool", () => {
|
||||
it("should parse description, prompt, and subagent_type into nativeArgs", () => {
|
||||
const toolCall = {
|
||||
id: "toolu_subagent_1",
|
||||
name: "subagent" as const,
|
||||
arguments: JSON.stringify({
|
||||
description: "Explore codebase",
|
||||
prompt: "List all exports from src/index.ts",
|
||||
subagent_type: "explore",
|
||||
}),
|
||||
}
|
||||
|
||||
const result = NativeToolCallParser.parseToolCall(toolCall)
|
||||
|
||||
expect(result).not.toBeNull()
|
||||
expect(result?.type).toBe("tool_use")
|
||||
if (result?.type === "tool_use") {
|
||||
expect(result.nativeArgs).toBeDefined()
|
||||
const nativeArgs = result.nativeArgs as {
|
||||
description: string
|
||||
prompt: string
|
||||
subagent_type: "general" | "explore"
|
||||
}
|
||||
expect(nativeArgs.description).toBe("Explore codebase")
|
||||
expect(nativeArgs.prompt).toBe("List all exports from src/index.ts")
|
||||
expect(nativeArgs.subagent_type).toBe("explore")
|
||||
}
|
||||
})
|
||||
|
||||
it("should parse general subagent_type", () => {
|
||||
const toolCall = {
|
||||
id: "toolu_subagent_2",
|
||||
name: "subagent" as const,
|
||||
arguments: JSON.stringify({
|
||||
description: "Fix bug",
|
||||
prompt: "Fix the null check in utils.ts",
|
||||
subagent_type: "general",
|
||||
}),
|
||||
}
|
||||
|
||||
const result = NativeToolCallParser.parseToolCall(toolCall)
|
||||
|
||||
expect(result).not.toBeNull()
|
||||
if (result?.type === "tool_use" && result.nativeArgs) {
|
||||
const nativeArgs = result.nativeArgs as { subagent_type: string }
|
||||
expect(nativeArgs.subagent_type).toBe("general")
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("processStreamingChunk", () => {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { customToolRegistry } from "@roo-code/core"
|
|||
|
||||
import { t } from "../../i18n"
|
||||
|
||||
import { SUBAGENT_STATUS_THINKING } from "../../shared/subagent"
|
||||
import { defaultModeSlug, getModeBySlug } from "../../shared/modes"
|
||||
import type { ToolParamName, ToolResponse, ToolUse, McpToolUse } from "../../shared/tools"
|
||||
|
||||
|
|
@ -30,6 +31,7 @@ import { askFollowupQuestionTool } from "../tools/AskFollowupQuestionTool"
|
|||
import { switchModeTool } from "../tools/SwitchModeTool"
|
||||
import { attemptCompletionTool, AttemptCompletionCallbacks } from "../tools/AttemptCompletionTool"
|
||||
import { newTaskTool } from "../tools/NewTaskTool"
|
||||
import { subagentTool } from "../tools/SubagentTool"
|
||||
import { updateTodoListTool } from "../tools/UpdateTodoListTool"
|
||||
import { runSlashCommandTool } from "../tools/RunSlashCommandTool"
|
||||
import { skillTool } from "../tools/SkillTool"
|
||||
|
|
@ -292,6 +294,7 @@ export async function presentAssistantMessage(cline: Task) {
|
|||
content = content.replace(/\s?<\/thinking>/g, "")
|
||||
}
|
||||
|
||||
cline.subagentProgressCallback?.(SUBAGENT_STATUS_THINKING)
|
||||
await cline.say("text", content, undefined, block.partial)
|
||||
break
|
||||
}
|
||||
|
|
@ -383,6 +386,8 @@ export async function presentAssistantMessage(cline: Task) {
|
|||
return `[${block.name} for '${block.params.skill}'${block.params.args ? ` with args: ${block.params.args}` : ""}]`
|
||||
case "generate_image":
|
||||
return `[${block.name} for '${block.params.path}']`
|
||||
case "subagent":
|
||||
return `[${block.name}: ${block.params.description ?? "(no description)"}]`
|
||||
default:
|
||||
return `[${block.name}]`
|
||||
}
|
||||
|
|
@ -675,6 +680,7 @@ export async function presentAssistantMessage(cline: Task) {
|
|||
}
|
||||
}
|
||||
|
||||
cline.subagentProgressCallback?.(toolDescription())
|
||||
switch (block.name) {
|
||||
case "write_to_file":
|
||||
await checkpointSaveAndMark(cline)
|
||||
|
|
@ -812,6 +818,14 @@ export async function presentAssistantMessage(cline: Task) {
|
|||
toolCallId: block.id,
|
||||
})
|
||||
break
|
||||
case "subagent":
|
||||
await subagentTool.handle(cline, block as ToolUse<"subagent">, {
|
||||
askApproval,
|
||||
handleError,
|
||||
pushToolResult,
|
||||
toolCallId: block.id,
|
||||
})
|
||||
break
|
||||
case "attempt_completion": {
|
||||
const completionCallbacks: AttemptCompletionCallbacks = {
|
||||
askApproval,
|
||||
|
|
|
|||
|
|
@ -89,3 +89,55 @@ describe("filterNativeToolsForMode - disabledTools", () => {
|
|||
expect(resultNames).not.toContain("edit")
|
||||
})
|
||||
})
|
||||
|
||||
describe("filterNativeToolsForMode - subagent experiment", () => {
|
||||
const codeMode = {
|
||||
slug: "code",
|
||||
name: "Code",
|
||||
roleDefinition: "Test",
|
||||
groups: ["read", "edit", "command", "mcp"] as ("read" | "edit" | "command" | "mcp")[],
|
||||
}
|
||||
|
||||
const mockSubagentTool: OpenAI.Chat.ChatCompletionTool = {
|
||||
type: "function",
|
||||
function: {
|
||||
name: "subagent",
|
||||
description: "Run subagent",
|
||||
parameters: {},
|
||||
},
|
||||
}
|
||||
|
||||
const mockNativeTools: OpenAI.Chat.ChatCompletionTool[] = [
|
||||
makeTool("read_file"),
|
||||
makeTool("write_to_file"),
|
||||
mockSubagentTool,
|
||||
]
|
||||
|
||||
it("should exclude subagent when experiment is not enabled", () => {
|
||||
const filtered = filterNativeToolsForMode(
|
||||
mockNativeTools,
|
||||
"code",
|
||||
[codeMode],
|
||||
{ subagent: false },
|
||||
undefined,
|
||||
{},
|
||||
undefined,
|
||||
)
|
||||
const toolNames = filtered.map((t) => ("function" in t ? t.function.name : ""))
|
||||
expect(toolNames).not.toContain("subagent")
|
||||
})
|
||||
|
||||
it("should include subagent when experiment is enabled", () => {
|
||||
const filtered = filterNativeToolsForMode(
|
||||
mockNativeTools,
|
||||
"code",
|
||||
[codeMode],
|
||||
{ subagent: true },
|
||||
undefined,
|
||||
{},
|
||||
undefined,
|
||||
)
|
||||
const toolNames = filtered.map((t) => ("function" in t ? t.function.name : ""))
|
||||
expect(toolNames).toContain("subagent")
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -291,6 +291,10 @@ export function filterNativeToolsForMode(
|
|||
allowedToolNames.delete("run_slash_command")
|
||||
}
|
||||
|
||||
if (!experiments?.subagent) {
|
||||
allowedToolNames.delete("subagent")
|
||||
}
|
||||
|
||||
// Remove tools that are explicitly disabled via the disabledTools setting
|
||||
if (settings?.disabledTools?.length) {
|
||||
for (const toolName of settings.disabledTools) {
|
||||
|
|
@ -379,6 +383,9 @@ export function isToolAllowedInMode(
|
|||
if (toolName === "run_slash_command") {
|
||||
return experiments?.runSlashCommand === true
|
||||
}
|
||||
if (toolName === "subagent") {
|
||||
return experiments?.subagent === true
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import newTask from "./new_task"
|
|||
import readCommandOutput from "./read_command_output"
|
||||
import { createReadFileTool, type ReadFileToolOptions } from "./read_file"
|
||||
import runSlashCommand from "./run_slash_command"
|
||||
import subagent from "./subagent"
|
||||
import skill from "./skill"
|
||||
import searchReplace from "./search_replace"
|
||||
import edit_file from "./edit_file"
|
||||
|
|
@ -60,6 +61,7 @@ export function getNativeTools(options: NativeToolsOptions = {}): OpenAI.Chat.Ch
|
|||
readCommandOutput,
|
||||
createReadFileTool(readFileOptions),
|
||||
runSlashCommand,
|
||||
subagent,
|
||||
skill,
|
||||
searchReplace,
|
||||
edit_file,
|
||||
|
|
|
|||
36
src/core/prompts/tools/native-tools/subagent.ts
Normal file
36
src/core/prompts/tools/native-tools/subagent.ts
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
import type OpenAI from "openai"
|
||||
|
||||
const SUBAGENT_DESCRIPTION = `Run a subagent in the background to do a focused sub-task. When the subagent finishes, its result or summary is returned as this tool's result and you can use it in your next step. Use when: (1) you need to offload research, exploration, or a multi-step sub-task without switching the user's view, or (2) you want read-only exploration (subagent_type "explore") with no file edits or commands. Do not use for creating a new user-visible task—use new_task instead.`
|
||||
|
||||
const DESCRIPTION_PARAM = `Short label for this subagent, shown in the chat (e.g. "List exports in src", "Check README")`
|
||||
const PROMPT_PARAM = `Full instructions for the subagent. Its result or summary will be returned as this tool's result.`
|
||||
const SUBAGENT_TYPE_PARAM = `"explore": read-only—subagent can only use read/search/list tools (no file edits or commands). Use for research or gathering information. "general": full tools—subagent can read, edit, and run commands. Use when the sub-task may need to change files or run commands.`
|
||||
|
||||
export default {
|
||||
type: "function",
|
||||
function: {
|
||||
name: "subagent",
|
||||
description: SUBAGENT_DESCRIPTION,
|
||||
strict: true,
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
description: {
|
||||
type: "string",
|
||||
description: DESCRIPTION_PARAM,
|
||||
},
|
||||
prompt: {
|
||||
type: "string",
|
||||
description: PROMPT_PARAM,
|
||||
},
|
||||
subagent_type: {
|
||||
type: "string",
|
||||
description: SUBAGENT_TYPE_PARAM,
|
||||
enum: ["general", "explore"],
|
||||
},
|
||||
},
|
||||
required: ["description", "prompt", "subagent_type"],
|
||||
additionalProperties: false,
|
||||
},
|
||||
},
|
||||
} satisfies OpenAI.Chat.ChatCompletionTool
|
||||
|
|
@ -70,6 +70,7 @@ import { t } from "../../i18n"
|
|||
import { getApiMetrics, hasTokenUsageChanged, hasToolUsageChanged } from "../../shared/getApiMetrics"
|
||||
import { ClineAskResponse } from "../../shared/WebviewMessage"
|
||||
import { defaultModeSlug, getModeBySlug } from "../../shared/modes"
|
||||
import { SUBAGENT_TOOL_NAMES, type SubagentRunningPayload, type SubagentStructuredResult } from "../../shared/subagent"
|
||||
import { DiffStrategy, type ToolUse, type ToolParamName, toolParamNames } from "../../shared/tools"
|
||||
import { getModelMaxOutputTokens } from "../../shared/api"
|
||||
|
||||
|
|
@ -157,6 +158,10 @@ export interface TaskOptions extends CreateTaskOptions {
|
|||
workspacePath?: string
|
||||
/** Initial status for the task's history item (e.g., "active" for child tasks) */
|
||||
initialStatus?: "active" | "delegated" | "completed"
|
||||
/** When set, this task runs as a background subagent; "explore" = read-only tools */
|
||||
subagentType?: "general" | "explore"
|
||||
/** When false, task is not persisted to task history (e.g. subagents). Defaults to false when subagentType is set, true otherwise. */
|
||||
needUpdateHistory?: boolean
|
||||
}
|
||||
|
||||
export class Task extends EventEmitter<TaskEvents> implements TaskLike {
|
||||
|
|
@ -417,6 +422,17 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
|
|||
// MessageManager for high-level message operations (lazy initialized)
|
||||
private _messageManager?: MessageManager
|
||||
|
||||
/** When set, attempt_completion will resolve this and abort instead of normal flow */
|
||||
public backgroundCompletionResolve?: (result: string | SubagentStructuredResult) => void
|
||||
/** When set, tool building is restricted to read-only for "explore" */
|
||||
public subagentType?: "general" | "explore"
|
||||
/** When set, child reports current step (e.g. tool description) for parent to show in subagentRunning row */
|
||||
public subagentProgressCallback?: (currentTask: string) => void
|
||||
/** When false, saveClineMessages does not call updateTaskHistory (e.g. subagents). */
|
||||
private readonly needUpdateHistory: boolean
|
||||
/** When this task is the parent of a running subagent, holds the child task until it completes or is cancelled. */
|
||||
public activeSubagentChild?: Task
|
||||
|
||||
constructor({
|
||||
provider,
|
||||
apiConfiguration,
|
||||
|
|
@ -436,9 +452,14 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
|
|||
initialTodos,
|
||||
workspacePath,
|
||||
initialStatus,
|
||||
subagentType,
|
||||
needUpdateHistory,
|
||||
}: TaskOptions) {
|
||||
super()
|
||||
|
||||
this.subagentType = subagentType
|
||||
this.needUpdateHistory = needUpdateHistory ?? subagentType === undefined
|
||||
|
||||
if (startTask && !task && !images && !historyItem) {
|
||||
throw new Error("Either historyItem or task/images must be provided")
|
||||
}
|
||||
|
|
@ -1240,7 +1261,9 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
|
|||
// - Final state is emitted when updates stop (trailing: true)
|
||||
this.debouncedEmitTokenUsage(tokenUsage, this.toolUsage)
|
||||
|
||||
await this.providerRef.deref()?.updateTaskHistory(historyItem)
|
||||
if (this.needUpdateHistory) {
|
||||
await this.providerRef.deref()?.updateTaskHistory(historyItem)
|
||||
}
|
||||
return true
|
||||
} catch (error) {
|
||||
console.error("Failed to save Roo messages:", error)
|
||||
|
|
@ -1258,6 +1281,32 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
|
|||
return undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the last "subagentRunning" say message with currentTask so the UI can show it in real time.
|
||||
* No-op if no such message exists.
|
||||
*/
|
||||
public reportSubagentProgress(currentTask: string): void {
|
||||
const idx = findLastIndex(this.clineMessages, (m) => {
|
||||
if (m.type !== "say" || m.say !== "tool" || !m.text) return false
|
||||
try {
|
||||
const parsed = JSON.parse(m.text) as { tool?: string }
|
||||
return parsed.tool === SUBAGENT_TOOL_NAMES.running
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
})
|
||||
if (idx === -1) return
|
||||
const msg = this.clineMessages[idx]
|
||||
try {
|
||||
const payload = JSON.parse(msg.text!) as SubagentRunningPayload
|
||||
payload.currentTask = currentTask
|
||||
msg.text = JSON.stringify(payload)
|
||||
void this.updateClineMessage(msg)
|
||||
} catch {
|
||||
// ignore malformed message
|
||||
}
|
||||
}
|
||||
|
||||
// Note that `partial` has three valid states true (partial message),
|
||||
// false (completion of partial message), undefined (individual complete
|
||||
// message).
|
||||
|
|
@ -1674,6 +1723,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
|
|||
disabledTools: state?.disabledTools,
|
||||
modelInfo,
|
||||
includeAllToolsWithRestrictions: false,
|
||||
subagentType: this.subagentType,
|
||||
})
|
||||
allTools = toolsResult.tools
|
||||
}
|
||||
|
|
@ -1798,6 +1848,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
|
|||
text,
|
||||
images,
|
||||
partial,
|
||||
progressStatus,
|
||||
contextCondense,
|
||||
contextTruncation,
|
||||
})
|
||||
|
|
@ -1836,6 +1887,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
|
|||
say: type,
|
||||
text,
|
||||
images,
|
||||
progressStatus,
|
||||
contextCondense,
|
||||
contextTruncation,
|
||||
})
|
||||
|
|
@ -1860,6 +1912,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
|
|||
text,
|
||||
images,
|
||||
checkpoint,
|
||||
progressStatus,
|
||||
contextCondense,
|
||||
contextTruncation,
|
||||
})
|
||||
|
|
@ -1876,6 +1929,28 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
|
|||
return formatResponse.toolError(formatResponse.missingToolParameterError(paramName))
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the task loop with the given prompt. Used for background subagents.
|
||||
* Call only when task was created with startTask: false and subagentType set.
|
||||
*/
|
||||
public async runBackgroundSubagentLoop(initialPrompt: string): Promise<void> {
|
||||
this.clineMessages = []
|
||||
this.apiConversationHistory = []
|
||||
const typeInstructions =
|
||||
this.subagentType === "explore"
|
||||
? "You are running as an **explore** subagent (read-only). Use only read, search, and list tools; do not edit files or run commands. Gather the requested information and put your findings and summary in your completion; that will be returned to the parent task.\n\n"
|
||||
: "You are running as a **general** subagent with full tool access. You may read, edit files, and run commands as needed. Put your findings and final result in your completion; that summary will be returned to the parent task.\n\n"
|
||||
const taskContent = `${typeInstructions}${initialPrompt}`
|
||||
await this.say("text", taskContent)
|
||||
this.isInitialized = true
|
||||
await this.initiateTaskLoop([{ type: "text", text: `<task>\n${taskContent}\n</task>` }]).catch((error) => {
|
||||
if (this.abandoned === true || this.backgroundCompletionResolve === undefined) {
|
||||
return
|
||||
}
|
||||
throw error
|
||||
})
|
||||
}
|
||||
|
||||
// Lifecycle
|
||||
// Start / Resume / Abort / Dispose
|
||||
|
||||
|
|
@ -3862,6 +3937,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
|
|||
disabledTools: state?.disabledTools,
|
||||
modelInfo,
|
||||
includeAllToolsWithRestrictions: false,
|
||||
subagentType: this.subagentType,
|
||||
})
|
||||
allTools = toolsResult.tools
|
||||
}
|
||||
|
|
@ -4076,6 +4152,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
|
|||
disabledTools: state?.disabledTools,
|
||||
modelInfo,
|
||||
includeAllToolsWithRestrictions: false,
|
||||
subagentType: this.subagentType,
|
||||
})
|
||||
contextMgmtTools = toolsResult.tools
|
||||
}
|
||||
|
|
@ -4240,6 +4317,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
|
|||
disabledTools: state?.disabledTools,
|
||||
modelInfo,
|
||||
includeAllToolsWithRestrictions: supportsAllowedFunctionNames,
|
||||
subagentType: this.subagentType,
|
||||
})
|
||||
allTools = toolsResult.tools
|
||||
allowedFunctionNames = toolsResult.allowedFunctionNames
|
||||
|
|
|
|||
|
|
@ -448,3 +448,130 @@ describe("flushPendingToolResultsToHistory", () => {
|
|||
expect(task.apiConversationHistory.length).toBe(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe("reportSubagentProgress", () => {
|
||||
let mockProvider: any
|
||||
let mockApiConfig: ProviderSettings
|
||||
let mockExtensionContext: vscode.ExtensionContext
|
||||
|
||||
beforeEach(() => {
|
||||
if (!TelemetryService.hasInstance()) {
|
||||
TelemetryService.createInstance([])
|
||||
}
|
||||
const storageUri = { fsPath: path.join(os.tmpdir(), "test-storage-reportSubagent") }
|
||||
mockExtensionContext = {
|
||||
globalState: {
|
||||
get: vi.fn().mockImplementation((_key: keyof GlobalState) => undefined),
|
||||
update: vi.fn().mockImplementation((_k, _v) => Promise.resolve()),
|
||||
keys: vi.fn().mockReturnValue([]),
|
||||
},
|
||||
globalStorageUri: storageUri,
|
||||
workspaceState: {
|
||||
get: vi.fn().mockImplementation((_k) => undefined),
|
||||
update: vi.fn().mockImplementation((_k, _v) => Promise.resolve()),
|
||||
keys: vi.fn().mockReturnValue([]),
|
||||
},
|
||||
secrets: {
|
||||
get: vi.fn().mockResolvedValue(undefined),
|
||||
store: vi.fn().mockResolvedValue(undefined),
|
||||
delete: vi.fn().mockResolvedValue(undefined),
|
||||
},
|
||||
extensionUri: { fsPath: "/mock/extension" },
|
||||
extension: { packageJSON: { version: "1.0.0" } },
|
||||
} as unknown as vscode.ExtensionContext
|
||||
const mockOutputChannel = {
|
||||
name: "test",
|
||||
appendLine: vi.fn(),
|
||||
append: vi.fn(),
|
||||
clear: vi.fn(),
|
||||
show: vi.fn(),
|
||||
hide: vi.fn(),
|
||||
dispose: vi.fn(),
|
||||
replace: vi.fn(),
|
||||
}
|
||||
mockProvider = new ClineProvider(
|
||||
mockExtensionContext,
|
||||
mockOutputChannel,
|
||||
"sidebar",
|
||||
new ContextProxy(mockExtensionContext),
|
||||
) as any
|
||||
mockApiConfig = {
|
||||
apiProvider: "anthropic",
|
||||
apiModelId: "claude-3-5-sonnet-20241022",
|
||||
apiKey: "test-key",
|
||||
}
|
||||
mockProvider.postMessageToWebview = vi.fn().mockResolvedValue(undefined)
|
||||
mockProvider.postStateToWebview = vi.fn().mockResolvedValue(undefined)
|
||||
mockProvider.updateTaskHistory = vi.fn().mockResolvedValue(undefined)
|
||||
})
|
||||
|
||||
it("updates last subagentRunning message with currentTask and calls postMessageToWebview", () => {
|
||||
const task = new Task({
|
||||
provider: mockProvider,
|
||||
apiConfiguration: mockApiConfig,
|
||||
task: "parent task",
|
||||
startTask: false,
|
||||
})
|
||||
const ts = 12345
|
||||
task.clineMessages = [
|
||||
{
|
||||
type: "say",
|
||||
say: "tool",
|
||||
text: JSON.stringify({ tool: "subagentRunning", description: "Explore codebase" }),
|
||||
ts,
|
||||
},
|
||||
]
|
||||
|
||||
task.reportSubagentProgress("Read file for src/index.ts")
|
||||
|
||||
const updated = JSON.parse(task.clineMessages[0].text!)
|
||||
expect(updated.currentTask).toBe("Read file for src/index.ts")
|
||||
expect(mockProvider.postMessageToWebview).toHaveBeenCalledWith({
|
||||
type: "messageUpdated",
|
||||
clineMessage: task.clineMessages[0],
|
||||
})
|
||||
})
|
||||
|
||||
it("does nothing when no subagentRunning message exists", () => {
|
||||
const task = new Task({
|
||||
provider: mockProvider,
|
||||
apiConfiguration: mockApiConfig,
|
||||
task: "parent task",
|
||||
startTask: false,
|
||||
})
|
||||
task.clineMessages = [{ type: "say", say: "tool", text: JSON.stringify({ tool: "runSlashCommand" }), ts: 1 }]
|
||||
|
||||
task.reportSubagentProgress("Step one")
|
||||
|
||||
expect(mockProvider.postMessageToWebview).not.toHaveBeenCalled()
|
||||
expect(task.clineMessages[0].text).not.toContain("currentTask")
|
||||
})
|
||||
|
||||
it("updates the last subagentRunning when multiple tool messages exist", () => {
|
||||
const task = new Task({
|
||||
provider: mockProvider,
|
||||
apiConfiguration: mockApiConfig,
|
||||
task: "parent task",
|
||||
startTask: false,
|
||||
})
|
||||
task.clineMessages = [
|
||||
{ type: "say", say: "tool", text: JSON.stringify({ tool: "readFile" }), ts: 1 },
|
||||
{
|
||||
type: "say",
|
||||
say: "tool",
|
||||
text: JSON.stringify({ tool: "subagentRunning", description: "Sub" }),
|
||||
ts: 2,
|
||||
},
|
||||
]
|
||||
|
||||
task.reportSubagentProgress("Attempt completion...")
|
||||
|
||||
const lastToolMsg = task.clineMessages[1]
|
||||
const parsed = JSON.parse(lastToolMsg.text!)
|
||||
expect(parsed.currentTask).toBe("Attempt completion...")
|
||||
expect(mockProvider.postMessageToWebview).toHaveBeenCalledWith({
|
||||
type: "messageUpdated",
|
||||
clineMessage: lastToolMsg,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import path from "path"
|
|||
|
||||
import type OpenAI from "openai"
|
||||
|
||||
import type { ProviderSettings, ModeConfig, ModelInfo } from "@roo-code/types"
|
||||
import type { ProviderSettings, ModeConfig, ModelInfo, ToolName } from "@roo-code/types"
|
||||
import { customToolRegistry, formatNative } from "@roo-code/core"
|
||||
|
||||
import type { ClineProvider } from "../webview/ClineProvider"
|
||||
|
|
@ -14,6 +14,42 @@ import {
|
|||
filterMcpToolsForMode,
|
||||
resolveToolAlias,
|
||||
} from "../prompts/tools/filter-tools-for-mode"
|
||||
import type { SubagentType } from "../../shared/subagent"
|
||||
import { TOOL_GROUPS } from "../../shared/tools"
|
||||
|
||||
/** Tools that require user interaction or affect parent-level UI. Disallowed for all subagents (general and explore). */
|
||||
const SUBAGENT_EXCLUDE_TOOLS: Set<string> = new Set([
|
||||
"ask_followup_question",
|
||||
"new_task",
|
||||
"switch_mode",
|
||||
"update_todo_list",
|
||||
] as ToolName[])
|
||||
|
||||
/**
|
||||
* Applies subagent-specific tool restrictions: for "explore" only read-only tools + attempt_completion;
|
||||
* for any subagent, excludes nested subagent and tools that require user interaction.
|
||||
*/
|
||||
function applySubagentToolRestrictions<T extends OpenAI.Chat.ChatCompletionTool>(
|
||||
tools: T[],
|
||||
subagentType: SubagentType | undefined,
|
||||
getName: (t: T) => string,
|
||||
resolveName: (name: string) => string,
|
||||
): T[] {
|
||||
let result = tools
|
||||
if (subagentType === "explore") {
|
||||
const readOnlyNames = new Set([...TOOL_GROUPS.read.tools, "attempt_completion"])
|
||||
result = result.filter((t) => readOnlyNames.has(resolveName(getName(t))))
|
||||
}
|
||||
if (subagentType !== undefined) {
|
||||
result = result.filter((t) => {
|
||||
const name = resolveName(getName(t))
|
||||
if (name === "subagent") return false
|
||||
if (SUBAGENT_EXCLUDE_TOOLS.has(name)) return false
|
||||
return true
|
||||
})
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
interface BuildToolsOptions {
|
||||
provider: ClineProvider
|
||||
|
|
@ -31,6 +67,8 @@ interface BuildToolsOptions {
|
|||
* to pass all tool definitions while restricting callable tools.
|
||||
*/
|
||||
includeAllToolsWithRestrictions?: boolean
|
||||
/** When "explore", only read-only tools are allowed (no edit/command) */
|
||||
subagentType?: "general" | "explore"
|
||||
}
|
||||
|
||||
interface BuildToolsResult {
|
||||
|
|
@ -90,6 +128,7 @@ export async function buildNativeToolsArrayWithRestrictions(options: BuildToolsO
|
|||
disabledTools,
|
||||
modelInfo,
|
||||
includeAllToolsWithRestrictions,
|
||||
subagentType,
|
||||
} = options
|
||||
|
||||
const mcpHub = provider.getMcpHub()
|
||||
|
|
@ -141,8 +180,9 @@ export async function buildNativeToolsArrayWithRestrictions(options: BuildToolsO
|
|||
}
|
||||
}
|
||||
|
||||
// Combine filtered tools (for backward compatibility and for allowedFunctionNames)
|
||||
const filteredTools = [...filteredNativeTools, ...filteredMcpTools, ...nativeCustomTools]
|
||||
// Combine filtered tools, then apply subagent restrictions when this task is a subagent
|
||||
const combined = [...filteredNativeTools, ...filteredMcpTools, ...nativeCustomTools]
|
||||
const filteredTools = applySubagentToolRestrictions(combined, subagentType, getToolName, resolveToolAlias)
|
||||
|
||||
// If includeAllToolsWithRestrictions is true, return ALL tools but provide
|
||||
// allowed names based on mode filtering
|
||||
|
|
@ -150,7 +190,7 @@ export async function buildNativeToolsArrayWithRestrictions(options: BuildToolsO
|
|||
// Combine ALL tools (unfiltered native + all MCP + custom)
|
||||
const allTools = [...nativeTools, ...mcpTools, ...nativeCustomTools]
|
||||
|
||||
// Extract names of tools that are allowed based on mode filtering.
|
||||
// Extract names of tools that are allowed based on mode filtering and subagent restrictions.
|
||||
// Resolve any alias names to canonical names to ensure consistency with allTools
|
||||
// (which uses canonical names). This prevents Gemini errors when tools are renamed
|
||||
// to aliases in filteredTools but allTools contains the original canonical names.
|
||||
|
|
|
|||
|
|
@ -80,6 +80,22 @@ export class AttemptCompletionTool extends BaseTool<"attempt_completion"> {
|
|||
|
||||
await task.say("completion_result", result, undefined, false)
|
||||
|
||||
// Force final token usage update before emitting TaskCompleted
|
||||
// This ensures the most recent stats are captured regardless of throttle timer
|
||||
// and properly updates the snapshot to prevent redundant emissions
|
||||
task.emitFinalTokenUsageUpdate()
|
||||
|
||||
TelemetryService.instance.captureTaskCompleted(task.taskId)
|
||||
task.emit(RooCodeEventName.TaskCompleted, task.taskId, task.getTokenUsage(), task.toolUsage)
|
||||
|
||||
if (task.backgroundCompletionResolve) {
|
||||
task.subagentProgressCallback = undefined
|
||||
task.backgroundCompletionResolve(result)
|
||||
task.backgroundCompletionResolve = undefined
|
||||
task.abortTask()
|
||||
return
|
||||
}
|
||||
|
||||
// Check for subtask using parentTaskId (metadata-driven delegation)
|
||||
if (task.parentTaskId) {
|
||||
// Check if this subtask has already completed and returned to parent
|
||||
|
|
|
|||
128
src/core/tools/SubagentTool.ts
Normal file
128
src/core/tools/SubagentTool.ts
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
import {
|
||||
SUBAGENT_CANCELLED_MODEL_MESSAGE,
|
||||
SUBAGENT_FAILED_MODEL_MESSAGE,
|
||||
SUBAGENT_STATUS_STARTING,
|
||||
SUBAGENT_TOOL_NAMES,
|
||||
type RunSubagentInBackgroundParams,
|
||||
type SubagentCompletedPayload,
|
||||
type SubagentRunningPayload,
|
||||
type SubagentStructuredResult,
|
||||
isSubagentRunner,
|
||||
} from "../../shared/subagent"
|
||||
import type { ToolUse } from "../../shared/tools"
|
||||
import { Task } from "../task/Task"
|
||||
import { formatResponse } from "../prompts/responses"
|
||||
import { BaseTool, ToolCallbacks } from "./BaseTool"
|
||||
|
||||
interface SubagentParams {
|
||||
description: string
|
||||
prompt: string
|
||||
subagent_type: "general" | "explore"
|
||||
}
|
||||
|
||||
export class SubagentTool extends BaseTool<"subagent"> {
|
||||
readonly name = "subagent" as const
|
||||
|
||||
parseLegacy(params: Partial<Record<string, string>>): SubagentParams {
|
||||
return {
|
||||
description: params.description ?? "",
|
||||
prompt: params.prompt ?? "",
|
||||
subagent_type: (params.subagent_type === "general" || params.subagent_type === "explore"
|
||||
? params.subagent_type
|
||||
: "general") as "general" | "explore",
|
||||
}
|
||||
}
|
||||
|
||||
async execute(params: SubagentParams, task: Task, callbacks: ToolCallbacks): Promise<void> {
|
||||
const { description, prompt, subagent_type } = params
|
||||
const { pushToolResult } = callbacks
|
||||
|
||||
const provider = task.providerRef.deref()
|
||||
if (!provider || !isSubagentRunner(provider)) {
|
||||
pushToolResult(formatResponse.toolError("Provider reference lost"))
|
||||
return
|
||||
}
|
||||
|
||||
if (!description?.trim()) {
|
||||
task.consecutiveMistakeCount++
|
||||
task.recordToolError("subagent")
|
||||
task.didToolFailInCurrentTurn = true
|
||||
pushToolResult(await task.sayAndCreateMissingParamError("subagent", "description"))
|
||||
return
|
||||
}
|
||||
if (!prompt?.trim()) {
|
||||
task.consecutiveMistakeCount++
|
||||
task.recordToolError("subagent")
|
||||
task.didToolFailInCurrentTurn = true
|
||||
pushToolResult(await task.sayAndCreateMissingParamError("subagent", "prompt"))
|
||||
return
|
||||
}
|
||||
|
||||
task.consecutiveMistakeCount = 0
|
||||
|
||||
const runningPayload: SubagentRunningPayload = {
|
||||
tool: SUBAGENT_TOOL_NAMES.running,
|
||||
description,
|
||||
currentTask: SUBAGENT_STATUS_STARTING,
|
||||
}
|
||||
const runningText = JSON.stringify(runningPayload)
|
||||
const progressStatus = { icon: "sync", spin: true }
|
||||
|
||||
await task.say("tool", runningText, undefined, true, undefined, progressStatus, {
|
||||
isNonInteractive: true,
|
||||
})
|
||||
|
||||
try {
|
||||
const runParams: RunSubagentInBackgroundParams = {
|
||||
parentTaskId: task.taskId,
|
||||
prompt,
|
||||
subagentType: subagent_type,
|
||||
onProgress: (currentTask) => task.reportSubagentProgress(currentTask),
|
||||
}
|
||||
const result = await provider.runSubagentInBackground(runParams)
|
||||
|
||||
const isStructured = (r: string | SubagentStructuredResult): r is SubagentStructuredResult =>
|
||||
typeof r === "object" && r !== null && "code" in r && "messageKey" in r
|
||||
|
||||
let completedPayload: SubagentCompletedPayload
|
||||
let toolResult: string
|
||||
if (isStructured(result)) {
|
||||
completedPayload = {
|
||||
tool: SUBAGENT_TOOL_NAMES.completed,
|
||||
description,
|
||||
result: SUBAGENT_CANCELLED_MODEL_MESSAGE,
|
||||
resultCode: result.code,
|
||||
messageKey: result.messageKey,
|
||||
}
|
||||
toolResult = SUBAGENT_CANCELLED_MODEL_MESSAGE
|
||||
} else {
|
||||
completedPayload = {
|
||||
tool: SUBAGENT_TOOL_NAMES.completed,
|
||||
description,
|
||||
result,
|
||||
}
|
||||
toolResult = result
|
||||
}
|
||||
const completedText = JSON.stringify(completedPayload)
|
||||
await task.say("tool", completedText, undefined, false, undefined, undefined, {
|
||||
isNonInteractive: true,
|
||||
})
|
||||
pushToolResult(formatResponse.toolResult(toolResult))
|
||||
} catch (error) {
|
||||
console.error("Subagent failed:", error)
|
||||
task.recordToolError("subagent")
|
||||
const errorPayload: SubagentCompletedPayload = {
|
||||
tool: SUBAGENT_TOOL_NAMES.completed,
|
||||
description,
|
||||
error: SUBAGENT_FAILED_MODEL_MESSAGE,
|
||||
}
|
||||
const errorPayloadStr = JSON.stringify(errorPayload)
|
||||
await task.say("tool", errorPayloadStr, undefined, false, undefined, undefined, {
|
||||
isNonInteractive: true,
|
||||
})
|
||||
pushToolResult(formatResponse.toolError(SUBAGENT_FAILED_MODEL_MESSAGE))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const subagentTool = new SubagentTool()
|
||||
196
src/core/tools/__tests__/SubagentTool.spec.ts
Normal file
196
src/core/tools/__tests__/SubagentTool.spec.ts
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
import {
|
||||
SUBAGENT_CANCELLED_MODEL_MESSAGE,
|
||||
SUBAGENT_CANCELLED_STRUCTURED_RESULT,
|
||||
SUBAGENT_FAILED_MODEL_MESSAGE,
|
||||
type SubagentStructuredResult,
|
||||
} from "../../../shared/subagent"
|
||||
import { subagentTool } from "../SubagentTool"
|
||||
import type { ToolUse } from "../../../shared/tools"
|
||||
|
||||
vi.mock("../../prompts/responses", () => ({
|
||||
formatResponse: {
|
||||
toolError: (msg: string) => `Tool Error: ${msg}`,
|
||||
toolResult: (content: string) => content,
|
||||
},
|
||||
}))
|
||||
|
||||
const mockRunSubagentInBackground =
|
||||
vi.fn<
|
||||
(p: {
|
||||
parentTaskId: string
|
||||
prompt: string
|
||||
subagentType: "general" | "explore"
|
||||
}) => Promise<string | SubagentStructuredResult>
|
||||
>()
|
||||
const mockSay = vi.fn()
|
||||
const mockPushToolResult = vi.fn()
|
||||
const mockHandleError = vi.fn()
|
||||
const mockSayAndCreateMissingParamError = vi.fn().mockResolvedValue("Missing param error")
|
||||
const mockRecordToolError = vi.fn()
|
||||
const mockReportSubagentProgress = vi.fn()
|
||||
|
||||
const mockTask = {
|
||||
taskId: "parent-1",
|
||||
consecutiveMistakeCount: 0,
|
||||
didToolFailInCurrentTurn: false,
|
||||
recordToolError: mockRecordToolError,
|
||||
sayAndCreateMissingParamError: mockSayAndCreateMissingParamError,
|
||||
say: mockSay,
|
||||
reportSubagentProgress: mockReportSubagentProgress,
|
||||
providerRef: {
|
||||
deref: () => ({
|
||||
runSubagentInBackground: mockRunSubagentInBackground,
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
describe("SubagentTool", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mockRunSubagentInBackground.mockResolvedValue("Subagent completed successfully.")
|
||||
})
|
||||
|
||||
describe("parseLegacy", () => {
|
||||
it("parses description, prompt, and subagent_type", () => {
|
||||
const params = subagentTool.parseLegacy({
|
||||
description: "Do something",
|
||||
prompt: "Detailed instructions",
|
||||
subagent_type: "explore",
|
||||
})
|
||||
expect(params).toEqual({
|
||||
description: "Do something",
|
||||
prompt: "Detailed instructions",
|
||||
subagent_type: "explore",
|
||||
})
|
||||
})
|
||||
|
||||
it("defaults subagent_type to general when invalid", () => {
|
||||
const params = subagentTool.parseLegacy({
|
||||
description: "X",
|
||||
prompt: "Y",
|
||||
subagent_type: "invalid",
|
||||
})
|
||||
expect(params.subagent_type).toBe("general")
|
||||
})
|
||||
})
|
||||
|
||||
describe("execute", () => {
|
||||
it("calls runSubagentInBackground and pushes result on success", async () => {
|
||||
const block: ToolUse<"subagent"> = {
|
||||
type: "tool_use",
|
||||
name: "subagent",
|
||||
params: {},
|
||||
partial: false,
|
||||
nativeArgs: {
|
||||
description: "Explore files",
|
||||
prompt: "List files in src/",
|
||||
subagent_type: "explore",
|
||||
},
|
||||
}
|
||||
|
||||
await subagentTool.execute(block.nativeArgs!, mockTask as any, {
|
||||
askApproval: vi.fn(),
|
||||
handleError: mockHandleError,
|
||||
pushToolResult: mockPushToolResult,
|
||||
})
|
||||
|
||||
expect(mockRunSubagentInBackground).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
parentTaskId: "parent-1",
|
||||
prompt: "List files in src/",
|
||||
subagentType: "explore",
|
||||
}),
|
||||
)
|
||||
expect(mockRunSubagentInBackground).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
onProgress: expect.any(Function),
|
||||
}),
|
||||
)
|
||||
expect(mockSay).toHaveBeenCalled()
|
||||
expect(mockPushToolResult).toHaveBeenCalledWith("Subagent completed successfully.")
|
||||
})
|
||||
|
||||
it("sends completed payload with resultCode/messageKey and pushes model message when result is structured (cancelled)", async () => {
|
||||
mockRunSubagentInBackground.mockResolvedValue(SUBAGENT_CANCELLED_STRUCTURED_RESULT)
|
||||
|
||||
await subagentTool.execute(
|
||||
{ description: "Do X", prompt: "Do it", subagent_type: "general" },
|
||||
mockTask as any,
|
||||
{
|
||||
askApproval: vi.fn(),
|
||||
handleError: mockHandleError,
|
||||
pushToolResult: mockPushToolResult,
|
||||
},
|
||||
)
|
||||
|
||||
const sayCalls = mockSay.mock.calls
|
||||
const completedCall = sayCalls.find((c) => {
|
||||
try {
|
||||
const payload = JSON.parse(c[1])
|
||||
return payload.tool === "subagentCompleted"
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
})
|
||||
expect(completedCall).toBeDefined()
|
||||
const payload = JSON.parse(completedCall![1])
|
||||
expect(payload.resultCode).toBe("CANCELLED")
|
||||
expect(payload.messageKey).toBe("chat:subagents.cancelledByUser")
|
||||
expect(payload.result).toBe(SUBAGENT_CANCELLED_MODEL_MESSAGE)
|
||||
expect(mockPushToolResult).toHaveBeenCalledWith(SUBAGENT_CANCELLED_MODEL_MESSAGE)
|
||||
})
|
||||
|
||||
it("pushes error when description is missing", async () => {
|
||||
await subagentTool.execute(
|
||||
{ description: "", prompt: "Do it", subagent_type: "general" },
|
||||
mockTask as any,
|
||||
{
|
||||
askApproval: vi.fn(),
|
||||
handleError: mockHandleError,
|
||||
pushToolResult: mockPushToolResult,
|
||||
},
|
||||
)
|
||||
|
||||
expect(mockRunSubagentInBackground).not.toHaveBeenCalled()
|
||||
expect(mockSayAndCreateMissingParamError).toHaveBeenCalledWith("subagent", "description")
|
||||
expect(mockPushToolResult).toHaveBeenCalledWith("Missing param error")
|
||||
})
|
||||
|
||||
it("pushes error when prompt is missing", async () => {
|
||||
await subagentTool.execute({ description: "X", prompt: "", subagent_type: "general" }, mockTask as any, {
|
||||
askApproval: vi.fn(),
|
||||
handleError: mockHandleError,
|
||||
pushToolResult: mockPushToolResult,
|
||||
})
|
||||
|
||||
expect(mockRunSubagentInBackground).not.toHaveBeenCalled()
|
||||
expect(mockSayAndCreateMissingParamError).toHaveBeenCalledWith("subagent", "prompt")
|
||||
})
|
||||
|
||||
it("logs error and pushes generic tool error when runSubagentInBackground rejects (no raw message leak)", async () => {
|
||||
mockRunSubagentInBackground.mockRejectedValue(new Error("API failed"))
|
||||
|
||||
await subagentTool.execute({ description: "X", prompt: "Y", subagent_type: "general" }, mockTask as any, {
|
||||
askApproval: vi.fn(),
|
||||
handleError: mockHandleError,
|
||||
pushToolResult: mockPushToolResult,
|
||||
})
|
||||
|
||||
expect(mockHandleError).not.toHaveBeenCalled()
|
||||
expect(mockPushToolResult).toHaveBeenCalledWith(expect.stringContaining(SUBAGENT_FAILED_MODEL_MESSAGE))
|
||||
expect(mockRecordToolError).toHaveBeenCalledWith("subagent")
|
||||
const sayCalls = mockSay.mock.calls
|
||||
const completedCall = sayCalls.find((c) => {
|
||||
try {
|
||||
const payload = JSON.parse(c[1])
|
||||
return payload.tool === "subagentCompleted" && payload.error !== undefined
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
})
|
||||
expect(completedCall).toBeDefined()
|
||||
const payload = JSON.parse(completedCall![1])
|
||||
expect(payload.error).toBe(SUBAGENT_FAILED_MODEL_MESSAGE)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -60,6 +60,11 @@ import { experimentDefault } from "../../shared/experiments"
|
|||
import { formatLanguage } from "../../shared/language"
|
||||
import { WebviewMessage } from "../../shared/WebviewMessage"
|
||||
import { EMBEDDING_MODEL_PROFILES } from "../../shared/embeddingModels"
|
||||
import {
|
||||
SUBAGENT_CANCELLED_STRUCTURED_RESULT,
|
||||
type RunSubagentInBackgroundParams,
|
||||
type SubagentStructuredResult,
|
||||
} from "../../shared/subagent"
|
||||
import { ProfileValidator } from "../../shared/ProfileValidator"
|
||||
|
||||
import { Terminal } from "../../integrations/terminal/Terminal"
|
||||
|
|
@ -2987,6 +2992,22 @@ export class ClineProvider
|
|||
return
|
||||
}
|
||||
|
||||
// If the current task has a running subagent, cancel only the subagent and return the cancellation result to the parent.
|
||||
const subagentChild = task.activeSubagentChild
|
||||
if (subagentChild) {
|
||||
task.activeSubagentChild = undefined
|
||||
subagentChild.subagentProgressCallback = undefined
|
||||
if (subagentChild.backgroundCompletionResolve) {
|
||||
subagentChild.backgroundCompletionResolve(SUBAGENT_CANCELLED_STRUCTURED_RESULT)
|
||||
subagentChild.backgroundCompletionResolve = undefined
|
||||
}
|
||||
subagentChild.abandoned = true
|
||||
subagentChild.cancelCurrentRequest()
|
||||
subagentChild.abortTask()
|
||||
this.log(`[cancelTask] cancelled subagent ${subagentChild.taskId}.${subagentChild.instanceId}`)
|
||||
return
|
||||
}
|
||||
|
||||
console.log(`[cancelTask] cancelling task ${task.taskId}.${task.instanceId}`)
|
||||
|
||||
let historyItem: HistoryItem | undefined
|
||||
|
|
@ -3360,6 +3381,85 @@ export class ClineProvider
|
|||
return child
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a subagent in the background. Parent stays current; child runs until attempt_completion.
|
||||
* Resolves with the completion result string, or rejects on error.
|
||||
*/
|
||||
public async runSubagentInBackground(
|
||||
params: RunSubagentInBackgroundParams,
|
||||
): Promise<string | SubagentStructuredResult> {
|
||||
const { parentTaskId, prompt, subagentType, onProgress } = params
|
||||
const parent = this.getCurrentTask()
|
||||
if (!parent) {
|
||||
throw new Error("[runSubagentInBackground] No current task")
|
||||
}
|
||||
if (parent.taskId !== parentTaskId) {
|
||||
throw new Error(
|
||||
`[runSubagentInBackground] Parent mismatch: expected ${parentTaskId}, current ${parent.taskId}`,
|
||||
)
|
||||
}
|
||||
|
||||
const { apiConfiguration, organizationAllowList, enableCheckpoints, checkpointTimeout, experiments } =
|
||||
await this.getState()
|
||||
|
||||
if (!ProfileValidator.isProfileAllowed(apiConfiguration, organizationAllowList)) {
|
||||
throw new OrganizationAllowListViolationError(t("common:errors.violated_organization_allowlist"))
|
||||
}
|
||||
|
||||
const child = new Task({
|
||||
provider: this,
|
||||
apiConfiguration,
|
||||
enableCheckpoints,
|
||||
checkpointTimeout,
|
||||
consecutiveMistakeLimit: apiConfiguration.consecutiveMistakeLimit,
|
||||
task: prompt,
|
||||
images: undefined,
|
||||
experiments,
|
||||
rootTask: this.clineStack.length > 0 ? this.clineStack[0] : undefined,
|
||||
parentTask: parent,
|
||||
taskNumber: this.clineStack.length + 1,
|
||||
onCreated: this.taskCreationCallback,
|
||||
startTask: false,
|
||||
subagentType,
|
||||
initialStatus: "active",
|
||||
})
|
||||
if (onProgress) {
|
||||
child.subagentProgressCallback = onProgress
|
||||
}
|
||||
|
||||
parent.activeSubagentChild = child
|
||||
|
||||
return new Promise<string | SubagentStructuredResult>((resolve, reject) => {
|
||||
let settled = false
|
||||
child.backgroundCompletionResolve = (result: string | SubagentStructuredResult) => {
|
||||
if (!settled) {
|
||||
settled = true
|
||||
parent.activeSubagentChild = undefined
|
||||
resolve(result)
|
||||
}
|
||||
}
|
||||
child
|
||||
.runBackgroundSubagentLoop(prompt)
|
||||
.then(() => {
|
||||
if (!settled) {
|
||||
settled = true
|
||||
parent.activeSubagentChild = undefined
|
||||
reject(new Error("Subagent ended without attempt_completion"))
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
if (!settled) {
|
||||
settled = true
|
||||
parent.activeSubagentChild = undefined
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
parent.activeSubagentChild = undefined
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Reopen parent task from delegation with write-back and events.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ describe("experiments", () => {
|
|||
imageGeneration: false,
|
||||
runSlashCommand: false,
|
||||
customTools: false,
|
||||
subagent: false,
|
||||
}
|
||||
expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.PREVENT_FOCUS_DISRUPTION)).toBe(false)
|
||||
})
|
||||
|
|
@ -31,6 +32,7 @@ describe("experiments", () => {
|
|||
imageGeneration: false,
|
||||
runSlashCommand: false,
|
||||
customTools: false,
|
||||
subagent: false,
|
||||
}
|
||||
expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.PREVENT_FOCUS_DISRUPTION)).toBe(true)
|
||||
})
|
||||
|
|
@ -41,6 +43,7 @@ describe("experiments", () => {
|
|||
imageGeneration: false,
|
||||
runSlashCommand: false,
|
||||
customTools: false,
|
||||
subagent: false,
|
||||
}
|
||||
expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.PREVENT_FOCUS_DISRUPTION)).toBe(false)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ export const EXPERIMENT_IDS = {
|
|||
IMAGE_GENERATION: "imageGeneration",
|
||||
RUN_SLASH_COMMAND: "runSlashCommand",
|
||||
CUSTOM_TOOLS: "customTools",
|
||||
SUBAGENT: "subagent",
|
||||
} as const satisfies Record<string, ExperimentId>
|
||||
|
||||
type _AssertExperimentIds = AssertEqual<Equals<ExperimentId, Values<typeof EXPERIMENT_IDS>>>
|
||||
|
|
@ -20,6 +21,7 @@ export const experimentConfigsMap: Record<ExperimentKey, ExperimentConfig> = {
|
|||
IMAGE_GENERATION: { enabled: false },
|
||||
RUN_SLASH_COMMAND: { enabled: false },
|
||||
CUSTOM_TOOLS: { enabled: false },
|
||||
SUBAGENT: { enabled: false },
|
||||
}
|
||||
|
||||
export const experimentDefault = Object.fromEntries(
|
||||
|
|
|
|||
74
src/shared/subagent.ts
Normal file
74
src/shared/subagent.ts
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
/**
|
||||
* Shared constants and types for the subagent feature.
|
||||
* Used by SubagentTool, Task, build-tools, and UI to stay in sync with tool message shapes.
|
||||
*/
|
||||
|
||||
/** Tool names used in say("tool", ...) payloads for subagent progress and completion. */
|
||||
export const SUBAGENT_TOOL_NAMES = {
|
||||
running: "subagentRunning",
|
||||
completed: "subagentCompleted",
|
||||
} as const
|
||||
|
||||
/** i18n keys sent as currentTask; webview uses t(key) to show locale. */
|
||||
export const SUBAGENT_STATUS_STARTING = "chat:subagents.starting"
|
||||
export const SUBAGENT_STATUS_THINKING = "chat:subagents.thinking"
|
||||
|
||||
/** Structured result codes for subagent completion; webview uses messageKey with t() for display. */
|
||||
export const SUBAGENT_RESULT_CODE_CANCELLED = "CANCELLED" as const
|
||||
export const SUBAGENT_CANCELLED_MESSAGE_KEY = "chat:subagents.cancelledByUser" as const
|
||||
|
||||
/** Sent when the user cancels the subagent. Extension passes this to backgroundCompletionResolve. */
|
||||
export const SUBAGENT_CANCELLED_STRUCTURED_RESULT = {
|
||||
code: SUBAGENT_RESULT_CODE_CANCELLED,
|
||||
messageKey: SUBAGENT_CANCELLED_MESSAGE_KEY,
|
||||
} as const
|
||||
|
||||
/** English message shown to the model when subagent is cancelled (tool result). */
|
||||
export const SUBAGENT_CANCELLED_MODEL_MESSAGE = "Subagent was cancelled by the user."
|
||||
|
||||
/** Generic message shown to the model/UI when subagent fails (avoids leaking internal error details). */
|
||||
export const SUBAGENT_FAILED_MODEL_MESSAGE = "The subagent failed."
|
||||
|
||||
export type SubagentStructuredResult = typeof SUBAGENT_CANCELLED_STRUCTURED_RESULT
|
||||
|
||||
/** Payload for the "subagentRunning" tool message (progress updates). */
|
||||
export interface SubagentRunningPayload {
|
||||
tool: typeof SUBAGENT_TOOL_NAMES.running
|
||||
description?: string
|
||||
currentTask?: string
|
||||
}
|
||||
|
||||
/** Payload for the "subagentCompleted" tool message (result or error). */
|
||||
export interface SubagentCompletedPayload {
|
||||
tool: typeof SUBAGENT_TOOL_NAMES.completed
|
||||
description?: string
|
||||
result?: string
|
||||
error?: string
|
||||
/** When set, webview shows t(messageKey) instead of result/error. */
|
||||
resultCode?: string
|
||||
messageKey?: string
|
||||
}
|
||||
|
||||
export type SubagentType = "general" | "explore"
|
||||
|
||||
/** Parameters for running a subagent in the background. */
|
||||
export interface RunSubagentInBackgroundParams {
|
||||
parentTaskId: string
|
||||
prompt: string
|
||||
subagentType: SubagentType
|
||||
onProgress?: (currentTask: string) => void
|
||||
}
|
||||
|
||||
/** Provider interface for running a subagent. Allows SubagentTool to call without type assertion. */
|
||||
export interface SubagentRunner {
|
||||
runSubagentInBackground(params: RunSubagentInBackgroundParams): Promise<string | SubagentStructuredResult>
|
||||
}
|
||||
|
||||
export function isSubagentRunner(provider: unknown): provider is SubagentRunner {
|
||||
return (
|
||||
typeof provider === "object" &&
|
||||
provider !== null &&
|
||||
"runSubagentInBackground" in provider &&
|
||||
typeof (provider as SubagentRunner).runSubagentInBackground === "function"
|
||||
)
|
||||
}
|
||||
|
|
@ -57,6 +57,8 @@ export const toolParamNames = [
|
|||
"end_line",
|
||||
"todos",
|
||||
"prompt",
|
||||
"description", // subagent short label
|
||||
"subagent_type",
|
||||
"image",
|
||||
// read_file parameters (native protocol)
|
||||
"operations", // search_and_replace parameter for multiple operations
|
||||
|
|
@ -116,6 +118,7 @@ export type NativeToolArgs = {
|
|||
update_todo_list: { todos: string }
|
||||
use_mcp_tool: { server_name: string; tool_name: string; arguments?: Record<string, unknown> }
|
||||
write_to_file: { path: string; content: string }
|
||||
subagent: { description: string; prompt: string; subagent_type: "general" | "explore" }
|
||||
// Add more tools as they are migrated to native protocol
|
||||
}
|
||||
|
||||
|
|
@ -290,6 +293,7 @@ export const TOOL_DISPLAY_NAMES: Record<ToolName, string> = {
|
|||
skill: "load skill",
|
||||
generate_image: "generate images",
|
||||
custom_tool: "use custom tools",
|
||||
subagent: "run subagent",
|
||||
} as const
|
||||
|
||||
// Define available tool groups.
|
||||
|
|
@ -322,6 +326,7 @@ export const ALWAYS_AVAILABLE_TOOLS: ToolName[] = [
|
|||
"update_todo_list",
|
||||
"run_slash_command",
|
||||
"skill",
|
||||
"subagent",
|
||||
] as const
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -76,6 +76,50 @@ import { cn } from "@/lib/utils"
|
|||
import { PathTooltip } from "../ui/PathTooltip"
|
||||
import { OpenMarkdownPreviewButton } from "./OpenMarkdownPreviewButton"
|
||||
|
||||
/** i18n keys backend sends as currentTask; we pass to t() for display. */
|
||||
const SUBAGENT_STATUS_KEYS = ["chat:subagents.starting", "chat:subagents.thinking"] as const
|
||||
|
||||
interface ParsedSubagentTask {
|
||||
toolLabel: string
|
||||
purpose: string | null
|
||||
isGeneric: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses backend progress strings like "[read_file for 15 files]" or "Thinking..." into
|
||||
* tool label and purpose for highlighted display. getToolLabel(key) returns the localized tool name.
|
||||
*/
|
||||
function parseSubagentCurrentTask(raw: string, getToolLabel: (toolKey: string) => string): ParsedSubagentTask {
|
||||
const s = raw.trim()
|
||||
if (!s) return { toolLabel: "", purpose: "...", isGeneric: true }
|
||||
|
||||
const fallbackFormat = (str: string) => str.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase())
|
||||
|
||||
// Generic status (no brackets) – show as-is
|
||||
if (!s.startsWith("[")) {
|
||||
return { toolLabel: "", purpose: s.endsWith("...") ? s : s + "...", isGeneric: true }
|
||||
}
|
||||
|
||||
// Strip brackets and parse "[tool_name for purpose]"
|
||||
const inner = s.slice(1, s.endsWith("]") ? s.length - 1 : s.length).trim()
|
||||
const forIndex = inner.indexOf(" for ")
|
||||
if (forIndex === -1) {
|
||||
const toolKey = inner.replace(/\s+/g, "_").toLowerCase()
|
||||
const toolLabel = getToolLabel(toolKey) || fallbackFormat(inner)
|
||||
return { toolLabel, purpose: null, isGeneric: false }
|
||||
}
|
||||
|
||||
const toolPart = inner.slice(0, forIndex).trim()
|
||||
const purposePart = inner.slice(forIndex + 5).trim()
|
||||
const toolKey = toolPart.replace(/\s+/g, "_").toLowerCase()
|
||||
const toolLabel = getToolLabel(toolKey) || fallbackFormat(toolPart)
|
||||
let purpose = purposePart
|
||||
if ((purpose.startsWith("'") && purpose.endsWith("'")) || (purpose.startsWith('"') && purpose.endsWith('"'))) {
|
||||
purpose = purpose.slice(1, -1)
|
||||
}
|
||||
return { toolLabel, purpose: purpose || null, isGeneric: false }
|
||||
}
|
||||
|
||||
// Helper function to get previous todos before a specific message
|
||||
function getPreviousTodos(messages: ClineMessage[], currentMessageTs: number): any[] {
|
||||
// Find the previous updateTodoList message before the current one
|
||||
|
|
@ -1518,6 +1562,91 @@ export const ChatRowContent = ({
|
|||
</div>
|
||||
)
|
||||
}
|
||||
case "subagentRunning": {
|
||||
const desc = sayTool.description ?? ""
|
||||
const currentTask = sayTool.currentTask
|
||||
const getToolLabel = (key: string) =>
|
||||
t("chat:subagents.toolDisplayNames." + key, {
|
||||
defaultValue: key.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()),
|
||||
})
|
||||
const parsed = currentTask ? parseSubagentCurrentTask(currentTask, getToolLabel) : null
|
||||
const purposeKey = parsed?.purpose?.replace(/\.\.\.$/, "")
|
||||
const displayPurpose =
|
||||
purposeKey &&
|
||||
SUBAGENT_STATUS_KEYS.includes(purposeKey as (typeof SUBAGENT_STATUS_KEYS)[number])
|
||||
? t(purposeKey)
|
||||
: parsed?.purpose
|
||||
return (
|
||||
<div>
|
||||
<div className="flex items-center gap-2.5 mb-1 break-words">
|
||||
{message.progressStatus?.icon && (
|
||||
<span
|
||||
className={cn(
|
||||
"codicon",
|
||||
`codicon-${message.progressStatus.icon}`,
|
||||
"mr-1.5 text-vscode-foreground",
|
||||
message.progressStatus.spin && "codicon-modifier-spin",
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
<span>{t("chat:subagents.runningLabel", { description: desc })}</span>
|
||||
</div>
|
||||
{parsed && (
|
||||
<div className="pl-6 mt-1.5 text-sm text-vscode-descriptionForeground">
|
||||
{parsed.toolLabel ? (
|
||||
displayPurpose ? (
|
||||
<>
|
||||
{parsed.toolLabel}: {displayPurpose}
|
||||
{!parsed.purpose?.endsWith("...") && "..."}
|
||||
</>
|
||||
) : (
|
||||
<>{parsed.toolLabel}...</>
|
||||
)
|
||||
) : (
|
||||
<>
|
||||
{displayPurpose?.endsWith("...")
|
||||
? displayPurpose
|
||||
: `${displayPurpose ?? ""}...`}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
case "subagentCompleted": {
|
||||
const hasError = !!sayTool.error
|
||||
return (
|
||||
<>
|
||||
<div className="flex items-center gap-2.5 mb-1 break-words">
|
||||
<span
|
||||
className={cn(
|
||||
`codicon codicon-${hasError ? "error" : "check-all"}`,
|
||||
"mr-1.5",
|
||||
hasError ? "text-vscode-errorForeground" : "text-vscode-foreground",
|
||||
)}
|
||||
/>
|
||||
<span>{t("chat:subagents.completedLabel")}</span>
|
||||
{sayTool.description && (
|
||||
<span className="text-vscode-descriptionForeground ml-2">
|
||||
{sayTool.description}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
{(sayTool.result ||
|
||||
sayTool.error ||
|
||||
(sayTool.resultCode && sayTool.messageKey)) && (
|
||||
<div className="pl-6 mt-2 text-vscode-descriptionForeground whitespace-pre-wrap">
|
||||
{sayTool.resultCode === "CANCELLED" && sayTool.messageKey
|
||||
? t(sayTool.messageKey)
|
||||
: hasError
|
||||
? sayTool.error
|
||||
: sayTool.result}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
default:
|
||||
return null
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { memo, useMemo } from "react"
|
||||
import { VSCodeProgressRing } from "@vscode/webview-ui-toolkit/react"
|
||||
import { type ToolProgressStatus } from "@roo-code/types"
|
||||
import { cn } from "@src/lib/utils"
|
||||
import { getLanguageFromPath } from "@src/utils/getLanguageFromPath"
|
||||
import { formatPathTooltip } from "@src/utils/formatPathTooltip"
|
||||
|
||||
|
|
@ -90,7 +91,14 @@ const CodeAccordion = ({
|
|||
progressStatus.text && (
|
||||
<>
|
||||
{progressStatus.icon && (
|
||||
<span className={`codicon codicon-${progressStatus.icon} mr-1`} />
|
||||
<span
|
||||
className={cn(
|
||||
"codicon",
|
||||
`codicon-${progressStatus.icon}`,
|
||||
"mr-1",
|
||||
progressStatus.spin && "codicon-modifier-spin",
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
<span className="mr-1 ml-auto text-vscode-descriptionForeground">
|
||||
{progressStatus.text}
|
||||
|
|
|
|||
|
|
@ -234,6 +234,7 @@ describe("mergeExtensionState", () => {
|
|||
imageGeneration: false,
|
||||
runSlashCommand: false,
|
||||
customTools: false,
|
||||
subagent: false,
|
||||
} as Record<ExperimentId, boolean>,
|
||||
checkpointTimeout: DEFAULT_CHECKPOINT_TIMEOUT_SECONDS + 5,
|
||||
}
|
||||
|
|
@ -250,6 +251,7 @@ describe("mergeExtensionState", () => {
|
|||
imageGeneration: false,
|
||||
runSlashCommand: false,
|
||||
customTools: false,
|
||||
subagent: false,
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
|||
30
webview-ui/src/i18n/locales/ca/chat.json
generated
30
webview-ui/src/i18n/locales/ca/chat.json
generated
|
|
@ -269,6 +269,36 @@
|
|||
"completionInstructions": "Subtasca completada! Pots revisar els resultats i suggerir correccions o següents passos. Si tot sembla correcte, confirma per tornar el resultat a la tasca principal.",
|
||||
"goToSubtask": "Veure tasca"
|
||||
},
|
||||
"subagents": {
|
||||
"runningLabel": "Running subagent: {{description}}",
|
||||
"completedLabel": "Subagent completed",
|
||||
"cancelledByUser": "Subagent was cancelled by the user.",
|
||||
"starting": "Starting...",
|
||||
"thinking": "Thinking...",
|
||||
"toolDisplayNames": {
|
||||
"read_file": "Read file",
|
||||
"write_to_file": "Write file",
|
||||
"apply_diff": "Apply diff",
|
||||
"search_files": "Search files",
|
||||
"search_and_replace": "Search and replace",
|
||||
"search_replace": "Search replace",
|
||||
"edit_file": "Edit file",
|
||||
"list_files": "List files",
|
||||
"codebase_search": "Codebase search",
|
||||
"execute_command": "Run command",
|
||||
"run_slash_command": "Slash command",
|
||||
"ask_followup_question": "Ask question",
|
||||
"attempt_completion": "Complete",
|
||||
"update_todo_list": "Update todos",
|
||||
"switch_mode": "Switch mode",
|
||||
"new_task": "New task",
|
||||
"generate_image": "Generate image",
|
||||
"use_mcp_tool": "MCP tool",
|
||||
"access_mcp_resource": "MCP resource",
|
||||
"apply_patch": "Apply patch",
|
||||
"subagent": "Subagent"
|
||||
}
|
||||
},
|
||||
"questions": {
|
||||
"hasQuestion": "Roo té una pregunta"
|
||||
},
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/ca/settings.json
generated
4
webview-ui/src/i18n/locales/ca/settings.json
generated
|
|
@ -823,6 +823,10 @@
|
|||
"refreshSuccess": "Eines actualitzades correctament",
|
||||
"refreshError": "Error en actualitzar les eines",
|
||||
"toolParameters": "Paràmetres"
|
||||
},
|
||||
"SUBAGENT": {
|
||||
"name": "Subagent tool",
|
||||
"description": "When enabled, the model can run subagents in the background for research or sub-tasks. The subagent's result is returned to the main task. Use \"explore\" for read-only sub-tasks or \"general\" when the sub-task may edit files or run commands."
|
||||
}
|
||||
},
|
||||
"promptCaching": {
|
||||
|
|
|
|||
30
webview-ui/src/i18n/locales/de/chat.json
generated
30
webview-ui/src/i18n/locales/de/chat.json
generated
|
|
@ -269,6 +269,36 @@
|
|||
"completionInstructions": "Teilaufgabe abgeschlossen! Du kannst die Ergebnisse überprüfen und Korrekturen oder nächste Schritte vorschlagen. Wenn alles gut aussieht, bestätige, um das Ergebnis an die übergeordnete Aufgabe zurückzugeben.",
|
||||
"goToSubtask": "Aufgabe anzeigen"
|
||||
},
|
||||
"subagents": {
|
||||
"runningLabel": "Running subagent: {{description}}",
|
||||
"completedLabel": "Subagent completed",
|
||||
"cancelledByUser": "Subagent was cancelled by the user.",
|
||||
"starting": "Starting...",
|
||||
"thinking": "Thinking...",
|
||||
"toolDisplayNames": {
|
||||
"read_file": "Read file",
|
||||
"write_to_file": "Write file",
|
||||
"apply_diff": "Apply diff",
|
||||
"search_files": "Search files",
|
||||
"search_and_replace": "Search and replace",
|
||||
"search_replace": "Search replace",
|
||||
"edit_file": "Edit file",
|
||||
"list_files": "List files",
|
||||
"codebase_search": "Codebase search",
|
||||
"execute_command": "Run command",
|
||||
"run_slash_command": "Slash command",
|
||||
"ask_followup_question": "Ask question",
|
||||
"attempt_completion": "Complete",
|
||||
"update_todo_list": "Update todos",
|
||||
"switch_mode": "Switch mode",
|
||||
"new_task": "New task",
|
||||
"generate_image": "Generate image",
|
||||
"use_mcp_tool": "MCP tool",
|
||||
"access_mcp_resource": "MCP resource",
|
||||
"apply_patch": "Apply patch",
|
||||
"subagent": "Subagent"
|
||||
}
|
||||
},
|
||||
"questions": {
|
||||
"hasQuestion": "Roo hat eine Frage"
|
||||
},
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/de/settings.json
generated
4
webview-ui/src/i18n/locales/de/settings.json
generated
|
|
@ -823,6 +823,10 @@
|
|||
"refreshSuccess": "Tools erfolgreich aktualisiert",
|
||||
"refreshError": "Fehler beim Aktualisieren der Tools",
|
||||
"toolParameters": "Parameter"
|
||||
},
|
||||
"SUBAGENT": {
|
||||
"name": "Subagent tool",
|
||||
"description": "When enabled, the model can run subagents in the background for research or sub-tasks. The subagent's result is returned to the main task. Use \"explore\" for read-only sub-tasks or \"general\" when the sub-task may edit files or run commands."
|
||||
}
|
||||
},
|
||||
"promptCaching": {
|
||||
|
|
|
|||
|
|
@ -311,6 +311,36 @@
|
|||
"completionInstructions": "You can review the results and suggest any corrections or next steps. If everything looks good, confirm to return the result to the parent task.",
|
||||
"goToSubtask": "View task"
|
||||
},
|
||||
"subagents": {
|
||||
"runningLabel": "Running subagent: {{description}}",
|
||||
"completedLabel": "Subagent completed",
|
||||
"cancelledByUser": "Subagent was cancelled by the user.",
|
||||
"starting": "Starting...",
|
||||
"thinking": "Thinking...",
|
||||
"toolDisplayNames": {
|
||||
"read_file": "Read file",
|
||||
"write_to_file": "Write file",
|
||||
"apply_diff": "Apply diff",
|
||||
"search_files": "Search files",
|
||||
"search_and_replace": "Search and replace",
|
||||
"search_replace": "Search replace",
|
||||
"edit_file": "Edit file",
|
||||
"list_files": "List files",
|
||||
"codebase_search": "Codebase search",
|
||||
"execute_command": "Run command",
|
||||
"run_slash_command": "Slash command",
|
||||
"ask_followup_question": "Ask question",
|
||||
"attempt_completion": "Complete",
|
||||
"update_todo_list": "Update todos",
|
||||
"switch_mode": "Switch mode",
|
||||
"new_task": "New task",
|
||||
"generate_image": "Generate image",
|
||||
"use_mcp_tool": "MCP tool",
|
||||
"access_mcp_resource": "MCP resource",
|
||||
"apply_patch": "Apply patch",
|
||||
"subagent": "Subagent"
|
||||
}
|
||||
},
|
||||
"questions": {
|
||||
"hasQuestion": "Roo has a question"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -886,6 +886,10 @@
|
|||
"refreshSuccess": "Tools refreshed successfully",
|
||||
"refreshError": "Failed to refresh tools",
|
||||
"toolParameters": "Parameters"
|
||||
},
|
||||
"SUBAGENT": {
|
||||
"name": "Subagent tool",
|
||||
"description": "When enabled, the model can run subagents in the background for research or sub-tasks. The subagent's result is returned to the main task. Use \"explore\" for read-only sub-tasks or \"general\" when the sub-task may edit files or run commands."
|
||||
}
|
||||
},
|
||||
"promptCaching": {
|
||||
|
|
|
|||
30
webview-ui/src/i18n/locales/es/chat.json
generated
30
webview-ui/src/i18n/locales/es/chat.json
generated
|
|
@ -269,6 +269,36 @@
|
|||
"completionInstructions": "¡Subtarea completada! Puedes revisar los resultados y sugerir correcciones o próximos pasos. Si todo se ve bien, confirma para devolver el resultado a la tarea principal.",
|
||||
"goToSubtask": "Ver tarea"
|
||||
},
|
||||
"subagents": {
|
||||
"runningLabel": "Running subagent: {{description}}",
|
||||
"completedLabel": "Subagent completed",
|
||||
"cancelledByUser": "Subagent was cancelled by the user.",
|
||||
"starting": "Starting...",
|
||||
"thinking": "Thinking...",
|
||||
"toolDisplayNames": {
|
||||
"read_file": "Read file",
|
||||
"write_to_file": "Write file",
|
||||
"apply_diff": "Apply diff",
|
||||
"search_files": "Search files",
|
||||
"search_and_replace": "Search and replace",
|
||||
"search_replace": "Search replace",
|
||||
"edit_file": "Edit file",
|
||||
"list_files": "List files",
|
||||
"codebase_search": "Codebase search",
|
||||
"execute_command": "Run command",
|
||||
"run_slash_command": "Slash command",
|
||||
"ask_followup_question": "Ask question",
|
||||
"attempt_completion": "Complete",
|
||||
"update_todo_list": "Update todos",
|
||||
"switch_mode": "Switch mode",
|
||||
"new_task": "New task",
|
||||
"generate_image": "Generate image",
|
||||
"use_mcp_tool": "MCP tool",
|
||||
"access_mcp_resource": "MCP resource",
|
||||
"apply_patch": "Apply patch",
|
||||
"subagent": "Subagent"
|
||||
}
|
||||
},
|
||||
"questions": {
|
||||
"hasQuestion": "Roo tiene una pregunta"
|
||||
},
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/es/settings.json
generated
4
webview-ui/src/i18n/locales/es/settings.json
generated
|
|
@ -823,6 +823,10 @@
|
|||
"refreshSuccess": "Herramientas actualizadas correctamente",
|
||||
"refreshError": "Error al actualizar las herramientas",
|
||||
"toolParameters": "Parámetros"
|
||||
},
|
||||
"SUBAGENT": {
|
||||
"name": "Subagent tool",
|
||||
"description": "When enabled, the model can run subagents in the background for research or sub-tasks. The subagent's result is returned to the main task. Use \"explore\" for read-only sub-tasks or \"general\" when the sub-task may edit files or run commands."
|
||||
}
|
||||
},
|
||||
"promptCaching": {
|
||||
|
|
|
|||
30
webview-ui/src/i18n/locales/fr/chat.json
generated
30
webview-ui/src/i18n/locales/fr/chat.json
generated
|
|
@ -269,6 +269,36 @@
|
|||
"completionInstructions": "Sous-tâche terminée ! Vous pouvez examiner les résultats et suggérer des corrections ou les prochaines étapes. Si tout semble bon, confirmez pour retourner le résultat à la tâche parente.",
|
||||
"goToSubtask": "Afficher la tâche"
|
||||
},
|
||||
"subagents": {
|
||||
"runningLabel": "Running subagent: {{description}}",
|
||||
"completedLabel": "Subagent completed",
|
||||
"cancelledByUser": "Subagent was cancelled by the user.",
|
||||
"starting": "Starting...",
|
||||
"thinking": "Thinking...",
|
||||
"toolDisplayNames": {
|
||||
"read_file": "Read file",
|
||||
"write_to_file": "Write file",
|
||||
"apply_diff": "Apply diff",
|
||||
"search_files": "Search files",
|
||||
"search_and_replace": "Search and replace",
|
||||
"search_replace": "Search replace",
|
||||
"edit_file": "Edit file",
|
||||
"list_files": "List files",
|
||||
"codebase_search": "Codebase search",
|
||||
"execute_command": "Run command",
|
||||
"run_slash_command": "Slash command",
|
||||
"ask_followup_question": "Ask question",
|
||||
"attempt_completion": "Complete",
|
||||
"update_todo_list": "Update todos",
|
||||
"switch_mode": "Switch mode",
|
||||
"new_task": "New task",
|
||||
"generate_image": "Generate image",
|
||||
"use_mcp_tool": "MCP tool",
|
||||
"access_mcp_resource": "MCP resource",
|
||||
"apply_patch": "Apply patch",
|
||||
"subagent": "Subagent"
|
||||
}
|
||||
},
|
||||
"questions": {
|
||||
"hasQuestion": "Roo a une question"
|
||||
},
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/fr/settings.json
generated
4
webview-ui/src/i18n/locales/fr/settings.json
generated
|
|
@ -823,6 +823,10 @@
|
|||
"refreshSuccess": "Outils actualisés avec succès",
|
||||
"refreshError": "Échec de l'actualisation des outils",
|
||||
"toolParameters": "Paramètres"
|
||||
},
|
||||
"SUBAGENT": {
|
||||
"name": "Subagent tool",
|
||||
"description": "When enabled, the model can run subagents in the background for research or sub-tasks. The subagent's result is returned to the main task. Use \"explore\" for read-only sub-tasks or \"general\" when the sub-task may edit files or run commands."
|
||||
}
|
||||
},
|
||||
"promptCaching": {
|
||||
|
|
|
|||
30
webview-ui/src/i18n/locales/hi/chat.json
generated
30
webview-ui/src/i18n/locales/hi/chat.json
generated
|
|
@ -269,6 +269,36 @@
|
|||
"completionInstructions": "उपकार्य पूर्ण! आप परिणामों की समीक्षा कर सकते हैं और सुधार या अगले चरण सुझा सकते हैं। यदि सब कुछ ठीक लगता है, तो मुख्य कार्य को परिणाम वापस करने के लिए पुष्टि करें।",
|
||||
"goToSubtask": "कार्य देखें"
|
||||
},
|
||||
"subagents": {
|
||||
"runningLabel": "Running subagent: {{description}}",
|
||||
"completedLabel": "Subagent completed",
|
||||
"cancelledByUser": "Subagent was cancelled by the user.",
|
||||
"starting": "Starting...",
|
||||
"thinking": "Thinking...",
|
||||
"toolDisplayNames": {
|
||||
"read_file": "Read file",
|
||||
"write_to_file": "Write file",
|
||||
"apply_diff": "Apply diff",
|
||||
"search_files": "Search files",
|
||||
"search_and_replace": "Search and replace",
|
||||
"search_replace": "Search replace",
|
||||
"edit_file": "Edit file",
|
||||
"list_files": "List files",
|
||||
"codebase_search": "Codebase search",
|
||||
"execute_command": "Run command",
|
||||
"run_slash_command": "Slash command",
|
||||
"ask_followup_question": "Ask question",
|
||||
"attempt_completion": "Complete",
|
||||
"update_todo_list": "Update todos",
|
||||
"switch_mode": "Switch mode",
|
||||
"new_task": "New task",
|
||||
"generate_image": "Generate image",
|
||||
"use_mcp_tool": "MCP tool",
|
||||
"access_mcp_resource": "MCP resource",
|
||||
"apply_patch": "Apply patch",
|
||||
"subagent": "Subagent"
|
||||
}
|
||||
},
|
||||
"questions": {
|
||||
"hasQuestion": "Roo का एक प्रश्न है"
|
||||
},
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/hi/settings.json
generated
4
webview-ui/src/i18n/locales/hi/settings.json
generated
|
|
@ -823,6 +823,10 @@
|
|||
"refreshSuccess": "टूल्स सफलतापूर्वक रिफ्रेश हुए",
|
||||
"refreshError": "टूल्स रिफ्रेश करने में विफल",
|
||||
"toolParameters": "पैरामीटर्स"
|
||||
},
|
||||
"SUBAGENT": {
|
||||
"name": "Subagent tool",
|
||||
"description": "When enabled, the model can run subagents in the background for research or sub-tasks. The subagent's result is returned to the main task. Use \"explore\" for read-only sub-tasks or \"general\" when the sub-task may edit files or run commands."
|
||||
}
|
||||
},
|
||||
"promptCaching": {
|
||||
|
|
|
|||
30
webview-ui/src/i18n/locales/id/chat.json
generated
30
webview-ui/src/i18n/locales/id/chat.json
generated
|
|
@ -320,6 +320,36 @@
|
|||
"completionInstructions": "Subtugas selesai! Kamu bisa meninjau hasilnya dan menyarankan koreksi atau langkah selanjutnya. Jika semuanya terlihat baik, konfirmasi untuk mengembalikan hasil ke tugas induk.",
|
||||
"goToSubtask": "Lihat tugas"
|
||||
},
|
||||
"subagents": {
|
||||
"runningLabel": "Running subagent: {{description}}",
|
||||
"completedLabel": "Subagent completed",
|
||||
"cancelledByUser": "Subagent was cancelled by the user.",
|
||||
"starting": "Starting...",
|
||||
"thinking": "Thinking...",
|
||||
"toolDisplayNames": {
|
||||
"read_file": "Read file",
|
||||
"write_to_file": "Write file",
|
||||
"apply_diff": "Apply diff",
|
||||
"search_files": "Search files",
|
||||
"search_and_replace": "Search and replace",
|
||||
"search_replace": "Search replace",
|
||||
"edit_file": "Edit file",
|
||||
"list_files": "List files",
|
||||
"codebase_search": "Codebase search",
|
||||
"execute_command": "Run command",
|
||||
"run_slash_command": "Slash command",
|
||||
"ask_followup_question": "Ask question",
|
||||
"attempt_completion": "Complete",
|
||||
"update_todo_list": "Update todos",
|
||||
"switch_mode": "Switch mode",
|
||||
"new_task": "New task",
|
||||
"generate_image": "Generate image",
|
||||
"use_mcp_tool": "MCP tool",
|
||||
"access_mcp_resource": "MCP resource",
|
||||
"apply_patch": "Apply patch",
|
||||
"subagent": "Subagent"
|
||||
}
|
||||
},
|
||||
"questions": {
|
||||
"hasQuestion": "Roo punya pertanyaan"
|
||||
},
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/id/settings.json
generated
4
webview-ui/src/i18n/locales/id/settings.json
generated
|
|
@ -823,6 +823,10 @@
|
|||
"refreshSuccess": "Tool berhasil direfresh",
|
||||
"refreshError": "Gagal merefresh tool",
|
||||
"toolParameters": "Parameter"
|
||||
},
|
||||
"SUBAGENT": {
|
||||
"name": "Subagent tool",
|
||||
"description": "When enabled, the model can run subagents in the background for research or sub-tasks. The subagent's result is returned to the main task. Use \"explore\" for read-only sub-tasks or \"general\" when the sub-task may edit files or run commands."
|
||||
}
|
||||
},
|
||||
"promptCaching": {
|
||||
|
|
|
|||
30
webview-ui/src/i18n/locales/it/chat.json
generated
30
webview-ui/src/i18n/locales/it/chat.json
generated
|
|
@ -269,6 +269,36 @@
|
|||
"completionInstructions": "Sottoattività completata! Puoi rivedere i risultati e suggerire correzioni o prossimi passi. Se tutto sembra a posto, conferma per restituire il risultato all'attività principale.",
|
||||
"goToSubtask": "Visualizza attività"
|
||||
},
|
||||
"subagents": {
|
||||
"runningLabel": "Running subagent: {{description}}",
|
||||
"completedLabel": "Subagent completed",
|
||||
"cancelledByUser": "Subagent was cancelled by the user.",
|
||||
"starting": "Starting...",
|
||||
"thinking": "Thinking...",
|
||||
"toolDisplayNames": {
|
||||
"read_file": "Read file",
|
||||
"write_to_file": "Write file",
|
||||
"apply_diff": "Apply diff",
|
||||
"search_files": "Search files",
|
||||
"search_and_replace": "Search and replace",
|
||||
"search_replace": "Search replace",
|
||||
"edit_file": "Edit file",
|
||||
"list_files": "List files",
|
||||
"codebase_search": "Codebase search",
|
||||
"execute_command": "Run command",
|
||||
"run_slash_command": "Slash command",
|
||||
"ask_followup_question": "Ask question",
|
||||
"attempt_completion": "Complete",
|
||||
"update_todo_list": "Update todos",
|
||||
"switch_mode": "Switch mode",
|
||||
"new_task": "New task",
|
||||
"generate_image": "Generate image",
|
||||
"use_mcp_tool": "MCP tool",
|
||||
"access_mcp_resource": "MCP resource",
|
||||
"apply_patch": "Apply patch",
|
||||
"subagent": "Subagent"
|
||||
}
|
||||
},
|
||||
"questions": {
|
||||
"hasQuestion": "Roo ha una domanda"
|
||||
},
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/it/settings.json
generated
4
webview-ui/src/i18n/locales/it/settings.json
generated
|
|
@ -823,6 +823,10 @@
|
|||
"refreshSuccess": "Strumenti aggiornati con successo",
|
||||
"refreshError": "Impossibile aggiornare gli strumenti",
|
||||
"toolParameters": "Parametri"
|
||||
},
|
||||
"SUBAGENT": {
|
||||
"name": "Subagent tool",
|
||||
"description": "When enabled, the model can run subagents in the background for research or sub-tasks. The subagent's result is returned to the main task. Use \"explore\" for read-only sub-tasks or \"general\" when the sub-task may edit files or run commands."
|
||||
}
|
||||
},
|
||||
"promptCaching": {
|
||||
|
|
|
|||
30
webview-ui/src/i18n/locales/ja/chat.json
generated
30
webview-ui/src/i18n/locales/ja/chat.json
generated
|
|
@ -269,6 +269,36 @@
|
|||
"completionInstructions": "サブタスク完了!結果を確認し、修正や次のステップを提案できます。問題なければ、親タスクに結果を返すために確認してください。",
|
||||
"goToSubtask": "タスクを表示"
|
||||
},
|
||||
"subagents": {
|
||||
"runningLabel": "Running subagent: {{description}}",
|
||||
"completedLabel": "Subagent completed",
|
||||
"cancelledByUser": "Subagent was cancelled by the user.",
|
||||
"starting": "Starting...",
|
||||
"thinking": "Thinking...",
|
||||
"toolDisplayNames": {
|
||||
"read_file": "Read file",
|
||||
"write_to_file": "Write file",
|
||||
"apply_diff": "Apply diff",
|
||||
"search_files": "Search files",
|
||||
"search_and_replace": "Search and replace",
|
||||
"search_replace": "Search replace",
|
||||
"edit_file": "Edit file",
|
||||
"list_files": "List files",
|
||||
"codebase_search": "Codebase search",
|
||||
"execute_command": "Run command",
|
||||
"run_slash_command": "Slash command",
|
||||
"ask_followup_question": "Ask question",
|
||||
"attempt_completion": "Complete",
|
||||
"update_todo_list": "Update todos",
|
||||
"switch_mode": "Switch mode",
|
||||
"new_task": "New task",
|
||||
"generate_image": "Generate image",
|
||||
"use_mcp_tool": "MCP tool",
|
||||
"access_mcp_resource": "MCP resource",
|
||||
"apply_patch": "Apply patch",
|
||||
"subagent": "Subagent"
|
||||
}
|
||||
},
|
||||
"questions": {
|
||||
"hasQuestion": "Rooは質問があります"
|
||||
},
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/ja/settings.json
generated
4
webview-ui/src/i18n/locales/ja/settings.json
generated
|
|
@ -823,6 +823,10 @@
|
|||
"refreshSuccess": "ツールが正常に更新されました",
|
||||
"refreshError": "ツールの更新に失敗しました",
|
||||
"toolParameters": "パラメーター"
|
||||
},
|
||||
"SUBAGENT": {
|
||||
"name": "Subagent tool",
|
||||
"description": "When enabled, the model can run subagents in the background for research or sub-tasks. The subagent's result is returned to the main task. Use \"explore\" for read-only sub-tasks or \"general\" when the sub-task may edit files or run commands."
|
||||
}
|
||||
},
|
||||
"promptCaching": {
|
||||
|
|
|
|||
30
webview-ui/src/i18n/locales/ko/chat.json
generated
30
webview-ui/src/i18n/locales/ko/chat.json
generated
|
|
@ -269,6 +269,36 @@
|
|||
"completionInstructions": "하위 작업 완료! 결과를 검토하고 수정 사항이나 다음 단계를 제안할 수 있습니다. 모든 것이 괜찮아 보이면, 부모 작업에 결과를 반환하기 위해 확인해주세요.",
|
||||
"goToSubtask": "작업 보기"
|
||||
},
|
||||
"subagents": {
|
||||
"runningLabel": "Running subagent: {{description}}",
|
||||
"completedLabel": "Subagent completed",
|
||||
"cancelledByUser": "Subagent was cancelled by the user.",
|
||||
"starting": "Starting...",
|
||||
"thinking": "Thinking...",
|
||||
"toolDisplayNames": {
|
||||
"read_file": "Read file",
|
||||
"write_to_file": "Write file",
|
||||
"apply_diff": "Apply diff",
|
||||
"search_files": "Search files",
|
||||
"search_and_replace": "Search and replace",
|
||||
"search_replace": "Search replace",
|
||||
"edit_file": "Edit file",
|
||||
"list_files": "List files",
|
||||
"codebase_search": "Codebase search",
|
||||
"execute_command": "Run command",
|
||||
"run_slash_command": "Slash command",
|
||||
"ask_followup_question": "Ask question",
|
||||
"attempt_completion": "Complete",
|
||||
"update_todo_list": "Update todos",
|
||||
"switch_mode": "Switch mode",
|
||||
"new_task": "New task",
|
||||
"generate_image": "Generate image",
|
||||
"use_mcp_tool": "MCP tool",
|
||||
"access_mcp_resource": "MCP resource",
|
||||
"apply_patch": "Apply patch",
|
||||
"subagent": "Subagent"
|
||||
}
|
||||
},
|
||||
"questions": {
|
||||
"hasQuestion": "Roo에게 질문이 있습니다"
|
||||
},
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/ko/settings.json
generated
4
webview-ui/src/i18n/locales/ko/settings.json
generated
|
|
@ -823,6 +823,10 @@
|
|||
"refreshSuccess": "도구가 성공적으로 새로고침되었습니다",
|
||||
"refreshError": "도구 새로고침에 실패했습니다",
|
||||
"toolParameters": "매개변수"
|
||||
},
|
||||
"SUBAGENT": {
|
||||
"name": "Subagent tool",
|
||||
"description": "When enabled, the model can run subagents in the background for research or sub-tasks. The subagent's result is returned to the main task. Use \"explore\" for read-only sub-tasks or \"general\" when the sub-task may edit files or run commands."
|
||||
}
|
||||
},
|
||||
"promptCaching": {
|
||||
|
|
|
|||
30
webview-ui/src/i18n/locales/nl/chat.json
generated
30
webview-ui/src/i18n/locales/nl/chat.json
generated
|
|
@ -264,6 +264,36 @@
|
|||
"completionInstructions": "Subtaak voltooid! Je kunt de resultaten bekijken en eventuele correcties of volgende stappen voorstellen. Als alles goed is, bevestig dan om het resultaat terug te sturen naar de hoofdtaak.",
|
||||
"goToSubtask": "Taak weergeven"
|
||||
},
|
||||
"subagents": {
|
||||
"runningLabel": "Running subagent: {{description}}",
|
||||
"completedLabel": "Subagent completed",
|
||||
"cancelledByUser": "Subagent was cancelled by the user.",
|
||||
"starting": "Starting...",
|
||||
"thinking": "Thinking...",
|
||||
"toolDisplayNames": {
|
||||
"read_file": "Read file",
|
||||
"write_to_file": "Write file",
|
||||
"apply_diff": "Apply diff",
|
||||
"search_files": "Search files",
|
||||
"search_and_replace": "Search and replace",
|
||||
"search_replace": "Search replace",
|
||||
"edit_file": "Edit file",
|
||||
"list_files": "List files",
|
||||
"codebase_search": "Codebase search",
|
||||
"execute_command": "Run command",
|
||||
"run_slash_command": "Slash command",
|
||||
"ask_followup_question": "Ask question",
|
||||
"attempt_completion": "Complete",
|
||||
"update_todo_list": "Update todos",
|
||||
"switch_mode": "Switch mode",
|
||||
"new_task": "New task",
|
||||
"generate_image": "Generate image",
|
||||
"use_mcp_tool": "MCP tool",
|
||||
"access_mcp_resource": "MCP resource",
|
||||
"apply_patch": "Apply patch",
|
||||
"subagent": "Subagent"
|
||||
}
|
||||
},
|
||||
"questions": {
|
||||
"hasQuestion": "Roo heeft een vraag"
|
||||
},
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/nl/settings.json
generated
4
webview-ui/src/i18n/locales/nl/settings.json
generated
|
|
@ -823,6 +823,10 @@
|
|||
"refreshSuccess": "Tools succesvol vernieuwd",
|
||||
"refreshError": "Fout bij vernieuwen van tools",
|
||||
"toolParameters": "Parameters"
|
||||
},
|
||||
"SUBAGENT": {
|
||||
"name": "Subagent tool",
|
||||
"description": "When enabled, the model can run subagents in the background for research or sub-tasks. The subagent's result is returned to the main task. Use \"explore\" for read-only sub-tasks or \"general\" when the sub-task may edit files or run commands."
|
||||
}
|
||||
},
|
||||
"promptCaching": {
|
||||
|
|
|
|||
30
webview-ui/src/i18n/locales/pl/chat.json
generated
30
webview-ui/src/i18n/locales/pl/chat.json
generated
|
|
@ -269,6 +269,36 @@
|
|||
"completionInstructions": "Podzadanie zakończone! Możesz przejrzeć wyniki i zasugerować poprawki lub następne kroki. Jeśli wszystko wygląda dobrze, potwierdź, aby zwrócić wynik do zadania nadrzędnego.",
|
||||
"goToSubtask": "Wyświetl zadanie"
|
||||
},
|
||||
"subagents": {
|
||||
"runningLabel": "Running subagent: {{description}}",
|
||||
"completedLabel": "Subagent completed",
|
||||
"cancelledByUser": "Subagent was cancelled by the user.",
|
||||
"starting": "Starting...",
|
||||
"thinking": "Thinking...",
|
||||
"toolDisplayNames": {
|
||||
"read_file": "Read file",
|
||||
"write_to_file": "Write file",
|
||||
"apply_diff": "Apply diff",
|
||||
"search_files": "Search files",
|
||||
"search_and_replace": "Search and replace",
|
||||
"search_replace": "Search replace",
|
||||
"edit_file": "Edit file",
|
||||
"list_files": "List files",
|
||||
"codebase_search": "Codebase search",
|
||||
"execute_command": "Run command",
|
||||
"run_slash_command": "Slash command",
|
||||
"ask_followup_question": "Ask question",
|
||||
"attempt_completion": "Complete",
|
||||
"update_todo_list": "Update todos",
|
||||
"switch_mode": "Switch mode",
|
||||
"new_task": "New task",
|
||||
"generate_image": "Generate image",
|
||||
"use_mcp_tool": "MCP tool",
|
||||
"access_mcp_resource": "MCP resource",
|
||||
"apply_patch": "Apply patch",
|
||||
"subagent": "Subagent"
|
||||
}
|
||||
},
|
||||
"questions": {
|
||||
"hasQuestion": "Roo ma pytanie"
|
||||
},
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/pl/settings.json
generated
4
webview-ui/src/i18n/locales/pl/settings.json
generated
|
|
@ -823,6 +823,10 @@
|
|||
"refreshSuccess": "Narzędzia odświeżone pomyślnie",
|
||||
"refreshError": "Nie udało się odświeżyć narzędzi",
|
||||
"toolParameters": "Parametry"
|
||||
},
|
||||
"SUBAGENT": {
|
||||
"name": "Subagent tool",
|
||||
"description": "When enabled, the model can run subagents in the background for research or sub-tasks. The subagent's result is returned to the main task. Use \"explore\" for read-only sub-tasks or \"general\" when the sub-task may edit files or run commands."
|
||||
}
|
||||
},
|
||||
"promptCaching": {
|
||||
|
|
|
|||
30
webview-ui/src/i18n/locales/pt-BR/chat.json
generated
30
webview-ui/src/i18n/locales/pt-BR/chat.json
generated
|
|
@ -269,6 +269,36 @@
|
|||
"completionInstructions": "Subtarefa concluída! Você pode revisar os resultados e sugerir correções ou próximos passos. Se tudo parecer bom, confirme para retornar o resultado à tarefa principal.",
|
||||
"goToSubtask": "Ver tarefa"
|
||||
},
|
||||
"subagents": {
|
||||
"runningLabel": "Running subagent: {{description}}",
|
||||
"completedLabel": "Subagent completed",
|
||||
"cancelledByUser": "Subagent was cancelled by the user.",
|
||||
"starting": "Starting...",
|
||||
"thinking": "Thinking...",
|
||||
"toolDisplayNames": {
|
||||
"read_file": "Read file",
|
||||
"write_to_file": "Write file",
|
||||
"apply_diff": "Apply diff",
|
||||
"search_files": "Search files",
|
||||
"search_and_replace": "Search and replace",
|
||||
"search_replace": "Search replace",
|
||||
"edit_file": "Edit file",
|
||||
"list_files": "List files",
|
||||
"codebase_search": "Codebase search",
|
||||
"execute_command": "Run command",
|
||||
"run_slash_command": "Slash command",
|
||||
"ask_followup_question": "Ask question",
|
||||
"attempt_completion": "Complete",
|
||||
"update_todo_list": "Update todos",
|
||||
"switch_mode": "Switch mode",
|
||||
"new_task": "New task",
|
||||
"generate_image": "Generate image",
|
||||
"use_mcp_tool": "MCP tool",
|
||||
"access_mcp_resource": "MCP resource",
|
||||
"apply_patch": "Apply patch",
|
||||
"subagent": "Subagent"
|
||||
}
|
||||
},
|
||||
"questions": {
|
||||
"hasQuestion": "Roo tem uma pergunta"
|
||||
},
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/pt-BR/settings.json
generated
4
webview-ui/src/i18n/locales/pt-BR/settings.json
generated
|
|
@ -823,6 +823,10 @@
|
|||
"refreshSuccess": "Ferramentas atualizadas com sucesso",
|
||||
"refreshError": "Falha ao atualizar ferramentas",
|
||||
"toolParameters": "Parâmetros"
|
||||
},
|
||||
"SUBAGENT": {
|
||||
"name": "Subagent tool",
|
||||
"description": "When enabled, the model can run subagents in the background for research or sub-tasks. The subagent's result is returned to the main task. Use \"explore\" for read-only sub-tasks or \"general\" when the sub-task may edit files or run commands."
|
||||
}
|
||||
},
|
||||
"promptCaching": {
|
||||
|
|
|
|||
30
webview-ui/src/i18n/locales/ru/chat.json
generated
30
webview-ui/src/i18n/locales/ru/chat.json
generated
|
|
@ -265,6 +265,36 @@
|
|||
"completionInstructions": "Подзадача завершена! Вы можете просмотреть результаты и предложить исправления или следующие шаги. Если всё в порядке, подтвердите для возврата результата в родительскую задачу.",
|
||||
"goToSubtask": "Просмотреть задачу"
|
||||
},
|
||||
"subagents": {
|
||||
"runningLabel": "Running subagent: {{description}}",
|
||||
"completedLabel": "Subagent completed",
|
||||
"cancelledByUser": "Subagent was cancelled by the user.",
|
||||
"starting": "Starting...",
|
||||
"thinking": "Thinking...",
|
||||
"toolDisplayNames": {
|
||||
"read_file": "Read file",
|
||||
"write_to_file": "Write file",
|
||||
"apply_diff": "Apply diff",
|
||||
"search_files": "Search files",
|
||||
"search_and_replace": "Search and replace",
|
||||
"search_replace": "Search replace",
|
||||
"edit_file": "Edit file",
|
||||
"list_files": "List files",
|
||||
"codebase_search": "Codebase search",
|
||||
"execute_command": "Run command",
|
||||
"run_slash_command": "Slash command",
|
||||
"ask_followup_question": "Ask question",
|
||||
"attempt_completion": "Complete",
|
||||
"update_todo_list": "Update todos",
|
||||
"switch_mode": "Switch mode",
|
||||
"new_task": "New task",
|
||||
"generate_image": "Generate image",
|
||||
"use_mcp_tool": "MCP tool",
|
||||
"access_mcp_resource": "MCP resource",
|
||||
"apply_patch": "Apply patch",
|
||||
"subagent": "Subagent"
|
||||
}
|
||||
},
|
||||
"questions": {
|
||||
"hasQuestion": "У Roo есть вопрос"
|
||||
},
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/ru/settings.json
generated
4
webview-ui/src/i18n/locales/ru/settings.json
generated
|
|
@ -823,6 +823,10 @@
|
|||
"refreshSuccess": "Инструменты успешно обновлены",
|
||||
"refreshError": "Не удалось обновить инструменты",
|
||||
"toolParameters": "Параметры"
|
||||
},
|
||||
"SUBAGENT": {
|
||||
"name": "Subagent tool",
|
||||
"description": "When enabled, the model can run subagents in the background for research or sub-tasks. The subagent's result is returned to the main task. Use \"explore\" for read-only sub-tasks or \"general\" when the sub-task may edit files or run commands."
|
||||
}
|
||||
},
|
||||
"promptCaching": {
|
||||
|
|
|
|||
30
webview-ui/src/i18n/locales/tr/chat.json
generated
30
webview-ui/src/i18n/locales/tr/chat.json
generated
|
|
@ -270,6 +270,36 @@
|
|||
"completionInstructions": "Alt görev tamamlandı! Sonuçları inceleyebilir ve düzeltmeler veya sonraki adımlar önerebilirsiniz. Her şey iyi görünüyorsa, sonucu üst göreve döndürmek için onaylayın.",
|
||||
"goToSubtask": "Görevi görüntüle"
|
||||
},
|
||||
"subagents": {
|
||||
"runningLabel": "Running subagent: {{description}}",
|
||||
"completedLabel": "Subagent completed",
|
||||
"cancelledByUser": "Subagent was cancelled by the user.",
|
||||
"starting": "Starting...",
|
||||
"thinking": "Thinking...",
|
||||
"toolDisplayNames": {
|
||||
"read_file": "Read file",
|
||||
"write_to_file": "Write file",
|
||||
"apply_diff": "Apply diff",
|
||||
"search_files": "Search files",
|
||||
"search_and_replace": "Search and replace",
|
||||
"search_replace": "Search replace",
|
||||
"edit_file": "Edit file",
|
||||
"list_files": "List files",
|
||||
"codebase_search": "Codebase search",
|
||||
"execute_command": "Run command",
|
||||
"run_slash_command": "Slash command",
|
||||
"ask_followup_question": "Ask question",
|
||||
"attempt_completion": "Complete",
|
||||
"update_todo_list": "Update todos",
|
||||
"switch_mode": "Switch mode",
|
||||
"new_task": "New task",
|
||||
"generate_image": "Generate image",
|
||||
"use_mcp_tool": "MCP tool",
|
||||
"access_mcp_resource": "MCP resource",
|
||||
"apply_patch": "Apply patch",
|
||||
"subagent": "Subagent"
|
||||
}
|
||||
},
|
||||
"questions": {
|
||||
"hasQuestion": "Roo'nun bir sorusu var"
|
||||
},
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/tr/settings.json
generated
4
webview-ui/src/i18n/locales/tr/settings.json
generated
|
|
@ -823,6 +823,10 @@
|
|||
"refreshSuccess": "Araçlar başarıyla yenilendi",
|
||||
"refreshError": "Araçlar yenilenemedi",
|
||||
"toolParameters": "Parametreler"
|
||||
},
|
||||
"SUBAGENT": {
|
||||
"name": "Subagent tool",
|
||||
"description": "When enabled, the model can run subagents in the background for research or sub-tasks. The subagent's result is returned to the main task. Use \"explore\" for read-only sub-tasks or \"general\" when the sub-task may edit files or run commands."
|
||||
}
|
||||
},
|
||||
"promptCaching": {
|
||||
|
|
|
|||
30
webview-ui/src/i18n/locales/vi/chat.json
generated
30
webview-ui/src/i18n/locales/vi/chat.json
generated
|
|
@ -270,6 +270,36 @@
|
|||
"completionInstructions": "Nhiệm vụ phụ đã hoàn thành! Bạn có thể xem lại kết quả và đề xuất các sửa đổi hoặc bước tiếp theo. Nếu mọi thứ có vẻ tốt, hãy xác nhận để trả kết quả về nhiệm vụ chính.",
|
||||
"goToSubtask": "Xem nhiệm vụ"
|
||||
},
|
||||
"subagents": {
|
||||
"runningLabel": "Running subagent: {{description}}",
|
||||
"completedLabel": "Subagent completed",
|
||||
"cancelledByUser": "Subagent was cancelled by the user.",
|
||||
"starting": "Starting...",
|
||||
"thinking": "Thinking...",
|
||||
"toolDisplayNames": {
|
||||
"read_file": "Read file",
|
||||
"write_to_file": "Write file",
|
||||
"apply_diff": "Apply diff",
|
||||
"search_files": "Search files",
|
||||
"search_and_replace": "Search and replace",
|
||||
"search_replace": "Search replace",
|
||||
"edit_file": "Edit file",
|
||||
"list_files": "List files",
|
||||
"codebase_search": "Codebase search",
|
||||
"execute_command": "Run command",
|
||||
"run_slash_command": "Slash command",
|
||||
"ask_followup_question": "Ask question",
|
||||
"attempt_completion": "Complete",
|
||||
"update_todo_list": "Update todos",
|
||||
"switch_mode": "Switch mode",
|
||||
"new_task": "New task",
|
||||
"generate_image": "Generate image",
|
||||
"use_mcp_tool": "MCP tool",
|
||||
"access_mcp_resource": "MCP resource",
|
||||
"apply_patch": "Apply patch",
|
||||
"subagent": "Subagent"
|
||||
}
|
||||
},
|
||||
"questions": {
|
||||
"hasQuestion": "Roo có một câu hỏi"
|
||||
},
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/vi/settings.json
generated
4
webview-ui/src/i18n/locales/vi/settings.json
generated
|
|
@ -823,6 +823,10 @@
|
|||
"refreshSuccess": "Làm mới công cụ thành công",
|
||||
"refreshError": "Không thể làm mới công cụ",
|
||||
"toolParameters": "Thông số"
|
||||
},
|
||||
"SUBAGENT": {
|
||||
"name": "Subagent tool",
|
||||
"description": "When enabled, the model can run subagents in the background for research or sub-tasks. The subagent's result is returned to the main task. Use \"explore\" for read-only sub-tasks or \"general\" when the sub-task may edit files or run commands."
|
||||
}
|
||||
},
|
||||
"promptCaching": {
|
||||
|
|
|
|||
30
webview-ui/src/i18n/locales/zh-CN/chat.json
generated
30
webview-ui/src/i18n/locales/zh-CN/chat.json
generated
|
|
@ -270,6 +270,36 @@
|
|||
"completionInstructions": "子任务已完成!您可以查看结果并提出修改或下一步建议。如果一切正常,请确认以将结果返回给主任务。",
|
||||
"goToSubtask": "查看任务"
|
||||
},
|
||||
"subagents": {
|
||||
"runningLabel": "Running subagent: {{description}}",
|
||||
"completedLabel": "Subagent completed",
|
||||
"cancelledByUser": "Subagent was cancelled by the user.",
|
||||
"starting": "Starting...",
|
||||
"thinking": "Thinking...",
|
||||
"toolDisplayNames": {
|
||||
"read_file": "Read file",
|
||||
"write_to_file": "Write file",
|
||||
"apply_diff": "Apply diff",
|
||||
"search_files": "Search files",
|
||||
"search_and_replace": "Search and replace",
|
||||
"search_replace": "Search replace",
|
||||
"edit_file": "Edit file",
|
||||
"list_files": "List files",
|
||||
"codebase_search": "Codebase search",
|
||||
"execute_command": "Run command",
|
||||
"run_slash_command": "Slash command",
|
||||
"ask_followup_question": "Ask question",
|
||||
"attempt_completion": "Complete",
|
||||
"update_todo_list": "Update todos",
|
||||
"switch_mode": "Switch mode",
|
||||
"new_task": "New task",
|
||||
"generate_image": "Generate image",
|
||||
"use_mcp_tool": "MCP tool",
|
||||
"access_mcp_resource": "MCP resource",
|
||||
"apply_patch": "Apply patch",
|
||||
"subagent": "Subagent"
|
||||
}
|
||||
},
|
||||
"questions": {
|
||||
"hasQuestion": "Roo有一个问题"
|
||||
},
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/zh-CN/settings.json
generated
4
webview-ui/src/i18n/locales/zh-CN/settings.json
generated
|
|
@ -823,6 +823,10 @@
|
|||
"refreshSuccess": "工具刷新成功",
|
||||
"refreshError": "工具刷新失败",
|
||||
"toolParameters": "参数"
|
||||
},
|
||||
"SUBAGENT": {
|
||||
"name": "Subagent tool",
|
||||
"description": "When enabled, the model can run subagents in the background for research or sub-tasks. The subagent's result is returned to the main task. Use \"explore\" for read-only sub-tasks or \"general\" when the sub-task may edit files or run commands."
|
||||
}
|
||||
},
|
||||
"promptCaching": {
|
||||
|
|
|
|||
30
webview-ui/src/i18n/locales/zh-TW/chat.json
generated
30
webview-ui/src/i18n/locales/zh-TW/chat.json
generated
|
|
@ -314,6 +314,36 @@
|
|||
"completionInstructions": "子任務已完成!您可以檢閱結果並提出修正或後續步驟。如果一切順利,請確認以將結果回傳給主任務。",
|
||||
"goToSubtask": "查看工作"
|
||||
},
|
||||
"subagents": {
|
||||
"runningLabel": "Running subagent: {{description}}",
|
||||
"completedLabel": "Subagent completed",
|
||||
"cancelledByUser": "Subagent was cancelled by the user.",
|
||||
"starting": "Starting...",
|
||||
"thinking": "Thinking...",
|
||||
"toolDisplayNames": {
|
||||
"read_file": "Read file",
|
||||
"write_to_file": "Write file",
|
||||
"apply_diff": "Apply diff",
|
||||
"search_files": "Search files",
|
||||
"search_and_replace": "Search and replace",
|
||||
"search_replace": "Search replace",
|
||||
"edit_file": "Edit file",
|
||||
"list_files": "List files",
|
||||
"codebase_search": "Codebase search",
|
||||
"execute_command": "Run command",
|
||||
"run_slash_command": "Slash command",
|
||||
"ask_followup_question": "Ask question",
|
||||
"attempt_completion": "Complete",
|
||||
"update_todo_list": "Update todos",
|
||||
"switch_mode": "Switch mode",
|
||||
"new_task": "New task",
|
||||
"generate_image": "Generate image",
|
||||
"use_mcp_tool": "MCP tool",
|
||||
"access_mcp_resource": "MCP resource",
|
||||
"apply_patch": "Apply patch",
|
||||
"subagent": "Subagent"
|
||||
}
|
||||
},
|
||||
"questions": {
|
||||
"hasQuestion": "Roo 有一個問題"
|
||||
},
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/zh-TW/settings.json
generated
4
webview-ui/src/i18n/locales/zh-TW/settings.json
generated
|
|
@ -833,6 +833,10 @@
|
|||
"refreshSuccess": "工具重新整理成功",
|
||||
"refreshError": "工具重新整理失敗",
|
||||
"toolParameters": "參數"
|
||||
},
|
||||
"SUBAGENT": {
|
||||
"name": "Subagent tool",
|
||||
"description": "When enabled, the model can run subagents in the background for research or sub-tasks. The subagent's result is returned to the main task. Use \"explore\" for read-only sub-tasks or \"general\" when the sub-task may edit files or run commands."
|
||||
}
|
||||
},
|
||||
"promptCaching": {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue