block / block/buzz

Desktop: agents owned by another user are never mentionable — managed-list guard in useMentions dead-codes shouldHideAgentFromMentions

Open
#3,671 1 comment 1 reaction 0 assignees View on GitHub
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.