codex-tools: extract utility tool specs (#16154)

## Why

The previous `codex-tools` migration steps moved the shared schema
models, local-host specs, collaboration specs, and related adapters out
of `codex-core`, but `core/src/tools/spec.rs` still contained a grab bag
of pure utility tool builders. Those specs do not need session state or
handler logic; they only describe wire shapes for tools that
`codex-core` already knows how to execute.

Moving that remaining low-coupling layer into `codex-tools` keeps the
migration moving in meaningful chunks and trims another large block of
passive tool-spec construction out of `codex-core` without touching the
runtime-coupled handlers.

## What changed

- extended `codex-tools` to own the pure spec builders for:
  - code-mode `exec` / `wait`
  - `js_repl` / `js_repl_reset`
- MCP resource tools `list_mcp_resources`,
`list_mcp_resource_templates`, and `read_mcp_resource`
  - utility tools `list_dir` and `test_sync_tool`
- split those builders across small module files with sibling
`*_tests.rs` coverage, keeping `src/lib.rs` exports-only
- rewired `core/src/tools/spec.rs` to call the extracted builders and
deleted the duplicated core-local implementations
- moved the direct JS REPL grammar seam test out of
`core/src/tools/spec_tests.rs` so it now lives with the extracted
implementation in `codex-tools`
- updated `codex-rs/tools/README.md` so the documented crate boundary
matches the new utility-spec surface

## Test plan

- `CARGO_TARGET_DIR=/tmp/codex-tools-utility-specs cargo test -p
codex-tools`
- `CARGO_TARGET_DIR=/tmp/codex-core-utility-specs cargo test -p
codex-core --lib tools::spec::`
- `just fix -p codex-tools -p codex-core`
- `just argument-comment-lint`

## References

- #15923
- #15928
- #15944
- #15953
- #16031
- #16047
- #16129
- #16132
- #16138
- #16141
This commit is contained in:
Michael Bolin
2026-03-29 14:34:36 -07:00
committed by GitHub
parent 38e648ca67
commit af568afdd5
12 changed files with 794 additions and 372 deletions

View File

@@ -1,6 +1,11 @@
use crate::FreeformTool;
use crate::FreeformToolFormat;
use crate::JsonSchema;
use crate::ResponsesApiTool;
use crate::ToolSpec;
use codex_code_mode::CodeModeToolKind;
use codex_code_mode::ToolDefinition as CodeModeToolDefinition;
use std::collections::BTreeMap;
/// Augment tool descriptions with code-mode-specific exec samples.
pub fn augment_tool_spec_for_code_mode(spec: ToolSpec) -> ToolSpec {
@@ -32,6 +37,85 @@ pub fn tool_spec_to_code_mode_tool_definition(spec: &ToolSpec) -> Option<CodeMod
.then(|| codex_code_mode::augment_tool_definition(definition))
}
pub fn create_wait_tool() -> ToolSpec {
let properties = BTreeMap::from([
(
"cell_id".to_string(),
JsonSchema::String {
description: Some("Identifier of the running exec cell.".to_string()),
},
),
(
"yield_time_ms".to_string(),
JsonSchema::Number {
description: Some(
"How long to wait (in milliseconds) for more output before yielding again."
.to_string(),
),
},
),
(
"max_tokens".to_string(),
JsonSchema::Number {
description: Some(
"Maximum number of output tokens to return for this wait call.".to_string(),
),
},
),
(
"terminate".to_string(),
JsonSchema::Boolean {
description: Some("Whether to terminate the running exec cell.".to_string()),
},
),
]);
ToolSpec::Function(ResponsesApiTool {
name: codex_code_mode::WAIT_TOOL_NAME.to_string(),
description: format!(
"Waits on a yielded `{}` cell and returns new output or completion.\n{}",
codex_code_mode::PUBLIC_TOOL_NAME,
codex_code_mode::build_wait_tool_description().trim()
),
strict: false,
parameters: JsonSchema::Object {
properties,
required: Some(vec!["cell_id".to_string()]),
additional_properties: Some(false.into()),
},
output_schema: None,
defer_loading: None,
})
}
pub fn create_code_mode_tool(
enabled_tools: &[(String, String)],
code_mode_only_enabled: bool,
) -> ToolSpec {
const CODE_MODE_FREEFORM_GRAMMAR: &str = r#"
start: pragma_source | plain_source
pragma_source: PRAGMA_LINE NEWLINE SOURCE
plain_source: SOURCE
PRAGMA_LINE: /[ \t]*\/\/ @exec:[^\r\n]*/
NEWLINE: /\r?\n/
SOURCE: /[\s\S]+/
"#;
ToolSpec::Freeform(FreeformTool {
name: codex_code_mode::PUBLIC_TOOL_NAME.to_string(),
description: codex_code_mode::build_exec_tool_description(
enabled_tools,
code_mode_only_enabled,
),
format: FreeformToolFormat {
r#type: "grammar".to_string(),
syntax: "lark".to_string(),
definition: CODE_MODE_FREEFORM_GRAMMAR.to_string(),
},
})
}
fn code_mode_tool_definition_for_spec(spec: &ToolSpec) -> Option<CodeModeToolDefinition> {
match spec {
ToolSpec::Function(tool) => Some(CodeModeToolDefinition {