Commit Graph

86 Commits

  • rollout: persist turn permission profiles (#18281)
    ## Why
    
    Resume and reconstruction need to preserve the permissions that were
    active for each user turn. If rollouts only keep legacy sandbox fields,
    replay cannot faithfully represent profile-shaped overrides introduced
    earlier in the stack.
    
    ## What changed
    
    This records `permission_profile` on user-turn rollout events,
    reconstructs it through history/state extraction, and updates rollout
    reconstruction and related fixtures to keep the field explicit.
    
    ## Verification
    
    - `cargo test -p codex-core --test all permissions_messages --
    --nocapture`
    - `cargo test -p codex-core --test all request_permissions --
    --nocapture`
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/18281).
    * #18288
    * #18287
    * #18286
    * #18285
    * #18284
    * #18283
    * #18282
    * __->__ #18281
  • Split DeveloperInstructions into individual fragments. (#18813)
    Split DeveloperInstructions into individual fragments.
  • Organize context fragments (#18794)
    Organize context fragments under `core/context`. Implement same trait on
    all of them.
  • Update image outputs to default to high detail (#18386)
    Do not assume the default `detail`.
  • 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(permissions): add glob deny-read policy support (#15979)
    ## Summary
    - adds first-class filesystem policy entries for deny-read glob patterns
    - parses config such as :project_roots { "**/*.env" = "none" } into
    pattern entries
    - enforces deny-read patterns in direct read/list helpers
    - fails closed for sandbox execution until platform backends enforce
    glob patterns in #18096
    - preserves split filesystem policy in turn context only when it cannot
    be reconstructed from legacy sandbox policy
    
    ## Stack
    1. This PR - glob deny-read policy/config/direct-tool support
    2. #18096 - macOS and Linux sandbox enforcement
    3. #17740 - managed deny-read requirements
    
    ## Verification
    - just fmt
    - cargo check -p codex-core -p codex-sandboxing --tests
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • feat(guardian): send only transcript deltas on guardian followups (#17269)
    ## Description
    
    We reuse a guardian thread for a given user thread when we can. However,
    we had always sent the full transcript history every time we made a
    followup review request to an existing guardian thread.
    
    This is especially bad for long guardian threads since we keep
    re-appending old transcript entries instead of just what has changed.
    The fix is to just send what's new.
    
    **Caveat**: Whenever a thread is compacted or rolled back, we fall back
    to sending the full transcript to guardian again since the thread's
    history has been modified. However in the happy path we get a nice
    optimization.
    
    ## Before
    Initial guardian review sends the full parent transcript:
    
    ```
    The following is the Codex agent history whose request action you are assessing...
    >>> TRANSCRIPT START
    [1] user: Please check the repo visibility and push the docs fix if needed.
    [2] tool gh_repo_view call: {"repo":"openai/codex"}
    [3] tool gh_repo_view result: repo visibility: public
    [4] assistant: The repo is public; I now need approval to push the docs fix.
    >>> TRANSCRIPT END
    The Codex agent has requested the following action:
    >>> APPROVAL REQUEST START
    ...
    >>> APPROVAL REQUEST END
    ```
    
    And a followup to the same guardian thread would send the full
    transcript again (including items 1-4 we already sent):
    ```
    The following is the Codex agent history whose request action you are assessing...
    >>> TRANSCRIPT START
    [1] user: Please check the repo visibility and push the docs fix if needed.
    [2] tool gh_repo_view call: {"repo":"openai/codex"}
    [3] tool gh_repo_view result: repo visibility: public
    [4] assistant: The repo is public; I now need approval to push the docs fix.
    [5] user: Please push the second docs fix too.
    [6] assistant: I need approval for the second docs fix.
    >>> TRANSCRIPT END
    The Codex agent has requested the following action:
    >>> APPROVAL REQUEST START
    ...
    >>> APPROVAL REQUEST END
    ```
    
    ## After
    Initial guardian review sends the full parent transcript (this is
    unchanged):
    
    ```
    The following is the Codex agent history whose request action you are assessing...
    >>> TRANSCRIPT START
    [1] user: Please check the repo visibility and push the docs fix if needed.
    [2] tool gh_repo_view call: {"repo":"openai/codex"}
    [3] tool gh_repo_view result: repo visibility: public
    [4] assistant: The repo is public; I now need approval to push the docs fix.
    >>> TRANSCRIPT END
    The Codex agent has requested the following action:
    >>> APPROVAL REQUEST START
    ...
    >>> APPROVAL REQUEST END
    ```
    
    But a followup now sends:
    ```
    The following is the Codex agent history added since your last approval assessment. Continue the same review conversation...
    >>> TRANSCRIPT DELTA START
    [5] user: Please push the second docs fix too.
    [6] assistant: I need approval for the second docs fix.
    >>> TRANSCRIPT DELTA END
    The Codex agent has requested the following next action:
    >>> APPROVAL REQUEST START
    ...
    >>> APPROVAL REQUEST END
    ```
  • [codex] allow disabling environment context injection (#16745)
    This adds an `include_environment_context` config/profile flag that
    defaults on, and guards both initial injection and later environment
    updates to allow skipping injection of `<environment_context>`.
  • [codex] allow disabling prompt instruction blocks (#16735)
    This PR adds root and profile config switches to omit the generated
    `<permissions instructions>` and `<apps_instructions>` prompt blocks
    while keeping both enabled by default, and it gates both the initial
    developer-context injection and later permissions diff injection so
    turning the permissions block off stays effective across turn-context
    overrides.
    
    Also added a prompt debug tool that can be used as `codex debug
    prompt-input "hello"` and dumps the constructed items list.
  • 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.
  • [codex] Defer fork context injection until first turn (#15699)
    ## Summary
    - remove the fork-startup `build_initial_context` injection
    - keep the reconstructed `reference_context_item` as the fork baseline
    until the first real turn
    - update fork-history tests and the request snapshot, and add a
    `TODO(ccunningham)` for remaining nondiffable initial-context inputs
    
    ## Why
    Fork startup was appending current-session initial context immediately
    after reconstructing the parent rollout, then the first real turn could
    emit context updates again. That duplicated model-visible context in the
    child rollout.
    
    ## Impact
    Forked sessions now behave like resume for context seeding: startup
    reconstructs history and preserves the prior baseline, and the first
    real turn handles any current-session context emission.
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • Move string truncation helpers into codex-utils-string (#15572)
    - move the shared byte-based middle truncation logic from `core` into
    `codex-utils-string`
    - keep token-specific truncation in `codex-core` so rollout can reuse
    the shared helper in the next stacked PR
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • 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>
  • Trim pre-turn context updates during rollback (#15577)
    ## Summary
    - trim contiguous developer/contextual-user pre-turn updates when
    rollback cuts back to a user turn
    - add a focused history regression test for the trim behavior
    - update the rollback request-boundary snapshots to show the fixed
    non-duplicating context shape
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • feat: communication pattern v2 (#15647)
    See internal communication
  • 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
  • feat: use serde to differenciate inter agent communication (#15560)
    Use `serde` to encode the inter agent communication to an assistant
    message and use the decode to see if this is such a message
    
    Note: this assume serde on small pattern is fast enough
  • feat: new op type for sub-agents communication (#15556)
    Add `InterAgentCommunication` for v2 agent communication
  • feat: structured multi-agent output (#15515)
    Send input now sends messages as assistant message and with this format:
    
    ```
    author: /root/worker_a
    recipient: /root/worker_a/tester
    other_recipients: []
    Content: bla bla bla. Actual content. Only text for now
    ```
  • chore(context) Include guardian approval context (#15366)
    ## Summary
    Include the guardian context in the developer message for approvals
    
    ## Testing
    - [x] Updated unit tests
  • Split features into codex-features crate (#15253)
    - Split the feature system into a new `codex-features` crate.
    - Cut `codex-core` and workspace consumers over to the new config and
    warning APIs.
    
    Co-authored-by: Ahmed Ibrahim <219906144+aibrahim-oai@users.noreply.github.com>
    Co-authored-by: Codex <noreply@openai.com>
  • [hooks] use a user message > developer message for prompt continuation (#14867)
    ## Summary
    
    Persist Stop-hook continuation prompts as `user` messages instead of
    hidden `developer` messages + some requested integration tests
    
    This is a followup to @pakrym 's comment in
    https://github.com/openai/codex/pull/14532 to make sure stop-block
    continuation prompts match training for turn loops
    
    - Stop continuation now writes `<hook_prompt hook_run_id="...">stop
    hook's user prompt<hook_prompt>`
    - Introduces quick-xml dependency, though we already indirectly depended
    on it anyway via syntect
    - This PR only has about 500 lines of actual logic changes, the rest is
    tests/schema
    
    ## Testing
    
    Example run (with a sessionstart hook and 3 stop hooks) - this shows
    context added by session start, then two stop hooks sending their own
    additional prompts in a new turn. The model responds with a single
    message addressing both. Then when that turn ends, the hooks detect that
    they just ran using `stop_hook_active` and decide not to infinite loop
    
    test files for this (unzip, move codex -> .codex):
    [codex.zip](https://github.com/user-attachments/files/26075806/codex.zip)
    
    ```
    › cats
    
    
    • Running SessionStart hook: lighting the observatory
    
    SessionStart hook (completed)
      warning: Hi, I'm a session start hook for wizard-tower (startup).
      hook context: A wimboltine stonpet is an exotic cuisine from hyperspace
    
    • Cats are tiny zen wizards, my friend: equal parts nap, mystery, and chaos. If you want, we can talk
      cat facts, cat breeds, cat names, or build something cat-themed in this repo.
    
    • Running Stop hook: checking the tower wards
    
    • Running Stop hook: sacking the guards
    
    • Running Stop hook: hiring the guards
    
    Stop hook (completed)
      warning: Wizard Tower Stop hook reviewed the completed reply (177 chars).
    
    Stop hook (blocked)
      warning: Wizard Tower Stop hook continuing conversation
      feedback: cook the stonpet
    
    Stop hook (blocked)
      warning: Wizard Tower Stop hook continuing conversation
      feedback: eat the cooked stonpet
    
    • Stonpet’s cooked, aloha style: flash-seared over a blue quasiflame, glazed with nebula salt, and
      rested until the hyperspace juices settle.
    
      Now we eat with gratitude, my friend. One mindful bite in, and the flavor is pure cosmic surf:
      smoky, bright, and totally out of this dimension.
    
    • Running Stop hook: checking the tower wards
    
    • Running Stop hook: sacking the guards
    
    • Running Stop hook: hiring the guards
    
    Stop hook (completed)
      warning: Wizard Tower Stop hook reviewed the completed reply (285 chars).
    
    Stop hook (completed)
      warning: Wizard Tower Stop hook saw a second pass and stayed calm to avoid a loop.
    
    Stop hook (completed)
      warning: Wizard Tower Stop hook saw a second pass and stayed calm to avoid a loop.
    ```
  • Add notify to code-mode (#14842)
    Allows model to send an out-of-band notification.
    
    The notification is injected as another tool call output for the same
    call_id.
  • Apply argument comment lint across codex-rs (#14652)
    ## Why
    
    Once the repo-local lint exists, `codex-rs` needs to follow the
    checked-in convention and CI needs to keep it from drifting. This commit
    applies the fallback `/*param*/` style consistently across existing
    positional literal call sites without changing those APIs.
    
    The longer-term preference is still to avoid APIs that require comments
    by choosing clearer parameter types and call shapes. This PR is
    intentionally the mechanical follow-through for the places where the
    existing signatures stay in place.
    
    After rebasing onto newer `main`, the rollout also had to cover newly
    introduced `tui_app_server` call sites. That made it clear the first cut
    of the CI job was too expensive for the common path: it was spending
    almost as much time installing `cargo-dylint` and re-testing the lint
    crate as a representative test job spends running product tests. The CI
    update keeps the full workspace enforcement but trims that extra
    overhead from ordinary `codex-rs` PRs.
    
    ## What changed
    
    - keep a dedicated `argument_comment_lint` job in `rust-ci`
    - mechanically annotate remaining opaque positional literals across
    `codex-rs` with exact `/*param*/` comments, including the rebased
    `tui_app_server` call sites that now fall under the lint
    - keep the checked-in style aligned with the lint policy by using
    `/*param*/` and leaving string and char literals uncommented
    - cache `cargo-dylint`, `dylint-link`, and the relevant Cargo
    registry/git metadata in the lint job
    - split changed-path detection so the lint crate's own `cargo test` step
    runs only when `tools/argument-comment-lint/*` or `rust-ci.yml` changes
    - continue to run the repo wrapper over the `codex-rs` workspace, so
    product-code enforcement is unchanged
    
    Most of the code changes in this commit are intentionally mechanical
    comment rewrites or insertions driven by the lint itself.
    
    ## Verification
    
    - `./tools/argument-comment-lint/run.sh --workspace`
    - `cargo test -p codex-tui-app-server -p codex-tui`
    - parsed `.github/workflows/rust-ci.yml` locally with PyYAML
    
    ---
    
    * -> #14652
    * #14651
  • sending back imagaegencall response back to responseapi (#14558)
    Sending back the ResponseItem::ImageGenerationCall as is, because it is
    now supported from the API-side.
  • feat: search_tool migrate to bring you own tool of Responses API (#14274)
    ## Why
    
    to support a new bring your own search tool in Responses
    API(https://developers.openai.com/api/docs/guides/tools-tool-search#client-executed-tool-search)
    we migrating our bm25 search tool to use official way to execute search
    on client and communicate additional tools to the model.
    
    ## What
    - replace the legacy `search_tool_bm25` flow with client-executed
    `tool_search`
    - add protocol, SSE, history, and normalization support for
    `tool_search_call` and `tool_search_output`
    - return namespaced Codex Apps search results and wire namespaced
    follow-up tool calls back into MCP dispatch
  • Add realtime start instructions config override (#14270)
    - add `realtime_start_instructions` config support
    - thread it into realtime context updates, schema, docs, and tests
  • unifying all image saves to /tmp to bug-proof (#14149)
    image-gen feature will have the model saving to /tmp by default + at all
    times
  • pass on save info to model + ui tweaks (#14123)
    Passing on more information to the model for context purposes, to
    streamline image-identification.
  • guardian initial feedback / tweaks (#13897)
    ## Summary
    - remove the remaining model-visible guardian-specific `on-request`
    prompt additions so enabling the feature does not change the main
    approval-policy instructions
    - neutralize user-facing guardian wording to talk about automatic
    approval review / approval requests rather than a second reviewer or
    only sandbox escalations
    - tighten guardian retry-context handling so agent-authored
    `justification` stays in the structured action JSON and is not also
    injected as raw retry context
    - simplify guardian review plumbing in core by deleting dead
    prompt-append paths and trimming some request/transcript setup code
    
    ## Notable Changes
    - delete the dead `permissions/approval_policy/guardian.md` append path
    and stop threading `guardian_approval_enabled` through model-facing
    developer-instruction builders
    - rename the experimental feature copy to `Automatic approval review`
    and update the `/experimental` snapshot text accordingly
    - make approval-review status strings generic across shell, patch,
    network, and MCP review types
    - forward real sandbox/network retry reasons for shell and unified-exec
    guardian review, but do not pass agent-authored justification as raw
    retry context
    - simplify `guardian.rs` by removing the one-field request wrapper,
    deduping reasoning-effort selection, and cleaning up transcript entry
    collection
    
    ## Testing
    - `just fmt`
    - full validation left to CI
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • Add guardian approval MVP (#13692)
    ## Summary
    - add the guardian reviewer flow for `on-request` approvals in command,
    patch, sandbox-retry, and managed-network approval paths
    - keep guardian behind `features.guardian_approval` instead of exposing
    a public `approval_policy = guardian` mode
    - route ordinary `OnRequest` approvals to the guardian subagent when the
    feature is enabled, without changing the public approval-mode surface
    
    ## Public model
    - public approval modes stay unchanged
    - guardian is enabled via `features.guardian_approval`
    - when that feature is on, `approval_policy = on-request` keeps the same
    approval boundaries but sends those approval requests to the guardian
    reviewer instead of the user
    - `/experimental` only persists the feature flag; it does not rewrite
    `approval_policy`
    - CLI and app-server no longer expose a separate `guardian` approval
    mode in this PR
    
    ## Guardian reviewer
    - the reviewer runs as a normal subagent and reuses the existing
    subagent/thread machinery
    - it is locked to a read-only sandbox and `approval_policy = never`
    - it does not inherit user/project exec-policy rules
    - it prefers `gpt-5.4` when the current provider exposes it, otherwise
    falls back to the parent turn's active model
    - it fail-closes on timeout, startup failure, malformed output, or any
    other review error
    - it currently auto-approves only when `risk_score < 80`
    
    ## Review context and policy
    - guardian mirrors `OnRequest` approval semantics rather than
    introducing a separate approval policy
    - explicit `require_escalated` requests follow the same approval surface
    as `OnRequest`; the difference is only who reviews them
    - managed-network allowlist misses that enter the approval flow are also
    reviewed by guardian
    - the review prompt includes bounded recent transcript history plus
    recent tool call/result evidence
    - transcript entries and planned-action strings are truncated with
    explicit `<guardian_truncated ... />` markers so large payloads stay
    bounded
    - apply-patch reviews include the full patch content (without
    duplicating the structured `changes` payload)
    - the guardian request layout is snapshot-tested using the same
    model-visible Responses request formatter used elsewhere in core
    
    ## Guardian network behavior
    - the guardian subagent inherits the parent session's managed-network
    allowlist when one exists, so it can use the same approved network
    surface while reviewing
    - exact session-scoped network approvals are copied into the guardian
    session with protocol/port scope preserved
    - those copied approvals are now seeded before the guardian's first turn
    is submitted, so inherited approvals are available during any immediate
    review-time checks
    
    ## Out of scope / follow-ups
    - the sandbox-permission validation split was pulled into a separate PR
    and is not part of this diff
    - a future follow-up can enable `serde_json` preserve-order in
    `codex-core` and then simplify the guardian action rendering further
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • image-gen-core (#13290)
    Core tool-calling for image-gen, handles requesting and receiving logic
    for images using response API
  • Add under-development original-resolution view_image support (#13050)
    ## Summary
    
    Add original-resolution support for `view_image` behind the
    under-development `view_image_original_resolution` feature flag.
    
    When the flag is enabled and the target model is `gpt-5.3-codex` or
    newer, `view_image` now preserves original PNG/JPEG/WebP bytes and sends
    `detail: "original"` to the Responses API instead of using the legacy
    resize/compress path.
    
    ## What changed
    
    - Added `view_image_original_resolution` as an under-development feature
    flag.
    - Added `ImageDetail` to the protocol models and support for serializing
    `detail: "original"` on tool-returned images.
    - Added `PromptImageMode::Original` to `codex-utils-image`.
      - Preserves original PNG/JPEG/WebP bytes.
      - Keeps legacy behavior for the resize path.
    - Updated `view_image` to:
    - use the shared `local_image_content_items_with_label_number(...)`
    helper in both code paths
      - select original-resolution mode only when:
        - the feature flag is enabled, and
        - the model slug parses as `gpt-5.3-codex` or newer
    - Kept local user image attachments on the existing resize path; this
    change is specific to `view_image`.
    - Updated history/image accounting so only `detail: "original"` images
    use the docs-based GPT-5 image cost calculation; legacy images still use
    the old fixed estimate.
    - Added JS REPL guidance, gated on the same feature flag, to prefer JPEG
    at 85% quality unless lossless is required, while still allowing other
    formats when explicitly requested.
    - Updated tests and helper code that construct
    `FunctionCallOutputContentItem::InputImage` to carry the new `detail`
    field.
    
    ## Behavior
    
    ### Feature off
    - `view_image` keeps the existing resize/re-encode behavior.
    - History estimation keeps the existing fixed-cost heuristic.
    
    ### Feature on + `gpt-5.3-codex+`
    - `view_image` sends original-resolution images with `detail:
    "original"`.
    - PNG/JPEG/WebP source bytes are preserved when possible.
    - History estimation uses the GPT-5 docs-based image-cost calculation
    for those `detail: "original"` images.
    
    
    #### [git stack](https://github.com/magus/git-stack-cli)
    - 👉 `1` https://github.com/openai/codex/pull/13050
    -  `2` https://github.com/openai/codex/pull/13331
    -  `3` https://github.com/openai/codex/pull/13049
  • Record realtime close marker on replacement (#13058)
    ## Summary
    - record a realtime close developer message when a new realtime session
    replaces an active one
    - assert the replacement marker through the mocked responses request
    path
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
    Co-authored-by: Charles Cunningham <ccunningham@openai.com>
  • Support multimodal custom tool outputs (#12948)
    ## Summary
    
    This changes `custom_tool_call_output` to use the same output payload
    shape as `function_call_output`, so freeform tools can return either
    plain text or structured content items.
    
    The main goal is to let `js_repl` return image content from nested
    `view_image` calls in its own `custom_tool_call_output`, instead of
    relying on a separate injected message.
    
    ## What changed
    
    - Changed `custom_tool_call_output.output` from `string` to
    `FunctionCallOutputPayload`
    - Updated freeform tool plumbing to preserve structured output bodies
    - Updated `js_repl` to aggregate nested tool content items and attach
    them to the outer `js_repl` result
    - Removed the old `js_repl` special case that injected `view_image`
    results as a separate pending user image message
    - Updated normalization/history/truncation paths to handle multimodal
    `custom_tool_call_output`
    - Regenerated app-server protocol schema artifacts
    
    ## Behavior
    
    Direct `view_image` calls still return a `function_call_output` with
    image content.
    
    When `view_image` is called inside `js_repl`, the outer `js_repl`
    `custom_tool_call_output` now carries:
    - an `input_text` item if the JS produced text output
    - one or more `input_image` items from nested tool results
    
    So the nested image result now stays inside the `js_repl` tool output
    instead of being injected as a separate message.
    
    ## Compatibility
    
    This is intended to be backward-compatible for resumed conversations.
    
    Older histories that stored `custom_tool_call_output.output` as a plain
    string still deserialize correctly, and older histories that used the
    previous injected-image-message flow also continue to resume.
    
    Added regression coverage for resuming a pre-change rollout containing:
    - string-valued `custom_tool_call_output`
    - legacy injected image message history
    
    
    #### [git stack](https://github.com/magus/git-stack-cli)
    - 👉 `1` https://github.com/openai/codex/pull/12948
  • core: bundle settings diff updates into one dev/user envelope (#12417)
    ## Summary
    - bundle contextual prompt injection into at most one developer message
    plus one contextual user message in both:
      - per-turn settings updates
      - initial context insertion
    - preserve `<model_switch>` across compaction by rebuilding it through
    canonical initial-context injection, instead of relying on
    strip/reattach hacks
    - centralize contextual user fragment detection in one shared definition
    table and reuse it for parsing/compaction logic
    - keep `AGENTS.md` in its natural serialized format:
      - `# AGENTS.md instructions for {dirname}`
      - `<INSTRUCTIONS>...</INSTRUCTIONS>`
    - simplify related tests/helpers and accept the expected snapshot/layout
    updates from bundled multi-part messages
    
    ## Why
    The goal is to converge toward a simpler, more intentional prompt shape
    where contextual updates are consistently represented as one developer
    envelope plus one contextual user envelope, while keeping parsing and
    compaction behavior aligned with that representation.
    
    ## Notable details
    - the temporary `SettingsUpdateEnvelope` wrapper was removed; these
    paths now return `Vec<ResponseItem>` directly
    - local/remote compaction no longer rely on model-switch strip/restore
    helpers
    - contextual user detection is now driven by shared fragment definitions
    instead of ad hoc matcher assembly
    - AGENTS/user instructions are still the same logical context; only the
    synthetic `<user_instructions>` wrapper was replaced by the natural
    AGENTS text format
    
    ## Testing
    - `just fmt`
    - `cargo test -p codex-app-server
    codex_message_processor::tests::extract_conversation_summary_prefers_plain_user_messages
    -- --exact`
    - `cargo test -p codex-core
    compact::tests::collect_user_messages_filters_session_prefix_entries
    --lib -- --exact`
    - `cargo test -p codex-core --test all
    'suite::compact::snapshot_request_shape_pre_turn_compaction_strips_incoming_model_switch'
    -- --exact`
    - `cargo test -p codex-core --test all
    'suite::compact_remote::snapshot_request_shape_remote_pre_turn_compaction_strips_incoming_model_switch'
    -- --exact`
    - `cargo test -p codex-core --test all
    'suite::client::includes_apps_guidance_as_developer_message_when_enabled'
    -- --exact`
    - `cargo test -p codex-core --test all
    'suite::client::includes_developer_instructions_message_in_request' --
    --exact`
    - `cargo test -p codex-core --test all
    'suite::client::includes_user_instructions_message_in_request' --
    --exact`
    - `cargo test -p codex-core --test all
    'suite::client::resume_includes_initial_messages_and_sends_prior_items'
    -- --exact`
    - `cargo test -p codex-core --test all
    'suite::review::review_input_isolated_from_parent_history' -- --exact`
    - `cargo test -p codex-exec --test all
    'suite::resume::exec_resume_last_respects_cwd_filter_and_all_flag' --
    --exact`
    - `cargo test -p core_test_support
    context_snapshot::tests::full_text_mode_preserves_unredacted_text --
    --exact`
    
    ## Notes
    - I also ran several targeted `compact`, `compact_remote`,
    `prompt_caching`, `model_visible_layout`, and `event_mapping` tests
    while iterating on prompt-shape changes.
    - I have not claimed a clean full-workspace `cargo test` from this
    environment because local sandbox/resource conditions have previously
    produced unrelated failures in large workspace runs.
  • Raise image byte estimate for compaction token accounting (#12717)
    Increase `IMAGE_BYTES_ESTIMATE` from 340 bytes to 7,373 bytes so the
    existing 4-bytes/token heuristic yields an image estimate of ~1,844
    tokens instead of ~85. This makes auto-compaction more conservative for
    image-heavy transcripts and avoids underestimating context usage, which
    can otherwise cause compaction to fail when there is not enough free
    context remaining. The new value was chosen because that's the image
    resolution cap used for our latest models.
    
    Follow-up to [#12419](https://github.com/openai/codex/pull/12419).
    Refs [#11845](https://github.com/openai/codex/issues/11845).
  • feat(core) Introduce Feature::RequestPermissions (#11871)
    ## Summary
    Introduces the initial implementation of Feature::RequestPermissions.
    RequestPermissions allows the model to request that a command be run
    inside the sandbox, with additional permissions, like writing to a
    specific folder. Eventually this will include other rules as well, and
    the ability to persist these permissions, but this PR is already quite
    large - let's get the core flow working and go from there!
    
    <img width="1279" height="541" alt="Screenshot 2026-02-15 at 2 26 22 PM"
    src="https://github.com/user-attachments/assets/0ee3ec0f-02ec-4509-91a2-809ac80be368"
    />
    
    ## Testing
    - [x] Added tests
    - [x] Tested locally
    - [x] Feature
  • Improve token usage estimate for images (#12419)
    Fixes #11845.
    
    Adjust context/token estimation for inline image `data:*;base64,...`
    URLs so we
    do not count the raw base64 payload as model-visible text.
    
    What changed:
    - keep the existing JSON-length estimator as the baseline
    - detect only inline base64 `data:` image URLs in message and
    function-call
      output content items
    - subtract only the base64 payload bytes (preserving data URL prefix +
    JSON
      overhead)
    - add a fixed per-image estimate of 340 bytes (~85 tokens at the repo’s
      4-bytes/token heuristic)
    
    This avoids large overestimates from MCP image tool outputs while
    leaving normal
    image URLs (`https://`, `file://`, non-base64 `data:` URLs) unchanged.
    
    Tests:
    - message image data URL estimate regression
    - function-call output image data URL estimate regression
    - non-base64 image URLs unchanged
    - non-base64 `data:` URLs unchanged
    - `data:application/octet-stream;base64,...` adjusted
    - multiple inline images apply multiple fixed costs
    - text-only items unchanged
  • Fix compaction context reinjection and model baselines (#12252)
    ## Summary
    - move regular-turn context diff/full-context persistence into
    `run_turn` so pre-turn compaction runs before incoming context updates
    are recorded
    - after successful pre-turn compaction, rely on a cleared
    `reference_context_item` to trigger full context reinjection on the
    follow-up regular turn (manual `/compact` keeps replacement history
    summary-only and also clears the baseline)
    - preserve `<model_switch>` when full context is reinjected, and inject
    it *before* the rest of the full-context items
    - scope `reference_context_item` and `previous_model` to regular user
    turns only so standalone tasks (`/compact`, shell, review, undo) cannot
    suppress future reinjection or `<model_switch>` behavior
    - make context-diff persistence + `reference_context_item` updates
    explicit in the regular-turn path, with clearer docs/comments around the
    invariant
    - stop persisting local `/compact` `RolloutItem::TurnContext` snapshots
    (only regular turns persist `TurnContextItem` now)
    - simplify resume/fork previous-model/reference-baseline hydration by
    looking up the last surviving turn context from rollout lifecycle
    events, including rollback and compaction-crossing handling
    - remove the legacy fallback that guessed from bare `TurnContext`
    rollouts without lifecycle events
    - update compaction/remote-compaction/model-visible snapshots and
    compact test assertions (including remote compaction mock response
    shape)
    
    ## Why
    We were persisting incoming context items before spawning the regular
    turn task, which let pre-turn compaction requests accidentally include
    incoming context diffs without the new user message. Fixing that exposed
    follow-on baseline issues around `/compact`, resume/fork, and standalone
    tasks that could cause duplicate context injection or suppress
    `<model_switch>` instructions.
    
    This PR re-centers the invariants around regular turns:
    - regular turns persist model-visible context diffs/full reinjection and
    update the `reference_context_item`
    - standalone tasks do not advance those regular-turn baselines
    - compaction clears the baseline when replacement history may have
    stripped the referenced context diffs
    
    ## Follow-ups (TODOs left in code)
    - `TODO(ccunningham)`: fix rollback/backtracking baseline handling more
    comprehensively
    - `TODO(ccunningham)`: include pending incoming context items in
    pre-turn compaction threshold estimation
    - `TODO(ccunningham)`: inject updated personality spec alongside
    `<model_switch>` so some model-switch paths can avoid forced full
    reinjection
    - `TODO(ccunningham)`: review task turn lifecycle
    (`TurnStarted`/`TurnComplete`) behavior and emit task-start context
    diffs for task types that should have them (excluding `/compact`)
    
    ## Validation
    - `just fmt`
    - CI should cover the updated compaction/resume/model-visible snapshot
    expectations and rollout-hydration behavior
    - I did **not** rerun the full local test suite after the latest
    resume-lookup / rollout-persistence simplifications
  • Move previous turn context tracking into ContextManager history (#12179)
    ## Summary
    - add `previous_context_item: Option<TurnContextItem>` to
    `ContextManager`
    - expose session/state accessors for reading and updating the stored
    previous context item
    - switch settings diffing to use `TurnContextItem` instead of
    `TurnContext`
    - remove submission-loop local `previous_context` and persist the
    previous context item in history
    
    ## Testing
    - `just fmt`
    - `just fix -p codex-core`
    - `cargo test -p codex-core --test all model_switching::`
    - `cargo test -p codex-core --test all collaboration_instructions::`
    - `cargo test -p codex-core --test all personality::`
    - `cargo test -p codex-core --test all
    permissions_messages::permissions_message_not_added_when_no_change`
  • feat: sub-agent injection (#12152)
    This PR adds parent-thread sub-agent completion notifications and change
    the prompt of the model to prevent if from being confused
  • Centralize context update diffing logic (#11807)
    ## Summary
    This PR centralizes model-visible state diffing for turn context updates
    into one module, while keeping existing behavior and call sites stable.
    
    ### What changed
    - Added `core/src/context_updates.rs` with the consolidated diffing
    logic for:
      - environment context updates
      - permissions/policy updates
      - collaboration mode updates
      - model-instruction switch updates
      - personality updates
    - Added `BuildSettingsUpdateItemsParams` so required dependencies are
    passed explicitly.
    - Updated `Session::build_settings_update_items` in `core/src/codex.rs`
    to delegate to the centralized module.
    - Reused the same centralized `personality_message_for` helper from
    initial-context assembly to avoid duplicated logic.
    - Registered the new module in `core/src/lib.rs`.
    
    ## Why
    This is a minimal, shippable step toward the model-visible-state design:
    all state diff decisions for turn-context update items now live in one
    place, improving reviewability and reducing drift risk without expanding
    scope.
    
    ## Behavior
    - Intended to be behavior-preserving.
    - No protocol/schema changes.
    - No call-site behavior changes beyond routing through the new
    centralized logic.
    
    ## Testing
    Ran targeted tests in this worktree:
    - `cargo test -p codex-core
    build_settings_update_items_emits_environment_item_for_network_changes`
    - `cargo test -p codex-core collaboration_instructions --test all`
    
    Both passed.
    
    ## Codex author
    `codex resume 019c540f-3951-7352-a3fa-6f07b834d4ce`
  • Strip unsupported images from prompt history to guard against model switch (#11349)
    - Make `ContextManager::for_prompt` modality-aware and strip input_image
    content when the active model is text-only.
    - Added a test for multi-model -> text-only model switch
  • core: add focused diagnostics for remote compaction overflows (#11133)
    ## Summary
    - add targeted remote-compaction failure diagnostics in compact_remote
    logging
    - log the specific values needed to explain overflow timing:
      - last_api_response_total_tokens
      - estimated_tokens_of_items_added_since_last_successful_api_response
      - estimated_bytes_of_items_added_since_last_successful_api_response
      - failing_compaction_request_body_bytes
    - simplify breakdown naming and remove
    last_api_response_total_bytes_estimate (it was an approximation and not
    useful for debugging)
    
    ## Why
    When compaction fails with context_length_exceeded, we need concrete,
    low-ambiguity numbers that map directly to:
    1) what the API most recently reported, and
    2) what local history added since then.
    
    This keeps the failure logs actionable without adding broad, noisy
    metrics.
    
    ## Testing
    - just fmt
    - cargo test -p codex-core
  • core: account for all post-response items in auto-compact token checks (#11132)
    ## Summary
    - change compaction pre-check accounting to include all items added
    after the last model-generated item, not only trailing codex-generated
    outputs
    - use that boundary consistently in get_total_token_usage() and
    get_total_token_usage_breakdown()
    - update history tests to cover user/tool-output items after the last
    model item
    
    ## Why
    last_token_usage.total_tokens is API-reported for the last successful
    model response. After that point, local history may gain additional
    items (user messages, injected context, tool outputs). Compaction
    triggering must account for all of those items to avoid late compaction
    attempts that can overflow context.
    
    ## Testing
    - just fmt
    - cargo test -p codex-core
  • Fix remote compaction estimator/payload instruction small mismatch (#10692)
    ## Summary
    This PR fixes a deterministic mismatch in remote compaction where
    pre-trim estimation and the `/v1/responses/compact` payload could use
    different base instructions.
    
    Before this change:
    - pre-trim estimation used model-derived instructions
    (`model_info.get_model_instructions(...)`)
    - compact payload used session base instructions
    (`sess.get_base_instructions()`)
    
    After this change:
    - remote pre-trim estimation and compact payload both use the same
    `BaseInstructions` instance from session state.
    
    ## Changes
    - Added a shared estimator entry point in `ContextManager`:
    - `estimate_token_count_with_base_instructions(&self, base_instructions:
    &BaseInstructions) -> Option<i64>`
    - Kept `estimate_token_count(&TurnContext)` as a thin wrapper that
    resolves model/personality instructions and delegates to the new helper.
    - Updated remote compaction flow to fetch base instructions once and
    reuse it for both:
      - trim preflight estimation
      - compact request payload construction
    - Added regression coverage for parity and behavior:
      - unit test verifying explicit-base estimator behavior
    - integration test proving remote compaction uses session override
    instructions and trims accordingly
    
    ## Why this matters
    This removes a deterministic divergence source where pre-trim could
    think the request fits while the actual compact request exceeded context
    because its instructions were longer/different.
    
    ## Scope
    In scope:
    - estimator/payload base-instructions parity in remote compaction
    
    Out of scope:
    - retry-on-`context_length_exceeded`
    - compaction threshold/headroom policy changes
    - broader trimming policy changes
    
    ## Codex author:
    `codex fork 019c2b24-c2df-7b31-a482-fb8cf7a28559`