@microsoft/agents-hosting 1.7.1 → 1.8.0-beta.34.g5d543f96c0
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/README.md +64 -2
- package/dist/package.json +3 -3
- package/dist/src/agent-client/agentClient.d.ts +18 -6
- package/dist/src/agent-client/agentClient.js +34 -18
- package/dist/src/agent-client/agentClient.js.map +1 -1
- package/dist/src/agent-client/agentResponseHandler.d.ts +14 -9
- package/dist/src/agent-client/agentResponseHandler.js +10 -5
- package/dist/src/agent-client/agentResponseHandler.js.map +1 -1
- package/dist/src/agent-client/createAgentResponseHandler.d.ts +7 -2
- package/dist/src/agent-client/createAgentResponseHandler.js +55 -8
- package/dist/src/agent-client/createAgentResponseHandler.js.map +1 -1
- package/dist/src/app/adaptiveCards/activityValueParsers.d.ts +1 -4
- package/dist/src/app/adaptiveCards/activityValueParsers.js +2 -6
- package/dist/src/app/adaptiveCards/activityValueParsers.js.map +1 -1
- package/dist/src/app/adaptiveCards/adaptiveCardsActions.d.ts +18 -0
- package/dist/src/app/adaptiveCards/adaptiveCardsActions.js +53 -23
- package/dist/src/app/adaptiveCards/adaptiveCardsActions.js.map +1 -1
- package/dist/src/app/agentApplication.d.ts +7 -0
- package/dist/src/app/agentApplication.js +130 -70
- package/dist/src/app/agentApplication.js.map +1 -1
- package/dist/src/app/attachmentDownloader.d.ts +4 -1
- package/dist/src/app/attachmentDownloader.js +27 -5
- package/dist/src/app/attachmentDownloader.js.map +1 -1
- package/dist/src/app/auth/handlers/agenticAuthorization.d.ts +1 -1
- package/dist/src/app/auth/handlers/azureBotAuthorization.d.ts +1 -1
- package/dist/src/app/auth/types.d.ts +3 -4
- package/dist/src/app/index.d.ts +2 -0
- package/dist/src/app/index.js.map +1 -1
- package/dist/src/app/streaming/streamingResponse.js +2 -9
- package/dist/src/app/streaming/streamingResponse.js.map +1 -1
- package/dist/src/app/teamsAttachmentDownloader.d.ts +3 -1
- package/dist/src/app/teamsAttachmentDownloader.js +12 -1
- package/dist/src/app/teamsAttachmentDownloader.js.map +1 -1
- package/dist/src/auth/authConfiguration.js +12 -0
- package/dist/src/auth/authConfiguration.js.map +1 -1
- package/dist/src/auth/index.d.ts +3 -1
- package/dist/src/auth/index.js +3 -1
- package/dist/src/auth/index.js.map +1 -1
- package/dist/src/auth/jwt-middleware.d.ts +6 -0
- package/dist/src/auth/jwt-middleware.js +263 -13
- package/dist/src/auth/jwt-middleware.js.map +1 -1
- package/dist/src/auth/msal/msalConnectionSettings.d.ts +8 -0
- package/dist/src/auth/msal/msalHttpRetryHandlerHelper.d.ts +34 -0
- package/dist/src/auth/msal/msalHttpRetryHandlerHelper.js +126 -0
- package/dist/src/auth/msal/msalHttpRetryHandlerHelper.js.map +1 -0
- package/dist/src/auth/msal/msalTokenProvider.d.ts +1 -0
- package/dist/src/auth/msal/msalTokenProvider.js +23 -14
- package/dist/src/auth/msal/msalTokenProvider.js.map +1 -1
- package/dist/src/auth/settings.d.ts +18 -0
- package/dist/src/auth/settings.js +8 -2
- package/dist/src/auth/settings.js.map +1 -1
- package/dist/src/cloudAdapter.d.ts +29 -2
- package/dist/src/cloudAdapter.js +44 -2
- package/dist/src/cloudAdapter.js.map +1 -1
- package/dist/src/errorHelper.js +137 -101
- package/dist/src/errorHelper.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/invoke/adaptiveCardInvokeValue.d.ts +5 -0
- package/dist/src/oauth/userTokenClient.d.ts +0 -1
- package/dist/src/oauth/userTokenClient.js +0 -1
- package/dist/src/oauth/userTokenClient.js.map +1 -1
- package/dist/src/observability/traces.js +3 -2
- package/dist/src/observability/traces.js.map +1 -1
- package/dist/src/outboundHostValidator.d.ts +47 -0
- package/dist/src/outboundHostValidator.js +149 -0
- package/dist/src/outboundHostValidator.js.map +1 -0
- package/dist/src/state/agentState.d.ts +5 -0
- package/dist/src/state/agentState.js +16 -3
- package/dist/src/state/agentState.js.map +1 -1
- package/package.json +3 -3
- package/src/agent-client/agentClient.ts +46 -23
- package/src/agent-client/agentResponseHandler.ts +14 -9
- package/src/agent-client/createAgentResponseHandler.ts +70 -9
- package/src/app/adaptiveCards/activityValueParsers.ts +3 -10
- package/src/app/adaptiveCards/adaptiveCardsActions.ts +52 -24
- package/src/app/agentApplication.ts +143 -67
- package/src/app/attachmentDownloader.ts +25 -5
- package/src/app/auth/handlers/agenticAuthorization.ts +1 -1
- package/src/app/auth/handlers/azureBotAuthorization.ts +1 -1
- package/src/app/auth/types.ts +3 -3
- package/src/app/index.ts +8 -0
- package/src/app/streaming/streamingResponse.ts +2 -8
- package/src/app/teamsAttachmentDownloader.ts +11 -1
- package/src/auth/authConfiguration.ts +12 -0
- package/src/auth/index.ts +3 -1
- package/src/auth/jwt-middleware.ts +298 -14
- package/src/auth/msal/msalConnectionSettings.ts +10 -0
- package/src/auth/msal/msalHttpRetryHandlerHelper.ts +161 -0
- package/src/auth/msal/msalTokenProvider.ts +32 -14
- package/src/auth/settings.ts +18 -3
- package/src/cloudAdapter.ts +56 -3
- package/src/errorHelper.ts +155 -114
- package/src/index.ts +1 -0
- package/src/invoke/adaptiveCardInvokeValue.ts +5 -0
- package/src/oauth/userTokenClient.ts +0 -1
- package/src/observability/traces.ts +2 -1
- package/src/outboundHostValidator.ts +172 -0
- package/src/state/agentState.ts +24 -6
package/src/errorHelper.ts
CHANGED
|
@@ -156,6 +156,14 @@ export const Errors: { [key: string]: AgentErrorDefinition } = {
|
|
|
156
156
|
description: 'serviceUrl must be a non-empty string'
|
|
157
157
|
},
|
|
158
158
|
|
|
159
|
+
/**
|
|
160
|
+
* Error thrown when the outbound service URL is denied by the configured host policy.
|
|
161
|
+
*/
|
|
162
|
+
OutboundServiceUrlNotAllowed: {
|
|
163
|
+
code: -120175,
|
|
164
|
+
description: 'serviceUrl host is not in the configured allowed hosts.'
|
|
165
|
+
},
|
|
166
|
+
|
|
159
167
|
/**
|
|
160
168
|
* Error thrown when conversationParameters must be defined.
|
|
161
169
|
*/
|
|
@@ -416,6 +424,22 @@ export const Errors: { [key: string]: AgentErrorDefinition } = {
|
|
|
416
424
|
description: 'Identity is required to get the token provider.'
|
|
417
425
|
},
|
|
418
426
|
|
|
427
|
+
/**
|
|
428
|
+
* Error thrown when an MSAL HTTP request cannot be completed.
|
|
429
|
+
*/
|
|
430
|
+
MsalHttpRequestFailed: {
|
|
431
|
+
code: -120391,
|
|
432
|
+
description: 'MSAL HTTP request failed: {message}'
|
|
433
|
+
},
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Error thrown when an MSAL HTTP response body cannot be parsed as JSON.
|
|
437
|
+
*/
|
|
438
|
+
MsalResponseUnparsable: {
|
|
439
|
+
code: -120392,
|
|
440
|
+
description: 'MSAL returned an unparsable response body'
|
|
441
|
+
},
|
|
442
|
+
|
|
419
443
|
FailedToAcquireTokenUsingCertificate: {
|
|
420
444
|
code: -120395,
|
|
421
445
|
description: 'Failed to acquire token using certificate'
|
|
@@ -673,108 +697,6 @@ export const Errors: { [key: string]: AgentErrorDefinition } = {
|
|
|
673
697
|
description: 'Token request timed out after {timeoutMs} ms'
|
|
674
698
|
},
|
|
675
699
|
|
|
676
|
-
// ============================================================================
|
|
677
|
-
// Entra Agent ID Sidecar Authentication Errors (-120800 to -120849)
|
|
678
|
-
// ============================================================================
|
|
679
|
-
|
|
680
|
-
/**
|
|
681
|
-
* Error thrown when the Entra sidecar auth provider is used without connection settings.
|
|
682
|
-
*/
|
|
683
|
-
SidecarConnectionSettingsRequired: {
|
|
684
|
-
code: -120800,
|
|
685
|
-
description: 'Connection settings must be provided when using the Entra sidecar auth provider'
|
|
686
|
-
},
|
|
687
|
-
|
|
688
|
-
/**
|
|
689
|
-
* Error thrown when the resolved sidecar base URL is not a valid absolute URL.
|
|
690
|
-
*/
|
|
691
|
-
SidecarBaseUrlInvalid: {
|
|
692
|
-
code: -120801,
|
|
693
|
-
description: 'The resolved sidecar base URL `{url}` is not a valid absolute URL'
|
|
694
|
-
},
|
|
695
|
-
|
|
696
|
-
/**
|
|
697
|
-
* Error thrown when the resolved sidecar base URL does not use the http or https scheme.
|
|
698
|
-
*/
|
|
699
|
-
SidecarBaseUrlInsecureScheme: {
|
|
700
|
-
code: -120802,
|
|
701
|
-
description: 'The resolved sidecar base URL `{url}` must use the http or https scheme'
|
|
702
|
-
},
|
|
703
|
-
|
|
704
|
-
/**
|
|
705
|
-
* Error thrown when the resolved sidecar base URL contains userinfo (credentials).
|
|
706
|
-
*/
|
|
707
|
-
SidecarBaseUrlUserInfo: {
|
|
708
|
-
code: -120803,
|
|
709
|
-
description: 'The resolved sidecar base URL `{url}` must not contain userinfo (credentials)'
|
|
710
|
-
},
|
|
711
|
-
|
|
712
|
-
/**
|
|
713
|
-
* Error thrown when the resolved sidecar base URL points to a non-loopback, non-private address.
|
|
714
|
-
*/
|
|
715
|
-
SidecarBaseUrlNotLocal: {
|
|
716
|
-
code: -120804,
|
|
717
|
-
description: 'The resolved sidecar base URL `{url}` points to a non-loopback, non-private address. The Entra Agent ID sidecar (agent container) must be reachable only from within the agent\'s network boundary. Set `bypassLocalNetworkRestriction` to true in the connection configuration to override this safety check (UNSAFE: only for carefully validated private-network configurations)'
|
|
718
|
-
},
|
|
719
|
-
|
|
720
|
-
/**
|
|
721
|
-
* Error thrown when both AgentUsername and AgentUserId are supplied on a sidecar request (mutually exclusive).
|
|
722
|
-
*/
|
|
723
|
-
SidecarUserIdentityMutuallyExclusive: {
|
|
724
|
-
code: -120805,
|
|
725
|
-
description: 'AgentUsername and AgentUserId are mutually exclusive; set only one on the sidecar request options'
|
|
726
|
-
},
|
|
727
|
-
|
|
728
|
-
/**
|
|
729
|
-
* Error thrown when a sidecar request fails after exhausting all retry attempts.
|
|
730
|
-
*/
|
|
731
|
-
SidecarRequestFailed: {
|
|
732
|
-
code: -120806,
|
|
733
|
-
description: 'Sidecar request failed after {attempts} attempt(s): {message}'
|
|
734
|
-
},
|
|
735
|
-
|
|
736
|
-
/**
|
|
737
|
-
* Error thrown when the sidecar responds with a non-success status code.
|
|
738
|
-
*/
|
|
739
|
-
SidecarErrorResponse: {
|
|
740
|
-
code: -120807,
|
|
741
|
-
description: 'Sidecar returned error status {status}: {message}'
|
|
742
|
-
},
|
|
743
|
-
|
|
744
|
-
/**
|
|
745
|
-
* Error thrown when the sidecar response is missing the authorizationHeader field.
|
|
746
|
-
*/
|
|
747
|
-
SidecarResponseMissingAuthorizationHeader: {
|
|
748
|
-
code: -120808,
|
|
749
|
-
description: 'Sidecar response missing `authorizationHeader` field'
|
|
750
|
-
},
|
|
751
|
-
|
|
752
|
-
/**
|
|
753
|
-
* Error thrown when the sidecar response body cannot be parsed.
|
|
754
|
-
*/
|
|
755
|
-
SidecarResponseUnparsable: {
|
|
756
|
-
code: -120809,
|
|
757
|
-
description: 'Sidecar returned an unparsable response body'
|
|
758
|
-
},
|
|
759
|
-
|
|
760
|
-
/**
|
|
761
|
-
* Error thrown when acquireTokenOnBehalfOf is called on the sidecar provider, which does not support OBO in Phase 1.
|
|
762
|
-
*/
|
|
763
|
-
OnBehalfOfNotSupportedBySidecar: {
|
|
764
|
-
code: -120810,
|
|
765
|
-
description: 'acquireTokenOnBehalfOf is not supported by the Entra sidecar auth provider in Phase 1'
|
|
766
|
-
},
|
|
767
|
-
|
|
768
|
-
/**
|
|
769
|
-
* Error thrown when the sidecar returns a token with an authorization scheme other than Bearer.
|
|
770
|
-
* The hosting stack always transmits tokens as `Bearer {token}`, so a non-Bearer scheme cannot be
|
|
771
|
-
* honored and is rejected rather than silently sent as an invalid Authorization header.
|
|
772
|
-
*/
|
|
773
|
-
SidecarUnsupportedAuthScheme: {
|
|
774
|
-
code: -120811,
|
|
775
|
-
description: 'The Entra Agent ID sidecar returned an unsupported authorization scheme `{scheme}`. Only the Bearer scheme is supported'
|
|
776
|
-
},
|
|
777
|
-
|
|
778
700
|
// ============================================================================
|
|
779
701
|
// Agent and Client Errors (-120600 to -120650)
|
|
780
702
|
// ============================================================================
|
|
@@ -1012,32 +934,105 @@ export const Errors: { [key: string]: AgentErrorDefinition } = {
|
|
|
1012
934
|
},
|
|
1013
935
|
|
|
1014
936
|
// ============================================================================
|
|
1015
|
-
//
|
|
937
|
+
// Entra Agent ID Sidecar Authentication Errors (-120800 to -120811)
|
|
1016
938
|
// ============================================================================
|
|
1017
939
|
|
|
1018
940
|
/**
|
|
1019
|
-
* Error thrown
|
|
1020
|
-
* Indicates the hosting layer (e.g. express.json() or Fastify's JSON parser) was not configured.
|
|
941
|
+
* Error thrown when the Entra sidecar auth provider is used without connection settings.
|
|
1021
942
|
*/
|
|
1022
|
-
|
|
943
|
+
SidecarConnectionSettingsRequired: {
|
|
1023
944
|
code: -120800,
|
|
1024
|
-
description: '
|
|
945
|
+
description: 'Connection settings must be provided when using the Entra sidecar auth provider'
|
|
1025
946
|
},
|
|
1026
947
|
|
|
1027
948
|
/**
|
|
1028
|
-
* Error thrown
|
|
949
|
+
* Error thrown when the resolved sidecar base URL is not a valid absolute URL.
|
|
1029
950
|
*/
|
|
1030
|
-
|
|
951
|
+
SidecarBaseUrlInvalid: {
|
|
952
|
+
code: -120801,
|
|
953
|
+
description: 'The resolved sidecar base URL `{url}` is not a valid absolute URL'
|
|
954
|
+
},
|
|
955
|
+
|
|
956
|
+
/**
|
|
957
|
+
* Error thrown when the resolved sidecar base URL does not use the http or https scheme.
|
|
958
|
+
*/
|
|
959
|
+
SidecarBaseUrlInsecureScheme: {
|
|
960
|
+
code: -120802,
|
|
961
|
+
description: 'The resolved sidecar base URL `{url}` must use the http or https scheme'
|
|
962
|
+
},
|
|
963
|
+
|
|
964
|
+
/**
|
|
965
|
+
* Error thrown when the resolved sidecar base URL contains userinfo (credentials).
|
|
966
|
+
*/
|
|
967
|
+
SidecarBaseUrlUserInfo: {
|
|
968
|
+
code: -120803,
|
|
969
|
+
description: 'The resolved sidecar base URL `{url}` must not contain userinfo (credentials)'
|
|
970
|
+
},
|
|
971
|
+
|
|
972
|
+
/**
|
|
973
|
+
* Error thrown when the resolved sidecar base URL points to a non-loopback, non-private address.
|
|
974
|
+
*/
|
|
975
|
+
SidecarBaseUrlNotLocal: {
|
|
976
|
+
code: -120804,
|
|
977
|
+
description: 'The resolved sidecar base URL `{url}` points to a non-loopback, non-private address. The Entra Agent ID sidecar (agent container) must be reachable only from within the agent\'s network boundary. Set `bypassLocalNetworkRestriction` to true in the connection configuration to override this safety check (UNSAFE: only for carefully validated private-network configurations)'
|
|
978
|
+
},
|
|
979
|
+
|
|
980
|
+
/**
|
|
981
|
+
* Error thrown when both AgentUsername and AgentUserId are supplied on a sidecar request (mutually exclusive).
|
|
982
|
+
*/
|
|
983
|
+
SidecarUserIdentityMutuallyExclusive: {
|
|
984
|
+
code: -120805,
|
|
985
|
+
description: 'AgentUsername and AgentUserId are mutually exclusive; set only one on the sidecar request options'
|
|
986
|
+
},
|
|
987
|
+
|
|
988
|
+
/**
|
|
989
|
+
* Error thrown when a sidecar request fails after exhausting all retry attempts.
|
|
990
|
+
*/
|
|
991
|
+
SidecarRequestFailed: {
|
|
992
|
+
code: -120806,
|
|
993
|
+
description: 'Sidecar request failed after {attempts} attempt(s): {message}'
|
|
994
|
+
},
|
|
995
|
+
|
|
996
|
+
/**
|
|
997
|
+
* Error thrown when the sidecar responds with a non-success status code.
|
|
998
|
+
*/
|
|
999
|
+
SidecarErrorResponse: {
|
|
1000
|
+
code: -120807,
|
|
1001
|
+
description: 'Sidecar returned error status {status}: {message}'
|
|
1002
|
+
},
|
|
1003
|
+
|
|
1004
|
+
/**
|
|
1005
|
+
* Error thrown when the sidecar response is missing the authorizationHeader field.
|
|
1006
|
+
*/
|
|
1007
|
+
SidecarResponseMissingAuthorizationHeader: {
|
|
1008
|
+
code: -120808,
|
|
1009
|
+
description: 'Sidecar response missing `authorizationHeader` field'
|
|
1010
|
+
},
|
|
1011
|
+
|
|
1012
|
+
/**
|
|
1013
|
+
* Error thrown when the sidecar response body cannot be parsed.
|
|
1014
|
+
*/
|
|
1015
|
+
SidecarResponseUnparsable: {
|
|
1016
|
+
code: -120809,
|
|
1017
|
+
description: 'Sidecar returned an unparsable response body'
|
|
1018
|
+
},
|
|
1019
|
+
|
|
1020
|
+
/**
|
|
1021
|
+
* Error thrown when acquireTokenOnBehalfOf is called on the sidecar provider, which does not support OBO in Phase 1.
|
|
1022
|
+
*/
|
|
1023
|
+
OnBehalfOfNotSupportedBySidecar: {
|
|
1031
1024
|
code: -120810,
|
|
1032
|
-
description: '
|
|
1025
|
+
description: 'acquireTokenOnBehalfOf is not supported by the Entra sidecar auth provider in Phase 1'
|
|
1033
1026
|
},
|
|
1034
1027
|
|
|
1035
1028
|
/**
|
|
1036
|
-
* Error thrown
|
|
1029
|
+
* Error thrown when the sidecar returns a token with an authorization scheme other than Bearer.
|
|
1030
|
+
* The hosting stack always transmits tokens as `Bearer {token}`, so a non-Bearer scheme cannot be
|
|
1031
|
+
* honored and is rejected rather than silently sent as an invalid Authorization header.
|
|
1037
1032
|
*/
|
|
1038
|
-
|
|
1039
|
-
code: -
|
|
1040
|
-
description: '
|
|
1033
|
+
SidecarUnsupportedAuthScheme: {
|
|
1034
|
+
code: -120811,
|
|
1035
|
+
description: 'The Entra Agent ID sidecar returned an unsupported authorization scheme `{scheme}`. Only the Bearer scheme is supported'
|
|
1041
1036
|
},
|
|
1042
1037
|
|
|
1043
1038
|
// ============================================================================
|
|
@@ -1102,6 +1097,52 @@ export const Errors: { [key: string]: AgentErrorDefinition } = {
|
|
|
1102
1097
|
description: "You must configure the Application with an 'adapter' before calling Application.continueConversationAsync()"
|
|
1103
1098
|
},
|
|
1104
1099
|
|
|
1100
|
+
// ============================================================================
|
|
1101
|
+
// Hosting / Web layer errors (-120910 to -120940)
|
|
1102
|
+
// ============================================================================
|
|
1103
|
+
|
|
1104
|
+
/**
|
|
1105
|
+
* Error thrown by CloudAdapter.process when the incoming request lacks a parsed body.
|
|
1106
|
+
* Indicates the hosting layer (e.g. express.json() or Fastify's JSON parser) was not configured.
|
|
1107
|
+
*/
|
|
1108
|
+
MissingRequestBody: {
|
|
1109
|
+
code: -120910,
|
|
1110
|
+
description: '`request.body` parameter required; ensure your hosting layer parses JSON request bodies before invoking the adapter (e.g., express.json() with Express or Fastify\'s built-in JSON parser).'
|
|
1111
|
+
},
|
|
1112
|
+
|
|
1113
|
+
/**
|
|
1114
|
+
* Error thrown by verifyToken when the provided JWT cannot be decoded.
|
|
1115
|
+
*/
|
|
1116
|
+
InvalidJwtToken: {
|
|
1117
|
+
code: -120920,
|
|
1118
|
+
description: 'invalid token'
|
|
1119
|
+
},
|
|
1120
|
+
|
|
1121
|
+
/**
|
|
1122
|
+
* Error thrown by verifyToken when the token audience does not match any configured connection clientId.
|
|
1123
|
+
*/
|
|
1124
|
+
JwtAudienceMismatch: {
|
|
1125
|
+
code: -120930,
|
|
1126
|
+
description: 'Audience mismatch'
|
|
1127
|
+
},
|
|
1128
|
+
|
|
1129
|
+
/**
|
|
1130
|
+
* Error thrown by verifyToken when the token issuer is not in the connection's allowed issuer list.
|
|
1131
|
+
*/
|
|
1132
|
+
JwtIssuerMismatch: {
|
|
1133
|
+
code: -120830,
|
|
1134
|
+
description: 'Issuer mismatch'
|
|
1135
|
+
},
|
|
1136
|
+
|
|
1137
|
+
/**
|
|
1138
|
+
* Error thrown by verifyToken when an Entra token's tenant id (`tid`) claim does not match the
|
|
1139
|
+
* tenant GUID embedded in its issuer (`iss`) claim.
|
|
1140
|
+
*/
|
|
1141
|
+
JwtTenantMismatch: {
|
|
1142
|
+
code: -120840,
|
|
1143
|
+
description: 'Tenant mismatch'
|
|
1144
|
+
},
|
|
1145
|
+
|
|
1105
1146
|
// ============================================================================
|
|
1106
1147
|
// Adaptive Cards Errors (-120950 to -120980)
|
|
1107
1148
|
// ============================================================================
|
package/src/index.ts
CHANGED
|
@@ -22,4 +22,9 @@ export interface AdaptiveCardInvokeValue {
|
|
|
22
22
|
* The state of the adaptive card.
|
|
23
23
|
*/
|
|
24
24
|
state: string
|
|
25
|
+
/**
|
|
26
|
+
* Indicates whether Teams initiated the action while rendering a refreshable
|
|
27
|
+
* card or a user explicitly selected an action.
|
|
28
|
+
*/
|
|
29
|
+
trigger?: 'automatic' | 'manual'
|
|
25
30
|
}
|
|
@@ -80,7 +80,6 @@ export class UserTokenClient {
|
|
|
80
80
|
/**
|
|
81
81
|
* Creates a new instance of UserTokenClient with authentication.
|
|
82
82
|
* @param baseURL - The base URL for the API.
|
|
83
|
-
* @param authConfig - The authentication configuration.
|
|
84
83
|
* @param authProvider - The authentication provider.
|
|
85
84
|
* @param scope - The scope for the authentication token.
|
|
86
85
|
* @param headers - Optional headers to propagate in the request.
|
|
@@ -26,7 +26,8 @@ export const AgentApplicationTraceDefinitions = {
|
|
|
26
26
|
span.setAttributes({
|
|
27
27
|
'route.authorized': record.authorized,
|
|
28
28
|
'route.matched': record.routeMatched,
|
|
29
|
-
...attributes
|
|
29
|
+
...attributes,
|
|
30
|
+
'activity.name': activity.name ?? 'unknown'
|
|
30
31
|
})
|
|
31
32
|
|
|
32
33
|
HostingMetrics.turnsTotalCounter.add(1, attributes)
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { parseBooleanEnv } from './utils/env'
|
|
7
|
+
|
|
8
|
+
/** Hosts used by Microsoft channel callbacks and hosted attachments. */
|
|
9
|
+
const DEFAULT_MICROSOFT_HOSTS = Object.freeze([
|
|
10
|
+
'botframework.com',
|
|
11
|
+
'smba.trafficmanager.net',
|
|
12
|
+
'teams.microsoft.com',
|
|
13
|
+
'teams.microsoft.us',
|
|
14
|
+
'graph.microsoft.com',
|
|
15
|
+
'sharepoint.com',
|
|
16
|
+
'svc.ms',
|
|
17
|
+
'blob.core.windows.net'
|
|
18
|
+
])
|
|
19
|
+
|
|
20
|
+
const OUTBOUND_HOST_VALIDATOR_ENV_PREFIX = 'OutboundHostValidator__'
|
|
21
|
+
const OUTBOUND_HOST_VALIDATOR_ENV_PREFIX_UPPER = OUTBOUND_HOST_VALIDATOR_ENV_PREFIX.toUpperCase()
|
|
22
|
+
|
|
23
|
+
/** Configuration for the shared outbound-host allowlist. */
|
|
24
|
+
export interface OutboundHostValidatorOptions {
|
|
25
|
+
/** Enables allowlist enforcement. Defaults to `false`. */
|
|
26
|
+
enabled?: boolean
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Indicates whether the built-in list of Microsoft first-party hosts
|
|
30
|
+
* (Bot Connector, Graph, SharePoint, Azure Blob/AMS) is included when enforcement is enabled.
|
|
31
|
+
* Defaults to `true`.
|
|
32
|
+
*/
|
|
33
|
+
includeDefaultMicrosoftHosts?: boolean
|
|
34
|
+
|
|
35
|
+
/** Additional exact hosts or host suffixes to allow.
|
|
36
|
+
* An entry matches a request host when the host equals the entry or is a subdomain of it (e.g. `contoso.com` matches `contoso.com` and `files.contoso.com`).
|
|
37
|
+
* A leading `*.` is accepted and ignored (treated as a suffix). Ports and paths are ignored if provided.
|
|
38
|
+
*/
|
|
39
|
+
hosts?: readonly string[]
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** A policy that decides whether an outbound URL is safe to request. */
|
|
43
|
+
export interface OutboundUrlPolicy {
|
|
44
|
+
readonly enabled: boolean
|
|
45
|
+
isAllowed(url: string | URL | null | undefined): boolean
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Shared allowlist policy for server-side outbound requests.
|
|
50
|
+
*
|
|
51
|
+
* A configured suffix matches both the exact host and its subdomains. The
|
|
52
|
+
* policy is disabled by default to preserve existing SDK behavior.
|
|
53
|
+
*/
|
|
54
|
+
export class OutboundHostValidator implements OutboundUrlPolicy {
|
|
55
|
+
public readonly enabled: boolean
|
|
56
|
+
private readonly suffixes: ReadonlySet<string>
|
|
57
|
+
|
|
58
|
+
public constructor (options: OutboundHostValidatorOptions = {}) {
|
|
59
|
+
this.enabled = options.enabled ?? false
|
|
60
|
+
|
|
61
|
+
const suffixes = new Set<string>()
|
|
62
|
+
if (options.includeDefaultMicrosoftHosts ?? true) {
|
|
63
|
+
for (const host of DEFAULT_MICROSOFT_HOSTS) suffixes.add(host)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
for (const host of options.hosts ?? []) {
|
|
67
|
+
const normalized = normalizeConfiguredHost(host)
|
|
68
|
+
if (normalized) suffixes.add(normalized)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
this.suffixes = suffixes
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
public isAllowed (input: string | URL | null | undefined): boolean {
|
|
75
|
+
if (!this.enabled) return true
|
|
76
|
+
|
|
77
|
+
const host = getUrlHost(input)
|
|
78
|
+
if (!host) return false
|
|
79
|
+
|
|
80
|
+
for (const suffix of this.suffixes) {
|
|
81
|
+
if (host === suffix || host.endsWith(`.${suffix}`)) return true
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return false
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Loads validator options from environment variables compatible with the
|
|
90
|
+
* .NET `OutboundHostValidator` configuration section.
|
|
91
|
+
*
|
|
92
|
+
* Hosts can be supplied either as a comma-separated `Hosts` value or as
|
|
93
|
+
* indexed values such as `Hosts__0`, `Hosts__1`, and so on.
|
|
94
|
+
*/
|
|
95
|
+
export function loadOutboundHostValidatorOptionsFromEnv (): OutboundHostValidatorOptions {
|
|
96
|
+
const result: OutboundHostValidatorOptions = {}
|
|
97
|
+
const indexedHosts = new Map<number, string>()
|
|
98
|
+
const hosts: string[] = []
|
|
99
|
+
|
|
100
|
+
for (const [envKey, rawValue] of Object.entries(process.env)) {
|
|
101
|
+
const upperKey = envKey.toUpperCase()
|
|
102
|
+
if (!upperKey.startsWith(OUTBOUND_HOST_VALIDATOR_ENV_PREFIX_UPPER)) continue
|
|
103
|
+
|
|
104
|
+
const property = envKey.substring(OUTBOUND_HOST_VALIDATOR_ENV_PREFIX.length)
|
|
105
|
+
const upperProperty = property.toUpperCase()
|
|
106
|
+
|
|
107
|
+
if (upperProperty === 'ENABLED') {
|
|
108
|
+
const value = parseBooleanEnv(rawValue)
|
|
109
|
+
if (value !== undefined) result.enabled = value
|
|
110
|
+
} else if (upperProperty === 'INCLUDEDEFAULTMICROSOFTHOSTS') {
|
|
111
|
+
const value = parseBooleanEnv(rawValue)
|
|
112
|
+
if (value !== undefined) result.includeDefaultMicrosoftHosts = value
|
|
113
|
+
} else if (upperProperty === 'HOSTS' && rawValue) {
|
|
114
|
+
hosts.push(...rawValue.split(',').map(host => host.trim()).filter(Boolean))
|
|
115
|
+
} else {
|
|
116
|
+
const match = /^HOSTS__(\d+)$/i.exec(property)
|
|
117
|
+
if (match && rawValue?.trim()) indexedHosts.set(Number(match[1]), rawValue.trim())
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
for (const [, host] of [...indexedHosts.entries()].sort(([left], [right]) => left - right)) {
|
|
122
|
+
hosts.push(host)
|
|
123
|
+
}
|
|
124
|
+
if (hosts.length > 0) result.hosts = hosts
|
|
125
|
+
|
|
126
|
+
return result
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Creates the default validator, with explicit settings overriding the environment. */
|
|
130
|
+
export function createOutboundHostValidator (options?: OutboundHostValidatorOptions): OutboundHostValidator {
|
|
131
|
+
const fromEnv = loadOutboundHostValidatorOptionsFromEnv()
|
|
132
|
+
return new OutboundHostValidator({
|
|
133
|
+
enabled: options?.enabled ?? fromEnv.enabled,
|
|
134
|
+
includeDefaultMicrosoftHosts: options?.includeDefaultMicrosoftHosts ?? fromEnv.includeDefaultMicrosoftHosts,
|
|
135
|
+
hosts: options?.hosts ?? fromEnv.hosts
|
|
136
|
+
})
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function getUrlHost (input: string | URL | null | undefined): string | undefined {
|
|
140
|
+
if (!input) return undefined
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
const url = input instanceof URL ? input : new URL(input)
|
|
144
|
+
return normalizeHostname(url.hostname)
|
|
145
|
+
} catch {
|
|
146
|
+
return undefined
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function normalizeConfiguredHost (input: string): string | undefined {
|
|
151
|
+
let candidate = input?.trim()
|
|
152
|
+
if (!candidate) return undefined
|
|
153
|
+
if (candidate.startsWith('*.')) candidate = candidate.slice(2)
|
|
154
|
+
|
|
155
|
+
try {
|
|
156
|
+
const absolute = new URL(candidate)
|
|
157
|
+
if (absolute.hostname) return normalizeHostname(absolute.hostname)
|
|
158
|
+
} catch {
|
|
159
|
+
// A bare host, host:port, or host/path is handled below.
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
return normalizeHostname(new URL(`http://${candidate}`).hostname)
|
|
164
|
+
} catch {
|
|
165
|
+
return undefined
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function normalizeHostname (host: string): string | undefined {
|
|
170
|
+
const normalized = host.trim().toLowerCase().replace(/\.$/, '')
|
|
171
|
+
return normalized || undefined
|
|
172
|
+
}
|
package/src/state/agentState.ts
CHANGED
|
@@ -44,7 +44,10 @@ export interface CustomKey {
|
|
|
44
44
|
* The ID of the conversation where the state should be stored
|
|
45
45
|
*/
|
|
46
46
|
conversationId: string;
|
|
47
|
-
|
|
47
|
+
/**
|
|
48
|
+
* Optional namespace appended to the generated storage key
|
|
49
|
+
*/
|
|
50
|
+
namespace?: string;
|
|
48
51
|
}
|
|
49
52
|
|
|
50
53
|
/**
|
|
@@ -152,8 +155,10 @@ export class AgentState {
|
|
|
152
155
|
private async getStorageOrCustomKey (customKey: CustomKey | undefined, context: TurnContext) {
|
|
153
156
|
let key: string | undefined
|
|
154
157
|
if (customKey && customKey.channelId && customKey.conversationId) {
|
|
155
|
-
|
|
156
|
-
|
|
158
|
+
key = `${customKey.channelId}/conversations/${customKey.conversationId}`
|
|
159
|
+
if (customKey.namespace) {
|
|
160
|
+
key = `${key}/${customKey.namespace}`
|
|
161
|
+
}
|
|
157
162
|
} else {
|
|
158
163
|
key = await this.storageKey(context)
|
|
159
164
|
}
|
|
@@ -217,13 +222,26 @@ export class AgentState {
|
|
|
217
222
|
*/
|
|
218
223
|
private readonly calculateChangeHash = (item: StoreItem): string => {
|
|
219
224
|
const { eTag, ...rest } = item
|
|
220
|
-
|
|
221
|
-
// TODO review circular json structure
|
|
222
|
-
const result = JSON.stringify(rest)
|
|
225
|
+
const result = this.stringifyForHash(rest)
|
|
223
226
|
|
|
224
227
|
const hash = createHash('sha256', { encoding: 'utf-8' })
|
|
225
228
|
const hashed = hash.update(result).digest('hex')
|
|
226
229
|
|
|
227
230
|
return hashed
|
|
228
231
|
}
|
|
232
|
+
|
|
233
|
+
private readonly stringifyForHash = (item: StoreItem): string => {
|
|
234
|
+
const seen = new WeakSet<object>()
|
|
235
|
+
|
|
236
|
+
return JSON.stringify(item, (_key, value) => {
|
|
237
|
+
if (value && typeof value === 'object') {
|
|
238
|
+
if (seen.has(value)) {
|
|
239
|
+
return '[Circular]'
|
|
240
|
+
}
|
|
241
|
+
seen.add(value)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return value
|
|
245
|
+
})
|
|
246
|
+
}
|
|
229
247
|
}
|