@mentra/engine 0.1.0-beta.0 → 0.1.0-dev.1

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 (355) hide show
  1. package/README.md +42 -1
  2. package/build/engine.d.ts +11 -3
  3. package/build/engine.d.ts.map +1 -1
  4. package/build/engine.js +7 -2
  5. package/build/engine.js.map +1 -1
  6. package/build/facades/dev.d.ts.map +1 -1
  7. package/build/facades/dev.js +9 -5
  8. package/build/facades/dev.js.map +1 -1
  9. package/build/facades/gallery.js +1 -1
  10. package/build/facades/gallery.js.map +1 -1
  11. package/build/facades/glasses.d.ts +1 -0
  12. package/build/facades/glasses.d.ts.map +1 -1
  13. package/build/facades/glasses.js +19 -4
  14. package/build/facades/glasses.js.map +1 -1
  15. package/build/facades/glassesSettings.d.ts.map +1 -1
  16. package/build/facades/glassesSettings.js +1 -0
  17. package/build/facades/glassesSettings.js.map +1 -1
  18. package/build/facades/miniapps.d.ts +4 -0
  19. package/build/facades/miniapps.d.ts.map +1 -1
  20. package/build/facades/miniapps.js +5 -0
  21. package/build/facades/miniapps.js.map +1 -1
  22. package/build/facades/ota.d.ts +11 -2
  23. package/build/facades/ota.d.ts.map +1 -1
  24. package/build/facades/ota.js +19 -4
  25. package/build/facades/ota.js.map +1 -1
  26. package/build/facades/pairing.d.ts +4 -0
  27. package/build/facades/pairing.d.ts.map +1 -1
  28. package/build/facades/pairing.js +10 -8
  29. package/build/facades/pairing.js.map +1 -1
  30. package/build/facades/phoneNotifications.d.ts +0 -4
  31. package/build/facades/phoneNotifications.d.ts.map +1 -1
  32. package/build/facades/phoneNotifications.js +4 -14
  33. package/build/facades/phoneNotifications.js.map +1 -1
  34. package/build/index.d.ts +6 -3
  35. package/build/index.d.ts.map +1 -1
  36. package/build/index.js +4 -2
  37. package/build/index.js.map +1 -1
  38. package/build/internal.d.ts +2 -2
  39. package/build/internal.d.ts.map +1 -1
  40. package/build/internal.js +1 -1
  41. package/build/internal.js.map +1 -1
  42. package/build/react/MentraLiveOtaFlow.d.ts +38 -0
  43. package/build/react/MentraLiveOtaFlow.d.ts.map +1 -0
  44. package/build/react/MentraLiveOtaFlow.js +528 -0
  45. package/build/react/MentraLiveOtaFlow.js.map +1 -0
  46. package/build/react/index.d.ts +2 -0
  47. package/build/react/index.d.ts.map +1 -0
  48. package/build/react/index.js +2 -0
  49. package/build/react/index.js.map +1 -0
  50. package/build/react/useEngineSnapshot.d.ts +2 -0
  51. package/build/react/useEngineSnapshot.d.ts.map +1 -0
  52. package/build/react/useEngineSnapshot.js +14 -0
  53. package/build/react/useEngineSnapshot.js.map +1 -0
  54. package/build/runtime/bootstrap.d.ts +24 -1
  55. package/build/runtime/bootstrap.d.ts.map +1 -1
  56. package/build/runtime/bootstrap.js +17 -0
  57. package/build/runtime/bootstrap.js.map +1 -1
  58. package/build/runtime/scanQrSeam.d.ts +7 -0
  59. package/build/runtime/scanQrSeam.d.ts.map +1 -0
  60. package/build/runtime/scanQrSeam.js +16 -0
  61. package/build/runtime/scanQrSeam.js.map +1 -0
  62. package/build/services/AppRegistry.d.ts +18 -8
  63. package/build/services/AppRegistry.d.ts.map +1 -1
  64. package/build/services/AppRegistry.js +44 -45
  65. package/build/services/AppRegistry.js.map +1 -1
  66. package/build/services/AudioCloudUplink.d.ts +9 -3
  67. package/build/services/AudioCloudUplink.d.ts.map +1 -1
  68. package/build/services/AudioCloudUplink.js +9 -3
  69. package/build/services/AudioCloudUplink.js.map +1 -1
  70. package/build/services/AudioPlaybackService.d.ts +9 -1
  71. package/build/services/AudioPlaybackService.d.ts.map +1 -1
  72. package/build/services/AudioPlaybackService.js +77 -32
  73. package/build/services/AudioPlaybackService.js.map +1 -1
  74. package/build/services/BlobStore.d.ts +2 -1
  75. package/build/services/BlobStore.d.ts.map +1 -1
  76. package/build/services/BlobStore.js +17 -9
  77. package/build/services/BlobStore.js.map +1 -1
  78. package/build/services/CloudAudioSubscriptionSync.d.ts.map +1 -1
  79. package/build/services/CloudAudioSubscriptionSync.js +13 -1
  80. package/build/services/CloudAudioSubscriptionSync.js.map +1 -1
  81. package/build/services/CloudClientService.d.ts +15 -0
  82. package/build/services/CloudClientService.d.ts.map +1 -1
  83. package/build/services/CloudClientService.js +75 -0
  84. package/build/services/CloudClientService.js.map +1 -1
  85. package/build/services/ForegroundLocationPermission.d.ts +13 -0
  86. package/build/services/ForegroundLocationPermission.d.ts.map +1 -0
  87. package/build/services/ForegroundLocationPermission.js +12 -0
  88. package/build/services/ForegroundLocationPermission.js.map +1 -0
  89. package/build/services/GlassesSettingsSync.d.ts.map +1 -1
  90. package/build/services/GlassesSettingsSync.js +9 -3
  91. package/build/services/GlassesSettingsSync.js.map +1 -1
  92. package/build/services/GlassesStatusProjection.d.ts +2 -1
  93. package/build/services/GlassesStatusProjection.d.ts.map +1 -1
  94. package/build/services/GlassesStatusProjection.js +15 -8
  95. package/build/services/GlassesStatusProjection.js.map +1 -1
  96. package/build/services/HotspotOtaTransport.d.ts +29 -0
  97. package/build/services/HotspotOtaTransport.d.ts.map +1 -0
  98. package/build/services/HotspotOtaTransport.js +146 -0
  99. package/build/services/HotspotOtaTransport.js.map +1 -0
  100. package/build/services/HotspotShutdown.d.ts +17 -0
  101. package/build/services/HotspotShutdown.d.ts.map +1 -0
  102. package/build/services/HotspotShutdown.js +29 -0
  103. package/build/services/HotspotShutdown.js.map +1 -0
  104. package/build/services/LocalDisplayManager.d.ts +15 -0
  105. package/build/services/LocalDisplayManager.d.ts.map +1 -1
  106. package/build/services/LocalDisplayManager.js +106 -20
  107. package/build/services/LocalDisplayManager.js.map +1 -1
  108. package/build/services/LocalMiniappRuntime.d.ts +67 -1
  109. package/build/services/LocalMiniappRuntime.d.ts.map +1 -1
  110. package/build/services/LocalMiniappRuntime.js +486 -126
  111. package/build/services/LocalMiniappRuntime.js.map +1 -1
  112. package/build/services/LocalMiniappStorage.d.ts +32 -0
  113. package/build/services/LocalMiniappStorage.d.ts.map +1 -0
  114. package/build/services/LocalMiniappStorage.js +66 -0
  115. package/build/services/LocalMiniappStorage.js.map +1 -0
  116. package/build/services/LocalMiniappUserIdentity.d.ts +20 -0
  117. package/build/services/LocalMiniappUserIdentity.d.ts.map +1 -0
  118. package/build/services/LocalMiniappUserIdentity.js +43 -0
  119. package/build/services/LocalMiniappUserIdentity.js.map +1 -0
  120. package/build/services/MicStateCoordinator.d.ts +42 -1
  121. package/build/services/MicStateCoordinator.d.ts.map +1 -1
  122. package/build/services/MicStateCoordinator.js +141 -3
  123. package/build/services/MicStateCoordinator.js.map +1 -1
  124. package/build/services/MiniappLauncher.d.ts.map +1 -1
  125. package/build/services/MiniappLauncher.js +23 -3
  126. package/build/services/MiniappLauncher.js.map +1 -1
  127. package/build/services/MiniappLiveness.d.ts +12 -0
  128. package/build/services/MiniappLiveness.d.ts.map +1 -0
  129. package/build/services/MiniappLiveness.js +16 -0
  130. package/build/services/MiniappLiveness.js.map +1 -0
  131. package/build/services/OtaArtifactDownloader.d.ts +65 -0
  132. package/build/services/OtaArtifactDownloader.d.ts.map +1 -0
  133. package/build/services/OtaArtifactDownloader.js +226 -0
  134. package/build/services/OtaArtifactDownloader.js.map +1 -0
  135. package/build/services/OtaAutoChain.d.ts +34 -0
  136. package/build/services/OtaAutoChain.d.ts.map +1 -0
  137. package/build/services/OtaAutoChain.js +83 -0
  138. package/build/services/OtaAutoChain.js.map +1 -0
  139. package/build/services/OtaErrorMapping.d.ts +18 -0
  140. package/build/services/OtaErrorMapping.d.ts.map +1 -0
  141. package/build/services/OtaErrorMapping.js +63 -0
  142. package/build/services/OtaErrorMapping.js.map +1 -0
  143. package/build/services/OtaInstallCoordinator.d.ts +94 -14
  144. package/build/services/OtaInstallCoordinator.d.ts.map +1 -1
  145. package/build/services/OtaInstallCoordinator.js +574 -130
  146. package/build/services/OtaInstallCoordinator.js.map +1 -1
  147. package/build/services/OtaService.d.ts.map +1 -1
  148. package/build/services/OtaService.js +8 -1
  149. package/build/services/OtaService.js.map +1 -1
  150. package/build/services/OtaUpdateCheckService.d.ts +38 -3
  151. package/build/services/OtaUpdateCheckService.d.ts.map +1 -1
  152. package/build/services/OtaUpdateCheckService.js +142 -22
  153. package/build/services/OtaUpdateCheckService.js.map +1 -1
  154. package/build/services/PhoneCalendarService.d.ts.map +1 -1
  155. package/build/services/PhoneCalendarService.js +22 -1
  156. package/build/services/PhoneCalendarService.js.map +1 -1
  157. package/build/services/PhoneCameraFovCoordinator.d.ts +2 -0
  158. package/build/services/PhoneCameraFovCoordinator.d.ts.map +1 -1
  159. package/build/services/PhoneCameraFovCoordinator.js +14 -0
  160. package/build/services/PhoneCameraFovCoordinator.js.map +1 -1
  161. package/build/services/PhoneNotificationsSync.d.ts.map +1 -1
  162. package/build/services/PhoneNotificationsSync.js +12 -10
  163. package/build/services/PhoneNotificationsSync.js.map +1 -1
  164. package/build/services/PhonePhotoCoordinator.d.ts +10 -3
  165. package/build/services/PhonePhotoCoordinator.d.ts.map +1 -1
  166. package/build/services/PhonePhotoCoordinator.js +21 -3
  167. package/build/services/PhonePhotoCoordinator.js.map +1 -1
  168. package/build/services/PhoneStreamCoordinator.d.ts +8 -1
  169. package/build/services/PhoneStreamCoordinator.d.ts.map +1 -1
  170. package/build/services/PhoneStreamCoordinator.js +125 -13
  171. package/build/services/PhoneStreamCoordinator.js.map +1 -1
  172. package/build/services/PhoneVideoCoordinator.d.ts +2 -0
  173. package/build/services/PhoneVideoCoordinator.d.ts.map +1 -1
  174. package/build/services/PhoneVideoCoordinator.js +10 -0
  175. package/build/services/PhoneVideoCoordinator.js.map +1 -1
  176. package/build/services/SceneRenderer.d.ts.map +1 -1
  177. package/build/services/SceneRenderer.js +15 -0
  178. package/build/services/SceneRenderer.js.map +1 -1
  179. package/build/services/SupportProfileSync.d.ts +22 -0
  180. package/build/services/SupportProfileSync.d.ts.map +1 -0
  181. package/build/services/SupportProfileSync.js +213 -0
  182. package/build/services/SupportProfileSync.js.map +1 -0
  183. package/build/services/TtsTextSanitizer.d.ts +5 -0
  184. package/build/services/TtsTextSanitizer.d.ts.map +1 -0
  185. package/build/services/TtsTextSanitizer.js +61 -0
  186. package/build/services/TtsTextSanitizer.js.map +1 -0
  187. package/build/services/WifiAdbRequest.d.ts +17 -0
  188. package/build/services/WifiAdbRequest.d.ts.map +1 -0
  189. package/build/services/WifiAdbRequest.js +35 -0
  190. package/build/services/WifiAdbRequest.js.map +1 -0
  191. package/build/services/asg/asgCameraApi.d.ts +3 -0
  192. package/build/services/asg/asgCameraApi.d.ts.map +1 -1
  193. package/build/services/asg/asgCameraApi.js +72 -2
  194. package/build/services/asg/asgCameraApi.js.map +1 -1
  195. package/build/services/asg/gallerySyncService.d.ts.map +1 -1
  196. package/build/services/asg/gallerySyncService.js +26 -14
  197. package/build/services/asg/gallerySyncService.js.map +1 -1
  198. package/build/services/asg/localNetworkTransport.d.ts +2 -2
  199. package/build/services/asg/localNetworkTransport.d.ts.map +1 -1
  200. package/build/services/asg/localNetworkTransport.js +6 -6
  201. package/build/services/asg/localNetworkTransport.js.map +1 -1
  202. package/build/services/audioPlaybackAssets.d.ts +8 -0
  203. package/build/services/audioPlaybackAssets.d.ts.map +1 -0
  204. package/build/services/audioPlaybackAssets.js +7 -0
  205. package/build/services/audioPlaybackAssets.js.map +1 -0
  206. package/build/services/glassesClockSync.js +2 -2
  207. package/build/services/glassesClockSync.js.map +1 -1
  208. package/build/services/manifestActions.d.ts +7 -0
  209. package/build/services/manifestActions.d.ts.map +1 -0
  210. package/build/services/manifestActions.js +25 -0
  211. package/build/services/manifestActions.js.map +1 -0
  212. package/build/services/otaDisplayState.d.ts +22 -11
  213. package/build/services/otaDisplayState.d.ts.map +1 -1
  214. package/build/services/otaDisplayState.js +36 -14
  215. package/build/services/otaDisplayState.js.map +1 -1
  216. package/build/services/otaInstallPolicy.d.ts +15 -3
  217. package/build/services/otaInstallPolicy.d.ts.map +1 -1
  218. package/build/services/otaInstallPolicy.js +24 -3
  219. package/build/services/otaInstallPolicy.js.map +1 -1
  220. package/build/services/otaManifestUrl.d.ts +2 -0
  221. package/build/services/otaManifestUrl.d.ts.map +1 -0
  222. package/build/services/{asgOtaVersionUrl.js → otaManifestUrl.js} +15 -5
  223. package/build/services/otaManifestUrl.js.map +1 -0
  224. package/build/services/slimStreamStatus.d.ts +11 -0
  225. package/build/services/slimStreamStatus.d.ts.map +1 -1
  226. package/build/services/slimStreamStatus.js +14 -4
  227. package/build/services/slimStreamStatus.js.map +1 -1
  228. package/build/stores/apps.d.ts.map +1 -1
  229. package/build/stores/apps.js +11 -0
  230. package/build/stores/apps.js.map +1 -1
  231. package/build/stores/bluetoothSettingKeys.d.ts.map +1 -1
  232. package/build/stores/bluetoothSettingKeys.js +2 -0
  233. package/build/stores/bluetoothSettingKeys.js.map +1 -1
  234. package/build/stores/gallerySync.d.ts +2 -0
  235. package/build/stores/gallerySync.d.ts.map +1 -1
  236. package/build/stores/gallerySync.js +2 -0
  237. package/build/stores/gallerySync.js.map +1 -1
  238. package/build/stores/glasses.d.ts.map +1 -1
  239. package/build/stores/glasses.js +3 -0
  240. package/build/stores/glasses.js.map +1 -1
  241. package/build/stores/settings.d.ts +17 -0
  242. package/build/stores/settings.d.ts.map +1 -1
  243. package/build/stores/settings.js +152 -19
  244. package/build/stores/settings.js.map +1 -1
  245. package/build/types/applet.d.ts +2 -0
  246. package/build/types/applet.d.ts.map +1 -1
  247. package/build/types/applet.js.map +1 -1
  248. package/build/types/enums.d.ts +1 -0
  249. package/build/types/enums.d.ts.map +1 -1
  250. package/build/types/enums.js +1 -0
  251. package/build/types/enums.js.map +1 -1
  252. package/build/types/hardware.d.ts +1 -0
  253. package/build/types/hardware.d.ts.map +1 -1
  254. package/build/types/hardware.js +14 -0
  255. package/build/types/hardware.js.map +1 -1
  256. package/build/utils/configuredDevHost.d.ts +6 -0
  257. package/build/utils/configuredDevHost.d.ts.map +1 -0
  258. package/build/utils/configuredDevHost.js +19 -0
  259. package/build/utils/configuredDevHost.js.map +1 -0
  260. package/build/utils/devMiniappLaunch.d.ts +19 -8
  261. package/build/utils/devMiniappLaunch.d.ts.map +1 -1
  262. package/build/utils/devMiniappLaunch.js +144 -21
  263. package/build/utils/devMiniappLaunch.js.map +1 -1
  264. package/build/utils/diagnosticContext.d.ts.map +1 -1
  265. package/build/utils/diagnosticContext.js +15 -17
  266. package/build/utils/diagnosticContext.js.map +1 -1
  267. package/build/utils/hardware/hardware.d.ts.map +1 -1
  268. package/build/utils/hardware/hardware.js +7 -3
  269. package/build/utils/hardware/hardware.js.map +1 -1
  270. package/build/utils/miniappDiagnostics.d.ts +64 -0
  271. package/build/utils/miniappDiagnostics.d.ts.map +1 -0
  272. package/build/utils/miniappDiagnostics.js +173 -0
  273. package/build/utils/miniappDiagnostics.js.map +1 -0
  274. package/build/utils/timers/timers.d.ts.map +1 -1
  275. package/build/utils/timers/timers.js +55 -18
  276. package/build/utils/timers/timers.js.map +1 -1
  277. package/package.json +27 -18
  278. package/src/assets/silence.wav +0 -0
  279. package/src/engine.ts +7 -2
  280. package/src/facades/dev.ts +9 -5
  281. package/src/facades/gallery.ts +1 -1
  282. package/src/facades/glasses.ts +17 -4
  283. package/src/facades/glassesSettings.ts +5 -0
  284. package/src/facades/miniapps.ts +7 -0
  285. package/src/facades/ota.ts +19 -4
  286. package/src/facades/pairing.ts +11 -7
  287. package/src/facades/phoneNotifications.ts +4 -13
  288. package/src/index.ts +26 -2
  289. package/src/internal.ts +2 -1
  290. package/src/react/MentraLiveOtaFlow.tsx +856 -0
  291. package/src/react/index.ts +7 -0
  292. package/src/react/useEngineSnapshot.ts +16 -0
  293. package/src/runtime/bootstrap.ts +33 -1
  294. package/src/runtime/scanQrSeam.ts +20 -0
  295. package/src/services/AppRegistry.ts +72 -48
  296. package/src/services/AudioCloudUplink.ts +9 -3
  297. package/src/services/AudioPlaybackService.ts +94 -34
  298. package/src/services/BlobStore.ts +22 -9
  299. package/src/services/CloudAudioSubscriptionSync.ts +13 -1
  300. package/src/services/CloudClientService.ts +74 -0
  301. package/src/services/ForegroundLocationPermission.ts +24 -0
  302. package/src/services/GlassesSettingsSync.ts +9 -3
  303. package/src/services/GlassesStatusProjection.ts +16 -8
  304. package/src/services/HotspotOtaTransport.ts +184 -0
  305. package/src/services/HotspotShutdown.ts +41 -0
  306. package/src/services/LocalDisplayManager.ts +112 -21
  307. package/src/services/LocalMiniappRuntime.ts +576 -130
  308. package/src/services/LocalMiniappStorage.ts +82 -0
  309. package/src/services/LocalMiniappUserIdentity.ts +46 -0
  310. package/src/services/MicStateCoordinator.ts +169 -4
  311. package/src/services/MiniappLauncher.ts +30 -3
  312. package/src/services/MiniappLiveness.ts +24 -0
  313. package/src/services/OtaArtifactDownloader.ts +312 -0
  314. package/src/services/OtaAutoChain.ts +106 -0
  315. package/src/services/OtaErrorMapping.ts +80 -0
  316. package/src/services/OtaInstallCoordinator.ts +645 -143
  317. package/src/services/OtaService.ts +11 -5
  318. package/src/services/OtaUpdateCheckService.ts +180 -20
  319. package/src/services/PhoneCalendarService.ts +27 -4
  320. package/src/services/PhoneCameraFovCoordinator.ts +15 -0
  321. package/src/services/PhoneNotificationsSync.ts +12 -10
  322. package/src/services/PhonePhotoCoordinator.ts +33 -7
  323. package/src/services/PhoneStreamCoordinator.ts +139 -25
  324. package/src/services/PhoneVideoCoordinator.ts +11 -0
  325. package/src/services/SceneRenderer.ts +16 -0
  326. package/src/services/SupportProfileSync.ts +218 -0
  327. package/src/services/TtsTextSanitizer.ts +66 -0
  328. package/src/services/WifiAdbRequest.ts +53 -0
  329. package/src/services/asg/asgCameraApi.ts +99 -8
  330. package/src/services/asg/gallerySyncService.ts +28 -19
  331. package/src/services/asg/localNetworkTransport.ts +7 -11
  332. package/src/services/audioPlaybackAssets.ts +8 -0
  333. package/src/services/glassesClockSync.ts +2 -2
  334. package/src/services/manifestActions.ts +30 -0
  335. package/src/services/otaDisplayState.ts +57 -16
  336. package/src/services/otaInstallPolicy.ts +29 -3
  337. package/src/services/{asgOtaVersionUrl.ts → otaManifestUrl.ts} +16 -4
  338. package/src/services/slimStreamStatus.ts +16 -5
  339. package/src/stores/apps.ts +12 -0
  340. package/src/stores/bluetoothSettingKeys.ts +2 -0
  341. package/src/stores/gallerySync.ts +4 -0
  342. package/src/stores/glasses.ts +3 -0
  343. package/src/stores/settings.ts +174 -21
  344. package/src/types/applet.ts +2 -0
  345. package/src/types/enums.ts +2 -1
  346. package/src/types/hardware.ts +17 -0
  347. package/src/utils/configuredDevHost.ts +17 -0
  348. package/src/utils/devMiniappLaunch.ts +151 -23
  349. package/src/utils/diagnosticContext.ts +17 -17
  350. package/src/utils/hardware/hardware.ts +7 -9
  351. package/src/utils/miniappDiagnostics.ts +226 -0
  352. package/src/utils/timers/timers.ts +57 -21
  353. package/build/services/asgOtaVersionUrl.d.ts +0 -2
  354. package/build/services/asgOtaVersionUrl.d.ts.map +0 -1
  355. package/build/services/asgOtaVersionUrl.js.map +0 -1
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Phone-local SimpleStorage for bundled miniapps.
3
+ *
4
+ * Keys are scoped to the stable Mentra user id and package name. Access tokens
5
+ * must never be part of the namespace: Core access tokens rotate and are held
6
+ * in memory only, so using one makes every app restart look like a new user.
7
+ */
8
+
9
+ export interface LocalMiniappStorageBackend {
10
+ get(key: string): unknown | null
11
+ has(key: string): boolean
12
+ set(key: string, value: unknown): void
13
+ remove(key: string): void
14
+ keys(): string[]
15
+ }
16
+
17
+ export interface LocalMiniappStorageHooks {
18
+ getUserId: () => Promise<string>
19
+ backend: LocalMiniappStorageBackend
20
+ }
21
+
22
+ const KEY_ROOT = "mentraos_localstorage_"
23
+
24
+ export class LocalMiniappStorage {
25
+ constructor(private readonly hooks: LocalMiniappStorageHooks) {}
26
+
27
+ async get(packageName: string, key: string): Promise<unknown | null> {
28
+ return this.hooks.backend.get((await this.prefix(packageName)) + key)
29
+ }
30
+
31
+ async set(packageName: string, key: string, value: unknown): Promise<void> {
32
+ this.hooks.backend.set((await this.prefix(packageName)) + key, value)
33
+ }
34
+
35
+ async delete(packageName: string, key: string): Promise<void> {
36
+ this.hooks.backend.remove((await this.prefix(packageName)) + key)
37
+ }
38
+
39
+ async keys(packageName: string): Promise<string[]> {
40
+ const prefix = await this.prefix(packageName)
41
+ return this.hooks.backend
42
+ .keys()
43
+ .filter((key) => key.startsWith(prefix))
44
+ .map((key) => key.slice(prefix.length))
45
+ }
46
+
47
+ async clear(packageName: string): Promise<void> {
48
+ const prefix = await this.prefix(packageName)
49
+ for (const key of this.hooks.backend.keys()) {
50
+ if (key.startsWith(prefix)) this.hooks.backend.remove(key)
51
+ }
52
+ }
53
+
54
+ async has(packageName: string, key: string): Promise<boolean> {
55
+ return this.hooks.backend.has((await this.prefix(packageName)) + key)
56
+ }
57
+
58
+ async getAll(packageName: string): Promise<Record<string, string>> {
59
+ const prefix = await this.prefix(packageName)
60
+ const values: Record<string, string> = {}
61
+ for (const key of this.hooks.backend.keys()) {
62
+ if (!key.startsWith(prefix)) continue
63
+ if (!this.hooks.backend.has(key)) continue
64
+ const value = this.hooks.backend.get(key)
65
+ values[key.slice(prefix.length)] = typeof value === "string" ? value : String(value ?? "")
66
+ }
67
+ return values
68
+ }
69
+
70
+ async setMultiple(packageName: string, values: Record<string, unknown>): Promise<void> {
71
+ const prefix = await this.prefix(packageName)
72
+ for (const [key, value] of Object.entries(values)) {
73
+ this.hooks.backend.set(prefix + key, value ?? null)
74
+ }
75
+ }
76
+
77
+ private async prefix(packageName: string): Promise<string> {
78
+ const userId = (await this.hooks.getUserId()).trim()
79
+ if (!userId) throw new Error("Mentra user identity is unavailable")
80
+ return `${KEY_ROOT}${userId}_${packageName}_`
81
+ }
82
+ }
@@ -0,0 +1,46 @@
1
+ export interface LocalMiniappUserIdentityBackend {
2
+ get(): string | null
3
+ set(userId: string): void
4
+ remove(): void
5
+ }
6
+
7
+ /**
8
+ * Keeps the stable Mentra user id available to local miniapps across process
9
+ * restarts. The id is non-secret and is deliberately stored separately from
10
+ * the short-lived Core access token.
11
+ */
12
+ export class LocalMiniappUserIdentity {
13
+ private current: string | null = null
14
+
15
+ constructor(private readonly backend: LocalMiniappUserIdentityBackend) {}
16
+
17
+ async resolve(resolveOnline: () => Promise<string>): Promise<string> {
18
+ const available = this.get()
19
+ if (available) return available
20
+
21
+ const resolved = (await resolveOnline()).trim()
22
+ if (!resolved) throw new Error("Mentra user identity is unavailable")
23
+ this.remember(resolved)
24
+ return resolved
25
+ }
26
+
27
+ get(): string | null {
28
+ if (this.current) return this.current
29
+ const cached = this.backend.get()?.trim() ?? ""
30
+ if (!cached) return null
31
+ this.current = cached
32
+ return cached
33
+ }
34
+
35
+ remember(userId: string): void {
36
+ const resolved = userId.trim()
37
+ if (!resolved) throw new Error("Mentra user identity is unavailable")
38
+ this.backend.set(resolved)
39
+ this.current = resolved
40
+ }
41
+
42
+ forget(): void {
43
+ this.current = null
44
+ this.backend.remove()
45
+ }
46
+ }
@@ -14,11 +14,27 @@ import {createDebouncedPatchFlusher} from "../utils/debouncedPatch"
14
14
 
15
15
  const LOG_TAG = "MIC_COORDINATOR"
16
16
 
17
+ type MicGate = "vad" | "loudnessGate"
18
+
19
+ interface GateOverride {
20
+ enabled: boolean
21
+ order: number
22
+ }
23
+
24
+ interface ConfiguredMicGates {
25
+ vadEnabled?: boolean | null
26
+ loudnessGateEnabled?: boolean | null
27
+ }
28
+
17
29
  /** Mic-requirement flips are debounced (300ms) and merged into one BLE write
18
30
  * (wire v2 keeps BLE JSON small and infrequent). */
19
31
  const flushMicRequirementsPatch = createDebouncedPatchFlusher<Record<string, unknown>>((patch) => {
20
32
  try {
21
- void Promise.resolve(BluetoothSdk.updateBluetoothSettings(patch)).catch((err) => {
33
+ // Resolve runtime overrides at flush time. A miniapp can acquire/release a
34
+ // gate override during the debounce window, and a captured stale value
35
+ // must not win after that lifecycle transition.
36
+ const runtimePatch = micStateCoordinator.applyEffectiveGatePolicy(patch)
37
+ void Promise.resolve(BluetoothSdk.updateBluetoothSettings(runtimePatch)).catch((err) => {
22
38
  console.error(`${LOG_TAG}: failed to apply mic requirements:`, err)
23
39
  })
24
40
  } catch (err) {
@@ -32,6 +48,11 @@ class MicStateCoordinator {
32
48
  // Local miniapp requirements (set when miniapps subscribe to audio streams)
33
49
  private localWantsPcm = false
34
50
  private localWantsLc3 = false
51
+ private configuredVad: boolean | undefined
52
+ private configuredLoudnessGate: boolean | undefined
53
+ private readonly miniappVadOverrides = new Map<string, GateOverride>()
54
+ private readonly miniappLoudnessGateOverrides = new Map<string, GateOverride>()
55
+ private overrideSequence = 0
35
56
 
36
57
  private constructor() {}
37
58
 
@@ -46,13 +67,110 @@ class MicStateCoordinator {
46
67
  * Update local miniapp requirements. Called by LocalMiniappRuntime when
47
68
  * the aggregated set of local subscriptions changes.
48
69
  */
49
- public setLocalRequirements(req: {pcm: boolean; lc3: boolean}): void {
70
+ public setLocalRequirements(req: {pcm: boolean; lc3: boolean} & ConfiguredMicGates): void {
50
71
  this.localWantsPcm = req.pcm
51
72
  this.localWantsLc3 = req.lc3
73
+ this.rememberConfiguredGates(req)
52
74
  console.log(`${LOG_TAG}: local requirements updated — pcm=${req.pcm} lc3=${req.lc3}`)
53
75
  this.applyUnion()
54
76
  }
55
77
 
78
+ /**
79
+ * Apply a miniapp-owned gate override without changing the OS preference.
80
+ * Overrides are lifecycle-scoped and last-live-owner-wins independently for
81
+ * VAD and Barrier.
82
+ */
83
+ public async setMiniappGateOverride(
84
+ packageName: string,
85
+ gate: MicGate,
86
+ enabled: boolean,
87
+ configured: ConfiguredMicGates = {},
88
+ ): Promise<void> {
89
+ this.rememberConfiguredGates(configured)
90
+ const overrides = this.overridesFor(gate)
91
+ const previous = overrides.get(packageName)
92
+ const next = {enabled, order: ++this.overrideSequence}
93
+ overrides.set(packageName, next)
94
+
95
+ try {
96
+ await BluetoothSdk.updateBluetoothSettings(this.effectiveGatePatch())
97
+ } catch (error) {
98
+ // Do not roll back a newer request from the same package that landed
99
+ // while this native write was in flight.
100
+ if (overrides.get(packageName) === next) {
101
+ if (previous) overrides.set(packageName, previous)
102
+ else overrides.delete(packageName)
103
+ }
104
+ throw error
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Remove every gate override owned by a miniapp. This is synchronous so
110
+ * unregister can drop ownership before recomputing aggregate mic state.
111
+ */
112
+ public clearMiniappGateOverrides(packageName: string): boolean {
113
+ const removedVad = this.miniappVadOverrides.delete(packageName)
114
+ const removedLoudness = this.miniappLoudnessGateOverrides.delete(packageName)
115
+ return removedVad || removedLoudness
116
+ }
117
+
118
+ /** Re-apply the current runtime policy after an owner is released. */
119
+ public async syncEffectiveGatePolicy(configured: ConfiguredMicGates = {}): Promise<void> {
120
+ this.rememberConfiguredGates(configured)
121
+ const patch = this.effectiveGatePatch()
122
+ if (Object.keys(patch).length === 0) return
123
+ await BluetoothSdk.updateBluetoothSettings(patch)
124
+ }
125
+
126
+ /**
127
+ * Preserve the runtime microphone contract when the persisted device
128
+ * settings are replayed (for example after a glasses reconnect). Active
129
+ * miniapp gate overrides replace the OS values, and raw PCM keeps VAD off
130
+ * until the last raw-audio consumer unsubscribes.
131
+ */
132
+ public applyRuntimeOverrides(settings: Record<string, unknown>): Record<string, unknown> {
133
+ this.rememberConfiguredGates({
134
+ vadEnabled:
135
+ typeof settings.voice_activity_detection_enabled === "boolean"
136
+ ? settings.voice_activity_detection_enabled
137
+ : undefined,
138
+ loudnessGateEnabled:
139
+ typeof settings.loudness_gate_enabled === "boolean" ? settings.loudness_gate_enabled : undefined,
140
+ })
141
+
142
+ return this.applyActiveRuntimeOverrides(settings)
143
+ }
144
+
145
+ /**
146
+ * Apply the complete current gate policy without treating the input as an OS
147
+ * preference update. Used by debounced mic-requirement writes, whose queued
148
+ * gate values may be stale after an override is acquired or released.
149
+ */
150
+ public applyEffectiveGatePolicy(settings: Record<string, unknown>): Record<string, unknown> {
151
+ return {
152
+ ...settings,
153
+ ...this.effectiveGatePatch(),
154
+ }
155
+ }
156
+
157
+ private applyActiveRuntimeOverrides(settings: Record<string, unknown>): Record<string, unknown> {
158
+ const runtimeSettings = {...settings}
159
+ const vadOverride = this.latestOverride(this.miniappVadOverrides)
160
+ const loudnessOverride = this.latestOverride(this.miniappLoudnessGateOverrides)
161
+
162
+ if (this.localWantsPcm) {
163
+ runtimeSettings.voice_activity_detection_enabled = false
164
+ } else if (vadOverride) {
165
+ runtimeSettings.voice_activity_detection_enabled = vadOverride.enabled
166
+ }
167
+ if (loudnessOverride) {
168
+ runtimeSettings.loudness_gate_enabled = loudnessOverride.enabled
169
+ }
170
+
171
+ return runtimeSettings
172
+ }
173
+
56
174
  /**
57
175
  * Push local requirements to BluetoothSdk. `should_send_pcm` is strictly for
58
176
  * on-device PCM consumers; cloud audio uses LC3 through AudioCloudUplink.
@@ -67,11 +185,56 @@ class MicStateCoordinator {
67
185
 
68
186
  // The mic control plane is a direct btsdk call now (was a host setMicRequirements
69
187
  // hook) so a bare OEM streams audio without wiring it.
70
- flushMicRequirementsPatch({
188
+ const patch: Record<string, unknown> = {
71
189
  should_send_pcm: shouldSendPcm,
72
190
  should_send_lc3: shouldSendLc3,
73
191
  should_send_transcript: false,
74
- })
192
+ ...this.effectiveGatePatch(),
193
+ }
194
+
195
+ flushMicRequirementsPatch(patch)
196
+ }
197
+
198
+ private rememberConfiguredGates(configured: ConfiguredMicGates): void {
199
+ // null means the connected device intentionally omits that setting.
200
+ // undefined means the caller is not updating the remembered preference.
201
+ if (configured.vadEnabled !== undefined) {
202
+ this.configuredVad = configured.vadEnabled ?? undefined
203
+ }
204
+ if (configured.loudnessGateEnabled !== undefined) {
205
+ this.configuredLoudnessGate = configured.loudnessGateEnabled ?? undefined
206
+ }
207
+ }
208
+
209
+ private overridesFor(gate: MicGate): Map<string, GateOverride> {
210
+ return gate === "vad" ? this.miniappVadOverrides : this.miniappLoudnessGateOverrides
211
+ }
212
+
213
+ private latestOverride(overrides: Map<string, GateOverride>): GateOverride | undefined {
214
+ let latest: GateOverride | undefined
215
+ for (const entry of overrides.values()) {
216
+ if (!latest || entry.order > latest.order) latest = entry
217
+ }
218
+ return latest
219
+ }
220
+
221
+ private effectiveGatePatch(): Record<string, unknown> {
222
+ const patch: Record<string, unknown> = {}
223
+ const vadOverride = this.latestOverride(this.miniappVadOverrides)
224
+ const loudnessOverride = this.latestOverride(this.miniappLoudnessGateOverrides)
225
+
226
+ // Hardware VAD suppresses silence. Raw-audio consumers need a continuous
227
+ // timeline, so their requirement wins over both OS and miniapp VAD values.
228
+ if (this.localWantsPcm) patch.voice_activity_detection_enabled = false
229
+ else if (vadOverride) patch.voice_activity_detection_enabled = vadOverride.enabled
230
+ else if (this.configuredVad !== undefined) patch.voice_activity_detection_enabled = this.configuredVad
231
+
232
+ if (loudnessOverride) patch.loudness_gate_enabled = loudnessOverride.enabled
233
+ else if (this.configuredLoudnessGate !== undefined) {
234
+ patch.loudness_gate_enabled = this.configuredLoudnessGate
235
+ }
236
+
237
+ return patch
75
238
  }
76
239
 
77
240
  /**
@@ -85,6 +248,8 @@ class MicStateCoordinator {
85
248
 
86
249
  public cleanup(): void {
87
250
  console.log(`${LOG_TAG}: cleanup()`)
251
+ this.miniappVadOverrides.clear()
252
+ this.miniappLoudnessGateOverrides.clear()
88
253
  this.reset()
89
254
  MicStateCoordinator.instance = null
90
255
  }
@@ -109,7 +109,9 @@ class MiniappLauncher {
109
109
  const entry = manifest.entry as {background?: string; ui?: string} | undefined
110
110
  if (!entry?.background) return null
111
111
 
112
- const base = devUrl.replace(/\/$/, "")
112
+ // Use the host that actually answered — may differ from the stored IP
113
+ // after a laptop Wi-Fi change (mDNS / Metro failover inside decideDevLaunchRoute).
114
+ const base = route.resolvedUrl.replace(/\/$/, "")
113
115
  // entry.* are bundle-root paths (dist/ stripped); the dev server serves
114
116
  // files relative to cwd, so prepend dist/.
115
117
  const bgUrl = `${base}/dist/${entry.background.replace(/^\.?\/+/, "")}`
@@ -120,9 +122,16 @@ class MiniappLauncher {
120
122
  .map((p) => (typeof p === "string" ? p : p?.type))
121
123
  .filter((t): t is string => typeof t === "string")
122
124
  const installedManifest: InstalledMiniappManifest = {
125
+ packageName: typeof manifest.packageName === "string" ? manifest.packageName : packageName,
123
126
  name: manifest.name,
127
+ version: typeof manifest.version === "string" ? manifest.version : undefined,
128
+ sdkVersion: typeof manifest.sdkVersion === "string" ? manifest.sdkVersion : undefined,
129
+ minHostVersion: typeof manifest.minHostVersion === "string" ? manifest.minHostVersion : undefined,
130
+ type: typeof manifest.type === "string" ? manifest.type : undefined,
131
+ entry: manifest.entry as InstalledMiniappManifest["entry"],
124
132
  permissions: manifest.permissions as InstalledMiniappManifest["permissions"],
125
133
  hardwareRequirements: manifest.hardwareRequirements as InstalledMiniappManifest["hardwareRequirements"],
134
+ actions: manifest.actions as InstalledMiniappManifest["actions"],
126
135
  }
127
136
 
128
137
  let bgSource: string
@@ -140,7 +149,7 @@ class MiniappLauncher {
140
149
  uiBaseDir: uiUri ? uiUri.replace(/\/[^/]+$/, "/") : null,
141
150
  declaredPermissions,
142
151
  installedManifest,
143
- devUrl,
152
+ devUrl: route.resolvedUrl,
144
153
  devPort: this.resolveDevPort(hints?.devPort, packageName),
145
154
  }
146
155
  }
@@ -152,15 +161,33 @@ class MiniappLauncher {
152
161
  if (!entryPaths?.background) return null
153
162
 
154
163
  const manifest = appRegistry.getMiniappManifest(packageName, version) as {
164
+ packageName?: string
155
165
  name?: string
166
+ version?: string
167
+ sdkVersion?: string
168
+ minHostVersion?: string
169
+ type?: string
170
+ entry?: {background?: string; ui?: string}
156
171
  permissions?: Array<{type: string; required?: boolean; description?: string}>
157
172
  hardwareRequirements?: Array<{type: string; level: string; description?: string}>
173
+ actions?: Array<{id?: unknown; description?: unknown; parameters?: unknown; outputSchema?: unknown}>
158
174
  } | null
159
175
  const declaredPermissions = (manifest?.permissions ?? [])
160
176
  .map((p) => p.type)
161
177
  .filter((t): t is string => typeof t === "string")
162
178
  const installedManifest: InstalledMiniappManifest | undefined = manifest
163
- ? {name: manifest.name, permissions: manifest.permissions, hardwareRequirements: manifest.hardwareRequirements}
179
+ ? {
180
+ packageName: manifest.packageName ?? packageName,
181
+ name: manifest.name,
182
+ version: manifest.version ?? version,
183
+ sdkVersion: manifest.sdkVersion,
184
+ minHostVersion: manifest.minHostVersion,
185
+ type: manifest.type,
186
+ entry: manifest.entry,
187
+ permissions: manifest.permissions,
188
+ hardwareRequirements: manifest.hardwareRequirements,
189
+ actions: manifest.actions,
190
+ }
164
191
  : undefined
165
192
 
166
193
  let bgSource: string
@@ -0,0 +1,24 @@
1
+ export interface MiniappPingLivenessDecision {
2
+ shouldUnregister: boolean
3
+ unansweredPingRounds: number
4
+ }
5
+
6
+ /**
7
+ * Advance liveness by one scheduled ping round.
8
+ *
9
+ * Counting actual rounds avoids treating time while React Native's scheduler
10
+ * was paused in the background as pings the miniapp failed to answer.
11
+ */
12
+ export function advanceMiniappPingLiveness(
13
+ unansweredPingRounds: number,
14
+ timeoutThreshold: number,
15
+ ): MiniappPingLivenessDecision {
16
+ if (unansweredPingRounds >= timeoutThreshold) {
17
+ return {shouldUnregister: true, unansweredPingRounds}
18
+ }
19
+
20
+ return {
21
+ shouldUnregister: false,
22
+ unansweredPingRounds: unansweredPingRounds + 1,
23
+ }
24
+ }