NanoClaw: a real-world consumer of the JSON-RPC interface #1934
nickpourazima
started this conversation in
Show and tell
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
I've been building a personal AI assistant (NanoClaw) that runs Claude agents in isolated containers and communicates over Signal using signal-cli's JSON-RPC interface. Wanted to share some notes from integrating with the protocol in case they're useful.
What it does
NanoClaw spawns signal-cli in JSON-RPC mode (
signal-cli -a +NUMBER -o json jsonRpc), sends/receives over stdin/stdout, and routes messages to containerized Claude agents that can respond, react, reply with quotes, create polls, and send styled text.Protocol details that might be useful feedback
A few things I ran into during integration:
U+FFFC mention handling — signal-cli encodes @mentions as
{start, length, uuid, number, name}objects and inserts U+FFFC (\uFFFC) placeholder characters in the message body. I replace these with@nametext so the AI assistant's trigger pattern (@Echo) works correctly. The sort-by-descending-position approach avoids index shifting during replacement.Typing indicator 15s expiry — Signal's typing indicators expire after 15 seconds. For long-running AI operations (30s-2min), I resend the typing indicator every 10 seconds to keep it visible.
UTF-16 textStyle positioning — The
textStylefield for bold/italic/strikethrough uses UTF-16 offsets, not JavaScript string indices. Characters outside the BMP (emoji, CJK) take 2 units. I convert to UTF-16 code units before calculating style ranges to get correct positioning.Attachment lookup — When resolving attachments from the signal-cli data directory, attachment IDs can appear with or without file extensions. I match on
filename === id || filename.startsWith(id + '.')to handle both cases.Sync messages — For the assistant to see messages sent from the primary device, handling
syncMessage.sentMessage(not justdataMessage) is important. The destination becomes the chat JID rather than the source.Links
src/channels/signal.tsin the reposignal-cli's JSON-RPC interface has been solid to work with. Happy to answer questions about the integration.
Beta Was this translation helpful? Give feedback.
All reactions