mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
hooks: emit Bash PostToolUse when exec_command completes via write_stdin (#18888)
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`
This commit is contained in:
committed by
GitHub
Unverified
parent
6ca038bbd1
commit
eed0e07825
@@ -148,7 +148,7 @@ struct ProcessEntry {
|
||||
process: Arc<UnifiedExecProcess>,
|
||||
call_id: String,
|
||||
process_id: i32,
|
||||
command: Vec<String>,
|
||||
hook_command: String,
|
||||
tty: bool,
|
||||
network_approval_id: Option<String>,
|
||||
session: Weak<Session>,
|
||||
|
||||
@@ -113,7 +113,7 @@ async fn exec_command_with_tty(
|
||||
process: Arc::clone(&process),
|
||||
call_id: context.call_id.clone(),
|
||||
process_id,
|
||||
command: command.clone(),
|
||||
hook_command: cmd.to_string(),
|
||||
tty,
|
||||
network_approval_id: None,
|
||||
session: Arc::downgrade(session),
|
||||
@@ -165,7 +165,7 @@ async fn exec_command_with_tty(
|
||||
process_id: response_process_id,
|
||||
exit_code,
|
||||
original_token_count: Some(approx_token_count(&text)),
|
||||
session_command: Some(command),
|
||||
hook_command: Some(cmd.to_string()),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -169,7 +169,7 @@ struct PreparedProcessHandles {
|
||||
output_closed_notify: Arc<Notify>,
|
||||
cancellation_token: CancellationToken,
|
||||
pause_state: Option<watch::Receiver<bool>>,
|
||||
command: Vec<String>,
|
||||
hook_command: String,
|
||||
process_id: i32,
|
||||
tty: bool,
|
||||
}
|
||||
@@ -279,6 +279,7 @@ impl UnifiedExecProcessManager {
|
||||
Arc::clone(&process),
|
||||
context,
|
||||
&request.command,
|
||||
request.hook_command.clone(),
|
||||
cwd.clone(),
|
||||
start,
|
||||
request.process_id,
|
||||
@@ -398,7 +399,7 @@ impl UnifiedExecProcessManager {
|
||||
process_id: response_process_id,
|
||||
exit_code,
|
||||
original_token_count: Some(original_token_count),
|
||||
session_command: Some(request.command.clone()),
|
||||
hook_command: Some(request.hook_command.clone()),
|
||||
};
|
||||
|
||||
Ok(response)
|
||||
@@ -418,7 +419,7 @@ impl UnifiedExecProcessManager {
|
||||
output_closed_notify,
|
||||
cancellation_token,
|
||||
pause_state,
|
||||
command: session_command,
|
||||
hook_command,
|
||||
process_id,
|
||||
tty,
|
||||
..
|
||||
@@ -517,7 +518,7 @@ impl UnifiedExecProcessManager {
|
||||
process_id,
|
||||
exit_code,
|
||||
original_token_count: Some(original_token_count),
|
||||
session_command: Some(session_command.clone()),
|
||||
hook_command: Some(hook_command),
|
||||
};
|
||||
|
||||
Ok(response)
|
||||
@@ -585,7 +586,7 @@ impl UnifiedExecProcessManager {
|
||||
output_closed_notify,
|
||||
cancellation_token,
|
||||
pause_state,
|
||||
command: entry.command.clone(),
|
||||
hook_command: entry.hook_command.clone(),
|
||||
process_id: entry.process_id,
|
||||
tty: entry.tty,
|
||||
})
|
||||
@@ -597,6 +598,7 @@ impl UnifiedExecProcessManager {
|
||||
process: Arc<UnifiedExecProcess>,
|
||||
context: &UnifiedExecContext,
|
||||
command: &[String],
|
||||
hook_command: String,
|
||||
cwd: AbsolutePathBuf,
|
||||
started_at: Instant,
|
||||
process_id: i32,
|
||||
@@ -608,7 +610,7 @@ impl UnifiedExecProcessManager {
|
||||
process: Arc::clone(&process),
|
||||
call_id: context.call_id.clone(),
|
||||
process_id,
|
||||
command: command.to_vec(),
|
||||
hook_command,
|
||||
tty,
|
||||
network_approval_id,
|
||||
session: Arc::downgrade(&context.session),
|
||||
|
||||
Reference in New Issue
Block a user