Function-call apply_patch does not route through apply_patch handler/hooks
Summary
When apply_patch arrives as a Responses function_call rather than a custom_tool_call, Codex does not route it through the existing apply_patch runtime contract. This means hook behavior can diverge from the freeform/custom apply_patch path.
The failing shape is a function call named apply_patch, including the functions. namespace form:
{
"type": "function_call",
"namespace": "functions.",
"name": "apply_patch",
"arguments": "*** Begin Patch\n*** Add File: file.txt\n+patched\n*** End Patch",
"call_id": "call-id"
}
Expected
The call should route to the existing apply_patch handler so that:
- the patch is applied through the same verification/runtime path as custom/freeform
apply_patch - PreToolUse/PostToolUse hooks see
tool_name: apply_patch - function-call payloads still produce
function_call_output, preserving the original wire shape
Observed
A regression test that emits functions.apply_patch as a function_call fails on the current implementation because the PostToolUse additional context is not injected into the follow-up request.
Candidate fix
I pushed a tested branch here:
https://github.com/trotsky1997/codex/compare/main...trotsky1997:fix-apply-patch-hooks
Commit: 5efd838408df9e5c0aa4d8897c4eb404eb80cf06
The patch:
- canonicalizes
apply_patchfunction calls in the router to the existing plainapply_patchtool name - allows the apply_patch handler to consume
ToolPayload::Functionwhile preserving function-call output responses - supports both raw patch arguments and JSON
{ "command": "..." }arguments for hook-facing command extraction - adds router, handler, and suite-level hook coverage
I attempted to open a draft PR, but both gh pr create and the GitHub connector were denied by repository permissions.
Verification
Red/green verified in a WSL scratch checkout: the new suite test fails on the old implementation because PostToolUse context is not injected, then passes with the candidate fix.
Passing commands:
cargo test -p codex-core --lib build_tool_call_canonicalizes_apply_patch_function_calls -- --nocapture
cargo test -p codex-core --lib pre_tool_use_payload_ -- --nocapture
RUST_MIN_STACK=8388608 cargo test -p codex-core --test all post_tool_use_records_additional_context_for_apply_patch_function_call -- --nocapture
RUST_MIN_STACK=8388608 cargo test -p codex-core --test all post_tool_use_records_additional_context_for_apply_patch -- --nocapture
RUST_MIN_STACK=8388608 cargo test -p codex-core --test all pre_tool_use_blocks_apply_patch_before_execution -- --nocapture
cargo test -p codex-core --lib function_tools_expose_default_hook_payloads_and_rewrites -- --nocapture
cargo test -p codex-core --lib spawn_agent_function_tools_use_agent_matcher_alias -- --nocapture
cargo fmt -p codex-core -- --check
git diff --check
4 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Candidate fix branch / compare URL:
https://github.com/openai/codex/compare/main...trotsky1997:fix-apply-patch-hooks
I attempted to open this as a draft PR, but both
gh pr createand the GitHub connector were denied by repository permissions (CreatePullRequest/Resource not accessible by integration). So I cannot submit the PR directly from this environment, but the branch is pushed and ready for maintainers to inspect or open as a PR.Closing as not reproducible. Function-style
apply_patchwas intentionally removed;apply_patchis now exclusively a freeformcustom_tool_call. The proposed regression test constructs an unsupportedfunction_callshape, so it does not represent a supported execution path. If a real session produces this shape, please share the corresponding rollout entry and CLI version/build.I can reproduce the same gap from a current Codex Desktop function-call surface.
Environment:
codex-cli 0.144.6functions.apply_patchcodex features listreportshooks stable trueHook setup:
~/.codex/hooks.jsonPreToolUsematcher includesWrite|Edit|apply_patch|functions.apply_patch|Bash|exec_command|functions.exec_commandControl proving hooks are active for exec in the same session:
functions.exec_commandsmoke containing a rich inlinenode -epayload was blocked with the expected guard message (RICH_INLINE_PAYLOAD_BLOCKED).functions.exec_commandwas also blocked.Failing case:
functions.apply_patchcall adding two temporary files in one patch succeeded.PreToolUseblock did not happen; no guard message surfaced before the files were written.Expected:
functions.apply_patchcall should canonicalize to the existingapply_patchhandler/hook path soPreToolUsesees a matchableapply_patch-style tool name and can block before writing.Why this still matters:
apply_patchsupportsPreToolUse/PostToolUseand can be matched asapply_patch,Edit, orWrite.