fix(tui): refresh the context bar when auto-compaction starts

AutoCompactStarted carries the count the trigger fired on, but only
AutoCompactCompleted refreshed the context bar, so the "N% full" banner
sat next to a stale pre-turn number until the next stream update. Both
lifecycle updates now refresh the bar, on the root view and on subagent
views.

Ported from upstream 1.0.12 (grok-build acp_handler/session_notification.rs).

Claude-Session: https://claude.ai/code/session_01RUqV8HVGXcAETyYbZ2tPQ4
This commit is contained in:
雷电芽衣 committed 2026-09-13 09:35:27 -04:00
1 parent 584d0fb77e
commit a7b8438881
2 files changed
+51 -10

No files matched your search

@@ -45,6 +45,14 @@ pub(super) fn refresh_context_used(view: &mut AgentView, used: u64) {
let total = view.session.models.get_context_window().unwrap_or(0);
view.apply_context_used(used, total);
}
/// The count a compaction lifecycle update carries for the context bar: the banner's "N% full" must not sit next to a stale pre-turn number.
pub(super) fn compaction_context_refresh(update: &XaiSessionUpdate) -> Option<u64> {
match update {
XaiSessionUpdate::AutoCompactStarted { tokens_used, .. } => Some(*tokens_used),
XaiSessionUpdate::AutoCompactCompleted { tokens_after, .. } => Some(*tokens_after),
_ => None,
}
}
/// Refresh the bar and record `used` as the confirmed count for a pending
/// compaction message; call only from the `meta.totalTokens` path.
pub(super) fn confirm_context_used(view: &mut AgentView, used: u64) {
@@ -197,8 +205,10 @@ pub(super) fn handle_session_notification(notif: &acp::ExtNotification, app: &mu
&mut agent.scrollback,
is_api_key_auth,
);
if let XaiSessionUpdate::AutoCompactCompleted { tokens_after, .. } = update {
refresh_context_used(agent, *tokens_after);
if let Some(used) = compaction_context_refresh(update) {
refresh_context_used(agent, used);
}
if let XaiSessionUpdate::AutoCompactCompleted { .. } = update {
agent.todo.update_todos(Vec::new());
}
changed
@@ -1071,10 +1081,7 @@ pub(super) fn handle_child_session_notification(
| XaiSessionUpdate::AutoCompactFailed { .. }
| XaiSessionUpdate::AutoCompactCancelled { .. }
| XaiSessionUpdate::RetryState(_) => {
let compact_tokens = match &update {
XaiSessionUpdate::AutoCompactCompleted { tokens_after, .. } => Some(*tokens_after),
_ => None,
};
let compact_tokens = compaction_context_refresh(&update);
let mut changed = false;
if let Some(child_view) = agent.subagent_views.get_mut(child_sid) {
changed = apply_session_event(
@@ -576,9 +576,8 @@
}
#[test]
fn child_compact_started_does_not_reset_context_used() {
// Sibling variants in the same outer arm must not touch the numerator;
// guards against accidental widening of the AutoCompactCompleted gate.
fn child_compact_started_refreshes_context_used() {
// AutoCompactStarted carries the count the trigger fired on; the child bar must show it.
let mut agent = make_agent(Some("root-sess"));
let child_sid = "child-sess-3";
agent
@@ -603,7 +602,17 @@
let child_view = agent.subagent_views.get(child_sid).unwrap();
assert_eq!(
child_view.context_state.as_ref().map(|c| c.used),
Some(90_000)
Some(95_000)
);
// A sibling variant without a count leaves the numerator alone.
let cancelled = XaiSessionUpdate::AutoCompactCancelled {
reason: "user".into(),
};
let _ = handle_child_session_notification(cancelled, child_sid, &mut agent, false);
let child_view = agent.subagent_views.get(child_sid).unwrap();
assert_eq!(
child_view.context_state.as_ref().map(|c| c.used),
Some(95_000)
);
}
@@ -693,5 +702,30 @@
!session.model_incompatible,
"non-encrypted_content error types must not set model_incompatible"
);
}
/// The banner's count must also refresh the context bar.
#[test]
fn auto_compact_started_refreshes_the_context_bar() {
let mut app = make_app_with_agent("sess-1");
let id = AgentId(0);
app.agents.get_mut(&id).unwrap().apply_context_used(15_000, 262_144);
let payload = SessionNotification {
session_id: acp::SessionId::new("sess-1"),
update: XaiSessionUpdate::AutoCompactStarted {
tokens_used: 90_000,
context_window: 262_144,
percentage: 34,
reason: "threshold".into(),
},
meta: None,
};
let notif = acp::ExtNotification::new(
"kigi/session/update",
std::sync::Arc::from(serde_json::value::to_raw_value(&payload).unwrap()),
);
let _ = handle_ext_notification(&notif, &mut app);
let bar = app.agents[&id].context_state.as_ref().map(|c| (c.used, c.total));
assert_eq!(bar, Some((90_000, 262_144)));
}