use crate::memory::archivist::clip::clean_conversation;
use crate::memory::archivist::compose::compose_conversation_md;
use crate::memory::archivist::sink::{now, LeafMeta, TreeLeafSink};
use crate::memory::archivist::types::Turn;
use crate::memory::store::content::atomic::sha256_hex;
const TOKEN_DIVISOR: usize = 4;
#[derive(Clone, Debug, PartialEq)]
pub struct ArchiveOutcome {
pub chunk_id: String,
pub new_summary_ids: Vec<String>,
pub seal_pending: bool,
}
pub fn archive_to_tree(
sink: &dyn TreeLeafSink,
session_id: &str,
turns: &[Turn],
) -> anyhow::Result<ArchiveOutcome> {
let cleaned = clean_conversation(turns);
let md = compose_conversation_md(&cleaned);
let chunk_id = chunk_id_for_session(session_id, &md);
let token_count = (md.len() / TOKEN_DIVISOR).max(1) as u32;
let timestamp = cleaned.last().map(|t| t.timestamp).unwrap_or_else(now);
let meta = LeafMeta {
chunk_id: chunk_id.clone(),
session_id: session_id.to_string(),
token_count,
timestamp,
};
let new_summary_ids = sink.append_leaf(&md, &meta)?;
Ok(ArchiveOutcome {
chunk_id,
new_summary_ids,
seal_pending: false,
})
}
pub fn chunk_id_for_session(session_id: &str, md: &str) -> String {
let mut bytes = Vec::with_capacity(session_id.len() + 1 + md.len());
bytes.extend_from_slice(session_id.as_bytes());
bytes.push(0);
bytes.extend_from_slice(md.as_bytes());
let hex = sha256_hex(&bytes);
format!("archivist:{}", &hex[..32])
}
#[cfg(test)]
#[path = "tree_writer_tests.rs"]
mod tests;