diff --git a/src/hippocampus/memory_search.rs b/src/hippocampus/memory_search.rs index 672556d..c73c855 100644 --- a/src/hippocampus/memory_search.rs +++ b/src/hippocampus/memory_search.rs @@ -287,6 +287,39 @@ fn reflection_cycle(session: &Session, out: &mut String, log_f: &mut File) { let _ = writeln!(log_f, "reflect: spawned {:?}", pid); } +/// Run the journal agent on its own cadence — every 20KB of transcript. +/// Standalone agent that captures episodic memory independently of the +/// surface-observe pipeline. +fn journal_cycle(session: &Session, log_f: &mut File) { + let state_dir = crate::store::memory_dir() + .join("agent-output") + .join("journal"); + fs::create_dir_all(&state_dir).ok(); + + let offset_path = state_dir.join("transcript-offset"); + let transcript = session.transcript(); + + let last_offset: u64 = fs::read_to_string(&offset_path).ok() + .and_then(|s| s.trim().parse().ok()) + .unwrap_or(0); + + const JOURNAL_INTERVAL: u64 = 20_000; + if transcript.size.saturating_sub(last_offset) < JOURNAL_INTERVAL { + return; + } + + let live = crate::agents::knowledge::scan_pid_files(&state_dir, 300); + if !live.is_empty() { + let _ = writeln!(log_f, "journal: already running {:?}", live); + return; + } + + fs::write(&offset_path, transcript.size.to_string()).ok(); + let pid = crate::agents::knowledge::spawn_agent( + "journal", &state_dir, &session.session_id); + let _ = writeln!(log_f, "journal: spawned {:?}", pid); +} + fn cleanup_stale_files(dir: &Path, max_age: Duration) { let entries = match fs::read_dir(dir) { Ok(e) => e, @@ -367,6 +400,7 @@ fn hook(session: &Session) -> String { if cfg.surface_hooks.iter().any(|h| h == &session.hook_event) { surface_observe_cycle(session, &mut out, &mut log_f); reflection_cycle(session, &mut out, &mut log_f); + journal_cycle(session, &mut log_f); } }