This mechanically extracts the existing TUI plugin catalog and detail
popup rendering from `chatwidget/plugins.rs` into a new
`chatwidget/plugin_catalog.rs` module. `plugins.rs` now keeps the
stateful plugin workflow and orchestration, while `plugin_catalog.rs`
owns the presentation-heavy catalog/detail popup construction and its
pure helpers. The goal is to keep `plugins.rs` focused before later
plugin sharing work adds more catalog behavior.
- Moves existing catalog/detail popup builders and related pure helpers
into `plugin_catalog.rs`
- Leaves plugin fetch/state/key handling in `plugins.rs`
- Adds only minimal sibling-module visibility/import wiring
- Intentionally makes no product behavior or UI changes beyond the code
move
## Summary
Introduces the default-off `respect_system_proxy` feature flag used to
gate first-class system PAC/proxy support for Codex-owned native
clients.
With the feature disabled or absent, behavior remains unchanged. This PR
establishes the configuration and managed-requirement surface; proxy
discovery and request routing are implemented by follow-up PRs.
## Configuration
User configuration uses the standard boolean feature form:
```toml
[features]
respect_system_proxy = true
```
Managed feature requirements use the corresponding boolean key. The
effective runtime configuration is exposed as a boolean and defaults to
`false`.
## Implementation
- Registers `respect_system_proxy` as an under-development, default-off
feature.
- Resolves user configuration and managed feature requirements into
`Config.respect_system_proxy`.
- Provides bootstrap resolution for startup paths that must evaluate the
feature before full configuration loading completes.
- Uses the standard feature CLI and config-editing behavior.
- Excludes `features.respect_system_proxy` from project-local
configuration.
- Updates the generated configuration schema.
## End-user behavior
- No networking behavior changes when the feature is absent or disabled.
- Enabling the feature makes the boolean available to the native
proxy-routing implementation in follow-up PRs.
- Repository-local configuration cannot enable the feature.
## Test coverage
Covers scalar configuration and CLI override resolution, managed
requirement constraints, bootstrap resolution, and project-local
filtering.
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.
## Summary
This PR promotes Mentions 2.0 (unified TUI mention popup) to stable and
enables it by default.
- Keep `mentions_v2` as a temporary rollback path to the legacy split
popups (`--disable mentions_v2`).
- Add feature-default and snapshot coverage for the default experience.
## Prior work
- [#19068 — Unified mentions in
TUI](https://github.com/openai/codex/pull/19068)
- [#22375 — Use plugin/list to get plugins for
mentions](https://github.com/openai/codex/pull/22375)
- [#23363 — Unified mentions tweaks and rendering
polish](https://github.com/openai/codex/pull/23363)
## Test plan
- Launch Codex without any feature overrides.
- Type `@` in the TUI composer.
- Confirm the unified mentions menu opens and displays filesystem,
plugin, and skill results.
## Summary
Startup warnings for under-development features only recognized bare
boolean toggles like `features.foo = true`. An upcoming feature will use
table-format config, so `features.foo = { enabled = true, ... }` needs
to count as an explicit opt-in too.
This updates the warning predicate to recognize structured tables with
`enabled = true`, while leaving tables without that field unwarned.
## Testing
- `just fmt`
- `just test -p codex-features
unstable_warning_event_mentions_enabled_structured_under_development_feature`
This changes the `/review` escape path so `Esc` no longer behaves like
the normal queued-follow-up interrupt flow while a review is running.
Steering is not currently supported in `/review` mode, without this
change users are able to attempt a steer but it leads to a crash (see
#22815). If the user has already tried to send additional guidance
during `/review`, the TUI now keeps the review running and shows a
warning that steer messages are not supported in that mode, while still
pointing users to `Ctrl+C` if they actually want to cancel. It also adds
regression coverage for the review-specific warning behavior. When users
do cancel with Ctrl+C during /review, the TUI now tolerates the
active-turn race that can happen during review handoff, and any queued
steer messages are restored to the composer instead of being discarded.
- Special-case `Esc` during an active `/review` when follow-up steer
input is pending or has already been deferred.
- Show a clear warning instead of interrupting the running review.
- Make the Ctrl+C cancel path during /review resilient to active-turn
races, while preserving any queued steer text by restoring it to the
composer.
- Add review-mode test coverage for the warning path.
## Testing
1. Start a `/review` with a diff large enough that the review stays
active for more than a few seconds.
2. While the review is still running, type a follow-up / steer message,
submit it, and then press `Esc`.
Before: `Esc` causes the TUI to close abruptly.
After: the review keeps running and the transcript shows a warning that
steer messages are not supported during `/review`, with guidance to use
`Ctrl+C` if you want to cancel.
3. Press `Ctrl+C` if you actually want to stop the review.
Before: (after restarting the test since Pt. 2 crashed) this is the
intentional cancellation path.
After: this remains the intentional cancellation path, and any queued
follow-up steer text is restored to the composer instead of being lost.
## Note:
`/review` mode explicitly does not support steering at this time (as
noted in `turn_processer.rs`, if we want to explore that in the future
this code will need to be modified). This change keeps unsupported steer
attempts from crashing the TUI and preserves queued follow-up text if
the user cancels with Ctrl+C.
Fixes a TUI regression where thread transitions such as `/new` and
`/clear` could rebuild config without the cloud requirements loader,
allowing users to fall back to non-cloud-managed settings. The config
refresh path now preserves cloud requirements during thread
reinitialization, and config loading is moved off the deep TUI event
stack to avoid stack-overflow crashes during those reloads.
- Passes the cloud requirements loader through TUI config rebuild paths.
- Keeps cloud requirements applied for `/new`, `/clear`, `/fork`, side
conversations, and session picker transitions.
- Runs config building on a Tokio task so reloads do not occur on the
deep TUI caller stack.
- Adds regression coverage that cloud requirements survive
thread-transition config refreshes.
## Test/Repro:
- Start Codex with a cloud requirement applied.
- Use `/new` or `/clear`.
- The refreshed/fresh-session config should still include the cloud
requirements
This can be tested with any config item, at this moment for oai staff
the easiest item to test is the `mentions_v2` feature. This is currently
enabled in cloud requirements, but is not enabled by default. As a
result, prior to these changes that feature is disabled after `/new` or
`/clear`. Testing the same steps with a binary from this branch should
not drop the feature enablement.
## Summary
- Render `/debug-config`'s `allowed_sandbox_modes` from the finalized
permission constraints instead of the raw requirements list.
- Add regression coverage for configured full-access and external
sandbox modes being omitted when effective permissions reject them.
## Details
`allowed_sandbox_modes` comes from managed requirements, but the final
permissions can be further constrained by derived validation rules. For
example, `permissions.filesystem.deny_read` requires sandbox
enforcement, so modes that disable or externalize Codex's sandbox are
not actually usable even if they were present in the raw requirements
TOML.
The debug renderer now enumerates the configured sandbox-mode labels and
keeps only those accepted by `Config.permissions`. That makes
`/debug-config` reflect the same effective permission-profile constraint
path used by runtime config validation, while preserving the existing
source/provenance display.
## Validation
- Added a regression test for effective sandbox-mode filtering in
`/debug-config`.
This change keeps unified @mentions behind the mentions_v2 gate, moves
the flag to under-development, and polishes mention rendering/history
behavior.
It also adds a few small improvements to the mentions feature around
mention rendering and history round-tripping for plugin/tool mentions in
message edit scenarios. Plugin selections now insert `@` mentions with
better casing, and saved history preserves the visible sigil so recalled
messages look the same as what the user typed.
- Preserves `@` sigils when encoding/decoding mention history for
tool/plugin paths.
- Improves plugin mention insertion so display names/casing are
reflected more cleanly in the composer.
- Update composer to render user-entered plugin mentions in the same
color as the mentions menu. ALso applies to recalled/edited messages.
- Left/right arrows no longer switch unified-mention search modes after
an @mention has already been accepted (Ex: arrowing left through a
composed message that contains @mentions).
- Keeps bound mentions stable around punctuation, so accepted `@`
mentions do not reopen the popup and punctuated `$` mentions still
persist to cross-session history.
**Steps to test**
- Ensure mentions_v2 is enabled through configuration or `--enable
mentions_v2`
- Type `@` in the TUI composer and verify filesystem/plugin/skill
results are displayed in the unified mentions menu.
- Select a plugin mention from the `@` popup and confirm the inserted
text is an `@...` mention with casing, then recall/edit the message and
confirm it still renders as `@...`.
- Mention a skill and verify that skills still insert as `$skill`
mentions rather than `@` mentions.
- Verify punctuated mentions such as `@plugin.` and `($skill)` keep
their bound mention behavior across editing and history recall.
## Context
`docs/tui-chat-composer.md` was removed by #20896 as part of removing
local-only docs/specs from the repository. I checked the #20896 file
list and the merge commit: the composer doc was deleted, not moved or
copied, and current `main` does not contain a replacement composer
narrative doc.
Current guidance should keep contributors and agents focused on the docs
that still exist: the module docs in `chat_composer.rs` and
`paste_burst.rs`.
## Summary
- Removes the scoped TUI bottom-pane AGENTS.md requirement to update
`docs/tui-chat-composer.md`.
- Removes stale module-doc references to that deleted narrative doc from
`chat_composer.rs` and `paste_burst.rs`.
## Validation
- Checked #20896 and the merge commit with rename/copy detection to
confirm `docs/tui-chat-composer.md` was deleted rather than moved.
- Searched current `main` for a replacement composer narrative doc.
- Not run; documentation-only change.
This adds slash command completion behavior for argument-taking
commands, where text after the partially typed command becomes inline
arguments instead of being discarded. This addresses the workflow of
drafting text first, moving to the start, and completing a slash command
around that existing draft. Before this change, this workflow would
remove all user-input text aside from the slash command, which can be
frustrating if the user had just typed out a long and well thought out
goal.
- Preserves the draft tail for inline-argument slash commands like
`/goal` and `/review` when completing with `Tab` or `Enter`.
- Keeps popup filtering focused on the command fragment under the cursor
rather than the full draft text.
- Leaves slash commands that do not support inline arguments unchanged,
so completion still replaces the existing draft tail for those commands.
- Adds focused TUI tests under slash input covering preserved arguments,
cursor edge cases, and the negative case for a command without inline
args.
Follow-up simplification and test relocation from #24683 folded into
this PR.
---------
Co-authored-by: Eric Traut <etraut@openai.com>
## Summary
Fix the TUI `$` app mention paths so App Directory rows that are not
accessible are not treated as usable apps.
This includes the core preservation fix from #24104, but expands it to
the other app mention paths:
- preserve app-server `is_accessible` flags when partial
`app/list/updated` snapshots reach the TUI
- require apps to be both accessible and enabled when resolving exact
`$slug` mentions
- require restored/stale `app://...` bindings to point at accessible,
enabled apps before emitting structured app mentions
- remove the now-unused `codex-chatgpt` dependency from `codex-tui`,
which addresses the `cargo shear` failure seen on #24104
## Root Cause
The app server already sends merged app snapshots with accessibility
computed. The TUI handled app-server app list updates as partial app
loads and re-ran the old accessible-app merge path. That path treated
every notification row as accessible, so App Directory entries with
`isAccessible=false` could appear in `$` suggestions.
Regression source: #22914 routed app-list updates through the app server
while reusing the old TUI partial-load handling. Related precursor:
#14717 introduced the partial-load path, but #22914 made it user-visible
for app-server updates.
## Issues
Fixes#24145Fixes#24205Fixes#24319
## Validation
- `just fmt`
- `git diff --check`
- `just bazel-lock-update`
- `just bazel-lock-check`
- `just argument-comment-lint -p codex-tui`
- `just test -p codex-tui
chatwidget::tests::popups_and_settings::apps_notification_update_excludes_inaccessible_apps_from_mentions
chatwidget::tests::composer_submission::submit_user_message_ignores_inaccessible_app_mentions_from_bindings
chatwidget::skills::tests::find_app_mentions_requires_accessible_enabled_apps_for_bound_paths
chatwidget::skills::tests::find_app_mentions_requires_accessible_enabled_apps_for_slugs`
Recent composer cleanups split state ownership out of `ChatComposer`,
but slash-command handling still mixed parsing, popup coordination,
completion, submission validation, queue behavior, and argument element
rebasing into the main composer file. Pending changes to slash command
parsing and selection inspired this code move to prevent
`chat_composer.rs` bloat.
This is just a refactor, no functional or behavioral changes are
intended.
## What changed
- Move slash-command parsing and lookup helpers into
`bottom_pane/chat_composer/slash_input.rs`.
- Move slash popup key handling, command-name completion, and popup
construction into the slash input helper module.
- Centralize bare-command, inline-args, submission-validation, and
queued-input action selection behind slash-specific helpers.
- Move command argument text-element rebasing into the slash input
module so inline command submission keeps the same element behavior with
less composer-local logic.
## Verification
- `just fmt`
- `just test -p codex-tui`
- `cargo insta pending-snapshots -p codex-tui`
This fixes a regression wher codex could start in the wrong directory
when a live local app-server socket was present. The issue was that
implicit local socket reuse was being treated like an explicit remote
workspace session, which dropped the invoking cwd unless --cd was
passed.
The change separates local socket transport from true remote workspace
semantics.
- Plain local startup keeps local cwd, trust, resume, picker, and
config-refresh behavior.
- Explicit --remote keeps the existing remote cwd behavior.
- Added coverage for launch target selection and local-session
filtering/cwd behavior.
Steps to test:
- Start a local app-server from a different directory than the repo you
want to use.
- Launch codex from a project/worktree without --cd.
- Confirm the session starts in the invoking directory, not the
app-server process directory.
- Confirm explicit codex --remote ... still preserves existing remote
behavior.
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.
Fixes#20587, reported by @noeljackson.
This prevents the TUI wrapping code from panicking when `textwrap`
returns a borrowed slice that does not point into the original source
text. The fix follows the direction proposed by @misrtjakub in the issue
comment: validate the borrowed slice pointer range first, and fall back
to the existing owned-line mapper when the slice is external.
- Guards borrowed wrapped slices before converting pointer offsets into
byte ranges.
- Reuses the existing owned-line range recovery path for external
borrowed slices.
- Adds coverage for rejecting borrowed slices outside the source text.
End-user testing steps:
- Start Codex in TUI mode under a PTY wrapper that can inject stdin
after startup.
- Inject `\x1b[200~test message\x1b[201~\r` after the TUI is ready.
- Confirm Codex does not panic and the pasted text is handled normally.
Local validation:
- `cargo test -p codex-tui wrapping::tests::`
- `cargo test -p codex-tui -- --skip
status::tests::status_permissions_full_disk_managed_with_network_is_danger_full_access
--skip
status::tests::status_permissions_full_disk_managed_without_network_is_external_sandbox`
This switches TUI plugin mentions to use app-server `plugin/list` for
plugin inventory and metadata instead of `PluginManager`, while keeping
the same mention-eligibility filters as before.
Same filters as before:
- Only plugins in the current config / cwd scope.
- Only installed and enabled plugins.
- Only plugins that actually expose a capability, meaning at least one
skill, MCP server, or app connector.
- Uses `plugin/list` for the mention names/descriptions
This PR replaces the TUI’s file-only `@mention` popup with a unified
mentions experience. Typing `@...` now searches across filesystem
matches, installed plugins, and skills in one popup, with result types
clearly labeled and selectable from the same flow.
- Adds a unified `@mentions` popup that returns:
- plugins
- skills
- files
- directories
- Adds search modes so users can narrow the popup without changing their
query:
- All Results _(default/same as Codex App)_
- Filesystem Only
- Plugins _(...and skills)_
- Preserves existing insertion behavior:
- selected file paths are inserted into the prompt
- paths with spaces are quoted
- image file selections still attach as images when possible
- selecting a plugin or skill inserts the corresponding `$name`
- the composer records the canonical mention binding, such as
`plugin://...` or the skill path
- Expanded `@mentions` rendering:
- type tags for Plugin, Skill, File, and Dir
- distinct plugin/filesystem colors
- stable fixed-height layout (8 rows)
- truncation behavior for narrow terminals
Note:
- The unified mentions popup does not display app connectors under
`@mention` results for Codex App parity. Connector mentions remain
available through the existing `$mention` path.
https://github.com/user-attachments/assets/f93781ed-57d3-4cb5-9972-675bc5f3ef3f
Fixes#21070.
This is a small cleanup around model metadata handling for
gateway/provider model names. It follows the report and proposed
direction from @dkbush by keeping the fallback metadata warning useful
without repeating it every turn, and by tightening the existing
provider-prefix lookup path.
- Track fallback metadata warning slugs in session state so each
unresolved model warns once per session.
- Keep warning emission outside the session-state lock and preserve the
existing warning text.
- Allow one-segment provider prefixes with hyphenated provider IDs,
while preserving the multi-segment rejection behavior.
- Add focused coverage for warning dedupe and hyphenated provider-prefix
metadata matching.
Testing:
- Ran `just fmt`.
- Ran `git diff --check`.
- Added tests for the new warning dedupe and provider-prefix lookup
behavior.
Fixes#20945.
This keeps `codex fork --last` aligned with the neighboring
latest-session lookup flows. The local fork path now uses the same
cwd-scope helper as `resume --last`, which is also a small code cleanup
around how this selection logic is shared.
Credit to @chanwooyang1 for the report and for pointing out the narrow
fix direction.
What changed:
- Route `fork --last` through the shared latest-session cwd filter.
- Preserve `--all` as the explicit opt-in for global latest-session
selection.
- Keep remote cwd override behavior unchanged.
- Add focused coverage for local default, `--all`, and remote override
filter semantics.
Validation:
- Ran `just fmt`.
- Ran `git diff --check`.
- Reviewed the `fork --last`, `resume --last`, and fork picker selection
paths against the issue report.
Fixes#19940.
Large-paste placeholder numbering was backed by a per-size counter, so
clearing a draft with `Ctrl+C` left numbering state behind even though
the active pending paste state was gone. This updates the composer to
derive the next placeholder suffix from active pending pastes instead,
which keeps simultaneous same-size pastes distinct while letting fresh
drafts reuse the base label. This is also a small code cleanup: pending
paste state is now the source of truth instead of maintaining a separate
counter.
Credit to @Sungyoun-Kim for the issue report, root-cause notes, and fork
with the proposed fix, and to @charley-oai for the earlier related
#10032 proposal.
Changes:
- Remove the monotonic large-paste counter from the composer.
- Compute suffixes from currently active pending paste placeholders.
- Document large-paste placeholder behavior in the composer module docs.
- Add regression coverage for `Ctrl+C` clearing and deletion/reset
behavior.
Testing:
- `just fmt`
- `git diff --check`
This PR adds marketplace upgrade to the `/plugins` menu so users can
update configured marketplaces. It adds a `Ctrl+U` shortcut on eligible
marketplace tabs, a loading state, and the app-server request flow
needed to perform `marketplace/upgrade`. After a successful upgrade, the
TUI refreshes plugin data, plugin mentions, and user config so updated
marketplace contents show up across the menu and other plugin surfaces.
It also preserves the current marketplace tab on no-op and failure paths
and surfaces backend error details directly in the TUI.
- Add a `Ctrl+U` upgrade option for user-configured marketplace tabs in
`/plugins`
- Show the upgrade footer hint only on upgradeable marketplace tabs
- Show a loading state during `marketplace/upgrade`
- Surface already-up-to-date and per-marketplace failure results from
the backend
- Refresh plugin data, plugin mentions, and user config after successful
upgrades
- Add tests and snapshot updates for the shortcut flow, loading state,
and failure messaging
Steps to test:
1. Add a `/plugin` marketplace to Codex TUI.
2. Open `/plugins`, move to that marketplace tab, and confirm the footer
shows `Ctrl+U` to upgrade.
3. Press `Ctrl+U` and confirm the popup switches into an upgrade loading
state.
4. When the request finishes, confirm you see the expected result:
updated marketplace contents on success, an already-up-to-date message
on no-op, or backend error details on failure. On no-op or failure,
confirm the popup stays on the same marketplace tab.
This PR adds marketplace removal to the /plugins menu, giving users a
way to remove user-configured plugin marketplaces. It adds a `Ctrl+R`
shortcut to remove selected marketplace tabs, a confirmation prompt,
loading and error states, and the app-server request flow needed to
perform marketplace/remove. After a successful removal, the TUI
refreshes config, plugin mentions, user config, and plugin data so the
removed marketplace disappears from the menu and other surfaces in the
TUI.
- Add `Ctrl+R` removal option for user-configured marketplace tabs
- Show marketplace removal confirmation, loading, and error states
- Route `marketplace/remove` through the TUI background request flow
- Refresh config, plugin mentions, and plugin data after successful
removal
- Adds reusable per-tab footer hints so removal guidance only appears on
applicable tabs
- Add test coverage for `Ctrl+R` behavior while plugin search is active
Steps to test:
- Add a marketplace using the TUI /plugins menu
- Use Ctrl+R to remove the marketplace
- Accept the confirmation prompt
- Confirm the marketplace is removed when the process completes.
Fixes test that often fails locally when running `cargo test`
- Add an app-server test helper that combines managed-config isolation
with custom env overrides.
- Isolate `HOME` / `USERPROFILE` in plugin-list workspace settings tests
so host home marketplaces do not affect results.
Fix for #19925
Restore the `Working` indicator after a streamed final answer finishes
when a user steer message is sent.
Add regression coverage for long output plus a mid-stream steer:
`cargo test -p codex-tui
final_answer_completion_restores_status_indicator_for_pending_steer`
Duplication/testing steps:
1. Start a new thread and ask for a long response.
2. While the response is streaming, submit a steer message.
3. When the first response finishes, observe whether `Working...` is
shown while waiting for the steer message response.
This PR adds a new feature to the `/plugins` menu that gives users the
ability to add new plugin marketplaces. It introduces an Add Marketplace
tab to the right of installed marketplaces, a source prompt, loading and
error states, and the app-server request flow needed to perform the
install. After a successful `marketplace/add`, the popup refreshes back
into the newly added marketplace tab so the new plugins are immediately
visible.
- Add an Add Marketplace tab to the `/plugins` menu
- Prompt for marketplace source input from git repo, URL, or local path
- Show loading and error states during `marketplace/add`
- Refresh plugin data after success and switch into the newly added
marketplace tab
- Add tests and snapshot updates
Move local resume and fork cwd filtering to `thread/list` instead of
filtering in the TUI. This makes the `/resume` menu feel slightly faster
to load when working in repos with many historical threads, and
centralizes the cwd filtering in app-server.
**Affected:**
- /resume from inside the TUI.
- codex resume with no session ID and without --last
- codex resume --all
- codex fork with no session ID and without --last
- codex fork --all
**Not affected:**
- codex resume <id>
- codex fork <id>
- codex resume --last
- codex fork --last
Steps to test performance improvement in a real Codex environment:
- Launch `codex resume` using compiled binary in a directory that has
seen many threads.
- Launch `codex resume` using release binary in same directory.
- Observe difference in time-to-full-page as threads load.
- Marks `/title` and `/statusline` as available during active tasks.
- Extends the existing slash-command availability test coverage to
include these commands alongside `/goal`.
Implements #18162
This updates the TUI terminal title to show an explicit action-required
state when Codex is blocked on user approval or input. The terminal
title now uses the activity title item to cover both active work and
blocked-on-user states, while still accepting the legacy spinner config
value.
Changes
- Rename the terminal title item from `spinner` to `activity` while
preserving legacy config compatibility
- Show `[ ! ] Action Required `while approval or input overlays are
active, with a blinking `[ . ]` alternate state
- Suppress the normal working spinner while Codex is blocked on user
action
- Add targeted coverage for action-required title behavior and legacy
title-item parsing
Testing
- Trigger an approval or input modal and confirm the tab title
alternates between `[ ! ] Action Required` and `[ . ] Action Required`
- Disable the activity title item and confirm the action-required title
does not appear
- Resolve the prompt and confirm the title returns to the normal
spinning/idel state
https://github.com/user-attachments/assets/e9ecc530-a6be-4fd7-b9a6-d550a790eb2c
Selection menus in the TUI currently let disabled rows interfere with
numbering and default focus. This makes mixed menus harder to read and
can land selection on rows that are not actionable. This change updates
the shared selection-menu behavior in list_selection_view so disabled
rows are not selected when these views open, and prevents them from
being numbered like selectable rows.
- Disabled rows no longer receive numeric labels
- Digit shortcuts map to enabled rows only
- Default selection moves to the first enabled row in mixed menus
- Updated affected snapshot
- Added snapshot coverage for a plugin detail error popup
- Added a focused unit test for shared selection-view behavior
---------
Co-authored-by: Codex <noreply@openai.com>
This change aligns the `/statusline` and `/title` UIs around the same
normalized item model so both surfaces use consistent ids, labels, and
preview semantics. It keeps the shared preview work from #18435 ,
tightens the remaining mismatches by standardizing item naming, expands
title/status item coverage where appropriate, and makes `/title` preview
use the same title-specific formatting path as the real rendered
terminal title.
- Normalizes persisted item ids and keeps legacy aliases for
compatibility
- Aligns `status-line` and `terminal-title` items with the shared
preview model
- Routes `terminal-title` preview through title-specific formatting and
truncation
- Updates the affected status/title setup snapshots
Added to `/statusline`:
- status
- task-progress
Normalized in `/statusline`:
- model-name -> model
- project-root -> project-name
Added to `/title`:
- current-dir
- context-remaining
- context-used
- five-hour-limit
- weekly-limit
- codex-version
- used-tokens
- total-input-tokens
- total-output-tokens
- session-id
- fast-mode
- model-with-reasoning
Normalized in `/title`:
- project -> project-name
- thread -> thread-title
- model-name -> model
This updates TUI skill mentions to show a fallback label when a skill
does not define a display name, so unnamed skills remain understandable
in the picker without changing behavior for skills that already have
one.
<img width="1028" height="198" alt="Screenshot 2026-04-20 at 6 25 15 PM"
src="https://github.com/user-attachments/assets/84077b85-99d0-4db9-b533-37e1887b4506"
/>
This PR makes the `/statusline` and `/title` setup UIs share one
preview-value source instead of each surface using its own examples.
Both pickers now render consistent live values when available, and
stable placeholders when they are not. It also resolves live preview
values at the shared preview-item layer, so `/title` preview can use
real runtime values for title-specific cases like status text, task
progress, and project-name fallback behavior.
- Adds a shared preview data model for status surfaces
- Maps status-line items and terminal-title items onto that shared
preview list
- Feeds both setup views from the same chatwidget-derived preview data,
with terminal-title-specific formatting applied before `/title` preview
renders
- Keeps project-root preview aligned with status-line behavior while
project in /title keeps its title fallback/truncation behavior
- Adds snapshot coverage for live-only, hardcoded-only, and mixed cases
Test Steps
- Open Codex TUI and launch `/statusline`.
- Toggle and reorder items, then verify the preview uses current session
values when possible, and placeholder values for missing values (ex: no
thread ID).
- Open `/title` and verify it shows the same normalized values,
including live status/task-progress values when available.
This PR adds inline enable/disable controls to the new /plugins browse
menu. Installed plugins can now be toggled directly from the list with
keyboard interaction, and the associated config-write plumbing is
included so the UI and persisted plugin state stay in sync. This also
includes the queued-write handling needed to avoid stale toggle
completions overwriting newer intent.
- Add toggleable plugin rows for installed plugins in /plugins
- Support Space to enable or disable without leaving the list
- Persist plugin enablement through the existing app/config write path
- Preserve the current selection while the list refreshes after a toggle
- Add tests and snapshot updates for toggling behavior
---------
Co-authored-by: Codex <noreply@openai.com>
This PR moves `/plugins` onto the shared tabbed selection-list
infrastructure and introduces the new v2 menu. The menu now groups
plugins into All Plugins, Installed, OpenAI Curated, and per-marketplace
tabs.
- Rebuild /plugins on top of the shared tabbed selection list
- Add All Plugins, Installed, OpenAI Curated, and per-marketplace tabs
- Preserve active tab and selected-row behavior across popup refreshes
- Add duplicate marketplace tab-label disambiguation
- Update browse-mode popup tests and snapshots
Co-authored-by: Codex <noreply@openai.com>
This PR adds shared bottom-pane selection-list for future `/plugins`
menu work and wires the existing `/plugins` menu into the new
list-rendering path without changing it to tabs yet. The main
user-visible effect is that the current plugin list now renders as a
denser single-line list with shared name-column sizing, while the tabbed
selection support remains available for follow-up PRs but is currently
unused in production menus.
- Add generic tabbed selection-list support to the bottom pane,
including per-tab headers/items and tab-aware list state
- Add single-line row rendering with ellipsis truncation for dense list
UIs
- Add shared name-column width support so descriptions align
consistently across rows
- Wire the current /plugins menu to the new single-line and shared
column-width behavior only
- Keep tabbed menu adoption deferred; no existing menu is switched to
tabs in this PR
---------
Co-authored-by: Codex <noreply@openai.com>
- When launching the TUI client, if YOLO mode is enabled, display this
in the header.
- Eligibility is determined by `approval_policy = "never"` and
`sandbox_mode = "danger-full-access"`
<img width="886" height="230" alt="image"
src="https://github.com/user-attachments/assets/d7064778-e32c-4123-8e44-ca0c9016ab09"
/>
Fix clippy warnings in external agent config migration
```
error: this expression creates a reference which is immediately dereferenced by the compiler
--> core/src/external_agent_config.rs:188:55
|
188 | let migrated = build_config_from_external(&settings)?;
| ^^^^^^^^^ help: change this to: `settings`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.93.0/index.html#needless_borrow
= note: requested on the command line with `-D clippy::needless-borrow`
error: useless conversion to the same type: `codex_utils_absolute_path::AbsolutePathBuf`
--> core/src/external_agent_config.rs:355:27
|
355 | match AbsolutePathBuf::try_from(
| ___________________________^
356 | | add_marketplace_outcome
357 | | .installed_root
358 | | .join(INSTALLED_MARKETPLACE_MANIFEST_RELATIVE_PATH),
359 | | ) {
| |_____________________^
|
= help: consider removing `AbsolutePathBuf::try_from()`
= help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.93.0/index.html#useless_conversion
= note: `-D clippy::useless-conversion` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::useless_conversion)]`
error: aborting due to 2 previous errors
```
- Add thread-title as an optional TUI status line item, omitted unless
the user has set a custom name (`ChatWidget.thread_name`).
- Refresh the status line when threads are renamded
- Add snapshot coverage for renamed-thread footer behavior.
- Removes provenance filtering in the mentions feature for apps and
skills that were installed as part of a plugin.
- All skills and apps for a plugin are mentionable with this change.
Switch plugin-install background MCP OAuth to a silent login path so the
raw authorization URL is no longer printed in normal success cases.
OAuth behavior is otherwise unchanged, with fallback URL output via
stderr still shown only if browser launch fails.
Before:
https://github.com/user-attachments/assets/4bf387af-afa8-4b83-bcd6-4ca6b55da8db
- Remove marketplace from left column.
- Change `Can be installed` to `Available`
- Align right-column marketplace + selected-row hint text across states.
- Changes applied to both `tui` and `tui_app_server`.
- Update related snapshots/tests.
<img width="2142" height="590" alt="image"
src="https://github.com/user-attachments/assets/6e60b783-2bea-46d4-b353-f2fd328ac4d0"
/>
- Remove numeric prefixes for disabled rows in shared list rendering.
These numbers are shortcuts, Ex: Pressing "2" selects option `#2`.
Disabled items can not be selected, so keeping numbers on these items is
misleading.
- Apply the same behavior in both tui and tui_app_server.
- Update affected snapshots for apps/plugins loading and plugin detail
rows.
_**This is a global change.**_
Before:
<img width="1680" height="488" alt="image"
src="https://github.com/user-attachments/assets/4bcf94ad-285f-48d3-a235-a85b58ee58e2"
/>
After:
<img width="1706" height="484" alt="image"
src="https://github.com/user-attachments/assets/76bb6107-a562-42fe-ae94-29440447ca77"
/>
- Prefer plugin manifest `interface.displayName` for plugin labels.
- Preserve plugin provenance when handling `list_mcp_tools` so connector
`plugin_display_names` are not clobbered.
- Add a TUI test to ensure plugin-owned app mentions are deduped
correctly.
Updates plugin ordering so installed plugins are listed first, with
alphabetical sorting applied within the installed and uninstalled
groups. The behavior is now consistent across both `tui` and
`tui_app_server`, and related tests/snapshots were updated.
Show all plugin marketplaces in the /plugins popup by removing the
`openai-curated` marketplace filter, and update plugin popup
copy/tests/snapshots to match the new behavior in both TUI codepaths.
- Duplicate app mentions are now suppressed when they’re plugin-backed
with the same display name.
- Remaining connector mentions now label category as [Plugin] when
plugin metadata is present, otherwise [App].
- Mention result lists are now capped to 8 rows after filtering.
- Updates both tui and tui_app_server with the same changes.
### Preliminary /plugins TUI menu
- Adds a preliminary /plugins menu flow in both tui and tui_app_server.
- Fetches plugin list data asynchronously and shows loading/error/cached
states.
- Limits this first pass to the curated ChatGPT marketplace.
- Shows available plugins with installed/status metadata.
- Supports in-menu search over plugin display name, plugin id, plugin
name, and marketplace label.
- Opens a plugin detail view on selection, including summaries for
Skills, Apps, and MCP Servers, with back navigation.
### Testing
- Launch codex-cli with plugins enabled (`--enable plugins`).
- Run /plugins and verify:
- loading state appears first
- plugin list is shown
- search filters results
- selecting a plugin opens detail view, with a list of
skills/connectors/MCP servers for the plugin
- back action returns to the list.
- Verify disabled behavior by running /plugins without plugins enabled
(shows “Plugins are disabled” message).
- Launch with `--enable tui_app_server` (and plugins enabled) and repeat
the same /plugins flow; behavior should match.
This PR changes app and connector enablement when `requirements.toml` is
present locally or via remote configuration.
For apps.* entries:
- `enabled = false` in `requirements.toml` overrides the user’s local
`config.toml` and forces the app to be disabled.
- `enabled = true` in `requirements.toml` does not re-enable an app the
user has disabled in config.toml.
This behavior applies whether or not the user has an explicit entry for
that app in `config.toml`. It also applies to cloud-managed policies and
configurations when the admin sets the override through
`requirements.toml`.
Scenarios tested and verified:
- Remote managed, user config (present) override
- Admin-defined policies & configurations include a connector override:
`[apps.<appID>]
enabled = false`
- User's config.toml has the same connector configured with `enabled =
true`
- TUI/App should show connector as disabled
- Connector should be unavailable for use in the composer
- Remote managed, user config (absent) override
- Admin-defined policies & configurations include a connector override:
`[apps.<appID>]
enabled = false`
- User's config.toml has no entry for the the same connector
- TUI/App should show connector as disabled
- Connector should be unavailable for use in the composer
- Locally managed, user config (present) override
- Local requirements.toml includes a connector override:
`[apps.<appID>]
enabled = false`
- User's config.toml has the same connector configured with `enabled =
true`
- TUI/App should show connector as disabled
- Connector should be unavailable for use in the composer
- Locally managed, user config (absent) override
- Local requirements.toml includes a connector override:
`[apps.<appID>]
enabled = false`
- User's config.toml has no entry for the the same connector
- TUI/App should show connector as disabled
- Connector should be unavailable for use in the composer
<img width="1446" height="753" alt="image"
src="https://github.com/user-attachments/assets/61c714ca-dcca-4952-8ad2-0afc16ff3835"
/>
<img width="595" height="233" alt="image"
src="https://github.com/user-attachments/assets/7c8ab147-8fd7-429a-89fb-591c21c15621"
/>
Adds a new apps_mcp_gateway flag to route Apps MCP calls through
https://api.openai.com/v1/connectors/mcp/ when enabled, while keeping
legacy MCP routing as default.
This PR introduces a skill-expansion mechanism for mentions so nested or
skill or connection mentions are expanded if present in skills invoked
by the user. This keeps behavior aligned with existing mention handling
while extending coverage to deeper scenarios. With these changes, users
can create skills that invoke connectors, and skills that invoke other
skills.
Replaces #10863, which is not needed with the addition of
[search_tool_bm25](https://github.com/openai/codex/issues/10657)
This PR makes `Config.apps `experimental-only and fixes a TS schema
post-processing bug that removed needed imports. The bug happened
because import pruning only checked the inner type body after filtering,
not the full alias, so `JsonValue` got dropped from `Config.ts`. We now
prune against the full alias body and added a regression test for this
scenario.
This PR fixes jitter in the TUI apps menu by making the description
column stable during rendering and height measurement.
Added a `stable_desc_col` option to
`SelectionViewParams`/`ListSelectionView`, introduced stable variants of
the shared row render/measure helpers in `selection_popup_common`, and
enabled the stable mode for the apps/connectors picker in `chatwidget`.
With these changes, only the apps/connectors picker uses this new
option, though it could be used elsewhere in the future.
Why: previously, the description column was computed from only currently
visible rows, so as you scrolled or filtered, the column could shift and
cause wrapping/height changes that looked jumpy. Computing it from all
rows in this popup keeps alignment and layout consistent as users scroll
through avaialble apps.
**Before:**
https://github.com/user-attachments/assets/3856cb72-5465-4b90-a993-65a2ffb09113
**After:**
https://github.com/user-attachments/assets/37b9d626-0b21-4c0f-8bb8-244c9ef971ff
This PR adds a new approval option for app/MCP tool calls: “Allow and
remember” (session-scoped).
When selected, Codex stores a temporary approval and auto-approves
matching future calls for the rest of the session.
Added a session-scoped approval key (`server`, `connector_id`,
`tool_name`) and persisted it in `tool_approvals` as
`ApprovedForSession`.
On subsequent matching calls, approval is skipped and treated as
accepted.
- Updated the approval question options to conditionally include:
- Accept
- Allow and remember (conditional)
- Decline
- Cancel
The new “Allow and remember” option is only shown when all of these are
true:
1. The call is routed through the Codex Apps MCP server (codex_apps).
2. The tool requires approval based on annotations:
- read_only_hint == false, and
- destructive_hint == true or open_world_hint == true.
3. The tool includes a connector_id in metadata (used to build the
remembered approval key).
If no `connector_id` is present, the prompt still appears (when approval
is required), but only with the existing choices (Accept / Decline /
Cancel). Approval prompting in this path has an explicit early return
unless server == `codex_apps`.