Keep OpenAI tool search server-side alongside deferred capabilities and unify the `tool_availability_delta` profile field
Stacked on #6793 — retarget to main when that merges; only the last four commits are new.
When a run mixes searchable deferred tools with lazy-capability tools on an OpenAI Responses model with native tool search (gpt-5.4/5.5/5.6 + first-party provider), we currently force the search client-side (execution: 'client' dispatching into our local search_tools), because the capability-gated tools sit in tools with the same defer_loading flag the search corpus uses and server-side search would surface them before their capability loads.
That forcing is only necessary on providers whose reveal channel references pre-advertised tools. On first-party OpenAI Responses, a reveal is an additional_tools input item carrying the full definition — no pre-advertisement needed — so a capability-gated tool can stay off the wire entirely until its capability loads, and server-side search over the searchable corpus is safe: it cannot reveal what it never saw.
Live-verified (2026-08-03, gpt-5.6 + gpt-5.4-mini, with controls): server-executed tool_search coexists with an additional_tools item declaring a never-advertised tool (the model calls it; without the item it can't); server-side search still works over the corpus; and the item is cache-safe (warmed 2430-token prefix, 2427 read from cache after appending the item; mutating tools[] as control reads 0).
Changes
- Unify the two delta profile flags (both introduced on #6793, unreleased, so no shim):
anthropic_supports_tool_availability_deltaandopenai_responses_supports_tool_availability_deltabecome one generic field,tool_availability_delta: Literal['by_reference', 'with_definitions'] | None.'by_reference'= Anthropictool_addition(reveal references a pre-advertised deferred entry);'with_definitions'= OpenAI Responsesadditional_tools(reveal carries the definitions; Kimi K3 will be a second'with_definitions'renderer, #6977);None= project viaprepare_messages. This is the axis the resolution logic needs, and it answers the flag-collapse question raised in #6977. Naming is open to bikeshedding. - Keep capability-gated tools off the wire when reveals carry definitions: Rule 3 in
Model._resolve_native_tool_swapnow treats a capability-gated deferred tool on a'with_definitions'model like a tool the request can't hide in place — omitted while hidden, demoted to a plain visible definition once revealed, at which point the adapter carries it in the append-onlyadditional_toolsitem andtoolsstays byte-identical. - Scope the client-side forcing to
'by_reference'models:_resolve_tool_search_native_for_capability_gated_toolsno longer downgradesToolSearchTool(strategy=None)to'custom'when there is nothing on the wire to leak. Anthropic and third-party Responses (no delta channel) keep the existing forcing.
Mixed-corpus wire on gpt-5.6 before and after a capability load, asserted byte-identically: tool_search (server-executed), load_capability, searchable tools with defer_loading: true — the capability tool never appears in tools, and after the load an additional_tools item carries its full definition. A live two-interaction cassette puts the path under the autouse cache-prefix checker, which passes without exemption.
Follow-up: #7087 extends the same idea to Anthropic via lazy advertisement (deferred entries are outside Anthropic's cache key), which would drop the forcing there too.
Checklist
- Any AI generated code has been reviewed line-by-line by the human PR author, who stands by it.
- No breaking changes in accordance with the version policy.
- PR title is fit for the release changelog.