mind: move all slash commands to event_loop dispatch
All slash command routing now lives in user/event_loop.rs. Mind receives typed messages (NewSession, Score, DmnSleep, etc.) and handles them as named methods. No more handle_command() dispatch table or Command enum. Commands that only need Agent state (/model, /retry) run directly in the UI task. Commands that need Mind state (/new, /score, /dmn, /sleep, /wake, /pause) send a MindMessage. Mind is now purely: turn lifecycle, DMN state machine, and the named handlers for each message type. Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
parent
b05c956ab8
commit
64add58caa
2 changed files with 98 additions and 108 deletions
|
|
@ -35,11 +35,6 @@ fn compaction_threshold(app: &AppConfig) -> u32 {
|
|||
(crate::agent::context::context_window() as u32) * app.compaction.hard_threshold_pct / 100
|
||||
}
|
||||
|
||||
/// Result of slash command handling.
|
||||
pub enum Command {
|
||||
Handled,
|
||||
None,
|
||||
}
|
||||
|
||||
// --- Mind: all mutable state for a running agent session ---
|
||||
|
||||
|
|
@ -345,15 +340,7 @@ impl Mind {
|
|||
self.spawn_turn(prompt, StreamTarget::Autonomous);
|
||||
}
|
||||
|
||||
/// Handle slash commands. Returns how the main loop should respond.
|
||||
async fn handle_command(&mut self, input: &str) -> Command {
|
||||
match input {
|
||||
"/new" | "/clear" => {
|
||||
if self.turn_in_progress {
|
||||
let _ = self.ui_tx.send(UiMessage::Info("(turn in progress, please wait)".into()));
|
||||
return Command::Handled;
|
||||
}
|
||||
{
|
||||
async fn cmd_new(&mut self) {
|
||||
let new_log = log::ConversationLog::new(
|
||||
self.config.session_dir.join("conversation.jsonl"),
|
||||
).ok();
|
||||
|
|
@ -370,56 +357,50 @@ impl Mind {
|
|||
shared_ctx,
|
||||
shared_tools,
|
||||
);
|
||||
}
|
||||
drop(agent_guard);
|
||||
self.dmn = dmn::State::Resting { since: Instant::now() };
|
||||
let _ = self.ui_tx.send(UiMessage::Info("New session started.".into()));
|
||||
Command::Handled
|
||||
}
|
||||
"/score" => {
|
||||
|
||||
fn cmd_score(&mut self) {
|
||||
if self.scoring_in_flight {
|
||||
let _ = self.ui_tx.send(UiMessage::Info("(scoring already in progress)".into()));
|
||||
return Command::Handled;
|
||||
return;
|
||||
}
|
||||
let (context, client) = {
|
||||
let agent = self.agent.lock().await;
|
||||
(agent.context.clone(), agent.client_clone())
|
||||
};
|
||||
self.scoring_in_flight = true;
|
||||
let agent = self.agent.clone();
|
||||
let ui_tx = self.ui_tx.clone();
|
||||
self.scoring_in_flight = true;
|
||||
tokio::spawn(async move {
|
||||
let result = learn::score_memories(
|
||||
&context, &client, &ui_tx,
|
||||
).await;
|
||||
let agent = agent.lock().await;
|
||||
let (context, client) = {
|
||||
let ag = agent.lock().await;
|
||||
(ag.context.clone(), ag.client_clone())
|
||||
};
|
||||
let result = learn::score_memories(&context, &client, &ui_tx).await;
|
||||
let ag = agent.lock().await;
|
||||
match result {
|
||||
Ok(scores) => {
|
||||
agent.publish_context_state_with_scores(Some(&scores));
|
||||
}
|
||||
Err(e) => {
|
||||
let _ = ui_tx.send(UiMessage::Info(format!("[scoring failed: {:#}]", e)));
|
||||
}
|
||||
Ok(scores) => ag.publish_context_state_with_scores(Some(&scores)),
|
||||
Err(e) => { let _ = ui_tx.send(UiMessage::Info(format!("[scoring failed: {:#}]", e))); }
|
||||
}
|
||||
});
|
||||
Command::Handled
|
||||
}
|
||||
"/dmn" => {
|
||||
|
||||
fn cmd_dmn_query(&self) {
|
||||
let _ = self.ui_tx.send(UiMessage::Info(format!("DMN state: {:?}", self.dmn)));
|
||||
let _ = self.ui_tx.send(UiMessage::Info(format!("Next tick in: {:?}", self.dmn.interval())));
|
||||
let _ = self.ui_tx.send(UiMessage::Info(format!(
|
||||
"Consecutive DMN turns: {}/{}", self.dmn_turns, self.max_dmn_turns,
|
||||
)));
|
||||
Command::Handled
|
||||
}
|
||||
"/sleep" => {
|
||||
|
||||
fn cmd_dmn_sleep(&mut self) {
|
||||
self.dmn = dmn::State::Resting { since: Instant::now() };
|
||||
self.dmn_turns = 0;
|
||||
let _ = self.ui_tx.send(UiMessage::Info(
|
||||
"DMN sleeping (heartbeat every 5 min). Type anything to wake.".into(),
|
||||
));
|
||||
Command::Handled
|
||||
}
|
||||
"/wake" => {
|
||||
|
||||
fn cmd_dmn_wake(&mut self) {
|
||||
let was_paused = matches!(self.dmn, dmn::State::Paused | dmn::State::Off);
|
||||
if matches!(self.dmn, dmn::State::Off) {
|
||||
dmn::set_off(false);
|
||||
|
|
@ -430,19 +411,15 @@ impl Mind {
|
|||
else { "DMN waking — entering foraging mode." };
|
||||
let _ = self.ui_tx.send(UiMessage::Info(msg.into()));
|
||||
self.update_status();
|
||||
Command::Handled
|
||||
}
|
||||
"/pause" => {
|
||||
|
||||
fn cmd_dmn_pause(&mut self) {
|
||||
self.dmn = dmn::State::Paused;
|
||||
self.dmn_turns = 0;
|
||||
let _ = self.ui_tx.send(UiMessage::Info(
|
||||
"DMN paused — no autonomous ticks. Ctrl+P or /wake to resume.".into(),
|
||||
));
|
||||
self.update_status();
|
||||
Command::Handled
|
||||
}
|
||||
_ => Command::None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Interrupt: kill processes, abort current turn, clear pending queue.
|
||||
|
|
@ -598,12 +575,7 @@ impl Mind {
|
|||
|
||||
Some(msg) = input_rx.recv() => {
|
||||
match msg {
|
||||
MindMessage::UserInput(input) => {
|
||||
match self.handle_command(&input).await {
|
||||
Command::Handled => {}
|
||||
Command::None => self.submit_input(input),
|
||||
}
|
||||
}
|
||||
MindMessage::UserInput(input) => self.submit_input(input),
|
||||
MindMessage::Hotkey(action) => {
|
||||
match action {
|
||||
HotkeyAction::CycleReasoning => self.cycle_reasoning(),
|
||||
|
|
@ -622,6 +594,12 @@ impl Mind {
|
|||
}
|
||||
}
|
||||
}
|
||||
MindMessage::NewSession => self.cmd_new().await,
|
||||
MindMessage::Score => self.cmd_score(),
|
||||
MindMessage::DmnQuery => self.cmd_dmn_query(),
|
||||
MindMessage::DmnSleep => self.cmd_dmn_sleep(),
|
||||
MindMessage::DmnWake => self.cmd_dmn_wake(),
|
||||
MindMessage::DmnPause => self.cmd_dmn_pause(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,12 @@ use crate::user::ui_channel::{self, UiMessage};
|
|||
pub enum MindMessage {
|
||||
UserInput(String),
|
||||
Hotkey(HotkeyAction),
|
||||
DmnSleep,
|
||||
DmnWake,
|
||||
DmnPause,
|
||||
DmnQuery,
|
||||
NewSession,
|
||||
Score,
|
||||
}
|
||||
|
||||
fn send_help(ui_tx: &ui_channel::UiSender) {
|
||||
|
|
@ -232,6 +238,12 @@ pub async fn run(
|
|||
let _ = ui_tx.send(UiMessage::Info("(busy)".into()));
|
||||
}
|
||||
}
|
||||
"/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); }
|
||||
"/score" => { let _ = mind_tx.send(MindMessage::Score); }
|
||||
"/retry" => {
|
||||
let agent = agent.clone();
|
||||
let ui_tx = ui_tx.clone();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue