Skip to content

Commit 53e264b

Browse files
author
Roo
committed
fix: comprehensive orchestrator mode restoration for sub-tasks
- Enhanced resumePausedTask method with comprehensive mode restoration logic - Added mode restoration safety net in finishSubTask method - Improved mode validation and restoration in task resumption from history - Added error handling for interrupted sub-tasks with mode restoration - Ensures parent task mode is properly restored regardless of how sub-task ends Fixes #5747
1 parent d0452d0 commit 53e264b

File tree

2 files changed

+117
-0
lines changed

2 files changed

+117
-0
lines changed

src/core/task/Task.ts

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -763,6 +763,35 @@ export class Task extends EventEmitter<ClineEvents> {
763763
this.isPaused = false
764764
this.emit("taskUnpaused")
765765

766+
// Restore the mode that was active when this task was paused
767+
const provider = this.providerRef.deref()
768+
if (provider && this.pausedModeSlug) {
769+
try {
770+
const currentState = await provider.getState()
771+
const currentMode = currentState?.mode ?? defaultModeSlug
772+
773+
if (currentMode !== this.pausedModeSlug) {
774+
provider.log(
775+
`[subtasks] task ${this.taskId}.${this.instanceId} restoring mode from '${currentMode}' to '${this.pausedModeSlug}'`,
776+
)
777+
778+
await provider.handleModeSwitch(this.pausedModeSlug)
779+
780+
// Delay to allow mode change to take effect
781+
await delay(500)
782+
783+
provider.log(
784+
`[subtasks] task ${this.taskId}.${this.instanceId} successfully restored to '${this.pausedModeSlug}' mode`,
785+
)
786+
}
787+
} catch (error) {
788+
provider.log(
789+
`[subtasks] task ${this.taskId}.${this.instanceId} failed to restore mode to '${this.pausedModeSlug}': ${error}`,
790+
)
791+
// Continue execution even if mode restoration fails
792+
}
793+
}
794+
766795
// Fake an answer from the subtask that it has completed running and
767796
// this is the result of what it has done add the message to the chat
768797
// history and to the webview ui.
@@ -795,6 +824,35 @@ export class Task extends EventEmitter<ClineEvents> {
795824
modifiedClineMessages.splice(lastRelevantMessageIndex + 1)
796825
}
797826

827+
// Restore mode if this task was paused with a specific mode
828+
const provider = this.providerRef.deref()
829+
if (provider && this.pausedModeSlug && this.pausedModeSlug !== defaultModeSlug) {
830+
try {
831+
const currentState = await provider.getState()
832+
const currentMode = currentState?.mode ?? defaultModeSlug
833+
834+
if (currentMode !== this.pausedModeSlug) {
835+
provider.log(
836+
`[subtasks] task ${this.taskId}.${this.instanceId} resuming from history - restoring mode from '${currentMode}' to '${this.pausedModeSlug}'`,
837+
)
838+
839+
await provider.handleModeSwitch(this.pausedModeSlug)
840+
841+
// Delay to allow mode change to take effect
842+
await delay(500)
843+
844+
provider.log(
845+
`[subtasks] task ${this.taskId}.${this.instanceId} successfully restored to '${this.pausedModeSlug}' mode from history`,
846+
)
847+
}
848+
} catch (error) {
849+
provider.log(
850+
`[subtasks] task ${this.taskId}.${this.instanceId} failed to restore mode to '${this.pausedModeSlug}' from history: ${error}`,
851+
)
852+
// Continue execution even if mode restoration fails
853+
}
854+
}
855+
798856
// since we don't use api_req_finished anymore, we need to check if the last api_req_started has a cost value, if it doesn't and no cancellation reason to present, then we remove it since it indicates an api request without any partial content streamed
799857
const lastApiReqStartedIndex = findLastIndex(
800858
modifiedClineMessages,
@@ -1451,6 +1509,35 @@ export class Task extends EventEmitter<ClineEvents> {
14511509

14521510
await abortStream(cancelReason, streamingFailedMessage)
14531511

1512+
// Enhanced error handling for sub-tasks: ensure parent task mode is restored
1513+
// when a sub-task is interrupted
1514+
if (this.parentTask && provider) {
1515+
try {
1516+
const parentPausedMode = this.parentTask.pausedModeSlug
1517+
if (parentPausedMode && parentPausedMode !== defaultModeSlug) {
1518+
const currentState = await provider.getState()
1519+
const currentMode = currentState?.mode ?? defaultModeSlug
1520+
1521+
if (currentMode !== parentPausedMode) {
1522+
provider.log(
1523+
`[subtasks] sub-task ${this.taskId}.${this.instanceId} interrupted - restoring parent mode from '${currentMode}' to '${parentPausedMode}'`,
1524+
)
1525+
1526+
await provider.handleModeSwitch(parentPausedMode)
1527+
1528+
provider.log(
1529+
`[subtasks] successfully restored parent mode to '${parentPausedMode}' after sub-task interruption`,
1530+
)
1531+
}
1532+
}
1533+
} catch (modeError) {
1534+
provider.log(
1535+
`[subtasks] failed to restore parent mode after sub-task interruption: ${modeError}`,
1536+
)
1537+
// Continue execution even if mode restoration fails
1538+
}
1539+
}
1540+
14541541
const history = await provider?.getTaskWithId(this.taskId)
14551542

14561543
if (history) {

src/core/webview/ClineProvider.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,8 +228,38 @@ export class ClineProvider
228228
// this is used when a sub task is finished and the parent task needs to be resumed
229229
async finishSubTask(lastMessage: string) {
230230
console.log(`[subtasks] finishing subtask ${lastMessage}`)
231+
232+
// Get the parent task before removing the current one
233+
const parentTask = this.clineStack.length > 1 ? this.clineStack[this.clineStack.length - 2] : undefined
234+
231235
// remove the last cline instance from the stack (this is the finished sub task)
232236
await this.removeClineFromStack()
237+
238+
// Additional safety net: ensure mode is restored for the parent task
239+
if (parentTask && parentTask.pausedModeSlug) {
240+
try {
241+
const currentState = await this.getState()
242+
const currentMode = currentState?.mode ?? defaultModeSlug
243+
244+
if (currentMode !== parentTask.pausedModeSlug) {
245+
this.log(
246+
`[subtasks] finishSubTask: restoring mode from '${currentMode}' to '${parentTask.pausedModeSlug}' for parent task ${parentTask.taskId}`,
247+
)
248+
249+
await this.handleModeSwitch(parentTask.pausedModeSlug)
250+
251+
this.log(
252+
`[subtasks] finishSubTask: successfully restored mode to '${parentTask.pausedModeSlug}' for parent task ${parentTask.taskId}`,
253+
)
254+
}
255+
} catch (error) {
256+
this.log(
257+
`[subtasks] finishSubTask: failed to restore mode to '${parentTask.pausedModeSlug}' for parent task ${parentTask.taskId}: ${error}`,
258+
)
259+
// Continue execution even if mode restoration fails
260+
}
261+
}
262+
233263
// resume the last cline instance in the stack (if it exists - this is the 'parent' calling task)
234264
await this.getCurrentCline()?.resumePausedTask(lastMessage)
235265
}

0 commit comments

Comments
 (0)