diff --git a/cron/scheduler.py b/cron/scheduler.py index b585ef2e42ba..f17bf996d88d 100644 --- a/cron/scheduler.py +++ b/cron/scheduler.py @@ -112,6 +112,7 @@ def _resolve_cron_enabled_toolsets(job: dict, cfg: dict) -> list[str] | None: "bluebubbles": "BLUEBUBBLES_HOME_CHANNEL", "qqbot": "QQBOT_HOME_CHANNEL", "whatsapp": "WHATSAPP_HOME_CHANNEL", + "yuanbao": "YUANBAO_HOME_CHANNEL", } # Legacy env var names kept for back-compat. Each entry is the current diff --git a/gateway/run.py b/gateway/run.py index 46c508e4bde0..1cabf7b744b3 100644 --- a/gateway/run.py +++ b/gateway/run.py @@ -7029,6 +7029,7 @@ async def _handle_message_with_agent(self, event, source, _quick_key: str, run_g await self.hooks.emit("session:start", { "platform": source.platform.value if source.platform else "", "user_id": source.user_id, + "chat_id": source.chat_id or "", "session_id": session_entry.session_id, "session_key": session_key, }) @@ -8161,6 +8162,7 @@ async def _handle_reset_command(self, event: MessageEvent) -> Union[str, Ephemer await self.hooks.emit("session:end", { "platform": source.platform.value if source.platform else "", "user_id": source.user_id, + "chat_id": source.chat_id or "", "session_key": session_key, }) @@ -8168,6 +8170,7 @@ async def _handle_reset_command(self, event: MessageEvent) -> Union[str, Ephemer await self.hooks.emit("session:reset", { "platform": source.platform.value if source.platform else "", "user_id": source.user_id, + "chat_id": source.chat_id or "", "session_key": session_key, }) diff --git a/tests/gateway/test_session_boundary_hooks.py b/tests/gateway/test_session_boundary_hooks.py index 255795492fc7..bfbb44243791 100644 --- a/tests/gateway/test_session_boundary_hooks.py +++ b/tests/gateway/test_session_boundary_hooks.py @@ -99,6 +99,40 @@ async def test_reset_fires_reset_hook(mock_invoke_hook): ) +@pytest.mark.asyncio +@patch("hermes_cli.plugins.invoke_hook") +async def test_session_end_hook_includes_chat_id(mock_invoke_hook): + """session:end gateway hook must carry chat_id so hook authors can route replies.""" + runner = _make_runner() + + await runner._handle_reset_command(_make_event("/new")) + + end_calls = [ + c for c in runner.hooks.emit.call_args_list + if c.args and c.args[0] == "session:end" + ] + assert end_calls, "session:end was not emitted" + ctx = end_calls[0].args[1] + assert ctx.get("chat_id") == "c1" + + +@pytest.mark.asyncio +@patch("hermes_cli.plugins.invoke_hook") +async def test_session_reset_hook_includes_chat_id(mock_invoke_hook): + """session:reset gateway hook must carry chat_id so hook authors can route replies.""" + runner = _make_runner() + + await runner._handle_reset_command(_make_event("/new")) + + reset_calls = [ + c for c in runner.hooks.emit.call_args_list + if c.args and c.args[0] == "session:reset" + ] + assert reset_calls, "session:reset was not emitted" + ctx = reset_calls[0].args[1] + assert ctx.get("chat_id") == "c1" + + @pytest.mark.asyncio @patch("hermes_cli.plugins.invoke_hook") async def test_finalize_before_reset(mock_invoke_hook):