notedeck

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

commit 090c93d73b7562e20e1129c4c85bc14209995501
parent dddf12aa6730b4ade3df437f0caf65b3796e34cd
Author: William Casarin <jb55@jb55.com>
Date:   Fri, 27 Feb 2026 12:57:50 -0800

fix: enable remote session kickstart via ndb subscriptions

Set up live_conversation_sub and conversation_action_sub immediately
after creating a session from a remote spawn command, using the
session's stable event_id as the d-tag filter. This allows the desktop
to receive kind-1988 user messages from mobile clients and dispatch
them to start the Claude CLI backend without requiring a local user
to send the first message.

Also fix handle_session_info to subscribe using event_id instead of
the Claude CLI session ID, since all live events use event_id as
their Nostr d-tag identity.

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

Diffstat:
Mcrates/notedeck_dave/src/lib.rs | 52+++++++++++++++++++++++++++++++++-------------------
Mcrates/notedeck_dave/src/update.rs | 10++++++++++
2 files changed, 43 insertions(+), 19 deletions(-)

diff --git a/crates/notedeck_dave/src/lib.rs b/crates/notedeck_dave/src/lib.rs @@ -594,6 +594,7 @@ You are an AI agent for the nostr protocol called Dave, created by Damus. nostr cwd, &self.hostname, self.model_config.backend, + None, ); if let Some(session) = self.session_manager.get_mut(id) { @@ -1090,6 +1091,7 @@ You are an AI agent for the nostr protocol called Dave, created by Damus. nostr cwd, &self.hostname, backend_type, + None, ); } @@ -1872,6 +1874,7 @@ You are an AI agent for the nostr protocol called Dave, created by Damus. nostr PathBuf::from(cwd), &self.hostname, backend, + Some(ctx.ndb), ); } } @@ -2978,6 +2981,31 @@ pub(crate) fn setup_conversation_subscription( } } +/// Subscribe for kind-1988 conversation action events (permission responses, +/// mode commands) for the given session d-tag. +pub(crate) fn setup_conversation_action_subscription( + agentic: &mut session::AgenticSessionData, + event_id: &str, + ndb: &nostrdb::Ndb, +) { + if agentic.conversation_action_sub.is_some() { + return; + } + let filter = nostrdb::Filter::new() + .kinds([session_events::AI_CONVERSATION_KIND as u64]) + .tags([event_id], 'd') + .build(); + match ndb.subscribe(&[filter]) { + Ok(sub) => { + agentic.conversation_action_sub = Some(sub); + tracing::info!("subscribed for conversation actions (session {})", event_id,); + } + Err(e) => { + tracing::warn!("failed to subscribe for conversation actions: {:?}", e); + } + } +} + /// Check if a session state represents a remote session. /// /// A session is remote if its hostname differs from the local hostname, @@ -3351,26 +3379,12 @@ fn handle_query_complete(session: &mut session::ChatSession, info: messages::Usa /// when we first learn the claude session ID. fn handle_session_info(session: &mut session::ChatSession, info: SessionInfo, ndb: &nostrdb::Ndb) { if let Some(agentic) = &mut session.agentic { - if let Some(ref csid) = info.claude_session_id { - // Subscribe for kind-1988 events (permission responses, commands) - if agentic.conversation_action_sub.is_none() { - let filter = nostrdb::Filter::new() - .kinds([session_events::AI_CONVERSATION_KIND as u64]) - .tags([csid.as_str()], 'd') - .build(); - match ndb.subscribe(&[filter]) { - Ok(sub) => { - tracing::info!("subscribed for conversation actions (session {})", csid); - agentic.conversation_action_sub = Some(sub); - } - Err(e) => { - tracing::warn!("failed to subscribe for conversation actions: {:?}", e); - } - } - } + // Use the stable event_id (not the CLI session ID) for subscriptions, + // since all live events are tagged with event_id as the d-tag. + let event_id = agentic.event_session_id().to_string(); + setup_conversation_action_subscription(agentic, &event_id, ndb); + setup_conversation_subscription(agentic, &event_id, ndb); - setup_conversation_subscription(agentic, csid, ndb); - } agentic.session_info = Some(info); } // Persist initial session state now that we know the claude_session_id diff --git a/crates/notedeck_dave/src/update.rs b/crates/notedeck_dave/src/update.rs @@ -925,6 +925,7 @@ pub fn create_session_with_cwd( cwd: PathBuf, hostname: &str, backend_type: BackendType, + ndb: Option<&nostrdb::Ndb>, ) -> SessionId { directory_picker.add_recent(cwd.clone()); @@ -938,6 +939,14 @@ pub fn create_session_with_cwd( scene.focus_on(agentic.scene_position); } } + + // Set up ndb subscriptions so remote clients can send messages + // to this session (e.g. to kickstart the backend remotely). + if let (Some(ndb), Some(agentic)) = (ndb, &mut session.agentic) { + let event_id = agentic.event_session_id().to_string(); + crate::setup_conversation_subscription(agentic, &event_id, ndb); + crate::setup_conversation_action_subscription(agentic, &event_id, ndb); + } } session_manager.rebuild_host_groups(); id @@ -996,6 +1005,7 @@ pub fn clone_active_agent( cwd, hostname, backend_type, + None, )) }