notedeck

One damus client to rule them all
git clone git://jb55.com/notedeck
Log | Files | Refs | README | LICENSE

commit 3b7650a11bc1313a6b817b8d46db0a4b3066d162
parent 127897b23a15532973e680d9e9e45f9f28bf7948
Author: William Casarin <jb55@jb55.com>
Date:   Mon,  9 Feb 2026 12:42:54 -0800

dave: fix tool responses for non-active sessions

Previously, process_events only set should_send when the active session
emitted ToolCalls, preventing non-active sessions from sending their
tool responses back to the AI backend. This could hang their streams.

Change process_events to return a HashSet of session IDs that need to
send, and dispatch tool responses to each session's backend via a new
send_user_message_for helper.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

Diffstat:
Mcrates/notedeck_dave/src/lib.rs | 49+++++++++++++++++++++++++------------------------
1 file changed, 25 insertions(+), 24 deletions(-)

diff --git a/crates/notedeck_dave/src/lib.rs b/crates/notedeck_dave/src/lib.rs @@ -23,7 +23,7 @@ use enostr::KeypairUnowned; use focus_queue::FocusQueue; use nostrdb::Transaction; use notedeck::{ui::is_narrow, AppAction, AppContext, AppResponse}; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::path::PathBuf; use std::string::ToString; use std::sync::Arc; @@ -226,10 +226,10 @@ You are an AI agent for the nostr protocol called Dave, created by Damus. nostr } /// Process incoming tokens from the ai backend for ALL sessions - fn process_events(&mut self, app_ctx: &AppContext) -> bool { - // Should we continue sending requests? Set this to true if - // we have tool responses to send back to the ai (only for active session) - let mut should_send = false; + /// Returns a set of session IDs that need to send tool responses + fn process_events(&mut self, app_ctx: &AppContext) -> HashSet<SessionId> { + // Track which sessions need to send tool responses + let mut needs_send: HashSet<SessionId> = HashSet::new(); let active_id = self.session_manager.active_id(); // Get all session IDs to process @@ -283,33 +283,26 @@ You are an AI agent for the nostr protocol called Dave, created by Damus. nostr ToolResponses::PresentNotes(present.note_ids.len() as i32), ))); - // Only send for active session - if active_id == Some(session_id) { - should_send = true; - } + needs_send.insert(session_id); } ToolCalls::Invalid(invalid) => { - if active_id == Some(session_id) { - should_send = true; - } - session.chat.push(Message::tool_error( call.id().to_string(), invalid.error.clone(), )); + + needs_send.insert(session_id); } ToolCalls::Query(search_call) => { - if active_id == Some(session_id) { - should_send = true; - } - let resp = search_call.execute(&txn, app_ctx.ndb); session.chat.push(Message::ToolResponse(ToolResponse::new( call.id().to_owned(), ToolResponses::Query(resp), - ))) + ))); + + needs_send.insert(session_id); } } } @@ -416,7 +409,7 @@ You are an AI agent for the nostr protocol called Dave, created by Damus. nostr } } - should_send + needs_send } fn ui(&mut self, app_ctx: &mut AppContext, ui: &mut egui::Ui) -> DaveResponse { @@ -821,7 +814,15 @@ You are an AI agent for the nostr protocol called Dave, created by Damus. nostr } fn send_user_message(&mut self, app_ctx: &AppContext, ctx: &egui::Context) { - let Some(session) = self.session_manager.get_active_mut() else { + let Some(active_id) = self.session_manager.active_id() else { + return; + }; + self.send_user_message_for(active_id, app_ctx, ctx); + } + + /// Send a message for a specific session by ID + fn send_user_message_for(&mut self, sid: SessionId, app_ctx: &AppContext, ctx: &egui::Context) { + let Some(session) = self.session_manager.get_mut(sid) else { return; }; @@ -886,7 +887,7 @@ impl notedeck::App for Dave { self.check_interrupt_timeout(); // Process incoming AI responses for all sessions - let should_send = self.process_events(ctx); + let sessions_needing_send = self.process_events(ctx); // Update all session statuses after processing events self.session_manager.update_all_statuses(); @@ -912,9 +913,9 @@ impl notedeck::App for Dave { } } - // Send continuation message if we have tool responses - if should_send { - self.send_user_message(ctx, ui.ctx()); + // Send continuation messages for all sessions that have tool responses + for session_id in sessions_needing_send { + self.send_user_message_for(session_id, ctx, ui.ctx()); } AppResponse::action(app_action)