Hooks 是可扩展的事件驱动系统:当代理命令或生命周期事件发生时,在 Gateway 内部运行小型脚本来自动化处理。Hooks 会从目录中自动发现,并可通过 CLI 管理(类似技能)。
快速定位
Hooks 是“发生某件事就运行的脚本”,分两类:
- Hooks(本页):在 Gateway 内部运行,响应
/new、/reset、/stop等命令或生命周期事件。 - Webhooks:外部 HTTP 触发 OpenClaw 的工作流,详见 Webhooks,Gmail 辅助命令在
openclaw webhooks命名空间下。
Hooks 也可以打包在插件中(见 插件系统)。常见用途:
- 会话重置时保存记忆快照
- 记录命令审计日志
- 会话开始/结束时触发自动化
- 事件触发时写入工作区文件或调用外部 API
能写一个小的 TypeScript 函数,就能写一个 Hook。
概述
- 在执行
/new时保存会话上下文 - 记录所有命令用于审计
- 在代理生命周期事件上触发自定义自动化
- 无需改动核心代码即可扩展 OpenClaw 行为
快速开始
内置 Hooks
OpenClaw 内置 4 个自动发现的 hooks:
session-memory:执行/new时保存会话上下文(默认路径~/.openclaw/workspace/memory/)command-logger:记录所有命令事件到~/.openclaw/logs/commands.logboot-md:Gateway 启动后执行BOOT.md(需启用 internal hooks)soul-evil:在清除窗口或随机机会将注入的SOUL.md替换为SOUL_EVIL.md
列出可用 hooks:
openclaw hooks list
启用一个 hook:
openclaw hooks enable session-memory
检查 hook 状态:
openclaw hooks check
查看详细信息:
openclaw hooks info session-memory
引导流程
在 openclaw onboard 过程中,向导会自动发现可用 hooks 并提示你启用推荐项。
Hook 发现
Hooks 会从以下目录自动发现(优先级从高到低):
- 工作区 hooks:
<workspace>/hooks/(每代理,优先级最高) - 托管 hooks:
~/.openclaw/hooks/(用户安装,跨工作区共享) - 内置 hooks:
<openclaw>/dist/hooks/bundled/(随 OpenClaw 发布)
每个 hook 是一个目录,包含:
my-hook/
├── HOOK.md # 元数据 + 文档
└── handler.ts # 处理器实现
Hook 包(npm/archives)
Hook pack 是标准 npm 包,在 package.json 里通过 openclaw.hooks 导出:
openclaw hooks install <path-or-spec>
示例 package.json:
{
"name": "@acme/my-hooks",
"version": "0.1.0",
"openclaw": {
"hooks": ["./hooks/my-hook", "./hooks/other-hook"]
}
}
每个条目指向包含 HOOK.md 与 handler.ts(或 index.ts)的目录。Hook pack 的依赖会被安装在 ~/.openclaw/hooks/<id> 下。
Hook 结构
HOOK.md 格式
HOOK.md 由 YAML frontmatter + Markdown 文档组成:
---
name: my-hook
description: "Short description of what this hook does"
homepage: https://docs.openclaw.ai/hooks#my-hook
metadata:
{ "openclaw": { "emoji": "", "events": ["command:new"], "requires": { "bins": ["node"] } } }
---
# My Hook
Detailed documentation goes here...
## What It Does
- Listens for `/new` commands
- Performs some action
- Logs the result
## Requirements
- Node.js must be installed
## Configuration
No configuration needed.
元数据字段
metadata.openclaw 支持:
emoji:CLI 显示用表情(如"")events:监听事件数组(如["command:new", "command:reset"])export:命名导出(默认"default")homepage:文档 URLrequires:可选依赖bins:PATH 中必须存在的二进制anyBins:至少存在其中一个二进制env:需要的环境变量config:需要的配置路径(如["workspace.dir"])os:需要的平台(如["darwin", "linux"])
always:跳过 eligibility 校验install:安装方式(内置 hooks 示例:[{"id":"bundled","kind":"bundled"}])
处理器实现
handler.ts 需导出 HookHandler:
import type { HookHandler } from "../../src/hooks/hooks.js";
const myHandler: HookHandler = async (event) => {
// Only trigger on 'new' command
if (event.type !== "command" || event.action !== "new") {
return;
}
console.log(`[my-hook] New command triggered`);
console.log(` Session: ${event.sessionKey}`);
console.log(` Timestamp: ${event.timestamp.toISOString()}`);
// Your custom logic here
// Optionally send message to user
event.messages.push("✨ My hook executed!");
};
export default myHandler;
事件上下文
每个事件包含:
{
type: "command" | "session" | "agent" | "gateway",
action: string, // e.g., "new", "reset", "stop"
sessionKey: string, // Session identifier
timestamp: Date, // When the event occurred
messages: string[], // Push messages here to send to user
context: {
sessionEntry?: SessionEntry,
sessionId?: string,
sessionFile?: string,
commandSource?: string, // e.g., "whatsapp", "telegram"
senderId?: string,
workspaceDir?: string,
bootstrapFiles?: WorkspaceBootstrapFile[],
cfg?: OpenClawConfig
}
}
事件类型
命令事件
command:所有命令事件command:new:/newcommand:reset:/resetcommand:stop:/stop
代理事件
agent:bootstrap:注入工作区 bootstrap 文件前触发(可修改context.bootstrapFiles)
网关事件
gateway:startup:网关启动(通道已启动且 hooks 已加载后)
工具结果钩子(插件 API)
这些 hook 不是事件流监听器,而是允许插件在工具结果写入会话记录前进行同步修改:
tool_result_persist:同步转换工具结果;返回更新后的 payload,或返回undefined保持原样。
未来事件
session:start/session:endagent:errormessage:sent/message:received
创建自定义 Hook
1) 选择位置
- 工作区 hooks:
<workspace>/hooks/ - 托管 hooks:
~/.openclaw/hooks/
2) 创建目录结构
mkdir -p ~/.openclaw/hooks/my-hook
cd ~/.openclaw/hooks/my-hook
3) 创建 HOOK.md
---
name: my-hook
description: "Does something useful"
metadata: { "openclaw": { "emoji": "", "events": ["command:new"] } }
---
# My Custom Hook
This hook does something useful when you issue `/new`.
4) 创建 handler.ts
import type { HookHandler } from "../../src/hooks/hooks.js";
const handler: HookHandler = async (event) => {
if (event.type !== "command" || event.action !== "new") {
return;
}
console.log("[my-hook] Running!");
// Your logic here
};
export default handler;
5) 启用并测试
# Verify hook is discovered
openclaw hooks list
# Enable it
openclaw hooks enable my-hook
# Restart your gateway process (menu bar app restart on macOS, or restart your dev process)
# Trigger the event
# Send /new via your messaging channel
配置
新配置格式(推荐)
{
"hooks": {
"internal": {
"enabled": true,
"entries": {
"session-memory": { "enabled": true },
"command-logger": { "enabled": false }
}
}
}
}
单 Hook 配置
{
"hooks": {
"internal": {
"enabled": true,
"entries": {
"my-hook": {
"enabled": true,
"env": {
"MY_CUSTOM_VAR": "value"
}
}
}
}
}
}
额外目录
{
"hooks": {
"internal": {
"enabled": true,
"load": {
"extraDirs": ["/path/to/more/hooks"]
}
}
}
}
旧配置格式(兼容)
{
"hooks": {
"internal": {
"enabled": true,
"handlers": [
{
"event": "command:new",
"module": "./hooks/handlers/my-handler.ts",
"export": "default"
}
]
}
}
}
迁移建议:新 hook 使用“发现式”目录结构;旧 handlers 会在目录型 hooks 之后加载。
CLI 命令
列出 Hooks
# List all hooks
openclaw hooks list
# Show only eligible hooks
openclaw hooks list --eligible
# Verbose output (show missing requirements)
openclaw hooks list --verbose
# JSON output
openclaw hooks list --json
Hook 信息
# Show detailed info about a hook
openclaw hooks info session-memory
# JSON output
openclaw hooks info session-memory --json
检查可用性
# Show eligibility summary
openclaw hooks check
# JSON output
openclaw hooks check --json
启用 / 禁用
# Enable a hook
openclaw hooks enable session-memory
# Disable a hook
openclaw hooks disable command-logger
内置 Hooks 详解
session-memory
执行 /new 时保存会话上下文。事件:command:new。需求:配置 workspace.dir。输出:<workspace>/memory/YYYY-MM-DD-slug.md(默认 ~/.openclaw/workspace)。
行为:
- 使用重置前的 session entry 定位正确的 transcript
- 提取最近 15 行对话
- 调用 LLM 生成描述性 slug
- 保存到带日期的记忆文件
文件示例:
# Session: 2026-01-16 14:30:00 UTC
- **Session Key**: agent:main:main
- **Session ID**: abc123def456
- **Source**: telegram
文件名示例:
2026-01-16-vendor-pitch.md2026-01-16-api-design.md2026-01-16-1430.md(slug 失败时的回退)
启用:
openclaw hooks enable session-memory
command-logger
记录所有命令事件到 ~/.openclaw/logs/commands.log。事件:command。
过滤示例:
# Filter by action
grep '"action":"new"' ~/.openclaw/logs/commands.log | jq .
启用:
openclaw hooks enable command-logger
soul-evil
在清理窗口或随机机会用 SOUL_EVIL.md 替换注入的 SOUL.md(内存中替换,不写文件)。事件:agent:bootstrap。
启用:
openclaw hooks enable soul-evil
配置:
{
"hooks": {
"internal": {
"enabled": true,
"entries": {
"soul-evil": {
"enabled": true,
"file": "SOUL_EVIL.md",
"chance": 0.1,
"purge": { "at": "21:00", "duration": "15m" }
}
}
}
}
}
boot-md
Gateway 启动后执行 BOOT.md(通道启动后)。事件:gateway:startup。需求:workspace.dir 已配置。
- 读取工作区中的
BOOT.md - 通过代理运行指令
- 通过消息工具发送请求的外发消息
启用:
openclaw hooks enable boot-md
最佳实践
保持处理器轻量
// ✓ Good - async work, returns immediately
const handler: HookHandler = async (event) => {
void processInBackground(event); // Fire and forget
};
// ✗ Bad - blocks command processing
const handler: HookHandler = async (event) => {
await slowDatabaseQuery(event);
await evenSlowerAPICall(event);
};
优雅处理错误
const handler: HookHandler = async (event) => {
try {
await riskyOperation(event);
} catch (err) {
console.error("[my-handler] Failed:", err instanceof Error ? err.message : String(err));
// Don't throw - let other handlers run
}
};
尽早过滤事件
const handler: HookHandler = async (event) => {
// Only handle 'new' commands
if (event.type !== "command" || event.action !== "new") {
return;
}
// Your logic here
};
使用更具体的事件键
metadata: { "openclaw": { "events": ["command:new"] } } # Specific
# Rather than:
metadata: { "openclaw": { "events": ["command"] } } # General - more overhead
调试
启用 Hook 日志
Gateway 在启动时会记录已注册 hooks:
Registered hook: session-memory -> command:new
Registered hook: command-logger -> command
Registered hook: boot-md -> gateway:startup
检查发现
openclaw hooks list --verbose
检查注册
const handler: HookHandler = async (event) => {
console.log("[my-handler] Triggered:", event.type, event.action);
// Your logic
};
检查可用性
openclaw hooks info my-hook
查看输出中缺失的依赖项。
测试
网关日志
# macOS
./scripts/clawlog.sh -f
# Other platforms
tail -f ~/.openclaw/gateway.log
直接测试 Hook
import { test } from "vitest";
import { createHookEvent } from "./src/hooks/hooks.js";
import myHandler from "./hooks/my-hook/handler.js";
test("my handler works", async () => {
const event = createHookEvent("command", "new", "test-session", {
foo: "bar",
});
await myHandler(event);
// Assert side effects
});
架构
核心组件
src/hooks/types.ts:类型定义src/hooks/workspace.ts:目录扫描与加载src/hooks/frontmatter.ts:HOOK.md 元数据解析src/hooks/config.ts:可用性校验src/hooks/hooks-status.ts:状态报告src/hooks/loader.ts:动态模块加载src/cli/hooks-cli.ts:CLI 命令src/gateway/server-startup.ts:网关启动时加载 hookssrc/auto-reply/reply/commands-core.ts:触发命令事件
发现流程
Gateway startup
↓
Scan directories (workspace → managed → bundled)
↓
Parse HOOK.md files
↓
Check eligibility (bins, env, config, os)
↓
Load handlers from eligible hooks
↓
Register handlers for events
事件流程
User sends /new
↓
Command validation
↓
Create hook event
↓
Trigger hook (all registered handlers)
↓
Command processing continues
↓
Session reset
故障排除
Hook 未被发现
ls -la ~/.openclaw/hooks/my-hook/
# Should show: HOOK.md, handler.ts
cat ~/.openclaw/hooks/my-hook/HOOK.md
# Should have YAML frontmatter with name and metadata
openclaw hooks list
Hook 不可用
openclaw hooks info my-hook
检查缺失:
- 二进制(PATH)
- 环境变量
- 配置项
- OS 兼容性
Hook 不执行
- 确认已启用:
openclaw hooks list(启用的 hook 应有 ✓) - 重启网关以重新加载 hooks
- 检查日志:
./scripts/clawlog.sh | grep hook
处理器错误
# Test import directly
node -e "import('./path/to/handler.ts').then(console.log)"
迁移指南
从旧配置迁移到发现式
旧配置:
{
"hooks": {
"internal": {
"enabled": true,
"handlers": [
{
"event": "command:new",
"module": "./hooks/handlers/my-handler.ts"
}
]
}
}
}
迁移后:
# 1) Create hook directory
mkdir -p ~/.openclaw/hooks/my-hook
mv ./hooks/handlers/my-handler.ts ~/.openclaw/hooks/my-hook/handler.ts
# 2) Create HOOK.md
---
name: my-hook
description: "My custom hook"
metadata: { "openclaw": { "emoji": "", "events": ["command:new"] } }
---
# My Hook
Does something useful.
# 3) Update config
{
"hooks": {
"internal": {
"enabled": true,
"entries": {
"my-hook": { "enabled": true }
}
}
}
}
# 4) Verify and restart your gateway process
openclaw hooks list
# Should show: my-hook ✓