feat: sync force-quit Flow teardown and polish macOS local ASR UX

End Live Activities and release the audio session synchronously on
applicationWillTerminate, and continue macOS local-model install,
onboarding, and settings polish on this branch.
This commit is contained in:
Rocky
2026-07-09 17:13:07 +08:00
parent 200265fbd6
commit dcb66a9849
34 changed files with 1936 additions and 340 deletions
@@ -88,6 +88,7 @@ final class FlowSessionManager: ObservableObject {
capture.onEngineLiveChanged = { [weak self] _ in
self?.refreshHostReady()
}
FlowTerminationCoordinator.register(self)
}
// MARK: - Public
@@ -210,6 +211,73 @@ final class FlowSessionManager: ObservableObject {
AppPermissions.openSystemSettings()
}
/// teardown ASR/LLMLive Activity
/// `FlowTerminationCoordinator` `end`
func prepareForProcessTermination() {
debug("prepareForProcessTermination")
coldStartContext = nil
isColdStartHandoff = false
coldStartRecoveryTask?.cancel()
coldStartRecoveryTask = nil
startTask?.cancel()
startTask = nil
commandObserver = nil
pollingTask?.cancel()
pollingTask = nil
heartbeatTask?.cancel()
heartbeatTask = nil
expiryTask?.cancel()
expiryTask = nil
levelTask?.cancel()
levelTask = nil
finalizeTask?.cancel()
finalizeTask = nil
utteranceSafetyTask?.cancel()
utteranceSafetyTask = nil
asrTask?.cancel()
asrTask = nil
chunkedPipeline = nil
if isUtteranceRecording || isUtteranceProcessing {
capture.cancelUtterance()
asr.cancel()
}
capture.cancelUtterance()
if capture.running {
capture.stop()
}
endBackgroundKeepAlive()
ScreenWakeLock.release()
sessionASR?.cancel()
sessionASR = nil
sessionASREngineMode = nil
sessionASRWarmedLocaleID = nil
if isActive || FlowSessionBridge.isSessionActive() {
FlowSessionBridge.markSessionInactive()
FlowSessionDarwin.postSessionChanged()
}
activeSessionId = nil
currentUtteranceId = nil
currentCommandSeq = 0
lastHandledCommandSeq = 0
isUtteranceRecording = false
isUtteranceProcessing = false
isActive = false
isStarting = false
sessionExpiresAt = nil
sessionWarning = nil
currentPartial = ""
lastFinal = ""
chunkWarnings = []
FlowSessionBridge.setHostReady(false)
}
func endSession() {
guard isActive else { return }
debug("Flow session ended")