Commit Graph
7862 Commits
Author SHA1 Message Date
Winston HowesandGitHub 472619f9fc Add SOCKS5 TCP MITM coverage (#22685)
## Summary
- reuse the MITM HTTPS serving path for raw SOCKS5 TCP streams
- route limited-mode and hooked SOCKS5 TCP requests through MITM before
dialing upstream
- keep SOCKS5 UDP limited-mode behavior unchanged

## Validation
- `just fmt`
- `just test -p codex-network-proxy`
- `just fix -p codex-network-proxy`
- `git diff --check`
2026-06-09 13:08:01 -07:00
Eric NingandGitHub 7a7cee1be4 fix: use plugin service route for remote uninstall (#27223) 2026-06-09 12:52:05 -07:00
Adam Perry @ OpenAIandGitHub 6e7ab52929 [codex] Speed up local nextest runs (#26479)
## Why

`just test` currently uses the CI-oriented nextest profile, which
serializes app-server integration tests even on developer machines that
can run several safely. Bounded local parallelism substantially shortens
this common iteration loop without changing CI behavior.

Eight-worker experiments were faster, but keeping them reliable required
relaxing several test deadlines. Four workers for integration tests is a
solid tradeoff that speeds up local testing without needing to change
test logic.

## What changed

- Add a `local` nextest profile that inherits the existing defaults.
- Allow up to four app-server integration tests to run concurrently
under that profile.
- Make `just test` select the local profile on Unix and Windows.
- Keep the default CI profile serialized and leave all test deadlines
unchanged.

The tests use separate processes, randomized temporary `CODEX_HOME`
directories, and ephemeral ports. The remaining shared constraints are
system resources; each app-server also uses a multi-thread Tokio
runtime, and fuzzy-search tests can create additional worker threads, so
the local cap remains intentionally conservative.

## Performance and validation

All measurements below are warm, execution-only app-server runs with
nextest retries disabled.

On the current rebased branch, an AMD EPYC 7763 machine with 16 logical
CPUs and 62 GiB RAM completed three consecutive runs:

| Run | Nextest time | Wall time | Result |
| --- | ---: | ---: | --- |
| 1 | 142.941s | 145.17s | 836/836 passed |
| 2 | 143.402s | 145.59s | 836/836 passed |
| 3 | 142.870s | 145.08s | 836/836 passed |

The mean wall time was 145.28s. The slow-inventory, approval replay, and
zsh-fork tests all passed with their original deadlines.

Earlier measurements on the same Linux machine, before the suite grew,
showed the scaling that motivated the change:

| App-server concurrency | Nextest time | Result |
| --- | ---: | --- |
| 1 | 369.5s | 572/572 passed |
| 2 | 194.5s | 572/572 passed |
| 4 | 111.0s mean over 3 runs | 3/3 clean |

Four workers reduced that execution time by about 70%, a roughly 3.3x
speedup over serialization.
2026-06-09 12:48:04 -07:00
marksteinbrick-oaiandGitHub a71e040df5 [codex-analytics] add extensible feature thread sources (#27063)
## Why
- `ThreadSource` currently defines a closed set of core-owned values
- Product features also create threads for background or scheduled work
- Adding every product-specific value to the core enum would require
repeated `codex-rs` protocol changes
- Feature-backed values let product callers provide precise attribution
while preserving the existing core classifications

## What Changed
- Adds `ThreadSource::Feature(String)` for app-owned thread source
values
- Represents all app-server v2 thread sources as scalar strings, so a
feature source is supplied as `"automation"`
- Persists and emits the feature's plain string label, so `"automation"`
produces `thread_source="automation"` in analytics
- Keeps `user`, `subagent`, and `memory_consolidation` as explicit
core-owned values and regenerates the app-server schemas and TypeScript
bindings

## Verification
- `just write-app-server-schema`
- `cargo check --workspace`
- `just test -p codex-protocol
feature_thread_source_serializes_as_its_app_owned_label`
- `just test -p codex-app-server-protocol
thread_sources_round_trip_as_scalar_labels`
- `cargo test -p codex-analytics
thread_initialized_event_serializes_expected_shape`
- `just fmt`
2026-06-09 12:27:10 -07:00
Adam Perry @ OpenAIandGitHub 99da697e4c [codex] Test extension API contracts (#26835)
## Why

`codex-extension-api` defines contracts shared by extension crates and
their hosts, but it had no direct test suite. Host and feature tests
cover downstream behavior, while regressions in the API crate's own
typed state, registry ordering, and capability adapters could go
unnoticed.

## What

- Add public-surface integration tests for `ExtensionData`, including
concurrent initialization and poison recovery.
- Cover contributor registration order, approval short-circuiting, event
sink retention, no-op response injection, and closure-based agent
spawning.
- Add the test-only dependencies used by the suite.

## Validation

- `just test -p codex-extension-api`
- `just argument-comment-lint -p codex-extension-api`
- `just bazel-lock-check`
2026-06-09 18:30:24 +00:00
jifandGitHub 89ac3ec27c Load selected executor skills through extensions (#27184)
## Why

CCA is moving toward a split runtime where the orchestrator may not have
a filesystem, while executors can expose preinstalled plugins and
skills. A thread therefore needs to select capabilities without asking
app-server or core to interpret executor-owned paths through the
orchestrator's filesystem.

The longer-term model is broader than executor skills:

- A plugin is a bundle of skills, MCP servers, connectors/apps, and
hooks.
- A plugin root can be local, executor-owned, or hosted by a backend.
- Components inside one plugin can use different access and execution
mechanisms. A skill may be read from a filesystem or through backend
tools; an HTTP MCP server can run without an executor; a stdio MCP
server or hook needs an execution environment.
- Core should carry generic extension initialization data. The extension
that owns a component should discover it, expose it to the model, and
invoke it through the appropriate runtime.

This PR establishes that architecture through one complete vertical:
selecting a root on an executor, discovering the skills beneath it,
exposing those skills to the model, and reading an explicitly invoked
`SKILL.md` through the same executor.

## Contract

`thread/start` gains an experimental `selectedCapabilityRoots` field:

```json
{
  "selectedCapabilityRoots": [
    {
      "id": "deploy-plugin@1",
      "location": {
        "type": "environment",
        "environmentId": "workspace",
        "path": "/opt/codex/plugins/deploy"
      }
    }
  ]
}
```

The root is intentionally not classified as a "plugin" or "skill" in the
API. It can point at a standalone skill, a directory containing several
skills, or a plugin containing skills and other components. This PR only
teaches the skills extension how to consume it; later extensions can
resolve MCP, connector, and hook components from the same selection.

The platform-supplied `id` is stable selection identity. The location
says which runtime owns the root and gives that runtime an opaque path.
App-server does not inspect or canonicalize the path.

## What changed

### Generic thread extension initialization

App-server converts selected roots into `ExtensionDataInit`. Core
carries that generic initialization value until the final thread ID is
known, then creates thread-scoped `ExtensionData` before lifecycle
contributors run.

This keeps `Session` and core independent of the capability-selection
contract. The initialization value is consumed during construction; it
is not retained as another long-lived `Session` field.

### Executor-backed skills

The skills extension now owns an `ExecutorSkillProvider` that:

- resolves the selected environment through `EnvironmentManager`
- discovers, canonicalizes, and reads skills through that environment's
`ExecutorFileSystem`
- contributes the bounded selected-skill catalog as stable developer
context
- reads an explicitly invoked skill body through the authority that
listed it
- warns when an environment or root is unavailable
- never falls back to the orchestrator filesystem for an executor-owned
root

Skill catalog and instruction fragments have hard byte bounds, which
also bound them below the 10K-token per-item context limit. If a
selected executor skill has the same name as a legacy local skill, the
executor selection owns that invocation and the local body is not
injected a second time.

Existing local and bundled skill loading remains in place. Omitting
`selectedCapabilityRoots` therefore preserves current local-only
behavior.

## Current semantics

- Only environment-owned locations are represented in this first
contract.
- Roots are resolved by the destination extension, not by app-server or
core.
- An unavailable executor or invalid root produces a warning and no
capabilities from that root; it does not trigger a local-filesystem
fallback.
- Selection applies to a newly started active thread.
- MCP servers, connectors, and hooks beneath a selected plugin root are
not activated yet.
- Selection is not yet persisted or inherited across resume, fork, or
subagent creation. Existing local capabilities continue to behave as
they do today in those flows.

## Planned vertical follow-ups

1. **Hosted HTTP MCP:** add an extension-backed HTTP MCP source that
works without an executor, then replace the special-purpose MCP plugins
loader with that implementation.
2. **Executor MCP:** register and execute stdio MCP servers through the
environment that owns the selected plugin root.
3. **Backend skills:** add a hosted skill source whose catalog and
bodies are accessed through extension tools rather than a filesystem.
4. **Connectors and hooks:** activate those components through their
owning extensions, using the same selected-root boundary and
component-specific runtime.
5. **Durable selection:** define the desired-selection lifecycle,
persist it, and make resume, fork, and subagent inheritance explicit
rather than accidental.
6. **Local convergence:** incrementally route existing local plugin,
skill, and MCP loading through the same extension model while preserving
current local behavior.

Each follow-up remains reviewable as an end-to-end capability. The
platform selects roots, generic thread extension data carries the
selection, and the owning extension resolves and operates its component.

## Verification

Coverage added for:

- app-server end-to-end discovery and explicit invocation of a skill
inside an executor-selected plugin root
- exclusive invocation when a selected executor skill collides with a
local skill name
- executor filesystem authority for discovery, canonicalization, and
reads
- thread extension initialization before lifecycle contributors run
- stable executor catalog context, explicit invocation, context
rebuilding, hidden skills, and preserved host/remote catalog behavior

Targeted protocol, core-skills, skills-extension, core lifecycle, and
app-server executor-skill tests were run during development.
2026-06-09 19:51:54 +02:00
jifandGitHub 1026e9de1b app-server: reject direct input to multi-agent v2 sub-agents (#27173)
## Why

Multi-agent v2 sub-agents are owned and coordinated by their parent
agent. Allowing an app-server client to start or steer turns on a
spawned child bypasses the multi-agent messaging path and creates a
second, conflicting source of work for that sub-agent.

## What changed

- Reject direct `turn/start` and `turn/steer` requests targeting
multi-agent v2 thread-spawn sub-agents.
- Identify these targets using both the thread's resolved multi-agent
version and its `SubAgentSource::ThreadSpawn` session source, leaving
root threads, v1 agents, and other sub-agent types unchanged.
- Return a consistent invalid-request error before validating or
applying the submitted input.

## Testing

- Added an app-server integration test that spawns a real multi-agent v2
child and verifies that direct `turn/start` and `turn/steer` requests
are rejected.
2026-06-09 19:40:40 +02:00
canvrno-oaiandGitHub 8a299fb704 fix: Prevent /review crash when entering Esc on steer message (#22879)
This changes the `/review` escape path so `Esc` no longer behaves like
the normal queued-follow-up interrupt flow while a review is running.
Steering is not currently supported in `/review` mode, without this
change users are able to attempt a steer but it leads to a crash (see
#22815). If the user has already tried to send additional guidance
during `/review`, the TUI now keeps the review running and shows a
warning that steer messages are not supported in that mode, while still
pointing users to `Ctrl+C` if they actually want to cancel. It also adds
regression coverage for the review-specific warning behavior. When users
do cancel with Ctrl+C during /review, the TUI now tolerates the
active-turn race that can happen during review handoff, and any queued
steer messages are restored to the composer instead of being discarded.

- Special-case `Esc` during an active `/review` when follow-up steer
input is pending or has already been deferred.
- Show a clear warning instead of interrupting the running review.
- Make the Ctrl+C cancel path during /review resilient to active-turn
races, while preserving any queued steer text by restoring it to the
composer.
- Add review-mode test coverage for the warning path.

## Testing

1. Start a `/review` with a diff large enough that the review stays
active for more than a few seconds.

2. While the review is still running, type a follow-up / steer message,
submit it, and then press `Esc`.
   Before: `Esc` causes the TUI to close abruptly.  
After: the review keeps running and the transcript shows a warning that
steer messages are not supported during `/review`, with guidance to use
`Ctrl+C` if you want to cancel.

3. Press `Ctrl+C` if you actually want to stop the review.  
Before: (after restarting the test since Pt. 2 crashed) this is the
intentional cancellation path.
After: this remains the intentional cancellation path, and any queued
follow-up steer text is restored to the composer instead of being lost.
   
## Note:
`/review` mode explicitly does not support steering at this time (as
noted in `turn_processer.rs`, if we want to explore that in the future
this code will need to be modified). This change keeps unsupported steer
attempts from crashing the TUI and preserves queued follow-up text if
the user cancels with Ctrl+C.
2026-06-09 09:41:58 -07:00
Zanie BlueandGitHub 6a9a49b334 Avoid rereading rollout history during cold resume (#27031)
## Summary

- reuse the history-bearing `StoredThread` loaded while probing for a
running thread
- avoid rereading and reparsing the rollout when that probe finds no
active process
- reload after shutting down a loaded thread because shutdown may flush
newer rollout items
- add a regression test that verifies cold resume performs one
history-bearing store read

## Problem

`thread/resume` first reads the persisted thread with history while
checking whether the thread is
already running. When no running process exists, cold resume currently
falls through to
`resume_thread_from_rollout`, which reads and parses the same history
again.

That duplicate work grows with rollout size and remains on the
synchronous resume path even when
the caller requests `excludeTurns`.

## Background

The duplicate read was introduced by #24528, which fixed resume
overrides for idle cached
threads. To support resumes specified by rollout path,
`resume_running_thread` began loading the
stored thread with history so it could resolve the canonical thread ID
and determine whether a
cached `CodexThread` was already loaded.

That history is needed when the loaded-thread path handles the request.
On a cold miss, however,
the function's boolean result could only report that no loaded thread
handled the request. It
discarded the history-bearing `StoredThread`, and the normal cold-resume
path immediately loaded
and parsed the same rollout again.

This change preserves the idle cached-thread behavior from #24528 while
allowing the cold-resume
path to reuse the probe result.

## Performance

I benchmarked real retained rollouts using isolated `CODEX_HOME`
directories, explicit rollout
paths, debug builds of the commit and its exact parent, and alternating
parent/patch order. The
table below uses `thread/resume` with `excludeTurns: true`; response
payload sizes were identical.

| Rollout size | Records | Parent median | Patch median | Median paired
saving |
| ---: | ---: | ---: | ---: | ---: |
| 6 MB | 3,574 | 541 ms | 441 ms | 132 ms |
| 30 MB | 15,220 | 1.505 s | 1.041 s | 701 ms |
| 60 MB | 31,453 | 2.644 s | 1.742 s | 970 ms |
| 149 MB | 100,874 | 10.506 s | 7.156 s | 3.350 s |
| 559 MB | 259,734 | 27.759 s | 16.725 s | 9.836 s |

The absolute saving increases with thread size, as expected when
removing one complete JSONL
history read and parse. Total resume time is also content-dependent, so
the relationship is not
perfectly linear.

I also tested full-history resume with `excludeTurns: false`. The
response payload was
byte-identical between variants, and the same size-dependent improvement
remained visible:

| Rollout size | Parent median | Patch median | Median paired saving |
| ---: | ---: | ---: | ---: |
| 6 MB | 1.052 s | 904 ms | 270 ms |
| 30 MB | 2.667 s | 1.762 s | 924 ms |
| 60 MB | 8.464 s | 6.272 s | 3.680 s |
| 149 MB | 26.719 s | 12.118 s | 14.601 s |
| 559 MB | 40.359 s | 25.475 s | 16.590 s |

## Validation

- `just test -p codex-app-server
cold_thread_resume_reuses_non_local_history_probe`
- `just fix -p codex-app-server -p codex-thread-store`
- `just fmt`
2026-06-09 11:16:27 -05:00
Zanie BlueandGitHub 18ce671fed Avoid no-op backfill state writes (#26420)
## Summary

- avoid acquiring SQLite's writer slot when the singleton backfill row
already exists
- preserve race-safe repair when the row is missing
- add regressions for writer contention and missing-row repair

## Why

State runtime initialization and backfill-state reads previously
executed
`INSERT ... ON CONFLICT DO NOTHING` even in the steady state. SQLite
still
enters the writer path for that statement, so TUI and app-server startup
could
wait behind another writer for up to the configured five-second busy
timeout.

## Validation

- `just test -p codex-state` (134 tests passed)
- `just fix -p codex-state`
- `just fmt`
2026-06-09 10:50:13 -05:00
Adam Perry @ OpenAIandGitHub a770e5b847 [codex] Ignore pending PR review comments (#27080)
## Why

The PR babysitter could surface inline comments from a GitHub review
that was still in the `PENDING` state. That allowed Codex to start
acting on feedback before the reviewer submitted it.

## What changed

- Correlate inline comments with their parent review and ignore pending
reviews and their comments.
- Remove pending review IDs from saved watcher state so the feedback
surfaces normally after publication.
- Update the skill instructions and add regression coverage for the
draft-to-published transition.

## Validation

- `python3 -m pytest
.codex/skills/babysit-pr/scripts/test_gh_pr_watch.py`
- Skill package validation with `quick_validate.py`
- Live verification on #26835: the draft comment stayed hidden and
surfaced after the review was submitted.
2026-06-09 08:29:32 -07:00
jifandGitHub 1547785657 app-server: clear stale thread watches after v2 agent interruption (#27166)
## Why

PR #27007 moved MultiAgentV2 interruption reporting from the legacy
collaboration close event to `SubAgentActivity::Interrupted`.
App-server's missing-thread cleanup still ran only for the legacy event,
so an interrupted child that had already been unloaded could remain
marked as loaded and running in `ThreadWatchManager`. That leaves thread
status and running-turn accounting stale, including the count used
during graceful shutdown.

## What changed

- Handle `SubAgentActivity::Interrupted` separately in app-server event
processing.
- Remove the child's thread watch when `ThreadManager` no longer has
that thread.
- Continue forwarding the same completed sub-agent activity notification
to clients.

## Testing

- Added a regression test that starts with a running watch for an
unloaded child, applies the interrupted activity event, and verifies the
watch is removed, the running count returns to zero, and the client
notification is still emitted.
2026-06-09 13:27:53 +02:00
jifandGitHub fae2709320 multi-agent: add path-based v2 activity tracking (#27007)
## Why

Multi-agent v2 identifies agents by canonical paths, but its tool
handlers still emitted the larger legacy collaboration begin/end events
built around nickname and role metadata. App-server, rollout-trace,
analytics, and TUI consumers therefore lacked one compact path-based
completion signal that behaved consistently across live events and
replay.

The TUI also needs a bounded `/agent` status surface for v2 agents. It
should use recent local activity for previews, refresh liveness without
loading full histories, and keep the legacy picker available when no
path-backed v2 agent is known.

## What changed

- Replace the v2 `spawn_agent`, `send_message`, `followup_task`, and
`interrupt_agent` legacy lifecycle emissions with a success-only
`SubAgentActivity` event. The event records the tool call ID, occurrence
time, affected thread, canonical agent path, and `started`,
`interacted`, or `interrupted` kind.
- Expose the activity as a completion-only app-server v2
`subAgentActivity` thread item in live notifications and reconstructed
history, regenerate the protocol schemas, and count it in sub-agent tool
analytics.
- Track canonical paths from live activity and loaded-thread metadata in
the TUI, and render the activity in live and replayed transcripts.
- Make `/agent` list running path-backed agents with summaries from
bounded local event buffers. Each summary is capped at 240 graphemes,
the scan is capped at six recent items, only the last three wrapped
lines are shown, and command output is omitted. Liveness falls back to
metadata-only `thread/read` when local turn state is unavailable.
- Persist the activity as a terminal rollout-trace runtime payload and
reduce it to the corresponding spawn, send, follow-up, or close
interaction edge. `interrupt_agent` is classified as a close-edge
operation.
- Preserve the legacy picker when no path-backed v2 agent is known.

## Compatibility

App-server v2 clients that consumed `collabAgentToolCall` begin/end
pairs for these tools must handle the new completion-only
`subAgentActivity` item. Legacy v1 collaboration behavior is unchanged.

## Screenshot

<img width="684" height="288" alt="Screenshot 2026-06-08 at 15 40 47"
src="https://github.com/user-attachments/assets/194b3cd0-619d-45fb-b587-cf3e2b1b8a1d"
/>

## Testing

- `just test -p codex-app-server-protocol`
- `just test -p codex-rollout-trace`
- Added focused coverage for activity analytics, terminal trace
serialization, spawn-edge reduction, `interrupt_agent` classification,
TUI status rendering without aggregated command output, and clearing
stale running state after a completed turn.
2026-06-09 12:14:48 +02:00
xl-openaiandGitHub a304569c79 [codex] Return workspace directory installed plugins (#27098)
## Summary

- return installed `workspace-directory` remote plugins by default in
`plugin/installed`
- keep shared-with-me installed plugins gated behind `plugin_sharing`
- filter remote installed plugin marketplaces by canonical marketplace
name instead of coarse workspace scope

## Validation

- `just fmt`
- `just test -p codex-core-plugins`
- `just test -p codex-app-server`
- `just fix -p codex-core-plugins`
- `just fix -p codex-app-server`
- `$xin-build` targeted verification:
- `just test -p codex-core-plugins
build_remote_installed_plugin_marketplaces_from_cache_filters_by_marketplace_name`
- `just test -p codex-app-server
plugin_installed_includes_workspace_directory_without_plugin_sharing`
- `just test -p codex-app-server
plugin_installed_includes_remote_shared_with_me_plugins`
- `just test -p codex-app-server
plugin_list_omits_shared_with_me_kind_when_plugin_sharing_disabled`
2026-06-09 01:23:16 -07:00
xl-openaiandGitHub 14660c22d1 Use server app auth requirements for remote plugin install (#27085)
## Summary
- request `includeAppsNeedingAuth=true` when installing remote plugins
- return backend-provided `app_ids_needing_auth` from the remote install
client
- use those app IDs to populate `appsNeedingAuth` without refetching
accessible apps, with fallback for older responses

## Testing
- `just fmt`
- `just test -p codex-app-server`
- `just test -p codex-core-plugins`
- real app-server install/uninstall check with Notion remote plugin
- subagent review found no blocking issues
2026-06-08 21:39:35 -07:00
dffc4bf75d [codex] preserve fsmonitor for worktree Git reads (#26880)
Codex forces `core.fsmonitor=false` on internal Git commands so a
repository cannot select an executable fsmonitor helper. This also
disables Git's built-in daemon for `status`, `diff`, and `ls-files`,
turning those worktree reads into full scans in large repositories.

Read the raw effective `core.fsmonitor` value and preserve it only when
Git interprets it as true and advertises built-in daemon support through
`git version --build-options`. Query uncommon boolean spellings back
through Git using the exact effective value. Unset, false, helper paths,
malformed values, probe failures, and unsupported Git builds continue to
force `core.fsmonitor=false`.

Centralize this policy in `git-utils` while keeping process execution in
the existing local and workspace-command adapters. Probe once per
worktree workflow and reuse the result for its Git commands, including
the TUI `/diff` path. Metadata-only commands and repository discovery
remain disabled without probing. Each probe and requested Git process
keeps its own existing timeout, and the decision is not cached because
layered and conditional Git configuration can change while Codex runs.

---------

Co-authored-by: Chris Bookholt <bookholt@openai.com>
2026-06-08 21:32:46 -07:00
pakrym-oaiandGitHub 08cb633c06 [codex] Remove remote compaction failure log (#27106)
## Why

`log_remote_compact_failure` was the only consumer of the
compact-request logging payload and most of the token-usage breakdown
fields. Once that failure log is removed, keeping the surrounding
carrier types leaves dead plumbing in the compaction path and context
manager.

## What changed

- Remove `log_remote_compact_failure`, `CompactRequestLogData`, and the
v2 wrapper that only fed that log.
- Let both remote compaction implementations return the original
compaction error directly.
- Replace `TotalTokenUsageBreakdown` with a narrow helper that returns
only the remaining value needed by compaction analytics.
- Keep `estimate_response_item_model_visible_bytes` private to the
context manager implementation.

## Validation

- `cargo check -p codex-core`
2026-06-08 19:23:35 -07:00
canvrno-oaiandGitHub 0beb5c7f32 Preserve cloud requirements across TUI thread resets (#25177)
Fixes a TUI regression where thread transitions such as `/new` and
`/clear` could rebuild config without the cloud requirements loader,
allowing users to fall back to non-cloud-managed settings. The config
refresh path now preserves cloud requirements during thread
reinitialization, and config loading is moved off the deep TUI event
stack to avoid stack-overflow crashes during those reloads.

- Passes the cloud requirements loader through TUI config rebuild paths.
- Keeps cloud requirements applied for `/new`, `/clear`, `/fork`, side
conversations, and session picker transitions.
- Runs config building on a Tokio task so reloads do not occur on the
deep TUI caller stack.
- Adds regression coverage that cloud requirements survive
thread-transition config refreshes.

## Test/Repro:
  - Start Codex with a cloud requirement applied.
  - Use `/new` or `/clear`.
- The refreshed/fresh-session config should still include the cloud
requirements
  
This can be tested with any config item, at this moment for oai staff
the easiest item to test is the `mentions_v2` feature. This is currently
enabled in cloud requirements, but is not enabled by default. As a
result, prior to these changes that feature is disabled after `/new` or
`/clear`. Testing the same steps with a binary from this branch should
not drop the feature enablement.
2026-06-08 18:08:48 -07:00
yuning-oaiandGitHub c656cc4a83 Update web search citation prompt (#27096)
## Summary

- Update the web search tool prompt to require Markdown links for cited
sources.
- Explicitly tell the model not to use `turnX`-style citations in
responses.

## Context


https://openai.slack.com/archives/C0AU83S0ZQU/p1780964147777649?thread_ts=1780352049.512299&cid=C0AU83S0ZQU

## Test plan

- `git diff --check`
- `python3 scripts/format.py --check` (fails only on Rust formatter
setup: rustup cannot create temp files under `/home/dev-user/.rustup`;
Just and Python formatter checks pass when using temp cache dirs)
2026-06-09 00:55:00 +00:00
Boyang NiuandGitHub e127a0cf99 Add extra config to StoredThread, leave empty for now (#27092) 2026-06-09 00:38:35 +00:00
canvrno-oaiandGitHub 8534912df9 Show effective sandbox modes in /debug-config (#27068)
## Summary
- Render `/debug-config`'s `allowed_sandbox_modes` from the finalized
permission constraints instead of the raw requirements list.
- Add regression coverage for configured full-access and external
sandbox modes being omitted when effective permissions reject them.

## Details
`allowed_sandbox_modes` comes from managed requirements, but the final
permissions can be further constrained by derived validation rules. For
example, `permissions.filesystem.deny_read` requires sandbox
enforcement, so modes that disable or externalize Codex's sandbox are
not actually usable even if they were present in the raw requirements
TOML.

The debug renderer now enumerates the configured sandbox-mode labels and
keeps only those accepted by `Config.permissions`. That makes
`/debug-config` reflect the same effective permission-profile constraint
path used by runtime config validation, while preserving the existing
source/provenance display.

## Validation
- Added a regression test for effective sandbox-mode filtering in
`/debug-config`.
2026-06-08 17:03:52 -07:00
Felipe CouryandGitHub 4ca2e436e5 fix(tui): linkify complete bare URLs with tildes (#27088)
## Background

Bare URLs containing `~` in their path are currently only clickable up
to the tilde in the interactive TUI. For example, Codex renders the
visible text for:


`https://www.cs.tufts.edu/~nr/cs257/archive/olin-shivers/dissertation.pdf`

but the OSC 8 destination stops at `https://www.cs.tufts.edu/`. This
makes Cmd-click open the wrong location even though the terminal
recognizes the complete URL outside Codex.

Fixes #26774.

## Root Cause

The URL scanner already accepts `~`. The truncation happens earlier:
with strikethrough parsing enabled, `pulldown-cmark` splits this URL
into adjacent decoded `Event::Text` values around the tilde. The
Markdown renderer annotated each text event independently, so only the
first event still looked like a complete URL with a supported scheme.

The renderer now merges adjacent decoded text events before URL
annotation. It preserves the combined source range while retaining
parser-decoded contents, which avoids regressing entities such as
`&amp;`.

## Changes

- Add a small iterator that merges adjacent decoded Markdown text events
and their source ranges.
- Apply it at the Markdown renderer boundary before hyperlink detection.
- Add regression coverage for the reported URL in prose, wrapped table
output, and entity-decoded URLs.

## How to Test

1. Run Codex with `just c`.
2. Ask the assistant to output this exact bare URL with no Markdown link
syntax:

`https://www.cs.tufts.edu/~nr/cs257/archive/olin-shivers/dissertation.pdf`
3. Hold Cmd and hover or click the URL.
4. Confirm the complete URL, including the suffix after `~`, is one
destination.
5. Repeat with the URL inside a Markdown table and confirm wrapped
portions retain the same complete destination.

Targeted tests:

- `just test -p codex-tui url_with_tilde`
- `just test -p codex-tui merged_text_events_preserve_entity_decoding`

The full `codex-tui` test run was also executed. Its only failures were
the two existing Guardian feature-flag tests:

-
`app::tests::update_feature_flags_disabling_guardian_clears_review_policy_and_restores_default`
-
`app::tests::update_feature_flags_disabling_guardian_clears_manual_review_policy_without_history`
2026-06-08 17:02:36 -07:00
Adam Perry @ OpenAIandGitHub ffec7c0933 Add typed file URIs (#26840)
## Why

Codex needs stable `file:` URI identifiers that can cross process and
operating-system boundaries without eagerly interpreting them as native
paths. Existing fields also need to keep accepting absolute path strings
during migration.

## What changed

- Add `codex-utils-path-uri` with a validated, immutable `PathUri`
wrapper that currently accepts only `file:` URLs.
- Expose URI-level `basename`, `parent`, and `join` operations that
preserve authorities and percent encoding without guessing the source
operating system.
- Keep native conversion explicit through `AbsolutePathBuf` and the
current host rules.
- Serialize as canonical URI text while accepting both URI text and
legacy absolute native paths during deserialization.
- Add adversarial coverage for Windows-looking and POSIX paths, UNC
authorities, encoded metadata characters, non-UTF-8 POSIX paths, URI
hierarchy operations, and legacy serde round trips.
2026-06-08 16:33:41 -07:00
Celia ChenandGitHub 6042e5810e chore: preserve one more schema layer during large tool compaction (#27084)
## Summary

Some customer MCP tools expose large input schemas that exceed Codex's
compact schema budget even after description stripping. Today, the final
compaction pass collapses complex schemas starting at depth 2, which can
erase important shallow call structure such as small `anyOf` branches,
required fields, and help-mode entry points. In one reported case, this
degraded a tool schema into `query: any | any`, leaving the model
without enough structure to discover the required help call.

This change raises the deep-schema collapse boundary from depth 2 to
depth 3. That preserves one additional layer of the tool contract while
still collapsing deeper expensive subtrees to `{}` when a schema remains
over budget.

## What Changed

- Increased `MAX_COMPACT_TOOL_SCHEMA_DEPTH` from `2` to `3`.
- Updated the schema compaction traversal test to assert the new
collapse boundary.
- The resulting compacted shape keeps useful shallow structure, for
example:
  - top-level argument names
  - shallow `anyOf` branches
  - required object fields
  - nested property names one level deeper than before

## Validation

- Ran `just test -p codex-tools`: 81 tests passed.
- Ran a golden schema corpus comparison over 214 discovered tool input
schemas under `golden_schemas/*/mcp_tools/*/input_schema.json`.
- Depth 2 and depth 3 had identical percentile token counts across the
corpus.
  - Both ended with `0 / 214` schemas over 1k tokens.
- Both ended with `0 / 214` schemas over the 4,000-byte compact JSON
budget.
- Only one golden schema changed, increasing from 49 to 56 tokens, so
this does not appear to introduce a meaningful corpus-wide regression.

Corpus percentile results:

| Percentile | Depth 2 | Depth 3 |
|---|---:|---:|
| p0 | 9 | 9 |
| p10 | 31 | 31 |
| p25 | 54 | 54 |
| p50 | 81 | 81 |
| p75 | 143 | 143 |
| p90 | 290 | 290 |
| p95 | 431 | 431 |
| p99 | 600 | 600 |
| max | 832 | 832 |
2026-06-08 23:07:56 +00:00
Felipe CouryandGitHub 0473a5cc52 feat(doctor): report editor and pager environment (#27081)
## Background

This was prompted by
[#26858](https://github.com/openai/codex/issues/26858), where the
attached doctor report did not include the editor selection and I had to
[ask which editor was in
use](https://github.com/openai/codex/issues/26858#issuecomment-4653829891)
before investigating the external-editor newline issue. Capturing these
variables in doctor makes that context available up front in future
reports.

`codex doctor` is intended to capture enough local context to diagnose
startup and terminal behavior, but it did not report the environment
variables that select an external editor or configure command pagers.

The TUI [prefers `VISUAL` over
`EDITOR`](https://github.com/openai/codex/blob/56554904babcaacf4444a2cc90716880837dff7c/codex-rs/tui/src/external_editor.rs#L31-L38),
so missing or unexpected values can explain why the external-editor
shortcut fails or launches the wrong command. Pager values are also
useful inherited-shell context even though [unified exec normalizes its
effective pager variables to
`cat`](https://github.com/openai/codex/blob/56554904babcaacf4444a2cc90716880837dff7c/codex-rs/core/src/unified_exec/process_manager.rs#L60-L70).

These variables can contain arbitrary command arguments or inline
environment assignments. The human report is local, but `codex doctor
--json` may be attached to feedback, so the machine-readable report
should not include their raw contents.

## What Changed

- Report `VISUAL` and `EDITOR` in the system environment details, using
`not set` when either variable is absent.
- Report inherited `PAGER`, `GIT_PAGER`, `GH_PAGER`, and `LESS` values
when present.
- Preserve full values in local human output while reducing these fields
to `set` or `not set` in redacted JSON output.
- Add structured check, JSON-redaction, rendered-output, and snapshot
coverage.

## How to Test

1. From `codex-rs`, run Codex with explicit editor and pager variables:

   ```sh
env VISUAL='code --wait' EDITOR=vim PAGER='less -R' GIT_PAGER=delta
GH_PAGER=less LESS=-FRX \
     cargo run -p codex-cli --bin codex -- doctor --no-color
   ```

2. Confirm the `system` details show the full values for all six
variables.
3. Unset the pager variables and rerun the command. Confirm pager rows
are omitted while missing editor variables are shown as `not set`.
4. Run the same configured environment with `doctor --json`. Confirm
each configured editor or pager field is reported as `set` and none of
the raw commands or arguments appear in the JSON.

Targeted tests:

- `just test -p codex-cli` (279 tests passed)
2026-06-08 15:43:08 -07:00
richardopenaiandGitHub feca160da4 [codex] Add OTEL counter descriptions (#26091)
## Why

Metric descriptions should be declared with reusable OTEL instruments
instead of being coupled to individual consumers. Counter descriptions
are the smallest API primitive needed by the exec-server observability
work.

## What changed

- Adds `counter_with_description` while preserving the existing counter
API.
- Caches counters by name and description so instrument metadata remains
part of the declaration identity.
- Covers the exported description together with the existing value and
attribute contract.

This PR only adds counter descriptions. It does not add gauges,
second-based durations, or exec-server adoption.

## Stack

1. **#26091: counter descriptions**
2. #27057: gauge instruments
3. #27058: second-based duration histograms

Related independent coverage: #27059 tests OTLP HTTP log and trace event
export.

The `codex-exec-server` bounded service tag now stays with the
exec-server adoption change instead of this reusable infrastructure
stack.

## Validation

- `just test -p codex-otel`
- `just fix -p codex-otel`
- `just fmt`
2026-06-08 22:29:51 +00:00
xl-openaiandGitHub 381f0de531 Use cached remote plugin catalog for plugin list (#26932)
## Summary

This changes the default remote plugin marketplace listing to use the
cached global remote catalog when it is already present on disk. The
foreground `plugin/list` response can then return from the local catalog
cache instead of waiting on `/ps/plugins/list`.

When a cached global catalog was present at the start of the request,
`plugin/list` still schedules a background refresh through the existing
plugin-list background task path so the disk cache is updated for future
requests. Cache misses keep the existing synchronous remote fetch path
and write the cache, and they do not schedule an extra duplicate
background `/ps/plugins/list` refresh.

Installed/enabled state continues to come from the existing remote
installed overlay path. This change only affects the global remote
catalog directory data used by `plugin/list`.

## Testing

- `just fmt`
- `just test -p codex-app-server
plugin_list_uses_cached_global_remote_catalog_and_refreshes_it`
- `just test -p codex-core-plugins`
- `git diff --check`
2026-06-08 14:47:09 -07:00
xl-openaiandGitHub daf76a57d2 [codex] Prune stale curated plugin caches (#26934)
Curated plugin startup refresh now removes cached plugins whose names no
longer appear in the raw openai-curated marketplace. This prevents users
with the old standalone Google Sheets plugin selected locally from
continuing to load its stale cache after the curated repo drops it.

Existing config is left untouched, and plugins still present in the
marketplace continue to refresh from local curated sources.

Validation:
- `just fmt`
- `just test -p codex-core-plugins`
- `git diff --check`
2026-06-08 14:46:59 -07:00
Celia ChenandGitHub b89d91f6ff feat: support oneOf and allOf in tool input schemas (#24118)
## Why

Some connector golden schemas use JSON Schema composition keywords
beyond `anyOf`, specifically top-level or nested `oneOf` and `allOf`.
Codex currently needs to preserve those shapes when parsing MCP tool
input schemas so connector tools do not lose valid schema structure
during normalization.

To prevent an increased Responses API error rate, this PR will be merged
after the Responses API supports top-level `oneOf`/`allOf`.

## What Changed

- Adds `oneOf` and `allOf` support to `JsonSchema`, matching the
existing `anyOf` handling.
- Traverses `oneOf` and `allOf` anywhere schema children are visited,
including sanitization, definition reachability, description stripping,
and deep schema compaction.
- Adds a final large-schema compaction pass that prunes schema objects
containing `anyOf`, `oneOf`, or `allOf` to `{}` if earlier compaction
passes still leave the schema over budget.

## Validation
Golden schema token validation over `2,025` schemas under
`golden_schemas`, all parsed successfully. Token count is `o200k_base`
over compact JSON from `parse_tool_input_schema`.

| Percentile | Before PR | After oneOf/allOf | After pruning |
|---|---:|---:|---:|
| p0 | 9 | 9 | 9 |
| p10 | 63 | 64 | 64 |
| p25 | 86 | 87 | 87 |
| p50 | 125 | 128 | 128 |
| p75 | 203 | 206 | 206 |
| p90 | 327 | 333 | 333 |
| p95 | 460 | 473 | 473 |
| p99 | 763 | 779 | 779 |
| max | 891 | 955 | 955 |

Totals:

| Parser state | Total tokens |
|---|---:|
| Before PR | 345,713 |
| After oneOf/allOf | 352,686 |
| After pruning | 352,686 |

The pruning column matches the oneOf/allOf column for this corpus
because no parsed compact golden schema remains over the `4,000`
compact-byte budget after the earlier compaction passes.
2026-06-08 21:41:05 +00:00
fchen-oaiandGitHub 56554904ba [codex] Require complete main-agent skill reads (#27044)
## Summary
- require the main agent to read selected `SKILL.md` files completely,
continuing truncated or paginated reads through EOF
- require the main agent to personally read task-required instruction
references instead of delegating their interpretation
- clarify that progressive disclosure selects relevant files without
permitting partial reads
- preserve subagent use for task work when the selected skill allows it
- cover both absolute-path and aliased-root prompt variants

## Why
Partial reads can skip routing and verification requirements later in
skill instructions. Delegated summaries can also omit constraints the
main agent needs to follow. The existing "Read only enough" wording made
both behaviors appear acceptable.

## Impact
Agents should follow complete selected skill instructions while
continuing to avoid unrelated references, scripts, and assets. Subagents
remain available for task execution where permitted.

## Test plan
- `just test -p codex-core-skills` (101 passed)
- `just fmt`
- `git diff --check`
2026-06-08 14:33:57 -07:00
rhan-oaiandGitHub ee6c91d5cf [codex-analytics] stop sending codex error subreason (#27060)
## Summary
- stop emitting `codex_error_subreason` on `codex_turn_event`
- remove the transient analytics fact plumbing that copied
`CodexErr::InvalidRequest(String)` into the event
- update analytics serialization coverage accordingly

## Why
`codex_error_subreason` is a free-form copy of `InvalidRequest(String)`,
including raw provider 400 bodies in some paths. That makes it unsafe as
an analytics field because it can carry user-derived or sensitive text.

## Validation
- `just fmt`
- `just test -p codex-analytics`
2026-06-08 21:29:06 +00:00
Won ParkandGitHub 123cf62a48 Route image edits through referenced file paths (#26486)
## Why

Image edits should use the exact images selected by the model instead of
inferring edit inputs from conversation history.

## What changed

- Replaced the image tool's `action` argument with optional
`referenced_image_paths`.
- Treats omitted or empty references as generation and populated
references as editing.
- Reads referenced absolute image paths and packages them as image data
URLs for the edit request.
- Removed the previous history-selection and image-count heuristics.
- Updated direct and code-mode tool instructions and calls.
- Added an app-server integration test covering an attached image routed
to the image edit endpoint.

## Validation
- Tested end-to-end on local `just codex` with copy pasted image,
attached image, etc.
- `just test -p codex-image-generation-extension`
- `just test -p codex-app-server
standalone_image_edit_uses_attached_model_visible_image`
- `just fix -p codex-image-generation-extension`
- `just bazel-lock-check`
2026-06-08 14:23:55 -07:00
viyatb-oaiandGitHub 85fd52f7e4 Enforce configured network proxy in codex sandbox (#27035)
## Why

`codex sandbox` can start a network proxy from a configured permission
profile. Previously, sandbox-level containment was tied to managed
network requirements rather than whether a proxy was actually active.
This meant config-driven proxy policies were not consistently enforced
as the sandbox's only network path.

## What changed

- Enable proxy-only network containment whenever `codex sandbox` starts
a network proxy.
- Apply the same active-proxy check to the macOS and Linux sandbox
paths.
- Add a Linux regression test that verifies a sandboxed command cannot
establish a direct connection while the configured proxy is active.

## Test plan

- `just test -p codex-cli debug_sandbox::tests`
- `sandbox_with_network_proxy_blocks_direct_loopback_access` runs on
Linux to cover the config-driven proxy path end to end.
2026-06-08 14:03:37 -07:00
Michael BolinandGitHub e0ee491df3 cli: add -P sandbox permissions profile alias (#27054)
## Why

`codex sandbox --permissions-profile` is useful when running commands
under a named permissions profile, but the long option is cumbersome for
a debugging-oriented command. `-p` is already used for the config
profile selector, so `-P` gives the permissions profile selector a
compact, non-conflicting alias.

## What Changed

- Added `short = 'P'` to the `permissions_profile` option for the macOS,
Linux, and Windows sandbox command structs in
[`codex-rs/cli/src/lib.rs`](https://github.com/openai/codex/blob/6d9f9c5cdcaa0a156aa2dabbde259ae5e9e8bc0b/codex-rs/cli/src/lib.rs#L29-L112).
- Added parser coverage for `codex sandbox -P :workspace -- echo` in
[`codex-rs/cli/src/main.rs`](https://github.com/openai/codex/blob/6d9f9c5cdcaa0a156aa2dabbde259ae5e9e8bc0b/codex-rs/cli/src/main.rs#L2883-L2896).

## Verification

- `just test -p codex-cli` passed, including the new
`sandbox_parses_permissions_profile_short_alias` parser test.
2026-06-08 20:59:23 +00:00
pakrym-oaiandGitHub f3c1283411 Pair thread environment settings (#26687)
## Why

Thread cwd and environment selections are a single logical setting in
core: updating one without the other can silently desynchronize the
next-turn execution context. This change makes that relationship
explicit in the internal thread settings flow while preserving the
existing app-server public API shape.

## What changed

- Moved the cwd/environment pair through internal
`ThreadSettingsOverrides.environment_settings` instead of a top-level
internal `cwd` field.
- Kept `thread/settings/update` public params unchanged, with app-server
translating top-level `cwd` into the paired internal settings shape.
- Moved `Op::UserInput` environment overrides into thread settings so
user turns and settings updates use the same core path.
- Updated core, app-server, MCP, memories, sample, and test callsites to
construct the paired settings shape.

## Verification

- `git diff --check`
- Local test run starting after PR creation.
2026-06-08 13:55:15 -07:00
jifandGitHub f9a680b907 [codex] Calm multi-agent v2 usage prompts (#27037)
## Summary
- tighten the default multi-agent v2 root and subagent usage hints to
bias toward local work
- add a pre-call gate to the v2 spawn_agent description for independent,
bounded, parallelizable subtasks

## Validation
- just fmt
- started just test -p codex-core, but it was interrupted before
completion per follow-up request to commit and push immediately
2026-06-08 22:32:10 +02:00
Adam Perry @ OpenAIandGitHub f3a8074975 [codex] Clarify PR babysitter state mutations (#27038)
# Why

Codex is doing a bit too much on my PRs that it's babysitting. In
particular I'd like it to not interact with comment threads that involve
other humans -- I should be the one doing human interaction. This is
tricky because it's still very useful to be able to drop review comments
myself and have Codex iterate on them.

## What

This updates `.codex/skills/babysit-pr/SKILL.md` with an explicit GitHub
state mutation policy.
2026-06-08 13:09:06 -07:00
9e0d7f02c9 fix: preserve auto review across config and delegation (#26230)
## Why

Auto Review should remain the effective approval reviewer when settings
cross runtime boundaries. A config or app-server round trip must not
change the reviewer identity, and delegated work must not silently fall
back to user review.

This requires both a stable canonical serialized value and propagation
of the effective setting. `auto_review` is the canonical value across
protocol and app-server output, while `guardian_subagent` remains
accepted as backward-compatible input.

## What changed

- serialize `ApprovalsReviewer::AutoReview` consistently as
`auto_review` across core protocol and app-server v2
- continue accepting `guardian_subagent` when reading existing config or
client requests
- carry the active turn's approval reviewer into spawned agents
- update config/debug expectations and add delegated-task regression
coverage

## Scope

This does not change Guardian policy or remove compatibility with
existing `guardian_subagent` inputs. It preserves the selected reviewer
across serialization, config reloads, app-server settings, and delegated
task setup.

Related Guardian changes are split independently:

- #26231 adds denials and soft denials
- #26334 retries transient reviewer failures
- #26333 reuses narrowly scoped low-risk approvals
- #26232 adds TUI denial recovery

## Validation

- `just test -p codex-app-server-protocol` (224 passed)
- regression coverage for delegated task reviewer propagation
- serialization coverage for canonical `auto_review` output and legacy
`guardian_subagent` input

---------

Co-authored-by: saud-oai <saud@openai.com>
2026-06-08 18:59:50 +00:00
Michael BolinandGitHub 2375cb6449 ci: template custom runner names by repo (#27024)
## Why

These workflows currently hard-code the `codex` runner group and custom
runner labels. That makes the same workflow definitions less portable
across repository copies or renamed repos, even though the runner fleet
follows the repository name scheme. Template the runner identities from
the repository name so `openai/codex` still resolves to the existing
`codex-*` runners while other repos can use their own `<repo>-*` runner
names.

## What Changed

- Replaced custom runner `group` values such as `codex-runners` with
`${{ github.event.repository.name }}-runners`.
- Replaced custom runner labels such as `codex-linux-x64` and
`codex-windows-arm64` with `${{ github.event.repository.name }}-...`.
- Covered direct `runs-on` objects, matrix `runs_on` entries, reusable
workflow runner inputs, and release runner labels.

## Verification

- Parsed all `.github/workflows/*.yml` files as YAML with Ruby.
- Searched `.github/workflows` to confirm no hardcoded runner-field
`codex-runners` or `codex-*` labels remain.
2026-06-08 11:47:23 -07:00
mpc-oaiandGitHub 0aa9931aea [plugins] Expose marketplace source in marketplace list JSON (#27009)
## Summary
- Follow-up to #26417 and #26631
- Add `marketplaceSource` to `codex plugin marketplace list --json`
entries for configured marketplaces
- Reuse the existing `marketplaceSource` shape from `codex plugin list
--json`
- Keep human-readable marketplace list output unchanged
- Add CLI coverage for configured local and git marketplace sources

Example:

```json
{
  "marketplaces": [
    {
      "name": "debug",
      "root": "/path/to/.codex/.tmp/marketplaces/debug",
      "marketplaceSource": {
        "sourceType": "git",
        "source": "https://example.com/acme/agent-skills.git"
      }
    }
  ]
}
```

## Validation
- `just fmt`
- `just fix -p codex-cli`
- `just test -p codex-cli marketplace_list`
- `just test -p codex-cli`
2026-06-08 13:37:55 -05:00
stefanstokic-oaiandGitHub 6d8e12ac42 [codex] Speed up external agent session imports (#26637)
## Why

Importing large external-agent session histories currently starts a full
live Codex thread for every imported session. This initializes unrelated
runtime systems and repeats expensive transcript, metadata, hashing, and
ledger work.

On a 50-session, 238 MiB fixture, the existing path took roughly 70
seconds to complete the import and 77 seconds end to end.

## What changed

- Persist imported sessions directly through `ThreadStore` instead of
starting full live threads.
- Process imports through a bounded five-session pipeline.
- Parse, extract, and hash each source file in one pass.
- Move blocking source preparation onto the blocking thread pool.
- Reuse prepared content hashes and update the import ledger once per
batch.
- Avoid metadata readback for newly written rollouts.
- Preserve imported conversation history and visible thread metadata.
- Keep the implementation out of `codex-core` and avoid changes to the
public `ThreadStore` trait.

## Performance

For the same 50-session, 238 MiB fixture:

| Path | Import completion | End to end |
| --- | ---: | ---: |
| Existing import | 69.61s | 76.62s |
| This change | 5.95s | 6.58s |

All 50 sessions imported successfully with no warnings or contention
signals.

## Validation

- `just test -p codex-external-agent-sessions`
- `just test -p codex-app-server external_agent_config_import`
- Verified imports do not initialize unrelated required MCP servers.
- Verified previously imported source versions are skipped and changed
sources can be imported again.
- Verified imported rollouts remain readable through thread listing and
history APIs.
2026-06-08 14:16:32 -04:00
rhan-oaiandGitHub f1c18df9ae [codex-analytics] report compaction analytics details (#26680)
## Why

Compaction analytics adds retained image count and compaction summary
output tokens for v1.5 specifically.

## What changed

- Add nullable `retained_image_count` and `compaction_summary_tokens`
fields to `codex_compaction_event`.
- Populate them only for `responses_compaction_v2`: retained images come
from the retained v2 compacted history, and summary tokens come from
`response.completed.token_usage.output_tokens`.
- Leave local and legacy remote compaction events as `null` for these
detail fields.

## Verification

- `just fmt`
- `just fix -p codex-core`
- `just test -p codex-core
build_v2_compacted_history_counts_retained_input_images`
- `git diff --check`
2026-06-08 10:52:31 -07:00
ningyi-oaiandGitHub a815311466 Add HTTP window ID to Responses client metadata (#26923)
## Summary

- Keep the existing `x-codex-window-id` HTTP header unchanged.
- Also send the same window ID in Responses `client_metadata`, allowing
supported backend paths to surface it as
`x-client-meta-x-codex-window-id`.
- Cover normal HTTP Responses and remote compaction v2 requests without
changing window generation or compaction behavior.

## Why

In the `2026-06-06T23` production hour, all 28,729 HTTP compaction
requests had `window_id` in `x-codex-turn-metadata`, but only 73
retained the direct `x-codex-window-id` header. The request-body
`client_metadata` path is already used for installation ID and is
preserved through supported Responses API paths.

This is additive metadata only. It does not change the direct header,
request count, model input, compaction routing, window generation, or
user response behavior.

Legacy `/v1/responses/compact` is intentionally unchanged. Its current
server-side `CompressBody` schema does not accept `client_metadata` and
rejects unknown fields, so supporting that path requires a backend
schema change before the Codex client can safely send this field.

## Validation

- Current head: `219baef3c`, rebased onto `origin/main` at `26d932983`.
- The post-rebase diff remains limited to the original five files (`22`
insertions, `6` deletions); the legacy experiment remains fully
reverted.
- `just test -p codex-core
responses_stream_includes_subagent_header_on_review`: passed; validates
normal HTTP Responses metadata.
- `just test -p codex-core
remote_compact_v2_reuses_compaction_trigger_for_followups`: passed;
validates remote compaction v2.
- `just test -p codex-core
remote_manual_compact_chatgpt_auth_reuses_service_tier_and_prompt_cache_key`:
passed; validates that legacy compact keeps its accepted payload shape.
- `just test -p codex-core
remote_manual_compact_api_auth_omits_service_tier_and_reuses_prompt_cache_key`:
passed; validates the legacy API-key payload as well.
- `just fmt`: passed; an unrelated root `justfile` rewrite produced by
the formatter was discarded.
- `git diff --check origin/main...HEAD`: passed.

The focused server pytest could not start in the local monorepo
environment because test setup is missing the `dotenv` module. Server
source and tests explicitly show that `CompressBody` omits
`client_metadata` and `/v1/responses/compact` returns HTTP 400 for
unknown body fields.
2026-06-08 10:49:59 -07:00
Anton PanasenkoandGitHub b128da272e fix(app-server): avoid blocking connection cleanup (#26852)
## Why

Remote-control app-server sessions can reconnect every 5-7 seconds when
the shared transport-event queue fills. The queue's consumer handled
`ConnectionClosed` by awaiting all in-flight RPCs for the disconnected
connection. A stuck RPC therefore blocked processing of replacement
connection and initialize events until remote-control forwarding hit its
five-second timeout and reconnected again.

Related issue: N/A (internal remote-control incident investigation).

## What Changed

- Split fast RPC admission closure from draining:
`ConnectionRpcGate::close()` rejects queued and future RPCs, while
`shutdown()` continues waiting for RPCs that already started.
- Close a disconnected connection's RPC gate before spawning the
existing RPC drain and resource cleanup in a tracked background task, so
the transport-event consumer remains available without waiting for
active RPCs.
- Reap completed cleanup tasks during normal operation, drain them
during graceful shutdown, and abort them during forced shutdown.
- Add regression coverage for closing with an active RPC, rejecting
post-close requests without polling them, and preserving the existing
shutdown wait behavior.

## Verification

`just test -p codex-app-server --lib connection_rpc_gate` passes all 6
tests, including the new close-versus-drain regression coverage.
2026-06-08 10:20:54 -07:00
Jeremy RoseandGitHub 6d0e313e23 [codex] Restore release symbol artifacts with line tables (#26202)
## Summary

- Restore separate release symbol archives for macOS, Linux, and Windows
binaries.
- Build release binaries with `line-tables-only` debuginfo instead of
full debuginfo.
- Strip Unix distribution binaries after extracting symbols, preserve
Windows PDBs, and keep symbol archives available to the release job.
- Strip the packaged Linux `bwrap` binary before hashing it so the
embedded digest matches the distributed bytes.

## Root cause

The first symbol-artifact implementation enabled
`CARGO_PROFILE_RELEASE_DEBUG=full`. In the June 2 release runs, macOS
ARM primary builds reached the 90-minute timeout while still inside
`Cargo build`. After the symbol changes were reverted, the same primary
build completed in about 22 minutes. The archive step itself completed
in tens of seconds when reached.

Rust's `line-tables-only` debuginfo level preserves function names and
source locations for symbolication without emitting the heavier variable
and type information from full debuginfo.

## Validation

- Ran `just fmt` from `codex-rs`.
- Ran `just test-github-scripts` from the repository root: 23 tests
passed.
- Ran `bash -n` and `shellcheck` on
`.github/scripts/archive-release-symbols-and-strip-binaries.sh`.
- Parsed both modified workflows as YAML and ran `git diff --check`.
- Built a macOS release smoke binary with `line-tables-only`, archived
its dSYM through the restored script, stripped the production binary,
and verified that `atos` resolves `symbol_smoke_function` to
`main.rs:2`.
- Ran Linux archive-script control-flow coverage with stubbed `objcopy`
and `strip` commands.
- Ran Windows PDB archive staging coverage and verified
underscore-emitted Rust PDB names are staged under shipped hyphenated
binary names.

## Follow-up

The release workflow only runs for tags or manual dispatches, so CI
cannot dry-run the full release matrix on this PR. The next release run
will verify runner time and memory behavior under `line-tables-only`.
2026-06-08 17:16:36 +00:00
rka-oaiandGitHub 26d9329833 [codex] Exclude external tool output from memories (#26821)
## Summary

- add contains_external_context() to tool output so other tools can be
opted out of influencing memory when disable_on_external_context=true
- Classify standalone web-search output as external context (to match
behavior as hosted web search)
- Verify with integration test
2026-06-08 16:53:04 +00:00
jifandGitHub 0526cb56ac Avoid reopening v2 descendants on resume (#26997)
## Why

Multi-agent v2 residency is intended to keep only the threads that need
to be live. The existing rollout resume path still walked persisted open
descendants and reopened the entire descendant tree when resuming a v2
root, which turns resume into an eager reload of work that should stay
unloaded until it is explicitly needed.

The interrupted-agent path has a related residency issue. Interrupted
agents remain open by design, so an idle interrupted resident should be
eligible for eviction just like an idle completed or errored resident.
Otherwise a resident set full of interrupted agents can consume every v2
slot and block later spawns or reloads with `AgentLimitReached`.

## What Changed

- Return early from `resume_agent_from_rollout` after resuming a v2
thread so persisted v2 descendants are not reopened eagerly.
- Treat idle `Interrupted` v2 residents as unloadable in the LRU
residency path.
- Add focused coverage for v2 root resume leaving descendants unloaded
and for eviction of an idle interrupted v2 resident when a new slot is
needed.

## Verification

Added targeted `codex-core` tests covering:

- v2 root resume with persisted descendants, verifying only the root is
loaded after resume.
- residency eviction of an idle interrupted v2 agent when the resident
set is full.
2026-06-08 16:44:50 +02:00
jifandGitHub 8d415050fc Rename multi-agent v2 close_agent to interrupt_agent (#26994)
## Why

`close_agent` is the wrong model-facing name for the v2 operation after
the residency changes. V2 agents remain reusable by task name, and
residency/unloading owns capacity management; the exposed tool should
describe the action it actually performs: interrupt the target agent's
current turn without making the agent unavailable for future messages or
follow-up tasks.

## What changed

- Rename the multi-agent v2 tool from `close_agent` to
`interrupt_agent`.
- Keep the v1 `close_agent` surface unchanged.
- Update the v2 handler to send `Op::Interrupt`, keep interrupted agents
registered, and reject root/self targets with interrupt-specific errors.
- Route interrupt delivery through the existing dead-thread cleanup path
so stale resident entries do not keep consuming capacity.
- Update tool planning and handler tests for the new v2 surface and
semantics.

## Verification

Added focused coverage in:

- `core/src/tools/spec_plan_tests.rs`
- `core/src/tools/handlers/multi_agents_tests.rs`
2026-06-08 14:46:35 +02:00
jifandGitHub 743f5aad38 feat: count V2 concurrency by active execution (#26969)
## Why

Multi-Agent V2 concurrency should count active non-root turns, not
resident or durable agent threads. The limit is intentionally best
effort: admission checks are synchronous, but concurrent successful
checks may overshoot slightly.

## What changed

- Keep one root-derived execution limit on the shared `AgentControl`.
- Count active V2 subagent turns with an RAII guard owned by
`RunningTask`.
- Check capacity before spawning or starting an idle agent, including
direct app-server `turn/start` submissions.
- Preserve queued delivery for agents that are already running.
- Exempt automatic idle continuations so `/goal` work is not dropped
when capacity is temporarily full.
- Keep root and V1 turns outside this limiter.

## Test coverage

- `execution_guards_count_active_v2_subagent_turns`
- `execution_guards_ignore_root_and_v1_turns`
- `v2_nested_spawn_checks_shared_active_execution_capacity`
2026-06-08 14:21:28 +02:00
jifandGitHub 8f1aad58dc Ignore proc-macro-error2 advisory (#26974)
## Summary
- ignore RUSTSEC-2026-0173 in cargo-deny and cargo-audit config
- document that proc-macro-error2 is pulled in transitively via
i18n-embed-fl/age/codex-secrets
- leave the ignore temporary until codex-secrets moves off age or age
drops i18n-embed-fl

## Validation
- just fmt
- cargo deny check --hide-inclusion-graph
2026-06-08 11:39:08 +02:00