Commit Graph

1517 Commits

Author SHA1 Message Date
David Z Hao
7c43f8bb5e Fix tui compilation (#17691)
The recent release broke, codex suggested this as the fix

Source failure:
https://github.com/openai/codex/actions/runs/24362949066/job/71147202092

Probably from
ac82443d07

For why it got in:
```
The relevant setup:

.github/workflows/rust-ci.yml (line 1) runs on PRs, but for codex-rs it only does:

cargo fmt --check
cargo shear
argument-comment lint via Bazel
no cargo check, no cargo clippy over the workspace, no cargo test over codex-tui
.github/workflows/rust-ci-full.yml (line 1) runs on pushes to main and branches matching **full-ci**. That one does compile TUI because:

codex-rs/Cargo.toml includes "tui" as a workspace member
lint_build runs cargo clippy --target ... --tests --profile ...
the matrix includes both dev and release profiles
tests runs cargo nextest run ..., but only dev-profile tests
Release CI also compiles it indirectly. .github/workflows/rust-release.yml (line 235) builds --bin codex, and cli/Cargo.toml (line 46) depends on codex-tui.
```

Codex tested locally with `cargo check -p codex-tui --release` and was
able to repro, and verified that this fixed it
2026-04-13 21:43:33 +01:00
pakrym-oai
ac82443d07 Use AbsolutePathBuf in skill loading and codex_home (#17407)
Helps with FS migration later
2026-04-13 10:26:51 -07:00
Eric Traut
313ad29ad7 Fix TUI compaction item replay (#17657)
Problem: PR #17601 updated context-compaction replay to call a new
ChatWidget handler, but the handler was never implemented, breaking
codex-tui compilation on main.

Solution: Render context-compaction replay through the existing
info-message path, preserving the intended `Context compacted` UI marker
without adding a one-off handler.
2026-04-13 09:20:10 -07:00
Eric Traut
7c797c6544 Suppress duplicate compaction and terminal wait events (#17601)
Addresses #17514

Problem: PR #16966 made the TUI render the deprecated context-compaction
notification, while v2 could also receive legacy unified-exec
interaction items alongside terminal-interaction notifications, causing
duplicate "Context compacted" and "Waited for background terminal"
messages.

Solution: Suppress deprecated context-compaction notifications and
legacy unified-exec interaction command items from the app-server v2
projection, and render canonical context-compaction items through the
existing TUI info-event path.
2026-04-13 08:59:19 -07:00
Eric Traut
370be363f1 Wrap status reset timestamps in narrow layouts (#17481)
Addresses #17453

Problem: /status rate-limit reset timestamps can be truncated in narrow
layouts, leaving users with partial times or dates.

Solution: Let narrow rate-limit rows drop the fixed progress bar to
preserve the percent summary, and wrap reset timestamps onto
continuation lines instead of truncating them.
2026-04-13 08:53:37 -07:00
Eric Traut
ce5ad7b295 Emit plan-mode prompt notifications for questionnaires (#17417)
Addresses #17252

Problem: Plan-mode clarification questionnaires used the generic
user-input notification type, so configs listening for plan-mode-prompt
did not fire when request_user_input waited for an answer.

Solution: Map request_user_input prompts to the plan-mode-prompt
notification and remove the obsolete user-input TUI notification
variant.
2026-04-13 08:52:14 -07:00
jif-oai
3f62b5cc61 fix: dedup compact (#17643) 2026-04-13 16:08:53 +01:00
starr-openai
d626dc3895 Run exec-server fs operations through sandbox helper (#17294)
## Summary
- run exec-server filesystem RPCs requiring sandboxing through a
`codex-fs` arg0 helper over stdin/stdout
- keep direct local filesystem execution for `DangerFullAccess` and
external sandbox policies
- remove the standalone exec-server binary path in favor of top-level
arg0 dispatch/runtime paths
- add sandbox escape regression coverage for local and remote filesystem
paths

## Validation
- `just fmt`
- `git diff --check`
- remote devbox: `cd codex-rs && bazel test --bes_backend=
--bes_results_url= //codex-rs/exec-server:all` (6/6 passed)

---------

Co-authored-by: Codex <noreply@openai.com>
2026-04-12 18:36:03 -07:00
Eric Traut
46ab9974dc Expose instruction sources (AGENTS.md) via app server (#17506)
Addresses #17498

Problem: The TUI derived /status instruction source paths from the local
client environment, which could show stale <none> output or incorrect
paths when connected to a remote app server.

Solution: Add an app-server v2 instructionSources snapshot to thread
start/resume/fork responses, default it to an empty list when older
servers omit it, and render TUI /status from that server-provided
session data.

Additional context: The app-server field is intentionally named
instructionSources rather than AGENTS.md-specific terminology because
the loaded instruction sources can include global instructions, project
AGENTS.md files, AGENTS.override.md, user-defined instruction files, and
future dynamic sources.
2026-04-12 15:50:12 -07:00
Eric Traut
470510174b Remove context status-line meter (#17420)
Addresses #17313

Problem: The visual context meter in the status line was confusing and
continued to draw negative feedback, and context reporting should remain
an explicit opt-in rather than part of the default footer.

Solution: Remove the visual meter, restore opt-in context remaining/used
percentage items that explicitly say "Context", keep existing
context-usage configs working as a hidden alias, and update the setup
text and snapshots.
2026-04-12 15:42:09 -07:00
Felipe Coury
0393a485ed feat(tui): add reverse history search to composer (#17550)
## Problem

The TUI had shell-style Up/Down history recall, but `Ctrl+R` did not
provide the reverse incremental search workflow users expect from
shells. Users needed a way to search older prompts without immediately
replacing the current draft, and the interaction needed to handle async
persistent history, repeated navigation keys, duplicate prompt text,
footer hints, and preview highlighting without making the main composer
file even harder to review.


https://github.com/user-attachments/assets/5165affd-4c9a-46e9-adbd-89088f5f7b6b

<img width="1227" height="722" alt="image"
src="https://github.com/user-attachments/assets/8bc83289-eeca-47c7-b0c3-8975101901af"
/>

## Mental model

`Ctrl+R` opens a temporary search session owned by the composer. The
footer line becomes the search input, the composer body previews the
current match only after the query has text, and `Enter` accepts that
preview as an editable draft while `Esc` restores the draft that existed
before search started. The history layer provides a combined offset
space over persistent and local history, but search navigation exposes
unique prompt text rather than every physical history row.

## Non-goals

This change does not rewrite stored history, change normal Up/Down
browsing semantics, add fuzzy matching, or add persistent metadata for
attachments in cross-session history. Search deduplication is
deliberately scoped to the active Ctrl+R search session and uses exact
prompt text, so case, whitespace, punctuation, and attachment-only
differences are not normalized.

## Tradeoffs

The implementation keeps search state in the existing composer and
history state machines instead of adding a new cross-module controller.
That keeps ownership local and testable, but it means the composer still
coordinates visible search status, draft restoration, footer rendering,
cursor placement, and match highlighting while `ChatComposerHistory`
owns traversal, async fetch continuation, boundary clamping, and
unique-result caching. Unique-result caching stores cloned
`HistoryEntry` values so known matches can be revisited without cache
lookups; this is simple and robust for interactive search sizes, but it
is not a global history index.

## Architecture

`ChatComposer` detects `Ctrl+R`, snapshots the current draft, switches
the footer to `FooterMode::HistorySearch`, and routes search-mode keys
before normal editing. Query edits call `ChatComposerHistory::search`
with `restart = true`, which starts from the newest combined-history
offset. Repeated `Ctrl+R` or Up searches older; Down searches newer
through already discovered unique matches or continues the scan.
Persistent history entries still arrive asynchronously through
`on_entry_response`, where a pending search either accepts the response,
skips a duplicate, or requests the next offset.

The composer-facing pieces now live in
`codex-rs/tui/src/bottom_pane/chat_composer/history_search.rs`, leaving
`chat_composer.rs` responsible for routing and rendering integration
instead of owning every search helper inline.
`codex-rs/tui/src/bottom_pane/chat_composer_history.rs` remains the
owner of stored history, combined offsets, async fetch state, boundary
semantics, and duplicate suppression. Match highlighting is computed
from the current composer text while search is active and disappears
when the match is accepted.

## Observability

There are no new logs or telemetry. The practical debug path is state
inspection: `ChatComposer.history_search` tells whether the footer query
is idle, searching, matched, or unmatched; `ChatComposerHistory.search`
tracks selected raw offsets, pending persistent fetches, exhausted
directions, and unique match cache state. If a user reports skipped or
repeated results, first inspect the exact stored prompt text, the
selected offset, whether an async persistent response is still pending,
and whether a query edit restarted the search session.

## Tests

The change is covered by focused `codex-tui` unit tests for opening
search without previewing the latest entry, accepting and canceling
search, no-match restoration, boundary clamping, footer hints,
case-insensitive highlighting, local duplicate skipping, and persistent
duplicate skipping through async responses. Snapshot coverage captures
the footer-mode visual changes. Local verification used `just fmt`,
`cargo test -p codex-tui history_search`, `cargo test -p codex-tui`, and
`just fix -p codex-tui`.
2026-04-12 19:32:19 -03:00
Ahmed Ibrahim
d840b247d7 Mirror user text into realtime (#17520)
- Let typed user messages submit while realtime is active and mirror
accepted text into the realtime text stream.
- Add integration coverage and snapshot for outbound realtime text.
2026-04-12 15:03:14 -07:00
Eric Traut
7a6266323c Restore codex-tui resume hint on exit (#17415)
Addresses #17303

Problem: The standalone codex-tui entrypoint only printed token usage on
exit, so resumable sessions could omit the codex resume footer even when
thread metadata was available.

Solution: Format codex-tui exit output from AppExitInfo so it includes
the same resume hint as the main CLI and reports fatal exits
consistently.
2026-04-11 15:46:54 -07:00
Eric Traut
1e27028360 Clear /ps after /stop (#17416)
Addresses #17311

Problem: `/stop` stops background terminals, but `/ps` can still show
stale entries because the TUI process cache is cleared only after later
exec end events arrive.

Solution: Clear the TUI's tracked unified exec process list and footer
immediately when `/stop` submits background terminal cleanup.
2026-04-11 15:45:58 -07:00
Eric Traut
3b948d9dd8 Support prolite plan type (#17419)
Addresses #17353

Problem: Codex rate-limit fetching failed when the backend returned the
new `prolite` subscription plan type.

Solution: Add `prolite` to the backend/account/auth plan mappings, keep
unknown WHAM plan values decodable, and regenerate app-server plan
schemas.
2026-04-11 13:58:16 -07:00
Eric Traut
51d58c56d5 Handle closed TUI input stream as shutdown (#17430)
Addresses #17276

Problem: Closing the terminal while the TUI input stream is pending
could leave the app outside the normal shutdown path, which is risky
when an approval prompt is active.

Solution: Treat a closed TUI input stream as ShutdownFirst so existing
thread shutdown behavior cancels pending work and approvals before exit.
2026-04-11 09:02:05 -07:00
Felipe Coury
0bdeab330b fix(tui): recall accepted slash commands locally (#17336)
# TL;DR

- Adds recognized slash commands to the TUI's local in-session recall
history.
- This is the MVP of the whole feature: it keeps slash-command recall
local only: nothing is written to persistent history, app-server
history, or core history storage.
- Treats slash commands like submitted text once they parse as a known
built-in command, regardless of whether command dispatch later succeeds.

# Problem

Slash commands are handled outside the normal message submission path,
so they could clear the composer without becoming part of the local
Up-arrow recall list. That made command-heavy workflows awkward: after
running `/diff`, `/rename Better title`, `/plan investigate this`, or
even a valid command that reports a usage error, users had to retype the
command instead of recalling and editing it like a normal prompt.

The goal of this PR is to make slash commands feel like submitted input
inside the current TUI session while keeping the change deliberately
local. This is not persistent history yet; it only affects the
composer's in-memory recall behavior.

# Mental model

The composer owns draft state and local recall. When slash input parses
as a recognized built-in command, the composer stages the submitted
command text before returning `InputResult::Command` or
`InputResult::CommandWithArgs`. `ChatWidget` then dispatches the command
and records the staged entry once dispatch returns to the input-result
path.

Command-name recognition is the only validation before local recall. A
valid slash command is recallable whether it succeeds, fails with a
usage error, no-ops, is unavailable while a task is running, or is
skipped by command-specific logic. An unrecognized slash command is
different: it is restored as a draft, surfaces the existing
unrecognized-command message, and is not added to recall.

Bare commands recalled from typed text use the trimmed submitted draft.
Commands selected from the popup record the canonical command text, such
as `/diff`, rather than the partial filter text the user typed. Inline
commands with arguments keep the original command invocation available
locally even when their arguments are later prepared through the normal
submission pipeline.

# Non-goals

Persisting slash commands across sessions is intentionally out of scope.
This change does not modify app-server history, core history storage,
protocol events, or message submission semantics.

This does not change command availability, command side effects, popup
filtering, command parsing, or the semantics of unsupported commands. It
only changes whether recognized slash-command invocations are available
through local Up-arrow recall after the user submits them.

# Tradeoffs

The main tradeoff is that recall is based on command recognition, not
command outcome. This intentionally favors a simpler user model: if the
TUI accepted the input as a slash command, the user can recall and edit
that input just like plain text. That means valid-but-unsuccessful
invocations such as usage errors are recallable, which is useful when
the next action is usually to edit and retry.

The previous accept/reject design required command dispatch to report a
boolean outcome, which made the dispatcher API noisier and forced every
branch to decide history behavior. This version keeps the dispatch APIs
as side-effect-only methods and localizes history recording to the
slash-command input path.

Inline command handling still avoids double-recording by preparing
inline arguments without using the normal message-submission history
path. The staged slash-command entry remains the single local recall
record for the command invocation.

# Architecture

`ChatComposer` stages a pending `HistoryEntry` when recognized
slash-command input is promoted into an input result. The pending entry
mirrors the existing local history payload shape so recall can restore
text elements, local images, remote images, mention bindings, and
pending paste state when those are present.

`BottomPane` exposes a narrow method for recording that staged command
entry because it owns the composer. `ChatWidget` records the staged
entry after dispatching a recognized command from the input-result
match. Valid commands rejected before they reach `ChatWidget`, such as
commands unavailable while a task is running, are staged and recorded in
the composer path that detects the rejection.

Slash-command dispatch itself now lives in
`chatwidget/slash_dispatch.rs` so the behavior is reviewable without
adding more weight to `chatwidget.rs`. The extraction is
behavior-preserving: the dispatch match arms stay intact, while the
input flow in `chatwidget.rs` remains the single place that connects
submitted slash-command input to dispatch.

# Observability

There is no new logging because this is a local UI recall behavior and
the result is directly visible through Up-arrow recall. The practical
debug path is to trace Enter through
`ChatComposer::try_dispatch_bare_slash_command`,
`ChatComposer::try_dispatch_slash_command_with_args`, or popup Enter/Tab
handling, then confirm the recognized command is staged before dispatch
and recorded exactly once afterward.

If a valid command unexpectedly does not appear in recall, check whether
the input path staged slash history before clearing the composer and
whether it used the `ChatWidget` slash-dispatch wrapper. If an
unrecognized command unexpectedly appears in recall, check the parser
branch that should restore the draft instead of staging history.

# Tests

Composer-level tests cover staging and recording for a bare typed slash
command, a popup-selected command, and an inline command with arguments.

Chat-widget tests cover valid commands being recallable after normal
dispatch, inline dispatch, usage errors, task-running unavailability,
no-op stub dispatch, and command-specific skip behavior such as `/init`
when an instructions file already exists. They also cover the negative
case: unrecognized slash commands are not added to local recall.
2026-04-11 12:40:08 -03:00
ningyi-oai
be13f03c39 Pass turn id with feedback uploads (#17314)
## Summary
- Add an optional `tags` dictionary to feedback upload params.
- Capture the active app-server turn id in the TUI and submit it as
`tags.turn_id` with `/feedback` uploads.
- Merge client-provided feedback tags into Sentry feedback tags while
preserving reserved system fields like `thread_id`, `classification`,
`cli_version`, `session_source`, and `reason`.

## Behavior / impact
Existing feedback upload callers remain compatible because `tags` is
optional and nullable. The wire shape is still a normal JSON object /
TypeScript dictionary, so adding future feedback metadata will not
require a new top-level protocol field each time. This change only adds
feedback metadata for Codex CLI/TUI uploads; it does not affect existing
pipelines, DAGs, exports, or downstream consumers unless they choose to
read the new `turn_id` feedback tag.

## Tests
- `cargo fmt -- --config imports_granularity=Item` passed; stable
rustfmt warned that `imports_granularity` is nightly-only.
- `cargo run -p codex-app-server-protocol --bin write_schema_fixtures`
- `cargo test -p codex-feedback
upload_tags_include_client_tags_and_preserve_reserved_fields`
- `cargo test -p codex-app-server-protocol
schema_fixtures_match_generated`
- `cargo test -p codex-tui build_feedback_upload_params`
- `cargo test -p codex-tui
live_app_server_turn_started_sets_feedback_turn_id`
- `cargo check -p codex-app-server --tests`
- `git diff --check`

---------

Co-authored-by: Codex <noreply@openai.com>
2026-04-11 00:23:50 -07:00
Eric Traut
e9e7ef3d36 Fix thread/list cwd filtering for Windows verbatim paths (#17414)
Addresses #17302

Problem: `thread/list` compared cwd filters with raw path equality, so
`resume --last` could miss Windows sessions when the saved cwd used a
verbatim path form and the current cwd did not.

Solution: Normalize cwd comparisons through the existing path comparison
utilities before falling back to direct equality, and add Windows
regression coverage for verbatim paths. I made this a general utility
function and replaced all of the duplicated instance of it across the
code base.
2026-04-10 23:08:02 -07:00
Eric Traut
66e13efd9c TUI: enforce core boundary (#17399)
Problem: The TUI still depended on `codex-core` directly in a number of
places, and we had no enforcement from keeping this problem from getting
worse.

Solution: Route TUI core access through
`codex-app-server-client::legacy_core`, add CI enforcement for that
boundary, and re-export this legacy bridge inside the TUI as
`crate::legacy_core` so the remaining call sites stay readable. There is
no functional change in this PR — just changes to import targets.

Over time, we can whittle away at the remaining symbols in this legacy
namespace with the eventual goal of removing them all. In the meantime,
this linter rule will prevent us from inadvertently importing new
symbols from core.
2026-04-10 20:25:31 -07:00
Won Park
37aac89a6d representing guardian review timeouts in protocol types (#17381)
## Summary

- Add `TimedOut` to Guardian/review carrier types:
  - `ReviewDecision::TimedOut`
  - `GuardianAssessmentStatus::TimedOut`
  - app-server v2 `GuardianApprovalReviewStatus::TimedOut`
- Regenerate app-server JSON/TypeScript schemas for the new wire shape.
- Wire the new status through core/app-server/TUI mappings with
conservative fail-closed handling.
- Keep `TimedOut` non-user-selectable in the approval UI.

**Does not change runtime behavior yet; emitting `TimeOut` and
parent-model timeout messaging will come in followup PRs**
2026-04-10 20:02:33 -07:00
viyatb-oai
b114781495 fix(permissions): fix symlinked writable roots in sandbox permissions (#15981)
## Summary
- preserve logical symlink paths during permission normalization and
config cwd handling
- bind real targets for symlinked readable/writable roots in bwrap and
remap carveouts and unreadable roots there
- add regressions for symlinked carveouts and nested symlink escape
masking

## Root cause
Permission normalization canonicalized symlinked writable roots and cwd
to their real targets too early. That drifted policy checks away from
the logical paths the sandboxed process can actually address, while
bwrap still needed the real targets for mounts. The mismatch caused
shell and apply_patch failures on symlinked writable roots.

## Impact
Fixes #15781.

Also fixes #17079:
- #17079 is the protected symlinked carveout side: bwrap now binds the
real symlinked writable-root target and remaps carveouts before masking.

Related to #15157:
- #15157 is the broader permission-check side of this path-identity
problem. This PR addresses the shared logical-vs-canonical normalization
issue, but the reported Darwin prompt behavior should be validated
separately before auto-closing it.

This should also fix #14672, #14694, #14715, and #15725:
- #14672, #14694, and #14715 are the same Linux
symlinked-writable-root/bwrap family as #15781.
- #15725 is the protected symlinked workspace path variant; the PR
preserves the protected logical path in policy space while bwrap applies
read-only or unreadable treatment to the resolved target so
file-vs-directory bind mismatches do not abort sandbox setup.

## Notes
- Added Linux-only regressions for symlinked writable ancestors and
protected symlinked directory targets, including nested symlink escape
masking without rebinding the escape target writable.

---------

Co-authored-by: Codex <noreply@openai.com>
2026-04-10 17:00:58 -07:00
Shijie Rao
930e5adb7e Revert "Option to Notify Workspace Owner When Usage Limit is Reached" (#17391)
Reverts openai/codex#16969

#sev3-2026-04-10-accountscheckversion-500s-for-openai-workspace-7300
2026-04-10 23:33:13 +00:00
Owen Lin
a3be74143a fix(guardian, app-server): introduce guardian review ids (#17298)
## Description

This PR introduces `review_id` as the stable identifier for guardian
reviews and exposes it in app-server `item/autoApprovalReview/started`
and `item/autoApprovalReview/completed` events.

Internally, guardian rejection state is now keyed by `review_id` instead
of the reviewed tool item ID. `target_item_id` is still included when a
review maps to a concrete thread item, but it is no longer overloaded as
the review lifecycle identifier.

## Motivation

We'd like to give users the ability to preempt a guardian review while
it's running (approve or decline).

However, we can't implement the API that allows the user to override a
running guardian review because we didn't have a unique `review_id` per
guardian review. Using `target_item_id` is not correct since:
- with execve reviews, there can be multiple execve calls (and therefore
guardian reviews) per shell command
- with network policy reviews, there is no target item ID

The PR that actually implements user overrides will use `review_id` as
the stable identifier.
2026-04-10 16:21:02 -07:00
Abhinav
7999b0f60f Support clear SessionStart source (#17073)
## Motivation

The `SessionStart` hook already receives `startup` and `resume` sources,
but sessions created from `/clear` previously looked like normal startup
sessions. This makes it impossible for hook authors to distinguish
between these with the matcher.

## Summary

- Add `InitialHistory::Cleared` so `/clear`-created sessions can be
distinguished from ordinary startup sessions.
- Add `SessionStartSource::Clear` and wire it through core, app-server
thread start params, and TUI clear-session flow.
- Update app-server protocol schemas, generated TypeScript, docs, and
related tests.


https://github.com/user-attachments/assets/9cae3cb4-41c7-4d06-b34f-966252442e5c
2026-04-10 16:05:21 -07:00
Abhinav
87b9275fff [codex] Improve hook status rendering (#17266)
# Motivation

Make hook display less noisy and more useful by keeping transient hook
activity out of permanent history unless there is useful output,
preserving visibility for meaningful hook work, and making completed
hook severity easier to scan.

Also addresses some of the concerns in
https://github.com/openai/codex/issues/15497

# Changes

## Demo


https://github.com/user-attachments/assets/9d8cebd4-a502-4c95-819c-c806c0731288

Reverse spec for the behavior changes in this branch:

## Hook Lifecycle Rendering
- Hook start events no longer write permanent history rows like `Running
PreToolUse hook`.
- Running hooks now render in a dedicated live hook area above the
composer. It's similar to the active cell we use for tool calls but its
a separate lane.
- Running hook rows use the existing animation setting.

## Hook Reveal Timing
- We wait 300ms before showing running hook rows and linger for up to
600ms once visible.
- This is so fast hooks don't flash a transient `Running hook` row
before user can read it every time.
- If a fast hook completes with meaningful output, only the completed
hook result is written to history.
- If a fast hook completes successfully with no output, it leaves no
visible trace.

## Completed Hook Output
- Completed hooks with output are sticky, for example `• SessionStart
hook (completed)`.
- Hook output entries are rendered under that row with stable prefixes:
`warning:`, `stop:`, `feedback:`, `hook context:`, and `error:`.
- Blocked hooks show feedback entries, for example `• PreToolUse hook
(blocked)` followed by `feedback: ...`.
- Failed hooks show error entries, for example `• PostToolUse hook
(failed)` followed by `error: ...`.
- Stopped hooks show stop entries and remain visually treated as
non-success.

## Parallel Hook Behavior
- Multiple simultaneously running hooks can be tracked in one live hook
cell.
- Adjacent running hooks with the same hook event name and same status
message collapse into a count, for example `• Running 3 PreToolUse
hooks: checking command policy`.
- Running hooks with different event names or different status messages
remain separate rows.

## Hook Run Identity
- `PreToolUse` and `PostToolUse` hook run IDs now include the tool call
ID which prevents concurrent tool-use hooks from sharing a run ID and
clobbering each other in the UI.
- This ID scoping applies to tool-use hooks only; other hook event types
keep their existing run identity behavior.

## App-Server Hook Notifications
- App-server `HookStarted` and `HookCompleted` notifications use the
same live hook rendering path as core hook events.
- `UserPromptSubmit` hook notifications now render through the same
completed hook output format, including warning and stop entries.
2026-04-10 14:05:47 -07:00
canvrno-oai
aac1e74cd5 Add thread title to configurable TUI status line (#17187)
- 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.
2026-04-10 13:24:07 -07:00
Ahmed Ibrahim
2e81eac004 Queue Realtime V2 response.create while active (#17306)
Builds on #17264.

- queues Realtime V2 `response.create` while an active response is open,
then flushes it after `response.done` or `response.cancelled`
- requests `response.create` after background agent final output and
steering acknowledgements
- adds app-server integration coverage for all `response.create` paths

Validation:
- `just fmt`
- `cargo check -p codex-app-server --tests`
- `git diff --check`
- CI green

---------

Co-authored-by: Codex <noreply@openai.com>
2026-04-10 09:09:13 -07:00
richardopenai
9f2a585153 Option to Notify Workspace Owner When Usage Limit is Reached (#16969)
## Summary
- Replace the manual `/notify-owner` flow with an inline confirmation
prompt when a usage-based workspace member hits a credits-depleted
limit.
- Fetch the current workspace role from the live ChatGPT
`accounts/check/v4-2023-04-27` endpoint so owner/member behavior matches
the desktop and web clients.
- Keep owner, member, and spend-cap messaging distinct so we only offer
the owner nudge when the workspace is actually out of credits.

## What Changed
- `backend-client`
- Added a typed fetch for the current account role from
`accounts/check`.
  - Mapped backend role values into a Rust workspace-role enum.
- `app-server` and protocol
  - Added `workspaceRole` to `account/read` and `account/updated`.
- Derived `isWorkspaceOwner` from the live role, with a fallback to the
cached token claim when the role fetch is unavailable.
- `tui`
  - Removed the explicit `/notify-owner` slash command.
- When a member is blocked because the workspace is out of credits, the
error now prompts:
- `Your workspace is out of credits. Request more from your workspace
owner? [y/N]`
  - Choosing `y` sends the existing owner-notification request.
- Choosing `n`, pressing `Esc`, or accepting the default selection
dismisses the prompt without sending anything.
- Selection popups now honor explicit item shortcuts, which is how the
`y` / `n` interaction is wired.

## Reviewer Notes
- The main behavior change is scoped to usage-based workspace members
whose workspace credits are depleted.
- Spend-cap reached should not show the owner-notification prompt.
- Owners and admins should continue to see `/usage` guidance instead of
the member prompt.
- The live role fetch is best-effort; if it fails, we fall back to the
existing token-derived ownership signal.

## Testing
- Manual verification
  - Workspace owner does not see the member prompt.
- Workspace member with depleted credits sees the confirmation prompt
and can send the nudge with `y`.
- Workspace member with spend cap reached does not see the
owner-notification prompt.

### Workspace member out of usage

https://github.com/user-attachments/assets/341ac396-eff4-4a7f-bf0c-60660becbea1

### Workspace owner
<img width="1728" height="1086" alt="Screenshot 2026-04-09 at 11 48
22 AM"
src="https://github.com/user-attachments/assets/06262a45-e3fc-4cc4-8326-1cbedad46ed6"
/>
2026-04-09 21:15:17 -07:00
Felipe Coury
ef330eff6d feat(tui): Ctrl+O copy hotkey and harden copy-as-markdown behavior (#16966)
## TL;DR

- New `Ctrl+O` shortcut on top of the existing `/copy` command, allowing
users to copy the latest agent response without having to cancel a plan
or type `/copy`
- Copy server clipboard to the client over SSH (OSC 52)
- Fixes linux copy behavior: a clipboard handle has to be kept alive
while the paste happens for the contents to be preserved
- Uses arboard as primary mechanism on Windows, falling back to
PowerShell copy clipboard function
- Works with resumes, rolling back during a session, etc.

Tested on macOS, Linux/X11, Windows WSL2, Windows cmd.exe, Windows
PowerShell, Windows VSCode PowerShell, Windows VSCode WSL2, SSH (macOS
-> macOS).

## Problem

The TUI's `/copy` command was fragile. It relied on a single
`last_copyable_output` field that was bluntly cleared on every rollback
and thread reconfiguration, making copied content unavailable after
common operations like backtracking. It also had no keyboard shortcut,
requiring users to type `/copy` each time. The previous clipboard
backend mixed platform selection policy with low-level I/O in a way that
was hard to test, and it did not keep the Linux clipboard owner alive —
meaning pasted content could vanish once the process that wrote it
dropped its `arboard::Clipboard`.

This addresses the text-copy failure modes reported in #12836, #15452,
and #15663: native Linux clipboard access failing in remote or
unreachable-display environments, copy state going blank even after
visible assistant output, and local Linux X11 reporting success while
leaving the clipboard empty.

## Shortcut rationale

The copy hotkey is `Ctrl+O` rather than `Alt+C` because Alt/Option
combinations are not delivered consistently by macOS terminal emulators.
Terminal.app and iTerm2 can treat Option as text input or as a
configurable Meta/Esc prefix, and Option+C may be consumed or
transformed before the TUI sees an `Alt+C` key event. `Ctrl+O` is a
stable control-key chord in Terminal.app, iTerm2, SSH, and the existing
cross-platform terminal stack.

## Mental model

Agent responses are now tracked as a bounded, ordinal-indexed history
(`agent_turn_markdowns: Vec<AgentTurnMarkdown>`) rather than a single
nullable string. Each completed agent turn appends an entry keyed by its
ordinal (the number of user turns seen so far). Rollbacks pop entries
whose ordinal exceeds the remaining turn count, then use the visible
transcript cells as a best-effort fallback if the ordinal history no
longer has a surviving entry. This means `/copy` and `Ctrl+O` reflect
the most recent surviving agent response after a backtrack, instead of
going blank.

The clipboard backend was rewritten as `clipboard_copy.rs` with a
strategy-injection design: `copy_to_clipboard_with` accepts closures for
the OSC 52, arboard, and WSL PowerShell paths, making the selection
logic fully unit-testable without touching real clipboards. On Linux,
the `Clipboard` handle is returned as a `ClipboardLease` stored on
`ChatWidget`, keeping X11/Wayland clipboard ownership alive for the
lifetime of the TUI. When native copy fails under WSL, the backend now
tries the Windows clipboard through PowerShell before falling back to
OSC 52.

## Non-goals

- This change does not introduce rich-text (HTML) clipboard support; the
copied content is raw markdown.
- It does not add a paste-from-history picker or multi-entry clipboard
ring.
- WSL support remains a best-effort fallback, not a new configuration
surface or guarantee for every terminal/host combination.

## Tradeoffs

- **Bounded history (256 entries)**: `MAX_AGENT_COPY_HISTORY` caps
memory. For sessions with thousands of turns this silently drops the
oldest entries. The cap is generous enough for realistic sessions.
- **`saw_copy_source_this_turn` flag**: Prevents double-recording when
both `AgentMessage` and `TurnComplete.last_agent_message` fire for the
same turn. The flag is reset on turn start and on turn complete,
creating a narrow window where a race between the two events could
theoretically skip recording. In practice the protocol delivers them
sequentially.
- **Transcript fallback on rollback**:
`last_agent_markdown_from_transcript` walks the visible transcript cells
to reconstruct plain text when the ordinal history has been fully
truncated. This path uses `AgentMessageCell::plain_text()` which joins
rendered spans, so it reconstructs display text rather than the original
raw markdown. It keeps visible text copyable after rollback, but
responses with markdown-specific syntax can diverge from the original
source.
- **Clipboard fallback ordering**: SSH still uses OSC 52 exclusively
because native/PowerShell clipboard access would target the wrong
machine. Local sessions try native clipboard first, then WSL PowerShell
when running under WSL, then OSC 52. This adds one process-spawn
fallback for WSL users but keeps the normal desktop and SSH paths
simple.

## Architecture

```
chatwidget.rs
├── agent_turn_markdowns: Vec<AgentTurnMarkdown>  // ordinal-indexed history
├── last_agent_markdown: Option<String>            // always == last entry's markdown
├── completed_turn_count: usize                    // incremented when user turns enter history
├── saw_copy_source_this_turn: bool                // dedup guard
├── clipboard_lease: Option<ClipboardLease>        // keeps Linux clipboard owner alive
│
├── record_agent_markdown(&str)                    // append/update history entry
├── truncate_agent_turn_markdowns_to_turn_count()  // rollback support
├── copy_last_agent_markdown()                     // public entry point (slash + hotkey)
└── copy_last_agent_markdown_with(fn)              // testable core

clipboard_copy.rs
├── copy_to_clipboard(text) -> Result<Option<ClipboardLease>>
├── copy_to_clipboard_with(text, ssh, wsl, osc52_fn, arboard_fn, wsl_fn)
├── ClipboardLease { _clipboard on linux }
├── arboard_copy(text)          // platform-conditional native clipboard path
├── wsl_clipboard_copy(text)    // WSL PowerShell fallback
├── osc52_copy(text)            // /dev/tty -> stdout fallback
├── SuppressStderr              // macOS stderr redirect guard
├── is_ssh_session()
└── is_wsl_session()

app_backtrack.rs
├── last_agent_markdown_from_transcript()  // reconstruct from visible cells
└── truncate call sites in trim/apply_confirmed_rollback
```

## Observability

- `tracing::warn!` on native clipboard failure before OSC 52 fallback.
- `tracing::debug!` on `/dev/tty` open/write failure before stdout
fallback.
- History cell messages: "Copied last message to clipboard", "Copy
failed: {error}", "No agent response to copy" appear in the TUI
transcript.

## Tests

- `clipboard_copy.rs`: Unit tests cover OSC 52 encoding roundtrip,
payload size rejection, writer output, SSH-only OSC52 routing, non-WSL
native-to-OSC52 fallback, WSL native-to-PowerShell fallback, WSL
PowerShell-to-OSC52 fallback, and all-error reporting via strategy
injection.
- `chatwidget/tests/slash_commands.rs`: Updated existing `/copy` tests
to use `last_agent_markdown_text()` accessor. Added coverage for the
Linux clipboard lease lifecycle, missing
`TurnComplete.last_agent_message` fallback through completed assistant
items, replayed legacy agent messages, stale-output prevention after
rollback, and the `Ctrl+O` no-output hotkey path.
- `app_backtrack.rs`: Added
`agent_group_count_ignores_context_compacted_marker` verifying that
info-event cells don't inflate the agent group count.

---------

Co-authored-by: Felipe Coury <felipe.coury@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 18:10:38 -03:00
neil-oai
a92a5085bd Forward app-server turn clientMetadata to Responses (#16009)
## Summary
App-server v2 already receives turn-scoped `clientMetadata`, but the
Rust app-server was dropping it before the outbound Responses request.
This change keeps the fix lightweight by threading that metadata through
the existing turn-metadata path rather than inventing a new transport.

## What we're trying to do and why
We want turn-scoped metadata from the app-server protocol layer,
especially fields like Hermes/GAAS run IDs, to survive all the way to
the actual Responses API request so it is visible in downstream
websocket request logging and analytics.

The specific bug was:
- app-server protocol uses camelCase `clientMetadata`
- Responses transport already has an existing turn metadata carrier:
`x-codex-turn-metadata`
- websocket transport already rewrites that header into
`request.request_body.client_metadata["x-codex-turn-metadata"]`
- but the Rust app-server never parsed or stored `clientMetadata`, so
nothing from the app-server request was making it into that existing
path

This PR fixes that without adding a new header or a second metadata
channel.

## How we did it
### Protocol surface
- Add optional `clientMetadata` to v2 `TurnStartParams` and
`TurnSteerParams`
- Regenerate the JSON schema / TypeScript fixtures
- Update app-server docs to describe the field and its behavior

### Runtime plumbing
- Add a dedicated core op for app-server user input carrying turn-scoped
metadata: `Op::UserInputWithClientMetadata`
- Wire `turn/start` and `turn/steer` through that op / signature path
instead of dropping the metadata at the message-processor boundary
- Store the metadata in `TurnMetadataState`

### Transport behavior
- Reuse the existing serialized `x-codex-turn-metadata` payload
- Merge the new app-server `clientMetadata` into that JSON additively
- Do **not** replace built-in reserved fields already present in the
turn metadata payload
- Keep websocket behavior unchanged at the outer shape level: it still
sends only `client_metadata["x-codex-turn-metadata"]`, but that JSON
string now contains the merged fields
- Keep HTTP fallback behavior unchanged except that the existing
`x-codex-turn-metadata` header now includes the merged fields too

### Request shape before / after
Before, a websocket `response.create` looked like:
```json
{
  "type": "response.create",
  "client_metadata": {
    "x-codex-turn-metadata": "{\"session_id\":\"...\",\"turn_id\":\"...\"}"
  }
}
```
Even if the app-server caller supplied `clientMetadata`, it was not
represented there.

After, the same request shape is preserved, but the serialized payload
now includes the new turn-scoped fields:
```json
{
  "type": "response.create",
  "client_metadata": {
    "x-codex-turn-metadata": "{\"session_id\":\"...\",\"turn_id\":\"...\",\"fiber_run_id\":\"fiber-start-123\",\"origin\":\"gaas\"}"
  }
}
```

## Validation
### Targeted tests added / updated
- protocol round-trip coverage for `clientMetadata` on `turn/start` and
`turn/steer`
- protocol round-trip coverage for `Op::UserInputWithClientMetadata`
- `TurnMetadataState` merge test proving client metadata is added
without overwriting reserved built-in fields
- websocket request-shape test proving outbound `response.create`
contains merged metadata inside
`client_metadata["x-codex-turn-metadata"]`
- app-server integration tests proving:
- `turn/start` forwards `clientMetadata` into the outbound Responses
request path
  - websocket warmup + real turn request both behave correctly
  - `turn/steer` updates the follow-up request metadata

### Commands run
- `just write-app-server-schema`
- `cargo test -p codex-app-server-protocol`
- `cargo test -p codex-protocol`
- `cargo test -p codex-core
turn_metadata_state_merges_client_metadata_without_replacing_reserved_fields
--lib`
- `cargo test -p codex-core --test all
responses_websocket_preserves_custom_turn_metadata_fields`
- `cargo test -p codex-app-server --test all client_metadata`
- `cargo test -p codex-app-server --test all
turn_start_forwards_client_metadata_to_responses_websocket_request_body_v2
-- --nocapture`
- `just fmt`
- `just fix -p codex-core -p codex-protocol -p codex-app-server-protocol
-p codex-app-server`
- `just fix -p codex-exec -p codex-tui-app-server`
- `just argument-comment-lint`

### Full suite note
`cargo test` in `codex-rs` still fails in:
-
`suite::v2::turn_interrupt::turn_interrupt_resolves_pending_command_approval_request`

I verified that same failure on a clean detached `HEAD` worktree with an
isolated `CARGO_TARGET_DIR`, so it is not caused by this patch.
2026-04-09 11:52:37 -07:00
mom-oai
25a0f6784d [codex] Show ctrl + t hint on truncated exec output in TUI (#17076)
## 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`
2026-04-09 11:01:30 -07:00
jif-oai
12f0e0b0eb chore: merge name and title (#17116)
Merge title and name concept to leverage the sqlite title column and
have more efficient queries

---------

Co-authored-by: Codex <noreply@openai.com>
2026-04-09 18:44:26 +01:00
Eric Traut
598d6ff056 Render statusline context as a meter (#17170)
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"
/>
2026-04-09 07:52:07 -07:00
jif-oai
9f6f2c84c1 feat: advanced announcements per OS and plans (#17226)
Support things like
```
  [[announcements]]
  content = "custom message"
  from_date = "2026-04-09"
  to_date = "2026-06-01"
  target_app = "cli"
  target_plan_types = ["pro"]
  target_oses = ["macos"]
  version_regex = "..." # add version of the patch
  ```
2026-04-09 15:17:06 +01:00
jif-oai
6c5471feb2 feat: /resume per ID/name (#17222)
Support `/resume 00000-0000-0000-00000000` from the TUI (equivalent for
the name)
2026-04-09 14:21:27 +01:00
Eric Traut
23f4cd8459 Skip update prompts for source builds (#17186)
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.
2026-04-08 22:26:05 -07:00
Eric Traut
6dc5391c7c Add TUI notification condition config (#17175)
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.
2026-04-08 21:50:02 -07:00
Ahmed Ibrahim
2f9090be62 Add realtime voice selection (#17176)
- Add realtime voice selection for realtime/start.
- Expose the supported v1/v2 voice lists and cover explicit, configured,
default, and invalid voice paths.
2026-04-08 20:19:15 -07:00
Ahmed Ibrahim
4c2a1ae31b Move default realtime prompt into core (#17165)
- 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>
2026-04-08 19:34:40 -07:00
Eric Traut
36586eafed Fix stale thread-name resume lookups (#16646)
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.
2026-04-08 18:51:29 -07:00
Eric Traut
4dca906e19 Support Warp for OSC 9 notifications (#17174)
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.
2026-04-08 18:49:31 -07:00
maja-openai
dcbc91fd39 Update guardian output schema (#17061)
## Summary
- Update guardian output schema to separate risk, authorization,
outcome, and rationale.
- Feed guardian rationale into rejection messages.
- Split the guardian policy into template and tenant-config sections.

## Validation
- `cargo test -p codex-core mcp_tool_call`
- `env -u CODEX_SANDBOX_NETWORK_DISABLED INSTA_UPDATE=always cargo test
-p codex-core guardian::`

---------

Co-authored-by: Owen Lin <owen@openai.com>
2026-04-08 15:47:29 -07:00
Ahmed Ibrahim
19bd018300 Wire realtime WebRTC native media into Bazel (#17145)
- 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>
2026-04-08 15:15:55 -07:00
pakrym-oai
e4d6702b87 [codex] Support remote exec cwd in TUI startup (#17142)
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.
2026-04-08 13:09:28 -07:00
Owen Lin
e794457a59 fix(debug-config, guardian): fix /debug-config rendering and guardian… (#17138)
## 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.
2026-04-08 11:08:09 -07:00
Ahmed Ibrahim
d90a348870 Add WebRTC media transport to realtime TUI (#17058)
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>
2026-04-08 10:26:55 -07:00
Matthew Zeng
7b6486a145 [mcp] Support server-driven elicitations (#17043)
- [x] Enables MCP elicitation for custom servers, not just Codex Apps
- [x] Adds an RMCP service wrapper to preserve elicitation _meta
- [x] Round-trips response _meta for persist/approval choices
- [x] Updates TUI empty-schema elicitations into message-only approval
prompts
2026-04-08 10:18:58 -07:00
Eric Traut
5c95e4588e Fix TUI crash when resuming the current thread (#17086)
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.
2026-04-08 09:35:54 -07:00
Eric Traut
dc5feb916d Show global AGENTS.md in /status (#17091)
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.
2026-04-08 09:04:32 -07:00