Compare commits

...

3 Commits

Author SHA1 Message Date
Felarof
f69b698834 fix: repair stale defaultProviderId in storage on load
When the stored default provider ID doesn't match any loaded provider,
write back the corrected ID (providers[0].id) to storage so it doesn't
silently persist across sessions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 09:03:25 -07:00
Felarof
1770db2433 feat: fallback to first provider when default provider ID is stale
When defaultProviderId in storage doesn't match any loaded provider
(e.g. after Kimi/Moonshot rollout), selectedProvider was null causing
provider: undefined in chat requests. Now falls back to providers[0].

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 08:52:10 -07:00
Felarof
91ba874a07 fix: fallback to default BrowserOS provider when provider is null
When the extension first loads, provider config is loaded async from
storage. If a chat request fires before loading completes (race
condition), provider is null and the server receives provider: undefined,
causing a Zod validation error. This adds a fallback to
createDefaultBrowserOSProvider() in both chat paths (sidepanel and
scheduled tasks) so provider.type is always defined.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 08:28:41 -07:00
3 changed files with 18 additions and 3 deletions

View File

@@ -23,6 +23,7 @@ import {
import { formatConversationHistory } from '@/lib/conversations/formatConversationHistory'
import { declinedAppsStorage } from '@/lib/declined-apps/storage'
import { useGraphqlQuery } from '@/lib/graphql/useGraphqlQuery'
import { createDefaultBrowserOSProvider } from '@/lib/llm-providers/storage'
import { useLlmProviders } from '@/lib/llm-providers/useLlmProviders'
import { track } from '@/lib/metrics/track'
import { searchActionsStorage } from '@/lib/search-actions/searchActionsStorage'
@@ -208,7 +209,7 @@ export const useChatSession = (options?: ChatSessionOptions) => {
})
const activeTab = activeTabsList?.[0] ?? undefined
const message = getLastMessageText(messages)
const provider = selectedLlmProviderRef.current
const provider = selectedLlmProviderRef.current ?? createDefaultBrowserOSProvider()
const currentMode = modeRef.current
const enabledMcpServers = enabledMcpServersRef.current
const customMcpServers = enabledCustomServersRef.current

View File

@@ -60,6 +60,15 @@ export function useLlmProviders(): UseLlmProvidersReturn {
await defaultProviderIdStorage.setValue(loadedDefaultId)
}
// Repair stale default ID that doesn't match any provider
const defaultExists = loadedProviders.some(
(p) => p.id === loadedDefaultId,
)
if (!defaultExists && loadedProviders.length > 0) {
loadedDefaultId = loadedProviders[0].id
await defaultProviderIdStorage.setValue(loadedDefaultId)
}
setProviders(loadedProviders)
setDefaultProviderId(loadedDefaultId)
} catch {
@@ -146,8 +155,12 @@ export function useLlmProviders(): UseLlmProvidersReturn {
await providersStorage.setValue(updatedProviders)
}
// Fall back to first provider if defaultProviderId is stale/invalid
const selectedProvider = useMemo(
() => providers.find((p) => p.id === defaultProviderId) ?? null,
() =>
providers.find((p) => p.id === defaultProviderId) ??
providers[0] ??
null,
[providers, defaultProviderId],
)

View File

@@ -2,6 +2,7 @@ import { createParser, type EventSourceMessage } from 'eventsource-parser'
import type { ChatMode } from '@/entrypoints/sidepanel/index/chatTypes'
import { getAgentServerUrl } from '@/lib/browseros/helpers'
import {
createDefaultBrowserOSProvider,
defaultProviderIdStorage,
providersStorage,
} from '@/lib/llm-providers/storage'
@@ -78,7 +79,7 @@ export async function getChatServerResponse(
request: ChatServerRequest,
): Promise<ChatServerResponse> {
const agentServerUrl = await getAgentServerUrl()
const provider = await getDefaultProvider()
const provider = (await getDefaultProvider()) ?? createDefaultBrowserOSProvider()
const conversationId = request.conversationId ?? crypto.randomUUID()
const personalization = await personalizationStorage.getValue()