主导航

钩子

在 Codex 生命周期中运行确定性脚本

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

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

钩子默认开启。如果需要在 config.toml 中将其关闭,请设置

[features]
hooks = false

使用 hooks 作为规范的功能键。codex_hooks 仍可作为已弃用的别名使用。

管理员可以在 requirements.toml 中通过 [features].hooks = false 强制关闭钩子。

需要注意的运行时行为

  • 来自多个文件的匹配钩子都会运行。
  • 针对同一事件的多个匹配命令钩子会并发启动,因此一个钩子无法阻止另一个匹配钩子启动。
  • 非受管命令钩子在运行前必须经过审查和信任。
  • PreToolUsePermissionRequestPostToolUsePreCompactPostCompactUserPromptSubmitSubagentStopStop 在轮次范围内运行。SessionStartSubagentStart 在线程或子智能体启动范围内运行。

Codex 查找钩子的位置

Codex 在以下任一形式的活动配置层旁边发现钩子

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

已安装的插件也可以通过其插件清单或默认的 hooks/hooks.json 文件捆绑生命周期配置。请参阅 构建插件 以了解插件打包规则。

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

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

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

Codex 还可以发现随已启用插件捆绑的钩子。插件捆绑的钩子会与其他钩子源一起加载,并使用与其它非受管钩子相同的信任审查流程。

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

审查与信任钩子

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

在 CLI 中使用 /hooks 来检查钩子源、审查新钩子或修改过的钩子、信任钩子,或禁用单个非受管钩子。如果钩子在启动时需要审查,Codex 会打印警告,提示你打开 /hooks

来自系统、MDM、云端或 requirements.toml 源的受管钩子会被标记为“已受管”,由策略默认信任,且无法在用户钩子浏览器中禁用。

对于已在 Codex 之外验证过钩子源的一次性自动化任务,可以传递 --dangerously-bypass-hook-trust 参数,以在无需持久化钩子信任的情况下运行已启用的钩子。

配置结构

钩子分为三个层级组织

  • 钩子事件,如 PreToolUsePostToolUsePreCompactSubagentStartStop
  • 决定该事件何时匹配的匹配器组
  • 当匹配器组匹配时运行的一个或多个钩子处理程序
{
  "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 作为工作目录运行。
  • 对于仓库本地钩子,建议使用从 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"

来自 requirements.toml 的受管钩子

企业受管要求也可以在 [hooks] 下定义内联钩子。这在管理员希望通过 MDM 或其他设备管理系统分发实际脚本时强制执行钩子配置非常有用。要即使用户在本地禁用了钩子仍强制执行受管钩子,可在 requirements.toml 中与 [hooks] 并列设置 [features].hooks = true。若要忽略用户、项目、会话和插件钩子,但仍允许管理员受管钩子,请设置 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 条目覆盖该默认值。清单条目可以是 ./ 前缀的路径、./ 前缀路径的数组、内联钩子对象,或内联钩子对象的数组。

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

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

插件钩子命令会接收以下环境变量

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

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

匹配器模式

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

目前仅部分 Codex 事件支持 matcher

事件matcher 过滤的内容备注
PermissionRequest工具名称支持包括 Bashapply_patch* 以及 MCP 工具名称
PostToolUse工具名称支持包括 Bashapply_patch* 以及 MCP 工具名称
PostCompact压缩触发器值为 manual (手动) 或 auto (自动)
PreCompact压缩触发器值为 manual (手动) 或 auto (自动)
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_pathstring | null会话转录文件的路径(如有)
cwdstring会话的工作目录
hook_event_namestring当前钩子事件名称
modelstringCodex 特定扩展。当前活动模型名称

轮次范围内的钩子在其事件特定表中将 turn_id 列为 Codex 特定扩展。

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

transcript_path 为了方便指向对话转录,但转录格式对于钩子而言并非稳定接口,可能会随时间改变。

如果需要完整的传输格式,请参阅 架构 (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 已解析但目前不支持该事件。

钩子

SessionStart

matcher 应用于此事件的 source

公共输入字段 之外的字段

字段类型含义
sourcestring会话启动方式:startupresumeclearcompact

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

stdout 上的 JSON 支持 公共输出字段 以及此钩子特定的形状

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

additionalContext 文本会作为额外的开发者上下文添加。

SubagentStart

matcher 应用于此事件的 agent_type

公共输入字段 之外的字段

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

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

stdout 上的 JSON 支持 systemMessage 以及此钩子特定的形状

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

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

PreToolUse

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 值工具特定的输入。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

PermissionRequest 在 Codex 即将请求批准时运行,例如 shell 提权或受管网络批准。它可以允许请求、拒绝请求,或不作决定并让正常的批准提示继续。它不会针对不需要批准的命令运行。

matcher 应用于 tool_name 和匹配器别名。当前的规范值包括 Bashapply_patch 和 MCP 工具名称,如 mcp__server__toolapply_patch 也匹配 EditWrite

公共输入字段 之外的字段

字段类型含义
turn_idstringCodex 特定扩展。当前活动 Codex 轮次 ID
tool_namestring规范的钩子工具名称,例如 Bashapply_patch,或像 mcp__fs__read 这样的 MCP 名称
tool_inputJSON 值工具特定输入。Bashapply_patch 使用 tool_input.command,而 MCP 工具发送所有参数。
tool_input.descriptionstring | null人类可读的批准原因(如有)

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

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 值工具特定的输入。Bashapply_patch 使用 tool_input.command,而 MCP 工具发送所有参数。
tool_responseJSON 值工具特定的输出。对于 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

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

公共输入字段 之外的字段

字段类型含义
turn_idstringCodex 特定扩展。当前活动 Codex 轮次 ID
triggerstring压缩触发原因:manualauto

stdout 上的纯文本将被忽略。

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

PostCompact

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

公共输入字段 之外的字段

字段类型含义
turn_idstringCodex 特定扩展。当前活动 Codex 轮次 ID
triggerstring压缩触发原因:manualauto

stdout 上的纯文本将被忽略。

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

UserPromptSubmit

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

SubagentStop

matcher 应用于此事件的 agent_type

公共输入字段 之外的字段

字段类型含义
turn_idstringCodex 特定扩展。当前活动 Codex 轮次 ID
agent_idstring子智能体的标识符
agent_typestring子智能体类型或配置概要
agent_transcript_pathstring | null子智能体转录文件的路径(如有)
stop_hook_activeboolean该子智能体是否已继续运行
last_assistant_messagestring | 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_idstringCodex 特定扩展。当前活动 Codex 轮次 ID
stop_hook_activeboolean该轮次是否已通过 Stop 继续
last_assistant_messagestring | 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 钩子的继续决策。

架构 (Schemas)

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

如果需要确切的当前传输格式,请参阅 Codex GitHub 仓库 中生成的架构。

© . This website operates independently and is not affiliated with or endorsed by OpenAI, Inc. All brand names, logos, and trademarks are the property of their respective owners.