@microsoft/agents-hosting 1.7.2 → 1.8.0-beta.36.gb033f0abc1
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/README.md +64 -2
- package/dist/package.json +3 -3
- package/dist/src/agent-client/agentClient.d.ts +18 -6
- package/dist/src/agent-client/agentClient.js +34 -18
- package/dist/src/agent-client/agentClient.js.map +1 -1
- package/dist/src/agent-client/agentResponseHandler.d.ts +14 -9
- package/dist/src/agent-client/agentResponseHandler.js +10 -5
- package/dist/src/agent-client/agentResponseHandler.js.map +1 -1
- package/dist/src/agent-client/createAgentResponseHandler.d.ts +7 -2
- package/dist/src/agent-client/createAgentResponseHandler.js +55 -8
- package/dist/src/agent-client/createAgentResponseHandler.js.map +1 -1
- package/dist/src/app/adaptiveCards/activityValueParsers.d.ts +1 -4
- package/dist/src/app/adaptiveCards/activityValueParsers.js +2 -5
- package/dist/src/app/adaptiveCards/activityValueParsers.js.map +1 -1
- package/dist/src/app/adaptiveCards/adaptiveCardsActions.d.ts +18 -0
- package/dist/src/app/adaptiveCards/adaptiveCardsActions.js +53 -23
- package/dist/src/app/adaptiveCards/adaptiveCardsActions.js.map +1 -1
- package/dist/src/app/agentApplication.d.ts +3 -0
- package/dist/src/app/agentApplication.js +140 -81
- package/dist/src/app/agentApplication.js.map +1 -1
- package/dist/src/app/attachmentDownloader.d.ts +4 -1
- package/dist/src/app/attachmentDownloader.js +27 -5
- package/dist/src/app/attachmentDownloader.js.map +1 -1
- package/dist/src/app/auth/handlers/agenticAuthorization.d.ts +1 -1
- package/dist/src/app/auth/handlers/azureBotAuthorization.d.ts +1 -1
- package/dist/src/app/auth/types.d.ts +3 -4
- package/dist/src/app/index.d.ts +2 -0
- package/dist/src/app/index.js.map +1 -1
- package/dist/src/app/streaming/streamingResponse.d.ts +56 -0
- package/dist/src/app/streaming/streamingResponse.js +250 -26
- package/dist/src/app/streaming/streamingResponse.js.map +1 -1
- package/dist/src/app/teamsAttachmentDownloader.d.ts +3 -1
- package/dist/src/app/teamsAttachmentDownloader.js +12 -1
- package/dist/src/app/teamsAttachmentDownloader.js.map +1 -1
- package/dist/src/auth/authConfiguration.js +12 -0
- package/dist/src/auth/authConfiguration.js.map +1 -1
- package/dist/src/auth/index.d.ts +2 -1
- package/dist/src/auth/index.js +2 -1
- package/dist/src/auth/index.js.map +1 -1
- package/dist/src/auth/jwt-middleware.d.ts +6 -0
- package/dist/src/auth/jwt-middleware.js +263 -13
- package/dist/src/auth/jwt-middleware.js.map +1 -1
- package/dist/src/auth/msal/msalConnectionSettings.d.ts +8 -0
- package/dist/src/auth/msal/msalHttpRetryHandlerHelper.d.ts +34 -0
- package/dist/src/auth/msal/msalHttpRetryHandlerHelper.js +126 -0
- package/dist/src/auth/msal/msalHttpRetryHandlerHelper.js.map +1 -0
- package/dist/src/auth/msal/msalTokenProvider.d.ts +1 -0
- package/dist/src/auth/msal/msalTokenProvider.js +23 -14
- package/dist/src/auth/msal/msalTokenProvider.js.map +1 -1
- package/dist/src/auth/settings.d.ts +18 -0
- package/dist/src/auth/settings.js +8 -2
- package/dist/src/auth/settings.js.map +1 -1
- package/dist/src/cloudAdapter.d.ts +29 -2
- package/dist/src/cloudAdapter.js +44 -2
- package/dist/src/cloudAdapter.js.map +1 -1
- package/dist/src/errorHelper.js +137 -101
- package/dist/src/errorHelper.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/invoke/adaptiveCardInvokeValue.d.ts +5 -0
- package/dist/src/oauth/userTokenClient.d.ts +0 -1
- package/dist/src/oauth/userTokenClient.js +0 -1
- package/dist/src/oauth/userTokenClient.js.map +1 -1
- package/dist/src/observability/traces.d.ts +6 -0
- package/dist/src/observability/traces.js +20 -2
- package/dist/src/observability/traces.js.map +1 -1
- package/dist/src/outboundHostValidator.d.ts +47 -0
- package/dist/src/outboundHostValidator.js +149 -0
- package/dist/src/outboundHostValidator.js.map +1 -0
- package/dist/src/state/agentState.d.ts +5 -0
- package/dist/src/state/agentState.js +16 -3
- package/dist/src/state/agentState.js.map +1 -1
- package/package.json +3 -3
- package/src/agent-client/agentClient.ts +46 -23
- package/src/agent-client/agentResponseHandler.ts +14 -9
- package/src/agent-client/createAgentResponseHandler.ts +70 -9
- package/src/app/adaptiveCards/activityValueParsers.ts +3 -9
- package/src/app/adaptiveCards/adaptiveCardsActions.ts +52 -24
- package/src/app/agentApplication.ts +149 -77
- package/src/app/attachmentDownloader.ts +25 -5
- package/src/app/auth/handlers/agenticAuthorization.ts +1 -1
- package/src/app/auth/handlers/azureBotAuthorization.ts +1 -1
- package/src/app/auth/types.ts +3 -3
- package/src/app/index.ts +8 -0
- package/src/app/streaming/streamingResponse.ts +276 -22
- package/src/app/teamsAttachmentDownloader.ts +11 -1
- package/src/auth/authConfiguration.ts +12 -0
- package/src/auth/index.ts +2 -1
- package/src/auth/jwt-middleware.ts +298 -14
- package/src/auth/msal/msalConnectionSettings.ts +10 -0
- package/src/auth/msal/msalHttpRetryHandlerHelper.ts +161 -0
- package/src/auth/msal/msalTokenProvider.ts +32 -14
- package/src/auth/settings.ts +18 -3
- package/src/cloudAdapter.ts +56 -3
- package/src/errorHelper.ts +155 -114
- package/src/index.ts +1 -0
- package/src/invoke/adaptiveCardInvokeValue.ts +5 -0
- package/src/oauth/userTokenClient.ts +0 -1
- package/src/observability/traces.ts +19 -1
- package/src/outboundHostValidator.ts +172 -0
- package/src/state/agentState.ts +24 -6
|
@@ -80,7 +80,6 @@ export class UserTokenClient {
|
|
|
80
80
|
/**
|
|
81
81
|
* Creates a new instance of UserTokenClient with authentication.
|
|
82
82
|
* @param baseURL - The base URL for the API.
|
|
83
|
-
* @param authConfig - The authentication configuration.
|
|
84
83
|
* @param authProvider - The authentication provider.
|
|
85
84
|
* @param scope - The scope for the authentication token.
|
|
86
85
|
* @param headers - Optional headers to propagate in the request.
|
|
@@ -26,7 +26,8 @@ export const AgentApplicationTraceDefinitions = {
|
|
|
26
26
|
span.setAttributes({
|
|
27
27
|
'route.authorized': record.authorized,
|
|
28
28
|
'route.matched': record.routeMatched,
|
|
29
|
-
...attributes
|
|
29
|
+
...attributes,
|
|
30
|
+
'activity.name': activity.name ?? 'unknown'
|
|
30
31
|
})
|
|
31
32
|
|
|
32
33
|
HostingMetrics.turnsTotalCounter.add(1, attributes)
|
|
@@ -48,6 +49,23 @@ export const AgentApplicationTraceDefinitions = {
|
|
|
48
49
|
span.setAttribute('agents.attachments.count', record.attachmentsCount ?? 0)
|
|
49
50
|
}
|
|
50
51
|
}),
|
|
52
|
+
typingIndicator: trace.define({
|
|
53
|
+
name: SpanNames.AGENTS_APP_TYPING_INDICATOR,
|
|
54
|
+
record: {
|
|
55
|
+
activityType: 'unknown',
|
|
56
|
+
channelId: 'unknown',
|
|
57
|
+
conversationId: 'unknown',
|
|
58
|
+
notificationSequence: 0,
|
|
59
|
+
},
|
|
60
|
+
end ({ span, record }) {
|
|
61
|
+
span.setAttributes({
|
|
62
|
+
'activity.type': record.activityType,
|
|
63
|
+
'activity.channel_id': record.channelId,
|
|
64
|
+
'activity.conversation_id': record.conversationId,
|
|
65
|
+
'typing.notification.sequence': record.notificationSequence,
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
}),
|
|
51
69
|
beforeTurn: trace.define({
|
|
52
70
|
name: SpanNames.AGENTS_APP_BEFORE_TURN,
|
|
53
71
|
record: {},
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { parseBooleanEnv } from './utils/env'
|
|
7
|
+
|
|
8
|
+
/** Hosts used by Microsoft channel callbacks and hosted attachments. */
|
|
9
|
+
const DEFAULT_MICROSOFT_HOSTS = Object.freeze([
|
|
10
|
+
'botframework.com',
|
|
11
|
+
'smba.trafficmanager.net',
|
|
12
|
+
'teams.microsoft.com',
|
|
13
|
+
'teams.microsoft.us',
|
|
14
|
+
'graph.microsoft.com',
|
|
15
|
+
'sharepoint.com',
|
|
16
|
+
'svc.ms',
|
|
17
|
+
'blob.core.windows.net'
|
|
18
|
+
])
|
|
19
|
+
|
|
20
|
+
const OUTBOUND_HOST_VALIDATOR_ENV_PREFIX = 'OutboundHostValidator__'
|
|
21
|
+
const OUTBOUND_HOST_VALIDATOR_ENV_PREFIX_UPPER = OUTBOUND_HOST_VALIDATOR_ENV_PREFIX.toUpperCase()
|
|
22
|
+
|
|
23
|
+
/** Configuration for the shared outbound-host allowlist. */
|
|
24
|
+
export interface OutboundHostValidatorOptions {
|
|
25
|
+
/** Enables allowlist enforcement. Defaults to `false`. */
|
|
26
|
+
enabled?: boolean
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Indicates whether the built-in list of Microsoft first-party hosts
|
|
30
|
+
* (Bot Connector, Graph, SharePoint, Azure Blob/AMS) is included when enforcement is enabled.
|
|
31
|
+
* Defaults to `true`.
|
|
32
|
+
*/
|
|
33
|
+
includeDefaultMicrosoftHosts?: boolean
|
|
34
|
+
|
|
35
|
+
/** Additional exact hosts or host suffixes to allow.
|
|
36
|
+
* An entry matches a request host when the host equals the entry or is a subdomain of it (e.g. `contoso.com` matches `contoso.com` and `files.contoso.com`).
|
|
37
|
+
* A leading `*.` is accepted and ignored (treated as a suffix). Ports and paths are ignored if provided.
|
|
38
|
+
*/
|
|
39
|
+
hosts?: readonly string[]
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** A policy that decides whether an outbound URL is safe to request. */
|
|
43
|
+
export interface OutboundUrlPolicy {
|
|
44
|
+
readonly enabled: boolean
|
|
45
|
+
isAllowed(url: string | URL | null | undefined): boolean
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Shared allowlist policy for server-side outbound requests.
|
|
50
|
+
*
|
|
51
|
+
* A configured suffix matches both the exact host and its subdomains. The
|
|
52
|
+
* policy is disabled by default to preserve existing SDK behavior.
|
|
53
|
+
*/
|
|
54
|
+
export class OutboundHostValidator implements OutboundUrlPolicy {
|
|
55
|
+
public readonly enabled: boolean
|
|
56
|
+
private readonly suffixes: ReadonlySet<string>
|
|
57
|
+
|
|
58
|
+
public constructor (options: OutboundHostValidatorOptions = {}) {
|
|
59
|
+
this.enabled = options.enabled ?? false
|
|
60
|
+
|
|
61
|
+
const suffixes = new Set<string>()
|
|
62
|
+
if (options.includeDefaultMicrosoftHosts ?? true) {
|
|
63
|
+
for (const host of DEFAULT_MICROSOFT_HOSTS) suffixes.add(host)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
for (const host of options.hosts ?? []) {
|
|
67
|
+
const normalized = normalizeConfiguredHost(host)
|
|
68
|
+
if (normalized) suffixes.add(normalized)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
this.suffixes = suffixes
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
public isAllowed (input: string | URL | null | undefined): boolean {
|
|
75
|
+
if (!this.enabled) return true
|
|
76
|
+
|
|
77
|
+
const host = getUrlHost(input)
|
|
78
|
+
if (!host) return false
|
|
79
|
+
|
|
80
|
+
for (const suffix of this.suffixes) {
|
|
81
|
+
if (host === suffix || host.endsWith(`.${suffix}`)) return true
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return false
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Loads validator options from environment variables compatible with the
|
|
90
|
+
* .NET `OutboundHostValidator` configuration section.
|
|
91
|
+
*
|
|
92
|
+
* Hosts can be supplied either as a comma-separated `Hosts` value or as
|
|
93
|
+
* indexed values such as `Hosts__0`, `Hosts__1`, and so on.
|
|
94
|
+
*/
|
|
95
|
+
export function loadOutboundHostValidatorOptionsFromEnv (): OutboundHostValidatorOptions {
|
|
96
|
+
const result: OutboundHostValidatorOptions = {}
|
|
97
|
+
const indexedHosts = new Map<number, string>()
|
|
98
|
+
const hosts: string[] = []
|
|
99
|
+
|
|
100
|
+
for (const [envKey, rawValue] of Object.entries(process.env)) {
|
|
101
|
+
const upperKey = envKey.toUpperCase()
|
|
102
|
+
if (!upperKey.startsWith(OUTBOUND_HOST_VALIDATOR_ENV_PREFIX_UPPER)) continue
|
|
103
|
+
|
|
104
|
+
const property = envKey.substring(OUTBOUND_HOST_VALIDATOR_ENV_PREFIX.length)
|
|
105
|
+
const upperProperty = property.toUpperCase()
|
|
106
|
+
|
|
107
|
+
if (upperProperty === 'ENABLED') {
|
|
108
|
+
const value = parseBooleanEnv(rawValue)
|
|
109
|
+
if (value !== undefined) result.enabled = value
|
|
110
|
+
} else if (upperProperty === 'INCLUDEDEFAULTMICROSOFTHOSTS') {
|
|
111
|
+
const value = parseBooleanEnv(rawValue)
|
|
112
|
+
if (value !== undefined) result.includeDefaultMicrosoftHosts = value
|
|
113
|
+
} else if (upperProperty === 'HOSTS' && rawValue) {
|
|
114
|
+
hosts.push(...rawValue.split(',').map(host => host.trim()).filter(Boolean))
|
|
115
|
+
} else {
|
|
116
|
+
const match = /^HOSTS__(\d+)$/i.exec(property)
|
|
117
|
+
if (match && rawValue?.trim()) indexedHosts.set(Number(match[1]), rawValue.trim())
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
for (const [, host] of [...indexedHosts.entries()].sort(([left], [right]) => left - right)) {
|
|
122
|
+
hosts.push(host)
|
|
123
|
+
}
|
|
124
|
+
if (hosts.length > 0) result.hosts = hosts
|
|
125
|
+
|
|
126
|
+
return result
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Creates the default validator, with explicit settings overriding the environment. */
|
|
130
|
+
export function createOutboundHostValidator (options?: OutboundHostValidatorOptions): OutboundHostValidator {
|
|
131
|
+
const fromEnv = loadOutboundHostValidatorOptionsFromEnv()
|
|
132
|
+
return new OutboundHostValidator({
|
|
133
|
+
enabled: options?.enabled ?? fromEnv.enabled,
|
|
134
|
+
includeDefaultMicrosoftHosts: options?.includeDefaultMicrosoftHosts ?? fromEnv.includeDefaultMicrosoftHosts,
|
|
135
|
+
hosts: options?.hosts ?? fromEnv.hosts
|
|
136
|
+
})
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function getUrlHost (input: string | URL | null | undefined): string | undefined {
|
|
140
|
+
if (!input) return undefined
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
const url = input instanceof URL ? input : new URL(input)
|
|
144
|
+
return normalizeHostname(url.hostname)
|
|
145
|
+
} catch {
|
|
146
|
+
return undefined
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function normalizeConfiguredHost (input: string): string | undefined {
|
|
151
|
+
let candidate = input?.trim()
|
|
152
|
+
if (!candidate) return undefined
|
|
153
|
+
if (candidate.startsWith('*.')) candidate = candidate.slice(2)
|
|
154
|
+
|
|
155
|
+
try {
|
|
156
|
+
const absolute = new URL(candidate)
|
|
157
|
+
if (absolute.hostname) return normalizeHostname(absolute.hostname)
|
|
158
|
+
} catch {
|
|
159
|
+
// A bare host, host:port, or host/path is handled below.
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
return normalizeHostname(new URL(`http://${candidate}`).hostname)
|
|
164
|
+
} catch {
|
|
165
|
+
return undefined
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function normalizeHostname (host: string): string | undefined {
|
|
170
|
+
const normalized = host.trim().toLowerCase().replace(/\.$/, '')
|
|
171
|
+
return normalized || undefined
|
|
172
|
+
}
|
package/src/state/agentState.ts
CHANGED
|
@@ -44,7 +44,10 @@ export interface CustomKey {
|
|
|
44
44
|
* The ID of the conversation where the state should be stored
|
|
45
45
|
*/
|
|
46
46
|
conversationId: string;
|
|
47
|
-
|
|
47
|
+
/**
|
|
48
|
+
* Optional namespace appended to the generated storage key
|
|
49
|
+
*/
|
|
50
|
+
namespace?: string;
|
|
48
51
|
}
|
|
49
52
|
|
|
50
53
|
/**
|
|
@@ -152,8 +155,10 @@ export class AgentState {
|
|
|
152
155
|
private async getStorageOrCustomKey (customKey: CustomKey | undefined, context: TurnContext) {
|
|
153
156
|
let key: string | undefined
|
|
154
157
|
if (customKey && customKey.channelId && customKey.conversationId) {
|
|
155
|
-
|
|
156
|
-
|
|
158
|
+
key = `${customKey.channelId}/conversations/${customKey.conversationId}`
|
|
159
|
+
if (customKey.namespace) {
|
|
160
|
+
key = `${key}/${customKey.namespace}`
|
|
161
|
+
}
|
|
157
162
|
} else {
|
|
158
163
|
key = await this.storageKey(context)
|
|
159
164
|
}
|
|
@@ -217,13 +222,26 @@ export class AgentState {
|
|
|
217
222
|
*/
|
|
218
223
|
private readonly calculateChangeHash = (item: StoreItem): string => {
|
|
219
224
|
const { eTag, ...rest } = item
|
|
220
|
-
|
|
221
|
-
// TODO review circular json structure
|
|
222
|
-
const result = JSON.stringify(rest)
|
|
225
|
+
const result = this.stringifyForHash(rest)
|
|
223
226
|
|
|
224
227
|
const hash = createHash('sha256', { encoding: 'utf-8' })
|
|
225
228
|
const hashed = hash.update(result).digest('hex')
|
|
226
229
|
|
|
227
230
|
return hashed
|
|
228
231
|
}
|
|
232
|
+
|
|
233
|
+
private readonly stringifyForHash = (item: StoreItem): string => {
|
|
234
|
+
const seen = new WeakSet<object>()
|
|
235
|
+
|
|
236
|
+
return JSON.stringify(item, (_key, value) => {
|
|
237
|
+
if (value && typeof value === 'object') {
|
|
238
|
+
if (seen.has(value)) {
|
|
239
|
+
return '[Circular]'
|
|
240
|
+
}
|
|
241
|
+
seen.add(value)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return value
|
|
245
|
+
})
|
|
246
|
+
}
|
|
229
247
|
}
|