mirror of
https://github.com/stablyai/orca.git
synced 2026-10-22 08:03:37 +00:00
fix(ssh): preserve verdicts across synthetic kill exits
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { SSH_PROVIDER_UNREGISTERED_REASON } from '../../shared/pty-liveness-verdict'
|
||||
import { setupPtyIpcSuite } from './pty-ipc-test-harness'
|
||||
import {
|
||||
registerPtyHandlers,
|
||||
@@ -111,6 +112,23 @@ describe('stopping a PTY whose SSH provider is unregistered', () => {
|
||||
deletePtyOwnership('ssh-detached-stop')
|
||||
})
|
||||
|
||||
it('preserves lost-contact evidence for renderer IPC teardown', async () => {
|
||||
const ptyId = 'ssh-renderer-detached'
|
||||
setPtyOwnership(ptyId, 'ssh-dropped')
|
||||
const { runtime } = installController()
|
||||
try {
|
||||
await handlers.get('pty:kill')!(null, { id: ptyId })
|
||||
|
||||
expect(runtime.markPtyLivenessUnverifiable).toHaveBeenCalledWith(
|
||||
ptyId,
|
||||
SSH_PROVIDER_UNREGISTERED_REASON
|
||||
)
|
||||
expect(runtime.onPtyExit).toHaveBeenCalledWith(ptyId, -1, undefined)
|
||||
} finally {
|
||||
deletePtyOwnership(ptyId)
|
||||
}
|
||||
})
|
||||
|
||||
it('still confirms a stop the owning provider actually performed', async () => {
|
||||
const daemon = installObservableDaemonTestProvider()
|
||||
vi.spyOn(getLocalPtyProvider(), 'listProcesses').mockResolvedValue([])
|
||||
@@ -149,6 +167,34 @@ describe('stopping a PTY whose SSH provider is unregistered', () => {
|
||||
}
|
||||
})
|
||||
|
||||
it('preserves lost-contact evidence when fire-and-forget shutdown rejects', async () => {
|
||||
const connectionId = 'ssh-async-kill-drop'
|
||||
const ptyId = 'ssh-async-kill-pty'
|
||||
const provider = {
|
||||
onExit: vi.fn(() => () => {}),
|
||||
shutdown: vi.fn(async () => {
|
||||
throw new Error('relay disconnected during kill')
|
||||
})
|
||||
}
|
||||
registerSshPtyProvider(connectionId, provider as never)
|
||||
setPtyOwnership(ptyId, connectionId)
|
||||
try {
|
||||
const { controller, runtime } = installController()
|
||||
|
||||
expect(controller.kill(ptyId)).toBe(true)
|
||||
await vi.waitFor(() =>
|
||||
expect(runtime.markPtyLivenessUnverifiable).toHaveBeenCalledWith(
|
||||
ptyId,
|
||||
'relay disconnected during kill'
|
||||
)
|
||||
)
|
||||
expect(runtime.onPtyExit).toHaveBeenCalledWith(ptyId, -1, undefined)
|
||||
} finally {
|
||||
deletePtyOwnership(ptyId)
|
||||
unregisterSshPtyProvider(connectionId)
|
||||
}
|
||||
})
|
||||
|
||||
it('reports a provider-observed survivor as live', async () => {
|
||||
const connectionId = 'ssh-still-live'
|
||||
const ptyId = 'ssh-still-live-pty'
|
||||
|
||||
@@ -5559,6 +5559,12 @@ export function registerPtyHandlers(
|
||||
// Why: close runtime tails without clearing provider ownership, so
|
||||
// a retry can still target a PTY that survived the failed shutdown.
|
||||
if (!retired) {
|
||||
if (connectionId) {
|
||||
runtime?.markPtyLivenessUnverifiable?.(
|
||||
ptyId,
|
||||
err instanceof Error ? err.message : String(err)
|
||||
)
|
||||
}
|
||||
runtime?.onPtyExit(ptyId, -1, ptyIncarnationById.get(ptyId))
|
||||
}
|
||||
})
|
||||
@@ -5572,6 +5578,12 @@ export function registerPtyHandlers(
|
||||
`[pty] Failed to stop PTY ${ptyId}: ${err instanceof Error ? err.message : String(err)}`
|
||||
)
|
||||
if (!retiredRejectedPtyIds.has(ptyId)) {
|
||||
if (connectionId) {
|
||||
runtime?.markPtyLivenessUnverifiable?.(
|
||||
ptyId,
|
||||
err instanceof Error ? err.message : String(err)
|
||||
)
|
||||
}
|
||||
runtime?.onPtyExit(ptyId, -1, ptyIncarnationById.get(ptyId))
|
||||
}
|
||||
})
|
||||
@@ -7604,6 +7616,7 @@ export function registerPtyHandlers(
|
||||
// provider is unregistered; hydrated app-scoped ids can also arrive
|
||||
// before ownership is rebuilt. Tombstone instead of falling back local.
|
||||
const incarnationId = finishPtyShutdown(args.id, connectionId, store)
|
||||
runtime?.markPtyLivenessUnverifiable?.(args.id, SSH_PROVIDER_UNREGISTERED_REASON)
|
||||
runtime?.onPtyExit(args.id, -1, incarnationId)
|
||||
rememberSyntheticKillExit(args.id)
|
||||
sendPtyExitToRenderer({ id: args.id, code: -1 })
|
||||
|
||||
Reference in New Issue
Block a user