# iMessage (`IMessageSync`)

## Purpose

Sync iMessage / SMS conversations and send messages from the web back to
contacts via Messages.app.

## Direction

Bidirectional. Inbound: read `chat.db` -> push to server. Outbound: poll the
legacy `icloud.send_queue` table -> AppleScript-driven send via Messages.app.

## OS surface

`~/Library/Messages/chat.db` (SQLite, accessed via GRDB) plus Messages.app via
AppleScript for outbound. Requires **Full Disk Access**; module surfaces
`.fullDiskAccessRequired` when missing
(`@packages/imessage/Sources/IMessageSync/SyncManager.swift:36, 132-133`).
Contacts are loaded via Contacts.framework
(`@packages/imessage/Sources/IMessageSync/Reader.swift:77`).

## Files

- `Reader.swift` (`@packages/imessage/Sources/IMessageSync/Reader.swift`):
  `class iMessageReader` (line 64) — `connect()`, `getConversations()`,
  `getMessages(conversationId:since:)`, `loadContacts()`. `getMessages` now
  invokes `extractTextFromAttributedBody(_:)` (line 371): when chat.db's
  `text` column is null/empty and `attributedBody` is non-nil, the byte-scan
  heuristic recovers plain text and substitutes it before sending. Limits of
  the heuristic — single text run only, no rich formatting — are documented
  in [known-limitations](../known-limitations.md#imessage-attributedbody-decoder-is-heuristic-not-a-full-typedstream-parser).
- `SyncManager.swift` — `final class SyncManager: BaseSyncManager<SyncStats, SyncError>`
  (line 44); 30s timer (line 93). Owns `sendQueueClient: SendQueueClient<IMessageSendTransport>`
  (lines 70-86), `didStartSync` / `willStopSync` (lines 170-176). Handles
  schema-version migrations via `Self.syncSchemaVersion` (line 47) and chunked
  payload retries (lines 331-354).
- `APIClient.swift` — `syncMessages`, `syncContacts`, `getStats`,
  `getPendingSends`, `reportSendResult`, `resetSync`
  (`@packages/imessage/Sources/IMessageSync/APIClient.swift:14-108`).
- `Sender.swift` — AppleScript driver `SendService`; uses
  `AppleScriptEscape` (`@packages/shared/Sources/MacSyncShared/Util/AppleScriptEscape.swift:13`)
  for all string interpolation.
- `SendQueueAdapter.swift` — `IMessageSendTransport` adapting the bespoke
  legacy send-queue endpoints into the generic `SendQueueTransport` protocol.
- `ChunkingStrategy.swift` — adaptive batch sizing for large message + attachment payloads.

## Timing

- Read interval: **30s**
  (`@packages/imessage/Sources/IMessageSync/SyncManager.swift:93`).
- Outbound poll interval: **30s**
  (`@packages/imessage/Sources/IMessageSync/SyncManager.swift:74`).
- Inbound batch size: adaptive via `ChunkingStrategy.createAdaptiveChunks` (line 202).

## Server surface

- Entity tables: `icloud.contacts`, `icloud.conversations`, `icloud.messages`,
  `icloud.send_queue` (legacy)
  (`src/server/src/app/server.ts:38-49`).
- Client routes (`src/server/src/surfaces/client/imessage.ts`):
  - `POST /client/imessage/sync` (line 71)
  - `POST /client/imessage/contacts` (line 77)
  - `GET /client/imessage/stats` (line 83)
  - `GET /client/imessage/send-queue/pending` (line 88)
  - `POST /client/imessage/send-queue/:id/result` (line 126)
  - `POST /client/imessage/reset` (line 172)
- Web routes (`src/server/src/surfaces/my/messages.ts`):
  - `GET /my/messages/conversations` (line 11)
  - `GET /my/messages/conversations/:id/messages` (line 15)
  - `GET /my/messages/last-sync` (line 21)
- Admin enqueue: `POST /admin/send-queue/enqueue`
  (`src/server/src/surfaces/admin/send-queue.ts:15`).

## Web surface

- Tab: `/messages` (`web/src/App.tsx:57`).
- API helpers: `web/src/api/messages.ts`.

## Known limitations

- `attributedBody` is forwarded raw; macOS 13+ messages with no plaintext
  appear blank in the dashboard. See
  [known-limitations](../known-limitations.md#imessage-attributedbody-is-not-parsed).
- AppleScript send can stall if Messages.app is unresponsive; manual restart
  required.
- The send queue is on a bespoke schema (extra columns vs the generic
  `<module>_send_queue`); admin tooling must special-case it.

## Tests (`@packages/imessage/Tests/IMessageSyncTests/`)

- `ChunkingTests.swift` — payload sizing/splitting (hermetic).
- `SendValidationTests.swift` — recipient/phone format (hermetic).
- `SyncPayloadTests.swift` — Codable round-trips (hermetic).

Not covered by tests: chat.db reading, AppleScript sending, contacts
permission (all require macOS Privacy permissions).
