Skip to content

feat(signal): add author-aware native reply quotes - #67595

Open
poisdahl wants to merge 15 commits into
NousResearch:mainfrom
poisdahl:agent/signal-native-quotes
Open

poisdahl wants to merge 15 commits into
NousResearch:mainfrom
poisdahl:agent/signal-native-quotes

Conversation

@poisdahl

@poisdahl poisdahl commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Add native Signal reply quotes end to end using the displayed-message timestamp and actual sender identity.

This supersedes #30453. Its core idea and original commit are preserved with @xyanglu's authorship, while the implementation is rebuilt for current main and fixes the group-chat author bug identified in review. Addresses the outbound-quote slice of #39043.

Why

signal-cli requires quoteAuthor whenever quoteTimestamp is present. A Signal group destination is not the author of the quoted message, so using chat_id can fail the send RPC. Edited envelopes also have a fresh event timestamp and a distinct editMessage.targetSentTimestamp for the displayed/original message.

Changes

  • preserve normal inbound data-message timestamp as the event/dedupe identity, with envelope fallback
  • keep edited events distinct while retaining targetSentTimestamp as the native quote/reaction anchor
  • prefer Signal ACI / sourceUuid as quote author, with sourceNumber fallback
  • emit quoteTimestamp and quoteAuthor atomically, or safely fall back to an unquoted send if either value is invalid
  • preserve the latest native anchor through debounce, separate busy bursts, and /steer queue fallbacks
  • cover text, images, documents, voice/video, and multi-image sends
  • quote only the first chunk when a long text reply is split into multiple Signal messages
  • correct the Signal guide's obsolete version-fallback claim and retain contributor attribution

Validation

Refreshed onto main@fef0e16fe on 2026-09-08; exact head 25d09c66de. Native quote metadata and upstream's durable route metadata are both preserved. MessageEvent imports follow the current owner module.

  • scripts/run_tests.sh tests/gateway/test_signal.py tests/gateway/test_queue_command.py87 passed, 0 failed
  • scripts/run_tests.sh tests/gateway/test_kanban_routed_transport.py tests/gateway/test_kanban_route_anchor_capture.py6 passed, 0 failed
  • Ruff on changed Python files and git diff --check against current main: clean
  • Fresh GitHub CI, Docs, Nix, Docker amd64/arm64, macOS, and Windows checks all succeeded

Scope and related work

This PR does not implement outbound edits, remote delete, read receipts, or arbitrary agent/MCP reply operations.

Merge sequencing with #67495

Whichever of #67495 and this PR lands second must be rebased and revalidated on the merged Signal envelope path.

The combined implementation must preserve separate timestamp meanings for edits:

  • read receipts target the fresh nested data-message/event timestamp
  • quote and reaction anchoring target editMessage.targetSentTimestamp
  • the merged raw metadata must retain feat(signal): add policy-aware read receipts #67495's sender_uuid and is_note_to_self fields as well as this PR's native anchor

Do not collapse receipt identity and displayed-message anchoring into one raw timestamp field.

xyanglu and others added 2 commits July 19, 2026 18:34
Two changes:
1. Set message_id on MessageEvent from envelope timestamp so
   _reply_anchor_for_event returns a valid value for Signal.
2. Pass quoteTimestamp to signal-cli send RPC when reply_to
   is a numeric timestamp, enabling native Signal quote bubbles
   on outbound replies.
@teknium1

Copy link
Copy Markdown
Collaborator

Thanks for rebuilding the quote path around the current Signal adapter. The premise is confirmed on current main: gateway/platforms/signal.py:749 constructs inbound events without message_id, gateway/platforms/base.py:115 therefore has no Signal reply anchor, and gateway/platforms/signal.py:1055-1095 does not translate reply_to into Signal RPC quote fields.

The proposed flow is appropriately scoped: it preserves the sender identity already retained as user_id_alt / user_id (gateway/platforms/signal.py:709-718), emits timestamp and author atomically, and follows existing metadata propagation across text and attachment delivery paths (gateway/platforms/base.py:5097-5103, 5128-5133, 5185-5224). No blocking implementation issue was identified from the diff.

Automated hermes-sweeper review.

@alt-glitch alt-glitch added type/feature New feature or request comp/gateway Gateway runner, session dispatch, delivery platform/signal Signal CLI adapter P3 Low — cosmetic, nice to have sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages labels Jul 19, 2026
@alt-glitch

Copy link
Copy Markdown
Contributor

This was generated by AI during triage.

Related to #30453 and the #39043 Signal capability umbrella. This is a current-main salvage that adds the group-safe quoteAuthor mechanism, so it is not marked duplicate.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 19, 2026
@poisdahl
poisdahl marked this pull request as ready for review July 27, 2026 08:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery P3 Low — cosmetic, nice to have platform/signal Signal CLI adapter sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants