diff --git a/CHANGELOG.md b/CHANGELOG.md index eb4cc429..9bc6d808 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ All notable changes to this project will be documented in this file. - Usage: GitHub Copilot now shows a single AI Credits window, matching Copilot's token-based quota, in place of the old Chat Requests and Completions windows (thanks to @jakoss). - Settings: fixed the Cloudflare Tunnel download link shown when cloudflared is not installed (thanks to @AyoubAchour). - Git: picking a remote branch such as `origin/main` in the branch selector now switches you to that branch instead of leaving the repository on a detached `HEAD` with no branch name. +- Providers: after saving a provider's credentials the settings page now refreshes the credential state (source refetch + optimistic auth mark) instead of keeping the stale "Credentials missing" / empty-models summary until the next restart (thanks to @herjarsa). - Desktop: "Restart to Update" no longer looks dead when the update cannot be installed — the update window now shows the reason, including when the running copy was not installed from an official signed release, and the button stays available to retry. ## [1.21.0] - 2026-08-26 diff --git a/packages/ui/src/components/sections/providers/ProvidersPage.tsx b/packages/ui/src/components/sections/providers/ProvidersPage.tsx index e0336df3..bddc99eb 100644 --- a/packages/ui/src/components/sections/providers/ProvidersPage.tsx +++ b/packages/ui/src/components/sections/providers/ProvidersPage.tsx @@ -20,6 +20,7 @@ import { toast } from '@/components/ui'; import { Icon } from "@/components/icon/Icon"; import type { IconName } from "@/components/icon/icons"; import { reloadOpenCodeConfiguration } from '@/stores/useAgentsStore'; +import type { ConfigChangeScope } from '@/lib/configSync'; import { recordDeferredOpenCodeRestart } from '@/lib/opencode/deferredRestart'; import { cn } from '@/lib/utils'; import { copyTextToClipboard } from '@/lib/clipboard'; @@ -421,6 +422,32 @@ export const ProvidersPage: React.FC = () => { refreshProviderSources(); }, [refreshProviderSources, setSelectedProvider]); + // The mutation above already persisted to disk. If OpenCode is externally + // managed (e.g. the user is running a separate `opencode serve` they have to + // restart themselves), reloadOpenCodeConfiguration throws with + // `requiresManualRestart`. Surface the restart guidance instead of a + // misleading "mutation failed" toast and ensure the deferred-restart + // payload is recorded so the Settings page can show pending-restart + // guidance consistently across providers, API keys, custom providers, + // and disconnects. + const applyConfigReloadOrRecordDeferred = React.useCallback( + async (scope: ConfigChangeScope, idForDeferred?: string) => { + try { + await reloadOpenCodeConfiguration({ scopes: [scope], mode: 'active' }); + return 'reloaded'; + } catch (error) { + const requiresManual = (error as Error & { requiresManualRestart?: boolean })?.requiresManualRestart === true; + if (requiresManual) { + if (idForDeferred) { + recordDeferredOpenCodeRestart(scope, { id: idForDeferred }); + } + return 'manual-restart'; + } + throw error; + } + }, + [], + ); const selectedProvider = providers.find((provider) => provider.id === selectedProviderId); const selectedSources = selectedProviderId ? providerSources[selectedProviderId] : undefined; @@ -445,7 +472,11 @@ export const ProvidersPage: React.FC = () => { toast.success(t('settings.providers.page.toast.apiKeySaved')); setApiKeyInputs((prev) => ({ ...prev, [providerId]: '' })); - await reloadOpenCodeConfiguration({ scopes: ["providers"], mode: "active" }); + // Mutation succeeded: the auth key is on disk. The reload can fail with + // requiresManualRestart when OpenCode is externally managed; the helper + // records the deferred-restart payload instead of throwing a misleading + // "mutation failed" toast. + await applyConfigReloadOrRecordDeferred('providers', providerId); markAuthWriteSucceeded(providerId); } catch (error) { console.error('Failed to save API key:', error);