Skip to content

在 Codex 生命周期中运行确定性脚本,实现自定义日志、安全扫描和验证。

钩子是 Codex 的一个可扩展性框架。它们允许您将自己的脚本注入到智能体循环(agentic loop)中,从而实现以下功能:

  • 将对话发送到自定义的日志/分析引擎
  • 扫描团队的提示词以阻止意外粘贴 API 密钥
  • 自动总结对话以创建持久记忆
  • 在对话轮次结束时运行自定义验证检查,以强制执行标准
  • 在特定目录下自定义提示词(prompting)

钩子默认处于启用状态。如果您需要在 config.toml 中关闭它们,请设置:

[features]
hooks = false

请使用 hooks 作为规范的功能键名(feature key)。codex_hooks 仍可作为已弃用的别名使用。

管理员可以通过相同的方式在 requirements.toml 中使用 [features].hooks = false 来强制关闭钩子。

需要注意的运行时行为:

  • 匹配自多个文件的钩子都会运行。
  • 针对同一事件的多个匹配的命令钩子是并发启动的,因此一个钩子无法阻止另一个匹配的钩子启动。
  • 非托管的(Non-managed)命令钩子在运行前必须经过审查和信任。
  • PreToolUsePermissionRequestPostToolUsePreCompactPostCompactUserPromptSubmitSubagentStopStop 在轮次作用域(turn scope)运行。SessionStartSubagentStart 在线程或子智能体启动作用域(thread or subagent-start scope)运行。

Codex 会在活动配置层相邻的位置寻找以下任一形式的钩子:

  • hooks.json
  • config.toml 内部的内联 [hooks]

已安装的插件也可以通过其插件清单(manifest)或默认的 hooks/hooks.json 文件来打包生命周期配置。有关插件打包规则,请参见 构建插件

在实践中,四个最实用的位置是:

  • ~/.codex/hooks.json
  • ~/.codex/config.toml
  • <repo>/.codex/hooks.json
  • <repo>/.codex/config.toml

如果存在多个钩子源,Codex 会加载所有匹配的钩子。高优先级的配置层不会替换低优先级的钩子。如果单个层同时包含 hooks.json 和内联 [hooks],Codex 会在启动时将它们合并并发出警告。建议每层只使用一种表示形式。

Codex 还可以发现与启用的插件打包在一起的钩子。插件自带的钩子会与其他钩子源一起加载,并使用与其他非托管钩子相同的信任审查流程。

项目本地(Project-local)的钩子仅在项目 .codex/ 层受到信任时才会加载。在不受信任的项目中,Codex 仍会从各自的活动配置层加载用户和系统钩子。

Codex 会在决定哪些钩子可以运行之前列出所有已配置的钩子。在非托管命令钩子运行之前,Codex 要求您审查并信任该钩子的精确定义。Codex 会针对钩子当前的哈希值记录信任状态,因此新钩子或更改过的钩子将被标记为待审查并在获得信任前被跳过。

在 CLI 中使用 /hooks 可以检查钩子源、审查新钩子或更改过的钩子、信任钩子,或者禁用单个非托管钩子。如果启动时有钩子需要审查,Codex 会打印一条警告,提示您打开 /hooks

来自系统、MDM、云端或 requirements.toml 源的托管钩子(Managed hooks)会被标记为已托管,根据策略自动信任,且无法从用户钩子浏览器中禁用。

对于已经在 Codex 外部对钩子源进行了审查的一次性自动化操作,可以传递 --dangerously-bypass-hook-trust 参数,从而在不需要为该次调用持久化钩子信任的情况下运行已启用的钩子。

钩子被组织在三个层级:

  • 一个钩子事件(hook event),例如 PreToolUsePostToolUsePreCompactSubagentStartStop
  • 一个决定该事件何时匹配的匹配器组(matcher group)
  • 当匹配器组匹配时运行的一个或多个钩子处理器(hook handlers)
{
"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 的命令覆盖。在 TOML 中,使用 command_windowscommandWindows
  • async 会被解析,但目前尚不支持异步命令钩子。Codex 会跳过带有 async: true 的处理器。
  • 目前只有 type: "command" 的处理器会运行。promptagent 处理器会被解析但会被跳过。
  • 命令运行时,会将会话的 cwd 作为其工作目录。
  • 对于存储库本地(repo-local)的钩子,建议从 git 根目录开始解析路径,而不是使用类似于 .codex/hooks/... 的相对路径。因为 Codex 可能会从子目录启动,基于 git 根目录的路径可以保持钩子位置的稳定。

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 = "Reviewing Bash output"

企业托管的需求也可以在 [hooks] 下内联定义钩子。当管理员想要强制执行钩子配置,同时通过 MDM 或其他设备管理系统分发实际脚本时,这非常有用。为了对在本地禁用了钩子的用户也强制执行托管钩子,请在 requirements.toml 中将 [features].hooks = true[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_dir 用于 macOS 和 Linux。
  • windows_managed_dir 用于 Windows。
  • Codex 不会分发 managed_dir 中的脚本;您的企业工具必须单独安装和更新它们。
  • 托管钩子命令应当使用配置的托管目录下的绝对脚本路径。
  • allow_managed_hooks_only = true 会跳过来自用户、项目、会话和插件源的钩子,但仍会加载来自 requirements.toml 和其他托管配置层的托管钩子。

当启用某个插件时,Codex 可以从该插件加载生命周期钩子,与用户、项目和托管钩子并列运行。

默认情况下,Codex 会在插件根目录下寻找 hooks/hooks.json。插件清单可以通过 .codex-plugin/plugin.json 中的 hooks 条目来覆盖该默认值。该清单条目可以是一个以 ./ 开头的路径、一个以 ./ 开头的路径数组、一个内联的 hooks 对象或一个内联 hooks 对象的数组。

{
"name": "repo-policy",
"hooks": "./hooks/hooks.json"
}

清单中的钩子路径是相对于插件根目录进行解析的,并且必须保持在该根目录内部。如果清单定义了 hooks,Codex 将使用这些清单条目,而不是默认的 hooks/hooks.json

插件钩子命令会接收到这些环境变量:

  • PLUGIN_ROOT 是一个 Codex 特有的扩展,指向已安装插件的根目录。
  • PLUGIN_DATA 是一个 Codex 特有的扩展,指向插件的可写数据目录。
  • Codex 还会设置 CLAUDE_PLUGIN_ROOTCLAUDE_PLUGIN_DATA,以保持与现有插件钩子的兼容性。

插件钩子使用与其他钩子相同的事件 Schema。安装或启用插件并不会自动信任其钩子;Codex 会跳过插件绑定的钩子,直到您审查并信任当前的钩子定义。

matcher 字段是一个正则表达式字符串,用于过滤钩子的触发时机。使用 "*""" 或完全省略 matcher 可以匹配支持的事件的每一次发生。

目前只有部分 Codex 事件支持 matcher

事件matcher 过滤的内容注意事项
PermissionRequest工具名称支持包括 Bashapply_patch* 以及 MCP 工具名称
PostToolUse工具名称支持包括 Bashapply_patch* 以及 MCP 工具名称
PostCompact压缩触发器取值为 manualauto
PreCompact压缩触发器取值为 manualauto
PreToolUse工具名称支持包括 Bashapply_patch* 以及 MCP 工具名称
SessionStart启动源取值为 startupresumeclearcompact
SubagentStart子智能体类型取值取决于启动的子智能体
SubagentStop子智能体类型取值取决于停止的子智能体
UserPromptSubmit不支持此事件将忽略任何已配置的 matcher
Stop不支持此事件将忽略任何已配置的 matcher

*对于 apply_patchmatcher 的值也可以使用 EditWrite

示例:

  • Bash
  • ^apply_patch$
  • Edit|Write
  • mcp__filesystem__read_file
  • mcp__filesystem__.*
  • startup|resume|clear|compact
  • manual|auto

每个命令钩子都会在 stdin 上接收到一个 JSON 对象。

以下是您通常会使用的共享字段:

字段类型含义
session_idstring当前 Codex 会话 ID。子智能体钩子使用父会话 ID。
transcript_path`stringnull`
cwdstring会话的工作目录
hook_event_namestring当前钩子事件的名称
modelstringCodex 特有扩展。当前活动的模型标识(slug)

轮次作用域(Turn-scoped)的钩子会在其特有事件表中将 turn_id 列为 Codex 特有扩展。

SessionStartPreToolUsePermissionRequestPostToolUseUserPromptSubmitSubagentStartSubagentStopStop 还包含 permission_mode,它将当前权限模式描述为 defaultacceptEditsplandontAskbypassPermissions

为了方便起见,transcript_path 指向了对话历史记录,但历史记录格式对于钩子来说并不是一个稳定的接口,可能会随着时间的推移而改变。

如果您需要完整的传输格式(wire format),请参见 Schemas

SessionStartPreCompactPostCompactUserPromptSubmitSubagentStopStop 支持这些共享的 JSON 字段。SubagentStartsystemMessage 和钩子特有上下文接受相同的结构,但 continue: false 不会停止子智能体:

{
"continue": true,
"stopReason": "optional",
"systemMessage": "optional",
"suppressOutput": false
}
字段效果
continue如果为 false,则将该钩子运行标记为已停止
stopReason记录为停止的原因
systemMessage在 UI 或事件流中作为警告呈现
suppressOutput目前已被解析,但尚未实现

退出码为 0 且无输出被视为成功,Codex 将继续运行。

PreToolUsePermissionRequest 支持 systemMessage,但这些事件目前不支持 continuestopReasonsuppressOutput。如果 PreToolUse 钩子返回了这些不支持的字段之一,Codex 会将该钩子运行标记为失败,报告错误,并继续执行工具调用。

PostToolUse 支持 systemMessagecontinue: falsestopReasonsuppressOutput 会被解析,但目前该事件不支持它。

在此事件中,matcher 应用于 source

通用输入字段 之外的字段:

字段类型含义
sourcestring会话是如何启动的:startupresumeclearcompact

stdout 上的纯文本将被添加为额外的开发者上下文。

stdout 上的 JSON 支持 通用输出字段 以及以下钩子特有结构:

{
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": "Load the workspace conventions before editing."
}
}

additionalContext 文本将被添加为额外的开发者上下文。

在此事件中,matcher 应用于 agent_type

通用输入字段 之外的字段:

字段类型含义
turn_idstringCodex 特有扩展。当前活动的 Codex 轮次 ID
agent_idstring子智能体的标识符
agent_typestring子智能体类型或配置文件(profile)
permission_modestring当前权限模式

stdout 上的纯文本将被添加为子智能体的额外开发者上下文。

stdout 上的 JSON 支持 systemMessage 以及以下钩子特有结构:

{
"hookSpecificOutput": {
"hookEventName": "SubagentStart",
"additionalContext": "Review the repository test conventions first."
}
}

additionalContext 文本将被添加为子智能体的额外开发者上下文。continue: false 会为了兼容性而被解析,但它不会阻止子智能体启动。

PreToolUse 可以拦截 Bash、通过 apply_patch 执行的文件编辑以及 MCP 工具调用。由于 Codex 通常可以通过另一个支持的工具路径执行等价的工作,因此它仍然是一个防护栏,而不是一个完全的强制执行边界。

这目前还没有拦截所有的 shell 调用,只拦截了简单的调用。较新的 unified_exec 机制允许对 shell 进行更丰富的流式 stdin/stdout 处理,但拦截仍不完整。同样,这不会拦截 WebSearch 或其他非 shell、非 MCP 的工具调用。

matcher 应用于 tool_name 和匹配器别名。对于通过 apply_patch 进行的文件编辑,matcher 的值可以使用 apply_patchEditWrite;钩子输入仍然报告 tool_name: "apply_patch"

通用输入字段 之外的字段:

字段类型含义
turn_idstringCodex 特有扩展。当前活动的 Codex 轮次 ID
tool_namestring规范的钩子工具名称,例如 Bashapply_patch,或者类似 mcp__fs__read 的 MCP 名称
tool_use_idstring本次调用的工具调用 ID
tool_inputJSON value工具特有的输入。Bashapply_patch 使用 tool_input.command,而 MCP 工具则发送所有参数。

stdout 上的纯文本将被忽略。

stdout 上的 JSON 可以使用 systemMessage。若要拒绝受支持的工具调用,请返回以下钩子特有结构:

{
"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."
}
}

若要在不阻止的情况下重写受支持的工具调用,请返回带有 updatedInputpermissionDecision: "allow"

{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"updatedInput": {
"command": "echo rewritten"
}
}
}

对于 Bash 命令和 apply_patchupdatedInput 必须包含一个字符串类型的 command 字段。对于 MCP 工具,updatedInput 是替换后的参数对象。请仅在 permissionDecision: "allow" 时返回 updatedInput;其他 updatedInput 结构将被报告为错误。

permissionDecision: "ask"、旧版 decision: "approve"continue: falsestopReasonsuppressOutput 会被解析,但目前尚不支持。Codex 会将钩子运行标记为失败,报告错误,并继续执行工具调用。

PermissionRequest 在 Codex 即效问审批时运行,例如 shell 提权或托管网络审批。它可以允许请求、拒绝请求,或者放弃决策并让正常的审批提示继续进行。对于不需要审批的命令,它不会运行。

matcher 应用于 tool_name 和匹配器别名。当前规范值包括 Bashapply_patch 以及类似 mcp__server__tool 的 MCP 工具名称;apply_patch 也能匹配 EditWrite

通用输入字段 之外的字段:

字段类型含义
turn_idstringCodex 特有扩展。当前活动的 Codex 轮次 ID
tool_namestring规范的钩子工具名称,例如 Bashapply_patch,或者类似 mcp__fs__read 的 MCP 名称
tool_inputJSON value工具特有的输入。Bashapply_patch 使用 tool_input.command,而 MCP 工具则发送所有参数。
tool_input.description`stringnull`

stdout 上的纯文本将被忽略。

某些工具输入可能包含人类可读的描述,但请勿依赖每个工具都具有 tool_input.description 字段。

若要批准请求,请返回:

{
"hookSpecificOutput": {
"hookEventName": "PermissionRequest",
"decision": {
"behavior": "allow"
}
}
}

若要拒绝请求,请返回:

{
"hookSpecificOutput": {
"hookEventName": "PermissionRequest",
"decision": {
"behavior": "deny",
"message": "Blocked by repository policy."
}
}
}

如果多个匹配的钩子都返回了决策,任何 deny 都会胜出。否则,allow 将允许请求继续进行,而不会呈现审批提示。如果没有匹配的钩子做出决定,Codex 将使用正常的审批流程。

请勿为 PermissionRequest 返回 updatedInputupdatedPermissionsinterrupt;这些字段保留供未来行为使用,目前会导致失败并关闭。

PostToolUse 在受支持的工具产生输出后运行,包括 Bash、apply_patch 和 MCP 工具调用。对于 Bash,它也会在以非零状态退出的命令之后运行。它无法撤销已经运行的工具所产生的副作用。

这目前还没有拦截所有的 shell 调用,只拦截了简单的调用。较新的 unified_exec 机制允许对 shell 进行更丰富的流式 stdin/stdout 处理,但拦截仍不完整。同样,这不会拦截 WebSearch 或其他非 shell、非 MCP 的工具调用。

matcher 应用于 tool_name 和匹配器别名。对于通过 apply_patch 进行的文件编辑,matcher 的值可以使用 apply_patchEditWrite;钩子输入仍然报告 tool_name: "apply_patch"

通用输入字段 之外的字段:

字段类型含义
turn_idstringCodex 特有扩展。当前活动的 Codex 轮次 ID
tool_namestring规范的钩子工具名称,例如 Bashapply_patch,或者类似 mcp__fs__read 的 MCP 名称
tool_use_idstring本次调用的工具调用 ID
tool_inputJSON value工具特有的输入。Bashapply_patch 使用 tool_input.command,而 MCP 工具则发送所有参数。
tool_responseJSON value工具特有的输出。对于 MCP 工具,这是 MCP 调用结果。

stdout 上的纯文本将被忽略。

stdout 上的 JSON 可以使用 systemMessage 以及以下钩子特有结构:

{
"decision": "block",
"reason": "The Bash output needs review before continuing.",
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"additionalContext": "The command updated generated files."
}
}

additionalContext 文本将被添加为额外的开发者上下文。

对于此事件,decision: "block" 并不会撤销已完成的 Bash 命令。相反,Codex 会记录反馈,用该反馈替换工具结果,并从钩子提供的消息中继续运行模型。

您也可以使用退出码 2 并将反馈原因写入 stderr

若要在命令运行后停止对原始工具结果的正常处理,请返回 continue: false。Codex 将用您的反馈或停止文本替换工具结果,并从那里继续。

updatedMCPToolOutputsuppressOutput 会被解析,但目前尚不支持。Codex 会将钩子运行标记为失败,报告错误,并继续对工具结果进行正常处理。

PreCompact 在 Codex 压缩对话之前运行。matcher 应用于 trigger,其取值为 manualauto

通用输入字段 之外的字段:

字段类型含义
turn_idstringCodex 特有扩展。当前活动的 Codex 轮次 ID
triggerstring是什么触发了压缩:manualauto

stdout 上的纯文本将被忽略。

stdout 上的 JSON 支持 通用输出字段。如果匹配的 PreCompact 钩子返回 continue: false,Codex 将在压缩前停止。

PostCompact 在 Codex 压缩对话之后运行。matcher 应用于 trigger,其取值为 manualauto

通用输入字段 之外的字段:

字段类型含义
turn_idstringCodex 特有扩展。当前活动的 Codex 轮次 ID
triggerstring是什么触发了压缩:manualauto

stdout 上的纯文本将被忽略。

stdout 上的 JSON 支持 通用输出字段。如果匹配的 PostCompact 钩子返回 continue: false,Codex 将在压缩后停止。

目前此事件不使用 matcher

通用输入字段 之外的字段:

字段类型含义
turn_idstringCodex 特有扩展。当前活动的 Codex 轮次 ID
promptstring即将发送的用户提示词

stdout 上的纯文本将被添加为额外的开发者上下文。

stdout 上的 JSON 支持 通用输出字段 以及以下钩子特有结构:

{
"hookSpecificOutput": {
"hookEventName": "UserPromptSubmit",
"additionalContext": "Ask for a clearer reproduction before editing files."
}
}

additionalContext 文本将被添加为额外的开发者上下文。

若要阻止提示词,请返回:

{
"decision": "block",
"reason": "Ask for confirmation before doing that."
}

您也可以使用退出码 2 并将阻止原因写入 stderr

在此事件中,matcher 应用于 agent_type

通用输入字段 之外的字段:

字段类型含义
turn_idstringCodex 特有扩展。当前活动的 Codex 轮次 ID
agent_idstring子智能体的标识符
agent_typestring子智能体类型或配置文件
agent_transcript_path`stringnull`
stop_hook_activeboolean该子智能体流程之前是否已经继续过
last_assistant_message`stringnull`

SubagentStop 期望在退出码为 0 时在 stdout 上输出 JSON。纯文本输出对于此事件是无效的。

stdout 上的 JSON 支持 通用输出字段。若要请求 Codex 继续子智能体流程,请返回:

{
"decision": "block",
"reason": "Run one more focused pass inside the subagent."
}

您也可以使用退出码 2 并将继续原因写入 stderr

如果任何匹配的 SubagentStop 钩子返回 continue: false,则该结果优先于其他匹配的 SubagentStop 钩子的继续运行决定。

目前此事件不使用 matcher

通用输入字段 之外的字段:

字段类型含义
turn_idstringCodex 特有扩展。当前活动的 Codex 轮次 ID
stop_hook_activeboolean该轮次之前是否已经由 Stop 继续过
last_assistant_message`stringnull`

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 钩子的继续运行决定。

链接的 main 分支 schema 可能包含当前发布版本中不存在的钩子字段。请将本页面作为发布版本行为的参考。

如果您需要确切的当前传输格式,请参见 Codex GitHub 存储库 中生成的 Schema。

-
0:000:00