#15956: feat(signal): enhanced inbound message handling
docs
channel: signal
gateway
size: XL
trusted-contributor
Cluster:
Slack and Signal Enhancements
## Summary
Signal inbound events currently drop or flatten important metadata (multi-attachment details, quotes, edits, stickers, polls, contacts, link previews, and text styles). That makes downstream hooks/templates/agents lose context, and it can also cause command or mention logic to misfire.
This PR upgrades Signal inbound normalization so that richer Signal payloads are preserved in `MsgContext` while keeping existing single-attachment behavior backward-compatible.
This matters because users can send far more than plain text in Signal, and missing metadata causes:
- quoted replies to lose who or what is being replied to,
- only one attachment to be represented even when multiple exist,
- stickers/polls/contacts to lose semantics,
- styled text to interfere with control-command and mention detection,
- and debounce merging to accidentally blend per-message metadata that should stay atomic.
## What Changed
### Enriched Signal inbound context
- `src/signal/monitor/event-handler.ts`: Captures full media arrays (`MediaPaths`, `MediaTypes`, `MediaCaptions`, `MediaDimensions`) and keeps first-item aliases (`MediaPath`, `MediaType`, `MediaCaption`, `MediaDimension`) for compatibility.
- `src/signal/monitor/event-handler.ts`: Handles sticker attachments as media and emits sticker metadata into `UntrustedContext`.
- `src/signal/monitor/event-handler.ts`: Maps quote metadata to `ReplyToId`, `ReplyToBody`, `ReplyToSender`, and `ReplyToIsQuote`.
- `src/signal/monitor/event-handler.ts`: Maps edit metadata to `EditTargetTimestamp`.
- `src/signal/monitor/event-handler.ts`: Adds link-preview/contact/poll context into `UntrustedContext`.
- `src/signal/monitor/event-handler.ts`: Uses `Promise.allSettled` for attachment fetches so one failed download does not discard other successful attachments.
### Safer mention and command behavior with styled text
- `src/signal/monitor/event-handler.ts`: Introduces text-style rendering (`applySignalTextStyles`) and keeps plain text separately for command/mention checks.
- `src/signal/monitor/event-handler.ts`: Mention and control-command matching now run on plain text, avoiding false negatives/positives from markdown markers.
- `src/signal/monitor/mentions.ts`: `renderSignalMentions` now returns expanded text plus offset-shift metadata so style ranges remain aligned after mention replacement.
### Debounce behavior for richer events
- `src/signal/monitor/event-handler.ts`: Debounce merging remains for plain text, but avoids carrying per-message reply/edit/media fields across merged entries.
- `src/signal/monitor/event-handler.ts`: Merges `UntrustedContext` across debounced entries so Signal metadata (for example poll votes and link previews) is not lost.
### Type, config, and docs updates
- `src/signal/monitor/event-handler.types.ts` and `src/auto-reply/templating.ts`: Extends wire/context types for richer inbound metadata.
- `src/config/types.signal.ts`, `src/config/zod-schema.providers-core.ts`, and `src/signal/monitor.ts`: Adds account-level toggles `injectLinkPreviews` (default `true`) and `preserveTextStyles` (default `true`).
- `docs/channels/signal.md` and `docs/gateway/configuration-reference.md`: Documents new inbound feature behavior and config switches.
### Media note formatting improvements
- `src/auto-reply/media-note.ts`: Adds dimension/caption output in inbound media note formatting.
- `src/auto-reply/media-note.test.ts`: Adds coverage for dimension and caption rendering.
## Design Notes
- Backward compatibility over purity: keep singular media fields alongside new plural fields so existing templates/hooks continue to work.
- Rich metadata goes into `UntrustedContext`: preserves Signal-specific context without polluting primary user text.
- Defaults are opt-out: link previews and text-style preservation default to enabled for better fidelity, but are configurable per account.
- Fault tolerance over fail-fast for media downloads: partial success is better than dropping all attachments.
## Testing
Ran targeted tests for touched behavior:
- `pnpm test -- src/auto-reply/media-note.test.ts src/signal/monitor/event-handler.inbound-contract.test.ts src/signal/monitor/event-handler.mention-gating.test.ts` (Result: `3` files passed, `64` tests passed)
- `pnpm vitest run --config vitest.e2e.config.ts src/signal/monitor.event-handler.sender-prefix.e2e.test.ts` (Result: `1` file passed, `1` test passed)
## AI Disclosure
- [x] AI-assisted
- [x] Fully tested
## Related Issues
- #10637
- #10638
- #13106
- #16545
- #18552
---
Closes #21958
Most Similar PRs
#8271: feat(signal): Add full quoted message context support
by ProofOfReach · 2026-02-03
74.1%
#6591: fix(signal): process all inbound attachments in parallel
by ProofOfReach · 2026-02-01
74.0%
#20732: fix(signal): forward replyTo as quoteTimestamp/quoteAuthor for nati...
by arjunblj · 2026-02-19
71.2%
#15841: Add Silent Ingest for Group Chat Messages
by napetrov · 2026-02-13
70.4%
#15467: feat(messages): add debounceMedia option for inbound debouncing
by tangcruz · 2026-02-13
70.2%
#11990: Fix media understanding file path suppression + image tool bare-ID ...
by robertbergman2 · 2026-02-08
69.8%
#15148: auto-reply: add message_time and compact inbound metadata JSON
by detecti1 · 2026-02-13
69.2%
#4878: fix: string/type handling and API fixes (#4537, #4380, #4373, #4547...
by lailoo · 2026-01-30
69.0%
#9701: feat: handle Signal message edits with [edited] marker (#9656)
by divol89 · 2026-02-05
68.5%
#7459: fix(signal): resolve mention placeholders to actual names
by gerigk-agent · 2026-02-02
68.3%