Hooks 是 OpenAI Codex 的扩展框架,能把自定义脚本接到会话、工具调用和结束收尾这些节点上。适合做日志、敏感信息拦截、自动记忆、执行前后检查;启用后需要按 hook 定义做 trust 审核,managed hooks 还能由 requirements.toml 强制生效。
OpenAI Codex Hooks 配置与事件参考
Hooks 是 OpenAI Codex 的扩展框架,允许你把自己的脚本注入编码代理的执行循环里,用来:
- 把对话发送到自定义日志或分析系统
- 扫描团队 Prompt,拦截误贴的 API key
- 自动汇总对话,生成持久化记忆
- 在会话回合停止时运行校验,强制执行规范
- 在特定目录下自定义 prompting 行为
Hooks 默认已开启。如果要在 config.toml 里关闭:
[features]
hooks = false
请使用 hooks 作为标准 feature key。codex_hooks 仍然可用,但已经是废弃别名。
管理员也可以在 requirements.toml 里用同样的方式关闭:
[features]
hooks = false
运行时要注意:
- 多个文件中匹配到的 hooks 都会运行
- 同一个事件里,多个匹配的 command hooks 会并发启动,一个 hook 不能阻止另一个匹配 hook 启动
- 非 managed 的 command hooks 在运行前必须先 review 并 trust
PreToolUse、PermissionRequest、PostToolUse、PreCompact、PostCompact、UserPromptSubmit、SubagentStop、Stop作用于 turn 范围SessionStart和SubagentStart作用于 thread 或 subagent 启动范围
Codex 在哪里查找 hooks
Codex 会在 active config layers 附近发现 hooks,支持两种形式:
hooks.jsonconfig.toml里的内联[hooks]表
已安装的 plugins 也可以通过 plugin manifest 或默认的 hooks/hooks.json 提供 lifecycle config。插件打包规则见 Build plugins。
实际最常用的四个位置是:
~/.codex/hooks.json~/.codex/config.toml<repo>/.codex/hooks.json<repo>/.codex/config.toml
如果存在多个 hook source,Codex 会加载所有匹配项。高优先级 config layer 不会覆盖低优先级 hooks。若同一层同时存在 hooks.json 和内联 [hooks],Codex 会合并两者,并在启动时给出警告。每一层建议只保留一种写法。
Codex 也会加载已启用 plugin 中打包的 hooks。它们和其他 hook source 一样走同一套 trust-review 流程。
项目级 hooks 只有在项目的 .codex/ 层已 trusted 时才会加载。对于不可信项目,Codex 仍会加载用户级和系统级 active config layers 中的 hooks。
如何 review 和 trust hooks
Codex 在决定哪些 hooks 能运行之前,会先列出已配置的 hooks。非 managed command hook 运行前,必须 review 并 trust 该 hook 的精确定义。Codex 会把 trust 记录到 hook 当前 hash 上,所以新 hook 或已修改的 hook 会重新进入 review 状态,在 trust 之前会被跳过。
在 CLI 里输入 /hooks,可以查看 hook source、review 新增或变更的 hook、trust hook,或者禁用单个非 managed hook。如果启动时需要 review,Codex 会打印警告并提示打开 /hooks。
来自 system、MDM、cloud 或 requirements.toml 的 managed hooks 会被标记为 managed,按策略 trusted,用户无法在 hook browser 里禁用它们。
如果是一次性自动化,而且你已经在 Codex 外部完成了 hook source 校验,可以加 --dangerously-bypass-hook-trust,让这次调用直接运行已启用 hooks,而不要求持久化 trust 记录。
配置结构
Hooks 分成三层:
- hook event,例如
PreToolUse、PostToolUse、PreCompact、SubagentStart、Stop - 决定匹配时机的 matcher group
- 一个或多个在 matcher 命中时运行的 hook handler
{
"hooks": {
"SessionStart": [
{
"matcher": "startup|resume",
"hooks": [
{
"type": "command",
"command": "python3 ~/.codex/hooks/session_start.py",
"statusMessage": "Loading session notes"
}
]
}
],
"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"
}
]
}
],
"PermissionRequest": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "/usr/bin/python3 \"$(git rev-parse --show-toplevel)/.codex/hooks/permission_request.py\"",
"statusMessage": "Checking approval request"
}
]
}
],
"PostToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "/usr/bin/python3 \"$(git rev-parse --show-toplevel)/.codex/hooks/post_tool_use_review.py\"",
"statusMessage": "Reviewing Bash output"
}
]
}
],
"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的单位是秒- 如果省略
timeout,Codex 使用600秒 statusMessage可选commandWindows是可选的 Windows-only command override;在 TOML 中可写成command_windows或commandWindowsasync会被解析,但异步 command hooks 目前不支持,Codex 会跳过async: true的 handler- 当前只有
type: "command"的 handler 会运行;prompt和agent会被解析但跳过 - 命令会以 session 的
cwd作为工作目录执行 - 仓库本地 hooks 建议从 git root 解析路径,不要直接写相对路径
.codex/hooks/...,因为 Codex 可能从子目录启动,基于 git root 的路径更稳定
config.toml 里的等价内联 TOML 写法:
[[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"
[[hooks.PostToolUse]]
matcher = "^Bash$"
[[hooks.PostToolUse.hooks]]
type = "command"
command = '/usr/bin/python3 "$(git rev-parse --show-toplevel)/.codex/hooks/post_tool_use_review.py"'
timeout = 30
statusMessage = "Checking Bash output"
requirements.toml 里的 managed hooks
企业管理的 requirements 也可以在 [hooks] 下直接定义 hooks。这个方式适合管理员强制统一 hook 配置,同时把脚本通过 MDM 或其他设备管理系统分发出去。要让 hooks 即使在用户本地关闭后也强制生效,可以在 requirements.toml 里和 [hooks] 一起固定 [features].hooks = true。如果你想忽略用户、项目、session 和 plugin hooks,只保留管理员 managed hooks,可以设置 allow_managed_hooks_only = true。
allow_managed_hooks_only = true
[features]
hooks = true
[hooks]
managed_dir = "/enterprise/hooks"
windows_managed_dir = 'C:\enterprise\hooks'
[[hooks.PreToolUse]]
matcher = "^Bash$"
[[hooks.PreToolUse.hooks]]
type = "command"
command = "python3 /enterprise/hooks/pre_tool_use_policy.py"
command_windows = 'py -3 C:\enterprise\hooks\pre_tool_use_policy.py'
timeout = 30
statusMessage = "Checking managed Bash command"
managed hooks 的注意事项:
managed_dir用于 macOS 和 Linuxwindows_managed_dir用于 Windows- Codex 不会分发
managed_dir里的脚本;企业工具需要单独安装和更新 - managed hook 命令应使用配置好的 managed directory 下的绝对脚本路径
allow_managed_hooks_only = true会跳过来自用户、项目、session 和 plugin 的 hooks,但仍会加载requirements.toml和其他 managed config layers 中的 managed hooks
plugin 打包的 hooks
启用 plugin 后,Codex 可以把 plugin 的 lifecycle hooks 和用户、项目、managed hooks 一起加载。
默认情况下,Codex 会在 plugin root 里找 hooks/hooks.json。plugin manifest 可以通过 .codex-plugin/plugin.json 里的 hooks 条目覆盖这个默认值。这个条目可以是带 ./ 前缀的路径、多个 ./ 路径组成的数组、内联 hooks object,或者多个内联 hooks object 的数组。
{
"name": "repo-policy",
"hooks": "./hooks/hooks.json"
}
manifest 中的 hook 路径会相对于 plugin root 解析,并且必须留在这个 root 内。如果 manifest 定义了 hooks,Codex 会优先使用这些 manifest 条目,而不是默认的 hooks/hooks.json。
plugin hook 命令会收到这些环境变量:
PLUGIN_ROOT:Codex 特有扩展,指向已安装的 plugin rootPLUGIN_DATA:Codex 特有扩展,指向 plugin 可写数据目录- 为了兼容已有 plugin hooks,Codex 也会设置
CLAUDE_PLUGIN_ROOT和CLAUDE_PLUGIN_DATA
plugin hooks 使用和其他 hooks 相同的 event schema。安装或启用 plugin 不会自动 trust 它的 hooks;Codex 会跳过 plugin 打包的 hooks,直到你 review 并 trust 当前 hook definition。
matcher 模式
matcher 字段是一个正则字符串,用来过滤 hook 触发时机。使用 "*"、"",或者直接省略 matcher,都表示匹配该支持事件的所有触发。
当前只有部分 Codex 事件会使用 matcher:
| Event | matcher 过滤的内容 |
备注 |
|---|---|---|
PermissionRequest |
tool name | 支持 Bash、apply_patch* 和 MCP tool names |
PostToolUse |
tool name | 支持 Bash、apply_patch* 和 MCP tool names |
PostCompact |
compaction trigger | 值为 manual 或 auto |
PreCompact |
compaction trigger | 值为 manual 或 auto |
PreToolUse |
tool name | 支持 Bash、apply_patch* 和 MCP tool names |
SessionStart |
start source | 值为 startup、resume、clear、compact |
SubagentStart |
subagent type | 值取决于启动的 subagent |
SubagentStop |
subagent type | 值取决于停止的 subagent |
UserPromptSubmit |
不支持 | 配置的 matcher 会被忽略 |
Stop |
不支持 | 配置的 matcher 会被忽略 |
* 对 apply_patch 来说,matcher 也可以写 Edit 或 Write。
可用例子:
Bash^apply_patch$Edit|Writemcp__filesystem__read_filemcp__filesystem__.*startup|resume|clear|compactmanual|auto
通用输入字段
每个 command hook 都会从 stdin 接收一个 JSON object。
最常用的通用字段如下:
| 字段 | 类型 | 含义 |
|---|---|---|
session_id |
string |
当前 Codex session id。subagent hooks 会使用父 session id |
transcript_path |
string | null |
会话 transcript 文件路径(如果有) |
cwd |
string |
会话工作目录 |
hook_event_name |
string |
当前 hook 事件名 |
model |
string |
Codex 特有扩展,当前活动模型 slug |
作用于 turn 的 hooks 在各自事件表里还会列出 turn_id,这是 Codex 特有扩展。
SessionStart、PreToolUse、PermissionRequest、PostToolUse、UserPromptSubmit、SubagentStart、SubagentStop 和 Stop 还包含 permission_mode,用于描述当前权限模式,值可能是 default、acceptEdits、plan、dontAsk 或 bypassPermissions。
transcript_path 只是为了方便访问会话记录,但 transcript 格式不是 hooks 的稳定接口,未来可能变化。
如果你需要完整 wire format,请看 Schemas。
通用输出字段
SessionStart、PreCompact、PostCompact、UserPromptSubmit、SubagentStop 和 Stop 支持下面这些共享 JSON 字段。SubagentStart 也接受相同形状的 systemMessage 和 hook-specific context,但 continue: false 不会停止 subagent:
{
"continue": true,
"stopReason": "optional",
"systemMessage": "optional",
"suppressOutput": false
}
| 字段 | 效果 |
|---|---|
continue |
如果是 false,表示这次 hook run 被标记为 stopped |
stopReason |
记录为停止原因 |
systemMessage |
在 UI 或 event stream 中作为 warning 展示 |
suppressOutput |
目前已解析,但还没实现 |
退出码 0 且没有输出,会被当成成功,Codex 会继续。
PreToolUse 和 PermissionRequest 支持 systemMessage,但当前不支持 continue、stopReason 和 suppressOutput。如果 PreToolUse hook 返回了这些不支持的字段,Codex 会把这次 hook run 标记为失败,报告错误,并继续工具调用。
PostToolUse 支持 systemMessage、continue: false 和 stopReason。suppressOutput 会被解析,但这个事件还不支持。
各个 Hooks 事件
SessionStart
matcher 会应用到这个事件的 source 上。
除了 通用输入字段 外,还包含:
| 字段 | 类型 | 含义 |
|---|---|---|
source |
string |
会话如何开始:startup、resume、clear 或 compact |
stdout 里的纯文本会被加入为额外的 developer context。
stdout 里的 JSON 也支持 通用输出字段 和下面这个 hook-specific 结构:
{
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": "Load the workspace conventions before editing."
}
}
这段 additionalContext 会作为额外 developer context 加进去。
SubagentStart
matcher 会应用到这个事件的 agent_type 上。
除了 通用输入字段 外,还包含:
| 字段 | 类型 | 含义 |
|---|---|---|
turn_id |
string |
Codex 特有扩展。当前 Codex turn id |
agent_id |
string |
subagent 标识 |
agent_type |
string |
subagent 类型或 profile |
permission_mode |
string |
当前权限模式 |
stdout 里的纯文本会作为 subagent 的额外 developer context。
stdout 里的 JSON 支持 systemMessage 和下面这个 hook-specific 结构:
{
"hookSpecificOutput": {
"hookEventName": "SubagentStart",
"additionalContext": "Review the repository test conventions first."
}
}
这段 additionalContext 会作为 subagent 的额外 developer context。continue: false 虽然会被解析以保持兼容,但不会阻止 subagent 启动。
PreToolUse
PreToolUse 可以拦截 Bash、通过 apply_patch 完成的文件编辑,以及 MCP tool calls。它仍然只是护栏,不是完整的强制边界,因为 Codex 往往可以通过其他支持的工具路径完成等价工作。
它目前不能拦截所有 shell 调用,只能拦截简单的那部分。新的 unified_exec 机制支持更丰富的 shell streaming stdin/stdout,但拦截还不完整。同样,它也不能拦截 WebSearch 或其他非 shell、非 MCP 的 tool calls。
matcher 会应用到 tool_name 和 matcher aliases。对于通过 apply_patch 进行的文件编辑,matcher 可以写 apply_patch、Edit 或 Write;hook 输入里仍然会报告 tool_name: "apply_patch"。
除了 通用输入字段 外,还包含:
| 字段 | 类型 | 含义 |
|---|---|---|
turn_id |
string |
Codex 特有扩展。当前 Codex turn id |
tool_name |
string |
标准 hook 工具名,例如 Bash、apply_patch,或类似 mcp__fs__read 的 MCP 名称 |
tool_use_id |
string |
本次工具调用 id |
tool_input |
JSON value |
工具特定输入。Bash 和 apply_patch 使用 tool_input.command,MCP tools 会传所有参数 |
stdout 里的纯文本会被忽略。
stdout 的 JSON 可以使用 systemMessage。如果要拒绝一个受支持的工具调用,返回下面这个结构:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Destructive command blocked by hook."
}
}
Codex 也接受较旧的 block 结构:
{
"decision": "block",
"reason": "Destructive command blocked by hook."
}
你也可以返回 exit code 2,并把阻止原因写到 stderr。
如果只是想给模型补充可见上下文而不拦截,可以返回 hookSpecificOutput.additionalContext:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"additionalContext": "The pending command touches generated files."
}
}
如果要在不阻止调用的情况下重写一个受支持的工具调用,可以返回 permissionDecision: "allow" 并提供 updatedInput:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"updatedInput": {
"command": "echo rewritten"
}
}
}
对于 Bash 命令和 apply_patch,updatedInput 必须包含字符串类型的 command 字段。对 MCP tools,updatedInput 是替换后的 arguments object。只有在 permissionDecision: "allow" 时才可以返回 updatedInput;其他 updatedInput 形状都会报错。
permissionDecision: "ask"、旧版 decision: "approve"、continue: false、stopReason 和 suppressOutput 会被解析,但目前不支持。Codex 会把这次 hook run 标记为失败,报告错误,并继续工具调用。
PermissionRequest
PermissionRequest 会在 Codex 准备请求用户批准时运行,例如 shell escalation 或 managed-network approval。它可以允许请求、拒绝请求,或者放弃决定,让正常的批准提示继续。对于不需要审批的命令,这个事件不会运行。
matcher 会应用到 tool_name 和 matcher aliases。当前 canonical 值包括 Bash、apply_patch,以及类似 mcp__server__tool 的 MCP tool names;apply_patch 也会匹配 Edit 和 Write。
除了 通用输入字段 外,还包含:
| 字段 | 类型 | 含义 |
|---|---|---|
turn_id |
string |
Codex 特有扩展。当前 Codex turn id |
tool_name |
string |
标准 hook 工具名,例如 Bash、apply_patch,或类似 mcp__fs__read 的 MCP 名称 |
tool_input |
JSON value |
工具特定输入。Bash 和 apply_patch 使用 tool_input.command,MCP tools 会传所有参数 |
tool_input.description |
string | null |
可读的审批原因(如果 Codex 提供了) |
stdout 的纯文本会被忽略。
有些 tool input 里会带人类可读的描述,但不要依赖每个工具都有 tool_input.description。
要批准请求,返回:
{
"hookSpecificOutput": {
"hookEventName": "PermissionRequest",
"decision": {
"behavior": "allow"
}
}
}
要拒绝请求,返回:
{
"hookSpecificOutput": {
"hookEventName": "PermissionRequest",
"decision": {
"behavior": "deny",
"message": "Blocked by repository policy."
}
}
}
如果多个匹配 hooks 都返回 decision,任何 deny 都会获胜。否则,allow 会让请求直接通过,不再显示批准提示。如果没有任何匹配 hook 做出决定,Codex 会走正常的 approval flow。
不要在 PermissionRequest 里返回 updatedInput、updatedPermissions 或 interrupt;这些字段预留给未来行为,目前会 fail closed。
PostToolUse
PostToolUse 会在受支持工具产出结果后运行,包括 Bash、apply_patch 和 MCP tool calls。对于 Bash,它也会在命令非 0 退出时运行。它不能撤销工具已经造成的副作用。
它目前不能拦截所有 shell 调用,只能拦截简单的那部分。新的 unified_exec 机制支持更丰富的 shell streaming stdin/stdout,但拦截还不完整。同样,它也不能拦截 WebSearch 或其他非 shell、非 MCP 的 tool calls。
matcher 会应用到 tool_name 和 matcher aliases。对于通过 apply_patch 进行的文件编辑,matcher 可以写 apply_patch、Edit 或 Write;hook 输入里仍然会报告 tool_name: "apply_patch"。
除了 通用输入字段 外,还包含:
| 字段 | 类型 | 含义 |
|---|---|---|
turn_id |
string |
Codex 特有扩展。当前 Codex turn id |
tool_name |
string |
标准 hook 工具名,例如 Bash、apply_patch,或类似 mcp__fs__read 的 MCP 名称 |
tool_use_id |
string |
本次工具调用 id |
tool_input |
JSON value |
工具特定输入。Bash 和 apply_patch 使用 tool_input.command,MCP tools 会传所有参数 |
tool_response |
JSON value |
工具特定输出。对 MCP tools 来说,这是 MCP 调用结果 |
stdout 的纯文本会被忽略。
stdout 里的 JSON 可以使用 systemMessage 和下面这个 hook-specific 结构:
{
"decision": "block",
"reason": "The Bash output needs review before continuing.",
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"additionalContext": "The command updated generated files."
}
}
这段 additionalContext 会作为额外 developer context 加进去。
在这个事件里,decision: "block" 不会撤销已经完成的 Bash 命令。相反,Codex 会记录这条反馈,用这条反馈替换工具结果,然后让模型从 hook 提供的消息继续。
你也可以用 exit code 2 并把反馈原因写到 stderr。
如果要在命令已经运行后停止正常处理原始工具结果,可以返回 continue: false。Codex 会用你的反馈或 stop text 替换工具结果,然后继续往下走。
updatedMCPToolOutput 和 suppressOutput 会被解析,但目前不支持。Codex 会把这次 hook run 标记为失败,报告错误,并继续按正常流程处理工具结果。
PreCompact
PreCompact 会在 Codex 压缩会话之前运行。matcher 会应用到 trigger,其值为 manual 和 auto。
除了 通用输入字段 外,还包含:
| 字段 | 类型 | 含义 |
|---|---|---|
turn_id |
string |
Codex 特有扩展。当前 Codex turn id |
trigger |
string |
触发压缩的原因:manual 或 auto |
stdout 的纯文本会被忽略。
stdout 里的 JSON 支持 通用输出字段。如果匹配的 PreCompact hook 返回 continue: false,Codex 会在压缩前停止。
PostCompact
PostCompact 会在 Codex 压缩会话后运行。matcher 会应用到 trigger,其值为 manual 和 auto.
除了 通用输入字段 外,还包含:
| 字段 | 类型 | 含义 |
|---|---|---|
turn_id |
string |
Codex 特有扩展。当前 Codex turn id |
trigger |
string |
触发压缩的原因:manual 或 auto |
stdout 的纯文本会被忽略。
stdout 里的 JSON 支持 通用输出字段。如果匹配的 PostCompact hook 返回 continue: false,Codex 会在压缩后停止。
UserPromptSubmit
这个事件当前不使用 matcher。
除了 通用输入字段 外,还包含:
| 字段 | 类型 | 含义 |
|---|---|---|
turn_id |
string |
Codex 特有扩展。当前 Codex turn id |
prompt |
string |
即将发送的用户 prompt |
stdout 的纯文本会作为额外 developer context 加进去。
stdout 里的 JSON 支持 通用输出字段 和下面这个 hook-specific 结构:
{
"hookSpecificOutput": {
"hookEventName": "UserPromptSubmit",
"additionalContext": "Ask for a clearer reproduction before editing files."
}
}
这段 additionalContext 会作为额外 developer context 加进去。
如果要阻止 prompt,返回:
{
"decision": "block",
"reason": "Ask for confirmation before doing that."
}
也可以用 exit code 2 并把阻止原因写到 stderr。
SubagentStop
matcher 会应用到这个事件的 agent_type 上。
除了 通用输入字段 外,还包含:
| 字段 | 类型 | 含义 |
|---|---|---|
turn_id |
string |
Codex 特有扩展。当前 Codex turn id |
agent_id |
string |
subagent 标识 |
agent_type |
string |
subagent 类型或 profile |
agent_transcript_path |
string | null |
subagent transcript 文件路径(如有) |
stop_hook_active |
boolean |
这个 subagent 是否已经被继续过 |
last_assistant_message |
string | null |
最新的 subagent assistant message(如有) |
SubagentStop 在退出码为 0 时期望 stdout 输出 JSON。这个事件不接受纯文本输出。
stdout 里的 JSON 支持 通用输出字段。如果要让 Codex 继续 subagent flow,返回:
{
"decision": "block",
"reason": "Run one more focused pass inside the subagent."
}
也可以用 exit code 2 并把 continuation reason 写到 stderr。
如果任何一个匹配的 SubagentStop hook 返回 continue: false,它的优先级高于其他匹配 SubagentStop hook 的 continuation decisions。
Stop
这个事件当前不使用 matcher。
除了 通用输入字段 外,还包含:
| 字段 | 类型 | 含义 |
|---|---|---|
turn_id |
string |
Codex 特有扩展。当前 Codex turn id |
stop_hook_active |
boolean |
这个 turn 是否已经被 Stop 继续过 |
last_assistant_message |
string | null |
最新的 assistant message 文本(如有) |
Stop 在退出码为 0 时期望 stdout 输出 JSON。这个事件不接受纯文本输出。
stdout 里的 JSON 支持 通用输出字段。如果要让 Codex 继续,返回:
{
"decision": "block",
"reason": "Run one more pass over the failing tests."
}
也可以用 exit code 2 并把 continuation reason 写到 stderr。
在这个事件里,decision: "block" 不是拒绝 turn。它会让 Codex 继续,并自动创建一个新的 continuation prompt,把你的 reason 当作新的 prompt 文本。
如果任何一个匹配的 Stop hook 返回 continue: false,它的优先级高于其他匹配 Stop hook 的 continuation decisions。
Schemas
链接到 main 分支的 schemas 可能包含当前发布版本里还没有的 hook 字段。请以本页描述的 release 行为为准。
如果你需要精确的当前 wire format,请查看 Codex GitHub 仓库里生成的 schemas:codex-rs/hooks/schema/generated
常见问题
OpenAI Codex Hooks 和 AGENTS.md 有什么区别?
AGENTS.md 是给 Codex 的静态指令文件,在会话开始时加载一次。Hooks 是动态脚本,会在 Agent 执行循环的特定节点实时运行,可以读取实时状态、输出反馈,甚至阻止某些操作。两者是互补关系。
PreToolUse 能拦截所有工具调用吗?
不能。当前只会拦截 Bash、apply_patch 和部分 MCP tool calls。WebSearch 以及其他非 shell、非 MCP 的工具调用不会被拦截。Codex 仍在推进更完整的拦截机制。
Hooks 里的脚本语言有限制吗?
没有。command 可以运行系统上任何可执行程序。示例用的是 Python,但也可以用 Node.js、Shell、Go binary 等。关键是脚本从 stdin 读 JSON,并向 stdout 输出 JSON 或纯文本。