use std::panic::{catch_unwind, AssertUnwindSafe};
use crate::memory::artifacts::{
compact_tool_results, render_persisted_artifacts_section, ToolResultArtifactConfig,
};
use crate::memory::post_compact_restore::{restore_key_files, PostCompactRestoreConfig};
use crate::memory::summary::LocalSummary;
use crate::types::{Message, MessageRole};
use super::helpers::{extract_original_user_request, normalize_summary_output};
use super::normalization;
use super::prompts;
use super::MemoryManager;
impl MemoryManager {
pub(super) fn compact_large_tool_results(
&self,
messages: &[Message],
cycle_index: Option<u32>,
) -> (Vec<Message>, bool) {
let (compacted, _artifacts, changed) = compact_tool_results(
messages,
&ToolResultArtifactConfig {
workspace: self.config.workspace.clone(),
artifact_dir: self.config.tool_result_artifact_dir.clone(),
compact_threshold: self.config.tool_result_compact_threshold,
keep_last: self.config.tool_result_keep_last,
excerpt_head: self.config.tool_result_excerpt_head,
excerpt_tail: self.config.tool_result_excerpt_tail,
},
cycle_index,
);
(compacted, changed)
}
pub(super) fn compress_memory(
&self,
messages: &[Message],
cycle_index: Option<u32>,
) -> (Vec<Message>, bool) {
let messages = self.strip_session_memory_context(messages);
if messages.len() <= 2 {
return (messages, false);
}
let system_message = messages
.iter()
.find(|message| message.role == MessageRole::System)
.cloned();
let (messages_for_summary, _normalized) = self.normalize_compaction_messages(&messages);
let (messages_for_summary, artifacts, _compacted_tools) = compact_tool_results(
&messages_for_summary,
&ToolResultArtifactConfig {
workspace: self.config.workspace.clone(),
artifact_dir: self.config.tool_result_artifact_dir.clone(),
compact_threshold: self.config.tool_result_compact_threshold,
keep_last: self.config.tool_result_keep_last,
excerpt_head: self.config.tool_result_excerpt_head,
excerpt_tail: self.config.tool_result_excerpt_tail,
},
cycle_index,
);
let original_request = extract_original_user_request(&messages).unwrap_or_default();
let summary_prompt = self.build_compress_memory_prompt(&messages_for_summary);
let mut compressed_memory = self.generate_summary(&summary_prompt, &messages_for_summary);
if let Ok(summary_data) = serde_json::from_str(&compressed_memory) {
let restored_context = restore_key_files(
&summary_data,
self.config.workspace.as_deref(),
&PostCompactRestoreConfig {
token_model: self.config.model.clone(),
..PostCompactRestoreConfig::default()
},
);
if !restored_context.is_empty() {
compressed_memory.push_str("\n\n");
compressed_memory.push_str(&restored_context);
}
}
if let Some(artifact_section) = render_persisted_artifacts_section(&artifacts) {
compressed_memory.push_str("\n\n");
compressed_memory.push_str(&artifact_section);
}
let mut compacted = Vec::new();
if let Some(system_message) = system_message {
compacted.push(system_message);
}
compacted.push(Message::user(format!(
"<Original User Request>\n{original_request}\n</Original User Request>\n\n<Compressed Agent Memory>\n{compressed_memory}\n</Compressed Agent Memory>"
)));
(compacted, true)
}
fn build_compress_memory_prompt(&self, messages: &[Message]) -> String {
prompts::build_compress_memory_prompt(
&self.config.language,
self.config.summary_event_limit,
messages,
)
}
fn generate_summary(&self, prompt: &str, messages: &[Message]) -> String {
if let Some(callback) = &self.config.summary_callback {
let callback_result = catch_unwind(AssertUnwindSafe(|| {
callback(
prompt,
self.config.summary_backend.as_deref(),
self.config.summary_model.as_deref(),
)
}));
if let Ok(Some(summary)) = callback_result {
let normalized = normalize_summary_output(&summary);
if !normalized.trim().is_empty() {
return normalized;
}
}
}
LocalSummary::from_messages(messages, self.config.summary_event_limit).to_json_string()
}
fn normalize_compaction_messages(&self, messages: &[Message]) -> (Vec<Message>, bool) {
normalization::normalize_compaction_messages(
messages,
self.config.tool_calls_keep_last,
self.config.assistant_no_tool_keep_last,
)
}
}