diff --git a/nanobot/agent/hook.py b/nanobot/agent/hook.py index 33db416ed..ad6b31ab1 100644 --- a/nanobot/agent/hook.py +++ b/nanobot/agent/hook.py @@ -65,6 +65,7 @@ class CompositeHook(AgentHook): __slots__ = ("_hooks",) def __init__(self, hooks: list[AgentHook]) -> None: + super().__init__() self._hooks = list(hooks) def wants_streaming(self) -> bool: @@ -72,7 +73,7 @@ class CompositeHook(AgentHook): async def _for_each_hook_safe(self, method_name: str, *args: Any, **kwargs: Any) -> None: for h in self._hooks: - if h._reraise: + if getattr(h, "_reraise", False): await getattr(h, method_name)(*args, **kwargs) continue diff --git a/nanobot/agent/subagent.py b/nanobot/agent/subagent.py index 585139972..63aa7ad7a 100644 --- a/nanobot/agent/subagent.py +++ b/nanobot/agent/subagent.py @@ -27,6 +27,7 @@ class _SubagentHook(AgentHook): """Logging-only hook for subagent execution.""" def __init__(self, task_id: str) -> None: + super().__init__() self._task_id = task_id async def before_execute_tools(self, context: AgentHookContext) -> None: diff --git a/tests/agent/test_hook_composite.py b/tests/agent/test_hook_composite.py index 590d8db64..c6077d526 100644 --- a/tests/agent/test_hook_composite.py +++ b/tests/agent/test_hook_composite.py @@ -232,6 +232,35 @@ async def test_composite_empty_hooks_no_ops(): assert hook.finalize_content(ctx, "test") == "test" +@pytest.mark.asyncio +async def test_composite_supports_legacy_hook_init_without_super(): + calls: list[str] = [] + + class LegacyHook(AgentHook): + def __init__(self, label: str) -> None: + self.label = label + + async def before_iteration(self, context: AgentHookContext) -> None: + calls.append(self.label) + + hook = CompositeHook([LegacyHook("legacy")]) + await hook.before_iteration(_ctx()) + assert calls == ["legacy"] + + +@pytest.mark.asyncio +async def test_composite_can_wrap_another_composite(): + calls: list[str] = [] + + class Inner(AgentHook): + async def before_iteration(self, context: AgentHookContext) -> None: + calls.append("inner") + + hook = CompositeHook([CompositeHook([Inner()])]) + await hook.before_iteration(_ctx()) + assert calls == ["inner"] + + # --------------------------------------------------------------------------- # Integration: AgentLoop with extra hooks # ---------------------------------------------------------------------------