@microsoft/agents-hosting 0.2.10-g3ac88ff25e → 0.3.5

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 (128) hide show
  1. package/README.md +17 -1
  2. package/dist/src/activityHandler.d.ts +304 -46
  3. package/dist/src/activityHandler.js +298 -45
  4. package/dist/src/activityHandler.js.map +1 -1
  5. package/dist/src/agent-client/agentClient.d.ts +50 -0
  6. package/dist/src/agent-client/agentClient.js +28 -0
  7. package/dist/src/agent-client/agentClient.js.map +1 -1
  8. package/dist/src/app/agentApplication.d.ts +272 -7
  9. package/dist/src/app/agentApplication.js +278 -12
  10. package/dist/src/app/agentApplication.js.map +1 -1
  11. package/dist/src/app/agentApplicationBuilder.d.ts +34 -3
  12. package/dist/src/app/agentApplicationBuilder.js +38 -6
  13. package/dist/src/app/agentApplicationBuilder.js.map +1 -1
  14. package/dist/src/app/agentApplicationOptions.d.ts +26 -2
  15. package/dist/src/app/appMemory.d.ts +34 -0
  16. package/dist/src/app/{memory.js → appMemory.js} +1 -1
  17. package/dist/src/app/appMemory.js.map +1 -0
  18. package/dist/src/app/appRoute.d.ts +6 -0
  19. package/dist/src/app/attachmentDownloader.d.ts +18 -0
  20. package/dist/src/app/attachmentDownloader.js +18 -0
  21. package/dist/src/app/attachmentDownloader.js.map +1 -1
  22. package/dist/src/app/conversationUpdateEvents.d.ts +6 -0
  23. package/dist/src/app/index.d.ts +4 -1
  24. package/dist/src/app/index.js +4 -1
  25. package/dist/src/app/index.js.map +1 -1
  26. package/dist/src/app/inputFileDownloader.d.ts +22 -0
  27. package/dist/src/app/oauth/authorization.d.ts +87 -0
  28. package/dist/src/app/oauth/authorization.js +135 -0
  29. package/dist/src/app/oauth/authorization.js.map +1 -0
  30. package/dist/src/app/routeHandler.d.ts +8 -0
  31. package/dist/src/app/routeSelector.d.ts +9 -0
  32. package/dist/src/app/turnEvents.d.ts +6 -0
  33. package/dist/src/app/turnState.d.ts +152 -2
  34. package/dist/src/app/turnState.js +125 -0
  35. package/dist/src/app/turnState.js.map +1 -1
  36. package/dist/src/app/turnStateEntry.d.ts +32 -0
  37. package/dist/src/app/turnStateEntry.js +32 -0
  38. package/dist/src/app/turnStateEntry.js.map +1 -1
  39. package/dist/src/auth/authConfiguration.d.ts +24 -0
  40. package/dist/src/auth/authConfiguration.js.map +1 -1
  41. package/dist/src/auth/request.d.ts +12 -0
  42. package/dist/src/baseAdapter.d.ts +17 -0
  43. package/dist/src/baseAdapter.js +17 -0
  44. package/dist/src/baseAdapter.js.map +1 -1
  45. package/dist/src/cards/cardFactory.d.ts +3 -0
  46. package/dist/src/cards/cardFactory.js +3 -0
  47. package/dist/src/cards/cardFactory.js.map +1 -1
  48. package/dist/src/cards/index.d.ts +1 -0
  49. package/dist/src/cards/index.js +1 -0
  50. package/dist/src/cards/index.js.map +1 -1
  51. package/dist/src/cards/o365ConnectorCardActionBase.d.ts +8 -0
  52. package/dist/src/cloudAdapter.d.ts +25 -3
  53. package/dist/src/cloudAdapter.js +25 -3
  54. package/dist/src/cloudAdapter.js.map +1 -1
  55. package/dist/src/getProductInfo.d.ts +6 -0
  56. package/dist/src/getProductInfo.js +6 -0
  57. package/dist/src/getProductInfo.js.map +1 -1
  58. package/dist/src/logger.d.ts +34 -2
  59. package/dist/src/logger.js +35 -0
  60. package/dist/src/logger.js.map +1 -1
  61. package/dist/src/oauth/oAuthFlow.d.ts +32 -3
  62. package/dist/src/oauth/oAuthFlow.js +38 -14
  63. package/dist/src/oauth/oAuthFlow.js.map +1 -1
  64. package/dist/src/oauth/userTokenClient.d.ts +2 -2
  65. package/dist/src/oauth/userTokenClient.js +3 -3
  66. package/dist/src/oauth/userTokenClient.js.map +1 -1
  67. package/dist/src/state/agentState.d.ts +79 -27
  68. package/dist/src/state/agentState.js +58 -27
  69. package/dist/src/state/agentState.js.map +1 -1
  70. package/dist/src/state/agentStatePropertyAccesor.d.ts +67 -11
  71. package/dist/src/state/agentStatePropertyAccesor.js +58 -11
  72. package/dist/src/state/agentStatePropertyAccesor.js.map +1 -1
  73. package/dist/src/statusCodes.d.ts +39 -0
  74. package/dist/src/statusCodes.js +39 -0
  75. package/dist/src/statusCodes.js.map +1 -1
  76. package/dist/src/storage/memoryStorage.d.ts +48 -14
  77. package/dist/src/storage/memoryStorage.js +48 -14
  78. package/dist/src/storage/memoryStorage.js.map +1 -1
  79. package/dist/src/storage/storage.d.ts +43 -13
  80. package/dist/src/tokenResponseEventName.d.ts +3 -0
  81. package/dist/src/tokenResponseEventName.js +3 -0
  82. package/dist/src/tokenResponseEventName.js.map +1 -1
  83. package/dist/src/turnContext.d.ts +142 -56
  84. package/dist/src/turnContext.js +123 -53
  85. package/dist/src/turnContext.js.map +1 -1
  86. package/package.json +6 -6
  87. package/src/activityHandler.ts +304 -46
  88. package/src/agent-client/agentClient.ts +55 -5
  89. package/src/app/agentApplication.ts +283 -15
  90. package/src/app/agentApplicationBuilder.ts +39 -7
  91. package/src/app/agentApplicationOptions.ts +33 -2
  92. package/src/app/appMemory.ts +38 -0
  93. package/src/app/appRoute.ts +7 -0
  94. package/src/app/attachmentDownloader.ts +18 -0
  95. package/src/app/conversationUpdateEvents.ts +6 -0
  96. package/src/app/index.ts +4 -1
  97. package/src/app/inputFileDownloader.ts +24 -0
  98. package/src/app/oauth/authorization.ts +162 -0
  99. package/src/app/routeHandler.ts +8 -0
  100. package/src/app/routeSelector.ts +9 -0
  101. package/src/app/turnEvents.ts +6 -0
  102. package/src/app/turnState.ts +153 -3
  103. package/src/app/turnStateEntry.ts +32 -0
  104. package/src/auth/authConfiguration.ts +32 -1
  105. package/src/auth/request.ts +15 -0
  106. package/src/baseAdapter.ts +18 -0
  107. package/src/cards/cardFactory.ts +3 -0
  108. package/src/cards/index.ts +1 -0
  109. package/src/cards/o365ConnectorCardActionBase.ts +8 -0
  110. package/src/cloudAdapter.ts +28 -3
  111. package/src/getProductInfo.ts +7 -0
  112. package/src/logger.ts +34 -1
  113. package/src/oauth/oAuthFlow.ts +59 -18
  114. package/src/oauth/userTokenClient.ts +4 -4
  115. package/src/state/agentState.ts +81 -29
  116. package/src/state/agentStatePropertyAccesor.ts +67 -11
  117. package/src/statusCodes.ts +51 -0
  118. package/src/storage/memoryStorage.ts +48 -14
  119. package/src/storage/storage.ts +51 -18
  120. package/src/tokenResponseEventName.ts +3 -0
  121. package/src/turnContext.ts +142 -56
  122. package/dist/src/app/memory.d.ts +0 -10
  123. package/dist/src/app/memory.js.map +0 -1
  124. package/dist/src/app/oauth/userIdentity.d.ts +0 -43
  125. package/dist/src/app/oauth/userIdentity.js +0 -54
  126. package/dist/src/app/oauth/userIdentity.js.map +0 -1
  127. package/src/app/memory.ts +0 -14
  128. package/src/app/oauth/userIdentity.ts +0 -78
@@ -13,8 +13,23 @@ export interface Request<
13
13
  Body extends Record<string, unknown> = Record<string, unknown>,
14
14
  Headers extends Record<string, string[] | string | undefined> = Record<string, string[] | string | undefined>
15
15
  > {
16
+ /**
17
+ * The body of the HTTP request, containing parsed data.
18
+ */
16
19
  body?: Body
20
+
21
+ /**
22
+ * The headers of the HTTP request, represented as key-value pairs.
23
+ */
17
24
  headers: Headers
25
+
26
+ /**
27
+ * The HTTP method of the request (e.g., GET, POST, PUT, DELETE).
28
+ */
18
29
  method?: string
30
+
31
+ /**
32
+ * The user information extracted from a JWT payload, if available.
33
+ */
19
34
  user?: JwtPayload
20
35
  }
@@ -20,6 +20,9 @@ const logger = debug('agents:base-adapter')
20
20
  * Base class for all adapters, providing middleware and error handling capabilities.
21
21
  */
22
22
  export abstract class BaseAdapter {
23
+ /**
24
+ * The middleware set used to process the pipeline of middleware handlers.
25
+ */
23
26
  protected middleware: MiddlewareSet = new MiddlewareSet()
24
27
 
25
28
  private turnError: (context: TurnContext, error: Error) => Promise<void> = async (context: TurnContext, error: Error) => {
@@ -42,7 +45,14 @@ export abstract class BaseAdapter {
42
45
  readonly ConnectorClientKey = Symbol('ConnectorClient')
43
46
  readonly OAuthScopeKey = Symbol('OAuthScope')
44
47
 
48
+ /**
49
+ * The authentication provider used for token management.
50
+ */
45
51
  authProvider: AuthProvider = new MsalTokenProvider()
52
+
53
+ /**
54
+ * The authentication configuration for the adapter.
55
+ */
46
56
  authConfig: AuthConfiguration = { issuers: [] }
47
57
 
48
58
  /**
@@ -103,10 +113,18 @@ export abstract class BaseAdapter {
103
113
  */
104
114
  abstract getAttachment (attachmentId: string, viewId: string): Promise<NodeJS.ReadableStream>
105
115
 
116
+ /**
117
+ * Gets the error handler for the adapter.
118
+ * @returns The current error handler function.
119
+ */
106
120
  get onTurnError (): (context: TurnContext, error: Error) => Promise<void> {
107
121
  return this.turnError
108
122
  }
109
123
 
124
+ /**
125
+ * Sets the error handler for the adapter.
126
+ * @param value - The error handler function to set.
127
+ */
110
128
  set onTurnError (value: (context: TurnContext, error: Error) => Promise<void>) {
111
129
  this.turnError = value
112
130
  }
@@ -21,6 +21,9 @@ import { SigninCard } from './signinCard'
21
21
  * Factory class for creating various types of cards.
22
22
  */
23
23
  export class CardFactory {
24
+ /**
25
+ * The content types supported by the card factory.
26
+ */
24
27
  static contentTypes: any = {
25
28
  adaptiveCard: 'application/vnd.microsoft.card.adaptive',
26
29
  animationCard: 'application/vnd.microsoft.card.animation',
@@ -15,3 +15,4 @@ export * from './receiptItem'
15
15
  export * from './taskModuleAction'
16
16
  export * from './thumbnailCard'
17
17
  export * from './videoCard'
18
+ export * from './thumbnailUrl'
@@ -3,6 +3,14 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
+ /**
7
+ * Defines the possible types of actions in an O365 connector card.
8
+ *
9
+ * - `ViewAction`: Represents an action to view content.
10
+ * - `OpenUri`: Represents an action to open a URI.
11
+ * - `HttpPOST`: Represents an action to make an HTTP POST request.
12
+ * - `ActionCard`: Represents an action that opens a card with additional actions or inputs.
13
+ */
6
14
  export type O365ConnectorCardActionType = 'ViewAction' | 'OpenUri' | 'HttpPOST' | 'ActionCard'
7
15
 
8
16
  /**
@@ -25,15 +25,23 @@ import { normalizeIncomingActivity } from './activityWireCompat'
25
25
  const logger = debug('agents:cloud-adapter')
26
26
 
27
27
  /**
28
- * Adapter for handling agent interactions.
28
+ * Adapter for handling agent interactions with various channels through cloud-based services.
29
+ *
30
+ * CloudAdapter processes incoming HTTP requests from Microsoft Bot Framework channels,
31
+ * authenticates them, and generates outgoing responses. It manages the communication
32
+ * flow between agents and users across different channels, handling activities, attachments,
33
+ * and conversation continuations.
29
34
  */
30
35
  export class CloudAdapter extends BaseAdapter {
36
+ /**
37
+ * Client for connecting to the Bot Framework Connector service
38
+ */
31
39
  public connectorClient!: ConnectorClient
32
40
 
33
41
  /**
34
42
  * Creates an instance of CloudAdapter.
35
- * @param authConfig - The authentication configuration.
36
- * @param authProvider - The authentication provider.
43
+ * @param authConfig - The authentication configuration for securing communications
44
+ * @param authProvider - Optional custom authentication provider. If not specified, a default MsalTokenProvider will be used
37
45
  */
38
46
  constructor (authConfig: AuthConfiguration, authProvider?: AuthProvider) {
39
47
  super()
@@ -47,6 +55,14 @@ export class CloudAdapter extends BaseAdapter {
47
55
  }
48
56
  }
49
57
 
58
+ /**
59
+ * Creates a connector client for a specific service URL and scope.
60
+ *
61
+ * @param serviceUrl - The URL of the service to connect to
62
+ * @param scope - The authentication scope to use
63
+ * @returns A promise that resolves to a ConnectorClient instance
64
+ * @protected
65
+ */
50
66
  protected async createConnectorClient (
51
67
  serviceUrl: string,
52
68
  scope: string
@@ -59,6 +75,12 @@ export class CloudAdapter extends BaseAdapter {
59
75
  )
60
76
  }
61
77
 
78
+ /**
79
+ * Sets the connector client on the turn context.
80
+ *
81
+ * @param context - The current turn context
82
+ * @protected
83
+ */
62
84
  protected setConnectorClient (
63
85
  context: TurnContext
64
86
  ) {
@@ -164,6 +186,9 @@ export class CloudAdapter extends BaseAdapter {
164
186
  }
165
187
  res.end()
166
188
  }
189
+ if (!request.body) {
190
+ throw new TypeError('`request.body` parameter required, make sure express.json() is used as middleware')
191
+ }
167
192
  const incoming = normalizeIncomingActivity(request.body!)
168
193
  const activity = Activity.fromObject(incoming)
169
194
  logger.info(`--> Processing incoming activity, type:${activity.type} channel:${activity.channelId}`)
@@ -1,3 +1,10 @@
1
1
  import pjson from '@microsoft/agents-hosting/package.json'
2
2
  import os from 'os'
3
+
4
+ /**
5
+ * Generates a string containing information about the SDK version and runtime environment.
6
+ * This is used for telemetry and User-Agent headers in HTTP requests.
7
+ *
8
+ * @returns A formatted string containing the SDK version, Node.js version, and OS details
9
+ */
3
10
  export const getProductInfo = () : string => `agents-sdk-js/${pjson.version} nodejs/${process.version} ${os.platform()}-${os.arch()}/${os.release()}`
package/src/logger.ts CHANGED
@@ -1,6 +1,10 @@
1
1
  import createDebug, { Debugger } from 'debug'
2
2
 
3
- class Logger {
3
+ /**
4
+ * Logger class that provides colored logging functionality using the debug package.
5
+ * Supports different log levels: info, warn, error, and debug.
6
+ */
7
+ export class Logger {
4
8
  private loggers: { [level: string]: Debugger } = {}
5
9
  private readonly levelColors: { [level: string]: string } = {
6
10
  info: '2', // Green
@@ -9,6 +13,10 @@ class Logger {
9
13
  debug: '4' // Blue
10
14
  }
11
15
 
16
+ /**
17
+ * Creates a new Logger instance with the specified namespace.
18
+ * @param namespace The namespace to use for the logger
19
+ */
12
20
  constructor (namespace: string = '') {
13
21
  this.initializeLoggers(namespace)
14
22
  }
@@ -21,23 +29,48 @@ class Logger {
21
29
  }
22
30
  }
23
31
 
32
+ /**
33
+ * Logs an informational message.
34
+ * @param message The message to log
35
+ * @param args Additional arguments to include in the log
36
+ */
24
37
  info (message: string, ...args: any[]) {
25
38
  this.loggers.info(message, ...args)
26
39
  }
27
40
 
41
+ /**
42
+ * Logs a warning message.
43
+ * @param message The message to log
44
+ * @param args Additional arguments to include in the log
45
+ */
28
46
  warn (message: string, ...args: any[]) {
29
47
  this.loggers.warn(message, ...args)
30
48
  }
31
49
 
50
+ /**
51
+ * Logs an error message.
52
+ * @param message The message to log
53
+ * @param args Additional arguments to include in the log
54
+ */
32
55
  error (message: string, ...args: any[]) {
33
56
  this.loggers.error(message, ...args)
34
57
  }
35
58
 
59
+ /**
60
+ * Logs a debug message.
61
+ * @param message The message to log
62
+ * @param args Additional arguments to include in the log
63
+ */
36
64
  debug (message: string, ...args: any[]) {
37
65
  this.loggers.debug(message, ...args)
38
66
  }
39
67
  }
40
68
 
69
+ /**
70
+ * Creates a new Logger instance with the specified namespace.
71
+ * @param namespace The namespace to use for the logger
72
+ * @returns A new Logger instance
73
+ */
41
74
  export function debug (namespace: string): Logger {
42
75
  return new Logger(namespace)
43
76
  }
@@ -1,14 +1,13 @@
1
1
  // Copyright (c) Microsoft Corporation. All rights reserved.
2
2
  // Licensed under the MIT License.
3
3
  import { debug } from './../logger'
4
- import { Activity, ActivityTypes, Attachment } from '@microsoft/agents-activity'
4
+ import { ActivityTypes, Attachment } from '@microsoft/agents-activity'
5
5
  import {
6
6
  CardFactory,
7
7
  AgentStatePropertyAccessor,
8
8
  UserState,
9
9
  TurnContext,
10
10
  MessageFactory,
11
- SigningResource,
12
11
  TokenExchangeRequest,
13
12
  UserTokenClient
14
13
  } from '../'
@@ -25,28 +24,72 @@ interface TokenVerifyState {
25
24
  state: string
26
25
  }
27
26
  /**
28
- * Manages the OAuth flow for Teams.
27
+ * Manages the OAuth flow
29
28
  */
30
29
  export class OAuthFlow {
31
- userTokenClient?: UserTokenClient
30
+ /**
31
+ * The user token client used for managing user tokens.
32
+ */
33
+ userTokenClient: UserTokenClient
34
+
35
+ /**
36
+ * The current state of the OAuth flow.
37
+ */
32
38
  state: FlowState | null
39
+
40
+ /**
41
+ * The accessor for managing the flow state in user state.
42
+ */
33
43
  flowStateAccessor: AgentStatePropertyAccessor<FlowState | null>
44
+
45
+ /**
46
+ * The ID of the token exchange request, used to deduplicate requests.
47
+ */
34
48
  tokenExchangeId: string | null = null
49
+
50
+ /**
51
+ * The name of the OAuth connection.
52
+ */
35
53
  absOauthConnectionName: string
54
+
55
+ /**
56
+ * The title of the OAuth card.
57
+ */
58
+ cardTitle: string = 'Sign in'
59
+
60
+ /**
61
+ * The text of the OAuth card.
62
+ */
63
+ cardText: string = 'login'
64
+
36
65
  /**
37
66
  * Creates a new instance of OAuthFlow.
38
67
  * @param userState The user state.
39
68
  */
40
- constructor (userState: UserState, absOauthConnectionName: string, tokenClient?: UserTokenClient) {
41
- this.state = null
69
+ constructor (userState: UserState, absOauthConnectionName: string, tokenClient?: UserTokenClient, cardTitle?: string, cardText?: string) {
70
+ this.state = new FlowState()
42
71
  this.flowStateAccessor = userState.createProperty('flowState')
43
72
  this.absOauthConnectionName = absOauthConnectionName
44
- this.userTokenClient = tokenClient
73
+ this.userTokenClient = tokenClient ?? null!
74
+ this.cardTitle = cardTitle ?? this.cardTitle
75
+ this.cardText = cardText ?? this.cardText
45
76
  }
46
77
 
78
+ /**
79
+ * Retrieves the user token from the user token service.
80
+ * @param context The turn context containing the activity information.
81
+ * @returns A promise that resolves to the user token response.
82
+ * @throws Will throw an error if the channelId or from properties are not set in the activity.
83
+ */
47
84
  public async getUserToken (context: TurnContext): Promise<TokenResponse> {
48
85
  await this.initializeTokenClient(context)
49
- return await this.userTokenClient?.getUserToken(this.absOauthConnectionName, context.activity.channelId!, context.activity.from?.id!)!
86
+ logger.info('Get token from user token service')
87
+ const activity = context.activity
88
+ if (activity.channelId && activity.from && activity.from.id) {
89
+ return await this.userTokenClient.getUserToken(this.absOauthConnectionName, activity.channelId, activity.from.id)
90
+ } else {
91
+ throw new Error('UserTokenService requires channelId and from to be set')
92
+ }
50
93
  }
51
94
 
52
95
  /**
@@ -55,16 +98,14 @@ export class OAuthFlow {
55
98
  * @returns A promise that resolves to the user token.
56
99
  */
57
100
  public async beginFlow (context: TurnContext): Promise<TokenResponse> {
58
- logger.info('Starting OAuth flow')
59
101
  this.state = await this.getUserState(context)
60
-
61
- const authConfig = context.adapter.authConfig
62
102
  if (this.absOauthConnectionName === '') {
63
- throw new Error('connectionName is not set in the auth config, review your environment variables')
103
+ throw new Error('connectionName is not set')
64
104
  }
105
+ logger.info('Starting OAuth flow for connectionName:', this.absOauthConnectionName)
65
106
  await this.initializeTokenClient(context)
66
107
 
67
- const tokenResponse = await this.userTokenClient!.getUserToken(this.absOauthConnectionName, context.activity.channelId!, context.activity.from?.id!)
108
+ const tokenResponse = await this.userTokenClient.getUserToken(this.absOauthConnectionName, context.activity.channelId!, context.activity.from?.id!)
68
109
  if (tokenResponse?.status === TokenRequestStatus.Success) {
69
110
  this.state.flowStarted = false
70
111
  this.state.flowExpires = 0
@@ -73,10 +114,10 @@ export class OAuthFlow {
73
114
  return tokenResponse
74
115
  }
75
116
 
76
- const signingResource: SigningResource = await this.userTokenClient!.getSignInResource(authConfig.clientId!, this.absOauthConnectionName, context.activity)
77
- const oCard: Attachment = CardFactory.oauthCard(this.absOauthConnectionName, 'Sign in', 'login', signingResource)
78
- const cardActivity : Activity = MessageFactory.attachment(oCard)
79
- await context.sendActivity(cardActivity)
117
+ const authConfig = context.adapter.authConfig
118
+ const signingResource = await this.userTokenClient.getSignInResource(authConfig.clientId!, this.absOauthConnectionName, context.activity.getConversationReference(), context.activity.relatesTo)
119
+ const oCard: Attachment = CardFactory.oauthCard(this.absOauthConnectionName, this.cardTitle, this.cardText, signingResource)
120
+ await context.sendActivity(MessageFactory.attachment(oCard))
80
121
  this.state.flowStarted = true
81
122
  this.state.flowExpires = Date.now() + 30000
82
123
  await this.flowStateAccessor.set(context, this.state)
@@ -166,7 +207,7 @@ export class OAuthFlow {
166
207
  }
167
208
 
168
209
  private async initializeTokenClient (context: TurnContext) {
169
- if (this.userTokenClient === undefined) {
210
+ if (this.userTokenClient === undefined || this.userTokenClient === null) {
170
211
  const scope = 'https://api.botframework.com'
171
212
  const accessToken = await context.adapter.authProvider.getAccessToken(context.adapter.authConfig, scope)
172
213
  this.userTokenClient = new UserTokenClient(accessToken)
@@ -3,7 +3,7 @@
3
3
 
4
4
  import axios, { AxiosInstance } from 'axios'
5
5
  import { SigningResource } from './signingResource'
6
- import { Activity } from '@microsoft/agents-activity'
6
+ import { ConversationReference } from '@microsoft/agents-activity'
7
7
  import { debug } from '../logger'
8
8
  import { TokenExchangeRequest } from './tokenExchangeRequest'
9
9
  import { normalizeTokenExchangeState } from '../activityWireCompat'
@@ -86,12 +86,12 @@ export class UserTokenClient {
86
86
  * @param activity The activity.
87
87
  * @returns A promise that resolves to the signing resource.
88
88
  */
89
- async getSignInResource (appId: string, cnxName: string, activity: Activity) : Promise<SigningResource> {
89
+ async getSignInResource (appId: string, cnxName: string, conversationReference: ConversationReference, relatesTo?: ConversationReference) : Promise<SigningResource> {
90
90
  try {
91
91
  const tokenExchangeState = {
92
92
  connectionName: cnxName,
93
- conversation: activity.getConversationReference(),
94
- relatesTo: activity.RelatesTo,
93
+ conversation: conversationReference,
94
+ relatesTo,
95
95
  msAppId: appId
96
96
  }
97
97
  const tokenExchangeStateNormalized = normalizeTokenExchangeState(tokenExchangeState)
@@ -11,34 +11,63 @@ import { debug } from '../logger'
11
11
 
12
12
  const logger = debug('agents:state')
13
13
 
14
+ /**
15
+ * Represents agent state that has been cached in the turn context.
16
+ * Used internally to track state changes and avoid unnecessary storage operations.
17
+ */
14
18
  export interface CachedAgentState {
15
- state: { [id: string]: any }
16
- hash: string
19
+ /**
20
+ * The state object containing all properties and their values
21
+ */
22
+ state: { [id: string]: any };
23
+ /**
24
+ * Hash of the state used to detect changes
25
+ */
26
+ hash: string;
17
27
  }
18
28
 
29
+ /**
30
+ * Represents a custom key for storing state in a specific location.
31
+ * Allows state to be persisted with channel and conversation identifiers
32
+ * independent of the current context.
33
+ */
19
34
  export interface CustomKey {
35
+ /**
36
+ * The ID of the channel where the state should be stored
37
+ */
20
38
  channelId: string;
39
+ /**
40
+ * The ID of the conversation where the state should be stored
41
+ */
21
42
  conversationId: string;
22
43
  // TODO: namespace needs to be added
23
44
  }
24
45
 
25
46
  /**
26
- * Manages the state of an Agent.
47
+ * Manages the state of an Agent across turns in a conversation.
48
+ *
49
+ * AgentState provides functionality to persist and retrieve state data using
50
+ * a storage provider. It handles caching state in the turn context for performance,
51
+ * calculating change hashes to detect modifications, and managing property accessors
52
+ * for typed access to state properties.
27
53
  */
28
54
  export class AgentState {
29
55
  private readonly stateKey = Symbol('state')
30
56
 
31
57
  /**
32
- * Creates a new instance of AgentState.
33
- * @param storage The storage provider.
34
- * @param storageKey The storage key factory.
35
- */
58
+ * Creates a new instance of AgentState.
59
+ *
60
+ * @param storage The storage provider used to persist state between turns
61
+ * @param storageKey A factory function that generates keys for storing state data
62
+ */
36
63
  constructor (protected storage: Storage, protected storageKey: StorageKeyFactory) { }
37
64
 
38
65
  /**
39
66
  * Creates a property accessor for the specified property.
40
- * @param name The name of the property.
41
- * @returns A property accessor for the specified property.
67
+ * Property accessors provide typed access to properties within the state object.
68
+ *
69
+ * @param name The name of the property to access
70
+ * @returns A property accessor for the specified property
42
71
  */
43
72
  createProperty<T = any>(name: string): AgentStatePropertyAccessor<T> {
44
73
  const prop: AgentStatePropertyAccessor<T> = new AgentStatePropertyAccessor<T>(this, name)
@@ -46,10 +75,13 @@ export class AgentState {
46
75
  }
47
76
 
48
77
  /**
49
- * Loads the state from storage.
50
- * @param context The turn context.
51
- * @param force Whether to force loading the state.
52
- * @returns A promise that resolves to the loaded state.
78
+ * Loads the state from storage into the turn context.
79
+ * If state is already cached in the turn context and force is not set, the cached version will be used.
80
+ *
81
+ * @param context The turn context to load state into
82
+ * @param force If true, forces a reload from storage even if state is cached
83
+ * @param customKey Optional custom storage key to use instead of the default
84
+ * @returns A promise that resolves to the loaded state object
53
85
  */
54
86
  public async load (context: TurnContext, force = false, customKey?: CustomKey): Promise<any> {
55
87
  const cached: CachedAgentState = context.turnState.get(this.stateKey)
@@ -70,10 +102,13 @@ export class AgentState {
70
102
  }
71
103
 
72
104
  /**
73
- * Saves the state to storage.
74
- * @param context The turn context.
75
- * @param force Whether to force saving the state.
76
- * @returns A promise that resolves when the save operation is complete.
105
+ * Saves the state to storage if it has changed since it was loaded.
106
+ * Change detection uses a hash of the state object to determine if saving is necessary.
107
+ *
108
+ * @param context The turn context containing the state to save
109
+ * @param force If true, forces a save to storage even if no changes are detected
110
+ * @param customKey Optional custom storage key to use instead of the default
111
+ * @returns A promise that resolves when the save operation is complete
77
112
  */
78
113
  public async saveChanges (context: TurnContext, force = false, customKey?: CustomKey): Promise<void> {
79
114
  let cached: CachedAgentState = context.turnState.get(this.stateKey)
@@ -95,6 +130,14 @@ export class AgentState {
95
130
  }
96
131
  }
97
132
 
133
+ /**
134
+ * Determines whether to use a custom key or generate one from the context.
135
+ *
136
+ * @param customKey Optional custom key with channel and conversation IDs
137
+ * @param context The turn context used to generate a key if no custom key is provided
138
+ * @returns The storage key to use
139
+ * @private
140
+ */
98
141
  private async getStorageOrCustomKey (customKey: CustomKey | undefined, context: TurnContext) {
99
142
  let key: string | undefined
100
143
  if (customKey && customKey.channelId && customKey.conversationId) {
@@ -107,9 +150,12 @@ export class AgentState {
107
150
  }
108
151
 
109
152
  /**
110
- * Clears the state from the turn context.
111
- * @param context The turn context.
112
- * @returns A promise that resolves when the clear operation is complete.
153
+ * Clears the state by setting it to an empty object in the turn context.
154
+ * Note: This does not remove the state from storage, it only clears the in-memory representation.
155
+ * Call saveChanges() after this to persist the empty state to storage.
156
+ *
157
+ * @param context The turn context containing the state to clear
158
+ * @returns A promise that resolves when the clear operation is complete
113
159
  */
114
160
  public async clear (context: TurnContext): Promise<void> {
115
161
  const emptyObjectToForceSave = { state: {}, hash: '' }
@@ -118,9 +164,11 @@ export class AgentState {
118
164
  }
119
165
 
120
166
  /**
121
- * Deletes the state from storage.
122
- * @param context The turn context.
123
- * @returns A promise that resolves when the delete operation is complete.
167
+ * Deletes the state from both the turn context and storage.
168
+ *
169
+ * @param context The turn context containing the state to delete
170
+ * @param customKey Optional custom storage key to use instead of the default
171
+ * @returns A promise that resolves when the delete operation is complete
124
172
  */
125
173
  public async delete (context: TurnContext, customKey?: CustomKey): Promise<void> {
126
174
  if (context.turnState.has(this.stateKey)) {
@@ -132,9 +180,10 @@ export class AgentState {
132
180
  }
133
181
 
134
182
  /**
135
- * Gets the state from the turn context.
136
- * @param context The turn context.
137
- * @returns The state, or undefined if the state is not found.
183
+ * Gets the state from the turn context without loading it from storage.
184
+ *
185
+ * @param context The turn context containing the state to get
186
+ * @returns The state object, or undefined if no state is found in the turn context
138
187
  */
139
188
  public get (context: TurnContext): any | undefined {
140
189
  const cached: CachedAgentState = context.turnState.get(this.stateKey)
@@ -143,9 +192,12 @@ export class AgentState {
143
192
  }
144
193
 
145
194
  /**
146
- * Calculates the change hash for the specified item.
147
- * @param item The item to calculate the hash for.
148
- * @returns The calculated hash.
195
+ * Calculates a hash for the specified state object to detect changes.
196
+ * The eTag property is excluded from the hash calculation.
197
+ *
198
+ * @param item The state object to calculate the hash for
199
+ * @returns A string hash representing the state
200
+ * @private
149
201
  */
150
202
  private readonly calculateChangeHash = (item: StoreItem): string => {
151
203
  const { eTag, ...rest } = item