From 9d597b5effc2c2ad973b72a9f11bcc1a6c9b7b53 Mon Sep 17 00:00:00 2001 From: Kent Overstreet Date: Sun, 5 Apr 2026 04:02:16 -0400 Subject: [PATCH] =?UTF-8?q?mind:=20zero=20UI=20dependencies=20=E2=80=94=20?= =?UTF-8?q?init()=20+=20run()=20split?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mind::init() restores conversation from log and starts scoring. No UiMessages sent. The UI event loop reads Mind's state after init and displays startup info (model, restored conversation) by reading the agent directly. mind/mod.rs has zero UiMessage imports or sends. Complete separation between cognitive state machine and user interface. Co-Authored-By: Kent Overstreet --- src/mind/mod.rs | 52 +++++++++++------------------------------- src/user/event_loop.rs | 13 ++++++++++- 2 files changed, 25 insertions(+), 40 deletions(-) diff --git a/src/mind/mod.rs b/src/mind/mod.rs index c2c79ca..cb70928 100644 --- a/src/mind/mod.rs +++ b/src/mind/mod.rs @@ -26,7 +26,7 @@ use crate::agent::{Agent, TurnResult}; use crate::agent::api::ApiClient; use crate::config::{self, AppConfig, SessionConfig}; use crate::user::{self as tui}; -use crate::user::ui_channel::{self, StatusInfo, StreamTarget, UiMessage}; +use crate::user::ui_channel::{self, StreamTarget}; use crate::subconscious::learn; /// Compaction threshold — context is rebuilt when prompt tokens exceed this. @@ -192,6 +192,17 @@ impl Mind { Self { agent, shared, config, ui_tx, turn_tx, turn_handle: None, turn_watch } } + /// Initialize — restore conversation from log, start background agents. + pub async fn init(&mut self) { + let mut ag = self.agent.lock().await; + ag.restore_from_log(); + drop(ag); + + if !self.config.no_agents { + self.start_memory_scoring(); + } + } + pub fn turn_watch(&self) -> tokio::sync::watch::Receiver { self.turn_watch.subscribe() } @@ -381,23 +392,11 @@ pub async fn run(cli: crate::user::CliArgs) -> Result<()> { let shared_context = ui_channel::shared_context_state(); let shared_active_tools = ui_channel::shared_active_tools(); - // Startup info - let _ = ui_tx.send(UiMessage::Info("consciousness v0.3 (tui)".into())); - let _ = ui_tx.send(UiMessage::Info(format!( - " model: {} (available: {})", config.model, config.app.model_names().join(", "), - ))); let client = ApiClient::new(&config.api_base, &config.api_key, &config.model); - let _ = ui_tx.send(UiMessage::Info(format!(" api: {} ({})", config.api_base, client.backend_label()))); - let _ = ui_tx.send(UiMessage::Info(format!( - " context: {}K chars ({} config, {} memory files)", - config.context_parts.iter().map(|(_, c)| c.len()).sum::() / 1024, - config.config_file_count, config.memory_file_count, - ))); let conversation_log_path = config.session_dir.join("conversation.jsonl"); let conversation_log = log::ConversationLog::new(conversation_log_path.clone()) .expect("failed to create conversation log"); - let _ = ui_tx.send(UiMessage::Info(format!(" log: {}", conversation_log.path().display()))); let agent = Arc::new(Mutex::new(Agent::new( client, @@ -410,37 +409,12 @@ pub async fn run(cli: crate::user::CliArgs) -> Result<()> { shared_active_tools.clone(), ))); - // Restore conversation from log - { - let mut agent_guard = agent.lock().await; - if agent_guard.restore_from_log() { - ui_channel::replay_session_to_ui(agent_guard.entries(), &ui_tx); - let _ = ui_tx.send(UiMessage::Info("--- restored from conversation log ---".into())); - } - } - - // Send initial budget to status bar - { - let agent_guard = agent.lock().await; - let _ = ui_tx.send(UiMessage::StatusUpdate(StatusInfo { - dmn_state: "resting".to_string(), - dmn_turns: 0, dmn_max_turns: 0, - prompt_tokens: 0, completion_tokens: 0, - model: agent_guard.model().to_string(), - turn_tools: 0, - context_budget: agent_guard.budget().status_string(), - })); - } - let (turn_tx, turn_rx) = mpsc::channel::<(Result, StreamTarget)>(1); let no_agents = config.no_agents; let shared_mind = shared_mind_state(config.app.dmn.max_turns); - crate::user::event_loop::send_context_info(&config, &ui_tx); let mut mind = Mind::new(agent, shared_mind.clone(), config, ui_tx.clone(), turn_tx); - if !no_agents { - mind.start_memory_scoring(); - } + mind.init().await; // Start observation socket let socket_path = mind.config.session_dir.join("agent.sock"); diff --git a/src/user/event_loop.rs b/src/user/event_loop.rs index ccd5a36..6b592d8 100644 --- a/src/user/event_loop.rs +++ b/src/user/event_loop.rs @@ -181,7 +181,6 @@ pub async fn run( mut app: tui::App, agent: Arc>, shared_mind: crate::mind::SharedMindState, - turn_watch: tokio::sync::watch::Receiver, mind_tx: tokio::sync::mpsc::UnboundedSender, ui_tx: ui_channel::UiSender, @@ -202,6 +201,18 @@ pub async fn run( terminal.hide_cursor()?; + // Startup info + let _ = ui_tx.send(UiMessage::Info("consciousness v0.3 (tui)".into())); + { + let ag = agent.lock().await; + let _ = ui_tx.send(UiMessage::Info(format!(" model: {}", ag.model()))); + // Replay restored conversation to UI + if !ag.entries().is_empty() { + ui_channel::replay_session_to_ui(ag.entries(), &ui_tx); + let _ = ui_tx.send(UiMessage::Info("--- restored from conversation log ---".into())); + } + } + // Initial render app.drain_messages(&mut ui_rx); terminal.draw(|f| app.draw(f))?;