mind: move DMN commands to event_loop, remove MindMessage variants

/dmn, /sleep, /wake, /pause now lock MindState directly from the
UI event loop. No MindMessage roundtrip needed — they're just
state transitions + info display.

MindMessage reduced to: Hotkey (Interrupt, CycleAutonomy),
NewSession, Score. Everything else handled directly by UI.

Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
Kent Overstreet 2026-04-05 03:08:36 -04:00
parent 7adc333219
commit 4eb0c891c4
2 changed files with 60 additions and 77 deletions

View file

@ -347,76 +347,51 @@ impl Mind {
biased;
Some(msg) = input_rx.recv() => {
{
let mut s = self.shared.lock().unwrap();
match msg {
MindMessage::Hotkey(HotkeyAction::CycleAutonomy) => {
let label = s.cycle_autonomy();
let _ = self.ui_tx.send(UiMessage::Info(
format!("DMN → {} (Ctrl+P to cycle)", label),
));
}
MindMessage::NewSession => {
s.dmn_sleep();
let _ = self.ui_tx.send(UiMessage::Info("New session started.".into()));
}
MindMessage::Score => {
if !s.scoring_in_flight {
s.scoring_in_flight = true;
} else {
let _ = self.ui_tx.send(UiMessage::Info("(scoring already in progress)".into()));
}
}
MindMessage::DmnQuery => {
let _ = self.ui_tx.send(UiMessage::Info(format!("DMN: {:?} ({}/{})", s.dmn, s.dmn_turns, s.max_dmn_turns)));
}
MindMessage::DmnSleep => {
s.dmn_sleep();
let _ = self.ui_tx.send(UiMessage::Info("DMN sleeping.".into()));
}
MindMessage::DmnWake => {
s.dmn_wake();
let _ = self.ui_tx.send(UiMessage::Info("DMN foraging.".into()));
}
MindMessage::DmnPause => {
s.dmn_pause();
let _ = self.ui_tx.send(UiMessage::Info("DMN paused.".into()));
}
MindMessage::Hotkey(HotkeyAction::Interrupt) => {
s.interrupt();
}
_ => {}
match msg {
MindMessage::Hotkey(HotkeyAction::CycleAutonomy) => {
let label = self.shared.lock().unwrap().cycle_autonomy();
let _ = self.ui_tx.send(UiMessage::Info(
format!("DMN → {} (Ctrl+P to cycle)", label),
));
}
}
// Handle interrupt — kill processes and abort turn
if matches!(msg, MindMessage::Hotkey(HotkeyAction::Interrupt)) {
let ag = self.agent.lock().await;
let mut tools = ag.active_tools.lock().unwrap();
for entry in tools.drain(..) { entry.handle.abort(); }
drop(tools); drop(ag);
if let Some(h) = self.turn_handle.take() { h.abort(); }
self.shared.lock().unwrap().turn_active = false;
let _ = self.turn_watch.send(false);
let _ = self.ui_tx.send(UiMessage::Info("(interrupted)".into()));
}
// Handle /new — reset agent
if matches!(msg, MindMessage::NewSession) {
let new_log = log::ConversationLog::new(
self.config.session_dir.join("conversation.jsonl"),
).ok();
let mut ag = self.agent.lock().await;
let shared_ctx = ag.shared_context.clone();
let shared_tools = ag.active_tools.clone();
*ag = Agent::new(
ApiClient::new(&self.config.api_base, &self.config.api_key, &self.config.model),
self.config.system_prompt.clone(), self.config.context_parts.clone(),
self.config.app.clone(), self.config.prompt_file.clone(),
new_log, shared_ctx, shared_tools,
);
}
// Handle /score — spawn scoring
if matches!(msg, MindMessage::Score) && self.shared.lock().unwrap().scoring_in_flight {
self.start_memory_scoring();
MindMessage::Hotkey(HotkeyAction::Interrupt) => {
self.shared.lock().unwrap().interrupt();
let ag = self.agent.lock().await;
let mut tools = ag.active_tools.lock().unwrap();
for entry in tools.drain(..) { entry.handle.abort(); }
drop(tools); drop(ag);
if let Some(h) = self.turn_handle.take() { h.abort(); }
self.shared.lock().unwrap().turn_active = false;
let _ = self.turn_watch.send(false);
let _ = self.ui_tx.send(UiMessage::Info("(interrupted)".into()));
}
MindMessage::NewSession => {
self.shared.lock().unwrap().dmn_sleep();
let new_log = log::ConversationLog::new(
self.config.session_dir.join("conversation.jsonl"),
).ok();
let mut ag = self.agent.lock().await;
let shared_ctx = ag.shared_context.clone();
let shared_tools = ag.active_tools.clone();
*ag = Agent::new(
ApiClient::new(&self.config.api_base, &self.config.api_key, &self.config.model),
self.config.system_prompt.clone(), self.config.context_parts.clone(),
self.config.app.clone(), self.config.prompt_file.clone(),
new_log, shared_ctx, shared_tools,
);
let _ = self.ui_tx.send(UiMessage::Info("New session started.".into()));
}
MindMessage::Score => {
let mut s = self.shared.lock().unwrap();
if !s.scoring_in_flight {
s.scoring_in_flight = true;
drop(s);
self.start_memory_scoring();
} else {
let _ = self.ui_tx.send(UiMessage::Info("(scoring already in progress)".into()));
}
}
_ => {}
}
// Check for pending input
let action = self.shared.lock().unwrap().take_pending_input();

View file

@ -20,10 +20,6 @@ use crate::user::ui_channel::{self, UiMessage};
/// Messages from the UI to the Mind.
pub enum MindMessage {
Hotkey(HotkeyAction),
DmnSleep,
DmnWake,
DmnPause,
DmnQuery,
NewSession,
Score,
}
@ -289,10 +285,22 @@ pub async fn run(
}
}
"/new" | "/clear" => { let _ = mind_tx.send(MindMessage::NewSession); }
"/dmn" => { let _ = mind_tx.send(MindMessage::DmnQuery); }
"/sleep" => { let _ = mind_tx.send(MindMessage::DmnSleep); }
"/wake" => { let _ = mind_tx.send(MindMessage::DmnWake); }
"/pause" => { let _ = mind_tx.send(MindMessage::DmnPause); }
"/dmn" => {
let s = shared_mind.lock().unwrap();
let _ = ui_tx.send(UiMessage::Info(format!("DMN: {:?} ({}/{})", s.dmn, s.dmn_turns, s.max_dmn_turns)));
}
"/sleep" => {
shared_mind.lock().unwrap().dmn_sleep();
let _ = ui_tx.send(UiMessage::Info("DMN sleeping.".into()));
}
"/wake" => {
shared_mind.lock().unwrap().dmn_wake();
let _ = ui_tx.send(UiMessage::Info("DMN foraging.".into()));
}
"/pause" => {
shared_mind.lock().unwrap().dmn_pause();
let _ = ui_tx.send(UiMessage::Info("DMN paused.".into()));
}
"/score" => { let _ = mind_tx.send(MindMessage::Score); }
"/retry" => {
let agent = agent.clone();