## Why
Unread queue-only messages prevented idle agents from unloading, and sending a
message to an evicted agent reloaded its session. Pending mail should not require
an idle recipient to occupy a loaded thread slot.
## What changed
- Retain queue-only mail in an in-memory runtime mailbox when a session unloads.
Queue new messages for locally evicted agents without reloading them or
starting a turn.
- Add `AgentControl::take_mailbox` and `AgentControl::watch_mailbox` so sessions
can consume retained mail and react to host mailbox notifications.
- Preserve message order across eviction, reload, and subsequent follow-ups.
Keep agents with outstanding durable sleep or turn-triggering mail loaded,
and discard retained mail when an agent is removed or its tree shuts down.
## Testing
Add integration coverage for retaining mail across eviction, sending while
unloaded, and ordering messages around a follow-up after reload. Add a host
controller test verifying that mailbox notifications wake `wait_agent` and
that the message is delivered exactly once.
GitOrigin-RevId: 4b55647f9ee9d1aaf3753f0688863bda81617327
## What changed
- Start a turn-owned notification receiver using the same remote board client and session identity as the board tools. Deliver post previews as agent messages and cancel the receiver when the turn stops, aborts, or errors.
- Wait for the initial connection attempt before inference, then retry failed or disconnected streams without waking idle agents.
- Skip malformed notices, mismatched recipients or turns, and oversized previews. Reject delivery to stale or canceled turns and bound remote notices to a 1 KiB context budget.
## Testing
Extend remote board tests to cover interrupted setup, connection failures, reconnection, invalid and stale notices, preview delivery into model context, and receiver shutdown after turn completion.
GitOrigin-RevId: c7c8d977fe74b7a5cb3bd8216dc0146f3f297e44
## Why
Message-board notifications delivered as ordinary injected input could cause an agent to sample again after its final answer.
## What changed
Route notifications through the current turn's mailbox as `InterAgentCommunication`, checking atomically that the turn is running and still accepts mailbox delivery. Skip notifications when delivery is closed. Accepted notifications may remain in conversation history for a later turn without reopening the finalized answer or starting a new turn.
Report pending inter-agent communications as mailbox activity, while preserving priority for steering input.
## Testing
Add regression scenarios for notifications arriving before and after finalization, asserting no extra sampling and the expected notification presence in the next turn. Extend input-queue coverage to recognize already-pending mailbox activity.
GitOrigin-RevId: 41d786041b4eccc5ccf926f2f190473676a3ac2f
## Why
User input queued during a long-running code-mode `exec` or `wait` can remain pending until the call returns. Allow these calls to yield early so Codex can receive the new input while the cell continues running.
## What changed
- Add the `instant_interrupt` feature flag, disabled by default.
- Watch queued user input for each sampling request and pass a shared preemption signal to code-mode `exec` and `wait` calls, including calls received later in the same response.
- Yield running cell IDs without stopping the cells, allowing subsequent `wait` calls to collect their results.
## Testing
Add integration coverage for enabled and disabled behavior, repeated steering during `exec` and `wait`, input deferred during compaction, and later calls in the same response. Verify that direct tool results and queued user messages are preserved, and add a scenario snapshot showing continuation after a cell yields.
GitOrigin-RevId: e33466a980d98e113bfb6c6a45c59de0cc7b3c1d
## Why
Unchanged scheduled heartbeat instructions should not become fresh authorization evidence that supersedes a later human override or floods Guardian review context.
## What changed
- Capture heartbeat provenance per input, including when steering an active turn, and persist it in history and retained context.
- Coalesce unchanged instructions for each automation at their original acceptance order. Preserve changed instruction versions and ordinary human messages.
- Represent repeated heartbeats as references to earlier instructions in Guardian transcripts, while tracking the latest input turn separately for trusted skill selection.
- Limit coalescing to recognized, bounded heartbeat envelopes with host-provided provenance.
## Testing
Add regression coverage for human overrides across repeated and steered heartbeats, instruction changes, checkpoint restoration, transcript references, envelope parsing, and latest-turn trusted skills in delegated reviews.
GitOrigin-RevId: 31e444ddb685a1a618be26d3cc32fb562296946b
## Why
Callers need to interrupt a specific active turn without discarding queued input. Tool callbacks must be able to await the interruption decision without blocking task cancellation.
## What changed
- Add `CodexThread::interrupt_if_no_pending_input` and `Op::InterruptIfNoPendingInput`.
- Check the turn identity and pending user input or mailbox items before taking the active task. Leave the turn running for stale or abandoned requests.
- Acknowledge the decision before joining cancellation, and unblock pending submissions when the active task is cancelled.
## Testing
Add regression coverage for successful interruption, stale turns, preservation of queued user input and agent mail, abandoned requests, and cancellation while the submission queue is full.
GitOrigin-RevId: 42e31c09369fb070ba1b4b3c64ef45334dd872d8
## Why
A delegated task can omit restrictions from the sender's user messages. Guardian needs that context when reviewing actions in the receiving thread.
## What changed
- When thread-owned Guardian context is enabled, capture up to three recent local user messages when a `send_message_to_thread` delivery from `codex_app` or `codex_tui` is accepted.
- Provide a bounded snapshot to synchronous and asynchronous reviewers, with explicit notices for unavailable evidence and a reminder that historical context does not transfer permission. Keep the snapshot out of the worker prompt.
- Retain snapshots with their deliveries across replay, compaction, and rollback, and strip them from forked agent history.
## Testing
Add coverage for reviewer-only visibility, missing sender provenance, context-budget preservation, checkpoint replay and rollback, fork isolation, and input serialization that rejects harness metadata.
GitOrigin-RevId: 3870ba762aa81689713b56d4cb3b98566bffed88
## Why
Background and empty-input turns could lack a `root_turn_id`, and detached memory requests omitted turn identity entirely. Tasks without a root could also adopt one from coalesced mailbox input.
## What changed
- Set `root_turn_id` to the task's turn ID at task start unless an inherited or recovered root is already present.
- Stop assigning roots from mailbox input or steering an active turn.
- Give detached memory requests a fresh UUID for both `turn_id` and `root_turn_id`, included in turn headers and client metadata while still omitting session and thread identity.
## Testing
Extend assertions for background turns, empty input, goal continuations, manual compaction, and detached memory requests. Exercise mailbox coalescing with both independent and inherited roots to verify the task keeps its established root.
GitOrigin-RevId: ee0f2f1b43542a9f5884f694c1d94341b476779c
## Why
Queued prompts and `request_user_input` answers can be persisted in a different
order from when the host accepted them. Using persistence order can therefore
reorder retained instructions during replay or apply the wrong rollback
boundary.
## What changed
- Reserve an acceptance sequence for user prompts and elicitation responses
when retained thread context is enabled.
- Persist that sequence with user-message metadata and verified answers, and
use it when ordering, bounding, replaying, and rolling back retained context.
- Keep events without acceptance metadata compatible with legacy recording
order.
## Testing
- Cover delayed queued-input recording, rejected prompts, checkpoint replay,
compaction, rollback migration, and legacy retained-context behavior.
GitOrigin-RevId: 42d24465cc2033bc51c0c2da79d3c0884a8ad98a
## Why
Reasoning configuration changes need to retain their position and trusted provenance when model history is persisted and replayed. Client-injected history must not be able to forge these controls.
## What changed
- Add a typed `configuration_update` response item carrying reasoning effort, including custom model-defined values.
- Persist harness-authored updates with provenance and preserve them across history reconstruction, thread resume, raw response notifications, and agent forks.
- Exclude untrusted configuration updates from model history, strip client-supplied provenance metadata, and reject configuration updates supplied as turn input.
- Export the new item through the JSON and TypeScript app-server schemas and classify it in telemetry and persistence metrics.
## Testing
- Cover serialization, provenance persistence, history filtering and rollback, resume reconstruction, and injection attempts before and after restart.
GitOrigin-RevId: eb5559d2b52b7a931621e7c9812f009ff9fb8939
## Why
Resumed threads need to continue per-turn and per-thread token totals without scanning arbitrarily far beyond the latest compaction checkpoint.
## What changed
- Add durable `TokenUsageRecord` rollout items with response, turn, thread, session, and root-turn attribution.
- Restore accumulated usage on resume and snapshot the latest record plus the compaction response ID in compaction checkpoints.
- Preserve root-turn lineage in persisted turn context, while ensuring forked child threads start with their own usage totals.
## Testing
- Cover usage accumulation across multiple responses and resumed turns.
- Cover local and remote compaction checkpoints, invalid remote compaction output, rollout reconstruction, and fork isolation.
GitOrigin-RevId: ef9e0c4a9102a08a2c382be4cdac68c84353c90a
## What changed
- Add a positive `output_token_limit` setting to each entry under an MCP server's `tools` configuration.
- Apply the most restrictive limit when plugin and user policies overlap, while keeping approval policy independent.
- Carry the effective MCP output budget in conversation history so tool output, post-tool hook responses, and resumed sessions use the same truncation limit.
## Testing
- Cover configuration parsing, serialization, schema validation, and plugin policy merging.
- Cover MCP output below and above the configured limit, post-tool hook responses, and session resume.
GitOrigin-RevId: d0beb4fca9ba6055d9e1d31c137373b465d50d61
## What changed
- Allow `start_or_steer_turn` to accept named `function_call_output` items without a `call_id`, starting a turn when idle or steering the active turn.
- Preserve standalone outputs as pending input, assign response item IDs, and record them in conversation history without treating them as user prompts.
## Testing
- Cover starting a turn with a standalone output and steering an active turn with one.
- Verify passive response items do not signal pending steering activity.
GitOrigin-RevId: e32b292106b3c5a5f03577b8ebf67d2d7dee4e5d
## What changed
- Add the experimental `turn/start.cyberAccessProgram` option with `standard`,
`daybreakBlue`, and `daybreakRed` values.
- Forward the selection as `access_programs.cyber` on Responses, WebSocket,
and remote-compaction requests made with ChatGPT authentication, while
omitting it for API-key and custom-provider requests.
- Preserve the per-turn selection across recovery, compaction, and child-agent
turns without making it a persistent thread setting.
## Testing
- Cover app-server forwarding, authentication boundaries, WebSocket reuse,
turn recovery, compaction, and child-agent inheritance.
GitOrigin-RevId: d2eb468f365b2214c5099bc21741cf30f8bd2eb5
## What changed
- Add `TurnInputRequest` and typed submission results for atomically starting a turn, steering the active turn, or declining input with a specific reason.
- Expose `start_or_steer_turn`, `start_turn_if_idle`, and `steer_turn` on `CodexThread`, and migrate Core consumers to these APIs.
- Make app-server `turn/start` steer an active regular turn and return that turn's ID. Reject incompatible output schemas and non-steerable turns without applying settings or enqueueing input.
## Testing
- Cover concurrent start-or-steer submissions, accepted and rejected settings updates, output-schema compatibility, idle-start rejection, and app-server steering.
GitOrigin-RevId: dd9b5528d76ec650c019e97af420bc13190ea86a
## What changed
- Mark developer messages received from clients with `client_authored` harness
metadata when `retain_client_developer_messages` is enabled.
- Carry that provenance through pending input and persist it alongside rollout
response items without adding it to provider requests or the serialized
`TurnInput` payload.
- Preserve existing behavior for unannotated response items and when the
feature is disabled.
## Testing
- Cover client item injection before and during turns, additional application
context, rollout replay, image resize notices, and serialization boundaries.
GitOrigin-RevId: f385c177732becde732272e4b8440eb91f5c1039
## Why
`parent_turn_id` identifies the immediate caller of a nested request, but not the
top-level turn that initiated a multi-level chain of work.
## What changed
- Add `root_turn_id` to turn state, submissions, and Responses API metadata.
- Propagate the root through spawned agents, delegated work, review sessions, and
turn-triggering inter-agent messages.
- Omit the root when background work has no user turn or combined input makes the
root ambiguous.
## Testing
Added coverage for nested agent resumes, reviews, mailbox delivery, steered input,
and reserved metadata handling.
GitOrigin-RevId: 60057db2ee81f4efefa946a9f63c74ff98b93929
## What changed
- Add lifecycle-backed gauges for in-flight and queued app requests, pending server requests and mailbox messages, active turns, and live MCP connections.
- Register each gauge on first use and decrement it when the tracked work or resource is dropped.
- Document the new `server/diagnostics` gauge names and account for the diagnostics request itself in the response example and test.
GitOrigin-RevId: 4bb82a3918af0b502d149b5d761ad669b2083f39
## What changed
- Add a storage-neutral queue extension for listing, adding, editing, reordering, deleting, and explicitly starting queued user messages.
- Dispatch queued messages in FIFO order when a thread becomes idle after a completed turn, while leaving them queued after interruptions or failures.
- Wait for user input to be persisted before removing its queue item, and report hook rejection, task termination, and persistence failures distinctly.
- Snapshot local image and audio attachments into validated data URLs before storing them, enforce input limits, and emit `ThreadQueueChanged` events after mutations.
## Testing
- Cover queue mutations, automatic and explicit dispatch, interruption and failure behavior, hook rejection, invalid records, input limits, and local attachment snapshotting.
GitOrigin-RevId: 14f39d7c8cbaccb767a6da84b82cced0edfcb4b9
## What changed
- Generalize `CodexThread::try_start_turn_if_idle` to accept exported
`TurnInput` values, preserving user content and client IDs in the first model
request.
- Allow non-empty user input to start an idle turn in Plan mode while retaining
the Plan-mode rejection for automatic response items and empty user input.
- Process idle user input through the normal user-turn path so connector
selections are cleared, prompt telemetry is recorded, and referenced skill
instructions are included.
## Testing
- Cover idle user input in Default and Plan modes, response-item mailbox
merging, empty-input rejection, and skill instruction injection.
GitOrigin-RevId: 116667d160d4464b525d9e4c7e8cc05d23a94008
## What changed
- Propagate the initiating turn ID through agent spawns, follow-up tasks, reviews, and delegated Codex sessions.
- Add `parent_turn_id` to Responses client and turn metadata while keeping it out of external MCP metadata.
- Preserve parent-turn provenance across queued agent messages when their triggering parent is unambiguous.
## Testing
- Cover spawned, resumed, nested, reviewed, delegated, and WebSocket request metadata.
- Verify queued messages do not claim ambiguous or queue-only parent turns.
GitOrigin-RevId: 481fdebbe7df2031880fe259509273cce50b20a8
## Why
Queue-only child-agent updates that arrive around a final answer should not
restart sampling, but they must remain available to the next turn. Explicitly
injected response items still need to reopen the current turn.
## What changed
- Defer queue-only inter-agent mail after a final answer without treating it as
same-turn pending input.
- Re-enable current-turn mailbox delivery when the model requests a follow-up,
a stop hook continues the turn, or a response item is explicitly injected.
- Check for turn-triggering mailbox work after clearing the completed active
turn so pending work can start safely.
## Testing
Added coverage that queued child mail waits for the next user turn and that an
injected response item after a final answer triggers a follow-up request.
GitOrigin-RevId: aa81d00707943284b7ab069b60970e547908c895
## Why
`wait_agent` can block for a long timeout while waiting for sub-agent
mailbox activity. Although same-turn user steer is accepted during that
tool call, the input remains pending until the wait returns, so an
explicit request to change direction can appear unresponsive.
## What changed
- Notify active `wait_agent` calls when user input is steered into the
current turn.
- Check for already-pending steer input when subscribing so input that
races with tool startup is not missed.
- Distinguish mailbox activity, steered input, and timeout outcomes,
returning `Wait interrupted by new input.` for the steer path.
- Update the `wait_agent` tool description to document the early-return
behavior.
## Testing
- `just test -p codex-core input_queue_`
- `just test -p codex-core wait_agent`
The coverage includes steer notification before and after subscription,
plus an end-to-end test that verifies the interrupted wait result and
steered user input are both included exactly once in the follow-up model
request.
## Why
Multi-agent v2 `send_message` deliveries already reach the receiving
model as typed `agent_message` items with encrypted content.
Child-completion notifications are generated by Codex itself, so their
content is plaintext and previously fell back to a serialized JSON
envelope inside an assistant message.
With plaintext `input_text` supported for `agent_message`, both delivery
paths can use the same model-visible type while preserving explicit
author and recipient metadata.
## What changed
- add plaintext `input_text` support to `AgentMessageInputContent` and
regenerate the affected app-server schemas
- preserve `InterAgentCommunication` as structured mailbox input instead
of converting it to assistant text
- record delivered communications as typed `agent_message` history items
- persist a dedicated rollout item so local delivery metadata such as
`trigger_turn` remains available without leaking into the Responses
request
- reconstruct typed agent messages on resume and preserve fork-turn
truncation behavior
- remove request-time assistant-content parsing
- preserve plaintext and encrypted inter-agent deliveries in stage-one
memory inputs
- normalize and link plaintext and encrypted agent messages in rollout
traces without treating inbound messages as child results
- cover the real MultiAgent V2 child-completion path end to end with
deterministic mailbox synchronization
## Verification
- `just test -p codex-core
plaintext_multi_agent_v2_completion_sends_agent_message`
- `just test -p codex-core input_queue_drains_mailbox_in_delivery_order
record_initial_history_reconstructs_typed_inter_agent_message
fork_turn_positions_use_inter_agent_delivery_metadata`
- `just test -p codex-memories-write
serializes_inter_agent_communications_for_memory`
- `just test -p codex-rollout-trace
agent_messages_preserve_routing_and_content
sub_agent_started_activity_creates_spawn_edge`
- `just test -p codex-rollout-trace
agent_result_edge_falls_back_to_child_thread_without_result_message`
- `just test -p codex-protocol -p codex-rollout -p
codex-app-server-protocol`
## Summary
Adds an optional `clientId` field to app-server v2 `UserInput` and
carries it through the core `UserInput` model so clients can correlate
echoed user input items without relying on payload equality.
## Details
- Adds `client_id: Option<String>` to core `UserInput` variants.
- Exposes the v2 app-server field as `clientId` on the wire and in
generated TypeScript.
- Preserves the id when converting between app-server v2 and core
protocol types.
- Regenerates app-server schema fixtures.
## Validation
- `just fmt`
- `just write-app-server-schema`
- `cargo test -p codex-app-server-protocol`
- `cargo test -p codex-protocol`
- `just fix -p codex-app-server-protocol`
- `just fix -p codex-protocol`
- `git diff --check`
## Summary
Adds experimental `additionalContext` support to `turn/start` and
`turn/steer` so clients can provide ephemeral external context, such as
browser or automation state, without turning that plumbing into a
visible user prompt or triggering user-prompt lifecycle behavior.
## API Shape
The parameter shape is:
```ts
additionalContext?: Record<string, {
value: string
kind: "untrusted" | "application"
}> | null
```
Example:
```json
{
"additionalContext": {
"browser_info": {
"value": "Active tab is CI failures.",
"kind": "untrusted"
},
"automation_info": {
"value": "CI rerun is in progress.",
"kind": "application"
}
}
}
```
The keys are opaque and caller-defined.
## Context Injection
When provided, accepted entries are inserted into model context as
hidden contextual message items, not as visible thread user-message
items.
`kind: "untrusted"` entries are inserted with role `user`:
```text
<external_${key}>${value}</external_${key}>
```
`kind: "application"` entries are inserted with role `developer`:
```text
<${key}>${value}</${key}>
```
Values are not escaped. Each value is truncated to 1k approximate tokens
before wrapping.
For `turn/start`, accepted additional context is inserted before normal
user input. For `turn/steer`, additional context is merged only when the
steer includes non-empty user input; context-only steers still reject as
empty input.
## Dedupe Strategy
`AdditionalContextStore` lives on session state and stores the latest
complete additional-context map.
Each `turn/start` or non-empty `turn/steer` treats its
`additionalContext` as the current complete set of values. Entries are
injected only when the key is new or the exact entry for that key
changed, including `value` or `kind`. After merging, the store is
replaced with the provided map, so omitted keys are removed from the
retained set and can be injected again later if reintroduced.
Omitting `additionalContext`, passing `null`, or passing an empty object
resets the store to empty and injects nothing.
## What Changed
- Threads experimental v2 `additionalContext` through app-server into
core turn start and steer handling.
- Adds separate contextual fragment types for untrusted user-role
context and application developer-role context.
- Uses pending response input items so additional context can be
combined with normal user input without treating it as prompt text.
- Adds integration coverage for start/steer flow, role routing,
dedupe/reset behavior, deletion/re-add behavior, hook-blocked input
behavior, empty context-only steer rejection, external-fragment marker
matching, and truncation.
## Why
`ActiveTurn` already runs at most one task: starting a task requires
that no task is present, and replacement aborts existing work first.
Representing that state as an `IndexMap` leaves a multi-task shape for a
single-task invariant and makes each lifecycle lookup operate like a
collection lookup.
The slot remains optional because goal continuation uses an empty active
turn as a reservation while deciding whether to start continuation work.
## What changed
- Replace `ActiveTurn.tasks` with `task: Option<RunningTask>`.
- Update task abort/completion, session lookup and steering, input-queue
matching, goal reservation, and network-approval lookup to operate on
the singular slot.
- Mutate the singular task slot directly instead of retaining
collection-era add/remove/take helpers.
- Record token usage on the completing active task span without a
regular-task-only opt-in flag.
## Validation
- `cargo test -p codex-core --lib session::tests::steer_input`
- `cargo test -p codex-core --lib
session::tests::abort_empty_active_turn_preserves_pending_input`
- `cargo test -p codex-core --lib
session::tests::queued_response_items_for_next_turn_move_into_next_active_turn`
- `cargo test -p codex-core --lib
session::tests::active_goal_continuation_runs_again_after_no_tool_turn`
- `cargo test -p codex-core --lib
session::tests::abort_regular_task_emits_turn_aborted_only`
- `cargo test -p codex-core --lib session::input_queue::tests`
## 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)
## 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.