0b87e89d40
Field-level reproduction (2026-09-02, v0.21.0, in the issue thread): an assistant row with content length 0 whose user-visible response exists only in reasoning / reasoning_content / codex_message_items renders live, then disappears from the transcript after a session/profile switch-back. DB intact - the row is still there on every re-read; only the rehydrated render loses it. Six independent confirmations across macOS and Windows since 2026-07-22; all prior fixes (#68329 envelope normalization, closed implemented_on_main; #77644 mid-turn reconcile; #101470 compaction-display projections) addressed adjacent producers, not this one. Two producers in toChatMessages hydration, both pinned by the new chat-messages.reasoning-survival.test.ts (fails 9/9 on current main): 1. codex_message_items never read. Responses-API turns can persist with `content` empty while the reply the user saw lives only in the message-items sidecar ({type:'message', role:'assistant', phase, content:[{type:'output_text', text}]}). The live stream painted that text; hydration ignored the sidecar, so the rehydrated bubble came back blank - and the blank chatMessageText at the same role-ordinal then made reconcileResumeMessages drop the cached row's parts (sameText/extension/isLiveTailRow all fail), erasing the reply on every switch-back. Fix: codexMessageItemText() extracts non-commentary assistant output_text as the bubble's text when content and reasoning produced no parts. Persisted content still wins when present; commentary narration is never promoted. 2. the zero-parts drop. An assistant row hydrating to no parts at all returned early and vanished from the transcript entirely - the "all assistant messages gone; user messages remain" shape. Fix: a wholly empty assistant row (display_kind != 'hidden') paints a _(assistant response not persisted)_ placeholder instead of disappearing. Hidden scaffolding rows keep dropping as designed (pinned by a test). Also adds codex_message_items to the SessionMessage type (the gateway has shipped it since the branch-copy lane; the type never declared it). Verification (native Windows, node 24.17): - new file: 9 passed (fails 9/9 without the hydration changes) - src/lib full dir: 1243 passed - reconcile suites (utils + resume-structural-parts): 115 passed - streaming/timeline reasoning-part suites: 25 passed - tsc --build tsconfig.json: clean Fixes #68321