fix: prevent hang when sending message during command_output

When a user sends a message while a command is running (command_output state),
the UI would hang because handleChatReset() disabled all inputs but no new
command_output ask was sent to re-enable them (since runInBackground became true).

This fix changes the behavior so that:
1. For command_output, send terminalOperation with the user message instead of
   askResponse
2. Clear the input fields but skip handleChatReset() to keep UI responsive
3. In backend, if terminalOperation includes text/images, resolve the pending
   ask so the loop can continue

Fixes #10329
This commit is contained in:
Roo Code 2026-01-09 01:27:00 +00:00
parent caa37792ca
commit 2712853f4e
3 changed files with 38 additions and 3 deletions

View file

@ -1588,8 +1588,24 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
}
}
async handleTerminalOperation(terminalOperation: "continue" | "abort") {
/**
* Handle terminal operations (continue/abort) with optional user message.
*
* When the user sends a message while a command is running (command_output ask),
* we need to both continue the terminal AND resolve the pending ask with the
* user's message. This prevents the UI from hanging.
*
* @param terminalOperation - "continue" to proceed or "abort" to kill the command
* @param text - Optional user message text
* @param images - Optional user message images
*/
async handleTerminalOperation(terminalOperation: "continue" | "abort", text?: string, images?: string[]) {
if (terminalOperation === "continue") {
// If user provided a message, resolve the pending ask with their message
// This allows the user to provide feedback while the command runs
if (text || (images && images.length > 0)) {
this.handleWebviewAskResponse("messageResponse", text, images)
}
this.terminalProcess?.continue()
} else if (terminalOperation === "abort") {
this.terminalProcess?.abort()

View file

@ -655,7 +655,11 @@ export const webviewMessageHandler = async (
case "terminalOperation":
if (message.terminalOperation) {
provider.getCurrentTask()?.handleTerminalOperation(message.terminalOperation)
// Pass text/images if user sent a message with the terminal operation
// This allows the user to provide additional context when continuing
// a long-running command
const resolved = await resolveIncomingImages({ text: message.text, images: message.images })
provider.getCurrentTask()?.handleTerminalOperation(message.terminalOperation, resolved.text, resolved.images)
}
break
case "clearTask":

View file

@ -616,11 +616,26 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
switch (
clineAskRef.current // Use clineAskRef.current
) {
case "command_output":
// For command_output, send terminalOperation with user's message
// instead of askResponse. This allows the command to continue
// running in the background while keeping the UI responsive.
// The user's message is passed to the backend to be used as
// additional context when the command finishes.
vscode.postMessage({
type: "terminalOperation",
terminalOperation: "continue",
text,
images,
})
// Clear input but don't fully reset UI to avoid hang
setInputValue("")
setSelectedImages([])
return // Don't call handleChatReset
case "followup":
case "tool":
case "browser_action_launch":
case "command": // User can provide feedback to a tool or command use.
case "command_output": // User can send input to command stdin.
case "use_mcp_server":
case "completion_result": // If this happens then the user has feedback for the completion result.
case "resume_task":