@microsoft/agents-hosting 1.6.0-beta.8.g226709c215 → 1.6.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.
- package/dist/package.json +4 -9
- package/dist/src/agent-client/agentClient.js +2 -2
- package/dist/src/agent-client/agentClient.js.map +1 -1
- package/dist/src/agent-client/agentResponseHandler.js +2 -2
- package/dist/src/agent-client/agentResponseHandler.js.map +1 -1
- package/dist/src/app/agentApplication.js +28 -2
- package/dist/src/app/agentApplication.js.map +1 -1
- package/dist/src/app/agentApplicationOptions.d.ts +10 -0
- package/dist/src/app/attachmentDownloader.js +5 -10
- package/dist/src/app/attachmentDownloader.js.map +1 -1
- package/dist/src/app/auth/authorizationManager.d.ts +4 -0
- package/dist/src/app/auth/authorizationManager.js +94 -67
- package/dist/src/app/auth/authorizationManager.js.map +1 -1
- package/dist/src/app/auth/handlers/azureBotAuthorization.js +2 -2
- package/dist/src/app/auth/handlers/azureBotAuthorization.js.map +1 -1
- package/dist/src/app/proactive/createConversationOptionsBuilder.js +10 -6
- package/dist/src/app/proactive/createConversationOptionsBuilder.js.map +1 -1
- package/dist/src/app/proactive/proactive.d.ts +1 -0
- package/dist/src/app/proactive/proactive.js +30 -15
- package/dist/src/app/proactive/proactive.js.map +1 -1
- package/dist/src/app/teamsAttachmentDownloader.js +6 -8
- package/dist/src/app/teamsAttachmentDownloader.js.map +1 -1
- package/dist/src/auth/authConfiguration.d.ts +2 -143
- package/dist/src/auth/authConfiguration.js +301 -257
- package/dist/src/auth/authConfiguration.js.map +1 -1
- package/dist/src/auth/jwt-middleware.js +2 -1
- package/dist/src/auth/jwt-middleware.js.map +1 -1
- package/dist/src/auth/msalConnectionManager.js +27 -14
- package/dist/src/auth/msalConnectionManager.js.map +1 -1
- package/dist/src/auth/msalTokenProvider.d.ts +7 -0
- package/dist/src/auth/msalTokenProvider.js +188 -44
- package/dist/src/auth/msalTokenProvider.js.map +1 -1
- package/dist/src/auth/settings.d.ts +327 -0
- package/dist/src/auth/settings.js +158 -0
- package/dist/src/auth/settings.js.map +1 -0
- package/dist/src/cloudAdapter.d.ts +58 -1
- package/dist/src/cloudAdapter.js +232 -52
- package/dist/src/cloudAdapter.js.map +1 -1
- package/dist/src/connector-client/connectorClient.d.ts +12 -8
- package/dist/src/connector-client/connectorClient.js +99 -82
- package/dist/src/connector-client/connectorClient.js.map +1 -1
- package/dist/src/errorHelper.js +85 -7
- package/dist/src/errorHelper.js.map +1 -1
- package/dist/src/getProductInfo.d.ts +23 -0
- package/dist/src/getProductInfo.js +71 -1
- package/dist/src/getProductInfo.js.map +1 -1
- package/dist/src/headerPropagation.d.ts +17 -2
- package/dist/src/headerPropagation.js +46 -24
- package/dist/src/headerPropagation.js.map +1 -1
- package/dist/src/httpClient.d.ts +60 -0
- package/dist/src/httpClient.js +173 -0
- package/dist/src/httpClient.js.map +1 -0
- package/dist/src/index.d.ts +2 -1
- package/dist/src/index.js +7 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/oauth/userTokenClient.d.ts +5 -4
- package/dist/src/oauth/userTokenClient.js +97 -78
- package/dist/src/oauth/userTokenClient.js.map +1 -1
- package/dist/src/observability/traces.d.ts +11 -4
- package/dist/src/observability/traces.js +39 -7
- package/dist/src/observability/traces.js.map +1 -1
- package/dist/src/utils/env.d.ts +50 -0
- package/dist/src/utils/env.js +113 -0
- package/dist/src/utils/env.js.map +1 -0
- package/dist/src/utils.d.ts +10 -0
- package/dist/src/utils.js +18 -0
- package/dist/src/utils.js.map +1 -0
- package/package.json +4 -9
- package/src/agent-client/agentClient.ts +2 -2
- package/src/agent-client/agentResponseHandler.ts +2 -2
- package/src/app/agentApplication.ts +30 -3
- package/src/app/agentApplicationOptions.ts +11 -0
- package/src/app/attachmentDownloader.ts +6 -8
- package/src/app/auth/authorizationManager.ts +84 -41
- package/src/app/auth/handlers/azureBotAuthorization.ts +2 -2
- package/src/app/proactive/createConversationOptionsBuilder.ts +8 -4
- package/src/app/proactive/proactive.ts +39 -23
- package/src/app/teamsAttachmentDownloader.ts +7 -6
- package/src/auth/authConfiguration.ts +317 -356
- package/src/auth/jwt-middleware.ts +1 -1
- package/src/auth/msalConnectionManager.ts +27 -10
- package/src/auth/msalTokenProvider.ts +180 -42
- package/src/auth/settings.ts +323 -0
- package/src/cloudAdapter.ts +291 -12
- package/src/connector-client/connectorClient.ts +116 -94
- package/src/errorHelper.ts +96 -7
- package/src/getProductInfo.ts +79 -1
- package/src/headerPropagation.ts +55 -23
- package/src/httpClient.ts +223 -0
- package/src/index.ts +5 -1
- package/src/oauth/userTokenClient.ts +103 -84
- package/src/observability/traces.ts +39 -8
- package/src/utils/env.ts +105 -0
- package/src/utils.ts +14 -0
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
3
6
|
import { AuthConfiguration } from '../auth/authConfiguration'
|
|
4
7
|
import { AuthProvider } from '../auth/authProvider'
|
|
5
8
|
import { debug } from '@microsoft/agents-telemetry'
|
|
@@ -11,72 +14,47 @@ import { ResourceResponse } from './resourceResponse'
|
|
|
11
14
|
import { AttachmentInfo } from './attachmentInfo'
|
|
12
15
|
import { AttachmentData } from './attachmentData'
|
|
13
16
|
import { normalizeOutgoingActivity } from '../activityWireCompat'
|
|
14
|
-
import { getProductInfo } from '../getProductInfo'
|
|
17
|
+
import { applyUserAgentHeader, getProductInfo } from '../getProductInfo'
|
|
15
18
|
import { HeaderPropagation, HeaderPropagationCollection } from '../headerPropagation'
|
|
16
19
|
import { trace } from '@microsoft/agents-telemetry'
|
|
17
20
|
import { ConnectorClientTraceDefinitions } from '../observability'
|
|
21
|
+
import { parseIntEnv } from '../utils/env'
|
|
22
|
+
import { HttpClient, HttpRequestConfig, HttpResponse, HttpError } from '../httpClient'
|
|
18
23
|
|
|
19
24
|
const logger = debug('agents:connector-client')
|
|
20
25
|
|
|
26
|
+
function formatHttpErrorMessage (error: HttpError): string {
|
|
27
|
+
const responseData = error.response?.data
|
|
28
|
+
if (responseData === undefined) {
|
|
29
|
+
return error.message
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
const serializedResponseData = JSON.stringify(responseData)
|
|
34
|
+
return serializedResponseData === undefined ? error.message : `${error.message}: ${serializedResponseData}`
|
|
35
|
+
} catch {
|
|
36
|
+
return error.message
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
21
40
|
export { getProductInfo }
|
|
22
41
|
|
|
23
42
|
/**
|
|
24
43
|
* ConnectorClient is a client for interacting with the Microsoft Connector API.
|
|
25
44
|
*/
|
|
26
45
|
export class ConnectorClient {
|
|
27
|
-
protected readonly
|
|
46
|
+
protected readonly _httpClient: HttpClient
|
|
28
47
|
|
|
29
48
|
/**
|
|
30
49
|
* Private constructor for the ConnectorClient.
|
|
31
|
-
* @param
|
|
50
|
+
* @param httpClient - The HttpClient instance to use for HTTP requests.
|
|
32
51
|
*/
|
|
33
|
-
protected constructor (
|
|
34
|
-
this.
|
|
35
|
-
this._axiosInstance.interceptors.request.use((config) => {
|
|
36
|
-
const { method, url, data, headers, params } = config
|
|
37
|
-
// Clone headers and remove Authorization before logging
|
|
38
|
-
const { Authorization, authorization, ...headersToLog } = headers || {}
|
|
39
|
-
logger.debug('Request: ', {
|
|
40
|
-
host: this._axiosInstance.getUri(),
|
|
41
|
-
url,
|
|
42
|
-
data,
|
|
43
|
-
method,
|
|
44
|
-
params,
|
|
45
|
-
headers: headersToLog
|
|
46
|
-
})
|
|
47
|
-
return config
|
|
48
|
-
})
|
|
49
|
-
this._axiosInstance.interceptors.response.use(
|
|
50
|
-
(config) => {
|
|
51
|
-
const { status, statusText, config: requestConfig } = config
|
|
52
|
-
logger.debug('Response: ', {
|
|
53
|
-
status,
|
|
54
|
-
statusText,
|
|
55
|
-
host: this._axiosInstance.getUri(),
|
|
56
|
-
url: requestConfig?.url,
|
|
57
|
-
data: config.config.data,
|
|
58
|
-
method: requestConfig?.method,
|
|
59
|
-
})
|
|
60
|
-
return config
|
|
61
|
-
},
|
|
62
|
-
(error) => {
|
|
63
|
-
const { code, message, stack, response } = error
|
|
64
|
-
const errorDetails = {
|
|
65
|
-
code,
|
|
66
|
-
host: this._axiosInstance.getUri(),
|
|
67
|
-
url: error.config.url,
|
|
68
|
-
method: error.config.method,
|
|
69
|
-
data: error.config.data,
|
|
70
|
-
message: message + JSON.stringify(response?.data),
|
|
71
|
-
stack,
|
|
72
|
-
}
|
|
73
|
-
return Promise.reject(errorDetails)
|
|
74
|
-
}
|
|
75
|
-
)
|
|
52
|
+
protected constructor (httpClient: HttpClient) {
|
|
53
|
+
this._httpClient = httpClient
|
|
76
54
|
}
|
|
77
55
|
|
|
78
|
-
public get
|
|
79
|
-
return this.
|
|
56
|
+
public get httpClient (): HttpClient {
|
|
57
|
+
return this._httpClient
|
|
80
58
|
}
|
|
81
59
|
|
|
82
60
|
/**
|
|
@@ -112,28 +90,23 @@ export class ConnectorClient {
|
|
|
112
90
|
headers?: HeaderPropagationCollection
|
|
113
91
|
): ConnectorClient {
|
|
114
92
|
const headerPropagation = headers ?? new HeaderPropagation({})
|
|
115
|
-
|
|
116
|
-
const productInfo = getProductInfo()
|
|
117
|
-
if (!userAgent) {
|
|
118
|
-
headerPropagation.add({ 'User-Agent': productInfo })
|
|
119
|
-
} else if (!userAgent.includes(productInfo)) {
|
|
120
|
-
headerPropagation.concat({ 'User-Agent': productInfo })
|
|
121
|
-
}
|
|
93
|
+
applyUserAgentHeader(headerPropagation)
|
|
122
94
|
headerPropagation.override({
|
|
123
95
|
Accept: 'application/json',
|
|
124
|
-
'Content-Type': 'application/json',
|
|
125
|
-
})
|
|
126
|
-
|
|
127
|
-
const axiosInstance = axios.create({
|
|
128
|
-
baseURL,
|
|
129
|
-
headers: headerPropagation.outgoing,
|
|
96
|
+
'Content-Type': 'application/json',
|
|
130
97
|
})
|
|
131
98
|
|
|
99
|
+
const clientHeaders: Record<string, string> = { ...headerPropagation.outgoing }
|
|
132
100
|
if (token && token.length > 1) {
|
|
133
|
-
|
|
101
|
+
clientHeaders.Authorization = `Bearer ${token}`
|
|
134
102
|
}
|
|
135
103
|
|
|
136
|
-
|
|
104
|
+
const httpClient = new HttpClient({
|
|
105
|
+
baseURL,
|
|
106
|
+
headers: clientHeaders,
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
return new ConnectorClient(httpClient)
|
|
137
110
|
}
|
|
138
111
|
|
|
139
112
|
/**
|
|
@@ -143,12 +116,12 @@ export class ConnectorClient {
|
|
|
143
116
|
*/
|
|
144
117
|
public async getConversations (continuationToken?: string): Promise<ConversationsResult> {
|
|
145
118
|
return trace(ConnectorClientTraceDefinitions.getConversations, async ({ record }) => {
|
|
146
|
-
const config:
|
|
119
|
+
const config: HttpRequestConfig = {
|
|
147
120
|
method: 'get',
|
|
148
121
|
url: '/v3/conversations',
|
|
149
122
|
params: continuationToken ? { continuationToken } : undefined
|
|
150
123
|
}
|
|
151
|
-
const response = await this.
|
|
124
|
+
const response = await this.executeRequest<ConversationsResult>(config)
|
|
152
125
|
record({ httpStatusCode: response.status?.toString() })
|
|
153
126
|
return response.data
|
|
154
127
|
})
|
|
@@ -159,14 +132,14 @@ export class ConnectorClient {
|
|
|
159
132
|
if (!userId || !conversationId) {
|
|
160
133
|
throw ExceptionHelper.generateException(Error, Errors.UserIdAndConversationIdRequired)
|
|
161
134
|
}
|
|
162
|
-
const config:
|
|
135
|
+
const config: HttpRequestConfig = {
|
|
163
136
|
method: 'get',
|
|
164
137
|
url: `v3/conversations/${conversationId}/members/${userId}`,
|
|
165
138
|
headers: {
|
|
166
139
|
'Content-Type': 'application/json'
|
|
167
140
|
}
|
|
168
141
|
}
|
|
169
|
-
const response = await this.
|
|
142
|
+
const response = await this.executeRequest<ChannelAccount>(config)
|
|
170
143
|
record({ httpStatusCode: response.status?.toString() })
|
|
171
144
|
return response.data
|
|
172
145
|
})
|
|
@@ -183,7 +156,7 @@ export class ConnectorClient {
|
|
|
183
156
|
...body,
|
|
184
157
|
activity: normalizeOutgoingActivity(body.activity)
|
|
185
158
|
}
|
|
186
|
-
const config:
|
|
159
|
+
const config: HttpRequestConfig = {
|
|
187
160
|
method: 'post',
|
|
188
161
|
url: '/v3/conversations',
|
|
189
162
|
headers: {
|
|
@@ -191,7 +164,7 @@ export class ConnectorClient {
|
|
|
191
164
|
},
|
|
192
165
|
data: payload
|
|
193
166
|
}
|
|
194
|
-
const response
|
|
167
|
+
const response = await this.executeRequest<ConversationResourceResponse>(config)
|
|
195
168
|
record({ httpStatusCode: response.status?.toString() })
|
|
196
169
|
return response.data
|
|
197
170
|
})
|
|
@@ -219,7 +192,7 @@ export class ConnectorClient {
|
|
|
219
192
|
|
|
220
193
|
const trimmedConversationId: string = this.conditionallyTruncateConversationId(conversationId, body)
|
|
221
194
|
|
|
222
|
-
const config:
|
|
195
|
+
const config: HttpRequestConfig = {
|
|
223
196
|
method: 'post',
|
|
224
197
|
url: `v3/conversations/${trimmedConversationId}/activities/${encodeURIComponent(activityId)}`,
|
|
225
198
|
headers: {
|
|
@@ -228,7 +201,7 @@ export class ConnectorClient {
|
|
|
228
201
|
data: normalizeOutgoingActivity(body),
|
|
229
202
|
...(body.channelId === Channels.Msteams && body.isTargetedActivity() ? { params: { isTargetedActivity: 'true' } } : {})
|
|
230
203
|
}
|
|
231
|
-
const response = await this.
|
|
204
|
+
const response = await this.executeRequest<ResourceResponse>(config)
|
|
232
205
|
record({ httpStatusCode: response.status?.toString() })
|
|
233
206
|
logger.info('Reply to conversation/activity: ', response.data.id!, activityId)
|
|
234
207
|
return response.data
|
|
@@ -236,20 +209,19 @@ export class ConnectorClient {
|
|
|
236
209
|
}
|
|
237
210
|
|
|
238
211
|
/**
|
|
239
|
-
* Trim the conversationId
|
|
212
|
+
* Trim and sanitize the conversationId when creating the URL for agentic calls in agents channels.
|
|
240
213
|
* @param conversationId The ID of the conversation to potentially truncate.
|
|
241
214
|
* @param activity The activity object used to determine if truncation is necessary.
|
|
242
|
-
* @returns The original
|
|
215
|
+
* @returns The original conversationId for non-agents channels/non-agentic roles, otherwise a trimmed and sanitized value.
|
|
243
216
|
*/
|
|
244
217
|
private conditionallyTruncateConversationId (conversationId: string, activity: Activity): string {
|
|
245
218
|
if (
|
|
246
|
-
|
|
219
|
+
activity.channelIdChannel === Channels.Agents &&
|
|
247
220
|
(activity.from?.role === RoleTypes.AgenticIdentity || activity.from?.role === RoleTypes.AgenticUser)) {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
return conversationId.length > maxLength ? conversationId.substring(0, maxLength) : conversationId
|
|
221
|
+
const maxLength = parseIntEnv(process.env.MAX_APX_CONVERSATION_ID_LENGTH, 150)
|
|
222
|
+
const trimmedConversationId = conversationId.length > maxLength ? conversationId.substring(0, maxLength) : conversationId
|
|
223
|
+
|
|
224
|
+
return trimmedConversationId.replace(/[/\\#?]/g, '_')
|
|
253
225
|
} else {
|
|
254
226
|
return conversationId
|
|
255
227
|
}
|
|
@@ -275,7 +247,7 @@ export class ConnectorClient {
|
|
|
275
247
|
|
|
276
248
|
const trimmedConversationId: string = this.conditionallyTruncateConversationId(conversationId, body)
|
|
277
249
|
|
|
278
|
-
const config:
|
|
250
|
+
const config: HttpRequestConfig = {
|
|
279
251
|
method: 'post',
|
|
280
252
|
url: `v3/conversations/${trimmedConversationId}/activities`,
|
|
281
253
|
headers: {
|
|
@@ -285,7 +257,7 @@ export class ConnectorClient {
|
|
|
285
257
|
...(body.channelId === Channels.Msteams && body.isTargetedActivity() ? { params: { isTargetedActivity: 'true' } } : {})
|
|
286
258
|
|
|
287
259
|
}
|
|
288
|
-
const response = await this.
|
|
260
|
+
const response = await this.executeRequest<ResourceResponse>(config)
|
|
289
261
|
record({ httpStatusCode: response.status?.toString() })
|
|
290
262
|
return response.data
|
|
291
263
|
})
|
|
@@ -308,7 +280,7 @@ export class ConnectorClient {
|
|
|
308
280
|
throw ExceptionHelper.generateException(Error, Errors.ConversationIdAndActivityIdRequired)
|
|
309
281
|
}
|
|
310
282
|
record({ conversationId, activityId })
|
|
311
|
-
const config:
|
|
283
|
+
const config: HttpRequestConfig = {
|
|
312
284
|
method: 'put',
|
|
313
285
|
url: `v3/conversations/${conversationId}/activities/${activityId}`,
|
|
314
286
|
headers: {
|
|
@@ -317,7 +289,7 @@ export class ConnectorClient {
|
|
|
317
289
|
data: normalizeOutgoingActivity(body),
|
|
318
290
|
...(body.channelId === Channels.Msteams && body.isTargetedActivity() ? { params: { isTargetedActivity: 'true' } } : {})
|
|
319
291
|
}
|
|
320
|
-
const response = await this.
|
|
292
|
+
const response = await this.executeRequest<ResourceResponse>(config)
|
|
321
293
|
record({ httpStatusCode: response.status?.toString() })
|
|
322
294
|
return response.data
|
|
323
295
|
})
|
|
@@ -341,7 +313,7 @@ export class ConnectorClient {
|
|
|
341
313
|
if (!conversationId || !activityId) {
|
|
342
314
|
throw ExceptionHelper.generateException(Error, Errors.ConversationIdAndActivityIdRequired)
|
|
343
315
|
}
|
|
344
|
-
const config:
|
|
316
|
+
const config: HttpRequestConfig = {
|
|
345
317
|
method: 'delete',
|
|
346
318
|
url: `v3/conversations/${conversationId}/activities/${activityId}`,
|
|
347
319
|
headers: {
|
|
@@ -349,7 +321,7 @@ export class ConnectorClient {
|
|
|
349
321
|
},
|
|
350
322
|
...(isTargetedActivity ? { params: { isTargetedActivity: 'true' } } : {})
|
|
351
323
|
}
|
|
352
|
-
const response = await this.
|
|
324
|
+
const response = await this.executeRequest<void>(config)
|
|
353
325
|
record({ httpStatusCode: response.status?.toString() })
|
|
354
326
|
return response.data
|
|
355
327
|
})
|
|
@@ -370,7 +342,7 @@ export class ConnectorClient {
|
|
|
370
342
|
if (conversationId === undefined) {
|
|
371
343
|
throw ExceptionHelper.generateException(Error, Errors.ConversationIdRequired)
|
|
372
344
|
}
|
|
373
|
-
const config:
|
|
345
|
+
const config: HttpRequestConfig = {
|
|
374
346
|
method: 'post',
|
|
375
347
|
url: `v3/conversations/${conversationId}/attachments`,
|
|
376
348
|
headers: {
|
|
@@ -378,7 +350,7 @@ export class ConnectorClient {
|
|
|
378
350
|
},
|
|
379
351
|
data: body
|
|
380
352
|
}
|
|
381
|
-
const response = await this.
|
|
353
|
+
const response = await this.executeRequest<ResourceResponse>(config)
|
|
382
354
|
record({ httpStatusCode: response.status?.toString() })
|
|
383
355
|
return response.data
|
|
384
356
|
})
|
|
@@ -397,14 +369,14 @@ export class ConnectorClient {
|
|
|
397
369
|
if (attachmentId === undefined) {
|
|
398
370
|
throw ExceptionHelper.generateException(Error, Errors.AttachmentIdRequired)
|
|
399
371
|
}
|
|
400
|
-
const config:
|
|
372
|
+
const config: HttpRequestConfig = {
|
|
401
373
|
method: 'get',
|
|
402
374
|
url: `v3/attachments/${attachmentId}`,
|
|
403
375
|
headers: {
|
|
404
376
|
'Content-Type': 'application/json'
|
|
405
377
|
}
|
|
406
378
|
}
|
|
407
|
-
const response = await this.
|
|
379
|
+
const response = await this.executeRequest<AttachmentInfo>(config)
|
|
408
380
|
record({ httpStatusCode: response.status?.toString() })
|
|
409
381
|
return response.data
|
|
410
382
|
})
|
|
@@ -428,16 +400,66 @@ export class ConnectorClient {
|
|
|
428
400
|
if (viewId === undefined) {
|
|
429
401
|
throw ExceptionHelper.generateException(Error, Errors.ViewIdRequired)
|
|
430
402
|
}
|
|
431
|
-
const config:
|
|
403
|
+
const config: HttpRequestConfig = {
|
|
432
404
|
method: 'get',
|
|
433
405
|
url: `v3/attachments/${attachmentId}/views/${viewId}`,
|
|
434
406
|
headers: {
|
|
435
407
|
'Content-Type': 'application/json'
|
|
436
|
-
}
|
|
408
|
+
},
|
|
409
|
+
responseType: 'stream'
|
|
437
410
|
}
|
|
438
|
-
const response = await this.
|
|
411
|
+
const response = await this.executeRequest<NodeJS.ReadableStream>(config)
|
|
439
412
|
record({ httpStatusCode: response.status?.toString() })
|
|
440
413
|
return response.data
|
|
441
414
|
})
|
|
442
415
|
}
|
|
416
|
+
|
|
417
|
+
private async executeRequest<T = unknown> (config: HttpRequestConfig): Promise<HttpResponse<T>> {
|
|
418
|
+
const { method, url, data, headers, params } = config
|
|
419
|
+
const { Authorization, authorization, ...headersToLog } = { ...this._httpClient.defaultHeaders, ...headers } as Record<string, string>
|
|
420
|
+
logger.debug('Request: ', {
|
|
421
|
+
host: this._httpClient.baseURL,
|
|
422
|
+
url,
|
|
423
|
+
data,
|
|
424
|
+
method,
|
|
425
|
+
params,
|
|
426
|
+
headers: headersToLog
|
|
427
|
+
})
|
|
428
|
+
|
|
429
|
+
try {
|
|
430
|
+
const response = await this._httpClient.request<T>(config)
|
|
431
|
+
logger.debug('Response: ', {
|
|
432
|
+
status: response.status,
|
|
433
|
+
statusText: response.statusText,
|
|
434
|
+
host: this._httpClient.baseURL,
|
|
435
|
+
url: response.config?.url,
|
|
436
|
+
data: response.config?.data,
|
|
437
|
+
method: response.config?.method,
|
|
438
|
+
})
|
|
439
|
+
return response
|
|
440
|
+
} catch (error) {
|
|
441
|
+
if (error instanceof HttpError) {
|
|
442
|
+
const message = formatHttpErrorMessage(error)
|
|
443
|
+
logger.debug('Response error: ', {
|
|
444
|
+
host: this._httpClient.baseURL,
|
|
445
|
+
url: error.config.url,
|
|
446
|
+
method: error.config.method,
|
|
447
|
+
data: error.config.data,
|
|
448
|
+
message,
|
|
449
|
+
stack: error.stack,
|
|
450
|
+
})
|
|
451
|
+
|
|
452
|
+
Object.assign(error, {
|
|
453
|
+
host: this._httpClient.baseURL,
|
|
454
|
+
url: error.config.url,
|
|
455
|
+
method: error.config.method,
|
|
456
|
+
data: error.config.data,
|
|
457
|
+
message,
|
|
458
|
+
})
|
|
459
|
+
|
|
460
|
+
throw error
|
|
461
|
+
}
|
|
462
|
+
throw error
|
|
463
|
+
}
|
|
464
|
+
}
|
|
443
465
|
}
|
package/src/errorHelper.ts
CHANGED
|
@@ -256,6 +256,14 @@ export const Errors: { [key: string]: AgentErrorDefinition } = {
|
|
|
256
256
|
description: 'request.body parameter required'
|
|
257
257
|
},
|
|
258
258
|
|
|
259
|
+
/**
|
|
260
|
+
* Error thrown when HttpClient is given a relative URL without a configured base URL.
|
|
261
|
+
*/
|
|
262
|
+
HttpClientRelativeUrlRequiresBaseUrl: {
|
|
263
|
+
code: -120291,
|
|
264
|
+
description: 'HttpClient requires baseURL when using a relative URL: {url}'
|
|
265
|
+
},
|
|
266
|
+
|
|
259
267
|
// ============================================================================
|
|
260
268
|
// Connection and Authentication Errors (-120300 to -120590)
|
|
261
269
|
// ============================================================================
|
|
@@ -516,8 +524,64 @@ export const Errors: { [key: string]: AgentErrorDefinition } = {
|
|
|
516
524
|
description: "The current token for the '{connectionName}' AzureBot connection is not exchangeable for an on-behalf-of flow. Ensure the base token audience is for the bot/resource app, such as an App ID URI like 'api://...' or otherwise includes the app's client id."
|
|
517
525
|
},
|
|
518
526
|
|
|
527
|
+
/**
|
|
528
|
+
* Error thrown when an unsupported authentication type is specified in authConfig.
|
|
529
|
+
*/
|
|
530
|
+
UnsupportedAuthType: {
|
|
531
|
+
code: -120593,
|
|
532
|
+
description: 'Unsupported authentication type: {authType}'
|
|
533
|
+
},
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Error thrown when WorkloadIdentity authentication requires `federatedTokenFile` or the deprecated `WIDAssertionFile` to be configured.
|
|
537
|
+
*/
|
|
538
|
+
WorkloadIdentityTokenFileRequired: {
|
|
539
|
+
code: -120594,
|
|
540
|
+
description: 'WorkloadIdentity authentication requires `federatedTokenFile` or the deprecated `WIDAssertionFile` to be configured'
|
|
541
|
+
},
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* Error thrown when ClientSecret authentication is specified but `clientSecret` is not configured.
|
|
545
|
+
*/
|
|
546
|
+
ClientSecretRequired: {
|
|
547
|
+
code: -120595,
|
|
548
|
+
description: 'ClientSecret authentication requires `clientSecret` to be configured'
|
|
549
|
+
},
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Error thrown when Certificate authentication is specified but `certPemFile` or `certKeyFile` is not configured.
|
|
553
|
+
*/
|
|
554
|
+
CertificateFilesRequired: {
|
|
555
|
+
code: -120596,
|
|
556
|
+
description: 'Certificate authentication requires both `certPemFile` and `certKeyFile` to be configured'
|
|
557
|
+
},
|
|
558
|
+
|
|
559
|
+
/**
|
|
560
|
+
* Error thrown when FederatedCredentials authentication is specified but `federatedClientId` or the deprecated `FICClientId` is not configured.
|
|
561
|
+
*/
|
|
562
|
+
FICClientIdRequired: {
|
|
563
|
+
code: -120597,
|
|
564
|
+
description: 'FederatedCredentials authentication requires `federatedClientId` or the deprecated `FICClientId` to be configured'
|
|
565
|
+
},
|
|
566
|
+
|
|
567
|
+
/**
|
|
568
|
+
* Error thrown when UserManagedIdentity authentication is specified but `clientId` is not configured.
|
|
569
|
+
*/
|
|
570
|
+
ClientIdRequiredForUserManagedIdentity: {
|
|
571
|
+
code: -120598,
|
|
572
|
+
description: 'UserManagedIdentity authentication requires `clientId` to be configured'
|
|
573
|
+
},
|
|
574
|
+
|
|
575
|
+
/**
|
|
576
|
+
* Error thrown when there's a timeout during a token request.
|
|
577
|
+
*/
|
|
578
|
+
TokenRequestTimeout: {
|
|
579
|
+
code: -120599,
|
|
580
|
+
description: 'Token request timed out after {timeoutMs} ms'
|
|
581
|
+
},
|
|
582
|
+
|
|
519
583
|
// ============================================================================
|
|
520
|
-
// Agent and Client Errors (-120600 to -
|
|
584
|
+
// Agent and Client Errors (-120600 to -120650)
|
|
521
585
|
// ============================================================================
|
|
522
586
|
|
|
523
587
|
/**
|
|
@@ -537,18 +601,34 @@ export const Errors: { [key: string]: AgentErrorDefinition } = {
|
|
|
537
601
|
},
|
|
538
602
|
|
|
539
603
|
/**
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
604
|
+
* Error thrown when agent ID is required.
|
|
605
|
+
*/
|
|
606
|
+
AgentIdRequired: {
|
|
543
607
|
code: -120620,
|
|
608
|
+
description: 'Agent ID is required to apply outbound agent headers'
|
|
609
|
+
},
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* Error thrown when agent name contains invalid characters.
|
|
613
|
+
*/
|
|
614
|
+
AgentNameInvalid: {
|
|
615
|
+
code: -120630,
|
|
616
|
+
description: 'Agent name contains invalid characters: {agentName}'
|
|
617
|
+
},
|
|
618
|
+
|
|
619
|
+
/**
|
|
620
|
+
* Error thrown when failed to post activity to agent.
|
|
621
|
+
*/
|
|
622
|
+
FailedToPostActivityToAgent: {
|
|
623
|
+
code: -120640,
|
|
544
624
|
description: 'Failed to post activity to agent: {statusText}'
|
|
545
625
|
},
|
|
546
626
|
|
|
547
627
|
/**
|
|
548
|
-
|
|
549
|
-
|
|
628
|
+
* Error thrown when logic parameter must be defined.
|
|
629
|
+
*/
|
|
550
630
|
LogicParameterRequired: {
|
|
551
|
-
code: -
|
|
631
|
+
code: -120650,
|
|
552
632
|
description: 'logic must be defined'
|
|
553
633
|
},
|
|
554
634
|
|
|
@@ -784,6 +864,15 @@ export const Errors: { [key: string]: AgentErrorDefinition } = {
|
|
|
784
864
|
description: 'This proactive operation requires app storage to load and save TurnState. Set options.storage on AgentApplication.'
|
|
785
865
|
},
|
|
786
866
|
|
|
867
|
+
/**
|
|
868
|
+
* Error thrown when CreateConversationOptionsBuilder.build() is called for a Teams channel
|
|
869
|
+
* conversation (withTeamsChannelId) without an activity set via withActivity().
|
|
870
|
+
*/
|
|
871
|
+
CreateConversationBuilderChannelActivityRequired: {
|
|
872
|
+
code: -120753,
|
|
873
|
+
description: 'CreateConversationOptionsBuilder: Teams channel conversations require an initial activity. Call withActivity() before build().'
|
|
874
|
+
},
|
|
875
|
+
|
|
787
876
|
// ============================================================================
|
|
788
877
|
// General Errors (-120990)
|
|
789
878
|
// ============================================================================
|
package/src/getProductInfo.ts
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
|
+
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
2
|
+
// Licensed under the MIT License.
|
|
3
|
+
|
|
1
4
|
import { version } from '../package.json'
|
|
2
5
|
import os from 'os'
|
|
6
|
+
import { Activity, ExceptionHelper } from '@microsoft/agents-activity'
|
|
7
|
+
import { Errors } from './errorHelper'
|
|
8
|
+
import { HeaderPropagationCollection } from './headerPropagation'
|
|
9
|
+
|
|
10
|
+
const AGENT_NAME_PATTERN = /^[A-Za-z0-9 _-]+$/
|
|
11
|
+
const AGENT_REGISTRAR = 'A365'
|
|
12
|
+
const DEFAULT_AGENT_NAME = 'Agents-SDK-JS'
|
|
3
13
|
|
|
4
14
|
/**
|
|
5
15
|
* Generates a string containing information about the SDK version and runtime environment.
|
|
@@ -7,4 +17,72 @@ import os from 'os'
|
|
|
7
17
|
*
|
|
8
18
|
* @returns A formatted string containing the SDK version, Node.js version, and OS details
|
|
9
19
|
*/
|
|
10
|
-
export const getProductInfo = () : string =>
|
|
20
|
+
export const getProductInfo = () : string => `${DEFAULT_AGENT_NAME.toLowerCase()}/${version} nodejs/${process.version} ${os.platform()}-${os.arch()}/${os.release()}`
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Applies the SDK product information to the User-Agent header of outgoing requests.
|
|
24
|
+
* @param headers The HeaderPropagationCollection to which the User-Agent header will be added or updated
|
|
25
|
+
*/
|
|
26
|
+
export function applyUserAgentHeader (headers: HeaderPropagationCollection): void {
|
|
27
|
+
const userAgentKey = headers.key?.('User-Agent') ?? 'User-Agent'
|
|
28
|
+
const userAgent = headers.outgoing[userAgentKey] ?? headers.incoming[userAgentKey]
|
|
29
|
+
const productInfo = getProductInfo()
|
|
30
|
+
if (!userAgent) {
|
|
31
|
+
headers.add({ [userAgentKey]: productInfo })
|
|
32
|
+
} else if (!userAgent.includes(productInfo)) {
|
|
33
|
+
headers.concat({ [userAgentKey]: productInfo })
|
|
34
|
+
} else {
|
|
35
|
+
headers.propagate([userAgentKey])
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Applies standardized agent-related headers for agentic requests.
|
|
41
|
+
*
|
|
42
|
+
* @param headers The HeaderPropagationCollection to which the agent headers will be applied.
|
|
43
|
+
* @param activity The incoming Activity containing agentic request information.
|
|
44
|
+
* @param agentName An optional human-friendly name for the agent, which will be validated and included in the headers.
|
|
45
|
+
*
|
|
46
|
+
* @remarks
|
|
47
|
+
* The function sets the following headers:
|
|
48
|
+
* - `AgentRegistrar`: A fixed value indicating the registrar of the agent (e.g., "A365").
|
|
49
|
+
* - `AgentName`: A human-friendly name for the agent, validated against a pattern to ensure it only contains allowed characters.
|
|
50
|
+
* - `AgentID`: A unique identifier for the agent instance, resolved from the activity's agentic instance ID or agentic user.
|
|
51
|
+
* - `Agent-Referrer`: The channel ID from the incoming activity, indicating the source of the request.
|
|
52
|
+
* The function ensures that the agent headers are consistently applied to outgoing requests for proper identification and tracing.
|
|
53
|
+
*/
|
|
54
|
+
export function applyAgenticHeaders (
|
|
55
|
+
headers: HeaderPropagationCollection,
|
|
56
|
+
activity: Activity,
|
|
57
|
+
agentName?: string
|
|
58
|
+
): void {
|
|
59
|
+
headers.override({
|
|
60
|
+
AgentRegistrar: AGENT_REGISTRAR,
|
|
61
|
+
AgentName: normalizeAgentName(agentName),
|
|
62
|
+
AgentID: resolveAgentId(activity),
|
|
63
|
+
'Agent-Referrer': activity.channelId ?? '',
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function resolveAgentId (activity: Activity): string {
|
|
68
|
+
const agentId = activity.getAgenticInstanceId() ?? activity.getAgenticUser()
|
|
69
|
+
|
|
70
|
+
if (!agentId) {
|
|
71
|
+
throw ExceptionHelper.generateException(Error, Errors.AgentIdRequired)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return agentId
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function normalizeAgentName (agentName?: string): string {
|
|
78
|
+
const normalizedAgentName = agentName?.trim()
|
|
79
|
+
if (!normalizedAgentName) {
|
|
80
|
+
return DEFAULT_AGENT_NAME
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (!AGENT_NAME_PATTERN.test(normalizedAgentName)) {
|
|
84
|
+
throw ExceptionHelper.generateException(Error, Errors.AgentNameInvalid, undefined, { agentName: normalizedAgentName })
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return normalizedAgentName
|
|
88
|
+
}
|