diff --git a/webview-ui/src/components/chat/TaskHeader.tsx b/webview-ui/src/components/chat/TaskHeader.tsx
index 1896df486b..dbce906a7c 100644
--- a/webview-ui/src/components/chat/TaskHeader.tsx
+++ b/webview-ui/src/components/chat/TaskHeader.tsx
@@ -15,6 +15,7 @@ import { useExtensionState } from "@src/context/ExtensionStateContext"
import { useSelectedModel } from "@/components/ui/hooks/useSelectedModel"
import Thumbnails from "../common/Thumbnails"
+import ModeBadge from "../common/ModeBadge"
import { TaskActions } from "./TaskActions"
import { ShareButton } from "./ShareButton"
@@ -91,10 +92,13 @@ const TaskHeader = ({
-
- {t("chat:task.title")}
- {!isTaskExpanded && ":"}
-
+
+
+ {t("chat:task.title")}
+ {!isTaskExpanded && ":"}
+
+ {currentTaskItem?.mode && }
+
{!isTaskExpanded && (
diff --git a/webview-ui/src/components/common/ModeBadge.tsx b/webview-ui/src/components/common/ModeBadge.tsx
new file mode 100644
index 0000000000..9782e39490
--- /dev/null
+++ b/webview-ui/src/components/common/ModeBadge.tsx
@@ -0,0 +1,51 @@
+import React from "react"
+import { getModeBySlug } from "../../../../src/shared/modes"
+import { useExtensionState } from "@/context/ExtensionStateContext"
+import { Badge } from "@/components/ui/badge"
+import { StandardTooltip } from "@/components/ui/standard-tooltip"
+
+export interface ModeBadgeProps {
+ modeSlug: string
+ className?: string
+}
+
+/**
+ * ModeBadge component displays a badge showing the mode name for a given mode slug.
+ * It handles cases where the mode is undefined or deleted gracefully.
+ * For long mode names, it truncates with ellipsis and shows full name in tooltip.
+ */
+const ModeBadge: React.FC = ({ modeSlug, className }) => {
+ const { customModes } = useExtensionState()
+
+ // Get mode details using the existing getModeBySlug function
+ const mode = getModeBySlug(modeSlug, customModes)
+
+ // If mode is not found, don't render anything
+ if (!mode) {
+ return null
+ }
+
+ // Extract just the mode name (without emoji if present)
+ // Mode names can be like "๐ป Code" or just "Code"
+ const modeName = mode.name
+
+ // For very long mode names, we'll let CSS handle truncation
+ const badgeContent = (
+
+ {modeName}
+
+ )
+
+ // If the mode name is longer than ~20 characters, wrap with tooltip
+ if (modeName.length > 20) {
+ return {badgeContent}
+ }
+
+ return badgeContent
+}
+
+export default ModeBadge
diff --git a/webview-ui/src/components/common/__tests__/ModeBadge.spec.tsx b/webview-ui/src/components/common/__tests__/ModeBadge.spec.tsx
new file mode 100644
index 0000000000..b76d2ee529
--- /dev/null
+++ b/webview-ui/src/components/common/__tests__/ModeBadge.spec.tsx
@@ -0,0 +1,169 @@
+// npx vitest run src/components/common/__tests__/ModeBadge.spec.tsx
+
+import { render, screen } from "@/utils/test-utils"
+import { DEFAULT_MODES, type ModeConfig } from "@roo-code/types"
+
+import ModeBadge from "../ModeBadge"
+
+// Mock the shared modes module
+vi.mock("../../../../../src/shared/modes", () => ({
+ getModeBySlug: vi.fn((slug: string, customModes?: any[]) => {
+ // First check custom modes
+ const customMode = customModes?.find((mode) => mode.slug === slug)
+ if (customMode) {
+ return customMode
+ }
+ // Then check built-in modes
+ return DEFAULT_MODES.find((mode) => mode.slug === slug)
+ }),
+}))
+
+// Mock the extension state context
+const mockExtensionState = {
+ customModes: [] as ModeConfig[],
+}
+
+vi.mock("../../../context/ExtensionStateContext", () => ({
+ useExtensionState: () => mockExtensionState,
+}))
+
+describe("ModeBadge", () => {
+ beforeEach(() => {
+ // Reset custom modes before each test
+ mockExtensionState.customModes = []
+ })
+
+ it("renders mode badge for built-in mode", () => {
+ render()
+
+ const badge = screen.getByText("๐ป Code")
+ expect(badge).toBeInTheDocument()
+ expect(badge).toHaveClass("border-vscode-input-border")
+ })
+
+ it("renders mode badge for architect mode", () => {
+ render()
+
+ expect(screen.getByText("๐๏ธ Architect")).toBeInTheDocument()
+ })
+
+ it("renders mode badge for ask mode", () => {
+ render()
+
+ expect(screen.getByText("โ Ask")).toBeInTheDocument()
+ })
+
+ it("renders mode badge for debug mode", () => {
+ render()
+
+ expect(screen.getByText("๐ชฒ Debug")).toBeInTheDocument()
+ })
+
+ it("renders mode badge for orchestrator mode", () => {
+ render()
+
+ expect(screen.getByText("๐ช Orchestrator")).toBeInTheDocument()
+ })
+
+ it("renders mode badge for custom mode", () => {
+ // Add a custom mode to the mock state
+ mockExtensionState.customModes = [
+ {
+ slug: "custom-test",
+ name: "๐งช Test Mode",
+ roleDefinition: "Test role",
+ groups: ["read"],
+ },
+ ]
+
+ render()
+
+ expect(screen.getByText("๐งช Test Mode")).toBeInTheDocument()
+ })
+
+ it("renders mode badge for custom mode without emoji", () => {
+ // Add a custom mode without emoji
+ mockExtensionState.customModes = [
+ {
+ slug: "plain-mode",
+ name: "Plain Mode",
+ roleDefinition: "Plain role",
+ groups: ["read"],
+ },
+ ]
+
+ render()
+
+ expect(screen.getByText("Plain Mode")).toBeInTheDocument()
+ })
+
+ it("returns null for undefined mode", () => {
+ const { container } = render()
+
+ expect(container.firstChild).toBeNull()
+ })
+
+ it("returns null for deleted custom mode", () => {
+ // Simulate a mode that was deleted but still referenced in history
+ render()
+
+ const { container } = render()
+ expect(container.firstChild).toBeNull()
+ })
+
+ it("applies custom className", () => {
+ render()
+
+ const badge = screen.getByText("๐ป Code")
+ expect(badge).toHaveClass("custom-class")
+ })
+
+ it("truncates long mode names with CSS", () => {
+ // Add a custom mode with a very long name
+ mockExtensionState.customModes = [
+ {
+ slug: "very-long-mode",
+ name: "This is a very long mode name that should be truncated",
+ roleDefinition: "Long role",
+ groups: ["read"],
+ },
+ ]
+
+ render()
+
+ const badge = screen.getByText("This is a very long mode name that should be truncated")
+ expect(badge).toHaveClass("max-w-24", "truncate")
+ })
+
+ it("has proper title attribute for accessibility", () => {
+ render()
+
+ const badge = screen.getByText("๐ป Code")
+ expect(badge).toHaveAttribute("title", "๐ป Code")
+ })
+
+ it("uses outline variant for consistent styling", () => {
+ render()
+
+ const badge = screen.getByText("๐ป Code")
+ expect(badge).toHaveClass("border-vscode-input-border")
+ })
+
+ it("handles custom mode overriding built-in mode", () => {
+ // Add a custom mode that overrides a built-in mode
+ mockExtensionState.customModes = [
+ {
+ slug: "code",
+ name: "๐ง Custom Code",
+ roleDefinition: "Custom code role",
+ groups: ["read", "edit"],
+ },
+ ]
+
+ render()
+
+ // Should show the custom mode name, not the built-in one
+ expect(screen.getByText("๐ง Custom Code")).toBeInTheDocument()
+ expect(screen.queryByText("๐ป Code")).not.toBeInTheDocument()
+ })
+})
diff --git a/webview-ui/src/components/history/TaskItemHeader.tsx b/webview-ui/src/components/history/TaskItemHeader.tsx
index bdddb090c8..a6916bcc46 100644
--- a/webview-ui/src/components/history/TaskItemHeader.tsx
+++ b/webview-ui/src/components/history/TaskItemHeader.tsx
@@ -3,6 +3,7 @@ import type { HistoryItem } from "@roo-code/types"
import { formatDate } from "@/utils/format"
import { DeleteButton } from "./DeleteButton"
import { cn } from "@/lib/utils"
+import ModeBadge from "../common/ModeBadge"
export interface TaskItemHeaderProps {
item: HistoryItem
@@ -22,6 +23,7 @@ const TaskItemHeader: React.FC = ({ item, isSelectionMode,
{formatDate(item.ts)}
+ {item.mode && }
{/* Action Buttons */}