feat(hooks): outbound webhooks — push signed lifecycle events to external HTTP endpoints

The inverse of the inbound webhook platform: hooks.outbound in
config.yaml lists HTTP targets + the plugin-hook events they subscribe
to (on_session_end, subagent_stop, post_tool_call, ...). Each firing
POSTs a JSON payload (same top-level shape as shell hooks' stdin wire)
signed GitHub-style with HMAC-SHA256 (X-Hermes-Signature-256).

Rides the existing hook bus — notify-only callbacks registered on the
plugin manager at the same CLI/gateway/main entry points as shell
hooks. Delivery is fire-and-forget via a bounded queue + single daemon
worker thread, so a dead endpoint can never stall a tool call. Bounded
retries (5xx/conn errors once; 4xx never). secret_env preferred over
inline secret. HERMES_SAFE_MODE skips registration. hermes hooks list
shows outbound targets with signed/UNSIGNED status.

Zero new model tools, zero new subsystems.
This commit is contained in:
Teknium
2026-07-22 07:26:29 -07:00
parent 43c79cd84a
commit 3829e34e23
9 changed files with 1129 additions and 43 deletions
+8 -1
View File
@@ -1047,7 +1047,14 @@ def _prepare_deferred_agent_startup() -> None:
from agent.shell_hooks import register_from_config
from hermes_cli.config import load_config
register_from_config(load_config(), accept_hooks=_accept_hooks)
_hooks_cfg = load_config()
register_from_config(_hooks_cfg, accept_hooks=_accept_hooks)
from agent.outbound_webhooks import (
register_from_config as register_outbound_webhooks,
)
register_outbound_webhooks(_hooks_cfg)
except Exception:
logger.debug(
"shell-hook registration failed at deferred CLI startup",