@microsoft/agents-hosting 1.10.0-beta.8.g158a2c07ad → 1.10.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 (169) hide show
  1. package/README.md +41 -2
  2. package/dist/package.json +3 -3
  3. package/dist/src/activityHandler.js +6 -3
  4. package/dist/src/activityHandler.js.map +1 -1
  5. package/dist/src/agent-client/agentResponseHandler.d.ts +7 -0
  6. package/dist/src/agent-client/agentResponseHandler.js.map +1 -1
  7. package/dist/src/agent-client/createAgentResponseHandler.d.ts +2 -0
  8. package/dist/src/agent-client/createAgentResponseHandler.js.map +1 -1
  9. package/dist/src/app/adaptiveCards/adaptiveCardsActions.d.ts +1 -0
  10. package/dist/src/app/adaptiveCards/adaptiveCardsActions.js +1 -0
  11. package/dist/src/app/adaptiveCards/adaptiveCardsActions.js.map +1 -1
  12. package/dist/src/app/attachmentDownloadHttpClient.d.ts +13 -0
  13. package/dist/src/app/attachmentDownloadHttpClient.js +80 -0
  14. package/dist/src/app/attachmentDownloadHttpClient.js.map +1 -0
  15. package/dist/src/app/attachmentDownloader.d.ts +2 -0
  16. package/dist/src/app/attachmentDownloader.js +6 -11
  17. package/dist/src/app/attachmentDownloader.js.map +1 -1
  18. package/dist/src/app/auth/authorization.d.ts +58 -0
  19. package/dist/src/app/auth/authorization.js.map +1 -1
  20. package/dist/src/app/proactive/conversation.d.ts +18 -0
  21. package/dist/src/app/proactive/conversation.js +6 -0
  22. package/dist/src/app/proactive/conversation.js.map +1 -1
  23. package/dist/src/app/proactive/conversationBuilder.js +2 -1
  24. package/dist/src/app/proactive/conversationBuilder.js.map +1 -1
  25. package/dist/src/app/proactive/conversationReferenceBuilder.js +8 -4
  26. package/dist/src/app/proactive/conversationReferenceBuilder.js.map +1 -1
  27. package/dist/src/app/proactive/createConversationOptionsBuilder.d.ts +1 -0
  28. package/dist/src/app/proactive/createConversationOptionsBuilder.js +2 -1
  29. package/dist/src/app/proactive/createConversationOptionsBuilder.js.map +1 -1
  30. package/dist/src/app/proactive/proactive.d.ts +6 -0
  31. package/dist/src/app/proactive/proactive.js +32 -1
  32. package/dist/src/app/proactive/proactive.js.map +1 -1
  33. package/dist/src/app/routeList.d.ts +21 -0
  34. package/dist/src/app/routeList.js +27 -3
  35. package/dist/src/app/routeList.js.map +1 -1
  36. package/dist/src/app/teamsAttachmentDownloader.d.ts +8 -0
  37. package/dist/src/app/teamsAttachmentDownloader.js +17 -12
  38. package/dist/src/app/teamsAttachmentDownloader.js.map +1 -1
  39. package/dist/src/auth/authConfiguration.d.ts +1 -0
  40. package/dist/src/auth/authConfiguration.js.map +1 -1
  41. package/dist/src/auth/authConstants.d.ts +7 -0
  42. package/dist/src/auth/authConstants.js +7 -0
  43. package/dist/src/auth/authConstants.js.map +1 -1
  44. package/dist/src/auth/authProvider.d.ts +36 -0
  45. package/dist/src/auth/connectionManager.js +2 -1
  46. package/dist/src/auth/connectionManager.js.map +1 -1
  47. package/dist/src/auth/connections.d.ts +1 -0
  48. package/dist/src/auth/msal/msalConnectionManager.d.ts +7 -0
  49. package/dist/src/auth/msal/msalConnectionManager.js +7 -0
  50. package/dist/src/auth/msal/msalConnectionManager.js.map +1 -1
  51. package/dist/src/auth/msal/msalTokenProvider.d.ts +44 -0
  52. package/dist/src/auth/msal/msalTokenProvider.js +43 -0
  53. package/dist/src/auth/msal/msalTokenProvider.js.map +1 -1
  54. package/dist/src/auth/settings.d.ts +28 -0
  55. package/dist/src/auth/settings.js +23 -0
  56. package/dist/src/auth/settings.js.map +1 -1
  57. package/dist/src/auth/sidecar/sidecarAuthProvider.d.ts +3 -0
  58. package/dist/src/auth/sidecar/sidecarAuthProvider.js.map +1 -1
  59. package/dist/src/auth/sidecar/sidecarHttpClient.d.ts +0 -8
  60. package/dist/src/auth/sidecar/sidecarHttpClient.js +2 -68
  61. package/dist/src/auth/sidecar/sidecarHttpClient.js.map +1 -1
  62. package/dist/src/cards/taskModuleAction.d.ts +6 -0
  63. package/dist/src/cards/taskModuleAction.js +6 -0
  64. package/dist/src/cards/taskModuleAction.js.map +1 -1
  65. package/dist/src/cloudAdapter.js +16 -8
  66. package/dist/src/cloudAdapter.js.map +1 -1
  67. package/dist/src/configuration/configuration.d.ts +1 -0
  68. package/dist/src/configuration/configuration.js +2 -1
  69. package/dist/src/configuration/configuration.js.map +1 -1
  70. package/dist/src/configuration/environmentConfiguration.js +1 -1
  71. package/dist/src/configuration/environmentConfiguration.js.map +1 -1
  72. package/dist/src/connector-client/connectorClient.d.ts +8 -0
  73. package/dist/src/connector-client/connectorClient.js +8 -0
  74. package/dist/src/connector-client/connectorClient.js.map +1 -1
  75. package/dist/src/createCloudAdapter.d.ts +2 -0
  76. package/dist/src/createCloudAdapter.js.map +1 -1
  77. package/dist/src/errorHelper.d.ts +1 -0
  78. package/dist/src/errorHelper.js +9 -0
  79. package/dist/src/errorHelper.js.map +1 -1
  80. package/dist/src/headerPropagation.d.ts +38 -0
  81. package/dist/src/headerPropagation.js +33 -0
  82. package/dist/src/headerPropagation.js.map +1 -1
  83. package/dist/src/httpClient.d.ts +58 -0
  84. package/dist/src/httpClient.js +40 -0
  85. package/dist/src/httpClient.js.map +1 -1
  86. package/dist/src/interfaces/webResponse.d.ts +7 -0
  87. package/dist/src/middlewareSet.d.ts +6 -0
  88. package/dist/src/middlewareSet.js.map +1 -1
  89. package/dist/src/oauth/userTokenClient.d.ts +6 -0
  90. package/dist/src/oauth/userTokenClient.js +12 -0
  91. package/dist/src/oauth/userTokenClient.js.map +1 -1
  92. package/dist/src/outboundHostValidator.d.ts +33 -1
  93. package/dist/src/outboundHostValidator.js +60 -25
  94. package/dist/src/outboundHostValidator.js.map +1 -1
  95. package/dist/src/storage/fileStorage.d.ts +26 -0
  96. package/dist/src/storage/fileStorage.js +35 -6
  97. package/dist/src/storage/fileStorage.js.map +1 -1
  98. package/dist/src/storage/memoryStorage.d.ts +34 -0
  99. package/dist/src/storage/memoryStorage.js +41 -4
  100. package/dist/src/storage/memoryStorage.js.map +1 -1
  101. package/dist/src/storage/storage.d.ts +20 -0
  102. package/dist/src/storage/storage.js +7 -0
  103. package/dist/src/storage/storage.js.map +1 -1
  104. package/dist/src/storage/storageCompatibility.js +12 -6
  105. package/dist/src/storage/storageCompatibility.js.map +1 -1
  106. package/dist/src/storage/storageExpiry.d.ts +7 -0
  107. package/dist/src/storage/storageExpiry.js +7 -0
  108. package/dist/src/storage/storageExpiry.js.map +1 -1
  109. package/dist/src/turnContext.d.ts +10 -0
  110. package/dist/src/turnContext.js +5 -0
  111. package/dist/src/turnContext.js.map +1 -1
  112. package/dist/src/utils/env.js +22 -11
  113. package/dist/src/utils/env.js.map +1 -1
  114. package/dist/src/utils/ipAddress.d.ts +6 -0
  115. package/dist/src/utils/ipAddress.js +64 -0
  116. package/dist/src/utils/ipAddress.js.map +1 -0
  117. package/dist/src/utils/outboundUrl.d.ts +7 -0
  118. package/dist/src/utils/outboundUrl.js +52 -0
  119. package/dist/src/utils/outboundUrl.js.map +1 -0
  120. package/dist/src/utils.js +2 -1
  121. package/dist/src/utils.js.map +1 -1
  122. package/package.json +3 -3
  123. package/src/activityHandler.ts +9 -3
  124. package/src/agent-client/agentResponseHandler.ts +7 -0
  125. package/src/agent-client/createAgentResponseHandler.ts +2 -0
  126. package/src/app/adaptiveCards/adaptiveCardsActions.ts +1 -0
  127. package/src/app/attachmentDownloadHttpClient.ts +80 -0
  128. package/src/app/attachmentDownloader.ts +6 -9
  129. package/src/app/auth/authorization.ts +58 -0
  130. package/src/app/proactive/conversation.ts +24 -0
  131. package/src/app/proactive/conversationBuilder.ts +3 -1
  132. package/src/app/proactive/conversationReferenceBuilder.ts +12 -4
  133. package/src/app/proactive/createConversationOptionsBuilder.ts +5 -1
  134. package/src/app/proactive/proactive.ts +33 -1
  135. package/src/app/routeList.ts +30 -3
  136. package/src/app/teamsAttachmentDownloader.ts +17 -10
  137. package/src/auth/authConfiguration.ts +1 -0
  138. package/src/auth/authConstants.ts +7 -0
  139. package/src/auth/authProvider.ts +36 -0
  140. package/src/auth/connectionManager.ts +3 -1
  141. package/src/auth/connections.ts +1 -0
  142. package/src/auth/msal/msalConnectionManager.ts +7 -0
  143. package/src/auth/msal/msalTokenProvider.ts +59 -0
  144. package/src/auth/settings.ts +28 -0
  145. package/src/auth/sidecar/sidecarAuthProvider.ts +4 -0
  146. package/src/auth/sidecar/sidecarHttpClient.ts +2 -62
  147. package/src/cards/taskModuleAction.ts +6 -0
  148. package/src/cloudAdapter.ts +30 -10
  149. package/src/configuration/configuration.ts +4 -2
  150. package/src/configuration/environmentConfiguration.ts +7 -1
  151. package/src/connector-client/connectorClient.ts +8 -0
  152. package/src/createCloudAdapter.ts +2 -0
  153. package/src/errorHelper.ts +11 -0
  154. package/src/headerPropagation.ts +38 -0
  155. package/src/httpClient.ts +58 -0
  156. package/src/interfaces/webResponse.ts +7 -0
  157. package/src/middlewareSet.ts +6 -0
  158. package/src/oauth/userTokenClient.ts +13 -0
  159. package/src/outboundHostValidator.ts +84 -23
  160. package/src/storage/fileStorage.ts +46 -6
  161. package/src/storage/memoryStorage.ts +45 -4
  162. package/src/storage/storage.ts +20 -0
  163. package/src/storage/storageCompatibility.ts +18 -6
  164. package/src/storage/storageExpiry.ts +7 -0
  165. package/src/turnContext.ts +11 -0
  166. package/src/utils/env.ts +33 -11
  167. package/src/utils/ipAddress.ts +63 -0
  168. package/src/utils/outboundUrl.ts +50 -0
  169. package/src/utils.ts +3 -1
@@ -33,6 +33,7 @@ interface MemoryStorageState {
33
33
  class MemoryStorageInternals {
34
34
  private static readonly states = new WeakMap<object, MemoryStorageState>()
35
35
 
36
+ /** The shared values and metadata for this storage instance. */
36
37
  readonly state: MemoryStorageState
37
38
 
38
39
  /**
@@ -47,24 +48,53 @@ class MemoryStorageInternals {
47
48
  this.state = state
48
49
  }
49
50
 
51
+ /**
52
+ * Saves a value and updates its version and expiration metadata.
53
+ *
54
+ * @param key The storage key.
55
+ * @param item The value to store.
56
+ * @param expiresAt The optional expiration time, expressed as Unix time in milliseconds.
57
+ * @returns The new version.
58
+ */
50
59
  save (key: string, item: unknown, expiresAt?: number): string {
51
60
  const version = (this.state.etag++).toString()
52
61
  this.state.memory[key] = JSON.stringify(item)
53
62
  this.state.versions[key] = version
54
- if (expiresAt === undefined) delete this.state.expirations[key]
55
- else this.state.expirations[key] = expiresAt
63
+ if (expiresAt === undefined) {
64
+ delete this.state.expirations[key]
65
+ } else {
66
+ this.state.expirations[key] = expiresAt
67
+ }
56
68
  return version
57
69
  }
58
70
 
71
+ /**
72
+ * Gets the version associated with a stored value.
73
+ *
74
+ * @param key The storage key.
75
+ * @param value The stored value.
76
+ * @returns The version, when available.
77
+ */
59
78
  getVersion (key: string, value: StoreItem): string | undefined {
60
79
  return this.state.versions[key] ?? value.eTag as string | undefined
61
80
  }
62
81
 
82
+ /**
83
+ * Determines whether a stored value has expired.
84
+ *
85
+ * @param key The storage key.
86
+ * @returns `true` when the value has expired; otherwise, `false`.
87
+ */
63
88
  isExpired (key: string): boolean {
64
89
  const expiresAt = this.state.expirations[key]
65
90
  return expiresAt !== undefined && expiresAt <= Date.now()
66
91
  }
67
92
 
93
+ /**
94
+ * Removes a value and its version and expiration metadata.
95
+ *
96
+ * @param key The storage key.
97
+ */
68
98
  remove (key: string): void {
69
99
  delete this.state.memory[key]
70
100
  delete this.state.versions[key]
@@ -81,6 +111,11 @@ class MemoryStorageInternals {
81
111
  export class MemoryStorage extends MemoryStorageInternals implements Storage {
82
112
  private static instance: MemoryStorage
83
113
 
114
+ /**
115
+ * Creates a legacy in-memory storage provider.
116
+ *
117
+ * @param memory Optional backing store to share with another in-memory storage instance.
118
+ */
84
119
  constructor (memory: { [key: string]: string } = {}) {
85
120
  super(memory)
86
121
  }
@@ -160,8 +195,11 @@ export class MemoryStorage extends MemoryStorageInternals implements Storage {
160
195
  })
161
196
  }
162
197
 
198
+ /** Returns the process-wide legacy memory storage instance. */
163
199
  static getSingleInstance (): MemoryStorage {
164
- if (!MemoryStorage.instance) MemoryStorage.instance = new MemoryStorage()
200
+ if (!MemoryStorage.instance) {
201
+ MemoryStorage.instance = new MemoryStorage()
202
+ }
165
203
  return MemoryStorage.instance
166
204
  }
167
205
  }
@@ -323,8 +361,11 @@ export class MemoryStorageV2 extends StorageV2 {
323
361
  })
324
362
  }
325
363
 
364
+ /** Returns the process-wide V2 memory storage instance. */
326
365
  static getSingleInstance (): MemoryStorageV2 {
327
- if (!MemoryStorageV2.instance) MemoryStorageV2.instance = new MemoryStorageV2()
366
+ if (!MemoryStorageV2.instance) {
367
+ MemoryStorageV2.instance = new MemoryStorageV2()
368
+ }
328
369
  return MemoryStorageV2.instance
329
370
  }
330
371
  }
@@ -111,9 +111,13 @@ export interface Storage {
111
111
  * available when `status` is {@link StorageOperationStatus.Succeeded}.
112
112
  */
113
113
  export interface StorageReadResult<T extends object = Record<string, unknown>> {
114
+ /** The requested storage key. */
114
115
  key: string;
116
+ /** The outcome of the read. */
115
117
  status: StorageOperationStatus;
118
+ /** The stored value when the read succeeds. */
116
119
  value?: T;
120
+ /** The stored version when available. */
117
121
  version?: string;
118
122
  }
119
123
 
@@ -121,8 +125,11 @@ export interface StorageReadResult<T extends object = Record<string, unknown>> {
121
125
  * The result of a version 2 storage write operation.
122
126
  */
123
127
  export interface StorageWriteResult {
128
+ /** The supplied storage key. */
124
129
  key: string;
130
+ /** The outcome of the write. */
125
131
  status: StorageOperationStatus;
132
+ /** The resulting or current storage version when available. */
126
133
  version?: string;
127
134
  }
128
135
 
@@ -130,8 +137,11 @@ export interface StorageWriteResult {
130
137
  * The result of a version 2 storage delete operation.
131
138
  */
132
139
  export interface StorageDeleteResult {
140
+ /** The supplied storage key. */
133
141
  key: string;
142
+ /** The outcome of the delete. */
134
143
  status: StorageOperationStatus;
144
+ /** The deleted or current storage version when available. */
135
145
  version?: string;
136
146
  }
137
147
 
@@ -146,16 +156,23 @@ export type StorageDeleteResults = Record<string, StorageDeleteResult>
146
156
 
147
157
  /** The outcome of one version 2 storage operation. */
148
158
  export enum StorageOperationStatus {
159
+ /** The operation completed successfully. */
149
160
  Succeeded = 'succeeded',
161
+ /** No item exists for the key. */
150
162
  NotFound = 'notFound',
163
+ /** A create-only operation found an existing item. */
151
164
  Conflict = 'conflict',
165
+ /** The expected version did not match the current version. */
152
166
  ConditionNotMet = 'conditionNotMet',
153
167
  }
154
168
 
155
169
  /** The write mode for a version 2 storage operation. */
156
170
  export enum StorageWriteMode {
171
+ /** Creates a new item or replaces an existing item. */
157
172
  Upsert = 'upsert',
173
+ /** Creates an item only when the key does not exist. */
158
174
  CreateOnly = 'createOnly',
175
+ /** Replaces an item only when the key exists. */
159
176
  Replace = 'replace',
160
177
  }
161
178
 
@@ -168,12 +185,15 @@ export interface StorageWriteOptions {
168
185
  * duration has elapsed. Values must be finite numbers greater than zero.
169
186
  */
170
187
  ttl?: number;
188
+ /** The condition controlling whether existing items may be written. */
171
189
  mode?: StorageWriteMode;
190
+ /** The storage version that must match before writing. */
172
191
  expectedVersion?: string;
173
192
  }
174
193
 
175
194
  /** Options applied to every item in a version 2 delete operation. */
176
195
  export interface StorageDeleteOptions {
196
+ /** The storage version that must match before deleting. */
177
197
  expectedVersion?: string;
178
198
  }
179
199
 
@@ -31,8 +31,12 @@ export function asStorageV2 (storage: StorageProvider): StorageV2 {
31
31
  /** Returns a successful V2 read value, maps not-found to undefined, and rejects invalid results. */
32
32
  export function getStorageReadValue<T extends object> (results: StorageReadResults<T> | null | undefined, key: string): T | undefined {
33
33
  const result = results?.[key]
34
- if (result?.status === StorageOperationStatus.NotFound) return undefined
35
- if (result?.status === StorageOperationStatus.Succeeded) return result.value
34
+ if (result?.status === StorageOperationStatus.NotFound) {
35
+ return undefined
36
+ }
37
+ if (result?.status === StorageOperationStatus.Succeeded) {
38
+ return result.value
39
+ }
36
40
  throwStorageResultError('read', key, result?.status)
37
41
  }
38
42
 
@@ -44,7 +48,9 @@ class StorageToStorageV2Adapter extends StorageV2 {
44
48
 
45
49
  async read<T extends object = Record<string, unknown>> (keys: string[]): Promise<StorageReadResults<T>> {
46
50
  validateKeys(keys)
47
- if (keys.length === 0) return {}
51
+ if (keys.length === 0) {
52
+ return {}
53
+ }
48
54
 
49
55
  const items = await this.storage.read(keys)
50
56
  return Object.fromEntries(keys.map(key => {
@@ -64,7 +70,9 @@ class StorageToStorageV2Adapter extends StorageV2 {
64
70
  throwUnsupportedOption('ttl')
65
71
  }
66
72
  validateExpectedVersion(options?.expectedVersion)
67
- if (Object.keys(changes).length === 0) return {}
73
+ if (Object.keys(changes).length === 0) {
74
+ return {}
75
+ }
68
76
  const legacyChanges = Object.fromEntries(Object.entries(changes).map(([key, value]) => [
69
77
  key,
70
78
  options?.expectedVersion === undefined
@@ -81,7 +89,9 @@ class StorageToStorageV2Adapter extends StorageV2 {
81
89
  if (options?.expectedVersion !== undefined) {
82
90
  throwUnsupportedOption('expectedVersion')
83
91
  }
84
- if (keys.length === 0) return {}
92
+ if (keys.length === 0) {
93
+ return {}
94
+ }
85
95
 
86
96
  await this.storage.delete(keys)
87
97
  return Object.fromEntries(keys.map(key => [key, { key, status: StorageOperationStatus.Succeeded }]))
@@ -116,7 +126,9 @@ function validateChanges (changes: Record<string, unknown>): void {
116
126
  }
117
127
 
118
128
  function removeLegacyETag<T> (value: T): T {
119
- if (value === null || typeof value !== 'object' || Array.isArray(value)) return value
129
+ if (value === null || typeof value !== 'object' || Array.isArray(value)) {
130
+ return value
131
+ }
120
132
  const { eTag: _eTag, ...withoutETag } = value as Record<string, unknown>
121
133
  return withoutETag as T
122
134
  }
@@ -7,6 +7,13 @@ import { StorageWriteOptions } from './storage'
7
7
  import { ExceptionHelper } from '@microsoft/agents-activity'
8
8
  import { Errors } from '../errorHelper'
9
9
 
10
+ /**
11
+ * Computes an absolute expiration time for a storage write.
12
+ *
13
+ * @param options The write options containing an optional TTL in seconds.
14
+ * @returns The expiration time in milliseconds since the Unix epoch, or `undefined` when no TTL is set.
15
+ * @throws A `RangeError` when the TTL is not finite or is not greater than zero.
16
+ */
10
17
  export function getStorageWriteExpiry (options?: StorageWriteOptions): number | undefined {
11
18
  const ttl = options?.ttl
12
19
  if (ttl === undefined) {
@@ -82,9 +82,16 @@ export class TurnContext {
82
82
  *
83
83
  * @param adapterOrContext The adapter that created this context, or another TurnContext to clone
84
84
  * @param request The activity for the turn (required when first parameter is an adapter)
85
+ * @param identity The authenticated identity for the turn.
85
86
  */
86
87
  constructor (adapterOrContext: BaseAdapter, request: Activity, identity?: JwtPayload)
88
+ /**
89
+ * Creates a context by copying another turn context.
90
+ *
91
+ * @param adapterOrContext The turn context to copy.
92
+ */
87
93
  constructor (adapterOrContext: TurnContext)
94
+ /** Implements the adapter and copy constructor overloads. */
88
95
  constructor (adapterOrContext: BaseAdapter | TurnContext, request?: Activity, identity?: JwtPayload) {
89
96
  if (adapterOrContext instanceof TurnContext) {
90
97
  adapterOrContext.copyTo(this)
@@ -367,6 +374,7 @@ export class TurnContext {
367
374
  return this._activity as Activity
368
375
  }
369
376
 
377
+ /** Gets the authenticated identity for the turn. */
370
378
  get identity (): JwtPayload {
371
379
  return this._identity as JwtPayload
372
380
  }
@@ -382,6 +390,7 @@ export class TurnContext {
382
390
  return this._respondedRef.responded
383
391
  }
384
392
 
393
+ /** Marks the turn as having sent a response. */
385
394
  set responded (value: boolean) {
386
395
  if (!value) {
387
396
  throw ExceptionHelper.generateException(Error, Errors.CannotSetRespondedToFalse)
@@ -404,6 +413,7 @@ export class TurnContext {
404
413
  return undefined
405
414
  }
406
415
 
416
+ /** Sets the locale stored in the turn state. */
407
417
  set locale (value: string | undefined) {
408
418
  let turnObj = this._turnState.get(this._turn)
409
419
  if (turnObj) {
@@ -426,6 +436,7 @@ export class TurnContext {
426
436
  return this._turnState
427
437
  }
428
438
 
439
+ /** Gets the streaming response associated with this context. */
429
440
  get streamingResponse (): StreamingResponse {
430
441
  return this._streamingResponse
431
442
  }
package/src/utils/env.ts CHANGED
@@ -87,11 +87,19 @@ export const envParserUtils = {
87
87
  * unrecognized.
88
88
  */
89
89
  export function parseBooleanEnv (value: string | undefined): boolean | undefined {
90
- if (value === undefined) return undefined
90
+ if (value === undefined) {
91
+ return undefined
92
+ }
91
93
  const v = value.trim().toLowerCase()
92
- if (v === 'true' || v === '1') return true
93
- if (v === '') return undefined
94
- if (v === 'false' || v === '0') return false
94
+ if (v === 'true' || v === '1') {
95
+ return true
96
+ }
97
+ if (v === '') {
98
+ return undefined
99
+ }
100
+ if (v === 'false' || v === '0') {
101
+ return false
102
+ }
95
103
  return undefined
96
104
  }
97
105
 
@@ -104,7 +112,9 @@ export function parseBooleanEnv (value: string | undefined): boolean | undefined
104
112
  * @returns A finite integer, or `fallback` when the input cannot be parsed.
105
113
  */
106
114
  export function parseIntEnv (value: string | undefined, fallback: number): number {
107
- if (value === undefined || value.trim() === '') return fallback
115
+ if (value === undefined || value.trim() === '') {
116
+ return fallback
117
+ }
108
118
  const parsed = parseInt(value, 10)
109
119
  return Number.isNaN(parsed) ? fallback : parsed
110
120
  }
@@ -132,7 +142,9 @@ export function suggestClosest (
132
142
  candidates: readonly string[],
133
143
  maxDistance = 2
134
144
  ): string | undefined {
135
- if (!input) return undefined
145
+ if (!input) {
146
+ return undefined
147
+ }
136
148
  const a = input.toLowerCase()
137
149
  const cutoff = Math.min(maxDistance, Math.max(1, Math.ceil(a.length / 2)))
138
150
  let best: { name: string, distance: number } | undefined
@@ -140,7 +152,9 @@ export function suggestClosest (
140
152
  const d = levenshtein(a, candidate.toLowerCase())
141
153
  if (d <= cutoff && (!best || d < best.distance)) {
142
154
  best = { name: candidate, distance: d }
143
- if (d === 0) break
155
+ if (d === 0) {
156
+ break
157
+ }
144
158
  }
145
159
  }
146
160
  return best?.name
@@ -148,11 +162,19 @@ export function suggestClosest (
148
162
 
149
163
  /** Standard Levenshtein edit distance using a single-row dynamic-programming table. */
150
164
  function levenshtein (a: string, b: string): number {
151
- if (a === b) return 0
152
- if (a.length === 0) return b.length
153
- if (b.length === 0) return a.length
165
+ if (a === b) {
166
+ return 0
167
+ }
168
+ if (a.length === 0) {
169
+ return b.length
170
+ }
171
+ if (b.length === 0) {
172
+ return a.length
173
+ }
154
174
  const row = new Array<number>(b.length + 1)
155
- for (let j = 0; j <= b.length; j++) row[j] = j
175
+ for (let j = 0; j <= b.length; j++) {
176
+ row[j] = j
177
+ }
156
178
  for (let i = 1; i <= a.length; i++) {
157
179
  let prev = i - 1
158
180
  row[0] = i
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Copyright (c) Microsoft Corporation. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import { isIP } from 'node:net'
7
+
8
+ function normalizeAddress (address: string): string | undefined {
9
+ const unwrapped = address.startsWith('[') && address.endsWith(']') ? address.slice(1, -1) : address
10
+ const version = isIP(unwrapped)
11
+ if (version === 0) {
12
+ return undefined
13
+ }
14
+ return version === 6 ? new URL(`http://[${unwrapped}]`).hostname.slice(1, -1) : unwrapped
15
+ }
16
+
17
+ function mappedIPv4 (address: string): string | undefined {
18
+ const match = address.match(/^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/)
19
+ if (!match) {
20
+ return undefined
21
+ }
22
+ const high = parseInt(match[1], 16)
23
+ const low = parseInt(match[2], 16)
24
+ return `${high >> 8}.${high & 0xff}.${low >> 8}.${low & 0xff}`
25
+ }
26
+
27
+ export function isPrivateOrLoopbackAddress (address: string): boolean {
28
+ const normalized = normalizeAddress(address)
29
+ if (!normalized) {
30
+ return false
31
+ }
32
+ const ipv4 = mappedIPv4(normalized) ?? normalized
33
+ if (isIP(ipv4) === 4) {
34
+ const bytes = ipv4.split('.').map(Number)
35
+ return bytes[0] === 127 ||
36
+ bytes[0] === 10 ||
37
+ (bytes[0] === 172 && bytes[1] >= 16 && bytes[1] <= 31) ||
38
+ (bytes[0] === 192 && bytes[1] === 168) ||
39
+ (bytes[0] === 169 && bytes[1] === 254)
40
+ }
41
+
42
+ if (normalized === '::1') {
43
+ return true
44
+ }
45
+ const firstHextet = parseInt(normalized.split(':')[0], 16)
46
+ return (firstHextet & 0xfe00) === 0xfc00 ||
47
+ (firstHextet & 0xffc0) === 0xfe80 ||
48
+ (firstHextet & 0xffc0) === 0xfec0
49
+ }
50
+
51
+ export function isBlockedOutboundAddress (address: string): boolean {
52
+ const normalized = normalizeAddress(address)
53
+ if (!normalized) {
54
+ return true
55
+ }
56
+ const ipv4 = mappedIPv4(normalized) ?? normalized
57
+ const [first, second] = ipv4.split('.').map(Number)
58
+ // RFC 6598 shared space is outbound-only; it must not become trusted sidecar space.
59
+ return normalized === '::' ||
60
+ ipv4 === '0.0.0.0' ||
61
+ (first === 100 && second >= 64 && second <= 127) ||
62
+ isPrivateOrLoopbackAddress(normalized)
63
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Copyright (c) Microsoft Corporation. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import { promises as dns } from 'node:dns'
7
+ import { isIP } from 'node:net'
8
+ import { debug } from '@microsoft/agents-telemetry'
9
+ import { isBlockedOutboundAddress } from './ipAddress'
10
+
11
+ const logger = debug('agents:outboundHostValidator')
12
+
13
+ export function getHttpsUrl (input: string | URL | null | undefined): URL | undefined {
14
+ if (!input) {
15
+ return undefined
16
+ }
17
+ try {
18
+ const url = input instanceof URL ? input : new URL(input)
19
+ return url.protocol === 'https:' && !url.username && !url.password ? url : undefined
20
+ } catch {
21
+ return undefined
22
+ }
23
+ }
24
+
25
+ export function isLocalOrBlockedHost (host: string): boolean {
26
+ const unwrapped = host.startsWith('[') && host.endsWith(']') ? host.slice(1, -1) : host
27
+ return host === 'localhost' || host.endsWith('.localhost') ||
28
+ (isIP(unwrapped) !== 0 && isBlockedOutboundAddress(unwrapped))
29
+ }
30
+
31
+ export async function hasAllowedOutboundAddresses (url: URL, allowPrivateNetworkAddresses = false): Promise<boolean> {
32
+ if (allowPrivateNetworkAddresses) {
33
+ return true
34
+ }
35
+ const host = url.hostname.replace(/^\[|\]$/g, '').replace(/\.$/, '')
36
+ if (isLocalOrBlockedHost(host)) {
37
+ return false
38
+ }
39
+ if (isIP(host) !== 0) {
40
+ return true
41
+ }
42
+
43
+ try {
44
+ const addresses = await dns.lookup(host, { all: true })
45
+ return addresses.length > 0 && addresses.every(({ address }) => !isBlockedOutboundAddress(address))
46
+ } catch {
47
+ logger.warn(`Attachment destination DNS lookup failed. Host='${host}'`)
48
+ return false
49
+ }
50
+ }
package/src/utils.ts CHANGED
@@ -18,7 +18,9 @@ export const mergeDefined = <T extends Record<string, any>>(
18
18
  ): T => {
19
19
  const result: Partial<T> = {}
20
20
  for (const layer of layers) {
21
- if (layer) Object.assign(result, prune(layer))
21
+ if (layer) {
22
+ Object.assign(result, prune(layer))
23
+ }
22
24
  }
23
25
  return result as T
24
26
  }