Hooks 是 OpenAI Codex 的扩展框架,能把自定义脚本接到会话、工具调用和结束收尾这些节点上。适合做日志、敏感信息拦截、自动记忆、执行前后检查;启用后需要按 hook 定义做 trust 审核,managed hooks 还能由 requirements.toml 强制生效。

OpenAI Codex Hooks 配置与事件参考

Hooks 是 OpenAI Codex 的扩展框架,允许你把自己的脚本注入编码代理的执行循环里,用来:

  • 把对话发送到自定义日志或分析系统
  • 扫描团队 Prompt,拦截误贴的 API key
  • 自动汇总对话,生成持久化记忆
  • 在会话回合停止时运行校验,强制执行规范
  • 在特定目录下自定义 prompting 行为

Hooks 默认已开启。如果要在 config.toml 里关闭:

[features]
hooks = false

请使用 hooks 作为标准 feature key。codex_hooks 仍然可用,但已经是废弃别名。

管理员也可以在 requirements.toml 里用同样的方式关闭:

[features]
hooks = false

运行时要注意:

  • 多个文件中匹配到的 hooks 都会运行
  • 同一个事件里,多个匹配的 command hooks 会并发启动,一个 hook 不能阻止另一个匹配 hook 启动
  • 非 managed 的 command hooks 在运行前必须先 review 并 trust
  • PreToolUsePermissionRequestPostToolUsePreCompactPostCompactUserPromptSubmitSubagentStopStop 作用于 turn 范围
  • SessionStartSubagentStart 作用于 thread 或 subagent 启动范围

Codex 在哪里查找 hooks

Codex 会在 active config layers 附近发现 hooks,支持两种形式:

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

已安装的 plugins 也可以通过 plugin manifest 或默认的 hooks/hooks.json 提供 lifecycle config。插件打包规则见 Build plugins

实际最常用的四个位置是:

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

如果存在多个 hook source,Codex 会加载所有匹配项。高优先级 config layer 不会覆盖低优先级 hooks。若同一层同时存在 hooks.json 和内联 [hooks],Codex 会合并两者,并在启动时给出警告。每一层建议只保留一种写法。

Codex 也会加载已启用 plugin 中打包的 hooks。它们和其他 hook source 一样走同一套 trust-review 流程。

项目级 hooks 只有在项目的 .codex/ 层已 trusted 时才会加载。对于不可信项目,Codex 仍会加载用户级和系统级 active config layers 中的 hooks。

如何 review 和 trust hooks

Codex 在决定哪些 hooks 能运行之前,会先列出已配置的 hooks。非 managed command hook 运行前,必须 review 并 trust 该 hook 的精确定义。Codex 会把 trust 记录到 hook 当前 hash 上,所以新 hook 或已修改的 hook 会重新进入 review 状态,在 trust 之前会被跳过。

在 CLI 里输入 /hooks,可以查看 hook source、review 新增或变更的 hook、trust hook,或者禁用单个非 managed hook。如果启动时需要 review,Codex 会打印警告并提示打开 /hooks

来自 system、MDM、cloud 或 requirements.toml 的 managed hooks 会被标记为 managed,按策略 trusted,用户无法在 hook browser 里禁用它们。

如果是一次性自动化,而且你已经在 Codex 外部完成了 hook source 校验,可以加 --dangerously-bypass-hook-trust,让这次调用直接运行已启用 hooks,而不要求持久化 trust 记录。

配置结构

Hooks 分成三层:

  • hook event,例如 PreToolUsePostToolUsePreCompactSubagentStartStop
  • 决定匹配时机的 matcher group
  • 一个或多个在 matcher 命中时运行的 hook handler
{
  "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-only command override;在 TOML 中可写成 command_windowscommandWindows
  • async 会被解析,但异步 command hooks 目前不支持,Codex 会跳过 async: true 的 handler
  • 当前只有 type: "command" 的 handler 会运行;promptagent 会被解析但跳过
  • 命令会以 session 的 cwd 作为工作目录执行
  • 仓库本地 hooks 建议从 git root 解析路径,不要直接写相对路径 .codex/hooks/...,因为 Codex 可能从子目录启动,基于 git root 的路径更稳定

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

requirements.toml 里的 managed hooks

企业管理的 requirements 也可以在 [hooks] 下直接定义 hooks。这个方式适合管理员强制统一 hook 配置,同时把脚本通过 MDM 或其他设备管理系统分发出去。要让 hooks 即使在用户本地关闭后也强制生效,可以在 requirements.toml 里和 [hooks] 一起固定 [features].hooks = true。如果你想忽略用户、项目、session 和 plugin hooks,只保留管理员 managed 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 hooks 的注意事项:

  • managed_dir 用于 macOS 和 Linux
  • windows_managed_dir 用于 Windows
  • Codex 不会分发 managed_dir 里的脚本;企业工具需要单独安装和更新
  • managed hook 命令应使用配置好的 managed directory 下的绝对脚本路径
  • allow_managed_hooks_only = true 会跳过来自用户、项目、session 和 plugin 的 hooks,但仍会加载 requirements.toml 和其他 managed config layers 中的 managed hooks

plugin 打包的 hooks

启用 plugin 后,Codex 可以把 plugin 的 lifecycle hooks 和用户、项目、managed hooks 一起加载。

默认情况下,Codex 会在 plugin root 里找 hooks/hooks.json。plugin manifest 可以通过 .codex-plugin/plugin.json 里的 hooks 条目覆盖这个默认值。这个条目可以是带 ./ 前缀的路径、多个 ./ 路径组成的数组、内联 hooks object,或者多个内联 hooks object 的数组。

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

manifest 中的 hook 路径会相对于 plugin root 解析,并且必须留在这个 root 内。如果 manifest 定义了 hooks,Codex 会优先使用这些 manifest 条目,而不是默认的 hooks/hooks.json

plugin hook 命令会收到这些环境变量:

  • PLUGIN_ROOT:Codex 特有扩展,指向已安装的 plugin root
  • PLUGIN_DATA:Codex 特有扩展,指向 plugin 可写数据目录
  • 为了兼容已有 plugin hooks,Codex 也会设置 CLAUDE_PLUGIN_ROOTCLAUDE_PLUGIN_DATA

plugin hooks 使用和其他 hooks 相同的 event schema。安装或启用 plugin 不会自动 trust 它的 hooks;Codex 会跳过 plugin 打包的 hooks,直到你 review 并 trust 当前 hook definition。

matcher 模式

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

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

Event matcher 过滤的内容 备注
PermissionRequest tool name 支持 Bashapply_patch* 和 MCP tool names
PostToolUse tool name 支持 Bashapply_patch* 和 MCP tool names
PostCompact compaction trigger 值为 manualauto
PreCompact compaction trigger 值为 manualauto
PreToolUse tool name 支持 Bashapply_patch* 和 MCP tool names
SessionStart start source 值为 startupresumeclearcompact
SubagentStart subagent type 值取决于启动的 subagent
SubagentStop subagent type 值取决于停止的 subagent
UserPromptSubmit 不支持 配置的 matcher 会被忽略
Stop 不支持 配置的 matcher 会被忽略

* 对 apply_patch 来说,matcher 也可以写 EditWrite

可用例子:

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

通用输入字段

每个 command hook 都会从 stdin 接收一个 JSON object。

最常用的通用字段如下:

字段 类型 含义
session_id string 当前 Codex session id。subagent hooks 会使用父 session id
transcript_path string | null 会话 transcript 文件路径(如果有)
cwd string 会话工作目录
hook_event_name string 当前 hook 事件名
model string Codex 特有扩展,当前活动模型 slug

作用于 turn 的 hooks 在各自事件表里还会列出 turn_id,这是 Codex 特有扩展。

SessionStartPreToolUsePermissionRequestPostToolUseUserPromptSubmitSubagentStartSubagentStopStop 还包含 permission_mode,用于描述当前权限模式,值可能是 defaultacceptEditsplandontAskbypassPermissions

transcript_path 只是为了方便访问会话记录,但 transcript 格式不是 hooks 的稳定接口,未来可能变化。

如果你需要完整 wire format,请看 Schemas

通用输出字段

SessionStartPreCompactPostCompactUserPromptSubmitSubagentStopStop 支持下面这些共享 JSON 字段。SubagentStart 也接受相同形状的 systemMessage 和 hook-specific context,但 continue: false 不会停止 subagent:

{
  "continue": true,
  "stopReason": "optional",
  "systemMessage": "optional",
  "suppressOutput": false
}
字段 效果
continue 如果是 false,表示这次 hook run 被标记为 stopped
stopReason 记录为停止原因
systemMessage 在 UI 或 event stream 中作为 warning 展示
suppressOutput 目前已解析,但还没实现

退出码 0 且没有输出,会被当成成功,Codex 会继续。

PreToolUsePermissionRequest 支持 systemMessage,但当前不支持 continuestopReasonsuppressOutput。如果 PreToolUse hook 返回了这些不支持的字段,Codex 会把这次 hook run 标记为失败,报告错误,并继续工具调用。

PostToolUse 支持 systemMessagecontinue: falsestopReasonsuppressOutput 会被解析,但这个事件还不支持。

各个 Hooks 事件

SessionStart

matcher 会应用到这个事件的 source 上。

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

字段 类型 含义
source string 会话如何开始:startupresumeclearcompact

stdout 里的纯文本会被加入为额外的 developer context。

stdout 里的 JSON 也支持 通用输出字段 和下面这个 hook-specific 结构:

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

这段 additionalContext 会作为额外 developer context 加进去。

SubagentStart

matcher 会应用到这个事件的 agent_type 上。

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

字段 类型 含义
turn_id string Codex 特有扩展。当前 Codex turn id
agent_id string subagent 标识
agent_type string subagent 类型或 profile
permission_mode string 当前权限模式

stdout 里的纯文本会作为 subagent 的额外 developer context。

stdout 里的 JSON 支持 systemMessage 和下面这个 hook-specific 结构:

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

这段 additionalContext 会作为 subagent 的额外 developer context。continue: false 虽然会被解析以保持兼容,但不会阻止 subagent 启动。

PreToolUse

PreToolUse 可以拦截 Bash、通过 apply_patch 完成的文件编辑,以及 MCP tool calls。它仍然只是护栏,不是完整的强制边界,因为 Codex 往往可以通过其他支持的工具路径完成等价工作。

它目前不能拦截所有 shell 调用,只能拦截简单的那部分。新的 unified_exec 机制支持更丰富的 shell streaming stdin/stdout,但拦截还不完整。同样,它也不能拦截 WebSearch 或其他非 shell、非 MCP 的 tool calls。

matcher 会应用到 tool_name 和 matcher aliases。对于通过 apply_patch 进行的文件编辑,matcher 可以写 apply_patchEditWrite;hook 输入里仍然会报告 tool_name: "apply_patch"

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

字段 类型 含义
turn_id string Codex 特有扩展。当前 Codex turn id
tool_name string 标准 hook 工具名,例如 Bashapply_patch,或类似 mcp__fs__read 的 MCP 名称
tool_use_id string 本次工具调用 id
tool_input JSON value 工具特定输入。Bashapply_patch 使用 tool_input.command,MCP tools 会传所有参数

stdout 里的纯文本会被忽略。

stdout 的 JSON 可以使用 systemMessage。如果要拒绝一个受支持的工具调用,返回下面这个结构:

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

Codex 也接受较旧的 block 结构:

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

你也可以返回 exit code 2,并把阻止原因写到 stderr

如果只是想给模型补充可见上下文而不拦截,可以返回 hookSpecificOutput.additionalContext

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "additionalContext": "The pending command touches generated files."
  }
}

如果要在不阻止调用的情况下重写一个受支持的工具调用,可以返回 permissionDecision: "allow" 并提供 updatedInput

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

对于 Bash 命令和 apply_patchupdatedInput 必须包含字符串类型的 command 字段。对 MCP tools,updatedInput 是替换后的 arguments object。只有在 permissionDecision: "allow" 时才可以返回 updatedInput;其他 updatedInput 形状都会报错。

permissionDecision: "ask"、旧版 decision: "approve"continue: falsestopReasonsuppressOutput 会被解析,但目前不支持。Codex 会把这次 hook run 标记为失败,报告错误,并继续工具调用。

PermissionRequest

PermissionRequest 会在 Codex 准备请求用户批准时运行,例如 shell escalation 或 managed-network approval。它可以允许请求、拒绝请求,或者放弃决定,让正常的批准提示继续。对于不需要审批的命令,这个事件不会运行。

matcher 会应用到 tool_name 和 matcher aliases。当前 canonical 值包括 Bashapply_patch,以及类似 mcp__server__tool 的 MCP tool names;apply_patch 也会匹配 EditWrite

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

字段 类型 含义
turn_id string Codex 特有扩展。当前 Codex turn id
tool_name string 标准 hook 工具名,例如 Bashapply_patch,或类似 mcp__fs__read 的 MCP 名称
tool_input JSON value 工具特定输入。Bashapply_patch 使用 tool_input.command,MCP tools 会传所有参数
tool_input.description string | null 可读的审批原因(如果 Codex 提供了)

stdout 的纯文本会被忽略。

有些 tool input 里会带人类可读的描述,但不要依赖每个工具都有 tool_input.description

要批准请求,返回:

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

要拒绝请求,返回:

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

如果多个匹配 hooks 都返回 decision,任何 deny 都会获胜。否则,allow 会让请求直接通过,不再显示批准提示。如果没有任何匹配 hook 做出决定,Codex 会走正常的 approval flow。

不要在 PermissionRequest 里返回 updatedInputupdatedPermissionsinterrupt;这些字段预留给未来行为,目前会 fail closed。

PostToolUse

PostToolUse 会在受支持工具产出结果后运行,包括 Bash、apply_patch 和 MCP tool calls。对于 Bash,它也会在命令非 0 退出时运行。它不能撤销工具已经造成的副作用。

它目前不能拦截所有 shell 调用,只能拦截简单的那部分。新的 unified_exec 机制支持更丰富的 shell streaming stdin/stdout,但拦截还不完整。同样,它也不能拦截 WebSearch 或其他非 shell、非 MCP 的 tool calls。

matcher 会应用到 tool_name 和 matcher aliases。对于通过 apply_patch 进行的文件编辑,matcher 可以写 apply_patchEditWrite;hook 输入里仍然会报告 tool_name: "apply_patch"

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

字段 类型 含义
turn_id string Codex 特有扩展。当前 Codex turn id
tool_name string 标准 hook 工具名,例如 Bashapply_patch,或类似 mcp__fs__read 的 MCP 名称
tool_use_id string 本次工具调用 id
tool_input JSON value 工具特定输入。Bashapply_patch 使用 tool_input.command,MCP tools 会传所有参数
tool_response JSON value 工具特定输出。对 MCP tools 来说,这是 MCP 调用结果

stdout 的纯文本会被忽略。

stdout 里的 JSON 可以使用 systemMessage 和下面这个 hook-specific 结构:

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

这段 additionalContext 会作为额外 developer context 加进去。

在这个事件里,decision: "block" 不会撤销已经完成的 Bash 命令。相反,Codex 会记录这条反馈,用这条反馈替换工具结果,然后让模型从 hook 提供的消息继续。

你也可以用 exit code 2 并把反馈原因写到 stderr

如果要在命令已经运行后停止正常处理原始工具结果,可以返回 continue: false。Codex 会用你的反馈或 stop text 替换工具结果,然后继续往下走。

updatedMCPToolOutputsuppressOutput 会被解析,但目前不支持。Codex 会把这次 hook run 标记为失败,报告错误,并继续按正常流程处理工具结果。

PreCompact

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

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

字段 类型 含义
turn_id string Codex 特有扩展。当前 Codex turn id
trigger string 触发压缩的原因:manualauto

stdout 的纯文本会被忽略。

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

PostCompact

PostCompact 会在 Codex 压缩会话后运行。matcher 会应用到 trigger,其值为 manualauto.

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

字段 类型 含义
turn_id string Codex 特有扩展。当前 Codex turn id
trigger string 触发压缩的原因:manualauto

stdout 的纯文本会被忽略。

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

UserPromptSubmit

这个事件当前不使用 matcher

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

字段 类型 含义
turn_id string Codex 特有扩展。当前 Codex turn id
prompt string 即将发送的用户 prompt

stdout 的纯文本会作为额外 developer context 加进去。

stdout 里的 JSON 支持 通用输出字段 和下面这个 hook-specific 结构:

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

这段 additionalContext 会作为额外 developer context 加进去。

如果要阻止 prompt,返回:

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

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

SubagentStop

matcher 会应用到这个事件的 agent_type 上。

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

字段 类型 含义
turn_id string Codex 特有扩展。当前 Codex turn id
agent_id string subagent 标识
agent_type string subagent 类型或 profile
agent_transcript_path string | null subagent transcript 文件路径(如有)
stop_hook_active boolean 这个 subagent 是否已经被继续过
last_assistant_message string | null 最新的 subagent assistant message(如有)

SubagentStop 在退出码为 0 时期望 stdout 输出 JSON。这个事件不接受纯文本输出。

stdout 里的 JSON 支持 通用输出字段。如果要让 Codex 继续 subagent flow,返回:

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

也可以用 exit code 2 并把 continuation reason 写到 stderr

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

Stop

这个事件当前不使用 matcher

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

字段 类型 含义
turn_id string Codex 特有扩展。当前 Codex turn id
stop_hook_active boolean 这个 turn 是否已经被 Stop 继续过
last_assistant_message string | null 最新的 assistant message 文本(如有)

Stop 在退出码为 0 时期望 stdout 输出 JSON。这个事件不接受纯文本输出。

stdout 里的 JSON 支持 通用输出字段。如果要让 Codex 继续,返回:

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

也可以用 exit code 2 并把 continuation reason 写到 stderr

在这个事件里,decision: "block" 不是拒绝 turn。它会让 Codex 继续,并自动创建一个新的 continuation prompt,把你的 reason 当作新的 prompt 文本。

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

Schemas

链接到 main 分支的 schemas 可能包含当前发布版本里还没有的 hook 字段。请以本页描述的 release 行为为准。

如果你需要精确的当前 wire format,请查看 Codex GitHub 仓库里生成的 schemas:codex-rs/hooks/schema/generated

常见问题

OpenAI Codex Hooks 和 AGENTS.md 有什么区别?

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

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

不能。当前只会拦截 Bash、apply_patch 和部分 MCP tool calls。WebSearch 以及其他非 shell、非 MCP 的工具调用不会被拦截。Codex 仍在推进更完整的拦截机制。

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

没有。command 可以运行系统上任何可执行程序。示例用的是 Python,但也可以用 Node.js、Shell、Go binary 等。关键是脚本从 stdin 读 JSON,并向 stdout 输出 JSON 或纯文本。