Skip to content

Commit e945dcf

Browse files
authored
Merge pull request RooCodeInc#1491 from shaybc/sbc_subtasks_approve_btn
Sbc subtasks approve button
2 parents 10fca7b + 27624a2 commit e945dcf

File tree

11 files changed

+90
-13
lines changed

11 files changed

+90
-13
lines changed

src/core/Cline.ts

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1417,6 +1417,18 @@ export class Cline {
14171417
return true
14181418
}
14191419

1420+
const askFinishSubTaskApproval = async () => {
1421+
// ask the user to approve this task has completed, and he has reviewd it, and we can declare task is finished
1422+
// and return control to the parent task to continue running the rest of the sub-tasks
1423+
const toolMessage = JSON.stringify({
1424+
tool: "finishTask",
1425+
content:
1426+
"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.",
1427+
})
1428+
1429+
return await askApproval("tool", toolMessage)
1430+
}
1431+
14201432
const handleError = async (action: string, error: Error) => {
14211433
const errorString = `Error ${action}: ${JSON.stringify(serializeError(error))}`
14221434
await this.say(
@@ -2945,13 +2957,6 @@ export class Cline {
29452957
// havent sent a command message yet so first send completion_result then command
29462958
await this.say("completion_result", result, undefined, false)
29472959
telemetryService.captureTaskCompleted(this.taskId)
2948-
if (this.isSubTask) {
2949-
// tell the provider to remove the current subtask and resume the previous task in the stack
2950-
await this.providerRef
2951-
.deref()
2952-
?.finishSubTask(`Task complete: ${lastMessage?.text}`)
2953-
break
2954-
}
29552960
}
29562961

29572962
// complete command message
@@ -2970,13 +2975,17 @@ export class Cline {
29702975
} else {
29712976
await this.say("completion_result", result, undefined, false)
29722977
telemetryService.captureTaskCompleted(this.taskId)
2973-
if (this.isSubTask) {
2974-
// tell the provider to remove the current subtask and resume the previous task in the stack
2975-
await this.providerRef
2976-
.deref()
2977-
?.finishSubTask(`Task complete: ${lastMessage?.text}`)
2978+
}
2979+
2980+
if (this.isSubTask) {
2981+
const didApprove = await askFinishSubTaskApproval()
2982+
if (!didApprove) {
29782983
break
29792984
}
2985+
2986+
// tell the provider to remove the current subtask and resume the previous task in the stack
2987+
await this.providerRef.deref()?.finishSubTask(`Task complete: ${lastMessage?.text}`)
2988+
break
29802989
}
29812990

29822991
// we already sent completion_result says, an empty string asks relinquishes control over button and field

src/core/webview/ClineProvider.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -984,6 +984,10 @@ export class ClineProvider implements vscode.WebviewViewProvider {
984984
await this.updateGlobalState("alwaysAllowModeSwitch", message.bool)
985985
await this.postStateToWebview()
986986
break
987+
case "alwaysAllowFinishTask":
988+
await this.updateGlobalState("alwaysAllowFinishTask", message.bool)
989+
await this.postStateToWebview()
990+
break
987991
case "askResponse":
988992
this.getCurrentCline()?.handleWebviewAskResponse(
989993
message.askResponse!,
@@ -2177,6 +2181,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
21772181
alwaysAllowBrowser,
21782182
alwaysAllowMcp,
21792183
alwaysAllowModeSwitch,
2184+
alwaysAllowFinishTask,
21802185
soundEnabled,
21812186
diffEnabled,
21822187
enableCheckpoints,
@@ -2224,6 +2229,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
22242229
alwaysAllowBrowser: alwaysAllowBrowser ?? false,
22252230
alwaysAllowMcp: alwaysAllowMcp ?? false,
22262231
alwaysAllowModeSwitch: alwaysAllowModeSwitch ?? false,
2232+
alwaysAllowFinishTask: alwaysAllowFinishTask ?? false,
22272233
uriScheme: vscode.env.uriScheme,
22282234
currentTaskItem: this.getCurrentCline()?.taskId
22292235
? (taskHistory || []).find((item: HistoryItem) => item.id === this.getCurrentCline()?.taskId)
@@ -2385,6 +2391,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
23852391
alwaysAllowBrowser: stateValues.alwaysAllowBrowser ?? false,
23862392
alwaysAllowMcp: stateValues.alwaysAllowMcp ?? false,
23872393
alwaysAllowModeSwitch: stateValues.alwaysAllowModeSwitch ?? false,
2394+
alwaysAllowFinishTask: stateValues.alwaysAllowFinishTask ?? false,
23882395
taskHistory: stateValues.taskHistory,
23892396
allowedCommands: stateValues.allowedCommands,
23902397
soundEnabled: stateValues.soundEnabled ?? false,

src/shared/ExtensionMessage.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ export interface ExtensionState {
109109
alwaysAllowMcp?: boolean
110110
alwaysApproveResubmit?: boolean
111111
alwaysAllowModeSwitch?: boolean
112+
alwaysAllowFinishTask?: boolean
112113
browserToolEnabled?: boolean
113114
requestDelaySeconds: number
114115
rateLimitSeconds: number // Minimum time between successive requests (0 = disabled)
@@ -168,6 +169,7 @@ export type ClineAsk =
168169
| "mistake_limit_reached"
169170
| "browser_action_launch"
170171
| "use_mcp_server"
172+
| "finishTask"
171173

172174
export type ClineSay =
173175
| "task"
@@ -207,6 +209,7 @@ export interface ClineSayTool {
207209
| "searchFiles"
208210
| "switchMode"
209211
| "newTask"
212+
| "finishTask"
210213
path?: string
211214
diff?: string
212215
content?: string

src/shared/WebviewMessage.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export interface WebviewMessage {
4848
| "alwaysAllowBrowser"
4949
| "alwaysAllowMcp"
5050
| "alwaysAllowModeSwitch"
51+
| "alwaysAllowFinishTask"
5152
| "playSound"
5253
| "soundEnabled"
5354
| "soundVolume"

src/shared/globalState.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export const GLOBAL_STATE_KEYS = [
4040
"alwaysAllowBrowser",
4141
"alwaysAllowMcp",
4242
"alwaysAllowModeSwitch",
43+
"alwaysAllowFinishTask",
4344
"taskHistory",
4445
"openAiBaseUrl",
4546
"openAiModelId",

webview-ui/src/components/chat/AutoApproveMenu.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => {
3030
setAlwaysAllowMcp,
3131
alwaysAllowModeSwitch,
3232
setAlwaysAllowModeSwitch,
33+
alwaysAllowFinishTask,
34+
setAlwaysAllowFinishTask,
3335
alwaysApproveResubmit,
3436
setAlwaysApproveResubmit,
3537
autoApprovalEnabled,
@@ -81,6 +83,13 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => {
8183
description:
8284
"Allows automatic switching between different AI modes and creating new tasks without requiring approval.",
8385
},
86+
{
87+
id: "finishTask",
88+
label: "Continue to next task",
89+
shortName: "Continue",
90+
enabled: alwaysAllowFinishTask ?? false,
91+
description: "Allow tasks to end execution and continue to the next task, without user review or approval.",
92+
},
8493
{
8594
id: "retryRequests",
8695
label: "Retry failed requests",
@@ -136,6 +145,12 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => {
136145
vscode.postMessage({ type: "alwaysAllowModeSwitch", bool: newValue })
137146
}, [alwaysAllowModeSwitch, setAlwaysAllowModeSwitch])
138147

148+
const handleFinishTaskChange = useCallback(() => {
149+
const newValue = !(alwaysAllowFinishTask ?? false)
150+
setAlwaysAllowFinishTask(newValue)
151+
vscode.postMessage({ type: "alwaysAllowFinishTask", bool: newValue })
152+
}, [alwaysAllowFinishTask, setAlwaysAllowFinishTask])
153+
139154
const handleRetryChange = useCallback(() => {
140155
const newValue = !(alwaysApproveResubmit ?? false)
141156
setAlwaysApproveResubmit(newValue)
@@ -150,6 +165,7 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => {
150165
useBrowser: handleBrowserChange,
151166
useMcp: handleMcpChange,
152167
switchModes: handleModeSwitchChange,
168+
finishTask: handleFinishTaskChange,
153169
retryRequests: handleRetryChange,
154170
}
155171

webview-ui/src/components/chat/ChatRow.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,18 @@ export const ChatRowContent = ({
459459
</div>
460460
</>
461461
)
462+
case "finishTask":
463+
return (
464+
<>
465+
<div style={headerStyle}>
466+
{toolIcon("new-file")}
467+
<span style={{ fontWeight: "bold" }}>Roo wants to finish this task</span>
468+
</div>
469+
<div style={{ paddingLeft: "26px", marginTop: "4px" }}>
470+
<code>{tool.content}</code>
471+
</div>
472+
</>
473+
)
462474
default:
463475
return null
464476
}

webview-ui/src/components/chat/ChatView.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
6161
setMode,
6262
autoApprovalEnabled,
6363
alwaysAllowModeSwitch,
64+
alwaysAllowFinishTask,
6465
customModes,
6566
telemetrySetting,
6667
} = useExtensionState()
@@ -148,6 +149,10 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
148149
setPrimaryButtonText("Save")
149150
setSecondaryButtonText("Reject")
150151
break
152+
case "finishTask":
153+
setPrimaryButtonText("Approve & Continue to the next Task")
154+
setSecondaryButtonText(undefined)
155+
break
151156
default:
152157
setPrimaryButtonText("Approve")
153158
setSecondaryButtonText("Reject")
@@ -642,7 +647,10 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
642647
(alwaysAllowModeSwitch &&
643648
message.ask === "tool" &&
644649
(JSON.parse(message.text || "{}")?.tool === "switchMode" ||
645-
JSON.parse(message.text || "{}")?.tool === "newTask"))
650+
JSON.parse(message.text || "{}")?.tool === "newTask")) ||
651+
(alwaysAllowFinishTask &&
652+
message.ask === "tool" &&
653+
JSON.parse(message.text || "{}")?.tool === "finishTask")
646654
)
647655
},
648656
[
@@ -657,6 +665,7 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
657665
alwaysAllowMcp,
658666
isMcpToolAlwaysAllowed,
659667
alwaysAllowModeSwitch,
668+
alwaysAllowFinishTask,
660669
],
661670
)
662671

webview-ui/src/components/settings/AutoApproveSettings.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ type AutoApproveSettingsProps = HTMLAttributes<HTMLDivElement> & {
1818
requestDelaySeconds: number
1919
alwaysAllowMcp?: boolean
2020
alwaysAllowModeSwitch?: boolean
21+
alwaysAllowFinishTask?: boolean
2122
alwaysAllowExecute?: boolean
2223
allowedCommands?: string[]
2324
setCachedStateField: SetCachedStateField<keyof ExtensionStateContextType>
@@ -32,6 +33,7 @@ export const AutoApproveSettings = ({
3233
requestDelaySeconds,
3334
alwaysAllowMcp,
3435
alwaysAllowModeSwitch,
36+
alwaysAllowFinishTask,
3537
alwaysAllowExecute,
3638
allowedCommands,
3739
setCachedStateField,
@@ -180,6 +182,18 @@ export const AutoApproveSettings = ({
180182
</p>
181183
</div>
182184

185+
<div>
186+
<VSCodeCheckbox
187+
checked={alwaysAllowFinishTask}
188+
onChange={(e: any) => setCachedStateField("alwaysAllowFinishTask", e.target.checked)}>
189+
<span className="font-medium">Always approve finish & continue to next task</span>
190+
</VSCodeCheckbox>
191+
<p className="text-vscode-descriptionForeground text-sm mt-0">
192+
Automatically approve tasks to finish execution and continue to the next task, without user
193+
review or approval
194+
</p>
195+
</div>
196+
183197
<div>
184198
<VSCodeCheckbox
185199
checked={alwaysAllowExecute}

webview-ui/src/components/settings/SettingsView.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone },
6363
alwaysAllowExecute,
6464
alwaysAllowMcp,
6565
alwaysAllowModeSwitch,
66+
alwaysAllowFinishTask,
6667
alwaysAllowWrite,
6768
alwaysApproveResubmit,
6869
browserToolEnabled,
@@ -184,6 +185,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone },
184185
vscode.postMessage({ type: "currentApiConfigName", text: currentApiConfigName })
185186
vscode.postMessage({ type: "updateExperimental", values: experiments })
186187
vscode.postMessage({ type: "alwaysAllowModeSwitch", bool: alwaysAllowModeSwitch })
188+
vscode.postMessage({ type: "alwaysAllowFinishTask", bool: alwaysAllowFinishTask })
187189
vscode.postMessage({ type: "upsertApiConfiguration", text: currentApiConfigName, apiConfiguration })
188190
vscode.postMessage({ type: "telemetrySetting", text: telemetrySetting })
189191
setChangeDetected(false)
@@ -364,6 +366,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone },
364366
requestDelaySeconds={requestDelaySeconds}
365367
alwaysAllowMcp={alwaysAllowMcp}
366368
alwaysAllowModeSwitch={alwaysAllowModeSwitch}
369+
alwaysAllowFinishTask={alwaysAllowFinishTask}
367370
alwaysAllowExecute={alwaysAllowExecute}
368371
allowedCommands={allowedCommands}
369372
setCachedStateField={setCachedStateField}

0 commit comments

Comments
 (0)