diff --git a/src/main/agent-hooks/restored-subagent-liveness-sweep.test.ts b/src/main/agent-hooks/restored-subagent-liveness-sweep.test.ts index 394d6b001b2..616ba91102c 100644 --- a/src/main/agent-hooks/restored-subagent-liveness-sweep.test.ts +++ b/src/main/agent-hooks/restored-subagent-liveness-sweep.test.ts @@ -375,6 +375,33 @@ describe('restored subagent liveness sweep', () => { } }) + it('keeps a pane that rebinds its persisted PTY while the probe is pending', async () => { + const server = await restartWithInFlightSubagent() + const boundPtyIdByPaneKey: Record = {} + let resolveProbe!: (live: boolean | null) => void + const probeLiveLocalPty = vi.fn( + () => + new Promise((resolve) => { + resolveProbe = resolve + }) + ) + try { + const sweep = sweepWith(server, { + probeLiveLocalPty, + boundPtyIdByPaneKey, + persistedPtyIdByPaneKey: { [PANE]: PTY } + }) + await vi.waitFor(() => expect(probeLiveLocalPty).toHaveBeenCalledExactlyOnceWith(PTY)) + boundPtyIdByPaneKey[PANE] = PTY + resolveProbe(false) + + expect(await sweep).toBe(0) + expect(paneStatus(server).state).toBe('working') + } finally { + server.stop() + } + }) + it('leaves a pane whose lead is genuinely mid-turn at working', async () => { const server = await restartWithInFlightSubagent() try { diff --git a/src/main/agent-hooks/restored-subagent-liveness-sweep.ts b/src/main/agent-hooks/restored-subagent-liveness-sweep.ts index b1b92bf6959..e659e53c460 100644 --- a/src/main/agent-hooks/restored-subagent-liveness-sweep.ts +++ b/src/main/agent-hooks/restored-subagent-liveness-sweep.ts @@ -32,9 +32,8 @@ export async function sweepRestoredSubagentsWithoutLiveAgent( return await deps.reap( (worktreeId) => deps.isLocalExecutionHost(worktreeId), async (paneKey) => { - const resolvePtyId = (): string | undefined => - deps.getBoundPtyIdForPaneKey(paneKey) ?? deps.getPersistedPtyIdForPaneKey(paneKey) - const ptyId = resolvePtyId() + const boundPtyId = deps.getBoundPtyIdForPaneKey(paneKey) + const ptyId = boundPtyId ?? deps.getPersistedPtyIdForPaneKey(paneKey) if (!ptyId) { return true } @@ -45,7 +44,9 @@ export async function sweepRestoredSubagentsWithoutLiveAgent( probesByPtyId.set(ptyId, probe) } const live = await probe - return resolvePtyId() !== ptyId || live !== false + const currentBoundPtyId = deps.getBoundPtyIdForPaneKey(paneKey) + // Why: cold restore can rebind the persisted id while its absence probe is in flight. + return currentBoundPtyId !== boundPtyId || live !== false } catch { return true }