diff --git a/desktop/src/features/settings/ui/DoctorSettingsPanel.tsx b/desktop/src/features/settings/ui/DoctorSettingsPanel.tsx index 894d88bd2..042b9eca9 100644 --- a/desktop/src/features/settings/ui/DoctorSettingsPanel.tsx +++ b/desktop/src/features/settings/ui/DoctorSettingsPanel.tsx @@ -10,6 +10,10 @@ import { useInstallAcpRuntimeMutation, } from "@/features/agents/hooks"; import { ProfileAvatar } from "@/features/profile/ui/ProfileAvatar"; +import { + PRESET_LOGOS, + RuntimeIcon, +} from "@/features/onboarding/ui/RuntimeIcon"; import type { AcpAuthMethod, AcpRuntimeCatalogEntry } from "@/shared/api/types"; import { getInstallErrorMessage } from "@/shared/lib/installError"; import { cn } from "@/shared/lib/cn"; @@ -61,6 +65,22 @@ function runtimeInstallGuideLabel(runtime: AcpRuntimeCatalogEntry) { } function RuntimeLogo({ runtime }: { runtime: AcpRuntimeCatalogEntry }) { + // Bundled preset logos: presets deliberately emit an empty avatar_url (no + // remote or user-supplied icon URLs), so route them through RuntimeIcon — + // the same component the preset gallery uses — which owns the PRESET_LOGOS + // map and the per-logo contrast treatments (omp needs a dark chip, grok a + // light one). Builtins keep the ProfileAvatar path below. + if (PRESET_LOGOS[runtime.id.trim().toLowerCase()]) { + return ( + + + + ); + } + const avatarUrl = RUNTIME_LOGO_URLS[runtime.id] ?? runtime.avatarUrl; return ( diff --git a/desktop/tests/e2e/harness-management.spec.ts b/desktop/tests/e2e/harness-management.spec.ts index cf0064da4..04ccd062d 100644 --- a/desktop/tests/e2e/harness-management.spec.ts +++ b/desktop/tests/e2e/harness-management.spec.ts @@ -171,6 +171,29 @@ test.describe("preset gallery", () => { }); }); +// ── Preset logos in the Agent runtimes list ────────────────────────────────── + +test("Agent runtimes rows render bundled preset logos, not initials", async ({ + page, +}) => { + await installMockBridge(page, { + acpRuntimesCatalog: [HERMES_AVAILABLE, OPENCLAW_NOT_INSTALLED], + }); + await openHarnessSettings(page); + + // Preset rows in "Agent runtimes" must show the same bundled logo the + // preset gallery uses (PRESET_LOGOS via RuntimeIcon), even though presets + // emit an empty avatar_url (the no-remote-icon security line). + for (const [id, file] of [ + ["hermes", "/harness-logos/hermes.png"], + ["openclaw", "/harness-logos/openclaw.svg"], + ] as const) { + const logo = page.getByTestId(`doctor-runtime-logo-${id}`); + await expect(logo).toBeVisible(); + await expect(logo.locator("img")).toHaveAttribute("src", file); + } +}); + // ── Custom harness add ──────────────────────────────────────────────────────── test.describe("add custom harness", () => {