@ai-matrx/meet 0.11.51 → 0.11.53

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog — `@ai-matrx/meet`
2
2
 
3
+ ## 0.11.53
4
+
5
+ Automatic changed-only republish (docs/metadata drift since the last tag — see
6
+ `git diff npm/meet/v0.11.52..npm/meet/v0.11.53 -- apps/shared/meet`).
7
+ No source changes intended and no consumer action required.
8
+
9
+ ## 0.11.52
10
+
11
+ Automatic changed-only republish (docs/metadata drift since the last tag — see
12
+ `git diff npm/meet/v0.11.51..npm/meet/v0.11.52 -- apps/shared/meet`).
13
+ No source changes intended and no consumer action required.
14
+
3
15
  ## 0.11.51
4
16
 
5
17
  Automatic changed-only republish (docs/metadata drift since the last tag — see
package/dist/core.cjs CHANGED
@@ -3835,6 +3835,7 @@ function consentPending(policy, role, recordingOn, given) {
3835
3835
  function mayAdmitNow(role, participants, policy) {
3836
3836
  if (role === "none") return false;
3837
3837
  if (role === "host" || role === "cohost") return true;
3838
+ if (role === "viewer") return false;
3838
3839
  const hostIn = participants.some((p) => p.role === "host" || p.role === "cohost");
3839
3840
  return !hostIn && policy.headlessPowers === "members_admit";
3840
3841
  }
@@ -4896,6 +4897,16 @@ var ALIVE_TOPIC = "mx.alive";
4896
4897
  function encodeBeat(beat) {
4897
4898
  return JSON.stringify({ q: beat.q, vp: beat.vp });
4898
4899
  }
4900
+ function encodeBye() {
4901
+ return JSON.stringify({ bye: true });
4902
+ }
4903
+ function isBye(payload) {
4904
+ try {
4905
+ return JSON.parse(payload).bye === true;
4906
+ } catch {
4907
+ return false;
4908
+ }
4909
+ }
4899
4910
  function decodeBeat(payload) {
4900
4911
  try {
4901
4912
  const raw = JSON.parse(payload);
@@ -5529,6 +5540,17 @@ function createMeetingSession(options) {
5529
5540
  }
5530
5541
  return identity.userId;
5531
5542
  }
5543
+ function requireAdmitterUser(operation) {
5544
+ const snapshot = store.snapshot();
5545
+ if (identity.userId === null || !mayAdmitNow(effectiveRole(snapshot), snapshot.participants, snapshot.livePolicy)) {
5546
+ throw new MeetError(
5547
+ "forbidden",
5548
+ `${operation}: this participant may not let people in`,
5549
+ "Only a host or co-host admits people, unless the meeting has no host in it and lets members admit."
5550
+ );
5551
+ }
5552
+ return identity.userId;
5553
+ }
5532
5554
  function requireMeeting(operation) {
5533
5555
  const meeting = store.snapshot().meeting;
5534
5556
  if (meeting === null) {
@@ -5935,7 +5957,7 @@ function createMeetingSession(options) {
5935
5957
  meetingChannel.requestBackfill("manual");
5936
5958
  },
5937
5959
  async admit(entry, admitted) {
5938
- const by = requireHostUser("meet.admit");
5960
+ const by = requireAdmitterUser("meet.admit");
5939
5961
  const meeting = requireMeeting("meet.admit");
5940
5962
  const row = await repository.decideAdmission({
5941
5963
  meetingId: meeting.id,
@@ -5953,7 +5975,7 @@ function createMeetingSession(options) {
5953
5975
  hint();
5954
5976
  },
5955
5977
  async undeny(entry) {
5956
- const by = requireHostUser("meet.undeny");
5978
+ const by = requireAdmitterUser("meet.undeny");
5957
5979
  const meeting = requireMeeting("meet.undeny");
5958
5980
  const row = await repository.decideAdmission({
5959
5981
  meetingId: meeting.id,
@@ -5966,7 +5988,7 @@ function createMeetingSession(options) {
5966
5988
  hint();
5967
5989
  },
5968
5990
  async admitAll() {
5969
- const by = requireHostUser("meet.admitAll");
5991
+ const by = requireAdmitterUser("meet.admitAll");
5970
5992
  const meeting = requireMeeting("meet.admitAll");
5971
5993
  const rows = await repository.admitAll(meeting.id, by);
5972
5994
  for (const row of rows) {
@@ -7145,6 +7167,7 @@ function createRoomEngine(options) {
7145
7167
  const autoOff = createVideoAutoOff(() => connectionPolicy.videoAutoOffAfterPoorSeconds * 1e3, now);
7146
7168
  const lkQuality = /* @__PURE__ */ new Map();
7147
7169
  let silent = /* @__PURE__ */ new Set();
7170
+ const saidBye = /* @__PURE__ */ new Set();
7148
7171
  let healthTimer = null;
7149
7172
  let lastBeatAt = 0;
7150
7173
  let wasUnstable = false;
@@ -7175,6 +7198,7 @@ function createRoomEngine(options) {
7175
7198
  assessor.reset();
7176
7199
  autoOff.reset();
7177
7200
  silent = /* @__PURE__ */ new Set();
7201
+ saidBye.clear();
7178
7202
  lkQuality.clear();
7179
7203
  lastBeatAt = 0;
7180
7204
  wasUnstable = false;
@@ -7232,6 +7256,13 @@ function createRoomEngine(options) {
7232
7256
  await target?.disconnect();
7233
7257
  }
7234
7258
  async function leaveRoom() {
7259
+ const target = driver;
7260
+ if (target !== null && store.snapshot().phase === "in_call") {
7261
+ await Promise.race([
7262
+ target.publishData(ALIVE_TOPIC, encodeBye()).catch(() => void 0),
7263
+ new Promise((resolve) => setTimeout(resolve, 300))
7264
+ ]);
7265
+ }
7235
7266
  store.dispatch({ type: "leave" });
7236
7267
  await dropRoom();
7237
7268
  options.onLeft?.(null);
@@ -7327,8 +7358,6 @@ function createRoomEngine(options) {
7327
7358
  },
7328
7359
  participantLeft(who, reason) {
7329
7360
  departures.set(who, reason);
7330
- liveness.forget(who);
7331
- silent.delete(who);
7332
7361
  },
7333
7362
  roomMetadata(metadata) {
7334
7363
  let parsed = {};
@@ -7371,8 +7400,12 @@ function createRoomEngine(options) {
7371
7400
  const snap = store.snapshot();
7372
7401
  if (snap.phase !== "in_call" || snap.connection !== "stable") return;
7373
7402
  startHealth();
7403
+ const before = assessor.current();
7374
7404
  const quality = assessor.push(reading);
7375
7405
  const path = pathOf(reading);
7406
+ if (quality !== before) {
7407
+ report("info", `meet: this connection reads ${quality} (round trip ${reading.rttMs ?? "?"} ms, path ${path})`, "Diagnostic only.");
7408
+ }
7376
7409
  patchLocalNetwork({ quality, ...path !== "unknown" ? { path } : {} });
7377
7410
  refreshQualities();
7378
7411
  const cameraOn = snap.media.camera.enabled;
@@ -7387,6 +7420,11 @@ function createRoomEngine(options) {
7387
7420
  connectionState(state) {
7388
7421
  supervisor.driverState(state);
7389
7422
  if (state === "connected" && store.snapshot().phase === "joining") store.dispatch({ type: "connected" });
7423
+ if (state === "connected") {
7424
+ startHealth();
7425
+ lastBeatAt = 0;
7426
+ healthTick();
7427
+ }
7390
7428
  },
7391
7429
  transcription(event) {
7392
7430
  const snapshot = store.snapshot();
@@ -7478,6 +7516,10 @@ function createRoomEngine(options) {
7478
7516
  }
7479
7517
  const sender = personOf(from ?? "");
7480
7518
  if (from !== null) {
7519
+ if (topic === ALIVE_TOPIC && isBye(payload)) {
7520
+ saidBye.add(from);
7521
+ return;
7522
+ }
7481
7523
  const beat = topic === ALIVE_TOPIC ? decodeBeat(payload) : null;
7482
7524
  const before = liveness.reported(from);
7483
7525
  liveness.heard(from, beat);
@@ -7710,10 +7752,17 @@ function createRoomEngine(options) {
7710
7752
  const graceMs = connectionPolicy.reconnectGraceSeconds * 1e3;
7711
7753
  for (const [who, reason] of departures) {
7712
7754
  const last = previous.get(who);
7713
- if (last !== void 0 && !last.isLocal && !present.has(who) && graceMs > 0 && isUnannouncedDeparture(reason)) {
7755
+ const dropped = !saidBye.has(who) && (isUnannouncedDeparture(reason) || silent.has(who));
7756
+ report("info", `meet: ${who} left the room (${reason ?? "no reason"}${dropped ? ", held as reconnecting" : ""})`, "Diagnostic only.");
7757
+ if (last !== void 0 && !last.isLocal && !present.has(who) && graceMs > 0 && dropped) {
7714
7758
  ghosts.set(who, { participant: last, until: at + graceMs });
7715
7759
  }
7716
7760
  }
7761
+ for (const who of departures.keys()) {
7762
+ liveness.forget(who);
7763
+ silent.delete(who);
7764
+ saidBye.delete(who);
7765
+ }
7717
7766
  departures.clear();
7718
7767
  liveRoster = list;
7719
7768
  const localNow = list.find((p) => p.isLocal);