From 813b35445eeaf07cc2f4b5a009233ab088c153ef Mon Sep 17 00:00:00 2001 From: mrdulasolutions Date: Wed, 13 May 2026 14:15:38 -0400 Subject: [PATCH] fix(settings): forward saved API keys to sidecar in same session MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit User report: "OpenRouter API key isn't saving." Same underlying bug affects the Anthropic key. Architecture (per sidecar/src/lib/secrets.ts): keychain is the canonical store, sidecar caches keys in memory, and the renderer is responsible for writing the keychain AND forwarding the new value to the sidecar via the appropriate RPC. The sidecar comment even spells this out: > "When the user changes a key in Settings, the renderer writes the > keychain first, then forwards the new value to the sidecar via > setSecret(name, value)..." But handleSaveApiKey and handleSaveOpenRouterKey both stopped at step one. The keychain got written; the sidecar's in-memory map never did. Result: the new key only takes effect after restart (which re-bootstraps secrets from the keychain at boot). Within the same session — set key → run agent → 401 / "API key not set." Fix: after writing the keychain, forward to the sidecar. - Anthropic: settings.set({ anthropicApiKey: trimmed }) — already routes through SECRET_KEYS in settings.ts, lands in setSecret() plus clears the cached Anthropic SDK client. - OpenRouter: openrouter.setApiKey / openrouter.clearApiKey — the purpose-built RPC, lands in setSecret() too. Both writes are best-effort follow-ups to the keychain write; the keychain remains the source of truth, so a sidecar transient failure here can still be recovered by an app restart. Doesn't address two adjacent reports also flagged by the user: - "Model changes in General not saving" — General tab's first model picker (line ~1378) only updates local React state; user must click the Save button. That's the existing UX; if the user clicks Save and it still doesn't persist, that's a separate issue I couldn't reproduce from code-read alone. - "Notifications / test notifications not working" — needs runtime diagnostics to know which step fails (macOS permission, bridge.isTauri, sendNotification). Code looks correct on inspection. Co-Authored-By: Claude Opus 4.7 --- src/renderer/components/SettingsPanel.tsx | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/renderer/components/SettingsPanel.tsx b/src/renderer/components/SettingsPanel.tsx index bfa0e1f..e4e21a9 100644 --- a/src/renderer/components/SettingsPanel.tsx +++ b/src/renderer/components/SettingsPanel.tsx @@ -712,6 +712,12 @@ export function SettingsPanel({ onClose, initialTab }: SettingsPanelProps) { } else { await deleteKeychainSecret("anthropicApiKey"); } + // Forward to the sidecar's in-memory secrets store so the next + // Anthropic call this session picks up the new key. Without this, + // the keychain has it but the sidecar's getSecret() returns the + // pre-change value until the app restarts (which re-bootstraps + // secrets from the keychain at boot). + await window.api.settings.set({ anthropicApiKey: trimmed }); queryClient.invalidateQueries({ queryKey: ["general-config"] }); setApiKeySaved(true); setTimeout(() => setApiKeySaved(false), 3000); @@ -748,6 +754,17 @@ export function SettingsPanel({ onClose, initialTab }: SettingsPanelProps) { setOpenRouterError(err instanceof Error ? err.message : "Save failed"); return; } + // Forward to the sidecar's in-memory secrets store. The keychain + // is the canonical store (re-bootstrapped at app boot), but the + // sidecar caches the key in memory for the current session — + // without this call, OpenRouter requests this session see the + // stale value (or null) and fail with "API key not set" until + // the user restarts the app. + if (trimmed) { + await window.api.openrouter.setApiKey(trimmed); + } else { + await window.api.openrouter.clearApiKey(); + } setOpenRouterConfigured(!!trimmed); setOpenRouterSaved(true); setTimeout(() => setOpenRouterSaved(false), 3000);