@@ -45,7 +45,7 @@ import {
4545 normalizeThinkLevel ,
4646 resolveThinkingDefaultForModel ,
4747} from "../thinking.ts" ;
48- import type { FastMode , GatewayThinkingLevelOption , SessionsListResult } from "../types.ts" ;
48+ import type { FastMode , GatewayThinkingLevelOption , SessionsListResult , SessionsPatchResult } from "../types.ts" ;
4949
5050type ChatSessionSwitchHandler = ( state : AppViewState , nextSessionKey : string ) => void ;
5151type ChatSessionSelectSurface = "desktop" | "mobile" | "sidebar" ;
@@ -1391,36 +1391,34 @@ async function switchChatModel(state: AppViewState, nextModel: string): Promise<
13911391 } ;
13921392 const switchPromise : Promise < boolean > = ( async ( ) => {
13931393 try {
1394- await client . request ( "sessions.patch" , {
1394+ const patched = await client . request < SessionsPatchResult > ( "sessions.patch" , {
13951395 key : targetSessionKey ,
13961396 ...scopedAgentParamsForSession ( state , targetSessionKey ) ,
13971397 model : nextModel || null ,
13981398 } ) ;
1399- // Refresh the sessions list first so the model-fallback reconciliation
1400- // below reads the authoritative server-confirmed model from the session
1401- // row. The server can silently fall back to a different provider (e.g.
1402- // when the requested provider is unreachable), and the refreshed row
1403- // carries the final effective model after all fallback lifecycle hooks
1404- // have run — matching the contract already used by the /model command.
14051399 await refreshSessionOptions ( state ) ;
1406- // Reconcile the cached override for explicit (non-empty) selections.
1407- // When the user picked Default the cache was already set to null above
1408- // and must stay null so the selector keeps following the current
1409- // default model.
1400+ // Reconcile the cached override when the user made an explicit (non-empty)
1401+ // model selection. `patched.resolved` from the RPC carries the request-time
1402+ // resolution even before the sessions list is populated. When the sessions
1403+ // list already has a row (existing session), prefer the refreshed row
1404+ // because it carries the final effective model after server-side fallback
1405+ // lifecycle hooks. When the row is absent (new session, test scenarios),
1406+ // fall back to `patched.resolved` so the dropdown still shows the resolved
1407+ // model rather than getting blanked out.
14101408 if ( nextModel ) {
14111409 const row = state . sessionsResult ?. sessions ?. find (
14121410 ( s ) => s . key === targetSessionKey ,
14131411 ) ;
1412+ const resolvedModel =
1413+ row ?. model ?? patched . resolved ?. model ?? nextModel ;
1414+ const resolvedProvider =
1415+ row ?. modelProvider ?? patched . resolved ?. modelProvider ?? "" ;
14141416 const resolvedValue = resolvePreferredServerChatModelValue (
1415- row ?. model ,
1416- row ?. modelProvider ,
1417+ resolvedModel ,
1418+ resolvedProvider ,
14171419 state . chatModelCatalog ?? [ ] ,
14181420 ) ;
14191421 const resolvedOverride = createChatModelOverride ( resolvedValue ) ;
1420- // Only update when the resolved override differs. This avoids
1421- // unnecessary re-renders and preserves the cached override kind
1422- // (raw vs qualified) when the server confirms the same model the
1423- // user requested — no provider fallback occurred.
14241422 if (
14251423 JSON . stringify ( resolvedOverride ) !==
14261424 JSON . stringify ( state . chatModelOverrides [ targetSessionKey ] )
0 commit comments