Files
codex/codex-rs/config/src/hook_config.rs
T
andrewgu-oai 7a5860da35 Add hook configuration to environment attachments (#56302)
## What changed

- Add `environment_hooks` to `EnvironmentConfig` for hooks from an environment's effective managed requirements. Initialize it empty for inferred thread configurations and report the handler count in debug output.
- Move `HookEventsToml`, `MatcherGroup`, and `HookHandlerConfig` into `codex_protocol::hooks`, with re-exports from `codex_config::hook_config`.
- Validate MCP hook input recursively to reject nulls and integers outside the signed 64-bit range without converting through `toml::Value`.

GitOrigin-RevId: 410506c8e2b89c9519c81d86b44ccd760d751693
2026-10-23 00:02:29 +00:00

78 lines
2.2 KiB
Rust

use std::collections::BTreeMap;
use std::path::Path;
use std::path::PathBuf;
pub use codex_protocol::hooks::HookEventsToml;
pub use codex_protocol::hooks::HookHandlerConfig;
pub use codex_protocol::hooks::MatcherGroup;
use schemars::JsonSchema;
use serde::Deserialize;
use serde::Serialize;
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct HooksFile {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(default)]
pub hooks: HookEventsToml,
}
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct HooksToml {
#[serde(flatten)]
pub events: HookEventsToml,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub state: BTreeMap<String, HookStateToml>,
}
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct HookStateToml {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub enabled: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub trusted_hash: Option<String>,
/// Approved plugin contents; kept separate for declaration-only clients.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub trusted_content_hash: Option<String>,
}
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ManagedHooksRequirementsToml {
pub managed_dir: Option<PathBuf>,
pub windows_managed_dir: Option<PathBuf>,
#[serde(flatten)]
pub hooks: Box<HookEventsToml>,
}
impl ManagedHooksRequirementsToml {
pub fn is_empty(&self) -> bool {
let Self {
managed_dir,
windows_managed_dir,
hooks,
} = self;
managed_dir.is_none() && windows_managed_dir.is_none() && hooks.is_empty()
}
pub fn handler_count(&self) -> usize {
self.hooks.handler_count()
}
pub fn managed_dir_for_current_platform(&self) -> Option<&Path> {
#[cfg(windows)]
{
self.windows_managed_dir.as_deref()
}
#[cfg(not(windows))]
{
self.managed_dir.as_deref()
}
}
}
#[cfg(test)]
#[path = "hooks_tests.rs"]
mod tests;