forked from kent/consciousness
replace try_lock() with lock_blocking() across UI thread
Add lock_blocking() to TrackedMutex: blocks current thread using block_in_place + futures::executor::block_on, safe for sync contexts. Replace all try_lock() calls with lock_blocking() in slash commands, UI rendering, and status reads. Lock hold times are fast enough that blocking briefly is fine, and this eliminates the spurious 'lock unavailable' paths that were never actually hit. Kept rx_mutex.try_lock() in mod.rs (std::sync::Mutex for stderr rx).
This commit is contained in:
parent
5210f7dd66
commit
4225294d16
28 changed files with 4199 additions and 67 deletions
|
|
@ -34,12 +34,12 @@ fn commands() -> Vec<SlashCommand> { vec![
|
|||
handler: |s, _| { let _ = s.mind_tx.send(MindCommand::NewSession); } },
|
||||
SlashCommand { name: "/save", help: "Save session to disk",
|
||||
handler: |s, _| {
|
||||
if let Ok(mut ag) = s.agent.state.try_lock() { ag.notify("saved"); }
|
||||
{ let mut ag = s.agent.state.lock_blocking(); ag.notify("saved"); }
|
||||
} },
|
||||
SlashCommand { name: "/model", help: "Show/switch model (/model <name>)",
|
||||
handler: |s, arg| {
|
||||
if arg.is_empty() {
|
||||
if let Ok(mut ag) = s.agent.state.try_lock() {
|
||||
{ let mut ag = s.agent.state.lock_blocking();
|
||||
let names = s.agent.app_config.model_names();
|
||||
let label = if names.is_empty() {
|
||||
format!("model: {}", s.agent.model())
|
||||
|
|
@ -62,7 +62,7 @@ fn commands() -> Vec<SlashCommand> { vec![
|
|||
SlashCommand { name: "/dmn", help: "Show DMN state",
|
||||
handler: |s, _| {
|
||||
let st = s.shared_mind.lock().unwrap();
|
||||
if let Ok(mut ag) = s.agent.state.try_lock() {
|
||||
{ let mut ag = s.agent.state.lock_blocking();
|
||||
ag.notify(format!("DMN: {:?} ({}/{})", st.dmn, st.dmn_turns, st.max_dmn_turns));
|
||||
}
|
||||
} },
|
||||
|
|
@ -71,7 +71,7 @@ fn commands() -> Vec<SlashCommand> { vec![
|
|||
let mut st = s.shared_mind.lock().unwrap();
|
||||
st.dmn = crate::mind::subconscious::State::Resting { since: std::time::Instant::now() };
|
||||
st.dmn_turns = 0;
|
||||
if let Ok(mut ag) = s.agent.state.try_lock() { ag.notify("DMN sleeping"); }
|
||||
{ let mut ag = s.agent.state.lock_blocking(); ag.notify("DMN sleeping"); }
|
||||
} },
|
||||
SlashCommand { name: "/wake", help: "Wake DMN to foraging",
|
||||
handler: |s, _| {
|
||||
|
|
@ -79,14 +79,14 @@ fn commands() -> Vec<SlashCommand> { vec![
|
|||
if matches!(st.dmn, crate::mind::subconscious::State::Off) { crate::mind::subconscious::set_off(false); }
|
||||
st.dmn = crate::mind::subconscious::State::Foraging;
|
||||
st.dmn_turns = 0;
|
||||
if let Ok(mut ag) = s.agent.state.try_lock() { ag.notify("DMN foraging"); }
|
||||
{ let mut ag = s.agent.state.lock_blocking(); ag.notify("DMN foraging"); }
|
||||
} },
|
||||
SlashCommand { name: "/pause", help: "Full stop — no autonomous ticks (Ctrl+P)",
|
||||
handler: |s, _| {
|
||||
let mut st = s.shared_mind.lock().unwrap();
|
||||
st.dmn = crate::mind::subconscious::State::Paused;
|
||||
st.dmn_turns = 0;
|
||||
if let Ok(mut ag) = s.agent.state.try_lock() { ag.notify("DMN paused"); }
|
||||
{ let mut ag = s.agent.state.lock_blocking(); ag.notify("DMN paused"); }
|
||||
} },
|
||||
SlashCommand { name: "/help", help: "Show this help",
|
||||
handler: |s, _| { notify_help(&s.agent); } },
|
||||
|
|
@ -116,7 +116,7 @@ pub async fn cmd_switch_model(
|
|||
}
|
||||
|
||||
fn notify_help(agent: &std::sync::Arc<crate::agent::Agent>) {
|
||||
if let Ok(mut ag) = agent.state.try_lock() {
|
||||
{ let mut ag = agent.state.lock_blocking();
|
||||
let mut help = String::new();
|
||||
for cmd in &commands() {
|
||||
help.push_str(&format!("{:12} {}\n", cmd.name, cmd.help));
|
||||
|
|
@ -581,16 +581,10 @@ impl InteractScreen {
|
|||
self.pending_display_count = 0;
|
||||
|
||||
let (generation, entries) = {
|
||||
let st = match self.agent.state.try_lock() {
|
||||
Ok(st) => st,
|
||||
Err(_) => return,
|
||||
};
|
||||
let st = self.agent.state.lock_blocking();
|
||||
let generation = st.generation;
|
||||
drop(st);
|
||||
let ctx = match self.agent.context.try_lock() {
|
||||
Ok(ctx) => ctx,
|
||||
Err(_) => return,
|
||||
};
|
||||
let ctx = self.agent.context.lock_blocking();
|
||||
(generation, ctx.conversation().to_vec())
|
||||
};
|
||||
|
||||
|
|
@ -654,7 +648,7 @@ impl InteractScreen {
|
|||
if let Some(cmd) = dispatch_command(input) {
|
||||
(cmd.handler)(self, &input[cmd.name.len()..].trim_start());
|
||||
} else {
|
||||
if let Ok(mut ag) = self.agent.state.try_lock() {
|
||||
{ let mut ag = self.agent.state.lock_blocking();
|
||||
ag.notify(format!("unknown: {}", input.split_whitespace().next().unwrap_or(input)));
|
||||
}
|
||||
}
|
||||
|
|
@ -770,9 +764,8 @@ impl InteractScreen {
|
|||
/// Draw the main (F1) screen — four-pane layout with status bar.
|
||||
fn draw_main(&mut self, frame: &mut Frame, size: Rect, app: &App) {
|
||||
// Main layout: content area + active tools overlay + status bar
|
||||
let st_guard = app.agent.state.try_lock().ok();
|
||||
let tool_lines = st_guard.as_ref()
|
||||
.map(|st| st.active_tools.len() as u16).unwrap_or(0);
|
||||
let st_guard = app.agent.state.lock_blocking();
|
||||
let tool_lines = st_guard.active_tools.len() as u16;
|
||||
let main_chunks = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints([
|
||||
|
|
@ -861,10 +854,9 @@ impl InteractScreen {
|
|||
frame.render_widget(gutter, input_chunks[0]);
|
||||
frame.render_widget(&self.textarea, input_chunks[1]);
|
||||
|
||||
if let Some(ref st) = st_guard {
|
||||
if !st.active_tools.is_empty() {
|
||||
if !st_guard.active_tools.is_empty() {
|
||||
let tool_style = Style::default().fg(Color::Yellow).add_modifier(Modifier::DIM);
|
||||
let tool_text: Vec<Line> = st.active_tools.iter().map(|t| {
|
||||
let tool_text: Vec<Line> = st_guard.active_tools.iter().map(|t| {
|
||||
let elapsed = t.started.elapsed().as_secs();
|
||||
let line = if t.detail.is_empty() {
|
||||
format!(" [{}] ({}s)", t.name, elapsed)
|
||||
|
|
@ -875,7 +867,7 @@ impl InteractScreen {
|
|||
}).collect();
|
||||
let tool_para = Paragraph::new(tool_text);
|
||||
frame.render_widget(tool_para, tools_overlay_area);
|
||||
}}
|
||||
}
|
||||
|
||||
// Draw status bar with live activity indicator
|
||||
let timer = if !app.activity.is_empty() {
|
||||
|
|
@ -1026,7 +1018,7 @@ impl ScreenView for InteractScreen {
|
|||
self.sync_from_agent();
|
||||
|
||||
// Read status from agent + mind state
|
||||
if let Ok(mut st) = self.agent.state.try_lock() {
|
||||
{ let mut st = self.agent.state.lock_blocking();
|
||||
st.expire_activities();
|
||||
app.status.prompt_tokens = st.last_prompt_tokens;
|
||||
app.status.model = self.agent.model().to_string();
|
||||
|
|
@ -1036,7 +1028,7 @@ impl ScreenView for InteractScreen {
|
|||
app.activity_started = st.activities.last()
|
||||
.map(|a| a.started);
|
||||
}
|
||||
if let Ok(ctx) = self.agent.context.try_lock() {
|
||||
{ let ctx = self.agent.context.lock_blocking();
|
||||
let window = crate::agent::context::context_window();
|
||||
if window > 0 {
|
||||
let sys = ctx.system().iter().map(|n| n.tokens()).sum::<usize>();
|
||||
|
|
|
|||
|
|
@ -20,10 +20,7 @@ impl ConsciousScreen {
|
|||
}
|
||||
|
||||
fn read_context_views(&self) -> Vec<SectionView> {
|
||||
let ctx = match self.agent.context.try_lock() {
|
||||
Ok(ctx) => ctx,
|
||||
Err(_) => return Vec::new(),
|
||||
};
|
||||
let ctx = self.agent.context.lock_blocking();
|
||||
|
||||
let mut views: Vec<SectionView> = Vec::new();
|
||||
|
||||
|
|
@ -161,8 +158,7 @@ impl ScreenView for ConsciousScreen {
|
|||
)));
|
||||
lines.push(Line::raw(format!(" Reasoning: {}", app.reasoning_effort)));
|
||||
lines.push(Line::raw(format!(" Running processes: {}", app.running_processes)));
|
||||
let tool_count = app.agent.state.try_lock()
|
||||
.map(|st| st.active_tools.len()).unwrap_or(0);
|
||||
let tool_count = { let st = app.agent.state.lock_blocking(); st.active_tools.len() };
|
||||
lines.push(Line::raw(format!(" Active tools: {}", tool_count)));
|
||||
|
||||
let block = pane_block("context")
|
||||
|
|
|
|||
|
|
@ -292,7 +292,7 @@ async fn start(cli: crate::user::CliArgs) -> Result<()> {
|
|||
}
|
||||
|
||||
fn hotkey_cycle_reasoning(mind: &crate::mind::Mind) {
|
||||
if let Ok(mut ag) = mind.agent.state.try_lock() {
|
||||
{ let mut ag = mind.agent.state.lock_blocking();
|
||||
let next = match ag.reasoning_effort.as_str() {
|
||||
"none" => "low",
|
||||
"low" => "high",
|
||||
|
|
@ -344,7 +344,7 @@ fn hotkey_cycle_autonomy(mind: &crate::mind::Mind) {
|
|||
};
|
||||
s.dmn_turns = 0;
|
||||
drop(s);
|
||||
if let Ok(mut ag) = mind.agent.state.try_lock() {
|
||||
{ let mut ag = mind.agent.state.lock_blocking();
|
||||
ag.notify(format!("DMN → {}", label));
|
||||
}
|
||||
}
|
||||
|
|
@ -419,7 +419,7 @@ async fn run(
|
|||
|
||||
terminal.hide_cursor()?;
|
||||
|
||||
if let Ok(mut ag) = agent.state.try_lock() { ag.notify("consciousness v0.3"); }
|
||||
{ let mut ag = agent.state.lock_blocking(); ag.notify("consciousness v0.3"); }
|
||||
|
||||
// Initial render
|
||||
{
|
||||
|
|
@ -526,7 +526,7 @@ async fn run(
|
|||
}
|
||||
app.walked_count = mind.subconscious_walked().await.len();
|
||||
if !startup_done {
|
||||
if let Ok(mut ag) = agent.state.try_lock() {
|
||||
{ let mut ag = agent.state.lock_blocking();
|
||||
let model = agent.model().to_string();
|
||||
ag.notify(format!("model: {}", model));
|
||||
startup_done = true;
|
||||
|
|
@ -545,7 +545,7 @@ async fn run(
|
|||
if let Some(rx_mutex) = STDERR_RX.get() {
|
||||
if let Ok(rx) = rx_mutex.try_lock() {
|
||||
while let Ok(line) = rx.try_recv() {
|
||||
if let Ok(mut ag) = agent.state.try_lock() {
|
||||
{ let mut ag = agent.state.lock_blocking();
|
||||
ag.notify(format!("stderr: {}", line));
|
||||
dirty = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -222,31 +222,30 @@ impl SubconsciousScreen {
|
|||
let fork_point = app.agent_state.get(self.selected())
|
||||
.map(|s| s.fork_point).unwrap_or(0);
|
||||
|
||||
agent.context.try_lock().ok()
|
||||
.map(|ctx| {
|
||||
let mut views = Vec::new();
|
||||
views.push(section_to_view("System", ctx.system()));
|
||||
views.push(section_to_view("Identity", ctx.identity()));
|
||||
views.push(section_to_view("Journal", ctx.journal()));
|
||||
{
|
||||
let ctx = agent.context.lock_blocking();
|
||||
let mut views = Vec::new();
|
||||
views.push(section_to_view("System", ctx.system()));
|
||||
views.push(section_to_view("Identity", ctx.identity()));
|
||||
views.push(section_to_view("Journal", ctx.journal()));
|
||||
|
||||
// Conversation: skip to fork point for subconscious agents
|
||||
let conv = ctx.conversation();
|
||||
let conv_view = section_to_view("Conversation", conv);
|
||||
let fork = fork_point.min(conv_view.children.len());
|
||||
let conv_children: Vec<SectionView> = conv_view.children
|
||||
.into_iter().skip(fork).collect();
|
||||
views.push(SectionView {
|
||||
name: format!("Conversation ({} entries)", conv_children.len()),
|
||||
tokens: conv_children.iter().map(|c| c.tokens).sum(),
|
||||
content: String::new(),
|
||||
token_ids: Vec::new(),
|
||||
children: conv_children,
|
||||
status: String::new(),
|
||||
});
|
||||
// Conversation: skip to fork point for subconscious agents
|
||||
let conv = ctx.conversation();
|
||||
let conv_view = section_to_view("Conversation", conv);
|
||||
let fork = fork_point.min(conv_view.children.len());
|
||||
let conv_children: Vec<SectionView> = conv_view.children
|
||||
.into_iter().skip(fork).collect();
|
||||
views.push(SectionView {
|
||||
name: format!("Conversation ({} entries)", conv_children.len()),
|
||||
tokens: conv_children.iter().map(|c| c.tokens).sum(),
|
||||
content: String::new(),
|
||||
token_ids: Vec::new(),
|
||||
children: conv_children,
|
||||
status: String::new(),
|
||||
});
|
||||
|
||||
views
|
||||
})
|
||||
.unwrap_or_default()
|
||||
views
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_list(&mut self, frame: &mut Frame, area: Rect, app: &App) {
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ impl ScreenView for ThalamusScreen {
|
|||
}
|
||||
KeyCode::Char('t') => {
|
||||
app.think_native = !app.think_native;
|
||||
if let Ok(mut st) = app.agent.state.try_lock() {
|
||||
{ let mut st = app.agent.state.lock_blocking();
|
||||
st.think_native = app.think_native;
|
||||
let status = if app.think_native { "enabled" } else { "disabled" };
|
||||
st.notify(format!("native thinking {}", status));
|
||||
|
|
@ -53,7 +53,7 @@ impl ScreenView for ThalamusScreen {
|
|||
}
|
||||
KeyCode::Char('T') => {
|
||||
app.think_tool = !app.think_tool;
|
||||
if let Ok(mut st) = app.agent.state.try_lock() {
|
||||
{ let mut st = app.agent.state.lock_blocking();
|
||||
st.think_tool = app.think_tool;
|
||||
// Add or remove the think tool from the tools list
|
||||
if app.think_tool {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue