@ai-matrx/meet 0.11.44 → 0.11.45

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/core.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { P as ParticipantIdentity, e as ParticipantRole, k as AdmissionState, a1 as NoteTakerState, a as MeetParticipant, M as MeetingRecord, R as RoomName, d as CallInvite, a6 as ParticipantStanding, a3 as ObserversVisibleTo, S as StageLayout, L as LobbyEntry, aa as RecordingState, U as UserId, aF as RecordingSource, ae as TranscriptSegment, g as MeetingNote, W as WrapUpAnnouncement, j as CollabSnapshot, p as ConnectionQuality, h as MeetingId, b as MeetApi, f as CallMode, O as OrganizationId, m as CallId, n as CallState, Z as MeetingKind, a8 as RecordingPolicy, V as MeetingAttendee, a9 as RecordingRecord, a0 as MeetingSettings, X as MeetingChanges, a4 as OccurrenceOverride, $ as MeetingOccurrence, af as UpcomingOccurrence, Y as MeetingInvitee, I as InviteeInput, q as InviteeRole, ab as RsvpAnswer, ac as RsvpInvitation, c as MeetIdentity, C as CollabPanel } from './collab-BNJNgXFB.cjs';
2
- export { A as AccessTokenSource, l as CAPTION_LANGUAGES, o as CallablePerson, E as EMPTY_COLLAB, F as FILE_SESSION_PATH, J as JsonObject, r as JsonValue, s as MeetApiOptions, t as MeetApiRequest, u as MeetAppFocus, v as MeetBreakoutBroadcast, w as MeetBreakoutPlan, x as MeetBreakoutRoom, y as MeetCollabSettings, z as MeetFileMedia, B as MeetPoll, D as MeetPollOption, G as MeetPollState, H as MeetQuestion, K as MeetQuestionState, N as MeetStroke, Q as MeetingActivities, i as MeetingAppPanel, _ as MeetingNoteKind, a2 as ORGANIZATION_HEADER, a5 as OccurrenceState, a7 as ROLE_RANK, ad as RsvpState, T as TranscriptSource, ag as appPanelOf, ah as applyCollabPacket, ai as asCallId, aj as asMeetingId, ak as asOrganizationId, al as asParticipantIdentity, am as asRoomName, an as asUserId, ao as assignedRoom, ap as autoAssign, aq as breakoutClosingMs, ar as breakoutRemainingMs, as as captureHeldOf, at as collabFromState, au as createMeetApi, av as defaultCaptionLanguage, aw as fileDownloadPath, ax as focusFromRow, ay as isGuest, az as isHostRole, aA as isLinkFollower, aB as isObserverRole, aC as loadMeetingActivities, aD as scheduleSettingsPatch, aE as withItem } from './collab-BNJNgXFB.cjs';
1
+ import { P as ParticipantIdentity, e as ParticipantRole, k as AdmissionState, a1 as NoteTakerState, a as MeetParticipant, M as MeetingRecord, R as RoomName, d as CallInvite, a6 as ParticipantStanding, a3 as ObserversVisibleTo, S as StageLayout, L as LobbyEntry, aa as RecordingState, U as UserId, aF as RecordingSource, ae as TranscriptSegment, g as MeetingNote, W as WrapUpAnnouncement, j as CollabSnapshot, p as ConnectionQuality, h as MeetingId, b as MeetApi, f as CallMode, O as OrganizationId, m as CallId, n as CallState, Z as MeetingKind, a8 as RecordingPolicy, V as MeetingAttendee, a9 as RecordingRecord, a0 as MeetingSettings, X as MeetingChanges, a4 as OccurrenceOverride, $ as MeetingOccurrence, af as UpcomingOccurrence, Y as MeetingInvitee, I as InviteeInput, q as InviteeRole, ab as RsvpAnswer, ac as RsvpInvitation, c as MeetIdentity, C as CollabPanel } from './collab-D1TX5uf9.cjs';
2
+ export { A as AccessTokenSource, l as CAPTION_LANGUAGES, o as CallablePerson, E as EMPTY_COLLAB, F as FILE_SESSION_PATH, J as JsonObject, r as JsonValue, s as MeetApiOptions, t as MeetApiRequest, u as MeetAppFocus, v as MeetBreakoutBroadcast, w as MeetBreakoutPlan, x as MeetBreakoutRoom, y as MeetCollabSettings, z as MeetFileMedia, B as MeetPoll, D as MeetPollOption, G as MeetPollState, H as MeetQuestion, K as MeetQuestionState, N as MeetStroke, Q as MeetingActivities, i as MeetingAppPanel, _ as MeetingNoteKind, a2 as ORGANIZATION_HEADER, a5 as OccurrenceState, a7 as ROLE_RANK, ad as RsvpState, T as TranscriptSource, ag as appPanelOf, ah as applyCollabPacket, ai as asCallId, aj as asMeetingId, ak as asOrganizationId, al as asParticipantIdentity, am as asRoomName, an as asUserId, ao as assignedRoom, ap as autoAssign, aq as breakoutClosingMs, ar as breakoutRemainingMs, as as captureHeldOf, at as collabFromState, au as createMeetApi, av as defaultCaptionLanguage, aw as fileDownloadPath, ax as focusFromRow, ay as isGuest, az as isHostRole, aA as isLinkFollower, aB as isObserverRole, aC as loadMeetingActivities, aD as scheduleSettingsPatch, aE as withItem } from './collab-D1TX5uf9.cjs';
3
3
  import { RealtimeManager } from '@ai-matrx/realtime';
4
4
  import { MessagingSupabaseClient, ActionHandler } from '@ai-matrx/messaging';
5
5
  import { RoomOptions, ConnectionQuality as ConnectionQuality$1, Participant } from 'livekit-client';
@@ -1206,6 +1206,10 @@ declare const RPCS: {
1206
1206
  readonly admitAll: "meet_admit_all";
1207
1207
  readonly setLock: "meet_set_lock";
1208
1208
  readonly recordConsent: "meet_record_consent";
1209
+ /** One meeting rule as the gate obeys it (the knob ladder, this meeting's rung included). */
1210
+ readonly meetingRule: "meet_policy";
1211
+ /** A host sets one rule for THIS meeting (the `meet_meeting` rung); refusals come back as `{ok:false}`. */
1212
+ readonly setMeetingRule: "meet_policy_set";
1209
1213
  /** Schedule with every setting at create; unset settings come from the knobs. */
1210
1214
  readonly scheduleMeeting: "meet_schedule_meeting";
1211
1215
  /** Edit / reschedule — a JSON patch with a version compare-and-swap. */
@@ -1297,6 +1301,10 @@ interface MeetRepository {
1297
1301
  /** S4: admit everyone still waiting (not expired); returns the admitted rows. */
1298
1302
  admitAll(meetingId: MeetingId, byUserId: UserId): Promise<readonly MeetingAttendee[]>;
1299
1303
  setLock(meetingId: MeetingId, locked: boolean, byUserId: UserId): Promise<MeetingRecord>;
1304
+ /** `wr-policy-who-waits`: read one rule of this meeting (`lobby_bypass`, `access_type`…). */
1305
+ meetingRule(meetingId: MeetingId, key: string): Promise<unknown>;
1306
+ /** A host sets one rule for this meeting; a refusal throws with the server's sentence. */
1307
+ setMeetingRule(meetingId: MeetingId, key: string, value: string | number | boolean): Promise<unknown>;
1300
1308
  projectSegment(row: Row): TranscriptSegment;
1301
1309
  projectNote(row: Row): MeetingNote;
1302
1310
  /** One `meet_recordings` row, however it arrived (row or room packet). */
@@ -2308,6 +2316,10 @@ interface MeetingSession {
2308
2316
  readonly assignTo: string;
2309
2317
  }): Promise<void>;
2310
2318
  setLock(locked: boolean): Promise<MeetingRecord>;
2319
+ /** `wr-policy-who-waits`: one rule of the meeting on screen (null when unreadable). */
2320
+ meetingRule(key: string): Promise<unknown>;
2321
+ /** A host changes one rule for this meeting; waiting people are told to read again. */
2322
+ setMeetingRule(key: string, value: string | number | boolean): Promise<unknown>;
2311
2323
  /** End it for everyone, through the server (MRI-A6). */
2312
2324
  endForEveryone(): Promise<EndMeetingResult>;
2313
2325
  dispose(): void;
@@ -2498,10 +2510,18 @@ interface TabClaim {
2498
2510
  /** This page's claimed tab id (resolves once the claim settles). */
2499
2511
  readonly tabId: Promise<string>;
2500
2512
  /** Announce that this tab is in `room` as `person` (until `left`); answers presence questions. */
2501
- inRoom(room: string, person: string): void;
2513
+ inRoom(room: string, person: string, rule?: string | null): void;
2502
2514
  left(): void;
2503
2515
  /** Is `person` in `room` on ANOTHER tab of this browser? Answers within `TAB_ANSWER_MS`. */
2504
2516
  presentElsewhere(room: string, person: string): Promise<boolean>;
2517
+ /**
2518
+ * The same question, with the meeting's `second_device` rule the live tab holds — so the second
2519
+ * tab offers the PROFILE's choice (Meet: Switch here / Join here too; Teams: Transfer here / Add
2520
+ * this device) before anybody presses Join. Null when no other tab has the person live.
2521
+ */
2522
+ presenceElsewhere?(room: string, person: string): Promise<{
2523
+ readonly rule: string | null;
2524
+ } | null>;
2505
2525
  dispose(): void;
2506
2526
  }
2507
2527
 
@@ -2907,6 +2927,8 @@ interface RoomToken {
2907
2927
  * LiveKit token was issued (`token` is empty); the pre-join offers the choice.
2908
2928
  */
2909
2929
  readonly secondDeviceChoice?: string | null | undefined;
2930
+ /** S7: the meeting's `second_device` rule, always — told to a second tab of this browser. */
2931
+ readonly secondDeviceRule?: string | null | undefined;
2910
2932
  readonly displayName: string;
2911
2933
  /** ISO. The package refreshes at 80% of the remaining life. */
2912
2934
  readonly expiresAt: string;
package/dist/core.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { P as ParticipantIdentity, e as ParticipantRole, k as AdmissionState, a1 as NoteTakerState, a as MeetParticipant, M as MeetingRecord, R as RoomName, d as CallInvite, a6 as ParticipantStanding, a3 as ObserversVisibleTo, S as StageLayout, L as LobbyEntry, aa as RecordingState, U as UserId, aF as RecordingSource, ae as TranscriptSegment, g as MeetingNote, W as WrapUpAnnouncement, j as CollabSnapshot, p as ConnectionQuality, h as MeetingId, b as MeetApi, f as CallMode, O as OrganizationId, m as CallId, n as CallState, Z as MeetingKind, a8 as RecordingPolicy, V as MeetingAttendee, a9 as RecordingRecord, a0 as MeetingSettings, X as MeetingChanges, a4 as OccurrenceOverride, $ as MeetingOccurrence, af as UpcomingOccurrence, Y as MeetingInvitee, I as InviteeInput, q as InviteeRole, ab as RsvpAnswer, ac as RsvpInvitation, c as MeetIdentity, C as CollabPanel } from './collab-BNJNgXFB.js';
2
- export { A as AccessTokenSource, l as CAPTION_LANGUAGES, o as CallablePerson, E as EMPTY_COLLAB, F as FILE_SESSION_PATH, J as JsonObject, r as JsonValue, s as MeetApiOptions, t as MeetApiRequest, u as MeetAppFocus, v as MeetBreakoutBroadcast, w as MeetBreakoutPlan, x as MeetBreakoutRoom, y as MeetCollabSettings, z as MeetFileMedia, B as MeetPoll, D as MeetPollOption, G as MeetPollState, H as MeetQuestion, K as MeetQuestionState, N as MeetStroke, Q as MeetingActivities, i as MeetingAppPanel, _ as MeetingNoteKind, a2 as ORGANIZATION_HEADER, a5 as OccurrenceState, a7 as ROLE_RANK, ad as RsvpState, T as TranscriptSource, ag as appPanelOf, ah as applyCollabPacket, ai as asCallId, aj as asMeetingId, ak as asOrganizationId, al as asParticipantIdentity, am as asRoomName, an as asUserId, ao as assignedRoom, ap as autoAssign, aq as breakoutClosingMs, ar as breakoutRemainingMs, as as captureHeldOf, at as collabFromState, au as createMeetApi, av as defaultCaptionLanguage, aw as fileDownloadPath, ax as focusFromRow, ay as isGuest, az as isHostRole, aA as isLinkFollower, aB as isObserverRole, aC as loadMeetingActivities, aD as scheduleSettingsPatch, aE as withItem } from './collab-BNJNgXFB.js';
1
+ import { P as ParticipantIdentity, e as ParticipantRole, k as AdmissionState, a1 as NoteTakerState, a as MeetParticipant, M as MeetingRecord, R as RoomName, d as CallInvite, a6 as ParticipantStanding, a3 as ObserversVisibleTo, S as StageLayout, L as LobbyEntry, aa as RecordingState, U as UserId, aF as RecordingSource, ae as TranscriptSegment, g as MeetingNote, W as WrapUpAnnouncement, j as CollabSnapshot, p as ConnectionQuality, h as MeetingId, b as MeetApi, f as CallMode, O as OrganizationId, m as CallId, n as CallState, Z as MeetingKind, a8 as RecordingPolicy, V as MeetingAttendee, a9 as RecordingRecord, a0 as MeetingSettings, X as MeetingChanges, a4 as OccurrenceOverride, $ as MeetingOccurrence, af as UpcomingOccurrence, Y as MeetingInvitee, I as InviteeInput, q as InviteeRole, ab as RsvpAnswer, ac as RsvpInvitation, c as MeetIdentity, C as CollabPanel } from './collab-D1TX5uf9.js';
2
+ export { A as AccessTokenSource, l as CAPTION_LANGUAGES, o as CallablePerson, E as EMPTY_COLLAB, F as FILE_SESSION_PATH, J as JsonObject, r as JsonValue, s as MeetApiOptions, t as MeetApiRequest, u as MeetAppFocus, v as MeetBreakoutBroadcast, w as MeetBreakoutPlan, x as MeetBreakoutRoom, y as MeetCollabSettings, z as MeetFileMedia, B as MeetPoll, D as MeetPollOption, G as MeetPollState, H as MeetQuestion, K as MeetQuestionState, N as MeetStroke, Q as MeetingActivities, i as MeetingAppPanel, _ as MeetingNoteKind, a2 as ORGANIZATION_HEADER, a5 as OccurrenceState, a7 as ROLE_RANK, ad as RsvpState, T as TranscriptSource, ag as appPanelOf, ah as applyCollabPacket, ai as asCallId, aj as asMeetingId, ak as asOrganizationId, al as asParticipantIdentity, am as asRoomName, an as asUserId, ao as assignedRoom, ap as autoAssign, aq as breakoutClosingMs, ar as breakoutRemainingMs, as as captureHeldOf, at as collabFromState, au as createMeetApi, av as defaultCaptionLanguage, aw as fileDownloadPath, ax as focusFromRow, ay as isGuest, az as isHostRole, aA as isLinkFollower, aB as isObserverRole, aC as loadMeetingActivities, aD as scheduleSettingsPatch, aE as withItem } from './collab-D1TX5uf9.js';
3
3
  import { RealtimeManager } from '@ai-matrx/realtime';
4
4
  import { MessagingSupabaseClient, ActionHandler } from '@ai-matrx/messaging';
5
5
  import { RoomOptions, ConnectionQuality as ConnectionQuality$1, Participant } from 'livekit-client';
@@ -1206,6 +1206,10 @@ declare const RPCS: {
1206
1206
  readonly admitAll: "meet_admit_all";
1207
1207
  readonly setLock: "meet_set_lock";
1208
1208
  readonly recordConsent: "meet_record_consent";
1209
+ /** One meeting rule as the gate obeys it (the knob ladder, this meeting's rung included). */
1210
+ readonly meetingRule: "meet_policy";
1211
+ /** A host sets one rule for THIS meeting (the `meet_meeting` rung); refusals come back as `{ok:false}`. */
1212
+ readonly setMeetingRule: "meet_policy_set";
1209
1213
  /** Schedule with every setting at create; unset settings come from the knobs. */
1210
1214
  readonly scheduleMeeting: "meet_schedule_meeting";
1211
1215
  /** Edit / reschedule — a JSON patch with a version compare-and-swap. */
@@ -1297,6 +1301,10 @@ interface MeetRepository {
1297
1301
  /** S4: admit everyone still waiting (not expired); returns the admitted rows. */
1298
1302
  admitAll(meetingId: MeetingId, byUserId: UserId): Promise<readonly MeetingAttendee[]>;
1299
1303
  setLock(meetingId: MeetingId, locked: boolean, byUserId: UserId): Promise<MeetingRecord>;
1304
+ /** `wr-policy-who-waits`: read one rule of this meeting (`lobby_bypass`, `access_type`…). */
1305
+ meetingRule(meetingId: MeetingId, key: string): Promise<unknown>;
1306
+ /** A host sets one rule for this meeting; a refusal throws with the server's sentence. */
1307
+ setMeetingRule(meetingId: MeetingId, key: string, value: string | number | boolean): Promise<unknown>;
1300
1308
  projectSegment(row: Row): TranscriptSegment;
1301
1309
  projectNote(row: Row): MeetingNote;
1302
1310
  /** One `meet_recordings` row, however it arrived (row or room packet). */
@@ -2308,6 +2316,10 @@ interface MeetingSession {
2308
2316
  readonly assignTo: string;
2309
2317
  }): Promise<void>;
2310
2318
  setLock(locked: boolean): Promise<MeetingRecord>;
2319
+ /** `wr-policy-who-waits`: one rule of the meeting on screen (null when unreadable). */
2320
+ meetingRule(key: string): Promise<unknown>;
2321
+ /** A host changes one rule for this meeting; waiting people are told to read again. */
2322
+ setMeetingRule(key: string, value: string | number | boolean): Promise<unknown>;
2311
2323
  /** End it for everyone, through the server (MRI-A6). */
2312
2324
  endForEveryone(): Promise<EndMeetingResult>;
2313
2325
  dispose(): void;
@@ -2498,10 +2510,18 @@ interface TabClaim {
2498
2510
  /** This page's claimed tab id (resolves once the claim settles). */
2499
2511
  readonly tabId: Promise<string>;
2500
2512
  /** Announce that this tab is in `room` as `person` (until `left`); answers presence questions. */
2501
- inRoom(room: string, person: string): void;
2513
+ inRoom(room: string, person: string, rule?: string | null): void;
2502
2514
  left(): void;
2503
2515
  /** Is `person` in `room` on ANOTHER tab of this browser? Answers within `TAB_ANSWER_MS`. */
2504
2516
  presentElsewhere(room: string, person: string): Promise<boolean>;
2517
+ /**
2518
+ * The same question, with the meeting's `second_device` rule the live tab holds — so the second
2519
+ * tab offers the PROFILE's choice (Meet: Switch here / Join here too; Teams: Transfer here / Add
2520
+ * this device) before anybody presses Join. Null when no other tab has the person live.
2521
+ */
2522
+ presenceElsewhere?(room: string, person: string): Promise<{
2523
+ readonly rule: string | null;
2524
+ } | null>;
2505
2525
  dispose(): void;
2506
2526
  }
2507
2527
 
@@ -2907,6 +2927,8 @@ interface RoomToken {
2907
2927
  * LiveKit token was issued (`token` is empty); the pre-join offers the choice.
2908
2928
  */
2909
2929
  readonly secondDeviceChoice?: string | null | undefined;
2930
+ /** S7: the meeting's `second_device` rule, always — told to a second tab of this browser. */
2931
+ readonly secondDeviceRule?: string | null | undefined;
2910
2932
  readonly displayName: string;
2911
2933
  /** ISO. The package refreshes at 80% of the remaining life. */
2912
2934
  readonly expiresAt: string;
package/dist/core.js CHANGED
@@ -323,7 +323,8 @@ function createWebResumeStorage() {
323
323
  }
324
324
  };
325
325
  }
326
- var CLEARS_RESUME = (phase) => phase === "left" || phase === "removed" || phase === "superseded" || phase === "inactive" || phase === "ended" || phase === "record" || phase === "prejoin" || phase.startsWith("refused:") || phase.startsWith("waiting:");
326
+ 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:");
327
+ var WAITING_RESUMES = (phase) => phase.startsWith("waiting:");
327
328
  function autoRejoinDelayMs(attempt, random = Math.random) {
328
329
  const base = Math.min(15e3, 1e3 * 2 ** Math.max(0, attempt));
329
330
  return Math.round(base * (0.5 + random()));
@@ -455,6 +456,10 @@ function createConnectionSupervisor(options) {
455
456
  rejoinAttempts = 0;
456
457
  stopRejoin();
457
458
  saveMarker(true);
459
+ } else if (WAITING_RESUMES(phase)) {
460
+ stopRejoin();
461
+ driverRegion = null;
462
+ saveMarker(true);
458
463
  } else if (phase === "disconnected") {
459
464
  driverRegion = null;
460
465
  saveMarker(true);
@@ -758,6 +763,7 @@ function parseRoomToken(body, roomName) {
758
763
  connectionIdentity: readString(payload, "connection_identity") ?? personKey,
759
764
  connectionKind: readString(payload, "connection_kind") === "companion" ? "companion" : "main",
760
765
  secondDeviceChoice,
766
+ secondDeviceRule: readString(payload, "second_device_rule"),
761
767
  displayName: readString(payload, "display_name") ?? readString(payload, "displayName") ?? identity,
762
768
  expiresAt,
763
769
  canPublish: readBoolean(payload, "can_publish", true),
@@ -1496,6 +1502,12 @@ var JOINABLE_FROM = /* @__PURE__ */ new Set([
1496
1502
  // allows the press; the server's gate decides whether it knocks or is refused again.
1497
1503
  "refused:lobby_rejects"
1498
1504
  ]);
1505
+ var RETRYABLE_REFUSALS = /* @__PURE__ */ new Set([
1506
+ "refused:locked",
1507
+ "refused:too_early",
1508
+ "refused:meeting_full",
1509
+ "refused:platform_capacity"
1510
+ ]);
1499
1511
  var NOT_ENDABLE = /* @__PURE__ */ new Set([
1500
1512
  "ended",
1501
1513
  "record",
@@ -1561,7 +1573,7 @@ function nextPhase(from, event) {
1561
1573
  case "link_state":
1562
1574
  return from === "resolving" && event.reason !== null && isJoinRefusalReason(event.reason) ? refusalPhase(event.reason) : null;
1563
1575
  case "join":
1564
- return JOINABLE_FROM.has(from) ? "joining" : null;
1576
+ return JOINABLE_FROM.has(from) || RETRYABLE_REFUSALS.has(from) ? "joining" : null;
1565
1577
  case "auto_rejoin":
1566
1578
  return from === "disconnected" ? "joining" : null;
1567
1579
  case "resumed":
@@ -1986,6 +1998,10 @@ var RPCS = {
1986
1998
  admitAll: "meet_admit_all",
1987
1999
  setLock: "meet_set_lock",
1988
2000
  recordConsent: "meet_record_consent",
2001
+ /** One meeting rule as the gate obeys it (the knob ladder, this meeting's rung included). */
2002
+ meetingRule: "meet_policy",
2003
+ /** A host sets one rule for THIS meeting (the `meet_meeting` rung); refusals come back as `{ok:false}`. */
2004
+ setMeetingRule: "meet_policy_set",
1989
2005
  /*
1990
2006
  * ── MEET WAVE 1 — management (0.8). Every write is a SECURITY DEFINER door
1991
2007
  * authorized by the meeting's host or a real iam grant (editor to change,
@@ -2032,7 +2048,7 @@ var INVITEE_COLUMNS = "id,meeting_id,invitee_user_id,email,display_name,role,rsv
2032
2048
  var RECORDING_COLUMNS = "id,meeting_id,state,started_at,ended_at,started_by,file_id,failure_reason";
2033
2049
  var SEGMENT_COLUMNS = "id,meeting_id,identity,speaker,text,started_at,is_final,metadata";
2034
2050
  var NOTE_COLUMNS = "id,meeting_id,text,kind,assignee_user_id,created_at,metadata";
2035
- 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";
2051
+ 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";
2036
2052
  function projectStays(value) {
2037
2053
  if (!Array.isArray(value)) return [];
2038
2054
  const stays = [];
@@ -2432,6 +2448,8 @@ function createMeetRepository(options) {
2432
2448
  admissionState: states.includes(admission) ? admission : "admitted",
2433
2449
  knockedAt: text(row, "knocked_at"),
2434
2450
  knockExpiresAt: text(row, "knock_expires_at"),
2451
+ // The list read selects `metadata->>knock_flag`; a Postgres Changes row carries `metadata`.
2452
+ knockFlag: text(row, "knock_flag") ?? (typeof row["metadata"] === "object" && row["metadata"] !== null ? text(row["metadata"], "knock_flag") : null),
2435
2453
  isAgent: bool(row, "is_agent", false),
2436
2454
  joinedAt: text(row, "joined_at"),
2437
2455
  leftAt: text(row, "left_at"),
@@ -2755,6 +2773,25 @@ function createMeetRepository(options) {
2755
2773
  );
2756
2774
  return projectMeeting(row);
2757
2775
  },
2776
+ async meetingRule(meetingId, key) {
2777
+ const response = await db.rpc(RPCS.meetingRule, { p_meeting_id: meetingId, p_key: key });
2778
+ if (response.error !== null) throw normalizeMeetError(response.error, "meet.meetingRule");
2779
+ return response.data;
2780
+ },
2781
+ async setMeetingRule(meetingId, key, value) {
2782
+ const response = await db.rpc(RPCS.setMeetingRule, { p_meeting_id: meetingId, p_key: key, p_value: value });
2783
+ if (response.error !== null) throw normalizeMeetError(response.error, "meet.setMeetingRule");
2784
+ const answer = response.data ?? {};
2785
+ if (answer["ok"] !== true) {
2786
+ throw new MeetError(
2787
+ "forbidden",
2788
+ typeof answer["detail"] === "string" ? answer["detail"] : "This meeting's rule was not changed.",
2789
+ "Only the meeting's host or a co-host sets its rules; an organization can lock a rule for everyone.",
2790
+ answer
2791
+ );
2792
+ }
2793
+ return answer["effective_value"];
2794
+ },
2758
2795
  async transcript(meetingId, limit = 1e3) {
2759
2796
  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);
2760
2797
  if (response.error !== null) throw normalizeMeetError(response.error, "meet.transcript");
@@ -4920,7 +4957,8 @@ function lobbyEntryFrom(attendee, now = Date.now()) {
4920
4957
  knockedAt: attendee.knockedAt ?? attendee.joinedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
4921
4958
  knockExpiresAt: attendee.knockExpiresAt ?? null,
4922
4959
  decidedAt: null,
4923
- decidedBy: null
4960
+ decidedBy: null,
4961
+ flag: attendee.knockFlag ?? null
4924
4962
  };
4925
4963
  }
4926
4964
  function deniedEntryFrom(attendee) {
@@ -5452,6 +5490,17 @@ function createMeetingSession(options) {
5452
5490
  async askAgain() {
5453
5491
  await options.rejoin?.();
5454
5492
  },
5493
+ async meetingRule(key) {
5494
+ const meeting = requireMeeting("meet.meetingRule");
5495
+ return repository.meetingRule(meeting.id, key);
5496
+ },
5497
+ async setMeetingRule(key, value) {
5498
+ requireHostUser("meet.setMeetingRule");
5499
+ const meeting = requireMeeting("meet.setMeetingRule");
5500
+ const effective = await repository.setMeetingRule(meeting.id, key, value);
5501
+ hint();
5502
+ return effective;
5503
+ },
5455
5504
  async setLock(locked) {
5456
5505
  const by = requireHostUser("meet.setLock");
5457
5506
  const meeting = requireMeeting("meet.setLock");
@@ -6843,7 +6892,7 @@ function createRoomEngine(options) {
6843
6892
  const { secondDevice: _answered, ...rest } = lastRequest;
6844
6893
  lastRequest = rest;
6845
6894
  }
6846
- options.tabClaim?.inRoom(String(request.roomName), String(token.identity));
6895
+ options.tabClaim?.inRoom(String(request.roomName), String(token.identity), token.secondDeviceRule ?? null);
6847
6896
  current = token;
6848
6897
  lastServerUrl = token.serverUrl;
6849
6898
  connectionPolicy = token.connectionPolicy ?? DEFAULT_CONNECTION_POLICY;
@@ -7155,10 +7204,12 @@ function createRoomEngine(options) {
7155
7204
  if (claim === void 0 || userId === null) return false;
7156
7205
  const before = store.snapshot();
7157
7206
  if (before.phase !== "prejoin" || before.secondDevice !== null) return false;
7158
- if (!await claim.presentElsewhere(String(roomName), `user:${String(userId)}`)) return false;
7207
+ const person = `user:${String(userId)}`;
7208
+ const found = claim.presenceElsewhere !== void 0 ? await claim.presenceElsewhere(String(roomName), person) : await claim.presentElsewhere(String(roomName), person) ? { rule: null } : null;
7209
+ if (found === null || found.rule === "allow_both") return false;
7159
7210
  const now2 = store.snapshot();
7160
7211
  if (now2.phase !== "prejoin" || now2.secondDevice !== null) return false;
7161
- store.patch({ secondDevice: { rule: "ask_switch_or_join" } });
7212
+ store.patch({ secondDevice: { rule: found.rule ?? "ask_switch_or_join" } });
7162
7213
  report("info", "meet: this browser already has you in this meeting \u2014 offering the choice before Join");
7163
7214
  return true;
7164
7215
  },