## 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`
## Why
The reset flow introduced in #28154 still describes earned reset credits
as "rate-limit resets" and uses generic reset-scope copy. It can also
retain a stale available-credit count after redemption or an account
change, leaving the reset action enabled after the last credit is used.
This follow-up updates terminology only within that reset feature.
Existing rate-limit wording elsewhere in the CLI and TUI is unchanged.
## What changed
- Rename reset-specific `/usage` menu items, startup hints, and reset
dialogs to "usage limit reset."
- Describe monthly resets for Free, Go, and accounts that report a
monthly usage window; otherwise describe the current 5-hour and weekly
limits.
- Recheck a cached zero balance when `/usage` is reopened, and refresh
the balance after redemption so the final reset immediately disables the
action.
- Correlate async refresh results before updating snapshots and clear
account-derived reset state, warnings, prompts, and status surfaces when
the account changes.
## Validation
- `just test -p codex-tui chatwidget::tests::usage` — 29 passed.
- `just test -p codex-tui chatwidget::tests::status_command_tests` — 7
passed.
- Account-boundary prompt and plan-mode prompt regression tests passed.
- `cargo insta pending-snapshots` from `codex-rs/tui` — no pending
snapshots.\
<img width="814" height="318" alt="image"
src="https://github.com/user-attachments/assets/2a460e96-458b-4805-8d9f-c759382d21a4"
/>
view for monthly
<img width="905" height="243" alt="image"
src="https://github.com/user-attachments/assets/179f88e3-08fb-4af5-8dc6-ce6a944ed681"
/>
This adds the background plumbing for remote-backed plugin catalog
sections while leaving the fuller directory presentation to the next PR.
The TUI can fetch section-specific remote marketplace results, keep
local plugin data available, and carry section errors forward for later
rendering.
- Fetches explicit remote marketplace kinds for curated, workspace, and
shared-with-me sections.
- Gates shared-with-me loading on the plugin sharing feature flag.
- Adds section-level error state and user-actionable error copy.
- Merges remote marketplace results into the cached plugin list without
discarding local results.
## 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`](411410b85c)
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.
## Why
Removes the realtime audio support from TUI.
## What Changed
- Removed the TUI `/realtime` and realtime `/settings` command paths.
- Deleted TUI voice capture/playback, WebRTC session handling,
audio-device selection UI, and recording-meter code.
- Removed TUI realtime tests and snapshots that covered the deleted
surfaces.
- Dropped the TUI-only `cpal` and `codex-realtime-webrtc` dependencies
and refreshed the Rust/Bazel locks.
## Why
This is part of an ongoing attempt to eliminate the TUI's direct
dependency on core features. When we moved the TUI to the app server, we
left a `legacy_core` shim that re-exported some remaining core symbols
for the TUI. The intent was to eventually remove all of these.
In this PR, we remove the symbols related to the Windows sandbox.
The change should be behavior-neutral and low risk because it's just
refactoring and removal of code that is now effectively dead.
When working on this PR, I noticed a big existing problem that affects
mixed-platform remoting. For example, if you run the TUI on a Linux box
and remote into a Windows box, the TUI logic doesn't properly handle
Windows sandbox setup properly. Fixing this is beyond the scope of this
PR, but I've left a TODO comment in place so we don't forget.
## What changed
- Move the remaining TUI-specific sandbox level, setup, telemetry, and
read-root helpers into `codex-tui`, calling `codex-windows-sandbox`
directly.
- Remove the Windows sandbox namespace and read-root grant re-exports
from the client-side `legacy_core` facade.
- Remove the dormant pre-elevation prompt fallback guarded by the
permanently enabled `ELEVATED_SANDBOX_NUX_ENABLED` switch. The reachable
elevated and non-elevated setup flows remain unchanged.
## 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#24411.
`/status` currently has no way to show when the TUI is talking to Codex
through a remote transport. That makes embedded local sessions, local
daemon sessions, and true remote sessions look the same, and it hides
the remote server version when debugging connection-specific behavior.
This PR adds a single `Remote` row for non-embedded connections only.
The row shows the sanitized connection address and a dimmed version
parenthetical, preserving the existing status output for embedded local
sessions.
<img width="791" height="144" alt="image"
src="https://github.com/user-attachments/assets/529d7940-1c45-4586-8b06-f20a1f04b771"
/>
## Verification
- Manually validated when connecting remotely (either implicitly to
local daemon or explicitly)
## Why
Model catalog responses can now advertise a nullable
`default_service_tier` for each model. Codex needs to preserve three
distinct states all the way from config/app-server inputs to inference:
- no explicit service tier, so the client may apply the current model
catalog default when FastMode is enabled
- explicit `default`, meaning the user intentionally wants standard
routing
- explicit catalog tier ids such as `priority`, `flex`, or future tiers
Keeping those states distinct prevents the UI from showing one tier
while core sends another, especially after model switches or app-server
`thread/start` / `turn/start` updates.
## What Changed
- Plumbed `default_service_tier` through model catalog protocol types,
app-server model responses, generated schemas, model cache fixtures, and
provider/model-manager conversions.
- Added the request-only `default` service tier sentinel and normalized
legacy config spelling so `fast` in `config.toml` still materializes as
the runtime/request id `priority`.
- Moved catalog default resolution to the TUI/client side, including
recomputing the effective service tier when model/FastMode-dependent
surfaces change.
- Updated app-server thread lifecycle config construction so
`serviceTier: null` preserves explicit standard-routing intent by
mapping to `default` instead of internal `None`.
- Kept core responsible for validating explicit tiers against the
current model and stripping `default` before `/v1/responses`, without
applying catalog defaults itself.
## Validation
- `CARGO_INCREMENTAL=0 cargo build -p codex-cli`
- `CARGO_INCREMENTAL=0 cargo test -p codex-app-server model_list`
- `cargo test -p codex-tui service_tier`
- `cargo test -p codex-protocol service_tier_for_request`
- `cargo test -p codex-core get_service_tier`
- `RUST_MIN_STACK=8388608 CARGO_INCREMENTAL=0 cargo test -p codex-core
service_tier`
## Why
App and skill toggles are user config mutations too. When the TUI is
attached to a remote app server, writing those toggles into the local
`config.toml` makes the UI report success without updating the server
that actually owns the session.
This is **[2 of 4]** in a stacked series that moves TUI-owned config
mutations onto app-server APIs.
## What changed
- Routed app enable/disable persistence through app-server config batch
writes.
- Routed skill enable/disable persistence through `skills/config/write`.
- Avoided refreshing local config from disk after these writes when the
TUI is connected to a remote app server.
## Config keys affected
- `apps.<app_id>.enabled`
- `apps.<app_id>.disabled_reason`
- `[[skills.config]]` entries keyed by `path`, with `enabled = false`
used for persisted disables
## Suggested manual validation
- Connect the TUI to a remote app server, disable an app, reconnect, and
confirm the app remains disabled from remote config rather than local
disk state.
- Re-enable the same app and confirm both `apps.<app_id>.enabled` and
`apps.<app_id>.disabled_reason` are cleared remotely.
- Disable a skill in the manage-skills UI and confirm a remote
`[[skills.config]]` disable entry appears.
- Re-enable that skill and confirm the disable entry is removed and the
effective enabled state updates without relying on local config reloads.
## Stack
1. [#22913](https://github.com/openai/codex/pull/22913) `[1 of 4]`
primary settings writes
2. [#22914](https://github.com/openai/codex/pull/22914) `[2 of 4]` app
and skill enablement
3. [#22915](https://github.com/openai/codex/pull/22915) `[3 of 4]`
feature and memory toggles
4. [#22916](https://github.com/openai/codex/pull/22916) `[4 of 4]`
startup and onboarding bookkeeping
## 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, #22433 completed phase 3 by extracting protocol,
replay, streaming, and tool lifecycle handling, and #22518 completed
phase 4 by extracting settings, popups, and status surfaces.
This PR is phase 5. It cleans up the remaining constructor and
orchestration code now that the larger behavior domains have moved out,
leaving `chatwidget.rs` much closer to the composition layer the cleanup
was aiming for. This is once again a mechanical movement of existing
functions. No functional changes.
## What Changed
- Added focused modules for widget construction and initial wiring,
session configuration flow, key/composer interaction routing, review
popup orchestration, desktop notification coalescing, and render
composition.
- Moved the remaining constructor, session setup, interaction,
notification, review picker, and rendering helpers out of
`codex-rs/tui/src/chatwidget.rs`.
- Preserved the existing startup/session behavior, keyboard handling,
review picker flow, notification priority behavior, and render
composition while shrinking the central widget module substantially.
- Left `codex-rs/tui/src/chatwidget.rs` as the registration and
composition surface for the extracted behavior modules.
## 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.
Completed in #22433.
4. Phase 4: extract settings, popups, and status surfaces, including
model/reasoning/collaboration/personality popups, permission prompts,
rate-limit UI, and connectors helpers. Completed in #22518.
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. This PR.
## Verification
- `cargo check -p codex-tui`
- `cargo test -p codex-tui chatwidget::tests::popups_and_settings`
- `cargo test -p codex-tui chatwidget::tests::plan_mode`
- `cargo test -p codex-tui chatwidget::tests::review_mode`
- `cargo test -p codex-tui chatwidget::tests::status_and_layout`
`cargo test -p codex-tui` also compiles and begins running, but aborts
in the unchanged app-side test
`app::tests::discard_side_thread_keeps_local_state_when_server_close_fails`
with the same reproducible stack overflow noted in phase 4.