Discussion: https://gist.github.com/PowerCreek/833cda14a6528f031fcc334305e56c63
Problem
After landing G1–G5 + #67 trio + devagentic#218 silo-v2 + #71 lazy-load, the poly-explorer worker spawned successfully but empties across all prompts that exercise MCP tools, even with #71's reduced preamble. Direct API probe with a single tool attached returns a perfect tool_call; hermes attaching all 33 tools per turn produces finish_reason=stop tool_calls=0 content="". Pure tool-paralysis from schema-attention overload.
This is the integration ceiling: the fused stack is architecturally complete but the per-call tool surface is too broad for current models to use without choking.
Root cause (static analysis)
agent.tools populated once at session boot via agent/agent_init.py:818:
agent.tools = _ra().get_tool_definitions(enabled_toolsets=…, disabled_toolsets=…, …)
- Same
agent.tools attached to EVERY chat-completions request — three call sites at agent/conversation_loop.py:513 / 559 / 3471: tools=agent.tools or None. No per-turn filtering.
- Existing
--enable-toolset / --disable-toolset flags only work at TOOLSET (plugin) granularity. Post-G1-G5 stack pulls 33 tools across 6 plugins; toolset disabling is too coarse for surgical narrowing (disabling canvas loses all canvas tools as a unit).
Proposed fix
Add HERMES_TOOLS_SUBSET env var — comma-separated allow-list of tool names. When set, agent.tools is filtered to only those tools at session boot. Composes with existing toolset flags (subset narrows what's already enabled; doesn't override).
Implementation (~15 LOC)
Insert immediately after agent.tools = _ra().get_tool_definitions(…) in agent_init.py:818:
_subset_raw = (os.environ.get("HERMES_TOOLS_SUBSET") or "").strip()
if _subset_raw and agent.tools:
_wanted = {n.strip() for n in _subset_raw.split(",") if n.strip()}
if _wanted:
_before = len(agent.tools)
agent.tools = [
t for t in agent.tools
if (t.get("function") or {}).get("name") in _wanted
]
if not agent.quiet_mode:
_kept = sorted({(t.get("function") or {}).get("name", "?")
for t in agent.tools})
print(f"🎯 HERMES_TOOLS_SUBSET narrowed tool surface: "
f"{_before} → {len(agent.tools)} ({', '.join(_kept)})")
agent.valid_tool_names recomputation immediately below (agent.valid_tool_names = {tool["function"]["name"] for tool in agent.tools}) automatically reflects the filtered set — no extra change needed.
Operator usage
# Polynomial-explorer in observation mode (read-only):
HERMES_TOOLS_SUBSET=grafted_context_fetch,lane_h_list,lane_h_fetch,doc_search,silo_query hermes
# Polynomial-explorer in execution mode (read + confer):
HERMES_TOOLS_SUBSET=grafted_context_fetch,doc_search,doc_write,silo_query,confer_run hermes
# Default profile dev work (no narrowing needed):
hermes # all 33 tools attached as today
Acceptance
HERMES_TOOLS_SUBSET=A,B,C env in the hermes process narrows agent.tools to just A/B/C (when those names exist in the underlying registry).
- Empty / unset env preserves current behavior (no filtering).
- Names not present in the registry are silently ignored (plugins add/remove tools at runtime; pre-validation would over-warn).
- One INFO line at session start showing the narrowing effect (
33 → 5 (grafted_context_fetch, …)).
- 5-6 unit tests cover: env unset → no change · env set with subset → filtered · env set with names not in registry → silent skip · env set to empty string → no change · composability with
enabled_toolsets (toolset filter runs first, env subset narrows further).
- README addition in
agent/ or docs/ documenting the env var + when to use it.
Why now
Out of scope
Severity
Quick unblock for polynomial-explorer + any future >5-tool vertical session. Small surgical change. Filing per orchestrator's direct ask.
Discussion: https://gist.github.com/PowerCreek/833cda14a6528f031fcc334305e56c63
Problem
After landing G1–G5 + #67 trio + devagentic#218 silo-v2 + #71 lazy-load, the poly-explorer worker spawned successfully but empties across all prompts that exercise MCP tools, even with #71's reduced preamble. Direct API probe with a single tool attached returns a perfect tool_call; hermes attaching all 33 tools per turn produces
finish_reason=stop tool_calls=0 content="". Pure tool-paralysis from schema-attention overload.This is the integration ceiling: the fused stack is architecturally complete but the per-call tool surface is too broad for current models to use without choking.
Root cause (static analysis)
agent.toolspopulated once at session boot viaagent/agent_init.py:818:agent.toolsattached to EVERY chat-completions request — three call sites atagent/conversation_loop.py:513 / 559 / 3471:tools=agent.tools or None. No per-turn filtering.--enable-toolset/--disable-toolsetflags only work at TOOLSET (plugin) granularity. Post-G1-G5 stack pulls 33 tools across 6 plugins; toolset disabling is too coarse for surgical narrowing (disablingcanvasloses all canvas tools as a unit).Proposed fix
Add
HERMES_TOOLS_SUBSETenv var — comma-separated allow-list of tool names. When set,agent.toolsis filtered to only those tools at session boot. Composes with existing toolset flags (subset narrows what's already enabled; doesn't override).Implementation (~15 LOC)
Insert immediately after
agent.tools = _ra().get_tool_definitions(…)inagent_init.py:818:agent.valid_tool_namesrecomputation immediately below (agent.valid_tool_names = {tool["function"]["name"] for tool in agent.tools}) automatically reflects the filtered set — no extra change needed.Operator usage
Acceptance
HERMES_TOOLS_SUBSET=A,B,Cenv in the hermes process narrowsagent.toolsto just A/B/C (when those names exist in the underlying registry).33 → 5 (grafted_context_fetch, …)).enabled_toolsets(toolset filter runs first, env subset narrows further).agent/ordocs/documenting the env var + when to use it.Why now
agent.toolsis the same hook point R1/R2 will use, just driven by a classifier instead of an env var.Out of scope
Severity
Quick unblock for polynomial-explorer + any future >5-tool vertical session. Small surgical change. Filing per orchestrator's direct ask.