This pull request updates the ChatGPT login description in the
onboarding authentication widgets to clarify which plans include usage.
The description now lists "Business" rather than "Team" and adds
"Education" plans in addition to the previously mentioned plans.
I have read the CLA Document and I hereby sign the CLAs.
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
# External (non-OpenAI) Pull Request Requirements
Before opening this Pull Request, please read the dedicated
"Contributing" markdown file or your PR may be closed:
https://github.com/openai/codex/blob/main/docs/contributing.md
If your PR conforms to our contribution guidelines, replace this text
with a detailed and high quality description of your changes.
Include a link to a bug report or enhancement request.
This PR does various types of cleanup before I can proceed with more
ambitious changes to config loading.
First, I noticed duplicated code across these two methods:
774bd9e432/codex-rs/core/src/config/mod.rs (L314-L324)774bd9e432/codex-rs/core/src/config/mod.rs (L334-L344)
This has now been consolidated in
`load_config_as_toml_with_cli_overrides()`.
Further, I noticed that `Config::load_with_cli_overrides()` took two
similar arguments:
774bd9e432/codex-rs/core/src/config/mod.rs (L308-L311)
The difference between `cli_overrides` and `overrides` was not
immediately obvious to me. At first glance, it appears that one should
be able to be expressed in terms of the other, but it turns out that
some fields of `ConfigOverrides` (such as `cwd` and
`codex_linux_sandbox_exe`) are, by design, not configurable via a
`.toml` file or a command-line `--config` flag.
That said, I discovered that many callers of
`Config::load_with_cli_overrides()` were passing
`ConfigOverrides::default()` for `overrides`, so I created two separate
methods:
- `Config::load_with_cli_overrides(cli_overrides: Vec<(String,
TomlValue)>)`
- `Config::load_with_cli_overrides_and_harness_overrides(cli_overrides:
Vec<(String, TomlValue)>, harness_overrides: ConfigOverrides)`
The latter has a long name, as it is _not_ what should be used in the
common case, so the extra typing is designed to draw attention to this
fact. I tried to update the existing callsites to use the shorter name,
where possible.
Further, in the cases where `ConfigOverrides` is used, usually only a
limited subset of fields are actually set, so I updated the declarations
to leverage `..Default::default()` where possible.
# External (non-OpenAI) Pull Request Requirements
Before opening this Pull Request, please read the dedicated
"Contributing" markdown file or your PR may be closed:
https://github.com/openai/codex/blob/main/docs/contributing.md
If your PR conforms to our contribution guidelines, replace this text
with a detailed and high quality description of your changes.
Include a link to a bug report or enhancement request.
Constrain `approval_policy` through new `admin_policy` config.
This PR will:
1. Add a `admin_policy` section to config, with a single field (for now)
`allowed_approval_policies`. This list constrains the set of
user-settable `approval_policy`s.
2. Introduce a new `Constrained<T>` type, which combines a current value
and a validator function. The validator function ensures disallowed
values are not set.
3. Change the type of `approval_policy` on `Config` and
`SessionConfiguration` from `AskForApproval` to
`Constrained<AskForApproval>`. The validator function is set by the
values passed into `allowed_approval_policies`.
4. `GenericDisplayRow`: add a `disabled_reason: Option<String>`. When
set, it disables selection of the value and indicates as such in the
menu. This also makes it unselectable with arrow keys or numbers. This
is used in the `/approvals` menu.
Follow ups are:
1. Do the same thing to `sandbox_policy`.
2. Propagate the allowed set of values through app-server for the
extension (though already this should prevent app-server from setting
this values, it's just that we want to disable UI elements that are
unsettable).
Happy to split this PR up if you prefer, into the logical numbered areas
above. Especially if there are parts we want to gavel on separately
(e.g. admin_policy).
Disabled full access:
<img width="1680" height="380" alt="image"
src="https://github.com/user-attachments/assets/1fb61c8c-1fcb-4dc4-8355-2293edb52ba0"
/>
Disabled `--yolo` on startup:
<img width="749" height="76" alt="image"
src="https://github.com/user-attachments/assets/0a1211a0-6eb1-40d6-a1d7-439c41e94ddb"
/>
CODEX-4087
1. Adds SkillScope::Public end-to-end (core + protocol) and loads skills
from the public cache directory
2. Improves repo skill discovery by searching upward for the nearest
.codex/skills within a git repo
3. Deduplicates skills by name with deterministic ordering to avoid
duplicates across sources
4. Fixes garbled “Skill errors” overlay rendering by preventing pending
history lines from being injected during the modal
5. Updates the project docs “Skills” intro wording to avoid hardcoded
paths
Introduces an `EventBroker` between the crossterm `EventStream` source
and the consumers in the TUI. This enables dropping + recreating the
`crossterm_events` without invalidating the consumer.
Dropping and recreating the crossterm event stream enables us to fully
relinquish `stdin` while the app keeps running. If the stream is not
dropped, it will continue to read from `stdin` even when it is not
actively being polled, potentially stealing input from other processes.
See
[here](https://www.reddit.com/r/rust/comments/1f3o33u/myterious_crossterm_input_after_running_vim/?utm_source=chatgpt.com)
and [here](https://ratatui.rs/recipes/apps/spawn-vim/) for details.
### Tests
Added tests for new `EventBroker` setup, existing tests pass, tested
locally.
Human TL;DR - in some situations, pasting/rapidly inputting text will
currently cause `?` characters to be stripped from the input message
content, and display the key bindings helper. For instance, writing
"Where is X defined? Can we do Y?" results in "Where is X defined Can we
do Y" being added to the message draft area. This is mildly annoying.
The fix was a simple one line addition. Added a test, ran linters, and
all looks good to me. I didn't create an issue to link to in this PR - I
had submitted this bug as a report a while ago but can't seem to find it
now. Let me know if it's an absolute must for the PR to be accepted.
I have read the CLA Document and I hereby sign the CLA
Below is Codex's summary.
---
# `?` characters toggling shortcuts / being dropped
## Symptom
On Termux (and potentially other terminal environments), composing text
in the native input field and sending it to the TTY can cause:
- The shortcuts overlay to appear (as if `?` was pressed on an empty
prompt), and
- All of the literal `?` characters in the text to be **missing** from
the composer input,
even when `?` is not the first character.
This typically happens when the composer was previously empty and the
terminal delivers the text as a rapid sequence of key events rather than
a single bracketed paste event.
## Root cause
The TUI has two relevant behaviors:
1. **Shortcut toggle on `?` when empty**
- `ChatComposer::handle_shortcut_overlay_key` treats a plain `?` press
as a toggle between the shortcut summary and the full shortcut overlay,
but only when the composer is empty.
- When it toggles, it consumes the key event (so `?` is *not* inserted
into the text input).
2. **“Paste burst” buffering for fast key streams**
- The TUI uses a heuristic to detect “paste-like” input bursts even when
the terminal doesn’t send an explicit paste event.
- During that burst detection, characters can be buffered (and the text
area can remain empty temporarily) while the system decides whether to
treat the stream as paste-like input.
In Termux’s “send composed text all at once” mode, the input often
arrives as a very fast stream of `KeyCode::Char(...)` events. While that
stream is being buffered as a burst, the visible textarea can still be
empty. If a `?` arrives during this window, it matches “empty composer”
and is interpreted as “toggle shortcuts” instead of “insert literal
`?`”, so the `?` is dropped.
## Fix
Make the `?` toggle conditional on not being in any paste-burst
transient state.
Implementation:
- `ChatComposer::handle_shortcut_overlay_key` now checks
`!self.is_in_paste_burst()` in addition to `self.is_empty()` before
toggling.
- This ensures that when input is arriving as a fast burst (including
the “pending first char” case), `?` is treated as normal text input
rather than a UI toggle.
## Test coverage
Added a test that simulates a Termux-like fast stream:
- Sends `h i ? t h e r e` as immediate successive `KeyEvent::Char`
events (no delays).
- Asserts that a paste burst is active and the textarea is still empty
while buffering.
- Flushes the burst and verifies:
- The final text contains the literal `?` (`"hi?there"`), and
- The footer mode is not `ShortcutOverlay`.
## Notes
This fix intentionally keeps the existing UX:
- `?` still toggles shortcuts when the composer is genuinely empty and
the user is not in the middle of entering text.
- `?` typed while composing content (including IME/native-input fast
streams) remains literal.
refactor the way we load and manage skills:
1. Move skill discovery/caching into SkillsManager and reuse it across
sessions.
2. Add the skills/list API (Op::ListSkills/SkillsListResponse) to fetch
skills for one or more cwds. Also update app-server for VSCE/App;
3. Trigger skills/list during session startup so UIs preload skills and
handle errors immediately.
Codex identified this as the cause of a reported hang:
https://github.com/openai/codex/issues/7822. Apparently, the wrapping
algorithm we're using has known issues and bad worst-case behaviors when
OptimalFit is used on certain strings. It recommended switching to
FirstFit instead.
Changes the `writable_roots` field of the `WorkspaceWrite` variant of
the `SandboxPolicy` enum from `Vec<PathBuf>` to `Vec<AbsolutePathBuf>`.
This is helpful because now callers can be sure the value is an absolute
path rather than a relative one. (Though when using an absolute path in
a Seatbelt config policy, we still have to _canonicalize_ it first.)
Because `writable_roots` can be read from a config file, it is important
that we are able to resolve relative paths properly using the parent
folder of the config file as the base path.
## Problem
The introduction of `notify_sandbox_state_change()` in #7112 caused a
regression where the blocking call in `Session::new()` waits for all MCP
servers to fully initialize before returning. This prevents the TUI
event loop from starting, resulting in `McpStartupUpdateEvent` messages
being emitted but never consumed or displayed. As a result, the app
appears to hang during startup, and users do not see the expected
"Booting MCP server: {name}" status line.
Issue: [#7827](https://github.com/openai/codex/issues/7827)
## Solution
This change moves sandbox state notification into each MCP server's
background initialization task. The notification is sent immediately
after the server transitions to the Ready state. This approach:
- Avoids blocking `Session::new()`, allowing the TUI event loop to start
promptly.
- Ensures each MCP server receives its sandbox state before handling any
tool calls.
- Restores the display of "Booting MCP server" status lines during
startup.
## Key Changes
- Added `ManagedClient::notify_sandbox_state()` method.
- Passed sandbox_state to `McpConnectionManager::initialize()`.
- Sends sandbox state notification in the background task after the
server reaches Ready status.
- Removed blocking notify_sandbox_state_change() methods.
- Added a chatwidget snapshot test for the "Booting MCP server" status
line.
## Regression Details
Regression was bisected to #7112, which introduced the blocking
behavior.
---------
Co-authored-by: Michael Bolin <bolinfest@gmail.com>
Co-authored-by: Michael Bolin <mbolin@openai.com>
# External (non-OpenAI) Pull Request Requirements
Before opening this Pull Request, please read the dedicated
"Contributing" markdown file or your PR may be closed:
https://github.com/openai/codex/blob/main/docs/contributing.md
If your PR conforms to our contribution guidelines, replace this text
with a detailed and high quality description of your changes.
Include a link to a bug report or enhancement request.
### What
Don't treat `q` as a special quit character on the API key paste page in
the onboarding flow.
This addresses #7413, where pasting API keys with `q` would cause codex
to quit on Windows.
### Test Plan
Tested on Windows and MacOS.
1. Skills load once in core at session start; the cached outcome is
reused across core and surfaced to TUI via SessionConfigured.
2. TUI detects explicit skill selections, and core injects the matching
SKILL.md content into the turn when a selected skill is present.
## Slash Command popup issue
#7659
When recalling history, the
composer(`codex_tui::bottom_pane::chat_composer`) restores the previous
prompt text (which may start with `/`) and then calls
`sync_command_popup`. The logic in `sync_command_popup` treats any first
line that starts with `/` and has the caret inside the initial `/name`
token as an active slash command name:
```rust
let is_editing_slash_command_name = if first_line.starts_with('/') && caret_on_first_line {
let token_end = first_line
.char_indices()
.find(|(_, c)| c.is_whitespace())
.map(|(i, _)| i)
.unwrap_or(first_line.len());
cursor <= token_end
} else {
false
};
```
This detection does not distinguish between an actual interactive slash
command being typed and a normal historical prompt that happens to begin
with `/`. As a result, after history recall, the restored prompt like `/
test` is interpreted as an "editing command name" context and the
slash-command popup is (re)activated. Once `active_popup` is
`ActivePopup::Command`, subsequent `Up` key presses are handled by
`handle_key_event_with_slash_popup` instead of
`handle_key_event_without_popup`, so they no longer trigger
`history.navigate_up(...)` and the session prompt history cannot be
scrolled.
- Make Config.model optional and centralize default-selection logic in
ModelsManager, including a default_model helper (with
codex-auto-balanced when available) so sessions now carry an explicit
chosen model separate from the base config.
- Resolve `model` once in `core` and `tui` from config. Then store the
state of it on other structs.
- Move refreshing models to be before resolving the default model
## Summary
Support "j" and "k" keys as aliases for "down" and "up" so vim users
feel loved. Only support these keys when the selection is not
searchable.
## Testing
- env -u NO_COLOR TERM=xterm-256color cargo test -p codex-tui
------
[Codex
Task](https://chatgpt.com/codex/tasks/task_i_693771b53bc8833088669060dfac2083)
## What
Fix PageUp/PageDown behaviour in the Ctrl+T transcript overlay so that
paging is continuous and reversible, and add tests to lock in the
expected behaviour.
## Why
Today, paging in the transcript overlay uses the raw viewport height
instead of the effective content height after layout. Because the
overlay reserves some rows for chrome (header/footer), this can cause:
- PageDown to skip transcript lines between pages.
- PageUp/PageDown not to “round-trip” cleanly (PageDown then PageUp does
not always return to the same set of visible lines).
This shows up when inspecting longer transcripts via Ctrl+T; see #7356
for context.
## How
- Add a dedicated `PagerView::page_step` helper that computes the page
size from the last rendered content height and falls back to
`content_area(viewport_area).height` when that is not yet available.
- Use `page_step(...)` for both PageUp and PageDown (including SPACE) so
the scroll step always matches the actual content area height, not the
full viewport height.
- Add a focused test
`transcript_overlay_paging_is_continuous_and_round_trips` that:
- Renders a synthetic transcript with numbered `line-NN` rows.
- Asserts that successive PageDown operations show continuous line
numbers (no gaps).
- Asserts that PageDown+PageUp and PageUp+PageDown round-trip correctly
from non-edge offsets.
The change is limited to `codex-rs/tui/src/pager_overlay.rs` and only
affects the transcript overlay paging semantics.
## Related issue
- #7356
## Testing
On Windows 11, using PowerShell 7 in the repo root:
```powershell
cargo test
cargo clippy --tests
cargo fmt -- --config imports_granularity=Item
```
- All tests passed.
- `cargo clippy --tests` reported some pre-existing warnings that are
unrelated to this change; no new lints were introduced in the modified
code.
---------
Signed-off-by: muyuanjin <24222808+muyuanjin@users.noreply.github.com>
Co-authored-by: Eric Traut <etraut@openai.com>
This is a step towards removing the need to know `model` when
constructing config. We firstly don't need to know `model_info` and just
respect if the user has already set it. Next step, we don't need to know
`model` unless the user explicitly set it in `config.toml`
## Summary
- restore the previous status header when a non-error event arrives
after a stream retry
- add a regression test to ensure the reconnect banner clears once
streaming resumes
## Testing
- cargo fmt -- --config imports_granularity=Item
- cargo clippy --fix --all-features --tests --allow-dirty -p codex-tui
- NO_COLOR=0 cargo test -p codex-tui *(fails: vt100 color assertion
tests expect colored cells but the environment returns Default colors
even with NO_COLOR cleared and TERM/COLORTERM set)*
------
[Codex
Task](https://chatgpt.com/codex/tasks/task_i_69337f8c77508329b3ea85134d4a7ac7)
# External (non-OpenAI) Pull Request Requirements
Before opening this Pull Request, please read the dedicated
"Contributing" markdown file or your PR may be closed:
https://github.com/openai/codex/blob/main/docs/contributing.md
If your PR conforms to our contribution guidelines, replace this text
with a detailed and high quality description of your changes.
Include a link to a bug report or enhancement request.
# External (non-OpenAI) Pull Request Requirements
Before opening this Pull Request, please read the dedicated
"Contributing" markdown file or your PR may be closed:
https://github.com/openai/codex/blob/main/docs/contributing.md
If your PR conforms to our contribution guidelines, replace this text
with a detailed and high quality description of your changes.
Include a link to a bug report or enhancement request.