Skip to content

Commit 1feddff

Browse files
Bartok9Bartok9
authored andcommitted
fix(desktop+acp): wake agents on edit-added @mentions
Issue #2540: editing a kind-9 message to add @agent never fired a turn because (1) Mentions-mode ACP only subscribed to kind 9 (not 40003 edits) and (2) build_message_edit emitted `p` tags but not explicit `mention` reference tags that send already attaches. - Default Mentions subscription kinds include KIND_STREAM_MESSAGE_EDIT - build_message_edit emits matching `mention` refs with newly added `p`s - Unit test asserts mention ref on edit-added audience Closes #2540 Signed-off-by: Bartok9 <danielrpike9@gmail.com> (cherry picked from commit 4935102) Signed-off-by: Bartok9 <259807879+Bartok9@users.noreply.github.com> (cherry picked from commit d3cfe82) Signed-off-by: Bartok9 <259807879+Bartok9@users.noreply.github.com> (cherry picked from commit d516ed0) Signed-off-by: Bartok9 <259807879+Bartok9@users.noreply.github.com> (cherry picked from commit 0c8ec32) Signed-off-by: Bartok9 <259807879+Bartok9@users.noreply.github.com>
1 parent b1f6b7e commit 1feddff

2 files changed

Lines changed: 42 additions & 4 deletions

File tree

crates/buzz-acp/src/lib.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use acp::{AcpClient, EnvVar, McpServer};
2525
use anyhow::{ensure, Context, Result};
2626
use buzz_core::kind::{
2727
KIND_MEMBER_ADDED_NOTIFICATION, KIND_MEMBER_REMOVED_NOTIFICATION, KIND_STREAM_MESSAGE,
28-
KIND_STREAM_REMINDER, KIND_WORKFLOW_APPROVAL_REQUESTED,
28+
KIND_STREAM_MESSAGE_EDIT, KIND_STREAM_REMINDER, KIND_WORKFLOW_APPROVAL_REQUESTED,
2929
};
3030
use buzz_core::observer::{
3131
decrypt_observer_payload, encrypt_observer_payload, OBSERVER_FRAME_TELEMETRY,
@@ -2674,8 +2674,12 @@ async fn tokio_main() -> Result<()> {
26742674
name: "mentions".into(),
26752675
channels: filter::ChannelScope::All("all".into()),
26762676
kinds: config.kinds_override.clone().unwrap_or_else(|| {
2677+
// Include kind:40003 message edits so adding an @mention
2678+
// via edit can wake the agent the same way a fresh send does
2679+
// (#2540). require_mention still gates on a matching `p` tag.
26772680
vec![
26782681
KIND_STREAM_MESSAGE,
2682+
KIND_STREAM_MESSAGE_EDIT,
26792683
KIND_WORKFLOW_APPROVAL_REQUESTED,
26802684
KIND_STREAM_REMINDER,
26812685
]

desktop/src-tauri/src/events.rs

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,18 @@ pub struct MessageEditTags<'a> {
356356

357357
/// Kind 40003 — edit a message with full content, media, emoji, mentions,
358358
/// and optional monotonic link-preview suppression.
359+
///
360+
/// Carries the full new content AND a fresh imeta tag set; the receiver overlays
361+
/// the imeta tags onto the original event so the rendered message reflects exactly
362+
/// the edited state. NIP-30 custom-emoji tags ride along the same way so an edited
363+
/// body's `:shortcode:`s stay resolvable (the send path attaches these too).
364+
///
365+
/// `mentions` carries the pubkeys of mentions that are *newly added* by this
366+
/// edit (the caller diffs the edited body against the original). Only those get
367+
/// a `p` tag (wake/notify) **and** a matching `mention` reference tag (explicit
368+
/// @mention signal — same as the send path's `MENTION_REFERENCE_TAG`), while a
369+
/// typo-fix edit that leaves the mention set unchanged emits neither and never
370+
/// re-wakes anyone. The receiver overlays these onto the original event's audience.
359371
pub fn build_message_edit(
360372
channel_id: Uuid,
361373
target_event_id: EventId,
@@ -368,13 +380,30 @@ pub fn build_message_edit(
368380
tag(vec!["h", &channel_id.to_string()])?,
369381
tag(vec!["e", &target_event_id.to_hex()])?,
370382
];
371-
tags.extend(mention_tags(edit_tags.mentions)?);
372-
imeta_tags(edit_tags.media, &mut tags)?;
373-
emoji_tags(edit_tags.custom_emoji, &mut tags)?;
383+
let p_tags = mention_tags(edit_tags.mentions)?;
384+
tags.extend(p_tags.clone());
385+
// Parity with send: explicit `mention` refs so relay offline-notice (#1743)
386+
// and desktop resolve-mention can treat edit-added @mentions like send.
387+
// Prefer caller-supplied mention_refs (snapshot path); otherwise derive
388+
// parallel `mention` tags from the p-tags we just emitted.
374389
if let Some(mention_refs) = edit_tags.mention_refs {
375390
mention_reference_tags(mention_refs, &mut tags)?;
376391
tags.push(tag(vec!["buzz:mention-snapshot"])?);
392+
} else {
393+
let derived_refs: Vec<Vec<String>> = p_tags
394+
.iter()
395+
.filter_map(|t| {
396+
let s = t.as_slice();
397+
let pk = s.get(1)?.clone();
398+
Some(vec!["mention".to_string(), pk])
399+
})
400+
.collect();
401+
if !derived_refs.is_empty() {
402+
mention_reference_tags(&derived_refs, &mut tags)?;
403+
}
377404
}
405+
imeta_tags(edit_tags.media, &mut tags)?;
406+
emoji_tags(edit_tags.custom_emoji, &mut tags)?;
378407
if suppress_link_previews {
379408
tags.push(tag(vec!["link-preview", "none"])?);
380409
}
@@ -893,6 +922,11 @@ mod tests {
893922
assert_eq!(tags[0][0], "h");
894923
assert_eq!(tags[1][0], "e");
895924
assert_eq!(tags[2], vec!["p".to_string(), ALICE_HEX.to_string()]);
925+
// Send-path parity: explicit mention ref for newly added @mentions (#2540).
926+
assert!(
927+
tags.iter().any(|t| t.as_slice() == ["mention", ALICE_HEX]),
928+
"edit-added mention must emit mention ref tag, got {tags:?}"
929+
);
896930
}
897931

898932
#[test]

0 commit comments

Comments
 (0)