This document captures the current Codex hooks surface and the remaining
feature-parity gap versus Claude Code's documented hooks system. It is intended
to be the canonical planning doc for expanding codex_hooks.
Bring Codex's public hooks.json lifecycle hooks close enough to Claude
Code's model that Claude-oriented hook setups can be ported with predictable,
documented edits rather than custom runtime patches.
This does not require byte-for-byte compatibility in one step. It does require:
- matching the major public event categories users expect,
- supporting the handler types those configurations rely on,
- honoring documented decision-control fields when they are accepted by schema,
- documenting any intentional deltas that remain.
If you are implementing against this doc, read the current source in this order:
docs/claude-code-hooks-parity.mdcodex-rs/hooks/src/engine/config.rscodex-rs/hooks/src/engine/discovery.rscodex-rs/hooks/src/schema.rscodex-rs/hooks/src/engine/output_parser.rscodex-rs/core/src/hook_runtime.rscodex-rs/core/src/codex.rscodex-rs/core/src/tools/registry.rs
This order moves from public contract to discovery, then schema, then parser, then runtime wiring, then legacy behavior.
This doc is based on the current implementation shape in this checkout:
- public
hooks.jsonevent groups are defined incodex-rs/hooks/src/engine/config.rs, - handler discovery and unsupported-handler warnings live in
codex-rs/hooks/src/engine/discovery.rs, - public wire schema lives in
codex-rs/hooks/src/schema.rs, - output acceptance and rejection behavior lives in
codex-rs/hooks/src/engine/output_parser.rs, - runtime dispatch for start, prompt-submit, pre-tool, and post-tool hooks
lives in
codex-rs/core/src/hook_runtime.rs, Stophook wiring lives incodex-rs/core/src/codex.rs,- deprecated legacy
AfterToolUsedispatch still exists incodex-rs/core/src/tools/registry.rs, - no repository-local
hooks.jsonfiles are checked into this tree today.
Today Codex exposes five public hooks.json event groups:
PreToolUsePostToolUseSessionStartUserPromptSubmitStop
The current engine only executes synchronous command handlers. prompt,
agent, and async configurations are parsed but skipped with warnings.
The current runtime also has narrower execution coverage than Claude Code:
PreToolUseandPostToolUseare currently wired through the shell path, with runtime requests usingtool_name: "Bash".UserPromptSubmitandStopignore matchers.- some wire fields are present in schema but are rejected by the output parser as unsupported.
Legacy internal paths still exist for notification-style hooks
(AfterAgent / deprecated AfterToolUse), but they are not part of the
public hooks.json contract.
Claude Code's current hooks reference documents a larger event surface and more handler modes than Codex currently supports.
Codex does not yet expose public hooks.json support for these documented
Claude Code event families:
InstructionsLoadedPermissionRequestPostToolUseFailureNotificationSubagentStartSubagentStopStopFailureTeammateIdleTaskCompletedConfigChangeCwdChangedFileChangedWorktreeCreateWorktreeRemovePreCompactPostCompactSessionEndElicitationElicitationResult
Codex does not yet support these Claude Code hook handler categories in the public engine:
- async command hooks,
- HTTP hooks,
- prompt hooks,
- agent hooks.
Codex schema already models some advanced fields, but runtime support is still partial:
PreToolUse.updatedInputis rejected.PreToolUse.additionalContextis rejected.PreToolUse.permissionDecision: allowis rejected.PreToolUse.permissionDecision: askis rejected.PostToolUse.updatedMCPToolOutputis rejected.suppressOutputis rejected forPreToolUseandPostToolUse.stopReasonandcontinue: falseare rejected forPreToolUse.
This creates a confusing state where the schema shape suggests broader support than the runtime actually honors.
PreToolUseandPostToolUseshould evolve from shell-centric wiring to a consistent tool-event contract across relevant tool classes.- matcher support should be explicit and consistent across all events that Claude users expect to filter.
- MCP-aware hook behavior should be designed as first-class runtime behavior, not as a schema placeholder.
- Reproducing Claude Code internals exactly where Codex architecture differs.
- Preserving every existing partial or deprecated behavior forever.
- Adding public hook types without app-server, TUI, and docs visibility for the resulting runs.
- Public contract first: do not expose schema fields that the runtime will immediately reject unless they are clearly marked unsupported.
- Event completeness over aliases: add real lifecycle events before adding compatibility shims.
- One event, one payload contract: every public event needs stable input and output schema fixtures, runtime execution, and surfaced hook-run reporting.
- Fail-open unless explicitly blocking: invalid hook output should not cause surprising hard failures outside events whose contract is intentionally blocking.
- No hidden UI drift: hook additions must be visible in the TUI and app-server surfaces anywhere hook runs are rendered today.
- Do not add a new public event without input schema, runtime dispatch, hook-run reporting, and docs in the same lane.
- Do not keep wire fields in public schema as if they are live when the parser still rejects them.
- Do not use deprecated
AfterAgentor legacyAfterToolUseinternals as the long-term public parity path. - Do not widen event coverage while leaving handler type and execution mode reporting misleading in run summaries.
- Do not make hook support TUI-only; app-server and protocol surfaces must stay aligned.
Prefer this implementation order:
- contract cleanup for the existing five events,
- runtime event expansion on the command-hook engine,
- handler-type and execution-mode expansion,
- advanced decision-control support,
- pre/post tool-class parity work,
- final doc consolidation and examples.
Do not mix all six into one change. Keep each lane reviewable.
Goal: remove misleading partial support inside the existing five events.
Required work:
- align schema and parser behavior for the five existing events,
- either implement or remove unsupported schema fields that are already emitted in fixtures,
- document matcher behavior explicitly,
- document current shell-centric tool coverage explicitly,
- add a dedicated user-facing reference doc for
hooks.jsonbehavior if the main docs site still only mentions legacy notification hooks.
Acceptance:
- no schema field is silently accepted but runtime-rejected without explicit documentation,
- the docs explain exactly which event fields and decisions are live,
- existing five-event behavior is covered by tests and schema fixtures.
Goal: add missing lifecycle events before broadening handler types.
Priority order:
PermissionRequestNotificationSubagentStartandSubagentStopPostToolUseFailureandStopFailureSessionEndConfigChange,CwdChanged, andFileChangedPreCompactandPostCompactTaskCompletedandTeammateIdleInstructionsLoadedWorktreeCreateandWorktreeRemoveElicitationandElicitationResult
Acceptance:
- each event has an input schema fixture,
- each event has runtime dispatch wiring,
- each event emits
HookStartedandHookCompletedconsistently, - each event has an explicit matcher story,
- docs list the event as supported.
Goal: match the main Claude Code hook execution modes.
Required work:
- implement async command hooks,
- add HTTP hook handlers,
- add prompt hook handlers,
- add agent hook handlers,
- surface handler type and execution mode accurately in run summaries.
Acceptance:
- discovery no longer skips supported handler types with warnings,
HookRunSummaryreports real handler type and execution mode,- command, HTTP, prompt, and agent handlers have stable input/output contracts,
- async execution semantics are documented, especially ordering and failure behavior.
Goal: implement or explicitly drop advanced output fields.
Required work:
- decide whether
PreToolUse.updatedInputwill be supported in Codex, - decide whether
PreToolUse.permissionDecision: askmaps to an approval prompt, a model-visible continuation, or remains unsupported, - implement
additionalContextanywhere the contract claims it exists, - decide whether
PostToolUse.updatedMCPToolOutputis part of the public runtime contract, - review event-specific
continue,stopReason, andsuppressOutputsemantics for consistency.
Acceptance:
- advanced hook output fields are either implemented end-to-end or removed from public schema,
- runtime behavior matches docs and tests,
- no event-specific decision-control behavior relies on undocumented parser special cases.
Goal: make tool hooks genuinely tool-aware rather than shell-specific.
Required work:
- define which Codex tool classes participate in
PreToolUseandPostToolUse, - expose stable tool identifiers and input payloads for those classes,
- define MCP-tool matcher behavior explicitly,
- preserve backward compatibility for existing Bash-oriented hooks where feasible.
Acceptance:
- users can target more than the shell path with pre/post tool hooks,
- tool names and payloads are documented and stable,
- MCP tool behavior is implemented rather than placeholder-only.
- update docs under
docs/when public behavior changes, - keep generated schema fixtures in sync,
- extend TUI and app-server visibility for new hook events when needed,
- add focused tests for parser behavior, discovery behavior, and runtime dispatch,
- decide whether legacy notification hooks remain supported long term or are explicitly deprecated in docs.
Every parity PR should satisfy all of these before merge:
- docs updated for the newly supported behavior,
- generated hook schema fixtures updated if the public schema changed,
- focused tests added or updated for discovery, parser, and runtime behavior,
- hook run summaries still render correctly in TUI and app-server surfaces,
- unsupported behavior is either removed from schema or clearly documented as unsupported.
- Should Codex aim for Claude-compatible field names and semantics wherever possible, or only for event-name parity?
- Should prompt and agent hooks be first-class in the initial public contract, or stay experimental behind feature flags after implementation?
- Should unsupported advanced fields be removed now to reduce confusion, or kept in schema as forward-compatibility placeholders?
- Which events should be thread-scoped versus turn-scoped in app-server and TUI reporting?
If this work is started incrementally, the highest-leverage first slice is:
- publish a real user-facing hooks reference for Codex,
- make the existing five events internally coherent,
- add
PermissionRequest,Notification,SubagentStart,SubagentStop, andSessionEnd, - then add async and HTTP handler support.
That sequence closes the largest user-visible parity gaps without mixing event expansion, execution-model expansion, and advanced mutation semantics into one hard-to-review change.