mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
fix: eliminate UI rerender during task cancellation
- Add resetToResumableState() method to Task class to reset internal state without recreation - Simplify cancellation flow to handle everything within Task class - Update abortStream() to properly mark API requests as cancelled - Remove initClineWithHistoryItem() call from ClineProvider.cancelTask() - Task instance now persists through cancellation, preventing UI flicker The task now handles its own cancellation and resumption internally, maintaining the same instance throughout. This provides a seamless user experience with no visual disruption when cancelling and resuming tasks.
This commit is contained in:
parent
aee531a143
commit
c93d6908e2
2 changed files with 72 additions and 40 deletions
|
|
@ -1489,8 +1489,8 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
|
|||
}
|
||||
}
|
||||
|
||||
public async abortTask(isAbandoned = false) {
|
||||
// Aborting task
|
||||
public async abortTask(isAbandoned = false, skipSave = false) {
|
||||
console.log(`[subtasks] aborting task ${this.taskId}.${this.instanceId}`)
|
||||
|
||||
// Will stop any autonomously running promises.
|
||||
if (isAbandoned) {
|
||||
|
|
@ -1506,15 +1506,74 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
|
|||
console.error(`Error during task ${this.taskId}.${this.instanceId} disposal:`, error)
|
||||
// Don't rethrow - we want abort to always succeed
|
||||
}
|
||||
// Save the countdown message in the automatic retry or other content.
|
||||
try {
|
||||
// Save the countdown message in the automatic retry or other content.
|
||||
await this.saveClineMessages()
|
||||
} catch (error) {
|
||||
console.error(`Error saving messages during abort for task ${this.taskId}.${this.instanceId}:`, error)
|
||||
|
||||
// Only save messages if not skipping (e.g., during user cancellation where messages are already saved)
|
||||
if (!skipSave) {
|
||||
try {
|
||||
// Save the countdown message in the automatic retry or other content.
|
||||
await this.saveClineMessages()
|
||||
} catch (error) {
|
||||
console.error(`Error saving messages during abort for task ${this.taskId}.${this.instanceId}:`, error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the task to a resumable state without recreating the instance.
|
||||
* This is used when canceling a task to avoid unnecessary rerenders.
|
||||
*/
|
||||
public async resetToResumableState() {
|
||||
console.log(`[subtasks] resetting task ${this.taskId}.${this.instanceId} to resumable state`)
|
||||
|
||||
// Reset abort flags
|
||||
this.abort = false
|
||||
this.abandoned = false
|
||||
|
||||
// Reset streaming state
|
||||
this.isStreaming = false
|
||||
this.isWaitingForFirstChunk = false
|
||||
this.didFinishAbortingStream = true
|
||||
this.didCompleteReadingStream = false
|
||||
|
||||
// Clear streaming content
|
||||
this.currentStreamingContentIndex = 0
|
||||
this.currentStreamingDidCheckpoint = false
|
||||
this.assistantMessageContent = []
|
||||
this.userMessageContent = []
|
||||
this.userMessageContentReady = false
|
||||
this.didRejectTool = false
|
||||
this.didAlreadyUseTool = false
|
||||
this.presentAssistantMessageLocked = false
|
||||
this.presentAssistantMessageHasPendingUpdates = false
|
||||
|
||||
// Reset API state
|
||||
this.consecutiveMistakeCount = 0
|
||||
|
||||
// Reset ask response state to allow new messages
|
||||
this.askResponse = undefined
|
||||
this.askResponseText = undefined
|
||||
this.askResponseImages = undefined
|
||||
this.blockingAsk = undefined
|
||||
|
||||
// Reset parser if exists
|
||||
if (this.assistantMessageParser) {
|
||||
this.assistantMessageParser.reset()
|
||||
}
|
||||
|
||||
// Only reset diff view if it's actively editing
|
||||
// This avoids unnecessary operations when diff view is not in use
|
||||
if (this.diffViewProvider && this.diffViewProvider.isEditing) {
|
||||
await this.diffViewProvider.reset()
|
||||
}
|
||||
|
||||
// The task is now ready to be resumed
|
||||
// The API request status has already been updated by abortStream
|
||||
// We don't add the resume_task message here because ask() will add it
|
||||
|
||||
// Keep messages and history intact for resumption
|
||||
// The task is now ready to be resumed without recreation
|
||||
}
|
||||
|
||||
// Used when a sub-task is launched and the parent task is waiting for it to
|
||||
// finish.
|
||||
// TBD: The 1s should be added to the settings, also should add a timeout to
|
||||
|
|
@ -1584,7 +1643,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
|
|||
const currentUserContent = currentItem.userContent
|
||||
const currentIncludeFileDetails = currentItem.includeFileDetails
|
||||
|
||||
if (this.abort) {
|
||||
if (this.abort) {
|
||||
throw new Error(`[RooCode#recursivelyMakeRooRequests] task ${this.taskId}.${this.instanceId} aborted`)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1279,38 +1279,11 @@ export class ClineProvider
|
|||
|
||||
console.log(`[subtasks] cancelling task ${cline.taskId}.${cline.instanceId}`)
|
||||
|
||||
const { historyItem } = await this.getTaskWithId(cline.taskId)
|
||||
// Preserve parent and root task information for history item.
|
||||
const rootTask = cline.rootTask
|
||||
const parentTask = cline.parentTask
|
||||
// Just set the abort flag - the task will handle its own resumption
|
||||
cline.abort = true
|
||||
|
||||
cline.abortTask()
|
||||
|
||||
await pWaitFor(
|
||||
() =>
|
||||
this.getCurrentTask()! === undefined ||
|
||||
this.getCurrentTask()!.isStreaming === false ||
|
||||
this.getCurrentTask()!.didFinishAbortingStream ||
|
||||
// If only the first chunk is processed, then there's no
|
||||
// need to wait for graceful abort (closes edits, browser,
|
||||
// etc).
|
||||
this.getCurrentTask()!.isWaitingForFirstChunk,
|
||||
{
|
||||
timeout: 3_000,
|
||||
},
|
||||
).catch(() => {
|
||||
console.error("Failed to abort task")
|
||||
})
|
||||
|
||||
if (this.getCurrentTask()) {
|
||||
// 'abandoned' will prevent this Cline instance from affecting
|
||||
// future Cline instances. This may happen if its hanging on a
|
||||
// streaming request.
|
||||
this.getCurrentTask()!.abandoned = true
|
||||
}
|
||||
|
||||
// Clears task again, so we need to abortTask manually above.
|
||||
await this.createTaskWithHistoryItem({ ...historyItem, rootTask, parentTask })
|
||||
// The task's streaming loop will detect the abort flag and handle the resumption
|
||||
// No need to wait or do anything else here
|
||||
}
|
||||
|
||||
async updateCustomInstructions(instructions?: string) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue