Skip to content

Add dedicated-outbound iMessage group chats - #13

Merged
dimavrem22 merged 3 commits into
mainfrom
feat/imessage-group-chats
Jul 29, 2026
Merged

Add dedicated-outbound iMessage group chats#13
dimavrem22 merged 3 commits into
mainfrom
feat/imessage-group-chats

Conversation

@dimavrem22

@dimavrem22 dimavrem22 commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Summary

Brings iMessage groups into line with the rest of the plugin fleet (inkbox-ai/hermes-agent-plugin#76).

This bridge already had a channel-agnostic group path — countParticipants, group: { participantCount, participants }, and groupReminder's reply-only-when-addressed + [SILENT] policy — so iMessage groups partly worked already. Three gaps kept them from behaving like the other four plugins.

1. A group is one shared context

chatKeyFor prefers contactId, so in a group each participant got their own chat. The agent saw N disconnected 1:1s instead of one conversation it could follow. The conversation now keys the chat whenever there is more than one participant; 1:1 keeps its per-contact chat.

2. Groups were only detected from resolved event contacts

countParticipants counted data.contacts / data.agent_identities. A conversation that names its participants inline on the message, or is simply flagged is_group, was treated as 1:1. It now also reads the message's own participants and is_group.

3. The frame did not say how to reply

Phone-channel group frames now carry reply_mode=conversation_id, matching the other plugins. Two existing tests pinned the exact frame string and were updated.

Outbound

to accepts one recipient or 1–8 distinct recipients. Opening a group requires a dedicated outbound iMessage line — checked locally so the failure names the reason rather than surfacing as an API error. Existing recipient allowlist and approval flows apply per recipient.

Tests

661 pass, tsc clean against SDK 0.5.7. Two new: an iMessage group frame carrying participants, reply mode and the silent policy; and a 1:1 that gets none of it.

Version

0.2.6 → 0.2.7, matching the rest of the fleet.

dimavrem22 and others added 3 commits July 29, 2026 01:38
Mirrors the Hermes bridge's group flow so the two behave the same on the
wire.

Inbound: a group is one shared context, so the conversation - not the
sender - is the chat, and every participant's message lands in the same
session. The prompt carries a group marker with the participant list and
reply_mode=conversation_id, plus the response policy the group SMS path
already uses: reply only when the latest message actually addresses the
agent, treat the rest as context, and return [SILENT] otherwise. Group
flags and participants are read from the event, falling back to a
conversation lookup for events that omit them.

Outbound: `to` now accepts one recipient or a list of 2-8 to open a
group. Starting a group needs a dedicated outbound iMessage line, so the
tool checks for one and returns a specific error code rather than letting
the send fail at the API. Shared and dedicated inbound lines stay
recipient-first, which is why conversation_id remains the documented
reply path.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The SDK has typed `to` as `string | string[]` since 0.5.6, which is the
floor this plugin already pins. The cast came from reading a stale local
node_modules (0.4.24) rather than the pinned range.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@dimavrem22
dimavrem22 marked this pull request as ready for review July 29, 2026 03:59
@dimavrem22
dimavrem22 merged commit 9fcb9cd into main Jul 29, 2026
20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant