18 Commits
Author SHA1 Message Date
Teknium 8e0b489266 fix(config): exempt only known platforms' own bundles from the toolset warning
The own-bundle exemption compared name == hermes-<platform> for any key, so a
misspelt platform key naming its own bundle (telegarm: [hermes-telegarm]) went
silent in both the session-time warning and `hermes config check`, where main
reported it. is_known_platform_bundle() exempts the bundle only for a builtin,
shipped plugins/platforms, left-core or registered platform.
2026-10-28 13:10:38 -07:00
Teknium 06662ba995 fix(config): stop seeding and flagging plugin-platform toolset rows (teams, google_chat)
cli-config.yaml.example seeded `teams: [hermes-teams]` and
`google_chat: [hermes-google_chat]` into every install (install.sh,
install.ps1, the Docker stage2 hook, `hermes doctor --fix`). Both are
bundled plugin platforms with no static toolset, so every schema build
(the kanban gate resolves every saved row) logged "platform 'teams' has
no valid toolsets configured" to errors.log on homes that never set up
Teams or Google Chat, and still did once Teams was enabled.

- Drop the two rows from the example: a plugin platform falls back to
  hermes-<platform> on its own (resolution identical with and without
  the row, before and after plugin discovery).
- The runtime warning and `hermes config check` count a platform's own
  hermes-<platform> bundle as valid, so existing homes (incl. the
  homeassistant row pre-left-core homes carry) stay quiet; a typo in the
  row still warns. The own-bundle check runs before the resolver, so the
  hot path does no manifest scan, and the catalog scan from the salvaged
  commit is dropped (862 catalog YAMLs parsed per call).

Found on Hermes Cloud staging.
2026-10-28 13:10:38 -07:00
Kuxin 3935c04c0f fix(tools): recognize unloaded platform plugins 2026-10-28 13:10:38 -07:00
Maciej Lisiewski d76cce4f11 ci: apply all automatic linter fixes available 2026-10-08 17:22:54 +02:00
kshitijk4poor c731522814 test(config): cover plugin MCP servers' mcp-<server> names; name the canonical form in the docstring
mcp-<server> is the registered toolset name and the bare server name its
alias, not the other way round. The stale-toolset test now stubs one
portable MCP server so the prefixed-name rule for plugin-provided servers
is covered too (removing it turns the test red).
2026-10-03 16:29:54 +05:30
a692570 3cc2e8aa81 fix(config): accept mcp-<server> names in saved platform_toolsets checks
MCP tools register under the toolset name mcp-<server>, an alias that only
exists after MCP discovery. `hermes config migrate`, `hermes update` and
`hermes config check` run without discovery, so a platform_toolsets entry
such as mcp-codegraph for a configured server was reported as an unknown
toolset with a 'hermes-cli' hint (#78102).

saved_toolset_resolver now accepts the prefixed form of every configured
and plugin-provided (portable) MCP server. An mcp-<name> with no such
server is still reported.

Rule from #106715 by @a692570, adapted onto the shared resolver that
landed in #127782.
2026-10-03 16:29:54 +05:30
kshitijk4poor 16c59d0e78 perf(cli): scan platform manifests only for names the cheap checks cannot place
saved_toolset_resolver built the hermes-<platform> bundle set eagerly, so
every migrate_config run (hermes update, config migrate, doctor) and every
config check paid a full bundled+user manifest scan even with no saved
platform_toolsets. The scan is now cached and lazy, like the plugin lookup.
The stale-toolset test stubs the plugin lookups so host-installed plugins
cannot change its verdict.
2026-09-29 20:34:45 +05:30
kshitijk4poor 361dc5cf69 fix(cli): judge saved selections the way the runtime does in config check
Follow-up to the config check diagnostics from #127392.

- Saved platform_toolsets are validated by one resolver,
  toolset_validation.saved_toolset_resolver, shared with
  `hermes config migrate`. The two commands previously disagreed: migrate
  flagged MCP server names and plugin-platform bundles (even suggesting
  'hermes-teams' for 'hermes-teams'), while config check accepted them.
- A configured MCP server is known even when disabled (enabled: false);
  it is inactive, not a typo.
- known_plugin_toolsets is no longer treated as proof that a toolset
  exists. It records what a platform was offered at save time, so it hid
  toolsets of uninstalled plugins, the stale case this check is for.
- Plugin-provided names are consulted only for names nothing else
  explains, so a clean config no longer runs plugin discovery.
- A malformed mcp_servers (a list) no longer crashes the command.
- The top-level `toolsets` check is dropped: the CLI resolves its
  toolsets from --toolsets or platform_toolsets, and reads the top-level
  key only for the legacy kanban opt-in.
- Disabled platforms are read with the runtime's plugins.disabled reader,
  so a non-list value (which the runtime ignores) is no longer reported
  as disabling a platform. Required credentials come from
  _platform_manifest_env_entries, which now takes optional=False.
- Tests use a synthetic platform manifest instead of the real Telegram
  one, and assert config check and migrate report the same names.
2026-09-29 20:34:45 +05:30
teknium1 b5d7a7b15d fix(config): hermes doctor and plugins enable read a list-literal platform_toolsets string
#116691 taught the runtime (`_get_platform_tools`) to read the
`'["browser", "terminal"]'` string an older `hermes config set` stored as
the user's real toolset selection, but the two other readers of the section
kept the old `isinstance(raw, list)` gate:

- `validate_platform_toolsets` (hermes doctor / startup warnings) reported
  "invalid toolset value ... falling back to 'hermes-cli'" for a value the
  runtime resolves to browser+terminal — a false warning.
- `plugins_cmd._toggle_plugin_toolset` skipped the string entry, so
  `hermes plugins enable <p>` never reached that platform.
- `nous_subscription._toolset_enabled` treated it as an empty list.

Move the list-literal parse into one function,
`toolset_validation.parse_platform_toolsets_value`, and route all four
readers through it; the plugin toggle re-saves the entry as a real list so
the string never persists past the next write. `_coerce_platform_toolsets_value`
keeps its once-per-platform warning for values that do not parse.

Why one parser: three readers with three notions of "configured" is exactly
how #115866 went unnoticed. Follow-up to #116691 (independent review finding).
2026-09-20 12:44:48 -07:00
Teknium fe8bfc1d68 refactor(hermes_cli): tools_config group — dense docstring reflow (word-identical, AST-verified) 2026-09-02 21:31:17 -07:00
Teknium 316427f215 refactor(hermes_cli): tools_config group — AST-neutral closer hugging and item packing (data tables kept expanded) 2026-09-02 21:22:11 -07:00
Teknium 3df6a09d62 refactor(hermes_cli): tools_config_post_setup/mcp/toolset_validation — shared _run_text, flattened branches 2026-09-02 20:48:27 -07:00
Teknium f332d5b2ba refactor(hermes_cli): tools_config group — AST-neutral bracket hugging 2026-09-02 20:38:18 -07:00
Teknium 5d4b97939e refactor(hclib): config — config/config_migrations/tools_config/toolset_* dispatch tables and dedupe 2026-09-02 14:42:18 -07:00
phi hu 081030a7dd fix(config): warn for empty platform toolsets 2026-08-31 10:08:31 -07:00
phihuandClaude Opus 5 ccd32a9f0b fix(config): warn when a platform_toolsets entry is an empty list
validate_platform_toolsets() accumulated a single valid_count across every
platform, so the "zero valid toolsets" safety net was suppressed as soon as any
one platform carried a valid toolset. A platform wiped to [] — the active one,
typically cli — therefore produced no warning at all.

resolve_enabled_toolsets() honours that empty list verbatim ([] is a list, so
the platform-default fallback is skipped), leaving the agent with zero tool
schemas. The model then has nothing to call and emits the tool call as
assistant text with finish_reason=stop: no error, no warning, no log entry.
That is the silent-failure mode this module was written to prevent (#38798).

Note the asymmetry this leaves intact: a malformed *string* value is not a list,
so it falls back to the platform default and fails open (#78103); an empty list
fails closed. The fail-closed resolution is deliberate (the explicit_empty_
selection contract in tools_config.py, and #82010 wants it persistable), so this
only adds the missing warning and does not change resolution semantics.

Fixes #89050

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 10:08:31 -07:00
teknium1 5b751dc0ad chore: remove unused imports and dead locals (ruff F401/F841 sweep)
Cleans F401 unused imports and F841 dead local assignments across
root *.py, agent/, hermes_cli/, tools/, gateway/, cron/, tui_gateway/
(tests/, plugins/, skills/ excluded).

Intentionally KEPT (false positives / test-patch surfaces):
- agent/transports/__init__.py package re-exports
- cli.py browser_connect re-exports (DEFAULT_BROWSER_CDP_URL area,
  used by tests/cli/test_cli_browser_connect.py)
- hermes_cli/main.py _prompt_auth_credentials_choice /
  _model_flow_bedrock_api_key (accessed via main_mod attr in tests)
- gateway/run.py aliased replay_cleanup + whatsapp_identity re-exports
  and _PORT_BINDING_PLATFORM_VALUES (test-referenced)
- hermes_cli/web_server.py get_running_pid (tests monkeypatch it) and
  _OAUTH_TOKEN_URL availability probe
- hermes_cli/config.py get_process_hermes_home re-export (noqa'd F811
  chain) and yaml availability-probe import
- hermes_cli/nous_subscription.py managed_nous_tools_enabled
  (tests patch hermes_cli.nous_subscription.managed_nous_tools_enabled)
- try/except ImportError availability probes (env_loader, tts_tool,
  mcp_tool, web_server anthropic OAuth block)
- tools/web_tools.py noqa F401 re-exports
- hermes_cli/setup_whatsapp_cloud.py:263 'proceed' skipped: possible
  missing-guard bug, flagged for separate review
- unused function parameters (signature changes out of scope)

Side-effect RHS calls preserved where only the binding was dead
(e.g. web_server proc = _spawn_hermes_action -> bare call).
2026-07-29 11:53:39 -07:00
lEWFkRAD 41ede84b93 fix(config): surface invalid platform_toolsets instead of silently dropping tools (#38798)
A config migration (or hand-edit) that leaves an invalid toolset name in
`platform_toolsets` — e.g. the #38798 corruption that rewrote `hermes-cli` to
the non-existent `hermes` — silently disabled all affected tools:
resolve_toolset() returns [] for an unknown name, so the agent quietly lost its
tools with no error, warning, or log entry and degraded to text-only replies.

Surface it loudly at two points:
- After migration (migrate_config): validate platform_toolsets and record/print
  a warning per unknown name, with a `hermes-<platform>` suggestion when that
  would have been valid (the exact #38798 shape).
- At runtime (_get_platform_tools): if a platform was explicitly configured but
  every toolset name is invalid, log a warning when tools are resolved for a
  session — so an ALREADY-corrupted config is caught at startup, not only on the
  next `hermes update`.

Logic lives in a new pure, side-effect-free helper (toolset_validation.py) with
validate_toolset injected, so it is unit-testable without the tool registry.

Note: the original v25→v26 migration that caused the corruption no longer
exists (config format is now v30; no migration step rewrites toolset names).
This change is the durable defense against the silent-failure mode regardless
of cause, matching the issue's "Expected: log a warning".

Salvaged from #39207 by @lEWFkRAD (authorship preserved via cherry-pick).
Tests: 9 helper cases (incl. the #38798 corruption shape, mixed valid/invalid,
zero-tools state, non-dict/scalar/non-string) + a runtime caplog test — both the
helper warning and the runtime guard mutation-verified to fail without the fix.

Closes #38798. Supersedes #39581 (prevent-in-v25→v26 — that path is gone),
#41006 / #40208 (repair-migration for already-corrupted configs).
2026-06-26 14:07:43 +05:30