fix: prevent checkpoint restore from auto-resuming tasks

- Modified ClineProvider.cancelTask() to accept optional startTask parameter
- Updated checkpointRestore() to pass startTask: false to cancelTask()
- Updated checkpointRestoreHandler delete operation to pass startTask: false to createTaskWithHistoryItem()
- Updated tests to reflect the new behavior

This fixes the issue where checkpoint restore would re-execute
restored tasks instead of just displaying the restored state.

Fixes #10402
This commit is contained in:
Roo Code 2025-12-30 14:01:47 +00:00
parent 6d8fa39319
commit dc965d74ea
4 changed files with 16 additions and 5 deletions

View file

@ -294,7 +294,11 @@ export async function checkpointRestore(
// I'd like to revisit this in the future and try to improve the
// task flow and the communication between the webview and the
// `Task` instance.
provider?.cancelTask()
//
// Pass startTask: false to prevent the rehydrated task from auto-resuming
// and re-executing the restored message history. The task should only
// display the restored state, not continue processing.
provider?.cancelTask({ startTask: false })
} catch (err) {
provider?.log("[checkpointRestore] disabling checkpoints for this task")
task.enableCheckpoints = false

View file

@ -2739,7 +2739,7 @@ export class ClineProvider
return task
}
public async cancelTask(): Promise<void> {
public async cancelTask(options?: { startTask?: boolean }): Promise<void> {
const task = this.getCurrentTask()
if (!task) {
@ -2807,7 +2807,10 @@ export class ClineProvider
}
// Clears task again, so we need to abortTask manually above.
await this.createTaskWithHistoryItem({ ...historyItem, rootTask, parentTask })
// Pass startTask option to control whether the rehydrated task auto-resumes.
// When called from checkpoint restore, startTask should be false to prevent
// the task from re-executing its message history.
await this.createTaskWithHistoryItem({ ...historyItem, rootTask, parentTask }, { startTask: options?.startTask })
}
// Clear the current task without treating it as a subtask.

View file

@ -190,7 +190,8 @@ describe("checkpointRestoreHandler", () => {
expect(mockProvider.getTaskWithId).toHaveBeenCalledWith("test-task-123")
// Verify createTaskWithHistoryItem was called with the correct history item
expect(mockProvider.createTaskWithHistoryItem).toHaveBeenCalledWith(expectedHistoryItem)
// startTask: false prevents the restored task from auto-resuming
expect(mockProvider.createTaskWithHistoryItem).toHaveBeenCalledWith(expectedHistoryItem, { startTask: false })
})
it("should not save messages or reinitialize for edit operation", async () => {

View file

@ -73,8 +73,11 @@ export async function handleCheckpointRestoreOperation(config: CheckpointRestore
})
// Get the updated history item and reinitialize
// Pass startTask: false to prevent the restored task from auto-resuming
// and re-executing the message history. The task should only display
// the restored state, not continue processing.
const { historyItem } = await provider.getTaskWithId(currentCline.taskId)
await provider.createTaskWithHistoryItem(historyItem)
await provider.createTaskWithHistoryItem(historyItem, { startTask: false })
}
// For edit operations, the task cancellation in checkpointRestore
// will trigger reinitialization, which will process pendingEditAfterRestore