use crate::services::compact::microcompact::reset_microcompact_state;
use std::collections::HashMap;
use std::sync::{LazyLock, Mutex};
const MAIN_THREAD_SOURCES: &[&str] = &["repl_main_thread", "sdk"];
pub fn is_main_thread_compact(query_source: Option<&str>) -> bool {
match query_source {
Some(source) => MAIN_THREAD_SOURCES.contains(&source),
None => true, }
}
#[derive(Debug, Default, Clone)]
pub struct CleanupState {
pub context_collapse_reset: bool,
pub user_context_cleared: bool,
pub memory_files_cleared: bool,
pub system_prompt_cleared: bool,
pub classifier_approvals_cleared: bool,
pub speculative_checks_cleared: bool,
pub beta_tracing_cleared: bool,
pub session_messages_cleared: bool,
pub file_content_swept: bool,
}
static CLEANUP_STATE: LazyLock<Mutex<CleanupState>> =
LazyLock::new(|| Mutex::new(CleanupState::default()));
pub fn get_cleanup_state() -> CleanupState {
CLEANUP_STATE.lock().unwrap().clone()
}
pub fn reset_cleanup_state_for_testing() {
*CLEANUP_STATE.lock().unwrap() = CleanupState::default();
}
pub fn run_post_compact_cleanup(query_source: Option<&str>) {
let mut cleanup = CleanupState::default();
reset_microcompact_state();
if !is_main_thread_compact(query_source) {
log::debug!(
"[post-compact] Skipping main-thread cleanup for sub-agent source: {:?}",
query_source
);
return;
}
reset_context_collapse();
cleanup.context_collapse_reset = true;
clear_user_context_cache();
cleanup.user_context_cleared = true;
clear_memory_files_cache();
cleanup.memory_files_cleared = true;
clear_system_prompt_sections();
cleanup.system_prompt_cleared = true;
clear_classifier_approvals();
cleanup.classifier_approvals_cleared = true;
clear_speculative_checks();
cleanup.speculative_checks_cleared = true;
clear_beta_tracing_state();
cleanup.beta_tracing_cleared = true;
clear_session_messages_cache();
cleanup.session_messages_cleared = true;
sweep_file_content_cache();
cleanup.file_content_swept = true;
*CLEANUP_STATE.lock().unwrap() = cleanup;
log::info!("[post-compact] Cleanup complete");
}
pub fn reset_context_collapse() {
log::debug!("[context-collapse] State reset - clearing collapse tracking");
}
pub fn clear_user_context_cache() {
log::debug!("[post-compact] User context cache cleared");
}
pub fn clear_memory_files_cache() {
log::debug!("[post-compact] Memory files cache cleared");
}
pub fn clear_system_prompt_sections() {
log::debug!("[post-compact] System prompt sections cleared");
}
pub fn clear_classifier_approvals() {
log::debug!("[post-compact] Classifier approvals cleared");
}
pub fn clear_speculative_checks() {
log::debug!("[post-compact] Speculative checks cleared");
}
pub fn clear_beta_tracing_state() {
log::debug!("[post-compact] Beta tracing state cleared");
}
pub fn clear_session_messages_cache() {
log::debug!("[post-compact] Session messages cache cleared");
}
pub fn sweep_file_content_cache() {
log::debug!("[post-compact] File content cache swept");
}
pub fn clear_all_caches() {
reset_context_collapse();
clear_user_context_cache();
clear_memory_files_cache();
clear_system_prompt_sections();
clear_classifier_approvals();
clear_speculative_checks();
clear_beta_tracing_state();
clear_session_messages_cache();
sweep_file_content_cache();
}
pub fn get_last_cleanup_summary() -> String {
let state = CLEANUP_STATE.lock().unwrap();
let mut parts = Vec::new();
if state.context_collapse_reset {
parts.push("context_collapse");
}
if state.user_context_cleared {
parts.push("user_context");
}
if state.memory_files_cleared {
parts.push("memory_files");
}
if state.system_prompt_cleared {
parts.push("system_prompt");
}
if state.classifier_approvals_cleared {
parts.push("classifier_approvals");
}
if state.speculative_checks_cleared {
parts.push("speculative_checks");
}
if state.beta_tracing_cleared {
parts.push("beta_tracing");
}
if state.session_messages_cleared {
parts.push("session_messages");
}
if state.file_content_swept {
parts.push("file_content");
}
if parts.is_empty() {
"No caches cleared (possibly sub-agent compact)".to_string()
} else {
format!("Caches cleared: {}", parts.join(", "))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_main_thread_compact_repl() {
assert!(is_main_thread_compact(Some("repl_main_thread")));
}
#[test]
fn test_is_main_thread_compact_sdk() {
assert!(is_main_thread_compact(Some("sdk")));
}
#[test]
fn test_is_main_thread_compact_none() {
assert!(is_main_thread_compact(None));
}
#[test]
fn test_is_main_thread_compact_subagent() {
assert!(!is_main_thread_compact(Some("session_memory")));
assert!(!is_main_thread_compact(Some("compact")));
assert!(!is_main_thread_compact(Some("prompt_suggestion")));
}
#[test]
fn test_run_post_compact_cleanup_main_thread() {
run_post_compact_cleanup(Some("repl_main_thread"));
let state = get_cleanup_state();
assert!(state.context_collapse_reset);
assert!(state.user_context_cleared);
}
#[test]
fn test_run_post_compact_cleanup_subagent() {
{
let mut state = CLEANUP_STATE.lock().unwrap();
*state = CleanupState::default();
}
run_post_compact_cleanup(Some("session_memory"));
let state = get_cleanup_state();
assert!(!state.context_collapse_reset);
}
#[test]
fn test_reset_context_collapse() {
reset_context_collapse();
}
#[test]
fn test_clear_system_prompt_sections() {
clear_system_prompt_sections();
}
#[test]
fn test_clear_all_caches() {
clear_all_caches();
}
#[test]
fn test_get_last_cleanup_summary_empty() {
reset_cleanup_state_for_testing();
run_post_compact_cleanup(Some("subagent"));
let summary = get_last_cleanup_summary();
assert!(summary.contains("No caches cleared"), "Expected 'No caches cleared', got: {}", summary);
}
#[test]
fn test_get_last_cleanup_summary_populated() {
reset_cleanup_state_for_testing();
run_post_compact_cleanup(Some("repl_main_thread"));
let summary = get_last_cleanup_summary();
assert!(summary.contains("context_collapse"));
assert!(summary.contains("user_context"));
}
}