Skip to content

feat(chatwoot-adapter): history backfill (lazy + opt-in bulk) (#609)#27

Merged
rmyndharis merged 1 commit into
mainfrom
feat/chatwoot-history-backfill
Jul 3, 2026
Merged

feat(chatwoot-adapter): history backfill (lazy + opt-in bulk) (#609)#27
rmyndharis merged 1 commit into
mainfrom
feat/chatwoot-history-backfill

Conversation

@rmyndharis

Copy link
Copy Markdown
Owner

Implements the "initial history sync" P1 of rmyndharis/OpenWA#609. Pairs with OpenWA 0.8.5 (engine.getChatHistory).

Why

An opened Chatwoot conversation started mid-thread — agents lost the prior WhatsApp context. This backfills it, replacing the external sync-inbound.sh workaround.

Two modes (both off by default, composable)

  • Lazy — backfillLimit (messages per chat): when a chat first opens as a Chatwoot conversation, its recent history is replayed oldest→newest before the triggering message, so the thread reads chronologically and the live message's quote resolves against a just-posted source_id. Reactive context, exactly when an agent needs it.
  • Bulk — backfillAllOnce: a one-time sweep that imports every existing chat's history on setup (for mirroring a whole inbox). Sequential, best-effort, and run once per session behind a durable marker plus an in-memory in-flight guard.

Both reconstruct both directions: business/fromMe messages post as Chatwoot outgoing, contact messages as incoming. Historical media is uploaded fully (like live). Everything is deduped against the same markSeen store the live path uses, so the two modes compose and nothing double-posts.

Shape

  • Core (0.8.5) exposes engine.getChatHistory (gated by engine:read + active session, limit clamped host-side). This plugin pins minOpenWAVersion 0.8.5.
  • The per-message render and conversation resolve are extracted into a leaf relay.ts (relayMessage, ensureConversation), so live inbound (inbound.ts) and backfill (backfill.ts) render identically with no import cycle. postText/postMedia gain an optional message_type.

Not included (still #609 roadmap)

Retry/dead-letter, read receipts, and pinned-message banner — each needs a further host capability.

Tests

New backfill.test.ts: backfillHistory posts oldest→newest with fromMe→outgoing, dedups, and swallows a fetch failure; backfillAllChats sweeps each chat once, skips groups when relayGroups is off, survives a per-chat failure, and is a no-op after the run-once marker is set. Plus a client message_type: outgoing test. Full suite green (231 tests across all plugins), tsc --noEmit clean, plugin packages, catalog up to date.

Agents now see prior WhatsApp context in Chatwoot instead of a conversation
that starts mid-thread. Two composable modes, both off by default:

- Lazy (backfillLimit): when a chat first opens as a Chatwoot conversation,
  its recent messages (both directions, with media) are replayed
  oldest->newest before the triggering message, deduped against the live
  path so nothing double-posts.
- Bulk (backfillAllOnce): a one-time sweep imports every existing chat's
  history on setup, sequential and best-effort, run once per session behind
  a durable marker + an in-flight guard.

Business-side (fromMe) messages post as Chatwoot outgoing, contact messages
as incoming (postText/postMedia gain a message_type). The per-message render
and conversation resolve are extracted into a shared relay.ts (leaf module)
so live inbound and backfill render identically without an import cycle.

Bumps minOpenWAVersion to 0.8.5 (engine.getChatHistory) and the plugin to
0.3.0.
@rmyndharis rmyndharis merged commit d3ae81a into main Jul 3, 2026
1 check passed
@rmyndharis rmyndharis deleted the feat/chatwoot-history-backfill branch July 3, 2026 09:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant