@microsoft/agents-hosting 1.9.0-beta.8.gf58e66b2ee → 1.9.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 (139) hide show
  1. package/README.md +182 -4
  2. package/dist/package.json +3 -3
  3. package/dist/src/app/agentApplication.d.ts +4 -3
  4. package/dist/src/app/agentApplication.js +24 -21
  5. package/dist/src/app/agentApplication.js.map +1 -1
  6. package/dist/src/app/agentApplicationBuilder.d.ts +9 -2
  7. package/dist/src/app/agentApplicationBuilder.js +9 -0
  8. package/dist/src/app/agentApplicationBuilder.js.map +1 -1
  9. package/dist/src/app/agentApplicationOptions.d.ts +16 -4
  10. package/dist/src/app/auth/authorizationManager.d.ts +0 -8
  11. package/dist/src/app/auth/authorizationManager.js +104 -178
  12. package/dist/src/app/auth/authorizationManager.js.map +1 -1
  13. package/dist/src/app/auth/handlerStorage.d.ts +5 -3
  14. package/dist/src/app/auth/handlerStorage.js +36 -10
  15. package/dist/src/app/auth/handlerStorage.js.map +1 -1
  16. package/dist/src/app/auth/types.d.ts +2 -2
  17. package/dist/src/app/index.d.ts +1 -0
  18. package/dist/src/app/proactive/proactive.js +10 -6
  19. package/dist/src/app/proactive/proactive.js.map +1 -1
  20. package/dist/src/app/proactive/proactiveOptions.d.ts +2 -2
  21. package/dist/src/app/rateLimit.d.ts +315 -0
  22. package/dist/src/app/rateLimit.js +333 -0
  23. package/dist/src/app/rateLimit.js.map +1 -0
  24. package/dist/src/app/turnState.d.ts +6 -3
  25. package/dist/src/app/turnState.js +43 -4
  26. package/dist/src/app/turnState.js.map +1 -1
  27. package/dist/src/auth/authConfiguration.d.ts +10 -3
  28. package/dist/src/auth/authConfiguration.js +292 -259
  29. package/dist/src/auth/authConfiguration.js.map +1 -1
  30. package/dist/src/auth/settings.d.ts +8 -70
  31. package/dist/src/auth/settings.js +5 -67
  32. package/dist/src/auth/settings.js.map +1 -1
  33. package/dist/src/cloudAdapter.d.ts +5 -0
  34. package/dist/src/cloudAdapter.js +13 -103
  35. package/dist/src/cloudAdapter.js.map +1 -1
  36. package/dist/src/configuration/botFrameworkEnvironmentCompatibility.d.ts +23 -0
  37. package/dist/src/configuration/botFrameworkEnvironmentCompatibility.js +183 -0
  38. package/dist/src/configuration/botFrameworkEnvironmentCompatibility.js.map +1 -0
  39. package/dist/src/configuration/configuration.d.ts +83 -0
  40. package/dist/src/configuration/configuration.js +836 -0
  41. package/dist/src/configuration/configuration.js.map +1 -0
  42. package/dist/src/configuration/configurationSource.d.ts +57 -0
  43. package/dist/src/configuration/configurationSource.js +7 -0
  44. package/dist/src/configuration/configurationSource.js.map +1 -0
  45. package/dist/src/configuration/environmentConfiguration.d.ts +14 -0
  46. package/dist/src/configuration/environmentConfiguration.js +271 -0
  47. package/dist/src/configuration/environmentConfiguration.js.map +1 -0
  48. package/dist/src/configuration/index.d.ts +7 -0
  49. package/dist/src/configuration/index.js +14 -0
  50. package/dist/src/configuration/index.js.map +1 -0
  51. package/dist/src/configuration/jsonFileConfigurationSource.d.ts +15 -0
  52. package/dist/src/configuration/jsonFileConfigurationSource.js +58 -0
  53. package/dist/src/configuration/jsonFileConfigurationSource.js.map +1 -0
  54. package/dist/src/createCloudAdapter.d.ts +30 -5
  55. package/dist/src/createCloudAdapter.js +17 -10
  56. package/dist/src/createCloudAdapter.js.map +1 -1
  57. package/dist/src/errorHelper.js +117 -5
  58. package/dist/src/errorHelper.js.map +1 -1
  59. package/dist/src/index.d.ts +1 -0
  60. package/dist/src/index.js +1 -0
  61. package/dist/src/index.js.map +1 -1
  62. package/dist/src/observability/traces.d.ts +6 -4
  63. package/dist/src/observability/traces.js +10 -9
  64. package/dist/src/observability/traces.js.map +1 -1
  65. package/dist/src/outboundHostValidator.d.ts +4 -1
  66. package/dist/src/outboundHostValidator.js +25 -48
  67. package/dist/src/outboundHostValidator.js.map +1 -1
  68. package/dist/src/state/agentState.d.ts +8 -3
  69. package/dist/src/state/agentState.js +26 -8
  70. package/dist/src/state/agentState.js.map +1 -1
  71. package/dist/src/state/conversationState.d.ts +2 -2
  72. package/dist/src/state/conversationState.js.map +1 -1
  73. package/dist/src/state/userState.d.ts +2 -2
  74. package/dist/src/state/userState.js.map +1 -1
  75. package/dist/src/statusCodes.d.ts +5 -0
  76. package/dist/src/statusCodes.js +5 -0
  77. package/dist/src/statusCodes.js.map +1 -1
  78. package/dist/src/storage/fileStorage.d.ts +83 -68
  79. package/dist/src/storage/fileStorage.js +279 -105
  80. package/dist/src/storage/fileStorage.js.map +1 -1
  81. package/dist/src/storage/index.d.ts +1 -0
  82. package/dist/src/storage/index.js +1 -0
  83. package/dist/src/storage/index.js.map +1 -1
  84. package/dist/src/storage/memoryStorage.d.ts +85 -60
  85. package/dist/src/storage/memoryStorage.js +244 -87
  86. package/dist/src/storage/memoryStorage.js.map +1 -1
  87. package/dist/src/storage/storage.d.ts +100 -2
  88. package/dist/src/storage/storage.js +25 -0
  89. package/dist/src/storage/storage.js.map +1 -1
  90. package/dist/src/storage/storageCompatibility.d.ts +17 -0
  91. package/dist/src/storage/storageCompatibility.js +157 -0
  92. package/dist/src/storage/storageCompatibility.js.map +1 -0
  93. package/dist/src/storage/storageExpiry.d.ts +6 -0
  94. package/dist/src/storage/storageExpiry.js +20 -0
  95. package/dist/src/storage/storageExpiry.js.map +1 -0
  96. package/dist/src/utils/env.d.ts +43 -0
  97. package/dist/src/utils/env.js +42 -6
  98. package/dist/src/utils/env.js.map +1 -1
  99. package/dist/src/utils.d.ts +1 -0
  100. package/dist/src/utils.js +10 -1
  101. package/dist/src/utils.js.map +1 -1
  102. package/package.json +3 -3
  103. package/src/app/agentApplication.ts +27 -14
  104. package/src/app/agentApplicationBuilder.ts +13 -2
  105. package/src/app/agentApplicationOptions.ts +18 -4
  106. package/src/app/auth/authorizationManager.ts +126 -204
  107. package/src/app/auth/handlerStorage.ts +51 -7
  108. package/src/app/auth/types.ts +2 -2
  109. package/src/app/index.ts +7 -0
  110. package/src/app/proactive/proactive.ts +19 -9
  111. package/src/app/proactive/proactiveOptions.ts +2 -2
  112. package/src/app/rateLimit.ts +659 -0
  113. package/src/app/turnState.ts +50 -7
  114. package/src/auth/authConfiguration.ts +415 -260
  115. package/src/auth/settings.ts +5 -82
  116. package/src/cloudAdapter.ts +38 -113
  117. package/src/configuration/botFrameworkEnvironmentCompatibility.ts +245 -0
  118. package/src/configuration/configuration.ts +1076 -0
  119. package/src/configuration/configurationSource.ts +76 -0
  120. package/src/configuration/environmentConfiguration.ts +377 -0
  121. package/src/configuration/index.ts +19 -0
  122. package/src/configuration/jsonFileConfigurationSource.ts +74 -0
  123. package/src/createCloudAdapter.ts +52 -9
  124. package/src/errorHelper.ts +143 -5
  125. package/src/index.ts +1 -0
  126. package/src/observability/traces.ts +8 -7
  127. package/src/outboundHostValidator.ts +37 -44
  128. package/src/state/agentState.ts +39 -13
  129. package/src/state/conversationState.ts +2 -2
  130. package/src/state/userState.ts +2 -2
  131. package/src/statusCodes.ts +6 -0
  132. package/src/storage/fileStorage.ts +299 -106
  133. package/src/storage/index.ts +1 -0
  134. package/src/storage/memoryStorage.ts +268 -85
  135. package/src/storage/storage.ts +114 -2
  136. package/src/storage/storageCompatibility.ts +185 -0
  137. package/src/storage/storageExpiry.ts +21 -0
  138. package/src/utils/env.ts +63 -0
  139. package/src/utils.ts +10 -0
@@ -448,6 +448,14 @@ export const Errors: { [key: string]: AgentErrorDefinition } = {
448
448
  description: 'MSAL returned an unparsable response body'
449
449
  },
450
450
 
451
+ /**
452
+ * Error thrown when a security-sensitive boolean configuration value is invalid.
453
+ */
454
+ InvalidBooleanConfigurationValue: {
455
+ code: -120394,
456
+ description: 'Configuration source "{sourceName}" returned an invalid boolean for "{path}". Expected one of true/false/1/0.'
457
+ },
458
+
451
459
  FailedToAcquireTokenUsingCertificate: {
452
460
  code: -120395,
453
461
  description: 'Failed to acquire token using certificate'
@@ -578,11 +586,11 @@ export const Errors: { [key: string]: AgentErrorDefinition } = {
578
586
  },
579
587
 
580
588
  /**
581
- * Error thrown when authorization option is not available in app options.
589
+ * Error thrown when no authorization handlers were resolved.
582
590
  */
583
591
  AuthorizationOptionNotAvailable: {
584
592
  code: -120530,
585
- description: 'The Application.authorization property is unavailable because no authorization options were configured.'
593
+ description: 'The Application.authorization property is unavailable because no authorization handlers were resolved.'
586
594
  },
587
595
 
588
596
  /**
@@ -790,14 +798,60 @@ export const Errors: { [key: string]: AgentErrorDefinition } = {
790
798
  description: 'Changes are required when writing.'
791
799
  },
792
800
 
793
- /**
794
- * Error thrown when storage option is not available in app options.
795
- */
801
+ /** Error thrown when a V2 storage key is empty or whitespace. */
802
+ StorageV2KeyRequired: {
803
+ code: -120703,
804
+ description: 'Storage V2 keys must be non-empty strings.'
805
+ },
806
+
807
+ /** Error thrown when the legacy storage adapter cannot honor a V2 option. */
808
+ StorageV2UnsupportedOption: {
809
+ code: -120704,
810
+ description: 'The legacy storage adapter does not support the V2 storage option "{option}".'
811
+ },
812
+
813
+ /** Error thrown when an empty V2 storage version token is provided. */
814
+ StorageV2ExpectedVersionEmpty: {
815
+ code: -120706,
816
+ description: 'Storage V2 expectedVersion cannot be empty.'
817
+ },
818
+
819
+ /** Error thrown when a V2 storage operation returns a failed or missing result. */
820
+ StorageV2OperationFailed: {
821
+ code: -120707,
822
+ description: 'Storage V2 {operation} failed for key "{key}" with status "{status}".'
823
+ },
824
+
825
+ /** Error thrown when a V2 value is not a non-array object. */
826
+ StorageV2ValueRequired: {
827
+ code: -120708,
828
+ description: 'Storage V2 values must be non-null, non-array objects.'
829
+ },
830
+
831
+ /** Error thrown when keys are required for storage delete operations. */
832
+ StorageDeleteKeysRequired: {
833
+ code: -120709,
834
+ description: 'Keys are required when deleting.'
835
+ },
836
+
837
+ /** Error thrown when storage option is not available in app options. */
796
838
  StorageOptionNotAvailable: {
797
839
  code: -120710,
798
840
  description: "The 'storage' option is not available in the app options. Ensure that the app is properly configured.",
799
841
  },
800
842
 
843
+ /** Error thrown when a V2 write mode is not supported. */
844
+ StorageV2WriteModeUnsupported: {
845
+ code: -120711,
846
+ description: 'Storage V2 write mode "{mode}" is not supported.'
847
+ },
848
+
849
+ /** Error thrown when another turn saved state after this turn loaded it. */
850
+ AgentStateWriteConflict: {
851
+ code: -120712,
852
+ description: "AgentState '{name}' could not save key '{key}' because another turn updated the state first (status: {status}). This turn's state changes were not saved."
853
+ },
854
+
801
855
  /**
802
856
  * Error thrown when state is not loaded.
803
857
  */
@@ -1043,6 +1097,90 @@ export const Errors: { [key: string]: AgentErrorDefinition } = {
1043
1097
  description: 'The Entra Agent ID sidecar returned an unsupported authorization scheme `{scheme}`. Only the Bearer scheme is supported'
1044
1098
  },
1045
1099
 
1100
+ // ============================================================================
1101
+ // Configuration Source Errors (-120812 to -120827)
1102
+ // ============================================================================
1103
+
1104
+ ConfigurationAlreadyConsumed: {
1105
+ code: -120812,
1106
+ description: 'Configuration sources must be preloaded before the first configuration consumer is created.'
1107
+ },
1108
+
1109
+ ConfigurationSourceNameRequired: {
1110
+ code: -120813,
1111
+ description: 'Configuration source name must be a non-empty string.'
1112
+ },
1113
+
1114
+ DuplicateConfigurationSource: {
1115
+ code: -120814,
1116
+ description: 'Configuration source name `{sourceName}` is registered more than once.'
1117
+ },
1118
+
1119
+ ConfigurationSourceLoadFailed: {
1120
+ code: -120815,
1121
+ description: 'Configuration source `{sourceName}` failed to load.'
1122
+ },
1123
+
1124
+ InvalidConfigurationPath: {
1125
+ code: -120816,
1126
+ description: 'Configuration source `{sourceName}` returned unsupported canonical path `{path}`.'
1127
+ },
1128
+
1129
+ InvalidConfigurationValue: {
1130
+ code: -120817,
1131
+ description: 'Configuration source `{sourceName}` returned an invalid value for canonical path `{path}`.'
1132
+ },
1133
+
1134
+ ConfigurationPreloadInProgress: {
1135
+ code: -120818,
1136
+ description: 'Another configuration preload is already in progress.'
1137
+ },
1138
+
1139
+ InvalidConnectionMapEntry: {
1140
+ code: -120819,
1141
+ description: 'Connection map entry at index {index} must define both serviceUrl and connection.'
1142
+ },
1143
+
1144
+ ConfigurationAlreadyPreloaded: {
1145
+ code: -120820,
1146
+ description: 'Configuration sources have already been preloaded for this process.'
1147
+ },
1148
+
1149
+ UnsupportedRuntimeConfigurationField: {
1150
+ code: -120821,
1151
+ description: 'Configuration source `{sourceName}` returned field `{path}`, which is not supported by the JavaScript SDK.'
1152
+ },
1153
+
1154
+ InvalidConfigurationSourceResult: {
1155
+ code: -120822,
1156
+ description: 'Configuration source `{sourceName}` returned an invalid source result.'
1157
+ },
1158
+
1159
+ InvalidConfigurationContext: {
1160
+ code: -120823,
1161
+ description: 'The configuration context is invalid. Create contexts with createConfigurationContext().'
1162
+ },
1163
+
1164
+ InvalidConfigurationSourceMode: {
1165
+ code: -120824,
1166
+ description: 'Configuration source `{sourceName}` uses unsupported mode `{mode}`. Use `fallback`, `overrideEnvironment`, or `enforce`.'
1167
+ },
1168
+
1169
+ JsonConfigurationFileReadFailed: {
1170
+ code: -120825,
1171
+ description: 'JSON configuration file `{filePath}` could not be read.'
1172
+ },
1173
+
1174
+ InvalidJsonConfigurationFile: {
1175
+ code: -120826,
1176
+ description: 'JSON configuration file `{filePath}` does not contain valid JSON.'
1177
+ },
1178
+
1179
+ JsonConfigurationDocumentRequired: {
1180
+ code: -120827,
1181
+ description: 'JSON configuration file `{filePath}` must contain an object at the document root.'
1182
+ },
1183
+
1046
1184
  // ============================================================================
1047
1185
  // Application Configuration Errors (-120850 to -120902)
1048
1186
  // ============================================================================
package/src/index.ts CHANGED
@@ -8,6 +8,7 @@ export { authorizeJWT, buildJwksUri, clearJwksClients } from './auth/jwt-middlew
8
8
 
9
9
  export * from './app'
10
10
  export * from './cards'
11
+ export * from './configuration'
11
12
  export * from './connector-client'
12
13
  export { Errors as HostingErrors } from './errorHelper'
13
14
  export * from './invoke'
@@ -5,7 +5,8 @@ import { SpanNames, trace } from '@microsoft/agents-telemetry'
5
5
  import { HostingMetrics } from './metrics'
6
6
  import { Activity, ConversationReference } from '@microsoft/agents-activity'
7
7
  import { HandlerStorage } from '../app/auth/handlerStorage'
8
- import type { Storage } from '../storage'
8
+ import type { StorageProvider } from '../storage'
9
+ import { asStorageV2 } from '../storage/storageCompatibility'
9
10
 
10
11
  export const AgentApplicationTraceDefinitions = {
11
12
  run: trace.define({
@@ -279,8 +280,8 @@ export const ProactiveTraceDefinitions = {
279
280
  conversationId: '',
280
281
  },
281
282
  actions: ({ span }) => ({
282
- async link (storage: Storage, key: string) {
283
- const item = (await storage.read([key]))?.[key] ?? {}
283
+ async link (storage: StorageProvider, key: string) {
284
+ const item = ((await asStorageV2(storage).read([key]))?.[key]?.value ?? {}) as { __link?: unknown }
284
285
  return link(span, item)
285
286
  }
286
287
  }),
@@ -333,8 +334,8 @@ export const ProactiveTraceDefinitions = {
333
334
  activityType: '',
334
335
  },
335
336
  actions: ({ span }) => ({
336
- async link (storage: Storage, key: string) {
337
- const item = (await storage.read([key]))?.[key] ?? {}
337
+ async link (storage: StorageProvider, key: string) {
338
+ const item = ((await asStorageV2(storage).read([key]))?.[key]?.value ?? {}) as { __link?: unknown }
338
339
  link(span, item)
339
340
  }
340
341
  }),
@@ -367,8 +368,8 @@ export const ProactiveTraceDefinitions = {
367
368
  hasAutoSignIn: false,
368
369
  },
369
370
  actions: ({ span }) => ({
370
- async link (storage: Storage, key: string) {
371
- const item = (await storage.read([key]))?.[key] ?? {}
371
+ async link (storage: StorageProvider, key: string) {
372
+ const item = ((await asStorageV2(storage).read([key]))?.[key]?.value ?? {}) as { __link?: unknown }
372
373
  link(span, item)
373
374
  }
374
375
  }),
@@ -3,7 +3,12 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { parseBooleanEnv } from './utils/env'
6
+ import {
7
+ ConfigurationContext,
8
+ getConfigurationSnapshot
9
+ } from './configuration/configuration'
10
+ import { loadModernEnvironmentConfiguration } from './configuration/environmentConfiguration'
11
+ import { mergeDefined } from './utils'
7
12
 
8
13
  /** Hosts used by Microsoft channel callbacks and hosted attachments. */
9
14
  const DEFAULT_MICROSOFT_HOSTS = Object.freeze([
@@ -17,11 +22,11 @@ const DEFAULT_MICROSOFT_HOSTS = Object.freeze([
17
22
  'blob.core.windows.net'
18
23
  ])
19
24
 
20
- const OUTBOUND_HOST_VALIDATOR_ENV_PREFIX = 'OutboundHostValidator__'
21
- const OUTBOUND_HOST_VALIDATOR_ENV_PREFIX_UPPER = OUTBOUND_HOST_VALIDATOR_ENV_PREFIX.toUpperCase()
22
-
23
25
  /** Configuration for the shared outbound-host allowlist. */
24
26
  export interface OutboundHostValidatorOptions {
27
+ /** Optional host-scoped external configuration. */
28
+ configurationContext?: ConfigurationContext
29
+
25
30
  /** Enables allowlist enforcement. Defaults to `false`. */
26
31
  enabled?: boolean
27
32
 
@@ -56,14 +61,17 @@ export class OutboundHostValidator implements OutboundUrlPolicy {
56
61
  private readonly suffixes: ReadonlySet<string>
57
62
 
58
63
  public constructor (options: OutboundHostValidatorOptions = {}) {
59
- this.enabled = options.enabled ?? false
64
+ const resolved = options.configurationContext
65
+ ? resolveOutboundHostValidatorOptions(options)
66
+ : options
67
+ this.enabled = resolved.enabled ?? false
60
68
 
61
69
  const suffixes = new Set<string>()
62
- if (options.includeDefaultMicrosoftHosts ?? true) {
70
+ if (resolved.includeDefaultMicrosoftHosts ?? true) {
63
71
  for (const host of DEFAULT_MICROSOFT_HOSTS) suffixes.add(host)
64
72
  }
65
73
 
66
- for (const host of options.hosts ?? []) {
74
+ for (const host of resolved.hosts ?? []) {
67
75
  const normalized = normalizeConfiguredHost(host)
68
76
  if (normalized) suffixes.add(normalized)
69
77
  }
@@ -93,47 +101,32 @@ export class OutboundHostValidator implements OutboundUrlPolicy {
93
101
  * indexed values such as `Hosts__0`, `Hosts__1`, and so on.
94
102
  */
95
103
  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
104
+ return { ...loadModernEnvironmentConfiguration().outboundHostValidator }
105
+ }
125
106
 
126
- return result
107
+ /** Creates a validator using the shared configuration precedence. */
108
+ export function createOutboundHostValidator (options: OutboundHostValidatorOptions = {}): OutboundHostValidator {
109
+ return new OutboundHostValidator(resolveOutboundHostValidatorOptions(options))
127
110
  }
128
111
 
129
- /** Creates the default validator, with explicit settings overriding the environment. */
130
- export function createOutboundHostValidator (options?: OutboundHostValidatorOptions): OutboundHostValidator {
112
+ function resolveOutboundHostValidatorOptions (
113
+ options: OutboundHostValidatorOptions
114
+ ): OutboundHostValidatorOptions {
115
+ const external = getConfigurationSnapshot(options.configurationContext)
131
116
  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
- })
117
+ const direct: OutboundHostValidatorOptions = {}
118
+ if (options.enabled !== undefined) direct.enabled = options.enabled
119
+ if (options.includeDefaultMicrosoftHosts !== undefined) {
120
+ direct.includeDefaultMicrosoftHosts = options.includeDefaultMicrosoftHosts
121
+ }
122
+ if (options.hosts !== undefined) direct.hosts = options.hosts
123
+ return mergeDefined<OutboundHostValidatorOptions>(
124
+ external.fallback.outboundHostValidator,
125
+ fromEnv,
126
+ external.overrideEnvironment.outboundHostValidator,
127
+ direct,
128
+ external.enforce.outboundHostValidator
129
+ )
137
130
  }
138
131
 
139
132
  function getUrlHost (input: string | URL | null | undefined): string | undefined {
@@ -3,7 +3,14 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { Storage, StorageKeyFactory, StoreItem } from '../storage/storage'
6
+ import { StorageKeyFactory, StorageOperationStatus, StorageProvider, StorageV2, StorageWriteMode, StoreItem } from '../storage/storage'
7
+ import {
8
+ asStorageV2,
9
+ assertAgentStateWriteSucceeded,
10
+ assertStorageDeleteSucceeded,
11
+ getStorageReadValue,
12
+ isStorageV2,
13
+ } from '../storage/storageCompatibility'
7
14
  import { TurnContext } from '../turnContext'
8
15
  import { createHash } from 'node:crypto'
9
16
  import { AgentStatePropertyAccessor } from './agentStatePropertyAccesor'
@@ -26,6 +33,10 @@ export interface CachedAgentState {
26
33
  * Hash of the state used to detect changes
27
34
  */
28
35
  hash: string;
36
+ /** Version returned when the state was loaded, used to reject stale V2 writes. */
37
+ version?: string;
38
+ /** Whether the state was not found when it was loaded. */
39
+ isNew?: boolean;
29
40
  }
30
41
 
31
42
  /**
@@ -62,13 +73,17 @@ export interface CustomKey {
62
73
  export class AgentState {
63
74
  private readonly stateKey = Symbol('state')
64
75
 
76
+ private get internalStorage (): StorageV2 {
77
+ return asStorageV2(this.storage)
78
+ }
79
+
65
80
  /**
66
81
  * Creates a new instance of AgentState.
67
82
  *
68
83
  * @param storage The storage provider used to persist state between turns
69
84
  * @param storageKey A factory function that generates keys for storing state data
70
85
  */
71
- constructor (protected storage: Storage, protected storageKey: StorageKeyFactory) { }
86
+ constructor (protected storage: StorageProvider, protected storageKey: StorageKeyFactory) {}
72
87
 
73
88
  /**
74
89
  * Creates a property accessor for the specified property.
@@ -101,11 +116,15 @@ export class AgentState {
101
116
  if (force || !cached || !cached.state) {
102
117
  const key: string = await this.getStorageOrCustomKey(customKey, context)
103
118
  logger.info(`Reading storage with key ${key}`)
104
- const storedItem = await this.storage.read([key])
105
-
106
- const state: any = storedItem[key] || {}
119
+ const storedItems = await this.internalStorage.read<Record<string, any>>([key])
120
+ const state: any = getStorageReadValue(storedItems, key) ?? {}
107
121
  const hash: string = this.calculateChangeHash(state)
108
- context.turnState.set(this.stateKey, { state, hash })
122
+ context.turnState.set(this.stateKey, {
123
+ state,
124
+ hash,
125
+ version: storedItems[key]?.version,
126
+ isNew: storedItems[key]?.status === StorageOperationStatus.NotFound,
127
+ })
109
128
 
110
129
  return state
111
130
  }
@@ -130,15 +149,21 @@ export class AgentState {
130
149
  if (!cached) {
131
150
  cached = { state: {}, hash: '' }
132
151
  }
133
- cached.state.eTag = '*'
134
- const changes: StoreItem = {} as StoreItem
135
-
136
152
  const key: string = await this.getStorageOrCustomKey(customKey, context)
137
153
 
138
- changes[key] = cached.state
139
-
140
154
  logger.info(`Writing storage with key ${key}`)
141
- await this.storage.write(changes)
155
+ const results = await this.internalStorage.write(
156
+ { [key]: cached.state },
157
+ cached.version !== undefined
158
+ ? { expectedVersion: cached.version }
159
+ : cached.isNew && isStorageV2(this.storage)
160
+ ? { mode: StorageWriteMode.CreateOnly }
161
+ : undefined
162
+ )
163
+ assertAgentStateWriteSucceeded(results, key, this.constructor.name)
164
+ const result = results[key]
165
+ cached.version = result.version
166
+ cached.isNew = false
142
167
  cached.hash = this.calculateChangeHash(cached.state)
143
168
  context.turnState.set(this.stateKey, cached)
144
169
  }
@@ -195,7 +220,8 @@ export class AgentState {
195
220
  }
196
221
  const key = await this.getStorageOrCustomKey(customKey, context)
197
222
  logger.info(`Deleting storage with key ${key}`)
198
- await this.storage.delete([key])
223
+ const results = await this.internalStorage.delete([key])
224
+ assertStorageDeleteSucceeded(results, [key])
199
225
  }
200
226
 
201
227
  /**
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  import { AgentState } from './agentState'
7
- import { Storage } from '../storage/storage'
7
+ import { StorageProvider } from '../storage/storage'
8
8
  import { TurnContext } from '../turnContext'
9
9
  import { Activity, ExceptionHelper } from '@microsoft/agents-activity'
10
10
  import { Errors } from '../errorHelper'
@@ -17,7 +17,7 @@ export class ConversationState extends AgentState {
17
17
  * Creates a new instance of ConversationState.
18
18
  * @param storage The storage provider.
19
19
  */
20
- constructor (storage: Storage, private readonly namespace: string = '') {
20
+ constructor (storage: StorageProvider, private readonly namespace: string = '') {
21
21
  super(storage, (context: TurnContext) => {
22
22
  const key: string = this.getStorageKey(context)
23
23
 
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  import { AgentState } from './agentState'
7
- import { Storage } from '../storage/storage'
7
+ import { StorageProvider } from '../storage/storage'
8
8
  import { TurnContext } from '../turnContext'
9
9
  import { Activity, ExceptionHelper } from '@microsoft/agents-activity'
10
10
  import { Errors } from '../errorHelper'
@@ -17,7 +17,7 @@ export class UserState extends AgentState {
17
17
  * Creates a new instance of UserState.
18
18
  * @param storage The storage provider.
19
19
  */
20
- constructor (storage: Storage, private readonly namespace: string = '') {
20
+ constructor (storage: StorageProvider, private readonly namespace: string = '') {
21
21
  super(storage, (context: TurnContext) => {
22
22
  const key: string = this.getStorageKey(context)
23
23
 
@@ -78,6 +78,12 @@ export enum StatusCodes {
78
78
  */
79
79
  UPGRADE_REQUIRED = 426,
80
80
 
81
+ /**
82
+ * The user has sent too many requests in a given amount of time.
83
+ * Used when rate limiting or throttling prevents a turn from being processed.
84
+ */
85
+ TOO_MANY_REQUESTS = 429,
86
+
81
87
  /**
82
88
  * The server encountered an unexpected condition that prevented it from fulfilling the request.
83
89
  * Generic error message when an unexpected agent hosting error occurs