@@ -58,6 +58,7 @@ public sealed partial class CopilotSession : IAsyncDisposable
5858{
5959 private readonly Dictionary < string , AIFunction > _toolHandlers = [ ] ;
6060 private readonly Dictionary < string , Func < CommandContext , Task > > _commandHandlers = [ ] ;
61+ private readonly Dictionary < string , Func < ProviderTokenArgs , Task < string > > > _bearerTokenProviders = new ( StringComparer . Ordinal ) ;
6162 private readonly ILogger _logger ;
6263 private readonly CopilotClient _parentClient ;
6364
@@ -76,9 +77,7 @@ private sealed record EventSubscription(Type EventType, Action<SessionEvent> Han
7677 private Dictionary < string , Func < string , Task < string > > > ? _transformCallbacks ;
7778 private readonly SemaphoreSlim _transformCallbacksLock = new ( 1 , 1 ) ;
7879
79- #pragma warning disable GHCP001
8080 private IReadOnlyList < OpenCanvasInstance > _openCanvases = Array . Empty < OpenCanvasInstance > ( ) ;
81- #pragma warning restore GHCP001
8281
8382 private int _isDisposed ;
8483
@@ -126,7 +125,6 @@ public SessionCapabilities Capabilities
126125 private set ;
127126 }
128127
129- #pragma warning disable GHCP001
130128 /// <summary>
131129 /// Canvas instances currently known to be open for this session.
132130 /// </summary>
@@ -136,7 +134,6 @@ public SessionCapabilities Capabilities
136134 /// </remarks>
137135 [ Experimental ( Diagnostics . Experimental ) ]
138136 public IReadOnlyList < OpenCanvasInstance > OpenCanvases => _openCanvases ;
139- #pragma warning restore GHCP001
140137
141138 /// <summary>
142139 /// Gets the UI API for eliciting information from the user during this session.
@@ -873,6 +870,51 @@ internal void RegisterAutoModeSwitchHandler(Func<AutoModeSwitchRequest, AutoMode
873870 _autoModeSwitchHandler = handler ;
874871 }
875872
873+ /// <summary>
874+ /// Registers per-provider <c>BearerTokenProvider</c> callbacks for BYOK
875+ /// providers configured with managed-identity / on-demand bearer-token auth.
876+ /// </summary>
877+ /// <remarks>
878+ /// The runtime never receives the callback itself; the SDK strips it from the
879+ /// provider config and instead sends <c>hasBearerTokenProvider: true</c>. When
880+ /// the runtime needs a token it issues a session-scoped
881+ /// <c>providerToken.getToken</c> request, which this handler routes to the
882+ /// matching per-provider callback.
883+ /// </remarks>
884+ /// <param name="providers">Map of provider name to callback, or null/empty to clear.</param>
885+ internal void RegisterBearerTokenProviders ( IReadOnlyDictionary < string , Func < ProviderTokenArgs , Task < string > > > ? providers )
886+ {
887+ _bearerTokenProviders . Clear ( ) ;
888+ if ( providers is null || providers . Count == 0 )
889+ {
890+ ClientSessionApis . ProviderToken = null ;
891+ return ;
892+ }
893+ foreach ( var ( name , callback ) in providers )
894+ {
895+ _bearerTokenProviders [ name ] = callback ;
896+ }
897+ ClientSessionApis . ProviderToken = new BearerTokenProviderHandler ( this ) ;
898+ }
899+
900+ /// <summary>
901+ /// Routes runtime <c>providerToken.getToken</c> requests to the matching
902+ /// per-provider <c>BearerTokenProvider</c> callback registered on the session.
903+ /// </summary>
904+ private sealed class BearerTokenProviderHandler ( CopilotSession session ) : IProviderTokenHandler
905+ {
906+ public async Task < ProviderTokenAcquireResult > GetTokenAsync ( ProviderTokenAcquireRequest request , CancellationToken cancellationToken = default )
907+ {
908+ if ( ! session . _bearerTokenProviders . TryGetValue ( request . ProviderName , out var callback ) )
909+ {
910+ throw new InvalidOperationException (
911+ $ "No bearer-token provider registered for provider \" { request . ProviderName } \" ") ;
912+ }
913+ var token = await callback ( new ProviderTokenArgs { ProviderName = request . ProviderName , SessionId = request . SessionId } ) . ConfigureAwait ( false ) ;
914+ return new ProviderTokenAcquireResult { Token = token } ;
915+ }
916+ }
917+
876918 /// <summary>
877919 /// Sets the capabilities reported by the host for this session.
878920 /// </summary>
@@ -882,7 +924,6 @@ internal void SetCapabilities(SessionCapabilities? capabilities)
882924 Capabilities = capabilities ?? new SessionCapabilities ( ) ;
883925 }
884926
885- #pragma warning disable GHCP001
886927 internal void SetOpenCanvases ( IList < OpenCanvasInstance > ? canvases )
887928 {
888929 _openCanvases = canvases is { Count : > 0 }
@@ -911,22 +952,19 @@ private void UpdateOpenCanvasesFromEvent(SessionEvent sessionEvent)
911952 var data = canvasEvent . Data ;
912953 if ( string . IsNullOrEmpty ( data . InstanceId )
913954 || string . IsNullOrEmpty ( data . CanvasId )
914- || string . IsNullOrEmpty ( data . ExtensionId )
915- || string . IsNullOrEmpty ( data . Availability . Value ) )
955+ || string . IsNullOrEmpty ( data . ExtensionId ) )
916956 {
917957 _logger . LogWarning ( "failed to deserialize session.canvas.opened payload" ) ;
918958 return ;
919959 }
920960
921961 UpsertOpenCanvas ( new OpenCanvasInstance
922962 {
923- Availability = new CanvasInstanceAvailability ( data . Availability . Value ) ,
924963 CanvasId = data . CanvasId ,
925964 ExtensionId = data . ExtensionId ,
926965 ExtensionName = data . ExtensionName ,
927966 Input = data . Input ,
928967 InstanceId = data . InstanceId ,
929- Reopen = data . Reopen ,
930968 Status = data . Status ,
931969 Title = data . Title ,
932970 Url = data . Url ,
@@ -962,7 +1000,6 @@ private static JsonElement SerializeActionResult(object? value)
9621000 var element = CopilotClient . ToJsonElementForWire ( value ) ;
9631001 return element ?? NullJsonElement ;
9641002 }
965- #pragma warning restore GHCP001
9661003
9671004 private sealed class CanvasHandlerAdapter ( ICanvasHandler handler ) : Rpc . ICanvasHandler
9681005 {
0 commit comments