cinny-threads: send thread read receipt against the thread's true latest reply
markThreadAsRead sent the receipt against thread.events[last]. Two ways
that fails to clear the thread server-side:
- The SDK's threadIdForReceipt() routes a receipt for the thread root,
or a relation to it (e.g. a reaction on the root), to the "main"
timeline, which never touches the thread's unread bucket.
- Receipting an event that is not the thread's true latest reply leaves
a residual server-side count, so the notification reappears on the
next sync/reload even for threads that were opened.
Target thread.lastReply() - the SDK's canonical latest m.thread reply,
taken from the authoritative thread timeline (including the bundled
latest_event from /threads) and the same event the SDK uses for its
read-up-to comparison. Fall back to a newest-first scan of the live
timeline / rendered replies only when the Thread object is not hydrated.
Assisted-by: Claude Code (claude-opus-4-8[1m])