diff --git a/crates/loopal-protocol/src/event_summary.rs b/crates/loopal-protocol/src/event_summary.rs index 0cf5b949..e1b14bb2 100644 --- a/crates/loopal-protocol/src/event_summary.rs +++ b/crates/loopal-protocol/src/event_summary.rs @@ -36,7 +36,10 @@ pub enum CompactPhase { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CompactionSummary { pub kept: usize, - pub removed: usize, + /// Message-count reduction (before − after). These were condensed into the + /// summary, NOT discarded — their content survives in the summary plus the + /// re-read (`files_rehydrated`) files. + pub summarized: usize, pub tokens_before: u32, pub tokens_after: u32, pub strategy: String, diff --git a/crates/loopal-protocol/tests/suite/event_lifecycle_test.rs b/crates/loopal-protocol/tests/suite/event_lifecycle_test.rs index 55deca59..7b1658b5 100644 --- a/crates/loopal-protocol/tests/suite/event_lifecycle_test.rs +++ b/crates/loopal-protocol/tests/suite/event_lifecycle_test.rs @@ -80,7 +80,7 @@ fn test_turn_completed_wire_format_unchanged() { fn test_compacted_wire_format_unchanged() { let event = AgentEvent::root(AgentEventPayload::Compacted(CompactionSummary { kept: 5, - removed: 10, + summarized: 10, tokens_before: 1000, tokens_after: 500, strategy: "smart".into(), @@ -89,7 +89,7 @@ fn test_compacted_wire_format_unchanged() { })); let json = serde_json::to_string(&event).unwrap(); assert!( - json.contains(r#""Compacted":{"kept":5,"removed":10"#), + json.contains(r#""Compacted":{"kept":5,"summarized":10"#), "wire shape regressed: {json}" ); } diff --git a/crates/loopal-runtime/src/agent_loop/compaction_run.rs b/crates/loopal-runtime/src/agent_loop/compaction_run.rs index e17baa91..27502333 100644 --- a/crates/loopal-runtime/src/agent_loop/compaction_run.rs +++ b/crates/loopal-runtime/src/agent_loop/compaction_run.rs @@ -158,13 +158,13 @@ impl AgentLoopRunner { files_rehydrated: usize, ) -> Result<()> { let after = self.turns.view().len(); - let removed = before.saturating_sub(after); + let summarized = before.saturating_sub(after); let tokens_after = self.turns.view().current_tokens(); self.emit(AgentEventPayload::Compacted( loopal_protocol::CompactionSummary { kept: after, - removed, + summarized, tokens_before, tokens_after, strategy: strategy.to_string(), @@ -197,7 +197,7 @@ impl AgentLoopRunner { info!( before, - after, removed, tokens_before, tokens_after, strategy, "compaction complete" + after, summarized, tokens_before, tokens_after, strategy, "compaction complete" ); Ok(()) } diff --git a/crates/loopal-runtime/tests/agent_loop/compact_bare_summary_e2e_test.rs b/crates/loopal-runtime/tests/agent_loop/compact_bare_summary_e2e_test.rs index 8d4a7b6c..4e3e832f 100644 --- a/crates/loopal-runtime/tests/agent_loop/compact_bare_summary_e2e_test.rs +++ b/crates/loopal-runtime/tests/agent_loop/compact_bare_summary_e2e_test.rs @@ -62,7 +62,7 @@ async fn compact_falls_back_to_bare_summary_on_llm_failure() { _ => None, }) .expect("Compacted event must fire even on LLM failure"); - assert!(summary.removed > 0); + assert!(summary.summarized > 0); assert!(summary.kept > 0); } diff --git a/crates/loopal-runtime/tests/agent_loop/compact_force_e2e_test.rs b/crates/loopal-runtime/tests/agent_loop/compact_force_e2e_test.rs index bb95ce8a..6749340d 100644 --- a/crates/loopal-runtime/tests/agent_loop/compact_force_e2e_test.rs +++ b/crates/loopal-runtime/tests/agent_loop/compact_force_e2e_test.rs @@ -71,7 +71,7 @@ async fn compact_event_payload_carries_manual_strategy_label() { }); let stats = stats.expect("Compacted event must fire"); assert!(stats.kept > 0); - assert!(stats.removed > 0); + assert!(stats.summarized > 0); assert!( stats.strategy.starts_with("manual"), "expected manual-* strategy, got {}", diff --git a/crates/loopal-runtime/tests/agent_loop/compaction_run_e2e_test.rs b/crates/loopal-runtime/tests/agent_loop/compaction_run_e2e_test.rs index 7fe8c45e..2550235c 100644 --- a/crates/loopal-runtime/tests/agent_loop/compaction_run_e2e_test.rs +++ b/crates/loopal-runtime/tests/agent_loop/compaction_run_e2e_test.rs @@ -138,10 +138,10 @@ async fn force_compact_emits_compacted_event_with_kept_removed_stats() { assert_eq!(summary.strategy, "manual"); // before: 5 user messages; after: [summary, ack, last_user_trigger] = 3 - // → removed message count = 2. Exact value catches off-by-one regression. + // → summarized message count = 2. Exact value catches off-by-one regression. assert_eq!( - summary.removed, 2, - "Compacted.removed must reflect message-count delta (5 before → 3 after)" + summary.summarized, 2, + "Compacted.summarized must reflect message-count delta (5 before → 3 after)" ); assert!( summary.tokens_before > 0, diff --git a/crates/loopal-tui/tests/suite/e2e_compact_banner_test.rs b/crates/loopal-tui/tests/suite/e2e_compact_banner_test.rs index 609f3f84..55456023 100644 --- a/crates/loopal-tui/tests/suite/e2e_compact_banner_test.rs +++ b/crates/loopal-tui/tests/suite/e2e_compact_banner_test.rs @@ -107,7 +107,7 @@ fn compacted_event_clears_stale_banner_from_frame() { app.dispatch_event(AgentEvent::root(AgentEventPayload::Compacted( CompactionSummary { kept: 4, - removed: 80, + summarized: 80, tokens_before: 40_000, tokens_after: 4_000, strategy: "auto".into(), diff --git a/crates/loopal-view-state/src/conversation/conversation_display.rs b/crates/loopal-view-state/src/conversation/conversation_display.rs index bf3da73f..de5879d8 100644 --- a/crates/loopal-view-state/src/conversation/conversation_display.rs +++ b/crates/loopal-view-state/src/conversation/conversation_display.rs @@ -58,7 +58,7 @@ pub fn handle_auto_continuation(conv: &mut AgentConversation, cont: u32, max: u3 pub fn handle_compaction( conv: &mut AgentConversation, kept: usize, - removed: usize, + summarized: usize, tokens_before: u32, tokens_after: u32, strategy: &str, @@ -69,11 +69,12 @@ pub fn handle_compaction( } else { 0 }; + let before = kept + summarized; push_system_msg( conv, &format!( - "Context compacted ({strategy}): {removed} messages removed, \ - {kept} kept. {tokens_before}→{tokens_after} tokens ({pct}% freed).", + "Context compacted ({strategy}): {before}→{kept} messages \ + ({summarized} summarized), {tokens_before}→{tokens_after} tokens ({pct}% freed).", ), ); // Self-correct ctx counter from the Compacted event alone, in case the diff --git a/crates/loopal-view-state/src/mutators/interactive.rs b/crates/loopal-view-state/src/mutators/interactive.rs index a198c860..31277fd5 100644 --- a/crates/loopal-view-state/src/mutators/interactive.rs +++ b/crates/loopal-view-state/src/mutators/interactive.rs @@ -76,7 +76,7 @@ pub(super) fn auto_continuation( pub(super) fn compacted( state: &mut SessionViewState, kept: usize, - removed: usize, + summarized: usize, tokens_before: u32, tokens_after: u32, strategy: &str, @@ -85,7 +85,7 @@ pub(super) fn compacted( conversation_display::handle_compaction( &mut state.agent.conversation, kept, - removed, + summarized, tokens_before, tokens_after, strategy, diff --git a/crates/loopal-view-state/src/mutators/mod.rs b/crates/loopal-view-state/src/mutators/mod.rs index db23ff96..507cbb44 100644 --- a/crates/loopal-view-state/src/mutators/mod.rs +++ b/crates/loopal-view-state/src/mutators/mod.rs @@ -111,7 +111,7 @@ pub(crate) fn mutate(state: &mut SessionViewState, event: &AgentEventPayload) -> Compacted(s) => interactive::compacted( state, s.kept, - s.removed, + s.summarized, s.tokens_before, s.tokens_after, &s.strategy, diff --git a/crates/loopal-view-state/tests/suite/compact_banner_mutator_test.rs b/crates/loopal-view-state/tests/suite/compact_banner_mutator_test.rs index 21665af2..4dda0cfa 100644 --- a/crates/loopal-view-state/tests/suite/compact_banner_mutator_test.rs +++ b/crates/loopal-view-state/tests/suite/compact_banner_mutator_test.rs @@ -71,7 +71,7 @@ fn compacted_event_clears_banner() { assert!(banner_of(&r).is_some()); r.apply(AgentEventPayload::Compacted(CompactionSummary { kept: 5, - removed: 100, + summarized: 100, tokens_before: 50_000, tokens_after: 5_000, strategy: "smart".into(), @@ -119,7 +119,7 @@ fn compacted_event_refreshes_ctx() { let mut r = ViewStateReducer::new("root"); r.apply(AgentEventPayload::Compacted(CompactionSummary { kept: 9, - removed: 491, + summarized: 491, tokens_before: 259_392, tokens_after: 6_453, strategy: "manual".into(), diff --git a/crates/loopal-view-state/tests/suite/compact_idle_e2e_test.rs b/crates/loopal-view-state/tests/suite/compact_idle_e2e_test.rs index 7cdb6e4c..7a022ca1 100644 --- a/crates/loopal-view-state/tests/suite/compact_idle_e2e_test.rs +++ b/crates/loopal-view-state/tests/suite/compact_idle_e2e_test.rs @@ -38,7 +38,7 @@ fn manual_compact_from_idle_keeps_idle_and_refreshes_ctx() { r.apply(summarize("259392 tokens before")); r.apply(AgentEventPayload::Compacted(CompactionSummary { kept: 9, - removed: 491, + summarized: 491, tokens_before: 259_392, tokens_after: 6_453, strategy: "manual".into(), @@ -81,7 +81,7 @@ fn manual_compact_idle_without_token_usage_still_consistent() { r.apply(summarize("259392 tokens before")); r.apply(AgentEventPayload::Compacted(CompactionSummary { kept: 9, - removed: 491, + summarized: 491, tokens_before: 259_392, tokens_after: 6_453, strategy: "manual".into(),