diff --git a/server/lib/tuiHandshake.js b/server/lib/tuiHandshake.js index 93c5f085a..ed3520bb3 100644 --- a/server/lib/tuiHandshake.js +++ b/server/lib/tuiHandshake.js @@ -837,8 +837,15 @@ export function createMcpBootTracker() { observe(strippedText) { if (active) return true; if (typeof strippedText !== 'string' || !strippedText) return active; - tail = (tail + strippedText).slice(-MCP_BOOT_TAIL_CAP); - if (isMcpBootSignal(tail)) active = true; + // Search BEFORE truncating. Codex commonly sends a whole-screen repaint + // in one PTY chunk: the MCP status sits above the composer/footer, and + // that suffix alone can exceed MCP_BOOT_TAIL_CAP. Truncating first drops + // a perfectly visible `Starting MCP servers` banner and falls back to the + // ordinary three-paste budget while Codex is still booting. The retained + // tail exists only to join a marker split across consecutive chunks. + const combined = tail + strippedText; + if (isMcpBootSignal(combined)) active = true; + tail = combined.slice(-MCP_BOOT_TAIL_CAP); return active; }, get active() { return active; }, diff --git a/server/services/agentTuiSpawning.test.js b/server/services/agentTuiSpawning.test.js index 0ed20a712..68ef58394 100644 --- a/server/services/agentTuiSpawning.test.js +++ b/server/services/agentTuiSpawning.test.js @@ -1666,8 +1666,13 @@ describe('spawnTuiAgent runtime', () => { runSpawn({ prompt: 'evaluate our animation prompts and generate drafts' }); await flushMicrotasks(); - // Codex prints its MCP-boot banner during startup → latches the boot tracker. - await capturedOnData(Buffer.from('>_ OpenAI Codex (v0.144.1)\nStarting MCP servers (0/3): codex_apps, node_repl, playwright\n')); + // Codex prints its MCP-boot banner during a whole-screen repaint. The + // composer/footer after the status line exceeds the tracker's 256-char + // cross-chunk tail in real transcripts; the tracker must search the full + // new repaint before truncating that tail, or it misses the banner and + // kills the run at the ordinary three-attempt cap. + const footer = `\n\n› Ask Codex to do anything\n\n gpt-5.6-sol high · ${'workspace footer '.repeat(24)}`; + await capturedOnData(Buffer.from(`>_ OpenAI Codex (v0.148.0)\nStarting MCP servers (1/2): codex_apps (0s • esc to interrupt)${footer}`)); await flushMicrotasks(); // Fire the paste (past prompt-delay floor + readiness idle threshold). await vi.advanceTimersByTimeAsync(2000);