Dolphin3.0-CoreML / examples /ios /DolphinCoreMLTestApp /Sources /SystemTools /SystemToolModels.swift
| import Foundation | |
| /// Sensitive system data is opt-in per capability. Keeping the future | |
| /// capabilities in the persisted vocabulary lets Settings evolve without | |
| /// changing the policy contract used by tools. | |
| enum AgentSystemCapability: String, CaseIterable, Sendable, Codable, Hashable { | |
| case calendarRead = "calendar_read" | |
| case contactsLookup = "contacts_lookup" | |
| case locationSnapshot = "location_snapshot" | |
| } | |
| enum CalendarQueryWindow: String, CaseIterable, Sendable, Codable, Equatable { | |
| case today | |
| case tomorrow | |
| case nextSevenDays = "next_7_days" | |
| func dateInterval(containing now: Date, calendar: Calendar) -> DateInterval { | |
| let startOfToday = calendar.startOfDay(for: now) | |
| let startOffset: Int | |
| let endOffset: Int | |
| switch self { | |
| case .today: | |
| startOffset = 0 | |
| endOffset = 1 | |
| case .tomorrow: | |
| startOffset = 1 | |
| endOffset = 2 | |
| case .nextSevenDays: | |
| startOffset = 0 | |
| endOffset = 7 | |
| } | |
| let start = calendar.date( | |
| byAdding: .day, | |
| value: startOffset, | |
| to: startOfToday | |
| ) ?? startOfToday | |
| let end = calendar.date( | |
| byAdding: .day, | |
| value: endOffset, | |
| to: startOfToday | |
| ) ?? start.addingTimeInterval(TimeInterval(endOffset - startOffset) * 86_400) | |
| return DateInterval(start: start, end: end) | |
| } | |
| var displayLabel: String { | |
| switch self { | |
| case .today: "today" | |
| case .tomorrow: "tomorrow" | |
| case .nextSevenDays: "for the next 7 days" | |
| } | |
| } | |
| } | |
| /// Deliberately contains only the fields the assistant may disclose. EventKit | |
| /// identifiers, notes, attendees, URLs, locations, and recurrence metadata | |
| /// never cross this boundary. | |
| struct SystemCalendarEvent: Sendable, Equatable { | |
| let title: String | |
| let startsAt: Date | |
| let endsAt: Date | |
| let isAllDay: Bool | |
| } | |
| struct SystemCalendarEventPage: Sendable, Equatable { | |
| let events: [SystemCalendarEvent] | |
| let hasMore: Bool | |
| } | |
| enum SystemCalendarAuthorization: String, Sendable, Equatable { | |
| case fullAccess | |
| case notDetermined | |
| case denied | |
| case restricted | |
| case writeOnly | |
| } | |
| enum SystemCalendarReaderError: LocalizedError, Sendable, Equatable { | |
| case fullAccessRequired(SystemCalendarAuthorization) | |
| case unavailable(String) | |
| var errorDescription: String? { | |
| switch self { | |
| case .fullAccessRequired(let status): | |
| switch status { | |
| case .notDetermined: | |
| "Calendar full access has not been granted. Grant access from Dolphin Settings before trying again." | |
| case .denied: | |
| "Calendar full access is denied. Enable it in iOS Settings before trying again." | |
| case .restricted: | |
| "Calendar full access is restricted on this iPhone." | |
| case .writeOnly: | |
| "Calendar access is write-only. Full access is required to read events." | |
| case .fullAccess: | |
| "Calendar events are temporarily unavailable." | |
| } | |
| case .unavailable(let reason): | |
| "Calendar events are unavailable: \(reason)" | |
| } | |
| } | |
| } | |
| protocol SystemCalendarReading: Sendable { | |
| func authorizationStatus() async -> SystemCalendarAuthorization | |
| /// Implementations return at most `limit` events and indicate whether more | |
| /// matched. This method must never request permission. | |
| func readEvents( | |
| in interval: DateInterval, | |
| limit: Int | |
| ) async throws -> SystemCalendarEventPage | |
| } | |
| /// Pure fallback used by host tests and non-iOS consumers. The production app | |
| /// injects EventKit explicitly, keeping platform APIs out of the agent core. | |
| struct UnavailableSystemCalendarReader: SystemCalendarReading { | |
| func authorizationStatus() async -> SystemCalendarAuthorization { | |
| .denied | |
| } | |
| func readEvents( | |
| in interval: DateInterval, | |
| limit: Int | |
| ) async throws -> SystemCalendarEventPage { | |
| throw SystemCalendarReaderError.unavailable( | |
| "No system calendar adapter is available in this environment." | |
| ) | |
| } | |
| } | |