fix(bluebubbles): include sender identity in group chat envelopes (#16326)

* fix(bluebubbles): include sender identity in group chat envelopes

Use formatInboundEnvelope (matching iMessage/Signal pattern) so group
messages show the group label in the envelope header and include the
sender name in the message body. ConversationLabel now resolves to the
group name for groups instead of being undefined.

Fixes #16210

Co-authored-by: zerone0x <hi@trine.dev>

* fix(bluebubbles): use finalizeInboundContext and set BodyForAgent to raw text

Wrap ctxPayload with finalizeInboundContext (matching iMessage/Signal/
every other channel) so field normalization, ChatType, ConversationLabel
fallback, and MediaType alignment are applied consistently.

Change BodyForAgent from the envelope-formatted body to rawBody so the
agent prompt receives clean message text instead of the [BlueBubbles ...]
envelope wrapper.

Co-authored-by: zerone0x <hi@trine.dev>

* docs: add changelog entry for BlueBubbles group sender fix (#16326)

* fix(bluebubbles): include id in fromLabel matching formatInboundFromLabel

Align fromLabel output with the shared formatInboundFromLabel pattern:
groups get 'GroupName id:peerId', DMs get 'Name id:senderId' when the
name differs from the id. Addresses PR review feedback.

Co-authored-by: zerone0x <hi@trine.dev>

---------

Co-authored-by: zerone0x <hi@trine.dev>
This commit is contained in:
Christian Klotz
2026-02-14 18:17:26 +00:00
committed by GitHub
parent 3369ef5aef
commit df7464ddf6
3 changed files with 160 additions and 8 deletions

View File

@@ -506,7 +506,15 @@ export async function processMessage(
? `${rawBody} ${replyTag}`
: `${replyTag} ${rawBody}`
: rawBody;
const fromLabel = isGroup ? undefined : message.senderName || `user:${message.senderId}`;
// Build fromLabel the same way as iMessage/Signal (formatInboundFromLabel):
// group label + id for groups, sender for DMs.
// The sender identity is included in the envelope body via formatInboundEnvelope.
const senderLabel = message.senderName || `user:${message.senderId}`;
const fromLabel = isGroup
? `${message.chatName?.trim() || "Group"} id:${peerId}`
: senderLabel !== message.senderId
? `${senderLabel} id:${message.senderId}`
: senderLabel;
const groupSubject = isGroup ? message.chatName?.trim() || undefined : undefined;
const groupMembers = isGroup
? formatGroupMembers({
@@ -522,13 +530,15 @@ export async function processMessage(
storePath,
sessionKey: route.sessionKey,
});
const body = core.channel.reply.formatAgentEnvelope({
const body = core.channel.reply.formatInboundEnvelope({
channel: "BlueBubbles",
from: fromLabel,
timestamp: message.timestamp,
previousTimestamp,
envelope: envelopeOptions,
body: baseBody,
chatType: isGroup ? "group" : "direct",
sender: { name: message.senderName || undefined, id: message.senderId },
});
let chatGuidForActions = chatGuid;
if (!chatGuidForActions && baseUrl && password) {
@@ -652,9 +662,9 @@ export async function processMessage(
.trim();
};
const ctxPayload = {
const ctxPayload = core.channel.reply.finalizeInboundContext({
Body: body,
BodyForAgent: body,
BodyForAgent: rawBody,
RawBody: rawBody,
CommandBody: rawBody,
BodyForCommands: rawBody,
@@ -689,7 +699,7 @@ export async function processMessage(
OriginatingTo: `bluebubbles:${outboundTarget}`,
WasMentioned: effectiveWasMentioned,
CommandAuthorized: commandAuthorized,
};
});
let sentMessage = false;
let streamingActive = false;