move startup orchestration from mind to event_loop

The top-level run() that creates Mind, wires channels, spawns the
Mind event loop, and starts the UI event loop is orchestration —
it belongs with the UI entry point, not in the cognitive layer.

Renamed to event_loop::start(cli). mind/mod.rs is now purely the
Mind struct: state machine, MindCommand, and the run loop.

Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
Kent Overstreet 2026-04-05 04:29:56 -04:00
parent e449cda40f
commit 57b0f94b54
3 changed files with 38 additions and 40 deletions

View file

@ -24,10 +24,9 @@ use tokio::sync::{mpsc, Mutex};
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, StreamTarget};
use crate::config::{AppConfig, SessionConfig};
use crate::subconscious::learn;
use crate::user::ui_channel::{self, StreamTarget};
/// Compaction threshold — context is rebuilt when prompt tokens exceed this.
fn compaction_threshold(app: &AppConfig) -> u32 {
@ -92,7 +91,7 @@ impl MindState {
}
/// Consume pending input, return a Turn command if ready.
pub fn take_pending_input(&mut self) -> MindCommand {
fn take_pending_input(&mut self) -> MindCommand {
if self.turn_active || self.input.is_empty() {
return MindCommand::None;
}
@ -106,7 +105,7 @@ impl MindState {
}
/// Process turn completion, return model switch name if requested.
pub fn complete_turn(&mut self, result: &Result<TurnResult>, target: StreamTarget) -> Option<String> {
fn complete_turn(&mut self, result: &Result<TurnResult>, target: StreamTarget) -> Option<String> {
self.turn_active = false;
match result {
Ok(turn_result) => {
@ -137,7 +136,7 @@ impl MindState {
}
/// DMN tick — returns a Turn action with the DMN prompt, or None.
pub fn dmn_tick(&mut self) -> MindCommand {
fn dmn_tick(&mut self) -> MindCommand {
if matches!(self.dmn, dmn::State::Paused | dmn::State::Off) {
return MindCommand::None;
}
@ -158,7 +157,7 @@ impl MindState {
MindCommand::Turn(prompt, StreamTarget::Autonomous)
}
pub fn interrupt(&mut self) {
fn interrupt(&mut self) {
self.input.clear();
self.dmn = dmn::State::Resting { since: Instant::now() };
}
@ -383,35 +382,3 @@ impl Mind {
}
}
}
// --- Startup ---
pub async fn run(cli: crate::user::CliArgs) -> Result<()> {
let (config, _figment) = config::load_session(&cli)?;
if config.app.debug {
unsafe { std::env::set_var("POC_DEBUG", "1") };
}
let (ui_tx, ui_rx) = ui_channel::channel();
let (turn_tx, turn_rx) = mpsc::channel::<(Result<TurnResult>, StreamTarget)>(1);
let (mind_tx, mind_rx) = tokio::sync::mpsc::unbounded_channel();
let mut mind = Mind::new(config, ui_tx.clone(), turn_tx);
mind.init().await;
let ui_agent = mind.agent.clone();
let shared_mind = mind.shared.clone();
let shared_context = mind.agent.lock().await.shared_context.clone();
let shared_active_tools = mind.agent.lock().await.active_tools.clone();
let turn_watch = mind.turn_watch();
tokio::spawn(async move {
mind.run(mind_rx, turn_rx).await;
});
crate::user::event_loop::run(
tui::App::new(String::new(), shared_context, shared_active_tools),
ui_agent, shared_mind, turn_watch, mind_tx, ui_tx, ui_rx,
).await
}