* fix(tmux): send Ctrl+C before kill-pane and respawn-pane to prevent orphaned processes
* fix(tmux-subagent): prevent premature pane closure with stability detection
Implements stability detection pattern from background-agent to prevent
tmux panes from closing while agents are still working (issue #1330).
Problem: Session status 'idle' doesn't mean 'finished' - agent may still
be thinking/reasoning. Previous code closed panes immediately on idle.
Solution:
- Require MIN_STABILITY_TIME_MS (10s) before stability detection activates
- Track message count changes to detect ongoing activity
- Require STABLE_POLLS_REQUIRED (3) consecutive polls with same message count
- Double-check session status before closing
Changes:
- types.ts: Add lastMessageCount and stableIdlePolls to TrackedSession
- manager.ts: Implement stability detection in pollSessions()
- manager.test.ts: Add 4 tests for stability detection behavior
* test(tmux-subagent): improve stability detection tests to properly verify age gate
- First test now sets session age >10s and verifies 3 polls don't close
- Last test now does 5 polls to prove age gate prevents closure
- Added comments explaining what each poll does
* fix: prevent zombie processes with proper process lifecycle management
- Await proc.exited for fire-and-forget spawns in tmux-utils.ts
- Remove competing process.exit() calls from LSP client and skill-mcp-manager
signal handlers to let background-agent manager coordinate final exit
- Await process exit after kill() in interactive-bash timeout handler
- Await process exit after kill() in LSP client stop() method
These changes ensure spawned processes are properly reaped and prevent
orphan/zombie processes when running with tmux integration.
* fix: address Copilot review comments on process cleanup
- LSP cleanup: use async/sync split with Promise.allSettled for proper subprocess cleanup
- LSP stop(): make idempotent by nulling proc before await to prevent race conditions
- Interactive-bash timeout: use .then()/.catch() pattern instead of async callback to avoid unhandled rejections
- Skill-mcp-manager: use void+catch pattern for fire-and-forget signal handlers
* fix: address remaining Copilot review comments
- interactive-bash: reject timeout immediately, fire-and-forget zombie cleanup
- skill-mcp-manager: update comments to accurately describe signal handling strategy
* fix: address additional Copilot review comments
- LSP stop(): add 5s timeout to prevent indefinite hang on stuck processes
- tmux-utils: log warnings when pane title setting fails (both spawn/replace)
- BackgroundManager: delay process.exit() to next tick via setImmediate to allow other signal handlers to complete cleanup
* fix: address code review findings
- Increase exit delay from setImmediate to 100ms setTimeout to allow async cleanup
- Use asyncCleanup for SIGBREAK on Windows for consistency with SIGINT/SIGTERM
- Add try/catch around stderr read in spawnTmuxPane for consistency with replaceTmuxPane
* fix: address latest Copilot review comments
- LSP stop(): properly clear timeout when proc.exited wins the race
- BackgroundManager: use process.exitCode before delayed exit for cleaner shutdown
- spawnTmuxPane: remove redundant log import, reuse existing one
* fix: address latest Copilot review comments
- LSP stop(): escalate to SIGKILL on timeout, add logging
- tmux spawnTmuxPane/replaceTmuxPane: drain stderr immediately to avoid backpressure
* fix: address latest Copilot review comments
- Add .catch() to asyncCleanup() signal handlers to prevent unhandled rejections
- Await proc.exited after SIGKILL with 1s timeout to confirm termination
* fix: increase exit delay to 6s to accommodate LSP cleanup
LSP cleanup can take up to 5s (timeout) + 1s (SIGKILL wait), so the exit
delay must be at least 6s to ensure child processes are properly reaped.
- Account for tmux pane dividers (1 char) in all size calculations
- Reduce MIN_PANE_WIDTH from 53 to 52 to fit 2 columns in standard terminals
- Fix enforceMainPaneWidth to use (windowWidth - divider) / 2
- Add virtual mainPane handling for close-spawn eviction loop
- Add comprehensive decision-engine tests (23 test cases)