@microsoft/agents-hosting 1.5.0-beta.6.ga236d9a19c → 1.5.1

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 (157) hide show
  1. package/dist/package.json +10 -9
  2. package/dist/src/activityHandler.js +2 -2
  3. package/dist/src/activityHandler.js.map +1 -1
  4. package/dist/src/agent-client/agentClient.js +49 -40
  5. package/dist/src/agent-client/agentClient.js.map +1 -1
  6. package/dist/src/agent-client/agentResponseHandler.js +2 -2
  7. package/dist/src/agent-client/agentResponseHandler.js.map +1 -1
  8. package/dist/src/app/agentApplication.d.ts +36 -10
  9. package/dist/src/app/agentApplication.js +169 -99
  10. package/dist/src/app/agentApplication.js.map +1 -1
  11. package/dist/src/app/agentApplicationBuilder.d.ts +15 -0
  12. package/dist/src/app/agentApplicationBuilder.js +22 -4
  13. package/dist/src/app/agentApplicationBuilder.js.map +1 -1
  14. package/dist/src/app/agentApplicationOptions.d.ts +38 -0
  15. package/dist/src/app/attachmentDownloader.js +2 -2
  16. package/dist/src/app/attachmentDownloader.js.map +1 -1
  17. package/dist/src/app/auth/authorization.js +12 -9
  18. package/dist/src/app/auth/authorization.js.map +1 -1
  19. package/dist/src/app/auth/authorizationManager.d.ts +18 -5
  20. package/dist/src/app/auth/authorizationManager.js +258 -45
  21. package/dist/src/app/auth/authorizationManager.js.map +1 -1
  22. package/dist/src/app/auth/handlerStorage.js +3 -1
  23. package/dist/src/app/auth/handlerStorage.js.map +1 -1
  24. package/dist/src/app/auth/handlers/agenticAuthorization.d.ts +19 -16
  25. package/dist/src/app/auth/handlers/agenticAuthorization.js +46 -52
  26. package/dist/src/app/auth/handlers/agenticAuthorization.js.map +1 -1
  27. package/dist/src/app/auth/handlers/azureBotAuthorization.d.ts +51 -75
  28. package/dist/src/app/auth/handlers/azureBotAuthorization.js +217 -192
  29. package/dist/src/app/auth/handlers/azureBotAuthorization.js.map +1 -1
  30. package/dist/src/app/auth/types.d.ts +100 -1
  31. package/dist/src/app/auth/utils.d.ts +10 -0
  32. package/dist/src/app/auth/utils.js +21 -0
  33. package/dist/src/app/auth/utils.js.map +1 -0
  34. package/dist/src/app/index.d.ts +1 -0
  35. package/dist/src/app/index.js +1 -0
  36. package/dist/src/app/index.js.map +1 -1
  37. package/dist/src/app/proactive/conversation.d.ts +43 -0
  38. package/dist/src/app/proactive/conversation.js +67 -0
  39. package/dist/src/app/proactive/conversation.js.map +1 -0
  40. package/dist/src/app/proactive/conversationBuilder.d.ts +54 -0
  41. package/dist/src/app/proactive/conversationBuilder.js +110 -0
  42. package/dist/src/app/proactive/conversationBuilder.js.map +1 -0
  43. package/dist/src/app/proactive/conversationReferenceBuilder.d.ts +68 -0
  44. package/dist/src/app/proactive/conversationReferenceBuilder.js +125 -0
  45. package/dist/src/app/proactive/conversationReferenceBuilder.js.map +1 -0
  46. package/dist/src/app/proactive/createConversationOptions.d.ts +30 -0
  47. package/dist/src/app/proactive/createConversationOptions.js +10 -0
  48. package/dist/src/app/proactive/createConversationOptions.js.map +1 -0
  49. package/dist/src/app/proactive/createConversationOptionsBuilder.d.ts +69 -0
  50. package/dist/src/app/proactive/createConversationOptionsBuilder.js +141 -0
  51. package/dist/src/app/proactive/createConversationOptionsBuilder.js.map +1 -0
  52. package/dist/src/app/proactive/index.d.ts +7 -0
  53. package/dist/src/app/proactive/index.js +26 -0
  54. package/dist/src/app/proactive/index.js.map +1 -0
  55. package/dist/src/app/proactive/proactive.d.ts +248 -0
  56. package/dist/src/app/proactive/proactive.js +310 -0
  57. package/dist/src/app/proactive/proactive.js.map +1 -0
  58. package/dist/src/app/proactive/proactiveOptions.d.ts +19 -0
  59. package/dist/src/app/proactive/proactiveOptions.js +5 -0
  60. package/dist/src/app/proactive/proactiveOptions.js.map +1 -0
  61. package/dist/src/app/streaming/streamingResponse.js +2 -2
  62. package/dist/src/app/streaming/streamingResponse.js.map +1 -1
  63. package/dist/src/app/teamsAttachmentDownloader.js +2 -2
  64. package/dist/src/app/teamsAttachmentDownloader.js.map +1 -1
  65. package/dist/src/app/turnState.js +2 -2
  66. package/dist/src/app/turnState.js.map +1 -1
  67. package/dist/src/auth/authConfiguration.d.ts +61 -0
  68. package/dist/src/auth/authConfiguration.js +52 -3
  69. package/dist/src/auth/authConfiguration.js.map +1 -1
  70. package/dist/src/auth/jwt-middleware.js +2 -2
  71. package/dist/src/auth/jwt-middleware.js.map +1 -1
  72. package/dist/src/auth/msalConnectionManager.js +20 -0
  73. package/dist/src/auth/msalConnectionManager.js.map +1 -1
  74. package/dist/src/auth/msalTokenCredential.js +3 -0
  75. package/dist/src/auth/msalTokenCredential.js.map +1 -1
  76. package/dist/src/auth/msalTokenProvider.js +136 -110
  77. package/dist/src/auth/msalTokenProvider.js.map +1 -1
  78. package/dist/src/baseAdapter.js +2 -2
  79. package/dist/src/baseAdapter.js.map +1 -1
  80. package/dist/src/cloudAdapter.js +201 -154
  81. package/dist/src/cloudAdapter.js.map +1 -1
  82. package/dist/src/connector-client/connectorClient.js +176 -127
  83. package/dist/src/connector-client/connectorClient.js.map +1 -1
  84. package/dist/src/errorHelper.js +108 -0
  85. package/dist/src/errorHelper.js.map +1 -1
  86. package/dist/src/middlewareSet.js +2 -2
  87. package/dist/src/middlewareSet.js.map +1 -1
  88. package/dist/src/oauth/userTokenClient.js +78 -48
  89. package/dist/src/oauth/userTokenClient.js.map +1 -1
  90. package/dist/src/observability/index.d.ts +2 -0
  91. package/dist/src/observability/index.js +21 -0
  92. package/dist/src/observability/index.js.map +1 -0
  93. package/dist/src/observability/metrics.d.ts +21 -0
  94. package/dist/src/observability/metrics.js +87 -0
  95. package/dist/src/observability/metrics.js.map +1 -0
  96. package/dist/src/observability/traces.d.ts +234 -0
  97. package/dist/src/observability/traces.js +962 -0
  98. package/dist/src/observability/traces.js.map +1 -0
  99. package/dist/src/state/agentState.js +2 -2
  100. package/dist/src/state/agentState.js.map +1 -1
  101. package/dist/src/storage/fileStorage.js +38 -28
  102. package/dist/src/storage/fileStorage.js.map +1 -1
  103. package/dist/src/storage/memoryStorage.js +41 -30
  104. package/dist/src/storage/memoryStorage.js.map +1 -1
  105. package/dist/src/transcript/fileTranscriptLogger.js +2 -2
  106. package/dist/src/transcript/fileTranscriptLogger.js.map +1 -1
  107. package/dist/src/transcript/transcriptLoggerMiddleware.js +2 -2
  108. package/dist/src/transcript/transcriptLoggerMiddleware.js.map +1 -1
  109. package/dist/src/turnContext.js +48 -42
  110. package/dist/src/turnContext.js.map +1 -1
  111. package/package.json +10 -9
  112. package/src/activityHandler.ts +1 -1
  113. package/src/agent-client/agentClient.ts +53 -42
  114. package/src/agent-client/agentResponseHandler.ts +1 -1
  115. package/src/app/agentApplication.ts +212 -86
  116. package/src/app/agentApplicationBuilder.ts +26 -4
  117. package/src/app/agentApplicationOptions.ts +43 -0
  118. package/src/app/attachmentDownloader.ts +1 -1
  119. package/src/app/auth/authorization.ts +11 -8
  120. package/src/app/auth/authorizationManager.ts +297 -45
  121. package/src/app/auth/handlerStorage.ts +3 -1
  122. package/src/app/auth/handlers/agenticAuthorization.ts +68 -72
  123. package/src/app/auth/handlers/azureBotAuthorization.ts +260 -264
  124. package/src/app/auth/types.ts +102 -1
  125. package/src/app/auth/utils.ts +22 -0
  126. package/src/app/index.ts +1 -0
  127. package/src/app/proactive/conversation.ts +87 -0
  128. package/src/app/proactive/conversationBuilder.ts +139 -0
  129. package/src/app/proactive/conversationReferenceBuilder.ts +161 -0
  130. package/src/app/proactive/createConversationOptions.ts +35 -0
  131. package/src/app/proactive/createConversationOptionsBuilder.ts +181 -0
  132. package/src/app/proactive/index.ts +10 -0
  133. package/src/app/proactive/proactive.ts +524 -0
  134. package/src/app/proactive/proactiveOptions.ts +24 -0
  135. package/src/app/streaming/streamingResponse.ts +1 -1
  136. package/src/app/teamsAttachmentDownloader.ts +1 -1
  137. package/src/app/turnState.ts +1 -1
  138. package/src/auth/authConfiguration.ts +58 -1
  139. package/src/auth/jwt-middleware.ts +1 -1
  140. package/src/auth/msalConnectionManager.ts +22 -0
  141. package/src/auth/msalTokenCredential.ts +4 -0
  142. package/src/auth/msalTokenProvider.ts +138 -107
  143. package/src/baseAdapter.ts +1 -1
  144. package/src/cloudAdapter.ts +239 -184
  145. package/src/connector-client/connectorClient.ts +169 -126
  146. package/src/errorHelper.ts +124 -0
  147. package/src/middlewareSet.ts +1 -1
  148. package/src/oauth/userTokenClient.ts +70 -46
  149. package/src/observability/index.ts +5 -0
  150. package/src/observability/metrics.ts +103 -0
  151. package/src/observability/traces.ts +988 -0
  152. package/src/state/agentState.ts +1 -1
  153. package/src/storage/fileStorage.ts +36 -26
  154. package/src/storage/memoryStorage.ts +40 -29
  155. package/src/transcript/fileTranscriptLogger.ts +1 -1
  156. package/src/transcript/transcriptLoggerMiddleware.ts +1 -1
  157. package/src/turnContext.ts +47 -41
@@ -0,0 +1,524 @@
1
+ // Copyright (c) Microsoft Corporation. All rights reserved.
2
+ // Licensed under the MIT License.
3
+
4
+ import type { Activity } from '@microsoft/agents-activity'
5
+ import type { ResourceResponse } from '../../connector-client'
6
+ import type { BaseAdapter } from '../../baseAdapter'
7
+ import type { TurnContext } from '../../turnContext'
8
+ import type { TurnState } from '../turnState'
9
+ import type { RouteHandler } from '../routeHandler'
10
+ import type { Storage } from '../../storage/storage'
11
+ import type { AgentApplication } from '../agentApplication'
12
+ import type { ProactiveOptions } from './proactiveOptions'
13
+ import type { CreateConversationOptions } from './createConversationOptions'
14
+ import { ExceptionHelper } from '@microsoft/agents-activity'
15
+ import { Conversation } from './conversation'
16
+ import { debug, trace } from '@microsoft/agents-telemetry'
17
+ import { Errors } from '../../errorHelper'
18
+ import { ProactiveTraceDefinitions } from '../../observability/traces'
19
+
20
+ const logger = debug('agents:proactive')
21
+ const STORAGE_KEY_PREFIX = 'proactive/conversations/'
22
+
23
+ /**
24
+ * Provides methods for storing, retrieving, and managing conversation references to enable
25
+ * proactive messaging scenarios. Supports sending activities and continuing conversations outside
26
+ * the standard request/response flow using stored conversation references.
27
+ *
28
+ * @remarks
29
+ * Use the `Proactive` class to implement scenarios where an agent needs to initiate conversations
30
+ * or send messages to users without an incoming activity, such as notifications or scheduled alerts.
31
+ * Some operations require that conversation references be stored using {@link storeConversation}
32
+ * before they can be used.
33
+ *
34
+ * Access via `app.proactive` after configuring `proactive` in {@link AgentApplicationOptions}.
35
+ */
36
+ export class Proactive<TState extends TurnState> {
37
+ /**
38
+ * `activity.valueType` that indicates additional key/values for the ContinueConversation event.
39
+ */
40
+ static readonly ContinueConversationValueType = 'application/vnd.microsoft.activity.continueconversation+json'
41
+
42
+ private readonly _app: AgentApplication<TState>
43
+ private readonly _options: ProactiveOptions
44
+ private readonly _storage?: Storage
45
+
46
+ constructor (app: AgentApplication<TState>, options: ProactiveOptions) {
47
+ this._app = app
48
+ this._options = options
49
+ this._storage = options.storage
50
+ }
51
+
52
+ private requireStorage (): Storage {
53
+ if (!this._storage) {
54
+ throw ExceptionHelper.generateException(Error, Errors.ProactiveStorageRequired)
55
+ }
56
+ return this._storage
57
+ }
58
+
59
+ private requireAppStorage (): Storage {
60
+ const storage = this._app.options.storage
61
+ if (!storage) {
62
+ throw ExceptionHelper.generateException(Error, Errors.ProactiveAppStorageRequired)
63
+ }
64
+ return storage
65
+ }
66
+
67
+ // ---------------------------------------------------------------------------
68
+ // Conversation reference storage
69
+ // ---------------------------------------------------------------------------
70
+
71
+ /**
72
+ * Stores the current conversation reference from a live {@link TurnContext} in proactive storage.
73
+ *
74
+ * @param context - The context object for the current turn, containing activity and conversation
75
+ * information.
76
+ * @returns The conversation ID that can be used to retrieve the reference in future operations.
77
+ * @example
78
+ * ```typescript
79
+ * // Inside an onMessage handler — save the conversation so we can message later
80
+ * app.onActivity('message', async (ctx, state) => {
81
+ * const convId = await app.proactive.storeConversation(ctx)
82
+ * await ctx.sendActivity(`Conversation stored. ID: ${convId}`)
83
+ * })
84
+ * ```
85
+ */
86
+ storeConversation (context: TurnContext): Promise<string>
87
+ /**
88
+ * Stores an explicit {@link Conversation} object in proactive storage.
89
+ *
90
+ * @param conversation - The conversation reference record to store.
91
+ * @returns The conversation ID that can be used to retrieve the reference in future operations.
92
+ * @throws If the conversation fails validation (missing `conversation.id`, `serviceUrl`, or
93
+ * `claims.aud`).
94
+ * @example
95
+ * ```typescript
96
+ * // Build a Conversation manually and store it
97
+ * const conv = ConversationBuilder
98
+ * .create('my-app-id', 'msteams')
99
+ * .withUser('user-aad-id')
100
+ * .withConversationId('19:existing-thread-id@thread.tacv2')
101
+ * .build()
102
+ * const convId = await app.proactive.storeConversation(conv)
103
+ * ```
104
+ */
105
+ storeConversation (conversation: Conversation): Promise<string>
106
+ async storeConversation (contextOrConversation: TurnContext | Conversation): Promise<string> {
107
+ return trace(ProactiveTraceDefinitions.storeConversation, async ({ record }) => {
108
+ const conv =
109
+ contextOrConversation instanceof Conversation
110
+ ? contextOrConversation
111
+ : new Conversation(contextOrConversation as TurnContext)
112
+
113
+ conv.validate()
114
+ const id = conv.reference.conversation.id
115
+ record({ conversationId: id })
116
+ await this.requireStorage().write({ [`${STORAGE_KEY_PREFIX}${id}`]: { reference: conv.reference, claims: conv.claims } })
117
+ return id
118
+ })
119
+ }
120
+
121
+ /**
122
+ * Retrieves the stored {@link Conversation} associated with the given conversation ID.
123
+ *
124
+ * @param conversationId - The unique identifier of the conversation to retrieve.
125
+ * @returns The stored `Conversation`, or `undefined` if no record exists for that ID.
126
+ * @example
127
+ * ```typescript
128
+ * const conv = await app.proactive.getConversation(convId)
129
+ * if (conv) {
130
+ * await app.proactive.sendActivity(adapter, conv, { text: 'Hello!' })
131
+ * }
132
+ * ```
133
+ */
134
+ async getConversation (conversationId: string): Promise<Conversation | undefined> {
135
+ return trace(ProactiveTraceDefinitions.getConversation, async ({ record }) => {
136
+ record({ conversationId })
137
+ const result = await this.requireStorage().read([`${STORAGE_KEY_PREFIX}${conversationId}`])
138
+ const stored = result[`${STORAGE_KEY_PREFIX}${conversationId}`] as { reference: any; claims: any } | undefined
139
+ if (!stored) {
140
+ record({ found: false })
141
+ return undefined
142
+ }
143
+ record({ found: true })
144
+ return new Conversation(stored.claims, stored.reference)
145
+ })
146
+ }
147
+
148
+ /**
149
+ * Retrieves the stored {@link Conversation} for the given ID, throwing if no record is found.
150
+ *
151
+ * @param conversationId - The unique identifier of the conversation to retrieve.
152
+ * @returns The stored `Conversation`.
153
+ * @throws `Error` if no conversation reference is found for the specified ID.
154
+ * @example
155
+ * ```typescript
156
+ * // Use when absence of the conversation should be treated as an error
157
+ * const conv = await app.proactive.getConversationOrThrow(convId)
158
+ * await app.proactive.sendActivity(adapter, conv, { text: 'Alert: your report is ready.' })
159
+ * ```
160
+ */
161
+ async getConversationOrThrow (conversationId: string): Promise<Conversation> {
162
+ return trace(ProactiveTraceDefinitions.getConversationOrThrow, async ({ record }) => {
163
+ record({ conversationId })
164
+ const conv = await this.getConversation(conversationId)
165
+ if (!conv) {
166
+ throw ExceptionHelper.generateException(Error, Errors.ProactiveConversationNotFound, undefined, { conversationId })
167
+ }
168
+ return conv
169
+ })
170
+ }
171
+
172
+ /**
173
+ * Deletes the stored conversation reference for the given conversation ID.
174
+ *
175
+ * @param conversationId - The unique identifier of the conversation whose reference should be
176
+ * deleted.
177
+ * @remarks If no record exists for the given ID, no action is taken.
178
+ * @example
179
+ * ```typescript
180
+ * // Clean up after a conversation has ended
181
+ * app.onActivity('endOfConversation', async (ctx, state) => {
182
+ * await app.proactive.deleteConversation(ctx.activity.conversation.id)
183
+ * })
184
+ * ```
185
+ */
186
+ async deleteConversation (conversationId: string): Promise<void> {
187
+ return trace(ProactiveTraceDefinitions.deleteConversation, async ({ record }) => {
188
+ record({ conversationId })
189
+ await this.requireStorage().delete([`${STORAGE_KEY_PREFIX}${conversationId}`])
190
+ })
191
+ }
192
+
193
+ // ---------------------------------------------------------------------------
194
+ // Send activity
195
+ // ---------------------------------------------------------------------------
196
+
197
+ /**
198
+ * Sends an activity to a stored conversation, looking it up by ID.
199
+ *
200
+ * @param adapter - The channel adapter used to send the activity.
201
+ * @param conversationId - The ID of a conversation previously stored via {@link storeConversation}.
202
+ * @param activity - The activity to send. If `type` is not set it defaults to `'message'`.
203
+ * @returns A {@link ResourceResponse} with the ID of the sent activity.
204
+ * @throws `Error` if no conversation reference is found for the specified ID.
205
+ * @example
206
+ * ```typescript
207
+ * // Send a notification using a previously stored conversation ID
208
+ * await app.proactive.sendActivity(adapter, storedConvId, { text: 'Your order has shipped!' })
209
+ * ```
210
+ */
211
+ sendActivity (adapter: BaseAdapter, conversationId: string, activity: Partial<Activity>): Promise<ResourceResponse>
212
+ /**
213
+ * Sends an activity to an existing conversation using the provided {@link Conversation} reference.
214
+ *
215
+ * @param adapter - The channel adapter used to send the activity.
216
+ * @param conversation - A `Conversation` instance created via its constructor or
217
+ * {@link ConversationBuilder}.
218
+ * @param activity - The activity to send. If `type` is not set it defaults to `'message'`.
219
+ * @returns A {@link ResourceResponse} with the ID of the sent activity.
220
+ * @example
221
+ * ```typescript
222
+ * // Build a Conversation from a stored reference and send a message
223
+ * const conv = await app.proactive.getConversationOrThrow(convId)
224
+ * const response = await app.proactive.sendActivity(adapter, conv, { text: 'Hello from the agent!' })
225
+ * console.log('Sent activity ID:', response.id)
226
+ * ```
227
+ */
228
+ sendActivity (adapter: BaseAdapter, conversation: Conversation, activity: Partial<Activity>): Promise<ResourceResponse>
229
+ async sendActivity (
230
+ adapter: BaseAdapter,
231
+ conversationOrId: Conversation | string,
232
+ activity: Partial<Activity>
233
+ ): Promise<ResourceResponse> {
234
+ return trace(ProactiveTraceDefinitions.sendActivity, async ({ record }) => {
235
+ const conv =
236
+ typeof conversationOrId === 'string'
237
+ ? await this.getConversationOrThrow(conversationOrId)
238
+ : conversationOrId
239
+
240
+ const activityToSend: Partial<Activity> = { type: 'message', ...activity }
241
+
242
+ record({
243
+ conversationId: conv.reference.conversation.id,
244
+ channelId: conv.reference.channelId,
245
+ activityType: activityToSend.type ?? 'message',
246
+ })
247
+
248
+ logger.info('sendActivity: conversation=%s channel=%s serviceUrl=%s',
249
+ conv.reference.conversation.id, conv.reference.channelId, conv.reference.serviceUrl)
250
+
251
+ let response: ResourceResponse | undefined
252
+ let caughtError: unknown
253
+
254
+ await adapter.continueConversation(conv.identity, conv.reference, async (ctx: TurnContext) => {
255
+ try {
256
+ const result = await ctx.sendActivity(activityToSend as Activity)
257
+ response = result as ResourceResponse
258
+ } catch (err) {
259
+ caughtError = err
260
+ }
261
+ })
262
+
263
+ if (caughtError !== undefined) {
264
+ logger.warn('sendActivity: failed for conversation=%s: %s', conv.reference.conversation.id, caughtError)
265
+ throw caughtError
266
+ }
267
+ if (response === undefined) throw ExceptionHelper.generateException(Error, Errors.ProactiveSendActivityNoResponse)
268
+ logger.debug('sendActivity: sent activity id=%s', response.id)
269
+ return response
270
+ })
271
+ }
272
+
273
+ // ---------------------------------------------------------------------------
274
+ // Full-turn handler (loads TurnState, handles auth tokens)
275
+ // ---------------------------------------------------------------------------
276
+
277
+ /**
278
+ * Continues a stored conversation by executing the given handler within the context of that
279
+ * conversation, looking it up by ID.
280
+ *
281
+ * See the {@link Conversation} overload for full details.
282
+ *
283
+ * @param adapter - The channel adapter used to continue the conversation.
284
+ * @param conversationId - The ID of a conversation previously stored via {@link storeConversation}.
285
+ * @param handler - The route handler to execute within the continued conversation context.
286
+ * @param autoSignInHandlers - Optional list of OAuth connection names whose tokens should be
287
+ * acquired before invoking the handler.
288
+ * @param continuationActivity - Optional activity fields merged into the continuation activity,
289
+ * making them available on `ctx.activity` inside the handler (e.g. `value`, `valueType`).
290
+ * @throws `Error` if no conversation reference is found for the specified ID.
291
+ * @example
292
+ * ```typescript
293
+ * // Scheduled job: send a daily digest to all stored conversations
294
+ * for (const convId of storedIds) {
295
+ * await app.proactive.continueConversation(adapter, convId, async (ctx, state) => {
296
+ * await ctx.sendActivity('Here is your daily digest...')
297
+ * })
298
+ * }
299
+ * ```
300
+ */
301
+ continueConversation (adapter: BaseAdapter, conversationId: string, handler: RouteHandler<TState>, autoSignInHandlers?: string[], continuationActivity?: Partial<Activity>): Promise<void>
302
+ /**
303
+ * Continues an existing conversation by executing the given handler within the context of the
304
+ * provided {@link Conversation} reference. The handler receives a {@link TurnContext} and a
305
+ * freshly loaded {@link TurnState} scoped to the original conversation, enabling the agent to
306
+ * respond as if replying to an incoming activity.
307
+ *
308
+ * @remarks
309
+ * Exceptions thrown inside the handler are captured and re-thrown after the adapter callback
310
+ * completes, since the adapter would otherwise silently swallow them.
311
+ *
312
+ * If `autoSignInHandlers` are supplied and the application has user authorization configured,
313
+ * tokens are acquired before the handler is called. If not all tokens are available and
314
+ * `proactiveOptions.failOnUnsignedInConnections` is not `false`, an error is thrown.
315
+ *
316
+ * @param adapter - The channel adapter used to continue the conversation.
317
+ * @param conversation - A `Conversation` instance created via its constructor or
318
+ * {@link ConversationBuilder}.
319
+ * @param handler - The route handler to execute within the continued conversation context.
320
+ * @param autoSignInHandlers - Optional list of OAuth connection names whose tokens should be
321
+ * acquired before invoking the handler.
322
+ * @param continuationActivity - Optional activity fields merged into the continuation activity,
323
+ * making them available on `ctx.activity` inside the handler (e.g. `value`, `valueType`).
324
+ * @example
325
+ * ```typescript
326
+ * // Continue a conversation with a custom value payload
327
+ * const conv = await app.proactive.getConversationOrThrow(convId)
328
+ * await app.proactive.continueConversation(
329
+ * adapter,
330
+ * conv,
331
+ * async (ctx, state) => {
332
+ * const payload = ctx.activity.value as { alertType: string }
333
+ * await ctx.sendActivity(`Alert triggered: ${payload.alertType}`)
334
+ * },
335
+ * undefined,
336
+ * { value: { alertType: 'threshold-exceeded' }, valueType: Proactive.ContinueConversationValueType }
337
+ * )
338
+ * ```
339
+ */
340
+ continueConversation (adapter: BaseAdapter, conversation: Conversation, handler: RouteHandler<TState>, autoSignInHandlers?: string[], continuationActivity?: Partial<Activity>): Promise<void>
341
+ async continueConversation (
342
+ adapter: BaseAdapter,
343
+ conversationOrId: Conversation | string,
344
+ handler: RouteHandler<TState>,
345
+ autoSignInHandlers?: string[],
346
+ continuationActivity?: Partial<Activity>
347
+ ): Promise<void> {
348
+ return trace(ProactiveTraceDefinitions.continueConversation, async ({ record }) => {
349
+ const conv =
350
+ typeof conversationOrId === 'string'
351
+ ? await this.getConversationOrThrow(conversationOrId)
352
+ : conversationOrId
353
+
354
+ record({
355
+ conversationId: conv.reference.conversation.id,
356
+ channelId: conv.reference.channelId,
357
+ hasAutoSignIn: !!autoSignInHandlers?.length,
358
+ })
359
+
360
+ logger.info('continueConversation: conversation=%s channel=%s serviceUrl=%s',
361
+ conv.reference.conversation.id, conv.reference.channelId, conv.reference.serviceUrl)
362
+
363
+ let caughtError: unknown
364
+
365
+ await adapter.continueConversation(conv.identity, conv.reference, async (ctx: TurnContext) => {
366
+ try {
367
+ // Merge caller-supplied activity fields (e.g. value, valueType) into the
368
+ // continuation activity so the handler can read request-time parameters.
369
+ if (continuationActivity) {
370
+ Object.assign(ctx.activity, continuationActivity)
371
+ }
372
+
373
+ const state = this._app.options.turnStateFactory()
374
+ await state.load(ctx, this.requireAppStorage())
375
+
376
+ // Token acquisition (optional — only when auth is configured)
377
+ if (autoSignInHandlers?.length && this._app.hasUserAuthorization) {
378
+ logger.debug('continueConversation: acquiring tokens for handlers: %o', autoSignInHandlers)
379
+ const results = await Promise.all(
380
+ autoSignInHandlers.map((handlerId) =>
381
+ this._app.authorization.getToken(ctx, handlerId).catch(() => ({ token: undefined }))
382
+ )
383
+ )
384
+ const allAcquired = results.every((r) => !!r.token)
385
+ if (!allAcquired) {
386
+ logger.warn('continueConversation: not all tokens acquired for conversation=%s handlers=%o',
387
+ conv.reference.conversation.id, autoSignInHandlers)
388
+ if (this._options.failOnUnsignedInConnections !== false) {
389
+ throw ExceptionHelper.generateException(Error, Errors.ProactiveNotAllTokensAcquired)
390
+ }
391
+ }
392
+ }
393
+
394
+ await handler(ctx, state)
395
+ await state.save(ctx, this.requireAppStorage())
396
+ } catch (err) {
397
+ caughtError = err
398
+ } finally {
399
+ if ((ctx as any).streamingResponse?.isStreamStarted?.()) {
400
+ await (ctx as any).streamingResponse.endStream()
401
+ }
402
+ }
403
+ })
404
+
405
+ if (caughtError !== undefined) {
406
+ logger.warn('continueConversation: failed for conversation=%s: %s', conv.reference.conversation.id, caughtError)
407
+ throw caughtError
408
+ }
409
+ logger.debug('continueConversation: complete for conversation=%s', conv.reference.conversation.id)
410
+ })
411
+ }
412
+
413
+ // ---------------------------------------------------------------------------
414
+ // Create new conversation
415
+ // ---------------------------------------------------------------------------
416
+
417
+ /**
418
+ * Creates a new conversation using the specified channel adapter and conversation options.
419
+ *
420
+ * @remarks
421
+ * This wraps `CloudAdapter.createConversationAsync()`, which requires real network connectivity
422
+ * and authentication. The provided adapter must implement
423
+ * `createConversationAsync()`; a `TypeError` is thrown if it does not.
424
+ *
425
+ * If `createOptions.storeConversation` is `true`, the resulting {@link Conversation} is
426
+ * automatically stored via {@link storeConversation} so it can be retrieved by ID later.
427
+ *
428
+ * If a `handler` is provided it is executed within the newly created conversation, giving the
429
+ * agent a chance to send an initial message or load state.
430
+ *
431
+ * @param adapter - The channel adapter used to create the conversation. Must implement
432
+ * `createConversationAsync()` (i.e. a `CloudAdapter` instance).
433
+ * @param createOptions - Details required to create the conversation, including identity, channel,
434
+ * service URL, OAuth scope, and `ConversationParameters`. Build with
435
+ * {@link CreateConversationOptionsBuilder}.
436
+ * @param handler - Optional route handler executed immediately after the conversation is created.
437
+ * @returns The newly created {@link Conversation}.
438
+ * @throws `TypeError` if the adapter does not implement `createConversationAsync()`.
439
+ * @example
440
+ * ```typescript
441
+ * // Initiate a new 1:1 conversation with a Teams user and send a welcome message
442
+ * const opts = CreateConversationOptionsBuilder
443
+ * .create(process.env.APP_ID!, 'msteams')
444
+ * .withUser('user-aad-object-id')
445
+ * .withTenantId('tenant-id')
446
+ * .storeConversation(true)
447
+ * .build()
448
+ *
449
+ * const conv = await app.proactive.createConversation(adapter, opts, async (ctx, state) => {
450
+ * await ctx.sendActivity('Hi! I have an update for you.')
451
+ * })
452
+ * console.log('New conversation ID:', conv.reference.conversation.id)
453
+ * ```
454
+ */
455
+ async createConversation (
456
+ adapter: BaseAdapter,
457
+ createOptions: CreateConversationOptions,
458
+ handler?: RouteHandler<TState>
459
+ ): Promise<Conversation> {
460
+ return trace(ProactiveTraceDefinitions.createConversation, async ({ record }) => {
461
+ record({
462
+ channelId: createOptions.channelId,
463
+ storeConversation: !!createOptions.storeConversation,
464
+ hasHandler: !!handler,
465
+ })
466
+
467
+ if (!createOptions.parameters.members?.length) {
468
+ throw ExceptionHelper.generateException(Error, Errors.ProactiveMembersRequired)
469
+ }
470
+
471
+ record({ membersCount: createOptions.parameters.members.length })
472
+
473
+ // CloudAdapter.createConversationAsync(agentAppId, channelId, serviceUrl, audience, params, logic)
474
+ // The logic callback IS the handler — context is created internally by the adapter.
475
+ const cloudAdapter = adapter as any
476
+ if (typeof cloudAdapter.createConversationAsync !== 'function') {
477
+ throw ExceptionHelper.generateException(TypeError, Errors.ProactiveCloudAdapterRequired)
478
+ }
479
+ logger.info('createConversation: channel=%s serviceUrl=%s members=%d',
480
+ createOptions.channelId, createOptions.serviceUrl, createOptions.parameters.members?.length ?? 0)
481
+
482
+ let capturedConv: Conversation | undefined
483
+ let caughtError: unknown
484
+
485
+ await cloudAdapter.createConversationAsync(
486
+ createOptions.identity.aud,
487
+ createOptions.channelId,
488
+ createOptions.serviceUrl,
489
+ createOptions.scope,
490
+ createOptions.parameters,
491
+ async (ctx: TurnContext) => {
492
+ try {
493
+ const conv = new Conversation(createOptions.identity, ctx.activity.getConversationReference())
494
+ capturedConv = conv
495
+ logger.debug('createConversation: created conversation=%s', conv.reference.conversation.id)
496
+
497
+ if (createOptions.storeConversation) {
498
+ await this.storeConversation(conv)
499
+ }
500
+
501
+ if (handler) {
502
+ const state = this._app.options.turnStateFactory()
503
+ await state.load(ctx, this.requireAppStorage())
504
+ await handler(ctx, state)
505
+ await state.save(ctx, this.requireAppStorage())
506
+ }
507
+ } catch (err) {
508
+ caughtError = err
509
+ }
510
+ }
511
+ )
512
+
513
+ if (caughtError !== undefined) {
514
+ logger.warn('createConversation: failed for channel=%s: %s', createOptions.channelId, caughtError)
515
+ throw caughtError
516
+ }
517
+
518
+ if (!capturedConv) {
519
+ throw ExceptionHelper.generateException(Error, Errors.ProactiveCallbackNotInvoked)
520
+ }
521
+ return capturedConv
522
+ })
523
+ }
524
+ }
@@ -0,0 +1,24 @@
1
+ // Copyright (c) Microsoft Corporation. All rights reserved.
2
+ // Licensed under the MIT License.
3
+
4
+ import type { Storage } from '../../storage/storage'
5
+
6
+ /**
7
+ * Configuration for the proactive messaging subsystem.
8
+ */
9
+ export interface ProactiveOptions {
10
+ /**
11
+ * Storage backend for persisting conversation references.
12
+ *
13
+ * If omitted, falls back to `AgentApplicationOptions.storage`.
14
+ * A warning is logged when the fallback is used.
15
+ * Throws at initialization time if neither is configured.
16
+ */
17
+ storage?: Storage
18
+
19
+ /**
20
+ * When `true` (default), `continueConversation()` throws if any requested
21
+ * token handler's user has not previously signed in.
22
+ */
23
+ failOnUnsignedInConnections?: boolean
24
+ }
@@ -7,7 +7,7 @@ import { Activity, addAIToActivity, Attachment, Entity, ClientCitation, Sensitiv
7
7
  import { TurnContext } from '../../turnContext'
8
8
  import { Citation } from './citation'
9
9
  import { CitationUtil } from './citationUtil'
10
- import { debug } from '@microsoft/agents-activity/logger'
10
+ import { debug } from '@microsoft/agents-telemetry'
11
11
 
12
12
  const logger = debug('agents:streamingResponse')
13
13
 
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  import { Attachment, Channels } from '@microsoft/agents-activity'
7
- import { debug } from '@microsoft/agents-activity/logger'
7
+ import { debug } from '@microsoft/agents-telemetry'
8
8
  import { ConnectorClient } from '../connector-client'
9
9
  import { InputFile, InputFileDownloader } from './inputFileDownloader'
10
10
  import { TurnContext } from '../turnContext'
@@ -7,7 +7,7 @@ import { Storage, StoreItems } from '../storage'
7
7
  import { AppMemory } from './appMemory'
8
8
  import { TurnStateEntry } from './turnStateEntry'
9
9
  import { TurnContext } from '../turnContext'
10
- import { debug } from '@microsoft/agents-activity/logger'
10
+ import { debug } from '@microsoft/agents-telemetry'
11
11
 
12
12
  const logger = debug('agents:turnState')
13
13
 
@@ -3,7 +3,7 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { debug } from '@microsoft/agents-activity/logger'
6
+ import { debug } from '@microsoft/agents-telemetry'
7
7
  import { ConnectionMapItem } from './msalConnectionManager'
8
8
  import objectPath from 'object-path'
9
9
 
@@ -385,3 +385,60 @@ function getDefaultIssuers (tenantId: string, authority: string) : string[] {
385
385
  `${resolveAuthority(authority, t)}/v2.0`
386
386
  ]
387
387
  }
388
+
389
+ /**
390
+ * A type representing a parser settings object.
391
+ */
392
+ type ParserSettings<K extends string> = {
393
+ [key in K]: (value: string) => { key?: string, value?: any } | undefined
394
+ }
395
+
396
+ /**
397
+ * Creates an environment variable parser that maps the variable keys to parsing functions.
398
+ * @param settings An object where each key is an environment variable name and the value is a function
399
+ * that takes the variable value as input and returns an object with optional `key` and `value` properties.
400
+ * @remarks
401
+ * The `key` property in the returned object can be used to rename the environment variable key,
402
+ * while the `value` property contains the parsed value.
403
+ * @returns An object with a `parse` method that takes an environment variable key and value,
404
+ * and returns the parsed result.
405
+ */
406
+ export function envParser<K extends string> (settings: ParserSettings<K> & ThisType<ParserSettings<K>>) {
407
+ const keys = Object.keys(settings) as K[]
408
+ return {
409
+ /**
410
+ * Parses the given environment variable key and value using the provided settings.
411
+ * @param key The environment variable key.
412
+ * @param value The environment variable value.
413
+ * @returns The parsed result with optional renamed key and parsed value.
414
+ */
415
+ parse (key: K, value: string) {
416
+ const match = keys.find(k => k.toUpperCase() === key.toUpperCase())
417
+ if (!match) {
418
+ return {}
419
+ }
420
+
421
+ const result = settings[match](value)
422
+ return { key: result?.key ?? match, value: result?.value }
423
+ }
424
+ }
425
+ }
426
+
427
+ /**
428
+ * Utility functions for environment variable parsers.
429
+ */
430
+ export const envParserUtils = {
431
+ /**
432
+ * Bypass parser that returns the value as is.
433
+ * @param value The environment variable value.
434
+ * @returns An object with the original value.
435
+ */
436
+ bypass: (value: string) => ({ value }),
437
+ /**
438
+ * Redirects the parsing to another parser for a specific key.
439
+ * @param parser The target parser to redirect to.
440
+ * @param key The key to use in the target parser.
441
+ * @returns A function that takes the environment variable value and returns the parsed result from the target parser.
442
+ */
443
+ redirect: <Parser extends ReturnType<typeof envParser>>(parser: Parser, key: Parameters<Parser['parse']>[0]) => (value: string) => parser.parse(key, value)
444
+ }
@@ -8,7 +8,7 @@ import { Response, NextFunction } from 'express'
8
8
  import { Request } from './request'
9
9
  import jwksRsa, { JwksClient, SigningKey } from 'jwks-rsa'
10
10
  import jwt, { JwtHeader, JwtPayload, SignCallback, GetPublicKeyOrSecret } from 'jsonwebtoken'
11
- import { debug } from '@microsoft/agents-activity/logger'
11
+ import { debug } from '@microsoft/agents-telemetry'
12
12
 
13
13
  const logger = debug('agents:jwt-middleware')
14
14