Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions crates/buzz-acp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,22 +222,24 @@ Start with **N=2** for most deployments. Increase if queue depth grows under loa

## Forum Channels

By default, the ACP harness subscribes to stream message kinds (9, 46010, 40007). To receive forum events, opt in with `--kinds` and disable the mention filter (forum posts don't @mention agents):
`subscribe=mentions` subscribes by default to kinds **9, 40002, 45001, 45003, 46010, 40007** — both canonical stream message kinds, forum posts and comments, approval requests and reminders. A forum post or comment that @mentions the agent is therefore delivered with no extra flags.

What still needs opting in is *all* forum traffic — posts and comments that do not mention the agent, and votes (45002), which are never mentions. That means dropping the mention filter:

**CLI flags:**
```bash
buzz-acp --kinds 9,46010,40007,45001,45002,45003 --no-mention-filter
buzz-acp --kinds 9,40002,46010,40007,45001,45002,45003 --no-mention-filter
```

**Or with `--subscribe all`:**
```bash
buzz-acp --subscribe all --kinds 9,46010,40007,45001,45002,45003
buzz-acp --subscribe all --kinds 9,40002,46010,40007,45001,45002,45003
```

**Per-channel config:**
```toml
[channel.CHANNEL_UUID]
kinds = [9, 46010, 40007, 45001, 45002, 45003]
kinds = [9, 40002, 46010, 40007, 45001, 45002, 45003]
require_mention = false
```

Expand All @@ -246,7 +248,9 @@ Forum event kinds:
- **45002** — Vote on a post or comment
- **45003** — Comment reply on a forum post

> **Note:** Without `--no-mention-filter` (or `require_mention = false`), the default `subscribe=mentions` mode filters events that don't @mention the agent — forum posts will be invisible.
> **Note:** `--kinds` **replaces** the default list rather than adding to it, so any list you pass must repeat the kinds you still want. Leaving 40002 out of one of these examples is enough to go deaf to direct mentions in v2 stream messages.
>
> Without `--no-mention-filter` (or `require_mention = false`), `subscribe=mentions` still filters out events that don't @mention the agent — forum posts addressed to nobody in particular remain invisible.

## How It Works

Expand Down
77 changes: 69 additions & 8 deletions crates/buzz-acp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ use std::time::Duration;
use acp::{AcpClient, EnvVar, McpServer};
use anyhow::Result;
use buzz_core::kind::{
KIND_MEMBER_ADDED_NOTIFICATION, KIND_MEMBER_REMOVED_NOTIFICATION, KIND_STREAM_MESSAGE,
KIND_FORUM_COMMENT, KIND_FORUM_POST, KIND_MEMBER_ADDED_NOTIFICATION,
KIND_MEMBER_REMOVED_NOTIFICATION, KIND_STREAM_MESSAGE, KIND_STREAM_MESSAGE_V2,
KIND_STREAM_REMINDER, KIND_WORKFLOW_APPROVAL_REQUESTED,
};
use buzz_core::observer::{
Expand Down Expand Up @@ -2074,13 +2075,10 @@ async fn tokio_main() -> Result<()> {
vec![SubscriptionRule {
name: "mentions".into(),
channels: filter::ChannelScope::All("all".into()),
kinds: config.kinds_override.clone().unwrap_or_else(|| {
vec![
KIND_STREAM_MESSAGE,
KIND_WORKFLOW_APPROVAL_REQUESTED,
KIND_STREAM_REMINDER,
]
}),
kinds: config
.kinds_override
.clone()
.unwrap_or_else(default_mention_kinds),
require_mention: !config.no_mention_filter,
filter: None,
compiled_filter: None,
Expand Down Expand Up @@ -3517,6 +3515,29 @@ fn event_mentions_agent(event: &nostr::Event, agent_pubkey_hex: &str) -> bool {
})
}

/// Event kinds `--subscribe mentions` listens to by default.
///
/// A forum channel carries its conversation as kind:45001 posts and kind:45003
/// comments, not kind:9 — so an agent left on the default subscription was
/// deaf in exactly the channels built for threaded discussion: it joined, it
/// showed online, and an `@mention` in a forum post produced no inbound event
/// at all (#5268).
///
/// The canonical message kinds are 9 *and* 40002; `buzz-db`'s mentions query
/// (`feed.rs`) selects both alongside the two forum kinds, and this list has
/// to match it or an agent stays deaf to direct mentions in v2 stream
/// messages.
fn default_mention_kinds() -> Vec<u32> {
vec![
KIND_STREAM_MESSAGE,
KIND_STREAM_MESSAGE_V2,
KIND_FORUM_POST,
KIND_FORUM_COMMENT,
KIND_WORKFLOW_APPROVAL_REQUESTED,
KIND_STREAM_REMINDER,
]
}

fn is_owner_control_command(
event: &nostr::Event,
kind_u32: u32,
Expand Down Expand Up @@ -8674,3 +8695,43 @@ mod observer_payload_trim_tests {
assert!(leaf.contains("[elided"));
}
}

#[cfg(test)]
mod default_mention_kinds_tests {
use super::default_mention_kinds;
use buzz_core::kind::{
KIND_FORUM_COMMENT, KIND_FORUM_POST, KIND_STREAM_MESSAGE, KIND_STREAM_MESSAGE_V2,
KIND_STREAM_REMINDER, KIND_WORKFLOW_APPROVAL_REQUESTED,
};

#[test]
fn mentions_cover_forum_channels() {
// A forum channel's conversation is 45001/45003, so leaving them out
// makes the default subscription deaf there (#5268).
let kinds = default_mention_kinds();
assert!(kinds.contains(&KIND_FORUM_POST), "{kinds:?}");
assert!(kinds.contains(&KIND_FORUM_COMMENT), "{kinds:?}");
}

#[test]
fn mentions_still_cover_the_stream_kinds_they_always_did() {
let kinds = default_mention_kinds();
for kind in [
KIND_STREAM_MESSAGE,
KIND_WORKFLOW_APPROVAL_REQUESTED,
KIND_STREAM_REMINDER,
] {
assert!(kinds.contains(&kind), "{kind} missing from {kinds:?}");
}
}

#[test]
fn mentions_cover_both_canonical_message_kinds() {
// buzz-db's mentions query selects 9 and 40002 together; a default
// that carries only 9 leaves an agent deaf to direct mentions in v2
// stream messages.
let kinds = default_mention_kinds();
assert!(kinds.contains(&KIND_STREAM_MESSAGE), "{kinds:?}");
assert!(kinds.contains(&KIND_STREAM_MESSAGE_V2), "{kinds:?}");
}
}