Skip to content

Add iMessage tapback removal to the SDKs and CLI - #155

Merged
alex-w-99 merged 3 commits into
mainfrom
feat/imessage-reaction-removal
Aug 22, 2026
Merged

Add iMessage tapback removal to the SDKs and CLI#155
alex-w-99 merged 3 commits into
mainfrom
feat/imessage-reaction-removal

Conversation

@alex-w-99

@alex-w-99 alex-w-99 commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

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.

Surface Call
Python identity.remove_imessage_reaction(reaction_id)
TypeScript await identity.removeIMessageReaction(reactionId)
Rust identity.remove_imessage_reaction(&reaction_id)?
CLI inkbox imessage unreact <reaction-id> -i <handle>

Each delegates to a new remove_reaction / removeReaction on the iMessage resource.

Two behaviors worth knowing, and the docstrings say both:

  • Only the sender can take a tapback back. A reaction the other party sent is rejected.
  • A tapback Inkbox already shows as gone succeeds without contacting the messaging network — whether you took it back already or a newer tapback replaced it. There is no "already removed" error to handle.
  • A failed removal leaves the tapback in place rather than clearing it locally, so the call can be retried. Note what is not claimed: the retry re-issues the removal, and how the messaging network treats a removal for a tapback it has already dropped is not something the wording promises. An unconfirmed failure can therefore leave a reaction reading as live until a retry succeeds.

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/sdk dependency range, and all four lockfiles. Changelog entries are in CHANGELOG.md and the three per-package logs.

Additive only: no existing signature, argument, or return type changes.

Skills

inkbox-python, inkbox-ts, and inkbox-cli now 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

  • Pythonuv run pytest: 1066 passed, 3 skipped. New cases cover the DELETE path and a UUID argument alongside a string.
  • TypeScriptnpx vitest run: 77 files, 1077 passed, 3 skipped. New case asserts the method, URL, and that no body is sent.
  • Rustcargo test: all pass, including a new mocked 204 removal. cargo fmt --check and cargo clippy --all-targets -- -D warnings clean.
  • CLInpx tsc --noEmit clean after rebuilding the TypeScript SDK, and inkbox imessage unreact --help renders the command. (The CLI resolves types through a symlink to sdk/typescript/dist, so a stale local build reports errors for this branch and for Add agent contact sharing controls #154's contactSharingEnabled until you run npm run build in sdk/typescript.)

To try it against a live identity with iMessage enabled and a contact who has messaged first:

inkbox imessage react <inbound-message-id> -i <handle> --reaction like
inkbox imessage unreact <reaction-id> -i <handle>

The tapback appears and then clears on the recipient's device, and it drops out of the message's reactions on the next read. Running unreact a 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

  • Package READMEs updated. They are what PyPI, npm, and crates.io render, and they still described tapbacks as send-only. All four tapback sections now show removal, and the CLI command list gains inkbox imessage unreact <reaction-id> -i <handle>.
  • Garbled release-note sentence fixed in the root CHANGELOG.md.
  • unreact now has a registration test alongside the existing react one, pinning the reaction-id argument and the required identity flag.
  • Identity-level docstrings carry the sender-only clause, since that is the 422 callers will actually hit and the identity method is the one they use.

Left as-is: inkbox imessage unreact prints { id, removed } while the MCP tool returns { reaction_id, removed }. The CLI is internally consistent (react prints id), so changing it would be the odd one out.

Base automatically changed from feat/agent-contact-sharing-toggle to main August 22, 2026 05:11
alex-w-99 and others added 2 commits August 22, 2026 05:16
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
alex-w-99 force-pushed the feat/imessage-reaction-removal branch from b116b2b to dec3bef Compare August 22, 2026 05:18
@alex-w-99
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 dimavrem22 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@alex-w-99

Copy link
Copy Markdown
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.

@alex-w-99
alex-w-99 merged commit 21e6589 into main Aug 22, 2026
12 checks passed
@alex-w-99
alex-w-99 deleted the feat/imessage-reaction-removal branch August 22, 2026 06:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants