@ai-matrx/meet 0.11.44 → 0.11.46

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/dist/react.cjs CHANGED
@@ -358,7 +358,14 @@ function createTabClaim(platform = webTabPlatform()) {
358
358
  if (message.kind === "tab-alive?" && mine !== null && message.tabId === mine && message.from !== self) {
359
359
  platform.bus?.post({ kind: "tab-alive", tabId: mine });
360
360
  } else if (message.kind === "who-is-in?" && current !== null && mine !== null && message.from !== self && message.room === current.room && message.person === current.person) {
361
- platform.bus?.post({ kind: "in-room", room: current.room, person: current.person, tabId: mine, to: message.from });
361
+ platform.bus?.post({
362
+ kind: "in-room",
363
+ room: current.room,
364
+ person: current.person,
365
+ tabId: mine,
366
+ to: message.from,
367
+ rule: current.rule
368
+ });
362
369
  }
363
370
  }) ?? (() => {
364
371
  });
@@ -388,28 +395,32 @@ function createTabClaim(platform = webTabPlatform()) {
388
395
  return candidate;
389
396
  }
390
397
  const tabId = claim();
398
+ async function presence(room, person) {
399
+ const bus = platform.bus;
400
+ if (bus === null) return null;
401
+ let seen = null;
402
+ const stop = bus.subscribe((message) => {
403
+ if (message.kind === "in-room" && message.to === self && message.room === room && message.person === person) {
404
+ seen = { rule: seen?.rule ?? message.rule ?? null };
405
+ }
406
+ });
407
+ bus.post({ kind: "who-is-in?", room, person, from: self });
408
+ await wait(TAB_ANSWER_MS);
409
+ stop();
410
+ return seen;
411
+ }
391
412
  return {
392
413
  tabId,
393
- inRoom(room, person) {
394
- current = { room, person };
414
+ inRoom(room, person, rule) {
415
+ current = { room, person, rule: rule ?? null };
395
416
  },
396
417
  left() {
397
418
  current = null;
398
419
  },
399
420
  async presentElsewhere(room, person) {
400
- const bus = platform.bus;
401
- if (bus === null) return false;
402
- let seen = false;
403
- const stop = bus.subscribe((message) => {
404
- if (message.kind === "in-room" && message.to === self && message.room === room && message.person === person) {
405
- seen = true;
406
- }
407
- });
408
- bus.post({ kind: "who-is-in?", room, person, from: self });
409
- await wait(TAB_ANSWER_MS);
410
- stop();
411
- return seen;
421
+ return await presence(room, person) !== null;
412
422
  },
423
+ presenceElsewhere: (room, person) => presence(room, person),
413
424
  dispose() {
414
425
  unsubscribe();
415
426
  }
@@ -587,7 +598,8 @@ function createWebResumeStorage() {
587
598
  }
588
599
  };
589
600
  }
590
- var CLEARS_RESUME = (phase) => phase === "left" || phase === "removed" || phase === "superseded" || phase === "inactive" || phase === "ended" || phase === "record" || phase === "prejoin" || phase.startsWith("refused:") || phase.startsWith("waiting:");
601
+ var CLEARS_RESUME = (phase) => phase === "left" || phase === "removed" || phase === "superseded" || phase === "inactive" || phase === "ended" || phase === "record" || phase === "prejoin" || phase === "denied" || phase === "knock_expired" || phase.startsWith("refused:");
602
+ var WAITING_RESUMES = (phase) => phase.startsWith("waiting:");
591
603
  function autoRejoinDelayMs(attempt, random = Math.random) {
592
604
  const base = Math.min(15e3, 1e3 * 2 ** Math.max(0, attempt));
593
605
  return Math.round(base * (0.5 + random()));
@@ -719,6 +731,10 @@ function createConnectionSupervisor(options) {
719
731
  rejoinAttempts = 0;
720
732
  stopRejoin();
721
733
  saveMarker(true);
734
+ } else if (WAITING_RESUMES(phase)) {
735
+ stopRejoin();
736
+ driverRegion = null;
737
+ saveMarker(true);
722
738
  } else if (phase === "disconnected") {
723
739
  driverRegion = null;
724
740
  saveMarker(true);
@@ -1111,10 +1127,10 @@ var MEET_ROUTES = {
1111
1127
  recordingStop: "/api/v1/meet/recording/stop",
1112
1128
  noteTakerStart: "/api/v1/meet/agent/start",
1113
1129
  noteTakerStop: "/api/v1/meet/agent/stop",
1114
- /** In-meeting Q&A. Streams NDJSON; body is `{meeting_id, question}` only. */
1115
- intelligenceAsk: "/api/v1/meet/intelligence/ask",
1116
1130
  /** Anyone in the meeting (guests with the room pass included) asks for captions for themselves. */
1117
1131
  noteTakerCaptions: "/api/v1/meet/agent/captions",
1132
+ /** In-meeting Q&A. Streams NDJSON; body is `{meeting_id, question}` only. */
1133
+ intelligenceAsk: "/api/v1/meet/intelligence/ask",
1118
1134
  /** A host ends the meeting for everyone. Served by MRI-A6. */
1119
1135
  meetingEnd: "/api/v1/meet/end",
1120
1136
  /**
@@ -1239,6 +1255,7 @@ function parseRoomToken(body, roomName) {
1239
1255
  connectionIdentity: readString(payload, "connection_identity") ?? personKey,
1240
1256
  connectionKind: readString(payload, "connection_kind") === "companion" ? "companion" : "main",
1241
1257
  secondDeviceChoice,
1258
+ secondDeviceRule: readString(payload, "second_device_rule"),
1242
1259
  displayName: readString(payload, "display_name") ?? readString(payload, "displayName") ?? identity,
1243
1260
  expiresAt,
1244
1261
  canPublish: readBoolean(payload, "can_publish", true),
@@ -1952,6 +1969,12 @@ var JOINABLE_FROM = /* @__PURE__ */ new Set([
1952
1969
  // allows the press; the server's gate decides whether it knocks or is refused again.
1953
1970
  "refused:lobby_rejects"
1954
1971
  ]);
1972
+ var RETRYABLE_REFUSALS = /* @__PURE__ */ new Set([
1973
+ "refused:locked",
1974
+ "refused:too_early",
1975
+ "refused:meeting_full",
1976
+ "refused:platform_capacity"
1977
+ ]);
1955
1978
  var NOT_ENDABLE = /* @__PURE__ */ new Set([
1956
1979
  "ended",
1957
1980
  "record",
@@ -2017,7 +2040,7 @@ function nextPhase(from, event) {
2017
2040
  case "link_state":
2018
2041
  return from === "resolving" && event.reason !== null && isJoinRefusalReason(event.reason) ? refusalPhase(event.reason) : null;
2019
2042
  case "join":
2020
- return JOINABLE_FROM.has(from) ? "joining" : null;
2043
+ return JOINABLE_FROM.has(from) || RETRYABLE_REFUSALS.has(from) ? "joining" : null;
2021
2044
  case "auto_rejoin":
2022
2045
  return from === "disconnected" ? "joining" : null;
2023
2046
  case "resumed":
@@ -2859,6 +2882,10 @@ var RPCS = {
2859
2882
  admitAll: "meet_admit_all",
2860
2883
  setLock: "meet_set_lock",
2861
2884
  recordConsent: "meet_record_consent",
2885
+ /** One meeting rule as the gate obeys it (the knob ladder, this meeting's rung included). */
2886
+ meetingRule: "meet_policy",
2887
+ /** A host sets one rule for THIS meeting (the `meet_meeting` rung); refusals come back as `{ok:false}`. */
2888
+ setMeetingRule: "meet_policy_set",
2862
2889
  /*
2863
2890
  * ── MEET WAVE 1 — management (0.8). Every write is a SECURITY DEFINER door
2864
2891
  * authorized by the meeting's host or a real iam grant (editor to change,
@@ -2905,7 +2932,7 @@ var INVITEE_COLUMNS = "id,meeting_id,invitee_user_id,email,display_name,role,rsv
2905
2932
  var RECORDING_COLUMNS = "id,meeting_id,state,started_at,ended_at,started_by,file_id,failure_reason";
2906
2933
  var SEGMENT_COLUMNS = "id,meeting_id,identity,speaker,text,started_at,is_final,metadata";
2907
2934
  var NOTE_COLUMNS = "id,meeting_id,text,kind,assignee_user_id,created_at,metadata";
2908
- var ATTENDEE_COLUMNS = "id,meeting_id,identity,participant_user_id,display_name,avatar_url,role,admission_state,knocked_at,knock_expires_at,is_agent,joined_at,left_at,sessions:metadata->sessions";
2935
+ var ATTENDEE_COLUMNS = "id,meeting_id,identity,participant_user_id,display_name,avatar_url,role,admission_state,knocked_at,knock_expires_at,is_agent,joined_at,left_at,sessions:metadata->sessions,knock_flag:metadata->>knock_flag";
2909
2936
  function projectStays(value) {
2910
2937
  if (!Array.isArray(value)) return [];
2911
2938
  const stays = [];
@@ -3305,6 +3332,8 @@ function createMeetRepository(options) {
3305
3332
  admissionState: states.includes(admission) ? admission : "admitted",
3306
3333
  knockedAt: text(row, "knocked_at"),
3307
3334
  knockExpiresAt: text(row, "knock_expires_at"),
3335
+ // The list read selects `metadata->>knock_flag`; a Postgres Changes row carries `metadata`.
3336
+ knockFlag: text(row, "knock_flag") ?? (typeof row["metadata"] === "object" && row["metadata"] !== null ? text(row["metadata"], "knock_flag") : null),
3308
3337
  isAgent: bool(row, "is_agent", false),
3309
3338
  joinedAt: text(row, "joined_at"),
3310
3339
  leftAt: text(row, "left_at"),
@@ -3628,6 +3657,25 @@ function createMeetRepository(options) {
3628
3657
  );
3629
3658
  return projectMeeting(row);
3630
3659
  },
3660
+ async meetingRule(meetingId, key) {
3661
+ const response = await db.rpc(RPCS.meetingRule, { p_meeting_id: meetingId, p_key: key });
3662
+ if (response.error !== null) throw normalizeMeetError(response.error, "meet.meetingRule");
3663
+ return response.data;
3664
+ },
3665
+ async setMeetingRule(meetingId, key, value) {
3666
+ const response = await db.rpc(RPCS.setMeetingRule, { p_meeting_id: meetingId, p_key: key, p_value: value });
3667
+ if (response.error !== null) throw normalizeMeetError(response.error, "meet.setMeetingRule");
3668
+ const answer = response.data ?? {};
3669
+ if (answer["ok"] !== true) {
3670
+ throw new MeetError(
3671
+ "forbidden",
3672
+ typeof answer["detail"] === "string" ? answer["detail"] : "This meeting's rule was not changed.",
3673
+ "Only the meeting's host or a co-host sets its rules; an organization can lock a rule for everyone.",
3674
+ answer
3675
+ );
3676
+ }
3677
+ return answer["effective_value"];
3678
+ },
3631
3679
  async transcript(meetingId, limit = 1e3) {
3632
3680
  const response = await db.from(TABLES.transcripts).select(SEGMENT_COLUMNS).eq("meeting_id", meetingId).order("started_at", { ascending: true }).order("id", { ascending: true }).limit(limit);
3633
3681
  if (response.error !== null) throw normalizeMeetError(response.error, "meet.transcript");
@@ -4522,7 +4570,8 @@ function lobbyEntryFrom(attendee, now = Date.now()) {
4522
4570
  knockedAt: attendee.knockedAt ?? attendee.joinedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
4523
4571
  knockExpiresAt: attendee.knockExpiresAt ?? null,
4524
4572
  decidedAt: null,
4525
- decidedBy: null
4573
+ decidedBy: null,
4574
+ flag: attendee.knockFlag ?? null
4526
4575
  };
4527
4576
  }
4528
4577
  function deniedEntryFrom(attendee) {
@@ -4694,11 +4743,15 @@ function createMeetingSession(options) {
4694
4743
  }
4695
4744
  let policyTimer = null;
4696
4745
  let policyReading = false;
4746
+ let policyInFlight = null;
4697
4747
  async function readLivePolicy() {
4698
4748
  const meeting = store.snapshot().meeting;
4699
4749
  const pass = options.resolvePass?.() ?? null;
4700
- if (meeting === null || pass === null || policyReading) return;
4750
+ if (meeting === null || pass === null) return;
4751
+ if (policyReading) return policyInFlight ?? void 0;
4701
4752
  policyReading = true;
4753
+ let done = () => void 0;
4754
+ policyInFlight = new Promise((resolve) => done = resolve);
4702
4755
  try {
4703
4756
  const response = await options.api.request({
4704
4757
  path: `${MEET_ROUTES.state}?meeting_id=${encodeURIComponent(meeting.id)}`,
@@ -4719,6 +4772,8 @@ function createMeetingSession(options) {
4719
4772
  } catch {
4720
4773
  } finally {
4721
4774
  policyReading = false;
4775
+ policyInFlight = null;
4776
+ done();
4722
4777
  }
4723
4778
  }
4724
4779
  function syncLivePolicyTimer() {
@@ -5017,6 +5072,7 @@ function createMeetingSession(options) {
5017
5072
  },
5018
5073
  stopWaiting,
5019
5074
  readState: readState2,
5075
+ readLivePolicy,
5020
5076
  async readExit(connection, held) {
5021
5077
  const meeting = store.snapshot().meeting;
5022
5078
  const pass = held ?? options.resolvePass?.() ?? null;
@@ -5054,6 +5110,17 @@ function createMeetingSession(options) {
5054
5110
  async askAgain() {
5055
5111
  await options.rejoin?.();
5056
5112
  },
5113
+ async meetingRule(key) {
5114
+ const meeting = requireMeeting("meet.meetingRule");
5115
+ return repository.meetingRule(meeting.id, key);
5116
+ },
5117
+ async setMeetingRule(key, value) {
5118
+ requireHostUser("meet.setMeetingRule");
5119
+ const meeting = requireMeeting("meet.setMeetingRule");
5120
+ const effective = await repository.setMeetingRule(meeting.id, key, value);
5121
+ hint();
5122
+ return effective;
5123
+ },
5057
5124
  async setLock(locked) {
5058
5125
  const by = requireHostUser("meet.setLock");
5059
5126
  const meeting = requireMeeting("meet.setLock");
@@ -5831,6 +5898,7 @@ var STATES = [
5831
5898
  "ended"
5832
5899
  ];
5833
5900
  function readState(value) {
5901
+ if (value === "dispatching") return "joining";
5834
5902
  return typeof value === "string" && STATES.includes(value) ? value : null;
5835
5903
  }
5836
5904
  function isNoteTaker(participant) {
@@ -6319,7 +6387,7 @@ function createRoomEngine(options) {
6319
6387
  const { secondDevice: _answered, ...rest } = lastRequest;
6320
6388
  lastRequest = rest;
6321
6389
  }
6322
- options.tabClaim?.inRoom(String(request.roomName), String(token.identity));
6390
+ options.tabClaim?.inRoom(String(request.roomName), String(token.identity), token.secondDeviceRule ?? null);
6323
6391
  current = token;
6324
6392
  lastServerUrl = token.serverUrl;
6325
6393
  connectionPolicy = token.connectionPolicy ?? DEFAULT_CONNECTION_POLICY;
@@ -6631,10 +6699,12 @@ function createRoomEngine(options) {
6631
6699
  if (claim === void 0 || userId === null) return false;
6632
6700
  const before = store.snapshot();
6633
6701
  if (before.phase !== "prejoin" || before.secondDevice !== null) return false;
6634
- if (!await claim.presentElsewhere(String(roomName), `user:${String(userId)}`)) return false;
6702
+ const person = `user:${String(userId)}`;
6703
+ const found = claim.presenceElsewhere !== void 0 ? await claim.presenceElsewhere(String(roomName), person) : await claim.presentElsewhere(String(roomName), person) ? { rule: null } : null;
6704
+ if (found === null || found.rule === "allow_both") return false;
6635
6705
  const now2 = store.snapshot();
6636
6706
  if (now2.phase !== "prejoin" || now2.secondDevice !== null) return false;
6637
- store.patch({ secondDevice: { rule: "ask_switch_or_join" } });
6707
+ store.patch({ secondDevice: { rule: found.rule ?? "ask_switch_or_join" } });
6638
6708
  report("info", "meet: this browser already has you in this meeting \u2014 offering the choice before Join");
6639
6709
  return true;
6640
6710
  },
@@ -6890,6 +6960,7 @@ function createInRoomClient(options) {
6890
6960
  action: request.action,
6891
6961
  ...request.identity != null ? { identity: request.identity } : {},
6892
6962
  ...request.name !== void 0 ? { name: request.name } : {},
6963
+ ...request.value !== void 0 ? { value: request.value } : {},
6893
6964
  ...request.reason !== void 0 ? { reason: request.reason } : {}
6894
6965
  },
6895
6966
  ...hdrs !== void 0 ? { headers: hdrs } : {}
@@ -9265,7 +9336,10 @@ function useEndMeeting() {
9265
9336
  function useConsentNotice() {
9266
9337
  const snapshot = useMeetSnapshot();
9267
9338
  const agent = noteTakerParticipant(snapshot?.participants ?? []);
9268
- if (agent === null) return null;
9339
+ if (agent === null) {
9340
+ const state = noteTakerStatus({ meeting: snapshot?.meeting ?? null, participants: [] }).state;
9341
+ return state === "joining" ? "AI notes started. The note-taker is joining this meeting." : null;
9342
+ }
9269
9343
  return `AI notes are on. ${agent.displayName} is in this meeting, transcribing what is said and writing notes everyone here can read.`;
9270
9344
  }
9271
9345
  function useMediaSupervisor() {
@@ -10439,6 +10513,10 @@ function actionsFor(host) {
10439
10513
  leave: () => engine.leave(),
10440
10514
  backToPrejoin: () => engine.backToPrejoin(),
10441
10515
  retry: () => {
10516
+ if (RETRYABLE_REFUSALS.has(store.snapshot().phase)) {
10517
+ void engine.rejoin().catch(() => void 0);
10518
+ return;
10519
+ }
10442
10520
  store.dispatch({ type: "retry", click: true });
10443
10521
  },
10444
10522
  resolved: () => {
@@ -12158,6 +12236,7 @@ function ControlBar({ onLeave }) {
12158
12236
  ]
12159
12237
  }
12160
12238
  ),
12239
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(WaitingRoomRule, {}),
12161
12240
  queue.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
12162
12241
  MenuItem,
12163
12242
  {
@@ -12377,6 +12456,58 @@ function MenuItem({
12377
12456
  }
12378
12457
  );
12379
12458
  }
12459
+ var LOBBY_BYPASS_CHOICES = [
12460
+ { value: "everyone", label: "Everyone" },
12461
+ { value: "org_and_invited", label: "Your organization and invited people" },
12462
+ { value: "org", label: "Your organization" },
12463
+ { value: "invited", label: "Invited people" },
12464
+ { value: "host_only", label: "Only hosts" }
12465
+ ];
12466
+ function WaitingRoomRule() {
12467
+ const host = useRequiredMeetHost();
12468
+ const [open, setOpen] = (0, import_react10.useState)(false);
12469
+ const [value, setValue] = (0, import_react10.useState)(null);
12470
+ const [notice, setNotice] = (0, import_react10.useState)(null);
12471
+ (0, import_react10.useEffect)(() => {
12472
+ let live = true;
12473
+ void host.meetings.meetingRule("lobby_bypass").then((rule) => {
12474
+ if (live) setValue(typeof rule === "string" ? rule : null);
12475
+ }).catch(() => void 0);
12476
+ return () => {
12477
+ live = false;
12478
+ };
12479
+ }, [host]);
12480
+ const current = LOBBY_BYPASS_CHOICES.find((choice) => choice.value === value)?.label ?? null;
12481
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
12482
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(MenuItem, { onSelect: () => setOpen(!open), children: [
12483
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(LockIcon, { width: 16, height: 16 }),
12484
+ " Who can skip the waiting room",
12485
+ current !== null ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "mx-meet__menu-value", children: current }) : null
12486
+ ] }),
12487
+ open ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { role: "group", "aria-label": "Who can skip the waiting room", "data-meet-rule": "lobby_bypass", children: [
12488
+ LOBBY_BYPASS_CHOICES.map((choice) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
12489
+ "button",
12490
+ {
12491
+ type: "button",
12492
+ role: "menuitemradio",
12493
+ "aria-checked": choice.value === value,
12494
+ className: "mx-meet__menu-item mx-meet__menu-item--sub",
12495
+ onClick: () => {
12496
+ setNotice(null);
12497
+ void host.meetings.setMeetingRule("lobby_bypass", choice.value).then(() => setValue(choice.value)).catch((error) => setNotice(error instanceof Error ? error.message : String(error)));
12498
+ },
12499
+ children: [
12500
+ choice.value === value ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CheckIcon, { width: 14, height: 14 }) : null,
12501
+ " ",
12502
+ choice.label
12503
+ ]
12504
+ },
12505
+ choice.value
12506
+ )),
12507
+ notice !== null ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "mx-meet__menu-note", role: "status", children: notice }) : null
12508
+ ] }) : null
12509
+ ] });
12510
+ }
12380
12511
  function Toast({ text: text3, failed, onDone }) {
12381
12512
  (0, import_react10.useEffect)(() => {
12382
12513
  const timer = setTimeout(onDone, failed ? 7e3 : 4e3);
@@ -12690,6 +12821,17 @@ function ChatPanel({
12690
12821
  const snapshot = useMeetSnapshot();
12691
12822
  const room = useRoom();
12692
12823
  const live = useLivePolicy();
12824
+ const host = useMeetHost();
12825
+ const [rulesChecked, setRulesChecked] = (0, import_react10.useState)(false);
12826
+ (0, import_react10.useEffect)(() => {
12827
+ let alive = true;
12828
+ const done = () => alive && setRulesChecked(true);
12829
+ if (host === null) done();
12830
+ else void host.meetings.readLivePolicy().then(done, done);
12831
+ return () => {
12832
+ alive = false;
12833
+ };
12834
+ }, [host]);
12693
12835
  const [draft, setDraft] = (0, import_react10.useState)("");
12694
12836
  const [recipient, setRecipient] = (0, import_react10.useState)(to);
12695
12837
  const listRef = (0, import_react10.useRef)(null);
@@ -12714,7 +12856,7 @@ function ChatPanel({
12714
12856
  snapshot?.chatLoaded !== true && messages.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("li", { className: "mx-meet__panel-note", children: "Reading earlier messages\u2026" }) : messages.length === 0 && snapshot?.chatHistoryError == null ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("li", { className: "mx-meet__panel-note", children: "No messages yet. Messages to everyone stay with the meeting, so people who join later see them." }) : null,
12715
12857
  messages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ChatLine, { message, local, people }, message.id))
12716
12858
  ] }),
12717
- !live.canChat ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "mx-meet__panel-note", role: "status", "data-meet-notice": "chat-restricted", children: live.chatMode === "off" ? "Chat is turned off for this meeting." : "Chat is limited to the host and co-hosts in this meeting." }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
12859
+ !rulesChecked ? null : !live.canChat ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "mx-meet__panel-note", role: "status", "data-meet-notice": "chat-restricted", children: live.chatMode === "off" ? "Chat is turned off for this meeting." : "Chat is limited to the host and co-hosts in this meeting." }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
12718
12860
  "form",
12719
12861
  {
12720
12862
  className: "mx-meet__composer mx-meet__composer--chat",
@@ -13465,7 +13607,11 @@ function WaitingList({ lobby }) {
13465
13607
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Avatar, { name: entry.displayName, url: entry.avatarUrl, size: 32 }),
13466
13608
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "mx-meet__person-name", children: [
13467
13609
  entry.displayName,
13468
- entry.userId === null ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "mx-meet__badge", children: "Guest" }) : null
13610
+ entry.userId === null ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "mx-meet__badge", children: "Guest" }) : null,
13611
+ entry.flag === "previously_removed" ? (
13612
+ // `mod-removed-bypass`: advice for the host, never a block — Admit stays.
13613
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "mx-meet__badge mx-meet__badge--warn", "data-meet-notice": "knock-flagged", children: "Previously removed" })
13614
+ ) : null
13469
13615
  ] }),
13470
13616
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ControlButton, { label: `Admit ${entry.displayName}`, onClick: () => void lobby.admit(entry), children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CheckIcon, { width: 16, height: 16 }) }),
13471
13617
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
@@ -14090,7 +14236,20 @@ function ExitScreen({ onLeave }) {
14090
14236
  const { phase, reason, host, snapshot, actions, call, connection } = useMeetCore();
14091
14237
  const [busy, setBusy] = (0, import_react10.useState)(false);
14092
14238
  const failure = snapshot?.failure ?? null;
14093
- const retryLeft = useCountdown(phase === "refused:lobby_rejects" ? failure?.retryAt ?? null : null);
14239
+ const retryLeft = useCountdown(
14240
+ phase === "refused:lobby_rejects" || phase === "refused:too_early" ? failure?.retryAt ?? null : null
14241
+ );
14242
+ const reaskedAtZero = (0, import_react10.useRef)(false);
14243
+ (0, import_react10.useEffect)(() => {
14244
+ if (phase !== "refused:too_early") {
14245
+ reaskedAtZero.current = false;
14246
+ return;
14247
+ }
14248
+ if (retryLeft === 0 && !reaskedAtZero.current) {
14249
+ reaskedAtZero.current = true;
14250
+ actions.retry();
14251
+ }
14252
+ }, [phase, retryLeft, actions]);
14094
14253
  const rejoin = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
14095
14254
  import_design_system.Button,
14096
14255
  {
@@ -14128,11 +14287,19 @@ function ExitScreen({ onLeave }) {
14128
14287
  if (phase.startsWith("refused:")) {
14129
14288
  const code = phase.slice("refused:".length);
14130
14289
  title = REFUSED_TITLES[code] ?? "You can't join this meeting";
14131
- line = code === "too_early" || code === "registration_required" || code === "cancelled" ? failure?.remedy ?? null : null;
14290
+ line = code === "too_early" || code === "registration_required" || code === "cancelled" || code === "wrong_account" ? failure?.remedy ?? null : null;
14291
+ if (code === "too_early" && retryLeft !== null && retryLeft > 0) line = `Opens in ${clockText(retryLeft)}`;
14132
14292
  actionsNode = /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
14133
14293
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_design_system.Button, { type: "button", variant: "outline", onClick: actions.retry, children: "Try again" }),
14134
14294
  home
14135
14295
  ] });
14296
+ if (code === "sign_in_required" || code === "wrong_account") {
14297
+ const back = typeof window === "undefined" ? "/" : `${window.location.pathname}${window.location.search}`;
14298
+ actionsNode = /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
14299
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_design_system.Button, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("a", { href: `/login?redirectTo=${encodeURIComponent(back)}`, children: code === "sign_in_required" ? "Sign in" : "Switch account" }) }),
14300
+ home
14301
+ ] });
14302
+ }
14136
14303
  if (code === "lobby_rejects") {
14137
14304
  if (retryLeft !== null && retryLeft > 0) {
14138
14305
  line = `You can ask again in ${clockText(retryLeft)}`;