@ai-matrx/meet 0.11.42 → 0.11.43
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 +6 -0
- package/dist/{collab-Bf5evcLO.d.cts → collab-BNJNgXFB.d.cts} +10 -1
- package/dist/{collab-Bf5evcLO.d.ts → collab-BNJNgXFB.d.ts} +10 -1
- package/dist/{components-Frpvls4N.d.ts → components-BDtGzHf4.d.ts} +4 -2
- package/dist/{components-roCEgyFH.d.cts → components-BxK993iW.d.cts} +4 -2
- package/dist/core.cjs +182 -14
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +56 -6
- package/dist/core.d.ts +56 -6
- package/dist/core.js +182 -14
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +182 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +182 -14
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +456 -54
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +21 -4
- package/dist/react.d.ts +21 -4
- package/dist/react.js +456 -54
- package/dist/react.js.map +1 -1
- package/dist/skins/meet.cjs +321 -47
- package/dist/skins/meet.cjs.map +1 -1
- package/dist/skins/meet.d.cts +5 -3
- package/dist/skins/meet.d.ts +5 -3
- package/dist/skins/meet.js +321 -47
- package/dist/skins/meet.js.map +1 -1
- package/package.json +3 -3
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,
|
|
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
|
|
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';
|
|
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';
|
|
@@ -158,6 +158,29 @@ declare function leaveChoices(args: {
|
|
|
158
158
|
readonly othersPresent: boolean;
|
|
159
159
|
}): readonly LeaveChoice[];
|
|
160
160
|
|
|
161
|
+
/**
|
|
162
|
+
* THE RULES A SCREEN REDRAWS FROM WHILE THE CALL IS RUNNING (P1: chat-restrictions,
|
|
163
|
+
* mod-attendee-av-disabled, time-limit-warning, rec-consent-gate).
|
|
164
|
+
*
|
|
165
|
+
* The host can change a meeting's rules mid-call through the meeting's own rules door, which
|
|
166
|
+
* never talks to the room. The server's state read answers the live values (`live_policy`), and
|
|
167
|
+
* the room reads it on a short timer while the person is in the call — so a rule changed at
|
|
168
|
+
* second 40 is on every screen within one poll. Pure functions here; the timer lives in
|
|
169
|
+
* `meetings.ts`.
|
|
170
|
+
*/
|
|
171
|
+
|
|
172
|
+
type ChatMode = "everyone" | "hosts_only" | "off";
|
|
173
|
+
type AvLock = "off" | "mic" | "camera" | "both";
|
|
174
|
+
type RecordingConsent = "notice" | "ok_or_leave" | "view_only_until_agree";
|
|
175
|
+
interface LivePolicy {
|
|
176
|
+
readonly chatMode: ChatMode;
|
|
177
|
+
readonly avLock: AvLock;
|
|
178
|
+
/** 0 = no limit. */
|
|
179
|
+
readonly timeLimitMinutes: number;
|
|
180
|
+
readonly timeLimitWarningMinutes: number;
|
|
181
|
+
readonly recordingConsent: RecordingConsent;
|
|
182
|
+
}
|
|
183
|
+
|
|
161
184
|
/**
|
|
162
185
|
* LISTEN-ONLY ATTENDANCE — being IN the room is never contingent on a device.
|
|
163
186
|
*
|
|
@@ -593,7 +616,7 @@ interface RoomChatMessage {
|
|
|
593
616
|
* packet, addressed to this participant. Rendered once, dismissible, in words.
|
|
594
617
|
*/
|
|
595
618
|
interface ModerationNotice {
|
|
596
|
-
readonly kind: "muted" | "ask-unmute" | "removed" | "role";
|
|
619
|
+
readonly kind: "muted" | "ask-unmute" | "removed" | "role" | "locked" | "capability";
|
|
597
620
|
readonly text: string;
|
|
598
621
|
readonly at: number;
|
|
599
622
|
}
|
|
@@ -760,6 +783,16 @@ interface MeetSnapshot {
|
|
|
760
783
|
readonly waitingCount: number;
|
|
761
784
|
/** S8: what the host's Leave offers in this meeting (from the token's `leave_policy`). */
|
|
762
785
|
readonly leavePolicy: LeavePolicy;
|
|
786
|
+
/** The meeting's rules as they are RIGHT NOW (chat mode, attendee lock, time limit), read live while in the call. */
|
|
787
|
+
readonly livePolicy: LivePolicy;
|
|
788
|
+
/** host-reclaim: this person is the meeting's owner and may take the host powers back (Zoom rules). */
|
|
789
|
+
readonly canReclaim: boolean;
|
|
790
|
+
/** rec-consent-gate: this person agreed to the recording that is running. */
|
|
791
|
+
readonly recordingConsentGiven: boolean;
|
|
792
|
+
/** When this run started (epoch ms), so every screen counts a time limit down together. */
|
|
793
|
+
readonly sessionStartedAt: number | null;
|
|
794
|
+
/** True from the moment a host muted THIS microphone until the person turns it on themselves. */
|
|
795
|
+
readonly mutedByHost: boolean;
|
|
763
796
|
/** The last thing a host did to THIS participant, in words. Dismissible. */
|
|
764
797
|
readonly moderationNotice: ModerationNotice | null;
|
|
765
798
|
/**
|
|
@@ -821,6 +854,10 @@ interface MeetSnapshot {
|
|
|
821
854
|
* last device change and an offered new device. Written only by `media-supervisor.ts`.
|
|
822
855
|
*/
|
|
823
856
|
readonly mediaRegion: MediaRegionState;
|
|
857
|
+
/** `captions-toggle`: THIS viewer wants captions. Local to the viewer; never sent to the room. */
|
|
858
|
+
readonly captionsOn: boolean;
|
|
859
|
+
/** Why asking for captions failed, in the server's words (`captions-unavailable`). */
|
|
860
|
+
readonly captionsRefusal: NoteTakerRefusal | null;
|
|
824
861
|
/** The last failure, with its remedy. Never null-and-hoping. */
|
|
825
862
|
readonly failure: {
|
|
826
863
|
readonly message: string;
|
|
@@ -976,6 +1013,14 @@ interface MeetAi {
|
|
|
976
1013
|
meetingId: MeetingId;
|
|
977
1014
|
roomName: RoomName;
|
|
978
1015
|
}): Promise<NoteTakerDispatch>;
|
|
1016
|
+
/**
|
|
1017
|
+
* Make sure the note-taker (the speech-to-text) is in the room because THIS viewer turned
|
|
1018
|
+
* captions on. Open to everyone in the meeting, guests included. Never throws: a refusal
|
|
1019
|
+
* is stored as `captionsRefusal` in the server's own words.
|
|
1020
|
+
*/
|
|
1021
|
+
requestCaptions(args: {
|
|
1022
|
+
meetingId: MeetingId;
|
|
1023
|
+
}): Promise<void>;
|
|
979
1024
|
/** Dismiss it. Nothing automatic brings a dismissed note-taker back (D9). */
|
|
980
1025
|
stopNoteTaker(args: {
|
|
981
1026
|
meetingId: MeetingId;
|
|
@@ -2876,6 +2921,8 @@ interface RoomToken {
|
|
|
2876
2921
|
readonly connectionPolicy?: ConnectionPolicy | undefined;
|
|
2877
2922
|
/** S8: what the host's Leave offers for this meeting (`host_leave_prompt`, `end_confirm`). */
|
|
2878
2923
|
readonly leavePolicy?: LeavePolicy | undefined;
|
|
2924
|
+
/** host-reclaim (`host_reclaim = prompt`): the returning owner may take the host powers back. */
|
|
2925
|
+
readonly canReclaim?: boolean | undefined;
|
|
2879
2926
|
}
|
|
2880
2927
|
|
|
2881
2928
|
interface TokenClientOptions {
|
|
@@ -2976,6 +3023,8 @@ declare const MEET_ROUTES: {
|
|
|
2976
3023
|
readonly noteTakerStop: "/api/v1/meet/agent/stop";
|
|
2977
3024
|
/** In-meeting Q&A. Streams NDJSON; body is `{meeting_id, question}` only. */
|
|
2978
3025
|
readonly intelligenceAsk: "/api/v1/meet/intelligence/ask";
|
|
3026
|
+
/** Anyone in the meeting (guests with the room pass included) asks for captions for themselves. */
|
|
3027
|
+
readonly noteTakerCaptions: "/api/v1/meet/agent/captions";
|
|
2979
3028
|
/** A host ends the meeting for everyone. Served by MRI-A6. */
|
|
2980
3029
|
readonly meetingEnd: "/api/v1/meet/end";
|
|
2981
3030
|
/**
|
|
@@ -3589,12 +3638,13 @@ declare function createWebEffectsStorage(key?: string): EffectsStorage;
|
|
|
3589
3638
|
*/
|
|
3590
3639
|
|
|
3591
3640
|
/** Every host power the server performs. One vocabulary with `services/meet/moderation.py`. */
|
|
3592
|
-
type ModerationAction = "mute" | "stop_video" | "stop_screen" | "mute_all" | "ask_unmute" | "ask_all_unmute" | "spotlight" | "sync_presenter" | "raise_hand" | "lower_hand" | "lower_all_hands" | "make_cohost" | "revoke_cohost" | "transfer_host" | "rename" | "remove" | "report";
|
|
3641
|
+
type ModerationAction = "mute" | "stop_video" | "stop_screen" | "mute_all" | "ask_unmute" | "ask_all_unmute" | "spotlight" | "sync_presenter" | "raise_hand" | "lower_hand" | "lower_all_hands" | "make_cohost" | "make_presenter" | "revoke_presenter" | "set_av_lock" | "revoke_cohost" | "transfer_host" | "reclaim_host" | "rename" | "remove" | "report";
|
|
3593
3642
|
declare const MODERATION_ACTIONS: readonly ModerationAction[];
|
|
3594
3643
|
interface ModerationRequest {
|
|
3595
3644
|
readonly action: ModerationAction;
|
|
3596
3645
|
readonly identity?: ParticipantIdentity | null | undefined;
|
|
3597
3646
|
readonly name?: string | undefined;
|
|
3647
|
+
readonly value?: string | undefined;
|
|
3598
3648
|
readonly reason?: string | undefined;
|
|
3599
3649
|
}
|
|
3600
3650
|
interface ModerationResult {
|
|
@@ -3869,7 +3919,7 @@ declare function takeMoveRequest(store: CollabStoreLike): {
|
|
|
3869
3919
|
* a link follower whose role is `participant` renders `guest` (CORE-DESIGN §2.1); `presenter` /
|
|
3870
3920
|
* `viewer` render `participant` until the contract adopts them (§3.7 C3).
|
|
3871
3921
|
*/
|
|
3872
|
-
type ObservedRole = "host" | "cohost" | "participant" | "guest";
|
|
3922
|
+
type ObservedRole = "host" | "cohost" | "presenter" | "participant" | "guest";
|
|
3873
3923
|
/** `data-meet-phase` — the core's phase IS the contract value (S6a: no translation layer). */
|
|
3874
3924
|
type ObservedPhase = MeetPhase;
|
|
3875
3925
|
type ObservedConnection = ConnectionRegion;
|
|
@@ -3898,7 +3948,7 @@ interface ObserveInput {
|
|
|
3898
3948
|
* The reason comes from the media supervisor (S6c), which reads the browser's permission store: a
|
|
3899
3949
|
* kind the browser denies is `unavailable:denied` even when nothing tried to open it.
|
|
3900
3950
|
*/
|
|
3901
|
-
declare function observedDevice(track: LocalTrackState, permission?: PermissionReading): string;
|
|
3951
|
+
declare function observedDevice(track: LocalTrackState, permission?: PermissionReading, held?: "locked" | "muted_by_host" | null): string;
|
|
3902
3952
|
/**
|
|
3903
3953
|
* The phase the person is looking at: the store's phase, verbatim (CORE-DESIGN §3.1). The one
|
|
3904
3954
|
* derivation left is the ended meeting: a row that says it is over renders `ended` for anyone not
|
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,
|
|
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
|
|
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';
|
|
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';
|
|
@@ -158,6 +158,29 @@ declare function leaveChoices(args: {
|
|
|
158
158
|
readonly othersPresent: boolean;
|
|
159
159
|
}): readonly LeaveChoice[];
|
|
160
160
|
|
|
161
|
+
/**
|
|
162
|
+
* THE RULES A SCREEN REDRAWS FROM WHILE THE CALL IS RUNNING (P1: chat-restrictions,
|
|
163
|
+
* mod-attendee-av-disabled, time-limit-warning, rec-consent-gate).
|
|
164
|
+
*
|
|
165
|
+
* The host can change a meeting's rules mid-call through the meeting's own rules door, which
|
|
166
|
+
* never talks to the room. The server's state read answers the live values (`live_policy`), and
|
|
167
|
+
* the room reads it on a short timer while the person is in the call — so a rule changed at
|
|
168
|
+
* second 40 is on every screen within one poll. Pure functions here; the timer lives in
|
|
169
|
+
* `meetings.ts`.
|
|
170
|
+
*/
|
|
171
|
+
|
|
172
|
+
type ChatMode = "everyone" | "hosts_only" | "off";
|
|
173
|
+
type AvLock = "off" | "mic" | "camera" | "both";
|
|
174
|
+
type RecordingConsent = "notice" | "ok_or_leave" | "view_only_until_agree";
|
|
175
|
+
interface LivePolicy {
|
|
176
|
+
readonly chatMode: ChatMode;
|
|
177
|
+
readonly avLock: AvLock;
|
|
178
|
+
/** 0 = no limit. */
|
|
179
|
+
readonly timeLimitMinutes: number;
|
|
180
|
+
readonly timeLimitWarningMinutes: number;
|
|
181
|
+
readonly recordingConsent: RecordingConsent;
|
|
182
|
+
}
|
|
183
|
+
|
|
161
184
|
/**
|
|
162
185
|
* LISTEN-ONLY ATTENDANCE — being IN the room is never contingent on a device.
|
|
163
186
|
*
|
|
@@ -593,7 +616,7 @@ interface RoomChatMessage {
|
|
|
593
616
|
* packet, addressed to this participant. Rendered once, dismissible, in words.
|
|
594
617
|
*/
|
|
595
618
|
interface ModerationNotice {
|
|
596
|
-
readonly kind: "muted" | "ask-unmute" | "removed" | "role";
|
|
619
|
+
readonly kind: "muted" | "ask-unmute" | "removed" | "role" | "locked" | "capability";
|
|
597
620
|
readonly text: string;
|
|
598
621
|
readonly at: number;
|
|
599
622
|
}
|
|
@@ -760,6 +783,16 @@ interface MeetSnapshot {
|
|
|
760
783
|
readonly waitingCount: number;
|
|
761
784
|
/** S8: what the host's Leave offers in this meeting (from the token's `leave_policy`). */
|
|
762
785
|
readonly leavePolicy: LeavePolicy;
|
|
786
|
+
/** The meeting's rules as they are RIGHT NOW (chat mode, attendee lock, time limit), read live while in the call. */
|
|
787
|
+
readonly livePolicy: LivePolicy;
|
|
788
|
+
/** host-reclaim: this person is the meeting's owner and may take the host powers back (Zoom rules). */
|
|
789
|
+
readonly canReclaim: boolean;
|
|
790
|
+
/** rec-consent-gate: this person agreed to the recording that is running. */
|
|
791
|
+
readonly recordingConsentGiven: boolean;
|
|
792
|
+
/** When this run started (epoch ms), so every screen counts a time limit down together. */
|
|
793
|
+
readonly sessionStartedAt: number | null;
|
|
794
|
+
/** True from the moment a host muted THIS microphone until the person turns it on themselves. */
|
|
795
|
+
readonly mutedByHost: boolean;
|
|
763
796
|
/** The last thing a host did to THIS participant, in words. Dismissible. */
|
|
764
797
|
readonly moderationNotice: ModerationNotice | null;
|
|
765
798
|
/**
|
|
@@ -821,6 +854,10 @@ interface MeetSnapshot {
|
|
|
821
854
|
* last device change and an offered new device. Written only by `media-supervisor.ts`.
|
|
822
855
|
*/
|
|
823
856
|
readonly mediaRegion: MediaRegionState;
|
|
857
|
+
/** `captions-toggle`: THIS viewer wants captions. Local to the viewer; never sent to the room. */
|
|
858
|
+
readonly captionsOn: boolean;
|
|
859
|
+
/** Why asking for captions failed, in the server's words (`captions-unavailable`). */
|
|
860
|
+
readonly captionsRefusal: NoteTakerRefusal | null;
|
|
824
861
|
/** The last failure, with its remedy. Never null-and-hoping. */
|
|
825
862
|
readonly failure: {
|
|
826
863
|
readonly message: string;
|
|
@@ -976,6 +1013,14 @@ interface MeetAi {
|
|
|
976
1013
|
meetingId: MeetingId;
|
|
977
1014
|
roomName: RoomName;
|
|
978
1015
|
}): Promise<NoteTakerDispatch>;
|
|
1016
|
+
/**
|
|
1017
|
+
* Make sure the note-taker (the speech-to-text) is in the room because THIS viewer turned
|
|
1018
|
+
* captions on. Open to everyone in the meeting, guests included. Never throws: a refusal
|
|
1019
|
+
* is stored as `captionsRefusal` in the server's own words.
|
|
1020
|
+
*/
|
|
1021
|
+
requestCaptions(args: {
|
|
1022
|
+
meetingId: MeetingId;
|
|
1023
|
+
}): Promise<void>;
|
|
979
1024
|
/** Dismiss it. Nothing automatic brings a dismissed note-taker back (D9). */
|
|
980
1025
|
stopNoteTaker(args: {
|
|
981
1026
|
meetingId: MeetingId;
|
|
@@ -2876,6 +2921,8 @@ interface RoomToken {
|
|
|
2876
2921
|
readonly connectionPolicy?: ConnectionPolicy | undefined;
|
|
2877
2922
|
/** S8: what the host's Leave offers for this meeting (`host_leave_prompt`, `end_confirm`). */
|
|
2878
2923
|
readonly leavePolicy?: LeavePolicy | undefined;
|
|
2924
|
+
/** host-reclaim (`host_reclaim = prompt`): the returning owner may take the host powers back. */
|
|
2925
|
+
readonly canReclaim?: boolean | undefined;
|
|
2879
2926
|
}
|
|
2880
2927
|
|
|
2881
2928
|
interface TokenClientOptions {
|
|
@@ -2976,6 +3023,8 @@ declare const MEET_ROUTES: {
|
|
|
2976
3023
|
readonly noteTakerStop: "/api/v1/meet/agent/stop";
|
|
2977
3024
|
/** In-meeting Q&A. Streams NDJSON; body is `{meeting_id, question}` only. */
|
|
2978
3025
|
readonly intelligenceAsk: "/api/v1/meet/intelligence/ask";
|
|
3026
|
+
/** Anyone in the meeting (guests with the room pass included) asks for captions for themselves. */
|
|
3027
|
+
readonly noteTakerCaptions: "/api/v1/meet/agent/captions";
|
|
2979
3028
|
/** A host ends the meeting for everyone. Served by MRI-A6. */
|
|
2980
3029
|
readonly meetingEnd: "/api/v1/meet/end";
|
|
2981
3030
|
/**
|
|
@@ -3589,12 +3638,13 @@ declare function createWebEffectsStorage(key?: string): EffectsStorage;
|
|
|
3589
3638
|
*/
|
|
3590
3639
|
|
|
3591
3640
|
/** Every host power the server performs. One vocabulary with `services/meet/moderation.py`. */
|
|
3592
|
-
type ModerationAction = "mute" | "stop_video" | "stop_screen" | "mute_all" | "ask_unmute" | "ask_all_unmute" | "spotlight" | "sync_presenter" | "raise_hand" | "lower_hand" | "lower_all_hands" | "make_cohost" | "revoke_cohost" | "transfer_host" | "rename" | "remove" | "report";
|
|
3641
|
+
type ModerationAction = "mute" | "stop_video" | "stop_screen" | "mute_all" | "ask_unmute" | "ask_all_unmute" | "spotlight" | "sync_presenter" | "raise_hand" | "lower_hand" | "lower_all_hands" | "make_cohost" | "make_presenter" | "revoke_presenter" | "set_av_lock" | "revoke_cohost" | "transfer_host" | "reclaim_host" | "rename" | "remove" | "report";
|
|
3593
3642
|
declare const MODERATION_ACTIONS: readonly ModerationAction[];
|
|
3594
3643
|
interface ModerationRequest {
|
|
3595
3644
|
readonly action: ModerationAction;
|
|
3596
3645
|
readonly identity?: ParticipantIdentity | null | undefined;
|
|
3597
3646
|
readonly name?: string | undefined;
|
|
3647
|
+
readonly value?: string | undefined;
|
|
3598
3648
|
readonly reason?: string | undefined;
|
|
3599
3649
|
}
|
|
3600
3650
|
interface ModerationResult {
|
|
@@ -3869,7 +3919,7 @@ declare function takeMoveRequest(store: CollabStoreLike): {
|
|
|
3869
3919
|
* a link follower whose role is `participant` renders `guest` (CORE-DESIGN §2.1); `presenter` /
|
|
3870
3920
|
* `viewer` render `participant` until the contract adopts them (§3.7 C3).
|
|
3871
3921
|
*/
|
|
3872
|
-
type ObservedRole = "host" | "cohost" | "participant" | "guest";
|
|
3922
|
+
type ObservedRole = "host" | "cohost" | "presenter" | "participant" | "guest";
|
|
3873
3923
|
/** `data-meet-phase` — the core's phase IS the contract value (S6a: no translation layer). */
|
|
3874
3924
|
type ObservedPhase = MeetPhase;
|
|
3875
3925
|
type ObservedConnection = ConnectionRegion;
|
|
@@ -3898,7 +3948,7 @@ interface ObserveInput {
|
|
|
3898
3948
|
* The reason comes from the media supervisor (S6c), which reads the browser's permission store: a
|
|
3899
3949
|
* kind the browser denies is `unavailable:denied` even when nothing tried to open it.
|
|
3900
3950
|
*/
|
|
3901
|
-
declare function observedDevice(track: LocalTrackState, permission?: PermissionReading): string;
|
|
3951
|
+
declare function observedDevice(track: LocalTrackState, permission?: PermissionReading, held?: "locked" | "muted_by_host" | null): string;
|
|
3902
3952
|
/**
|
|
3903
3953
|
* The phase the person is looking at: the store's phase, verbatim (CORE-DESIGN §3.1). The one
|
|
3904
3954
|
* derivation left is the ended meeting: a row that says it is over renders `ended` for anyone not
|
package/dist/core.js
CHANGED
|
@@ -632,6 +632,8 @@ var MEET_ROUTES = {
|
|
|
632
632
|
noteTakerStop: "/api/v1/meet/agent/stop",
|
|
633
633
|
/** In-meeting Q&A. Streams NDJSON; body is `{meeting_id, question}` only. */
|
|
634
634
|
intelligenceAsk: "/api/v1/meet/intelligence/ask",
|
|
635
|
+
/** Anyone in the meeting (guests with the room pass included) asks for captions for themselves. */
|
|
636
|
+
noteTakerCaptions: "/api/v1/meet/agent/captions",
|
|
635
637
|
/** A host ends the meeting for everyone. Served by MRI-A6. */
|
|
636
638
|
meetingEnd: "/api/v1/meet/end",
|
|
637
639
|
/**
|
|
@@ -778,7 +780,8 @@ function parseRoomToken(body, roomName) {
|
|
|
778
780
|
breakoutRoomId: readString(payload, "breakout_room_id") ?? null,
|
|
779
781
|
observersVisibleTo: readString(payload, "observers_visible_to") === "hosts" ? "hosts" : "everyone",
|
|
780
782
|
connectionPolicy: connectionPolicyFrom(payload["connection_policy"]),
|
|
781
|
-
leavePolicy: leavePolicyFrom(payload["leave_policy"])
|
|
783
|
+
leavePolicy: leavePolicyFrom(payload["leave_policy"]),
|
|
784
|
+
canReclaim: payload["can_reclaim"] === true
|
|
782
785
|
};
|
|
783
786
|
}
|
|
784
787
|
function createTokenClient(options) {
|
|
@@ -1240,6 +1243,42 @@ function createMeetAi(options) {
|
|
|
1240
1243
|
},
|
|
1241
1244
|
"meet.ai.startNoteTaker"
|
|
1242
1245
|
),
|
|
1246
|
+
requestCaptions: async ({ meetingId }) => {
|
|
1247
|
+
const roomToken = options.resolveRoomToken?.() ?? null;
|
|
1248
|
+
const operation = "meet.ai.requestCaptions";
|
|
1249
|
+
try {
|
|
1250
|
+
const response = await send({
|
|
1251
|
+
path: MEET_ROUTES.noteTakerCaptions,
|
|
1252
|
+
body: { meeting_id: meetingId },
|
|
1253
|
+
operation,
|
|
1254
|
+
requireAuth: roomToken === null,
|
|
1255
|
+
...roomToken !== null ? { headers: { [MEETING_PASS_HEADER]: roomToken } } : {}
|
|
1256
|
+
});
|
|
1257
|
+
if (!response.ok) {
|
|
1258
|
+
options.store.patch({ captionsRefusal: await refusalFor(response, operation) });
|
|
1259
|
+
return;
|
|
1260
|
+
}
|
|
1261
|
+
let payload = null;
|
|
1262
|
+
try {
|
|
1263
|
+
payload = await response.json();
|
|
1264
|
+
} catch {
|
|
1265
|
+
payload = null;
|
|
1266
|
+
}
|
|
1267
|
+
const row = typeof payload === "object" && payload !== null ? payload : {};
|
|
1268
|
+
const detail = typeof row["detail"] === "string" ? row["detail"] : "";
|
|
1269
|
+
options.store.patch({
|
|
1270
|
+
captionsRefusal: row["dispatched"] === true ? null : { status: 200, reason: detail, remedy: "" }
|
|
1271
|
+
});
|
|
1272
|
+
} catch (error) {
|
|
1273
|
+
options.store.patch({
|
|
1274
|
+
captionsRefusal: {
|
|
1275
|
+
status: 0,
|
|
1276
|
+
reason: error.message,
|
|
1277
|
+
remedy: error.remedy ?? ""
|
|
1278
|
+
}
|
|
1279
|
+
});
|
|
1280
|
+
}
|
|
1281
|
+
},
|
|
1243
1282
|
stopNoteTaker: ({ meetingId }) => dispatch(
|
|
1244
1283
|
MEET_ROUTES.noteTakerStop,
|
|
1245
1284
|
{ meeting_id: meetingId },
|
|
@@ -1278,7 +1317,7 @@ function exactNoteTakerParticipant(meeting, participants) {
|
|
|
1278
1317
|
}
|
|
1279
1318
|
function noteTakerStatus(args) {
|
|
1280
1319
|
const present = exactNoteTakerParticipant(args.meeting, args.participants) !== null;
|
|
1281
|
-
const raw = args.meeting?.metadata[NOTE_TAKER_METADATA_KEY];
|
|
1320
|
+
const raw = args.meeting?.metadata?.[NOTE_TAKER_METADATA_KEY];
|
|
1282
1321
|
const row = typeof raw === "object" && raw !== null && !Array.isArray(raw) ? raw : {};
|
|
1283
1322
|
const recorded = readState(row["state"]);
|
|
1284
1323
|
const detail = typeof row["reason"] === "string" && row["reason"].length > 0 ? row["reason"] : null;
|
|
@@ -3011,6 +3050,8 @@ function planFromRow(row) {
|
|
|
3011
3050
|
minutes: num2(row["minutes"]) ?? 0,
|
|
3012
3051
|
openedAt: str(row["opened_at"]),
|
|
3013
3052
|
endsAt: str(row["ends_at"]),
|
|
3053
|
+
closingAt: str(row["closing_at"]),
|
|
3054
|
+
moveMode: row["move_mode"] === "auto" ? "auto" : "invite",
|
|
3014
3055
|
closedAt: str(row["closed_at"]),
|
|
3015
3056
|
broadcasts: (Array.isArray(row["broadcasts"]) ? row["broadcasts"] : []).map(broadcastFrom).filter((b) => b !== null)
|
|
3016
3057
|
};
|
|
@@ -3162,9 +3203,17 @@ function autoAssign(identities, rooms, exclude = []) {
|
|
|
3162
3203
|
return out;
|
|
3163
3204
|
}
|
|
3164
3205
|
function breakoutRemainingMs(plan, now) {
|
|
3165
|
-
if (plan === null || plan.state !== "open"
|
|
3166
|
-
const
|
|
3167
|
-
|
|
3206
|
+
if (plan === null || plan.state !== "open") return null;
|
|
3207
|
+
const ends = [plan.endsAt, plan.closingAt].map((stamp) => stamp === null ? Number.NaN : Date.parse(stamp)).filter((time) => !Number.isNaN(time));
|
|
3208
|
+
if (ends.length === 0) return null;
|
|
3209
|
+
return Math.max(0, Math.min(...ends) - now);
|
|
3210
|
+
}
|
|
3211
|
+
var BREAKOUT_COUNTDOWN_MS = 3e4;
|
|
3212
|
+
function breakoutClosingMs(plan, now) {
|
|
3213
|
+
if (plan === null || plan.state !== "open") return null;
|
|
3214
|
+
const remaining = breakoutRemainingMs(plan, now);
|
|
3215
|
+
if (remaining === null) return null;
|
|
3216
|
+
return plan.closingAt !== null || remaining <= BREAKOUT_COUNTDOWN_MS ? remaining : null;
|
|
3168
3217
|
}
|
|
3169
3218
|
function defaultCaptionLanguage(settings, viewerLanguage) {
|
|
3170
3219
|
const configured = settings?.captionTranslationLanguage.trim() ?? "";
|
|
@@ -3411,6 +3460,47 @@ function recordingLabel(state) {
|
|
|
3411
3460
|
}
|
|
3412
3461
|
}
|
|
3413
3462
|
|
|
3463
|
+
// src/core/live-policy.ts
|
|
3464
|
+
var DEFAULT_LIVE_POLICY = {
|
|
3465
|
+
chatMode: "everyone",
|
|
3466
|
+
avLock: "off",
|
|
3467
|
+
timeLimitMinutes: 0,
|
|
3468
|
+
timeLimitWarningMinutes: 5,
|
|
3469
|
+
recordingConsent: "notice"
|
|
3470
|
+
};
|
|
3471
|
+
function oneOf(value, allowed, fallback) {
|
|
3472
|
+
return typeof value === "string" && allowed.includes(value) ? value : fallback;
|
|
3473
|
+
}
|
|
3474
|
+
function whole(value, fallback) {
|
|
3475
|
+
const n = typeof value === "number" ? value : typeof value === "string" ? Number(value) : NaN;
|
|
3476
|
+
return Number.isFinite(n) && n >= 0 ? Math.floor(n) : fallback;
|
|
3477
|
+
}
|
|
3478
|
+
function livePolicyFrom(raw) {
|
|
3479
|
+
const o = typeof raw === "object" && raw !== null ? raw : {};
|
|
3480
|
+
return {
|
|
3481
|
+
chatMode: oneOf(o["chat_mode"], ["everyone", "hosts_only", "off"], DEFAULT_LIVE_POLICY.chatMode),
|
|
3482
|
+
avLock: oneOf(o["attendee_av_lock"], ["off", "mic", "camera", "both"], DEFAULT_LIVE_POLICY.avLock),
|
|
3483
|
+
timeLimitMinutes: whole(o["time_limit_minutes"], 0),
|
|
3484
|
+
timeLimitWarningMinutes: whole(o["time_limit_warning_minutes"], DEFAULT_LIVE_POLICY.timeLimitWarningMinutes),
|
|
3485
|
+
recordingConsent: oneOf(
|
|
3486
|
+
o["recording_consent"],
|
|
3487
|
+
["notice", "ok_or_leave", "view_only_until_agree"],
|
|
3488
|
+
DEFAULT_LIVE_POLICY.recordingConsent
|
|
3489
|
+
)
|
|
3490
|
+
};
|
|
3491
|
+
}
|
|
3492
|
+
function samePolicy(a, b) {
|
|
3493
|
+
return a.chatMode === b.chatMode && a.avLock === b.avLock && a.timeLimitMinutes === b.timeLimitMinutes && a.timeLimitWarningMinutes === b.timeLimitWarningMinutes && a.recordingConsent === b.recordingConsent;
|
|
3494
|
+
}
|
|
3495
|
+
var MODERATORS = /* @__PURE__ */ new Set(["host", "cohost"]);
|
|
3496
|
+
function isLockedFor(policy, role, kind) {
|
|
3497
|
+
if (MODERATORS.has(role)) return false;
|
|
3498
|
+
return policy.avLock === "both" || (kind === "microphone" ? policy.avLock === "mic" : policy.avLock === "camera");
|
|
3499
|
+
}
|
|
3500
|
+
function consentPending(policy, role, recordingOn, given) {
|
|
3501
|
+
return recordingOn && !given && policy.recordingConsent !== "notice" && !MODERATORS.has(role);
|
|
3502
|
+
}
|
|
3503
|
+
|
|
3414
3504
|
// src/core/attendance.ts
|
|
3415
3505
|
var IDLE_MEDIA_REGION = {
|
|
3416
3506
|
permission: { microphone: "unknown", camera: "unknown" },
|
|
@@ -3481,6 +3571,11 @@ var EMPTY = {
|
|
|
3481
3571
|
presenter: null,
|
|
3482
3572
|
waitingCount: 0,
|
|
3483
3573
|
leavePolicy: DEFAULT_LEAVE_POLICY,
|
|
3574
|
+
livePolicy: DEFAULT_LIVE_POLICY,
|
|
3575
|
+
canReclaim: false,
|
|
3576
|
+
recordingConsentGiven: false,
|
|
3577
|
+
sessionStartedAt: null,
|
|
3578
|
+
mutedByHost: false,
|
|
3484
3579
|
moderationNotice: null,
|
|
3485
3580
|
actionNotice: null,
|
|
3486
3581
|
privateChatRequest: null,
|
|
@@ -3490,6 +3585,8 @@ var EMPTY = {
|
|
|
3490
3585
|
names: {},
|
|
3491
3586
|
wrapUp: null,
|
|
3492
3587
|
noteTakerRefusal: null,
|
|
3588
|
+
captionsOn: false,
|
|
3589
|
+
captionsRefusal: null,
|
|
3493
3590
|
media: IDLE_MEDIA,
|
|
3494
3591
|
mediaRegion: IDLE_MEDIA_REGION,
|
|
3495
3592
|
failure: null,
|
|
@@ -3694,7 +3791,12 @@ function createMeetStore(initial = {}) {
|
|
|
3694
3791
|
if (current.enabled === next.enabled && current.unavailable?.code === next.unavailable?.code && current.unavailable?.reason === next.unavailable?.reason && current.unavailable?.message === next.unavailable?.message) {
|
|
3695
3792
|
return;
|
|
3696
3793
|
}
|
|
3697
|
-
commit({
|
|
3794
|
+
commit({
|
|
3795
|
+
...state,
|
|
3796
|
+
media: { ...state.media, [kind]: next },
|
|
3797
|
+
// The person turned their own microphone on again: the host's mute is over.
|
|
3798
|
+
...kind === "microphone" && next.enabled && state.mutedByHost ? { mutedByHost: false } : {}
|
|
3799
|
+
});
|
|
3698
3800
|
},
|
|
3699
3801
|
reset() {
|
|
3700
3802
|
const phase = nextPhase(state.phase, { type: "reset", teardown: true }) ?? EMPTY.phase;
|
|
@@ -3920,10 +4022,11 @@ function moderationText(kind, payload) {
|
|
|
3920
4022
|
switch (kind) {
|
|
3921
4023
|
case "muted": {
|
|
3922
4024
|
const source = text2(payload["source"]) ?? "microphone";
|
|
3923
|
-
|
|
4025
|
+
const who = payload["by_role"] === "host" || payload["by_role"] === "cohost" ? "the host" : by;
|
|
4026
|
+
return source === "microphone" ? `You've been muted by ${who}.` : source === "camera" ? `Your camera was turned off by ${who}.` : `Your screen share was stopped by ${who}.`;
|
|
3924
4027
|
}
|
|
3925
4028
|
case "ask-unmute":
|
|
3926
|
-
return `${by}
|
|
4029
|
+
return `${by === "The host" ? "The host" : by} asked you to unmute.`;
|
|
3927
4030
|
case "removed":
|
|
3928
4031
|
return `${by} removed you from this meeting.`;
|
|
3929
4032
|
default:
|
|
@@ -3949,7 +4052,7 @@ function applyModeration(store, payload, now) {
|
|
|
3949
4052
|
grants: { ...snapshot.grants, role },
|
|
3950
4053
|
moderationNotice: {
|
|
3951
4054
|
kind: "role",
|
|
3952
|
-
text: role === "host" ? "You are now the host." : role === "cohost" ? "You
|
|
4055
|
+
text: role === "host" ? "You are now the host." : role === "cohost" ? "You're now a co-host." : role === "presenter" ? "You're now a presenter." : snapshot.grants.role === "presenter" ? "You're now a participant: you can no longer present." : "You're now a participant: you are no longer a co-host.",
|
|
3953
4056
|
at: now()
|
|
3954
4057
|
}
|
|
3955
4058
|
});
|
|
@@ -3961,7 +4064,10 @@ function applyModeration(store, payload, now) {
|
|
|
3961
4064
|
case "removed": {
|
|
3962
4065
|
const sentence = moderationText(kind, payload);
|
|
3963
4066
|
if (sentence === null) return ignored("a moderation event with no sentence");
|
|
3964
|
-
store.patch({
|
|
4067
|
+
store.patch({
|
|
4068
|
+
moderationNotice: { kind, text: sentence, at: now() },
|
|
4069
|
+
...kind === "muted" && (text2(payload["source"]) ?? "microphone") === "microphone" ? { mutedByHost: true } : {}
|
|
4070
|
+
});
|
|
3965
4071
|
return HANDLED;
|
|
3966
4072
|
}
|
|
3967
4073
|
default:
|
|
@@ -4783,6 +4889,7 @@ function applyDoorRow(store, attendee) {
|
|
|
4783
4889
|
function decidedState(state) {
|
|
4784
4890
|
return WAITING_ADMISSION_STATES.includes(state) ? "expired" : state;
|
|
4785
4891
|
}
|
|
4892
|
+
var LIVE_POLICY_POLL_MS = 3e3;
|
|
4786
4893
|
function createMeetingSession(options) {
|
|
4787
4894
|
const { manager, repository, store, identity } = options;
|
|
4788
4895
|
let channel = null;
|
|
@@ -4924,6 +5031,46 @@ function createMeetingSession(options) {
|
|
|
4924
5031
|
stopWaiting();
|
|
4925
5032
|
}
|
|
4926
5033
|
}
|
|
5034
|
+
let policyTimer = null;
|
|
5035
|
+
let policyReading = false;
|
|
5036
|
+
async function readLivePolicy() {
|
|
5037
|
+
const meeting = store.snapshot().meeting;
|
|
5038
|
+
const pass = options.resolvePass?.() ?? null;
|
|
5039
|
+
if (meeting === null || pass === null || policyReading) return;
|
|
5040
|
+
policyReading = true;
|
|
5041
|
+
try {
|
|
5042
|
+
const response = await options.api.request({
|
|
5043
|
+
path: `${MEET_ROUTES.state}?meeting_id=${encodeURIComponent(meeting.id)}`,
|
|
5044
|
+
method: "GET",
|
|
5045
|
+
requireAuth: false,
|
|
5046
|
+
operation: "meet.state(policy)",
|
|
5047
|
+
headers: { [MEETING_PASS_HEADER]: pass }
|
|
5048
|
+
});
|
|
5049
|
+
if (!response.ok) return;
|
|
5050
|
+
const read = await response.json();
|
|
5051
|
+
const next = livePolicyFrom(read.live_policy);
|
|
5052
|
+
const started = read.session_started_at ? Date.parse(read.session_started_at) : NaN;
|
|
5053
|
+
const snapshot = store.snapshot();
|
|
5054
|
+
const startedAt = Number.isFinite(started) ? started : snapshot.sessionStartedAt;
|
|
5055
|
+
if (!samePolicy(snapshot.livePolicy, next) || startedAt !== snapshot.sessionStartedAt) {
|
|
5056
|
+
store.patch({ livePolicy: next, sessionStartedAt: startedAt });
|
|
5057
|
+
}
|
|
5058
|
+
} catch {
|
|
5059
|
+
} finally {
|
|
5060
|
+
policyReading = false;
|
|
5061
|
+
}
|
|
5062
|
+
}
|
|
5063
|
+
function syncLivePolicyTimer() {
|
|
5064
|
+
const inCall = store.snapshot().phase === "in_call";
|
|
5065
|
+
if (inCall && policyTimer === null) {
|
|
5066
|
+
void readLivePolicy();
|
|
5067
|
+
policyTimer = setInterval(() => void readLivePolicy(), LIVE_POLICY_POLL_MS);
|
|
5068
|
+
} else if (!inCall && policyTimer !== null) {
|
|
5069
|
+
clearInterval(policyTimer);
|
|
5070
|
+
policyTimer = null;
|
|
5071
|
+
}
|
|
5072
|
+
}
|
|
5073
|
+
const unsubscribePolicy = store.subscribe(syncLivePolicyTimer);
|
|
4927
5074
|
async function readState2() {
|
|
4928
5075
|
if (reading !== null) return reading;
|
|
4929
5076
|
const meeting = store.snapshot().meeting;
|
|
@@ -5310,6 +5457,9 @@ function createMeetingSession(options) {
|
|
|
5310
5457
|
},
|
|
5311
5458
|
dispose() {
|
|
5312
5459
|
stopWaiting();
|
|
5460
|
+
unsubscribePolicy();
|
|
5461
|
+
if (policyTimer !== null) clearInterval(policyTimer);
|
|
5462
|
+
policyTimer = null;
|
|
5313
5463
|
channel?.close();
|
|
5314
5464
|
channel = null;
|
|
5315
5465
|
meetingChannel?.close();
|
|
@@ -6627,6 +6777,7 @@ function createRoomEngine(options) {
|
|
|
6627
6777
|
lastServerUrl = token.serverUrl;
|
|
6628
6778
|
connectionPolicy = token.connectionPolicy ?? DEFAULT_CONNECTION_POLICY;
|
|
6629
6779
|
if (token.leavePolicy !== void 0) store.patch({ leavePolicy: token.leavePolicy });
|
|
6780
|
+
store.patch({ canReclaim: token.canReclaim === true });
|
|
6630
6781
|
store.patch({
|
|
6631
6782
|
localIdentity: token.identity,
|
|
6632
6783
|
breakoutRoomId: token.breakoutRoomId ?? null,
|
|
@@ -7126,8 +7277,12 @@ var MODERATION_ACTIONS = [
|
|
|
7126
7277
|
"lower_hand",
|
|
7127
7278
|
"lower_all_hands",
|
|
7128
7279
|
"make_cohost",
|
|
7280
|
+
"make_presenter",
|
|
7281
|
+
"revoke_presenter",
|
|
7282
|
+
"set_av_lock",
|
|
7129
7283
|
"revoke_cohost",
|
|
7130
7284
|
"transfer_host",
|
|
7285
|
+
"reclaim_host",
|
|
7131
7286
|
"rename",
|
|
7132
7287
|
"remove",
|
|
7133
7288
|
"report"
|
|
@@ -8057,11 +8212,13 @@ function takeMoveRequest(store) {
|
|
|
8057
8212
|
|
|
8058
8213
|
// src/core/observation.ts
|
|
8059
8214
|
function observedRole(role, standing) {
|
|
8060
|
-
if (role === "host" || role === "cohost") return role;
|
|
8215
|
+
if (role === "host" || role === "cohost" || role === "presenter") return role;
|
|
8061
8216
|
return isLinkFollower(standing) ? "guest" : "participant";
|
|
8062
8217
|
}
|
|
8063
|
-
function observedDevice(track, permission = "unknown") {
|
|
8218
|
+
function observedDevice(track, permission = "unknown", held = null) {
|
|
8219
|
+
if (held === "locked") return "locked";
|
|
8064
8220
|
if (track.enabled) return "on";
|
|
8221
|
+
if (held === "muted_by_host") return "muted_by_host";
|
|
8065
8222
|
const reason = deviceUnavailableReason(track, permission);
|
|
8066
8223
|
return reason === null ? "muted_by_me" : `unavailable:${reason}`;
|
|
8067
8224
|
}
|
|
@@ -8098,10 +8255,20 @@ function observe(input) {
|
|
|
8098
8255
|
recording: snapshot?.recording === "recording" || snapshot?.recording === "stopping",
|
|
8099
8256
|
// S6c: the autoplay region is known only inside the room (no remote audio exists elsewhere).
|
|
8100
8257
|
audioBlocked: inCall && snapshot !== null ? snapshot.mediaRegion.audioOutput === "blocked" : null,
|
|
8101
|
-
camera: deviceInPlay && snapshot !== null ? observedDevice(snapshot.media.camera, snapshot.mediaRegion.permission.camera) : null,
|
|
8102
|
-
microphone: deviceInPlay && snapshot !== null ? observedDevice(snapshot.media.microphone, snapshot.mediaRegion.permission.microphone) : null
|
|
8258
|
+
camera: deviceInPlay && snapshot !== null ? observedDevice(snapshot.media.camera, snapshot.mediaRegion.permission.camera, lockedFor(snapshot, "camera", inCall)) : null,
|
|
8259
|
+
microphone: deviceInPlay && snapshot !== null ? observedDevice(snapshot.media.microphone, snapshot.mediaRegion.permission.microphone, lockedFor(snapshot, "microphone", inCall)) : null
|
|
8103
8260
|
};
|
|
8104
8261
|
}
|
|
8262
|
+
function lockedFor(snapshot, kind, inCall) {
|
|
8263
|
+
if (!inCall) return null;
|
|
8264
|
+
const role = effectiveRole(snapshot);
|
|
8265
|
+
if (isLockedFor(snapshot.livePolicy, role, kind)) return "locked";
|
|
8266
|
+
if (snapshot.livePolicy.recordingConsent === "view_only_until_agree" && consentPending(snapshot.livePolicy, role, snapshot.recording === "recording", snapshot.recordingConsentGiven)) {
|
|
8267
|
+
return "locked";
|
|
8268
|
+
}
|
|
8269
|
+
if (kind === "microphone" && snapshot.mutedByHost) return "muted_by_host";
|
|
8270
|
+
return null;
|
|
8271
|
+
}
|
|
8105
8272
|
function staticObservation(phase, reason = null) {
|
|
8106
8273
|
return {
|
|
8107
8274
|
phase,
|
|
@@ -8250,6 +8417,7 @@ export {
|
|
|
8250
8417
|
autoAssign,
|
|
8251
8418
|
autoRejoinDelayMs,
|
|
8252
8419
|
availableActivities,
|
|
8420
|
+
breakoutClosingMs,
|
|
8253
8421
|
breakoutRemainingMs,
|
|
8254
8422
|
callInviteAction,
|
|
8255
8423
|
callInviteText,
|