diff --git a/packages/dashboard/app/components/ProviderIcon.tsx b/packages/dashboard/app/components/ProviderIcon.tsx
index c0cf113ffd..969e1d4b62 100644
--- a/packages/dashboard/app/components/ProviderIcon.tsx
+++ b/packages/dashboard/app/components/ProviderIcon.tsx
@@ -526,6 +526,27 @@ function GitHubIcon({ size, color, label = "GitHub" }: { size: number; color: st
);
}
+/*
+FNXC:ProviderIcons 2026-07-18-18:24:
+FN-8354: OMP ACP is a first-class provider, so its shared dashboard icon must use the official omp.sh favicon's stepped T mark rather than the generic Cpu fallback. Render its official single-color silhouette with the provider token so it remains legible in either theme.
+Source: https://omp.sh/favicon.svg
+*/
+function OmpIcon({ size, color, label = "Oh My Pi" }: { size: number; color: string; label?: string }) {
+ return (
+
+ );
+}
+
// Hermes — caduceus (winged staff) mark, single-color so it adapts to theme.
function HermesIcon({ size, color, label = "Hermes" }: { size: number; color: string; label?: string }) {
return (
@@ -817,7 +838,10 @@ const providerConfig: Record<
vercel: { component: VercelIcon, color: "var(--provider-vercel)" },
"vercel-ai-gateway": { component: VercelIcon, color: "var(--provider-vercel)", label: "Vercel AI Gateway" },
- // Runtime-plugin marks (Hermes / OpenClaw / Paperclip).
+ // Runtime-plugin marks (OMP / Hermes / OpenClaw / Paperclip).
+ "omp-cli": { component: OmpIcon, color: "var(--provider-omp)", label: "Oh My Pi — via omp ACP" },
+ omp: { component: OmpIcon, color: "var(--provider-omp)", label: "Oh My Pi" },
+ "oh-my-pi": { component: OmpIcon, color: "var(--provider-omp)", label: "Oh My Pi" },
hermes: { component: HermesIcon, color: "var(--provider-hermes)", label: "Hermes" },
"hermes-agent": { component: HermesIcon, color: "var(--provider-hermes)", label: "Hermes" },
hermesagent: { component: HermesIcon, color: "var(--provider-hermes)", label: "Hermes" },
diff --git a/packages/dashboard/app/components/__tests__/ProviderIcon.test.tsx b/packages/dashboard/app/components/__tests__/ProviderIcon.test.tsx
index 0eb638c3b6..66d981f64b 100644
--- a/packages/dashboard/app/components/__tests__/ProviderIcon.test.tsx
+++ b/packages/dashboard/app/components/__tests__/ProviderIcon.test.tsx
@@ -74,6 +74,29 @@ describe("ProviderIcon", () => {
expect(document.querySelector('[data-provider="cursor"] svg:not([data-testid])')).not.toBeInTheDocument();
});
+ it("renders the OMP brand icon, auth label, and tokenized color for omp-cli", () => {
+ render();
+ const svg = screen.getByTestId("omp-icon");
+ expect(svg).toBeInTheDocument();
+ expect(screen.getByLabelText("Oh My Pi — via omp ACP")).toBeInTheDocument();
+ expect(svg.parentElement).toHaveAttribute("data-provider", "omp-cli");
+ expect(svg.parentElement).toHaveStyle({ color: "var(--provider-omp)" });
+ expect(svg.querySelector("path")).toHaveAttribute("fill", "var(--provider-omp)");
+ });
+
+ it.each(["omp", "oh-my-pi"])("renders the OMP brand icon for the %s alias", (provider) => {
+ render();
+ const svg = screen.getByTestId("omp-icon");
+ expect(svg.parentElement).toHaveAttribute("data-provider", provider);
+ expect(svg.parentElement).toHaveStyle({ color: "var(--provider-omp)" });
+ });
+
+ it("infers OMP model-provider strings to the OMP brand icon", () => {
+ render();
+ expect(screen.getByTestId("omp-icon")).toBeInTheDocument();
+ expect(document.querySelector('[data-provider="omp/claude-sonnet-4"] svg:not([data-testid])')).not.toBeInTheDocument();
+ });
+
it("renders grok-cli icon reusing the xAI brand mark", () => {
render();
const svg = screen.getByTestId("xai-icon");
diff --git a/packages/dashboard/app/styles.css b/packages/dashboard/app/styles.css
index 7d84fbc13e..f440740687 100644
--- a/packages/dashboard/app/styles.css
+++ b/packages/dashboard/app/styles.css
@@ -385,6 +385,7 @@ svg.spinning {
--provider-cursor-cli: #7c3aed;
--provider-ollama: #d4a27f;
/* Runtime-plugin marks. */
+ --provider-omp: var(--text);
--provider-hermes: #d4961c;
--provider-openclaw: #ff4f40;
--provider-paperclip: var(--text);
diff --git a/packages/dashboard/app/utils/__tests__/providerIconKey.test.ts b/packages/dashboard/app/utils/__tests__/providerIconKey.test.ts
index 8b54994fe0..aea710a4f5 100644
--- a/packages/dashboard/app/utils/__tests__/providerIconKey.test.ts
+++ b/packages/dashboard/app/utils/__tests__/providerIconKey.test.ts
@@ -13,6 +13,9 @@ describe("inferProviderIconKey", () => {
["Cursor", "cursor-cli"],
["cursor-agent", "cursor-cli"],
["cursor/gpt-5", "cursor-cli"],
+ ["omp-cli/MiniMax-M2.5", "omp-cli"],
+ ["omp/claude-sonnet-4", "omp-cli"],
+ ["Oh My Pi", "omp-cli"],
["ollama/llama3", "ollama"],
["minimax-text-01", "minimax"],
["zhipu-glm-4", "zai"],
@@ -32,6 +35,11 @@ describe("inferProviderIconKey", () => {
expect(inferProviderIconKey(input)).toBe(expected);
});
+ it("does not attribute bare model ids to OMP", () => {
+ expect(inferProviderIconKey("MiniMax-M2.5")).toBe("minimax");
+ expect(inferProviderIconKey("claude-sonnet-4")).not.toBe("omp-cli");
+ });
+
it("returns unknown ids unchanged so ProviderIcon can render its fallback", () => {
expect(inferProviderIconKey("custom-model-v1")).toBe("custom-model-v1");
expect(inferProviderIconKey("not-a-glmish-model")).toBe("not-a-glmish-model");
diff --git a/packages/dashboard/app/utils/providerIconKey.ts b/packages/dashboard/app/utils/providerIconKey.ts
index 467e9ce23f..6eb11e7ef8 100644
--- a/packages/dashboard/app/utils/providerIconKey.ts
+++ b/packages/dashboard/app/utils/providerIconKey.ts
@@ -6,6 +6,8 @@ FNXC:ProviderIcons 2026-06-28-20:46:
Standalone GLM model ids such as glm-5.1, glm-4.5-air, and glm-5v-turbo are Z.ai/Zhipu-family models even when the analytics label omits zai/zhipu. Match GLM only at model-id segment boundaries so unrelated words do not hijack the Z.ai provider icon.
*/
const GLM_MODEL_SEGMENT_PATTERN = /(?:^|[/:_-])glm(?:$|[\d._:-]|-[\da-z])/;
+const OMP_PROVIDER_SEGMENT_PATTERN = /(?:^|[/:_\s-])omp(?:$|[/:_\s-])/;
+const OMP_PROVIDER_NAME_PATTERN = /oh[-\s]my[-\s]pi/;
export function inferProviderIconKey(modelOrProviderName: string): string {
const normalized = modelOrProviderName.toLowerCase();
@@ -19,6 +21,13 @@ export function inferProviderIconKey(modelOrProviderName: string): string {
if (normalized.includes("cursor")) {
return "cursor-cli";
}
+ /*
+ FNXC:ProviderIcons 2026-07-18-18:24:
+ FN-8354: omp ACP model ids can contain another vendor model name (for example omp/claude-sonnet-4), but must retain the OMP brand mark on selection and analytics surfaces. Match only an OMP segment or the Oh My Pi name so unrelated strings and untagged model ids keep existing inference/fallback behavior.
+ */
+ if (OMP_PROVIDER_SEGMENT_PATTERN.test(normalized) || OMP_PROVIDER_NAME_PATTERN.test(normalized)) {
+ return "omp-cli";
+ }
if (normalized.includes("claude") || normalized.includes("anthropic")) {
return "anthropic";
}