@ai-matrx/meet 0.7.5 → 0.7.7
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 +34 -0
- package/dist/index.cjs +569 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +540 -232
- package/dist/index.d.ts +540 -232
- package/dist/index.js +569 -20
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +543 -38
- 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 +543 -38
- package/dist/react.js.map +1 -1
- package/dist/styles.css +8 -0
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -192,6 +192,12 @@ var MEET_ROUTES = {
|
|
|
192
192
|
meetingAnnounce: "/api/v1/meet/meetings/{meeting_id}/announce",
|
|
193
193
|
/** Put a one-off meeting on the caller's own Google Calendar (gated by the server). */
|
|
194
194
|
meetingGoogleCalendar: "/api/v1/meet/meetings/{meeting_id}/google-calendar",
|
|
195
|
+
/**
|
|
196
|
+
* Meet wave 3: the recap email after the meeting. `GET` = the draft (subject,
|
|
197
|
+
* body from the wrap-up, who it can go to); `POST` = send what the host
|
|
198
|
+
* reviewed (`dry_run` renders and queues nothing). Never sent automatically.
|
|
199
|
+
*/
|
|
200
|
+
meetingFollowUp: "/api/v1/meet/meetings/{meeting_id}/follow-up",
|
|
195
201
|
/**
|
|
196
202
|
* Meet wave 2: EVERY in-meeting host power (mute, stop video, mute all, ask to
|
|
197
203
|
* unmute, spotlight, lower hands, co-host, hand over, rename, remove, report).
|
|
@@ -321,10 +327,11 @@ function createTokenClient(options) {
|
|
|
321
327
|
throw error;
|
|
322
328
|
}
|
|
323
329
|
if (response.status === 401 || response.status === 403) {
|
|
330
|
+
const said = await serverSentence(response);
|
|
324
331
|
throw new MeetError(
|
|
325
332
|
"forbidden",
|
|
326
|
-
|
|
327
|
-
"
|
|
333
|
+
said?.message ?? "You can't join this meeting.",
|
|
334
|
+
said?.remedy ?? "Ask the person who shared the link to let you in, then open the link again."
|
|
328
335
|
);
|
|
329
336
|
}
|
|
330
337
|
if (response.status === 404) {
|
|
@@ -335,12 +342,7 @@ function createTokenClient(options) {
|
|
|
335
342
|
);
|
|
336
343
|
}
|
|
337
344
|
if (response.status === 410) {
|
|
338
|
-
|
|
339
|
-
try {
|
|
340
|
-
const body = await response.json();
|
|
341
|
-
if (typeof body?.detail?.message === "string") reason = body.detail.message;
|
|
342
|
-
} catch {
|
|
343
|
-
}
|
|
345
|
+
const reason = (await serverSentence(response))?.message ?? "This meeting was cancelled.";
|
|
344
346
|
throw new MeetError(
|
|
345
347
|
"cancelled",
|
|
346
348
|
reason,
|
|
@@ -348,10 +350,11 @@ function createTokenClient(options) {
|
|
|
348
350
|
);
|
|
349
351
|
}
|
|
350
352
|
if (response.status === 423) {
|
|
353
|
+
const said = await serverSentence(response);
|
|
351
354
|
throw new MeetError(
|
|
352
355
|
"room-locked",
|
|
353
|
-
"
|
|
354
|
-
"The host locked the room. Ask them to unlock
|
|
356
|
+
said?.message ?? "The meeting is locked.",
|
|
357
|
+
said?.remedy ?? "The host locked the room. Ask them to unlock it, then join again."
|
|
355
358
|
);
|
|
356
359
|
}
|
|
357
360
|
if (response.status === 503) {
|
|
@@ -422,6 +425,19 @@ function placeholder(roomName) {
|
|
|
422
425
|
lobby: false
|
|
423
426
|
};
|
|
424
427
|
}
|
|
428
|
+
async function serverSentence(response) {
|
|
429
|
+
try {
|
|
430
|
+
const body = await response.json();
|
|
431
|
+
const source = body !== null && typeof body.detail === "object" && body.detail !== null ? body.detail : body;
|
|
432
|
+
if (source === null) return null;
|
|
433
|
+
const message = typeof source.user_message === "string" && source.user_message.trim() !== "" ? source.user_message : typeof source.message === "string" && source.message.trim() !== "" ? source.message : null;
|
|
434
|
+
if (message === null) return null;
|
|
435
|
+
const remedy = typeof source.remedy === "string" && source.remedy.trim() !== "" ? source.remedy : void 0;
|
|
436
|
+
return { message, remedy };
|
|
437
|
+
} catch {
|
|
438
|
+
return null;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
425
441
|
|
|
426
442
|
// src/core/ai.ts
|
|
427
443
|
var MEET_ANSWER_EVENTS = {
|
|
@@ -890,7 +906,7 @@ var DEFAULT_RING_TIMEOUT_MS = 45e3;
|
|
|
890
906
|
function createCallCenter(options) {
|
|
891
907
|
const { manager, repository, identity } = options;
|
|
892
908
|
const timers = options.timers ?? {
|
|
893
|
-
setTimeout: (run,
|
|
909
|
+
setTimeout: (run, ms2) => setTimeout(run, ms2),
|
|
894
910
|
clearTimeout: (handle) => clearTimeout(handle),
|
|
895
911
|
now: () => Date.now()
|
|
896
912
|
};
|
|
@@ -1174,7 +1190,16 @@ var RPCS = {
|
|
|
1174
1190
|
/** One meeting's occurrences between two instants (INVOKER — the reader's RLS). */
|
|
1175
1191
|
meetingOccurrences: "meet_meeting_occurrences",
|
|
1176
1192
|
/** Every occurrence the reader can see between two instants — the Upcoming list. */
|
|
1177
|
-
occurrencesBetween: "meet_occurrences_between"
|
|
1193
|
+
occurrencesBetween: "meet_occurrences_between",
|
|
1194
|
+
/*
|
|
1195
|
+
* ── MEET WAVE 3 — after the meeting. Both INVOKER (the reader's RLS) and both
|
|
1196
|
+
* scoped to the reader's OWN meetings (host, invitee, attended) — RLS is the
|
|
1197
|
+
* ceiling, never the view.
|
|
1198
|
+
*/
|
|
1199
|
+
/** The recordings library. */
|
|
1200
|
+
myRecordings: "meet_my_recordings",
|
|
1201
|
+
/** Titles, agendas, current notes and final transcript lines; snippets mark hits with ⟦ ⟧. */
|
|
1202
|
+
search: "meet_search"
|
|
1178
1203
|
};
|
|
1179
1204
|
var VOID_RPCS = [RPCS.admit, RPCS.recordConsent];
|
|
1180
1205
|
var MEETING_COLUMNS = "id,organization_id,room_name,slug,title,kind,host_user_id,scheduled_for,scheduled_duration_minutes,started_at,ended_at,locked,lobby_enabled,recording_policy,ai_enabled,metadata,time_zone,agenda,recurrence_rule,join_before_host,cancelled_at,cancellation_reason,deleted_at,calendar_sequence,version";
|
|
@@ -1879,12 +1904,117 @@ function createMeetRepository(options) {
|
|
|
1879
1904
|
);
|
|
1880
1905
|
},
|
|
1881
1906
|
async recordings(meetingId) {
|
|
1882
|
-
const response = await db.from(TABLES.recordings).select(RECORDING_COLUMNS).eq("meeting_id", meetingId).order("started_at", { ascending: false });
|
|
1907
|
+
const response = await db.from(TABLES.recordings).select(RECORDING_COLUMNS).eq("meeting_id", meetingId).is("deleted_at", null).order("started_at", { ascending: false });
|
|
1883
1908
|
if (response.error !== null) throw normalizeMeetError(response.error, "meet.recordings");
|
|
1884
1909
|
return (response.data ?? []).map(projectRecording);
|
|
1910
|
+
},
|
|
1911
|
+
async myRecordings({ includeArchived = false, limit = 200 } = {}) {
|
|
1912
|
+
const rows = await guard(
|
|
1913
|
+
"meet.myRecordings",
|
|
1914
|
+
() => db.rpc(RPCS.myRecordings, { p_include_archived: includeArchived, p_limit: limit })
|
|
1915
|
+
);
|
|
1916
|
+
return rows.map(projectLibraryRecording);
|
|
1917
|
+
},
|
|
1918
|
+
async searchMeetings(query, limit = 60) {
|
|
1919
|
+
if (query.trim().length === 0) return [];
|
|
1920
|
+
const rows = await guard(
|
|
1921
|
+
"meet.searchMeetings",
|
|
1922
|
+
() => db.rpc(RPCS.search, { p_query: query, p_limit: limit })
|
|
1923
|
+
);
|
|
1924
|
+
return rows.map(projectSearchHit);
|
|
1925
|
+
},
|
|
1926
|
+
async renameRecording(recordingId, title) {
|
|
1927
|
+
const clean = title === null ? null : title.trim().slice(0, 200) || null;
|
|
1928
|
+
const response = await db.from(TABLES.recordings).update({ title: clean }).eq("id", recordingId).select("id");
|
|
1929
|
+
if (response.error !== null) throw normalizeMeetError(response.error, "meet.renameRecording");
|
|
1930
|
+
if ((response.data ?? []).length === 0) {
|
|
1931
|
+
throw normalizeMeetError(
|
|
1932
|
+
{ message: "permission denied: only the meeting's host or an editor can rename it", code: "42501" },
|
|
1933
|
+
"meet.renameRecording"
|
|
1934
|
+
);
|
|
1935
|
+
}
|
|
1936
|
+
},
|
|
1937
|
+
async setRecordingArchived(recordingId, archived) {
|
|
1938
|
+
const response = await db.from(TABLES.recordings).update({ deleted_at: archived ? (/* @__PURE__ */ new Date()).toISOString() : null }).eq("id", recordingId).select("id");
|
|
1939
|
+
if (response.error !== null) throw normalizeMeetError(response.error, "meet.setRecordingArchived");
|
|
1940
|
+
if ((response.data ?? []).length === 0) {
|
|
1941
|
+
throw normalizeMeetError(
|
|
1942
|
+
{ message: "permission denied: only the meeting's host or an editor can archive it", code: "42501" },
|
|
1943
|
+
"meet.setRecordingArchived"
|
|
1944
|
+
);
|
|
1945
|
+
}
|
|
1885
1946
|
}
|
|
1886
1947
|
};
|
|
1887
1948
|
}
|
|
1949
|
+
var LIBRARY_STATES = [
|
|
1950
|
+
"idle",
|
|
1951
|
+
"starting",
|
|
1952
|
+
"recording",
|
|
1953
|
+
"stopping",
|
|
1954
|
+
"processing",
|
|
1955
|
+
"available",
|
|
1956
|
+
"expired",
|
|
1957
|
+
"failed"
|
|
1958
|
+
];
|
|
1959
|
+
function projectLibraryRecording(row) {
|
|
1960
|
+
const operation = "meet.projectLibraryRecording";
|
|
1961
|
+
const state = text(row, "state");
|
|
1962
|
+
const role = text(row, "my_role");
|
|
1963
|
+
return {
|
|
1964
|
+
id: requireText(row, "recording_id", operation),
|
|
1965
|
+
meetingId: asMeetingId(requireText(row, "meeting_id", operation)),
|
|
1966
|
+
meetingTitle: text(row, "meeting_title") ?? "Meeting",
|
|
1967
|
+
meetingSlug: requireText(row, "meeting_slug", operation),
|
|
1968
|
+
role: role === "host" || role === "cohost" || role === "invitee" ? role : "attendee",
|
|
1969
|
+
state: LIBRARY_STATES.includes(state) ? state : "failed",
|
|
1970
|
+
title: text(row, "recording_title"),
|
|
1971
|
+
fileId: text(row, "file_id"),
|
|
1972
|
+
startedAt: text(row, "started_at"),
|
|
1973
|
+
endedAt: text(row, "ended_at"),
|
|
1974
|
+
durationMs: num(row, "duration_ms"),
|
|
1975
|
+
sizeBytes: num(row, "size_bytes"),
|
|
1976
|
+
failureReason: text(row, "failure_reason"),
|
|
1977
|
+
archivedAt: text(row, "deleted_at"),
|
|
1978
|
+
organizationId: asOrganizationId(requireText(row, "organization_id", operation))
|
|
1979
|
+
};
|
|
1980
|
+
}
|
|
1981
|
+
var SEARCH_SOURCES = [
|
|
1982
|
+
"title",
|
|
1983
|
+
"summary",
|
|
1984
|
+
"decision",
|
|
1985
|
+
"action_item",
|
|
1986
|
+
"note",
|
|
1987
|
+
"transcript"
|
|
1988
|
+
];
|
|
1989
|
+
function projectSearchHit(row) {
|
|
1990
|
+
const operation = "meet.projectSearchHit";
|
|
1991
|
+
const source = text(row, "source");
|
|
1992
|
+
return {
|
|
1993
|
+
meetingId: asMeetingId(requireText(row, "meeting_id", operation)),
|
|
1994
|
+
meetingTitle: text(row, "meeting_title") ?? "Meeting",
|
|
1995
|
+
meetingSlug: requireText(row, "meeting_slug", operation),
|
|
1996
|
+
meetingAt: text(row, "meeting_at"),
|
|
1997
|
+
source: SEARCH_SOURCES.includes(source) ? source : "note",
|
|
1998
|
+
refId: requireText(row, "ref_id", operation),
|
|
1999
|
+
speaker: text(row, "speaker"),
|
|
2000
|
+
snippet: text(row, "snippet") ?? "",
|
|
2001
|
+
at: text(row, "said_at"),
|
|
2002
|
+
rank: num(row, "rank") ?? 0
|
|
2003
|
+
};
|
|
2004
|
+
}
|
|
2005
|
+
function snippetRuns(snippet) {
|
|
2006
|
+
const runs = [];
|
|
2007
|
+
const pattern = /⟦([^⟧]*)⟧/g;
|
|
2008
|
+
let last = 0;
|
|
2009
|
+
for (const match of snippet.matchAll(pattern)) {
|
|
2010
|
+
const start = match.index ?? 0;
|
|
2011
|
+
if (start > last) runs.push({ text: snippet.slice(last, start), hit: false });
|
|
2012
|
+
runs.push({ text: match[1] ?? "", hit: true });
|
|
2013
|
+
last = start + match[0].length;
|
|
2014
|
+
}
|
|
2015
|
+
if (last < snippet.length) runs.push({ text: snippet.slice(last), hit: false });
|
|
2016
|
+
return runs;
|
|
2017
|
+
}
|
|
1888
2018
|
|
|
1889
2019
|
// src/core/room-events.ts
|
|
1890
2020
|
var RECORDING_STATES = [
|
|
@@ -3048,6 +3178,7 @@ var EMPTY = {
|
|
|
3048
3178
|
recordingId: null,
|
|
3049
3179
|
chat: [],
|
|
3050
3180
|
chatLoaded: false,
|
|
3181
|
+
chatHistoryError: null,
|
|
3051
3182
|
reactions: [],
|
|
3052
3183
|
spotlight: null,
|
|
3053
3184
|
moderationNotice: null,
|
|
@@ -3157,7 +3288,7 @@ function createMeetStore(initial = {}) {
|
|
|
3157
3288
|
state.names,
|
|
3158
3289
|
messages.map((message) => ({ identity: message.identity, displayName: message.displayName }))
|
|
3159
3290
|
);
|
|
3160
|
-
commit({ ...state, chat: merged.slice(-CHAT_LIMIT), chatLoaded: true, names });
|
|
3291
|
+
commit({ ...state, chat: merged.slice(-CHAT_LIMIT), chatLoaded: true, chatHistoryError: null, names });
|
|
3161
3292
|
},
|
|
3162
3293
|
settleChat(localId, next) {
|
|
3163
3294
|
const index = state.chat.findIndex((message) => message.id === localId);
|
|
@@ -3317,7 +3448,9 @@ function createMeetingSession(options) {
|
|
|
3317
3448
|
return identity.userId;
|
|
3318
3449
|
}
|
|
3319
3450
|
function knockIdentity() {
|
|
3320
|
-
return asParticipantIdentity(
|
|
3451
|
+
return asParticipantIdentity(
|
|
3452
|
+
identity.userId !== null ? `user:${identity.userId}` : `guest:${identity.deviceId}`
|
|
3453
|
+
);
|
|
3321
3454
|
}
|
|
3322
3455
|
function requireMeeting(operation) {
|
|
3323
3456
|
const meeting = store.snapshot().meeting;
|
|
@@ -3352,8 +3485,9 @@ function createMeetingSession(options) {
|
|
|
3352
3485
|
verdict.admitted ? "admitted" : "denied",
|
|
3353
3486
|
verdict.by === null ? null : verdict.by
|
|
3354
3487
|
);
|
|
3355
|
-
if (
|
|
3356
|
-
options.onAdmitted?.();
|
|
3488
|
+
if (decided === knockIdentity()) {
|
|
3489
|
+
if (verdict.admitted) options.onAdmitted?.();
|
|
3490
|
+
else options.onDenied?.();
|
|
3357
3491
|
}
|
|
3358
3492
|
}
|
|
3359
3493
|
},
|
|
@@ -3725,6 +3859,341 @@ function fileDownloadPath(fileId) {
|
|
|
3725
3859
|
return `/files/${encodeURIComponent(fileId)}/download?inline=1`;
|
|
3726
3860
|
}
|
|
3727
3861
|
|
|
3862
|
+
// src/core/after.ts
|
|
3863
|
+
import { formatDurationMs } from "@ai-matrx/kit/format";
|
|
3864
|
+
function kindOf(person) {
|
|
3865
|
+
if (person.isAgent) return "assistant";
|
|
3866
|
+
if (person.role === "host") return "host";
|
|
3867
|
+
if (person.role === "cohost") return "cohost";
|
|
3868
|
+
if (person.role === "guest") return person.userId !== null ? "signed-in guest" : "guest";
|
|
3869
|
+
return "member";
|
|
3870
|
+
}
|
|
3871
|
+
function ms(iso) {
|
|
3872
|
+
if (iso === null) return null;
|
|
3873
|
+
const at = new Date(iso).getTime();
|
|
3874
|
+
return Number.isNaN(at) ? null : at;
|
|
3875
|
+
}
|
|
3876
|
+
function attendanceReport(meeting, attendees, names = {}) {
|
|
3877
|
+
const end = ms(meeting.endedAt);
|
|
3878
|
+
const rows = attendees.map((person) => {
|
|
3879
|
+
const joined = ms(person.joinedAt);
|
|
3880
|
+
const left = ms(person.leftAt);
|
|
3881
|
+
const until = left ?? end;
|
|
3882
|
+
const duration = joined !== null && until !== null && until >= joined ? until - joined : null;
|
|
3883
|
+
return {
|
|
3884
|
+
id: person.id,
|
|
3885
|
+
identity: person.identity,
|
|
3886
|
+
name: displayNameFor(names, person.identity, person.displayName),
|
|
3887
|
+
userId: person.userId,
|
|
3888
|
+
kind: kindOf(person),
|
|
3889
|
+
joinedAt: person.joinedAt,
|
|
3890
|
+
leftAt: person.leftAt,
|
|
3891
|
+
durationMs: duration,
|
|
3892
|
+
durationToEnd: duration !== null && left === null,
|
|
3893
|
+
admission: person.admissionState
|
|
3894
|
+
};
|
|
3895
|
+
});
|
|
3896
|
+
const rank = {
|
|
3897
|
+
host: 0,
|
|
3898
|
+
cohost: 1,
|
|
3899
|
+
member: 2,
|
|
3900
|
+
"signed-in guest": 3,
|
|
3901
|
+
guest: 4,
|
|
3902
|
+
assistant: 5
|
|
3903
|
+
};
|
|
3904
|
+
return rows.sort((a, b) => {
|
|
3905
|
+
if (rank[a.kind] !== rank[b.kind]) return rank[a.kind] - rank[b.kind];
|
|
3906
|
+
return (ms(a.joinedAt) ?? Number.MAX_SAFE_INTEGER) - (ms(b.joinedAt) ?? Number.MAX_SAFE_INTEGER);
|
|
3907
|
+
});
|
|
3908
|
+
}
|
|
3909
|
+
function attendanceTotals(rows) {
|
|
3910
|
+
const humans = rows.filter((row) => row.kind !== "assistant");
|
|
3911
|
+
const attended = humans.filter((row) => row.joinedAt !== null);
|
|
3912
|
+
const timed = attended.filter((row) => row.durationMs !== null);
|
|
3913
|
+
return {
|
|
3914
|
+
people: attended.length,
|
|
3915
|
+
guests: attended.filter((row) => row.kind === "guest" || row.kind === "signed-in guest").length,
|
|
3916
|
+
neverAdmitted: humans.filter((row) => row.joinedAt === null).length,
|
|
3917
|
+
averageMs: timed.length === 0 ? null : Math.round(timed.reduce((sum, row) => sum + (row.durationMs ?? 0), 0) / timed.length)
|
|
3918
|
+
};
|
|
3919
|
+
}
|
|
3920
|
+
function csvCell(value) {
|
|
3921
|
+
if (value === null) return "";
|
|
3922
|
+
const text3 = String(value);
|
|
3923
|
+
return /[",\n\r]/.test(text3) ? `"${text3.replace(/"/g, '""')}"` : text3;
|
|
3924
|
+
}
|
|
3925
|
+
var ATTENDANCE_CSV_HEADER = [
|
|
3926
|
+
"Meeting",
|
|
3927
|
+
"Meeting started",
|
|
3928
|
+
"Name",
|
|
3929
|
+
"Attended as",
|
|
3930
|
+
"Joined",
|
|
3931
|
+
"Left",
|
|
3932
|
+
"Minutes in the meeting",
|
|
3933
|
+
"Admission"
|
|
3934
|
+
];
|
|
3935
|
+
function attendanceCsv(reports) {
|
|
3936
|
+
const lines = [ATTENDANCE_CSV_HEADER.join(",")];
|
|
3937
|
+
for (const { meeting, rows } of reports) {
|
|
3938
|
+
for (const row of rows) {
|
|
3939
|
+
lines.push(
|
|
3940
|
+
[
|
|
3941
|
+
meeting.title,
|
|
3942
|
+
meeting.startedAt ?? meeting.scheduledFor,
|
|
3943
|
+
row.name,
|
|
3944
|
+
row.kind,
|
|
3945
|
+
row.joinedAt,
|
|
3946
|
+
row.leftAt,
|
|
3947
|
+
row.durationMs === null ? null : Math.round(row.durationMs / 6e3) / 10,
|
|
3948
|
+
row.admission
|
|
3949
|
+
].map(csvCell).join(",")
|
|
3950
|
+
);
|
|
3951
|
+
}
|
|
3952
|
+
}
|
|
3953
|
+
return `${lines.join("\r\n")}\r
|
|
3954
|
+
`;
|
|
3955
|
+
}
|
|
3956
|
+
function recordingOffsetMs(at, recording) {
|
|
3957
|
+
if (recording === null || recording.state !== "available") return null;
|
|
3958
|
+
const start = ms(recording.startedAt);
|
|
3959
|
+
const moment = ms(at);
|
|
3960
|
+
if (start === null || moment === null || moment < start) return null;
|
|
3961
|
+
const end = ms(recording.endedAt);
|
|
3962
|
+
if (end !== null && moment > end) return null;
|
|
3963
|
+
return moment - start;
|
|
3964
|
+
}
|
|
3965
|
+
function clockPosition(offsetMs) {
|
|
3966
|
+
const total = Math.max(0, Math.floor(offsetMs / 1e3));
|
|
3967
|
+
const h = Math.floor(total / 3600);
|
|
3968
|
+
const m = Math.floor(total % 3600 / 60);
|
|
3969
|
+
const s = total % 60;
|
|
3970
|
+
const ss = String(s).padStart(2, "0");
|
|
3971
|
+
return h > 0 ? `${h}:${String(m).padStart(2, "0")}:${ss}` : `${m}:${ss}`;
|
|
3972
|
+
}
|
|
3973
|
+
function searchTranscript(segments, query, names = {}) {
|
|
3974
|
+
const needle = fold(query.trim());
|
|
3975
|
+
if (needle.length === 0) return [];
|
|
3976
|
+
const out = [];
|
|
3977
|
+
for (const segment of segments) {
|
|
3978
|
+
const hay = fold(segment.text);
|
|
3979
|
+
const ranges = [];
|
|
3980
|
+
let from = 0;
|
|
3981
|
+
for (; ; ) {
|
|
3982
|
+
const at = hay.indexOf(needle, from);
|
|
3983
|
+
if (at < 0) break;
|
|
3984
|
+
ranges.push([at, at + needle.length]);
|
|
3985
|
+
from = at + needle.length;
|
|
3986
|
+
}
|
|
3987
|
+
const speaker = fold(displayNameFor(names, segment.identity, segment.speaker));
|
|
3988
|
+
if (ranges.length > 0 || speaker.includes(needle)) {
|
|
3989
|
+
out.push({ segmentId: segment.id, ranges });
|
|
3990
|
+
}
|
|
3991
|
+
}
|
|
3992
|
+
return out;
|
|
3993
|
+
}
|
|
3994
|
+
function fold(value) {
|
|
3995
|
+
return value.normalize("NFD").replace(/[̀-ͯ]/g, (mark) => mark.length === 1 ? "" : mark).toLowerCase();
|
|
3996
|
+
}
|
|
3997
|
+
function clockOfDay(iso, options) {
|
|
3998
|
+
const at = new Date(iso);
|
|
3999
|
+
if (Number.isNaN(at.getTime())) return "";
|
|
4000
|
+
return at.toLocaleTimeString(options.locale, {
|
|
4001
|
+
hour: "numeric",
|
|
4002
|
+
minute: "2-digit",
|
|
4003
|
+
second: "2-digit",
|
|
4004
|
+
...options.timeZone !== void 0 ? { timeZone: options.timeZone } : {}
|
|
4005
|
+
});
|
|
4006
|
+
}
|
|
4007
|
+
function vttStamp(offsetMs) {
|
|
4008
|
+
const total = Math.max(0, Math.round(offsetMs));
|
|
4009
|
+
const h = Math.floor(total / 36e5);
|
|
4010
|
+
const m = Math.floor(total % 36e5 / 6e4);
|
|
4011
|
+
const s = Math.floor(total % 6e4 / 1e3);
|
|
4012
|
+
const milli = total % 1e3;
|
|
4013
|
+
return `${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}.${String(milli).padStart(3, "0")}`;
|
|
4014
|
+
}
|
|
4015
|
+
function transcriptDocument(bundle, format, options = {}) {
|
|
4016
|
+
const names = options.names ?? bundle.names;
|
|
4017
|
+
const lines = bundle.transcript.filter((line) => line.text.trim().length > 0);
|
|
4018
|
+
const speaker = (line) => displayNameFor(names, line.identity, line.speaker);
|
|
4019
|
+
if (format === "vtt") {
|
|
4020
|
+
const zeroIso = bundle.recording?.state === "available" && bundle.recording.startedAt !== null ? bundle.recording.startedAt : bundle.meeting.startedAt ?? lines[0]?.startedAt ?? null;
|
|
4021
|
+
const zero = ms(zeroIso) ?? 0;
|
|
4022
|
+
const cues = ["WEBVTT", ""];
|
|
4023
|
+
lines.forEach((line, index) => {
|
|
4024
|
+
const start = (ms(line.startedAt) ?? zero) - zero;
|
|
4025
|
+
if (start < 0) return;
|
|
4026
|
+
const next = lines[index + 1];
|
|
4027
|
+
const nextStart = next !== void 0 ? (ms(next.startedAt) ?? zero) - zero : start + 4e3;
|
|
4028
|
+
const end = Math.max(start + 500, Math.min(nextStart, start + 1e4));
|
|
4029
|
+
cues.push(`${vttStamp(start)} --> ${vttStamp(end)}`);
|
|
4030
|
+
cues.push(`<v ${speaker(line).replace(/[<>]/g, "")}>${line.text.replace(/-->/g, "->")}`);
|
|
4031
|
+
cues.push("");
|
|
4032
|
+
});
|
|
4033
|
+
return cues.join("\n");
|
|
4034
|
+
}
|
|
4035
|
+
const out = [];
|
|
4036
|
+
const title = bundle.meeting.title;
|
|
4037
|
+
out.push(format === "md" ? `# ${title} \u2014 transcript` : `${title} \u2014 transcript`);
|
|
4038
|
+
out.push("");
|
|
4039
|
+
let last = null;
|
|
4040
|
+
for (const line of lines) {
|
|
4041
|
+
if (line.identity !== last) {
|
|
4042
|
+
if (last !== null) out.push("");
|
|
4043
|
+
const who = speaker(line);
|
|
4044
|
+
const clock = clockOfDay(line.startedAt, options);
|
|
4045
|
+
out.push(format === "md" ? `**${who}** \xB7 ${clock}` : `${who} (${clock})`);
|
|
4046
|
+
last = line.identity;
|
|
4047
|
+
}
|
|
4048
|
+
out.push(line.text.trim());
|
|
4049
|
+
}
|
|
4050
|
+
if (lines.length === 0) out.push("No transcript was captured for this meeting.");
|
|
4051
|
+
return `${out.join("\n")}
|
|
4052
|
+
`;
|
|
4053
|
+
}
|
|
4054
|
+
function bullet(note) {
|
|
4055
|
+
const owner = note.assigneeDisplayName !== null ? ` \u2014 ${note.assigneeDisplayName}` : "";
|
|
4056
|
+
return `- ${note.text.trim()}${owner}`;
|
|
4057
|
+
}
|
|
4058
|
+
function notesDocument(bundle, options = {}) {
|
|
4059
|
+
const out = [`# ${bundle.meeting.title}`, ""];
|
|
4060
|
+
if (options.when) out.push(`_${options.when}_`, "");
|
|
4061
|
+
const people = bundle.attendees.filter((person) => !person.isAgent && person.joinedAt !== null).map((person) => displayNameFor(bundle.names, person.identity, person.displayName));
|
|
4062
|
+
if (people.length > 0) out.push(`**Attended:** ${people.join(", ")}`, "");
|
|
4063
|
+
out.push("## Summary", "", bundle.summary?.text.trim() || "No summary was written.", "");
|
|
4064
|
+
out.push("## Decisions", "");
|
|
4065
|
+
out.push(...bundle.decisions.length > 0 ? bundle.decisions.map(bullet) : ["None recorded."], "");
|
|
4066
|
+
out.push("## Action items", "");
|
|
4067
|
+
out.push(
|
|
4068
|
+
...bundle.actionItems.length > 0 ? bundle.actionItems.map(bullet) : ["None recorded."],
|
|
4069
|
+
""
|
|
4070
|
+
);
|
|
4071
|
+
if (options.link) out.push(`[Open the meeting record](${options.link})`, "");
|
|
4072
|
+
return out.join("\n");
|
|
4073
|
+
}
|
|
4074
|
+
function meetingLength(meeting) {
|
|
4075
|
+
const start = ms(meeting.startedAt);
|
|
4076
|
+
const end = ms(meeting.endedAt);
|
|
4077
|
+
if (start === null || end === null || end < start) return null;
|
|
4078
|
+
return formatDurationMs(end - start, { style: "long" });
|
|
4079
|
+
}
|
|
4080
|
+
async function loadMeetingChat(api, meetingId, roomToken = null) {
|
|
4081
|
+
const operation = "meet.loadMeetingChat";
|
|
4082
|
+
let response;
|
|
4083
|
+
try {
|
|
4084
|
+
response = await api.request({
|
|
4085
|
+
path: `${MEET_ROUTES.chat}?meeting_id=${encodeURIComponent(meetingId)}`,
|
|
4086
|
+
method: "GET",
|
|
4087
|
+
operation,
|
|
4088
|
+
requireAuth: roomToken === null,
|
|
4089
|
+
...roomToken !== null ? { headers: { [ROOM_TOKEN_HEADER]: roomToken } } : {}
|
|
4090
|
+
});
|
|
4091
|
+
} catch (error) {
|
|
4092
|
+
return {
|
|
4093
|
+
messages: [],
|
|
4094
|
+
refusal: {
|
|
4095
|
+
message: `The chat could not be read (${error.message}).`,
|
|
4096
|
+
remedy: error.remedy ?? "Reload the page."
|
|
4097
|
+
}
|
|
4098
|
+
};
|
|
4099
|
+
}
|
|
4100
|
+
if (!response.ok) {
|
|
4101
|
+
let body = null;
|
|
4102
|
+
try {
|
|
4103
|
+
body = await response.json();
|
|
4104
|
+
} catch {
|
|
4105
|
+
body = null;
|
|
4106
|
+
}
|
|
4107
|
+
const said = readServerRefusal(body);
|
|
4108
|
+
return {
|
|
4109
|
+
messages: [],
|
|
4110
|
+
refusal: {
|
|
4111
|
+
message: said?.reason ?? `The chat could not be read (the server answered ${response.status}).`,
|
|
4112
|
+
remedy: said?.remedy || "Reload the page."
|
|
4113
|
+
}
|
|
4114
|
+
};
|
|
4115
|
+
}
|
|
4116
|
+
const payload = await response.json();
|
|
4117
|
+
const rows = Array.isArray(payload.messages) ? payload.messages : [];
|
|
4118
|
+
return {
|
|
4119
|
+
messages: rows.map(
|
|
4120
|
+
(row) => typeof row === "object" && row !== null ? chatFromRow(row) : null
|
|
4121
|
+
).filter((message) => message !== null),
|
|
4122
|
+
refusal: null
|
|
4123
|
+
};
|
|
4124
|
+
}
|
|
4125
|
+
async function refusalOf(response, operation) {
|
|
4126
|
+
let body = null;
|
|
4127
|
+
try {
|
|
4128
|
+
body = await response.json();
|
|
4129
|
+
} catch {
|
|
4130
|
+
body = null;
|
|
4131
|
+
}
|
|
4132
|
+
const said = readServerRefusal(body);
|
|
4133
|
+
return new MeetError(
|
|
4134
|
+
response.status === 403 || response.status === 401 ? "forbidden" : response.status === 404 ? "not-found" : response.status === 409 ? "conflict" : "transport",
|
|
4135
|
+
`${operation}: ${said?.reason ?? `the server answered ${response.status}`}`,
|
|
4136
|
+
said?.remedy || "Nothing was sent. Try again."
|
|
4137
|
+
);
|
|
4138
|
+
}
|
|
4139
|
+
function asRecipient(row) {
|
|
4140
|
+
const sources = Array.isArray(row["sources"]) ? row["sources"] : [];
|
|
4141
|
+
return {
|
|
4142
|
+
key: String(row["key"] ?? ""),
|
|
4143
|
+
name: String(row["name"] ?? ""),
|
|
4144
|
+
email: typeof row["email"] === "string" ? row["email"] : null,
|
|
4145
|
+
userId: typeof row["user_id"] === "string" ? row["user_id"] : null,
|
|
4146
|
+
sources: sources.filter((s) => s === "invitee" || s === "attendee"),
|
|
4147
|
+
reachable: row["reachable"] !== false
|
|
4148
|
+
};
|
|
4149
|
+
}
|
|
4150
|
+
async function loadFollowUpDraft(api, meetingId) {
|
|
4151
|
+
const operation = "meet.loadFollowUpDraft";
|
|
4152
|
+
const response = await api.request({
|
|
4153
|
+
path: meetingPath(MEET_ROUTES.meetingFollowUp, meetingId),
|
|
4154
|
+
method: "GET",
|
|
4155
|
+
requireAuth: true,
|
|
4156
|
+
operation
|
|
4157
|
+
});
|
|
4158
|
+
if (!response.ok) throw await refusalOf(response, operation);
|
|
4159
|
+
const body = await response.json();
|
|
4160
|
+
const last = body["last_sent"];
|
|
4161
|
+
return {
|
|
4162
|
+
subject: String(body["subject"] ?? ""),
|
|
4163
|
+
body: String(body["body"] ?? ""),
|
|
4164
|
+
recipients: (Array.isArray(body["recipients"]) ? body["recipients"] : []).map(
|
|
4165
|
+
(row) => asRecipient(row)
|
|
4166
|
+
),
|
|
4167
|
+
wrapUpWritten: body["wrap_up_written"] === true,
|
|
4168
|
+
lastSent: last && typeof last["at"] === "string" ? { at: last["at"], count: typeof last["count"] === "number" ? last["count"] : 0 } : null
|
|
4169
|
+
};
|
|
4170
|
+
}
|
|
4171
|
+
async function sendFollowUp(api, meetingId, input) {
|
|
4172
|
+
const operation = "meet.sendFollowUp";
|
|
4173
|
+
const response = await api.request({
|
|
4174
|
+
path: meetingPath(MEET_ROUTES.meetingFollowUp, meetingId),
|
|
4175
|
+
method: "POST",
|
|
4176
|
+
requireAuth: true,
|
|
4177
|
+
operation,
|
|
4178
|
+
body: {
|
|
4179
|
+
subject: input.subject,
|
|
4180
|
+
body: input.body,
|
|
4181
|
+
recipient_keys: [...input.recipientKeys],
|
|
4182
|
+
dry_run: input.dryRun === true
|
|
4183
|
+
}
|
|
4184
|
+
});
|
|
4185
|
+
if (!response.ok) throw await refusalOf(response, operation);
|
|
4186
|
+
const body = await response.json();
|
|
4187
|
+
return {
|
|
4188
|
+
dryRun: body["dry_run"] === true,
|
|
4189
|
+
queued: Array.isArray(body["queued"]) ? body["queued"] : [],
|
|
4190
|
+
skipped: Array.isArray(body["skipped"]) ? body["skipped"].map((row) => ({
|
|
4191
|
+
key: String(row["key"] ?? ""),
|
|
4192
|
+
reason: String(row["reason"] ?? "")
|
|
4193
|
+
})) : []
|
|
4194
|
+
};
|
|
4195
|
+
}
|
|
4196
|
+
|
|
3728
4197
|
// src/core/management.ts
|
|
3729
4198
|
async function refusal(response, operation) {
|
|
3730
4199
|
let body = null;
|
|
@@ -4481,7 +4950,7 @@ function createInRoomClient(options) {
|
|
|
4481
4950
|
}
|
|
4482
4951
|
|
|
4483
4952
|
// src/core/record.ts
|
|
4484
|
-
import { formatDurationMs } from "@ai-matrx/kit/format";
|
|
4953
|
+
import { formatDurationMs as formatDurationMs2 } from "@ai-matrx/kit/format";
|
|
4485
4954
|
function activeWrapUpSummary(notes) {
|
|
4486
4955
|
return notes.find((note) => note.role === "summary" && note.supersededBy === null) ?? null;
|
|
4487
4956
|
}
|
|
@@ -4564,6 +5033,36 @@ function wasRefused(bundle, part) {
|
|
|
4564
5033
|
function absenceSentence(bundle, part, absence) {
|
|
4565
5034
|
return wasRefused(bundle, part) ? REFUSED_ABSENCE : absence;
|
|
4566
5035
|
}
|
|
5036
|
+
function noteTakerMetadata(meeting) {
|
|
5037
|
+
const raw = meeting.metadata["note_taker"];
|
|
5038
|
+
return typeof raw === "object" && raw !== null && !Array.isArray(raw) ? raw : {};
|
|
5039
|
+
}
|
|
5040
|
+
var WRAP_UP_IN_PROGRESS = "The wrap-up is being written\u2026 The summary, the decisions and the action items are produced from the whole transcript once the meeting ends, which takes a moment longer than the meeting does.";
|
|
5041
|
+
var WRAP_UP_ABSENT = "No wrap-up was written for this meeting. A summary is produced from the transcript when the meeting ends and the note-taker was in the room.";
|
|
5042
|
+
function wrapUpStatus(bundle) {
|
|
5043
|
+
if (bundle.summary !== null) return { state: "written" };
|
|
5044
|
+
if (wasRefused(bundle, "notes")) return { state: "refused", message: REFUSED_ABSENCE };
|
|
5045
|
+
const noteTaker = noteTakerMetadata(bundle.meeting);
|
|
5046
|
+
const intelligenceRaw = noteTaker["intelligence"];
|
|
5047
|
+
const intelligence = typeof intelligenceRaw === "object" && intelligenceRaw !== null && !Array.isArray(intelligenceRaw) ? intelligenceRaw : {};
|
|
5048
|
+
const wrapUpRaw = intelligence["wrap_up"];
|
|
5049
|
+
const wrapUp = typeof wrapUpRaw === "object" && wrapUpRaw !== null && !Array.isArray(wrapUpRaw) ? wrapUpRaw : {};
|
|
5050
|
+
if (wrapUp["state"] === "failed") {
|
|
5051
|
+
const reason = wrapUp["reason"];
|
|
5052
|
+
return {
|
|
5053
|
+
state: "failed",
|
|
5054
|
+
// THE SERVER'S OWN SENTENCE, verbatim. It already names the remedy, and a
|
|
5055
|
+
// house sentence over it would hide the one thing worth reading.
|
|
5056
|
+
message: typeof reason === "string" && reason.trim().length > 0 ? reason.trim() : "The wrap-up pass failed and no summary was written. It is not retried automatically; a host can end the meeting again to re-queue it."
|
|
5057
|
+
};
|
|
5058
|
+
}
|
|
5059
|
+
const queued = typeof wrapUp["run_id"] === "string" || typeof intelligence["wrap_up_run_id"] === "string" || wrapUp["state"] === "queued" || wrapUp["state"] === "running";
|
|
5060
|
+
const attended = noteTaker["state"] === "ended" || noteTaker["state"] === "transcribing" || noteTaker["state"] === "handoff_pending";
|
|
5061
|
+
if (hasEnded(bundle.meeting) && (queued || attended)) {
|
|
5062
|
+
return { state: "in-progress", message: WRAP_UP_IN_PROGRESS, canRefresh: true };
|
|
5063
|
+
}
|
|
5064
|
+
return { state: "absent", message: WRAP_UP_ABSENT };
|
|
5065
|
+
}
|
|
4567
5066
|
function groupTranscript(segments, names = {}) {
|
|
4568
5067
|
const blocks = [];
|
|
4569
5068
|
for (const segment of segments) {
|
|
@@ -4632,7 +5131,7 @@ function meetingWhen(meeting, locale) {
|
|
|
4632
5131
|
const endedAt = new Date(meeting.endedAt);
|
|
4633
5132
|
if (Number.isNaN(endedAt.getTime())) return when2;
|
|
4634
5133
|
const elapsedMs = endedAt.getTime() - startedAt.getTime();
|
|
4635
|
-
return `${when2} \xB7 ${
|
|
5134
|
+
return `${when2} \xB7 ${formatDurationMs2(elapsedMs, { style: "long" })}`;
|
|
4636
5135
|
}
|
|
4637
5136
|
var GUEST_RECORD_ACCESS_LEVELS = [
|
|
4638
5137
|
"none",
|
|
@@ -4681,6 +5180,37 @@ var NOT_SHARED = {
|
|
|
4681
5180
|
recording: "The recording of this meeting is not shared with guests."
|
|
4682
5181
|
};
|
|
4683
5182
|
var NOT_SHARED_REMEDY = "The organization that held this meeting decided how much of its record guests can read. If you need more, ask whoever invited you \u2014 they can send it to you, or change the setting for this meeting.";
|
|
5183
|
+
var RESTRICTION_PART_NAME = {
|
|
5184
|
+
attendance: "attendance",
|
|
5185
|
+
notes: "notes and decisions",
|
|
5186
|
+
transcript: "word-for-word transcript",
|
|
5187
|
+
recording: "recording"
|
|
5188
|
+
};
|
|
5189
|
+
function joinPartNames(parts) {
|
|
5190
|
+
const names = parts.map((part) => RESTRICTION_PART_NAME[part]);
|
|
5191
|
+
if (names.length < 2) return names[0] ?? "this part of the record";
|
|
5192
|
+
if (names.length === 2) return `${names[0]} and ${names[1]}`;
|
|
5193
|
+
return `${names.slice(0, -1).join(", ")}, and ${names[names.length - 1]}`;
|
|
5194
|
+
}
|
|
5195
|
+
function summarizeMeetingRecordRestrictions(restrictions) {
|
|
5196
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
5197
|
+
for (const restriction of restrictions) {
|
|
5198
|
+
const rows = grouped.get(restriction.remedy) ?? [];
|
|
5199
|
+
rows.push(restriction);
|
|
5200
|
+
grouped.set(restriction.remedy, rows);
|
|
5201
|
+
}
|
|
5202
|
+
return [...grouped.values()].map((rows) => {
|
|
5203
|
+
if (rows.length === 1) {
|
|
5204
|
+
const row = rows[0];
|
|
5205
|
+
return { parts: [row.part], message: row.message, remedy: row.remedy };
|
|
5206
|
+
}
|
|
5207
|
+
return {
|
|
5208
|
+
parts: rows.map((row) => row.part),
|
|
5209
|
+
message: `Some parts of this meeting record are not available to this reader: ${joinPartNames(rows.map((row) => row.part))}.`,
|
|
5210
|
+
remedy: rows[0].remedy
|
|
5211
|
+
};
|
|
5212
|
+
});
|
|
5213
|
+
}
|
|
4684
5214
|
async function loadGuestMeetingRecord(args) {
|
|
4685
5215
|
const { api, repository, meetingId, meeting, roomToken } = args;
|
|
4686
5216
|
if (roomToken === null) {
|
|
@@ -4812,6 +5342,7 @@ async function setGuestRecordAccess(args) {
|
|
|
4812
5342
|
}
|
|
4813
5343
|
var GUEST_RECORD_NOT_SHARED = NOT_SHARED;
|
|
4814
5344
|
export {
|
|
5345
|
+
ATTENDANCE_CSV_HEADER,
|
|
4815
5346
|
CALL_INVITE_KIND,
|
|
4816
5347
|
CALL_INVITE_VERSION,
|
|
4817
5348
|
CONSENT_NOTICE_ATTRIBUTE,
|
|
@@ -4853,6 +5384,7 @@ export {
|
|
|
4853
5384
|
TRANSCRIPTION_ATTRIBUTES,
|
|
4854
5385
|
TRANSCRIPTION_TOPIC,
|
|
4855
5386
|
absenceSentence,
|
|
5387
|
+
activeWrapUpSummary,
|
|
4856
5388
|
announceMeeting,
|
|
4857
5389
|
applyRoomEvent,
|
|
4858
5390
|
asCallId,
|
|
@@ -4861,6 +5393,9 @@ export {
|
|
|
4861
5393
|
asParticipantIdentity,
|
|
4862
5394
|
asRoomName,
|
|
4863
5395
|
asUserId,
|
|
5396
|
+
attendanceCsv,
|
|
5397
|
+
attendanceReport,
|
|
5398
|
+
attendanceTotals,
|
|
4864
5399
|
callInviteAction,
|
|
4865
5400
|
callInviteText,
|
|
4866
5401
|
callStateLabel,
|
|
@@ -4869,6 +5404,7 @@ export {
|
|
|
4869
5404
|
canTransition,
|
|
4870
5405
|
captionSegment,
|
|
4871
5406
|
changesPatch,
|
|
5407
|
+
clockPosition,
|
|
4872
5408
|
computeStage,
|
|
4873
5409
|
countLabel,
|
|
4874
5410
|
createCallCenter,
|
|
@@ -4909,11 +5445,14 @@ export {
|
|
|
4909
5445
|
learnName,
|
|
4910
5446
|
learnNames,
|
|
4911
5447
|
listenOnlyNotice,
|
|
5448
|
+
loadFollowUpDraft,
|
|
4912
5449
|
loadGuestMeetingRecord,
|
|
5450
|
+
loadMeetingChat,
|
|
4913
5451
|
loadMeetingRecord,
|
|
4914
5452
|
mapConnectionState,
|
|
4915
5453
|
mapQuality,
|
|
4916
5454
|
meetingInviteAction,
|
|
5455
|
+
meetingLength,
|
|
4917
5456
|
meetingLink,
|
|
4918
5457
|
meetingPath,
|
|
4919
5458
|
meetingSlug,
|
|
@@ -4928,16 +5467,19 @@ export {
|
|
|
4928
5467
|
noteTakerLabel,
|
|
4929
5468
|
noteTakerParticipant,
|
|
4930
5469
|
noteTakerStatus,
|
|
5470
|
+
notesDocument,
|
|
4931
5471
|
orderParticipants,
|
|
4932
5472
|
parseRoomToken,
|
|
4933
5473
|
participantSummary,
|
|
4934
5474
|
playableRecordingFileId,
|
|
4935
5475
|
projectInvitee,
|
|
5476
|
+
projectLibraryRecording,
|
|
4936
5477
|
projectNote,
|
|
4937
5478
|
projectOccurrence,
|
|
4938
5479
|
projectOverride,
|
|
4939
5480
|
projectParticipant,
|
|
4940
5481
|
projectRsvpInvitation,
|
|
5482
|
+
projectSearchHit,
|
|
4941
5483
|
projectSegment,
|
|
4942
5484
|
projectUpcoming,
|
|
4943
5485
|
pushMeetingToGoogleCalendar,
|
|
@@ -4945,13 +5487,20 @@ export {
|
|
|
4945
5487
|
readableTranscript,
|
|
4946
5488
|
recordingLabel,
|
|
4947
5489
|
recordingNotice,
|
|
5490
|
+
recordingOffsetMs,
|
|
4948
5491
|
roleOf,
|
|
4949
5492
|
roomTopic,
|
|
5493
|
+
searchTranscript,
|
|
5494
|
+
sendFollowUp,
|
|
4950
5495
|
setGuestRecordAccess,
|
|
4951
5496
|
settingsPatch,
|
|
4952
5497
|
snapshotParticipant,
|
|
5498
|
+
snippetRuns,
|
|
5499
|
+
summarizeMeetingRecordRestrictions,
|
|
5500
|
+
transcriptDocument,
|
|
4953
5501
|
unavailableFrom,
|
|
4954
5502
|
userTopic,
|
|
4955
|
-
wasRefused
|
|
5503
|
+
wasRefused,
|
|
5504
|
+
wrapUpStatus
|
|
4956
5505
|
};
|
|
4957
5506
|
//# sourceMappingURL=index.js.map
|