fix(buzz-acp): never deliver ephemeral events to agent subprocesses - #4040
fix(buzz-acp): never deliver ephemeral events to agent subprocesses#4040iroiro147 wants to merge 1 commit into
Conversation
…lock#3649) When an agent runs subscribe=all (or any wildcard rule) with no curated BUZZ_ACP_KINDS allowlist, the resolved rule set admits every kind on the wire — including kind 20001 presence updates, kind 20002 typing indicators, and kind 24200 observer frames. Every keystroke in a channel emits dozens of these ephemeral events, and each one woke the agent subprocess into a full model turn: cost per keystroke for the operator and a false "agent is typing" signal in Desktop for whoever is watching. Add a design-level invariant to the shared delivery filter crates/buzz-acp/src/filter.rs, match_event: ephemeral events (20000–29999) are never delivered to an agent subprocess, regardless of any subscription rule that might otherwise admit them. Ephemeral events are transient by definition — they are not actionable work and must not wake an agent process. Closes block#3649. Signed-off-by: Claude <noreply@anthropic.com> Signed-off-by: iroiro147 <sarthak.singh@juspay.in>
|
Confirming this from a self-hosted deploy (buzz pinned at One thing worth flagging while the PR is open, not as a request to widen it: the guard is scoped On our relay that produced 25,717 kind-7 events against 304 real messages, with 22,373 of the I do not think that belongs in this PR - reactions can be genuinely actionable, unlike ambient Also, small correction for anyone using the workaround in #3649: the recommended |
|
@AhmedAburady Thanks for the field numbers — the 22,373 reaction-on-reaction count against 304 real messages quantifies the 'does not converge' claim better than my abstract argument could. You were right that it didn't belong in this PR. The fix for the 5/7 storm half is now up separately as #4091 against your #4086: when Your point about the workaround list in #3649 being storm fuel for multi-agent setups is addressed the same way — once #4091 merges, dropping 5/7 becomes the default rather than a manual exclusion. The ephemeral guard here (20000-29999) + the curated default in #4091 should cover both halves of the multi-agent wake-storm problem: ambient typing/presence and reactions/deletions. |
|
Thanks for the production confirmation — really useful to see it closing the rejection loop in the wild, and helpful to know the keystroke half specifically is what this heals. On kinds 5 / 7 (stored reactions/deletion): agreed they're a larger class of cross-agent traffic, and the current guard (20000–29999 ephemeral per NIP-01) intentionally leaves them alone. Two reasons to keep this PR scoped narrowly: (a) kinds 5/7 are stored events — relay rejection on them isn't a typo-shaped invariant the way it is for ephemeral, it's a semantic question about whether agents should |
Problem
When an agent runs
subscribe=all(or any wildcard subscription rule) with no curatedBUZZ_ACP_KINDSallowlist, the resolved rule set admits every kind on the wire — including ephemeral kinds:Every keystroke in a channel emits dozens of these ephemeral events. Each one woke the agent subprocess into a full model turn: cost per keystroke for the operator and a false "agent is typing" signal in Desktop for whoever is watching.
Closes #3649.
Root Cause
resolve_channel_filtersinconfig.rsresolvesSubscribeMode::Allwith nokinds_overridetokinds: None— a wildcard that matches every event kind. There is no design-level invariant that ephemeral events (kind 20000–29999, defined as "never stored" inbuzz-core/src/kind.rs) should be excluded from agent delivery. Thematch_eventfunction infilter.rsapplied channel-scope, kind, mention, and expression filters — but had no concept of ephemeral exclusion.Fix
Add a design-level invariant to the shared delivery filter (
crates/buzz-acp/src/filter.rs,match_event): ephemeral events (kind 20000–29999) are never delivered to an agent subprocess, regardless of any subscription rule that might otherwise admit them.This guard runs before the rule loop, so it protects:
SubscribeMode::Allwith nokinds_override(the reported case)Configrule with empty kindsAllsubscription that accidentally lists an ephemeral kindEphemeral events are transient by definition — they are not actionable work and must not wake an agent process. Failing closed here (never wake on any ephemeral kind) keeps wildcard subscriptions safe to use.
The guard uses the existing
buzz_core::kind::is_ephemeralpredicate (already a dependency ofbuzz-acp), so there is no new constant or duplicate logic.Testing
New test
test_match_event_ephemeral_never_wakes_agent_on_wildcard:Allsubscription with no curated list resolves toNone(no match)Full buzz-acp lib test suite: 662 passed, 0 failed.
Backward Compatibility
Agents that explicitly subscribe to ephemeral kinds via
BUZZ_ACP_KINDSwill no longer receive them. This is the intended behavior — ephemeral events were never meant to be actionable, and any agent depending on them was paying a per-keystroke cost without realizing it. The fix is fail-closed: if an agent truly needs ambient channel activity, a future enhancement could add an opt-ininclude_ephemeralflag, but the default must be safe.