Hooks
Hooks は、エージェントのコマンドやイベントに応じてアクションを自動化する拡張可能なイベント駆動型システムです。ディレクトリから自動検出され、OpenClaw のスキルと同様に CLI コマンドで管理できます。
はじめに
Hooks は何かが起きたときに実行される小さなスクリプトです。2 種類あります:
- Hooks(このページ):
/new、/reset、/stopなどのエージェントイベントやライフサイクルイベントが発火したとき、Gateway 内部で実行。 - Webhooks: 外部システムから OpenClaw にトリガーする HTTP Webhook。Webhook Hooks を参照するか、Gmail ヘルパーコマンドには
openclaw webhooksを使用。
Hooks はプラグイン内にバンドルすることもできます。Plugins を参照。
よくある使い方:
- セッションリセット時にメモリスナップショットを保存
- トラブルシューティングやコンプライアンスのためにコマンドの監査証跡を保持
- セッションの開始/終了時にフォローアップ自動化をトリガー
- イベント発火時にファイルをエージェントのワークスペースに書き込んだり、外部 API を呼び出す
TypeScript の関数が書ければ Hook を作成できます。Hooks は自動検出され、CLI で有効/無効を切り替えます。
概要
hooks システムでは以下が可能です:
/new発行時にセッションコンテキストをメモリに保存- すべてのコマンドを監査用にログ記録
- エージェントのライフサイクルイベントでカスタム自動化をトリガー
- コアコードを変更せずに OpenClaw の動作を拡張
使い始める
バンドル済み Hooks
OpenClaw には自動検出される 4 つのバンドル済み Hook が同梱されています:
- session-memory:
/new発行時にセッションコンテキストをエージェントのワークスペース(デフォルト~/.openclaw/workspace/memory/)に保存 - bootstrap-extra-files:
agent:bootstrap時に設定されたグロブ/パスパターンから追加のワークスペースブートストラップファイルを注入 - command-logger: すべてのコマンドイベントを
~/.openclaw/logs/commands.logにログ記録 - boot-md: Gateway 起動時に
BOOT.mdを実行(内部 hooks 有効時のみ)
利用可能な hooks を一覧:
openclaw hooks list
hook を有効化:
openclaw hooks enable session-memory
hook のステータス確認:
openclaw hooks check
詳細情報の取得:
openclaw hooks info session-memory
オンボーディング
オンボーディング(openclaw onboard)時に、推奨される hooks の有効化が提案されます。ウィザードが対象の hooks を自動検出し、選択肢として提示します。
Hook の検出
Hooks は 3 つのディレクトリから自動検出されます(優先順位順):
- ワークスペースの hooks:
<workspace>/hooks/(エージェントごと、最優先) - マネージド hooks:
~/.openclaw/hooks/(ユーザーがインストール、ワークスペース間で共有) - バンドル済み hooks:
<openclaw>/dist/hooks/bundled/(OpenClaw に同梱)
マネージドの hook ディレクトリは単一の hook または hook パック(パッケージディレクトリ)のいずれかです。
各 hook は以下を含むディレクトリです:
my-hook/
├── HOOK.md # メタデータ + ドキュメント
└── handler.ts # ハンドラー実装
Hook パック(npm / アーカイブ)
Hook パックは package.json の openclaw.hooks で 1 つ以上の hooks をエクスポートする標準 npm パッケージです。以下でインストールします:
openclaw hooks install <path-or-spec>
npm スペックはレジストリのみ(パッケージ名 + オプションの正確なバージョンまたは dist-tag)です。 Git/URL/ファイルスペックや semver 範囲は拒否されます。
ベアスペックと @latest は安定トラックを使用します。npm がプレリリースに解決した場合、OpenClaw は停止し、@beta/@rc のようなプレリリースタグまたは正確なプレリリースバージョンでの明示的なオプトインを求めます。
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 ディレクトリを指します。
Hook パックは依存関係を含むことができ、~/.openclaw/hooks/<id> にインストールされます。
openclaw.hooks の各エントリはシンボリックリンク解決後もパッケージディレクトリ内に留まる必要があります。逸脱したエントリは拒否されます。
セキュリティに関する注意: openclaw hooks install は npm install --ignore-scripts(ライフサイクルスクリプトなし)で依存関係をインストールします。Hook パックの依存関係ツリーは「純粋な JS/TS」に保ち、postinstall ビルドに依存するパッケージは避けてください。
Hook の構造
HOOK.md のフォーマット
HOOK.md ファイルには YAML フロントマターの中にメタデータが、その後に Markdown のドキュメントが含まれます:
---
name: my-hook
description: "この hook が何をするかの簡潔な説明"
homepage: https://docs.openclaw.ai/automation/hooks#my-hook
metadata:
{ "openclaw": { "emoji": "🔗", "events": ["command:new"], "requires": { "bins": ["node"] } } }
---
# My Hook
詳細なドキュメントはここに...
## 機能
- `/new` コマンドを監視
- アクションを実行
- 結果をログ記録
## 要件
- Node.js がインストール済みであること
## 設定
設定不要。
メタデータフィールド
metadata.openclaw オブジェクトがサポートするフィールド:
emoji: CLI の表示用絵文字(例:"💾")events: リッスンするイベントの配列(例:["command:new", "command:reset"])export: 使用する名前付きエクスポート(デフォルト"default")homepage: ドキュメント URLrequires: オプションの要件bins: PATH 上に必要なバイナリ(例:["git", "node"])anyBins: これらのバイナリのうち少なくとも 1 つが存在する必要があるenv: 必要な環境変数config: 必要な設定パス(例:["workspace.dir"])os: 必要なプラットフォーム(例:["darwin", "linux"])
always: 適格性チェックをバイパス(ブーリアン)install: インストール方法(バンドル済み hooks の場合:[{"id":"bundled","kind":"bundled"}])
ハンドラーの実装
handler.ts ファイルは HookHandler 関数をエクスポートします:
const myHandler = async (event) => {
// 'new' コマンド時のみトリガー
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()}`);
// カスタムロジックをここに
// オプションでユーザーにメッセージ送信
event.messages.push("✨ My hook executed!");
};
export default myHandler;
イベントコンテキスト
各イベントに含まれる内容:
{
type: 'command' | 'session' | 'agent' | 'gateway' | 'message',
action: string, // 例: 'new', 'reset', 'stop', 'received', 'sent'
sessionKey: string, // セッション識別子
timestamp: Date, // イベント発生時刻
messages: string[], // ユーザーにメッセージ送信するにはここに push
context: {
// コマンドイベント:
sessionEntry?: SessionEntry,
sessionId?: string,
sessionFile?: string,
commandSource?: string, // 例: 'whatsapp', 'telegram'
senderId?: string,
workspaceDir?: string,
bootstrapFiles?: WorkspaceBootstrapFile[],
cfg?: OpenClawConfig,
// メッセージイベント(詳細はメッセージイベントセクションを参照):
from?: string, // message:received
to?: string, // message:sent
content?: string,
channelId?: string,
success?: boolean, // message:sent
}
}
イベントタイプ
コマンドイベント
エージェントコマンドの発行時にトリガーされます:
command: すべてのコマンドイベント(汎用リスナー)command:new:/newコマンド発行時command:reset:/resetコマンド発行時command:stop:/stopコマンド発行時
セッションイベント
session:compact:before: コンパクションが履歴を要約する直前session:compact:after: コンパクション完了後(サマリーメタデータ付き)
内部 hook ペイロードはこれらを type: "session" の action: "compact:before" / action: "compact:after" として発行します。リスナーは上記の結合キーでサブスクライブします。
ハンドラー登録はリテラルキー形式 ${type}:${action} を使用します。これらのイベントでは session:compact:before と session:compact:after で登録してください。
エージェントイベント
agent:bootstrap: ワークスペースブートストラップファイルの注入前(hooks がcontext.bootstrapFilesを変更可能)
Gateway イベント
Gateway 起動時にトリガー:
gateway:startup: チャネルの起動と hooks の読み込み後
メッセージイベント
メッセージの受信/送信時にトリガー:
message: すべてのメッセージイベント(汎用リスナー)message:received: いずれかのチャネルからインバウンドメッセージを受信したとき。メディア理解前の処理初期段階で発火します。コンテンツにはまだ処理されていないメディア添付ファイル用の<media:audio>のような生のプレースホルダーが含まれる場合があります。message:transcribed: 音声の文字起こしやリンク理解を含む完全な処理が完了したとき。この時点でtranscriptには音声メッセージの完全な文字起こしテキストが含まれます。文字起こし済み音声コンテンツへのアクセスが必要な場合はこの hook を使用してください。message:preprocessed: すべてのメディア + リンク理解が完了した後のすべてのメッセージに対して発火し、エージェントが参照する前の完全にエンリッチされた本文(文字起こし、画像説明、リンクサマリー)への hooks アクセスを提供します。message:sent: アウトバウンドメッセージの送信成功時
メッセージイベントのコンテキスト
メッセージイベントにはメッセージに関する詳細なコンテキストが含まれます:
// message:received のコンテキスト
{
from: string, // 送信者識別子(電話番号、ユーザー ID など)
content: string, // メッセージ内容
timestamp?: number, // 受信時の Unix タイムスタンプ
channelId: string, // チャネル(例: "whatsapp", "telegram", "discord")
accountId?: string, // マルチアカウント構成のプロバイダーアカウント ID
conversationId?: string, // チャット/会話 ID
messageId?: string, // プロバイダーのメッセージ ID
metadata?: { // プロバイダー固有の追加データ
to?: string,
provider?: string,
surface?: string,
threadId?: string,
senderId?: string,
senderName?: string,
senderUsername?: string,
senderE164?: string,
}
}
// message:sent のコンテキスト
{
to: string, // 受信者識別子
content: string, // 送信されたメッセージ内容
success: boolean, // 送信が成功したか
error?: string, // 送信失敗時のエラーメッセージ
channelId: string, // チャネル(例: "whatsapp", "telegram", "discord")
accountId?: string, // プロバイダーアカウント ID
conversationId?: string, // チャット/会話 ID
messageId?: string, // プロバイダーが返したメッセージ ID
isGroup?: boolean, // アウトバウンドメッセージがグループ/チャネルコンテキストに属するか
groupId?: string, // message:received との相関用のグループ/チャネル識別子
}
// message:transcribed のコンテキスト
{
body?: string, // エンリッチメント前の生のインバウンド本文
bodyForAgent?: string, // エージェントに表示されるエンリッチ済み本文
transcript: string, // 音声文字起こしテキスト
channelId: string, // チャネル(例: "telegram", "whatsapp")
conversationId?: string,
messageId?: string,
}
// message:preprocessed のコンテキスト
{
body?: string, // 生のインバウンド本文
bodyForAgent?: string, // メディア/リンク理解後の最終エンリッチ済み本文
transcript?: string, // 音声が存在した場合の文字起こし
channelId: string, // チャネル(例: "telegram", "whatsapp")
conversationId?: string,
messageId?: string,
isGroup?: boolean,
groupId?: string,
}
例: メッセージロガー Hook
const isMessageReceivedEvent = (event: { type: string; action: string }) =>
event.type === "message" && event.action === "received";
const isMessageSentEvent = (event: { type: string; action: string }) =>
event.type === "message" && event.action === "sent";
const handler = async (event) => {
if (isMessageReceivedEvent(event as { type: string; action: string })) {
console.log(`[message-logger] Received from ${event.context.from}: ${event.context.content}`);
} else if (isMessageSentEvent(event as { type: string; action: string })) {
console.log(`[message-logger] Sent to ${event.context.to}: ${event.context.content}`);
}
};
export default handler;
ツール結果 Hooks(プラグイン API)
これらの hooks はイベントストリームリスナーではありません。プラグインが OpenClaw がツール結果を永続化する前に同期的に調整できるようにするものです。
tool_result_persist: セッショントランスクリプトに書き込まれる前にツール結果を変換します。同期的でなければなりません。更新されたツール結果ペイロードを返すか、undefinedを返してそのまま維持します。Agent Loop を参照。
プラグイン Hook イベント
プラグイン hook ランナーを通じて公開されるコンパクションライフサイクル hooks:
before_compaction: カウント/トークンメタデータ付きでコンパクション前に実行after_compaction: コンパクションサマリーメタデータ付きでコンパクション後に実行
将来のイベント
計画中のイベントタイプ:
session:start: 新しいセッション開始時session:end: セッション終了時agent:error: エージェントがエラーに遭遇した時
カスタム 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: "便利な処理を行う"
metadata: { "openclaw": { "emoji": "🎯", "events": ["command:new"] } }
---
# My Custom Hook
`/new` 発行時に便利な処理を実行します。
4. handler.ts を作成
const handler = async (event) => {
if (event.type !== "command" || event.action !== "new") {
return;
}
console.log("[my-hook] Running!");
// ロジックをここに
};
export default handler;
5. 有効化とテスト
# hook が検出されたことを確認
openclaw hooks list
# 有効化
openclaw hooks enable my-hook
# Gateway プロセスを再起動(macOS のメニューバーアプリの再起動、または開発プロセスの再起動)
# イベントをトリガー
# メッセージングチャネルから /new を送信
設定
新しい設定フォーマット(推奨)
{
"hooks": {
"internal": {
"enabled": true,
"entries": {
"session-memory": { "enabled": true },
"command-logger": { "enabled": false }
}
}
}
}
Hook ごとの設定
Hooks にはカスタム設定を持たせることができます:
{
"hooks": {
"internal": {
"enabled": true,
"entries": {
"my-hook": {
"enabled": true,
"env": {
"MY_CUSTOM_VAR": "value"
}
}
}
}
}
}
追加ディレクトリ
追加ディレクトリから hooks を読み込む:
{
"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"
}
]
}
}
}
注意: module はワークスペース相対パスでなければなりません。絶対パスやワークスペース外へのトラバーサルは拒否されます。
移行: 新しい hooks にはディスカバリーベースのシステムを使用してください。レガシーハンドラーはディレクトリベースの hooks の後に読み込まれます。
CLI コマンド
Hook の一覧
# すべての hooks を一覧
openclaw hooks list
# 適格な hooks のみ表示
openclaw hooks list --eligible
# 詳細出力(不足している要件を表示)
openclaw hooks list --verbose
# JSON 出力
openclaw hooks list --json
Hook の情報
# hook の詳細情報を表示
openclaw hooks info session-memory
# JSON 出力
openclaw hooks info session-memory --json
適格性チェック
# 適格性サマリーを表示
openclaw hooks check
# JSON 出力
openclaw hooks check --json
有効化/無効化
# hook を有効化
openclaw hooks enable session-memory
# hook を無効化
openclaw hooks disable command-logger
バンドル済み Hook リファレンス
session-memory
/new 発行時にセッションコンテキストをメモリに保存します。
イベント: command:new
要件: workspace.dir が設定されていること
出力: <workspace>/memory/YYYY-MM-DD-slug.md(デフォルト ~/.openclaw/workspace)
処理内容:
- リセット前のセッションエントリを使用して正しいトランスクリプトを特定
- 会話の最後 15 行を抽出
- LLM を使用して説明的なファイル名スラグを生成
- セッションメタデータを日付付きメモリファイルに保存
出力例:
# 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(スラグ生成失敗時のフォールバックタイムスタンプ)
有効化:
openclaw hooks enable session-memory
bootstrap-extra-files
agent:bootstrap 時に追加のブートストラップファイル(モノレポ内の AGENTS.md / TOOLS.md など)を注入します。
イベント: agent:bootstrap
要件: workspace.dir が設定されていること
出力: ファイルの書き込みなし。ブートストラップコンテキストはメモリ内でのみ変更。
設定:
{
"hooks": {
"internal": {
"enabled": true,
"entries": {
"bootstrap-extra-files": {
"enabled": true,
"paths": ["packages/*/AGENTS.md", "packages/*/TOOLS.md"]
}
}
}
}
}
補足:
- パスはワークスペース相対で解決。
- ファイルはワークスペース内に留まる必要があります(realpath チェック)。
- 認識されるブートストラップ用のベースネームのみが読み込まれます。
- サブエージェントの許可リストは維持されます(
AGENTS.mdとTOOLS.mdのみ)。
有効化:
openclaw hooks enable bootstrap-extra-files
command-logger
すべてのコマンドイベントを集中監査ファイルに記録します。
イベント: command
要件: なし
出力: ~/.openclaw/logs/commands.log
処理内容:
- イベントの詳細(コマンドアクション、タイムスタンプ、セッションキー、送信者 ID、ソース)をキャプチャ
- JSONL 形式でログファイルに追記
- バックグラウンドで静かに動作
ログエントリの例:
{"timestamp":"2026-01-16T14:30:00.000Z","action":"new","sessionKey":"agent:main:main","senderId":"+1234567890","source":"telegram"}
{"timestamp":"2026-01-16T15:45:22.000Z","action":"stop","sessionKey":"agent:main:main","senderId":"[email protected]","source":"whatsapp"}
ログの閲覧:
# 最近のコマンドを表示
tail -n 20 ~/.openclaw/logs/commands.log
# jq で整形表示
cat ~/.openclaw/logs/commands.log | jq .
# アクションでフィルター
grep '"action":"new"' ~/.openclaw/logs/commands.log | jq .
有効化:
openclaw hooks enable command-logger
boot-md
Gateway 起動時(チャネル起動後)に BOOT.md を実行します。
内部 hooks が有効でなければ実行されません。
イベント: gateway:startup
要件: workspace.dir が設定されていること
処理内容:
- ワークスペースから
BOOT.mdを読み取り - エージェントランナーで指示を実行
- メッセージツール経由でリクエストされたアウトバウンドメッセージを送信
有効化:
openclaw hooks enable boot-md
ベストプラクティス
ハンドラーは高速に
Hooks はコマンド処理中に実行されます。軽量に保ちましょう:
// ○ 良い例 - 非同期処理で即座にリターン
const handler: HookHandler = async (event) => {
void processInBackground(event); // Fire and forget
};
// × 悪い例 - コマンド処理をブロック
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));
// throw しない - 他のハンドラーの実行を妨げない
}
};
イベントは早期フィルター
関係ないイベントは早期にリターンしてください:
const handler: HookHandler = async (event) => {
// 'new' コマンドのみ処理
if (event.type !== "command" || event.action !== "new") {
return;
}
// ロジックをここに
};
具体的なイベントキーを使用
可能な限りメタデータで正確なイベントを指定してください:
metadata: { "openclaw": { "events": ["command:new"] } } # 具体的
以下よりも:
metadata: { "openclaw": { "events": ["command"] } } # 汎用 - オーバーヘッドが大きい
デバッグ
Hook ログの有効化
Gateway は起動時に hook の読み込みをログ出力します:
Registered hook: session-memory -> command:new
Registered hook: bootstrap-extra-files -> agent:bootstrap
Registered hook: command-logger -> command
Registered hook: boot-md -> gateway:startup
検出の確認
検出されたすべての hooks を一覧:
openclaw hooks list --verbose
登録の確認
ハンドラー内で呼び出し時にログ出力:
const handler: HookHandler = async (event) => {
console.log("[my-handler] Triggered:", event.type, event.action);
// ロジック
};
適格性の検証
hook が適格でない理由を確認:
openclaw hooks info my-hook
出力で不足している要件を探してください。
テスト
Gateway ログ
Gateway ログを監視して hook の実行を確認:
# macOS
./scripts/clawlog.sh -f
# その他のプラットフォーム
tail -f ~/.openclaw/gateway.log
Hook の直接テスト
ハンドラーを単体でテスト:
import { test } from "vitest";
import myHandler from "./hooks/my-hook/handler.js";
test("my handler works", async () => {
const event = {
type: "command",
action: "new",
sessionKey: "test-session",
timestamp: new Date(),
messages: [],
context: { foo: "bar" },
};
await myHandler(event);
// 副作用をアサート
});
アーキテクチャ
コアコンポーネント
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: Gateway 起動時に hooks を読み込みsrc/auto-reply/reply/commands-core.ts: コマンドイベントのトリガー
検出フロー
Gateway 起動
↓
ディレクトリをスキャン(ワークスペース → マネージド → バンドル済み)
↓
HOOK.md ファイルをパース
↓
適格性をチェック(bins、env、config、os)
↓
適格な hooks からハンドラーを読み込み
↓
イベントにハンドラーを登録
イベントフロー
ユーザーが /new を送信
↓
コマンドバリデーション
↓
hook イベントを作成
↓
hook をトリガー(登録済みハンドラー全て)
↓
コマンド処理を続行
↓
セッションリセット
トラブルシューティング
Hook が検出されない
-
ディレクトリ構造を確認:
ls -la ~/.openclaw/hooks/my-hook/ # HOOK.md, handler.ts が表示されるはず -
HOOK.md のフォーマットを検証:
cat ~/.openclaw/hooks/my-hook/HOOK.md # name とメタデータを含む YAML フロントマターがあるはず -
検出されたすべての hooks を一覧:
openclaw hooks list
Hook が適格でない
要件を確認:
openclaw hooks info my-hook
以下が不足していないか確認:
- バイナリ(PATH を確認)
- 環境変数
- 設定値
- OS の互換性
Hook が実行されない
-
hook が有効か確認:
openclaw hooks list # 有効な hooks の横に ✓ が表示されるはず -
Gateway プロセスを再起動して hooks をリロード。
-
Gateway ログでエラーを確認:
./scripts/clawlog.sh | grep hook
ハンドラーのエラー
TypeScript / インポートエラーを確認:
# インポートを直接テスト
node -e "import('./path/to/handler.ts').then(console.log)"
移行ガイド
レガシー設定からディスカバリーへ
変更前:
{
"hooks": {
"internal": {
"enabled": true,
"handlers": [
{
"event": "command:new",
"module": "./hooks/handlers/my-handler.ts"
}
]
}
}
}
変更後:
-
hook ディレクトリを作成:
mkdir -p ~/.openclaw/hooks/my-hook mv ./hooks/handlers/my-handler.ts ~/.openclaw/hooks/my-hook/handler.ts -
HOOK.md を作成:
--- name: my-hook description: "My custom hook" metadata: { "openclaw": { "emoji": "🎯", "events": ["command:new"] } } --- # My Hook Does something useful. -
設定を更新:
{ "hooks": { "internal": { "enabled": true, "entries": { "my-hook": { "enabled": true } } } } } -
検証して Gateway プロセスを再起動:
openclaw hooks list # 🎯 my-hook ✓ と表示されるはず
移行の利点:
- 自動検出
- CLI による管理
- 適格性チェック
- ドキュメントの充実
- 統一された構造