Skip to content

feat(hooks): add centralized HookCenter for named hook points and plugin discovery#3541

Closed
aiguozhi123456 wants to merge 1 commit into
HKUDS:nightlyfrom
aiguozhi123456:feat/hook-center
Closed

feat(hooks): add centralized HookCenter for named hook points and plugin discovery#3541
aiguozhi123456 wants to merge 1 commit into
HKUDS:nightlyfrom
aiguozhi123456:feat/hook-center

Conversation

@aiguozhi123456
Copy link
Copy Markdown
Contributor

@aiguozhi123456 aiguozhi123456 commented Apr 29, 2026

Summary

Add a centralized HookCenter infrastructure that lets internal developers declare named hook points and external plugins attach handlers via Python entry_points. The system supports full interceptor semantics: handlers can observe, mutate data, short-circuit, or cancel the flow. Existing AgentHook/CompositeHook remain untouched.

Context

nanobot already uses entry_points for channel plugin discovery (nanobot.channels group) and has AgentHook/CompositeHook for agent-lifecycle callbacks. Neither mechanism provides a generic, named hook point system that external plugins can tap into. Contributors who want to inject behavior at arbitrary points must modify core code.

Why

  • No generic hook registry: Each feature area (channels, agent loop) rolls its own callback pattern. A shared registry eliminates duplication.
  • External plugins have no extension surface: Third-party packages cannot intercept or augment behavior without forking.
  • Inconsistent error handling: Some callback paths crash on handler errors; others silently swallow them. A centralized system standardizes error isolation.

Solve

Introduce nanobot/hooks/ — a self-contained package with four components:

  1. HookContext / HookResult — A mutable data bag passed between handlers, and a Literal-typed control-flow signal (continue / short_circuit / cancel).
  2. HookCenter — Global singleton registry with register_point(), register_handler(), and async emit(). Handlers run in registration order with per-handler error isolation (failing handler is logged and skipped).
  3. Plugin discovery — Scans the nanobot.hooks entry_points group. Callable plugins must declare a hook_points attribute; dict plugins map {point_name: handler}. Invalid plugins are logged and skipped.
  4. AgentLoop integration_init_hook_center() runs on startup, discovers external plugins, and logs registration count.

Changes

Component File Description
Types nanobot/hooks/context.py HookContext dataclass (mutable dict), HookResult with Literal["continue","short_circuit","cancel"] action
Types nanobot/hooks/types.py HookHandler protocol definition (`async (HookContext) -> HookResult
Core nanobot/hooks/center.py HookCenter class: register_point, register_handler, emit, reset; global get_center()/reset_center()
Discovery nanobot/hooks/discovery.py discover_hook_plugins() and register_discovered() via importlib.metadata.entry_points
Init nanobot/hooks/__init__.py Public re-exports: HookCenter, HookContext, HookResult, HookHandler
Integration nanobot/agent/loop.py _init_hook_center() called in AgentLoop.__init__
Docs docs/hook-center-guide.md Developer guide for internal and external plugin authors

Test

Suite File Count Coverage
Core tests/hooks/test_center.py 22 HookContext, HookResult, register/emit, error isolation, global singleton
Discovery tests/hooks/test_discovery.py 9 entry_points load, dict/callable plugins, missing hook_points rejection
Integration tests/hooks/test_integration.py 4 AgentLoop init, plugin discovery on startup, AgentHook backward compat, plugin failure resilience
Regression tests/agent/test_hook_composite.py 18 Existing AgentHook/CompositeHook unchanged

Total: 53 tests, all passing. No regressions in existing test suites.


Built with OpenCode
Compound Engineering
HARNESS

@aiguozhi123456 aiguozhi123456 force-pushed the feat/hook-center branch 3 times, most recently from 4f1f997 to 0e556dc Compare April 29, 2026 16:31
Add a new nanobot/hooks package providing:
- HookCenter with register_point/register_handler/emit for named hook points
- HookContext (mutable data bag) and HookResult (Literal-typed action)
- entry_points-based plugin discovery (nanobot.hooks group) with error isolation
- AgentLoop integration: 4 standard hook points with emit in _LoopHook lifecycle
- Full cancel semantics: before_iteration and before_execute_tools can cancel
  the agent run via AgentHookContext.cancel flag, checked by AgentRunner
- Handler dedup, warning on bad return types, empty hook_points handling
- 59 tests covering core, discovery, integration, cancel, and backward compat
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant