Skip to content

Hooks 是 Codex 的扩展框架,允许你在 Agent 执行循环的关键节点注入自己的脚本。常见用途:把对话发送到自定义日志系统、扫描 Prompt 中的 API key 泄露风险、自动生成持久化记忆、在会话结束时运行校验器。本文覆盖 hooks.json 配置格式、匹配器规则、5 种事件的完整输入输出字段,以及常见场景示例。

OpenAI Codex Hooks

实验性功能,正在持续开发中。Windows 暂不支持。

Hooks 让你把自己的脚本注入 Codex 的 agent 循环,实现:

  • 把对话发送到自定义日志或分析系统
  • 扫描 Prompt,防止意外粘贴 API key 等敏感信息
  • 自动汇总对话,创建持久化记忆
  • 在会话回合结束时运行校验器,强制执行规范
  • 根据当前目录定制 Prompting 行为

启用 Hooks

config.toml 里开启 feature flag:

toml
[features]
codex_hooks = true

运行时注意事项

  • 多个匹配的 hooks 文件都会运行
  • 同一事件的多个匹配 command hooks 并发启动,一个 hook 无法阻止另一个 hook 的启动
  • PreToolUsePostToolUseUserPromptSubmitStop 在 turn 级别运行
  • Hooks 当前不支持 Windows

hooks.json 的位置

Codex 在活跃 config 层级旁边查找 hooks.json。最常用的两个位置:

  • ~/.codex/hooks.json(全局 hooks)
  • <repo>/.codex/hooks.json(仓库级 hooks)

多个 hooks.json 文件同时存在时,Codex 加载所有匹配的 hooks。高优先级 config 层不会覆盖低优先级的 hooks。


配置格式

Hooks 分三层结构:事件匹配器组hook 处理器列表

json
{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "startup|resume",
        "hooks": [
          {
            "type": "command",
            "command": "python3 ~/.codex/hooks/session_start.py",
            "statusMessage": "加载会话笔记"
          }
        ]
      }
    ],
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "/usr/bin/python3 \"$(git rev-parse --show-toplevel)/.codex/hooks/pre_tool_use_policy.py\"",
            "statusMessage": "检查 Bash 命令"
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "/usr/bin/python3 \"$(git rev-parse --show-toplevel)/.codex/hooks/post_tool_use_review.py\"",
            "statusMessage": "审查 Bash 输出"
          }
        ]
      }
    ],
    "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:单位秒。省略时默认 600 秒,timeoutSec 也是有效别名
  • statusMessage:可选,在 UI 或事件流中显示
  • Commands 以 session cwd 作为工作目录执行
  • 仓库级 hooks 建议从 git root 解析路径(git rev-parse --show-toplevel),避免从子目录启动时路径错误

匹配器(matcher)

matcher 是一个正则表达式字符串,用于过滤 hook 的触发时机。用 "*""" 或省略 matcher 表示匹配该事件的所有触发。

各事件的 matcher 作用对象:

事件matcher 过滤的对象备注
PostToolUse工具名称当前运行时只触发 Bash
PreToolUse工具名称当前运行时只触发 Bash
SessionStart启动来源(startupresume
UserPromptSubmit不支持配置了 matcher 也会被忽略
Stop不支持配置了 matcher 也会被忽略

所有 Hook 共有的输入字段

每个 command hook 通过 stdin 接收一个 JSON 对象,包含以下通用字段:

字段类型含义
session_idstring当前会话或线程 ID
transcript_pathstring | null会话记录文件路径(如有)
cwdstring当前工作目录
hook_event_namestring当前 hook 事件名
modelstring当前使用的模型 slug

所有 Hook 共有的输出字段

SessionStartUserPromptSubmitStop 支持以下 JSON 输出:

json
{
  "continue": true,
  "stopReason": "可选原因",
  "systemMessage": "可选系统消息",
  "suppressOutput": false
}
字段效果
continue: false标记这次 hook 运行为已停止
stopReason记录停止原因
systemMessage在 UI 或事件流中显示为警告
suppressOutput已解析但尚未实现

exit 0 且无输出 = 成功,Codex 继续执行。


五种 Hook 事件详解

SessionStart

在每次会话启动或恢复时触发。

额外输入字段

字段类型含义
sourcestring启动方式:startupresume

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

也可以输出 JSON 返回额外上下文:

json
{
  "hookSpecificOutput": {
    "hookEventName": "SessionStart",
    "additionalContext": "编辑前先加载工作区规范。"
  }
}

PreToolUse

在 Codex 运行工具前触发。当前只支持拦截 Bash 工具(模型仍可以把脚本写到磁盘后用 Bash 运行,因此这更像是一个有用的护栏而不是完整的执行边界)。

额外输入字段

字段类型含义
turn_idstring当前 Codex turn ID
tool_namestring目前始终为 Bash
tool_use_idstring本次工具调用的 ID
tool_input.commandstringCodex 即将执行的 shell 命令

拦截 Bash 命令的输出格式(两种方式都支持):

json
{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "破坏性命令被 hook 阻止。"
  }
}

或者更简洁的旧格式:

json
{
  "decision": "block",
  "reason": "破坏性命令被 hook 阻止。"
}

也可以用 exit code 2 并把阻止原因写到 stderr


PostToolUse

在工具运行完成后触发。当前只支持 Bash 工具结果,不限于成功执行的命令。无法撤销已经运行的命令的副作用。

额外输入字段

字段类型含义
turn_idstring当前 Codex turn ID
tool_namestring目前始终为 Bash
tool_use_idstring本次工具调用的 ID
tool_input.commandstring刚刚运行的 shell 命令
tool_responseJSON valueBash 工具输出(通常是 JSON 字符串)

添加反馈的输出格式

json
{
  "decision": "block",
  "reason": "Bash 输出需要在继续之前先审查。",
  "hookSpecificOutput": {
    "hookEventName": "PostToolUse",
    "additionalContext": "命令更新了自动生成的文件。"
  }
}

注意:PostToolUse 里的 decision: "block" 不会撤销已完成的命令,而是把反馈记录进去,用这个反馈替换工具结果,让模型从 hook 提供的消息继续。


UserPromptSubmit

在用户 Prompt 被发送前触发,可用于内容检查或注入额外上下文。

额外输入字段

字段类型含义
turn_idstring当前 Codex turn ID
promptstring即将发送的用户 Prompt

stdout 输出的纯文本作为额外上下文添加。

阻止 Prompt 的输出格式

json
{
  "decision": "block",
  "reason": "请在编辑文件前先确认。"
}

Stop

在每次会话回合结束时触发,可用于自动继续执行或做收尾工作。

额外输入字段

字段类型含义
turn_idstring当前 Codex turn ID
stop_hook_activeboolean本次 turn 是否已被 Stop hook 继续过
last_assistant_messagestring | null最新的 assistant 消息文本

让 Codex 继续运行的输出格式

json
{
  "decision": "block",
  "reason": "再跑一遍失败的测试。"
}

注意:Stop 里的 decision: "block" 不是拒绝,而是告诉 Codex 继续,并用你的 reason 作为一个新的 Prompt 自动发送。

如果任何一个匹配的 Stop hook 返回 continue: false,它的优先级高于其他 Stop hook 的 continuation decisions。


完整 Schema 参考

需要完整的输入输出格式,参考 Codex GitHub 仓库里的生成 Schema:codex-rs/hooks/schema/generated


常见问题

Q: Hooks 和 AGENTS.md 有什么区别?

A: AGENTS.md 是给 Codex 的静态指令文件,在会话开始时加载一次。Hooks 是动态脚本,在 Agent 执行循环的特定节点实时运行,可以读取实时状态、输出反馈,甚至阻止某些操作。两者互补。

Q: PreToolUse 能拦截所有工具调用吗?

A: 不能,目前只能拦截 Bash 工具。MCP、Write、WebSearch 等工具调用无法被拦截。官方正在开发更完整的拦截机制。

Q: Hooks 里的脚本语言有限制吗?

A: 没有限制,command 字段可以运行任何系统上的可执行程序。示例用 Python,但你也可以用 Node.js、Shell、Go binary 等。关键是脚本通过 stdin 接收 JSON,通过 stdout 输出 JSON(或纯文本)。