@mapier/imsg-sdk 0.2.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/gateway/chat-background.d.ts +40 -0
- package/dist/gateway/chat-background.js +219 -0
- package/dist/gateway/chat-background.js.map +1 -0
- package/dist/gateway/edit-message.d.ts +17 -0
- package/dist/gateway/edit-message.js +70 -0
- package/dist/gateway/edit-message.js.map +1 -0
- package/dist/gateway/external-ids.d.ts +1 -0
- package/dist/gateway/external-ids.js +3 -1
- package/dist/gateway/external-ids.js.map +1 -1
- package/dist/gateway/fake.d.ts +13 -4
- package/dist/gateway/fake.js +82 -9
- package/dist/gateway/fake.js.map +1 -1
- package/dist/gateway/imsg.d.ts +6 -4
- package/dist/gateway/imsg.js +98 -33
- package/dist/gateway/imsg.js.map +1 -1
- package/dist/gateway/send-rich.d.ts +17 -0
- package/dist/gateway/send-rich.js +43 -0
- package/dist/gateway/send-rich.js.map +1 -0
- package/dist/gateway/types.d.ts +77 -5
- package/dist/gateway/types.js +165 -0
- package/dist/gateway/types.js.map +1 -1
- package/dist/imsg/rpc.d.ts +33 -2
- package/dist/imsg/rpc.js +31 -3
- package/dist/imsg/rpc.js.map +1 -1
- package/dist/imsg/status.js +17 -0
- package/dist/imsg/status.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/docs/api.md +1 -0
- package/docs/bridge-contract.md +7 -3
- package/docs/capability-matrix.md +4 -1
- package/docs/compatibility.md +4 -1
- package/docs/gateway-contract.md +271 -13
- package/docs/imcore_selectors.md +634 -0
- package/package.json +1 -1
|
@@ -0,0 +1,634 @@
|
|
|
1
|
+
# IMCore Selectors
|
|
2
|
+
|
|
3
|
+
Every bridge handler in `Sources/IMsgHelper/IMsgInjected.m` ultimately calls one
|
|
4
|
+
or more **private Apple selectors**. This maps each verb → handler → the selector
|
|
5
|
+
it fires → the class and framework that selector lives in on the Mac. Use it when
|
|
6
|
+
a macOS release breaks a verb (a selector renamed/removed, like `leaveChat` in
|
|
7
|
+
[#5]) and you need to find what to re-point to.
|
|
8
|
+
|
|
9
|
+
## Where the selectors live
|
|
10
|
+
|
|
11
|
+
| Framework | Classes used here | Path (pre-26) |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| **IMCore** | `IMChat`, `IMMessage`, `IMMessageItem`, `IMMessagePartChatItem`, `IMChatRegistry`, `IMHandle`, `IMHandleRegistrar`, `IMAccount`, `IMAccountController`, `IMChatHistoryController`, `IMNicknameController`, `IMFileTransfer`, `IMFileTransferCenter`, `IMDaemonController`, `IMEmojiTapback`, `IMTapbackSender`, `IMMutableChatContext` | `/System/Library/PrivateFrameworks/IMCore.framework` |
|
|
14
|
+
| **IDS** (Apple Identity Services) | `IDSIDQueryController` | `/System/Library/PrivateFrameworks/IDS.framework` |
|
|
15
|
+
| **IMDPersistence / daemon** | `IMDPersistentAttachmentController` | daemon-side (IMSharedUtilities/IMDPersistence) |
|
|
16
|
+
|
|
17
|
+
On **macOS 26** the framework binaries moved, and that changes how you inspect them:
|
|
18
|
+
|
|
19
|
+
- **The binary moved off disk.** Pre-26, a framework's code was a real file inside its
|
|
20
|
+
bundle (`IMCore.framework/Versions/A/IMCore`) that you could open, link, or dump. On
|
|
21
|
+
macOS 26 that file is gone — the code exists **only inside the dyld shared cache**, one
|
|
22
|
+
giant pre-linked blob that's mapped into every running process. The `.framework` folder
|
|
23
|
+
may still be there, but it's an empty shell (Info.plist, no executable).
|
|
24
|
+
- **`class-dump` must target the cache, not a raw binary.** `class-dump` lists a binary's
|
|
25
|
+
Objective-C classes and selectors — this is how you'd find what `leaveChat` became. Since
|
|
26
|
+
there's no on-disk `IMCore` binary to point it at, use a cache-aware tool
|
|
27
|
+
(`ipsw class-dump`, `dsdump`) or first extract the image out with `ipsw dyld extract`.
|
|
28
|
+
- **Some C functions must be found at runtime (`dlsym`), not linked at build time.** To call
|
|
29
|
+
a function the computer needs its memory address. Normally the *linker* fills that in when
|
|
30
|
+
you build — but it needs an IMCore file/stub at build time, and there isn't one, so the
|
|
31
|
+
build would fail ("undefined symbol"). Instead the dylib looks the address up **while
|
|
32
|
+
running** and populates the map from function to address dynamically with function `dlsym` — the code is already in memory (via the shared cache),
|
|
33
|
+
so `dlsym("IMCreate…")` hands back a pointer to call through.
|
|
34
|
+
|
|
35
|
+
## Consequences of linking at runtime instead of compile-time
|
|
36
|
+
|
|
37
|
+
Because there are no framework files to link against, the dylib resolves every
|
|
38
|
+
private class, selector, and C symbol **by name at runtime** (`NSClassFromString`
|
|
39
|
+
+ `performSelector:` + `dlsym`). That trades the compiler's build-time guarantees
|
|
40
|
+
for per-host fragility, and it's the reason for nearly every defensive choice in
|
|
41
|
+
`IMsgInjected.m`:
|
|
42
|
+
|
|
43
|
+
1. **No compiler safety net — this *is* why a selector like `leaveChat` fails the
|
|
44
|
+
way it does.** A renamed/removed name still **compiles fine**; it only fails when
|
|
45
|
+
it runs on a host that lacks it. So `#5` is a runtime `respondsToSelector: NO`,
|
|
46
|
+
not a build error. The whole "a selector died on the new OS" bug class exists
|
|
47
|
+
*because* resolution is deferred to runtime.
|
|
48
|
+
2. **Fragile to OS version upgrades.** The same shipped dylib
|
|
49
|
+
can work on one macOS and silently no-op on another. That's why the SDK model is
|
|
50
|
+
**capability-based, not version-based**: `imsg status --json` probes each selector
|
|
51
|
+
live (`respondsToSelector:` / `dlsym != NULL`) and reports what actually resolved.
|
|
52
|
+
3. **Everything is hand-declared, and nothing checks you.** No headers to `#import`,
|
|
53
|
+
so the dylib forward-declares the private classes/selectors itself and writes
|
|
54
|
+
function-pointer typedefs for C funcs. If Apple changes a method's *signature*
|
|
55
|
+
(arg types, not just its name), the declaration is now wrong with **no compiler to
|
|
56
|
+
flag it** — you get corruption or a crash at runtime. Correctness is on you + tests.
|
|
57
|
+
* Particularly dangerous for long arg calls. `performSelector:` only handles
|
|
58
|
+
simple signatures (object args, ≤2). For the 4-arg `editMessageItem:…` or the 13-arg `initWithSender:…` drop to `objc_msgSend` cast to a hand-written function-pointer type, and a wrong function declaration can cause memory corruption (usually compiler is there to guard).
|
|
59
|
+
4. **Every call is defensive by necessity.** `respondsToSelector:` guard →
|
|
60
|
+
`performSelector:` → `@try/@catch`, degrading to `{ok:false}` rather than crashing
|
|
61
|
+
Messages.app. Compiler would have caught this at compile-time.
|
|
62
|
+
5. **Two runtime gates stack.** The dylib both injects (`DYLD_INSERT_LIBRARIES`, SIP off) and resolves privately at runtime. macOS 26 library-validation/entitlement limits can block the injection entirely; even injected, the names must still resolve against the mapped shared cache. Neither is knowable at build time.
|
|
63
|
+
6. **Testing is the only verification.** Since neither compiler nor linker validates any
|
|
64
|
+
of this, the only way to know a build works is to run it on a specific macOS and smoke
|
|
65
|
+
it — which is why the release record pins `imsg status --json` + `live-smoke` per OS
|
|
66
|
+
version, and why "wired ≠ host-verified."
|
|
67
|
+
|
|
68
|
+
**The upside:** runtime resolution also buys adaptivity. One dylib can carry fallbacks
|
|
69
|
+
and pick per host — which is exactly why `handleEditMessage` tries the macOS 27
|
|
70
|
+
five-argument `editMessageItem:…newPartTranslation:…` first, then `editMessageItem:…`,
|
|
71
|
+
then `editMessage:…`, and why a fixed `leaveChat` would probe candidates
|
|
72
|
+
and choose whichever the host has. A statically-linked binary couldn't branch like that;
|
|
73
|
+
a runtime-resolved one straddles multiple macOS versions from a single build.
|
|
74
|
+
|
|
75
|
+
## The map
|
|
76
|
+
|
|
77
|
+
Target class in **bold**; `sel` is the selector the handler invokes on it.
|
|
78
|
+
|
|
79
|
+
**Note: In Objective C, the arguments are part of the function name itself, and are separated by colons. Square brackets denote a function call.**
|
|
80
|
+
|
|
81
|
+
Ex.
|
|
82
|
+
```
|
|
83
|
+
[chat sendMessage:aMessage reason:aReason]; // call
|
|
84
|
+
selector: sendMessage:reason:
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Send
|
|
88
|
+
| Verb | Handler | Call | Host |
|
|
89
|
+
|---|---|---|---|
|
|
90
|
+
| `send-message` / `send-rich-link` | `handleSendMessage` | **IMChat** `sendMessage:reason:` (msg built on **IMMessage**, threaded via `setThreadOriginator:` / `setThreadIdentifier:`) | ✓ |
|
|
91
|
+
| `send-poll` | `handleSendPoll` | **IMMessage** `initWithSender:time:text:…associatedMessageType:…` + `setBalloonBundleID:` + `setPayloadData:` → **IMChat** send | ✓ |
|
|
92
|
+
| `send-poll-vote` / `-unvote` | `handleSendPollVoteMutation` | **IMChat** `sendMessage:` (summary-info mutation) | ✓ selector live, not wired above |
|
|
93
|
+
| `send-multipart` / `send-attachment` | `handleSendMultipart` | **IMFileTransferCenter** `guidForNewOutgoingTransferWithLocalURL:` · `transferForGUID:` · `registerTransferWithDaemon:` · **IMDPersistentAttachmentController** `_persistentPathForTransfer:filename:highQuality:chatGUID:storeAtExternalPath:` · **IMFileTransfer** `setLocalURL:`/`setMimeType:`/`setTransferredFilename:` | ✓ |
|
|
94
|
+
| `send-sticker` | `handleSendSticker` | **IMChat** `sendMessage:` targeting **IMMessagePartChatItem** (`guid`/`index`/`messagePartRange`) | ✓ |
|
|
95
|
+
| `send-reaction` | `handleSendReaction` | **IMTapbackSender** `initWithTapback:chat:messagePartChatItem:` / `initWithTapback:chat:messageGUID:messagePartRange:messageSummaryInfo:threadIdentifier:` + `send`; emoji via **IMEmojiTapback** `initWithEmoji:isRemoved:`; legacy fallback **IMChat** `sendMessage:` | ✓ |
|
|
96
|
+
| `notify-anyways` | `handleNotifyAnyways` | **IMChat** `markChatItemAsNotifyRecipient:` | ✗ not wired above |
|
|
97
|
+
|
|
98
|
+
### Mutate a message
|
|
99
|
+
| Verb | Handler | Call | Host |
|
|
100
|
+
|---|---|---|---|
|
|
101
|
+
| `edit-message` | `handleEditMessage` | **IMChat** `editMessageItem:atPartIndex:withNewPartText:newPartTranslation:backwardCompatabilityText:` (macOS 27; fallbacks `editMessageItem:…` / `editMessage:…`) | ✓ wired on macOS 27 (imsg#3), native CLI/RPC E2E'd (imsg#11); SDK verb capability-gated + preflighted, host `tier2-smoke --only-edit-message` pending |
|
|
102
|
+
| `unsend-message` | `handleUnsendMessage` | **IMChat** `retractMessagePart:` | ✓ |
|
|
103
|
+
| `delete-message` | `handleDeleteMessage` | **IMChat** `deleteChatItems:` (device-local only) | ✓ |
|
|
104
|
+
|
|
105
|
+
### Group / chat management
|
|
106
|
+
| Verb | Handler | Call | Host |
|
|
107
|
+
|---|---|---|---|
|
|
108
|
+
| `add-participant` | `handleAddParticipant` | **IMChat** `inviteParticipants:reason:` / `inviteParticipantsToiMessageChat:reason:` | ✓ |
|
|
109
|
+
| `remove-participant` | `handleRemoveParticipant` | **IMChat** `removeParticipants:reason:` / `removeParticipantsFromiMessageChat:reason:` | ✓ |
|
|
110
|
+
| `set-display-name` | `handleSetDisplayName` | **IMChat** `_setDisplayName:` (+ `sendGroupPhotoUpdate:`) | ✓ |
|
|
111
|
+
| `update-group-photo` | `handleUpdateGroupPhoto` | **IMChat** `sendGroupPhotoUpdate:` + **IMFileTransferCenter** `createNewOutgoingGroupPhotoTransferWithLocalFileURL:` | ✓ |
|
|
112
|
+
| `create-chat` | `handleCreateChat` | **IMChatRegistry** `chatForIMHandle:` / `chatForIMHandles:` (+ `_setDisplayName:`) | ✓ |
|
|
113
|
+
| **`leave-chat`** | `handleLeaveChat` | **IMChat** `leave` (macOS 27; `leaveChat` fallback for older hosts) | **⚠ call site updated to `leave`; not yet tested on relay e2e** |
|
|
114
|
+
| `delete-chat` | `handleDeleteChat` | **IMChatRegistry** `sharedInstance` (delete path selector-gated) | ✗ not wired above |
|
|
115
|
+
|
|
116
|
+
### Read / presence
|
|
117
|
+
| Verb | Handler | Call | Host |
|
|
118
|
+
|---|---|---|---|
|
|
119
|
+
| `mark-chat-read` | `handleMarkChatRead` | **IMChat** `markAllMessagesAsRead` | ✓ internal only |
|
|
120
|
+
| `mark-chat-unread` | `handleMarkChatUnread` | **IMChat** `markLastMessageAsUnread` | ✗ not wired above |
|
|
121
|
+
| `start-/stop-/check-typing` | `handleStartTyping` … | **IMChat** `isCurrentlyTyping` (+ typing setter) | ✓ internal only |
|
|
122
|
+
|
|
123
|
+
### Identity / introspection
|
|
124
|
+
| Verb | Handler | Call | Host |
|
|
125
|
+
|---|---|---|---|
|
|
126
|
+
| `check-imessage-availability` | `handleCheckIMessageAvailability` | **IDSIDQueryController** `_currentIDStatusForDestination:service:listenerID:` / `currentIDStatusForDestination:service:` | ✓ selector live, not wired above |
|
|
127
|
+
| `get-account-info` | `handleGetAccountInfo` | **IMAccountController** `sharedInstance`/`activeIMessageAccount` → **IMAccount** `vettedAliases`/`loginIMHandle` | ✓ |
|
|
128
|
+
| `get-nickname-info` / `share-nickname` / `should-offer-nickname-sharing` | `handleGetNicknameInfo` … | **IMNicknameController** `nicknameForHandle:` · `personalNickname` · `shouldOfferNicknameSharingForChat:` | ✓ (Name & Photo) |
|
|
129
|
+
| `download-purged-attachment` | `handleDownloadPurgedAttachment` | **IMFileTransferCenter** `acceptTransfer:` · **IMDaemonController** `connectToDaemon` | ✓ |
|
|
130
|
+
|
|
131
|
+
The selector **markers** reported by `imsg status --json` are probed in
|
|
132
|
+
`IMsgInjected.m` near the top of the file: it checks `IMChat` for
|
|
133
|
+
`editMessageItem:…newPartTranslation:…` (marker `editMessageItemTranslation`),
|
|
134
|
+
`editMessageItem:…`, `editMessage:…`, `retractMessagePart:`, `sendMessage:reason:`.
|
|
135
|
+
Add a probe there when adding a verb so the SDK can capability-gate it; the SDK's
|
|
136
|
+
`editMessage` capability is any one of the three edit markers.
|
|
137
|
+
|
|
138
|
+
## macOS 27 operation callsites — MAP-175
|
|
139
|
+
|
|
140
|
+
**Research attribution:** These replacement entrypoints and downstream
|
|
141
|
+
operation callsites were found and verified by **Neo Shangguan** for MAP-175.
|
|
142
|
+
|
|
143
|
+
| Operation | Current bridge selector | macOS 27 entrypoint found | Downstream operation dispatch | Integration status | Found by |
|
|
144
|
+
|---|---|---|---|---|---|
|
|
145
|
+
| `leave-chat` | `-[IMChat leaveChat]` — absent | `-[IMChat leave]` | `[provider _chat_leave:chat]` | Call site updated to `leave`; not yet tested on relay e2e | **Neo Shangguan** |
|
|
146
|
+
| `edit-message` | Four-argument `-[IMChat editMessageItem:…]` / `-[IMChat editMessage:…]` — absent | Five-argument `-[IMChat editMessageItem:…newPartTranslation:…]` | `[provider _chat:chat sendEditedMessageItem:editedItem previousMessageItem:originalItem partIndex:index editType:1 backwardCompatabilityText:text]` | Wired (imsg#3), native CLI/RPC E2E'd (imsg#11) and SDK verb host-verified 2026-09-03 on macOS 27 | **Neo Shangguan** |
|
|
147
|
+
| `mentions` | `send-rich` / `send.rich` `text_formatting[].mention` (imsg#13) | Confirmed-mention `NSAttributedString` attribute (`__kIMMentionConfirmedMention`) resolved at runtime; no dedicated send selector | Existing `IMMessage` construction → `IMChat` send path | Wired + native E2E'd 2026-09-03 (attribute keys match a genuine iPhone mention row; highlight + notification on the recipient); SDK verb host-verified 2026-09-03 incl. recipient highlight | **Neo Shangguan** |
|
|
148
|
+
| `text-styling` / `text-effects` | No bridge operation | IM text-style/effect attributes on `NSAttributedString`; no dedicated send selector | Existing `IMMessage` construction → `IMChat` send path | Wired + tested e2e relayside | **Neo Shangguan** |
|
|
149
|
+
| `contact-card` | No contact-card-specific bridge operation | `.vcf` attachment identified by `kUTTypeVCard` / `public.vcard` | Existing `IMFileTransfer` attachment send path | Attachment representation found; not live-smoked | **Neo Shangguan** |
|
|
150
|
+
| `chat-background` | `chat.background.set` / `chat.background.remove` / `chat.background.status` (Mapier-Labs/imsg#12, merged) | `-[IMChat setTranscriptBackgroundAndSendToChat:transferID:]` | `[provider _chat:chat setTranscriptBackgroundAndSendToChat:background transferID:transferID]` | Native bridge merged after matched-build CLI/RPC E2E; SDK verbs `setChatBackground` / `removeChatBackground` / `chatBackgroundStatus` in this repo; relay command + recipient-side validation pending | **Neo Shangguan** |
|
|
151
|
+
| `location-request` | No bridge operation | `+[MSMessage findMyLocationRequestMessage]` | `+[CKComposition compositionWithMSMessage:appExtensionIdentifier:]` using `IMBalloonBundleIdentifierWaldo` → `-[CKChatController setComposition:]` | Factory/staging callsites found; payload/bridge route pending; not wired/live-smoked | **Neo Shangguan** |
|
|
152
|
+
|
|
153
|
+
The summary records stable selector names for implementation. Numeric callsite
|
|
154
|
+
addresses remain in the detailed evidence below because they are specific to a
|
|
155
|
+
single macOS build and must not become bridge constants.
|
|
156
|
+
|
|
157
|
+
The research ran against the development `mapidev` Messages profile, not the
|
|
158
|
+
production profile, on macOS 27.0 build `26A5421a` (`arm64e`) with IMCore
|
|
159
|
+
`1491.100.1.1.9`. `ipsw 3.1.712` read the native IMCore image directly from the
|
|
160
|
+
dyld shared cache. Runtime availability was checked by loading IMCore in an
|
|
161
|
+
independent process and querying the Objective-C method table; the probe did
|
|
162
|
+
not attach to or mutate Messages.
|
|
163
|
+
|
|
164
|
+
All addresses below are unslid and specific to this OS build. They are audit
|
|
165
|
+
evidence only and must never be hard-coded by the bridge; selectors still need
|
|
166
|
+
to be resolved by name at runtime.
|
|
167
|
+
|
|
168
|
+
### `leave-chat` — found by Neo Shangguan
|
|
169
|
+
|
|
170
|
+
Runtime availability:
|
|
171
|
+
|
|
172
|
+
```text
|
|
173
|
+
-[IMChat leaveChat] false
|
|
174
|
+
-[IMChat leave] true
|
|
175
|
+
-[IMChat leaveConversation] true
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
`-[IMChat leave]` begins at `0x1bf212c88`. Its operation path is:
|
|
179
|
+
|
|
180
|
+
```text
|
|
181
|
+
-[IMChat leave]
|
|
182
|
+
[self daemonServiceProvider] callsite 0x1bf212da4
|
|
183
|
+
[provider _chat_leave:self] callsite 0x1bf212db4
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
The selector stubs resolve to `daemonServiceProvider` and `_chat_leave:`. At
|
|
187
|
+
the downstream callsite, the provider is the receiver and the current `IMChat`
|
|
188
|
+
is the operation argument. This establishes `-[IMChat leave]` as the high-level
|
|
189
|
+
replacement entrypoint; bridge code should call it rather than reaching into
|
|
190
|
+
`_chat_leave:` directly.
|
|
191
|
+
|
|
192
|
+
`-[IMChat leaveConversation]` was also disassembled and resolves to
|
|
193
|
+
`[self _closeSession:NO]`. It closes the session rather than dispatching the
|
|
194
|
+
group-leave operation, so it is not a `leaveChat` replacement.
|
|
195
|
+
|
|
196
|
+
The existing `leaveChat` selector can remain as the older-system path, with
|
|
197
|
+
`leave` as a runtime-gated fallback. **Update:** the bridge call site has since
|
|
198
|
+
been updated to probe `leave`; it has not yet been tested end-to-end through
|
|
199
|
+
relay (no relay e2e group-leave smoke performed).
|
|
200
|
+
|
|
201
|
+
### `edit-message` — found by Neo Shangguan
|
|
202
|
+
|
|
203
|
+
Runtime availability:
|
|
204
|
+
|
|
205
|
+
```text
|
|
206
|
+
editMessageItem:atPartIndex:withNewPartText:
|
|
207
|
+
backwardCompatabilityText: false
|
|
208
|
+
editMessage:atPartIndex:withNewPartText:
|
|
209
|
+
backwardCompatabilityText: false
|
|
210
|
+
editMessageItem:atPartIndex:withNewPartText:
|
|
211
|
+
newPartTranslation:backwardCompatabilityText: true
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
The new method inserts a `newPartTranslation:` argument. It begins at
|
|
215
|
+
`0x1bf209994`, with the arm64 business arguments laid out as:
|
|
216
|
+
|
|
217
|
+
```text
|
|
218
|
+
x2 = original message item
|
|
219
|
+
x3 = part index
|
|
220
|
+
x4 = new part text
|
|
221
|
+
x5 = new part translation
|
|
222
|
+
x6 = backward-compatibility text
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
The ordinary-message operation path is:
|
|
226
|
+
|
|
227
|
+
```text
|
|
228
|
+
-[IMChat editMessageItem:...newPartTranslation:...]
|
|
229
|
+
[factory editedMessageItemWithOriginalMessageItem:
|
|
230
|
+
editedPartIndex:newPartText:newPartTranslation:] callsite 0x1bf209b18
|
|
231
|
+
[self daemonServiceProvider] callsite 0x1bf209b28
|
|
232
|
+
[provider _chat:self
|
|
233
|
+
sendEditedMessageItem:editedItem
|
|
234
|
+
previousMessageItem:originalItem
|
|
235
|
+
partIndex:index
|
|
236
|
+
editType:1
|
|
237
|
+
backwardCompatabilityText:backwardText] callsite 0x1bf209b4c
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
The final selector is
|
|
241
|
+
`_chat:sendEditedMessageItem:previousMessageItem:partIndex:editType:backwardCompatabilityText:`.
|
|
242
|
+
The method constructs the new edited item, resolves the daemon service
|
|
243
|
+
provider, and dispatches the edit with constant `editType` value `1`. The
|
|
244
|
+
alternate branch calls
|
|
245
|
+
`editScheduledMessageItem:atPartIndex:withNewPartText:newPartTranslation:` and
|
|
246
|
+
is for scheduled messages, not the ordinary edit path.
|
|
247
|
+
|
|
248
|
+
`newPartTranslation` is retained and forwarded without a non-null branch, which
|
|
249
|
+
is consistent with passing `nil` for an ordinary untranslated edit. A live
|
|
250
|
+
smoke must verify that assumption. The high-level five-argument `IMChat` method
|
|
251
|
+
is the replacement target; it requires an accurately cast `objc_msgSend` call
|
|
252
|
+
rather than `performSelector:` and should be runtime-gated alongside the older
|
|
253
|
+
selectors.
|
|
254
|
+
|
|
255
|
+
### `mentions` — found by Neo Shangguan
|
|
256
|
+
|
|
257
|
+
Mentions do not introduce a separate daemon operation selector. `IMMessage`
|
|
258
|
+
continues to carry an `NSAttributedString`, and the existing `IMChat` send path
|
|
259
|
+
dispatches the message. The missing bridge work is therefore attributed-text
|
|
260
|
+
payload construction rather than discovery of another `IMChat` send method.
|
|
261
|
+
|
|
262
|
+
The native macOS 27 `IMSharedUtilities` image exports the transport constants
|
|
263
|
+
needed to construct and inspect that payload:
|
|
264
|
+
|
|
265
|
+
```text
|
|
266
|
+
IMMentionAttributeName "__kIMMentionAttributeName"
|
|
267
|
+
IMMentionAttributeNameNeedsAnimation "__kIMMentionAttributeNameNeedsAnimation"
|
|
268
|
+
IMMentionConfirmedMention "__kIMMentionConfirmedMention"
|
|
269
|
+
IMMentionAutomaticConfirmedMention "__kIMMentionAutomaticConfirmedMention"
|
|
270
|
+
IMMentionUnconfirmedDirectMention "__kIMMentionUnconfirmedDirectMention"
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
They were resolved from
|
|
274
|
+
`/System/Library/PrivateFrameworks/IMSharedUtilities.framework` in an
|
|
275
|
+
independent runtime probe. Bridge code should likewise resolve symbols by name
|
|
276
|
+
and must not embed their build-specific addresses or private string values.
|
|
277
|
+
|
|
278
|
+
The construction pattern is visible in the iOSSupport ChatKit image at
|
|
279
|
+
`+[CKMentionsUtilities configureAttributedString:inTextView:
|
|
280
|
+
forConfirmedMentionInRange:needingAnimation:]`, beginning at `0x1dbecbe38`:
|
|
281
|
+
|
|
282
|
+
```text
|
|
283
|
+
add IMMentionAttributeName to the confirmed mention range 0x1dbecbea0
|
|
284
|
+
remove IMMentionUnconfirmedDirectMention from that range 0x1dbecbebc
|
|
285
|
+
remove IMMentionAutomaticConfirmedMention from that range 0x1dbecbed8
|
|
286
|
+
conditionally add IMMentionAttributeNameNeedsAnimation 0x1dbecbf54
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
The mutation callsites resolve to
|
|
290
|
+
`-[NSMutableAttributedString addAttribute:value:range:]` and
|
|
291
|
+
`-[NSMutableAttributedString removeAttribute:range:]`. The related
|
|
292
|
+
`configureAttributedString:automaticMentionAttributeWithOriginalText:
|
|
293
|
+
entityNode:nodeId:forRange:` method builds composer/autocomplete metadata and
|
|
294
|
+
stores it under `IMMentionAutomaticConfirmedMention`; the confirmed path
|
|
295
|
+
removes that temporary marker before sending.
|
|
296
|
+
|
|
297
|
+
ChatKit is present in this cache under `/System/iOSSupport` rather than as the
|
|
298
|
+
native transport framework, so its helper itself is evidence for the payload
|
|
299
|
+
shape, not a selector the bridge should invoke. The native
|
|
300
|
+
`IMSharedUtilities` constants plus the existing attributed `IMMessage` send
|
|
301
|
+
path are the candidate integration boundary. Exact attribute values and range
|
|
302
|
+
handling still require a development-profile live send smoke before the
|
|
303
|
+
feature can be declared available.
|
|
304
|
+
|
|
305
|
+
### `text-styling` and `text-effects` — found by Neo Shangguan
|
|
306
|
+
|
|
307
|
+
Inline bold, italic, underline, strikethrough, and animated text effects also
|
|
308
|
+
reuse the attributed `IMMessage` and existing `IMChat` send operation. There is
|
|
309
|
+
no additional daemon send selector. The missing bridge work is to construct
|
|
310
|
+
the transport attributes over the requested text ranges.
|
|
311
|
+
|
|
312
|
+
The native macOS 27 `IMSharedUtilities` image exports the relevant integration
|
|
313
|
+
symbols:
|
|
314
|
+
|
|
315
|
+
```text
|
|
316
|
+
IMTextBoldAttributeName
|
|
317
|
+
IMTextItalicAttributeName
|
|
318
|
+
IMTextUnderlineAttributeName
|
|
319
|
+
IMTextStrikethroughAttributeName
|
|
320
|
+
IMTextEffectAttributeName
|
|
321
|
+
IMTextStyleAll
|
|
322
|
+
IMTextEffectNameFromType
|
|
323
|
+
IMTextEffectTypeFromName
|
|
324
|
+
IMTextEffectOrderedSupportedNames
|
|
325
|
+
IMTextEffectNameBig / Bloom / Bounce / Explode / Jitter / Nod /
|
|
326
|
+
ScaleRipple / Shake / Small / Somersault / Squish / Stretch
|
|
327
|
+
IMServiceCapabilityTextEffects
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
The iOSSupport ChatKit construction reference maps the inline-style bit values
|
|
331
|
+
to transport and local display attributes as follows:
|
|
332
|
+
|
|
333
|
+
| Style value | IM transport marker | Local display attribute |
|
|
334
|
+
|---|---|---|
|
|
335
|
+
| `1` | `IMTextBoldAttributeName` | `NSFontAttributeName` with the bold trait |
|
|
336
|
+
| `2` | `IMTextItalicAttributeName` | `NSFontAttributeName` with the italic trait |
|
|
337
|
+
| `4` | `IMTextUnderlineAttributeName` | `NSUnderlineStyleAttributeName` |
|
|
338
|
+
| `8` | `IMTextStrikethroughAttributeName` | `NSStrikethroughStyleAttributeName` |
|
|
339
|
+
|
|
340
|
+
`-[NSMutableAttributedString(TextEffects)
|
|
341
|
+
ck_applyTextStyle:options:range:]` begins at `0x1dbcc19fc`. It asks
|
|
342
|
+
`ck_actionForIMTextStyle:range:` whether the requested style should be added,
|
|
343
|
+
removed, or left unchanged, then tail-dispatches to
|
|
344
|
+
`ck_addTextStyle:options:range:` at `0x1dbcc1a74` or
|
|
345
|
+
`ck_removeTextStyle:options:range:` at `0x1dbcc1aac`. The add implementation
|
|
346
|
+
begins at `0x1dbcc1ac4`.
|
|
347
|
+
|
|
348
|
+
Within `ck_addTextStyle:options:range:`, option bit zero controls the private IM
|
|
349
|
+
transport marker and option bit one controls the corresponding local display
|
|
350
|
+
attribute. The callsites that add the four IM markers are:
|
|
351
|
+
|
|
352
|
+
```text
|
|
353
|
+
IMTextBoldAttributeName 0x1dbcc1d04
|
|
354
|
+
IMTextItalicAttributeName 0x1dbcc1c2c
|
|
355
|
+
IMTextUnderlineAttributeName 0x1dbcc1ccc
|
|
356
|
+
IMTextStrikethroughAttributeName 0x1dbcc1da8
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
Each transport marker receives a constant `NSNumber` value. ChatKit separately
|
|
360
|
+
updates font traits or the standard underline/strikethrough attributes for
|
|
361
|
+
local display. Those visual attributes must not be confused with the private
|
|
362
|
+
IM markers that describe the wire payload.
|
|
363
|
+
|
|
364
|
+
Animated effects are constructed by
|
|
365
|
+
`-[NSMutableAttributedString(TextEffects)
|
|
366
|
+
ck_applyTextEffectType:range:]`, beginning at `0x1dbcc1340`. Its primary path
|
|
367
|
+
is:
|
|
368
|
+
|
|
369
|
+
```text
|
|
370
|
+
remove IMTextEffectAttributeName and NSTextAnimationAttributeName
|
|
371
|
+
remove IMTextStyleAll with both transport and display options
|
|
372
|
+
effectName = IMTextEffectNameFromType(effectType) 0x1dbcc14e4
|
|
373
|
+
animation = [UITextAnimation animationWithName:effectName] 0x1dbcc1670
|
|
374
|
+
wireValue = [NSNumber numberWithInteger:effectType] 0x1dbcc16a0
|
|
375
|
+
add NSTextAnimationAttributeName = animation 0x1dbcc1694
|
|
376
|
+
add IMTextEffectAttributeName = wireValue 0x1dbcc16c0
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
The method also checks confirmed-mention and attachment ranges before applying
|
|
380
|
+
an effect, and may repeat the two add operations over adjusted subranges. The
|
|
381
|
+
`NSTextAnimationAttributeName`/`UITextAnimation` pair is the local preview;
|
|
382
|
+
the `IMTextEffectAttributeName`/numeric effect type pair is the transport
|
|
383
|
+
representation.
|
|
384
|
+
|
|
385
|
+
As with mentions, these mutation helpers live in the `/System/iOSSupport`
|
|
386
|
+
ChatKit image and are evidence for payload shape rather than bridge call
|
|
387
|
+
targets. A bridge implementation should resolve the native
|
|
388
|
+
`IMSharedUtilities` symbols by name, construct the attributed payload itself,
|
|
389
|
+
and reuse the existing `IMMessage`/`IMChat` send path. **Update:** this is now
|
|
390
|
+
wired and tested end-to-end through relay — the attributed payload is
|
|
391
|
+
constructed over the requested ranges and sent via the existing path, with
|
|
392
|
+
effects animating on device on the `mapidev` development profile. Inbound
|
|
393
|
+
styled-text parsing remains open if that direction is needed.
|
|
394
|
+
|
|
395
|
+
### `contact-card` — found by Neo Shangguan
|
|
396
|
+
|
|
397
|
+
A real contact card is a specialized vCard attachment, not a distinct IMCore
|
|
398
|
+
send operation. The recipient-side ChatKit type is `CKContactMediaObject`,
|
|
399
|
+
which inherits through `CKCardMediaObject` from the generic `CKMediaObject`
|
|
400
|
+
attachment model:
|
|
401
|
+
|
|
402
|
+
```text
|
|
403
|
+
CKContactMediaObject : CKCardMediaObject : CKMediaObject
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
`CKMediaObject` directly stores a `CKFileTransfer`, file URL, transfer GUID,
|
|
407
|
+
data, filename, MIME type, and UTI. `CKContactMediaObject` adds vCard parsing,
|
|
408
|
+
summary, image, preview, and contact-balloon behavior; its methods include
|
|
409
|
+
`contactCardPayloadFileURL:`, `vCardSummary`, and `vCardImageOfSize:`.
|
|
410
|
+
|
|
411
|
+
`+[CKContactMediaObject UTITypes]` begins at `0x1dbcf40b4`. It loads
|
|
412
|
+
`kUTTypeVCard` and returns it in a one-element array via
|
|
413
|
+
`+[NSArray arrayWithObjects:count:]` at `0x1dbcf40f4`:
|
|
414
|
+
|
|
415
|
+
```text
|
|
416
|
+
+[CKContactMediaObject UTITypes] → @[ kUTTypeVCard ]
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
The shared-cache wire identifiers corroborate the representation:
|
|
420
|
+
|
|
421
|
+
```text
|
|
422
|
+
public.vcard native IMSharedUtilities
|
|
423
|
+
text/vcard IMDaemonCore
|
|
424
|
+
text/x-vcard IMDaemonCore and IMFoundation (legacy-compatible MIME)
|
|
425
|
+
.vcf Contacts filename generation
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
The outbound integration path is therefore:
|
|
429
|
+
|
|
430
|
+
```text
|
|
431
|
+
CNContact or caller-supplied vCard data
|
|
432
|
+
→ serialize/write a .vcf file
|
|
433
|
+
→ filename + vCard MIME/UTI metadata
|
|
434
|
+
→ existing IMFileTransfer attachment send
|
|
435
|
+
→ recipient CKContactMediaObject
|
|
436
|
+
→ CKContactBalloonView
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
The existing bridge attachment handler already stages a local file through
|
|
440
|
+
`IMFileTransferCenter`, sets the transfer filename and MIME type, registers the
|
|
441
|
+
transfer with the daemon, and reuses the ordinary message send operation. A
|
|
442
|
+
contact-card integration should build on that path rather than call ChatKit.
|
|
443
|
+
It may either accept a valid `.vcf` or serialize a contact using the Contacts
|
|
444
|
+
framework; the exact accepted MIME choice, transfer metadata, recipient
|
|
445
|
+
rendering, and inbound parsing still require a development-profile live smoke.
|
|
446
|
+
|
|
447
|
+
One apparent UI lead was explicitly ruled out. Despite its name,
|
|
448
|
+
`-[CKChatController(Contacts) contactPicker:didSelectContact:]` at
|
|
449
|
+
`0x1dc070830` eventually dispatches
|
|
450
|
+
`updateContact:withNicknameUpdate:updateType:addHandleToContact:presentationMode:`
|
|
451
|
+
at `0x1dc070a00` / `0x1dc070a40`. It updates the local address book after a
|
|
452
|
+
contact/nickname prompt; it is not a contact-card send callsite.
|
|
453
|
+
|
|
454
|
+
### `chat-background` — found by Neo Shangguan
|
|
455
|
+
|
|
456
|
+
The macOS 27 `IMChat` entrypoint is:
|
|
457
|
+
|
|
458
|
+
```objc
|
|
459
|
+
-[IMChat setTranscriptBackgroundAndSendToChat:transferID:]
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
It begins at `0x1bf20b080`. The arm64 argument flow shows the first business
|
|
463
|
+
argument retained as the pending background object and the second retained as
|
|
464
|
+
the transfer identifier. The operation path is:
|
|
465
|
+
|
|
466
|
+
```text
|
|
467
|
+
-[IMChat setTranscriptBackgroundAndSendToChat:background transferID:transferID]
|
|
468
|
+
[self daemonServiceProvider] callsite 0x1bf20b0b8
|
|
469
|
+
[provider _chat:self
|
|
470
|
+
setTranscriptBackgroundAndSendToChat:background
|
|
471
|
+
transferID:transferID] callsite 0x1bf20b0d0
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
The final selector stub resolves to
|
|
475
|
+
`_chat:setTranscriptBackgroundAndSendToChat:transferID:`. ChatKit also exposes
|
|
476
|
+
`setPendingTranscriptBackground:transferID:`, corroborating that the outbound
|
|
477
|
+
UI flow stages a background and its associated transfer before invoking the
|
|
478
|
+
`IMChat` entrypoint.
|
|
479
|
+
|
|
480
|
+
The ChatKit set/remove control flow was also recovered. The UI entrypoint
|
|
481
|
+
`-[CKCoreChatController setNewTranscriptBackground:]` begins at
|
|
482
|
+
`0x1dbc7d1b8` and generates the transfer identifier with
|
|
483
|
+
`+[NSString stringGUID]`. It stores the pending state on the conversation and
|
|
484
|
+
then branches on the background argument:
|
|
485
|
+
|
|
486
|
+
```text
|
|
487
|
+
transferID = [NSString stringGUID]
|
|
488
|
+
[self.conversation setPendingTranscriptBackground:background
|
|
489
|
+
transferID:transferID]
|
|
490
|
+
|
|
491
|
+
if background != nil:
|
|
492
|
+
[background createBackgroundWithWatchDataWithCompletion:^(preparedBackground) {
|
|
493
|
+
[self updateTranscriptBackground:preparedBackground
|
|
494
|
+
transferID:transferID]
|
|
495
|
+
}]
|
|
496
|
+
else:
|
|
497
|
+
[self updateTranscriptBackground:nil
|
|
498
|
+
transferID:transferID]
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
`-[CKCoreChatController updateTranscriptBackground:transferID:]` begins at
|
|
502
|
+
`0x1dbc7c538`. Its non-null branch stages the prepared poster and watch payloads
|
|
503
|
+
on disk, obtains the poster URL's `absoluteString`, and then converges with the
|
|
504
|
+
null removal branch on the same `IMChat` selector:
|
|
505
|
+
|
|
506
|
+
```text
|
|
507
|
+
set: [chat setTranscriptBackgroundAndSendToChat:posterURL.absoluteString
|
|
508
|
+
transferID:transferID] callsite 0x1dbc7cc38
|
|
509
|
+
remove: [chat setTranscriptBackgroundAndSendToChat:nil
|
|
510
|
+
transferID:transferID] callsite 0x1dbc7c710
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
The `absoluteString` selector stub immediately before the non-null callsite is
|
|
514
|
+
at `0x1e0016430` and resolves to selector address `0x1f5907443`. A live smoke
|
|
515
|
+
that passed the `CKTranscriptBackground` object itself produced
|
|
516
|
+
`-[ChatKit.CKTranscriptBackground length]: unrecognized selector`, further
|
|
517
|
+
confirming that the daemon-facing first argument is the staged URL string, not
|
|
518
|
+
the ChatKit wrapper object.
|
|
519
|
+
|
|
520
|
+
This establishes that removal is represented by a null background sent through
|
|
521
|
+
the same operation, not by a separate remove selector. The complete native
|
|
522
|
+
recipe archives the poster configuration, prepares its watch snapshot, stages
|
|
523
|
+
the resulting `posterData` and `watchData` at the poster URL and its
|
|
524
|
+
`im_associatedWatchBackgroundURL`, and passes the poster URL string to IMChat.
|
|
525
|
+
Recipient behavior outside the development profile still requires validation.
|
|
526
|
+
|
|
527
|
+
A read-only runtime probe on the macOS 27 development host (build `26A5421a`)
|
|
528
|
+
confirmed that `IMChat` responds to
|
|
529
|
+
`setTranscriptBackgroundAndSendToChat:transferID:`. Its live Objective-C method
|
|
530
|
+
signature returns `void`, has four total arguments (`self`, `_cmd`, and two
|
|
531
|
+
object arguments), and `+[NSString stringGUID]` is available for generating the
|
|
532
|
+
transfer identifier. A development-profile semantic smoke then generated and
|
|
533
|
+
staged a Gradient background, invoked IMChat, and matched the live background
|
|
534
|
+
GUID to the persisted `set` event and cache assets. Passing a null first
|
|
535
|
+
argument with a fresh transfer identifier subsequently cleared the same live
|
|
536
|
+
GUID and persisted a matching `clear` event. This verified the direct native
|
|
537
|
+
set/remove operations. The production bridge (Gradient builder, null-background
|
|
538
|
+
removal handler, capability probes, standalone smoke scripts, and
|
|
539
|
+
persistence-confirming CLI/RPC surfaces) then passed its matched CLI/dylib
|
|
540
|
+
E2E on the development profile and merged as Mapier-Labs/imsg#12 into
|
|
541
|
+
`mapier/deploy`. This repo's `ImsgGateway` exposes it as `setChatBackground`,
|
|
542
|
+
`removeChatBackground`, and `chatBackgroundStatus` (see
|
|
543
|
+
`docs/gateway-contract.md` §2 "Chat backgrounds"). Relay command wiring and
|
|
544
|
+
recipient-side end-to-end coverage remain pending.
|
|
545
|
+
|
|
546
|
+
### `location-request` — found by Neo Shangguan
|
|
547
|
+
|
|
548
|
+
Location Request is a Find My Messages-app payload, not an
|
|
549
|
+
`IMFMFSession` tracking operation. The ChatKit entrypoint
|
|
550
|
+
`-[CKChatController _stageFindMyLocationRequest]` begins at `0x1dbc13240`.
|
|
551
|
+
Its relevant operation path is:
|
|
552
|
+
|
|
553
|
+
```text
|
|
554
|
+
[IMFeatureFlags sharedFeatureFlags]
|
|
555
|
+
-isWaldoEnabled
|
|
556
|
+
|
|
557
|
+
soft-link the MSMessage class
|
|
558
|
+
+respondsToSelector:@selector(findMyLocationRequestMessage)
|
|
559
|
+
+findMyLocationRequestMessage
|
|
560
|
+
|
|
561
|
+
[CKComposition compositionWithMSMessage:requestMessage
|
|
562
|
+
appExtensionIdentifier:IMBalloonBundleIdentifierWaldo]
|
|
563
|
+
→ [CKChatController setComposition:composition]
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
The feature-gate selectors resolve to `sharedFeatureFlags` and
|
|
567
|
+
`isWaldoEnabled`. ChatKit soft-links the `MSMessage` class, verifies the class
|
|
568
|
+
factory at runtime, and then invokes `+[MSMessage findMyLocationRequestMessage]`.
|
|
569
|
+
It wraps the returned request in a `CKComposition` using the private
|
|
570
|
+
`IMBalloonBundleIdentifierWaldo` constant before staging the composition.
|
|
571
|
+
`Waldo` is the Find My Messages-balloon/app identity used for this request.
|
|
572
|
+
|
|
573
|
+
This rules out two misleading IMCore paths. `-[IMFMFSession
|
|
574
|
+
startTrackingLocationForChat:]` refreshes an already-shared location, while
|
|
575
|
+
`-[IMFMFSession startSharingWithChat:withDuration:]` starts sharing the local
|
|
576
|
+
user's location. Neither is the operation that constructs a request asking the
|
|
577
|
+
other participant to share.
|
|
578
|
+
|
|
579
|
+
The callable factory and staging callsites are now known, but a bridge recipe
|
|
580
|
+
is still incomplete. The implementation of
|
|
581
|
+
`+[MSMessage findMyLocationRequestMessage]`, the serialized payload it returns,
|
|
582
|
+
and the safest route from that payload into the bridge's existing `IMMessage`
|
|
583
|
+
send path remain to be recovered. Calling a `CKChatController` UI instance is
|
|
584
|
+
not assumed to be an appropriate relay integration path.
|
|
585
|
+
|
|
586
|
+
### Implementation readiness and remaining work
|
|
587
|
+
|
|
588
|
+
Finding a callsite removes the selector-discovery blocker; it does not by
|
|
589
|
+
itself make the operation available. This table is the implementation handoff
|
|
590
|
+
for the MAP-175 findings above:
|
|
591
|
+
|
|
592
|
+
| Operation | Readiness | What is resolved | Remaining implementation work |
|
|
593
|
+
|---|---|---|---|
|
|
594
|
+
| `leave-chat` | **Call site updated** | macOS 27 `-[IMChat leave]` entrypoint and `_chat_leave:` dispatch; call site re-pointed to `leave` with the `leaveChat` fallback retained | Test end-to-end through relay on `mapidev` (bridge call site updated, relay e2e not yet run) |
|
|
595
|
+
| `edit-message` | **Native merged (imsg#3/#11); SDK verb host-verified 2026-09-03** | Five-argument selector, argument order, edited-item builder, daemon dispatch, runtime probe/fallbacks, native CLI/RPC E2E | Relay `message.moderate edit` mapping of `effectStarted`; recipient-side rendering check |
|
|
596
|
+
| `mentions` | **Native merged (imsg#13, `7c31772`); SDK verb host-verified 2026-09-03** | Attribute key + handle value confirmed against a genuine row, recipient highlight + notification, membership fail-closed | Relay `message.send` payload for mention ranges |
|
|
597
|
+
| `text-styling` / `text-effects` | **Done — tested e2e relayside** | Transport attributes, style bit mapping, effect type/name mapping, existing send boundary; attributed payload constructed and sent through the relay path, animating on device | — (inbound styled-text parsing still open if needed) |
|
|
598
|
+
| `contact-card` | **Prototype-ready** | vCard UTI/content representation and reuse of the existing attachment-transfer path | Choose/validate MIME and transfer metadata, accept or serialize `.vcf` input, verify recipient rendering and inbound parsing |
|
|
599
|
+
| `chat-background` remove | **Native merged (imsg#12); SDK verb in this repo** | Null-background semantics, transfer-ID generation, selector ABI, live clear, persisted clear event, race-guard contract, matched-build CLI/RPC E2E | Host-Mac `tier2-smoke.ts --chat-background`; relay `chat.background.remove` command; validate recipient state |
|
|
600
|
+
| `chat-background` set | **Native merged (imsg#12); SDK verb in this repo** | Gradient configuration build, archive, poster/watch snapshot, staging contract, URL-string IMChat argument, live set, persisted event, cache/upload metadata, matched-build CLI/RPC E2E | Host-Mac `tier2-smoke.ts --chat-background`; relay `chat.background.set` command; validate recipient rendering and retry behavior |
|
|
601
|
+
| `location-request` | **Callsite-only** | Waldo feature gate, `MSMessage` factory selector, `CKComposition` wrapper, and app-extension identifier | Recover the factory's serialized Waldo payload and a safe bridge route into the existing send path without depending on a `CKChatController` UI instance |
|
|
602
|
+
|
|
603
|
+
Every new or replaced bridge path still needs defensive runtime capability
|
|
604
|
+
probing, native wiring, a matched CLI/dylib rebuild, injection under the
|
|
605
|
+
development `mapidev` profile, and operation-specific live smoke evidence.
|
|
606
|
+
Build-specific addresses above remain audit evidence and must never become
|
|
607
|
+
runtime constants.
|
|
608
|
+
|
|
609
|
+
These findings do not include an upstream Messages UI-controller trace. MAP-175
|
|
610
|
+
does not require that trace once the callable entrypoint and downstream
|
|
611
|
+
operation dispatch have been established.
|
|
612
|
+
|
|
613
|
+
## Finding a replacement selector (the archaeology)
|
|
614
|
+
|
|
615
|
+
When a selector goes dead (host `✗`, `respondsToSelector:` NO), on a host of that
|
|
616
|
+
macOS version:
|
|
617
|
+
|
|
618
|
+
1. **Dump the class.** `class-dump` (or Hopper/IDA) the dyld shared cache and read
|
|
619
|
+
the target class — e.g. all `IMChat` methods — to spot the renamed/new one.
|
|
620
|
+
2. **Trace the UI.** Attach `lldb` / `frida-trace` to Messages.app, do the action
|
|
621
|
+
by hand, and watch which IMCore selectors fire (`objc` method trace on `IMChat`).
|
|
622
|
+
3. **Cross-check open source.** BlueBubbles and Barcelona (open-imcore) implement
|
|
623
|
+
leave / edit / etc.; their newer-macOS handling names candidate selectors.
|
|
624
|
+
4. Swap the `@selector(...)` in the handler, add its status-probe marker, rebuild
|
|
625
|
+
the matched CLI+dylib, inject (SIP off), and smoke.
|
|
626
|
+
|
|
627
|
+
For `leave-chat`: `handleLeaveChat` and everything above it (relay → RPC → file
|
|
628
|
+
IPC → dispatch) is intact. The only dead link was `-[IMChat leaveChat]`;
|
|
629
|
+
`-[IMChat leave]` is its macOS 27 replacement and the call site has since been
|
|
630
|
+
re-pointed to it (with `leaveChat` retained as the older-host path). The
|
|
631
|
+
remaining work is to test it end-to-end through relay on the development
|
|
632
|
+
profile.
|
|
633
|
+
|
|
634
|
+
[#5]: https://github.com/Mapier-Labs/imsg-sdk/issues/5
|
package/package.json
CHANGED