diff --git a/src/core/Cline.ts b/src/core/Cline.ts index 51fb5265d4..c3e90d4300 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -1414,6 +1414,18 @@ export class Cline { return true } + const askFinishSubTaskApproval = async () => { + // ask the user to approve this task has completed, and he has reviewd it, and we can declare task is finished + // and return control to the parent task to continue running the rest of the sub-tasks + const toolMessage = JSON.stringify({ + tool: "finishTask", + content: + "Task completed! You can review the results and suggest any corrections or next steps. If everything looks good, confirm to continue with the next task.", + }) + + return await askApproval("tool", toolMessage) + } + const handleError = async (action: string, error: Error) => { const errorString = `Error ${action}: ${JSON.stringify(serializeError(error))}` await this.say( @@ -2941,8 +2953,13 @@ export class Cline { if (lastMessage && lastMessage.ask !== "command") { // havent sent a command message yet so first send completion_result then command await this.say("completion_result", result, undefined, false) - telemetryService.captureTaskCompleted(this.taskId) + // telemetryService.captureTaskCompleted(this.taskId) if (this.isSubTask) { + const didApprove = await askFinishSubTaskApproval() + if (!didApprove) { + break + } + // tell the provider to remove the current subtask and resume the previous task in the stack await this.providerRef .deref() @@ -2966,8 +2983,13 @@ export class Cline { commandResult = execCommandResult } else { await this.say("completion_result", result, undefined, false) - telemetryService.captureTaskCompleted(this.taskId) + // telemetryService.captureTaskCompleted(this.taskId) if (this.isSubTask) { + const didApprove = await askFinishSubTaskApproval() + if (!didApprove) { + break + } + // tell the provider to remove the current subtask and resume the previous task in the stack await this.providerRef .deref() diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts index 98ff9b36e1..dcdaf017f3 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -109,6 +109,7 @@ export interface ExtensionState { alwaysAllowMcp?: boolean alwaysApproveResubmit?: boolean alwaysAllowModeSwitch?: boolean + alwaysAllowFinishTask?: boolean browserToolEnabled?: boolean requestDelaySeconds: number rateLimitSeconds: number // Minimum time between successive requests (0 = disabled) @@ -168,6 +169,7 @@ export type ClineAsk = | "mistake_limit_reached" | "browser_action_launch" | "use_mcp_server" + | "finishTask" export type ClineSay = | "task" @@ -207,6 +209,7 @@ export interface ClineSayTool { | "searchFiles" | "switchMode" | "newTask" + | "finishTask" path?: string diff?: string content?: string diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index 10af6f7a94..086701a43f 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -48,6 +48,7 @@ export interface WebviewMessage { | "alwaysAllowBrowser" | "alwaysAllowMcp" | "alwaysAllowModeSwitch" + | "alwaysAllowFinishTask" | "playSound" | "soundEnabled" | "soundVolume" diff --git a/webview-ui/src/components/chat/AutoApproveMenu.tsx b/webview-ui/src/components/chat/AutoApproveMenu.tsx index 161f3032b0..02f75cd28a 100644 --- a/webview-ui/src/components/chat/AutoApproveMenu.tsx +++ b/webview-ui/src/components/chat/AutoApproveMenu.tsx @@ -30,6 +30,8 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => { setAlwaysAllowMcp, alwaysAllowModeSwitch, setAlwaysAllowModeSwitch, + alwaysAllowFinishTask, + setAlwaysAllowFinishTask, alwaysApproveResubmit, setAlwaysApproveResubmit, autoApprovalEnabled, @@ -81,6 +83,13 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => { description: "Allows automatic switching between different AI modes and creating new tasks without requiring approval.", }, + { + id: "finishTask", + label: "Finish subtasks tasks", + shortName: "Finish", + enabled: alwaysAllowFinishTask ?? false, + description: "Allows automatic completeing a sub-task without requiring user review or approval.", + }, { id: "retryRequests", label: "Retry failed requests", @@ -136,6 +145,12 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => { vscode.postMessage({ type: "alwaysAllowModeSwitch", bool: newValue }) }, [alwaysAllowModeSwitch, setAlwaysAllowModeSwitch]) + const handleFinishTaskChange = useCallback(() => { + const newValue = !(alwaysAllowFinishTask ?? false) + setAlwaysAllowFinishTask(newValue) + vscode.postMessage({ type: "alwaysAllowFinishTask", bool: newValue }) + }, [alwaysAllowFinishTask, setAlwaysAllowFinishTask]) + const handleRetryChange = useCallback(() => { const newValue = !(alwaysApproveResubmit ?? false) setAlwaysApproveResubmit(newValue) @@ -150,6 +165,7 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => { useBrowser: handleBrowserChange, useMcp: handleMcpChange, switchModes: handleModeSwitchChange, + finishTask: handleFinishTaskChange, retryRequests: handleRetryChange, } diff --git a/webview-ui/src/components/chat/ChatRow.tsx b/webview-ui/src/components/chat/ChatRow.tsx index 1533bba3a8..6f19df665e 100644 --- a/webview-ui/src/components/chat/ChatRow.tsx +++ b/webview-ui/src/components/chat/ChatRow.tsx @@ -459,6 +459,18 @@ export const ChatRowContent = ({ > ) + case "finishTask": + return ( + <> +
{tool.content}
+