diff --git a/nanobot/channels/telegram.py b/nanobot/channels/telegram.py index 8cc064704..c88f1080c 100644 --- a/nanobot/channels/telegram.py +++ b/nanobot/channels/telegram.py @@ -261,12 +261,21 @@ class TelegramChannel(BaseChannel): BotCommand("restart", "Restart the bot"), BotCommand("status", "Show bot status"), BotCommand("history", "Show recent conversation messages"), + BotCommand("goal", "Start a sustained objective (long-running task)"), + BotCommand("pairing", "Manage DM pairing (approve/deny/list)"), + BotCommand("model", "Switch runtime model preset"), BotCommand("dream", "Run Dream memory consolidation now"), BotCommand("dream_log", "Show the latest Dream memory change"), BotCommand("dream_restore", "Restore Dream memory to an earlier version"), BotCommand("help", "Show available commands"), ] + # Regex for slash commands routed to AgentLoop via ``_forward_command``. + # Hyphenated ``dream-*`` commands stay on a separate handler (below). + TELEGRAM_BUS_SLASH_COMMAND_RE = re.compile( + r"^/(?:new|stop|restart|status|dream|history|goal|pairing|model)(?:@\w+)?(?:\s+.*)?$" + ) + @classmethod def default_config(cls) -> dict[str, Any]: return TelegramConfig().model_dump(by_alias=True) @@ -354,7 +363,7 @@ class TelegramChannel(BaseChannel): self._app.add_handler(MessageHandler(filters.Regex(r"^/start(?:@\w+)?$"), self._on_start)) self._app.add_handler( MessageHandler( - filters.Regex(r"^/(new|stop|restart|status|dream)(?:@\w+)?(?:\s+.*)?$"), + filters.Regex(TelegramChannel.TELEGRAM_BUS_SLASH_COMMAND_RE), self._forward_command, ) ) diff --git a/tests/channels/test_telegram_channel.py b/tests/channels/test_telegram_channel.py index 95865096c..362bfbea9 100644 --- a/tests/channels/test_telegram_channel.py +++ b/tests/channels/test_telegram_channel.py @@ -1294,6 +1294,20 @@ async def test_forward_command_normalizes_telegram_safe_dream_aliases() -> None: assert handled[0]["content"] == "/dream-restore deadbeef" +def test_telegram_bus_slash_command_regex_matches_agent_loop_commands() -> None: + """Bus-routed slash commands must match the Telegram handler regex (see builtin router).""" + pat = TelegramChannel.TELEGRAM_BUS_SLASH_COMMAND_RE + assert pat.fullmatch("/history") + assert pat.fullmatch("/history 5") + assert pat.fullmatch("/goal ship the feature") + assert pat.fullmatch("/pairing list") + assert pat.fullmatch("/model fast") + assert pat.fullmatch("/new@nanobot_bot") + assert pat.fullmatch("/goal@nanobot_bot refine objective") + assert pat.fullmatch("/dream-log deadbeef") is None + assert pat.fullmatch("/dream-restore deadbeef") is None + + @pytest.mark.asyncio async def test_on_help_includes_restart_command() -> None: channel = TelegramChannel( @@ -1311,6 +1325,9 @@ async def test_on_help_includes_restart_command() -> None: assert "/status" in help_text assert "/dream" in help_text assert "/dream-log" in help_text + assert "/goal" in help_text + assert "/pairing" in help_text + assert "/model" in help_text assert "/dream-restore" in help_text