From c4a92b3deff85a72006749c7b9ddda109ce650ad Mon Sep 17 00:00:00 2001 From: CeciliaAvila Date: Tue, 1 Sep 2026 16:29:20 -0300 Subject: [PATCH 1/4] Stop typing on update or delete activity --- .../hosting/core/app/typing_indicator.py | 10 ++++ .../hosting_core/app/test_typing_indicator.py | 46 +++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/typing_indicator.py b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/typing_indicator.py index c73f1c99..0eefa96c 100644 --- a/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/typing_indicator.py +++ b/libraries/microsoft-agents-hosting-core/microsoft_agents/hosting/core/app/typing_indicator.py @@ -217,7 +217,17 @@ async def _on_send_activities_handler(ctx, activities, next_handler): self._stop_loop() return await next_handler() + async def _on_update_activity_handler(ctx, activity, next_handler): + self._stop_loop() + return await next_handler() + + async def _on_delete_activity_handler(ctx, reference, next_handler): + self._stop_loop() + return await next_handler() + self._context.on_send_activities(_on_send_activities_handler) + self._context.on_update_activity(_on_update_activity_handler) + self._context.on_delete_activity(_on_delete_activity_handler) self._hook_registered = True def _stop_loop(self) -> None: diff --git a/tests/hosting_core/app/test_typing_indicator.py b/tests/hosting_core/app/test_typing_indicator.py index 609f4261..fc4667a2 100644 --- a/tests/hosting_core/app/test_typing_indicator.py +++ b/tests/hosting_core/app/test_typing_indicator.py @@ -46,10 +46,18 @@ def __init__(self, should_raise: bool = False, channel_id: str = "test") -> None recipient={"id": "user"}, ) self._on_send_handlers = [] + self._on_update_handlers = [] + self._on_delete_handlers = [] def on_send_activities(self, handler): self._on_send_handlers.append(handler) + def on_update_activity(self, handler): + self._on_update_handlers.append(handler) + + def on_delete_activity(self, handler): + self._on_delete_handlers.append(handler) + @property def sent_activities(self): return self.adapter.sent_activities @@ -334,6 +342,44 @@ async def _next_handler(): await indicator._stop_async() +@pytest.mark.asyncio +@pytest.mark.parametrize( + ("handlers_attribute", "mutation"), + [ + ("_on_update_handlers", Activity(type=ActivityTypes.message)), + ("_on_delete_handlers", "activity-id"), + ], +) +async def test_mutation_hook_stops_before_initial_typing_send( + handlers_attribute, mutation +): + """Update and delete operations should cancel typing before the mutation.""" + context = StubTurnContext() + opts = _fast_options(initial_delay_ms=50, interval_ms=10) + indicator = TypingIndicator(context, typing_options=opts) + indicator.start() + + handlers = getattr(context, handlers_attribute) + assert len(handlers) == 1 + + next_handler_called = False + + async def _next_handler(): + nonlocal next_handler_called + next_handler_called = True + return "mutation-result" + + result = await handlers[0](context, mutation, _next_handler) + await asyncio.sleep(0.075) + + assert result == "mutation-result" + assert next_handler_called + assert indicator._stopped + assert context.sent_activities == [] + + await indicator._stop_async() + + # --------------------------------------------------------------------------- # Per-channel strategy tests # --------------------------------------------------------------------------- From 3f97b2acfdf64dbcc385ecfbd64a5ed7ea838e52 Mon Sep 17 00:00:00 2001 From: Cecilia Avila <44245136+ceciliaavila@users.noreply.github.com> Date: Tue, 1 Sep 2026 16:52:26 -0300 Subject: [PATCH 2/4] Improve newly added tests Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- tests/hosting_core/app/test_typing_indicator.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/tests/hosting_core/app/test_typing_indicator.py b/tests/hosting_core/app/test_typing_indicator.py index fc4667a2..d303785b 100644 --- a/tests/hosting_core/app/test_typing_indicator.py +++ b/tests/hosting_core/app/test_typing_indicator.py @@ -49,14 +49,17 @@ def __init__(self, should_raise: bool = False, channel_id: str = "test") -> None self._on_update_handlers = [] self._on_delete_handlers = [] - def on_send_activities(self, handler): - self._on_send_handlers.append(handler) +def on_send_activities(self, handler): + self._on_send_handlers.append(handler) + return self - def on_update_activity(self, handler): - self._on_update_handlers.append(handler) +def on_update_activity(self, handler): + self._on_update_handlers.append(handler) + return self - def on_delete_activity(self, handler): - self._on_delete_handlers.append(handler) +def on_delete_activity(self, handler): + self._on_delete_handlers.append(handler) + return self @property def sent_activities(self): From 5a20abb76e9cab8be0639d19f9becedb409c3894 Mon Sep 17 00:00:00 2001 From: CeciliaAvila Date: Tue, 1 Sep 2026 16:54:10 -0300 Subject: [PATCH 3/4] Fix indentation --- .../hosting_core/app/test_typing_indicator.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/hosting_core/app/test_typing_indicator.py b/tests/hosting_core/app/test_typing_indicator.py index d303785b..e8ca6dc9 100644 --- a/tests/hosting_core/app/test_typing_indicator.py +++ b/tests/hosting_core/app/test_typing_indicator.py @@ -49,17 +49,17 @@ def __init__(self, should_raise: bool = False, channel_id: str = "test") -> None self._on_update_handlers = [] self._on_delete_handlers = [] -def on_send_activities(self, handler): - self._on_send_handlers.append(handler) - return self + def on_send_activities(self, handler): + self._on_send_handlers.append(handler) + return self -def on_update_activity(self, handler): - self._on_update_handlers.append(handler) - return self + def on_update_activity(self, handler): + self._on_update_handlers.append(handler) + return self -def on_delete_activity(self, handler): - self._on_delete_handlers.append(handler) - return self + def on_delete_activity(self, handler): + self._on_delete_handlers.append(handler) + return self @property def sent_activities(self): From 550f5ad8b2fdeaa36977e661d66de668c022577e Mon Sep 17 00:00:00 2001 From: Cecilia Avila <44245136+ceciliaavila@users.noreply.github.com> Date: Thu, 3 Sep 2026 10:01:39 -0300 Subject: [PATCH 4/4] Fix unit test Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- tests/hosting_core/app/test_typing_indicator.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/tests/hosting_core/app/test_typing_indicator.py b/tests/hosting_core/app/test_typing_indicator.py index e8ca6dc9..4fd3cf2b 100644 --- a/tests/hosting_core/app/test_typing_indicator.py +++ b/tests/hosting_core/app/test_typing_indicator.py @@ -347,21 +347,24 @@ async def _next_handler(): @pytest.mark.asyncio @pytest.mark.parametrize( - ("handlers_attribute", "mutation"), + "handlers_attribute", [ - ("_on_update_handlers", Activity(type=ActivityTypes.message)), - ("_on_delete_handlers", "activity-id"), + "_on_update_handlers", + "_on_delete_handlers", ], ) -async def test_mutation_hook_stops_before_initial_typing_send( - handlers_attribute, mutation -): +async def test_mutation_hook_stops_before_initial_typing_send(handlers_attribute): """Update and delete operations should cancel typing before the mutation.""" context = StubTurnContext() opts = _fast_options(initial_delay_ms=50, interval_ms=10) indicator = TypingIndicator(context, typing_options=opts) indicator.start() + mutation = Activity(type=ActivityTypes.message) + if handlers_attribute == "_on_delete_handlers": + mutation = context.activity.get_conversation_reference() + mutation.activity_id = "activity-id" + handlers = getattr(context, handlers_attribute) assert len(handlers) == 1