diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index cb6694b7f04..3fb059103b3 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -1663,6 +1663,26 @@ export class Task extends EventEmitter implements TaskLike { const iterator = stream[Symbol.asyncIterator]() let item = await iterator.next() while (!item.done) { + // Check abort flag BEFORE processing the chunk + if (this.abort) { + console.log(`aborting stream (early check), this.abandoned = ${this.abandoned}`) + + if (!this.abandoned) { + // Only need to gracefully abort if this instance + // isn't abandoned (sometimes OpenRouter stream + // hangs, in which case this would affect future + // instances of Cline). + await abortStream("user_cancelled") + } + + // Clean up the iterator if it has a return method + if (iterator.return) { + await iterator.return(undefined).catch(() => {}) + } + + break // Aborts the stream. + } + const chunk = item.value item = await iterator.next() if (!chunk) { @@ -1707,8 +1727,9 @@ export class Task extends EventEmitter implements TaskLike { } } + // Check abort flag AFTER processing the chunk as well if (this.abort) { - console.log(`aborting stream, this.abandoned = ${this.abandoned}`) + console.log(`aborting stream (after chunk), this.abandoned = ${this.abandoned}`) if (!this.abandoned) { // Only need to gracefully abort if this instance @@ -1718,6 +1739,11 @@ export class Task extends EventEmitter implements TaskLike { await abortStream("user_cancelled") } + // Clean up the iterator if it has a return method + if (iterator.return) { + await iterator.return(undefined).catch(() => {}) + } + break // Aborts the stream. } diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index e6817e1825f..d07ddaba271 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -1228,6 +1228,10 @@ export class ClineProvider const rootTask = cline.rootTask const parentTask = cline.parentTask + // Set the abort flag immediately to signal cancellation + cline.abort = true + + // Then call abortTask to handle cleanup cline.abortTask() await pWaitFor( @@ -1243,7 +1247,7 @@ export class ClineProvider timeout: 3_000, }, ).catch(() => { - console.error("Failed to abort task") + console.error("Failed to abort task gracefully") }) if (this.getCurrentCline()) {