add0a7da0a
## Summary Sending a message to a chat whose run had ended could make the message vanish: the continuation run replayed already-answered messages, never processed the new one, and a page refresh lost it entirely. Chasing that report surfaced four composing message-loss bugs in the chat session runtime; this PR fixes all of them, each with a regression test. ## The fixes 1. **Stale resume cursor.** Records delivered while a run was suspended (the waitpoint path) advanced the SSE resume counter but not the committed-consume cursor, so the `session-in-event-id` header stamped on turn-completes went stale by one record per suspended turn. Continuation boots seed from that header, which is what made them replay already-processed messages. `session.in.wait()` now advances both cursors. 2. **Only the first buffered message dispatched.** Messages arriving during a turn are consumed into a buffer whose end-of-turn pickup dispatched only the first entry; the buffer was recreated each turn, so the rest were discarded, and since consuming a record commits the cursor the loss was permanent. A continuation boot's replay delivers several records back-to-back, which put the user's new message at index 1 or later. The buffer now outlives the turn and drains one message per turn in both `chat.agent` and `chat.createSession` (whose equivalent buffer was never read at all). 3. **Post-stop window in `chat.createSession`.** The turn's message listener stayed attached through the stopped turn's post-stream work, so a message sent shortly after stopping a turn was consumed into the dead steering queue and lost. The listener now detaches when the stream settles, matching the `chat.agent` loop. 4. **Handler leak on errored turns.** A turn that threw outside the streaming section (for example from an `onTurnStart` hook) leaked its message listener. Previously that silently lost mid-turn messages; with the loop-level buffer it would have duplicated them instead. The subscription handle is now detached by the turn's catch/finally, and `chat.createSession` defensively detaches its prior turn's listener when user code exits a turn without `complete()`/`done()`. ## Verification Reproduced end-to-end with the ai-chat reference project before the fix (message consumed but never answered, two replayed turns, gone on refresh) and verified after (single clean turn, survives refresh, turn-complete cursors strictly advancing). Regression tests in `packages/trigger-sdk/test/pending-message-drain.test.ts` cover all four, each verified red against the unfixed behavior. A smoke sweep of the standard chat scenarios (basic send, multi-turn, suspend/resume, mid-stream refresh, stop, steering, cancel + continue, and the `createSession` variant) passes on the final branch state.