// FlowCommand.swift // OSGKeyboard · Shared import Foundation public struct FlowCommand: Codable, Equatable, Sendable { public enum Action: String, Codable, Sendable { case startRecording case stopRecording case abort /// Light warm-up: ASR locale/assets only — no mic capture. case prewarm /// User has touched the mic; prime capture before tap/hold resolves. case primeAudio /// Touch ended without an utterance adopting the primed capture. case cancelPrimeAudio /// Remove one temporary AI conversation from host memory. case endAIConversation /// AI mode: submit a prefilled question and skip ASR. case submitAIQuestion } /// Wire version that includes host-side webpage summary extraction. public static let currentProtocolVersion = 10 public let protocolVersion: Int public let sessionId: UUID public let utteranceId: UUID public let commandSeq: Int64 public let action: Action public let localeId: String public let createdAt: TimeInterval public let fieldContext: FlowFieldContext? /// Dictation (default) vs explicit edit mode. Absent on legacy v1 → dictation. public let utteranceMode: FlowUtteranceMode? /// Verified source for explicit last-input editing. public let editSourceText: String? public let sourceHistoryEntryID: UUID? public let sourceHistoryEntryRevision: Int64? /// Host-memory conversation used only by `.aiQuestion`. public let aiConversationID: UUID? /// Prefilled question used only by `.submitAIQuestion`. public let aiQuestionText: String? /// Public webpage fetched by the host for an explicit summary skill. public let aiWebPageURL: URL? /// Fine-grained managed-gateway intent for AI question submissions. public let aiTaskKind: ManagedGatewayTaskKind? /// Audited product entry point for managed usage attribution. public let managedRequestSource: ManagedGatewayRequestSource? /// Optional server-audited purpose for managed gateway billing policy. public let managedRequestPurpose: ManagedGatewayRequestPurpose? /// Required feature discriminator when `managedRequestPurpose == .oobe`. public let managedOOBEFeature: ManagedGatewayOOBEFeature? /// Clipboard-skill thinking override. Nil keeps AI-mode default (on). public let aiThinkingEnabled: Bool? /// Absolute wall-clock deadlines survive extension reconstruction. public let startDeadlineAt: TimeInterval? public let processingDeadlineAt: TimeInterval? public init( protocolVersion: Int = FlowCommand.currentProtocolVersion, sessionId: UUID, utteranceId: UUID, commandSeq: Int64, action: Action, localeId: String, createdAt: TimeInterval = Date().timeIntervalSince1970, fieldContext: FlowFieldContext? = nil, utteranceMode: FlowUtteranceMode? = nil, editSourceText: String? = nil, sourceHistoryEntryID: UUID? = nil, sourceHistoryEntryRevision: Int64? = nil, aiConversationID: UUID? = nil, aiQuestionText: String? = nil, aiWebPageURL: URL? = nil, aiTaskKind: ManagedGatewayTaskKind? = nil, managedRequestSource: ManagedGatewayRequestSource? = nil, managedRequestPurpose: ManagedGatewayRequestPurpose? = nil, managedOOBEFeature: ManagedGatewayOOBEFeature? = nil, aiThinkingEnabled: Bool? = nil, startDeadlineAt: TimeInterval? = nil, processingDeadlineAt: TimeInterval? = nil ) { self.protocolVersion = protocolVersion self.sessionId = sessionId self.utteranceId = utteranceId self.commandSeq = commandSeq self.action = action self.localeId = localeId self.createdAt = createdAt self.fieldContext = fieldContext self.utteranceMode = utteranceMode self.editSourceText = editSourceText self.sourceHistoryEntryID = sourceHistoryEntryID self.sourceHistoryEntryRevision = sourceHistoryEntryRevision self.aiConversationID = aiConversationID self.aiQuestionText = aiQuestionText self.aiWebPageURL = aiWebPageURL self.aiTaskKind = aiTaskKind self.managedRequestSource = managedRequestSource self.managedRequestPurpose = managedRequestPurpose self.managedOOBEFeature = managedOOBEFeature self.aiThinkingEnabled = aiThinkingEnabled self.startDeadlineAt = startDeadlineAt self.processingDeadlineAt = processingDeadlineAt } public var resolvedUtteranceMode: FlowUtteranceMode { utteranceMode ?? .dictation } }