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:
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,
))
}