Let any tool reveal deferred tools via `ToolReturn.tools_added`
Stacked on #6793 (base branch tool-availability-delta); retarget to main when that merges.
What this does
Capability-load tool reveals stop being special-cased. Two building blocks any library user can compose now carry every reveal:
ToolReturn.tools_added(new public field): a tool that makes deferred tools available declares their names on its rich return, and the executor records aToolAvailabilityDeltaPart(added=..., tool_call_id=<the call's id>)in the sameModelRequest, immediately after that tool'sToolReturnPart— the delta is a side effect of the call, and its placement now says so. Parallel tool calls each keep their own return/delta adjacency.- Names recorded in history are the reveal state.
RunContext.is_tool_availableand therevealed_tool_namesbuilders reduce to two arms — always-visible, or named in history — with no knowledge of capabilities at all.
load_capability becomes a plain consumer of (1): it reads its capability's tool names from the already-resolved ctx.tools and returns them on tools_added. The headline test is a user-built mini-load_capability: a plain tool with ToolReturn(tools_added=['get_weather']) reveals a deferred tool with zero capability machinery, and the model calls it on the next step.
The before_model_request hook that previously authored capability deltas demotes to backfill for histories that can't self-author: pre-existing ID-only records, and Temporal (tool bodies run in activities without a tool_manager). Its discovered_tool_names dedup prevents double-authoring; the capability-ID→definitions derivation survives only there, marked as legacy support.
Semantic shift (intended)
Reveals become durable history facts, exactly like tool-search discoveries: a recorded delta keeps revealing its names on resume regardless of current capability configuration (rendering still filters names against currently-served definitions). The old behavior — a loaded capability ID only revealing tools while the capability is deferred in the current run — remains covered as an ID-only-backfill configuration test. Cross-provider replay snapshots moved the delta beside the load return; replay behavior itself is unchanged.
Not built, deliberately
- Anthropic
tool_reference-inside-tool-result rendering for call-tied deltas: plausible optimization, behaviorally equivalent to the system-entry rendering a step later, not worth the provider-support matrix today. ctx.enqueueadjacency for the non-tool-tied case: #7014.
Open review item
Whether tools_added is the final public field name.
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 — the reveal-state shift only affects the unreleased #6793 surface.
- PR title is fit for the release changelog.