@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.
@@ -115,6 +115,33 @@ var import_react10 = require("react");
115
115
  // src/react/facade.ts
116
116
  var import_react8 = require("react");
117
117
 
118
+ // src/core/phase.ts
119
+ var LIVE_PHASES = /* @__PURE__ */ new Set(["joining", "in_call"]);
120
+ var WAITING_PHASES = /* @__PURE__ */ new Set([
121
+ "waiting:host_not_started",
122
+ "waiting:knocking",
123
+ "denied",
124
+ "knock_expired"
125
+ ]);
126
+ var RETRYABLE_REFUSALS = /* @__PURE__ */ new Set([
127
+ "refused:locked",
128
+ "refused:too_early",
129
+ "refused:meeting_full",
130
+ "refused:platform_capacity"
131
+ ]);
132
+ function isLivePhase(phase) {
133
+ return LIVE_PHASES.has(phase);
134
+ }
135
+ function isWaitingPhase(phase) {
136
+ return WAITING_PHASES.has(phase);
137
+ }
138
+ function isRefusedPhase(phase) {
139
+ return phase.startsWith("refused:");
140
+ }
141
+ function reasonOf(phase) {
142
+ return isRefusedPhase(phase) ? phase.slice("refused:".length) : null;
143
+ }
144
+
118
145
  // src/core/names.ts
119
146
  var CONNECTION_SEPARATOR = "~";
120
147
  function personOf(identity) {
@@ -266,10 +293,10 @@ var MEET_ROUTES = {
266
293
  recordingStop: "/api/v1/meet/recording/stop",
267
294
  noteTakerStart: "/api/v1/meet/agent/start",
268
295
  noteTakerStop: "/api/v1/meet/agent/stop",
269
- /** In-meeting Q&A. Streams NDJSON; body is `{meeting_id, question}` only. */
270
- intelligenceAsk: "/api/v1/meet/intelligence/ask",
271
296
  /** Anyone in the meeting (guests with the room pass included) asks for captions for themselves. */
272
297
  noteTakerCaptions: "/api/v1/meet/agent/captions",
298
+ /** In-meeting Q&A. Streams NDJSON; body is `{meeting_id, question}` only. */
299
+ intelligenceAsk: "/api/v1/meet/intelligence/ask",
273
300
  /** A host ends the meeting for everyone. Served by MRI-A6. */
274
301
  meetingEnd: "/api/v1/meet/end",
275
302
  /**
@@ -430,27 +457,6 @@ function isObserverRole(role) {
430
457
  return role === "viewer";
431
458
  }
432
459
 
433
- // src/core/phase.ts
434
- var LIVE_PHASES = /* @__PURE__ */ new Set(["joining", "in_call"]);
435
- var WAITING_PHASES = /* @__PURE__ */ new Set([
436
- "waiting:host_not_started",
437
- "waiting:knocking",
438
- "denied",
439
- "knock_expired"
440
- ]);
441
- function isLivePhase(phase) {
442
- return LIVE_PHASES.has(phase);
443
- }
444
- function isWaitingPhase(phase) {
445
- return WAITING_PHASES.has(phase);
446
- }
447
- function isRefusedPhase(phase) {
448
- return phase.startsWith("refused:");
449
- }
450
- function reasonOf(phase) {
451
- return isRefusedPhase(phase) ? phase.slice("refused:".length) : null;
452
- }
453
-
454
460
  // src/core/app-panel.ts
455
461
  function appPanelOf(meeting) {
456
462
  const raw = meeting?.metadata?.["app_panel"];
@@ -852,6 +858,7 @@ var STATES = [
852
858
  "ended"
853
859
  ];
854
860
  function readState(value) {
861
+ if (value === "dispatching") return "joining";
855
862
  return typeof value === "string" && STATES.includes(value) ? value : null;
856
863
  }
857
864
  function isNoteTaker(participant) {
@@ -1696,7 +1703,10 @@ function useEndMeeting() {
1696
1703
  function useConsentNotice() {
1697
1704
  const snapshot = useMeetSnapshot();
1698
1705
  const agent = noteTakerParticipant(snapshot?.participants ?? []);
1699
- if (agent === null) return null;
1706
+ if (agent === null) {
1707
+ const state = noteTakerStatus({ meeting: snapshot?.meeting ?? null, participants: [] }).state;
1708
+ return state === "joining" ? "AI notes started. The note-taker is joining this meeting." : null;
1709
+ }
1700
1710
  return `AI notes are on. ${agent.displayName} is in this meeting, transcribing what is said and writing notes everyone here can read.`;
1701
1711
  }
1702
1712
  function useMediaSupervisor() {
@@ -2095,6 +2105,10 @@ function actionsFor(host) {
2095
2105
  leave: () => engine.leave(),
2096
2106
  backToPrejoin: () => engine.backToPrejoin(),
2097
2107
  retry: () => {
2108
+ if (RETRYABLE_REFUSALS.has(store.snapshot().phase)) {
2109
+ void engine.rejoin().catch(() => void 0);
2110
+ return;
2111
+ }
2098
2112
  store.dispatch({ type: "retry", click: true });
2099
2113
  },
2100
2114
  resolved: () => {
@@ -3830,6 +3844,7 @@ function ControlBar({ onLeave }) {
3830
3844
  ]
3831
3845
  }
3832
3846
  ),
3847
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(WaitingRoomRule, {}),
3833
3848
  queue.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
3834
3849
  MenuItem,
3835
3850
  {
@@ -4049,6 +4064,58 @@ function MenuItem({
4049
4064
  }
4050
4065
  );
4051
4066
  }
4067
+ var LOBBY_BYPASS_CHOICES = [
4068
+ { value: "everyone", label: "Everyone" },
4069
+ { value: "org_and_invited", label: "Your organization and invited people" },
4070
+ { value: "org", label: "Your organization" },
4071
+ { value: "invited", label: "Invited people" },
4072
+ { value: "host_only", label: "Only hosts" }
4073
+ ];
4074
+ function WaitingRoomRule() {
4075
+ const host = useRequiredMeetHost();
4076
+ const [open, setOpen] = (0, import_react10.useState)(false);
4077
+ const [value, setValue] = (0, import_react10.useState)(null);
4078
+ const [notice, setNotice] = (0, import_react10.useState)(null);
4079
+ (0, import_react10.useEffect)(() => {
4080
+ let live = true;
4081
+ void host.meetings.meetingRule("lobby_bypass").then((rule) => {
4082
+ if (live) setValue(typeof rule === "string" ? rule : null);
4083
+ }).catch(() => void 0);
4084
+ return () => {
4085
+ live = false;
4086
+ };
4087
+ }, [host]);
4088
+ const current = LOBBY_BYPASS_CHOICES.find((choice) => choice.value === value)?.label ?? null;
4089
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
4090
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(MenuItem, { onSelect: () => setOpen(!open), children: [
4091
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(LockIcon, { width: 16, height: 16 }),
4092
+ " Who can skip the waiting room",
4093
+ current !== null ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "mx-meet__menu-value", children: current }) : null
4094
+ ] }),
4095
+ open ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { role: "group", "aria-label": "Who can skip the waiting room", "data-meet-rule": "lobby_bypass", children: [
4096
+ LOBBY_BYPASS_CHOICES.map((choice) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
4097
+ "button",
4098
+ {
4099
+ type: "button",
4100
+ role: "menuitemradio",
4101
+ "aria-checked": choice.value === value,
4102
+ className: "mx-meet__menu-item mx-meet__menu-item--sub",
4103
+ onClick: () => {
4104
+ setNotice(null);
4105
+ void host.meetings.setMeetingRule("lobby_bypass", choice.value).then(() => setValue(choice.value)).catch((error) => setNotice(error instanceof Error ? error.message : String(error)));
4106
+ },
4107
+ children: [
4108
+ choice.value === value ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CheckIcon, { width: 14, height: 14 }) : null,
4109
+ " ",
4110
+ choice.label
4111
+ ]
4112
+ },
4113
+ choice.value
4114
+ )),
4115
+ notice !== null ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "mx-meet__menu-note", role: "status", children: notice }) : null
4116
+ ] }) : null
4117
+ ] });
4118
+ }
4052
4119
  function Toast({ text, failed, onDone }) {
4053
4120
  (0, import_react10.useEffect)(() => {
4054
4121
  const timer = setTimeout(onDone, failed ? 7e3 : 4e3);
@@ -4362,6 +4429,17 @@ function ChatPanel({
4362
4429
  const snapshot = useMeetSnapshot();
4363
4430
  const room = useRoom();
4364
4431
  const live = useLivePolicy();
4432
+ const host = useMeetHost();
4433
+ const [rulesChecked, setRulesChecked] = (0, import_react10.useState)(false);
4434
+ (0, import_react10.useEffect)(() => {
4435
+ let alive = true;
4436
+ const done = () => alive && setRulesChecked(true);
4437
+ if (host === null) done();
4438
+ else void host.meetings.readLivePolicy().then(done, done);
4439
+ return () => {
4440
+ alive = false;
4441
+ };
4442
+ }, [host]);
4365
4443
  const [draft, setDraft] = (0, import_react10.useState)("");
4366
4444
  const [recipient, setRecipient] = (0, import_react10.useState)(to);
4367
4445
  const listRef = (0, import_react10.useRef)(null);
@@ -4386,7 +4464,7 @@ function ChatPanel({
4386
4464
  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,
4387
4465
  messages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ChatLine, { message, local, people }, message.id))
4388
4466
  ] }),
4389
- !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)(
4467
+ !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)(
4390
4468
  "form",
4391
4469
  {
4392
4470
  className: "mx-meet__composer mx-meet__composer--chat",
@@ -5137,7 +5215,11 @@ function WaitingList({ lobby }) {
5137
5215
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Avatar, { name: entry.displayName, url: entry.avatarUrl, size: 32 }),
5138
5216
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "mx-meet__person-name", children: [
5139
5217
  entry.displayName,
5140
- entry.userId === null ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "mx-meet__badge", children: "Guest" }) : null
5218
+ entry.userId === null ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "mx-meet__badge", children: "Guest" }) : null,
5219
+ entry.flag === "previously_removed" ? (
5220
+ // `mod-removed-bypass`: advice for the host, never a block — Admit stays.
5221
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "mx-meet__badge mx-meet__badge--warn", "data-meet-notice": "knock-flagged", children: "Previously removed" })
5222
+ ) : null
5141
5223
  ] }),
5142
5224
  /* @__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 }) }),
5143
5225
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
@@ -5762,7 +5844,20 @@ function ExitScreen({ onLeave }) {
5762
5844
  const { phase, reason, host, snapshot, actions, call, connection } = useMeetCore();
5763
5845
  const [busy, setBusy] = (0, import_react10.useState)(false);
5764
5846
  const failure = snapshot?.failure ?? null;
5765
- const retryLeft = useCountdown(phase === "refused:lobby_rejects" ? failure?.retryAt ?? null : null);
5847
+ const retryLeft = useCountdown(
5848
+ phase === "refused:lobby_rejects" || phase === "refused:too_early" ? failure?.retryAt ?? null : null
5849
+ );
5850
+ const reaskedAtZero = (0, import_react10.useRef)(false);
5851
+ (0, import_react10.useEffect)(() => {
5852
+ if (phase !== "refused:too_early") {
5853
+ reaskedAtZero.current = false;
5854
+ return;
5855
+ }
5856
+ if (retryLeft === 0 && !reaskedAtZero.current) {
5857
+ reaskedAtZero.current = true;
5858
+ actions.retry();
5859
+ }
5860
+ }, [phase, retryLeft, actions]);
5766
5861
  const rejoin = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
5767
5862
  import_design_system.Button,
5768
5863
  {
@@ -5800,11 +5895,19 @@ function ExitScreen({ onLeave }) {
5800
5895
  if (phase.startsWith("refused:")) {
5801
5896
  const code = phase.slice("refused:".length);
5802
5897
  title = REFUSED_TITLES[code] ?? "You can't join this meeting";
5803
- line = code === "too_early" || code === "registration_required" || code === "cancelled" ? failure?.remedy ?? null : null;
5898
+ line = code === "too_early" || code === "registration_required" || code === "cancelled" || code === "wrong_account" ? failure?.remedy ?? null : null;
5899
+ if (code === "too_early" && retryLeft !== null && retryLeft > 0) line = `Opens in ${clockText(retryLeft)}`;
5804
5900
  actionsNode = /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
5805
5901
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_design_system.Button, { type: "button", variant: "outline", onClick: actions.retry, children: "Try again" }),
5806
5902
  home
5807
5903
  ] });
5904
+ if (code === "sign_in_required" || code === "wrong_account") {
5905
+ const back = typeof window === "undefined" ? "/" : `${window.location.pathname}${window.location.search}`;
5906
+ actionsNode = /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
5907
+ /* @__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" }) }),
5908
+ home
5909
+ ] });
5910
+ }
5808
5911
  if (code === "lobby_rejects") {
5809
5912
  if (retryLeft !== null && retryLeft > 0) {
5810
5913
  line = `You can ask again in ${clockText(retryLeft)}`;