Polish hook compatibility cleanup

This commit is contained in:
Abhinav Vedmala
2026-05-06 14:28:12 -07:00
parent ca7a4f3f30
commit be8d719275
6 changed files with 20 additions and 14 deletions

View File

@@ -240,7 +240,11 @@ fn write_permissions_for_paths(
normalize_additional_permissions(permissions).ok()
}
/// Extracts the raw patch text from either supported apply_patch payload form.
/// Extracts the raw patch text used as the command-shaped hook input for apply_patch.
///
/// The apply_patch tool can arrive as the older JSON/function shape or as a
/// freeform custom tool call. Both represent the same file edit operation, so
/// hooks see the raw patch body in `tool_input.command` either way.
pub(crate) fn apply_patch_payload_command(payload: &ToolPayload) -> Option<String> {
match payload {
ToolPayload::Function { arguments } => parse_arguments::<ApplyPatchToolArgs>(arguments)

View File

@@ -16,6 +16,7 @@ use tokio::sync::Mutex;
use crate::session::tests::make_session_and_context;
use crate::tools::context::ToolInvocation;
use crate::tools::hook_compat;
use crate::tools::hook_names::HookToolName;
use crate::tools::registry::PostToolUsePayload;
use crate::tools::registry::PreToolUsePayload;
@@ -50,7 +51,7 @@ async fn pre_tool_use_payload_uses_json_patch_input() {
};
let invocation = invocation_for_payload(payload).await;
assert_eq!(
crate::tools::hook_compat::pre_tool_use_payload(&invocation),
hook_compat::pre_tool_use_payload(&invocation),
Some(PreToolUsePayload {
tool_name: HookToolName::apply_patch(),
tool_input: json!({ "command": patch }),
@@ -66,7 +67,7 @@ async fn pre_tool_use_payload_uses_freeform_patch_input() {
};
let invocation = invocation_for_payload(payload).await;
assert_eq!(
crate::tools::hook_compat::pre_tool_use_payload(&invocation),
hook_compat::pre_tool_use_payload(&invocation),
Some(PreToolUsePayload {
tool_name: HookToolName::apply_patch(),
tool_input: json!({ "command": patch }),

View File

@@ -106,6 +106,7 @@ mod tests {
use super::*;
use crate::session::tests::make_session_and_context;
use crate::tools::context::ToolCallSource;
use crate::tools::hook_compat;
use crate::turn_diff_tracker::TurnDiffTracker;
use pretty_assertions::assert_eq;
use serde_json::json;
@@ -127,7 +128,7 @@ mod tests {
};
let (session, turn) = make_session_and_context().await;
assert_eq!(
crate::tools::hook_compat::pre_tool_use_payload(&ToolInvocation {
hook_compat::pre_tool_use_payload(&ToolInvocation {
session: session.into(),
turn: turn.into(),
cancellation_token: tokio_util::sync::CancellationToken::new(),
@@ -213,9 +214,6 @@ mod tests {
#[test]
fn mcp_hook_tool_input_defaults_empty_args_to_object() {
assert_eq!(
crate::tools::hook_compat::mcp_hook_tool_input(" "),
json!({})
);
assert_eq!(hook_compat::mcp_hook_tool_input(" "), json!({}));
}
}

View File

@@ -17,6 +17,7 @@ use crate::tools::context::ToolCallSource;
use crate::tools::context::ToolInvocation;
use crate::tools::context::ToolPayload;
use crate::tools::handlers::ShellCommandHandler;
use crate::tools::hook_compat;
use crate::tools::hook_names::HookToolName;
use crate::tools::registry::ToolHandler;
use crate::turn_diff_tracker::TurnDiffTracker;
@@ -222,7 +223,7 @@ async fn local_shell_pre_tool_use_payload_uses_joined_command() {
let (session, turn) = make_session_and_context().await;
assert_eq!(
crate::tools::hook_compat::pre_tool_use_payload(&ToolInvocation {
hook_compat::pre_tool_use_payload(&ToolInvocation {
session: session.into(),
turn: turn.into(),
cancellation_token: tokio_util::sync::CancellationToken::new(),
@@ -247,7 +248,7 @@ async fn shell_command_pre_tool_use_payload_uses_raw_command() {
let (session, turn) = make_session_and_context().await;
assert_eq!(
crate::tools::hook_compat::pre_tool_use_payload(&ToolInvocation {
hook_compat::pre_tool_use_payload(&ToolInvocation {
session: session.into(),
turn: turn.into(),
cancellation_token: tokio_util::sync::CancellationToken::new(),

View File

@@ -11,6 +11,7 @@ use crate::tools::context::ExecCommandToolOutput;
use crate::tools::context::ToolCallSource;
use crate::tools::context::ToolInvocation;
use crate::tools::context::ToolPayload;
use crate::tools::hook_compat;
use crate::tools::hook_names::HookToolName;
use crate::tools::registry::ToolHandler;
use crate::turn_diff_tracker::TurnDiffTracker;
@@ -185,7 +186,7 @@ async fn exec_command_pre_tool_use_payload_uses_raw_command() {
};
let (session, turn) = make_session_and_context().await;
assert_eq!(
crate::tools::hook_compat::pre_tool_use_payload(&ToolInvocation {
hook_compat::pre_tool_use_payload(&ToolInvocation {
session: session.into(),
turn: turn.into(),
cancellation_token: tokio_util::sync::CancellationToken::new(),
@@ -209,7 +210,7 @@ async fn exec_command_pre_tool_use_payload_skips_write_stdin() {
};
let (session, turn) = make_session_and_context().await;
assert_eq!(
crate::tools::hook_compat::pre_tool_use_payload(&ToolInvocation {
hook_compat::pre_tool_use_payload(&ToolInvocation {
session: session.into(),
turn: turn.into(),
cancellation_token: tokio_util::sync::CancellationToken::new(),

View File

@@ -5,6 +5,7 @@ use std::time::Instant;
use crate::function_tool::FunctionCallError;
use crate::goals::GoalRuntimeEvent;
use crate::hook_runtime::PreToolUseHookResult;
use crate::hook_runtime::record_additional_contexts;
use crate::hook_runtime::run_post_tool_use_hooks;
use crate::hook_runtime::run_pre_tool_use_hooks;
@@ -351,12 +352,12 @@ impl ToolRegistry {
)
.await
{
crate::hook_runtime::PreToolUseHookResult::Blocked(message) => {
PreToolUseHookResult::Blocked(message) => {
let err = FunctionCallError::RespondToModel(message);
dispatch_trace.record_failed(&err);
return Err(err);
}
crate::hook_runtime::PreToolUseHookResult::Continue {
PreToolUseHookResult::Continue {
updated_input: Some(updated_input),
} => {
invocation = hook_compat::apply_updated_input(invocation, updated_input)?;