Files
codex/codex-rs/core/src/tools/hook_names.rs
T
AbhinavandGitHub 5c20513a1b Default function tools into tool hooks (#23757)
# Why

`PreToolUse`, `PostToolUse`, and `updatedInput` coverage for local
function tools currently depends on each handler remembering to wire up
the hook contract itself. That makes coverage easy to miss as new
function tools are added, even though most of them share the same basic
shape: a model-facing function call with JSON arguments.

# What

This makes `CoreToolRuntime` provide the default hook contract for
ordinary local function tools:

- build generic `PreToolUse` and `PostToolUse` payloads from the
function tool name and arguments
- apply `updatedInput` rewrites back into function-tool arguments
through the same default path
- let tool outputs override the post-hook input or response when they
have a more stable hook-facing contract

The exceptions stay explicit:

- hosted tools remain outside the generic local function path
- code-mode `wait` and `write_stdin` opt out for now
- `PostToolUse` feedback replaces only the model-visible response, so
code mode keeps its typed tool result

With the generic path in place, the MCP and extension-tool adapters no
longer need their own duplicate pre/post hook plumbing. The new coverage
exercises the registry default plus end-to-end local function behavior
for pre-hook blocking, `updatedInput` rewriting, and post-hook context.
2026-05-23 00:56:58 +00:00

68 lines
2.4 KiB
Rust

//! Hook-facing tool names and matcher compatibility aliases.
//!
//! Hook stdin exposes one canonical `tool_name`, but matcher selection may also
//! need to recognize names from adjacent tool ecosystems. Keeping those two
//! concepts together prevents handlers from accidentally serializing a
//! compatibility alias, such as `Write`, as the stable hook payload name.
/// Identifies a tool in hook payloads and hook matcher selection.
///
/// `name` is the canonical value serialized into hook stdin. Matcher aliases are
/// internal-only compatibility names that may select the same hook handlers but
/// must not change the payload seen by hook processes.
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct HookToolName {
name: String,
matcher_aliases: Vec<String>,
}
impl HookToolName {
/// Builds a hook tool name with no matcher aliases.
pub(crate) fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
matcher_aliases: Vec::new(),
}
}
/// Returns the hook identity for file edits performed through `apply_patch`.
///
/// The serialized name remains `apply_patch` so logs and policies can key
/// off the actual Codex tool. `Write` and `Edit` are accepted as matcher
/// aliases for compatibility with hook configurations that describe edits
/// using Claude Code-style names.
pub(crate) fn apply_patch() -> Self {
Self {
name: "apply_patch".to_string(),
matcher_aliases: vec!["Write".to_string(), "Edit".to_string()],
}
}
/// Returns the hook identity for spawning sub-agents.
///
/// The serialized name remains `spawn_agent`, while `Agent` is accepted as
/// a matcher alias for compatibility with hook configurations that describe
/// sub-agent creation using Claude Code-style names.
pub(crate) fn spawn_agent() -> Self {
Self {
name: "spawn_agent".to_string(),
matcher_aliases: vec!["Agent".to_string()],
}
}
/// Returns the hook identity historically used for shell-like tools.
pub(crate) fn bash() -> Self {
Self::new("Bash")
}
/// Returns the canonical hook name serialized into hook stdin.
pub(crate) fn name(&self) -> &str {
&self.name
}
/// Returns additional matcher inputs that should select the same handlers.
pub(crate) fn matcher_aliases(&self) -> &[String] {
&self.matcher_aliases
}
}