From 17e85008ddf1ffa48ff55b662e8409bbf8b242b6 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Sun, 2 Aug 2026 17:20:22 -0700 Subject: [PATCH] fix(codex): contain detached restart cleanup --- .../codex-detached-pane-restart-scheduler.ts | 6 +++++- .../terminal-pane/codex-detached-pane-restart.test.ts | 1 + .../terminal-pane/codex-detached-pane-restart.ts | 8 +++----- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/renderer/src/components/terminal-pane/codex-detached-pane-restart-scheduler.ts b/src/renderer/src/components/terminal-pane/codex-detached-pane-restart-scheduler.ts index 7039c57d96d..96c33225498 100644 --- a/src/renderer/src/components/terminal-pane/codex-detached-pane-restart-scheduler.ts +++ b/src/renderer/src/components/terminal-pane/codex-detached-pane-restart-scheduler.ts @@ -70,10 +70,14 @@ function scheduleClaimSweep(): void { const generation = executorGeneration // Exact mounted-owner checks fence the claim; a microtask only exits the store write. queueMicrotask(() => { - sweepQueued = false if (!executorInstalled || executorGeneration !== generation) { return } + sweepQueued = false + if (sweepRunning) { + sweepRequestedAfterRun = true + return + } sweepRunning = true void sweepUnclaimedCodexPaneRestarts().finally(() => { sweepRunning = false diff --git a/src/renderer/src/components/terminal-pane/codex-detached-pane-restart.test.ts b/src/renderer/src/components/terminal-pane/codex-detached-pane-restart.test.ts index 528ada86623..10cbd7a30a4 100644 --- a/src/renderer/src/components/terminal-pane/codex-detached-pane-restart.test.ts +++ b/src/renderer/src/components/terminal-pane/codex-detached-pane-restart.test.ts @@ -395,6 +395,7 @@ describe('codex detached pane restart executor', () => { command: 'codex', launchAgent: 'codex' }) + expect(useAppStore.getState().suppressedPtyExitIds[OLD_PTY]).toBeUndefined() pendingKill.resolve() await restart }) diff --git a/src/renderer/src/components/terminal-pane/codex-detached-pane-restart.ts b/src/renderer/src/components/terminal-pane/codex-detached-pane-restart.ts index 69b6d9aaac8..c7a2f90f707 100644 --- a/src/renderer/src/components/terminal-pane/codex-detached-pane-restart.ts +++ b/src/renderer/src/components/terminal-pane/codex-detached-pane-restart.ts @@ -160,12 +160,13 @@ async function executeDetachedCodexPaneRestart( reopenCurrentCodexRestartPrompt(located, ptyId) return } - const killPromise = killReplacedCodexPanePty(ptyId) const store = useAppStore.getState() + store.suppressPtyExit(ptyId) store.clearTabPtyId(located.tab.id, ptyId) + store.consumeSuppressedPtyExit(ptyId) store.queueTabStartupCommand(located.tab.id, { ...CODEX_ACCOUNT_RESTART_STARTUP }) store.clearCodexRestartNotice(ptyId) - await killPromise + await killReplacedCodexPanePty(ptyId) return } const { worktreeId, tab, leafId } = located @@ -310,7 +311,6 @@ async function killReplacedCodexPanePty(ptyId: string): Promise { // Why the disposal: a parked tab's exit sidecar treats any exit as the pane // dying — it would collapse the just-rebound leaf or close the whole tab. disposeParkedTerminalWatchersForPtyIds([ptyId]) - useAppStore.getState().suppressPtyExit(ptyId) for (const snapshot of unregisterPtyDataHandlers([ptyId])) { snapshot.commit() } @@ -322,6 +322,4 @@ async function killReplacedCodexPanePty(ptyId: string): Promise { console.warn('[codex-restart] failed to kill replaced Codex pane PTY:', err) } discardPreHandlerPtyState(ptyId) - // No exit consumer remains after the watcher/handler disposal above. - useAppStore.getState().consumeSuppressedPtyExit(ptyId) }