Let any tool reveal deferred tools via `ToolReturn.tools_added`

Closed
DO
3mo ago
+9310-2388

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:

  1. ToolReturn.tools_added (new public field): a tool that makes deferred tools available declares their names on its rich return, and the executor records a ToolAvailabilityDeltaPart(added=..., tool_call_id=<the call's id>) in the same ModelRequest, immediately after that tool's ToolReturnPart — 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.
  2. Names recorded in history are the reveal state. RunContext.is_tool_available and the revealed_tool_names builders 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.enqueue adjacency 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.