2026-03-26 14:41:04 -07:00
|
|
|
"""Tests for gateway /verbose command (config-gated tool progress cycling)."""
|
|
|
|
|
|
|
|
|
|
import asyncio
|
|
|
|
|
from unittest.mock import AsyncMock, MagicMock
|
|
|
|
|
|
|
|
|
|
import pytest
|
|
|
|
|
import yaml
|
|
|
|
|
|
|
|
|
|
import gateway.run as gateway_run
|
|
|
|
|
from gateway.config import Platform
|
|
|
|
|
from gateway.platforms.base import MessageEvent
|
|
|
|
|
from gateway.session import SessionSource
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _make_event(text="/verbose", platform=Platform.TELEGRAM, user_id="12345", chat_id="67890"):
|
|
|
|
|
"""Build a MessageEvent for testing."""
|
|
|
|
|
source = SessionSource(
|
|
|
|
|
platform=platform,
|
|
|
|
|
user_id=user_id,
|
|
|
|
|
chat_id=chat_id,
|
|
|
|
|
user_name="testuser",
|
|
|
|
|
)
|
|
|
|
|
return MessageEvent(text=text, source=source)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _make_runner():
|
|
|
|
|
"""Create a bare GatewayRunner without calling __init__."""
|
|
|
|
|
runner = object.__new__(gateway_run.GatewayRunner)
|
|
|
|
|
runner.adapters = {}
|
|
|
|
|
runner._ephemeral_system_prompt = ""
|
|
|
|
|
runner._prefill_messages = []
|
|
|
|
|
runner._reasoning_config = None
|
|
|
|
|
runner._show_reasoning = False
|
|
|
|
|
runner._provider_routing = {}
|
|
|
|
|
runner._fallback_model = None
|
|
|
|
|
runner._running_agents = {}
|
|
|
|
|
runner.hooks = MagicMock()
|
|
|
|
|
runner.hooks.emit = AsyncMock()
|
|
|
|
|
runner.hooks.loaded_hooks = []
|
|
|
|
|
runner._session_db = None
|
|
|
|
|
runner._get_or_create_gateway_honcho = lambda session_key: (None, None)
|
|
|
|
|
return runner
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestVerboseCommand:
|
|
|
|
|
"""Tests for _handle_verbose_command in the gateway."""
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
|
async def test_disabled_by_default(self, tmp_path, monkeypatch):
|
|
|
|
|
"""When tool_progress_command is false, /verbose returns an info message."""
|
|
|
|
|
hermes_home = tmp_path / "hermes"
|
|
|
|
|
hermes_home.mkdir()
|
|
|
|
|
config_path = hermes_home / "config.yaml"
|
|
|
|
|
config_path.write_text("display:\n tool_progress: all\n", encoding="utf-8")
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(gateway_run, "_hermes_home", hermes_home)
|
|
|
|
|
|
|
|
|
|
runner = _make_runner()
|
|
|
|
|
result = await runner._handle_verbose_command(_make_event())
|
|
|
|
|
|
|
|
|
|
assert "not enabled" in result.lower()
|
|
|
|
|
assert "tool_progress_command" in result
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
|
async def test_enabled_cycles_mode(self, tmp_path, monkeypatch):
|
feat: per-platform display verbosity configuration (#8006)
Add display.platforms section to config.yaml for per-platform overrides of
display settings (tool_progress, show_reasoning, streaming, tool_preview_length).
Each platform gets sensible built-in defaults based on capability tier:
- High (telegram, discord): tool_progress=all, streaming follows global
- Medium (slack, mattermost, matrix, feishu): tool_progress=new
- Low (signal, whatsapp, bluebubbles, wecom, etc.): tool_progress=off, streaming=false
- Minimal (email, sms, webhook, homeassistant): tool_progress=off, streaming=false
Example config:
display:
platforms:
telegram:
tool_progress: all
show_reasoning: true
slack:
tool_progress: off
Resolution order: platform override > global setting > built-in platform default.
Changes:
- New gateway/display_config.py: resolver module with tier-based platform defaults
- gateway/run.py: tool_progress, tool_preview_length, streaming, show_reasoning
all resolve per-platform via the new resolver
- /verbose command: now cycles tool_progress per-platform (saves to
display.platforms.<platform>.tool_progress instead of global)
- /reasoning show|hide: now saves show_reasoning per-platform
- Config version 15 -> 16: migrates tool_progress_overrides into display.platforms
- Backward compat: legacy tool_progress_overrides still read as fallback
- 27 new tests for resolver, normalization, migration, backward compat
- Updated verbose command tests for per-platform behavior
Addresses community request for per-channel verbosity control (Guillaume Meyer,
Nathan Danielsen) — high verbosity on backchannel Telegram, low on customer-facing
Slack, none on email.
2026-04-11 17:20:34 -07:00
|
|
|
"""When enabled, /verbose cycles tool_progress mode per-platform."""
|
2026-03-26 14:41:04 -07:00
|
|
|
hermes_home = tmp_path / "hermes"
|
|
|
|
|
hermes_home.mkdir()
|
|
|
|
|
config_path = hermes_home / "config.yaml"
|
|
|
|
|
config_path.write_text(
|
|
|
|
|
"display:\n tool_progress_command: true\n tool_progress: all\n",
|
|
|
|
|
encoding="utf-8",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(gateway_run, "_hermes_home", hermes_home)
|
|
|
|
|
|
|
|
|
|
runner = _make_runner()
|
|
|
|
|
result = await runner._handle_verbose_command(_make_event())
|
|
|
|
|
|
|
|
|
|
# all -> verbose
|
|
|
|
|
assert "VERBOSE" in result
|
feat: per-platform display verbosity configuration (#8006)
Add display.platforms section to config.yaml for per-platform overrides of
display settings (tool_progress, show_reasoning, streaming, tool_preview_length).
Each platform gets sensible built-in defaults based on capability tier:
- High (telegram, discord): tool_progress=all, streaming follows global
- Medium (slack, mattermost, matrix, feishu): tool_progress=new
- Low (signal, whatsapp, bluebubbles, wecom, etc.): tool_progress=off, streaming=false
- Minimal (email, sms, webhook, homeassistant): tool_progress=off, streaming=false
Example config:
display:
platforms:
telegram:
tool_progress: all
show_reasoning: true
slack:
tool_progress: off
Resolution order: platform override > global setting > built-in platform default.
Changes:
- New gateway/display_config.py: resolver module with tier-based platform defaults
- gateway/run.py: tool_progress, tool_preview_length, streaming, show_reasoning
all resolve per-platform via the new resolver
- /verbose command: now cycles tool_progress per-platform (saves to
display.platforms.<platform>.tool_progress instead of global)
- /reasoning show|hide: now saves show_reasoning per-platform
- Config version 15 -> 16: migrates tool_progress_overrides into display.platforms
- Backward compat: legacy tool_progress_overrides still read as fallback
- 27 new tests for resolver, normalization, migration, backward compat
- Updated verbose command tests for per-platform behavior
Addresses community request for per-channel verbosity control (Guillaume Meyer,
Nathan Danielsen) — high verbosity on backchannel Telegram, low on customer-facing
Slack, none on email.
2026-04-11 17:20:34 -07:00
|
|
|
assert "telegram" in result.lower() # per-platform feedback
|
2026-03-26 14:41:04 -07:00
|
|
|
|
feat: per-platform display verbosity configuration (#8006)
Add display.platforms section to config.yaml for per-platform overrides of
display settings (tool_progress, show_reasoning, streaming, tool_preview_length).
Each platform gets sensible built-in defaults based on capability tier:
- High (telegram, discord): tool_progress=all, streaming follows global
- Medium (slack, mattermost, matrix, feishu): tool_progress=new
- Low (signal, whatsapp, bluebubbles, wecom, etc.): tool_progress=off, streaming=false
- Minimal (email, sms, webhook, homeassistant): tool_progress=off, streaming=false
Example config:
display:
platforms:
telegram:
tool_progress: all
show_reasoning: true
slack:
tool_progress: off
Resolution order: platform override > global setting > built-in platform default.
Changes:
- New gateway/display_config.py: resolver module with tier-based platform defaults
- gateway/run.py: tool_progress, tool_preview_length, streaming, show_reasoning
all resolve per-platform via the new resolver
- /verbose command: now cycles tool_progress per-platform (saves to
display.platforms.<platform>.tool_progress instead of global)
- /reasoning show|hide: now saves show_reasoning per-platform
- Config version 15 -> 16: migrates tool_progress_overrides into display.platforms
- Backward compat: legacy tool_progress_overrides still read as fallback
- 27 new tests for resolver, normalization, migration, backward compat
- Updated verbose command tests for per-platform behavior
Addresses community request for per-channel verbosity control (Guillaume Meyer,
Nathan Danielsen) — high verbosity on backchannel Telegram, low on customer-facing
Slack, none on email.
2026-04-11 17:20:34 -07:00
|
|
|
# Verify config was saved to display.platforms.telegram
|
2026-03-26 14:41:04 -07:00
|
|
|
saved = yaml.safe_load(config_path.read_text(encoding="utf-8"))
|
feat: per-platform display verbosity configuration (#8006)
Add display.platforms section to config.yaml for per-platform overrides of
display settings (tool_progress, show_reasoning, streaming, tool_preview_length).
Each platform gets sensible built-in defaults based on capability tier:
- High (telegram, discord): tool_progress=all, streaming follows global
- Medium (slack, mattermost, matrix, feishu): tool_progress=new
- Low (signal, whatsapp, bluebubbles, wecom, etc.): tool_progress=off, streaming=false
- Minimal (email, sms, webhook, homeassistant): tool_progress=off, streaming=false
Example config:
display:
platforms:
telegram:
tool_progress: all
show_reasoning: true
slack:
tool_progress: off
Resolution order: platform override > global setting > built-in platform default.
Changes:
- New gateway/display_config.py: resolver module with tier-based platform defaults
- gateway/run.py: tool_progress, tool_preview_length, streaming, show_reasoning
all resolve per-platform via the new resolver
- /verbose command: now cycles tool_progress per-platform (saves to
display.platforms.<platform>.tool_progress instead of global)
- /reasoning show|hide: now saves show_reasoning per-platform
- Config version 15 -> 16: migrates tool_progress_overrides into display.platforms
- Backward compat: legacy tool_progress_overrides still read as fallback
- 27 new tests for resolver, normalization, migration, backward compat
- Updated verbose command tests for per-platform behavior
Addresses community request for per-channel verbosity control (Guillaume Meyer,
Nathan Danielsen) — high verbosity on backchannel Telegram, low on customer-facing
Slack, none on email.
2026-04-11 17:20:34 -07:00
|
|
|
assert saved["display"]["platforms"]["telegram"]["tool_progress"] == "verbose"
|
2026-03-26 14:41:04 -07:00
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
|
async def test_cycles_through_all_modes(self, tmp_path, monkeypatch):
|
|
|
|
|
"""Calling /verbose repeatedly cycles through all four modes."""
|
|
|
|
|
hermes_home = tmp_path / "hermes"
|
|
|
|
|
hermes_home.mkdir()
|
|
|
|
|
config_path = hermes_home / "config.yaml"
|
|
|
|
|
config_path.write_text(
|
|
|
|
|
"display:\n tool_progress_command: true\n tool_progress: 'off'\n",
|
|
|
|
|
encoding="utf-8",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(gateway_run, "_hermes_home", hermes_home)
|
|
|
|
|
runner = _make_runner()
|
|
|
|
|
|
|
|
|
|
# off -> new -> all -> verbose -> off
|
|
|
|
|
expected = ["new", "all", "verbose", "off"]
|
|
|
|
|
for mode in expected:
|
|
|
|
|
result = await runner._handle_verbose_command(_make_event())
|
|
|
|
|
saved = yaml.safe_load(config_path.read_text(encoding="utf-8"))
|
feat: per-platform display verbosity configuration (#8006)
Add display.platforms section to config.yaml for per-platform overrides of
display settings (tool_progress, show_reasoning, streaming, tool_preview_length).
Each platform gets sensible built-in defaults based on capability tier:
- High (telegram, discord): tool_progress=all, streaming follows global
- Medium (slack, mattermost, matrix, feishu): tool_progress=new
- Low (signal, whatsapp, bluebubbles, wecom, etc.): tool_progress=off, streaming=false
- Minimal (email, sms, webhook, homeassistant): tool_progress=off, streaming=false
Example config:
display:
platforms:
telegram:
tool_progress: all
show_reasoning: true
slack:
tool_progress: off
Resolution order: platform override > global setting > built-in platform default.
Changes:
- New gateway/display_config.py: resolver module with tier-based platform defaults
- gateway/run.py: tool_progress, tool_preview_length, streaming, show_reasoning
all resolve per-platform via the new resolver
- /verbose command: now cycles tool_progress per-platform (saves to
display.platforms.<platform>.tool_progress instead of global)
- /reasoning show|hide: now saves show_reasoning per-platform
- Config version 15 -> 16: migrates tool_progress_overrides into display.platforms
- Backward compat: legacy tool_progress_overrides still read as fallback
- 27 new tests for resolver, normalization, migration, backward compat
- Updated verbose command tests for per-platform behavior
Addresses community request for per-channel verbosity control (Guillaume Meyer,
Nathan Danielsen) — high verbosity on backchannel Telegram, low on customer-facing
Slack, none on email.
2026-04-11 17:20:34 -07:00
|
|
|
actual = saved["display"]["platforms"]["telegram"]["tool_progress"]
|
|
|
|
|
assert actual == mode, \
|
|
|
|
|
f"Expected {mode}, got {actual}"
|
2026-03-26 14:41:04 -07:00
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
|
async def test_defaults_to_all_when_no_tool_progress_set(self, tmp_path, monkeypatch):
|
|
|
|
|
"""When tool_progress is not in config, defaults to 'all' then cycles to verbose."""
|
|
|
|
|
hermes_home = tmp_path / "hermes"
|
|
|
|
|
hermes_home.mkdir()
|
|
|
|
|
config_path = hermes_home / "config.yaml"
|
|
|
|
|
config_path.write_text(
|
|
|
|
|
"display:\n tool_progress_command: true\n",
|
|
|
|
|
encoding="utf-8",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(gateway_run, "_hermes_home", hermes_home)
|
|
|
|
|
|
|
|
|
|
runner = _make_runner()
|
|
|
|
|
result = await runner._handle_verbose_command(_make_event())
|
|
|
|
|
|
feat: per-platform display verbosity configuration (#8006)
Add display.platforms section to config.yaml for per-platform overrides of
display settings (tool_progress, show_reasoning, streaming, tool_preview_length).
Each platform gets sensible built-in defaults based on capability tier:
- High (telegram, discord): tool_progress=all, streaming follows global
- Medium (slack, mattermost, matrix, feishu): tool_progress=new
- Low (signal, whatsapp, bluebubbles, wecom, etc.): tool_progress=off, streaming=false
- Minimal (email, sms, webhook, homeassistant): tool_progress=off, streaming=false
Example config:
display:
platforms:
telegram:
tool_progress: all
show_reasoning: true
slack:
tool_progress: off
Resolution order: platform override > global setting > built-in platform default.
Changes:
- New gateway/display_config.py: resolver module with tier-based platform defaults
- gateway/run.py: tool_progress, tool_preview_length, streaming, show_reasoning
all resolve per-platform via the new resolver
- /verbose command: now cycles tool_progress per-platform (saves to
display.platforms.<platform>.tool_progress instead of global)
- /reasoning show|hide: now saves show_reasoning per-platform
- Config version 15 -> 16: migrates tool_progress_overrides into display.platforms
- Backward compat: legacy tool_progress_overrides still read as fallback
- 27 new tests for resolver, normalization, migration, backward compat
- Updated verbose command tests for per-platform behavior
Addresses community request for per-channel verbosity control (Guillaume Meyer,
Nathan Danielsen) — high verbosity on backchannel Telegram, low on customer-facing
Slack, none on email.
2026-04-11 17:20:34 -07:00
|
|
|
# Telegram default is "all" (high tier) → cycles to verbose
|
2026-03-26 14:41:04 -07:00
|
|
|
assert "VERBOSE" in result
|
|
|
|
|
saved = yaml.safe_load(config_path.read_text(encoding="utf-8"))
|
feat: per-platform display verbosity configuration (#8006)
Add display.platforms section to config.yaml for per-platform overrides of
display settings (tool_progress, show_reasoning, streaming, tool_preview_length).
Each platform gets sensible built-in defaults based on capability tier:
- High (telegram, discord): tool_progress=all, streaming follows global
- Medium (slack, mattermost, matrix, feishu): tool_progress=new
- Low (signal, whatsapp, bluebubbles, wecom, etc.): tool_progress=off, streaming=false
- Minimal (email, sms, webhook, homeassistant): tool_progress=off, streaming=false
Example config:
display:
platforms:
telegram:
tool_progress: all
show_reasoning: true
slack:
tool_progress: off
Resolution order: platform override > global setting > built-in platform default.
Changes:
- New gateway/display_config.py: resolver module with tier-based platform defaults
- gateway/run.py: tool_progress, tool_preview_length, streaming, show_reasoning
all resolve per-platform via the new resolver
- /verbose command: now cycles tool_progress per-platform (saves to
display.platforms.<platform>.tool_progress instead of global)
- /reasoning show|hide: now saves show_reasoning per-platform
- Config version 15 -> 16: migrates tool_progress_overrides into display.platforms
- Backward compat: legacy tool_progress_overrides still read as fallback
- 27 new tests for resolver, normalization, migration, backward compat
- Updated verbose command tests for per-platform behavior
Addresses community request for per-channel verbosity control (Guillaume Meyer,
Nathan Danielsen) — high verbosity on backchannel Telegram, low on customer-facing
Slack, none on email.
2026-04-11 17:20:34 -07:00
|
|
|
assert saved["display"]["platforms"]["telegram"]["tool_progress"] == "verbose"
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
|
async def test_per_platform_isolation(self, tmp_path, monkeypatch):
|
|
|
|
|
"""Cycling /verbose on Telegram doesn't change Slack's setting.
|
|
|
|
|
|
|
|
|
|
Without a global tool_progress, each platform uses its built-in
|
|
|
|
|
default: Telegram = 'all' (high tier), Slack = 'new' (medium tier).
|
|
|
|
|
"""
|
|
|
|
|
hermes_home = tmp_path / "hermes"
|
|
|
|
|
hermes_home.mkdir()
|
|
|
|
|
config_path = hermes_home / "config.yaml"
|
|
|
|
|
# No global tool_progress → built-in platform defaults apply
|
|
|
|
|
config_path.write_text(
|
|
|
|
|
"display:\n tool_progress_command: true\n",
|
|
|
|
|
encoding="utf-8",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(gateway_run, "_hermes_home", hermes_home)
|
|
|
|
|
runner = _make_runner()
|
|
|
|
|
|
|
|
|
|
# Cycle on Telegram
|
|
|
|
|
await runner._handle_verbose_command(
|
|
|
|
|
_make_event(platform=Platform.TELEGRAM)
|
|
|
|
|
)
|
|
|
|
|
# Cycle on Slack
|
|
|
|
|
await runner._handle_verbose_command(
|
|
|
|
|
_make_event(platform=Platform.SLACK)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
saved = yaml.safe_load(config_path.read_text(encoding="utf-8"))
|
|
|
|
|
platforms = saved["display"]["platforms"]
|
|
|
|
|
# Telegram: all -> verbose (high tier default = all)
|
|
|
|
|
assert platforms["telegram"]["tool_progress"] == "verbose"
|
|
|
|
|
# Slack: new -> all (medium tier default = new, cycle to all)
|
|
|
|
|
assert platforms["slack"]["tool_progress"] == "all"
|
2026-03-26 14:41:04 -07:00
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
|
async def test_no_config_file_returns_disabled(self, tmp_path, monkeypatch):
|
|
|
|
|
"""When config.yaml doesn't exist, command reports disabled."""
|
|
|
|
|
hermes_home = tmp_path / "hermes"
|
|
|
|
|
hermes_home.mkdir()
|
|
|
|
|
# No config.yaml
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(gateway_run, "_hermes_home", hermes_home)
|
|
|
|
|
|
|
|
|
|
runner = _make_runner()
|
|
|
|
|
result = await runner._handle_verbose_command(_make_event())
|
|
|
|
|
assert "not enabled" in result.lower()
|
|
|
|
|
|
|
|
|
|
def test_verbose_is_in_gateway_known_commands(self):
|
|
|
|
|
"""The /verbose command is recognized by the gateway dispatch."""
|
|
|
|
|
from hermes_cli.commands import GATEWAY_KNOWN_COMMANDS
|
|
|
|
|
assert "verbose" in GATEWAY_KNOWN_COMMANDS
|