Repository navigation
Description
Describe the bug
A managed agent can only be @mentioned from the machine that runs it. From any
other client on the same relay, the agent produces no autocomplete entry, so the
sent message carries no p tag and the agent's harness never dispatches. The
message lands in the channel as plain text and nothing happens.
There is no error and no UI signal that this is unsupported — the agent shows in
the member list, its kind:0 profile resolves, and it replies normally when
mentioned from its host machine. The failure is indistinguishable from a
misconfiguration, which makes it expensive to diagnose.
This blocks the "one shared agent on an always-on host" setup: today every person
has to run their own copy of every agent, producing duplicate identities with
duplicate display names.
Steps to reproduce
- Point two Buzz clients (A and B) at the same relay.
- On A, create a managed agent, add it to a channel, set
respond_toto
anyone, and start it. Confirm it is abotmember of that channel. - On A,
@mentionthe agent in that channel — it autocompletes and replies. - On B, open the same channel and type
@followed by the agent's name. - No autocomplete entry appears. Sending the text anyway produces an event with
noptag, and the agent never runs.
Expected behavior
The agent appears in B's autocomplete and is mentionable, producing the same
p-tagged event that A produces.
Version and platform
- Buzz version: v0.4.26 desktop (also reproduces on current
main) - OS: macOS
Logs / additional context
Two independent causes, either of which is sufficient on its own.
1. The desktop app never publishes the kind:10100 agent profile it reads back.
list_relay_agents queries {"kinds":[10100]} unfiltered, and
agents_from_events parses the result into RelayAgentInfo. But no code path in
desktop/src-tauri ever publishes a 10100 event — the only publisher in the
repo is buzz channels set-add-policy in the CLI, which writes only
channel_add_policy. The relay does not synthesize one either;
handle_agent_profile just reads channel_add_policy off it as a side effect.
So relayAgentsQuery.data is empty for any desktop-created agent, and the entire
relay-agent branch of getMentionableAgentPubkeys is unreachable in practice.
2. isAgentIdentityInManagedList drops every non-locally-managed agent.
Added in a4dfead ("fix(desktop): prefer live agent mentions", #2149), the gate at
useMentions.ts addCandidate runs before shouldHideAgentFromMentions and
discards any candidate with isAgent === true whose pubkey is not in the
locally managed set. A remote agent that is a bot channel member enters via
the members loop with isAgent set from member.role === "bot" and is dropped
there.
First released in v0.4.20; git diff v0.4.26 HEAD on useMentions.ts and
agentAutocompleteEligibility.ts is empty, so current main behaves the same.
On a fix
I prototyped against the real eligibility functions. No single-signal change
works — each one either fails to unblock the remote agent or un-hides an identity
that your existing tests require to stay hidden:
| must be | swap gate to mentionable |
bypass gate for members |
|
|---|---|---|---|
mira — agent-flagged profile, channel member, no runtime |
HIDDEN | HIDDEN | SHOWN ✗ |
quinn — relay-only, allowlists the viewer, not a member |
HIDDEN | SHOWN ✗ | HIDDEN |
remote agent — directory-backed, respond_to: anyone, bot member |
SHOWN | SHOWN | SHOWN |
| locally managed agent | SHOWN | SHOWN | SHOWN |
(mira and quinn are the existing fixtures behind "own profile-only agents
are hidden from channel mentions" and "relay-only agents stay hidden from
channel mentions even when allowlisted".)
Two rules satisfy all four rows:
- Gate on managed OR (in the directory and invocable by me and a
member of a channel I share); or - require channel overlap in both branches of
relayAgentIsSharedWithUser—
its allowlist branch currently ignoreschannelIdsentirely — and then gate on
the resulting mentionable set.
Either way, cause 1 has to be fixed first: with no 10100 record published, every
rule that keeps mira hidden also keeps the remote agent hidden. The client
change alone is a no-op.
I read #2149 as deliberate rather than accidental — it is solving real clutter
from stale and duplicate agent identities using the only liveness signal
available at the time ("does this machine run it"). Publishing the directory
record supplies a better signal, so the filter can keep its intent without
excluding remote agents.
Happy to open a PR for either or both halves if you'd like — flagging first per
CONTRIBUTING.
Related but distinct: #1743 covers a resolved mention not being delivered to an
offline agent; this is a mention that cannot be resolved at all from another
device.
Activity
surfingdev commentedon Jul 28, 2026
This matches what we independently traced while chasing the same symptom from the other side — cross-member @mention, not cross-machine, but same two causes. Our case (managed relay, Buzz Desktop 0.4.26/macOS) is in #2950 (comment: #2950 (comment)), and we'd separately flagged the missing kind:10100 publish path in #2987.
Your repro is exactly our real-world blocker: we run one shared agent per role (landing, support, etc.) on an always-on host, and every other member/agent on the team is unable to @-mention it from their own machine —
respond_to: anyonehas been set for over a day with zero observable effect, consistent with your point that the client change alone is a no-op without the 10100 publish.The two-rule fix design here (gate on managed OR (directory AND invocable AND channel-shared)) looks like the right shape to us too, and it's the only proposal we've seen validated against the existing
mira/quinnfixtures rather than just eyeballed. If nobody's picked this up in the next day or two we're glad to build it to this design and open the PR — flagging that here per CONTRIBUTING so we don't duplicate effort if you're already writing it. Let us know either way.(Separate but related on our end: even where the
ptag ships correctly, we're seeing the harness ignore non-owner mentions receive-side — #2860/#2747/#3125. Worth keeping in scope when this lands so the fix doesn't just move the failure from "not in the picker" to "in the picker, still ignored.")rrickels commentedon Jul 28, 2026
Confirming this still reproduces on v0.5.0 (both clients updated), and adding relay-layer evidence plus the business case for prioritising it.
Same shape as reported: headless
buzz-acpagent on an always-on mini-server, systemd-hosted under an unprivileged service account, owned by a dedicated "Agent Operations" identity,respond_to = anyone,botmember of the shared channel. It answers perfectly when mentioned from the owning account and is completely un-mentionable from every other member's client.The data the client needs is already on the relay and readable
I authenticated to the relay directly over NIP-42 as two identities and ran identical
REQs, to check whether this is a data-availability problem or purely client-side gate ordering. It is purely the latter:kind:30177(instance, carriesrespond_to)kind:30175(persona){"name":"…","persona_id":"…","parallelism":4,"respond_to":"anyone"}So a non-owner client can read
respond_to: "anyone", and the agent is a channel member alongside that user. Both halves ofrelayAgentIsSharedWithUserare satisfiable with data already in hand — the candidate is just dropped byisAgentIdentityInManagedListinuseMentions.tsbefore that logic is ever consulted, exactly as #2987 and #2603 describe.One refinement to the workaround in #2987
#2987 frames it as an either/or on
kind:10100("no 10100 → mentionable as a plain member"). In our case that wasn't sufficient: the agent had nokind:10100at all and was still un-mentionable, because the NIP-OAauthtag on itskind:0profile is enough on its own to setisAgent = trueand trip the managed-list gate. #2603's phrasing ("stripping the agent's auth tag/10100") is the accurate one — thekind:0auth tag has to go too. Worth noting since the 10100-only workaround will look broken for anyone whose profile carries the tag.We also tried publishing a
kind:10100shaped identically to one from an agent that already had a working record, and separately opting the persona into community visibility with["shared","true"]per NIP-AP. Both were verified effective at the relay layer (a non-owner could then read the persona) and neither changed mention eligibility — consistent with the gate ordering being the sole cause.Why this matters commercially
The setup this blocks is, I think, the natural one for any company adopting Buzz:
One "Agent Operations" identity on one always-on machine (or VM) owns and administers the org's agents, and everyone in the company uses them. Agent Operations owns the shared context — the knowledge base, the tool access, the system prompts, the model configuration — and every employee talks to the same agent with the same grounding.
That single-owner model is what makes agents trustworthy in a company setting:
The per-user-copy pattern the current gate forces isn't just inconvenient — it inverts the security model, because the thing you must hand out is the agent's identity key.
Everything else in the stack already works well: the ACP harness integration (we're on the new BYOH Hermes preset from #2773), NIP-OA owner attestation, relay membership, channel propagation, presence, and the agent's actual answers with accurate citations. The single missing piece is that nobody but the owning device can address the agent.
Happy to test a patch against this setup — we have a reproducible headless deployment and can run against
main.docs(remote-agents): note the observer needs a resolved owner
docs(remote-agents): note the observer needs a resolved owner
safcbee commentedon Jul 29, 2026
Confirming this on Buzz Desktop v0.5.0 with a distinct same-owner setup: both Macs are paired to the same human identity, joined to the same community, and viewing the same channel.
Reproduction
@picker.@autocomplete.Second-install interaction
The second install initially minted another Fizz/Honey/Bumble trio with fresh identities and blank provider/model settings. That produced seven channel members and ambiguous names. After archiving only those three duplicate identities, the roster returned to the expected four members (one human plus the original working trio), but the MacBook still could not mention the original agents.
This links the user impact here to #2648: duplicate agents are not only roster clutter; they are an easy trap when the real, remotely hosted agents cannot be addressed from a paired device.
Expected behavior
A paired device should be able to select an owner-managed agent that is already a member of the channel, attach its existing pubkey as a Nostr
ptag, and send the message to the runtime hosted on the other Mac. It should not import/sync the private key, create a secretless local managed-agent record, or attempt to start the agent locally. A UI marker such as “Hosted on another device” would make the lifecycle boundary clear.The current behavior is explicit in v0.5.0:
Keeping secrets device-local is correct; the missing capability is remote mention/invocation without local lifecycle ownership. This is a key everyday workflow: an always-on Mac mini hosts the agents while a MacBook or iPhone invokes the same identities in shared channels.
chinglinwen commentedon Jul 30, 2026
Confirming on v0.5.0 (two Macs, different identities, same community), and adding three findings plus the stopgap we're running.
1. Mobile already behaves the way this issue asks for — the two first-party clients disagree. The Flutter client builds mention candidates straight from channel members with no managed-list gate (
mobile/lib/features/channels/mentions/mention_candidates.dart), andrankMentionCandidatessorts remote agents below humans rather than dropping them (mention_ranking_test.dart: "channel members outrank people and other agents"). The same remote agent that is unmentionable from desktop is mentionable from Buzz mobile on the same identity — a usable workaround today, and a sign the desktop gate is the outlier rather than the intended product rule.2. The DM escape hatch only works for the owner. The third comment above notes DMs from the paired secondary client work — that holds because a paired device is the same identity. For any other member it does not:
author_allowedincrates/buzz-acp/src/lib.rshardens DMs to the owner and cryptographically verified same-owner siblings, andanyone/allowlistexplicitly do not apply inside a DM. Flagging so readers don't chase DMs as a general workaround.3. Refinement to "the client change alone is a no-op." On a relay with zero
kind:10100records, reverting only the a4dfead gate does restore the candidate — the agent re-enters via the members loop withisAgentset fromrole === "bot", and reappears in the picker. Not shippable, since it also un-hides the profile-onlymirafixture. But it shows cause 2 is independently sufficient to produce the symptom, and that the 10100 publish is needed specifically to keepmirahidden while un-hiding the remote agent — consistent with the two-rule design proposed above.Stopgap: local patched build
For anyone blocked today, a local build of
v0.5.0with the gate reverted restores the picker entry. Reverse-apply the a4dfead hunks indesktop/src/features/messages/lib/useMentions.tsanddesktop/src/features/channels/ui/MembersSidebar.tsx(the latter so the add-member picker stops filtering remote agents too) —git show a4dfead2 -- <those two files> | git apply -R --3wayapplies cleanly on the tag. Typecheck is clean and all 3721 desktop unit tests pass; the e2e cases that assert the gate fail by design. Branch: https://github.com/chinglinf/buzz/tree/local/mention-gate-relaxThree build gotchas that cost us time:
just desktop-release-buildonlytouches 0-byte sidecar stubs, so the app it produces cannot run managed agents at all. Do whatrelease.ymldoes first:cargo build --release -p buzz-acp -p buzz-agent -p buzz-dev-mcp -p git-credential-nostr -p buzz-cli && ./scripts/bundle-sidecars.sh.beforeBuildCommandshells out to a realpnpmbinary;corepack pnpmin your shell isn't enough, the child process needspnpmonPATH.--identifier xyz.block.buzz.app --entitlements src-tauri/Entitlements.plist, and expect macOS Keychain re-prompts — the ACLs on the stored identity key were bound to Block's Developer ID. The bundle id is unchanged, so app data, identity, and agents survive. Note v0.5.0 shipsendpoints: [], so a build made without the CI-generated release config has no updater and won't silently revert itself.fix(desktop): allow mentioning agents hosted on another machine
chinglinwen commentedon Jul 30, 2026
Having read through the open PRs, they split into two families and I don't think either can merge as written. Sharing the check in case it saves someone repeating it.
Family A — member-based (#2893, #3056): stop dropping agent candidates that are channel members. This does fix the reported symptom with no relay-side change, and it matches what the mobile client already does. But it trips an existing fixture:
mirais a member of the mockgeneralchannel —createMockMember(PROFILE_ONLY_AGENT_PUBKEY, "member", 840)indesktop/src/testing/e2eBridge.ts— andmentions.spec.ts"own profile-only agents are hidden from channel mentions" opensgeneral, types@mira, and asserts no dropdown appears. A never-drop-members rule showsmirathere. Neither PR touches the e2e spec, so this looks like a real conflict rather than an oversight in the test.Family B — directory-based (#2693, #3472, and the two-rule design in the issue body): admit an agent when its kind:10100 record says invocable and channel-shared. This keeps
mirahidden, so it's the better-behaved half — butkind:10100is never published by the desktop, so the set is empty in practice. On our self-hosted relay there are zero kind:10100 events across all agents, which makes the added clause always false and the change a no-op for exactly the case being reported. That matches the "cause 1 has to be fixed first" point in the issue body.The missing dependency. I checked all nine open PRs I could find on this (#2314, #2333, #2514, #2693, #2752, #2893, #3056, #3127, #3472): none of them touches
desktop/src-tauri. So the publisher that Family B depends on is still unwritten, and Family A is blocked on a product question rather than on code:If the answer is "no, membership is enough", Family A merges once the fixture is updated, and the publisher isn't needed for this bug at all. If the answer is "yes, stay hidden", then nothing merges until someone writes the kind:10100 publish path, because membership alone can't distinguish the two cases. One maintainer sentence resolves roughly half the queue either way.
(Caveat on scope: I read the eligibility diffs of #2693, #2893, #3056 and #3472 in full; for the remaining PRs I only ran the mechanical
src-tauricheck, so I'm not characterising their rules.)okhan1980 commentedon Jul 30, 2026
Confirming this on Desktop 0.5.2 (you filed against 0.4.26), so nothing between those releases fixed it. Same shape: five Desktop-managed agents on machine A, each a
botmember of a private channel with Who can talk to this agent = Anyone; a second human on machine B,memberrole, 0.5.2, gets no autocomplete entry for any of them and sends untagged text that never dispatches.Two additions from our repro, detailed in my comment on #2508:
ptag the picker withholds is the entire difference.nostr:npub1…URI in the body produces no mention, alone or paired with an unresolvable@Name(mention_pubkeys: []both times). And an unresolvable@Namedoes not block the send — it goes out untagged, with no warning. So the user gets no signal at all that their mention was inert, which is exactly the "indistinguishable from a misconfiguration" cost you describe.Working around it with per-agent
BUZZ_ACP_SUBSCRIBE=allplus theBUZZ_ACP_KINDSallowlist from #3649 — verified: the remote member's untagged and misspelled message got a substantive reply 22 seconds after the agent restarted with those variables.fix(desktop): offer declaration-less attested agents to their verifie…
4dlt commentedon Jul 30, 2026
Reproduced on v0.4.26 (two Macs, same identity, self-hosted relay). Per @okhan1980 above it still reproduces on 0.5.2, so the affected range is at least 0.4.26 through 0.5.2. That is consistent with what I see in the source:
git diff v0.4.26 HEADis empty on bothagentAutocompleteEligibility.tsanduseMentions.ts, so the gate has not changed at all across those releases.To answer @surfingdev's offer directly: I built the kind:10100 publisher and it works end to end. It is up as #3708 (Rust only, no client-visible behaviour change), so nobody needs to duplicate it. Below are the implementation findings, because they are traps that are not obvious from the spec, and one of them is a security bug in the design as written.
The queue is blocked on a dependency that does not exist
@chinglinwen's mechanical check was right, and it holds on a wider set. I ran
gh pr view --json filesover the 19 open PRs I could find on this (#2314, #2333, #2514, #2605, #2693, #2752, #2893, #3056, #3108, #3110, #3127, #3292, #3448, #3472, #3599, #3609, #3648, #3672, #3676):Every directory-based PR therefore gates on a set that is empty in every real deployment. My relay had zero kind:10100 rows across all agents before this work, matching what @chinglinwen and @rrickels observed.
One more thing that may help triage: of the directory-based PRs, only #3672 touches both
mentions.spec.tsande2eBridge.ts. #2693, #2893, #3056, #3472, #3676, #3448 and #3609 modify the eligibility unit test but leave themirae2e fixture conflict unaddressed.Trap 1: the publish must hook the runtime start path
The natural home is
reconcile_agent_profileindesktop/src-tauri/src/commands/agents_profile.rs, next to the existing kind:0 sync. Putting it there publishes nothing in practice.A managed agent can start four different ways.
reconcile_agent_profileis reached from the UI start command and from boot restore, but the renderer drivesstart_managed_agent_runtime, which callsstart_pairthenspawn_agent_childindesktop/src-tauri/src/managed_agents/runtime_commands.rsand never reconciles anything. That is the common path, and it was skipping the publish silently.I publish from
start_pairinstead, immediately after the child spawns, so the record lands no matter which path started the agent. This took me three build-and-deploy cycles to find, because the harness log tells you nothing here (see Trap 3).Trap 2:
channel_add_policywill silently widen permissionsThe spec says to include
channel_add_policybecause the relay's side-effect handler requires it. That handler writes the value straight into the users table:So publishing a hardcoded value resets a deliberate
owner_onlyornobodyto whatever you send, on every agent start. Anyone implementing this should read the agent's prior kind:10100 back and re-send its policy verbatim, falling back to the schema default (anyone, permigrations/0001_initial_schema.sql) only when there is no prior record. That fallback is a genuine no-op for a never-configured agent.Trap 3: two inverted guards, and invisible turn logging
managed_agent_profile_reconcile_enabledstores the negation of theagentManagedProfilesexperiment (seeagent_settings.rsandworkspace.rs), and it is checked in two places: insidereconcile_agent_profileand again at thestart_managed_agentcall site. With that experiment enabled, which is a normal user configuration, reconciliation never runs at all. The field name reads as "reconcile is on" while the value means the opposite.My reading is that this guard should not apply to the directory record. It governs whether the desktop or the agent owns kind:0 profile metadata, whereas kind:10100 is discovery, and nothing else in the product publishes it. Gating discovery on that experiment makes remote agents unmentionable for exactly the users who opted in. Happy to be corrected if that was intentional.
Separately, and this cost real diagnostic time: the harness logs nothing about turns in any default deployment. Mention dispatch, turn start and turn stop are all invisible, because the custom targets in
buzz-acpare bare (pool::prompt,acp::stream) andEnvFiltermatches by path segment, so the shippedbuzz_acp=infofilter drops them. RaisingRUST_LOGdoes not help. #3648 fixes this by prefixing all 19 targets, and it is worth landing early purely as an observability unblock for everything else in this area.Trap 4: the record discloses private and DM channel ids
channel_idscomes from every kind:39002 membership event tagging the agent. Unfiltered, that includes["private"]["hidden"]["t","dm"]channels, so an agent publishes the UUIDs of its DM conversations into a record the relay serves to any authenticated member. The field allowlist in the spec is careful about secrets, but does not consider that channel membership is itself metadata.Filtering them is not obviously correct either, because
relayAgentIsSharedWithUserdecides invocability from channel overlap, so dropping DM channels would break mentioning an agent from inside a DM. I have left them in and raised it on the PR rather than deciding it alone, but anyone implementing this should make that choice consciously.Verified against a live relay
Querying the relay's event store directly after an agent start, on a 0.5.2 build from current
main:{ "name": "Tester", "agent_type": "goose", "status": "online", "respond_to": "anyone", "respond_to_allowlist": [], "channel_ids": ["<general-uuid>", "<uuid>", "<uuid>"], "channel_add_policy": "anyone", "capabilities": [] }The record is signed by the agent's own key, which matters because the client overwrites the content pubkey with the event author, so identity comes from the signature rather than a self-declared field. It carries no nsec, no NIP-OA auth tag, no environment variables and no runtime configuration, all pinned by tests.
The client half then behaves as the two-rule design predicts: mentionable = locally managed, or in the directory and invocable and sharing a channel.
miraandquinnstay hidden with their existing assertions untouched. The payoff, from the relay's own log:ptag@Tester hi(after the fix)@Tester hello(before the fix)Same author, same channel. The harness now acknowledges within one second where previously nothing happened at all.
One fixture change this design requires
mentions.spec.ts"@ trigger prioritizes channel members before runnable personas and other managed agents" asserts thataliceis absent. ButaliceindefaultMockRelayAgentsisis_agent: true,respond_to: "anyone",status: "online", and a member of the channel under test, which is exactly the shape this issue asks to be mentionable.git log -Sshows that assertion was introduced by a4dfead itself, the same commit that added the gate, so it encodes the pre-fix semantics rather than independent prior behaviour. Under the two-rule design it flips totoBeVisible().The two assertions people are actually worried about, profile-only
miraand relay-only allowlistedquinn, pass unchanged. I am flagging this because it is the kind of change that should be deliberate rather than discovered during review.On @surfingdev's receive-side warning
That was worth flagging, because it is exactly where I landed. With mentions working, my agent acknowledges with 👀 💬, runs a turn, emits a
kind:44200turn metric, retracts its own working indicator, and posts nokind:9. It behaves identically when mentioned from the owning machine, so it is independent of anything in this issue.@okhan1980's control is the useful discriminator here, and it rules out the explanation I had assumed:
So a correctly tagged non-owner mention does dispatch and answer. My silence is not the author gate and not a missing tag; the tag is present and the harness acks within a second. It matches #3648's dead turn instead: "ends
end_turnhaving streamed no assistant text and completed no tool call … the mention is dequeued as handled, the retry machinery never engages." Two of my turns ended at 4s and 37s, both emitting a metric and no message.This issue and #3648 are independent, and both are needed before a shared agent actually works. One practical consequence for anyone verifying a fix for this issue: assert on the
ptag of the sent event rather than on receiving a reply, otherwise a dead turn will read as a failed mention fix.The remaining question
Everything above is implementation detail. The real blocker is the product decision @chinglinwen identified:
If the answer is "no, membership is enough", the member-based PRs merge once the fixture is updated and this publisher is not needed for this bug at all. That also reconciles desktop with mobile, which has no such gate today. If the answer is "yes, stay hidden", nothing merges until a publisher exists, because membership alone cannot distinguish
mirafrom a live remote agent.I am happy to open a PR either way, and would suggest splitting it. The kind:10100 publisher on its own first, since it changes no client-visible behaviour, keeps
mirahidden, and unblocks every directory-based PR in the queue regardless of how that question is answered. The eligibility change can follow once the rule is settled.Maintainers: is that split useful, and is there a preferred answer on the
miraquestion? Happy to hold off if this is already in hand internally.llamallamaredpajama commentedon Jul 30, 2026
Adding a verified cross-owner reproduction from a hosted community, with one additional failure path: Replying directly to a remote agent's own message also fails to attach the agent
ptag.Setup
bot; their kind:0 profiles resolve normally.Signed-event evidence
Typing the remote agent's exact full display name produces a kind:9 event with the channel and thread
etags, but noptag for the agent.We then used the UI's Reply action on an actual kind:9 message authored by that remote agent. The resulting event correctly carries:
etag, andereply tag pointing to the exact agent-authored message,but it still has no
ptag for the author being replied to.Messages from the owning user to the same agent in the same channel do contain
["p","<agent-pubkey>"].The remote agents never dispatch for the non-owner messages, consistent with the harness never receiving a real mention.
Sanitized shapes:
This confirms that access policy is not reached: changing allowlist/Anyone cannot help because Desktop fails earlier while creating the outbound event. It also shows two compose paths need coverage:
A send-time warning for mention-looking text with no resolved recipient would also prevent this from failing silently.
Raw event IDs, channel ID, and pubkeys are omitted from the public report but can be supplied privately if maintainers need them.
lolamathematician commentedon Aug 1, 2026
Confirmed on Windows using the same human Buzz identity on two machines.
A Desktop-managed Claude Code agent is running on the primary PC. From the laptop, the real PC-hosted agent is absent from
@autocomplete. An accidental duplicate agent created on the laptop caused Buzz Desktop to attempt local Claude launch and report that Claude was not installed.Sending through the bundled CLI with the correct agent pubkey works:
buzz messages send --channel <channel> --reply-to <thread-root> --content "@Claude Code …" --mention <agent-pubkey>The PC-hosted Claude receives the message and responds correctly. This isolates the failure to Desktop mention discovery /
p-tag generation. The relay, shared identity, permissions, thread routing and remote runtime are all working.A separately hosted VPS agent, Daphne, is mentionable from the laptop UI. That may be useful as a control because the two agents appear to have different directory/discovery metadata.
Happy to test a Windows release-candidate build. Important regression cases for us are:
Shipitrealgood commentedon Aug 1, 2026
@lolamathematician tracked here from your comment on #3676 .
Produceable build is at Shipitrealgood/buzz@test/external-agent-eligibility covering both mentions and channel adds.
Also noted on comment here - #2987 (comment) in #2987
That said though, I agree with statement of op here.
Namely what is the role of 10100, mobile seems to be consumer of it, but as mentioned writes only at cli.
bayramog commentedon Aug 1, 2026
Confirming on Buzz Desktop 0.5.3 / macOS, with the exact always-on-host topology in this issue: the same human Buzz identity is paired on a Mac mini and a MacBook Pro; one
Hermes Prodidentity is hosted continuously on the mini and is already abotmember of the shared channel.This reproduction adds a dangerous secondary-client failure mode that is worth pinning in tests:
Hermes Proddefinition, selectingHermes Prodfrom@autocomplete did not resolve the mini-hosted identity.ptag for that local duplicate, not the mini-hosted agent.buzz-acpthen initialized local Hermes ACP children and ran the prompt on the MacBook. Because the visible text still contained@Hermes Prod, the centrally hosted Hermes also answered by name, which can mask the ownership bug unless the signed event'sptag and process tree are checked.buzz-desktoponly; nohermes-acp, no Hermes gateway), butHermes Proddisappeared from@autocomplete completely even though the real hosted identity remained an active bot member of the channel.ptag and dispatched only to the Mac mini; the response returned the mini hostname.The source path matches the report:
useMentions.tsdrops the channel-member agent atisAgentIdentityInManagedList(...)before the policy-aware relay eligibility path can admit it.A regression test should cover the same-owner, second-device case and require all three invariants:
ptag; andstart_managed_agent/ local runtime command is issued on the invoking device.PR #2514's E2E shape (
routes its pubkey without starting it locally) is particularly close to this production failure. The extra assertion I would add is that no local keyed or keyless definition is required for the candidate to appear; preserving a keyless definition is enough to rematerialize the wrong local execution owner.ahmadashfq commentedon Aug 4, 2026
Confirming on Desktop 0.5.4 (extends the confirmed range past 0.5.3), with the same-owner topology from the OP: one human Buzz identity paired on a Mac mini and a MacBook Pro, all Desktop-managed agents hosted on the mini.
From the MacBook Pro, the channel mention picker offers only that machine's local starter agents; none of the mini-hosted agents appear, despite being
botmembers of the channel with clean, unique kind-0 profiles. The mobile app's member-list-driven picker tags the same agents from anywhere without issue, consistent with earlier comments here.Also confirming the reply path an earlier comment raised: replying directly to a remote agent's message does not attach the author p-tag, so replying is not a workaround (cf. #2270, #4535). The only cross-machine path that works for us today is mobile + explicit mention.
(Posted from my account by my Buzz agent, at my request.)
DocAwk commentedon Aug 8, 2026
The core promise is currently vaporware, and that deserves to be said plainly.
Buzz is marketed as "Slack for humans and AI agents" — the whole pitch is that an agent is a durable identity on the relay, and any client on any machine can talk to it. That's the reason the app exists. But the shipping desktop build makes that impossible in the most common case: an agent hosted on one machine is invisible and unaddressable from a second machine, with no error and no UI signal — the message posts, looks correct, and silently carries no
ptag, so the agent never runs.This isn't a niche edge case. It's the difference between "the app does what it says" and "the app doesn't." Shipping a product whose headline feature fails silently in the default two-machine setup is a product/positioning failure, not a bug report. It's like releasing a car engine and listing "powering a vehicle" as a 2.0 feature.
Concrete repro (verified, current build):
botmember of the channel withrespond_to: anyone.@agentautocompletes, sends ap-tagged event, agent replies.@agentsends without aptag — agent never notified, never runs. No warning anywhere.I understand remote agents are spec-in-review and the fix is in flight (#5084, #4999). I'm not asking for a timeline. I'm asking that the product not be marketed as functional for a use case that silently fails — and that a send-time warning ("no recipient was addressed") ship as a minimum, because right now this is indistinguishable from a misconfiguration and costs users hours to diagnose.
Happy to validate a release-candidate build on the exact two-machine topology (Mac mini host + laptop client) if that helps the fix land.
fix(desktop): let channel-member agents reach the mention picker
fix(desktop): let channel-member agents reach the mention picker
hondoaus commentedon Aug 15, 2026
Adding a current field report from a self-hosted Buzz + external Hermes Agent setup.
We reproduced the remote mentionability problem, then got the agent into Desktop autocomplete without creating a local managed-agent record by publishing a complete agent-signed kind:10100 record.
One important detail from current Desktop eligibility behavior:
respond_to=owner-onlydid not make the remote agent mentionable. The currentrelayAgentIsSharedWithUser()path admits relay agents via either:respond_to=anyone+ sharedchannel_ids, orrespond_to=allowlist+ the viewer pubkey.Changing our remote Hermes agent from
owner-onlytoallowlistwith the current owner pubkey made@KITTappear immediately in the Windows Desktop composer. The identity was already a channel member and already classified as an agent in the member UI.So the relay-directory approach is viable for cross-device/external runtimes, but it depends on a complete kind:10100 record with invocability metadata. The remaining UX gap in our case is the Agents tab, which still only shows the local managed store (#3054/#4834).
This strongly supports keeping remote discovery/invocation separate from local runtime ownership.
Open-luci commentedon Aug 16, 2026
Could not reproduce the kind:10100 workaround on desktop-v0.5.14 / relay 0.2.1 — clean negative with every precondition verified
@hondoaus thanks for the field report — we tried to reproduce it on a self-hosted setup and could not. Posting the negative result with everything verified, because it rules out the three causes most commonly suggested in this thread (stale app, stale relay, wrong
respond_to).Setup, all verified from the host side (not from the UI):
Test,5fe6b235…ba1a, Claude harness, running on the always-on Mac minirespond_to: allowlist, containing the second person's pubkey93925100…138e— confirmed inmanaged-agents.jsonand in the harness startup line:respond_to=allowlist(1)discovered 1 channel(s)→subscribed to channel 903e5e84-…@Testin that channel and got a normal reply, so the agent is alive, subscribed and answeringThe second identity is a genuinely separate person — own keypair, invited to the community as a teammate, and joined the channel (the join event is visible in the channel history) — not a second device on the same owner identity.
Result: the agent never appears in that person's mention picker in the shared channel. Not as a relay agent, not as a stale duplicate, not at all.
One timing detail worth recording for anyone else testing this. The directory entry is only as fresh as the last harness start. Our first attempt was invalid because the agent had started before the channel existed and published a profile listing zero channels; the live membership notification subscribed it at runtime but did not refresh what the relay advertises. We restarted the agent (
discovered 1 channel(s)in the log, so the entry then carried the channel) and retested — same result. So the stale-directory explanation does not account for it either.Notable difference from our earlier report (#5319): no duplicate identity was minted this time. The host still holds exactly one
Testidentity. Whatever landed in #5681 does appear to have closed the sender-side mint on this path; the outcome has changed from "you reach a ghost" to "you cannot see the agent at all", which matches #5264.@hondoaus — could you say what your kind:10100 record contains that a Desktop-managed agent's does not? Your report mentions publishing "a complete agent-signed kind:10100 record" for an external Hermes runtime. If Desktop-managed agents publish an incomplete directory entry (missing
channel_idsor the invocability metadata), that would explain both your success and our failure, and it would make this a concrete, fixable gap rather than an open-ended one.Happy to run any probe on this setup: two harnesses, a self-hosted relay we control, and a real second human identity.
hondoaus commentedon Aug 16, 2026
@Open-luci thanks for the careful negative repro. We now have a stronger data point from our side than the earlier owner-only test, and I think it is useful for narrowing the difference.
We are running externally hosted native Hermes Agent runtimes rather than Desktop-managed agents. The agent runtime lives on an always-on host; Buzz Desktop is only the client. We publish the relay/directory metadata ourselves and keep runtime authorization in Hermes aligned with the public directory policy.
I will avoid posting any private deployment details, user names, relay hostname, channel UUIDs, or raw pubkeys here, but the effective
kind:10100document for the test agent is structurally:{ "name": "KITT", "display_name": "KITT", "agent_type": "hermes", "channels": ["general"], "channel_ids": ["<shared-channel-uuid>"], "capabilities": [], "status": "offline", "respond_to": "allowlist", "respond_to_allowlist": [ "<owner-pubkey>", "<second-human-pubkey>" ], "channel_add_policy": "owner_only" }A few details that may matter:
channelsandchannel_idsare present.channel_idscontains the actual shared-channel UUID, not just a display name.respond_to=allowlistis explicit. We do not depend on an implicit owner exception in the Desktop directory path.respond_to_allowlistexplicitly contains both identities. After our first test, we extended it from only the owner to the owner plus a genuinely separate second human community member.channel_add_policy=owner_onlyis present in the same complete replacement document. We are not publishing a partial 10100 that only changes one policy field.botmember of the same channel as both humans.BUZZ_ALLOWED_USERSlist,BUZZ_ALLOW_ALL_USERS=false, andBUZZ_REQUIRE_MENTION=true. In other words, directory eligibility and receive-side authorization describe the same two humans.Stronger result from our latest test
We have now tested the exact non-owner case you are describing.
Sequence:
botchannel member.kind:10100document withrespond_to_allowlistcount = 2.@KITTin the shared channel and the remote Hermes agent received and answered the message successfully.So on our topology this is not just an owner/autocomplete success anymore: a separate non-owner human can invoke the same externally hosted agent successfully after being added to both the complete 10100 allowlist and the Hermes runtime allowlist.
That makes your negative result especially interesting, because it suggests the difference may be between the directory document emitted by Desktop-managed/buzz-acp agents and the complete external record above, rather than simply Desktop version / relay version / stale channel membership.
What I would compare first
If you can dump the actual 10100 content emitted by your Desktop-managed
Testagent after the clean restart, I would compare these fields exactly:namedisplay_nameagent_typechannelschannel_idsstatusrespond_torespond_to_allowlistchannel_add_policyIn particular I would check whether the Desktop-managed record:
channel_idsarray with the exact channel UUID;respond_to_allowlistin the shape Desktop actually evaluates for the current policy;One subtlety we hit independently: using the textual channel name (for example
general) where the integration expects the channel UUID can produce a superficially connected agent that cannot correctly seed/watch the channel. We fixed our Hermes setup by using the actual UUID for both its watched channel and home channel. That may not apply to your Desktop-managed case, but it is worth verifying the directory field contains the real UUID and not only the display name.Ownership / NIP-OA note
Our remotely hosted agents also have a persisted NIP-OA owner relationship at the relay. For later agents we materialized the owner mapping through the NIP-42 WebSocket path, then restored normal direct relay membership. I do not currently think that alone explains mention eligibility, but it is another difference from a plain local managed record and may be worth recording in the comparison.
Agents tab remains a separate issue
Even with the working cross-user mention path above, these external Hermes agents still do not appear as normal locally managed agents in the main Agents tab. We consider that a separate UI/lifecycle concern (#3054/#4834). We deliberately do not create fake local managed-agent entries for these identities, because doing so risks Desktop trying to start/intercept a runtime it does not own.
So the working model on our side is:
If useful, I am happy to run a sanitized field-by-field comparison against a sample 10100 from your
Testagent, or test a diagnostic branch. The fact that your fully verified v0.5.14/0.2.1 setup still fails while our external Hermes record works for a genuine second human should give us a relatively small difference set to inspect.DefiantToast commentedon Aug 16, 2026
Confirming this on a self-hosted relay, with two independent non-owner accounts on the latest desktop version. Adding measurements in case they help narrow it down.
Setup: two headless agents (
buzz-acp, one with codex-acp and one with claude-agent-acp) running on always-on hosts, joined to a shared channel. Owner runs Buzz Desktop on a separate machine. This is exactly the "one shared agent on an always-on host" case the issue mentions.Symptom for non-owner members: the agents appear in the channel member list (role
bot), but autocomplete never offers them, and messages sent by those members carry noptag.What we measured
Comparing events in the same channel (all
kind: 9):[h, e, p][h, e, p, auth][h]— channel onlyAgent profiles (
kind: 0) are published and resolvable —users getreturnsdisplay_nameandpicturefor both agents. So the agents are discoverable at the relay level; it's the client that won't surface them.A second failure that may or may not be the same root cause
Even with
--subscribe all(so the agent receives non-mention messages), the agent still can't reply to those members. Theevent_iddelivered to the agent isn't resolvable:Same message, same channel, same agent identity — only the ID differs. Adding a reaction to the subscription-delivered ID also fails with
POST /events → HTTP 400. Since the harness always replies as a reply (["e", <id>, "", "reply"]), the agent generates its response, the publish is rejected, and the turn endsokwith nothing in the channel.I haven't proven where the two IDs come from — could be double publication, a synthetic envelope, or something specific to the hosted path. Happy to capture raw WebSocket frames if that would help.
Workaround, if useful to anyone blocked on this: our deterministic guard reads via
messages get(canonical IDs, not the subscription), so it re-emits member reports under its own agent identity, mentioning the intake agent. The agent then replies to a message it can resolve. Costs one extra message per report and a second agent identity — each needs its own NIP-OAauth_tag, since tags from the same owner start identically but aren't interchangeable.BrianArbuckle commentedon Sep 9, 2026
Still reproduces on
desktop-v0.5.23(the latest tag as of 2026-09-09). #5084 was closed without merging and #4999 is still open, so nothing has landed for this.Repro, verified from the relay database rather than the UI. Self-hosted relay
ghcr.io/block/buzz:0.2.0. Host A (Mac mini, macOS 14.7.1, Desktop 0.5.23) runs three managed agents (codex-acp 1.10.0). Client B is a different identity on a laptop, same Desktop version, on each agent'srespond_to_allowlist. B creates aprivatestream channel with the CLI and adds one agent as a member; the agent's harness logsmembership notification: subscribing to new channelwithin a second.@in that channel: the agent is not offered. It is in the member list andbuzz users getresolves its profile.Helloanyway. The stored event's tags are exactly[["h", "<channel>"]]: noptag. The harness (defaultsubscribe=mentions) never receives it; its log stays empty.buzz messages send --channel <id> --content - --mention <agent pubkey>: the event carries theptag, the harness dispatches, the reply lands 11 s later.So relay, membership, allowlist and harness are all fine; the candidate list on the non-hosting desktop is what drops the mention, exactly as described above and in #5264.
Workaround that does not depend on the picker, in case it helps others: run the harness with
--subscribe configand a rules file, and give each person a private channel per agent:An agent subscribes only to channels it is a member of, so one shared file serves every agent, and anything typed in a person's room with one agent reaches that agent with no mention at all. Verified: three plain, untagged messages from client B, three replies within 25 s.
m13v commentedon Sep 11, 2026
publishing the 10100 directory record fixes resolution but swaps a live signal for a stale one. 'does this machine run it' was at least real-time; a published record is a snapshot that outlives the agent. retire the host or let the process die and the 10100 lingers, so it still autocompletes and you still emit a p-tagged event, except now nothing dispatches it. that's the same 'no error, no UI signal' failure you're killing, just moved from 'remote' to 'dead'. worth pairing the publish with a heartbeat or expiry so a mentionable agent means a reachable one, not one that was reachable once.
feat(cli): add `agents attest` and `agents register` for agents run o…