From 75c18f46b29db815755075948bb423407a2dcca7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 20 May 2026 22:24:20 +0000 Subject: [PATCH 1/4] Initial plan From a95f259ad884429065952b0fb4d63ef13de31df2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 20 May 2026 22:33:17 +0000 Subject: [PATCH 2/4] URL-encode conversation ID for agentic agents/msteams channels Agent-Logs-Url: https://github.com/microsoft/Agents-for-python/sessions/374b1ff5-61b6-415c-aa3e-b9a2623cc766 Co-authored-by: MattB-msft <10568244+MattB-msft@users.noreply.github.com> --- .../core/connector/client/connector_client.py | 32 ++- .../connector/test_connector_client.py | 223 +++++++++++++++++- 2 files changed, 249 insertions(+), 6 deletions(-) diff --git a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/connector/client/connector_client.py b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/connector/client/connector_client.py index 777c685ab..9b358c401 100644 --- a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/connector/client/connector_client.py +++ b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/connector/client/connector_client.py @@ -5,18 +5,22 @@ import logging from typing import Any, Optional +from urllib.parse import quote from aiohttp import ClientSession from io import BytesIO from microsoft_agents.activity import ( Activity, ChannelAccount, + Channels, ConversationParameters, ConversationResourceResponse, ResourceResponse, + RoleTypes, ConversationsResult, PagedMembersResult, ) +from microsoft_agents.activity.channel_id import ChannelId from microsoft_agents.hosting.core.connector import ConnectorClientBase from ..attachments_base import AttachmentsBase from ..conversations_base import ConversationsBase @@ -138,8 +142,26 @@ def __init__(self, client: ClientSession, **kwargs): self.client = client self._max_conversation_id_length = kwargs.get("max_conversation_id_length", 150) - def _normalize_conversation_id(self, conversation_id: str) -> str: - return conversation_id[: self._max_conversation_id_length] + def _normalize_conversation_id( + self, conversation_id: str, activity: Optional[Activity] = None + ) -> str: + trimmed = conversation_id[: self._max_conversation_id_length] + if activity is not None and self._should_url_encode_conversation_id(activity): + return quote(trimmed, safe="") + return trimmed + + @staticmethod + def _should_url_encode_conversation_id(activity: Activity) -> bool: + channel_id = activity.channel_id + if not channel_id: + return False + base_channel = channel_id.channel if isinstance(channel_id, ChannelId) else channel_id.split(":", 1)[0] + if base_channel not in (Channels.ms_teams, Channels.agents): + return False + from_property = activity.from_property + if not from_property or not from_property.role: + return False + return from_property.role in (RoleTypes.agentic_identity, RoleTypes.agentic_user) async def get_conversations( self, continuation_token: Optional[str] = None @@ -221,7 +243,7 @@ async def reply_to_activity( with spans.ConnectorReplyToActivity(conversation_id, activity_id) as span: - conversation_id = self._normalize_conversation_id(conversation_id) + conversation_id = self._normalize_conversation_id(conversation_id, body) url = f"v3/conversations/{conversation_id}/activities/{activity_id}" logger.info( @@ -283,7 +305,7 @@ async def send_to_conversation( with spans.ConnectorSendToConversation(conversation_id, body.id) as span: - conversation_id = self._normalize_conversation_id(conversation_id) + conversation_id = self._normalize_conversation_id(conversation_id, body) url = f"v3/conversations/{conversation_id}/activities" logger.info( @@ -330,7 +352,7 @@ async def update_activity( with spans.ConnectorUpdateActivity(conversation_id, activity_id) as span: - conversation_id = self._normalize_conversation_id(conversation_id) + conversation_id = self._normalize_conversation_id(conversation_id, body) url = f"v3/conversations/{conversation_id}/activities/{activity_id}" logger.info( diff --git a/tests/hosting_core/connector/test_connector_client.py b/tests/hosting_core/connector/test_connector_client.py index 59ae34cb3..c91dbe06c 100644 --- a/tests/hosting_core/connector/test_connector_client.py +++ b/tests/hosting_core/connector/test_connector_client.py @@ -9,7 +9,8 @@ from aiohttp import web, ClientSession from aiohttp.test_utils import TestServer -from microsoft_agents.activity import Activity, ResourceResponse +from microsoft_agents.activity import Activity, Channels, ResourceResponse, RoleTypes +from microsoft_agents.activity.channel_account import ChannelAccount from microsoft_agents.hosting.core.connector.client.connector_client import ( ConversationsOperations, ) @@ -134,3 +135,223 @@ async def handler(request): assert result.id is None finally: await server.close() + + +class TestNormalizeConversationId: + """Tests for ConversationsOperations._normalize_conversation_id and _should_url_encode_conversation_id.""" + + def _make_ops(self): + return ConversationsOperations(None) + + # --- _should_url_encode_conversation_id --- + + @pytest.mark.parametrize( + "channel, role", + [ + (Channels.agents, RoleTypes.agentic_identity), + (Channels.agents, RoleTypes.agentic_user), + (Channels.ms_teams, RoleTypes.agentic_identity), + (Channels.ms_teams, RoleTypes.agentic_user), + ], + ) + def test_should_url_encode_when_agentic_channel_and_agentic_role(self, channel, role): + activity = Activity( + type="message", + channel_id=channel, + from_property=ChannelAccount(id="user1", role=role), + ) + assert ConversationsOperations._should_url_encode_conversation_id(activity) is True + + @pytest.mark.parametrize( + "channel", + [Channels.agents, Channels.ms_teams], + ) + @pytest.mark.parametrize( + "role", + [RoleTypes.user, RoleTypes.agent, RoleTypes.skill], + ) + def test_should_not_url_encode_when_non_agentic_role(self, channel, role): + activity = Activity( + type="message", + channel_id=channel, + from_property=ChannelAccount(id="user1", role=role), + ) + assert ConversationsOperations._should_url_encode_conversation_id(activity) is False + + @pytest.mark.parametrize( + "channel", + [Channels.email, Channels.direct_line, Channels.webchat, Channels.emulator], + ) + def test_should_not_url_encode_when_non_agentic_channel(self, channel): + activity = Activity( + type="message", + channel_id=channel, + from_property=ChannelAccount(id="user1", role=RoleTypes.agentic_identity), + ) + assert ConversationsOperations._should_url_encode_conversation_id(activity) is False + + def test_should_not_url_encode_when_no_channel_id(self): + activity = Activity( + type="message", + from_property=ChannelAccount(id="user1", role=RoleTypes.agentic_identity), + ) + assert ConversationsOperations._should_url_encode_conversation_id(activity) is False + + def test_should_not_url_encode_when_no_from(self): + activity = Activity(type="message", channel_id=Channels.agents) + assert ConversationsOperations._should_url_encode_conversation_id(activity) is False + + def test_should_not_url_encode_when_no_role(self): + activity = Activity( + type="message", + channel_id=Channels.agents, + from_property=ChannelAccount(id="user1"), + ) + assert ConversationsOperations._should_url_encode_conversation_id(activity) is False + + # --- _normalize_conversation_id --- + + def test_normalize_truncates_to_max_length(self): + ops = self._make_ops() + long_id = "a" * 200 + result = ops._normalize_conversation_id(long_id) + assert result == "a" * 150 + + def test_normalize_does_not_url_encode_without_activity(self): + ops = self._make_ops() + conv_id = "conv/with/slashes" + result = ops._normalize_conversation_id(conv_id) + assert result == conv_id + + def test_normalize_url_encodes_for_agents_channel_with_agentic_role(self): + ops = self._make_ops() + conv_id = "conv/with/slashes" + activity = Activity( + type="message", + channel_id=Channels.agents, + from_property=ChannelAccount(id="user1", role=RoleTypes.agentic_identity), + ) + result = ops._normalize_conversation_id(conv_id, activity) + assert result == "conv%2Fwith%2Fslashes" + + def test_normalize_url_encodes_for_agents_subchannel(self): + """Test that agents:email sub-channel also triggers URL encoding.""" + ops = self._make_ops() + conv_id = "conv/with/slashes" + activity = Activity( + type="message", + channel_id="agents:email", + from_property=ChannelAccount(id="user1", role=RoleTypes.agentic_user), + ) + result = ops._normalize_conversation_id(conv_id, activity) + assert result == "conv%2Fwith%2Fslashes" + + def test_normalize_url_encodes_for_msteams_with_agentic_role(self): + ops = self._make_ops() + conv_id = "conv/with/slashes" + activity = Activity( + type="message", + channel_id=Channels.ms_teams, + from_property=ChannelAccount(id="user1", role=RoleTypes.agentic_user), + ) + result = ops._normalize_conversation_id(conv_id, activity) + assert result == "conv%2Fwith%2Fslashes" + + def test_normalize_truncates_before_url_encoding(self): + ops = ConversationsOperations(None, max_conversation_id_length=5) + conv_id = "ab/cd/ef" + activity = Activity( + type="message", + channel_id=Channels.agents, + from_property=ChannelAccount(id="user1", role=RoleTypes.agentic_identity), + ) + # Truncated to 5 chars first: "ab/cd", then URL encoded + result = ops._normalize_conversation_id(conv_id, activity) + assert result == "ab%2Fcd" + + def test_normalize_no_url_encode_for_non_agentic_role_with_agents_channel(self): + ops = self._make_ops() + conv_id = "conv/with/slashes" + activity = Activity( + type="message", + channel_id=Channels.agents, + from_property=ChannelAccount(id="user1", role=RoleTypes.user), + ) + result = ops._normalize_conversation_id(conv_id, activity) + assert result == conv_id + + +class TestSendToConversationUrlEncoding: + """Integration tests: URL encoding of conversation_id in send_to_conversation.""" + + @pytest.mark.asyncio + async def test_send_to_conversation_encodes_conversation_id_for_agentic_agents_channel( + self, + ): + captured = {} + + async def handler(request): + captured["raw_path"] = request.raw_path + return web.json_response({"id": "resp-1"}) + + routes = [web.post("/v3/conversations/{tail:.*}/activities", handler)] + app = web.Application() + app.router.add_routes(routes) + + server = TestServer(app) + await server.start_server() + try: + async with ClientSession(base_url=server.make_url("/")) as session: + ops = ConversationsOperations(session) + activity = Activity( + type="message", + channel_id=Channels.agents, + from_property=ChannelAccount( + id="user1", role=RoleTypes.agentic_identity + ), + ) + await ops.send_to_conversation("conv/sub/id", activity) + + assert "conv%2Fsub%2Fid" in captured["raw_path"] + finally: + await server.close() + + +class TestReplyToActivityUrlEncoding: + """Integration tests: URL encoding of conversation_id in reply_to_activity.""" + + @pytest.mark.asyncio + async def test_reply_to_activity_encodes_conversation_id_for_agentic_agents_channel( + self, + ): + captured = {} + + async def handler(request): + captured["raw_path"] = request.raw_path + return web.json_response({"id": "resp-1"}) + + routes = [ + web.post( + "/v3/conversations/{tail:.*}/activities/{activity_id}", handler + ) + ] + app = web.Application() + app.router.add_routes(routes) + + server = TestServer(app) + await server.start_server() + try: + async with ClientSession(base_url=server.make_url("/")) as session: + ops = ConversationsOperations(session) + activity = Activity( + type="message", + channel_id=Channels.ms_teams, + from_property=ChannelAccount( + id="user1", role=RoleTypes.agentic_user + ), + ) + await ops.reply_to_activity("conv/sub/id", "act-1", activity) + + assert "conv%2Fsub%2Fid" in captured["raw_path"] + finally: + await server.close() From aff91f8858268e4c310fe9faedd1c23041e81970 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 2 Jun 2026 22:33:52 +0000 Subject: [PATCH 3/4] Replace HTTP path-special chars with _ in agents channel conversation IDs - Change approach from URL-encoding to replacing /, \, #, ? with _ - Restrict sanitization to agents channel only (remove msteams) - Rename _should_url_encode_conversation_id to _should_sanitize_conversation_id - Update tests accordingly --- .../core/connector/client/connector_client.py | 10 +- .../connector/test_connector_client.py | 98 ++++++++++--------- 2 files changed, 56 insertions(+), 52 deletions(-) diff --git a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/connector/client/connector_client.py b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/connector/client/connector_client.py index 9b358c401..3d15ae77f 100644 --- a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/connector/client/connector_client.py +++ b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/connector/client/connector_client.py @@ -4,8 +4,8 @@ """Connector Client for Microsoft Agents.""" import logging +import re from typing import Any, Optional -from urllib.parse import quote from aiohttp import ClientSession from io import BytesIO @@ -146,17 +146,17 @@ def _normalize_conversation_id( self, conversation_id: str, activity: Optional[Activity] = None ) -> str: trimmed = conversation_id[: self._max_conversation_id_length] - if activity is not None and self._should_url_encode_conversation_id(activity): - return quote(trimmed, safe="") + if activity is not None and self._should_sanitize_conversation_id(activity): + return re.sub(r"[/\\#?]", "_", trimmed) return trimmed @staticmethod - def _should_url_encode_conversation_id(activity: Activity) -> bool: + def _should_sanitize_conversation_id(activity: Activity) -> bool: channel_id = activity.channel_id if not channel_id: return False base_channel = channel_id.channel if isinstance(channel_id, ChannelId) else channel_id.split(":", 1)[0] - if base_channel not in (Channels.ms_teams, Channels.agents): + if base_channel != Channels.agents: return False from_property = activity.from_property if not from_property or not from_property.role: diff --git a/tests/hosting_core/connector/test_connector_client.py b/tests/hosting_core/connector/test_connector_client.py index c91dbe06c..90454ad70 100644 --- a/tests/hosting_core/connector/test_connector_client.py +++ b/tests/hosting_core/connector/test_connector_client.py @@ -138,76 +138,67 @@ async def handler(request): class TestNormalizeConversationId: - """Tests for ConversationsOperations._normalize_conversation_id and _should_url_encode_conversation_id.""" + """Tests for ConversationsOperations._normalize_conversation_id and _should_sanitize_conversation_id.""" def _make_ops(self): return ConversationsOperations(None) - # --- _should_url_encode_conversation_id --- + # --- _should_sanitize_conversation_id --- @pytest.mark.parametrize( - "channel, role", - [ - (Channels.agents, RoleTypes.agentic_identity), - (Channels.agents, RoleTypes.agentic_user), - (Channels.ms_teams, RoleTypes.agentic_identity), - (Channels.ms_teams, RoleTypes.agentic_user), - ], + "role", + [RoleTypes.agentic_identity, RoleTypes.agentic_user], ) - def test_should_url_encode_when_agentic_channel_and_agentic_role(self, channel, role): + def test_should_sanitize_when_agents_channel_and_agentic_role(self, role): activity = Activity( type="message", - channel_id=channel, + channel_id=Channels.agents, from_property=ChannelAccount(id="user1", role=role), ) - assert ConversationsOperations._should_url_encode_conversation_id(activity) is True + assert ConversationsOperations._should_sanitize_conversation_id(activity) is True - @pytest.mark.parametrize( - "channel", - [Channels.agents, Channels.ms_teams], - ) @pytest.mark.parametrize( "role", [RoleTypes.user, RoleTypes.agent, RoleTypes.skill], ) - def test_should_not_url_encode_when_non_agentic_role(self, channel, role): + def test_should_not_sanitize_when_non_agentic_role(self, role): activity = Activity( type="message", - channel_id=channel, + channel_id=Channels.agents, from_property=ChannelAccount(id="user1", role=role), ) - assert ConversationsOperations._should_url_encode_conversation_id(activity) is False + assert ConversationsOperations._should_sanitize_conversation_id(activity) is False @pytest.mark.parametrize( "channel", - [Channels.email, Channels.direct_line, Channels.webchat, Channels.emulator], + [Channels.ms_teams, Channels.email, Channels.direct_line, Channels.webchat, Channels.emulator], ) - def test_should_not_url_encode_when_non_agentic_channel(self, channel): + def test_should_not_sanitize_when_non_agents_channel(self, channel): activity = Activity( type="message", channel_id=channel, from_property=ChannelAccount(id="user1", role=RoleTypes.agentic_identity), ) - assert ConversationsOperations._should_url_encode_conversation_id(activity) is False + assert ConversationsOperations._should_sanitize_conversation_id(activity) is False - def test_should_not_url_encode_when_no_channel_id(self): + def test_should_not_sanitize_when_no_channel_id(self): activity = Activity( type="message", from_property=ChannelAccount(id="user1", role=RoleTypes.agentic_identity), ) - assert ConversationsOperations._should_url_encode_conversation_id(activity) is False + assert ConversationsOperations._should_sanitize_conversation_id(activity) is False - def test_should_not_url_encode_when_no_from(self): + def test_should_not_sanitize_when_no_from(self): activity = Activity(type="message", channel_id=Channels.agents) - assert ConversationsOperations._should_url_encode_conversation_id(activity) is False + assert ConversationsOperations._should_sanitize_conversation_id(activity) is False - def test_should_not_url_encode_when_no_role(self): + def test_should_not_sanitize_when_no_role(self): activity = Activity( type="message", channel_id=Channels.agents, from_property=ChannelAccount(id="user1"), ) - assert ConversationsOperations._should_url_encode_conversation_id(activity) is False + assert ConversationsOperations._should_sanitize_conversation_id(activity) is False # --- _normalize_conversation_id --- @@ -217,13 +208,13 @@ def test_normalize_truncates_to_max_length(self): result = ops._normalize_conversation_id(long_id) assert result == "a" * 150 - def test_normalize_does_not_url_encode_without_activity(self): + def test_normalize_does_not_sanitize_without_activity(self): ops = self._make_ops() conv_id = "conv/with/slashes" result = ops._normalize_conversation_id(conv_id) assert result == conv_id - def test_normalize_url_encodes_for_agents_channel_with_agentic_role(self): + def test_normalize_sanitizes_slashes_for_agents_channel_with_agentic_role(self): ops = self._make_ops() conv_id = "conv/with/slashes" activity = Activity( @@ -232,10 +223,22 @@ def test_normalize_url_encodes_for_agents_channel_with_agentic_role(self): from_property=ChannelAccount(id="user1", role=RoleTypes.agentic_identity), ) result = ops._normalize_conversation_id(conv_id, activity) - assert result == "conv%2Fwith%2Fslashes" + assert result == "conv_with_slashes" + + def test_normalize_sanitizes_all_path_chars_for_agents_channel(self): + """Test that /, \\, #, and ? are all replaced with _.""" + ops = self._make_ops() + conv_id = "conv/with\\special#chars?here" + activity = Activity( + type="message", + channel_id=Channels.agents, + from_property=ChannelAccount(id="user1", role=RoleTypes.agentic_user), + ) + result = ops._normalize_conversation_id(conv_id, activity) + assert result == "conv_with_special_chars_here" - def test_normalize_url_encodes_for_agents_subchannel(self): - """Test that agents:email sub-channel also triggers URL encoding.""" + def test_normalize_sanitizes_for_agents_subchannel(self): + """Test that agents:email sub-channel also triggers sanitization.""" ops = self._make_ops() conv_id = "conv/with/slashes" activity = Activity( @@ -244,9 +247,10 @@ def test_normalize_url_encodes_for_agents_subchannel(self): from_property=ChannelAccount(id="user1", role=RoleTypes.agentic_user), ) result = ops._normalize_conversation_id(conv_id, activity) - assert result == "conv%2Fwith%2Fslashes" + assert result == "conv_with_slashes" - def test_normalize_url_encodes_for_msteams_with_agentic_role(self): + def test_normalize_does_not_sanitize_for_msteams_with_agentic_role(self): + """msteams channel should NOT sanitize the conversation ID.""" ops = self._make_ops() conv_id = "conv/with/slashes" activity = Activity( @@ -255,9 +259,9 @@ def test_normalize_url_encodes_for_msteams_with_agentic_role(self): from_property=ChannelAccount(id="user1", role=RoleTypes.agentic_user), ) result = ops._normalize_conversation_id(conv_id, activity) - assert result == "conv%2Fwith%2Fslashes" + assert result == conv_id - def test_normalize_truncates_before_url_encoding(self): + def test_normalize_truncates_before_sanitizing(self): ops = ConversationsOperations(None, max_conversation_id_length=5) conv_id = "ab/cd/ef" activity = Activity( @@ -265,11 +269,11 @@ def test_normalize_truncates_before_url_encoding(self): channel_id=Channels.agents, from_property=ChannelAccount(id="user1", role=RoleTypes.agentic_identity), ) - # Truncated to 5 chars first: "ab/cd", then URL encoded + # Truncated to 5 chars first: "ab/cd", then sanitized result = ops._normalize_conversation_id(conv_id, activity) - assert result == "ab%2Fcd" + assert result == "ab_cd" - def test_normalize_no_url_encode_for_non_agentic_role_with_agents_channel(self): + def test_normalize_no_sanitize_for_non_agentic_role_with_agents_channel(self): ops = self._make_ops() conv_id = "conv/with/slashes" activity = Activity( @@ -282,10 +286,10 @@ def test_normalize_no_url_encode_for_non_agentic_role_with_agents_channel(self): class TestSendToConversationUrlEncoding: - """Integration tests: URL encoding of conversation_id in send_to_conversation.""" + """Integration tests: sanitization of conversation_id in send_to_conversation.""" @pytest.mark.asyncio - async def test_send_to_conversation_encodes_conversation_id_for_agentic_agents_channel( + async def test_send_to_conversation_sanitizes_conversation_id_for_agentic_agents_channel( self, ): captured = {} @@ -312,16 +316,16 @@ async def handler(request): ) await ops.send_to_conversation("conv/sub/id", activity) - assert "conv%2Fsub%2Fid" in captured["raw_path"] + assert "conv_sub_id" in captured["raw_path"] finally: await server.close() class TestReplyToActivityUrlEncoding: - """Integration tests: URL encoding of conversation_id in reply_to_activity.""" + """Integration tests: sanitization of conversation_id in reply_to_activity.""" @pytest.mark.asyncio - async def test_reply_to_activity_encodes_conversation_id_for_agentic_agents_channel( + async def test_reply_to_activity_sanitizes_conversation_id_for_agentic_agents_channel( self, ): captured = {} @@ -345,13 +349,13 @@ async def handler(request): ops = ConversationsOperations(session) activity = Activity( type="message", - channel_id=Channels.ms_teams, + channel_id=Channels.agents, from_property=ChannelAccount( id="user1", role=RoleTypes.agentic_user ), ) await ops.reply_to_activity("conv/sub/id", "act-1", activity) - assert "conv%2Fsub%2Fid" in captured["raw_path"] + assert "conv_sub_id" in captured["raw_path"] finally: await server.close() From cc6eb313d580b08ebd8327ae340819f54e0e7693 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 3 Jun 2026 16:25:40 +0000 Subject: [PATCH 4/4] Format connector client changes with black --- .../core/connector/client/connector_client.py | 11 ++++-- .../connector/test_connector_client.py | 36 +++++++++++++------ 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/connector/client/connector_client.py b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/connector/client/connector_client.py index 3d15ae77f..28562e7ef 100644 --- a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/connector/client/connector_client.py +++ b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/connector/client/connector_client.py @@ -155,13 +155,20 @@ def _should_sanitize_conversation_id(activity: Activity) -> bool: channel_id = activity.channel_id if not channel_id: return False - base_channel = channel_id.channel if isinstance(channel_id, ChannelId) else channel_id.split(":", 1)[0] + base_channel = ( + channel_id.channel + if isinstance(channel_id, ChannelId) + else channel_id.split(":", 1)[0] + ) if base_channel != Channels.agents: return False from_property = activity.from_property if not from_property or not from_property.role: return False - return from_property.role in (RoleTypes.agentic_identity, RoleTypes.agentic_user) + return from_property.role in ( + RoleTypes.agentic_identity, + RoleTypes.agentic_user, + ) async def get_conversations( self, continuation_token: Optional[str] = None diff --git a/tests/hosting_core/connector/test_connector_client.py b/tests/hosting_core/connector/test_connector_client.py index 90454ad70..2a8db01d3 100644 --- a/tests/hosting_core/connector/test_connector_client.py +++ b/tests/hosting_core/connector/test_connector_client.py @@ -155,7 +155,9 @@ def test_should_sanitize_when_agents_channel_and_agentic_role(self, role): channel_id=Channels.agents, from_property=ChannelAccount(id="user1", role=role), ) - assert ConversationsOperations._should_sanitize_conversation_id(activity) is True + assert ( + ConversationsOperations._should_sanitize_conversation_id(activity) is True + ) @pytest.mark.parametrize( "role", @@ -167,11 +169,19 @@ def test_should_not_sanitize_when_non_agentic_role(self, role): channel_id=Channels.agents, from_property=ChannelAccount(id="user1", role=role), ) - assert ConversationsOperations._should_sanitize_conversation_id(activity) is False + assert ( + ConversationsOperations._should_sanitize_conversation_id(activity) is False + ) @pytest.mark.parametrize( "channel", - [Channels.ms_teams, Channels.email, Channels.direct_line, Channels.webchat, Channels.emulator], + [ + Channels.ms_teams, + Channels.email, + Channels.direct_line, + Channels.webchat, + Channels.emulator, + ], ) def test_should_not_sanitize_when_non_agents_channel(self, channel): activity = Activity( @@ -179,18 +189,24 @@ def test_should_not_sanitize_when_non_agents_channel(self, channel): channel_id=channel, from_property=ChannelAccount(id="user1", role=RoleTypes.agentic_identity), ) - assert ConversationsOperations._should_sanitize_conversation_id(activity) is False + assert ( + ConversationsOperations._should_sanitize_conversation_id(activity) is False + ) def test_should_not_sanitize_when_no_channel_id(self): activity = Activity( type="message", from_property=ChannelAccount(id="user1", role=RoleTypes.agentic_identity), ) - assert ConversationsOperations._should_sanitize_conversation_id(activity) is False + assert ( + ConversationsOperations._should_sanitize_conversation_id(activity) is False + ) def test_should_not_sanitize_when_no_from(self): activity = Activity(type="message", channel_id=Channels.agents) - assert ConversationsOperations._should_sanitize_conversation_id(activity) is False + assert ( + ConversationsOperations._should_sanitize_conversation_id(activity) is False + ) def test_should_not_sanitize_when_no_role(self): activity = Activity( @@ -198,7 +214,9 @@ def test_should_not_sanitize_when_no_role(self): channel_id=Channels.agents, from_property=ChannelAccount(id="user1"), ) - assert ConversationsOperations._should_sanitize_conversation_id(activity) is False + assert ( + ConversationsOperations._should_sanitize_conversation_id(activity) is False + ) # --- _normalize_conversation_id --- @@ -335,9 +353,7 @@ async def handler(request): return web.json_response({"id": "resp-1"}) routes = [ - web.post( - "/v3/conversations/{tail:.*}/activities/{activity_id}", handler - ) + web.post("/v3/conversations/{tail:.*}/activities/{activity_id}", handler) ] app = web.Application() app.router.add_routes(routes)