Commit Graph

3348 Commits

Author SHA1 Message Date
Michael Bolin
c58c84d6ee test: fix multi-agent service tier assertion (#23576)
## Why

`openai/codex#22169` added a regression test that expects an invalid
child `service_tier` to be rejected, but the test used
`Result::expect_err` on `SpawnAgentHandler::handle`. That requires the
`Ok` type to implement `Debug`, and this handler returns `Box<dyn
ToolOutput>`, so Bazel failed while compiling `codex-core` tests before
it could run them.

## What changed

- Capture the handler result and assert on `result.err()` instead of
calling `expect_err`.
- Keep the same `FunctionCallError::RespondToModel` assertion for the
rejected service tier.

## Verification

- `cargo test -p codex-core
spawn_agent_role_service_tier_does_not_hide_invalid_spawn_request`
2026-05-19 16:47:20 -07:00
Matthew Zeng
b019a678d8 Remove unused ARC monitor path (#23573)
## Summary
- remove the unreachable ARC monitor path from MCP tool approval
handling
- delete the unused ARC monitor module/tests and trim the orphaned
safety-monitor decision plumbing
- keep `always allow` approvals on the existing auto-approval
short-circuit without a dead monitor hop

## Testing
- `cargo test -p codex-core mcp_tool_call`
- `just fmt`
- `just fix -p codex-core`
- `git diff --check`

## Additional validation
- Attempted `cargo test -p codex-core`; the library test target passed,
then the integration target failed in this local environment.
- The narrower MCP-focused rerun passed its unit coverage and only hit
missing local `test_stdio_server` binaries in filtered integration
cases.
2026-05-19 16:23:25 -07:00
adams-oai
d86352d520 Add CUA requirements subsection for locked computer use (#23555)
Adds a new top-level section for "CUA" requirements that can allow for
disablement of specific features as needed for enterprises.
2026-05-19 15:41:44 -07:00
Ahmed Ibrahim
c53da029bc [codex] Honor role-defined spawn service tiers (#22169)
## Why
Custom agent roles are ordinary config layers, so a role file can
already express `service_tier` just like other config values. The
spawned-agent tier path needs to preserve that effective role config and
follow the same precedence pattern as model/reasoning.

## What changed
- Apply an explicit spawn-time `service_tier` onto the child config
before role application, so a role config layer can override it just
like role-defined model/reasoning settings do.
- Validate the final effective child tier after the final child model is
known, while still falling back to the parent tier when no child tier
survives.
- Add focused integration coverage for both v1 and v2 proving role TOML
loads a service tier, spawned children keep that role-configured tier,
and a role tier wins over a conflicting spawn-time tier.

## Validation
- `just fmt`
- `git diff --check`
- Local Rust tests not run, per repo guidance; CI should exercise the
new coverage.
2026-05-19 22:40:41 +00:00
Matthew Zeng
8335b56c33 Split plugin install discovery into list and request tools (#23372)
## Summary
- Add `list_available_plugins_to_install` as the inventory step for
plugin and connector install suggestions.
- Slim `request_plugin_install` so it only handles the actual
elicitation, instead of carrying the full discoverable list in its
prompt.
- Emit send-time telemetry when an install elicitation is dispatched,
including requested tool identity in the event payload.
- Emit install-result telemetry through `SessionTelemetry`, including
tool type, user response action, and completion status.
- Update registration and tests to cover the new two-step flow while
keeping the existing `tool_suggest` feature gate unchanged.

## Testing
- `just fmt`
- `cargo test -p codex-tools`
- `cargo test -p codex-core request_plugin_install`
- `cargo test -p codex-core list_available_plugins_to_install`
- `cargo test -p codex-core
install_suggestion_tools_can_be_registered_without_search_tool`
- `cargo test -p codex-otel
manager_records_plugin_install_suggestion_metric`
- `cargo test -p codex-otel
manager_records_plugin_install_elicitation_sent_metric`
- `just fix -p codex-core`
- `just fix -p codex-tools`
- `just fix -p codex-otel`
- `cargo check -p codex-core`
2026-05-19 14:45:37 -07:00
starr-openai
5c43a64e2b Make local environment optional in EnvironmentManager (#23369)
## Summary
- make `EnvironmentManager` local environment/runtime paths optional
- simplify constructor surface around snapshot materialization
- rename local env accessors to `require_local_environment` /
`try_local_environment`

## Validation
- devbox Bazel build for touched crate surfaces
- `//codex-rs/exec-server:exec-server-unit-tests`
- `//codex-rs/app-server-client:app-server-client-unit-tests`
- filtered touched `//codex-rs/core:core-unit-tests` cases
2026-05-19 12:55:34 -07:00
Abhinav
d661ab70ed Add SubagentStart hook (#22782)
# What

`SubagentStart` runs once when Codex creates a thread-spawned subagent,
before that child sends its first model request. Thread-spawned
subagents use `SubagentStart` instead of the normal root-agent
`SessionStart` hook.

Configured handlers match on the subagent `agent_type`, using the same
value passed to `spawn_agent`. When no agent type is specified, Codex
uses the default agent type.

Hook input includes the normal session-start fields plus:

- `agent_id`: the child thread id.
- `agent_type`: the resolved subagent type.

`SubagentStart` may return `hookSpecificOutput.additionalContext`. That
context is added to the child conversation before the first model
request.

# Lifecycle Scope

Only thread-spawned subagents run `SubagentStart`.

Internal/system subagents such as Review, Compact, MemoryConsolidation,
and Other do not run normal `SessionStart` hooks and do not run
`SubagentStart`. This avoids exposing synthetic matcher labels for
internal implementation paths.

Also the `SessionStart` hook no longer fires for subagents, this matches
behavior with other coding agents' implementation

# Stack

1. This PR: add `SubagentStart`.
2. #22873: add `SubagentStop`.
3. #22882: add subagent identity to normal hook inputs.
2026-05-19 12:45:08 -07:00
viyatb-oai
3c76081876 Make deny canonical for filesystem permission entries (#23493)
## Why
Filesystem permission profiles used `none` for deny-read entries, which
is less direct than the action the entry actually represents. This
change makes `deny` the canonical filesystem permission spelling while
preserving compatibility for older configs that still send `none`.

## What changed
- rename `FileSystemAccessMode::None` to `Deny`
- serialize and generate schemas with `deny` as the canonical value
- retain `none` only as a legacy input alias for temporary config
compatibility
- update filesystem glob diagnostics and regression coverage to use the
canonical spelling
- refresh config and app-server schema fixtures to match the new wire
shape

## Validation
- `cargo test -p codex-protocol`
- `cargo test -p codex-app-server-protocol`
- `cargo test -p codex-core config_toml_deserializes_permission_profiles
--lib`
- `cargo test -p codex-core
read_write_glob_patterns_still_reject_non_subpath_globs --lib`

Earlier in the session, a broad `cargo test -p codex-core` run reached
unrelated pre-existing failures in timing/snapshot/git-info tests under
this environment; the targeted surfaces touched by this PR passed
cleanly.
2026-05-19 11:03:47 -07:00
jif-oai
05b8ce4354 chore: namespace v1 sub-agent tools (#23475)
## Why

The v1 sub-agent tools are a single tool family, but they were exposed
as separate flat function tools. This makes the model-visible surface
less clearly grouped and leaves the legacy names in the same flat
namespace as newer agent tooling.

## What

- Wraps the v1 `spawn_agent`, `send_input`, `resume_agent`,
`wait_agent`, and `close_agent` specs in the `multi_agent_v1` namespace.
- Registers the corresponding handlers with namespaced runtime tool
names.
- Updates tool-planning, deferred tool search, and sub-agent
notification tests to assert the namespace shape and child `spawn_agent`
lookup.

## Verification

- Updated `codex-core` coverage for the v1 multi-agent tool plan,
deferred tool search output, and sub-agent tool descriptions.
2026-05-19 19:46:17 +02:00
pakrym-oai
ccbf0137db [codex] Make contextual user fragments dyn-renderable (#23397)
## Why
`ContextualUserFragment` needs to be usable behind `dyn` for render-only
paths, but associated constants made the trait non-object-safe.

## What changed
- Replaced associated constants with trait methods so `dyn
ContextualUserFragment` can render fragments.
- Preserved the existing typed `T::matches_text(text)` registration
pattern via `type_markers()`.
- Kept default `render()` on the main trait so implementations only
provide role, markers, and body.
- Added unit coverage for rendering a `Box<dyn ContextualUserFragment>`.

## Verification
- `cargo test -p codex-core contextual_user_fragment_is_dyn_compatible`
- `just fix -p codex-core`
2026-05-19 10:42:54 -07:00
pakrym-oai
f0663fd4fd [codex] Preserve steer input as user input (#23405)
## Why

Steered input was queued as a `ResponseInputItem`, then parsed back into
a user message before recording. That path loses information that only
exists on `UserInput`, such as UI text elements.

This change keeps turn-local pending input typed as either original
`UserInput` or existing response items, so steered user input reaches
user-message recording without being reconstructed from a response item.

## What changed

- Add `TurnInput` for active-turn pending input.
- Queue `Session::steer_input` as `TurnInput::UserInput`.
- Run pending-input hook inspection only for `TurnInput::UserInput`.
- Process drained pending input item by item: accepted items are
recorded, blocked items append hook context and are skipped.
- Remove the pending-input prepend/requeue path.

## Validation

- `just fmt`
- `just fix -p codex-core`
- `RUST_MIN_STACK=16777216 cargo test -p codex-core --lib
session::tests::task_finish_emits_turn_item_lifecycle_for_leftover_pending_user_input
-- --nocapture`
- `RUST_MIN_STACK=16777216 cargo test -p codex-core --lib steer_input`
- `RUST_MIN_STACK=16777216 cargo test -p codex-core --lib pending_input`
- `RUST_MIN_STACK=16777216 cargo test -p codex-core --test all
pending_input`
- `RUST_MIN_STACK=16777216 cargo test -p codex-core` (unit tests passed:
1835 passed, 0 failed, 4 ignored; integration `all` target failed due
missing helper binaries such as `codex`/`test_stdio_server` plus
unrelated MCP/search/code-mode expectations)
2026-05-19 09:47:43 -07:00
pakrym-oai
9289b7cea8 [codex] Move hook request plumbing into hook runtime (#23388)
## Why

`run_turn` was still hand-building hook payloads and lifecycle events
for a couple of hook paths. Most hook call sites already delegate
request construction and event emission to `hook_runtime`, which keeps
turn orchestration focused on model-flow decisions rather than hook
plumbing.

This also keeps the legacy `after_agent` message extraction next to the
legacy hook dispatch instead of leaving response-item walking in
`run_turn`.

## What changed

- Added `run_stop_hooks` in `hook_runtime` to build `StopRequest`, emit
preview start events, run the hook, and emit completion events.
- Added `run_legacy_after_agent_hook` in `hook_runtime` to build and
dispatch the legacy `AfterAgent` hook payload, including extracting
input messages from response items.
- Updated `run_turn` to call the hook runtime helpers and keep only the
resulting continuation/block/stop decisions inline.
- Removed the repeated pending session-start hook check from the run
loop.

## Validation

- `cargo test -p codex-core hook_runtime`
2026-05-19 08:41:26 -07:00
pakrym-oai
ef24ef127f [codex] Allow empty turn/start requests (#23409)
## Why

`turn/start` already accepts an input array on the wire, including an
empty array, but core treated empty input as a no-op before the turn
could reach the model. App-server clients need to be able to start a
real turn even when there is no new user message, for example to let the
model proceed from existing thread context.

## What changed

- Removed the `run_turn` early return that skipped empty-input turns
when there was no pending input.
- Kept empty active-turn steering rejected by moving the `steer_input`
empty-input check until after core has determined whether there is an
active regular turn.
- Empty regular turns now refresh `previous_turn_settings` like other
regular turns, so follow-up context injection state advances
consistently.
- Added an app-server v2 integration test proving `turn/start` with
`input: []` emits started/completed notifications, sends one Responses
request, and does not synthesize an empty user message.

## Validation

- `cargo test -p codex-app-server --test all
turn_start_with_empty_input_runs_model_request`
2026-05-19 08:39:45 -07:00
jif-oai
b3ae3de405 Defer v1 multi-agent tools behind tool search (#23144)
Summary: defer v1 multi-agent tools when tool_search and namespace tools
are available; keep concise searchable descriptions and move the v1
usage guidance into developer instructions; add targeted coverage.
Testing: not run per request; ran just fmt.
2026-05-19 15:04:35 +02:00
jif-oai
80fdd4688f Add body_after_prefix auto-compact token limit scope (#22870)
## Why

`model_auto_compact_token_limit` has only been able to budget the full
active context. That makes it hard to set a small "growth since
compaction" budget for sessions that preserve a large carried window
prefix: the preserved prefix can consume the whole budget and force
immediate repeated compaction.

This PR adds an opt-in `body_after_prefix` scope so callers can apply
`model_auto_compact_token_limit` to sampled output and later growth
after the current carried prefix, while still forcing compaction before
the full model context window is exhausted.

## What changed

- Adds `AutoCompactTokenLimitScope` with the existing `total` behavior
as the default and a new `body_after_prefix` mode:
[`config_types.rs`](973806b1cb/codex-rs/protocol/src/config_types.rs (L24-L37)).
- Threads `model_auto_compact_token_limit_scope` through config loading,
`Config`, `core-api`, and app-server v2 schema/TypeScript generation.
- Records the first observed input-token count for a `body_after_prefix`
compaction window and uses it as the baseline when deciding whether the
scoped auto-compaction budget is exhausted:
[`turn.rs`](973806b1cb/codex-rs/core/src/session/turn.rs (L743-L781)).
- Keeps a hard context-window cap in `body_after_prefix`, so scoped
budgeting cannot let the active context overrun the usable window.

## Verification

Added compact-suite coverage for the two key behaviors:
`body_after_prefix` does not re-compact just because the carried prefix
is larger than the scoped budget, and it still compacts when the total
active context reaches the configured context window:
[`compact.rs`](973806b1cb/codex-rs/core/tests/suite/compact.rs (L3003-L3128)).
2026-05-19 10:19:46 +00:00
jif-oai
05e171094d Remove ToolsConfig from tool planning (#22835)
## Why

`codex-tools` is meant to hold reusable tool primitives, but
`ToolsConfig` had become a second copy of core runtime decisions instead
of a small shared contract. It carried provider capabilities, auth/model
gates, permission and environment state, web/search/image feature gates,
multi-agent settings, and goal availability from core into `codex-tools`
([definition](22dd9ad392/codex-rs/tools/src/tool_config.rs (L97)),
[stored on each
`TurnContext`](22dd9ad392/codex-rs/core/src/session/turn_context.rs (L87))).
Every session/context variant then had to build and mutate that snapshot
before assembling tools.

This PR removes that master object instead of renaming it. Tool planning
now reads the live `TurnContext`, where `codex-core` already owns those
decisions, while `codex-tools` keeps only reusable primitives and a
generic `ToolSetBuilder`/`ToolSet` accumulator.

## What Changed

- Removed `ToolsConfig` / `ToolsConfigParams` from `codex-tools`; the
crate keeps the shared helpers that still belong there, including
request-user-input mode selection, shell backend/type resolution,
`UnifiedExecShellMode`, and `ToolEnvironmentMode`.
- Replaced config-snapshot planning with `ToolRouter::from_turn_context`
and a `spec_plan` pipeline over `CoreToolPlanContext`, deriving provider
capabilities, auth gates, model support, feature gates, environment
count, goal support, multi-agent options, web search, and image
generation from the authoritative turn state.
- Added generic `codex_tools::ToolSetBuilder` / `ToolSet`, plus the
small core adapter needed to accumulate `CoreToolRuntime` values and
hosted model specs.
- Added the `tool_family::shell` registration module and moved
shell/unified-exec/memory accounting call sites to read the narrow
per-turn fields directly.
- Narrowed `TurnContext` to the remaining explicit per-turn fields
needed by planning: `available_models`, `unified_exec_shell_mode`, and
`goal_tools_supported`.
- Reworked MCP exposure and tool-search setup so deferred/direct MCP
behavior is driven by the current turn rather than a precomputed config
snapshot.
- Replaced the large expected-spec fixture tests with focused
behavior-level coverage for shell tools, environments, goal and
agent-job gates, MCP direct/deferred exposure, tool search,
request-plugin-install, code mode, multi-agent mode, hosted tools, and
extension executor dispatch.

## Verification

- `cargo check -p codex-tools`
- `cargo check -p codex-core --lib`
- `cargo test -p codex-tools`
- `cargo test -p codex-core spec_plan --lib`
- `cargo test -p codex-core router --lib`
2026-05-19 11:24:09 +02:00
jif-oai
ba57aab13a feat: dedicated goal DB (#23300)
## Why

Thread goals are moving toward extension-owned runtime behavior, but
their persisted state was still stored in the shared state database.
This makes the goal store harder to isolate and keeps future storage
splits tied to ad hoc runtime plumbing.

This PR gives goals their own SQLite database while keeping the existing
`StateRuntime` entry point. The goal is to make this the pattern for
adding more dedicated runtime databases later.

This also reduce load on existing DB and reduce contention

## Limitation
Thread preview from goal is not supported anymore. I'm looking into this
[EDIT]: solved

## What changed

- Added a dedicated `goals_1.sqlite` database with its own
`goals_migrations` directory.
- Moved `thread_goals` creation into the goals DB migration set.
- Dropped the old `thread_goals` table from the main state DB with a
normal state migration. There is intentionally no backfill for existing
goal rows.
- Changed `GoalStore` to be backed only by the goals DB pool.
- Removed the old goal-write side effect that filled empty
`threads.preview` values from the goal objective.
- Added shared runtime DB path metadata so startup, telemetry, `codex
doctor`, and repair handling can include future DBs without bespoke path
lists.
- Updated Bazel compile data so the new goals migration directory is
available to `sqlx::migrate!`.

## Verification

- `cargo check --tests -p codex-state -p codex-cli -p codex-core -p
codex-app-server`
- `just fix -p codex-state`
- `just fix -p codex-cli`
- `just fix -p codex-app-server`
2026-05-19 11:11:41 +02:00
jif-oai
826b2182ed Preserve context baselines for full-history agent forks (#23352)
## Why

Full-history agent forks should continue from the same prompt prefix as
the parent. Dropping the stored `TurnContext` baseline forced the child
to rebuild startup context on its first turn, which can duplicate
developer instructions and also loses the cache continuity that a
full-history fork is supposed to preserve.

Truncated forks are different: once we keep only the last N turns, the
original prompt prefix is no longer intact, so the child must establish
a fresh context baseline.

## What changed

- Preserve `RolloutItem::TurnContext` when forking with
`SpawnAgentForkMode::FullHistory`, and keep dropping it for truncated
forks:
4090717d94/codex-rs/core/src/agent/control.rs (L98-L126)
and
4090717d94/codex-rs/core/src/agent/control.rs (L399-L401)
- Remove the special-case MultiAgentV2 usage-hint filtering path.
Full-history fork now preserves the cached developer prefix instead of
trying to reconstruct part of it.
- Extend the fork coverage to assert both sides of the contract:
full-history forks keep the parent reference baseline, while last-N
forks rebuild context after truncation:
4090717d94/codex-rs/core/src/agent/control_tests.rs (L603-L759)
and
4090717d94/codex-rs/core/src/agent/control_tests.rs (L854-L977)

## Verification

- `cargo test -p codex-core
spawn_agent_can_fork_parent_thread_history_with_sanitized_items --
--nocapture`
- `RUST_MIN_STACK=16777216 cargo test -p codex-core
spawn_agent_fork_last_n_turns_keeps_only_recent_turns -- --nocapture`
2026-05-19 10:34:24 +02:00
viyatb-oai
3009e23644 core: expose permission profile picker metadata (#22928)
## Why

The `/permissions` picker needs a config-level way to distinguish legacy
anonymous presets from named permission-profile mode. That signal cannot
be inferred reliably in the TUI, especially for the edge case where
`default_permissions = ":workspace"` is present without a
`[permissions]` table.

## What changed

- Expose whether the merged config is explicitly in permission-profile
mode.
- Expose the configured custom permission profile IDs alongside the
built-in profile semantics.
- Add regression coverage for profile mode detection and custom profile
metadata, including the `default_permissions = ":workspace"` case.
- Update the thread-manager sample config literal to match the expanded
config shape.

## Stack

1. **This PR**: config metadata needed by downstream permission-profile
consumers.
2. [#22931](https://github.com/openai/codex/pull/22931): refresh active
permission profiles through runtime/session/network state.
3. [#21559](https://github.com/openai/codex/pull/21559): switch
`/permissions` to the profile-aware TUI picker.

## Verification

- `cargo check -p codex-thread-manager-sample`
- `cargo test -p codex-core
default_permissions_can_select_builtin_profile_without_permissions_table`
- `cargo test -p codex-core
permissions_profiles_allow_direct_write_roots_outside_workspace_root`
2026-05-18 23:26:17 -07:00
sayan-oai
1dd9bf9a74 Remove explicit connector tool undeferral (#23390)
## Summary
- remove the explicit-connector carveout that kept mentioned app tools
directly exposed instead of deferred
- keep the surviving explicit-mention reconstruction only for analytics,
preserving `codex_app_mentioned` and `codex_app_used.invoke_type`
- trim the now-unused prompt/tool-exposure plumbing and refresh coverage
around always-defer behavior

## Verification
- `just fmt`
- `cargo test -p codex-analytics`
- `cargo test -p codex-core` *(one transient timeout in
`shell_snapshot::tests::macos_zsh_snapshot_includes_sections`; isolated
rerun passed)*
- `cargo test -p codex-core --lib
shell_snapshot::tests::macos_zsh_snapshot_includes_sections`
- `cargo test -p codex-core --test all
explicit_app_mentions_respect_always_defer`
- `cargo test -p codex-core --lib
mcp_tool_exposure::tests::always_defer_feature_defers_apps_too`
- `just fix -p codex-analytics`
- `just fix -p codex-core`
2026-05-18 21:33:46 -07:00
Eric Traut
a668379abf [5 of 7] Replace OverrideTurnContext with ThreadSettings (#22508)
**Stack position:** [5 of 7]

## Summary

This PR adds `Op::ThreadSettings`, a queued settings-only update
mechanism for changing stored thread settings without starting a new
turn. It also removes the legacy `Op::OverrideTurnContext` in the same
layer, so reviewers can see the replacement and deletion together.

## Changes

- Add `Op::ThreadSettings` for settings-only queued updates.
- Emit `ThreadSettingsApplied` with the effective thread settings
snapshot after core applies an update.
- Route settings-only updates through the same submission queue as user
input.
- Migrate remaining `OverrideTurnContext` tests and callers to the
queued `Op::ThreadSettings` path.
- Delete `Op::OverrideTurnContext` from the core protocol and submission
loop.

This stack addresses #20656 and #22090.

## Stack

1. [1 of 7] [Add thread settings to
UserInput](https://github.com/openai/codex/pull/23080)
2. [2 of 7] [Remove
UserInputWithTurnContext](https://github.com/openai/codex/pull/23081)
3. [3 of 7] [Remove
UserTurn](https://github.com/openai/codex/pull/23075)
4. [4 of 7] [Placeholder for OverrideTurnContext
cleanup](https://github.com/openai/codex/pull/23087)
5. [5 of 7] [Replace OverrideTurnContext with
ThreadSettings](https://github.com/openai/codex/pull/22508) (this PR)
6. [6 of 7] [Add app-server thread settings
API](https://github.com/openai/codex/pull/22509)
7. [7 of 7] [Sync TUI thread
settings](https://github.com/openai/codex/pull/22510)
2026-05-18 21:03:51 -07:00
pakrym-oai
9e9a62dc28 [codex] Extract turn skill and plugin injections (#23396)
## Why

`run_turn` had accumulated the turn-scoped skill, plugin, app, MCP,
connector-selection, and analytics setup inline. That made the
orchestration path harder to scan even though the actual turn item
injection still needs to stay in `run_turn` so ordering is explicit.

## What changed

This extracts that setup into `build_skills_and_plugins`, which returns
the combined injection `ResponseItem`s and the explicitly enabled
connector IDs. `run_turn` now keeps the required orchestration pieces:
context update recording, user input handling, connector selection
merge, and the explicit per-item `record_conversation_items` calls for
injection items.

The refactor keeps the change LOC-neutral in `core/src/session/turn.rs`
and preserves the existing response-item based injection path.

## Validation

- `cargo test -p codex-core collect_explicit_app_ids_from_skill_items`
- `just fix -p codex-core`
2026-05-18 20:33:27 -07:00
Eric Traut
1a25d8b6e5 [3 of 7] Remove UserTurn (#23075)
**Stack position:** [3 of 7]

## Summary

This PR finishes the input-op consolidation by moving the remaining
`Op::UserTurn` callers onto `Op::UserInput` and deleting `Op::UserTurn`.
This touches a lot of files, but it is a low-risk mechanical migration.

## Stack

1. [1 of 7] [Add thread settings to
UserInput](https://github.com/openai/codex/pull/23080)
2. [2 of 7] [Remove
UserInputWithTurnContext](https://github.com/openai/codex/pull/23081)
3. [3 of 7] [Remove
UserTurn](https://github.com/openai/codex/pull/23075) (this PR)
4. [4 of 7] [Placeholder for OverrideTurnContext
cleanup](https://github.com/openai/codex/pull/23087)
5. [5 of 7] [Replace OverrideTurnContext with
ThreadSettings](https://github.com/openai/codex/pull/22508)
6. [6 of 7] [Add app-server thread settings
API](https://github.com/openai/codex/pull/22509)
7. [7 of 7] [Sync TUI thread
settings](https://github.com/openai/codex/pull/22510)
2026-05-18 19:56:00 -07:00
Eric Traut
e811234484 [2 of 7] Remove UserInputWithTurnContext (#23081)
**Stack position:** [2 of 7]

## Summary

This PR removes the overlapping `Op::UserInputWithTurnContext` variant
now that `Op::UserInput` can carry thread settings overrides directly.

## Stack

1. [1 of 7] [Add thread settings to
UserInput](https://github.com/openai/codex/pull/23080)
2. [2 of 7] [Remove
UserInputWithTurnContext](https://github.com/openai/codex/pull/23081)
(this PR)
3. [3 of 7] [Remove
UserTurn](https://github.com/openai/codex/pull/23075)
4. [4 of 7] [Placeholder for OverrideTurnContext
cleanup](https://github.com/openai/codex/pull/23087)
5. [5 of 7] [Replace OverrideTurnContext with
ThreadSettings](https://github.com/openai/codex/pull/22508)
6. [6 of 7] [Add app-server thread settings
API](https://github.com/openai/codex/pull/22509)
7. [7 of 7] [Sync TUI thread
settings](https://github.com/openai/codex/pull/22510)
2026-05-18 19:41:33 -07:00
Eric Traut
84d941d07f [1 of 7] Add thread settings to UserInput (#23080)
**Stack position:** [1 of 7]

## Summary

The first three PRs in this stack are a cleanup pass before the actual
thread settings API work.

Today, core has several overlapping "user input" ops: `UserInput`,
`UserInputWithTurnContext`, and `UserTurn`. They differ mostly in how
much next-turn state they carry, which makes the later queued thread
settings update harder to reason about and review.

This PR starts that cleanup by adding the shared
`ThreadSettingsOverrides` payload and allowing `Op::UserInput` to carry
it. Existing variants remain in place here, so this layer is mostly a
behavior-preserving API shape change plus mechanical constructor
updates.

## End State After PR3

By the end of PR3, `Op::UserInput` is the only "user input" core op. It
can carry optional thread settings overrides for callers that need to
update stored defaults with a turn, while callers without updates use
empty settings. `Op::UserInputWithTurnContext` and `Op::UserTurn` are
deleted.

## End State After PR5

By the end of PR5, core will have only two ops for this area:

- `Op::UserInput` for user-input-bearing submissions.
- `Op::ThreadSettings` for settings-only updates.

## Stack

1. [1 of 7] [Add thread settings to
UserInput](https://github.com/openai/codex/pull/23080) (this PR)
2. [2 of 7] [Remove
UserInputWithTurnContext](https://github.com/openai/codex/pull/23081)
3. [3 of 7] [Remove
UserTurn](https://github.com/openai/codex/pull/23075)
4. [4 of 7] [Placeholder for OverrideTurnContext
cleanup](https://github.com/openai/codex/pull/23087)
5. [5 of 7] [Replace OverrideTurnContext with
ThreadSettings](https://github.com/openai/codex/pull/22508)
6. [6 of 7] [Add app-server thread settings
API](https://github.com/openai/codex/pull/22509)
7. [7 of 7] [Sync TUI thread
settings](https://github.com/openai/codex/pull/22510)
2026-05-18 18:48:35 -07:00
sayan-oai
daa11820b0 Remove ToolSearch feature toggle (#23389)
## Summary
- mark `ToolSearch` as removed and ignore stale config writes for its
legacy key
- make search tool exposure depend only on model capability, not a
feature toggle
- remove app-server enablement support and prune now-obsolete test
coverage/setup

## Verification
- `cargo test -p codex-features`
- `cargo test -p codex-tools`
- `cargo test -p codex-core search_tool_requires_model_capability`
- `cargo test -p codex-app-server experimental_feature_enablement_set_`

## Notes
- This keeps the legacy config key as a no-op for compatibility while
removing the ability to toggle the behavior off cleanly.
- No developer-facing docs update outside the touched app-server README
was needed.
2026-05-19 01:24:39 +00:00
xl-openai
6b54ced108 cleanup: Remove skill env var dependency prompting (#22721)
Deletes the skill env var dependency prompt feature and its runtime
path. env_var entries in skill dependency metadata are now silently
ignored during skill loading.
2026-05-19 01:24:19 +00:00
pakrym-oai
17d552fb4d [codex] Remove external websocket session resets (#23384)
## Why

Compaction now installs replacement history inside the session, but the
turn and compaction callers were still reaching into
`ModelClientSession` to reset websocket transport state after that
install. That made a transport-level reset part of the compaction API
even though websocket incremental request selection already checks
whether the next request is a strict extension of the previous one and
falls back to a full `response.create` when it is not.

## What changed

- Removed the compaction-side calls to `reset_websocket_session` from
`compact.rs` and `session/turn.rs`.
- Simplified pre-sampling and mid-turn compaction helpers so they return
`CodexResult<()>` instead of carrying a reset flag.
- Made `ModelClientSession::reset_websocket_session` private to
`client.rs`, leaving only the websocket timeout recovery path inside the
client as a caller.

## Validation

- `cargo test -p codex-core --test all
responses_websocket_creates_on_non_prefix`
- `cargo test -p codex-core --test all
steered_user_input_waits_for_model_continuation_after_mid_turn_compact`
- `cargo test -p codex-core --test all
pre_sampling_compact_runs_on_switch_to_smaller_context_model`
2026-05-19 01:13:38 +00:00
pakrym-oai
afa0101ae2 [codex] Move pending input into input queue (#22728)
## Why

Pending model input was split across `Session`, `TurnState`, and the
agent mailbox. That made it easy for new paths to manage queued user
input or mailbox delivery outside the intended ownership boundary.

This PR consolidates the model-facing input lifecycle behind the session
input queue so turn-local pending input, next-turn queued items, and
mailbox delivery coordination are owned in one place.

## What Changed

- Added `session/input_queue.rs` to own pending input queues and mailbox
delivery coordination.
- Removed the standalone `agent/mailbox.rs` channel wrapper and store
mailbox items directly in the input queue.
- Moved pending-input mutations off `TurnState`; `TurnState` now exposes
the queue-owned storage directly for now.
- Routed abort cleanup, mailbox delivery phase changes, next-turn queued
items, and active-turn pending input through `InputQueue`.
- Boxed stack-heavy agent resume/fork startup futures that the refactor
pushed over the default test stack.
- Updated session, task, goal, stream-event, and multi-agent call sites
and tests to use the new queue ownership.

## Verification

- `cargo test -p codex-core --lib agent::control::tests`
- `cargo test -p codex-core --lib
agent::control::tests::resume_closed_child_reopens_open_descendants --
--exact`
- `cargo test -p codex-core --lib
agent::control::tests::spawn_agent_fork_last_n_turns_keeps_only_recent_turns
-- --exact`
- `cargo test -p codex-core --lib
agent::control::tests::resume_thread_subagent_restores_stored_nickname_and_role
-- --exact`
- `cargo test -p codex-core` was also run; it completed with 1814
passed, 4 ignored, and one timeout in
`agent::control::tests::resume_thread_subagent_restores_stored_nickname_and_role`,
which passed when rerun in isolation.
2026-05-18 15:43:01 -07:00
Matthew Zeng
a66e0e9c4b Include plugin id in plugin MCP tool metadata (#23353)
Adding the id of the plugin that contains the MCP (if any) so we can
apply filters at plugin level.

## Summary
- carry the plugin owner into MCP runtime provenance
- attach `plugin_id` to outbound plugin-backed MCP tool-call `_meta`
- avoid misattributing user-configured MCP servers that shadow plugin
server names

## Testing
- `just fmt`
- `just fix -p codex-mcp`
- `just fix -p codex-core`
- `cargo test -p codex-mcp`
- `cargo test -p codex-core
plugin_mcp_tool_call_request_meta_includes_plugin_id`
- `cargo test -p codex-core
to_mcp_config_omits_plugin_id_when_user_server_shadows_plugin_mcp`
- `cargo test -p codex-core
rebuild_preserving_session_layers_refreshes_plugin_derived_mcp_config`
- `git diff --check`

## Notes
- Attempted `cargo test -p codex-core`; it aborted in
`agent::control::tests::resume_agent_from_rollout_skips_descendants_when_parent_resume_fails`
with a stack overflow before the full suite completed.
2026-05-18 15:33:33 -07:00
pakrym-oai
f2368b7de6 [codex] Trim unused TurnContextItem fields (#22709)
## Why

`TurnContextItem` is the durable baseline used to reconstruct context
diffs across resume/fork. Most of the old persisted-only fields on it
are no longer read, so keeping them in rollout snapshots adds schema
surface and state that can drift without affecting reconstruction.

`summary` is the exception: older Codex versions require it to
deserialize `turn_context` records, so keep writing a default
compatibility value until that schema surface can be removed safely.

## What changed

- Removed the unused persisted fields from `TurnContextItem`: trace ids,
user/developer instructions, output schema, and truncation policy.
- Kept `summary` with a compatibility comment and made
`TurnContext::to_turn_context_item` write `ReasoningSummary::Auto`
instead of live turn state.
- Updated rollout/context reconstruction fixtures for the retained
summary field.

## Verification

- `cargo test -p codex-protocol --lib turn_context_item`
- `cargo test -p codex-rollout
resume_candidate_matches_cwd_reads_latest_turn_context`
- `cargo test -p codex-state turn_context`
- `cargo test -p codex-core --lib
new_default_turn_captures_current_span_trace_id`
- `cargo test -p codex-core --lib
record_initial_history_resumed_turn_context_after_compaction_reestablishes_reference_context_item`
- `cargo test -p codex-core --test all
emits_warning_when_resumed_model_differs`
- `git diff --check`
2026-05-18 21:54:36 +00:00
jif-oai
c69cde3547 Add tool lifecycle extension contributor (#23309)
## Why

Extensions that need to track runtime progress currently have no typed
host signal for tool execution. The goal extension in particular needs
to observe tool attempts without inspecting tool payloads, owning tool
implementations, or staying coupled to core-only runtime plumbing.

This adds a narrow lifecycle contributor API for host-owned tool
execution: extensions can observe when an accepted tool call starts and
how it finishes, while policy hooks and tool handlers continue to own
payload rewriting, blocking, and execution.

Relevant code:

-
[`ToolLifecycleContributor`](3ad2850ffc/codex-rs/ext/extension-api/src/contributors.rs (L119))
defines the extension-facing observer contract.
-
[`tool_lifecycle.rs`](3ad2850ffc/codex-rs/ext/extension-api/src/contributors/tool_lifecycle.rs)
defines the typed start/finish inputs, source, and outcome enums.
- [`notify_tool_start` /
`notify_tool_finish`](3ad2850ffc/codex-rs/core/src/tools/lifecycle.rs)
bridges core tool dispatch into the extension registry.

## What Changed

- Added `ToolLifecycleContributor` to `codex-extension-api`, including:
  - `ToolStartInput`
  - `ToolFinishInput`
  - `ToolCallSource`
  - `ToolCallOutcome`
- Added registration and lookup support on `ExtensionRegistryBuilder` /
`ExtensionRegistry`.
- Wired core tool dispatch to notify lifecycle contributors for:
  - accepted tool starts
  - completed tool calls, including the tool output success marker
  - pre-tool-use blocks
  - failures before or after the handler runs
  - cancellation/abort in the parallel tool path
- Registered the goal extension as a lifecycle contributor and added the
outcome filter it will use for goal progress accounting.

## Test Coverage

- Added `dispatch_notifies_tool_lifecycle_contributors` to cover
lifecycle notification ordering and outcomes for successful and
handler-failed tool calls.
2026-05-18 21:55:57 +02:00
Celia Chen
4dbca61e20 fix: default unknown tool schemas to empty schemas (#22380)
## Why

Some tool providers, especially MCP servers and dynamic tool sources,
can supply schema nodes that omit `type` and have no recognized JSON
Schema shape hints. Previously, `sanitize_json_schema` filled those
unknown nodes in as `string`, which made the schema parseable but
invented a scalar constraint that the provider did not specify. For
description-only fields, that could incorrectly steer tool arguments
away from the provider's actual accepted shape.

The Responses API accepts permissive empty schemas such as `{}` at
nested property positions, so Codex should preserve that permissive
meaning instead of coercing unknown schema nodes into a misleading
scalar type.

## What Changed

- Changed the no-hints fallback in `codex-rs/tools/src/json_schema.rs`
to clear unrecognized object schema nodes to `{}`.
- Empty schemas now remain `{}` rather than becoming `type: "string"`.
- Description-only or otherwise metadata-only nested property schemas
now become `{}` while surrounding object/array/string/number inference
still applies when recognized hints are present.
- Updated `codex-tools` and `codex-core` tests to cover top-level empty
schemas, nested empty schemas, metadata-only malformed schemas, dynamic
tools, and MCP tool specs.

## Verification

- `cargo test -p codex-tools`
- `cargo test -p codex-core
test_mcp_tool_property_missing_type_defaults_to_empty_schema`
- Manually verified the real Responses API behavior for both
empty-schema positions:
- Top-level function `parameters: {}` is accepted and echoed back as
`{"type":"object","properties":{}}`; when forced to call the tool,
Responses emitted empty object arguments: `"arguments": "{}"`.
- Nested property schema `{}` is accepted and preserved as `{}`; when
forced to call a tool with `metadata.extra`, Responses emitted
`"arguments": "{\"metadata\":{\"extra\":\"codex schema sanitizer
behavior\"}}"`.
2026-05-18 12:41:10 -07:00
starr-openai
10f7dc6eb5 codex: route global AGENTS reads through LOCAL_FS (#23343)
## Summary
- make `load_global_instructions` read through an `ExecutorFileSystem`
- call global AGENTS reads with explicit `LOCAL_FS` so they stay tied to
local codex-home state

## Validation
- `bazel test --bes_backend= --bes_results_url=
--test_filter=instruction_sources_include_global_before_agents_md_docs
//codex-rs/core:core-unit-tests` on `dev`
2026-05-18 19:26:10 +00:00
Eric Traut
55f6bbc667 goals: keep pause transitions explicit (#23088)
## Problem

This addresses several user-reported cases where active goals were
paused even though the user had not explicitly asked for that
transition:

- the guardian approval-review circuit breaker interrupted a turn and
implicitly paused the goal
- a shutdown in one app-server instance could pause a goal while a
second instance was still actively running the same thread
- steering-style interrupts could also pause the goal even though they
are meant to redirect work, not stop the goal lifecycle

The common problem was that core treated `TurnAbortReason::Interrupted`
as an implicit request to transition the persisted goal to `paused`.
That made unrelated interrupt paths mutate goal state as a side effect,
and in the multi-app-server case it allowed stale process teardown to
pause a live goal owned by another running client.

After this change, transitioning a goal to `paused` is always an
explicit action performed by a client or another intentional goal-state
mutation. It is never an implicit transition triggered by generic
interrupt handling.

Refs #22884.

## What changed

- Remove the goal runtime path that paused active goals after
interrupted task aborts.
- Drop the now-unused abort reason from `GoalRuntimeEvent::TaskAborted`.
- Update the focused regression coverage so an interrupted active goal
still accounts usage but remains `active`.
2026-05-18 11:58:40 -07:00
Eric Traut
0d344aca9b goal: pause continuation loops on usage limits and blockers (#23094)
Addresses #22833, #22245, #23067

## Why
`/goal` can keep synthesizing turns even when the next turn cannot make
meaningful progress. Hard usage exhaustion can replay failing turns, and
repeated permission or external-resource blockers can keep burning
tokens while waiting for user or system intervention.

## What changed
- Add resumable `blocked` and `usageLimited` goal states. As with
`paused`, goal continuation stops with these states.
- Move to `usageLimited` after usage-limit failures.
- Allow the built-in `update_goal` tool to set `blocked` only under
explicit repeated-impasse guidance. Updated goal continuation prompt to
specify that agent should use `blocked` only when it has made at least
three attempts to get past an impasse.

Most of the files touched by this PR are because of the small app server
protocol update.

## Validation

I manually reproduced a number of situations where an agent can run into
a true impasse and verified that it properly enters `blocked` state. I
then resumed and verified that it once again entered `blocked` state
several turns later if the impasse still exists.

I also manually reproduced the usage-limit condition by creating a
simulated responses API endpoint that returns 429 errors with the
appropriate error message. Verified that the goal runtime properly moves
the goal into `usageLimited` state and TUI UI updates appropriately.
Verified that `/goal resume` resumes (and immediately goes back into
`ussageLImited` state if appropriate).


## Follow-up PRs

Small changes will be needed to the GUI clients to properly handle the
two new states.
2026-05-18 11:28:53 -07:00
starr-openai
9286ff2805 Fix remote turn diff display roots (#23261)
## Why

`TurnDiffTracker` computes a display root so turn diffs can be rendered
repo-relative. For remote exec-server turns, the selected turn `cwd` may
exist only inside the selected environment, but `run_turn` was
discovering the git root through the local host filesystem. When that
lookup failed, nested remote-session diffs fell back to the nested `cwd`
and showed `/tmp/...`-prefixed paths instead of repo-relative paths.

## What changed

- Resolve the diff display root from the primary selected turn
environment when one exists, using that environment's filesystem and
`cwd`.
- Add `codex_git_utils::get_git_repo_root_with_fs(...)` so git-root
discovery can run against an `ExecutorFileSystem`, including remote
environments.
- Reuse that helper from `resolve_root_git_project_for_trust(...)` and
add coverage for `.git` gitdir-pointer detection.

## Validation

- Devbox Bazel: `//codex-rs/core:core-unit-tests
--test_filter=get_git_repo_root_with_fs_detects_gitdir_pointer`
- Devbox Docker-backed remote-env repro: `//codex-rs/core:core-all-test
--test_filter=apply_patch_turn_diff_paths_stay_repo_relative_when_session_cwd_is_nested`
2026-05-18 10:53:49 -07:00
pakrym-oai
82061660ae [codex] Remove legacy shell output formatting paths (#22706)
## Why

The client and tool pipeline still carried compatibility code for legacy
structured shell output. Current shell and apply_patch responses are
already plain text for model consumption, so keeping a
JSON-serialization path plus shell-item rewrite logic makes the request
formatter and tests preserve a format we do not need anymore.

## What Changed

- Removed the client-side shell output rewrite from
`core/src/client_common.rs`.
- Removed the structured exec-output formatter and the shell `freeform`
switch so tool emitters use one model-facing formatter.
- Collapsed apply_patch/shell serialization tests around the remaining
plain-text output expectations and removed duplicate one-variant
parameterized cases.
- Kept the `ApplyPatchModelOutput::ShellCommandViaHeredoc` compatibility
input shape, but no longer treats it as a separate output-format mode.

## Validation

- `cargo test -p codex-core client_common`
- `cargo test -p codex-core shell_serialization`
- `cargo test -p codex-core apply_patch_cli`
- `just fix -p codex-core`

## Documentation

No external Codex documentation update is needed.
2026-05-18 09:57:54 -07:00
jif-oai
b631d92170 chore: make token usage async (#23305)
Make the `TokenUsageContributor` async. This will be required for future
extension and it's basically free
2026-05-18 15:59:06 +02:00
jif-oai
500ef67ed1 chore: goal resumed metrics (#23301)
Add metrics for goal resume
2026-05-18 15:19:23 +02:00
jif-oai
7ee7fe239f chore: isolate thread goal storage behind GoalStore (#23295)
## Why

Thread goal persistence is being prepared for a dedicated storage
boundary. Before that split, goal-specific reads, writes, accounting,
and cleanup were exposed directly on `StateRuntime`, so core and
app-server callsites stayed coupled to the full runtime instead of a
goal-specific store.

This PR introduces that boundary without changing the goal wire API or
current persistence behavior. Callers now go through
`StateRuntime::thread_goals()` and the new `GoalStore`, while
`GoalStore` still uses the existing state DB pool underneath.

## What changed

- Added `GoalStore` in `state/src/runtime/goals.rs` and exposed it from
`StateRuntime` via `thread_goals()`.
- Moved thread-goal reads, writes, status updates, pause, delete, and
usage accounting onto `GoalStore`.
- Updated core session goal handling, app-server goal RPCs, resume
snapshots, and goal tests to use the store boundary.
- Kept thread deletion responsible for cascading goal cleanup by
deleting the goal through the store only after a thread row is removed.

## Testing

- Existing goal persistence, resume, and accounting tests were updated
to exercise the new `GoalStore` access path.
2026-05-18 14:47:05 +02:00
jif-oai
9531e932ef Make extension lifecycle hooks async (#23291)
## Why

Extension lifecycle hooks sit on the host/extension boundary, but the
current trait surface only allows synchronous callbacks. That forces
extensions that need to seed, rehydrate, observe, or flush
extension-owned state during thread and turn transitions to either block
inside the callback or move async work into separate host plumbing.

This PR makes those lifecycle callbacks awaitable so extension
implementations can perform async work directly at the lifecycle point
where the host already has the relevant session, thread, or turn stores
available.

## What changed

- Makes `ThreadLifecycleContributor` and `TurnLifecycleContributor`
async in `codex-extension-api`.
- Awaits thread start/resume/stop and turn start/stop/abort lifecycle
callbacks from `codex-core`.
- Updates the guardian and memories extensions to implement the async
lifecycle trait surface.
- Updates the existing lifecycle tests to use async contributor
implementations.
- Adds `async-trait` to the crates that now expose or implement these
async object-safe lifecycle traits.

## Testing

- Existing `codex-core` lifecycle tests were updated to cover async
implementations for thread stop and turn abort ordering.
2026-05-18 13:53:58 +02:00
Michael Bolin
0a83353ca3 test: reduce core sandbox policy test setup (#23036)
## Why

`SandboxPolicy` is a legacy compatibility shape, but several core tests
still used it for ordinary turn setup even when the runtime path now
carries `PermissionProfile`. With the first cleanup PR merged, this
follow-up trims more core test scaffolding so remaining `SandboxPolicy`
matches are easier to classify as production compatibility,
legacy-boundary coverage, or explicit conversion tests.

## What Changed

- Updated apply-patch handler and runtime tests to pass
`PermissionProfile` directly.
- Changed sandboxing test helpers to build permission profiles without
first creating `SandboxPolicy` values.
- Converted request-permissions integration turns to pass
`PermissionProfile` through the test helper, leaving legacy sandbox
projection at the `Op::UserTurn` boundary.
- Converted unified exec integration helpers and direct turn submissions
to use `PermissionProfile` values instead of `SandboxPolicy` setup.
- Removed now-unused `SandboxPolicy` imports from the touched core
tests.

## Test Plan

- `just fmt`
- `cargo test -p codex-core --lib tools::sandboxing::tests`
- `cargo test -p codex-core --lib tools::runtimes::apply_patch::tests`
- `cargo test -p codex-core --lib tools::handlers::apply_patch::tests`
- `cargo test -p codex-core --lib unified_exec::process_manager::tests`
- `cargo test -p codex-core --test all request_permissions::`
- `cargo test -p codex-core --test all unified_exec::`
- `just fix -p codex-core`
2026-05-17 08:39:41 -07:00
jif-oai
545ede569c Make multi-agent v2 tool namespace configurable (#23147)
## Summary
- Add `features.multi_agent_v2.tool_namespace` with config/schema
validation for Responses-compatible namespace values.
- Thread the resolved namespace into `ToolsConfig` for normal turns and
review turns.
- Wrap MultiAgentV2 tool specs and registry names in the configured
namespace when namespace tools are supported, while falling back to the
plain tool names when they are not.

## Validation
- `just fmt`
- `just write-config-schema`
- `cargo test -p codex-features multi_agent_v2_feature_config --
--nocapture`
- `cargo test -p codex-core test_build_specs_multi_agent_v2 --
--nocapture`
- `cargo test -p codex-core multi_agent_v2_config -- --nocapture`
- `cargo test -p codex-core
multi_agent_v2_rejects_invalid_tool_namespace -- --nocapture`
- `cargo test -p codex-tools`
- `git diff --check`
2026-05-17 15:27:43 +02:00
sayan-oai
061a614d85 multiagent: trim model-visible description, cap to 5 models (#23069)
## Why

The `spawn_agent` model override guidance is uncapped and bloating
context. We need to trim down each entry and cap total entries.

picked 5 as cap, we can change

## What changed

- Cap the model override summaries shown in `spawn_agent` to the first 5
picker-visible models, preserving the existing priority ordering from
the models manager.
- Condense each rendered entry to the actionable pieces the model needs:
  - use the model slug as the label
  - render compact reasoning effort lists with the default marked inline
- render only service tier IDs, and omit the clause when no tiers are
available
- Update coverage so the compact formatter shape and the top-5 cap are
exercised, and keep the end-to-end request assertion aligned with real
model metadata.

## Example

Before:

`- gpt-5.4 ('gpt-5.4\'): Strong model for everyday coding. Default
reasoning effort: medium. Supported reasoning efforts: low (Fast
responses with lighter reasoning), medium (Balances speed and reasoning
depth for everyday tasks), high (Greater reasoning depth for complex
problems), xhigh (Extra high reasoning depth for complex problems).
Supported service tiers: priority (Fast: 1.5x speed, increased usage).`

After:

`- 'gpt-5.4': Strong model for everyday coding. Reasoning efforts: low,
medium (default), high, xhigh. Service tiers: priority.`
2026-05-16 13:43:30 -07:00
Michael Bolin
d91bc15618 test: construct permission profiles directly (#23030)
## Why

`SandboxPolicy` is now a legacy compatibility shape, but several tests
still built a `SandboxPolicy` only to immediately convert it into
`PermissionProfile` for APIs that already accept canonical runtime
permissions. Those detours make it harder to audit where legacy sandbox
policy is still required, because boundary-only usages are mixed
together with ordinary test setup.

## What Changed

- Updated tests in `codex-core`, `codex-exec`, `codex-analytics`, and
`codex-config` to construct `PermissionProfile` values directly when the
code under test takes a permission profile.
- Changed exec-policy, request-permissions, session, and sandbox test
helpers to pass `PermissionProfile` through instead of converting from
`SandboxPolicy` internally.
- Left `SandboxPolicy` in place where tests are explicitly exercising
legacy compatibility or request/response boundaries.

## Test Plan

- `cargo test -p codex-analytics -p codex-config`
- `cargo test -p codex-core --lib safety::tests`
- `cargo test -p codex-core --lib exec_policy::tests::`
- `cargo test -p codex-core --lib exec::tests`
- `cargo test -p codex-core --lib guardian_review_session_config`
- `cargo test -p codex-core --lib tools::network_approval::tests`
- `cargo test -p codex-core --lib
tools::runtimes::shell::unix_escalation::tests`
- `cargo test -p codex-core --lib managed_network`
- `cargo test -p codex-core --test all request_permissions::`
- `cargo test -p codex-exec sandbox`


---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/23030).
* #23036
* __->__ #23030
2026-05-16 12:12:37 -07:00
Eric Traut
941e7f825e Improve goal completion usage reporting (#22907)
## Why

Goal completion follow-up turns currently receive a preformatted English
usage sentence such as `time used: 2586 seconds`. That nudges the model
to echo an awkward raw seconds count in the final reply, even though the
tool result already exposes structured usage fields like
`goal.timeUsedSeconds`, `goal.tokensUsed`, and `goal.tokenBudget`.

## What changed

- Replace the preformatted completion usage sentence with guidance to
read the structured goal fields from the tool result.
- Preserve token-budget reporting while allowing the model to phrase
elapsed time in a concise, human-friendly way that fits the response
language.
- Update core coverage for both the generated completion guidance and
the session flow that forwards it back to the model.

## Verification

Previously, it would have output a final message indicating that it
"worked for 303 seconds". Now it shows the following:

<img width="286" height="35" alt="image"
src="https://github.com/user-attachments/assets/d7011880-9449-46a7-856f-4e50ae00eb45"
/>
2026-05-16 11:49:40 -07:00
Michael Bolin
108234b5eb core: set permission profiles from snapshots (#22920)
## Why

#22891 moved the TUI turn-command path to pass `ActivePermissionProfile`
instead of the full `PermissionProfile`, but the remaining
config/session bridge still accepted the concrete `PermissionProfile`
and active profile id as separate arguments. That shape made it too easy
for future callers to update the concrete profile and active profile id
out of sync.

This PR makes the trusted session snapshot path pass one coherent value
into `Permissions`, while keeping `requirements.toml` enforcement owned
by the existing constrained permission state.

## What Changed

- Added `PermissionProfileSnapshot` as the public snapshot value for
trusted session/config synchronization.
- Changed `Permissions::set_permission_profile_from_session_snapshot()`
and `replace_permission_profile_from_session_snapshot()` to take a
`PermissionProfileSnapshot`.
- Updated the replacement path to derive its constrained
`PermissionProfile` from the snapshot, so callers cannot pass a separate
profile that disagrees with the snapshot.
- Removed the internal tuple-style
`PermissionProfileState::set_active_permission_profile()` mutation path.
- Updated core session projection and TUI call sites to construct
explicit legacy or active snapshots.
- Documented the snapshot constructors so legacy use and id/profile
mismatch hazards are called out at the API boundary.
- Added a focused config test that verifies snapshot updates still
respect existing permission constraints.

## How To Review

1. Start with `codex-rs/core/src/config/resolved_permission_profile.rs`;
`PermissionProfileSnapshot` is the public wrapper, while
`ResolvedPermissionProfile` stays internal.
2. Check `codex-rs/core/src/config/mod.rs` to confirm both
session-snapshot setters validate through `PermissionProfileState` and
no longer accept loose profile/id pairs.
3. Skim `codex-rs/core/src/session/session.rs` for the session
projection path; it now builds the snapshot before installing it.
4. Skim the TUI changes as call-site migration from loose argument pairs
to explicit snapshot construction.

## Verification

- `cargo test -p codex-core
permission_snapshot_setter_preserves_permission_constraints`
- `cargo test -p codex-tui status_permissions_`
- `cargo test -p codex-tui
session_configured_preserves_profile_workspace_roots`
- `just fix -p codex-core -p codex-tui`
2026-05-16 07:26:18 -07:00
Curtis 'Fjord' Hawthorne
8543e39885 Preserve image detail in app-server inputs (#20693)
## Summary

- Add optional image detail to user image inputs across core, app-server
v2, thread history/event mapping, and the generated app-server
schemas/types.
- Preserve requested detail when serializing Responses image inputs:
omitted detail stays on the existing `high` default, while explicit
`original` keeps local images on the original-resolution path.
- Support `high`/`original` consistently for tool image outputs,
including MCP `codex/imageDetail`, code-mode image helpers, and
`view_image`.
2026-05-15 15:04:04 -07:00
Michael Bolin
8df2d96860 core: construct test permission profiles directly (#22795)
## Why

The core migration is trying to make `PermissionProfile` the shape tests
and runtime code reason about, leaving `SandboxPolicy` only where legacy
behavior is explicitly under test. The local
`permission_profile_for_sandbox_policy()` test helpers kept new
permission-profile tests mentally tied to the old sandbox model even
when the equivalent profile is straightforward.

## What Changed

- Removed the `permission_profile_for_sandbox_policy()` helper from the
network proxy spec tests and session tests.
- Replaced legacy conversions for read-only, workspace-write, and
full-access cases with `PermissionProfile::read_only()`,
`PermissionProfile::workspace_write()`, and
`PermissionProfile::Disabled`.
- Constructed the external-sandbox session test's
`PermissionProfile::External` directly, while preserving the legacy
`SandboxPolicy` only where the test still exercises legacy config update
behavior.

## How To Review

This PR is intentionally test-only. Review the two touched files and
check that each replacement preserves the old legacy mapping:

- `SandboxPolicy::new_read_only_policy()` ->
`PermissionProfile::read_only()`
- `SandboxPolicy::new_workspace_write_policy()` ->
`PermissionProfile::workspace_write()`
- `SandboxPolicy::DangerFullAccess` -> `PermissionProfile::Disabled`
- `SandboxPolicy::ExternalSandbox { network_access: Restricted }` ->
`PermissionProfile::External { network: Restricted }`

## Verification

- `cargo test -p codex-core
requirements_allowed_domains_are_a_baseline_for_user_allowlist`
- `cargo test -p codex-core
start_managed_network_proxy_applies_execpolicy_network_rules`
- `cargo test -p codex-core
session_configured_reports_permission_profile_for_external_sandbox`
- `cargo test -p codex-core
managed_network_proxy_decider_survives_full_access_start`
- `just fix -p codex-core`








---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/22795).
* #22891
* __->__ #22795
2026-05-15 13:09:25 -07:00