Commit Graph

193 Commits

  • [codex] Add token usage to turn tracing spans (#19432)
    ## Why
    
    Slow Codex turns are easier to debug when token usage is visible in the
    trace itself, without joining against separate analytics. This adds
    token usage to existing turn-handling spans for regular user turns only.
    
    [Example
    turn](https://openai.datadoghq.com/apm/trace/9d353efa2cb5de1f4c5b93dc33c3df04?colorBy=service&graphType=flamegraph&shouldShowLegend=true&sort=time&spanID=3555541504891512675&spanViewType=metadata&traceQuery=)
    <img width="1447" height="967" alt="Screenshot 2026-04-24 at 3 03 07 PM"
    src="https://github.com/user-attachments/assets/ab7bb187-e7fc-41f0-a366-6c44610b2b2c"
    />
    
    ## What Changed
    
    Added response-level token fields on completed handle_responses spans:
    
    gen_ai.usage.input_tokens
    gen_ai.usage.cache_read.input_tokens
    gen_ai.usage.output_tokens
    codex.usage.reasoning_output_tokens
    codex.usage.total_tokens
    Added aggregate token fields on regular turn spans:
    
    codex.turn.token_usage.*
    Added an explicit regular-turn opt-in via
    SessionTask::records_turn_token_usage_on_span() so this is not coupled
    to span-name strings.
    
    ## Testing
    
    - `cargo test -p codex-otel`
    - `cargo test -p codex-core
    turn_and_completed_response_spans_record_token_usage`
    - `just fmt`
    - `just fix -p codex-core`
    - `just fix -p codex-otel`
    - Manual local Electron/app-server smoke test: regular user turn emits
    the new span fields
    
    Known status: `cargo test -p codex-core` was attempted and failed in
    unrelated existing areas: config approvals, request-permissions,
    git-info ordering, and subagent metadata persistence.
  • Add turn start timestamp to turn metadata (#19473)
    ## Why
    - Without change: MCP tool calls receive
    `_meta["x-codex-turn-metadata"]` with `session_id` and `turn_id`.
    - Issue: MCP servers may want the turn start timestamp to measure
    internal latency relative to turn start.
    
    ## What Changed
    - With change: turn metadata now includes `turn_started_at_unix_ms`,
    which is propagated to MCP tool calls in
    `_meta["x-codex-turn-metadata"]`.
    
    ## Verification
    - `codex-rs/core/src/mcp_tool_call_tests.rs`
    - `codex-rs/core/src/turn_metadata_tests.rs`
    - `codex-rs/core/src/turn_timing_tests.rs`
    - `codex-rs/core/tests/responses_headers.rs`
    - `codex-rs/core/tests/suite/search_tool.rs`
  • Remove ghost snapshots (#19481)
    ## Summary
    - Remove `ghost_snapshot` / `GhostCommit` from the Responses API surface
    and generated SDK/schema artifacts.
    - Keep legacy config loading compatible, but make undo a no-op that
    reports the feature is unavailable.
    - Clean up core history, compaction, telemetry, rollout, and tests to
    stop carrying ghost snapshot items.
    
    ## Testing
    - Unit tests passed for `codex-protocol`, `codex-core` targeted undo and
    compaction flows, `codex-rollout`, and `codex-app-server-protocol`.
    - Regenerated config and app-server schemas plus Python SDK artifacts
    and verified they match the checked-in outputs.
  • permissions: derive legacy exec policies at boundaries (#19737)
    ## Why
    
    After config and requirements store canonical profiles, exec requests
    should not cache a derived `SandboxPolicy`. The cached legacy value can
    drift from the richer profile state, and most execution paths already
    have the filesystem and network runtime policies they need.
    
    ## What Changed
    
    - Removes `sandbox_policy` from `codex_sandboxing::SandboxExecRequest`
    and `codex_core::sandboxing::ExecRequest`.
    - Adds an on-demand `ExecRequest::compatibility_sandbox_policy()` helper
    for the Windows and legacy call sites that still need a `SandboxPolicy`
    projection.
    - Updates Windows filesystem override setup and unified exec policy
    serialization to derive that compatibility policy at the boundary.
    - Updates Unix escalation reruns and direct shell requests to
    reconstruct exec requests from `PermissionProfile` plus runtime
    filesystem/network policy, without carrying a cached legacy policy.
    - Adjusts sandboxing manager tests to assert the effective profile
    rather than the removed legacy field.
    
    ## Verification
    
    - `cargo check -p codex-config -p codex-core -p codex-sandboxing -p
    codex-app-server -p codex-cli -p codex-tui`
    - `cargo test -p codex-sandboxing manager`
    - `cargo test -p codex-core
    exec_server_params_use_env_policy_overlay_contract`
    - `cargo test -p codex-core unix_escalation`
    - `cargo test -p codex-core exec::tests`
    - `cargo test -p codex-core sandboxing::tests`
  • permissions: migrate approval and sandbox consumers to profiles (#19393)
    ## Why
    
    Runtime decisions should not infer permissions from the lossy legacy
    sandbox projection once `PermissionProfile` is available. In particular,
    `Disabled` and `External` need to remain distinct, and managed profiles
    with split filesystem or deny-read rules should not be collapsed before
    approval, network, safety, or analytics code makes decisions.
    
    ## What Changed
    
    - Changes managed network proxy setup and network approval logic to use
    `PermissionProfile` when deciding whether a managed sandbox is active.
    - Migrates patch safety, Guardian/user-shell approval paths, Landlock
    helper setup, analytics sandbox classification, and selected
    turn/session code to profile-backed permissions.
    - Validates command-level profile overrides against the constrained
    `PermissionProfile` rather than a strict `SandboxPolicy` round trip.
    - Preserves configured deny-read restrictions when command profiles are
    narrowed.
    - Adds coverage for profile-backed trust, network proxy/approval
    behavior, patch safety, analytics classification, and command-profile
    narrowing.
    
    ## Verification
    
    - `cargo test -p codex-core direct_write_roots`
    - `cargo test -p codex-core runtime_roots_to_legacy_projection`
    - `cargo test -p codex-app-server
    requested_permissions_trust_project_uses_permission_profile_intent`
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/19393).
    * #19395
    * #19394
    * __->__ #19393
  • permissions: make runtime config profile-backed (#19606)
    ## Why
    
    This supersedes #19391. During stack repair, GitHub marked #19391 as
    merged into a temporary stack branch rather than into `main`, so the
    runtime-config change needed a fresh PR.
    
    `PermissionProfile` is now the canonical permissions shape after #19231
    because it can distinguish `Managed`, `Disabled`, and `External`
    enforcement while also carrying filesystem rules that legacy
    `SandboxPolicy` cannot represent cleanly. Core config and session state
    still needed to accept profile-backed permissions without forcing every
    profile through the strict legacy bridge, which rejected valid runtime
    profiles such as direct write roots.
    
    The unrelated CI/test hardening that previously rode along with this PR
    has been split into #19683 so this PR stays focused on the permissions
    model migration.
    
    ## What Changed
    
    - Adds `Permissions.permission_profile` and
    `SessionConfiguration.permission_profile` as constrained runtime state,
    while keeping `sandbox_policy` as a legacy compatibility projection.
    - Introduces profile setters that keep `PermissionProfile`, split
    filesystem/network policies, and legacy `SandboxPolicy` projections
    synchronized.
    - Uses a compatibility projection for requirement checks and legacy
    consumers instead of rejecting profiles that cannot round-trip through
    `SandboxPolicy` exactly.
    - Updates config loading, config overrides, session updates, turn
    context plumbing, prompt permission text, sandbox tags, and exec request
    construction to carry profile-backed runtime permissions.
    - Preserves configured deny-read entries and `glob_scan_max_depth` when
    command/session profiles are narrowed.
    - Adds `PermissionProfile::read_only()` and
    `PermissionProfile::workspace_write()` presets that match legacy
    defaults.
    
    ## Verification
    
    - `cargo test -p codex-core direct_write_roots`
    - `cargo test -p codex-core runtime_roots_to_legacy_projection`
    - `cargo test -p codex-app-server
    requested_permissions_trust_project_uses_permission_profile_intent`
    
    
    
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/19606).
    * #19395
    * #19394
    * #19393
    * #19392
    * __->__ #19606
  • Add goal core runtime (4 / 5) (#18076)
    Adds the core runtime behavior for active goals on top of the model
    tools from PR 3.
    
    ## Why
    
    A long-running goal should be a core runtime concern, not something
    every client has to implement. Core owns the turn lifecycle, tool
    completion boundaries, interruptions, resume behavior, and token usage,
    so it is the right place to account progress, enforce budgets, and
    decide when to continue work.
    
    ## What changed
    
    - Centralized goal lifecycle side effects behind
    `Session::goal_runtime_apply(GoalRuntimeEvent::...)`.
    - Starts goal continuation turns only when the session is idle; pending
    user input and mailbox work take priority.
    - Accounts token and wall-clock usage at turn, tool, mutation,
    interrupt, and resume boundaries; `get_thread_goal` remains read-only.
    - Preserves sub-second wall-clock remainder across accounting boundaries
    so long-running goals do not drift downward over time.
    - Treats token budget exhaustion as a soft stop by marking the goal
    `budget_limited` and injecting wrap-up steering instead of aborting the
    active turn.
    - Suppresses budget steering when `update_goal` marks a goal complete.
    - Pauses active goals on interrupt and auto-reactivates paused goals
    when a thread resumes outside plan mode.
    - Suppresses repeated automatic continuation when a continuation turn
    makes no tool calls.
    - Added continuation and budget-limit prompt templates.
    
    ## Verification
    
    - Added focused core coverage for continuation scheduling, accounting
    boundaries, budget-limit steering, completion accounting, interrupt
    pause behavior, resume auto-activation, and wall-clock remainder
    accounting.
  • Add agents.interrupt_message for interruption markers (#19351)
    ## Why
    
    Agent interruptions currently always persist a model-visible
    interrupted-turn marker before emitting `TurnAborted`. That marker is
    useful by default because it gives the next model turn context about a
    deliberately interrupted task, but some deployments need to suppress
    that history injection entirely while still keeping the client-visible
    interruption event.
    
    ## What changed
    
    - Add `[agents] interrupt_message = false` to disable the model-visible
    interrupted-turn marker.
    - Resolve the setting into `Config::agent_interrupt_message_enabled`,
    defaulting to `true` so existing behavior is unchanged.
    - Apply the setting to both live interrupted turns and interrupted fork
    snapshots.
    - Keep emitting `TurnAborted` even when the history marker is disabled.
    - Regenerate `core/config.schema.json` for the new
    `agents.interrupt_message` field.
    
    ## Testing
    
    - `cargo test -p codex-core load_config_resolves_agent_interrupt_message
    -- --nocapture`
    - `cargo test -p codex-core
    disabled_interrupted_fork_snapshot_appends_only_interrupt_event --
    --nocapture`
    - `cargo test -p codex-core
    multi_agent_v2_interrupted_marker_uses_developer_input_message --
    --nocapture`
    - `cargo test -p codex-core
    multi_agent_v2_followup_task_can_disable_interrupted_marker --
    --nocapture`
    - `cargo test -p codex-core
    multi_agent_v2_followup_task_interrupts_busy_child_without_losing_message
    -- --nocapture`
    - `cargo check -p codex-core`
  • Make MultiAgentV2 interruption markers assistant-authored (#19124)
    ## Why
    
    `MultiAgentV2` follow-up messages are delivered to agents as
    assistant-authored `InterAgentCommunication` envelopes. When
    `followup_task` used `interrupt: true`, the interrupted-turn guidance
    was still persisted as a contextual user message, so model-visible
    history made a system-generated interruption boundary look
    user-authored.
    
    This keeps interruption guidance consistent with the rest of the v2
    inter-agent message stream while preserving the legacy marker shape for
    non-v2 sessions.
    
    ## What changed
    
    - Make `interrupted_turn_history_marker` feature-aware.
    - Record the interrupted-turn marker as an assistant `OutputText`
    message when `Feature::MultiAgentV2` is enabled.
    - Keep the existing user contextual fragment for non-v2 sessions.
    - Apply the same feature-aware marker to interrupted fork snapshots.
    - Add coverage for the live `followup_task` interrupt path and the
    helper-level v2 marker shape.
    
    ## Testing
    
    - `cargo test -p codex-core
    multi_agent_v2_followup_task_interrupts_busy_child_without_losing_message
    -- --nocapture`
    - `cargo test -p codex-core
    multi_agent_v2_interrupted_marker_uses_assistant_output_message --
    --nocapture`
    - `cargo test -p codex-core interrupted_fork_snapshot -- --nocapture`
  • feat: let model providers own model discovery (#18950)
    ## Why
    
    `codex-models-manager` had grown to own provider-specific concerns:
    constructing OpenAI-compatible `/models` requests, resolving provider
    auth, emitting request telemetry, and deciding how provider catalogs
    should be sourced. That made the manager harder to reuse for providers
    whose model catalog is not fetched from the OpenAI `/models` endpoint,
    such as Amazon Bedrock.
    
    This change moves provider-specific model discovery behind
    provider-owned implementations, so the models manager can focus on
    refresh policy, cache behavior, picker ordering, and model metadata
    merging.
    
    ## What Changed
    
    - Introduced a `ModelsManager` trait with separate `OpenAiModelsManager`
    and `StaticModelsManager` implementations.
    - Added `ModelsEndpointClient` so OpenAI-compatible HTTP fetching lives
    outside `codex-models-manager`.
    - Moved `/models` request construction, provider auth resolution,
    timeout handling, and request telemetry into `codex-model-provider` via
    `OpenAiModelsEndpoint`.
    - Added provider-owned `models_manager(...)` construction so configured
    OpenAI-compatible providers use `OpenAiModelsManager`, while
    static/catalog-backed providers can return `StaticModelsManager`.
    - Added an Amazon Bedrock static model catalog for the GPT OSS Bedrock
    model IDs.
    - Updated core/session/thread manager code and tests to depend on
    `Arc<dyn ModelsManager>`.
    - Moved offline model test helpers into
    `codex_models_manager::test_support`.
    ## Metadata References
    
    The Bedrock catalog metadata is based on the official Amazon Bedrock
    OpenAI model documentation:
    
    - [Amazon Bedrock OpenAI
    models](https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-openai.html)
    lists the Bedrock model IDs, text input/output modalities, and `128,000`
    token context window for `gpt-oss-20b` and `gpt-oss-120b`.
    - [Amazon Bedrock `gpt-oss-120b` model
    card](https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-openai-gpt-oss-120b.html)
    lists the `bedrock-runtime` model ID `openai.gpt-oss-120b-1:0`, the
    `bedrock-mantle` model ID `openai.gpt-oss-120b`, text-only modalities,
    and `128K` context window.
    - [OpenAI `gpt-oss-120b` model
    docs](https://developers.openai.com/api/docs/models/gpt-oss-120b)
    document configurable reasoning effort with `low`, `medium`, and `high`,
    plus text input/output modality.
    
    The display names, default reasoning effort, and priority ordering are
    Codex-local catalog choices.
    
    ## Test Plan
    - Manually verified app-server model listing with an AWS profile:
    
    ```shell
    CODEX_HOME="$(mktemp -d)" cargo run -p codex-app-server-test-client -- \
      --codex-bin ./target/debug/codex \
      -c 'model_provider="amazon-bedrock"' \
      -c 'model_providers.amazon-bedrock.aws.profile="codex-bedrock"' \
      -c 'model_providers.amazon-bedrock.aws.region="us-west-2"' \
      model-list
    ```
    
    The response returned the Bedrock catalog with `openai.gpt-oss-120b-1:0`
    as the default model and `openai.gpt-oss-20b-1:0` as the second listed
    model, both text-only and supporting low/medium/high reasoning effort.
  • feat(auto-review) short-circuit (#18890)
    ## Summary
    Short circuit the convo if auto-review hits too many denials
    
    ## Testing
    - [x] Added unit tests
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • [codex-analytics] guardian review TTFT plumbing and emission (#17696)
    ## Why
    
    Guardian analytics includes time-to-first-token, but the Guardian
    reviewer runs as a normal Codex session and `TurnCompleteEvent` did not
    expose TTFT. The timing needs to flow through the standard
    turn-completion protocol so Guardian review analytics can consume the
    same value as the rest of the session machinery.
    
    ## What changed
    
    Adds optional `time_to_first_token_ms` to `TurnCompleteEvent` and
    populates it from `TurnTiming`. The value is carried through app-server
    thread history, rollout reconstruction, TUI/app-server adapters, and
    Guardian review session handling.
    
    Guardian review analytics now captures TTFT from the reviewer
    turn-complete event when available. Existing tests and fixtures are
    updated to set the new optional field to `None` where TTFT is not
    relevant.
    
    ## Verification
    
    - `cargo clippy -p codex-tui --tests -- -D warnings`
    - `cargo clippy -p codex-core --lib --tests -- -D warnings`
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/17696).
    * __->__ #17696
    * #17695
    * #17693
    * #18278
    * #18953
  • Add Windows sandbox unified exec runtime support (#15578)
    ## Summary
    
    This is the runtime/foundation half of the Windows sandbox unified-exec
    work.
    
    - add Windows sandbox `unified_exec` session support in
    `windows-sandbox-rs` for both:
      - the legacy restricted-token backend
      - the elevated runner backend
    - extend the PTY/process runtime so driver-backed sessions can support:
      - stdin streaming
      - stdout/stderr separation
      - exit propagation
      - PTY resize hooks
    - add Windows sandbox runtime coverage in `codex-windows-sandbox` /
    `codex-utils-pty`
    
    This PR does **not** enable Windows sandbox `UnifiedExec` for product
    callers yet because hooking this up to app-server comes in the next PR.
    
    Windows sandbox advertising is intentionally kept aligned with `main`,
    so sandboxed Windows callers still fall back to `ShellCommand`.
    
    This PR isolates the runtime/session layer so it can be reviewed
    independently from product-surface enablement.
    
    ---------
    
    Co-authored-by: jif-oai <jif@openai.com>
    Co-authored-by: Codex <noreply@openai.com>
  • Organize context fragments (#18794)
    Organize context fragments under `core/context`. Implement same trait on
    all of them.
  • Update models.json (#18586)
    - Replace the active models-manager catalog with the deleted core
    catalog contents.
    - Replace stale hardcoded test model slugs with current bundled model
    slugs.
    - Keep this as a stacked change on top of the cleanup PR.
  • feat: add metric to track the number of turns with memory usage (#18662)
    Add a metric `codex.turn.memory` to know if a turn used memories or not.
    This is not part of the other turn metrics as a label to limit
    cardinality
  • Move codex module under session (#18249)
    ## Summary
    - rename the core codex module root to session/mod.rs without using
    #[path]
    - move the codex module directory and tests under core/src/session
    - remove session/mod.rs reexports so call sites use explicit child
    module paths
    
    ## Testing
    - cargo test -p codex-core --lib
    - cargo check -p codex-core --tests
    - just fmt
    - just fix -p codex-core
    - git diff --check
  • feat: add opt-in provider runtime abstraction (#17713)
    ## Summary
    
    - Add `codex-model-provider` as the runtime home for model-provider
    behavior that does not belong in `codex-core`, `codex-login`, or
    `codex-api`.
    - The new crate wraps configured `ModelProviderInfo` in a
    `ModelProvider` trait object that can resolve the API provider config,
    provider-scoped auth manager, and request auth provider for each call.
    - This centralizes provider auth behavior in one place today, and gives
    us an extension point for future provider-specific auth, model listing,
    request setup, and related runtime behavior.
    
    ## Tests
    Ran tests manually to make sure that provider auth under different
    configs still work as expected.
    
    ---------
    
    Co-authored-by: pakrym-oai <pakrym@openai.com>
  • removing network proxy for yolo (#17742)
    **Summary**
    - prevent managed requirements.toml network settings from leaking into
    DangerFullAccess / yolo turns by gating managed proxy attachment on
    sandbox mode
    - keep guardian/sandboxed modes on the managed proxy path, while making
    true yolo bypass the proxy entirely, including /shell full-access
    commands
  • Spread AbsolutePathBuf (#17792)
    Mechanical change to promote absolute paths through code.
  • [codex-analytics] feature plumbing and emittance (#16640)
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/16640).
    * #16870
    * #16706
    * #16641
    * __->__ #16640
  • Build remote exec env from exec-server policy (#17216)
    ## Summary
    - add an exec-server `envPolicy` field; when present, the server starts
    from its own process env and applies the shell environment policy there
    - keep `env` as the exact environment for local/embedded starts, but
    make it an overlay for remote unified-exec starts
    - move the shell-environment-policy builder into `codex-config` so Core
    and exec-server share the inherit/filter/set/include behavior
    - overlay only runtime/sandbox/network deltas from Core onto the
    exec-server-derived env
    
    ## Why
    Remote unified exec was materializing the shell env inside Core and
    forwarding the whole map to exec-server, so remote processes could
    inherit the orchestrator machine's `HOME`, `PATH`, etc. This keeps the
    base env on the executor while preserving Core-owned runtime additions
    like `CODEX_THREAD_ID`, unified-exec defaults, network proxy env, and
    sandbox marker env.
    
    ## Validation
    - `just fmt`
    - `git diff --check`
    - `cargo test -p codex-exec-server --lib`
    - `cargo test -p codex-core --lib unified_exec::process_manager::tests`
    - `cargo test -p codex-core --lib exec_env::tests`
    - `cargo test -p codex-core --lib exec_env_tests` (compile-only; filter
    matched 0 tests)
    - `cargo test -p codex-config --lib shell_environment` (compile-only;
    filter matched 0 tests)
    - `just bazel-lock-update`
    
    ## Known local validation issue
    - `just bazel-lock-check` is not runnable in this checkout: it invokes
    `./scripts/check-module-bazel-lock.sh`, which is missing.
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
    Co-authored-by: pakrym-oai <pakrym@openai.com>
  • feat: make rollout recorder reliable against errors (#17214)
    The rollout writer now keeps an owned/monitored task handle, returns
    real Result acks for flush/persist/shutdown, retries failed flushes by
    reopening the rollout file, and keeps buffered items until they are
    successfully written. Session flushes are now real durability barriers
    for fork/rollback/read-after-write paths, while turn completion surfaces
    a warning if the rollout still cannot be saved after recovery.
  • fix: support split carveouts in windows elevated sandbox (#14568)
    ## Summary
    - preserve legacy Windows elevated sandbox behavior for existing
    policies
    - add elevated-only support for split filesystem policies that can be
    represented as readable-root overrides, writable-root overrides, and
    extra deny-write carveouts
    - resolve those elevated filesystem overrides during sandbox transform
    and thread them through setup and policy refresh
    - keep failing closed for explicit unreadable (`none`) carveouts and
    reopened writable descendants under read-only carveouts
    - for explicit read-only-under-writable-root carveouts, materialize
    missing carveout directories during elevated setup before applying the
    deny-write ACL
    - document the elevated vs restricted-token support split in the core
    README
    
    ## Example
    Given a split filesystem policy like:
    
    ```toml
    ":root" = "read"
    ":cwd" = "write"
    "./docs" = "read"
    "C:/scratch" = "write"
    ```
    
    the elevated backend now provisions the readable-root overrides,
    writable-root overrides, and extra deny-write carveouts during setup and
    refresh instead of collapsing back to the legacy workspace-only shape.
    
    If a read-only carveout under a writable root is missing at setup time,
    elevated setup creates that carveout as an empty directory before
    applying its deny-write ACE; otherwise the sandboxed command could
    create it later and bypass the carveout. This is only for explicit
    policy carveouts. Best-effort workspace protections like `.codex/` and
    `.agents/` still skip missing directories.
    
    A policy like:
    
    ```toml
    "/workspace" = "write"
    "/workspace/docs" = "read"
    "/workspace/docs/tmp" = "write"
    ```
    
    still fails closed, because the elevated backend does not reopen
    writable descendants under read-only carveouts yet.
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • Use AbsolutePathBuf for exec cwd plumbing (#17063)
    ## Summary
    - Carry `AbsolutePathBuf` through tool cwd parsing/resolution instead of
    resolving workdirs to raw `PathBuf`s.
    - Type exec/sandbox request cwd fields as `AbsolutePathBuf` through
    `ExecParams`, `ExecRequest`, `SandboxCommand`, and unified exec runtime
    requests.
    - Keep `PathBuf` conversions at external/event boundaries and update
    existing tests/fixtures for the typed cwd.
    
    ## Validation
    - `cargo check -p codex-core --tests`
    - `cargo check -p codex-sandboxing --tests`
    - `cargo test -p codex-sandboxing`
    - `cargo test -p codex-core --lib tools::handlers::`
    - `just fix -p codex-sandboxing`
    - `just fix -p codex-core`
    - `just fmt`
    
    Full `codex-core` test suite was not run locally; per repo guidance I
    kept local validation targeted.
  • Fix nested exec thread ID restore (#16882)
    Addresses #15527
    
    Problem: Nested `codex exec` commands could source a shell snapshot that
    re-exported the parent `CODEX_THREAD_ID`, so commands inside the nested
    session were attributed to the wrong thread.
    
    Solution: Reapply the live command env's `CODEX_THREAD_ID` after
    sourcing the snapshot.
  • [codex] reduce module visibility (#16978)
    ## Summary
    - reduce public module visibility across Rust crates, preferring private
    or crate-private modules with explicit crate-root public exports
    - update external call sites and tests to use the intended public crate
    APIs instead of reaching through module trees
    - add the module visibility guideline to AGENTS.md
    
    ## Validation
    - `cargo check --workspace --all-targets --message-format=short` passed
    before the final fix/format pass
    - `just fix` completed successfully
    - `just fmt` completed successfully
    - `git diff --check` passed
  • Honor null thread instructions (#16964)
    - Treat explicit null thread instructions as a blank-slate override
    while preserving omitted-field fallback behavior.
    - Preserve null through rollout resume/fork and keep explicit empty
    strings distinct.
    - Add app-server v2 start/fork coverage for the tri-state instruction
    params.
  • [codex-analytics] add protocol-native turn timestamps (#16638)
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/16638).
    * #16870
    * #16706
    * #16659
    * #16641
    * #16640
    * __->__ #16638
  • remove temporary ownership re-exports (#16626)
    Stacked on #16508.
    
    This removes the temporary `codex-core` / `codex-login` re-export shims
    from the ownership split and rewrites callsites to import directly from
    `codex-model-provider-info`, `codex-models-manager`, `codex-api`,
    `codex-protocol`, `codex-feedback`, and `codex-response-debug-context`.
    
    No behavior change intended; this is the mechanical import cleanup layer
    split out from the ownership move.
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • core: cut codex-core compile time 48% with native async SessionTask (#16631)
    ## Why
    
    This continues the compile-time cleanup from #16630. `SessionTask`
    implementations are monomorphized, but `Session` stores the task behind
    a `dyn` boundary so it can drive and abort heterogenous turn tasks
    uniformly. That means we can move the `#[async_trait]` expansion off the
    implementation trait, keep a small boxed adapter only at the storage
    boundary, and preserve the existing task lifecycle semantics while
    reducing the amount of generated async-trait glue in `codex-core`.
    
    One measurement caveat showed up while exploring this: a warm
    incremental benchmark based on `touch core/src/tasks/mod.rs && cargo
    check -p codex-core --lib` was basically flat, but that was the wrong
    benchmark for this change. Using package-clean `codex-core` rebuilds,
    like #16630, shows the real win.
    
    Relevant pre-change code:
    
    - [`SessionTask` with
    `#[async_trait]`](https://github.com/openai/codex/blob/3c7f013f9735e67796c70d95f75f436b7f97e3ec/codex-rs/core/src/tasks/mod.rs#L129-L182)
    - [`RunningTask` storing `Arc<dyn
    SessionTask>`](https://github.com/openai/codex/blob/3c7f013f9735e67796c70d95f75f436b7f97e3ec/codex-rs/core/src/state/turn.rs#L69-L77)
    
    ## What changed
    
    - Switched `SessionTask::{run, abort}` to native RPITIT futures with
    explicit `Send` bounds.
    - Added a private `AnySessionTask` adapter that boxes those futures only
    at the `Arc<dyn ...>` storage boundary.
    - Updated `RunningTask` to store `Arc<dyn AnySessionTask>` and removed
    `#[async_trait]` from the concrete task impls plus test-only
    `SessionTask` impls.
    
    ## Timing
    
    Benchmarked package-clean `codex-core` rebuilds with dependencies left
    warm:
    
    ```shell
    cargo check -p codex-core --lib >/dev/null
    cargo clean -p codex-core >/dev/null
    /usr/bin/time -p cargo +nightly rustc -p codex-core --lib -- \
      -Z time-passes \
      -Z time-passes-format=json >/dev/null
    ```
    
    | revision | rustc `total` | process `real` | `generate_crate_metadata`
    | `MIR_borrow_checking` | `monomorphization_collector_graph_walk` |
    | --- | ---: | ---: | ---: | ---: | ---: |
    | parent `3c7f013f9735` | 67.21s | 67.71s | 24.61s | 23.43s | 22.43s |
    | this PR `2cafd783ac22` | 35.08s | 35.60s | 8.01s | 7.25s | 7.15s |
    | delta | -47.8% | -47.4% | -67.5% | -69.1% | -68.1% |
    
    For completeness, the warm touched-file benchmark stayed flat (`1.96s`
    parent vs `1.97s` this PR), which is why that benchmark should not be
    used to evaluate this refactor.
    
    ## Verification
    
    - Ran `cargo test -p codex-core`; this change compiled and task-related
    tests passed before hitting the same unrelated 5
    `config::tests::*guardian*` failures already present on the parent
    stack.
  • core: remove cross-crate re-exports from lib.rs (#16512)
    ## Why
    
    `codex-core` was re-exporting APIs owned by sibling `codex-*` crates,
    which made downstream crates depend on `codex-core` as a proxy module
    instead of the actual owner crate.
    
    Removing those forwards makes crate boundaries explicit and lets leaf
    crates drop unnecessary `codex-core` dependencies. In this PR, this
    reduces the dependency on `codex-core` to `codex-login` in the following
    files:
    
    ```
    codex-rs/backend-client/Cargo.toml
    codex-rs/mcp-server/tests/common/Cargo.toml
    ```
    
    ## What
    
    - Remove `codex-rs/core/src/lib.rs` re-exports for symbols owned by
    `codex-login`, `codex-mcp`, `codex-rollout`, `codex-analytics`,
    `codex-protocol`, `codex-shell-command`, `codex-sandboxing`,
    `codex-tools`, and `codex-utils-path`.
    - Delete the `default_client` forwarding shim in `codex-rs/core`.
    - Update in-crate and downstream callsites to import directly from the
    owning `codex-*` crate.
    - Add direct Cargo dependencies where callsites now target the owner
    crate, and remove `codex-core` from `codex-rs/backend-client`.
  • fix(core) rm execute_exec_request sandbox_policy (#16422)
    ## Summary
    In #11871 we started consolidating on ExecRequest.sandbox_policy instead
    of passing in a separate policy object that theoretically could differ
    (but did not). This finishes the some parameter cleanup.
    
    This should be a simple noop, since all 3 callsites of this function
    already used a cloned object from the ExecRequest value.
    
    ## Testing
    - [x] Existing tests pass
  • feat: add mailbox concept for wait (#16010)
    Add a mailbox we can use for inter-agent communication
    `wait` is now based on it and don't take target anymore
  • chore: clean up argument-comment lint and roll out all-target CI on macOS (#16054)
    ## Why
    
    `argument-comment-lint` was green in CI even though the repo still had
    many uncommented literal arguments. The main gap was target coverage:
    the repo wrapper did not force Cargo to inspect test-only call sites, so
    examples like the `latest_session_lookup_params(true, ...)` tests in
    `codex-rs/tui_app_server/src/lib.rs` never entered the blocking CI path.
    
    This change cleans up the existing backlog, makes the default repo lint
    path cover all Cargo targets, and starts rolling that stricter CI
    enforcement out on the platform where it is currently validated.
    
    ## What changed
    
    - mechanically fixed existing `argument-comment-lint` violations across
    the `codex-rs` workspace, including tests, examples, and benches
    - updated `tools/argument-comment-lint/run-prebuilt-linter.sh` and
    `tools/argument-comment-lint/run.sh` so non-`--fix` runs default to
    `--all-targets` unless the caller explicitly narrows the target set
    - fixed both wrappers so forwarded cargo arguments after `--` are
    preserved with a single separator
    - documented the new default behavior in
    `tools/argument-comment-lint/README.md`
    - updated `rust-ci` so the macOS lint lane keeps the plain wrapper
    invocation and therefore enforces `--all-targets`, while Linux and
    Windows temporarily pass `-- --lib --bins`
    
    That temporary CI split keeps the stricter all-targets check where it is
    already cleaned up, while leaving room to finish the remaining Linux-
    and Windows-specific target-gated cleanup before enabling
    `--all-targets` on those runners. The Linux and Windows failures on the
    intermediate revision were caused by the wrapper forwarding bug, not by
    additional lint findings in those lanes.
    
    ## Validation
    
    - `bash -n tools/argument-comment-lint/run.sh`
    - `bash -n tools/argument-comment-lint/run-prebuilt-linter.sh`
    - shell-level wrapper forwarding check for `-- --lib --bins`
    - shell-level wrapper forwarding check for `-- --tests`
    - `just argument-comment-lint`
    - `cargo test` in `tools/argument-comment-lint`
    - `cargo test -p codex-terminal-detection`
    
    ## Follow-up
    
    - Clean up remaining Linux-only target-gated callsites, then switch the
    Linux lint lane back to the plain wrapper invocation.
    - Clean up remaining Windows-only target-gated callsites, then switch
    the Windows lint lane back to the plain wrapper invocation.
  • fix: fix Windows CI regression introduced in #15999 (#16027)
    #15999 introduced a Windows-only `\r\n` mismatch in review-exit template
    handling. This PR normalizes those template newlines and separates that
    fix from [#16014](https://github.com/openai/codex/pull/16014) so it can
    be reviewed independently.
  • Use AbsolutePathBuf for cwd state (#15710)
    Migrate `cwd` and related session/config state to `AbsolutePathBuf` so
    downstream consumers consistently see absolute working directories.
    
    Add test-only `.abs()` helpers for `Path`, `PathBuf`, and `TempDir`, and
    update branch-local tests to use them instead of
    `AbsolutePathBuf::try_from(...)`.
    
    For the remaining TUI/app-server snapshot coverage that renders absolute
    cwd values, keep the snapshots unchanged and skip the Windows-only cases
    where the platform-specific absolute path layout differs.
  • fix: support split carveouts in windows restricted-token sandbox (#14172)
    ## Summary
    - keep legacy Windows restricted-token sandboxing as the supported
    baseline
    - support the split-policy subset that restricted-token can enforce
    directly today
    - support full-disk read, the same writable root set as legacy
    `WorkspaceWrite`, and extra read-only carveouts under those writable
    roots via additional deny-write ACLs
    - continue to fail closed for unsupported split-only shapes, including
    explicit unreadable (`none`) carveouts, reopened writable descendants
    under read-only carveouts, and writable root sets that do not match the
    legacy workspace roots
    
    ## Example
    Given a filesystem policy like:
    
    ```toml
    ":root" = "read"
    ":cwd" = "write"
    "./docs" = "read"
    ```
    
    the restricted-token backend can keep the workspace writable while
    denying writes under `docs` by layering an extra deny-write carveout on
    top of the legacy workspace-write roots.
    
    A policy like:
    
    ```toml
    "/workspace" = "write"
    "/workspace/docs" = "read"
    "/workspace/docs/tmp" = "write"
    ```
    
    still fails closed, because the unelevated backend cannot reopen the
    nested writable descendant safely.
    
    ## Stack
    -> fix: support split carveouts in windows restricted-token sandbox
    #14172
    fix: support split carveouts in windows elevated sandbox #14568
  • Drop sandbox_permissions from sandbox exec requests (#15665)
    ## Summary
    - drop `sandbox_permissions` from the sandboxing `ExecOptions` and
    `ExecRequest` adapter types
    - remove the now-unused plumbing from shell, unified exec, JS REPL, and
    apply-patch runtime call sites
    - default reconstructed `ExecParams` to `SandboxPermissions::UseDefault`
    where the lower-level API still requires the field
    
    ## Testing
    - `just fmt`
    - `just argument-comment-lint`
    - `cargo test -p codex-core` (still running locally; first failures
    observed in `suite::cli_stream::responses_mode_stream_cli`,
    `suite::cli_stream::responses_mode_stream_cli_supports_openai_base_url_config_override`,
    and
    `suite::cli_stream::responses_mode_stream_cli_supports_openai_base_url_env_fallback`)
  • Move git utilities into a dedicated crate (#15564)
    - create `codex-git-utils` and move the shared git helpers into it with
    file moves preserved for diff readability
    - move the `GitInfo` helpers out of `core` so stacked rollout work can
    depend on the shared crate without carrying its own git info module
    
    ---------
    
    Co-authored-by: Ahmed Ibrahim <219906144+aibrahim-oai@users.noreply.github.com>
    Co-authored-by: Codex <noreply@openai.com>
  • feat: disable notifier v2 and start turn on agent interaction (#15624)
    Make the inter-agent communication start a turn
    
    As part of this, we disable the v2 notifier to prevent some odd
    behaviour where the agent restart working while you're talking to it for
    example
  • Add fork snapshot modes (#15239)
    ## Summary
    - add `ForkSnapshotMode` to `ThreadManager::fork_thread` so callers can
    request either a committed snapshot or an interrupted snapshot
    - share the model-visible `<turn_aborted>` history marker between the
    live interrupt path and interrupted forks
    - update the small set of direct fork callsites to pass
    `ForkSnapshotMode::Committed`
    
    Note: this enables /btw to work similarly as Esc to interrupt (hopefully
    somewhat in distribution)
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>