Skip to content

Add dedicated-outbound iMessage group chats - #49

Merged
dimavrem22 merged 1 commit into
mainfrom
feat/imessage-group-chats
Jul 29, 2026
Merged

Add dedicated-outbound iMessage group chats#49
dimavrem22 merged 1 commit into
mainfrom
feat/imessage-group-chats

Conversation

@dimavrem22

Copy link
Copy Markdown
Contributor

Summary

Ports the Hermes bridge's iMessage group-chat flow (inkbox-ai/hermes-agent-plugin#76) so the two behave the same on the wire.

Inbound — a group is one shared context

A group conversation is a single context that everyone in it shares, so the conversation is the chat, not the sender. Every participant's message lands in the same session, which is what lets the agent follow a thread rather than seeing N disconnected 1:1s. One-to-one keeps its per-contact chat exactly as before.

The prompt gains a group marker and the response policy:

[inkbox:group_imessage conversation_id=… from=… participants=…,… reply_mode=conversation_id | contact=…]
Group iMessage response policy: you receive every message in this group so you can track context.
Reply only when the latest message clearly addresses this Inkbox agent, asks it to act, or a
visible answer would be expected from the agent.
Treat ordinary group chatter as context only.
If no visible reply is warranted, return exactly [SILENT].

That is the same policy this bridge already applies to group SMS — _group_imessage_prompt deliberately mirrors _group_sms_prompt rather than importing Hermes's internals, so it reads like the rest of the file.

Group flags and participants come from the event when present, falling back to a conversation lookup for events that omit them (mirroring _lookup_text_conversation_summary).

Outbound — starting a group

to now accepts a single E.164 recipient or a list of 2–8 to open a group. This bridge previously had no to at all — the tool was conversation_id-only on the grounds that iMessage is recipient-first. That stays true for shared and dedicated inbound lines; a dedicated outbound line is the exception, and only it may initiate.

The tool checks for that line and returns a specific error code rather than letting the send fail at the API:

imessage_group_requires_dedicated_outbound

plus validation for exactly-one-of to/conversation_id, at least one recipient, at most 8, and distinct.

Tests

6 new: group routing into the shared conversation chat with the policy and participants present; 1:1 keeping its per-contact chat with no policy injected; and the four outbound rejection paths (dedicated-line gate, both-targets, >8, duplicates).

314 pass. The 14 tests/test_sessions.py failures are pre-existing on main — a TypeError from an SDK mismatch in this environment, unrelated and confirmed against a clean checkout.

Version

0.2.6 → 0.2.7, matching the Hermes group-chat release so the fleet stays on one number.

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>
@dimavrem22
dimavrem22 marked this pull request as ready for review July 29, 2026 03:59
@dimavrem22
dimavrem22 merged commit 1cdf872 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