diff --git a/features/conversation-assistant/macos/Sources/Services/iMessageReader.swift b/features/conversation-assistant/macos/Sources/Services/iMessageReader.swift index 231bf75da..6d7fb92dd 100644 --- a/features/conversation-assistant/macos/Sources/Services/iMessageReader.swift +++ b/features/conversation-assistant/macos/Sources/Services/iMessageReader.swift @@ -82,6 +82,7 @@ class iMessageReader { // Fetch all contacts with phone numbers and emails let keysToFetch: [CNKeyDescriptor] = [ + CNContactIdentifierKey as CNKeyDescriptor, CNContactGivenNameKey as CNKeyDescriptor, CNContactFamilyNameKey as CNKeyDescriptor, CNContactPhoneNumbersKey as CNKeyDescriptor, @@ -96,27 +97,30 @@ class iMessageReader { guard !fullName.isEmpty else { return } - // Index by phone numbers - for phone in contact.phoneNumbers { - let normalized = self.normalizePhoneNumber(phone.value.stringValue) - self.contactCache[normalized] = ContactInfo( - identifier: normalized, - displayName: fullName, - phoneNumber: normalized, - email: nil - ) + // Get all phones and emails for this single contact + let phones = contact.phoneNumbers.map { self.normalizePhoneNumber($0.value.stringValue) } + let emails = contact.emailAddresses.map { ($0.value as String).lowercased() } + + // Create a complete ContactInfo with first phone/email (for sync) + let completeContact = ContactInfo( + identifier: phones.first ?? emails.first ?? contact.identifier, + displayName: fullName, + phoneNumber: phones.first, + email: emails.first + ) + + // Index by all phones (all point to same complete contact) + for phone in phones { + self.contactCache[phone] = completeContact } - // Index by emails - for email in contact.emailAddresses { - let emailStr = email.value as String - self.contactCache[emailStr.lowercased()] = ContactInfo( - identifier: emailStr, - displayName: fullName, - phoneNumber: nil, - email: emailStr - ) + // Index by all emails (all point to same complete contact) + for email in emails { + self.contactCache[email] = completeContact } + + // Also index by contact identifier for direct lookup + self.contactCache[contact.identifier] = completeContact } NSLog("iMessageReader: Loaded \(contactCache.count) contact entries") @@ -140,12 +144,14 @@ class iMessageReader { } /// Get all cached contacts for syncing + /// Each CNContact from address book is already a single person - deduplicate by identifier func getAllContacts() -> [ContactInfo] { - // Deduplicate by display name (same person may have multiple phones/emails) + // Deduplicate by identifier (unique per CNContact, not by name) + // This prevents merging two different "John Smith" contacts var seen = Set() return contactCache.values.filter { contact in - if seen.contains(contact.displayName) { return false } - seen.insert(contact.displayName) + if seen.contains(contact.identifier) { return false } + seen.insert(contact.identifier) return true } }