diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index c5be865731a..cf16df8dcc7 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -845,9 +845,19 @@ export class Task extends EventEmitter implements TaskLike { const message = this.messageQueueService.dequeueMessage() if (message) { - setTimeout(async () => { - await this.submitUserMessage(message.text, message.images) - }, 0) + // Check if this is a tool approval ask that needs to be handled + if ( + type === "tool" || + type === "command" || + type === "browser_action_launch" || + type === "use_mcp_server" + ) { + // For tool approvals, we need to approve first, then send the message if there's text/images + this.handleWebviewAskResponse("yesButtonClicked", message.text, message.images) + } else { + // For other ask types (like followup), fulfill the ask directly + this.setMessageResponse(message.text, message.images) + } } } @@ -2898,4 +2908,28 @@ export class Task extends EventEmitter implements TaskLike { public get cwd() { return this.workspacePath } + + /** + * Process any queued messages by dequeuing and submitting them. + * This ensures that queued user messages are sent when appropriate, + * preventing them from getting stuck in the queue. + * + * @param context - Context string for logging (e.g., the calling tool name) + */ + public processQueuedMessages(): void { + try { + if (!this.messageQueueService.isEmpty()) { + const queued = this.messageQueueService.dequeueMessage() + if (queued) { + setTimeout(() => { + this.submitUserMessage(queued.text, queued.images).catch((err) => + console.error(`[Task] Failed to submit queued message:`, err), + ) + }, 0) + } + } + } catch (e) { + console.error(`[Task] Queue processing error:`, e) + } + } } diff --git a/src/core/tools/__tests__/applyDiffTool.experiment.spec.ts b/src/core/tools/__tests__/applyDiffTool.experiment.spec.ts index e763125d4a4..f82d4b1820c 100644 --- a/src/core/tools/__tests__/applyDiffTool.experiment.spec.ts +++ b/src/core/tools/__tests__/applyDiffTool.experiment.spec.ts @@ -40,6 +40,7 @@ describe("applyDiffTool experiment routing", () => { api: { getModel: vi.fn().mockReturnValue({ id: "test-model" }), }, + processQueuedMessages: vi.fn(), } as any mockBlock = { diff --git a/src/core/tools/__tests__/multiApplyDiffTool.spec.ts b/src/core/tools/__tests__/multiApplyDiffTool.spec.ts index 0bdedb9cd56..5e591f9fe79 100644 --- a/src/core/tools/__tests__/multiApplyDiffTool.spec.ts +++ b/src/core/tools/__tests__/multiApplyDiffTool.spec.ts @@ -91,6 +91,7 @@ describe("multiApplyDiffTool", () => { trackFileContext: vi.fn().mockResolvedValue(undefined), }, didEditFile: false, + processQueuedMessages: vi.fn(), } as any mockAskApproval = vi.fn().mockResolvedValue(true) diff --git a/src/core/tools/applyDiffTool.ts b/src/core/tools/applyDiffTool.ts index 903e3c846ec..dcdd1346240 100644 --- a/src/core/tools/applyDiffTool.ts +++ b/src/core/tools/applyDiffTool.ts @@ -207,6 +207,7 @@ export async function applyDiffToolLegacy( if (!didApprove) { await cline.diffViewProvider.revertChanges() // Cline likely handles closing the diff view + cline.processQueuedMessages() return } @@ -245,11 +246,15 @@ export async function applyDiffToolLegacy( await cline.diffViewProvider.reset() + // Process any queued messages after file edit completes + cline.processQueuedMessages() + return } } catch (error) { await handleError("applying diff", error) await cline.diffViewProvider.reset() + cline.processQueuedMessages() return } } diff --git a/src/core/tools/insertContentTool.ts b/src/core/tools/insertContentTool.ts index e22a3681671..e7d3a06ab92 100644 --- a/src/core/tools/insertContentTool.ts +++ b/src/core/tools/insertContentTool.ts @@ -187,6 +187,9 @@ export async function insertContentTool( pushToolResult(message) await cline.diffViewProvider.reset() + + // Process any queued messages after file edit completes + cline.processQueuedMessages() } catch (error) { handleError("insert content", error) await cline.diffViewProvider.reset() diff --git a/src/core/tools/multiApplyDiffTool.ts b/src/core/tools/multiApplyDiffTool.ts index 50695b1da73..d0fe6557503 100644 --- a/src/core/tools/multiApplyDiffTool.ts +++ b/src/core/tools/multiApplyDiffTool.ts @@ -172,6 +172,7 @@ Original error: ${errorMessage}` TelemetryService.instance.captureDiffApplicationError(cline.taskId, cline.consecutiveMistakeCount) await cline.say("diff_error", `Failed to parse apply_diff XML: ${errorMessage}`) pushToolResult(detailedError) + cline.processQueuedMessages() return } } else if (legacyPath && typeof legacyDiffContent === "string") { @@ -195,6 +196,7 @@ Original error: ${errorMessage}` "args (or legacy 'path' and 'diff' parameters)", ) pushToolResult(errorMsg) + cline.processQueuedMessages() return } @@ -210,6 +212,7 @@ Original error: ${errorMessage}` : "args (must contain at least one valid file element)", ), ) + cline.processQueuedMessages() return } @@ -675,10 +678,12 @@ ${errorDetails ? `\nTechnical details:\n${errorDetails}\n` : ""} // Push the final result combining all operation results pushToolResult(results.join("\n\n") + singleBlockNotice) + cline.processQueuedMessages() return } catch (error) { await handleError("applying diff", error) await cline.diffViewProvider.reset() + cline.processQueuedMessages() return } } diff --git a/src/core/tools/searchAndReplaceTool.ts b/src/core/tools/searchAndReplaceTool.ts index 49129344158..b0ee3947e1e 100644 --- a/src/core/tools/searchAndReplaceTool.ts +++ b/src/core/tools/searchAndReplaceTool.ts @@ -263,6 +263,9 @@ export async function searchAndReplaceTool( // Record successful tool usage and cleanup cline.recordToolUsage("search_and_replace") await cline.diffViewProvider.reset() + + // Process any queued messages after file edit completes + cline.processQueuedMessages() } catch (error) { handleError("search and replace", error) await cline.diffViewProvider.reset() diff --git a/src/core/tools/writeToFileTool.ts b/src/core/tools/writeToFileTool.ts index e82eab92bc8..5abd96a20af 100644 --- a/src/core/tools/writeToFileTool.ts +++ b/src/core/tools/writeToFileTool.ts @@ -308,6 +308,9 @@ export async function writeToFileTool( await cline.diffViewProvider.reset() + // Process any queued messages after file edit completes + cline.processQueuedMessages() + return } } catch (error) {