block / block/buzz

External (non-desktop-managed) agents: publishing kind:10100 makes them un-mentionable — `shouldHideAgentFromMentions`'s invocability logic is unreachable

Open
#2,987 15 comments 8 reactions 0 assignees View on GitHub
bug
Dominant language
Rust
Stars
32.7k
Forks
4.3k
Avg merge
1d 13h
Merged PRs (30d)
253

Description

**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:

```ts
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

1. 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).
2. No kind:10100 published: user A types `@` in that channel → agent appears (as a regular member); mention works; agent responds. ✓
3. Publish kind:10100 for the agent, content:
```json
{"name":"Agent","agent_type":"agent","respond_to":"allowlist","respond_to_allowlist":[""]}
```
4. 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.:

```ts
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::`): `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.

Contributor guide

Open the contributing guide

Research direction

Start in useMentions.ts around the managed-list gate and shouldHideAgentFromMentions, then compare the analogous search flow in MembersSidebar.tsx. Trace isAgentIdentityInManagedList, relayAgentIsSharedWithUser, and the existing mentionable-agent handling; the work is done when an invocable external agent appears in mention autocomplete and add-member search while non-invocable agents remain hidden.

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
Active
Clarity
Clearly specified
Newbie friendliness
76/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.