diff --git a/src/main/runtime/orca-runtime.test.ts b/src/main/runtime/orca-runtime.test.ts index f64a163cfe..1b2410224d 100644 --- a/src/main/runtime/orca-runtime.test.ts +++ b/src/main/runtime/orca-runtime.test.ts @@ -6989,6 +6989,30 @@ describe('OrcaRuntimeService', () => { } }) + it('marks a direct dispatch failed when its worker PTY exits', () => { + const runtime = createRuntime() + const workerHandle = runtime.preAllocateHandleForPty('pty-1') + const failDispatch = vi.fn() + const updateTaskStatus = vi.fn() + const insertMessage = vi.fn() + runtime.setOrchestrationDb({ + getActiveDispatchForTerminal: vi.fn((handle: string) => + handle === workerHandle ? { id: 'ctx-1', task_id: 'task-1' } : undefined + ), + failDispatch, + getActiveCoordinatorRun: vi.fn(() => undefined), + updateTaskStatus, + insertMessage + } as never) + syncSinglePty(runtime) + + runtime.onPtyExit('pty-1', -1) + + expect(failDispatch).toHaveBeenCalledWith('ctx-1', 'Agent exited with code -1') + expect(updateTaskStatus).toHaveBeenCalledWith('task-1', 'failed', 'Agent exited with code -1') + expect(insertMessage).not.toHaveBeenCalled() + }) + it('keeps stale-title timers isolated per PTY', async () => { vi.useFakeTimers() try { diff --git a/src/main/runtime/orca-runtime.ts b/src/main/runtime/orca-runtime.ts index 3e7ba7e279..fd66a1e2e3 100644 --- a/src/main/runtime/orca-runtime.ts +++ b/src/main/runtime/orca-runtime.ts @@ -10308,20 +10308,25 @@ export class OrcaRuntimeService { // the unexpected exit on its next check cycle, even if the circuit breaker // hasn't tripped yet. const run = this._orchestrationDb.getActiveCoordinatorRun() - if (run) { - this._orchestrationDb.insertMessage({ - from: handle, - to: run.coordinator_handle, - subject: `Agent exited unexpectedly (code ${exitCode})`, - type: 'escalation', - priority: 'high', - payload: JSON.stringify({ - taskId: dispatch.task_id, - exitCode, - handle - }) - }) + if (!run) { + // Why: a direct dispatch has no coordinator loop to consume the ready + // retry. Mark it failed so an exited agent cannot silently lose the task. + this._orchestrationDb.updateTaskStatus(dispatch.task_id, 'failed', errorContext) + return } + + this._orchestrationDb.insertMessage({ + from: handle, + to: run.coordinator_handle, + subject: `Agent exited unexpectedly (code ${exitCode})`, + type: 'escalation', + priority: 'high', + payload: JSON.stringify({ + taskId: dispatch.task_id, + exitCode, + handle + }) + }) } async listTerminals(