Vercel AI SDK: iMessage Adapter Skill
This skill provides a complete reference for using chat-adapter-imessage, the official Photon adapter for connecting the Vercel AI SDK (Chat SDK) to iMessage.
Overview
The adapter acts as a bridge, allowing you to build AI agents and assistants with the Vercel AI SDK that communicate with users over iMessage. It leverages @photon-ai/imessage-kit (self-hosted) and @photon-ai/advanced-imessage-kit (production infrastructure by Photon) under the hood.
Key Features
- Two Modes: Run in local mode on any Mac for development, or remote mode connected to Photon's production iMessage infrastructure.
- Unified Interface: Provides a consistent API for sending and receiving messages, regardless of the underlying mode.
- Vercel AI SDK Integration: Implements the
Adapterinterface from thechatpackage, making it a drop-in solution. - Markdown Conversion: Automatically converts markdown from the AI SDK into iMessage-compatible plain text.
Setup and Initialization
Installation
pnpm install chat-adapter-imessage @photon-ai/imessage-kit
# For remote mode, also install the advanced kit
pnpm install @photon-ai/advanced-imessage-kit
createiMessageAdapter(config)
This is the main entry point. It creates and configures an instance of the iMessageAdapter.
import { createiMessageAdapter } from 'chat-adapter-imessage';
// Local Mode: Runs on your Mac, uses your iMessage
const localAdapter = createiMessageAdapter({ local: true });
// Remote Mode: Connects to Photon's production servers
const remoteAdapter = createiMessageAdapter({
local: false,
serverUrl: process.env.IMESSAGE_SERVER_URL, // Provided by Photon
apiKey: process.env.IMESSAGE_API_KEY // Provided by Photon
});
Configuration (iMessageAdapterConfig)
| Mode | local | serverUrl | apiKey | Description |
|---|---|---|---|---|
| Local | true | Optional | Optional | Runs on macOS using @photon-ai/imessage-kit. serverUrl and apiKey are ignored. |
| Remote | false | Required | Required | Connects to Photon's infra using @photon-ai/advanced-imessage-kit. |
Integrating with Chat
Once created, the adapter is passed to the Chat constructor from the Vercel AI SDK.
import { Chat } from 'chat';
import { createiMessageAdapter } from 'chat-adapter-imessage';
const adapter = createiMessageAdapter({ local: true });
const chat = new Chat({
adapter,
// ... your other Chat config
});
Core Adapter Methods
adapter.initialize(chat)
Called by the Chat instance to link the adapter. In remote mode, this method also establishes the WebSocket connection to the Photon server.
adapter.postMessage(threadId, message)
Sends a message to a specific iMessage thread.
threadId: The encoded thread identifier, which is thechatGuid(e.g.,iMessage;-;+15551234567).message: AnAdapterPostableMessageobject, which can be a simple string or aFormattedContentobject with markdown.
const threadId = 'iMessage;-;+15551234567'; // A direct message thread
// Send a simple text message
await adapter.postMessage(threadId, 'Hello from the adapter!');
// Send a message with markdown (will be converted to plain text)
await adapter.postMessage(threadId, {
markdown: 'Here are the results:\n\n- **Item 1**\n- *Item 2*'
});
adapter.editMessage(threadId, messageId, message)
Edits a previously sent message. Only supported in remote mode.
await adapter.editMessage(
'iMessage;-;+15551234567',
'p:0/GUID-OF-MESSAGE-TO-EDIT',
'This is the corrected text.'
);
adapter.deleteMessage(threadId, messageId)
Unsends/deletes a previously sent message. Only supported in remote mode.
await adapter.deleteMessage(
'iMessage;-;+15551234567',
'p:0/GUID-OF-MESSAGE-TO-DELETE'
);
adapter.react(threadId, messageId, emoji)
Adds a tapback/reaction to a message. Only supported in remote mode.
emoji: A standard emoji character or a name likeheart,like,laugh.
await adapter.react(
'iMessage;-;+15551234567',
'p:0/GUID-OF-MESSAGE-TO-REACT-TO',
'❤️'
);
adapter.startGatewayListener(options)
This is the primary method for receiving messages. It starts a listener that ingests messages from both local and remote SDKs and forwards them to the Chat instance.
// In your main application file
const adapter = createiMessageAdapter({ local: true });
const chat = new Chat({ adapter });
// Start listening for incoming iMessages
adapter.startGatewayListener();
// Now, messages sent to your iMessage will be processed by the Chat SDK
chat.on('message', (message) => {
console.log(`[${message.author.userName}]: ${message.text}`);
// Your AI logic here...
});
adapter.handleWebhook(request)
This method is not supported. The adapter does not use traditional webhooks for message ingestion. You must use startGatewayListener() instead.
Type Reference
iMessageThreadId
The decoded threadId object.
interface iMessageThreadId {
chatGuid: string; // e.g., "iMessage;-;+1234567890"
}
iMessageGatewayMessageData
The normalized message object that the adapter uses internally.
interface iMessageGatewayMessageData {
guid: string;
text: string | null;
sender: string;
senderName: string | null;
chatId: string;
isGroupChat: boolean;
isFromMe: boolean;
date: string; // ISO 8601
attachments: iMessageAttachment[];
source: 'local' | 'remote';
raw?: unknown; // Original message object from the underlying SDK
}
NativeWebhookPayload
If using the Self-Hosted Kit's native webhook feature, this is the shape of the JSON payload that will be sent to your endpoint. The adapter can process this payload.
interface NativeWebhookPayload {
guid: string;
text: string | null;
sender: string;
chatId: string;
// ... and other fields from the Basic Kit Message object
}