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: ...