c2db3ed7d8
A session interrupted by a gateway restart is flagged resume_pending and auto-continued on startup via _schedule_resume_pending_sessions(), which dispatches an empty-text internal MessageEvent. The recovery system note that should fill that empty turn is gated, in _run_agent(), on _interruption_is_fresh — the age of the LAST PERSISTED TRANSCRIPT ROW. For an active thread returned to after >1h of silence, that transcript clock is stale even though the interruption (last_resume_marked_at) is seconds old. The gate evaluates False, the note is not prepended, and the model receives a genuinely blank user turn — replying with confused 'that message came through blank' noise. Fix (two parts, both default-on, behavior unchanged for healthy turns): 1. resume_pending freshness now also considers last_resume_marked_at (the restart watchdog's own stamp). The branch fires when EITHER the transcript clock OR the resume mark is fresh, so the startup scheduler's freshness decision and the per-turn injection agree. 2. Empty-turn safety net: if the user turn is still blank after all injections AND the session is resume_pending, backfill a recovery note so a blank turn can never reach the model. Scoped to resume_pending so ordinary empty turns (e.g. uncaptioned image) are untouched. Adds 3 regression tests; the two core ones fail on the pre-fix logic.