From e9e74580132c0f83b4beafbfeb0d0042e61948f4 Mon Sep 17 00:00:00 2001 From: Kent Overstreet Date: Sat, 11 Apr 2026 21:57:24 -0400 Subject: [PATCH] Fix agent provenance and add store activity for unconscious agents - Remove bogus "agent:" prefix from provenance - just use agent name - Add history field to UnconsciousSnapshot - Update snapshots() to fetch store activity via recent_by_provenance - Fix TUI to display store activity for both agent types Co-Authored-By: Proof of Concept --- src/mind/mod.rs | 8 ++++++- src/mind/subconscious.rs | 11 ++++------ src/mind/unconscious.rs | 27 +++++++++++++++--------- src/user/mod.rs | 7 ++++++- src/user/subconscious.rs | 45 ++++++++++++++++++++++++++++------------ 5 files changed, 66 insertions(+), 32 deletions(-) diff --git a/src/mind/mod.rs b/src/mind/mod.rs index 38e8fdf..376e241 100644 --- a/src/mind/mod.rs +++ b/src/mind/mod.rs @@ -380,7 +380,13 @@ impl Mind { } pub async fn unconscious_snapshots(&self) -> Vec { - self.unconscious.lock().await.snapshots() + let unc = self.unconscious.lock().await; + let store = crate::store::Store::cached().await.ok(); + let store_guard = match &store { + Some(s) => Some(s.lock().await), + None => None, + }; + unc.snapshots(store_guard.as_deref()) } pub async fn init(&self) { diff --git a/src/mind/subconscious.rs b/src/mind/subconscious.rs index 58f971e..ae26fe2 100644 --- a/src/mind/subconscious.rs +++ b/src/mind/subconscious.rs @@ -456,10 +456,8 @@ impl Subconscious { pub fn snapshots(&self, store: Option<&crate::store::Store>) -> Vec { self.agents.iter().map(|s| { - let history = store.map(|st| { - let prov = format!("agent:{}", s.name); - st.recent_by_provenance(&prov, 30) - }).unwrap_or_default(); + let history = store.map(|st| st.recent_by_provenance(&s.name, 30)) + .unwrap_or_default(); s.snapshot(&self.state, history) }).collect() } @@ -595,10 +593,9 @@ impl Subconscious { dbglog!("[subconscious] triggering {}", auto.name); let forked = agent.fork(auto.tools.clone()).await; - let prov = format!("agent:{}", auto.name); { let mut st = forked.state.lock().await; - st.provenance = prov.clone(); + st.provenance = auto.name.clone(); // Surface agent gets near-interactive priority; // other subconscious agents get lower priority. st.priority = Some(if auto.name == "surface" { 1 } else { 2 }); @@ -611,7 +608,7 @@ impl Subconscious { let keys = memory_keys.clone(); let st = self.state.clone(); let recent: Vec = store_guard.as_ref() - .map(|s| s.recent_by_provenance(&prov, 50) + .map(|s| s.recent_by_provenance(&auto.name, 50) .into_iter().map(|(k, _)| k).collect()) .unwrap_or_default(); diff --git a/src/mind/unconscious.rs b/src/mind/unconscious.rs index 5bd6183..7cd7d64 100644 --- a/src/mind/unconscious.rs +++ b/src/mind/unconscious.rs @@ -62,6 +62,8 @@ pub struct UnconsciousSnapshot { pub last_run_secs_ago: Option, pub agent: Option>, pub last_stats: Option, + /// Recent store activity for this agent: (key, timestamp), newest first. + pub history: Vec<(String, i64)>, } pub struct Unconscious { @@ -138,15 +140,20 @@ impl Unconscious { save_enabled_config(&map); } - pub fn snapshots(&self) -> Vec { - self.agents.iter().map(|a| UnconsciousSnapshot { - name: a.name.clone(), - running: a.is_running(), - enabled: a.enabled, - runs: a.runs, - last_run_secs_ago: a.last_run.map(|t| t.elapsed().as_secs_f64()), - agent: a.agent.clone(), - last_stats: a.last_stats.clone(), + pub fn snapshots(&self, store: Option<&crate::store::Store>) -> Vec { + self.agents.iter().map(|a| { + let history = store.map(|st| st.recent_by_provenance(&a.name, 30)) + .unwrap_or_default(); + UnconsciousSnapshot { + name: a.name.clone(), + running: a.is_running(), + enabled: a.enabled, + runs: a.runs, + last_run_secs_ago: a.last_run.map(|t| t.elapsed().as_secs_f64()), + agent: a.agent.clone(), + last_stats: a.last_stats.clone(), + history, + } }).collect() } @@ -285,7 +292,7 @@ impl Unconscious { ).await; { let mut st = agent.state.lock().await; - st.provenance = format!("unconscious:{}", auto.name); + st.provenance = auto.name.clone(); st.priority = Some(10); } diff --git a/src/user/mod.rs b/src/user/mod.rs index 34aec26..87c005a 100644 --- a/src/user/mod.rs +++ b/src/user/mod.rs @@ -404,7 +404,12 @@ async fn run( unc.toggle(name).await; } } - app.unconscious_state = unc.snapshots(); + let store = crate::store::Store::cached().await.ok(); + let store_guard = match &store { + Some(s) => Some(s.lock().await), + None => None, + }; + app.unconscious_state = unc.snapshots(store_guard.as_deref()); app.graph_health = unc.graph_health.clone(); app.mind_state = Some(mind.shared.lock().unwrap().clone()); } diff --git a/src/user/subconscious.rs b/src/user/subconscious.rs index 214adea..a15f840 100644 --- a/src/user/subconscious.rs +++ b/src/user/subconscious.rs @@ -160,6 +160,21 @@ impl SubconsciousScreen { app.unconscious_state.get(unc_idx)?.agent.clone() } + /// Get store activity history for the selected agent. + fn selected_history<'a>(&self, app: &'a App) -> &'a [(String, i64)] { + let idx = self.selected(); + let sub_count = app.agent_state.len(); + if idx < sub_count { + return app.agent_state.get(idx) + .map(|s| s.history.as_slice()) + .unwrap_or(&[]); + } + idx.checked_sub(sub_count + 1) + .and_then(|i| app.unconscious_state.get(i)) + .map(|s| s.history.as_slice()) + .unwrap_or(&[]) + } + fn output_sections(&self, app: &App) -> Vec { let snap = match app.agent_state.get(self.selected()) { Some(s) => s, @@ -306,23 +321,27 @@ impl SubconsciousScreen { let key_style = Style::default().fg(Color::Yellow); let mut lines: Vec = Vec::new(); - let mut title = "memory store activity".to_string(); - if let Some(snap) = app.agent_state.get(self.selected()) { - let short_name = snap.name.strip_prefix("subconscious-").unwrap_or(&snap.name); - title = format!("{} store activity", short_name); + let name = self.selected_agent_name(app); + let short_name = name.as_ref() + .map(|n| n.strip_prefix("subconscious-").unwrap_or(n)) + .unwrap_or("—"); + let title = format!("{} store activity", short_name); - if snap.history.is_empty() { - lines.push(Line::styled(" (no store activity)", dim)); - } else { - for (key, ts) in &snap.history { - lines.push(Line::from(vec![ - Span::styled(format!(" {:>6} ", format_ts_age(*ts)), dim), - Span::styled(key.as_str(), key_style), - ])); - } + let history = self.selected_history(app); + if history.is_empty() { + lines.push(Line::styled(" (no store activity)", dim)); + } else { + for (key, ts) in history { + lines.push(Line::from(vec![ + Span::styled(format!(" {:>6} ", format_ts_age(*ts)), dim), + Span::styled(key.as_str(), key_style), + ])); } + } + // Walked state (subconscious only) + if let Some(snap) = app.agent_state.get(self.selected()) { if let Some(walked_str) = snap.state.get("walked") { let walked: Vec<&str> = walked_str.lines() .map(|l| l.trim()).filter(|l| !l.is_empty()).collect();