@mentra/engine 3.2.0-dev.115 → 3.2.0-dev.120

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 (65) hide show
  1. package/LICENSE +201 -0
  2. package/build/engine.d.ts.map +1 -1
  3. package/build/engine.js +7 -2
  4. package/build/engine.js.map +1 -1
  5. package/build/generated/releaseMetadata.js +5 -5
  6. package/build/generated/releaseMetadata.js.map +1 -1
  7. package/build/runtime/streamConfig.d.ts +7 -0
  8. package/build/runtime/streamConfig.d.ts.map +1 -1
  9. package/build/runtime/streamConfig.js +7 -0
  10. package/build/runtime/streamConfig.js.map +1 -1
  11. package/build/services/AcsMeetingService.d.ts +154 -0
  12. package/build/services/AcsMeetingService.d.ts.map +1 -0
  13. package/build/services/AcsMeetingService.js +477 -0
  14. package/build/services/AcsMeetingService.js.map +1 -0
  15. package/build/services/DeviceEventRouter.d.ts.map +1 -1
  16. package/build/services/DeviceEventRouter.js +12 -3
  17. package/build/services/DeviceEventRouter.js.map +1 -1
  18. package/build/services/GlassesSettingsSync.d.ts.map +1 -1
  19. package/build/services/GlassesSettingsSync.js +11 -1
  20. package/build/services/GlassesSettingsSync.js.map +1 -1
  21. package/build/services/GlassesStatusProjection.d.ts +12 -0
  22. package/build/services/GlassesStatusProjection.d.ts.map +1 -1
  23. package/build/services/GlassesStatusProjection.js +23 -0
  24. package/build/services/GlassesStatusProjection.js.map +1 -1
  25. package/build/services/LocalMiniappRuntime.d.ts +6 -0
  26. package/build/services/LocalMiniappRuntime.d.ts.map +1 -1
  27. package/build/services/LocalMiniappRuntime.js +130 -1
  28. package/build/services/LocalMiniappRuntime.js.map +1 -1
  29. package/build/services/MentraJSRouter.d.ts.map +1 -1
  30. package/build/services/MentraJSRouter.js +8 -1
  31. package/build/services/MentraJSRouter.js.map +1 -1
  32. package/build/services/PhoneCameraFovCoordinator.d.ts +18 -0
  33. package/build/services/PhoneCameraFovCoordinator.d.ts.map +1 -1
  34. package/build/services/PhoneCameraFovCoordinator.js +36 -0
  35. package/build/services/PhoneCameraFovCoordinator.js.map +1 -1
  36. package/build/services/PhoneStreamCoordinator.d.ts +67 -1
  37. package/build/services/PhoneStreamCoordinator.d.ts.map +1 -1
  38. package/build/services/PhoneStreamCoordinator.js +204 -15
  39. package/build/services/PhoneStreamCoordinator.js.map +1 -1
  40. package/build/services/StreamLifecycleController.d.ts +23 -5
  41. package/build/services/StreamLifecycleController.d.ts.map +1 -1
  42. package/build/services/StreamLifecycleController.js +31 -10
  43. package/build/services/StreamLifecycleController.js.map +1 -1
  44. package/build/services/acsAudioSource.d.ts +24 -0
  45. package/build/services/acsAudioSource.d.ts.map +1 -0
  46. package/build/services/acsAudioSource.js +37 -0
  47. package/build/services/acsAudioSource.js.map +1 -0
  48. package/build/utils/devMiniappLaunch.d.ts.map +1 -1
  49. package/build/utils/devMiniappLaunch.js +4 -0
  50. package/build/utils/devMiniappLaunch.js.map +1 -1
  51. package/package.json +8 -7
  52. package/src/engine.ts +10 -2
  53. package/src/generated/releaseMetadata.ts +5 -5
  54. package/src/runtime/streamConfig.ts +13 -0
  55. package/src/services/AcsMeetingService.ts +570 -0
  56. package/src/services/DeviceEventRouter.ts +12 -3
  57. package/src/services/GlassesSettingsSync.ts +13 -3
  58. package/src/services/GlassesStatusProjection.ts +28 -0
  59. package/src/services/LocalMiniappRuntime.ts +142 -1
  60. package/src/services/MentraJSRouter.ts +7 -1
  61. package/src/services/PhoneCameraFovCoordinator.ts +34 -0
  62. package/src/services/PhoneStreamCoordinator.ts +232 -19
  63. package/src/services/StreamLifecycleController.ts +42 -12
  64. package/src/services/acsAudioSource.ts +53 -0
  65. package/src/utils/devMiniappLaunch.ts +4 -0
@@ -1 +1 @@
1
- {"version":3,"file":"DeviceEventRouter.js","sourceRoot":"","sources":["../../src/services/DeviceEventRouter.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Device-event router — engine-owned. Subscribes to the inbound native BLE/device\n * events and routes them into the engine runtime: device stores, the process event\n * bus, the photo/stream coordinators, and local miniapps (via forwardEvent).\n *\n * Why this exists: engine owns the stores, coordinators, miniapp runtime, and facades,\n * but it never *subscribed to the device* for most events — the host MantleManager was\n * still the event router the whole runtime secretly depended on. So a bare OEM that\n * imported engine + called engine.start() got a connected runtime with almost no device\n * data flowing in (no miniapp input, dead gallery sync, starved coordinators). This\n * service moves those inbound bridges into engine so ANY host gets them.\n *\n * Scope: the engine legs only. The Cloud V1 relays that used to live beside them in\n * MantleManager (touch→cloud, the cloud-SDK stream/photo legs, etc.) were deleted with\n * Cloud V1 app end-of-life.\n *\n * Started by `engine.start()`. Idempotent.\n */\nimport BluetoothSdk from \"@mentra/bluetooth-sdk/internal\"\nimport {shallow} from \"zustand/shallow\"\n\nimport localMiniappRuntime from \"./LocalMiniappRuntime\"\nimport localSttFallbackCoordinator from \"./LocalSttFallbackCoordinator\"\nimport {phonePhotoCoordinator} from \"./PhonePhotoCoordinator\"\nimport {phoneStreamCoordinator} from \"./PhoneStreamCoordinator\"\nimport {isGlassesConnected} from \"./GlassesReadiness\"\nimport {useGlassesStore} from \"../stores/glasses\"\nimport {useSettingsStore} from \"../stores/settings\"\nimport {useAppStatusStore} from \"../stores/apps\"\nimport {retirePendingSelectionOnPromotion} from \"./PairingIdentity\"\nimport GlobalEventEmitter from \"../utils/GlobalEventEmitter\"\nimport {asgCameraApi} from \"./asg/asgCameraApi\"\n\nlet subs: Array<{remove: () => void}> = []\n\nexport function startDeviceEventRouter(): void {\n if (subs.length) return\n\n // --- device state → engine stores ---\n\n // Standalone WiFi status → glasses store.\n subs.push(\n BluetoothSdk.addListener(\"wifi_status_change\", (event) => {\n const {type: _type, ...wifi} = event\n useGlassesStore.getState().setGlassesInfo({wifi})\n }),\n )\n\n // Forward glasses Wi-Fi to miniapps (session.glasses.onWifi) from the STORE — the\n // single source of truth — so every path converges here: wifi_status_change,\n // onGlassesStatus, and BLE disconnect. Effective connectivity requires the glasses\n // to be connected AND on Wi-Fi, so a disconnect correctly flips `connected` false.\n subs.push({\n remove: useGlassesStore.subscribe(\n (s) => {\n const connected = isGlassesConnected(s.connection) && s.wifi.state === \"connected\"\n return {\n connected,\n ssid: s.wifi.state === \"connected\" ? s.wifi.ssid : undefined,\n localIp: s.wifi.state === \"connected\" ? s.wifi.localIp : undefined,\n }\n },\n (wifi) => localMiniappRuntime.forwardEvent(\"glasses_wifi\", wifi),\n {equalityFn: shallow},\n ),\n })\n\n // Incremental battery status → glasses store + local miniapps.\n subs.push(\n BluetoothSdk.addListener(\"battery_status\", (event) => {\n const state = useGlassesStore.getState()\n state.setBatteryInfo(event.level, event.charging, state.caseBatteryLevel, state.caseCharging)\n localMiniappRuntime.forwardEvent(\"glasses_battery_update\", {\n type: \"glasses_battery_update\",\n level: event.level,\n charging: event.charging,\n timestamp: event.timestamp ?? Date.now(),\n })\n }),\n )\n\n // Hotspot status → glasses store + event bus. engine's own gallerySyncService listens\n // on the bus for these, so without this bridge engine's gallery sync is dead.\n subs.push(\n BluetoothSdk.addListener(\"hotspot_status_change\", (event) => {\n const enabled = event.state === \"enabled\"\n const ssid = enabled ? event.ssid : \"\"\n const password = enabled ? event.password : \"\"\n const localIp = enabled ? event.localIp : \"\"\n useGlassesStore.getState().setHotspotInfo(enabled, ssid, password, localIp)\n // Always retarget the ASG camera client: on disable (or an event with no\n // IP) fall back to the localhost default so later calls fail fast instead\n // of hitting the previous network's stale glasses IP.\n asgCameraApi.setServer(localIp || \"localhost\", 8089)\n GlobalEventEmitter.emit(\"hotspot_status_change\", {enabled, ssid, password, local_ip: localIp})\n }),\n )\n subs.push(\n BluetoothSdk.addListener(\"hotspot_error\", (event) => {\n GlobalEventEmitter.emit(\"hotspot_error\", {error_message: event.errorMessage, timestamp: event.timestamp})\n }),\n )\n\n // Glasses gallery content counts → event bus (consumed by gallerySyncService).\n subs.push(\n BluetoothSdk.addListener(\"gallery_status\", (event) => {\n GlobalEventEmitter.emit(\"gallery_status\", {\n photos: event.photos,\n videos: event.videos,\n total: event.total,\n has_content: event.hasContent,\n camera_busy: event.cameraBusy,\n })\n }),\n )\n\n // Hardware-originated setting changes (user changes a setting ON the glasses) → store.\n // The inbound complement to GlassesSettingsSync (which only pushes store→device).\n subs.push(\n BluetoothSdk.addListener(\"save_setting\", async (event) => {\n const settings = useSettingsStore.getState()\n // Damp the relay: native re-echoes some settings unconditionally (e.g.\n // the identity block at every handleDeviceReady) — a same-value echo\n // must not become a store write (persistence churn + change-push noise).\n if (settings.getSetting(event.key) !== event.value) {\n await settings.setSetting(event.key, event.value)\n }\n // Two-phase identity: a promoted default retires the pending selection\n // marker. The rule lives with PairingIdentity (a no-op for other keys).\n await retirePendingSelectionOnPromotion(event.key, event.value)\n }),\n )\n\n // --- coordinators (owns()-gated) ---\n\n // Phone-owned photo errors settle the in-flight long-poll fast (vs. timeout).\n // Non-owned photo responses used to forward to the Cloud V1 photo pipeline via\n // restComms; that relay was removed with Cloud V1 app end-of-life, so they drop.\n // Wire v2 sends a short 4-hex BLE requestId; the coordinator maps it back to\n // the cloud requestId (resolveCloudRequestId) before ownership checks.\n subs.push(\n BluetoothSdk.addListener(\"photo_response\", (event) => {\n const cloudRequestId = event.requestId ? phonePhotoCoordinator.resolveCloudRequestId(event.requestId) : \"\"\n if (cloudRequestId && phonePhotoCoordinator.owns(cloudRequestId)) {\n if (event.state === \"error\") {\n phonePhotoCoordinator.handlePhotoError(\n event.requestId ?? cloudRequestId,\n event.errorCode ?? \"GLASSES_ERROR\",\n event.errorMessage ?? \"Glasses reported an error\",\n )\n }\n }\n }),\n )\n\n // Phone-owned stream status / keep-alive → the stream coordinator. The Cloud V1 relay\n // for non-owned streams was removed with Cloud V1 app end-of-life.\n subs.push(\n BluetoothSdk.addListener(\"stream_status\", (event) => {\n if (event.streamId && phoneStreamCoordinator.owns(event.streamId)) {\n phoneStreamCoordinator.handleGlassesStatus(event)\n }\n }),\n )\n subs.push(\n BluetoothSdk.addListener(\"keep_alive_ack\", (event) => {\n if (event.streamId && phoneStreamCoordinator.owns(event.streamId)) {\n phoneStreamCoordinator.handleKeepAliveAck(event)\n }\n }),\n )\n\n // --- device input → local miniapps (forwardEvent is subscriber-gated; a no-op when\n // no miniapp listens). The v1 SocketComms cloud legs for these were removed with\n // the Cloud V1 ripout. ---\n\n subs.push(\n BluetoothSdk.addListener(\"button_press\", (event) => {\n localMiniappRuntime.forwardEvent(\"button_press\", event)\n }),\n )\n subs.push(\n BluetoothSdk.addListener(\"touch_event\", (event) => {\n localMiniappRuntime.forwardEvent(\"touch_event\", event)\n }),\n )\n // G2 IMU accelerometer — payload already matches the miniapp AccelData shape.\n subs.push(\n BluetoothSdk.addListener(\"accel_event\", (event) => {\n localMiniappRuntime.forwardEvent(\"accel_event\", {\n x: event.x,\n y: event.y,\n z: event.z,\n timestamp: typeof event.timestamp === \"number\" ? event.timestamp : Date.now(),\n })\n }),\n )\n // Head position — translate native {up:boolean} → SDK {position:\"up\"|\"down\"}.\n subs.push(\n BluetoothSdk.addListener(\"head_up\", (event) => {\n localMiniappRuntime.forwardEvent(\"head_up\", {position: event.up ? \"up\" : \"down\", timestamp: Date.now()})\n }),\n )\n // On-device STT transcripts → local miniapps only while the local engine is\n // active (cloud fallback or an explicit forceLocal subscription). The runtime\n // filters mixed local/cloud delivery per miniapp to avoid double-delivery.\n // forwardEvent is subscriber-gated — a no-op when no miniapp listens.\n // (Was MantleManager.handle_local_transcription; its offline-captions display branch\n // went away with the pseudo captions renderer.)\n subs.push(\n BluetoothSdk.addListener(\"local_transcription\", (event) => {\n if (!localSttFallbackCoordinator.isActive()) return\n const lang = event.transcribeLanguage ?? localSttFallbackCoordinator.getActiveLanguage() ?? \"en-US\"\n localMiniappRuntime.forwardEvent(`transcription:${lang}`, event, \"local\")\n }),\n )\n\n // --- glasses swipe-menu app launcher (G2) ---\n subs.push(\n BluetoothSdk.addListener(\"miniapp_selected\", (event) => {\n const packageName = event.packageName as string\n if (!packageName) return\n const app = useAppStatusStore.getState().apps.find((a) => a.packageName === packageName)\n if (!app) return\n // Toggle: stop if running, else start.\n if (app.running) {\n useAppStatusStore.getState().stop(packageName)\n } else {\n useAppStatusStore.getState().start(app, {skipNavigation: true})\n }\n }),\n )\n}\n\nexport function stopDeviceEventRouter(): void {\n for (const sub of subs) sub.remove()\n subs = []\n}\n"]}
1
+ {"version":3,"file":"DeviceEventRouter.js","sourceRoot":"","sources":["../../src/services/DeviceEventRouter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,YAAY,MAAM,gCAAgC,CAAA;AACzD,OAAO,EAAC,OAAO,EAAC,MAAM,iBAAiB,CAAA;AAEvC,OAAO,mBAAmB,MAAM,uBAAuB,CAAA;AACvD,OAAO,2BAA2B,MAAM,+BAA+B,CAAA;AACvE,OAAO,EAAC,qBAAqB,EAAC,MAAM,yBAAyB,CAAA;AAC7D,OAAO,EAAC,sBAAsB,EAAC,MAAM,0BAA0B,CAAA;AAC/D,OAAO,EAAC,kBAAkB,EAAC,MAAM,oBAAoB,CAAA;AACrD,OAAO,EAAC,eAAe,EAAC,MAAM,mBAAmB,CAAA;AACjD,OAAO,EAAC,gBAAgB,EAAC,MAAM,oBAAoB,CAAA;AACnD,OAAO,EAAC,iBAAiB,EAAC,MAAM,gBAAgB,CAAA;AAChD,OAAO,EAAC,iCAAiC,EAAC,MAAM,mBAAmB,CAAA;AACnE,OAAO,kBAAkB,MAAM,6BAA6B,CAAA;AAC5D,OAAO,EAAC,YAAY,EAAC,MAAM,oBAAoB,CAAA;AAE/C,IAAI,IAAI,GAAgC,EAAE,CAAA;AAE1C,MAAM,UAAU,sBAAsB;IACpC,IAAI,IAAI,CAAC,MAAM;QAAE,OAAM;IAEvB,uCAAuC;IAEvC,0CAA0C;IAC1C,IAAI,CAAC,IAAI,CACP,YAAY,CAAC,WAAW,CAAC,oBAAoB,EAAE,CAAC,KAAK,EAAE,EAAE;QACvD,MAAM,EAAC,IAAI,EAAE,KAAK,EAAE,GAAG,IAAI,EAAC,GAAG,KAAK,CAAA;QACpC,eAAe,CAAC,QAAQ,EAAE,CAAC,cAAc,CAAC,EAAC,IAAI,EAAC,CAAC,CAAA;IACnD,CAAC,CAAC,CACH,CAAA;IAED,kFAAkF;IAClF,6EAA6E;IAC7E,mFAAmF;IACnF,mFAAmF;IACnF,gFAAgF;IAChF,8EAA8E;IAC9E,wDAAwD;IACxD,IAAI,CAAC,IAAI,CAAC;QACR,MAAM,EAAE,eAAe,CAAC,SAAS,CAC/B,CAAC,CAAC,EAAE,EAAE;YACJ,MAAM,aAAa,GAAG,kBAAkB,CAAC,CAAC,CAAC,UAAU,CAAC,CAAA;YACtD,MAAM,SAAS,GAAG,aAAa,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,KAAK,WAAW,CAAA;YAC/D,yEAAyE;YACzE,yEAAyE;YACzE,wEAAwE;YACxE,+CAA+C;YAC/C,OAAO;gBACL,SAAS;gBACT,aAAa;gBACb,IAAI,EAAE,aAAa,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,KAAK,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;gBAC7E,OAAO,EAAE,aAAa,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,KAAK,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS;aACpF,CAAA;QACH,CAAC,EACD,CAAC,IAAI,EAAE,EAAE,CAAC,mBAAmB,CAAC,YAAY,CAAC,cAAc,EAAE,IAAI,CAAC,EAChE,EAAC,UAAU,EAAE,OAAO,EAAC,CACtB;KACF,CAAC,CAAA;IAEF,+DAA+D;IAC/D,IAAI,CAAC,IAAI,CACP,YAAY,CAAC,WAAW,CAAC,gBAAgB,EAAE,CAAC,KAAK,EAAE,EAAE;QACnD,MAAM,KAAK,GAAG,eAAe,CAAC,QAAQ,EAAE,CAAA;QACxC,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,gBAAgB,EAAE,KAAK,CAAC,YAAY,CAAC,CAAA;QAC7F,mBAAmB,CAAC,YAAY,CAAC,wBAAwB,EAAE;YACzD,IAAI,EAAE,wBAAwB;YAC9B,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,SAAS,EAAE,KAAK,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE;SACzC,CAAC,CAAA;IACJ,CAAC,CAAC,CACH,CAAA;IAED,sFAAsF;IACtF,8EAA8E;IAC9E,IAAI,CAAC,IAAI,CACP,YAAY,CAAC,WAAW,CAAC,uBAAuB,EAAE,CAAC,KAAK,EAAE,EAAE;QAC1D,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,KAAK,SAAS,CAAA;QACzC,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAA;QACtC,MAAM,QAAQ,GAAG,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAA;QAC9C,MAAM,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAA;QAC5C,eAAe,CAAC,QAAQ,EAAE,CAAC,cAAc,CAAC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAA;QAC3E,yEAAyE;QACzE,0EAA0E;QAC1E,sDAAsD;QACtD,YAAY,CAAC,SAAS,CAAC,OAAO,IAAI,WAAW,EAAE,IAAI,CAAC,CAAA;QACpD,kBAAkB,CAAC,IAAI,CAAC,uBAAuB,EAAE,EAAC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAC,CAAC,CAAA;IAChG,CAAC,CAAC,CACH,CAAA;IACD,IAAI,CAAC,IAAI,CACP,YAAY,CAAC,WAAW,CAAC,eAAe,EAAE,CAAC,KAAK,EAAE,EAAE;QAClD,kBAAkB,CAAC,IAAI,CAAC,eAAe,EAAE,EAAC,aAAa,EAAE,KAAK,CAAC,YAAY,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAC,CAAC,CAAA;IAC3G,CAAC,CAAC,CACH,CAAA;IAED,+EAA+E;IAC/E,IAAI,CAAC,IAAI,CACP,YAAY,CAAC,WAAW,CAAC,gBAAgB,EAAE,CAAC,KAAK,EAAE,EAAE;QACnD,kBAAkB,CAAC,IAAI,CAAC,gBAAgB,EAAE;YACxC,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,WAAW,EAAE,KAAK,CAAC,UAAU;YAC7B,WAAW,EAAE,KAAK,CAAC,UAAU;SAC9B,CAAC,CAAA;IACJ,CAAC,CAAC,CACH,CAAA;IAED,uFAAuF;IACvF,kFAAkF;IAClF,IAAI,CAAC,IAAI,CACP,YAAY,CAAC,WAAW,CAAC,cAAc,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;QACvD,MAAM,QAAQ,GAAG,gBAAgB,CAAC,QAAQ,EAAE,CAAA;QAC5C,uEAAuE;QACvE,qEAAqE;QACrE,yEAAyE;QACzE,IAAI,QAAQ,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,KAAK,EAAE,CAAC;YACnD,MAAM,QAAQ,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,KAAK,CAAC,CAAA;QACnD,CAAC;QACD,uEAAuE;QACvE,wEAAwE;QACxE,MAAM,iCAAiC,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,KAAK,CAAC,CAAA;IACjE,CAAC,CAAC,CACH,CAAA;IAED,sCAAsC;IAEtC,8EAA8E;IAC9E,+EAA+E;IAC/E,iFAAiF;IACjF,6EAA6E;IAC7E,uEAAuE;IACvE,IAAI,CAAC,IAAI,CACP,YAAY,CAAC,WAAW,CAAC,gBAAgB,EAAE,CAAC,KAAK,EAAE,EAAE;QACnD,MAAM,cAAc,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,qBAAqB,CAAC,qBAAqB,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;QAC1G,IAAI,cAAc,IAAI,qBAAqB,CAAC,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC;YACjE,IAAI,KAAK,CAAC,KAAK,KAAK,OAAO,EAAE,CAAC;gBAC5B,qBAAqB,CAAC,gBAAgB,CACpC,KAAK,CAAC,SAAS,IAAI,cAAc,EACjC,KAAK,CAAC,SAAS,IAAI,eAAe,EAClC,KAAK,CAAC,YAAY,IAAI,2BAA2B,CAClD,CAAA;YACH,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CACH,CAAA;IAED,sFAAsF;IACtF,mEAAmE;IACnE,IAAI,CAAC,IAAI,CACP,YAAY,CAAC,WAAW,CAAC,eAAe,EAAE,CAAC,KAAK,EAAE,EAAE;QAClD,IAAI,KAAK,CAAC,QAAQ,IAAI,sBAAsB,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;YAClE,sBAAsB,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAA;QACnD,CAAC;IACH,CAAC,CAAC,CACH,CAAA;IACD,IAAI,CAAC,IAAI,CACP,YAAY,CAAC,WAAW,CAAC,gBAAgB,EAAE,CAAC,KAAK,EAAE,EAAE;QACnD,IAAI,KAAK,CAAC,QAAQ,IAAI,sBAAsB,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;YAClE,sBAAsB,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAA;QAClD,CAAC;IACH,CAAC,CAAC,CACH,CAAA;IAED,oFAAoF;IACpF,iFAAiF;IACjF,2BAA2B;IAE3B,IAAI,CAAC,IAAI,CACP,YAAY,CAAC,WAAW,CAAC,cAAc,EAAE,CAAC,KAAK,EAAE,EAAE;QACjD,mBAAmB,CAAC,YAAY,CAAC,cAAc,EAAE,KAAK,CAAC,CAAA;IACzD,CAAC,CAAC,CACH,CAAA;IACD,IAAI,CAAC,IAAI,CACP,YAAY,CAAC,WAAW,CAAC,aAAa,EAAE,CAAC,KAAK,EAAE,EAAE;QAChD,mBAAmB,CAAC,YAAY,CAAC,aAAa,EAAE,KAAK,CAAC,CAAA;IACxD,CAAC,CAAC,CACH,CAAA;IACD,8EAA8E;IAC9E,IAAI,CAAC,IAAI,CACP,YAAY,CAAC,WAAW,CAAC,aAAa,EAAE,CAAC,KAAK,EAAE,EAAE;QAChD,mBAAmB,CAAC,YAAY,CAAC,aAAa,EAAE;YAC9C,CAAC,EAAE,KAAK,CAAC,CAAC;YACV,CAAC,EAAE,KAAK,CAAC,CAAC;YACV,CAAC,EAAE,KAAK,CAAC,CAAC;YACV,SAAS,EAAE,OAAO,KAAK,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE;SAC9E,CAAC,CAAA;IACJ,CAAC,CAAC,CACH,CAAA;IACD,8EAA8E;IAC9E,IAAI,CAAC,IAAI,CACP,YAAY,CAAC,WAAW,CAAC,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE;QAC5C,mBAAmB,CAAC,YAAY,CAAC,SAAS,EAAE,EAAC,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,EAAC,CAAC,CAAA;IAC1G,CAAC,CAAC,CACH,CAAA;IACD,4EAA4E;IAC5E,8EAA8E;IAC9E,2EAA2E;IAC3E,sEAAsE;IACtE,qFAAqF;IACrF,gDAAgD;IAChD,IAAI,CAAC,IAAI,CACP,YAAY,CAAC,WAAW,CAAC,qBAAqB,EAAE,CAAC,KAAK,EAAE,EAAE;QACxD,IAAI,CAAC,2BAA2B,CAAC,QAAQ,EAAE;YAAE,OAAM;QACnD,MAAM,IAAI,GAAG,KAAK,CAAC,kBAAkB,IAAI,2BAA2B,CAAC,iBAAiB,EAAE,IAAI,OAAO,CAAA;QACnG,mBAAmB,CAAC,YAAY,CAAC,iBAAiB,IAAI,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,CAAA;IAC3E,CAAC,CAAC,CACH,CAAA;IAED,+CAA+C;IAC/C,IAAI,CAAC,IAAI,CACP,YAAY,CAAC,WAAW,CAAC,kBAAkB,EAAE,CAAC,KAAK,EAAE,EAAE;QACrD,MAAM,WAAW,GAAG,KAAK,CAAC,WAAqB,CAAA;QAC/C,IAAI,CAAC,WAAW;YAAE,OAAM;QACxB,MAAM,GAAG,GAAG,iBAAiB,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,KAAK,WAAW,CAAC,CAAA;QACxF,IAAI,CAAC,GAAG;YAAE,OAAM;QAChB,uCAAuC;QACvC,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;YAChB,iBAAiB,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;QAChD,CAAC;aAAM,CAAC;YACN,iBAAiB,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,EAAC,cAAc,EAAE,IAAI,EAAC,CAAC,CAAA;QACjE,CAAC;IACH,CAAC,CAAC,CACH,CAAA;AACH,CAAC;AAED,MAAM,UAAU,qBAAqB;IACnC,KAAK,MAAM,GAAG,IAAI,IAAI;QAAE,GAAG,CAAC,MAAM,EAAE,CAAA;IACpC,IAAI,GAAG,EAAE,CAAA;AACX,CAAC","sourcesContent":["/**\n * Device-event router — engine-owned. Subscribes to the inbound native BLE/device\n * events and routes them into the engine runtime: device stores, the process event\n * bus, the photo/stream coordinators, and local miniapps (via forwardEvent).\n *\n * Why this exists: engine owns the stores, coordinators, miniapp runtime, and facades,\n * but it never *subscribed to the device* for most events — the host MantleManager was\n * still the event router the whole runtime secretly depended on. So a bare OEM that\n * imported engine + called engine.start() got a connected runtime with almost no device\n * data flowing in (no miniapp input, dead gallery sync, starved coordinators). This\n * service moves those inbound bridges into engine so ANY host gets them.\n *\n * Scope: the engine legs only. The Cloud V1 relays that used to live beside them in\n * MantleManager (touch→cloud, the cloud-SDK stream/photo legs, etc.) were deleted with\n * Cloud V1 app end-of-life.\n *\n * Started by `engine.start()`. Idempotent.\n */\nimport BluetoothSdk from \"@mentra/bluetooth-sdk/internal\"\nimport {shallow} from \"zustand/shallow\"\n\nimport localMiniappRuntime from \"./LocalMiniappRuntime\"\nimport localSttFallbackCoordinator from \"./LocalSttFallbackCoordinator\"\nimport {phonePhotoCoordinator} from \"./PhonePhotoCoordinator\"\nimport {phoneStreamCoordinator} from \"./PhoneStreamCoordinator\"\nimport {isGlassesConnected} from \"./GlassesReadiness\"\nimport {useGlassesStore} from \"../stores/glasses\"\nimport {useSettingsStore} from \"../stores/settings\"\nimport {useAppStatusStore} from \"../stores/apps\"\nimport {retirePendingSelectionOnPromotion} from \"./PairingIdentity\"\nimport GlobalEventEmitter from \"../utils/GlobalEventEmitter\"\nimport {asgCameraApi} from \"./asg/asgCameraApi\"\n\nlet subs: Array<{remove: () => void}> = []\n\nexport function startDeviceEventRouter(): void {\n if (subs.length) return\n\n // --- device state → engine stores ---\n\n // Standalone WiFi status → glasses store.\n subs.push(\n BluetoothSdk.addListener(\"wifi_status_change\", (event) => {\n const {type: _type, ...wifi} = event\n useGlassesStore.getState().setGlassesInfo({wifi})\n }),\n )\n\n // Forward glasses Wi-Fi to miniapps (session.glasses.onWifi) from the STORE — the\n // single source of truth — so every path converges here: wifi_status_change,\n // onGlassesStatus, and BLE disconnect. Effective connectivity requires the glasses\n // to be connected AND on Wi-Fi, so a disconnect correctly flips `connected` false.\n // `linkConnected` carries the BLE leg on its own so a miniapp can tell \"glasses\n // out of Bluetooth range\" from \"glasses on Bluetooth but off Wi-Fi\" — the two\n // need opposite recoveries (wait vs. open Wi-Fi setup).\n subs.push({\n remove: useGlassesStore.subscribe(\n (s) => {\n const linkConnected = isGlassesConnected(s.connection)\n const connected = linkConnected && s.wifi.state === \"connected\"\n // ssid/localIp describe how to reach the glasses over Wi-Fi, so they are\n // only meaningful under effective connectivity. Gate them on `connected`\n // (not raw wifi.state) so a BLE drop with a stale \"connected\" Wi-Fi leg\n // never hands miniapps an unreachable address.\n return {\n connected,\n linkConnected,\n ssid: linkConnected && s.wifi.state === \"connected\" ? s.wifi.ssid : undefined,\n localIp: linkConnected && s.wifi.state === \"connected\" ? s.wifi.localIp : undefined,\n }\n },\n (wifi) => localMiniappRuntime.forwardEvent(\"glasses_wifi\", wifi),\n {equalityFn: shallow},\n ),\n })\n\n // Incremental battery status → glasses store + local miniapps.\n subs.push(\n BluetoothSdk.addListener(\"battery_status\", (event) => {\n const state = useGlassesStore.getState()\n state.setBatteryInfo(event.level, event.charging, state.caseBatteryLevel, state.caseCharging)\n localMiniappRuntime.forwardEvent(\"glasses_battery_update\", {\n type: \"glasses_battery_update\",\n level: event.level,\n charging: event.charging,\n timestamp: event.timestamp ?? Date.now(),\n })\n }),\n )\n\n // Hotspot status → glasses store + event bus. engine's own gallerySyncService listens\n // on the bus for these, so without this bridge engine's gallery sync is dead.\n subs.push(\n BluetoothSdk.addListener(\"hotspot_status_change\", (event) => {\n const enabled = event.state === \"enabled\"\n const ssid = enabled ? event.ssid : \"\"\n const password = enabled ? event.password : \"\"\n const localIp = enabled ? event.localIp : \"\"\n useGlassesStore.getState().setHotspotInfo(enabled, ssid, password, localIp)\n // Always retarget the ASG camera client: on disable (or an event with no\n // IP) fall back to the localhost default so later calls fail fast instead\n // of hitting the previous network's stale glasses IP.\n asgCameraApi.setServer(localIp || \"localhost\", 8089)\n GlobalEventEmitter.emit(\"hotspot_status_change\", {enabled, ssid, password, local_ip: localIp})\n }),\n )\n subs.push(\n BluetoothSdk.addListener(\"hotspot_error\", (event) => {\n GlobalEventEmitter.emit(\"hotspot_error\", {error_message: event.errorMessage, timestamp: event.timestamp})\n }),\n )\n\n // Glasses gallery content counts → event bus (consumed by gallerySyncService).\n subs.push(\n BluetoothSdk.addListener(\"gallery_status\", (event) => {\n GlobalEventEmitter.emit(\"gallery_status\", {\n photos: event.photos,\n videos: event.videos,\n total: event.total,\n has_content: event.hasContent,\n camera_busy: event.cameraBusy,\n })\n }),\n )\n\n // Hardware-originated setting changes (user changes a setting ON the glasses) → store.\n // The inbound complement to GlassesSettingsSync (which only pushes store→device).\n subs.push(\n BluetoothSdk.addListener(\"save_setting\", async (event) => {\n const settings = useSettingsStore.getState()\n // Damp the relay: native re-echoes some settings unconditionally (e.g.\n // the identity block at every handleDeviceReady) — a same-value echo\n // must not become a store write (persistence churn + change-push noise).\n if (settings.getSetting(event.key) !== event.value) {\n await settings.setSetting(event.key, event.value)\n }\n // Two-phase identity: a promoted default retires the pending selection\n // marker. The rule lives with PairingIdentity (a no-op for other keys).\n await retirePendingSelectionOnPromotion(event.key, event.value)\n }),\n )\n\n // --- coordinators (owns()-gated) ---\n\n // Phone-owned photo errors settle the in-flight long-poll fast (vs. timeout).\n // Non-owned photo responses used to forward to the Cloud V1 photo pipeline via\n // restComms; that relay was removed with Cloud V1 app end-of-life, so they drop.\n // Wire v2 sends a short 4-hex BLE requestId; the coordinator maps it back to\n // the cloud requestId (resolveCloudRequestId) before ownership checks.\n subs.push(\n BluetoothSdk.addListener(\"photo_response\", (event) => {\n const cloudRequestId = event.requestId ? phonePhotoCoordinator.resolveCloudRequestId(event.requestId) : \"\"\n if (cloudRequestId && phonePhotoCoordinator.owns(cloudRequestId)) {\n if (event.state === \"error\") {\n phonePhotoCoordinator.handlePhotoError(\n event.requestId ?? cloudRequestId,\n event.errorCode ?? \"GLASSES_ERROR\",\n event.errorMessage ?? \"Glasses reported an error\",\n )\n }\n }\n }),\n )\n\n // Phone-owned stream status / keep-alive → the stream coordinator. The Cloud V1 relay\n // for non-owned streams was removed with Cloud V1 app end-of-life.\n subs.push(\n BluetoothSdk.addListener(\"stream_status\", (event) => {\n if (event.streamId && phoneStreamCoordinator.owns(event.streamId)) {\n phoneStreamCoordinator.handleGlassesStatus(event)\n }\n }),\n )\n subs.push(\n BluetoothSdk.addListener(\"keep_alive_ack\", (event) => {\n if (event.streamId && phoneStreamCoordinator.owns(event.streamId)) {\n phoneStreamCoordinator.handleKeepAliveAck(event)\n }\n }),\n )\n\n // --- device input → local miniapps (forwardEvent is subscriber-gated; a no-op when\n // no miniapp listens). The v1 SocketComms cloud legs for these were removed with\n // the Cloud V1 ripout. ---\n\n subs.push(\n BluetoothSdk.addListener(\"button_press\", (event) => {\n localMiniappRuntime.forwardEvent(\"button_press\", event)\n }),\n )\n subs.push(\n BluetoothSdk.addListener(\"touch_event\", (event) => {\n localMiniappRuntime.forwardEvent(\"touch_event\", event)\n }),\n )\n // G2 IMU accelerometer — payload already matches the miniapp AccelData shape.\n subs.push(\n BluetoothSdk.addListener(\"accel_event\", (event) => {\n localMiniappRuntime.forwardEvent(\"accel_event\", {\n x: event.x,\n y: event.y,\n z: event.z,\n timestamp: typeof event.timestamp === \"number\" ? event.timestamp : Date.now(),\n })\n }),\n )\n // Head position — translate native {up:boolean} → SDK {position:\"up\"|\"down\"}.\n subs.push(\n BluetoothSdk.addListener(\"head_up\", (event) => {\n localMiniappRuntime.forwardEvent(\"head_up\", {position: event.up ? \"up\" : \"down\", timestamp: Date.now()})\n }),\n )\n // On-device STT transcripts → local miniapps only while the local engine is\n // active (cloud fallback or an explicit forceLocal subscription). The runtime\n // filters mixed local/cloud delivery per miniapp to avoid double-delivery.\n // forwardEvent is subscriber-gated — a no-op when no miniapp listens.\n // (Was MantleManager.handle_local_transcription; its offline-captions display branch\n // went away with the pseudo captions renderer.)\n subs.push(\n BluetoothSdk.addListener(\"local_transcription\", (event) => {\n if (!localSttFallbackCoordinator.isActive()) return\n const lang = event.transcribeLanguage ?? localSttFallbackCoordinator.getActiveLanguage() ?? \"en-US\"\n localMiniappRuntime.forwardEvent(`transcription:${lang}`, event, \"local\")\n }),\n )\n\n // --- glasses swipe-menu app launcher (G2) ---\n subs.push(\n BluetoothSdk.addListener(\"miniapp_selected\", (event) => {\n const packageName = event.packageName as string\n if (!packageName) return\n const app = useAppStatusStore.getState().apps.find((a) => a.packageName === packageName)\n if (!app) return\n // Toggle: stop if running, else start.\n if (app.running) {\n useAppStatusStore.getState().stop(packageName)\n } else {\n useAppStatusStore.getState().start(app, {skipNavigation: true})\n }\n }),\n )\n}\n\nexport function stopDeviceEventRouter(): void {\n for (const sub of subs) sub.remove()\n subs = []\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"GlassesSettingsSync.d.ts","sourceRoot":"","sources":["../../src/services/GlassesSettingsSync.ts"],"names":[],"mappings":"AAuCA;;;;;;;;;GASG;AACH,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAChC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAOzB;AAED,uEAAuE;AACvE,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAO/F;AAKD;;;;;;GAMG;AACH,wBAAsB,wBAAwB,IAAI,OAAO,CAAC,IAAI,CAAC,CAM9D;AAED;;;;;;;GAOG;AACH,wBAAsB,2BAA2B,IAAI,OAAO,CAAC,IAAI,CAAC,CAGjE;AAED,wBAAgB,wBAAwB,IAAI,IAAI,CA8B/C;AAED,wBAAgB,uBAAuB,IAAI,IAAI,CAK9C"}
1
+ {"version":3,"file":"GlassesSettingsSync.d.ts","sourceRoot":"","sources":["../../src/services/GlassesSettingsSync.ts"],"names":[],"mappings":"AAwCA;;;;;;;;;GASG;AACH,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAChC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAOzB;AAED,uEAAuE;AACvE,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAO/F;AAKD;;;;;;GAMG;AACH,wBAAsB,wBAAwB,IAAI,OAAO,CAAC,IAAI,CAAC,CAM9D;AAED;;;;;;;GAOG;AACH,wBAAsB,2BAA2B,IAAI,OAAO,CAAC,IAAI,CAAC,CAGjE;AAED,wBAAgB,wBAAwB,IAAI,IAAI,CAuC/C;AAED,wBAAgB,uBAAuB,IAAI,IAAI,CAK9C"}
@@ -19,6 +19,7 @@ import { useGlassesStore } from "../stores/glasses";
19
19
  import { createDebouncedPatchFlusher } from "../utils/debouncedPatch";
20
20
  import { isGlassesConnected } from "./GlassesReadiness";
21
21
  import micStateCoordinator from "./MicStateCoordinator";
22
+ import { phoneCameraFovCoordinator } from "./PhoneCameraFovCoordinator";
22
23
  /**
23
24
  * Change-pushes are debounced (300ms) and merged so a burst of setSetting
24
25
  * calls becomes ONE BLE write (wire v2 keeps BLE JSON small and infrequent).
@@ -111,8 +112,17 @@ export function startGlassesSettingsSync() {
111
112
  const connected = isGlassesConnected(useGlassesStore.getState().connection);
112
113
  if (connected && !wasConnected) {
113
114
  // Background sync: log-and-continue if the device drops right after connect.
114
- void pushDeviceSettingsOnConnect().catch((error) => {
115
+ void pushDeviceSettingsOnConnect()
116
+ .catch((error) => {
115
117
  console.warn("GlassesSettingsSync: on-connect settings push failed:", error);
118
+ })
119
+ .finally(() => {
120
+ // That push replays the persistent camera_fov, which stomps whatever
121
+ // override a miniapp currently owns. Re-assert after it either way:
122
+ // a failed push may still have written some keys.
123
+ void phoneCameraFovCoordinator.reapplyEffectiveOverride().catch((error) => {
124
+ console.warn("GlassesSettingsSync: camera FOV re-apply failed:", error);
125
+ });
116
126
  });
117
127
  }
118
128
  wasConnected = connected;
@@ -1 +1 @@
1
- {"version":3,"file":"GlassesSettingsSync.js","sourceRoot":"","sources":["../../src/services/GlassesSettingsSync.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Glasses settings sync — engine-owned. Keeps the connected glasses in sync with\n * the phone's device settings (`BLUETOOTH_SETTING_KEYS`) over the bluetooth-sdk, so\n * `engine.glasses.settings.set()` (and any other settings-store write) reaches the\n * device for ANY host — not just the first-party Mentra app, where this used to live\n * in MantleManager.\n *\n * Two triggers:\n * 1. on change — push the keys that changed.\n * 2. on (re)connect — push the FULL set, so a freshly-connected device gets the\n * phone's current settings (not just future changes).\n *\n * Started by `engine.start()`. Idempotent.\n */\nimport {shallow} from \"zustand/shallow\"\n\nimport BluetoothSdk from \"@mentra/bluetooth-sdk/internal\"\nimport {useSettingsStore, PAIRING_IDENTITY_KEYS} from \"../stores/settings\"\nimport {useGlassesStore} from \"../stores/glasses\"\nimport {createDebouncedPatchFlusher} from \"../utils/debouncedPatch\"\nimport {isGlassesConnected} from \"./GlassesReadiness\"\nimport micStateCoordinator from \"./MicStateCoordinator\"\n\n/**\n * Change-pushes are debounced (300ms) and merged so a burst of setSetting\n * calls becomes ONE BLE write (wire v2 keeps BLE JSON small and infrequent).\n * The full on-connect/seed pushes stay direct — callers await their ordering.\n */\nconst flushBluetoothSettingsPatch = createDebouncedPatchFlusher<Record<string, unknown>>((patch) => {\n // Settings can change while the glasses are disconnected — a native\n // rejection must not surface as an unhandled promise rejection.\n // Apply mic overrides at flush time, not enqueue time: raw PCM can stop\n // during the debounce window and a captured VAD=false would then be stale.\n const runtimePatch = micStateCoordinator.applyRuntimeOverrides(patch)\n void Promise.resolve(BluetoothSdk.updateBluetoothSettings(runtimePatch)).catch((error) => {\n console.warn(\"GlassesSettingsSync: updateBluetoothSettings failed:\", error)\n })\n}, 300)\n\n/**\n * The changed-keys diff for the change-push, MINUS the pairing-identity keys.\n * Identity is native-authoritative (promoted on pairing success, cleared on\n * forget, echoed down via save_setting) and reaches native only through the\n * explicit full seeds. Pushing identity from the change subscription would\n * close a feedback loop: native echo → store setSetting → change-push →\n * native apply → echo…, which self-sustains the moment two different values\n * are in flight (e.g. a boot demotion crossing a native promotion) and flaps\n * the pairing UI.\n */\nexport function diffBluetoothSettingsForPush(\n settings: Record<string, unknown>,\n previous: Record<string, unknown>,\n): Record<string, unknown> {\n const changed: Record<string, unknown> = {}\n for (const key in settings) {\n if (PAIRING_IDENTITY_KEYS.includes(key)) continue\n if (settings[key] !== previous[key]) changed[key] = settings[key]\n }\n return changed\n}\n\n/** A copy of the settings record without the pairing-identity keys. */\nexport function stripPairingIdentity(settings: Record<string, unknown>): Record<string, unknown> {\n const stripped: Record<string, unknown> = {}\n for (const key in settings) {\n if (PAIRING_IDENTITY_KEYS.includes(key)) continue\n stripped[key] = settings[key]\n }\n return stripped\n}\n\nlet unsubChange: (() => void) | null = null\nlet unsubConnect: (() => void) | null = null\n\n/**\n * Push the FULL current device-settings set — identity included — to native.\n * This is the explicit identity SEED, for the flows where native genuinely\n * needs the phone's persisted identity BEFORE it can act: the device-store\n * hydration at start, the pre-connect seed (connectDefault targets the seeded\n * identity), the post-demotion re-push, and the abandon re-seed.\n */\nexport async function pushAllBluetoothSettings(): Promise<void> {\n // Returns the native write promise so callers can await the seed before the\n // connect handshake replays settings to the glasses (otherwise the handshake\n // can race ahead and replay stale native settings).\n const settings = useSettingsStore.getState().getBluetoothSettings()\n await BluetoothSdk.updateBluetoothSettings(micStateCoordinator.applyRuntimeOverrides(settings))\n}\n\n/**\n * Push the device-settings set MINUS the pairing identity. The on-connect\n * replay uses this: while connected, NATIVE owns the identity (it promotes at\n * device-ready and echoes down), and the JS snapshot can be mid-relay stale —\n * on Mentra Live \"connected\" lands together with device-ready, and a full\n * replay raced the promotion echoes and overwrote the just-promoted identity\n * with pre-promotion empties (wiping the pairing it had just made).\n */\nexport async function pushDeviceSettingsOnConnect(): Promise<void> {\n const settings = stripPairingIdentity(useSettingsStore.getState().getBluetoothSettings())\n await BluetoothSdk.updateBluetoothSettings(micStateCoordinator.applyRuntimeOverrides(settings))\n}\n\nexport function startGlassesSettingsSync(): void {\n if (unsubChange || unsubConnect) return\n\n // 1. Push only the keys that changed (matching the prior MantleManager sync).\n // Pairing identity is excluded — see diffBluetoothSettingsForPush.\n unsubChange = useSettingsStore.subscribe(\n (state) => state.getBluetoothSettings(),\n (settings: Record<string, unknown>, previous: Record<string, unknown>) => {\n const changed = diffBluetoothSettingsForPush(settings, previous)\n if (Object.keys(changed).length > 0) {\n flushBluetoothSettingsPatch(changed)\n }\n },\n {equalityFn: shallow},\n )\n\n // 2. Push the device settings whenever the glasses transition to connected —\n // identity EXCLUDED: native owns the identity it just promoted, and a full\n // replay here raced the promotion echoes (see pushDeviceSettingsOnConnect).\n let wasConnected = isGlassesConnected(useGlassesStore.getState().connection)\n unsubConnect = useGlassesStore.subscribe(() => {\n const connected = isGlassesConnected(useGlassesStore.getState().connection)\n if (connected && !wasConnected) {\n // Background sync: log-and-continue if the device drops right after connect.\n void pushDeviceSettingsOnConnect().catch((error) => {\n console.warn(\"GlassesSettingsSync: on-connect settings push failed:\", error)\n })\n }\n wasConnected = connected\n })\n}\n\nexport function stopGlassesSettingsSync(): void {\n unsubChange?.()\n unsubConnect?.()\n unsubChange = null\n unsubConnect = null\n}\n"]}
1
+ {"version":3,"file":"GlassesSettingsSync.js","sourceRoot":"","sources":["../../src/services/GlassesSettingsSync.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAC,OAAO,EAAC,MAAM,iBAAiB,CAAA;AAEvC,OAAO,YAAY,MAAM,gCAAgC,CAAA;AACzD,OAAO,EAAC,gBAAgB,EAAE,qBAAqB,EAAC,MAAM,oBAAoB,CAAA;AAC1E,OAAO,EAAC,eAAe,EAAC,MAAM,mBAAmB,CAAA;AACjD,OAAO,EAAC,2BAA2B,EAAC,MAAM,yBAAyB,CAAA;AACnE,OAAO,EAAC,kBAAkB,EAAC,MAAM,oBAAoB,CAAA;AACrD,OAAO,mBAAmB,MAAM,uBAAuB,CAAA;AACvD,OAAO,EAAC,yBAAyB,EAAC,MAAM,6BAA6B,CAAA;AAErE;;;;GAIG;AACH,MAAM,2BAA2B,GAAG,2BAA2B,CAA0B,CAAC,KAAK,EAAE,EAAE;IACjG,oEAAoE;IACpE,gEAAgE;IAChE,wEAAwE;IACxE,2EAA2E;IAC3E,MAAM,YAAY,GAAG,mBAAmB,CAAC,qBAAqB,CAAC,KAAK,CAAC,CAAA;IACrE,KAAK,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,uBAAuB,CAAC,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;QACvF,OAAO,CAAC,IAAI,CAAC,sDAAsD,EAAE,KAAK,CAAC,CAAA;IAC7E,CAAC,CAAC,CAAA;AACJ,CAAC,EAAE,GAAG,CAAC,CAAA;AAEP;;;;;;;;;GASG;AACH,MAAM,UAAU,4BAA4B,CAC1C,QAAiC,EACjC,QAAiC;IAEjC,MAAM,OAAO,GAA4B,EAAE,CAAA;IAC3C,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,IAAI,qBAAqB,CAAC,QAAQ,CAAC,GAAG,CAAC;YAAE,SAAQ;QACjD,IAAI,QAAQ,CAAC,GAAG,CAAC,KAAK,QAAQ,CAAC,GAAG,CAAC;YAAE,OAAO,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAA;IACnE,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,oBAAoB,CAAC,QAAiC;IACpE,MAAM,QAAQ,GAA4B,EAAE,CAAA;IAC5C,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,IAAI,qBAAqB,CAAC,QAAQ,CAAC,GAAG,CAAC;YAAE,SAAQ;QACjD,QAAQ,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAA;IAC/B,CAAC;IACD,OAAO,QAAQ,CAAA;AACjB,CAAC;AAED,IAAI,WAAW,GAAwB,IAAI,CAAA;AAC3C,IAAI,YAAY,GAAwB,IAAI,CAAA;AAE5C;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB;IAC5C,4EAA4E;IAC5E,6EAA6E;IAC7E,oDAAoD;IACpD,MAAM,QAAQ,GAAG,gBAAgB,CAAC,QAAQ,EAAE,CAAC,oBAAoB,EAAE,CAAA;IACnE,MAAM,YAAY,CAAC,uBAAuB,CAAC,mBAAmB,CAAC,qBAAqB,CAAC,QAAQ,CAAC,CAAC,CAAA;AACjG,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,2BAA2B;IAC/C,MAAM,QAAQ,GAAG,oBAAoB,CAAC,gBAAgB,CAAC,QAAQ,EAAE,CAAC,oBAAoB,EAAE,CAAC,CAAA;IACzF,MAAM,YAAY,CAAC,uBAAuB,CAAC,mBAAmB,CAAC,qBAAqB,CAAC,QAAQ,CAAC,CAAC,CAAA;AACjG,CAAC;AAED,MAAM,UAAU,wBAAwB;IACtC,IAAI,WAAW,IAAI,YAAY;QAAE,OAAM;IAEvC,8EAA8E;IAC9E,mEAAmE;IACnE,WAAW,GAAG,gBAAgB,CAAC,SAAS,CACtC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,oBAAoB,EAAE,EACvC,CAAC,QAAiC,EAAE,QAAiC,EAAE,EAAE;QACvE,MAAM,OAAO,GAAG,4BAA4B,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAA;QAChE,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpC,2BAA2B,CAAC,OAAO,CAAC,CAAA;QACtC,CAAC;IACH,CAAC,EACD,EAAC,UAAU,EAAE,OAAO,EAAC,CACtB,CAAA;IAED,6EAA6E;IAC7E,2EAA2E;IAC3E,4EAA4E;IAC5E,IAAI,YAAY,GAAG,kBAAkB,CAAC,eAAe,CAAC,QAAQ,EAAE,CAAC,UAAU,CAAC,CAAA;IAC5E,YAAY,GAAG,eAAe,CAAC,SAAS,CAAC,GAAG,EAAE;QAC5C,MAAM,SAAS,GAAG,kBAAkB,CAAC,eAAe,CAAC,QAAQ,EAAE,CAAC,UAAU,CAAC,CAAA;QAC3E,IAAI,SAAS,IAAI,CAAC,YAAY,EAAE,CAAC;YAC/B,6EAA6E;YAC7E,KAAK,2BAA2B,EAAE;iBAC/B,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;gBACf,OAAO,CAAC,IAAI,CAAC,uDAAuD,EAAE,KAAK,CAAC,CAAA;YAC9E,CAAC,CAAC;iBACD,OAAO,CAAC,GAAG,EAAE;gBACZ,qEAAqE;gBACrE,oEAAoE;gBACpE,kDAAkD;gBAClD,KAAK,yBAAyB,CAAC,wBAAwB,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;oBACxE,OAAO,CAAC,IAAI,CAAC,kDAAkD,EAAE,KAAK,CAAC,CAAA;gBACzE,CAAC,CAAC,CAAA;YACJ,CAAC,CAAC,CAAA;QACN,CAAC;QACD,YAAY,GAAG,SAAS,CAAA;IAC1B,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,UAAU,uBAAuB;IACrC,WAAW,EAAE,EAAE,CAAA;IACf,YAAY,EAAE,EAAE,CAAA;IAChB,WAAW,GAAG,IAAI,CAAA;IAClB,YAAY,GAAG,IAAI,CAAA;AACrB,CAAC","sourcesContent":["/**\n * Glasses settings sync — engine-owned. Keeps the connected glasses in sync with\n * the phone's device settings (`BLUETOOTH_SETTING_KEYS`) over the bluetooth-sdk, so\n * `engine.glasses.settings.set()` (and any other settings-store write) reaches the\n * device for ANY host — not just the first-party Mentra app, where this used to live\n * in MantleManager.\n *\n * Two triggers:\n * 1. on change — push the keys that changed.\n * 2. on (re)connect — push the FULL set, so a freshly-connected device gets the\n * phone's current settings (not just future changes).\n *\n * Started by `engine.start()`. Idempotent.\n */\nimport {shallow} from \"zustand/shallow\"\n\nimport BluetoothSdk from \"@mentra/bluetooth-sdk/internal\"\nimport {useSettingsStore, PAIRING_IDENTITY_KEYS} from \"../stores/settings\"\nimport {useGlassesStore} from \"../stores/glasses\"\nimport {createDebouncedPatchFlusher} from \"../utils/debouncedPatch\"\nimport {isGlassesConnected} from \"./GlassesReadiness\"\nimport micStateCoordinator from \"./MicStateCoordinator\"\nimport {phoneCameraFovCoordinator} from \"./PhoneCameraFovCoordinator\"\n\n/**\n * Change-pushes are debounced (300ms) and merged so a burst of setSetting\n * calls becomes ONE BLE write (wire v2 keeps BLE JSON small and infrequent).\n * The full on-connect/seed pushes stay direct — callers await their ordering.\n */\nconst flushBluetoothSettingsPatch = createDebouncedPatchFlusher<Record<string, unknown>>((patch) => {\n // Settings can change while the glasses are disconnected — a native\n // rejection must not surface as an unhandled promise rejection.\n // Apply mic overrides at flush time, not enqueue time: raw PCM can stop\n // during the debounce window and a captured VAD=false would then be stale.\n const runtimePatch = micStateCoordinator.applyRuntimeOverrides(patch)\n void Promise.resolve(BluetoothSdk.updateBluetoothSettings(runtimePatch)).catch((error) => {\n console.warn(\"GlassesSettingsSync: updateBluetoothSettings failed:\", error)\n })\n}, 300)\n\n/**\n * The changed-keys diff for the change-push, MINUS the pairing-identity keys.\n * Identity is native-authoritative (promoted on pairing success, cleared on\n * forget, echoed down via save_setting) and reaches native only through the\n * explicit full seeds. Pushing identity from the change subscription would\n * close a feedback loop: native echo → store setSetting → change-push →\n * native apply → echo…, which self-sustains the moment two different values\n * are in flight (e.g. a boot demotion crossing a native promotion) and flaps\n * the pairing UI.\n */\nexport function diffBluetoothSettingsForPush(\n settings: Record<string, unknown>,\n previous: Record<string, unknown>,\n): Record<string, unknown> {\n const changed: Record<string, unknown> = {}\n for (const key in settings) {\n if (PAIRING_IDENTITY_KEYS.includes(key)) continue\n if (settings[key] !== previous[key]) changed[key] = settings[key]\n }\n return changed\n}\n\n/** A copy of the settings record without the pairing-identity keys. */\nexport function stripPairingIdentity(settings: Record<string, unknown>): Record<string, unknown> {\n const stripped: Record<string, unknown> = {}\n for (const key in settings) {\n if (PAIRING_IDENTITY_KEYS.includes(key)) continue\n stripped[key] = settings[key]\n }\n return stripped\n}\n\nlet unsubChange: (() => void) | null = null\nlet unsubConnect: (() => void) | null = null\n\n/**\n * Push the FULL current device-settings set — identity included — to native.\n * This is the explicit identity SEED, for the flows where native genuinely\n * needs the phone's persisted identity BEFORE it can act: the device-store\n * hydration at start, the pre-connect seed (connectDefault targets the seeded\n * identity), the post-demotion re-push, and the abandon re-seed.\n */\nexport async function pushAllBluetoothSettings(): Promise<void> {\n // Returns the native write promise so callers can await the seed before the\n // connect handshake replays settings to the glasses (otherwise the handshake\n // can race ahead and replay stale native settings).\n const settings = useSettingsStore.getState().getBluetoothSettings()\n await BluetoothSdk.updateBluetoothSettings(micStateCoordinator.applyRuntimeOverrides(settings))\n}\n\n/**\n * Push the device-settings set MINUS the pairing identity. The on-connect\n * replay uses this: while connected, NATIVE owns the identity (it promotes at\n * device-ready and echoes down), and the JS snapshot can be mid-relay stale —\n * on Mentra Live \"connected\" lands together with device-ready, and a full\n * replay raced the promotion echoes and overwrote the just-promoted identity\n * with pre-promotion empties (wiping the pairing it had just made).\n */\nexport async function pushDeviceSettingsOnConnect(): Promise<void> {\n const settings = stripPairingIdentity(useSettingsStore.getState().getBluetoothSettings())\n await BluetoothSdk.updateBluetoothSettings(micStateCoordinator.applyRuntimeOverrides(settings))\n}\n\nexport function startGlassesSettingsSync(): void {\n if (unsubChange || unsubConnect) return\n\n // 1. Push only the keys that changed (matching the prior MantleManager sync).\n // Pairing identity is excluded — see diffBluetoothSettingsForPush.\n unsubChange = useSettingsStore.subscribe(\n (state) => state.getBluetoothSettings(),\n (settings: Record<string, unknown>, previous: Record<string, unknown>) => {\n const changed = diffBluetoothSettingsForPush(settings, previous)\n if (Object.keys(changed).length > 0) {\n flushBluetoothSettingsPatch(changed)\n }\n },\n {equalityFn: shallow},\n )\n\n // 2. Push the device settings whenever the glasses transition to connected —\n // identity EXCLUDED: native owns the identity it just promoted, and a full\n // replay here raced the promotion echoes (see pushDeviceSettingsOnConnect).\n let wasConnected = isGlassesConnected(useGlassesStore.getState().connection)\n unsubConnect = useGlassesStore.subscribe(() => {\n const connected = isGlassesConnected(useGlassesStore.getState().connection)\n if (connected && !wasConnected) {\n // Background sync: log-and-continue if the device drops right after connect.\n void pushDeviceSettingsOnConnect()\n .catch((error) => {\n console.warn(\"GlassesSettingsSync: on-connect settings push failed:\", error)\n })\n .finally(() => {\n // That push replays the persistent camera_fov, which stomps whatever\n // override a miniapp currently owns. Re-assert after it either way:\n // a failed push may still have written some keys.\n void phoneCameraFovCoordinator.reapplyEffectiveOverride().catch((error) => {\n console.warn(\"GlassesSettingsSync: camera FOV re-apply failed:\", error)\n })\n })\n }\n wasConnected = connected\n })\n}\n\nexport function stopGlassesSettingsSync(): void {\n unsubChange?.()\n unsubConnect?.()\n unsubChange = null\n unsubConnect = null\n}\n"]}
@@ -12,6 +12,18 @@
12
12
  * Started by `engine.start()`. Idempotent.
13
13
  */
14
14
  import { type PublicGlassesStatus } from "@mentra/bluetooth-sdk";
15
+ /** Miniapp `session.glasses.onConnection` payload. */
16
+ export type MiniappConnectionData = {
17
+ connected: boolean;
18
+ modelName?: string;
19
+ };
20
+ /**
21
+ * Native `glasses_status` is a store delta (`connection.state`, battery, wifi…).
22
+ * Miniapps subscribe to a boolean `connected` field. Forwarding the raw status
23
+ * makes `Boolean(data.connected)` false on every heartbeat while the Mentra App
24
+ * still shows the glasses linked.
25
+ */
26
+ export declare function toMiniappConnectionData(status: unknown): MiniappConnectionData | null;
15
27
  export declare function startGlassesStatusProjection(forwarder?: (status: Partial<PublicGlassesStatus>) => void): Promise<void>;
16
28
  export declare function stopGlassesStatusProjection(): void;
17
29
  //# sourceMappingURL=GlassesStatusProjection.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"GlassesStatusProjection.d.ts","sourceRoot":"","sources":["../../src/services/GlassesStatusProjection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAqB,EAAC,KAAK,mBAAmB,EAAC,MAAM,uBAAuB,CAAA;AAS5E,wBAAgB,4BAA4B,CAC1C,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,mBAAmB,CAAC,KAAK,IAAI,GACzD,OAAO,CAAC,IAAI,CAAC,CAiDf;AAED,wBAAgB,2BAA2B,IAAI,IAAI,CAMlD"}
1
+ {"version":3,"file":"GlassesStatusProjection.d.ts","sourceRoot":"","sources":["../../src/services/GlassesStatusProjection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAqB,EAAC,KAAK,mBAAmB,EAAC,MAAM,uBAAuB,CAAA;AAK5E,sDAAsD;AACtD,MAAM,MAAM,qBAAqB,GAAG;IAClC,SAAS,EAAE,OAAO,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,OAAO,GAAG,qBAAqB,GAAG,IAAI,CAarF;AAOD,wBAAgB,4BAA4B,CAC1C,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,mBAAmB,CAAC,KAAK,IAAI,GACzD,OAAO,CAAC,IAAI,CAAC,CAiDf;AAED,wBAAgB,2BAA2B,IAAI,IAAI,CAMlD"}
@@ -14,6 +14,29 @@
14
14
  import BluetoothSdk from "@mentra/bluetooth-sdk";
15
15
  import { useCoreStore } from "../stores/core";
16
16
  import { useGlassesStore } from "../stores/glasses";
17
+ import { isGlassesConnected } from "./GlassesReadiness";
18
+ /**
19
+ * Native `glasses_status` is a store delta (`connection.state`, battery, wifi…).
20
+ * Miniapps subscribe to a boolean `connected` field. Forwarding the raw status
21
+ * makes `Boolean(data.connected)` false on every heartbeat while the Mentra App
22
+ * still shows the glasses linked.
23
+ */
24
+ export function toMiniappConnectionData(status) {
25
+ if (!status || typeof status !== "object")
26
+ return null;
27
+ const rec = status;
28
+ if (typeof rec.connected === "boolean") {
29
+ const modelName = rec.modelName || rec.deviceModel;
30
+ return { connected: rec.connected, ...(modelName ? { modelName } : {}) };
31
+ }
32
+ if (!rec.connection)
33
+ return null;
34
+ const modelName = rec.deviceModel;
35
+ return {
36
+ connected: isGlassesConnected(rec.connection),
37
+ ...(modelName ? { modelName } : {}),
38
+ };
39
+ }
17
40
  let unsubs = [];
18
41
  let projectionRunId = 0;
19
42
  let glassesStatusForwarder = null;
@@ -1 +1 @@
1
- {"version":3,"file":"GlassesStatusProjection.js","sourceRoot":"","sources":["../../src/services/GlassesStatusProjection.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Glasses status projection — engine-owned. Subscribes to the native bluetooth-sdk\n * status events and projects them into the engine device stores, so engine's own\n * services (which react to those stores) work for ANY host — not just the first-party\n * Mentra app, where this used to live in MantleManager.\n *\n * This is the INBOUND mirror of GlassesSettingsSync: that pushes the settings store\n * OUT to the device; this pulls device status IN to the stores. It's the feed that\n * the rest of the engine runtime depends on (MicStateCoordinator, the settings-sync\n * on-connect trigger, the facade read-models all read these stores).\n *\n * Started by `engine.start()`. Idempotent.\n */\nimport BluetoothSdk, {type PublicGlassesStatus} from \"@mentra/bluetooth-sdk\"\nimport {useCoreStore} from \"../stores/core\"\nimport {useGlassesStore} from \"../stores/glasses\"\n\nlet unsubs: Array<() => void> = []\nlet projectionRunId = 0\nlet glassesStatusForwarder: ((status: Partial<PublicGlassesStatus>) => void) | null = null\nlet hydrationPromise: Promise<void> | null = null\n\nexport function startGlassesStatusProjection(\n forwarder?: (status: Partial<PublicGlassesStatus>) => void,\n): Promise<void> {\n if (forwarder) glassesStatusForwarder = forwarder\n if (unsubs.length) return hydrationPromise ?? Promise.resolve()\n\n const runId = ++projectionRunId\n let bluetoothEventSeen = false\n let glassesEventSeen = false\n\n const bluetoothHydration = BluetoothSdk.getBluetoothStatus()\n .then((status) => {\n if (runId !== projectionRunId || bluetoothEventSeen) return\n useCoreStore.getState().setCoreInfo(status)\n })\n .catch((error) => {\n console.warn(\"GlassesStatusProjection: getBluetoothStatus failed\", error)\n })\n\n const glassesHydration = BluetoothSdk.getGlassesStatus()\n .then((status) => {\n if (runId !== projectionRunId || glassesEventSeen) return\n useGlassesStore.getState().setGlassesInfo(status)\n })\n .catch((error) => {\n console.warn(\"GlassesStatusProjection: getGlassesStatus failed\", error)\n })\n\n // Bluetooth-adapter status -> core store.\n unsubs.push(\n BluetoothSdk.subscribeBluetoothStatus((changed) => {\n bluetoothEventSeen = true\n useCoreStore.getState().setCoreInfo(changed)\n }),\n )\n\n // Glasses status -> glasses store (+ optional full-runtime forwarding to local\n // miniapps; clear any stale OTA-available flag on disconnect).\n unsubs.push(\n BluetoothSdk.subscribeGlassesStatus((changed) => {\n glassesEventSeen = true\n useGlassesStore.getState().setGlassesInfo(changed)\n glassesStatusForwarder?.(changed)\n if (changed.connection?.state === \"disconnected\") {\n useGlassesStore.getState().setOtaUpdateAvailable(null)\n }\n }),\n )\n\n hydrationPromise = Promise.allSettled([bluetoothHydration, glassesHydration]).then(() => undefined)\n return hydrationPromise\n}\n\nexport function stopGlassesStatusProjection(): void {\n projectionRunId++\n unsubs.forEach((unsub) => unsub())\n unsubs = []\n glassesStatusForwarder = null\n hydrationPromise = null\n}\n"]}
1
+ {"version":3,"file":"GlassesStatusProjection.js","sourceRoot":"","sources":["../../src/services/GlassesStatusProjection.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Glasses status projection — engine-owned. Subscribes to the native bluetooth-sdk\n * status events and projects them into the engine device stores, so engine's own\n * services (which react to those stores) work for ANY host — not just the first-party\n * Mentra app, where this used to live in MantleManager.\n *\n * This is the INBOUND mirror of GlassesSettingsSync: that pushes the settings store\n * OUT to the device; this pulls device status IN to the stores. It's the feed that\n * the rest of the engine runtime depends on (MicStateCoordinator, the settings-sync\n * on-connect trigger, the facade read-models all read these stores).\n *\n * Started by `engine.start()`. Idempotent.\n */\nimport BluetoothSdk, {type PublicGlassesStatus} from \"@mentra/bluetooth-sdk\"\nimport {useCoreStore} from \"../stores/core\"\nimport {useGlassesStore} from \"../stores/glasses\"\nimport {isGlassesConnected} from \"./GlassesReadiness\"\n\n/** Miniapp `session.glasses.onConnection` payload. */\nexport type MiniappConnectionData = {\n connected: boolean\n modelName?: string\n}\n\n/**\n * Native `glasses_status` is a store delta (`connection.state`, battery, wifi…).\n * Miniapps subscribe to a boolean `connected` field. Forwarding the raw status\n * makes `Boolean(data.connected)` false on every heartbeat while the Mentra App\n * still shows the glasses linked.\n */\nexport function toMiniappConnectionData(status: unknown): MiniappConnectionData | null {\n if (!status || typeof status !== \"object\") return null\n const rec = status as Partial<PublicGlassesStatus> & {connected?: boolean; modelName?: string}\n if (typeof rec.connected === \"boolean\") {\n const modelName = rec.modelName || rec.deviceModel\n return {connected: rec.connected, ...(modelName ? {modelName} : {})}\n }\n if (!rec.connection) return null\n const modelName = rec.deviceModel\n return {\n connected: isGlassesConnected(rec.connection),\n ...(modelName ? {modelName} : {}),\n }\n}\n\nlet unsubs: Array<() => void> = []\nlet projectionRunId = 0\nlet glassesStatusForwarder: ((status: Partial<PublicGlassesStatus>) => void) | null = null\nlet hydrationPromise: Promise<void> | null = null\n\nexport function startGlassesStatusProjection(\n forwarder?: (status: Partial<PublicGlassesStatus>) => void,\n): Promise<void> {\n if (forwarder) glassesStatusForwarder = forwarder\n if (unsubs.length) return hydrationPromise ?? Promise.resolve()\n\n const runId = ++projectionRunId\n let bluetoothEventSeen = false\n let glassesEventSeen = false\n\n const bluetoothHydration = BluetoothSdk.getBluetoothStatus()\n .then((status) => {\n if (runId !== projectionRunId || bluetoothEventSeen) return\n useCoreStore.getState().setCoreInfo(status)\n })\n .catch((error) => {\n console.warn(\"GlassesStatusProjection: getBluetoothStatus failed\", error)\n })\n\n const glassesHydration = BluetoothSdk.getGlassesStatus()\n .then((status) => {\n if (runId !== projectionRunId || glassesEventSeen) return\n useGlassesStore.getState().setGlassesInfo(status)\n })\n .catch((error) => {\n console.warn(\"GlassesStatusProjection: getGlassesStatus failed\", error)\n })\n\n // Bluetooth-adapter status -> core store.\n unsubs.push(\n BluetoothSdk.subscribeBluetoothStatus((changed) => {\n bluetoothEventSeen = true\n useCoreStore.getState().setCoreInfo(changed)\n }),\n )\n\n // Glasses status -> glasses store (+ optional full-runtime forwarding to local\n // miniapps; clear any stale OTA-available flag on disconnect).\n unsubs.push(\n BluetoothSdk.subscribeGlassesStatus((changed) => {\n glassesEventSeen = true\n useGlassesStore.getState().setGlassesInfo(changed)\n glassesStatusForwarder?.(changed)\n if (changed.connection?.state === \"disconnected\") {\n useGlassesStore.getState().setOtaUpdateAvailable(null)\n }\n }),\n )\n\n hydrationPromise = Promise.allSettled([bluetoothHydration, glassesHydration]).then(() => undefined)\n return hydrationPromise\n}\n\nexport function stopGlassesStatusProjection(): void {\n projectionRunId++\n unsubs.forEach((unsub) => unsub())\n unsubs = []\n glassesStatusForwarder = null\n hydrationPromise = null\n}\n"]}
@@ -359,6 +359,12 @@ declare class LocalMiniappRuntime {
359
359
  private handleStreamStop;
360
360
  private handleManagedStreamStart;
361
361
  private handleManagedStreamStop;
362
+ private ensureMeetingStateBridge;
363
+ private handleMeetingJoin;
364
+ private handleMeetingLeave;
365
+ private handleMeetingSetMuted;
366
+ private handleMeetingUpdateVideoSource;
367
+ private handleMeetingGetState;
362
368
  /**
363
369
  * session.glasses.requestWifiSetup — open the phone's glasses Wi-Fi setup
364
370
  * flow. The host owns the actual UI via the `engine.configure({ui})` seam;
@@ -1 +1 @@
1
- {"version":3,"file":"LocalMiniappRuntime.d.ts","sourceRoot":"","sources":["../../src/services/LocalMiniappRuntime.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"LocalMiniappRuntime.d.ts","sourceRoot":"","sources":["../../src/services/LocalMiniappRuntime.ts"],"names":[],"mappings":"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"}
@@ -45,7 +45,7 @@ import { miniappLauncher } from "./MiniappLauncher";
45
45
  import { ISLAND_SETTINGS_KEYS, } from "../runtime/config";
46
46
  import { getAnalytics, getUiSeams } from "../runtime/bootstrap";
47
47
  import { invokeScanQrSeam } from "../runtime/scanQrSeam";
48
- import { normalizeStreamAudioConfig, normalizeStreamVideoConfig } from "../runtime/streamConfig";
48
+ import { normalizeStreamAudioConfig, normalizeStreamVideoConfig, resolveCaptureAudio } from "../runtime/streamConfig";
49
49
  import { toLanguageHint } from "@mentra/cloud-protocol/languages";
50
50
  import { buildMiniappManifestSnapshot } from "../utils/miniappDiagnostics";
51
51
  import ttsModelManager from "./TTSModelManager";
@@ -56,6 +56,7 @@ import { resolveForegroundLocationPermission } from "./ForegroundLocationPermiss
56
56
  import { advanceMiniappPingLiveness } from "./MiniappLiveness";
57
57
  import { listPhoneCalendarEvents, PhoneCalendarError } from "./PhoneCalendarService";
58
58
  import { LocalMiniappStorage } from "./LocalMiniappStorage";
59
+ import acsMeetingService, { parseAcsOutgoingVideo, resolveAcsAudioSource } from "./AcsMeetingService";
59
60
  /**
60
61
  * Strictness ordering for `location_stream` rate values. Higher index =
61
62
  * higher accuracy and higher power cost. The aggregate tier applied to
@@ -780,6 +781,9 @@ class LocalMiniappRuntime {
780
781
  void phoneVideoCoordinator.stopForApp(packageName).catch((error) => {
781
782
  console.warn(`${LOG_TAG}: failed to stop video recording for ${packageName} on unregister`, error);
782
783
  });
784
+ void acsMeetingService.leaveIfOwner(packageName).catch((error) => {
785
+ console.warn(`${LOG_TAG}: failed to leave ACS meeting for ${packageName} on unregister`, error);
786
+ });
783
787
  // Detach the per-app nav event forwarder but leave the native nav session
784
788
  // running. The user may have just closed the mini-app UI and will reopen
785
789
  // it; stopping the session here would kill an active trip mid-route.
@@ -1089,6 +1093,21 @@ class LocalMiniappRuntime {
1089
1093
  case MiniappRequestType.MANAGED_STREAM_STOP:
1090
1094
  void this.handleManagedStreamStop(packageName, payload, requestId);
1091
1095
  break;
1096
+ case MiniappRequestType.MEETING_JOIN:
1097
+ void this.handleMeetingJoin(packageName, payload, requestId);
1098
+ break;
1099
+ case MiniappRequestType.MEETING_LEAVE:
1100
+ void this.handleMeetingLeave(packageName, requestId);
1101
+ break;
1102
+ case MiniappRequestType.MEETING_SET_MUTED:
1103
+ void this.handleMeetingSetMuted(packageName, payload, requestId);
1104
+ break;
1105
+ case MiniappRequestType.MEETING_UPDATE_VIDEO_SOURCE:
1106
+ void this.handleMeetingUpdateVideoSource(packageName, payload, requestId);
1107
+ break;
1108
+ case MiniappRequestType.MEETING_GET_STATE:
1109
+ void this.handleMeetingGetState(packageName, requestId);
1110
+ break;
1092
1111
  case REQUEST_WIFI_SETUP_TYPE:
1093
1112
  void this.handleRequestWifiSetup(packageName, payload, requestId);
1094
1113
  break;
@@ -1172,6 +1191,7 @@ class LocalMiniappRuntime {
1172
1191
  packageName,
1173
1192
  capabilities,
1174
1193
  permissions: declaredPermissions,
1194
+ hostFeatures: { captureAudio: true },
1175
1195
  ...(initialAuth ? { auth: initialAuth } : {}),
1176
1196
  }, requestId);
1177
1197
  if (!initialAuth) {
@@ -2938,6 +2958,7 @@ class LocalMiniappRuntime {
2938
2958
  audio: normalizeStreamAudioConfig(payload.audio),
2939
2959
  sound: payload.sound,
2940
2960
  authToken: typeof payload.authToken === "string" ? payload.authToken : undefined,
2961
+ captureAudio: resolveCaptureAudio(payload.captureAudio, resolveAcsAudioSource().source),
2941
2962
  });
2942
2963
  this.sendResult(packageName, requestId, true, result);
2943
2964
  }
@@ -2986,6 +3007,7 @@ class LocalMiniappRuntime {
2986
3007
  audio: normalizeStreamAudioConfig(payload.audio),
2987
3008
  sound: payload.sound,
2988
3009
  ingest: payload.ingest,
3010
+ captureAudio: resolveCaptureAudio(payload.captureAudio, resolveAcsAudioSource().source),
2989
3011
  });
2990
3012
  this.sendResult(packageName, requestId, true, result);
2991
3013
  }
@@ -3001,6 +3023,113 @@ class LocalMiniappRuntime {
3001
3023
  async handleManagedStreamStop(packageName, payload, requestId) {
3002
3024
  return this.handleStreamStop(packageName, payload, requestId);
3003
3025
  }
3026
+ ensureMeetingStateBridge() {
3027
+ acsMeetingService.setStateHandler((owner, state) => {
3028
+ this.sendToMiniapp(owner, {
3029
+ type: MiniappResponseType.MEETING_STATE,
3030
+ ...state,
3031
+ });
3032
+ });
3033
+ }
3034
+ async handleMeetingJoin(packageName, payload, requestId) {
3035
+ this.ensureMeetingStateBridge();
3036
+ const meetingUrl = typeof payload.meetingUrl === "string" ? payload.meetingUrl : "";
3037
+ const token = typeof payload.token === "string" ? payload.token : "";
3038
+ const videoSource = payload.videoSource;
3039
+ const whepUrl = videoSource?.type === "whep" ? videoSource.url ?? "" : "";
3040
+ const displayName = typeof payload.displayName === "string" ? payload.displayName : undefined;
3041
+ if (!meetingUrl || !token || !whepUrl) {
3042
+ this.sendResult(packageName, requestId, false, undefined, {
3043
+ code: MiniappErrorCode.INVALID_ARGUMENT,
3044
+ message: "meetingUrl, token, and a WHEP videoSource are required",
3045
+ });
3046
+ return;
3047
+ }
3048
+ try {
3049
+ let video;
3050
+ try {
3051
+ video = parseAcsOutgoingVideo(payload.video);
3052
+ }
3053
+ catch (error) {
3054
+ this.sendResult(packageName, requestId, false, undefined, {
3055
+ code: MiniappErrorCode.INVALID_ARGUMENT,
3056
+ message: error instanceof Error ? error.message : "Invalid meeting video",
3057
+ });
3058
+ return;
3059
+ }
3060
+ const state = await acsMeetingService.join(packageName, {
3061
+ meetingUrl,
3062
+ token,
3063
+ whepUrl,
3064
+ displayName,
3065
+ ...(video ? { video } : {}),
3066
+ });
3067
+ this.sendResult(packageName, requestId, true, state);
3068
+ }
3069
+ catch (err) {
3070
+ this.sendResult(packageName, requestId, false, undefined, {
3071
+ code: MiniappErrorCode.INTERNAL,
3072
+ message: err instanceof Error ? err.message : "ACS meeting join failed",
3073
+ });
3074
+ }
3075
+ }
3076
+ async handleMeetingLeave(packageName, requestId) {
3077
+ try {
3078
+ await acsMeetingService.leave(packageName);
3079
+ this.sendResult(packageName, requestId, true);
3080
+ }
3081
+ catch (err) {
3082
+ this.sendResult(packageName, requestId, false, undefined, {
3083
+ code: MiniappErrorCode.INTERNAL,
3084
+ message: err instanceof Error ? err.message : "ACS meeting leave failed",
3085
+ });
3086
+ }
3087
+ }
3088
+ async handleMeetingSetMuted(packageName, payload, requestId) {
3089
+ try {
3090
+ const state = await acsMeetingService.setMuted(packageName, Boolean(payload.muted));
3091
+ this.sendResult(packageName, requestId, true, state);
3092
+ }
3093
+ catch (err) {
3094
+ this.sendResult(packageName, requestId, false, undefined, {
3095
+ code: MiniappErrorCode.INTERNAL,
3096
+ message: err instanceof Error ? err.message : "ACS mute failed",
3097
+ });
3098
+ }
3099
+ }
3100
+ async handleMeetingUpdateVideoSource(packageName, payload, requestId) {
3101
+ const videoSource = payload.videoSource;
3102
+ const whepUrl = videoSource?.type === "whep" ? videoSource.url ?? "" : "";
3103
+ if (!whepUrl) {
3104
+ this.sendResult(packageName, requestId, false, undefined, {
3105
+ code: MiniappErrorCode.INVALID_ARGUMENT,
3106
+ message: "videoSource must be a WHEP URL",
3107
+ });
3108
+ return;
3109
+ }
3110
+ try {
3111
+ await acsMeetingService.updateVideoSource(packageName, whepUrl);
3112
+ this.sendResult(packageName, requestId, true);
3113
+ }
3114
+ catch (err) {
3115
+ this.sendResult(packageName, requestId, false, undefined, {
3116
+ code: MiniappErrorCode.INTERNAL,
3117
+ message: err instanceof Error ? err.message : "ACS video source update failed",
3118
+ });
3119
+ }
3120
+ }
3121
+ async handleMeetingGetState(packageName, requestId) {
3122
+ try {
3123
+ const state = await acsMeetingService.readState(packageName);
3124
+ this.sendResult(packageName, requestId, true, state);
3125
+ }
3126
+ catch (err) {
3127
+ this.sendResult(packageName, requestId, false, undefined, {
3128
+ code: MiniappErrorCode.INTERNAL,
3129
+ message: err instanceof Error ? err.message : "ACS getState failed",
3130
+ });
3131
+ }
3132
+ }
3004
3133
  /**
3005
3134
  * session.glasses.requestWifiSetup — open the phone's glasses Wi-Fi setup
3006
3135
  * flow. The host owns the actual UI via the `engine.configure({ui})` seam;