Skip to content

Follow up Hermes Agent upstream updates: MCP ssl_verify/skip_preflight/sampling dropped and clobbered, stale delegate_task toolsets argument #2414

Description

@dyoshikawa

Summary

No Hermes Agent release newer than the one rulesync is validated against has shipped (v2026.7.20 / v0.19.0 is still Latest), but three drifts exist: three MCP per-server keys are dropped and destroyed on regenerate, and the generated subagent/command plugins still pass a toolsets argument that delegate_task no longer accepts.

This does not overlap with open issue #2389 (honor HERMES_HOME for all global configuration), whose scope is global config root resolution rather than the per-feature schemas below.

Recent Releases

gh release list -R NousResearch/hermes-agent confirms v2026.7.20 (Hermes Agent v0.19.0, 2026-07-20) is still Latest as of 2026-07-26 — i.e. rulesync's documented compatibility baseline is current.

Version Tag Date Notes
v0.19.0 "Quicksilver" v2026.7.20 2026-07-20 release — smart approvals default + approvals.deny user deny rules, mcp__server__tool naming, hosted MCP OAuth + redirect_uri/redirect_host, live subagent transcripts + durable background delegation, stacked slash-skill invocations, pre_tool_call approve escalation re-landed. Rolls up the v0.18.1/v0.18.2 windows.
v0.18.2 v2026.7.7.2 2026-07-07 release — WhatsApp Baileys dependency patch only. No config surface.
v0.18.1 v2026.7.7 2026-07-07 release — infrastructure patch tag, uncurated; contents documented in v0.19.0.
v0.18.0 "Judgment" v2026.7.1 2026-07-01 release
v0.17.0 v2026.6.19 2026-06-19 release
v0.16.0 "Surface" v2026.6.5 2026-06-06 release
v0.15.x v2026.5.28v2026.5.29.2 2026-05-28/29 mTLS + ssl_verify MCP keys land in this window (PR #33721, doc commit 2026-05-28)

Unreleased main changes since v0.19.0 were also inspected and none require a rulesync change: fnmatch glob support in tools.include/exclude filters (2026-07-22), approvals.smart_policy / approvals.denial_breaker_threshold (2026-07-26), and Slack reaction:added / reaction:removed gateway hook events (2026-07-23).

Dry-runs used for validation (from /workspace):

--targets hermesagent --features "*" --dry-run
  → .hermes.md, .hermes/plugins/rulesync-ignore/*, .hermes/rulesync/subagents/*.json,
    .hermes/plugins/rulesync-subagents/*, .hermes/config.yaml (activation);
    mcp/hooks/permissions skipped with "global scope only"
--targets hermesagent --features "*" --global --dry-run
  → ~/.hermes/config.yaml (mcp + hooks + permissions), ~/.hermes/skills/**, ~/.hermes/rulesync/subagents/*

Gaps

1. mcp — ssl_verify, skip_preflight and sampling are dropped, and destroyed on regenerate

  • Upstream: the MCP config reference documents these per-server keys under mcp_servers.<name>:

    • ssl_verify (bool or PEM CA-bundle path) — landed with the mTLS work, doc commit 87e5b2f (2026-05-28, PR #33721), the same PR as client_cert / client_key.
    • skip_preflight (bool, HTTP) — bypasses the fail-fast content-type probe; doc commit 81becec4 (2026-07-06), shipped in v0.19.0.
    • sampling (mapping: enabled, model, max_tokens_cap, timeout, max_rpm, max_tool_rounds, allowed_models, log_level) — MCP sampling support (PR chore: remove AmazonQ Developer CLI support #753).
  • rulesync (global): copyHermesAdvancedFields (src/features/mcp/hermesagent-mcp.ts) whitelists only auth, client_cert, client_key, connect_timeout, supports_parallel_tool_calls, oauth.{redirect_uri,redirect_host,client_id,client_secret,redirect_port,scopes}, idle_timeout_seconds, max_lifetime_seconds. ssl_verify, skip_preflight and sampling are in neither the export path (convertServerToHermes) nor the import path (convertFromHermesFormat).

    Two user-visible consequences:

    1. Those keys cannot be authored from .rulesync/mcp.json at all — not even through the hermesagent override, which is populated exclusively from copyHermesAdvancedFields.
    2. They are destroyed on regenerate: mergeHermesMcpServers merges at the server-name level ({...existingMcpServers, ...mcpServers}), so a hand-written ssl_verify: "~/ca.pem" on a server rulesync also manages is replaced wholesale by the converted entry. This is the same class of bug Follow up Hermes Agent upstream updates: MCP advanced per-server fields (auth/oauth, mTLS, connect_timeout) dropped on export #2236 fixed for auth/mTLS; ssl_verify was part of that same upstream PR but was missed.

2. subagents / commands — generated plugins still pass a removed delegate_task argument

  • Upstream: delegate_task no longer accepts a model-facing toolsets argument. Confirmed in tools/delegate_tool.py:

    # NOTE: nested delegation is granted by role='orchestrator' (which re-adds the
    # "delegation" toolset in _build_child_agent), NOT by the model naming toolsets
    # — the model has no toolsets argument. Subagents inherit the parent's toolsets.

    The signature is delegate_task(goal, context, tasks, max_iterations, role, background, parent_agent), and the registry handler explicitly picks only those keys out of args. Documented in commit 35c5781 "docs(delegation): align guidance with current contract" (2026-07-17), which rewrote the delegation docs to state: "delegate_task does not accept a model-facing toolsets parameter. Each subagent inherits the parent's enabled toolsets so the model cannot grant a child capabilities that the parent does not have." Shipped in v0.19.0.

  • rulesync (project + global for subagents, global for commands): both generated Python plugins still send toolsets in the dispatch_tool payload — src/features/subagents/hermesagent-subagent.ts (toolsets = subagent.get("toolsets") or ["terminal", "file", "web"], "toolsets": toolsets), so every emitted .hermes/rulesync/subagents/<slug>.json carries a hardcoded toolsets: ["terminal", "file", "web"]; and src/features/commands/hermesagent-command.ts ("toolsets": ["terminal", "file", "web"]).

  • Because the registry handler ignores unknown keys, this fails silently rather than erroring — but the emitted spec advertises a per-subagent tool restriction Hermes no longer honors, and the toolsets field in the generated JSON is dead schema that an importer, or a user editing the file, would reasonably trust.

3. skills — skills.external_dirs unused, so project-scope skills stay unsupported (minor)

  • Upstream: skills.external_dirs in ~/.hermes/config.yaml adds extra skill roots that are fully integrated (system-prompt index, skills_list, skill_view, /skill-name slash commands), with ~ and ${VAR} expansion — external skill directories (introduced 2026-03-29, PR #3678 — not a recent change; listed only because rulesync has never claimed it).
  • rulesync (global only): HermesagentSkill.getSettablePaths() (src/features/skills/hermesagent-skill.ts) hardcodes HERMESAGENT_SKILLS_DIR_PATH = .hermes/skills, and the matrix marks skills 🌏 global-only. rulesync already writes to the global config.yaml from a project generate run (the project-plugin activation path in src/features/shared/hermes-project-plugin-activation.ts), so the mechanism for registering a project path exists.

Proposed Follow-up

  1. Extend copyHermesAdvancedFields to carry ssl_verify (boolean | string), skip_preflight (boolean) and sampling (plain object, deep-copied with prototype-pollution key stripping) verbatim in both directions, and cover them in hermesagent-mcp.test.ts.
  2. Drop toolsets from the generated delegate_task payload and from the emitted .hermes/rulesync/{subagents,commands}/*.json spec in both adapters (and from the round-trip readers). If a per-subagent knob is still wanted, role (leaf | orchestrator) and max_iterations are the parameters the current schema actually accepts.
  3. Optional, and a design decision rather than a regression: emit project skills to <project>/.hermes/skills/ and append that absolute path to skills.external_dirs in the resolved HERMES_HOME config, mirroring the project-plugin activation flow, to earn a ✅ in the skills column.

Per-feature verification (non-gaps)

Feature Upstream surface rulesync today Verdict
rules .hermes.md / HERMES.md root context file (walks to git root); nested AGENTS.md/CLAUDE.md/.cursorrules progressive discovery; ~/.hermes/SOUL.md is identity, not user rules project — root .hermes.md, non-root bodies folded in (decision recorded in #2214) no gap
ignore no native ignore file; enforced via a project plugin on pre_tool_call + transform_tool_result project — guards read_file/write_file/patch and filters search_files; the Hermes file toolset still contains exactly these four tools no gap
hooks shell hooks under hooks: in ~/.hermes/config.yaml; VALID_HOOKS event keys global — rulesync's native list is a superset of the documented VALID_HOOKS table (incl. pre_verify, subagent_start/stop, pre_gateway_dispatch, pre/post_approval_response, the three transform_*, and kanban_task_*). Gateway hooks (HOOK.yaml + handler.py, incl. the new reaction:* events) are Python-only and out of the canonical command-hook model no gap
permissions command_allowlist, approvals.{mode,deny,denial_breaker_threshold,smart_policy}, security.{redact_secrets,tirith_*,website_blocklist} global — bash deny → approvals.deny, webfetch deny → security.website_blocklist, allow → command_allowlist, and every other approvals.*/security.*/skills.*/memory.* key passes through buildHermesOverride; the new smart_policy / denial_breaker_threshold keys are already expressible no gap
checks pre_verify hook (added 2026-06-30) project, simulated via a rulesync-checks plugin registering pre_verify no gap

Open questions / unconfirmed

  • mcp__server__tool naming convention (v0.19.0 highlight, PR #52750) vs. mcp_<server>_<tool> still documented in mcp-config-reference.md#tool-naming. The two primary sources disagree and this could not be resolved from docs alone. It does not affect rulesync either way: tools.include/exclude filters use the original (unsanitized) MCP tool names, which is exactly what rulesync passes through from enabledTools/disabledTools.
  • fnmatch globs in tools.include/exclude (main, 2026-07-22, unreleased) — purely a widening of pattern semantics; rulesync passes the strings through verbatim, so no adapter change is implied.
  • The hooks.md shell-hook section says hooks are declared in cli-config.yaml in its prose intro but ~/.hermes/config.yaml in its comparison table and every example. cli-config.yaml.example is the in-repo template that installs as ~/.hermes/config.yaml, so rulesync's target file is correct; the prose is an upstream doc slip.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    consideringenhancementNew feature or requestmaintainer-scrapRough notes for AI implementation. Not for human eyes.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions