Bug Description
Two bugs in tools/delegate_tool.py reduce delegate_task subagent reliability:
Bug A — Status misclassification (line ~1609-1617)
When a child agent completes normally (completed=True) but returns an empty final_response, the status is set to "failed" instead of "completed". This causes parents to treat successfully-completed children as failures.
The current logic only checks elif summary: (non-empty final_response) to determine "completed" status, ignoring the completed flag entirely.
Bug B — Missing interrupt on batch parent interrupt (line ~2104-2139)
When a parent is interrupted during batch execution, pending child futures are abandoned with a fabricated "interrupted" status — but child_agent.interrupt() is never called. The child threads continue running, making API calls and consuming tokens as orphan processes.
Steps to Reproduce
- Use delegate_task in batch mode with 2+ children
- Interrupt the parent while children are still running (e.g. via /stop or sending a new message)
- Observe that pending children continue consuming API tokens in background
- Also: trigger a child to complete with an empty final_response (e.g. reasoning exhaustion)
- Check the returned status — it will be "failed" despite the child having completed normally
Expected Behavior
- Bug A: Child agents that complete normally should report
status: "completed" regardless of whether final_response is empty. The completed flag from run_conversation() should be respected.
- Bug B: When the parent is interrupted, all pending child agents should have
.interrupt() called on them before the parent abandons the batch, preventing orphan thread resource consumption.
Actual Behavior
- Bug A:
status = "failed" even when completed = True and final_response = ""
- Bug B: Child threads keep running after parent interrupt. Each abandoned child continues to make LLM API calls, consuming tokens and holding open connections until they naturally exhaust their own iteration budget.
Affected Component
Agent Core (conversation loop, context compression, memory)
Messaging Platform (if gateway-related)
No response
Debug Report
Report https://paste.rs/KV3eM
agent.log https://paste.rs/0ymtC
gateway.log https://paste.rs/MvObr
Operating System
Linux (Debian-based, kernel 5.15)
Python Version
3.11.2
Hermes Version
v0.13.0 (2026.5.7)
Additional Logs / Traceback (optional)
Root Cause Analysis (optional)
**Bug A** — `tools/delegate_tool.py` `_run_single_child()` around line 1609:
```python
# CURRENT (buggy):
if interrupted:
status = "interrupted"
elif summary: # ← only checks final_response emptiness
status = "completed"
else:
status = "failed" # ← even if completed=True!
The completed flag (from result.get("completed", False)) is read but never used in the status determination. It's only used later for exit_reason, creating contradictory results where exit_reason="completed" but status="failed".
Bug B — tools/delegate_tool.py batch execution path around line 2104:
# CURRENT (buggy):
if getattr(parent_agent, "_interrupt_requested", False) is True:
for f in pending:
# ...fabricates "interrupted" entry, never calls interrupt()
The _child_by_index map (line 2100) already provides access to each child agent, but it's not used in the interrupt path. The interrupt_subagent() function (line 183) and agent.interrupt() method both exist but aren't called.
Bug A — Add completed to the status check:
if interrupted:
status = "interrupted"
elif completed or summary: # ← respect the completed flag
status = "completed"
else:
status = "failed"
Bug B — Call child_agent.interrupt() on pending children before abandoning:
if getattr(parent_agent, "_interrupt_requested", False) is True: # Interrupt running children first for f in pending: if not f.done(): idx = futures[f] child_agent = _child_by_index.get(idx) if child_agent is not None: try: child_agent.interrupt("Batch parent interrupted") except Exception: pass # Then collect results as before for f in pending: ...
Both fixes pass existing tests (127/127 in `test_delegate.py`).
Proposed Fix (optional)
No response
Are you willing to submit a PR for this?
Bug Description
Two bugs in
tools/delegate_tool.pyreducedelegate_tasksubagent reliability:Bug A — Status misclassification (line ~1609-1617)
When a child agent completes normally (
completed=True) but returns an emptyfinal_response, the status is set to"failed"instead of"completed". This causes parents to treat successfully-completed children as failures.The current logic only checks
elif summary:(non-empty final_response) to determine "completed" status, ignoring thecompletedflag entirely.Bug B — Missing interrupt on batch parent interrupt (line ~2104-2139)
When a parent is interrupted during batch execution, pending child futures are abandoned with a fabricated
"interrupted"status — butchild_agent.interrupt()is never called. The child threads continue running, making API calls and consuming tokens as orphan processes.Steps to Reproduce
Expected Behavior
status: "completed"regardless of whetherfinal_responseis empty. Thecompletedflag fromrun_conversation()should be respected..interrupt()called on them before the parent abandons the batch, preventing orphan thread resource consumption.Actual Behavior
status = "failed"even whencompleted = Trueandfinal_response = ""Affected Component
Agent Core (conversation loop, context compression, memory)
Messaging Platform (if gateway-related)
No response
Debug Report
Operating System
Linux (Debian-based, kernel 5.15)
Python Version
3.11.2
Hermes Version
v0.13.0 (2026.5.7)
Additional Logs / Traceback (optional)
Root Cause Analysis (optional)
The
completedflag (fromresult.get("completed", False)) is read but never used in the status determination. It's only used later forexit_reason, creating contradictory results whereexit_reason="completed"butstatus="failed".Bug B —
tools/delegate_tool.pybatch execution path around line 2104:The
_child_by_indexmap (line 2100) already provides access to each child agent, but it's not used in the interrupt path. Theinterrupt_subagent()function (line 183) andagent.interrupt()method both exist but aren't called.Bug A — Add
completedto the status check:Bug B — Call
child_agent.interrupt()on pending children before abandoning:if getattr(parent_agent, "_interrupt_requested", False) is True: # Interrupt running children first for f in pending: if not f.done(): idx = futures[f] child_agent = _child_by_index.get(idx) if child_agent is not None: try: child_agent.interrupt("Batch parent interrupted") except Exception: pass # Then collect results as before for f in pending: ...
Proposed Fix (optional)
No response
Are you willing to submit a PR for this?