Separate effective tool definitions from tool execution state

Author: adtyavrdhnCreated Sep 18, 2026Updated Sep 18, 2026
Labelsrefactortools

Problem

ToolsetTool currently mixes the effective ToolDefinition with execution state. CombinedToolset therefore retains a complete source_tool, including an older definition, while wrappers update the outer definition. This caused #7380 and requires #8082 to synchronize definitions, timeout, and toolset identity manually.

combined tool
├── effective tool_def
└── source_tool
    ├── stale tool_def
    └── callable / validator / routing state

Suggested direction

Keep exactly one effective definition and separate it from the execution handle:

python
@dataclass
class ExecutableTool:
    tool_def: ToolDefinition
    execution: ToolExecution
python
await tool.execution.call(
    args=tool_args,
    ctx=ctx,
    tool_def=tool.tool_def,
)

Wrappers should replace only the effective definition. Execution-specific state should contain the callable, validators, retry state, source toolset, and original registration identity—but not another authoritative ToolDefinition. Fields such as timeout should not be cached separately from tool_def.timeout.

Plan

  1. Document the invariant that preparation and execution use the same effective definition.
  2. Introduce an internal execution handle without changing public behavior.
  3. Move duplicated execution fields behind that handle incrementally.
  4. Simplify CombinedToolset so it routes execution without retaining a competing definition.
  5. Cover nested combined/wrapper compositions, metadata, timeout, approval/defer state, and toolset identity.

This should follow the narrow fix in #8082 rather than delay it.