Context
odek PR BackendStack21/odek#180 ships the turn_started wire frame (v1.42.0): emitted for every turn immediately after the session frame and before the first streamed frame.
Streamed frames (thinking / token / tool_call / tool_result / done / error) now carry the same turn_id while the turn is active. Docs: odek docs/WEBUI.md (frame table + compatibility matrix).
Task
Make bodek consume turn_started as the primary trigger for opening the streaming card, superseding the lazy-open heuristic shipped as the client-side mitigation on 2026-09-02.
- Primary path: open/upsert the streaming card on
turn_started, keyed by turn_id. Idempotent: a replayed or duplicate turn_started must never open a second card.
- Supersede the heuristic: the lazy-open (first inbound stream while idle) drops to belt-and-suspenders — keep it, but it should no longer be load-bearing.
- Wake turns: rely on
initiated: "system" from turn_started instead of the session frame's system_initiated stamp (the stamp remains for old clients but is deprecated as a signal).
- Reconnect attribution (optional polish): on reconnect mid-turn, use
turn_id on streamed frames to attach strays to the existing card instead of guessing from idle state.
Acceptance
- Card opens from
turn_started on both operator and system-initiated (wake) turns.
- Duplicate/replayed
turn_started is a no-op (upsert by turn_id).
- Lazy-open heuristic retained only as fallback.
- Old-odek compatibility: if the server never sends
turn_started, behavior degrades to today's (heuristic path still works).
Context
odek PR BackendStack21/odek#180 ships the
turn_startedwire frame (v1.42.0): emitted for every turn immediately after thesessionframe and before the first streamed frame.{ "type": "turn_started", "turn_id": "t_<hex>", "session_id": "…", "initiated": "operator" | "system", "model": "…" }Streamed frames (
thinking/token/tool_call/tool_result/done/error) now carry the sameturn_idwhile the turn is active. Docs: odekdocs/WEBUI.md(frame table + compatibility matrix).Task
Make bodek consume
turn_startedas the primary trigger for opening the streaming card, superseding the lazy-open heuristic shipped as the client-side mitigation on 2026-09-02.turn_started, keyed byturn_id. Idempotent: a replayed or duplicateturn_startedmust never open a second card.initiated: "system"fromturn_startedinstead of thesessionframe'ssystem_initiatedstamp (the stamp remains for old clients but is deprecated as a signal).turn_idon streamed frames to attach strays to the existing card instead of guessing from idle state.Acceptance
turn_startedon both operator and system-initiated (wake) turns.turn_startedis a no-op (upsert byturn_id).turn_started, behavior degrades to today's (heuristic path still works).