diff --git a/src/core/Cline.ts b/src/core/Cline.ts index 158b96ff2c..32a520ca2f 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -374,7 +374,7 @@ export class Cline { ): Promise<{ response: ClineAskResponse; text?: string; images?: string[] }> { // If this Cline instance was aborted by the provider, then the only thing keeping us alive is a promise still running in the background, in which case we don't want to send its result to the webview as it is attached to a new instance of Cline now. So we can safely ignore the result of any active promises, and this class will be deallocated. (Although we set Cline = undefined in provider, that simply removes the reference to this instance, but the instance is still alive until this promise resolves or rejects.) if (this.abort) { - throw new Error("Roo Code instance aborted") + throw new Error(`Task: ${this.taskNumber} Roo Code instance aborted (#1)`) } let askTs: number if (partial !== undefined) { @@ -392,7 +392,7 @@ export class Cline { await this.providerRef .deref() ?.postMessageToWebview({ type: "partialMessage", partialMessage: lastMessage }) - throw new Error("Current ask promise was ignored 1") + throw new Error("Current ask promise was ignored (#1)") } else { // this is a new partial message, so add it with partial state // this.askResponse = undefined @@ -402,7 +402,7 @@ export class Cline { this.lastMessageTs = askTs await this.addToClineMessages({ ts: askTs, type: "ask", ask: type, text, partial }) await this.providerRef.deref()?.postStateToWebview() - throw new Error("Current ask promise was ignored 2") + throw new Error("Current ask promise was ignored (#2)") } } else { // partial=false means its a complete version of a previously partial message @@ -476,7 +476,7 @@ export class Cline { checkpoint?: Record, ): Promise { if (this.abort) { - throw new Error("Roo Code instance aborted") + throw new Error(`Task: ${this.taskNumber} Roo Code instance aborted (#2)`) } if (partial !== undefined) { @@ -568,8 +568,9 @@ export class Cline { // release this Cline instance from paused state this.isPaused = false + // fake an answer from the subtask that it has completed running and this is the result of what it has done + // add the message to the chat history and to the webview ui try { - // This adds the completion message to conversation history await this.say("text", `${lastMessage ?? "Please continue to the next task."}`) await this.addToApiConversationHistory({ @@ -587,21 +588,6 @@ export class Cline { ?.log(`Error failed to add reply from subtast into conversation of parent task, error: ${error}`) throw error } - - try { - // Resume parent task - await this.ask("resume_task") - } catch (error) { - if (error.message === "Current ask promise was ignored") { - // ignore the ignored promise, since it was performed by launching a subtask and it probably took more then 1 sec, - // also set the didAlreadyUseTool flag to indicate that the tool was already used, and there is no need to relaunch it - this.didAlreadyUseTool = true - } else { - // Handle error appropriately - console.error("Failed to resume task:", error) - throw error - } - } } private async resumeTaskFromHistory() { @@ -1182,7 +1168,7 @@ export class Cline { async presentAssistantMessage() { if (this.abort) { - throw new Error("Roo Code instance aborted") + throw new Error(`Task: ${this.taskNumber} Roo Code instance aborted (#3)`) } if (this.presentAssistantMessageLocked) { @@ -2819,7 +2805,10 @@ export class Cline { await this.say("completion_result", result, undefined, false) if (this.isSubTask) { // tell the provider to remove the current subtask and resume the previous task in the stack - this.providerRef.deref()?.finishSubTask(lastMessage?.text) + await this.providerRef + .deref() + ?.finishSubTask(`new_task finished successfully! ${lastMessage?.text}`) + break } } @@ -2840,7 +2829,10 @@ export class Cline { await this.say("completion_result", result, undefined, false) if (this.isSubTask) { // tell the provider to remove the current subtask and resume the previous task in the stack - this.providerRef.deref()?.finishSubTask(lastMessage?.text) + await this.providerRef + .deref() + ?.finishSubTask(`new_task finished successfully! ${lastMessage?.text}`) + break } } @@ -2936,7 +2928,7 @@ export class Cline { includeFileDetails: boolean = false, ): Promise { if (this.abort) { - throw new Error("Roo Code instance aborted") + throw new Error(`Task: ${this.taskNumber} Roo Code instance aborted (#4)`) } if (this.consecutiveMistakeCount >= 3) { @@ -3178,7 +3170,7 @@ export class Cline { // need to call here in case the stream was aborted if (this.abort || this.abandoned) { - throw new Error("Roo Code instance aborted") + throw new Error(`Task: ${this.taskNumber} Roo Code instance aborted (#5)`) } this.didCompleteReadingStream = true diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index f92650f887..3cd01d275a 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -152,7 +152,8 @@ export class ClineProvider implements vscode.WebviewViewProvider { const removedTaskNumber = clineToBeRemoved.getTaskNumber() try { - await clineToBeRemoved.abortTask() + // abort the running task and set isAbandoned to true so all running promises will exit as well + await clineToBeRemoved.abortTask(true) } catch (abortError) { this.log(`Error failed aborting task ${removedTaskNumber}: ${abortError.message}`) } @@ -223,7 +224,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { // remove the last cline instance from the stack (this is the finished sub task) await this.removeClineFromStack() // resume the last cline instance in the stack (if it exists - this is the 'parnt' calling task) - this.getCurrentCline()?.resumePausedTask(`new_task finished successfully! ${lastMessage}`) + this.getCurrentCline()?.resumePausedTask(lastMessage) } catch (error) { this.log(`Error in finishSubTask: ${error.message}`) throw error @@ -923,13 +924,10 @@ export class ClineProvider implements vscode.WebviewViewProvider { ) break case "clearTask": - // newTask will start a new task with a given task text, while clear task resets the current session and allows for a new task to be started - await this.removeClineFromStack() - // resume previouse task with subtask failed error - this.getCurrentCline()?.resumePausedTask( + // clear task resets the current session and allows for a new task to be started, if this session is a subtask - it allows the parent task to be resumed + await this.finishSubTask( `new_task finished with an error!, it was stopped and canceled by the user.`, ) - await this.postStateToWebview() break case "didShowAnnouncement": @@ -2087,11 +2085,9 @@ export class ClineProvider implements vscode.WebviewViewProvider { // remove task from stack if it's the current task if (id === this.getCurrentCline()?.taskId) { - await this.removeClineWithIdFromStack(id) - // resume previouse task with subtask failed error - this.getCurrentCline()?.resumePausedTask( - `new_task finished with an error!, it was stopped and delted by the user.`, - ) + // if we found the taskid to delete - call finish to abort this task and allow a new task to be started, + // if we are deleting a subtask and parent task is still waiting for subtask to finish - it allows the parent to resume (this case should neve exist) + await this.finishSubTask(`new_task finished with an error!, it was stopped and delted by the user.`) } // delete task from the task history state