Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 37 additions & 3 deletions src/core/task/Task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -845,9 +845,19 @@ export class Task extends EventEmitter<TaskEvents> 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)
}
}
}

Expand Down Expand Up @@ -2898,4 +2908,28 @@ export class Task extends EventEmitter<TaskEvents> 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)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ describe("applyDiffTool experiment routing", () => {
api: {
getModel: vi.fn().mockReturnValue({ id: "test-model" }),
},
processQueuedMessages: vi.fn(),
} as any

mockBlock = {
Expand Down
1 change: 1 addition & 0 deletions src/core/tools/__tests__/multiApplyDiffTool.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ describe("multiApplyDiffTool", () => {
trackFileContext: vi.fn().mockResolvedValue(undefined),
},
didEditFile: false,
processQueuedMessages: vi.fn(),
} as any

mockAskApproval = vi.fn().mockResolvedValue(true)
Expand Down
5 changes: 5 additions & 0 deletions src/core/tools/applyDiffTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ export async function applyDiffToolLegacy(

if (!didApprove) {
await cline.diffViewProvider.revertChanges() // Cline likely handles closing the diff view
cline.processQueuedMessages()
return
}

Expand Down Expand Up @@ -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
}
}
3 changes: 3 additions & 0 deletions src/core/tools/insertContentTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
5 changes: 5 additions & 0 deletions src/core/tools/multiApplyDiffTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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") {
Expand All @@ -195,6 +196,7 @@ Original error: ${errorMessage}`
"args (or legacy 'path' and 'diff' parameters)",
)
pushToolResult(errorMsg)
cline.processQueuedMessages()
return
}

Expand All @@ -210,6 +212,7 @@ Original error: ${errorMessage}`
: "args (must contain at least one valid file element)",
),
)
cline.processQueuedMessages()
return
}

Expand Down Expand Up @@ -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
}
}
3 changes: 3 additions & 0 deletions src/core/tools/searchAndReplaceTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
3 changes: 3 additions & 0 deletions src/core/tools/writeToFileTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Loading