@mentra/engine 0.1.0-dev.0 → 3.1.0-dev.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (503) hide show
  1. package/README.md +55 -18
  2. package/build/bluetooth-sdk/debug.d.ts +2 -0
  3. package/build/bluetooth-sdk/debug.d.ts.map +1 -0
  4. package/build/bluetooth-sdk/debug.js +2 -0
  5. package/build/bluetooth-sdk/debug.js.map +1 -0
  6. package/build/bluetooth-sdk/index.d.ts +3 -0
  7. package/build/bluetooth-sdk/index.d.ts.map +1 -0
  8. package/build/bluetooth-sdk/index.js +3 -0
  9. package/build/bluetooth-sdk/index.js.map +1 -0
  10. package/build/bluetooth-sdk/photo-receiver.d.ts +3 -0
  11. package/build/bluetooth-sdk/photo-receiver.d.ts.map +1 -0
  12. package/build/bluetooth-sdk/photo-receiver.js +3 -0
  13. package/build/bluetooth-sdk/photo-receiver.js.map +1 -0
  14. package/build/bluetooth-sdk/react.d.ts +2 -0
  15. package/build/bluetooth-sdk/react.d.ts.map +1 -0
  16. package/build/bluetooth-sdk/react.js +2 -0
  17. package/build/bluetooth-sdk/react.js.map +1 -0
  18. package/build/bluetooth-sdk/types.d.ts +2 -0
  19. package/build/bluetooth-sdk/types.d.ts.map +1 -0
  20. package/build/bluetooth-sdk/types.js +2 -0
  21. package/build/bluetooth-sdk/types.js.map +1 -0
  22. package/build/engine.d.ts +24 -16
  23. package/build/engine.d.ts.map +1 -1
  24. package/build/engine.js +9 -4
  25. package/build/engine.js.map +1 -1
  26. package/build/facades/dev.d.ts +3 -2
  27. package/build/facades/dev.d.ts.map +1 -1
  28. package/build/facades/dev.js +18 -12
  29. package/build/facades/dev.js.map +1 -1
  30. package/build/facades/display.d.ts +1 -1
  31. package/build/facades/display.d.ts.map +1 -1
  32. package/build/facades/display.js +1 -1
  33. package/build/facades/display.js.map +1 -1
  34. package/build/facades/gallery.js +1 -1
  35. package/build/facades/gallery.js.map +1 -1
  36. package/build/facades/glasses.d.ts +4 -13
  37. package/build/facades/glasses.d.ts.map +1 -1
  38. package/build/facades/glasses.js +21 -6
  39. package/build/facades/glasses.js.map +1 -1
  40. package/build/facades/glassesSettings.d.ts.map +1 -1
  41. package/build/facades/glassesSettings.js +1 -0
  42. package/build/facades/glassesSettings.js.map +1 -1
  43. package/build/facades/miniapps.d.ts +4 -0
  44. package/build/facades/miniapps.d.ts.map +1 -1
  45. package/build/facades/miniapps.js +5 -0
  46. package/build/facades/miniapps.js.map +1 -1
  47. package/build/facades/ota.d.ts +14 -16
  48. package/build/facades/ota.d.ts.map +1 -1
  49. package/build/facades/ota.js +22 -7
  50. package/build/facades/ota.js.map +1 -1
  51. package/build/facades/pairing.d.ts +8 -5
  52. package/build/facades/pairing.d.ts.map +1 -1
  53. package/build/facades/pairing.js +12 -10
  54. package/build/facades/pairing.js.map +1 -1
  55. package/build/facades/permissions.d.ts +10 -0
  56. package/build/facades/permissions.d.ts.map +1 -1
  57. package/build/facades/permissions.js +10 -0
  58. package/build/facades/permissions.js.map +1 -1
  59. package/build/facades/phoneNotifications.d.ts +0 -4
  60. package/build/facades/phoneNotifications.d.ts.map +1 -1
  61. package/build/facades/phoneNotifications.js +4 -14
  62. package/build/facades/phoneNotifications.js.map +1 -1
  63. package/build/facades/speech.d.ts +7 -6
  64. package/build/facades/speech.d.ts.map +1 -1
  65. package/build/facades/speech.js +1 -1
  66. package/build/facades/speech.js.map +1 -1
  67. package/build/generated/releaseMetadata.d.ts +12 -0
  68. package/build/generated/releaseMetadata.d.ts.map +1 -0
  69. package/build/generated/releaseMetadata.js +10 -0
  70. package/build/generated/releaseMetadata.js.map +1 -0
  71. package/build/index.d.ts +10 -8
  72. package/build/index.d.ts.map +1 -1
  73. package/build/index.js +6 -5
  74. package/build/index.js.map +1 -1
  75. package/build/react/MentraLiveOtaFlow.d.ts +38 -0
  76. package/build/react/MentraLiveOtaFlow.d.ts.map +1 -0
  77. package/build/react/MentraLiveOtaFlow.js +528 -0
  78. package/build/react/MentraLiveOtaFlow.js.map +1 -0
  79. package/build/react/index.d.ts +2 -0
  80. package/build/react/index.d.ts.map +1 -0
  81. package/build/react/index.js +2 -0
  82. package/build/react/index.js.map +1 -0
  83. package/build/react/useEngineSnapshot.d.ts +2 -0
  84. package/build/react/useEngineSnapshot.d.ts.map +1 -0
  85. package/build/react/useEngineSnapshot.js +14 -0
  86. package/build/react/useEngineSnapshot.js.map +1 -0
  87. package/build/runtime/bootstrap.d.ts +24 -1
  88. package/build/runtime/bootstrap.d.ts.map +1 -1
  89. package/build/runtime/bootstrap.js +17 -0
  90. package/build/runtime/bootstrap.js.map +1 -1
  91. package/build/runtime/scanQrSeam.d.ts +7 -0
  92. package/build/runtime/scanQrSeam.d.ts.map +1 -0
  93. package/build/runtime/scanQrSeam.js +16 -0
  94. package/build/runtime/scanQrSeam.js.map +1 -0
  95. package/build/services/AppRegistry.d.ts +41 -57
  96. package/build/services/AppRegistry.d.ts.map +1 -1
  97. package/build/services/AppRegistry.js +208 -144
  98. package/build/services/AppRegistry.js.map +1 -1
  99. package/build/services/AudioCloudUplink.d.ts +12 -0
  100. package/build/services/AudioCloudUplink.d.ts.map +1 -1
  101. package/build/services/AudioCloudUplink.js +22 -1
  102. package/build/services/AudioCloudUplink.js.map +1 -1
  103. package/build/services/AudioPlaybackService.d.ts +41 -2
  104. package/build/services/AudioPlaybackService.d.ts.map +1 -1
  105. package/build/services/AudioPlaybackService.js +250 -33
  106. package/build/services/AudioPlaybackService.js.map +1 -1
  107. package/build/services/BlobStore.d.ts +2 -1
  108. package/build/services/BlobStore.d.ts.map +1 -1
  109. package/build/services/BlobStore.js +17 -9
  110. package/build/services/BlobStore.js.map +1 -1
  111. package/build/services/CloudAudioSubscriptionSync.d.ts.map +1 -1
  112. package/build/services/CloudAudioSubscriptionSync.js +13 -1
  113. package/build/services/CloudAudioSubscriptionSync.js.map +1 -1
  114. package/build/services/CloudClientService.d.ts +15 -0
  115. package/build/services/CloudClientService.d.ts.map +1 -1
  116. package/build/services/CloudClientService.js +93 -1
  117. package/build/services/CloudClientService.js.map +1 -1
  118. package/build/services/DeviceEventRouter.js +5 -5
  119. package/build/services/DeviceEventRouter.js.map +1 -1
  120. package/build/services/ForegroundLocationPermission.d.ts +13 -0
  121. package/build/services/ForegroundLocationPermission.d.ts.map +1 -0
  122. package/build/services/ForegroundLocationPermission.js +12 -0
  123. package/build/services/ForegroundLocationPermission.js.map +1 -0
  124. package/build/services/GlassesSettingsSync.d.ts.map +1 -1
  125. package/build/services/GlassesSettingsSync.js +9 -3
  126. package/build/services/GlassesSettingsSync.js.map +1 -1
  127. package/build/services/GlassesStatusProjection.d.ts +2 -1
  128. package/build/services/GlassesStatusProjection.d.ts.map +1 -1
  129. package/build/services/GlassesStatusProjection.js +15 -8
  130. package/build/services/GlassesStatusProjection.js.map +1 -1
  131. package/build/services/HotspotOtaTransport.d.ts +29 -0
  132. package/build/services/HotspotOtaTransport.d.ts.map +1 -0
  133. package/build/services/HotspotOtaTransport.js +146 -0
  134. package/build/services/HotspotOtaTransport.js.map +1 -0
  135. package/build/services/HotspotShutdown.d.ts +17 -0
  136. package/build/services/HotspotShutdown.d.ts.map +1 -0
  137. package/build/services/HotspotShutdown.js +29 -0
  138. package/build/services/HotspotShutdown.js.map +1 -0
  139. package/build/services/LocalDisplayManager.d.ts +15 -0
  140. package/build/services/LocalDisplayManager.d.ts.map +1 -1
  141. package/build/services/LocalDisplayManager.js +106 -20
  142. package/build/services/LocalDisplayManager.js.map +1 -1
  143. package/build/services/LocalMiniappRuntime.d.ts +74 -2
  144. package/build/services/LocalMiniappRuntime.d.ts.map +1 -1
  145. package/build/services/LocalMiniappRuntime.js +785 -291
  146. package/build/services/LocalMiniappRuntime.js.map +1 -1
  147. package/build/services/LocalMiniappStorage.d.ts +32 -0
  148. package/build/services/LocalMiniappStorage.d.ts.map +1 -0
  149. package/build/services/LocalMiniappStorage.js +66 -0
  150. package/build/services/LocalMiniappStorage.js.map +1 -0
  151. package/build/services/LocalMiniappUserIdentity.d.ts +20 -0
  152. package/build/services/LocalMiniappUserIdentity.d.ts.map +1 -0
  153. package/build/services/LocalMiniappUserIdentity.js +43 -0
  154. package/build/services/LocalMiniappUserIdentity.js.map +1 -0
  155. package/build/services/LocalSttFallbackCoordinator.d.ts +9 -6
  156. package/build/services/LocalSttFallbackCoordinator.d.ts.map +1 -1
  157. package/build/services/LocalSttFallbackCoordinator.js +18 -12
  158. package/build/services/LocalSttFallbackCoordinator.js.map +1 -1
  159. package/build/services/MicStateCoordinator.d.ts +42 -1
  160. package/build/services/MicStateCoordinator.d.ts.map +1 -1
  161. package/build/services/MicStateCoordinator.js +141 -3
  162. package/build/services/MicStateCoordinator.js.map +1 -1
  163. package/build/services/MiniappLauncher.d.ts.map +1 -1
  164. package/build/services/MiniappLauncher.js +23 -3
  165. package/build/services/MiniappLauncher.js.map +1 -1
  166. package/build/services/MiniappLiveness.d.ts +12 -0
  167. package/build/services/MiniappLiveness.d.ts.map +1 -0
  168. package/build/services/MiniappLiveness.js +16 -0
  169. package/build/services/MiniappLiveness.js.map +1 -0
  170. package/build/services/NativeHttpResponse.d.ts +3 -0
  171. package/build/services/NativeHttpResponse.d.ts.map +1 -0
  172. package/build/services/NativeHttpResponse.js +6 -0
  173. package/build/services/NativeHttpResponse.js.map +1 -0
  174. package/build/services/OtaArtifactDownloader.d.ts +65 -0
  175. package/build/services/OtaArtifactDownloader.d.ts.map +1 -0
  176. package/build/services/OtaArtifactDownloader.js +226 -0
  177. package/build/services/OtaArtifactDownloader.js.map +1 -0
  178. package/build/services/OtaAutoChain.d.ts +34 -0
  179. package/build/services/OtaAutoChain.d.ts.map +1 -0
  180. package/build/services/OtaAutoChain.js +83 -0
  181. package/build/services/OtaAutoChain.js.map +1 -0
  182. package/build/services/OtaErrorMapping.d.ts +18 -0
  183. package/build/services/OtaErrorMapping.d.ts.map +1 -0
  184. package/build/services/OtaErrorMapping.js +63 -0
  185. package/build/services/OtaErrorMapping.js.map +1 -0
  186. package/build/services/OtaInstallCoordinator.d.ts +95 -15
  187. package/build/services/OtaInstallCoordinator.d.ts.map +1 -1
  188. package/build/services/OtaInstallCoordinator.js +577 -130
  189. package/build/services/OtaInstallCoordinator.js.map +1 -1
  190. package/build/services/OtaService.d.ts.map +1 -1
  191. package/build/services/OtaService.js +8 -1
  192. package/build/services/OtaService.js.map +1 -1
  193. package/build/services/OtaUpdateCheckService.d.ts +39 -4
  194. package/build/services/OtaUpdateCheckService.d.ts.map +1 -1
  195. package/build/services/OtaUpdateCheckService.js +138 -22
  196. package/build/services/OtaUpdateCheckService.js.map +1 -1
  197. package/build/services/PhoneCalendarHelpers.d.ts +32 -0
  198. package/build/services/PhoneCalendarHelpers.d.ts.map +1 -0
  199. package/build/services/PhoneCalendarHelpers.js +157 -0
  200. package/build/services/PhoneCalendarHelpers.js.map +1 -0
  201. package/build/services/PhoneCalendarService.d.ts +5 -0
  202. package/build/services/PhoneCalendarService.d.ts.map +1 -0
  203. package/build/services/PhoneCalendarService.js +45 -0
  204. package/build/services/PhoneCalendarService.js.map +1 -0
  205. package/build/services/PhoneCameraFovCoordinator.d.ts +30 -0
  206. package/build/services/PhoneCameraFovCoordinator.d.ts.map +1 -0
  207. package/build/services/PhoneCameraFovCoordinator.js +194 -0
  208. package/build/services/PhoneCameraFovCoordinator.js.map +1 -0
  209. package/build/services/PhoneNotificationsSync.d.ts.map +1 -1
  210. package/build/services/PhoneNotificationsSync.js +12 -10
  211. package/build/services/PhoneNotificationsSync.js.map +1 -1
  212. package/build/services/PhonePhotoCoordinator.d.ts +26 -2
  213. package/build/services/PhonePhotoCoordinator.d.ts.map +1 -1
  214. package/build/services/PhonePhotoCoordinator.js +79 -9
  215. package/build/services/PhonePhotoCoordinator.js.map +1 -1
  216. package/build/services/PhoneStreamCoordinator.d.ts +8 -1
  217. package/build/services/PhoneStreamCoordinator.d.ts.map +1 -1
  218. package/build/services/PhoneStreamCoordinator.js +125 -13
  219. package/build/services/PhoneStreamCoordinator.js.map +1 -1
  220. package/build/services/PhoneVideoCoordinator.d.ts +2 -0
  221. package/build/services/PhoneVideoCoordinator.d.ts.map +1 -1
  222. package/build/services/PhoneVideoCoordinator.js +10 -0
  223. package/build/services/PhoneVideoCoordinator.js.map +1 -1
  224. package/build/services/SceneRenderer.d.ts.map +1 -1
  225. package/build/services/SceneRenderer.js +15 -0
  226. package/build/services/SceneRenderer.js.map +1 -1
  227. package/build/services/SentenceTtsPipeline.d.ts +31 -0
  228. package/build/services/SentenceTtsPipeline.d.ts.map +1 -0
  229. package/build/services/SentenceTtsPipeline.js +67 -0
  230. package/build/services/SentenceTtsPipeline.js.map +1 -0
  231. package/build/services/SupportProfileSync.d.ts +22 -0
  232. package/build/services/SupportProfileSync.d.ts.map +1 -0
  233. package/build/services/SupportProfileSync.js +213 -0
  234. package/build/services/SupportProfileSync.js.map +1 -0
  235. package/build/services/TranscriptionRouting.d.ts +12 -0
  236. package/build/services/TranscriptionRouting.d.ts.map +1 -0
  237. package/build/services/TranscriptionRouting.js +24 -0
  238. package/build/services/TranscriptionRouting.js.map +1 -0
  239. package/build/services/TtsTextSanitizer.d.ts +5 -0
  240. package/build/services/TtsTextSanitizer.d.ts.map +1 -0
  241. package/build/services/TtsTextSanitizer.js +61 -0
  242. package/build/services/TtsTextSanitizer.js.map +1 -0
  243. package/build/services/WifiAdbRequest.d.ts +17 -0
  244. package/build/services/WifiAdbRequest.d.ts.map +1 -0
  245. package/build/services/WifiAdbRequest.js +35 -0
  246. package/build/services/WifiAdbRequest.js.map +1 -0
  247. package/build/services/asg/asgCameraApi.d.ts +43 -0
  248. package/build/services/asg/asgCameraApi.d.ts.map +1 -1
  249. package/build/services/asg/asgCameraApi.js +509 -26
  250. package/build/services/asg/asgCameraApi.js.map +1 -1
  251. package/build/services/asg/cameraRollExportCoordinator.d.ts +60 -0
  252. package/build/services/asg/cameraRollExportCoordinator.d.ts.map +1 -0
  253. package/build/services/asg/cameraRollExportCoordinator.js +618 -0
  254. package/build/services/asg/cameraRollExportCoordinator.js.map +1 -0
  255. package/build/services/asg/cameraRollExportLedger.d.ts +44 -0
  256. package/build/services/asg/cameraRollExportLedger.d.ts.map +1 -0
  257. package/build/services/asg/cameraRollExportLedger.js +78 -0
  258. package/build/services/asg/cameraRollExportLedger.js.map +1 -0
  259. package/build/services/asg/galleryNotices.d.ts +1 -1
  260. package/build/services/asg/galleryNotices.d.ts.map +1 -1
  261. package/build/services/asg/galleryNotices.js.map +1 -1
  262. package/build/services/asg/galleryPathMigration.d.ts +3 -0
  263. package/build/services/asg/galleryPathMigration.d.ts.map +1 -0
  264. package/build/services/asg/galleryPathMigration.js +26 -0
  265. package/build/services/asg/galleryPathMigration.js.map +1 -0
  266. package/build/services/asg/gallerySettingsService.d.ts +1 -0
  267. package/build/services/asg/gallerySettingsService.d.ts.map +1 -1
  268. package/build/services/asg/gallerySettingsService.js +2 -0
  269. package/build/services/asg/gallerySettingsService.js.map +1 -1
  270. package/build/services/asg/gallerySyncService.d.ts.map +1 -1
  271. package/build/services/asg/gallerySyncService.js +162 -56
  272. package/build/services/asg/gallerySyncService.js.map +1 -1
  273. package/build/services/asg/galleryTransferLedger.d.ts +58 -0
  274. package/build/services/asg/galleryTransferLedger.d.ts.map +1 -0
  275. package/build/services/asg/galleryTransferLedger.js +225 -0
  276. package/build/services/asg/galleryTransferLedger.js.map +1 -0
  277. package/build/services/asg/localNetworkTransport.d.ts +15 -0
  278. package/build/services/asg/localNetworkTransport.d.ts.map +1 -0
  279. package/build/services/asg/localNetworkTransport.js +144 -0
  280. package/build/services/asg/localNetworkTransport.js.map +1 -0
  281. package/build/services/asg/localStorageService.d.ts +18 -0
  282. package/build/services/asg/localStorageService.d.ts.map +1 -1
  283. package/build/services/asg/localStorageService.js +168 -19
  284. package/build/services/asg/localStorageService.js.map +1 -1
  285. package/build/services/asg/mediaProcessingQueue.d.ts +10 -0
  286. package/build/services/asg/mediaProcessingQueue.d.ts.map +1 -1
  287. package/build/services/asg/mediaProcessingQueue.js +193 -62
  288. package/build/services/asg/mediaProcessingQueue.js.map +1 -1
  289. package/build/services/audioPlaybackAssets.d.ts +8 -0
  290. package/build/services/audioPlaybackAssets.d.ts.map +1 -0
  291. package/build/services/audioPlaybackAssets.js +7 -0
  292. package/build/services/audioPlaybackAssets.js.map +1 -0
  293. package/build/services/gallerySyncClock.js +1 -1
  294. package/build/services/gallerySyncClock.js.map +1 -1
  295. package/build/services/glassesClockSync.d.ts.map +1 -1
  296. package/build/services/glassesClockSync.js +6 -2
  297. package/build/services/glassesClockSync.js.map +1 -1
  298. package/build/services/manifestActions.d.ts +7 -0
  299. package/build/services/manifestActions.d.ts.map +1 -0
  300. package/build/services/manifestActions.js +25 -0
  301. package/build/services/manifestActions.js.map +1 -0
  302. package/build/services/otaDisplayState.d.ts +23 -12
  303. package/build/services/otaDisplayState.d.ts.map +1 -1
  304. package/build/services/otaDisplayState.js +36 -14
  305. package/build/services/otaDisplayState.js.map +1 -1
  306. package/build/services/otaInstallPolicy.d.ts +16 -4
  307. package/build/services/otaInstallPolicy.d.ts.map +1 -1
  308. package/build/services/otaInstallPolicy.js +24 -3
  309. package/build/services/otaInstallPolicy.js.map +1 -1
  310. package/build/services/otaLegacyMapping.d.ts +1 -1
  311. package/build/services/otaLegacyMapping.d.ts.map +1 -1
  312. package/build/services/otaLegacyMapping.js.map +1 -1
  313. package/build/services/otaManifestPolicy.d.ts +14 -0
  314. package/build/services/otaManifestPolicy.d.ts.map +1 -0
  315. package/build/services/otaManifestPolicy.js +19 -0
  316. package/build/services/otaManifestPolicy.js.map +1 -0
  317. package/build/services/otaManifestUrl.d.ts +3 -0
  318. package/build/services/otaManifestUrl.d.ts.map +1 -0
  319. package/build/services/otaManifestUrl.js +38 -0
  320. package/build/services/otaManifestUrl.js.map +1 -0
  321. package/build/services/slimStreamStatus.d.ts +11 -0
  322. package/build/services/slimStreamStatus.d.ts.map +1 -1
  323. package/build/services/slimStreamStatus.js +14 -4
  324. package/build/services/slimStreamStatus.js.map +1 -1
  325. package/build/stores/apps.d.ts.map +1 -1
  326. package/build/stores/apps.js +11 -0
  327. package/build/stores/apps.js.map +1 -1
  328. package/build/stores/bluetoothSettingKeys.d.ts.map +1 -1
  329. package/build/stores/bluetoothSettingKeys.js +2 -0
  330. package/build/stores/bluetoothSettingKeys.js.map +1 -1
  331. package/build/stores/gallerySync.d.ts +2 -0
  332. package/build/stores/gallerySync.d.ts.map +1 -1
  333. package/build/stores/gallerySync.js +2 -0
  334. package/build/stores/gallerySync.js.map +1 -1
  335. package/build/stores/glasses.d.ts.map +1 -1
  336. package/build/stores/glasses.js +3 -0
  337. package/build/stores/glasses.js.map +1 -1
  338. package/build/stores/settings.d.ts +17 -0
  339. package/build/stores/settings.d.ts.map +1 -1
  340. package/build/stores/settings.js +157 -17
  341. package/build/stores/settings.js.map +1 -1
  342. package/build/types/applet.d.ts +2 -0
  343. package/build/types/applet.d.ts.map +1 -1
  344. package/build/types/applet.js.map +1 -1
  345. package/build/types/asg.d.ts +7 -0
  346. package/build/types/asg.d.ts.map +1 -1
  347. package/build/types/asg.js.map +1 -1
  348. package/build/types/enums.d.ts +2 -25
  349. package/build/types/enums.d.ts.map +1 -1
  350. package/build/types/enums.js +2 -27
  351. package/build/types/enums.js.map +1 -1
  352. package/build/types/hardware.d.ts +4 -13
  353. package/build/types/hardware.d.ts.map +1 -1
  354. package/build/types/hardware.js +15 -3
  355. package/build/types/hardware.js.map +1 -1
  356. package/build/types/index.d.ts +2 -6
  357. package/build/types/index.d.ts.map +1 -1
  358. package/build/types/index.js +2 -6
  359. package/build/types/index.js.map +1 -1
  360. package/build/utils/configuredDevHost.d.ts +6 -0
  361. package/build/utils/configuredDevHost.d.ts.map +1 -0
  362. package/build/utils/configuredDevHost.js +19 -0
  363. package/build/utils/configuredDevHost.js.map +1 -0
  364. package/build/utils/devMiniappLaunch.d.ts +19 -8
  365. package/build/utils/devMiniappLaunch.d.ts.map +1 -1
  366. package/build/utils/devMiniappLaunch.js +144 -21
  367. package/build/utils/devMiniappLaunch.js.map +1 -1
  368. package/build/utils/diagnosticContext.d.ts.map +1 -1
  369. package/build/utils/diagnosticContext.js +15 -17
  370. package/build/utils/diagnosticContext.js.map +1 -1
  371. package/build/utils/hardware/hardware.d.ts.map +1 -1
  372. package/build/utils/hardware/hardware.js +7 -3
  373. package/build/utils/hardware/hardware.js.map +1 -1
  374. package/build/utils/miniappDiagnostics.d.ts +64 -0
  375. package/build/utils/miniappDiagnostics.d.ts.map +1 -0
  376. package/build/utils/miniappDiagnostics.js +173 -0
  377. package/build/utils/miniappDiagnostics.js.map +1 -0
  378. package/build/utils/permissions/MediaLibraryPermissions.d.ts +11 -0
  379. package/build/utils/permissions/MediaLibraryPermissions.d.ts.map +1 -1
  380. package/build/utils/permissions/MediaLibraryPermissions.js +27 -4
  381. package/build/utils/permissions/MediaLibraryPermissions.js.map +1 -1
  382. package/build/utils/timers/timers.d.ts.map +1 -1
  383. package/build/utils/timers/timers.js +55 -18
  384. package/build/utils/timers/timers.js.map +1 -1
  385. package/package.json +59 -31
  386. package/src/assets/silence.wav +0 -0
  387. package/src/bluetooth-sdk/debug.ts +1 -0
  388. package/src/bluetooth-sdk/index.ts +2 -0
  389. package/src/bluetooth-sdk/photo-receiver.ts +2 -0
  390. package/src/bluetooth-sdk/react.ts +1 -0
  391. package/src/bluetooth-sdk/types.ts +1 -0
  392. package/src/engine.ts +9 -4
  393. package/src/facades/dev.ts +17 -12
  394. package/src/facades/display.ts +1 -1
  395. package/src/facades/gallery.ts +1 -1
  396. package/src/facades/glasses.ts +20 -7
  397. package/src/facades/glassesSettings.ts +5 -0
  398. package/src/facades/miniapps.ts +7 -0
  399. package/src/facades/ota.ts +23 -8
  400. package/src/facades/pairing.ts +15 -11
  401. package/src/facades/permissions.ts +14 -0
  402. package/src/facades/phoneNotifications.ts +4 -13
  403. package/src/facades/speech.ts +21 -7
  404. package/src/generated/releaseMetadata.ts +20 -0
  405. package/src/index.ts +39 -13
  406. package/src/react/MentraLiveOtaFlow.tsx +856 -0
  407. package/src/react/index.ts +7 -0
  408. package/src/react/useEngineSnapshot.ts +16 -0
  409. package/src/runtime/bootstrap.ts +33 -1
  410. package/src/runtime/scanQrSeam.ts +20 -0
  411. package/src/services/AppRegistry.ts +240 -149
  412. package/src/services/AudioCloudUplink.ts +23 -1
  413. package/src/services/AudioPlaybackService.ts +287 -38
  414. package/src/services/BlobStore.ts +22 -9
  415. package/src/services/CloudAudioSubscriptionSync.ts +13 -1
  416. package/src/services/CloudClientService.ts +96 -1
  417. package/src/services/DeviceEventRouter.ts +5 -5
  418. package/src/services/ForegroundLocationPermission.ts +24 -0
  419. package/src/services/GlassesSettingsSync.ts +9 -3
  420. package/src/services/GlassesStatusProjection.ts +16 -8
  421. package/src/services/HotspotOtaTransport.ts +184 -0
  422. package/src/services/HotspotShutdown.ts +41 -0
  423. package/src/services/LocalDisplayManager.ts +112 -21
  424. package/src/services/LocalMiniappRuntime.ts +967 -346
  425. package/src/services/LocalMiniappStorage.ts +82 -0
  426. package/src/services/LocalMiniappUserIdentity.ts +46 -0
  427. package/src/services/LocalSttFallbackCoordinator.ts +19 -12
  428. package/src/services/MicStateCoordinator.ts +169 -4
  429. package/src/services/MiniappLauncher.ts +30 -3
  430. package/src/services/MiniappLiveness.ts +24 -0
  431. package/src/services/NativeHttpResponse.ts +6 -0
  432. package/src/services/OtaArtifactDownloader.ts +312 -0
  433. package/src/services/OtaAutoChain.ts +106 -0
  434. package/src/services/OtaErrorMapping.ts +80 -0
  435. package/src/services/OtaInstallCoordinator.ts +649 -144
  436. package/src/services/OtaService.ts +11 -5
  437. package/src/services/OtaUpdateCheckService.ts +181 -21
  438. package/src/services/PhoneCalendarHelpers.ts +212 -0
  439. package/src/services/PhoneCalendarService.ts +65 -0
  440. package/src/services/PhoneCameraFovCoordinator.ts +210 -0
  441. package/src/services/PhoneNotificationsSync.ts +12 -10
  442. package/src/services/PhonePhotoCoordinator.ts +116 -18
  443. package/src/services/PhoneStreamCoordinator.ts +139 -25
  444. package/src/services/PhoneVideoCoordinator.ts +11 -0
  445. package/src/services/SceneRenderer.ts +16 -0
  446. package/src/services/SentenceTtsPipeline.ts +106 -0
  447. package/src/services/SupportProfileSync.ts +218 -0
  448. package/src/services/TranscriptionRouting.ts +37 -0
  449. package/src/services/TtsTextSanitizer.ts +66 -0
  450. package/src/services/WifiAdbRequest.ts +53 -0
  451. package/src/services/asg/asgCameraApi.ts +615 -33
  452. package/src/services/asg/cameraRollExportCoordinator.ts +627 -0
  453. package/src/services/asg/cameraRollExportLedger.ts +122 -0
  454. package/src/services/asg/galleryNotices.ts +1 -0
  455. package/src/services/asg/galleryPathMigration.ts +22 -0
  456. package/src/services/asg/gallerySettingsService.ts +3 -0
  457. package/src/services/asg/gallerySyncService.ts +184 -73
  458. package/src/services/asg/galleryTransferLedger.ts +312 -0
  459. package/src/services/asg/localNetworkTransport.ts +169 -0
  460. package/src/services/asg/localStorageService.ts +179 -24
  461. package/src/services/asg/mediaProcessingQueue.ts +209 -62
  462. package/src/services/audioPlaybackAssets.ts +8 -0
  463. package/src/services/gallerySyncClock.ts +1 -1
  464. package/src/services/glassesClockSync.ts +6 -2
  465. package/src/services/manifestActions.ts +30 -0
  466. package/src/services/otaDisplayState.ts +58 -17
  467. package/src/services/otaInstallPolicy.ts +30 -4
  468. package/src/services/otaLegacyMapping.ts +1 -1
  469. package/src/services/otaManifestPolicy.ts +42 -0
  470. package/src/services/otaManifestUrl.ts +44 -0
  471. package/src/services/slimStreamStatus.ts +16 -5
  472. package/src/stores/apps.ts +12 -0
  473. package/src/stores/bluetoothSettingKeys.ts +2 -0
  474. package/src/stores/gallerySync.ts +4 -0
  475. package/src/stores/glasses.ts +3 -0
  476. package/src/stores/settings.ts +179 -18
  477. package/src/types/applet.ts +2 -0
  478. package/src/types/asg.ts +7 -0
  479. package/src/types/enums.ts +3 -28
  480. package/src/types/hardware.ts +20 -13
  481. package/src/types/index.ts +2 -6
  482. package/src/utils/configuredDevHost.ts +17 -0
  483. package/src/utils/devMiniappLaunch.ts +151 -23
  484. package/src/utils/diagnosticContext.ts +17 -17
  485. package/src/utils/hardware/hardware.ts +7 -9
  486. package/src/utils/miniappDiagnostics.ts +226 -0
  487. package/src/utils/permissions/MediaLibraryPermissions.ts +35 -4
  488. package/src/utils/timers/timers.ts +57 -21
  489. package/build/devtools.d.ts +0 -11
  490. package/build/devtools.d.ts.map +0 -1
  491. package/build/devtools.js +0 -13
  492. package/build/devtools.js.map +0 -1
  493. package/build/internal.d.ts +0 -66
  494. package/build/internal.d.ts.map +0 -1
  495. package/build/internal.js +0 -89
  496. package/build/internal.js.map +0 -1
  497. package/build/services/asgOtaVersionUrl.d.ts +0 -2
  498. package/build/services/asgOtaVersionUrl.d.ts.map +0 -1
  499. package/build/services/asgOtaVersionUrl.js +0 -36
  500. package/build/services/asgOtaVersionUrl.js.map +0 -1
  501. package/src/devtools.ts +0 -13
  502. package/src/internal.ts +0 -158
  503. package/src/services/asgOtaVersionUrl.ts +0 -41
@@ -15,11 +15,7 @@ import type {OtaStatus} from "@mentra/bluetooth-sdk/internal"
15
15
  import GlobalEventEmitter from "../utils/GlobalEventEmitter"
16
16
  import {useGlassesStore} from "../stores/glasses"
17
17
  import {handleOtaClockSkewFromGlasses} from "./glassesClockSync"
18
- import {
19
- legacyOtaProgressFromOtaStatusEvent,
20
- normalizeOtaStatusEvent,
21
- otaStatusFromNormalized,
22
- } from "./otaLegacyMapping"
18
+ import {legacyOtaProgressFromOtaStatusEvent, normalizeOtaStatusEvent, otaStatusFromNormalized} from "./otaLegacyMapping"
23
19
 
24
20
  let subs: Array<{remove: () => void}> = []
25
21
 
@@ -40,6 +36,16 @@ export function startOtaService(): void {
40
36
  }),
41
37
  )
42
38
 
39
+ // The glasses process restarted under a live BLE link (sid changed in
40
+ // glasses_ready/version_info_1). The physical connection never dropped, so this is
41
+ // the ONLY reconnect-edge signal the OTA coordinator gets after an APK install.
42
+ subs.push(
43
+ BluetoothSdk.addListener("glasses_session_changed", (event) => {
44
+ console.log(`[OTA] glasses_session_changed ${event.previous_sid || "<none>"} -> ${event.sid}`)
45
+ GlobalEventEmitter.emit("glasses_session_changed", {previousSid: event.previous_sid, sid: event.sid})
46
+ }),
47
+ )
48
+
43
49
  // Install progress / lifecycle. Updates both the new (otaStatus) and the legacy
44
50
  // (otaProgress) store shapes; auto-fixes clock skew on the relevant failures.
45
51
  subs.push(
@@ -1,8 +1,18 @@
1
1
  import BluetoothSdk from "@mentra/bluetooth-sdk/internal"
2
- import type {OtaUpdateInfo} from "@mentra/bluetooth-sdk/internal"
2
+ import type {OtaUpdateInfo} from "@mentra/bluetooth-sdk"
3
3
  import {getGlassesSystemTimeMs, isGlassesConnected, useGlassesStore, waitForGlassesState} from "../stores/glasses"
4
4
  import {maybeFixGlassesClockFromVersionInfo} from "./glassesClockSync"
5
- import {getAsgOtaVersionUrl} from "./asgOtaVersionUrl"
5
+ import {hasConfiguredModernOtaManifestPin, resolveOtaManifestUrl} from "./otaManifestUrl"
6
+
7
+ /**
8
+ * Phone-side mirror of ASG's `DOWNGRADE_FLOOR_VERSION_CODE`. Set to the versionCode of the first
9
+ * release shipping the shared media root (the first downgrade-safe build). A non-positive value
10
+ * means downgrades are disabled, so the phone must NOT offer them — offering a downgrade the
11
+ * glasses' `DowngradeGate` will refuse leaves the version-change session with no install and the
12
+ * glasses reporting a bare "complete", which would otherwise present as false success. Keep this
13
+ * in lockstep with the ASG/recovery constant at release time.
14
+ */
15
+ export const DOWNGRADE_FLOOR_VERSION_CODE = 0
6
16
 
7
17
  export interface VersionInfo {
8
18
  versionCode: number
@@ -18,11 +28,13 @@ export interface MtkPatch {
18
28
  start_firmware: string
19
29
  end_firmware: string
20
30
  url: string
31
+ sha256?: string
21
32
  }
22
33
 
23
34
  export interface BesFirmware {
24
35
  version: string
25
36
  url: string
37
+ sha256?: string
26
38
  }
27
39
 
28
40
  export interface VersionJson {
@@ -46,9 +58,23 @@ export interface OtaCheckResult {
46
58
  updates: string[]
47
59
  mtkPatch: MtkPatch | null
48
60
  besVersion: string | null
61
+ /** True when the pending APK step installs an OLDER build than the glasses currently run. */
62
+ isApkDowngrade: boolean
63
+ /**
64
+ * Raw manifest JSON (stringified) this check actually fetched, or null when the
65
+ * fetch failed. Lets change-watchers baseline on exactly what the check saw,
66
+ * instead of racing it with a second fetch of the same URL.
67
+ */
68
+ manifestBody: string | null
69
+ /**
70
+ * Why the check failed, when it did. "network" is retryable (connection/server
71
+ * trouble); "pin_unavailable" is permanent for this app build (manifest 404/gone,
72
+ * unparseable, or carrying no valid ASG pin) — the remedy is updating the app.
73
+ */
74
+ checkFailureReason?: "network" | "pin_unavailable"
49
75
  }
50
76
 
51
- export type OtaCheckSkippedReason = "disconnected" | "missing_build"
77
+ export type OtaCheckSkippedReason = "disconnected" | "missing_build" | "dev_build"
52
78
 
53
79
  export interface OtaCheckCurrentGlassesResult extends OtaCheckResult {
54
80
  updateInfo: OtaUpdateInfo | null
@@ -66,6 +92,8 @@ export interface OtaCheckCurrentGlassesOptions {
66
92
  waitForMtkVersionMs?: number
67
93
  refreshVersionInfo?: boolean
68
94
  fixClockBeforeCheck?: boolean
95
+ /** Downgrade floor override (defaults to DOWNGRADE_FLOOR_VERSION_CODE); for tests. */
96
+ floorVersionCode?: number
69
97
  }
70
98
 
71
99
  function emptyCheckResult(skippedReason?: OtaCheckSkippedReason): OtaCheckCurrentGlassesResult {
@@ -76,6 +104,8 @@ function emptyCheckResult(skippedReason?: OtaCheckSkippedReason): OtaCheckCurren
76
104
  updates: [],
77
105
  mtkPatch: null,
78
106
  besVersion: null,
107
+ isApkDowngrade: false,
108
+ manifestBody: null,
79
109
  updateInfo: null,
80
110
  isRequired: true,
81
111
  skippedReason,
@@ -86,46 +116,90 @@ function glassesConnectedNow(): boolean {
86
116
  return isGlassesConnected(useGlassesStore.getState().connection)
87
117
  }
88
118
 
89
- export async function fetchVersionInfo(url: string): Promise<VersionJson | null> {
119
+ type ManifestFetchResult = {json: VersionJson | null; failureReason?: "network" | "pin_unavailable"}
120
+
121
+ async function fetchVersionInfoDetailed(url: string): Promise<ManifestFetchResult> {
90
122
  try {
91
123
  const response = await fetch(url)
92
124
  if (!response.ok) {
93
125
  console.error("Failed to fetch version info:", response.status)
94
- return null
126
+ // 4xx: the manifest does not exist (or is gone) — permanent for this app
127
+ // build, retrying cannot help. Everything else is transient server/network
128
+ // trouble and retryable.
129
+ return {
130
+ json: null,
131
+ failureReason: response.status >= 400 && response.status < 500 ? "pin_unavailable" : "network",
132
+ }
133
+ }
134
+ try {
135
+ return {json: await response.json()}
136
+ } catch (parseError) {
137
+ console.error("OTA: manifest is not valid JSON:", parseError)
138
+ return {json: null, failureReason: "pin_unavailable"}
95
139
  }
96
- return await response.json()
97
140
  } catch (error) {
98
141
  console.error("OTA: Error fetching version info:", error)
99
- return null
142
+ return {json: null, failureReason: "network"}
100
143
  }
101
144
  }
102
145
 
146
+ export async function fetchVersionInfo(url: string): Promise<VersionJson | null> {
147
+ return (await fetchVersionInfoDetailed(url)).json
148
+ }
149
+
103
150
  export function checkVersionUpdateAvailable(
104
151
  currentBuildNumber: string | undefined,
105
152
  versionJson: VersionJson | null,
153
+ floorVersionCode: number = DOWNGRADE_FLOOR_VERSION_CODE,
106
154
  ): boolean {
155
+ return getApkUpdateDirection(currentBuildNumber, versionJson, floorVersionCode) !== null
156
+ }
157
+
158
+ /**
159
+ * Direction of the pending APK change, or null when the glasses already match the manifest.
160
+ * Every apps-shaped manifest the phone drives is an exact pin, so both directions are
161
+ * actionable (mirroring the glasses-side `DowngradeGate`); the ancient top-level manifest
162
+ * shape stays upgrade-only, and zeroed rescue pins are never actionable.
163
+ */
164
+ export function getApkUpdateDirection(
165
+ currentBuildNumber: string | undefined,
166
+ versionJson: VersionJson | null,
167
+ floorVersionCode: number = DOWNGRADE_FLOOR_VERSION_CODE,
168
+ ): "upgrade" | "downgrade" | null {
107
169
  if (!currentBuildNumber || !versionJson) {
108
- return false
170
+ return null
109
171
  }
110
172
 
111
173
  const currentVersion = parseInt(currentBuildNumber, 10)
112
174
  if (isNaN(currentVersion)) {
113
- return false
175
+ return null
114
176
  }
115
177
 
116
178
  let serverVersion: number | undefined
179
+ let exactPin = false
117
180
 
118
- if (versionJson.apps?.["com.mentra.asg_client"]) {
119
- serverVersion = versionJson.apps["com.mentra.asg_client"].versionCode
181
+ const appEntry = versionJson.apps?.["com.mentra.asg_client"]
182
+ if (appEntry) {
183
+ serverVersion = appEntry.versionCode
184
+ exactPin = true
120
185
  } else if (versionJson.versionCode) {
121
186
  serverVersion = versionJson.versionCode
122
187
  }
123
188
 
124
189
  if (!serverVersion || isNaN(serverVersion)) {
125
- return false
190
+ return null
126
191
  }
127
192
 
128
- return serverVersion > currentVersion
193
+ if (serverVersion > currentVersion) {
194
+ return "upgrade"
195
+ }
196
+ // Downgrades require an exact pin AND a target at/above the enabled floor. A non-positive floor
197
+ // disables downgrades entirely, matching ASG's fail-closed DowngradeGate — so the phone never
198
+ // offers what the glasses would refuse.
199
+ if (serverVersion < currentVersion && exactPin && floorVersionCode > 0 && serverVersion >= floorVersionCode) {
200
+ return "downgrade"
201
+ }
202
+ return null
129
203
  }
130
204
 
131
205
  export function getLatestVersionInfo(versionJson: VersionJson | null): VersionInfo | null {
@@ -184,7 +258,7 @@ export function checkBesUpdate(besFirmware: BesFirmware | undefined, currentVers
184
258
  return compareVersions(besFirmware.version, currentVersion) > 0
185
259
  }
186
260
 
187
- function compareVersions(version1: string, version2: string): number {
261
+ export function compareVersions(version1: string, version2: string): number {
188
262
  if (version1.includes(".") && version2.includes(".")) {
189
263
  const parts1 = version1.split(".")
190
264
  const parts2 = version2.split(".")
@@ -208,14 +282,62 @@ export async function checkForOtaUpdate(
208
282
  currentBuildNumber: string,
209
283
  currentMtkVersion?: string,
210
284
  currentBesVersion?: string,
285
+ floorVersionCode: number = DOWNGRADE_FLOOR_VERSION_CODE,
211
286
  ): Promise<OtaCheckResult> {
212
287
  try {
213
288
  console.log("OTA: Checking for OTA update - URL: " + otaVersionUrl + ", current build: " + currentBuildNumber)
214
- const versionJson = await fetchVersionInfo(otaVersionUrl)
289
+ const fetched = await fetchVersionInfoDetailed(otaVersionUrl)
290
+ const versionJson = fetched.json
291
+ if (!versionJson) {
292
+ return {
293
+ hasCheckCompleted: false,
294
+ updateAvailable: false,
295
+ latestVersionInfo: null,
296
+ updates: [],
297
+ mtkPatch: null,
298
+ besVersion: null,
299
+ isApkDowngrade: false,
300
+ manifestBody: null,
301
+ checkFailureReason: fetched.failureReason ?? "network",
302
+ }
303
+ }
215
304
  const latestVersionInfo = getLatestVersionInfo(versionJson)
216
305
 
217
- const apkUpdateAvailable = checkVersionUpdateAvailable(currentBuildNumber, versionJson)
218
- console.log(`OTA: APK update available: ${apkUpdateAvailable} (current: ${currentBuildNumber})`)
306
+ // A manifest whose ASG pin is missing/zeroed cannot verify anything for modern
307
+ // glasses (zeroed pins are only legitimate in the frozen legacy rescue manifests
308
+ // that pre-39 glasses are checked against). Report the check as FAILED rather
309
+ // than silently completing as "up to date" — an unverifiable state must never
310
+ // present as a verified one.
311
+ const currentVersionNumber = parseInt(currentBuildNumber, 10)
312
+ const pinnedEntry = versionJson?.apps?.["com.mentra.asg_client"]
313
+ const pinnedVersion = pinnedEntry?.versionCode ?? versionJson?.versionCode
314
+ if (
315
+ versionJson &&
316
+ Number.isFinite(currentVersionNumber) &&
317
+ currentVersionNumber >= 39 &&
318
+ !(typeof pinnedVersion === "number" && pinnedVersion > 0)
319
+ ) {
320
+ console.error(
321
+ `OTA: manifest at ${otaVersionUrl} has no valid ASG pin (versionCode=${String(pinnedVersion)}) - treating check as failed`,
322
+ )
323
+ return {
324
+ hasCheckCompleted: false,
325
+ updateAvailable: false,
326
+ latestVersionInfo: null,
327
+ updates: [],
328
+ mtkPatch: null,
329
+ besVersion: null,
330
+ isApkDowngrade: false,
331
+ manifestBody: null,
332
+ checkFailureReason: "pin_unavailable",
333
+ }
334
+ }
335
+
336
+ const apkDirection = getApkUpdateDirection(currentBuildNumber, versionJson, floorVersionCode)
337
+ const apkUpdateAvailable = apkDirection !== null
338
+ console.log(
339
+ `OTA: APK update available: ${apkUpdateAvailable} (current: ${currentBuildNumber}, direction: ${apkDirection ?? "none"})`,
340
+ )
219
341
 
220
342
  const mtkPatch = findMatchingMtkPatch(versionJson?.mtk_patches, currentMtkVersion)
221
343
  const mtkUpdateAvailable = mtkPatch !== null
@@ -243,6 +365,8 @@ export async function checkForOtaUpdate(
243
365
  updates,
244
366
  mtkPatch,
245
367
  besVersion: versionJson?.bes_firmware?.version || null,
368
+ isApkDowngrade: apkDirection === "downgrade",
369
+ manifestBody: versionJson ? JSON.stringify(versionJson) : null,
246
370
  }
247
371
  } catch (error) {
248
372
  console.error("Error checking for OTA update:", error)
@@ -253,6 +377,9 @@ export async function checkForOtaUpdate(
253
377
  updates: [],
254
378
  mtkPatch: null,
255
379
  besVersion: null,
380
+ isApkDowngrade: false,
381
+ manifestBody: null,
382
+ checkFailureReason: "network",
256
383
  }
257
384
  }
258
385
  }
@@ -266,12 +393,18 @@ export async function checkCurrentGlassesForUpdate(
266
393
  waitForMtkVersionMs = 2000,
267
394
  refreshVersionInfo = true,
268
395
  fixClockBeforeCheck = true,
396
+ floorVersionCode = DOWNGRADE_FLOOR_VERSION_CODE,
269
397
  } = options
270
398
 
271
399
  if (!glassesConnectedNow()) {
272
400
  return emptyCheckResult("disconnected")
273
401
  }
274
402
 
403
+ if (!hasConfiguredModernOtaManifestPin()) {
404
+ console.log("OTA: check skipped - this app or Engine package has no embedded OTA manifest pin")
405
+ return emptyCheckResult("dev_build")
406
+ }
407
+
275
408
  if (refreshVersionInfo) {
276
409
  void BluetoothSdk.requestVersionInfo().catch((error) => {
277
410
  console.warn("OTA: Failed to request version_info from glasses:", error)
@@ -284,7 +417,10 @@ export async function checkCurrentGlassesForUpdate(
284
417
  buildNumber = useGlassesStore.getState().buildNumber
285
418
  }
286
419
 
287
- if (!buildNumber) {
420
+ if (!buildNumber || !(parseInt(buildNumber, 10) > 0)) {
421
+ // Covers absent, non-numeric, and zero build numbers: a glasses version we
422
+ // cannot parse means nothing is verifiable, so skip rather than ghost an
423
+ // "up to date" result from a comparison that never really ran.
288
424
  return emptyCheckResult("missing_build")
289
425
  }
290
426
 
@@ -325,8 +461,18 @@ export async function checkCurrentGlassesForUpdate(
325
461
  })
326
462
  }
327
463
 
328
- const manifestUrl = getAsgOtaVersionUrl(useGlassesStore.getState().otaVersionUrl, buildNumber)
329
- const result = await checkForOtaUpdate(manifestUrl, buildNumber, mtkFirmwareVersion, besFirmwareVersion)
464
+ const manifestUrl = resolveOtaManifestUrl(useGlassesStore.getState().otaVersionUrl, buildNumber)
465
+ if (!manifestUrl) {
466
+ console.log("OTA: check skipped - no OTA manifest pin resolved")
467
+ return emptyCheckResult("dev_build")
468
+ }
469
+ const result = await checkForOtaUpdate(
470
+ manifestUrl,
471
+ buildNumber,
472
+ mtkFirmwareVersion,
473
+ besFirmwareVersion,
474
+ floorVersionCode,
475
+ )
330
476
 
331
477
  if (!result.hasCheckCompleted) {
332
478
  return {
@@ -360,6 +506,7 @@ export async function checkCurrentGlassesForUpdate(
360
506
  )
361
507
  }
362
508
  const updateAvailable = filteredUpdates.length > 0 && !!result.latestVersionInfo
509
+ const isApkDowngrade = result.isApkDowngrade && filteredUpdates.includes("apk")
363
510
  const updateInfo = updateAvailable
364
511
  ? {
365
512
  available: true,
@@ -367,6 +514,8 @@ export async function checkCurrentGlassesForUpdate(
367
514
  versionName: result.latestVersionInfo?.versionName || "",
368
515
  updates: filteredUpdates,
369
516
  totalSize: 0,
517
+ ...(filteredUpdates.includes("bes") && result.besVersion ? {besVersion: result.besVersion} : {}),
518
+ isDowngrade: isApkDowngrade,
370
519
  }
371
520
  : null
372
521
 
@@ -376,8 +525,19 @@ export async function checkCurrentGlassesForUpdate(
376
525
  ...result,
377
526
  updateAvailable,
378
527
  updates: filteredUpdates,
528
+ isApkDowngrade,
379
529
  updateInfo,
380
- isRequired: result.latestVersionInfo?.isRequired !== false,
530
+ // Required-update policy: an EXPLICIT isRequired in the manifest is the
531
+ // release-engineering escape hatch and wins in both directions. When absent,
532
+ // upgrades keep the historical forced default, but downgrades are always
533
+ // skippable — a version change that resets glasses settings must be
534
+ // declinable unless someone explicitly forced it.
535
+ isRequired:
536
+ result.latestVersionInfo?.isRequired === true
537
+ ? true
538
+ : result.latestVersionInfo?.isRequired === false
539
+ ? false
540
+ : !isApkDowngrade,
381
541
  manifestUrl,
382
542
  buildNumber,
383
543
  mtkFirmwareVersion,
@@ -0,0 +1,212 @@
1
+ import {MiniappErrorCode, type CalendarEvent} from "@mentra/miniapp"
2
+
3
+ const DEFAULT_LIMIT = 50
4
+ const MAX_LIMIT = 100
5
+ const MAX_RANGE_MS = 31 * 24 * 60 * 60 * 1000
6
+
7
+ export interface CalendarListRequest {
8
+ startsAt?: unknown
9
+ endsAt?: unknown
10
+ limit?: unknown
11
+ }
12
+
13
+ export class PhoneCalendarError extends Error {
14
+ constructor(
15
+ public readonly code: MiniappErrorCode,
16
+ message: string,
17
+ ) {
18
+ super(message)
19
+ this.name = "PhoneCalendarError"
20
+ }
21
+ }
22
+
23
+ export interface ValidatedCalendarListRequest {
24
+ startsAt: Date
25
+ endsAt: Date
26
+ limit: number
27
+ }
28
+
29
+ export interface RawCalendarEvent {
30
+ id?: string
31
+ calendarId?: string
32
+ title?: string | null
33
+ startDate: string | Date
34
+ endDate: string | Date
35
+ timeZone?: string | null
36
+ allDay?: boolean
37
+ location?: string | null
38
+ notes?: string | null
39
+ url?: string | null
40
+ }
41
+
42
+ export function validateCalendarListRequest(request: CalendarListRequest): ValidatedCalendarListRequest {
43
+ const startsAt = parseDate(request.startsAt, "startsAt")
44
+ const endsAt = parseDate(request.endsAt, "endsAt")
45
+ if (endsAt.getTime() <= startsAt.getTime()) {
46
+ throw new PhoneCalendarError(MiniappErrorCode.INVALID_ARGUMENT, "endsAt must be after startsAt")
47
+ }
48
+ if (endsAt.getTime() - startsAt.getTime() > MAX_RANGE_MS) {
49
+ throw new PhoneCalendarError(MiniappErrorCode.INVALID_ARGUMENT, "Calendar query range may not exceed 31 days")
50
+ }
51
+
52
+ const limit = request.limit === undefined ? DEFAULT_LIMIT : request.limit
53
+ if (typeof limit !== "number" || !Number.isInteger(limit) || limit < 1 || limit > MAX_LIMIT) {
54
+ throw new PhoneCalendarError(
55
+ MiniappErrorCode.INVALID_ARGUMENT,
56
+ `limit must be an integer between 1 and ${MAX_LIMIT}`,
57
+ )
58
+ }
59
+ return {startsAt, endsAt, limit}
60
+ }
61
+
62
+ function parseDate(value: unknown, field: string): Date {
63
+ if (typeof value !== "string") {
64
+ throw new PhoneCalendarError(MiniappErrorCode.INVALID_ARGUMENT, `${field} must be an ISO 8601 string`)
65
+ }
66
+ const date = new Date(value)
67
+ if (Number.isNaN(date.getTime())) {
68
+ throw new PhoneCalendarError(MiniappErrorCode.INVALID_ARGUMENT, `${field} must be a valid ISO 8601 string`)
69
+ }
70
+ return date
71
+ }
72
+
73
+ export function extractHttpsLinks(...values: Array<string | null | undefined>): string[] {
74
+ const links = new Set<string>()
75
+ for (const value of values) {
76
+ for (const match of value?.match(/https:\/\/[^\s<>"']+/gi) ?? []) {
77
+ let link = match
78
+ while (".,;:!?)]}".includes(link[link.length - 1] ?? "")) link = link.slice(0, -1)
79
+ if (link) links.add(link)
80
+ }
81
+ }
82
+ return [...links]
83
+ }
84
+
85
+ interface CalendarDateParts {
86
+ year: number
87
+ month: number
88
+ day: number
89
+ hour: number
90
+ minute: number
91
+ second: number
92
+ }
93
+
94
+ function localDateParts(date: Date): CalendarDateParts {
95
+ return {
96
+ year: date.getFullYear(),
97
+ month: date.getMonth() + 1,
98
+ day: date.getDate(),
99
+ hour: date.getHours(),
100
+ minute: date.getMinutes(),
101
+ second: date.getSeconds(),
102
+ }
103
+ }
104
+
105
+ function datePartsInTimeZone(date: Date, timeZone: string): CalendarDateParts | undefined {
106
+ try {
107
+ const parts = new Intl.DateTimeFormat("en-US", {
108
+ timeZone,
109
+ year: "numeric",
110
+ month: "2-digit",
111
+ day: "2-digit",
112
+ hour: "2-digit",
113
+ minute: "2-digit",
114
+ second: "2-digit",
115
+ hourCycle: "h23",
116
+ }).formatToParts(date)
117
+ const value = (type: Intl.DateTimeFormatPartTypes) => Number(parts.find((part) => part.type === type)?.value)
118
+ return {
119
+ year: value("year"),
120
+ month: value("month"),
121
+ day: value("day"),
122
+ hour: value("hour"),
123
+ minute: value("minute"),
124
+ second: value("second"),
125
+ }
126
+ } catch {
127
+ return undefined
128
+ }
129
+ }
130
+
131
+ function timeZoneOffsetMinutes(date: Date, timeZone: string): number | undefined {
132
+ const parts = datePartsInTimeZone(date, timeZone)
133
+ if (!parts) return undefined
134
+ const representedAsUtc = Date.UTC(
135
+ parts.year,
136
+ parts.month - 1,
137
+ parts.day,
138
+ parts.hour,
139
+ parts.minute,
140
+ parts.second,
141
+ date.getUTCMilliseconds(),
142
+ )
143
+ return Math.round((representedAsUtc - date.getTime()) / 60_000)
144
+ }
145
+
146
+ function pad(value: number, length = 2): string {
147
+ return String(value).padStart(length, "0")
148
+ }
149
+
150
+ function formatOffset(offsetMinutes: number): string {
151
+ if (offsetMinutes === 0) return "Z"
152
+ const sign = offsetMinutes < 0 ? "-" : "+"
153
+ const absolute = Math.abs(offsetMinutes)
154
+ return `${sign}${pad(Math.floor(absolute / 60))}:${pad(absolute % 60)}`
155
+ }
156
+
157
+ function formatAllDayDate(value: string | Date, timeZone?: string | null): string {
158
+ const dateOnly = typeof value === "string" ? /^(\d{4})-(\d{2})-(\d{2})$/.exec(value) : null
159
+ if (dateOnly) {
160
+ const [, year, month, day] = dateOnly
161
+ const localMidnight = new Date(Number(year), Number(month) - 1, Number(day))
162
+ let offsetMinutes = -localMidnight.getTimezoneOffset()
163
+ if (timeZone) {
164
+ // Resolve the offset at midnight in the event timezone. Repeating once
165
+ // accounts for the initial UTC guess landing across a DST boundary.
166
+ const wallTime = Date.UTC(Number(year), Number(month) - 1, Number(day))
167
+ let instant = new Date(wallTime)
168
+ for (let attempt = 0; attempt < 2; attempt += 1) {
169
+ const offset = timeZoneOffsetMinutes(instant, timeZone)
170
+ if (offset === undefined) break
171
+ offsetMinutes = offset
172
+ instant = new Date(wallTime - offset * 60_000)
173
+ }
174
+ }
175
+ return `${year}-${month}-${day}T00:00:00.000${formatOffset(offsetMinutes)}`
176
+ }
177
+
178
+ const date = new Date(value)
179
+ const zonedParts = timeZone ? datePartsInTimeZone(date, timeZone) : undefined
180
+ const parts = zonedParts ?? localDateParts(date)
181
+ const offsetMinutes =
182
+ timeZone && zonedParts
183
+ ? (timeZoneOffsetMinutes(date, timeZone) ?? -date.getTimezoneOffset())
184
+ : -date.getTimezoneOffset()
185
+ return `${pad(parts.year, 4)}-${pad(parts.month)}-${pad(parts.day)}T${pad(parts.hour)}:${pad(parts.minute)}:${pad(parts.second)}.${pad(date.getUTCMilliseconds(), 3)}${formatOffset(offsetMinutes)}`
186
+ }
187
+
188
+ export function normalizeCalendarEvent(event: RawCalendarEvent): CalendarEvent {
189
+ const startsAt = event.allDay
190
+ ? formatAllDayDate(event.startDate, event.timeZone)
191
+ : new Date(event.startDate).toISOString()
192
+ const endsAt = event.allDay ? formatAllDayDate(event.endDate, event.timeZone) : new Date(event.endDate).toISOString()
193
+ const calendarId = event.calendarId ?? ""
194
+ const eventId = event.id ?? ""
195
+ return {
196
+ id: `${calendarId}:${eventId}:${startsAt}`,
197
+ calendarId,
198
+ title: event.title ?? "",
199
+ startsAt,
200
+ endsAt,
201
+ ...(event.timeZone ? {timezone: event.timeZone} : {}),
202
+ allDay: !!event.allDay,
203
+ ...(event.location ? {location: event.location} : {}),
204
+ ...(event.notes ? {notes: event.notes} : {}),
205
+ ...(event.url ? {url: event.url} : {}),
206
+ links: extractHttpsLinks(event.url, event.location, event.notes),
207
+ }
208
+ }
209
+
210
+ export function calendarEventOverlapsWindow(event: CalendarEvent, startsAt: Date, endsAt: Date): boolean {
211
+ return Date.parse(event.endsAt) > startsAt.getTime() && Date.parse(event.startsAt) < endsAt.getTime()
212
+ }
@@ -0,0 +1,65 @@
1
+ import * as Calendar from "expo-calendar"
2
+ import {Platform} from "react-native"
3
+ import {check, PERMISSIONS, RESULTS} from "react-native-permissions"
4
+
5
+ import {MiniappErrorCode, type CalendarListResult} from "@mentra/miniapp"
6
+
7
+ import {
8
+ calendarEventOverlapsWindow,
9
+ type CalendarListRequest,
10
+ normalizeCalendarEvent,
11
+ PhoneCalendarError,
12
+ validateCalendarListRequest,
13
+ } from "./PhoneCalendarHelpers"
14
+
15
+ export {PhoneCalendarError} from "./PhoneCalendarHelpers"
16
+
17
+ type CalendarPermissionStatus = Awaited<
18
+ ReturnType<typeof Calendar.getCalendarPermissionsAsync>
19
+ >["status"]
20
+
21
+ /**
22
+ * expo-calendar reports anything below EventKit full access as not granted,
23
+ * so distinguish the states the user can actually fix. "Reopen the miniapp"
24
+ * is only true while the permission is undetermined — once iOS has resolved
25
+ * it (denied or write-only/limited), only Settings can change it.
26
+ */
27
+ async function calendarPermissionMessage(status: CalendarPermissionStatus): Promise<string> {
28
+ if (status === "undetermined") {
29
+ return "Calendar permission has not been granted yet. Reopen the miniapp and allow calendar access."
30
+ }
31
+ if (Platform.OS === "ios") {
32
+ const writeOnly = await check(PERMISSIONS.IOS.CALENDARS_WRITE_ONLY).catch(() => null)
33
+ if (writeOnly === RESULTS.GRANTED || writeOnly === RESULTS.LIMITED) {
34
+ return 'Calendar access is limited to write-only. Enable "Full Access" for Mentra in Settings › Privacy & Security › Calendars.'
35
+ }
36
+ return "Calendar access is denied. Enable it for Mentra in Settings › Privacy & Security › Calendars."
37
+ }
38
+ return "Calendar access is denied. Enable the Calendar permission for Mentra in your phone's app settings."
39
+ }
40
+
41
+ export async function listPhoneCalendarEvents(request: CalendarListRequest): Promise<CalendarListResult> {
42
+ const {startsAt, endsAt, limit} = validateCalendarListRequest(request)
43
+ const permission = await Calendar.getCalendarPermissionsAsync()
44
+ if (permission.status !== "granted") {
45
+ throw new PhoneCalendarError(MiniappErrorCode.PERMISSION_DENIED, await calendarPermissionMessage(permission.status))
46
+ }
47
+
48
+ const calendars = await Calendar.getCalendarsAsync(Calendar.EntityTypes.EVENT)
49
+ if (calendars.length === 0) return {events: [], truncated: false}
50
+
51
+ const rawEvents = await Calendar.getEventsAsync(
52
+ calendars.map((calendar) => calendar.id),
53
+ startsAt,
54
+ endsAt,
55
+ )
56
+ const events = rawEvents
57
+ .map(normalizeCalendarEvent)
58
+ .filter((event) => calendarEventOverlapsWindow(event, startsAt, endsAt))
59
+ .sort((a, b) => Date.parse(a.startsAt) - Date.parse(b.startsAt) || Date.parse(a.endsAt) - Date.parse(b.endsAt))
60
+
61
+ return {
62
+ events: events.slice(0, limit),
63
+ truncated: events.length > limit,
64
+ }
65
+ }