notedeck

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

commit f4e662a1cb0994e32e705a98c4d34bc37320ceff
parent 22c4e29ea0a36009d6b36595edbed732f081082a
Author: William Casarin <jb55@jb55.com>
Date:   Tue, 17 Feb 2026 14:30:34 -0800

re-subscribe to PNS relay on reconnect and fix duplicate sessions

When the PNS relay connection drops and reconnects, re-send the
kind-1080 subscription so events keep flowing. Also fix duplicate
session creation in poll_session_state_events by updating the
existing_ids set as new sessions are created.

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

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

diff --git a/crates/notedeck_dave/src/lib.rs b/crates/notedeck_dave/src/lib.rs @@ -1451,12 +1451,13 @@ You are an AI agent for the nostr protocol called Dave, created by Damus. nostr } } - /// Poll for new kind-1988 conversation events on remote sessions. + /// Poll for new kind-1988 conversation events. /// - /// Remote sessions subscribe to conversation events via ndb. When new - /// events arrive (from PNS unwrapping), this converts them to Messages - /// and appends them to the chat, keeping the phone UI in sync with the - /// desktop's conversation. + /// For remote sessions: process all roles (user, assistant, tool_call, etc.) + /// to keep the phone UI in sync with the desktop's conversation. + /// + /// For local sessions: only process `role=user` messages arriving from + /// remote clients (phone), collecting them for backend dispatch. fn poll_remote_conversation_events(&mut self, ndb: &nostrdb::Ndb) { let session_ids = self.session_manager.session_ids(); for session_id in session_ids { @@ -1835,8 +1836,27 @@ impl notedeck::App for Dave { fn update(&mut self, ctx: &mut AppContext<'_>, ui: &mut egui::Ui) -> AppResponse { let mut app_action: Option<AppAction> = None; - // Process relay events into ndb (needed when dave is the active app) - try_process_events_core(ctx, ui.ctx(), |_, _| {}); + // Process relay events into ndb (needed when dave is the active app). + // Re-send PNS subscription when the relay (re)connects. + let pns_sub_id = self.pns_relay_sub.clone(); + try_process_events_core(ctx, ui.ctx(), |app_ctx, ev| { + if let enostr::RelayEvent::Opened = (&ev.event).into() { + if ev.relay == PNS_RELAY_URL { + if let Some(sub_id) = &pns_sub_id { + if let Some(sk) = app_ctx.accounts.get_selected_account().keypair().secret_key { + let pns_keys = enostr::pns::derive_pns_keys(&sk.secret_bytes()); + let pns_filter = nostrdb::Filter::new() + .kinds([enostr::pns::PNS_KIND as u64]) + .authors([pns_keys.keypair.pubkey.bytes()]) + .build(); + let req = enostr::ClientMessage::req(sub_id.clone(), vec![pns_filter]); + app_ctx.pool.send_to(&req, PNS_RELAY_URL); + tracing::info!("re-subscribed for PNS events after relay reconnect"); + } + } + } + } + }); // Poll for external spawn-agent commands via IPC self.poll_ipc_commands();