← Back to PRs

#15240: fix(bluebubbles): URL dropped when sent in same iMessage bubble as text (#15065)

by yinghaosang open 2026-02-13 06:16 View on GitHub →
channel: bluebubbles stale size: M
## Summary When a user sends text + URL in the same iMessage bubble, macOS splits it into two `chat.db` rows with different GUIDs and no `associatedMessageGuid`. BlueBubbles delivers both as separate webhooks ~870ms apart, but the debouncer keyed each by `messageId`, so they got different keys and never merged. The assistant only saw the text — the URL got dropped. Closes #15065 lobster-biscuit ## Root Cause `buildKey` in `getOrCreateDebouncer` (`extensions/bluebubbles/src/monitor.ts`) fell through to the `msg:<messageId>` path for URL balloons that lack `associatedMessageGuid`. Since both the text and the balloon have different GUIDs, they flushed independently. The default debounce window (500ms) was also too short — URL balloons arrive ~870ms after the text. ## Changes - Inbound (not from-me) messages now use sender+chat keys instead of message-id keys, so text and subsequent URL balloon land in the same debounce bucket. This matches the iMessage native channel's debounce strategy (`monitorIMessageProvider` in `src/imessage/monitor/monitor-provider.ts`). - Extracted `buildChatKey` helper with type prefixes (`chat_guid:`, `chat_identifier:`, `chat_id:`) consistent with `formatGroupAllowlistEntry`, avoiding cross-identifier-type collisions. - `DEFAULT_INBOUND_DEBOUNCE_MS` bumped from 500 to 1000 (covers the observed ~870ms balloon delay with margin, well below typical human inter-message gaps of 2s+). - From-me messages keep message-id keying to avoid merging distinct outbound messages. ### Before / After - Before: text + URL in same iMessage bubble → assistant sees only the text, URL is lost - After: both coalesce into a single dispatch containing text and URL ## Tests - `monitor.test.ts` — new test: sends text then URL balloon (different GUIDs, no `associatedMessageGuid`, `balloonBundleId` set) 870ms apart; verifies they coalesce into a single dispatch. Fails before fix, passes after. - `monitor.test.ts` — new test: two separate inbound messages from the same sender outside the debounce window are dispatched independently (proves the window doesn't over-merge). - Existing debounce test timer adjusted from 600ms to 1100ms to match the new default window. - All 53 tests in `monitor.test.ts` pass. - `pnpm build && pnpm check` pass (lint/format clean on changed files). <!-- greptile_comment --> <h2>Greptile Overview</h2> <h3>Greptile Summary</h3> Fixes a bug where a URL sent in the same iMessage bubble as text was dropped by the BlueBubbles extension. macOS splits such messages into two `chat.db` rows with different GUIDs and no `associatedMessageGuid`, causing the debouncer to bucket them separately and lose the URL. - **Debounce key change**: Inbound (not from-me) messages now use `sender + chat` keying instead of `messageId` keying, matching the iMessage native channel's debounce strategy. This ensures text and subsequent URL balloon land in the same debounce bucket. - **`buildChatKey` helper**: Extracted a type-prefixed helper (`chat_guid:`, `chat_id:`, `chat_identifier:`) consistent with `formatGroupAllowlistEntry`, preventing cross-identifier-type collisions. - **Debounce window**: `DEFAULT_INBOUND_DEBOUNCE_MS` increased from 500ms to 1000ms to cover the observed ~870ms balloon delay. - **From-me messages**: Retain message-id keying to avoid merging distinct outbound messages. - **Tests**: Two new tests verify coalescing behavior and independent dispatch outside the debounce window. Existing test timer adjusted for the new window. <h3>Confidence Score: 4/5</h3> - This PR is safe to merge — the fix is well-scoped, addresses a real bug with the correct approach, and has good test coverage. - Score of 4 reflects a clean, well-tested fix that aligns with established patterns (iMessage native channel debounce strategy, `formatGroupAllowlistEntry` prefix convention). The only minor concern is the theoretical risk of over-merging two genuinely distinct rapid-fire inbound messages within the 1000ms window, but this is acknowledged in comments, mitigated by the typical 2s+ human inter-message gap, and `combineDebounceEntries` preserves all text content even if coalescing occurs. Previous review comments about key collisions and over-merging have been addressed. - No files require special attention. Both changed files are well-structured and the test coverage is solid. <sub>Last reviewed commit: 691cffd</sub> <!-- greptile_other_comments_section --> <!-- /greptile_comment -->

Most Similar PRs