Component: desktop
Version: v0.4.26, source at 0096d710ed2e6abab19aaf7cdc14e3ee603d7ec8
Summary
We run two headless buzz-acp agents on a server (systemd, one per team member, each on its own subscription). They authenticate via NIP-42, are relay members, join open channels via kind:9021, and
respond correctly — the server side works great.
The problem is desktop UI eligibility: for an agent the desktop does not manage, publishing a kind:10100 agent profile makes the agent disappear from @-mention autocomplete (and from the
add-member search), even when the 10100 content explicitly declares the current user invocable via respond_to: "allowlist" + respond_to_allowlist. This forces an either/or choice for external
agents:
- No kind:10100 → agent is mentionable (appears as a plain member), but is absent from the Agents directory.
- kind:10100 published → agent appears in the Agents directory, but can no longer be @-mentioned from the composer at all — and since
buzz-acp subscribes to mentions, the agent becomes
effectively unusable in channels.
Root cause
useMentions.ts:249 runs the managed-list gate before the invocability logic:
if (!isAgentIdentityInManagedList(candidate, managedAgentPubkeys)) {
return; // ← any isAgent=true candidate not managed by THIS desktop is dropped here
}
if (shouldHideAgentFromMentions({ ... })) { return; }
isAgentIdentityInManagedList (agentAutocompleteEligibility.ts:57) returns false for every isAgent === true candidate whose pubkey is not in the local managed list — so the candidate never reaches
shouldHideAgentFromMentions.
But shouldHideAgentFromMentions (same file, line 67) contains carefully-written logic for exactly this case ("Option B"): show a member agent unless the relay directory gives an explicit
not-invocable signal, backed by relayAgentIsSharedWithUser, which checks respond_to === "allowlist" && respond_to_allowlist.includes(currentPubkey). Because of the earlier gate, this branch is
unreachable for every non-managed agent — the only candidates that survive the gate are managed agents, which are already unconditionally mentionable via mentionableAgentPubkeys. The invocability
check appears to be dead code, which suggests the gate ordering broke an intended behavior rather than implementing one.
The same pattern exists in the channel members add-search (MembersSidebar.tsx:285), so a non-managed agent with a 10100 profile cannot be added to channels from the UI either.
Reproduction
- Create a headless agent: relay member, NIP-42 auth,
buzz-acp with --respond-to=allowlist including user A's pubkey; join an open channel (kind:9021).
- No kind:10100 published: user A types
@ in that channel → agent appears (as a regular member); mention works; agent responds. ✓
- Publish kind:10100 for the agent, content:
{"name":"Agent","agent_type":"agent","respond_to":"allowlist","respond_to_allowlist":["<user A pubkey hex>"]}
- After the directory refetch (≤5 min or app restart):
@ no longer offers the agent to user A. Deleting the 10100 (kind:5, e-tag) restores mentionability.
Verified the relay serves the correct, latest-only 10100 with the allowlist fields; the raw→camelCase mapping in fromRawRelayAgent is correct; the drop happens purely in the gate above.
Expected
A directory agent whose kind:10100 declares the current user invocable (respond_to: "anyone", or allowlist including the user) should be mentionable and addable, per the logic already present in
shouldHideAgentFromMentions / relayAgentIsSharedWithUser.
Suggested fix
Let candidates that pass relayAgentIsSharedWithUser through the managed-list gate, e.g.:
if (
!isAgentIdentityInManagedList(candidate, managedAgentPubkeys) &&
!mentionableAgentPubkeys.has(normalizePubkey(candidate.pubkey))
) {
return;
}
(and the analogous change in MembersSidebar.tsx), which makes the existing Option B logic reachable while preserving the current behavior for unmanaged agents with no invocability signal.
Side notes from the same investigation (happy to split into separate issues)
- kind:5 a-tag deletion silently no-ops for plain replaceable kinds (e.g.
10100:<pubkey>:): handle_a_tag_deletion only applies coordinates for parameterized-replaceable (30000-range) kinds. The
deletion event is accepted and stored, but the target keeps being served — surprising combination. An e-tag deletion of the latest version works.
buzz-acp never publishes kind:0 or kind:10100 for its identity, so headless agents show as bare npubs until profiles are published out-of-band (we used buzz users set-profile + a small NIP-42
publishing script). A --publish-profile option on buzz-acp would make headless deployments much smoother.
Component: desktop
Version: v0.4.26, source at
0096d710ed2e6abab19aaf7cdc14e3ee603d7ec8Summary
We run two headless
buzz-acpagents on a server (systemd, one per team member, each on its own subscription). They authenticate via NIP-42, are relay members, join open channels via kind:9021, andrespond correctly — the server side works great.
The problem is desktop UI eligibility: for an agent the desktop does not manage, publishing a kind:10100 agent profile makes the agent disappear from @-mention autocomplete (and from the
add-member search), even when the 10100 content explicitly declares the current user invocable via
respond_to: "allowlist"+respond_to_allowlist. This forces an either/or choice for externalagents:
buzz-acpsubscribes to mentions, the agent becomeseffectively unusable in channels.
Root cause
useMentions.ts:249runs the managed-list gate before the invocability logic:isAgentIdentityInManagedList(agentAutocompleteEligibility.ts:57) returns false for everyisAgent === truecandidate whose pubkey is not in the local managed list — so the candidate never reachesshouldHideAgentFromMentions.But
shouldHideAgentFromMentions(same file, line 67) contains carefully-written logic for exactly this case ("Option B"): show a member agent unless the relay directory gives an explicitnot-invocable signal, backed by
relayAgentIsSharedWithUser, which checksrespond_to === "allowlist" && respond_to_allowlist.includes(currentPubkey). Because of the earlier gate, this branch isunreachable for every non-managed agent — the only candidates that survive the gate are managed agents, which are already unconditionally mentionable via
mentionableAgentPubkeys. The invocabilitycheck appears to be dead code, which suggests the gate ordering broke an intended behavior rather than implementing one.
The same pattern exists in the channel members add-search (
MembersSidebar.tsx:285), so a non-managed agent with a 10100 profile cannot be added to channels from the UI either.Reproduction
buzz-acpwith--respond-to=allowlistincluding user A's pubkey; join an open channel (kind:9021).@in that channel → agent appears (as a regular member); mention works; agent responds. ✓{"name":"Agent","agent_type":"agent","respond_to":"allowlist","respond_to_allowlist":["<user A pubkey hex>"]}@no longer offers the agent to user A. Deleting the 10100 (kind:5, e-tag) restores mentionability.Verified the relay serves the correct, latest-only 10100 with the allowlist fields; the raw→camelCase mapping in
fromRawRelayAgentis correct; the drop happens purely in the gate above.Expected
A directory agent whose kind:10100 declares the current user invocable (
respond_to: "anyone", orallowlistincluding the user) should be mentionable and addable, per the logic already present inshouldHideAgentFromMentions/relayAgentIsSharedWithUser.Suggested fix
Let candidates that pass
relayAgentIsSharedWithUserthrough the managed-list gate, e.g.:(and the analogous change in
MembersSidebar.tsx), which makes the existing Option B logic reachable while preserving the current behavior for unmanaged agents with no invocability signal.Side notes from the same investigation (happy to split into separate issues)
10100:<pubkey>:):handle_a_tag_deletiononly applies coordinates for parameterized-replaceable (30000-range) kinds. Thedeletion event is accepted and stored, but the target keeps being served — surprising combination. An e-tag deletion of the latest version works.
buzz-acpnever publishes kind:0 or kind:10100 for its identity, so headless agents show as bare npubs until profiles are published out-of-band (we usedbuzz users set-profile+ a small NIP-42publishing script). A
--publish-profileoption on buzz-acp would make headless deployments much smoother.