Hooks
概要
Codex の agentic loop に自前スクリプトを差し込む拡張フレームワーク。ログ収集、プロンプトの検査、ターン終了時の検証、ディレクトリごとのプロンプト調整などに使う。既定で有効。
仕様
実行時の挙動
- 複数のファイルから一致した hook はすべて実行される
- 同一イベントに一致する複数の command hook は並行して起動される。ある hook が別の hook の起動を止めることはできない
- managed でない command hook は、実行前にレビューと信頼付与が必要
イベントの発火タイミング
| タイミング | イベント |
|---|---|
| ターン中 | PreToolUse、PermissionRequest、PostToolUse、PreCompact、PostCompact、UserPromptSubmit、SubagentStop、Stop |
| セッション/subagent の開始時 | SessionStart、SubagentStart |
| メインスレッドの終了時 | SessionEnd(subagent では実行されない) |
定義場所
有効な config レイヤーの隣にある次のいずれかの形式で発見される。
hooks.jsonconfig.toml内のインライン[hooks]テーブル
実際に使う主な場所:
-
~/.codex/hooks.json -
~/.codex/config.toml -
<repo>/.codex/hooks.json -
<repo>/.codex/config.toml -
複数の hook ソースがある場合、一致するものをすべて読み込む。優先度の高い config レイヤーが低いレイヤーの hook を置き換えることはない
-
同一レイヤーに
hooks.jsonとインライン[hooks]の両方があると両者をマージし、起動時に警告する。1 レイヤーにつき 1 表現を推奨 -
project-local の hook は、プロジェクトの
.codex/レイヤーが trusted のときだけ読み込まれる。untrusted なプロジェクトでも user / system の hook はそれぞれの有効な config レイヤーから読み込まれる -
有効な plugin がバンドルする hook も、他のソースと並んで読み込まれる
レビューと信頼
- managed でない command hook は、その定義そのものをレビューして信頼するまで実行されない
- 信頼は hook の現在のハッシュに対して記録される。新規または変更された hook は review 対象としてマークされ、信頼するまでスキップされる
/hooksで hook ソースの確認、レビュー、信頼付与、managed でない hook の個別無効化を行う。起動時にレビュー待ちがあると/hooksを開くよう警告が出る- system / MDM / cloud /
requirements.toml由来の managed hook は managed かつポリシーで信頼済みとして扱われ、ユーザーの hook ブラウザからは無効化できない --dangerously-bypass-hook-trustを渡すと、その呼び出しに限り永続的な信頼なしに有効な hook を実行する
設定の構造
3 段のネストで定義する。
- hook event(
PreToolUse、PostToolUse、PreCompact、SubagentStart、Stopなど) - そのイベントの発火条件を決める matcher group
- 一致したときに動く hook handler
| フィールド | 説明 |
|---|---|
description | hooks.json ファイルのトップレベルの任意メタデータ。どの hook が動くかには影響しない |
timeout | 秒。省略時はほとんどの hook で 600 秒。SessionEnd は既定 1 秒で、上限は 3 秒 |
statusMessage | 任意。実行中に表示するメッセージ |
additionalContextLimit | command hook が additionalContext としてモデルへ送れる量のしきい値 |
commandWindows | Windows 専用のコマンド上書き(任意)。TOML では command_windows または commandWindows |
async | パースはされるが、非同期 command hook は未対応 |
- 現時点で動く handler は
type: "command"のみ。promptとagentはパースされるがスキップされる - コマンドはセッションの
cwdを作業ディレクトリとして実行される - repo-local の hook では、
.codex/hooks/...のような相対パスではなく git root から解決することが推奨されている(サブディレクトリから起動されうるため)
無効化
config.toml で次のように設定する。
[features]
hooks = false
- 正式なフィーチャーキーは
hooks。codex_hooksは deprecated なエイリアスとして今も動く - 管理者は
requirements.tomlの[features].hooks = falseで同様に強制的に無効化できる
requirements.toml による managed hook
- エンタープライズの requirements は
[hooks]配下にインラインで hook を定義できる - ローカルで hook を無効化したユーザーに対しても managed hook を強制するには、
requirements.tomlで[features].hooks = trueを[hooks]と併せて固定する allow_managed_hooks_only = trueで user / project / session / plugin の hook を無視し、管理者の managed hook だけを読み込むmanaged_dirは macOS と Linux、windows_managed_dirは Windows で使われるmanaged_dir配下のスクリプト自体は Codex が配布しない。導入と更新は各社のツールで行う- managed hook のコマンドは、設定した managed ディレクトリ配下の絶対パスを使うべきとされている
plugin がバンドルする hook
- 既定では plugin root 内の
hooks/hooks.jsonを探す。.codex-plugin/plugin.jsonのhooksエントリでこの既定を上書きできる - マニフェストの
hooksは./始まりのパス、そのパスの配列、インラインの hooks オブジェクト、またはその配列を受け付ける - マニフェストのパスは plugin root からの相対で解決され、root の外に出てはならない。マニフェストが
hooksを定義すると、既定のhooks/hooks.jsonの代わりにそれが使われる - plugin hook のコマンドが受け取る環境変数
PLUGIN_ROOT(Codex 独自の拡張): インストール済み plugin rootPLUGIN_DATA(Codex 独自の拡張): plugin の書き込み可能データディレクトリ- 既存の plugin hook との互換のため
CLAUDE_PLUGIN_ROOTとCLAUDE_PLUGIN_DATAも設定される
- plugin をインストール・有効化しても、その hook が自動的に信頼されることはない
matcher
matcher は正規表現の文字列。"*"、""、または省略で、そのイベントのすべてに一致する。
| イベント | matcher が絞るもの | 備考 |
|---|---|---|
PermissionRequest | ツール名 | Bash、apply_patch、MCP ツール名に対応 |
PostToolUse | ツール名 | ツール対応範囲は下記 |
PostCompact | compaction のトリガー | manual / auto |
PreCompact | compaction のトリガー | manual / auto |
PreToolUse | ツール名 | ツール対応範囲は下記 |
SessionEnd | 終了理由 | 現時点では other のみ |
SessionStart | 開始のソース | startup / resume / clear / compact |
SubagentStart | subagent の種別 | 起動する subagent による |
SubagentStop | subagent の種別 | 停止する subagent による |
UserPromptSubmit | 非対応 | 設定しても無視される |
Stop | 非対応 | 設定しても無視される |
apply_patchに対してはEditやWriteも matcher 値として使える
ツールの対応範囲
| ツール経路 | PreToolUse | PostToolUse | 備考 |
|---|---|---|---|
| shell コマンド | 対応 | 対応 | Bash として一致させる |
unified exec(exec_command) | 対応 | 対応 | Bash として一致。後続の write_stdin のポーリングが、元コマンド完了時の PostToolUse を届けることがある |
apply_patch | 対応 | 対応 | apply_patch / Edit / Write |
| MCP ツール | 対応 | 対応 | mcp__filesystem__read_file のようなツール名 |
| その他のローカル function tool | 対応 | 対応 | update_plan などの関数名。spawn_agent は Agent にも一致する |
WebSearch などの hosted tool | 非対応 | 非対応 | ローカル function-tool の hook 経路を通らない |
write_stdinは既存の unified-exec セッションへの転送であり、既にPreToolUseを通ったコマンドに入力を送る/ポーリングするときはPreToolUseを再発火しない- 一部の特殊なツール経路は既定の hook 経路をオプトアウトできる。ツール hook は有用なガードレールであって完全な強制境界ではないと明記されている
共通の入力フィールド
command hook は JSON オブジェクト 1 個を stdin で受け取る。
| フィールド | 型 | 意味 |
|---|---|---|
session_id | string | 現在のセッション ID。subagent の hook は親セッションの ID |
transcript_path | string | null | セッションのトランスクリプトファイルのパス |
cwd | string | セッションの作業ディレクトリ |
hook_event_name | string | 発火したイベント名 |
model | string | Codex 独自の拡張。有効なモデル slug |
- ターンにスコープされる hook は
turn_id(Codex 独自の拡張)をイベント固有のフィールドとして持つ SessionStart、PreToolUse、PermissionRequest、PostToolUse、UserPromptSubmit、SubagentStart、SubagentStop、Stopはpermission_modeも含む(default/acceptEdits/plan/dontAsk/bypassPermissions)transcript_pathは利便性のためのもので、トランスクリプト形式は hook にとって安定インターフェースではなく変わりうる
共通の出力フィールド
SessionStart、PreCompact、PostCompact、UserPromptSubmit、SubagentStop、Stop が対応する。SubagentStart は systemMessage と hook 固有コンテキストについて同じ形を受け付けるが、continue: false で subagent を止めることはできない。
| フィールド | 効果 |
|---|---|
continue | false でその hook run を stopped として記録する |
stopReason | 停止理由として記録される |
systemMessage | UI またはイベントストリームに警告として表示される |
suppressOutput | パースはされるが未実装 |
- 出力なしの exit
0は成功として扱われ、Codex は続行する PreToolUseとPermissionRequestはsystemMessageに対応するが、continue/stopReason/suppressOutputは現時点では非対応。PreToolUsehook がこれらを返すと、その hook run を失敗としてマークしエラーを報告したうえでツール呼び出しを続行するPostToolUseはsystemMessage、continue: false、stopReasonに対応する。suppressOutputはパースされるが非対応
大きな hook 出力(spilling)
- 既定では、モデルから見える hook 出力メッセージ 1 件を約 2,500 トークンに制限する
- 超えた場合は全文を
<temp_dir>/hook_outputs/<session_id>/<uuid>.txtに保存し、モデルには先頭と末尾のプレビューと保存先パスを渡す。ファイルを書けない場合もモデルには切り詰めたプレビューが届く additionalContextLimitを handler に設定するとしきい値を変えられる。省略時は既定の2500トークン。正の整数で任意のしきい値、0でハンドラの additional context をそのままモデルへ渡す- Codex は一致した handler ごとに独立して評価する。additional context を出せないイベントでは
additionalContextLimitを無視し設定警告を報告する - このしきい値は
additionalContextにのみ適用される。ツールのフィードバックと継続プロンプトは既定の制限のまま - 出力がディスクに書かれうるため、hook 出力に秘密情報を含めないこと
イベント別の入出力
SessionStart
matcherはsourceに適用される。sourceはstartup/resume/clear/compactstdoutのプレーンテキストは追加の developer context として渡される- JSON では共通出力フィールドに加え
hookSpecificOutput.additionalContextが使える - root セッションの compaction 後、
source: "compact"に一致するSessionStarthook は次のモデルリクエストの前に走る。ターンの途中の自動 compaction でも同様で、追加コンテキストは後のユーザーターンを待たずその継続に届く。continue: falseを返すと、次のモデルリクエストを送らずにターンを終える
SessionEnd
- メインスレッドについて、開いている会話をアーカイブ/削除したとき、Codex が正常終了したとき、会話がアイドルで 30 分どのクライアントからも開かれていないときに実行される。subagent では実行されない
- 会話を切り替えたり
thread/unsubscribeを呼んだりしてもすぐにはセッションが終わらないため、即座には実行されない matcherはreasonを絞る。現時点ではreasonは常にother- advisory(助言的)であり、出力が Codex を誘導したりスレッドを開いたままにしたりはしない。タイムアウトやエラー終了は hook failure として報告される
SubagentStart
matcherはagent_typeに適用される- 追加フィールド:
turn_id、agent_id、agent_type、permission_mode stdoutのプレーンテキストは subagent 向けの追加 developer context になるcontinue: falseは互換のためパースされるが、subagent の起動を止めない
PreToolUse
- 追加フィールド:
turn_id、tool_name、tool_use_id、tool_input matcherはtool_nameとそのエイリアスに適用される。apply_patch経由のファイル編集ではapply_patch/Edit/Writeが使えるが、hook 入力のtool_nameはapply_patchのままstdoutのプレーンテキストは無視される- 拒否は
hookSpecificOutput.permissionDecision: "deny"とpermissionDecisionReason。旧来の{"decision": "block", "reason": ...}も受け付ける。exit code2とstderrへの理由出力でも拒否できる - ブロックせずコンテキストを足すには
hookSpecificOutput.additionalContext - 書き換えは
permissionDecision: "allow"とupdatedInputを併せて返す。Bashとapply_patchではupdatedInputに文字列のcommandが必要。MCP など他のローカル function tool では置き換え後の引数オブジェクト。updatedInputは"allow"とのみ併用し、それ以外の形はエラーとして報告される permissionDecision: "ask"、旧来のdecision: "approve"、continue: false、stopReason、suppressOutputはパースされるが未対応。hook run を失敗としてマークし、エラーを報告してツール呼び出しを続行する
PermissionRequest
- Codex が承認を求めようとするとき(shell の escalation や managed-network の承認など)に走る。承認不要のコマンドでは走らない
- 許可・拒否・判断の見送り(通常の承認プロンプトに任せる)ができる
- 追加フィールド:
turn_id、tool_name、tool_input、tool_input.description(string | null。Codex が持つときだけの人間可読な承認理由) - 承認は
hookSpecificOutput.decision.behavior: "allow"、拒否は"deny"とmessage - 複数の hook が決定を返した場合、
denyが優先される。denyが無くallowがあれば承認プロンプトを出さずに進む。どの hook も決めなければ通常の承認フローになる updatedInput/updatedPermissions/interruptは返してはならない。将来のために予約されており、現時点では fail closed になる
PostToolUse
- 対応ツールが出力を出した後に走る。Bash では非ゼロ終了のコマンドの後にも走る。既に実行されたツールの副作用は取り消せない
- 追加フィールド:
turn_id、tool_name、tool_use_id、tool_input、tool_response stdoutのプレーンテキストは無視されるdecision: "block"とreason、hookSpecificOutput.additionalContextが使える。exit code2とstderrでも同じ- このイベントの
decision: "block"は完了済みの Bash コマンドを取り消さない。フィードバックを記録し、ツール結果をそのフィードバックに置き換えてモデルを続行させる continue: falseを返すと、元のツール結果の通常処理を止めてフィードバックまたは stop テキストから続行するupdatedMCPToolOutputとsuppressOutputはパースされるが未対応。hook run を失敗としてマークし、ツール結果の通常処理を続ける
code mode(モデルが JavaScript からツールを呼ぶ経路)での挙動:
| hook の結果 | code mode から見える挙動 |
|---|---|
PreToolUse がブロック | ツール実行前に promise が reject される |
PreToolUse が updatedInput を返す | 書き換え後の入力でツールが走り、その結果で promise が resolve する |
PostToolUse が decision: "block" または exit 2 | ツールは走り、その後 hook の理由で promise が reject される |
PostToolUse が continue: false | モデルから見える結果には hook のフィードバックを使うが、ネストしたツールの promise は reject しない |
PreCompact / PostCompact
matcherはtrigger(manual/auto)に適用される- 追加フィールド:
turn_id、trigger stdoutのプレーンテキストは無視されるPreCompactがcontinue: falseを返すと compaction の前に停止する。PostCompactのcontinue: falseは compaction の後に停止する
UserPromptSubmit
matcherは使われない- 追加フィールド:
turn_id、prompt stdoutのプレーンテキストは追加の developer context になるhookSpecificOutput.additionalContextでコンテキストを足せる{"decision": "block", "reason": ...}または exit code2+stderrでプロンプトをブロックできる
SubagentStop
matcherはagent_typeに適用される- 追加フィールド:
turn_id、agent_id、agent_type、agent_transcript_path、stop_hook_active、last_assistant_message - exit
0のとき JSON を要求する。プレーンテキスト出力はこのイベントでは不正 {"decision": "block", "reason": ...}または exit code2で subagent の継続を求める- いずれかの
SubagentStophook がcontinue: falseを返すと、他の hook の継続決定より優先される
Stop
matcherは使われない- 追加フィールド:
turn_id、stop_hook_active、last_assistant_message - exit
0のとき JSON を要求する。プレーンテキスト出力は不正 decision: "block"はターンを拒否するのではなく、reasonをプロンプト文とする新しい継続プロンプトを自動生成して Codex を続行させる- いずれかの
Stophook がcontinue: falseを返すと、他の hook の継続決定より優先される
設定
{
"description": "Optional lifecycle hooks for this workspace.",
"hooks": {
"SessionStart": [
{
"matcher": "startup|resume",
"hooks": [
{
"type": "command",
"command": "python3 ~/.codex/hooks/session_start.py",
"statusMessage": "Loading session notes",
"additionalContextLimit": 5000
}
]
}
],
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "/usr/bin/python3 \"$(git rev-parse --show-toplevel)/.codex/hooks/pre_tool_use_policy.py\"",
"statusMessage": "Checking Bash command"
}
]
}
]
}
}
[[hooks.PreToolUse]]
matcher = "^Bash$"
[[hooks.PreToolUse.hooks]]
type = "command"
command = '/usr/bin/python3 "$(git rev-parse --show-toplevel)/.codex/hooks/pre_tool_use_policy.py"'
timeout = 30
statusMessage = "Checking Bash command"
allow_managed_hooks_only = true
[features]
hooks = true
[hooks]
managed_dir = "/enterprise/hooks"
windows_managed_dir = 'C:\enterprise\hooks'
制約・注意点
- ツール hook は完全な強制境界ではない。一部の特殊なツール経路は hook 経路をオプトアウトできる
- hook 出力はディスクに書かれることがあるため、秘密情報を返さない
- hook と plugin からのコンテキストは累積し、モデル性能を落としうる。
additionalContextLimitを上げるとそのリスクが増える。0はハンドラ側で厳密な出力上限を持つ場合を除いて避ける - 公式ドキュメントの Schemas 節は、GitHub の
mainブランチの生成スキーマにリリース版に無い hook フィールドが含まれうると注意している。リリース時の挙動はドキュメント本文が基準
関連
facts/codex/subagents.mdfacts/codex/plugins.mdfacts/codex/permissions.mdfacts/codex/configuration-reference.mdfacts/codex/config-basics.md