diff --git a/apps/desktop/src/app/command-palette/index.tsx b/apps/desktop/src/app/command-palette/index.tsx
index 0014e4e71f1..59f8ba0cbae 100644
--- a/apps/desktop/src/app/command-palette/index.tsx
+++ b/apps/desktop/src/app/command-palette/index.tsx
@@ -49,6 +49,7 @@ import {
} from '@/lib/icons'
import { normalize } from '@/lib/text'
import { cn } from '@/lib/utils'
+import { resolveVersionStatus } from '@/lib/version-status'
import { $repoWorktrees } from '@/store/coding-status'
import {
$commandPaletteOpen,
@@ -59,8 +60,16 @@ import {
import { $bindings } from '@/store/keybinds'
import { openPetGenerate } from '@/store/pet-generate'
import { requestStartWorkSession } from '@/store/projects'
+import { $connection } from '@/store/session'
import { runGatewayRestart } from '@/store/system-actions'
-import { applyBackendUpdate } from '@/store/updates'
+import {
+ $backendUpdateApply,
+ $backendUpdateStatus,
+ $desktopVersion,
+ $updateApply,
+ $updateStatus,
+ requestActiveUpdate
+} from '@/store/updates'
import { canOpenNewWindow, openNewWindow } from '@/store/windows'
import { luminance } from '@/themes/color'
import { type ThemeMode, useTheme } from '@/themes/context'
@@ -93,6 +102,8 @@ interface PaletteItem {
action?: string
/** Renders a trailing check: this row IS the current setting (theme, mode). */
active?: boolean
+ /** Muted text beside the label — state the row acts on (a version, a count). */
+ detail?: string
icon: IconComponent
id: string
/** Keep the palette open after running (live-preview pickers like theme/mode). */
@@ -242,6 +253,7 @@ const PaletteRow = memo(function PaletteRow({
>
{item.label}
+ {item.detail && {item.detail}}
{combo && }
{item.to && }
{item.active && }
@@ -345,6 +357,35 @@ export function CommandPalette() {
const [search, setSearch] = useState('')
const [page, setPage] = useState(null)
+ // The Update row names the same install the statusbar names — same target
+ // selection, same resolver. Reduced to the label string: an in-flight apply
+ // rewrites these stores on every progress line, and only a changed string
+ // should rebuild the palette's groups.
+ const connection = useStore($connection)
+ const desktopVersion = useStore($desktopVersion)
+ const clientStatus = useStore($updateStatus)
+ const clientApply = useStore($updateApply)
+ const backendStatus = useStore($backendUpdateStatus)
+ const backendApply = useStore($backendUpdateApply)
+
+ const updateVersionLabel = useMemo(() => {
+ const backend = connection?.mode === 'remote'
+ const apply = backend ? backendApply : clientApply
+ const status = backend ? backendStatus : clientStatus
+
+ return resolveVersionStatus({
+ applying: apply.applying || apply.stage === 'restart',
+ behind: status?.behind ?? 0,
+ copy: t.shell.statusbar,
+ remote: backend,
+ restarting: apply.stage === 'restart',
+ sha: status?.currentSha?.slice(0, 7) ?? null,
+ target: backend ? 'backend' : 'client',
+ updateAvailable: status?.updateAvailable,
+ version: backend ? status?.currentVersion : desktopVersion?.appVersion
+ }).label
+ }, [backendApply, backendStatus, clientApply, clientStatus, connection?.mode, desktopVersion?.appVersion, t])
+
// cmdk's onSelect doesn't forward the triggering event — keep the last
// click/keydown modifiers so session rows can honour ⌘-Enter / ⌘-click.
const lastSelectMods = useRef<{ ctrlKey: boolean; metaKey: boolean; shiftKey: boolean }>({
@@ -582,11 +623,12 @@ export function CommandPalette() {
run: () => void runGatewayRestart()
},
{
+ detail: updateVersionLabel,
icon: Download,
id: 'cc-update-hermes',
keywords: ['update', 'upgrade', 'hermes', 'version', 'system', 'restart'],
label: cc.updateHermes,
- run: () => void applyBackendUpdate()
+ run: () => requestActiveUpdate()
}
]
},
@@ -663,7 +705,7 @@ export function CommandPalette() {
]
: [])
]
- }, [contributedItems, go, settingsSectionLabel, t, worktrees])
+ }, [contributedItems, go, settingsSectionLabel, t, updateVersionLabel, worktrees])
// The long, granular lists (settings fields, API keys, MCP servers, archived
// chats) only surface once the user types — otherwise they'd bury the