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,4 +1,6 @@
use super::augment_tool_spec_for_code_mode;
use super::create_code_mode_tool;
use super::create_wait_tool;
use super::tool_spec_to_code_mode_tool_definition;
use crate::AdditionalProperties;
use crate::FreeformTool;
@@ -121,3 +123,89 @@ fn tool_spec_to_code_mode_tool_definition_skips_unsupported_variants() {
None
);
}
#[test]
fn create_wait_tool_matches_expected_spec() {
assert_eq!(
create_wait_tool(),
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,
defer_loading: None,
parameters: JsonSchema::Object {
properties: BTreeMap::from([
(
"cell_id".to_string(),
JsonSchema::String {
description: Some("Identifier of the running exec cell.".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(),
),
},
),
(
"yield_time_ms".to_string(),
JsonSchema::Number {
description: Some(
"How long to wait (in milliseconds) for more output before yielding again."
.to_string(),
),
},
),
]),
required: Some(vec!["cell_id".to_string()]),
additional_properties: Some(false.into()),
},
output_schema: None,
})
);
}
#[test]
fn create_code_mode_tool_matches_expected_spec() {
let enabled_tools = vec![("update_plan".to_string(), "Update the plan".to_string())];
assert_eq!(
create_code_mode_tool(&enabled_tools, /*code_mode_only_enabled*/ true),
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*/ true
),
format: FreeformToolFormat {
r#type: "grammar".to_string(),
syntax: "lark".to_string(),
definition: 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]+/
"#
.to_string(),
},
})
);
}