fix(desktop): make cross-owner channel-member agents mentionable - #4716
fix(desktop): make cross-owner channel-member agents mentionable#4716oleg-ai-dev wants to merge 4 commits into
Conversation
…plete isAgentIdentityInManagedList dropped any agent candidate not in the viewer's own locally-managed set, before shouldHideAgentFromMentions's existing relay-sharing logic ever ran. A bot member of a shared channel owned by a different identity could never be @-mentioned or respond. Thread the already-computed mentionableAgentPubkeys set into the gate so a real channel member is admitted when relayAgentIsSharedWithUser says the agent is shared, while relay-directory-only (non-member) agents stay hidden exactly as before. Signed-off-by: oleg-ai-dev <oleg.ai.development@gmail.com>
…ests Decouple the default relay-agent-directory fixture (alice/charlie) from the real human TEST_IDENTITIES pubkeys they coincidentally reused, which started colliding with the mention-eligibility fix (a human member whose pubkey happens to also carry relay-agent-directory data should not be mistaken for a mentionable agent). Extend the relay-agent mock seed to also register real channel bot membership, and add tests mirroring the exact reported repro: a bot member of a shared channel, owned by another identity, with respond_to=anyone, must be mentionable — and must stay hidden if it is only relay-directory-listed without real channel membership. Signed-off-by: oleg-ai-dev <oleg.ai.development@gmail.com>
…lished kind:10100 list_relay_agents queried kind:10100 agent profile events, but nothing in the codebase publishes that kind — managed agents are published as kind:30177 (managed_agents/agent_events.rs). The relay-agent directory the mention-eligibility check depends on was always empty, so relayAgentIsSharedWithUser could never return true for anyone. Repoint the query at kind:30177 and resolve channel membership via a kind:39002 #p query — the same pattern buzz-acp's own RelayClient::discover_channels and get_channels already use. The agent's pubkey is read from kind:30177's d tag, not the event's own pubkey (which is the owner's identity, since kind:30177 is a parameterized-replaceable event published by the owner). Signed-off-by: oleg-ai-dev <oleg.ai.development@gmail.com>
biome missed the wrapped pubkey literals in e2eBridge.ts (Task 3), and cargo fmt was never run against the Tauri crate's separate format domain for nostr_convert.rs (Task 4) — just fmt only covers the root workspace, not desktop/src-tauri. Pure formatting, no logic change; verified via diff. Signed-off-by: oleg-ai-dev <oleg.ai.development@gmail.com>
|
@oleg-ai-dev @dexsynccom @anindyar, after comparing all three patches, I think each contributes part of the best fix. #4714 keeps the mention change narrow. #4716 correctly identifies the instance-tier policy and adds the cross-identity E2E proof this bug needs. #4713 recognizes that runtime discovery should be signed by the agent itself. I would preserve all three contributions. I still don’t think #4716’s My preferred combination is #4714’s mention fix, #4716’s policy handling and E2E coverage, and an agent-signed
If the |
|
Opened #5483 on the same layer, so this is a note from the one other PR that touches One factual correction on the premise for repointing, because it changes what the fix should be:
True of Desktop's own code, but not of deployments. Headless So repointing
The second row is the bug both of us set out to fix; the first row is the population #2987 is about. A headless agent has no Desktop, so nothing publishes a I've changed #5483 to read the union of both kinds, keyed by agent pubkey, then enrich with Not asking you to close this — the union is a small change on top of what you already have, and if you'd rather carry it here I'm happy to close mine instead. The part I'd push back on either way is dropping |
Problem
A bot agent that is a member of a channel shared between two different Buzz identities can never be
@-mentioned (or manually addressed via a typed mention) by anyone except the human who owns/hosts it — even when the agent'srespond_toisanyone. Reported and reproduced independently across desktop v0.4.20–v0.5.3, macOS/Windows, hosted/self-hosted relays: #3776 (canonical, most precise), #3277 (original report + a documented client-side stopgap), #2349 (long thread with the business case for team use), #2950.Heads up to reviewers: I'm aware this issue already has a large number of open PRs attempting the same fix. I looked through the ones I could find before opening this — none of the ones I checked source
respond_to/respond_to_allowlistfrom the correct (instance-tier) fields (see Cause 2 below), which as far as I can tell would leave the exact same bug at a different codepath if merged as-is. Flagging that here explicitly in case it's useful for triage, independent of whether this particular PR is the one that ends up merged.Root cause (two independent, stacked causes — both are required to fix this)
Cause 1 — the mention picker drops any agent you don't personally manage.
isAgentIdentityInManagedList(inagentAutocompleteEligibility.ts) only admits a candidate if it isn't flagged as an agent, or if its pubkey is in the viewer's ownmanagedAgentPubkeys— built solely from the locallist_managed_agentsTauri command. An agent owned by the other identity never enters that set (by design — managed-agent records hold device-local secrets and are never minted from a relay event). So the other person's agent is dropped inuseMentions.tsbeforeshouldHideAgentFromMentions/relayAgentIsSharedWithUserever run — including for a manually typed mention, sinceextractMentionPubkeysonly resolvesptags from the already-filtered candidate list.Cause 2 — the eligibility check reads an event kind nothing publishes. Even with Cause 1 fixed,
relayAgentIsSharedWithUserreadschannelIds/respondTo/respondToAllowlistfrom aRelayAgentsourced fromlist_relay_agents(agent_discovery.rs), which queries the relay forkind:10100. Nothing in the codebase publisheskind:10100as an agent profile (the only writer isbuzz channels set-add-policy, which writes unrelatedchannel_add_policycontent). Agent identity/config is actually published askind:0+kind:30177(managed_agents/agent_events.rs), which the eligibility code never reads — so theRelayAgentit needs is always empty, andrelayAgentIsSharedWithUseralways returnsfalse.Note: the agent-side authorization logic is already correct and needed no change —
author_allowedinbuzz-acpalready permits any author forRespondTo::Anyoneoutside DMs. The agent would answer if it could be addressed; it just could never produce a mention.Fix
mentionableAgentPubkeysset (which already accounts for relay-directory sharing) into theisAgentIdentityInManagedListgate, so a real channel member is admitted when it's shared, while relay-directory-only (non-member) agents stay hidden exactly as before. Same relaxation applied toextractMentionPubkeysso a p-tag is actually emitted for a manually typed mention of such an agent.list_relay_agents(agent_discovery.rs) fromkind:10100tokind:30177, and resolve channel membership via akind:39002 #pquery — the same patternbuzz-acp's own channel-discovery code already uses. The agent's pubkey is read fromkind:30177'sdtag (not the event's own pubkey, sincekind:30177is a parameterized-replaceable event published by the owner).respond_to/respond_to_allowlistare sourced from the record's instance-tier runtime fields, not the definition-tier fields — the snapshot this event is built from carries both, and they can diverge in practice (the running harness is spawned from instance-tier fields). Sourcing from definition-tier would ship a policy the running agent doesn't actually enforce (agent visible but silent, or answering but hidden).Test plan
agentAutocompleteEligibility.test.mjsfor the new gate logic, including the two cases that must stay hidden: a non-member agent that's only relay-directory-listed, and a channel member with an explicit not-invocable directory entry.mentions.spec.tsmirroring the exact reported repro: identity A creates an agent withrespond_to: anyone, adds it as a bot member of a channel shared with identity B; identity B can now see and use the mention, and a non-member/non-invocable agent stays hidden.cargo fmt --all -- --checkandcargo fmt(Tauri crate) clean.cargo clippy --all-features -- -D warningsreports zero issues in either file this PR touches (agent_discovery.rs,nostr_convert.rs). Note: the same command surfaces ~30 pre-existing warnings/errors in unrelated files on a cleanorigin/maincheckout under Windows +--all-features(confirmed by checking out main and re-running) — those are not introduced by this change and are left untouched per the "no drive-by fixes" guidance inCONTRIBUTING.md.tsc --noEmitclean.biome checkclean on all changed files.Closes #3776. Related: #3277, #2349, #2950.