## Why
Dogfooder feedback exposed two correctness gaps in normal-loop overflow
recovery:
1. a sampling request that hit `ContextWindowExceeded` could keep
re-entering auto-compaction indefinitely if the compacted retry still
did not fit, and
2. local compact-history rebuilds flattened user messages down to text,
so an overflowing `[image, "what is this?"]` turn could be retried
without the image after compaction.
That means recovery could either fail to terminate cleanly or proceed
with a materially weakened version of the user request.
## What changed
- Move normal-loop `ContextWindowExceeded` handling into the sampling
retry loop, so successful rescue compaction consumes the provider retry
budget instead of creating an unbounded outer-turn loop.
- Keep compacted user-history rebuilds structured:
`collect_user_messages` now carries user `UserInput` content rather than
flattened strings, and `build_compacted_history` reconstructs full user
messages from that structured representation.
- Preserve image inputs while retaining the existing text-budget
truncation behavior for compacted user history.
- Preserve existing compaction-task failure handling and client-session
reset behavior while bounding repeated overflow retries.
- Add focused regression coverage for:
- recovery after a normal-loop overflow,
- retry-budget exhaustion after repeated overflow,
- local recovery preserving image + text input,
- remote recovery preserving image + text input,
- remote compaction v2 preserving image + text input, and
- compaction failure still terminating cleanly.
The main behavior changes are in `codex-rs/core/src/session/turn.rs` and
`codex-rs/core/src/compact.rs`.
## Verification
- Not run locally; relying on PR CI for this update.
---------
Co-authored-by: Codex <noreply@openai.com>