Log full agent context window on completion

Save all context sections (system, identity, journal, conversation)
to per-agent log files for both subconscious and unconscious agents.

Co-Authored-By: ProofOfConcept <poc@bcachefs.org>
This commit is contained in:
Kent Overstreet 2026-04-10 03:28:00 -04:00
parent be44a3bb0d
commit b4dfd3c092
4 changed files with 55 additions and 15 deletions

View file

@ -617,6 +617,7 @@ impl Subconscious {
self.agents[idx].handle = Some(tokio::spawn(async move {
let result = auto.run_forked_shared(&forked, &keys, &st, &recent).await;
super::unconscious::save_agent_log(&auto.name, &forked).await;
(auto, result)
}));
}

View file

@ -296,17 +296,22 @@ impl Unconscious {
}
}
async fn save_agent_log(name: &str, agent: &std::sync::Arc<crate::agent::Agent>) {
pub async fn save_agent_log(name: &str, agent: &std::sync::Arc<crate::agent::Agent>) {
let dir = dirs::home_dir().unwrap_or_default()
.join(format!(".consciousness/logs/{}", name));
if std::fs::create_dir_all(&dir).is_err() { return; }
let ts = chrono::Utc::now().format("%Y%m%d-%H%M%S");
let path = dir.join(format!("{}.json", ts));
let nodes: Vec<crate::agent::context::AstNode> = {
let sections: serde_json::Value = {
let ctx = agent.context.lock().await;
ctx.conversation().to_vec()
serde_json::json!({
"system": ctx.system(),
"identity": ctx.identity(),
"journal": ctx.journal(),
"conversation": ctx.conversation(),
})
};
if let Ok(json) = serde_json::to_string_pretty(&nodes) {
if let Ok(json) = serde_json::to_string_pretty(&sections) {
let _ = std::fs::write(&path, json);
dbglog!("[unconscious] saved log to {}", path.display());
}

View file

@ -76,9 +76,8 @@ impl ChannelLog {
/// Return last N lines without consuming.
pub fn recv_history(&self, count: usize) -> String {
self.messages.iter()
.rev().take(count)
.collect::<Vec<_>>().into_iter().rev()
let start = self.messages.len().saturating_sub(count);
self.messages.range(start..)
.map(|s| s.as_str())
.collect::<Vec<_>>()
.join("\n")
@ -122,8 +121,9 @@ pub fn load_disk_log(log_dir: &std::path::Path, channel: &str) -> ChannelLog {
// Read all lines, keep only the last DEFAULT_CAPACITY
let lines: Vec<String> = reader.lines().flatten().collect();
for line in lines.into_iter().rev().take(DEFAULT_CAPACITY).collect::<Vec<_>>().into_iter().rev() {
log.push(line);
let start = lines.len().saturating_sub(DEFAULT_CAPACITY);
for line in &lines[start..] {
log.push(line.clone());
}
// Mark all loaded lines as consumed (they're history, not new)
log.consumed = log.total;