mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-22 16:25:58 +00:00
fix(tools): /tools shows the full pre-assembly catalog; adapt tests to tiered disclosure
CI slices 2 and 7 caught three tests broken by always-defer: - /tools (CLI show_tools + TUI gateway tools.show) now passes skip_tool_search_assembly=True — it's a discovery/inspection surface, so users verifying an MCP installed must see deferred tools, not a collapsed bridge row. This also fixes test_slash_worker_mcp_discovery (profile MCP tool visible in /tools). - test_plugins.py::test_plugin_tools_in_definitions: 'visible' becomes 'reachable' — direct schema OR listed in the bridge description; scope-exclusion assertions unchanged (not direct AND not listed). - test_discord_tool.py dynamic-schema-rebuild test reads the pre-assembly list (the rebuilt schema is what tool_describe serves). Banner/status tool counts intentionally keep the post-assembly view — they reflect what the model actually sees.
This commit is contained in:
parent
a521ed703b
commit
8f6ecbae26
4 changed files with 37 additions and 13 deletions
6
cli.py
6
cli.py
|
|
@ -6874,7 +6874,11 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin, CLIBillingMixin):
|
|||
|
||||
def show_tools(self):
|
||||
"""Display available tools with kawaii ASCII art."""
|
||||
tools = get_tool_definitions(enabled_toolsets=self.enabled_toolsets, quiet_mode=True)
|
||||
# Pre-assembly list: /tools is a discovery/inspection surface, so it
|
||||
# must show the full catalog including tools deferred behind the
|
||||
# tool_search bridge (users check this to verify an MCP installed).
|
||||
tools = get_tool_definitions(enabled_toolsets=self.enabled_toolsets, quiet_mode=True,
|
||||
skip_tool_search_assembly=True)
|
||||
|
||||
if not tools:
|
||||
print("(;_;) No tools available")
|
||||
|
|
|
|||
|
|
@ -1544,7 +1544,14 @@ class TestPluginToolVisibility:
|
|||
"""Plugin-registered tools appear in get_tool_definitions()."""
|
||||
|
||||
def test_plugin_tools_in_definitions(self, tmp_path, monkeypatch):
|
||||
"""Plugin tools are included when their toolset is in enabled_toolsets."""
|
||||
"""Plugin tools are reachable when their toolset is in enabled_toolsets.
|
||||
|
||||
Under tiered disclosure (any MCP/plugin tool defers behind the
|
||||
tool_search bridge), a plugin tool no longer appears as a direct
|
||||
schema — it is deferred and surfaced via the bridge's catalog
|
||||
listing. 'Reachable' therefore means: present directly OR listed
|
||||
in the tool_search bridge description.
|
||||
"""
|
||||
import hermes_cli.plugins as plugins_mod
|
||||
|
||||
plugins_dir = tmp_path / "hermes_test" / "plugins"
|
||||
|
|
@ -1572,20 +1579,26 @@ class TestPluginToolVisibility:
|
|||
|
||||
from model_tools import get_tool_definitions
|
||||
|
||||
# Plugin tools are included when their toolset is explicitly enabled
|
||||
def _reachable(tools):
|
||||
names = [t["function"]["name"] for t in tools]
|
||||
if "vis_tool" in names:
|
||||
return True # tool_search inactive → direct schema
|
||||
search = next((t for t in tools
|
||||
if t["function"]["name"] == "tool_search"), None)
|
||||
return bool(search and "vis_tool" in search["function"]["description"])
|
||||
|
||||
# Reachable when its toolset is explicitly enabled
|
||||
tools = get_tool_definitions(enabled_toolsets=["terminal", "plugin_vis_plugin"], quiet_mode=True)
|
||||
tool_names = [t["function"]["name"] for t in tools]
|
||||
assert "vis_tool" in tool_names
|
||||
assert _reachable(tools)
|
||||
|
||||
# Plugin tools are excluded when only other toolsets are enabled
|
||||
# Excluded entirely when only other toolsets are enabled — not
|
||||
# direct, not in the deferred listing.
|
||||
tools2 = get_tool_definitions(enabled_toolsets=["terminal"], quiet_mode=True)
|
||||
tool_names2 = [t["function"]["name"] for t in tools2]
|
||||
assert "vis_tool" not in tool_names2
|
||||
assert not _reachable(tools2)
|
||||
|
||||
# Plugin tools are included when no toolset filter is active (all enabled)
|
||||
# Reachable when no toolset filter is active (all enabled)
|
||||
tools3 = get_tool_definitions(quiet_mode=True)
|
||||
tool_names3 = [t["function"]["name"] for t in tools3]
|
||||
assert "vis_tool" in tool_names3
|
||||
assert _reachable(tools3)
|
||||
|
||||
|
||||
# ── TestPluginManagerList ──────────────────────────────────────────────────
|
||||
|
|
|
|||
|
|
@ -1268,7 +1268,11 @@ class TestModelToolsIntegration:
|
|||
mock_req.return_value = {"flags": 0}
|
||||
|
||||
from model_tools import get_tool_definitions
|
||||
tools = get_tool_definitions(enabled_toolsets=["hermes-discord"], quiet_mode=True)
|
||||
# skip_tool_search_assembly: this test exercises the dynamic schema
|
||||
# rebuild; under tiered disclosure the discord tools defer behind the
|
||||
# bridge, but the rebuilt schema is what tool_describe serves.
|
||||
tools = get_tool_definitions(enabled_toolsets=["hermes-discord"], quiet_mode=True,
|
||||
skip_tool_search_assembly=True)
|
||||
discord_admin_tool = next(
|
||||
(t for t in tools if t.get("function", {}).get("name") == "discord_admin"),
|
||||
None,
|
||||
|
|
|
|||
|
|
@ -15980,7 +15980,10 @@ def _(rid, params: dict) -> dict:
|
|||
if session
|
||||
else _load_enabled_toolsets()
|
||||
)
|
||||
tools = get_tool_definitions(enabled_toolsets=enabled, quiet_mode=True)
|
||||
# Pre-assembly list: /tools is a discovery surface and must show
|
||||
# tools deferred behind the tool_search bridge (same as the CLI).
|
||||
tools = get_tool_definitions(enabled_toolsets=enabled, quiet_mode=True,
|
||||
skip_tool_search_assembly=True)
|
||||
sections = {}
|
||||
|
||||
for tool in sorted(tools, key=lambda t: t["function"]["name"]):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue