microsoft-agents-hosting-core 0.7.0.dev12__py3-none-any.whl → 0.7.0.dev16__py3-none-any.whl
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.
- microsoft_agents/hosting/core/__init__.py +0 -1
- microsoft_agents/hosting/core/app/agent_application.py +12 -24
- microsoft_agents/hosting/core/app/oauth/__init__.py +2 -0
- microsoft_agents/hosting/core/app/oauth/_handlers/__init__.py +2 -0
- microsoft_agents/hosting/core/app/oauth/_handlers/agentic_user_authorization.py +5 -2
- microsoft_agents/hosting/core/app/oauth/_handlers/connector_user_authorization.py +240 -0
- microsoft_agents/hosting/core/app/oauth/authorization.py +18 -8
- microsoft_agents/hosting/core/authorization/access_token_provider_base.py +7 -3
- microsoft_agents/hosting/core/authorization/agent_auth_configuration.py +29 -0
- microsoft_agents/hosting/core/authorization/anonymous_token_provider.py +7 -3
- microsoft_agents/hosting/core/authorization/claims_identity.py +16 -0
- microsoft_agents/hosting/core/authorization/jwt_token_validator.py +2 -2
- microsoft_agents/hosting/core/channel_service_adapter.py +1 -3
- microsoft_agents/hosting/core/connector/__init__.py +4 -0
- microsoft_agents/hosting/core/connector/client/connector_client.py +0 -1
- microsoft_agents/hosting/core/connector/client/user_token_client.py +0 -1
- microsoft_agents/hosting/core/connector/mcs/__init__.py +3 -0
- microsoft_agents/hosting/core/connector/mcs/mcs_connector_client.py +249 -0
- microsoft_agents/hosting/core/errors/error_resources.py +15 -0
- microsoft_agents/hosting/core/http/_http_adapter_base.py +3 -6
- microsoft_agents/hosting/core/rest_channel_service_client_factory.py +17 -3
- microsoft_agents/hosting/core/storage/memory_storage.py +0 -1
- microsoft_agents/hosting/core/storage/storage.py +0 -1
- microsoft_agents/hosting/core/storage/transcript_logger.py +0 -1
- {microsoft_agents_hosting_core-0.7.0.dev12.dist-info → microsoft_agents_hosting_core-0.7.0.dev16.dist-info}/METADATA +2 -2
- {microsoft_agents_hosting_core-0.7.0.dev12.dist-info → microsoft_agents_hosting_core-0.7.0.dev16.dist-info}/RECORD +29 -26
- {microsoft_agents_hosting_core-0.7.0.dev12.dist-info → microsoft_agents_hosting_core-0.7.0.dev16.dist-info}/WHEEL +1 -1
- {microsoft_agents_hosting_core-0.7.0.dev12.dist-info → microsoft_agents_hosting_core-0.7.0.dev16.dist-info}/licenses/LICENSE +0 -0
- {microsoft_agents_hosting_core-0.7.0.dev12.dist-info → microsoft_agents_hosting_core-0.7.0.dev16.dist-info}/top_level.txt +0 -0
|
@@ -120,11 +120,9 @@ class AgentApplication(Agent, Generic[StateT]):
|
|
|
120
120
|
"ApplicationOptions.storage is required and was not configured.",
|
|
121
121
|
stack_info=True,
|
|
122
122
|
)
|
|
123
|
-
raise ApplicationError(
|
|
124
|
-
"""
|
|
123
|
+
raise ApplicationError("""
|
|
125
124
|
The `ApplicationOptions.storage` property is required and was not configured.
|
|
126
|
-
"""
|
|
127
|
-
)
|
|
125
|
+
""")
|
|
128
126
|
|
|
129
127
|
if options.long_running_messages and (
|
|
130
128
|
not options.adapter or not options.bot_app_id
|
|
@@ -133,12 +131,10 @@ class AgentApplication(Agent, Generic[StateT]):
|
|
|
133
131
|
"ApplicationOptions.long_running_messages requires an adapter and bot_app_id.",
|
|
134
132
|
stack_info=True,
|
|
135
133
|
)
|
|
136
|
-
raise ApplicationError(
|
|
137
|
-
"""
|
|
134
|
+
raise ApplicationError("""
|
|
138
135
|
The `ApplicationOptions.long_running_messages` property is unavailable because
|
|
139
136
|
no adapter or `bot_app_id` was configured.
|
|
140
|
-
"""
|
|
141
|
-
)
|
|
137
|
+
""")
|
|
142
138
|
|
|
143
139
|
if options.adapter:
|
|
144
140
|
self._adapter = options.adapter
|
|
@@ -180,12 +176,10 @@ class AgentApplication(Agent, Generic[StateT]):
|
|
|
180
176
|
"AgentApplication.adapter(): self._adapter is not configured.",
|
|
181
177
|
stack_info=True,
|
|
182
178
|
)
|
|
183
|
-
raise ApplicationError(
|
|
184
|
-
"""
|
|
179
|
+
raise ApplicationError("""
|
|
185
180
|
The AgentApplication.adapter property is unavailable because it was
|
|
186
181
|
not configured when creating the AgentApplication.
|
|
187
|
-
"""
|
|
188
|
-
)
|
|
182
|
+
""")
|
|
189
183
|
|
|
190
184
|
return self._adapter
|
|
191
185
|
|
|
@@ -203,12 +197,10 @@ class AgentApplication(Agent, Generic[StateT]):
|
|
|
203
197
|
"AgentApplication.auth(): self._auth is not configured.",
|
|
204
198
|
stack_info=True,
|
|
205
199
|
)
|
|
206
|
-
raise ApplicationError(
|
|
207
|
-
"""
|
|
200
|
+
raise ApplicationError("""
|
|
208
201
|
The `AgentApplication.auth` property is unavailable because
|
|
209
202
|
no Auth options were configured.
|
|
210
|
-
"""
|
|
211
|
-
)
|
|
203
|
+
""")
|
|
212
204
|
|
|
213
205
|
return self._auth
|
|
214
206
|
|
|
@@ -592,12 +584,10 @@ class AgentApplication(Agent, Generic[StateT]):
|
|
|
592
584
|
f"Failed to register sign-in success handler for route handler {func.__name__}",
|
|
593
585
|
stack_info=True,
|
|
594
586
|
)
|
|
595
|
-
raise ApplicationError(
|
|
596
|
-
"""
|
|
587
|
+
raise ApplicationError("""
|
|
597
588
|
The `AgentApplication.on_sign_in_success` method is unavailable because
|
|
598
589
|
no Auth options were configured.
|
|
599
|
-
"""
|
|
600
|
-
)
|
|
590
|
+
""")
|
|
601
591
|
return func
|
|
602
592
|
|
|
603
593
|
def on_sign_in_failure(
|
|
@@ -628,12 +618,10 @@ class AgentApplication(Agent, Generic[StateT]):
|
|
|
628
618
|
f"Failed to register sign-in failure handler for route handler {func.__name__}",
|
|
629
619
|
stack_info=True,
|
|
630
620
|
)
|
|
631
|
-
raise ApplicationError(
|
|
632
|
-
"""
|
|
621
|
+
raise ApplicationError("""
|
|
633
622
|
The `AgentApplication.on_sign_in_failure` method is unavailable because
|
|
634
623
|
no Auth options were configured.
|
|
635
|
-
"""
|
|
636
|
-
)
|
|
624
|
+
""")
|
|
637
625
|
return func
|
|
638
626
|
|
|
639
627
|
def error(
|
|
@@ -9,6 +9,7 @@ from ._sign_in_response import _SignInResponse
|
|
|
9
9
|
from ._handlers import (
|
|
10
10
|
_UserAuthorization,
|
|
11
11
|
AgenticUserAuthorization,
|
|
12
|
+
ConnectorUserAuthorization,
|
|
12
13
|
_AuthorizationHandler,
|
|
13
14
|
)
|
|
14
15
|
|
|
@@ -20,4 +21,5 @@ __all__ = [
|
|
|
20
21
|
"_SignInResponse",
|
|
21
22
|
"_UserAuthorization",
|
|
22
23
|
"AgenticUserAuthorization",
|
|
24
|
+
"ConnectorUserAuthorization",
|
|
23
25
|
]
|
|
@@ -4,11 +4,13 @@ Licensed under the MIT License.
|
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
6
|
from .agentic_user_authorization import AgenticUserAuthorization
|
|
7
|
+
from .connector_user_authorization import ConnectorUserAuthorization
|
|
7
8
|
from ._user_authorization import _UserAuthorization
|
|
8
9
|
from ._authorization_handler import _AuthorizationHandler
|
|
9
10
|
|
|
10
11
|
__all__ = [
|
|
11
12
|
"AgenticUserAuthorization",
|
|
13
|
+
"ConnectorUserAuthorization",
|
|
12
14
|
"_UserAuthorization",
|
|
13
15
|
"_AuthorizationHandler",
|
|
14
16
|
]
|
|
@@ -75,7 +75,7 @@ class AgenticUserAuthorization(_AuthorizationHandler):
|
|
|
75
75
|
agentic_instance_id = context.activity.get_agentic_instance_id()
|
|
76
76
|
assert agentic_instance_id
|
|
77
77
|
instance_token, _ = await connection.get_agentic_instance_token(
|
|
78
|
-
agentic_instance_id
|
|
78
|
+
context.activity.get_agentic_tenant_id(), agentic_instance_id
|
|
79
79
|
)
|
|
80
80
|
return (
|
|
81
81
|
TokenResponse(token=instance_token) if instance_token else TokenResponse()
|
|
@@ -131,7 +131,10 @@ class AgenticUserAuthorization(_AuthorizationHandler):
|
|
|
131
131
|
)
|
|
132
132
|
|
|
133
133
|
token = await connection.get_agentic_user_token(
|
|
134
|
-
|
|
134
|
+
context.activity.get_agentic_tenant_id(),
|
|
135
|
+
agentic_instance_id,
|
|
136
|
+
agentic_user_id,
|
|
137
|
+
scopes,
|
|
135
138
|
)
|
|
136
139
|
return TokenResponse(token=token) if token else TokenResponse()
|
|
137
140
|
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
Licensed under the MIT License.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import logging
|
|
7
|
+
import jwt
|
|
8
|
+
from datetime import datetime, timezone
|
|
9
|
+
from typing import Optional
|
|
10
|
+
|
|
11
|
+
from microsoft_agents.activity import TokenResponse
|
|
12
|
+
from microsoft_agents.hosting.core.errors import ErrorResources
|
|
13
|
+
|
|
14
|
+
from ...._oauth._flow_state import _FlowStateTag
|
|
15
|
+
from ....turn_context import TurnContext
|
|
16
|
+
from ....storage import Storage
|
|
17
|
+
from ....authorization import Connections
|
|
18
|
+
from ..auth_handler import AuthHandler
|
|
19
|
+
from ._authorization_handler import _AuthorizationHandler
|
|
20
|
+
from .._sign_in_response import _SignInResponse
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ConnectorUserAuthorization(_AuthorizationHandler):
|
|
26
|
+
"""
|
|
27
|
+
User Authorization handling for Copilot Studio Connector requests.
|
|
28
|
+
Extracts token from the identity and performs OBO token exchange.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def __init__(
|
|
32
|
+
self,
|
|
33
|
+
storage: Storage,
|
|
34
|
+
connection_manager: Connections,
|
|
35
|
+
auth_handler: Optional[AuthHandler] = None,
|
|
36
|
+
*,
|
|
37
|
+
auth_handler_id: Optional[str] = None,
|
|
38
|
+
auth_handler_settings: Optional[dict] = None,
|
|
39
|
+
**kwargs,
|
|
40
|
+
) -> None:
|
|
41
|
+
"""
|
|
42
|
+
Creates a new instance of ConnectorUserAuthorization.
|
|
43
|
+
|
|
44
|
+
:param storage: The storage system to use for state management.
|
|
45
|
+
:type storage: Storage
|
|
46
|
+
:param connection_manager: The connection manager for OAuth providers.
|
|
47
|
+
:type connection_manager: Connections
|
|
48
|
+
:param auth_handler: Configuration for OAuth provider.
|
|
49
|
+
:type auth_handler: AuthHandler, Optional
|
|
50
|
+
:param auth_handler_id: Optional ID of the auth handler.
|
|
51
|
+
:type auth_handler_id: str, Optional
|
|
52
|
+
:param auth_handler_settings: Optional settings dict for the auth handler.
|
|
53
|
+
:type auth_handler_settings: dict, Optional
|
|
54
|
+
"""
|
|
55
|
+
super().__init__(
|
|
56
|
+
storage,
|
|
57
|
+
connection_manager,
|
|
58
|
+
auth_handler,
|
|
59
|
+
auth_handler_id=auth_handler_id,
|
|
60
|
+
auth_handler_settings=auth_handler_settings,
|
|
61
|
+
**kwargs,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
async def _sign_in(
|
|
65
|
+
self,
|
|
66
|
+
context: TurnContext,
|
|
67
|
+
exchange_connection: Optional[str] = None,
|
|
68
|
+
exchange_scopes: Optional[list[str]] = None,
|
|
69
|
+
) -> _SignInResponse:
|
|
70
|
+
"""
|
|
71
|
+
For connector requests, there is no separate sign-in flow.
|
|
72
|
+
The token is extracted from the identity.
|
|
73
|
+
|
|
74
|
+
:param context: The turn context for the current turn of conversation.
|
|
75
|
+
:type context: TurnContext
|
|
76
|
+
:param exchange_connection: Optional connection name for token exchange.
|
|
77
|
+
:type exchange_connection: Optional[str]
|
|
78
|
+
:param exchange_scopes: Optional list of scopes (unused for connector auth).
|
|
79
|
+
:type exchange_scopes: Optional[list[str]]
|
|
80
|
+
:return: A SignInResponse with the extracted token.
|
|
81
|
+
:rtype: _SignInResponse
|
|
82
|
+
"""
|
|
83
|
+
# Connector auth uses the token from the request, not a separate sign-in flow
|
|
84
|
+
token_response = await self.get_refreshed_token(context)
|
|
85
|
+
return _SignInResponse(
|
|
86
|
+
token_response=token_response, tag=_FlowStateTag.COMPLETE
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
async def get_refreshed_token(
|
|
90
|
+
self,
|
|
91
|
+
context: TurnContext,
|
|
92
|
+
exchange_connection: Optional[str] = None,
|
|
93
|
+
exchange_scopes: Optional[list[str]] = None,
|
|
94
|
+
) -> TokenResponse:
|
|
95
|
+
"""
|
|
96
|
+
Gets the connector user token and optionally exchanges it via OBO.
|
|
97
|
+
|
|
98
|
+
:param context: The turn context for the current turn of conversation.
|
|
99
|
+
:type context: TurnContext
|
|
100
|
+
:param exchange_connection: Optional name of the connection to use for token exchange.
|
|
101
|
+
:type exchange_connection: Optional[str], Optional
|
|
102
|
+
:param exchange_scopes: Optional list of scopes to request during token exchange.
|
|
103
|
+
:type exchange_scopes: Optional[list[str]], Optional
|
|
104
|
+
:return: The token response, potentially after OBO exchange.
|
|
105
|
+
:rtype: TokenResponse
|
|
106
|
+
"""
|
|
107
|
+
token_response = self._create_token_response(context)
|
|
108
|
+
|
|
109
|
+
# Check if token is expired
|
|
110
|
+
if token_response.expiration:
|
|
111
|
+
try:
|
|
112
|
+
# Parse ISO 8601 format
|
|
113
|
+
expiration = datetime.fromisoformat(
|
|
114
|
+
token_response.expiration.replace("Z", "+00:00")
|
|
115
|
+
)
|
|
116
|
+
if expiration <= datetime.now(timezone.utc):
|
|
117
|
+
raise ValueError(
|
|
118
|
+
f"Unexpected connector token expiration for handler: {self._id}"
|
|
119
|
+
)
|
|
120
|
+
except (ValueError, AttributeError) as ex:
|
|
121
|
+
logger.error(
|
|
122
|
+
f"Error checking token expiration for handler {self._id}: {ex}"
|
|
123
|
+
)
|
|
124
|
+
raise
|
|
125
|
+
|
|
126
|
+
# Perform OBO exchange if configured
|
|
127
|
+
try:
|
|
128
|
+
return await self._handle_obo(
|
|
129
|
+
context, token_response, exchange_connection, exchange_scopes
|
|
130
|
+
)
|
|
131
|
+
except Exception:
|
|
132
|
+
await self._sign_out(context)
|
|
133
|
+
raise
|
|
134
|
+
|
|
135
|
+
async def _sign_out(self, context: TurnContext) -> None:
|
|
136
|
+
"""
|
|
137
|
+
Sign-out is a no-op for connector authorization.
|
|
138
|
+
|
|
139
|
+
:param context: The turn context for the current turn of conversation.
|
|
140
|
+
:type context: TurnContext
|
|
141
|
+
"""
|
|
142
|
+
# No concept of sign-out with ConnectorAuth
|
|
143
|
+
logger.debug("Sign-out called for ConnectorUserAuthorization (no-op)")
|
|
144
|
+
|
|
145
|
+
async def _handle_obo(
|
|
146
|
+
self,
|
|
147
|
+
context: TurnContext,
|
|
148
|
+
input_token_response: TokenResponse,
|
|
149
|
+
exchange_connection: Optional[str] = None,
|
|
150
|
+
exchange_scopes: Optional[list[str]] = None,
|
|
151
|
+
) -> TokenResponse:
|
|
152
|
+
"""
|
|
153
|
+
Exchanges a token for another token with different scopes via OBO flow.
|
|
154
|
+
|
|
155
|
+
:param context: The context object for the current turn.
|
|
156
|
+
:type context: TurnContext
|
|
157
|
+
:param input_token_response: The input token to exchange.
|
|
158
|
+
:type input_token_response: TokenResponse
|
|
159
|
+
:param exchange_connection: Optional connection name for exchange.
|
|
160
|
+
:type exchange_connection: Optional[str]
|
|
161
|
+
:param exchange_scopes: Optional scopes for the exchanged token.
|
|
162
|
+
:type exchange_scopes: Optional[list[str]]
|
|
163
|
+
:return: The token response after exchange, or the original if exchange not configured.
|
|
164
|
+
:rtype: TokenResponse
|
|
165
|
+
"""
|
|
166
|
+
if not input_token_response:
|
|
167
|
+
return input_token_response
|
|
168
|
+
|
|
169
|
+
connection_name = exchange_connection or self._handler.obo_connection_name
|
|
170
|
+
scopes = exchange_scopes or self._handler.scopes
|
|
171
|
+
|
|
172
|
+
# If OBO is not configured, return token as-is
|
|
173
|
+
if not connection_name or not scopes:
|
|
174
|
+
return input_token_response
|
|
175
|
+
|
|
176
|
+
# Check if token is exchangeable
|
|
177
|
+
if not input_token_response.is_exchangeable():
|
|
178
|
+
raise ValueError(
|
|
179
|
+
str(ErrorResources.OboNotExchangeableToken.format(self._id))
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
# Get the connection that supports OBO
|
|
183
|
+
token_provider = self._connection_manager.get_connection(connection_name)
|
|
184
|
+
if not token_provider:
|
|
185
|
+
raise ValueError(
|
|
186
|
+
str(
|
|
187
|
+
ErrorResources.ResourceNotFound.format(
|
|
188
|
+
f"Connection '{connection_name}'"
|
|
189
|
+
)
|
|
190
|
+
)
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
# Perform the OBO exchange
|
|
194
|
+
# Note: In Python, the acquire_token_on_behalf_of method is on the AccessTokenProviderBase
|
|
195
|
+
token = await token_provider.acquire_token_on_behalf_of(
|
|
196
|
+
scopes=scopes,
|
|
197
|
+
user_assertion=input_token_response.token,
|
|
198
|
+
)
|
|
199
|
+
return TokenResponse(token=token) if token else None
|
|
200
|
+
|
|
201
|
+
def _create_token_response(self, context: TurnContext) -> TokenResponse:
|
|
202
|
+
"""
|
|
203
|
+
Creates a TokenResponse from the security token in the turn context identity.
|
|
204
|
+
|
|
205
|
+
:param context: The turn context for the current turn of conversation.
|
|
206
|
+
:type context: TurnContext
|
|
207
|
+
:return: A TokenResponse containing the extracted token.
|
|
208
|
+
:rtype: TokenResponse
|
|
209
|
+
:raises ValueError: If the identity doesn't have a security token.
|
|
210
|
+
"""
|
|
211
|
+
if not context.identity or not hasattr(context.identity, "security_token"):
|
|
212
|
+
raise ValueError(
|
|
213
|
+
f"Unexpected connector request - no security token found for handler: {self._id}"
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
security_token = context.identity.security_token
|
|
217
|
+
if not security_token:
|
|
218
|
+
raise ValueError(
|
|
219
|
+
f"Unexpected connector request - security token is None for handler: {self._id}"
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
token_response = TokenResponse(token=security_token)
|
|
223
|
+
|
|
224
|
+
# Try to extract expiration and check if exchangeable
|
|
225
|
+
try:
|
|
226
|
+
# TODO: (connector) validate this decoding
|
|
227
|
+
jwt_token = jwt.decode(security_token, options={"verify_signature": False})
|
|
228
|
+
|
|
229
|
+
# Set expiration if present
|
|
230
|
+
if "exp" in jwt_token:
|
|
231
|
+
# JWT exp is in Unix timestamp (seconds since epoch)
|
|
232
|
+
expiration = datetime.fromtimestamp(jwt_token["exp"], tz=timezone.utc)
|
|
233
|
+
# Convert to ISO 8601 format
|
|
234
|
+
token_response.expiration = expiration.isoformat()
|
|
235
|
+
|
|
236
|
+
except Exception as ex:
|
|
237
|
+
logger.warning(f"Failed to parse JWT token for handler {self._id}: {ex}")
|
|
238
|
+
raise ex
|
|
239
|
+
|
|
240
|
+
return token_response
|
|
@@ -20,6 +20,7 @@ from ._sign_in_state import _SignInState
|
|
|
20
20
|
from ._sign_in_response import _SignInResponse
|
|
21
21
|
from ._handlers import (
|
|
22
22
|
AgenticUserAuthorization,
|
|
23
|
+
ConnectorUserAuthorization,
|
|
23
24
|
_UserAuthorization,
|
|
24
25
|
_AuthorizationHandler,
|
|
25
26
|
)
|
|
@@ -29,6 +30,7 @@ logger = logging.getLogger(__name__)
|
|
|
29
30
|
AUTHORIZATION_TYPE_MAP = {
|
|
30
31
|
"userauthorization": _UserAuthorization,
|
|
31
32
|
"agenticuserauthorization": AgenticUserAuthorization,
|
|
33
|
+
"connectoruserauthorization": ConnectorUserAuthorization,
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
|
|
@@ -44,7 +46,7 @@ class Authorization:
|
|
|
44
46
|
storage: Storage,
|
|
45
47
|
connection_manager: Connections,
|
|
46
48
|
auth_handlers: Optional[dict[str, AuthHandler]] = None,
|
|
47
|
-
|
|
49
|
+
auto_sign_in: bool = False,
|
|
48
50
|
use_cache: bool = False,
|
|
49
51
|
**kwargs,
|
|
50
52
|
):
|
|
@@ -77,21 +79,26 @@ class Authorization:
|
|
|
77
79
|
|
|
78
80
|
self._handlers = {}
|
|
79
81
|
|
|
82
|
+
auth_configuration: dict = kwargs.get("AGENTAPPLICATION", {}).get(
|
|
83
|
+
"USERAUTHORIZATION", {}
|
|
84
|
+
)
|
|
80
85
|
if not auth_handlers:
|
|
81
86
|
# get from config
|
|
82
|
-
auth_configuration: dict = kwargs.get("AGENTAPPLICATION", {}).get(
|
|
83
|
-
"USERAUTHORIZATION", {}
|
|
84
|
-
)
|
|
85
87
|
handlers_config: dict[str, dict] = auth_configuration.get("HANDLERS")
|
|
86
88
|
if not auth_handlers and handlers_config:
|
|
87
89
|
auth_handlers = {
|
|
88
90
|
handler_name: AuthHandler(
|
|
89
|
-
name=handler_name,
|
|
91
|
+
name=handler_name,
|
|
92
|
+
auth_type=config.get("TYPE", None),
|
|
93
|
+
**config.get("SETTINGS", {}),
|
|
90
94
|
)
|
|
91
95
|
for handler_name, config in handlers_config.items()
|
|
92
96
|
}
|
|
93
97
|
|
|
94
98
|
self._handler_settings = auth_handlers
|
|
99
|
+
self._auto_sign_in = auto_sign_in or bool(
|
|
100
|
+
auth_configuration.get("AUTO_SIGN_IN", False)
|
|
101
|
+
)
|
|
95
102
|
|
|
96
103
|
# operations default to the first handler if none specified
|
|
97
104
|
if self._handler_settings:
|
|
@@ -296,13 +303,16 @@ class Authorization:
|
|
|
296
303
|
"""
|
|
297
304
|
sign_in_state = await self._load_sign_in_state(context)
|
|
298
305
|
|
|
299
|
-
if sign_in_state:
|
|
300
|
-
auth_handler_id = sign_in_state.active_handler_id
|
|
301
|
-
if auth_handler_id:
|
|
306
|
+
if sign_in_state or self._auto_sign_in:
|
|
307
|
+
auth_handler_id = sign_in_state.active_handler_id if sign_in_state else None
|
|
308
|
+
if auth_handler_id or self._auto_sign_in:
|
|
302
309
|
sign_in_response = await self._start_or_continue_sign_in(
|
|
303
310
|
context, state, auth_handler_id
|
|
304
311
|
)
|
|
305
312
|
if sign_in_response.tag == _FlowStateTag.COMPLETE:
|
|
313
|
+
if not sign_in_state:
|
|
314
|
+
# flow just completed, no continuation activity
|
|
315
|
+
return False, None
|
|
306
316
|
assert sign_in_state.continuation_activity is not None
|
|
307
317
|
continuation_activity = (
|
|
308
318
|
sign_in_state.continuation_activity.model_copy()
|
|
@@ -33,16 +33,20 @@ class AccessTokenProviderBase(Protocol):
|
|
|
33
33
|
raise NotImplementedError()
|
|
34
34
|
|
|
35
35
|
async def get_agentic_application_token(
|
|
36
|
-
self, agent_app_instance_id: str
|
|
36
|
+
self, tenant_id: str, agent_app_instance_id: str
|
|
37
37
|
) -> Optional[str]:
|
|
38
38
|
raise NotImplementedError()
|
|
39
39
|
|
|
40
40
|
async def get_agentic_instance_token(
|
|
41
|
-
self, agent_app_instance_id: str
|
|
41
|
+
self, tenant_id: str, agent_app_instance_id: str
|
|
42
42
|
) -> tuple[str, str]:
|
|
43
43
|
raise NotImplementedError()
|
|
44
44
|
|
|
45
45
|
async def get_agentic_user_token(
|
|
46
|
-
self,
|
|
46
|
+
self,
|
|
47
|
+
tenant_id: str,
|
|
48
|
+
agent_app_instance_id: str,
|
|
49
|
+
agentic_user_id: str,
|
|
50
|
+
scopes: list[str],
|
|
47
51
|
) -> Optional[str]:
|
|
48
52
|
raise NotImplementedError()
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
2
2
|
# Licensed under the MIT License.
|
|
3
3
|
|
|
4
|
+
from __future__ import annotations
|
|
4
5
|
from typing import Optional
|
|
5
6
|
|
|
6
7
|
from microsoft_agents.hosting.core.authorization.auth_types import AuthTypes
|
|
@@ -32,6 +33,16 @@ class AgentAuthConfiguration:
|
|
|
32
33
|
SCOPES: Optional[list[str]]
|
|
33
34
|
AUTHORITY: Optional[str]
|
|
34
35
|
ALT_BLUEPRINT_ID: Optional[str]
|
|
36
|
+
ANONYMOUS_ALLOWED: bool = False
|
|
37
|
+
|
|
38
|
+
# Multi-connection support: Maintains a map of all configured connections
|
|
39
|
+
# to enable JWT validation across connections. This allows tokens issued
|
|
40
|
+
# for any configured connection to be validated, supporting multi-tenant
|
|
41
|
+
# scenarios where connections share a security boundary.
|
|
42
|
+
#
|
|
43
|
+
# Note: This is an internal implementation detail. External code should
|
|
44
|
+
# not directly access _connections.
|
|
45
|
+
_connections: dict[str, AgentAuthConfiguration]
|
|
35
46
|
|
|
36
47
|
def __init__(
|
|
37
48
|
self,
|
|
@@ -44,6 +55,7 @@ class AgentAuthConfiguration:
|
|
|
44
55
|
connection_name: Optional[str] = None,
|
|
45
56
|
authority: Optional[str] = None,
|
|
46
57
|
scopes: Optional[list[str]] = None,
|
|
58
|
+
anonymous_allowed: bool = False,
|
|
47
59
|
**kwargs: Optional[dict[str, str]],
|
|
48
60
|
):
|
|
49
61
|
|
|
@@ -57,6 +69,12 @@ class AgentAuthConfiguration:
|
|
|
57
69
|
self.CONNECTION_NAME = connection_name or kwargs.get("CONNECTIONNAME", None)
|
|
58
70
|
self.SCOPES = scopes or kwargs.get("SCOPES", None)
|
|
59
71
|
self.ALT_BLUEPRINT_ID = kwargs.get("ALT_BLUEPRINT_NAME", None)
|
|
72
|
+
self.ANONYMOUS_ALLOWED = anonymous_allowed or kwargs.get(
|
|
73
|
+
"ANONYMOUS_ALLOWED", False
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# JWT-patch: always at least include self for backward compat
|
|
77
|
+
self._connections = {str(self.CONNECTION_NAME): self}
|
|
60
78
|
|
|
61
79
|
@property
|
|
62
80
|
def ISSUERS(self) -> list[str]:
|
|
@@ -68,3 +86,14 @@ class AgentAuthConfiguration:
|
|
|
68
86
|
f"https://sts.windows.net/{self.TENANT_ID}/",
|
|
69
87
|
f"https://login.microsoftonline.com/{self.TENANT_ID}/v2.0",
|
|
70
88
|
]
|
|
89
|
+
|
|
90
|
+
def _jwt_patch_is_valid_aud(self, aud: str) -> bool:
|
|
91
|
+
"""
|
|
92
|
+
JWT-patch: Checks if the given audience is valid for any of the connections.
|
|
93
|
+
"""
|
|
94
|
+
for conn in self._connections.values():
|
|
95
|
+
if not conn.CLIENT_ID:
|
|
96
|
+
continue
|
|
97
|
+
if aud.lower() == conn.CLIENT_ID.lower():
|
|
98
|
+
return True
|
|
99
|
+
return False
|
|
@@ -23,16 +23,20 @@ class AnonymousTokenProvider(AccessTokenProviderBase):
|
|
|
23
23
|
return ""
|
|
24
24
|
|
|
25
25
|
async def get_agentic_application_token(
|
|
26
|
-
self, agent_app_instance_id: str
|
|
26
|
+
self, tenant_id: str, agent_app_instance_id: str
|
|
27
27
|
) -> Optional[str]:
|
|
28
28
|
return ""
|
|
29
29
|
|
|
30
30
|
async def get_agentic_instance_token(
|
|
31
|
-
self, agent_app_instance_id: str
|
|
31
|
+
self, tenant_id: str, agent_app_instance_id: str
|
|
32
32
|
) -> tuple[str, str]:
|
|
33
33
|
return "", ""
|
|
34
34
|
|
|
35
35
|
async def get_agentic_user_token(
|
|
36
|
-
self,
|
|
36
|
+
self,
|
|
37
|
+
tenant_id: str,
|
|
38
|
+
agent_app_instance_id: str,
|
|
39
|
+
agentic_user_id: str,
|
|
40
|
+
scopes: list[str],
|
|
37
41
|
) -> Optional[str]:
|
|
38
42
|
return ""
|
|
@@ -11,10 +11,12 @@ class ClaimsIdentity:
|
|
|
11
11
|
claims: dict[str, str],
|
|
12
12
|
is_authenticated: bool,
|
|
13
13
|
authentication_type: Optional[str] = None,
|
|
14
|
+
security_token: Optional[str] = None,
|
|
14
15
|
):
|
|
15
16
|
self.claims = claims
|
|
16
17
|
self.is_authenticated = is_authenticated
|
|
17
18
|
self.authentication_type = authentication_type
|
|
19
|
+
self.security_token = security_token
|
|
18
20
|
|
|
19
21
|
def get_claim_value(self, claim_type: str) -> Optional[str]:
|
|
20
22
|
return self.claims.get(claim_type)
|
|
@@ -83,3 +85,17 @@ class ClaimsIdentity:
|
|
|
83
85
|
if self.is_agent_claim()
|
|
84
86
|
else AuthenticationConstants.AGENTS_SDK_SCOPE
|
|
85
87
|
)
|
|
88
|
+
|
|
89
|
+
def get_token_scope(self) -> list[str]:
|
|
90
|
+
"""
|
|
91
|
+
Gets the token scope from current claims.
|
|
92
|
+
|
|
93
|
+
:return: The token scope.
|
|
94
|
+
"""
|
|
95
|
+
return [
|
|
96
|
+
(
|
|
97
|
+
f"{self.get_outgoing_app_id()}/.default"
|
|
98
|
+
if self.is_agent_claim()
|
|
99
|
+
else AuthenticationConstants.AGENTS_SDK_SCOPE + "/.default"
|
|
100
|
+
)
|
|
101
|
+
]
|
|
@@ -28,13 +28,13 @@ class JwtTokenValidator:
|
|
|
28
28
|
leeway=300.0,
|
|
29
29
|
options={"verify_aud": False},
|
|
30
30
|
)
|
|
31
|
-
if decoded_token["aud"]
|
|
31
|
+
if not self.configuration._jwt_patch_is_valid_aud(decoded_token["aud"]):
|
|
32
32
|
logger.error(f"Invalid audience: {decoded_token['aud']}", stack_info=True)
|
|
33
33
|
raise ValueError("Invalid audience.")
|
|
34
34
|
|
|
35
35
|
# This probably should return a ClaimsIdentity
|
|
36
36
|
logger.debug("JWT token validated successfully.")
|
|
37
|
-
return ClaimsIdentity(decoded_token, True)
|
|
37
|
+
return ClaimsIdentity(decoded_token, True, security_token=token)
|
|
38
38
|
|
|
39
39
|
def get_anonymous_claims(self) -> ClaimsIdentity:
|
|
40
40
|
logger.debug("Returning anonymous claims identity.")
|
|
@@ -381,16 +381,14 @@ class ChannelServiceAdapter(ChannelAdapter, ABC):
|
|
|
381
381
|
If the task completes successfully, then an :class:`microsoft_agents.activity.InvokeResponse` is returned;
|
|
382
382
|
otherwise, `None` is returned.
|
|
383
383
|
"""
|
|
384
|
-
scopes: list[str] =
|
|
384
|
+
scopes: list[str] = claims_identity.get_token_scope()
|
|
385
385
|
outgoing_audience: str = None
|
|
386
386
|
|
|
387
387
|
if claims_identity.is_agent_claim():
|
|
388
388
|
outgoing_audience = claims_identity.get_token_audience()
|
|
389
|
-
scopes = [f"{claims_identity.get_outgoing_app_id()}/.default"]
|
|
390
389
|
activity.caller_id = f"{CallerIdConstants.agent_to_agent_prefix}{claims_identity.get_outgoing_app_id()}"
|
|
391
390
|
else:
|
|
392
391
|
outgoing_audience = AuthenticationConstants.AGENTS_SDK_SCOPE
|
|
393
|
-
scopes = [f"{AuthenticationConstants.AGENTS_SDK_SCOPE}/.default"]
|
|
394
392
|
|
|
395
393
|
use_anonymous_auth_callback = False
|
|
396
394
|
if (
|
|
@@ -9,11 +9,15 @@ from .client.user_token_client import UserTokenClient
|
|
|
9
9
|
# Teams API
|
|
10
10
|
from .teams.teams_connector_client import TeamsConnectorClient
|
|
11
11
|
|
|
12
|
+
# MCS API
|
|
13
|
+
from .mcs.mcs_connector_client import MCSConnectorClient
|
|
14
|
+
|
|
12
15
|
__all__ = [
|
|
13
16
|
"ConnectorClient",
|
|
14
17
|
"UserTokenClient",
|
|
15
18
|
"UserTokenClientBase",
|
|
16
19
|
"TeamsConnectorClient",
|
|
20
|
+
"MCSConnectorClient",
|
|
17
21
|
"ConnectorClientBase",
|
|
18
22
|
"get_product_info",
|
|
19
23
|
]
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
|
|
4
|
+
"""MCS Connector Client for Microsoft Copilot Studio via Power Apps Connector."""
|
|
5
|
+
|
|
6
|
+
import logging
|
|
7
|
+
from typing import Optional
|
|
8
|
+
from aiohttp import ClientSession
|
|
9
|
+
|
|
10
|
+
from microsoft_agents.activity import Activity, ResourceResponse
|
|
11
|
+
from ..connector_client_base import ConnectorClientBase
|
|
12
|
+
from ..attachments_base import AttachmentsBase
|
|
13
|
+
from ..conversations_base import ConversationsBase
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class MCSConversations(ConversationsBase):
|
|
19
|
+
"""
|
|
20
|
+
Conversations implementation for Microsoft Copilot Studio Connector.
|
|
21
|
+
|
|
22
|
+
Only supports SendToConversation and ReplyToActivity operations.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(self, client: ClientSession, endpoint: str):
|
|
26
|
+
self._client = client
|
|
27
|
+
self._endpoint = endpoint
|
|
28
|
+
|
|
29
|
+
async def send_to_conversation(
|
|
30
|
+
self,
|
|
31
|
+
conversation_id: str,
|
|
32
|
+
activity: Activity,
|
|
33
|
+
**kwargs,
|
|
34
|
+
) -> ResourceResponse:
|
|
35
|
+
"""
|
|
36
|
+
Send an activity to a conversation.
|
|
37
|
+
|
|
38
|
+
:param conversation_id: The conversation ID (not used for MCS connector).
|
|
39
|
+
:param activity: The activity to send.
|
|
40
|
+
:return: A resource response.
|
|
41
|
+
"""
|
|
42
|
+
if activity is None:
|
|
43
|
+
raise ValueError("activity is required")
|
|
44
|
+
|
|
45
|
+
logger.info(
|
|
46
|
+
"MCS Connector: Sending activity to conversation: %s. Activity type is %s",
|
|
47
|
+
conversation_id,
|
|
48
|
+
activity.type,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
async with self._client.post(
|
|
52
|
+
self._endpoint,
|
|
53
|
+
json=activity.model_dump(by_alias=True, exclude_unset=True, mode="json"),
|
|
54
|
+
headers={"Accept": "application/json", "Content-Type": "application/json"},
|
|
55
|
+
) as response:
|
|
56
|
+
if response.status >= 300:
|
|
57
|
+
logger.error(
|
|
58
|
+
"MCS Connector: Error sending activity: %s",
|
|
59
|
+
response.status,
|
|
60
|
+
stack_info=True,
|
|
61
|
+
)
|
|
62
|
+
response.raise_for_status()
|
|
63
|
+
|
|
64
|
+
data = await response.json()
|
|
65
|
+
return ResourceResponse.model_validate(data)
|
|
66
|
+
|
|
67
|
+
async def reply_to_activity(
|
|
68
|
+
self,
|
|
69
|
+
conversation_id: str,
|
|
70
|
+
activity_id: str,
|
|
71
|
+
activity: Activity,
|
|
72
|
+
**kwargs,
|
|
73
|
+
) -> ResourceResponse:
|
|
74
|
+
"""
|
|
75
|
+
Reply to an activity in a conversation.
|
|
76
|
+
|
|
77
|
+
For MCS Connector, this falls back to send_to_conversation.
|
|
78
|
+
|
|
79
|
+
:param conversation_id: The conversation ID.
|
|
80
|
+
:param activity_id: The activity ID to reply to.
|
|
81
|
+
:param activity: The activity to send.
|
|
82
|
+
:return: A resource response.
|
|
83
|
+
"""
|
|
84
|
+
return await self.send_to_conversation(conversation_id, activity, **kwargs)
|
|
85
|
+
|
|
86
|
+
async def update_activity(
|
|
87
|
+
self,
|
|
88
|
+
conversation_id: str,
|
|
89
|
+
activity_id: str,
|
|
90
|
+
activity: Activity,
|
|
91
|
+
**kwargs,
|
|
92
|
+
) -> ResourceResponse:
|
|
93
|
+
"""Not supported for MCS Connector."""
|
|
94
|
+
raise NotImplementedError(
|
|
95
|
+
"UpdateActivity is not supported for Microsoft Copilot Studio Connector"
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
async def delete_activity(
|
|
99
|
+
self, conversation_id: str, activity_id: str, **kwargs
|
|
100
|
+
) -> None:
|
|
101
|
+
"""Not supported for MCS Connector."""
|
|
102
|
+
raise NotImplementedError(
|
|
103
|
+
"DeleteActivity is not supported for Microsoft Copilot Studio Connector"
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
async def get_conversation_member(
|
|
107
|
+
self, conversation_id: str, member_id: str, **kwargs
|
|
108
|
+
) -> list:
|
|
109
|
+
"""Not supported for MCS Connector."""
|
|
110
|
+
raise NotImplementedError(
|
|
111
|
+
"GetConversationMember is not supported for Microsoft Copilot Studio Connector"
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
async def get_conversation_members(self, conversation_id: str, **kwargs) -> list:
|
|
115
|
+
"""Not supported for MCS Connector."""
|
|
116
|
+
raise NotImplementedError(
|
|
117
|
+
"GetConversationMembers is not supported for Microsoft Copilot Studio Connector"
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
async def get_activity_members(
|
|
121
|
+
self, conversation_id: str, activity_id: str, **kwargs
|
|
122
|
+
) -> list:
|
|
123
|
+
"""Not supported for MCS Connector."""
|
|
124
|
+
raise NotImplementedError(
|
|
125
|
+
"GetActivityMembers is not supported for Microsoft Copilot Studio Connector"
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
async def delete_conversation_member(
|
|
129
|
+
self, conversation_id: str, member_id: str, **kwargs
|
|
130
|
+
) -> None:
|
|
131
|
+
"""Not supported for MCS Connector."""
|
|
132
|
+
raise NotImplementedError(
|
|
133
|
+
"DeleteConversationMember is not supported for Microsoft Copilot Studio Connector"
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
async def send_conversation_history(
|
|
137
|
+
self, conversation_id: str, transcript: dict, **kwargs
|
|
138
|
+
) -> ResourceResponse:
|
|
139
|
+
"""Not supported for MCS Connector."""
|
|
140
|
+
raise NotImplementedError(
|
|
141
|
+
"SendConversationHistory is not supported for Microsoft Copilot Studio Connector"
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
async def upload_attachment(
|
|
145
|
+
self, conversation_id: str, attachment_upload: dict, **kwargs
|
|
146
|
+
) -> ResourceResponse:
|
|
147
|
+
"""Not supported for MCS Connector."""
|
|
148
|
+
raise NotImplementedError(
|
|
149
|
+
"UploadAttachment is not supported for Microsoft Copilot Studio Connector"
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
async def create_conversation(self, parameters: dict, **kwargs) -> dict:
|
|
153
|
+
"""Not supported for MCS Connector."""
|
|
154
|
+
raise NotImplementedError(
|
|
155
|
+
"CreateConversation is not supported for Microsoft Copilot Studio Connector"
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
async def get_conversations(
|
|
159
|
+
self, continuation_token: Optional[str] = None, **kwargs
|
|
160
|
+
) -> dict:
|
|
161
|
+
"""Not supported for MCS Connector."""
|
|
162
|
+
raise NotImplementedError(
|
|
163
|
+
"GetConversations is not supported for Microsoft Copilot Studio Connector"
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
async def get_conversation_paged_members(
|
|
167
|
+
self,
|
|
168
|
+
conversation_id: str,
|
|
169
|
+
page_size: Optional[int] = None,
|
|
170
|
+
continuation_token: Optional[str] = None,
|
|
171
|
+
**kwargs,
|
|
172
|
+
) -> dict:
|
|
173
|
+
"""Not supported for MCS Connector."""
|
|
174
|
+
raise NotImplementedError(
|
|
175
|
+
"GetConversationPagedMembers is not supported for Microsoft Copilot Studio Connector"
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
class MCSAttachments(AttachmentsBase):
|
|
180
|
+
"""
|
|
181
|
+
Attachments implementation for Microsoft Copilot Studio Connector.
|
|
182
|
+
|
|
183
|
+
Attachments operations are not supported.
|
|
184
|
+
"""
|
|
185
|
+
|
|
186
|
+
async def get_attachment_info(self, attachment_id: str, **kwargs) -> dict:
|
|
187
|
+
"""Not supported for MCS Connector."""
|
|
188
|
+
raise NotImplementedError(
|
|
189
|
+
"GetAttachmentInfo is not supported for Microsoft Copilot Studio Connector"
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
async def get_attachment(self, attachment_id: str, *args, **kwargs) -> bytes:
|
|
193
|
+
"""Not supported for MCS Connector."""
|
|
194
|
+
raise NotImplementedError(
|
|
195
|
+
"GetAttachment is not supported for Microsoft Copilot Studio Connector"
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
class MCSConnectorClient(ConnectorClientBase):
|
|
200
|
+
"""
|
|
201
|
+
Connector client suited for communicating with Microsoft Copilot Studio
|
|
202
|
+
via a Power Apps Connector request.
|
|
203
|
+
|
|
204
|
+
Only supports SendToConversation and ReplyToActivity operations.
|
|
205
|
+
All other operations will raise NotImplementedError.
|
|
206
|
+
"""
|
|
207
|
+
|
|
208
|
+
def __init__(self, endpoint: str, client: Optional[ClientSession] = None):
|
|
209
|
+
"""
|
|
210
|
+
Initialize the MCS Connector Client.
|
|
211
|
+
|
|
212
|
+
:param endpoint: The endpoint URL for the MCS connector.
|
|
213
|
+
:param client: Optional aiohttp ClientSession. If not provided, one will be created.
|
|
214
|
+
"""
|
|
215
|
+
if not endpoint:
|
|
216
|
+
raise ValueError("endpoint is required")
|
|
217
|
+
|
|
218
|
+
self._endpoint = endpoint
|
|
219
|
+
self._client = client or ClientSession()
|
|
220
|
+
self._conversations = MCSConversations(self._client, self._endpoint)
|
|
221
|
+
self._attachments = MCSAttachments()
|
|
222
|
+
|
|
223
|
+
@property
|
|
224
|
+
def base_uri(self) -> str:
|
|
225
|
+
"""Get the base URI for this connector client."""
|
|
226
|
+
return self._endpoint
|
|
227
|
+
|
|
228
|
+
@property
|
|
229
|
+
def attachments(self) -> AttachmentsBase:
|
|
230
|
+
"""Get the attachments operations (not supported for MCS)."""
|
|
231
|
+
return self._attachments
|
|
232
|
+
|
|
233
|
+
@property
|
|
234
|
+
def conversations(self) -> ConversationsBase:
|
|
235
|
+
"""Get the conversations operations."""
|
|
236
|
+
return self._conversations
|
|
237
|
+
|
|
238
|
+
async def close(self):
|
|
239
|
+
"""Close the client session."""
|
|
240
|
+
if self._client:
|
|
241
|
+
await self._client.close()
|
|
242
|
+
|
|
243
|
+
async def __aenter__(self):
|
|
244
|
+
"""Async context manager entry."""
|
|
245
|
+
return self
|
|
246
|
+
|
|
247
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
248
|
+
"""Async context manager exit."""
|
|
249
|
+
await self.close()
|
|
@@ -112,6 +112,21 @@ class ErrorResources:
|
|
|
112
112
|
-63017,
|
|
113
113
|
)
|
|
114
114
|
|
|
115
|
+
UnexpectedConnectorRequestToken = ErrorMessage(
|
|
116
|
+
"Connector request did not contain a valid security token for handler: {0}",
|
|
117
|
+
-63018,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
UnexpectedConnectorTokenExpiration = ErrorMessage(
|
|
121
|
+
"Connector token has expired for handler: {0}",
|
|
122
|
+
-63019,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
OboNotExchangeableToken = ErrorMessage(
|
|
126
|
+
"The provided token is not exchangeable for handler: {0}",
|
|
127
|
+
-63020,
|
|
128
|
+
)
|
|
129
|
+
|
|
115
130
|
# General/Validation Errors (-66000 to -66999)
|
|
116
131
|
InvalidConfiguration = ErrorMessage(
|
|
117
132
|
"Invalid configuration: {0}",
|
|
@@ -87,15 +87,12 @@ class HttpAdapterBase(ChannelServiceAdapter, ABC):
|
|
|
87
87
|
if request.method != "POST":
|
|
88
88
|
return HttpResponseFactory.method_not_allowed()
|
|
89
89
|
|
|
90
|
-
# Deserialize the incoming Activity
|
|
91
|
-
content_type = request.headers.get("Content-Type", "")
|
|
92
|
-
if "application/json" not in content_type:
|
|
93
|
-
return HttpResponseFactory.unsupported_media_type()
|
|
94
|
-
|
|
95
90
|
try:
|
|
96
91
|
body = await request.json()
|
|
97
92
|
except Exception:
|
|
98
|
-
return HttpResponseFactory.bad_request(
|
|
93
|
+
return HttpResponseFactory.bad_request(
|
|
94
|
+
"Invalid JSON or unsupported Content-Type"
|
|
95
|
+
)
|
|
99
96
|
|
|
100
97
|
activity: Activity = Activity.model_validate(body)
|
|
101
98
|
|
|
@@ -15,6 +15,7 @@ from microsoft_agents.hosting.core.authorization import AccessTokenProviderBase
|
|
|
15
15
|
from microsoft_agents.hosting.core.connector import ConnectorClientBase
|
|
16
16
|
from microsoft_agents.hosting.core.connector.client import UserTokenClient
|
|
17
17
|
from microsoft_agents.hosting.core.connector.teams import TeamsConnectorClient
|
|
18
|
+
from microsoft_agents.hosting.core.connector.mcs import MCSConnectorClient
|
|
18
19
|
|
|
19
20
|
from .channel_service_client_factory_base import ChannelServiceClientFactoryBase
|
|
20
21
|
from .turn_context import TurnContext
|
|
@@ -66,12 +67,15 @@ class RestChannelServiceClientFactory(ChannelServiceClientFactoryBase):
|
|
|
66
67
|
raise ValueError("Agent instance ID is required for agentic identity role")
|
|
67
68
|
|
|
68
69
|
if context.activity.recipient.role == RoleTypes.agentic_identity:
|
|
69
|
-
token, _ = await connection.get_agentic_instance_token(
|
|
70
|
+
token, _ = await connection.get_agentic_instance_token(
|
|
71
|
+
context.activity.get_agentic_tenant_id(), agent_instance_id
|
|
72
|
+
)
|
|
70
73
|
else:
|
|
71
74
|
agentic_user = context.activity.get_agentic_user()
|
|
72
75
|
if not agentic_user:
|
|
73
76
|
raise ValueError("Agentic user is required for agentic user role")
|
|
74
77
|
token = await connection.get_agentic_user_token(
|
|
78
|
+
context.activity.get_agentic_tenant_id(),
|
|
75
79
|
agent_instance_id,
|
|
76
80
|
agentic_user,
|
|
77
81
|
[AuthenticationConstants.APX_PRODUCTION_SCOPE],
|
|
@@ -113,7 +117,17 @@ class RestChannelServiceClientFactory(ChannelServiceClientFactoryBase):
|
|
|
113
117
|
)
|
|
114
118
|
|
|
115
119
|
token = await token_provider.get_access_token(
|
|
116
|
-
audience, scopes or
|
|
120
|
+
audience, scopes or claims_identity.get_token_scope()
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
# Check if this is a connector request (e.g., from Copilot Studio)
|
|
124
|
+
if (
|
|
125
|
+
context
|
|
126
|
+
and context.activity.recipient
|
|
127
|
+
and context.activity.recipient.role == RoleTypes.connector_user
|
|
128
|
+
) or service_url.startswith("https://pvaruntime"):
|
|
129
|
+
return MCSConnectorClient(
|
|
130
|
+
endpoint=service_url,
|
|
117
131
|
)
|
|
118
132
|
|
|
119
133
|
return TeamsConnectorClient(
|
|
@@ -142,7 +156,7 @@ class RestChannelServiceClientFactory(ChannelServiceClientFactoryBase):
|
|
|
142
156
|
if context.activity.is_agentic_request():
|
|
143
157
|
token = await self._get_agentic_token(context, self._token_service_endpoint)
|
|
144
158
|
else:
|
|
145
|
-
scopes =
|
|
159
|
+
scopes = claims_identity.get_token_scope()
|
|
146
160
|
|
|
147
161
|
token_provider = self._connection_manager.get_token_provider(
|
|
148
162
|
claims_identity, self._token_service_endpoint
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: microsoft-agents-hosting-core
|
|
3
|
-
Version: 0.7.0.
|
|
3
|
+
Version: 0.7.0.dev16
|
|
4
4
|
Summary: Core library for Microsoft Agents
|
|
5
5
|
Author: Microsoft Corporation
|
|
6
6
|
License-Expression: MIT
|
|
@@ -15,7 +15,7 @@ Classifier: Operating System :: OS Independent
|
|
|
15
15
|
Requires-Python: >=3.10
|
|
16
16
|
Description-Content-Type: text/markdown
|
|
17
17
|
License-File: LICENSE
|
|
18
|
-
Requires-Dist: microsoft-agents-activity==0.7.0.
|
|
18
|
+
Requires-Dist: microsoft-agents-activity==0.7.0.dev16
|
|
19
19
|
Requires-Dist: pyjwt>=2.10.1
|
|
20
20
|
Requires-Dist: isodate>=0.6.1
|
|
21
21
|
Requires-Dist: azure-core>=1.30.0
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
microsoft_agents/hosting/core/__init__.py,sha256=
|
|
1
|
+
microsoft_agents/hosting/core/__init__.py,sha256=bpUnDxIdWHzcta50KQ0GVul9b7Y5-o4VxFJ20CQm4hQ,5458
|
|
2
2
|
microsoft_agents/hosting/core/activity_handler.py,sha256=1hsSmVCnQLS44RK05v8j6mlmV38_JGmJTPR9LkogQuc,27779
|
|
3
3
|
microsoft_agents/hosting/core/agent.py,sha256=K8v84y8ULP7rbcMKg8LxaM3haAq7f1oHFCLy3AAphQE,574
|
|
4
4
|
microsoft_agents/hosting/core/card_factory.py,sha256=UDmPEpOk2SpEr9ShN9Q0CiaI_GTD3qjHgkDMOWinW9I,6926
|
|
5
5
|
microsoft_agents/hosting/core/channel_adapter.py,sha256=MqES9gHGS0nrKBR7u8zLQCsuIksl-hScZy_jg4LTTHo,10669
|
|
6
6
|
microsoft_agents/hosting/core/channel_api_handler_protocol.py,sha256=RO59hiOYx7flQVWhX6VGvfpFPoKVkdT_la-7WhUl8UM,4506
|
|
7
|
-
microsoft_agents/hosting/core/channel_service_adapter.py,sha256=
|
|
7
|
+
microsoft_agents/hosting/core/channel_service_adapter.py,sha256=9wUrCGLO4WiHt_NLrSwUShHQAHxNsIefndOXZxKZu8M,21474
|
|
8
8
|
microsoft_agents/hosting/core/channel_service_client_factory_base.py,sha256=9LcpxiWMXmWAdGQt6lRzvY9ybF_5mGc5Focd1Z7f9MY,1711
|
|
9
9
|
microsoft_agents/hosting/core/message_factory.py,sha256=F9QJBF4yBupHXxOW984ZzZomVEG57t9IUnTHwub-lX0,7822
|
|
10
10
|
microsoft_agents/hosting/core/middleware_set.py,sha256=TBsBs4KwAfKyHlQTlG4bl1y5UjkBzeMDs5w7LNB-Bi4,2585
|
|
11
|
-
microsoft_agents/hosting/core/rest_channel_service_client_factory.py,sha256=
|
|
11
|
+
microsoft_agents/hosting/core/rest_channel_service_client_factory.py,sha256=yk_UhFiXm_-shkQYo731JHNGi4fElfiRshsHo1-bgWI,6789
|
|
12
12
|
microsoft_agents/hosting/core/turn_context.py,sha256=muA8S4R6Xxja5it7DiFqr5J5zmttwDN-Mj5_SDdxZ4A,14874
|
|
13
13
|
microsoft_agents/hosting/core/_oauth/__init__.py,sha256=sU1HsIXbETRYwnudtFc6GrNbM6C3oYjmruqXc6kIAFw,405
|
|
14
14
|
microsoft_agents/hosting/core/_oauth/_flow_state.py,sha256=BQbXn0a3Fw4aozS-WSjL0Y7vEdb4eua1ZitSr0qZ6bE,2207
|
|
@@ -16,7 +16,7 @@ microsoft_agents/hosting/core/_oauth/_flow_storage_client.py,sha256=1MLD8m_qw0jS
|
|
|
16
16
|
microsoft_agents/hosting/core/_oauth/_oauth_flow.py,sha256=vgg_sQLYr83YA0F7aQ1K5j8vEATw7ZS151ODkpCGYAM,12211
|
|
17
17
|
microsoft_agents/hosting/core/app/__init__.py,sha256=GZQdog7BBMA8uD9iaRMNeRJf12rJZB3bAKFAD8Y8dVo,1228
|
|
18
18
|
microsoft_agents/hosting/core/app/_type_defs.py,sha256=b5VZFWnQ5ONUZMtFxw7Q-mbbxIUSJ7RXcCXjqdHwSQw,438
|
|
19
|
-
microsoft_agents/hosting/core/app/agent_application.py,sha256=
|
|
19
|
+
microsoft_agents/hosting/core/app/agent_application.py,sha256=24WjroVgiHavWdJTsfyOgQTjRR8H-jF98JVLfkwWcXo,33804
|
|
20
20
|
microsoft_agents/hosting/core/app/app_error.py,sha256=JgYBgv2EKe9Q2TFi5FeG_RneulBEa5SyBpWSF-duBzE,301
|
|
21
21
|
microsoft_agents/hosting/core/app/app_options.py,sha256=P3XTFFuQCnEcHixfdmr5RPG-Wli4c1VSmCMRKVBBLsw,2792
|
|
22
22
|
microsoft_agents/hosting/core/app/input_file.py,sha256=AEzVAnAPO1V7MWDI_uoZfcYY8Oog3XgvEpAeO-tATeY,1546
|
|
@@ -26,15 +26,16 @@ microsoft_agents/hosting/core/app/_routes/__init__.py,sha256=faz05Hk5Z1IGIXUwXlj
|
|
|
26
26
|
microsoft_agents/hosting/core/app/_routes/_route.py,sha256=gs5XVi74H1wzDShpZd9ZXVDrREjricFYVPz10XrK06c,2669
|
|
27
27
|
microsoft_agents/hosting/core/app/_routes/_route_list.py,sha256=fe1-wmFaJJqYkWFcbNhJ9OMH954TneQR0-uoxyKQVlU,874
|
|
28
28
|
microsoft_agents/hosting/core/app/_routes/route_rank.py,sha256=-UwmLMCtgLT2cWnWvLfE2puV0RuQfI2m3WyEvkBkSBg,324
|
|
29
|
-
microsoft_agents/hosting/core/app/oauth/__init__.py,sha256=
|
|
29
|
+
microsoft_agents/hosting/core/app/oauth/__init__.py,sha256=IBfDWU5JHl9KuPI2JnOfkoCpDtQM80EdeWqTxebZq8A,622
|
|
30
30
|
microsoft_agents/hosting/core/app/oauth/_sign_in_response.py,sha256=H5VBX69WiYXGALzCDVnF_-Y0brbafbJgfF4mS2wZSr0,851
|
|
31
31
|
microsoft_agents/hosting/core/app/oauth/_sign_in_state.py,sha256=gtrLa_BgQq-yrTu8ItVwQ8fntfSKjlng2lCj6A8mCrc,897
|
|
32
32
|
microsoft_agents/hosting/core/app/oauth/auth_handler.py,sha256=KyhEfpKtoVKcapOt4szpDdbgk_KSwXF46GGOeJrmsRI,3464
|
|
33
|
-
microsoft_agents/hosting/core/app/oauth/authorization.py,sha256=
|
|
34
|
-
microsoft_agents/hosting/core/app/oauth/_handlers/__init__.py,sha256=
|
|
33
|
+
microsoft_agents/hosting/core/app/oauth/authorization.py,sha256=gNiCYg9ETKczxwfxrg9UdxQxeig7c37jX9JzZ5gtagY,18761
|
|
34
|
+
microsoft_agents/hosting/core/app/oauth/_handlers/__init__.py,sha256=ZfUCRGb55A8RVNNKLKe0o_BkkvcOFgynxIrXmVfGuD4,479
|
|
35
35
|
microsoft_agents/hosting/core/app/oauth/_handlers/_authorization_handler.py,sha256=W36Y1m0zFaRmdt-cL3ZKoHxx95y0O7YJF9D0cXx_s24,4185
|
|
36
36
|
microsoft_agents/hosting/core/app/oauth/_handlers/_user_authorization.py,sha256=o_pn5ZO7O-YvcC5NGlUgcCmVLD-f6KwQ5CRIsPp8zQE,10186
|
|
37
|
-
microsoft_agents/hosting/core/app/oauth/_handlers/agentic_user_authorization.py,sha256=
|
|
37
|
+
microsoft_agents/hosting/core/app/oauth/_handlers/agentic_user_authorization.py,sha256=3B4Ph2L0VFE6cVPKgHSAuHnmPNfsgDjtjdAAmpjFDMs,7374
|
|
38
|
+
microsoft_agents/hosting/core/app/oauth/_handlers/connector_user_authorization.py,sha256=lnVD4xSjWKVX3gAi1Xux2xkdbui9jgTgFVDTlJojfFg,9387
|
|
38
39
|
microsoft_agents/hosting/core/app/state/__init__.py,sha256=aL_8GB7_de8LUSEOgTJQFRx1kvgvJHHJVv7nWAoRPmU,331
|
|
39
40
|
microsoft_agents/hosting/core/app/state/conversation_state.py,sha256=LfcSwvhaU0JeAahwg9YA9uz0kKHa9c6Y3XBAWqwuMg0,1593
|
|
40
41
|
microsoft_agents/hosting/core/app/state/state.py,sha256=mbNrHuAc7d9vJep3PtExbLFtfKxZoAUX3Jr45GT3SuA,7373
|
|
@@ -45,14 +46,14 @@ microsoft_agents/hosting/core/app/streaming/citation.py,sha256=ZGaMUOWxxoMplwRrk
|
|
|
45
46
|
microsoft_agents/hosting/core/app/streaming/citation_util.py,sha256=c95c3Y3genmFc0vSXppPaD1-ShFohAV1UABZnyJS_BQ,2478
|
|
46
47
|
microsoft_agents/hosting/core/app/streaming/streaming_response.py,sha256=uwNYaVJ8cghdjHS_Y8ZeOiYnpyeQGHt0rQQlay6a-C8,13799
|
|
47
48
|
microsoft_agents/hosting/core/authorization/__init__.py,sha256=pOTmTJFS5CMPEcHRadBTgrbWUP5lOIxsPMgTreFq7mw,634
|
|
48
|
-
microsoft_agents/hosting/core/authorization/access_token_provider_base.py,sha256=
|
|
49
|
-
microsoft_agents/hosting/core/authorization/agent_auth_configuration.py,sha256=
|
|
50
|
-
microsoft_agents/hosting/core/authorization/anonymous_token_provider.py,sha256=
|
|
49
|
+
microsoft_agents/hosting/core/authorization/access_token_provider_base.py,sha256=a3nyMsN539akjT3srdOmi0PShDJAJNXzcMpfFfzXj48,1727
|
|
50
|
+
microsoft_agents/hosting/core/authorization/agent_auth_configuration.py,sha256=JgyudvnJicU7pqdChnrv3hiULROKiQNdY4ZbNJ881fU,4150
|
|
51
|
+
microsoft_agents/hosting/core/authorization/anonymous_token_provider.py,sha256=N60FVEXCFJDSgwqJPQHZQYlbgVldzOIpwdn7aArj0dA,1157
|
|
51
52
|
microsoft_agents/hosting/core/authorization/auth_types.py,sha256=Fg_ywEItm3xL_DBUNzi0QsfDPVY5S3HlAiNw6I2SW64,374
|
|
52
53
|
microsoft_agents/hosting/core/authorization/authentication_constants.py,sha256=ABEwosDCMYhubDiGrD8QULboTkACqDvNp_x_XBPt6dQ,4618
|
|
53
|
-
microsoft_agents/hosting/core/authorization/claims_identity.py,sha256=
|
|
54
|
+
microsoft_agents/hosting/core/authorization/claims_identity.py,sha256=KPcWbrFmlGwv1T493asolyiERuxq6Y2l77o7oDzvTzs,3094
|
|
54
55
|
microsoft_agents/hosting/core/authorization/connections.py,sha256=f_NQpuJzhCBNZLk56SK4kvb_V_g2x2XMnFwWbYyDX_s,1247
|
|
55
|
-
microsoft_agents/hosting/core/authorization/jwt_token_validator.py,sha256=
|
|
56
|
+
microsoft_agents/hosting/core/authorization/jwt_token_validator.py,sha256=HQqs8pNVrysBWB9EgLbhVtm70rqrf7w5mh7DIGHCWWM,2063
|
|
56
57
|
microsoft_agents/hosting/core/client/__init__.py,sha256=HMWtSUXu1akaX6qtDx412Lr8vLi5SEslc-DYrvZAPWs,1299
|
|
57
58
|
microsoft_agents/hosting/core/client/agent_conversation_reference.py,sha256=3Zf3IGbJVWv4pF6kA6vtWkpV3Ap3z6jVkimeEtFMvJI,288
|
|
58
59
|
microsoft_agents/hosting/core/client/channel_factory_protocol.py,sha256=r270WJwySugnzY4AITT8oSRQHKqeqYAknkmD2eq_CqA,394
|
|
@@ -67,7 +68,7 @@ microsoft_agents/hosting/core/client/conversation_id_factory_options.py,sha256=r
|
|
|
67
68
|
microsoft_agents/hosting/core/client/conversation_id_factory_protocol.py,sha256=fjQawuVwiQm-pdDMDZBUIdlnKyPe1gg-0tN3fNxrMr8,1323
|
|
68
69
|
microsoft_agents/hosting/core/client/http_agent_channel.py,sha256=2D2sjw8HtZvp_43xDjTLXp6_DLNrXziYhNmob14RwCM,3634
|
|
69
70
|
microsoft_agents/hosting/core/client/http_agent_channel_factory.py,sha256=ESY2EJwZMdSAa5I06pFHc89vO5Lflp86O6wJAnfz2qI,523
|
|
70
|
-
microsoft_agents/hosting/core/connector/__init__.py,sha256=
|
|
71
|
+
microsoft_agents/hosting/core/connector/__init__.py,sha256=Fn5SgrVhm7ZaolXHDy8qmVt7uYBRbS3H-svpzj7ZzVM,616
|
|
71
72
|
microsoft_agents/hosting/core/connector/agent_sign_in_base.py,sha256=Bk3Ssg3ilk1Ld2mSjsCYgfqvy6pq1us9Ja2j4lF7CTc,647
|
|
72
73
|
microsoft_agents/hosting/core/connector/attachments_base.py,sha256=eqr5dbrc7oE77xeFfr-5UIi4gkDvOp5zWbZ02Qb4IDI,507
|
|
73
74
|
microsoft_agents/hosting/core/connector/connector_client_base.py,sha256=DKog6C1Z8V9r_Lp-IvXUiAbJN_iJMFary5FvejjHlaw,583
|
|
@@ -76,15 +77,17 @@ microsoft_agents/hosting/core/connector/get_product_info.py,sha256=SDxPqBCzzQLEU
|
|
|
76
77
|
microsoft_agents/hosting/core/connector/user_token_base.py,sha256=h_l5D1SghN2RrUkFcKWQhCHlO9r7akMbzsm2x8MvomI,3639
|
|
77
78
|
microsoft_agents/hosting/core/connector/user_token_client_base.py,sha256=dfUTUsBNOzWze9_JldAeLe73sydSDzlKyMKMvj48g2E,471
|
|
78
79
|
microsoft_agents/hosting/core/connector/client/__init__.py,sha256=6JdKhmm7btmo0omxMBd8PJbtGFk0cnMwVUoStyW7Ft0,143
|
|
79
|
-
microsoft_agents/hosting/core/connector/client/connector_client.py,sha256=
|
|
80
|
-
microsoft_agents/hosting/core/connector/client/user_token_client.py,sha256=
|
|
80
|
+
microsoft_agents/hosting/core/connector/client/connector_client.py,sha256=S0JzWkOCgsVKLUi0KGplXT0SCbQ0HEahhTe3ex2iO_E,23865
|
|
81
|
+
microsoft_agents/hosting/core/connector/client/user_token_client.py,sha256=rtQ5GivBhCOpDeUkAdI0_R5c0RE-FbLxzTttopxOH2o,10535
|
|
82
|
+
microsoft_agents/hosting/core/connector/mcs/__init__.py,sha256=fXHQt7WCkJ4bio5h7IAhH2vP6VE8WBf9IOeHwBnT0cA,87
|
|
83
|
+
microsoft_agents/hosting/core/connector/mcs/mcs_connector_client.py,sha256=MAmgkbbzh7pPG92YwaCp3QZTfWd7KvyKCNaTiisfrlA,8517
|
|
81
84
|
microsoft_agents/hosting/core/connector/teams/__init__.py,sha256=3ZMPGYyZ15EwvfQzfJJQy1J58oIt4InSxibl3BN6R54,100
|
|
82
85
|
microsoft_agents/hosting/core/connector/teams/teams_connector_client.py,sha256=XGQDTYHrA_I9n9JlxGST5eesjsFhz2dnSaMSuyoFnKU,12676
|
|
83
86
|
microsoft_agents/hosting/core/errors/__init__.py,sha256=kcfwDGDaopWfLyvYsNXM6j4cYt2oGKysjCRDezcVY0A,500
|
|
84
|
-
microsoft_agents/hosting/core/errors/error_resources.py,sha256=
|
|
87
|
+
microsoft_agents/hosting/core/errors/error_resources.py,sha256=A8UdgOsM55XJFlscfkHjGQiXpw0C-OTT7HloN_H8r1U,4350
|
|
85
88
|
microsoft_agents/hosting/core/http/__init__.py,sha256=Jh6lIivBN9YsNaLPzYvzvbhLAwJbeQTSRHtFupfSy-I,532
|
|
86
89
|
microsoft_agents/hosting/core/http/_channel_service_routes.py,sha256=80IWoUDKywUurXnh7vmG0QMxu3sk7W38zgHsw0eJ2DA,8436
|
|
87
|
-
microsoft_agents/hosting/core/http/_http_adapter_base.py,sha256=
|
|
90
|
+
microsoft_agents/hosting/core/http/_http_adapter_base.py,sha256=L8cPsPluPOUVu42aXFq4hugxbJ3gDzSALQgDvb9-2bA,4396
|
|
88
91
|
microsoft_agents/hosting/core/http/_http_request_protocol.py,sha256=YTfcp_K1IswAIWVQ5QFBTyX-ssuUo6BChdlPHEh-t4Q,1002
|
|
89
92
|
microsoft_agents/hosting/core/http/_http_response.py,sha256=e_bY0bJJXx--Zb_Yd-f9iDPFbK4bWkFi92a0nq1A6us,1862
|
|
90
93
|
microsoft_agents/hosting/core/state/__init__.py,sha256=yckKi1wg_86ng-DL9Q3R49QiWKvNjPkVNk6HClWgVrY,208
|
|
@@ -94,16 +97,16 @@ microsoft_agents/hosting/core/state/user_state.py,sha256=zEigX-sroNAyoQAxQjG1Ogm
|
|
|
94
97
|
microsoft_agents/hosting/core/storage/__init__.py,sha256=Df_clI0uMRgcr4Td-xkP83bU_mGae7_gRMhtVDPZDmE,729
|
|
95
98
|
microsoft_agents/hosting/core/storage/_type_aliases.py,sha256=VCKtjiCBrhEsGSm3zVVSSccdoiY02GYhABvrLjhAcz8,72
|
|
96
99
|
microsoft_agents/hosting/core/storage/error_handling.py,sha256=kTMQ68GxL4GgVKpo3SexzyRARLw6UbsVhHU723XxIck,1360
|
|
97
|
-
microsoft_agents/hosting/core/storage/memory_storage.py,sha256=
|
|
98
|
-
microsoft_agents/hosting/core/storage/storage.py,sha256=
|
|
100
|
+
microsoft_agents/hosting/core/storage/memory_storage.py,sha256=VUKFWc6aAfUzwfH_hiPvFsbESGY78qCCTkBnpoNZUe4,2447
|
|
101
|
+
microsoft_agents/hosting/core/storage/storage.py,sha256=zHnQNaLYgzjLP_XWfL3e-Dlw7eQbNgXUYbnT_26rYvM,3321
|
|
99
102
|
microsoft_agents/hosting/core/storage/store_item.py,sha256=rjtzB5yufsKuY1O5PjCqWHjjmO6UiORwkzpwbsaxp_4,371
|
|
100
103
|
microsoft_agents/hosting/core/storage/transcript_file_store.py,sha256=eJdcU6BgMbvFYunWul2CZURH762HmUr11iUN7NgvZek,10045
|
|
101
104
|
microsoft_agents/hosting/core/storage/transcript_info.py,sha256=5VN32j99tshChAffvuZ6D3GH3ABCZsQGHC_bYDAwFOk,328
|
|
102
|
-
microsoft_agents/hosting/core/storage/transcript_logger.py,sha256=
|
|
105
|
+
microsoft_agents/hosting/core/storage/transcript_logger.py,sha256=rgFeghzyRd9M3IUtI9n5y_rdAOL9s6qgGVD4OTOjkEg,8236
|
|
103
106
|
microsoft_agents/hosting/core/storage/transcript_memory_store.py,sha256=v1Ud9LSs8m5c9_Fa8i49SuAjw80dX1hDciqbRduDEOE,6444
|
|
104
107
|
microsoft_agents/hosting/core/storage/transcript_store.py,sha256=ka74o0WvI5GhMZcFqSxVdamBhGzZcDZe6VNkG-sMy74,1944
|
|
105
|
-
microsoft_agents_hosting_core-0.7.0.
|
|
106
|
-
microsoft_agents_hosting_core-0.7.0.
|
|
107
|
-
microsoft_agents_hosting_core-0.7.0.
|
|
108
|
-
microsoft_agents_hosting_core-0.7.0.
|
|
109
|
-
microsoft_agents_hosting_core-0.7.0.
|
|
108
|
+
microsoft_agents_hosting_core-0.7.0.dev16.dist-info/licenses/LICENSE,sha256=ws_MuBL-SCEBqPBFl9_FqZkaaydIJmxHrJG2parhU4M,1141
|
|
109
|
+
microsoft_agents_hosting_core-0.7.0.dev16.dist-info/METADATA,sha256=wHXiJtG1nuxuYpXFlY_ze9P3fnaUND2SEnnhJ8g8h6Y,9821
|
|
110
|
+
microsoft_agents_hosting_core-0.7.0.dev16.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
111
|
+
microsoft_agents_hosting_core-0.7.0.dev16.dist-info/top_level.txt,sha256=lWKcT4v6fTA_NgsuHdNvuMjSrkiBMXohn64ApY7Xi8A,17
|
|
112
|
+
microsoft_agents_hosting_core-0.7.0.dev16.dist-info/RECORD,,
|
|
File without changes
|