Reinject context for summary-only mid-turn compaction

This commit is contained in:
Charles Cunningham
2026-02-18 01:50:58 -08:00
parent 0fbc4c76a8
commit 96b88aee15
4 changed files with 91 additions and 11 deletions

View File

@@ -300,7 +300,7 @@ async fn run_compact_task_inner(
// These callsites do not get a later post-compaction canonical-context write in
// `run_turn`, so replacement history must carry canonical context directly.
let initial_context = sess.build_initial_context(turn_context.as_ref()).await;
insert_initial_context_before_last_real_user(&mut new_history, initial_context);
insert_initial_context_before_last_user_anchor(&mut new_history, initial_context);
}
CompactCallsite::ManualCompact => {
// Manual `/compact` intentionally rewrites transcript history without reseeding turn
@@ -389,15 +389,27 @@ pub(crate) fn process_compacted_history(
compacted_history
}
pub(crate) fn insert_initial_context_before_last_real_user(
pub(crate) fn insert_initial_context_before_last_user_anchor(
compacted_history: &mut Vec<ResponseItem>,
initial_context: Vec<ResponseItem>,
) {
if initial_context.is_empty() {
return;
}
if let Some(last_real_user_index) = compacted_history.iter().rposition(is_real_user_message) {
compacted_history.splice(last_real_user_index..last_real_user_index, initial_context);
let insertion_index = compacted_history
.iter()
.rposition(is_real_user_message)
.or_else(|| {
compacted_history.iter().rposition(|item| {
matches!(
crate::event_mapping::parse_turn_item(item),
Some(TurnItem::UserMessage(user_message))
if is_summary_message(&user_message.message())
)
})
});
if let Some(index) = insertion_index {
compacted_history.splice(index..index, initial_context);
}
}
@@ -1268,4 +1280,70 @@ keep me updated
}];
assert_eq!(refreshed, expected);
}
#[test]
fn insert_initial_context_before_last_user_anchor_falls_back_to_last_summary() {
let mut compacted_history = vec![
ResponseItem::Message {
id: None,
role: "user".to_string(),
content: vec![ContentItem::InputText {
text: format!("{SUMMARY_PREFIX}\nolder summary"),
}],
end_turn: None,
phase: None,
},
ResponseItem::Message {
id: None,
role: "user".to_string(),
content: vec![ContentItem::InputText {
text: format!("{SUMMARY_PREFIX}\nlatest summary"),
}],
end_turn: None,
phase: None,
},
];
let initial_context = vec![ResponseItem::Message {
id: None,
role: "developer".to_string(),
content: vec![ContentItem::InputText {
text: "fresh permissions".to_string(),
}],
end_turn: None,
phase: None,
}];
insert_initial_context_before_last_user_anchor(&mut compacted_history, initial_context);
let expected = vec![
ResponseItem::Message {
id: None,
role: "user".to_string(),
content: vec![ContentItem::InputText {
text: format!("{SUMMARY_PREFIX}\nolder summary"),
}],
end_turn: None,
phase: None,
},
ResponseItem::Message {
id: None,
role: "developer".to_string(),
content: vec![ContentItem::InputText {
text: "fresh permissions".to_string(),
}],
end_turn: None,
phase: None,
},
ResponseItem::Message {
id: None,
role: "user".to_string(),
content: vec![ContentItem::InputText {
text: format!("{SUMMARY_PREFIX}\nlatest summary"),
}],
end_turn: None,
phase: None,
},
];
assert_eq!(compacted_history, expected);
}
}

View File

@@ -6,7 +6,7 @@ use crate::codex::TurnContext;
use crate::compact::CompactCallsite;
use crate::compact::extract_latest_model_switch_update_from_items;
use crate::compact::extract_trailing_model_switch_update_for_compaction_request;
use crate::compact::insert_initial_context_before_last_real_user;
use crate::compact::insert_initial_context_before_last_user_anchor;
use crate::compact::process_compacted_history;
use crate::compact::should_keep_compacted_history_item;
use crate::context_manager::ContextManager;
@@ -162,7 +162,7 @@ async fn run_remote_compact_task_inner_impl(
// These callsites do not get a later post-compaction canonical-context write in
// `run_turn`, so replacement history must carry canonical context directly.
let initial_context = sess.build_initial_context(turn_context.as_ref()).await;
insert_initial_context_before_last_real_user(&mut new_history, initial_context);
insert_initial_context_before_last_user_anchor(&mut new_history, initial_context);
}
CompactCallsite::ManualCompact => {
// Manual `/compact` intentionally rewrites transcript history without reseeding turn

View File

@@ -1790,7 +1790,7 @@ async fn snapshot_request_shape_remote_mid_turn_compaction_summary_only_layout()
insta::assert_snapshot!(
"remote_mid_turn_compaction_summary_only_shapes",
format_labeled_requests_snapshot(
"Remote mid-turn compaction where compact output has only summary user content: continuation layout keeps the summary-only compact output without inserting extra context items.",
"Remote mid-turn compaction where compact output has only summary user content: continuation layout reinjects canonical context before the latest summary.",
&[
("Remote Compaction Request", &compact_request),
(

View File

@@ -1,9 +1,8 @@
---
source: core/tests/suite/compact_remote.rs
assertion_line: 1790
expression: "format_labeled_requests_snapshot(\"Remote mid-turn compaction where compact output has only summary user content: continuation layout keeps the summary-only compact output without inserting extra context items.\",\n&[(\"Remote Compaction Request\", &compact_request),\n(\"Remote Post-Compaction History Layout\", &post_compact_turn_request),])"
expression: "format_labeled_requests_snapshot(\"Remote mid-turn compaction where compact output has only summary user content: continuation layout reinjects canonical context before the latest summary.\",\n&[(\"Remote Compaction Request\", &compact_request),\n(\"Remote Post-Compaction History Layout\", &post_compact_turn_request),])"
---
Scenario: Remote mid-turn compaction where compact output has only summary user content: continuation layout keeps the summary-only compact output without inserting extra context items.
Scenario: Remote mid-turn compaction where compact output has only summary user content: continuation layout reinjects canonical context before the latest summary.
## Remote Compaction Request
00:message/developer:<PERMISSIONS_INSTRUCTIONS>
@@ -14,4 +13,7 @@ Scenario: Remote mid-turn compaction where compact output has only summary user
05:function_call_output:unsupported call: test_tool
## Remote Post-Compaction History Layout
00:message/user:<COMPACTION_SUMMARY>\nREMOTE_SUMMARY_ONLY
00:message/developer:<PERMISSIONS_INSTRUCTIONS>
01:message/user:<AGENTS_MD>
02:message/user:<ENVIRONMENT_CONTEXT:cwd=<CWD>>
03:message/user:<COMPACTION_SUMMARY>\nREMOTE_SUMMARY_ONLY