Currently, our `npm publish` logic is failing.
There were a number of things that were merged recently that seemed to
contribute to this situation, though I think we have fixed most of them,
but this one stands out:
https://github.com/openai/codex/pull/10115
As best I can tell, we tried to fix the pnpm version to a specific hash,
but we did not do it consistently (though `shell-tool-mcp/package.json`
had it specified twice...), so for this PR, I ran:
```
$ git ls-files | grep package.json
codex-cli/package.json
codex-rs/responses-api-proxy/npm/package.json
package.json
sdk/typescript/package.json
shell-tool-mcp/package.json
```
and ensured that all of them now have this line:
```json
"packageManager": "pnpm@10.28.2+sha512.41872f037ad22f7348e3b1debbaf7e867cfd448f2726d9cf74c08f19507c31d2c8e7a11525b983febc2df640b5438dee6023ebb1f84ed43cc2d654d2bc326264"
```
I also went and deleted all of the `corepack` stuff that was added by
https://github.com/openai/codex/pull/10115.
If someone can explain why we need it and verify it does not break `npm
publish`, then we can bring it back.
## Summary
- guard onboarding key handling to ignore KeyEventKind::Release
- handle key events at the onboarding screen boundary to avoid
double-triggering widgets
## Related
- https://github.com/ratatui/ratatui/issues/347
## Testing
- cd codex-rs && just fmt
- cd codex-rs && cargo test -p codex-tui
- [x] Support `/apps` slash command to browse the apps in tui.
- [x] Support inserting apps to prompt using `$`.
- [x] Lots of simplification/renaming from connectors to apps.
## Summary
Add personality instructions so we can let users try it out, in tandem
with making it an experimental feature
## Testing
- [x] Tested locally
## Summary
Sets up an explicit Feature flag for `/personality`, so users can now
opt in to it via `/experimental`. #10114 also updates the config
## Testing
- [x] Tested locally
we can't use runfiles directory on Windows due to path lengths, so swap
to manifest strategy. Parsing the manifest is a bit complex and the
format is changing in Bazel upstream, so pull in the official Rust
library (via a small hack to make it importable...) and cleanup all the
associated logic to work cleanly in both bazel and cargo without extra
confusion
This updates the CI workflows for shell-tool-mcp to use the pnpm version
from package.json and print it in the build for verification.
I have read the CLA Document and I hereby sign the CLA
This PR adds a new `tui.notifications_method` config option that accepts
values of "auto", "osc9" and "bel". It defaults to "auto", which
attempts to auto-detect whether the terminal supports OSC 9 escape
sequences and falls back to BEL if not.
The PR also removes the inconsistent handling of notifications on
Windows when WSL was used.
# 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.
deprecate all old web search flags and aliases, including:
- `[features].web_search_request` and `[features].web_search_cached`
- `[tools].web_search`
- `[features].web_search`
slightly rework `legacy_usages` to enable pointing to non-features from
deprecated features; we need to point to `web_search` (not under
`[features]`) from things like `[features].web_search_cached` and
`[features].web_search_request`.
Added integration tests to confirm deprecation notice is shown on
explicit enablement and disablement of deprecated flags.
I needed to upgrade bazel one to get gnullvm artifacts and then noticed
monorepo had drifted forward. They should move in lockstep. Also 1.93
already shipped so we can try that instead.
## Summary
Overhaul the ask‑user‑questions TUI to support “Other/None” answers,
better notes handling, improved option selection
UX, and a safer submission flow with confirmation for unanswered
questions.
Multiple choice (number keys for quick selection, up/down or jk for
cycling through options):
<img width="856" height="169" alt="Screenshot 2026-01-27 at 7 22 29 PM"
src="https://github.com/user-attachments/assets/cabd1b0e-25e0-4859-bd8f-9941192ca274"
/>
Tab to add notes:
<img width="856" height="197" alt="Screenshot 2026-01-27 at 7 22 45 PM"
src="https://github.com/user-attachments/assets/a807db5e-e966-412c-af91-6edc60062f35"
/>
Freeform (also note enter tooltip is highlighted on last question to
indicate questions UI will be exited upon submission):
<img width="854" height="112" alt="Screenshot 2026-01-27 at 7 23 13 PM"
src="https://github.com/user-attachments/assets/2e7b88bf-062b-4b9f-a9da-c9d8c8a59643"
/>
Confirmation dialogue (submitting with unanswered questions):
<img width="854" height="126" alt="Screenshot 2026-01-27 at 7 23 29 PM"
src="https://github.com/user-attachments/assets/93965c8f-54ac-45bc-a660-9625bcd101f8"
/>
## Key Changes
- **Options UI refresh**
- Render options as numbered entries; allow number keys to select &
submit.
- Remove “Option X/Y” header and allow the question UI height to expand
naturally.
- Keep spacing between question, options, and notes even when notes are
visible.
- Hide the title line and render the question prompt in cyan **only when
uncommitted**.
- **“Other / None of the above” support**
- Wire `isOther` to add “None of the above”.
- Add guidance text: “Optionally, add details in notes (tab).”
- **Notes composer UX**
- Remove “Notes” heading; place composer directly under the selected
option.
- Preserve pending paste placeholders across question navigation and
after submission.
- Ctrl+C clears notes **only when the notes composer has focus**.
- Ctrl+C now triggers an immediate redraw so the clear is visible.
- **Committed vs uncommitted state**
- Introduce a unified `answer_committed` flag per question.
- Editing notes (including adding text or pastes) marks the answer
uncommitted.
- Changing the option highlight (j/k, up/down) marks the answer
uncommitted.
- Clearing options (Backspace/Delete) also clears pending notes.
- Question prompt turns cyan only when the answer is uncommitted.
- **Submission safety & confirmation**
- Only submit notes/freeform text once explicitly committed.
- Last-question submit with unanswered questions shows a confirmation
dialog.
- Confirmation options:
1. **Proceed** (default)
2. **Go back**
- Description reflects count: “Submit with N unanswered question(s).”
- Esc/Backspace in confirmation returns to first unanswered question.
- Ctrl+C in confirmation interrupts and exits the overlay.
- **Footer hints**
- Cyan highlight restored for “enter to submit answer” / “enter to
submit all”.
## Codex author
`codex fork 019c00ed-323a-7000-bdb5-9f9c5a635bd9`
Add a `.sqlite` database to be used to store rollout metatdata (and
later logs)
This PR is phase 1:
* Add the database and the required infrastructure
* Add a backfill of the database
* Persist the newly created rollout both in files and in the DB
* When we need to get metadata or a rollout, consider the `JSONL` as the
source of truth but compare the results with the DB and show any errors
**Summary**
- Up/Down input history now restores image attachments and text elements
for local entries.
- Composer history stores rich local entries (text + text elements +
local image paths) while persistent history remains text-only.
- Added tests to verify history recall rehydrates image placeholders and
attachments in both `tui` and `tui2`.
**Changes**
- `tui/src/bottom_pane/chat_composer_history.rs`: store `HistoryEntry`
(text + elements + image paths) for local history; adapt navigation +
tests.
- `tui2/src/bottom_pane/chat_composer_history.rs`: same as above.
- `tui/src/bottom_pane/chat_composer.rs`: record rich history entries
and restore them on Up/Down; update Ctrl+C history and tests.
- `tui2/src/bottom_pane/chat_composer.rs`: same as above.
web_search can now be updated per-turn, for things like changes to
sandbox policy.
`SandboxPolicy::DangerFullAccess` now sets web_search to `live`, and the
default is still `cached`.
Added integration tests.
Fixes#9361
## Context
Split out from #9059 per review:
https://github.com/openai/codex/pull/9059#issuecomment-3757859033
## Summary
The shortcut overlay renders different paste-image bindings on WSL
(Ctrl+Alt+V) vs non-WSL (Ctrl+V), which makes snapshot tests
non-deterministic when run under WSL.
## Changes
- Gate WSL detection behind `cfg(not(test))` so snapshot tests are
deterministic across environments.
- Add a focused unit test that still asserts the WSL-specific
paste-image binding.
## Testing
- `just fmt`
- `just fix -p codex-tui`
- `just fix -p codex-tui2`
- `cargo test -p codex-tui`
- `cargo test -p codex-tui2`
## Summary
Polishes the `request_user_input` TUI overlay
Question 1 (unanswered)
<img width="853" height="167" alt="Screenshot 2026-01-27 at 1 30 09 PM"
src="https://github.com/user-attachments/assets/3c305644-449e-4e8d-a47b-d689ebd8702c"
/>
Tab to add notes
<img width="856" height="198" alt="Screenshot 2026-01-27 at 1 30 25 PM"
src="https://github.com/user-attachments/assets/0d2801b0-df0c-49ae-85af-e6d56fc2c67c"
/>
Question 2 (unanswered)
<img width="854" height="168" alt="Screenshot 2026-01-27 at 1 30 55 PM"
src="https://github.com/user-attachments/assets/b3723062-51f9-49c9-a9ab-bb1b32964542"
/>
Ctrl+p or h to go back to q1 (answered)
<img width="853" height="195" alt="Screenshot 2026-01-27 at 1 31 27 PM"
src="https://github.com/user-attachments/assets/c602f183-1c25-4c51-8f9f-e565cb6bd637"
/>
Unanswered freeform
<img width="856" height="126" alt="Screenshot 2026-01-27 at 1 31 42 PM"
src="https://github.com/user-attachments/assets/7e3d9d8b-820b-4b9a-9ef2-4699eed484c5"
/>
## Key changes
- Footer tips wrap at tip boundaries (no truncation mid‑tip); footer
height scales to wrapped tips.
- Keep tooltip text as Esc: interrupt in all states.
- Make the full Tab: add notes tip cyan/bold when applicable; hide notes
UI by default.
- Notes toggling/backspace:
- Tab opens notes when an option is selected; Tab again clears notes and
hides the notes UI.
- Backspace in options clears the current selection.
- Backspace in empty notes closes notes and returns to options.
- Selection/answering behavior:
- Option questions highlight a default option but are not answered until
Enter.
- Enter no longer auto‑selects when there’s no selection (prevents
accidental answers).
- Notes submission can commit the selected option when present.
- Freeform questions require Enter with non‑empty text to mark answered;
drafts are not submitted unless committed.
- Unanswered cues:
- Skipped option questions count as unanswered.
- Unanswered question titles are highlighted for visibility.
- Typing/navigation in options:
- Typing no longer opens notes; notes are Tab‑only.
- j/k move option selection; h/l switch questions (Ctrl+n/Ctrl+p still
work).
## Tests
- Added unit coverage for:
- tip‑level wrapping
- focus reset when switching questions with existing drafts
- backspace clearing selection
- backspace closing empty notes
- typing in options does not open notes
- freeform draft submission gating
- h/l question navigation in options
- Updated snapshots, including narrow footer wrap.
## Why
These changes make the ask‑user‑question overlay:
- safer (no silent auto‑selection or accidental freeform submission),
- clearer (tips wrap cleanly and unanswered states stand out),
- more ergonomic (Tab explicitly controls notes; backspace acts like
undo/close).
## Codex author
`codex fork 019bfc3c-2c42-7982-9119-fee8b9315c2f`
---------
Co-authored-by: Ahmed Ibrahim <aibrahim@openai.com>
### Motivation
- Improve UX by making it explicit that `VISUAL`/`EDITOR` must be set
before launching Codex, not during a running session.
### Description
- Update the external editor error text in `codex-rs/tui/src/app.rs` to:
`"Cannot open external editor: set $VISUAL or $EDITOR before starting
Codex."` and run `just fmt` to apply formatting.
### Testing
- Ran `just fmt` successfully; attempted `cargo test -p codex-tui` but
it failed due to network errors when fetching git dependencies (tests
did not complete).
------
[Codex
Task](https://chatgpt.com/codex/tasks/task_i_6972c2c984948329b1a37d5c5839aff3)
### Motivation
- The local OAuth callback server returned a generic "Invalid OAuth
callback" on failures even when the query contained an
`error_description`, making it hard to debug OAuth failures.
### Description
- Update `codex-rs/rmcp-client/src/perform_oauth_login.rs` to surface
`error_description` values from the callback query in the HTTP response.
- Introduce a `CallbackOutcome` enum and change `parse_oauth_callback`
to return it, parsing `code`, `state`, and `error_description` from the
query string.
- Change `spawn_callback_server` to match on `CallbackOutcome` and
return `OAuth error: <description>` with a 400 status when
`error_description` is present, while preserving the existing success
and invalid flows.
- Use inline formatting for the error response string.
### Testing
- Ran `just fmt` in the `codex-rs` workspace to format changes
successfully.
- Ran `cargo test -p codex-rmcp-client` and all tests passed.
------
[Codex
Task](https://chatgpt.com/codex/tasks/task_i_6971aadc68d0832e93159efea8cd48a9)
## What?
- Render an MCP image output cell whenever a decodable image block
exists in `CallToolResult.content` (including text-before-image or
malformed image before valid image).
## Why?
- Tool results that include caption text before the image currently drop
the image output cell.
- A malformed image block can also suppress later valid image output.
## How?
- Iterate `content` and return the first successfully decoded image
instead of only checking the first block.
- Add unit tests that cover text-before-image ordering and
invalid-image-before-valid.
## Before
```rust
let image = match result {
Ok(mcp_types::CallToolResult { content, .. }) => {
if let Some(mcp_types::ContentBlock::ImageContent(image)) = content.first() {
// decode image (fails -> None)
} else {
None
}
}
_ => None,
}?;
```
## After
```rust
let image = result
.as_ref()
.ok()?
.content
.iter()
.find_map(decode_mcp_image)?;
```
## Risk / Impact
- Low: only affects image cell creation for MCP tool results; no change
for non-image outputs.
## Tests
- [x] `just fmt`
- [x] `cargo test -p codex-tui`
- [x] Rerun after branch update (2026-01-27): `just fmt`, `cargo test -p
codex-tui`
Manual testing
# Manual testing: MCP image tool result rendering (Codex TUI)
# Build the rmcp stdio test server binary:
cd codex-rs
cargo build -p codex-rmcp-client --bin test_stdio_server
# Register the server as an MCP server (absolute path to the built binary):
codex mcp add mcpimg -- /Users/joshka/code/codex-pr-review/codex-rs/target/debug/test_stdio_server
# Then in Codex TUI, ask it to call:
- mcpimg.image_scenario({"scenario":"image_only"})
- mcpimg.image_scenario({"scenario":"text_then_image","caption":"Here is the image:"})
- mcpimg.image_scenario({"scenario":"invalid_base64_then_image"})
- mcpimg.image_scenario({"scenario":"invalid_image_bytes_then_image"})
- mcpimg.image_scenario({"scenario":"multiple_valid_images"})
- mcpimg.image_scenario({"scenario":"image_then_text","caption":"Here is the image:"})
- mcpimg.image_scenario({"scenario":"text_only","caption":"Here is the image:"})
# Expected:
# - You should see an extra history cell: "tool result (image output)" when the
# tool result contains at least one decodable image block (even if earlier
# blocks are text or invalid images).
Fixes#9814
---------
Co-authored-by: Josh McKinney <joshka@openai.com>
Auto-enable live `web_search` tool when sandbox policy is
`DangerFullAccess`.
Explicitly setting `web_search` (canonical setting), or enabling
`web_search_cached` or `web_search_request` still takes precedence over
this sandbox-policy-driven enablement.
Constructors with long param lists can be hard to reason about when a
number of the args are `None`, in practice. Introducing a struct to use
as the args type helps make things more self-documenting.
Updates the `CodexOptions` passed to the `Codex()` constructor in the
SDK to support a `config` property that is a map of configuration data
that will be transformed into `--config` flags passed to the invocation
of `codex`.
Therefore, something like this:
```typescript
const codex = new Codex({
config: {
show_raw_agent_reasoning: true,
sandbox_workspace_write: { network_access: true },
},
});
```
would result in the following args being added to the invocation of
`codex`:
```shell
--config show_raw_agent_reasoning=true --config sandbox_workspace_write.network_access=true
```