从写代码,到创作下一幕

探索 字节跳动 - 火山方舟 的 AI 编程与视频创作活动。

Agent Plan & Coding Plan

一站体验多款热门模型,为 AI 编程与智能体开发提供更多选择。新用户可联系(微信: goo_lvyouyou)免费体验 9.9 agent plan。

Seedance 2.5

让创意,跃然成片。探索 30 秒视频、多模态参考与局部编辑,把脑海中的画面变成下一支作品。

中文

钩子

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

钩子 是 Codex 的可扩展性框架。你可以借助它们在智能体循环期间运行脚本或 MCP 工具,从而实现以下功能:

  • 将聊天发送到自定义日志记录/分析引擎
  • 扫描团队的提示词,防止意外粘贴 API key
  • 汇总聊天内容,自动创建持久记忆
  • 在一轮聊天停止时运行自定义验证检查,以强制执行标准
  • 在特定目录中自定义提示方式

钩子的运行方式:

  • 来自多个文件的所有匹配 钩子 都会运行。
  • 同一事件的多个匹配命令 钩子 会并发启动, 因此一个 钩子 无法阻止另一个匹配 钩子 启动。
  • 非受管 钩子 必须先经过评审并受信任,才能运行。

钩子 会在对话的不同阶段运行:

时机 钩子
在一个轮次期间 PreToolUse、PermissionRequest、PostToolUse、PreCompact、PostCompact、UserPromptSubmit、SubagentStop、Stop
当你中断活跃轮次时 Interrupt(不为子智能体运行)
当会话或子智能体启动时 SessionStart、SubagentStart
当主聊天结束时 SessionEnd(不为子智能体运行)

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,从而在该次调用中运行已启用的 钩子,且无需 持久保存 钩子 信任状态。

配置结构

钩子 分为三个层级:

  • Hook 事件,例如 PreToolUse、PostToolUse、PreCompact、 SubagentStart 或 Stop
  • 用于决定该事件何时匹配的匹配器组
  • 匹配器组命中时运行的一个或多个 钩子 处理程序
{
  "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 文件的可选顶层元数据。它 不会改变要运行哪些 钩子。
  • timeout 的单位为秒。
  • 如果省略 timeout,Codex 会对大多数 钩子 使用 600 秒。
    • SessionEnd 和 Interrupt 默认使用 1 秒,最大支持 3 秒。
  • statusMessage 是可选的。
  • additionalContextLimit 设置命令 钩子 可向模型发送多少 additionalContext, 超出后 Codex 会将完整文本保存到磁盘,并改为发送较短的 预览。请参阅大型 钩子 输出。
  • commandWindows 是仅适用于 Windows 的可选命令覆盖项。在 TOML 中,请使用 command_windows 或 commandWindows。
  • 将 async 设为 true,可在后台运行命令 钩子。
  • 支持 command 和 mcp_tool 处理程序。prompt 和 agent 处理程序会被解析,但会跳过执行。
  • 命令会以会话的 cwd 作为工作目录运行。
  • 对于仓库本地 钩子,建议从 git 根目录解析路径,而不是使用 .codex/hooks/... 之类的相对路径。Codex 可能从 子目录启动,而基于 git 根目录的路径可以保持 钩子 位置稳定。

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 工具 钩子

MCP 工具 钩子 允许生命周期事件调用已连接 MCP server上的工具。它会将结构化参数直接发送给该工具,并采用与命令 钩子 相同的信任评审和输出约定。

配置 MCP 工具 钩子

此 钩子 会要求 scanner MCP server 在 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 server 的名称,必填。
tool 该服务器所公开工具的名称,必填。
input 可选的参数模板 JSON 对象。默认为 {}。
timeout 可选的活跃执行超时时间,单位为秒。默认为 600。
statusMessage Hook 运行时显示的可选消息。

根据 钩子 事件展开参数

使用 ${field.nested} 读取 钩子 事件中的点号分隔字段。如果占位符 填充整个值,则会保留其 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"
}

执行和生命周期

  • 钩子 使用现有 MCP 连接,不会启动或重新连接服务器。
  • 当工具返回阻止决定时,钩子 可以阻止操作。 错误、服务器缺失和工具不可用不会阻止操作。
  • MCP 工具 钩子 会同步运行。它们不会请求工具批准,也不会触发 其他 钩子。
  • 以 钩子 或服务器中较短的超时时间为准。等待 MCP 引导响应所用的时间不计入超时。
  • SessionStart 钩子 可能在 MCP server 准备就绪前运行。如果发生这种情况, 它们不会阻止会话。
  • SessionEnd 不支持 MCP 工具 钩子。

关闭 钩子

钩子 默认启用。要在 config.toml 中将其关闭,请设置:

[features]
hooks = false

使用 hooks 作为功能键。codex_hooks 仍可用作 已弃用的别名。管理员也可以在 requirements.toml 中使用 [features].hooks = false,以同样的方式强制关闭钩子。

来自 requirements.toml 的受管钩子

启用托管策略和远程钩子后,具有本地访问权限的 Work Cloud 和 dots 会在云端编排器上使用管理员托管的远程 MCP 钩子。在全局 requirements.toml 中配置 mcp_tool 处理程序。不具有本地访问权限的 Work Cloud 和个人账户不使用这些企业钩子。云端编排不支持命令/shell、提示词和智能体处理程序;来自本地配置、插件或本地目录的钩子;限定于环境的钩子;以及 SessionEnd MCP 钩子,即使工具在本地执行也是如此。当编排和执行都在本地进行时,现有受支持的钩子仍可在仅本地运行的 Work 和 Codex 聊天中使用。管理员仍可在 Agent Security 中为这些工作流配置受支持的托管钩子。

在依赖这些钩子之前,请测试回调连通性、所需事件和失败时的行为。受支持的显式拒绝可以阻止操作,但 PreToolUse 回调出错、超时或响应格式错误可能导致钩子失败,却不会阻止工具运行。MCP 钩子不提供完整的 Compliance API 审计记录。

本节中的 Codex 说明仍适用于受支持的 Codex 工作流。在为 Work Cloud 启用本地计算机访问之前,请查看同步设置和 Agent Security 中的兼容性警告。参阅托管配置。

在 Codex 中,企业托管的要求可以在 [hooks] 下内联定义钩子。 下面的命令钩子示例仅适用于 Codex。 当管理员希望强制执行钩子配置,同时 通过 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_ROOT 和 CLAUDE_PLUGIN_DATA,以便 与现有插件 钩子 保持兼容。

插件 钩子 与其他 钩子 使用相同的事件数据结构。安装或启用 插件并不会自动信任其 钩子;在你评审并信任当前 钩子 定义前, Codex 会跳过插件捆绑的 钩子。

匹配器模式

matcher 字段是一个正则表达式字符串,用于筛选 钩子 何时触发。使用 "*"、 "",或完全省略 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 都会被忽略
Interrupt 不支持 为此事件配置的任何 matcher 都会被忽略

*对于 apply_patch,matcher 值也可以使用 Edit 或 Write。

示例:

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

工具覆盖范围

PreToolUse 和 PostToolUse 可以观察 shell 和 MCP 调用之外的操作。大多数 本地函数工具都使用相同的 钩子 路径,因此你可以匹配其工具名称、 检查其 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 否 否 这些工具不使用本地函数工具 钩子 路径。

write_stdin 是现有统一执行会话的传输机制。它在发送输入或轮询 已通过 PreToolUse 的命令时,不会再次运行 PreToolUse。

某些专用工具路径可以选择不使用默认 钩子 路径。请将工具 钩子 视为实用的防护机制,而不是完整的强制执行边界。

通用输入字段

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

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

字段 类型 含义
session_id string 当前 Codex 会话 ID。子智能体 钩子 使用父会话 ID。
transcript_path string | null 会话转录文件的路径(如果有)
cwd string 会话的工作目录
hook_event_name string 当前 钩子 事件名称
model string Codex 专用扩展。活跃模型 slug

轮次范围的 钩子 会在其事件专用表中将 turn_id 列为 Codex 专用扩展。

SessionStart、PreToolUse、PermissionRequest、PostToolUse、 UserPromptSubmit、SubagentStart、SubagentStop、Stop 和 Interrupt 还会包含 permission_mode,用于描述当前权限模式,其值为 default、 acceptEdits、plan、dontAsk 或 bypassPermissions。

为了方便使用,transcript_path 指向聊天转录,但 转录格式并非 钩子 的稳定接口,可能会随时间变化。

如需完整的传输格式,请参阅数据结构。

通用输出字段

SessionStart、PreCompact、PostCompact、UserPromptSubmit、 SubagentStop 和 Stop 支持以下共享 JSON 字段。SubagentStart 为 systemMessage 和 钩子 专用上下文接受相同的结构,但 continue: false 不会停止子智能体:

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

退出码为 0 且没有输出时会被视为成功,Codex 将继续运行。

PreToolUse 和 PermissionRequest 支持 systemMessage,但目前这些事件不支持 continue、stopReason 和 suppressOutput。 如果 PreToolUse 钩子 返回其中一个不受支持的字段,Codex 会将 该次 钩子 运行标记为失败、报告错误,然后继续工具调用。

PostToolUse 支持 systemMessage、continue: false 和 stopReason。 suppressOutput 会被解析,但目前不支持用于该事件。

大型 钩子 输出

默认情况下,Codex 会将每条模型可见的 钩子 输出消息限制在约 2,500 个 token。如果 钩子 返回的内容更多,Codex 会将完整文本保存到 <temp_dir>/hook_outputs/<session_id>/<uuid>.txt 下,并向模型提供包含已保存文件路径的 首尾预览。此行为称为 溢出:Codex 将过大的输出存储到磁盘,并替换为 模型可见的较短预览。如果文件无法写入,模型仍会 收到截断后的预览。

对于任何返回 additionalContext 的命令 钩子,可在处理程序上设置 additionalContextLimit,以自定义大致的 token 阈值:

{
  "type": "command",
  "command": "python3 ~/.codex/hooks/session_start.py",
  "additionalContextLimit": 5000
}

省略 additionalContextLimit 即可使用默认的 2500 token 阈值。使用 正整数可选择其他阈值,使用 0 则可将处理程序的 完整附加上下文直接传给模型。Codex 会单独评估每个 匹配的处理程序。对于无法生成附加 上下文的事件,Codex 会忽略 additionalContextLimit 并报告配置 警告。

该设置仅适用于 additionalContext。工具反馈和续接 提示仍使用默认限制。

由于过大的输出可能会写入磁盘,请避免在 钩子 输出中返回密钥或 其他敏感数据。

在后台运行 钩子

默认情况下,Codex 会等待命令 钩子 完成,再继续执行 触发它的操作。将 async 设为 true,即可在 Codex 继续运行的同时, 在后台运行命令 钩子。

配置后台 钩子

在 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 中的内联 钩子,请设置 async = true:

[[hooks.PostToolUse]]
matcher = "Bash"

[[hooks.PostToolUse.hooks]]
type = "command"
command = "python3 ~/.codex/hooks/post_tool_use.py"
async = true
timeout = 120

后台 钩子 与同步命令 钩子 使用相同的输入、matcher、信任评审、超时和 大型输出处理方式。与其他命令 钩子 一样,timeout 以秒为单位,默认值为 600。Interrupt 钩子 的默认超时时间为一秒,上限为三秒, 在后台运行时也是如此。

后台 钩子 的运行方式

后台 钩子 完成后,Codex 会在对话中的下一个安全时机传递 受支持的信息性输出:

  • 如果一轮对话正在进行,Codex 会等待当前模型请求和工具调用 完成,然后让该输出可供本轮中的下一次模型请求使用。
  • 如果当前没有进行中的轮次,Codex 会等到下一个用户轮次。后台 钩子 完成不会启动新的轮次。

请使用与同步 钩子 相同的事件专用 JSON 输出。Codex 会将 additionalContext 添加到模型上下文,并将 systemMessage 显示为 警告。

限制

  • Codex 在每个会话中最多并发运行八个后台 钩子。其他 钩子 会等待运行中的 钩子 完成。
  • 每次匹配的调用都会独立运行,后台 钩子 完成的 顺序可能与启动顺序不同。
  • 会话结束时,Codex 会取消尚未完成的后台 钩子,并丢弃 尚未传递的输出。
  • SessionEnd 钩子 始终同步运行。

钩子

SessionStart

此事件会将 matcher 应用于 source。

除通用输入字段外,还包含以下字段:

字段 类型 含义
source string 会话的启动方式:startup、resume、clear 或 compact

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

stdout 上的 JSON 支持通用输出字段以及 此钩子专用的结构:

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

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

Codex 压缩根会话后,匹配 source: "compact" 的 SessionStart 钩子 会在下一次模型请求前运行。这也适用于 在一轮对话中途发生自动压缩的情况:Codex 会将 钩子 的 附加上下文传递给紧随其后的续接过程,而不是等待 后续用户轮次。如果 钩子 返回 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 钩子 始终同步运行,即使 async 为 true。它们 仅提供建议,因此其输出不会引导 Codex,也不会让线程保持打开。如果 命令超时或以错误退出,Codex 会将其报告为 钩子 失败。

SubagentStart

此事件会将 matcher 应用于 agent_type。

除通用输入字段外,还包含以下字段:

字段 类型 含义
turn_id string Codex 专用扩展。活跃的 Codex 轮次 ID
agent_id string 子智能体标识符
agent_type string 子智能体类型或配置档案
permission_mode string 当前权限模式

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

stdout 上的 JSON 支持 systemMessage 以及以下 钩子 专用结构:

{
  "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;钩子 输入 仍会报告 tool_name: "apply_patch"。

除通用输入字段外,还包含以下字段:

字段 类型 含义
turn_id string Codex 专用扩展。活跃的 Codex 轮次 ID
tool_name string 规范 钩子 工具名称,例如 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。要拒绝受支持的工具调用,请返回 以下 钩子 专用结构:

{
  "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 会将 该次 钩子 运行标记为失败、报告错误,然后继续工具调用。

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 规范 钩子 工具名称,例如 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."
    }
  }
}

如果多个匹配 钩子 返回决定,则任何 deny 都优先。否则, allow 会让请求继续,而不显示批准提示。如果没有 匹配的 钩子 作出决定,Codex 会使用正常的批准流程。

请勿为 PermissionRequest 返回 updatedInput、updatedPermissions 或 interrupt; 这些字段为未来行为预留,目前会拒绝请求并终止相应操作。

PostToolUse

PostToolUse 会在受支持的工具产生输出后运行,包括 Bash、 apply_patch、MCP 工具调用及其他本地函数工具。对于 Bash, 它也会在命令以非零状态退出后运行。它无法撤销 已运行工具产生的副作用。有关受支持的路径和例外情况, 请参阅工具覆盖范围。

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

除通用输入字段外,还包含以下字段:

字段 类型 含义
turn_id string Codex 专用扩展。活跃的 Codex 轮次 ID
tool_name string 规范 钩子 工具名称,例如 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 及以下 钩子 专用结构:

{
  "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 会用你的反馈或停止文本替换工具结果, 然后从此处继续。

updatedMCPToolOutput 和 suppressOutput 会被解析,但尚不受支持。 Codex 会将该次 钩子 运行标记为失败、报告错误,然后继续 正常处理工具结果。

代码模式中的工具调用

当模型在代码模式下通过 JavaScript 调用工具时,钩子 决定会应用于 该嵌套调用。PreToolUse 可以在工具运行前将其停止或重写 其输入。具有阻止作用的 PostToolUse 无法撤销工具的副作用,但可以 阻止原始结果到达正在运行的脚本。

Hook 结果 代码模式看到的内容
PreToolUse 阻止 工具 promise 会在工具运行前被拒绝。
PreToolUse 返回 updatedInput 工具使用重写后的输入运行,promise 以该结果兑现。
PostToolUse 返回 decision: "block" 或以代码 2 退出 工具先运行,然后 promise 会以 钩子 原因为由被拒绝。
PostToolUse 返回 continue: false Codex 将 钩子 反馈用作模型可见结果,但不会拒绝嵌套工具 promise。

PreCompact

PreCompact 会在 Codex 压缩聊天前运行。matcher 会应用于 trigger,其值为 manual 和 auto。

除通用输入字段外,还包含以下字段:

字段 类型 含义
turn_id string Codex 专用扩展。活跃的 Codex 轮次 ID
trigger string 压缩的触发方式:manual 或 auto

stdout 上的纯文本会被忽略。

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

PostCompact

PostCompact 会在 Codex 压缩聊天后运行。matcher 会应用于 trigger,其值为 manual 和 auto。

除通用输入字段外,还包含以下字段:

字段 类型 含义
turn_id string Codex 专用扩展。活跃的 Codex 轮次 ID
trigger string 压缩的触发方式:manual 或 auto

stdout 上的纯文本会被忽略。

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

UserPromptSubmit

matcher 目前不用于此事件。

除通用输入字段外,还包含以下字段:

字段 类型 含义
turn_id string Codex 专用扩展。活跃的 Codex 轮次 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。

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

Interrupt

当你中断主聊天上正在进行的轮次时,Interrupt 会运行。可使用它 记录中断,或清理由钩子启动的工作。它不会针对空闲聊天或子智能体运行, 并且会忽略任何已配置的 matcher。

除通用输入字段外,该事件还包括 turn_id(被中断轮次的 id)和 permission_mode。

命令钩子的默认超时时间为一秒。配置的超时时间 仅限一至三秒。钩子输出无法阻止 中断或重新启动轮次。以 0 退出且不输出任何内容,或者返回包含 可选 systemMessage 的 JSON 以显示警告。纯文本输出对此事件无效。

{ "systemMessage": "Saved the interrupted turn to the local audit log." }

数据结构

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

纯文本别名

  • string | null