@@ -43,6 +43,7 @@ const TOOL_NAME_CACHE = new Map();
4343const TOOL_PREFIX_REGEX = / ^ (?: o c _ | m c p _ ) / i;
4444
4545let cachedMetadataUserIdPromise ;
46+ let tokenRefreshPromise = null ;
4647
4748// ============================================================================
4849// Debug Logging
@@ -283,7 +284,7 @@ function replaceToolNamesInText(text) {
283284 let output = text . replace ( / " n a m e " \s * : \s * " (?: o c _ | m c p _ ) ( [ ^ " ] + ) " / g, '"name": "$1"' ) ;
284285
285286 output = output . replace (
286- / " n a m e " \s * : \s * " ( B a s h | R e a d | E d i t | W r i t e | T a s k | G l o b | G r e p | W e b F e t c h | W e b S e a r c h | T o d o W r i t e ) " / g,
287+ / " n a m e " \s * : \s * " ( B a s h | R e a d | E d i t | W r i t e | T a s k | G l o b | G r e p | W e b F e t c h | W e b S e a r c h | T o d o W r i t e | A s k U s e r Q u e s t i o n ) " / g,
287288 ( _ , name ) => `"name": "${ normalizeToolNameForOpenCode ( name ) } "` ,
288289 ) ;
289290
@@ -382,6 +383,11 @@ function createTransformedResponse(response) {
382383 async pull ( controller ) {
383384 const { done, value } = await reader . read ( ) ;
384385 if ( done ) {
386+ // Flush any remaining bytes from the decoder
387+ const flushed = decoder . decode ( new Uint8Array ( ) , { stream : false } ) ;
388+ if ( flushed ) {
389+ buffer += flushed ;
390+ }
385391 // Process any remaining buffered content
386392 if ( buffer . length > 0 ) {
387393 controller . enqueue ( encoder . encode ( replaceToolNamesInText ( buffer ) ) ) ;
@@ -642,7 +648,7 @@ async function authorize(mode) {
642648 url . searchParams . set ( "client_id" , CLIENT_ID ) ;
643649 url . searchParams . set ( "response_type" , "code" ) ;
644650 url . searchParams . set ( "redirect_uri" , "https://console.anthropic.com/oauth/code/callback" ) ;
645- url . searchParams . set ( "scope" , "org:create_api_key user:profile user:inference" ) ;
651+ url . searchParams . set ( "scope" , "org:create_api_key user:profile user:inference user:sessions:claude_code " ) ;
646652 url . searchParams . set ( "code_challenge" , pkce . challenge ) ;
647653 url . searchParams . set ( "code_challenge_method" , "S256" ) ;
648654 url . searchParams . set ( "state" , pkce . verifier ) ;
@@ -713,17 +719,30 @@ export async function AnthropicAuthPlugin({ client }) {
713719 const baseFetch = getBaseFetch ( ) ;
714720
715721 if ( ! auth . access || auth . expires < Date . now ( ) ) {
716- const json = await refreshOAuthToken ( auth , baseFetch ) ;
717- await client . auth . set ( {
718- path : { id : "anthropic" } ,
719- body : {
720- type : "oauth" ,
721- refresh : json . refresh_token ,
722- access : json . access_token ,
723- expires : Date . now ( ) + json . expires_in * 1000 ,
724- } ,
725- } ) ;
726- auth . access = json . access_token ;
722+ // Prevent race condition: cache the refresh promise
723+ if ( ! tokenRefreshPromise ) {
724+ tokenRefreshPromise = ( async ( ) => {
725+ try {
726+ const json = await refreshOAuthToken ( auth , baseFetch ) ;
727+ const newExpires = Date . now ( ) + json . expires_in * 1000 ;
728+ await client . auth . set ( {
729+ path : { id : "anthropic" } ,
730+ body : {
731+ type : "oauth" ,
732+ refresh : json . refresh_token ,
733+ access : json . access_token ,
734+ expires : newExpires ,
735+ } ,
736+ } ) ;
737+ auth . access = json . access_token ;
738+ auth . expires = newExpires ;
739+ return json ;
740+ } finally {
741+ tokenRefreshPromise = null ;
742+ }
743+ } ) ( ) ;
744+ }
745+ await tokenRefreshPromise ;
727746 }
728747
729748 return handleAnthropicRequest ( input , init , auth , baseFetch ) ;
0 commit comments