···11+"""Provider-neutral data types shared by the AgentLoop, Provider, and Broker.
22+33+These shapes are deliberately independent of any vendor wire format. Each concrete
44+Provider translates between these and its backend's request/response JSON, so the
55+AgentLoop and Broker never branch on which backend is configured (PLAN.md §6.3).
66+"""
77+88+from dataclasses import dataclass
99+from typing import Any
1010+1111+1212+@dataclass
1313+class ToolCall:
1414+ """A model's request to invoke one tool, normalized across providers."""
1515+1616+ id: str
1717+ name: str
1818+ arguments: dict
1919+ # Set when the provider could not parse the model's raw arguments as a JSON
2020+ # object. The broker turns this into an error result so the model can retry,
2121+ # rather than the harness crashing on malformed output.
2222+ argument_error: str | None = None
2323+2424+2525+@dataclass
2626+class AssistantTurn:
2727+ """One assistant response, normalized across providers."""
2828+2929+ text: str | None
3030+ tool_calls: list[ToolCall]
3131+ raw: dict[str, Any] # provider-native message, appended to the transcript verbatim
3232+ stop_reason: str # "tool_calls" | "end" | "length" | "error"
3333+3434+3535+@dataclass
3636+class ToolResult:
3737+ """The outcome of brokering one tool call.
3838+3939+ The provider renders this into its backend's native tool-result message.
4040+ """
4141+4242+ call_id: str
4343+ output: str
4444+ is_error: bool
4545+4646+4747+# --- Streaming events ---------------------------------------------------------
4848+#
4949+# A provider's stream() yields these as a turn is generated. The AgentLoop
5050+# forwards TextDelta to the UI as text arrives and uses the terminal
5151+# TurnComplete to broker any tool calls and extend the transcript. Keeping
5252+# these provider-neutral means the loop never branches on the backend.
5353+5454+5555+@dataclass
5656+class TextDelta:
5757+ """A chunk of assistant text produced mid-turn."""
5858+5959+ text: str
6060+6161+6262+@dataclass
6363+class ToolOutputDelta:
6464+ """A chunk of command output produced while a tool is still running."""
6565+6666+ call: ToolCall
6767+ text: str
6868+6969+7070+@dataclass
7171+class TurnComplete:
7272+ """Terminal stream event carrying the fully assembled turn."""
7373+7474+ turn: AssistantTurn
7575+7676+7777+# What a provider's stream() yields.
7878+StreamEvent = TextDelta | TurnComplete