diff --git a/packages/views/chat/components/chat-session-header.tsx b/packages/views/chat/components/chat-session-header.tsx
index aab8366808..d4be8d87ff 100644
--- a/packages/views/chat/components/chat-session-header.tsx
+++ b/packages/views/chat/components/chat-session-header.tsx
@@ -105,14 +105,7 @@ export function ChatSessionHeader({
return (
{agent ? (
-
+
) : (
)}
diff --git a/packages/views/common/actor-avatar.tsx b/packages/views/common/actor-avatar.tsx
index 3a088d97e5..59a455add5 100644
--- a/packages/views/common/actor-avatar.tsx
+++ b/packages/views/common/actor-avatar.tsx
@@ -11,10 +11,6 @@ import {
import { useActorName } from "@multica/core/workspace/hooks";
import { useAgentPresenceDetail } from "@multica/core/agents";
import { useCurrentWorkspace, useWorkspacePaths } from "@multica/core/paths";
-import {
- AgentRuntimeBadge,
- RUNTIME_BADGE_MIN_AVATAR_PX,
-} from "./agent-runtime-badge";
import { AgentProfileCard } from "../agents/components/agent-profile-card";
import { AgentLivePeekCard } from "../agents/components/agent-live-peek-card";
import { MemberProfileCard } from "../members/member-profile-card";
@@ -55,18 +51,6 @@ interface ActorAvatarProps {
* popover inside the dropdown.
*/
showStatusDot?: boolean;
- /**
- * Overlay the provider mark of the agent's runtime at the avatar's top-right.
- * Use on identity/config surfaces — the agent's own page, its profile card,
- * a chat session header — where "what is running underneath this?" is a live
- * question. Has no effect for non-agent actors.
- *
- * Independent of `showStatusDot`, and the two are designed to coexist: they
- * sit at opposite ends of the circle and answer different questions (can it
- * take work right now, vs what backs it). No-ops below
- * {@link RUNTIME_BADGE_MIN_AVATAR_PX} — see that constant for why.
- */
- showRuntimeBadge?: boolean;
/**
* When `enableHoverCard` is on for an agent, choose which payload to
* render. See {@link AgentHoverCardVariant}. Defaults to `"profile"` so
@@ -92,7 +76,6 @@ export function ActorAvatar({
className,
enableHoverCard,
showStatusDot,
- showRuntimeBadge,
hoverCardVariant = "profile",
profileLink,
}: ActorAvatarProps) {
@@ -111,23 +94,19 @@ export function ActorAvatar({
/>
);
- // Optional overlays. Only meaningful for agents — members have no presence
- // backbone and no runtime. Wrapping unconditionally with relative inline-flex
- // would create extra DOM for every avatar; we only wrap when an overlay is
+ // Optional presence overlay. Only meaningful for agents — members have no
+ // presence backbone. Wrapping unconditionally with relative inline-flex
+ // would create extra DOM for every avatar; we only wrap when the dot is
// asked for.
- const isAgent = actorType === "agent";
- const wrapDot = showStatusDot && isAgent;
- const wrapBadge = showRuntimeBadge && isAgent;
- const dotted =
- wrapDot || wrapBadge ? (
-
- {avatar}
- {wrapBadge && }
- {wrapDot && }
-
- ) : (
- avatar
- );
+ const wrapDot = showStatusDot && actorType === "agent";
+ const dotted = wrapDot ? (
+
+ {avatar}
+
+
+ ) : (
+ avatar
+ );
const shouldLinkToProfile =
profileLink ??
(actorType === "member" || actorType === "agent" || actorType === "squad");
@@ -237,7 +216,6 @@ export function AgentStatusDot({ agentId, size }: { agentId: string; size?: Avat
);
}
-
/**
* Wraps an agent avatar in a hover-card. The trigger is keyboard-focusable
* only when no focusable ancestor (link/button) already provides a tab stop —
diff --git a/packages/views/common/agent-runtime-badge.test.tsx b/packages/views/common/agent-runtime-badge.test.tsx
deleted file mode 100644
index 21d04f06d0..0000000000
--- a/packages/views/common/agent-runtime-badge.test.tsx
+++ /dev/null
@@ -1,115 +0,0 @@
-/**
- * Runtime provider badge on agent avatars.
- *
- * The badge and the presence dot are two overlays on one circle, so the rules
- * that keep them from fighting each other are the ones worth pinning: the
- * badge only appears where a provider mark is actually legible, it never
- * displaces the dot, and it stays away from anything it can't resolve.
- */
-import type { ReactElement } from "react";
-import { afterEach, describe, expect, it, vi } from "vitest";
-import { cleanup, render, screen } from "@testing-library/react";
-import { NavigationProvider } from "../navigation/context";
-import type { NavigationAdapter } from "../navigation/types";
-
-const provider = vi.hoisted(() => ({ current: "claude" as string | null }));
-
-vi.mock("@multica/core/workspace/hooks", () => ({
- useActorName: () => ({
- getActorName: () => "Lambda",
- getActorInitials: () => "L",
- getActorAvatarUrl: () => null,
- }),
-}));
-
-vi.mock("@multica/core/paths", () => ({
- useWorkspacePaths: () => ({
- memberDetail: (id: string) => `/acme/members/${id}`,
- agentDetail: (id: string) => `/acme/agents/${id}`,
- squadDetail: (id: string) => `/acme/squads/${id}`,
- }),
- useCurrentWorkspace: () => ({ id: "ws1", slug: "acme" }),
-}));
-
-vi.mock("@multica/core/agents", () => ({
- useAgentPresenceDetail: () => ({ availability: "online", workload: "idle" }),
- useAgentRuntimeProvider: () => provider.current,
-}));
-
-vi.mock("../agents/components/agent-profile-card", () => ({
- AgentProfileCard: () => null,
-}));
-vi.mock("../agents/components/agent-live-peek-card", () => ({
- AgentLivePeekCard: () => null,
-}));
-vi.mock("../members/member-profile-card", () => ({
- MemberProfileCard: () => null,
-}));
-vi.mock("../squads/components/squad-profile-card", () => ({
- SquadProfileCard: () => null,
-}));
-
-import { ActorAvatar } from "./actor-avatar";
-
-const AGENT_ID = "8f14e45f-ceea-4d0e-a1a2-9b1c0d3e4f5a";
-
-// The avatar wraps itself in a profile link, which needs the adapter.
-const ADAPTER: NavigationAdapter = {
- push: vi.fn(),
- replace: vi.fn(),
- back: vi.fn(),
- pathname: "/",
- searchParams: new URLSearchParams(),
- getShareableUrl: (p) => `https://app.example${p}`,
-};
-
-function renderAvatar(ui: ReactElement) {
- return render(
{ui});
-}
-
-afterEach(() => {
- cleanup();
- provider.current = "claude";
-});
-
-describe("AgentRuntimeBadge", () => {
- it("marks the avatar with the runtime's provider", () => {
- renderAvatar(
);
- expect(screen.getByLabelText("Runtime: Claude")).toBeInTheDocument();
- });
-
- // Provider marks are detailed artwork; at the ~8px a badge gets on a 20px
- // picker row they're a smudge. That size is the status dot's home turf, so
- // the badge has to stay out of it even when a caller asks for one.
- it("stays off avatars too small to render a legible mark", () => {
- renderAvatar(
);
- expect(screen.queryByLabelText("Runtime: Claude")).not.toBeInTheDocument();
- });
-
- it("renders both overlays when a surface asks for the dot too", () => {
- renderAvatar(
-
,
- );
- expect(screen.getByLabelText("Runtime: Claude")).toBeInTheDocument();
- expect(screen.getByLabelText("Status: Online")).toBeInTheDocument();
- });
-
- // Archived agent, deleted runtime, or a backend that omitted the field. A
- // mark that names the wrong runtime is worse than no mark.
- it("renders nothing when the provider cannot be resolved", () => {
- provider.current = null;
- renderAvatar(
);
- expect(screen.queryByLabelText(/^Runtime:/)).not.toBeInTheDocument();
- });
-
- it("has no effect for non-agent actors", () => {
- renderAvatar(
);
- expect(screen.queryByLabelText(/^Runtime:/)).not.toBeInTheDocument();
- });
-});
diff --git a/packages/views/common/agent-runtime-badge.tsx b/packages/views/common/agent-runtime-badge.tsx
deleted file mode 100644
index ee80778b3e..0000000000
--- a/packages/views/common/agent-runtime-badge.tsx
+++ /dev/null
@@ -1,72 +0,0 @@
-"use client";
-
-import { useAgentRuntimeProvider } from "@multica/core/agents";
-import { providerDisplayName } from "@multica/core/runtimes";
-import { useCurrentWorkspace } from "@multica/core/paths";
-import { AVATAR_SIZE_PX, type AvatarSize } from "@multica/ui/lib/avatar-size";
-import { ProviderLogo } from "../runtimes/components/provider-logo";
-
-/**
- * Smallest avatar that can carry a provider mark.
- *
- * A badge gets roughly 40% of the avatar's diameter, and provider marks are
- * detailed artwork (Claude's glyph, CodeBuddy's tile) rather than a flat shape
- * like the presence dot — below ~12px they read as a smudge. 32px is the first
- * tier that clears it. That rules the badge out of exactly the surfaces the
- * status dot lives on: nearly every `showStatusDot` call site is a 20px picker
- * row, where "can it take work right now" is the question and the provider
- * isn't.
- */
-export const RUNTIME_BADGE_MIN_AVATAR_PX = 32;
-
-/**
- * Provider mark overlaid on the top-right of an agent avatar — the opposite
- * end from the presence dot, so a surface can carry both without them
- * touching.
- *
- * Derived from the agent's current runtime on every render, so switching
- * runtimes moves the mark with it. Renders nothing when the provider can't be
- * resolved (loading, archived agent, deleted runtime): a wrong mark is worse
- * than none.
- *
- * Lives in its own module rather than beside `AgentStatusDot` because the
- * agent profile card needs it, and `actor-avatar` already imports that card
- * for its hover payload — importing back would close a cycle.
- */
-export function AgentRuntimeBadge({
- agentId,
- size,
-}: {
- agentId: string;
- size?: AvatarSize;
-}) {
- const ws = useCurrentWorkspace();
- const provider = useAgentRuntimeProvider(ws?.id, agentId);
- const px = size ? AVATAR_SIZE_PX[size] : 24;
- if (!provider || px < RUNTIME_BADGE_MIN_AVATAR_PX) return null;
-
- // The disc is background-coloured so a multi-coloured mark stays legible
- // over an emoji or photo avatar; the mark itself fills most of it. The
- // hairline is what makes it read as a chip sitting ON the avatar rather than
- // an icon floating beside it — several provider marks (Claude's asterisk,
- // Codex's ring) are airy line art with no silhouette of their own, and
- // without an edge the badge loses its anchor. Same treatment as ProviderChip.
- const disc = Math.round(px * 0.42);
- // Push the badge out past the bounding box so its centre lands on the
- // avatar's rim instead of inside it. Flush to the corner (`top-0 right-0`)
- // looks correct on a square but not on a circle: at 45° the edge has already
- // curved ~29% of the diameter away from that corner, so the badge ends up
- // sitting over the face. Offsetting by (badge radius − rim inset) leaves
- // roughly half of it overhanging, which is the usual platform-badge look.
- const offset = Math.round(px * 0.064);
-
- return (
-
-
-
- );
-}