Skip to content

Commit 4d7f8ad

Browse files
authored
feat(install): warn pip/Homebrew installs are unsupported (CLI, TUI, desktop) (NousResearch#57225)
* feat(install): warn pip/Homebrew installs are unsupported (CLI, TUI, desktop) pip and Homebrew are now Unsupported install methods per website/docs/getting-started/platform-support.md. Surface a warn-don't-block deprecation notice everywhere the install method is already shown, pointing at the platform-support docs and noting these installs will not receive further updates. NixOS (Tier 2) is untouched. - hermes_cli/config.py: shared is_unsupported_install_method() / format_unsupported_install_warning() helpers so the wording and docs link stay consistent across every surface. - hermes_cli/banner.py: generalize the existing pip-only banner warning to also cover Homebrew. - hermes_cli/main.py: hermes update and hermes update --check print the warning before proceeding (still update; warn, don't block). - tui_gateway/server.py: session.info gains install_warning. - ui-tui: SessionPanel renders install_warning alongside the existing 'N commits behind' notice. - apps/desktop: SessionRuntimeInfo/GatewayEventPayload gain install_warning; applyRuntimeInfo + the live session.info event fire a snoozable warning toast via a new reportInstallMethodWarning(), mirroring the existing backend-contract-skew toast pattern. i18n strings added for en/zh/zh-hant/ja. - Tests: updated pip banner assertions for the new wording, added a Homebrew banner test, and two tui_gateway session_info tests (install_warning present for pip, absent for git). * fix(nix): make `hermes` in developement environment actually work install modules as editable overlay with uv * feat: print install method when running --version * fix: correct detect install method when running from a subtree
1 parent 9de9c25 commit 4d7f8ad

23 files changed

Lines changed: 346 additions & 101 deletions

File tree

.envrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
watch_file pyproject.toml uv.lock
1+
watch_file pyproject.toml uv.lock hermes
22
watch_file package-lock.json package.json web/package.json ui-tui/package.json website/package.json apps/shared/package.json apps/desktop/package.json ui-tui/packages/hermes-ink/package.json
33
watch_file flake.nix flake.lock nix/devShell.nix nix/tui.nix nix/package.nix nix/python.nix nix/hermes-agent.nix nix/desktop.nix
44

apps/desktop/src/app/session/hooks/use-message-stream/gateway-event.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import {
3939
import { clearSessionSubagents, pruneDelegateFallbackSubagents, upsertSubagent } from '@/store/subagents'
4040
import { clearActiveSessionTodos } from '@/store/todos'
4141
import { recordToolDiff } from '@/store/tool-diffs'
42+
import { reportInstallMethodWarning } from '@/store/updates'
4243
import { notifyWorkspaceChanged, toolMayMutateFiles } from '@/store/workspace-events'
4344
import type { RpcEvent } from '@/types/hermes'
4445

@@ -216,6 +217,10 @@ export function useGatewayEventHandler(deps: GatewayEventDeps) {
216217
requestDesktopOnboarding(payload.credential_warning)
217218
}
218219

220+
if (apply) {
221+
reportInstallMethodWarning(payload?.install_warning)
222+
}
223+
219224
void refreshHermesConfig()
220225

221226
if (modelChanged || providerChanged) {

apps/desktop/src/app/session/hooks/use-session-actions/utils.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import {
1919
setSessions,
2020
setYoloActive
2121
} from '@/store/session'
22-
import { reportBackendContract } from '@/store/updates'
22+
import { reportBackendContract, reportInstallMethodWarning } from '@/store/updates'
2323
import type { SessionCreateResponse, SessionInfo, SessionRuntimeInfo } from '@/types/hermes'
2424

2525
import type { ClientSessionState } from '../../../types'
@@ -270,6 +270,8 @@ export function applyRuntimeInfo(info: SessionRuntimeInfo | undefined): SessionR
270270
requestDesktopOnboarding(info.credential_warning)
271271
}
272272

273+
reportInstallMethodWarning(info.install_warning)
274+
273275
if (typeof info.model === 'string') {
274276
setCurrentModel(info.model)
275277
sessionState.model = info.model

apps/desktop/src/i18n/en.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ export const en: Translations = {
119119
backendOutOfDateTitle: 'Backend out of date',
120120
backendOutOfDateMessage:
121121
'Your Hermes backend is older than this desktop build and may not work correctly. Update to align them.',
122+
installMethodUnsupportedTitle: 'Unsupported install method',
122123
updateHermes: 'Update Hermes',
123124
updateReadyTitle: 'Update ready',
124125
updateReadyMessage: count => `${count} new change${count === 1 ? '' : 's'} available.`,

apps/desktop/src/i18n/ja.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ export const ja = defineLocale({
120120
backendOutOfDateTitle: 'バックエンドが古いです',
121121
backendOutOfDateMessage:
122122
'Hermes バックエンドがこのデスクトップビルドより古く、正常に動作しない場合があります。更新して揃えてください。',
123+
installMethodUnsupportedTitle: 'サポート対象外のインストール方法',
123124
updateHermes: 'Hermes を更新',
124125
updateReadyTitle: '更新の準備ができました',
125126
updateReadyMessage: count => `${count} 件の新しい変更が利用可能です。`,

apps/desktop/src/i18n/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ export interface Translations {
161161
copyDetailFailed: string
162162
backendOutOfDateTitle: string
163163
backendOutOfDateMessage: string
164+
installMethodUnsupportedTitle: string
164165
updateHermes: string
165166
updateReadyTitle: string
166167
updateReadyMessage: (count: number) => string

apps/desktop/src/i18n/zh-hant.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ export const zhHant = defineLocale({
116116
copyDetailFailed: '無法複製通知詳情',
117117
backendOutOfDateTitle: '後端版本過舊',
118118
backendOutOfDateMessage: '您的 Hermes 後端早於目前的桌面版本,可能無法正常運作。請更新以保持一致。',
119+
installMethodUnsupportedTitle: '不受支援的安裝方式',
119120
updateHermes: '更新 Hermes',
120121
updateReadyTitle: '有可用更新',
121122
updateReadyMessage: count => `有 ${count} 項新變更可用。`,

apps/desktop/src/i18n/zh.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ export const zh: Translations = {
116116
copyDetailFailed: '无法复制通知详情',
117117
backendOutOfDateTitle: '后端版本过旧',
118118
backendOutOfDateMessage: '你的 Hermes 后端早于当前桌面构建,可能无法正常工作。请更新以保持一致。',
119+
installMethodUnsupportedTitle: '不受支持的安装方式',
119120
updateHermes: '更新 Hermes',
120121
updateReadyTitle: '有可用更新',
121122
updateReadyMessage: count => `有 ${count} 项新更改可用。`,

apps/desktop/src/lib/chat-messages.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ export type GatewayEventPayload = {
5151
cwd?: string
5252
branch?: string
5353
credential_warning?: string
54+
install_warning?: string
5455
personality?: string
5556
usage?: Partial<UsageStats>
5657
// agent.terminal.output — live chunk for a read-only agent terminal tab

apps/desktop/src/store/updates.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,24 @@ function isSkewToastSnoozed(): boolean {
111111
return Number.isFinite(until) && Date.now() < until
112112
}
113113

114+
const INSTALL_METHOD_TOAST_ID = 'install-method-not-supported'
115+
// Same time-based snooze pattern as the update/skew toasts: the warning is
116+
// re-derived from every session.info (session.create/resume/activate all
117+
// route through applyRuntimeInfo), so without a snooze it would re-pop on
118+
// every session switch even right after the user dismissed it.
119+
const INSTALL_METHOD_TOAST_SNOOZE_KEY = 'hermes:install-method-toast-snooze-until'
120+
const INSTALL_METHOD_TOAST_COOLDOWN_MS = 24 * 60 * 60 * 1000
121+
122+
function snoozeInstallMethodToast(): void {
123+
persistString(INSTALL_METHOD_TOAST_SNOOZE_KEY, String(Date.now() + INSTALL_METHOD_TOAST_COOLDOWN_MS))
124+
}
125+
126+
function isInstallMethodToastSnoozed(): boolean {
127+
const until = Number(storedString(INSTALL_METHOD_TOAST_SNOOZE_KEY) || 0)
128+
129+
return Number.isFinite(until) && Date.now() < until
130+
}
131+
114132
/**
115133
* Guard against a desktop GUI talking to a backend that predates its contract
116134
* (e.g. a bb/gui-built app pointed at a `main` checkout). Rather than failing
@@ -151,6 +169,27 @@ export function reportBackendContract(contract: number | undefined): void {
151169
})
152170
}
153171

172+
export function reportInstallMethodWarning(message: string | undefined): void {
173+
if (!message) {
174+
dismissNotification(INSTALL_METHOD_TOAST_ID)
175+
176+
return
177+
}
178+
179+
if (isInstallMethodToastSnoozed()) {
180+
return
181+
}
182+
183+
notify({
184+
durationMs: 0,
185+
id: INSTALL_METHOD_TOAST_ID,
186+
kind: 'warning',
187+
message,
188+
onDismiss: () => snoozeInstallMethodToast(),
189+
title: translateNow('notifications.installMethodUnsupportedTitle')
190+
})
191+
}
192+
154193
/**
155194
* Fire a toast when an update is available, at most once per cooldown window.
156195
* Closing the toast — dismissing it or opening the updates window from it —

0 commit comments

Comments
 (0)