Skip to content

hooks: emit Bash PostToolUse when exec_command completes via write_stdin#18888

Merged
eternal-openai merged 5 commits intomainfrom
eternal/hooks_unified_exec_post_tool_use
Apr 23, 2026
Merged

hooks: emit Bash PostToolUse when exec_command completes via write_stdin#18888
eternal-openai merged 5 commits intomainfrom
eternal/hooks_unified_exec_post_tool_use

Conversation

@eternal-openai
Copy link
Copy Markdown
Contributor

@eternal-openai eternal-openai commented Apr 21, 2026

Fixes #16246.

Why

exec_command already emits PreToolUse, but long-running unified exec commands that finish on a later write_stdin poll could miss the matching PostToolUse. That left the Bash hook lifecycle inconsistent, broke expectations around tool_use_id and tool_input.command, and meant PostToolUse block/replacement feedback could fail to replace the final session output before it reached model context.

This keeps the fix scoped to the exec_command / write_stdin lifecycle. Broader non-Bash hook expansion is still out of scope here and remains tracked separately in #16732.

What changed

  • Compute and store PostToolUsePayload while handlers still have access to their concrete output type, and carry tool_use_id through that payload.
  • Preserve the original hook-facing exec_command string through unified exec state (ExecCommandRequest, ProcessEntry, PreparedProcessHandles, and ExecCommandToolOutput) via hook_command, and remove the now-unused session_command output metadata.
  • Emit exactly one Bash PostToolUse for long-running exec_command sessions when a later write_stdin poll observes final completion, using the original exec_command call id and hook-facing command.
  • Keep one-shot exec_command behavior aligned with the same payload construction, including interactive completions that return a final result directly.
  • Apply PostToolUse block/replacement feedback before the final write_stdin completion output is sent back to the model.
  • Keep write_stdin itself out of PreToolUse matching so it continues to act as transport/polling for the original Bash tool call.
  • Restore plain matcher behavior for tool-name matchers such as Bash and Edit|Write, while still treating patterns with regex characters (for example mcp__.*) as regexes.
  • Add unit coverage for unified exec payload construction and parallel session separation, plus a core integration regression that verifies a blocked PostToolUse replaces the final write_stdin output in model context.

Testing

  • cargo test -p codex-hooks
  • cargo test -p codex-core post_tool_use_payload
  • cargo test -p codex-core post_tool_use_blocks_when_exec_session_completes_via_write_stdin

# Conflicts:
#	codex-rs/core/src/tools/handlers/unified_exec.rs
#	codex-rs/core/src/tools/registry.rs
#	codex-rs/core/src/unified_exec/mod.rs
@eternal-openai eternal-openai changed the title Eternal/hooks unified exec post tool use hooks: emit Bash PostToolUse when exec_command completes via write_stdin Apr 21, 2026
@eternal-openai eternal-openai marked this pull request as ready for review April 22, 2026 00:45
@eternal-openai eternal-openai requested a review from a team as a code owner April 22, 2026 00:45
Comment thread codex-rs/hooks/src/events/common.rs

let tool_response = result.post_tool_use_response(call_id, payload)?;
let command = result.hook_command.clone()?;
let tool_use_id = if result.event_call_id.is_empty() {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[question] when would this be empty?

and isn't it the same as call_id?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, good ask - I think you're right that this should never happen. It’s only there as a defensive fallback because ExecCommandToolOutput has one synthetic constructor path (intercept_apply_patch) that doesn’t participate in the normal unified-exec lifecycle and leaves event_call_id empty. That path also never produces a PostToolUse payload, but I'll leave it in just in case

Copy link
Copy Markdown
Collaborator

@abhinav-oai abhinav-oai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 🚢

@eternal-openai eternal-openai merged commit eed0e07 into main Apr 23, 2026
25 checks passed
@eternal-openai eternal-openai deleted the eternal/hooks_unified_exec_post_tool_use branch April 23, 2026 00:14
@github-actions github-actions Bot locked and limited conversation to collaborators Apr 23, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Hooks: PostToolUse is missing for tools that complete via exec session / polling path

3 participants