Repository navigation
feat(whatsapp): durably observe approved group history - #85490
NehoraiHadad wants to merge 7 commits into
Conversation
|
Live deployment datapoint. We ran this branch (merged together with the botIds device-suffix fix) against a real WhatsApp group: Baileys linked-device session, Observed over a multi-activation session:
Happy to share redacted logs if useful. |
2daa140 to
226aa1c
Compare
226aa1c to
9a1c45d
Compare
feat(whatsapp): durably observe approved group history Well-structured feature with thorough tests: per-(chat, sender) watermarks, ring-buffer bound, per-chat isolation, and prompt-injection care (neutralized names,
|
|
Full live E2E validation (2026-08-16). Ran this branch (as Verified end-to-end on a live group:
Two deployment findings from the same session, filed separately since they're pre-existing and not caused by this PR: #87830 (routed-turn authorization never consults On the review points above: (1) agreed — collapsing body newlines like the name handling is cheap hardening, will fold into this branch; (2) correct, native-bridge only for now — will state that explicitly in the docs section; (3) fair point on the watermark advancing before the event is fully built, and on "durably" — the durable half is the store, the backfill window is in-memory as documented; (4) will add the comment on the merge interaction. |
|
Review follow-ups pushed (
Also from the review: "durably observe" refers to the store (one row per message, survives restart), not the backfill window, which the docs already describe as bounded and in memory. Tests: |
The observed-history block is a flat list of "[name] text" lines, but only sender names were neutralized; bodies were injected verbatim. WhatsApp messages can contain newlines, so any sender could forge extra lines or a second "[Recent group messages]" header inside the block. Bodies now go through the same neutralizer as names, with the length bound left to the ring buffer rather than the per-line cap. Also documents the two ordering decisions reviewers asked about: when the per-sender watermark advances relative to delivery, and that these helpers are only called by the native Baileys adapter. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WoHsiYcUqXNyasB1Tvq2TY
Both group-history features are wired into the local Baileys adapter only. The Cloud API adapter inherits the shared behavior mixin but never calls them, so history_backfill and observe_unmentioned_group_messages silently do nothing on a Cloud API connection. Say so where the settings are documented instead of leaving it to be discovered. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WoHsiYcUqXNyasB1Tvq2TY
Keeping only the first context of a batch is the dedup rule, not an oversight: the watermark advances once per build, so batched events carry disjoint windows and merging them would splice a second block header into the prompt. Record the trade-off at the merge site. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WoHsiYcUqXNyasB1Tvq2TY
09a80c8 to
723d02d
Compare
|
Rebased onto current New head: Conflict resolved in Validation:
|
Summary
Problem
With
require_mention: true, WhatsApp drops ambient group messages before aMessageEventreaches the session store. The next addressed turn therefore has neither durable searchable history nor the nearby conversation that made a follow-up meaningful.The initial implementation also exposed three edge cases:
observed=Truerows were withheld from ordinary user replay only when an adapter prompt marker was present;Behavior
Both observation and legacy RAM backfill remain opt-in:
For an approved ambient message, the adapter:
observed=Trueuser row to the existingSessionStore/state.db;The gateway removes observed rows from replay regardless of adapter prompt markers. Only an observer-marked addressed group turn may render them, and it receives at most the latest 50 as a separate context-only block. Markerless cron,
send_message, and non-adapter dispatches receive no observed chatter. When durable observation is active for that chat, the duplicate RAMchannel_contextis suppressed. When durable observation is inactive, the existing RAM backfill behavior is preserved.WhatsApp text batching uses raw
(profile, chatId, senderId)identity, while the eventual agent session remains shared at the group level. Commands deliberately retain their sender-scoped source and original text so existing slash authorization remains fail-closed.Scope and privacy
group_policy: open.observe_allowed_chatsmust resolve to an explicit non-empty group allowlist; it can narrowgroup_allow_from.Tests
Validation on current
main:This supersedes the PR's original backfill-only implementation while preserving its immediate 50-message context goal exactly once.