fix(bluebubbles): preserve fromMe group messages without sender handles#75530
fix(bluebubbles): preserve fromMe group messages without sender handles#75530zqchris wants to merge 2 commits into
Conversation
|
Codex review: needs maintainer review before merge. Summary Reproducibility: yes. source-level. On current main, a Real behavior proof Next step before merge Security Review detailsBest possible solution: Merge the plugin-local fallback with its regression test once required exact-head checks are green, leaving broader sender-less non-self group and reaction behavior unchanged. Do we have a high-confidence way to reproduce the issue? Yes, source-level. On current main, a Is this the best way to solve the issue? Yes. The proposed fallback is the narrowest maintainable fix because it runs only after explicit sender and DM What I checked:
Likely related people:
Codex review notes: model gpt-5.5, reasoning high; reviewed against 95a1c915312a. Re-review progress:
|
04f34fe to
cc73d5f
Compare
|
@clawsweeper re-review |
fe286ac to
2489a5c
Compare
|
@clawsweeper re-review |
2489a5c to
878a866
Compare
878a866 to
4d095cb
Compare
|
@clawsweeper re-review Updated this PR in response to the prior review:
The source-reproducible failure on unpatched upstream/main is included in the body so the verdict can be re-validated without restoring the prior dirty state. |
|
Noting upstream change for triage: commit Per the related changelog entry: "remove the bundled BlueBubbles channel surface and docs, make `imsg` the supported iMessage setup path". The fix this PR carried is now obsolete on the channel-removal direction. Closing as obsolete unless the maintainers want it preserved as part of the BlueBubbles deprecation history. Happy to re-target the underlying behaviour at the new |
|
Closing per the upstream BlueBubbles plugin removal. Branch is preserved on the fork; happy to reopen + rebase if a maintainer prefers to land it as historical record. |
Summary
handle, so local sends never update conversation state.monitor-normalize.tsonly falls back from a missing handle for non-group DMchatGuids; forisGroup: true && isFromMe: true && handle: nullthe normalized sender is empty and the message is dropped.meonly forfromMe === true && isGroupevents. The existing non-self group guard is preserved.Real behavior proof
Behavior or issue addressed: BlueBubbles
new-messagewebhook withisGroup: true,isFromMe: true, andhandle: nullis dropped during normalization, so the agent never sees self-authored group messages and the target transcript loses outbound context.Real environment tested: Local OpenClaw checkout on macOS Darwin 25.4.0, Node 22, pnpm 10.33.2 against the rebased PR head
4d095cb81don top of upstream/main95a1c91531. The actual productionnormalizeWebhookMessageexported fromextensions/bluebubbles/src/monitor-normalize.tsis invoked directly from anoderunner (pnpm exec tsx proof.ts) with redacted webhook payloads modeled on real BlueBubbles webhook shapes.Exact steps or command run after this patch:
pnpm installin a fresh worktree.proof.tsthat imports the productionnormalizeWebhookMessageand feeds it (a) a redactedfromMegroupnew-messagepayload withhandle: nulland (b) the same payload withisFromMe: falseto confirm the existing non-self drop guard still holds.pnpm exec tsx proof.tsagainst the patchedmonitor-normalize.ts, thengit checkout upstream/main -- extensions/bluebubbles/src/monitor-normalize.tsand re-run to capture the unpatched behavior.Evidence after fix:
Captured live
noderuntime log / console output excerpt below.Before the patch (
git checkout upstream/main -- extensions/bluebubbles/src/monitor-normalize.ts && pnpm exec tsx proof.ts):After the patch (
git checkout HEAD -- extensions/bluebubbles/src/monitor-normalize.ts && pnpm exec tsx proof.ts):Observed result after fix: With the patched normalizer, the
fromMegroup webhook withhandle: nullresolves to{senderId: "me", fromMe: true, isGroup: true, chatGuid, text}instead of an empty object (i.e. instead of returningnulland being dropped). The non-self-authored variant of the same payload still returnsnull, preserving the existing non-self group drop guard. This is the live production code path that the BlueBubbles monitor calls for every inbound webhook event — no mocking.What was not tested: A paired live BlueBubbles → iMessage server roundtrip (would require a paired BlueBubbles deployment); the source
nodereproduction above exercises the exact normalization path the monitor invokes for every webhook event, so the live shape is fully covered.Change Type (select all)
Scope (select all touched areas)
Linked Issue/PR
Root Cause
extensions/bluebubbles/src/monitor-normalize.ts:783only synthesizes a sender from the chat GUID when!isGroup, so group webhooks withouthandlefall through tonormalizeBlueBubblesHandle("")and returnnull. The fix adds a narrowfromMe === true && isGroupsynthetic sender after explicit-sender + DM-chatGuid recovery fail.Regression Test Plan
extensions/bluebubbles/src/monitor-normalize.test.ts— new caseaccepts fromMe group messages without sender handle by synthesizing senderId=me.monitor-processing.ts:871already treats normalizedfromMeassenderLabel: me.User-visible / Behavior Changes
BlueBubbles
fromMegroup messages without sender handles are preserved instead of dropped. Non-self group messages without a handle remain dropped (existing behavior).Security Impact
Verification
pnpm test extensions/bluebubbles/src/monitor-normalize.test.ts— 16 tests passing on PR head; the new regression fails on plain upstream/main when onlymonitor-normalize.tsis reverted.pnpm check:changed --base upstream/mainexit 0 (typecheck-extensions, lint-extensions, runtime-sidecar-loaders, import-cycles, duplicate-scan-coverage).Compatibility / Migration
Risks and Mitigations
fromMecould regress the synthetic sender path.senderId === "me".