diff --git a/packages/coding-agent/src/modes/rpc/rpc-mode.ts b/packages/coding-agent/src/modes/rpc/rpc-mode.ts index 0eb081d72..0d1d1e884 100644 --- a/packages/coding-agent/src/modes/rpc/rpc-mode.ts +++ b/packages/coding-agent/src/modes/rpc/rpc-mode.ts @@ -181,10 +181,17 @@ async function drainQueuedSfFeedbackCommands(cwd: string): Promise { } function scheduleQueuedSfFeedbackDrain(cwd: string): void { - const timer = setTimeout(() => { + // Intentionally NOT unref'd: the drain must keep the process alive until it + // finishes. In a long-running rpc-mode child this is moot — the process has + // plenty of other handles. In a transient drainer subprocess (packaged- + // standalone path) the setTimeout was the only event-loop handle, so an + // unref'd timer let Node exit before the drain ran, stranding the renamed + // .draining file forever (sf-mpa6wuhm-wwddd1). The setTimeout(…, 0) is + // still desirable so the RPC response goes back to the caller first; we + // just don't want the timer to be release-eligible. + setTimeout(() => { void drainQueuedSfFeedbackCommands(cwd); }, 0); - timer.unref?.(); } async function captureProcessWrites(