@@ -790,6 +790,62 @@ async def mock_stream():
790790 assert getattr (engaging , "decision_path" , None ) == "name_mentioned_fast_path"
791791 assert getattr (engaging , "engagement_reason" , None ) == "name_mentioned"
792792
793+ async def test_handle_passive_message_response_policy_skips_response_but_still_extracts (
794+ self , mock_provider , mock_agent , caplog
795+ ):
796+ """Response policy should suppress passive replies while still extracting."""
797+ import asyncio
798+ import logging
799+
800+ from ash .config .models import PassiveListeningConfig
801+ from ash .providers .base import IncomingMessage
802+ from ash .providers .telegram .handlers import TelegramMessageHandler
803+
804+ mock_provider .passive_config = PassiveListeningConfig (
805+ enabled = True ,
806+ response_allowed_chats = ["group_allowed" ],
807+ )
808+ mock_provider .bot_username = "ash_bot"
809+
810+ handler = TelegramMessageHandler (
811+ provider = mock_provider ,
812+ agent = mock_agent ,
813+ streaming = False ,
814+ )
815+
816+ mock_decider = MagicMock ()
817+ mock_decider .decide = AsyncMock (return_value = True )
818+ handler ._passive_handler ._passive_decider = mock_decider # type: ignore[union-attr]
819+ handler ._passive_handler ._memory_manager = MagicMock () # type: ignore[union-attr]
820+ handler ._passive_handler ._passive_extractor = MagicMock () # type: ignore[union-attr]
821+ handler ._passive_handler ._extract_passive_memories = AsyncMock () # type: ignore[method-assign]
822+
823+ passive_message = IncomingMessage (
824+ id = "99" ,
825+ chat_id = "group_blocked" ,
826+ user_id = "user_456" ,
827+ text = "normal group chatter" ,
828+ username = "otheruser" ,
829+ display_name = "Other User" ,
830+ )
831+
832+ with caplog .at_level (logging .INFO , logger = "telegram" ):
833+ await handler .handle_passive_message (passive_message )
834+ await asyncio .sleep (0 )
835+
836+ mock_decider .decide .assert_not_called ()
837+ handler ._passive_handler ._extract_passive_memories .assert_awaited_once_with ( # type: ignore[attr-defined]
838+ passive_message
839+ )
840+ mock_agent .process_message .assert_not_called ()
841+ mock_agent .process_message_streaming .assert_not_called ()
842+ skipped = next (
843+ (r for r in caplog .records if r .msg == "passive_engagement_skipped" ), None
844+ )
845+ assert skipped is not None
846+ assert getattr (skipped , "decision_path" , None ) == "response_policy"
847+ assert getattr (skipped , "engagement_reason" , None ) == "response_policy"
848+
793849 async def test_handle_passive_message_direct_followup_bypasses_throttle_but_uses_decider (
794850 self , mock_provider , mock_agent , tmp_path , caplog
795851 ):
0 commit comments