# ADR-002: Observer Pattern Replaces Hooks Dict

**Status**: Accepted (hooks removed in v1.0)
**Date**: 2026-03-15
**Deciders**: Core maintainers

## Context

The original notification system was a `hooks` dict on `AgentConfig` — string-keyed callbacks with 11 events. In v0.15.0, `AgentObserver` was added as a class-based alternative with 25 events and richer data (run_id, call_id, system_prompt). Both systems coexisted, requiring every notification site to call both `_call_hook()` and `_notify_observers()`.

By v0.16.4, this dual system was:
- **Error-prone**: New events were sometimes added to observers but forgotten in hooks.
- **Noisy**: ~55 `_call_hook` calls alongside ~55 `_notify_observers` calls in core.py.
- **Type-unsafe**: Hook names were strings — `hooks["on_toll_start"]` (typo) would silently do nothing.

## Decision

1. Create `_HooksAdapter(AgentObserver)` that wraps a hooks dict as an observer, mapping the 11 hook events to observer methods.
2. In `Agent.__init__`, when `config.hooks` is set, emit `DeprecationWarning` and prepend a `_HooksAdapter` to the observers list.
3. Remove all `_call_hook()` calls — hooks are now served through the single observer pipeline.
4. Add `AsyncAgentObserver` for async-native integrations (blocking and non-blocking modes).

## Rationale

- **Single notification path**: One pipeline to maintain, one place to add new events.
- **Backward compatible**: The adapter makes existing hooks work transparently. Users see a deprecation warning but nothing breaks.
- **Type safety**: Observer methods are real Python methods — typos are caught by IDE autocompletion and mypy.
- **Richer data**: Observers receive `run_id` for correlation, `call_id` for parallel tool matching, `system_prompt` for debugging — data that hooks never had.

## Consequences

- **Positive**: core.py lost ~55 lines of `_call_hook` calls. The notification logic is consolidated.
- **Positive**: `AsyncAgentObserver` enables async DB writes without blocking the agent loop.
- **Negative**: Users with existing `hooks` usage see `DeprecationWarning`. Migration is straightforward: create an `AgentObserver` subclass and move hook logic to the corresponding `on_*` methods.
- **Decision**: No removal timeline set. Hooks remain functional via the adapter indefinitely. Removal will be evaluated for v0.18.0+.
