From 40538055f2111f857a21e5ee0bde6e3d5acece47 Mon Sep 17 00:00:00 2001 From: liuhao1024 Date: Thu, 28 May 2026 05:32:38 +0800 Subject: [PATCH] fix(telegram): raise command menu cap from 30 to 100 so plugin commands appear MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #33480 MAX_COMMANDS_PER_SCOPE was hardcoded to 30, which caused all plugin slash commands to be truncated from the Telegram bot menu. Built-in commands fill 22 priority-0 slots, leaving only 8 for remaining commands — plugin commands (priority 1) were always pushed out. Telegram's Bot API officially supports up to 100 commands via setMyCommands. Raise the cap to 100 so plugin and skill commands are included in the menu alongside built-ins. Also fixes the log message to reference MAX_COMMANDS_PER_SCOPE instead of the hardcoded literal 30. --- gateway/platforms/telegram.py | 9 ++++----- tests/hermes_cli/test_commands.py | 32 +++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/gateway/platforms/telegram.py b/gateway/platforms/telegram.py index 300fc49c04faa..0fbc189f3db9e 100644 --- a/gateway/platforms/telegram.py +++ b/gateway/platforms/telegram.py @@ -105,7 +105,7 @@ class _MockContextTypes: } -MAX_COMMANDS_PER_SCOPE = 30 +MAX_COMMANDS_PER_SCOPE = 100 def check_telegram_requirements() -> bool: @@ -1693,9 +1693,8 @@ def _polling_error_callback(error: Exception) -> None: BotCommandScopeChat, ) from hermes_cli.commands import telegram_menu_commands - # Telegram allows up to 100 commands but has an undocumented - # payload size limit (~4KB total). Limit to 30 core commands - # to stay well under the threshold while covering all categories. + # Telegram allows up to 100 commands via setMyCommands. + # Register all discovered commands (core + plugin + skill). menu_commands, hidden_count = telegram_menu_commands(max_commands=MAX_COMMANDS_PER_SCOPE) bot_commands = [BotCommand(name, desc) for name, desc in menu_commands] # Register for all scopes independently — Telegram picks the @@ -1715,7 +1714,7 @@ def _polling_error_callback(error: Exception) -> None: if hidden_count: logger.info( "[%s] Telegram menu: %d commands registered, %d hidden (over %d limit). Use /commands for full list.", - self.name, len(menu_commands), hidden_count, 30, + self.name, len(menu_commands), hidden_count, MAX_COMMANDS_PER_SCOPE, ) except Exception as e: logger.warning( diff --git a/tests/hermes_cli/test_commands.py b/tests/hermes_cli/test_commands.py index 7324adbe4302a..e7cbb549106f3 100644 --- a/tests/hermes_cli/test_commands.py +++ b/tests/hermes_cli/test_commands.py @@ -975,6 +975,38 @@ def test_operational_builtins_survive_thirty_command_cap(self, tmp_path, monkeyp ): assert name in names + + def test_plugin_commands_survive_hundred_command_cap(self, tmp_path, monkeypatch): + """Regression test for #33480: plugin commands must appear in Telegram menu. + + With MAX_COMMANDS_PER_SCOPE raised from 30 to 100, plugin commands + should no longer be truncated by the built-in command priority. + """ + from unittest.mock import patch + import hermes_cli.plugins as plugins_mod + + plugin_dir = tmp_path / "plugins" / "test-plugin" + plugin_dir.mkdir(parents=True, exist_ok=True) + (plugin_dir / "plugin.yaml").write_text( + "name: test-plugin\nversion: 0.1.0\ndescription: Test plugin\n" + ) + (plugin_dir / "__init__.py").write_text( + "def register(ctx):\n" + " ctx.register_command('my_plugin_cmd', lambda args: 'ok', description='Plugin test command')\n" + ) + (tmp_path / "config.yaml").write_text( + "plugins:\n enabled:\n - test-plugin\n" + ) + monkeypatch.setenv("HERMES_HOME", str(tmp_path)) + + with patch.object(plugins_mod, "_plugin_manager", None): + menu, _ = telegram_menu_commands(max_commands=100) + + menu_names = {name for name, _ in menu} + assert "my_plugin_cmd" in menu_names, ( + "Plugin command 'my_plugin_cmd' should appear in Telegram menu " + "with 100-command cap (#33480 regression)" + ) def test_includes_plugin_commands_via_lazy_discovery(self, tmp_path, monkeypatch): """Telegram menu generation should discover plugin slash commands on first access.""" from unittest.mock import patch