@@ -527,8 +527,12 @@ export class EditorKeyboardController {
527527 void this . host . session ?. cancel ( ) ;
528528 }
529529
530+ /** Guards Shift-Tab cycling while a setThinking round-trip is still pending. */
531+ private thinkingCycleInFlight = false ;
532+
530533 /** Shift-Tab: cycle the thinking effort to the current model's next level (wraps). */
531534 private async cycleThinkingEffort ( ) : Promise < void > {
535+ if ( this . thinkingCycleInFlight ) return ;
532536 const { host } = this ;
533537 if ( host . state . appState . streamingPhase !== 'idle' || host . state . appState . isCompacting ) {
534538 host . showError ( 'Cannot change thinking effort while streaming — press Esc or Ctrl-C first.' ) ;
@@ -549,32 +553,37 @@ export class EditorKeyboardController {
549553 host . showNotice ( `${ alias } does not offer selectable thinking effort levels.` ) ;
550554 return ;
551555 }
552- const prev = host . state . appState . thinkingEffort ;
553- const currentIndex = levels . indexOf ( prev ) ;
554- // An out-of-list live effort (e.g. a provider-specific value) restarts the
555- // cycle from the off entry when offered, else from the first level.
556- const startIndex = currentIndex !== - 1 ? currentIndex + 1 : Math . max ( 0 , levels . indexOf ( 'off' ) ) ;
557- const next = levels [ startIndex % levels . length ] ?? levels [ 0 ] ! ;
558- if ( host . session !== undefined ) {
559- try {
560- await host . session . setThinking ( next ) ;
561- } catch ( error ) {
562- host . showError ( `Failed to set thinking effort: ${ formatErrorMessage ( error ) } ` ) ;
556+ this . thinkingCycleInFlight = true ;
557+ try {
558+ const prev = host . state . appState . thinkingEffort ;
559+ const currentIndex = levels . indexOf ( prev ) ;
560+ // An out-of-list live effort (e.g. a provider-specific value) restarts the
561+ // cycle from the off entry when offered, else from the first level.
562+ const startIndex = currentIndex !== - 1 ? currentIndex + 1 : Math . max ( 0 , levels . indexOf ( 'off' ) ) ;
563+ const next = levels [ startIndex % levels . length ] ?? levels [ 0 ] ! ;
564+ if ( host . session !== undefined ) {
565+ try {
566+ await host . session . setThinking ( next ) ;
567+ } catch ( error ) {
568+ host . showError ( `Failed to set thinking effort: ${ formatErrorMessage ( error ) } ` ) ;
569+ return ;
570+ }
571+ } else if ( ! host . engineV2 ) {
572+ host . showError ( NO_ACTIVE_SESSION_MESSAGE ) ;
563573 return ;
564574 }
565- } else if ( ! host . engineV2 ) {
566- host . showError ( NO_ACTIVE_SESSION_MESSAGE ) ;
567- return ;
575+ // v2 session-less: carry the choice into the first lazy-created session,
576+ // the same way a session-only Alt+S choice is applied on creation.
577+ const patch : Partial < AppState > = { thinkingEffort : next } ;
578+ if ( host . session === undefined ) patch . lazySessionThinking = next ;
579+ host . setAppState ( patch ) ;
580+ host . track ( 'thinking_toggle' , { enabled : next !== 'off' , effort : next , from : prev } ) ;
581+ // No transcript notice: the footer already shows the new level live, and
582+ // rapid cycling would stack a line per keypress in the chat history.
583+ await this . persistDefaultEffort ( alias , model , next ) ;
584+ } finally {
585+ this . thinkingCycleInFlight = false ;
568586 }
569- // v2 session-less: carry the choice into the first lazy-created session,
570- // the same way a session-only Alt+S choice is applied on creation.
571- const patch : Partial < AppState > = { thinkingEffort : next } ;
572- if ( host . session === undefined ) patch . lazySessionThinking = next ;
573- host . setAppState ( patch ) ;
574- host . track ( 'thinking_toggle' , { enabled : next !== 'off' , effort : next , from : prev } ) ;
575- // No transcript notice: the footer already shows the new level live, and
576- // rapid cycling would stack a line per keypress in the chat history.
577- await this . persistDefaultEffort ( alias , model , next ) ;
578587 }
579588
580589 /** Best-effort persist of the cycled effort as the config default. */
0 commit comments