Files
Oleg Maslov 0c3e2ead3b
Some checks failed
Installer Smoke / installer-smoke (push) Has been cancelled
moving
2026-09-02 10:10:29 +02:00

85 lines
4.8 KiB
Markdown

# @waggle/hive-mind-hooks-hermes
Silent-capture shim that wires **Hermes** (`NousResearch/hermes-agent`)
lifecycle hooks into [hive-mind](https://github.com/marolinik/hive-mind)
frames. Every Hermes session deterministically captures session-start /
prompt / response events into your personal memory via `hive-mind-cli` — the
same every-turn pattern proven by `@waggle/hive-mind-hooks-claude-code`, built
on the shared `@waggle/hive-mind-hooks-core` foundation.
Hermes's hook config is a **YAML installer** with **create-if-missing**
semantics. It targets the **SHELL-HOOKS** system — the top-level `hooks:`
block in `~/.hermes/config.yaml`. It does **not** touch Hermes's two other
"hooks" systems (the directory-based gateway hooks under `~/.hermes/hooks/`,
or the in-process plugin hooks) — only the shell-hooks block.
> **Not hook parity with claude-code.** Hermes ships **only 3 lifecycle
> hooks** — there is **no PreCompact event** — and splits SessionStart across
> two events. See the Capture fidelity table below.
## Install
```bash
npx @waggle/hive-mind-hooks-hermes install
# Windows / production: pin the CLI path
npx @waggle/hive-mind-hooks-hermes install --cli-path "C:\\path\\to\\hive-mind-cli\\dist\\index.js"
```
The installer additively merges hive-mind shell-hook entries into
`~/.hermes/config.yaml` (creating the file if absent), preserving any existing
hooks you have, and writes a pointer + **literal byte-identical backup** so
uninstall is exact. (YAML re-serialization loses comments and key ordering, so
uninstall restores the original bytes rather than a re-serialized diff.)
> **Headless / gateway consent (IMPORTANT).** Hermes keeps a first-use consent
> allow-list keyed on the exact hook command string. Under a **headless /
> non-TTY launch** the hooks register **only** if `hooks_auto_accept: true` is
> in the config (the installer seeds this by default) **or**
> `HERMES_ACCEPT_HOOKS=1` is set — otherwise they silently never register.
> Pass `--no-auto-accept` to manage consent yourself.
```bash
npx @waggle/hive-mind-hooks-hermes verify # smoke-check
npx @waggle/hive-mind-hooks-hermes uninstall # byte-identical restore (or remove if we created it)
```
## Capture fidelity
| Lifecycle | Hermes event(s) | Status | Notes |
|---|---|---|---|
| SessionStart (recall + inject) | `on_session_start` (observer) **+** `pre_llm_call` (`is_first_turn`) | full (split) | observer side is a no-op; inject side fires on the first turn only and appends `{ "context": "..." }` to the user message (not the system prompt — preserves the prefix cache) |
| UserPromptSubmit (save temporary) | `pre_llm_call` | full | the user text arrives at `extra.user_message`; saved every turn |
| Stop (summarize + save) | `post_llm_call` | full | the assistant text arrives at `extra.assistant_response`; `on_session_finalize` is gateway-only and is **not** used |
| PreCompact (compact memory) | **— (none)** | **opt-in approximation** | Hermes ships **no compaction hook** (`VALID_HOOKS` has no compact entry) — there is genuinely nothing to bind. Set `WAGGLE_HERMES_COMPACT_ON_STOP=1` (default off) to approximate it from the per-turn Stop hook, time-gated by `WAGGLE_HERMES_COMPACT_WINDOW_MIN` (minutes, default 10) so it runs at most once per window |
**Disclosures:**
- **No PreCompact event at all.** Hermes has no compaction hook, so the
`cleanup_frames` maintenance step that claude-code runs on PreCompact does
**not** run under Hermes by default. Memory still accrues correctly; only the
periodic compaction nudge is absent. **Opt-in:** set
`WAGGLE_HERMES_COMPACT_ON_STOP=1` to approximate it from the per-turn Stop
hook, time-gated by `WAGGLE_HERMES_COMPACT_WINDOW_MIN` (minutes, default 10)
so the maintenance pass fires at most once per window.
- **SessionStart is split** across `on_session_start` (observer) and
`pre_llm_call` (inject). One compiled script is registered under both; it
gates injection on `is_first_turn` so it injects once per session, not every
turn.
- **Block/inject is via stdout JSON**, not exit codes — Hermes has no
exit-code-2 contract. Our hooks are capture-only: they emit `{}` (or the
SessionStart `{ "context": ... }`) and exit 0.
- **Headless consent** — without `hooks_auto_accept: true` (seeded by default)
or `HERMES_ACCEPT_HOOKS=1`, the hooks silently never register under a
headless launch.
## How it works
Each hook is a short-lived Node subprocess. Hermes spawns the configured
`command` (`shlex.split` + `shell=False`), pipes the event JSON to the hook's
stdin, and reads optional JSON back from stdout. The hook shells to
`hive-mind-cli` to recall or save frames, then exits 0. **Fail-open:** if
`hive-mind-cli` is unreachable or the payload is malformed, the hook logs to
stderr and exits 0 — Hermes is never blocked.
License: Apache-2.0.