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:
| Event | What matcher filters | Notes |
|---|---|---|
| PermissionRequest | tool name | Support includes Bash, apply_patch*, 和 MCP tool names |
| PostToolUse | tool name | Support includes Bash, apply_patch*, 和 MCP tool names |
| PostCompact | compaction trigger | Values are manual 或 auto |
| PreCompact | compaction trigger | Values are manual 或 auto |
| PreToolUse | tool name | Support includes Bash, apply_patch*, 和 MCP tool names |
| SessionStart | 启动 source | Values are startup, 恢复, clear, 和 compact |
| SubagentStart | subagent 输入 | Values depend on the subagent that starts |
| SubagentStop | subagent 输入 | Values depend on the subagent that stops |
| UserPromptSubmit | not 支持的 | Any configured matcher is ignored for this event |
| Stop | not 支持的 | 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_id | string | Current Codex session id。Subagent hooks 使用 the parent session id. |
| transcript_path | string / null | Path 到 session transcript file, 如果 any |
| cwd | string | Working directory for session |
| hook_event_name | string | Current hook event name |
| model | string | Codex-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
} | Field | Effect |
|---|---|
| continue | 如果 false, marks that hook 运行 as stopped |
| stopReason | Recorded as the reason for stopping |
| systemMessage | Surfaced as a warning in the UI 或 event stream |
| suppressOutput | Parsed 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 |
|---|---|---|
| source | string | How 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_id | string | Codex-specific extension。Active Codex turn id |
| agent_id | string | Identifier for the subagent |
| agent_type | string | Subagent 输入 或 profile |
| permission_mode | string | Current 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_id | string | Codex-specific extension。Active Codex turn id |
| tool_name | string | Canonical hook tool name, such as Bash, apply_patch, 或 an MCP name like mcp__fs__读取 |
| tool_use_id | string | Tool-call id for this invocation |
| tool_input | JSON value | Tool-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_id | string | Codex-specific extension。Active Codex turn id |
| tool_name | string | Canonical hook tool name, such as Bash, apply_patch, 或 an MCP name like mcp__fs__读取 |
| tool_input | JSON value | Tool-specific input。Bash 和 apply_patch 使用 tool_input.command while MCP tools send all the args. |
| tool_input.description | string / null | Human-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_id | string | Codex-specific extension。Active Codex turn id |
| tool_name | string | Canonical hook tool name, such as Bash, apply_patch, 或 an MCP name like mcp__fs__读取 |
| tool_use_id | string | Tool-call id for this invocation |
| tool_input | JSON value | Tool-specific input。Bash 和 apply_patch 使用 tool_input.command while MCP tools send all arguments. |
| tool_response | JSON value | Tool-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_id | string | Codex-specific extension。Active Codex turn id |
| trigger | string | What 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_id | string | Codex-specific extension。Active Codex turn id |
| trigger | string | What 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_id | string | Codex-specific extension。Active Codex turn id |
| prompt | string | User 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_id | string | Codex-specific extension。Active Codex turn id |
| agent_id | string | Identifier for the subagent |
| agent_type | string | Subagent 输入 或 profile |
| agent_transcript_path | string / null | Path 到 the subagent transcript file, 如果 any |
| stop_hook_active | boolean | Whether this subagent was already continued |
| last_assistant_message | string / null | Latest 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_id | string | Codex-specific extension。Active Codex turn id |
| stop_hook_active | boolean | Whether this turn was already continued by Stop |
| last_assistant_message | string / null | Latest 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 .