Skip to content

bluebubbles: forward per-group systemPrompt into GroupSystemPrompt#69198

Merged
omarshahine merged 3 commits into
mainfrom
bb-per-group-system-prompt
Apr 21, 2026
Merged

bluebubbles: forward per-group systemPrompt into GroupSystemPrompt#69198
omarshahine merged 3 commits into
mainfrom
bb-per-group-system-prompt

Conversation

@omarshahine

Copy link
Copy Markdown
Contributor

Summary

Add optional systemPrompt to the BlueBubbles per-group config and forward it into inbound context as GroupSystemPrompt, so configured per-group behavioral directives are injected into the agent's system prompt on every turn.

BlueBubbles was the only major channel missing this pattern — Discord, Feishu, Google Chat, IRC, LINE, Matrix, Nextcloud-Talk, Slack, and Telegram already wire GroupSystemPrompt into sessionCtx via the core resolver at src/auto-reply/reply/get-reply-run.ts:289. This PR is a scoped BB-only implementation that matches the existing Feishu shape (extensions/feishu/src/bot.ts:1023).

Why this matters

Operators can now set per-group rules via config alone, without agent-side workarounds. A motivating use case documented in the new docs section: with BlueBubbles Private API enabled, inbound messages carry [[reply_to:N]] short IDs and the agent has action=reply and action=react tools available. A per-group systemPrompt is the reliable way to keep the agent consistently using threaded replies and tapback reactions instead of dropping plain-text new messages.

Change shape

File Change
extensions/bluebubbles/src/config-schema.ts systemPrompt: z.string().optional() on bluebubblesGroupConfigSchema
extensions/bluebubbles/src/types.ts Matching field on hand-written BlueBubblesGroupConfig
extensions/bluebubbles/src/monitor-processing.ts GroupSystemPrompt: isGroup ? normalizeOptionalString(account.config.groups?.[peerId]?.systemPrompt) : undefined on finalizeInboundContext
extensions/bluebubbles/src/monitor.test.ts Two new tests: group-path threads value through; DM-path omits it
docs/channels/bluebubbles.md New "Per-group system prompt" section with the reply/react worked example
docs/.generated/config-baseline.sha256 Regenerated via pnpm config:docs:gen

No plugin-sdk surface change; no other channel touched. Core extension-agnostic boundary preserved.

Change Type

  • Feature

Scope

  • Integrations

Linked Issue

User-visible / Behavior Changes

  • New optional config field: channels.bluebubbles.accounts.<name>.groups.<peerId>.systemPrompt: string.
  • When set and the inbound message is a group message, the value is appended to the agent's system prompt for that turn (same mechanism already used by other channels).
  • DMs and unset groups: no behavior change.

Security Impact

None. The value is injected only into the agent's own system prompt (trusted config surface), not into any model-facing untrusted context. No new network, filesystem, or auth surface.

Rollback

Revert this commit. No config migration; the new field is additive and optional.

Test plan

  • pnpm test extensions/bluebubbles/src/monitor.test.ts — 72/72 pass, including two new assertions
  • pnpm check — tsgo (core + core:test + extensions + extensions:test), oxlint, webhook/auth/scope checks, import-cycle + madge all green
  • pnpm config:docs:check — schema baseline hash stable after regen

Changelog entry intentionally omitted here; will be added during /landpr.

@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation channel: bluebubbles Channel integration: bluebubbles size: S maintainer Maintainer-authored PR labels Apr 20, 2026
@greptile-apps

greptile-apps Bot commented Apr 20, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds an optional systemPrompt field to the BlueBubbles per-group config and forwards it into inbound context as GroupSystemPrompt, matching the established pattern already used by Feishu, LINE, Telegram, Discord, and others. A "*" wildcard fallback key is also supported (exact match preferred), and the implementation is gated to group messages only (DMs always receive undefined). The allowlist line number in scripts/check-no-raw-channel-fetch.mjs was correctly bumped to reflect the line shift introduced by the new type field.

Confidence Score: 5/5

Safe to merge — additive optional config field with no breaking changes and good test coverage.

All changes are additive, the implementation faithfully mirrors the Feishu/LINE pattern, four test cases cover the exact-match, wildcard-fallback, priority, and DM-exclusion paths, and the pnpm check suite is reported clean.

No files require special attention.

Reviews (2): Last reviewed commit: "bluebubbles: honor "*" wildcard fallback..." | Re-trigger Greptile

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9ba05e280c

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +1540 to +1542
GroupSystemPrompt: isGroup
? normalizeOptionalString(account.config.groups?.[peerId]?.systemPrompt)
: undefined,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Honor '*' fallback for GroupSystemPrompt

GroupSystemPrompt is currently resolved only from account.config.groups?.[peerId], so a default config like channels.bluebubbles.groups["*"].systemPrompt is silently ignored for all group chats. In this same flow, other per-group settings (e.g. requireMention/tools) go through group-policy resolvers that support wildcard fallback, so this introduces inconsistent behavior where operators cannot set one global group prompt and must duplicate the value per group ID.

Useful? React with 👍 / 👎.

@omarshahine

Copy link
Copy Markdown
Contributor Author

@greptile review — added '*' wildcard fallback per Codex consistency nit, plus CI allowlist line-number bump.

@openclaw-barnacle openclaw-barnacle Bot added the scripts Repository scripts label Apr 20, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ef0ba1ebab

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

historyLimit: z.number().int().min(0).optional(),
dmHistoryLimit: z.number().int().min(0).optional(),
textChunkLimit: z.number().int().positive().optional(),
sendTimeoutMs: z.number().int().positive().optional(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Remove no-op sendTimeoutMs config key

Adding sendTimeoutMs to the BlueBubbles schema exposes a user-facing setting that the runtime never consumes, so operators can set channels.bluebubbles(...).sendTimeoutMs and still get the existing hard-coded/per-call timeouts in send/probe paths. This creates a misleading config contract (it validates and appears in generated metadata/docs but has no behavior), which is likely to cause confusing timeout-debugging in production.

Useful? React with 👍 / 👎.

@omarshahine omarshahine force-pushed the bb-per-group-system-prompt branch from ef0ba1e to 29aae94 Compare April 21, 2026 02:03
@aisle-research-bot

aisle-research-bot Bot commented Apr 21, 2026

Copy link
Copy Markdown

🔒 Aisle Security Analysis

We found 1 potential security issue(s) in this PR:

# Severity Title
1 🟡 Medium Webhook payload can spoof group identity to select unintended per-group systemPrompt
1. 🟡 Webhook payload can spoof group identity to select unintended per-group systemPrompt
Property Value
Severity Medium
CWE CWE-345
Location extensions/bluebubbles/src/monitor-processing.ts:971-1547

Description

peerId for group messages is derived directly from webhook payload fields (chatGuid/chatIdentifier/chatId) and is then used to select channels.bluebubbles.groups[peerId].systemPrompt (with "*" fallback) which is injected into the agent context.

If an attacker can submit webhook requests with a valid webhook token (e.g., token leakage, proxy/log exposure, or an internal threat), they can craft chatGuid to impersonate a different group and cause the agent to apply a different/more-permissive systemPrompt than intended.

Key points:

  • Input control: peerId comes from webhook JSON fields without any independent validation that the event actually belongs to that chat.
  • Sensitive sink: GroupSystemPrompt is threaded into finalizeInboundContext, influencing the LLM system prompt and downstream behavior.

Vulnerable code (new behavior):

const peerId = isGroup
  ? (chatGuid ?? chatIdentifier ?? (chatId ? String(chatId) : "group"))
  : message.senderId;
...
GroupSystemPrompt: isGroup
  ? normalizeOptionalString(
      account.config.groups?.[peerId]?.systemPrompt ??
        account.config.groups?.["*"]?.systemPrompt,
    )
  : undefined,

Recommendation

Treat webhook payload fields (chatGuid/chatIdentifier/chatId/senderId) as untrusted even after endpoint-token auth.

Recommended mitigations (defense in depth):

  1. Add payload integrity/authenticity (preferred)
  • Configure BlueBubbles (or a gateway in front) to sign the raw request body with an HMAC secret.
  • Verify signature before parsing/processing, and reject if missing/invalid.

Example pattern:

import { createHmac, timingSafeEqual } from "node:crypto";

function verifyHmac(rawBody: string, signatureHeader: string, secret: string): boolean {
  const expected = createHmac("sha256", secret).update(rawBody).digest("hex");
  const got = signatureHeader.replace(/^sha256=/, "");
  return expected.length === got.length && timingSafeEqual(Buffer.from(expected), Buffer.from(got));
}
  1. Bind peerId to server-side lookups
  • When feasible, resolve/validate chatGuid via the BlueBubbles API using the authenticated account before using it as a policy/config key.
  • If the API indicates the chat doesn’t exist / isn’t accessible, reject the webhook event.
  1. Normalize and constrain group keys
  • Apply strict normalization and validation to peerId (e.g., disallow control chars, trim, reject unexpected unicode, enforce expected iMessage;+;... pattern when using GUIDs).
  1. Reduce blast radius
  • Avoid injecting per-group systemPrompt unless the message origin has been authenticated to that group.
  • Consider an allowlist of known group IDs learned during pairing / initial sync.

Analyzed PR: #69198 at commit 614b071

Last updated on: 2026-04-21T02:17:31Z

@omarshahine omarshahine merged commit b5f25de into main Apr 21, 2026
96 checks passed
@omarshahine omarshahine deleted the bb-per-group-system-prompt branch April 21, 2026 03:01
@omarshahine

Copy link
Copy Markdown
Contributor Author

Merged via squash as b5f25de on main. Closes #60665. Thanks for the feature!

loongfay pushed a commit to YuanbaoTeam/openclaw that referenced this pull request Apr 21, 2026
…penclaw#69198)

Forward per-group systemPrompt config into inbound context GroupSystemPrompt so configured group-specific behavioral instructions (for example threaded-reply and tapback conventions) are injected on every turn. Supports "*" wildcard fallback matching the existing requireMention pattern.

Closes openclaw#60665.

Co-authored-by: Omar Shahine <omarshahine@users.noreply.github.com>
lovewanwan pushed a commit to lovewanwan/openclaw that referenced this pull request Apr 28, 2026
…penclaw#69198)

Forward per-group systemPrompt config into inbound context GroupSystemPrompt so configured group-specific behavioral instructions (for example threaded-reply and tapback conventions) are injected on every turn. Supports "*" wildcard fallback matching the existing requireMention pattern.

Closes openclaw#60665.

Co-authored-by: Omar Shahine <omarshahine@users.noreply.github.com>
ogt-redknie pushed a commit to ogt-redknie/OPENX that referenced this pull request May 2, 2026
…penclaw#69198)

Forward per-group systemPrompt config into inbound context GroupSystemPrompt so configured group-specific behavioral instructions (for example threaded-reply and tapback conventions) are injected on every turn. Supports "*" wildcard fallback matching the existing requireMention pattern.

Closes openclaw#60665.

Co-authored-by: Omar Shahine <omarshahine@users.noreply.github.com>
zhonghe0615 pushed a commit to zhonghe0615/openclaw that referenced this pull request May 7, 2026
…penclaw#69198)

Forward per-group systemPrompt config into inbound context GroupSystemPrompt so configured group-specific behavioral instructions (for example threaded-reply and tapback conventions) are injected on every turn. Supports "*" wildcard fallback matching the existing requireMention pattern.

Closes openclaw#60665.

Co-authored-by: Omar Shahine <omarshahine@users.noreply.github.com>
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 9, 2026
…penclaw#69198)

Forward per-group systemPrompt config into inbound context GroupSystemPrompt so configured group-specific behavioral instructions (for example threaded-reply and tapback conventions) are injected on every turn. Supports "*" wildcard fallback matching the existing requireMention pattern.

Closes openclaw#60665.

Co-authored-by: Omar Shahine <omarshahine@users.noreply.github.com>
globalcaos pushed a commit to globalcaos/tinkerclaw that referenced this pull request May 13, 2026
…penclaw#69198)

Forward per-group systemPrompt config into inbound context GroupSystemPrompt so configured group-specific behavioral instructions (for example threaded-reply and tapback conventions) are injected on every turn. Supports "*" wildcard fallback matching the existing requireMention pattern.

Closes openclaw#60665.

Co-authored-by: Omar Shahine <omarshahine@users.noreply.github.com>
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 24, 2026
…penclaw#69198)

Forward per-group systemPrompt config into inbound context GroupSystemPrompt so configured group-specific behavioral instructions (for example threaded-reply and tapback conventions) are injected on every turn. Supports "*" wildcard fallback matching the existing requireMention pattern.

Closes openclaw#60665.

Co-authored-by: Omar Shahine <omarshahine@users.noreply.github.com>
jameslcowan pushed a commit to jameslcowan/openclaw that referenced this pull request Jun 2, 2026
…penclaw#69198)

Forward per-group systemPrompt config into inbound context GroupSystemPrompt so configured group-specific behavioral instructions (for example threaded-reply and tapback conventions) are injected on every turn. Supports "*" wildcard fallback matching the existing requireMention pattern.

Closes openclaw#60665.

Co-authored-by: Omar Shahine <omarshahine@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: bluebubbles Channel integration: bluebubbles docs Improvements or additions to documentation maintainer Maintainer-authored PR scripts Repository scripts size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: per-channel systemPrompt support for BlueBubbles (and other channels)

1 participant