Skip to content

Hooks(内部事件钩子)

Hooks 是在 Agent 内部事件触发时自动运行的小型 TypeScript 函数,无需用户主动发消息。

Hooks 与 Webhook 的区别

  • Hooks(本页):Gateway 内部事件(命令、会话、消息)触发的函数
  • Webhook:外部 HTTP 端点,让第三方系统触发 OpenClaw(见 Webhook

内置 Hooks(开箱即用)

OpenClaw 附带 4 个常用 Hook:

Hook功能
session-memory 💾/new 命令时,将会话上下文保存到工作区记忆
bootstrap-extra-files 📎agent:bootstrap 时注入额外的工作区启动文件
command-logger 📝记录所有命令事件到 ~/.openclaw/logs/commands.log
boot-md 🚀Gateway 启动时运行 BOOT.md

管理 Hooks

bash
openclaw hooks list              # 列出所有可用 Hook
openclaw hooks enable session-memory  # 启用 Hook
openclaw hooks check             # 检查 Hook 状态
openclaw hooks info session-memory    # 查看 Hook 详情

Hook 发现机制

Hook 从三个目录自动加载(优先级从高到低):

  1. 工作区 Hooks<workspace>/hooks/(专属于当前 Agent)
  2. 本机共享 Hooks~/.openclaw/hooks/(跨 Agent 共享)
  3. 内置 Hooks<openclaw>/dist/hooks/bundled/

每个 Hook 是一个目录,包含:

my-hook/
├── HOOK.md          # 元数据 + 文档
└── handler.ts       # 处理函数实现

Hook Packs(批量安装)

Hook Pack 是标准 npm 包,通过 package.jsonopenclaw.hooks 字段声明包含哪些 Hook:

bash
openclaw hooks install <路径或包>

安全提示:安装时使用 npm install --ignore-scripts,不执行 lifecycle scripts。

编写自定义 Hook

HOOK.md 格式

markdown
---
name: my-hook
description: "发生 /new 时执行某些操作"
metadata:
  { "openclaw": { "emoji": "🔗", "events": ["command:new"] } }
---

# My Hook

描述这个 Hook 做什么……

元数据字段metadata.openclaw 下):

字段说明
emojiCLI 显示的 emoji
events监听的事件列表(如 ["command:new", "message:received"]
export使用哪个导出(默认 default
requires.bins必须在 PATH 上的可执行文件
requires.env必须存在的环境变量
requires.os仅在指定平台加载(darwin/linux/win32
always跳过资格检查(布尔值)

handler.ts 格式

typescript
const myHandler = async (event) => {
  // 过滤事件类型
  if (event.type !== "command" || event.action !== "new") {
    return;
  }

  console.log(`[my-hook] 触发:${event.sessionKey}`);
  console.log(`  时间:${event.timestamp.toISOString()}`);

  // 自定义逻辑……

  // 向用户发送消息(可选)
  event.messages.push("✨ Hook 已执行!");
};

export default myHandler;

事件上下文结构

typescript
{
  type: 'command' | 'session' | 'agent' | 'gateway' | 'message',
  action: string,       // 'new'、'reset'、'received'、'sent' 等
  sessionKey: string,
  timestamp: Date,
  messages: string[],   // 向 messages 数组 push 内容即可发送给用户
  context: {
    // 命令事件
    sessionEntry?: SessionEntry,
    workspaceDir?: string,
    bootstrapFiles?: WorkspaceBootstrapFile[],
    cfg?: OpenClawConfig,
    // 消息事件(见下方)
    from?: string,
    to?: string,
    content?: string,
    channelId?: string,
  }
}

事件类型参考

命令事件

事件触发时机
command所有命令事件(通用监听)
command:new用户发送 /new
command:reset用户发送 /reset
command:stop用户发送 /stop

会话事件

事件触发时机
session:compact:before上下文压缩前
session:compact:after上下文压缩完成后(含摘要元数据)

Agent 事件

事件触发时机
agent:bootstrap工作区启动文件注入前(可修改 context.bootstrapFiles

Gateway 事件

事件触发时机
gateway:startupGateway 启动完成后(渠道和 Hook 加载完成)

消息事件

事件触发时机
message所有消息事件(通用监听)
message:received收到入站消息(媒体尚未处理,可能含 <media:audio> 占位符)
message:transcribed音频转写完成(context.transcript 包含转写文字)
message:preprocessed所有媒体 + 链接理解完成后(Agent 看到消息前的最终版本)
message:sent出站消息发送成功

消息事件上下文(message:received)

typescript
{
  from: string,           // 发送者标识(手机号、用户 ID 等)
  content: string,        // 消息内容
  channelId: string,      // 渠道("whatsapp"、"telegram"、"discord" 等)
  accountId?: string,     // 多账号场景的 Provider 账号 ID
  conversationId?: string,
  messageId?: string,
  metadata?: {            // Provider 专有额外数据
    senderName?: string,
    senderUsername?: string,
    threadId?: string,
  }
}

消息日志示例

typescript
const handler = async (event) => {
  if (event.type === "message" && event.action === "received") {
    const { from, content, channelId } = event.context;
    console.log(`[message-logger] 收到消息 from=${from} channel=${channelId}`);
    console.log(`  内容: ${content.substring(0, 100)}`);
  }

  if (event.type === "message" && event.action === "sent") {
    const { to, success, channelId } = event.context;
    console.log(`[message-logger] 发送消息 to=${to} success=${success} channel=${channelId}`);
  }
};

export default handler;