notedeck

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

commit f96313f88dd727f71ac8182f09fa65ad235898cc
parent fe86926bf75c8dd4e5175fc67704d330c9823929
Author: William Casarin <jb55@jb55.com>
Date:   Tue, 24 Feb 2026 17:50:45 -0800

dave: fix remote session dispatch spam and hostname sync

Three fixes for remote session handling:

- Don't dispatch to backends that aren't locally available, preventing
  an infinite redispatch loop on devices without Claude/Codex binaries
- Skip publishing state events for remote sessions so they can't
  overwrite the session owner's hostname with the viewer's hostname
- Sync hostname from newer state events in poll_session_state_events
  so remote devices pick up the correct hostname when it changes

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

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

diff --git a/crates/notedeck_dave/src/lib.rs b/crates/notedeck_dave/src/lib.rs @@ -1413,6 +1413,13 @@ You are an AI agent for the nostr protocol called Dave, created by Damus. nostr continue; } + // Remote sessions are owned by another machine — only the + // session owner should publish state events. + if session.is_remote() { + session.state_dirty = false; + continue; + } + let Some(agentic) = &session.agentic else { continue; }; @@ -1744,6 +1751,7 @@ You are an AI agent for the nostr protocol called Dave, created by Damus. nostr let new_status = AgentStatus::from_status_str(status_str); let new_custom_title = session_events::get_tag_value(&note, "custom_title").map(|s| s.to_string()); + let new_hostname = session_events::get_tag_value(&note, "hostname").unwrap_or(""); for session in self.session_manager.iter_mut() { let is_remote = session.is_remote(); if let Some(agentic) = &mut session.agentic { @@ -1758,6 +1766,10 @@ You are an AI agent for the nostr protocol called Dave, created by Damus. nostr if let Some(backend) = backend_tag { session.backend_type = backend; } + // Hostname syncs for remote sessions from the event + if is_remote && !new_hostname.is_empty() { + session.details.hostname = new_hostname.to_string(); + } // Status only updates for remote sessions (local // sessions derive status from the actual process) if is_remote { @@ -1766,6 +1778,7 @@ You are an AI agent for the nostr protocol called Dave, created by Damus. nostr } } } + self.session_manager.rebuild_host_groups(); continue; } @@ -2409,6 +2422,13 @@ You are an AI agent for the nostr protocol called Dave, created by Damus. nostr return; }; + // Only dispatch if we have the backend this session needs. + // Without this guard, get_backend falls back to Remote which + // immediately disconnects, causing an infinite redispatch loop. + if !self.backends.contains_key(&session.backend_type) { + return; + } + // Count trailing user messages being dispatched so append_token // knows how many to skip when inserting the assistant response. let trailing_user_count = session