diff --git a/desktop/src/features/agents/ui/AgentsView.tsx b/desktop/src/features/agents/ui/AgentsView.tsx index edb6f55bdfa..c6e836e2648 100644 --- a/desktop/src/features/agents/ui/AgentsView.tsx +++ b/desktop/src/features/agents/ui/AgentsView.tsx @@ -24,7 +24,8 @@ import { useManagedAgentActions } from "./useManagedAgentActions"; import { usePersonaActions } from "./usePersonaActions"; import { useTeamActions } from "./useTeamActions"; import { useProfilePanel } from "@/shared/context/ProfilePanelContext"; -import { useBakedBuildEnvQuery } from "@/features/agents/hooks"; +import { useBakedBuildEnvQuery, useRelayAgentsQuery } from "@/features/agents/hooks"; +import { normalizePubkey } from "@/shared/lib/pubkey"; import { isManagedAgentActive } from "@/features/agents/lib/managedAgentControlActions"; import { useGlobalAgentConfig } from "@/features/agents/useGlobalAgentConfig"; import { Button } from "@/shared/ui/button"; @@ -39,10 +40,19 @@ import { getInheritedAgentDefaults } from "./bakedEnvHelpers"; export function AgentsView() { const { openPersonaProfilePanel, openProfilePanel } = useProfilePanel(); + const relayAgentsQuery = useRelayAgentsQuery(); const { globalConfig } = useGlobalAgentConfig(); const { data: bakedEnv } = useBakedBuildEnvQuery({ enabled: true }); const inheritedDefaults = getInheritedAgentDefaults(globalConfig, bakedEnv); const agents = useManagedAgentActions(); + const externalAgents = React.useMemo(() => { + const localPubkeys = new Set( + agents.managedAgents.map((agent) => normalizePubkey(agent.pubkey)), + ); + return (relayAgentsQuery.data ?? []).filter( + (agent) => !localPubkeys.has(normalizePubkey(agent.pubkey)), + ); + }, [agents.managedAgents, relayAgentsQuery.data]); const personas = usePersonaActions(); const teamImportInputRef = React.useRef(null); const aiDefaultsTriggerRef = React.useRef(null); @@ -274,6 +284,40 @@ export function AgentsView() { }} /> + {externalAgents.length > 0 ? ( +
+
+

External agents

+

+ Running outside this Desktop. Their deployment owns their keys and lifecycle. +

+
+
+ {externalAgents.map((agent) => ( + + ))} +
+
+ ) : null} + { + const externalPubkey = "ab".repeat(32); + await installMockBridge(page, { + relayAgents: [ + { + agentType: "omp", + name: "Cluster GLM", + pubkey: externalPubkey, + status: "online", + }, + ], + }); + + await gotoApp(page); + await page.getByTestId("open-agents-view").click(); + + const section = page.getByTestId("external-agents-section"); + await expect(section).toBeVisible(); + await expect(section).toContainText("Cluster GLM"); + await expect(section).toContainText("External"); + await expect(section).toContainText("omp · online"); +}); + test("catalog hides built-ins and shows the shared-agent empty state", async ({ page, }) => {