@ai-matrx/meet 0.7.84 → 0.8.0
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 +35 -0
- package/README.md +13 -7
- package/dist/collab-Bss-_Jfp.d.cts +805 -0
- package/dist/collab-Bss-_Jfp.d.ts +805 -0
- package/dist/components-BOUmOiPj.d.cts +240 -0
- package/dist/components-BmgstFx_.d.ts +240 -0
- package/dist/core.cjs +6724 -0
- package/dist/core.cjs.map +1 -0
- package/dist/core.d.cts +3048 -0
- package/dist/core.d.ts +3048 -0
- package/dist/core.js +6691 -0
- package/dist/core.js.map +1 -0
- package/dist/index.cjs +81 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -3780
- package/dist/index.d.ts +5 -3780
- package/dist/index.js +81 -15
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +468 -674
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +77 -365
- package/dist/react.d.ts +77 -365
- package/dist/react.js +412 -629
- package/dist/react.js.map +1 -1
- package/dist/skins/meet.cjs +4636 -0
- package/dist/skins/meet.cjs.map +1 -0
- package/dist/skins/meet.d.cts +113 -0
- package/dist/skins/meet.d.ts +113 -0
- package/dist/skins/meet.js +4617 -0
- package/dist/skins/meet.js.map +1 -0
- package/package.json +28 -6
|
@@ -0,0 +1,805 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE MEET DOMAIN MODEL.
|
|
3
|
+
*
|
|
4
|
+
* D3: an audio call, a video call, and a meeting are ONE thing — a room. An
|
|
5
|
+
* audio call is a room with video off; a meeting is a room plus scheduling,
|
|
6
|
+
* lobby and host metadata. That ruling is expressed here as ONE `MeetRoom`
|
|
7
|
+
* identity carried by both `/calls` and `/meetings`, not as three parallel
|
|
8
|
+
* hierarchies that drift.
|
|
9
|
+
*
|
|
10
|
+
* Identity-bearing values are BRANDED (C22). A `MeetingId` and a `RoomName` are
|
|
11
|
+
* both strings and are NOT interchangeable — passing the meeting's row id where
|
|
12
|
+
* LiveKit wants the room name produces a room nobody else joins, which looks
|
|
13
|
+
* exactly like "the other person's camera is broken".
|
|
14
|
+
*/
|
|
15
|
+
declare const brand: unique symbol;
|
|
16
|
+
type Brand<T, B extends string> = T & {
|
|
17
|
+
readonly [brand]: B;
|
|
18
|
+
};
|
|
19
|
+
type MeetingId = Brand<string, "MeetingId">;
|
|
20
|
+
type CallId = Brand<string, "CallId">;
|
|
21
|
+
/** The LiveKit room name. THE MEDIA ROOM KEY — durable for a meeting link. */
|
|
22
|
+
type RoomName = Brand<string, "RoomName">;
|
|
23
|
+
type UserId = Brand<string, "UserId">;
|
|
24
|
+
type OrganizationId = Brand<string, "OrganizationId">;
|
|
25
|
+
/** LiveKit participant identity. `user:<uuid>` or `guest:<opaque>`. */
|
|
26
|
+
type ParticipantIdentity = Brand<string, "ParticipantIdentity">;
|
|
27
|
+
declare const asMeetingId: (value: string) => MeetingId;
|
|
28
|
+
declare const asCallId: (value: string) => CallId;
|
|
29
|
+
declare const asRoomName: (value: string) => RoomName;
|
|
30
|
+
declare const asUserId: (value: string) => UserId;
|
|
31
|
+
declare const asOrganizationId: (value: string) => OrganizationId;
|
|
32
|
+
declare const asParticipantIdentity: (value: string) => ParticipantIdentity;
|
|
33
|
+
type JsonValue = string | number | boolean | null | JsonValue[] | {
|
|
34
|
+
[key: string]: JsonValue;
|
|
35
|
+
};
|
|
36
|
+
type JsonObject = {
|
|
37
|
+
[key: string]: JsonValue;
|
|
38
|
+
};
|
|
39
|
+
/** Audio-only or with video. A call escalates; it never becomes a new call. */
|
|
40
|
+
type CallMode = "audio" | "video";
|
|
41
|
+
/**
|
|
42
|
+
* WHO IS IN THE ROOM, as far as authorization is concerned.
|
|
43
|
+
*
|
|
44
|
+
* A guest has no `userId` — by construction, not by convention. Everything that
|
|
45
|
+
* requires authentication (host powers, recording, starting a call) takes a
|
|
46
|
+
* `userId` and therefore cannot compile against a guest identity (D6).
|
|
47
|
+
*/
|
|
48
|
+
interface MeetIdentity {
|
|
49
|
+
readonly userId: UserId | null;
|
|
50
|
+
readonly organizationId: OrganizationId;
|
|
51
|
+
readonly displayName: string;
|
|
52
|
+
readonly avatarUrl: string | null;
|
|
53
|
+
/** Stable per-device value so a reconnect keeps ONE LiveKit identity. */
|
|
54
|
+
readonly deviceId: string;
|
|
55
|
+
}
|
|
56
|
+
declare function isGuest(identity: MeetIdentity): boolean;
|
|
57
|
+
/**
|
|
58
|
+
* WHO SOMEBODY IS IN A ROOM — the SERVER's word, never this client's (MRI-A11).
|
|
59
|
+
*
|
|
60
|
+
* `host` is `meet_meetings.host_user_id` or a real `iam` grant on the meeting;
|
|
61
|
+
* `participant` is an authenticated member of the meeting's organization;
|
|
62
|
+
* `guest` is a link-follower with no session. Organization membership is
|
|
63
|
+
* TENANCY and never permission, which is why "member of the org" lands on
|
|
64
|
+
* `participant` and not on `host`.
|
|
65
|
+
*
|
|
66
|
+
* `cohost` has no server-side minter yet and is kept because the rank ladder
|
|
67
|
+
* below is the thing that stops a co-host from removing the host; the moment a
|
|
68
|
+
* co-host grant exists it slots in without a second vocabulary.
|
|
69
|
+
*/
|
|
70
|
+
type ParticipantRole = "host" | "cohost" | "participant" | "guest";
|
|
71
|
+
/** Rank, so "can this actor do that to that participant" is one comparison. */
|
|
72
|
+
declare const ROLE_RANK: Readonly<Record<ParticipantRole, number>>;
|
|
73
|
+
/**
|
|
74
|
+
* 🚨 THE ONE PREDICATE FOR "DOES THIS PERSON HOLD HOST POWERS".
|
|
75
|
+
*
|
|
76
|
+
* Every host control in this package answers this question and no other. It is
|
|
77
|
+
* a pure function of a SERVER-minted role, so there is no path by which a
|
|
78
|
+
* client's own state can grant it anything.
|
|
79
|
+
*/
|
|
80
|
+
declare function isHostRole(role: ParticipantRole): boolean;
|
|
81
|
+
/**
|
|
82
|
+
* THE LOBBY LADDER (D6). A guest link-follower starts `knocking`; the host
|
|
83
|
+
* admits or denies. An authenticated member of the meeting's org is `admitted`
|
|
84
|
+
* on arrival unless the meeting is locked.
|
|
85
|
+
*/
|
|
86
|
+
type AdmissionState = "knocking" | "admitted" | "denied" | "removed";
|
|
87
|
+
interface LobbyEntry {
|
|
88
|
+
readonly identity: ParticipantIdentity;
|
|
89
|
+
readonly displayName: string;
|
|
90
|
+
readonly avatarUrl: string | null;
|
|
91
|
+
readonly userId: UserId | null;
|
|
92
|
+
readonly state: AdmissionState;
|
|
93
|
+
readonly knockedAt: string;
|
|
94
|
+
readonly decidedAt: string | null;
|
|
95
|
+
readonly decidedBy: UserId | null;
|
|
96
|
+
}
|
|
97
|
+
type ConnectionQuality = "excellent" | "good" | "poor" | "lost" | "unknown";
|
|
98
|
+
/** One person in the room, as the UI needs them. Media state is a FACT here. */
|
|
99
|
+
interface MeetParticipant {
|
|
100
|
+
readonly identity: ParticipantIdentity;
|
|
101
|
+
readonly userId: UserId | null;
|
|
102
|
+
readonly displayName: string;
|
|
103
|
+
readonly avatarUrl: string | null;
|
|
104
|
+
readonly role: ParticipantRole;
|
|
105
|
+
/**
|
|
106
|
+
* Whether the SERVER granted this participant the power to start a recording
|
|
107
|
+
* (MRI-A11). It is not `role === "host"`: a meeting whose recording policy is
|
|
108
|
+
* `always-on` is the server's to start and a host may not touch it, and a
|
|
109
|
+
* `disabled` policy grants it to nobody. The client renders this; it never
|
|
110
|
+
* recomputes the policy.
|
|
111
|
+
*/
|
|
112
|
+
readonly canRecord: boolean;
|
|
113
|
+
readonly isLocal: boolean;
|
|
114
|
+
readonly isAgent: boolean;
|
|
115
|
+
readonly micEnabled: boolean;
|
|
116
|
+
readonly cameraEnabled: boolean;
|
|
117
|
+
readonly screenSharing: boolean;
|
|
118
|
+
readonly speaking: boolean;
|
|
119
|
+
readonly handRaised: boolean;
|
|
120
|
+
/**
|
|
121
|
+
* When the hand went up (epoch ms), or null. The HAND QUEUE is ordered by it —
|
|
122
|
+
* first raised, first called on (Meet wave 2). Optional so a roster built by a
|
|
123
|
+
* host before 0.8 still type-checks.
|
|
124
|
+
*/
|
|
125
|
+
readonly handRaisedAt?: number | null | undefined;
|
|
126
|
+
readonly quality: ConnectionQuality;
|
|
127
|
+
readonly joinedAt: string;
|
|
128
|
+
/**
|
|
129
|
+
* LiveKit participant attributes, verbatim. The note-taker announces itself
|
|
130
|
+
* here (`matrx.agent.consent_notice`), which is how a consent notice can be
|
|
131
|
+
* rendered for a participant whose token metadata this client cannot read.
|
|
132
|
+
*/
|
|
133
|
+
readonly attributes: Readonly<Record<string, string>>;
|
|
134
|
+
}
|
|
135
|
+
type RoomPhase = "idle" | "requesting-token" | "lobby" | "connecting" | "connected" | "reconnecting" | "left" | "failed";
|
|
136
|
+
type MeetingKind = "instant" | "scheduled" | "recurring";
|
|
137
|
+
interface MeetingRecord {
|
|
138
|
+
readonly id: MeetingId;
|
|
139
|
+
readonly organizationId: OrganizationId;
|
|
140
|
+
/** Durable: the same room name before, during, and after (R3). */
|
|
141
|
+
readonly roomName: RoomName;
|
|
142
|
+
/** The public slug in a meeting link. Durable for recurring meetings. */
|
|
143
|
+
readonly slug: string;
|
|
144
|
+
readonly title: string;
|
|
145
|
+
readonly kind: MeetingKind;
|
|
146
|
+
readonly hostUserId: UserId;
|
|
147
|
+
readonly scheduledFor: string | null;
|
|
148
|
+
readonly scheduledDurationMinutes: number | null;
|
|
149
|
+
readonly startedAt: string | null;
|
|
150
|
+
readonly endedAt: string | null;
|
|
151
|
+
readonly locked: boolean;
|
|
152
|
+
readonly lobbyEnabled: boolean;
|
|
153
|
+
readonly recordingPolicy: RecordingPolicy;
|
|
154
|
+
readonly aiEnabled: boolean;
|
|
155
|
+
readonly metadata: JsonObject;
|
|
156
|
+
/** IANA zone the meeting's wall-clock time is authored in (`America/Los_Angeles`). */
|
|
157
|
+
readonly timeZone?: string;
|
|
158
|
+
/** Description / agenda text. */
|
|
159
|
+
readonly agenda?: string | null;
|
|
160
|
+
/** RFC 5545 RRULE subset — one row and one durable link per series. */
|
|
161
|
+
readonly recurrenceRule?: string | null;
|
|
162
|
+
/** Signed-in members and invitees may enter before a host arrives. */
|
|
163
|
+
readonly joinBeforeHost?: boolean;
|
|
164
|
+
/** Cancel is a STATE: the link still resolves and says so; nobody can join. */
|
|
165
|
+
readonly cancelledAt?: string | null;
|
|
166
|
+
readonly cancellationReason?: string | null;
|
|
167
|
+
/** Archived (soft deleted). Restorable by the host or a co-host. */
|
|
168
|
+
readonly deletedAt?: string | null;
|
|
169
|
+
/** iCalendar SEQUENCE — bumped by the database on every reschedule and on cancel. */
|
|
170
|
+
readonly calendarSequence?: number;
|
|
171
|
+
/** Row version, for the compare-and-swap in `updateMeeting`. */
|
|
172
|
+
readonly version?: number;
|
|
173
|
+
}
|
|
174
|
+
/** An invitee's answer. `needs_action` = not answered yet (iCalendar NEEDS-ACTION). */
|
|
175
|
+
type RsvpState = "needs_action" | "accepted" | "declined" | "tentative";
|
|
176
|
+
/** The answers a person can give. */
|
|
177
|
+
type RsvpAnswer = Exclude<RsvpState, "needs_action">;
|
|
178
|
+
/** A co-host holds host moderation through a real `iam` admin grant on the meeting. */
|
|
179
|
+
type InviteeRole = "invitee" | "cohost";
|
|
180
|
+
/** One person a meeting was sent to (`communication.meet_invitees`). */
|
|
181
|
+
interface MeetingInvitee {
|
|
182
|
+
readonly id: string;
|
|
183
|
+
readonly meetingId: MeetingId;
|
|
184
|
+
/** Set when the person has an account — they skip the waiting room and answer in-app. */
|
|
185
|
+
readonly userId: UserId | null;
|
|
186
|
+
readonly email: string | null;
|
|
187
|
+
readonly displayName: string | null;
|
|
188
|
+
readonly role: InviteeRole;
|
|
189
|
+
readonly rsvpState: RsvpState;
|
|
190
|
+
readonly respondedAt: string | null;
|
|
191
|
+
readonly rsvpNote: string | null;
|
|
192
|
+
/** The calendar_sequence of the last invitation they were sent; null = never told. */
|
|
193
|
+
readonly lastSentSequence: number | null;
|
|
194
|
+
readonly lastSentAt: string | null;
|
|
195
|
+
}
|
|
196
|
+
/** Who to invite: an account (`userId`) or an address (`email`), or both. */
|
|
197
|
+
interface InviteeInput {
|
|
198
|
+
readonly userId?: UserId | undefined;
|
|
199
|
+
readonly email?: string | undefined;
|
|
200
|
+
readonly displayName?: string | undefined;
|
|
201
|
+
/** Omit to keep an existing invitee's role; `cohost` needs the host or a co-host. */
|
|
202
|
+
readonly role?: InviteeRole | undefined;
|
|
203
|
+
}
|
|
204
|
+
/** Where one occurrence of a meeting stands. */
|
|
205
|
+
type OccurrenceState = "scheduled" | "moved" | "cancelled";
|
|
206
|
+
/** One occurrence — for a one-off meeting, the meeting itself. */
|
|
207
|
+
interface MeetingOccurrence {
|
|
208
|
+
readonly meetingId: MeetingId;
|
|
209
|
+
/** The start the rule generates (iCalendar RECURRENCE-ID). The key for exceptions. */
|
|
210
|
+
readonly originalStart: string;
|
|
211
|
+
/** When it actually happens (differs from `originalStart` only when `moved`). */
|
|
212
|
+
readonly occurrenceStart: string;
|
|
213
|
+
readonly durationMinutes: number | null;
|
|
214
|
+
readonly state: OccurrenceState;
|
|
215
|
+
readonly sequence: number;
|
|
216
|
+
}
|
|
217
|
+
/** One row of the Upcoming list — an occurrence plus what the list shows about its meeting. */
|
|
218
|
+
interface UpcomingOccurrence extends MeetingOccurrence {
|
|
219
|
+
readonly organizationId: OrganizationId;
|
|
220
|
+
readonly title: string;
|
|
221
|
+
readonly slug: string;
|
|
222
|
+
readonly kind: MeetingKind;
|
|
223
|
+
readonly hostUserId: UserId;
|
|
224
|
+
readonly timeZone: string;
|
|
225
|
+
readonly meetingCancelled: boolean;
|
|
226
|
+
/** The signed-in reader's own answer, when they are an invitee. */
|
|
227
|
+
readonly myRsvp: RsvpState | null;
|
|
228
|
+
/** `host`, `cohost` or `invitee` — null when the reader simply can see it (org member). */
|
|
229
|
+
readonly myRole: "host" | InviteeRole | null;
|
|
230
|
+
}
|
|
231
|
+
/** An exception row (`communication.meet_occurrence_overrides`). */
|
|
232
|
+
interface OccurrenceOverride {
|
|
233
|
+
readonly id: string;
|
|
234
|
+
readonly meetingId: MeetingId;
|
|
235
|
+
readonly originalStart: string;
|
|
236
|
+
readonly state: "cancelled" | "moved";
|
|
237
|
+
readonly newStart: string | null;
|
|
238
|
+
readonly newDurationMinutes: number | null;
|
|
239
|
+
readonly reason: string | null;
|
|
240
|
+
readonly calendarSequence: number;
|
|
241
|
+
/** Set when the exception was withdrawn (`restore`) and the occurrence is back on schedule. */
|
|
242
|
+
readonly deletedAt: string | null;
|
|
243
|
+
}
|
|
244
|
+
/** Settings a meeting carries; unset ones come from the person's / organization's knobs. */
|
|
245
|
+
interface MeetingSettings {
|
|
246
|
+
readonly lobbyEnabled?: boolean | undefined;
|
|
247
|
+
readonly recordingPolicy?: RecordingPolicy | undefined;
|
|
248
|
+
readonly aiEnabled?: boolean | undefined;
|
|
249
|
+
readonly joinBeforeHost?: boolean | undefined;
|
|
250
|
+
}
|
|
251
|
+
/** A partial edit. `null` clears a clearable field (agenda, recurrence rule, time). */
|
|
252
|
+
interface MeetingChanges extends MeetingSettings {
|
|
253
|
+
readonly title?: string | undefined;
|
|
254
|
+
readonly agenda?: string | null | undefined;
|
|
255
|
+
readonly timeZone?: string | undefined;
|
|
256
|
+
readonly scheduledFor?: string | null | undefined;
|
|
257
|
+
readonly scheduledDurationMinutes?: number | null | undefined;
|
|
258
|
+
readonly recurrenceRule?: string | null | undefined;
|
|
259
|
+
/**
|
|
260
|
+
* When the SERIES moves (start, rule or zone), carry its per-date changes
|
|
261
|
+
* (cancelled or moved occurrences) to the new schedule instead of archiving
|
|
262
|
+
* them — Google Calendar's and Outlook's "keep changes to individual events".
|
|
263
|
+
* Absent or false archives them (restorable). Ask the person; never decide.
|
|
264
|
+
*/
|
|
265
|
+
readonly keepExceptions?: boolean | undefined;
|
|
266
|
+
}
|
|
267
|
+
/** What the emailed RSVP link shows and answers (no account needed). */
|
|
268
|
+
type RsvpInvitation = {
|
|
269
|
+
readonly ok: true;
|
|
270
|
+
readonly meeting: {
|
|
271
|
+
readonly title: string;
|
|
272
|
+
readonly slug: string;
|
|
273
|
+
readonly scheduledFor: string | null;
|
|
274
|
+
readonly durationMinutes: number | null;
|
|
275
|
+
readonly timeZone: string;
|
|
276
|
+
readonly agenda: string | null;
|
|
277
|
+
readonly recurrenceRule: string | null;
|
|
278
|
+
readonly cancelledAt: string | null;
|
|
279
|
+
readonly cancellationReason: string | null;
|
|
280
|
+
readonly endedAt: string | null;
|
|
281
|
+
};
|
|
282
|
+
readonly invitee: {
|
|
283
|
+
readonly displayName: string | null;
|
|
284
|
+
readonly email: string | null;
|
|
285
|
+
readonly rsvpState: RsvpState;
|
|
286
|
+
readonly respondedAt: string | null;
|
|
287
|
+
};
|
|
288
|
+
} | {
|
|
289
|
+
readonly ok: false;
|
|
290
|
+
readonly message: string;
|
|
291
|
+
};
|
|
292
|
+
/** Org-configurable, never hardcoded taste (Law 6 / "opinions become knobs"). */
|
|
293
|
+
type RecordingPolicy = "disabled" | "host-controlled" | "always-on";
|
|
294
|
+
type RecordingState = "idle" | "starting" | "recording" | "stopping" | "processing" | "available"
|
|
295
|
+
/** Bytes were purged under retention; notes and transcript remain durable. */
|
|
296
|
+
| "expired" | "failed";
|
|
297
|
+
/**
|
|
298
|
+
* WHICH transport last said what the recording is doing (MRI-A16).
|
|
299
|
+
*
|
|
300
|
+
* `local` — this browser's own optimistic `starting`/`stopping`, which is all
|
|
301
|
+
* the indicator ever had through 0.4.7, and the reason it never went solid.
|
|
302
|
+
* `room` — a `matrx.meet.recording` packet, the ONLY feed a guest has.
|
|
303
|
+
* `record` — the durable `meet_recordings` row. It outranks both (D16).
|
|
304
|
+
*/
|
|
305
|
+
type RecordingSource = "local" | "room" | "record";
|
|
306
|
+
/** The server's durable retention receipt; never inferred from a browser clock. */
|
|
307
|
+
interface RecordingRetention {
|
|
308
|
+
readonly operationId: string;
|
|
309
|
+
readonly policyId: string;
|
|
310
|
+
readonly policyUpdatedAt: string;
|
|
311
|
+
readonly phase: "warning" | "expired";
|
|
312
|
+
readonly dueAt: string;
|
|
313
|
+
readonly expiredAt: string | null;
|
|
314
|
+
}
|
|
315
|
+
interface RecordingRecord {
|
|
316
|
+
readonly id: string;
|
|
317
|
+
readonly meetingId: MeetingId;
|
|
318
|
+
readonly state: RecordingState;
|
|
319
|
+
readonly startedAt: string | null;
|
|
320
|
+
readonly endedAt: string | null;
|
|
321
|
+
readonly startedBy: UserId | null;
|
|
322
|
+
/** The PLATFORM file id. Never a provider URL (R3). */
|
|
323
|
+
readonly fileId: string | null;
|
|
324
|
+
readonly failureReason: string | null;
|
|
325
|
+
/** Present when central retention warned or purged this recording. */
|
|
326
|
+
readonly retention: RecordingRetention | null;
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* WHERE A TRANSCRIPT LINE CAME FROM — and therefore how much it is worth.
|
|
330
|
+
*
|
|
331
|
+
* D16: `lk.transcription` is published by a room participant and its
|
|
332
|
+
* `sender_identity` is client-supplied and unvalidated by LiveKit, so a caption
|
|
333
|
+
* is a HINT — good enough to read live, never good enough to reason from. A
|
|
334
|
+
* `record` line is a durable `meet_transcript_segments` row written server-side
|
|
335
|
+
* by the note-taker: that is the transcript. A record always beats a caption;
|
|
336
|
+
* a caption never overwrites a record.
|
|
337
|
+
*/
|
|
338
|
+
type TranscriptSource = "caption" | "record";
|
|
339
|
+
interface TranscriptSegment {
|
|
340
|
+
readonly id: string;
|
|
341
|
+
readonly meetingId: MeetingId;
|
|
342
|
+
readonly identity: ParticipantIdentity;
|
|
343
|
+
readonly speaker: string;
|
|
344
|
+
readonly text: string;
|
|
345
|
+
readonly startedAt: string;
|
|
346
|
+
/** Interim text is replaced in place by its final; it is never appended. */
|
|
347
|
+
readonly final: boolean;
|
|
348
|
+
readonly source: TranscriptSource;
|
|
349
|
+
/**
|
|
350
|
+
* The `lk.segment_id` this line was captioned under, when known. A durable
|
|
351
|
+
* row carries it too (the worker writes it into the row's metadata), which is
|
|
352
|
+
* what lets a record REPLACE the caption it supersedes rather than doubling it.
|
|
353
|
+
*/
|
|
354
|
+
readonly captionId: string | null;
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* SOMEBODY WHO WAS IN THE MEETING — the durable `meet_participants` row, which
|
|
358
|
+
* outlives the LiveKit room (MRI-D2). `MeetParticipant` above is a live tile;
|
|
359
|
+
* THIS is the attendance record a person reads a week later.
|
|
360
|
+
*/
|
|
361
|
+
interface MeetingAttendee {
|
|
362
|
+
readonly id: string;
|
|
363
|
+
readonly meetingId: MeetingId;
|
|
364
|
+
readonly identity: ParticipantIdentity;
|
|
365
|
+
readonly displayName: string;
|
|
366
|
+
readonly avatarUrl: string | null;
|
|
367
|
+
readonly userId: UserId | null;
|
|
368
|
+
readonly role: ParticipantRole;
|
|
369
|
+
readonly admissionState: AdmissionState;
|
|
370
|
+
readonly isAgent: boolean;
|
|
371
|
+
/** FIRST arrival in the room — a rejoin never moves it. */
|
|
372
|
+
readonly joinedAt: string | null;
|
|
373
|
+
/** Null while in the room, else the last time they left. */
|
|
374
|
+
readonly leftAt: string | null;
|
|
375
|
+
/**
|
|
376
|
+
* Every stay in the room, oldest first (`metadata.sessions`, written by the
|
|
377
|
+
* server's one attendance rule). Empty for a row recorded before stays were
|
|
378
|
+
* kept; the report then falls back to `joinedAt`–`leftAt`.
|
|
379
|
+
*/
|
|
380
|
+
readonly sessions?: readonly AttendanceStay[] | undefined;
|
|
381
|
+
}
|
|
382
|
+
/** One stay in the room: arrived, and left (null = still there or never recorded). */
|
|
383
|
+
interface AttendanceStay {
|
|
384
|
+
readonly joinedAt: string;
|
|
385
|
+
readonly leftAt: string | null;
|
|
386
|
+
}
|
|
387
|
+
type MeetingNoteKind = "note" | "decision" | "action-item";
|
|
388
|
+
/**
|
|
389
|
+
* ONE ROW OF WHAT THE MEETING DECIDED. `meet_notes.kind` is a live CHECK
|
|
390
|
+
* constraint of exactly three values (D5) — there is no `summary` kind and no
|
|
391
|
+
* `transcript` kind — so the wrap-up summary is a `note` carrying
|
|
392
|
+
* `metadata.role = "summary"`, and a live-window note carries `metadata.window`.
|
|
393
|
+
* Both are projected here so the UI never re-reads raw metadata.
|
|
394
|
+
*/
|
|
395
|
+
interface MeetingNote {
|
|
396
|
+
readonly id: string;
|
|
397
|
+
readonly meetingId: MeetingId;
|
|
398
|
+
readonly text: string;
|
|
399
|
+
readonly createdAt: string;
|
|
400
|
+
readonly kind: MeetingNoteKind;
|
|
401
|
+
/** Set ONLY when the assignee resolved to a real user (D18). */
|
|
402
|
+
readonly assigneeUserId: UserId | null;
|
|
403
|
+
/** Who the item is for, as a person reads it. A guest keeps their name here. */
|
|
404
|
+
readonly assigneeDisplayName: string | null;
|
|
405
|
+
/** `"summary"` for the wrap-up; `null` for everything else. */
|
|
406
|
+
readonly role: "summary" | null;
|
|
407
|
+
/** The live-notes window index this came out of, when it came from one. */
|
|
408
|
+
readonly window: number | null;
|
|
409
|
+
/**
|
|
410
|
+
* 🚨 THE WRAP-UP IS THE AUTHORITATIVE POST-MEETING SET (D20).
|
|
411
|
+
*
|
|
412
|
+
* A live-notes window and the wrap-up both write decisions and action items,
|
|
413
|
+
* and on production 2026-09-09 meeting `9cb-39f8-782` carried the SAME
|
|
414
|
+
* decision twice — once from the note-taker at 04:43:57 and once from the
|
|
415
|
+
* wrap-up at 04:47:15 — and the record view listed both. A reader cannot tell
|
|
416
|
+
* a repeated decision from two decisions.
|
|
417
|
+
*
|
|
418
|
+
* So when the wrap-up lands, the server MARKS the window-written decisions
|
|
419
|
+
* and action items it supersedes (`metadata.superseded_by` = the wrap-up's
|
|
420
|
+
* run id) and never deletes them: the row is still the timeline of what was
|
|
421
|
+
* known at 04:43. This field carries that marker, and the record view and the
|
|
422
|
+
* assistant panel render only the rows where it is `null`.
|
|
423
|
+
*/
|
|
424
|
+
readonly supersededBy: string | null;
|
|
425
|
+
/**
|
|
426
|
+
* WHICH TRANSPORT DELIVERED THIS COPY (MRI-A12). The same durable row reaches
|
|
427
|
+
* a signed-in member twice — once as a `meet_notes` row over Postgres Changes
|
|
428
|
+
* (`"record"`) and once as a `matrx.meet.note` packet on the media plane
|
|
429
|
+
* (`"room"`), which is the ONLY copy a session-less guest can receive, because
|
|
430
|
+
* `anon` holds no grant on the table (MRI-A10).
|
|
431
|
+
*
|
|
432
|
+
* They carry the same id and the same words, so the store renders one note.
|
|
433
|
+
* Where they could ever disagree, `"record"` wins and `"room"` never overwrites
|
|
434
|
+
* it — the database is the record and the packet is the courtesy copy. That is
|
|
435
|
+
* the same precedence captions and segments already follow (D16).
|
|
436
|
+
*/
|
|
437
|
+
readonly source: "record" | "room";
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* THE WRAP-UP, ANNOUNCED (MRI-A12). The summary, decisions and action items
|
|
441
|
+
* arrive as ordinary notes; this says the pass that wrote them FINISHED, which
|
|
442
|
+
* is the difference between "no summary yet" and "no summary is coming".
|
|
443
|
+
*
|
|
444
|
+
* 🚨 It reaches a screen only while the room still exists. `POST /v1/meet/end`
|
|
445
|
+
* deletes the LiveKit room and the wrap-up runs afterwards, so on the ordinary
|
|
446
|
+
* ending path nobody is in the room to be told — the post-meeting record view is
|
|
447
|
+
* where the summary is read. A surface must therefore treat `null` as "not
|
|
448
|
+
* announced", never as "not written".
|
|
449
|
+
*/
|
|
450
|
+
interface WrapUpAnnouncement {
|
|
451
|
+
readonly meetingId: MeetingId;
|
|
452
|
+
/** The `meet_notes` ids the pass wrote, in the order it wrote them. */
|
|
453
|
+
readonly noteIds: readonly string[];
|
|
454
|
+
/** How many durable transcript segments it summarized. */
|
|
455
|
+
readonly segmentCount: number;
|
|
456
|
+
readonly at: string;
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* THE NOTE-TAKER'S STATE, as the meeting row records it
|
|
460
|
+
* (`meet_meetings.metadata.note_taker.state`, MRI-A2). Five values and no
|
|
461
|
+
* spinner: a surface renders the state it is in, or the server's refusal.
|
|
462
|
+
*
|
|
463
|
+
* - `absent` — nobody summoned it. The control offers to.
|
|
464
|
+
* - `joining` — dispatched; it has not entered the room yet.
|
|
465
|
+
* - `transcribing` — the server recorded active transcription and the exact
|
|
466
|
+
* deterministic actor is present in the room.
|
|
467
|
+
* - `handoff_pending` — a row that said transcribing no longer has that exact
|
|
468
|
+
* actor in the room. The next state and any missed audio are not inferred.
|
|
469
|
+
* - `ended` — the recorded note-taker stopped. The cause is not inferred.
|
|
470
|
+
*/
|
|
471
|
+
type NoteTakerState = "absent" | "joining" | "transcribing" | "handoff_pending" | "ended";
|
|
472
|
+
/** The lifecycle of a 1:1 or ad-hoc group call (R2). */
|
|
473
|
+
type CallState = "idle" | "dialing" | "ringing" | "connecting" | "active" | "declined" | "missed" | "busy" | "cancelled" | "ended" | "failed";
|
|
474
|
+
interface CallInvite {
|
|
475
|
+
readonly id: CallId;
|
|
476
|
+
readonly organizationId: OrganizationId;
|
|
477
|
+
readonly roomName: RoomName;
|
|
478
|
+
readonly mode: CallMode;
|
|
479
|
+
readonly callerUserId: UserId;
|
|
480
|
+
readonly callerName: string;
|
|
481
|
+
readonly callerAvatarUrl: string | null;
|
|
482
|
+
readonly calleeUserIds: readonly UserId[];
|
|
483
|
+
/** The durable conversation the invite was delivered into, if any. */
|
|
484
|
+
readonly conversationId: string | null;
|
|
485
|
+
readonly createdAt: string;
|
|
486
|
+
readonly expiresAt: string;
|
|
487
|
+
readonly state: CallState;
|
|
488
|
+
readonly declineMessage: string | null;
|
|
489
|
+
}
|
|
490
|
+
/** A person the UI can call. Deliberately minimal: hosts already have this. */
|
|
491
|
+
interface CallablePerson {
|
|
492
|
+
readonly userId: UserId;
|
|
493
|
+
readonly displayName: string;
|
|
494
|
+
readonly avatarUrl: string | null;
|
|
495
|
+
}
|
|
496
|
+
type StageLayout = "grid" | "speaker" | "sidebar";
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* THE ONE WAY THIS PACKAGE TALKS TO AIDREAM (MRI-A16).
|
|
500
|
+
*
|
|
501
|
+
* 🚨 **Every request to the aidream server carries `x-organization-id`, and the
|
|
502
|
+
* only way to guarantee "every" is for there to be ONE place that builds the
|
|
503
|
+
* request.** aidream's org-admission middleware refuses an authenticated
|
|
504
|
+
* request that does not name the organization it is acting in — 400
|
|
505
|
+
* `organization_required`, before the router is ever reached.
|
|
506
|
+
*
|
|
507
|
+
* 0.1.3 learned that lesson and put the header on the three meet callers by
|
|
508
|
+
* hand. That fixed the instances and left the CLASS wide open, and on
|
|
509
|
+
* 2026-09-09 the class collected its next victim: `<RecordingPlayer>` fetched
|
|
510
|
+
* `GET /files/{id}/download?inline=1` through a bare `fetch` with only a bearer
|
|
511
|
+
* token, so a perfectly landed 1.38 MB recording could not be played by the
|
|
512
|
+
* host who made it (MRI-C8; the identical URL returned `200 video/mp4` the
|
|
513
|
+
* moment the header was added by hand). A fourth caller was written months
|
|
514
|
+
* after the lesson and never heard it, because a lesson written into three call
|
|
515
|
+
* sites is not a lesson, it is three copies of a workaround.
|
|
516
|
+
*
|
|
517
|
+
* So: this module owns the base URL, the organization header and the bearer
|
|
518
|
+
* token, and `api.test.ts` holds a guard that FAILS THE BUILD if any other file
|
|
519
|
+
* in the package calls `fetch(` against a base URL or points an element `src=`
|
|
520
|
+
* at one. A new caller cannot be written without the header any more.
|
|
521
|
+
*
|
|
522
|
+
* 🚨 **HOW A `<video>` GETS ITS BYTES, given that signed URLs are eradicated.**
|
|
523
|
+
* A browser media element cannot attach a header, so a bare
|
|
524
|
+
* `src="https://server…/files/{id}/download"` carries no session and no
|
|
525
|
+
* organization and is refused — which is precisely what MRI-C8 saw. The
|
|
526
|
+
* platform's answer is NOT a signed URL (`aidream/api/routers/files` mints
|
|
527
|
+
* none, by law) but the FILE-SESSION COOKIE: `POST /files/session` mints an
|
|
528
|
+
* HttpOnly `SameSite=None` cookie that the auth middleware accepts on exactly
|
|
529
|
+
* the GET byte-serving routes (`COOKIE_AUTH_PATH_RE` in
|
|
530
|
+
* `matrx_connect/middleware/session_cookie.py`), and a cookie rides a media
|
|
531
|
+
* request that a header cannot. Every private `<img>`/`<video>` in the platform
|
|
532
|
+
* already renders this way.
|
|
533
|
+
*
|
|
534
|
+
* So `fileMedia` mints the cookie and hands back a STREAMING url — which is
|
|
535
|
+
* what lets a person scrub a two-hour recording instead of waiting for a
|
|
536
|
+
* gigabyte to arrive. When the mint is refused (no session, an older server,
|
|
537
|
+
* a browser blocking third-party cookies) it falls back to reading the bytes
|
|
538
|
+
* with the organization header into a `blob:` url — and it SAYS it did, with
|
|
539
|
+
* the reason, because a silent fallback that downloads a gigabyte is exactly
|
|
540
|
+
* the kind of automatic intervention that must announce itself.
|
|
541
|
+
*/
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* How the host tells us who is asking. Returns null before sign-in resolves,
|
|
545
|
+
* and for a guest, who has no session at all and is still a first-class joiner.
|
|
546
|
+
*
|
|
547
|
+
* It lives HERE rather than in `tokens.ts` (where it was born) because this is
|
|
548
|
+
* now the one module that turns a session into a request.
|
|
549
|
+
*/
|
|
550
|
+
type AccessTokenSource = () => Promise<string | null>;
|
|
551
|
+
/** The header aidream's org-admission middleware demands. One definition. */
|
|
552
|
+
declare const ORGANIZATION_HEADER = "x-organization-id";
|
|
553
|
+
interface MeetApiOptions {
|
|
554
|
+
/** aidream's origin. Never a LiveKit or provider URL. */
|
|
555
|
+
baseUrl: string;
|
|
556
|
+
/** Named on EVERY request; the server refuses one that omits it. */
|
|
557
|
+
organizationId: OrganizationId;
|
|
558
|
+
accessToken: AccessTokenSource;
|
|
559
|
+
fetchImpl?: typeof fetch | undefined;
|
|
560
|
+
}
|
|
561
|
+
interface MeetApiRequest {
|
|
562
|
+
/** An absolute path on the aidream origin, e.g. `/api/v1/meet/token`. */
|
|
563
|
+
path: string;
|
|
564
|
+
method?: "GET" | "POST" | undefined;
|
|
565
|
+
/** JSON body. Serialized here so no caller can forget the content type. */
|
|
566
|
+
body?: Record<string, unknown> | undefined;
|
|
567
|
+
/**
|
|
568
|
+
* `true` — refuse before the wire when there is no session, with the given
|
|
569
|
+
* operation named. `false` — send whatever session exists, or none (a guest
|
|
570
|
+
* minting a room token is a real, authorized case).
|
|
571
|
+
*/
|
|
572
|
+
requireAuth?: boolean | undefined;
|
|
573
|
+
/** Names the caller in every error this raises. */
|
|
574
|
+
operation: string;
|
|
575
|
+
/** Extra headers. May not override the organization header. */
|
|
576
|
+
headers?: Record<string, string> | undefined;
|
|
577
|
+
/**
|
|
578
|
+
* `"include"` for the ONE request that mints the file-session cookie — a
|
|
579
|
+
* `Set-Cookie` on a cross-origin response is dropped without it.
|
|
580
|
+
*/
|
|
581
|
+
credentials?: RequestCredentials | undefined;
|
|
582
|
+
/** Cancels the request. The Q&A stream is abortable by the person asking. */
|
|
583
|
+
signal?: AbortSignal | undefined;
|
|
584
|
+
}
|
|
585
|
+
interface MeetApi {
|
|
586
|
+
readonly baseUrl: string;
|
|
587
|
+
readonly organizationId: OrganizationId;
|
|
588
|
+
/** The URL a request to `path` would go to. For diagnostics, never for fetching. */
|
|
589
|
+
url(path: string): string;
|
|
590
|
+
/** One request, with the organization header and the session already on it. */
|
|
591
|
+
request(input: MeetApiRequest): Promise<Response>;
|
|
592
|
+
/**
|
|
593
|
+
* Read an authenticated file into a `blob:` URL an element can load. The
|
|
594
|
+
* caller MUST `URL.revokeObjectURL` it — a leaked one pins the whole file in
|
|
595
|
+
* memory for the life of the tab.
|
|
596
|
+
*/
|
|
597
|
+
fetchObjectUrl(input: MeetApiRequest): Promise<string>;
|
|
598
|
+
/**
|
|
599
|
+
* A url a `<video>`/`<img>` can load for a platform file. Streams when the
|
|
600
|
+
* file-session cookie can be minted; otherwise reads the bytes with the
|
|
601
|
+
* organization header and says why it had to.
|
|
602
|
+
*/
|
|
603
|
+
fileMedia(fileId: string, operation: string): Promise<MeetFileMedia>;
|
|
604
|
+
/** Re-mint the file-session cookie. Returns whether it now exists. */
|
|
605
|
+
refreshFileSession(): Promise<boolean>;
|
|
606
|
+
}
|
|
607
|
+
/** Where a media element's bytes are coming from, and whether that is plan A. */
|
|
608
|
+
interface MeetFileMedia {
|
|
609
|
+
readonly url: string;
|
|
610
|
+
/** `stream` seeks and starts instantly; `bytes` is a fully downloaded blob. */
|
|
611
|
+
readonly kind: "stream" | "bytes";
|
|
612
|
+
/**
|
|
613
|
+
* Set only for `bytes`, and never null-and-hoping: the sentence a surface
|
|
614
|
+
* shows to explain why it had to download the whole file first.
|
|
615
|
+
*/
|
|
616
|
+
readonly fallbackReason: string | null;
|
|
617
|
+
}
|
|
618
|
+
/** `POST /files/session` — mints the `mx_files_session` cookie (files law). */
|
|
619
|
+
declare const FILE_SESSION_PATH = "/files/session";
|
|
620
|
+
declare function createMeetApi(options: MeetApiOptions): MeetApi;
|
|
621
|
+
/** `GET /files/{id}/download?inline=1` — the platform files route (R3). */
|
|
622
|
+
declare function fileDownloadPath(fileId: string): string;
|
|
623
|
+
|
|
624
|
+
/**
|
|
625
|
+
* MEETING ROOM ITEMS — polls, Q&A, the shared whiteboard and breakout rooms (Meet wave 5).
|
|
626
|
+
*
|
|
627
|
+
* The server decides every one of them (`aidream/services/meet/collab.py`,
|
|
628
|
+
* `breakouts.py`): who may create, launch, vote, ask, answer, draw, erase, open rooms or
|
|
629
|
+
* move people — and what each viewer SEES (a draft poll is the host's alone; counts
|
|
630
|
+
* reach everyone only once shared; anonymous votes and questions carry no identity;
|
|
631
|
+
* a dismissed question stays with the hosts). This file only holds what the server
|
|
632
|
+
* said and folds the room's `matrx.meet.collab` packets into it.
|
|
633
|
+
*
|
|
634
|
+
* Two reads, one truth: `GET /v1/meet/collab` once per join (a late joiner sees every
|
|
635
|
+
* poll, question, stroke and the open breakout plan), then packets. Every item is keyed
|
|
636
|
+
* by its row id, so the same item arriving twice is one item.
|
|
637
|
+
*/
|
|
638
|
+
|
|
639
|
+
interface MeetPollOption {
|
|
640
|
+
readonly id: string;
|
|
641
|
+
readonly text: string;
|
|
642
|
+
/** Present when this viewer may see counts (a host, or results shared). */
|
|
643
|
+
readonly votes?: number | undefined;
|
|
644
|
+
/** Who picked it — hosts only, never for an anonymous poll. */
|
|
645
|
+
readonly voters?: readonly string[] | undefined;
|
|
646
|
+
}
|
|
647
|
+
type MeetPollState = "draft" | "live" | "closed";
|
|
648
|
+
interface MeetPoll {
|
|
649
|
+
readonly id: string;
|
|
650
|
+
readonly question: string;
|
|
651
|
+
readonly options: readonly MeetPollOption[];
|
|
652
|
+
readonly multiple: boolean;
|
|
653
|
+
readonly anonymous: boolean;
|
|
654
|
+
readonly state: MeetPollState;
|
|
655
|
+
readonly resultsShared: boolean;
|
|
656
|
+
readonly voterCount: number | null;
|
|
657
|
+
/** This viewer's own answer — kept locally, since a room packet cannot know it. */
|
|
658
|
+
readonly myVote: readonly string[];
|
|
659
|
+
readonly createdByName: string;
|
|
660
|
+
readonly createdAt: string | null;
|
|
661
|
+
}
|
|
662
|
+
type MeetQuestionState = "open" | "answered" | "dismissed";
|
|
663
|
+
interface MeetQuestion {
|
|
664
|
+
readonly id: string;
|
|
665
|
+
readonly text: string;
|
|
666
|
+
readonly askerName: string;
|
|
667
|
+
readonly anonymous: boolean;
|
|
668
|
+
readonly state: MeetQuestionState;
|
|
669
|
+
readonly answer: string | null;
|
|
670
|
+
readonly answeredByName: string | null;
|
|
671
|
+
readonly upvotes: number;
|
|
672
|
+
readonly upvotedByMe: boolean;
|
|
673
|
+
readonly mine: boolean;
|
|
674
|
+
readonly createdAt: string | null;
|
|
675
|
+
}
|
|
676
|
+
interface MeetStroke {
|
|
677
|
+
readonly id: string;
|
|
678
|
+
/** Points in the board's own 0..1 space. */
|
|
679
|
+
readonly points: readonly (readonly [number, number])[];
|
|
680
|
+
readonly color: string;
|
|
681
|
+
readonly width: number;
|
|
682
|
+
readonly tool: "pen" | "highlighter";
|
|
683
|
+
readonly by: string;
|
|
684
|
+
readonly byName: string;
|
|
685
|
+
/** True while this browser's stroke has not come back from the server. */
|
|
686
|
+
readonly pending?: boolean | undefined;
|
|
687
|
+
}
|
|
688
|
+
interface MeetBreakoutRoom {
|
|
689
|
+
readonly id: string;
|
|
690
|
+
readonly name: string;
|
|
691
|
+
readonly identities: readonly string[];
|
|
692
|
+
}
|
|
693
|
+
interface MeetBreakoutBroadcast {
|
|
694
|
+
readonly text: string;
|
|
695
|
+
readonly by: string;
|
|
696
|
+
readonly at: string;
|
|
697
|
+
}
|
|
698
|
+
interface MeetBreakoutPlan {
|
|
699
|
+
readonly id: string;
|
|
700
|
+
readonly state: "open" | "closed";
|
|
701
|
+
readonly rooms: readonly MeetBreakoutRoom[];
|
|
702
|
+
readonly allowChoose: boolean;
|
|
703
|
+
readonly minutes: number;
|
|
704
|
+
readonly openedAt: string | null;
|
|
705
|
+
readonly endsAt: string | null;
|
|
706
|
+
readonly closedAt: string | null;
|
|
707
|
+
readonly broadcasts: readonly MeetBreakoutBroadcast[];
|
|
708
|
+
}
|
|
709
|
+
/** The org's knobs for this meeting — which controls EXIST (absent, never dead). */
|
|
710
|
+
interface MeetCollabSettings {
|
|
711
|
+
readonly pollsEnabled: boolean;
|
|
712
|
+
readonly pollMaxOptions: number;
|
|
713
|
+
readonly qaEnabled: boolean;
|
|
714
|
+
readonly qaAnonymousAllowed: boolean;
|
|
715
|
+
readonly whiteboardEnabled: boolean;
|
|
716
|
+
readonly breakoutsEnabled: boolean;
|
|
717
|
+
readonly breakoutMaxRooms: number;
|
|
718
|
+
readonly breakoutDefaultMinutes: number;
|
|
719
|
+
readonly captionTranslationEnabled: boolean;
|
|
720
|
+
/** Empty = the viewer's own language. */
|
|
721
|
+
readonly captionTranslationLanguage: string;
|
|
722
|
+
}
|
|
723
|
+
type CollabPanel = "polls" | "qa" | "whiteboard" | "breakouts";
|
|
724
|
+
interface CollabSnapshot {
|
|
725
|
+
readonly loaded: boolean;
|
|
726
|
+
/** Why the state could not be read, in words — or null. */
|
|
727
|
+
readonly error: string | null;
|
|
728
|
+
readonly settings: MeetCollabSettings | null;
|
|
729
|
+
readonly polls: readonly MeetPoll[];
|
|
730
|
+
readonly questions: readonly MeetQuestion[];
|
|
731
|
+
readonly strokes: readonly MeetStroke[];
|
|
732
|
+
readonly breakout: MeetBreakoutPlan | null;
|
|
733
|
+
/** The newest message a host sent to every breakout room, for the banner. */
|
|
734
|
+
readonly broadcast: MeetBreakoutBroadcast | null;
|
|
735
|
+
/** A host moved THIS person (or everyone came back): where to go. `undefined` = nothing asked. */
|
|
736
|
+
readonly moveRequest: {
|
|
737
|
+
readonly roomId: string | null;
|
|
738
|
+
readonly at: number;
|
|
739
|
+
} | null;
|
|
740
|
+
/** Something asked a panel to open (a poll launched, a question arrived for a host). */
|
|
741
|
+
readonly panelRequest: {
|
|
742
|
+
readonly panel: CollabPanel;
|
|
743
|
+
readonly at: number;
|
|
744
|
+
} | null;
|
|
745
|
+
/** Caption translations for THIS viewer: line id → text, for `captionLanguage`. */
|
|
746
|
+
readonly captionLanguage: string | null;
|
|
747
|
+
readonly captionTranslations: Readonly<Record<string, string>>;
|
|
748
|
+
readonly captionTranslationError: string | null;
|
|
749
|
+
}
|
|
750
|
+
declare const EMPTY_COLLAB: CollabSnapshot;
|
|
751
|
+
type Row = Record<string, unknown>;
|
|
752
|
+
/** The server's `GET /collab` body → a loaded snapshot (keeps this viewer's caption state). */
|
|
753
|
+
declare function collabFromState(body: Row, previous: CollabSnapshot): CollabSnapshot;
|
|
754
|
+
/**
|
|
755
|
+
* Fold one server item into the snapshot. `fromRoom` = it came on the room broadcast,
|
|
756
|
+
* which cannot know THIS viewer's vote, upvote or authorship — those are kept from what
|
|
757
|
+
* the viewer's own reads and answers already said.
|
|
758
|
+
*/
|
|
759
|
+
declare function withItem(collab: CollabSnapshot, row: Row, fromRoom?: boolean): CollabSnapshot;
|
|
760
|
+
interface CollabPacketContext {
|
|
761
|
+
readonly localIdentity: ParticipantIdentity | null;
|
|
762
|
+
/** Hosts and co-hosts receive a second, fuller packet; they ignore the room copy. */
|
|
763
|
+
readonly isHost: boolean;
|
|
764
|
+
readonly now: number;
|
|
765
|
+
}
|
|
766
|
+
/**
|
|
767
|
+
* One `matrx.meet.collab` packet → the next snapshot, or `null` when it must be dropped
|
|
768
|
+
* (with the reason in `dropped`). Pure; the room-event dispatcher patches the store.
|
|
769
|
+
*/
|
|
770
|
+
declare function applyCollabPacket(collab: CollabSnapshot, payload: Row, context: CollabPacketContext): {
|
|
771
|
+
next: CollabSnapshot | null;
|
|
772
|
+
dropped?: string;
|
|
773
|
+
};
|
|
774
|
+
/** The breakout room a person is assigned to, or null. */
|
|
775
|
+
declare function assignedRoom(plan: MeetBreakoutPlan | null, identity: string | null): MeetBreakoutRoom | null;
|
|
776
|
+
/**
|
|
777
|
+
* Split people into `rooms` rooms, round-robin in the order given — Zoom's "assign
|
|
778
|
+
* automatically". Hosts are left out (they visit); pass them in `exclude`.
|
|
779
|
+
*/
|
|
780
|
+
declare function autoAssign(identities: readonly string[], rooms: number, exclude?: readonly string[]): string[][];
|
|
781
|
+
/** Milliseconds until the plan's timer ends, or null when it has none. */
|
|
782
|
+
declare function breakoutRemainingMs(plan: MeetBreakoutPlan | null, now: number): number | null;
|
|
783
|
+
/** The language translated captions start in: the knob, else the viewer's own. */
|
|
784
|
+
declare function defaultCaptionLanguage(settings: MeetCollabSettings | null, viewerLanguage: string | null | undefined): string;
|
|
785
|
+
/** Languages offered in the captions menu (tags the translator understands). */
|
|
786
|
+
declare const CAPTION_LANGUAGES: readonly {
|
|
787
|
+
readonly tag: string;
|
|
788
|
+
readonly name: string;
|
|
789
|
+
}[];
|
|
790
|
+
interface MeetingActivities {
|
|
791
|
+
/** Polls (with results), questions, whiteboard strokes — or null when refused. */
|
|
792
|
+
readonly collab: CollabSnapshot | null;
|
|
793
|
+
readonly refusal: {
|
|
794
|
+
readonly message: string;
|
|
795
|
+
readonly remedy: string;
|
|
796
|
+
} | null;
|
|
797
|
+
}
|
|
798
|
+
/**
|
|
799
|
+
* THE MEETING'S POLLS, Q&A AND WHITEBOARD, AFTER IT (Meet wave 5) — for the record page.
|
|
800
|
+
* The same `GET /v1/meet/collab` the room reads on join; a refusal is returned as data,
|
|
801
|
+
* so the record still renders everything else.
|
|
802
|
+
*/
|
|
803
|
+
declare function loadMeetingActivities(api: MeetApi, meetingId: string, roomToken?: string | null): Promise<MeetingActivities>;
|
|
804
|
+
|
|
805
|
+
export { type NoteTakerState as $, type AccessTokenSource as A, type MeetPollState as B, type CollabPanel as C, type MeetQuestion as D, EMPTY_COLLAB as E, FILE_SESSION_PATH as F, type MeetQuestionState as G, type MeetStroke as H, type InviteeInput as I, type JsonObject as J, type MeetingActivities as K, type LobbyEntry as L, type MeetingRecord as M, type MeetingAttendee as N, type OrganizationId as O, type ParticipantIdentity as P, type MeetingChanges as Q, type RoomName as R, type StageLayout as S, type TranscriptSource as T, type UserId as U, type MeetingInvitee as V, type WrapUpAnnouncement as W, type MeetingKind as X, type MeetingNoteKind as Y, type MeetingOccurrence as Z, type MeetingSettings as _, type MeetParticipant as a, ORGANIZATION_HEADER as a0, type OccurrenceOverride as a1, type OccurrenceState as a2, ROLE_RANK as a3, type RecordingPolicy as a4, type RecordingRecord as a5, type RecordingState as a6, type RoomPhase as a7, type RsvpAnswer as a8, type RsvpInvitation as a9, type RsvpState as aa, type TranscriptSegment as ab, type UpcomingOccurrence as ac, applyCollabPacket as ad, asCallId as ae, asMeetingId as af, asOrganizationId as ag, asParticipantIdentity as ah, asRoomName as ai, asUserId as aj, assignedRoom as ak, autoAssign as al, breakoutRemainingMs as am, collabFromState as an, createMeetApi as ao, defaultCaptionLanguage as ap, fileDownloadPath as aq, isGuest as ar, isHostRole as as, loadMeetingActivities as at, withItem as au, type RecordingSource as av, type MeetApi as b, type MeetIdentity as c, type CallInvite as d, type ParticipantRole as e, type CallMode as f, type MeetingNote as g, type MeetingId as h, type CollabSnapshot as i, type AdmissionState as j, CAPTION_LANGUAGES as k, type CallId as l, type CallState as m, type CallablePerson as n, type ConnectionQuality as o, type InviteeRole as p, type JsonValue as q, type MeetApiOptions as r, type MeetApiRequest as s, type MeetBreakoutBroadcast as t, type MeetBreakoutPlan as u, type MeetBreakoutRoom as v, type MeetCollabSettings as w, type MeetFileMedia as x, type MeetPoll as y, type MeetPollOption as z };
|