observal registry hook) and hooks wired into harness configs by observal agent pull / observal doctor patch.
Claude Code session hooks currently use HOOKS_SPEC_VERSION = "12" in observal_cli/harness_specs/claude_code_hooks_spec.py. Other harnesses have their own hook specs.
Where hooks live
Two distinct things share the name “hook”:- Registry hooks: packaged, versioned hook definitions in the Observal registry. Install them via
observal registry hook install. - harness hooks: entries in
~/.claude/settings.json,.kiro/agents/<name>.json, etc. These are written byobserval agent pullandobserval doctor patch.
Events
Source:
observal_cli/constants.py:VALID_HOOK_EVENTS.
Handler types
Execution modes
Source:
observal_cli/constants.py:VALID_HOOK_EXECUTION_MODES.
Scopes
Session delivery hooks
Telemetry is delivered by the session push hook, which reads the local harness transcript and sends session records to/api/v1/ingest/session. It uses the CLI configuration in ~/.observal/config.json; it does not require a curl wrapper, a telemetry environment variable, or an API key header.
For Claude Code, observal doctor patch --harness claude-code writes two Observal-managed command hooks into ~/.claude/settings.json: UserPromptSubmit and Stop. Each runs the installed Python interpreter with -m observal_cli.hooks.session_push --harness claude-code. Later patches recognize Observal’s own hooks by their command, so user-authored hooks are left alone.
For Kiro, the current IDE/CLI reads .kiro/hooks/observal.json with UserPromptSubmit and Stop triggers. Legacy CLI 2.x (without the IDE) uses inline userPromptSubmit and stop hooks on its agent profile. Both use -m observal_cli.hooks.session_push --harness kiro, not curl. See observal_cli/harness_specs/kiro_hooks_spec.py for the exact formats.
Other harnesses have their own capability-specific specs. Use observal doctor to inspect the current installation and observal doctor patch --all-harnesses to instrument detected harnesses.
Event name mapping (Claude Code ↔ Kiro)
Legacy Kiro CLI 2.x inline hooks use camelCase / lowercase event names; current standalone Kiro hooks use PascalCase triggers, like Claude Code. Observal maps the legacy names as follows:Registry hook payload shape
When submitting a hook to the registry (observal registry hook submit):
observal_cli/constants.py (mirrored from observal-server/schemas/constants.py).
Source of truth
observal_cli/harness_specs/: per-harness session hook specifications and versionsobserval_cli/constants.py: valid events, handler types, execution modes, scopesobserval-server/schemas/constants.py: server-side mirror
tests/test_constants_sync.py) ensures CLI and server stay in lockstep.

