# NousResearch/hermes-agent · Composite toolset expansion in delegate_task intersection

## Open now

- 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.

## TL;DR

- 4 member(s): 0 open issue(s), 1 closed issue(s), 0 open PR(s), 1 merged PR(s), 2 PR(s) closed without merging.
- A fix was merged to main in PR #21300. Verify it addresses your case.
- **Recommended:** read PR #21300 (and any competing PRs below) before filing. Confirm the merged change covers your symptom.

## Maintainer resolution verdict

- Internal-tracker verdict over this cluster's issue members: `resolved` · deciding PR(s): `pr-21300`. This is distinct from the fix status above. Weigh it yourself.

## How this cluster was grouped

- Link evidence (`STRUCTURAL_SPANNING`): every member is connected by structural evidence: shared changed files, or a closing/superseded reference.
- Pairwise link density `0.50`: the share of member 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.

## Closed / duplicate issues

- #19447: Bug: delegate_task toolset intersection fails with composite toolsets (e.g. hermes-cli)

## Prior & competing PRs

- **#21300: fix(delegate): expand composite toolsets before intersection (salvage #19455)**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`
  - files: `tests/tools/test_delegate_composite_toolsets.py`, `tools/delegate_tool.py`
  - fix hunks:
  - `tests/tools/test_delegate_composite_toolsets.py`

    ```diff
    @@ -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`

    ```diff
    @@ -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]:
    ```

    ```diff
    @@ -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
    ```
- **#19455: fix(delegate): expand composite toolsets before intersection in delegate_task**. closed without merge
  - maintainer direction: category `silent_close`; implication `active`
- **#19468: fix(tools): expand composite toolsets in delegate intersection**. closed without merge
  - maintainer direction: category `superseded_by_pr`; implication `superseded`; refs `#19455`

## Fix status

- Merged to main in PR #21300 (cited above). No release data is tracked. Verify downstream.
- Fix last verified (merged) `2026-05-07`.
