feat(flow): implement ABCD session policy and host return whitelist

- Scheme A: on-demand session start, inactivity-based expiry, handoff auto-recording
- Scheme B: cold-start overlay with swipe guidance and return alert
- Scheme C+D: HostAppURLRegistry (20 apps) and sourceApplication capture
- Settings: skip app switch toggle and inactivity duration picker
- Add LSApplicationQueriesSchemes for canOpenURL checks

Co-authored-by: Rocky <hkgood@users.noreply.github.com>
This commit is contained in:
Cursor Agent
2026-07-06 07:38:26 +00:00
parent 6489340b2e
commit 13cd7b30f9
25 changed files with 936 additions and 77 deletions
@@ -36,8 +36,6 @@ final class KeyboardFlowCoordinator {
private var wasFlowSessionActive = false
private var flowSessionMonitorTask: Task<Void, Never>?
private var isAwaitingFlowResult = false
private var lastFlowAutoStartAttempt: TimeInterval = 0
private static let flowAutoStartCooldown: TimeInterval = 20
init(
state: KeyboardState,
@@ -95,10 +93,6 @@ final class KeyboardFlowCoordinator {
}
}
wasFlowSessionActive = active
if !active {
maybeAutoStartFlowSession()
}
}
func toggleRecording() {
@@ -224,18 +218,6 @@ final class KeyboardFlowCoordinator {
}
}
private func maybeAutoStartFlowSession() {
guard !FlowSessionBridge.isSessionActive() else { return }
guard !isPendingFlowStart, !isFlowRecording, !isAwaitingFlowResult else { return }
guard hasFullAccess(), AppGroup.isAvailable else { return }
guard case .idle = state.phase else { return }
let now = Date().timeIntervalSince1970
guard now - lastFlowAutoStartAttempt >= Self.flowAutoStartCooldown else { return }
lastFlowAutoStartAttempt = now
beginFlowStart()
}
private func showFlowSessionExpiredHint() {
let message = ExtL10n.string("keyboard.flow.sessionExpired")
state.phase = .error(.flowSessionExpired, message: message)
@@ -332,9 +314,9 @@ final class KeyboardFlowCoordinator {
flowStartDeadline = 0
stopFlowWatchdog()
state.lastTranscript = ""
state.phase = .idle
refreshSessionState()
debug("completeFlowStartHandoff")
startFlowRecording()
debug("completeFlowStartHandoff → auto startFlowRecording")
}
private func startFlowLevelWatchdog() {