Commit Graph

12 Commits

Author SHA1 Message Date
Kenneth Lien
22bd61d01f imessage: bump to 0.1.0 2026-03-26 23:43:51 -07:00
Kenneth Lien
60c3fc36ed imessage: drop SMS/RCS by default, opt-in via IMESSAGE_ALLOW_SMS
SMS sender IDs are spoofable; iMessage is Apple-ID-authenticated and
end-to-end encrypted. The plugin previously treated both identically,
so a forged SMS from the owner's own number would match SELF, bypass
the access gate, and inherit owner-level trust — including permission
approval.

handleInbound now drops anything with service != 'iMessage' unless
IMESSAGE_ALLOW_SMS=true. Default is the safe path; users who want SMS
can opt in after reading the warning in README.
2026-03-26 23:41:39 -07:00
Kenneth Lien
c4274521de imessage: trim comment cruft 2026-03-26 23:16:27 -07:00
Kenneth Lien
8dfc279258 imessage: harden echo filter normalization
The self-chat echo filter matches outbound text against what chat.db
stores on round-trip. Three divergence sources caused false negatives
and duplicate bubbles:

- Signature suffix: "\nSent by Claude" is appended on send, but the
  \n may not round-trip identically through attributedBody
- Emoji variation selectors (U+FE00-FE0F) and ZWJ (U+200D): chat.db
  can add or drop these on emoji characters
- Smart quotes: macOS auto-substitutes straight quotes on the way in

Strip/normalize all three in echoKey() before the existing whitespace
collapse.

Fixes #1024
2026-03-26 23:12:19 -07:00
Kenneth Lien
c29338f276 imessage: drop whitespace-only messages from tapbacks/receipts
Tapback reactions and read receipts synced from linked devices arrive
as chat.db rows with whitespace-only text. The existing empty-check
used falsy comparison which doesn't catch ' ' or invisible chars,
causing unsolicited replies to reaction taps.

Fixes #1041
2026-03-26 23:11:49 -07:00
Kenneth Lien
03a685d5f6 imessage: restrict permission relay to self-chat only
Permission prompts were being broadcast to all allowlisted contacts plus
every DM resolvable from the SELF address set. Two compounding bugs:

1. SELF was polluted by chat.last_addressed_handle, which on machines
   with SMS history returns short codes, business handles, and other
   contacts' numbers — not just the owner's addresses. One reporter's
   query returned 50 addresses (2 actually theirs) resolving to 148 DM
   chats, all of which received permission prompts.

2. Even with a clean SELF, the handler sent to allowFrom + SELF, so
   every allowlisted contact received the prompt and could reply to
   approve tool execution on the owner's machine.

Fix:
- Build SELF from message.account WHERE is_from_me=1 only
- Send permission prompts to self-chat only, not allowFrom
- Accept permission replies from self-chat only

Fixes #1048
Fixes #1010
2026-03-26 23:11:29 -07:00
Kenneth Lien
12e9c01d5f Regenerate imessage bun.lock without artifactory URLs
The lockfile had 94 artifactory.infra.ant.dev URLs baked in from
generation behind a private registry. External users hit 401s on
'bun install' and the server never starts. Regenerated against
registry.npmjs.org to match the .npmrc.
2026-03-23 23:45:03 -07:00
Kenneth Lien
d49d339d1e Show input_preview only for Bash in permission prompts
Write/Edit previews are unbearably long over iMessage. Bash is the
dangerous one where seeing the command matters; everything else gets
tool_name + description only.
2026-03-23 23:05:00 -07:00
Kenneth Lien
9693fd75c3 Document IMESSAGE_STATE_DIR in README 2026-03-23 20:12:20 -07:00
Kenneth Lien
bfed4635f5 feat(imessage): port permission-relay + lifecycle fixes from telegram
Brings the imessage channel to parity with recent telegram/discord
hardening:

- Permission-relay capability: declare claude/channel/permission,
  handle inbound permission_request notifications by fanning out to
  allowlisted DM chats + self-chat, intercept "yes/no <id>" replies
  after the gate check and emit structured permission events instead
  of relaying as chat. Groups excluded per single-user-mode policy.
- Global unhandledRejection/uncaughtException handlers so the server
  logs instead of dying silently.
- IMESSAGE_STATE_DIR env override for the state directory.
- .unref() on both setInterval timers so they don't block shutdown.
- stdin EOF / SIGTERM / SIGINT shutdown handler that closes chat.db
  and exits cleanly instead of leaving a zombie poll loop.

Adds zod as a direct dep (already transitively present via the MCP SDK)
for the notification handler schema.
2026-03-23 20:10:34 -07:00
Kenneth Lien
6d0053f69e Add IMESSAGE_APPEND_SIGNATURE env var (default true) 2026-03-20 14:51:47 -07:00
Kenneth Lien
1c95fc662b Add imessage channel plugin
iMessage bridge for Claude Code. Reads ~/Library/Messages/chat.db
directly for history and new-message polling; sends via AppleScript
to Messages.app. macOS only.

Built-in access control: inbound messages are gated by an allowlist
(default: self-chat only), outbound sends are scoped to the same
allowlist. The /imessage:access skill manages allowlists and policy.

Requires Full Disk Access and Automation TCC grants — both prompted
by macOS on first use.

Ships full source — server.ts runs locally via bun, started by the
.mcp.json command.
2026-03-18 16:23:29 -07:00