Add mind/unconscious.rs: standalone graph maintenance agents

Unconscious agents (organize, linker, distill, etc.) run independently
of the conversation context. They create fresh Agent instances, select
target nodes via their .agent file queries, and are scheduled by the
consolidation plan which analyzes graph health metrics.

Key differences from subconscious agents:
- No fork — standalone agents with fresh context
- Self-selecting — queries in .agent files pick target nodes
- Budget-driven — consolidation plan allocates runs per type
- Max 2 concurrent, 60s min interval between same-type runs

Wired into Mind event loop alongside subconscious trigger/collect.
TUI display not yet implemented.

Co-Authored-By: Proof of Concept <poc@bcachefs.org>
This commit is contained in:
ProofOfConcept 2026-04-08 23:39:48 -04:00
parent 9704e7a698
commit 0314619579
2 changed files with 255 additions and 1 deletions

View file

@ -5,6 +5,7 @@
// user interface (TUI, CLI) and the agent execution (tools, API).
pub mod subconscious;
pub mod unconscious;
pub mod identity;
pub mod log;
@ -27,6 +28,7 @@ use crate::config::{AppConfig, SessionConfig};
use crate::subconscious::learn;
pub use subconscious::{SubconsciousSnapshot, Subconscious};
pub use unconscious::{UnconsciousSnapshot, Unconscious};
use crate::agent::context::{AstNode, NodeBody, Section, Ast, ContextState};
@ -252,6 +254,7 @@ pub struct Mind {
pub shared: Arc<SharedMindState>,
pub config: SessionConfig,
subconscious: Arc<tokio::sync::Mutex<Subconscious>>,
unconscious: tokio::sync::Mutex<Unconscious>,
turn_tx: mpsc::Sender<(Result<TurnResult>, StreamTarget)>,
turn_watch: tokio::sync::watch::Sender<bool>,
bg_tx: mpsc::UnboundedSender<BgEvent>,
@ -291,7 +294,9 @@ impl Mind {
subconscious.lock().await.init_output_tool(subconscious.clone());
Self { agent, shared, config,
subconscious, turn_tx, turn_watch, bg_tx,
subconscious,
unconscious: tokio::sync::Mutex::new(Unconscious::new()),
turn_tx, turn_watch, bg_tx,
bg_rx: std::sync::Mutex::new(Some(bg_rx)), _supervisor: sup }
}
@ -311,6 +316,10 @@ impl Mind {
self.subconscious.lock().await.walked()
}
pub async fn unconscious_snapshots(&self) -> Vec<UnconsciousSnapshot> {
self.unconscious.lock().await.snapshots()
}
pub async fn init(&self) {
// Restore conversation
self.agent.restore_from_log().await;
@ -523,6 +532,11 @@ impl Mind {
let mut sub = self.subconscious.lock().await;
sub.collect_results(&self.agent).await;
sub.trigger(&self.agent).await;
drop(sub);
let mut unc = self.unconscious.lock().await;
unc.collect_results().await;
unc.trigger();
}
// Check for pending user input → push to agent context and start turn