diff --git a/codex-rs/core/src/codex.rs b/codex-rs/core/src/codex.rs index 4c9951a96c..30794012f5 100644 --- a/codex-rs/core/src/codex.rs +++ b/codex-rs/core/src/codex.rs @@ -5966,12 +5966,25 @@ fn build_server_side_compaction_replacement_history( let checkpoint_turn_items = history_at_checkpoint .strip_prefix(history_before_turn) .unwrap_or(history_at_checkpoint); - let checkpoint_turn_items = checkpoint_turn_items - .strip_prefix(turn_start_context_items) - .unwrap_or(checkpoint_turn_items); - let checkpoint_turn_items = checkpoint_turn_items - .strip_prefix(compaction_initial_context) - .unwrap_or(checkpoint_turn_items); + let stripped_compaction_initial_context = + checkpoint_turn_items.strip_prefix(compaction_initial_context); + let stripped_turn_start_context_items = + checkpoint_turn_items.strip_prefix(turn_start_context_items); + let checkpoint_turn_items = match ( + stripped_compaction_initial_context, + stripped_turn_start_context_items, + ) { + (Some(after_compaction_initial_context), Some(after_turn_start_context_items)) => { + if compaction_initial_context.len() >= turn_start_context_items.len() { + after_compaction_initial_context + } else { + after_turn_start_context_items + } + } + (Some(after_compaction_initial_context), None) => after_compaction_initial_context, + (None, Some(after_turn_start_context_items)) => after_turn_start_context_items, + (None, None) => checkpoint_turn_items, + }; let post_checkpoint_turn_items = current_history .strip_prefix(history_at_checkpoint) .unwrap_or_default(); diff --git a/codex-rs/core/src/codex_tests.rs b/codex-rs/core/src/codex_tests.rs index ce97e48604..d95c7774c6 100644 --- a/codex-rs/core/src/codex_tests.rs +++ b/codex-rs/core/src/codex_tests.rs @@ -549,6 +549,60 @@ fn build_server_side_compaction_replacement_history_reuses_existing_initial_cont ); } +#[test] +fn build_server_side_compaction_replacement_history_prefers_longer_initial_context_prefix() { + let history_before_turn = vec![user_message("earlier")]; + let turn_start_context_items = vec![developer_message("\nuse the new model")]; + let compaction_initial_context = vec![ + turn_start_context_items[0].clone(), + environment_context_message("/fresh"), + ]; + let current_turn_user = user_message("current turn"); + let prior_compaction = ResponseItem::Compaction { + encrypted_content: "INLINE_SUMMARY_1".to_string(), + }; + let new_compaction = ResponseItem::Compaction { + encrypted_content: "INLINE_SUMMARY_2".to_string(), + }; + let current_turn_tool_output = ResponseItem::FunctionCallOutput { + call_id: "call-1".to_string(), + output: FunctionCallOutputPayload::from_text("tool result".to_string()), + }; + let history_at_checkpoint = vec![ + compaction_initial_context[0].clone(), + compaction_initial_context[1].clone(), + current_turn_user.clone(), + prior_compaction, + ]; + let current_history = vec![ + history_at_checkpoint[0].clone(), + history_at_checkpoint[1].clone(), + history_at_checkpoint[2].clone(), + history_at_checkpoint[3].clone(), + current_turn_tool_output.clone(), + ]; + + let replacement_history = build_server_side_compaction_replacement_history( + new_compaction.clone(), + &compaction_initial_context, + &turn_start_context_items, + &history_before_turn, + &history_at_checkpoint, + ¤t_history, + ); + + assert_eq!( + replacement_history, + vec![ + compaction_initial_context[0].clone(), + compaction_initial_context[1].clone(), + current_turn_user, + new_compaction, + current_turn_tool_output, + ] + ); +} + #[test] fn build_server_side_compaction_replacement_history_keeps_checkpoint_before_post_compaction_items() {