use super::AgentRunner;
use crate::compaction::auto::{AutoCompactionInput, auto_compact_messages};
use crate::compaction::memory_envelope::{MemoryEnvelopePlacement, local_compaction_config};
use crate::core::agent::compaction_checkpoint::write_compaction_checkpoint_async;
use crate::core::agent::context_reset::maybe_write_reset_after_compaction_with_context_async;
use crate::core::agent::conversation::conversation_from_messages;
use crate::core::agent::session::AgentSessionState;
use crate::exec::events::CompactionTrigger;
use std::sync::Arc;
use tracing::{info, warn};
impl AgentRunner {
pub(super) async fn maybe_auto_compact(
&mut self,
session_state: &mut AgentSessionState,
event_recorder: &mut crate::core::agent::events::ExecEventRecorder,
turn_model: &str,
preserve_recent_turns: usize,
prompt_overhead_tokens: usize,
reserved_output_tokens: usize,
request_envelope: &mut crate::core::agent::request_envelope::SessionRequestEnvelope,
) {
let mut engine_cfg = local_compaction_config(Some(self.config()), false);
engine_cfg.keep_last_messages = preserve_recent_turns;
let outcome = match auto_compact_messages(
AutoCompactionInput {
provider: self.provider_client.as_ref(),
model: turn_model,
session_id: &self.session_id,
workspace_root: self._workspace.as_path(),
vt_cfg: Some(self.config()),
current_token_usage: session_state.total_tokens().saturating_add(prompt_overhead_tokens),
reserved_output_tokens,
touched_files: &session_state.modified_files,
engine_cfg,
manual_options: crate::compaction::ManualCompactionOptions::default(),
placement: MemoryEnvelopePlacement::BeforeLastUserOrSummary,
prefire: None,
auto_compact_suppressed: &mut session_state.auto_compact_suppressed,
force_compaction: false,
steering_update: None,
},
Arc::make_mut(&mut session_state.messages),
)
.await
{
Ok(Some(outcome)) => outcome,
Ok(None) => return,
Err(error) => {
warn!(
error = %error,
"Automatic context compaction failed; continuing with full history"
);
return;
}
};
session_state.conversation = conversation_from_messages(&session_state.messages);
session_state.normalize();
session_state.last_processed_message_idx = session_state.conversation.len();
if let Some(ref envelope) = outcome.envelope {
write_compaction_checkpoint_async(self._workspace.as_path(), envelope).await;
}
let previous_envelope = request_envelope.clone();
let next_segment_id = format!("{}-compact-{}", previous_envelope.segment_id(), outcome.original_len);
*request_envelope = previous_envelope.begin_segment(next_segment_id);
let reset_mode = self.config().agent.harness.context_reset_mode.as_str().to_owned();
let workspace = self._workspace.clone();
let mut checkpoint_paths = outcome.history_artifact_path.iter().cloned().collect::<Vec<_>>();
if outcome.envelope.is_some() {
checkpoint_paths.extend([
"memories/progress.md".to_string(),
"memories/compaction_summary.md".to_string(),
]);
}
match maybe_write_reset_after_compaction_with_context_async(
workspace.as_path(),
&reset_mode,
Some(self.session_id.clone()),
Some(format!("compaction-{}", outcome.original_len)),
checkpoint_paths,
Some(format!("segment:{}", request_envelope.segment_id())),
)
.await
{
Ok(true) => info!("Context reset manifest written after compaction (mode: {})", reset_mode),
Ok(false) => {}
Err(error) => warn!(error = %error, "Failed to write context reset manifest after compaction"),
}
event_recorder.compact_boundary(
CompactionTrigger::Auto,
outcome.mode,
outcome.original_len,
outcome.compacted_len,
outcome.history_artifact_path.as_deref(),
Some(&previous_envelope),
Some(request_envelope),
);
info!(
provider = %self.provider_client.name(),
model = turn_model,
original_len = outcome.original_len,
compacted_len = outcome.compacted_len,
compaction_mode = %outcome.mode.as_str(),
"Applied automatic conversation compaction (core loop)"
);
}
}