Configuration

Hooks

Hooks 适合做生命周期里的机械检查、记录和限制,不应承载复杂产品判断。

Hooks 是 Codex 的扩展框架。它们允许你把自己的 scripts 注入 agentic loop,从而启用以下能力:

将 conversation 发送到自定义 logging/analytics engine

扫描团队 prompts,阻止意外粘贴 API keys

总结 conversations,自动创建 persistent memories

在 conversation turn 停止时运行自定义 validation check,以执行标准

在特定 directory 中自定义 prompting

Hooks are enabled by 默认。如果 you need 到 turn them off in config.toml, 设置:

[features]
hooks = false

使用 hooks as the canonical feature key。codex_hooks still works as a deprecated alias.

Admins can force hooks off the same way in requirements.toml 使用 [features].hooks = false.

Runtime behavior 到 keep in mind:

Matching hooks 从 multiple files all 运行.

Multiple matching command hooks for the same event are launched concurrently, so one hook cannot prevent another matching hook 从 starting.

Non-managed command hooks must be reviewed 和 trusted 之前 they 运行.

PreToolUse, PermissionRequest, PostToolUse, PreCompact, PostCompact, UserPromptSubmit, SubagentStop, 和 Stop 运行 at turn scope。SessionStart 和 SubagentStart 运行 at thread 或 subagent-start scope.

Codex 在哪里查找 hooks

Codex discovers hooks next 到 active config layers in either of these forms:

hooks.json

inline [hooks] tables inside config.toml

Installed plugins can also bundle lifecycle config through their plugin manifest 或 a 默认 hooks/hooks.json file。See Build plugins for the plugin packaging rules.

In practice, the four most useful locations are:

~/.codex/hooks.json

~/.codex/config.toml

<repo>/.codex/hooks.json

<repo>/.codex/config.toml

如果 多个 hook source exists, Codex loads all matching hooks。Higher-precedence config layers don’t replace lower-precedence hooks。如果 a single layer contains both hooks.json 和 inline [hooks], Codex merges them 和 warns at startup。Prefer one representation per layer.

Codex can also discover hooks bundled 使用 enabled plugins。Plugin-bundled hooks load alongside other hook sources 和 使用 the same trust-review flow as other non-managed hooks.

Project-local hooks load only 当 the project .codex/ layer is trusted。In untrusted projects, Codex still loads user 和 system hooks 从 their own active config layers.

审查并信任 hooks

Codex lists configured hooks 之前 deciding which ones can 运行。Before a non-managed command hook can 运行, Codex requires you 到 检查 和 trust the exact hook definition。Codex records trust against the hook’s 当前 hash, so 新的 或 changed hooks are marked for 检查 和 skipped until trusted.

使用 /hooks in the CLI 到 inspect hook sources, 检查 新的 或 changed hooks, trust hooks, 或 disable individual non-managed hooks。如果 hooks need 检查 at startup, Codex prints a warning that tells you 到 打开 /hooks.

Managed hooks 从 system, MDM, cloud, 或 requirements.toml sources are marked as managed, trusted by policy, 和 can’t be disabled 从 the user hook browser.

For one-off automation that already vets hook sources outside Codex, pass --dangerously-bypass-hook-trust 到 运行 enabled hooks 不使用 requiring 持久化ed hook trust for that invocation.

配置结构

Hooks are organized in three levels:

A hook event such as PreToolUse, PostToolUse, PreCompact, SubagentStart, 或 Stop

A matcher group that decides 当 that event matches

One 或 more hook handlers that 运行 当 the matcher group matches

{
  "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
          }
        ]
      }
    ]
  }
}

Notes:

timeout is in seconds.

如果 timeout is omitted, Codex uses 600 seconds.

statusMessage is 可选.

commandWindows is an 可选 Windows-only command override。In TOML, 使用 command_windows 或 commandWindows.

async is parsed, but async command hooks aren’t 支持的 yet。Codex skips handlers 使用 async:true.

Only 输入:"command" handlers 运行 today。prompt 和 agent handlers are parsed but skipped.

Commands 运行 使用 session cwd as their working directory.

For repo-local hooks, prefer resolving 从 the git root instead of using a relative path such as .codex/hooks/.... Codex may be started from a subdirectory, 和 a git-root-based path keeps the hook location stable.

Equivalent inline TOML in config.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 的 managed hooks

Enterprise-managed requirements can also define hooks inline under [hooks]。This is useful 当 admins want 到 enforce the hook configuration while delivering the actual scripts through MDM 或 another device-management system。To enforce managed hooks even for users who disabled hooks locally, pin [features].hooks = true in requirements.toml alongside [hooks]。To ignore user, project, session, 和 plugin hooks while still allowing administrator 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"

Notes for managed hooks:

managed_dir is used on macOS 和 Linux.

windows_managed_dir is used on Windows.

Codex doesn’t distribute the scripts in managed_dir;your enterprise tooling must 安装 和 update them separately.

Managed hook commands should 使用 absolute script paths under the configured managed directory.

allow_managed_hooks_only = true skips hooks 从 user, project, session, 和 plugin sources, but still loads managed hooks 从 requirements.toml 和 other managed config layers.

插件内置 hooks

当 a plugin is enabled, Codex can load lifecycle hooks 从 that plugin alongside user, project, 和 managed hooks.

By 默认, Codex looks for hooks/hooks.json inside the plugin root。A plugin manifest can override that 默认 使用 a hooks entry in .codex-plugin/plugin.json。The manifest entry can be a ./-prefixed path, an array of ./-prefixed paths, an inline hooks object, 或 an array of inline hooks objects.

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

Manifest hook paths are resolved relative 到 the plugin root 和 must stay inside that root。如果 a manifest defines hooks, Codex uses those manifest entries 而不是 the 默认 hooks/hooks.json.

Plugin hook commands receive these environment variables:

PLUGIN_ROOT is a Codex-specific extension that points 到 the installed plugin root.

PLUGIN_DATA is a Codex-specific extension that points 到 the plugin’s writable data directory.

Codex also sets CLAUDE_PLUGIN_ROOT 和 CLAUDE_PLUGIN_DATA for compatibility 使用 已有 plugin hooks.

Plugin hooks 使用 the same event schema as other hooks。Installing 或 enabling a plugin doesn’t 自动 trust its hooks;Codex skips plugin-bundled hooks until you 检查 和 trust the 当前 hook definition.

Matcher patterns

The matcher field is a regex string that filters 当 hooks fire。使用 "*", "", 或 omit matcher entirely 到 match every occurrence of a 支持的 event.

Only some 当前 Codex events honor matcher:

EventWhat matcher filtersNotes
PermissionRequesttool nameSupport includes Bash, apply_patch*, 和 MCP tool names
PostToolUsetool nameSupport includes Bash, apply_patch*, 和 MCP tool names
PostCompactcompaction triggerValues are manual 或 auto
PreCompactcompaction triggerValues are manual 或 auto
PreToolUsetool nameSupport includes Bash, apply_patch*, 和 MCP tool names
SessionStart启动 sourceValues are startup, 恢复, clear, 和 compact
SubagentStartsubagent 输入Values depend on the subagent that starts
SubagentStopsubagent 输入Values depend on the subagent that stops
UserPromptSubmitnot 支持的Any configured matcher is ignored for this event
Stopnot 支持的Any configured matcher is ignored for this event

*For apply_patch, matcher values can also 使用 Edit 或 写入.

Examples:

Bash

^apply_patch$

Edit|写入

mcp__filesystem__read_file

mcp__filesystem__.*

startup|恢复|clear|compact

manual|auto

通用输入字段

Every command hook receives one JSON object on stdin.

These are the shared fields you will usually 使用:

Field输入Meaning
session_idstringCurrent Codex session id。Subagent hooks 使用 the parent session id.
transcript_pathstring / nullPath 到 session transcript file, 如果 any
cwdstringWorking directory for session
hook_event_namestringCurrent hook event name
modelstringCodex-specific extension。Active model slug

Turn-scoped hooks 列出 turn_id as a Codex-specific extension in their event-specific tables.

SessionStart, PreToolUse, PermissionRequest, PostToolUse, UserPromptSubmit, SubagentStart, SubagentStop, 和 Stop also include permission_mode, which describes the 当前 permission mode as 默认, acceptEdits, plan, dontAsk, 或 bypassPermissions.

transcript_path points 到 a conversation transcript for convenience, but the transcript format is not a stable interface for hooks 和 may change over time.

如果 you need the full wire format, see Schemas .

通用输出字段

SessionStart, PreCompact, PostCompact, UserPromptSubmit, SubagentStop, 和 Stop support these shared JSON fields。SubagentStart accepts the same shape for systemMessage 和 hook-specific context, but continue:false doesn’t stop the subagent:

{
  "continue": true,
  "stopReason": "optional",
  "systemMessage": "optional",
  "suppressOutput": false
}
FieldEffect
continue如果 false, marks that hook 运行 as stopped
stopReasonRecorded as the reason for stopping
systemMessageSurfaced as a warning in the UI 或 event stream
suppressOutputParsed today but not yet implemented

Exit 0 使用 no output is treated as success 和 Codex continues.

PreToolUse 和 PermissionRequest support systemMessage, but continue, stopReason, 和 suppressOutput aren’t currently 支持的 for those events。如果 a PreToolUse hook returns one of those un支持的 fields, Codex marks that hook 运行 as failed, reports the error, 和 continues the tool call.

PostToolUse supports systemMessage, continue:false, 和 stopReason。suppressOutput is parsed but not currently 支持的 for that event.

Hooks

SessionStart

matcher is applied 到 source for this event.

Fields in addition 到 Common input fields :

Field输入Meaning
sourcestringHow session started:startup, 恢复, clear, 或 compact

Plain text on stdout is added as extra developer context.

JSON on stdout supports Common output fields 和 this hook-specific shape:

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

That additionalContext text is added as extra developer context.

SubagentStart

matcher is applied 到 agent_type for this event.

Fields in addition 到 Common input fields :

Field输入Meaning
turn_idstringCodex-specific extension。Active Codex turn id
agent_idstringIdentifier for the subagent
agent_typestringSubagent 输入 或 profile
permission_modestringCurrent permission mode

Plain text on stdout is added as extra developer context for the subagent.

JSON on stdout supports systemMessage 和 this hook-specific shape:

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

That additionalContext text is added as extra developer context for the subagent。continue:false is parsed for compatibility, but it doesn’t stop the subagent 从 starting.

PreToolUse

PreToolUse can intercept Bash, file edits performed through apply_patch, 和 MCP tool calls。It’s still a guardrail rather than a complete enforcement boundary because Codex can often perform equivalent work through another 支持的 tool path.

This doesn’t intercept all shell calls yet, only the simple ones。The newer unified_exec mechanism allows richer streaming stdin/stdout handling of shell, but interception is incomplete。Similarly, this doesn’t intercept WebSearch 或 other non-shell, non-MCP tool calls.

matcher is applied 到 tool_name 和 matcher aliases。For file edits through apply_patch, matcher values can 使用 apply_patch, Edit, 或 写入;hook input still reports tool_name:"apply_patch".

Fields in addition 到 Common input fields :

Field输入Meaning
turn_idstringCodex-specific extension。Active Codex turn id
tool_namestringCanonical hook tool name, such as Bash, apply_patch, 或 an MCP name like mcp__fs__读取
tool_use_idstringTool-call id for this invocation
tool_inputJSON valueTool-specific input。Bash 和 apply_patch 使用 tool_input.command while MCP tools send all arguments.

Plain text on stdout is ignored.

JSON on stdout can 使用 systemMessage。To deny a 支持的 tool call, return this hook-specific shape:

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

Codex also accepts this older block shape:

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

You can also 使用 exit code 2 和 写入 the blocking reason 到 stderr.

To 添加 model-visible context 不使用 blocking, return hookSpecificOutput.additionalContext:

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

To rewrite a 支持的 tool call 不使用 blocking, return permissionDecision:"allow" 使用 updatedInput:

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

For Bash commands 和 apply_patch, updatedInput must include a string command field。For MCP tools, updatedInput is the replacement arguments object。Return updatedInput only 使用 permissionDecision:"allow";other updatedInput shapes are reported as errors.

permissionDecision:"ask", legacy decision:"approve", continue:false, stopReason, 和 suppressOutput are parsed but not 支持的 yet。Codex marks the hook 运行 as failed, reports the error, 和 continues the tool call.

PermissionRequest

PermissionRequest runs 当 Codex is about 到 ask for approval, such as a shell escalation 或 managed-network approval。It can allow the request, deny the request, 或 decline 到 decide 和 let the normal approval prompt continue。It doesn’t 运行 for commands that don’t need approval.

matcher is applied 到 tool_name 和 matcher aliases。Current canonical values include Bash, apply_patch, 和 MCP tool names such as mcp__server__tool;apply_patch also matches Edit 和 写入.

Fields in addition 到 Common input fields :

Field输入Meaning
turn_idstringCodex-specific extension。Active Codex turn id
tool_namestringCanonical hook tool name, such as Bash, apply_patch, 或 an MCP name like mcp__fs__读取
tool_inputJSON valueTool-specific input。Bash 和 apply_patch 使用 tool_input.command while MCP tools send all the args.
tool_input.descriptionstring / nullHuman-readable approval reason, 当 Codex has one

Plain text on stdout is ignored.

Some tool inputs may include a human-readable description, but don’t rely on a tool_input.description field for every tool.

To approve the request, return:

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

To deny the request, return:

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

如果 multiple matching hooks return decisions, any deny wins。Otherwise, an allow lets the request proceed 不使用 surfacing the approval prompt。如果 no matching hook decides, Codex uses the normal approval flow.

Don’t return updatedInput, updatedPermissions, 或 interrupt for PermissionRequest;those fields are reserved for future behavior 和 fail closed today.

PostToolUse

PostToolUse runs 之后 支持的 tools produce output, including Bash, apply_patch, 和 MCP tool calls。For Bash, it also runs 之后 commands that exit 使用 a non-zero status。It can’t undo side effects 从 the tool that already ran.

This doesn’t intercept all shell calls yet, only the simple ones。The newer unified_exec mechanism allows richer streaming stdin/stdout handling of shell, but interception is incomplete。Similarly, this doesn’t intercept WebSearch 或 other non-shell, non-MCP tool calls.

matcher is applied 到 tool_name 和 matcher aliases。For file edits through apply_patch, matcher values can 使用 apply_patch, Edit, 或 写入;hook input still reports tool_name:"apply_patch".

Fields in addition 到 Common input fields :

Field输入Meaning
turn_idstringCodex-specific extension。Active Codex turn id
tool_namestringCanonical hook tool name, such as Bash, apply_patch, 或 an MCP name like mcp__fs__读取
tool_use_idstringTool-call id for this invocation
tool_inputJSON valueTool-specific input。Bash 和 apply_patch 使用 tool_input.command while MCP tools send all arguments.
tool_responseJSON valueTool-specific output。For MCP tools, this is the MCP call result.

Plain text on stdout is ignored.

JSON on stdout can 使用 systemMessage 和 this hook-specific shape:

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

That additionalContext text is added as extra developer context.

For this event, decision:"block" doesn’t undo the completed Bash command。Instead, Codex records the feedback, replaces the tool result 使用 that feedback, 和 continues the model 从 the hook-provided message.

You can also 使用 exit code 2 和 写入 the feedback reason 到 stderr.

To stop normal processing of the original tool result 之后 command has already 运行, return continue:false。Codex will replace the tool result 使用 your feedback 或 stop text 和 continue 从 there.

updatedMCPToolOutput 和 suppressOutput are parsed but not 支持的 yet。Codex marks the hook 运行 as failed, reports the error, 和 continues normal processing of the tool result.

PreCompact

PreCompact runs 之前 Codex compacts the conversation。matcher is applied 到 trigger, whose values are manual 和 auto.

Fields in addition 到 Common input fields :

Field输入Meaning
turn_idstringCodex-specific extension。Active Codex turn id
triggerstringWhat triggered compaction:manual 或 auto

Plain text on stdout is ignored.

JSON on stdout supports Common output fields 。如果 a matching PreCompact hook returns continue:false, Codex stops 之前 compacting.

PostCompact

PostCompact runs 之后 Codex compacts the conversation。matcher is applied 到 trigger, whose values are manual 和 auto.

Fields in addition 到 Common input fields :

Field输入Meaning
turn_idstringCodex-specific extension。Active Codex turn id
triggerstringWhat triggered compaction:manual 或 auto

Plain text on stdout is ignored.

JSON on stdout supports Common output fields 。如果 a matching PostCompact hook returns continue:false, Codex stops 之后 compacting.

UserPromptSubmit

matcher isn’t currently used for this event.

Fields in addition 到 Common input fields :

Field输入Meaning
turn_idstringCodex-specific extension。Active Codex turn id
promptstringUser prompt that’s about 到 be sent

Plain text on stdout is added as extra developer context.

JSON on stdout supports Common output fields 和 this hook-specific shape:

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

That additionalContext text is added as extra developer context.

To block prompt, return:

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

You can also 使用 exit code 2 和 写入 the blocking reason 到 stderr.

SubagentStop

matcher is applied 到 agent_type for this event.

Fields in addition 到 Common input fields :

Field输入Meaning
turn_idstringCodex-specific extension。Active Codex turn id
agent_idstringIdentifier for the subagent
agent_typestringSubagent 输入 或 profile
agent_transcript_pathstring / nullPath 到 the subagent transcript file, 如果 any
stop_hook_activebooleanWhether this subagent was already continued
last_assistant_messagestring / nullLatest subagent assistant message, 如果 可用

SubagentStop expects JSON on stdout 当 it exits 0。Plain text output is invalid for this event.

JSON on stdout supports Common output fields 。To ask Codex 到 continue the subagent flow, return:

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

You can also 使用 exit code 2 和 写入 the continuation reason 到 stderr.

如果 any matching SubagentStop hook returns continue:false, that takes precedence over continuation decisions 从 other matching SubagentStop hooks.

Stop

matcher isn’t currently used for this event.

Fields in addition 到 Common input fields :

Field输入Meaning
turn_idstringCodex-specific extension。Active Codex turn id
stop_hook_activebooleanWhether this turn was already continued by Stop
last_assistant_messagestring / nullLatest assistant message text, 如果 可用

Stop expects JSON on stdout 当 it exits 0。Plain text output is invalid for this event.

JSON on stdout supports Common output fields 。To keep Codex going, return:

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

You can also 使用 exit code 2 和 写入 the continuation reason 到 stderr.

For this event, decision:"block" doesn’t reject the turn。Instead, it tells Codex 到 continue 和 自动 creates a 新的 continuation prompt that acts as a 新的 user prompt, using your reason as that prompt text.

如果 any matching Stop hook returns continue:false, that takes precedence over continuation decisions 从 other matching Stop hooks.

Schemas

The linked main branch schemas may include hook fields that are not in the 当前 release。使用 this page as the release behavior reference.

如果 you need the exact 当前 wire format, see the generated schemas in the Codex GitHub repository .

站内延伸阅读