@mentra/engine 3.2.0-dev.227 → 3.2.0-dev.233

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.
@@ -775,13 +775,14 @@ class LocalMiniappRuntime {
775
775
  // remain active for another subscriber; a rejected FOV release then stops
776
776
  // renewing the departed owner's lease and ASG restores it once idle.
777
777
  const cameraCleanup = Promise.allSettled([
778
+ this.leaveMeetingForApp(packageName),
778
779
  phonePhotoCoordinator.stopWarmUpForApp(packageName),
779
780
  phoneStreamCoordinator.stop(packageName),
780
781
  phoneVideoCoordinator.stopForApp(packageName),
781
782
  ]).then((results) => {
782
783
  for (const result of results) {
783
784
  if (result.status === "rejected") {
784
- console.warn(`${LOG_TAG}: failed to stop camera consumer for ${packageName} on unregister`, result.reason);
785
+ console.warn(`${LOG_TAG}: failed to stop miniapp resource for ${packageName} on unregister`, result.reason);
785
786
  }
786
787
  }
787
788
  });
@@ -809,9 +810,6 @@ class LocalMiniappRuntime {
809
810
  // Tear down this app's blob state: abort in-flight uploads + close readers
810
811
  // so a crashed/closed miniapp doesn't leak partial files or file handles.
811
812
  this.blobStore.onAppGone(packageName);
812
- void acsMeetingService.leaveIfOwner(packageName).catch((error) => {
813
- console.warn(`${LOG_TAG}: failed to leave ACS meeting for ${packageName} on unregister`, error);
814
- });
815
813
  // Detach the per-app nav event forwarder but leave the native nav session
816
814
  // running. The user may have just closed the mini-app UI and will reopen
817
815
  // it; stopping the session here would kill an active trip mid-route.
@@ -3113,12 +3111,26 @@ class LocalMiniappRuntime {
3113
3111
  }
3114
3112
  ensureMeetingStateBridge() {
3115
3113
  acsMeetingService.setStateHandler((owner, state) => {
3114
+ const attempt = this.softapAttempt;
3115
+ // While a replacement waits for cleanup, native events still describe its predecessor.
3116
+ if (attempt?.packageName === owner && !attempt.ownsResources)
3117
+ return;
3116
3118
  this.sendToMiniapp(owner, {
3117
3119
  type: MiniappResponseType.MEETING_STATE,
3118
3120
  ...state,
3119
3121
  });
3120
3122
  });
3121
3123
  }
3124
+ /** Startup owns the hotspot before ACS has an owner. Closing the app must retire both. */
3125
+ async leaveMeetingForApp(packageName) {
3126
+ const attempt = this.softapAttempt;
3127
+ if (attempt?.packageName === packageName) {
3128
+ await this.retireSoftapAttempt({ attempt });
3129
+ }
3130
+ else {
3131
+ await acsMeetingService.leaveIfOwner(packageName);
3132
+ }
3133
+ }
3122
3134
  async handleMeetingJoin(packageName, payload, requestId) {
3123
3135
  this.ensureMeetingStateBridge();
3124
3136
  // A meeting puts the glasses camera and mic in front of remote strangers; it
@@ -3325,7 +3337,8 @@ class LocalMiniappRuntime {
3325
3337
  return;
3326
3338
  }
3327
3339
  attempt.progress = progress;
3328
- const current = acsMeetingService.getState();
3340
+ // A reservation waiting behind another call must not inherit that call's connected state.
3341
+ const current = attempt.transport ? acsMeetingService.getState() : { state: "connecting", muted: false };
3329
3342
  this.sendToMiniapp(attempt.packageName, {
3330
3343
  type: MiniappResponseType.MEETING_STATE,
3331
3344
  ...current,
@@ -3353,6 +3366,7 @@ class LocalMiniappRuntime {
3353
3366
  async runSoftapAttempt(attempt, previous, args) {
3354
3367
  const packageName = attempt.packageName;
3355
3368
  if (previous) {
3369
+ this.narrateSoftapPreflight(attempt, "Finishing the previous call’s cleanup…");
3356
3370
  softapTrace("softap_join_superseding", {
3357
3371
  previousAttempt: previous.id,
3358
3372
  previousAgeMs: Date.now() - previous.startedAt,
@@ -3414,6 +3428,7 @@ class LocalMiniappRuntime {
3414
3428
  },
3415
3429
  joinScopedNetwork: (ssid, passphrase, gateway) => acsMeetingService.joinScopedNetwork(ssid, passphrase, gateway),
3416
3430
  leaveScopedNetwork: () => acsMeetingService.leaveScopedNetwork(),
3431
+ cancelScopedNetworkJoin: () => acsMeetingService.cancelScopedNetworkJoin(),
3417
3432
  probeGateway: async () => {
3418
3433
  const verdict = await acsMeetingService.probeScopedGateway();
3419
3434
  // No native probe means no verdict, and the orchestrator must not read that as "down".
@@ -3664,7 +3679,7 @@ class LocalMiniappRuntime {
3664
3679
  // Teams configuration problem.
3665
3680
  const defaultNetworkStartedAt = Date.now();
3666
3681
  try {
3667
- const network = await acsMeetingService.awaitValidatedDefaultNetwork();
3682
+ const network = await acsMeetingService.awaitDefaultNetworkAfterHotspot();
3668
3683
  softapTrace("softap_teardown_step", {
3669
3684
  attempt: attempt.id,
3670
3685
  step: "awaitValidatedDefaultNetwork",