@microsoft/agents-hosting 1.5.0-beta.5.g18f3420031 → 1.5.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.
- package/dist/package.json +10 -9
- package/dist/src/activityHandler.js +2 -2
- package/dist/src/activityHandler.js.map +1 -1
- package/dist/src/agent-client/agentClient.js +49 -40
- package/dist/src/agent-client/agentClient.js.map +1 -1
- package/dist/src/agent-client/agentResponseHandler.js +2 -2
- package/dist/src/agent-client/agentResponseHandler.js.map +1 -1
- package/dist/src/app/agentApplication.d.ts +36 -10
- package/dist/src/app/agentApplication.js +169 -99
- package/dist/src/app/agentApplication.js.map +1 -1
- package/dist/src/app/agentApplicationBuilder.d.ts +15 -0
- package/dist/src/app/agentApplicationBuilder.js +22 -4
- package/dist/src/app/agentApplicationBuilder.js.map +1 -1
- package/dist/src/app/agentApplicationOptions.d.ts +38 -0
- package/dist/src/app/attachmentDownloader.js +2 -2
- package/dist/src/app/attachmentDownloader.js.map +1 -1
- package/dist/src/app/auth/authorization.js +12 -9
- package/dist/src/app/auth/authorization.js.map +1 -1
- package/dist/src/app/auth/authorizationManager.d.ts +18 -5
- package/dist/src/app/auth/authorizationManager.js +258 -45
- package/dist/src/app/auth/authorizationManager.js.map +1 -1
- package/dist/src/app/auth/handlerStorage.js +3 -1
- package/dist/src/app/auth/handlerStorage.js.map +1 -1
- package/dist/src/app/auth/handlers/agenticAuthorization.d.ts +19 -16
- package/dist/src/app/auth/handlers/agenticAuthorization.js +46 -52
- package/dist/src/app/auth/handlers/agenticAuthorization.js.map +1 -1
- package/dist/src/app/auth/handlers/azureBotAuthorization.d.ts +51 -75
- package/dist/src/app/auth/handlers/azureBotAuthorization.js +217 -192
- package/dist/src/app/auth/handlers/azureBotAuthorization.js.map +1 -1
- package/dist/src/app/auth/types.d.ts +100 -1
- package/dist/src/app/auth/utils.d.ts +10 -0
- package/dist/src/app/auth/utils.js +21 -0
- package/dist/src/app/auth/utils.js.map +1 -0
- package/dist/src/app/index.d.ts +1 -0
- package/dist/src/app/index.js +1 -0
- package/dist/src/app/index.js.map +1 -1
- package/dist/src/app/proactive/conversation.d.ts +43 -0
- package/dist/src/app/proactive/conversation.js +67 -0
- package/dist/src/app/proactive/conversation.js.map +1 -0
- package/dist/src/app/proactive/conversationBuilder.d.ts +54 -0
- package/dist/src/app/proactive/conversationBuilder.js +110 -0
- package/dist/src/app/proactive/conversationBuilder.js.map +1 -0
- package/dist/src/app/proactive/conversationReferenceBuilder.d.ts +68 -0
- package/dist/src/app/proactive/conversationReferenceBuilder.js +125 -0
- package/dist/src/app/proactive/conversationReferenceBuilder.js.map +1 -0
- package/dist/src/app/proactive/createConversationOptions.d.ts +30 -0
- package/dist/src/app/proactive/createConversationOptions.js +10 -0
- package/dist/src/app/proactive/createConversationOptions.js.map +1 -0
- package/dist/src/app/proactive/createConversationOptionsBuilder.d.ts +69 -0
- package/dist/src/app/proactive/createConversationOptionsBuilder.js +141 -0
- package/dist/src/app/proactive/createConversationOptionsBuilder.js.map +1 -0
- package/dist/src/app/proactive/index.d.ts +7 -0
- package/dist/src/app/proactive/index.js +26 -0
- package/dist/src/app/proactive/index.js.map +1 -0
- package/dist/src/app/proactive/proactive.d.ts +248 -0
- package/dist/src/app/proactive/proactive.js +310 -0
- package/dist/src/app/proactive/proactive.js.map +1 -0
- package/dist/src/app/proactive/proactiveOptions.d.ts +19 -0
- package/dist/src/app/proactive/proactiveOptions.js +5 -0
- package/dist/src/app/proactive/proactiveOptions.js.map +1 -0
- package/dist/src/app/streaming/streamingResponse.js +2 -2
- package/dist/src/app/streaming/streamingResponse.js.map +1 -1
- package/dist/src/app/teamsAttachmentDownloader.js +2 -2
- package/dist/src/app/teamsAttachmentDownloader.js.map +1 -1
- package/dist/src/app/turnState.js +2 -2
- package/dist/src/app/turnState.js.map +1 -1
- package/dist/src/auth/authConfiguration.d.ts +61 -0
- package/dist/src/auth/authConfiguration.js +52 -3
- package/dist/src/auth/authConfiguration.js.map +1 -1
- package/dist/src/auth/jwt-middleware.js +2 -2
- package/dist/src/auth/jwt-middleware.js.map +1 -1
- package/dist/src/auth/msalConnectionManager.js +20 -0
- package/dist/src/auth/msalConnectionManager.js.map +1 -1
- package/dist/src/auth/msalTokenCredential.js +3 -0
- package/dist/src/auth/msalTokenCredential.js.map +1 -1
- package/dist/src/auth/msalTokenProvider.js +136 -110
- package/dist/src/auth/msalTokenProvider.js.map +1 -1
- package/dist/src/baseAdapter.js +2 -2
- package/dist/src/baseAdapter.js.map +1 -1
- package/dist/src/cloudAdapter.js +201 -154
- package/dist/src/cloudAdapter.js.map +1 -1
- package/dist/src/connector-client/connectorClient.js +176 -127
- package/dist/src/connector-client/connectorClient.js.map +1 -1
- package/dist/src/errorHelper.js +108 -0
- package/dist/src/errorHelper.js.map +1 -1
- package/dist/src/middlewareSet.js +2 -2
- package/dist/src/middlewareSet.js.map +1 -1
- package/dist/src/oauth/userTokenClient.js +78 -48
- package/dist/src/oauth/userTokenClient.js.map +1 -1
- package/dist/src/observability/index.d.ts +2 -0
- package/dist/src/observability/index.js +21 -0
- package/dist/src/observability/index.js.map +1 -0
- package/dist/src/observability/metrics.d.ts +21 -0
- package/dist/src/observability/metrics.js +87 -0
- package/dist/src/observability/metrics.js.map +1 -0
- package/dist/src/observability/traces.d.ts +234 -0
- package/dist/src/observability/traces.js +962 -0
- package/dist/src/observability/traces.js.map +1 -0
- package/dist/src/state/agentState.js +2 -2
- package/dist/src/state/agentState.js.map +1 -1
- package/dist/src/storage/fileStorage.js +38 -28
- package/dist/src/storage/fileStorage.js.map +1 -1
- package/dist/src/storage/memoryStorage.js +41 -30
- package/dist/src/storage/memoryStorage.js.map +1 -1
- package/dist/src/transcript/fileTranscriptLogger.js +2 -2
- package/dist/src/transcript/fileTranscriptLogger.js.map +1 -1
- package/dist/src/transcript/transcriptLoggerMiddleware.js +2 -2
- package/dist/src/transcript/transcriptLoggerMiddleware.js.map +1 -1
- package/dist/src/turnContext.js +48 -42
- package/dist/src/turnContext.js.map +1 -1
- package/package.json +10 -9
- package/src/activityHandler.ts +1 -1
- package/src/agent-client/agentClient.ts +53 -42
- package/src/agent-client/agentResponseHandler.ts +1 -1
- package/src/app/agentApplication.ts +212 -86
- package/src/app/agentApplicationBuilder.ts +26 -4
- package/src/app/agentApplicationOptions.ts +43 -0
- package/src/app/attachmentDownloader.ts +1 -1
- package/src/app/auth/authorization.ts +11 -8
- package/src/app/auth/authorizationManager.ts +297 -45
- package/src/app/auth/handlerStorage.ts +3 -1
- package/src/app/auth/handlers/agenticAuthorization.ts +68 -72
- package/src/app/auth/handlers/azureBotAuthorization.ts +260 -264
- package/src/app/auth/types.ts +102 -1
- package/src/app/auth/utils.ts +22 -0
- package/src/app/index.ts +1 -0
- package/src/app/proactive/conversation.ts +87 -0
- package/src/app/proactive/conversationBuilder.ts +139 -0
- package/src/app/proactive/conversationReferenceBuilder.ts +161 -0
- package/src/app/proactive/createConversationOptions.ts +35 -0
- package/src/app/proactive/createConversationOptionsBuilder.ts +181 -0
- package/src/app/proactive/index.ts +10 -0
- package/src/app/proactive/proactive.ts +524 -0
- package/src/app/proactive/proactiveOptions.ts +24 -0
- package/src/app/streaming/streamingResponse.ts +1 -1
- package/src/app/teamsAttachmentDownloader.ts +1 -1
- package/src/app/turnState.ts +1 -1
- package/src/auth/authConfiguration.ts +58 -1
- package/src/auth/jwt-middleware.ts +1 -1
- package/src/auth/msalConnectionManager.ts +22 -0
- package/src/auth/msalTokenCredential.ts +4 -0
- package/src/auth/msalTokenProvider.ts +138 -107
- package/src/baseAdapter.ts +1 -1
- package/src/cloudAdapter.ts +239 -184
- package/src/connector-client/connectorClient.ts +169 -126
- package/src/errorHelper.ts +124 -0
- package/src/middlewareSet.ts +1 -1
- package/src/oauth/userTokenClient.ts +70 -46
- package/src/observability/index.ts +5 -0
- package/src/observability/metrics.ts +103 -0
- package/src/observability/traces.ts +988 -0
- package/src/state/agentState.ts +1 -1
- package/src/storage/fileStorage.ts +36 -26
- package/src/storage/memoryStorage.ts +40 -29
- package/src/transcript/fileTranscriptLogger.ts +1 -1
- package/src/transcript/transcriptLoggerMiddleware.ts +1 -1
- package/src/turnContext.ts +47 -41
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "@microsoft/agents-hosting",
|
|
4
|
-
"version": "1.5.
|
|
4
|
+
"version": "1.5.1",
|
|
5
5
|
"homepage": "https://github.com/microsoft/Agents-for-js",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -19,13 +19,14 @@
|
|
|
19
19
|
"main": "dist/src/index.js",
|
|
20
20
|
"types": "dist/src/index.d.ts",
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@azure/core-auth": "
|
|
23
|
-
"@azure/msal-node": "
|
|
24
|
-
"@microsoft/agents-activity": "1.5.
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
22
|
+
"@azure/core-auth": "1.10.1",
|
|
23
|
+
"@azure/msal-node": "5.0.6",
|
|
24
|
+
"@microsoft/agents-activity": "1.5.1",
|
|
25
|
+
"@microsoft/agents-telemetry": "1.5.1",
|
|
26
|
+
"axios": "1.15.0",
|
|
27
|
+
"jsonwebtoken": "9.0.3",
|
|
28
|
+
"jwks-rsa": "4.0.1",
|
|
29
|
+
"object-path": "0.11.8",
|
|
29
30
|
"zod": "3.25.75"
|
|
30
31
|
},
|
|
31
32
|
"license": "MIT",
|
|
@@ -47,6 +48,6 @@
|
|
|
47
48
|
"node": ">=20.0.0"
|
|
48
49
|
},
|
|
49
50
|
"devDependencies": {
|
|
50
|
-
"@types/object-path": "
|
|
51
|
+
"@types/object-path": "0.11.4"
|
|
51
52
|
}
|
|
52
53
|
}
|
package/src/activityHandler.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
|
-
import { debug } from '@microsoft/agents-
|
|
5
|
+
import { debug } from '@microsoft/agents-telemetry'
|
|
6
6
|
import { TurnContext } from './turnContext'
|
|
7
7
|
import { Activity, ActivityTypes, Channels, ExceptionHelper } from '@microsoft/agents-activity'
|
|
8
8
|
import { Errors } from './errorHelper'
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { AuthConfiguration, MsalTokenProvider } from '../auth'
|
|
2
2
|
import { Activity, ConversationReference, RoleTypes } from '@microsoft/agents-activity'
|
|
3
3
|
import { v4 } from 'uuid'
|
|
4
|
-
import { debug } from '@microsoft/agents-
|
|
4
|
+
import { debug } from '@microsoft/agents-telemetry'
|
|
5
5
|
import { ConversationState } from '../state'
|
|
6
6
|
import { TurnContext } from '../turnContext'
|
|
7
|
+
import { trace } from '@microsoft/agents-telemetry'
|
|
8
|
+
import { AgentClientTraceDefinitions } from '../observability'
|
|
7
9
|
|
|
8
10
|
const logger = debug('agents:agent-client')
|
|
9
11
|
|
|
@@ -68,56 +70,65 @@ export class AgentClient {
|
|
|
68
70
|
* @throws Error if the request to the agent endpoint fails
|
|
69
71
|
*/
|
|
70
72
|
public async postActivity (activity: Activity, authConfig: AuthConfiguration, conversationState: ConversationState, context: TurnContext): Promise<string> {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
73
|
+
return trace(AgentClientTraceDefinitions.postActivity, async ({ record }) => {
|
|
74
|
+
record({
|
|
75
|
+
endpoint: this.agentClientConfig.endPoint,
|
|
76
|
+
clientId: this.agentClientConfig.clientId,
|
|
77
|
+
})
|
|
78
|
+
const activityCopy = activity.clone()
|
|
79
|
+
activityCopy.serviceUrl = this.agentClientConfig.serviceUrl
|
|
80
|
+
activityCopy.recipient = { ...activityCopy.recipient, role: RoleTypes.Skill }
|
|
81
|
+
activityCopy.relatesTo = {
|
|
82
|
+
serviceUrl: activity.serviceUrl,
|
|
83
|
+
activityId: activityCopy.id,
|
|
84
|
+
channelId: activityCopy.channelId!,
|
|
85
|
+
locale: activityCopy.locale,
|
|
86
|
+
conversation: {
|
|
87
|
+
id: activity.conversation!.id,
|
|
88
|
+
...activityCopy.conversation
|
|
89
|
+
}
|
|
82
90
|
}
|
|
83
|
-
|
|
84
|
-
activityCopy.conversation!.id = v4()
|
|
91
|
+
activityCopy.conversation!.id = v4()
|
|
85
92
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
93
|
+
const conversationDataAccessor = conversationState.createProperty<ConversationData>(activityCopy.conversation!.id)
|
|
94
|
+
const convRef = await conversationDataAccessor.set(context,
|
|
95
|
+
{ conversationReference: activity.getConversationReference(), nameRequested: false },
|
|
96
|
+
{ channelId: activityCopy.channelId!, conversationId: activityCopy.conversation!.id }
|
|
97
|
+
)
|
|
91
98
|
|
|
92
|
-
|
|
93
|
-
|
|
99
|
+
const stateChanges = JSON.stringify(convRef)
|
|
100
|
+
logger.debug('stateChanges: ', stateChanges)
|
|
94
101
|
|
|
95
|
-
|
|
96
|
-
|
|
102
|
+
const authProvider = new MsalTokenProvider(authConfig)
|
|
103
|
+
const token = await authProvider.getAccessToken(this.agentClientConfig.clientId)
|
|
97
104
|
|
|
98
|
-
|
|
105
|
+
logger.debug('agent request: ', activityCopy)
|
|
99
106
|
|
|
100
|
-
|
|
107
|
+
let authHeader = '' // Allow anonymous auth.
|
|
101
108
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
109
|
+
if (token.trim().length > 0) {
|
|
110
|
+
authHeader = `Bearer ${token}`
|
|
111
|
+
}
|
|
105
112
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
113
|
+
await conversationState.saveChanges(context, false, { channelId: activityCopy.channelId!, conversationId: activityCopy.conversation!.id })
|
|
114
|
+
const response = await fetch(this.agentClientConfig.endPoint, {
|
|
115
|
+
method: 'POST',
|
|
116
|
+
headers: {
|
|
117
|
+
'Content-Type': 'application/json',
|
|
118
|
+
Authorization: authHeader,
|
|
119
|
+
'x-ms-conversation-id': activityCopy.conversation!.id
|
|
120
|
+
},
|
|
121
|
+
body: JSON.stringify(activityCopy)
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
record({ httpStatusCode: response.status.toString() })
|
|
125
|
+
|
|
126
|
+
if (!response.ok) {
|
|
127
|
+
await conversationDataAccessor.delete(context, { channelId: activityCopy.channelId!, conversationId: activityCopy.conversation!.id })
|
|
128
|
+
throw new Error(`Failed to post activity to agent: ${response.statusText}`)
|
|
129
|
+
}
|
|
130
|
+
return response.statusText
|
|
115
131
|
})
|
|
116
|
-
if (!response.ok) {
|
|
117
|
-
await conversationDataAccessor.delete(context, { channelId: activityCopy.channelId!, conversationId: activityCopy.conversation!.id })
|
|
118
|
-
throw new Error(`Failed to post activity to agent: ${response.statusText}`)
|
|
119
|
-
}
|
|
120
|
-
return response.statusText
|
|
121
132
|
}
|
|
122
133
|
|
|
123
134
|
/**
|
|
@@ -5,7 +5,7 @@ import { Request, Response, Application } from 'express'
|
|
|
5
5
|
import { TurnContext } from '../turnContext'
|
|
6
6
|
import { v4 } from 'uuid'
|
|
7
7
|
import { normalizeIncomingActivity } from '../activityWireCompat'
|
|
8
|
-
import { debug } from '@microsoft/agents-
|
|
8
|
+
import { debug } from '@microsoft/agents-telemetry'
|
|
9
9
|
import { ConversationState } from '../state'
|
|
10
10
|
|
|
11
11
|
const logger = debug('agents:agent-client')
|
|
@@ -3,12 +3,11 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { Activity, ActivityTypes, ConversationReference } from '@microsoft/agents-activity'
|
|
6
|
+
import { Activity, ActivityTypes, ConversationReference, ExceptionHelper } from '@microsoft/agents-activity'
|
|
7
7
|
import { ResourceResponse } from '../connector-client'
|
|
8
|
-
import { debug } from '@microsoft/agents-activity/logger'
|
|
9
8
|
import { TurnContext } from '../turnContext'
|
|
10
9
|
import { AdaptiveCardsActions } from './adaptiveCards'
|
|
11
|
-
import { AgentApplicationOptions } from './agentApplicationOptions'
|
|
10
|
+
import { AgentApplicationOptions, TypingTimingOptions } from './agentApplicationOptions'
|
|
12
11
|
import { ConversationUpdateEvents } from './conversationUpdateEvents'
|
|
13
12
|
import { AgentExtension } from './extensions'
|
|
14
13
|
import { RouteHandler } from './routeHandler'
|
|
@@ -20,11 +19,30 @@ import { RouteList } from './routeList'
|
|
|
20
19
|
import { TranscriptLoggerMiddleware } from '../transcript'
|
|
21
20
|
import { CloudAdapter } from '../cloudAdapter'
|
|
22
21
|
import { Authorization, UserAuthorization, AuthorizationManager } from './auth'
|
|
22
|
+
import { Proactive } from './proactive'
|
|
23
23
|
import { JwtPayload } from 'jsonwebtoken'
|
|
24
|
+
import { trace, debug } from '@microsoft/agents-telemetry'
|
|
25
|
+
import { AgentApplicationTraceDefinitions } from '../observability'
|
|
26
|
+
import { Errors } from '../errorHelper'
|
|
24
27
|
|
|
25
28
|
const logger = debug('agents:app')
|
|
26
29
|
|
|
27
|
-
|
|
30
|
+
// Resend typing every 4 seconds to stay ahead of the ~5 second timeout seen in
|
|
31
|
+
// Web Chat and Microsoft 365. Teams may keep typing indicators visible longer.
|
|
32
|
+
const DEFAULT_TYPING_INITIAL_DELAY = 0
|
|
33
|
+
const DEFAULT_TYPING_INTERVAL = 4000
|
|
34
|
+
const TYPING_TIMER_STATE_KEY = Symbol('typingTimerState')
|
|
35
|
+
|
|
36
|
+
type TypingTimerState = {
|
|
37
|
+
timer?: NodeJS.Timeout
|
|
38
|
+
lastSend: Promise<unknown>
|
|
39
|
+
stop: () => void
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
type StreamInfoEntity = {
|
|
43
|
+
type?: string
|
|
44
|
+
streamType?: string
|
|
45
|
+
}
|
|
28
46
|
|
|
29
47
|
/**
|
|
30
48
|
* Event handler function type for application events.
|
|
@@ -74,9 +92,9 @@ export class AgentApplication<TState extends TurnState> {
|
|
|
74
92
|
protected readonly _beforeTurn: ApplicationEventHandler<TState>[] = []
|
|
75
93
|
protected readonly _afterTurn: ApplicationEventHandler<TState>[] = []
|
|
76
94
|
private readonly _adapter?: CloudAdapter
|
|
77
|
-
private readonly _authorizationManager
|
|
78
|
-
private readonly _authorization
|
|
79
|
-
private
|
|
95
|
+
private readonly _authorizationManager: AuthorizationManager
|
|
96
|
+
private readonly _authorization: Authorization
|
|
97
|
+
private readonly _proactive?: Proactive<TState>
|
|
80
98
|
protected readonly _extensions: AgentExtension<TState>[] = []
|
|
81
99
|
private readonly _adaptiveCards: AdaptiveCardsActions<TState>
|
|
82
100
|
|
|
@@ -112,6 +130,7 @@ export class AgentApplication<TState extends TurnState> {
|
|
|
112
130
|
...options,
|
|
113
131
|
turnStateFactory: options?.turnStateFactory || (() => new TurnState() as TState),
|
|
114
132
|
startTypingTimer: options?.startTypingTimer !== undefined ? options.startTypingTimer : false,
|
|
133
|
+
typing: options?.typing || undefined,
|
|
115
134
|
longRunningMessages: options?.longRunningMessages !== undefined ? options.longRunningMessages : false,
|
|
116
135
|
removeRecipientMention: options?.removeRecipientMention !== undefined ? options.removeRecipientMention : true,
|
|
117
136
|
transcriptLogger: options?.transcriptLogger || undefined,
|
|
@@ -125,9 +144,12 @@ export class AgentApplication<TState extends TurnState> {
|
|
|
125
144
|
this._adapter = new CloudAdapter()
|
|
126
145
|
}
|
|
127
146
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
147
|
+
// Create Proactive whenever proactive options are explicitly configured or a storage
|
|
148
|
+
// backend is available — no explicit `proactive` option is required.
|
|
149
|
+
if (this._options.proactive !== undefined || this._options.storage !== undefined) {
|
|
150
|
+
const proactiveOpts = this._options.proactive ?? {}
|
|
151
|
+
const proactiveStorage = proactiveOpts.storage ?? this._options.storage
|
|
152
|
+
this._proactive = new Proactive<TState>(this, { ...proactiveOpts, storage: proactiveStorage })
|
|
131
153
|
}
|
|
132
154
|
|
|
133
155
|
if (this._options.longRunningMessages && !this._adapter && !this._options.agentAppId) {
|
|
@@ -141,7 +163,11 @@ export class AgentApplication<TState extends TurnState> {
|
|
|
141
163
|
this._adapter?.use(new TranscriptLoggerMiddleware(this._options.transcriptLogger))
|
|
142
164
|
}
|
|
143
165
|
}
|
|
166
|
+
|
|
144
167
|
logger.debug('AgentApplication created with options:', this._options)
|
|
168
|
+
|
|
169
|
+
this._authorizationManager = new AuthorizationManager(this, this._adapter.connectionManager)
|
|
170
|
+
this._authorization = new UserAuthorization(this._authorizationManager)
|
|
145
171
|
}
|
|
146
172
|
|
|
147
173
|
/**
|
|
@@ -151,12 +177,33 @@ export class AgentApplication<TState extends TurnState> {
|
|
|
151
177
|
* @throws Error if no authentication options were configured.
|
|
152
178
|
*/
|
|
153
179
|
public get authorization (): Authorization {
|
|
154
|
-
if (
|
|
180
|
+
if (this._authorizationManager.handlers.length === 0) {
|
|
155
181
|
throw new Error('The Application.authorization property is unavailable because no authorization options were configured.')
|
|
156
182
|
}
|
|
157
183
|
return this._authorization
|
|
158
184
|
}
|
|
159
185
|
|
|
186
|
+
/**
|
|
187
|
+
* Gets the proactive messaging subsystem.
|
|
188
|
+
*
|
|
189
|
+
* @throws Error if no storage backend was configured (neither `options.storage` nor
|
|
190
|
+
* `options.proactive.storage`).
|
|
191
|
+
*/
|
|
192
|
+
public get proactive (): Proactive<TState> {
|
|
193
|
+
if (!this._proactive) {
|
|
194
|
+
throw ExceptionHelper.generateException(Error, Errors.ProactivePropertyUnavailable)
|
|
195
|
+
}
|
|
196
|
+
return this._proactive
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Returns `true` if user authorization was configured, without throwing.
|
|
201
|
+
* Used internally by the Proactive subsystem to check whether token acquisition is available.
|
|
202
|
+
*/
|
|
203
|
+
public get hasUserAuthorization (): boolean {
|
|
204
|
+
return this._authorization !== undefined
|
|
205
|
+
}
|
|
206
|
+
|
|
160
207
|
/**
|
|
161
208
|
* Gets the options used to configure the application.
|
|
162
209
|
*
|
|
@@ -605,6 +652,9 @@ export class AgentApplication<TState extends TurnState> {
|
|
|
605
652
|
const { authorized, context } = await this.handleAuthorization(turnContext)
|
|
606
653
|
|
|
607
654
|
if (!authorized) {
|
|
655
|
+
const managed = trace(AgentApplicationTraceDefinitions.run)
|
|
656
|
+
managed.record({ authorized, activity: context.activity })
|
|
657
|
+
managed.end()
|
|
608
658
|
// We don't log a message here because it is handled by the authorization manager and could cause confusion during mid sign-in operations.
|
|
609
659
|
return false
|
|
610
660
|
}
|
|
@@ -644,54 +694,75 @@ export class AgentApplication<TState extends TurnState> {
|
|
|
644
694
|
* Executes the turn processing logic for the given context, including routing and handler execution.
|
|
645
695
|
*/
|
|
646
696
|
private async runTurn (context: TurnContext): Promise<boolean> {
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
this.startTypingTimer(context)
|
|
650
|
-
}
|
|
697
|
+
return trace(AgentApplicationTraceDefinitions.run, async ({ record }) => {
|
|
698
|
+
record({ authorized: true, activity: context.activity })
|
|
651
699
|
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
700
|
+
try {
|
|
701
|
+
if (this._options.startTypingTimer) {
|
|
702
|
+
this.startTypingTimer(context)
|
|
703
|
+
}
|
|
655
704
|
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
705
|
+
if (this._options.removeRecipientMention && context.activity.type === ActivityTypes.Message) {
|
|
706
|
+
context.activity.removeRecipientMention()
|
|
707
|
+
}
|
|
659
708
|
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
709
|
+
if (this._options.normalizeMentions && context.activity.type === ActivityTypes.Message) {
|
|
710
|
+
context.activity.normalizeMentions()
|
|
711
|
+
}
|
|
663
712
|
|
|
664
|
-
|
|
713
|
+
const { storage, turnStateFactory } = this._options
|
|
714
|
+
const state = turnStateFactory()
|
|
715
|
+
await state.load(context, storage)
|
|
665
716
|
|
|
666
|
-
|
|
667
|
-
logger.debug('No matching route found for activity:', context.activity)
|
|
668
|
-
return false
|
|
669
|
-
}
|
|
717
|
+
const route = await this.getRoute(context)
|
|
670
718
|
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
719
|
+
record({ routeMatched: route !== undefined })
|
|
720
|
+
|
|
721
|
+
if (!route) {
|
|
722
|
+
logger.debug('No matching route found for activity:', context.activity)
|
|
723
|
+
return false
|
|
674
724
|
}
|
|
675
|
-
}
|
|
676
725
|
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
726
|
+
const fileDownloaders = this._options.fileDownloaders
|
|
727
|
+
if (Array.isArray(fileDownloaders) && fileDownloaders.length > 0) {
|
|
728
|
+
await trace(AgentApplicationTraceDefinitions.downloadFiles, async ({ record }) => {
|
|
729
|
+
record({ attachmentsCount: context.activity.attachments?.length })
|
|
730
|
+
for (let i = 0; i < fileDownloaders.length; i++) {
|
|
731
|
+
await fileDownloaders[i].downloadAndStoreFiles(context, state)
|
|
732
|
+
}
|
|
733
|
+
})
|
|
734
|
+
}
|
|
681
735
|
|
|
682
|
-
|
|
736
|
+
if (this._beforeTurn.length > 0) {
|
|
737
|
+
await trace(AgentApplicationTraceDefinitions.beforeTurn, async () => {
|
|
738
|
+
if (!(await this.callEventHandlers(context, state, this._beforeTurn))) {
|
|
739
|
+
await state.save(context, storage)
|
|
740
|
+
return false
|
|
741
|
+
}
|
|
742
|
+
})
|
|
743
|
+
}
|
|
683
744
|
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
745
|
+
await trace(AgentApplicationTraceDefinitions.routeHandler, async ({ record }) => {
|
|
746
|
+
record({ isInvoke: route.isInvokeRoute, isAgentic: route.isAgenticRoute })
|
|
747
|
+
await route.handler(context, state)
|
|
748
|
+
})
|
|
687
749
|
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
750
|
+
if (this._afterTurn.length > 0) {
|
|
751
|
+
await trace(AgentApplicationTraceDefinitions.afterTurn, async () => {
|
|
752
|
+
if (await this.callEventHandlers(context, state, this._afterTurn)) {
|
|
753
|
+
await state.save(context, storage)
|
|
754
|
+
}
|
|
755
|
+
})
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
return true
|
|
759
|
+
} catch (err: any) {
|
|
760
|
+
logger.error(err)
|
|
761
|
+
throw err
|
|
762
|
+
} finally {
|
|
763
|
+
this.stopTypingTimer(context)
|
|
764
|
+
}
|
|
765
|
+
})
|
|
695
766
|
}
|
|
696
767
|
|
|
697
768
|
/**
|
|
@@ -766,43 +837,84 @@ export class AgentApplication<TState extends TurnState> {
|
|
|
766
837
|
*
|
|
767
838
|
*/
|
|
768
839
|
public startTypingTimer (context: TurnContext): void {
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
840
|
+
const turnState = context.turnState
|
|
841
|
+
const typingOptions = this.getTypingTimingOptions(context)
|
|
842
|
+
// Timer state is stored on the current turn so concurrent turns stay isolated.
|
|
843
|
+
const currentState = () => turnState.get<TypingTimerState>(TYPING_TIMER_STATE_KEY)
|
|
844
|
+
|
|
845
|
+
if (context.activity.type !== ActivityTypes.Message || currentState()) {
|
|
846
|
+
return
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
const state: TypingTimerState = {
|
|
850
|
+
lastSend: Promise.resolve(),
|
|
851
|
+
stop: () => {
|
|
852
|
+
if (state.timer) {
|
|
853
|
+
clearTimeout(state.timer)
|
|
854
|
+
state.timer = undefined
|
|
781
855
|
}
|
|
782
856
|
|
|
783
|
-
|
|
784
|
-
}
|
|
857
|
+
turnState.delete(TYPING_TIMER_STATE_KEY)
|
|
858
|
+
}
|
|
859
|
+
}
|
|
785
860
|
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
861
|
+
turnState.set(TYPING_TIMER_STATE_KEY, state)
|
|
862
|
+
|
|
863
|
+
context.onSendActivities(async (context, activities, next) => {
|
|
864
|
+
// Any real response or stream start ends the typing loop for this turn.
|
|
865
|
+
if (activities.some(activity => activity.type === ActivityTypes.Message || this.getStreamType(activity) !== undefined)) {
|
|
866
|
+
state.stop()
|
|
867
|
+
// Wait for any in-flight typing send to finish before sending the real response.
|
|
868
|
+
await state.lastSend.catch((err: any) => {
|
|
792
869
|
logger.error(err)
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
lastSend = Promise.resolve()
|
|
796
|
-
}
|
|
870
|
+
})
|
|
871
|
+
}
|
|
797
872
|
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
873
|
+
return next()
|
|
874
|
+
})
|
|
875
|
+
|
|
876
|
+
const onTimeout = async () => {
|
|
877
|
+
try {
|
|
878
|
+
state.lastSend = this.sendTypingActivity(context)
|
|
879
|
+
await state.lastSend
|
|
880
|
+
} catch (err: any) {
|
|
881
|
+
logger.error(err)
|
|
882
|
+
state.lastSend = Promise.resolve()
|
|
883
|
+
state.stop()
|
|
884
|
+
return
|
|
801
885
|
}
|
|
802
|
-
|
|
886
|
+
|
|
887
|
+
// Only reschedule if this turn still owns the active timer state.
|
|
888
|
+
if (currentState() === state) {
|
|
889
|
+
state.timer = setTimeout(onTimeout, typingOptions.intervalMs)
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
state.timer = setTimeout(onTimeout, typingOptions.initialDelayMs)
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
private getTypingTimingOptions (context: TurnContext): Required<TypingTimingOptions> {
|
|
897
|
+
const channelId = context.activity.channelId || context.activity.channelIdChannel || ''
|
|
898
|
+
const channelOptions = channelId ? this._options.typing?.channelStrategies?.[channelId] : undefined
|
|
899
|
+
|
|
900
|
+
return {
|
|
901
|
+
initialDelayMs: channelOptions?.initialDelayMs ?? this._options.typing?.initialDelayMs ?? DEFAULT_TYPING_INITIAL_DELAY,
|
|
902
|
+
intervalMs: channelOptions?.intervalMs ?? this._options.typing?.intervalMs ?? DEFAULT_TYPING_INTERVAL
|
|
803
903
|
}
|
|
804
904
|
}
|
|
805
905
|
|
|
906
|
+
private getStreamType (activity: Activity): string | undefined {
|
|
907
|
+
const streamingEntity = activity.entities?.find((entity) => (entity as StreamInfoEntity).type === 'streaminfo') as StreamInfoEntity | undefined
|
|
908
|
+
return streamingEntity?.streamType ?? activity.channelData?.streamType
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
private async sendTypingActivity (context: TurnContext): Promise<ResourceResponse[] | undefined> {
|
|
912
|
+
const conversationReference = context.activity.getConversationReference()
|
|
913
|
+
const typingActivity = Activity.fromObject({ type: ActivityTypes.Typing }).applyConversationReference(conversationReference)
|
|
914
|
+
|
|
915
|
+
return await context.adapter.sendActivities(context, [typingActivity])
|
|
916
|
+
}
|
|
917
|
+
|
|
806
918
|
/**
|
|
807
919
|
* Registers an extension with the application.
|
|
808
920
|
*
|
|
@@ -838,23 +950,37 @@ export class AgentApplication<TState extends TurnState> {
|
|
|
838
950
|
* @returns void
|
|
839
951
|
*
|
|
840
952
|
* @remarks
|
|
841
|
-
*
|
|
842
|
-
*
|
|
843
|
-
*
|
|
953
|
+
* Calling this overload without a context is deprecated. It only logs a warning and does not stop any timer.
|
|
954
|
+
*
|
|
955
|
+
* @deprecated Pass the current TurnContext to stop only that turn's typing timer.
|
|
956
|
+
*/
|
|
957
|
+
public stopTypingTimer (): void
|
|
958
|
+
|
|
959
|
+
/**
|
|
960
|
+
* Stops the typing indicator timer for the provided turn context.
|
|
961
|
+
*
|
|
962
|
+
* @param context - The turn context whose typing timer should be stopped.
|
|
963
|
+
* @returns void
|
|
964
|
+
*
|
|
965
|
+
* @remarks
|
|
966
|
+
* This method clears the typing indicator timer for the current turn to prevent further typing indicators
|
|
967
|
+
* from being sent. It's typically called automatically when a message is sent, but can also be called manually.
|
|
844
968
|
*
|
|
845
969
|
* @example
|
|
846
970
|
* ```typescript
|
|
847
|
-
* app.startTypingTimer(turnContext)
|
|
971
|
+
* app.startTypingTimer(turnContext)
|
|
848
972
|
* // Do some processing...
|
|
849
|
-
* app.stopTypingTimer()
|
|
973
|
+
* app.stopTypingTimer(turnContext)
|
|
850
974
|
* ```
|
|
851
|
-
*
|
|
852
975
|
*/
|
|
853
|
-
public stopTypingTimer (): void
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
976
|
+
public stopTypingTimer (context: TurnContext): void
|
|
977
|
+
public stopTypingTimer (context?: TurnContext): void {
|
|
978
|
+
if (!context) {
|
|
979
|
+
logger.warn('Application.stopTypingTimer() without a context is deprecated. Pass the current TurnContext instead.')
|
|
980
|
+
return
|
|
857
981
|
}
|
|
982
|
+
|
|
983
|
+
context.turnState.get<TypingTimerState>(TYPING_TIMER_STATE_KEY)?.stop()
|
|
858
984
|
}
|
|
859
985
|
|
|
860
986
|
/**
|
|
@@ -7,7 +7,9 @@ import { Storage } from '../storage'
|
|
|
7
7
|
import { AgentApplication } from './agentApplication'
|
|
8
8
|
import { AgentApplicationOptions } from './agentApplicationOptions'
|
|
9
9
|
import { AuthorizationOptions } from './auth/types'
|
|
10
|
+
import { ProactiveOptions } from './proactive'
|
|
10
11
|
import { TurnState } from './turnState'
|
|
12
|
+
import { TypingOptions } from './agentApplicationOptions'
|
|
11
13
|
|
|
12
14
|
/**
|
|
13
15
|
* Builder class for creating and configuring AgentApplication instances.
|
|
@@ -49,10 +51,20 @@ export class AgentApplicationBuilder<TState extends TurnState = TurnState> {
|
|
|
49
51
|
* @param startTypingTimer Whether to show typing indicators
|
|
50
52
|
* @returns This builder instance for chaining
|
|
51
53
|
*/
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
public setStartTypingTimer (startTypingTimer: boolean): this {
|
|
55
|
+
this._options.startTypingTimer = startTypingTimer
|
|
56
|
+
return this
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Configures timing behavior for automatic typing indicators.
|
|
61
|
+
* @param typing Typing timer options
|
|
62
|
+
* @returns This builder instance for chaining
|
|
63
|
+
*/
|
|
64
|
+
public withTyping (typing: TypingOptions): this {
|
|
65
|
+
this._options.typing = typing
|
|
66
|
+
return this
|
|
67
|
+
}
|
|
56
68
|
|
|
57
69
|
/**
|
|
58
70
|
* Sets authentication options for the AgentApplication.
|
|
@@ -64,6 +76,16 @@ export class AgentApplicationBuilder<TState extends TurnState = TurnState> {
|
|
|
64
76
|
return this
|
|
65
77
|
}
|
|
66
78
|
|
|
79
|
+
/**
|
|
80
|
+
* Configures the proactive messaging subsystem.
|
|
81
|
+
* @param options Proactive options including optional storage backend
|
|
82
|
+
* @returns This builder instance for chaining
|
|
83
|
+
*/
|
|
84
|
+
public withProactive (options: ProactiveOptions): this {
|
|
85
|
+
this._options.proactive = options
|
|
86
|
+
return this
|
|
87
|
+
}
|
|
88
|
+
|
|
67
89
|
/**
|
|
68
90
|
* Builds and returns a new AgentApplication instance configured with the provided options.
|
|
69
91
|
* @returns A new AgentApplication instance
|