diff --git a/desktop/src/features/agents/lib/agentAutocompleteEligibility.test.mjs b/desktop/src/features/agents/lib/agentAutocompleteEligibility.test.mjs index 4e02b7bd68..074aea4be5 100644 --- a/desktop/src/features/agents/lib/agentAutocompleteEligibility.test.mjs +++ b/desktop/src/features/agents/lib/agentAutocompleteEligibility.test.mjs @@ -6,6 +6,7 @@ import { getMentionableAgentPubkeys, getSharedChannelIds, isAgentIdentityInManagedList, + isMentionCandidateEligible, relayAgentIsSharedWithUser, shouldHideAgentFromMentions, } from "./agentAutocompleteEligibility.ts"; @@ -188,6 +189,20 @@ test("shouldHideAgentFromMentions: shows invocable agents even when non-member", ); }); +test("isMentionCandidateEligible: admits another owner's anyone agent in a shared channel", () => { + assert.equal( + isMentionCandidateEligible({ + isArchived: false, + isAgent: true, + isMember: true, + pubkey: PUB_A, + mentionableAgentPubkeys: new Set([PUB_A]), + directoryAgentPubkeys: new Set([PUB_A]), + }), + true, + ); +}); + test("shouldHideAgentFromMentions: hides non-member non-invocable agents", () => { assert.equal( shouldHideAgentFromMentions({ diff --git a/desktop/src/features/agents/lib/agentAutocompleteEligibility.ts b/desktop/src/features/agents/lib/agentAutocompleteEligibility.ts index e4afe7fea4..8dff5a9b01 100644 --- a/desktop/src/features/agents/lib/agentAutocompleteEligibility.ts +++ b/desktop/src/features/agents/lib/agentAutocompleteEligibility.ts @@ -97,6 +97,33 @@ export function shouldHideAgentFromMentions({ return directoryAgentPubkeys.has(normalized); } +export function isMentionCandidateEligible({ + isArchived, + isAgent, + isMember, + pubkey, + mentionableAgentPubkeys, + directoryAgentPubkeys, +}: { + isArchived: boolean; + isAgent: boolean; + isMember: boolean; + pubkey: string; + mentionableAgentPubkeys: ReadonlySet; + directoryAgentPubkeys: ReadonlySet; +}) { + return ( + !isArchived && + !shouldHideAgentFromMentions({ + isAgent, + isMember, + pubkey, + mentionableAgentPubkeys, + directoryAgentPubkeys, + }) + ); +} + type AgentAutocompleteCandidate = { pubkey?: string; displayName?: string | null; diff --git a/desktop/src/features/messages/lib/useMentions.ts b/desktop/src/features/messages/lib/useMentions.ts index 0c73b75339..18e89a8093 100644 --- a/desktop/src/features/messages/lib/useMentions.ts +++ b/desktop/src/features/messages/lib/useMentions.ts @@ -16,8 +16,7 @@ import { coalesceAutocompleteCandidatesByKey, getMentionableAgentPubkeys, getSharedChannelIds, - isAgentIdentityInManagedList, - shouldHideAgentFromMentions, + isMentionCandidateEligible, } from "@/features/agents/lib/agentAutocompleteEligibility"; import { useInfiniteUserSearchQuery, @@ -243,14 +242,9 @@ export function useMentions( const addCandidate = (candidate: MentionCandidate & { pubkey: string }) => { const pubkey = normalizePubkey(candidate.pubkey); - if (isArchivedDiscovery(pubkey)) { - return; - } - if (!isAgentIdentityInManagedList(candidate, managedAgentPubkeys)) { - return; - } if ( - shouldHideAgentFromMentions({ + !isMentionCandidateEligible({ + isArchived: isArchivedDiscovery(pubkey), isAgent: candidate.isAgent === true, isMember: candidate.isMember === true, pubkey, @@ -420,7 +414,6 @@ export function useMentions( managedAgentNamesByPubkey, managedAgentPersonaIds, managedAgentPersonaIdsByPubkey, - managedAgentPubkeys, managedAgentsQuery.data, memberPubkeys, members, diff --git a/desktop/tests/e2e/mentions.spec.ts b/desktop/tests/e2e/mentions.spec.ts index 5e31235a18..a8973004e7 100644 --- a/desktop/tests/e2e/mentions.spec.ts +++ b/desktop/tests/e2e/mentions.spec.ts @@ -187,7 +187,7 @@ async function expectAgentProfileActionsHidden( ).toHaveCount(0); } -test("@ trigger prioritizes channel members before runnable personas and other managed agents", async ({ +test("@ trigger shows shared external agents and prioritizes channel members", async ({ page, }) => { await installMockBridge(page, { @@ -209,7 +209,7 @@ test("@ trigger prioritizes channel members before runnable personas and other m const dropdown = autocomplete(page); await expect(dropdown).toBeVisible(); - await expect(dropdown.getByText("alice")).toHaveCount(0); + await expect(dropdown.getByText("alice")).toBeVisible(); await expect(dropdown.getByText("bob")).toBeVisible(); await expect(dropdown.getByText("Fizz")).toBeVisible(); await expect(dropdown.getByText("charlie")).toBeVisible(); @@ -225,6 +225,7 @@ test("@ trigger prioritizes channel members before runnable personas and other m const suggestions = dropdown.locator("button"); const suggestionText = await suggestions.allInnerTexts(); + const aliceIndex = suggestionText.findIndex((text) => text.includes("alice")); const fizzIndex = suggestionText.findIndex((text) => text.includes("Fizz")); const bobIndex = suggestionText.findIndex((text) => text.includes("bob")); const charlieIndex = suggestionText.findIndex((text) => @@ -233,10 +234,12 @@ test("@ trigger prioritizes channel members before runnable personas and other m const outsiderIndex = suggestionText.findIndex((text) => text.includes("outsider"), ); + expect(aliceIndex).toBeGreaterThanOrEqual(0); expect(fizzIndex).toBeGreaterThanOrEqual(0); expect(bobIndex).toBeGreaterThanOrEqual(0); expect(charlieIndex).toBeGreaterThanOrEqual(0); expect(outsiderIndex).toEqual(-1); + expect(aliceIndex).toBeLessThan(fizzIndex); expect(bobIndex).toBeLessThan(fizzIndex); expect(fizzIndex).toBeLessThan(charlieIndex); }); @@ -826,7 +829,7 @@ test("managed relay agents are visible in channel mentions regardless of relay p await expect(dropdown.getByText("agent")).toBeVisible(); }); -test("relay-only agents stay hidden from channel mentions even when allowlisted", async ({ +test("another owner's anyone agent in a shared channel is visible in mentions", async ({ page, }) => { await installMockBridge(page, { @@ -834,8 +837,8 @@ test("relay-only agents stay hidden from channel mentions even when allowlisted" { pubkey: ALLOWLIST_RELAY_AGENT_PUBKEY, name: "quinn", - respondTo: "allowlist", - respondToAllowlist: [MOCK_VIEWER_PUBKEY], + respondTo: "anyone", + channelNames: ["general"], }, ], }); @@ -846,7 +849,9 @@ test("relay-only agents stay hidden from channel mentions even when allowlisted" const input = page.getByTestId("message-input"); await input.fill("@quinn"); - await expect(autocomplete(page)).toHaveCount(0); + const dropdown = autocomplete(page); + await expect(dropdown.getByText("quinn")).toBeVisible(); + await expect(dropdown.getByText("agent")).toBeVisible(); }); test("mentioning an in-channel stopped managed agent starts it before sending", async ({