@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.
Files changed (102) hide show
  1. package/README.md +64 -2
  2. package/dist/package.json +3 -3
  3. package/dist/src/agent-client/agentClient.d.ts +18 -6
  4. package/dist/src/agent-client/agentClient.js +34 -18
  5. package/dist/src/agent-client/agentClient.js.map +1 -1
  6. package/dist/src/agent-client/agentResponseHandler.d.ts +14 -9
  7. package/dist/src/agent-client/agentResponseHandler.js +10 -5
  8. package/dist/src/agent-client/agentResponseHandler.js.map +1 -1
  9. package/dist/src/agent-client/createAgentResponseHandler.d.ts +7 -2
  10. package/dist/src/agent-client/createAgentResponseHandler.js +55 -8
  11. package/dist/src/agent-client/createAgentResponseHandler.js.map +1 -1
  12. package/dist/src/app/adaptiveCards/activityValueParsers.d.ts +1 -4
  13. package/dist/src/app/adaptiveCards/activityValueParsers.js +2 -5
  14. package/dist/src/app/adaptiveCards/activityValueParsers.js.map +1 -1
  15. package/dist/src/app/adaptiveCards/adaptiveCardsActions.d.ts +18 -0
  16. package/dist/src/app/adaptiveCards/adaptiveCardsActions.js +53 -23
  17. package/dist/src/app/adaptiveCards/adaptiveCardsActions.js.map +1 -1
  18. package/dist/src/app/agentApplication.d.ts +3 -0
  19. package/dist/src/app/agentApplication.js +140 -81
  20. package/dist/src/app/agentApplication.js.map +1 -1
  21. package/dist/src/app/attachmentDownloader.d.ts +4 -1
  22. package/dist/src/app/attachmentDownloader.js +27 -5
  23. package/dist/src/app/attachmentDownloader.js.map +1 -1
  24. package/dist/src/app/auth/handlers/agenticAuthorization.d.ts +1 -1
  25. package/dist/src/app/auth/handlers/azureBotAuthorization.d.ts +1 -1
  26. package/dist/src/app/auth/types.d.ts +3 -4
  27. package/dist/src/app/index.d.ts +2 -0
  28. package/dist/src/app/index.js.map +1 -1
  29. package/dist/src/app/streaming/streamingResponse.d.ts +56 -0
  30. package/dist/src/app/streaming/streamingResponse.js +250 -26
  31. package/dist/src/app/streaming/streamingResponse.js.map +1 -1
  32. package/dist/src/app/teamsAttachmentDownloader.d.ts +3 -1
  33. package/dist/src/app/teamsAttachmentDownloader.js +12 -1
  34. package/dist/src/app/teamsAttachmentDownloader.js.map +1 -1
  35. package/dist/src/auth/authConfiguration.js +12 -0
  36. package/dist/src/auth/authConfiguration.js.map +1 -1
  37. package/dist/src/auth/index.d.ts +2 -1
  38. package/dist/src/auth/index.js +2 -1
  39. package/dist/src/auth/index.js.map +1 -1
  40. package/dist/src/auth/jwt-middleware.d.ts +6 -0
  41. package/dist/src/auth/jwt-middleware.js +263 -13
  42. package/dist/src/auth/jwt-middleware.js.map +1 -1
  43. package/dist/src/auth/msal/msalConnectionSettings.d.ts +8 -0
  44. package/dist/src/auth/msal/msalHttpRetryHandlerHelper.d.ts +34 -0
  45. package/dist/src/auth/msal/msalHttpRetryHandlerHelper.js +126 -0
  46. package/dist/src/auth/msal/msalHttpRetryHandlerHelper.js.map +1 -0
  47. package/dist/src/auth/msal/msalTokenProvider.d.ts +1 -0
  48. package/dist/src/auth/msal/msalTokenProvider.js +23 -14
  49. package/dist/src/auth/msal/msalTokenProvider.js.map +1 -1
  50. package/dist/src/auth/settings.d.ts +18 -0
  51. package/dist/src/auth/settings.js +8 -2
  52. package/dist/src/auth/settings.js.map +1 -1
  53. package/dist/src/cloudAdapter.d.ts +29 -2
  54. package/dist/src/cloudAdapter.js +44 -2
  55. package/dist/src/cloudAdapter.js.map +1 -1
  56. package/dist/src/errorHelper.js +137 -101
  57. package/dist/src/errorHelper.js.map +1 -1
  58. package/dist/src/index.d.ts +1 -0
  59. package/dist/src/index.js +1 -0
  60. package/dist/src/index.js.map +1 -1
  61. package/dist/src/invoke/adaptiveCardInvokeValue.d.ts +5 -0
  62. package/dist/src/oauth/userTokenClient.d.ts +0 -1
  63. package/dist/src/oauth/userTokenClient.js +0 -1
  64. package/dist/src/oauth/userTokenClient.js.map +1 -1
  65. package/dist/src/observability/traces.d.ts +6 -0
  66. package/dist/src/observability/traces.js +20 -2
  67. package/dist/src/observability/traces.js.map +1 -1
  68. package/dist/src/outboundHostValidator.d.ts +47 -0
  69. package/dist/src/outboundHostValidator.js +149 -0
  70. package/dist/src/outboundHostValidator.js.map +1 -0
  71. package/dist/src/state/agentState.d.ts +5 -0
  72. package/dist/src/state/agentState.js +16 -3
  73. package/dist/src/state/agentState.js.map +1 -1
  74. package/package.json +3 -3
  75. package/src/agent-client/agentClient.ts +46 -23
  76. package/src/agent-client/agentResponseHandler.ts +14 -9
  77. package/src/agent-client/createAgentResponseHandler.ts +70 -9
  78. package/src/app/adaptiveCards/activityValueParsers.ts +3 -9
  79. package/src/app/adaptiveCards/adaptiveCardsActions.ts +52 -24
  80. package/src/app/agentApplication.ts +149 -77
  81. package/src/app/attachmentDownloader.ts +25 -5
  82. package/src/app/auth/handlers/agenticAuthorization.ts +1 -1
  83. package/src/app/auth/handlers/azureBotAuthorization.ts +1 -1
  84. package/src/app/auth/types.ts +3 -3
  85. package/src/app/index.ts +8 -0
  86. package/src/app/streaming/streamingResponse.ts +276 -22
  87. package/src/app/teamsAttachmentDownloader.ts +11 -1
  88. package/src/auth/authConfiguration.ts +12 -0
  89. package/src/auth/index.ts +2 -1
  90. package/src/auth/jwt-middleware.ts +298 -14
  91. package/src/auth/msal/msalConnectionSettings.ts +10 -0
  92. package/src/auth/msal/msalHttpRetryHandlerHelper.ts +161 -0
  93. package/src/auth/msal/msalTokenProvider.ts +32 -14
  94. package/src/auth/settings.ts +18 -3
  95. package/src/cloudAdapter.ts +56 -3
  96. package/src/errorHelper.ts +155 -114
  97. package/src/index.ts +1 -0
  98. package/src/invoke/adaptiveCardInvokeValue.ts +5 -0
  99. package/src/oauth/userTokenClient.ts +0 -1
  100. package/src/observability/traces.ts +19 -1
  101. package/src/outboundHostValidator.ts +172 -0
  102. 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
+ }
@@ -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
- // TODO: namespace needs to be added
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
- // TODO check ConversationState.ts line 40. This line below should follow the same pattern
156
- key = `${customKey!.channelId}/conversations/${customKey!.conversationId}`
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
  }