Compare commits
No commits in common. "be44a3bb0da51717fe996689cbc85673bcd6f50f" and "58cec97e573da39257fb67a185fd165e47143dcc" have entirely different histories.
be44a3bb0d
...
58cec97e57
7 changed files with 69 additions and 149 deletions
|
|
@ -193,7 +193,7 @@ impl AutoAgent {
|
||||||
|
|
||||||
if next_step < self.steps.len() {
|
if next_step < self.steps.len() {
|
||||||
backend.push_node(
|
backend.push_node(
|
||||||
AstNode::system_msg(&self.steps[next_step].prompt)).await;
|
AstNode::user_msg(&self.steps[next_step].prompt)).await;
|
||||||
next_step += 1;
|
next_step += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -218,8 +218,8 @@ impl AutoAgent {
|
||||||
let text = result.text;
|
let text = result.text;
|
||||||
if text.is_empty() {
|
if text.is_empty() {
|
||||||
dbglog!("[auto] {} empty response, retrying", self.name);
|
dbglog!("[auto] {} empty response, retrying", self.name);
|
||||||
backend.push_node(AstNode::system_msg(
|
backend.push_node(AstNode::user_msg(
|
||||||
"Your previous response was empty. \
|
"[system] Your previous response was empty. \
|
||||||
Please respond with text or use a tool."
|
Please respond with text or use a tool."
|
||||||
)).await;
|
)).await;
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -234,7 +234,7 @@ impl AutoAgent {
|
||||||
}
|
}
|
||||||
self.current_phase = self.steps[next_step].phase.clone();
|
self.current_phase = self.steps[next_step].phase.clone();
|
||||||
backend.push_node(
|
backend.push_node(
|
||||||
AstNode::system_msg(&self.steps[next_step].prompt)).await;
|
AstNode::user_msg(&self.steps[next_step].prompt)).await;
|
||||||
next_step += 1;
|
next_step += 1;
|
||||||
dbglog!("[auto] {} step {}/{}",
|
dbglog!("[auto] {} step {}/{}",
|
||||||
self.name, next_step, self.steps.len());
|
self.name, next_step, self.steps.len());
|
||||||
|
|
|
||||||
|
|
@ -113,10 +113,6 @@ pub struct MindState {
|
||||||
pub last_turn_had_tools: bool,
|
pub last_turn_had_tools: bool,
|
||||||
/// Handle to the currently running turn task.
|
/// Handle to the currently running turn task.
|
||||||
pub turn_handle: Option<tokio::task::JoinHandle<()>>,
|
pub turn_handle: Option<tokio::task::JoinHandle<()>>,
|
||||||
/// Unconscious agent idle state — true when 60s timer has expired.
|
|
||||||
pub unc_idle: bool,
|
|
||||||
/// When the unconscious idle timer will fire (for UI display).
|
|
||||||
pub unc_idle_deadline: Instant,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for MindState {
|
impl Clone for MindState {
|
||||||
|
|
@ -133,8 +129,6 @@ impl Clone for MindState {
|
||||||
consecutive_errors: self.consecutive_errors,
|
consecutive_errors: self.consecutive_errors,
|
||||||
last_turn_had_tools: self.last_turn_had_tools,
|
last_turn_had_tools: self.last_turn_had_tools,
|
||||||
turn_handle: None, // Not cloned — only Mind's loop uses this
|
turn_handle: None, // Not cloned — only Mind's loop uses this
|
||||||
unc_idle: self.unc_idle,
|
|
||||||
unc_idle_deadline: self.unc_idle_deadline,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -170,8 +164,6 @@ impl MindState {
|
||||||
consecutive_errors: 0,
|
consecutive_errors: 0,
|
||||||
last_turn_had_tools: false,
|
last_turn_had_tools: false,
|
||||||
turn_handle: None,
|
turn_handle: None,
|
||||||
unc_idle: false,
|
|
||||||
unc_idle_deadline: Instant::now() + std::time::Duration::from_secs(60),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -272,9 +264,6 @@ pub struct Mind {
|
||||||
pub unconscious: Arc<tokio::sync::Mutex<Unconscious>>,
|
pub unconscious: Arc<tokio::sync::Mutex<Unconscious>>,
|
||||||
turn_tx: mpsc::Sender<(Result<TurnResult>, StreamTarget)>,
|
turn_tx: mpsc::Sender<(Result<TurnResult>, StreamTarget)>,
|
||||||
turn_watch: tokio::sync::watch::Sender<bool>,
|
turn_watch: tokio::sync::watch::Sender<bool>,
|
||||||
/// Signals conscious activity to the unconscious loop.
|
|
||||||
/// true = active, false = idle opportunity.
|
|
||||||
conscious_active: tokio::sync::watch::Sender<bool>,
|
|
||||||
bg_tx: mpsc::UnboundedSender<BgEvent>,
|
bg_tx: mpsc::UnboundedSender<BgEvent>,
|
||||||
bg_rx: std::sync::Mutex<Option<mpsc::UnboundedReceiver<BgEvent>>>,
|
bg_rx: std::sync::Mutex<Option<mpsc::UnboundedReceiver<BgEvent>>>,
|
||||||
_supervisor: crate::thalamus::supervisor::Supervisor,
|
_supervisor: crate::thalamus::supervisor::Supervisor,
|
||||||
|
|
@ -302,7 +291,6 @@ impl Mind {
|
||||||
|
|
||||||
let shared = Arc::new(std::sync::Mutex::new(MindState::new(config.app.dmn.max_turns)));
|
let shared = Arc::new(std::sync::Mutex::new(MindState::new(config.app.dmn.max_turns)));
|
||||||
let (turn_watch, _) = tokio::sync::watch::channel(false);
|
let (turn_watch, _) = tokio::sync::watch::channel(false);
|
||||||
let (conscious_active, _) = tokio::sync::watch::channel(false);
|
|
||||||
let (bg_tx, bg_rx) = mpsc::unbounded_channel();
|
let (bg_tx, bg_rx) = mpsc::unbounded_channel();
|
||||||
|
|
||||||
let mut sup = crate::thalamus::supervisor::Supervisor::new();
|
let mut sup = crate::thalamus::supervisor::Supervisor::new();
|
||||||
|
|
@ -312,53 +300,10 @@ impl Mind {
|
||||||
let subconscious = Arc::new(tokio::sync::Mutex::new(Subconscious::new()));
|
let subconscious = Arc::new(tokio::sync::Mutex::new(Subconscious::new()));
|
||||||
subconscious.lock().await.init_output_tool(subconscious.clone());
|
subconscious.lock().await.init_output_tool(subconscious.clone());
|
||||||
|
|
||||||
let unconscious = Arc::new(tokio::sync::Mutex::new(Unconscious::new()));
|
|
||||||
|
|
||||||
// Spawn the unconscious loop on its own task
|
|
||||||
if !config.no_agents {
|
|
||||||
let unc = unconscious.clone();
|
|
||||||
let shared_for_unc = shared.clone();
|
|
||||||
let mut unc_rx = conscious_active.subscribe();
|
|
||||||
tokio::spawn(async move {
|
|
||||||
const IDLE_DELAY: std::time::Duration = std::time::Duration::from_secs(60);
|
|
||||||
loop {
|
|
||||||
// Wait for conscious side to go inactive
|
|
||||||
if *unc_rx.borrow() {
|
|
||||||
if unc_rx.changed().await.is_err() { break; }
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Conscious is inactive — wait 60s before starting
|
|
||||||
let deadline = tokio::time::Instant::now() + IDLE_DELAY;
|
|
||||||
{
|
|
||||||
let mut s = shared_for_unc.lock().unwrap();
|
|
||||||
s.unc_idle = false;
|
|
||||||
s.unc_idle_deadline = Instant::now() + IDLE_DELAY;
|
|
||||||
}
|
|
||||||
let went_active = tokio::select! {
|
|
||||||
_ = tokio::time::sleep_until(deadline) => false,
|
|
||||||
r = unc_rx.changed() => r.is_ok(),
|
|
||||||
};
|
|
||||||
if went_active { continue; }
|
|
||||||
|
|
||||||
// Idle period reached — run agents until conscious goes active
|
|
||||||
{
|
|
||||||
let mut s = shared_for_unc.lock().unwrap();
|
|
||||||
s.unc_idle = true;
|
|
||||||
}
|
|
||||||
loop {
|
|
||||||
unc.lock().await.trigger().await;
|
|
||||||
// Check if conscious became active
|
|
||||||
if *unc_rx.borrow() { break; }
|
|
||||||
// Brief yield to not starve other tasks
|
|
||||||
tokio::task::yield_now().await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Self { agent, shared, config,
|
Self { agent, shared, config,
|
||||||
subconscious, unconscious,
|
subconscious,
|
||||||
turn_tx, turn_watch, conscious_active, bg_tx,
|
unconscious: Arc::new(tokio::sync::Mutex::new(Unconscious::new())),
|
||||||
|
turn_tx, turn_watch, bg_tx,
|
||||||
bg_rx: std::sync::Mutex::new(Some(bg_rx)), _supervisor: sup }
|
bg_rx: std::sync::Mutex::new(Some(bg_rx)), _supervisor: sup }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -559,7 +504,6 @@ impl Mind {
|
||||||
}
|
}
|
||||||
self.shared.lock().unwrap().turn_active = true;
|
self.shared.lock().unwrap().turn_active = true;
|
||||||
let _ = self.turn_watch.send(true);
|
let _ = self.turn_watch.send(true);
|
||||||
let _ = self.conscious_active.send(true);
|
|
||||||
let agent = self.agent.clone();
|
let agent = self.agent.clone();
|
||||||
let result_tx = self.turn_tx.clone();
|
let result_tx = self.turn_tx.clone();
|
||||||
self.shared.lock().unwrap().turn_handle = Some(tokio::spawn(async move {
|
self.shared.lock().unwrap().turn_handle = Some(tokio::spawn(async move {
|
||||||
|
|
@ -581,6 +525,7 @@ impl Mind {
|
||||||
let mut bg_rx = self.bg_rx.lock().unwrap().take()
|
let mut bg_rx = self.bg_rx.lock().unwrap().take()
|
||||||
.expect("Mind::run() called twice");
|
.expect("Mind::run() called twice");
|
||||||
let mut sub_handle: Option<tokio::task::JoinHandle<()>> = None;
|
let mut sub_handle: Option<tokio::task::JoinHandle<()>> = None;
|
||||||
|
let mut unc_handle: Option<tokio::task::JoinHandle<()>> = None;
|
||||||
loop {
|
loop {
|
||||||
let (timeout, has_input) = {
|
let (timeout, has_input) = {
|
||||||
let me = self.shared.lock().unwrap();
|
let me = self.shared.lock().unwrap();
|
||||||
|
|
@ -609,7 +554,6 @@ impl Mind {
|
||||||
}
|
}
|
||||||
|
|
||||||
Some((result, target)) = turn_rx.recv() => {
|
Some((result, target)) = turn_rx.recv() => {
|
||||||
let _ = self.conscious_active.send(false);
|
|
||||||
let model_switch = {
|
let model_switch = {
|
||||||
let mut s = self.shared.lock().unwrap();
|
let mut s = self.shared.lock().unwrap();
|
||||||
s.turn_handle = None;
|
s.turn_handle = None;
|
||||||
|
|
@ -640,6 +584,12 @@ impl Mind {
|
||||||
s.trigger(&agent).await;
|
s.trigger(&agent).await;
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
if 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;
|
||||||
|
}));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for pending user input → push to agent context and start turn
|
// Check for pending user input → push to agent context and start turn
|
||||||
|
|
|
||||||
|
|
@ -278,11 +278,11 @@ use crate::subconscious::defs;
|
||||||
|
|
||||||
/// Names and byte-interval triggers for the built-in subconscious agents.
|
/// Names and byte-interval triggers for the built-in subconscious agents.
|
||||||
const AGENTS: &[(&str, u64)] = &[
|
const AGENTS: &[(&str, u64)] = &[
|
||||||
("subconscious-surface", 0), // every new conversation content
|
("subconscious-surface", 0), // every trigger
|
||||||
("subconscious-observe", 1_000), // every ~1KB of conversation
|
("subconscious-observe", 0), // every trigger
|
||||||
("subconscious-thalamus", 0), // every new conversation content
|
("subconscious-thalamus", 0), // every trigger
|
||||||
("subconscious-journal", 10_000), // every ~10KB of conversation
|
("subconscious-journal", 20_000), // every ~20KB of conversation
|
||||||
("subconscious-reflect", 10_000), // every ~10KB of conversation
|
("subconscious-reflect", 100_000), // every ~100KB of conversation
|
||||||
];
|
];
|
||||||
|
|
||||||
/// Snapshot for the TUI — includes a handle to the forked agent
|
/// Snapshot for the TUI — includes a handle to the forked agent
|
||||||
|
|
@ -354,9 +354,7 @@ impl SubconsciousAgent {
|
||||||
|
|
||||||
fn should_trigger(&self, conversation_bytes: u64, interval: u64) -> bool {
|
fn should_trigger(&self, conversation_bytes: u64, interval: u64) -> bool {
|
||||||
if !self.auto.enabled || self.is_running() { return false; }
|
if !self.auto.enabled || self.is_running() { return false; }
|
||||||
if interval == 0 {
|
if interval == 0 { return true; }
|
||||||
return conversation_bytes > self.last_trigger_bytes;
|
|
||||||
}
|
|
||||||
conversation_bytes.saturating_sub(self.last_trigger_bytes) >= interval
|
conversation_bytes.saturating_sub(self.last_trigger_bytes) >= interval
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -558,8 +556,7 @@ impl Subconscious {
|
||||||
let ctx = agent.context.lock().await;
|
let ctx = agent.context.lock().await;
|
||||||
let bytes = ctx.conversation().iter()
|
let bytes = ctx.conversation().iter()
|
||||||
.filter(|node| !matches!(node.leaf().map(|l| l.body()),
|
.filter(|node| !matches!(node.leaf().map(|l| l.body()),
|
||||||
Some(NodeBody::Log(_)) | Some(NodeBody::Memory { .. })
|
Some(NodeBody::Log(_)) | Some(NodeBody::Memory { .. })))
|
||||||
| Some(NodeBody::Dmn(_))))
|
|
||||||
.map(|node| node.render().len() as u64)
|
.map(|node| node.render().len() as u64)
|
||||||
.sum::<u64>();
|
.sum::<u64>();
|
||||||
let keys: Vec<String> = ctx.conversation().iter().filter_map(|node| {
|
let keys: Vec<String> = ctx.conversation().iter().filter_map(|node| {
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,10 @@
|
||||||
//
|
//
|
||||||
// Standalone agents that operate on the memory graph without needing
|
// Standalone agents that operate on the memory graph without needing
|
||||||
// conversation context. Each agent runs in a loop: finish one run,
|
// conversation context. Each agent runs in a loop: finish one run,
|
||||||
// start the next. Agents can be toggled on/off, persisted to
|
// wait a cooldown, start the next. Agents can be toggled on/off,
|
||||||
// ~/.consciousness/agent-enabled.json.
|
// persisted to ~/.consciousness/agent-enabled.json.
|
||||||
|
|
||||||
use std::time::Instant;
|
use std::time::{Duration, Instant};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
|
|
||||||
|
|
@ -13,6 +13,9 @@ use crate::agent::oneshot::{AutoAgent, AutoStep};
|
||||||
use crate::agent::tools;
|
use crate::agent::tools;
|
||||||
use crate::subconscious::defs;
|
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 {
|
fn config_path() -> std::path::PathBuf {
|
||||||
dirs::home_dir().unwrap_or_default()
|
dirs::home_dir().unwrap_or_default()
|
||||||
.join(".consciousness/agent-enabled.json")
|
.join(".consciousness/agent-enabled.json")
|
||||||
|
|
@ -47,7 +50,11 @@ impl UnconsciousAgent {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn should_run(&self) -> bool {
|
fn should_run(&self) -> bool {
|
||||||
self.enabled && !self.is_running()
|
if !self.enabled || self.is_running() { return false; }
|
||||||
|
match self.last_run {
|
||||||
|
Some(t) => t.elapsed() >= COOLDOWN,
|
||||||
|
None => true,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -160,7 +167,7 @@ impl Unconscious {
|
||||||
pub async fn trigger(&mut self) {
|
pub async fn trigger(&mut self) {
|
||||||
// Periodic graph health refresh (also on first call)
|
// Periodic graph health refresh (also on first call)
|
||||||
if self.last_health_check
|
if self.last_health_check
|
||||||
.map(|t| t.elapsed() > std::time::Duration::from_secs(600))
|
.map(|t| t.elapsed() > Duration::from_secs(600))
|
||||||
.unwrap_or(true)
|
.unwrap_or(true)
|
||||||
{
|
{
|
||||||
self.refresh_health();
|
self.refresh_health();
|
||||||
|
|
@ -187,14 +194,17 @@ impl Unconscious {
|
||||||
}
|
}
|
||||||
|
|
||||||
let running = self.agents.iter().filter(|a| a.is_running()).count();
|
let running = self.agents.iter().filter(|a| a.is_running()).count();
|
||||||
for _ in running..self.max_concurrent {
|
if running >= self.max_concurrent { return; }
|
||||||
let next = self.agents.iter().enumerate()
|
let slots = self.max_concurrent - running;
|
||||||
.filter(|(_, a)| a.should_run())
|
|
||||||
.min_by_key(|(_, a)| a.last_run);
|
let ready: Vec<usize> = self.agents.iter().enumerate()
|
||||||
match next {
|
.filter(|(_, a)| a.should_run())
|
||||||
Some((idx, _)) => self.spawn_agent(idx).await,
|
.map(|(i, _)| i)
|
||||||
None => break,
|
.take(slots)
|
||||||
}
|
.collect();
|
||||||
|
|
||||||
|
for idx in ready {
|
||||||
|
self.spawn_agent(idx).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -289,25 +299,8 @@ impl Unconscious {
|
||||||
|
|
||||||
self.agents[idx].handle = Some(tokio::spawn(async move {
|
self.agents[idx].handle = Some(tokio::spawn(async move {
|
||||||
let result = auto.run_shared(&agent).await;
|
let result = auto.run_shared(&agent).await;
|
||||||
save_agent_log(&auto.name, &agent).await;
|
|
||||||
auto.steps = orig_steps;
|
auto.steps = orig_steps;
|
||||||
(auto, result)
|
(auto, result)
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn save_agent_log(name: &str, agent: &std::sync::Arc<crate::agent::Agent>) {
|
|
||||||
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<crate::agent::context::AstNode> = {
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -30,29 +30,33 @@ Do no more than 3-5 operations.
|
||||||
|
|
||||||
=== PROMPT phase:observe ===
|
=== PROMPT phase:observe ===
|
||||||
|
|
||||||
Extract knowledge from the conversation into the memory graph. You're
|
Record what happened in the conversation. You're the librarian of the
|
||||||
the librarian — your job is to build a conceptual map that's useful for
|
memory system — your job is to organize knowledge so it can be found
|
||||||
future work, not a diary of events.
|
and used later. Update existing nodes and create new nodes as needed,
|
||||||
|
adding links so you can find these memories in the future.
|
||||||
|
|
||||||
Think in terms of *concepts*, not episodes. "What did I learn from this"? Find
|
Be factual and specific. For technical work, capture: what the bug was,
|
||||||
and update the relevant concepts, and consider what concepts are important to
|
what the root cause was, what the fix was, and why. For decisions, capture
|
||||||
you - what are the things you'll want to remember and come back to? What will
|
the decision and the rationale. For corrections, capture what was wrong
|
||||||
be useful? What did I learn about? You're building up a memory graph that is a
|
and what was right. These details are what future-you needs.
|
||||||
map of the things you know about, it should be organized in terms of people,
|
|
||||||
places, things, ideas, concepts that you've discovered and are important to
|
|
||||||
you.
|
|
||||||
|
|
||||||
For technical work: capture the *understanding*, not the narrative. What's the
|
Don't editorialize or draw metaphors — just record clearly. If something
|
||||||
architecture? What are the invariants? What's the tricky part? For decisions:
|
was emotionally significant, note that it was and what the emotion was,
|
||||||
capture the principle, not just the instance. For corrections: what's the
|
but don't build a theory around it. The journal is for reflection; observe
|
||||||
general lesson?
|
is for memory.
|
||||||
|
|
||||||
Different nodes should be about different things; don't create duplicates.
|
Different nodes should be about different things; don't create duplicate
|
||||||
Here's what you've recently written — update these instead of creating new ones
|
nodes. Here's what you've recently written — update these instead of
|
||||||
if the topic overlaps: {{recently_written}}
|
creating new ones if the topic overlaps:
|
||||||
|
{{recently_written}}
|
||||||
|
|
||||||
Before creating a new node, check what you've already walked — if a node for
|
Before creating a new node, check what you've already walked — if
|
||||||
this concept exists, update it instead of creating a new one.
|
a node for this concept exists, update it instead of creating a new one.
|
||||||
|
|
||||||
Focus on the recent conversation; you run frequently, so most of it should
|
Some things worth remembering: technical insights and root causes, work
|
||||||
already be covered.
|
practices and why they work, decisions with rationale, corrections
|
||||||
|
("I thought X but actually Y"), relationship dynamics, things you notice
|
||||||
|
about yourself and other people.
|
||||||
|
|
||||||
|
Focus on the recent stuff; you wake up and run frequently, so most of the
|
||||||
|
conversation should be things you've already seen before and added.
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,6 @@ struct App {
|
||||||
context_info: Option<ContextInfo>,
|
context_info: Option<ContextInfo>,
|
||||||
agent_state: Vec<crate::mind::SubconsciousSnapshot>,
|
agent_state: Vec<crate::mind::SubconsciousSnapshot>,
|
||||||
unconscious_state: Vec<crate::mind::UnconsciousSnapshot>,
|
unconscious_state: Vec<crate::mind::UnconsciousSnapshot>,
|
||||||
mind_state: Option<crate::mind::MindState>,
|
|
||||||
graph_health: Option<crate::subconscious::daemon::GraphHealth>,
|
graph_health: Option<crate::subconscious::daemon::GraphHealth>,
|
||||||
/// Agent toggle requests from UI — consumed by mind loop.
|
/// Agent toggle requests from UI — consumed by mind loop.
|
||||||
pub agent_toggles: Vec<String>,
|
pub agent_toggles: Vec<String>,
|
||||||
|
|
@ -138,7 +137,6 @@ impl App {
|
||||||
context_info: None,
|
context_info: None,
|
||||||
agent_state: Vec::new(),
|
agent_state: Vec::new(),
|
||||||
unconscious_state: Vec::new(),
|
unconscious_state: Vec::new(),
|
||||||
mind_state: None,
|
|
||||||
graph_health: None,
|
graph_health: None,
|
||||||
agent_toggles: Vec::new(),
|
agent_toggles: Vec::new(),
|
||||||
walked_count: 0,
|
walked_count: 0,
|
||||||
|
|
@ -405,7 +403,6 @@ async fn run(
|
||||||
}
|
}
|
||||||
app.unconscious_state = unc.snapshots();
|
app.unconscious_state = unc.snapshots();
|
||||||
app.graph_health = unc.graph_health.clone();
|
app.graph_health = unc.graph_health.clone();
|
||||||
app.mind_state = Some(mind.shared.lock().unwrap().clone());
|
|
||||||
}
|
}
|
||||||
app.walked_count = mind.subconscious_walked().await.len();
|
app.walked_count = mind.subconscious_walked().await.len();
|
||||||
if !startup_done {
|
if !startup_done {
|
||||||
|
|
|
||||||
|
|
@ -100,27 +100,6 @@ impl ScreenView for ThalamusScreen {
|
||||||
}
|
}
|
||||||
lines.push(Line::raw(""));
|
lines.push(Line::raw(""));
|
||||||
|
|
||||||
// Mind state
|
|
||||||
lines.push(Line::styled("── Mind ──", section));
|
|
||||||
lines.push(Line::raw(""));
|
|
||||||
if let Some(ref ms) = app.mind_state {
|
|
||||||
lines.push(Line::raw(format!(" DMN: {} (turn {}/{})",
|
|
||||||
ms.dmn.label(), ms.dmn_turns, ms.max_dmn_turns)));
|
|
||||||
lines.push(Line::raw(format!(" Turn active: {}", ms.turn_active)));
|
|
||||||
lines.push(Line::raw(format!(" Scoring: {}", ms.scoring_in_flight)));
|
|
||||||
let unc_status = if ms.unc_idle {
|
|
||||||
"idle (agents running)".to_string()
|
|
||||||
} else {
|
|
||||||
let remaining = ms.unc_idle_deadline
|
|
||||||
.saturating_duration_since(std::time::Instant::now());
|
|
||||||
format!("{:.0}s until idle", remaining.as_secs_f64())
|
|
||||||
};
|
|
||||||
lines.push(Line::raw(format!(" Unconscious: {}", unc_status)));
|
|
||||||
} else {
|
|
||||||
lines.push(Line::styled(" not initialized", dim));
|
|
||||||
}
|
|
||||||
lines.push(Line::raw(""));
|
|
||||||
|
|
||||||
// Channel status
|
// Channel status
|
||||||
lines.push(Line::styled("── Channels ──", section));
|
lines.push(Line::styled("── Channels ──", section));
|
||||||
lines.push(Line::raw(""));
|
lines.push(Line::raw(""));
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue