Skip to content

Commit 74d167e

Browse files
authored
fix(cli): make auto-compact non-agentic (#32)
Verified locally before merge: cargo check -p claurst --no-default-features (known claurst-tui::rustle warning only); git diff --check. Follow-up ensures failed/cancelled auto-compact does not reset context usage or show a false completion status. Co-authored-by: Nova <nova@openclaw.ai>
1 parent b263473 commit 74d167e

1 file changed

Lines changed: 79 additions & 52 deletions

File tree

src-rust/crates/cli/src/main.rs

Lines changed: 79 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1746,6 +1746,13 @@ async fn run_interactive(
17461746
app.provider_registry = base_query_config.provider_registry.clone();
17471747
app.refresh_context_window_size();
17481748
app.auto_compact_enabled = live_config.auto_compact;
1749+
let compact_threshold = live_config.effective_compact_threshold();
1750+
let compact_threshold_pct = if compact_threshold <= 1.0 {
1751+
compact_threshold * 100.0
1752+
} else {
1753+
compact_threshold
1754+
};
1755+
app.auto_compact_threshold = compact_threshold_pct.clamp(0.0, 100.0).ceil() as u8;
17491756
app.completion_toast_enabled = settings.completion_toast_enabled();
17501757
app.bell_on_complete = settings.bell_on_complete;
17511758

@@ -3034,62 +3041,59 @@ async fn run_interactive(
30343041
);
30353042
}
30363043

3037-
// Auto-compact: when context usage hits 99% and no query is running,
3038-
// automatically submit a compact request.
3039-
if app.context_window_size > 0
3044+
// Auto-compact: when enabled and context usage reaches the configured
3045+
// threshold with no query running, summarize through the dedicated
3046+
// no-tools compaction path instead of starting an agentic turn.
3047+
if app.auto_compact_enabled
3048+
&& app.context_window_size > 0
30403049
&& !app.is_streaming
30413050
&& current_query.is_none()
30423051
&& !app.auto_compact_running
30433052
{
3044-
let used_pct = (app.context_used_tokens as f64 / app.context_window_size as f64 * 100.0) as u64;
3045-
if used_pct >= 99 {
3053+
let used_pct =
3054+
(app.context_used_tokens as f64 / app.context_window_size as f64 * 100.0) as u64;
3055+
if used_pct >= u64::from(app.auto_compact_threshold) {
30463056
app.auto_compact_running = true;
3047-
let msg_count = messages.len();
3048-
let compact_msg = format!(
3049-
"[Auto-compact triggered ({} messages, {}% context used). \
3050-
Provide a detailed summary of our conversation so far, \
3051-
preserving all key technical details, decisions made, \
3052-
file paths mentioned, and current task status.]",
3053-
msg_count, used_pct
3054-
);
3055-
app.status_message = Some("Context 99% full — auto-compacting…".to_string());
3056-
let user_msg = claurst_core::types::Message::user(compact_msg);
3057-
messages.push(user_msg.clone());
3058-
app.push_message(user_msg);
3059-
session.messages = messages.clone();
3060-
session.updated_at = chrono::Utc::now();
3057+
app.status_message = Some(format!(
3058+
"Context {}% full — auto-compacting…",
3059+
used_pct
3060+
));
30613061

3062-
// Dispatch the compact query immediately.
30633062
let ct = CancellationToken::new();
30643063
cancel = Some(ct.clone());
30653064
let msgs_arc = Arc::new(tokio::sync::Mutex::new(messages.clone()));
30663065
let msgs_arc_clone = msgs_arc.clone();
3067-
let tools_arc_clone = tools_arc.clone();
3068-
let ctx_clone = tool_ctx.clone();
3069-
let mut qcfg = base_query_config.clone();
3070-
qcfg.model = claurst_api::effective_model_for_config(&cmd_ctx.config, &model_registry);
3071-
qcfg.max_tokens = cmd_ctx.config.effective_max_tokens();
3072-
let tracker = cost_tracker.clone();
3073-
let tx = event_tx.clone();
3066+
let compact_model =
3067+
claurst_api::effective_model_for_config(&cmd_ctx.config, &model_registry);
30743068
let client_clone = client.clone();
30753069
app.is_streaming = true;
30763070

30773071
let handle = tokio::spawn(async move {
3078-
let mut msgs = msgs_arc_clone.lock().await.clone();
3079-
let outcome = claurst_query::run_query_loop(
3072+
if ct.is_cancelled() {
3073+
return QueryOutcome::Cancelled;
3074+
}
3075+
3076+
let msgs = msgs_arc_clone.lock().await.clone();
3077+
match claurst_query::compact_conversation(
30803078
client_clone.as_ref(),
3081-
&mut msgs,
3082-
tools_arc_clone.as_slice(),
3083-
&ctx_clone,
3084-
&qcfg,
3085-
tracker,
3086-
Some(tx),
3087-
ct,
3088-
None,
3079+
&msgs,
3080+
&compact_model,
30893081
)
3090-
.await;
3091-
*msgs_arc_clone.lock().await = msgs;
3092-
outcome
3082+
.await
3083+
{
3084+
Ok(compacted) => {
3085+
let message = compacted
3086+
.first()
3087+
.cloned()
3088+
.unwrap_or_else(|| claurst_core::types::Message::assistant(""));
3089+
*msgs_arc_clone.lock().await = compacted;
3090+
QueryOutcome::EndTurn {
3091+
message,
3092+
usage: claurst_core::types::UsageInfo::default(),
3093+
}
3094+
}
3095+
Err(err) => QueryOutcome::Error(err),
3096+
}
30933097
});
30943098
current_query = Some((handle, msgs_arc));
30953099
}
@@ -3634,16 +3638,32 @@ async fn run_interactive(
36343638
if task_finished {
36353639
if let Some((handle, msgs_arc)) = current_query.take() {
36363640
// Get the outcome and handle errors
3637-
if let Ok(QueryOutcome::Error(err)) = handle.await {
3638-
while app.notifications.current_is_error() {
3639-
app.notifications.dismiss_current();
3641+
let query_outcome = handle.await;
3642+
match &query_outcome {
3643+
Ok(QueryOutcome::Error(err)) => {
3644+
while app.notifications.current_is_error() {
3645+
app.notifications.dismiss_current();
3646+
}
3647+
app.notifications.push(
3648+
claurst_tui::notifications::NotificationKind::Error,
3649+
err.to_string(),
3650+
None,
3651+
);
36403652
}
3641-
app.notifications.push(
3642-
claurst_tui::notifications::NotificationKind::Error,
3643-
err.to_string(),
3644-
None,
3645-
);
3646-
}
3653+
Err(err) => {
3654+
while app.notifications.current_is_error() {
3655+
app.notifications.dismiss_current();
3656+
}
3657+
app.notifications.push(
3658+
claurst_tui::notifications::NotificationKind::Error,
3659+
format!("Query task failed: {}", err),
3660+
None,
3661+
);
3662+
}
3663+
_ => {}
3664+
};
3665+
let auto_compact_succeeded =
3666+
matches!(query_outcome, Ok(QueryOutcome::EndTurn { .. }));
36473667
// Sync the updated conversation back to our local vector
36483668
messages = msgs_arc.lock().await.clone();
36493669
session.messages = messages.clone();
@@ -3661,9 +3681,16 @@ async fn run_interactive(
36613681
}
36623682
if app.auto_compact_running {
36633683
app.auto_compact_running = false;
3664-
// After auto-compact the context was summarised — reset usage.
3665-
app.context_used_tokens = 0;
3666-
app.status_message = Some("Auto-compact complete.".to_string());
3684+
if !auto_compact_succeeded {
3685+
app.auto_compact_enabled = false;
3686+
app.status_message = Some(
3687+
"Auto-compact failed and was disabled for this session.".to_string(),
3688+
);
3689+
} else {
3690+
// After auto-compact the context was summarised — reset usage.
3691+
app.context_used_tokens = 0;
3692+
app.status_message = Some("Auto-compact complete.".to_string());
3693+
}
36673694
}
36683695

36693696
// Save session to JSONL (primary storage)

0 commit comments

Comments
 (0)