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 |
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.