@openclaw/google-meet 2026.9.6 → 2026.9.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.
Files changed (62) hide show
  1. package/assets/icon.png +0 -0
  2. package/dist/.setup/{chrome-create-BZRBHJp9.mjs → chrome-create-wuAK5Pnq.mjs} +14 -19
  3. package/dist/.setup/{cli-CbxqjjTC.mjs → cli-DVDq0TFu.mjs} +27 -695
  4. package/dist/.setup/cli-export-D4IM6KLJ.mjs +632 -0
  5. package/dist/.setup/config-BXGAMntj.mjs +240 -0
  6. package/dist/.setup/{create-CG9Pgwsb.mjs → create-UrHOmnWb.mjs} +4 -17
  7. package/dist/.setup/{google-meet-platform-adapter-DQZ74Kd5.mjs → google-meet-platform-adapter-gosgbMIM.mjs} +337 -228
  8. package/dist/.setup/{google-meet-platform-constants-Bs5iAg3E.mjs → google-meet-platform-constants-CnRfrYX7.mjs} +1 -4
  9. package/dist/.setup/{node-host-BUrsParx.mjs → node-host-Tr7Mx3da.mjs} +3 -3
  10. package/dist/.setup/{node-invoke-policy-hwXq9MfV.mjs → node-invoke-policy-Dd3krTYV.mjs} +2 -2
  11. package/dist/.setup/{oauth-22Ey-zCb.mjs → oauth-BoyruWcS.mjs} +2 -2
  12. package/dist/.setup/{plugin-helpers--7X3icub.mjs → plugin-helpers-CdQ_YNA7.mjs} +147 -236
  13. package/dist/.setup/{plugin-registration-B3LXKV4R.mjs → plugin-registration-aCEuCerg.mjs} +34 -10
  14. package/dist/.setup/{runtime-BuIZ5e3V.mjs → runtime-BdiF53A2.mjs} +85 -41
  15. package/dist/doctor-contract-api.js +3 -6
  16. package/dist/index.js +48 -12
  17. package/node_modules/typebox/build/compile/code.mjs +1 -2
  18. package/node_modules/typebox/build/compile/validator.mjs +5 -1
  19. package/node_modules/typebox/build/format/iri.mjs +16 -9
  20. package/node_modules/typebox/build/guard/emit.mjs +2 -2
  21. package/node_modules/typebox/build/guard/guard.d.mts +7 -5
  22. package/node_modules/typebox/build/guard/guard.mjs +14 -9
  23. package/node_modules/typebox/build/guard/index.mjs +6 -0
  24. package/node_modules/typebox/build/guard/unicode/unicode.d.mts +6 -0
  25. package/node_modules/typebox/build/guard/unicode/unicode.mjs +35 -0
  26. package/node_modules/typebox/build/guard/{string.mjs → unicode/unicode_segment.mjs} +16 -19
  27. package/node_modules/typebox/build/schema/build.mjs +2 -3
  28. package/node_modules/typebox/build/schema/engine/_pathing.d.mts +1 -0
  29. package/node_modules/typebox/build/schema/engine/_pathing.mjs +11 -0
  30. package/node_modules/typebox/build/schema/engine/_refine.mjs +13 -5
  31. package/node_modules/typebox/build/schema/engine/additionalProperties.mjs +2 -1
  32. package/node_modules/typebox/build/schema/engine/dependencies.mjs +2 -1
  33. package/node_modules/typebox/build/schema/engine/dependentSchemas.mjs +2 -1
  34. package/node_modules/typebox/build/schema/engine/dynamicRef.d.mts +1 -1
  35. package/node_modules/typebox/build/schema/engine/dynamicRef.mjs +2 -2
  36. package/node_modules/typebox/build/schema/engine/format.mjs +9 -2
  37. package/node_modules/typebox/build/schema/engine/patternProperties.mjs +3 -2
  38. package/node_modules/typebox/build/schema/engine/properties.mjs +3 -2
  39. package/node_modules/typebox/build/schema/engine/propertyNames.mjs +2 -1
  40. package/node_modules/typebox/build/schema/engine/recursiveRef.d.mts +1 -1
  41. package/node_modules/typebox/build/schema/engine/recursiveRef.mjs +2 -2
  42. package/node_modules/typebox/build/schema/engine/ref.d.mts +1 -1
  43. package/node_modules/typebox/build/schema/engine/ref.mjs +2 -2
  44. package/node_modules/typebox/build/schema/engine/schema.mjs +24 -24
  45. package/node_modules/typebox/build/schema/parse.mjs +3 -0
  46. package/node_modules/typebox/build/schema/pointer/pointer.d.mts +1 -6
  47. package/node_modules/typebox/build/schema/pointer/pointer.mjs +47 -64
  48. package/node_modules/typebox/build/system/settings/internal.d.mts +2 -0
  49. package/node_modules/typebox/build/system/settings/internal.mjs +20 -0
  50. package/node_modules/typebox/build/system/settings/settings.d.mts +11 -0
  51. package/node_modules/typebox/build/system/settings/settings.mjs +14 -0
  52. package/node_modules/typebox/build/type/extends/inference.d.mts +1 -1
  53. package/node_modules/typebox/build/type/extends/inference.mjs +15 -2
  54. package/node_modules/typebox/build/type/extends/tuple.d.mts +2 -1
  55. package/node_modules/typebox/build/type/extends/tuple.mjs +21 -1
  56. package/node_modules/typebox/build/value/parse/parse.mjs +10 -4
  57. package/node_modules/typebox/package.json +1 -1
  58. package/node_modules/typebox/readme.md +3 -1
  59. package/package.json +5 -5
  60. package/dist/.setup/config-C-6enWkx.mjs +0 -328
  61. package/dist/.setup/google-api-errors-BDYXfKRa.mjs +0 -17
  62. /package/node_modules/typebox/build/guard/{string.d.mts → unicode/unicode_segment.d.mts} +0 -0
@@ -1,15 +1,346 @@
1
- import { n as GOOGLE_MEET_NODE_COMMAND } from "./google-meet-platform-constants-Bs5iAg3E.mjs";
1
+ import { n as GOOGLE_MEET_NODE_COMMAND } from "./google-meet-platform-constants-CnRfrYX7.mjs";
2
2
  import { t as normalizeMeetUrl } from "./meet-url-BP2Jlvjs.mjs";
3
- import { a as isSameMeetUrlForReuse, i as isRecoverableMeetTab, n as forceMeetEnglishUi, o as normalizeMeetUrlForReuse, r as isEnglishMeetTab, s as readMeetAuthUser, t as createMeetWithBrowserProxyOnNode } from "./chrome-create-BZRBHJp9.mjs";
4
- import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
3
+ import { a as isSameMeetUrlForReuse, i as isRecoverableMeetTab, n as forceMeetEnglishUi, o as normalizeMeetUrlForReuse, r as isEnglishMeetTab, s as readMeetAuthUser, t as createMeetWithBrowserProxyOnNode } from "./chrome-create-wuAK5Pnq.mjs";
4
+ import { asRecord, filterStringEntries, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
5
5
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
6
6
  import { MeetingPlatformAdapter } from "openclaw/plugin-sdk/meeting-runtime";
7
7
  import { createMeetingBrowserAudioCaptureSource } from "openclaw/plugin-sdk/meeting-page-script-runtime";
8
8
  //#region extensions/google-meet/src/transports/types.ts
9
9
  const GOOGLE_MEET_TRANSCRIPT_MAX_LINES = 2e3;
10
10
  //#endregion
11
- //#region extensions/google-meet/src/transports/google-meet-page-scripts.ts
11
+ //#region extensions/google-meet/src/transports/google-meet-caption-scripts.ts
12
+ const CAPTION_LINE_COMMIT_SOURCE = `
13
+ const captionSelf = (value) => value === true ? "self" : value === false ? "other" : "unknown";
14
+ const captionHistoryKey = (text, ownEcho) => JSON.stringify([text, captionSelf(ownEcho)]);
15
+ const captionObservedEcho = (entry) => entry.provenance?.self === "self" ? true : entry.provenance?.self === "other" ? false : undefined;
16
+ const captionOriginMatches = (entry, row) => {
17
+ if (entry.node === row.node && entry.speaker === row.speaker) return true;
18
+ const previous = captionObservedEcho(entry);
19
+ return previous === undefined || row.ownEcho === undefined || previous === row.ownEcho;
20
+ };
21
+ const captionProvenance = (state, row) => Object.freeze({
22
+ observer: "google-meet-caption-dom",
23
+ observationId: (state.sessionId || "unscoped") + ":" + state.epoch + ":observation:" + (state.nextObservationId = (state.nextObservationId || 0) + 1),
24
+ ...(state.sessionId ? { sessionId: state.sessionId } : {}),
25
+ epoch: state.epoch,
26
+ observedAt: new Date(Date.now()).toISOString(),
27
+ ...(row.speaker ? { speaker: row.speaker } : {}),
28
+ self: captionSelf(row.ownEcho)
29
+ });
30
+ const captionLine = (entry) => ({
31
+ at: entry.at,
32
+ speaker: entry.speaker,
33
+ text: entry.text,
34
+ ...(entry.source ? { source: { ...entry.source } } : {}),
35
+ ...(entry.provenance ? { provenance: { ...entry.provenance } } : {})
36
+ });
37
+ const rememberCaptionSource = (state, entry) => {
38
+ if (!entry.source || !state.sourceHistory) return;
39
+ state.sourceRevisions?.set(entry.source.id, Math.max(state.sourceRevisions.get(entry.source.id) || 0, Number(entry.source.revision)));
40
+ // Remember every retained native origin without weakening its sticky source authority.
41
+ const key = captionHistoryKey(entry.text, captionObservedEcho(entry));
42
+ if (state.sourceHistory.has(key) || state.sourceHistory.size < ${GOOGLE_MEET_TRANSCRIPT_MAX_LINES}) {
43
+ state.sourceHistory.set(key, { ...entry.source });
44
+ }
45
+ };
46
+ const reviseCaptionSource = (state, entry, changes) => {
47
+ if (!entry.source) return;
48
+ const revision = Number(entry.source.revision);
49
+ if (revision < (state.sourceRevisions?.get(entry.source.id) || 0)) {
50
+ entry.source = undefined;
51
+ return;
52
+ }
53
+ entry.source = { ...entry.source, ...changes, revision: String(revision + 1) };
54
+ };
55
+ const commitLines = (state, entries, stillVisible = []) => {
56
+ state.lines = Array.isArray(state.lines) ? state.lines : [];
57
+ const liveSources = new Set(stillVisible.flatMap((entry) => entry.source ? [entry.source.id] : []));
58
+ for (const entry of entries) {
59
+ if (entry.source && Number(entry.source.revision) < (state.sourceRevisions?.get(entry.source.id) || 0)) entry.source = undefined;
60
+ if (entry.source && !entry.source.finalized && !liveSources.has(entry.source.id)) {
61
+ reviseCaptionSource(state, entry, { finalized: true });
62
+ }
63
+ rememberCaptionSource(state, entry);
64
+ state.lines.push(captionLine(entry));
65
+ }
66
+ const excess = state.lines.length - ${GOOGLE_MEET_TRANSCRIPT_MAX_LINES};
67
+ if (excess > 0) {
68
+ state.lines.splice(0, excess);
69
+ state.droppedLines = (state.droppedLines || 0) + excess;
70
+ }
71
+ };
72
+ `;
73
+ function meetTranscriptScript(meetingUrl, meetingSessionId, finalize) {
74
+ const expectedMeetingUrl = normalizeMeetUrlForReuse(meetingUrl);
75
+ return `() => {
76
+ const expectedMeetingUrl = ${JSON.stringify(expectedMeetingUrl)};
77
+ const expectedSessionId = ${JSON.stringify(meetingSessionId)};
78
+ let currentMeetingUrl;
79
+ try {
80
+ const currentUrl = new URL(location.href);
81
+ currentMeetingUrl = currentUrl.origin + currentUrl.pathname.toLowerCase().replace(/\\/$/, "");
82
+ } catch {
83
+ return JSON.stringify({ urlMatched: false });
84
+ }
85
+ if (!expectedMeetingUrl || currentMeetingUrl !== expectedMeetingUrl) {
86
+ return JSON.stringify({ urlMatched: false });
87
+ }
88
+ const state = window.__openclawMeetCaptions;
89
+ ${CAPTION_LINE_COMMIT_SOURCE}
90
+ if (state?.sessionId && state.sessionId !== expectedSessionId) {
91
+ return JSON.stringify({ urlMatched: true, sessionMatched: false });
92
+ }
93
+ if (${JSON.stringify(finalize)} && Array.isArray(state?.visible) && state.visible.length > 0) {
94
+ if (state.settleTimer !== undefined) clearTimeout(state.settleTimer);
95
+ state.settleTimer = undefined;
96
+ commitLines(state, state.visible);
97
+ state.visible = [];
98
+ }
99
+ const lines = Array.isArray(state?.lines) ? state.lines : [];
100
+ return JSON.stringify({
101
+ urlMatched: true,
102
+ sessionMatched: true,
103
+ epoch: typeof state?.epoch === "string" ? state.epoch : undefined,
104
+ droppedLines: Number.isFinite(state?.droppedLines) ? Math.max(0, Math.trunc(state.droppedLines)) : 0,
105
+ lines: lines.map((line) => ({
106
+ at: typeof line?.at === "string" ? line.at : undefined,
107
+ speaker: typeof line?.speaker === "string" ? line.speaker : undefined,
108
+ text: typeof line?.text === "string" ? line.text : "",
109
+ ...(line?.source ? { source: { ...line.source } } : {}),
110
+ ...(line?.provenance ? { provenance: { ...line.provenance } } : {})
111
+ })).filter((line) => line.text),
112
+ pendingLines: (Array.isArray(state?.visible) ? state.visible : []).map(captionLine)
113
+ });
114
+ }`;
115
+ }
116
+ //#endregion
117
+ //#region extensions/google-meet/src/transports/google-meet-caption-observer-source.ts
12
118
  const GOOGLE_MEET_CAPTION_SETTLE_MS = 1e3;
119
+ const GOOGLE_MEET_CAPTION_OBSERVER_SOURCE = `
120
+ let captioning = false;
121
+ let captionsEnabledAttempted = false;
122
+ let transcriptLines = 0;
123
+ let lastCaptionAt;
124
+ let lastCaptionSpeaker;
125
+ let lastCaptionText;
126
+ let recentTranscript = [];
127
+ const captionSelector = '[role="region"][aria-label*="aption" i], [aria-live="polite"][role="region"], div[aria-live="polite"]';
128
+ const captionState = (() => {
129
+ if (!captureCaptions) return undefined;
130
+ const w = window;
131
+ if (!inCall && !w.__openclawMeetCaptions) return undefined;
132
+ // A reused tab starts a fresh logical transcript for each OpenClaw session.
133
+ // Status refreshes omit the id, so they preserve the active page-owned buffer.
134
+ if (!w.__openclawMeetCaptions || (captionSessionId && w.__openclawMeetCaptions.sessionId !== captionSessionId)) {
135
+ if (w.__openclawMeetCaptions?.settleTimer !== undefined) {
136
+ clearTimeout(w.__openclawMeetCaptions.settleTimer);
137
+ }
138
+ w.__openclawMeetCaptions?.observer?.disconnect?.();
139
+ w.__openclawMeetCaptions = {
140
+ sessionId: captionSessionId,
141
+ // Epochs cross document lifetimes in the runtime transcript cursor.
142
+ // Strong UUIDs keep a reloaded page distinct from its prior buffer.
143
+ epoch: crypto.randomUUID(),
144
+ enabledAttempted: false,
145
+ observerInstalled: false,
146
+ observer: undefined,
147
+ droppedLines: 0,
148
+ nextSourceId: 0,
149
+ nextObservationId: 0,
150
+ sourceNodes: new WeakMap(),
151
+ sourceHistory: new Map(),
152
+ sourceRevisions: new Map(),
153
+ lines: [],
154
+ settleTimer: undefined,
155
+ visible: []
156
+ };
157
+ }
158
+ return w.__openclawMeetCaptions;
159
+ })();
160
+ const normalizeCaption = (speaker, captionText) => {
161
+ if (!captionState) return;
162
+ const clean = String(captionText || "").replace(/\\s+/g, " ").trim();
163
+ const cleanSpeaker = String(speaker || "").replace(/\\s+/g, " ").trim();
164
+ if (!clean || clean.length < 2) return undefined;
165
+ if (/^(turn on captions|turn off captions|captions)$/i.test(clean)) return undefined;
166
+ return { speaker: cleanSpeaker || undefined, text: clean };
167
+ };
168
+ ${CAPTION_LINE_COMMIT_SOURCE}
169
+ const captionOwnEcho = (node) => {
170
+ const marked = node?.closest?.('[data-is-self]') || node;
171
+ const value = marked?.getAttribute?.("data-is-self");
172
+ return value === "true" ? true : value === "false" ? false : undefined;
173
+ };
174
+ const sourceForCaption = (row, continuing) => {
175
+ // Text is only a replay lookup, never the minted identity. Keep the original
176
+ // handle for ambiguous repeated utterances; their transcript entries remain.
177
+ const known = captionState.sourceNodes?.get(row.node);
178
+ const sameLifecycle = known && captionOriginMatches(known, row) && (continuing || known.text === row.text ||
179
+ known.text.startsWith(row.text) || row.text.startsWith(known.text));
180
+ if (sameLifecycle && !known.source) return undefined;
181
+ if (sameLifecycle && known.text !== row.text && known.text.startsWith(row.text)) return undefined;
182
+ const remembered = sameLifecycle ? known.source : captionState.sourceHistory?.get(captionHistoryKey(row.text, row.ownEcho));
183
+ if (remembered) {
184
+ if (Number(remembered.revision) < (captionState.sourceRevisions?.get(remembered.id) || 0)) return undefined;
185
+ return sameLifecycle && known.text !== row.text
186
+ ? { ...remembered, revision: String(Number(remembered.revision) + 1), finalized: false }
187
+ : { ...remembered };
188
+ }
189
+ // Missing attribution cannot turn remembered speech into fresh authority.
190
+ const unknown = captionState.sourceHistory?.get(captionHistoryKey(row.text, undefined));
191
+ // A text correction does not resolve the native attribution of its older observation.
192
+ if (unknown || (row.ownEcho === undefined && [true, false].some((self) => captionState.sourceHistory?.has(captionHistoryKey(row.text, self))))) return undefined;
193
+ // At the replay bound, retain row provenance but do not mint new authority.
194
+ if (!captionState.sessionId || !captionState.sourceHistory || row.text.length > 16_384 || captionState.sourceHistory.size >= ${GOOGLE_MEET_TRANSCRIPT_MAX_LINES}) return undefined;
195
+ captionState.nextSourceId += 1;
196
+ return {
197
+ id: captionState.sessionId + ":" + captionState.epoch + ":" + captionState.nextSourceId,
198
+ epoch: captionState.epoch,
199
+ revision: "1",
200
+ finalized: false,
201
+ ...(row.ownEcho === undefined ? {} : { ownEcho: row.ownEcho })
202
+ };
203
+ };
204
+ const scrapeCaptions = () => {
205
+ if (!captionState) return;
206
+ const regions = [...document.querySelectorAll(captionSelector)];
207
+ const rows = [];
208
+ for (const region of regions) {
209
+ const raw = text(region);
210
+ if (!raw) continue;
211
+ const pieces = raw.split(/\\n+/).map((part) => part.trim()).filter(Boolean);
212
+ const row = pieces.length >= 2
213
+ ? normalizeCaption(pieces[0], pieces.slice(1).join(" "))
214
+ : normalizeCaption("", pieces[0] || raw);
215
+ if (row) rows.push({ ...row, node: region, ownEcho: captionOwnEcho(region) });
216
+ }
217
+ if (rows.length === 0) {
218
+ // Meet briefly removes caption rows while rerendering. Keep them mutable
219
+ // for one settle window so a DOM gap cannot fabricate a repeated line.
220
+ if (captionState.visible.length > 0 && captionState.settleTimer === undefined) {
221
+ const pendingState = captionState;
222
+ pendingState.settleTimer = setTimeout(() => {
223
+ if (window.__openclawMeetCaptions !== pendingState) return;
224
+ commitLines(pendingState, pendingState.visible);
225
+ pendingState.visible = [];
226
+ pendingState.settleTimer = undefined;
227
+ }, ${GOOGLE_MEET_CAPTION_SETTLE_MS});
228
+ }
229
+ return;
230
+ }
231
+ if (captionState.settleTimer !== undefined) {
232
+ clearTimeout(captionState.settleTimer);
233
+ captionState.settleTimer = undefined;
234
+ }
235
+ const previous = Array.isArray(captionState.visible) ? captionState.visible : [];
236
+ const unmatchedPrevious = [...previous];
237
+ const nextVisible = [];
238
+ const retired = [];
239
+ const now = Date.now();
240
+ for (let index = 0; index < rows.length; index += 1) {
241
+ const row = rows[index];
242
+ const sameNodeIndex = unmatchedPrevious.findIndex((candidate) => candidate.node === row.node);
243
+ const matchesPrior = (candidate) => {
244
+ const sameTextLifecycle =
245
+ candidate.text === row.text ||
246
+ row.text.startsWith(candidate.text) ||
247
+ candidate.text.startsWith(row.text);
248
+ const sameDomLifecycle =
249
+ candidate.node === row.node || now - candidate.seenAt <= ${GOOGLE_MEET_CAPTION_SETTLE_MS};
250
+ return candidate.speaker === row.speaker && captionOriginMatches(candidate, row) && sameTextLifecycle && sameDomLifecycle;
251
+ };
252
+ const priorIndex = sameNodeIndex >= 0
253
+ ? (matchesPrior(unmatchedPrevious[sameNodeIndex]) ? sameNodeIndex : -1)
254
+ : unmatchedPrevious.findIndex(matchesPrior);
255
+ const prior = priorIndex >= 0 ? unmatchedPrevious.splice(priorIndex, 1)[0] : undefined;
256
+ if (prior) {
257
+ if (prior.source && Number(prior.source.revision) < (captionState.sourceRevisions?.get(prior.source.id) || 0)) prior.source = undefined;
258
+ // A shortening often accompanies a DOM redraw; preserve the fuller line.
259
+ const changed = prior.text !== row.text && !prior.text.startsWith(row.text);
260
+ const ownEcho = prior.source?.ownEcho === true || row.ownEcho === true
261
+ ? true : prior.source?.ownEcho ?? row.ownEcho;
262
+ if (prior.source && (changed || ownEcho !== prior.source.ownEcho)) {
263
+ reviseCaptionSource(captionState, prior, { finalized: changed ? false : prior.source.finalized, ...(ownEcho === undefined ? {} : { ownEcho }) });
264
+ }
265
+ if (changed || prior.node !== row.node || prior.provenance?.self !== captionSelf(row.ownEcho)) prior.provenance = captionProvenance(captionState, row);
266
+ if (changed) prior.text = row.text;
267
+ prior.node = row.node;
268
+ prior.seenAt = now;
269
+ captionState.sourceNodes?.set(row.node, prior);
270
+ rememberCaptionSource(captionState, prior);
271
+ nextVisible.push(prior);
272
+ continue;
273
+ }
274
+ // Keep the transcript's existing non-prefix line boundary, while a reused
275
+ // live DOM row cannot turn a correction into a fresh participation source.
276
+ const continuingIndex = unmatchedPrevious.findIndex((candidate) => candidate.node === row.node);
277
+ const continuing = continuingIndex >= 0 ? unmatchedPrevious.splice(continuingIndex, 1)[0] : undefined;
278
+ if (continuing) retired.push(continuing);
279
+ const entry = {
280
+ at: new Date().toISOString(),
281
+ node: row.node,
282
+ seenAt: now,
283
+ speaker: row.speaker,
284
+ text: row.text,
285
+ source: sourceForCaption(row, Boolean(continuing)),
286
+ provenance: captionProvenance(captionState, row)
287
+ };
288
+ const ownEcho = entry.source?.ownEcho === true || row.ownEcho === true
289
+ ? true : entry.source?.ownEcho ?? row.ownEcho;
290
+ if (entry.source && ownEcho !== entry.source.ownEcho) {
291
+ reviseCaptionSource(captionState, entry, { ownEcho });
292
+ }
293
+ captionState.sourceNodes?.set(row.node, entry);
294
+ rememberCaptionSource(captionState, entry);
295
+ nextVisible.push(entry);
296
+ }
297
+ commitLines(captionState, [...retired, ...unmatchedPrevious], nextVisible);
298
+ captionState.visible = nextVisible;
299
+ };
300
+ if (captionState) {
301
+ if (!readOnly && inCall && !captionState.enabledAttempted) {
302
+ const captionButton = findButton(/turn on captions|show captions|captions/i);
303
+ const captionLabel = captionButton ? (captionButton.getAttribute("aria-label") || captionButton.getAttribute("data-tooltip") || text(captionButton)) : "";
304
+ if (captionButton) {
305
+ captionState.enabledAttempted = true;
306
+ captionsEnabledAttempted = true;
307
+ if (!/turn off captions|hide captions/i.test(captionLabel)) {
308
+ captionButton.click();
309
+ notes.push("Attempted to enable Meet captions for observe-only transcript health.");
310
+ }
311
+ }
312
+ } else if (captionState.enabledAttempted) {
313
+ captionsEnabledAttempted = true;
314
+ }
315
+ if (inCall && !captionState.observerInstalled) {
316
+ captionState.observerInstalled = true;
317
+ captionState.observer = new MutationObserver(scrapeCaptions);
318
+ captionState.observer.observe(document.body, {
319
+ childList: true,
320
+ subtree: true,
321
+ characterData: true,
322
+ attributes: true,
323
+ attributeFilter: ["data-is-self"]
324
+ });
325
+ notes.push("Installed Meet caption observer for observe-only transcript health.");
326
+ }
327
+ if (inCall) {
328
+ scrapeCaptions();
329
+ }
330
+ const committedLines = Array.isArray(captionState.lines) ? captionState.lines : [];
331
+ const visibleLines = Array.isArray(captionState.visible) ? captionState.visible : [];
332
+ const lines = [...committedLines, ...visibleLines];
333
+ const last = lines[lines.length - 1];
334
+ captioning = document.querySelector(captionSelector) !== null || lines.length > 0;
335
+ transcriptLines = (captionState.droppedLines || 0) + lines.length;
336
+ lastCaptionAt = last?.at;
337
+ lastCaptionSpeaker = last?.speaker;
338
+ lastCaptionText = last?.text;
339
+ recentTranscript = lines.slice(-5).map(captionLine);
340
+ }
341
+ `;
342
+ //#endregion
343
+ //#region extensions/google-meet/src/transports/google-meet-page-scripts.ts
13
344
  function meetAudioCaptureScript(params) {
14
345
  return createMeetingBrowserAudioCaptureSource({
15
346
  ...params,
@@ -338,179 +669,7 @@ function meetStatusScript(params) {
338
669
  if (inCall) {
339
670
  await routeMeetAudioOutput();
340
671
  }
341
- let captioning = false;
342
- let captionsEnabledAttempted = false;
343
- let transcriptLines = 0;
344
- let lastCaptionAt;
345
- let lastCaptionSpeaker;
346
- let lastCaptionText;
347
- let recentTranscript = [];
348
- const captionSelector = '[role="region"][aria-label*="aption" i], [aria-live="polite"][role="region"], div[aria-live="polite"]';
349
- const captionState = (() => {
350
- if (!captureCaptions) return undefined;
351
- const w = window;
352
- if (!inCall && !w.__openclawMeetCaptions) return undefined;
353
- // A reused tab starts a fresh logical transcript for each OpenClaw session.
354
- // Status refreshes omit the id, so they preserve the active page-owned buffer.
355
- if (!w.__openclawMeetCaptions || (captionSessionId && w.__openclawMeetCaptions.sessionId !== captionSessionId)) {
356
- if (w.__openclawMeetCaptions?.settleTimer !== undefined) {
357
- clearTimeout(w.__openclawMeetCaptions.settleTimer);
358
- }
359
- w.__openclawMeetCaptions?.observer?.disconnect?.();
360
- w.__openclawMeetCaptions = {
361
- sessionId: captionSessionId,
362
- // Epochs cross document lifetimes in the runtime transcript cursor.
363
- // Strong UUIDs keep a reloaded page distinct from its prior buffer.
364
- epoch: crypto.randomUUID(),
365
- enabledAttempted: false,
366
- observerInstalled: false,
367
- observer: undefined,
368
- droppedLines: 0,
369
- lines: [],
370
- settleTimer: undefined,
371
- visible: []
372
- };
373
- }
374
- return w.__openclawMeetCaptions;
375
- })();
376
- const normalizeCaption = (speaker, captionText) => {
377
- if (!captionState) return;
378
- const clean = String(captionText || "").replace(/\\s+/g, " ").trim();
379
- const cleanSpeaker = String(speaker || "").replace(/\\s+/g, " ").trim();
380
- if (!clean || clean.length < 2) return undefined;
381
- if (/^(turn on captions|turn off captions|captions)$/i.test(clean)) return undefined;
382
- return { speaker: cleanSpeaker || undefined, text: clean };
383
- };
384
- const commitLines = (state, entries) => {
385
- state.lines.push(...entries.map((entry) => ({
386
- at: entry.at,
387
- speaker: entry.speaker,
388
- text: entry.text
389
- })));
390
- const excess = state.lines.length - ${GOOGLE_MEET_TRANSCRIPT_MAX_LINES};
391
- if (excess > 0) {
392
- state.lines.splice(0, excess);
393
- state.droppedLines = (state.droppedLines || 0) + excess;
394
- }
395
- };
396
- const scrapeCaptions = () => {
397
- if (!captionState) return;
398
- const regions = [...document.querySelectorAll(captionSelector)];
399
- const rows = [];
400
- for (const region of regions) {
401
- const raw = text(region);
402
- if (!raw) continue;
403
- const pieces = raw.split(/\\n+/).map((part) => part.trim()).filter(Boolean);
404
- const row = pieces.length >= 2
405
- ? normalizeCaption(pieces[0], pieces.slice(1).join(" "))
406
- : normalizeCaption("", pieces[0] || raw);
407
- if (row) rows.push({ ...row, node: region });
408
- }
409
- if (rows.length === 0) {
410
- // Meet briefly removes caption rows while rerendering. Keep them mutable
411
- // for one settle window so a DOM gap cannot fabricate a repeated line.
412
- if (captionState.visible.length > 0 && captionState.settleTimer === undefined) {
413
- const pendingState = captionState;
414
- pendingState.settleTimer = setTimeout(() => {
415
- if (window.__openclawMeetCaptions !== pendingState) return;
416
- commitLines(pendingState, pendingState.visible);
417
- pendingState.visible = [];
418
- pendingState.settleTimer = undefined;
419
- }, ${GOOGLE_MEET_CAPTION_SETTLE_MS});
420
- }
421
- return;
422
- }
423
- if (captionState.settleTimer !== undefined) {
424
- clearTimeout(captionState.settleTimer);
425
- captionState.settleTimer = undefined;
426
- }
427
- const previous = Array.isArray(captionState.visible) ? captionState.visible : [];
428
- const unmatchedPrevious = [...previous];
429
- const nextVisible = [];
430
- const now = Date.now();
431
- for (let index = 0; index < rows.length; index += 1) {
432
- const row = rows[index];
433
- const priorIndex = unmatchedPrevious.findIndex((candidate) => {
434
- const sameTextLifecycle =
435
- candidate.text === row.text ||
436
- row.text.startsWith(candidate.text) ||
437
- candidate.text.startsWith(row.text);
438
- const sameDomLifecycle =
439
- candidate.node === row.node || now - candidate.seenAt <= ${GOOGLE_MEET_CAPTION_SETTLE_MS};
440
- return candidate.speaker === row.speaker && sameTextLifecycle && sameDomLifecycle;
441
- });
442
- const prior = priorIndex >= 0 ? unmatchedPrevious.splice(priorIndex, 1)[0] : undefined;
443
- const sameSpeaker = Boolean(prior) && prior.speaker === row.speaker;
444
- if (sameSpeaker && prior.text === row.text) {
445
- prior.node = row.node;
446
- prior.seenAt = now;
447
- nextVisible.push(prior);
448
- continue;
449
- }
450
- if (sameSpeaker && row.text.startsWith(prior.text)) {
451
- prior.text = row.text;
452
- prior.node = row.node;
453
- prior.seenAt = now;
454
- nextVisible.push(prior);
455
- continue;
456
- }
457
- if (sameSpeaker && prior.text.startsWith(row.text)) {
458
- prior.node = row.node;
459
- prior.seenAt = now;
460
- nextVisible.push(prior);
461
- continue;
462
- }
463
- const entry = {
464
- at: new Date().toISOString(),
465
- node: row.node,
466
- seenAt: now,
467
- speaker: row.speaker,
468
- text: row.text
469
- };
470
- nextVisible.push(entry);
471
- }
472
- commitLines(captionState, unmatchedPrevious);
473
- captionState.visible = nextVisible;
474
- };
475
- if (captionState) {
476
- if (!readOnly && inCall && !captionState.enabledAttempted) {
477
- const captionButton = findButton(/turn on captions|show captions|captions/i);
478
- const captionLabel = captionButton ? (captionButton.getAttribute("aria-label") || captionButton.getAttribute("data-tooltip") || text(captionButton)) : "";
479
- if (captionButton) {
480
- captionState.enabledAttempted = true;
481
- captionsEnabledAttempted = true;
482
- if (!/turn off captions|hide captions/i.test(captionLabel)) {
483
- captionButton.click();
484
- notes.push("Attempted to enable Meet captions for observe-only transcript health.");
485
- }
486
- }
487
- } else if (captionState.enabledAttempted) {
488
- captionsEnabledAttempted = true;
489
- }
490
- if (inCall && !captionState.observerInstalled) {
491
- captionState.observerInstalled = true;
492
- captionState.observer = new MutationObserver(scrapeCaptions);
493
- captionState.observer.observe(document.body, {
494
- childList: true,
495
- subtree: true,
496
- characterData: true
497
- });
498
- notes.push("Installed Meet caption observer for observe-only transcript health.");
499
- }
500
- if (inCall) {
501
- scrapeCaptions();
502
- }
503
- const committedLines = Array.isArray(captionState.lines) ? captionState.lines : [];
504
- const visibleLines = Array.isArray(captionState.visible) ? captionState.visible : [];
505
- const lines = [...committedLines, ...visibleLines];
506
- const last = lines[lines.length - 1];
507
- captioning = document.querySelector(captionSelector) !== null || lines.length > 0;
508
- transcriptLines = (captionState.droppedLines || 0) + lines.length;
509
- lastCaptionAt = last?.at;
510
- lastCaptionSpeaker = last?.speaker;
511
- lastCaptionText = last?.text;
512
- recentTranscript = lines.slice(-5);
513
- }
672
+ ${GOOGLE_MEET_CAPTION_OBSERVER_SOURCE}
514
673
  const lobbyWaiting = !inCall && /asking to be let in|you.?ll join when someone lets you in|waiting to be let in|ask to join/i.test(pageText);
515
674
  const leaveReason = !inCall && /you left the meeting|you.?ve left the meeting|removed from the meeting|you were removed|call ended|meeting ended/i.test(pageText)
516
675
  ? pageText.match(/you left the meeting|you.?ve left the meeting|removed from the meeting|you were removed|call ended|meeting ended/i)?.[0]
@@ -559,55 +718,6 @@ function meetStatusScript(params) {
559
718
  });
560
719
  }`;
561
720
  }
562
- function meetTranscriptScript(meetingUrl, meetingSessionId, finalize) {
563
- const expectedMeetingUrl = normalizeMeetUrlForReuse(meetingUrl);
564
- return `() => {
565
- const expectedMeetingUrl = ${JSON.stringify(expectedMeetingUrl)};
566
- const expectedSessionId = ${JSON.stringify(meetingSessionId)};
567
- let currentMeetingUrl;
568
- try {
569
- const currentUrl = new URL(location.href);
570
- currentMeetingUrl = currentUrl.origin + currentUrl.pathname.toLowerCase().replace(/\\/$/, "");
571
- } catch {
572
- return JSON.stringify({ urlMatched: false });
573
- }
574
- if (!expectedMeetingUrl || currentMeetingUrl !== expectedMeetingUrl) {
575
- return JSON.stringify({ urlMatched: false });
576
- }
577
- const state = window.__openclawMeetCaptions;
578
- if (state?.sessionId && state.sessionId !== expectedSessionId) {
579
- return JSON.stringify({ urlMatched: true, sessionMatched: false });
580
- }
581
- if (${JSON.stringify(finalize)} && Array.isArray(state?.visible) && state.visible.length > 0) {
582
- if (state.settleTimer !== undefined) clearTimeout(state.settleTimer);
583
- state.settleTimer = undefined;
584
- state.lines = Array.isArray(state.lines) ? state.lines : [];
585
- state.lines.push(...state.visible.map((entry) => ({
586
- at: entry.at,
587
- speaker: entry.speaker,
588
- text: entry.text
589
- })));
590
- state.visible = [];
591
- const excess = state.lines.length - ${GOOGLE_MEET_TRANSCRIPT_MAX_LINES};
592
- if (excess > 0) {
593
- state.lines.splice(0, excess);
594
- state.droppedLines = (state.droppedLines || 0) + excess;
595
- }
596
- }
597
- const lines = Array.isArray(state?.lines) ? state.lines : [];
598
- return JSON.stringify({
599
- urlMatched: true,
600
- sessionMatched: true,
601
- epoch: typeof state?.epoch === "string" ? state.epoch : undefined,
602
- droppedLines: Number.isFinite(state?.droppedLines) ? Math.max(0, Math.trunc(state.droppedLines)) : 0,
603
- lines: lines.map((line) => ({
604
- at: typeof line?.at === "string" ? line.at : undefined,
605
- speaker: typeof line?.speaker === "string" ? line.speaker : undefined,
606
- text: typeof line?.text === "string" ? line.text : ""
607
- })).filter((line) => line.text)
608
- });
609
- }`;
610
- }
611
721
  function meetLeaveScript(meetingUrl) {
612
722
  const expectedMeetingUrl = normalizeMeetUrlForReuse(meetingUrl);
613
723
  return `() => {
@@ -700,8 +810,7 @@ function prefixDtmfWait(sequence, delayMs) {
700
810
  //#endregion
701
811
  //#region extensions/google-meet/src/transports/google-meet-platform-adapter.ts
702
812
  function parsePermissionGrantNotes(result) {
703
- const record = result && typeof result === "object" ? result : {};
704
- const unsupportedPermissions = Array.isArray(record.unsupportedPermissions) ? record.unsupportedPermissions.filter((value) => typeof value === "string") : [];
813
+ const unsupportedPermissions = filterStringEntries(asRecord(result).unsupportedPermissions);
705
814
  const notes = ["Granted Meet microphone/camera permissions through browser control."];
706
815
  if (unsupportedPermissions.includes("speakerSelection")) notes.push("Chrome did not accept the optional Meet speaker-selection permission.");
707
816
  return notes;
@@ -10,9 +10,6 @@ var GoogleMeetBrowserManualActionError = class extends Error {
10
10
  };
11
11
  }
12
12
  };
13
- function isGoogleMeetBrowserManualActionError(error) {
14
- return error instanceof GoogleMeetBrowserManualActionError;
15
- }
16
13
  //#endregion
17
14
  //#region extensions/google-meet/src/transports/google-meet-platform-constants.ts
18
15
  const GOOGLE_MEET_NODE_COMMAND = "googlemeet.chrome";
@@ -22,4 +19,4 @@ const GOOGLE_MEET_BROWSER_NODE_ADAPTER = {
22
19
  nodeConfigPath: "plugins.entries.google-meet.config.chromeNode.node"
23
20
  };
24
21
  //#endregion
25
- export { isGoogleMeetBrowserManualActionError as i, GOOGLE_MEET_NODE_COMMAND as n, GoogleMeetBrowserManualActionError as r, GOOGLE_MEET_BROWSER_NODE_ADAPTER as t };
22
+ export { GOOGLE_MEET_NODE_COMMAND as n, GoogleMeetBrowserManualActionError as r, GOOGLE_MEET_BROWSER_NODE_ADAPTER as t };
@@ -1,6 +1,6 @@
1
- import { n as GOOGLE_MEET_NODE_COMMAND } from "./google-meet-platform-constants-Bs5iAg3E.mjs";
2
- import { n as DEFAULT_GOOGLE_MEET_AUDIO_OUTPUT_COMMAND, t as DEFAULT_GOOGLE_MEET_AUDIO_INPUT_COMMAND } from "./config-C-6enWkx.mjs";
3
- import { t as GOOGLE_MEET_PLATFORM_ADAPTER } from "./google-meet-platform-adapter-DQZ74Kd5.mjs";
1
+ import { n as GOOGLE_MEET_NODE_COMMAND } from "./google-meet-platform-constants-CnRfrYX7.mjs";
2
+ import { n as DEFAULT_GOOGLE_MEET_AUDIO_OUTPUT_COMMAND, t as DEFAULT_GOOGLE_MEET_AUDIO_INPUT_COMMAND } from "./config-BXGAMntj.mjs";
3
+ import { t as GOOGLE_MEET_PLATFORM_ADAPTER } from "./google-meet-platform-adapter-gosgbMIM.mjs";
4
4
  import { MeetingPlatformAdapter } from "openclaw/plugin-sdk/meeting-runtime";
5
5
  //#region extensions/google-meet/src/node-host.ts
6
6
  function normalizeMeetKey(value) {
@@ -1,5 +1,5 @@
1
- import { n as GOOGLE_MEET_NODE_COMMAND } from "./google-meet-platform-constants-Bs5iAg3E.mjs";
2
- import { t as GOOGLE_MEET_PLATFORM_ADAPTER } from "./google-meet-platform-adapter-DQZ74Kd5.mjs";
1
+ import { n as GOOGLE_MEET_NODE_COMMAND } from "./google-meet-platform-constants-CnRfrYX7.mjs";
2
+ import { t as GOOGLE_MEET_PLATFORM_ADAPTER } from "./google-meet-platform-adapter-gosgbMIM.mjs";
3
3
  import { createMeetingBrowserNodeInvokePolicy } from "openclaw/plugin-sdk/meeting-runtime";
4
4
  //#region extensions/google-meet/src/node-invoke-policy.ts
5
5
  const GOOGLE_MEET_CHROME_NODE_COMMAND = GOOGLE_MEET_NODE_COMMAND;
@@ -1,8 +1,8 @@
1
- import { n as readGoogleApiErrorDetail } from "./google-api-errors-BDYXfKRa.mjs";
1
+ import { g as readGoogleApiErrorDetail } from "./plugin-helpers-CdQ_YNA7.mjs";
2
2
  import { MAX_DATE_TIMESTAMP_MS, resolveDateTimestampMs, resolveExpiresAtMsFromDurationSeconds } from "openclaw/plugin-sdk/number-runtime";
3
- import { parseOAuthCallbackInput, waitForLocalOAuthCallback } from "openclaw/plugin-sdk/provider-auth-runtime";
4
3
  import { readProviderJsonResponse } from "openclaw/plugin-sdk/provider-http";
5
4
  import { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime";
5
+ import { parseOAuthCallbackInput, waitForLocalOAuthCallback } from "openclaw/plugin-sdk/provider-auth-runtime";
6
6
  //#region extensions/google-meet/src/oauth.ts
7
7
  const GOOGLE_MEET_REDIRECT_URI = "http://localhost:8085/oauth2callback";
8
8
  const GOOGLE_MEET_AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";