fix(byoa): tear down a Codex session when the thread handshake fails (#24)
CodexSession's app-server handshake is one-shot: `threadReq` is consumed at the `initialize` ack, and only a failed `thread/resume` ever re-issues a `thread/start`. So once the thread fails to open, `ready` can never flip again. `failPending()` settles the in-flight turn but leaves the process running — and the app-server SURVIVES rejecting a handshake (a malformed ~/.codex/config.toml, a model the account can't use, protocol drift after a codex upgrade). Since `alive` only checks `!exited && stdin.writable`, the session keeps advertising itself as usable, so the daemon reuses it on every later wake. `send()` then takes the `else this.queuedPrompt = prompt` branch, and with nothing left that can call `onThreadReady()`, that promise never resolves. The daemon awaits it with no timeout, so `busy` never clears, the big-brain semaphore slot is never released, and `beatRun()` keeps heartbeating the run row so the server's stale-run sweeper never flags it either. The user sees one "could not run on local codex" notice from turn 1 and then an agent that is silently and permanently dead until the daemon is restarted. CUMORA_TURN_TIMEOUT_MS, documented as the cap on a single persistent-engine turn, is wired only into ClaudeSession — CodexSession has no timer at all. Treat a failure that happens before the thread ever opened as fatal to the SESSION, not just the turn: remember the reason and stop the process. The daemon's existing `if (!session.alive) this.engineSession = null` then drops the zombie and the next wake spawns a clean session. Turn 1's result is unchanged (exitCode 1 plus the engine's real error text); turn 2 now settles immediately with that same real reason instead of hanging. Post-handshake failures are untouched — a turn/start error, an `error` notification, or `turn/completed` with status failed still fail only that turn and keep the session alive, because `ready` is true by then.
X
Xialie Zhuang committed
81e81def952fd2d66c05d215da2e1991148a8f97
Parent: 97f77ce
Committed by GitHub <noreply@github.com>
on 8/19/2026, 8:00:16 PM