feat: extract shared tool executor interface (#22359)

## Why

Codex still models model-visible tools and executable behavior largely
inside `codex-core`, which makes it harder to evolve the tool system
toward a single reusable abstraction for built-ins, MCP-backed tools,
dynamic tools, and later tools injected from outside core.

This PR takes the next incremental step in that direction by moving the
common execution-facing pieces out of core and separating them from
core-only orchestration. The intent is to let shared tool abstractions
improve in one place, while `codex-core` keeps the parts that are still
inherently host-specific today, such as `ToolInvocation`, dispatch
wiring, and hook integration.

This PR is mostly moving things around. The only interesting piece is
this abstraction:
https://github.com/openai/codex/pull/22359/changes#diff-81af519002548ba51ed102bdaaf77e081d40a1e73a6e5f9b104bbbc96a6f1b3dR13

## What changed

- Added `codex_tools::ToolExecutor<Invocation>` as the shared execution
trait for model-visible tools.
- Moved the reusable execution support types from `codex-core` into
`codex-tools`:
  - `FunctionCallError`
  - `ToolPayload`
  - `ToolOutput`
- Refactored core tool implementations so that execution behavior lives
on `ToolExecutor<ToolInvocation>`, while `ToolHandler` remains the
core-local extension point for hook payloads, telemetry tags, diff
consumers, and other orchestration concerns.
- Kept the registry and dispatch flow behaviorally unchanged while
making the shared/extracted boundary explicit across built-in, MCP,
dynamic, extension-backed, shell, and multi-agent tool handlers.

## Verification

- `cargo test -p codex-tools`
- `just fix -p codex-tools`
- `just fix -p codex-core`
- `cargo test -p codex-core` progressed through the updated tool
surfaces and then hit the existing unrelated multi-agent stack overflow
in
`tools::handlers::multi_agents::tests::tool_handlers_cascade_close_and_resume_and_keep_explicitly_closed_subtrees_closed`.
This commit is contained in:
jif-oai
2026-05-13 11:31:27 +02:00
committed by GitHub
parent 155c04ad40
commit 1824685a00
54 changed files with 770 additions and 575 deletions
@@ -2,6 +2,7 @@ use crate::function_tool::FunctionCallError;
use crate::tools::context::FunctionToolOutput;
use crate::tools::context::ToolInvocation;
use crate::tools::context::ToolPayload;
use crate::tools::registry::ToolExecutor;
use crate::tools::registry::ToolHandler;
use codex_tools::ToolName;
use codex_tools::ToolSpec;
@@ -86,7 +87,7 @@ impl CodeModeExecuteHandler {
}
}
impl ToolHandler for CodeModeExecuteHandler {
impl ToolExecutor<ToolInvocation> for CodeModeExecuteHandler {
type Output = FunctionToolOutput;
fn tool_name(&self) -> ToolName {
@@ -97,10 +98,6 @@ impl ToolHandler for CodeModeExecuteHandler {
Some(self.spec.clone())
}
fn matches_kind(&self, payload: &ToolPayload) -> bool {
matches!(payload, ToolPayload::Custom { .. })
}
async fn handle(&self, invocation: ToolInvocation) -> Result<Self::Output, FunctionCallError> {
let ToolInvocation {
session,
@@ -121,3 +118,9 @@ impl ToolHandler for CodeModeExecuteHandler {
}
}
}
impl ToolHandler for CodeModeExecuteHandler {
fn matches_kind(&self, payload: &ToolPayload) -> bool {
matches!(payload, ToolPayload::Custom { .. })
}
}
@@ -4,6 +4,7 @@ use crate::function_tool::FunctionCallError;
use crate::tools::context::FunctionToolOutput;
use crate::tools::context::ToolInvocation;
use crate::tools::context::ToolPayload;
use crate::tools::registry::ToolExecutor;
use crate::tools::registry::ToolHandler;
use codex_tools::ToolName;
use codex_tools::ToolSpec;
@@ -40,7 +41,7 @@ where
})
}
impl ToolHandler for CodeModeWaitHandler {
impl ToolExecutor<ToolInvocation> for CodeModeWaitHandler {
type Output = FunctionToolOutput;
fn tool_name(&self) -> ToolName {
@@ -105,3 +106,5 @@ impl ToolHandler for CodeModeWaitHandler {
}
}
}
impl ToolHandler for CodeModeWaitHandler {}