iMessage module: fix sending on modern macOS, see images, survive message-to-self and iCloud re-sync - #1835
Open
J0UH wants to merge 2 commits into
Open
iMessage module: fix sending on modern macOS, see images, survive message-to-self and iCloud re-sync#1835J0UH wants to merge 2 commits into
J0UH wants to merge 2 commits into
Conversation
…work on modern macOS participant targetService handle "..." is not a valid object specifier and makes osascript exit 1, so every reply silently failed while the SDK sessions still ran. The working form is participant "..." of targetService. Verified on macOS 26.5.
…d re-sync guard Four fixes from running the module as a daily channel (texting my own number, macOS 26.5): - Attachments: read the attachment table, stage copies into the module state dir (the claude subprocess has no Full Disk Access — only the bun process does), convert HEIC to JPEG via sips, and hand the staged paths to the session for the Read tool. Attachment-only messages were previously dropped by the text IS NOT NULL filter. - Message-to-self: replies now carry a zero-width-space prefix and the poller ignores anything starting with one, otherwise the bot answers its own echo forever (mine got 255 turns in). iCloud also fails to reliably generate the received-echo copy of your own sent messages, so is_from_me=1 rows are now accepted, but only in a chat whose identifier is allowlisted, with a dedup set for late echo copies. - Freshness gate: iCloud can re-insert old messages under new ROWIDs with their original timestamps. The ROWID cursor then treats the entire history as new — mine answered ~240 day-old messages one morning. Skip anything older than 15 minutes regardless of ROWID. - Don't drop messages that arrive mid-session: poll() returns early while processing instead of advancing the cursor past them, so they process in order on the next tick. The old path replied 'Still processing' and then lost the message.
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.
I set LifeOS up on my Mac this week and made the iMessage module my main channel, with a twist: I text my own number, so the DA lives in my "messages to yourself" thread. That surfaced a string of bugs, a couple of which will bite any iMessage user, not just the self-chat case. Everything here is running on my machine now (macOS 26.5, LifeOS 7.28.3).
Sending was broken outright.
participant targetService handle "..."is not a valid AppleScript object specifier, at least on current macOS. osascript exits 1, every reply silently fails, and the SDK sessions still run and cost money. The working form isparticipant "..." of targetService. That's the first commit, and it's independent of everything else if you'd rather cherry-pick it.The second commit is the rest:
Images. The module only read chat.db's
textcolumn, so photos were invisible, and attachment-only messages never even reached the bot because of thetext IS NOT NULLfilter. Attachments now get staged into the module's state dir before the session starts. The copy step matters: only the bun process holds Full Disk Access, the claude subprocess it spawns does not, so it can't read ~/Library/Messages itself. HEIC converts to JPEG with sips, and the staged paths go into the prompt for the Read tool. Tested with photos and screenshots, the DA describes them fine.Message-to-self. If the allowlisted handle is your own Apple ID, every reply the bot sends comes back as an is_from_me=0 row, and the poller treats it as a new user message. Mine answered itself 255 times before I pulled the plug. Replies now carry a zero-width-space prefix and the poller skips anything that starts with one. There's a second problem in the same setup: iCloud does not reliably generate that received-echo copy of your own sent messages. Mine stopped generating them mid-day and the bot just went deaf. The poller now also accepts is_from_me=1 rows, but only inside a chat whose identifier is on the allowlist, with a dedup set for when an echo copy shows up late. Side benefit: you can talk to the DA straight from Messages.app on the Mac.
iCloud re-sync. This one cost me a morning. Overnight, iCloud re-inserted my entire previous day of messages under brand-new ROWIDs with their original timestamps. The cursor is ROWID-based, so at 8:46am the bot saw ~240 "new" messages and answered every single one, including naming each emoji I'd sent the day before. In Danish. The fix is a freshness gate: anything whose own timestamp is older than 15 minutes gets skipped no matter what ROWID it arrives on. ROWID order just isn't arrival order after a re-sync or a restore, so this protects every user of the module.
Dropped messages. Anything sent while a session was in flight got "Still processing your previous message. Please wait." and was then lost forever, because the cursor had already moved past it. poll() now returns early while processing, so pending messages stay in the DB and get handled in order on the next tick.
Happy to split any of this out or adjust the freshness window if you want it configurable.