← Back to PRs

#20860: feat(reactions): add configurable immediate reaction dispatch system

by davidrudduck open 2026-02-19 10:55 View on GitHub →
channel: bluebubbles channel: discord channel: signal channel: slack channel: telegram agents size: XL trusted-contributor
## Summary Adds configurable immediate reaction delivery with debouncing across all channel providers (Discord, Telegram, Slack, Signal, BlueBubbles). - **Per-channel config**: `reactionDelivery` ("deferred"|"immediate"), `reactionBundleWindowMs` (default: 2000ms), `reactionIncludeMessage` (bool) - **Debounced bundling**: Multiple reactions on the same message within the bundle window are grouped into a single dispatch - **Resilient fallback**: All providers wrap immediate dispatch in try/catch, falling back to deferred `enqueueSystemEvent` on failure (matching Signal's existing pattern) - **Multi-agent safe**: Uses `resolveAgentIdFromSessionKey()` for correct session store resolution - **Global singleton**: Debouncer is a process-wide singleton with mismatch detection for bundleWindowMs ### Key files - `src/infra/reaction-dispatch/` — debouncer, dispatch, global singleton, context builder, types (NEW) - `src/discord/monitor/listeners.ts` — immediate dispatch path - `src/telegram/bot.ts` — immediate dispatch path - `src/slack/monitor/events/reactions.ts` — immediate dispatch path with message fetch - `src/signal/monitor/event-handler.ts` — immediate dispatch path (reference implementation) - `extensions/bluebubbles/src/monitor-processing.ts` — immediate dispatch path - Config schema additions in `zod-schema.core.ts`, `types.base.ts`, `schema.help.ts`, `schema.labels.ts` ## Test plan - [ ] Verify typecheck passes (`pnpm tsgo`) - [ ] Verify build succeeds (`pnpm build`) - [ ] Test deferred mode (default): reactions enqueued via `enqueueSystemEvent` as before - [ ] Test immediate mode: set `reactionDelivery: "immediate"` on a channel, verify reactions are debounced and dispatched - [ ] Test fallback: simulate dispatch failure, verify graceful fallback to deferred path - [ ] Test bundling: rapid reactions on same message within window are grouped - [ ] Test multi-channel: different channels can have different `reactionDelivery` settings --- _Re-opened from #18976 (auto-closed during fork maintenance)._ <!-- greptile_comment --> <h3>Greptile Summary</h3> Adds configurable immediate reaction delivery with debouncing across Discord, Telegram, Slack, Signal, and BlueBubbles. Reactions can now trigger dedicated agent turns (immediate mode) or queue for next message (deferred mode, default). **Key changes:** - New `src/infra/reaction-dispatch/` module with debouncer, dispatch logic, and global singleton - Per-channel config: `reactionDelivery`, `reactionBundleWindowMs` (default 2000ms), `reactionIncludeMessage` - All providers wrap immediate dispatch in try/catch with fallback to deferred `enqueueSystemEvent` - Uses `resolveAgentIdFromSessionKey()` for multi-agent session store resolution - Drains pending reactions before user message turns (in `runPreparedReply`) **Minor improvement opportunity:** - Discord listener duplicates `emojiLabel`, `actorLabel`, `guildSlug`, and `channelLabel` calculation that already exists in the `resolveReactionBase()` helper (lines 407-418) <h3>Confidence Score: 4/5</h3> - Safe to merge with thorough implementation and proper error handling - Solid implementation with consistent error handling patterns, fallback mechanisms, and multi-agent safety. Minor code duplication in Discord listener doesn't affect correctness. All providers follow same resilient pattern with try/catch wrapping immediate dispatch. - No files require special attention — the minor duplication in `src/discord/monitor/listeners.ts` is a style improvement, not a functional issue <sub>Last reviewed commit: 90c8b9b</sub> <!-- greptile_other_comments_section --> <!-- /greptile_comment -->

Most Similar PRs