## What
Show an inline `ctrl + t to view transcript` hint when exec output is
truncated in the main TUI chat view.
## Why
Today, truncated exec output shows `… +N lines`, but it does not tell
users that the full content is already available through the existing
transcript overlay. That makes hidden output feel lost instead of
discoverable.
This change closes that discoverability gap without introducing a new
interaction model.
Fixes: CLI-5740
## How
- added an output-specific truncation hint in `ExecCell` rendering
- applied that hint in both exec-output truncation paths:
- logical head/tail truncation before wrapping
- row-budget truncation after wrapping
- preserved the existing row-budget behavior on narrow terminals by
reserving space for the longer hint line
- updated the relevant snapshot and added targeted regression coverage
## Intentional design decisions
- **Aligned shortcut styling with the visible footer UI**
The inline hint uses `ctrl + t`, not `Ctrl+T`, to match the TUI’s
rendered key-hint style.
- **Kept the noun `transcript`**
The product already exposes this flow as the transcript overlay, so the
hint points at the existing concept instead of inventing a new label.
- **Preserved narrow-terminal behavior**
The longer hint text is accounted for in the row-budget truncation path
so the visible output still respects the existing viewport cap.
- **Did not add the hint to long command truncation**
This PR only changes hidden **output** truncation. Long command
truncation still uses the plain ellipsis form because `ctrl + t` is not
the same kind of “show hidden output” escape hatch there.
- **Did not widen scope to other truncation surfaces**
This does not change MCP/tool-call truncation in `history_cell.rs`, and
it does not change transcript-overlay behavior itself.
## Validation
### Automated
- `just fmt`
- `cargo test -p codex-tui`
### Manual
- ran `just tui-with-exec-server`
- executed `!seq 1 200`
- confirmed the main view showed the new `ctrl + t to view transcript`
truncation hint
- pressed `ctrl + t` and confirmed the transcript overlay still exposed
the full output
- closed the overlay and returned to the main view
## Visual proof
Screenshot/video attached in the PR UI showing:
- the truncated exec output row with the new hint
- the transcript overlay after `ctrl + t`
## Summary
- Replace Codex-branded network-proxy block responses with concise
reason text
- Mention sandbox policy for local/private network and deny-policy
wording
- Remove “managed” from the proxy-disabled denial detail
# 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.
## Summary
- detect remote exec-server sessions in the unified-exec runtime
- bypass the local shell-snapshot bootstrap only for those remote
sessions
- preserve existing local snapshot wrapping, PowerShell UTF-8 prefixing,
sandbox orchestration, and zsh-fork handling
## Why
The shell snapshot file is currently captured and stored next to Core.
If Core wraps a remote command with `. /path/to/local/snapshot`, the
process starts on the executor and tries to source a path from the
orchestrator filesystem. This keeps remote commands from receiving that
known-local path until shell snapshots are captured/restored on the
executor side.
## Validation
- `just fmt`
- `git diff --check`
- `cargo test -p codex-core --lib tools::runtimes::tests`
Co-authored-by: Codex <noreply@openai.com>
Problem: The statusline reported context as an “X% left” value, which
could be mistaken for quota, and context usage was included in the
default footer.
Solution: Render configured context status items as a filling context
meter, preserve `context-used` as a legacy alias while hiding it from
the setup menu, and remove context from the default statusline. It will
still be available as an opt-in option for users who want to see it.
<img width="317" height="39" alt="image"
src="https://github.com/user-attachments/assets/3aeb39bb-f80d-471f-88fe-d55e25b31491"
/>
## Summary
- keep pending steered input buffered until the active user prompt has
received a model response
- keep steering pending across auto-compact when there is real
model/tool continuation to resume
- allow queued steering to follow compaction immediately when the prior
model response was already final
- keep pending-input follow-up owned by `run_turn` instead of folding it
into `SamplingRequestResult`
- add regression coverage for mid-turn compaction, final-response
compaction, and compaction triggered before the next request after tool
output
## Root Cause
Steered input was drained at the top of every `run_turn` loop. After
auto-compaction, the loop continued and immediately appended any pending
steer after the compact summary, making a queued prompt look like the
newest task instead of letting the model first resume interrupted
model/tool work.
## Implementation Notes
This patch keeps the follow-up signals separated:
- `SamplingRequestResult.needs_follow_up` means model/tool continuation
is needed
- `sess.has_pending_input().await` means queued user steering exists
- `run_turn` computes the combined loop condition from those two signals
In `run_turn`:
```rust
let has_pending_input = sess.has_pending_input().await;
let needs_follow_up = model_needs_follow_up || has_pending_input;
```
After auto-compact we choose whether the next request may drain
steering:
```rust
can_drain_pending_input = !model_needs_follow_up;
```
That means:
- model/tool continuation + pending steer: compact -> resume once
without draining steer
- completed model answer + pending steer: compact -> drain/send the
steer immediately
- fresh user prompt: do not drain steering before the model has answered
the prompt once
The drain is still only `sess.get_pending_input().await`; when
`can_drain_pending_input` is false, core uses an empty local vec and
leaves the steer pending in session state.
## Validation
- PASS `cargo test -p codex-core --test all steered_user_input --
--nocapture`
- PASS `just fmt`
- PASS `git diff --check`
- NOT PASSING HERE `just fix -p codex-core` currently stops before
linting this change on an unrelated mainline test-build error:
`core/src/tools/spec_tests.rs` initializes `ToolsConfigParams` without
`image_generation_tool_auth_allowed`; this PR does not touch that file.
## Summary
- make realtime default to the v2 WebRTC path
- keep partial realtime config tables inheriting
`RealtimeConfig::default()`
## Validation
- CI found a stale config-test expectation; fixed in 974ba51bb3
- just fmt
- git diff --check
---------
Co-authored-by: Codex <noreply@openai.com>
Addresses #17166
Problem: Source builds report version 0.0.0, so the TUI update path can
treat any released Codex version as upgradeable and show startup or
popup prompts.
Solution: Skip both TUI update prompt entry points when the running CLI
version is the source-build sentinel 0.0.0.
- Default realtime sessions to v2 and gpt-realtime-1.5 when no override
is configured.
- Add Op::RealtimeConversationStart integration coverage and keep
v1-specific tests explicit.
---------
Co-authored-by: Codex <noreply@openai.com>
Problem: TUI desktop notifications are hard-gated on terminal focus, so
terminal/IDE hosts that want in-focus notifications cannot opt in.
Solution: Add a flat `[tui] notification_condition` setting (`unfocused`
by default, `always` opt-in), carry grouped TUI notification settings
through runtime config, apply method + condition together in the TUI,
and regenerate the config schema.
- Adds a core-owned realtime backend prompt template and preparation
path.
- Makes omitted realtime start prompts use the core default, while null
or empty prompts intentionally send empty instructions.
- Covers the core realtime path and app-server v2 path with integration
coverage.
---------
Co-authored-by: Codex <noreply@openai.com>
Addresses #15943
Problem: Name-based resume could stop on a newer session_index entry
whose rollout was never persisted, shadowing an older saved thread with
the same name.
Solution: Materialize rollouts before indexing thread names and make
name lookup skip unresolved entries until it finds a persisted rollout.
Problem: Warp supports OSC 9 notifications, but the TUI's automatic
notification backend selection did not recognize its
`TERM_PROGRAM=WarpTerminal` environment value.
Solution: Treat `TERM_PROGRAM=WarpTerminal` as OSC 9-capable when
choosing the TUI desktop notification backend.
Currently, when a MCP server sends an elicitation to Codex running in
Full Access (`sandbox_policy: DangerFullAccess` + `approval_policy:
Never`), the elicitations are auto-cancelled.
This PR updates the automatic handling of MCP elicitations to be
consistent with other approvals in full-access, where they are
auto-approved. Because MCP elicitations may actually require user input,
this mechanism is limited to empty form elicitations.
## Changeset
- Add policy helper shared with existing MCP tool call approval
auto-approve
- Update `ElicitationRequestManager` to auto-approve elicitations in
full access when `can_auto_accept_elicitation` is true.
- Add tests
Co-authored-by: Codex <noreply@openai.com>
## Summary
- add a top-level `codex exec-server` subcommand, marked experimental in
CLI help
- launch an adjacent or PATH-provided `codex-exec-server`, with a
source-tree `cargo run -p codex-exec-server --` fallback
- cover the new subcommand parser path
## Validation
- `just fmt`
- `git diff --check`
- not run: Rust test suite
Co-authored-by: Codex <noreply@openai.com>
---------
Co-authored-by: Codex <noreply@openai.com>
Summary:
- parse the realtime call Location header and join that call over the
direct realtime WebSocket
- keep WebRTC starts alive on the existing realtime conversation path
Validation:
- just fmt
- git diff --check
- cargo check -p codex-api
- cargo check -p codex-core --tests
- local cargo tests not run; relying on PR CI
- Builds codex-realtime-webrtc through the normal Bazel Rust macro so
native macOS WebRTC sources are included.\n- Shares the macOS -ObjC link
flag with Bazel targets that can link libwebrtc.
---------
Co-authored-by: Codex <noreply@openai.com>
## Summary
- add the missing `image_generation_tool_auth_allowed` field to the new
tool registry plan test initializer
## Validation
- `just fmt`
- `cargo test -p codex-tools image_generation`
- `cargo test -p codex-tools --no-run`
When running with remote executor the cwd is the remote path. Today we
check for existence of a local directory on startup and attempt to load
config from it.
For remote executors don't do that.
## Summary
- add optional `sandboxPolicy` support to the app-server filesystem
request surface
- thread sandbox-aware filesystem options through app-server and
exec-server adapters
- enforce sandboxed read/write access in the filesystem abstraction with
focused local and remote coverage
## Validation
- `cargo test -p codex-app-server-protocol`
- `cargo test -p codex-exec-server file_system`
- `cargo test -p codex-app-server suite::v2::fs`
---------
Co-authored-by: Codex <noreply@openai.com>
**Disabling Image-Gen for Non-SIWC Codex Users**
We are only enabling image-gen feature for SIWC Codex users until there
comes a fix in ResponsesAPI to omit output from responses.completed, to
prevent the following issues:
1. websocket blows up due to heavier load (images) than before (text)
2. http parser streams through n^2 of n-base64 bytes (sum of base64s of
all images generated in turn) that causes long delays in
turn_completion.
## Description
This PR fixes `/debug-config` so it shows more of the active
requirements state, including reviewer requirements and managed feature
pins. This made it clear that legacy MDM config was setting
`approvals_reviewer = "guardian_subagent"` and that we were translating
that into a requirements constraint.
Also, translate `approvals_reviewer = "guardian_subagent"` (from legacy
managed_config.toml) to `allowed_approvals_reviewers: guardian_subagent,
user` instead of `allowed_approvals_reviewers: guardian_subagent`.
Example `/debug-config`:
```
Config layer stack (lowest precedence first):
1. system (/etc/codex/config.toml) (enabled)
2. user (/Users/owen/.codex/config.toml) (enabled)
3. project (/Users/owen/repos/codex/.codex/config.toml) (enabled)
4. legacy managed_config.toml (MDM) (enabled)
MDM value:
...
# Enable Guardian Mode
features.guardian_approval = true
approvals_reviewer = "guardian_subagent"
Requirements:
- allowed_approvals_reviewers: guardian_subagent, user (source: MDM managed_config.toml (legacy))
- features: apps=true, plugins=true (source: cloud requirements)
```
Before this PR, the `Requirements` section showed None.
## Summary
- Carry `AbsolutePathBuf` through tool cwd parsing/resolution instead of
resolving workdirs to raw `PathBuf`s.
- Type exec/sandbox request cwd fields as `AbsolutePathBuf` through
`ExecParams`, `ExecRequest`, `SandboxCommand`, and unified exec runtime
requests.
- Keep `PathBuf` conversions at external/event boundaries and update
existing tests/fixtures for the typed cwd.
## Validation
- `cargo check -p codex-core --tests`
- `cargo check -p codex-sandboxing --tests`
- `cargo test -p codex-sandboxing`
- `cargo test -p codex-core --lib tools::handlers::`
- `just fix -p codex-sandboxing`
- `just fix -p codex-core`
- `just fmt`
Full `codex-core` test suite was not run locally; per repo guidance I
kept local validation targeted.
Adds the `[realtime].transport = "webrtc"` TUI media path using a new
`codex-realtime-webrtc` crate, while leaving app-server as the
signaling/event source.\n\nLocal checks: fmt, diff-check, dependency
tree only; test signal should come from CI.
---------
Co-authored-by: Codex <noreply@openai.com>
Addresses #16971
Problem: Disabled MCP servers were still queried for streamable HTTP
auth status during MCP inventory, so unreachable disabled entries could
add startup latency.
Solution: Return `Unsupported` immediately for disabled MCP server
configs before bearer token/OAuth status discovery.
Problem: Resuming the live TUI thread through `/resume` could
unsubscribe and reconnect the same app-server thread, leaving the UI
crashed or disconnected.
Solution: No-op `/resume` only when the selected thread is the currently
attached active thread; keep the normal resume path for
stale/displayed-only threads so recovery and reattach still work.
Addresses #3793
Problem: /status only reported project-level AGENTS files, so sessions
with a loaded global $CODEX_HOME/AGENTS.md still showed Agents.md as
<none>.
Solution: Track the global instructions file loaded during config
initialization and prepend that path to the /status Agents.md summary,
with coverage for AGENTS.md, AGENTS.override.md, and global-plus-project
ordering.
Allow multi_agent_v2 features to have its own temporary configuration
under `[features.multi_agent_v2]`
```
[features.multi_agent_v2]
enabled = true
usage_hint_enabled = false
usage_hint_text = "Custom delegation guidance."
hide_spawn_agent_metadata = true
```
Absent `usage_hint_text` means use the default hint.
```
[features]
multi_agent_v2 = true
```
still works as the boolean shorthand.