File size: 1,137 Bytes
81e5fe7 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | """The tool invocation seam (§8.4) — the one interface the TaskRunner calls.
The agent layer stays tool-agnostic (INV-7) by invoking every tool through this
protocol, never importing a tool module directly. The **tool team owns the
implementation** (KM-418); this file defines only the contract the TaskRunner
depends on.
Frozen guarantees the implementation must hold:
1. **Never throws.** A tool failure returns `ToolOutput(kind="error", error=...)`,
not an exception — the TaskRunner's degrade-and-continue (§7.4) relies on this.
(The TaskRunner still wraps calls defensively, as a backstop.)
2. **Returns the `ToolOutput` envelope** (§8.1) — structured data only, never
rendered tables or prose (that is the Assembler's job).
3. **`tool_name` comes from the registry** (§9.2); unknown names return an error
envelope rather than throwing.
"""
from __future__ import annotations
from typing import Any, Protocol, runtime_checkable
from ..planner.contracts import ToolOutput
@runtime_checkable
class ToolInvoker(Protocol):
async def invoke(self, tool_name: str, args: dict[str, Any]) -> ToolOutput: ...
|