commit 7b4430340c1d6f091bcc628b6aa47a36ecabac4d
parent d9c46bc912d7f7d9953a846bc243f53046055a34
Author: William Casarin <jb55@jb55.com>
Date: Tue, 24 Feb 2026 16:25:04 -0800
dave: persist backend type in nostr session state events
Codex sessions were being restored as Claude after round-tripping
through nostrdb because the backend tag was missing from kind-31988
events. Add a "backend" tag to session state events and parse it
on restore, falling back to Claude for old events without the tag.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat:
4 files changed, 38 insertions(+), 1 deletion(-)
diff --git a/crates/notedeck_dave/src/backend/traits.rs b/crates/notedeck_dave/src/backend/traits.rs
@@ -38,6 +38,27 @@ impl BackendType {
BackendType::Remote => "",
}
}
+
+ /// Stable string for Nostr event tags.
+ pub fn as_str(&self) -> &'static str {
+ match self {
+ BackendType::OpenAI => "openai",
+ BackendType::Claude => "claude",
+ BackendType::Codex => "codex",
+ BackendType::Remote => "remote",
+ }
+ }
+
+ /// Parse from a Nostr event tag value.
+ pub fn from_tag_str(s: &str) -> Option<BackendType> {
+ match s {
+ "openai" => Some(BackendType::OpenAI),
+ "claude" => Some(BackendType::Claude),
+ "codex" => Some(BackendType::Codex),
+ "remote" => Some(BackendType::Remote),
+ _ => None,
+ }
+ }
}
/// Trait for AI backend implementations
diff --git a/crates/notedeck_dave/src/lib.rs b/crates/notedeck_dave/src/lib.rs
@@ -197,6 +197,7 @@ struct DeletedSessionInfo {
title: String,
cwd: String,
home_dir: String,
+ backend: BackendType,
}
/// Subscription waiting for ndb to index 1988 conversation events.
@@ -1433,6 +1434,7 @@ You are an AI agent for the nostr protocol called Dave, created by Damus. nostr
status,
&self.hostname,
&session.details.home_dir,
+ session.backend_type.as_str(),
&sk,
),
&format!("publishing session state: {} -> {}", claude_sid, status),
@@ -1466,6 +1468,7 @@ You are an AI agent for the nostr protocol called Dave, created by Damus. nostr
"deleted",
&self.hostname,
&info.home_dir,
+ info.backend.as_str(),
&sk,
),
&format!(
@@ -1557,13 +1560,18 @@ You are an AI agent for the nostr protocol called Dave, created by Damus. nostr
tracing::info!("restoring {} sessions from ndb", states.len());
for state in &states {
+ let backend = state
+ .backend
+ .as_deref()
+ .and_then(BackendType::from_tag_str)
+ .unwrap_or(BackendType::Claude);
let cwd = std::path::PathBuf::from(&state.cwd);
let dave_sid = self.session_manager.new_resumed_session(
cwd,
state.claude_session_id.clone(),
state.title.clone(),
AiMode::Agentic,
- BackendType::Claude,
+ backend,
);
// Load conversation history from kind-1988 events
@@ -2103,6 +2111,7 @@ You are an AI agent for the nostr protocol called Dave, created by Damus. nostr
title: session.details.title.clone(),
cwd: agentic.cwd.to_string_lossy().to_string(),
home_dir: session.details.home_dir.clone(),
+ backend: session.backend_type,
});
}
}
diff --git a/crates/notedeck_dave/src/session_events.rs b/crates/notedeck_dave/src/session_events.rs
@@ -737,6 +737,7 @@ pub fn build_session_state_event(
status: &str,
hostname: &str,
home_dir: &str,
+ backend: &str,
secret_key: &[u8; 32],
) -> Result<BuiltEvent, EventBuildError> {
let mut builder = init_note_builder(AI_SESSION_STATE_KIND, "", Some(now_secs()));
@@ -753,6 +754,7 @@ pub fn build_session_state_event(
builder = builder.start_tag().tag_str("status").tag_str(status);
builder = builder.start_tag().tag_str("hostname").tag_str(hostname);
builder = builder.start_tag().tag_str("home_dir").tag_str(home_dir);
+ builder = builder.start_tag().tag_str("backend").tag_str(backend);
// Discoverability
builder = builder.start_tag().tag_str("t").tag_str("ai-session-state");
@@ -1259,6 +1261,7 @@ mod tests {
"working",
"my-laptop",
"/home/testuser",
+ "claude",
&sk,
)
.unwrap();
@@ -1278,6 +1281,7 @@ mod tests {
assert!(json.contains("working"));
assert!(json.contains("/tmp/project"));
assert!(json.contains(r#""hostname","my-laptop"#));
+ assert!(json.contains(r#""backend","claude"#));
}
#[test]
diff --git a/crates/notedeck_dave/src/session_loader.rs b/crates/notedeck_dave/src/session_loader.rs
@@ -251,6 +251,7 @@ pub struct SessionState {
pub status: String,
pub hostname: String,
pub home_dir: String,
+ pub backend: Option<String>,
pub created_at: u64,
}
@@ -297,6 +298,7 @@ pub fn load_session_states(ndb: &Ndb, txn: &Transaction) -> Vec<SessionState> {
status: get_tag_value(¬e, "status").unwrap_or("idle").to_string(),
hostname: get_tag_value(¬e, "hostname").unwrap_or("").to_string(),
home_dir: get_tag_value(¬e, "home_dir").unwrap_or("").to_string(),
+ backend: get_tag_value(¬e, "backend").map(|s| s.to_string()),
created_at: note.created_at(),
});
}
@@ -342,6 +344,7 @@ pub fn latest_valid_session(
status: get_tag_value(note, "status").unwrap_or("idle").to_string(),
hostname: get_tag_value(note, "hostname").unwrap_or("").to_string(),
home_dir: get_tag_value(note, "home_dir").unwrap_or("").to_string(),
+ backend: get_tag_value(note, "backend").map(|s| s.to_string()),
created_at: note.created_at(),
})
}