Revert to tokio::sync::Mutex, fix lock-across-await bugs, move input ownership to InteractScreen
The std::sync::Mutex detour caught every place a MutexGuard lived across an await point in Agent::turn — the compiler enforced Send safety that tokio::sync::Mutex silently allows. With those fixed, switch back to tokio::sync::Mutex (std::sync blocks tokio worker threads and panics inside the runtime). Input and command dispatch now live in InteractScreen (chat.rs): - Enter pushes directly to SharedMindState.input (no app.submitted hop) - sync_from_agent displays pending input with dimmed color - Slash command table moved from event_loop.rs to chat.rs - cmd_switch_model kept as pub fn for tool-initiated switches Co-Authored-By: Proof of Concept <poc@bcachefs.org>
This commit is contained in:
parent
3e1be4d353
commit
48beb8b663
9 changed files with 404 additions and 370 deletions
|
|
@ -21,8 +21,6 @@ use anyhow::Result;
|
|||
use std::sync::Arc;
|
||||
use std::time::Instant;
|
||||
use tokio::sync::mpsc;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use crate::agent::{Agent, TurnResult};
|
||||
use crate::agent::api::ApiClient;
|
||||
use crate::config::{AppConfig, SessionConfig};
|
||||
|
|
@ -191,8 +189,8 @@ enum BgEvent {
|
|||
pub type SharedMindState = std::sync::Mutex<MindState>;
|
||||
|
||||
pub struct Mind {
|
||||
pub agent: Arc<Mutex<Agent>>,
|
||||
pub shared: SharedMindState,
|
||||
pub agent: Arc<tokio::sync::Mutex<Agent>>,
|
||||
pub shared: Arc<SharedMindState>,
|
||||
pub config: SessionConfig,
|
||||
ui_tx: ui_channel::UiSender,
|
||||
turn_tx: mpsc::Sender<(Result<TurnResult>, StreamTarget)>,
|
||||
|
|
@ -216,7 +214,7 @@ impl Mind {
|
|||
config.session_dir.join("conversation.jsonl"),
|
||||
).ok();
|
||||
|
||||
let agent = Arc::new(Mutex::new(Agent::new(
|
||||
let agent = Arc::new(tokio::sync::Mutex::new(Agent::new(
|
||||
client,
|
||||
config.system_prompt.clone(),
|
||||
config.context_parts.clone(),
|
||||
|
|
@ -227,7 +225,7 @@ impl Mind {
|
|||
shared_active_tools,
|
||||
)));
|
||||
|
||||
let shared = std::sync::Mutex::new(MindState::new(config.app.dmn.max_turns));
|
||||
let shared = Arc::new(std::sync::Mutex::new(MindState::new(config.app.dmn.max_turns)));
|
||||
let (turn_watch, _) = tokio::sync::watch::channel(false);
|
||||
let (bg_tx, bg_rx) = mpsc::unbounded_channel();
|
||||
|
||||
|
|
@ -242,7 +240,7 @@ impl Mind {
|
|||
/// Initialize — restore log, start daemons and background agents.
|
||||
pub async fn init(&self) {
|
||||
// Restore conversation
|
||||
let mut ag = self.agent.lock().unwrap();
|
||||
let mut ag = self.agent.lock().await;
|
||||
ag.restore_from_log();
|
||||
drop(ag);
|
||||
}
|
||||
|
|
@ -258,7 +256,7 @@ impl Mind {
|
|||
MindCommand::None => {}
|
||||
MindCommand::Compact => {
|
||||
let threshold = compaction_threshold(&self.config.app);
|
||||
let mut ag = self.agent.lock().unwrap();
|
||||
let mut ag = self.agent.lock().await;
|
||||
if ag.last_prompt_tokens() > threshold {
|
||||
ag.compact();
|
||||
}
|
||||
|
|
@ -273,7 +271,7 @@ impl Mind {
|
|||
}
|
||||
MindCommand::Interrupt => {
|
||||
self.shared.lock().unwrap().interrupt();
|
||||
let ag = self.agent.lock().unwrap();
|
||||
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);
|
||||
|
|
@ -290,7 +288,7 @@ impl Mind {
|
|||
let new_log = log::ConversationLog::new(
|
||||
self.config.session_dir.join("conversation.jsonl"),
|
||||
).ok();
|
||||
let mut ag = self.agent.lock().unwrap();
|
||||
let mut ag = self.agent.lock().await;
|
||||
let shared_ctx = ag.shared_context.clone();
|
||||
let shared_tools = ag.active_tools.clone();
|
||||
*ag = Agent::new(
|
||||
|
|
@ -324,7 +322,7 @@ impl Mind {
|
|||
let response_window = cfg.scoring_response_window;
|
||||
tokio::spawn(async move {
|
||||
let (context, client) = {
|
||||
let mut ag = agent.lock().unwrap();
|
||||
let mut ag = agent.lock().await;
|
||||
if ag.agent_cycles.memory_scoring_in_flight { return; }
|
||||
ag.agent_cycles.memory_scoring_in_flight = true;
|
||||
(ag.context.clone(), ag.client_clone())
|
||||
|
|
@ -333,7 +331,7 @@ impl Mind {
|
|||
&context, max_age as i64, response_window, &client, &ui_tx,
|
||||
).await;
|
||||
{
|
||||
let mut ag = agent.lock().unwrap();
|
||||
let mut ag = agent.lock().await;
|
||||
ag.agent_cycles.memory_scoring_in_flight = false;
|
||||
if let Ok(ref scores) = result { ag.agent_cycles.memory_scores = scores.clone(); }
|
||||
}
|
||||
|
|
@ -383,12 +381,12 @@ impl Mind {
|
|||
let _ = self.turn_watch.send(false);
|
||||
|
||||
if let Some(name) = model_switch {
|
||||
crate::user::event_loop::cmd_switch_model(&self.agent, &name, &self.ui_tx).await;
|
||||
crate::user::chat::cmd_switch_model(&self.agent, &name, &self.ui_tx).await;
|
||||
}
|
||||
|
||||
// Post-turn maintenance
|
||||
{
|
||||
let mut ag = self.agent.lock().unwrap();
|
||||
let mut ag = self.agent.lock().await;
|
||||
ag.age_out_images();
|
||||
ag.publish_context_state();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue