@microsoft/agents-hosting 1.1.0-alpha.8.g2362542eea → 1.1.0-alpha.85

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 (144) hide show
  1. package/dist/package.json +10 -6
  2. package/dist/src/activityWireCompat.d.ts +1 -1
  3. package/dist/src/activityWireCompat.js +11 -3
  4. package/dist/src/activityWireCompat.js.map +1 -1
  5. package/dist/src/agent-client/agentClient.js +7 -3
  6. package/dist/src/agent-client/agentClient.js.map +1 -1
  7. package/dist/src/agent-client/agentResponseHandler.js +6 -2
  8. package/dist/src/agent-client/agentResponseHandler.js.map +1 -1
  9. package/dist/src/app/agentApplication.d.ts +26 -11
  10. package/dist/src/app/agentApplication.js +90 -79
  11. package/dist/src/app/agentApplication.js.map +1 -1
  12. package/dist/src/app/agentApplicationBuilder.d.ts +2 -2
  13. package/dist/src/app/agentApplicationBuilder.js.map +1 -1
  14. package/dist/src/app/agentApplicationOptions.d.ts +9 -2
  15. package/dist/src/app/appRoute.d.ts +7 -0
  16. package/dist/src/app/{authorization.d.ts → auth/authorization.d.ts} +41 -139
  17. package/dist/src/app/auth/authorization.js +188 -0
  18. package/dist/src/app/auth/authorization.js.map +1 -0
  19. package/dist/src/app/auth/authorizationManager.d.ts +71 -0
  20. package/dist/src/app/auth/authorizationManager.js +170 -0
  21. package/dist/src/app/auth/authorizationManager.js.map +1 -0
  22. package/dist/src/app/auth/handlerStorage.d.ts +36 -0
  23. package/dist/src/app/auth/handlerStorage.js +62 -0
  24. package/dist/src/app/auth/handlerStorage.js.map +1 -0
  25. package/dist/src/app/auth/handlers/agenticAuthorization.d.ts +93 -0
  26. package/dist/src/app/auth/handlers/agenticAuthorization.js +134 -0
  27. package/dist/src/app/auth/handlers/agenticAuthorization.js.map +1 -0
  28. package/dist/src/app/auth/handlers/azureBotAuthorization.d.ts +222 -0
  29. package/dist/src/app/auth/handlers/azureBotAuthorization.js +428 -0
  30. package/dist/src/app/auth/handlers/azureBotAuthorization.js.map +1 -0
  31. package/dist/src/app/auth/handlers/index.d.ts +2 -0
  32. package/dist/src/app/auth/handlers/index.js +19 -0
  33. package/dist/src/app/auth/handlers/index.js.map +1 -0
  34. package/dist/src/app/auth/index.d.ts +2 -0
  35. package/dist/src/app/auth/index.js +19 -0
  36. package/dist/src/app/auth/index.js.map +1 -0
  37. package/dist/src/app/auth/types.d.ts +104 -0
  38. package/dist/src/app/auth/types.js +24 -0
  39. package/dist/src/app/auth/types.js.map +1 -0
  40. package/dist/src/app/index.d.ts +3 -3
  41. package/dist/src/app/index.js +2 -3
  42. package/dist/src/app/index.js.map +1 -1
  43. package/dist/src/app/routeList.d.ts +1 -1
  44. package/dist/src/app/routeList.js +22 -5
  45. package/dist/src/app/routeList.js.map +1 -1
  46. package/dist/src/app/streaming/streamingResponse.js +2 -1
  47. package/dist/src/app/streaming/streamingResponse.js.map +1 -1
  48. package/dist/src/auth/MemoryCache.d.ts +16 -0
  49. package/dist/src/auth/MemoryCache.js +58 -0
  50. package/dist/src/auth/MemoryCache.js.map +1 -0
  51. package/dist/src/auth/authConfiguration.d.ts +40 -2
  52. package/dist/src/auth/authConfiguration.js +209 -55
  53. package/dist/src/auth/authConfiguration.js.map +1 -1
  54. package/dist/src/auth/authConstants.d.ts +11 -0
  55. package/dist/src/auth/authConstants.js +15 -0
  56. package/dist/src/auth/authConstants.js.map +1 -0
  57. package/dist/src/auth/authProvider.d.ts +23 -0
  58. package/dist/src/auth/connections.d.ts +41 -0
  59. package/dist/src/auth/connections.js +7 -0
  60. package/dist/src/auth/connections.js.map +1 -0
  61. package/dist/src/auth/index.d.ts +2 -0
  62. package/dist/src/auth/index.js +2 -0
  63. package/dist/src/auth/index.js.map +1 -1
  64. package/dist/src/auth/jwt-middleware.js +31 -18
  65. package/dist/src/auth/jwt-middleware.js.map +1 -1
  66. package/dist/src/auth/msalConnectionManager.d.ts +64 -0
  67. package/dist/src/auth/msalConnectionManager.js +148 -0
  68. package/dist/src/auth/msalConnectionManager.js.map +1 -0
  69. package/dist/src/auth/msalTokenProvider.d.ts +24 -0
  70. package/dist/src/auth/msalTokenProvider.js +143 -16
  71. package/dist/src/auth/msalTokenProvider.js.map +1 -1
  72. package/dist/src/baseAdapter.d.ts +10 -25
  73. package/dist/src/baseAdapter.js +2 -15
  74. package/dist/src/baseAdapter.js.map +1 -1
  75. package/dist/src/cloudAdapter.d.ts +40 -23
  76. package/dist/src/cloudAdapter.js +141 -63
  77. package/dist/src/cloudAdapter.js.map +1 -1
  78. package/dist/src/connector-client/connectorClient.d.ts +15 -0
  79. package/dist/src/connector-client/connectorClient.js +49 -15
  80. package/dist/src/connector-client/connectorClient.js.map +1 -1
  81. package/dist/src/index.d.ts +0 -1
  82. package/dist/src/index.js +0 -1
  83. package/dist/src/index.js.map +1 -1
  84. package/dist/src/oauth/customUserTokenAPI.d.ts +1 -0
  85. package/dist/src/oauth/customUserTokenAPI.js +11 -0
  86. package/dist/src/oauth/customUserTokenAPI.js.map +1 -0
  87. package/dist/src/oauth/index.d.ts +0 -1
  88. package/dist/src/oauth/index.js +0 -1
  89. package/dist/src/oauth/index.js.map +1 -1
  90. package/dist/src/oauth/userTokenClient.d.ts +30 -13
  91. package/dist/src/oauth/userTokenClient.js +62 -26
  92. package/dist/src/oauth/userTokenClient.js.map +1 -1
  93. package/dist/src/oauth/userTokenClient.types.d.ts +19 -6
  94. package/dist/src/turnContext.d.ts +7 -1
  95. package/dist/src/turnContext.js +11 -4
  96. package/dist/src/turnContext.js.map +1 -1
  97. package/package.json +10 -6
  98. package/src/activityWireCompat.ts +12 -4
  99. package/src/agent-client/agentClient.ts +9 -3
  100. package/src/agent-client/agentResponseHandler.ts +5 -2
  101. package/src/app/agentApplication.ts +95 -74
  102. package/src/app/agentApplicationBuilder.ts +2 -2
  103. package/src/app/agentApplicationOptions.ts +10 -2
  104. package/src/app/appRoute.ts +8 -0
  105. package/src/app/auth/authorization.ts +261 -0
  106. package/src/app/auth/authorizationManager.ts +213 -0
  107. package/src/app/auth/handlerStorage.ts +61 -0
  108. package/src/app/auth/handlers/agenticAuthorization.ts +182 -0
  109. package/src/app/auth/handlers/azureBotAuthorization.ts +599 -0
  110. package/src/app/auth/handlers/index.ts +2 -0
  111. package/src/app/auth/index.ts +2 -0
  112. package/src/app/auth/types.ts +111 -0
  113. package/src/app/index.ts +3 -3
  114. package/src/app/routeList.ts +24 -5
  115. package/src/app/streaming/streamingResponse.ts +2 -1
  116. package/src/auth/MemoryCache.ts +59 -0
  117. package/src/auth/authConfiguration.ts +239 -53
  118. package/src/auth/authConstants.ts +11 -0
  119. package/src/auth/authProvider.ts +31 -0
  120. package/src/auth/connections.ts +47 -0
  121. package/src/auth/index.ts +2 -0
  122. package/src/auth/jwt-middleware.ts +38 -21
  123. package/src/auth/msalConnectionManager.ts +175 -0
  124. package/src/auth/msalTokenProvider.ts +185 -9
  125. package/src/baseAdapter.ts +10 -29
  126. package/src/cloudAdapter.ts +205 -71
  127. package/src/connector-client/connectorClient.ts +59 -15
  128. package/src/index.ts +0 -1
  129. package/src/oauth/customUserTokenAPI.ts +5 -0
  130. package/src/oauth/index.ts +0 -1
  131. package/src/oauth/userTokenClient.ts +76 -22
  132. package/src/oauth/userTokenClient.types.ts +20 -8
  133. package/src/turnContext.ts +16 -5
  134. package/dist/src/app/authorization.js +0 -387
  135. package/dist/src/app/authorization.js.map +0 -1
  136. package/dist/src/claimsIdentity.d.ts +0 -35
  137. package/dist/src/claimsIdentity.js +0 -43
  138. package/dist/src/claimsIdentity.js.map +0 -1
  139. package/dist/src/oauth/oAuthFlow.d.ts +0 -119
  140. package/dist/src/oauth/oAuthFlow.js +0 -316
  141. package/dist/src/oauth/oAuthFlow.js.map +0 -1
  142. package/src/app/authorization.ts +0 -432
  143. package/src/claimsIdentity.ts +0 -47
  144. package/src/oauth/oAuthFlow.ts +0 -378
@@ -9,11 +9,12 @@ import { TurnContext } from './turnContext'
9
9
  import { Response } from 'express'
10
10
  import { Request } from './auth/request'
11
11
  import { ConnectorClient } from './connector-client/connectorClient'
12
- import { AuthConfiguration, loadAuthConfigFromEnv } from './auth/authConfiguration'
12
+ import { AuthConfiguration, getAuthConfigWithDefaults } from './auth/authConfiguration'
13
13
  import { AuthProvider } from './auth/authProvider'
14
- import { Activity, ActivityEventNames, ActivityTypes, Channels, ConversationReference, DeliveryModes, ConversationParameters } from '@microsoft/agents-activity'
14
+ import { ApxProductionScope } from './auth/authConstants'
15
+ import { MsalConnectionManager } from './auth/msalConnectionManager'
16
+ import { Activity, ActivityEventNames, ActivityTypes, Channels, ConversationReference, DeliveryModes, ConversationParameters, RoleTypes } from '@microsoft/agents-activity'
15
17
  import { ResourceResponse } from './connector-client/resourceResponse'
16
- import { MsalTokenProvider } from './auth/msalTokenProvider'
17
18
  import * as uuid from 'uuid'
18
19
  import { debug } from '@microsoft/agents-activity/logger'
19
20
  import { StatusCodes } from './statusCodes'
@@ -23,34 +24,34 @@ import { AttachmentData } from './connector-client/attachmentData'
23
24
  import { normalizeIncomingActivity } from './activityWireCompat'
24
25
  import { UserTokenClient } from './oauth'
25
26
  import { HeaderPropagation, HeaderPropagationCollection, HeaderPropagationDefinition } from './headerPropagation'
26
-
27
+ import { JwtPayload } from 'jsonwebtoken'
28
+ import { getTokenServiceEndpoint } from './oauth/customUserTokenAPI'
27
29
  const logger = debug('agents:cloud-adapter')
28
30
 
29
31
  /**
30
32
  * Adapter for handling agent interactions with various channels through cloud-based services.
31
33
  *
32
34
  * @remarks
33
- * CloudAdapter processes incoming HTTP requests from Microsoft Bot Framework channels,
35
+ * CloudAdapter processes incoming HTTP requests from Azure Bot Service channels,
34
36
  * authenticates them, and generates outgoing responses. It manages the communication
35
37
  * flow between agents and users across different channels, handling activities, attachments,
36
38
  * and conversation continuations.
37
39
  */
38
40
  export class CloudAdapter extends BaseAdapter {
39
41
  /**
40
- * Client for connecting to the Bot Framework Connector service
42
+ * Client for connecting to the Azure Bot Service
41
43
  */
42
- public connectorClient!: ConnectorClient
43
- authConfig: AuthConfiguration
44
+ connectionManager: MsalConnectionManager
45
+
44
46
  /**
45
47
  * Creates an instance of CloudAdapter.
46
48
  * @param authConfig - The authentication configuration for securing communications
47
- * @param authProvider - Optional custom authentication provider. If not specified, a default MsalTokenProvider will be used
49
+ * @param authProvider - No longer used
48
50
  */
49
51
  constructor (authConfig?: AuthConfiguration, authProvider?: AuthProvider, userTokenClient?: UserTokenClient) {
50
52
  super()
51
- this.authConfig = authConfig ?? loadAuthConfigFromEnv()
52
- this.authProvider = authProvider ?? new MsalTokenProvider()
53
- this.userTokenClient = userTokenClient ?? new UserTokenClient(this.authConfig.clientId)
53
+ authConfig = getAuthConfigWithDefaults(authConfig)
54
+ this.connectionManager = new MsalConnectionManager(undefined, undefined, authConfig)
54
55
  }
55
56
 
56
57
  /**
@@ -92,17 +93,79 @@ export class CloudAdapter extends BaseAdapter {
92
93
  protected async createConnectorClient (
93
94
  serviceUrl: string,
94
95
  scope: string,
96
+ identity: JwtPayload,
95
97
  headers?: HeaderPropagationCollection
96
98
  ): Promise<ConnectorClient> {
97
- return ConnectorClient.createClientWithAuth(
99
+ // get the correct token provider
100
+ const tokenProvider = this.connectionManager.getTokenProvider(identity, serviceUrl)
101
+
102
+ const token = await tokenProvider.getAccessToken(scope)
103
+ return ConnectorClient.createClientWithToken(
98
104
  serviceUrl,
99
- this.authConfig,
100
- this.authProvider,
101
- scope,
105
+ token,
102
106
  headers
103
107
  )
104
108
  }
105
109
 
110
+ protected async createConnectorClientWithIdentity (
111
+ identity: JwtPayload,
112
+ activity: Activity,
113
+ headers?: HeaderPropagationCollection) {
114
+ if (!identity?.aud) {
115
+ // anonymous
116
+ return ConnectorClient.createClientWithToken(
117
+ activity.serviceUrl!,
118
+ null!,
119
+ headers
120
+ )
121
+ }
122
+
123
+ let connectorClient
124
+ const tokenProvider = this.connectionManager.getTokenProviderFromActivity(identity, activity)
125
+ if (activity.isAgenticRequest()) {
126
+ logger.debug('Activity is from an agentic source, using special scope', activity.recipient)
127
+
128
+ if (activity.recipient?.role === RoleTypes.AgenticIdentity && activity.getAgenticInstanceId()) {
129
+ // get agentic instance token
130
+ const token = await tokenProvider.getAgenticInstanceToken(activity.getAgenticInstanceId() ?? '')
131
+ connectorClient = ConnectorClient.createClientWithToken(
132
+ activity.serviceUrl!,
133
+ token,
134
+ headers
135
+ )
136
+ } else if (activity.recipient?.role === RoleTypes.AgenticUser && activity.getAgenticInstanceId() && activity.getAgenticUser()) {
137
+ const scope = tokenProvider.connectionSettings?.scope ?? ApxProductionScope
138
+ const token = await tokenProvider.getAgenticUserToken(activity.getAgenticInstanceId() ?? '', activity.getAgenticUser() ?? '', [scope])
139
+
140
+ connectorClient = ConnectorClient.createClientWithToken(
141
+ activity.serviceUrl!,
142
+ token,
143
+ headers
144
+ )
145
+ } else {
146
+ throw new Error('Could not create connector client for agentic user')
147
+ }
148
+ } else {
149
+ // ABS tokens will not have an azp/appid so use the botframework scope.
150
+ // Otherwise use the appId. This will happen when communicating back to another agent.
151
+ const scope = identity.azp ?? identity.appid ?? 'https://api.botframework.com'
152
+ const token = await tokenProvider.getAccessToken(scope)
153
+ connectorClient = ConnectorClient.createClientWithToken(
154
+ activity.serviceUrl!,
155
+ token,
156
+ headers
157
+ )
158
+ }
159
+
160
+ return connectorClient
161
+ }
162
+
163
+ static createIdentity (appId: string) : JwtPayload {
164
+ return {
165
+ aud: appId
166
+ } as JwtPayload
167
+ }
168
+
106
169
  /**
107
170
  * Sets the connector client on the turn context.
108
171
  *
@@ -110,24 +173,71 @@ export class CloudAdapter extends BaseAdapter {
110
173
  * @protected
111
174
  */
112
175
  protected setConnectorClient (
113
- context: TurnContext
176
+ context: TurnContext,
177
+ connectorClient?: ConnectorClient
178
+ ) {
179
+ context.turnState.set(this.ConnectorClientKey, connectorClient)
180
+ }
181
+
182
+ /**
183
+ * Creates a user token client for a specific service URL and scope.
184
+ *
185
+ * @param serviceUrl - The URL of the service to connect to
186
+ * @param scope - The authentication scope to use
187
+ * @param headers - Optional headers to propagate in the request
188
+ * @returns A promise that resolves to a ConnectorClient instance
189
+ * @protected
190
+ */
191
+ protected async createUserTokenClient (
192
+ identity: JwtPayload,
193
+ tokenServiceEndpoint: string = getTokenServiceEndpoint(),
194
+ scope: string = 'https://api.botframework.com',
195
+ audience: string = 'https://api.botframework.com',
196
+ headers?: HeaderPropagationCollection
197
+ ): Promise<UserTokenClient> {
198
+ if (!identity?.aud) {
199
+ // anonymous
200
+ return UserTokenClient.createClientWithScope(
201
+ tokenServiceEndpoint,
202
+ null!,
203
+ scope,
204
+ headers
205
+ )
206
+ }
207
+
208
+ // get the correct token provider
209
+ const tokenProvider = this.connectionManager.getTokenProvider(identity, tokenServiceEndpoint)
210
+
211
+ return UserTokenClient.createClientWithScope(
212
+ tokenServiceEndpoint,
213
+ tokenProvider,
214
+ scope,
215
+ headers
216
+ )
217
+ }
218
+
219
+ /**
220
+ * Sets the user token client on the turn context.
221
+ *
222
+ * @param context - The current turn context
223
+ * @protected
224
+ */
225
+ protected setUserTokenClient (
226
+ context: TurnContext,
227
+ userTokenClient?: UserTokenClient
114
228
  ) {
115
- context.turnState.set('connectorClient', this.connectorClient)
229
+ context.turnState.set(this.UserTokenClientKey, userTokenClient)
116
230
  }
117
231
 
118
232
  /**
233
+ * @deprecated This function will not be supported in future versions. Create TurnContext directly.
119
234
  * Creates a TurnContext for the given activity and logic.
120
235
  * @param activity - The activity to process.
121
236
  * @param logic - The logic to execute.
122
237
  * @returns The created TurnContext.
123
238
  */
124
- createTurnContext (activity: Activity, logic: AgentHandler): TurnContext {
125
- return new TurnContext(this, activity)
126
- }
127
-
128
- async createTurnContextWithScope (activity: Activity, logic: AgentHandler, scope: string): Promise<TurnContext> {
129
- this.connectorClient = await ConnectorClient.createClientWithAuth(activity.serviceUrl!, this.authConfig!, this.authProvider, scope)
130
- return new TurnContext(this, activity)
239
+ createTurnContext (activity: Activity, logic: AgentHandler, identity?: JwtPayload): TurnContext {
240
+ return new TurnContext(this, activity, identity)
131
241
  }
132
242
 
133
243
  /**
@@ -154,9 +264,7 @@ export class CloudAdapter extends BaseAdapter {
154
264
  delete activity.id
155
265
  let response: ResourceResponse = { id: '' }
156
266
 
157
- if (activity.type === ActivityTypes.Delay) {
158
- await setTimeout(() => { }, typeof activity.value === 'number' ? activity.value : 1000)
159
- } else if (activity.type === ActivityTypes.InvokeResponse) {
267
+ if (activity.type === ActivityTypes.InvokeResponse) {
160
268
  context.turnState.set(INVOKE_RESPONSE_KEY, activity)
161
269
  } else if (activity.type === ActivityTypes.Trace && activity.channelId !== Channels.Emulator) {
162
270
  // no-op
@@ -165,12 +273,10 @@ export class CloudAdapter extends BaseAdapter {
165
273
  throw new Error('Invalid activity object')
166
274
  }
167
275
 
168
- this.connectorClient = await this.createConnectorClient(activity.serviceUrl, 'https://api.botframework.com')
169
-
170
276
  if (activity.replyToId) {
171
- response = await this.connectorClient.replyToActivity(activity.conversation.id, activity.replyToId, activity)
277
+ response = await context.turnState.get(this.ConnectorClientKey).replyToActivity(activity.conversation.id, activity.replyToId, activity)
172
278
  } else {
173
- response = await this.connectorClient.sendToConversation(activity.conversation.id, activity)
279
+ response = await context.turnState.get(this.ConnectorClientKey).sendToConversation(activity.conversation.id, activity)
174
280
  }
175
281
  }
176
282
 
@@ -184,18 +290,6 @@ export class CloudAdapter extends BaseAdapter {
184
290
  return responses
185
291
  }
186
292
 
187
- /**
188
- * Replies to an activity.
189
- * @param activity - The activity to reply to.
190
- * @returns A promise representing the ResourceResponse for the sent activity.
191
- */
192
- async replyToActivity (activity: Activity): Promise<ResourceResponse> {
193
- if (!activity.serviceUrl || (activity.conversation == null) || !activity.conversation.id || !activity.id) {
194
- throw new Error('Invalid activity object')
195
- }
196
- return await this.connectorClient.replyToActivity(activity.conversation.id, activity.id, activity)
197
- }
198
-
199
293
  /**
200
294
  * Processes an incoming request and sends the response.
201
295
  * @param request - The incoming request.
@@ -236,14 +330,17 @@ export class CloudAdapter extends BaseAdapter {
236
330
  }
237
331
 
238
332
  logger.debug('Received activity: ', activity)
239
- const context = this.createTurnContext(activity, logic)
240
- const scope = request.user?.azp ?? request.user?.appid ?? 'https://api.botframework.com'
241
333
 
334
+ const context = new TurnContext(this, activity, request.user!)
242
335
  // if Delivery Mode == ExpectReplies, we don't need a connector client.
243
336
  if (this.resolveIfConnectorClientIsNeeded(activity)) {
244
- logger.debug('Creating connector client with scope: ', scope)
245
- this.connectorClient = await this.createConnectorClient(activity.serviceUrl!, scope, headers)
246
- this.setConnectorClient(context)
337
+ const connectorClient = await this.createConnectorClientWithIdentity(request.user!, activity, headers)
338
+ this.setConnectorClient(context, connectorClient)
339
+ }
340
+
341
+ if (!activity.isAgenticRequest()) {
342
+ const userTokenClient = await this.createUserTokenClient(request.user!)
343
+ this.setUserTokenClient(context, userTokenClient)
247
344
  }
248
345
 
249
346
  if (
@@ -259,7 +356,6 @@ export class CloudAdapter extends BaseAdapter {
259
356
 
260
357
  await this.runMiddleware(context, logic)
261
358
  const invokeResponse = this.processTurnResults(context)
262
-
263
359
  return end(invokeResponse?.status ?? StatusCodes.OK, invokeResponse?.body)
264
360
  }
265
361
 
@@ -301,7 +397,7 @@ export class CloudAdapter extends BaseAdapter {
301
397
  throw new Error('Invalid activity object')
302
398
  }
303
399
 
304
- const response = await this.connectorClient.updateActivity(
400
+ const response = await context.turnState.get(this.ConnectorClientKey).updateActivity(
305
401
  activity.conversation.id,
306
402
  activity.id,
307
403
  activity
@@ -325,7 +421,7 @@ export class CloudAdapter extends BaseAdapter {
325
421
  throw new Error('Invalid conversation reference object')
326
422
  }
327
423
 
328
- await this.connectorClient.deleteActivity(reference.conversation.id, reference.activityId)
424
+ await context.turnState.get(this.ConnectorClientKey).deleteActivity(reference.conversation.id, reference.activityId)
329
425
  }
330
426
 
331
427
  /**
@@ -334,25 +430,44 @@ export class CloudAdapter extends BaseAdapter {
334
430
  * @param logic - The logic to execute.
335
431
  * @returns A promise representing the completion of the continue operation.
336
432
  */
337
- async continueConversation (reference: ConversationReference, logic: (revocableContext: TurnContext) => Promise<void>, isResponse: Boolean = false): Promise<void> {
433
+ async continueConversation (
434
+ botAppIdOrIdentity: string | JwtPayload,
435
+ reference: ConversationReference,
436
+ logic: (revocableContext: TurnContext) => Promise<void>,
437
+ isResponse: Boolean = false): Promise<void> {
338
438
  if (!reference || !reference.serviceUrl || (reference.conversation == null) || !reference.conversation.id) {
339
- throw new Error('Invalid conversation reference object')
439
+ throw new Error('continueConversation: Invalid conversation reference object')
340
440
  }
341
441
 
342
- let context
343
- if (isResponse) {
344
- context = await this.createTurnContextWithScope(Activity.getContinuationActivity(reference), logic, 'https://api.botframework.com')
345
- } else {
346
- context = this.createTurnContext(Activity.getContinuationActivity(reference), logic)
442
+ if (!botAppIdOrIdentity) {
443
+ throw new TypeError('continueConversation: botAppIdOrIdentity is required')
444
+ }
445
+ const botAppId = typeof botAppIdOrIdentity === 'string' ? botAppIdOrIdentity : botAppIdOrIdentity.aud as string
446
+
447
+ // Only having the botId will only work against ABS or Agentic. Proactive to other agents will
448
+ // not work with just botId. Use a JwtPayload with property aud (which is botId) and appid populated.
449
+ const identity =
450
+ typeof botAppIdOrIdentity !== 'string'
451
+ ? botAppIdOrIdentity
452
+ : CloudAdapter.createIdentity(botAppId)
453
+
454
+ const context = new TurnContext(this, Activity.getContinuationActivity(reference), identity)
455
+ const connectorClient = await this.createConnectorClientWithIdentity(identity, context.activity)
456
+ this.setConnectorClient(context, connectorClient)
457
+
458
+ if (!context.activity.isAgenticRequest()) {
459
+ const userTokenClient = await this.createUserTokenClient(identity)
460
+ this.setUserTokenClient(context, userTokenClient)
347
461
  }
462
+
348
463
  await this.runMiddleware(context, logic)
349
464
  }
350
465
 
351
466
  /**
352
- * Processes the turn results and returns an InvokeResponse if applicable.
353
- * @param context - The TurnContext for the current turn.
354
- * @returns The InvokeResponse if applicable, otherwise undefined.
355
- */
467
+ * Processes the turn results and returns an InvokeResponse if applicable.
468
+ * @param context - The TurnContext for the current turn.
469
+ * @returns The InvokeResponse if applicable, otherwise undefined.
470
+ */
356
471
  protected processTurnResults (context: TurnContext): InvokeResponse | undefined {
357
472
  logger.info('<--Sending back turn results')
358
473
  // Handle ExpectedReplies scenarios where all activities have been buffered and sent back at once in an invoke response.
@@ -437,7 +552,9 @@ export class CloudAdapter extends BaseAdapter {
437
552
  if (!conversationParameters) throw new TypeError('`conversationParameters` must be defined')
438
553
  if (!logic) throw new TypeError('`logic` must be defined')
439
554
 
440
- const restClient = await this.createConnectorClient(serviceUrl, audience)
555
+ const identity = CloudAdapter.createIdentity(audience)
556
+ const restClient = await this.createConnectorClient(serviceUrl, audience, identity)
557
+ const userTokenClient = await this.createUserTokenClient(identity)
441
558
  const createConversationResult = await restClient.createConversation(conversationParameters)
442
559
  const createActivity = this.createCreateActivity(
443
560
  createConversationResult.id,
@@ -445,17 +562,24 @@ export class CloudAdapter extends BaseAdapter {
445
562
  serviceUrl,
446
563
  conversationParameters
447
564
  )
448
- const context = new TurnContext(this, createActivity)
565
+ const context = new TurnContext(this, createActivity, CloudAdapter.createIdentity(agentAppId))
566
+ this.setConnectorClient(context, restClient)
567
+ this.setUserTokenClient(context, userTokenClient)
449
568
  await this.runMiddleware(context, logic)
450
569
  }
451
570
 
452
571
  /**
572
+ * @deprecated This function will not be supported in future versions. Use TurnContext.turnState.get<ConnectorClient>(CloudAdapter.ConnectorClientKey).
453
573
  * Uploads an attachment.
454
574
  * @param conversationId - The conversation ID.
455
575
  * @param attachmentData - The attachment data.
456
576
  * @returns A promise representing the ResourceResponse for the uploaded attachment.
457
577
  */
458
- async uploadAttachment (conversationId: string, attachmentData: AttachmentData): Promise<ResourceResponse> {
578
+ async uploadAttachment (context: TurnContext, conversationId: string, attachmentData: AttachmentData): Promise<ResourceResponse> {
579
+ if (context === undefined) {
580
+ throw new Error('context is required')
581
+ }
582
+
459
583
  if (conversationId === undefined) {
460
584
  throw new Error('conversationId is required')
461
585
  }
@@ -464,29 +588,39 @@ export class CloudAdapter extends BaseAdapter {
464
588
  throw new Error('attachmentData is required')
465
589
  }
466
590
 
467
- return await this.connectorClient.uploadAttachment(conversationId, attachmentData)
591
+ return await context.turnState.get<ConnectorClient>(this.ConnectorClientKey).uploadAttachment(conversationId, attachmentData)
468
592
  }
469
593
 
470
594
  /**
595
+ * @deprecated This function will not be supported in future versions. Use TurnContext.turnState.get<ConnectorClient>(CloudAdapter.ConnectorClientKey).
471
596
  * Gets attachment information.
472
597
  * @param attachmentId - The attachment ID.
473
598
  * @returns A promise representing the AttachmentInfo for the requested attachment.
474
599
  */
475
- async getAttachmentInfo (attachmentId: string): Promise<AttachmentInfo> {
600
+ async getAttachmentInfo (context: TurnContext, attachmentId: string): Promise<AttachmentInfo> {
601
+ if (context === undefined) {
602
+ throw new Error('context is required')
603
+ }
604
+
476
605
  if (attachmentId === undefined) {
477
606
  throw new Error('attachmentId is required')
478
607
  }
479
608
 
480
- return await this.connectorClient.getAttachmentInfo(attachmentId)
609
+ return await context.turnState.get<ConnectorClient>(this.ConnectorClientKey).getAttachmentInfo(attachmentId)
481
610
  }
482
611
 
483
612
  /**
613
+ * @deprecated This function will not be supported in future versions. Use TurnContext.turnState.get<ConnectorClient>(CloudAdapter.ConnectorClientKey).
484
614
  * Gets an attachment.
485
615
  * @param attachmentId - The attachment ID.
486
616
  * @param viewId - The view ID.
487
617
  * @returns A promise representing the NodeJS.ReadableStream for the requested attachment.
488
618
  */
489
- async getAttachment (attachmentId: string, viewId: string): Promise<NodeJS.ReadableStream> {
619
+ async getAttachment (context: TurnContext, attachmentId: string, viewId: string): Promise<NodeJS.ReadableStream> {
620
+ if (context === undefined) {
621
+ throw new Error('context is required')
622
+ }
623
+
490
624
  if (attachmentId === undefined) {
491
625
  throw new Error('attachmentId is required')
492
626
  }
@@ -495,6 +629,6 @@ export class CloudAdapter extends BaseAdapter {
495
629
  throw new Error('viewId is required')
496
630
  }
497
631
 
498
- return await this.connectorClient.getAttachment(attachmentId, viewId)
632
+ return await context.turnState.get<ConnectorClient>(this.ConnectorClientKey).getAttachment(attachmentId, viewId)
499
633
  }
500
634
  }
@@ -3,7 +3,7 @@ import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
3
3
  import { AuthConfiguration } from '../auth/authConfiguration'
4
4
  import { AuthProvider } from '../auth/authProvider'
5
5
  import { debug } from '@microsoft/agents-activity/logger'
6
- import { Activity, ChannelAccount, ConversationParameters } from '@microsoft/agents-activity'
6
+ import { Activity, ChannelAccount, ConversationParameters, RoleTypes, Channels } from '@microsoft/agents-activity'
7
7
  import { ConversationsResult } from './conversationsResult'
8
8
  import { ConversationResourceResponse } from './conversationResourceResponse'
9
9
  import { ResourceResponse } from './resourceResponse'
@@ -91,23 +91,38 @@ export class ConnectorClient {
91
91
  scope: string,
92
92
  headers?: HeaderPropagationCollection
93
93
  ): Promise<ConnectorClient> {
94
+ const token = await authProvider.getAccessToken(authConfig, scope)
95
+ return this.createClientWithToken(baseURL, token, headers)
96
+ }
97
+
98
+ /**
99
+ * Creates a new instance of ConnectorClient with token.
100
+ * @param baseURL - The base URL for the API.
101
+ * @param token - The authentication token.
102
+ * @param headers - Optional headers to propagate in the request.
103
+ * @returns A new instance of ConnectorClient.
104
+ */
105
+ static createClientWithToken (
106
+ baseURL: string,
107
+ token: string,
108
+ headers?: HeaderPropagationCollection
109
+ ): ConnectorClient {
94
110
  const headerPropagation = headers ?? new HeaderPropagation({ 'User-Agent': '' })
95
111
  headerPropagation.concat({ 'User-Agent': getProductInfo() })
96
- headerPropagation.override({ Accept: 'application/json' })
112
+ headerPropagation.override({
113
+ Accept: 'application/json',
114
+ 'Content-Type': 'application/json', // Required by transformRequest
115
+ })
97
116
 
98
117
  const axiosInstance = axios.create({
99
118
  baseURL,
100
119
  headers: headerPropagation.outgoing,
101
- transformRequest: [
102
- (data, headers) => {
103
- return JSON.stringify(normalizeOutgoingActivity(data))
104
- }]
105
120
  })
106
121
 
107
- const token = await authProvider.getAccessToken(authConfig, scope)
108
- if (token.length > 1) {
122
+ if (token && token.length > 1) {
109
123
  axiosInstance.defaults.headers.common.Authorization = `Bearer ${token}`
110
124
  }
125
+
111
126
  return new ConnectorClient(axiosInstance)
112
127
  }
113
128
 
@@ -147,14 +162,17 @@ export class ConnectorClient {
147
162
  * @returns The conversation resource response.
148
163
  */
149
164
  public async createConversation (body: ConversationParameters): Promise<ConversationResourceResponse> {
150
- // const payload = normalizeOutgoingConvoParams(body)
165
+ const payload = {
166
+ ...body,
167
+ activity: normalizeOutgoingActivity(body.activity)
168
+ }
151
169
  const config: AxiosRequestConfig = {
152
170
  method: 'post',
153
171
  url: '/v3/conversations',
154
172
  headers: {
155
173
  'Content-Type': 'application/json'
156
174
  },
157
- data: body
175
+ data: payload
158
176
  }
159
177
  const response: AxiosResponse = await this._axiosInstance(config)
160
178
  return response.data
@@ -176,19 +194,42 @@ export class ConnectorClient {
176
194
  if (!conversationId || !activityId) {
177
195
  throw new Error('conversationId and activityId are required')
178
196
  }
197
+
198
+ const trimmedConversationId: string = this.conditionallyTruncateConversationId(conversationId, body)
199
+
179
200
  const config: AxiosRequestConfig = {
180
201
  method: 'post',
181
- url: `v3/conversations/${conversationId}/activities/${encodeURIComponent(activityId)}`,
202
+ url: `v3/conversations/${trimmedConversationId}/activities/${encodeURIComponent(activityId)}`,
182
203
  headers: {
183
204
  'Content-Type': 'application/json'
184
205
  },
185
- data: body
206
+ data: normalizeOutgoingActivity(body)
186
207
  }
187
208
  const response = await this._axiosInstance(config)
188
209
  logger.info('Reply to conversation/activity: ', response.data.id!, activityId)
189
210
  return response.data
190
211
  }
191
212
 
213
+ /**
214
+ * Trim the conversationId to a fixed length when creating the URL. This is applied only in specific API calls for agentic calls.
215
+ * @param conversationId The ID of the conversation to potentially truncate.
216
+ * @param activity The activity object used to determine if truncation is necessary.
217
+ * @returns The original or truncated conversationId, depending on the channel and activity role.
218
+ */
219
+ private conditionallyTruncateConversationId (conversationId: string, activity: Activity): string {
220
+ if (
221
+ (activity.channelIdChannel === Channels.Msteams || activity.channelIdChannel === Channels.Agents) &&
222
+ (activity.from?.role === RoleTypes.AgenticIdentity || activity.from?.role === RoleTypes.AgenticUser)) {
223
+ let maxLength = 150
224
+ if (process.env.MAX_APX_CONVERSATION_ID_LENGTH && !isNaN(parseInt(process.env.MAX_APX_CONVERSATION_ID_LENGTH, 10))) {
225
+ maxLength = parseInt(process.env.MAX_APX_CONVERSATION_ID_LENGTH, 10)
226
+ }
227
+ return conversationId.length > maxLength ? conversationId.substring(0, maxLength) : conversationId
228
+ } else {
229
+ return conversationId
230
+ }
231
+ }
232
+
192
233
  /**
193
234
  * Sends an activity to a conversation.
194
235
  * @param conversationId - The ID of the conversation.
@@ -203,13 +244,16 @@ export class ConnectorClient {
203
244
  if (!conversationId) {
204
245
  throw new Error('conversationId is required')
205
246
  }
247
+
248
+ const trimmedConversationId: string = this.conditionallyTruncateConversationId(conversationId, body)
249
+
206
250
  const config: AxiosRequestConfig = {
207
251
  method: 'post',
208
- url: `v3/conversations/${conversationId}/activities`,
252
+ url: `v3/conversations/${trimmedConversationId}/activities`,
209
253
  headers: {
210
254
  'Content-Type': 'application/json'
211
255
  },
212
- data: body
256
+ data: normalizeOutgoingActivity(body)
213
257
  }
214
258
  const response = await this._axiosInstance(config)
215
259
  return response.data
@@ -236,7 +280,7 @@ export class ConnectorClient {
236
280
  headers: {
237
281
  'Content-Type': 'application/json'
238
282
  },
239
- data: body
283
+ data: normalizeOutgoingActivity(body)
240
284
  }
241
285
  const response = await this._axiosInstance(config)
242
286
  return response.data
package/src/index.ts CHANGED
@@ -19,7 +19,6 @@ export * from './activityHandler'
19
19
  export * from './baseAdapter'
20
20
  export * from './cloudAdapter'
21
21
  export * from './middlewareSet'
22
- export * from './claimsIdentity'
23
22
  export * from './messageFactory'
24
23
  export * from './statusCodes'
25
24
  export * from './turnContext'
@@ -0,0 +1,5 @@
1
+ // Copyright (c) Microsoft Corporation. All rights reserved.
2
+ // Licensed under the MIT License.
3
+ export const getTokenServiceEndpoint = (): string => {
4
+ return process.env.TOKEN_SERVICE_ENDPOINT ?? 'https://api.botframework.com'
5
+ }
@@ -1,3 +1,2 @@
1
1
  export * from './userTokenClient'
2
- export * from './oAuthFlow'
3
2
  export * from './userTokenClient.types'