From 3a8383ba37c8f214e5068568dda080510a13117e Mon Sep 17 00:00:00 2001 From: ProofOfConcept Date: Fri, 27 Mar 2026 20:41:41 -0400 Subject: [PATCH] journal: wire standalone agent into hook cycle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add journal_cycle() to memory_search.rs, triggered every 20KB of transcript growth. Runs independently of the surface-observe pipeline so it doesn't depend on the 5-step pipeline surviving bail checks. Journal agent doesn't inject output into conversation context (unlike surface and reflect) — it just writes episodic memory entries. --- src/hippocampus/memory_search.rs | 34 ++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) 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); } }