@microsoft/agents-hosting 1.7.0-beta.1.g8bcd4f11a7 → 1.7.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 (104) hide show
  1. package/README.md +16 -0
  2. package/dist/package.json +3 -3
  3. package/dist/src/agent-client/agentClient.js +4 -3
  4. package/dist/src/agent-client/agentClient.js.map +1 -1
  5. package/dist/src/agent-client/agentResponseHandler.d.ts +25 -6
  6. package/dist/src/agent-client/agentResponseHandler.js +16 -55
  7. package/dist/src/agent-client/agentResponseHandler.js.map +1 -1
  8. package/dist/src/agent-client/createAgentResponseHandler.d.ts +48 -0
  9. package/dist/src/agent-client/createAgentResponseHandler.js +92 -0
  10. package/dist/src/agent-client/createAgentResponseHandler.js.map +1 -0
  11. package/dist/src/agent-client/index.d.ts +1 -0
  12. package/dist/src/agent-client/index.js +1 -0
  13. package/dist/src/agent-client/index.js.map +1 -1
  14. package/dist/src/app/adaptiveCards/activityValueParsers.js +2 -1
  15. package/dist/src/app/adaptiveCards/activityValueParsers.js.map +1 -1
  16. package/dist/src/app/adaptiveCards/adaptiveCardsActions.js +13 -12
  17. package/dist/src/app/adaptiveCards/adaptiveCardsActions.js.map +1 -1
  18. package/dist/src/app/agentApplication.js +8 -8
  19. package/dist/src/app/agentApplication.js.map +1 -1
  20. package/dist/src/app/streaming/streamingResponse.js +2 -2
  21. package/dist/src/app/streaming/streamingResponse.js.map +1 -1
  22. package/dist/src/app/turnState.d.ts +0 -1
  23. package/dist/src/app/turnState.js +15 -14
  24. package/dist/src/app/turnState.js.map +1 -1
  25. package/dist/src/app/turnStateProperty.js +4 -2
  26. package/dist/src/app/turnStateProperty.js.map +1 -1
  27. package/dist/src/auth/authConfiguration.js +5 -3
  28. package/dist/src/auth/authConfiguration.js.map +1 -1
  29. package/dist/src/auth/jwt-middleware.d.ts +2 -2
  30. package/dist/src/auth/jwt-middleware.js +84 -6
  31. package/dist/src/auth/jwt-middleware.js.map +1 -1
  32. package/dist/src/auth/msal/msalTokenProvider.js +16 -16
  33. package/dist/src/auth/msal/msalTokenProvider.js.map +1 -1
  34. package/dist/src/baseAdapter.js +3 -1
  35. package/dist/src/baseAdapter.js.map +1 -1
  36. package/dist/src/cloudAdapter.d.ts +2 -2
  37. package/dist/src/cloudAdapter.js +10 -10
  38. package/dist/src/cloudAdapter.js.map +1 -1
  39. package/dist/src/createCloudAdapter.d.ts +40 -0
  40. package/dist/src/createCloudAdapter.js +44 -0
  41. package/dist/src/createCloudAdapter.js.map +1 -0
  42. package/dist/src/errorHelper.js +206 -79
  43. package/dist/src/errorHelper.js.map +1 -1
  44. package/dist/src/headerPropagation.js +3 -1
  45. package/dist/src/headerPropagation.js.map +1 -1
  46. package/dist/src/index.d.ts +2 -0
  47. package/dist/src/index.js +2 -0
  48. package/dist/src/index.js.map +1 -1
  49. package/dist/src/interfaces/index.d.ts +5 -0
  50. package/dist/src/interfaces/index.js +22 -0
  51. package/dist/src/interfaces/index.js.map +1 -0
  52. package/dist/src/interfaces/webResponse.d.ts +41 -0
  53. package/dist/src/interfaces/webResponse.js +7 -0
  54. package/dist/src/interfaces/webResponse.js.map +1 -0
  55. package/dist/src/middlewareSet.d.ts +0 -1
  56. package/dist/src/middlewareSet.js +4 -1
  57. package/dist/src/middlewareSet.js.map +1 -1
  58. package/dist/src/oauth/userTokenClient.js +2 -1
  59. package/dist/src/oauth/userTokenClient.js.map +1 -1
  60. package/dist/src/state/conversationState.js +4 -2
  61. package/dist/src/state/conversationState.js.map +1 -1
  62. package/dist/src/state/userState.js +4 -2
  63. package/dist/src/state/userState.js.map +1 -1
  64. package/dist/src/storage/memoryStorage.js +5 -3
  65. package/dist/src/storage/memoryStorage.js.map +1 -1
  66. package/dist/src/transcript/consoleTranscriptLogger.js +3 -1
  67. package/dist/src/transcript/consoleTranscriptLogger.js.map +1 -1
  68. package/dist/src/transcript/fileTranscriptLogger.js +5 -4
  69. package/dist/src/transcript/fileTranscriptLogger.js.map +1 -1
  70. package/dist/src/transcript/transcriptLoggerMiddleware.js +2 -1
  71. package/dist/src/transcript/transcriptLoggerMiddleware.js.map +1 -1
  72. package/dist/src/turnContext.js +2 -1
  73. package/dist/src/turnContext.js.map +1 -1
  74. package/package.json +3 -3
  75. package/src/agent-client/agentClient.ts +5 -4
  76. package/src/agent-client/agentResponseHandler.ts +43 -67
  77. package/src/agent-client/createAgentResponseHandler.ts +133 -0
  78. package/src/agent-client/index.ts +1 -0
  79. package/src/app/adaptiveCards/activityValueParsers.ts +3 -2
  80. package/src/app/adaptiveCards/adaptiveCardsActions.ts +5 -5
  81. package/src/app/agentApplication.ts +8 -16
  82. package/src/app/streaming/streamingResponse.ts +2 -2
  83. package/src/app/turnState.ts +15 -14
  84. package/src/app/turnStateProperty.ts +4 -2
  85. package/src/auth/authConfiguration.ts +5 -3
  86. package/src/auth/jwt-middleware.ts +87 -8
  87. package/src/auth/msal/msalTokenProvider.ts +16 -16
  88. package/src/baseAdapter.ts +3 -2
  89. package/src/cloudAdapter.ts +12 -12
  90. package/src/createCloudAdapter.ts +56 -0
  91. package/src/errorHelper.ts +244 -89
  92. package/src/headerPropagation.ts +4 -1
  93. package/src/index.ts +2 -0
  94. package/src/interfaces/index.ts +6 -0
  95. package/src/interfaces/webResponse.ts +44 -0
  96. package/src/middlewareSet.ts +3 -1
  97. package/src/oauth/userTokenClient.ts +3 -2
  98. package/src/state/conversationState.ts +4 -3
  99. package/src/state/userState.ts +4 -3
  100. package/src/storage/memoryStorage.ts +5 -3
  101. package/src/transcript/consoleTranscriptLogger.ts +3 -2
  102. package/src/transcript/fileTranscriptLogger.ts +6 -5
  103. package/src/transcript/transcriptLoggerMiddleware.ts +3 -2
  104. package/src/turnContext.ts +3 -2
@@ -1,11 +1,12 @@
1
1
  import { AuthConfiguration, MsalTokenProvider } from '../auth'
2
- import { Activity, ConversationReference, RoleTypes } from '@microsoft/agents-activity'
2
+ import { Activity, ConversationReference, ExceptionHelper, RoleTypes } from '@microsoft/agents-activity'
3
3
  import { randomUUID } from 'crypto'
4
4
  import { debug } from '@microsoft/agents-telemetry'
5
5
  import { ConversationState } from '../state'
6
6
  import { TurnContext } from '../turnContext'
7
7
  import { trace } from '@microsoft/agents-telemetry'
8
8
  import { AgentClientTraceDefinitions } from '../observability'
9
+ import { Errors } from '../errorHelper'
9
10
 
10
11
  const logger = debug('agents:agent-client')
11
12
 
@@ -125,7 +126,7 @@ export class AgentClient {
125
126
 
126
127
  if (!response.ok) {
127
128
  await conversationDataAccessor.delete(context, { channelId: activityCopy.channelId!, conversationId: activityCopy.conversation!.id })
128
- throw new Error(`Failed to post activity to agent: ${response.statusText}`)
129
+ throw ExceptionHelper.generateException(Error, Errors.FailedToPostActivityToAgent, undefined, { statusText: response.statusText })
129
130
  }
130
131
  return response.statusText
131
132
  })
@@ -150,10 +151,10 @@ export class AgentClient {
150
151
  serviceUrl: process.env[`${agentName}_serviceUrl`]!
151
152
  }
152
153
  } else {
153
- throw new Error(`Missing agent client config for agent ${agentName}`)
154
+ throw ExceptionHelper.generateException(Error, Errors.MissingAgentClientConfig, undefined, { agentName })
154
155
  }
155
156
  } else {
156
- throw new Error('Agent name is required')
157
+ throw ExceptionHelper.generateException(Error, Errors.AgentNameRequired)
157
158
  }
158
159
  }
159
160
  }
@@ -1,17 +1,33 @@
1
- import { Activity, ActivityTypes, ConversationReference } from '@microsoft/agents-activity'
1
+ /**
2
+ * Copyright (c) Microsoft Corporation. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
2
6
  import { ActivityHandler } from '../activityHandler'
3
7
  import { CloudAdapter } from '../cloudAdapter'
4
- import { Request, Response, Application } from 'express'
5
- import { TurnContext } from '../turnContext'
6
- import { randomUUID } from 'crypto'
7
- import { normalizeIncomingActivity } from '../activityWireCompat'
8
- import { debug } from '@microsoft/agents-telemetry'
9
8
  import { ConversationState } from '../state'
9
+ import { WebRequestParamsCarrier, WebResponse } from '../interfaces/webResponse'
10
+ import { Request } from '../auth/request'
11
+ import { AGENT_RESPONSE_ROUTE_PATH, createAgentResponseHandler } from './createAgentResponseHandler'
10
12
 
11
- const logger = debug('agents:agent-client')
12
-
13
- interface ConversationReferenceState {
14
- conversationReference: ConversationReference
13
+ /**
14
+ * Minimal application surface needed by {@link configureResponseController} to
15
+ * register the agent-to-agent response POST route. Express's `Application`
16
+ * structurally satisfies this; frameworks that do not (e.g., Fastify) should
17
+ * call {@link createAgentResponseHandler} directly and register the route
18
+ * themselves rather than passing a synthetic `WebApp`.
19
+ *
20
+ * `WebApp` is a minimal structural shape rather than a richer, named
21
+ * route-registrar contract. It is exported so it has a stable name in the
22
+ * generated type declarations and API report (it appears in the exported
23
+ * {@link configureResponseController} signature). Any framework `app` whose
24
+ * `post(path, handler)` method structurally matches satisfies it.
25
+ */
26
+ export interface WebApp {
27
+ post (
28
+ path: string,
29
+ handler: (req: any, res: any) => unknown | Promise<unknown>
30
+ ): unknown
15
31
  }
16
32
 
17
33
  /**
@@ -33,10 +49,10 @@ interface ConversationReferenceState {
33
49
  * - Processing EndOfConversation activities by cleaning up conversation state
34
50
  * - Sending activities through the turn context and returning responses
35
51
  *
36
- * @param app - The Express application instance to configure the route on
37
- * @param adapter - The CloudAdapter instance used for processing bot framework activities and managing conversations
38
- * @param agent - The ActivityHandler instance that contains the bot's logic for processing different types of activities
39
- * @param conversationState - The ConversationState instance used for managing conversation-specific state and conversation references
52
+ * @param app - The application instance (Express `Application` or any framework that satisfies {@link WebApp}) to configure the route on.
53
+ * @param adapter - The CloudAdapter instance used for processing bot framework activities and managing conversations.
54
+ * @param agent - The ActivityHandler instance that contains the bot's logic for processing different types of activities.
55
+ * @param conversationState - The ConversationState instance used for managing conversation-specific state and conversation references.
40
56
  *
41
57
  * @example
42
58
  * ```typescript
@@ -48,57 +64,17 @@ interface ConversationReferenceState {
48
64
  * configureResponseController(app, adapter, agent, conversationState);
49
65
  * ```
50
66
  */
51
- export const configureResponseController = (app: Application, adapter: CloudAdapter, agent: ActivityHandler, conversationState: ConversationState) => {
52
- app.post('/api/agentresponse/v3/conversations/:conversationId/activities/:activityId', handleResponse(adapter, agent, conversationState))
53
- }
54
-
55
- const handleResponse = (adapter: CloudAdapter, handler: ActivityHandler, conversationState: ConversationState) => async (req: Request, res: Response) => {
56
- const incoming = normalizeIncomingActivity(req.body!)
57
- const activity = Activity.fromObject(incoming)
58
-
59
- logger.debug('received response: ', activity)
60
-
61
- const connection = adapter.connectionManager.getDefaultConnection()
62
- const appId = connection?.connectionSettings?.clientId ?? ''
63
-
64
- const myTurnContext = new TurnContext(adapter, activity, CloudAdapter.createIdentity(appId))
65
- const conversationDataAccessor = conversationState.createProperty<ConversationReferenceState>(req.params!.conversationId as string)
66
- const conversationRefState = await conversationDataAccessor.get(myTurnContext, undefined, { channelId: activity.channelId!, conversationId: req.params!.conversationId as string })
67
-
68
- const conversationRef = JSON.stringify(conversationRefState.conversationReference)
69
- console.log('conversationRef', conversationRef)
70
- const callback = async (turnContext: TurnContext) => {
71
- activity.applyConversationReference(conversationRefState.conversationReference)
72
- turnContext.activity.id = req.params!.activityId as string
73
-
74
- let response
75
- if (activity.type === ActivityTypes.EndOfConversation) {
76
- await conversationDataAccessor.delete(turnContext, { channelId: activity.channelId!, conversationId: activity.conversation!.id })
77
-
78
- applyActivityToTurnContext(turnContext, activity)
79
- await handler.run(turnContext)
80
-
81
- response = randomUUID().replace(/-/g, '')
82
- } else {
83
- response = await turnContext.sendActivity(activity)
84
- }
85
- res.status(200).send(response)
86
- }
87
-
88
- await adapter.continueConversation(myTurnContext.identity, conversationRefState.conversationReference, callback)
89
- }
90
-
91
- const applyActivityToTurnContext = (turnContext : TurnContext, activity : Activity) => {
92
- turnContext.activity.channelData = activity.channelData
93
- turnContext.activity.code = activity.code
94
- turnContext.activity.entities = activity.entities
95
- turnContext.activity.locale = activity.locale
96
- turnContext.activity.localTimestamp = activity.localTimestamp
97
- turnContext.activity.name = activity.name
98
- turnContext.activity.relatesTo = activity.relatesTo
99
- turnContext.activity.replyToId = activity.replyToId
100
- turnContext.activity.timestamp = activity.timestamp
101
- turnContext.activity.text = activity.text
102
- turnContext.activity.type = activity.type
103
- turnContext.activity.value = activity.value
67
+ export const configureResponseController = (
68
+ app: WebApp,
69
+ adapter: CloudAdapter,
70
+ agent: ActivityHandler,
71
+ conversationState: ConversationState
72
+ ) => {
73
+ const handler = createAgentResponseHandler(adapter, agent, conversationState)
74
+ app.post(AGENT_RESPONSE_ROUTE_PATH, async (req: Request & WebRequestParamsCarrier, res: WebResponse) => {
75
+ await handler(req, res, {
76
+ conversationId: req.params!.conversationId as string,
77
+ activityId: req.params!.activityId as string
78
+ })
79
+ })
104
80
  }
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Copyright (c) Microsoft Corporation. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import { Activity, ActivityTypes, ConversationReference, ExceptionHelper } from '@microsoft/agents-activity'
7
+ import { ActivityHandler } from '../activityHandler'
8
+ import { CloudAdapter } from '../cloudAdapter'
9
+ import { Request } from '../auth/request'
10
+ import { WebResponse } from '../interfaces/webResponse'
11
+ import { TurnContext } from '../turnContext'
12
+ import { randomUUID } from 'crypto'
13
+ import { normalizeIncomingActivity } from '../activityWireCompat'
14
+ import { Errors } from '../errorHelper'
15
+ import { debug } from '@microsoft/agents-telemetry'
16
+ import { ConversationState } from '../state'
17
+
18
+ const logger = debug('agents:agent-client')
19
+
20
+ interface ConversationReferenceState {
21
+ conversationReference: ConversationReference
22
+ }
23
+
24
+ /**
25
+ * Route parameters supplied to {@link AgentResponseHandler} — typically pulled from
26
+ * the framework's URL path parser.
27
+ */
28
+ export interface AgentResponseHandlerParams {
29
+ conversationId: string
30
+ activityId: string
31
+ }
32
+
33
+ /**
34
+ * Framework-agnostic handler signature for the agent response controller endpoint.
35
+ *
36
+ * @remarks
37
+ * The handler is intended to be invoked by a thin framework-specific wrapper (such
38
+ * as Express's `configureResponseController(app, ...)`) that extracts the path
39
+ * parameters and forwards a parsed `req.body` plus an {@link WebResponse}.
40
+ */
41
+ export type AgentResponseHandler = (
42
+ req: Request,
43
+ res: WebResponse,
44
+ params: AgentResponseHandlerParams
45
+ ) => Promise<void>
46
+
47
+ /**
48
+ * Creates a framework-agnostic handler for the agent response controller endpoint.
49
+ *
50
+ * This is the core, Express-free implementation used by:
51
+ * - `configureResponseController` in `@microsoft/agents-hosting-express`
52
+ * - `configureResponseController` in `@microsoft/agents-hosting-fastify`
53
+ *
54
+ * Both wrappers register the canonical route
55
+ * `POST /api/agentresponse/v3/conversations/:conversationId/activities/:activityId`
56
+ * and forward the parsed body + path parameters to the handler returned here.
57
+ *
58
+ * @param adapter - The CloudAdapter used for processing activities and managing conversations.
59
+ * @param agent - The ActivityHandler containing the agent logic.
60
+ * @param conversationState - The ConversationState used to look up the stored conversation reference.
61
+ * @returns A handler `(req, res, params) => Promise<void>`.
62
+ */
63
+ export const createAgentResponseHandler = (
64
+ adapter: CloudAdapter,
65
+ agent: ActivityHandler,
66
+ conversationState: ConversationState
67
+ ): AgentResponseHandler => {
68
+ return async (req: Request, res: WebResponse, params: AgentResponseHandlerParams) => {
69
+ if (!req.body) {
70
+ throw ExceptionHelper.generateException(TypeError, Errors.MissingRequestBody)
71
+ }
72
+ const incoming = normalizeIncomingActivity(req.body)
73
+ const activity = Activity.fromObject(incoming)
74
+
75
+ logger.debug('received response: ', activity)
76
+
77
+ const connection = adapter.connectionManager.getDefaultConnection()
78
+ const appId = connection?.connectionSettings?.clientId ?? ''
79
+
80
+ const myTurnContext = new TurnContext(adapter, activity, CloudAdapter.createIdentity(appId))
81
+ const conversationDataAccessor = conversationState.createProperty<ConversationReferenceState>(params.conversationId)
82
+ const conversationRefState = await conversationDataAccessor.get(myTurnContext, undefined, { channelId: activity.channelId!, conversationId: params.conversationId })
83
+
84
+ const callback = async (turnContext: TurnContext) => {
85
+ activity.applyConversationReference(conversationRefState.conversationReference)
86
+ turnContext.activity.id = params.activityId
87
+
88
+ let response: unknown
89
+ let responseContentType: string | undefined
90
+ if (activity.type === ActivityTypes.EndOfConversation) {
91
+ await conversationDataAccessor.delete(turnContext, { channelId: activity.channelId!, conversationId: activity.conversation!.id })
92
+
93
+ applyActivityToTurnContext(turnContext, activity)
94
+ await agent.run(turnContext)
95
+
96
+ response = randomUUID().replace(/-/g, '')
97
+ // Explicitly set the content-type so Express and Fastify both emit the same
98
+ // value for this raw-string body (Express defaults to text/html, Fastify to
99
+ // text/plain when the framework auto-detects).
100
+ responseContentType = 'text/plain; charset=utf-8'
101
+ } else {
102
+ response = await turnContext.sendActivity(activity)
103
+ }
104
+ if (responseContentType !== undefined) {
105
+ res.setHeader('content-type', responseContentType)
106
+ }
107
+ res.status(200).send(response)
108
+ }
109
+
110
+ await adapter.continueConversation(myTurnContext.identity, conversationRefState.conversationReference, callback)
111
+ }
112
+ }
113
+
114
+ const applyActivityToTurnContext = (turnContext: TurnContext, activity: Activity) => {
115
+ turnContext.activity.channelData = activity.channelData
116
+ turnContext.activity.code = activity.code
117
+ turnContext.activity.entities = activity.entities
118
+ turnContext.activity.locale = activity.locale
119
+ turnContext.activity.localTimestamp = activity.localTimestamp
120
+ turnContext.activity.name = activity.name
121
+ turnContext.activity.relatesTo = activity.relatesTo
122
+ turnContext.activity.replyToId = activity.replyToId
123
+ turnContext.activity.timestamp = activity.timestamp
124
+ turnContext.activity.text = activity.text
125
+ turnContext.activity.type = activity.type
126
+ turnContext.activity.value = activity.value
127
+ }
128
+
129
+ /**
130
+ * Canonical route path for the agent response controller endpoint.
131
+ * Both Express and Fastify wrappers should register on this path.
132
+ */
133
+ export const AGENT_RESPONSE_ROUTE_PATH = '/api/agentresponse/v3/conversations/:conversationId/activities/:activityId'
@@ -1,2 +1,3 @@
1
1
  export * from './agentClient'
2
2
  export * from './agentResponseHandler'
3
+ export * from './createAgentResponseHandler'
@@ -4,9 +4,10 @@
4
4
  */
5
5
 
6
6
  import { z } from 'zod'
7
- import { activityZodSchema, AdaptiveCardInvokeAction, adaptiveCardInvokeActionZodSchema } from '@microsoft/agents-activity'
7
+ import { activityZodSchema, AdaptiveCardInvokeAction, adaptiveCardInvokeActionZodSchema, ExceptionHelper } from '@microsoft/agents-activity'
8
8
  // import { MessagingExtensionQuery, messagingExtensionQueryZodSchema } from '../messageExtension/messagingExtensionQuery'
9
9
  import { adaptiveCardsSearchParamsZodSchema } from './adaptiveCardsSearchParams'
10
+ import { Errors } from '../../errorHelper'
10
11
 
11
12
  /**
12
13
  * Parses the given value as a value action.
@@ -76,7 +77,7 @@ export function parseValueActionExecuteSelector (value: unknown): ValueAction |
76
77
  })
77
78
  const safeParsedValue = actionValueExecuteSelector.passthrough().safeParse(value)
78
79
  if (!safeParsedValue.success) {
79
- throw new Error(`Invalid action value: ${safeParsedValue.error}`)
80
+ throw ExceptionHelper.generateException(Error, Errors.InvalidActionValue, undefined, { error: String(safeParsedValue.error) })
80
81
  }
81
82
  const parsedValue = safeParsedValue.data
82
83
  return {
@@ -3,13 +3,14 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { Activity, ActivityTypes } from '@microsoft/agents-activity'
6
+ import { Activity, ActivityTypes, ExceptionHelper } from '@microsoft/agents-activity'
7
7
  import { AdaptiveCardInvokeResponse, AgentApplication, CardFactory, INVOKE_RESPONSE_KEY, InvokeResponse, MessageFactory, RouteSelector, TurnContext, TurnState } from '../../'
8
8
  import { AdaptiveCardActionExecuteResponseType } from './adaptiveCardActionExecuteResponseType'
9
9
  import { parseAdaptiveCardInvokeAction, parseValueActionExecuteSelector, parseValueDataset, parseValueSearchQuery } from './activityValueParsers'
10
10
  import { AdaptiveCardsSearchParams } from './adaptiveCardsSearchParams'
11
11
  import { AdaptiveCard } from '../../cards/adaptiveCard'
12
12
  import { Query } from './query'
13
+ import { Errors } from '../../errorHelper'
13
14
 
14
15
  export const ACTION_INVOKE_NAME = 'adaptiveCard/action'
15
16
  const ACTION_EXECUTE_TYPE = 'Action.Execute'
@@ -117,8 +118,7 @@ export class AdaptiveCardsActions<TState extends TurnState> {
117
118
  a?.name !== ACTION_INVOKE_NAME ||
118
119
  (invokeAction?.action.type !== ACTION_EXECUTE_TYPE)
119
120
  ) {
120
- throw new Error(`Unexpected AdaptiveCards.actionExecute() triggered for activity type: ${invokeAction?.action.type}`
121
- )
121
+ throw ExceptionHelper.generateException(Error, Errors.UnexpectedActionExecute, undefined, { activityType: invokeAction?.action.type ?? 'undefined' })
122
122
  }
123
123
 
124
124
  if (invokeAction.action.verb !== v) {
@@ -196,7 +196,7 @@ export class AdaptiveCardsActions<TState extends TurnState> {
196
196
  this._app.addRoute(selector, async (context, state) => {
197
197
  const a = context?.activity
198
198
  if (a?.type !== ActivityTypes.Message || a?.text || typeof a?.value !== 'object') {
199
- throw new Error(`Unexpected AdaptiveCards.actionSubmit() triggered for activity type: ${a?.type}`)
199
+ throw ExceptionHelper.generateException(Error, Errors.UnexpectedActionSubmit, undefined, { activityType: a?.type ?? 'undefined' })
200
200
  }
201
201
 
202
202
  await handler(context, state as TState, (parseAdaptiveCardInvokeAction(a.value)) as TData ?? {} as TData)
@@ -227,7 +227,7 @@ export class AdaptiveCardsActions<TState extends TurnState> {
227
227
  async (context, state) => {
228
228
  const a = context?.activity
229
229
  if (a?.type !== 'invoke' || a?.name !== SEARCH_INVOKE_NAME) {
230
- throw new Error(`Unexpected AdaptiveCards.search() triggered for activity type: ${a?.type}`)
230
+ throw ExceptionHelper.generateException(Error, Errors.UnexpectedSearchAction, undefined, { activityType: a?.type ?? 'undefined' })
231
231
  }
232
232
 
233
233
  const parsedQuery = parseValueSearchQuery(a.value)
@@ -155,12 +155,12 @@ export class AgentApplication<TState extends TurnState> {
155
155
  }
156
156
 
157
157
  if (this._options.longRunningMessages && !this._adapter && !this._options.agentAppId) {
158
- throw new Error('The Application.longRunningMessages property is unavailable because no adapter was configured in the app.')
158
+ throw ExceptionHelper.generateException(Error, Errors.LongRunningMessagesPropertyUnavailable)
159
159
  }
160
160
 
161
161
  if (this._options.transcriptLogger) {
162
162
  if (!this._options.adapter) {
163
- throw new Error('The Application.transcriptLogger property is unavailable because no adapter was configured in the app.')
163
+ throw ExceptionHelper.generateException(Error, Errors.TranscriptLoggerPropertyUnavailable)
164
164
  } else {
165
165
  this._adapter?.use(new TranscriptLoggerMiddleware(this._options.transcriptLogger))
166
166
  }
@@ -205,7 +205,7 @@ export class AgentApplication<TState extends TurnState> {
205
205
  */
206
206
  public get authorization (): Authorization {
207
207
  if (this._authorizationManager.handlers.length === 0) {
208
- throw new Error('The Application.authorization property is unavailable because no authorization options were configured.')
208
+ throw ExceptionHelper.generateException(Error, Errors.AuthorizationOptionNotAvailable)
209
209
  }
210
210
  return this._authorization
211
211
  }
@@ -400,9 +400,7 @@ export class AgentApplication<TState extends TurnState> {
400
400
  isAgenticRoute: boolean = false
401
401
  ): this {
402
402
  if (typeof handler !== 'function') {
403
- throw new Error(
404
- `ConversationUpdate 'handler' for ${event} is ${typeof handler}. Type of 'handler' must be a function.`
405
- )
403
+ throw ExceptionHelper.generateException(Error, Errors.ConversationUpdateHandlerMustBeFunction, undefined, { event, handlerType: typeof handler })
406
404
  }
407
405
 
408
406
  const selector = this.createConversationUpdateSelector(event, isAgenticRoute)
@@ -424,9 +422,7 @@ export class AgentApplication<TState extends TurnState> {
424
422
  logic: (context: TurnContext) => Promise<void>
425
423
  ): Promise<void> {
426
424
  if (!this._adapter) {
427
- throw new Error(
428
- "You must configure the Application with an 'adapter' before calling Application.continueConversationAsync()"
429
- )
425
+ throw ExceptionHelper.generateException(Error, Errors.ContinueConversationAdapterRequired)
430
426
  }
431
427
 
432
428
  if (!this.options.agentAppId) {
@@ -510,9 +506,7 @@ export class AgentApplication<TState extends TurnState> {
510
506
  if (this.options.authorization) {
511
507
  this.authorization.onSignInSuccess(handler)
512
508
  } else {
513
- throw new Error(
514
- 'The Application.authorization property is unavailable because no authorization options were configured.'
515
- )
509
+ throw ExceptionHelper.generateException(Error, Errors.AuthorizationOptionNotAvailable)
516
510
  }
517
511
  return this
518
512
  }
@@ -540,9 +534,7 @@ export class AgentApplication<TState extends TurnState> {
540
534
  if (this.options.authorization) {
541
535
  this.authorization.onSignInFailure(handler)
542
536
  } else {
543
- throw new Error(
544
- 'The Application.authorization property is unavailable because no authorization options were configured.'
545
- )
537
+ throw ExceptionHelper.generateException(Error, Errors.AuthorizationOptionNotAvailable)
546
538
  }
547
539
  return this
548
540
  }
@@ -965,7 +957,7 @@ export class AgentApplication<TState extends TurnState> {
965
957
  */
966
958
  public registerExtension<T extends AgentExtension<TState>> (extension: T, regcb : (ext:T) => void): void {
967
959
  if (this._extensions.includes(extension)) {
968
- throw new Error('Extension already registered')
960
+ throw ExceptionHelper.generateException(Error, Errors.ExtensionAlreadyRegistered)
969
961
  }
970
962
  this._extensions.push(extension)
971
963
  regcb(extension)
@@ -138,7 +138,7 @@ export class StreamingResponse {
138
138
  }
139
139
 
140
140
  if (this._ended) {
141
- throw new Error('The stream has already ended.')
141
+ throw ExceptionHelper.generateException(Error, Errors.StreamAlreadyEnded)
142
142
  }
143
143
 
144
144
  // Queue a typing activity
@@ -170,7 +170,7 @@ export class StreamingResponse {
170
170
  }
171
171
 
172
172
  if (this._ended) {
173
- throw new Error('The stream has already ended.')
173
+ throw ExceptionHelper.generateException(Error, Errors.StreamAlreadyEnded)
174
174
  }
175
175
 
176
176
  // Update full message text
@@ -8,6 +8,8 @@ import { AppMemory } from './appMemory'
8
8
  import { TurnStateEntry } from './turnStateEntry'
9
9
  import { TurnContext } from '../turnContext'
10
10
  import { debug } from '@microsoft/agents-telemetry'
11
+ import { ExceptionHelper } from '@microsoft/agents-activity'
12
+ import { Errors } from '../errorHelper'
11
13
 
12
14
  const logger = debug('agents:turnState')
13
15
 
@@ -73,7 +75,6 @@ export class TurnState<
73
75
  private _scopes: Record<string, TurnStateEntry> = {}
74
76
  private _isLoaded = false
75
77
  private _loadingPromise?: Promise<boolean>
76
- private _stateNotLoadedString = 'TurnState hasn\'t been loaded. Call load() first.'
77
78
 
78
79
  /**
79
80
  * Gets the conversation-scoped state.
@@ -87,7 +88,7 @@ export class TurnState<
87
88
  public get conversation (): TConversationState {
88
89
  const scope = this.getScope(CONVERSATION_SCOPE)
89
90
  if (!scope) {
90
- throw new Error(this._stateNotLoadedString)
91
+ throw ExceptionHelper.generateException(Error, Errors.StateNotLoaded)
91
92
  }
92
93
  return scope.value as TConversationState
93
94
  }
@@ -101,7 +102,7 @@ export class TurnState<
101
102
  public set conversation (value: TConversationState) {
102
103
  const scope = this.getScope(CONVERSATION_SCOPE)
103
104
  if (!scope) {
104
- throw new Error(this._stateNotLoadedString)
105
+ throw ExceptionHelper.generateException(Error, Errors.StateNotLoaded)
105
106
  }
106
107
  scope.replace(value as Record<string, unknown>)
107
108
  }
@@ -127,7 +128,7 @@ export class TurnState<
127
128
  public get user (): TUserState {
128
129
  const scope = this.getScope(USER_SCOPE)
129
130
  if (!scope) {
130
- throw new Error(this._stateNotLoadedString)
131
+ throw ExceptionHelper.generateException(Error, Errors.StateNotLoaded)
131
132
  }
132
133
  return scope.value as TUserState
133
134
  }
@@ -141,7 +142,7 @@ export class TurnState<
141
142
  public set user (value: TUserState) {
142
143
  const scope = this.getScope(USER_SCOPE)
143
144
  if (!scope) {
144
- throw new Error(this._stateNotLoadedString)
145
+ throw ExceptionHelper.generateException(Error, Errors.StateNotLoaded)
145
146
  }
146
147
  scope.replace(value as Record<string, unknown>)
147
148
  }
@@ -157,7 +158,7 @@ export class TurnState<
157
158
  public deleteConversationState (): void {
158
159
  const scope = this.getScope(CONVERSATION_SCOPE)
159
160
  if (!scope) {
160
- throw new Error(this._stateNotLoadedString)
161
+ throw ExceptionHelper.generateException(Error, Errors.StateNotLoaded)
161
162
  }
162
163
  scope.delete()
163
164
  }
@@ -173,7 +174,7 @@ export class TurnState<
173
174
  public deleteUserState (): void {
174
175
  const scope = this.getScope(USER_SCOPE)
175
176
  if (!scope) {
176
- throw new Error(this._stateNotLoadedString)
177
+ throw ExceptionHelper.generateException(Error, Errors.StateNotLoaded)
177
178
  }
178
179
  scope.delete()
179
180
  }
@@ -316,7 +317,7 @@ export class TurnState<
316
317
  }
317
318
 
318
319
  if (!this._isLoaded) {
319
- throw new Error(this._stateNotLoadedString)
320
+ throw ExceptionHelper.generateException(Error, Errors.StateNotLoaded)
320
321
  }
321
322
 
322
323
  let changes: StoreItems | undefined
@@ -378,19 +379,19 @@ export class TurnState<
378
379
  const userId = activity?.from?.id
379
380
 
380
381
  if (!channelId) {
381
- throw new Error('missing context.activity.channelId')
382
+ throw ExceptionHelper.generateException(Error, Errors.MissingContextActivityChannelId)
382
383
  }
383
384
 
384
385
  if (!agentId) {
385
- throw new Error('missing context.activity.recipient.id')
386
+ throw ExceptionHelper.generateException(Error, Errors.MissingContextActivityRecipientId)
386
387
  }
387
388
 
388
389
  if (!conversationId) {
389
- throw new Error('missing context.activity.conversation.id')
390
+ throw ExceptionHelper.generateException(Error, Errors.MissingContextActivityConversationId)
390
391
  }
391
392
 
392
393
  if (!userId) {
393
- throw new Error('missing context.activity.from.id')
394
+ throw ExceptionHelper.generateException(Error, Errors.MissingContextActivityFromId)
394
395
  }
395
396
 
396
397
  const keys: Record<string, string> = {}
@@ -413,14 +414,14 @@ export class TurnState<
413
414
  private getScopeAndName (path: string): { scope: TurnStateEntry; name: string } {
414
415
  const parts = path.split('.')
415
416
  if (parts.length > 2) {
416
- throw new Error(`Invalid state path: ${path}`)
417
+ throw ExceptionHelper.generateException(Error, Errors.InvalidStatePath, undefined, { path })
417
418
  } else if (parts.length === 1) {
418
419
  parts.unshift(TEMP_SCOPE)
419
420
  }
420
421
 
421
422
  const scope = this.getScope(parts[0])
422
423
  if (scope === undefined) {
423
- throw new Error(`Invalid state scope: ${parts[0]}`)
424
+ throw ExceptionHelper.generateException(Error, Errors.InvalidStateScope, undefined, { scope: parts[0] })
424
425
  }
425
426
  return { scope, name: parts[1] }
426
427
  }
@@ -7,6 +7,8 @@ import { TurnContext } from '../turnContext'
7
7
  import { StatePropertyAccessor } from '../state'
8
8
  import { TurnStateEntry } from './turnStateEntry'
9
9
  import { TurnState } from './turnState'
10
+ import { ExceptionHelper } from '@microsoft/agents-activity'
11
+ import { Errors } from '../errorHelper'
10
12
 
11
13
  /**
12
14
  * Maps an application's Turn State property to a State property.
@@ -27,12 +29,12 @@ export class TurnStateProperty<T = any> implements StatePropertyAccessor<T> {
27
29
 
28
30
  const scope = state.getScope(scopeName)
29
31
  if (!scope) {
30
- throw new Error(`TurnStateProperty: TurnState missing state scope named "${scope}".`)
32
+ throw ExceptionHelper.generateException(Error, Errors.TurnStateMissingStateScope, undefined, { scope: scopeName })
31
33
  }
32
34
 
33
35
  this._state = scope
34
36
  if (!this._state) {
35
- throw new Error(`TurnStateProperty: TurnState missing state scope named "${scope}".`)
37
+ throw ExceptionHelper.generateException(Error, Errors.TurnStateMissingStateScope, undefined, { scope: scopeName })
36
38
  }
37
39
  }
38
40
 
@@ -8,6 +8,8 @@ import { loadEnvSettings, AuthConfiguration, envParser, envParserUtils, LoadEnv,
8
8
 
9
9
  export { type AuthConfiguration, type ConnectionSettings, type ConnectionSettingsBase, type MsalConnectionSettings, type SidecarConnectionSettings, AuthType, resolveAuthority, type ConnectionMapItem, resolveAuthType } from './settings'
10
10
  import { prune } from '../utils'
11
+ import { ExceptionHelper } from '@microsoft/agents-activity'
12
+ import { Errors } from '../errorHelper'
11
13
 
12
14
  const logger = debug('agents:authConfiguration')
13
15
 
@@ -118,12 +120,12 @@ const connectionsEnv = {
118
120
  const map = connectionsMap ?? connectionsMapEnv.connectionsMap
119
121
  const name = map?.find((item) => item.serviceUrl === '*')?.connection
120
122
  if (!name) {
121
- throw new Error('No default connection found in environment connections.')
123
+ throw ExceptionHelper.generateException(Error, Errors.NoDefaultConnectionFound)
122
124
  }
123
125
 
124
126
  const connection = conn?.get(name ?? '')
125
127
  if (!connection) {
126
- throw new Error(`Connection "${name}" not found in environment connections.`)
128
+ throw ExceptionHelper.generateException(Error, Errors.ConnectionNotFoundInEnvironment, undefined, { connectionName: name })
127
129
  }
128
130
 
129
131
  return applyDefaultSettings({ ...connection, connections: conn, connectionsMap: map })
@@ -359,7 +361,7 @@ export const loadAuthConfigFromEnv = (cnxName?: string): AuthConfiguration => {
359
361
  // No connections provided, we need to populate the connections map with the old config settings
360
362
  const result = applyDefaultSettings(cnxName?.trim() ? legacyPrefixEnv.process(globalEnv.env, cnxName) : globalEnv.legacyPrefixSettings)
361
363
  if (cnxName && !result.clientId) {
362
- throw new Error(`ClientId not found for connection: ${cnxName}`)
364
+ throw ExceptionHelper.generateException(Error, Errors.ClientIdNotFoundForConnection, undefined, { connectionName: cnxName })
363
365
  }
364
366
 
365
367
  logger.info('Auth settings loaded from environment', {