Task
📝 **Task:** Implement `route_turn(turn: dict) -> str` that returns one of `'haiku'`, `'sonnet'`, `'opus'` based on these rules (in priority order):
1. If `turn['escalate']` is truthy → `'opus'`
2. Else if `turn['tool_count']` ≥ 1 → `'sonnet'`
3. Else if `len(turn['text'])` < 80 chars AND no images → `'haiku'`
4. Else → `'sonnet'` (default)
Then implement `estimate_cost(model: str, input_tokens: int, output_tokens: int) -> float`. Use these per-1M-token prices: Haiku 1/5, Sonnet 3/15, Opus 5/25 (input/output USD).
Finally, exercise the router on six turns and the cost estimator on two cases — three canonical routings plus two boundary cases (image-only long turn that falls through to the default, plus the zero-token cost edge):
```
haiku # short text, no tools, no images, no escalate
sonnet # tool_count >= 1
opus # escalate=True
sonnet # boundary: text < 80 but images > 0 — image branch breaks the haiku rule
sonnet # boundary: text >= 80 + no tools + no escalate — defaults to sonnet
0.051 # canonical Sonnet 2K/3K-token estimate
0.0 # boundary: zero tokens → zero cost
```
📋 Implement the function above. Tests run automatically.
💡 **Hint:** Re-read the theory if stuck.