Commit Graph

8690 Commits

Author SHA1 Message Date
Michael Bolin
0dc066dfc4 Merge 8da75ba0ed into sapling-pr-archive-bolinfest 2026-02-27 21:52:50 -08:00
Michael Bolin
8da75ba0ed core: resolve host_executable() rules during preflight 2026-02-27 21:52:43 -08:00
Michael Bolin
da4f28980b merge commit for archive created by Sapling 2026-02-27 21:49:08 -08:00
Michael Bolin
adc1788332 fix: merge skill permissions into escalated sandbox by default 2026-02-27 21:48:57 -08:00
Michael Bolin
d33e8e3581 Merge 55dfa3cf21 into sapling-pr-archive-bolinfest 2026-02-27 21:36:00 -08:00
Michael Bolin
55dfa3cf21 core: resolve host_executable() rules during preflight 2026-02-27 21:35:48 -08:00
Michael Bolin
8caf978d0e Merge 615da1b670 into sapling-pr-archive-bolinfest 2026-02-27 17:58:13 -08:00
Michael Bolin
615da1b670 fix: merge skill permissions into escalated sandbox by default 2026-02-27 17:54:38 -08:00
sayan-oai
033ef9cb9d feat: add debug clear-memories command to hard-wipe memories state (#13085)
#### what
adds a `codex debug clear-memories` command to help with clearing all
memories state from disk, sqlite db, and marking threads as
`memory_mode=disabled` so they don't get resummarized when the
`memories` feature is re-enabled.

#### tests
add tests
2026-02-27 17:45:55 -08:00
Ruslan Nigmatullin
8c1e3f3e64 app-server: Add ephemeral field to Thread object (#13084)
Currently there is no alternative way to know that thread is ephemeral,
only client which did create it has the knowledge.
2026-02-27 17:42:25 -08:00
Michael Bolin
9c0185ebb8 Merge b26c6ce567 into sapling-pr-archive-bolinfest 2026-02-27 17:41:59 -08:00
Michael Bolin
b26c6ce567 fix: merge skill permissions into escalated sandbox by default 2026-02-27 17:41:47 -08:00
Michael Bolin
df0111ae85 Merge 719e20d779 into sapling-pr-archive-bolinfest 2026-02-27 17:41:43 -08:00
Michael Bolin
719e20d779 core: resolve host_executable() rules during preflight 2026-02-27 17:41:32 -08:00
Michael Bolin
1a8d930267 core: adopt host_executable() rules in zsh-fork (#13046)
## Why

[#12964](https://github.com/openai/codex/pull/12964) added
`host_executable()` support to `codex-execpolicy`, but the zsh-fork
interception path in `unix_escalation.rs` was still evaluating commands
with the default exact-token matcher.

That meant an intercepted absolute executable such as `/usr/bin/git
status` could still miss basename rules like `prefix_rule(pattern =
["git", "status"])`, even when the policy also defined a matching
`host_executable(name = "git", ...)` entry.

This PR adopts the new matching behavior in the zsh-fork runtime only.
That keeps the rollout intentionally narrow: zsh-fork already requires
explicit user opt-in, so it is a safer first caller to exercise the new
`host_executable()` scheme before expanding it to other execpolicy call
sites.

It also brings zsh-fork back in line with the current `prefix_rule()`
execution model. Until prefix rules can carry their own permission
profiles, a matched `prefix_rule()` is expected to rerun the intercepted
command unsandboxed on `allow`, or after the user accepts `prompt`,
instead of merely continuing inside the inherited shell sandbox.

## What Changed

- added `evaluate_intercepted_exec_policy()` in
`core/src/tools/runtimes/shell/unix_escalation.rs` to centralize
execpolicy evaluation for intercepted commands
- switched intercepted direct execs in the zsh-fork path to
`check_multiple_with_options(...)` with `MatchOptions {
resolve_host_executables: true }`
- added `commands_for_intercepted_exec_policy()` so zsh-fork policy
evaluation works from intercepted `(program, argv)` data instead of
reconstructing a synthetic command before matching
- left shell-wrapper parsing intentionally disabled by default behind
`ENABLE_INTERCEPTED_EXEC_POLICY_SHELL_WRAPPER_PARSING`, so
path-sensitive matching relies on later direct exec interception rather
than shell-script parsing
- made matched `prefix_rule()` decisions rerun intercepted commands with
`EscalationExecution::Unsandboxed`, while unmatched-command fallback
keeps the existing sandbox-preserving behavior
- extracted the zsh-fork test harness into
`core/tests/common/zsh_fork.rs` so both the skill-focused and
approval-focused integration suites can exercise the same runtime setup
- limited this change to the intercepted zsh-fork path rather than
changing every execpolicy caller at once
- added runtime coverage in
`core/src/tools/runtimes/shell/unix_escalation_tests.rs` for allowed and
disallowed `host_executable()` mappings and the wrapper-parsing modes
- added integration coverage in `core/tests/suite/approvals.rs` to
verify a saved `prefix_rule(pattern=["touch"], decision="allow")` reruns
under zsh-fork outside a restrictive `WorkspaceWrite` sandbox

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/13046).
* #13065
* __->__ #13046
2026-02-28 01:41:23 +00:00
Owen Lin
8fa792868c fix(app-server): make thread/start non-blocking (#13033)
Stop `thread/start` from blocking other app-server requests.

Before this change, `thread/start ran` inline on the request loop, so
slow startup paths like MCP auth checks could hold up unrelated requests
on the same connection, including `thread/loaded/list`. This moves
`thread/start` into a background task.

While doing so, it revealed an issue where we were doing nested locking
(and there were some race conditions possible that could introduce a
"phantom listener"). This PR also refactors the listener/subscription
bookkeeping - listener/subscription state is now centralized in
`ThreadStateManager` instead of being split across multiple lock
domains. That makes late auto-attach on `thread/start` race-safe and
avoids reintroducing disconnected clients as phantom subscribers.
2026-02-28 01:40:08 +00:00
Eric Traut
6604608bad Suppress duplicate assistant output on stdout in interactive sessions (#13082)
Addresses #12566

Summary
- stop printing the final assistant message on stdout when the process
is running in a terminal so interactive users only see it once
- add a helper that gates the stdout emission and cover it with unit
tests
2026-02-27 18:31:17 -07:00
Ruslan Nigmatullin
70ed6cbc71 app-server: Add an ability to watch events in the test client (#13080)
Add a `watch` subcommand to `codex-app-server-test-client` binary to
help in manual testing of events flow.
2026-02-27 17:19:53 -08:00
Ahmed Ibrahim
ec6f6aacbf Add model availability NUX tooltips (#13021)
- override startup tooltips with model availability NUX and persist
per-model show counts in config
- stop showing each model after four exposures and fall back to normal
tooltips
2026-02-27 17:14:06 -08:00
Michael Bolin
fd0722f69f Merge 4516726e36 into sapling-pr-archive-bolinfest 2026-02-27 17:09:09 -08:00
Michael Bolin
4516726e36 core: resolve host_executable() rules during preflight 2026-02-27 17:06:00 -08:00
Michael Bolin
ac119a12b8 core: adopt host_executable() rules in zsh-fork 2026-02-27 17:06:00 -08:00
Michael Bolin
2c382c3aea Merge 73f846a2d5 into sapling-pr-archive-bolinfest 2026-02-27 16:55:43 -08:00
Michael Bolin
73f846a2d5 core: resolve host_executable() rules during preflight 2026-02-27 16:55:37 -08:00
Michael Bolin
72e67da3b9 core: adopt host_executable() rules in zsh-fork 2026-02-27 16:55:37 -08:00
Eric Traut
ff5cbfd7d4 Handle missing plan info for ChatGPT accounts (#13072)
Addresses https://github.com/openai/codex/issues/13007 and
https://github.com/openai/codex/issues/12170

There are situations where the ChatGPT auth backend might return a JWT
that contains no plan information. Most code paths already handle this
case well, but the internal implementation of the "account/read" app
server call was failing in this case (returning an error rather than
properly returning None for the plan).

This resulted in a situation where users needed to log in every time the
extension or app started even if they successfully logged in the last
time.

Summary
- allow ChatGPT-authenticated accounts to fall back to
`AccountPlanType::Unknown` when the token omits the plan claim
- add regression coverage in `app-server/tests/suite/v2/account.rs` to
confirm `account/read` returns `plan_type: Unknown` when the claim is
absent
- ensure the Rust auth helpers and fixtures treat missing plan claims as
Optional and default to `Unknown`
2026-02-27 17:51:21 -07:00
Michael Bolin
56f663a0e2 Merge 93f803fd66 into sapling-pr-archive-bolinfest 2026-02-27 16:45:17 -08:00
Michael Bolin
93f803fd66 core: resolve host_executable() rules during preflight 2026-02-27 16:45:06 -08:00
Michael Bolin
4369a27938 core: adopt host_executable() rules in zsh-fork 2026-02-27 16:45:06 -08:00
Michael Bolin
df5f8f8833 Merge 8cbe3c28d6 into sapling-pr-archive-bolinfest 2026-02-27 16:27:17 -08:00
Michael Bolin
9a84a7c5be merge commit for archive created by Sapling 2026-02-27 16:21:02 -08:00
Michael Bolin
8cbe3c28d6 core: resolve host_executable() rules during preflight 2026-02-27 16:20:54 -08:00
Michael Bolin
a1ad578462 core: adopt host_executable() rules in zsh-fork 2026-02-27 16:20:54 -08:00
Eric Traut
61c42396ab Keep large-paste placeholders intact during file completion (#13070)
Addresses https://github.com/openai/codex/issues/13040

Fixes a regression in 0.106.0 introduced in
https://github.com/openai/codex/pull/9393

Summary
- replace only the active completion range so unrelated text elements
(e.g., large-paste placeholders) stay atomic and can still expand
- add a regression test verifying large paste placeholders persist
through completions and submit
- could not fetch issue details via GitHub API because network access is
disabled in this sandboxed environment
2026-02-27 17:19:11 -07:00
Michael Bolin
43738486ac merge commit for archive created by Sapling 2026-02-27 16:00:48 -08:00
Michael Bolin
4c0d9aedb8 core: adopt host_executable() rules in zsh-fork 2026-02-27 16:00:38 -08:00
Felipe Coury
c3c75878e8 fix(tui): theme-aware diff backgrounds with fallback behavior (#13037)
## Problem

The TUI diff renderer uses hardcoded background palettes for
insert/delete lines that don't respect the user's chosen syntax theme.
When a theme defines `markup.inserted` / `markup.deleted` scope
backgrounds (the convention used by GitHub, Solarized, Monokai, and most
VS Code themes), those colors are ignored — the diff always renders with
the same green/red tints regardless of theme selection.

Separately, ANSI-16 terminals (and Windows Terminal sessions misreported
as ANSI-16) rendered diff backgrounds as full-saturation blocks that
obliterated syntax token colors, making highlighted diffs unreadable.

## Mental model

Diff backgrounds are resolved in three layers:

1. **Color level detection** — `diff_color_level_for_terminal()` maps
the raw `supports-color` probe + Windows Terminal heuristics to a
`DiffColorLevel` (TrueColor / Ansi256 / Ansi16). Windows Terminal gets
promoted from Ansi16 to TrueColor when `WT_SESSION` is present.

2. **Background resolution** — `resolve_diff_backgrounds()` queries the
active syntax theme for `markup.inserted`/`markup.deleted` (falling back
to `diff.inserted`/`diff.deleted`), then overlays those on top of the
hardcoded palette. For ANSI-256, theme RGB values are quantized to the
nearest xterm-256 index. For ANSI-16, backgrounds are `None`
(foreground-only).

3. **Style composition** — The resolved `ResolvedDiffBackgrounds` is
threaded through every call to `style_add`, `style_del`, `style_sign_*`,
and `style_line_bg_for`, which decide how to compose
foreground+background for each line kind and theme variant.

A new `RichDiffColorLevel` type (a subset of `DiffColorLevel` without
Ansi16) encodes the invariant "we have enough depth for tinted
backgrounds" at the type level, so background-producing functions have
exhaustive matches without unreachable arms.

## Non-goals

- No change to gutter (line number column) styling — gutter backgrounds
still use the hardcoded palette.
- No per-token scope background resolution — this is line-level
background only; syntax token colors come from the existing
`highlight_code_to_styled_spans` path.
- No dark/light theme auto-switching from scope backgrounds —
`DiffTheme` is still determined by querying the terminal's background
color.

## Tradeoffs

- **Theme trust vs. visual safety:** When a theme defines scope
backgrounds, we trust them unconditionally for rich color levels. A
badly authored theme could produce illegible combinations. The fallback
for `None` backgrounds (foreground-only) is intentionally conservative.
- **Quantization quality:** ANSI-256 quantization uses perceptual
distance across indices 16–255, skipping system colors. The result is
approximate — a subtle theme tint may land on a noticeably different
xterm index.
- **Single-query caching:** `resolve_diff_backgrounds` is called once
per `render_change` invocation (i.e., once per file in a diff). If the
theme changes mid-render (live preview), the next file picks up the new
backgrounds.

## Architecture

Files changed:

| File | Role |
|---|---|
| `tui/src/render/highlight.rs` | New: `DiffScopeBackgroundRgbs`,
`diff_scope_background_rgbs()`, scope extraction helpers |
| `tui/src/diff_render.rs` | New: `RichDiffColorLevel`,
`ResolvedDiffBackgrounds`, `resolve_diff_backgrounds*`,
`quantize_rgb_to_ansi256`, Windows Terminal promotion; modified: all
style helpers to accept/thread `ResolvedDiffBackgrounds` |

The scope-extraction code lives in `highlight.rs` because it uses
`syntect::highlighting::Highlighter` and the theme singleton. The
resolution and quantization logic lives in `diff_render.rs` because it
depends on diff-specific types (`DiffTheme`, `DiffColorLevel`, ratatui
`Color`).

## Observability

No runtime logging was added. The most useful debugging aid is the
`diff_color_level_for_terminal` function, which is pure and fully
unit-tested — to diagnose a color-depth mismatch, log its four inputs
(`StdoutColorLevel`, `TerminalName`, `WT_SESSION` presence,
`FORCE_COLOR` presence).

Scope resolution can be tested by loading a custom `.tmTheme` with known
`markup.inserted` / `markup.deleted` backgrounds and checking the diff
output in a truecolor terminal.

## Tests

- **Windows Terminal promotion:** 7 unit tests cover every branch of
`diff_color_level_for_terminal` (ANSI-16 promotion, `WT_SESSION`
unconditional promotion, `FORCE_COLOR` suppression, conservative
`Unknown` level).
- **ANSI-16 foreground-only:** Tests verify that `style_add`,
`style_del`, `style_sign_*`, `style_line_bg_for`, and `style_gutter_for`
all return `None` backgrounds on ANSI-16.
- **Scope resolution:** Tests verify `markup.*` preference over
`diff.*`, `None` when no scope matches, bundled theme resolution, and
custom `.tmTheme` round-trip.
- **Quantization:** Test verifies ANSI-256 quantization of a known RGB
triple.
- **Insta snapshots:** 2 new snapshot tests
(`ansi16_insert_delete_no_background`,
`theme_scope_background_resolution`) lock visual output.
2026-02-27 16:44:56 -07:00
viyatb-oai
a39d76dc45 feat(linux-sandbox): support restricted ReadOnlyAccess in bwrap (#12369)
## Summary
Implements Linux bubblewrap support for restricted `ReadOnlyAccess`
(introduced in #11387) by honoring `readable_roots` and
`include_platform_defaults` instead of failing closed.

## What changed
- Added a Linux platform-default read allowlist for common
system/runtime paths (e.g. /usr, /etc, /lib*, Nix store roots).
- Updated the bwrap filesystem mount builder to support restricted read
access:
  - Full-read policies still use `--ro-bind / /`
- Restricted-read policies now start from` --tmpfs `/ and add scoped
`--ro-bind` mounts
- Preserved existing writable-root and protected-subpath behavior
(`.git`, `.codex`, etc.).

`ReadOnlyAccess::Restricted` was already modeled in protocol, but Linux
bwrap still returned `UnsupportedOperation` for restricted read access.
This closes that gap for the active Linux filesystem backend.


## Notes
Legacy Linux Landlock fallback still fail-closes for restricted read
access (unchanged).
2026-02-27 15:25:50 -08:00
Matthew Zeng
392fa7de50 [apps] Stablize app list updated event. (#13067)
Stablize app list updated event so that we only send 2 updates: 1 when
installed apps become available, one when all directory apps are
available. Previously it also updates when directory apps become
available before installed apps, which cuts off installed apps.
2026-02-27 15:23:24 -08:00
Michael Bolin
12f556972b merge commit for archive created by Sapling 2026-02-27 13:57:40 -08:00
Michael Bolin
c1f599d407 core: resolve host_executable() rules during preflight 2026-02-27 13:57:26 -08:00
Michael Bolin
152eeb08a4 core: adopt host_executable() rules in zsh-fork 2026-02-27 13:57:26 -08:00
Charley Cunningham
695957a348 Unify rollout reconstruction with resume/fork TurnContext hydration (#12612)
## Summary

This PR unifies rollout history reconstruction and resume/fork metadata
hydration under a single `Session::reconstruct_history_from_rollout`
implementation.

The key change from main is that replay metadata now comes from the same
reconstruction pass that rebuilds model-visible history, instead of
doing a second bespoke rollout scan to recover `previous_model` /
`reference_context_item`.

## What Changed

### Unified reconstruction output

`reconstruct_history_from_rollout` now returns a single
`RolloutReconstruction` bundle containing:

- rebuilt `history`
- `previous_model`
- `reference_context_item`

Resume and fork both consume that shared output directly.

### Reverse replay core

The reconstruction logic moved into
`codex-rs/core/src/codex/rollout_reconstruction.rs` and now scans
rollout items newest-to-oldest.

That reverse pass:

- derives `previous_model`
- derives whether `reference_context_item` is preserved or cleared
- stops early once it has both resume metadata and a surviving
`replacement_history` checkpoint

History materialization is still bridged eagerly for now by replaying
only the surviving suffix forward, which keeps the history result stable
while moving the control flow toward the future lazy reverse loader
design.

### Removed bespoke context lookup

This deletes `last_rollout_regular_turn_context_lookup` and its separate
compaction-aware scan.

The previous model / baseline metadata is now computed from the same
replay state that rebuilds history, so resume/fork cannot drift from the
reconstructed transcript view.

### `TurnContextItem` persistence contract

`TurnContextItem` is now treated as the replay source of truth for
durable model-visible baselines.

This PR keeps the following contract explicit:

- persist `TurnContextItem` for the first real user turn so resume can
recover `previous_model`
- persist it for later turns that emit model-visible context updates
- if mid-turn compaction reinjects full initial context into replacement
history, persist a fresh `TurnContextItem` after `Compacted` so
resume/fork can re-establish the baseline from the rewritten history
- do not treat manual compaction or pre-sampling compaction as creating
a new durable baseline on their own

## Behavior Preserved

- rollback replay stays aligned with `drop_last_n_user_turns`
- rollback skips only user turns
- incomplete active user turns are dropped before older finalized turns
when rollback applies
- unmatched aborts do not consume the current active turn
- missing abort IDs still conservatively clear stale compaction state
- compaction clears `reference_context_item` until a later
`TurnContextItem` re-establishes it
- `previous_model` still comes from the newest surviving user turn that
established one

## Tests

Targeted validation run for the current branch shape:

- `cd codex-rs && cargo test -p codex-core --lib
codex::rollout_reconstruction_tests -- --nocapture`
- `cd codex-rs && just fmt`

The branch also extracts the rollout reconstruction tests into
`codex-rs/core/src/codex/rollout_reconstruction_tests.rs` so this logic
has a dedicated home instead of living inline in `codex.rs`.
2026-02-27 13:50:45 -08:00
daniel-oai
6046ca19ba Clarify escalation guidance for sandbox-related network failures (#13051)
This updates the on-request permissions instructions so likely
sandbox-related network failures during dependency installation are
treated as escalation candidates.

Repro:
- Run `codex -a on-request -s workspace-write` in a fresh temp dir.
- Prompt: `Build a new rust app with one dependency, anyhow, and try
installing the dependency`.
- Before this change, DNS/registry failures like `Could not resolve
host: index.crates.io` could be treated like ordinary transient failures
and not escalate.

Fix:
- Clarify that likely sandbox-related network errors such as DNS/host
resolution, registry/index access, and dependency download failures
should trigger escalation.

Validation:
- Rebuild the CLI and rerun the same repro. The same instructions should
now be more likely to trigger escalation instead of silently stopping.

Related Slack canvas:
- https://openai.enterprise.slack.com/docs/T0BQTNSUF/F0ACVNJAV09
2026-02-27 13:48:52 -08:00
Michael Bolin
7499d9b636 merge commit for archive created by Sapling 2026-02-27 13:43:55 -08:00
Michael Bolin
f375477147 core: resolve host_executable() rules during preflight 2026-02-27 13:43:36 -08:00
Michael Bolin
acf683899c core: adopt host_executable() rules in zsh-fork 2026-02-27 13:43:36 -08:00
Michael Bolin
922b267ae5 Merge 4ccce990ca into sapling-pr-archive-bolinfest 2026-02-27 13:04:35 -08:00
Michael Bolin
b5406ce548 Merge 7e9e6630c3 into sapling-pr-archive-bolinfest 2026-02-27 13:04:27 -08:00
Michael Bolin
4ccce990ca core: adopt host_executable() rules in zsh-fork 2026-02-27 13:04:11 -08:00