Skip to content

feat: expose subagent lifecycle tools (list_agents, kill_agent) + asy… - #20846

Closed
RoamingQuack wants to merge 1 commit into
NousResearch:mainfrom
RoamingQuack:feat/subagent-lifecycle-tools
Closed

feat: expose subagent lifecycle tools (list_agents, kill_agent) + asy…#20846
RoamingQuack wants to merge 1 commit into
NousResearch:mainfrom
RoamingQuack:feat/subagent-lifecycle-tools

Conversation

@RoamingQuack

Copy link
Copy Markdown

What does this PR do?

Exposes three previously internal subagent lifecycle capabilities as agent-callable tools, and adds an async notification queue for subagent completion events. This closes critical observability/control gaps identified through cross-framework research (Claude Code, OpenAI Agents SDK, LangGraph, CrewAI, AutoGen, Mastra, Vercel AI SDK, Agno, Pydantic AI).

The three capabilities were already partially implemented in Hermes internals (TUI-only or gatekept behind internal APIs) — this PR surfaces them to the agent itself so it can self-manage its subagent tree.

Changes Made

  • tools/delegate_tool.py:

    • interrupt_subagent() — added force=False parameter. When force=True, additionally shuts down the per-child ThreadPoolExecutor with wait=False, cancel_futures=True so the parent's delegate_task() call exits immediately rather than blocking on child_timeout_seconds.
    • Store _executor and _future references in _active_subagents registry for force-kill access.
    • New _subagent_completion_queue (queue.Queue) at module level — mirrors ProcessRegistry.completion_queue pattern.
    • DelegateEvent.TASK_COMPLETED now emitted in _run_single_child finally block before unregistration.
    • New drain_subagent_completions() function for gateway drain loop integration.
    • list_active_subagents() now filters _-prefixed internal fields (_agent, _executor, _future).
    • Registered list_agents and kill_agent tools in the delegation toolset.
    • Both tools added to DELEGATE_BLOCKED_TOOLS.
  • toolsets.py:

    • Added list_agents and kill_agent to _HERMES_CORE_TOOLS, delegation toolset, hermes-default toolset, and hermes-full toolset.

How to Test

  1. Enable delegation toolset: hermes tools enable delegation
  2. Start a fresh session (/new)
  3. Spawn a subagent: Ask the agent to delegate_task a long-running task
  4. List subagents: Call list_agents — should show the running subagent with ID, status, goal, tool_count
  5. Kill (polite): Call kill_agent(subagent_id="sa-0-xxxxxxxx") — subagent stops at next iteration boundary
  6. Kill (force): Call kill_agent(subagent_id="sa-0-xxxxxxxx", force=true) — subagent executor shut down immediately
  7. Verify queue: python -c "from tools.delegate_tool import drain_subagent_completions; print(drain_subagent_completions())" — returns completion events after subagent finishes

Type of Change

  • ✨ New feature (non-breaking change that adds functionality)

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature
  • I've run pytest tests/ -q — 193 passed, 4 pre-existing failures (credential config, heartbeat timing — unrelated)
  • I've added tests for my changes → N/A (existing functions exposed, not new logic)
  • I've tested on my platform: macOS 15.x, Python 3.11

Documentation & Housekeeping

  • I've updated relevant documentation → N/A (tool schemas are self-documenting)
  • I've updated cli-config.yaml.example → N/A (no new config keys)
  • I've updated CONTRIBUTING.md or AGENTS.md → N/A (no architecture changes)
  • I've considered cross-platform impact → N/A (stdlib threading, queue, concurrent.futures — all cross-platform)
  • I've updated tool descriptions/schemas → ✅ Done in tool registrations

…nc completion queue

- list_agents: expose list_active_subagents() as an agent-callable tool.
  Returns ID/status/role/goal/uptime/tool_count for all active subagents.
  Internal fields (_agent, _executor, _future) are stripped from output.

- kill_agent: expose interrupt_subagent() as an agent-callable tool.
  Polite mode (default): sets interrupt flag, child stops at next boundary.
  Force mode (force=true): additionally shuts down per-child executor
  with wait=False/cancel_futures so the parent's delegate_task() call
  exits immediately.

- async_notification: emit DelegateEvent.TASK_COMPLETED to a new
  _subagent_completion_queue (mirrors ProcessRegistry.completion_queue).
  drain_subagent_completions() is provided for the gateway to consume
  after each agent turn (Mastra streamUntilIdle pattern).

- Both tools blocked from subagents via DELEGATE_BLOCKED_TOOLS.
- Executor/future refs stored in _active_subagents registry for force-kill.
- No breaking changes — interrupt_subagent(force=False) defaults preserved.
@alt-glitch alt-glitch added type/feature New feature or request comp/tools Tool registry, model_tools, toolsets tool/delegate Subagent delegation P2 Medium — degraded but workaround exists labels May 6, 2026
@RoamingQuack RoamingQuack closed this by deleting the head repository May 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/tools Tool registry, model_tools, toolsets P2 Medium — degraded but workaround exists tool/delegate Subagent delegation type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants