fix(tui): honor launch toolsets (#17623)

* fix(tui): honor launch toolsets

Carry chat --toolsets through the TUI launcher so TUI sessions use the same per-session tool scope as the classic CLI.

* fix(tui): parse top-level toolsets flag

Allow top-level hermes --tui --toolsets to reach the implicit chat session, matching chat subcommand behavior.

* fix(tui): validate launch toolsets

Filter invalid HERMES_TUI_TOOLSETS entries and fall back to configured CLI toolsets when the override contains no valid toolsets.

* fix(tui): avoid config load for builtin toolsets

Honor built-in HERMES_TUI_TOOLSETS values before loading config and treat all/* as the all-toolsets sentinel.

* fix(cli): honor toolsets in oneshot mode

Forward top-level --toolsets into oneshot agent construction so the flag is not silently ignored outside the TUI path.

* fix(cli): validate oneshot toolsets

Reject invalid-only oneshot toolset overrides before output redirection and clarify TUI fallback warnings.

* fix(cli): preserve all-toolsets sentinel

Map explicit all/* oneshot toolset overrides to the all-toolsets sentinel and replace locals() checks in TUI toolset loading.

* fix(cli): warn on extra all-toolset entries

Warn when all/* toolset overrides include additional ignored entries so typos are still visible.

* fix(tui): honor plugin toolset overrides

Discover plugin toolsets before rejecting unresolved explicit toolset overrides and read raw config for MCP name validation.

* fix(tui): reuse toolset argument normalizer

Share top-level TUI toolset argument parsing with the oneshot path to avoid duplicate normalization logic.

* fix(cli): reject disabled mcp toolsets

Validate explicit toolset overrides against enabled MCP servers only and clarify top-level toolset flag help.

* fix(cli): distinguish disabled mcp from unknown toolsets

Report disabled MCP servers separately from unknown toolset entries and stub plugin discovery in invalid-name tests for determinism.
This commit is contained in:
brooklyn! 2026-04-29 16:55:27 -07:00 committed by GitHub
parent d9bf093728
commit 5e6e8b6af3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 584 additions and 12 deletions

View file

@ -861,10 +861,100 @@ def _load_tool_progress_mode() -> str:
def _load_enabled_toolsets() -> list[str] | None:
explicit = [
item.strip()
for item in os.environ.get("HERMES_TUI_TOOLSETS", "").split(",")
if item.strip()
]
cfg = None
fallback_notice = None
try:
from toolsets import validate_toolset
except Exception:
validate_toolset = None
if explicit and validate_toolset is not None:
built_in = [name for name in explicit if validate_toolset(name)]
unresolved = [name for name in explicit if name not in built_in]
if unresolved:
try:
from hermes_cli.plugins import discover_plugins
discover_plugins()
plugin_valid = [name for name in unresolved if validate_toolset(name)]
except Exception:
plugin_valid = []
if plugin_valid:
built_in.extend(plugin_valid)
unresolved = [name for name in unresolved if name not in plugin_valid]
if any(name in {"all", "*"} for name in built_in):
ignored = [name for name in explicit if name not in {"all", "*"}]
if ignored:
print(
"[tui] HERMES_TUI_TOOLSETS=all enables every toolset; "
f"ignoring additional entries: {', '.join(ignored)}",
file=sys.stderr,
flush=True,
)
return None
if not unresolved:
return built_in
mcp_names: set[str] = set()
mcp_disabled: set[str] = set()
try:
from hermes_cli.config import read_raw_config
from hermes_cli.tools_config import _parse_enabled_flag
raw_cfg = read_raw_config()
mcp_servers = raw_cfg.get("mcp_servers") if isinstance(raw_cfg.get("mcp_servers"), dict) else {}
for name, server_cfg in mcp_servers.items():
if not isinstance(server_cfg, dict):
continue
if _parse_enabled_flag(server_cfg.get("enabled", True), default=True):
mcp_names.add(str(name))
else:
mcp_disabled.add(str(name))
except Exception:
mcp_names = set()
mcp_disabled = set()
mcp_valid = [name for name in unresolved if name in mcp_names]
disabled = [name for name in unresolved if name in mcp_disabled]
unknown = [name for name in unresolved if name not in mcp_names and name not in mcp_disabled]
valid = built_in + mcp_valid
if unknown:
print(
f"[tui] ignoring unknown HERMES_TUI_TOOLSETS entries: {', '.join(unknown)}",
file=sys.stderr,
flush=True,
)
if disabled:
print(
"[tui] ignoring disabled MCP servers in HERMES_TUI_TOOLSETS "
"(set enabled: true in config.yaml to use): "
f"{', '.join(disabled)}",
file=sys.stderr,
flush=True,
)
if valid:
return valid
fallback_notice = "[tui] no valid HERMES_TUI_TOOLSETS entries; using configured CLI toolsets"
try:
from hermes_cli.config import load_config
from hermes_cli.tools_config import _get_platform_tools
cfg = cfg if cfg is not None else load_config()
# Runtime toolset resolution must include default MCP servers so the
# agent can actually call them. Passing ``False`` here is the
# config-editing variant — used when we need to persist a toolset
@ -872,10 +962,18 @@ def _load_enabled_toolsets() -> list[str] | None:
# variant at agent creation time makes MCP tools silently missing
# from the TUI. See PR #3252 for the original design split.
enabled = sorted(
_get_platform_tools(load_config(), "cli", include_default_mcp_servers=True)
_get_platform_tools(cfg, "cli", include_default_mcp_servers=True)
)
if fallback_notice is not None:
print(fallback_notice, file=sys.stderr, flush=True)
return enabled or None
except Exception:
if fallback_notice is not None:
print(
"[tui] no valid HERMES_TUI_TOOLSETS entries and configured CLI toolsets could not be loaded; enabling all toolsets",
file=sys.stderr,
flush=True,
)
return None