fix(ssh): keep a reattached PTY live when its delivery cancellation is unproven

On the restoreRequired retry path the first attach already answered with an
incarnation, so the remote PTY is proven alive and only its delivery record was
retired. Filing that as ambiguous exit evidence recorded a live agent as
possibly dead. Report it as restore-required and accept the PTY as live.

Both errors already map to deliveryUnresumable in the renderer and to a
'detached' lease, so the pane behaves exactly as before.
This commit is contained in:
Brennan Benson
2026-08-20 01:43:32 -07:00
parent 23d7629c8d
commit 32f9225007
2 changed files with 10 additions and 5 deletions
@@ -303,7 +303,7 @@ describe('SSH PTY reattach when the relay requires source restoration', () => {
expect(mux.request.mock.calls.filter((call) => call[0] === 'pty.attach')).toHaveLength(2)
})
it('does not install a second activation when the first cancellation is unverifiable', async () => {
it('keeps the PTY live when the first activation cancellation is unproven', async () => {
const mux = createMockMux()
const activation = {
status: 'pending',
@@ -339,10 +339,12 @@ describe('SSH PTY reattach when the relay requires source restoration', () => {
const message = await spawnError(provider)
expect(message).toContain(SSH_PTY_LIVENESS_UNVERIFIABLE_ERROR)
// The attach answered with an incarnation, so the PTY is proven live; only the retry is blocked.
expect(message).toContain(SSH_PTY_RESTORE_REQUIRED_ERROR)
expect(message).not.toContain(SSH_PTY_LIVENESS_UNVERIFIABLE_ERROR)
expect(message).not.toContain(SSH_SESSION_EXPIRED_ERROR)
expect(provider.hasPty('ssh:conn-1@@pty-old')).toBe(false)
await expect(provider.probePtyLiveness('ssh:conn-1@@pty-old')).resolves.toBeNull()
expect(provider.hasPty('ssh:conn-1@@pty-old')).toBe(true)
await expect(provider.probePtyLiveness('ssh:conn-1@@pty-old')).resolves.toBe(true)
expect(mux.request.mock.calls.filter((call) => call[0] === 'pty.attach')).toHaveLength(1)
expect(mux.request.mock.calls.filter((call) => call[0] === 'pty.spawn')).toHaveLength(0)
expect(mux.request).toHaveBeenCalledWith('pty.cancelDelivery', {
@@ -246,8 +246,11 @@ export async function reattachSshPtySessionForSpawn(
if (unresumable.sourceActivationLease) {
const canceled = await unresumable.sourceActivationLease.rollback()
if (!canceled) {
// Why: the attach answered with an incarnation, so the PTY is live; only its delivery
// is stuck, and an unproven cancellation just blocks the second attach.
args.acceptLivePty(unresumable.id)
throw new Error(
`${SSH_PTY_LIVENESS_UNVERIFIABLE_ERROR}: ${toRelaySshPtyId(args.connectionId, unresumable.id)}`
`${SSH_PTY_RESTORE_REQUIRED_ERROR}: ${toRelaySshPtyId(args.connectionId, unresumable.id)}`
)
}
}