Skip to content

Commit e9acfae

Browse files
authored
🤖 fix: make temp directory cleanup non-blocking for faster stream interruption (#459)
## Problem When interrupting a stream (especially on SSH), the UI experienced a noticeable delay of 500ms-2s before responding. The user would hit Ctrl+C/Escape but the UI would remain in "streaming" state for 1-2 seconds. ## Root Cause The delay occurred because stream interruption waited for `processingPromise` to complete before emitting the `stream-abort` event. In the `finally` block of `processStreamWithCleanup()`, we were executing: ```typescript const result = await streamInfo.runtime.exec(`rm -rf "${streamInfo.runtimeTempDir}"`, { cwd: "~", timeout: 10, }); await result.exitCode; // Wait for completion ``` For SSH runtimes, this meant: 1. SSH connection to remote host 2. Execute recursive directory deletion 3. Wait for command completion 4. Close SSH connection 5. Return exit code Even with an empty directory, this SSH round-trip takes 500ms-2s, blocking the UI update. ## Solution Made the temp directory cleanup **fire-and-forget** using `void` promise. The cleanup still happens (and errors are logged), but it no longer blocks the critical path for emitting the `stream-abort` event. The change is safe because: - Temp directories are stream-specific (no conflicts between streams) - Cleanup is best-effort (failures already logged, don't affect stream) - No data loss (temp dir only contains tool outputs already captured in message) - OS cleanup as fallback ## Testing Verify with SSH workspace: 1. Start a stream that uses tools (generates temp directory) 2. Hit Ctrl+C to interrupt 3. UI should respond instantly (no 1-2 second delay) 4. Check logs to verify temp directory cleanup still happens in background _Generated with `cmux`_
1 parent 12bcd17 commit e9acfae

File tree

1 file changed

+12
-9
lines changed

1 file changed

+12
-9
lines changed

‎src/services/streamManager.ts‎

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -978,19 +978,22 @@ export class StreamManager extends EventEmitter {
978978
streamInfo.partialWriteTimer = undefined;
979979
}
980980

981-
// Clean up stream temp directory using runtime
981+
// Clean up stream temp directory using runtime (fire-and-forget)
982+
// Don't block stream completion waiting for directory deletion
983+
// This is especially important for SSH where rm -rf can take 500ms-2s
982984
if (streamInfo.runtimeTempDir) {
983-
try {
984-
const result = await streamInfo.runtime.exec(`rm -rf "${streamInfo.runtimeTempDir}"`, {
985+
void streamInfo.runtime
986+
.exec(`rm -rf "${streamInfo.runtimeTempDir}"`, {
985987
cwd: "~",
986988
timeout: 10,
989+
})
990+
.then(async (result) => {
991+
await result.exitCode;
992+
log.debug(`Cleaned up temp dir: ${streamInfo.runtimeTempDir}`);
993+
})
994+
.catch((error) => {
995+
log.error(`Failed to cleanup temp dir ${streamInfo.runtimeTempDir}:`, error);
987996
});
988-
await result.exitCode; // Wait for completion
989-
log.debug(`Cleaned up temp dir: ${streamInfo.runtimeTempDir}`);
990-
} catch (error) {
991-
log.error(`Failed to cleanup temp dir ${streamInfo.runtimeTempDir}:`, error);
992-
// Don't throw - cleanup is best-effort
993-
}
994997
}
995998

996999
this.workspaceStreams.delete(workspaceId);

0 commit comments

Comments
 (0)