@microsoft/agents-hosting 1.6.0-beta.4.g8ab0e486bc → 1.6.0-beta.41.g36be9b7b46

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/dist/package.json +5 -9
  2. package/dist/src/agent-client/agentClient.js +2 -2
  3. package/dist/src/agent-client/agentClient.js.map +1 -1
  4. package/dist/src/agent-client/agentResponseHandler.js +2 -2
  5. package/dist/src/agent-client/agentResponseHandler.js.map +1 -1
  6. package/dist/src/app/agentApplication.js +27 -2
  7. package/dist/src/app/agentApplication.js.map +1 -1
  8. package/dist/src/app/auth/authorizationManager.d.ts +4 -0
  9. package/dist/src/app/auth/authorizationManager.js +94 -67
  10. package/dist/src/app/auth/authorizationManager.js.map +1 -1
  11. package/dist/src/app/auth/handlers/azureBotAuthorization.js +2 -2
  12. package/dist/src/app/auth/handlers/azureBotAuthorization.js.map +1 -1
  13. package/dist/src/app/proactive/createConversationOptionsBuilder.js +10 -6
  14. package/dist/src/app/proactive/createConversationOptionsBuilder.js.map +1 -1
  15. package/dist/src/app/teamsAttachmentDownloader.js +7 -1
  16. package/dist/src/app/teamsAttachmentDownloader.js.map +1 -1
  17. package/dist/src/auth/authConfiguration.d.ts +2 -143
  18. package/dist/src/auth/authConfiguration.js +301 -257
  19. package/dist/src/auth/authConfiguration.js.map +1 -1
  20. package/dist/src/auth/jwt-middleware.js +2 -1
  21. package/dist/src/auth/jwt-middleware.js.map +1 -1
  22. package/dist/src/auth/msalConnectionManager.js +27 -14
  23. package/dist/src/auth/msalConnectionManager.js.map +1 -1
  24. package/dist/src/auth/msalTokenProvider.d.ts +7 -0
  25. package/dist/src/auth/msalTokenProvider.js +156 -38
  26. package/dist/src/auth/msalTokenProvider.js.map +1 -1
  27. package/dist/src/auth/settings.d.ts +327 -0
  28. package/dist/src/auth/settings.js +158 -0
  29. package/dist/src/auth/settings.js.map +1 -0
  30. package/dist/src/cloudAdapter.d.ts +51 -1
  31. package/dist/src/cloudAdapter.js +219 -50
  32. package/dist/src/cloudAdapter.js.map +1 -1
  33. package/dist/src/connector-client/connectorClient.d.ts +2 -2
  34. package/dist/src/connector-client/connectorClient.js +7 -8
  35. package/dist/src/connector-client/connectorClient.js.map +1 -1
  36. package/dist/src/errorHelper.js +50 -0
  37. package/dist/src/errorHelper.js.map +1 -1
  38. package/dist/src/index.d.ts +1 -1
  39. package/dist/src/index.js +6 -2
  40. package/dist/src/index.js.map +1 -1
  41. package/dist/src/oauth/userTokenClient.js +1 -1
  42. package/dist/src/oauth/userTokenClient.js.map +1 -1
  43. package/dist/src/observability/traces.d.ts +1 -1
  44. package/dist/src/observability/traces.js +2 -2
  45. package/dist/src/observability/traces.js.map +1 -1
  46. package/dist/src/utils/env.d.ts +50 -0
  47. package/dist/src/utils/env.js +113 -0
  48. package/dist/src/utils/env.js.map +1 -0
  49. package/dist/src/utils.d.ts +10 -0
  50. package/dist/src/utils.js +18 -0
  51. package/dist/src/utils.js.map +1 -0
  52. package/package.json +5 -9
  53. package/src/agent-client/agentClient.ts +2 -2
  54. package/src/agent-client/agentResponseHandler.ts +2 -2
  55. package/src/app/agentApplication.ts +28 -3
  56. package/src/app/auth/authorizationManager.ts +84 -41
  57. package/src/app/auth/handlers/azureBotAuthorization.ts +2 -2
  58. package/src/app/proactive/createConversationOptionsBuilder.ts +8 -4
  59. package/src/app/teamsAttachmentDownloader.ts +6 -1
  60. package/src/auth/authConfiguration.ts +317 -356
  61. package/src/auth/jwt-middleware.ts +1 -1
  62. package/src/auth/msalConnectionManager.ts +27 -10
  63. package/src/auth/msalTokenProvider.ts +143 -33
  64. package/src/auth/settings.ts +323 -0
  65. package/src/cloudAdapter.ts +272 -10
  66. package/src/connector-client/connectorClient.ts +8 -8
  67. package/src/errorHelper.ts +57 -0
  68. package/src/index.ts +4 -1
  69. package/src/oauth/userTokenClient.ts +1 -1
  70. package/src/observability/traces.ts +2 -2
  71. package/src/utils/env.ts +105 -0
  72. package/src/utils.ts +14 -0
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  import { Activity, ExceptionHelper } from '@microsoft/agents-activity'
7
- import { debug } from '@microsoft/agents-telemetry'
7
+ import { debug, redactScopes } from '@microsoft/agents-telemetry'
8
8
  import { AgentApplication } from '../agentApplication'
9
9
  import { AgenticAuthorization, AzureBotAuthorization } from './handlers'
10
10
  import { TurnContext } from '../../turnContext'
@@ -13,7 +13,8 @@ import { Errors } from '../../errorHelper'
13
13
  import { ActiveAuthorizationHandler, AuthorizationHandlerStatus, AuthorizationHandler, AuthorizationHandlerSettings, AuthorizationOptions } from './types'
14
14
  import { Connections } from '../../auth/connections'
15
15
  import { sendInvokeResponse } from './utils'
16
- import { envParser, envParserUtils } from '../../auth'
16
+ import { envParser, envParserUtils } from '../../auth/settings'
17
+ import { prune } from '../../utils'
17
18
 
18
19
  const logger = debug('agents:authorization:manager')
19
20
 
@@ -155,10 +156,6 @@ export class AuthorizationManager {
155
156
  if (this.handlers.length === 0 && app.options.authorization !== undefined) {
156
157
  throw ExceptionHelper.generateException(Error, Errors.NoAuthHandlersConfigured)
157
158
  }
158
-
159
- for (const [id, handler] of Object.entries(this._handlers)) {
160
- logger.debug('auth handler "%s" type=%s scopes=%o', id, handler.type, handler.scopes)
161
- }
162
159
  }
163
160
 
164
161
  /**
@@ -320,7 +317,7 @@ export class AuthorizationManager {
320
317
  private createHandlers () {
321
318
  let legacyMessage = ''
322
319
  const settings: AuthorizationHandlerSettings = { storage: this.app.options.storage!, connections: this.connections }
323
- let runtimeEntries = Object.entries(this.app.options.authorization ?? {})
320
+ const runtimeOptionEntries = Object.entries(this.app.options.authorization ?? {})
324
321
  const result = { latest: {}, legacy: {} } as {
325
322
  latest: Record<string, Record<string, any> | undefined>;
326
323
  legacy: Record<string, Record<string, any> | undefined>;
@@ -335,7 +332,7 @@ export class AuthorizationManager {
335
332
 
336
333
  // Legacy: extract handler ID, handler options key and its value, and assign it to the correct runtime handler ID.
337
334
  if (!upperEnvKey.startsWith(this._envLatest.key.prefix.toUpperCase())) {
338
- const [id] = runtimeEntries.find(([id]) => upperEnvKey.startsWith(`${id.toUpperCase()}${this._envLegacy.key.separator}`)) ?? []
335
+ const [id] = runtimeOptionEntries.find(([id]) => upperEnvKey.startsWith(`${id.toUpperCase()}${this._envLegacy.key.separator}`)) ?? []
339
336
  if (!id) {
340
337
  continue
341
338
  }
@@ -386,7 +383,7 @@ export class AuthorizationManager {
386
383
  const fixTypes = (entries: [string, any][]) => entries
387
384
  .map(([id, options]) => [id, { ...options, type: this.fixType(id, options?.type) }] as [string, AuthorizationOptions[string]])
388
385
 
389
- runtimeEntries = fixTypes(runtimeEntries)
386
+ const runtimeEntries = fixTypes(runtimeOptionEntries)
390
387
  const latestEntries = fixTypes(Object.entries(result.latest))
391
388
  const legacyEntries = fixTypes(Object.entries(result.legacy))
392
389
 
@@ -396,39 +393,27 @@ export class AuthorizationManager {
396
393
  continue
397
394
  }
398
395
 
399
- // Find entries case-insensitively for later processing
400
- const [, runtime] = runtimeEntries.find(([key]) => key.toLowerCase() === id.toLowerCase()) ?? []
401
- const [, latest] = latestEntries.find(([key]) => key.toLowerCase() === id.toLowerCase()) ?? []
402
- const [, legacy] = legacyEntries.find(([key]) => key.toLowerCase() === id.toLowerCase()) ?? []
396
+ const { options, format } = this.resolveHandlerConfiguration(id, runtimeEntries, latestEntries, legacyEntries)
403
397
 
404
- if (runtime !== undefined && latest !== undefined) {
405
- logger.warn(this.prefix(id, 'Both runtime and latest environment variable configurations detected. Runtime configuration will take precedence over latest environment variables.'))
398
+ if (options.type === AZURE_BOT) {
399
+ // Set default values if not provided
400
+ options.title ||= 'Sign-in'
401
+ options.text ||= 'Please sign-in to continue'
402
+ options.oboScopes ??= []
403
+ options.enableSso = options.enableSso !== false // default value is true if undefined.
406
404
  }
407
405
 
408
- // Normalize runtime legacy options to latest format
409
- if (runtime?.type === AZURE_BOT) {
410
- runtime.azureBotOAuthConnectionName ??= runtime.name
411
- runtime.invalidSignInRetryMax ??= runtime.maxAttempts
412
- runtime.invalidSignInRetryMessage ??= runtime.messages?.invalidCode
413
- runtime.invalidSignInRetryMessageFormat ??= runtime.messages?.invalidCodeFormat
414
- runtime.invalidSignInRetryMaxExceededMessage ??= runtime.messages?.maxAttemptsExceeded
415
- runtime.oboConnectionName ??= runtime.obo?.connection
416
- runtime.oboScopes ??= runtime.obo?.scopes
417
- delete runtime.name
418
- delete runtime.maxAttempts
419
- delete runtime.messages
420
- delete runtime.obo
406
+ const maskedOptions: any = {}
407
+
408
+ if ('oboScopes' in options && options.oboScopes && options.oboScopes.length > 0) {
409
+ maskedOptions.oboScopes = redactScopes(options.oboScopes)
421
410
  }
422
411
 
423
- // Helper to remove undefined options
424
- const prune = <T extends Record<string, any>>(obj: T) => {
425
- const entries = Object.entries(obj).filter(([, e]) => e !== undefined)
426
- return Object.fromEntries(entries) as T
412
+ if ('scopes' in options && options.scopes && options.scopes.length > 0) {
413
+ maskedOptions.scopes = redactScopes(options.scopes)
427
414
  }
428
415
 
429
- // Priority: runtime > latest > legacy
430
- // Pruning done individually to avoid overwriting with undefined when merging.
431
- const options: AuthorizationOptions[string] = runtime ? { ...prune(legacy || {}), ...prune(runtime) } : prune(latest || {})
416
+ logger.info(this.prefix(id, 'settings loaded from \'%s\''), format, { ...options, ...maskedOptions })
432
417
 
433
418
  if (!settings.storage) {
434
419
  throw ExceptionHelper.generateException(Error, Errors.StorageRequiredForAuthorization)
@@ -437,12 +422,6 @@ export class AuthorizationManager {
437
422
  if (options.type === AGENTIC) {
438
423
  this._handlers[id] = new AgenticAuthorization(id, options, settings)
439
424
  } else if (options.type === AZURE_BOT) {
440
- // Set default values if not provided
441
- options.title ||= 'Sign-in'
442
- options.text ||= 'Please sign-in to continue'
443
- options.oboScopes ??= []
444
- options.enableSso = options.enableSso !== false // default value is true if undefined.
445
-
446
425
  this._handlers[id] = new AzureBotAuthorization(id, options, settings)
447
426
  }
448
427
 
@@ -473,4 +452,68 @@ export class AuthorizationManager {
473
452
 
474
453
  throw ExceptionHelper.generateException(Error, Errors.UnsupportedAuthHandlerType, undefined, { handlerType: type })
475
454
  }
455
+
456
+ /**
457
+ * Resolves the effective handler configuration for a given handler ID.
458
+ */
459
+ private resolveHandlerConfiguration (
460
+ id: string,
461
+ runtimeEntries: Array<[string, AuthorizationOptions[string]]>,
462
+ latestEntries: Array<[string, AuthorizationOptions[string]]>,
463
+ legacyEntries: Array<[string, AuthorizationOptions[string]]>
464
+ ): {
465
+ options: AuthorizationOptions[string];
466
+ format: string;
467
+ } {
468
+ const matchesId = ([_id]: [string, AuthorizationOptions[string]]) => _id.toLowerCase() === id.toLowerCase()
469
+
470
+ // Find entries case-insensitively for later processing
471
+ const [, runtime] = runtimeEntries.find(matchesId) ?? []
472
+ const [, latest] = latestEntries.find(matchesId) ?? []
473
+ const [, legacy] = legacyEntries.find(matchesId) ?? []
474
+
475
+ if (runtime !== undefined && latest !== undefined) {
476
+ logger.warn(this.prefix(id, 'Both runtime options and latest environment variable configurations detected. Runtime configuration will take precedence over latest environment variables.'))
477
+ }
478
+
479
+ const runtimeLegacyKeys = ['name', 'maxAttempts', 'messages', 'obo']
480
+ const isRuntimeLegacy = runtime !== undefined && Object.keys(runtime).some(key => runtimeLegacyKeys.includes(key))
481
+
482
+ let runtimeLegacyFormat = ''
483
+ if (isRuntimeLegacy && runtime.type === AZURE_BOT) {
484
+ runtime.azureBotOAuthConnectionName ??= runtime.name
485
+ runtime.invalidSignInRetryMax ??= runtime.maxAttempts
486
+ runtime.invalidSignInRetryMessage ??= runtime.messages?.invalidCode
487
+ runtime.invalidSignInRetryMessageFormat ??= runtime.messages?.invalidCodeFormat
488
+ runtime.invalidSignInRetryMaxExceededMessage ??= runtime.messages?.maxAttemptsExceeded
489
+ runtime.oboConnectionName ??= runtime.obo?.connection
490
+ runtime.oboScopes ??= runtime.obo?.scopes
491
+ delete runtime.name
492
+ delete runtime.maxAttempts
493
+ delete runtime.messages
494
+ delete runtime.obo
495
+ runtimeLegacyFormat = 'runtime options (legacy)'
496
+ }
497
+
498
+ if (runtime !== undefined && legacy !== undefined) {
499
+ return { format: `${runtimeLegacyFormat || 'runtime options'} + .env variables (legacy)`, options: { ...prune(legacy), ...prune(runtime) } }
500
+ }
501
+
502
+ if (runtime !== undefined) {
503
+ return { format: runtimeLegacyFormat || 'runtime options', options: prune(runtime) }
504
+ }
505
+
506
+ if (latest !== undefined) {
507
+ return { format: '.env variables', options: prune(latest) }
508
+ }
509
+
510
+ if (legacy !== undefined) {
511
+ // Note: this should never happen, as legacy requires runtime to be present.
512
+ // However, we show a warning just in case to detect if something goes wrong.
513
+ logger.warn(this.prefix(id, 'Legacy environment variable configuration detected without a corresponding runtime options configuration.'))
514
+ return { format: '.env variables (legacy)', options: prune(legacy) }
515
+ }
516
+
517
+ return { format: 'empty options', options: {} }
518
+ }
476
519
  }
@@ -334,7 +334,7 @@ export class AzureBotAuthorization implements AuthorizationHandler {
334
334
  const { activity } = context
335
335
  const userTokenClient = await this.getUserTokenClient(context)
336
336
  // Using getTokenOrSignInResource instead of getUserToken to avoid HTTP 404 errors.
337
- const { tokenResponse } = await userTokenClient.getTokenOrSignInResource(activity.from?.id!, this.options.azureBotOAuthConnectionName!, activity.channelId!, activity.getConversationReference(), activity.relatesTo!, '')
337
+ const { tokenResponse } = await userTokenClient.getTokenOrSignInResource(activity.from?.id!, this.options.azureBotOAuthConnectionName!, activity.channelId!, activity.getConversationReference({ forceBaseChannel: true }), activity.relatesTo!, '')
338
338
  return tokenResponse?.token
339
339
  }
340
340
 
@@ -399,7 +399,7 @@ export class AzureBotAuthorization implements AuthorizationHandler {
399
399
  const { activity } = context
400
400
 
401
401
  const userTokenClient = await this.getUserTokenClient(context)
402
- const { tokenResponse, signInResource } = await userTokenClient.getTokenOrSignInResource(activity.from?.id!, this.options.azureBotOAuthConnectionName!, activity.channelId!, activity.getConversationReference(), activity.relatesTo!, code ?? '')
402
+ const { tokenResponse, signInResource } = await userTokenClient.getTokenOrSignInResource(activity.from?.id!, this.options.azureBotOAuthConnectionName!, activity.channelId!, activity.getConversationReference({ forceBaseChannel: true }), activity.relatesTo!, code ?? '')
403
403
 
404
404
  if (!tokenResponse && active && code) {
405
405
  reason.message = 'Invalid code entered. Restarting sign-in flow'
@@ -66,6 +66,9 @@ export class CreateConversationOptionsBuilder {
66
66
  const builder = new CreateConversationOptionsBuilder(claims, channelId, serviceUrl)
67
67
  if (parameters) {
68
68
  builder._parameters = { ...builder._parameters, ...parameters }
69
+ if (parameters.activity) {
70
+ builder._activity = parameters.activity
71
+ }
69
72
  }
70
73
 
71
74
  // Set parameters.agent if not already provided — matches C# behavior
@@ -161,9 +164,10 @@ export class CreateConversationOptionsBuilder {
161
164
  throw ExceptionHelper.generateException(Error, Errors.CreateConversationBuilderMembersRequired)
162
165
  }
163
166
 
164
- const activity: Partial<Activity> = {
165
- type: 'message',
166
- ...this._activity,
167
+ const isTeamsChannel = this._channelId === Channels.Msteams &&
168
+ Boolean((this._parameters.channelData as Record<string, any>)?.channel?.id)
169
+ if (isTeamsChannel && !this._activity) {
170
+ throw ExceptionHelper.generateException(Error, Errors.CreateConversationBuilderChannelActivityRequired)
167
171
  }
168
172
 
169
173
  return {
@@ -174,7 +178,7 @@ export class CreateConversationOptionsBuilder {
174
178
  storeConversation: this._storeConversation,
175
179
  parameters: {
176
180
  ...this._parameters,
177
- activity: activity as Activity,
181
+ activity: this._activity ? { type: 'message', ...this._activity } as Activity : undefined,
178
182
  },
179
183
  }
180
184
  }
@@ -72,7 +72,12 @@ export class M365AttachmentDownloader<TState extends TurnState = TurnState> impl
72
72
  const response = await this._httpClient.get(downloadUrl, { responseType: 'arraybuffer' })
73
73
 
74
74
  const content = Buffer.from(response.data, 'binary')
75
- const contentType = response.headers['content-type'] || 'application/octet-stream'
75
+ const contentTypeHeader = typeof response.headers.get === 'function'
76
+ ? response.headers.get('content-type')
77
+ : response.headers['content-type']
78
+ const contentType = Array.isArray(contentTypeHeader)
79
+ ? (contentTypeHeader[0] ?? 'application/octet-stream')
80
+ : (typeof contentTypeHeader === 'string' ? contentTypeHeader : 'application/octet-stream')
76
81
  inputFile = { content, contentType, contentUrl: attachment.contentUrl }
77
82
  } catch (error) {
78
83
  logger.error(`Failed to download Teams attachment: ${error}`)