feat(signal): add Signal channel via signal-cli#630
Open
achton wants to merge 4 commits intosipeed:mainfrom
Open
feat(signal): add Signal channel via signal-cli#630achton wants to merge 4 commits intosipeed:mainfrom
achton wants to merge 4 commits intosipeed:mainfrom
Conversation
|
Note: #603 also proposes native Signal support. Both address the same feature request. Suggesting the maintainer coordinate or pick one to avoid duplicated effort. |
7 tasks
47b2569 to
4744c69
Compare
Collaborator
|
@achton hi, could you please resolve conflicts, we just merged channel refactoring issue |
4744c69 to
e682afa
Compare
achton
added a commit
to achton/picoclaw
that referenced
this pull request
Feb 28, 2026
Adapt the Signal channel (PR sipeed#630) to the upstream channel system refactor (sipeed#662, sipeed#877). Fresh implementation on current main rather than rebasing 5 commits across 155 upstream changes. Changes from the original sipeed#630: - Moved from flat pkg/channels/signal.go to pkg/channels/signal/ subpackage - Factory registration via init() + blank import in gateway - New HandleMessage signature with bus.Peer, bus.SenderInfo, identity.BuildCanonicalID - IsAllowedSender() replaces IsAllowed() for structured identity matching - Manager-handled message splitting via WithMaxMessageLength(6000) - Typed errors (ErrNotRunning, ErrTemporary) per Phase 4 lifecycle contract - Proper goroutine tracking with sync.WaitGroup in Start/Stop - TypingCapable interface wraps existing typing indicator feature - ReactionCapable interface (first channel to implement — 👀 on inbound, undo on reply) - WithReasoningChannelID() option for routing LLM reasoning to separate channel All original features preserved: SSE inbound, JSON-RPC sending, markdown-to-Signal text styles, voice transcription, attachment handling, group/DM filtering, typing indicators. 28 tests passing (24 ported + 4 new for parseMessageID).
e682afa to
b492ecd
Compare
Contributor
Author
|
@yinwm I've rebased and reimplemented this feature on top of the channels refactor, ready for review now (again). |
achton
added a commit
to achton/picoclaw
that referenced
this pull request
Feb 28, 2026
Adapt the Signal channel (PR sipeed#630) to the upstream channel system refactor (sipeed#662, sipeed#877). Fresh implementation on current main rather than rebasing 5 commits across 155 upstream changes. Changes from the original sipeed#630: - Moved from flat pkg/channels/signal.go to pkg/channels/signal/ subpackage - Factory registration via init() + blank import in gateway - New HandleMessage signature with bus.Peer, bus.SenderInfo, identity.BuildCanonicalID - IsAllowedSender() replaces IsAllowed() for structured identity matching - Manager-handled message splitting via WithMaxMessageLength(6000) - Typed errors (ErrNotRunning, ErrTemporary) per Phase 4 lifecycle contract - Proper goroutine tracking with sync.WaitGroup in Start/Stop - TypingCapable interface wraps existing typing indicator feature - ReactionCapable interface (first channel to implement — 👀 on inbound, undo on reply) - WithReasoningChannelID() option for routing LLM reasoning to separate channel All original features preserved: SSE inbound, JSON-RPC sending, markdown-to-Signal text styles, voice transcription, attachment handling, group/DM filtering, typing indicators. 28 tests passing (24 ported + 4 new for parseMessageID).
988af27 to
47113b4
Compare
47113b4 to
c308fe6
Compare
achton
added a commit
to achton/picoclaw
that referenced
this pull request
Mar 4, 2026
Adapt the Signal channel (PR sipeed#630) to the upstream channel system refactor (sipeed#662, sipeed#877). Fresh implementation on current main rather than rebasing 5 commits across 155 upstream changes. Changes from the original sipeed#630: - Moved from flat pkg/channels/signal.go to pkg/channels/signal/ subpackage - Factory registration via init() + blank import in gateway - New HandleMessage signature with bus.Peer, bus.SenderInfo, identity.BuildCanonicalID - IsAllowedSender() replaces IsAllowed() for structured identity matching - Manager-handled message splitting via WithMaxMessageLength(6000) - Typed errors (ErrNotRunning, ErrTemporary) per Phase 4 lifecycle contract - Proper goroutine tracking with sync.WaitGroup in Start/Stop - TypingCapable interface wraps existing typing indicator feature - ReactionCapable interface (first channel to implement — 👀 on inbound, undo on reply) - WithReasoningChannelID() option for routing LLM reasoning to separate channel All original features preserved: SSE inbound, JSON-RPC sending, markdown-to-Signal text styles, voice transcription, attachment handling, group/DM filtering, typing indicators. 28 tests passing (24 ported + 4 new for parseMessageID).
Contributor
Author
|
I have rebased on main in order to resolve git conflicts in |
Adapt the Signal channel (PR sipeed#630) to the upstream channel system refactor (sipeed#662, sipeed#877). Fresh implementation on current main rather than rebasing 5 commits across 155 upstream changes. Changes from the original sipeed#630: - Moved from flat pkg/channels/signal.go to pkg/channels/signal/ subpackage - Factory registration via init() + blank import in gateway - New HandleMessage signature with bus.Peer, bus.SenderInfo, identity.BuildCanonicalID - IsAllowedSender() replaces IsAllowed() for structured identity matching - Manager-handled message splitting via WithMaxMessageLength(6000) - Typed errors (ErrNotRunning, ErrTemporary) per Phase 4 lifecycle contract - Proper goroutine tracking with sync.WaitGroup in Start/Stop - TypingCapable interface wraps existing typing indicator feature - ReactionCapable interface (first channel to implement — 👀 on inbound, undo on reply) - WithReasoningChannelID() option for routing LLM reasoning to separate channel All original features preserved: SSE inbound, JSON-RPC sending, markdown-to-Signal text styles, voice transcription, attachment handling, group/DM filtering, typing indicators. 28 tests passing (24 ported + 4 new for parseMessageID).
- Replace DMsEnabled/GroupsEnabled with GroupTrigger (mention_only, prefixes) - Add @mention detection for group chats (isBotMentioned, stripMention) - Fix sendReaction/sendTyping recipient type (string → []string) - Fix Send() error wrapping to preserve root cause - Add io.LimitReader guard on SSE error body read - Remove dead voice transcription code (deferred) - Remove compound senderID (redundant with SenderInfo) - Add isGroupChat safety comment - Update README config docs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The SSE client (no timeout, long-lived stream) was recreated on every reconnect, wasting connection pool resources. Hoist it to the struct alongside the RPC client (30s timeout) so connections can be reused. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
c308fe6 to
af25536
Compare
Contributor
Author
|
I have rebased on main again in order to resolve git conflicts. |
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.
📝 Description
Adds Signal as a messaging channel, implementing the signal-cli approach proposed in #41 (Option A). Rebased onto the channel refactor (#621 / #877).
PicoClaw connects to a signal-cli daemon running in HTTP mode. Inbound messages arrive via SSE (
/api/v1/events), outbound messages are sent via JSON-RPC (/api/v1/rpc). No CGO or additional compiled dependencies are needed in PicoClaw itself.Question for maintainers
signal-cli is a required external dependency (Java-based, ~50-100MB RAM). It runs as a separate daemon process — PicoClaw communicates with it over HTTP, so there's no compile-time coupling. Is this acceptable, or would you prefer a different approach? The alternative (native libsignal via CGO) adds significant build complexity and AGPL licensing concerns (see PR #603 for that approach).
Known upstream issue
signal-cli v0.13.24 has a bug (AsamK/signal-cli#1940) where the
mentionsarray in JSON output is empty due to a binary ACI parsing issue. The fix is in AsamK/signal-cli#1944 but not yet released. Our mention detection code is correct and will work once the fix ships — until then,group_trigger.mention_onlymode won't respond in groups (DMs work fine).Key design points
pkg/channels/signal/, factory registration viainit(),BaseChannelembedding with functional optionsTypingCapable(8s refresh loop),ReactionCapable(emoji reaction on inbound),MessageLengthProvider(6000 rune limit)bus.SenderInfowith canonicalsignal:+phoneformat via the newpkg/identitypackageGroupTriggerConfig(mention_only,prefixes) with @mention detection via structured mention data from signal-cli**bold**,*italic*,`code`,```blocks```,~~strike~~to signal-clitextStyleranges with correct UTF-16 code unit positionsErrTemporary/ErrNotRunningsentinels for Manager retry logicisGroupChat()heuristic, andparseMessageID()🗣️ Type of Change
🤖 AI Code Generation
🔗 Related Issue
Closes #41
📚 Technical Context (Skip for Docs)
🧪 Test Environment
📸 Evidence (Optional)
Click to view Logs/Screenshots
☑️ Checklist