mirror of
https://github.com/openai/codex.git
synced 2026-11-02 08:19:52 +00:00
Allow hooks to error (#11615)
Allow hooks to return errors. We should do this before introducing more hook types, or we'll have to migrate them all.
This commit is contained in:
1 parent
825a4af42f
commit
b3095679ed
6 files changed
+251
-43
No files matched your search
@@ -44,6 +44,7 @@ use async_channel::Sender;
|
||||
use codex_hooks::HookEvent;
|
||||
use codex_hooks::HookEventAfterAgent;
|
||||
use codex_hooks::HookPayload;
|
||||
use codex_hooks::HookResult;
|
||||
use codex_hooks::Hooks;
|
||||
use codex_hooks::HooksConfig;
|
||||
use codex_network_proxy::NetworkProxy;
|
||||
@@ -4533,7 +4534,8 @@ pub(crate) async fn run_turn(
|
||||
|
||||
if !needs_follow_up {
|
||||
last_agent_message = sampling_request_last_agent_message;
|
||||
sess.hooks()
|
||||
let hook_outcomes = sess
|
||||
.hooks()
|
||||
.dispatch(HookPayload {
|
||||
session_id: sess.conversation_id,
|
||||
cwd: turn_context.cwd.clone(),
|
||||
@@ -4548,6 +4550,47 @@ pub(crate) async fn run_turn(
|
||||
},
|
||||
})
|
||||
.await;
|
||||
|
||||
let mut abort_message = None;
|
||||
for hook_outcome in hook_outcomes {
|
||||
let hook_name = hook_outcome.hook_name;
|
||||
match hook_outcome.result {
|
||||
HookResult::Success => {}
|
||||
HookResult::FailedContinue(error) => {
|
||||
warn!(
|
||||
turn_id = %turn_context.sub_id,
|
||||
hook_name = %hook_name,
|
||||
error = %error,
|
||||
"after_agent hook failed; continuing"
|
||||
);
|
||||
}
|
||||
HookResult::FailedAbort(error) => {
|
||||
let message = format!(
|
||||
"after_agent hook '{hook_name}' failed and aborted turn completion: {error}"
|
||||
);
|
||||
warn!(
|
||||
turn_id = %turn_context.sub_id,
|
||||
hook_name = %hook_name,
|
||||
error = %error,
|
||||
"after_agent hook failed; aborting operation"
|
||||
);
|
||||
if abort_message.is_none() {
|
||||
abort_message = Some(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(message) = abort_message {
|
||||
sess.send_event(
|
||||
&turn_context,
|
||||
EventMsg::Error(ErrorEvent {
|
||||
message,
|
||||
codex_error_info: None,
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
return None;
|
||||
}
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
|
||||
@@ -15,6 +15,7 @@ use async_trait::async_trait;
|
||||
use codex_hooks::HookEvent;
|
||||
use codex_hooks::HookEventAfterToolUse;
|
||||
use codex_hooks::HookPayload;
|
||||
use codex_hooks::HookResult;
|
||||
use codex_hooks::HookToolInput;
|
||||
use codex_hooks::HookToolInputLocalShell;
|
||||
use codex_hooks::HookToolKind;
|
||||
@@ -172,7 +173,7 @@ impl ToolRegistry {
|
||||
Ok((preview, success)) => (preview.clone(), *success),
|
||||
Err(err) => (err.to_string(), false),
|
||||
};
|
||||
dispatch_after_tool_use_hook(AfterToolUseHookDispatch {
|
||||
let hook_abort_error = dispatch_after_tool_use_hook(AfterToolUseHookDispatch {
|
||||
invocation: &invocation,
|
||||
output_preview,
|
||||
success,
|
||||
@@ -182,6 +183,10 @@ impl ToolRegistry {
|
||||
})
|
||||
.await;
|
||||
|
||||
if let Some(err) = hook_abort_error {
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
match result {
|
||||
Ok(_) => {
|
||||
let mut guard = output_cell.lock().await;
|
||||
@@ -339,12 +344,14 @@ struct AfterToolUseHookDispatch<'a> {
|
||||
mutating: bool,
|
||||
}
|
||||
|
||||
async fn dispatch_after_tool_use_hook(dispatch: AfterToolUseHookDispatch<'_>) {
|
||||
async fn dispatch_after_tool_use_hook(
|
||||
dispatch: AfterToolUseHookDispatch<'_>,
|
||||
) -> Option<FunctionCallError> {
|
||||
let AfterToolUseHookDispatch { invocation, .. } = dispatch;
|
||||
let session = invocation.session.as_ref();
|
||||
let turn = invocation.turn.as_ref();
|
||||
let tool_input = HookToolInput::from(&invocation.payload);
|
||||
session
|
||||
let hook_outcomes = session
|
||||
.hooks()
|
||||
.dispatch(HookPayload {
|
||||
session_id: session.conversation_id,
|
||||
@@ -373,4 +380,34 @@ async fn dispatch_after_tool_use_hook(dispatch: AfterToolUseHookDispatch<'_>) {
|
||||
},
|
||||
})
|
||||
.await;
|
||||
|
||||
for hook_outcome in hook_outcomes {
|
||||
let hook_name = hook_outcome.hook_name;
|
||||
match hook_outcome.result {
|
||||
HookResult::Success => {}
|
||||
HookResult::FailedContinue(error) => {
|
||||
warn!(
|
||||
call_id = %invocation.call_id,
|
||||
tool_name = %invocation.tool_name,
|
||||
hook_name = %hook_name,
|
||||
error = %error,
|
||||
"after_tool_use hook failed; continuing"
|
||||
);
|
||||
}
|
||||
HookResult::FailedAbort(error) => {
|
||||
warn!(
|
||||
call_id = %invocation.call_id,
|
||||
tool_name = %invocation.tool_name,
|
||||
hook_name = %hook_name,
|
||||
error = %error,
|
||||
"after_tool_use hook failed; aborting operation"
|
||||
);
|
||||
return Some(FunctionCallError::Fatal(format!(
|
||||
"after_tool_use hook '{hook_name}' failed and aborted operation: {error}"
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
Reference in new issue
Block a user