🐒 Ming's Note

← Back to Concepts

Hermes Hook System — 完整參考文檔

Hermes 有三套 Hook 系統,對應唔同嘅自動化需求。呢份文檔整理晒所有可用嘅 Hook 事件、配置方法同實用場景。

三套系統總覽

系統 註冊方式 運行環境 主要用途
Gateway Event Hooks ~/.hermes/hooks/ 目錄,HOOK.yaml + handler.py Gateway only Logging, alerts, webhooks
Shell Hooks config.yaml 嘅 hooks: 區塊 CLI + Gateway Tool 拦截, auto-formatting, context injection
Webhook Subscriptions hermes webhook subscribe Gateway 外部服務觸發 AI 行跑

所有系統都係 non-blocking — hook 出錯會 catch 同 log,永遠唔會 crash agent。


1. Gateway Event Hooks

最適合做「開機問候」「摘要卡片」「長任務監控」等場景。

文件結構

~/.hermes/hooks/
└── my-hook/
    ├── HOOK.yaml      # 宣告聽邊啲 events
    └── handler.py     # Python handler

HOOK.yaml 格式

name: my-hook
description: 描述呢個 hook 做乜
events:
  - agent:start
  - agent:end

handler.py 格式

import json
from datetime import datetime
from pathlib import Path

LOG_FILE = Path.home() / ".hermes" / "hooks" / "my-hook" / "activity.log"

async def handle(event_type: str, context: dict):
    """必須叫 handle。支援 sync 或 async。"""
    entry = {
        "timestamp": datetime.now().isoformat(),
        "event": event_type,
        **context,
    }
    with open(LOG_FILE, "a") as f:
        f.write(json.dumps(entry) + "\n")

可用 Events

Event 幾時觸發 Context Keys
gateway:startup Gateway 進程啟動 platforms
session:start 新對話建立(第一條訊息) platform, user_id, session_id, session_key
session:end 對話結束(/new/reset 前) platform, user_id, session_key
session:reset Session reset 完成 platform, user_id, session_key
agent:start Agent 開始處理訊息 platform, user_id, session_id, message
agent:step 每次 tool-calling 迭代 platform, user_id, session_id, iteration, tool_names
agent:end Agent 完成處理 platform, user_id, session_id, message, response
command:* 任何 slash command(wildcard) platform, user_id, command, args

Wildcard 匹配: command:* 會 match 所有 command: event(command:model, command:reset 等)。

實用範例

長任務 Alert(超過10步推送到 Telegram)

# ~/.hermes/hooks/long-task-alert/HOOK.yaml
name: long-task-alert
description: Alert when agent is taking many steps
events:
  - agent:step
# ~/.hermes/hooks/long-task-alert/handler.py
import os, json, urllib.request

THRESHOLD = 10
TELEGRAM_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN")
CHAT_ID = os.getenv("TELEGRAM_CHAT_ID")

async def handle(event_type: str, context: dict):
    iteration = context.get("iteration", 0)
    if iteration == THRESHOLD:
        msg = f"⚠️ Agent 已經跑咗 {iteration} 步,可能係長任務。"
        url = f"https://api.telegram.org/bot{TELEGRAM_TOKEN}/sendMessage"
        data = json.dumps({"chat_id": CHAT_ID, "text": msg}).encode()
        req = urllib.request.Request(url, data=data, headers={"Content-Type": "application/json"})
        urllib.request.urlopen(req)

Activity Logger(記錄所有 agent 活動)

name: activity-logger
events:
  - agent:start
  - agent:end
  - agent:step
import json
from datetime import datetime
from pathlib import Path

LOG = Path.home() / ".hermes" / "hooks" / "activity-logger" / "activity.jsonl"

async def handle(event_type: str, context: dict):
    entry = {"ts": datetime.now().isoformat(), "event": event_type, **context}
    with open(LOG, "a") as f:
        f.write(json.dumps(entry, default=str) + "\n")

2. Shell Hooks

最適合做「權限彈窗」「文件整理」「攔截危險命令」等場景。

配置方式

config.yaml 加:

hooks:
  pre_tool_call:
    - command: /path/to/check-risk.py
      matcher: "terminal"    # regex,只 match terminal tool
      timeout: 30
  post_tool_call:
    - command: /path/to/notify.sh
  pre_llm_call:
    - command: /path/to/inject-context.py

Wire Protocol

Stdin(JSON):

{
  "hook_event_name": "pre_tool_call",
  "tool_name": "terminal",
  "tool_input": {"command": "rm -rf /"},
  "session_id": "sess_abc123",
  "cwd": "/home/user/project",
  "extra": {}
}

Stdout(JSON,可選):

// Block 一個 pre_tool_call
{"decision": "block", "reason": "Forbidden command"}
// 或
{"action": "block", "message": "Forbidden command"}

// 為 pre_llm_call 注入 context
{"context": "Today is Friday"}

// 無操作(空或唔 match 嘅 JSON)

可用 Events

Event 幾時觸發 Matcher 支援 可以 Block?
pre_tool_call 工具執行前 ✅ regex match tool name ✅ 可以 block
post_tool_call 工具執行後
transform_terminal_output 修改 terminal 輸出
transform_tool_result 修改工具結果
transform_llm_output 修改 AI 輸出文字
pre_llm_call LLM 呼叫前 ❌(注入 context)
post_llm_call LLM 呼叫後
pre_api_request API 請求前
post_api_request API 請求後
on_session_start 對話開始
on_session_end 對話結束
on_session_finalize 對話最終化
on_session_reset Session reset
subagent_stop Subagent 停止
pre_gateway_dispatch Gateway 訊息派發前 ✅ skip/rewrite
pre_approval_request 審批請求前 ❌(observer only)
post_approval_response 審批回應後 ❌(observer only)

Matcher 詳解

Matcher 係 regex,只適用於 pre_tool_call / post_tool_call

hooks:
  pre_tool_call:
    - command: /path/to/block-dangerous.sh
      matcher: "terminal"        # 只 match terminal tool
    - command: /path/to/check-write.sh
      matcher: "write_file|patch"  # match write_file 或 patch
    - command: /path/to/all-tools.py
      # 冇 matcher = match 所有 tools

實用範例

權限彈窗提醒(macOS)

#!/bin/bash
# ~/.hermes/scripts/notify-approval.sh
# stdin: JSON payload

INPUT=$(cat)
TOOL=$(echo "$INPUT" | python3 -c "import sys,json; print(json.load(sys.stdin).get('tool_name','unknown'))")

osascript -e "display notification \"需要你授權:$TOOL\" with title \"Hermes Hook\" sound name \"Glass\""
# config.yaml
hooks:
  pre_approval_request:
    - command: ~/.hermes/scripts/notify-approval.sh

危險命令攔截

#!/usr/bin/env python3
"""Block dangerous terminal commands."""
import sys, json

DANGEROUS = ["rm -rf", "git reset --hard", "git clean -fd", "> /dev/"]

payload = json.load(sys.stdin)
cmd = payload.get("tool_input", {}).get("command", "")

for pattern in DANGEROUS:
    if pattern in cmd:
        print(json.dumps({"decision": "block", "reason": f"Blocked: contains '{pattern}'"}))
        sys.exit(0)

# Allow
sys.exit(0)
hooks:
  pre_tool_call:
    - command: ~/.hermes/scripts/block-dangerous.py
      matcher: "terminal"
      timeout: 5

上下文注入(pre_llm_call)

#!/usr/bin/env python3
"""Inject current date into LLM context."""
import sys, json
from datetime import datetime

payload = json.load(sys.stdin)
context = f"Current datetime: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
print(json.dumps({"context": context}))
hooks:
  pre_llm_call:
    - command: ~/.hermes/scripts/inject-datetime.py

3. Webhook Subscriptions

最適合做「外部事件觸發 AI 行跑」—— GitHub、Stripe、CI/CD、IoT 等。

前置條件

Webhook platform 必須先 enable:

# 方法1:Setup wizard
hermes gateway setup

# 方法2:手動 config
# config.yaml
platforms:
  webhook:
    enabled: true
    extra:
      host: "0.0.0.0"
      port: 8644
      secret: "your-hmac-secret"

# 方法3:環境變數
# .env
WEBHOOK_ENABLED=true
WEBHOOK_PORT=8644
WEBHOOK_SECRET=your-secret

命令

# 建立 subscription
hermes webhook subscribe <name> \
  --prompt "Prompt template with {payload.fields}" \
  --events "event1,event2" \
  --description "What this does" \
  --skills "skill1,skill2" \
  --deliver telegram \
  --deliver-chat-id "12345" \
  --secret "optional-custom-secret"

# 列出所有 subscriptions
hermes webhook list

# 移除
hermes webhook remove <name>

# 測試
hermes webhook test <name>
hermes webhook test <name> --payload '{"key": "value"}'

Prompt Templates

{dot.notation} 存取 payload 內嘅欄位:

{issue.title}              → GitHub issue 標題
{pull_request.user.login}  → PR 作者
{data.object.amount}       → Stripe 付款金額
{sensor.temperature}       → IoT sensor 讀數

實用範例

GitHub New Issues → 自動 Triage

hermes webhook subscribe github-issues \
  --events "issues" \
  --prompt "New GitHub issue #{issue.number}: {issue.title}\n\nAction: {action}\nAuthor: {issue.user.login}\nBody:\n{issue.body}\n\nPlease triage this issue." \
  --deliver telegram \
  --deliver-chat-id "-100123456789"

Stripe 付款通知

hermes webhook subscribe stripe-payments \
  --events "payment_intent.succeeded,payment_intent.payment_failed" \
  --prompt "Payment {data.object.status}: {data.object.amount} cents from {data.object.receipt_email}" \
  --deliver telegram \
  --deliver-chat-id "-100123456789"

CI/CD Build 通知

hermes webhook subscribe ci-builds \
  --events "pipeline" \
  --prompt "Build {object_attributes.status} on {project.name} branch {object_attributes.ref}\nCommit: {commit.message}" \
  --deliver discord \
  --deliver-chat-id "1234567890"

Direct Delivery(零 LLM 成本)

hermes webhook subscribe antenna-matches \
  --deliver telegram \
  --deliver-chat-id "123456789" \
  --deliver-only \
  --prompt "🎉 New match: {match.user_name} matched with you!" \
  --description "Antenna match notifications"

4. 安全注意事項

Shell Hooks

Gateway Hooks

Webhook Subscriptions


5. 同 Loop Engineering 嘅對應

文章講嘅 Hook 玩法 Hermes 對應系統 實現方式
權限彈窗提醒 Shell Hook pre_approval_request + osascript/notify-send
開機日程播报 Gateway Hook session:start + 天氣 API + 日曆 API
摘要卡片 Gateway Hook agent:end + 寫入 markdown 文件
文件自動整理 Shell Hook + Skill post_tool_call + AI 識別 + 移動文件
久坐提醒 Cron Job 定時推送(唔係 Hook 但效果一樣)
長任務完成推送 Gateway Hook + Bark agent:end + Bark API

6. 同 Harness Enhancement 嘅關係

呢份文檔同 harness-enhancement project 直接相關:

Fence philosophy 嘅實踐:Hook = 確定性約束,唔靠 LLM 自律,靠代碼強制執行。


Related