mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-05 02:31:47 +00:00
fix: enforce priority order in Telegram menu — core > plugins > skills (#4023)
The menu now has explicit priority tiers: 1. Core CommandDef commands (always included, never bumped) 2. Plugin slash commands (take precedence over skills) 3. Built-in skill commands (fill remaining slots alphabetically) Only skills get trimmed when the 100-command cap is hit. Adding new core commands or plugin commands automatically pushes skills out, not the other way around.
This commit is contained in:
parent
86250a3e45
commit
158f49f19a
1 changed files with 31 additions and 14 deletions
|
|
@ -366,27 +366,41 @@ def telegram_bot_commands() -> list[tuple[str, str]]:
|
||||||
|
|
||||||
|
|
||||||
def telegram_menu_commands(max_commands: int = 100) -> tuple[list[tuple[str, str]], int]:
|
def telegram_menu_commands(max_commands: int = 100) -> tuple[list[tuple[str, str]], int]:
|
||||||
"""Return Telegram menu commands (built-in + active skills), capped to the Bot API limit.
|
"""Return Telegram menu commands capped to the Bot API limit.
|
||||||
|
|
||||||
Built-in commands come first, then active skill commands. Commands beyond
|
Priority order (higher priority = never bumped by overflow):
|
||||||
``max_commands`` remain callable in the gateway; they are just omitted from
|
1. Core CommandDef commands (always included)
|
||||||
Telegram's native slash-command picker.
|
2. Plugin slash commands (take precedence over skills)
|
||||||
|
3. Built-in skill commands (fill remaining slots, alphabetical)
|
||||||
|
|
||||||
|
Skills are the only tier that gets trimmed when the cap is hit.
|
||||||
|
User-installed hub skills are excluded — accessible via /skills.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
(menu_commands, hidden_count) where hidden_count is the number of
|
(menu_commands, hidden_count) where hidden_count is the number of
|
||||||
commands omitted due to the cap.
|
skill commands omitted due to the cap.
|
||||||
"""
|
"""
|
||||||
all_commands = list(telegram_bot_commands())
|
all_commands = list(telegram_bot_commands())
|
||||||
|
|
||||||
# Append active BUILT-IN skill commands only (not user-installed hub skills).
|
# Plugin slash commands get priority over skills
|
||||||
# User-installed skills stay accessible via /skills and by typing the command
|
try:
|
||||||
# directly, but don't clutter the Telegram menu.
|
from hermes_cli.plugins import get_plugin_manager
|
||||||
|
pm = get_plugin_manager()
|
||||||
|
plugin_cmds = getattr(pm, "_plugin_commands", {})
|
||||||
|
for cmd_name in sorted(plugin_cmds):
|
||||||
|
tg_name = cmd_name.replace("-", "_")
|
||||||
|
desc = "Plugin command"
|
||||||
|
if len(desc) > 40:
|
||||||
|
desc = desc[:37] + "..."
|
||||||
|
all_commands.append((tg_name, desc))
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Remaining slots go to built-in skill commands (not hub-installed).
|
||||||
|
skill_entries: list[tuple[str, str]] = []
|
||||||
try:
|
try:
|
||||||
from agent.skill_commands import get_skill_commands
|
from agent.skill_commands import get_skill_commands
|
||||||
from tools.skills_tool import SKILLS_DIR
|
from tools.skills_tool import SKILLS_DIR
|
||||||
# Built-in skills are synced to SKILLS_DIR (~/.hermes/skills/).
|
|
||||||
# Hub-installed skills go into SKILLS_DIR/.hub/. Exclude .hub/ skills
|
|
||||||
# from the menu — they're user-installed, not repo built-in.
|
|
||||||
_skills_dir = str(SKILLS_DIR.resolve())
|
_skills_dir = str(SKILLS_DIR.resolve())
|
||||||
_hub_dir = str((SKILLS_DIR / ".hub").resolve())
|
_hub_dir = str((SKILLS_DIR / ".hub").resolve())
|
||||||
skill_cmds = get_skill_commands()
|
skill_cmds = get_skill_commands()
|
||||||
|
|
@ -396,18 +410,21 @@ def telegram_menu_commands(max_commands: int = 100) -> tuple[list[tuple[str, str
|
||||||
if not skill_path.startswith(_skills_dir):
|
if not skill_path.startswith(_skills_dir):
|
||||||
continue
|
continue
|
||||||
if skill_path.startswith(_hub_dir):
|
if skill_path.startswith(_hub_dir):
|
||||||
continue # hub-installed, not built-in
|
continue
|
||||||
name = cmd_key.lstrip("/").replace("-", "_")
|
name = cmd_key.lstrip("/").replace("-", "_")
|
||||||
desc = info.get("description", "")
|
desc = info.get("description", "")
|
||||||
# Keep descriptions short — setMyCommands has an undocumented
|
# Keep descriptions short — setMyCommands has an undocumented
|
||||||
# total payload limit. 40 chars fits 100 commands safely.
|
# total payload limit. 40 chars fits 100 commands safely.
|
||||||
if len(desc) > 40:
|
if len(desc) > 40:
|
||||||
desc = desc[:37] + "..."
|
desc = desc[:37] + "..."
|
||||||
all_commands.append((name, desc))
|
skill_entries.append((name, desc))
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
hidden_count = max(0, len(all_commands) - max_commands)
|
# Skills fill remaining slots — they're the only tier that gets trimmed
|
||||||
|
remaining_slots = max(0, max_commands - len(all_commands))
|
||||||
|
hidden_count = max(0, len(skill_entries) - remaining_slots)
|
||||||
|
all_commands.extend(skill_entries[:remaining_slots])
|
||||||
return all_commands[:max_commands], hidden_count
|
return all_commands[:max_commands], hidden_count
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue