Desktop: agents owned by another user are never mentionable — managed-list guard in useMentions dead-codes shouldHideAgentFromMentions
- Dominant language
- Rust
- Stars
- 32.7k
- Forks
- 4.3k
- Avg merge
- 1d 13h
- Merged PRs (30d)
- 253
Description
# Desktop: agents owned by another user are never mentionable — the managed-list guard in `useMentions` dead-codes `shouldHideAgentFromMentions`
## Summary
On Buzz Desktop, an agent deployed by user A is invisible in user B's `@`-mention autocomplete — even when the agent is a **member of the same channel** and B is on the agent's `respond_to` allowlist. Typing the name by hand doesn't help either: the message is sent without a `p` tag, so the agent's harness never triggers.
This makes cross-user agent collaboration (the "agents are members, not bots" story from the README) work on **mobile but not on desktop**.
## Root cause
In `desktop/src/features/messages/lib/useMentions.ts`, `addCandidate` runs two gates in sequence:
```ts
if (!isAgentIdentityInManagedList(candidate, managedAgentPubkeys)) return; // gate 1
if (shouldHideAgentFromMentions({ ..., mentionableAgentPubkeys, ... })) return; // gate 2
```
1. **Gate 1** drops every `isAgent` candidate whose pubkey is not in the **local** managed-agent list (`managed-agents.json`). On any install that doesn't manage agents itself, this is the empty set — every agent candidate dies here, including channel members with role `bot`.
2. **Gate 2** (`shouldHideAgentFromMentions`) starts with `if (mentionableAgentPubkeys.has(normalized)) return false; // Invocable => always show`.
3. `getMentionableAgentPubkeys` **seeds its result with `managedAgentPubkeys`** (`agentAutocompleteEligibility.ts`), so `managedAgentPubkeys ⊆ mentionableAgentPubkeys` always holds.
⟹ Every agent candidate that survives gate 1 is already "invocable" to gate 2, which therefore returns `false` on its first line, **always**. All remaining branches of `shouldHideAgentFromMentions` — including the "Option B" member branch (lines 84–97) whose comment block explicitly specifies *"Member: … Unknown invocability (not in directory) => show"* — are unreachable from their only production call site. The policy documented there was designed for exactly this scenario, and gate 1 starves it.
## Evidence this is a regression, not intent
- **The mobile client is the witness.** `mobile/lib/features/channels/mentions/mention_candidates.dart` implements the same pipeline **without** the pre-gate (member bots are always candidates; `agentIsSharedWithUser` handles non-member directory agents), and its comments say *"Mirrors desktop's `shouldHideAgentFromMentions`"*. The port codified desktop's intended semantics; desktop no longer executes them.
- **Coverage has a hole exactly here.** The e2e suite pins "relay-only (non-member) agents stay hidden" and "own managed agents are visible" — there is no test for *channel-member agent owned by another user*.
- **The typed-mention fallback is also starved.** `extractMentionPubkeys` has an explicit loop resolving hand-typed `@Name` against channel members without autocomplete. It works for humans; for foreign agents, gate 1 has already removed them from `mentionCandidates`.
Net effect: on a desktop with an empty managed list, **no agent is mentionable at all — not even one with `respond_to: anyone`**. The client-side allowlist/anyone logic (`relayAgentIsSharedWithUser`, `getMentionableAgentPubkeys`) is unreachable ornament.
`isAgentIdentityInManagedList` has a legitimate job — its unit test says *"keeps people and only current managed agent identities"*, i.e. dropping stale/ghost agent identities surfaced by discovery/search. The bug is only that it also swallows relay-confirmed **channel members**.
## Fix
Exempt channel members from gate 1 so that gate 2 — which already encodes the correct member policy — actually decides:
```ts
if (
candidate.isMember !== true &&
!isAgentIdentityInManagedList(candidate, managedAgentPubkeys)
) {
return;
}
```
Ghost-guard semantics are preserved for non-members (discovery, directory, global search), so the pinned "relay-only agents stay hidden" behavior is unchanged.
PR with the fix and an e2e regression test (channel-member bot + empty managed list ⇒ visible in `@`, mention resolves to a `p` tag on the wire): #TBD
## Repro
1. User A deploys an agent and adds it to a shared channel (member role `bot`).
2. User B (no locally managed agents) opens the channel on **desktop** and types `@`.
3. The agent is missing from the list; typing `@AgentName` by hand sends a message without any `p` tag (verifiable in the event), so the agent never responds.
4. Same account, same channel on **mobile**: the agent appears and the mention works.
Contributor guide
Research direction
Start with desktop/src/features/messages/lib/useMentions.ts, especially addCandidate, then inspect agentAutocompleteEligibility.ts and the existing mention unit and e2e coverage. Done means channel-member agents remain mentionable with an empty managed-agent list, while relay-only non-member agents stay hidden and the resulting mention resolves to a p tag.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- desktop, frontend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 76/100