@@ -42,6 +42,11 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
4242import { useDebouncedValue } from "@tanstack/react-pacer" ;
4343import { useNavigate } from "@tanstack/react-router" ;
4444import { gitBranchesQueryOptions , gitCreateWorktreeMutationOptions } from "~/lib/gitReactQuery" ;
45+ import {
46+ getSelectableThreadProviders ,
47+ getThreadProviderLabel ,
48+ resolveThreadProviderSelection ,
49+ } from "~/lib/providerAvailability" ;
4550import { projectSearchEntriesQueryOptions } from "~/lib/projectReactQuery" ;
4651import {
4752 skillCatalogQueryOptions ,
@@ -194,7 +199,7 @@ import { useDiffViewerStore } from "~/diffViewerStore";
194199import { PreviewPanel } from "./PreviewPanel" ;
195200import { ContextWindowMeter } from "./chat/ContextWindowMeter" ;
196201import { buildExpandedImagePreview , ExpandedImagePreview } from "./chat/ExpandedImagePreview" ;
197- import { AVAILABLE_PROVIDER_OPTIONS , ProviderModelPicker } from "./chat/ProviderModelPicker" ;
202+ import { ProviderModelPicker } from "./chat/ProviderModelPicker" ;
198203import { ComposerCommandItem , ComposerCommandMenu } from "./chat/ComposerCommandMenu" ;
199204import { ComposerPendingApprovalActions } from "./chat/ComposerPendingApprovalActions" ;
200205import { CompactComposerControlsMenu } from "./chat/CompactComposerControlsMenu" ;
@@ -856,8 +861,18 @@ export default function ChatView({ threadId, onMinimize }: ChatViewProps) {
856861 markThreadVisited ,
857862 ] ) ;
858863
864+ const serverConfigQuery = useQuery ( serverConfigQueryOptions ( ) ) ;
859865 const sessionProvider = activeThread ?. session ?. provider ?? null ;
860866 const selectedProviderByThreadId = composerDraft . provider ;
867+ const providerStatuses = serverConfigQuery . data ?. providers ?? EMPTY_PROVIDER_STATUSES ;
868+ const selectableProviders = useMemo (
869+ ( ) =>
870+ getSelectableThreadProviders ( {
871+ statuses : providerStatuses ,
872+ openclawGatewayUrl : settings . openclawGatewayUrl ,
873+ } ) ,
874+ [ providerStatuses , settings . openclawGatewayUrl ] ,
875+ ) ;
861876 const hasThreadStarted = Boolean (
862877 activeThread &&
863878 ( activeThread . latestTurn !== null ||
@@ -867,7 +882,12 @@ export default function ChatView({ threadId, onMinimize }: ChatViewProps) {
867882 const lockedProvider : ProviderKind | null = hasThreadStarted
868883 ? ( sessionProvider ?? selectedProviderByThreadId ?? null )
869884 : null ;
870- const selectedProvider : ProviderKind = lockedProvider ?? selectedProviderByThreadId ?? "codex" ;
885+ const selectedProvider : ProviderKind =
886+ lockedProvider ??
887+ resolveThreadProviderSelection ( {
888+ preferredProvider : selectedProviderByThreadId ,
889+ selectableProviders,
890+ } ) ;
871891 const baseThreadModel = resolveModelSlugForProvider (
872892 selectedProvider ,
873893 activeThread ?. model ?? activeProject ?. model ?? getDefaultModel ( selectedProvider ) ,
@@ -907,20 +927,18 @@ export default function ChatView({ threadId, onMinimize }: ChatViewProps) {
907927 } , [ modelOptionsByProvider , selectedModelForPicker , selectedProvider ] ) ;
908928 const searchableModelOptions = useMemo (
909929 ( ) =>
910- AVAILABLE_PROVIDER_OPTIONS . filter (
911- ( option ) => lockedProvider === null || option . value === lockedProvider ,
912- ) . flatMap ( ( option ) =>
913- modelOptionsByProvider [ option . value ] . map ( ( { slug, name } ) => ( {
914- provider : option . value ,
915- providerLabel : option . label ,
930+ ( lockedProvider !== null ? [ lockedProvider ] : selectableProviders ) . flatMap ( ( provider ) =>
931+ modelOptionsByProvider [ provider ] . map ( ( { slug, name } ) => ( {
932+ provider,
933+ providerLabel : getThreadProviderLabel ( provider ) ,
916934 slug,
917935 name,
918936 searchSlug : slug . toLowerCase ( ) ,
919937 searchName : name . toLowerCase ( ) ,
920- searchProvider : option . label . toLowerCase ( ) ,
938+ searchProvider : getThreadProviderLabel ( provider ) . toLowerCase ( ) ,
921939 } ) ) ,
922940 ) ,
923- [ lockedProvider , modelOptionsByProvider ] ,
941+ [ lockedProvider , modelOptionsByProvider , selectableProviders ] ,
924942 ) ;
925943 const phase = derivePhase ( activeThread ?. session ?? null ) ;
926944 const isSendBusy = sendPhase !== "idle" ;
@@ -1313,7 +1331,6 @@ export default function ChatView({ threadId, onMinimize }: ChatViewProps) {
13131331 ) ;
13141332 const effectivePathQuery = pathTriggerQuery . length > 0 ? debouncedPathQuery : "" ;
13151333 const branchesQuery = useQuery ( gitBranchesQueryOptions ( gitCwd ) ) ;
1316- const serverConfigQuery = useQuery ( serverConfigQueryOptions ( ) ) ;
13171334 const workspaceEntriesQuery = useQuery (
13181335 projectSearchEntriesQueryOptions ( {
13191336 cwd : gitCwd ,
@@ -1535,7 +1552,6 @@ export default function ChatView({ threadId, onMinimize }: ChatViewProps) {
15351552 } ;
15361553 } , [ ] ) ;
15371554 const keybindings = serverConfigQuery . data ?. keybindings ?? EMPTY_KEYBINDINGS ;
1538- const providerStatuses = serverConfigQuery . data ?. providers ?? EMPTY_PROVIDER_STATUSES ;
15391555 const activeProviderStatus = useMemo (
15401556 ( ) => providerStatuses . find ( ( status ) => status . provider === selectedProvider ) ?? null ,
15411557 [ selectedProvider , providerStatuses ] ,
@@ -5319,6 +5335,7 @@ export default function ChatView({ threadId, onMinimize }: ChatViewProps) {
53195335 provider = { selectedProvider }
53205336 model = { selectedModelForPickerWithCustomFallback }
53215337 lockedProvider = { lockedProvider }
5338+ availableProviders = { selectableProviders }
53225339 modelOptionsByProvider = { modelOptionsByProvider }
53235340 { ...( composerProviderState . modelPickerIconClassName
53245341 ? {
0 commit comments