feat(flow): on-demand host handoff with reliable return and a back-tap tip

Keyboard
- Only jump to the app when the voice surface is shown (tab tap / opened
  there); typing tabs no longer leave the host on keyboard open.
- Wait up to 0.4 s for the arbiter to report the host app before leaving,
  and late-bind an id that arrives within 6 s of the jump.
- Empty arbiter callbacks no longer wipe an already captured host id;
  install the capture handler in every build configuration.

App
- Return to the previous app with up to four retries using iOS's real
  open result, and honor the skip-app-switch preference.
- Keep the pending host id across cold-start zombie reconciliation, and
  expire it after two minutes (legacy values without a timestamp stay valid).
- Show a light tip pointing at the system back control when we cannot
  return automatically ("点击返回,立即使用").

Tests: TTL behaviour and return retry policy.
This commit is contained in:
Rocky
2026-10-29 16:32:52 +08:00
parent 4dce67df84
commit e09ad34437
17 changed files with 454 additions and 63 deletions
@@ -30,6 +30,13 @@ public enum FlowHandoffPolicy {
/// capture, ASR and model warm-up remain lazy until an actual mic press.
public static let allowsProactiveHostAutoLaunch = true
/// Delays (seconds) before each attempt to return to the previous app once
/// the host session is ready after a keyboard handoff. A single try was
/// brittle: the first `open` can lose a race with PiP settling, and a late
/// host-id binding from the keyboard may land a moment after ready. The tip
/// is the fallback once every attempt has failed.
public static let hostReturnRetryDelays: [TimeInterval] = [0.45, 0.8, 1.2, 1.8]
/// Samples of "host truly dead" required before a cold-start jump is allowed
/// from a mic-driven ready-wait recovery. Idle opens must never cold-start.
/// Mic press uses `shouldOpenHostColdStart` directly.