## 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
- Resolve user message admission when Core accepts the input as a new turn or a steer, without waiting for rollout persistence.
- Remove persistence- and hook-specific admission errors and task bookkeeping.
- Delete queued messages after Core accepts them for turn processing, including messages subsequently stopped by a prompt hook.
## Testing
- Update concurrent admission and queue service tests for acceptance-based admission.
GitOrigin-RevId: 7dcc311cfecd3d710733b420f2add546edb86153
## 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
`TurnContext` stored model and reasoning settings both as resolved turn fields and inside `CollaborationMode`, requiring the copies to stay synchronized when the model changed.
## What changed
- Store the mode and collaboration-mode developer instructions directly on `TurnContext`.
- Reconstruct `CollaborationMode` from those values plus the turn's current model and reasoning effort when callers need it.
- Read the mode directly for plan-mode checks, lifecycle events, and telemetry.
GitOrigin-RevId: b8fccafdd5d5cbccc84bc70d542588087ddf0093
## Why
Goal idle continuation is extension-triggered model-visible work, so it
should follow one core-owned rule for when automatic work may start. In
particular, it should not jump ahead of queued user/client work, start
while another task is active, or inject a continuation turn while the
thread is in Plan mode.
Keeping this policy in `try_start_turn_if_idle` avoids passing
`collaboration_mode` or review-specific state through
`ThreadLifecycleContributor::on_thread_idle`. Active `/review` is
covered by the same active-task gate because Review turns are not
steerable.
## What Changed
- Teach `Session::try_start_turn_if_idle` to reject automatic idle turns
in Plan mode, both before reserving an idle turn and after building the
turn context.
- Document `CodexThread::try_start_turn_if_idle` as the extension-facing
gate for automatic idle work, including Plan-mode and active Review-task
behavior.
- Add focused coverage for Plan-mode rejection and active Review-task
rejection without queuing synthetic input.
## Testing
- `just test -p codex-core try_start_turn_if_idle`
## Why
`try_start_turn_if_idle` is the core helper for starting injected input
only when the session is actually idle. It should stay focused on
generic turn-lifecycle safety. The previous `ModeKind::Plan` guard mixed
caller policy into that helper: Plan mode may choose not to auto-start
some extension work, but that decision belongs at the extension or
caller boundary rather than in the session injection primitive.
## What changed
- Removed the `ModeKind::Plan` early return from
`Session::try_start_turn_if_idle`.
- Removed the now-unused `ModeKind` import from
`core/src/session/inject.rs`.
## Testing
Not run locally.
## Why
The goal extension needs a way to resume an active goal after the thread
becomes idle, but the old core goal runtime should not be refactored as
part of this step. The missing piece is a small core-owned turn-start
primitive: let an extension ask for a normal model turn only when the
thread is idle, and otherwise fail without injecting into whatever is
currently active.
## What Changed
- Adds `CodexThread::try_start_turn_if_idle(...)` as the narrow
extension-facing primitive for synthetic idle work.
- Implements the session side so it refuses to start when:
- the provided input is empty,
- the session is in plan mode,
- a turn is already active, or
- trigger-turn mailbox work is pending.
- Gives trigger-turn mailbox work priority if it appears while the idle
turn is being prepared.
- Wires `GoalExtension::on_thread_idle` to read the active persisted
goal and submit the continuation prompt through this idle-only
primitive.
- Keeps the legacy core goal continuation implementation in place
instead of folding it into this PR.
## Behavior
This is intentionally best-effort. If `try_start_turn_if_idle` observes
that the thread is not idle, or that higher-priority mailbox work should
run first, it returns the input to the caller. The goal extension drops
that continuation prompt and waits for a future idle opportunity instead
of injecting stale synthetic goal text into an active turn.
## Validation
- `just test -p codex-core
try_start_turn_if_idle_rejects_active_turn_without_injecting`
- `just test -p codex-goal-extension`