diff --git a/desktop/src/features/agents/lib/agentAutocompleteEligibility.test.mjs b/desktop/src/features/agents/lib/agentAutocompleteEligibility.test.mjs
index 4e02b7bd68..20c6b8f854 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,
+  isAgentIdentityMentionable,
   relayAgentIsSharedWithUser,
   shouldHideAgentFromMentions,
 } from "./agentAutocompleteEligibility.ts";
@@ -17,6 +18,7 @@ const PUB_A = "1".repeat(64);
 const PUB_B = "2".repeat(64);
 const PUB_C = "3".repeat(64);
 const PUB_D = "4".repeat(64);
+const PUB_HEX = "ab".repeat(32);
 
 function coalesce(candidates, options = {}) {
   return coalesceAgentAutocompleteCandidates(candidates, {
@@ -162,6 +164,52 @@ test("isAgentIdentityInManagedList: keeps people and only current managed agent
   );
 });
 
+test("isAgentIdentityMentionable: keeps people, managed agents, and mentionable relay agents", () => {
+  // Mirrors useMentions: the mentionable set from getMentionableAgentPubkeys
+  // is managed agents (PUB_A, PUB_HEX) plus shared relay agents (PUB_B).
+  const mentionableAgentPubkeys = new Set([PUB_A, PUB_B, PUB_HEX]);
+
+  assert.equal(
+    isAgentIdentityMentionable(
+      { isAgent: false, pubkey: PUB_C },
+      mentionableAgentPubkeys,
+    ),
+    true,
+  );
+  assert.equal(
+    isAgentIdentityMentionable(
+      { isAgent: true, pubkey: PUB_HEX.toUpperCase() },
+      mentionableAgentPubkeys,
+    ),
+    true,
+  );
+  assert.equal(
+    isAgentIdentityMentionable(
+      { isAgent: true, pubkey: PUB_B },
+      mentionableAgentPubkeys,
+    ),
+    true,
+  );
+  assert.equal(
+    isAgentIdentityMentionable(
+      { isAgent: true, pubkey: PUB_C },
+      mentionableAgentPubkeys,
+    ),
+    false,
+  );
+  // Member agent with no usable directory record: isMember does not bypass
+  // the gate — an agent identity outside the mentionable set is filtered
+  // here, before shouldHideAgentFromMentions' member branch can run
+  // (pre-existing behavior, deliberately pinned).
+  assert.equal(
+    isAgentIdentityMentionable(
+      { isAgent: true, isMember: true, pubkey: PUB_D },
+      mentionableAgentPubkeys,
+    ),
+    false,
+  );
+});
+
 test("shouldHideAgentFromMentions: never hides non-agents", () => {
   assert.equal(
     shouldHideAgentFromMentions({
@@ -214,6 +262,10 @@ test("shouldHideAgentFromMentions: hides member agents with an explicit not-invo
   );
 });
 
+// Note: in useMentions' addCandidate flow this member branch only runs for
+// candidates that already passed isAgentIdentityMentionable, which requires
+// mentionable-set membership — so this unit behavior is currently unreachable
+// end-to-end there (see the pinned member-agent case above).
 test("shouldHideAgentFromMentions: shows member agents with unknown invocability (not in directory)", () => {
   assert.equal(
     shouldHideAgentFromMentions({
diff --git a/desktop/src/features/agents/lib/agentAutocompleteEligibility.ts b/desktop/src/features/agents/lib/agentAutocompleteEligibility.ts
index e4afe7fea4..e1fdc6c52e 100644
--- a/desktop/src/features/agents/lib/agentAutocompleteEligibility.ts
+++ b/desktop/src/features/agents/lib/agentAutocompleteEligibility.ts
@@ -64,6 +64,25 @@ export function isAgentIdentityInManagedList(
   );
 }
 
+/**
+ * Managed-list membership alone cannot admit remote agents: managed agents
+ * are never minted from relay events (see apply_inbound_managed_agent), so a
+ * shared relay agent has no local record on this device. Mention candidacy
+ * therefore checks the mentionable set from getMentionableAgentPubkeys —
+ * locally managed agents plus directory-mentionable relay agents, the same
+ * eligibility `useNewMessageRecipients` already trusts. An agent identity
+ * outside that set stays hidden.
+ */
+export function isAgentIdentityMentionable(
+  candidate: { isAgent?: boolean; pubkey: string },
+  mentionableAgentPubkeys: ReadonlySet<string>,
+) {
+  return (
+    candidate.isAgent !== true ||
+    mentionableAgentPubkeys.has(normalizePubkey(candidate.pubkey))
+  );
+}
+
 export function shouldHideAgentFromMentions({
   isAgent,
   isMember,
diff --git a/desktop/src/features/agents/lib/personaCatalogRelay.test.mjs b/desktop/src/features/agents/lib/personaCatalogRelay.test.mjs
index fbaf1f5274..f3727598c0 100644
--- a/desktop/src/features/agents/lib/personaCatalogRelay.test.mjs
+++ b/desktop/src/features/agents/lib/personaCatalogRelay.test.mjs
@@ -356,7 +356,7 @@ test("test_foreign_entry_with_no_local_copy_stays_unselected", () => {
     BOB,
   );
 
-  assert.equal(personas[0].id, "catalog:" + ALICE + ":reviewer");
+  assert.equal(personas[0].id, `catalog:${ALICE}:reviewer`);
   assert.equal(personas[0].isActive, false);
 });
 
@@ -377,7 +377,7 @@ test("test_catalog_source_match_is_scoped_to_the_publishing_owner", () => {
     ALICE,
   );
 
-  assert.equal(personas[0].id, "catalog:" + BOB + ":reviewer");
+  assert.equal(personas[0].id, `catalog:${BOB}:reviewer`);
   assert.equal(personas[0].isActive, false);
 });
 
diff --git a/desktop/src/features/messages/lib/useMentions.ts b/desktop/src/features/messages/lib/useMentions.ts
index 0c73b75339..306438d20d 100644
--- a/desktop/src/features/messages/lib/useMentions.ts
+++ b/desktop/src/features/messages/lib/useMentions.ts
@@ -16,7 +16,7 @@ import {
   coalesceAutocompleteCandidatesByKey,
   getMentionableAgentPubkeys,
   getSharedChannelIds,
-  isAgentIdentityInManagedList,
+  isAgentIdentityMentionable,
   shouldHideAgentFromMentions,
 } from "@/features/agents/lib/agentAutocompleteEligibility";
 import {
@@ -246,7 +246,7 @@ export function useMentions(
       if (isArchivedDiscovery(pubkey)) {
         return;
       }
-      if (!isAgentIdentityInManagedList(candidate, managedAgentPubkeys)) {
+      if (!isAgentIdentityMentionable(candidate, mentionableAgentPubkeys)) {
         return;
       }
       if (
@@ -420,7 +420,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..29bed6ef2c 100644
--- a/desktop/tests/e2e/mentions.spec.ts
+++ b/desktop/tests/e2e/mentions.spec.ts
@@ -209,7 +209,10 @@ 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);
+  // alice is a shared relay agent (directory respond_to: "anyone" with a
+  // shared channel) and a channel member: mentionable even though she is
+  // not locally managed.
+  await expect(dropdown.getByText("alice")).toBeVisible();
   await expect(dropdown.getByText("bob")).toBeVisible();
   await expect(dropdown.getByText("Fizz")).toBeVisible();
   await expect(dropdown.getByText("charlie")).toBeVisible();
@@ -227,6 +230,7 @@ test("@ trigger prioritizes channel members before runnable personas and other m
   const suggestionText = await suggestions.allInnerTexts();
   const fizzIndex = suggestionText.findIndex((text) => text.includes("Fizz"));
   const bobIndex = suggestionText.findIndex((text) => text.includes("bob"));
+  const aliceIndex = suggestionText.findIndex((text) => text.includes("alice"));
   const charlieIndex = suggestionText.findIndex((text) =>
     text.includes("charlie"),
   );
@@ -235,9 +239,13 @@ test("@ trigger prioritizes channel members before runnable personas and other m
   );
   expect(fizzIndex).toBeGreaterThanOrEqual(0);
   expect(bobIndex).toBeGreaterThanOrEqual(0);
+  expect(aliceIndex).toBeGreaterThanOrEqual(0);
   expect(charlieIndex).toBeGreaterThanOrEqual(0);
   expect(outsiderIndex).toEqual(-1);
   expect(bobIndex).toBeLessThan(fizzIndex);
+  // alice is a channel member, so she sorts in the member tier ahead of
+  // personas and non-member managed agents.
+  expect(aliceIndex).toBeLessThan(fizzIndex);
   expect(fizzIndex).toBeLessThan(charlieIndex);
 });
 
