fix(ssh): distinguish host exits from relay loss

This commit is contained in:
Brennan Benson
2026-08-16 19:40:15 -07:00
parent f69e8b5c3e
commit 3cf030c2cd
3 changed files with 23 additions and 4 deletions
+3 -1
View File
@@ -3910,7 +3910,9 @@ export function registerPtyHandlers(
return release
},
finalizeExit: (event) => {
runtime?.onPtyExit(event.id, event.code, event.ptyIncarnation)
runtime?.onPtyExit(event.id, event.code, event.ptyIncarnation, {
hostExitConfirmed: true
})
finalizePtyExitForRenderer(event)
},
pauseProvider: (generation, id) => {
+11 -3
View File
@@ -14712,13 +14712,21 @@ export class OrcaRuntimeService {
}
}
onPtyExit(ptyId: string, exitCode: number, exitIncarnationId?: PtyIncarnationId): void {
onPtyExit(
ptyId: string,
exitCode: number,
exitIncarnationId?: PtyIncarnationId,
options?: { hostExitConfirmed?: boolean }
): void {
const pty = this.ptysById.get(ptyId)
if (exitIncarnationId && pty?.incarnationId && exitIncarnationId !== pty.incarnationId) {
return
}
const preservesAbnormalSshSurface =
this.isSshOwnedPtyId(ptyId) && pty?.connectionId != null && exitCode < 0
this.isSshOwnedPtyId(ptyId) &&
pty?.connectionId != null &&
exitCode < 0 &&
options?.hostExitConfirmed !== true
if (preservesAbnormalSshSurface) {
if (this.getPtyLivenessVerdict(ptyId)?.status !== 'unverifiable') {
this.markPtyLivenessUnverifiable(ptyId, SSH_EXIT_UNCONFIRMED_REASON)
@@ -14855,7 +14863,7 @@ export class OrcaRuntimeService {
this.setPairedRendererSessionOwnership(pty.ptyId, false)
pty.disconnectedAt = Date.now()
pty.lastExitCode = exitCode
if (exitCode >= 0) {
if (exitCode >= 0 || options?.hostExitConfirmed === true) {
// A real wait status from the owning host is the death certificate; the
// synthetic -1 we emit on a failed/unroutable stop is not.
this.forgetPtyLivenessVerdict(ptyId)
@@ -83,6 +83,15 @@ describe('inventory sweep liveness verdicts', () => {
})
})
it('accepts a current owning-host exit even when its numeric code is negative', () => {
const runtime = makeRuntimeMissingFromInventory(() => null)
runtime.markPtyLivenessUnverifiable(REMOTE_PTY_ID, 'inventory transport failed')
runtime.onPtyExit(REMOTE_PTY_ID, -1, undefined, { hostExitConfirmed: true })
expect(runtime.getPtyLivenessVerdict(REMOTE_PTY_ID)).toBeNull()
})
it('records lost contact when no provider can answer for the PTY', async () => {
const runtime = makeRuntimeMissingFromInventory(() => null)