Files
codex/codex-rs/core/src/session/inject_tests.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

66 lines
2.2 KiB
Rust

use crate::session::tests::make_session_and_context_with_rx;
use codex_features::Feature;
use codex_history::CodexHarnessMetadata;
use codex_history::ResponseItemEnvelope;
use codex_history::RolloutItem;
use codex_protocol::models::ConfigurationReasoning;
use codex_protocol::models::ResponseItem;
use codex_protocol::openai_models::ReasoningEffort;
use codex_protocol::protocol::EventMsg;
use pretty_assertions::assert_eq;
#[tokio::test]
async fn harness_authored_configuration_updates_preserve_metadata_and_resume() {
let (session, turn_context, rx_event) = make_session_and_context_with_rx().await;
assert!(!session.enabled(Feature::RetainClientDeveloperMessages));
let mut expected = ResponseItemEnvelope {
item: ResponseItem::ConfigurationUpdate {
reasoning: ConfigurationReasoning {
effort: ReasoningEffort::High,
},
},
metadata: Some(CodexHarnessMetadata {
harness_authored_configuration: true,
..Default::default()
}),
};
session
.record_annotated_conversation_items(
&turn_context,
turn_context.model_info(),
vec![expected.clone()],
)
.await;
let metadata = expected.metadata.as_mut().unwrap();
metadata.history_sequence = Some(1);
metadata.sampled_through_sequence = Some(0);
metadata.mcp_attribution = Some(
session
.services
.executed_tool_calls
.mcp_attribution_snapshot(),
);
let recorded = session.clone_history().await.into_annotated_items();
assert_eq!(recorded, vec![expected.clone()]);
let mut raw_items = Vec::new();
while let Ok(event) = rx_event.try_recv() {
if let EventMsg::RawResponseItem(event) = event.msg {
raw_items.push(event.item);
}
}
assert_eq!(raw_items, vec![expected.item]);
let rollout_items = recorded
.iter()
.cloned()
.map(RolloutItem::ResponseItem)
.collect::<Vec<_>>();
let reconstructed = session
.reconstruct_history_from_rollout(&turn_context, &rollout_items)
.await;
assert_eq!(reconstructed.history, recorded);
}