-
Notifications
You must be signed in to change notification settings - Fork 12.9k
fix(signal): deliver agent reactions and forward inbound reactions#2744
Open
WormyOne wants to merge 1 commit into
Open
fix(signal): deliver agent reactions and forward inbound reactions #2744WormyOne wants to merge 1 commit into
WormyOne wants to merge 1 commit into
Conversation
The Signal adapter silently dropped the add_reaction tool's output:
deliver() had no handling for `operation: 'reaction'` content, so the
host marked reaction rows delivered while nothing reached the daemon.
Inbound reaction envelopes were likewise dropped by the empty-text gate.
- deliver(): route `{operation: 'reaction', messageId, emoji}` to
signal-cli's sendReaction. Message ids arrive namespaced as
"<timestamp>:<agent-group-id>" (router messageIdForAgent); strip back
to the timestamp signal-cli targets by.
- Shortcode -> unicode map: the tool contract is shortcode names
(e.g. thumbs_up) but signal-cli requires actual emoji. Raw unicode
passes through; unknown shortcodes are dropped with a warn.
- Group targeting: sendReaction needs (targetAuthor, targetTimestamp).
Authors are remembered from inbound messages in a bounded FIFO cache;
DMs always target the peer.
- Inbound reactions: forwarded as "[<name> reacted <emoji> to your
message]" chat events, mirroring the voice-note text convention (the
host has no dedicated reaction kind). Reaction removals are ignored.
- 6 new tests covering all of the above; full signal suite passes
(43/43). Pre-existing typecheck errors in slack.ts/telegram.ts
(resolveChannelName) are untouched.
Verified end-to-end against a live deployment: signal-cli 0.14.x daemon
in multi-account TCP mode, reactions confirmed delivered on-device in
both directions.
Implemented by Claude (Fable 5) via Claude Code, directed by
Alexis Mather.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@WormyOne
WormyOne
requested review from
gabi-simons and
gavrielc
as code owners
June 11, 2026 23:34
This was referenced Jun 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The Signal adapter silently drops the agent's
add_reactiontool output, and ignores inbound reaction envelopes. Every agent on a Signal channel is offeredadd_reaction(core MCP tools) and believes the reaction was queued — butdeliver()has nooperation: 'reaction'handling, so the row resolves as a no-op (the host then logs "Message delivered"). Inbound reactions die at the empty-text gate inhandleEnvelope. This PR implements both directions, following the samecontent.operation === 'reaction'contract the WhatsApp adapter uses.What's in the change
Outbound (
deliver()→ signal-clisendReaction):"<timestamp>:<agent-group-id>"(routermessageIdForAgent); the adapter strips back to the timestamp signal-cli targets by.thumbs_up, percore.instructions.md) but signal-cli requires actual unicode — added a shortcode→emoji map (~100 common names); raw unicode passes through; unknown shortcodes drop with a warn instead of sending garbage.(targetAuthor, targetTimestamp): authors are remembered from inbound messages in a bounded FIFO cache (500 entries). DMs always target the peer.Inbound (
handleEnvelope):dataMessage.reactionenvelopes are forwarded as[<name> reacted <emoji> to your message]chat events — mirroring the existing[Voice: ...]text convention, sinceInboundMessage.kindhas no reaction variant. Reaction removals are ignored.Tests
6 new tests in
signal.test.ts(outbound DM + shortcode resolution, unicode passthrough, unknown-shortcode drop, group targeting via remembered author, inbound forwarding, removal ignore). Full signal suite: 43/43 pass. The pre-existingtscerrors on this branch (resolveChannelNamein slack.ts/telegram.ts) are untouched.Verification
Running in production on a live deployment (signal-cli 0.14.x daemon, multi-account TCP mode, external daemon via
SIGNAL_MANAGE_DAEMON=false): reactions confirmed delivered on-device in both directions, including the namespaced-id and group-author paths.Attribution
This work was implemented by Claude (Fable 5) running in Claude Code, directed and reviewed by Alexis Mather (@WormyOne), who operates the deployment it was verified on.
🤖 Generated with Claude Code