agent: switch from tokio::sync::Mutex to std::sync::Mutex

The agent lock is never held across await points — turns lock briefly,
do work, drop, then do async API calls. std::sync::Mutex works and
can be locked from sync contexts (screen tick inside terminal.draw).

Fixes: blocking_lock() panic when called inside tokio runtime.

Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
Kent Overstreet 2026-04-05 19:56:56 -04:00
parent f29b4be09c
commit 3e1be4d353
8 changed files with 41 additions and 39 deletions

View file

@ -20,7 +20,8 @@ pub mod log;
use anyhow::Result;
use std::sync::Arc;
use std::time::Instant;
use tokio::sync::{mpsc, Mutex};
use tokio::sync::mpsc;
use std::sync::Mutex;
use crate::agent::{Agent, TurnResult};
use crate::agent::api::ApiClient;
@ -241,7 +242,7 @@ impl Mind {
/// Initialize — restore log, start daemons and background agents.
pub async fn init(&self) {
// Restore conversation
let mut ag = self.agent.lock().await;
let mut ag = self.agent.lock().unwrap();
ag.restore_from_log();
drop(ag);
}
@ -257,7 +258,7 @@ impl Mind {
MindCommand::None => {}
MindCommand::Compact => {
let threshold = compaction_threshold(&self.config.app);
let mut ag = self.agent.lock().await;
let mut ag = self.agent.lock().unwrap();
if ag.last_prompt_tokens() > threshold {
ag.compact();
}
@ -272,7 +273,7 @@ impl Mind {
}
MindCommand::Interrupt => {
self.shared.lock().unwrap().interrupt();
let ag = self.agent.lock().await;
let ag = self.agent.lock().unwrap();
let mut tools = ag.active_tools.lock().unwrap();
for entry in tools.drain(..) { entry.handle.abort(); }
drop(tools); drop(ag);
@ -289,7 +290,7 @@ impl Mind {
let new_log = log::ConversationLog::new(
self.config.session_dir.join("conversation.jsonl"),
).ok();
let mut ag = self.agent.lock().await;
let mut ag = self.agent.lock().unwrap();
let shared_ctx = ag.shared_context.clone();
let shared_tools = ag.active_tools.clone();
*ag = Agent::new(
@ -323,7 +324,7 @@ impl Mind {
let response_window = cfg.scoring_response_window;
tokio::spawn(async move {
let (context, client) = {
let mut ag = agent.lock().await;
let mut ag = agent.lock().unwrap();
if ag.agent_cycles.memory_scoring_in_flight { return; }
ag.agent_cycles.memory_scoring_in_flight = true;
(ag.context.clone(), ag.client_clone())
@ -332,7 +333,7 @@ impl Mind {
&context, max_age as i64, response_window, &client, &ui_tx,
).await;
{
let mut ag = agent.lock().await;
let mut ag = agent.lock().unwrap();
ag.agent_cycles.memory_scoring_in_flight = false;
if let Ok(ref scores) = result { ag.agent_cycles.memory_scores = scores.clone(); }
}
@ -387,7 +388,7 @@ impl Mind {
// Post-turn maintenance
{
let mut ag = self.agent.lock().await;
let mut ag = self.agent.lock().unwrap();
ag.age_out_images();
ag.publish_context_state();
}