Skip to content

Cross-owner agent mentions are impossible: candidates filtered by local ownership, and eligibility reads a kind (10100) nothing publishes #3776

Description

@rjgalvez

Describe the bug

An agent owned by one person can never appear in another person's @ mention autocomplete, in any channel, under any configuration. respond_to: anyone, respond_to: allowlist, channel membership, and shared-channel overlap all have no effect.

There appear to be two independent causes, and the second makes the first unreachable.

Cause 1 — the eligibility gate is unreachable for agents you don't own

useMentions.ts applies three gates in order (v0.5.1 desktop/src/features/agents/hooks/useMentions.ts:245-262):

if (isArchivedDiscovery(pubkey)) return;
if (!isAgentIdentityInManagedList(candidate, managedAgentPubkeys)) return;   // :249
if (shouldHideAgentFromMentions({})) return;                                //  :252

isAgentIdentityInManagedList (lib/agentAutocompleteEligibility.ts:57-65) keeps a candidate only if isAgent !== true or its pubkey is in managedAgentPubkeys. That set is built from useManagedAgentsQuery → the local list_managed_agents Tauri command, i.e. strictly the viewer's own agents — and apply_inbound_managed_agent deliberately refuses to mint one from a relay event (commands/personas/inbound.rs:376-404, "managed agents carry device-local secrets and are never minted from a relay event").

Relay-agent candidates are added with isAgent: true unconditionally (useMentions.ts:334-346), and member candidates get isAgent: true whenever profile?.isAgent === true (:318-323), which for a kind:0 profile is derived as owner_pubkey.is_some() (nostr_convert.rs:345) — always true for an agent, since sync_managed_agent_profile always attaches a NIP-OA owner attestation (relay.rs:401-455).

So for any viewer who does not own the agent, the candidate is dropped at :249 before shouldHideAgentFromMentions / relayAgentIsSharedWithUser ever run.

Typing the mention manually doesn't help either: extractMentionPubkeys (:794-831) resolves p tags from mentionMapRef plus the post-filter mentionCandidates, so no p tag is emitted for a filtered-out agent.

Cause 2 — relayAgentIsSharedWithUser reads an event kind nothing publishes

Even if Cause 1 is fixed, the eligibility logic is inert. relayAgentIsSharedWithUser (lib/agentAutocompleteEligibility.ts:12-31) reads channelIds / respondTo / respondToAllowlist from a RelayAgent, which comes from list_relay_agents (desktop/src-tauri/src/commands/agent_discovery.rs:1057-1074) — a query for exactly [{"kinds":[10100]}].

Nothing in the repo publishes a kind:10100 agent profile. The only 10100 writer is buzz channels set-add-policy (crates/buzz-cli/src/commands/channels.rs:1004-1038), whose content is {"channel_add_policy": …}. Agent identity is published as kind:0 plus kind:30177 (managed_agents/agent_events.rs), and the persona catalog as kind:30175. The eligibility path never consults 30177.

Consequently agents_from_events (nostr_convert.rs:445-496) defaults channel_ids: [] (:469-480) and leaves respond_to absent → respondTo: null (shared/api/tauri.ts:686-695), so relayAgentIsSharedWithUser returns false for every agent and every viewer.

Secondary effect: publishing a 10100 makes things worse. directoryAgentPubkeys is every pubkey in relayAgentsQuery.data, with no eligibility filter (useMentions.ts:182-190), and for a channel member shouldHideAgentFromMentions ends in return directoryAgentPubkeys.has(normalized) (agentAutocompleteEligibility.ts:85-97). So a 10100 that doesn't satisfy relayAgentIsSharedWithUser converts "unknown invocability ⇒ show" into "explicit exclusion ⇒ hide", removing an agent that channel members could previously see. Note also that 10100 is replaceable-by-pubkey, so publishing one destroys that agent's existing channel_add_policy event.

Steps to reproduce

  1. User A creates an agent, sets Responds to: Anyone, and adds it as a member of a channel shared with user B.
  2. Confirm on the relay that the agent's kind:30177 content has "respond_to":"anyone" (visible in agents/retention/<64hex>.db, table persona_events).
  3. As user B, in that shared channel, type @ followed by the agent's exact name.
  4. The agent never appears in the autocomplete — no entry, and no "not in channel" label.

Same result with Responds to: Allowlist containing user B's 64-char hex pubkey.

Expected behavior

An agent whose owner has set respond_to: anyone (or an allowlist including the viewer) and which shares a channel with the viewer should be selectable in that viewer's mention autocomplete — which is what relayAgentIsSharedWithUser and the respond_to setting both clearly intend.

Version and platform

  • Buzz version: 0.5.1 (verified unchanged in 0.5.2 — agentAutocompleteEligibility.ts, agent_discovery.rs, agent_events.rs are all identical between the two tags)
  • OS: macOS 15 (Darwin 25.4.0), Apple Silicon

Logs / additional context

The agent-side gate is configured correctly and is not the problem: record.respond_to reaches the harness as --respond-to at spawn (managed_agents/runtime.rs:380-392), and author_allowed admits any author under RespondTo::Anyone for non-DM channels (crates/buzz-acp/src/lib.rs:250). The agent would answer; it simply cannot be addressed, because no p tag can be produced for it.

Possible fix directions (happy to open a PR if you can indicate a preference):

  1. Cause 1 — allow a candidate through :249 when its pubkey is in relayAgentsQuery and it passes relayAgentIsSharedWithUser, rather than requiring local ownership; and relax the corresponding early-return in extractMentionPubkeys so the p tag is emitted.
  2. Cause 2, option (a) — add a kind:10100 agent-profile publisher on the owner's desktop. Needs a new projection plus a source for channel_ids, which doesn't exist today.
  3. Cause 2, option (b) — repoint list_relay_agents at kind:30177 (which already carries respond_to / respond_to_allowlist) and resolve channel membership with a kind:39002 #p query, the same one the harness already uses at relay.rs:651-668. This needs no relay change and seems the cleaner shape.

Workaround for anyone hitting this: skip mentions entirely by setting per-agent env BUZZ_ACP_SUBSCRIBE=all (plus BUZZ_ACP_KINDS=9 and BUZZ_ACP_CHANNELS=<uuid> to avoid wildcard kinds and unintended channels), which makes the agent answer messages in a scoped channel without requiring a p tag.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions