Keep PreToolUse core boundary narrow

This commit is contained in:
Abhinav Vedmala
2026-05-01 16:18:20 -07:00
parent 550e0bc131
commit f9dfd3d50c
2 changed files with 40 additions and 40 deletions

View File

@@ -140,7 +140,7 @@ pub(crate) async fn run_pre_tool_use_hooks(
tool_use_id: String,
tool_name: &HookToolName,
tool_input: &Value,
) -> PreToolUseOutcome {
) -> Option<String> {
let request = PreToolUseRequest {
session_id: sess.conversation_id,
turn_id: turn_context.sub_id.clone(),
@@ -157,10 +157,31 @@ pub(crate) async fn run_pre_tool_use_hooks(
let preview_runs = hooks.preview_pre_tool_use(&request);
emit_hook_started_events(sess, turn_context, preview_runs).await;
let outcome = hooks.run_pre_tool_use(request).await;
emit_hook_completed_events(sess, turn_context, outcome.hook_events.clone()).await;
record_additional_contexts(sess, turn_context, outcome.additional_contexts.clone()).await;
outcome
let PreToolUseOutcome {
hook_events,
should_block,
block_reason,
additional_contexts,
} = hooks.run_pre_tool_use(request).await;
emit_hook_completed_events(sess, turn_context, hook_events).await;
record_additional_contexts(sess, turn_context, additional_contexts).await;
if should_block {
block_reason.map(|reason| {
if (tool_name.name() == "Bash" || tool_name.name() == "apply_patch")
&& let Some(command) = tool_input.get("command").and_then(Value::as_str)
{
format!("Command blocked by PreToolUse hook: {reason}. Command: {command}")
} else {
format!(
"Tool call blocked by PreToolUse hook: {reason}. Tool: {}",
tool_name.name()
)
}
})
} else {
None
}
}
// PermissionRequest hooks share the same preview/start/completed event flow as

View File

@@ -354,41 +354,20 @@ impl ToolRegistry {
return Err(err);
}
let pre_tool_use_outcome =
if let Some(pre_tool_use_payload) = handler.pre_tool_use_payload(&invocation) {
let outcome = run_pre_tool_use_hooks(
&invocation.session,
&invocation.turn,
invocation.call_id.clone(),
&pre_tool_use_payload.tool_name,
&pre_tool_use_payload.tool_input,
)
.await;
if outcome.should_block
&& let Some(reason) = outcome.block_reason.as_deref()
{
let message = if (pre_tool_use_payload.tool_name.name() == "Bash"
|| pre_tool_use_payload.tool_name.name() == "apply_patch")
&& let Some(command) = pre_tool_use_payload
.tool_input
.get("command")
.and_then(Value::as_str)
{
format!("Command blocked by PreToolUse hook: {reason}. Command: {command}")
} else {
format!(
"Tool call blocked by PreToolUse hook: {reason}. Tool: {}",
pre_tool_use_payload.tool_name.name()
)
};
let err = FunctionCallError::RespondToModel(message);
dispatch_trace.record_failed(&err);
return Err(err);
}
Some(outcome)
} else {
None
};
if let Some(pre_tool_use_payload) = handler.pre_tool_use_payload(&invocation)
&& let Some(message) = run_pre_tool_use_hooks(
&invocation.session,
&invocation.turn,
invocation.call_id.clone(),
&pre_tool_use_payload.tool_name,
&pre_tool_use_payload.tool_input,
)
.await
{
let err = FunctionCallError::RespondToModel(message);
dispatch_trace.record_failed(&err);
return Err(err);
}
let is_mutating = handler.is_mutating(&invocation).await;
let response_cell = tokio::sync::Mutex::new(None);