Files
codex/codex-rs/hooks/src/engine/config.rs
T
Andrei Eternal 244b2d53f4 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)
2026-03-10 04:11:31 +00:00

43 lines
1.1 KiB
Rust

use serde::Deserialize;
#[derive(Debug, Default, Deserialize)]
pub(crate) struct HooksFile {
#[serde(default)]
pub hooks: HookEvents,
}
#[derive(Debug, Default, Deserialize)]
pub(crate) struct HookEvents {
#[serde(rename = "SessionStart", default)]
pub session_start: Vec<MatcherGroup>,
#[serde(rename = "Stop", default)]
pub stop: Vec<MatcherGroup>,
}
#[derive(Debug, Default, Deserialize)]
pub(crate) struct MatcherGroup {
#[serde(default)]
pub matcher: Option<String>,
#[serde(default)]
pub hooks: Vec<HookHandlerConfig>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(tag = "type")]
pub(crate) enum HookHandlerConfig {
#[serde(rename = "command")]
Command {
command: String,
#[serde(default, rename = "timeout", alias = "timeoutSec")]
timeout_sec: Option<u64>,
#[serde(default)]
r#async: bool,
#[serde(default, rename = "statusMessage")]
status_message: Option<String>,
},
#[serde(rename = "prompt")]
Prompt {},
#[serde(rename = "agent")]
Agent {},
}