Add iMessage tapback removal to the SDKs and CLI - #155
Merged
Conversation
Agents could send a tapback but never take one back. Adds remove_imessage_reaction / removeIMessageReaction across Python, TypeScript, and Rust, plus `inkbox imessage unreact`, addressed by the reaction id the send result and message reads already carry. Only the sender can take a tapback back, and removing one that is already gone succeeds without doing anything, so a retry after an error is safe. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017jpSvaTc7twcRv3dkHf4z9
The docstrings said removing a tapback that is already gone succeeds and does nothing, and that a retry is always safe. What we can state is that Inkbox short-circuits when its own record already shows the tapback gone, and that a failed removal leaves it in place so the call can be retried. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017jpSvaTc7twcRv3dkHf4z9
alex-w-99
force-pushed
the
feat/imessage-reaction-removal
branch
from
August 22, 2026 05:18
b116b2b to
dec3bef
Compare
alex-w-99
marked this pull request as ready for review
August 22, 2026 05:24
The skills covered removal but the package READMEs, which are what PyPI, npm, and crates.io show, still described tapbacks as send-only. Adds the CLI command to its command list, restores a garbled release-note sentence, carries the sender-only clause into the identity-level docstrings, and pins that unreact is registered with a required identity flag. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017jpSvaTc7twcRv3dkHf4z9
dimavrem22
approved these changes
Aug 22, 2026
dimavrem22
left a comment
Contributor
There was a problem hiding this comment.
Approving — READMEs, changelog wording, unreact registration test, and the sender-only clause in the identity-level docstrings are all in. Publish 0.6.2 only once the removal endpoint is live in production.
Contributor
Author
|
Thanks. Gate acknowledged: 0.6.2 goes out only after the removal endpoint is live in production — the READMEs, skills, and plugin manifest all describe behaviour that 404s anywhere it isn't. |
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
Agents could send an iMessage tapback but never take one back. This adds removal to every surface, addressed by the reaction id that the send result and message reads already carry.
identity.remove_imessage_reaction(reaction_id)await identity.removeIMessageReaction(reactionId)identity.remove_imessage_reaction(&reaction_id)?inkbox imessage unreact <reaction-id> -i <handle>Each delegates to a new
remove_reaction/removeReactionon the iMessage resource.Two behaviors worth knowing, and the docstrings say both:
Version
0.6.1 → 0.6.2, in lockstep across
sdk/python/pyproject.toml,sdk/typescript/package.json,sdk/rust/Cargo.toml,cli/package.json, and.claude-plugin/plugin.json— plus the two User-Agent constants (sdk/typescript/src/version.ts,cli/src/client.ts), the CLI's@inkbox/sdkdependency range, and all four lockfiles. Changelog entries are inCHANGELOG.mdand the three per-package logs.Additive only: no existing signature, argument, or return type changes.
Skills
inkbox-python,inkbox-ts, andinkbox-clinow show the removal alongside the send in their tapback sections, including the retry-safety note. The Python and TypeScript samples bind the send result so the id has an obvious source.Testing
uv run pytest: 1066 passed, 3 skipped. New cases cover theDELETEpath and aUUIDargument alongside a string.npx vitest run: 77 files, 1077 passed, 3 skipped. New case asserts the method, URL, and that no body is sent.cargo test: all pass, including a new mocked 204 removal.cargo fmt --checkandcargo clippy --all-targets -- -D warningsclean.npx tsc --noEmitclean after rebuilding the TypeScript SDK, andinkbox imessage unreact --helprenders the command. (The CLI resolves types through a symlink tosdk/typescript/dist, so a stale local build reports errors for this branch and for Add agent contact sharing controls #154'scontactSharingEnableduntil you runnpm run buildinsdk/typescript.)To try it against a live identity with iMessage enabled and a contact who has messaged first:
The tapback appears and then clears on the recipient's device, and it drops out of the message's
reactionson the next read. Runningunreacta second time returns success without sending anything, since the reaction already reads as removed.Requires
The removal endpoint must be live in the target environment. Against an older deployment these calls return 404.
Review follow-ups
inkbox imessage unreact <reaction-id> -i <handle>.CHANGELOG.md.unreactnow has a registration test alongside the existingreactone, pinning thereaction-idargument and the required identity flag.Left as-is:
inkbox imessage unreactprints{ id, removed }while the MCP tool returns{ reaction_id, removed }. The CLI is internally consistent (reactprintsid), so changing it would be the odd one out.