Description
After completing initial setup (bootstrap + onboarding provider config), closing and re-opening the Hermes Desktop app shows the onboarding/install prompt again, as if the user never completed configuration.
Steps to Reproduce
- Install Hermes Desktop and complete first-launch bootstrap (
install.sh) + onboarding (provider/model setup)
- Confirm the main chat UI loads correctly
- Close the app (Cmd+Q or window close)
- Re-open Hermes Desktop
- Actual: The onboarding overlay ("Add a provider credential before sending your first message") or install overlay reappears instead of the main chat UI
- Closing and re-opening again repeats the cycle indefinitely
Expected Behavior
After successful first-time setup, subsequent launches should go straight to the main chat UI without re-prompting for provider/install configuration.
Root Cause (traced)
There are two overlays that gate the main UI, and both have persistence issues:
1. DesktopOnboardingOverlay (primary issue)
The overlay uses localStorage['hermes-desktop-onboarded-v1'] as an initial optimistic cache, but re-evaluates every time the gateway opens via evaluateRuntimeReadiness(), which calls:
setup.status — returns provider_configured: boolean
setup.runtime_check — returns ok: boolean
When the backend reports provider_configured: false (or the runtime check fails), the store resets configured → false and shows the overlay again, overwriting the localStorage cache.
So even though localStorage reads '1', the store re-evaluates and flips it to false on every boot.
Relevant code paths:
src/store/onboarding.ts → refreshOnboarding() line 385-407: calls checkRuntime(), and if !runtime.ready, sets configured: false + writeCachedConfigured(false)
src/components/desktop-onboarding-overlay.tsx line 153: if (onboarding.configured === true && !onboarding.manual) return null — so returning users get the overlay
src/lib/runtime-readiness.ts line 82-138: interpretRuntimeReadiness() — determines readiness from gateway responses
2. DesktopInstallOverlay (edge case)
The bootstrap-complete marker (~/.hermes/hermes-agent/.hermes-bootstrap-complete) is checked correctly in main.cjs via isBootstrapComplete() and should suppress re-install. However, if the venv is missing, the marker alone won't suffice — the check also requires fileExists(getVenvPython(VENV_ROOT)).
Environment
- macOS 26.5 (M1 Pro)
- Hermes Desktop v0.15.1
- Hermes Agent head (self-built from source)
Additional Context
Full trace available at electron/main.cjs → resolveHermesBackend(), isBootstrapComplete(), and src/store/onboarding.ts → refreshOnboarding().
The core issue is that localStorage is an unreliable persistence mechanism for onboarding state — any gateway response discrepancy resets it. A durable marker on disk (similar to the bootstrap-complete marker) or a dedicated setup.onboarded config flag would fix this.
Description
After completing initial setup (bootstrap + onboarding provider config), closing and re-opening the Hermes Desktop app shows the onboarding/install prompt again, as if the user never completed configuration.
Steps to Reproduce
install.sh) + onboarding (provider/model setup)Expected Behavior
After successful first-time setup, subsequent launches should go straight to the main chat UI without re-prompting for provider/install configuration.
Root Cause (traced)
There are two overlays that gate the main UI, and both have persistence issues:
1.
DesktopOnboardingOverlay(primary issue)The overlay uses
localStorage['hermes-desktop-onboarded-v1']as an initial optimistic cache, but re-evaluates every time the gateway opens viaevaluateRuntimeReadiness(), which calls:setup.status— returnsprovider_configured: booleansetup.runtime_check— returnsok: booleanWhen the backend reports
provider_configured: false(or the runtime check fails), the store resetsconfigured → falseand shows the overlay again, overwriting the localStorage cache.So even though
localStoragereads'1', the store re-evaluates and flips it tofalseon every boot.Relevant code paths:
src/store/onboarding.ts→refreshOnboarding()line 385-407: callscheckRuntime(), and if!runtime.ready, setsconfigured: false+writeCachedConfigured(false)src/components/desktop-onboarding-overlay.tsxline 153:if (onboarding.configured === true && !onboarding.manual) return null— so returning users get the overlaysrc/lib/runtime-readiness.tsline 82-138:interpretRuntimeReadiness()— determines readiness from gateway responses2.
DesktopInstallOverlay(edge case)The bootstrap-complete marker (
~/.hermes/hermes-agent/.hermes-bootstrap-complete) is checked correctly inmain.cjsviaisBootstrapComplete()and should suppress re-install. However, if the venv is missing, the marker alone won't suffice — the check also requiresfileExists(getVenvPython(VENV_ROOT)).Environment
Additional Context
Full trace available at
electron/main.cjs→resolveHermesBackend(),isBootstrapComplete(), andsrc/store/onboarding.ts→refreshOnboarding().The core issue is that
localStorageis an unreliable persistence mechanism for onboarding state — any gateway response discrepancy resets it. A durable marker on disk (similar to the bootstrap-complete marker) or a dedicatedsetup.onboardedconfig flag would fix this.