mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-08 13:12:08 +00:00
fix(toolsets): preserve core tools when a posture toolset is in disabled_toolsets (#57315)
The disabled_toolsets subtraction loop in _compute_tool_definitions preserved shared core tools only for hermes-* platform bundles (#33924), subtracting bundle_non_core_tools(); every other name took the else branch and got a full resolve_toolset() subtraction. The `coding` toolset is a posture toolset (posture: True) that re-lists the shared _HERMES_CORE_TOOLS it does not own, so disabled_toolsets=["coding"] stripped those core tools from the whole schema (34 tools collapsed to a handful; terminal/read_file/write_file/web_search/execute_code gone). Extend the core-preserving branch to also match posture toolsets, so they subtract only the non-core delta. Only `coding` carries posture: True, so atomic toolsets stay fully removable. The bundle-misconfiguration info log is gated to hermes-* names, since its wording is bundle-specific and disabled_toolsets=["coding"] is a legitimate config written by older `hermes setup` runs. Adds a regression test (TestDisabledToolsetsPostureToolset) alongside the existing #33924 bundle tests.
This commit is contained in:
parent
b57fe5ca01
commit
e5636da5d8
2 changed files with 61 additions and 7 deletions
|
|
@ -399,16 +399,19 @@ def _compute_tool_definitions(
|
|||
if disabled_toolsets:
|
||||
for toolset_name in disabled_toolsets:
|
||||
if validate_toolset(toolset_name):
|
||||
if toolset_name.startswith("hermes-"):
|
||||
# Platform bundles (hermes-*) include _HERMES_CORE_TOOLS, so
|
||||
# subtracting the whole bundle would strip core tools shared
|
||||
# by other enabled toolsets and empty the tool list (#33924).
|
||||
# Subtract only the bundle's non-core delta; keep core.
|
||||
from toolsets import bundle_non_core_tools
|
||||
from toolsets import bundle_non_core_tools, get_toolset
|
||||
if toolset_name.startswith("hermes-") or (get_toolset(toolset_name) or {}).get("posture"):
|
||||
# Platform bundles (hermes-*) include _HERMES_CORE_TOOLS, and
|
||||
# posture toolsets (`posture: True`, e.g. `coding`) re-list
|
||||
# those same core tools without owning them, so subtracting
|
||||
# the whole toolset would strip core tools shared by other
|
||||
# enabled toolsets and empty the tool list (#33924, #57315).
|
||||
# Subtract only the non-core delta; keep core.
|
||||
to_remove = bundle_non_core_tools(toolset_name)
|
||||
tools_to_include.difference_update(to_remove)
|
||||
resolved = sorted(to_remove)
|
||||
if not quiet_mode and toolset_name not in _WARNED_DISABLED_BUNDLES:
|
||||
if (not quiet_mode and toolset_name.startswith("hermes-")
|
||||
and toolset_name not in _WARNED_DISABLED_BUNDLES):
|
||||
_WARNED_DISABLED_BUNDLES.add(toolset_name)
|
||||
logger.info(
|
||||
"agent.disabled_toolsets contains platform-bundle "
|
||||
|
|
|
|||
|
|
@ -536,3 +536,54 @@ class TestDisabledToolsetsPlatformBundle:
|
|||
from toolsets import bundle_non_core_tools
|
||||
# A non-existent bundle resolves to an empty set (no tools), not a crash.
|
||||
assert bundle_non_core_tools("hermes-does-not-exist") == set()
|
||||
|
||||
|
||||
class TestDisabledToolsetsPostureToolset:
|
||||
"""Regression test for #57315: disabling a posture toolset (`coding`,
|
||||
posture: True) must preserve the shared core tools it re-lists but does
|
||||
not own -- same non-core-delta subtraction as hermes-* bundles (#33924) --
|
||||
while atomic toolsets stay fully removable."""
|
||||
|
||||
def test_disabling_coding_preserves_core_but_atomic_disables_still_remove(self):
|
||||
from model_tools import get_tool_definitions
|
||||
|
||||
# web_search is check_fn-gated (needs an API key); probe only the core
|
||||
# tools actually present in baseline so gating cannot mask the fix.
|
||||
core_probe = {"terminal", "read_file", "write_file", "web_search", "execute_code"}
|
||||
|
||||
baseline = {
|
||||
t["function"]["name"]
|
||||
for t in get_tool_definitions(quiet_mode=True)
|
||||
}
|
||||
present_core = core_probe & baseline
|
||||
# Sanity: at least some probed core tools are available in this env.
|
||||
assert present_core, "no probed core tools present in baseline"
|
||||
|
||||
no_coding = {
|
||||
t["function"]["name"]
|
||||
for t in get_tool_definitions(
|
||||
disabled_toolsets=["coding"], quiet_mode=True
|
||||
)
|
||||
}
|
||||
# Previously the full resolve_toolset("coding") subtraction stripped
|
||||
# these shared core tools, collapsing the schema to a handful (#57315).
|
||||
assert present_core <= no_coding, (
|
||||
f"Core tools stripped by disabling 'coding': {present_core - no_coding}"
|
||||
)
|
||||
|
||||
# Atomic (non-posture) toolsets must still be fully removable.
|
||||
no_terminal = {
|
||||
t["function"]["name"]
|
||||
for t in get_tool_definitions(
|
||||
disabled_toolsets=["terminal"], quiet_mode=True
|
||||
)
|
||||
}
|
||||
assert "terminal" not in no_terminal
|
||||
|
||||
no_file = {
|
||||
t["function"]["name"]
|
||||
for t in get_tool_definitions(
|
||||
disabled_toolsets=["file"], quiet_mode=True
|
||||
)
|
||||
}
|
||||
assert "write_file" not in no_file
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue