Commit Graph

7579 Commits

Author SHA1 Message Date
Cooper Gamble
482cffc020 [codex] validate bootstrap auth and redact metadata [ci changed_files] 2026-06-17 17:00:15 +00:00
Cooper Gamble
9129d0cba1 [codex] prevent api-only bootstrap from using WIF [ci changed_files] 2026-06-17 17:00:15 +00:00
Cooper Gamble
6c917d09e8 [codex] preserve local auth flows and credential denies [ci changed_files] 2026-06-17 17:00:15 +00:00
Cooper Gamble
ed2ea13f4b [codex] harden workload identity bootstrap and isolation [ci changed_files] 2026-06-17 17:00:15 +00:00
Cooper Gamble
46017a628f [codex] close workload identity context and auth gaps [ci changed_files] 2026-06-17 17:00:15 +00:00
Cooper Gamble
28aa3cea0c [codex] clean workload identity redaction test [ci changed_files] 2026-06-17 17:00:15 +00:00
Cooper Gamble
5489be5a27 [codex] address workload identity review findings [ci changed_files] 2026-06-17 17:00:15 +00:00
Cooper Gamble
c7c318280f [codex] close workload identity review gaps [ci changed_files] 2026-06-17 17:00:15 +00:00
Cooper Gamble
bb2b8ba0ae [codex] close cross-platform workload identity gaps [ci changed_files] 2026-06-17 17:00:15 +00:00
Cooper Gamble
878da562ad [codex] close workload identity review gaps [ci changed_files] 2026-06-17 17:00:14 +00:00
Cooper Gamble
be36e82566 [codex] harden workload identity auth boundaries [ci changed_files] 2026-06-17 17:00:14 +00:00
Cooper Gamble
d239918e76 [codex] productionize workload identity auth [ci changed_files] 2026-06-17 17:00:14 +00:00
Cooper Gamble
401bccc47a [codex] prototype AWS IAM workload federation [ci changed_files] 2026-06-17 17:00:14 +00:00
Cooper Gamble
7136620fcb [codex] prototype multi-provider workload identity [ci changed_files] 2026-06-17 17:00:14 +00:00
Cooper Gamble
b4e76ab3e5 [codex] enable Azure WIF in Bazel [ci changed_files] 2026-06-17 17:00:14 +00:00
Cooper Gamble
7050f9e1c6 [codex] Harden workload identity startup [ci changed_files] 2026-06-17 17:00:14 +00:00
Cooper Gamble
042b7dcfd3 [codex] Prototype Azure workload identity auth [ci changed_files] 2026-06-17 17:00:14 +00:00
Alex Zamoshchin
c78911e37f [ez][codex-rs] Support apps._default.default_tools_approval_mode (#27965)
[from codex]

## Summary

- add `default_tools_approval_mode` to `[apps._default]` and expose it
through app-server v2 `config/read`
- apply it after managed, per-tool, and per-app approval settings,
before the built-in `auto` fallback
- document the precedence, regenerate config/app-server schemas, and add
unit plus end-to-end approval coverage

## Configuration

```toml
[apps._default]
default_tools_approval_mode = "prompt"
```

The effective precedence is managed requirements, tool-specific
`approval_mode`, app-specific `default_tools_approval_mode`,
`apps._default.default_tools_approval_mode`, then `auto`.

## Test plan

- `just write-config-schema`
- `just write-app-server-schema`
- `just write-app-server-schema --experimental`
- `just test -p codex-core app_tool_policy`
- `just test -p codex-core mcp_turn_metadata`
- `just test -p codex-config`
- `just test -p codex-app-server-protocol`
- `just test -p codex-app-server config_read_includes_apps`
- `just fix -p codex-config -p codex-core -p codex-app-server-protocol
-p codex-app-server`
- `just fmt`
2026-06-17 08:50:39 -07:00
jif
0318381762 Replace SkillsManager with SkillsService (#28705)
## Why

Host skill discovery was still exposed as a manager even though it is a
process-owned service shared by sessions, the app-server catalog, and
file-watcher invalidation. The skills extension also consumed an ad hoc
loaded-skills wrapper instead of a named immutable snapshot.

## What changed

- replace `SkillsManager` with concrete `SkillsService`
- make the service cache and return immutable `HostSkillsSnapshot`
values
- migrate the skills extension host provider to the snapshot boundary
- migrate app-server catalog, watcher, and invalidation paths to the
service

This keeps the service limited to host discovery, caching, roots, and
invalidation. Catalog rendering and invocation remain extension
responsibilities for the next stacked change.
2026-06-17 17:01:06 +02:00
jif
5935a90619 app-server: keep the model cache warm (#28699)
## Why

The app server is long-lived, but its shared model cache otherwise
refreshes only when a caller needs it. Once the five-minute cache
expires, starting a thread or calling `model/list` can wait for
`/models` on the request path.

Refresh the cache in the background before it expires so foreground
callers normally use fresh local state.

## What changed

- Start an app-server worker that refreshes models immediately and then
every three minutes using the existing models-manager API.
- Hold only a weak reference to the models manager between refreshes, so
the worker does not extend its lifetime.
- Stop scheduling refreshes when the app-server lifecycle handle is shut
down or dropped. A refresh already in progress is allowed to finish.
- Adjust affected app-server test fixtures to distinguish the background
`/models` probe from the connection they are testing.

The existing models-manager cache, refresh strategies, auth handling,
ETag behavior, and concurrency semantics are unchanged.

## Testing

-
`models_refresh_worker::tests::refreshes_immediately_periodically_and_stops_when_dropped`
-
`suite::v2::remote_control::listen_off_honors_persisted_remote_control_enable`
-
`suite::v2::attestation::attestation_generate_round_trip_adds_header_to_responses_websocket_handshake`
2026-06-17 16:18:39 +02:00
jif
45f603302c Add join key for MAv2 inter-agent messages (#28561)
## Summary
This keeps inter-agent communication on the existing raw response item
path and adds a join key for MAv2 tool calls.

MAv2 `spawn_agent`, `send_message`, and `followup_task` now stamp the
originating tool call id into `ResponseItemMetadata.source_call_id` on
the raw `ResponseItem::AgentMessage`. App-server clients can join that
raw item back to the existing tool/activity event by call id, while
using the raw agent message's existing sender, receiver, and content
fields.

No new app-server `ThreadItem` or notification type is added.

## Tests
- `just fmt`
- `just write-app-server-schema`
- `just test -p codex-protocol`
- `just test -p codex-app-server-protocol`
- `just test -p codex-core
multi_agent_v2_spawn_returns_path_and_send_message_accepts_relative_path`
- `just test -p codex-core
multi_agent_v2_followup_task_completion_notifies_parent_on_every_turn`
- `just fix -p codex-protocol`
- `just fix -p codex-app-server-protocol`
- `just fix -p codex-core`
2026-06-17 14:48:56 +02:00
jif
a5229e0686 Back off registry retries during exec recovery (#28546)
## Why

PR #28512 retries a failed session recovery every 100 ms. Every Noise
recovery attempt first asks the environment registry for a fresh
connection bundle, even when the eventual failure comes from the
WebSocket or initialize handshake. During an outage, that could make
each disconnected client call the registry about 250 times during the
25-second recovery window.

## What changes

All retryable Noise recovery failures now use a separate backoff
schedule:

```text
base:    500 ms -> 1 s -> 2 s -> 4 s -> 5 s maximum
actual:  500-750 ms, 1-1.5 s, 2-3 s, 4-6 s, 5-7.5 s
```

The extra 0-50% is deterministic per-session jitter so disconnected
clients do not retry together. Direct WebSocket recovery keeps the
existing 100 ms retry because it does not re-enter the registry.
2026-06-17 11:52:23 +02:00
jif
cf17e1bc20 Resume exec-server sessions after disconnect (#28512)
Supersedes #28288 (closed).

## Why

A short WebSocket interruption currently ends every client-side process
handle, even though exec-server keeps the server session and its
processes alive for a short time.

This is especially visible for executor-backed stdio MCP servers: a
temporary connection loss becomes a permanent `Transport closed` error.
The server already has the information needed to resume the session, but
the client opens a fresh session instead of using it.

This change reconnects below the process and MCP layers. Existing
process handles stay valid, missed output is recovered, and the same
server-side processes continue running.

## State machine

One logical `ExecServerClient` stays alive while its underlying RPC
connection changes generations.

```text
                         transport closes
       +------------------------------------------------+
       |                                                v
+-------------+                                  +-------------+
|  Connected  |                                  | Recovering  |
+-------------+                                  +-------------+
       ^                                                |
       | session resumed, processes caught up           | retryable error
       +------------------------------------------------+ loops until deadline
                                                        |
                                                        | deadline or permanent error
                                                        v
                                                  +-------------+
                                                  |   Failed    |
                                                  +-------------+
```

### `Connected`

- New RPC calls use the current connection.
- Process notifications are published in sequence order.
- A disconnect only starts recovery if it came from the current
connection generation. Late events from older generations cannot replace
the active connection.

### `Recovering`

- New calls wait instead of choosing a half-connected RPC client.
- Existing process handles, wake subscriptions, and event subscriptions
stay open.
- Streaming HTTP response bodies fail immediately because their byte
streams cannot be resumed safely.
- Recovery first waits for process starts that were already in flight. A
start whose result became ambiguous is cleaned up after reconnection
instead of being silently adopted.
- The client reconnects with the learned `session_id`. The server may
briefly report that the old connection is still attached, so that error
is retried until the detach finishes.
- The notification consumer starts before the resume handshake
completes. This prevents a busy process from filling the notification
queue and blocking the initialize response.
- Before installing the new connection, the client catches up every
recoverable process with `process/read`.

### `Failed`

- Recovery stops after 25 seconds or after a permanent error.
- Waiting calls are released with one stable disconnect error.
- Existing process sessions receive a terminal failure instead of
waiting forever.

## Recovering process events

Output, exit, and close events share one sequence. During normal
operation, the client buffers early events until every lower sequence
has been published.

After reconnection, the client reads each process starting after its
last published sequence:

1. Retained output chunks are inserted by sequence number.
2. Exit and close state are reconstructed in their sequence positions.
3. Events already received as live notifications are ignored as
duplicates.
4. Newly contiguous events are published in order.
5. If the server no longer retains enough output to fill a sequence gap,
only that process is terminated and failed. The recovered connection
remains usable for other processes.

The server reports its full next event sequence for unbounded reads,
including exit and close events. Closed processes remain readable for
the same 30-second window used to retain detached sessions.

## Other details

- Detached server sessions are retained for 30 seconds, leaving margin
around the client's 25-second recovery deadline.
- Session attach and detach update the active notification sender under
the same attachment lock, so an old connection cannot clear a newly
attached sender.
- A dedicated error code distinguishes the temporary "session is still
attached" race from permanent initialization errors.
- Process starts are identity-checked on both client and server. Cleanup
from an older start cannot remove a newer process that reused the same
ID.
- Mutating requests that were already in flight when the transport
closed are not replayed, because the client cannot know whether the
server applied them. Requests started after recovery is known wait for
the replacement connection.
- We assume the server/client version stays in sync (on the before/after
this PR)

## User impact

Long-running commands and stdio MCP servers can survive a temporary
exec-server WebSocket interruption without changing process IDs or
losing output produced during the outage.
2026-06-17 10:20:39 +02:00
Won Park
1315198853 [codex] Persist built-in image results reported as generating (#28656)
## Why

#27920 stopped persisting image-generation items unless their status was
`completed`, preventing failed standalone extension items with empty
results from being saved. Built-in image generation can instead emit a
terminal `response.output_item.done` containing a complete base64 PNG
while the item status remains `generating`. In that case, app-server
emits no `savedPath`, so Codex Apps can render the inline image but
cannot expose a file artifact.

## What changed

- Persist image-generation items whenever `result` contains image data.
Failed terminal items still have empty results and remain unpersisted.
- Update the existing built-in image-generation integration test to
cover a terminal `generating` item and verify both `saved_path` and the
written PNG bytes.

## Validation

- Confirmed with a raw built-in websocket trace: the image progressed
through `in_progress`, `generating`, and `partial_image`, then emitted
one `response.output_item.done` with `status: "generating"` and a
complete PNG result.
- `just test -p codex-core builtin_image_generation_call_persisted` is
currently blocked before test execution by a pre-existing compile error
in `thread-store/src/thread_metadata_sync.rs:171`.
2026-06-17 06:03:00 +00:00
pakrym-oai
172b2218a5 core: remove redundant TurnContext and Prompt fields (#28638)
## Why

`TurnContext` had accumulated dead fields and cached projections of
values already owned by its per-turn `Config` or `ModelInfo`. Keeping
both copies made ownership unclear and allowed artificial split-brain
states, such as a compatibility hash differing from the model metadata
it came from.

`Prompt` similarly carried a write-only personality after personality
selection had already been materialized into its base instructions.

This makes the canonical owner explicit: configuration-backed values
come from `config`, model-derived values come from `model_info`, and
prompts contain only data consumed by request construction.

## What changed

- Remove the unused `ghost_snapshot`, `codex_self_exe`, and
`thread_source` fields.
- Remove duplicate `comp_hash`, `truncation_policy`, `features`,
`shell_environment_policy`, `codex_linux_sandbox_exe`, `compact_prompt`,
and `tool_mode` fields.
- Read those values directly from `TurnContext::config` or
`TurnContext::model_info` at their consumers.
- Remove the write-only `Prompt::personality` field and its constructor
assignments.
- Preserve review-turn inheritance of the parent turn's shell policy,
Linux sandbox executable, and compact prompt through the review config.

## Testing

- `cargo check -p codex-core --tests`
2026-06-16 22:17:24 -07:00
pakrym-oai
cb15c64760 Revert thread recencyAt for sidebar ordering (#28655)
## Why

Revert #27910 to remove the newly introduced thread `recencyAt`
persistence and API behavior from `main`.

## What changed

This reverts commit `fac3158c2a783095768076489815f361fa9b0db4`,
including the state migration, thread-store propagation, app-server API
surface, generated schemas, and related tests.

## Validation

Not run before opening; relying on CI for the initial fast signal.
2026-06-16 21:39:30 -07:00
Ahmed Ibrahim
0a3ad4c4ba [codex] Test code-mode variable truncation (#28471)
## Summary

Code mode has two separate truncation points: the nested tool result
returned to JavaScript and the code-mode output later recorded for the
model. These tests now verify those behaviors independently.

- Report whether `result.output` was truncated before printing it.
- Verify omitted or sufficiently large nested limits produce `Variable
truncated: False`, while allowing the printed value to be truncated
downstream.
- Verify an explicit nested limit produces `Variable truncated: True`
when the command output exceeds it.
- Use a token-policy model fixture so downstream truncation is visible
as `…N tokens truncated…`.
- Align the explicit nested-truncation expectation with the warning
header.

This PR changes test coverage only; runtime truncation behavior is
unchanged.

## Validation

- `env -u CODEX_SANDBOX_NETWORK_DISABLED RUST_MIN_STACK=8388608 cargo
test -p codex-core --test all code_mode_exec -- --nocapture` (8 passed)
2026-06-16 20:14:23 -07:00
Channing Conger
e2f074e16c code-mode: move cell state into library actor (#28599)
A code-mode cell is a single JavaScript execution that can produce
output, call tools, wait for asynchronous work, resume, or be
terminated. This PR extracts the existing per-cell run loop into a
dedicated actor that owns the cell’s lifecycle state. It is primarily an
ownership change rather than a new lifecycle contract: existing behavior
now has one clear implementation boundary.

### Architecture
The session service remains responsible for session-wide concerns:
allocating cell IDs, storing shared values, creating cells, and routing
requests to them.

Once a cell is created, its execution state belongs to its actor.
Callers interact with the actor through a handle. The actor receives two
kinds of input: runtime events and control requests.

A single event loop serializes these inputs and applies the lifecycle
rules. It tracks the current observer—the caller waiting for an
update—along with accumulated output, outstanding callbacks, runtime
state, yield deadlines, and termination progress. Observation,
termination, completion, and cleanup therefore have one consistent
owner.

When the runtime has no immediately runnable work and is waiting only on
timers or tool results, the actor can return accumulated output and
information about outstanding tool calls while keeping the cell
available to resume. On completion or termination, it performs the
appropriate callback cleanup before publishing the final result and
removing the cell from the session.

A small host interface connects the actor to session-owned facilities
such as tool dispatch, notifications, stored values, and final cell
removal, keeping those responsibilities outside the actor itself.

### Why
Previously, cell lifecycle state and coordination lived alongside
session management. The actor boundary makes each cell a self-contained
state machine with a single writer, while the service becomes a registry
and adapter around it.

This makes lifecycle behavior easier to reason about and test in
isolation. It also establishes a clean boundary for later changing where
cells run or how they communicate without recreating their lifecycle
rules.
2026-06-16 19:28:55 -07:00
charlesgong-openai
1883dedc0e [codex] Support object-valued plugin MCP manifests (#28580)
## Summary
This fixes plugin manifest parsing for MCP servers declared as an object
directly in `plugin.json`.

Before this change, Codex modeled `mcpServers` as only a string path,
for example:

```json
{
  "name": "counter-sample",
  "version": "1.1.1",
  "mcpServers": "./.mcp.json"
}
```

Some migrated plugins instead provide the server map directly in the
manifest:

```json
{
  "name": "counter-sample",
  "version": "1.1.1",
  "description": "Plugin that declares MCP servers in the manifest",
  "mcpServers": {
    "counter": {
      "type": "http",
      "url": "https://sample.example/counter/mcp"
    }
  }
}
```

That object form previously failed during install/load with an error
like:

```text
failed to parse plugin manifest: invalid type: map, expected a string
```

## What changed
- Add a manifest representation for `mcpServers` as either
`Path(Resource)` or `Object(map)`.
- Parse `plugin.json` `mcpServers` as either a string path or an object.
- Route object-valued MCP server maps through the existing plugin MCP
config parser instead of adding a second parser.
- Apply existing per-plugin MCP server policy to object-valued MCP
servers the same way as file-backed MCP servers.
- Include object-valued MCP server names in plugin telemetry/capability
metadata.
- Support object-valued MCP config for executor plugins without
requiring a `.mcp.json` filesystem read.
- Update the bundled plugin-creator validator and `plugin-json-spec.md`
so generated-plugin validation accepts the same object-valued shape.

## Compatibility
Existing plugin manifests that use `"mcpServers": "./.mcp.json"`
continue to work. Plugins can now also use the object shape shown above.

## Tests
Added coverage for the new manifest attribute shape at the install,
normal load, telemetry, and executor-provider layers:

- `install_accepts_manifest_mcp_server_objects`
- `load_plugins_loads_manifest_mcp_server_objects`
- `plugin_telemetry_metadata_uses_manifest_mcp_server_objects`
- `reads_manifest_object_config_without_executor_file_system_access`

Also smoke-tested the plugin-creator validator against both supported
forms:

- `mcpServers` as a direct object in `plugin.json`
- `mcpServers` as `"./.mcp.json"` with a companion `.mcp.json`

## Validation
- `just test -p codex-plugin`
- `just test -p codex-core-plugins`
- `just test -p codex-mcp-extension`
- `just bazel-lock-update`
- `just bazel-lock-check`
- `just fmt`
- `git diff --check`
- Focused rename/object-form rerun: `just test -p codex-core-plugins
manager::tests::load_plugins_loads_manifest_mcp_server_objects
manager::tests::plugin_telemetry_metadata_uses_manifest_mcp_server_objects
store::tests::install_accepts_manifest_mcp_server_objects`
- Focused executor rerun: `just test -p codex-mcp-extension
executor_plugin::provider::tests::reads_manifest_object_config_without_executor_file_system_access`
- `python3
codex-rs/skills/src/assets/samples/plugin-creator/scripts/validate_plugin.py
/private/tmp/codex-validator-object`
- `python3
codex-rs/skills/src/assets/samples/plugin-creator/scripts/validate_plugin.py
/private/tmp/codex-validator-path`
2026-06-16 19:22:57 -07:00
pakrym-oai
6f77491e95 thread-store: fix response fixture compilation (#28642)
## Why

A `codex-thread-store` test fixture still constructs
`ResponseItem::FunctionCallOutput` without its required `metadata`
field, preventing the crate's test targets from compiling on `main`.

## What changed

- Set the fixture's response-item metadata to `None`.

## Testing

- `cargo check -p codex-thread-store --tests`
2026-06-16 19:16:16 -07:00
Adam Perry @ OpenAI
3d02c443bc [codex] core: restore absolute turn context cwd (#28629)
## Why

#28152 jumped the gun on moving the rollout format to store URIs, and
would likely break compat with some features that don't go through the
same types as the core logic.

## What

Make `TurnContextItem.cwd` an `AbsolutePathBuf` again, remove test added
for `PathUri` serialization in rollouts. Also drops a bunch of error
paths that are no longer needed.
2026-06-16 19:05:26 -07:00
xl-openai
69bc0645ac [codex] Gate remote plugin catalog by auth (#28625)
## Summary

- Treat the remote global plugin catalog as active only when
`remote_plugin` is enabled and the current auth uses the Codex backend.
- Skip the local OpenAI curated marketplace for remote-enabled ChatGPT
users while preserving configured marketplaces.
- Keep the local curated marketplace for API-key users, unauthenticated
fallback, and ChatGPT users with `remote_plugin` disabled.
- Apply the same effective-remote gate to the remote
installed-marketplace cache.

## Root cause

The tool-suggestion discovery path unconditionally included the local
OpenAI curated marketplace. For remote-enabled ChatGPT users, that made
remote discovery additive: Codex parsed every local curated
`plugin.json` before also loading the remote catalog.

## Validation

- `just fmt`
- `cargo build -p codex-cli --bin codex`
- Targeted auth/feature matrix tests pass, including API-key auth with
`remote_plugin` enabled.
- Manual CLI validation confirmed:
  - ChatGPT + remote off includes local curated.
  - ChatGPT + remote on excludes local curated.
  - API-key auth keeps local curated when remote is enabled.
- `just test -p codex-core-plugins`: 235 passed; one unrelated existing
marketplace test failed because it loaded the developer's home
marketplace configuration.
2026-06-16 17:24:48 -07:00
Adam Perry @ OpenAI
bfe90188ad Revert "Tell codex about PathUri serde compat. (#28595)" (#28627)
This reverts commit bd2a786326, which
didn't capture all the nuance we need for this migration.
2026-06-16 17:18:20 -07:00
Jeremy Rose
fac3158c2a Add thread recencyAt for sidebar ordering (#27910)
## Summary

Add a server-owned `recencyAt` timestamp and `recency_at` thread-list
sort key for product recency ordering while preserving the existing
meaning of `updatedAt` as the latest persisted thread mutation.

This is the server-side alternative to #27697. Rather than narrowing
`updatedAt`, clients can sort the sidebar by `recency_at` and continue
treating `updatedAt` as mutation time.

Paired Codex Apps PR:
[openai/openai#1024599](https://github.com/openai/openai/pull/1024599)

## Contract

- `recencyAt` initializes when a thread is created.
- A turn start advances `recencyAt` monotonically.
- Commentary, agent output, tool results, token/accounting updates, turn
completion, archive, unarchive, resume, and generic metadata writes do
not advance it.
- `updatedAt` retains its existing behavior and continues to advance for
persisted thread mutations.
- Current servers populate `recencyAt`; the response field is optional
in generated TypeScript so clients connected to older servers can fall
back to `updatedAt`.
- Filesystem-only fallback uses existing updated/mtime ordering when
SQLite is unavailable.

## Persistence and compatibility

Migration 0038 adds second- and millisecond-precision recency columns,
backfills them from the existing updated timestamp, creates list
indexes, and includes an insert trigger so older binaries writing to a
migrated database seed recency without causing later mutations to
advance it.

Generic metadata upserts preserve existing recency values. Turn-start
updates use a dedicated monotonic touch, and process-local allocation
keeps millisecond cursor values unique. State DB list, search, read,
filtered-list repair, rollout fallback propagation, and app-server
conversions all carry the new field.

## API

`Thread` responses include:

```ts
recencyAt?: number
```

`thread/list` and `thread/search` accept:

```json
{ "sortKey": "recency_at" }
```

Generated TypeScript and JSON schemas are included.

## Validation

- `just test -p codex-state` — 146 passed
- `just test -p codex-rollout` — 69 passed
- `just test -p codex-thread-store` — 81 passed
- `just test -p codex-app-server-protocol` — 231 passed
- Focused app-server list ordering, response mapping, archive/unarchive,
and resume lifecycle tests passed
- Scoped `just fix` for state, rollout, thread-store,
app-server-protocol, and app-server
- `just fmt`
- `git diff --check`
- Independent correctness, simplicity, elegance, security, and
test-quality reviews; actionable ordering, lifecycle, query-projection,
and timestamp-uniqueness findings were addressed
2026-06-16 17:06:22 -07:00
canvrno-oai
f0cb96bcb1 PAC 1 - Add system proxy feature config surface (#26706)
## Summary

Introduces the default-off `respect_system_proxy` feature flag used to
gate first-class system PAC/proxy support for Codex-owned native
clients.

With the feature disabled or absent, behavior remains unchanged. This PR
establishes the configuration and managed-requirement surface; proxy
discovery and request routing are implemented by follow-up PRs.

## Configuration

User configuration uses the standard boolean feature form:

```toml
[features]
respect_system_proxy = true
```

Managed feature requirements use the corresponding boolean key. The
effective runtime configuration is exposed as a boolean and defaults to
`false`.

## Implementation

- Registers `respect_system_proxy` as an under-development, default-off
feature.
- Resolves user configuration and managed feature requirements into
`Config.respect_system_proxy`.
- Provides bootstrap resolution for startup paths that must evaluate the
feature before full configuration loading completes.
- Uses the standard feature CLI and config-editing behavior.
- Excludes `features.respect_system_proxy` from project-local
configuration.
- Updates the generated configuration schema.

## End-user behavior

- No networking behavior changes when the feature is absent or disabled.
- Enabling the feature makes the boolean available to the native
proxy-routing implementation in follow-up PRs.
- Repository-local configuration cannot enable the feature.

## Test coverage

Covers scalar configuration and CLI override resolution, managed
requirement constraints, bootstrap resolution, and project-local
filtering.
2026-06-16 16:54:37 -07:00
Alex Daley
a397b59887 [codex] [4/4] Simplify recommended plugin install schema (#28403)
## Summary
- Simplify recommendation-context `request_plugin_install` arguments to
`plugin_id` and `suggest_reason`.
- Derive plugin type and install action from the matched candidate while
preserving Codex-owned elicitation metadata.
- Keep the legacy list-backed schema unchanged and accept resumed calls
that still use `tool_id`.

## Stack
- #28399
- #28400
- #27704
- This PR

## Validation
- `just test -p codex-tools -p codex-core request_plugin_install` (25
passed)
- `just fix -p codex-tools -p codex-core`
- `just fmt`
- `git diff --check`
2026-06-16 23:44:42 +00:00
Adam Perry @ OpenAI
4c79527e31 core: render remote environment cwd natively (#28152)
## Why

Model-visible `<environment_context>` should match the environment of
the executor, not of the app server.

Stacked on #28146.

## What

- Keep selected environment cwd values as `PathUri` while building
environment context.
- Render cwd text using the path convention represented by the URI, with
the canonical URI as a fallback.
- Preserve compatibility with legacy `TurnContextItem.cwd` values when
reconstructing and diffing context.
- Extend the Wine-backed remote Windows test to assert that the model
sees `powershell` and `C:\windows`.
2026-06-16 16:17:47 -07:00
Alex Daley
a34da3b295 [codex] [3/4] Activate endpoint plugin recommendations (#27704)
Summary\n- Await endpoint recommendation selection while constructing
each authenticated turn, removing the first-turn cache race.\n- Snapshot
and filter endpoint candidates once per turn, then use that same set for
the bounded contextual user fragment, tool exposure, and exact install
validation.\n- Keep recommendation selection ephemeral: do not persist
recommendation state in or gate resumed threads on prior context.\n-
Hide the legacy list tool in endpoint mode and preserve legacy discovery
unchanged when the endpoint is disabled or unavailable.\n- Keep remote
plugin and connector app identities out of model-visible context and
attach them only to Codex-owned elicitation metadata.\n\nStack\n- 3/4,
based on #28400.\n- Endpoint client and cache: #28399.\n- Generalized
suggestion presentation: #28400.\n- Install-schema follow-up:
#28403.\n\nValidation\n- \n- \n- \n- \n- Full : 2,649 passed and 88
environment-dependent tests failed because this sandbox cannot write ,
nest Seatbelt, or locate auxiliary test binaries.
2026-06-16 23:04:07 +00:00
Alex Daley
587487df9e [codex] [2/4] Generalize plugin suggestion presentation (#28400)
Summary
- Add list-backed and developer-context presentations for plugin
suggestion candidates.
- Let tool planning, install validation, and request-tool copy follow
the selected presentation.
- Keep every production caller on the existing list-backed presentation,
preserving the current list tool, request schema, connector behavior,
and model-visible copy.
- Leave developer-context presentation latent until the final PR in the
stack.

Stack
- 2/3, based on #28399.
- Follow-up: #27704 activates endpoint recommendations.

Validation
- `just test -p codex-core request_plugin_install`
- `just test -p codex-core spec_plan`
- `just fix -p codex-core`
- `just fmt`
- `git diff --check`
2026-06-16 22:44:10 +00:00
Alex Daley
7e735b59ce [codex] [1/4] Add recommended plugin endpoint cache (#28399)
Summary
- Add authenticated parsing for `/ps/plugins/suggested?scope=GLOBAL`,
including remote plugin and connector app identities.
- Validate, deduplicate, sort, and cap endpoint candidates before
caching them by backend and account identity.
- Deduplicate concurrent cache misses and warm recommendations from the
existing remote-installed-plugin refresh path used at startup and after
account changes.
- Keep endpoint results model-invisible in this PR; failures and
responses without `enabled: true` resolve to legacy mode.

Stack
- 1/3. Follow-up: #28400 generalizes plugin suggestion presentation
without activating endpoint recommendations.
- Final activation: #27704.

Validation
- `just test -p codex-core-plugins recommended_plugins`
- `just fix -p codex-core-plugins`
- `just fmt`
- `git diff --check`
2026-06-16 22:22:21 +00:00
Adam Perry @ OpenAI
bd2a786326 Tell codex about PathUri serde compat. (#28595)
This addresses another wrinkle I keep having to re-prompt codex about
when migrating to cross-OS paths.
2026-06-16 15:01:22 -07:00
Adam Perry @ OpenAI
f8850cab1d app-server: preserve target-native environment cwd (#28146)
## Why

app-server may run on a different OS from the selected exec-server
environment. Parsing that environment’s cwd with the Codex host’s path
rules prevents thread startup.

## What

Carry environment cwd values as `LegacyAppPathString` at the app-server
boundary and `PathUri` internally. Existing tool-call schemas and
relative-path behavior stay host-native; remaining local-only consumers
convert explicitly and leave follow-up TODOs.

The Wine integration test verifies app-server can start a thread and
complete an ordinary turn with a Windows environment cwd from Linux.

## Validation

- `bazel test //codex-rs/core/tests/remote_env_windows:smoke-test
--test_output=errors`
- focused app-server environment-selection and protocol schema tests
- scoped Clippy for `codex-core` and `codex-app-server-protocol`
2026-06-16 21:42:28 +00:00
Adam Perry @ OpenAI
33d50234a8 Record invariants for path migration. (#28589)
## Why

Help Codex understand how to execute the migration to support cross-OS
paths.

## What

Expand the path-types skill with our goals and constraints.
2026-06-16 21:05:32 +00:00
Adam Perry @ OpenAI
322b83de5e Clarify model-generated and legacy app path types (#28577)
## Why

`ApiPathString` kind of implies that it can be used anywhere we pull a
path out of JSON, but it's not really appropriate for tool arguments
when the model might generate relative paths.

Prefer `String` for model-generated paths and we can handle the
conversion per feature for now and define a shared abstraction later if
it makes sense.

# What

Rename `ApiPathString` to `AppLegacyPathString` to clarify its role.

Expand the `path-types` skill to tell the model to leave tool args as
bare strings.
2026-06-16 20:47:43 +00:00
Adam Perry @ OpenAI
a50671e748 [codex] test exec relative additional permissions (#28587)
## Why

Review caught some would-be regressions in changes to unified_exec that
weren't surfaced in CI.

## What

Add coverage for requesting permissions through unified exec when there
are additional permissions. Previously this flow was only tested against
shell_command.
2026-06-16 20:45:57 +00:00
Channing Conger
e93516e259 code-mode: extend test coverage to lock in cell lifecycle (#28468)
This PR establishes the intended behavior as an executable contract
before a refactor of the cell runtime begins. It also fixes cases where
a second observer or termination request could replace an existing
response channel and leave the original caller unresolved.

### Behavior codified
- A cell can yield output and subsequently resume to completion.
- A caller can run a cell until it has no immediately runnable work,
receive its accumulated output and outstanding tool-call IDs, and then
resume the same cell when the awaited work is available.
- Each cell admits one active observer:
   - a second observer receives an explicit busy error
   - the existing observer remains registered and is not displaced
- A natural result (conclusion of the js module) that has already
reached the cell controller wins over a later termination request.
- Otherwise, termination preempts execution and resolves both:
  - the active observer, if present
  - the caller requesting termination
- Repeated termination requests are rejected while termination is
already in progress.
- Terminal responses are sent only after outstanding callback work has
been handled:
- natural completion drains notifications and cancels outstanding tool
calls
- termination cancels and drains both notification and tool callbacks.
- Cell removal and cell_closed notification happen after callback
cleanup
2026-06-16 13:34:16 -07:00
Adam Perry @ OpenAI
4b7351700f [codex] re-enable absolute workdir integration test (#28581)
## Why

In #28146 I missed the invariant that an absolute `exec_command` workdir
must override the environment cwd. The existing integration test would
have caught that regression, but it was ignored as flaky.

## What

Re-enable `unified_exec_respects_workdir_override`.

## Validation

`just test -p codex-core unified_exec_respects_workdir_override`
2026-06-16 20:19:41 +00:00
jameswt-oai
8a40200880 [codex-app-server-test-client] Plugin Install/Uninstall Analytics Smoke Test (#27100)
## This PR

The original [combined remote plugin analytics PR
#26281](https://github.com/openai/codex/pull/26281) mixed reusable
analytics test infrastructure, two manual smoke workflows, a metadata
refactor, and the final identity behavior. This PR adds the
account-mutating validation workflow separately so its cleanup and
recovery guarantees can be reviewed without the final analytics behavior
change.

- Add a manually invoked remote plugin install/uninstall smoke workflow.
- Require explicit account-mutation confirmation and an initially
uninstalled plugin.
- Validate the current `codex_plugin_installed` contract, where
`plugin_id` is the backend ID.
- Restore and verify the original uninstalled state, with a dedicated
recovery command.

This baseline intentionally does not require `codex_plugin_uninstalled`,
because production does not emit that event yet. The final PR will
update this smoke to require local `plugin_id`, `remote_plugin_id`, and
uninstall emission. Review this PR as the net diff against #27099.

## Testing

- `just test -p codex-app-server-test-client` (3 focused
capture/validation tests passed)
- The live workflow was previously exercised on the green combined
reference branch, and the original uninstalled account state was
restored.
- CI is green across the required platform matrix.

## Split Overview

```text
main
├── #27093  Debug analytics capture
│   └── #27099  Non-mutating plugin smoke
│       └── #27100  Remote install/uninstall smoke  ← you are here
└── #27102  Plugin telemetry metadata refactor

After #27093, #27099, #27100, and #27102 merge:
└── Final PR: add remote_plugin_id to plugin analytics
```

Review order and dependencies:

1. [#27093 Add debug-only analytics event
capture](https://github.com/openai/codex/pull/27093) (based on `main`)
2. [#27099 Add a plugin analytics smoke
workflow](https://github.com/openai/codex/pull/27099) (stacked on
#27093)
3. [#27100 Add a remote plugin analytics mutation smoke
workflow](https://github.com/openai/codex/pull/27100) **(this PR,
stacked on #27099)**
4. [#27102 Centralize plugin telemetry metadata
construction](https://github.com/openai/codex/pull/27102) (independent,
based on `main`)
5. Final remote-ID behavior PR (created after PRs 1-4 merge)

The original [#26281](https://github.com/openai/codex/pull/26281)
remains open as the green aggregate reference until the final PR is
published.
2026-06-16 12:28:45 -07:00
pakrym-oai
7baf7e467e [codex] Route MCP file uploads through environment filesystem (#27923)
## Why

Codex Apps tools can mark arguments with `openai/fileParams`, but the
execution path resolved and opened those files directly on the host.
That bypassed the selected turn environment and prevented annotated file
arguments from working with remote environments.

## What changed

- resolve annotated file arguments against the primary turn environment
- read file metadata and contents through that environment's sandboxed
`ExecutorFileSystem`
- reject files over the 512 MiB limit from metadata before reading or
transferring them
- retain the buffered upload-size check as defense in depth
- make the OpenAI upload API accept a filename and buffered contents
instead of owning local filesystem access
- describe the model-visible argument as a path in the primary
environment

This builds on #27927, which added `size` to internal filesystem
metadata.

## Testing

- `just test -p codex-api upload_openai_file_returns_canonical_uri`
- `just test -p codex-mcp
tool_with_model_visible_input_schema_masks_file_params`
- `just test -p codex-core mcp_openai_file`
- `just test -p codex-core
codex_apps_file_params_upload_environment_files_before_mcp_tool_call`
2026-06-16 11:27:46 -07:00
Channing Conger
009a2bb93d ci: run code-mode unit tests on all bazel targets (#28562)
## Why

V8 should be stable under Bazel, so the `codex-code-mode` unit tests
should run across the Bazel platform matrix. If these tests prove
unstable, we should fix the tests rather than exclude them from CI.

## What changed

- Remove the explicit `//codex-rs/code-mode:code-mode-unit-tests`
exclusion from the macOS and Linux Bazel test jobs.
- Remove the same exclusion from the native Windows post-merge job.
- Keep the existing Windows gnullvm shard coverage.

## Bazel test coverage

The target contains 26 unit tests. A fresh uncached local Bazel
execution ran all 26 with 0 failures, 0 ignored tests, and 0 filtered
tests.

PR Bazel CI selected the target on every enabled platform and reported a
cached pass:

| Platform | Passing CI job |
| --- | --- |
| macOS aarch64 | [Bazel test
passed](https://github.com/openai/codex/actions/runs/27636617545/job/81725447804)
|
| macOS x86_64 | [Bazel test passed in
2.2s](https://github.com/openai/codex/actions/runs/27636617545/job/81725448008)
|
| Linux GNU | [Bazel test passed in
0.4s](https://github.com/openai/codex/actions/runs/27636617545/job/81725447898)
|
| Linux musl | [Bazel test passed in
0.4s](https://github.com/openai/codex/actions/runs/27636617545/job/81725448117)
|
| Windows gnullvm | [Bazel test passed in shard 4/4 in
1.6s](https://github.com/openai/codex/actions/runs/27636617545/job/81725448166)
|
2026-06-16 11:26:33 -07:00