fix(desktop): render bundled preset logos in the Agent runtimes list

Preset rows in Settings → Agent runtimes fell back to initials because
DoctorSettingsPanel's RuntimeLogo only knew the builtin logo map, while
presets deliberately emit an empty avatar_url (no remote or user-supplied
icon URLs). Route preset ids through RuntimeIcon — the same component the
preset gallery uses — so both surfaces share PRESET_LOGOS and the per-logo
contrast treatments. Builtins keep the ProfileAvatar path; customs keep
the terminal glyph by design.

Playwright regression in harness-management.spec.ts asserts the doctor
rows render the bundled hermes/openclaw files, not initials.

Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
This commit is contained in:
npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d
2026-07-26 08:22:31 -04:00
co-authored by Tyler Longwell
parent 9787250322
commit 9fd2ed3ebd
2 changed files with 43 additions and 0 deletions
@@ -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 (
<span
className="flex h-9 w-9 shrink-0 items-center justify-center"
data-testid={`doctor-runtime-logo-${runtime.id}`}
>
<RuntimeIcon className="h-9 w-9" runtime={runtime} />
</span>
);
}
const avatarUrl = RUNTIME_LOGO_URLS[runtime.id] ?? runtime.avatarUrl;
return (
@@ -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", () => {