[codex] Remove legacy cloud applet routes#3216
Draft
PhilippeFerreiraDeSousa wants to merge 88 commits into
Draft
[codex] Remove legacy cloud applet routes#3216PhilippeFerreiraDeSousa wants to merge 88 commits into
PhilippeFerreiraDeSousa wants to merge 88 commits into
Conversation
Introduce the namespaced `island` object — the "(A) host toolkit API" from the Phase-1 contract (OS-1622). Additive: it lives alongside the flat named exports and grows one facade at a time. First facade: `island.glasses.wifi` (scan / connect / forget) over the bluetooth-sdk passthrough — the reference pattern the rest of the (A) domains copy. The wifi screens (scan, connecting) now call `island.glasses.wifi.*` and no longer import `@mentra/bluetooth-sdk` directly (the native-import boundary). status()/onStatus() are deferred to the glasses-store migration — wifi status derives from multiple sources in the glasses connection state, so the read-model lands with that store, not here. connect() propagates bluetooth-sdk's coded errors unchanged so callers keep their error mapping. Jest suite for the facade (by-path import, real code under the CI runner); the @mentra/island mock gains `island`. The bootstrap front door (island.configure/start/stop) and remaining domains follow in later PRs.
Two more pieces of the (A) host API on the island namespace.
Bootstrap front door — island.configure({auth, config?, analytics?}) + start() +
stop(), backed by an island config singleton (runtime/bootstrap.ts). The host now
hands island its auth provider at boot (MantleManager.init). This is ADDITIVE: the
three transitional config seams (configureRuntime/Launcher/Island) still wire the
host adapters during the migration and collapse into this front door only as each
domain lands in island.
island.display.mirror — island's own read-model of the latest processed display
event (current() / onMirror(cb)), fed in PARALLEL with the legacy host
useDisplayStore from both display paths (LocalDisplayManager + the cloud path in
SocketComms). This is the safe "facade" half. The "callback" half — flipping the
preview UI onto onMirror, adding view tracking, deleting useDisplayStore + the
setDisplayEvent adapter, and migrating the display tests (which are coupled to v1
SocketComms + MantleManager) — is its own focused PR.
Jest: @mentra/island mock gains the new members; by-path suites cover the
bootstrap lifecycle + the mirror read-model.
…s facade) The glasses store is the source of device state (connection, wifi, battery, OTA, hotspot) that every device facade needs, so it moves into island — and the wifi status read-model that was blocked on it now lands too. - stores/glasses.ts -> modules/island/src/stores/glasses.ts. btsdk type imports switch to the relative _internal path (standalone-build-safe, like the passthrough); connection predicates come from island's GlassesReadiness. - @/stores/glasses becomes a thin re-export shim — the ~46 existing importers are unchanged (the escape hatch from the Phase-1 plan). Also surfaced as island.glassesStore (the raw store) for the app to keep using directly. - jest: the @mentra/island mock provides the REAL store via jest.requireActual so every consumer's tests keep their real setState/getState/subscribe behavior. - island.glasses.wifi gains status() / onStatus() reading the island-owned store — the wifi facade is now complete. Verified: typecheck 0 new errors across all 46 consumers; jest 265 pass (the real store runs in-test via requireActual). The island standalone build uses the proven relative-_internal pattern; CI confirms it (local build is blocked only by the unrelated cloud-v2 zod dep not being installed in this checkout).
The OEM-facing API object is now `toolkit` (toolkit.glasses.wifi, toolkit.configure / start / stop, toolkit.display.mirror, toolkit.glassesStore). The code module stays @mentra/island — only the public surface is renamed, since the product is the OEM Integration Toolkit. Done now while the namespace has only a handful of consumers. All consumers updated: the wifi screens, SocketComms, MantleManager, the jest mock, and the runtime-visible bootstrap error string. The flat exports (useGlassesStore, BluetoothSdk, …) are unchanged.
…rror reads it) The glasses-screen mirror read-model now lives in island, so the mirror UI is on island-owned state too (the device store moved last commit). - stores/display.ts -> modules/island/src/stores/display.ts. The two pure @/utils/e2eMetrics helpers (extractDisplayText, logE2EMetric) are inlined so the store is self-contained inside island; the host e2eMetrics util is untouched. Added subscribeWithSelector for the facade's selector subscription. - @/stores/display becomes a re-export shim (consumers unchanged). The raw store is toolkit.displayStore — a Mentra-app escape hatch, NOT the OEM contract. - toolkit.display.mirror is now the typed READ facade over the island-owned store (current() / onMirror()), replacing the earlier parallel read-model. Reverted the parallel ingest feeds in LocalDisplayManager + SocketComms. - jest: the @mentra/island mock serves the real display store via requireActual. Verified: typecheck 0 new errors; jest 265 pass (GlassesDisplayMirror + the SocketComms display path stay green).
…der toolkit.stores
Continues the device-store consolidation.
- core / connection / gallerySync stores -> modules/island/src/stores/* via the
shim pattern (the @/stores/* files become 1-line re-exports; consumers unchanged).
- Two coupled types relocated into island so the stores are standalone-build-safe:
WebSocketStatus (was @/services/ws-types) and PhotoInfo (was @/types/asg). Both old
homes now re-export from island, so their other importers are unchanged.
- Grouped the raw-store escape hatches under toolkit.stores.{glasses,display,core,
connection,gallerySync} (was toolkit.glassesStore/displayStore — nothing consumed
those yet, so zero-risk). Reminder: stores are the Mentra-app escape hatch, NOT the
OEM contract; OEMs use the typed facades.
- Deferred cloudClientStatus: it imports cloud-v2 (@mentra/cloud-client) types, so it
belongs with the cloud-client / session work, not this batch.
- jest: the @mentra/island mock serves the real stores via requireActual.
Verified: typecheck 0 new errors across all consumers; jest 265 pass.
toolkit.glasses now carries the core surface (the central OEM glasses API): - connection: connectDefault / disconnect / forget (bluetooth-sdk passthrough) - read-model: status() / onStatus() / info() — a curated projection of the island-owned glasses store, so the store's field layout doesn't leak into UIs - capabilities() from the model table; requestVersionInfo() - input events: onButtonPress / onTouchGesture (passthrough listeners) - wifi nested under it Also adds agents/island-facade-buildout.md — the per-domain tracking spec for the rest of the buildout (two move-patterns, sequence, the settings keystone, the git-merge-dev gate for session). Verified: typecheck 0 new errors; jest 269 pass (new glassesFacade suite green).
toolkit.speech.{stt,tts} — currentLanguage / languages / languageInfo / download /
activate / cancelDownload / deleteModel, wrapping the in-island STT+TTS model
managers via type-safe Parameters<> forwarding. STT also exposes status() /
onStatusChanged() over the offline-model service. Logic was already in island, so
this is a thin facade. Tested (managers mocked to avoid native deps).
Verified: typecheck 0 new errors; jest 272 pass.
…atal island build) Mobile CI (and dev's, since the cloud-v2 merge) failed install on `@mentra/cloud-client - 404`. Two install-time causes, both fixed: 1) mobile/modules/island/package.json declared `"@mentra/cloud-client": "*"`, but cloud-client is resolved via metro + tsconfig PATH aliases (per the cloud-v2 setup), not node_modules — the `*` made bun try npm and 404. Removed it; the app still resolves cloud-client via the alias (island only type-imports it). 2) postinstall builds island standalone (expo-module), whose isolated tsconfig has no cloud-v2 aliases, so it can't resolve `@mentra/cloud-client` / `@mentra/cloud-runtime/protocol` and failed the whole install. But island's build/ is NOT consumed: metro (metro.config.js) and tsconfig both resolve `@mentra/island` to src/. Made the build non-fatal (warn) — same precedent as the root @mentra/miniapp postinstall. Unblocks mobile CI (mine + dev's). App typecheck + jest green.
…ypes - Restore the cloud-v2 path aliases in island/tsconfig.json (a local build:module regenerated and stripped them in the prior commit, and I accidentally committed that) — they let island's standalone build resolve cloud-client/cloud-runtime. - Export the 5 store state interfaces (CoreState, ConnectionState, DisplayStore, GlassesState, GallerySyncState) so toolkit.stores' declaration emit can name them (fixes the TS4023 the standalone build hit).
…resolves cloud-v2 source After the install 404 was fixed, the mobile typecheck failed: it follows the metro/tsconfig aliases into cloud-v2 SOURCE (manila/cloud-v2/packages/*), which import zod/tweetnacl. Module resolution is file-relative, so those resolve against cloud-v2/node_modules — never installed, because cloud-v2 is a SEPARATE bun workspace the mobile install doesn't cover. Install cloud-v2's deps in the mobile postinstall (non-fatal). Third and final piece of un-redding the cloud-v2 mobile CI (broken on dev since the cloud-v2 merge): drop spurious cloud-client dep → non-fatal island standalone build → install cloud-v2 deps.
…hotoSettings MentraBluetoothSDK.swift failed to compile, blocking the native iOS build: - :493 used `settings.resetCaptureTuning` (Bool?) directly in an `if`. - :504 used `if let size = settings.size`, but `size` is non-optional (ButtonPhotoSize, always defaulted to .medium in `from(params:)`). Unwrap the Bool? with `== true`; set `size` unconditionally to match its non-optional design. Both pre-existing on dev (camera/button-photo work) — the last red on the mobile CI native build, only reachable once the cloud-v2 install fixes let the build get past install to the Swift compile.
…t in buildout spec
…rface Documents the live OEM-facing API as we build it: lifecycle (configure/start/stop), glasses (connection/status/info/capabilities/version/events + wifi), speech (stt/tts), display.mirror, the stores escape hatch, and the host adapter seam. Page lives under docs/glasses-oems/ but is intentionally NOT nav-linked yet — Phase-1 preview, gated from the live OEM site until release.
…store into island Leaf moves ahead of relocating the CloudClient singleton itself: - cloudClientStatus store -> island/src/stores (host @/stores/cloudClientStatus becomes a re-export shim); surfaced as toolkit.stores.cloudClientStatus. - cloud secure store (MMKV KeyValueStore for the v2 refresh token) -> island/src/utils/cloudClient (host @/utils/cloudClient/MmkvSecureStore shim). react-native-mmkv is already an island dep, so this is adapter-free. jest @mentra/island mock gains the store via requireActual (pure zustand).
…nd + toolkit.session
The cloud-client/auth keystone — pulls backend comms ownership into island.
- island/src/services/CloudClientService.ts owns the @mentra/cloud-client
singleton. It builds the client from island-owned pieces (the native UDP
socket, the MMKV secure store, the cloud-status store) + the two things the
host provides through the toolkit FRONT DOOR (not a configureRuntime adapter):
auth.getSubjectToken (the one permanent seam) and the resolved cloud endpoints
+ LC3 frame size via toolkit.configure({config}). On init it SELF-WIRES the
runtime cloud/cloudConnection hooks, so the host no longer injects a
CloudRuntimeAdapter.
- toolkit.session exposes the live-session status read-model (status +
audioTransport) + isConnected. Account ops (delete/export) stay host RestComms
for now (cloud-client core has none) — they land with the account domain.
- Host @/services/cloudClient becomes a thin wrapper: keeps the dev/settings
endpoint resolution (resolvedEndpoints/lc3FrameSizeBytes/cloudConfigValues,
host-coupled) and delegates managed-photo/stream/connection to island, so
PhonePhotoCoordinator / cloudStreamApi / the dev Cloud-URL switcher are
untouched.
- MantleManager passes config via configure() and drops the host-injected
cloud/cloudConnection hooks.
- bootstrap IslandConfigValues gains audioFrameSizeBytes; jest @mentra/island
mock gains cloudClientService + toolkit.session.
Per OS-1622 this is the adapter-free path: storage/status/client all owned by
island; the configureRuntime cloud hook is now island-self-wired (deleted in the
#8 sweep). Docs (toolkit.session) + buildout spec updated.
Typecheck + full jest (281 tests) green. Device-level audio/transcription/photo
verification is still required on hardware.
…o island storage Increment 1 of the settings+RestComms co-move. GlobalEventEmitter -> island (one shared instance, host shim); ports storage.loadSubKeys() into island's MMKV util (settings.ts needs it). jest mock gains the shared emitter.
…-coupled v1-comms pair) The biggest single migration — settings (964 LOC) + RestComms (731 LOC) move together because they're mutually coupled (settings calls restComms.writeUserSettings for cloud-sync; RestComms reads the settings store for the backend URL). Moving the pair into island resolves BOTH the circular dependency AND the early-auth timing trap (RestComms reads the backend URL at exchangeToken, before island's config/adapters are wired — only the module-load-available store works there; now it's island's own store). - settings.ts -> island/src/stores; RestComms.ts -> island/src/services (git mv, history preserved). Host @/stores/settings + @/services/RestComms are re-export shims (96 + 24 consumers unchanged). Exposed as toolkit.stores.settings. - Import rewrites: settings -> island RestComms + island storage; RestComms -> island settings/connection stores, island GlobalEventEmitter, island types, BgTimer, and the internal btsdk surface via the relative build/_internal path (the @mentra/bluetooth-sdk-internal alias doesn't resolve in island's standalone build). - GlobalEventEmitter moved into island (one shared instance + host shim); ported storage.loadSubKeys into island's MMKV util; dropped two dead imports (Platform/Device). - jest: requireActual settings + RestComms (tests used the real host store before the move); moduleNameMapper maps the relative build/_internal path to the mockable source so requireActual doesn't load the real native btsdk; the mock exposes the REAL island GlobalEventEmitter so emit/listen share one instance. v1-transitional: RestComms is deleted in place when v1 retires; the point is the host no longer owns comms during the (multi-week) transition. SocketComms is a separate 4-6 week refactor and moves last. mobile typecheck + island standalone build + full jest (281 tests) all green.
…tions (fix TS4023) toolkit.stores.settings = useSettingsStore is typed with SettingsState, which was not exported; island's standalone build EMITS .d.ts (expo-module-tsc) and can't name an un-exported type -> TS4023. Passed local 'tsc --noEmit' (no emit) but failed CI's build:module. Exporting the interface fixes it (same pattern as the other moved-store State interfaces). Verified with 'bun run build:module' (the emit build) now green.
…facades
Typed (A) facades over logic just moved into island:
- toolkit.settings — keyed get/set/onChanged/descriptor/keys over the island settings
store (the OEM contract; toolkit.stores.settings stays the raw escape hatch).
- toolkit.session.account.{delete,confirmDelete} — adapter-free now that RestComms is in
island (corrects the earlier deferral). requestExport omitted (host UI flow, no backend).
- toolkit.dev — backend/cloud URL overrides (island settings store), reconnect the
island cloud client, minimumClientVersion (island RestComms).
Exported Setting + SettingsState interfaces (TS4023: descriptor()/stores.settings emit
declarations). Verified with the EMIT build (build:module), not just tsc --noEmit.
jest mock extended; full suite (281) green.
…incidents toolkit.incidents — typed passthrough over the now-island RestComms incident submission (create/uploadLogs/uploadAttachments/sendFeedback). The single-call file() that bundles diagnostics island-side is a follow-up (needs the host's native diagnostics-gathering + console logBuffer moved in). OEM docs updated for the new facades (settings, session.account, dev, incidents). Emit build + 281 jest green.
- glasses.onStatus + session.onStatus: dedupe — fire only when the projected snapshot actually changes (were firing on every store update). - CloudClientService: read LC3 frame size LIVE from the island settings store so a reconnect after a glasses swap (G1=20 <-> G2=40) isn't stale on the configure() snapshot. - toolkit.dev: setCloudUrls/reconnectCloud now reconnect onto the merged current cloud-URL overrides (a single-URL update used to set the setting but not apply it). - docs/glasses-oems/toolkit.mdx: remove stray </content></invoke> markup (broke MDX). Deferred (pre-existing, flagged): the ButtonPhotoSettings.size overwrite (proper fix = optional size, ripples into the BLE encoder — camera-domain) and the !client-vs-connected guard message in CloudClientService (carried over from the host, behavior-preserving).
…niapps lifecycle facade
- display.mirror: expose view()/setView('main'|'dashboard') — the store already
backed it; was just unexposed. (sendButtonPress simulated-input is a separate dev aid.)
- toolkit.miniapps: lifecycle facade over the island apps store —
list/onChanged(deduped)/refresh/start/stop/setForeground/clearForeground/stopAll/
install/uninstall. The WebView bridge primitives stay exported separately (host mounts
the <WebView>). jest mock extended; typecheck + emit build + 281 jest green.
file({feedbackData, phoneState, logs?, screenshots?}) -> {incidentId} orchestrates
the full flow island-side: createIncident -> uploadLogs -> sendIncidentId to glasses
-> uploadAttachments. The OEM gathers diagnostics (native-coupled: NetInfo/Constants/
Location/ImagePicker + console logBuffer) and passes them in; island owns the
submission. Lower-level primitives still exposed. Doc + jest mock updated; emit build +
281 jest green. Finishes the last of the 3 partial facades (with display.mirror.setView
+ toolkit.miniapps).
- Stale cloud endpoint override: reconnect(null) now CLEARS endpointsOverride and falls back to the boot config; toolkit.dev's empty-settings branch passes null, so cleared/default cloud URLs stop reconnecting to a stale override. Added cloudClientService.stop() for lifecycle symmetry. - start() skips cloud init: toolkit.start() now constructs/connects the cloud client (and toolkit.stop() tears it down), so the documented configure()+start() flow yields a live toolkit.session for OEMs. Idempotent — the Mentra app's explicit cloudClient.init() becomes a no-op. Composed in island.ts to avoid a bootstrap<->service import cycle; safe now that init() reads the settings STORE (module-load-available), not the adapter. Emit build + 281 jest green.
The iOS build intermittently failed at 'pod install' with 'React-Core-prebuilt/React.xcframework/Info.plist doesn't exist' — the cached Pods (keyed by lockfile) can be valid-by-key yet missing the prebuilt React xcframework (a known RN-0.83 prebuilt + CocoaPods cache flake on the self-hosted runners). pod install was a bare command with no fallback (only the later build step retried with clean caches). Make pod install self-heal: on failure, clear project Pods + the global pod cache and reinstall with --repo-update. Happy path unchanged. Not a TS issue — one of the two iOS build jobs passed (6m39s) on the same commit; only the runner with the corrupted cache failed.
- toolkit.glasses.settings: keyed DEVICE-settings facade (get/set/onChanged/descriptor/ available) scoped to BLUETOOTH_SETTING_KEYS (exported), minus the internal sync keys (core_token/auth_email). set() persists + auto-syncs to the glasses via the store. - toolkit.pairing: first-time discovery + pair — scan/scanning/searchResults/onFound(deduped) over the core store + BluetoothSdk startScan/connect/setDefaultDevice + the pair_failure/ glasses_not_ready events. (connectDefault for the already-paired default stays on glasses.) Doc + jest mock updated; mobile typecheck + island emit build + 281 jest green. permissions deferred — it's the host-coupled one (PermissionsUtils: i18n/theme/AlertUtils), needs the adapter pattern; recommended as a focused follow-up off this now-green PR.
…roller) + toolkit.phoneNotifications - toolkit.glasses: + connect(device), connectSimulated(), setDefault(device), and a controller sub-facade (connectDefault/disconnect/forget). Switched glasses.ts to the internal btsdk surface (these live there, not the public entry). Dropped reconnect() (no btsdk backing) and controller.connect() (only connectDefaultController exists). - toolkit.phoneNotifications: enabled/setEnabled + blocklist/setBlocklist (island settings), installedApps + has/requestListenerPermission (CrustModule). Android-only; iOS defaults. Emit build confirms crust resolves in island's standalone build. Emit build + 281 jest green.
…ost hooks Three runtime-blocking passthrough hooks an OEM had to inject become direct btsdk calls so a bare OEM works without them: - sendDisplayEvent -> LocalDisplayManager calls BluetoothSdk.displayEvent (the display output path — nothing renders on the glasses without it). - setMicRequirements -> MicStateCoordinator calls BluetoothSdk.updateBluetoothSettings (the mic control plane — the glasses never stream audio without it). - restartTranscriber -> LocalSttFallbackCoordinator calls BluetoothSdk.restartTranscriber. Removed the three RuntimeHooks fields + MantleManager bindings. setDisplayEvent (host glasses-mirror UI) + subscribeGlassesStatus stay host hooks for now. Verified: app tsc clean, island expo-module build clean, jest 40 suites / 289 tests.
…mapping
- MantleManager registered audio_pairing_needed / audio_connected / audio_disconnected /
save_setting / head_up TWICE in setupSubscriptions, so each handler fired twice per
event. Remove the redundant first-block registrations (the surviving head_up copy is
the superset that also forwards to miniapps).
- PhoneLocationService.getLocationAccuracy ignored the aggregate miniapp tiers
("passive"/"low"/"high") — they fell through to Lowest, so a miniapp requesting
high-rate GPS got the coarsest accuracy. Map passive→Lowest, low→Low, high→High
(realtime→BestForNavigation already handled); the accuracy-string vocabulary stays.
Verified: app tsc clean, island expo-module build clean, jest green.
…ata flow) island owned the stores, coordinators, miniapp runtime, and facades, but never subscribed to the device for most events — MantleManager was still the event router the whole runtime depended on. So a bare OEM that imported island + called toolkit.start() got a connected runtime with almost no device data flowing in. New self-starting DeviceEventRouter (started by toolkit.start()) subscribes to the inbound native events and routes the NON-v1 legs: - wifi_status_change / hotspot_status_change / hotspot_error / gallery_status -> island stores + the process event bus. NOTE: island's own gallerySyncService listens on the bus for hotspot/gallery events, so without this bridge island's gallery sync was DEAD for a bare OEM. - photo_response -> phonePhotoCoordinator (owned) / restComms (cloud-app); stream_status + keep_alive_ack -> phoneStreamCoordinator (owned). - button_press / touch_event / accel_event / head_up -> local miniapps (forwardEvent). - save_setting -> settings store (the inbound complement to GlassesSettingsSync). - miniapp_selected -> the glasses swipe-menu app launcher. MantleManager keeps only its v1 SocketComms legs (touch/stream/keepalive cloud forwards, etc., deleted at v1 retirement) + host-UI legs; the moved legs are removed there to avoid double-handling. mic_pcm->audio_chunk + phone_notification stay host for now (Buffer dep / v1 REST). Added a by-path deviceEventRouter.test.ts; moved the photo_response assertion out of MantleManager.test. Verified: app tsc clean, island expo-module build clean, jest 41 suites / 295 tests.
… hooks) DisplayProcessor (battery/connection display placeholders + device-model tracking) and LocalMiniappRuntime (initial glasses_battery snapshot) read the host glassesStatus + subscribeGlassesStatus hooks. island already owns this data in useGlassesStore (fed by GlassesStatusProjection), so point them at the store / GlassesReadiness directly and drop both hooks + the MantleManager bindings. A bare OEM gets glasses status without wiring them. Verified: app tsc clean, island expo-module build clean, jest 41 suites / 295 tests.
Resolve conflicts: - config.ts: keep the heading/locationTier/cameraSettings removals (moved into island on this branch); restore the CloudConnectionAdapter interface dev's tree dropped (this branch's cloudClientService self-wires cloudConnection + LocalSttFallbackCoordinator reads it). - MantleManager.ts: take dev's import line (the `crust` → `@mentra/crust` package rename + new BluetoothStatus/OtaStatus types); keep the cloudConnection self-wire comment. - cloudClient.ts: keep this branch's thin delegating wrapper (the cloud client lives in island's CloudClientService). Port dev's NEW local-dev runtime-token feature (getLocalDevRuntimeToken / shouldUseLocalDevRuntimeToken) into CloudClientService so a localhost dev cloud still authenticates without a Supabase exchange. - Update this branch's island `crust` imports + the gallery test mock to `@mentra/crust`. Verified: app tsc clean, island expo-module build clean, jest 41 suites / 295 tests.
… host hook) Miniapp speaker / TTS playback was a host-injected audioPlayback hook, even though the service is pure expo-audio + btsdk volume control with zero host coupling — so an OEM had to implement it. Move AudioPlaybackService into island and call BluetoothSdk directly for the glasses media-volume + own-app-playing hints (was toolkit.glasses.audio). - LocalMiniappRuntime calls audioPlaybackService directly (dropped the host-not-configured guard); the v1 SocketComms audio_play_request handler imports it from @mentra/island. - Removed the audioPlayback hook + AudioPlaybackAdapter/AudioPlayRequest from RuntimeHooks + the MantleManager binding. Added expo-audio to island deps. Repointed the AudioPlaybackService test by-path; the @mentra/island jest mock supplies it for host consumers. This completes the 5 passthrough-hook eliminations — an OEM gets miniapp audio with no wiring. Verified: app tsc clean, island expo-module build clean, jest 41 suites / 295 tests.
Resolve conflicts (same shape as prior merges — keep this branch's island moves, port dev's new cloud feature into island): - dev added a `miniappAuth` hook (package-scoped local-miniapp backend tokens via cloudClient.getMiniappAuthToken). Kept the hook; ported getMiniappAuthToken + normalizeExpiresAt into island's CloudClientService and delegated through the thin cloudClient wrapper. Dropped dev's re-added audioPlayback/glassesStatus hooks (moved into island) and the `cloud` configureRuntime arg (self-wired by cloudClientService). - config.ts / index.ts: kept MiniappAuthAdapter/MiniappAuthToken; dropped the AudioPlaybackAdapter/AudioPlayRequest + glassesStatus surfaces. - cloudClient.ts: kept the thin delegating wrapper. - jest.config.js: union (this branch's btsdk build/_internal map + dev's expo/rn/crust maps). - Removed a dead `setCaptureLedEnabled` btsdk binding (no native method, no public type, no consumers — leftover from dev's camera-LED removal; it failed tsc post-merge). Verified: app tsc clean, island expo-module build clean, jest 41 suites / 295 tests.
…OEM-readiness) Two of the "a bare OEM still has to inject this" gaps from the e2e trace: - settings hook eliminated. The runtime read user/device settings (coreToken, defaultWearable, localSttFallbackActive, cameraFov) through getRuntimeHooks().settings, but the host injector just re-wrapped island's own useSettingsStore. Point the 8 call sites (LocalMiniappRuntime / LocalSttFallbackCoordinator / DisplayProcessor) at useSettingsStore directly (same move as glassesStatus); remove the SettingsAccessor hook + the MantleManager binding. DisplayProcessor.attachToRuntime no longer early- returns when the hook is absent — a bare OEM now gets the right device profile + userId/coreToken instead of silent defaults. - Declared the 11 runtime deps island imports but didn't list in package.json (@mentra/crust, @mentra/cloud-client, @mentra/cloud-runtime, axios, expo-notifications, expo-modules-core, react-native-localize/-permissions/-wifi-reborn, @react-native-community/netinfo, @craftzdog/react-native-buffer) so a clean install resolves them. Verified: app tsc clean, island expo-module build clean, jest 41 suites / 295 tests.
Move the MentraJS engine construction out of the host bootstrap into island's new MiniappEngine service: ensureMiniappEngine() builds the crash controller, UI router, and JS router, binds them to the native Crust module + the launcher, wires the dev-server background-respawn signal, and starts the message pump. Idempotent — getMiniappEngine() returns the singleton. toolkit.start() now brings up the local-miniapp engine (LocalMiniappRuntime + the router + DisplayProcessor + gallery sync) so a bare OEM gets working miniapps with no host construction step. All four bringup calls are idempotent. The host mentraJsBootstrap shrinks to a thin shim that attaches only the genuinely host-owned concerns over the island engine: the inter-miniapp interop policy (system-app set, app-store start/stop, audit) and crashloop telemetry (Sentry, automatic incident filing, the user alert). getMentraJS() now delegates to the island engine singleton.
…jest mock Two OEM-readiness/test-fidelity gaps surfaced by an end-to-end review: - island's timers util require()s react-native-nitro-modules (the nitro bg-timer runtime probe) but the dep was undeclared. Harmless in the Mentra app (the host declares it directly), but a standalone OEM consumer would miss it. Declared in peerDependencies (* — host-provided), matching the other native peer deps. - the @mentra/island jest mock omitted ensureMiniappEngine/getMiniappEngine/ configureLauncher/miniappLauncher/sttModelManager — all public exports the host imports. Most notably ensureMiniappEngine is reached transitively by MantleManager.test via mantle.init() -> bootstrapMentraJS(), where the missing mock made the bootstrap throw-and-swallow instead of running. The mock now returns a minimal functional engine so the gate exercises the real host attach path.
…pace-wifi # Conflicts: # mobile/src/stores/settings.ts
…pace-wifi # Conflicts: # mobile/bun.lock # mobile/modules/island/src/index.ts # mobile/src/effects/OtaUpdateChecker.tsx # mobile/src/services/MantleManager.ts # mobile/src/stores/display.ts
…ettings seed)
- incidents.file() referenced this.notifyGlasses, so a detached call
(const {file} = toolkit.incidents; file(input)) dropped `this` and threw.
Reference the incidents object directly so destructured use still notifies.
- connectDefault()/reconnect() called pushAllBluetoothSettings() without
awaiting it; the native settings write is async, so the connect handshake
could race ahead and replay stale native settings. pushAllBluetoothSettings
now returns the write promise and both connect paths await the seed first.
…tically The wifi_off path set waitingForWifiRetry + wifiSettingsOpenedAt at notice-emit time, before the user picked Open Settings vs Cancel. A user who cancelled was left armed: a phantom "WiFi initializing" cooldown on the next tap, and an unsolicited auto-sync on a later foreground if WiFi happened to come up. Use the GalleryNotice.ack mechanism that already exists (the wifi-join notice uses it): the island attaches the arming as the notice's ack closure and stops setting the state optimistically; the host calls notice.ack() only on Open Settings. Cancel arms nothing. Runtime policy (what arming means) stays in the island; the host only reports which button the user pressed.
…rop pseudo offline-captions renderer Local on-device STT transcripts now reach local miniapps for ANY host (not just the Mentra app): DeviceEventRouter (started by toolkit.start()) listens for local_transcription and forwards transcription:<lang> to localMiniappRuntime, gated by localSttFallbackCoordinator.isActive() so cloud STT keeps owning delivery when the WS is up (no double-routing). With the pseudo offline-captions miniapp gone, MantleManager's handle_local_transcription — and the host-side caption rendering it drove (TranscriptProcessor, sendPendingTranscript, displayTextMain, resetDisplayTimeout) — are removed. Added DeviceEventRouter tests for the active/inactive paths; dropped the now-obsolete MantleManager offline-render test.
Route cloud, display, launcher, and interop runtime access through island-owned services and the toolkit front door instead of configureRuntime/configureLauncher/configureIsland. Keep the legacy Mentra app catalog behind a transitional app-store hook until cloud v1 app surfaces can be deleted.
Remove the legacy Mentra app catalog bridge that fetched and managed cloud-v1 applets through RestComms. Keep built-in/offline app registration in a smaller catalog and let the island app store own local-only app state without host hooks.
Contributor
📋 PR Review Helper📱 Mobile App Build✅ Ready to test! (commit 🕶️ ASG Client Build⏳ Waiting for build... 🔀 Test Locallygh pr checkout 3216 |
559daf1 to
0138b73
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
/applet/settingsand/applet/webviewmobile routes and their error screenRestCommsScope notes
cloud/backend changesmobile/modules/bluetooth-sdkchangesaea143b37db6015c69d44c690e359819e49bac05Validation
cd mobile && bun install --frozen-lockfileto install dependencies in the fresh worktreecd mobile && bun run compilepassed