Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions src/core/task/Task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1228,6 +1228,12 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
}
}

// Check if this task was paused waiting for a subtask that got interrupted
// If so, handle the interrupted subtask properly
if (this.isPaused && this.childTaskId) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could there be a race condition here? What happens if the child task completes between checking this.isPaused && this.childTaskId and calling handleInterruptedSubtask()? Should we consider using a lock or atomic operation?

await this.handleInterruptedSubtask()
}

const modifiedClineMessages = await this.getSavedClineMessages()

// Check for any stored GPT-5 response IDs in the message history.
Expand Down Expand Up @@ -1657,6 +1663,55 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
}
}

/**
* Handle the case when a subtask was interrupted (e.g., due to 5-hour limit)
* and the parent task needs to be resumed without the subtask result.
*/
public async handleInterruptedSubtask() {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I notice this method appears to be duplicated in the diff. Is this intentional, or did the same method get added twice? We should have only one definition of handleInterruptedSubtask().

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This critical functionality lacks test coverage. Could we add unit tests to verify that:

  • The method correctly clears the paused state
  • The pause interval is properly cleared
  • The correct events are emitted
  • The conversation history is updated appropriately?

if (!this.isPaused || !this.childTaskId) {
return // Not waiting for a subtask
}

const provider = this.providerRef.deref()
provider?.log(
`[handleInterruptedSubtask] Handling interrupted subtask ${this.childTaskId} for parent task ${this.taskId}`,
)

// Clear the paused state
this.isPaused = false
const interruptedChildId = this.childTaskId
this.childTaskId = undefined

// Clear any pause interval
if (this.pauseInterval) {
clearInterval(this.pauseInterval)
this.pauseInterval = undefined
}

// Emit event to update UI
this.emit(RooCodeEventName.TaskUnpaused, this.taskId)

// Add a message to the conversation history indicating the subtask was interrupted
try {
await this.say("subtask_result", `[Subtask ${interruptedChildId} was interrupted and could not complete]`)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider extracting this message format to a constant or configuration for better maintainability. We might want to reuse this format elsewhere or make it configurable.


await this.addToApiConversationHistory({
role: "user",
content: [
{
type: "text",
text: `[new_task interrupted] The subtask was interrupted before completion. Please continue with the main task.`,
},
],
})

// Set skipPrevResponseIdOnce to ensure continuity
this.skipPrevResponseIdOnce = true
} catch (error) {
provider?.log(`Error handling interrupted subtask: ${error}`)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error is only logged but not propagated. Is silent failure the intended behavior here? Should we consider rethrowing the error or at least emitting an error event?

}
}

// Task Loop

private async initiateTaskLoop(userContent: Anthropic.Messages.ContentBlockParam[]): Promise<void> {
Expand Down
Loading