cmux-imessage: send to a chat through Messages.app (opt-in mirror, library step)

Merged
TE
3d ago
+297-0

Leo's ask: "i mean like obviously we already are in an imessage type zone im saying like actual imessage chats with contacts/group chats" ... "hm. could we mirror it?"

This is the sending half of the opt-in Messages mirror, at the library level. It adds cmux_imessage::send to the crate from #32891. Nothing calls it yet: the daemon op and the composer come in a follow-up, behind the same off-by-default imessage.enabled setting, and every send needs a user gesture.

What it does

  • send(&SendRequest { chat_guid, text }, &SystemRunner) runs osascript with a fixed script: tell application "Messages" to send messageText to chat id chatGuid.
  • The chat guid and the text reach the script only as argv items and are never spliced into its source, so a message can't change what the script does. A test sends a hostile string and checks that it arrives verbatim.
  • Validation: refuses empty text, text over 20,000 bytes and NUL bytes. A chat guid must be service;-;id or service;+;id, with no control characters and no leading -, so osascript never reads it as an option.
  • Errors map to stable codes:
    • AppleScript -1743 (Automation of Messages not allowed) becomes not_permitted.
    • -1728 (no such chat) becomes invalid_chat.
    • No osascript becomes unsupported.
    • A send that runs past 2 minutes becomes timed_out.
    • Anything else becomes failed.
  • Stop condition and cleanup: the child is waited on, and on timeout it is killed and reaped. Nothing is left running.

Nothing leaves the Mac except the message Leo sends through his own Messages.app.

Tests

Two commits: the failing tests against stubs (5 of 5 red), then the implementation (5 of 5 green, the reader's 10 still green, clippy -D warnings clean on Linux CI). They use a fake runner, so no Messages.app or osascript is needed.

Agent session: agent session

🤖 Generated with Claude Code


Autofix with [code]smith
Open in [code]smith · Need help on this PR? Tag @codesmith-bot with what you need.


Summary by cubic

Adds cmux_imessage::send, the sending half of the opt-in Messages mirror at the library level. Nothing calls it yet; the daemon op and composer follow in a later PR, behind the same off-by-default imessage.enabled setting, and every send needs a user gesture.

Security and validation

  • The chat guid and text reach a fixed AppleScript only as argv items, never spliced into its source, so a hostile message can't change what the script does.
  • Refuses empty text, text over 20,000 bytes, and NUL bytes; chat guids must match service;-;id or service;+;id with no control characters and no leading -.
  • AppleScript -1743 (Automation not allowed) maps to not_permitted, -1728 (no such chat) to invalid_chat, missing osascript to unsupported, a two-minute overrun to timed_out, and anything else to failed.

Tests

  • Tests use a fake runner, so no Messages.app or osascript is needed, and cover the hostile-string case, validation, one osascript call per send, and error-code mapping.