Issue 2: card path skipped relay in outbound.ts
- Extract triggerRelay() helper to avoid duplication
- Card branch now calls triggerRelay() before returning instead of early-returning without relay
- Also passes threadId to relay so topic threads are preserved
Issue 4: streaming messageId any cast + closeStreaming timing
- Add getMessageId() typed accessor to FeishuStreamingSession
- Save messageId via streaming.getMessageId() BEFORE calling closeStreaming()
(closeStreaming sets streaming=null, making post-close access always undefined)
- Replace (streaming as any)?.state?.messageId with the typed accessor
Issue 5: sendChunk callbacks missing return value
- sendChunkedTextReply callbacks for both card and text paths now use
'return sendStructuredCardFeishu(...)' / 'return sendMessageFeishu(...)'
so result?.messageId is correctly captured in lastMessageId
Issue 8: relay missing topic/thread metadata
- Add threadId field to RelayOutboundParams
- Populate root_id/thread_id in synthetic event from threadId
- Pass rootId from reply-dispatcher relay calls
- Pass threadId from outbound.ts sendText relay calls