From fcdcbce902336f29686f95878d1954959d4eb44c Mon Sep 17 00:00:00 2001 From: Rodrigo Brandao Date: Wed, 22 Jul 2026 11:49:10 -0700 Subject: [PATCH 01/19] ChannelId channel safe access --- .../hosting/core/app/oauth/authorization.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/oauth/authorization.py b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/oauth/authorization.py index 51cc75bc..ebc7160b 100644 --- a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/oauth/authorization.py +++ b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/oauth/authorization.py @@ -7,7 +7,13 @@ from typing import Optional, Callable, Awaitable, cast from dataclasses import dataclass -from microsoft_agents.activity import Activity, Channels, SignInConstants, TokenResponse +from microsoft_agents.activity import ( + Activity, + Channels, + ChannelId, + SignInConstants, + TokenResponse, +) from microsoft_agents.activity.activity_types import ActivityTypes from ...turn_context import TurnContext @@ -283,7 +289,7 @@ async def _start_or_continue_sign_in( elif sign_in_response.tag in [_FlowStateTag.BEGIN, _FlowStateTag.CONTINUE]: # Handling special case for Teams SSO, ConsentRequired if not ( - context.activity.channel_id.channel == Channels.ms_teams + ChannelId.get_channel(context.activity.channel_id) == Channels.ms_teams.value and sign_in_state.continuation_activity and context.activity.type == ActivityTypes.invoke and context.activity.name From ddff5e690f54c6541266bc0378b356b66ff4dd6f Mon Sep 17 00:00:00 2001 From: Rodrigo Brandao Date: Wed, 22 Jul 2026 11:50:24 -0700 Subject: [PATCH 02/19] Removing strange annotations --- .../hosting/core/app/proactive/conversation.py | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/proactive/conversation.py b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/proactive/conversation.py index aadc876a..079136dc 100644 --- a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/proactive/conversation.py +++ b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/proactive/conversation.py @@ -5,7 +5,7 @@ from __future__ import annotations -from typing import Optional, TYPE_CHECKING +from typing import TYPE_CHECKING from microsoft_agents.activity import ConversationReference from microsoft_agents.hosting.core.authorization import ClaimsIdentity @@ -39,7 +39,7 @@ class Conversation(StoreItem): def __init__( self, - claims: "dict[str, str] | ClaimsIdentity", + claims: dict[str, str] | ClaimsIdentity, conversation_reference: ConversationReference, ) -> None: if isinstance(claims, ClaimsIdentity): @@ -55,7 +55,7 @@ def __init__( # ------------------------------------------------------------------ @classmethod - def from_turn_context(cls, context: "TurnContext") -> "Conversation": + def from_turn_context(cls, context: TurnContext) -> Conversation: """ Create a :class:`microsoft_agents.hosting.core.app.proactive.Conversation` from the current turn context. @@ -65,11 +65,7 @@ def from_turn_context(cls, context: "TurnContext") -> "Conversation": and conversation reference. :rtype: :class:`microsoft_agents.hosting.core.app.proactive.Conversation` """ - from microsoft_agents.hosting.core.channel_adapter import ChannelAdapter - - identity: Optional[ClaimsIdentity] = context.turn_state.get( - ChannelAdapter.AGENT_IDENTITY_KEY - ) + identity: ClaimsIdentity | None = context.identity reference = context.activity.get_conversation_reference() return cls(identity or {}, reference) @@ -78,7 +74,7 @@ def from_turn_context(cls, context: "TurnContext") -> "Conversation": # ------------------------------------------------------------------ @staticmethod - def claims_from_identity(identity: ClaimsIdentity) -> "dict[str, str]": + def claims_from_identity(identity: ClaimsIdentity) -> dict[str, str]: """ Return the subset of claims from *identity* that are relevant for proactive messaging (``aud``, ``azp``, ``appid``, ``idtyp``, ``ver``, ``iss``, ``tid``). @@ -91,7 +87,7 @@ def claims_from_identity(identity: ClaimsIdentity) -> "dict[str, str]": return {k: v for k, v in identity.claims.items() if k in _PERSISTED_CLAIM_KEYS} @staticmethod - def identity_from_claims(claims: "dict[str, str]") -> ClaimsIdentity: + def identity_from_claims(claims: dict[str, str]) -> ClaimsIdentity: """ Reconstruct a :class:`~microsoft_agents.hosting.core.authorization.ClaimsIdentity` from a previously persisted claims dict. @@ -142,7 +138,7 @@ def store_item_to_json(self) -> dict: } @staticmethod - def from_json_to_store_item(json_data: dict) -> "Conversation": + def from_json_to_store_item(json_data: dict) -> Conversation: reference = ConversationReference.model_validate( json_data.get("conversation_reference", {}) ) From 8bef03a78da3b2107564913e282405836b342f89 Mon Sep 17 00:00:00 2001 From: Rodrigo Brandao Date: Wed, 22 Jul 2026 11:52:28 -0700 Subject: [PATCH 03/19] ChannelAdapter linting fixes --- .../activity/conversation_parameters.py | 2 ++ .../hosting/core/channel_adapter.py | 13 ++++++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/libraries/microsoft-agents-activity/microsoft_agents/activity/conversation_parameters.py b/libraries/microsoft-agents-activity/microsoft_agents/activity/conversation_parameters.py index 747e7814..86ce7cf7 100644 --- a/libraries/microsoft-agents-activity/microsoft_agents/activity/conversation_parameters.py +++ b/libraries/microsoft-agents-activity/microsoft_agents/activity/conversation_parameters.py @@ -7,6 +7,7 @@ from .activity import Activity from .agents_model import AgentsModel from ._type_aliases import NonEmptyString +from .conversation_account import ConversationAccount class ConversationParameters(AgentsModel): @@ -38,3 +39,4 @@ class ConversationParameters(AgentsModel): activity: Activity = None channel_data: object = None tenant_id: NonEmptyString = None + conversation: ConversationAccount = None \ No newline at end of file diff --git a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/channel_adapter.py b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/channel_adapter.py index 7d00fc3a..8695b7ab 100644 --- a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/channel_adapter.py +++ b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/channel_adapter.py @@ -1,6 +1,8 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. +from __future__ import annotations + from abc import ABC, abstractmethod from collections.abc import Callable from typing import Awaitable @@ -8,6 +10,7 @@ from microsoft_agents.activity import ChannelAdapterProtocol from microsoft_agents.activity import ( Activity, + ChannelId, ConversationAccount, ConversationReference, ConversationParameters, @@ -15,7 +18,7 @@ ) from .turn_context import TurnContext -from .middleware_set import MiddlewareSet +from .middleware_set import MiddlewareSet, Middleware class ChannelAdapter(ABC, ChannelAdapterProtocol): @@ -78,7 +81,7 @@ async def delete_activity( """ raise NotImplementedError() - def use(self, middleware): + def use(self, middleware: Middleware) -> ChannelAdapter: """ Registers a middleware handler with the adapter. @@ -202,11 +205,11 @@ async def create_conversation( # Create a conversation update activity conversation_update = Activity( - type=ActivityTypes.CONVERSATION_UPDATE, - channel_id=channel_id, + type=ActivityTypes.conversation_update, + channel_id=ChannelId(channel_id), service_url=service_url, conversation=conversation_parameters.conversation, - recipient=conversation_parameters.bot, + recipient=conversation_parameters.agent, from_property=conversation_parameters.members[0], members_added=conversation_parameters.members, ) From 8bc8ece9a579200c6f335aa42c9461668aa7a38d Mon Sep 17 00:00:00 2001 From: Rodrigo Brandao Date: Wed, 22 Jul 2026 11:56:44 -0700 Subject: [PATCH 04/19] ChannelServiceAdapter fixes --- .../hosting/core/channel_service_adapter.py | 52 ++++++++----------- 1 file changed, 21 insertions(+), 31 deletions(-) diff --git a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/channel_service_adapter.py b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/channel_service_adapter.py index 1cda31b5..00a3b00b 100644 --- a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/channel_service_adapter.py +++ b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/channel_service_adapter.py @@ -4,9 +4,8 @@ from __future__ import annotations from abc import ABC -from copy import Error from http import HTTPStatus -from typing import Awaitable, Callable, cast, Optional +from typing import Awaitable, Callable, cast from uuid import uuid4 from microsoft_agents.activity import ( @@ -67,12 +66,6 @@ async def send_activities( :rtype: list[:class:`microsoft_agents.activity.ResourceResponse`] :raises TypeError: If context or activities are None/invalid. """ - if not context: - raise TypeError("Expected TurnContext but got None instead") - - if activities is None: - raise TypeError("Expected Activities list but got None instead") - if len(activities) == 0: raise TypeError("Expecting one or more activities, but the list was empty.") @@ -97,7 +90,7 @@ async def send_activities( context.turn_state.get(self._AGENT_CONNECTOR_CLIENT_KEY), ) if not connector_client: - raise Error("Unable to extract ConnectorClient from turn context.") + raise RuntimeError("Unable to extract ConnectorClient from turn context.") with spans.AdapterSendActivities([activity]): if activity.reply_to_id: @@ -133,11 +126,9 @@ async def update_activity(self, context: TurnContext, activity: Activity): :rtype: :class:`microsoft_agents.activity.ResourceResponse` :raises TypeError: If context or activity are None/invalid. """ - if not context: - raise TypeError("Expected TurnContext but got None instead") - - if activity is None: - raise TypeError("Expected Activity but got None instead") + + if activity.id is None: + raise TypeError("Expected Activity with an id but got None instead") with spans.AdapterUpdateActivity(activity): @@ -146,7 +137,7 @@ async def update_activity(self, context: TurnContext, activity: Activity): context.turn_state.get(self._AGENT_CONNECTOR_CLIENT_KEY), ) if not connector_client: - raise Error("Unable to extract ConnectorClient from turn context.") + raise RuntimeError("Unable to extract ConnectorClient from turn context.") return await connector_client.conversations.update_activity( activity.conversation.id, activity.id, activity @@ -164,11 +155,10 @@ async def delete_activity( :type reference: :class:`microsoft_agents.activity.ConversationReference` :raises TypeError: If context or reference are None/invalid. """ - if not context: - raise TypeError("Expected TurnContext but got None instead") - - if not reference: - raise TypeError("Expected ConversationReference but got None instead") + if not reference.conversation or not reference.activity_id: + raise TypeError( + "Expected ConversationReference with conversation and activity_id but got None instead" + ) with spans.AdapterDeleteActivity(context.activity): @@ -177,7 +167,7 @@ async def delete_activity( context.turn_state.get(self._AGENT_CONNECTOR_CLIENT_KEY), ) if not connector_client: - raise Error("Unable to extract ConnectorClient from turn context.") + raise RuntimeError("Unable to extract ConnectorClient from turn context.") await connector_client.conversations.delete_activity( reference.conversation.id, reference.activity_id @@ -238,7 +228,7 @@ async def continue_conversation_with_claims( :param callback: The method to call for the resulting agent turn. :type callback: Callable[[:class:`microsoft_agents.hosting.core.turn_context.TurnContext`], Awaitable] :param audience: The audience for the conversation. - :type audience: Optional[str] + :type audience: str | None """ with spans.AdapterContinueConversation(continuation_activity): return await self.process_proactive( @@ -369,7 +359,7 @@ async def process_activity( claims_identity: ClaimsIdentity, activity: Activity, callback: Callable[[TurnContext], Awaitable], - ): + ) -> InvokeResponse | None: """ Creates a turn context and runs the middleware pipeline for an incoming activity. @@ -381,7 +371,7 @@ async def process_activity( :type callback: Callable[[:class:`microsoft_agents.hosting.core.turn_context.TurnContext`], Awaitable] :return: A task that represents the work queued to execute. - :rtype: Optional[:class:`microsoft_agents.activity.InvokeResponse`] + :rtype: :class:`microsoft_agents.activity.InvokeResponse` | None .. note:: This class processes an activity received by the agents web server. This includes any messages @@ -424,7 +414,7 @@ async def process_activity( context.turn_state[self.USER_TOKEN_CLIENT_KEY] = user_token_client # Create the connector client to use for outbound requests. - connector_client: Optional[ConnectorClient] = None + connector_client: ConnectorClient | None = None if self._resolve_if_connector_client_is_needed(activity): connector_client = ( await self._channel_service_client_factory.create_connector_client( @@ -506,7 +496,7 @@ def _create_turn_context( claims_identity: ClaimsIdentity, oauth_scope: str, callback: Callable[[TurnContext], Awaitable], - activity: Optional[Activity] = None, + activity: Activity | None = None, ) -> TurnContext: context = TurnContext(self, activity, claims_identity) @@ -519,13 +509,13 @@ def _create_turn_context( return context - def _process_turn_results(self, context: TurnContext) -> Optional[InvokeResponse]: + def _process_turn_results(self, context: TurnContext) -> InvokeResponse | None: """Process the results of a turn and return the appropriate response. :param context: The turn context :type context: :class:`microsoft_agents.hosting.core.turn_context.TurnContext` :return: The invoke response, if applicable - :rtype: Optional[:class:`microsoft_agents.activity.InvokeResponse`] + :rtype: :class:`microsoft_agents.activity.InvokeResponse` | None """ # Handle ExpectedReplies scenarios where all activities have been # buffered and sent back at once in an invoke response. @@ -542,11 +532,11 @@ def _process_turn_results(self, context: TurnContext) -> Optional[InvokeResponse if context.activity.type == ActivityTypes.invoke: with spans.AdapterSendActivities([context.activity]): - activity_invoke_response: Activity = context.turn_state.get( + activity_invoke_response: Activity | None = cast(Activity | None, context.turn_state.get( self.INVOKE_RESPONSE_KEY - ) + )) if not activity_invoke_response: - return InvokeResponse(status=HTTPStatus.OK) + return InvokeResponse(status=HTTPStatus.NOT_IMPLEMENTED) return InvokeResponse.model_validate(activity_invoke_response.value) From ff05e93fca0ddf610eabdb572135649055be5dbb Mon Sep 17 00:00:00 2001 From: Rodrigo Brandao Date: Wed, 22 Jul 2026 11:57:40 -0700 Subject: [PATCH 05/19] ChannelHostProtocol field declarations --- .../hosting/core/client/channel_host_protocol.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/client/channel_host_protocol.py b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/client/channel_host_protocol.py index a2990357..9227a117 100644 --- a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/client/channel_host_protocol.py +++ b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/client/channel_host_protocol.py @@ -8,6 +8,11 @@ class ChannelHostProtocol(Protocol): + + host_endpoint: str + host_app_id: str + channels: dict[str, ChannelInfoProtocol] + def __init__( self, host_endpoint: str, From 7f927491c42c38198be18d075ff11a86e3fa6bd6 Mon Sep 17 00:00:00 2001 From: Rodrigo Brandao Date: Wed, 22 Jul 2026 11:57:58 -0700 Subject: [PATCH 06/19] ChannelProtocol.post_activity optional type --- .../microsoft_agents/hosting/core/client/channel_protocol.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/client/channel_protocol.py b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/client/channel_protocol.py index a5855af0..c045b3d0 100644 --- a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/client/channel_protocol.py +++ b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/client/channel_protocol.py @@ -16,7 +16,7 @@ async def post_activity( conversation_id: str, activity: Activity, *, - response_body_type: type[AgentsModel] = None, + response_body_type: type[AgentsModel] | None = None, **kwargs, ) -> InvokeResponse: raise NotImplementedError() From 6419c297b6408e2d84207971b86fa2b38058d7a6 Mon Sep 17 00:00:00 2001 From: Rodrigo Brandao Date: Wed, 22 Jul 2026 12:00:19 -0700 Subject: [PATCH 07/19] HttpAdapterBase improvements --- .../microsoft_agents/hosting/core/http/_http_adapter_base.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/http/_http_adapter_base.py b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/http/_http_adapter_base.py index ca84c0c2..19ed2121 100644 --- a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/http/_http_adapter_base.py +++ b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/http/_http_adapter_base.py @@ -5,6 +5,7 @@ from abc import ABC from traceback import format_exc +from http import HTTPStatus from microsoft_agents.activity import Activity, DeliveryModes from microsoft_agents.hosting.core.authorization import ClaimsIdentity, Connections @@ -134,8 +135,10 @@ async def process_request( ): with spans.AdapterWriteResponse(activity): # Invoke and ExpectReplies cannot be performed async + invoke_response_status = invoke_response.status if invoke_response else None return HttpResponseFactory.json( - invoke_response.body, invoke_response.status + invoke_response.body if invoke_response else None, + invoke_response_status or HTTPStatus.NOT_IMPLEMENTED ) return HttpResponseFactory.accepted() From a41426d273fa8ec122e5ed344bb64f02c6b2551e Mon Sep 17 00:00:00 2001 From: Rodrigo Brandao Date: Wed, 22 Jul 2026 12:02:43 -0700 Subject: [PATCH 08/19] MessageFactory tweaks --- .../hosting/core/message_factory.py | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/message_factory.py b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/message_factory.py index 91a55730..63a1a4ff 100644 --- a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/message_factory.py +++ b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/message_factory.py @@ -88,9 +88,9 @@ def suggested_actions( :param input_hint: :return: """ - actions = SuggestedActions(actions=actions) + suggested_actions = SuggestedActions(actions=actions) message = Activity( - type=ActivityTypes.message, input_hint=input_hint, suggested_actions=actions + type=ActivityTypes.message, input_hint=input_hint, suggested_actions=suggested_actions ) if text: message.text = text @@ -122,16 +122,14 @@ def attachment( :param input_hint: :return: """ - return attachment_activity( - AttachmentLayoutTypes.list, [attachment], text, speak, input_hint - ) + return MessageFactory.list([attachment], text, speak, input_hint) @staticmethod def list( attachments: list[Attachment], text: str | None = None, speak: str | None = None, - input_hint: InputHints | str = None, + input_hint: InputHints | str | None = None, ) -> Activity: """ Returns a message that will display a set of attachments in list form. @@ -154,6 +152,10 @@ def list( :param input_hint: :return: """ + if not input_hint: + return attachment_activity( + AttachmentLayoutTypes.list, attachments, text, speak + ) return attachment_activity( AttachmentLayoutTypes.list, attachments, text, speak, input_hint ) @@ -163,7 +165,7 @@ def carousel( attachments: list[Attachment], text: str | None = None, speak: str | None = None, - input_hint: InputHints | str = None, + input_hint: InputHints | str | None = None, ) -> Activity: """ Returns a message that will display a set of attachments using a carousel layout. @@ -186,6 +188,10 @@ def carousel( :param input_hint: :return: """ + if not input_hint: + return attachment_activity( + AttachmentLayoutTypes.carousel, attachments, text, speak + ) return attachment_activity( AttachmentLayoutTypes.carousel, attachments, text, speak, input_hint ) @@ -218,6 +224,4 @@ def content_url( attachment = Attachment(content_type=content_type, content_url=url) if name: attachment.name = name - return attachment_activity( - AttachmentLayoutTypes.list, [attachment], text, speak, input_hint - ) + return MessageFactory.attachment(attachment, text, speak, input_hint) From 931857dbfbf52dd334e18fc02717b694c7fce636 Mon Sep 17 00:00:00 2001 From: Rodrigo Brandao Date: Wed, 22 Jul 2026 12:03:58 -0700 Subject: [PATCH 09/19] RestChannelServiceClientFactory tweaks --- .../core/rest_channel_service_client_factory.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/rest_channel_service_client_factory.py b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/rest_channel_service_client_factory.py index e1347b78..5ba12e5e 100644 --- a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/rest_channel_service_client_factory.py +++ b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/rest_channel_service_client_factory.py @@ -70,17 +70,21 @@ async def _get_agentic_token(self, context: TurnContext, service_url: str) -> st agent_instance_id = context.activity.get_agentic_instance_id() if not agent_instance_id: raise ValueError("Agent instance ID is required for agentic identity role") + + tenant_id = context.activity.get_agentic_tenant_id() + if not tenant_id: + raise ValueError("Agentic tenant ID is required for agentic identity role") if context.activity.recipient.role == RoleTypes.agentic_identity: token, _ = await connection.get_agentic_instance_token( - context.activity.get_agentic_tenant_id(), agent_instance_id + tenant_id, agent_instance_id ) else: agentic_user = context.activity.get_agentic_user() if not agentic_user: raise ValueError("Agentic user is required for agentic user role") token = await connection.get_agentic_user_token( - context.activity.get_agentic_tenant_id(), + tenant_id, agent_instance_id, agentic_user, [AuthenticationConstants.APX_PRODUCTION_SCOPE], @@ -163,7 +167,7 @@ async def create_user_token_client( if not context or not claims_identity: raise ValueError("context and claims_identity are required") - scopes = claims_identity.get_token_scope() if claims_identity else None + scopes = claims_identity.get_token_scope() if claims_identity else [] with spans.AdapterCreateUserTokenClient( token_service_endpoint=self._token_service_endpoint, From e64ddef61104ffbdf3426d34b1d85f489fcaddc8 Mon Sep 17 00:00:00 2001 From: Rodrigo Brandao Date: Wed, 22 Jul 2026 12:06:39 -0700 Subject: [PATCH 10/19] Formatting --- .../activity/conversation_parameters.py | 2 +- .../hosting/core/app/oauth/authorization.py | 3 ++- .../hosting/core/channel_service_adapter.py | 20 ++++++++++++------- .../hosting/core/http/_http_adapter_base.py | 6 ++++-- .../hosting/core/message_factory.py | 4 +++- .../rest_channel_service_client_factory.py | 2 +- 6 files changed, 24 insertions(+), 13 deletions(-) diff --git a/libraries/microsoft-agents-activity/microsoft_agents/activity/conversation_parameters.py b/libraries/microsoft-agents-activity/microsoft_agents/activity/conversation_parameters.py index 86ce7cf7..5ca9defe 100644 --- a/libraries/microsoft-agents-activity/microsoft_agents/activity/conversation_parameters.py +++ b/libraries/microsoft-agents-activity/microsoft_agents/activity/conversation_parameters.py @@ -39,4 +39,4 @@ class ConversationParameters(AgentsModel): activity: Activity = None channel_data: object = None tenant_id: NonEmptyString = None - conversation: ConversationAccount = None \ No newline at end of file + conversation: ConversationAccount = None diff --git a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/oauth/authorization.py b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/oauth/authorization.py index ebc7160b..bd40288b 100644 --- a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/oauth/authorization.py +++ b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/oauth/authorization.py @@ -289,7 +289,8 @@ async def _start_or_continue_sign_in( elif sign_in_response.tag in [_FlowStateTag.BEGIN, _FlowStateTag.CONTINUE]: # Handling special case for Teams SSO, ConsentRequired if not ( - ChannelId.get_channel(context.activity.channel_id) == Channels.ms_teams.value + ChannelId.get_channel(context.activity.channel_id) + == Channels.ms_teams.value and sign_in_state.continuation_activity and context.activity.type == ActivityTypes.invoke and context.activity.name diff --git a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/channel_service_adapter.py b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/channel_service_adapter.py index 00a3b00b..969a2c2b 100644 --- a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/channel_service_adapter.py +++ b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/channel_service_adapter.py @@ -90,7 +90,9 @@ async def send_activities( context.turn_state.get(self._AGENT_CONNECTOR_CLIENT_KEY), ) if not connector_client: - raise RuntimeError("Unable to extract ConnectorClient from turn context.") + raise RuntimeError( + "Unable to extract ConnectorClient from turn context." + ) with spans.AdapterSendActivities([activity]): if activity.reply_to_id: @@ -126,7 +128,7 @@ async def update_activity(self, context: TurnContext, activity: Activity): :rtype: :class:`microsoft_agents.activity.ResourceResponse` :raises TypeError: If context or activity are None/invalid. """ - + if activity.id is None: raise TypeError("Expected Activity with an id but got None instead") @@ -137,7 +139,9 @@ async def update_activity(self, context: TurnContext, activity: Activity): context.turn_state.get(self._AGENT_CONNECTOR_CLIENT_KEY), ) if not connector_client: - raise RuntimeError("Unable to extract ConnectorClient from turn context.") + raise RuntimeError( + "Unable to extract ConnectorClient from turn context." + ) return await connector_client.conversations.update_activity( activity.conversation.id, activity.id, activity @@ -167,7 +171,9 @@ async def delete_activity( context.turn_state.get(self._AGENT_CONNECTOR_CLIENT_KEY), ) if not connector_client: - raise RuntimeError("Unable to extract ConnectorClient from turn context.") + raise RuntimeError( + "Unable to extract ConnectorClient from turn context." + ) await connector_client.conversations.delete_activity( reference.conversation.id, reference.activity_id @@ -532,9 +538,9 @@ def _process_turn_results(self, context: TurnContext) -> InvokeResponse | None: if context.activity.type == ActivityTypes.invoke: with spans.AdapterSendActivities([context.activity]): - activity_invoke_response: Activity | None = cast(Activity | None, context.turn_state.get( - self.INVOKE_RESPONSE_KEY - )) + activity_invoke_response: Activity | None = cast( + Activity | None, context.turn_state.get(self.INVOKE_RESPONSE_KEY) + ) if not activity_invoke_response: return InvokeResponse(status=HTTPStatus.NOT_IMPLEMENTED) diff --git a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/http/_http_adapter_base.py b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/http/_http_adapter_base.py index 19ed2121..869137fe 100644 --- a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/http/_http_adapter_base.py +++ b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/http/_http_adapter_base.py @@ -135,10 +135,12 @@ async def process_request( ): with spans.AdapterWriteResponse(activity): # Invoke and ExpectReplies cannot be performed async - invoke_response_status = invoke_response.status if invoke_response else None + invoke_response_status = ( + invoke_response.status if invoke_response else None + ) return HttpResponseFactory.json( invoke_response.body if invoke_response else None, - invoke_response_status or HTTPStatus.NOT_IMPLEMENTED + invoke_response_status or HTTPStatus.NOT_IMPLEMENTED, ) return HttpResponseFactory.accepted() diff --git a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/message_factory.py b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/message_factory.py index 63a1a4ff..987f6818 100644 --- a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/message_factory.py +++ b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/message_factory.py @@ -90,7 +90,9 @@ def suggested_actions( """ suggested_actions = SuggestedActions(actions=actions) message = Activity( - type=ActivityTypes.message, input_hint=input_hint, suggested_actions=suggested_actions + type=ActivityTypes.message, + input_hint=input_hint, + suggested_actions=suggested_actions, ) if text: message.text = text diff --git a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/rest_channel_service_client_factory.py b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/rest_channel_service_client_factory.py index 5ba12e5e..c311476c 100644 --- a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/rest_channel_service_client_factory.py +++ b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/rest_channel_service_client_factory.py @@ -70,7 +70,7 @@ async def _get_agentic_token(self, context: TurnContext, service_url: str) -> st agent_instance_id = context.activity.get_agentic_instance_id() if not agent_instance_id: raise ValueError("Agent instance ID is required for agentic identity role") - + tenant_id = context.activity.get_agentic_tenant_id() if not tenant_id: raise ValueError("Agentic tenant ID is required for agentic identity role") From 6f1889a1a5701c75e02cb8c58fbe00ca7780f081 Mon Sep 17 00:00:00 2001 From: Rodrigo Brandao Date: Wed, 22 Jul 2026 12:16:05 -0700 Subject: [PATCH 11/19] Removing None guarding for non-None args --- .../hosting/core/channel_service_adapter.py | 17 ++++------------- .../core/rest_channel_service_client_factory.py | 6 +----- 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/channel_service_adapter.py b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/channel_service_adapter.py index 969a2c2b..04d881e7 100644 --- a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/channel_service_adapter.py +++ b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/channel_service_adapter.py @@ -64,10 +64,9 @@ async def send_activities( :type activities: list[:class:`microsoft_agents.activity.Activity`] :return: List of resource responses for the sent activities. :rtype: list[:class:`microsoft_agents.activity.ResourceResponse`] - :raises TypeError: If context or activities are None/invalid. """ if len(activities) == 0: - raise TypeError("Expecting one or more activities, but the list was empty.") + return [] responses = [] @@ -126,7 +125,7 @@ async def update_activity(self, context: TurnContext, activity: Activity): :type activity: :class:`microsoft_agents.activity.Activity` :return: Resource response for the updated activity. :rtype: :class:`microsoft_agents.activity.ResourceResponse` - :raises TypeError: If context or activity are None/invalid. + :raises TypeError: activity.id is None """ if activity.id is None: @@ -157,7 +156,7 @@ async def delete_activity( :type context: :class:`microsoft_agents.hosting.core.turn_context.TurnContext` :param reference: Reference to the conversation and activity to delete. :type reference: :class:`microsoft_agents.activity.ConversationReference` - :raises TypeError: If context or reference are None/invalid. + :raises TypeError: reference.conversation or reference.activity_id is None """ if not reference.conversation or not reference.activity_id: raise TypeError( @@ -257,12 +256,6 @@ async def create_conversation( # pylint: disable=arguments-differ raise TypeError( "CloudAdapter.create_conversation(): service_url is required." ) - if not conversation_parameters: - raise TypeError( - "CloudAdapter.create_conversation(): conversation_parameters is required." - ) - if not callback: - raise TypeError("CloudAdapter.create_conversation(): callback is required.") # Create a ClaimsIdentity, to create the connector and for adding to the turn context. claims_identity = self.create_claims_identity(agent_app_id) @@ -462,9 +455,7 @@ def create_claims_identity(self, agent_app_id: str = "") -> ClaimsIdentity: @staticmethod def _validate_continuation_activity(continuation_activity: Activity): - if not continuation_activity: - raise TypeError("CloudAdapter: continuation_activity is required.") - + if not continuation_activity.conversation: raise TypeError( "CloudAdapter: continuation_activity.conversation is required." diff --git a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/rest_channel_service_client_factory.py b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/rest_channel_service_client_factory.py index c311476c..f00d912d 100644 --- a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/rest_channel_service_client_factory.py +++ b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/rest_channel_service_client_factory.py @@ -103,8 +103,6 @@ async def create_connector_client( scopes: list[str] | None = None, use_anonymous: bool = False, ) -> ConnectorClientBase: - if not claims_identity: - raise TypeError("claims_identity is required") if not service_url: raise TypeError( "RestChannelServiceClientFactory.create_connector_client: service_url can't be None or Empty" @@ -164,10 +162,8 @@ async def create_user_token_client( :param claims_identity: The ClaimsIdentity of the user. :param use_anonymous: Whether to use an anonymous token provider. """ - if not context or not claims_identity: - raise ValueError("context and claims_identity are required") - scopes = claims_identity.get_token_scope() if claims_identity else [] + scopes = claims_identity.get_token_scope() with spans.AdapterCreateUserTokenClient( token_service_endpoint=self._token_service_endpoint, From 0d2d49bee0ac42b61278eaf2fe6e7069e0139dcc Mon Sep 17 00:00:00 2001 From: Rodrigo Brandao Date: Wed, 22 Jul 2026 12:16:22 -0700 Subject: [PATCH 12/19] Removing None guarding for non-None args --- .../microsoft_agents/hosting/core/channel_service_adapter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/channel_service_adapter.py b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/channel_service_adapter.py index 04d881e7..9959e646 100644 --- a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/channel_service_adapter.py +++ b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/channel_service_adapter.py @@ -455,7 +455,7 @@ def create_claims_identity(self, agent_app_id: str = "") -> ClaimsIdentity: @staticmethod def _validate_continuation_activity(continuation_activity: Activity): - + if not continuation_activity.conversation: raise TypeError( "CloudAdapter: continuation_activity.conversation is required." From 350cbc6ba059c9bf813525b22425e989279d2771 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Brand=C3=A3o?= Date: Wed, 22 Jul 2026 12:23:31 -0700 Subject: [PATCH 13/19] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../hosting/core/rest_channel_service_client_factory.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/rest_channel_service_client_factory.py b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/rest_channel_service_client_factory.py index f00d912d..13d95a8d 100644 --- a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/rest_channel_service_client_factory.py +++ b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/rest_channel_service_client_factory.py @@ -71,9 +71,8 @@ async def _get_agentic_token(self, context: TurnContext, service_url: str) -> st if not agent_instance_id: raise ValueError("Agent instance ID is required for agentic identity role") - tenant_id = context.activity.get_agentic_tenant_id() if not tenant_id: - raise ValueError("Agentic tenant ID is required for agentic identity role") + raise ValueError("Agentic tenant ID is required for agentic activities") if context.activity.recipient.role == RoleTypes.agentic_identity: token, _ = await connection.get_agentic_instance_token( From 09e1ea63aafa47a533fb443a0dcda0e32f8753bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Brand=C3=A3o?= Date: Wed, 22 Jul 2026 12:24:06 -0700 Subject: [PATCH 14/19] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../microsoft_agents/hosting/core/message_factory.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/message_factory.py b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/message_factory.py index 987f6818..975ae88f 100644 --- a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/message_factory.py +++ b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/message_factory.py @@ -154,12 +154,12 @@ def list( :param input_hint: :return: """ - if not input_hint: - return attachment_activity( - AttachmentLayoutTypes.list, attachments, text, speak - ) return attachment_activity( - AttachmentLayoutTypes.list, attachments, text, speak, input_hint + AttachmentLayoutTypes.list, + attachments, + text, + speak, + input_hint or "", ) @staticmethod From 48a8a782fbdb1f65365304ac13feca1ede8bedb4 Mon Sep 17 00:00:00 2001 From: Rodrigo Brandao Date: Wed, 22 Jul 2026 12:25:02 -0700 Subject: [PATCH 15/19] Fixing tests --- .../activity/conversation_parameters.py | 2 ++ tests/hosting_core/app/proactive/test_conversation.py | 3 +-- tests/hosting_core/app/proactive/test_proactive.py | 3 +-- .../test_rest_channel_service_client_factory.py | 10 ++++++---- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/libraries/microsoft-agents-activity/microsoft_agents/activity/conversation_parameters.py b/libraries/microsoft-agents-activity/microsoft_agents/activity/conversation_parameters.py index 5ca9defe..0c06ef11 100644 --- a/libraries/microsoft-agents-activity/microsoft_agents/activity/conversation_parameters.py +++ b/libraries/microsoft-agents-activity/microsoft_agents/activity/conversation_parameters.py @@ -30,6 +30,8 @@ class ConversationParameters(AgentsModel): :type channel_data: object :param tenant_id: (Optional) The tenant ID in which the conversation should be created :type tenant_id: str + :param conversation: (Optional) The conversation account to use when creating the new conversation + :type conversation: ~microsoft_agents.activity.ConversationAccount """ is_group: bool = None diff --git a/tests/hosting_core/app/proactive/test_conversation.py b/tests/hosting_core/app/proactive/test_conversation.py index 3c65f168..8677ddf0 100644 --- a/tests/hosting_core/app/proactive/test_conversation.py +++ b/tests/hosting_core/app/proactive/test_conversation.py @@ -9,7 +9,6 @@ from microsoft_agents.activity import ConversationAccount, ConversationReference from microsoft_agents.hosting.core.app.proactive import Conversation from microsoft_agents.hosting.core.authorization import ClaimsIdentity -from microsoft_agents.hosting.core.channel_adapter import ChannelAdapter def _make_reference( @@ -75,7 +74,7 @@ def test_from_turn_context_extracts_reference_and_identity(self): ctx = MagicMock() ctx.activity.get_conversation_reference.return_value = ref - ctx.turn_state = {ChannelAdapter.AGENT_IDENTITY_KEY: identity} + ctx.identity = identity conv = Conversation.from_turn_context(ctx) diff --git a/tests/hosting_core/app/proactive/test_proactive.py b/tests/hosting_core/app/proactive/test_proactive.py index d7c73f51..e41c1038 100644 --- a/tests/hosting_core/app/proactive/test_proactive.py +++ b/tests/hosting_core/app/proactive/test_proactive.py @@ -24,7 +24,6 @@ ProactiveOptions, ) from microsoft_agents.hosting.core.authorization import ClaimsIdentity -from microsoft_agents.hosting.core.channel_adapter import ChannelAdapter # --------------------------------------------------------------------------- # Helpers @@ -165,7 +164,7 @@ async def test_store_from_turn_context(self, proactive): ctx = MagicMock(spec=TurnContext) ctx.activity = MagicMock() ctx.activity.get_conversation_reference.return_value = ref - ctx.turn_state = {ChannelAdapter.AGENT_IDENTITY_KEY: identity} + ctx.identity = identity await proactive.store_conversation(ctx) result = await proactive.get_conversation("ctx-conv") diff --git a/tests/hosting_core/test_rest_channel_service_client_factory.py b/tests/hosting_core/test_rest_channel_service_client_factory.py index 08c1d619..35fa25ab 100644 --- a/tests/hosting_core/test_rest_channel_service_client_factory.py +++ b/tests/hosting_core/test_rest_channel_service_client_factory.py @@ -51,6 +51,7 @@ def activity_agentic_user(self): id="bot1", agentic_app_id="agentic_app_id", agentic_user_id="agentic_user_id", + tenant_id="tenant_id", role=RoleTypes.agentic_user, ), service_url="https://service.url/", @@ -69,6 +70,7 @@ def activity_agentic_identity(self): recipient=ChannelAccount( id="bot1", agentic_app_id="agentic_app_id", + tenant_id="tenant_id", role=RoleTypes.agentic_identity, ), service_url="https://service.url/", @@ -325,7 +327,7 @@ async def test_create_connector_client_agentic_identity( ) assert token_provider.get_agentic_instance_token.call_count == 1 token_provider.get_agentic_instance_token.assert_called_once_with( - None, "agentic_app_id" + "tenant_id", "agentic_app_id" ) TeamsConnectorClient.__new__.assert_called_once_with( TeamsConnectorClient, endpoint=DEFAULTS.service_url, token=DEFAULTS.token @@ -376,7 +378,7 @@ async def test_create_connector_client_agentic_identity_non_msal_provider( # verify the alternate blueprint redirect happened via ``configuration`` connection_manager.get_connection.assert_called_once_with("alt_blueprint_id") token_provider.get_agentic_instance_token.assert_called_once_with( - None, "agentic_app_id" + "tenant_id", "agentic_app_id" ) @pytest.mark.asyncio @@ -419,7 +421,7 @@ async def test_create_connector_client_agentic_no_configuration( # verify: no redirect attempted, token still acquired connection_manager.get_connection.assert_not_called() token_provider.get_agentic_instance_token.assert_called_once_with( - None, "agentic_app_id" + "tenant_id", "agentic_app_id" ) @pytest.mark.parametrize("alt_blueprint", [True, False]) @@ -480,7 +482,7 @@ async def test_create_connector_client_agentic_user( ) assert token_provider.get_agentic_user_token.call_count == 1 token_provider.get_agentic_user_token.assert_called_once_with( - None, + "tenant_id", "agentic_app_id", "agentic_user_id", [AuthenticationConstants.APX_PRODUCTION_SCOPE], From f3a1e1248ea5f882ff44aa274ade1081c8bc8a7b Mon Sep 17 00:00:00 2001 From: Rodrigo Brandao Date: Wed, 22 Jul 2026 13:13:13 -0700 Subject: [PATCH 16/19] Adding missing line --- .../hosting/core/rest_channel_service_client_factory.py | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/rest_channel_service_client_factory.py b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/rest_channel_service_client_factory.py index 13d95a8d..8e82f7c2 100644 --- a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/rest_channel_service_client_factory.py +++ b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/rest_channel_service_client_factory.py @@ -71,6 +71,7 @@ async def _get_agentic_token(self, context: TurnContext, service_url: str) -> st if not agent_instance_id: raise ValueError("Agent instance ID is required for agentic identity role") + tenant_id = context.activity.get_agentic_tenant_id() if not tenant_id: raise ValueError("Agentic tenant ID is required for agentic activities") From 9675a1d28bf66566cf72ce90a174c4a3342b9226 Mon Sep 17 00:00:00 2001 From: Rodrigo Brandao Date: Wed, 22 Jul 2026 13:15:32 -0700 Subject: [PATCH 17/19] ADdressing PR feedback --- .../microsoft_agents/hosting/core/message_factory.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/message_factory.py b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/message_factory.py index 975ae88f..a8430d7a 100644 --- a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/message_factory.py +++ b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/message_factory.py @@ -154,12 +154,16 @@ def list( :param input_hint: :return: """ + if not input_hint: + return attachment_activity( + AttachmentLayoutTypes.list, attachments, text, speak + ) return attachment_activity( AttachmentLayoutTypes.list, attachments, text, speak, - input_hint or "", + input_hint ) @staticmethod From 5a4007daf44cef82abeeeefc17192f4d2890c744 Mon Sep 17 00:00:00 2001 From: Rodrigo Brandao Date: Wed, 22 Jul 2026 13:17:51 -0700 Subject: [PATCH 18/19] Formatting --- .../microsoft_agents/hosting/core/message_factory.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/message_factory.py b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/message_factory.py index a8430d7a..987f6818 100644 --- a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/message_factory.py +++ b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/message_factory.py @@ -159,11 +159,7 @@ def list( AttachmentLayoutTypes.list, attachments, text, speak ) return attachment_activity( - AttachmentLayoutTypes.list, - attachments, - text, - speak, - input_hint + AttachmentLayoutTypes.list, attachments, text, speak, input_hint ) @staticmethod From 221abbfd0f1b0419496b1f5ee919a3b1c492ea45 Mon Sep 17 00:00:00 2001 From: Rodrigo Brandao Date: Wed, 22 Jul 2026 13:26:57 -0700 Subject: [PATCH 19/19] Another commit --- .../microsoft_agents/hosting/core/channel_service_adapter.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/channel_service_adapter.py b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/channel_service_adapter.py index 9959e646..64e3a704 100644 --- a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/channel_service_adapter.py +++ b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/channel_service_adapter.py @@ -64,9 +64,10 @@ async def send_activities( :type activities: list[:class:`microsoft_agents.activity.Activity`] :return: List of resource responses for the sent activities. :rtype: list[:class:`microsoft_agents.activity.ResourceResponse`] + :raises ValueError: If the activities list is empty. """ if len(activities) == 0: - return [] + raise ValueError("send_activities: activities list cannot be empty") responses = []