feat(compaction): surface checkpoint warning

This commit is contained in:
James Peter
2025-11-07 19:06:13 +10:00
parent 752abff610
commit 8dd3c3014b
8 changed files with 111 additions and 0 deletions
+27
View File
@@ -12,6 +12,7 @@ use crate::error::Result as CodexResult;
use crate::protocol::AgentMessageEvent;
use crate::protocol::ErrorEvent;
use crate::protocol::EventMsg;
use code_protocol::protocol::CompactionCheckpointWarningEvent;
use crate::protocol::InputItem;
use crate::protocol::TaskCompleteEvent;
use crate::truncate::truncate_middle;
@@ -28,6 +29,7 @@ use base64::Engine;
use futures::prelude::*;
pub const SUMMARIZATION_PROMPT: &str = include_str!("../../templates/compact/prompt.md");
pub const COMPACTION_CHECKPOINT_MESSAGE: &str = "History checkpoint: earlier conversation compacted.";
const COMPACT_USER_MESSAGE_MAX_TOKENS: usize = 20_000;
const COMPACT_TEXT_CONTENT_MAX_BYTES: usize = 8 * 1024;
const COMPACT_TOOL_ARGS_MAX_BYTES: usize = 4 * 1024;
@@ -199,6 +201,8 @@ pub(super) async fn perform_compaction(
state.history.record_items(new_history.iter());
}
send_compaction_checkpoint_warning(&sess, &sub_id).await;
let rollout_item = RolloutItem::Compacted(CompactedItem {
message: summary_text.clone(),
});
@@ -305,6 +309,8 @@ async fn run_compact_task_inner_inline(
state.history.record_items(new_history.iter());
}
send_compaction_checkpoint_warning(&sess, &sub_id).await;
let rollout_item = RolloutItem::Compacted(CompactedItem {
message: summary_text.clone(),
});
@@ -449,6 +455,17 @@ fn sanitize_items_for_compact(items: Vec<ResponseItem>) -> Vec<ResponseItem> {
.collect()
}
fn compaction_checkpoint_warning_event() -> EventMsg {
EventMsg::CompactionCheckpointWarning(CompactionCheckpointWarningEvent {
message: COMPACTION_CHECKPOINT_MESSAGE.to_string(),
})
}
async fn send_compaction_checkpoint_warning(sess: &Arc<Session>, sub_id: &str) {
let event = sess.make_event(sub_id, compaction_checkpoint_warning_event());
sess.send_event(event).await;
}
pub(crate) fn collect_user_messages(items: &[ResponseItem]) -> Vec<String> {
items
.iter()
@@ -729,4 +746,14 @@ mod tests {
"bridge should include the provided summary text"
);
}
#[test]
fn compaction_checkpoint_warning_event_has_copy() {
match compaction_checkpoint_warning_event() {
EventMsg::CompactionCheckpointWarning(payload) => {
assert!(payload.message.contains("checkpoint"));
}
other => panic!("unexpected variant: {other:?}"),
}
}
}
+3
View File
@@ -723,6 +723,9 @@ pub enum EventMsg {
/// Browser snapshot metadata emitted alongside environment context.
BrowserSnapshot(BrowserSnapshotEvent),
/// Warning that the platform compacted conversation history to stay within limits.
CompactionCheckpointWarning(code_protocol::protocol::CompactionCheckpointWarningEvent),
/// Ack the client's configure message.
SessionConfigured(SessionConfiguredEvent),
@@ -618,6 +618,7 @@ impl EventProcessor for EventProcessorWithHumanOutput {
EventMsg::UserMessage(_) => {}
EventMsg::EnteredReviewMode(_) => {}
EventMsg::ExitedReviewMode(_) => {}
EventMsg::CompactionCheckpointWarning(_) => {}
}
CodexStatus::Running
}
@@ -290,6 +290,7 @@ async fn run_code_tool_session_inner(
| EventMsg::EnvironmentContextFull(_)
| EventMsg::EnvironmentContextDelta(_)
| EventMsg::AgentStatusUpdate(_)
| EventMsg::CompactionCheckpointWarning(_)
| EventMsg::TurnAborted(_)
| EventMsg::ConversationPath(_)
| EventMsg::UserMessage(_)
+27
View File
@@ -538,6 +538,9 @@ pub enum EventMsg {
/// Browser snapshot metadata emitted alongside environment context.
BrowserSnapshot(BrowserSnapshotEvent),
/// Warning that the platform compacted conversation history to stay within limits.
CompactionCheckpointWarning(CompactionCheckpointWarningEvent),
/// Ack the client's configure message.
SessionConfigured(SessionConfiguredEvent),
@@ -626,6 +629,11 @@ pub struct TaskStartedEvent {
pub model_context_window: Option<u64>,
}
#[derive(Debug, Clone, Deserialize, Serialize, TS)]
pub struct CompactionCheckpointWarningEvent {
pub message: String,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize, TS)]
pub struct TokenUsage {
pub input_tokens: u64,
@@ -1507,4 +1515,23 @@ mod tests {
assert_eq!(deserialized, event);
Ok(())
}
#[test]
fn compaction_checkpoint_warning_round_trips() -> Result<()> {
let event = EventMsg::CompactionCheckpointWarning(CompactionCheckpointWarningEvent {
message: "History checkpoint: earlier conversation compacted.".to_string(),
});
let serialized = serde_json::to_string(&event)?;
let restored: EventMsg = serde_json::from_str(&serialized)?;
match restored {
EventMsg::CompactionCheckpointWarning(payload) => {
assert!(payload.message.contains("checkpoint"));
}
other => panic!("unexpected variant: {other:?}"),
}
Ok(())
}
}
+8
View File
@@ -161,6 +161,7 @@ use code_core::protocol::PatchApplyEndEvent;
use code_core::protocol::TaskCompleteEvent;
use code_core::protocol::TokenUsage;
use code_core::protocol::TurnDiffEvent;
use code_core::codex::compact::COMPACTION_CHECKPOINT_MESSAGE;
use crate::bottom_pane::{
AutoActiveViewModel,
AutoCoordinatorButton,
@@ -10385,6 +10386,9 @@ impl ChatWidget<'_> {
EventMsg::BrowserSnapshot(ev) => {
self.handle_browser_snapshot_event(&ev);
}
EventMsg::CompactionCheckpointWarning(event) => {
self.history_push_plain_paragraphs(PlainMessageKind::Notice, [event.message]);
}
EventMsg::SessionConfigured(event) => {
// Remove stale "Connecting MCP servers…" status from the startup notice
// now that MCP initialization has completed in core.
@@ -14995,6 +14999,10 @@ Have we met every part of this goal and is there no further work to do?"#
self.auto_history.replace_all(conversation.clone());
self.auto_compaction_overlay =
self.derive_compaction_overlay(&previous_items, &previous_indices, &conversation);
self.history_push_plain_paragraphs(
PlainMessageKind::Notice,
[COMPACTION_CHECKPOINT_MESSAGE],
);
self.auto_rebuild_live_ring();
self.request_redraw();
}
@@ -0,0 +1,24 @@
---
source: tui/tests/vt100_chatwidget_snapshot.rs
assertion_line: 2006
expression: frame
---
+----------------------------------------------------------------------------+
| Code |
+----------------------------------------------------------------------------+
• I can help with various tasks including:
- Writing code
- Reading files
- Running commands
★ History checkpoint: earlier conversation compacted.
+----------------------------------------------------------------------------+
| What can I code for you today? |
+----------------------------------------------------------------------------+
Read Only (Shift+Tab change) Ctrl+H help • API key
@@ -32,6 +32,8 @@ use code_tui::test_helpers::{
AutoContinueModeFixture,
ChatWidgetHarness,
};
use code_core::codex::compact::COMPACTION_CHECKPOINT_MESSAGE;
use code_protocol::protocol::CompactionCheckpointWarningEvent;
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind, KeyEventState, KeyModifiers};
use regex_lite::{Captures, Regex};
use serde_json::json;
@@ -1986,6 +1988,24 @@ fn settings_help_overlay_from_section() {
insta::assert_snapshot!("settings_help_overlay_section_closed", section_closed);
}
#[test]
fn compaction_checkpoint_warning_notice() {
let _guard = ENV_LOCK.lock().unwrap();
let mut harness = ChatWidgetHarness::new();
harness.handle_event(Event {
id: "compaction-warning".into(),
event_seq: 0,
msg: EventMsg::CompactionCheckpointWarning(CompactionCheckpointWarningEvent {
message: COMPACTION_CHECKPOINT_MESSAGE.to_string(),
}),
order: None,
});
let frame = normalize_output(render_chat_widget_to_vt100(&mut harness, 80, 20));
insta::assert_snapshot!("compaction_checkpoint_warning_notice", frame);
}
#[test]
fn agent_status_missing_batch_displays_error() {
let mut harness = ChatWidgetHarness::new();