use std::path::{Path, PathBuf};
use super::{
AGENTS_DIR, Entry, EntryKind, JSON_EXT, MD_EXT, MESSAGES_DIR, TOOL_ORIGIN, Transcript,
compaction, parse_model, parse_tool_result,
};
pub fn build(workspace: &Path, agent_id: &str) -> Transcript {
let agent = workspace.join(AGENTS_DIR).join(agent_id);
let read = read_messages(&agent.join(MESSAGES_DIR));
Transcript {
entries: compaction::splice(&agent, read),
}
}
fn read_messages(dir: &Path) -> Vec<Entry> {
let Ok(read) = std::fs::read_dir(dir) else {
return Vec::new();
};
let mut files: Vec<(String, PathBuf)> = read
.flatten()
.filter_map(|e| {
let path = e.path();
path.is_file()
.then(|| (e.file_name().to_string_lossy().into_owned(), path))
})
.collect();
files.sort_by(|a, b| a.0.cmp(&b.0));
files
.into_iter()
.map(|(name, path)| {
let raw = std::fs::read(&path).unwrap_or_default();
let kind = classify(&name, &raw);
Entry { name, raw, kind }
})
.collect()
}
pub fn classify(name: &str, raw: &[u8]) -> EntryKind {
let Some((_, origin, ext)) = parse_name(name) else {
return EntryKind::Raw;
};
match ext {
MD_EXT => {
let deposit = crate::inboxview::parse_deposit(raw);
EntryKind::Delivered {
sender: origin.to_string(),
epitaph: deposit.epitaph,
body: deposit.body,
}
}
JSON_EXT if origin == TOOL_ORIGIN => parse_tool_result(raw).unwrap_or(EntryKind::Raw),
JSON_EXT => parse_model(origin, raw),
_ => EntryKind::Raw,
}
}
pub(super) fn parse_name(name: &str) -> Option<(&str, &str, &str)> {
let (stem, ext) = name.rsplit_once('.')?;
let (num, origin) = stem.split_once('-')?;
if num.is_empty() || !num.bytes().all(|b| b.is_ascii_digit()) || origin.is_empty() {
return None;
}
Some((num, origin, ext))
}