Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions agent/background_review.py
Original file line number Diff line number Diff line change
Expand Up @@ -1029,6 +1029,17 @@ def _run_review_fork(
_track_review_fork(agent, st.review_agent, register=True)
from hermes_cli.plugins import set_thread_tool_whitelist, clear_thread_tool_whitelist
review_whitelist, configured_extra_tools = _review_tool_whitelist(st.review_agent, task_cfg, review_memory)
# Dynamic memory-provider tools (e.g. Honcho's honcho_* set) are injected
# by the active MemoryManager at AIAgent init time, not by any static
# toolset definition. The review fork inherits the same provider
# configuration, so whitelist those tool names too without widening beyond
# the parent's live memory tool surface.
_memory_manager = getattr(agent, "_memory_manager", None)
if _memory_manager is not None:
try:
review_whitelist |= set(_memory_manager.get_all_tool_names())
except Exception:
pass
extra_list = ", ".join(sorted(configured_extra_tools))
deny_extra = f" Configured extra tools also allowed: {extra_list}." if configured_extra_tools else ""
prompt_extra = f" Exception — these configured tools are also allowed: {extra_list}." if configured_extra_tools else ""
Expand Down
39 changes: 39 additions & 0 deletions tests/run_agent/test_background_review_toolset_restriction.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,45 @@ def test_read_file_registers_background_review_read_mark(tmp_path):
reset_current_write_origin(token)


def test_background_review_whitelist_includes_dynamic_memory_provider_tools():
"""Dynamic memory-provider tools must be whitelisted alongside static toolsets."""
import run_agent
from hermes_cli import plugins as _plugins

captured = {}

def _capture_whitelist(whitelist, deny_msg_fmt=None):
captured["whitelist"] = set(whitelist)
captured["deny_msg_fmt"] = deny_msg_fmt
raise RuntimeError("stop after capturing whitelist")

agent = _make_agent_stub(run_agent.AIAgent)

class _MemoryManagerStub:
def get_all_tool_names(self):
return {"honcho_profile", "honcho_search", "honcho_context"}

agent._memory_manager = _MemoryManagerStub()

def _no_init(self, *args, **kwargs):
return None

with patch.object(run_agent.AIAgent, "__init__", _no_init), \
patch.object(_plugins, "set_thread_tool_whitelist", _capture_whitelist), \
patch("threading.Thread", _SyncThread):
agent._spawn_background_review(
messages_snapshot=[],
review_memory=True,
review_skills=False,
)

assert "whitelist" in captured, "set_thread_tool_whitelist was not called"
whitelist = captured["whitelist"]
assert "honcho_profile" in whitelist
assert "honcho_search" in whitelist
assert "honcho_context" in whitelist


def test_read_file_outside_review_does_not_mark(tmp_path):
"""Foreground reads must not populate the review-fork read set."""
from tools.file_tools import read_file_tool
Expand Down