- Extract quotedPlatformMsgId from Baileys contextInfo.stanzaId in normalizer
- Thread quotedPlatformMsgId through IngestJobData → ingest processor → Message record
- Widen ingest processor group/tenant selects to include name and organizationId
- Add replyToMessageId, sourceGroupName, organizationId to StreamMessagePayload and XADD
- Update StreamRecord, parseFields, and streamMessages in AI stream service with new fields
- Add optional ?tenant= and ?source= SSE filters; cursor advances for filtered entries
so Last-Event-ID resume remains accurate across reconnects
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces the Redis-over-Tailscale handoff with a far simpler HTTP surface
on the existing public API — no Redis client, Tailscale, or firewall changes
needed on the dev side.
- GET /ai/stream — SSE feed of every ingested message, reads the internal
Redis stream; resumes from Last-Event-ID on reconnect (no missed messages);
heartbeats keep the connection warm through proxies
- POST /ai/drafts — writeback: creates a ContentDraft (PENDING_REVIEW) that
shows up in the admin /admin/drafts review UI
- AiStreamGuard — static bearer token auth (AI_STREAM_TOKEN), accepts header
or ?token= query for browser EventSource
- Remove Tailscale sidecar + public Redis port; Redis stays internal-only
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>