OpenAI Codex 中文文档(4.14更新)
联系我
官方文档

钩子

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

钩子是 Codex 的一套扩展框架。它允许你把自己的脚本插入智能体循环中,从而实现例如以下能力:

  • 把对话发送到自定义日志或分析系统
  • 扫描团队提示词,阻止误粘贴 API key
  • 自动总结对话,生成持久记忆
  • 在回合结束时运行自定义校验器,强制执行团队标准
  • 当工作目录匹配特定路径时,动态调整提示词策略

钩子受 config.toml 中的功能开关控制:

[features]
codex_hooks = true

需要留意这些运行时行为:

  • 来自多个文件的匹配钩子都会运行。
  • 对同一事件命中的多个命令型钩子会并发启动,因此一个钩子不能阻止其他已命中的钩子启动。
  • PreToolUsePostToolUseUserPromptSubmitStop 都是按 turn 作用域运行的。
  • Hooks 当前在 Windows 上禁用。

Codex 在哪里查找钩子

Codex 会在当前激活配置层旁边查找 hooks.json

实际使用中,最常见也最有用的两个位置是:

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

如果存在多份 hooks.json,Codex 会加载所有命中的钩子。高优先级配置层不会替换低优先级配置层里的钩子。

配置结构

Hooks 分成三层:

  • 事件名,例如 PreToolUsePostToolUseStop
  • 决定该事件何时命中的 matcher 分组
  • 当 matcher 命中时实际执行的一个或多个钩子处理器
{
  "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"
          }
        ]
      }
    ],
    "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 的单位是秒。
  • 也接受 timeoutSec 作为别名。
  • 如果省略 timeout,Codex 会使用 600 秒。
  • statusMessage 是可选的。
  • 命令会以当前会话的 cwd 作为工作目录运行。
  • 对仓库级钩子,优先使用基于 Git 根目录解析的路径,而不是 .codex/hooks/... 这类相对路径。Codex 可能从子目录启动,基于 Git 根目录的写法更稳定。

匹配器模式

matcher 字段是一个正则表达式字符串,用来过滤 hook 何时触发。使用 "*""",或完全省略 matcher,都表示匹配该事件的所有支持触发。

当前只有部分 Codex 事件真正会使用 matcher

事件 matcher 过滤的对象 说明
PostToolUse 工具名 当前运行时只会发出 Bash
PreToolUse 工具名 当前运行时只会发出 Bash
SessionStart 启动来源 当前运行时的值只有 startupresume
UserPromptSubmit 不支持 该事件中配置的 matcher 会被忽略。
Stop 不支持 该事件中配置的 matcher 会被忽略。

示例:

  • Bash
  • startup|resume
  • Edit|Write

最后这个例子在正则语法上是有效的,但当前 Codex 的 PreToolUsePostToolUse 事件只会发出 Bash,所以今天它实际上不会匹配到任何内容。

通用输入字段

每个命令型钩子都会通过 stdin 收到一个 JSON 对象。

这些共享字段通常最常用:

字段 类型 含义
session_id string 当前 session 或 thread id。
transcript_path string | null session transcript 文件路径;如果不存在则为 null
cwd string 当前会话的工作目录。
hook_event_name string 当前 hook 事件名。
model string 当前激活模型的 slug。

按 turn 作用域运行的 hooks 会在各自的事件专属字段表里额外列出 turn_id

如果你需要完整的当前线格式,请参见 Schema 定义

通用输出字段

SessionStartUserPromptSubmitStop 支持以下共享 JSON 字段:

{
  "continue": true,
  "stopReason": "optional",
  "systemMessage": "optional",
  "suppressOutput": false
}
字段 作用
continue 若为 false,表示该次 hook 运行被标记为停止。
stopReason 记录为停止原因。
systemMessage 作为警告显示在界面或事件流中。
suppressOutput 当前会被解析,但尚未真正实现。

退出码为 0 且没有任何输出,会被视为成功,Codex 会继续执行。

PreToolUse 支持 systemMessage,但当前不支持 continuestopReasonsuppressOutput

PostToolUse 支持 systemMessagecontinue: falsestopReasonsuppressOutput 虽然会被解析,但当前仍未真正支持。

Hooks

SessionStart

这个事件中的 matcher 会作用在 source 上。

通用输入字段 外,还会额外提供:

字段 类型 含义
source string 会话启动方式:startupresume

写到 stdout 的纯文本会被追加为额外的 开发者上下文。

如果向 stdout 输出 JSON,则支持 通用输出字段,以及下面这个该事件专属结构:

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

其中 additionalContext 会被加入为额外的 开发者上下文。

PreToolUse

开发中

当前 PreToolUse 只支持拦截 Bash 工具。模型仍然可能通过先把脚本写到磁盘、再让 Bash 执行这个脚本的方式绕过它,因此应把它视为一个实用护栏,而不是绝对的强制边界。

matcher 会作用在 tool_name 上,而当前这个值始终是 Bash

通用输入字段 外,还会额外提供:

字段 类型 含义
turn_id string Codex 扩展字段。当前激活 turn 的 id。
tool_name string 当前始终是 Bash
tool_use_id string 本次调用对应的 tool-call id。
tool_input.command string Codex 即将执行的 shell 命令。

写到 stdout 的纯文本会被忽略。

如果向 stdout 输出 JSON,可以使用 systemMessage,也可以通过下面这个事件专属结构阻止 Bash 命令执行:

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "Destructive command blocked by hook."
  }
}

Codex 也接受旧版阻止格式:

{
  "decision": "block",
  "reason": "Destructive command blocked by hook."
}

你也可以直接使用退出码 2,并把阻止原因写到 stderr

permissionDecision: "allow""ask"、旧版 decision: "approve"updatedInputadditionalContextcontinue: falsestopReasonsuppressOutput 虽然会被解析,但目前尚未支持,因此会按开放失败处理。

PostToolUse

开发中

当前 PostToolUse 只支持 Bash 工具结果。它并不限于退出成功的命令:只要 Codex 发出了 Bash 后置工具负载,非交互 exec_command 的结果同样可能触发 PostToolUse。但它无法撤销那条已经执行过的命令所产生的副作用。

matcher 会作用在 tool_name 上,而当前该值始终是 Bash

通用输入字段 外,还会额外提供:

字段 类型 含义
turn_id string Codex 扩展字段。当前激活 turn 的 id。
tool_name string 当前始终是 Bash
tool_use_id string 本次调用对应的 tool-call id。
tool_input.command string Codex 刚刚执行过的 shell 命令。
tool_response JSON value Bash 工具输出负载;当前通常是一个 JSON 字符串。

写到 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 会记录这条反馈,用该反馈替换原始工具结果,并从 hook 提供的消息继续驱动模型。

你也可以使用退出码 2,并把反馈原因写到 stderr

如果你想在命令已经执行后,阻止对原始工具结果的正常处理,可以返回 continue: false。Codex 会用你的反馈或停止文本替换原始工具结果,然后从那里继续。

updatedMCPToolOutputsuppressOutput 会被解析,但当前尚未真正支持,因此仍按开放失败处理。

UserPromptSubmit

matcher 当前对这个事件不起作用。

通用输入字段 外,还会额外提供:

字段 类型 含义
turn_id string Codex 扩展字段。当前激活 turn 的 id。
prompt string 即将发送的用户提示词。

写到 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

Stop

matcher 当前对这个事件不起作用。

通用输入字段 外,还会额外提供:

字段 类型 含义
turn_id string Codex 扩展字段。当前激活 turn 的 id。
stop_hook_active boolean 当前这个 turn 是否已经被 Stop 继续过一次。
last_assistant_message string | null 最新 assistant 消息文本;如果不可用则为 null

Stop 要求在退出码为 0 时向 stdout 输出 JSON。对于这个事件,纯文本输出是无效的。

输出 JSON 时,支持 通用输出字段。如果你想让 Codex 继续运行,可返回:

{
  "decision": "block",
  "reason": "Run one more pass over the failing tests."
}

你也可以使用退出码 2,并把继续执行的原因写到 stderr

对这个事件来说,decision: "block" 并不会拒绝当前 turn。相反,它会告诉 Codex 继续,并自动创建一条 continuation prompt,把你的 reason 当作新的用户提示词发送下去。

如果有任意一个命中的 Stop hook 返回 continue: false,它会优先于其他 Stop hooks 的 continuation 决策生效。

Schema 定义

如果你需要当前精确的线格式,请查看 Codex GitHub 仓库 中生成的 schema。


来源:https://developers.openai.com/codex/hooks