@@ -33,6 +33,7 @@ import {
3333 setThinkingBudget ,
3434 setTemperature ,
3535 setMaxTokens ,
36+ buildThreadParamsPatch ,
3637} from "../features/Chat/Thread" ;
3738import { saveLastThreadParams } from "../utils/threadStorage" ;
3839import { statisticsApi } from "../services/refact/statistics" ;
@@ -82,7 +83,7 @@ const startListening = listenerMiddleware.startListening.withTypes<
8283
8384startListening ( {
8485 actionCreator : newChatAction ,
85- effect : ( _action , listenerApi ) => {
86+ effect : async ( _action , listenerApi ) => {
8687 const state = listenerApi . getState ( ) ;
8788 const chatId = state . chat . current_thread_id ;
8889
@@ -102,6 +103,36 @@ startListening({
102103 } ) ,
103104 ) ;
104105 listenerApi . dispatch ( clearError ( ) ) ;
106+
107+ // New chats are created client-side first; sync the initial params to backend
108+ // immediately so the first snapshot doesn't overwrite local defaults.
109+ const runtime = state . chat . threads [ chatId ] ;
110+ const port = state . config . lspPort ;
111+ if ( ! runtime || ! port || ! chatId ) return ;
112+
113+ try {
114+ const patch = buildThreadParamsPatch ( runtime . thread , true ) ;
115+
116+ // If reasoning is enabled by defaults (new chat), ensure temperature is sent as null.
117+ // Otherwise backend may fall back to a numeric default (often 0), which is invalid
118+ // for reasoning-enabled providers.
119+ const isReasoningEnabled =
120+ Boolean ( runtime . thread . boost_reasoning ) ||
121+ runtime . thread . reasoning_effort != null ||
122+ runtime . thread . thinking_budget != null ;
123+ if ( isReasoningEnabled ) {
124+ patch . temperature = null ;
125+ }
126+
127+ if ( Object . keys ( patch ) . length > 0 ) {
128+ await sendChatCommand ( chatId , port , state . config . apiKey ?? undefined , {
129+ type : "set_params" ,
130+ patch,
131+ } ) ;
132+ }
133+ } catch {
134+ // Silently ignore - backend may not support this command
135+ }
105136 } ,
106137} ) ;
107138
@@ -707,6 +738,15 @@ startListening({
707738 type : "set_params" ,
708739 patch : { boost_reasoning : action . payload . value } ,
709740 } ) ;
741+
742+ // When reasoning is enabled, temperature must be unset.
743+ // This avoids provider-side validation errors.
744+ if ( action . payload . value ) {
745+ await sendChatCommand ( chatId , port , apiKey ?? undefined , {
746+ type : "set_params" ,
747+ patch : { temperature : null } ,
748+ } ) ;
749+ }
710750 } catch {
711751 // Silently ignore - backend may not support this command
712752 }
@@ -731,6 +771,14 @@ startListening({
731771 type : "set_params" ,
732772 patch : { reasoning_effort : action . payload . value } ,
733773 } ) ;
774+
775+ // Any explicit reasoning effort implies reasoning mode: unset temperature.
776+ if ( action . payload . value != null ) {
777+ await sendChatCommand ( chatId , port , apiKey ?? undefined , {
778+ type : "set_params" ,
779+ patch : { temperature : null } ,
780+ } ) ;
781+ }
734782 } catch {
735783 // Silently ignore
736784 }
@@ -755,6 +803,14 @@ startListening({
755803 type : "set_params" ,
756804 patch : { thinking_budget : action . payload . value } ,
757805 } ) ;
806+
807+ // Any explicit thinking budget implies reasoning mode: unset temperature.
808+ if ( action . payload . value != null ) {
809+ await sendChatCommand ( chatId , port , apiKey ?? undefined , {
810+ type : "set_params" ,
811+ patch : { temperature : null } ,
812+ } ) ;
813+ }
758814 } catch {
759815 // Silently ignore errors - user will see them via SSE events
760816 }
@@ -1028,23 +1084,52 @@ startListening({
10281084 if ( ! runtime ) return ;
10291085
10301086 const isUnstartedChat = runtime . thread . messages . length === 0 ;
1031- if ( ! isUnstartedChat ) return ;
1032-
1033- saveLastThreadParams ( {
1034- model : runtime . thread . model ,
1035- mode : runtime . thread . mode ,
1036- boost_reasoning : runtime . thread . boost_reasoning ,
1037- reasoning_effort : runtime . thread . reasoning_effort ,
1038- thinking_budget : runtime . thread . thinking_budget ,
1039- temperature : runtime . thread . temperature ,
1040- max_tokens : runtime . thread . max_tokens ,
1041- increase_max_tokens : runtime . thread . increase_max_tokens ,
1042- include_project_info : runtime . thread . include_project_info ,
1043- context_tokens_cap : runtime . thread . context_tokens_cap ,
1044- system_prompt : state . chat . system_prompt ,
1045- checkpoints_enabled : state . chat . checkpoints_enabled ,
1046- follow_ups_enabled : state . chat . follow_ups_enabled ,
1047- } ) ;
1087+ const shouldPersistForNewChats =
1088+ isUnstartedChat ||
1089+ setBoostReasoning . match ( _action ) ||
1090+ setReasoningEffort . match ( _action ) ||
1091+ setThinkingBudget . match ( _action ) ;
1092+ if ( ! shouldPersistForNewChats ) return ;
1093+
1094+ // Persist the updated param(s) as defaults for *new* chats.
1095+ // IMPORTANT: For started chats, we only persist reasoning-related toggles
1096+ // (boost_reasoning / reasoning_effort / thinking_budget), keeping other
1097+ // sampling params “sticky” only before the first message.
1098+ const mode = runtime . thread . mode ;
1099+ const patch : Parameters < typeof saveLastThreadParams > [ 0 ] = { mode } ;
1100+
1101+ if ( isUnstartedChat ) {
1102+ patch . model = runtime . thread . model ;
1103+ patch . temperature = runtime . thread . temperature ;
1104+ patch . max_tokens = runtime . thread . max_tokens ;
1105+ patch . increase_max_tokens = runtime . thread . increase_max_tokens ;
1106+ patch . include_project_info = runtime . thread . include_project_info ;
1107+ patch . context_tokens_cap = runtime . thread . context_tokens_cap ;
1108+ patch . system_prompt = state . chat . system_prompt ;
1109+ patch . checkpoints_enabled = state . chat . checkpoints_enabled ;
1110+ patch . follow_ups_enabled = state . chat . follow_ups_enabled ;
1111+ }
1112+
1113+ if ( setBoostReasoning . match ( _action ) ) {
1114+ patch . boost_reasoning = runtime . thread . boost_reasoning ;
1115+ // preserve temperature reset as part of “reasoning defaults”
1116+ patch . temperature = runtime . thread . temperature ;
1117+ }
1118+ if ( setReasoningEffort . match ( _action ) ) {
1119+ patch . reasoning_effort = runtime . thread . reasoning_effort ;
1120+ patch . temperature = runtime . thread . temperature ;
1121+ }
1122+ if ( setThinkingBudget . match ( _action ) ) {
1123+ patch . thinking_budget = runtime . thread . thinking_budget ;
1124+ patch . temperature = runtime . thread . temperature ;
1125+ }
1126+
1127+ // Still persist model changes after start (matches current UX).
1128+ if ( setChatModel . match ( _action ) ) {
1129+ patch . model = runtime . thread . model ;
1130+ }
1131+
1132+ saveLastThreadParams ( patch ) ;
10481133 } ,
10491134} ) ;
10501135
0 commit comments