spb/poche Public
Agent personnel 100 % on-device — SwiftUI + Apple Foundation Models + EventKit + SwiftData. Aucune API, aucun serveur.
Swift 100%
1//2// PendingAction.swift3// Poche4//5// Author: Simon-Pierre Boucher6// Contact: contact@spboucher.ai7//89import Foundation1011/// A side-effect proposed by the model, waiting for the user's explicit12/// confirmation. The model never writes; it proposes, the app decides13/// (CLAUDE.md §3 — the non-negotiable principle).14struct PendingAction: Identifiable, Sendable {15 enum Status: Sendable, Equatable {16 case waiting17 case executing18 case failed(String)19 }2021 enum Payload: Sendable {22 case reminder(ReminderDraft)23 case event(EventDraft)24 case note(NoteDraft)25 case newTask(TaskDraft)26 case taskUpdate(TaskUpdateDraft)27 }2829 let id = UUID()30 let payload: Payload31 var status: Status = .waiting3233 /// Card headline — always future tense: nothing has happened yet.34 var title: String {35 switch payload {36 case .reminder: "Créer un rappel"37 case .event: "Créer un événement"38 case .note: "Enregistrer une note"39 case .newTask: "Créer une tâche"40 case .taskUpdate: "Modifier une tâche"41 }42 }4344 /// Resolved values shown in clear text (dates already validated).45 var fields: [(label: String, value: String)] {46 switch payload {47 case .reminder(let draft):48 [("Titre", draft.title),49 ("Échéance", DateResolver.display(draft.due))]50 + (draft.list.map { [("Liste", $0)] } ?? [])51 case .event(let draft):52 [("Titre", draft.title),53 ("Début", DateResolver.display(draft.start)),54 ("Fin", DateResolver.display(draft.end))]55 + (draft.location.map { [("Lieu", $0)] } ?? [])56 case .note(let draft):57 [("Titre", draft.title),58 ("Contenu", String(draft.content.prefix(140)))]59 case .newTask(let draft):60 [("Titre", draft.title)]61 + (draft.details.map { [("Détails", String($0.prefix(140)))] } ?? [])62 + (draft.due.map { [("Échéance", DateResolver.display($0))] } ?? [])63 case .taskUpdate(let draft):64 [("Tâche", draft.currentTitle)]65 + (draft.newTitle.map { [("Nouveau titre", $0)] } ?? [])66 + (draft.newDue.map { [("Nouvelle échéance", DateResolver.display($0))] } ?? [])67 + (draft.markDone == true ? [("État", "Marquer comme faite")] : [])68 }69 }7071 /// One-line summary used for system notices and agent notes.72 var summary: String {73 switch payload {74 case .reminder(let draft):75 "Rappel « \(draft.title) » pour \(DateResolver.display(draft.due))"76 case .event(let draft):77 "Événement « \(draft.title) » le \(DateResolver.display(draft.start))"78 case .note(let draft):79 "Note « \(draft.title) »"80 case .newTask(let draft):81 "Tâche « \(draft.title) »"82 case .taskUpdate(let draft):83 "Modification de la tâche « \(draft.currentTitle) »"84 }85 }86}8788struct ReminderDraft: Sendable {89 let title: String90 let due: Date91 let list: String?92}9394struct EventDraft: Sendable {95 let title: String96 let start: Date97 let end: Date98 let location: String?99}100101struct NoteDraft: Sendable {102 let title: String103 let content: String104}105106struct TaskDraft: Sendable {107 let title: String108 let details: String?109 let due: Date?110}111112struct TaskUpdateDraft: Sendable {113 let taskID: UUID114 let currentTitle: String115 let newTitle: String?116 let newDue: Date?117 let markDone: Bool?118}119