Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

fix(signal): deliver agent reactions and forward inbound reactions#2744

Open
WormyOne wants to merge 1 commit into
nanocoai:channels from
WormyOne:fix/signal-reactions
Open

fix(signal): deliver agent reactions and forward inbound reactions #2744
WormyOne wants to merge 1 commit into
nanocoai:channels from
WormyOne:fix/signal-reactions

Conversation

@WormyOne

@WormyOne WormyOne commented Jun 11, 2026

Copy link
Copy Markdown

Summary

The Signal adapter silently drops the agent's add_reaction tool output, and ignores inbound reaction envelopes. Every agent on a Signal channel is offered add_reaction (core MCP tools) and believes the reaction was queued — but deliver() has no operation: 'reaction' handling, so the row resolves as a no-op (the host then logs "Message delivered"). Inbound reactions die at the empty-text gate in handleEnvelope. This PR implements both directions, following the same content.operation === 'reaction' contract the WhatsApp adapter uses.

What's in the change

Outbound (deliver() → signal-cli sendReaction):

  • Message ids reach the adapter namespaced as "<timestamp>:<agent-group-id>" (router messageIdForAgent); the adapter strips back to the timestamp signal-cli targets by.
  • The tool contract is shortcode names (thumbs_up, per core.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.
  • Group reactions need (targetAuthor, targetTimestamp): authors are remembered from inbound messages in a bounded FIFO cache (500 entries). DMs always target the peer.

Inbound (handleEnvelope):

  • dataMessage.reaction envelopes are forwarded as [<name> reacted <emoji> to your message] chat events — mirroring the existing [Voice: ...] text convention, since InboundMessage.kind has 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-existing tsc errors on this branch (resolveChannelName in 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

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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@gavrielc gavrielc Awaiting requested review from gavrielc gavrielc is a code owner

@gabi-simons gabi-simons Awaiting requested review from gabi-simons gabi-simons is a code owner

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

1 participant

AltStyle によって変換されたページ (->オリジナル) /