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#60665.
Co-authored-by: Omar Shahine <omarshahine@users.noreply.github.com>
* bluebubbles: fall back unsupported reactions to love
iMessage tapback only supports love/like/dislike/laugh/emphasize/question.
Previously, `normalizeBlueBubblesReactionInput` threw when the input did
not map to one of those (e.g. a non-standard unicode emoji like 👀 used
to mean "seen, working on it"), which aborted the whole reaction request
and left the user with no feedback.
This splits the normalizer into a strict and lenient variant:
- `normalizeBlueBubblesReactionInputStrict` throws on unsupported input
and is used by validator-style callers (e.g. `resolveBlueBubblesAckReaction`
in monitor-processing.ts) that rely on the throw to detect misconfigured
ack reactions and skip them cleanly. This preserves the previous silent-skip
+ warn-once behavior for ack reactions configured with an unsupported
emoji.
- `normalizeBlueBubblesReactionInput` stays lenient and falls back to
`love` (or `-love` when removing) on unsupported input, so agent-driven
`sendBlueBubblesReaction` still produces a visible tapback instead of
failing the whole reaction request. Contract errors (empty input)
continue to bubble up.
`love` is chosen over `like` as the neutral default: `❤️` reads as a
general acknowledgment across chat norms, while `👍` carries an
agreement connotation that does not match the "seen, working on it"
semantic.
* CHANGELOG: note BlueBubbles reaction fallback
---------
Co-authored-by: Omar Shahine <10343873+omarshahine@users.noreply.github.com>
* fix(bluebubbles): restore inbound image attachments and accept updated-message events
Four interconnected fixes for BlueBubbles inbound media:
1. Strip bundled-undici dispatcher from non-SSRF fetch path so attachment
downloads no longer silently fail on Node 22+ (#64105, #61861)
2. Accept updated-message webhook events that carry attachments instead of
filtering them as non-reaction events (#65430)
3. Include eventType in the persistent GUID dedup key so updated-message
follow-ups are not rejected as duplicates of the original new-message (#52277)
4. Retry attachment fetch from BB API (2s delay) when the initial webhook
arrives with an empty attachments array — image-only messages and
updated-message events only (#67437)
Closes#64105, closes#61861, closes#65430.
* fix(bluebubbles): resolve review findings — SSRF policy, reuse extractAttachments, add tests
- F1 (BLOCKER): pass undefined instead of {} for SSRF policy when
allowPrivateNetwork is false, so localhost BB servers are not blocked.
- F2 (IMPORTANT): reuse exported extractAttachments() from monitor-normalize
instead of duplicating field extraction logic.
- F3 (IMPORTANT): simplify asRecord(asRecord(payload)?.data) to
asRecord(payload.data) since payload is already Record<string, unknown>.
- F4 (NIT): bind retryMessageId before the guard to eliminate non-null assertion.
- F5 (IMPORTANT): add 4 tests for fetchBlueBubblesMessageAttachments covering
success, non-ok HTTP, empty data, and guid-less entries.
- Add CHANGELOG entry for the user-facing fix.
* fix(ci): update raw-fetch allowlist line number after dispatcher strip
* fix(bluebubbles): resolve PR review findings (#67510)
- monitor-processing: move attachment retry into the !rawBody guard so
image-only new-message events that arrive with empty attachments and
empty text are recovered via a BB API refetch before being dropped.
The existing retry block at the end of processMessageAfterDedupe was
unreachable for this case because the !rawBody early-return fired
first. (Greptile)
- monitor: derive isAttachmentUpdate from the normalized message shape
instead of raw payload.data.attachments so updated-message webhooks
with attachments under wrapper formats (payload.message, JSON-string
payloads) are correctly routed through for processing instead of
silently filtered. (Codex)
- types: use bundled-undici fetch when init.dispatcher is present so
the SSRF guard's DNS-pinning dispatcher is preserved when this
function is called as fetchImpl from guarded callers (e.g. the
attachment download path via fetchRemoteMedia). Falls back to
globalThis.fetch when no dispatcher is present so tests that stub
globalThis.fetch keep working. (Codex)
- attachments: blueBubblesPolicy returns undefined for the non-private
case (matching monitor-processing's helper) so sendBlueBubblesAttachment
stops routing localhost BB through the SSRF guard. (Greptile)
- scripts/check-no-raw-channel-fetch: bump the types.ts allowlist line
to match the restructured non-SSRF branch.
* fix(bluebubbles): move attachment retry before rawBody guard, fix stale log
Move the attachment retry block (2s BB API refetch for empty attachments)
before the !rawBody early-return guard. Previously, image-only messages
with text='' and attachments=[] would be dropped by the !rawBody check
before the retry could fire, making fix#4 dead code for its primary
use-case. Now the retry runs first and recomputes the placeholder from
resolved attachments so rawBody becomes non-empty when media is found.
Also fix stale log message that still said 'without reaction' after the
filter was expanded to pass through attachment updates.
* fix(bluebubbles): revert undici import, restore dispatcher-strip approach
Revert the @claude bot's undici import in types.ts — it introduced a
direct 'undici' dependency that is not declared in the BB extension's
package.json and would break isolated plugin installs. Restore the
original dispatcher-strip approach which is correct: the SSRF guard
already completed validation upstream before calling this function as
fetchImpl, so stripping the dispatcher does not weaken security.
* fix(bluebubbles): remove dead empty-body recovery block in !rawBody guard
The empty-body attachment-recovery block added in the earlier PR revision
is now redundant because the main retry block was moved above the rawBody
computation in 0d7d1c4208. Worse, that leftover block reassigned the
(now-const) placeholder variable, throwing `TypeError: Assignment to
constant variable` at runtime for image-only messages — breaking the very
recovery path it was meant to protect (flagged by Codex on 4bfc2777).
Remove the dead block; the up-front retry already handles the image-only
case by recovering attachments before the rawBody computation, so once we
reach the !rawBody guard with an empty body it is genuinely empty and
should drop as before.
* fix(ci): update raw-fetch allowlist line after dispatcher-strip revert
279dba17d2 reverted types.ts back to the dispatcher-strip approach,
which put the `fetch(url, ...)` call at line 189 instead of line 198.
Bump the allowlist entry to match so `lint:tmp:no-raw-channel-fetch`
stops failing check-additional.
* test(pdf-tool): update stale opus-4-6 constant to opus-4-7
`628b454eff feat: default Anthropic to Opus 4.7` bumped the bundled
anthropic image default to `claude-opus-4-7` but missed updating the
`ANTHROPIC_PDF_MODEL` constant in pdf-tool.model-config.test.ts. The
tests now fail on any PR that runs the `checks-node-agentic-agents-plugins`
shard because the resolver returns 4-7 while the test asserts 4-6.
Bump the constant to 4-7 to match the bundled default.
---------
Co-authored-by: Lobster <10343873+omarshahine@users.noreply.github.com>
Adds an in-process startup catchup pass to the BlueBubbles channel that
queries BB Server for messages delivered since a persisted per-account
cursor and re-feeds each through the existing processMessage pipeline.
Fixes the missed-message hole documented in #66721: BB's WebhookService
is fire-and-forget on POST failure, and MessagePoller only re-fires
webhooks on BB-side reconnection events, not on webhook-receiver
recovery.
- New extensions/bluebubbles/src/catchup.ts with singleflight per
accountId, cursor persistence via the canonical state-paths
resolver, bounded query (perRunLimit + maxAgeMinutes), failure-held
cursor, truncation-aware page-boundary advancement, future-cursor
recovery, isFromMe filter (pre- and post-normalization).
- monitor.ts fires catchup as a background task after the webhook
target registers.
- config-schema.ts adds optional catchup block; accounts.ts adds
catchup to nestedObjectKeys for deep-merge per-account overrides.
- Dedupes against #66816's persistent inbound GUID cache.
- 22 scoped tests; full BB suite 411/411; pnpm check green; live E2E
on macOS 26.3 / BB Server 1.9.x recovered 3/3 missed messages.
Closes#66721.
Co-authored-by: Omar Shahine <omar@shahine.com>
BlueBubbles MessagePoller replays its ~1-week lookback window as new-message
webhooks after BB Server restart or reconnect. Add a persistent file-backed
GUID dedupe (TTL=7d) at the top of processMessage using createClaimableDedupe
from the Plugin SDK. Claim/finalize/release semantics ensure transient delivery
failures release the GUID so a later replay can retry.
Fixes#19176, #12053.
Co-authored-by: Omar Shahine <omar@shahine.com>
* fix(bluebubbles): lazy refresh Private API cache on send to prevent silent reply threading degradation (#43764)
When the 10-minute server info cache expires, sends requesting reply
threading or effects silently degrade to plain messages. Add a lazy
async refresh of the cache in the send path when Private API features
are needed but status is unknown, preserving graceful degradation if
the refresh fails.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(bluebubbles): apply lazy Private API refresh to attachment sends and add missing test coverage (#43764)
Attachment sends had the same cache-expiry bug as text sends: when the
10-minute Private API status cache TTL expired, reply threading metadata
was silently dropped. Apply the same lazy-refresh pattern from send.ts.
Also add the missing "refresh succeeds with private_api: false" test case
for both send.ts and attachments.ts — proves effects throw and reply
threading degrades without the "unknown" warning when the API is explicitly
disabled.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: update no-raw-channel-fetch allowlist for test-harness line shift
Adding fetchBlueBubblesServerInfo to the probe mock module shifted
globalThis.fetch in test-harness.ts from line 128 to 130.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Lobster <lobster@shahine.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>