@ai-matrx/meet 0.7.6 → 0.7.8
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 +65 -0
- package/dist/index.cjs +671 -44
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +654 -319
- package/dist/index.d.ts +654 -319
- package/dist/index.js +671 -44
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +692 -69
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.js +691 -68
- package/dist/react.js.map +1 -1
- package/dist/styles.css +18 -0
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -605,6 +605,55 @@ declare function createMeetApi(options: MeetApiOptions): MeetApi;
|
|
|
605
605
|
/** `GET /files/{id}/download?inline=1` — the platform files route (R3). */
|
|
606
606
|
declare function fileDownloadPath(fileId: string): string;
|
|
607
607
|
|
|
608
|
+
/**
|
|
609
|
+
* ONE error classification for the whole package (C22).
|
|
610
|
+
*
|
|
611
|
+
* The banned alternative is a host catching a LiveKit or PostgREST error and
|
|
612
|
+
* deciding for itself whether the camera is busy, the token expired, or the
|
|
613
|
+
* meeting is locked — which is how the same five-branch `if` gets copy-pasted
|
|
614
|
+
* into every consumer and drifts. Everything this package throws is a
|
|
615
|
+
* `MeetError` with a stable `code` and a `remedy` SENTENCE. Nothing fails
|
|
616
|
+
* silently, and nothing fails without saying what to do about it.
|
|
617
|
+
*
|
|
618
|
+
* The device codes exist because "it doesn't work" in a video product is almost
|
|
619
|
+
* always one of four specific, differently-remediable things: permission
|
|
620
|
+
* denied, no such device, device already in use by another app, or insecure
|
|
621
|
+
* context (http, where `getUserMedia` does not exist at all). A single
|
|
622
|
+
* "media-error" would make the UI say the wrong sentence three times out of
|
|
623
|
+
* four.
|
|
624
|
+
*/
|
|
625
|
+
type MeetErrorCode = "session-unavailable" | "forbidden" | "not-found" | "conflict" | "token" | "transport" | "invalid-response" | "misconfigured" | "permission-denied" | "device-not-found" | "device-in-use" | "insecure-context" | "media-unsupported" | "room-locked" | "lobby-denied" | "capacity"
|
|
626
|
+
/** Meet wave 1: the meeting was cancelled — its link resolves, its room never opens. */
|
|
627
|
+
| "cancelled" | "unknown";
|
|
628
|
+
declare class MeetError extends Error {
|
|
629
|
+
readonly code: MeetErrorCode;
|
|
630
|
+
/** What to DO. Rendered to the user for the device codes. */
|
|
631
|
+
readonly remedy: string;
|
|
632
|
+
readonly cause?: unknown;
|
|
633
|
+
constructor(code: MeetErrorCode, message: string, remedy: string, cause?: unknown);
|
|
634
|
+
/** True when retrying after the host re-establishes a session is the fix. */
|
|
635
|
+
get isRetryable(): boolean;
|
|
636
|
+
/** True when the user, not the code, must act — the UI shows `remedy`. */
|
|
637
|
+
get isUserActionable(): boolean;
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* THE ONE PLACE a raw database/transport error becomes a typed one. Called at
|
|
641
|
+
* every repository boundary; nothing else in the package inspects a raw error,
|
|
642
|
+
* and no consumer should ever have to.
|
|
643
|
+
*/
|
|
644
|
+
declare function normalizeMeetError(error: unknown, operation: string): MeetError;
|
|
645
|
+
/**
|
|
646
|
+
* THE DEVICE ERROR TRANSLATOR — the second-most-copied block in every video app.
|
|
647
|
+
*
|
|
648
|
+
* `getUserMedia` rejects with DOMException names, and every one of them means
|
|
649
|
+
* something a user could fix if we told them. This is the whole reason the
|
|
650
|
+
* device layer lives in the package (C22 test 3: less-than-perfect unless done
|
|
651
|
+
* perfectly).
|
|
652
|
+
*/
|
|
653
|
+
declare function normalizeDeviceError(error: unknown, operation: string): MeetError;
|
|
654
|
+
/** A response whose SHAPE is wrong — the ingress boundary, not the wire. */
|
|
655
|
+
declare function invalidResponse(operation: string, detail: string): MeetError;
|
|
656
|
+
|
|
608
657
|
/**
|
|
609
658
|
* WHO SAID THAT — one answer, every surface, both lanes (MRI-A16).
|
|
610
659
|
*
|
|
@@ -666,55 +715,6 @@ declare function learnNames(book: NameBook, entries: readonly {
|
|
|
666
715
|
*/
|
|
667
716
|
declare function displayNameFor(book: NameBook, identity: ParticipantIdentity | string, preferred?: string | null | undefined): string;
|
|
668
717
|
|
|
669
|
-
/**
|
|
670
|
-
* ONE error classification for the whole package (C22).
|
|
671
|
-
*
|
|
672
|
-
* The banned alternative is a host catching a LiveKit or PostgREST error and
|
|
673
|
-
* deciding for itself whether the camera is busy, the token expired, or the
|
|
674
|
-
* meeting is locked — which is how the same five-branch `if` gets copy-pasted
|
|
675
|
-
* into every consumer and drifts. Everything this package throws is a
|
|
676
|
-
* `MeetError` with a stable `code` and a `remedy` SENTENCE. Nothing fails
|
|
677
|
-
* silently, and nothing fails without saying what to do about it.
|
|
678
|
-
*
|
|
679
|
-
* The device codes exist because "it doesn't work" in a video product is almost
|
|
680
|
-
* always one of four specific, differently-remediable things: permission
|
|
681
|
-
* denied, no such device, device already in use by another app, or insecure
|
|
682
|
-
* context (http, where `getUserMedia` does not exist at all). A single
|
|
683
|
-
* "media-error" would make the UI say the wrong sentence three times out of
|
|
684
|
-
* four.
|
|
685
|
-
*/
|
|
686
|
-
type MeetErrorCode = "session-unavailable" | "forbidden" | "not-found" | "conflict" | "token" | "transport" | "invalid-response" | "misconfigured" | "permission-denied" | "device-not-found" | "device-in-use" | "insecure-context" | "media-unsupported" | "room-locked" | "lobby-denied" | "capacity"
|
|
687
|
-
/** Meet wave 1: the meeting was cancelled — its link resolves, its room never opens. */
|
|
688
|
-
| "cancelled" | "unknown";
|
|
689
|
-
declare class MeetError extends Error {
|
|
690
|
-
readonly code: MeetErrorCode;
|
|
691
|
-
/** What to DO. Rendered to the user for the device codes. */
|
|
692
|
-
readonly remedy: string;
|
|
693
|
-
readonly cause?: unknown;
|
|
694
|
-
constructor(code: MeetErrorCode, message: string, remedy: string, cause?: unknown);
|
|
695
|
-
/** True when retrying after the host re-establishes a session is the fix. */
|
|
696
|
-
get isRetryable(): boolean;
|
|
697
|
-
/** True when the user, not the code, must act — the UI shows `remedy`. */
|
|
698
|
-
get isUserActionable(): boolean;
|
|
699
|
-
}
|
|
700
|
-
/**
|
|
701
|
-
* THE ONE PLACE a raw database/transport error becomes a typed one. Called at
|
|
702
|
-
* every repository boundary; nothing else in the package inspects a raw error,
|
|
703
|
-
* and no consumer should ever have to.
|
|
704
|
-
*/
|
|
705
|
-
declare function normalizeMeetError(error: unknown, operation: string): MeetError;
|
|
706
|
-
/**
|
|
707
|
-
* THE DEVICE ERROR TRANSLATOR — the second-most-copied block in every video app.
|
|
708
|
-
*
|
|
709
|
-
* `getUserMedia` rejects with DOMException names, and every one of them means
|
|
710
|
-
* something a user could fix if we told them. This is the whole reason the
|
|
711
|
-
* device layer lives in the package (C22 test 3: less-than-perfect unless done
|
|
712
|
-
* perfectly).
|
|
713
|
-
*/
|
|
714
|
-
declare function normalizeDeviceError(error: unknown, operation: string): MeetError;
|
|
715
|
-
/** A response whose SHAPE is wrong — the ingress boundary, not the wire. */
|
|
716
|
-
declare function invalidResponse(operation: string, detail: string): MeetError;
|
|
717
|
-
|
|
718
718
|
/**
|
|
719
719
|
* LISTEN-ONLY ATTENDANCE — being IN the room is never contingent on a device.
|
|
720
720
|
*
|
|
@@ -995,6 +995,17 @@ interface MeetSnapshot {
|
|
|
995
995
|
readonly failed: boolean;
|
|
996
996
|
readonly at: number;
|
|
997
997
|
} | null;
|
|
998
|
+
/**
|
|
999
|
+
* "Message privately" asked for from ANY per-person menu — a tile's, the
|
|
1000
|
+
* People panel's — and answered by the one `ControlBar` that owns the chat
|
|
1001
|
+
* panel (0.7.8). Through 0.7.7 only the People panel could open a private
|
|
1002
|
+
* chat, so the Board's tile menu (and the Room's) had no such item while the
|
|
1003
|
+
* People panel did: two menus for one person that disagreed.
|
|
1004
|
+
*/
|
|
1005
|
+
readonly privateChatRequest: {
|
|
1006
|
+
readonly to: ParticipantIdentity;
|
|
1007
|
+
readonly at: number;
|
|
1008
|
+
} | null;
|
|
998
1009
|
readonly effects: MediaEffects;
|
|
999
1010
|
/**
|
|
1000
1011
|
* Captions AND durable rows, reconciled into one list (D16). A record
|
|
@@ -1342,6 +1353,10 @@ declare const RPCS: {
|
|
|
1342
1353
|
readonly meetingOccurrences: "meet_meeting_occurrences";
|
|
1343
1354
|
/** Every occurrence the reader can see between two instants — the Upcoming list. */
|
|
1344
1355
|
readonly occurrencesBetween: "meet_occurrences_between";
|
|
1356
|
+
/** The recordings library. */
|
|
1357
|
+
readonly myRecordings: "meet_my_recordings";
|
|
1358
|
+
/** Titles, agendas, current notes and final transcript lines; snippets mark hits with ⟦ ⟧. */
|
|
1359
|
+
readonly search: "meet_search";
|
|
1345
1360
|
};
|
|
1346
1361
|
type Row = Record<string, unknown>;
|
|
1347
1362
|
interface CreateCallInviteInput {
|
|
@@ -1428,6 +1443,17 @@ interface MeetRepository {
|
|
|
1428
1443
|
acknowledgedAt: string;
|
|
1429
1444
|
}): Promise<void>;
|
|
1430
1445
|
recordings(meetingId: MeetingId): Promise<readonly RecordingRecord[]>;
|
|
1446
|
+
/** Meet wave 3: every recording of the reader's own meetings, newest first. */
|
|
1447
|
+
myRecordings(args?: {
|
|
1448
|
+
includeArchived?: boolean;
|
|
1449
|
+
limit?: number;
|
|
1450
|
+
}): Promise<readonly LibraryRecording[]>;
|
|
1451
|
+
/** Meet wave 3: search the reader's own meetings. */
|
|
1452
|
+
searchMeetings(query: string, limit?: number): Promise<readonly MeetingSearchHit[]>;
|
|
1453
|
+
/** Meet wave 3: name a recording (`null` = back to the meeting's title and date). */
|
|
1454
|
+
renameRecording(recordingId: string, title: string | null): Promise<void>;
|
|
1455
|
+
/** Meet wave 3: archive (soft delete) or restore a recording. Never a purge. */
|
|
1456
|
+
setRecordingArchived(recordingId: string, archived: boolean): Promise<void>;
|
|
1431
1457
|
scheduleMeeting(input: ScheduleMeetingInput): Promise<MeetingRecord>;
|
|
1432
1458
|
/** `expectedVersion` from the row you edited; a stale one is a `conflict`. */
|
|
1433
1459
|
updateMeeting(meetingId: MeetingId, changes: MeetingChanges, options?: {
|
|
@@ -1521,6 +1547,50 @@ declare function settingsPatch(settings: MeetingSettings): Record<string, unknow
|
|
|
1521
1547
|
*/
|
|
1522
1548
|
declare function changesPatch(changes: MeetingChanges): Record<string, unknown>;
|
|
1523
1549
|
declare function createMeetRepository(options: RepositoryOptions): MeetRepository;
|
|
1550
|
+
/** One row of the recordings library (`meet_my_recordings`). */
|
|
1551
|
+
interface LibraryRecording {
|
|
1552
|
+
readonly id: string;
|
|
1553
|
+
readonly meetingId: MeetingId;
|
|
1554
|
+
readonly meetingTitle: string;
|
|
1555
|
+
readonly meetingSlug: string;
|
|
1556
|
+
/** The reader's strongest role on the meeting. */
|
|
1557
|
+
readonly role: "host" | "cohost" | "invitee" | "attendee";
|
|
1558
|
+
readonly state: RecordingState;
|
|
1559
|
+
/** A name a host gave it; null = show the meeting title. */
|
|
1560
|
+
readonly title: string | null;
|
|
1561
|
+
readonly fileId: string | null;
|
|
1562
|
+
readonly startedAt: string | null;
|
|
1563
|
+
readonly endedAt: string | null;
|
|
1564
|
+
readonly durationMs: number | null;
|
|
1565
|
+
readonly sizeBytes: number | null;
|
|
1566
|
+
readonly failureReason: string | null;
|
|
1567
|
+
readonly archivedAt: string | null;
|
|
1568
|
+
readonly organizationId: OrganizationId;
|
|
1569
|
+
}
|
|
1570
|
+
declare function projectLibraryRecording(row: Row): LibraryRecording;
|
|
1571
|
+
/** Where a search hit was found. */
|
|
1572
|
+
type MeetingSearchSource = "title" | "summary" | "decision" | "action_item" | "note" | "transcript";
|
|
1573
|
+
/** One hit from `meet_search`. `snippet` marks matched words with ⟦ ⟧. */
|
|
1574
|
+
interface MeetingSearchHit {
|
|
1575
|
+
readonly meetingId: MeetingId;
|
|
1576
|
+
readonly meetingTitle: string;
|
|
1577
|
+
readonly meetingSlug: string;
|
|
1578
|
+
readonly meetingAt: string | null;
|
|
1579
|
+
readonly source: MeetingSearchSource;
|
|
1580
|
+
/** The note or transcript segment id (the meeting id for a title hit). */
|
|
1581
|
+
readonly refId: string;
|
|
1582
|
+
readonly speaker: string | null;
|
|
1583
|
+
readonly snippet: string;
|
|
1584
|
+
/** When it was said / written. */
|
|
1585
|
+
readonly at: string | null;
|
|
1586
|
+
readonly rank: number;
|
|
1587
|
+
}
|
|
1588
|
+
declare function projectSearchHit(row: Row): MeetingSearchHit;
|
|
1589
|
+
/** A snippet split into plain and matched runs, for highlighting without HTML. */
|
|
1590
|
+
declare function snippetRuns(snippet: string): readonly {
|
|
1591
|
+
readonly text: string;
|
|
1592
|
+
readonly hit: boolean;
|
|
1593
|
+
}[];
|
|
1524
1594
|
|
|
1525
1595
|
/**
|
|
1526
1596
|
* THE CALL CENTER — ringing, answering, and the six ways a call ends badly.
|
|
@@ -2266,6 +2336,15 @@ interface MeetingSessionOptions {
|
|
|
2266
2336
|
* to `engine.rejoin()`.
|
|
2267
2337
|
*/
|
|
2268
2338
|
onAdmitted?: (() => void) | undefined;
|
|
2339
|
+
/**
|
|
2340
|
+
* 🚨 THE HOST DENIED *US*. Called once per denial verdict that names this
|
|
2341
|
+
* knocker. Without it the waiting room kept promising "you will join
|
|
2342
|
+
* automatically" to a person the host had already turned away (production
|
|
2343
|
+
* 2026-09-27). The provider wires it to `engine.rejoin()`: the server then
|
|
2344
|
+
* refuses the mint with its own sentence ("The host did not admit you…"),
|
|
2345
|
+
* so the words on screen are the server's decision, never the client's guess.
|
|
2346
|
+
*/
|
|
2347
|
+
onDenied?: (() => void) | undefined;
|
|
2269
2348
|
}
|
|
2270
2349
|
interface MeetingSession {
|
|
2271
2350
|
/** Subscribe to the room's out-of-band plane. Hosts see knocks; guests see verdicts. */
|
|
@@ -2357,39 +2436,6 @@ declare function createRecordingController(options: RecordingControllerOptions):
|
|
|
2357
2436
|
/** The indicator's words. One definition, so no surface invents a softer one. */
|
|
2358
2437
|
declare function recordingLabel(state: RecordingState): string | null;
|
|
2359
2438
|
|
|
2360
|
-
/**
|
|
2361
|
-
* MEET WAVE 1 — the two server calls a management screen makes (0.8).
|
|
2362
|
-
*
|
|
2363
|
-
* Every WRITE (schedule, edit, reschedule, cancel, archive, occurrence change,
|
|
2364
|
-
* invite, answer) goes straight to the database through `MeetRepository` — the
|
|
2365
|
-
* database decides who may. What a browser cannot do is send an email with a
|
|
2366
|
-
* calendar object in it, so after any of those writes a screen calls
|
|
2367
|
-
* `announceMeeting`: the server tells each invitee exactly what they have not
|
|
2368
|
-
* been told (invitation, update or cancellation, each with its `.ics`), and
|
|
2369
|
-
* re-queues reminders. It is idempotent — call it after every save.
|
|
2370
|
-
*/
|
|
2371
|
-
|
|
2372
|
-
interface AnnounceResult {
|
|
2373
|
-
readonly invitations: number;
|
|
2374
|
-
readonly updates: number;
|
|
2375
|
-
readonly cancellations: number;
|
|
2376
|
-
readonly alreadyCurrent: number;
|
|
2377
|
-
readonly remindersQueued: number;
|
|
2378
|
-
readonly remindersCancelled: number;
|
|
2379
|
-
}
|
|
2380
|
-
/** Bring every invitee's inbox and calendar up to date. The host, a co-host or an editor. */
|
|
2381
|
-
declare function announceMeeting(api: MeetApi, meetingId: MeetingId): Promise<AnnounceResult>;
|
|
2382
|
-
/**
|
|
2383
|
-
* Put a one-off meeting on the caller's own connected Google Calendar. GATED by
|
|
2384
|
-
* the server to Google's review accounts while the Calendar scope is in review —
|
|
2385
|
-
* a refusal is a `forbidden` MeetError carrying the server's sentence. Everyone
|
|
2386
|
-
* already gets the meeting into any calendar through the invitation's `.ics`.
|
|
2387
|
-
*/
|
|
2388
|
-
declare function pushMeetingToGoogleCalendar(api: MeetApi, meetingId: MeetingId, target: {
|
|
2389
|
-
connectionId: string;
|
|
2390
|
-
calendarId?: string;
|
|
2391
|
-
}): Promise<Record<string, unknown>>;
|
|
2392
|
-
|
|
2393
2439
|
/**
|
|
2394
2440
|
* THE TOKEN FLOW — the ONLY authorization path into a room (R1).
|
|
2395
2441
|
*
|
|
@@ -2546,6 +2592,12 @@ declare const MEET_ROUTES: {
|
|
|
2546
2592
|
readonly meetingAnnounce: "/api/v1/meet/meetings/{meeting_id}/announce";
|
|
2547
2593
|
/** Put a one-off meeting on the caller's own Google Calendar (gated by the server). */
|
|
2548
2594
|
readonly meetingGoogleCalendar: "/api/v1/meet/meetings/{meeting_id}/google-calendar";
|
|
2595
|
+
/**
|
|
2596
|
+
* Meet wave 3: the recap email after the meeting. `GET` = the draft (subject,
|
|
2597
|
+
* body from the wrap-up, who it can go to); `POST` = send what the host
|
|
2598
|
+
* reviewed (`dry_run` renders and queues nothing). Never sent automatically.
|
|
2599
|
+
*/
|
|
2600
|
+
readonly meetingFollowUp: "/api/v1/meet/meetings/{meeting_id}/follow-up";
|
|
2549
2601
|
/**
|
|
2550
2602
|
* Meet wave 2: EVERY in-meeting host power (mute, stop video, mute all, ask to
|
|
2551
2603
|
* unmute, spotlight, lower hands, co-host, hand over, rename, remove, report).
|
|
@@ -2564,56 +2616,533 @@ declare function parseRoomToken(body: unknown, roomName: RoomName): RoomToken;
|
|
|
2564
2616
|
declare function createTokenClient(options: TokenClientOptions): TokenClient;
|
|
2565
2617
|
|
|
2566
2618
|
/**
|
|
2567
|
-
* THE
|
|
2619
|
+
* THE MEETING RECORD — what a meeting IS once the room is gone (MRI-D2).
|
|
2568
2620
|
*
|
|
2569
|
-
*
|
|
2570
|
-
*
|
|
2571
|
-
*
|
|
2572
|
-
*
|
|
2621
|
+
* 🚨 THE DEFECT THIS EXISTS TO KILL. Through 2026-09-08 a meeting produced a
|
|
2622
|
+
* wrap-up summary, decisions, owner-assigned action items, five windows of live
|
|
2623
|
+
* notes, thirty-two transcript segments and a recording — and then had NO
|
|
2624
|
+
* SURFACE AT ALL. `/meet/<slug>` fell back to `<PreJoin>` on an ended meeting,
|
|
2625
|
+
* so the durable link that R3 promises would outlive the meeting resolved to a
|
|
2626
|
+
* screen offering to join a room that no longer exists. Everything the campaign
|
|
2627
|
+
* built after the room closed was reachable only from SQL.
|
|
2573
2628
|
*
|
|
2574
|
-
*
|
|
2629
|
+
* Three rules hold this file together:
|
|
2575
2630
|
*
|
|
2576
|
-
* 1. **
|
|
2577
|
-
*
|
|
2578
|
-
*
|
|
2579
|
-
*
|
|
2580
|
-
*
|
|
2581
|
-
*
|
|
2582
|
-
*
|
|
2583
|
-
*
|
|
2584
|
-
*
|
|
2585
|
-
*
|
|
2586
|
-
*
|
|
2631
|
+
* 1. **Every read is INDEPENDENT and every refusal is NAMED.** The transcript,
|
|
2632
|
+
* the notes, the attendance list and the recording live in four RLS-guarded
|
|
2633
|
+
* tables, and a guest holding the link can read NONE of them (only
|
|
2634
|
+
* `meet_meeting_by_slug` is granted to `anon`). One `Promise.all` would turn
|
|
2635
|
+
* a permission boundary into a blank page. Known guest restrictions never issue private reads; each authenticated read that is refused
|
|
2636
|
+
* becomes a `MeetingRecordRestriction` with a sentence — the reader is told
|
|
2637
|
+
* what exists and why they cannot see it, which is the difference between a
|
|
2638
|
+
* record they can ask for and a product that looks broken.
|
|
2639
|
+
* 2. **A partial record is still a record.** A failed transcript read never
|
|
2640
|
+
* hides the summary somebody CAN read.
|
|
2641
|
+
* 3. **Nothing here computes meaning.** Grouping, ordering and formatting only.
|
|
2642
|
+
* The summary, decisions and action items are durable rows written by the
|
|
2643
|
+
* wrap-up mandate (D5/D18); this module never re-derives them from the
|
|
2644
|
+
* transcript, because two sources for one fact is how they start to disagree.
|
|
2587
2645
|
*/
|
|
2588
2646
|
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
readonly
|
|
2593
|
-
|
|
2594
|
-
readonly
|
|
2647
|
+
/** One thing the reader could not see, and the honest reason. Never a blank. */
|
|
2648
|
+
interface MeetingRecordRestriction {
|
|
2649
|
+
/** Which part of the record: `transcript`, `notes`, `attendance`, `recording`. */
|
|
2650
|
+
readonly part: "transcript" | "notes" | "attendance" | "recording";
|
|
2651
|
+
readonly message: string;
|
|
2652
|
+
readonly remedy: string;
|
|
2653
|
+
}
|
|
2654
|
+
/** One visible access state, made from restrictions that share one remedy. */
|
|
2655
|
+
interface MeetingRecordRestrictionNotice {
|
|
2656
|
+
readonly parts: readonly MeetingRecordRestriction["part"][];
|
|
2657
|
+
readonly message: string;
|
|
2658
|
+
readonly remedy: string;
|
|
2659
|
+
}
|
|
2660
|
+
/** A run of consecutive lines by one speaker — how a person reads a transcript. */
|
|
2661
|
+
interface TranscriptBlock {
|
|
2662
|
+
readonly id: string;
|
|
2663
|
+
/** Who spoke. Blocks break on THIS, never on the rendered name (MRI-A16). */
|
|
2664
|
+
readonly identity: ParticipantIdentity;
|
|
2665
|
+
readonly speaker: string;
|
|
2666
|
+
/** ISO of the first line in the block. */
|
|
2667
|
+
readonly startedAt: string;
|
|
2668
|
+
readonly lines: readonly TranscriptSegment[];
|
|
2669
|
+
}
|
|
2670
|
+
interface MeetingRecordBundle {
|
|
2671
|
+
readonly meeting: MeetingRecord;
|
|
2672
|
+
readonly attendees: readonly MeetingAttendee[];
|
|
2673
|
+
/** The wrap-up summary: `kind='note'` with `metadata.role='summary'` (D5). */
|
|
2674
|
+
readonly summary: MeetingNote | null;
|
|
2675
|
+
/** D20: the AUTHORITATIVE set — window rows the wrap-up superseded are out. */
|
|
2676
|
+
readonly decisions: readonly MeetingNote[];
|
|
2677
|
+
readonly actionItems: readonly MeetingNote[];
|
|
2678
|
+
/** The live-notes windows, in window order (D3). */
|
|
2679
|
+
readonly liveNotes: readonly MeetingNote[];
|
|
2680
|
+
readonly transcript: readonly TranscriptSegment[];
|
|
2681
|
+
/** The most recent recording attempt, whatever state it reached. */
|
|
2682
|
+
readonly recording: RecordingRecord | null;
|
|
2683
|
+
/** What this reader could NOT see. Rendered; never swallowed. */
|
|
2684
|
+
readonly restrictions: readonly MeetingRecordRestriction[];
|
|
2595
2685
|
/**
|
|
2596
|
-
*
|
|
2597
|
-
*
|
|
2598
|
-
*
|
|
2599
|
-
*
|
|
2686
|
+
* Identity → name, learned from everything this read COULD see: the durable
|
|
2687
|
+
* attendance rows and the transcript's own `speaker` column. It is what stops
|
|
2688
|
+
* a raw LiveKit token reaching the screen when one of those reads was refused
|
|
2689
|
+
* (MRI-A16).
|
|
2600
2690
|
*/
|
|
2601
|
-
readonly
|
|
2691
|
+
readonly names: NameBook;
|
|
2602
2692
|
}
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2693
|
+
/**
|
|
2694
|
+
* The current wrap-up summary. Repairs preserve the prior durable row for
|
|
2695
|
+
* history, then mark it `superseded_by`; every current-facing projection must
|
|
2696
|
+
* therefore make the same distinction as decisions and action items.
|
|
2697
|
+
*/
|
|
2698
|
+
declare function activeWrapUpSummary(notes: readonly MeetingNote[]): MeetingNote | null;
|
|
2699
|
+
/**
|
|
2700
|
+
* Load the record parts the reader may request; state all restrictions.
|
|
2701
|
+
*
|
|
2702
|
+
* `meeting` is passed in when the caller already resolved it (the slug route
|
|
2703
|
+
* does, through the `anon`-granted RPC) — re-reading it through the table would
|
|
2704
|
+
* fail for exactly the reader who most needs the page to work.
|
|
2705
|
+
*/
|
|
2706
|
+
declare function loadMeetingRecord(repository: MeetRepository, meetingId: MeetingId, meeting: MeetingRecord, reader?: Pick<MeetIdentity, "userId">): Promise<MeetingRecordBundle>;
|
|
2707
|
+
/**
|
|
2708
|
+
* 🚨 **A READ THAT WAS REFUSED IS NOT AN EMPTY RESULT (MRI-A16).**
|
|
2709
|
+
*
|
|
2710
|
+
* On production 2026-09-09 a guest's record view said *"No wrap-up was written
|
|
2711
|
+
* for this meeting"* directly underneath a panel that had just told them, in
|
|
2712
|
+
* the same screen, that the notes are not readable from their account — and
|
|
2713
|
+
* *"Nothing was recorded as a decision"* beside a decision that exists
|
|
2714
|
+
* (MRI-C8). The page asserted an absence it had no way to know, which is worse
|
|
2715
|
+
* than saying nothing: a person who reads "no wrap-up was written" stops
|
|
2716
|
+
* looking for one.
|
|
2717
|
+
*
|
|
2718
|
+
* So no surface writes its own empty sentence any more. It asks here, and gets
|
|
2719
|
+
* either its absence sentence — earned, because the read SUCCEEDED and returned
|
|
2720
|
+
* nothing — or the refusal, said plainly.
|
|
2721
|
+
*/
|
|
2722
|
+
declare const REFUSED_ABSENCE = "This record is not shared with guests.";
|
|
2723
|
+
/** Was this part of the record refused to this reader? */
|
|
2724
|
+
declare function wasRefused(bundle: MeetingRecordBundle, part: MeetingRecordRestriction["part"]): boolean;
|
|
2725
|
+
/**
|
|
2726
|
+
* The sentence to show where a part of the record has nothing in it. NEVER an
|
|
2727
|
+
* absence claim over a read that was refused.
|
|
2728
|
+
*/
|
|
2729
|
+
declare function absenceSentence(bundle: MeetingRecordBundle, part: MeetingRecordRestriction["part"], absence: string): string;
|
|
2730
|
+
/**
|
|
2731
|
+
* 🚨 **"NO WRAP-UP WAS WRITTEN" IS A CLAIM, AND FOR ~33 SECONDS IT WAS FALSE.**
|
|
2732
|
+
*
|
|
2733
|
+
* On production 2026-09-09 the HOST's record view said *"No wrap-up was written
|
|
2734
|
+
* for this meeting"* between 04:46:42 — the flip from the room to the record —
|
|
2735
|
+
* and 04:47:15, when the wrap-up landed. The read was permitted and genuinely
|
|
2736
|
+
* empty, so this is not the refusal class above; it is the OTHER way a screen
|
|
2737
|
+
* lies, by reading "not here yet" as "never happened". A person who reads *"no
|
|
2738
|
+
* wrap-up was written"* closes the tab.
|
|
2739
|
+
*
|
|
2740
|
+
* The meeting row already knows the difference, and it is not a guess: the
|
|
2741
|
+
* wrap-up is a durable job, `enqueue_wrap_up` stamps
|
|
2742
|
+
* `metadata.note_taker.intelligence.wrap_up` when it queues it, and the graph
|
|
2743
|
+
* action stamps `state: "failed"` when the pass refuses TERMINALLY. So:
|
|
2744
|
+
*
|
|
2745
|
+
* - a summary exists → `written`;
|
|
2746
|
+
* - the notes read was refused → `refused` (never an absence claim);
|
|
2747
|
+
* - the job is marked failed → `failed`, in the server's own words;
|
|
2748
|
+
* - the job is queued or running → `in-progress`, with a refresh affordance;
|
|
2749
|
+
* - nothing ever queued one → `absent`, which is the only EARNED absence.
|
|
2750
|
+
*
|
|
2751
|
+
* There is no timeout that flips `in-progress` into `absent`. A wrap-up that is
|
|
2752
|
+
* genuinely stuck says it is still being written — which is TRUE — rather than
|
|
2753
|
+
* inventing a verdict this browser has no way to reach.
|
|
2754
|
+
*/
|
|
2755
|
+
type WrapUpStatus = {
|
|
2756
|
+
readonly state: "written";
|
|
2757
|
+
} | {
|
|
2758
|
+
readonly state: "in-progress";
|
|
2759
|
+
readonly message: string;
|
|
2760
|
+
readonly canRefresh: true;
|
|
2761
|
+
} | {
|
|
2762
|
+
readonly state: "failed";
|
|
2763
|
+
readonly message: string;
|
|
2764
|
+
} | {
|
|
2765
|
+
readonly state: "refused";
|
|
2766
|
+
readonly message: string;
|
|
2767
|
+
} | {
|
|
2768
|
+
readonly state: "absent";
|
|
2769
|
+
readonly message: string;
|
|
2770
|
+
};
|
|
2771
|
+
/** Where the wrap-up actually is. Never "absent" over a job that is running. */
|
|
2772
|
+
declare function wrapUpStatus(bundle: MeetingRecordBundle): WrapUpStatus;
|
|
2773
|
+
/**
|
|
2774
|
+
* GROUP THE TRANSCRIPT THE WAY A PERSON READS IT: consecutive lines by one
|
|
2775
|
+
* speaker become one block with one timestamp. Reading thirty-two rows each
|
|
2776
|
+
* re-stamped with a name and a clock is how a transcript becomes unreadable.
|
|
2777
|
+
*/
|
|
2778
|
+
declare function groupTranscript(segments: readonly TranscriptSegment[], names?: NameBook): readonly TranscriptBlock[];
|
|
2779
|
+
/**
|
|
2780
|
+
* WHAT THE RECORDING IS DOING, in a sentence — including the two states people
|
|
2781
|
+
* hit most and understand least. `processing` is not "broken" and `failed` is
|
|
2782
|
+
* never silence: both say what happened and what to do.
|
|
2783
|
+
*/
|
|
2784
|
+
declare function recordingNotice(recording: RecordingRecord | null): string | null;
|
|
2785
|
+
/** Only an `available` recording has a file to play. Nothing else pretends to. */
|
|
2786
|
+
declare function playableRecordingFileId(recording: RecordingRecord | null): string | null;
|
|
2787
|
+
/**
|
|
2788
|
+
* A MEETING IS OVER WHEN THE SERVER SAYS SO. `ended_at` is stamped by
|
|
2789
|
+
* `POST /v1/meet/end` or by the auto-end evaluation (MRI-A6) — never by a
|
|
2790
|
+
* client noticing the room is empty.
|
|
2791
|
+
*/
|
|
2792
|
+
declare function hasEnded(meeting: MeetingRecord | null): boolean;
|
|
2793
|
+
/**
|
|
2794
|
+
* "Thursday, 8 September 2026, 4:21 PM · 19 minutes". One shape everywhere.
|
|
2795
|
+
*
|
|
2796
|
+
* THE SPAN IS `formatDurationMs`'s (2026-09-12). This body used to divide by
|
|
2797
|
+
* `60_000` and pluralise "minute" itself — a duration formatter living in a
|
|
2798
|
+
* PACKAGE, found the day `aidream/apps/shared` became a scan root. `@ai-matrx/
|
|
2799
|
+
* meet` already depends on `@ai-matrx/kit`, and `style: "long"` is precisely
|
|
2800
|
+
* this voice ("19 minutes", "1 minute"). Three things improve by adopting it:
|
|
2801
|
+
* a 40-second meeting read "0 minutes" and now reads "40 seconds" (the
|
|
2802
|
+
* confident zero the package refuses), a 19-minute-35-second meeting read
|
|
2803
|
+
* "20 minutes" and now reads "19 minutes" (prose asserts at-least, so it
|
|
2804
|
+
* floors), and a 90-minute meeting reads "1 hour" instead of "90 minutes".
|
|
2805
|
+
*/
|
|
2806
|
+
declare function meetingWhen(meeting: MeetingRecord, locale?: string): string;
|
|
2807
|
+
/** How much of a finished meeting a GUEST may read. Pinned with the server. */
|
|
2808
|
+
type GuestRecordAccess = "none" | "summary" | "full";
|
|
2809
|
+
/** Widest last. The ORDER is the comparison, exactly as it is on the server. */
|
|
2810
|
+
declare const GUEST_RECORD_ACCESS_LEVELS: readonly GuestRecordAccess[];
|
|
2811
|
+
/**
|
|
2812
|
+
* What a HOST may choose for one meeting. `default` is not a fourth level — it
|
|
2813
|
+
* is the absence of a per-meeting choice, and it is the only way to un-choose.
|
|
2814
|
+
*/
|
|
2815
|
+
type GuestRecordAccessSetting = "default" | GuestRecordAccess;
|
|
2816
|
+
declare const GUEST_RECORD_ACCESS_CHOICES: readonly GuestRecordAccessSetting[];
|
|
2817
|
+
/** `meet_meetings.metadata.guest_record_access`. No DDL; a setting on a row. */
|
|
2818
|
+
declare const GUEST_RECORD_ACCESS_METADATA_KEY = "guest_record_access";
|
|
2819
|
+
/** What a host chose for THIS meeting, read from the row they already have. */
|
|
2820
|
+
declare function guestRecordAccessSetting(meeting: MeetingRecord | null): GuestRecordAccessSetting;
|
|
2821
|
+
/** The sentence on the host's control. Plain English, never a token. */
|
|
2822
|
+
declare function guestRecordAccessLabel(setting: GuestRecordAccessSetting): string;
|
|
2823
|
+
/** What each choice actually means for the people who were in the room. */
|
|
2824
|
+
declare function guestRecordAccessExplanation(setting: GuestRecordAccessSetting): string;
|
|
2825
|
+
/** What came back from the server, so a surface never has to infer a level. */
|
|
2826
|
+
interface GuestRecordAccessResolution {
|
|
2827
|
+
readonly level: GuestRecordAccess;
|
|
2828
|
+
/** `meeting` when a host chose for this one; `organization` otherwise. */
|
|
2829
|
+
readonly source: "meeting" | "organization";
|
|
2830
|
+
}
|
|
2831
|
+
/**
|
|
2832
|
+
* Repeated policy refusals are one access state, not four alarming cards.
|
|
2833
|
+
*
|
|
2834
|
+
* The individual rows remain durable data for absence handling in their own
|
|
2835
|
+
* sections. This projection groups only rows with the exact same remedy, so a
|
|
2836
|
+
* distinct transport or integrity failure remains visible on its own.
|
|
2837
|
+
*/
|
|
2838
|
+
declare function summarizeMeetingRecordRestrictions(restrictions: readonly MeetingRecordRestriction[]): readonly MeetingRecordRestrictionNotice[];
|
|
2839
|
+
/**
|
|
2840
|
+
* Read the record the server shares with THIS guest.
|
|
2841
|
+
*
|
|
2842
|
+
* `roomToken` is the pass this device kept from the meeting
|
|
2843
|
+
* (`TokenClient.remembered`). It is presented in a header, never appended to
|
|
2844
|
+
* the URL. A guest with no pass, an expired pass, or a level of `none` gets the
|
|
2845
|
+
* server's refusal — as restrictions on a real bundle, so the screen says what
|
|
2846
|
+
* it cannot show instead of collapsing into an error page.
|
|
2847
|
+
*/
|
|
2848
|
+
declare function loadGuestMeetingRecord(args: {
|
|
2849
|
+
api: MeetApi;
|
|
2850
|
+
repository: MeetRepository;
|
|
2851
|
+
meetingId: MeetingId;
|
|
2852
|
+
meeting: MeetingRecord;
|
|
2853
|
+
roomToken: RoomToken | null;
|
|
2854
|
+
}): Promise<MeetingRecordBundle>;
|
|
2855
|
+
/**
|
|
2856
|
+
* A HOST chooses what guests may read of THIS meeting.
|
|
2857
|
+
*
|
|
2858
|
+
* Host power is the SERVER's decision and it re-checks it; this function exists
|
|
2859
|
+
* so the control has one call to make, and it returns what the choice RESOLVES
|
|
2860
|
+
* to so the screen never has to guess what "my organization's setting" means
|
|
2861
|
+
* today.
|
|
2862
|
+
*/
|
|
2863
|
+
declare function setGuestRecordAccess(args: {
|
|
2864
|
+
api: MeetApi;
|
|
2865
|
+
meetingId: MeetingId;
|
|
2866
|
+
organizationId: OrganizationId;
|
|
2867
|
+
access: GuestRecordAccessSetting;
|
|
2868
|
+
}): Promise<{
|
|
2869
|
+
readonly setting: GuestRecordAccessSetting;
|
|
2870
|
+
readonly effective: GuestRecordAccessResolution;
|
|
2871
|
+
}>;
|
|
2872
|
+
/** What a guest is told when the level shares nothing. Rendered, never hidden. */
|
|
2873
|
+
declare const GUEST_RECORD_NOT_SHARED: Readonly<Record<"recording" | "transcript" | "notes" | "attendance", string>>;
|
|
2874
|
+
|
|
2875
|
+
/**
|
|
2876
|
+
* AFTER THE MEETING (Meet wave 3) — the pure pieces every client needs once the
|
|
2877
|
+
* room is gone: the attendance report, the transcript and notes as documents,
|
|
2878
|
+
* a moment in the transcript as a moment in the recording, search within one
|
|
2879
|
+
* transcript, and the two server calls behind the recap email and the saved
|
|
2880
|
+
* chat.
|
|
2881
|
+
*
|
|
2882
|
+
* Nothing here computes MEANING (same rule as `record.ts`): the summary,
|
|
2883
|
+
* decisions and action items are the wrap-up's durable rows; this file only
|
|
2884
|
+
* arranges, measures and formats them. Every function is framework-free so a
|
|
2885
|
+
* worker, a test and a browser produce byte-identical exports.
|
|
2886
|
+
*/
|
|
2887
|
+
|
|
2888
|
+
/**
|
|
2889
|
+
* Who someone was, in the words an attendance report uses. `signed-in guest` is
|
|
2890
|
+
* an account from outside the meeting's organization that followed the link;
|
|
2891
|
+
* `guest` has no account at all.
|
|
2892
|
+
*/
|
|
2893
|
+
type AttendanceKind = "host" | "cohost" | "member" | "signed-in guest" | "guest" | "assistant";
|
|
2894
|
+
interface AttendanceRow {
|
|
2895
|
+
readonly id: string;
|
|
2896
|
+
readonly identity: string;
|
|
2897
|
+
readonly name: string;
|
|
2898
|
+
readonly userId: string | null;
|
|
2899
|
+
readonly kind: AttendanceKind;
|
|
2900
|
+
/** First time they were in the room; null when they knocked and never came in. */
|
|
2901
|
+
readonly joinedAt: string | null;
|
|
2902
|
+
/** When they left; null while still in the room or when the leave was never recorded. */
|
|
2903
|
+
readonly leftAt: string | null;
|
|
2904
|
+
/**
|
|
2905
|
+
* Time in the room. A missing `leftAt` is measured to the meeting's end —
|
|
2906
|
+
* the room closed around them — and `null` only when neither is known.
|
|
2907
|
+
*/
|
|
2908
|
+
readonly durationMs: number | null;
|
|
2909
|
+
/** True when `durationMs` was measured to the meeting's end, not a recorded leave. */
|
|
2910
|
+
readonly durationToEnd: boolean;
|
|
2911
|
+
readonly admission: MeetingAttendee["admissionState"];
|
|
2912
|
+
/** How many times this person came in (a rejoin or a second device). */
|
|
2913
|
+
readonly joins: number;
|
|
2914
|
+
}
|
|
2915
|
+
/**
|
|
2916
|
+
* The attendance report of ONE meeting, host first then by arrival. People who
|
|
2917
|
+
* knocked and were never let in are kept (with no join time) — "who asked to
|
|
2918
|
+
* come in" is part of the record — but a denied knock has no duration.
|
|
2919
|
+
*/
|
|
2920
|
+
/**
|
|
2921
|
+
* WHO a participant row is, as a PERSON (verifier, 2026-09-27: an export listed
|
|
2922
|
+
* the same attendee twice under "attended"). A signed-in person is their
|
|
2923
|
+
* account — every device and every rejoin is one person. A guest without an
|
|
2924
|
+
* account gets a fresh `guest:<random>` identity on every join, so the name
|
|
2925
|
+
* they typed is the only thread between their joins (Zoom's attendee report
|
|
2926
|
+
* merges link guests the same way); two different accountless guests who
|
|
2927
|
+
* typed the identical name read as one, which is the lesser error.
|
|
2928
|
+
*/
|
|
2929
|
+
declare function attendeeKey(person: MeetingAttendee, names?: NameBook): string;
|
|
2930
|
+
/**
|
|
2931
|
+
* One row per PERSON, in first-seen order: the row that actually joined (the
|
|
2932
|
+
* earliest) represents them. For a list of names or a head-count; the
|
|
2933
|
+
* attendance REPORT merges times as well (`attendanceReport`).
|
|
2934
|
+
*/
|
|
2935
|
+
declare function uniqueAttendees(attendees: readonly MeetingAttendee[], names?: NameBook): readonly MeetingAttendee[];
|
|
2936
|
+
declare function attendanceReport(meeting: MeetingRecord, attendees: readonly MeetingAttendee[], names?: NameBook): readonly AttendanceRow[];
|
|
2937
|
+
/** Headline numbers for the top of a report. The assistant is never a person. */
|
|
2938
|
+
declare function attendanceTotals(rows: readonly AttendanceRow[]): {
|
|
2939
|
+
readonly people: number;
|
|
2940
|
+
readonly guests: number;
|
|
2941
|
+
readonly neverAdmitted: number;
|
|
2942
|
+
readonly averageMs: number | null;
|
|
2943
|
+
};
|
|
2944
|
+
declare const ATTENDANCE_CSV_HEADER: readonly ["Meeting", "Meeting started", "Name", "Attended as", "Joined", "Left", "Minutes in the meeting", "Admission"];
|
|
2945
|
+
/**
|
|
2946
|
+
* CSV for a spreadsheet: one header row, one row per person, ISO timestamps (a
|
|
2947
|
+
* spreadsheet parses them; a formatted local clock loses the zone). Several
|
|
2948
|
+
* meetings — a series — concatenate with one header.
|
|
2949
|
+
*/
|
|
2950
|
+
declare function attendanceCsv(reports: readonly {
|
|
2951
|
+
meeting: MeetingRecord;
|
|
2952
|
+
rows: readonly AttendanceRow[];
|
|
2953
|
+
}[]): string;
|
|
2954
|
+
/**
|
|
2955
|
+
* Where an instant of the meeting falls in the RECORDING, in milliseconds from
|
|
2956
|
+
* its start — or `null` when the recording does not cover it (a recording is
|
|
2957
|
+
* often a few minutes of a longer meeting). Never a clamp: seeking a line that
|
|
2958
|
+
* was said before recording began to 0:00 would play the wrong words.
|
|
2959
|
+
*/
|
|
2960
|
+
declare function recordingOffsetMs(at: string, recording: RecordingRecord | null): number | null;
|
|
2961
|
+
/** `m:ss` or `h:mm:ss` — a position in a recording or a meeting. */
|
|
2962
|
+
declare function clockPosition(offsetMs: number): string;
|
|
2963
|
+
interface TranscriptMatch {
|
|
2964
|
+
readonly segmentId: string;
|
|
2965
|
+
/** [start, end) character ranges inside the segment's text, in order. */
|
|
2966
|
+
readonly ranges: readonly (readonly [number, number])[];
|
|
2967
|
+
}
|
|
2968
|
+
/**
|
|
2969
|
+
* Case-insensitive search of the transcript text AND the speaker's
|
|
2970
|
+
* name. Returns matches in transcript order. An empty query matches nothing.
|
|
2971
|
+
*/
|
|
2972
|
+
declare function searchTranscript(segments: readonly TranscriptSegment[], query: string, names?: NameBook): readonly TranscriptMatch[];
|
|
2973
|
+
type TranscriptFormat = "txt" | "md" | "vtt";
|
|
2974
|
+
interface ExportOptions {
|
|
2975
|
+
readonly names?: NameBook;
|
|
2976
|
+
/** Timestamps as a clock of day (default) or offsets from `zero`. */
|
|
2977
|
+
readonly locale?: string;
|
|
2978
|
+
readonly timeZone?: string;
|
|
2979
|
+
}
|
|
2980
|
+
/**
|
|
2981
|
+
* The transcript as a document. `txt` and `md` group consecutive lines by one
|
|
2982
|
+
* speaker with the clock of day; `vtt` is WebVTT cued from the recording's
|
|
2983
|
+
* start when a recording exists (so it plays as captions over it), otherwise
|
|
2984
|
+
* from the meeting's start — and a cue ends where the next line begins.
|
|
2985
|
+
*/
|
|
2986
|
+
declare function transcriptDocument(bundle: Pick<MeetingRecordBundle, "meeting" | "transcript" | "recording" | "names">, format: TranscriptFormat, options?: ExportOptions): string;
|
|
2987
|
+
/**
|
|
2988
|
+
* The meeting's notes as Markdown: when, who attended, the summary, decisions
|
|
2989
|
+
* and action items (with owners), and the link back. What a person pastes into
|
|
2990
|
+
* a doc or an email; also the source for PDF / Word export.
|
|
2991
|
+
*/
|
|
2992
|
+
declare function notesDocument(bundle: Pick<MeetingRecordBundle, "meeting" | "summary" | "decisions" | "actionItems" | "attendees" | "names">, options?: {
|
|
2993
|
+
readonly link?: string | null;
|
|
2994
|
+
readonly when?: string | null;
|
|
2995
|
+
}): string;
|
|
2996
|
+
/** How long the meeting ran, in words ("42 minutes"), or null when unknown. */
|
|
2997
|
+
declare function meetingLength(meeting: MeetingRecord): string | null;
|
|
2998
|
+
interface MeetingChatHistory {
|
|
2999
|
+
readonly messages: readonly RoomChatMessage[];
|
|
3000
|
+
/** The server's refusal, in its words, when the history could not be read. */
|
|
3001
|
+
readonly refusal: {
|
|
3002
|
+
readonly message: string;
|
|
3003
|
+
readonly remedy: string;
|
|
3004
|
+
} | null;
|
|
3005
|
+
}
|
|
3006
|
+
/**
|
|
3007
|
+
* The saved chat of a finished meeting (`GET /v1/meet/chat`). Private messages
|
|
3008
|
+
* are never saved, by design, so they are never here. A refusal is returned as
|
|
3009
|
+
* data — the record still renders everything else.
|
|
3010
|
+
*/
|
|
3011
|
+
declare function loadMeetingChat(api: MeetApi, meetingId: MeetingId, roomToken?: string | null): Promise<MeetingChatHistory>;
|
|
3012
|
+
/** One person the recap can go to, as the server resolved them. */
|
|
3013
|
+
interface FollowUpRecipient {
|
|
3014
|
+
/** Stable key for the choice: `user:<id>` or `email:<address>`. */
|
|
3015
|
+
readonly key: string;
|
|
3016
|
+
readonly name: string;
|
|
3017
|
+
/** The address, when this reader may see it (the host sees their guests'). */
|
|
3018
|
+
readonly email: string | null;
|
|
3019
|
+
readonly userId: string | null;
|
|
3020
|
+
/** `invitee`, `attendee` or both — where the server found them. */
|
|
3021
|
+
readonly sources: readonly ("invitee" | "attendee")[];
|
|
3022
|
+
/** False when the server has no way to reach them (no account, no address). */
|
|
3023
|
+
readonly reachable: boolean;
|
|
3024
|
+
}
|
|
3025
|
+
/** The draft the host reviews. The text is the wrap-up's own, arranged. */
|
|
3026
|
+
interface FollowUpDraft {
|
|
3027
|
+
readonly subject: string;
|
|
3028
|
+
readonly body: string;
|
|
3029
|
+
readonly recipients: readonly FollowUpRecipient[];
|
|
3030
|
+
/** False while the wrap-up has not been written — the draft says so. */
|
|
3031
|
+
readonly wrapUpWritten: boolean;
|
|
3032
|
+
/** When a recap for this meeting was last sent, and to how many. */
|
|
3033
|
+
readonly lastSent: {
|
|
3034
|
+
readonly at: string;
|
|
3035
|
+
readonly count: number;
|
|
3036
|
+
} | null;
|
|
3037
|
+
}
|
|
3038
|
+
interface FollowUpSendResult {
|
|
3039
|
+
readonly dryRun: boolean;
|
|
3040
|
+
/** Recipients the send queued (or, on a dry run, would queue). */
|
|
3041
|
+
readonly queued: readonly string[];
|
|
3042
|
+
/** Recipient keys skipped, each with the reason. */
|
|
3043
|
+
readonly skipped: readonly {
|
|
3044
|
+
readonly key: string;
|
|
3045
|
+
readonly reason: string;
|
|
3046
|
+
}[];
|
|
3047
|
+
}
|
|
3048
|
+
/** The recap draft for a finished meeting. The host, a co-host or an editor. */
|
|
3049
|
+
declare function loadFollowUpDraft(api: MeetApi, meetingId: MeetingId): Promise<FollowUpDraft>;
|
|
3050
|
+
/**
|
|
3051
|
+
* Send the recap the host reviewed. `dryRun` resolves every recipient and
|
|
3052
|
+
* renders every email but queues nothing — how a test proves the path without
|
|
3053
|
+
* mailing a real inbox. Never called automatically.
|
|
3054
|
+
*/
|
|
3055
|
+
declare function sendFollowUp(api: MeetApi, meetingId: MeetingId, input: {
|
|
3056
|
+
readonly subject: string;
|
|
3057
|
+
readonly body: string;
|
|
3058
|
+
readonly recipientKeys: readonly string[];
|
|
3059
|
+
readonly dryRun?: boolean;
|
|
3060
|
+
}): Promise<FollowUpSendResult>;
|
|
3061
|
+
|
|
3062
|
+
/**
|
|
3063
|
+
* MEET WAVE 1 — the two server calls a management screen makes (0.8).
|
|
3064
|
+
*
|
|
3065
|
+
* Every WRITE (schedule, edit, reschedule, cancel, archive, occurrence change,
|
|
3066
|
+
* invite, answer) goes straight to the database through `MeetRepository` — the
|
|
3067
|
+
* database decides who may. What a browser cannot do is send an email with a
|
|
3068
|
+
* calendar object in it, so after any of those writes a screen calls
|
|
3069
|
+
* `announceMeeting`: the server tells each invitee exactly what they have not
|
|
3070
|
+
* been told (invitation, update or cancellation, each with its `.ics`), and
|
|
3071
|
+
* re-queues reminders. It is idempotent — call it after every save.
|
|
3072
|
+
*/
|
|
3073
|
+
|
|
3074
|
+
interface AnnounceResult {
|
|
3075
|
+
readonly invitations: number;
|
|
3076
|
+
readonly updates: number;
|
|
3077
|
+
readonly cancellations: number;
|
|
3078
|
+
readonly alreadyCurrent: number;
|
|
3079
|
+
readonly remindersQueued: number;
|
|
3080
|
+
readonly remindersCancelled: number;
|
|
3081
|
+
}
|
|
3082
|
+
/** Bring every invitee's inbox and calendar up to date. The host, a co-host or an editor. */
|
|
3083
|
+
declare function announceMeeting(api: MeetApi, meetingId: MeetingId): Promise<AnnounceResult>;
|
|
3084
|
+
/**
|
|
3085
|
+
* Put a one-off meeting on the caller's own connected Google Calendar. GATED by
|
|
3086
|
+
* the server to Google's review accounts while the Calendar scope is in review —
|
|
3087
|
+
* a refusal is a `forbidden` MeetError carrying the server's sentence. Everyone
|
|
3088
|
+
* already gets the meeting into any calendar through the invitation's `.ics`.
|
|
3089
|
+
*/
|
|
3090
|
+
declare function pushMeetingToGoogleCalendar(api: MeetApi, meetingId: MeetingId, target: {
|
|
3091
|
+
connectionId: string;
|
|
3092
|
+
calendarId?: string;
|
|
3093
|
+
}): Promise<Record<string, unknown>>;
|
|
3094
|
+
|
|
3095
|
+
/**
|
|
3096
|
+
* THE ROOM ENGINE — the object a host mounts once and never reasons about.
|
|
3097
|
+
*
|
|
3098
|
+
* It owns every sequence a calling product gets wrong: ask for a token, connect,
|
|
3099
|
+
* project the roster, carry the in-room control plane, keep the phase honest
|
|
3100
|
+
* through a reconnect, and tear everything down exactly once. A consumer's job
|
|
3101
|
+
* is `engine.join(...)` and rendering a snapshot.
|
|
3102
|
+
*
|
|
3103
|
+
* THREE rules are structural here, not documented-and-hoped:
|
|
3104
|
+
*
|
|
3105
|
+
* 1. **The phase never lies.** `connecting` becomes `connected` only when the
|
|
3106
|
+
* driver says so, and any failure becomes `failed` WITH the remedy attached
|
|
3107
|
+
* to the snapshot. There is no path that leaves a spinner running forever —
|
|
3108
|
+
* the single most common complaint about every video product.
|
|
3109
|
+
* 2. **Role comes from the TOKEN, never from the client.** A participant's role
|
|
3110
|
+
* is read out of the server-minted metadata. A client that could decide it
|
|
3111
|
+
* was host could mute anyone; here the claim is unforgeable because the
|
|
3112
|
+
* server signed it.
|
|
3113
|
+
* 3. **Teardown is idempotent and total.** Leaving twice, or unmounting during
|
|
3114
|
+
* a connect, disconnects once and leaves no listener behind. The half-torn
|
|
3115
|
+
* room is what produces "my camera light stays on after the call".
|
|
3116
|
+
*/
|
|
3117
|
+
|
|
3118
|
+
interface JoinRequest {
|
|
3119
|
+
readonly roomName: RoomName;
|
|
3120
|
+
readonly meetingId?: string | undefined;
|
|
3121
|
+
readonly slug?: string | undefined;
|
|
3122
|
+
/** `audio` joins with the camera off. Same room, same code path (D3). */
|
|
3123
|
+
readonly mode: CallMode;
|
|
3124
|
+
/**
|
|
3125
|
+
* Whether to ATTEMPT the microphone on join. Defaults to true. `false` is a
|
|
3126
|
+
* deliberate listen-only join — the pre-join screen's "Join with microphone"
|
|
3127
|
+
* toggle. Either way the join itself never depends on the outcome; a refused
|
|
3128
|
+
* device lands in `snapshot.media.microphone.unavailable`, not in a throw.
|
|
3129
|
+
*/
|
|
3130
|
+
readonly microphone?: boolean | undefined;
|
|
3131
|
+
}
|
|
3132
|
+
interface RoomEngineOptions {
|
|
3133
|
+
store: MeetStore;
|
|
3134
|
+
tokens: TokenClient;
|
|
3135
|
+
identity: MeetIdentity;
|
|
3136
|
+
driverFactory: RoomDriverFactory;
|
|
3137
|
+
/** Canonical public metadata read; room packets alone never authorize teardown. */
|
|
3138
|
+
resolveMeeting?: ((slug: string) => Promise<MeetingRecord>) | undefined;
|
|
3139
|
+
onDiagnostic?: ((event: {
|
|
3140
|
+
level: "info" | "warn" | "error";
|
|
3141
|
+
message: string;
|
|
3142
|
+
remedy?: string;
|
|
3143
|
+
}) => void) | undefined;
|
|
3144
|
+
/** Fired when the local participant leaves for any reason, including kicked. */
|
|
3145
|
+
onLeft?: ((reason: string | null) => void) | undefined;
|
|
2617
3146
|
now?: (() => number) | undefined;
|
|
2618
3147
|
/** Remembered noise suppression / background choice (Meet wave 2). */
|
|
2619
3148
|
effectsStorage?: EffectsStorage | undefined;
|
|
@@ -2755,198 +3284,4 @@ interface InRoomClientOptions {
|
|
|
2755
3284
|
}
|
|
2756
3285
|
declare function createInRoomClient(options: InRoomClientOptions): InRoomClient;
|
|
2757
3286
|
|
|
2758
|
-
|
|
2759
|
-
* THE MEETING RECORD — what a meeting IS once the room is gone (MRI-D2).
|
|
2760
|
-
*
|
|
2761
|
-
* 🚨 THE DEFECT THIS EXISTS TO KILL. Through 2026-09-08 a meeting produced a
|
|
2762
|
-
* wrap-up summary, decisions, owner-assigned action items, five windows of live
|
|
2763
|
-
* notes, thirty-two transcript segments and a recording — and then had NO
|
|
2764
|
-
* SURFACE AT ALL. `/meet/<slug>` fell back to `<PreJoin>` on an ended meeting,
|
|
2765
|
-
* so the durable link that R3 promises would outlive the meeting resolved to a
|
|
2766
|
-
* screen offering to join a room that no longer exists. Everything the campaign
|
|
2767
|
-
* built after the room closed was reachable only from SQL.
|
|
2768
|
-
*
|
|
2769
|
-
* Three rules hold this file together:
|
|
2770
|
-
*
|
|
2771
|
-
* 1. **Every read is INDEPENDENT and every refusal is NAMED.** The transcript,
|
|
2772
|
-
* the notes, the attendance list and the recording live in four RLS-guarded
|
|
2773
|
-
* tables, and a guest holding the link can read NONE of them (only
|
|
2774
|
-
* `meet_meeting_by_slug` is granted to `anon`). One `Promise.all` would turn
|
|
2775
|
-
* a permission boundary into a blank page. Known guest restrictions never issue private reads; each authenticated read that is refused
|
|
2776
|
-
* becomes a `MeetingRecordRestriction` with a sentence — the reader is told
|
|
2777
|
-
* what exists and why they cannot see it, which is the difference between a
|
|
2778
|
-
* record they can ask for and a product that looks broken.
|
|
2779
|
-
* 2. **A partial record is still a record.** A failed transcript read never
|
|
2780
|
-
* hides the summary somebody CAN read.
|
|
2781
|
-
* 3. **Nothing here computes meaning.** Grouping, ordering and formatting only.
|
|
2782
|
-
* The summary, decisions and action items are durable rows written by the
|
|
2783
|
-
* wrap-up mandate (D5/D18); this module never re-derives them from the
|
|
2784
|
-
* transcript, because two sources for one fact is how they start to disagree.
|
|
2785
|
-
*/
|
|
2786
|
-
|
|
2787
|
-
/** One thing the reader could not see, and the honest reason. Never a blank. */
|
|
2788
|
-
interface MeetingRecordRestriction {
|
|
2789
|
-
/** Which part of the record: `transcript`, `notes`, `attendance`, `recording`. */
|
|
2790
|
-
readonly part: "transcript" | "notes" | "attendance" | "recording";
|
|
2791
|
-
readonly message: string;
|
|
2792
|
-
readonly remedy: string;
|
|
2793
|
-
}
|
|
2794
|
-
/** A run of consecutive lines by one speaker — how a person reads a transcript. */
|
|
2795
|
-
interface TranscriptBlock {
|
|
2796
|
-
readonly id: string;
|
|
2797
|
-
/** Who spoke. Blocks break on THIS, never on the rendered name (MRI-A16). */
|
|
2798
|
-
readonly identity: ParticipantIdentity;
|
|
2799
|
-
readonly speaker: string;
|
|
2800
|
-
/** ISO of the first line in the block. */
|
|
2801
|
-
readonly startedAt: string;
|
|
2802
|
-
readonly lines: readonly TranscriptSegment[];
|
|
2803
|
-
}
|
|
2804
|
-
interface MeetingRecordBundle {
|
|
2805
|
-
readonly meeting: MeetingRecord;
|
|
2806
|
-
readonly attendees: readonly MeetingAttendee[];
|
|
2807
|
-
/** The wrap-up summary: `kind='note'` with `metadata.role='summary'` (D5). */
|
|
2808
|
-
readonly summary: MeetingNote | null;
|
|
2809
|
-
/** D20: the AUTHORITATIVE set — window rows the wrap-up superseded are out. */
|
|
2810
|
-
readonly decisions: readonly MeetingNote[];
|
|
2811
|
-
readonly actionItems: readonly MeetingNote[];
|
|
2812
|
-
/** The live-notes windows, in window order (D3). */
|
|
2813
|
-
readonly liveNotes: readonly MeetingNote[];
|
|
2814
|
-
readonly transcript: readonly TranscriptSegment[];
|
|
2815
|
-
/** The most recent recording attempt, whatever state it reached. */
|
|
2816
|
-
readonly recording: RecordingRecord | null;
|
|
2817
|
-
/** What this reader could NOT see. Rendered; never swallowed. */
|
|
2818
|
-
readonly restrictions: readonly MeetingRecordRestriction[];
|
|
2819
|
-
/**
|
|
2820
|
-
* Identity → name, learned from everything this read COULD see: the durable
|
|
2821
|
-
* attendance rows and the transcript's own `speaker` column. It is what stops
|
|
2822
|
-
* a raw LiveKit token reaching the screen when one of those reads was refused
|
|
2823
|
-
* (MRI-A16).
|
|
2824
|
-
*/
|
|
2825
|
-
readonly names: NameBook;
|
|
2826
|
-
}
|
|
2827
|
-
/**
|
|
2828
|
-
* Load the record parts the reader may request; state all restrictions.
|
|
2829
|
-
*
|
|
2830
|
-
* `meeting` is passed in when the caller already resolved it (the slug route
|
|
2831
|
-
* does, through the `anon`-granted RPC) — re-reading it through the table would
|
|
2832
|
-
* fail for exactly the reader who most needs the page to work.
|
|
2833
|
-
*/
|
|
2834
|
-
declare function loadMeetingRecord(repository: MeetRepository, meetingId: MeetingId, meeting: MeetingRecord, reader?: Pick<MeetIdentity, "userId">): Promise<MeetingRecordBundle>;
|
|
2835
|
-
/**
|
|
2836
|
-
* 🚨 **A READ THAT WAS REFUSED IS NOT AN EMPTY RESULT (MRI-A16).**
|
|
2837
|
-
*
|
|
2838
|
-
* On production 2026-09-09 a guest's record view said *"No wrap-up was written
|
|
2839
|
-
* for this meeting"* directly underneath a panel that had just told them, in
|
|
2840
|
-
* the same screen, that the notes are not readable from their account — and
|
|
2841
|
-
* *"Nothing was recorded as a decision"* beside a decision that exists
|
|
2842
|
-
* (MRI-C8). The page asserted an absence it had no way to know, which is worse
|
|
2843
|
-
* than saying nothing: a person who reads "no wrap-up was written" stops
|
|
2844
|
-
* looking for one.
|
|
2845
|
-
*
|
|
2846
|
-
* So no surface writes its own empty sentence any more. It asks here, and gets
|
|
2847
|
-
* either its absence sentence — earned, because the read SUCCEEDED and returned
|
|
2848
|
-
* nothing — or the refusal, said plainly.
|
|
2849
|
-
*/
|
|
2850
|
-
declare const REFUSED_ABSENCE = "This record is not shared with guests.";
|
|
2851
|
-
/** Was this part of the record refused to this reader? */
|
|
2852
|
-
declare function wasRefused(bundle: MeetingRecordBundle, part: MeetingRecordRestriction["part"]): boolean;
|
|
2853
|
-
/**
|
|
2854
|
-
* The sentence to show where a part of the record has nothing in it. NEVER an
|
|
2855
|
-
* absence claim over a read that was refused.
|
|
2856
|
-
*/
|
|
2857
|
-
declare function absenceSentence(bundle: MeetingRecordBundle, part: MeetingRecordRestriction["part"], absence: string): string;
|
|
2858
|
-
/**
|
|
2859
|
-
* GROUP THE TRANSCRIPT THE WAY A PERSON READS IT: consecutive lines by one
|
|
2860
|
-
* speaker become one block with one timestamp. Reading thirty-two rows each
|
|
2861
|
-
* re-stamped with a name and a clock is how a transcript becomes unreadable.
|
|
2862
|
-
*/
|
|
2863
|
-
declare function groupTranscript(segments: readonly TranscriptSegment[], names?: NameBook): readonly TranscriptBlock[];
|
|
2864
|
-
/**
|
|
2865
|
-
* WHAT THE RECORDING IS DOING, in a sentence — including the two states people
|
|
2866
|
-
* hit most and understand least. `processing` is not "broken" and `failed` is
|
|
2867
|
-
* never silence: both say what happened and what to do.
|
|
2868
|
-
*/
|
|
2869
|
-
declare function recordingNotice(recording: RecordingRecord | null): string | null;
|
|
2870
|
-
/** Only an `available` recording has a file to play. Nothing else pretends to. */
|
|
2871
|
-
declare function playableRecordingFileId(recording: RecordingRecord | null): string | null;
|
|
2872
|
-
/**
|
|
2873
|
-
* A MEETING IS OVER WHEN THE SERVER SAYS SO. `ended_at` is stamped by
|
|
2874
|
-
* `POST /v1/meet/end` or by the auto-end evaluation (MRI-A6) — never by a
|
|
2875
|
-
* client noticing the room is empty.
|
|
2876
|
-
*/
|
|
2877
|
-
declare function hasEnded(meeting: MeetingRecord | null): boolean;
|
|
2878
|
-
/**
|
|
2879
|
-
* "Thursday, 8 September 2026, 4:21 PM · 19 minutes". One shape everywhere.
|
|
2880
|
-
*
|
|
2881
|
-
* THE SPAN IS `formatDurationMs`'s (2026-09-12). This body used to divide by
|
|
2882
|
-
* `60_000` and pluralise "minute" itself — a duration formatter living in a
|
|
2883
|
-
* PACKAGE, found the day `aidream/apps/shared` became a scan root. `@ai-matrx/
|
|
2884
|
-
* meet` already depends on `@ai-matrx/kit`, and `style: "long"` is precisely
|
|
2885
|
-
* this voice ("19 minutes", "1 minute"). Three things improve by adopting it:
|
|
2886
|
-
* a 40-second meeting read "0 minutes" and now reads "40 seconds" (the
|
|
2887
|
-
* confident zero the package refuses), a 19-minute-35-second meeting read
|
|
2888
|
-
* "20 minutes" and now reads "19 minutes" (prose asserts at-least, so it
|
|
2889
|
-
* floors), and a 90-minute meeting reads "1 hour" instead of "90 minutes".
|
|
2890
|
-
*/
|
|
2891
|
-
declare function meetingWhen(meeting: MeetingRecord, locale?: string): string;
|
|
2892
|
-
/** How much of a finished meeting a GUEST may read. Pinned with the server. */
|
|
2893
|
-
type GuestRecordAccess = "none" | "summary" | "full";
|
|
2894
|
-
/** Widest last. The ORDER is the comparison, exactly as it is on the server. */
|
|
2895
|
-
declare const GUEST_RECORD_ACCESS_LEVELS: readonly GuestRecordAccess[];
|
|
2896
|
-
/**
|
|
2897
|
-
* What a HOST may choose for one meeting. `default` is not a fourth level — it
|
|
2898
|
-
* is the absence of a per-meeting choice, and it is the only way to un-choose.
|
|
2899
|
-
*/
|
|
2900
|
-
type GuestRecordAccessSetting = "default" | GuestRecordAccess;
|
|
2901
|
-
declare const GUEST_RECORD_ACCESS_CHOICES: readonly GuestRecordAccessSetting[];
|
|
2902
|
-
/** `meet_meetings.metadata.guest_record_access`. No DDL; a setting on a row. */
|
|
2903
|
-
declare const GUEST_RECORD_ACCESS_METADATA_KEY = "guest_record_access";
|
|
2904
|
-
/** What a host chose for THIS meeting, read from the row they already have. */
|
|
2905
|
-
declare function guestRecordAccessSetting(meeting: MeetingRecord | null): GuestRecordAccessSetting;
|
|
2906
|
-
/** The sentence on the host's control. Plain English, never a token. */
|
|
2907
|
-
declare function guestRecordAccessLabel(setting: GuestRecordAccessSetting): string;
|
|
2908
|
-
/** What each choice actually means for the people who were in the room. */
|
|
2909
|
-
declare function guestRecordAccessExplanation(setting: GuestRecordAccessSetting): string;
|
|
2910
|
-
/** What came back from the server, so a surface never has to infer a level. */
|
|
2911
|
-
interface GuestRecordAccessResolution {
|
|
2912
|
-
readonly level: GuestRecordAccess;
|
|
2913
|
-
/** `meeting` when a host chose for this one; `organization` otherwise. */
|
|
2914
|
-
readonly source: "meeting" | "organization";
|
|
2915
|
-
}
|
|
2916
|
-
/**
|
|
2917
|
-
* Read the record the server shares with THIS guest.
|
|
2918
|
-
*
|
|
2919
|
-
* `roomToken` is the pass this device kept from the meeting
|
|
2920
|
-
* (`TokenClient.remembered`). It is presented in a header, never appended to
|
|
2921
|
-
* the URL. A guest with no pass, an expired pass, or a level of `none` gets the
|
|
2922
|
-
* server's refusal — as restrictions on a real bundle, so the screen says what
|
|
2923
|
-
* it cannot show instead of collapsing into an error page.
|
|
2924
|
-
*/
|
|
2925
|
-
declare function loadGuestMeetingRecord(args: {
|
|
2926
|
-
api: MeetApi;
|
|
2927
|
-
repository: MeetRepository;
|
|
2928
|
-
meetingId: MeetingId;
|
|
2929
|
-
meeting: MeetingRecord;
|
|
2930
|
-
roomToken: RoomToken | null;
|
|
2931
|
-
}): Promise<MeetingRecordBundle>;
|
|
2932
|
-
/**
|
|
2933
|
-
* A HOST chooses what guests may read of THIS meeting.
|
|
2934
|
-
*
|
|
2935
|
-
* Host power is the SERVER's decision and it re-checks it; this function exists
|
|
2936
|
-
* so the control has one call to make, and it returns what the choice RESOLVES
|
|
2937
|
-
* to so the screen never has to guess what "my organization's setting" means
|
|
2938
|
-
* today.
|
|
2939
|
-
*/
|
|
2940
|
-
declare function setGuestRecordAccess(args: {
|
|
2941
|
-
api: MeetApi;
|
|
2942
|
-
meetingId: MeetingId;
|
|
2943
|
-
organizationId: OrganizationId;
|
|
2944
|
-
access: GuestRecordAccessSetting;
|
|
2945
|
-
}): Promise<{
|
|
2946
|
-
readonly setting: GuestRecordAccessSetting;
|
|
2947
|
-
readonly effective: GuestRecordAccessResolution;
|
|
2948
|
-
}>;
|
|
2949
|
-
/** What a guest is told when the level shares nothing. Rendered, never hidden. */
|
|
2950
|
-
declare const GUEST_RECORD_NOT_SHARED: Readonly<Record<"recording" | "transcript" | "notes" | "attendance", string>>;
|
|
2951
|
-
|
|
2952
|
-
export { type AccessTokenSource, type AdmissionState, type AnnounceResult, type ApplyRoomEventArgs, type AskArgs, type AudioProcessing, CALL_INVITE_KIND, CALL_INVITE_VERSION, CONSENT_NOTICE_ATTRIBUTE, type CallCenter, type CallCenterOptions, type CallId, type CallInvite, type CallInviteHandlerOptions, type CallInvitePayload, type CallMode, type CallState, type CallablePerson, type ConnectionQuality, type CreateCallInviteInput, type CreateMeetingInput, DATA_TOPICS, DEFAULT_EFFECTS, DEFAULT_PAGE_SIZE, type DataTopic, type DeviceKind, type DeviceManager, type DeviceManagerOptions, type DeviceSnapshot, type DeviceStorage, type DriverConnectionState, type DriverParticipant, type DriverTrackKind, type DriverTranscription, END_MEETING_NOT_SERVED, type EffectsStorage, type EndMeetingResult, FILE_SESSION_PATH, GUEST_RECORD_ACCESS_CHOICES, GUEST_RECORD_ACCESS_LEVELS, GUEST_RECORD_ACCESS_METADATA_KEY, GUEST_RECORD_NOT_SHARED, type GuestRecordAccess, type GuestRecordAccessResolution, type GuestRecordAccessSetting, IDLE_MEDIA, IDLE_TRACK, type InRoomClient, type InRoomClientOptions, type InviteeInput, type InviteeRole, type JoinRequest, type JsonObject, type JsonValue, LISTEN_ONLY_NOTICE, type LiveKitDriverOptions, type LobbyEntry, type LocalMediaState, type LocalTrackState, MEETING_INVITE_KIND, MEETING_INVITE_VERSION, MEET_ANSWER_CONTEXT_EVENT, MEET_ANSWER_EVENTS, MEET_EVENTS, MEET_ROUTES, MEET_SCHEMA, MODERATION_ACTIONS, type MediaDevicesLike, type MediaEffects, type MediaUnavailable, type MeetAi, type MeetAiAnswer, type MeetAiOptions, type MeetAnswerCoverage, type MeetApi, type MeetApiOptions, type MeetApiRequest, type MeetDevice, MeetError, type MeetErrorCode, type MeetEvent, type MeetFileMedia, type MeetGrants, type MeetIdentity, type MeetParticipant, type MeetRepository, type MeetRoomDriver, type MeetSnapshot, type MeetStore, type MeetingAttendee, type MeetingChanges, type MeetingId, type MeetingInviteHandlerOptions, type MeetingInvitePayload, type MeetingInvitee, type MeetingKind, type MeetingNote, type MeetingNoteKind, type MeetingOccurrence, type MeetingRecord, type MeetingRecordBundle, type MeetingRecordRestriction, type MeetingSession, type MeetingSessionOptions, type MeetingSettings, type ModerationAction, type ModerationNotice, type ModerationRequest, type ModerationResult, NOTE_TAKER_IDENTITY_PREFIX, NOTE_TAKER_METADATA_KEY, NO_GRANTS, type NameBook, type NoteTakerDispatch, type NoteTakerRefusal, type NoteTakerState, type NoteTakerStatus, ORGANIZATION_HEADER, type OccurrenceOverride, type OccurrenceState, type OrganizationId, PARTICIPANT_CAN_RECORD_ATTRIBUTE, PARTICIPANT_ROLE_ATTRIBUTE, type ParticipantIdentity, type ParticipantRole, REACTION_TTL_MS, REFUSED_ABSENCE, ROLE_RANK, ROOM_EVENTS, ROOM_EVENT_PREFIX, ROOM_TOKEN_HEADER, RPCS, type RecordingController, type RecordingControllerOptions, type RecordingPolicy, type RecordingRecord, type RecordingState, type RepositoryOptions, type RoomChatMessage, type RoomDriverFactory, type RoomDriverHandlers, type RoomEngine, type RoomEngineOptions, type RoomEventOutcome, type RoomEventTopic, type RoomName, type RoomPhase, type RoomReaction, type RoomToken, type RoomTokenStorage, type RsvpAnswer, type RsvpInvitation, type RsvpState, type ScheduleMeetingInput, type Stage, type StageArgs, type StageLayout, TABLES, TRANSCRIPTION_ATTRIBUTES, TRANSCRIPTION_TOPIC, type TokenClient, type TokenClientOptions, type TokenRequest, type TranscriptBlock, type TranscriptSegment, type TranscriptSource, type UpcomingOccurrence, type UserId, type VideoEffect, type WrapUpAnnouncement, absenceSentence, announceMeeting, applyRoomEvent, asCallId, asMeetingId, asOrganizationId, asParticipantIdentity, asRoomName, asUserId, callInviteAction, callInviteText, callStateLabel, canModerate, canModerateParticipant, canTransition, captionSegment, changesPatch, computeStage, countLabel, createCallCenter, createCallInviteHandler, createDeviceManager, createInRoomClient, createLiveKitDriver, createMeetAi, createMeetApi, createMeetRepository, createMeetStore, createMeetingInviteHandler, createMeetingSession, createMemoryDeviceStorage, createRecordingController, createRoomEngine, createTokenClient, createWebDeviceStorage, createWebEffectsStorage, createWebRoomTokenStorage, displayNameFor, fileDownloadPath, gridColumns, groupTranscript, guestRecordAccessExplanation, guestRecordAccessLabel, guestRecordAccessSetting, hasEnded, hostUserIds, invalidResponse, isGuest, isGuestIdentity, isHostRole, isListenOnly, isNoteTaker, isRealName, isRoomEventTopic, learnName, learnNames, listenOnlyNotice, loadGuestMeetingRecord, loadMeetingRecord, mapConnectionState, mapQuality, meetingInviteAction, meetingLink, meetingPath, meetingSlug, meetingTopic, meetingWhen, mergeCaption, mergeRecord, normalizeDeviceError, normalizeMeetError, normalizeSpoken, noteTakerIdentity, noteTakerLabel, noteTakerParticipant, noteTakerStatus, orderParticipants, parseRoomToken, participantSummary, playableRecordingFileId, projectInvitee, projectNote, projectOccurrence, projectOverride, projectParticipant, projectRsvpInvitation, projectSegment, projectUpcoming, pushMeetingToGoogleCalendar, readServerRefusal, readableTranscript, recordingLabel, recordingNotice, roleOf, roomTopic, setGuestRecordAccess, settingsPatch, snapshotParticipant, unavailableFrom, userTopic, wasRefused };
|
|
3287
|
+
export { ATTENDANCE_CSV_HEADER, type AccessTokenSource, type AdmissionState, type AnnounceResult, type ApplyRoomEventArgs, type AskArgs, type AttendanceKind, type AttendanceRow, type AudioProcessing, CALL_INVITE_KIND, CALL_INVITE_VERSION, CONSENT_NOTICE_ATTRIBUTE, type CallCenter, type CallCenterOptions, type CallId, type CallInvite, type CallInviteHandlerOptions, type CallInvitePayload, type CallMode, type CallState, type CallablePerson, type ConnectionQuality, type CreateCallInviteInput, type CreateMeetingInput, DATA_TOPICS, DEFAULT_EFFECTS, DEFAULT_PAGE_SIZE, type DataTopic, type DeviceKind, type DeviceManager, type DeviceManagerOptions, type DeviceSnapshot, type DeviceStorage, type DriverConnectionState, type DriverParticipant, type DriverTrackKind, type DriverTranscription, END_MEETING_NOT_SERVED, type EffectsStorage, type EndMeetingResult, FILE_SESSION_PATH, type FollowUpDraft, type FollowUpRecipient, type FollowUpSendResult, GUEST_RECORD_ACCESS_CHOICES, GUEST_RECORD_ACCESS_LEVELS, GUEST_RECORD_ACCESS_METADATA_KEY, GUEST_RECORD_NOT_SHARED, type GuestRecordAccess, type GuestRecordAccessResolution, type GuestRecordAccessSetting, IDLE_MEDIA, IDLE_TRACK, type InRoomClient, type InRoomClientOptions, type InviteeInput, type InviteeRole, type JoinRequest, type JsonObject, type JsonValue, LISTEN_ONLY_NOTICE, type LibraryRecording, type LiveKitDriverOptions, type LobbyEntry, type LocalMediaState, type LocalTrackState, MEETING_INVITE_KIND, MEETING_INVITE_VERSION, MEET_ANSWER_CONTEXT_EVENT, MEET_ANSWER_EVENTS, MEET_EVENTS, MEET_ROUTES, MEET_SCHEMA, MODERATION_ACTIONS, type MediaDevicesLike, type MediaEffects, type MediaUnavailable, type MeetAi, type MeetAiAnswer, type MeetAiOptions, type MeetAnswerCoverage, type MeetApi, type MeetApiOptions, type MeetApiRequest, type MeetDevice, MeetError, type MeetErrorCode, type MeetEvent, type MeetFileMedia, type MeetGrants, type MeetIdentity, type MeetParticipant, type MeetRepository, type MeetRoomDriver, type MeetSnapshot, type MeetStore, type MeetingAttendee, type MeetingChanges, type MeetingChatHistory, type MeetingId, type MeetingInviteHandlerOptions, type MeetingInvitePayload, type MeetingInvitee, type MeetingKind, type MeetingNote, type MeetingNoteKind, type MeetingOccurrence, type MeetingRecord, type MeetingRecordBundle, type MeetingRecordRestriction, type MeetingRecordRestrictionNotice, type MeetingSearchHit, type MeetingSearchSource, type MeetingSession, type MeetingSessionOptions, type MeetingSettings, type ModerationAction, type ModerationNotice, type ModerationRequest, type ModerationResult, NOTE_TAKER_IDENTITY_PREFIX, NOTE_TAKER_METADATA_KEY, NO_GRANTS, type NameBook, type NoteTakerDispatch, type NoteTakerRefusal, type NoteTakerState, type NoteTakerStatus, ORGANIZATION_HEADER, type OccurrenceOverride, type OccurrenceState, type OrganizationId, PARTICIPANT_CAN_RECORD_ATTRIBUTE, PARTICIPANT_ROLE_ATTRIBUTE, type ParticipantIdentity, type ParticipantRole, REACTION_TTL_MS, REFUSED_ABSENCE, ROLE_RANK, ROOM_EVENTS, ROOM_EVENT_PREFIX, ROOM_TOKEN_HEADER, RPCS, type RecordingController, type RecordingControllerOptions, type RecordingPolicy, type RecordingRecord, type RecordingState, type RepositoryOptions, type RoomChatMessage, type RoomDriverFactory, type RoomDriverHandlers, type RoomEngine, type RoomEngineOptions, type RoomEventOutcome, type RoomEventTopic, type RoomName, type RoomPhase, type RoomReaction, type RoomToken, type RoomTokenStorage, type RsvpAnswer, type RsvpInvitation, type RsvpState, type ScheduleMeetingInput, type Stage, type StageArgs, type StageLayout, TABLES, TRANSCRIPTION_ATTRIBUTES, TRANSCRIPTION_TOPIC, type TokenClient, type TokenClientOptions, type TokenRequest, type TranscriptBlock, type TranscriptFormat, type TranscriptMatch, type TranscriptSegment, type TranscriptSource, type UpcomingOccurrence, type UserId, type VideoEffect, type WrapUpAnnouncement, type WrapUpStatus, absenceSentence, activeWrapUpSummary, announceMeeting, applyRoomEvent, asCallId, asMeetingId, asOrganizationId, asParticipantIdentity, asRoomName, asUserId, attendanceCsv, attendanceReport, attendanceTotals, attendeeKey, callInviteAction, callInviteText, callStateLabel, canModerate, canModerateParticipant, canTransition, captionSegment, changesPatch, clockPosition, computeStage, countLabel, createCallCenter, createCallInviteHandler, createDeviceManager, createInRoomClient, createLiveKitDriver, createMeetAi, createMeetApi, createMeetRepository, createMeetStore, createMeetingInviteHandler, createMeetingSession, createMemoryDeviceStorage, createRecordingController, createRoomEngine, createTokenClient, createWebDeviceStorage, createWebEffectsStorage, createWebRoomTokenStorage, displayNameFor, fileDownloadPath, gridColumns, groupTranscript, guestRecordAccessExplanation, guestRecordAccessLabel, guestRecordAccessSetting, hasEnded, hostUserIds, invalidResponse, isGuest, isGuestIdentity, isHostRole, isListenOnly, isNoteTaker, isRealName, isRoomEventTopic, learnName, learnNames, listenOnlyNotice, loadFollowUpDraft, loadGuestMeetingRecord, loadMeetingChat, loadMeetingRecord, mapConnectionState, mapQuality, meetingInviteAction, meetingLength, meetingLink, meetingPath, meetingSlug, meetingTopic, meetingWhen, mergeCaption, mergeRecord, normalizeDeviceError, normalizeMeetError, normalizeSpoken, noteTakerIdentity, noteTakerLabel, noteTakerParticipant, noteTakerStatus, notesDocument, orderParticipants, parseRoomToken, participantSummary, playableRecordingFileId, projectInvitee, projectLibraryRecording, projectNote, projectOccurrence, projectOverride, projectParticipant, projectRsvpInvitation, projectSearchHit, projectSegment, projectUpcoming, pushMeetingToGoogleCalendar, readServerRefusal, readableTranscript, recordingLabel, recordingNotice, recordingOffsetMs, roleOf, roomTopic, searchTranscript, sendFollowUp, setGuestRecordAccess, settingsPatch, snapshotParticipant, snippetRuns, summarizeMeetingRecordRestrictions, transcriptDocument, unavailableFrom, uniqueAttendees, userTopic, wasRefused, wrapUpStatus };
|