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
|
|
@ -14,7 +14,7 @@ pub(super) fn tools() -> [super::Tool; 3] {
|
|||
.ok_or_else(|| anyhow::anyhow!("'model' parameter is required"))?;
|
||||
if model.is_empty() { anyhow::bail!("'model' parameter cannot be empty"); }
|
||||
if let Some(agent) = agent {
|
||||
let mut a = agent.lock().unwrap();
|
||||
let mut a = agent.lock().await;
|
||||
a.pending_model_switch = Some(model.to_string());
|
||||
}
|
||||
Ok(format!("Switching to model '{}' after this turn.", model))
|
||||
|
|
@ -24,7 +24,7 @@ pub(super) fn tools() -> [super::Tool; 3] {
|
|||
parameters_json: r#"{"type":"object","properties":{}}"#,
|
||||
handler: |agent, _v| Box::pin(async move {
|
||||
if let Some(agent) = agent {
|
||||
let mut a = agent.lock().unwrap();
|
||||
let mut a = agent.lock().await;
|
||||
a.pending_yield = true;
|
||||
a.pending_dmn_pause = true;
|
||||
}
|
||||
|
|
@ -36,7 +36,7 @@ pub(super) fn tools() -> [super::Tool; 3] {
|
|||
handler: |agent, v| Box::pin(async move {
|
||||
let msg = v.get("message").and_then(|v| v.as_str()).unwrap_or("Waiting for input.");
|
||||
if let Some(agent) = agent {
|
||||
let mut a = agent.lock().unwrap();
|
||||
let mut a = agent.lock().await;
|
||||
a.pending_yield = true;
|
||||
}
|
||||
Ok(format!("Yielding. {}", msg))
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ fn default_timeout() -> u64 { 120 }
|
|||
/// Async tool handler function.
|
||||
/// Agent is None when called from contexts without an agent (MCP server, subconscious).
|
||||
pub type ToolHandler = fn(
|
||||
Option<std::sync::Arc<std::sync::Mutex<super::Agent>>>,
|
||||
Option<std::sync::Arc<tokio::sync::Mutex<super::Agent>>>,
|
||||
serde_json::Value,
|
||||
) -> Pin<Box<dyn Future<Output = anyhow::Result<String>> + Send>>;
|
||||
|
||||
|
|
@ -94,11 +94,11 @@ pub async fn dispatch(
|
|||
pub async fn dispatch_with_agent(
|
||||
name: &str,
|
||||
args: &serde_json::Value,
|
||||
agent: Option<std::sync::Arc<std::sync::Mutex<super::Agent>>>,
|
||||
agent: Option<std::sync::Arc<tokio::sync::Mutex<super::Agent>>>,
|
||||
) -> String {
|
||||
// Look up in agent's tools if available, otherwise global
|
||||
let tool = if let Some(ref a) = agent {
|
||||
let guard = a.lock().unwrap();
|
||||
let guard = a.lock().await;
|
||||
guard.tools.iter().find(|t| t.name == name).copied()
|
||||
} else {
|
||||
None
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ pub fn tool() -> super::Tool {
|
|||
parameters_json: r#"{"type":"object","properties":{"action":{"type":"string","enum":["push","pop","update","switch"],"description":"Stack operation"},"content":{"type":"string","description":"Task description (for push/update)"},"index":{"type":"integer","description":"Stack index (for switch, 0=bottom)"}},"required":["action"]}"#,
|
||||
handler: |agent, v| Box::pin(async move {
|
||||
if let Some(agent) = agent {
|
||||
let mut a = agent.lock().unwrap();
|
||||
let mut a = agent.lock().await;
|
||||
Ok(handle(&v, &mut a.context.working_stack))
|
||||
} else {
|
||||
anyhow::bail!("working_stack requires agent context")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue