diff --git a/apps/desktop/src/app/chat/composer/controls.tsx b/apps/desktop/src/app/chat/composer/controls.tsx
index 8bc1a2b7cf9..b79753804c1 100644
--- a/apps/desktop/src/app/chat/composer/controls.tsx
+++ b/apps/desktop/src/app/chat/composer/controls.tsx
@@ -9,6 +9,7 @@ import { formatCombo } from '@/lib/keybinds/combo'
import { cn } from '@/lib/utils'
import type { ConversationStatus } from './hooks/use-voice-conversation'
+import { ModelPill } from './model-pill'
import type { ChatBarState, VoiceStatus } from './types'
export const ICON_BTN = 'size-(--composer-control-size) shrink-0 rounded-md'
@@ -81,6 +82,7 @@ export function ComposerControls({
return (
+
{canSteer && (
diff --git a/apps/desktop/src/app/chat/composer/model-pill.tsx b/apps/desktop/src/app/chat/composer/model-pill.tsx
new file mode 100644
index 00000000000..0ea963a3628
--- /dev/null
+++ b/apps/desktop/src/app/chat/composer/model-pill.tsx
@@ -0,0 +1,72 @@
+import { useStore } from '@nanostores/react'
+
+import { Button } from '@/components/ui/button'
+import { DropdownMenu, DropdownMenuContent, DropdownMenuTrigger } from '@/components/ui/dropdown-menu'
+import { useI18n } from '@/i18n'
+import { ChevronDown } from '@/lib/icons'
+import { formatModelStatusLabel } from '@/lib/model-status-label'
+import { cn } from '@/lib/utils'
+import {
+ $currentFastMode,
+ $currentModel,
+ $currentProvider,
+ $currentReasoningEffort,
+ setModelPickerOpen
+} from '@/store/session'
+
+import type { ChatBarState } from './types'
+
+const PILL = cn(
+ 'h-(--composer-control-size) max-w-40 shrink-0 gap-1 rounded-md px-2 text-xs font-normal',
+ 'text-(--ui-text-tertiary) hover:bg-(--chrome-action-hover) hover:text-foreground'
+)
+
+/**
+ * Composer model selector — the relocated status-bar pill. Reuses the live
+ * `model.options` dropdown (`modelMenuContent`) verbatim; falls back to the
+ * full picker when the gateway is closed and no live menu exists.
+ */
+export function ModelPill({ disabled, model }: { disabled: boolean; model: ChatBarState['model'] }) {
+ const copy = useI18n().t.shell.statusbar
+ const currentModel = useStore($currentModel)
+ const currentProvider = useStore($currentProvider)
+ const fastMode = useStore($currentFastMode)
+ const reasoningEffort = useStore($currentReasoningEffort)
+
+ const label = (
+ <>
+ {formatModelStatusLabel(currentModel, { fastMode, reasoningEffort })}
+
+ >
+ )
+ const title = currentProvider ? copy.modelTitle(currentProvider, currentModel || copy.modelNone) : copy.switchModel
+
+ if (!model.modelMenuContent) {
+ return (
+
+ )
+ }
+
+ return (
+
+
+
+
+
+ {model.modelMenuContent}
+
+
+ )
+}
diff --git a/apps/desktop/src/app/chat/composer/types.ts b/apps/desktop/src/app/chat/composer/types.ts
index 36b3b8e6d3d..6d9444a6d93 100644
--- a/apps/desktop/src/app/chat/composer/types.ts
+++ b/apps/desktop/src/app/chat/composer/types.ts
@@ -1,3 +1,5 @@
+import type { ReactNode } from 'react'
+
import type { HermesGateway } from '@/hermes'
import type { ComposerAttachment } from '@/store/composer'
@@ -22,6 +24,8 @@ export interface ChatBarState {
canSwitch: boolean
loading?: boolean
quickModels?: QuickModelOption[]
+ /** Reused status-bar dropdown (built with gateway + selectModel upstream). */
+ modelMenuContent?: ReactNode
}
tools: { enabled: boolean; label: string; suggestions?: ContextSuggestion[] }
voice: { enabled: boolean; active: boolean }
diff --git a/apps/desktop/src/app/chat/index.tsx b/apps/desktop/src/app/chat/index.tsx
index c9f525653e7..63983caaa1a 100644
--- a/apps/desktop/src/app/chat/index.tsx
+++ b/apps/desktop/src/app/chat/index.tsx
@@ -62,6 +62,7 @@ import { threadLoadingState } from './thread-loading'
interface ChatViewProps extends Omit, 'onSubmit'> {
gateway: HermesGateway | null
+ modelMenuContent?: React.ReactNode
onToggleSelectedPin: () => void
onDeleteSelectedSession: () => void
onCancel: () => Promise | void
@@ -250,6 +251,7 @@ function ChatRuntimeBoundary({
export function ChatView({
className,
gateway,
+ modelMenuContent,
onToggleSelectedPin,
onDeleteSelectedSession,
onCancel,
@@ -346,6 +348,7 @@ export function ChatView({
provider: currentProvider,
canSwitch: gatewayOpen,
loading: !gatewayOpen || (!currentModel && !currentProvider),
+ modelMenuContent,
quickModels
},
tools: {
@@ -358,7 +361,7 @@ export function ChatView({
active: false
}
}),
- [contextSuggestions, currentModel, currentProvider, gatewayOpen, quickModels]
+ [contextSuggestions, currentModel, currentProvider, gatewayOpen, modelMenuContent, quickModels]
)
// Drop files anywhere in the conversation area, not just on the composer
diff --git a/apps/desktop/src/app/desktop-controller.tsx b/apps/desktop/src/app/desktop-controller.tsx
index 5ff162a2ca4..e071a2a0ce6 100644
--- a/apps/desktop/src/app/desktop-controller.tsx
+++ b/apps/desktop/src/app/desktop-controller.tsx
@@ -859,7 +859,6 @@ export function DesktopController() {
gatewayLogLines,
gatewayState,
inferenceStatus,
- modelMenuContent,
openAgents,
freshDraftReady,
openCommandCenterSection,
@@ -981,6 +980,7 @@ export function DesktopController() {
composer.addContextRefAttachment(`@url:${formatRefValue(url)}`, url)}
onAttachDroppedItems={composer.attachDroppedItems}
diff --git a/apps/desktop/src/app/shell/hooks/use-statusbar-items.tsx b/apps/desktop/src/app/shell/hooks/use-statusbar-items.tsx
index 53ce2dcc150..b9a2d715454 100644
--- a/apps/desktop/src/app/shell/hooks/use-statusbar-items.tsx
+++ b/apps/desktop/src/app/shell/hooks/use-statusbar-items.tsx
@@ -1,5 +1,4 @@
import { useStore } from '@nanostores/react'
-import type { ReactNode } from 'react'
import { useCallback, useMemo } from 'react'
import type { CommandCenterSection } from '@/app/command-center'
@@ -9,7 +8,6 @@ import { useI18n } from '@/i18n'
import {
Activity,
AlertCircle,
- ChevronDown,
Clock,
Command,
Hash,
@@ -19,7 +17,6 @@ import {
Zap,
ZapFilled
} from '@/lib/icons'
-import { formatModelStatusLabel } from '@/lib/model-status-label'
import type { RuntimeReadinessResult } from '@/lib/runtime-readiness'
import { contextBarLabel, LiveDuration, usageContextLabel } from '@/lib/statusbar'
import { cn } from '@/lib/utils'
@@ -30,16 +27,11 @@ import {
$activeSessionId,
$busy,
$connection,
- $currentFastMode,
- $currentModel,
- $currentProvider,
- $currentReasoningEffort,
$currentUsage,
$sessionStartedAt,
$turnStartedAt,
$workingSessionIds,
$yoloActive,
- setModelPickerOpen,
setYoloActive
} from '@/store/session'
import { $subagentsBySession, activeSubagentCount } from '@/store/subagents'
@@ -65,7 +57,6 @@ interface StatusbarItemsOptions {
gatewayLogLines: readonly string[]
gatewayState: string
inferenceStatus: RuntimeReadinessResult | null
- modelMenuContent?: ReactNode
openAgents: () => void
openCommandCenterSection: (section: CommandCenterSection) => void
freshDraftReady: boolean
@@ -83,7 +74,6 @@ export function useStatusbarItems({
gatewayLogLines,
gatewayState,
inferenceStatus,
- modelMenuContent,
openAgents,
openCommandCenterSection,
freshDraftReady,
@@ -97,10 +87,6 @@ export function useStatusbarItems({
const terminalTakeover = useStore($terminalTakeover)
const yoloActive = useStore($yoloActive)
const busy = useStore($busy)
- const currentFastMode = useStore($currentFastMode)
- const currentModel = useStore($currentModel)
- const currentProvider = useStore($currentProvider)
- const currentReasoningEffort = useStore($currentReasoningEffort)
const currentUsage = useStore($currentUsage)
const desktopActionTasks = useStore($desktopActionTasks)
const previewServerRestartStatus = useStore($previewServerRestartStatus)
@@ -416,37 +402,6 @@ export function useStatusbarItems({
title: yoloActive ? copy.yoloOn : copy.yoloOff,
variant: 'action'
},
- {
- id: 'model-summary',
- label: (
-
-
- {formatModelStatusLabel(currentModel, {
- fastMode: currentFastMode,
- reasoningEffort: currentReasoningEffort
- })}
-
-
-
- ),
- ...(modelMenuContent
- ? {
- menuAlign: 'end' as const,
- menuClassName: 'w-64',
- menuContent: modelMenuContent,
- title: currentProvider
- ? copy.modelTitle(currentProvider, currentModel || copy.modelNone)
- : copy.switchModel,
- variant: 'menu' as const
- }
- : {
- onSelect: () => setModelPickerOpen(true),
- title: currentProvider
- ? copy.providerModelTitle(currentProvider, currentModel || copy.noModel)
- : copy.openModelPicker,
- variant: 'action' as const
- })
- },
{
className: `w-7 justify-center px-0${terminalTakeover ? ' bg-accent/55 text-foreground' : ''}`,
hidden: !chatOpen,
@@ -465,11 +420,6 @@ export function useStatusbarItems({
contextBar,
contextUsage,
copy,
- currentFastMode,
- currentModel,
- currentProvider,
- currentReasoningEffort,
- modelMenuContent,
sessionStartedAt,
showYoloToggle,
terminalTakeover,