commit 7490143289a082fd35b3fd13f0199c8b5153e907
parent 94a7f9c4b40b07b11b07518802a27ef87aa232c1
Author: William Casarin <jb55@jb55.com>
Date: Wed, 18 Feb 2026 10:33:57 -0800
fix remote session status using stale replaceable event revisions
When multiple revisions of a kind-31988 session state event arrive
out of order (e.g. after relay reconnect from sleep), the last one
processed would win regardless of timestamp. Track remote_status_ts
so we only apply updates from newer events.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat:
3 files changed, 24 insertions(+), 5 deletions(-)
diff --git a/crates/notedeck_dave/src/lib.rs b/crates/notedeck_dave/src/lib.rs
@@ -1321,6 +1321,7 @@ You are an AI agent for the nostr protocol called Dave, created by Damus. nostr
agentic.seen_note_ids = loaded.note_ids;
// Set remote status from state event
agentic.remote_status = AgentStatus::from_status_str(&state.status);
+ agentic.remote_status_ts = state.created_at;
// Set up live conversation subscription for remote sessions
if is_remote && agentic.live_conversation_sub.is_none() {
@@ -1395,14 +1396,18 @@ You are an AI agent for the nostr protocol called Dave, created by Damus. nostr
// Skip deleted sessions entirely — don't create or keep them
if status_str == "deleted" {
- // If we have this session locally, remove it
+ // If we have this session locally, remove it (only if this
+ // event is newer than the last state we applied).
if existing_ids.contains(claude_sid) {
+ let ts = note.created_at();
let to_delete: Vec<SessionId> = self
.session_manager
.iter()
.filter(|s| {
- s.agentic.as_ref().and_then(|a| a.event_session_id())
- == Some(claude_sid)
+ s.agentic.as_ref().is_some_and(|a| {
+ a.event_session_id() == Some(claude_sid)
+ && ts > a.remote_status_ts
+ })
})
.map(|s| s.id)
.collect();
@@ -1419,14 +1424,21 @@ You are an AI agent for the nostr protocol called Dave, created by Damus. nostr
continue;
}
- // Update remote_status for existing remote sessions
+ // Update remote_status for existing remote sessions, but only
+ // if this event is newer than the one we already applied.
+ // Multiple revisions of the same replaceable event can arrive
+ // out of order (e.g. after a relay reconnect).
if existing_ids.contains(claude_sid) {
+ let ts = note.created_at();
let new_status = AgentStatus::from_status_str(status_str);
for session in self.session_manager.iter_mut() {
if session.is_remote() {
if let Some(agentic) = &mut session.agentic {
- if agentic.event_session_id() == Some(claude_sid) {
+ if agentic.event_session_id() == Some(claude_sid)
+ && ts > agentic.remote_status_ts
+ {
agentic.remote_status = new_status;
+ agentic.remote_status_ts = ts;
}
}
}
@@ -1488,6 +1500,7 @@ You are an AI agent for the nostr protocol called Dave, created by Damus. nostr
agentic.seen_note_ids = loaded.note_ids;
// Set remote status
agentic.remote_status = AgentStatus::from_status_str(status_str);
+ agentic.remote_status_ts = note.created_at();
// Set up live conversation subscription for remote sessions
if is_remote && agentic.live_conversation_sub.is_none() {
diff --git a/crates/notedeck_dave/src/session.rs b/crates/notedeck_dave/src/session.rs
@@ -115,6 +115,9 @@ pub struct AgenticSessionData {
/// Status as reported by the remote desktop's kind-31988 event.
/// Only meaningful when session source is Remote.
pub remote_status: Option<AgentStatus>,
+ /// Timestamp of the kind-31988 event that last set `remote_status`.
+ /// Used to ignore older replaceable event revisions that arrive out of order.
+ pub remote_status_ts: u64,
/// Subscription for live kind-1988 conversation events from relays.
/// Used by remote sessions to receive new messages in real-time.
pub live_conversation_sub: Option<nostrdb::Subscription>,
@@ -151,6 +154,7 @@ impl AgenticSessionData {
live_threading: ThreadingState::new(),
perm_response_sub: None,
remote_status: None,
+ remote_status_ts: 0,
live_conversation_sub: None,
seen_note_ids: HashSet::new(),
}
diff --git a/crates/notedeck_dave/src/session_loader.rs b/crates/notedeck_dave/src/session_loader.rs
@@ -229,6 +229,7 @@ pub struct SessionState {
pub cwd: String,
pub status: String,
pub hostname: String,
+ pub created_at: u64,
}
/// Load all session states from kind-31988 events in ndb.
@@ -275,6 +276,7 @@ pub fn load_session_states(ndb: &Ndb, txn: &Transaction) -> Vec<SessionState> {
cwd: get_tag_value(¬e, "cwd").unwrap_or("").to_string(),
status: get_tag_value(¬e, "status").unwrap_or("idle").to_string(),
hostname: get_tag_value(¬e, "hostname").unwrap_or("").to_string(),
+ created_at: note.created_at(),
});
}