From 127626a12220033f4a7254aa2cbc20563080cdbe Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Fri, 27 Mar 2026 15:23:48 +0900 Subject: [PATCH] fix(#2822): properly cleanup tmux sessions on process shutdown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two issues fixed: 1. process-cleanup.ts used fire-and-forget void Promise for shutdown handlers — now properly collects and awaits all cleanup promises via Promise.allSettled, with dedup guard to prevent double cleanup 2. TmuxSessionManager was never registered for process cleanup — now registered in create-managers.ts via registerManagerForCleanup Also fixed setTimeout().unref() which could let the process exit before cleanup completes. --- src/create-managers.ts | 9 +++++++++ .../background-agent/process-cleanup.ts | 18 ++++++++++++++---- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/create-managers.ts b/src/create-managers.ts index d6e548de4..81023071c 100644 --- a/src/create-managers.ts +++ b/src/create-managers.ts @@ -7,6 +7,7 @@ import { BackgroundManager } from "./features/background-agent" import { SkillMcpManager } from "./features/skill-mcp-manager" import { initTaskToastManager } from "./features/task-toast-manager" import { TmuxSessionManager } from "./features/tmux-subagent" +import { registerManagerForCleanup } from "./features/background-agent/process-cleanup" import { createConfigHandler } from "./plugin-handlers" import { log } from "./shared" import { markServerRunningInProcess } from "./shared/tmux/tmux-utils/server-health" @@ -30,6 +31,14 @@ export function createManagers(args: { markServerRunningInProcess() const tmuxSessionManager = new TmuxSessionManager(ctx, tmuxConfig) + registerManagerForCleanup({ + shutdown: async () => { + await tmuxSessionManager.cleanup().catch((error) => { + log("[create-managers] tmux cleanup error during process shutdown:", error) + }) + }, + }) + const backgroundManager = new BackgroundManager( ctx, pluginConfig.background_task, diff --git a/src/features/background-agent/process-cleanup.ts b/src/features/background-agent/process-cleanup.ts index a2f9b4b72..d2627fecb 100644 --- a/src/features/background-agent/process-cleanup.ts +++ b/src/features/background-agent/process-cleanup.ts @@ -11,7 +11,7 @@ function registerProcessSignal( handler() if (exitAfter) { process.exitCode = 0 - setTimeout(() => process.exit(), 6000).unref() + setTimeout(() => process.exit(), 6000) } } process.on(signal, listener) @@ -32,16 +32,26 @@ export function registerManagerForCleanup(manager: CleanupTarget): void { if (cleanupRegistered) return cleanupRegistered = true + let cleanupPromise: Promise | undefined + const cleanupAll = () => { + if (cleanupPromise) return + const promises: Promise[] = [] for (const m of cleanupManagers) { try { - void Promise.resolve(m.shutdown()).catch((error) => { - log("[background-agent] Error during async shutdown cleanup:", error) - }) + promises.push( + Promise.resolve(m.shutdown()).catch((error) => { + log("[background-agent] Error during async shutdown cleanup:", error) + }) + ) } catch (error) { log("[background-agent] Error during shutdown cleanup:", error) } } + cleanupPromise = Promise.allSettled(promises).then(() => {}) + cleanupPromise.then(() => { + log("[background-agent] All shutdown cleanup completed") + }) } const registerSignal = (signal: ProcessCleanupEvent, exitAfter: boolean): void => {