refactor: migrate tool progress configuration from environment variables to config.yaml

This commit is contained in:
teknium1 2026-02-28 00:05:58 -08:00
parent 0862fa96fd
commit de5a88bd97
9 changed files with 114 additions and 52 deletions

View file

@ -248,9 +248,7 @@ DISCORD_ALLOWED_USERS=123456789012345678 # Comma-separated user IDs
HERMES_MAX_ITERATIONS=60 # Max tool-calling iterations HERMES_MAX_ITERATIONS=60 # Max tool-calling iterations
MESSAGING_CWD=/home/myuser # Terminal working directory for messaging MESSAGING_CWD=/home/myuser # Terminal working directory for messaging
# Tool Progress (optional) # Tool progress is configured in config.yaml (display.tool_progress: off|new|all|verbose)
HERMES_TOOL_PROGRESS=true # Send progress messages
HERMES_TOOL_PROGRESS_MODE=new # "new" or "all"
``` ```
### Working Directory Behavior ### Working Directory Behavior
@ -301,7 +299,7 @@ Files: `gateway/hooks.py`
### Tool Progress Notifications ### Tool Progress Notifications
When `HERMES_TOOL_PROGRESS=true`, the bot sends status messages as it works: When `tool_progress` is enabled in `config.yaml`, the bot sends status messages as it works:
- `💻 \`ls -la\`...` (terminal commands show the actual command) - `💻 \`ls -la\`...` (terminal commands show the actual command)
- `🔍 web_search...` - `🔍 web_search...`
- `📄 web_extract...` - `📄 web_extract...`
@ -411,8 +409,7 @@ Terminal tool configuration (in `~/.hermes/config.yaml`):
Agent behavior (in `~/.hermes/.env`): Agent behavior (in `~/.hermes/.env`):
- `HERMES_MAX_ITERATIONS` - Max tool-calling iterations (default: 60) - `HERMES_MAX_ITERATIONS` - Max tool-calling iterations (default: 60)
- `MESSAGING_CWD` - Working directory for messaging platforms (default: ~) - `MESSAGING_CWD` - Working directory for messaging platforms (default: ~)
- `HERMES_TOOL_PROGRESS` - Enable tool progress messages (`true`/`false`) - `display.tool_progress` in config.yaml - Tool progress: `off`, `new`, `all`, `verbose`
- `HERMES_TOOL_PROGRESS_MODE` - Progress mode: `new` (tool changes) or `all`
- `OPENAI_API_KEY` - Voice transcription (Whisper STT) - `OPENAI_API_KEY` - Voice transcription (Whisper STT)
- `SLACK_BOT_TOKEN` / `SLACK_APP_TOKEN` - Slack integration (Socket Mode) - `SLACK_BOT_TOKEN` / `SLACK_APP_TOKEN` - Slack integration (Socket Mode)
- `SLACK_ALLOWED_USERS` - Comma-separated Slack user IDs - `SLACK_ALLOWED_USERS` - Comma-separated Slack user IDs

View file

@ -325,14 +325,22 @@ TERMINAL_CWD=/workspace # All terminal sessions (local or contain
### Tool Progress Notifications ### Tool Progress Notifications
Get real-time updates as the agent works: Control how much tool activity is displayed. Set in `~/.hermes/config.yaml`:
```bash ```yaml
# Enable in ~/.hermes/.env display:
HERMES_TOOL_PROGRESS=true tool_progress: all # off | new | all | verbose
HERMES_TOOL_PROGRESS_MODE=all # or "new" for only when tool changes
``` ```
| Mode | What you see |
|------|-------------|
| `off` | Silent — just the final response |
| `new` | Tool indicator only when the tool changes (skip repeats) |
| `all` | Every tool call with a short preview (default) |
| `verbose` | Full args, results, and debug logs |
Toggle at runtime in the CLI with `/verbose` (cycles through all four modes).
--- ---
## Commands ## Commands
@ -1568,8 +1576,6 @@ All variables go in `~/.hermes/.env`. Run `hermes config set VAR value` to set t
| Variable | Description | | Variable | Description |
|----------|-------------| |----------|-------------|
| `HERMES_MAX_ITERATIONS` | Max tool-calling iterations per conversation (default: 60) | | `HERMES_MAX_ITERATIONS` | Max tool-calling iterations per conversation (default: 60) |
| `HERMES_TOOL_PROGRESS` | Send progress messages when using tools (`true`/`false`) |
| `HERMES_TOOL_PROGRESS_MODE` | `all` (every call, default) or `new` (only when tool changes) |
**Context Compression:** **Context Compression:**
| Variable | Description | | Variable | Description |

View file

@ -487,3 +487,11 @@ delegation:
display: display:
# Use compact banner mode # Use compact banner mode
compact: false compact: false
# Tool progress display level (CLI and gateway)
# off: Silent — no tool activity shown, just the final response
# new: Show a tool indicator only when the tool changes (skip repeats)
# all: Show every tool call with a short preview (default)
# verbose: Full args, results, and debug logs (same as /verbose)
# Toggle at runtime with /verbose in the CLI
tool_progress: all

25
cli.py
View file

@ -793,7 +793,9 @@ class HermesCLI:
# Initialize Rich console # Initialize Rich console
self.console = Console() self.console = Console()
self.compact = compact if compact is not None else CLI_CONFIG["display"].get("compact", False) self.compact = compact if compact is not None else CLI_CONFIG["display"].get("compact", False)
self.verbose = verbose if verbose is not None else CLI_CONFIG["agent"].get("verbose", False) # tool_progress: "off", "new", "all", "verbose" (from config.yaml display section)
self.tool_progress_mode = CLI_CONFIG["display"].get("tool_progress", "all")
self.verbose = verbose if verbose is not None else (self.tool_progress_mode == "verbose")
# Configuration - priority: CLI args > env vars > config file # Configuration - priority: CLI args > env vars > config file
# Model can come from: CLI arg, LLM_MODEL env, OPENAI_MODEL env (custom endpoint), or config # Model can come from: CLI arg, LLM_MODEL env, OPENAI_MODEL env (custom endpoint), or config
@ -1697,24 +1699,35 @@ class HermesCLI:
return True return True
def _toggle_verbose(self): def _toggle_verbose(self):
"""Toggle verbose mode on/off at runtime.""" """Cycle tool progress mode: off → new → all → verbose → off."""
self.verbose = not self.verbose cycle = ["off", "new", "all", "verbose"]
try:
idx = cycle.index(self.tool_progress_mode)
except ValueError:
idx = 2 # default to "all"
self.tool_progress_mode = cycle[(idx + 1) % len(cycle)]
self.verbose = self.tool_progress_mode == "verbose"
if self.agent: if self.agent:
self.agent.verbose_logging = self.verbose self.agent.verbose_logging = self.verbose
self.agent.quiet_mode = not self.verbose self.agent.quiet_mode = not self.verbose
# Reconfigure logging level to match new state labels = {
"off": "[dim]Tool progress: OFF[/] — silent mode, just the final response.",
"new": "[yellow]Tool progress: NEW[/] — show each new tool (skip repeats).",
"all": "[green]Tool progress: ALL[/] — show every tool call.",
"verbose": "[bold green]Tool progress: VERBOSE[/] — full args, results, and debug logs.",
}
self.console.print(labels.get(self.tool_progress_mode, ""))
if self.verbose: if self.verbose:
logging.getLogger().setLevel(logging.DEBUG) logging.getLogger().setLevel(logging.DEBUG)
for noisy in ('openai', 'openai._base_client', 'httpx', 'httpcore', 'asyncio', 'hpack', 'grpc', 'modal'): for noisy in ('openai', 'openai._base_client', 'httpx', 'httpcore', 'asyncio', 'hpack', 'grpc', 'modal'):
logging.getLogger(noisy).setLevel(logging.WARNING) logging.getLogger(noisy).setLevel(logging.WARNING)
self.console.print("[bold green]Verbose mode ON[/] — tool calls, parameters, and results will be shown.")
else: else:
logging.getLogger().setLevel(logging.INFO) logging.getLogger().setLevel(logging.INFO)
for quiet_logger in ('tools', 'minisweagent', 'run_agent', 'trajectory_compressor', 'cron', 'hermes_cli'): for quiet_logger in ('tools', 'minisweagent', 'run_agent', 'trajectory_compressor', 'cron', 'hermes_cli'):
logging.getLogger(quiet_logger).setLevel(logging.ERROR) logging.getLogger(quiet_logger).setLevel(logging.ERROR)
self.console.print("[dim]Verbose mode OFF[/] — returning to normal display.")
def _clarify_callback(self, question, choices): def _clarify_callback(self, question, choices):
""" """

View file

@ -223,11 +223,9 @@ MESSAGING_CWD=/home/myuser
# TOOL PROGRESS NOTIFICATIONS # TOOL PROGRESS NOTIFICATIONS
# ============================================================================= # =============================================================================
# Show progress messages as agent uses tools # Tool progress is now configured in config.yaml:
HERMES_TOOL_PROGRESS=true # display:
# tool_progress: all # off | new | all | verbose
# Mode: "new" (only when tool changes) or "all" (every tool call)
HERMES_TOOL_PROGRESS_MODE=new
# ============================================================================= # =============================================================================
# SESSION SETTINGS # SESSION SETTINGS
@ -301,7 +299,7 @@ The gateway keeps the "typing..." indicator active throughout processing, refres
### Tool Progress Notifications ### Tool Progress Notifications
When `HERMES_TOOL_PROGRESS=true`, the bot sends status messages as it works: When `tool_progress` is enabled in `config.yaml`, the bot sends status messages as it works:
```text ```text
💻 `ls -la`... 💻 `ls -la`...

View file

@ -1462,9 +1462,24 @@ class GatewayRunner:
default_toolset = default_toolset_map.get(source.platform, "hermes-telegram") default_toolset = default_toolset_map.get(source.platform, "hermes-telegram")
enabled_toolsets = [default_toolset] enabled_toolsets = [default_toolset]
# Check if tool progress notifications are enabled # Tool progress mode from config.yaml: "all", "new", "verbose", "off"
tool_progress_enabled = os.getenv("HERMES_TOOL_PROGRESS", "true").lower() in ("1", "true", "yes") # Falls back to env vars for backward compatibility
progress_mode = os.getenv("HERMES_TOOL_PROGRESS_MODE", "all") # "all" or "new" (only new tools) _progress_cfg = {}
try:
_tp_cfg_path = _hermes_home / "config.yaml"
if _tp_cfg_path.exists():
import yaml as _tp_yaml
with open(_tp_cfg_path) as _tp_f:
_tp_data = _tp_yaml.safe_load(_tp_f) or {}
_progress_cfg = _tp_data.get("display", {})
except Exception:
pass
progress_mode = (
_progress_cfg.get("tool_progress")
or os.getenv("HERMES_TOOL_PROGRESS_MODE")
or "all"
)
tool_progress_enabled = progress_mode != "off"
# Queue for progress messages (thread-safe) # Queue for progress messages (thread-safe)
progress_queue = queue.Queue() if tool_progress_enabled else None progress_queue = queue.Queue() if tool_progress_enabled else None
@ -1627,6 +1642,7 @@ class GatewayRunner:
base_url=base_url, base_url=base_url,
max_iterations=max_iterations, max_iterations=max_iterations,
quiet_mode=True, quiet_mode=True,
verbose_logging=False,
enabled_toolsets=enabled_toolsets, enabled_toolsets=enabled_toolsets,
ephemeral_system_prompt=combined_ephemeral or None, ephemeral_system_prompt=combined_ephemeral or None,
prefill_messages=self._prefill_messages or None, prefill_messages=self._prefill_messages or None,

View file

@ -25,7 +25,7 @@ COMMANDS = {
"/cron": "Manage scheduled tasks (list, add, remove)", "/cron": "Manage scheduled tasks (list, add, remove)",
"/skills": "Search, install, inspect, or manage skills from online registries", "/skills": "Search, install, inspect, or manage skills from online registries",
"/platforms": "Show gateway/messaging platform status", "/platforms": "Show gateway/messaging platform status",
"/verbose": "Toggle verbose mode (show tool calls, parameters, and results)", "/verbose": "Cycle tool progress display: off → new → all → verbose",
"/quit": "Exit the CLI (also: /exit, /q)", "/quit": "Exit the CLI (also: /exit, /q)",
} }

View file

@ -136,7 +136,7 @@ DEFAULT_CONFIG = {
"command_allowlist": [], "command_allowlist": [],
# Config schema version - bump this when adding new required fields # Config schema version - bump this when adding new required fields
"_config_version": 3, "_config_version": 4,
} }
# ============================================================================= # =============================================================================
@ -318,16 +318,19 @@ OPTIONAL_ENV_VARS = {
"password": False, "password": False,
"category": "setting", "category": "setting",
}, },
# HERMES_TOOL_PROGRESS and HERMES_TOOL_PROGRESS_MODE are deprecated —
# now configured via display.tool_progress in config.yaml (off|new|all|verbose).
# Gateway falls back to these env vars for backward compatibility.
"HERMES_TOOL_PROGRESS": { "HERMES_TOOL_PROGRESS": {
"description": "Send tool progress messages in messaging channels (true/false)", "description": "(deprecated) Use display.tool_progress in config.yaml instead",
"prompt": "Enable tool progress messages", "prompt": "Tool progress (deprecated — use config.yaml)",
"url": None, "url": None,
"password": False, "password": False,
"category": "setting", "category": "setting",
}, },
"HERMES_TOOL_PROGRESS_MODE": { "HERMES_TOOL_PROGRESS_MODE": {
"description": "Progress mode: 'all' (every tool) or 'new' (only when tool changes)", "description": "(deprecated) Use display.tool_progress in config.yaml instead",
"prompt": "Progress mode (all/new)", "prompt": "Progress mode (deprecated — use config.yaml)",
"url": None, "url": None,
"password": False, "password": False,
"category": "setting", "category": "setting",
@ -442,6 +445,29 @@ def migrate_config(interactive: bool = True, quiet: bool = False) -> Dict[str, A
# Check config version # Check config version
current_ver, latest_ver = check_config_version() current_ver, latest_ver = check_config_version()
# ── Version 3 → 4: migrate tool progress from .env to config.yaml ──
if current_ver < 4:
config = load_config()
display = config.get("display", {})
if not isinstance(display, dict):
display = {}
if "tool_progress" not in display:
old_enabled = get_env_value("HERMES_TOOL_PROGRESS")
old_mode = get_env_value("HERMES_TOOL_PROGRESS_MODE")
if old_enabled and old_enabled.lower() in ("false", "0", "no"):
display["tool_progress"] = "off"
results["config_added"].append("display.tool_progress=off (from HERMES_TOOL_PROGRESS=false)")
elif old_mode and old_mode.lower() in ("new", "all"):
display["tool_progress"] = old_mode.lower()
results["config_added"].append(f"display.tool_progress={old_mode.lower()} (from HERMES_TOOL_PROGRESS_MODE)")
else:
display["tool_progress"] = "all"
results["config_added"].append("display.tool_progress=all (default)")
config["display"] = display
save_config(config)
if not quiet:
print(f" ✓ Migrated tool progress to config.yaml: {display['tool_progress']}")
if current_ver < latest_ver and not quiet: if current_ver < latest_ver and not quiet:
print(f"Config version: {current_ver}{latest_ver}") print(f"Config version: {current_ver}{latest_ver}")

View file

@ -1044,27 +1044,25 @@ def run_setup_wizard(args):
except ValueError: except ValueError:
print_warning("Invalid number, keeping current value") print_warning("Invalid number, keeping current value")
# Tool progress notifications (for messaging) # Tool progress notifications
print_info("") print_info("")
print_info("Tool Progress Notifications (Messaging only)") print_info("Tool Progress Display")
print_info("Send status messages when the agent uses tools.") print_info("Controls how much tool activity is shown (CLI and messaging).")
print_info("Example: '💻 ls -la...' or '🔍 web_search...'") print_info(" off — Silent, just the final response")
print_info(" new — Show tool name only when it changes (less noise)")
print_info(" all — Show every tool call with a short preview")
print_info(" verbose — Full args, results, and debug logs")
current_progress = get_env_value('HERMES_TOOL_PROGRESS') or 'true' current_mode = config.get("display", {}).get("tool_progress", "all")
if prompt_yes_no("Enable tool progress messages?", current_progress.lower() in ('1', 'true', 'yes')): mode = prompt("Tool progress mode", current_mode)
save_env_value("HERMES_TOOL_PROGRESS", "true") if mode.lower() in ("off", "new", "all", "verbose"):
if "display" not in config:
# Progress mode config["display"] = {}
current_mode = get_env_value('HERMES_TOOL_PROGRESS_MODE') or 'all' config["display"]["tool_progress"] = mode.lower()
print_info(" Mode options:") save_config(config)
print_info(" 'new' - Only when switching tools (less spam)") print_success(f"Tool progress set to: {mode.lower()}")
print_info(" 'all' - Every tool call")
mode = prompt(" Progress mode", current_mode)
if mode.lower() in ('all', 'new'):
save_env_value("HERMES_TOOL_PROGRESS_MODE", mode.lower())
print_success("Tool progress enabled")
else: else:
save_env_value("HERMES_TOOL_PROGRESS", "false") print_warning(f"Unknown mode '{mode}', keeping '{current_mode}'")
# ========================================================================= # =========================================================================
# Step 6: Context Compression # Step 6: Context Compression