NousResearch/hermes-agent

Composite toolset expansion in delegate_task intersection

Cluster 19447. Data as of

Nothing open on GitHub. Fix merged in #21300 on 7 May 2026. Check it covers your case.

The short answer

0open issues
0open PRs
4items grouped
Known?
Yes. 4 related items grouped here, 1 issue already closed.
Fix event
A fix was merged to main in PR #21300. Check it covers your case. No release data is tracked.
Tracker
The maintainers' tracker marks this resolved, deciding PR #21300. That is separate from the fix event.
Still open
Nothing open on GitHub.

Members appear to center on delegate_task toolset intersection when composite toolsets (e.g. hermes-cli) are involved. One item reports this as a bug, and the others are titled as fixes that expand composite toolsets before the intersection, one of them noted as a salvage of #19455.

Open issues0

No open issues.

Open PRs0

No open pull requests.

Fixes on record

Merged to main. A stamp counts the issues a fix is linked to.

  • #21300 fix(delegate): expand composite toolsets before intersection (salvage #19455)

    merged to main

    files: tests/tools/test_delegate_composite_toolsets.py, tools/delegate_tool.py

    Show the diff
    • tests/tools/test_delegate_composite_toolsets.py
      @@ -0,0 +1,46 @@
      +"""Tests for composite toolset expansion in delegate_task intersection."""
      +
      +import unittest
      +from unittest.mock import patch
      +
      +from tools.delegate_tool import _expand_parent_toolsets
      +
      +
      +class TestExpandParentToolsets(unittest.TestCase):
      +    """Verify _expand_parent_toolsets recognises individual toolsets within composites."""
      +
      +    def test_composite_hermes_cli_expands_web(self):
      +        """hermes-cli includes web_search/web_extract → 'web' should be in expansion."""
      +        expanded = _expand_parent_toolsets({"hermes-cli"})
      +        self.assertIn("web", expanded)
      +        self.assertIn("terminal", expanded)
      +        self.assertIn("browser", expanded)
      +        # Original composite is preserved
      +        self.assertIn("hermes-cli", expanded)
      +
      +    def test_individual_toolset_unchanged(self):
      +        """When parent already uses individual toolsets, expansion keeps them."""
      +        expanded = _expand_parent_toolsets({"web", "terminal"})
      +        self.assertIn("web", expanded)
      +        self.assertIn("terminal", expanded)
      +
      +    def test_empty_parent_toolsets(self):
      +        expanded = _expand_parent_toolsets(set())
      +        self.assertEqual(expanded, set())
      +
      +    def test_unknown_toolset_passthrough(self):
      +        """Unknown toolset names pass through without error."""
      +        expanded = _expand_parent_toolsets({"nonexistent-toolset-xyz"})
      +        self.assertIn("nonexistent-toolset-xyz", expanded)
      +
      +    def test_intersection_with_expanded_composite(self):
      +        """End-to-end: requesting ['web'] from parent with ['hermes-cli'] yields ['web']."""
      +        parent_toolsets = {"hermes-cli"}
      +        expanded = _expand_parent_toolsets(parent_toolsets)
      +        toolsets = ["web"]
      +        child_toolsets = [t for t in toolsets if t in expanded]
      +        self.assertEqual(child_toolsets, ["web"])
      +
      +
      +if __name__ == "__main__":
      +    unittest.main()
    • tools/delegate_tool.py
      @@ -462,6 +462,37 @@ def _is_mcp_toolset_name(name: str) -> bool:
           return bool(target and str(target).startswith("mcp-"))
       
       
      +def _expand_parent_toolsets(parent_toolsets: set) -> set:
      +    """Expand composite toolsets so individual toolset names are recognized.
      +
      +    When a parent uses a composite toolset like ``hermes-cli`` (which bundles
      +    all core tools), the child may request individual toolsets such as ``web``
      +    or ``terminal``.  A simple name-based intersection would reject them
      +    because ``"web" != "hermes-cli"``.
      +
      +    This helper collects the tool names from each parent toolset, then adds
      +    the names of any individual toolsets whose tools are a *subset* of the
      +    parent's available tools.  The original parent toolset names are preserved.
      +    """
      +    parent_tool_names: set = set()
      +    for ts_name in parent_toolsets:
      +        ts_def = TOOLSETS.get(ts_name)
      +        if ts_def:
      +            parent_tool_names.update(ts_def.get("tools", []))
      +
      +    if not parent_tool_names:
      +        return set(parent_toolsets)
      +
      +    expanded = set(parent_toolsets)
      +    for ts_name, ts_def in TOOLSETS.items():
      +        if ts_name in expanded:
      +            continue
      +        ts_tools = ts_def.get("tools", [])
      +        if ts_tools and set(ts_tools).issubset(parent_tool_names):
      +            expanded.add(ts_name)
      +    return expanded
      +
      +
       def _preserve_parent_mcp_toolsets(
           child_toolsets: List[str], parent_toolsets: set[str]
       ) -> List[str]:
      @@ -907,8 +938,11 @@ def _build_child_agent(
               parent_toolsets = set(DEFAULT_TOOLSETS)
       
           if toolsets:
      -        # Intersect with parent — subagent must not gain tools the parent lacks
      -        child_toolsets = [t for t in toolsets if t in parent_toolsets]
      +        # Intersect with parent — subagent must not gain tools the parent lacks.
      +        # Expand composite toolsets (e.g. hermes-cli) so that individual
      +        # toolset names (e.g. web, terminal) are recognised during intersection.
      +        expanded_parent = _expand_parent_toolsets(parent_toolsets)
      +        child_toolsets = [t for t in toolsets if t in expanded_parent]
               if _get_inherit_mcp_toolsets():
                   child_toolsets = _preserve_parent_mcp_toolsets(
                       child_toolsets, parent_toolsets

2 PRs closed without merging

From the close comments: closed without comment 1 · superseded by another PR 1

  • #19455 fix(delegate): expand composite toolsets before intersection in delegate_task · closed without comment
  • #19468 fix(tools): expand composite toolsets in delegate intersection · superseded by another PR → #19455
Closed issues · 1
  • Closed#19447Bug: delegate_task toolset intersection fails with composite toolsets (e.g. hermes-cli)
How this cluster was grouped
  • Link evidence (structural links throughout): every member is connected by structural evidence: shared changed files, or a closing/superseded reference.
  • Pairwise link density 0.50: the share of item pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. 1.00 means every pair is linked.

Pick up a Lab task

Check whether a fix covers the open issues here, with your own agent. Bring back evidence for maintainers.

Open the Lab

At a glance

Merged PRs
#21300
Closed issues
1
Attempts
2 PRs closed without merging
Fix link checked
7 May 2026

For your agent

The same facts, typed.