## What changed
Show a brief starfield in the empty composer when a new task starts with Astra or the user selects Astra through `/model` before entering ordinary prompt content.
- Respect `tui.animations`, `tui.whimsy`, and true-color support; protect the placeholder and cursor and fade out by 15 seconds.
- Preserve eligibility through recognized slash commands, but permanently dismiss it after ordinary draft content, attachments, or work. Carry draft eligibility through startup and disconnected editing.
- Exclude resumed and forked tasks, and verify model-picker changes against the originating task and effective model when applied.
- Hide the effect during overlays or focus loss without restarting its deadline or scheduling hidden animation frames.
## Testing
Add regression tests and snapshots for task eligibility, model-picker transitions, draft and paste handling, history search, overlays, submissions, rendering protection, and animation expiry.
GitOrigin-RevId: 832705543e9cae1d561125ece853a16c4eefe627
## Why
Interrupted or failed turns can discard buffered answer or plan source, leaving incomplete math content missing from the transcript or unable to reflow correctly.
## What changed
Flush and consolidate both answer and plan streams before `finalize_turn` clears streaming state. Reuse the same helper for normal completion, preserving received source for terminal resizing and raw output mode changes.
## Testing
Add regression coverage for interrupted answers and plans, failed plans, and policy errors with unfinished math blocks, with and without a trailing newline. Verify source preservation and stable content across width and raw output mode changes, with narrow transcript snapshots. Update the usage activity test to verify that history insertion waits for stream consolidation.
GitOrigin-RevId: 1ae0c192e2fee00dc0b5ee2ac07f3d5f2313ad1b
## What changed
- Update the status heading from the latest usable reasoning line and retain it through tool activity and empty reasoning items. Keep completed reasoning in the expanded transcript only.
- Restore active reasoning after resume, thread switches, and session refreshes, including when earlier events have left the replay buffer.
- Default new embedded TUI threads to `model_reasoning_summary = "detailed"`, honoring explicit summary settings. Keep `concurrent_reasoning_summaries` opt-in and disable it when summaries are `none`.
- Persist the request's captured reasoning summary setting in context updates and local and remote compaction checkpoints.
- Animate status text with a smooth, grapheme-aware shimmer that respects terminal colors and reduced motion.
## Testing
Add regression coverage for reasoning status updates, transcript visibility, resume and buffered replay, outbound summary configuration, and summary settings across all three compaction modes. Add shimmer snapshots and checks for smooth brightness changes and Unicode grapheme preservation.
GitOrigin-RevId: 725c7de20f23166d7a3da6daeca617add5cbc600
## What changed
- Add feature-gated `/voice`, `/voice mute`, and `/voice stop` commands with local WebRTC audio and app-server signaling.
- Show live transcripts, conversation status, and microphone and speaker levels. Preserve captions and undelivered answers across thread switches.
- Speak final answers from voice handoffs while keeping delegated reasoning and commentary hidden and typed answers unspoken.
- Retry eligible startup failures once and clean up voice sessions on thread switches and disconnects.
- Stop voice and block late handoffs after a misalignment policy violation. Remove realtime event payloads and spoken text from receipt and debug logs.
## Testing
Add coverage for voice command mapping, rejected starts, disconnect cleanup, delegated final-answer speech, late handoff rejection, and transcript and footer rendering.
GitOrigin-RevId: 11def1f86b0e023b21d0d92072d6636ccad89815
## What changed
Replace horizontal turn separators with dim completion metadata after the final answer, including plain conversational answers. Show local completion times such as `done 2:32 PM`, adding the date for other days and the year for other years. Retain `Worked for` durations above sixty seconds and runtime metrics, and wrap metadata on narrow terminals.
Restore saved completion metadata when resuming conversations or loading older history. Keep each footer after its turn's items and deduplicate it across repeated notifications and overlapping pages. Only live completions fall back to the current time when a saved timestamp is unavailable. Failed, interrupted, and in-progress turns do not receive success footers.
Avoid submitting queued input or emitting completion notifications while replaying history.
## Testing
Add unit and snapshot coverage for timestamp formatting, duration thresholds, muted styling, narrow layouts, live and replayed completions, missing metadata, and pagination boundaries with overlapping pages.
GitOrigin-RevId: 308c35c6720720da43c9a391aa5a68c1b231c9a5
## What changed
- Prefetch ChatGPT account eligibility in the background and cache it across TUI widget replacements.
- Show an application link when Daybreak is available but not enabled, an Astra-specific explanation for unsupported models, and neutral copy when access is already present or eligibility cannot be determined.
- Refresh eligibility after account changes and reconnects without changing the selected model or delaying refusal handling.
## Testing
- Cover eligibility responses, account changes, model-specific notices, fallback behavior, and rendered notice snapshots.
GitOrigin-RevId: 7ae8613a946cb46b8f0b46bf47e5e15ae258ed86
## What changed
- Coalesce configuration, skill, sandbox, and MCP startup diagnostics into a single summary below the session header, including MCP and sign-in counts.
- Keep the complete warning text in the transcript and update the summary's transcript shortcut when key bindings change.
- Continue rendering warnings that arrive after work begins as inline history entries.
- Hide the MCP startup status row before a turn while preserving the configured interrupt shortcut.
## Testing
- Add coverage for warning ordering and deduplication, transcript details, MCP sign-in counts, resumed sessions, runtime warnings, reflow, backtracking, and interruption without a visible status row.
GitOrigin-RevId: 009657cf04f1d3a52270cf096eebf9db7513c040
## Why
When ordinary included usage is exhausted, eligible users need a way to keep working with the backend-provided Luna Reserve quota and return to their previous model once ordinary usage recovers.
## What changed
- Automatically switch eligible TUI tasks to Luna Reserve after an authoritative account usage read, preserving the prior model and reasoning effort per task.
- Restrict model selection while on Reserve to its supported reasoning levels, keep queued turns on the accepted model, and restore the saved model after a fresh read confirms recovery.
- Surface Reserve usage in the composer and `/status`, and expose `normalModelSlug` in app-server rate-limit snapshots so clients can use the associated model's display metadata without changing the request model.
- Poll account limits more frequently near exhaustion and retain compatibility with app servers that only accept the legacy null request parameters.
## Testing
- Added coverage for Reserve entry, queued-turn handling, task reconstruction, recovery authorization, account changes, model selection, usage rendering, and banner dismissal.
GitOrigin-RevId: 46a435e9531051f8cec3f6a46c4b9f7cfa479747
## Why
Context compaction was only reported after it completed, so the TUI did not
show when compaction was actively making room to continue.
## What changed
- Show `Compacting context` as soon as manual or server-driven compaction
starts, with an elapsed timer independent of the current turn timer.
- Preserve the compaction status across follow-up input and transient activity,
while still allowing retry errors to take precedence.
- Restore in-progress compaction timing from thread snapshots and add the live
duration to the completion message.
- Clear stale compaction state when the turn starts, completes, or terminates.
## Testing
Added TUI tests covering manual and server-driven compaction, narrow rendering,
follow-up input, replay and task switching, retry status, timer preservation,
and turn termination.
GitOrigin-RevId: 804e1bc553315da24ff1f99462e8e282818cada3
## Why
Streaming can temporarily hide the status row, but its elapsed time is still
needed when the row returns and when the final `Worked for` duration falls back
to the local clock. Approval prompts must also keep that clock paused.
## What changed
- Own the pause-aware status timer in the bottom pane independently of the
optional status row.
- Preserve elapsed time and modal pause state while hiding or restoring the row,
and reset the clock for new turns and Windows sandbox setup.
- Use the independent timer as the fallback for the final turn duration.
## Testing
Add timer unit coverage and TUI tests for hidden rows, outstanding approvals,
fallback turn durations, and fresh Windows sandbox setup clocks.
GitOrigin-RevId: a77931e465d5aedd4a8cd606a3a3117d1bb37ce7
## What changed
- Emit a separate history cell for each completed command instead of grouping
consecutive successes into a `Ran N commands` summary.
- Preserve `Explored` grouping for related file reads, searches, and listings.
- Replay completed commands as individual entries so their output and failure
status remain visible.
## Testing
- Update TUI tests and snapshots for live exploration and replayed commands.
GitOrigin-RevId: 7dafe0b7846fc89784093791a6be137a1b1ab8ff
## What changed
- Carry optional backend-owned banner and account identity data through
`account/rateLimits/read`, filtering banners that do not match the authenticated
account and user.
- Render supported rate-limit notices above the composer with actions for usage,
credits, resets, owner notifications, and plan management while preserving the
existing fallback UI for absent or unsupported banners.
- Refresh usage after limit errors, reject stale responses, and keep queued input
paused until recovery completes. When instructed by the banner, switch to the
first available fallback model without changing unrelated thread settings.
## Testing
- Add protocol, banner parsing and rendering, CTA, identity filtering, refresh
ordering, fallback-model, account-change, and queued-input recovery coverage.
GitOrigin-RevId: e03d75a82e425deb87f4b120a71cf2286c0ef6cc
## What changed
- Send `Free`, `Go`, `Plus`, `Pro`, and `ProLite` ChatGPT users to the individual Trusted Access page when a cybersecurity policy error ends a turn.
- Keep the enterprise Trusted Access application for other plans and sessions without a ChatGPT account or known plan.
## Testing
- Cover individual and enterprise link selection in chat widget tests and history-cell snapshots.
GitOrigin-RevId: a563560d15c401b47f1865a6f58ea4692d31d457
## What changed
- Keep the normal composer footer visible when a draft contains the word `plan`.
- Remove the contextual “Create a plan?” prompt, its Escape dismissal behavior, and its per-thread dismissal state.
## Testing
- Update wide and narrow composer snapshots to cover the standard footer for plan-related drafts.
GitOrigin-RevId: 1a3c8cfce9d4effa2149790975133f652aa7c914
## Why
When the app server reports a misalignment policy violation, the TUI must stop the affected chat rather than allow more work to continue in it.
## What changed
- Finalize the active turn, clear queued and draft input, disable the composer, and reject further operations and interactive requests for the stopped chat.
- Interrupt an active side conversation when its parent reports the violation, and prevent queued turns, forks, approvals, or goal resumption from restarting work.
- Show a non-dismissible precaution view that directs users to start a new chat or resume another one, with agent command center navigation for remote sessions.
## Testing
Added coverage for stopped-chat behavior, parent/side-thread handling, blocked goal resumption, navigation actions, and narrow and remote-session rendering.
GitOrigin-RevId: 0268c46a19294a17765ea4099f389f15ffaaf030
## What changed
- Group consecutive successful agent and unified-exec startup commands into a compact `Ran N commands` entry while preserving their full transcript.
- Flush command groups at interaction boundaries, after failures, and after 32 completed commands. Keep manual shell commands and failed or declined commands visible.
- Apply the same grouping rules when replaying thread history without duplicating command starts or disturbing unrelated active commands.
## Testing
- Add TUI coverage for live and replayed grouping, transcript preservation, failures, overlapping commands, group limits, and interaction boundaries.
GitOrigin-RevId: d6602a20201644012fcff3d6cecf4690024383a9
## What changed
When `Ctrl-C` is pressed with an empty composer while a task is running in a local daemon session, show a menu that lets the user:
- cancel the task and stay in Codex;
- exit Codex while leaving the task running; or
- stop the task and exit Codex.
Do not offer background execution for side-thread tasks or when follow-up messages are queued. Preserve existing `Ctrl-C` behavior for non-daemon sessions, drafts, paste bursts, MCP startup, and returning from an active side thread.
## Testing
Add TUI tests for each exit action, menu dismissal, side threads, queued follow-ups, composer input, MCP startup, and non-daemon sessions.
GitOrigin-RevId: 539c33d3a4ff288a015bddbe9465d2bbdbe018d0
## What changed
- Add `thread-credits` and `estimated-thread-cost` items to the configurable
status line and terminal title for Enterprise workspaces.
- Fetch one shared thread usage estimate only when either item is selected,
omit unavailable values, and refresh the display after turns complete.
- Preserve the last nonzero estimate while usage settles, with bounded delayed
refreshes and retries for transient failures.
## Testing
- Add coverage for configuration, rendering, previews, plan availability,
replay handling, retries, and post-turn settlement.
GitOrigin-RevId: dd69ee621c5bca0e5d9edd32b2bba42487eb1500
## Why
Pending MCP server startup can outlive an agent turn. Treating its shared
running indicator as an active user turn prevents queued follow-ups and slash
commands from advancing after the turn completes.
## What changed
- Allow the input queue to drain while only MCP startup remains active.
- Keep queued input blocked while a review, foreground shell command, or
compaction is pending or running.
- Resume queue draining after session-selection flows close.
## Testing
Add TUI tests covering queued follow-ups, slash commands, reviews, shell
commands, and compaction while MCP startup is pending.
GitOrigin-RevId: f5684f61c32e3728ff71cf222f0486df637dc3a5
## Why
Streamed message deltas can be dropped when the transport is saturated, leaving
clients with a truncated final response.
## What changed
- Include the last non-empty final agent message in successful `turn/completed`
notifications and mark the items as a summary.
- Treat completed message content as authoritative in the TUI, repairing an
incomplete stream while avoiding duplicate messages when `item/completed` was
already received.
- Continue backfilling full turn items for persisted `codex exec` threads when a
completion contains only the summary view.
## Testing
Added coverage for completion summaries, dropped stream deltas, duplicate item
handling, and directive-only responses.
GitOrigin-RevId: ffe9d36d683d6b6690e4a468ff68aa9189fe6ab7
## What changed
- Handle app-server `turn/start` rejections for pending user turns as turn
errors instead of exiting the TUI.
- Finalize the pending turn, display the failure in the transcript, and resume
queued input handling.
- Add coverage that submits a turn to a missing thread and verifies that the
error is shown while the app continues running.
GitOrigin-RevId: 6d291ba62b9e50d49dd4e3d7c429e98c1f754680
## Why
Pending Guardian review state could outlive an interrupted or completed turn,
leaving stale review details visible during a following goal turn.
## What changed
- Clear pending Guardian reviews during both turn completion and finalization.
- Restore an active MCP startup header after cleanup, falling back to `Working`
when no MCP server is starting.
## Testing
Added coverage for interrupted and completed turns, including goal continuation
and overlapping MCP startup status.
GitOrigin-RevId: a15ed81a5d6005e9294089f4424a8dedbc6e9cb5
## What changed
- Redraw assistant and plan stream tails only when a completed line changes the visible tail.
- Cache the first reasoning header and update the status indicator only when its visible state changes, while restoring the cached header if the indicator is recreated.
- Keep regular commit ticks able to clear stale stream tails.
## Testing
- Add coverage for unchanged partial deltas, changed newline-delimited tails, reasoning-header redraws, recreated status indicators, stale plan tails, and history-line draw scheduling.
GitOrigin-RevId: 9016175f97ef2db2edc2fd5ba13a4d731dc2d98c
## What changed
- Leave an output-free interrupted prompt in the transcript and open a blank
composer for the user's next instruction.
- Use the normal interruption notice for both `Esc` and `Ctrl-C` interrupts.
- Track safety-buffering prompts separately so failed retries can still restore
the prompt, including after switching between threads.
## Testing
- Cover output-free `Esc` and `Ctrl-C` interrupts, failed safety-buffer retries,
and restoration of safety-buffer state from thread snapshots.
GitOrigin-RevId: 30b368d00dfdb981d06d4c95ce3f252389b7917c
## Why
Reasoning summaries can contain an empty HTML comment placeholder, `<!--
-->`, when a generated summary part has no prose. The TUI treated that
placeholder as visible summary content, so it leaked into both the
completed conversation history and persisted transcript. Multi-part
empty summaries could also leave later generated status headings visible
as orphaned transcript content.
## What
- Strip the empty reasoning-summary placeholder at the shared
`ReasoningSummaryCell` boundary.
- Suppress the cell when removing placeholders leaves only generated
bold status headings.
- Cover the multi-part placeholder-only shape with a rendering
regression test for both normal history and transcript output.
## How to Test
1. Start Codex TUI with reasoning summaries visible, for example with
`hide_agent_reasoning = false` and `model_reasoning_summary =
"detailed"`.
2. Run a turn that receives an empty generated reasoning-summary part.
3. Confirm that the in-progress status heading may appear while the turn
is running, but the completed history and transcript contain neither
literal `<!-- -->` tokens nor orphaned reasoning headings.
4. Confirm that a reasoning summary with actual prose still renders
normally.
The model output is nondeterministic, so the targeted regression covers
the exact multi-part placeholder shape from the report:
- `just test -p codex-tui
reasoning_summary_block_hides_empty_html_comment_parts`
## Summary
- Treat streamed Responses `bio_policy` failures as terminal invalid
requests instead of retryable stream errors.
- Recognize the new biology policy code and message in the TUI while
preserving the legacy `invalid_prompt` contract.
- Keep the existing dedicated biology safety notice and add
regression/snapshot coverage for all supported error shapes.
## Why
[openai/openai#1068559](https://github.com/openai/openai/pull/1068559)
gates a Responses API contract change from `invalid_prompt` and the
legacy message to `bio_policy` and new biology copy.
Without this compatibility change, streamed blocks are retried as
transient failures and the OSS TUI falls back to a generic/raw error
instead of the dedicated safety notice.
## Validation
- `just test -p codex-api` — 137 passed
- `just test -p codex-tui
app_server_safety_access_errors_render_dedicated_notice` — passed
- `just fix -p codex-api`
- `just fix -p codex-tui`
- `just fmt`
- `just test -p codex-tui` — 2,957 passed; two reproducible failures
remain in untouched Guardian feature-flag persistence tests:
-
`update_feature_flags_disabling_guardian_clears_review_policy_and_restores_default`
-
`update_feature_flags_disabling_guardian_clears_manual_review_policy_without_history`
## Why
The safety-buffering prompt is a modal TUI view, but the normal
successful-turn path only hid the running status indicator. If the turn
completed while the prompt was open, the stale modal remained over the
composer until the user dismissed it or another turn started.
This aligns the TUI with the app behavior: keep the safety notice
visible while the turn is active, then remove it when the turn becomes
terminal. It also prevents the stale retry action from changing the
model and reasoning effort for a future turn after the buffered turn has
already completed.
| New copy |
|---|
| <img width="1014" height="313" alt="CleanShot 2026-06-28 at 20 27 18"
src="https://github.com/user-attachments/assets/f0f37359-5d77-442f-add2-9d1874bdc422"
/> |
## What changed
- Clear the active safety-buffering view and retry state when a turn
completes successfully.
- Update the retry-capable message to say “Hang tight or retry with a
faster model”.
- Extend the safety-buffering regression coverage to verify that the
prompt remains visible after assistant output starts and disappears when
the turn completes.
- Update the TUI snapshot for the revised copy.
This is a follow-up to #29919.
## How to Test
1. Start a TUI turn that receives `model/safetyBuffering/updated` with
`showBufferingUi: true` and a `fasterModel`.
2. Confirm the prompt says “Hang tight or retry with a faster model”.
3. Let the turn continue and confirm the prompt remains visible while
the turn is active.
4. Let the turn finish successfully and confirm the prompt disappears
and the composer is restored without requiring an extra keypress.
5. Confirm a buffering update without a faster model still shows the
shorter non-retry message.
Targeted automated coverage:
- `just test -p codex-tui safety_buffering` — 4 passed.
- `just test -p codex-tui` — 2,951 passed; two unrelated Guardian
feature-flag tests failed identically on `main` in this environment.
The argument-comment lint was also audited manually. The workspace Bazel
invocation was blocked by a missing external LLVM `compiler-rt` BUILD
file, and the packaged per-crate fallback uses a nightly older than the
current `sqlx` minimum Rust version.
## Why
[#25345](https://github.com/openai/codex/pull/25345) was approved,
green, and squash-merged into its stacked base branch,
`fcoury/tokenmaxxing-api`. Four minutes later, that base branch was
force-pushed back to an API-only rebased head while preparing
[#25344](https://github.com/openai/codex/pull/25344) for `main`. As a
result, the squash commit from #25345 was orphaned and the TUI command
never reached `main` or a release.
This PR relands the orphaned TUI change from
[`411410b8`](https://github.com/openai/codex/commit/411410b85c2d8eb050d441f17396c5c4048d866f)
on current `main`.
## What changed
- Add `/usage`, `/usage daily`, `/usage weekly`, and `/usage cumulative`
for account token activity.
- Fetch account usage asynchronously through the existing
`account/usage/read` app-server RPC.
- Render daily, weekly, and cumulative activity with theme-aware
terminal palettes and bounded transient cards.
- Preserve transcript ordering while assistant streams, history
consolidations, active cells, and hooks complete.
- Hide `/usage` from completion when backend auth is unavailable while
keeping typed-command guidance.
- Carry current-main behavior forward for cwd-aware Markdown parsing,
Windows Terminal color detection, and personal access token auth.
- Clear pending usage cards on thread rollback and delay completed cards
until live hook output is committed.
- Add focused regression and snapshot coverage for loading, auth errors,
invalid views, rollback, hook ordering, layout, and charts.
## Prior review
The original implementation was approved by Eric Traut in #25345 after
testing multiple themes and light/dark terminals. This PR preserves that
reviewed implementation while adapting it to current `main` and adding
regression coverage for newer rollback and hook lifecycle behavior.
## Validation
- `just test -p codex-tui token_activity palette renderable
usage_command` — 37 passed.
- Focused rollback, hook-ordering, and error snapshot tests — 4 passed.
- `just fix -p codex-tui` — passed.
- `UV_CACHE_DIR=/private/tmp/codex-uv-cache just fmt` — passed.
- `cargo insta pending-snapshots` — no pending snapshots.
- `just test -p codex-tui` — 2,870 passed; two unrelated guardian
feature-flag tests failed because their expected `OverrideTurnContext`
event was absent:
-
`update_feature_flags_disabling_guardian_clears_manual_review_policy_without_history`
-
`update_feature_flags_disabling_guardian_clears_review_policy_and_restores_default`
- `just argument-comment-lint` could not complete because the local
Bazel LLVM `compiler-rt` repository is missing `include/sanitizer/*.h`.
The touched Rust diff was manually inspected and no missing
opaque-literal argument comments were found.
Fixes#27210.
## Why
When the app server reports a visible `HookStarted` event for a
`PostToolUse` hook but the turn reaches `TurnCompleted` before a
matching hook completion event arrives, the TUI can leave the transient
`Running PostToolUse hook` row visible after the agent is done.
Interrupted and failed turn cleanup already drops transient live hook
rows; the normal completion path did not.
## What Changed
- Added `ChatWidget::clear_active_hook_cell()` for dropping transient
live hook status without writing it to history.
- Call that cleanup from normal task completion, while reusing it for
the existing start/finalize cleanup paths.
- Added `completed_turn_clears_visible_running_hook` snapshot coverage
for the reported `PostToolUse` case.
## Tests
- `just test -p codex-tui completed_turn_clears_visible_running_hook`
- `just test -p codex-tui` (fails on current `main` in unrelated
guardian tests:
`update_feature_flags_disabling_guardian_clears_review_policy_and_restores_default`
and
`update_feature_flags_disabling_guardian_clears_manual_review_policy_without_history`)
## Summary
Resumed Codex App or VS Code review sessions can contain
`::code-comment` directives that the TUI previously displayed verbatim
because only rich clients interpret them.
This change rewrites valid line-start directives into readable Markdown
during assistant-message parsing, using the session working directory
for relative file paths. The fallback is applied consistently to live
messages, replayed transcripts, and resume previews while preserving
malformed directives and existing `::git-*` parsing.
## Before
The TUI exposed the raw client directive:
```text
::code-comment{title="Fix body= parsing" body="Keep role=\"tab\", ::git-stage{cwd=/tmp}, file=, and \n literal." file="/repo/src/app.ts" start=10 end=12 priority="P2"}
```
## After
The same directive is rendered as readable review feedback:
```text
- [P2] Fix body= parsing — src/app.ts:10-12
Keep role="tab", ::git-stage{cwd=/tmp}, file=, and \n literal.
```
Fixes#25658
## TL;DR
When you press Esc or Ctrl+C after sending a prompt but before any
output was rendering, it restores the last composer and the message.
## Summary
Cancelling a prompt immediately after submission should behave like
returning to edit that prompt, not like discarding the user's draft.
Today, pressing `Esc` or `Ctrl+C` before Codex responds leaves the
submitted prompt in the transcript and returns an empty composer,
forcing the user to recall or retype it.
When an interrupted turn has not produced substantive visible output,
restore its submitted prompt directly into the composer and roll back
that latest turn. This also covers the first prompt in a fresh thread,
before the TUI has retained a local user-history cell. The restored
draft keeps its text, image attachments, and active collaboration mode
so it can be edited and resubmitted in place.
Restoration is intentionally suppressed once the turn has produced
user-visible activity such as assistant output, tool work, hooks, or
patches. A transient thinking status does not make the prompt
ineligible. Rollback also rebuilds terminal scrollback from the retained
transcript cells so repeated cancellations and terminal resizes do not
duplicate history.
## How to Test
1. Start the TUI with `cargo run -p codex-cli --bin codex`.
2. In a fresh thread, submit the first prompt and press `Esc` before
Codex emits substantive output. Confirm that the prompt returns to the
composer for editing and its submitted transcript row is removed.
3. Repeat with `Ctrl+C`, then repeat after at least one completed turn.
Confirm the same behavior.
4. Submit a prompt, wait for assistant output or tool activity, then
cancel. Confirm that the transcript remains intact and the prompt is not
restored into the composer.
5. Cancel several output-free prompts and resize the terminal between
attempts. Confirm that the startup banner, tip, and transcript history
do not duplicate in scrollback.
Targeted tests:
- `just test -p codex-tui cancelled_turn_edit_restores_prompt`
- `just test -p codex-tui
output_free_interrupted_turn_requests_prompt_restore`
- `just test -p codex-tui
visible_output_prevents_cancelled_turn_prompt_restore`
- `just test -p codex-tui
thinking_status_keeps_cancelled_turn_prompt_restore_eligible`
- `just test -p codex-tui
patch_activity_prevents_cancelled_turn_prompt_restore`
The full `just test -p codex-tui` run completed with `2746` passing
tests and two unrelated existing guardian feature-flag failures. `just
argument-comment-lint` remains blocked locally by the existing Bazel
LLVM `compiler-rt` sanitizer-header glob failure; the touched Rust diff
was manually audited for positional literal comments.
## Summary
Fixes#22726.
After a Responses stream disconnect, the live TUI could keep accepting
prompts while leaving partially streamed assistant output in its
transient streaming-cell form. That made fenced diffs or SVG/XML-like
content appear as raw transcript text until the user closed the TUI and
resumed the same session, which rebuilt the transcript from saved
history.
This change finalizes the active answer stream before generic
failed-turn cleanup clears the stream controller, so the live transcript
takes the same source-backed markdown consolidation path as a successful
turn.
## Reviewer repro
1. Start a local Codex TUI session.
2. Trigger an assistant turn that streams markdown content, especially a
fenced diff or SVG/XML-like block.
3. Force or encounter a non-retry stream disconnect before the turn
completes.
4. Continue using the same still-open TUI session.
5. Before this fix, the live history can stay raw/plain even though
`codex resume` renders the same session normally.
6. After this fix, the failed-turn path consolidates the partial stream
before rendering the error, so the live TUI keeps normal transcript
rendering.
This change fixes the case where the UI can sit on _"Starting MCP
servers"_ even though the review work is already running or has already
completed.
- MCP startup status header is visible when a `/review` turn starts with
enabled MCP server startups
- Restore the underlying _Working..._ status after MCP startup completes
or fails
- Add regression coverage for overlapping startup/turn flows and status
restoration
_De-scoped from a broader thread-scoped MCP status change that would
have made it easier to route MCP startup statuses to the appropriate
thread (parent vs. review). These changes address the UI regression
without requiring more significant changes across app-server & core._
Fixes#18792.
## Why
`chatwidget.rs` is still carrying too many unrelated responsibilities in
one file. #22269 started a five-phase cleanup to move coherent behavior
domains into focused modules while keeping `chatwidget.rs` as the
composition layer. #22407 completed phase 2 by extracting input and
submission flow.
This PR is phase 3. It keeps moving high-churn event handling out of the
central widget by extracting protocol, replay, streaming, and tool
lifecycle handling without changing the visible behavior those flows
already provide. This is once again just a mechanical movement of
existing functions. No functional changes.
## What Changed
- Added focused modules for protocol request dispatch, replay rendering,
assistant/plan/reasoning streaming, turn runtime bookkeeping, hook
lifecycle handling, command lifecycle handling, tool lifecycle
rendering, and interactive tool request prompts.
- Kept active-cell grouping, transcript invalidation, interrupt
deferral, and final-message separator behavior in the same flows, just
moved into smaller files.
- Added module header comments to the new files so the ownership
boundaries are explicit.
- Left `codex-rs/tui/src/chatwidget.rs` as the registration and
orchestration surface for these extracted behaviors.
## Cleanup Phases
The five-phase cleanup plan from #22269 is:
1. Phase 1: mechanical helper and state moves. Completed in #22269.
2. Phase 2: extract input and submission flow, including queued user
messages, shell prompt submission, pending steer restoration, and thread
input snapshot/restore behavior. Completed in #22407.
3. Phase 3: extract protocol, replay, streaming, and tool lifecycle
handling, while preserving active-cell grouping, transcript
invalidation, interrupt deferral, and final-message separator behavior.
This PR.
4. Phase 4: extract settings, popups, and status surfaces, including
model/reasoning/collaboration/personality popups, permission prompts,
rate-limit UI, and connectors helpers.
5. Phase 5: clean up the remaining constructor and orchestration code
once the larger behavior domains have moved out, leaving `chatwidget.rs`
as the composition layer.