use crate::core::agent::events::ExecEventRecorder;
use crate::core::agent::runtime::AgentRuntime;
use crate::exec::events::{ItemCompletedEvent, ThreadEvent, ThreadItemDetails, ToolCallStatus, ToolOutcome};
use tracing::{error, warn};
pub(super) fn reject_tool_call(
runtime: &mut AgentRuntime,
event_recorder: &mut ExecEventRecorder,
tool_name: &str,
args: Option<&serde_json::Value>,
tool_call_id: &str,
detail: &str,
outcome: ToolOutcome,
) {
if runtime.tool_call_item_id(tool_call_id).is_some() {
runtime.complete_tool_call(tool_call_id, ToolCallStatus::Failed, Some(outcome));
let lifecycle_events = runtime.take_emitted_events();
event_recorder.record_thread_events(lifecycle_events.clone());
emit_failed_tool_outputs_for_completed_invocations(event_recorder, &lifecycle_events, detail);
event_recorder.warning(detail);
return;
}
event_recorder.tool_rejected(tool_name, args, Some(tool_call_id), detail);
}
pub(super) fn reject_invalid_args(
runtime: &mut AgentRuntime,
event_recorder: &mut ExecEventRecorder,
agent_prefix: &str,
tool_name: &str,
tool_call_id: &str,
args: Option<&serde_json::Value>,
err: &dyn std::fmt::Display,
is_gemini: bool,
log_msg: &'static str,
) {
let detail = format!("Invalid arguments for tool '{tool_name}': {err}");
error!(agent = %agent_prefix, tool = %tool_name, error = %err, "{log_msg}");
reject_tool_call(runtime, event_recorder, tool_name, args, tool_call_id, &detail, ToolOutcome::InvalidTool);
runtime
.state
.push_tool_error(tool_call_id.to_string(), tool_name, &serde_json::Value::String(detail), is_gemini);
}
pub(super) fn reject_denied_tool(
runtime: &mut AgentRuntime,
event_recorder: &mut ExecEventRecorder,
agent_prefix: &str,
tool_name: &str,
tool_call_id: &str,
args: Option<&serde_json::Value>,
is_gemini: bool,
quiet: bool,
) {
let detail = format!("Tool execution denied: {tool_name}");
if !quiet {
warn!(agent = %agent_prefix, tool = %tool_name, message = %detail);
}
runtime.state.warnings.push(detail.clone());
runtime.state.push_tool_error(
tool_call_id.to_string(),
tool_name,
&serde_json::Value::String(detail.clone()),
is_gemini,
);
reject_tool_call(runtime, event_recorder, tool_name, args, tool_call_id, &detail, ToolOutcome::HookDenied);
}
pub(super) fn emit_failed_tool_outputs_for_completed_invocations(
event_recorder: &mut ExecEventRecorder,
lifecycle_events: &[ThreadEvent],
detail: &str,
) {
for event in lifecycle_events {
let ThreadEvent::ItemCompleted(ItemCompletedEvent { item }) = event else {
continue;
};
let ThreadItemDetails::ToolInvocation(details) = &item.details else {
continue;
};
event_recorder.tool_output_started(&item.id, details.tool_call_id.as_deref());
event_recorder.tool_output_finished(
&item.id,
details.tool_call_id.as_deref(),
details.status.clone(),
None,
detail,
None,
);
}
}