Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -306,3 +306,66 @@ test("coalesceAgentAutocompleteCandidates: leaves non-agents alone", () => {

assert.deepEqual(coalesce([first, second]), [first, second]);
});

// Regression: an externally-hosted relay agent (kind:10100 announcement with
// respond_to="allowlist" naming the viewer) must survive the composer's
// candidate gates. useMentions gates candidates with
// isAgentIdentityInManagedList using the MENTIONABLE set (managed ∪ invocable
// relay agents); gating on the managed set alone silently dropped external
// agents before shouldHideAgentFromMentions could show them.
test("invocable external relay agent passes the composed mention gates", () => {
const relayAgents = [
{
pubkey: PUB_A,
respondTo: "allowlist",
respondToAllowlist: [CURRENT_PUBKEY],
channelIds: [],
},
];
const mentionable = getMentionableAgentPubkeys({
currentPubkey: CURRENT_PUBKEY,
managedAgentPubkeys: new Set(),
relayAgents,
sharedChannelIds: new Set(),
});
const candidate = { pubkey: PUB_A, isAgent: true, isMember: true };

// Gate 1 (managed-list identity gate, fed the mentionable set):
assert.equal(isAgentIdentityInManagedList(candidate, mentionable), true);
// Gate 2 (invocable => always show):
assert.equal(
shouldHideAgentFromMentions({
isAgent: true,
isMember: true,
pubkey: PUB_A,
mentionableAgentPubkeys: mentionable,
directoryAgentPubkeys: new Set([PUB_A]),
}),
false,
);
});

test("non-invocable external relay agent is still hidden from mentions", () => {
const relayAgents = [
{
pubkey: PUB_B,
respondTo: "owner-only",
respondToAllowlist: [],
channelIds: [],
},
];
const mentionable = getMentionableAgentPubkeys({
currentPubkey: CURRENT_PUBKEY,
managedAgentPubkeys: new Set(),
relayAgents,
sharedChannelIds: new Set(),
});

assert.equal(
isAgentIdentityInManagedList(
{ pubkey: PUB_B, isAgent: true, isMember: true },
mentionable,
),
false,
);
});
9 changes: 7 additions & 2 deletions desktop/src/features/messages/lib/useMentions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,13 @@ export function useMentions(
if (isArchivedDiscovery(pubkey)) {
return;
}
if (!isAgentIdentityInManagedList(candidate, managedAgentPubkeys)) {
// Gate on the full invocable set (locally managed ∪ relay agents this
// viewer can invoke), not the managed list alone. With managed-only,
// externally-hosted agents were dropped here before
// `shouldHideAgentFromMentions` could apply its invocable-⇒-show rule,
// making that path unreachable — an external agent (kind:10100 with
// respond_to allowlisting the viewer) could never be @-mentioned.
if (!isAgentIdentityInManagedList(candidate, mentionableAgentPubkeys)) {
return;
}
if (
Expand Down Expand Up @@ -420,7 +426,6 @@ export function useMentions(
managedAgentNamesByPubkey,
managedAgentPersonaIds,
managedAgentPersonaIdsByPubkey,
managedAgentPubkeys,
managedAgentsQuery.data,
memberPubkeys,
members,
Expand Down