commit e6c9c5427f76aefa505e5ff68a20b9b4ed1e42ba
parent 44233ecf9f1df49d19b76b95f2429b3b98183e34
Author: William Casarin <jb55@jb55.com>
Date: Wed, 18 Feb 2026 10:09:33 -0800
add tool-name tag to tool_call/tool_result nostr events
Tool result messages from nostr notes were showing "tool" as the tool
name instead of the actual name (e.g. "Bash", "Read") because the
tool-name tag was never written when building events. The session_loader
was already reading this tag but it always fell back to "tool".
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat:
2 files changed, 54 insertions(+), 15 deletions(-)
diff --git a/crates/notedeck_dave/src/lib.rs b/crates/notedeck_dave/src/lib.rs
@@ -197,6 +197,7 @@ fn ingest_live_event(
content: &str,
role: &str,
tool_id: Option<&str>,
+ tool_name: Option<&str>,
) -> Option<session_events::BuiltEvent> {
let agentic = session.agentic.as_mut()?;
let session_id = agentic.event_session_id().map(|s| s.to_string())?;
@@ -208,6 +209,7 @@ fn ingest_live_event(
&session_id,
cwd,
tool_id,
+ tool_name,
&mut agentic.live_threading,
secret_key,
) {
@@ -411,7 +413,7 @@ You are an AI agent for the nostr protocol called Dave, created by Damus. nostr
DaveApiResponse::Failed(ref err) => {
if let Some(sk) = &secret_key {
if let Some(evt) =
- ingest_live_event(session, app_ctx.ndb, sk, err, "error", None)
+ ingest_live_event(session, app_ctx.ndb, sk, err, "error", None, None)
{
events_to_publish.push(evt);
}
@@ -538,6 +540,7 @@ You are an AI agent for the nostr protocol called Dave, created by Damus. nostr
&content,
"tool_result",
None,
+ Some(result.tool_name.as_str()),
) {
events_to_publish.push(evt);
}
@@ -683,6 +686,7 @@ You are an AI agent for the nostr protocol called Dave, created by Damus. nostr
&text,
"assistant",
None,
+ None,
) {
events_to_publish.push(evt);
}
@@ -1599,10 +1603,13 @@ You are an AI agent for the nostr protocol called Dave, created by Damus. nostr
} else {
content.to_string()
};
+ let tool_name = session_events::get_tag_value(note, "tool-name")
+ .unwrap_or("tool")
+ .to_string();
session
.chat
.push(Message::ToolResult(crate::messages::ToolResult {
- tool_name: "tool".to_string(),
+ tool_name,
summary,
}));
}
@@ -1827,7 +1834,7 @@ You are an AI agent for the nostr protocol called Dave, created by Damus. nostr
// Generate live event for user message
if let Some(sk) = secret_key_bytes(app_ctx.accounts.get_selected_account().keypair()) {
if let Some(evt) =
- ingest_live_event(session, app_ctx.ndb, &sk, &user_text, "user", None)
+ ingest_live_event(session, app_ctx.ndb, &sk, &user_text, "user", None, None)
{
self.pending_relay_events.push(evt);
}
diff --git a/crates/notedeck_dave/src/session_events.rs b/crates/notedeck_dave/src/session_events.rs
@@ -258,6 +258,19 @@ pub fn build_events(
ContentBlock::ToolResult { tool_use_id, .. } => Some(*tool_use_id),
_ => None,
};
+ let tool_name = match block {
+ ContentBlock::ToolUse { name, .. } => Some(*name),
+ ContentBlock::ToolResult { tool_use_id, .. } => {
+ // Look up tool name from a prior ToolUse block with matching id
+ blocks.iter().find_map(|b| match b {
+ ContentBlock::ToolUse { id, name, .. } if *id == *tool_use_id => {
+ Some(*name)
+ }
+ _ => None,
+ })
+ }
+ _ => None,
+ };
let event = build_single_event(
Some(line),
@@ -266,6 +279,7 @@ pub fn build_events(
"claude-code",
Some((i, total)),
tool_id,
+ tool_name,
session_id.as_deref(),
None,
timestamp,
@@ -281,19 +295,26 @@ pub fn build_events(
let content = session_jsonl::extract_display_content(line);
let role = line.role().unwrap_or("unknown");
- // Extract tool_id from single-block messages
- let tool_id = msg.as_ref().and_then(|m| {
- let blocks = m.content_blocks();
- if blocks.len() == 1 {
- match &blocks[0] {
- ContentBlock::ToolUse { id, .. } => Some(id.to_string()),
- ContentBlock::ToolResult { tool_use_id, .. } => Some(tool_use_id.to_string()),
- _ => None,
+ // Extract tool_id and tool_name from single-block messages
+ let (tool_id, tool_name) = msg
+ .as_ref()
+ .and_then(|m| {
+ let blocks = m.content_blocks();
+ if blocks.len() == 1 {
+ match &blocks[0] {
+ ContentBlock::ToolUse { id, name, .. } => {
+ Some((id.to_string(), Some(name.to_string())))
+ }
+ ContentBlock::ToolResult { tool_use_id, .. } => {
+ Some((tool_use_id.to_string(), None))
+ }
+ _ => None,
+ }
+ } else {
+ None
}
- } else {
- None
- }
- });
+ })
+ .map_or((None, None), |(id, name)| (Some(id), name));
let event = build_single_event(
Some(line),
@@ -302,6 +323,7 @@ pub fn build_events(
"claude-code",
None,
tool_id.as_deref(),
+ tool_name.as_deref(),
session_id.as_deref(),
None,
timestamp,
@@ -391,6 +413,8 @@ fn build_source_data_event(
/// assistant message.
///
/// `tool_id`: The tool use/result ID for tool_call and tool_result events.
+///
+/// `tool_name`: The tool name (e.g. "Bash", "Read") for tool_call and tool_result events.
#[allow(clippy::too_many_arguments)]
fn build_single_event(
line: Option<&JsonlLine>,
@@ -399,6 +423,7 @@ fn build_single_event(
source: &str,
split_index: Option<(usize, usize)>,
tool_id: Option<&str>,
+ tool_name: Option<&str>,
session_id: Option<&str>,
cwd: Option<&str>,
timestamp: Option<u64>,
@@ -475,6 +500,11 @@ fn build_single_event(
builder = builder.start_tag().tag_str("tool-id").tag_str(tid);
}
+ // -- Tool name tag --
+ if let Some(tn) = tool_name {
+ builder = builder.start_tag().tag_str("tool-name").tag_str(tn);
+ }
+
// -- Discoverability --
builder = builder.start_tag().tag_str("t").tag_str("ai-conversation");
@@ -493,6 +523,7 @@ pub fn build_live_event(
session_id: &str,
cwd: Option<&str>,
tool_id: Option<&str>,
+ tool_name: Option<&str>,
threading: &mut ThreadingState,
secret_key: &[u8; 32],
) -> Result<BuiltEvent, EventBuildError> {
@@ -503,6 +534,7 @@ pub fn build_live_event(
"notedeck-dave",
None,
tool_id,
+ tool_name,
Some(session_id),
cwd,
Some(now_secs()),