start of hooks engine (#13276)

(Experimental)

This PR adds a first MVP for hooks, with SessionStart and Stop

The core design is:

- hooks live in a dedicated engine under codex-rs/hooks
- each hook type has its own event-specific file
- hook execution is synchronous and blocks normal turn progression while
running
- matching hooks run in parallel, then their results are aggregated into
a normalized HookRunSummary

On the AppServer side, hooks are exposed as operational metadata rather
than transcript-native items:

- new live notifications: hook/started, hook/completed
- persisted/replayed hook results live on Turn.hookRuns
- we intentionally did not add hook-specific ThreadItem variants

Hooks messages are not persisted, they remain ephemeral. The context
changes they add are (they get appended to the user's prompt)
This commit is contained in:
Andrei Eternal
2026-03-09 21:11:31 -07:00
committed by GitHub
parent da616136cc
commit 244b2d53f4
73 changed files with 4791 additions and 483 deletions

View File

@@ -2183,6 +2183,7 @@ pub(crate) async fn make_session_and_context() -> (Session, TurnContext) {
),
hooks: Hooks::new(HooksConfig {
legacy_notify_argv: config.notify.clone(),
..HooksConfig::default()
}),
rollout: Mutex::new(None),
user_shell: Arc::new(default_user_shell()),
@@ -2300,7 +2301,7 @@ async fn request_permissions_emits_event_when_reject_policy_allows_requests() {
}),
..Default::default()
},
scope: codex_protocol::request_permissions::PermissionGrantScope::Turn,
scope: PermissionGrantScope::Turn,
};
let handle = tokio::spawn({
@@ -2385,7 +2386,7 @@ async fn request_permissions_returns_empty_grant_when_reject_policy_blocks_reque
Some(
codex_protocol::request_permissions::RequestPermissionsResponse {
permissions: codex_protocol::models::PermissionProfile::default(),
scope: codex_protocol::request_permissions::PermissionGrantScope::Turn,
scope: PermissionGrantScope::Turn,
}
)
);
@@ -2737,6 +2738,7 @@ pub(crate) async fn make_session_and_context_with_dynamic_tools_and_rx(
),
hooks: Hooks::new(HooksConfig {
legacy_notify_argv: config.notify.clone(),
..HooksConfig::default()
}),
rollout: Mutex::new(None),
user_shell: Arc::new(default_user_shell()),