Files
codex/codex-rs/core/src/compact_pending_input.rs
T
jif 48524f337a Preserve pending agent messages across compaction (#59404)
## Why

Agent messages that arrive after a model request starts must survive compaction until a normal inference request includes them. Compactor output alone does not establish that those messages were delivered.

## What changed

- Persist history sequences and sampling checkpoints in harness metadata, capturing the input prefix for each normal inference request.
- Retain pending agent messages within existing size budgets during local and remote compaction, placing the summary before pending messages and subsequent caller input.
- Preserve sampling progress across history replacement and resume, and drain queued mail after interrupted sampling even when compaction leaves context above the token limit.

## Testing

Add regression coverage for request-boundary sampling checkpoints, pending messages across local compaction and repeated remote compaction with resume, and injected messages that interrupt sampling before mid-turn compaction.

GitOrigin-RevId: 26c04ab96b5a1c3cc1d6625ccc74a0f00201d14c
2026-11-04 13:03:01 +00:00

50 lines
1.8 KiB
Rust

//! Retains bounded unsampled mail and positions summaries before it and subsequent caller input.
//! Delivery is proven only by a regular inference checkpoint, never by compactor output.
use codex_history::ResponseItemEnvelope;
use codex_protocol::models::ResponseItem;
use crate::context_manager::estimate_item_token_count;
use crate::context_manager::sampled_through;
pub(crate) const MAX_RETAINED_AGENT_MESSAGE_TOKENS: i64 = 10_000;
pub(crate) fn pending_mail_sequence(history: &[ResponseItemEnvelope]) -> Option<u64> {
let sampled_through = sampled_through(history);
history
.iter()
.filter(|envelope| matches!(envelope.item, ResponseItem::AgentMessage { .. }))
.filter_map(|envelope| envelope.metadata.as_ref()?.history_sequence)
.find(|sequence| *sequence > sampled_through)
}
pub(crate) fn is_retained_pending_mail(envelope: &ResponseItemEnvelope, first: u64) -> bool {
matches!(envelope.item, ResponseItem::AgentMessage { .. })
&& envelope
.metadata
.as_ref()
.and_then(|metadata| metadata.history_sequence)
.is_some_and(|sequence| sequence >= first)
&& estimate_item_token_count(&envelope.item) <= MAX_RETAINED_AGENT_MESSAGE_TOKENS
}
pub(crate) fn insert_summary(
mut retained: Vec<ResponseItemEnvelope>,
summary: ResponseItemEnvelope,
pending_sequence: Option<u64>,
) -> Vec<ResponseItemEnvelope> {
let position = pending_sequence
.and_then(|first| {
retained.iter().position(|envelope| {
envelope
.metadata
.as_ref()
.and_then(|metadata| metadata.history_sequence)
.is_some_and(|sequence| sequence >= first)
})
})
.unwrap_or(retained.len());
retained.insert(position, summary);
retained
}