Add hooks implementation and wire up to notify (#9691)

This introduces a `Hooks` service. It registers hooks from config and
dispatches hook events at runtime.

N.B. The hook config is not wired up to this yet. But for legacy
reasons, we wire up `notify` from config and power it using hooks now.
Nothing about the `notify` interface has changed.

I'd start by reviewing `hooks/types.rs`

Some things to note:
  - hook names subject to change
  - no hook result yet
  - stopping semantics yet to be introduced
  - additional hooks yet to be introduced
This commit is contained in:
gt-oai
2026-02-05 16:49:35 +00:00
committed by GitHub
Unverified
parent 9ee746afd6
commit 3b54fd7336
8 changed files with 608 additions and 105 deletions
+23 -15
View File
@@ -24,6 +24,9 @@ use crate::features::FEATURES;
use crate::features::Feature;
use crate::features::Features;
use crate::features::maybe_push_unstable_features_warning;
use crate::hooks::HookEvent;
use crate::hooks::HookEventAfterAgent;
use crate::hooks::Hooks;
use crate::models_manager::manager::ModelsManager;
use crate::parse_command::parse_command;
use crate::parse_turn_item;
@@ -35,7 +38,6 @@ use crate::stream_events_utils::last_assistant_message_from_item;
use crate::terminal;
use crate::truncate::TruncationPolicy;
use crate::turn_metadata::build_turn_metadata_header;
use crate::user_notification::UserNotifier;
use crate::util::error_or_panic;
use async_channel::Receiver;
use async_channel::Sender;
@@ -201,7 +203,6 @@ use crate::tools::spec::ToolsConfig;
use crate::tools::spec::ToolsConfigParams;
use crate::turn_diff_tracker::TurnDiffTracker;
use crate::unified_exec::UnifiedExecProcessManager;
use crate::user_notification::UserNotification;
use crate::util::backoff;
use crate::windows_sandbox::WindowsSandboxLevelExt;
use codex_async_utils::OrCancelExt;
@@ -1015,7 +1016,7 @@ impl Session {
Arc::clone(&config),
Arc::clone(&auth_manager),
),
notifier: UserNotifier::new(config.notify.clone()),
hooks: Hooks::new(config.as_ref()),
rollout: Mutex::new(rollout_recorder),
user_shell: Arc::new(default_shell),
show_raw_agent_reasoning: config.show_raw_agent_reasoning,
@@ -2450,8 +2451,8 @@ impl Session {
}
}
pub(crate) fn notifier(&self) -> &UserNotifier {
&self.services.notifier
pub(crate) fn hooks(&self) -> &Hooks {
&self.services.hooks
}
pub(crate) fn user_shell(&self) -> Arc<shell::Shell> {
@@ -3776,14 +3777,21 @@ pub(crate) async fn run_turn(
if !needs_follow_up {
last_agent_message = sampling_request_last_agent_message;
sess.notifier()
.notify(&UserNotification::AgentTurnComplete {
thread_id: sess.conversation_id.to_string(),
turn_id: turn_context.sub_id.clone(),
cwd: turn_context.cwd.display().to_string(),
input_messages: sampling_request_input_messages,
last_assistant_message: last_agent_message.clone(),
});
sess.hooks()
.dispatch(crate::hooks::HookPayload {
session_id: sess.conversation_id,
cwd: turn_context.cwd.clone(),
triggered_at: chrono::Utc::now(),
hook_event: HookEvent::AfterAgent {
event: HookEventAfterAgent {
thread_id: sess.conversation_id,
turn_id: turn_context.sub_id.clone(),
input_messages: sampling_request_input_messages,
last_assistant_message: last_agent_message.clone(),
},
},
})
.await;
break;
}
continue;
@@ -5724,7 +5732,7 @@ mod tests {
Arc::clone(&config),
Arc::clone(&auth_manager),
),
notifier: UserNotifier::new(None),
hooks: Hooks::new(&config),
rollout: Mutex::new(None),
user_shell: Arc::new(default_user_shell()),
show_raw_agent_reasoning: config.show_raw_agent_reasoning,
@@ -5854,7 +5862,7 @@ mod tests {
Arc::clone(&config),
Arc::clone(&auth_manager),
),
notifier: UserNotifier::new(None),
hooks: Hooks::new(&config),
rollout: Mutex::new(None),
user_shell: Arc::new(default_user_shell()),
show_raw_agent_reasoning: config.show_raw_agent_reasoning,