mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-29 01:31:41 +00:00
Implements the full multi-agent system for Hermes Agent, allowing a single
installation to host multiple named agents, each with its own model,
personality, toolset, workspace, and session history.
## New Files
- gateway/agent_registry.py: AgentConfig, ToolPolicy, SubagentPolicy,
AgentRegistry, TOOL_PROFILES (minimal/coding/messaging/full), and
normalize_tool_config() for shorthand YAML parsing
- gateway/router.py: BindingRouter with 7-tier deterministic routing
(chat_id > peer > guild+type > guild > platform+type > platform > default)
## Core Changes
- model_tools.py: get_tool_definitions() accepts agent_tool_policy for
per-agent tool filtering; handle_function_call() extended enabled_tools
check to gate ALL tool calls (defense-in-depth)
- gateway/session.py: build_session_key() now accepts agent_id and dm_scope
parameters, replacing hardcoded 'agent:main' with 'agent:{agent_id}'
- tools/memory_tool.py: MemoryStore accepts memory_dir parameter for
per-agent memory isolation
- agent/prompt_builder.py: build_context_files_prompt() accepts
agent_workspace for SOUL.md lookup; build_skills_system_prompt()
accepts agent_skills_dir for per-agent skill overlay
- run_agent.py: AIAgent accepts agent_tool_policy and agent_workspace,
passes policy through to get_tool_definitions()
- gateway/run.py: Initializes AgentRegistry + BindingRouter, resolves
agent per-message in _handle_message(), passes config to _run_agent(),
adds /agents command
- cli.py: --agent flag for selecting named agent profiles, /agents
slash command, agent config override for model/personality/tools
- hermes_cli/config.py: agents/bindings in DEFAULT_CONFIG, version 7
- tools/delegate_tool.py: Configurable max_depth per-agent, tool policy
inheritance from parent to child
## Config Format
agents:
main:
default: true
coder:
model: anthropic/claude-sonnet-4
personality: 'You are a coding assistant.'
tools: coding # or [tool1, tool2] or {profile: x, deny: [...]}
bindings:
- agent: coder
telegram: '-100123456'
## Tests
168 new tests across 3 test files (agent_registry, router, integration).
All 3106 tests pass.
104 lines
4.2 KiB
Python
104 lines
4.2 KiB
Python
"""Slash command definitions and autocomplete for the Hermes CLI.
|
|
|
|
Contains the shared built-in ``COMMANDS`` dict and ``SlashCommandCompleter``.
|
|
The completer can optionally include dynamic skill slash commands supplied by the
|
|
interactive CLI.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from collections.abc import Callable, Mapping
|
|
from typing import Any
|
|
|
|
from prompt_toolkit.completion import Completer, Completion
|
|
|
|
|
|
COMMANDS = {
|
|
"/help": "Show this help message",
|
|
"/tools": "List available tools",
|
|
"/toolsets": "List available toolsets",
|
|
"/model": "Show or change the current model",
|
|
"/provider": "Show available providers and current provider",
|
|
"/prompt": "View/set custom system prompt",
|
|
"/personality": "Set a predefined personality",
|
|
"/clear": "Clear screen and reset conversation (fresh start)",
|
|
"/history": "Show conversation history",
|
|
"/new": "Start a new conversation (reset history)",
|
|
"/reset": "Reset conversation only (keep screen)",
|
|
"/retry": "Retry the last message (resend to agent)",
|
|
"/undo": "Remove the last user/assistant exchange",
|
|
"/save": "Save the current conversation",
|
|
"/config": "Show current configuration",
|
|
"/agents": "List configured agents and active bindings",
|
|
"/cron": "Manage scheduled tasks (list, add, remove)",
|
|
"/skills": "Search, install, inspect, or manage skills from online registries",
|
|
"/platforms": "Show gateway/messaging platform status",
|
|
"/verbose": "Cycle tool progress display: off → new → all → verbose",
|
|
"/compress": "Manually compress conversation context (flush memories + summarize)",
|
|
"/title": "Set a title for the current session (usage: /title My Session Name)",
|
|
"/usage": "Show token usage for the current session",
|
|
"/insights": "Show usage insights and analytics (last 30 days)",
|
|
"/paste": "Check clipboard for an image and attach it",
|
|
"/reload-mcp": "Reload MCP servers from config.yaml",
|
|
"/rollback": "List or restore filesystem checkpoints (usage: /rollback [number])",
|
|
"/skin": "Show or change the display skin/theme",
|
|
"/quit": "Exit the CLI (also: /exit, /q)",
|
|
}
|
|
|
|
|
|
class SlashCommandCompleter(Completer):
|
|
"""Autocomplete for built-in slash commands and optional skill commands."""
|
|
|
|
def __init__(
|
|
self,
|
|
skill_commands_provider: Callable[[], Mapping[str, dict[str, Any]]] | None = None,
|
|
) -> None:
|
|
self._skill_commands_provider = skill_commands_provider
|
|
|
|
def _iter_skill_commands(self) -> Mapping[str, dict[str, Any]]:
|
|
if self._skill_commands_provider is None:
|
|
return {}
|
|
try:
|
|
return self._skill_commands_provider() or {}
|
|
except Exception:
|
|
return {}
|
|
|
|
@staticmethod
|
|
def _completion_text(cmd_name: str, word: str) -> str:
|
|
"""Return replacement text for a completion.
|
|
|
|
When the user has already typed the full command exactly (``/help``),
|
|
returning ``help`` would be a no-op and prompt_toolkit suppresses the
|
|
menu. Appending a trailing space keeps the dropdown visible and makes
|
|
backspacing retrigger it naturally.
|
|
"""
|
|
return f"{cmd_name} " if cmd_name == word else cmd_name
|
|
|
|
def get_completions(self, document, complete_event):
|
|
text = document.text_before_cursor
|
|
if not text.startswith("/"):
|
|
return
|
|
|
|
word = text[1:]
|
|
|
|
for cmd, desc in COMMANDS.items():
|
|
cmd_name = cmd[1:]
|
|
if cmd_name.startswith(word):
|
|
yield Completion(
|
|
self._completion_text(cmd_name, word),
|
|
start_position=-len(word),
|
|
display=cmd,
|
|
display_meta=desc,
|
|
)
|
|
|
|
for cmd, info in self._iter_skill_commands().items():
|
|
cmd_name = cmd[1:]
|
|
if cmd_name.startswith(word):
|
|
description = str(info.get("description", "Skill command"))
|
|
short_desc = description[:50] + ("..." if len(description) > 50 else "")
|
|
yield Completion(
|
|
self._completion_text(cmd_name, word),
|
|
start_position=-len(word),
|
|
display=cmd,
|
|
display_meta=f"⚡ {short_desc}",
|
|
)
|