Commit Graph

8 Commits

  • Represent dynamic tools with explicit namespaces internally (#27365)
    Follow-up to #27356.
    
    ## Stack note
    
    This PR changes Codex's internal dynamic-tool shape while leaving
    `thread/start` unchanged. App-server therefore converts the existing
    per-tool input into explicit functions and namespaces before passing it
    to core.
    
    [#27371](https://github.com/openai/codex/pull/27371) updates
    `thread/start` to use the same explicit shape and removes this temporary
    conversion.
    
    ## Why
    
    Dynamic tools repeat namespace metadata on every function. Core should
    keep one explicit namespace with its member tools so descriptions and
    membership stay consistent across sessions and runtime planning.
    
    ## What changed
    
    - Represent dynamic tools as top-level functions or explicit namespaces
    in protocol and session state.
    - Read old flat rollout metadata and write the canonical hierarchy.
    - Flatten namespace members only when registering callable tools.
    - Keep `thread/start.dynamicTools` flat for now and normalize it at the
    app-server boundary.
    
    New builds can read old rollout metadata. Older builds cannot read newly
    written hierarchical metadata.
    
    ## Test plan
    
    - `just test -p codex-app-server
    thread_start_normalizes_legacy_dynamic_tools_into_model_request`
    - `just test -p codex-protocol
    session_meta_normalizes_legacy_dynamic_tools`
    - `just test -p codex-core
    resume_restores_dynamic_tools_from_rollout_with_sqlite_enabled`
    - `just test -p codex-core
    tool_search_returns_deferred_dynamic_tool_and_routes_follow_up_call`
    - `just test -p codex-core code_mode_can_call_hidden_dynamic_tools`
    - `just test -p codex-tools`
  • Encapsulate tool search entries in handlers (#22261)
    ## Why
    
    This builds on the handler-owned spec refactor by moving deferred
    tool-search metadata to the same handlers that already own tool specs.
    The registry builder no longer needs a separate prebuilt
    `tool_search_entries` path; it can collect searchable entries from
    deferred handlers directly.
    
    ## What changed
    
    - Added `search_info()` to tool handlers and implemented it for MCP and
    dynamic handlers.
    - Reused handler `spec()` output when constructing tool-search entries,
    adapting it into the deferred `LoadableToolSpec` shape expected by
    `tool_search`.
    - Simplified `build_tool_registry_builder(...)` so `tool_search`
    registration is based on deferred handlers with search info.
    - Removed the old standalone search-entry builders and now-unused
    `codex-tools` discovery helper exports.
    
    ## Verification
    
    - `cargo test -p codex-core tools::handlers::tool_search::tests:: --
    --nocapture`
    - `cargo test -p codex-core tools::spec_plan::tests::search_tool --
    --nocapture`
    - `cargo test -p codex-core tools::spec::tests:: -- --nocapture`
    - `cargo test -p codex-core tools::spec_plan::tests:: -- --nocapture`
    - `cargo test -p codex-tools`
    - `just fix -p codex-core`
    - `just fix -p codex-tools`
  • [codex] Move tool specs into core handlers (#21416)
    ## Why
    
    This is the first mechanical slice of moving tool spec ownership toward
    the handlers. `codex-tools` should keep shared primitives and conversion
    helpers, while builtin tool specs and registration planning live in
    `codex-core` with the handlers that own those tools.
    
    Keeping this PR to relocation and import updates isolates the copy/move
    review from the later logic change that wires specs through registered
    handlers.
    
    ## What changed
    
    - Moved builtin tool spec constructors from `codex-rs/tools/src` into
    `codex-rs/core/src/tools/handlers/*_spec.rs` or nearby core tool
    modules.
    - Moved the registry planning code into
    `codex-rs/core/src/tools/spec_plan.rs` and its associated types/tests
    into core.
    - Kept shared primitives in `codex-tools`, including `ToolSpec`,
    schema/types, discovery/config primitives, dynamic/MCP conversion
    helpers, and code-mode collection helpers.
    - Updated handlers that referenced moved argument types or tool-name
    constants to use the core spec modules.
    - Moved spec tests next to the moved spec modules.
    
    ## Verification
    
    - `cargo check -p codex-tools`
    - `cargo check -p codex-core`
    - `cargo test -p codex-tools`
    - `cargo test -p codex-core _spec::tests`
    - `cargo test -p codex-core tools::spec_plan::tests`
    - `just fix -p codex-tools`
    - `just fix -p codex-core`
    
    Note: I also tried the broader `cargo test -p codex-core tools::`; it
    reached the moved spec-plan/spec tests successfully, then aborted with a
    stack overflow in
    `tools::handlers::multi_agents::tests::tool_handlers_cascade_close_and_resume_and_keep_explicitly_closed_subtrees_closed`,
    which is outside this spec relocation.
  • [tool search] support namespaced deferred dynamic tools (#18413)
    Deferred dynamic tools need to round-trip a namespace so a tool returned
    by `tool_search` can be called through the same registry key that core
    uses for dispatch.
    
    This change adds namespace support for dynamic tool specs/calls,
    persists it through app-server thread state, and routes dynamic tool
    calls by full `ToolName` while still sending the app the leaf tool name.
    Deferred dynamic tools must provide a namespace; non-deferred dynamic
    tools may remain top-level.
    
    It also introduces `LoadableToolSpec` as the shared
    function-or-namespace Responses shape used by both `tool_search` output
    and dynamic tool registration, so dynamic tools use the same wrapping
    logic in both paths.
    
    Validation:
    - `cargo test -p codex-tools`
    - `cargo test -p codex-core tool_search`
    
    ---------
    
    Co-authored-by: Sayan Sisodiya <sayan@openai.com>
  • Fix empty tool descriptions (#17946)
    ## Summary
    - Ensure direct namespaced MCP tool groups are emitted with a non-empty
    namespace description even when namespace metadata is missing or blank.
    - Add regression coverage for missing MCP namespace descriptions.
    
    ## Cause
    Latest `main` can serialize a direct namespaced MCP tool group with an
    empty top-level `description`. The namespace description path used
    `unwrap_or_default()` when `tool_namespaces` did not include metadata
    for that namespace, so the outbound Responses API payload could contain
    a tool like `{"type":"namespace","description":""}`. The Responses API
    rejects that because namespace tool descriptions must be a non-empty
    string.
    
    ## Fix
    - Add a fallback namespace description: `Tools in the <namespace>
    namespace.`
    - Preserve provided namespace descriptions after trimming, but treat
    blank descriptions as missing.
    
    ### Issue I am seeing
    This is what I am seeing on the local build.
    <img width="1593" height="488" alt="Screenshot 2026-04-15 at 10 55 55
    AM"
    src="https://github.com/user-attachments/assets/bab668ba-bf17-4c71-be4e-b102202fce57"
    />
    
    ---------
    
    Co-authored-by: Sayan Sisodiya <sayan@openai.com>
  • register all mcp tools with namespace (#17404)
    stacked on #17402.
    
    MCP tools returned by `tool_search` (deferred tools) get registered in
    our `ToolRegistry` with a different format than directly available
    tools. this leads to two different ways of accessing MCP tools from our
    tool catalog, only one of which works for each. fix this by registering
    all MCP tools with the namespace format, since this info is already
    available.
    
    also, direct MCP tools are registered to responsesapi without a
    namespace, while deferred MCP tools have a namespace. this means we can
    receive MCP `FunctionCall`s in both formats from namespaces. fix this by
    always registering MCP tools with namespace, regardless of deferral
    status.
    
    make code mode track `ToolName` provenance of tools so it can map the
    literal JS function name string to the correct `ToolName` for
    invocation, rather than supporting both in core.
    
    this lets us unify to a single canonical `ToolName` representation for
    each MCP tool and force everywhere to use that one, without supporting
    fallbacks.
  • Support anyOf and enum in JsonSchema (#16875)
    This brings us into better alignment with the JSON schema subset that is
    supported in
    <https://developers.openai.com/api/docs/guides/structured-outputs#supported-schemas>,
    and also allows us to render richer function signatures in code mode
    (e.g., anyOf{null, OtherObjectType})
  • codex-tools: extract responses API tool models (#16031)
    ## Why
    
    The previous extraction steps moved shared tool-schema parsing into
    `codex-tools`, but `codex-core` still owned the generic Responses API
    tool models and the last adapter layer that turned parsed tool
    definitions into `ResponsesApiTool` values.
    
    That left `core/src/tools/spec.rs` and `core/src/client_common.rs`
    holding a chunk of tool-shaping code that does not need session state,
    runtime plumbing, or any other `codex-core`-specific dependency. As a
    result, `codex-tools` owned the parsed tool definition, but `codex-core`
    still owned the generic wire model that those definitions are converted
    into.
    
    This change moves that boundary one step further. `codex-tools` now owns
    the reusable Responses/tool wire structs and the shared conversion
    helpers for dynamic tools, MCP tools, and deferred MCP aliases.
    `codex-core` continues to own `ToolSpec` orchestration and the remaining
    web-search-specific request shapes.
    
    ## What changed
    
    - added `tools/src/responses_api.rs` to own `ResponsesApiTool`,
    `FreeformTool`, `ToolSearchOutputTool`, namespace output types, and the
    shared `ToolDefinition -> ResponsesApiTool` adapter helpers
    - added `tools/src/responses_api_tests.rs` for deferred-loading
    behavior, adapter coverage, and namespace serialization coverage
    - rewired `core/src/tools/spec.rs` to use the extracted dynamic/MCP
    adapter helpers instead of defining those conversions locally
    - rewired `core/src/tools/handlers/tool_search.rs` to use the extracted
    deferred MCP adapter and namespace output types directly
    - slimmed `core/src/client_common.rs` so it now keeps `ToolSpec` and the
    web-search-specific wire types, while reusing the extracted tool models
    from `codex-tools`
    - moved the extracted seam tests out of `core` and updated
    `codex-rs/tools/README.md` plus `tools/src/lib.rs` to reflect the
    expanded `codex-tools` boundary
    
    ## Test plan
    
    - `cargo test -p codex-tools`
    - `cargo test -p codex-core --lib tools::spec::`
    - `cargo test -p codex-core --lib tools::handlers::tool_search::`
    - `just fix -p codex-tools -p codex-core`
    - `just argument-comment-lint`
    
    ## References
    
    - [#15923](https://github.com/openai/codex/pull/15923) `codex-tools:
    extract shared tool schema parsing`
    - [#15928](https://github.com/openai/codex/pull/15928) `codex-tools:
    extract MCP schema adapters`
    - [#15944](https://github.com/openai/codex/pull/15944) `codex-tools:
    extract dynamic tool adapters`
    - [#15953](https://github.com/openai/codex/pull/15953) `codex-tools:
    introduce named tool definitions`