Commit Graph

331 Commits

Author SHA1 Message Date
guinness-oai
cc8325f181 Stop mirroring Codex user input into realtime (#27116)
## Why

The realtime frontend model and the backing Codex thread should present
one coherent assistant. Raw typed messages, steers, and worker reports
belong to the orchestrator; the frontend model should receive the
orchestrator's user-facing result rather than a second copy of those
inputs.

Today normal `turn/start` input is automatically inserted into the
realtime conversation, while `turn/steer` is not. Besides creating
inconsistent context, this can make the frontend model react
independently before Codex has produced the response it should speak.

## What changed

- Remove automatic accepted-user-input mirroring into realtime
- Remove the mirror-only echo-suppression flag and dead V2 prefix helper
- Preserve explicit app-to-realtime text injection and FEM-to-Codex
delegation
- Replace the positive mirror tests and obsolete snapshots with a
negative routing regression test

## Test plan

- `cargo test -p codex-core
conversation_user_text_turn_is_not_sent_to_realtime`
- `cargo test -p codex-core
conversation_startup_context_is_truncated_and_sent_once_per_start`
- `cargo test -p codex-core inbound_handoff_request_starts_turn`
2026-06-09 15:20:01 -07:00
jif
4ec3b8eeea Route hosted Apps MCP through extensions (#27191)
## Stack

- Base: #27184
- This PR is the second vertical and should be reviewed against
`jif/external-plugins-1`, not `main`.

## Why

CCA is moving toward a split runtime where the orchestrator may have no
filesystem or executor, but it still needs to activate remotely hosted
plugin components. HTTP MCP servers are the simplest complete example:
they need configuration and host authentication, but they do not need an
executor process.

The Apps MCP endpoint is currently synthesized by a special-purpose
loader inside the MCP runtime. That works locally, but it leaves hosted
MCP activation outside the extension model being established in #27184.
It also makes the Apps path a poor foundation for plugins whose skills,
MCP servers, connectors, and hooks may come from different sources or
execute in different places.

This PR moves that one behavior behind an extension-owned contribution
while preserving the existing local fallback. It deliberately does not
introduce a generic plugin activation framework.

## What changed

### MCP extension contribution

`codex-extension-api` gains an ordered `McpServerContributor` contract.
A contributor returns typed `Set` or `Remove` overlays for MCP server
configuration; later contributors win for the names they own.

The contract stays at the existing MCP configuration boundary.
Extensions do not create a second connection manager or transport
abstraction.

### Hosted Apps MCP extension

A new `codex-mcp-extension` contributes the reserved `codex_apps` server
from the existing Apps feature, ChatGPT base URL, path override, and
product SKU configuration.

When `apps_mcp_path_override` is enabled for `https://chatgpt.com`, the
resulting streamable HTTP endpoint is
`https://chatgpt.com/backend-api/ps/mcp`. The existing ChatGPT-auth gate
remains authoritative, so this server can run in an orchestrator-only
process without being exposed for API-key sessions.

### One resolved runtime view

`McpManager` now distinguishes three views:

- **configured:** config- and plugin-backed servers before extension
overlays;
- **runtime:** configured servers plus host-installed extension
contributions;
- **effective:** runtime servers after auth gating and compatibility
built-ins.

App-server installs the hosted MCP extension and uses the runtime view
for thread startup, refresh, status, threadless resource reads,
connector discovery, and MCP OAuth lookup. This keeps
`mcpServer/oauth/login` consistent with the servers exposed by the other
MCP APIs. The hosted Apps server itself continues to use existing
ChatGPT host authentication rather than MCP OAuth.

## Compatibility

Hosts that do not install the MCP extension retain the existing Apps MCP
synthesis path. This preserves current local-only, CLI, and
standalone-host behavior while app-server exercises the extension path.

Disabling Apps removes the reserved `codex_apps` entry, and losing
ChatGPT auth removes it from the effective runtime view. Executor
availability is not consulted for this HTTP transport.

## Follow-ups

The next vertical will resolve a manifest-declared stdio MCP server from
an executor-selected plugin root and execute it in the environment that
owns that root. Later verticals can add backend-owned skills, connector
metadata, hooks, durable selection semantics, and incremental local
convergence without changing the component-specific runtime boundaries
introduced here.

## Verification

Focused coverage was added for:

- contributing the hosted Apps MCP at `/backend-api/ps/mcp` without an
executor;
- requiring ChatGPT auth in the effective runtime view;
- removing a reserved configured Apps server when the Apps feature is
disabled.

`cargo check -p codex-app-server -p codex-mcp-extension -p
codex-extension-api -p codex-mcp` passed. Tests and Clippy were not run
locally under the current development instruction; CI provides the full
validation pass.
2026-06-09 22:44:16 +02:00
marksteinbrick-oai
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
jif
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
jif
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
pakrym-oai
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
Boyang Niu
e127a0cf99 Add extra config to StoredThread, leave empty for now (#27092) 2026-06-09 00:38:35 +00:00
pakrym-oai
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
jif
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
Eric Traut
479a14cf59 [2 of 2] Finish moving goal runtime to extension (#26548)
## Stack

1. [#26547](https://github.com/openai/codex/pull/26547) - [1 of 2] Align
goal extension with core behavior
2. [#26548](https://github.com/openai/codex/pull/26548) - [2 of 2] Move
goal runtime to extension

## Why

This PR completes the switch of the goal behavior to the
extension-backed runtime and removes the old core goal implementation.

## What Changed

- Installs the goal extension for app-server `ThreadManager` sessions.
- Routes app-server thread goal `get`, `set`, and `clear` through
`GoalService`.
- Uses thread-idle lifecycle emission after goal resume and snapshot
ordering so the extension can decide whether to continue the goal.
- Forwards extension goal updates through a FIFO async app-server
notification path so backpressure does not drop them or reorder updates.
- Keeps review turns from enabling goal runtime behavior.
- Plans extension tools before dynamic tools so built-in goal tool names
keep their old precedence when goals are enabled.
- Removes the old core goal runtime, core goal tool handlers, and core
goal tool specs.
- Updates tests that were coupled to the core-owned goal runtime while
leaving the legacy `<goal_context>` compatibility path in core for old
threads.
- Removes the stale cargo-shear ignore now that `codex-goal-extension`
is used by the workspace.
- Keeps realtime event matching exhaustive after removing the old
goal-specific realtime text path.


## Validation

- Ran manual `/goal` runs in TUI. Validated time accounting matched
wall-clock time and goal lifecycle state transitions.
2026-06-05 14:17:30 -07:00
Ahmed Ibrahim
8d72fb6de9 [codex] Add turn profiling analytics (#26484)
## Summary

Add flat profiling fields to `codex_turn_event` so analytics can explain
where turn wall-clock time is spent without changing tool execution
behavior.

The profile reports:
- time before the first sampling request
- sampling time across all attempts and follow-ups
- overhead between sampling requests
- time blocked in the post-sampling tool drain
- time after the final sampling request
- sampling request and retry counts

## Implementation

- Extend the existing turn timing state with constant-memory phase
accounting and one RAII phase guard.
- Observe sampling and the existing post-sampling drain only at turn
orchestration boundaries.
- Keep tool runtime, tool futures, response item handling, and turn
lifecycle values unchanged.
- Add the profiling fields directly to the existing analytics turn event
without changing app-server protocol or rollout persistence.
- Use the existing turn `status` to distinguish completed, failed, and
interrupted profiles.

Exact sampling/tool overlap is intentionally omitted because measuring
tool completion accurately would require hooks in the tool execution
path.

## Validation

- Add app-server end-to-end coverage for a single-sampling turn with no
blocking tool work.
- Add app-server end-to-end coverage for `request_user_input` blocking
followed by a second sampling request.
- CI is running on the PR; tests were not executed locally per
repository guidance.
2026-06-05 11:27:10 -07:00
iceweasel-oai
82b15b65e2 [codex] Respect Windows sandbox backend in exec policy (#26307)
## Why

Windows managed filesystem permissions can now be backed by a real
Windows sandbox. `exec-policy` was still treating the managed read-only
policy shape as if there were never a sandbox backend, so benign
unmatched commands such as PowerShell directory listings could be
rejected with `blocked by policy` even when `windows.sandbox` was
enabled.

The inverse case still needs to stay conservative: when the Windows
sandbox backend is disabled, managed filesystem restrictions are only
configuration intent, not an enforced filesystem boundary. That applies
to writable-root restricted profiles too, not just read-only profiles.

## What Changed

- Thread the effective `WindowsSandboxLevel` into exec-policy approval
decisions for shell, unified exec, and intercepted shell exec paths.
- Treat managed restricted filesystem profiles as lacking sandbox
protection only on Windows when `WindowsSandboxLevel::Disabled`.
- Exclude full-disk-write profiles from that no-backend path because
they do not rely on filesystem sandbox enforcement.
- Remove the cwd-sensitive read-only heuristic and the now-stale cwd
plumbing from exec-policy approval contexts.
- Add Windows coverage for both enabled-sandbox and disabled-backend
behavior, including a writable-root managed profile.

## Validation

- Added/updated `exec_policy` coverage for managed filesystem
restrictions, full-disk-write exclusion, enabled Windows sandbox
behavior, and disabled-backend read-only/writable-root behavior.
- `just test -p codex-core exec_policy` — 100 passed, 10 leaky
- Empirical local `codex exec` probe with `--sandbox read-only -c
'windows.sandbox="unelevated"'`: PowerShell directory listing completed
successfully.
- Disabled-backend control with Windows sandbox cleared: the same
command was rejected with `blocked by policy`.
2026-06-05 11:20:52 -07:00
pakrym-oai
86a1ddd028 Make turn diff tracker multi-env aware (#26433)
## Why

Turn diffs were tracked as one flat set of absolute paths. In
multi-environment turns, local and remote environments can report the
same path while representing different filesystems, so a single path key
can collapse distinct changes or attribute them to the wrong
environment.

The environment name is **NOT** included in the generated unified diff.
This can come later.
2026-06-05 17:31:22 +00:00
pakrym-oai
40c8f1a007 Require absolute cwd in thread settings (#26532)
## Why

Thread settings cwd overrides are expected to be resolved before they
enter core. Keeping this boundary as a plain `PathBuf` made it easy for
core/session code to keep fallback normalization and relative-path
resolution logic in places that should only receive an already-resolved
cwd.

This is intentionally the absolute-cwd-only slice: it does not change
environment selection stickiness or cwd-to-default-environment fallback
behavior.

## What changed

- Changes `ThreadSettingsOverrides.cwd`,
`CodexThreadSettingsOverrides.cwd`, and `SessionSettingsUpdate.cwd` to
use `AbsolutePathBuf`.
- Removes core-side cwd normalization/resolution from session settings
updates.
- Updates affected core/app-server test helpers and callsites to pass
existing absolute cwd values or use `abs()` helpers.

## Validation

Opening as draft so CI can start while local validation continues.
2026-06-05 09:29:15 -07:00
carlc-oai
55aa071b17 [codex] Forward turn moderation metadata through app-server (#25710)
## Why
First-party backends can supply turn-scoped moderation metadata that
app-server clients need for client-side presentation. Exposing this as
an experimental typed notification lets opted-in clients consume it
without interpreting raw Responses API events.

## What changed
- forward `response.metadata.openai_chatgpt_moderation_metadata` from
Responses API SSE and WebSocket streams as turn-scoped moderation
metadata
- emit the experimental app-server v2 `turn/moderationMetadata`
notification with `{ threadId, turnId, metadata }`
- add app-server integration coverage for the typed moderation metadata
notification

## Testing
- `just test -p codex-core
build_ws_client_metadata_includes_window_lineage_and_turn_metadata`
- `just test -p codex-core` (fails locally: 46 failures and 1 timeout,
primarily missing `test_stdio_server` and shell snapshot timeouts)
- `just test -p codex-app-server-protocol`
- `just test -p codex-app-server
turn_moderation_metadata_emits_typed_notification_v2`
- `just test -p codex-app-server` (fails locally: 792 passed, 10 failed,
and 5 timed out; failures are in existing environment-sensitive tests,
primarily because nested macOS `sandbox-exec` is not permitted)
- `just write-app-server-schema --experimental --schema-root
/tmp/codex-app-server-schema-experimental`
2026-06-05 02:41:06 -07:00
jif
5f4d06ef18 Encrypt multi-agent v2 message payloads (#26210)
## Why

Multi-agent v2 currently routes agent instructions through normal tool
arguments and inter-agent context. That means the parent model can emit
plaintext task text, Codex can persist it in history/rollouts, and the
recipient can receive it as ordinary assistant-message JSON.

This changes the v2 path so agent instructions stay encrypted between
model calls: Responses encrypts the `message` argument returned by the
model, Codex forwards only that ciphertext, and Responses decrypts it
internally for the recipient model.

## What changed

- Mark the v2 `message` parameter as encrypted for `spawn_agent`,
`send_message`, and `followup_task`.
- Treat multi-agent v2 tool `message` values as ciphertext
unconditionally.
- Store v2 inter-agent task text in
`InterAgentCommunication.encrypted_content` with empty plaintext
`content`.
- Convert encrypted inter-agent communications into the Responses
`agent_message` input item before sending the child request.
- Preserve `agent_message` items across history, rollout, compaction,
telemetry, and app-server schema paths.
- Leave multi-agent v1 unchanged.

## Message shape

The model still calls the v2 tools with a `message` argument, but that
value is now ciphertext:

```json
{
  "name": "spawn_agent",
  "arguments": {
    "task_name": "worker",
    "message": "<ciphertext>"
  }
}
```

Codex stores the task as encrypted inter-agent communication:

```json
{
  "author": "/root",
  "recipient": "/root/worker",
  "content": "",
  "encrypted_content": "<ciphertext>",
  "trigger_turn": true
}
```

When Codex builds the recipient request, it forwards the ciphertext
using the new Responses input item:

```json
{
  "type": "agent_message",
  "author": "/root",
  "recipient": "/root/worker",
  "content": [
    {
      "type": "encrypted_content",
      "encrypted_content": "<ciphertext>"
    }
  ]
}
```

Responses decrypts that item internally for the recipient model.

## Context impact

- Parent context no longer carries plaintext v2 agent task instructions
from these tool arguments.
- Codex rollout/history stores ciphertext for v2 agent instructions.
- Recipient requests receive an `agent_message` item instead of
assistant commentary JSON for encrypted task delivery.
- Plaintext completion/status notifications are still plaintext because
they are Codex-generated status messages, not encrypted model tool
arguments.

## Validation

- `just test -p codex-tools`
- `just test -p codex-protocol`
- `just test -p codex-rollout`
- `just test -p codex-rollout-trace`
- `just test -p codex-otel`
- `just write-app-server-schema`
2026-06-05 10:25:57 +02:00
Michael Bolin
a2f5874b7a core: derive exec policy filesystem policy from profile (#26499)
## Why

`PermissionProfile` already owns the runtime filesystem sandbox policy
through `file_system_sandbox_policy()`. Keeping a separate
`FileSystemSandboxPolicy` on exec-policy fallback contexts made it
possible for callers and tests to construct split states that the
production permission model should not rely on.

## What changed

- Removed `file_system_sandbox_policy` from `UnmatchedCommandContext`,
`ExecApprovalRequest`, and the intercepted Unix exec-policy context.
- Derived filesystem sandbox policy inside unmatched-command decision
logic from `PermissionProfile::file_system_sandbox_policy()`.
- Simplified shell/unified-exec callers and tests that were only
plumbing the duplicate policy through.

## Testing

Local tests not run per request; relying on remote CI.
2026-06-04 21:48:45 -07:00
Ahmed Ibrahim
8ac304c299 [codex] Support model-defined reasoning efforts (#26444)
## Summary
- accept non-empty model-defined reasoning effort values while
preserving built-in effort behavior
- propagate the non-Copy effort type through core, app-server, TUI,
telemetry, and persistence call sites
- preserve string wire encoding and expose an open-string schema for
clients
- update model selection and shortcut behavior for model-advertised
effort values

## Root cause
`ReasoningEffort` gained a string-backed custom variant, so it could no
longer implement `Copy` or rely on derived closed-enum serialization.
Existing consumers still moved effort values from shared references and
assumed a fixed built-in value set.

## Validation
- `just fmt`
- Local tests and compilation were not run per request; relying on CI.
2026-06-04 13:36:24 -07:00
pakrym-oai
d312a53e2a Remove response.processed websocket request (#26447)
## Why

The Responses websocket client no longer needs to send a follow-up
`response.processed` request after a turn response has already been
recorded. Keeping that extra acknowledgement path adds feature-gated
control flow and a second websocket request shape that no longer carries
useful behavior.

## What Changed

- Removed the `response.processed` websocket request type and sender.
- Removed the `responses_websocket_response_processed` feature flag and
schema entry.
- Removed turn and remote-compaction plumbing that only tracked response
IDs to send the acknowledgement.
- Removed tests that existed solely to cover the deleted feature path.

## Validation

- `just fix -p codex-core -p codex-api -p codex-features`
2026-06-04 13:15:50 -07:00
Adam Perry @ OpenAI
e64b469bbc Route AGENTS.md loading through environment filesystems (#26205)
## Why

Workspace-specific `AGENTS.md` loading needs to use the selected
environment filesystem so remote workspaces and child agents read
instructions from their actual environment instead of the host
filesystem. The app-server should report the same instruction sources
the initialized thread actually loaded, rather than independently
rescanning configuration and filesystem state.

## What changed

- Introduce `LoadedAgentsMd` to retain ordered user, project, and
internal instructions with their provenance.
- Load and canonicalize workspace `AGENTS.md` paths through the primary
`EnvironmentManager` environment, then render the loaded instructions
when constructing turn context.
- Expose cached loaded instruction sources from initialized threads and
use them for app-server start, resume, and fork responses.
- Preserve global `CODEX_HOME` loading and separator behavior while
excluding empty project files that did not supply model-visible
instructions.
- Add integration coverage for CLI injection, selected-environment
provenance and rendering, empty environment selection, and cached
sources on loaded-thread resume.

## Validation

- `just test -p codex-core agents_md`
- `just test -p codex-core
selected_environment_sources_match_model_visible_instructions`
- `just test -p codex-exec agents_md`
- `just test -p codex-app-server instruction_sources`
- `just test -p codex-app-server --status-level fail`
2026-06-04 12:43:07 -07:00
sayan-oai
8b1238856b core: allow excluding tool namespaces from code mode (#26320)
## Why

Research and training setups need to control which tool namespaces
appear inside code mode's nested `tools` surface without disabling those
tools entirely. This makes it possible to train against a deliberately
reduced nested-tool setup while preserving the normal direct and
deferred tool paths.

## What

- Extend `features.code_mode` to accept structured configuration while
preserving the existing boolean syntax.
- Add an exact `excluded_tool_namespaces` list under
`[features.code_mode]`:

  ```toml
  [features.code_mode]
  enabled = true
  excluded_tool_namespaces = ["mcp__codex_apps", "multi_agent_v1"]
  ```

- Filter matching canonical `ToolName` namespaces when constructing code
mode's nested router and code-mode-specific direct tool descriptions.
- Keep excluded tools registered, directly exposed in mixed code mode,
and discoverable through top-level `tool_search` when otherwise
eligible.
- Derive deferred nested-tool guidance after namespace filtering so the
`exec` description does not advertise excluded-only deferred tools.
- Preserve the boolean/table representation when materializing config
locks and update the generated config schema.

## Testing

- `just test -p codex-features`
- `just test -p codex-config`
- `just test -p codex-core load_config_resolves_code_mode_config`
- `just test -p codex-core
lock_contains_prompts_and_materializes_features`
- `just test -p codex-core
excluded_deferred_namespaces_do_not_enable_nested_tool_guidance`
- `just test -p codex-core
code_mode_excludes_configured_nested_tool_namespaces`
- `cargo check -p codex-thread-manager-sample`
2026-06-04 18:40:18 +00:00
kbazzi
9e41f8ddbe [codex-analytics] emit forked thread id on initialization (#26248)
## Why
- Thread initialization analytics do not identify the source thread for
forked threads.
- The session viewer needs this lineage to construct thread trees.
- Depends on openai/openai#987854. Do not release this change before
that backend schema change is deployed.

## What Changed
- Adds optional `forked_from_thread_id` to `codex_thread_initialized`.
- Populates it from the existing thread fork lineage for app-server and
in-process subagent initialization paths.
- Keeps it null for non-forked threads.

## Verification
- `just fmt`
- `just test -p codex-analytics`
- `just test -p codex-app-server
thread_fork_tracks_thread_initialized_analytics`
2026-06-04 11:24:12 -07:00
jif
d46a98d31a Bridge host-loaded skills into the skills extension (#26172)
## Why

The skills extension needs to become the path that exposes local host
skills without losing the behavior already owned by core skill loading.
Host skill discovery is not just `$CODEX_HOME/skills`: it also includes
config layers, bundled-skill settings, plugin roots, runtime extra
roots, and the filesystem for the selected primary environment.

Rather than making the extension reload host skills and risk drifting
from that authoritative load, this PR bridges the already-loaded
per-turn skills outcome into the extension. That lets the extension
advertise host skills and inject explicit `$skill` prompts while
preserving the same roots, disabled/hidden state, rendered paths, and
environment-backed file reads that the legacy path uses.

## What Changed

- Adds `HostLoadedSkills` in `core-skills` to wrap the turn's
`SkillLoadOutcome` and read `SKILL.md` through the filesystem that
loaded that skill.
- Stores `HostLoadedSkills` in turn extension data for normal turns and
review turns, so the skills extension can consume the loaded host
catalog without reloading it.
- Adds `HostSkillProvider` under `ext/skills/src/provider/host.rs`,
mapping host-loaded skill metadata into the skills-extension
catalog/read contract.
- Registers the host provider by default from
`codex_skills_extension::install()`.
- Preserves host skill metadata such as dependencies, disabled state,
hidden-from-prompt policy, and slash-normalized display paths.
- Passes host-loaded skills through `SkillListQuery` and
`SkillReadRequest` so explicit skill invocation reads only resources
from the loaded host catalog.
- Adds integration coverage for a real legacy
`$CODEX_HOME/skills/.../SKILL.md` skill being listed and injected
through the installed extension.

## Testing

- Added `installed_extension_loads_host_skills_from_legacy_roots` in
`ext/skills/tests/skills_extension.rs`.
- `just test -p codex-skills-extension`
2026-06-04 15:28:06 +02:00
jif
d297616d3e Gate automatic idle turns in Plan mode (#26147)
## Why

Goal idle continuation is extension-triggered model-visible work, so it
should follow one core-owned rule for when automatic work may start. In
particular, it should not jump ahead of queued user/client work, start
while another task is active, or inject a continuation turn while the
thread is in Plan mode.

Keeping this policy in `try_start_turn_if_idle` avoids passing
`collaboration_mode` or review-specific state through
`ThreadLifecycleContributor::on_thread_idle`. Active `/review` is
covered by the same active-task gate because Review turns are not
steerable.

## What Changed

- Teach `Session::try_start_turn_if_idle` to reject automatic idle turns
in Plan mode, both before reserving an idle turn and after building the
turn context.
- Document `CodexThread::try_start_turn_if_idle` as the extension-facing
gate for automatic idle work, including Plan-mode and active Review-task
behavior.
- Add focused coverage for Plan-mode rejection and active Review-task
rejection without queuing synthetic input.

## Testing

- `just test -p codex-core try_start_turn_if_idle`
2026-06-04 14:44:45 +02:00
jif
11bceb8f8b feat: catalog multi-agent v2 config (#26254)
## Why

Model metadata can now select multi-agent v2 even when a user has not
enabled `features.multi_agent_v2` in their config. Some existing configs
still set the legacy `agents.max_threads` knob for v1 multi-agent
behavior, so treating every v2 runtime as incompatible with
`agents.max_threads` would break users whose only v2 signal came from
the model catalog.

The incompatible configuration is specifically enabling
`features.multi_agent_v2` while also setting `agents.max_threads`.
Catalog-forced v2 should use the v2 concurrency setting and ignore the
legacy v1 cap instead of rejecting the config.

## What changed

- Split config validation from runtime concurrency calculation:
`effective_agent_max_threads` now just returns the effective cap for the
resolved multi-agent runtime.
- Added explicit validation for `features.multi_agent_v2` +
`agents.max_threads` at session startup.
- Preserved catalog-selected v2 behavior when `features.multi_agent_v2`
is disabled, so existing configs with `agents.max_threads` keep
starting.
- Updated model-runtime selector coverage so a catalog v2 model still
exposes v2 tools even when `agents.max_threads` is set and the config
flag is disabled.

## Validation

- `cargo check -p codex-core --lib`
- `just test -p codex-core --lib -E
"test(multi_agent_v2_feature_rejects_agents_max_threads) |
test(catalog_v2_allows_agents_max_threads_when_feature_disabled)"`
2026-06-04 00:24:40 +02:00
Michael Bolin
52b359b249 core: stop threading SandboxPolicy through exec (#25700)
## Why

#25450 attempts a broad `SandboxPolicy` removal across several unrelated
surfaces, which makes it hard to review and still leaves new helper code
moving legacy policies around. This PR is a narrower alternative:
migrate only the exec-side Windows sandbox plumbing so the review can
focus on one production path and one compatibility boundary.

The goal is to stop threading `SandboxPolicy` through exec code without
expanding the migration into app-server, protocol, telemetry, config, or
session behavior.

## What changed

- Removed `ExecRequest::compatibility_sandbox_policy()`.
- Changed the Windows restricted-token and elevated filesystem override
helpers to accept `PermissionProfile` plus the split filesystem/network
policies instead of a `SandboxPolicy`.
- Kept the remaining legacy projection local to the writable-root
comparison that still needs to compare split policy behavior against the
legacy Windows backend model.
- Rejected restricted split filesystem policies that still grant
full-disk writes before using the Windows restricted-token backend,
preserving the previous clear-failure behavior for profiles that project
to `ExternalSandbox`.
- Updated the Windows sandbox override tests to exercise the new call
shape and cover the full-write split-profile regression.

## Verification

- `just test -p codex-core windows_restricted_token`
- `just test -p codex-core windows_elevated`
2026-06-03 10:41:41 -07:00
jif
8030c36970 feat: guard git enrichment (#26175)
Skip turn git metadata enrichment when a turn has remote or multiple
executors, so we do not report the orchestrator checkout as executor
workspace metadata.

Test: `just test -p codex-core` (blocked by existing
`Session::conversation_id` compile error in `close_agent.rs`).
2026-06-03 18:36:10 +02:00
jif
c9ae0f48a1 chore: mechanical rename (#26156)
Rename `Session::conversation_id` to `Session::thread_id` with an auto
refactor in RustRover
2026-06-03 15:38:30 +02:00
jif
3389fa554e skills: resolve per-turn catalogs from turn input context (#26106)
## Why

The skills extension needs the resolved turn environments to build a
real per-turn `SkillListQuery`. The previous `TurnLifecycleContributor`
hook only had a turn id, so it could only seed a placeholder query and
never carry the executor authorities that executor-scoped skill routing
will need.

Moving catalog resolution onto `TurnInputContributor` puts the skills
extension on the same turn-preparation path that already has the
environment ids and working directories for the submitted turn, while
keeping the actual prompt injection work for follow-up changes.

## What changed

- switch `ext/skills` from `TurnLifecycleContributor` to
`TurnInputContributor`
- build `executor_authorities` from `TurnInputContext.environments` and
pass them through `SkillListQuery`
- keep storing the resolved catalog in `SkillsTurnState`, but drop the
placeholder query helper that no longer matches the real data flow
- update the extension TODOs to reflect that per-turn catalog resolution
now happens in the turn-input contributor, and that prompt/context
injection still needs to move later

## Testing

- Not run locally.
2026-06-03 13:32:55 +02:00
jif
271d5cecf2 feat: add extension turn-input contributors (#25959)
## Disclaimer
Do not use for now

## Why

Extensions can already contribute prompt fragments and request same-turn
item injection, but there was no host-owned hook for contributing
structured `ResponseItem`s while Codex is assembling a new turn's
initial model input. This change adds that seam so extensions can attach
turn-local input that depends on the submitted user input and resolved
turn environments without routing through prompt text or late injection.

## What changed

- add `TurnInputContributor` to `codex_extension_api` and export the new
`TurnInputContext` / `TurnInputEnvironment` types it receives
- teach `ExtensionRegistry` to register and expose turn-input
contributors alongside the existing extension hooks
- call registered turn-input contributors from
`core/src/session/turn.rs` while building the initial injected input for
a turn, then append their returned `ResponseItem`s after the skill and
plugin injections
2026-06-03 01:33:31 +02:00
joeflorencio-openai
d45cd26248 Switch runtime to cloud config bundle (#24622)
## Summary

- Adapts the moved `codex-cloud-config` crate from the legacy cloud
requirements endpoint to the new config bundle endpoint.
- Switches runtime consumers from `CloudRequirementsLoader` to
`CloudConfigBundleLoader` so one shared bundle supplies cloud-delivered
config and requirements.
- Removes the legacy cloud requirements domain loader path.

## Details

This intentionally keeps `codex-cloud-config` monolithic for review
lineage: the previous PR establishes the crate move, and this PR shows
the behavior change against that moved implementation. A follow-up PR
splits the module back into focused files.

The new bundle path preserves the important cloud requirements loader
semantics where intended: account-scoped signed cache, 30 minute TTL, 5
minute refresh cadence, retry/backoff, auth recovery, and fail-closed
startup loading. The cached payload changes from a single requirements
TOML string to the backend-delivered bundle, and validation rejects
malformed config or requirements fragments before cache write/use.
2026-06-02 13:18:59 -07:00
knittel-openai
b794182ea7 Populate workspace kind on Codex turn events (#25135)
## Summary
- carry `workspace_kind` from Responses API client metadata into the
turn resolved analytics fact
- serialize the optional value on `codex_turn_event`
- cover both the turn metadata source and turn event serialization

The `workspace_kind` tells us whether a thread had a project attached vs
projectless. this is an indicator for who is adopting Codex for
knowledge work outside of coding

## Testing
- `env UV_CACHE_DIR=/private/tmp/uv-cache
/private/tmp/cargo-tools/bin/just fmt`
- `env PATH=/private/tmp/cargo-tools/bin:$PATH
CARGO_HOME=/private/tmp/cargo-home UV_CACHE_DIR=/private/tmp/uv-cache
/private/tmp/cargo-tools/bin/just test -p codex-analytics`
- `env PATH=/private/tmp/cargo-tools/bin:$PATH
CARGO_HOME=/private/tmp/cargo-home UV_CACHE_DIR=/private/tmp/uv-cache
/private/tmp/cargo-tools/bin/just test -p codex-core turn_metadata`

Paired with openai/openai#970661, which keeps forwarding the same
metadata key through Responses API headers.
2026-06-02 12:46:14 -07:00
jif
9de568372d Propagate permission approval environment id (#25862)
## Stack

1. #25850 - Key request-permission grants by environment: stores and
applies sticky permission grants per environment id.
2. #25858 - Add `environmentId` to `request_permissions`: lets the model
target a selected environment and resolves relative permission paths
against it.
3. This PR (#25862) - Propagate permission approval environment id:
carries the selected environment id through approval events, app-server
requests, TUI prompts, and delegate forwarding.
4. #25867 - Add remote request permissions integration coverage:
verifies the selected remote environment across request, approval, grant
reuse, and exec.

This PR is stacked on #25858, and #25867 is stacked on this PR.

## Why

PR2 lets the model bind a `request_permissions` call to a selected
environment, but the approval event and client-facing request still
needed to carry that binding. For CCA, the user-facing prompt and
delegated approval path should know which environment the grant applies
to instead of relying on cwd alone.

## What Changed

- Added optional `environmentId` to `RequestPermissionsEvent`.
- Emit the selected environment id from core permission approval events.
- Preserve the environment id through delegate forwarding, including
cwd-based delegated requests.
- Added `environmentId` to app-server permission approval params,
generated schema/TypeScript artifacts, and README examples.
- Preserve and display the environment id in TUI permission approval
prompts.
- Updated focused core, app-server protocol, and TUI conversion
coverage.

## Testing

Not run locally per instruction. Performed read-only `git diff --check`.
2026-06-02 21:09:34 +02:00
jif
e29071e4c9 Add environmentId to request_permissions (#25858)
## Stack

1. #25850 - Key request-permission grants by environment: stores and
applies sticky permission grants per environment id.
2. This PR (#25858) - Add `environmentId` to `request_permissions`: lets
the model target a selected environment and resolves relative permission
paths against it.
3. #25862 - Propagate permission approval environment id: carries the
selected environment id through approval events, app-server requests,
TUI prompts, and delegate forwarding.
4. #25867 - Add remote request permissions integration coverage:
verifies the selected remote environment across request, approval, grant
reuse, and exec.

This PR is stacked on #25850; #25862 and #25867 are stacked on this PR.

## Why

PR1 made request-permission grants internally environment-keyed, but the
model-facing `request_permissions` tool could still only target the
primary environment. For CCA and multi-environment turns, the tool needs
an explicit way to bind a permission request to a selected attached
environment before resolving relative paths.

## What Changed

- Added optional `environmentId` to `RequestPermissionsArgs`, with
`environment_id` accepted as an alias.
- Exposed `environmentId` in the `request_permissions` tool schema and
description.
- Resolve the selected environment before parsing filesystem permission
paths, so relative paths bind to the selected environment cwd.
- Route validated tool calls through
`request_permissions_for_environment` directly instead of duplicating
environment lookup in `Session::request_permissions`.
- Reject unknown environment ids with a model-facing error.
- Updated focused request-permissions and Guardian call sites for the
new optional field.

## Testing

Not run locally per instruction.
2026-06-02 20:51:25 +02:00
rhan-oai
8e4b92d294 [codex-analytics] Track CodexErr details in turn analytics (#25707)
## Summary
- add analytics-only `CodexErr` telemetry to `codex_turn_event` while
leaving existing `turn_error` unchanged
- record terminal `CodexErr` facts from core immediately before the
existing turn error event is sent
- emit source-truth `codex_error_*` fields for downstream analytics,
including the raw `CodexErr::InvalidRequest(String)` message as
`codex_error_subreason`

## Validation
- `just test -p codex-analytics`
- attempted `just test -p codex-core`, but the local run timed out
across unrelated integration suites in this environment and is not being
used as validation
2026-06-02 11:40:35 -07:00
jif
503ec190a8 Key request-permission grants by environment (#25850)
## Stack

1. This PR (#25850) - Key request-permission grants by environment:
stores and applies sticky permission grants per environment id.
2. #25858 - Add `environmentId` to `request_permissions`: lets the model
target a selected environment and resolves relative permission paths
against it.
3. #25862 - Propagate permission approval environment id: carries the
selected environment id through approval events, app-server requests,
TUI prompts, and delegate forwarding.
4. #25867 - Add remote request permissions integration coverage:
verifies the selected remote environment across request, approval, grant
reuse, and exec.

#25858, #25862, and #25867 are stacked on this PR and should be reviewed
after it.

## Why

Multi-environment CCA turns can attach both local and remote executors,
but request-permission grants were still effectively cwd-only. Pending
permission requests tracked a cwd, while stored turn/session grants had
no environment identity, so sticky grants could be reused through the
wrong executor context.

This makes the first permission-grant step environment-aware without
changing the external `request_permissions` payload shape: omitted
environment targeting remains bound to the primary turn environment.

## What Changed

- Store turn- and session-scoped request-permission grants by
`environment_id`.
- Keep the selected `TurnEnvironmentSelection` with pending
`request_permissions` calls so approval responses normalize and record
grants against the same environment.
- Resolve relative `request_permissions` file paths against the primary
turn environment cwd instead of deprecated `turn.cwd`.
- Apply sticky grants in `shell`, `exec_command`, and `apply_patch` by
selected environment id while still using the actual tool cwd for
cwd-relative permission materialization.
- Update Guardian and request-permissions coverage for the
environment-keyed grant behavior.

## Testing

Not run locally. Added or updated focused coverage for:

- `request_permission_grants_are_environment_keyed`
-
`request_permissions_tool_resolves_relative_paths_against_primary_environment`
- related Guardian/request-permissions sticky grant tests
2026-06-02 20:16:57 +02:00
Alex Zamoshchin
4d80d808b4 [app-server][core] Add connector-level Guardian reviewer overrides (#25167)
Context: https://openai.slack.com/archives/C0B4JAF0Q2C/p1779912328647229

```
approvals_reviewer = "auto_review"

[apps.connector_5f3c8c41a1e54ad7a76272c89e2554fa]
enabled = true
approvals_reviewer = "user"
default_tools_approval_mode = "prompt"
```

<img width="230" height="84" alt="Screenshot 2026-05-31 at 11 56 34 AM"
src="https://github.com/user-attachments/assets/e319f8f7-0983-42a7-98cd-3302732fa406"
/>

<img width="841" height="233" alt="Screenshot 2026-05-31 at 11 52 42 AM"
src="https://github.com/user-attachments/assets/7ac76645-4e90-4d00-8242-f031146a22a5"
/>

-------

```
approvals_reviewer = "user"

[apps.connector_5f3c8c41a1e54ad7a76272c89e2554fa]
enabled = true
approvals_reviewer = "auto_review"
default_tools_approval_mode = "prompt"
```
<img width="195" height="83" alt="Screenshot 2026-05-31 at 12 02 27 PM"
src="https://github.com/user-attachments/assets/3d374dc8-8aa2-466f-a13f-e4ed8567aa2e"
/>
<img width="771" height="207" alt="Screenshot 2026-05-31 at 12 05 42 PM"
src="https://github.com/user-attachments/assets/105c2575-68d6-4ca6-8e69-dc8c82da36a2"
/>



## Summary
- add `apps.<connector_id>.approvals_reviewer` to override Guardian or
user review routing per connected app
- apply overrides across direct app MCP calls, delegated MCP prompts,
and app-server MCP elicitation review while preserving global behavior
for non-app MCP servers
- expose and document the config through app-server v2 and generated
schemas, while honoring global managed reviewer requirements

---------

Co-authored-by: jif-oai <jif@openai.com>
2026-06-02 17:04:11 +02:00
jif-oai
1dd731305a Reduce stack pressure in session startup and config rebuilds (#25844)
## Why

`/clear` starts a fresh thread with `InitialHistory::Cleared`, which
re-enters the thread/session startup path. That path now builds large
async futures through `ThreadManagerState::spawn_thread_with_source`,
`Codex::spawn`, and `Session::new`. Separately, TUI config rebuilds for
cwd and permission-profile changes build a similarly heavy
`ConfigBuilder::build()` future inside the app task. In debug and Bazel
runs, those call chains can put enough state on the caller stack to
abort before startup or config refresh completes.

This change keeps the behavior the same while moving the heaviest future
frames off the caller stack.

## What changed

- Box `Codex::spawn(...)` in `codex-rs/core/src/thread_manager.rs`
before awaiting it from `spawn_thread_with_source`.
- Box `Session::new(...)` in `codex-rs/core/src/session/mod.rs` before
awaiting it from `Codex::spawn_internal`.
- Route `ConfigBuilder::build()` through a small `tokio::spawn` helper
in `codex-rs/tui/src/app/config_persistence.rs` so cwd and
permission-profile config rebuilds run on a runtime worker stack while
preserving error context.

## Verification

CI is running on the PR.

No new targeted tests were added. This is a mechanical stack-pressure
reduction that keeps the existing behavior and error propagation intact.
2026-06-02 15:42:47 +02:00
jif-oai
3cf6f08da5 session: keep startup prewarm aligned with resolved multi-agent runtime (#25841)
## Why

Follow-up to #25722. Startup prewarm builds a preview `TurnContext`
before the first real turn so it can precompute the initial prompt and
tool surface. After the per-thread runtime work landed, that preview
path still recomputed multi-agent mode from `model_info` and feature
defaults instead of reusing the runtime the session had already resolved
from persisted metadata or inheritance.

That could leave the prewarmed session primed for a different
multi-agent mode than the first real turn, which is especially risky
because collaboration tool exposure depends on
`turn_context.multi_agent_version`.

## What changed

- In the `TurnMultiAgentRuntime::Preview` path, prefer
`Session::multi_agent_version()` when it is already known.
- Only fall back to `model_info.multi_agent_version` and feature
defaults when the session has not resolved a runtime yet.
- Keep preview mode read-only: this still avoids storing a runtime
during startup prewarm.

## Testing

- Not run (small runtime-selection follow-up)
2026-06-02 14:35:26 +02:00
jif-oai
bf9fd885b2 Resolve per-thread multi-agent runtime (#25722)
Stack split from #25708. Original PR intentionally left open. This third
PR resolves the effective per-thread multi-agent runtime from persisted
metadata, inherited runtime, and current model selection.
2026-06-02 14:31:00 +02:00
jif-oai
0c5ccd18ab Persist multi-agent runtime metadata (#25721)
Stack split from #25708. Original PR intentionally left open. This
second PR persists multi-agent runtime metadata through thread creation,
rollout recording, and thread storage.
2026-06-02 13:05:20 +02:00
jif-oai
3f1fb7ed8b Add multi-agent runtime metadata types (#25720)
Stack split from #25708. Original PR intentionally left open. This first
PR adds the multi-agent runtime metadata types and catalog plumbing used
by the rest of the stack.
2026-06-02 12:10:14 +02:00
xl-openai
f2b725102b [codex] Cache remote plugin catalog for suggestions (#25457)
## Summary
- cache the global remote plugin catalog when remote plugin listing runs
and warm it during startup
- use the cached remote catalog in plugin install recommendations with
canonical `plugin@openai-curated-remote` ids
- reuse the session `PluginsManager` for plugin recommendations so
remote cache state is visible on the recommend path
- skip core installed-state verification for remote plugin install
suggestions while leaving local plugin and connector verification
unchanged

## Testing
- `just fmt`
- `git diff --check`
- `cargo test -p codex-core
list_tool_suggest_discoverable_plugins_includes_cached_remote_global_plugins`
- `cargo test -p codex-core
remote_plugin_install_suggestions_skip_core_installed_verification`
- `cargo test -p codex-app-server
plugin_list_includes_remote_marketplaces_when_remote_plugin_enabled`

Earlier focused checks during the same branch: codex-tools TUI filter
test, request_plugin_install tests, and codex-app-server build.
2026-06-01 22:10:52 -07:00
efrazer-oai
c8e5db16c9 feat: show enterprise monthly credit limits in status (#24812)
## Summary

Enterprise users can have an effective monthly credit limit, but Codex
`/status` currently drops that metadata from the account-usage response.

This change adds the optional `spend_control.individual_limit`
projection to the existing rate-limit snapshot flow. The backend client
reads the monthly limit, app-server exposes it as `individualLimit`, and
the TUI renders a `Monthly credit limit` row through the existing
progress-bar renderer.

When the backend does not return an effective monthly limit, existing
rate-limit behavior is unchanged.

## Existing backend state

The account-usage backend already returns the effective monthly limit
and current usage together:

```json
{
  "spend_control": {
    "reached": false,
    "individual_limit": {
      "limit": "25000",
      "used": "8000",
      "remaining": "17000",
      "used_percent": 32,
      "remaining_percent": 68,
      "reset_after_seconds": 86400,
      "reset_at": 1778137680
    }
  }
}
```

Before this change, Codex projected rolling `primary` and `secondary`
windows plus `credits`. It ignored `spend_control.individual_limit`, so
app-server clients and `/status` could not render the monthly cap.

The updated flow is:

```text
account usage backend
  -> backend-client reads spend_control.individual_limit
  -> existing rate-limit snapshot carries optional individual_limit
  -> app-server exposes optional individualLimit
  -> TUI renders Monthly credit limit
```

## App-server contract

`account/rateLimits/read` and sparse `account/rateLimits/updated`
notifications now include an additive nullable
`rateLimits.individualLimit` field:

```json
{
  "individualLimit": {
    "limit": "25000",
    "used": "8000",
    "remainingPercent": 68,
    "resetsAt": 1778137680
  }
}
```

In an `account/rateLimits/read` response, `null` means no monthly limit
is available. `account/rateLimits/updated` remains a sparse rolling
notification: clients merge available values into their most recent
`account/rateLimits/read` snapshot or refetch. Nullable account metadata
in a rolling notification does not clear a previously observed value.

## Design decisions

- Extend the existing rate-limit snapshot instead of introducing a
separate request or wire-level update protocol.
- Keep the Codex projection narrow: `/status` needs the effective limit,
current usage, remaining percentage, and reset timestamp.
- Render the monthly row through the existing progress-bar renderer,
with one optional detail line for `8,000 of 25,000 credits used`.
- Keep the backend response optional so existing accounts and older
usage states preserve their current behavior.
- Preserve cached monthly metadata when sparse rolling notifications
omit it. Live account-usage reads remain authoritative and can clear a
removed limit.

## Visual evidence

```text
 Monthly credit limit:   [██████████████░░░░░░] 68% left (resets 07:08 on 7 May)
                         8,000 of 25,000 credits used
```

Snapshot:
`codex-rs/tui/src/status/snapshots/codex_tui__status__tests__status_snapshot_includes_enterprise_monthly_credit_limit.snap`

## Testing

Tests: generated app-server schema verification, protocol tests,
backend-client tests, app-server integration coverage, TUI snapshot
coverage, formatting, and workspace lint cleanup.
2026-06-01 21:25:42 -07:00
Owen Lin
11e0f3d3ae app-server: remove experimental persist_extended_history bool flag (#25712)
## Summary

Remove the dead experimental `persistExtendedHistory` app-server flag
and collapse rollout persistence to the single policy app-server already
used.

## What Changed

- Removed `persistExtendedHistory` from v2 thread start/resume/fork
params and deleted its deprecation notice path.
- Removed the persistence-mode enums and plumbing through core, rollout,
and thread-store.
- Made rollout filtering mode-free, keeping the existing limited
persisted-history behavior.

## Test Plan

- `just write-app-server-schema`
- `cargo nextest run --no-fail-fast -p codex-app-server-protocol
schema_fixtures`
- `cargo nextest run --no-fail-fast -p codex-app-server
thread_shell_command_history_responses_exclude_persisted_command_executions`
- `cargo nextest run --no-fail-fast -p codex-rollout -p
codex-thread-store`
- final `rg` for removed flag/type names
2026-06-01 23:33:42 +00:00
Michael Bolin
d6748f741a feat: gate unified exec zsh fork composition (#24979)
## Why

`shell_zsh_fork` and unified exec need to remain independently
controllable for enterprise rollouts, but we also need a third mode that
composes them. That composed mode is intended to preserve unified exec
command lifecycle support while letting the zsh fork provide more
accurate `execv(2)` interception.

Enabling `unified_exec_zsh_fork` by itself is intentionally not
sufficient. It is a composition gate, not a dependency-enabling
shortcut:

- `unified_exec` selects the PTY-backed unified exec tool.
- `shell_zsh_fork` opts into the zsh fork backend.
- `unified_exec_zsh_fork` only allows those two already-enabled modes to
be composed so local zsh unified exec commands can launch through the
zsh fork.

This separation is deliberate. Enterprises and staged rollouts must be
able to enable or disable unified exec and zsh-fork independently. If
`unified_exec_zsh_fork` implied either dependency, then enabling one
under-development composition flag would silently activate a shell
backend that the configured feature set left disabled.

This PR introduces only the configuration and planning gate for that
composition. Existing `shell_zsh_fork` behavior continues to use the
standalone shell tool unless the new composition feature is explicitly
enabled alongside both dependencies.

## What Changed

- Added the under-development feature flag `unified_exec_zsh_fork`.
- Added `UnifiedExecFeatureMode` so the three input feature flags
collapse into `Disabled`, `Direct`, or `ZshFork` mode before tool
planning.
- Updated tool selection so zsh-fork composition requires
`unified_exec`, `shell_zsh_fork`, and `unified_exec_zsh_fork`.
- Kept the existing standalone zsh-fork shell tool behavior when only
`shell_zsh_fork` is enabled.
- Updated config schema output for the new feature flag.

## Verification

- Added feature and tool-config coverage for the new gate.
- Added planner coverage proving `shell_zsh_fork` remains standalone
until composition is explicitly enabled.
- Ran focused tests for `codex-features`, `codex-tools`, and the
affected `codex-core` planner case.





---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/24979).
* #24982
* #24981
* #24980
* __->__ #24979
2026-06-01 13:01:36 -07:00
jif-oai
48c16b8bcb Remove Plan-mode gate from idle turn injection (#25577)
## Why

`try_start_turn_if_idle` is the core helper for starting injected input
only when the session is actually idle. It should stay focused on
generic turn-lifecycle safety. The previous `ModeKind::Plan` guard mixed
caller policy into that helper: Plan mode may choose not to auto-start
some extension work, but that decision belongs at the extension or
caller boundary rather than in the session injection primitive.

## What changed

- Removed the `ModeKind::Plan` early return from
`Session::try_start_turn_if_idle`.
- Removed the now-unused `ModeKind` import from
`core/src/session/inject.rs`.

## Testing

Not run locally.
2026-06-01 11:06:38 +02:00
jif-oai
f1b1b64005 Add goal extension idle continuation (#25060)
## Why

The goal extension needs a way to resume an active goal after the thread
becomes idle, but the old core goal runtime should not be refactored as
part of this step. The missing piece is a small core-owned turn-start
primitive: let an extension ask for a normal model turn only when the
thread is idle, and otherwise fail without injecting into whatever is
currently active.

## What Changed

- Adds `CodexThread::try_start_turn_if_idle(...)` as the narrow
extension-facing primitive for synthetic idle work.
- Implements the session side so it refuses to start when:
  - the provided input is empty,
  - the session is in plan mode,
  - a turn is already active, or
  - trigger-turn mailbox work is pending.
- Gives trigger-turn mailbox work priority if it appears while the idle
turn is being prepared.
- Wires `GoalExtension::on_thread_idle` to read the active persisted
goal and submit the continuation prompt through this idle-only
primitive.
- Keeps the legacy core goal continuation implementation in place
instead of folding it into this PR.

## Behavior

This is intentionally best-effort. If `try_start_turn_if_idle` observes
that the thread is not idle, or that higher-priority mailbox work should
run first, it returns the input to the caller. The goal extension drops
that continuation prompt and waits for a future idle opportunity instead
of injecting stale synthetic goal text into an active turn.

## Validation

- `just test -p codex-core
try_start_turn_if_idle_rejects_active_turn_without_injecting`
- `just test -p codex-goal-extension`
2026-06-01 10:42:01 +02:00
jif-oai
8d49394feb Set multi-agent v2 dogfood defaults (#25266)
## Summary
- default multi-agent v2 to direct-model-only tools so code mode does
not wrap subagent tools
- add default root/subagent team prompts aligned with dogfood training
assumptions
- tighten spawn-agent model override wording to prefer the inherited
model by default

## Tests
- just fmt
- just test -p codex-core
spawn_agent_description_lists_visible_models_and_reasoning_efforts
- just test -p codex-core
multi_agent_v2_default_session_thread_cap_counts_root
- just test -p codex-rollout-trace
- just fix -p codex-core
- just fix -p codex-rollout-trace

Note: a broad just test -p codex-core run was attempted locally, but
this sandbox produced unrelated environment failures around
sandbox-exec, missing test_stdio_server, and realtime timeouts.
2026-06-01 10:24:46 +02:00
Owen Lin
cf0911076f store and expose parent_thread_id on Threads (#25113)
## Why

This PR
https://github.com/openai/codex/pull/24161#discussion_r3325692763
revealed a subagent data modeling issue, where we overloaded
`forked_from_id` to also mean `parent_thread_id`. That's incorrect since
guardian and review subagents can be a subagent and NOT fork the main
thread's history.

The solution here is to explicitly store a new `parent_thread_id` on
`SessionMeta`, alongside `forked_from_id` which already exists. While
we're at it, also expose it in the app-server protocol on the `Thread`
object.

A thread->subagent relationship and a fork of thread history are
orthogonal concepts.

## What Changed

- Added top-level `parent_thread_id` persistence on `SessionMeta` and
runtime/session plumbing through `SessionConfiguredEvent`,
`CodexSpawnArgs`, `SessionConfiguration`, `ThreadConfigSnapshot`,
`TurnContext`, and `ModelClient`.
- Made turn metadata, request headers, analytics, and subagent-start
events read the separate runtime/top-level parent field instead of
deriving general parent lineage from `SessionSource` or
`forked_from_thread_id`.
- Passed parent lineage separately at delegated subagent, review,
guardian, agent-job, and multi-agent spawn construction sites;
copied-history fork lineage remains derived only from `InitialHistory`.
- Persisted and exposed parent lineage through rollout/thread-store
projections and app-server v2 `Thread.parentThreadId`.
- Updated app-server README text and regenerated app-server schema
fixtures for the additive `parentThreadId` response field.
2026-06-01 04:33:20 +00:00