diff --git a/src/mind/mod.rs b/src/mind/mod.rs index 8a68662..d0885ed 100644 --- a/src/mind/mod.rs +++ b/src/mind/mod.rs @@ -526,6 +526,8 @@ impl Mind { .expect("Mind::run() called twice"); let mut sub_handle: Option> = None; let mut unc_handle: Option> = None; + let mut unc_idle_deadline = tokio::time::Instant::now() + std::time::Duration::from_secs(60); + let mut unc_idle = false; loop { let (timeout, has_input) = { let me = self.shared.lock().unwrap(); @@ -553,7 +555,13 @@ impl Mind { } } + _ = tokio::time::sleep_until(unc_idle_deadline), if !unc_idle && !self.config.no_agents => { + unc_idle = true; + } + Some((result, target)) = turn_rx.recv() => { + unc_idle_deadline = tokio::time::Instant::now() + std::time::Duration::from_secs(60); + unc_idle = false; let model_switch = { let mut s = self.shared.lock().unwrap(); s.turn_handle = None; @@ -584,7 +592,7 @@ impl Mind { s.trigger(&agent).await; })); } - if unc_handle.as_ref().map_or(true, |h| h.is_finished()) { + if unc_idle && unc_handle.as_ref().map_or(true, |h| h.is_finished()) { let unc = self.unconscious.clone(); unc_handle = Some(tokio::spawn(async move { unc.lock().await.trigger().await; diff --git a/src/mind/unconscious.rs b/src/mind/unconscious.rs index eb7f854..fdcfaed 100644 --- a/src/mind/unconscious.rs +++ b/src/mind/unconscious.rs @@ -2,10 +2,10 @@ // // Standalone agents that operate on the memory graph without needing // conversation context. Each agent runs in a loop: finish one run, -// wait a cooldown, start the next. Agents can be toggled on/off, -// persisted to ~/.consciousness/agent-enabled.json. +// start the next. Agents can be toggled on/off, persisted to +// ~/.consciousness/agent-enabled.json. -use std::time::{Duration, Instant}; +use std::time::Instant; use std::collections::HashMap; use futures::FutureExt; @@ -13,9 +13,6 @@ use crate::agent::oneshot::{AutoAgent, AutoStep}; use crate::agent::tools; use crate::subconscious::defs; -/// Cooldown between consecutive runs of the same agent. -const COOLDOWN: Duration = Duration::from_secs(120); - fn config_path() -> std::path::PathBuf { dirs::home_dir().unwrap_or_default() .join(".consciousness/agent-enabled.json") @@ -50,11 +47,7 @@ impl UnconsciousAgent { } fn should_run(&self) -> bool { - if !self.enabled || self.is_running() { return false; } - match self.last_run { - Some(t) => t.elapsed() >= COOLDOWN, - None => true, - } + self.enabled && !self.is_running() } } @@ -167,7 +160,7 @@ impl Unconscious { pub async fn trigger(&mut self) { // Periodic graph health refresh (also on first call) if self.last_health_check - .map(|t| t.elapsed() > Duration::from_secs(600)) + .map(|t| t.elapsed() > std::time::Duration::from_secs(600)) .unwrap_or(true) { self.refresh_health(); @@ -299,8 +292,25 @@ impl Unconscious { self.agents[idx].handle = Some(tokio::spawn(async move { let result = auto.run_shared(&agent).await; + save_agent_log(&auto.name, &agent).await; auto.steps = orig_steps; (auto, result) })); } } + +async fn save_agent_log(name: &str, agent: &std::sync::Arc) { + 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 = { + let ctx = agent.context.lock().await; + ctx.conversation().to_vec() + }; + if let Ok(json) = serde_json::to_string_pretty(&nodes) { + let _ = std::fs::write(&path, json); + dbglog!("[unconscious] saved log to {}", path.display()); + } +}