fix(#2822): properly cleanup tmux sessions on process shutdown

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.
This commit is contained in:
YeonGyu-Kim
2026-03-27 15:23:48 +09:00
parent 5765168af4
commit 127626a122
2 changed files with 23 additions and 4 deletions
+9
View File
@@ -7,6 +7,7 @@ import { BackgroundManager } from "./features/background-agent"
import { SkillMcpManager } from "./features/skill-mcp-manager" import { SkillMcpManager } from "./features/skill-mcp-manager"
import { initTaskToastManager } from "./features/task-toast-manager" import { initTaskToastManager } from "./features/task-toast-manager"
import { TmuxSessionManager } from "./features/tmux-subagent" import { TmuxSessionManager } from "./features/tmux-subagent"
import { registerManagerForCleanup } from "./features/background-agent/process-cleanup"
import { createConfigHandler } from "./plugin-handlers" import { createConfigHandler } from "./plugin-handlers"
import { log } from "./shared" import { log } from "./shared"
import { markServerRunningInProcess } from "./shared/tmux/tmux-utils/server-health" import { markServerRunningInProcess } from "./shared/tmux/tmux-utils/server-health"
@@ -30,6 +31,14 @@ export function createManagers(args: {
markServerRunningInProcess() markServerRunningInProcess()
const tmuxSessionManager = new TmuxSessionManager(ctx, tmuxConfig) 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( const backgroundManager = new BackgroundManager(
ctx, ctx,
pluginConfig.background_task, pluginConfig.background_task,
@@ -11,7 +11,7 @@ function registerProcessSignal(
handler() handler()
if (exitAfter) { if (exitAfter) {
process.exitCode = 0 process.exitCode = 0
setTimeout(() => process.exit(), 6000).unref() setTimeout(() => process.exit(), 6000)
} }
} }
process.on(signal, listener) process.on(signal, listener)
@@ -32,16 +32,26 @@ export function registerManagerForCleanup(manager: CleanupTarget): void {
if (cleanupRegistered) return if (cleanupRegistered) return
cleanupRegistered = true cleanupRegistered = true
let cleanupPromise: Promise<void> | undefined
const cleanupAll = () => { const cleanupAll = () => {
if (cleanupPromise) return
const promises: Promise<void>[] = []
for (const m of cleanupManagers) { for (const m of cleanupManagers) {
try { try {
void Promise.resolve(m.shutdown()).catch((error) => { promises.push(
log("[background-agent] Error during async shutdown cleanup:", error) Promise.resolve(m.shutdown()).catch((error) => {
}) log("[background-agent] Error during async shutdown cleanup:", error)
})
)
} catch (error) { } catch (error) {
log("[background-agent] Error during shutdown cleanup:", 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 => { const registerSignal = (signal: ProcessCleanupEvent, exitAfter: boolean): void => {