microsoft-agents-hosting-core 0.6.1__py3-none-any.whl → 0.7.0__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.
Files changed (24) hide show
  1. microsoft_agents/hosting/core/__init__.py +24 -1
  2. microsoft_agents/hosting/core/app/agent_application.py +12 -24
  3. microsoft_agents/hosting/core/app/streaming/__init__.py +14 -0
  4. microsoft_agents/hosting/core/app/streaming/citation.py +22 -0
  5. microsoft_agents/hosting/core/app/streaming/citation_util.py +85 -0
  6. microsoft_agents/hosting/core/app/streaming/streaming_response.py +411 -0
  7. microsoft_agents/hosting/core/authorization/claims_identity.py +14 -0
  8. microsoft_agents/hosting/core/channel_service_adapter.py +28 -14
  9. microsoft_agents/hosting/core/connector/client/connector_client.py +0 -1
  10. microsoft_agents/hosting/core/connector/client/user_token_client.py +0 -1
  11. microsoft_agents/hosting/core/http/__init__.py +17 -0
  12. microsoft_agents/hosting/core/http/_channel_service_routes.py +202 -0
  13. microsoft_agents/hosting/core/http/_http_adapter_base.py +136 -0
  14. microsoft_agents/hosting/core/http/_http_request_protocol.py +36 -0
  15. microsoft_agents/hosting/core/http/_http_response.py +56 -0
  16. microsoft_agents/hosting/core/rest_channel_service_client_factory.py +2 -2
  17. microsoft_agents/hosting/core/storage/memory_storage.py +0 -1
  18. microsoft_agents/hosting/core/storage/storage.py +0 -1
  19. microsoft_agents/hosting/core/storage/transcript_logger.py +0 -1
  20. {microsoft_agents_hosting_core-0.6.1.dist-info → microsoft_agents_hosting_core-0.7.0.dist-info}/METADATA +21 -3
  21. {microsoft_agents_hosting_core-0.6.1.dist-info → microsoft_agents_hosting_core-0.7.0.dist-info}/RECORD +24 -15
  22. {microsoft_agents_hosting_core-0.6.1.dist-info → microsoft_agents_hosting_core-0.7.0.dist-info}/WHEEL +0 -0
  23. {microsoft_agents_hosting_core-0.6.1.dist-info → microsoft_agents_hosting_core-0.7.0.dist-info}/licenses/LICENSE +0 -0
  24. {microsoft_agents_hosting_core-0.6.1.dist-info → microsoft_agents_hosting_core-0.7.0.dist-info}/top_level.txt +0 -0
@@ -341,6 +341,19 @@ class ChannelServiceAdapter(ChannelAdapter, ABC):
341
341
  await connector_client.close()
342
342
  await user_token_client.close()
343
343
 
344
+ def _resolve_if_connector_client_is_needed(self, activity: Activity) -> bool:
345
+ """Determine if a connector client is needed based on the activity's delivery mode and service URL.
346
+
347
+ :param activity: The activity to evaluate.
348
+ :type activity: :class:`microsoft_agents.activity.Activity`
349
+ """
350
+ if activity.delivery_mode in [
351
+ DeliveryModes.expect_replies,
352
+ DeliveryModes.stream,
353
+ ]:
354
+ return False
355
+ return True
356
+
344
357
  async def process_activity(
345
358
  self,
346
359
  claims_identity: ClaimsIdentity,
@@ -368,16 +381,14 @@ class ChannelServiceAdapter(ChannelAdapter, ABC):
368
381
  If the task completes successfully, then an :class:`microsoft_agents.activity.InvokeResponse` is returned;
369
382
  otherwise, `None` is returned.
370
383
  """
371
- scopes: list[str] = None
384
+ scopes: list[str] = claims_identity.get_token_scope()
372
385
  outgoing_audience: str = None
373
386
 
374
387
  if claims_identity.is_agent_claim():
375
388
  outgoing_audience = claims_identity.get_token_audience()
376
- scopes = [f"{claims_identity.get_outgoing_app_id()}/.default"]
377
389
  activity.caller_id = f"{CallerIdConstants.agent_to_agent_prefix}{claims_identity.get_outgoing_app_id()}"
378
390
  else:
379
391
  outgoing_audience = AuthenticationConstants.AGENTS_SDK_SCOPE
380
- scopes = [f"{AuthenticationConstants.AGENTS_SDK_SCOPE}/.default"]
381
392
 
382
393
  use_anonymous_auth_callback = False
383
394
  if (
@@ -403,21 +414,24 @@ class ChannelServiceAdapter(ChannelAdapter, ABC):
403
414
  context.turn_state[self.USER_TOKEN_CLIENT_KEY] = user_token_client
404
415
 
405
416
  # Create the connector client to use for outbound requests.
406
- connector_client: ConnectorClient = (
407
- await self._channel_service_client_factory.create_connector_client(
408
- context,
409
- claims_identity,
410
- activity.service_url,
411
- outgoing_audience,
412
- scopes,
413
- use_anonymous_auth_callback,
417
+ connector_client: Optional[ConnectorClient] = None
418
+ if self._resolve_if_connector_client_is_needed(activity):
419
+ connector_client = (
420
+ await self._channel_service_client_factory.create_connector_client(
421
+ context,
422
+ claims_identity,
423
+ activity.service_url,
424
+ outgoing_audience,
425
+ scopes,
426
+ use_anonymous_auth_callback,
427
+ )
414
428
  )
415
- )
416
- context.turn_state[self._AGENT_CONNECTOR_CLIENT_KEY] = connector_client
429
+ context.turn_state[self._AGENT_CONNECTOR_CLIENT_KEY] = connector_client
417
430
 
418
431
  await self.run_pipeline(context, callback)
419
432
 
420
- await connector_client.close()
433
+ if connector_client:
434
+ await connector_client.close()
421
435
  await user_token_client.close()
422
436
 
423
437
  # If there are any results they will have been left on the TurnContext.
@@ -22,7 +22,6 @@ from ..attachments_base import AttachmentsBase
22
22
  from ..conversations_base import ConversationsBase
23
23
  from ..get_product_info import get_product_info
24
24
 
25
-
26
25
  logger = logging.getLogger(__name__)
27
26
 
28
27
 
@@ -18,7 +18,6 @@ from ..get_product_info import get_product_info
18
18
  from ..user_token_base import UserTokenBase
19
19
  from ..agent_sign_in_base import AgentSignInBase
20
20
 
21
-
22
21
  logger = logging.getLogger(__name__)
23
22
 
24
23
 
@@ -0,0 +1,17 @@
1
+ # Copyright (c) Microsoft Corporation. All rights reserved.
2
+ # Licensed under the MIT License.
3
+
4
+ """HTTP abstractions for framework-agnostic adapter implementations."""
5
+
6
+ from ._http_request_protocol import HttpRequestProtocol
7
+ from ._http_response import HttpResponse, HttpResponseFactory
8
+ from ._http_adapter_base import HttpAdapterBase
9
+ from ._channel_service_routes import ChannelServiceRoutes
10
+
11
+ __all__ = [
12
+ "HttpRequestProtocol",
13
+ "HttpResponse",
14
+ "HttpResponseFactory",
15
+ "HttpAdapterBase",
16
+ "ChannelServiceRoutes",
17
+ ]
@@ -0,0 +1,202 @@
1
+ # Copyright (c) Microsoft Corporation. All rights reserved.
2
+ # Licensed under the MIT License.
3
+
4
+ """Channel service route definitions (framework-agnostic logic)."""
5
+
6
+ from typing import Type, List, Union
7
+
8
+ from microsoft_agents.activity import (
9
+ AgentsModel,
10
+ Activity,
11
+ AttachmentData,
12
+ ConversationParameters,
13
+ Transcript,
14
+ )
15
+ from microsoft_agents.hosting.core import ChannelApiHandlerProtocol
16
+
17
+ from ._http_request_protocol import HttpRequestProtocol
18
+
19
+
20
+ class ChannelServiceRoutes:
21
+ """Defines the Channel Service API routes and their handlers.
22
+
23
+ This class provides framework-agnostic route logic that can be
24
+ adapted to different web frameworks (aiohttp, FastAPI, etc.).
25
+ """
26
+
27
+ def __init__(self, handler: ChannelApiHandlerProtocol, base_url: str = ""):
28
+ """Initialize channel service routes.
29
+
30
+ Args:
31
+ handler: The handler that implements the Channel API protocol.
32
+ base_url: Optional base URL prefix for all routes.
33
+ """
34
+ self.handler = handler
35
+ self.base_url = base_url
36
+
37
+ @staticmethod
38
+ async def deserialize_from_body(
39
+ request: HttpRequestProtocol, target_model: Type[AgentsModel]
40
+ ) -> AgentsModel:
41
+ """Deserialize request body to target model."""
42
+ content_type = request.headers.get("Content-Type", "")
43
+ if "application/json" not in content_type:
44
+ raise ValueError("Content-Type must be application/json")
45
+
46
+ body = await request.json()
47
+ return target_model.model_validate(body)
48
+
49
+ @staticmethod
50
+ def serialize_model(model_or_list: Union[AgentsModel, List[AgentsModel]]) -> dict:
51
+ """Serialize model or list of models to JSON-compatible dict."""
52
+ if isinstance(model_or_list, AgentsModel):
53
+ return model_or_list.model_dump(
54
+ mode="json", exclude_unset=True, by_alias=True
55
+ )
56
+ else:
57
+ return [
58
+ model.model_dump(mode="json", exclude_unset=True, by_alias=True)
59
+ for model in model_or_list
60
+ ]
61
+
62
+ # Route handler methods
63
+ async def send_to_conversation(self, request: HttpRequestProtocol) -> dict:
64
+ """Handle POST /v3/conversations/{conversation_id}/activities."""
65
+ activity = await self.deserialize_from_body(request, Activity)
66
+ conversation_id = request.get_path_param("conversation_id")
67
+ result = await self.handler.on_send_to_conversation(
68
+ request.get_claims_identity(),
69
+ conversation_id,
70
+ activity,
71
+ )
72
+ return self.serialize_model(result)
73
+
74
+ async def reply_to_activity(self, request: HttpRequestProtocol) -> dict:
75
+ """Handle POST /v3/conversations/{conversation_id}/activities/{activity_id}."""
76
+ activity = await self.deserialize_from_body(request, Activity)
77
+ conversation_id = request.get_path_param("conversation_id")
78
+ activity_id = request.get_path_param("activity_id")
79
+ result = await self.handler.on_reply_to_activity(
80
+ request.get_claims_identity(),
81
+ conversation_id,
82
+ activity_id,
83
+ activity,
84
+ )
85
+ return self.serialize_model(result)
86
+
87
+ async def update_activity(self, request: HttpRequestProtocol) -> dict:
88
+ """Handle PUT /v3/conversations/{conversation_id}/activities/{activity_id}."""
89
+ activity = await self.deserialize_from_body(request, Activity)
90
+ conversation_id = request.get_path_param("conversation_id")
91
+ activity_id = request.get_path_param("activity_id")
92
+ result = await self.handler.on_update_activity(
93
+ request.get_claims_identity(),
94
+ conversation_id,
95
+ activity_id,
96
+ activity,
97
+ )
98
+ return self.serialize_model(result)
99
+
100
+ async def delete_activity(self, request: HttpRequestProtocol) -> None:
101
+ """Handle DELETE /v3/conversations/{conversation_id}/activities/{activity_id}."""
102
+ conversation_id = request.get_path_param("conversation_id")
103
+ activity_id = request.get_path_param("activity_id")
104
+ await self.handler.on_delete_activity(
105
+ request.get_claims_identity(),
106
+ conversation_id,
107
+ activity_id,
108
+ )
109
+
110
+ async def get_activity_members(self, request: HttpRequestProtocol) -> dict:
111
+ """Handle GET /v3/conversations/{conversation_id}/activities/{activity_id}/members."""
112
+ conversation_id = request.get_path_param("conversation_id")
113
+ activity_id = request.get_path_param("activity_id")
114
+ result = await self.handler.on_get_activity_members(
115
+ request.get_claims_identity(),
116
+ conversation_id,
117
+ activity_id,
118
+ )
119
+ return self.serialize_model(result)
120
+
121
+ async def create_conversation(self, request: HttpRequestProtocol) -> dict:
122
+ """Handle POST /."""
123
+ conversation_parameters = await self.deserialize_from_body(
124
+ request, ConversationParameters
125
+ )
126
+ result = await self.handler.on_create_conversation(
127
+ request.get_claims_identity(), conversation_parameters
128
+ )
129
+ return self.serialize_model(result)
130
+
131
+ async def get_conversations(self, request: HttpRequestProtocol) -> dict:
132
+ """Handle GET /."""
133
+ # TODO: continuation token? conversation_id?
134
+ result = await self.handler.on_get_conversations(
135
+ request.get_claims_identity(), None
136
+ )
137
+ return self.serialize_model(result)
138
+
139
+ async def get_conversation_members(self, request: HttpRequestProtocol) -> dict:
140
+ """Handle GET /v3/conversations/{conversation_id}/members."""
141
+ conversation_id = request.get_path_param("conversation_id")
142
+ result = await self.handler.on_get_conversation_members(
143
+ request.get_claims_identity(),
144
+ conversation_id,
145
+ )
146
+ return self.serialize_model(result)
147
+
148
+ async def get_conversation_member(self, request: HttpRequestProtocol) -> dict:
149
+ """Handle GET /v3/conversations/{conversation_id}/members/{member_id}."""
150
+ conversation_id = request.get_path_param("conversation_id")
151
+ member_id = request.get_path_param("member_id")
152
+ result = await self.handler.on_get_conversation_member(
153
+ request.get_claims_identity(),
154
+ member_id,
155
+ conversation_id,
156
+ )
157
+ return self.serialize_model(result)
158
+
159
+ async def get_conversation_paged_members(
160
+ self, request: HttpRequestProtocol
161
+ ) -> dict:
162
+ """Handle GET /v3/conversations/{conversation_id}/pagedmembers."""
163
+ conversation_id = request.get_path_param("conversation_id")
164
+ # TODO: continuation token? page size?
165
+ result = await self.handler.on_get_conversation_paged_members(
166
+ request.get_claims_identity(),
167
+ conversation_id,
168
+ )
169
+ return self.serialize_model(result)
170
+
171
+ async def delete_conversation_member(self, request: HttpRequestProtocol) -> dict:
172
+ """Handle DELETE /v3/conversations/{conversation_id}/members/{member_id}."""
173
+ conversation_id = request.get_path_param("conversation_id")
174
+ member_id = request.get_path_param("member_id")
175
+ result = await self.handler.on_delete_conversation_member(
176
+ request.get_claims_identity(),
177
+ conversation_id,
178
+ member_id,
179
+ )
180
+ return self.serialize_model(result)
181
+
182
+ async def send_conversation_history(self, request: HttpRequestProtocol) -> dict:
183
+ """Handle POST /v3/conversations/{conversation_id}/activities/history."""
184
+ conversation_id = request.get_path_param("conversation_id")
185
+ transcript = await self.deserialize_from_body(request, Transcript)
186
+ result = await self.handler.on_send_conversation_history(
187
+ request.get_claims_identity(),
188
+ conversation_id,
189
+ transcript,
190
+ )
191
+ return self.serialize_model(result)
192
+
193
+ async def upload_attachment(self, request: HttpRequestProtocol) -> dict:
194
+ """Handle POST /v3/conversations/{conversation_id}/attachments."""
195
+ conversation_id = request.get_path_param("conversation_id")
196
+ attachment_data = await self.deserialize_from_body(request, AttachmentData)
197
+ result = await self.handler.on_upload_attachment(
198
+ request.get_claims_identity(),
199
+ conversation_id,
200
+ attachment_data,
201
+ )
202
+ return self.serialize_model(result)
@@ -0,0 +1,136 @@
1
+ # Copyright (c) Microsoft Corporation. All rights reserved.
2
+ # Licensed under the MIT License.
3
+
4
+ """Base HTTP adapter with shared processing logic."""
5
+
6
+ from abc import ABC
7
+ from traceback import format_exc
8
+
9
+ from microsoft_agents.activity import Activity, DeliveryModes
10
+ from microsoft_agents.hosting.core.authorization import ClaimsIdentity, Connections
11
+ from microsoft_agents.hosting.core import (
12
+ Agent,
13
+ ChannelServiceAdapter,
14
+ ChannelServiceClientFactoryBase,
15
+ MessageFactory,
16
+ RestChannelServiceClientFactory,
17
+ TurnContext,
18
+ )
19
+
20
+ from ._http_request_protocol import HttpRequestProtocol
21
+ from ._http_response import HttpResponse, HttpResponseFactory
22
+
23
+
24
+ class HttpAdapterBase(ChannelServiceAdapter, ABC):
25
+ """Base adapter for HTTP-based agent hosting with shared processing logic.
26
+
27
+ This class contains all the common logic for processing HTTP requests
28
+ and can be subclassed by framework-specific adapters (aiohttp, FastAPI, etc).
29
+ """
30
+
31
+ def __init__(
32
+ self,
33
+ *,
34
+ connection_manager: Connections = None,
35
+ channel_service_client_factory: ChannelServiceClientFactoryBase = None,
36
+ ):
37
+ """Initialize the HTTP adapter.
38
+
39
+ Args:
40
+ connection_manager: Optional connection manager for OAuth.
41
+ channel_service_client_factory: Factory for creating channel service clients.
42
+ """
43
+
44
+ async def on_turn_error(context: TurnContext, error: Exception):
45
+ error_message = f"Exception caught : {error}"
46
+ print(format_exc())
47
+
48
+ await context.send_activity(MessageFactory.text(error_message))
49
+
50
+ # Send a trace activity
51
+ await context.send_trace_activity(
52
+ "OnTurnError Trace",
53
+ error_message,
54
+ "https://www.botframework.com/schemas/error",
55
+ "TurnError",
56
+ )
57
+
58
+ self.on_turn_error = on_turn_error
59
+
60
+ channel_service_client_factory = (
61
+ channel_service_client_factory
62
+ or RestChannelServiceClientFactory(connection_manager)
63
+ )
64
+
65
+ super().__init__(channel_service_client_factory)
66
+
67
+ async def process_request(
68
+ self, request: HttpRequestProtocol, agent: Agent
69
+ ) -> HttpResponse:
70
+ """Process an incoming HTTP request.
71
+
72
+ Args:
73
+ request: The HTTP request to process.
74
+ agent: The agent to handle the request.
75
+
76
+ Returns:
77
+ HttpResponse with the result.
78
+
79
+ Raises:
80
+ TypeError: If request or agent is None.
81
+ """
82
+ if not request:
83
+ raise TypeError("HttpAdapterBase.process_request: request can't be None")
84
+ if not agent:
85
+ raise TypeError("HttpAdapterBase.process_request: agent can't be None")
86
+
87
+ if request.method != "POST":
88
+ return HttpResponseFactory.method_not_allowed()
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
+ try:
96
+ body = await request.json()
97
+ except Exception:
98
+ return HttpResponseFactory.bad_request("Invalid JSON")
99
+
100
+ activity: Activity = Activity.model_validate(body)
101
+
102
+ # Get claims identity (default to anonymous if not set by middleware)
103
+ claims_identity: ClaimsIdentity = (
104
+ request.get_claims_identity() or ClaimsIdentity({}, False)
105
+ )
106
+
107
+ # Validate required activity fields
108
+ if (
109
+ not activity.type
110
+ or not activity.conversation
111
+ or not activity.conversation.id
112
+ ):
113
+ return HttpResponseFactory.bad_request(
114
+ "Activity must have type and conversation.id"
115
+ )
116
+
117
+ try:
118
+ # Process the inbound activity with the agent
119
+ invoke_response = await self.process_activity(
120
+ claims_identity, activity, agent.on_turn
121
+ )
122
+
123
+ # Check if we need to return a synchronous response
124
+ if (
125
+ activity.type == "invoke"
126
+ or activity.delivery_mode == DeliveryModes.expect_replies
127
+ ):
128
+ # Invoke and ExpectReplies cannot be performed async
129
+ return HttpResponseFactory.json(
130
+ invoke_response.body, invoke_response.status
131
+ )
132
+
133
+ return HttpResponseFactory.accepted()
134
+
135
+ except PermissionError:
136
+ return HttpResponseFactory.unauthorized()
@@ -0,0 +1,36 @@
1
+ # Copyright (c) Microsoft Corporation. All rights reserved.
2
+ # Licensed under the MIT License.
3
+
4
+ """Protocol for abstracting HTTP request objects across frameworks."""
5
+
6
+ from typing import Protocol, Dict, Any, Optional
7
+
8
+
9
+ class HttpRequestProtocol(Protocol):
10
+ """Protocol for HTTP requests that adapters must implement.
11
+
12
+ This protocol defines the interface that framework-specific request
13
+ adapters must implement to work with the shared HTTP adapter logic.
14
+ """
15
+
16
+ @property
17
+ def method(self) -> str:
18
+ """HTTP method (GET, POST, etc.)."""
19
+ ...
20
+
21
+ @property
22
+ def headers(self) -> Dict[str, str]:
23
+ """Request headers."""
24
+ ...
25
+
26
+ async def json(self) -> Dict[str, Any]:
27
+ """Parse request body as JSON."""
28
+ ...
29
+
30
+ def get_claims_identity(self) -> Optional[Any]:
31
+ """Get claims identity attached by auth middleware."""
32
+ ...
33
+
34
+ def get_path_param(self, name: str) -> str:
35
+ """Get path parameter by name."""
36
+ ...
@@ -0,0 +1,56 @@
1
+ # Copyright (c) Microsoft Corporation. All rights reserved.
2
+ # Licensed under the MIT License.
3
+
4
+ """HTTP response abstraction."""
5
+
6
+ from typing import Any, Optional, Dict
7
+ from dataclasses import dataclass
8
+
9
+
10
+ @dataclass
11
+ class HttpResponse:
12
+ """Framework-agnostic HTTP response."""
13
+
14
+ status_code: int
15
+ body: Optional[Any] = None
16
+ headers: Optional[Dict[str, str]] = None
17
+ content_type: Optional[str] = "application/json"
18
+
19
+
20
+ class HttpResponseFactory:
21
+ """Factory for creating HTTP responses."""
22
+
23
+ @staticmethod
24
+ def ok(body: Any = None) -> HttpResponse:
25
+ """Create 200 OK response."""
26
+ return HttpResponse(status_code=200, body=body)
27
+
28
+ @staticmethod
29
+ def accepted() -> HttpResponse:
30
+ """Create 202 Accepted response."""
31
+ return HttpResponse(status_code=202)
32
+
33
+ @staticmethod
34
+ def json(body: Any, status_code: int = 200) -> HttpResponse:
35
+ """Create JSON response."""
36
+ return HttpResponse(status_code=status_code, body=body)
37
+
38
+ @staticmethod
39
+ def bad_request(message: str = "Bad Request") -> HttpResponse:
40
+ """Create 400 Bad Request response."""
41
+ return HttpResponse(status_code=400, body={"error": message})
42
+
43
+ @staticmethod
44
+ def unauthorized(message: str = "Unauthorized") -> HttpResponse:
45
+ """Create 401 Unauthorized response."""
46
+ return HttpResponse(status_code=401, body={"error": message})
47
+
48
+ @staticmethod
49
+ def method_not_allowed(message: str = "Method Not Allowed") -> HttpResponse:
50
+ """Create 405 Method Not Allowed response."""
51
+ return HttpResponse(status_code=405, body={"error": message})
52
+
53
+ @staticmethod
54
+ def unsupported_media_type(message: str = "Unsupported Media Type") -> HttpResponse:
55
+ """Create 415 Unsupported Media Type response."""
56
+ return HttpResponse(status_code=415, body={"error": message})
@@ -113,7 +113,7 @@ class RestChannelServiceClientFactory(ChannelServiceClientFactoryBase):
113
113
  )
114
114
 
115
115
  token = await token_provider.get_access_token(
116
- audience, scopes or [f"{audience}/.default"]
116
+ audience, scopes or claims_identity.get_token_scope()
117
117
  )
118
118
 
119
119
  return TeamsConnectorClient(
@@ -142,7 +142,7 @@ class RestChannelServiceClientFactory(ChannelServiceClientFactoryBase):
142
142
  if context.activity.is_agentic_request():
143
143
  token = await self._get_agentic_token(context, self._token_service_endpoint)
144
144
  else:
145
- scopes = [f"{self._token_service_audience}/.default"]
145
+ scopes = claims_identity.get_token_scope()
146
146
 
147
147
  token_provider = self._connection_manager.get_token_provider(
148
148
  claims_identity, self._token_service_endpoint
@@ -8,7 +8,6 @@ from ._type_aliases import JSON
8
8
  from .storage import Storage
9
9
  from .store_item import StoreItem
10
10
 
11
-
12
11
  StoreItemT = TypeVar("StoreItemT", bound=StoreItem)
13
12
 
14
13
 
@@ -8,7 +8,6 @@ from asyncio import gather
8
8
  from ._type_aliases import JSON
9
9
  from .store_item import StoreItem
10
10
 
11
-
12
11
  StoreItemT = TypeVar("StoreItemT", bound=StoreItem)
13
12
 
14
13
 
@@ -19,7 +19,6 @@ from microsoft_agents.activity.conversation_reference import ActivityEventNames
19
19
  from microsoft_agents.hosting.core.middleware_set import Middleware, TurnContext
20
20
  from typing import Generic, TypeVar
21
21
 
22
-
23
22
  T = TypeVar("T")
24
23
 
25
24
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: microsoft-agents-hosting-core
3
- Version: 0.6.1
3
+ Version: 0.7.0
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.6.1
18
+ Requires-Dist: microsoft-agents-activity==0.7.0
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
@@ -41,11 +41,29 @@ This library is part of the **Microsoft 365 Agents SDK for Python** - a comprehe
41
41
  <th style="width:20%">Date</th>
42
42
  <th style="width:60%">Release Notes</th>
43
43
  </tr>
44
+ <tr>
45
+ <td>0.6.1</td>
46
+ <td>2025-12-01</td>
47
+ <td>
48
+ <a href="https://github.com/microsoft/Agents-for-python/blob/main/changelog.md#microsoft-365-agents-sdk-for-python---release-notes-v061">
49
+ 0.6.1 Release Notes
50
+ </a>
51
+ </td>
52
+ </tr>
53
+ <tr>
54
+ <td>0.6.0</td>
55
+ <td>2025-11-18</td>
56
+ <td>
57
+ <a href="https://github.com/microsoft/Agents-for-python/blob/main/changelog.md#microsoft-365-agents-sdk-for-python---release-notes-v060">
58
+ 0.6.0 Release Notes
59
+ </a>
60
+ </td>
61
+ </tr>
44
62
  <tr>
45
63
  <td>0.5.0</td>
46
64
  <td>2025-10-22</td>
47
65
  <td>
48
- <a href="https://github.com/microsoft/Agents-for-python/blob/main/changelog.md">
66
+ <a href="https://github.com/microsoft/Agents-for-python/blob/main/changelog.md#microsoft-365-agents-sdk-for-python---release-notes-v050">
49
67
  0.5.0 Release Notes
50
68
  </a>
51
69
  </td>