From e31b74073beacb4204ff97c7a907aa88504eb44d Mon Sep 17 00:00:00 2001 From: Brooklyn Nicholson Date: Thu, 7 May 2026 23:02:34 -0400 Subject: [PATCH] fix(desktop): route gateway provider errors to onboarding The "No inference provider configured" auth error reaches the renderer through gateway error events, not the prompt.submit promise; the previous patch only caught the latter, so the error toast still surfaced and onboarding never opened. Also strip credential-shaped env vars from the test:desktop:fresh sandbox so the packaged backend can't see provider keys leaking from the launching shell. --- apps/desktop/scripts/test-desktop.mjs | 52 ++++++++++++++++--- .../app/session/hooks/use-message-stream.ts | 12 ++++- 2 files changed, 55 insertions(+), 9 deletions(-) diff --git a/apps/desktop/scripts/test-desktop.mjs b/apps/desktop/scripts/test-desktop.mjs index 7436e91d867..a6154f1fb3b 100644 --- a/apps/desktop/scripts/test-desktop.mjs +++ b/apps/desktop/scripts/test-desktop.mjs @@ -110,6 +110,37 @@ function openDmg() { run('open', [dmgPath]) } +const CREDENTIAL_ENV_SUFFIXES = [ + '_API_KEY', + '_TOKEN', + '_SECRET', + '_PASSWORD', + '_CREDENTIALS', + '_ACCESS_KEY', + '_PRIVATE_KEY', + '_OAUTH_TOKEN' +] + +const CREDENTIAL_ENV_NAMES = new Set([ + 'ANTHROPIC_BASE_URL', + 'ANTHROPIC_TOKEN', + 'AWS_ACCESS_KEY_ID', + 'AWS_SECRET_ACCESS_KEY', + 'AWS_SESSION_TOKEN', + 'CUSTOM_API_KEY', + 'GEMINI_BASE_URL', + 'OPENAI_BASE_URL', + 'OPENROUTER_BASE_URL', + 'OLLAMA_BASE_URL', + 'GROQ_BASE_URL', + 'XAI_BASE_URL' +]) + +function isCredentialEnvVar(name) { + if (CREDENTIAL_ENV_NAMES.has(name)) return true + return CREDENTIAL_ENV_SUFFIXES.some(suffix => name.endsWith(suffix)) +} + function launchFresh() { if (!exists(APP_BIN)) { die(`Missing app executable: ${APP_BIN}`) @@ -129,14 +160,21 @@ function launchFresh() { fs.mkdirSync(hermesHome, { recursive: true }) fs.mkdirSync(cwd, { recursive: true }) - const env = { - ...process.env, - HERMES_DESKTOP_CWD: cwd, - HERMES_DESKTOP_IGNORE_EXISTING: '1', - HERMES_DESKTOP_TEST_MODE: 'fresh-install', - HERMES_DESKTOP_USER_DATA_DIR: userDataDir, - HERMES_HOME: hermesHome + // Strip every credential-shaped env var so the sandbox is actually fresh. + // Without this, shell-set OPENAI_API_KEY/OPENAI_BASE_URL/etc. leak into the + // packaged backend, making setup.status report "configured" while the + // agent's own credential resolution still fails. + const env = {} + for (const [key, value] of Object.entries(process.env)) { + if (isCredentialEnvVar(key)) continue + env[key] = value } + + env.HERMES_DESKTOP_CWD = cwd + env.HERMES_DESKTOP_IGNORE_EXISTING = '1' + env.HERMES_DESKTOP_TEST_MODE = 'fresh-install' + env.HERMES_DESKTOP_USER_DATA_DIR = userDataDir + env.HERMES_HOME = hermesHome delete env.HERMES_DESKTOP_HERMES delete env.HERMES_DESKTOP_HERMES_ROOT diff --git a/apps/desktop/src/app/session/hooks/use-message-stream.ts b/apps/desktop/src/app/session/hooks/use-message-stream.ts index 529da6769d2..5f0e52ee624 100644 --- a/apps/desktop/src/app/session/hooks/use-message-stream.ts +++ b/apps/desktop/src/app/session/hooks/use-message-stream.ts @@ -35,6 +35,9 @@ import type { RpcEvent } from '@/types/hermes' import type { ClientSessionState } from '../../types' +const PROVIDER_SETUP_ERROR_RE = + /No inference provider configured|no_provider_configured|OPENROUTER_API_KEY|OPENAI_API_KEY|ANTHROPIC_API_KEY|set an API key/i + interface MessageStreamOptions { activeSessionIdRef: MutableRefObject hydrateFromStoredSession: ( @@ -585,11 +588,16 @@ export function useMessageStream({ }) } } else if (event.type === 'error') { - if (isActiveEvent) { + const errorMessage = payload?.message || 'Hermes reported an error' + const looksLikeProviderSetup = PROVIDER_SETUP_ERROR_RE.test(errorMessage) + + if (looksLikeProviderSetup) { + requestDesktopOnboarding(errorMessage) + } else if (isActiveEvent) { notify({ kind: 'error', title: 'Hermes error', - message: payload?.message || 'Hermes reported an error' + message: errorMessage }) }