钩子
Hooks
在 Codex 生命周期中运行确定性脚本
Hooks 是 Codex 的可扩展性框架。你可以借助它们在智能体循环期间运行脚本或 MCP 工具,从而实现以下功能:
- 将聊天发送到自定义日志记录/分析引擎
- 扫描团队的提示词,防止意外粘贴 API key
- 汇总聊天内容,自动创建持久记忆
- 在一轮聊天停止时运行自定义验证检查,以强制执行标准
- 在特定目录中自定义提示方式
需要注意的运行时行为:
- 来自多个文件的所有匹配 hooks 都会运行。
- 同一事件的多个匹配命令 hooks 会并发启动, 因此一个 hook 无法阻止另一个匹配 hook 启动。
- 非托管 hooks 必须先经过审查并受信任,才能运行。
Hooks 会在对话的不同阶段运行:
| 时机 | Hooks |
|---|---|
| 一轮对话期间 | PreToolUse, PermissionRequest, PostToolUse, PreCompact, PostCompact, UserPromptSubmit, SubagentStop, Stop |
| 会话或子智能体启动时 | SessionStart, SubagentStart |
| 主线程结束时 | SessionEnd(不会为子智能体运行) |
Codex 在哪里查找 hooks
Codex 会在活跃配置层旁边查找以下任一形式的 hooks:
hooks.jsonconfig.toml内的内联[hooks]表
已安装的插件也可以通过其插件清单或默认的 hooks/hooks.json 文件捆绑生命周期配置。
有关插件打包规则,请参阅构建
插件。
实际使用中,最有用的四个位置是:
~/.codex/hooks.json~/.codex/config.toml<repo>/.codex/hooks.json<repo>/.codex/config.toml
如果存在多个 hook 来源,Codex 会加载所有匹配的 hooks。
优先级较高的配置层不会替换优先级较低的 hooks。
如果同一层同时包含 hooks.json 和内联 [hooks],Codex
会合并二者并在启动时发出警告。每一层最好只使用一种表示方式。
Codex 还可以发现已启用插件中捆绑的 hooks。插件捆绑的 hooks 会与其他 hook 来源一同加载,并采用与 其他非托管 hooks 相同的信任审查流程。
仅当项目 .codex/ 层受信任时,才会加载项目本地 hooks。在
不受信任的项目中,Codex 仍会从各自的活跃配置层加载用户和系统 hooks。
审查和信任 hooks
Codex 会先列出已配置的 hooks,再决定哪些可以运行。在 非托管 hook 运行之前,Codex 要求你审查并信任其确切的 hook 定义。Codex 会针对 hook 当前的哈希值记录信任状态,因此新增或 更改后的 hooks 会被标记为待审查,并在获得信任前跳过。
在 CLI 中使用 /hooks 可检查 hook 来源、审查新增或更改的 hooks、
信任 hooks,或禁用单个非托管 hook。如果启动时有 hooks 需要审查,
Codex 会输出警告,提示你打开 /hooks。
来自系统、MDM、云端或 requirements.toml 来源的托管 hooks 会被标记为
托管项,根据策略受到信任,并且无法从用户 hook 浏览器中禁用。
对于已在 Codex 外部审查 hook 来源的一次性自动化任务,可传入
--dangerously-bypass-hook-trust,从而在该次调用中运行已启用的 hooks,且无需
持久保存 hook 信任状态。
配置结构
Hooks 分为三个层级:
- Hook 事件,例如
PreToolUse、PostToolUse、PreCompact、SubagentStart或Stop - 用于决定该事件何时匹配的匹配器组
- 匹配器组命中时运行的一个或多个 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
}
]
}
],
"SessionEnd": [
{
"hooks": [
{
"type": "command",
"command": "python3 ~/.codex/hooks/session_end.py",
"timeout": 3
}
]
}
],
"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
}
]
}
]
}
}注意:
description是hooks.json文件可选的顶层元数据。它 不会改变哪些 hooks 会运行。timeout的单位为秒。- 如果省略
timeout,Codex 会为大多数 hooks 使用600秒。SessionEnd默认使用1秒,最高支持3秒。
statusMessage是可选项。additionalContextLimit设置命令 hook 可以向模型发送多少additionalContext, 超出后 Codex 会把完整文本保存到磁盘,改为发送较短的 预览。请参阅大型 hook 输出。commandWindows是可选的 Windows 专用命令替代项。在 TOML 中,请使用command_windows或commandWindows。- 将
async设为true,即可在后台运行命令 hook。 - 支持
command和mcp_tool处理程序。prompt和agent处理程序会被解析,但会跳过。 - 命令以会话
cwd作为工作目录运行。 - 对于仓库本地 hooks,最好从 git 根目录解析路径,而不是使用
.codex/hooks/...之类的相对路径。Codex 可能从 子目录启动,基于 git 根目录的路径能让 hook 位置保持稳定。
config.toml 中等效的内联 TOML:
[[hooks.SessionStart]]
matcher = "^compact$"
[[hooks.SessionStart.hooks]]
type = "command"
command = '/usr/bin/python3 "$(git rev-parse --show-toplevel)/.codex/hooks/session_start.py"'
additionalContextLimit = 5000
[[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 = "Reviewing Bash output"MCP 工具 hooks
MCP 工具 hook 允许生命周期事件调用已连接 MCP 服务器上的工具。它会将结构化参数直接发送给该工具,并采用与命令 hook 相同的信任审查和输出约定。
配置 MCP 工具 hook
此 hook 会要求 scanner MCP 服务器在 Codex 写入或
编辑文件后扫描每个补丁:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "mcp_tool",
"server": "scanner",
"tool": "scan_patch",
"input": { "patch": "${tool_input.command}" },
"timeout": 30,
"statusMessage": "Scanning edited files"
}
]
}
]
}
}| 字段 | 含义 |
|---|---|
type |
必须为 mcp_tool。 |
server |
已连接 MCP 服务器的名称,必填。 |
tool |
该服务器所公开工具的名称,必填。 |
input |
可选的参数模板 JSON 对象。默认为 {}。 |
timeout |
可选的活跃执行超时时间,单位为秒。默认为 600。 |
statusMessage |
Hook 运行时显示的可选消息。 |
根据 hook 事件展开参数
使用 ${field.nested} 读取 hook 事件中的点号分隔字段。如果占位符
填充整个值,则会保留其 JSON 类型。如果占位符位于较长的
字符串中,则会呈现为文本。Codex 会递归展开对象和数组。
对于包含 {"tool_input":{"file_path":"src/main.rs","count":3}} 的事件,
以下参数模板:
{
"path": "${tool_input.file_path}",
"count": "${tool_input.count}",
"message": "Scanning ${tool_input.file_path}"
}会变为:
{
"path": "src/main.rs",
"count": 3,
"message": "Scanning src/main.rs"
}执行和生命周期
- Hooks 使用现有 MCP 连接,不会启动或重新连接服务器。
- 当工具返回阻止决定时,hook 可以阻止操作。 错误、服务器缺失和工具不可用不会阻止操作。
- MCP 工具 hooks 会同步运行。它们不会请求工具批准,也不会触发 其他 hooks。
- 以 hook 或服务器中较短的超时时间为准。等待 MCP 引导响应所用的时间不计入超时。
SessionStarthooks 可能在 MCP 服务器准备就绪前运行。如果发生这种情况, 它们不会阻止会话。SessionEnd不支持 MCP 工具 hooks。
关闭 hooks
Hooks 默认启用。要在 config.toml 中将其关闭,请设置:
[features]
hooks = false请使用 hooks 作为规范功能键。codex_hooks 作为
已弃用的别名仍然有效。管理员可以在 requirements.toml 中使用 [features].hooks = false,
以相同方式强制关闭 hooks。
来自 requirements.toml 的托管 hooks
企业托管要求也可以在 [hooks] 下以内联方式定义 hooks。
当管理员希望强制执行 hook 配置,同时通过 MDM 或其他设备管理系统
分发实际脚本时,这种方式非常有用。即使用户已在本地禁用 hooks,
如需强制执行托管 hooks,请在 requirements.toml 中将 [features].hooks = true 与 [hooks] 一同固定。若要忽略
用户、项目、会话和插件 hooks,同时仍允许管理员
托管的 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"托管 hooks 的注意事项:
managed_dir用于 macOS 和 Linux。windows_managed_dir用于 Windows。- Codex 不会分发
managed_dir中的脚本;你的企业 工具必须单独安装和更新这些脚本。 - 托管 hook 命令应使用所配置托管目录下脚本的绝对路径。
allow_managed_hooks_only = true会跳过来自用户、项目、会话和 插件来源的 hooks,但仍会加载来自requirements.toml和 其他托管配置层的托管 hooks。
插件捆绑的 hooks
启用插件后,Codex 可以加载该插件中的生命周期 hooks, 并与用户、项目和托管 hooks 一同运行。
默认情况下,Codex 会在插件根目录中查找 hooks/hooks.json。插件
清单可以使用 .codex-plugin/plugin.json 中的 hooks 条目替代该默认设置。
清单条目可以是以 ./ 为前缀的路径、以 ./ 为前缀的路径数组、
内联 hooks 对象,或内联 hooks 对象数组。
{
"name": "repo-policy",
"hooks": "./hooks/hooks.json"
}清单 hook 路径相对于插件根目录解析,且必须位于
该根目录之内。如果清单定义了 hooks,Codex 将使用这些清单
条目,而不是默认的 hooks/hooks.json。
插件 hook 命令会收到以下环境变量:
PLUGIN_ROOT是 Codex 专用扩展,指向已安装的 插件根目录。PLUGIN_DATA是 Codex 专用扩展,指向插件的 可写数据目录。- Codex 还会设置
CLAUDE_PLUGIN_ROOT和CLAUDE_PLUGIN_DATA,以便 与现有插件 hooks 保持兼容。
插件 hooks 与其他 hooks 使用相同的事件架构。安装或启用 插件并不会自动信任其 hooks;在你审查并信任当前 hook 定义前, Codex 会跳过插件捆绑的 hooks。
匹配器模式
matcher 字段是一个正则表达式字符串,用于筛选 hooks 何时触发。使用 "*"、
"",或完全省略 matcher,即可匹配受支持事件的每次
出现。
目前只有部分 Codex 事件会采用 matcher:
| 事件 | matcher 筛选的内容 |
备注 |
|---|---|---|
PermissionRequest |
工具名称 | 支持范围包括 Bash、apply_patch* 和 MCP 工具名称 |
PostToolUse |
工具名称 | 请参阅工具覆盖范围 |
PostCompact |
压缩触发方式 | 值为 manual 或 auto |
PreCompact |
压缩触发方式 | 值为 manual 或 auto |
PreToolUse |
工具名称 | 请参阅工具覆盖范围 |
SessionEnd |
结束原因 | 目前仅有 other |
SessionStart |
启动来源 | 值为 startup、resume、clear 和 compact |
SubagentStart |
子智能体类型 | 值取决于所启动的子智能体 |
SubagentStop |
子智能体类型 | 值取决于所停止的子智能体 |
UserPromptSubmit |
不支持 | 为此事件配置的任何 matcher 都会被忽略 |
Stop |
不支持 | 为此事件配置的任何 matcher 都会被忽略 |
*对于 apply_patch,matcher 值也可以使用 Edit 或 Write。
示例:
Bash^apply_patch$Edit|Writemcp__filesystem__read_filemcp__filesystem__.*startup|resume|clear|compactmanual|auto
工具覆盖范围
PreToolUse 和 PostToolUse 可以观察 shell 和 MCP 调用之外的操作。大多数
本地函数工具都使用相同的 hook 路径,因此你可以匹配其工具名称、
检查其 JSON 参数,并通过 PreToolUse 阻止或重写调用。
| 工具路径 | PreToolUse |
PostToolUse |
备注 |
|---|---|---|---|
| Shell 命令 | 是 | 是 | 匹配为 Bash。 |
统一执行(exec_command) |
是 | 是 | 匹配为 Bash。后续 write_stdin 轮询可在原始命令完成时传递其 PostToolUse。 |
apply_patch |
是 | 是 | 匹配为 apply_patch、Edit 或 Write。 |
| MCP 工具 | 是 | 是 | 匹配 MCP 工具名称,例如 mcp__filesystem__read_file。 |
| 其他本地函数工具 | 是 | 是 | 匹配函数工具名称,例如 update_plan。spawn_agent 也会匹配 Agent。 |
托管工具,例如 WebSearch |
否 | 否 | 这些工具不使用本地函数工具 hook 路径。 |
write_stdin 是现有统一执行会话的传输机制。它在发送输入或轮询
已通过 PreToolUse 的命令时,不会再次运行
PreToolUse。
某些专用工具路径可以选择不使用默认 hook 路径。请将工具 hooks 视为实用的防护机制,而不是完整的强制执行边界。
通用输入字段
每个命令 hook 都会在 stdin 上接收一个 JSON 对象。
以下是通常会用到的共享字段:
| 字段 | 类型 | 含义 |
|---|---|---|
session_id |
string |
当前 Codex 会话 ID。子智能体 hooks 使用父会话 ID。 |
transcript_path |
string | null |
会话转录文件的路径(如果有) |
cwd |
string |
会话的工作目录 |
hook_event_name |
string |
当前 hook 事件名称 |
model |
string |
Codex 专用扩展。活跃模型 slug |
轮次范围的 hooks 会在其事件专用表中将 turn_id 列为 Codex 专用扩展。
SessionStart、PreToolUse、PermissionRequest、PostToolUse、
UserPromptSubmit、SubagentStart、SubagentStop 和 Stop 还包含
permission_mode,它将当前权限模式描述为 default、
acceptEdits、plan、dontAsk 或 bypassPermissions。
为了方便使用,transcript_path 指向聊天转录,但
转录格式并非 hooks 的稳定接口,可能会随时间变化。
如需完整的线上格式,请参阅架构。
通用输出字段
SessionStart、PreCompact、PostCompact、UserPromptSubmit、
SubagentStop 和 Stop 支持以下共享 JSON 字段。SubagentStart
为 systemMessage 和 hook 专用上下文接受相同的结构,但
continue: false 不会停止子智能体:
{
"continue": true,
"stopReason": "optional",
"systemMessage": "optional",
"suppressOutput": false
}| 字段 | 效果 |
|---|---|
continue |
如果为 false,将该次 hook 运行标记为已停止 |
stopReason |
记录为停止原因 |
systemMessage |
在 UI 或事件流中显示为警告 |
suppressOutput |
目前会被解析,但尚未实现 |
退出码为 0 且没有输出时会被视为成功,Codex 将继续运行。
PreToolUse 和 PermissionRequest 支持 systemMessage,但目前这些事件不支持
continue、stopReason 和 suppressOutput。
如果 PreToolUse hook 返回其中一个不受支持的字段,Codex 会将
该次 hook 运行标记为失败、报告错误,然后继续工具调用。
PostToolUse 支持 systemMessage、continue: false 和 stopReason。
suppressOutput 会被解析,但目前不支持用于该事件。
大型 hook 输出
默认情况下,Codex 会将每条模型可见的 hook 输出消息限制在约
2,500 个 token。如果 hook 返回的内容更多,Codex 会将完整文本保存到
<temp_dir>/hook_outputs/<session_id>/<uuid>.txt 下,并向模型提供包含已保存文件路径的
首尾预览。此行为称为
溢出:Codex 将过大的输出存储到磁盘,并替换为
模型可见的较短预览。如果文件无法写入,模型仍会
收到截断后的预览。
对于任何返回 additionalContext 的命令 hook,可在处理程序上设置
additionalContextLimit,以自定义大致的 token
阈值:
{
"type": "command",
"command": "python3 ~/.codex/hooks/session_start.py",
"additionalContextLimit": 5000
}省略 additionalContextLimit 即可使用默认的 2500 token 阈值。使用
正整数可选择其他阈值,使用 0 则可将处理程序的
完整附加上下文直接传给模型。Codex 会单独评估每个
匹配的处理程序。对于无法生成附加
上下文的事件,Codex 会忽略 additionalContextLimit 并报告配置
警告。
该设置仅适用于 additionalContext。工具反馈和续接
提示仍使用默认限制。
由于过大的输出可能会写入磁盘,请避免在 hook 输出中返回密钥或 其他敏感数据。
在后台运行 hooks
默认情况下,Codex 会等待命令 hook 完成,再继续执行
触发它的操作。将 async 设为 true,即可在 Codex 继续运行的同时,
在后台运行命令 hook。
配置后台 hook
在 hooks.json 的命令处理程序中添加 "async": true:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python3 ~/.codex/hooks/post_tool_use.py",
"async": true,
"timeout": 120
}
]
}
]
}
}对于 config.toml 中的内联 hook,请设置 async = true:
[[hooks.PostToolUse]]
matcher = "Bash"
[[hooks.PostToolUse.hooks]]
type = "command"
command = "python3 ~/.codex/hooks/post_tool_use.py"
async = true
timeout = 120后台 hooks 使用与同步命令 hooks 相同的输入、匹配器、信任审查、超时和
大型输出处理方式。与
其他命令 hooks 一样,timeout 以秒为单位,默认为
600。
后台 hooks 的运行方式
后台 hook 完成后,Codex 会在对话中的下一个安全时机传递 受支持的信息性输出:
- 如果一轮对话正在进行,Codex 会等待当前模型请求和工具调用 完成,然后让该输出可供本轮中的下一次模型请求使用。
- 如果当前没有进行中的轮次,Codex 会等到下一个用户轮次。后台 hook 完成不会启动新的轮次。
请使用与同步 hook 相同的事件专用 JSON 输出。Codex 会将
additionalContext 添加到模型上下文,并将 systemMessage 显示为
警告。
限制
- Codex 在每个会话中最多并发运行八个后台 hooks。其他 hooks 会等待运行中的 hook 完成。
- 每次匹配的调用都会独立运行,后台 hooks 完成的 顺序可能与启动顺序不同。
- 会话结束时,Codex 会取消尚未完成的后台 hooks,并丢弃 尚未传递的输出。
SessionEndhooks 始终同步运行。
Hooks
SessionStart
此事件会将 matcher 应用于 source。
除通用输入字段外,还包含以下字段:
| 字段 | 类型 | 含义 |
|---|---|---|
source |
string |
会话的启动方式:startup、resume、clear 或 compact |
stdout 上的纯文本会被添加为额外的开发者上下文。
stdout 上的 JSON 支持通用输出字段以及以下
hook 专用结构:
{
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": "Load the workspace conventions before editing."
}
}其中的 additionalContext 文本会被添加为额外的开发者上下文。
Codex 压缩根会话后,匹配
source: "compact" 的 SessionStart hooks 会在下一次模型请求前运行。这也适用于
在一轮对话中途发生自动压缩的情况:Codex 会将 hook 的
附加上下文传递给紧随其后的续接过程,而不是等待
后续用户轮次。如果 hook 返回 continue: false,Codex 会结束该轮,
不再发送模型请求。
SessionEnd
SessionEnd 允许你在会话结束时运行命令,例如保存最终
备注或清理文件。它会在以下情况下为主线程运行:你归档或
删除仍处于打开状态的对话、Codex 正常关闭,或某个
对话已闲置 30 分钟且未在任何已连接的客户端中打开。
它不会为子智能体运行。
切换到其他对话或调用 thread/unsubscribe 不会立即结束
会话,因此不会立即运行 SessionEnd。Hook 在运行时
仍可读取会话转录。
此事件使用 matcher 筛选 reason。目前,reason 始终为 other。
你可以省略 matcher 或使用 other,以便在每个 SessionEnd 事件中运行。
除通用输入字段外,还包含以下字段:
| 字段 | 类型 | 含义 |
|---|---|---|
reason |
string |
会话结束的原因:other |
例如,SessionEnd 命令会收到:
{
"session_id": "thr_123",
"transcript_path": "/workspace/.codex/rollout.jsonl",
"cwd": "/workspace",
"hook_event_name": "SessionEnd",
"reason": "other"
}SessionEnd hooks 始终同步运行,即使 async 为 true。它们
仅提供建议,因此其输出不会引导 Codex,也不会让线程保持打开。如果
命令超时或以错误退出,Codex 会将其报告为 hook 失败。
SubagentStart
此事件会将 matcher 应用于 agent_type。
除通用输入字段外,还包含以下字段:
| 字段 | 类型 | 含义 |
|---|---|---|
turn_id |
string |
Codex 专用扩展。活跃的 Codex 轮次 ID |
agent_id |
string |
子智能体标识符 |
agent_type |
string |
子智能体类型或配置文件 |
permission_mode |
string |
当前权限模式 |
stdout 上的纯文本会被添加为子智能体的额外开发者上下文。
stdout 上的 JSON 支持 systemMessage 以及以下 hook 专用结构:
{
"hookSpecificOutput": {
"hookEventName": "SubagentStart",
"additionalContext": "Review the repository test conventions first."
}
}其中的 additionalContext 文本会被添加为子智能体的额外开发者上下文。
continue: false 会出于兼容性目的进行解析,但不会阻止
子智能体启动。
PreToolUse
PreToolUse 可以拦截 Bash、通过 apply_patch 执行的文件编辑、
MCP 工具调用以及其他本地函数工具。有关受支持的路径和例外情况,
请参阅工具覆盖范围。
matcher 会应用于 tool_name 及匹配器别名。对于通过
apply_patch 进行的文件编辑,matcher 值可以使用 apply_patch、Edit 或 Write;hook 输入
仍会报告 tool_name: "apply_patch"。
除通用输入字段外,还包含以下字段:
| 字段 | 类型 | 含义 |
|---|---|---|
turn_id |
string |
Codex 专用扩展。活跃的 Codex 轮次 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 和其他本地函数工具会发送其参数。 |
stdout 上的纯文本会被忽略。
stdout 上的 JSON 可以使用 systemMessage。要拒绝受支持的工具调用,请返回
以下 hook 专用结构:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Destructive command blocked by hook."
}
}Codex 也接受以下旧版阻止结构:
{
"decision": "block",
"reason": "Destructive command blocked by hook."
}你也可以使用退出码 2,并将阻止原因写入 stderr。
要在不阻止操作的情况下添加模型可见的上下文,请返回
hookSpecificOutput.additionalContext:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"additionalContext": "The pending command touches generated files."
}
}要在不阻止操作的情况下重写受支持的工具调用,请返回
包含 updatedInput 的 permissionDecision: "allow":
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"updatedInput": {
"command": "echo rewritten"
}
}
}对于 Bash 命令和 apply_patch,updatedInput 必须包含字符串
command 字段。对于 MCP 和其他本地函数工具,updatedInput 是
替代参数对象。仅将 updatedInput 与
permissionDecision: "allow" 一同返回;其他 updatedInput 结构会被报告为
错误。
permissionDecision: "ask"、旧版 decision: "approve"、continue: false、
stopReason 和 suppressOutput 会被解析,但尚不受支持。Codex 会将
该次 hook 运行标记为失败、报告错误,然后继续工具调用。
PermissionRequest
当 Codex 即将请求批准时(例如 shell 权限提升或托管网络批准),
PermissionRequest 会运行。它可以允许请求、拒绝请求,
也可以不作决定,让正常的批准提示继续出现。
对于无需批准的命令,它不会运行。
matcher 会应用于 tool_name 及匹配器别名。目前的规范
值包括 Bash、apply_patch 和 mcp__server__tool 等 MCP 工具名称;
apply_patch 也会匹配 Edit 和 Write。
除通用输入字段外,还包含以下字段:
| 字段 | 类型 | 含义 |
|---|---|---|
turn_id |
string |
Codex 专用扩展。活跃的 Codex 轮次 ID |
tool_name |
string |
规范 hook 工具名称,例如 Bash、apply_patch,或 mcp__fs__read 之类的 MCP 名称 |
tool_input |
JSON value |
工具专用输入。Bash 和 apply_patch 使用 tool_input.command,而 MCP 工具会发送所有参数。 |
tool_input.description |
string | null |
供人阅读的批准原因(如果 Codex 提供) |
stdout 上的纯文本会被忽略。
某些工具输入可能包含供人阅读的描述,但不要假定
每种工具都有 tool_input.description 字段。
要批准请求,请返回:
{
"hookSpecificOutput": {
"hookEventName": "PermissionRequest",
"decision": {
"behavior": "allow"
}
}
}要拒绝请求,请返回:
{
"hookSpecificOutput": {
"hookEventName": "PermissionRequest",
"decision": {
"behavior": "deny",
"message": "Blocked by repository policy."
}
}
}如果多个匹配 hooks 返回决定,则任何 deny 都优先。否则,
allow 会让请求继续,而不显示批准提示。如果没有
匹配的 hook 作出决定,Codex 会使用正常的批准流程。
请勿为 PermissionRequest 返回 updatedInput、updatedPermissions 或 interrupt;
这些字段为未来行为预留,目前会以封闭方式失败。
PostToolUse
PostToolUse 会在受支持的工具产生输出后运行,包括 Bash、
apply_patch、MCP 工具调用及其他本地函数工具。对于 Bash,
它也会在命令以非零状态退出后运行。它无法撤销
已运行工具产生的副作用。有关受支持的路径和例外情况,
请参阅工具覆盖范围。
matcher 会应用于 tool_name 及匹配器别名。对于通过
apply_patch 进行的文件编辑,matcher 值可以使用 apply_patch、Edit 或 Write;hook 输入
仍会报告 tool_name: "apply_patch"。
除通用输入字段外,还包含以下字段:
| 字段 | 类型 | 含义 |
|---|---|---|
turn_id |
string |
Codex 专用扩展。活跃的 Codex 轮次 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 和其他本地函数工具会发送其参数。 |
tool_response |
JSON value |
工具专用输出。MCP 工具会发送 MCP 调用结果。其他本地函数工具通常会发送面向模型的输出。 |
stdout 上的纯文本会被忽略。
stdout 上的 JSON 可以使用 systemMessage 及以下 hook 专用结构:
{
"decision": "block",
"reason": "The Bash output needs review before continuing.",
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"additionalContext": "The command updated generated files."
}
}其中的 additionalContext 文本会被添加为额外的开发者上下文。
对于此事件,decision: "block" 不会撤销已完成的 Bash 命令。
相反,Codex 会记录反馈、用该反馈替换工具结果,
并从 hook 提供的消息处继续运行模型。
你也可以使用退出码 2,并将反馈原因写入 stderr。
要在命令已经运行后停止对原始工具结果的正常处理,
请返回 continue: false。Codex 会用你的反馈或停止文本替换工具结果,
然后从此处继续。
updatedMCPToolOutput 和 suppressOutput 会被解析,但尚不受支持。
Codex 会将该次 hook 运行标记为失败、报告错误,然后继续
正常处理工具结果。
代码模式中的工具调用
当模型在代码模式下通过 JavaScript 调用工具时,hook 决定会应用于
该嵌套调用。PreToolUse 可以在工具运行前将其停止或重写
其输入。具有阻止作用的 PostToolUse 无法撤销工具的副作用,但可以
阻止原始结果到达正在运行的脚本。
| Hook 结果 | 代码模式看到的内容 |
|---|---|
PreToolUse 阻止 |
工具 promise 会在工具运行前被拒绝。 |
PreToolUse 返回 updatedInput |
工具使用重写后的输入运行,promise 以该结果兑现。 |
PostToolUse 返回 decision: "block" 或以代码 2 退出 |
工具先运行,然后 promise 会以 hook 原因为由被拒绝。 |
PostToolUse 返回 continue: false |
Codex 将 hook 反馈用作模型可见结果,但不会拒绝嵌套工具 promise。 |
PreCompact
PreCompact 会在 Codex 压缩聊天前运行。matcher 会应用于
trigger,其值为 manual 和 auto。
除通用输入字段外,还包含以下字段:
| 字段 | 类型 | 含义 |
|---|---|---|
turn_id |
string |
Codex 专用扩展。活跃的 Codex 轮次 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 轮次 ID |
trigger |
string |
压缩的触发方式:manual 或 auto |
stdout 上的纯文本会被忽略。
stdout 上的 JSON 支持通用输出字段。如果匹配的
PostCompact hook 返回 continue: false,Codex 会在压缩后停止。
UserPromptSubmit
此事件目前不使用 matcher。
除通用输入字段外,还包含以下字段:
| 字段 | 类型 | 含义 |
|---|---|---|
turn_id |
string |
Codex 专用扩展。活跃的 Codex 轮次 ID |
prompt |
string |
即将发送的用户提示词 |
stdout 上的纯文本会被添加为额外的开发者上下文。
stdout 上的 JSON 支持通用输出字段以及
以下 hook 专用结构:
{
"hookSpecificOutput": {
"hookEventName": "UserPromptSubmit",
"additionalContext": "Ask for a clearer reproduction before editing files."
}
}其中的 additionalContext 文本会被添加为额外的开发者上下文。
要阻止提示词,请返回:
{
"decision": "block",
"reason": "Ask for confirmation before doing that."
}你也可以使用退出码 2,并将阻止原因写入 stderr。
SubagentStop
此事件会将 matcher 应用于 agent_type。
除通用输入字段外,还包含以下字段:
| 字段 | 类型 | 含义 |
|---|---|---|
turn_id |
string |
Codex 专用扩展。当前 Codex 轮次 ID |
agent_id |
string |
子智能体的标识符 |
agent_type |
string |
子智能体类型或配置档案 |
agent_transcript_path |
string | null |
子智能体记录文件的路径(如有) |
stop_hook_active |
boolean |
此子智能体是否已继续运行 |
last_assistant_message |
string | null |
最新的子智能体助手消息(如有) |
SubagentStop 退出 0 时,要求在 stdout 上输出 JSON。对于此事件,
纯文本输出无效。
stdout 上的 JSON 支持通用输出字段。若要让
Codex 继续子智能体流程,请返回:
{
"decision": "block",
"reason": "Run one more focused pass inside the subagent."
}你也可以使用退出代码 2,并将继续运行的原因写入 stderr。
如果任一匹配的 SubagentStop 钩子返回 continue: false,其优先级将
高于其他匹配的 SubagentStop
钩子所作的继续运行决定。
Stop
matcher 目前不用于此事件。
除通用输入字段之外的字段:
| 字段 | 类型 | 含义 |
|---|---|---|
turn_id |
string |
Codex 专用扩展。当前 Codex 轮次 ID |
stop_hook_active |
boolean |
此轮次是否已由 Stop 继续运行 |
last_assistant_message |
string | null |
最新的助手消息文本(如有) |
Stop 退出 0 时,要求在 stdout 上输出 JSON。对于此事件,纯文本输出
无效。
stdout 上的 JSON 支持通用输出字段。若要让
Codex 继续运行,请返回:
{
"decision": "block",
"reason": "Run one more pass over the failing tests."
}你也可以使用退出代码 2,并将继续运行的原因写入 stderr。
对于此事件,decision: "block" 不会拒绝该轮次。相反,它会指示
Codex 继续运行,并自动创建一个新的继续提示词;该提示词将作为
新的用户提示词,并以你的 reason 作为提示词文本。
如果任一匹配的 Stop 钩子返回 continue: false,其优先级将高于
其他匹配的 Stop 钩子所作的继续运行决定。
架构
如果需要当前准确的线上格式,请参阅 Codex GitHub 仓库中生成的架构。
纯文本别名
- string | null