Repository navigation
Mention picker lists locally-managed agents instead of channel members, so an agent running on another machine can never be @-mentioned #3204
Description
Describe the bug
The @ mention picker does not build its candidate list from the channel's members. It appears to be scoped to the agents that that Desktop install manages locally, which produces two symptoms at once in the same channel:
- It offers an agent that is not a member of the channel — selecting it sends a
ptag to a pubkey the channel has never contained. - It omits an agent that is a member of the channel — there is no way to mention it at all.
The practical consequence: an agent whose runtime lives on a different machine can never be @-mentioned. Its private key lives in install A's Desktop, so install B's picker will never list it, no matter that it is a bot member of the channel and actively posting in it. There is no setting to change this.
This is adjacent to #2423 (which lists "mention autocomplete should resolve only current channel members" under Expected behavior) and #2648 (which explains where the duplicate identity comes from), but neither demonstrates the candidate-source defect directly. The two-channel control below isolates it.
To Reproduce
- Install Buzz Desktop on machine A. Create an agent — call it
Claude, pubkeybf0e814b…— and let it run there. - Add
bf0e814b…as abotmember of two channels, #alpha and #beta. - Install Buzz Desktop on machine B, same community, same user. Per [Bug] Second Desktop install creates duplicate agent identities, inflating channel membership #2648 it mints its own agent identity — here
6285b97a…, whose relay profile name is the persona-template defaultFizz. It is abotmember of #alpha only. It has never completed setup and cannot respond. - On machine B, open #beta and type
@claude.
Expected behavior
The picker offers Claude (bf0e814b…), the only agent member of #beta. If a locally-unmanaged agent cannot be mentioned for some architectural reason, the picker should say so rather than silently substituting a different pubkey.
Actual behavior
The picker offers Fizz — 6285b97a…, which is not a member of #beta — and does not offer Claude, which is. Accepting the suggestion sends a mention to an agent that is not in the channel and cannot reply.
Supporting material
Relay-side member lists, buzz channels members (channel UUIDs redacted, pubkeys truncated):
#beta bf0e814b… role=bot <- Claude, machine A
fc4907bc… role=owner <- human
#alpha bf0e814b… role=bot <- Claude, machine A
fc4907bc… role=owner <- human
6285b97a… role=bot <- Fizz, machine B
6285b97a… does not appear in #beta's roster, yet it is what #beta's picker offers on machine B. bf0e814b… does appear, and is not offered. Membership cannot be the candidate source.
Profiles, buzz users get:
bf0e814b… display_name: "Claude"
6285b97a… display_name: "Fizz"
A secondary confusion, and probably the same root as #2423: machine B's Desktop agent list shows this agent as Claude (the user renamed it locally), while its kind-0 profile still says Fizz. The rename never republished the profile, so the sidebar and the picker disagree about the same keypair — which makes the wrong suggestion look like the right one until you compare pubkeys.
Suggested fix
- Derive candidates from the channel's current member list, then intersect with (or annotate by) locally-managed agents — not the other way round.
- Never emit a
ptag for a pubkey that is not a member of the channel being posted to. - Disambiguate duplicates in the dropdown by relay profile name plus a pubkey prefix, since default persona names collide by construction ([Bug] Second Desktop install creates duplicate agent identities, inflating channel membership #2648).
- Renaming an agent locally should republish its kind-0 profile, or the picker should render the local label so the two never diverge.
Environment
- Buzz Desktop 0.4.26 (both machines), macOS (Darwin 25.5.0)
- Self-hosted community, two Desktop installs, same user
- Agent runtime: buzz-acp
Activity
whotookmylogin commentedon Jul 29, 2026
Adjacent data point from Desktop 0.5.0 (macOS aarch64, self-hosted relay), which suggests the picker's candidate list and its membership check draw from different wrong sources:
Setup: second Desktop install, same community, signed in with the owner key (import). The agent in question runs headless on another machine via the
buzz-acpharness (OpenCode ACP adapter) — so it is not locally managed on either Desktop install — and is abotmember of one channel. Relay-side its pubkey is present in that channel's kind-39002 member event (twoptags: owner + agent), and the channel header's member count includes it.Observed in that channel's picker:
ptag, agent wakes and replies. The badge is purely cosmetic in this configuration.@nameas plain text without accepting the popup delivers noptag and the agent never wakes — same footgun as Mobile: DM omits recipient p-tag unless a mention is picked, so agents are never woken #2835, made worse here because the misleading badge discourages users from accepting the popup entry that would work.So on 0.5.0 there appear to be two separable defects: the candidate list scoped to locally-managed agents (OP's case), and the membership badge checking the wrong source even when the candidate list is right (this case).
matthewdcage commentedon Jul 29, 2026
Additional data point — duplicates from team deploy worsen picker ambiguity
Your two-channel control isolates the candidate-source bug clearly (local managed list vs channel members). Adding one related mint path still on upstream
mainthat inflates same-name pubkeys on a single install:#3639 —
AddTeamToChannelDialog.tsxsetsforceNewInstance: trueon every Deploy team to channel persona, bypassingfindReusablePersonaAgent(). Repeated deploys across channels gave us ~24 in-channel bot pubkeys vs 12 running instances (Fizz ×4, etc.) before cleanup.That is the same default persona name collision class as #2648 but without a second machine — it makes the picker/membership confusion in your repro more likely.
Scope boundary
A fix for this issue still requires deriving mention candidates from channel members (as you and @whotookmylogin note — including the separate “not in channel” badge defect on 0.5.0). Removing
forceNewInstancedoes not changeuseMentions.ts; it only stops minting new same-name pubkeys going forward.We are not duplicating the #2149 /
isAgentIdentityInManagedListanalysis already covered in #2508 — linking #3639 ↔ #3204 for triage.DongwonGim commentedon Jul 30, 2026
Another self-hosted data point, and an interaction with #2857 that makes this a trap rather than an inconvenience
Setup: self-hosted relay, five headless agents running under
buzz-acpon a Linux server, none of them locally managed by any Desktop install. All five arebotmembers of the channels they serve and have published kind:0 profiles. Desktop on macOS. Same symptom as the OP, and as #2508.Line references below are against main
63496cc.1. The workaround for the duplicate-start bug is exactly what makes the agent unmentionable
This is the part I have not seen stated anywhere, and it is what turns two separate defects into a dead end.
With #2857 unmerged, the practical way to stop Desktop from double-answering an externally hosted identity is to keep that identity out of the local
managed-agents.json. ButisAgentIdentityInManagedList(useMentions.ts:249) makes presence in that same file the requirement for the identity to be mentionable at all.So for a self-hosted agent you can have:
There is no third option in a shipped build. We hit both sides of this within 48 hours.
I can confirm the #2857 description directly. When we restored our five records with
is_active: falseandstart_on_app_launch: false, Desktop still spawned one on mention-send.agents/agent-pids/<pubkey>.jsonwas created at the moment of the mention, and the reply came from the Mac rather than the server. Neither auto-start path (restore.rs:168,runtime_commands.rs:471) is involved, since both filter onstart_on_app_launch. It is the mention-send preflight, as #2857 says.2. Timeline, which may explain why some reporters attribute this to an app update
Ours looked like a regression introduced by an update, and it was not. From backup file timestamps:
managed-agents.json@The running app apparently keeps the managed list from process start, so editing the file has no visible effect until the next launch. That delay makes the eventual breakage land on whatever restart happens to come next, which is usually an update. Worth keeping in mind when triaging reports that say "broke after upgrading to X".
For the record, the gate is not new: it arrived in #2149 (
a4dfead2, 2026-07-20) and first shipped inv0.4.20the same day. I checked each tag fromv0.4.10throughv0.5.2, and every release fromv0.4.20onward contains it atuseMentions.ts:249. Upgrading does not help.3.
role != "bot"is not a workaround, because NIP-OA alone trips the gateThe existing reports focus on the
role == "bot"path. There is a second, independent one that matters for anyone following the NIP-OA owner attestation guidance:So any identity whose kind:0 carries a valid
authtag isisAgent: truein the picker, regardless of channel role. Our five agents are in this state and the relay has verified the tags (users.agent_owner_pubkeyis populated for all five).The practical consequence: attaching an owner attestation, which is the documented way to make the app show who owns an agent, is also sufficient on its own to remove that agent from the mention picker. Dropping
role=botdoes not recover it. You would have to also drop theauthtag, at which point the app showsowner unavailableinstead. Owner attribution and mentionability are currently mutually exclusive for an externally hosted agent.4. All four candidate sources funnel through the same gate
addCandidate(useMentions.ts:244) is the only way into the candidate map, and the gate is its first statement. The four producers are:isAgentis set:295)role=="bot" || profile.isAgent || ...:334)true:349)true:365)user.isAgent || ...Two things follow. First, typing the full name to force a global search does not surface it either, so there is no user-side escape hatch. Second, the relay-directory branch at
:334is unreachable in practice: it inserts candidates withisAgent: true, which the gate then drops unless the same pubkey is also in the local file. That in turn makesgetMentionableAgentPubkeys's relay branch,directoryAgentPubkeys, and theshouldHideAgentFromMentions"Option B" logic dead for exactly the population they were written for. The comment in that function states the intent:"Not in directory => show" is the stated intent, and the preceding gate implements "not in local managed list => hide".
5. Two adjacent observations while in this code
relayAgentIsSharedWithUserhas noowner-onlybranch. It returns true only forallowlistcontaining the caller, oranyoneplus a shared channel.owner-onlyis a valid value of the enum (RelayAgentInfo) and is the settingcrates/buzz-acp/README.mdrecommends when first attaching an agent, so those agents can never reachmentionableAgentPubkeyseven once the gate above is fixed.Nothing in-tree publishes kind:10100 except one CLI subcommand. The only writer I can find is
cmd_set_add_policyincrates/buzz-cli/src/commands/channels.rs, which emits{"channel_add_policy": policy}and nothing else.buzz-acpandbuzz-desktopcontain no reference tochannel_add_policyat all. On our relay the directory is empty (0 events of kind 10100), which is consistent with there being no publisher for an agent that was not created through Desktop. If the relay directory is meant to be the discovery source for externally hosted agents, something needs to populate it, and the harness looks like the natural place.6. Workaround that does not depend on Desktop, for anyone else stuck here
Since the blocked step is the client attaching the
ptag, one option is to stop requiring theptag on the agent side.buzz-acpconfig mode filters can read the message body:Verified on our relay. The sent event carried no
ptag at all and the agent still woke and replied:--respond-to owner-onlyis a separate author gate and still applies, so this does not open the agent up to everyone.Two notes on it. Desktop puts the literal
@Nametext in the body even when the mention was picked from the popup, so a single rule covers both the picked case and the typed case. And this also sidesteps the footgun in #2835 and in @whotookmylogin's comment above, where typing@nameas plain text produces noptag and the agent never wakes. The cost is that the subscription widens from mentions to allkind:9in the agent's channels, which is not free on a busy relay.This is a harness-side workaround, not a fix. The candidate-source change the OP proposes is still the right one.
Environment
ghcr.io/block/buzz:mainbuzz-acpwithclaude-agent-acp,--respond-to owner-only, on a separate Linux hostevents,channel_members, andusersmotox23 commentedon Jul 30, 2026
Additional data point from Buzz Desktop v0.5.2 on two Macs using the same owner identity:
On the secondary Mac,
managed-agents.jsoncontains seven account-synced definitions whosepubkeyvalue is an empty string:The only record on that Mac with a non-empty local pubkey is a separate laptop-local agent named MBA (
0dabb74e…52c12).In a channel where the relay roster confirms Wrench (
686b3e44…967e) and Bumble (7cba9463…0405) are bot members, typing@on the secondary Mac shows the synced Wrench and Bumble definitions as “agent not in channel.” It does not offer the actual channel-member identities.This suggests the picker is admitting the account-synced definition rows even though they carry no usable pubkey, while the real remote member pubkeys are filtered out. An empty string is distinct from a missing/null value, so any eligibility or deduplication check that only tests for nullish
pubkeymay allow these bodyless rows through.We did not select one of those rows because that could enter the duplicate-mint path described in #3414. Merely launching the secondary client did not change relay identity counts.
daguiler76 commentedon Jul 31, 2026
Additional reproduction on Buzz Desktop 0.5.2, macOS, with a narrower
control: this happens on a single Desktop installation even when the omitted
agents are locally managed, running, and subscribed to the channel.
Setup
botmembers: Grok, Claude, andCodex.
Actual behavior
Opening
@in the channel composer:managed by you · not in channel.Typing
@codexmanually produces only visible text. The published kind:9 hasthe channel
htag but no Codexptag, sobuzz-acpis never triggered.The same Codex identity remains reachable through
Channel members → Codex → DM, and the DM works. Restarting Desktop does not
change the picker.
Local-state control
The installation's
managed-agents.jsonhas 16 records:display_nameandslug, butpubkey: "";pubkeyandpersona_id, butdisplay_name: nullandslug: null.Every running instance's
persona_idmatches its persona definition'sslug.All eight running instances are missing
display_name.For Codex specifically:
to the affected channel;
No full pubkeys, channel IDs, relay URLs, auth tags, or credentials are included
here.
Interpretation
This looks like the picker is admitting persona-definition rows without first
joining them to their running instances, while membership is keyed by the
instance pubkey. The empty-string persona pubkeys then cannot match the channel
roster, and the valid in-channel instances are omitted.
The join available in the local state is:
Mention candidates should ultimately be keyed by the channel-member instance
pubkey; the visible label can come from the joined persona
display_nameorthe instance
name.This is consistent with the secondary-Mac data point already reported here,
but confirms the defect does not require a remote/headless agent or two
Desktop installations. It also affects Desktop's own active local instances.
fix(desktop): resolve mentions from channel members [issue:block#3204]
test(desktop): cover mention allowlist visibility [issue:block#3204]
rudymccomb commentedon Aug 3, 2026
Another confirmed reproduction on Buzz Desktop v0.5.3, macOS, self-hosted relay, two Macs sharing the same owner identity (agents' bound records live on Mac A; Mac B is effectively a client).
Relay-side state is fully correct — verified directly in the relay DB:
channel_memberslists the agents as members of the channels in question, and the current kind:39002 replaceable events match exactly (agents taggedbot), e.g. one channel with justowner+ onebotmember.buzz-acpruntime is up on Mac A, presence online) and actively posting in the same channel.Symptoms on Mac B, both from the same defect:
pubkey: "") with the same display names, the picker offers those dead templates labeled "agent not in channel" — while the real, in-channel, online instances are absent (matches Mention picker fails to coalesce local persona definitions with their running channel-member instances #3947's coalescing angle).@+ prefix yields no candidates whatsoever, despite that agent being one of the channel's two members and the newest poster in it.Our agents run
respond_to=owner-only, so this also lines up with #4535's eligibility analysis (noowner-onlybranch, and kind:10100 cards never receiverespond_toupdates) — i.e. no user-facing setting can work around it. Mentioning works only from the machine that holds the bound records; thread replies are the only cross-device path to reach an agent.🤖 Generated with Claude Code
Tasuku0127 commentedon Aug 4, 2026
Adding a concrete cross-client evidence point. In the same channel and thread, sending the identical typed text
@general (fable)(an agent running only on a separate always-on host) from different clients produced different tags:p=[]— no p tag, so the mention was inert plain text and the agent was never notified.p=["864def9c…0810d63"](the agent's real pubkey) — a real mention that delivered.So mobile/web already resolve the picker against channel membership; only Desktop scopes candidates to locally-managed agents. This makes the "agents on a 24/7 exec host + human opens Desktop on a second machine" setup silently broken, with no error or fallback. +1 to the suggested fix: derive candidates from the channel member list and never emit a
ptag for a pubkey that is not a member.eireal commentedon Aug 4, 2026
Another reproduction on Buzz Desktop 0.5.3, macOS + Windows 11, two machines under the same owner identity, two self-hosted communities (
leevia.andknowhow.communities.buzz.xyz). Relay-side state is correct; the defect is entirely local. Two data points I have not seen in this thread yet.1. The local agent registry stores synced agents with an empty
pubkey~/Library/Application Support/xyz.block.buzz.app/agents/managed-agents.jsonon the Mac holds 13 records. The 8 created on this machine carry a realpubkey(and, for community-bound ones, arelay_url). The 5 that arrived by sync from the Windows machine carry"pubkey": ""and"relay_url": "":So multi-device sync propagates the agent definition but not its key. If the picker is sourced from this file — as the title of this issue states — then on each machine the remote agents are present by name but unmentionable by construction: there is no pubkey to attach. This is symmetric, and it explains why the failure is total rather than intermittent.
Relay-side, by contrast,
buzz channels memberslists every one of those agents withrole=botin the channels concerned. Nothing is missing on the relay.2. The
nostr:npub…escape hatch does not work eitherThe obvious workaround — bypass the picker and paste the bech32 URI into the composer — also produces an inert message. Sent from Buzz Desktop into a channel where the target agent is a
botmember:Event
c05c168fe4efd22d75cb8dd122daf535b6412dfc40071dcaaf6a53dbae6bcf38, 2026-08-04T14:05:44Z. Noptag. The composer does not resolve a pastednostr:npub…URI into a mention, so the agent was never woken. (The URI was inside**…**bold markers here; I have not isolated whether unwrapped text behaves differently.)The practical consequence for a two-machine owner is that there is no path from the client machine to an agent hosted on the other one: not the picker, not a pasted npub. The only channel that still works is a DM, where the recipient
ptag is added automatically regardless of the picker.zacklavin11 commentedon Aug 6, 2026
Another confirmed reproduction, macOS, hosted community relay.
Setup: whole agent fleet (6 agents) migrated from Desktop-managed on machine A (MacBook, Desktop-spawned seats) to a headless Mac mini running
buzz-acpunder a separate user account — same keypairs transplanted, so relay-side nothing changed: every agent still has its kind-0 profile and is abotmember of the channels concerned (buzz channels membersconfirms), all actively replying.Result on the original machine's Desktop: the
@picker now offers no agents at all in any channel — the local managed-agents store no longer holds them, so they are dropped before eligibility runs, exactly as described above. Attempted composer sends reach the relay withp=[].What still works, consistent with the other reports here:
buzz-cli messages send --mention <pubkey>delivers with a correct receipt.+1 to deriving picker candidates from the channel's member list and intersecting/annotating with the local store, rather than gating on it. For a multi-machine or headless-fleet setup there is currently no Desktop path to mention a channel-member agent.
🤖 Generated with Claude Code
fix(desktop): enforce shared agent access across devices (block#6086)
fix(desktop): enforce shared agent access across devices (block#6086)
fix(desktop): enforce shared agent access across devices (block#6086)
fix(desktop): enforce shared agent access across devices (block#6086)