From 156aa19364db14db432f4fe20de01dc73e8791c0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 13 May 2026 22:00:13 +0000 Subject: [PATCH 1/6] Initial plan From d30ac55a170ad8b033593834218e428420cc2e62 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 13 May 2026 22:04:49 +0000 Subject: [PATCH 2/6] Add force_base_channel support for conversation reference in OAuth flow Agent-Logs-Url: https://github.com/microsoft/Agents-for-python/sessions/533beebe-4148-4f5a-bf80-93305e50a881 Co-authored-by: MattB-msft <10568244+MattB-msft@users.noreply.github.com> --- .../microsoft_agents/activity/activity.py | 12 ++++++++++-- .../hosting/core/_oauth/_oauth_flow.py | 2 +- tests/activity/test_activity.py | 9 +++++++++ tests/hosting_core/_oauth/test_oauth_flow.py | 3 +++ 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/libraries/microsoft-agents-activity/microsoft_agents/activity/activity.py b/libraries/microsoft-agents-activity/microsoft_agents/activity/activity.py index b146a459..4aa6fae4 100644 --- a/libraries/microsoft-agents-activity/microsoft_agents/activity/activity.py +++ b/libraries/microsoft-agents-activity/microsoft_agents/activity/activity.py @@ -618,10 +618,14 @@ def create_typing_activity() -> "Activity": """ return Activity(type=ActivityTypes.typing) - def get_conversation_reference(self) -> ConversationReference: + def get_conversation_reference( + self, force_base_channel: bool | None = None + ) -> ConversationReference: """ Creates a ConversationReference based on this activity. + :param force_base_channel: Optional, when True use only the base channel value + from the channel id (for example ``msteams`` from ``msteams:copilotweb``). :returns: A conversation reference for the conversation that contains this activity. """ return pick_model( @@ -635,7 +639,11 @@ def get_conversation_reference(self) -> ConversationReference: user=copy(self.from_property), agent=copy(self.recipient), conversation=copy(self.conversation), - channel_id=self.channel_id, + channel_id=( + self.channel_id.channel + if force_base_channel and self.channel_id is not None + else self.channel_id + ), locale=self.locale, service_url=self.service_url, ) diff --git a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/_oauth/_oauth_flow.py b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/_oauth/_oauth_flow.py index f396c17f..0c1369b4 100644 --- a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/_oauth/_oauth_flow.py +++ b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/_oauth/_oauth_flow.py @@ -187,7 +187,7 @@ async def begin_flow(self, activity: Activity) -> _FlowResponse: token_exchange_state = TokenExchangeState( connection_name=self._abs_oauth_connection_name, - conversation=activity.get_conversation_reference(), + conversation=activity.get_conversation_reference(force_base_channel=True), relates_to=activity.relates_to, ms_app_id=self._ms_app_id, ) diff --git a/tests/activity/test_activity.py b/tests/activity/test_activity.py index 35a04893..588dda88 100644 --- a/tests/activity/test_activity.py +++ b/tests/activity/test_activity.py @@ -66,6 +66,15 @@ def test_get_conversation_reference(self, activity): assert activity.locale == conversation_reference.locale assert activity.service_url == conversation_reference.service_url + def test_get_conversation_reference_force_base_channel(self, activity): + activity.channel_id = "msteams:copilot-web" + + conversation_reference = activity.get_conversation_reference( + force_base_channel=True + ) + + assert conversation_reference.channel_id == "msteams" + def test_get_reply_conversation_reference(self, activity): reply = ResourceResponse(id="1234") conversation_reference = activity.get_reply_conversation_reference(reply) diff --git a/tests/hosting_core/_oauth/test_oauth_flow.py b/tests/hosting_core/_oauth/test_oauth_flow.py index 9fe4e3dc..a8946da4 100644 --- a/tests/hosting_core/_oauth/test_oauth_flow.py +++ b/tests/hosting_core/_oauth/test_oauth_flow.py @@ -197,6 +197,9 @@ async def test_begin_flow_easy_case(self, mocker, flow_state, activity): activity.channel_id, "encoded_state", ) + Activity.get_conversation_reference.assert_called_once_with( + force_base_channel=True + ) @pytest.mark.asyncio async def test_begin_flow_long_case(self, mocker, flow_state, activity): From 3297ec97fb8dd8ce4c2e3d60dc37d195d03f2b51 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 13 May 2026 22:07:36 +0000 Subject: [PATCH 3/6] Refine base-channel extraction and OAuth flow test call verification Agent-Logs-Url: https://github.com/microsoft/Agents-for-python/sessions/533beebe-4148-4f5a-bf80-93305e50a881 Co-authored-by: MattB-msft <10568244+MattB-msft@users.noreply.github.com> --- .../microsoft_agents/activity/activity.py | 6 +++++- tests/hosting_core/_oauth/test_oauth_flow.py | 20 +++++++++---------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/libraries/microsoft-agents-activity/microsoft_agents/activity/activity.py b/libraries/microsoft-agents-activity/microsoft_agents/activity/activity.py index 4aa6fae4..bd678aac 100644 --- a/libraries/microsoft-agents-activity/microsoft_agents/activity/activity.py +++ b/libraries/microsoft-agents-activity/microsoft_agents/activity/activity.py @@ -640,7 +640,11 @@ def get_conversation_reference( agent=copy(self.recipient), conversation=copy(self.conversation), channel_id=( - self.channel_id.channel + ( + self.channel_id.channel + if hasattr(self.channel_id, "channel") + else str(self.channel_id).split(":", 1)[0] + ) if force_base_channel and self.channel_id is not None else self.channel_id ), diff --git a/tests/hosting_core/_oauth/test_oauth_flow.py b/tests/hosting_core/_oauth/test_oauth_flow.py index a8946da4..aae663d6 100644 --- a/tests/hosting_core/_oauth/test_oauth_flow.py +++ b/tests/hosting_core/_oauth/test_oauth_flow.py @@ -32,25 +32,22 @@ def create_testing_Activity( value=None, text="a", ): - # mock_conversation_ref = mocker.MagicMock(ConversationReference) conversation_reference = ConversationReference( conversation={"id": "conv1"}, ) - mocker.patch.object( - Activity, - "get_conversation_reference", - return_value=conversation_reference, - ) - return Activity( + activity = Activity( type=type, name=name, from_property=ChannelAccount(id=DEFAULTS.user_id), + recipient=ChannelAccount(id="agent-id"), + conversation={"id": "conv1"}, channel_id=DEFAULTS.channel_id, - # get_conversation_reference=mocker.Mock(return_value=conv_ref), + service_url=DEFAULTS.service_url, relates_to=conversation_reference, value=value, text=text, ) + return activity class TestUtils(FlowStateFixtures): @@ -174,6 +171,9 @@ async def test_begin_flow_easy_case(self, mocker, flow_state, activity): mocker.patch.object( TokenExchangeState, "get_encoded_state", return_value="encoded_state" ) + get_conversation_reference_spy = mocker.spy( + Activity, "get_conversation_reference" + ) flow = _OAuthFlow(flow_state, user_token_client) expected_flow_state = flow_state expected_flow_state.tag = _FlowStateTag.COMPLETE @@ -197,8 +197,8 @@ async def test_begin_flow_easy_case(self, mocker, flow_state, activity): activity.channel_id, "encoded_state", ) - Activity.get_conversation_reference.assert_called_once_with( - force_base_channel=True + get_conversation_reference_spy.assert_any_call( + activity, force_base_channel=True ) @pytest.mark.asyncio From 46997f6948db3cda6b0700ae5cf214cefcf11727 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 13 May 2026 22:08:52 +0000 Subject: [PATCH 4/6] Simplify base channel parsing and extend force_base_channel tests Agent-Logs-Url: https://github.com/microsoft/Agents-for-python/sessions/533beebe-4148-4f5a-bf80-93305e50a881 Co-authored-by: MattB-msft <10568244+MattB-msft@users.noreply.github.com> --- .../microsoft_agents/activity/activity.py | 6 +---- tests/activity/test_activity.py | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/libraries/microsoft-agents-activity/microsoft_agents/activity/activity.py b/libraries/microsoft-agents-activity/microsoft_agents/activity/activity.py index bd678aac..a5379260 100644 --- a/libraries/microsoft-agents-activity/microsoft_agents/activity/activity.py +++ b/libraries/microsoft-agents-activity/microsoft_agents/activity/activity.py @@ -640,11 +640,7 @@ def get_conversation_reference( agent=copy(self.recipient), conversation=copy(self.conversation), channel_id=( - ( - self.channel_id.channel - if hasattr(self.channel_id, "channel") - else str(self.channel_id).split(":", 1)[0] - ) + str(self.channel_id).split(":", 1)[0] if force_base_channel and self.channel_id is not None else self.channel_id ), diff --git a/tests/activity/test_activity.py b/tests/activity/test_activity.py index 588dda88..bc54230e 100644 --- a/tests/activity/test_activity.py +++ b/tests/activity/test_activity.py @@ -75,6 +75,30 @@ def test_get_conversation_reference_force_base_channel(self, activity): assert conversation_reference.channel_id == "msteams" + @pytest.mark.parametrize( + "channel_id, expected_base_channel", + [("msteams", "msteams"), ("msteams:copilot:web", "msteams")], + ) + def test_get_conversation_reference_force_base_channel_variants( + self, activity, channel_id, expected_base_channel + ): + activity.channel_id = channel_id + + conversation_reference = activity.get_conversation_reference( + force_base_channel=True + ) + + assert conversation_reference.channel_id == expected_base_channel + + def test_get_conversation_reference_does_not_force_base_channel(self, activity): + activity.channel_id = "msteams:copilot-web" + + conversation_reference = activity.get_conversation_reference( + force_base_channel=False + ) + + assert conversation_reference.channel_id == "msteams:copilot-web" + def test_get_reply_conversation_reference(self, activity): reply = ResourceResponse(id="1234") conversation_reference = activity.get_reply_conversation_reference(reply) From f3fdff701bbf3a9162397c9c1281436ea8747676 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 13 May 2026 22:10:02 +0000 Subject: [PATCH 5/6] Polish force_base_channel docs and test variants Agent-Logs-Url: https://github.com/microsoft/Agents-for-python/sessions/533beebe-4148-4f5a-bf80-93305e50a881 Co-authored-by: MattB-msft <10568244+MattB-msft@users.noreply.github.com> --- .../microsoft_agents/activity/activity.py | 2 +- tests/activity/test_activity.py | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/libraries/microsoft-agents-activity/microsoft_agents/activity/activity.py b/libraries/microsoft-agents-activity/microsoft_agents/activity/activity.py index a5379260..2376582f 100644 --- a/libraries/microsoft-agents-activity/microsoft_agents/activity/activity.py +++ b/libraries/microsoft-agents-activity/microsoft_agents/activity/activity.py @@ -625,7 +625,7 @@ def get_conversation_reference( Creates a ConversationReference based on this activity. :param force_base_channel: Optional, when True use only the base channel value - from the channel id (for example ``msteams`` from ``msteams:copilotweb``). + from the channel id (for example ``msteams`` from ``msteams:copilot-web``). :returns: A conversation reference for the conversation that contains this activity. """ return pick_model( diff --git a/tests/activity/test_activity.py b/tests/activity/test_activity.py index bc54230e..f44667dc 100644 --- a/tests/activity/test_activity.py +++ b/tests/activity/test_activity.py @@ -77,7 +77,11 @@ def test_get_conversation_reference_force_base_channel(self, activity): @pytest.mark.parametrize( "channel_id, expected_base_channel", - [("msteams", "msteams"), ("msteams:copilot:web", "msteams")], + [ + ("msteams", "msteams"), + ("msteams:copilot-web", "msteams"), + ("msteams:copilot:web", "msteams"), + ], ) def test_get_conversation_reference_force_base_channel_variants( self, activity, channel_id, expected_base_channel From 9f7cd1836dabd38464881209e8b339d7273a7f29 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 13 May 2026 22:11:08 +0000 Subject: [PATCH 6/6] Clarify force_base_channel doc behavior and simplify split logic Agent-Logs-Url: https://github.com/microsoft/Agents-for-python/sessions/533beebe-4148-4f5a-bf80-93305e50a881 Co-authored-by: MattB-msft <10568244+MattB-msft@users.noreply.github.com> --- .../microsoft_agents/activity/activity.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/microsoft-agents-activity/microsoft_agents/activity/activity.py b/libraries/microsoft-agents-activity/microsoft_agents/activity/activity.py index 2376582f..a21ec8c0 100644 --- a/libraries/microsoft-agents-activity/microsoft_agents/activity/activity.py +++ b/libraries/microsoft-agents-activity/microsoft_agents/activity/activity.py @@ -626,6 +626,7 @@ def get_conversation_reference( :param force_base_channel: Optional, when True use only the base channel value from the channel id (for example ``msteams`` from ``msteams:copilot-web``). + Composite values are split only on the first ``:``. :returns: A conversation reference for the conversation that contains this activity. """ return pick_model( @@ -640,7 +641,7 @@ def get_conversation_reference( agent=copy(self.recipient), conversation=copy(self.conversation), channel_id=( - str(self.channel_id).split(":", 1)[0] + self.channel_id.split(":", 1)[0] if force_base_channel and self.channel_id is not None else self.channel_id ),