Iterate on subtasks UX

This commit is contained in:
Matt Rubens 2025-03-08 23:46:28 -05:00
parent 1c8071bbb3
commit e10757d6fa
11 changed files with 47 additions and 52 deletions

View file

@ -1431,7 +1431,7 @@ export class Cline {
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.",
"Subtask completed! 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.",
})
return await askApproval("tool", toolMessage)

View file

@ -984,8 +984,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
await this.updateGlobalState("alwaysAllowModeSwitch", message.bool)
await this.postStateToWebview()
break
case "alwaysAllowFinishTask":
await this.updateGlobalState("alwaysAllowFinishTask", message.bool)
case "alwaysAllowSubtasks":
await this.updateGlobalState("alwaysAllowSubtasks", message.bool)
await this.postStateToWebview()
break
case "askResponse":
@ -997,9 +997,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
break
case "clearTask":
// clear task resets the current session and allows for a new task to be started, if this session is a subtask - it allows the parent task to be resumed
await this.finishSubTask(
`new_task finished with an error!, it was stopped and canceled by the user.`,
)
await this.finishSubTask(`Task error: It was stopped and canceled by the user.`)
await this.postStateToWebview()
break
case "didShowAnnouncement":
@ -2181,7 +2179,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
alwaysAllowBrowser,
alwaysAllowMcp,
alwaysAllowModeSwitch,
alwaysAllowFinishTask,
alwaysAllowSubtasks,
soundEnabled,
diffEnabled,
enableCheckpoints,
@ -2229,7 +2227,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
alwaysAllowBrowser: alwaysAllowBrowser ?? false,
alwaysAllowMcp: alwaysAllowMcp ?? false,
alwaysAllowModeSwitch: alwaysAllowModeSwitch ?? false,
alwaysAllowFinishTask: alwaysAllowFinishTask ?? false,
alwaysAllowSubtasks: alwaysAllowSubtasks ?? false,
uriScheme: vscode.env.uriScheme,
currentTaskItem: this.getCurrentCline()?.taskId
? (taskHistory || []).find((item: HistoryItem) => item.id === this.getCurrentCline()?.taskId)
@ -2391,7 +2389,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
alwaysAllowBrowser: stateValues.alwaysAllowBrowser ?? false,
alwaysAllowMcp: stateValues.alwaysAllowMcp ?? false,
alwaysAllowModeSwitch: stateValues.alwaysAllowModeSwitch ?? false,
alwaysAllowFinishTask: stateValues.alwaysAllowFinishTask ?? false,
alwaysAllowSubtasks: stateValues.alwaysAllowSubtasks ?? false,
taskHistory: stateValues.taskHistory,
allowedCommands: stateValues.allowedCommands,
soundEnabled: stateValues.soundEnabled ?? false,

View file

@ -109,7 +109,7 @@ export interface ExtensionState {
alwaysAllowMcp?: boolean
alwaysApproveResubmit?: boolean
alwaysAllowModeSwitch?: boolean
alwaysAllowFinishTask?: boolean
alwaysAllowSubtasks?: boolean
browserToolEnabled?: boolean
requestDelaySeconds: number
rateLimitSeconds: number // Minimum time between successive requests (0 = disabled)

View file

@ -48,7 +48,7 @@ export interface WebviewMessage {
| "alwaysAllowBrowser"
| "alwaysAllowMcp"
| "alwaysAllowModeSwitch"
| "alwaysAllowFinishTask"
| "alwaysAllowSubtasks"
| "playSound"
| "soundEnabled"
| "soundVolume"

View file

@ -40,7 +40,7 @@ export const GLOBAL_STATE_KEYS = [
"alwaysAllowBrowser",
"alwaysAllowMcp",
"alwaysAllowModeSwitch",
"alwaysAllowFinishTask",
"alwaysAllowSubtasks",
"taskHistory",
"openAiBaseUrl",
"openAiModelId",

View file

@ -30,8 +30,8 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => {
setAlwaysAllowMcp,
alwaysAllowModeSwitch,
setAlwaysAllowModeSwitch,
alwaysAllowFinishTask,
setAlwaysAllowFinishTask,
alwaysAllowSubtasks,
setAlwaysAllowSubtasks,
alwaysApproveResubmit,
setAlwaysApproveResubmit,
autoApprovalEnabled,
@ -77,18 +77,17 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => {
},
{
id: "switchModes",
label: "Switch modes & create tasks",
label: "Switch modes",
shortName: "Modes",
enabled: alwaysAllowModeSwitch ?? false,
description:
"Allows automatic switching between different AI modes and creating new tasks without requiring approval.",
description: "Allows automatic switching between different modes without requiring approval.",
},
{
id: "finishTask",
label: "Continue to next task",
shortName: "Continue",
enabled: alwaysAllowFinishTask ?? false,
description: "Allow tasks to end execution and continue to the next task, without user review or approval.",
id: "subtasks",
label: "Create & complete subtasks",
shortName: "Subtasks",
enabled: alwaysAllowSubtasks ?? false,
description: "Allow creation and completion of subtasks without requiring approval.",
},
{
id: "retryRequests",
@ -145,11 +144,11 @@ 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 handleSubtasksChange = useCallback(() => {
const newValue = !(alwaysAllowSubtasks ?? false)
setAlwaysAllowSubtasks(newValue)
vscode.postMessage({ type: "alwaysAllowSubtasks", bool: newValue })
}, [alwaysAllowSubtasks, setAlwaysAllowSubtasks])
const handleRetryChange = useCallback(() => {
const newValue = !(alwaysApproveResubmit ?? false)
@ -165,7 +164,7 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => {
useBrowser: handleBrowserChange,
useMcp: handleMcpChange,
switchModes: handleModeSwitchChange,
finishTask: handleFinishTaskChange,
subtasks: handleSubtasksChange,
retryRequests: handleRetryChange,
}

View file

@ -452,7 +452,7 @@ export const ChatRowContent = ({
<div style={headerStyle}>
{toolIcon("new-file")}
<span style={{ fontWeight: "bold" }}>
Roo wants to create a new task in <code>{tool.mode}</code> mode:
Roo wants to create a new subtask in <code>{tool.mode}</code> mode:
</span>
</div>
<div style={{ paddingLeft: "26px", marginTop: "4px" }}>
@ -464,8 +464,8 @@ export const ChatRowContent = ({
return (
<>
<div style={headerStyle}>
{toolIcon("new-file")}
<span style={{ fontWeight: "bold" }}>Roo wants to finish this task</span>
{toolIcon("checklist")}
<span style={{ fontWeight: "bold" }}>Roo wants to finish this subtask</span>
</div>
<div style={{ paddingLeft: "26px", marginTop: "4px" }}>
<code>{tool.content}</code>

View file

@ -61,7 +61,7 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
setMode,
autoApprovalEnabled,
alwaysAllowModeSwitch,
alwaysAllowFinishTask,
alwaysAllowSubtasks,
customModes,
telemetrySetting,
} = useExtensionState()
@ -150,7 +150,7 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
setSecondaryButtonText("Reject")
break
case "finishTask":
setPrimaryButtonText("Approve & Continue to the next Task")
setPrimaryButtonText("Complete Subtask and Return")
setSecondaryButtonText(undefined)
break
default:
@ -646,11 +646,10 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
(alwaysAllowMcp && message.ask === "use_mcp_server" && isMcpToolAlwaysAllowed(message)) ||
(alwaysAllowModeSwitch &&
message.ask === "tool" &&
(JSON.parse(message.text || "{}")?.tool === "switchMode" ||
JSON.parse(message.text || "{}")?.tool === "newTask")) ||
(alwaysAllowFinishTask &&
JSON.parse(message.text || "{}")?.tool === "switchMode") ||
(alwaysAllowSubtasks &&
message.ask === "tool" &&
JSON.parse(message.text || "{}")?.tool === "finishTask")
["newTask", "finishTask"].includes(JSON.parse(message.text || "{}")?.tool))
)
},
[
@ -665,7 +664,7 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
alwaysAllowMcp,
isMcpToolAlwaysAllowed,
alwaysAllowModeSwitch,
alwaysAllowFinishTask,
alwaysAllowSubtasks,
],
)

View file

@ -18,7 +18,7 @@ type AutoApproveSettingsProps = HTMLAttributes<HTMLDivElement> & {
requestDelaySeconds: number
alwaysAllowMcp?: boolean
alwaysAllowModeSwitch?: boolean
alwaysAllowFinishTask?: boolean
alwaysAllowSubtasks?: boolean
alwaysAllowExecute?: boolean
allowedCommands?: string[]
setCachedStateField: SetCachedStateField<keyof ExtensionStateContextType>
@ -33,7 +33,7 @@ export const AutoApproveSettings = ({
requestDelaySeconds,
alwaysAllowMcp,
alwaysAllowModeSwitch,
alwaysAllowFinishTask,
alwaysAllowSubtasks,
alwaysAllowExecute,
allowedCommands,
setCachedStateField,
@ -175,22 +175,21 @@ export const AutoApproveSettings = ({
<VSCodeCheckbox
checked={alwaysAllowModeSwitch}
onChange={(e: any) => setCachedStateField("alwaysAllowModeSwitch", e.target.checked)}>
<span className="font-medium">Always approve mode switching & task creation</span>
<span className="font-medium">Always approve mode switching</span>
</VSCodeCheckbox>
<p className="text-vscode-descriptionForeground text-sm mt-0">
Automatically switch between different AI modes and create new tasks without requiring approval
Automatically switch between different modes without requiring approval
</p>
</div>
<div>
<VSCodeCheckbox
checked={alwaysAllowFinishTask}
onChange={(e: any) => setCachedStateField("alwaysAllowFinishTask", e.target.checked)}>
<span className="font-medium">Always approve finish & continue to next task</span>
checked={alwaysAllowSubtasks}
onChange={(e: any) => setCachedStateField("alwaysAllowSubtasks", e.target.checked)}>
<span className="font-medium">Always approve creation & completion of subtasks</span>
</VSCodeCheckbox>
<p className="text-vscode-descriptionForeground text-sm mt-0">
Automatically approve tasks to finish execution and continue to the next task, without user
review or approval
Allow creation and completion of subtasks without requiring approval
</p>
</div>

View file

@ -63,7 +63,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone },
alwaysAllowExecute,
alwaysAllowMcp,
alwaysAllowModeSwitch,
alwaysAllowFinishTask,
alwaysAllowSubtasks,
alwaysAllowWrite,
alwaysApproveResubmit,
browserToolEnabled,
@ -185,7 +185,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone },
vscode.postMessage({ type: "currentApiConfigName", text: currentApiConfigName })
vscode.postMessage({ type: "updateExperimental", values: experiments })
vscode.postMessage({ type: "alwaysAllowModeSwitch", bool: alwaysAllowModeSwitch })
vscode.postMessage({ type: "alwaysAllowFinishTask", bool: alwaysAllowFinishTask })
vscode.postMessage({ type: "alwaysAllowSubtasks", bool: alwaysAllowSubtasks })
vscode.postMessage({ type: "upsertApiConfiguration", text: currentApiConfigName, apiConfiguration })
vscode.postMessage({ type: "telemetrySetting", text: telemetrySetting })
setChangeDetected(false)
@ -366,7 +366,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone },
requestDelaySeconds={requestDelaySeconds}
alwaysAllowMcp={alwaysAllowMcp}
alwaysAllowModeSwitch={alwaysAllowModeSwitch}
alwaysAllowFinishTask={alwaysAllowFinishTask}
alwaysAllowSubtasks={alwaysAllowSubtasks}
alwaysAllowExecute={alwaysAllowExecute}
allowedCommands={allowedCommands}
setCachedStateField={setCachedStateField}

View file

@ -31,7 +31,7 @@ export interface ExtensionStateContextType extends ExtensionState {
setAlwaysAllowBrowser: (value: boolean) => void
setAlwaysAllowMcp: (value: boolean) => void
setAlwaysAllowModeSwitch: (value: boolean) => void
setAlwaysAllowFinishTask: (value: boolean) => void
setAlwaysAllowSubtasks: (value: boolean) => void
setBrowserToolEnabled: (value: boolean) => void
setShowRooIgnoredFiles: (value: boolean) => void
setShowAnnouncement: (value: boolean) => void
@ -248,7 +248,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
setAlwaysAllowBrowser: (value) => setState((prevState) => ({ ...prevState, alwaysAllowBrowser: value })),
setAlwaysAllowMcp: (value) => setState((prevState) => ({ ...prevState, alwaysAllowMcp: value })),
setAlwaysAllowModeSwitch: (value) => setState((prevState) => ({ ...prevState, alwaysAllowModeSwitch: value })),
setAlwaysAllowFinishTask: (value) => setState((prevState) => ({ ...prevState, alwaysAllowFinishTask: value })),
setAlwaysAllowSubtasks: (value) => setState((prevState) => ({ ...prevState, alwaysAllowSubtasks: value })),
setShowAnnouncement: (value) => setState((prevState) => ({ ...prevState, shouldShowAnnouncement: value })),
setAllowedCommands: (value) => setState((prevState) => ({ ...prevState, allowedCommands: value })),
setSoundEnabled: (value) => setState((prevState) => ({ ...prevState, soundEnabled: value })),