mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-01 00:11:39 +08:00
Compare commits
1 Commits
fix/plugin
...
hermes/her
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f781de0e88 |
@@ -364,7 +364,7 @@ Rendering bugs in tmux/iTerm2 — ghosting on scroll. Use `curses` (stdlib) inst
|
|||||||
Leaks as literal `?[K` text under `prompt_toolkit`'s `patch_stdout`. Use space-padding: `f"\r{line}{' ' * pad}"`.
|
Leaks as literal `?[K` text under `prompt_toolkit`'s `patch_stdout`. Use space-padding: `f"\r{line}{' ' * pad}"`.
|
||||||
|
|
||||||
### `_last_resolved_tool_names` is a process-global in `model_tools.py`
|
### `_last_resolved_tool_names` is a process-global in `model_tools.py`
|
||||||
When subagents overwrite this global, `execute_code` calls after delegation may fail with missing tool imports. Known bug.
|
`_run_single_child()` in `delegate_tool.py` saves and restores this global around subagent execution. If you add new code that reads this global, be aware it may be temporarily stale during child agent runs.
|
||||||
|
|
||||||
### Tests must not write to `~/.hermes/`
|
### Tests must not write to `~/.hermes/`
|
||||||
The `_isolate_hermes_home` autouse fixture in `tests/conftest.py` redirects `HERMES_HOME` to a temp dir. Never hardcode `~/.hermes/` paths in tests.
|
The `_isolate_hermes_home` autouse fixture in `tests/conftest.py` redirects `HERMES_HOME` to a temp dir. Never hardcode `~/.hermes/` paths in tests.
|
||||||
|
|||||||
@@ -247,6 +247,49 @@ class TestDelegateTask(unittest.TestCase):
|
|||||||
self.assertEqual(kwargs["api_mode"], parent.api_mode)
|
self.assertEqual(kwargs["api_mode"], parent.api_mode)
|
||||||
|
|
||||||
|
|
||||||
|
class TestToolNamePreservation(unittest.TestCase):
|
||||||
|
"""Verify _last_resolved_tool_names is restored after subagent runs."""
|
||||||
|
|
||||||
|
def test_global_tool_names_restored_after_delegation(self):
|
||||||
|
"""The process-global _last_resolved_tool_names must be restored
|
||||||
|
after a subagent completes so the parent's execute_code sandbox
|
||||||
|
generates correct imports."""
|
||||||
|
import model_tools
|
||||||
|
|
||||||
|
parent = _make_mock_parent(depth=0)
|
||||||
|
original_tools = ["terminal", "read_file", "web_search", "execute_code", "delegate_task"]
|
||||||
|
model_tools._last_resolved_tool_names = list(original_tools)
|
||||||
|
|
||||||
|
with patch("run_agent.AIAgent") as MockAgent:
|
||||||
|
mock_child = MagicMock()
|
||||||
|
mock_child.run_conversation.return_value = {
|
||||||
|
"final_response": "done", "completed": True, "api_calls": 1,
|
||||||
|
}
|
||||||
|
MockAgent.return_value = mock_child
|
||||||
|
|
||||||
|
delegate_task(goal="Test tool preservation", parent_agent=parent)
|
||||||
|
|
||||||
|
self.assertEqual(model_tools._last_resolved_tool_names, original_tools)
|
||||||
|
|
||||||
|
def test_global_tool_names_restored_after_child_failure(self):
|
||||||
|
"""Even when the child agent raises, the global must be restored."""
|
||||||
|
import model_tools
|
||||||
|
|
||||||
|
parent = _make_mock_parent(depth=0)
|
||||||
|
original_tools = ["terminal", "read_file", "web_search"]
|
||||||
|
model_tools._last_resolved_tool_names = list(original_tools)
|
||||||
|
|
||||||
|
with patch("run_agent.AIAgent") as MockAgent:
|
||||||
|
mock_child = MagicMock()
|
||||||
|
mock_child.run_conversation.side_effect = RuntimeError("boom")
|
||||||
|
MockAgent.return_value = mock_child
|
||||||
|
|
||||||
|
result = json.loads(delegate_task(goal="Crash test", parent_agent=parent))
|
||||||
|
self.assertEqual(result["results"][0]["status"], "error")
|
||||||
|
|
||||||
|
self.assertEqual(model_tools._last_resolved_tool_names, original_tools)
|
||||||
|
|
||||||
|
|
||||||
class TestDelegateObservability(unittest.TestCase):
|
class TestDelegateObservability(unittest.TestCase):
|
||||||
"""Tests for enriched metadata returned by _run_single_child."""
|
"""Tests for enriched metadata returned by _run_single_child."""
|
||||||
|
|
||||||
|
|||||||
@@ -175,6 +175,11 @@ def _run_single_child(
|
|||||||
model on OpenRouter while the parent runs on Nous Portal).
|
model on OpenRouter while the parent runs on Nous Portal).
|
||||||
"""
|
"""
|
||||||
from run_agent import AIAgent
|
from run_agent import AIAgent
|
||||||
|
import model_tools
|
||||||
|
|
||||||
|
# Save the parent's resolved tool names before the child agent can
|
||||||
|
# overwrite the process-global via get_tool_definitions().
|
||||||
|
_saved_tool_names = list(model_tools._last_resolved_tool_names)
|
||||||
|
|
||||||
child_start = time.monotonic()
|
child_start = time.monotonic()
|
||||||
|
|
||||||
@@ -352,6 +357,10 @@ def _run_single_child(
|
|||||||
}
|
}
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
|
# Restore the parent's tool names so the process-global is correct
|
||||||
|
# for any subsequent execute_code calls or other consumers.
|
||||||
|
model_tools._last_resolved_tool_names = _saved_tool_names
|
||||||
|
|
||||||
# Unregister child from interrupt propagation
|
# Unregister child from interrupt propagation
|
||||||
if hasattr(parent_agent, '_active_children'):
|
if hasattr(parent_agent, '_active_children'):
|
||||||
try:
|
try:
|
||||||
|
|||||||
Reference in New Issue
Block a user