Appearance
Hooks 是 Codex 的扩展框架,允许你在 Agent 执行循环的关键节点注入自己的脚本。常见用途:把对话发送到自定义日志系统、扫描 Prompt 中的 API key 泄露风险、自动生成持久化记忆、在会话结束时运行校验器。本文覆盖 hooks.json 配置格式、匹配器规则、5 种事件的完整输入输出字段,以及常见场景示例。
OpenAI Codex Hooks
实验性功能,正在持续开发中。Windows 暂不支持。
Hooks 让你把自己的脚本注入 Codex 的 agent 循环,实现:
- 把对话发送到自定义日志或分析系统
- 扫描 Prompt,防止意外粘贴 API key 等敏感信息
- 自动汇总对话,创建持久化记忆
- 在会话回合结束时运行校验器,强制执行规范
- 根据当前目录定制 Prompting 行为
启用 Hooks
在 config.toml 里开启 feature flag:
toml
[features]
codex_hooks = true运行时注意事项:
- 多个匹配的 hooks 文件都会运行
- 同一事件的多个匹配 command hooks 并发启动,一个 hook 无法阻止另一个 hook 的启动
PreToolUse、PostToolUse、UserPromptSubmit、Stop在 turn 级别运行- Hooks 当前不支持 Windows
hooks.json 的位置
Codex 在活跃 config 层级旁边查找 hooks.json。最常用的两个位置:
~/.codex/hooks.json(全局 hooks)<repo>/.codex/hooks.json(仓库级 hooks)
多个 hooks.json 文件同时存在时,Codex 加载所有匹配的 hooks。高优先级 config 层不会覆盖低优先级的 hooks。
配置格式
Hooks 分三层结构:事件 → 匹配器组 → hook 处理器列表。
json
{
"hooks": {
"SessionStart": [
{
"matcher": "startup|resume",
"hooks": [
{
"type": "command",
"command": "python3 ~/.codex/hooks/session_start.py",
"statusMessage": "加载会话笔记"
}
]
}
],
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "/usr/bin/python3 \"$(git rev-parse --show-toplevel)/.codex/hooks/pre_tool_use_policy.py\"",
"statusMessage": "检查 Bash 命令"
}
]
}
],
"PostToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "/usr/bin/python3 \"$(git rev-parse --show-toplevel)/.codex/hooks/post_tool_use_review.py\"",
"statusMessage": "审查 Bash 输出"
}
]
}
],
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "/usr/bin/python3 \"$(git rev-parse --show-toplevel)/.codex/hooks/user_prompt_submit_data_flywheel.py\""
}
]
}
],
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "/usr/bin/python3 \"$(git rev-parse --show-toplevel)/.codex/hooks/stop_continue.py\"",
"timeout": 30
}
]
}
]
}
}配置说明:
timeout:单位秒。省略时默认 600 秒,timeoutSec也是有效别名statusMessage:可选,在 UI 或事件流中显示- Commands 以 session
cwd作为工作目录执行 - 仓库级 hooks 建议从 git root 解析路径(
git rev-parse --show-toplevel),避免从子目录启动时路径错误
匹配器(matcher)
matcher 是一个正则表达式字符串,用于过滤 hook 的触发时机。用 "*"、"" 或省略 matcher 表示匹配该事件的所有触发。
各事件的 matcher 作用对象:
| 事件 | matcher 过滤的对象 | 备注 |
|---|---|---|
PostToolUse | 工具名称 | 当前运行时只触发 Bash |
PreToolUse | 工具名称 | 当前运行时只触发 Bash |
SessionStart | 启动来源(startup 或 resume) | |
UserPromptSubmit | 不支持 | 配置了 matcher 也会被忽略 |
Stop | 不支持 | 配置了 matcher 也会被忽略 |
所有 Hook 共有的输入字段
每个 command hook 通过 stdin 接收一个 JSON 对象,包含以下通用字段:
| 字段 | 类型 | 含义 |
|---|---|---|
session_id | string | 当前会话或线程 ID |
transcript_path | string | null | 会话记录文件路径(如有) |
cwd | string | 当前工作目录 |
hook_event_name | string | 当前 hook 事件名 |
model | string | 当前使用的模型 slug |
所有 Hook 共有的输出字段
SessionStart、UserPromptSubmit、Stop 支持以下 JSON 输出:
json
{
"continue": true,
"stopReason": "可选原因",
"systemMessage": "可选系统消息",
"suppressOutput": false
}| 字段 | 效果 |
|---|---|
continue: false | 标记这次 hook 运行为已停止 |
stopReason | 记录停止原因 |
systemMessage | 在 UI 或事件流中显示为警告 |
suppressOutput | 已解析但尚未实现 |
exit 0 且无输出 = 成功,Codex 继续执行。
五种 Hook 事件详解
SessionStart
在每次会话启动或恢复时触发。
额外输入字段:
| 字段 | 类型 | 含义 |
|---|---|---|
source | string | 启动方式:startup 或 resume |
stdout 输出的纯文本会作为额外的开发者上下文添加进去。
也可以输出 JSON 返回额外上下文:
json
{
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": "编辑前先加载工作区规范。"
}
}PreToolUse
在 Codex 运行工具前触发。当前只支持拦截 Bash 工具(模型仍可以把脚本写到磁盘后用 Bash 运行,因此这更像是一个有用的护栏而不是完整的执行边界)。
额外输入字段:
| 字段 | 类型 | 含义 |
|---|---|---|
turn_id | string | 当前 Codex turn ID |
tool_name | string | 目前始终为 Bash |
tool_use_id | string | 本次工具调用的 ID |
tool_input.command | string | Codex 即将执行的 shell 命令 |
拦截 Bash 命令的输出格式(两种方式都支持):
json
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "破坏性命令被 hook 阻止。"
}
}或者更简洁的旧格式:
json
{
"decision": "block",
"reason": "破坏性命令被 hook 阻止。"
}也可以用 exit code 2 并把阻止原因写到 stderr。
PostToolUse
在工具运行完成后触发。当前只支持 Bash 工具结果,不限于成功执行的命令。无法撤销已经运行的命令的副作用。
额外输入字段:
| 字段 | 类型 | 含义 |
|---|---|---|
turn_id | string | 当前 Codex turn ID |
tool_name | string | 目前始终为 Bash |
tool_use_id | string | 本次工具调用的 ID |
tool_input.command | string | 刚刚运行的 shell 命令 |
tool_response | JSON value | Bash 工具输出(通常是 JSON 字符串) |
添加反馈的输出格式:
json
{
"decision": "block",
"reason": "Bash 输出需要在继续之前先审查。",
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"additionalContext": "命令更新了自动生成的文件。"
}
}注意:PostToolUse 里的 decision: "block" 不会撤销已完成的命令,而是把反馈记录进去,用这个反馈替换工具结果,让模型从 hook 提供的消息继续。
UserPromptSubmit
在用户 Prompt 被发送前触发,可用于内容检查或注入额外上下文。
额外输入字段:
| 字段 | 类型 | 含义 |
|---|---|---|
turn_id | string | 当前 Codex turn ID |
prompt | string | 即将发送的用户 Prompt |
stdout 输出的纯文本作为额外上下文添加。
阻止 Prompt 的输出格式:
json
{
"decision": "block",
"reason": "请在编辑文件前先确认。"
}Stop
在每次会话回合结束时触发,可用于自动继续执行或做收尾工作。
额外输入字段:
| 字段 | 类型 | 含义 |
|---|---|---|
turn_id | string | 当前 Codex turn ID |
stop_hook_active | boolean | 本次 turn 是否已被 Stop hook 继续过 |
last_assistant_message | string | null | 最新的 assistant 消息文本 |
让 Codex 继续运行的输出格式:
json
{
"decision": "block",
"reason": "再跑一遍失败的测试。"
}注意:Stop 里的 decision: "block" 不是拒绝,而是告诉 Codex 继续,并用你的 reason 作为一个新的 Prompt 自动发送。
如果任何一个匹配的 Stop hook 返回 continue: false,它的优先级高于其他 Stop hook 的 continuation decisions。
完整 Schema 参考
需要完整的输入输出格式,参考 Codex GitHub 仓库里的生成 Schema:codex-rs/hooks/schema/generated
常见问题
Q: Hooks 和 AGENTS.md 有什么区别?
A: AGENTS.md 是给 Codex 的静态指令文件,在会话开始时加载一次。Hooks 是动态脚本,在 Agent 执行循环的特定节点实时运行,可以读取实时状态、输出反馈,甚至阻止某些操作。两者互补。
Q: PreToolUse 能拦截所有工具调用吗?
A: 不能,目前只能拦截 Bash 工具。MCP、Write、WebSearch 等工具调用无法被拦截。官方正在开发更完整的拦截机制。
Q: Hooks 里的脚本语言有限制吗?
A: 没有限制,command 字段可以运行任何系统上的可执行程序。示例用 Python,但你也可以用 Node.js、Shell、Go binary 等。关键是脚本通过 stdin 接收 JSON,通过 stdout 输出 JSON(或纯文本)。