Fix #5179: Add timeout to shell execution complete event

- Add timeout mechanism to shellExecutionComplete promise in TerminalProcess
- Prevents indefinite hanging when VSCode's onDidEndTerminalShellExecution event doesn't fire
- Common issue on Mac systems where commands complete but event is not emitted
- Assumes success (exitCode: 0) after timeout to allow execution to proceed
- Add comprehensive test coverage for timeout scenario
- Uses existing shell integration timeout configuration
This commit is contained in:
Roo Code 2025-06-30 08:16:28 +00:00
parent 3a8ba27615
commit fd95eb9962
2 changed files with 58 additions and 1 deletions

View file

@ -101,7 +101,18 @@ export class TerminalProcess extends BaseTerminalProcess {
// Create promise that resolves when shell execution completes for this terminal
const shellExecutionComplete = new Promise<ExitCodeDetails>((resolve) => {
this.once("shell_execution_complete", (details: ExitCodeDetails) => resolve(details))
const timeoutId = setTimeout(() => {
console.warn(
"[TerminalProcess] Shell execution complete event not received within timeout, assuming success. This may indicate a VSCode shell integration issue on this platform.",
)
// Assume success if we don't get the event (common on Mac)
resolve({ exitCode: 0 })
}, Terminal.getShellIntegrationTimeout())
this.once("shell_execution_complete", (details: ExitCodeDetails) => {
clearTimeout(timeoutId)
resolve(details)
})
})
// Execute command

View file

@ -165,6 +165,52 @@ describe("TerminalProcess", () => {
await completePromise
expect(terminalProcess.isHot).toBe(false)
})
it("handles missing shell_execution_complete event with timeout", async () => {
// Temporarily suppress the expected console.warn for this test
const consoleWarnSpy = vi.spyOn(console, "warn").mockImplementation(() => {})
let completedOutput: string | undefined
terminalProcess.on("completed", (output) => {
completedOutput = output
})
// Mock stream data with shell integration sequences but NO shell_execution_complete event
mockStream = (async function* () {
yield "\x1b]633;C\x07" // Command start sequence
yield "Command output\n"
yield "More output"
yield "\x1b]633;D\x07" // Command end sequence
// NOTE: We intentionally do NOT emit "shell_execution_complete" to simulate the Mac issue
})()
mockTerminal.shellIntegration.executeCommand.mockReturnValue({
read: vi.fn().mockReturnValue(mockStream),
})
// Set a very short timeout for testing (override the default)
vi.spyOn(Terminal, "getShellIntegrationTimeout").mockReturnValue(100)
const runPromise = terminalProcess.run("test command")
terminalProcess.emit("stream_available", mockStream)
// Wait for the command to complete via timeout
await runPromise
// Verify the command completed successfully despite missing event
expect(completedOutput).toBe("Command output\nMore output")
expect(terminalProcess.isHot).toBe(false)
// Verify warning was logged
expect(consoleWarnSpy).toHaveBeenCalledWith(
"[TerminalProcess] Shell execution complete event not received within timeout, assuming success. This may indicate a VSCode shell integration issue on this platform.",
)
// Restore mocks
consoleWarnSpy.mockRestore()
vi.restoreAllMocks()
})
})
describe("continue", () => {