move hotkey handlers from Mind to event_loop

cycle_reasoning, kill_processes, and AdjustSampling only need the
Agent lock — they're pure Agent operations. Handle them directly
in the UI event loop instead of routing through Mind.

Mind now only receives Interrupt and CycleAutonomy as hotkeys,
which genuinely need Mind state (turn handles, DMN state).

mind/mod.rs: 957 → 688 lines across the session.

Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
Kent Overstreet 2026-04-05 02:44:58 -04:00
parent 64add58caa
commit 05d6bbc912
2 changed files with 58 additions and 53 deletions

View file

@ -86,6 +86,55 @@ async fn cmd_retry(
}
}
fn cmd_cycle_reasoning(agent: &Arc<Mutex<Agent>>, ui_tx: &ui_channel::UiSender) {
if let Ok(mut ag) = agent.try_lock() {
let next = match ag.reasoning_effort.as_str() {
"none" => "low",
"low" => "high",
_ => "none",
};
ag.reasoning_effort = next.to_string();
let label = match next {
"none" => "off (monologue hidden)",
"low" => "low (brief monologue)",
"high" => "high (full monologue)",
_ => next,
};
let _ = ui_tx.send(UiMessage::Info(format!("Reasoning: {} — ^R to cycle", label)));
} else {
let _ = ui_tx.send(UiMessage::Info(
"(agent busy — reasoning change takes effect next turn)".into(),
));
}
}
async fn cmd_kill_processes(agent: &Arc<Mutex<Agent>>, ui_tx: &ui_channel::UiSender) {
let active_tools = agent.lock().await.active_tools.clone();
let mut tools = active_tools.lock().unwrap();
if tools.is_empty() {
let _ = ui_tx.send(UiMessage::Info("(no running tool calls)".into()));
} else {
for entry in tools.drain(..) {
let elapsed = entry.started.elapsed();
let _ = ui_tx.send(UiMessage::Info(format!(
" killing {} ({:.0}s): {}", entry.name, elapsed.as_secs_f64(), entry.detail,
)));
entry.handle.abort();
}
}
}
fn cmd_adjust_sampling(agent: &Arc<Mutex<Agent>>, param: usize, delta: f32) {
if let Ok(mut ag) = agent.try_lock() {
match param {
0 => ag.temperature = (ag.temperature + delta).clamp(0.0, 2.0),
1 => ag.top_p = (ag.top_p + delta).clamp(0.0, 1.0),
2 => ag.top_k = (ag.top_k as f32 + delta).max(0.0) as u32,
_ => {}
}
}
}
pub async fn cmd_switch_model(
agent: &Arc<Mutex<Agent>>,
name: &str,
@ -271,10 +320,16 @@ pub async fn run(
}
}
// Send hotkey actions to Mind
// Handle hotkey actions
let actions: Vec<HotkeyAction> = app.hotkey_actions.drain(..).collect();
for action in actions {
let _ = mind_tx.send(MindMessage::Hotkey(action));
match action {
HotkeyAction::CycleReasoning => cmd_cycle_reasoning(&agent, &ui_tx),
HotkeyAction::KillProcess => cmd_kill_processes(&agent, &ui_tx).await,
HotkeyAction::Interrupt => { let _ = mind_tx.send(MindMessage::Hotkey(action)); }
HotkeyAction::CycleAutonomy => { let _ = mind_tx.send(MindMessage::Hotkey(action)); }
HotkeyAction::AdjustSampling(param, delta) => cmd_adjust_sampling(&agent, param, delta),
}
}
if app.drain_messages(&mut ui_rx) {