@openclaw/google-meet 2026.7.2-beta.3 → 2026.7.2-beta.5

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/dist/index.js CHANGED
@@ -1,23 +1,24 @@
1
1
  import { a as buildGoogleMeetCalendarDayWindow, c as normalizeMeetUrl, i as resolveGoogleMeetGatewayOperationTimeoutMs, n as DEFAULT_GOOGLE_MEET_AUDIO_OUTPUT_COMMAND, o as findGoogleMeetCalendarEvent, r as resolveGoogleMeetConfig, s as listGoogleMeetCalendarEvents, t as DEFAULT_GOOGLE_MEET_AUDIO_INPUT_COMMAND } from "./config-BcaS1BDr.js";
2
- import { _ as fetchGoogleMeetAttendance, a as isRecoverableMeetTab, c as readMeetAuthUser, d as resolveChromeNodeInfo, f as GOOGLE_MEET_NODE_COMMAND, g as fetchGoogleMeetArtifacts, h as endGoogleMeetActiveConference, i as isEnglishMeetTab, l as callBrowserProxyOnNode, n as isGoogleMeetBrowserManualActionError, o as isSameMeetUrlForReuse, p as buildGoogleMeetPreflightReport, r as forceMeetEnglishUi, s as normalizeMeetUrlForReuse, t as createMeetWithBrowserProxyOnNode, u as resolveChromeNode, v as fetchGoogleMeetSpace, y as fetchLatestGoogleMeetConferenceRecord } from "./chrome-create-CBdUFMBH.js";
2
+ import { t as GOOGLE_MEET_CLI_DESCRIPTOR } from "./cli-output-mode-BwH_6tH8.js";
3
+ import { _ as endGoogleMeetActiveConference, a as isRecoverableMeetTab, c as readMeetAuthUser, d as resolveChromeNodeInfo, f as GOOGLE_MEET_NODE_COMMAND, h as fetchGoogleMeetAttendance, i as isEnglishMeetTab, l as callBrowserProxyOnNode, m as fetchGoogleMeetArtifacts, n as isGoogleMeetBrowserManualActionError, o as isSameMeetUrlForReuse, p as buildGoogleMeetPreflightReport, r as forceMeetEnglishUi, s as normalizeMeetUrlForReuse, t as createMeetWithBrowserProxyOnNode, u as resolveChromeNode, v as fetchGoogleMeetSpace, y as fetchLatestGoogleMeetConferenceRecord } from "./chrome-create-DMKruRFW.js";
3
4
  import { optionalPositiveIntegerSchema, readPositiveIntegerParam } from "openclaw/plugin-sdk/channel-actions";
4
- import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
5
5
  import { ErrorCodes, GatewayClient, callGatewayFromCli, errorShape, startGatewayClientWhenEventLoopReady } from "openclaw/plugin-sdk/gateway-runtime";
6
- import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
7
6
  import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
8
7
  import { normalizeAgentId, parseAgentSessionKey } from "openclaw/plugin-sdk/routing";
9
8
  import { asRecord, normalizeOptionalString, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
10
9
  import { jsonResult } from "openclaw/plugin-sdk/tool-results";
11
- import { Type } from "typebox";
10
+ import { createMeetingTranscriptSourceProvider, resolveTranscriptsConfig } from "openclaw/plugin-sdk/transcripts";
12
11
  import { isBlockedHostnameOrIp } from "openclaw/plugin-sdk/ssrf-runtime";
13
- import { MeetingSessionRuntime, addMeetingSetupCheck, consultMeetingAgent, createLocalMeetingRealtimeAudioTransport, createMeetingBrowserNodeInvokePolicy, createMeetingNodeHost, createMeetingSetupStatus, createMeetingVoiceCallGateway, createNodeMeetingRealtimeAudioTransport, endMeetingVoiceCallGatewayCall, getMeetingVoiceCallGatewayCall, handleMeetingRealtimeConsultToolCall, isMeetingVoiceCallMissingError, joinMeetingViaVoiceCallGateway, leaveMeetingWithBrowser, openMeetingWithBrowser, readMeetingTranscriptWithBrowser, recoverMeetingBrowserTab, resolveLocalMeetingBrowserRequest, resolveMeetingRealtimeTools, speakMeetingViaVoiceCallGateway, startMeetingAgentRealtimeEngine, startMeetingRealtimeEngine } from "openclaw/plugin-sdk/meeting-runtime";
14
- import { addTimerTimeoutGraceMs } from "openclaw/plugin-sdk/number-runtime";
15
12
  import { spawnSync } from "node:child_process";
16
- import { sleep } from "openclaw/plugin-sdk/runtime-env";
17
- import { randomUUID } from "node:crypto";
13
+ import { MeetingPlatformAdapter, MeetingSessionRuntime, addMeetingSetupCheck, createLocalMeetingRealtimeAudioTransport, createMeetingBrowserNodeInvokePolicy, createMeetingNodeHost, createMeetingRealtimeEngineBindings, createMeetingSession, createMeetingSetupStatus, createMeetingVoiceCallGateway, createNodeMeetingRealtimeAudioTransport, endMeetingVoiceCallGatewayCall, getMeetingVoiceCallGatewayCall, isMeetingVoiceCallMissingError, joinMeetingViaVoiceCallGateway, leaveMeetingWithBrowser, openMeetingWithBrowser, readMeetingTranscriptWithBrowser, recoverMeetingBrowserTab, resolveLocalMeetingBrowserRequest, speakMeetingViaVoiceCallGateway, startMeetingAgentRealtimeEngine, startMeetingRealtimeEngine } from "openclaw/plugin-sdk/meeting-runtime";
14
+ import { addTimerTimeoutGraceMs } from "openclaw/plugin-sdk/number-runtime";
15
+ import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
16
+ import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
17
+ import { resolveDefaultAgentId } from "openclaw/plugin-sdk/agent-runtime";
18
18
  import fs from "node:fs";
19
19
  import os from "node:os";
20
20
  import path from "node:path";
21
+ import { Type } from "typebox";
21
22
  //#region extensions/google-meet/src/transports/chrome-audio-device.ts
22
23
  const GOOGLE_MEET_SYSTEM_PROFILER_COMMAND = "/usr/sbin/system_profiler";
23
24
  function outputMentionsBlackHole2ch(output) {
@@ -32,6 +33,7 @@ const GOOGLE_MEET_CAPTION_SETTLE_MS = 1e3;
32
33
  function meetStatusScript(params) {
33
34
  return `async () => {
34
35
  const text = (node) => (node?.innerText || node?.textContent || "").trim();
36
+ const manualActionFor = (reason, message) => ({ reason, message });
35
37
  const allowMicrophone = ${JSON.stringify(params.allowMicrophone)};
36
38
  const captionSessionId = ${JSON.stringify(params.captionSessionId)};
37
39
  const captureCaptions = ${JSON.stringify(params.captureCaptions)};
@@ -330,27 +332,17 @@ function meetStatusScript(params) {
330
332
  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)
331
333
  ? pageText.match(/you left the meeting|you.?ve left the meeting|removed from the meeting|you were removed|call ended|meeting ended/i)?.[0]
332
334
  : undefined;
333
- let manualActionReason;
334
- let manualActionMessage;
335
+ let manualAction;
335
336
  if (!inCall && (host === "accounts.google.com" || /use your google account|to continue to google meet|choose an account|sign in to (join|continue)/i.test(pageText))) {
336
- manualActionReason = "google-login-required";
337
- manualActionMessage = "Sign in to Google in the OpenClaw browser profile, then retry the Meet join.";
337
+ manualAction = manualActionFor("google-login-required", "Sign in to Google in the OpenClaw browser profile, then retry the Meet join.");
338
338
  } else if (!inCall && joinElsewhere) {
339
- manualActionReason = "meet-session-conflict";
340
- manualActionMessage = "Meet is already active in another tab or device. Leave that session or reuse an English-pinned tab before retrying.";
339
+ manualAction = manualActionFor("meet-session-conflict", "Meet is already active in another tab or device. Leave that session or reuse an English-pinned tab before retrying.");
341
340
  } else if (!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)) {
342
- manualActionReason = "meet-admission-required";
343
- manualActionMessage = "Admit the OpenClaw browser participant in Google Meet, then retry speech.";
341
+ manualAction = manualActionFor("meet-admission-required", "Admit the OpenClaw browser participant in Google Meet, then retry speech.");
344
342
  } else if (permissionNeeded) {
345
- manualActionReason = "meet-permission-required";
346
- manualActionMessage = allowMicrophone
347
- ? "Allow microphone/camera/speaker permissions for Meet in the OpenClaw browser profile, then retry."
348
- : "Join without microphone/camera permissions in the OpenClaw browser profile, then retry.";
343
+ manualAction = manualActionFor("meet-permission-required", allowMicrophone ? "Allow microphone/camera/speaker permissions for Meet in the OpenClaw browser profile, then retry." : "Join without microphone/camera permissions in the OpenClaw browser profile, then retry.");
349
344
  } else if (!inCall && (allowMicrophone ? !microphoneChoice : !noMicrophoneChoice) && /do you want people to hear you in the meeting/i.test(pageText)) {
350
- manualActionReason = "meet-audio-choice-required";
351
- manualActionMessage = allowMicrophone
352
- ? "Meet is showing the microphone choice. Click Use microphone in the OpenClaw browser profile, then retry."
353
- : "Meet is showing the microphone choice. Choose the no-microphone option in the OpenClaw browser profile, then retry.";
345
+ manualAction = manualActionFor("meet-audio-choice-required", allowMicrophone ? "Meet is showing the microphone choice. Click Use microphone in the OpenClaw browser profile, then retry." : "Meet is showing the microphone choice. Choose the no-microphone option in the OpenClaw browser profile, then retry.");
354
346
  }
355
347
  return JSON.stringify({
356
348
  clickedJoin: Boolean(join),
@@ -369,9 +361,7 @@ function meetStatusScript(params) {
369
361
  audioOutputRouted,
370
362
  audioOutputDeviceLabel,
371
363
  audioOutputRouteError,
372
- manualActionRequired: Boolean(manualActionReason),
373
- manualActionReason,
374
- manualActionMessage,
364
+ manualAction,
375
365
  title: document.title,
376
366
  url: pageUrl,
377
367
  notes
@@ -518,42 +508,6 @@ function prefixDtmfWait(sequence, delayMs) {
518
508
  }
519
509
  //#endregion
520
510
  //#region extensions/google-meet/src/transports/google-meet-platform-adapter.ts
521
- function isGoogleMeetTalkBackMode$2(mode) {
522
- return mode === "agent" || mode === "bidi";
523
- }
524
- function parseMeetBrowserStatus(result) {
525
- const raw = (result && typeof result === "object" ? result : {}).result;
526
- if (typeof raw !== "string" || !raw.trim()) return;
527
- let parsed;
528
- try {
529
- parsed = JSON.parse(raw);
530
- } catch {
531
- throw new Error("Google Meet browser status JSON is malformed.");
532
- }
533
- return {
534
- inCall: parsed.inCall,
535
- micMuted: parsed.micMuted,
536
- lobbyWaiting: parsed.lobbyWaiting,
537
- leaveReason: parsed.leaveReason,
538
- captioning: parsed.captioning,
539
- captionsEnabledAttempted: parsed.captionsEnabledAttempted,
540
- transcriptLines: parsed.transcriptLines,
541
- lastCaptionAt: parsed.lastCaptionAt,
542
- lastCaptionSpeaker: parsed.lastCaptionSpeaker,
543
- lastCaptionText: parsed.lastCaptionText,
544
- recentTranscript: parsed.recentTranscript,
545
- audioOutputRouted: parsed.audioOutputRouted,
546
- audioOutputDeviceLabel: parsed.audioOutputDeviceLabel,
547
- audioOutputRouteError: parsed.audioOutputRouteError,
548
- manualActionRequired: parsed.manualActionRequired,
549
- manualActionReason: parsed.manualActionReason,
550
- manualActionMessage: parsed.manualActionMessage,
551
- browserUrl: parsed.url,
552
- browserTitle: parsed.title,
553
- status: "browser-control",
554
- notes: Array.isArray(parsed.notes) ? parsed.notes.filter((note) => typeof note === "string") : void 0
555
- };
556
- }
557
511
  function parsePermissionGrantNotes(result) {
558
512
  const record = result && typeof result === "object" ? result : {};
559
513
  const unsupportedPermissions = Array.isArray(record.unsupportedPermissions) ? record.unsupportedPermissions.filter((value) => typeof value === "string") : [];
@@ -561,67 +515,41 @@ function parsePermissionGrantNotes(result) {
561
515
  if (unsupportedPermissions.includes("speakerSelection")) notes.push("Chrome did not accept the optional Meet speaker-selection permission.");
562
516
  return notes;
563
517
  }
564
- function parseMeetTranscriptSnapshot(result) {
565
- const raw = (result && typeof result === "object" ? result : {}).result;
566
- if (typeof raw !== "string" || !raw.trim()) return {
567
- droppedLines: 0,
568
- lines: []
569
- };
570
- let parsed;
571
- try {
572
- parsed = JSON.parse(raw);
573
- } catch {
574
- throw new Error("Google Meet transcript JSON is malformed.");
575
- }
576
- if (!parsed || typeof parsed !== "object") throw new Error("Google Meet transcript payload is invalid.");
577
- const payload = parsed;
578
- const droppedLines = typeof payload.droppedLines === "number" && Number.isSafeInteger(payload.droppedLines) ? Math.max(0, payload.droppedLines) : 0;
579
- const lines = Array.isArray(payload.lines) ? payload.lines.flatMap((value) => {
580
- if (!value || typeof value !== "object") return [];
581
- const line = value;
582
- if (typeof line.text !== "string" || !line.text.trim()) return [];
583
- return [{
584
- ...typeof line.at === "string" ? { at: line.at } : {},
585
- ...typeof line.speaker === "string" ? { speaker: line.speaker } : {},
586
- text: line.text
587
- }];
588
- }) : [];
589
- return {
590
- droppedLines,
591
- ...typeof payload.epoch === "string" ? { epoch: payload.epoch } : {},
592
- lines,
593
- ...typeof payload.urlMatched === "boolean" ? { urlMatched: payload.urlMatched } : {},
594
- ...typeof payload.sessionMatched === "boolean" ? { sessionMatched: payload.sessionMatched } : {}
595
- };
596
- }
597
- function parseMeetLeaveResult(result) {
598
- const raw = (result && typeof result === "object" ? result : {}).result;
599
- if (typeof raw !== "string" || !raw.trim()) return { departed: false };
600
- try {
601
- const parsed = JSON.parse(raw);
602
- const leaveAction = parsed.leaveAction === "leave" || parsed.leaveAction === "confirm" ? parsed.leaveAction : void 0;
603
- return {
604
- departed: parsed.departed === true,
605
- ...leaveAction ? { leaveAction } : {},
606
- ...typeof parsed.urlMatched === "boolean" ? { urlMatched: parsed.urlMatched } : {}
607
- };
608
- } catch {
609
- return { departed: false };
610
- }
611
- }
612
- function classifyMeetManualAction(health) {
613
- if (!health.manualActionRequired || !health.manualActionReason || !health.manualActionMessage) return;
614
- return {
615
- category: health.manualActionReason === "google-login-required" ? "login-required" : health.manualActionReason === "meet-admission-required" ? "admission-required" : health.manualActionReason === "meet-permission-required" ? "permission-required" : health.manualActionReason === "meet-audio-choice-required" ? "audio-choice-required" : health.manualActionReason === "meet-locale-required" ? "locale-required" : health.manualActionReason === "meet-session-conflict" ? "session-conflict" : health.manualActionReason === "browser-control-unavailable" ? "browser-control-unavailable" : "custom",
616
- reason: health.manualActionReason,
617
- message: health.manualActionMessage
618
- };
518
+ const manualActionCategories = /* @__PURE__ */ new Map([
519
+ ["browser-control-unavailable", "browser-control-unavailable"],
520
+ ["google-login-required", "login-required"],
521
+ ["meet-admission-required", "admission-required"],
522
+ ["meet-audio-choice-required", "audio-choice-required"],
523
+ ["meet-locale-required", "locale-required"],
524
+ ["meet-permission-required", "permission-required"],
525
+ ["meet-session-conflict", "session-conflict"]
526
+ ]);
527
+ function classifyManualActionReason(reason) {
528
+ return manualActionCategories.get(reason) ?? "custom";
619
529
  }
620
- const GOOGLE_MEET_PLATFORM_ADAPTER = {
530
+ const GOOGLE_MEET_PLATFORM_ADAPTER = MeetingPlatformAdapter.create({
621
531
  id: "google-meet",
622
532
  displayName: "Google Meet",
623
533
  browserLabel: "Meet",
624
534
  logScope: "[google-meet]",
535
+ agentConsult: {
536
+ surface: "a private Google Meet",
537
+ userLabel: "Participant",
538
+ assistantLabel: "Agent",
539
+ questionSourceLabel: "participant",
540
+ workingResponseLabel: "participant",
541
+ extraSystemPrompt: [
542
+ "You are a behind-the-scenes consultant for a live meeting voice agent.",
543
+ "Prioritize a fast, speakable answer over exhaustive investigation.",
544
+ "For tool-backed status checks, prefer one or two bounded read-only queries before answering.",
545
+ "Do not print secret values or dump environment variables; only check whether required configuration is present.",
546
+ "Be accurate, brief, and speakable."
547
+ ].join(" ")
548
+ },
549
+ session: {
550
+ idPrefix: "meet",
551
+ participantIdentity: (transport) => transport === "twilio" ? "Twilio phone participant" : transport === "chrome-node" ? "signed-in Google Chrome profile on a paired node" : "signed-in Google Chrome profile"
552
+ },
625
553
  nodeCommandName: GOOGLE_MEET_NODE_COMMAND,
626
554
  nodeConfigPath: "plugins.entries.google-meet.config.chromeNode.node",
627
555
  urls: {
@@ -642,28 +570,24 @@ const GOOGLE_MEET_PLATFORM_ADAPTER = {
642
570
  }
643
571
  },
644
572
  browser: {
645
- allowsMicrophone: isGoogleMeetTalkBackMode$2,
573
+ allowsMicrophone: MeetingPlatformAdapter.isTalkBackMode,
646
574
  buildStatusJoinScript: (params) => meetStatusScript({
647
- allowMicrophone: isGoogleMeetTalkBackMode$2(params.mode),
575
+ allowMicrophone: MeetingPlatformAdapter.isTalkBackMode(params.mode),
648
576
  autoJoin: params.autoJoin,
649
577
  captionSessionId: params.meetingSessionId || void 0,
650
578
  captureCaptions: params.captureCaptions,
651
579
  guestName: params.guestName,
652
580
  readOnly: params.readOnly
653
581
  }),
654
- parseStatus: parseMeetBrowserStatus,
655
- classifyManualAction: classifyMeetManualAction,
656
582
  browserControlUnavailable: () => ({
657
583
  category: "browser-control-unavailable",
658
584
  reason: "browser-control-unavailable",
659
585
  message: "Open the OpenClaw browser profile, finish Google Meet login, admission, or permission prompts, then retry."
660
586
  }),
661
587
  buildLeaveScript: meetLeaveScript,
662
- parseLeaveResult: parseMeetLeaveResult,
663
588
  captions: {
664
- enabled: (mode) => mode === "transcribe",
665
- buildTranscriptScript: ({ finalize, meetingSessionId, meetingUrl }) => meetTranscriptScript(meetingUrl, meetingSessionId, finalize),
666
- parseTranscript: parseMeetTranscriptSnapshot
589
+ enabled: (_mode) => true,
590
+ buildTranscriptScript: ({ finalize, meetingSessionId, meetingUrl }) => meetTranscriptScript(meetingUrl, meetingSessionId, finalize)
667
591
  },
668
592
  permissions: ({ allowMicrophone }) => allowMicrophone ? {
669
593
  origin: "https://meet.google.com",
@@ -689,8 +613,16 @@ const GOOGLE_MEET_PLATFORM_ADAPTER = {
689
613
  pin,
690
614
  dtmfSequence: params.dtmfSequence || params.defaultDtmfSequence ? rawDtmfSequence : prefixDtmfWait(rawDtmfSequence, params.dtmfDelayMs)
691
615
  };
692
- } }
693
- };
616
+ } },
617
+ parsing: {
618
+ classifyManualActionReason,
619
+ displayName: "Meet",
620
+ invalidTranscriptMessage: "Google Meet transcript payload is invalid.",
621
+ malformedStatusMessage: "Google Meet browser status JSON is malformed.",
622
+ malformedTranscriptMessage: "Google Meet transcript JSON is malformed.",
623
+ statusFields: (parsed) => ({ leaveReason: typeof parsed.leaveReason === "string" ? parsed.leaveReason : void 0 })
624
+ }
625
+ });
694
626
  //#endregion
695
627
  //#region extensions/google-meet/src/node-host.ts
696
628
  function assertBlackHoleAvailable(timeoutMs) {
@@ -760,139 +692,53 @@ function createGoogleMeetChromeNodeInvokePolicy(config) {
760
692
  }
761
693
  //#endregion
762
694
  //#region extensions/google-meet/src/runtime-probes.ts
763
- function resolveMode$1(request, config) {
764
- return request.mode === "realtime" ? "agent" : request.mode ?? config.defaultMode;
765
- }
766
695
  function resolveProbeTimeoutMs(input, fallback) {
767
696
  if (input === void 0) return Math.min(Math.max(fallback, 1), 12e4);
768
697
  if (!Number.isFinite(input) || input <= 0) throw new Error("timeoutMs must be a positive number");
769
698
  return Math.min(Math.trunc(input), 12e4);
770
699
  }
771
- async function testGoogleMeetSpeech(context, request) {
772
- if (request.mode === "transcribe") throw new Error("test_speech requires mode: agent or bidi; use join mode: transcribe for observe-only sessions.");
773
- const requestedMode = request.mode ? resolveMode$1(request, context.config) : void 0;
774
- const mode = requestedMode === "agent" || requestedMode === "bidi" ? requestedMode : context.config.defaultMode === "agent" || context.config.defaultMode === "bidi" ? context.config.defaultMode : "agent";
775
- const resolved = {
776
- url: normalizeMeetUrl(request.url),
777
- transport: request.transport ?? context.config.defaultTransport,
778
- mode,
779
- agentId: context.resolveAgentId(request)
780
- };
781
- const beforeSessions = context.list();
782
- const before = new Set(beforeSessions.map((session) => session.id));
783
- const existing = beforeSessions.find((session) => context.isReusable(session, resolved));
784
- const existingOutputBytes = existing?.chrome?.health?.lastOutputBytes ?? 0;
785
- const result = await context.join({
786
- ...request,
787
- ...resolved,
788
- message: request.message ?? "Say exactly: Google Meet speech test complete."
789
- });
790
- const startOutputBytes = existing?.id === result.session.id ? existingOutputBytes : 0;
791
- let health = result.session.chrome?.health;
792
- const shouldWait = result.spoken === true && health?.manualActionRequired !== true && context.hasHealthHandle(result.session.id);
793
- if (shouldWait && (health?.lastOutputBytes ?? 0) <= startOutputBytes) {
794
- const deadline = Date.now() + Math.min(context.config.chrome.joinTimeoutMs, 5e3);
795
- while (Date.now() < deadline) {
796
- await sleep(100);
797
- context.refreshHealth(result.session.id);
798
- health = result.session.chrome?.health;
799
- if ((health?.lastOutputBytes ?? 0) > startOutputBytes) break;
800
- }
801
- }
802
- const speechOutputVerified = (health?.lastOutputBytes ?? 0) > startOutputBytes;
803
- return {
804
- createdSession: !before.has(result.session.id),
805
- inCall: health?.inCall,
806
- manualActionRequired: health?.manualActionRequired,
807
- manualActionReason: health?.manualActionReason,
808
- manualActionMessage: health?.manualActionMessage,
809
- spoken: result.spoken ?? false,
810
- speechOutputVerified,
811
- speechOutputTimedOut: shouldWait && !speechOutputVerified,
812
- speechReady: health?.speechReady,
813
- speechBlockedReason: health?.speechBlockedReason,
814
- speechBlockedMessage: health?.speechBlockedMessage,
815
- audioOutputActive: health?.audioOutputActive,
816
- lastOutputBytes: health?.lastOutputBytes,
817
- session: result.session
818
- };
819
- }
820
- async function testGoogleMeetListening(context, request) {
821
- const requestedMode = request.mode ? resolveMode$1(request, context.config) : void 0;
822
- if (requestedMode === "agent" || requestedMode === "bidi") throw new Error("test_listen requires mode: transcribe; use test_speech for talk-back sessions.");
823
- const resolved = {
824
- url: normalizeMeetUrl(request.url),
825
- transport: request.transport ?? context.config.defaultTransport,
826
- mode: "transcribe",
827
- agentId: context.resolveAgentId(request)
828
- };
829
- if (resolved.transport === "twilio") throw new Error("test_listen supports chrome or chrome-node transports");
830
- const beforeSessions = context.list();
831
- const before = new Set(beforeSessions.map((session) => session.id));
832
- const existing = beforeSessions.find((session) => context.isReusable(session, resolved));
833
- const start = {
834
- lines: existing?.chrome?.health?.transcriptLines ?? 0,
835
- at: existing?.chrome?.health?.lastCaptionAt,
836
- text: existing?.chrome?.health?.lastCaptionText
837
- };
838
- const result = await context.join({
839
- ...request,
840
- ...resolved,
841
- message: void 0
842
- });
843
- let health = result.session.chrome?.health;
844
- const advanced = () => (health?.transcriptLines ?? 0) > (existing?.id === result.session.id ? start.lines : 0) || Boolean(health?.lastCaptionAt && health.lastCaptionAt !== start.at) || Boolean(health?.lastCaptionText && health.lastCaptionText !== start.text);
845
- const shouldWait = health?.manualActionRequired !== true && Boolean((result.session.transport === "chrome" || result.session.transport === "chrome-node") && result.session.chrome?.launched);
846
- if (shouldWait && !advanced()) {
847
- const deadline = Date.now() + resolveProbeTimeoutMs(request.timeoutMs, context.config.chrome.joinTimeoutMs);
848
- while (Date.now() < deadline) {
849
- await sleep(250);
850
- await context.refreshCaptionHealth(result.session);
851
- health = result.session.chrome?.health;
852
- if (health?.manualActionRequired || advanced()) break;
853
- }
854
- }
855
- const listenVerified = advanced();
856
- return {
857
- createdSession: !before.has(result.session.id),
858
- inCall: health?.inCall,
859
- manualActionRequired: health?.manualActionRequired,
860
- manualActionReason: health?.manualActionReason,
861
- manualActionMessage: health?.manualActionMessage,
862
- listenVerified,
863
- listenTimedOut: shouldWait && !listenVerified && health?.manualActionRequired !== true,
864
- captioning: health?.captioning,
865
- captionsEnabledAttempted: health?.captionsEnabledAttempted,
866
- transcriptLines: health?.transcriptLines,
867
- lastCaptionAt: health?.lastCaptionAt,
868
- lastCaptionSpeaker: health?.lastCaptionSpeaker,
869
- lastCaptionText: health?.lastCaptionText,
870
- recentTranscript: health?.recentTranscript,
871
- session: result.session
872
- };
873
- }
700
+ const probes = MeetingPlatformAdapter.createRuntimeProbes({
701
+ defaultSpeechMessage: "Say exactly: Google Meet speech test complete.",
702
+ invalidRequest: (message) => new Error(message),
703
+ resolveTimeoutMs: resolveProbeTimeoutMs,
704
+ shouldWaitForListening: (session) => Boolean((session.transport === "chrome" || session.transport === "chrome-node") && session.chrome?.launched),
705
+ talkBackMode: MeetingPlatformAdapter.isTalkBackMode,
706
+ normalizeUrl: normalizeMeetUrl,
707
+ resolveRequestMode: (mode) => mode === "realtime" ? "agent" : mode,
708
+ defaultTransport: (config) => config.defaultTransport,
709
+ validateListeningTransport: (transport) => {
710
+ if (transport === "twilio") throw new Error("test_listen supports chrome or chrome-node transports");
711
+ },
712
+ resolveSpeechTimeoutMs: (_request, config) => Math.min(config.chrome.joinTimeoutMs, 5e3),
713
+ refreshCaptionHealth: async (context, session) => await context.refreshCaptionHealth(session),
714
+ speechModeError: "test_speech requires mode: agent or bidi; use join mode: transcribe for observe-only sessions.",
715
+ listeningModeError: "test_listen requires mode: transcribe; use test_speech for talk-back sessions."
716
+ });
717
+ const testGoogleMeetListening = probes.testListening;
718
+ const testGoogleMeetSpeech = probes.testSpeech;
874
719
  //#endregion
875
720
  //#region extensions/google-meet/src/runtime-session.ts
876
- function createGoogleMeetSession(params) {
877
- const { config, createdAt, resolved } = params;
878
- return {
879
- id: `meet_${randomUUID()}`,
880
- ...resolved,
881
- state: "active",
882
- createdAt,
883
- updatedAt: createdAt,
884
- participantIdentity: resolved.transport === "twilio" ? "Twilio phone participant" : resolved.transport === "chrome-node" ? "signed-in Google Chrome profile on a paired node" : "signed-in Google Chrome profile",
721
+ function resolveTransport(input, config) {
722
+ return input ?? config.defaultTransport;
723
+ }
724
+ function resolveMode(input, config) {
725
+ return input === "realtime" ? "agent" : input ?? config.defaultMode;
726
+ }
727
+ function withSessionAgentConfig(config, agentId) {
728
+ return config.realtime.agentId === agentId ? config : {
729
+ ...config,
885
730
  realtime: {
886
- enabled: resolved.mode === "agent" || resolved.mode === "bidi",
887
- strategy: resolved.mode === "bidi" ? "bidi" : "agent",
888
- provider: resolved.mode === "bidi" ? config.realtime.voiceProvider ?? config.realtime.provider : void 0,
889
- model: resolved.mode === "bidi" ? config.realtime.model : void 0,
890
- transcriptionProvider: resolved.mode === "agent" ? config.realtime.transcriptionProvider ?? config.realtime.provider : void 0,
891
- toolPolicy: config.realtime.toolPolicy
892
- },
893
- notes: []
731
+ ...config.realtime,
732
+ agentId
733
+ }
894
734
  };
895
735
  }
736
+ function isBrowserTransport(transport) {
737
+ return transport === "chrome" || transport === "chrome-node";
738
+ }
739
+ function noteSession(session, note) {
740
+ session.notes = [...session.notes.filter((item) => item !== note), note];
741
+ }
896
742
  //#endregion
897
743
  //#region extensions/google-meet/src/setup.ts
898
744
  function resolveUserPath(input) {
@@ -946,7 +792,7 @@ function getGoogleMeetSetupStatus(config, options) {
946
792
  const fullConfig = asRecord(options?.fullConfig);
947
793
  const mode = options?.mode ?? config.defaultMode;
948
794
  const transport = options?.transport ?? config.defaultTransport;
949
- const needsChromeRealtimeAudio = (mode === "agent" || mode === "bidi") && (transport === "chrome" || transport === "chrome-node");
795
+ const needsChromeRealtimeAudio = MeetingPlatformAdapter.isTalkBackMode(mode) && (transport === "chrome" || transport === "chrome-node");
950
796
  const pluginEntries = asRecord(asRecord(fullConfig.plugins).entries);
951
797
  const pluginAllow = asRecord(fullConfig.plugins).allow;
952
798
  const voiceCallEntry = asRecord(pluginEntries["voice-call"]);
@@ -1035,84 +881,9 @@ function addGoogleMeetSetupCheck(status, check) {
1035
881
  return addMeetingSetupCheck(status, check);
1036
882
  }
1037
883
  //#endregion
1038
- //#region extensions/google-meet/src/agent-consult.ts
1039
- const GOOGLE_MEET_CONSULT_SURFACE = {
1040
- id: "google-meet",
1041
- provider: "google-meet",
1042
- lane: "google-meet",
1043
- surface: "a private Google Meet",
1044
- userLabel: "Participant",
1045
- assistantLabel: "Agent",
1046
- questionSourceLabel: "participant",
1047
- workingResponseLabel: "participant",
1048
- extraSystemPrompt: [
1049
- "You are a behind-the-scenes consultant for a live meeting voice agent.",
1050
- "Prioritize a fast, speakable answer over exhaustive investigation.",
1051
- "For tool-backed status checks, prefer one or two bounded read-only queries before answering.",
1052
- "Do not print secret values or dump environment variables; only check whether required configuration is present.",
1053
- "Be accurate, brief, and speakable."
1054
- ].join(" ")
1055
- };
1056
- function resolveGoogleMeetRealtimeTools(policy) {
1057
- return resolveMeetingRealtimeTools(policy);
1058
- }
1059
- async function consultOpenClawAgentForGoogleMeet(params) {
1060
- return await consultMeetingAgent({
1061
- surface: GOOGLE_MEET_CONSULT_SURFACE,
1062
- config: params.fullConfig,
1063
- runtime: params.runtime,
1064
- logger: params.logger,
1065
- agentId: params.config.realtime.agentId,
1066
- toolPolicy: params.config.realtime.toolPolicy,
1067
- meetingSessionId: params.meetingSessionId,
1068
- requesterSessionKey: params.requesterSessionKey,
1069
- args: params.args,
1070
- transcript: params.transcript
1071
- });
1072
- }
1073
- async function handleGoogleMeetRealtimeConsultToolCall(params) {
1074
- await handleMeetingRealtimeConsultToolCall({
1075
- surface: GOOGLE_MEET_CONSULT_SURFACE,
1076
- strategy: params.strategy,
1077
- session: params.session,
1078
- event: params.event,
1079
- config: params.fullConfig,
1080
- runtime: params.runtime,
1081
- logger: params.logger,
1082
- agentId: params.config.realtime.agentId,
1083
- toolPolicy: params.config.realtime.toolPolicy,
1084
- meetingSessionId: params.meetingSessionId,
1085
- requesterSessionKey: params.requesterSessionKey,
1086
- transcript: params.transcript,
1087
- onTalkEvent: params.onTalkEvent
1088
- });
1089
- }
1090
- //#endregion
1091
884
  //#region extensions/google-meet/src/transports/chrome.ts
1092
- const GOOGLE_MEET_RUNTIME_PLATFORM = {
1093
- displayName: GOOGLE_MEET_PLATFORM_ADAPTER.displayName,
1094
- logScope: GOOGLE_MEET_PLATFORM_ADAPTER.logScope,
1095
- sessionIdPrefix: GOOGLE_MEET_PLATFORM_ADAPTER.id
1096
- };
1097
- function createGoogleMeetRealtimeEngineBindings(params) {
1098
- return {
1099
- platform: GOOGLE_MEET_RUNTIME_PLATFORM,
1100
- consultAgent: (consult) => consultOpenClawAgentForGoogleMeet({
1101
- config: params.config,
1102
- fullConfig: params.fullConfig,
1103
- runtime: params.runtime,
1104
- logger: params.logger,
1105
- ...consult
1106
- }),
1107
- tools: resolveGoogleMeetRealtimeTools(params.config.realtime.toolPolicy),
1108
- handleToolCall: (call) => handleGoogleMeetRealtimeConsultToolCall({
1109
- config: params.config,
1110
- fullConfig: params.fullConfig,
1111
- runtime: params.runtime,
1112
- logger: params.logger,
1113
- ...call
1114
- })
1115
- };
885
+ function shouldCaptureCaptions(mode, fullConfig) {
886
+ return mode === "transcribe" || !fullConfig || resolveTranscriptsConfig(fullConfig.transcripts).enabled;
1116
887
  }
1117
888
  async function assertBlackHole2chAvailable(params) {
1118
889
  if (process.platform !== "darwin") throw new Error("Chrome Meet transport with blackhole-2ch audio is currently macOS-only");
@@ -1132,7 +903,7 @@ async function assertBlackHole2chAvailable(params) {
1132
903
  }
1133
904
  async function launchChromeMeet(params) {
1134
905
  const checkRealtimeAudioPrerequisites = async () => {
1135
- if (!isGoogleMeetTalkBackMode$2(params.mode)) return;
906
+ if (!MeetingPlatformAdapter.isTalkBackMode(params.mode)) return;
1136
907
  await assertBlackHole2chAvailable({
1137
908
  runtime: params.runtime,
1138
909
  timeoutMs: Math.min(params.config.chrome.joinTimeoutMs, 1e4)
@@ -1143,7 +914,7 @@ async function launchChromeMeet(params) {
1143
914
  }
1144
915
  };
1145
916
  const startRealtimeAudioBridge = async () => {
1146
- if (!isGoogleMeetTalkBackMode$2(params.mode)) return;
917
+ if (!MeetingPlatformAdapter.isTalkBackMode(params.mode)) return;
1147
918
  if (params.config.chrome.audioBridgeCommand) {
1148
919
  if (params.mode === "agent") throw new Error("Chrome agent mode requires chrome.audioInputCommand and chrome.audioOutputCommand so OpenClaw can run STT and regular TTS directly.");
1149
920
  const bridge = await params.runtime.system.runCommandWithTimeout(params.config.chrome.audioBridgeCommand, { timeoutMs: params.config.chrome.joinTimeoutMs });
@@ -1154,14 +925,18 @@ async function launchChromeMeet(params) {
1154
925
  const transport = createLocalMeetingRealtimeAudioTransport({
1155
926
  inputCommand: params.config.chrome.audioInputCommand,
1156
927
  outputCommand: params.config.chrome.audioOutputCommand,
928
+ audioFormat: params.config.chrome.audioFormat,
1157
929
  bargeInInputCommand: params.config.chrome.bargeInInputCommand,
1158
930
  bargeInRmsThreshold: params.config.chrome.bargeInRmsThreshold,
1159
931
  bargeInPeakThreshold: params.config.chrome.bargeInPeakThreshold,
1160
932
  bargeInCooldownMs: params.config.chrome.bargeInCooldownMs,
1161
933
  logger: params.logger,
1162
- logScope: GOOGLE_MEET_RUNTIME_PLATFORM.logScope
934
+ logScope: GOOGLE_MEET_PLATFORM_ADAPTER.logScope
935
+ });
936
+ const bindings = createMeetingRealtimeEngineBindings({
937
+ platform: GOOGLE_MEET_PLATFORM_ADAPTER,
938
+ ...params
1163
939
  });
1164
- const bindings = createGoogleMeetRealtimeEngineBindings(params);
1165
940
  const engine = params.mode === "agent" ? await startMeetingAgentRealtimeEngine({
1166
941
  config: params.config,
1167
942
  fullConfig: params.fullConfig,
@@ -1205,12 +980,13 @@ async function launchChromeMeet(params) {
1205
980
  callBrowser: await resolveLocalMeetingBrowserRequest(params.runtime),
1206
981
  config: params.config.chrome,
1207
982
  session: {
983
+ captureCaptions: shouldCaptureCaptions(params.mode, params.fullConfig),
1208
984
  meetingSessionId: params.meetingSessionId,
1209
985
  mode: params.mode,
1210
986
  url: params.url
1211
987
  }
1212
988
  });
1213
- const audioBridge = isGoogleMeetTalkBackMode$2(params.mode) && result.browser?.inCall === true && result.browser.micMuted === false && result.browser.manualActionRequired !== true ? await startRealtimeAudioBridge() : void 0;
989
+ const audioBridge = MeetingPlatformAdapter.isTalkBackMode(params.mode) && result.browser?.inCall === true && result.browser.micMuted === false && result.browser.manualAction === void 0 ? await startRealtimeAudioBridge() : void 0;
1214
990
  return {
1215
991
  ...result,
1216
992
  audioBridge
@@ -1226,6 +1002,7 @@ async function leaveChromeMeet(params) {
1226
1002
  adapter: GOOGLE_MEET_PLATFORM_ADAPTER,
1227
1003
  callBrowser: await resolveLocalMeetingBrowserRequest(params.runtime),
1228
1004
  launch: params.config.chrome.launch,
1005
+ meetingSessionId: params.meetingSessionId,
1229
1006
  meetingUrl: params.meetingUrl,
1230
1007
  tab: params.tab,
1231
1008
  timeoutMs: params.config.chrome.joinTimeoutMs
@@ -1281,6 +1058,7 @@ async function leaveChromeMeetOnNode(params) {
1281
1058
  timeoutMs: request.timeoutMs
1282
1059
  }),
1283
1060
  launch: params.config.chrome.launch,
1061
+ meetingSessionId: params.meetingSessionId,
1284
1062
  meetingUrl: params.meetingUrl,
1285
1063
  tab: params.tab,
1286
1064
  timeoutMs: params.config.chrome.joinTimeoutMs
@@ -1299,6 +1077,7 @@ async function openMeetWithBrowserProxy(params) {
1299
1077
  }),
1300
1078
  config: params.config.chrome,
1301
1079
  session: {
1080
+ captureCaptions: params.captureCaptions,
1302
1081
  mode: params.mode,
1303
1082
  meetingSessionId: params.meetingSessionId,
1304
1083
  url: params.url
@@ -1311,6 +1090,7 @@ async function recoverCurrentMeetTab(params) {
1311
1090
  ...await recoverMeetingBrowserTab({
1312
1091
  adapter: GOOGLE_MEET_PLATFORM_ADAPTER,
1313
1092
  callBrowser: await resolveLocalMeetingBrowserRequest(params.runtime),
1093
+ captureCaptions: shouldCaptureCaptions(params.mode ?? "bidi", params.fullConfig),
1314
1094
  config: params.config.chrome,
1315
1095
  locationLabel: "in local Chrome",
1316
1096
  mode: params.mode ?? "bidi",
@@ -1339,6 +1119,7 @@ async function recoverCurrentMeetTabOnNode(params) {
1339
1119
  body: request.body,
1340
1120
  timeoutMs: request.timeoutMs
1341
1121
  }),
1122
+ captureCaptions: shouldCaptureCaptions(params.mode ?? "bidi", params.fullConfig),
1342
1123
  config: params.config.chrome,
1343
1124
  locationLabel: "on the selected Chrome node",
1344
1125
  mode: params.mode ?? "bidi",
@@ -1372,11 +1153,12 @@ async function launchChromeMeetOnNode(params) {
1372
1153
  runtime: params.runtime,
1373
1154
  nodeId,
1374
1155
  config: params.config,
1156
+ captureCaptions: shouldCaptureCaptions(params.mode, params.fullConfig),
1375
1157
  mode: params.mode,
1376
1158
  meetingSessionId: params.meetingSessionId,
1377
1159
  url: params.url
1378
1160
  });
1379
- if (params.config.chrome.launch && isGoogleMeetTalkBackMode$2(params.mode) && (browserControl.browser?.inCall !== true || browserControl.browser.micMuted !== false || browserControl.browser.manualActionRequired === true)) return {
1161
+ if (params.config.chrome.launch && MeetingPlatformAdapter.isTalkBackMode(params.mode) && (browserControl.browser?.inCall !== true || browserControl.browser.micMuted !== false || browserControl.browser.manualAction)) return {
1380
1162
  nodeId,
1381
1163
  launched: browserControl.launched,
1382
1164
  browser: browserControl.browser,
@@ -1405,12 +1187,16 @@ async function launchChromeMeetOnNode(params) {
1405
1187
  runtime: params.runtime,
1406
1188
  nodeId,
1407
1189
  bridgeId: result.bridgeId,
1190
+ audioFormat: params.config.chrome.audioFormat,
1408
1191
  logger: params.logger,
1409
1192
  commandName: GOOGLE_MEET_NODE_COMMAND,
1410
- logScope: GOOGLE_MEET_RUNTIME_PLATFORM.logScope,
1193
+ logScope: GOOGLE_MEET_PLATFORM_ADAPTER.logScope,
1411
1194
  logPrefix: params.mode === "agent" ? "node agent" : "node"
1412
1195
  });
1413
- const bindings = createGoogleMeetRealtimeEngineBindings(params);
1196
+ const bindings = createMeetingRealtimeEngineBindings({
1197
+ platform: GOOGLE_MEET_PLATFORM_ADAPTER,
1198
+ ...params
1199
+ });
1414
1200
  const engine = params.mode === "agent" ? await startMeetingAgentRealtimeEngine({
1415
1201
  config: params.config,
1416
1202
  fullConfig: params.fullConfig,
@@ -1627,45 +1413,18 @@ async function speakMeetViaVoiceCallGateway(params) {
1627
1413
  }
1628
1414
  //#endregion
1629
1415
  //#region extensions/google-meet/src/runtime.ts
1630
- function nowIso() {
1631
- return (/* @__PURE__ */ new Date()).toISOString();
1632
- }
1633
- function buildTwilioVoiceCallSessionKey(meetingSessionId) {
1634
- return `voice:google-meet:${meetingSessionId}`;
1635
- }
1636
- function resolveTransport(input, config) {
1637
- return input ?? config.defaultTransport;
1638
- }
1639
- function resolveMode(input, config) {
1640
- return input === "realtime" ? "agent" : input ?? config.defaultMode;
1641
- }
1642
- function resolveSessionAgentId(request, config) {
1643
- return normalizeAgentId(request.agentId ?? config.realtime.agentId);
1644
- }
1645
- function withSessionAgentConfig(config, agentId) {
1646
- return config.realtime.agentId === agentId ? config : {
1647
- ...config,
1648
- realtime: {
1649
- ...config.realtime,
1650
- agentId
1651
- }
1652
- };
1653
- }
1654
- function isGoogleMeetTalkBackMode$1(mode) {
1655
- return mode === "agent" || mode === "bidi";
1656
- }
1657
- function isBrowserTransport(transport) {
1658
- return transport === "chrome" || transport === "chrome-node";
1659
- }
1660
- function noteSession(session, note) {
1661
- session.notes = [...session.notes.filter((item) => item !== note), note];
1662
- }
1416
+ const nowIso = () => (/* @__PURE__ */ new Date()).toISOString();
1663
1417
  var GoogleMeetRuntime = class {
1664
- #createdBrowserTabs = /* @__PURE__ */ new Map();
1418
+ #createdBrowserTabs;
1419
+ #agentId;
1665
1420
  #voiceCallGateway;
1666
1421
  #sessions;
1667
1422
  constructor(params) {
1668
1423
  this.params = params;
1424
+ this.#createdBrowserTabs = /* @__PURE__ */ new Map();
1425
+ this.transcriptSourceRuntime = () => this.#sessions;
1426
+ const adapter = GOOGLE_MEET_PLATFORM_ADAPTER;
1427
+ this.#agentId = resolveDefaultAgentId(params.fullConfig);
1669
1428
  this.#voiceCallGateway = createVoiceCallGateway(params);
1670
1429
  this.#sessions = new MeetingSessionRuntime({
1671
1430
  logger: params.logger,
@@ -1689,7 +1448,6 @@ var GoogleMeetRuntime = class {
1689
1448
  speech: {
1690
1449
  audioBridgeUnavailable: "Realtime speech requires an active Chrome audio bridge.",
1691
1450
  browserUnverified: "Google Meet browser state has not been verified yet.",
1692
- manualActionFallback: "Resolve the Google Meet browser prompt before asking OpenClaw to speak.",
1693
1451
  microphoneMuted: "Turn on the OpenClaw Google Meet microphone before asking OpenClaw to speak.",
1694
1452
  microphoneMutedReason: "meet-microphone-muted",
1695
1453
  notInCall: "Google Meet has not reported that the browser participant is in the call.",
@@ -1699,22 +1457,23 @@ var GoogleMeetRuntime = class {
1699
1457
  }
1700
1458
  },
1701
1459
  resolveJoin: (request) => ({
1702
- url: GOOGLE_MEET_PLATFORM_ADAPTER.urls.validateAndNormalize(request.url),
1460
+ url: adapter.urls.validateAndNormalize(request.url),
1703
1461
  transport: resolveTransport(request.transport, params.config),
1704
1462
  mode: resolveMode(request.mode, params.config),
1705
- agentId: resolveSessionAgentId(request, params.config)
1463
+ agentId: normalizeAgentId(request.agentId ?? params.config.realtime.agentId ?? this.#agentId)
1706
1464
  }),
1707
- createSession: ({ request: _request, resolved, createdAt }) => createGoogleMeetSession({
1465
+ createSession: ({ resolved, createdAt }) => createMeetingSession({
1466
+ platform: adapter,
1708
1467
  config: params.config,
1709
1468
  resolved,
1710
1469
  createdAt
1711
1470
  }),
1712
1471
  resolveSpeechInstructions: (request) => request.message ?? params.config.realtime.introMessage,
1713
1472
  isBrowserTransport,
1714
- isTalkBackMode: isGoogleMeetTalkBackMode$1,
1473
+ isTalkBackMode: (mode) => MeetingPlatformAdapter.isTalkBackMode(mode),
1715
1474
  isTranscribeMode: (mode) => mode === "transcribe",
1716
- sameMeetingUrl: (left, right) => GOOGLE_MEET_PLATFORM_ADAPTER.urls.isSameMeeting(left, right),
1717
- normalizeMeetingUrlForReuse: (url) => GOOGLE_MEET_PLATFORM_ADAPTER.urls.normalizeForReuse(url),
1475
+ sameMeetingUrl: (left, right) => adapter.urls.isSameMeeting(left, right),
1476
+ normalizeMeetingUrlForReuse: (url) => adapter.urls.normalizeForReuse(url),
1718
1477
  getBrowser: (session) => session.chrome ? {
1719
1478
  launched: session.chrome.launched,
1720
1479
  nodeId: session.chrome.nodeId,
@@ -1732,12 +1491,17 @@ var GoogleMeetRuntime = class {
1732
1491
  releaseBrowserTab: async (session) => await this.#releaseBrowserTab(session),
1733
1492
  refreshBrowserHealth: async (session, options) => await this.#refreshBrowserHealth(session, options),
1734
1493
  refreshStatus: async (session) => await this.#refreshStatus(session),
1735
- refreshReusableSession: async (session) => {
1494
+ refreshReusableSession: async (session, _request, _resolved) => {
1736
1495
  if (session.transport === "twilio") await this.#refreshTwilioVoiceCallStatus(session);
1737
1496
  },
1738
1497
  ensureRealtimeBridge: async (session) => await this.#ensureChromeRealtimeBridge(session),
1739
1498
  captureTranscript: async (session, options) => await this.#captureTranscript(session, options),
1740
- speakViaTransport: async (session, instructions) => await this.#speakViaTransport(session, instructions)
1499
+ speakViaTransport: async (session, instructions) => await this.#speakViaTransport(session, instructions),
1500
+ durableTranscripts: {
1501
+ config: params.fullConfig.transcripts,
1502
+ providerId: "google-meet",
1503
+ providerName: "Google Meet"
1504
+ }
1741
1505
  });
1742
1506
  }
1743
1507
  list() {
@@ -1772,10 +1536,12 @@ var GoogleMeetRuntime = class {
1772
1536
  return transport === "chrome-node" ? await recoverCurrentMeetTabOnNode({
1773
1537
  runtime: this.params.runtime,
1774
1538
  config: this.params.config,
1539
+ fullConfig: this.params.fullConfig,
1775
1540
  url
1776
1541
  }) : await recoverCurrentMeetTab({
1777
1542
  runtime: this.params.runtime,
1778
1543
  config: this.params.config,
1544
+ fullConfig: this.params.fullConfig,
1779
1545
  url
1780
1546
  });
1781
1547
  }
@@ -1797,7 +1563,7 @@ var GoogleMeetRuntime = class {
1797
1563
  #probeContext() {
1798
1564
  return {
1799
1565
  config: this.params.config,
1800
- resolveAgentId: (request) => resolveSessionAgentId(request, this.params.config),
1566
+ resolveAgentId: (request) => normalizeAgentId(request.agentId ?? this.params.config.realtime.agentId ?? this.#agentId),
1801
1567
  list: () => this.list(),
1802
1568
  join: async (request) => await this.join(request),
1803
1569
  isReusable: (session, resolved) => this.#sessions.isReusableSession(session, resolved),
@@ -1854,7 +1620,7 @@ var GoogleMeetRuntime = class {
1854
1620
  };
1855
1621
  const handles = this.#attachChromeAudioBridge(session, result.audioBridge);
1856
1622
  if (handles) context.attachRuntimeHandles(session, handles);
1857
- session.notes.push(result.audioBridge ? session.transport === "chrome-node" ? "Chrome node transport joins as the signed-in Google profile on the selected node and routes realtime audio through the node bridge." : "Chrome transport joins as the signed-in Google profile and routes realtime audio through the configured bridge." : isGoogleMeetTalkBackMode$1(session.mode) ? "Chrome transport joins as the signed-in Google profile and expects BlackHole 2ch audio routing." : "Chrome transport joins as the signed-in Google profile without starting the realtime audio bridge.");
1623
+ session.notes.push(result.audioBridge ? session.transport === "chrome-node" ? "Chrome node transport joins as the signed-in Google profile on the selected node and routes realtime audio through the node bridge." : "Chrome transport joins as the signed-in Google profile and routes realtime audio through the configured bridge." : MeetingPlatformAdapter.isTalkBackMode(session.mode) ? "Chrome transport joins as the signed-in Google profile and expects BlackHole 2ch audio routing." : "Chrome transport joins as the signed-in Google profile without starting the realtime audio bridge.");
1858
1624
  this.#sessions.refreshSpeechReadiness(session);
1859
1625
  return {};
1860
1626
  }
@@ -1879,8 +1645,8 @@ var GoogleMeetRuntime = class {
1879
1645
  logger: this.params.logger,
1880
1646
  ...request.requesterSessionKey ? { requesterSessionKey: request.requesterSessionKey } : {},
1881
1647
  agentId: delegatedAgentId,
1882
- sessionKey: delegatedAgentId ? `agent:${delegatedAgentId}:google-meet:${session.id}` : buildTwilioVoiceCallSessionKey(session.id),
1883
- message: isGoogleMeetTalkBackMode$1(session.mode) ? request.message ?? this.params.config.voiceCall.introMessage ?? this.params.config.realtime.introMessage : void 0
1648
+ sessionKey: delegatedAgentId ? `agent:${delegatedAgentId}:google-meet:${session.id}` : `voice:google-meet:${session.id}`,
1649
+ message: MeetingPlatformAdapter.isTalkBackMode(session.mode) ? request.message ?? this.params.config.voiceCall.introMessage ?? this.params.config.realtime.introMessage : void 0
1884
1650
  }) : void 0;
1885
1651
  session.twilio = {
1886
1652
  dialInNumber,
@@ -1912,7 +1678,7 @@ var GoogleMeetRuntime = class {
1912
1678
  } : void 0;
1913
1679
  }
1914
1680
  async #ensureChromeRealtimeBridge(session) {
1915
- if (!isGoogleMeetTalkBackMode$1(session.mode) || !isBrowserTransport(session.transport) || session.state !== "active" || !session.chrome || session.chrome.audioBridge || session.chrome.health?.inCall !== true || session.chrome.health.micMuted !== false || session.chrome.health.manualActionRequired === true) return;
1681
+ if (!MeetingPlatformAdapter.isTalkBackMode(session.mode) || !isBrowserTransport(session.transport) || session.state !== "active" || !session.chrome || session.chrome.audioBridge || session.chrome.health?.inCall !== true || session.chrome.health.micMuted !== false || session.chrome.health.manualAction) return;
1916
1682
  const config = withSessionAgentConfig(this.params.config, session.agentId);
1917
1683
  const recoveryConfig = {
1918
1684
  ...config,
@@ -1950,6 +1716,7 @@ var GoogleMeetRuntime = class {
1950
1716
  const result = session.transport === "chrome-node" ? await recoverCurrentMeetTabOnNode({
1951
1717
  runtime: this.params.runtime,
1952
1718
  config: this.params.config,
1719
+ fullConfig: this.params.fullConfig,
1953
1720
  mode: session.mode,
1954
1721
  readOnly: options.readOnly,
1955
1722
  trackedMeetingUrl: session.url,
@@ -1958,6 +1725,7 @@ var GoogleMeetRuntime = class {
1958
1725
  }) : await recoverCurrentMeetTab({
1959
1726
  runtime: this.params.runtime,
1960
1727
  config: this.params.config,
1728
+ fullConfig: this.params.fullConfig,
1961
1729
  mode: session.mode,
1962
1730
  readOnly: options.readOnly,
1963
1731
  trackedMeetingUrl: session.url,
@@ -2068,11 +1836,13 @@ var GoogleMeetRuntime = class {
2068
1836
  runtime: this.params.runtime,
2069
1837
  nodeId: session.chrome?.nodeId,
2070
1838
  config: this.params.config,
1839
+ meetingSessionId: session.id,
2071
1840
  meetingUrl: session.url,
2072
1841
  tab
2073
1842
  }) : await leaveChromeMeet({
2074
1843
  runtime: this.params.runtime,
2075
1844
  config: this.params.config,
1845
+ meetingSessionId: session.id,
2076
1846
  meetingUrl: session.url,
2077
1847
  tab
2078
1848
  });
@@ -2082,107 +1852,367 @@ var GoogleMeetRuntime = class {
2082
1852
  noteSession(session, `Browser control could not leave the Meet tab: ${formatErrorMessage(error)}`);
2083
1853
  left = false;
2084
1854
  }
2085
- if (session.chrome && left) session.chrome.browserTab = void 0;
1855
+ if (session.chrome && left) {
1856
+ session.chrome.browserTab = void 0;
1857
+ if (session.chrome.health) session.chrome.health = {
1858
+ ...session.chrome.health,
1859
+ captioning: false,
1860
+ audioOutputRouted: false,
1861
+ providerConnected: false,
1862
+ realtimeReady: false,
1863
+ audioInputActive: false,
1864
+ audioOutputActive: false
1865
+ };
1866
+ }
2086
1867
  session.browserLeft = left;
2087
1868
  return left;
2088
1869
  }
2089
1870
  };
2090
1871
  //#endregion
2091
- //#region extensions/google-meet/index.ts
2092
- const loadGoogleMeetCreateModule = createLazyRuntimeModule(() => import("./create-DK5lNDEp.js"));
2093
- const loadGoogleMeetCliModule = createLazyRuntimeModule(() => import("./cli-CxRkKrq1.js"));
2094
- const googleMeetConfigSchema = {
2095
- parse(value) {
2096
- return resolveGoogleMeetConfig(value);
1872
+ //#region extensions/google-meet/src/plugin-helpers.ts
1873
+ const loadGoogleMeetCreateModule = createLazyRuntimeModule(() => import("./create-af1MDlus.js").then((n) => n.t));
1874
+ const loadGoogleMeetCliModule = createLazyRuntimeModule(() => import("./cli-GeuAaEIy.js"));
1875
+ function asParamRecord(params) {
1876
+ return params && typeof params === "object" && !Array.isArray(params) ? params : {};
1877
+ }
1878
+ function normalizeTransport(value) {
1879
+ return value === "chrome" || value === "chrome-node" || value === "twilio" ? value : void 0;
1880
+ }
1881
+ function normalizeMode(value) {
1882
+ if (value === "realtime") return "agent";
1883
+ return value === "agent" || value === "bidi" || value === "transcribe" ? value : void 0;
1884
+ }
1885
+ function resolveMeetingInput(config, value) {
1886
+ const meeting = normalizeOptionalString(value) ?? config.defaults.meeting;
1887
+ if (!meeting) throw new Error("Meeting input is required");
1888
+ return meeting;
1889
+ }
1890
+ function shouldJoinCreatedMeet(raw) {
1891
+ return raw.join !== false && raw.join !== "false";
1892
+ }
1893
+ const googleMeetToolDeps = {
1894
+ callGatewayFromCli,
1895
+ platform: () => process.platform
1896
+ };
1897
+ const testing = {
1898
+ setCallGatewayFromCliForTests(next) {
1899
+ googleMeetToolDeps.callGatewayFromCli = next ?? callGatewayFromCli;
2097
1900
  },
2098
- uiHints: {
2099
- "defaults.meeting": {
2100
- label: "Default Meeting",
2101
- help: "Meet URL, meeting code, or spaces/{id} used when CLI commands omit a meeting."
2102
- },
2103
- "preview.enrollmentAcknowledged": {
2104
- label: "Preview Acknowledged",
2105
- help: "Confirms you understand the Google Meet Media API is still Developer Preview.",
2106
- advanced: true
2107
- },
2108
- defaultTransport: {
2109
- label: "Default Transport",
2110
- help: "Chrome uses a signed-in browser profile. Chrome-node runs Chrome on a paired node. Twilio uses Meet dial-in numbers."
2111
- },
2112
- defaultMode: {
2113
- label: "Default Mode",
2114
- help: "Agent uses realtime transcription plus regular OpenClaw TTS. Bidi uses the realtime voice model directly. Transcribe observes only."
2115
- },
2116
- "chrome.audioBackend": {
2117
- label: "Chrome Audio Backend",
2118
- help: "BlackHole 2ch is required for local duplex audio routing."
2119
- },
2120
- "chrome.launch": { label: "Launch Chrome" },
2121
- "chrome.browserProfile": {
2122
- label: "Chrome Profile",
2123
- advanced: true
2124
- },
2125
- "chrome.guestName": {
2126
- label: "Guest Name",
2127
- help: "Used when Chrome lands on the signed-out Meet guest-name screen."
2128
- },
2129
- "chrome.reuseExistingTab": {
2130
- label: "Reuse Existing Meet Tab",
2131
- help: "Avoids opening duplicate tabs for the same Meet URL."
2132
- },
2133
- "chrome.autoJoin": {
2134
- label: "Auto Join Guest Screen",
2135
- help: "Best-effort guest-name fill and Join Now click through OpenClaw browser automation."
2136
- },
2137
- "chrome.waitForInCallMs": {
2138
- label: "Wait For In-Call (ms)",
2139
- help: "Waits for Chrome to report that the Meet tab is in-call before the realtime intro speaks.",
2140
- advanced: true
2141
- },
2142
- "chrome.audioFormat": {
2143
- label: "Audio Format",
2144
- help: "Command-pair audio format. PCM16 24 kHz is the default Chrome/Meet path; G.711 mu-law 8 kHz remains available for legacy command pairs.",
2145
- advanced: true
2146
- },
2147
- "chrome.audioBufferBytes": {
2148
- label: "Audio Buffer Bytes",
2149
- help: "SoX processing buffer for generated Chrome command-pair audio commands. Lower values reduce latency but may underrun on busy hosts.",
2150
- advanced: true
2151
- },
2152
- "chrome.audioInputCommand": {
2153
- label: "Audio Input Command",
2154
- help: "Command that writes meeting audio to stdout in chrome.audioFormat.",
2155
- advanced: true
2156
- },
2157
- "chrome.audioOutputCommand": {
2158
- label: "Audio Output Command",
2159
- help: "Command that reads assistant audio from stdin in chrome.audioFormat.",
2160
- advanced: true
2161
- },
2162
- "chrome.bargeInInputCommand": {
2163
- label: "Barge-In Input Command",
2164
- help: "Optional Gateway-hosted microphone command that writes signed 16-bit little-endian mono PCM for human interruption detection while assistant playback is active.",
2165
- advanced: true
2166
- },
2167
- "chrome.bargeInRmsThreshold": {
2168
- label: "Barge-In RMS Threshold",
2169
- help: "RMS level on chrome.bargeInInputCommand that counts as a human interruption.",
2170
- advanced: true
2171
- },
2172
- "chrome.bargeInPeakThreshold": {
2173
- label: "Barge-In Peak Threshold",
2174
- help: "Peak level on chrome.bargeInInputCommand that counts as a human interruption.",
2175
- advanced: true
2176
- },
2177
- "chrome.bargeInCooldownMs": {
2178
- label: "Barge-In Cooldown (ms)",
2179
- help: "Minimum delay between repeated barge-in clears.",
2180
- advanced: true
2181
- },
2182
- "chrome.audioBridgeCommand": {
2183
- label: "Audio Bridge Command",
2184
- advanced: true
2185
- },
1901
+ setPlatformForTests(next) {
1902
+ googleMeetToolDeps.platform = next ?? (() => process.platform);
1903
+ },
1904
+ isGoogleMeetAgentToolActionUnsupportedOnHost,
1905
+ resolveGoogleMeetGatewayOperationTimeoutMs
1906
+ };
1907
+ function googleMeetGatewayMethodForToolAction(action) {
1908
+ switch (action) {
1909
+ case "recover_current_tab": return "googlemeet.recoverCurrentTab";
1910
+ case "setup_status": return "googlemeet.setup";
1911
+ case "test_speech": return "googlemeet.testSpeech";
1912
+ case "test_listen": return "googlemeet.testListen";
1913
+ case "end_active_conference": return "googlemeet.endActiveConference";
1914
+ default: return `googlemeet.${action}`;
1915
+ }
1916
+ }
1917
+ function isGoogleMeetAgentToolActionUnsupportedOnHost(params) {
1918
+ if ((params.platform ?? googleMeetToolDeps.platform()) === "darwin") return false;
1919
+ const action = params.raw.action;
1920
+ if (action !== "join" && action !== "test_speech" && !(action === "create" && shouldJoinCreatedMeet(params.raw))) return false;
1921
+ const transport = normalizeTransport(params.raw.transport) ?? params.config.defaultTransport;
1922
+ const mode = action === "test_speech" ? "agent" : normalizeMode(params.raw.mode) ?? params.config.defaultMode;
1923
+ return transport === "chrome" && MeetingPlatformAdapter.isTalkBackMode(mode);
1924
+ }
1925
+ function assertGoogleMeetAgentToolActionSupported(params) {
1926
+ if (!isGoogleMeetAgentToolActionUnsupportedOnHost(params)) return;
1927
+ throw new Error("Google Meet local Chrome talk-back audio is macOS-only. On this host, use mode: transcribe, transport: twilio, or transport: chrome-node backed by a macOS node.");
1928
+ }
1929
+ function readGatewayErrorDetails(err) {
1930
+ if (!err || typeof err !== "object" || !("details" in err)) return;
1931
+ return err.details;
1932
+ }
1933
+ async function callGoogleMeetGatewayFromTool(params) {
1934
+ try {
1935
+ if (params.runtime) return await params.runtime.gateway.request(googleMeetGatewayMethodForToolAction(params.action), params.raw, {
1936
+ timeoutMs: resolveGoogleMeetGatewayOperationTimeoutMs(params.config),
1937
+ scopes: ["operator.admin"]
1938
+ });
1939
+ return await googleMeetToolDeps.callGatewayFromCli(googleMeetGatewayMethodForToolAction(params.action), {
1940
+ json: true,
1941
+ timeout: String(resolveGoogleMeetGatewayOperationTimeoutMs(params.config))
1942
+ }, params.raw, {
1943
+ progress: false,
1944
+ scopes: ["operator.admin"]
1945
+ });
1946
+ } catch (err) {
1947
+ const details = readGatewayErrorDetails(err);
1948
+ if (details && typeof details === "object") return details;
1949
+ throw err;
1950
+ }
1951
+ }
1952
+ function keepTrustedToolAgentId(raw, client) {
1953
+ const { agentId: rawAgentId, ...rest } = raw;
1954
+ if (client?.internal?.pluginRuntimeOwnerId !== "google-meet") return rest;
1955
+ const agentId = normalizeOptionalString(rawAgentId);
1956
+ return agentId ? {
1957
+ ...rest,
1958
+ agentId
1959
+ } : rest;
1960
+ }
1961
+ async function createMeetFromParams(params) {
1962
+ return (await loadGoogleMeetCreateModule()).createMeetFromParams(params);
1963
+ }
1964
+ async function createAndJoinMeetFromParams(params) {
1965
+ return (await loadGoogleMeetCreateModule()).createAndJoinMeetFromParams(params);
1966
+ }
1967
+ async function resolveGoogleMeetTokenFromParams(config, raw) {
1968
+ const { resolveGoogleMeetAccessToken } = await import("./oauth-DOfMKlGp.js");
1969
+ return resolveGoogleMeetAccessToken({
1970
+ clientId: normalizeOptionalString(raw.clientId) ?? config.oauth.clientId,
1971
+ clientSecret: normalizeOptionalString(raw.clientSecret) ?? config.oauth.clientSecret,
1972
+ refreshToken: normalizeOptionalString(raw.refreshToken) ?? config.oauth.refreshToken,
1973
+ accessToken: normalizeOptionalString(raw.accessToken) ?? config.oauth.accessToken,
1974
+ expiresAt: typeof raw.expiresAt === "number" ? raw.expiresAt : config.oauth.expiresAt
1975
+ });
1976
+ }
1977
+ function wantsCalendarLookup(raw) {
1978
+ return raw.today === true || Boolean(normalizeOptionalString(raw.event));
1979
+ }
1980
+ async function resolveMeetingFromParams(params) {
1981
+ if (wantsCalendarLookup(params.raw)) {
1982
+ const window = params.raw.today === true ? buildGoogleMeetCalendarDayWindow() : {};
1983
+ const calendarEvent = await findGoogleMeetCalendarEvent({
1984
+ accessToken: params.accessToken,
1985
+ calendarId: normalizeOptionalString(params.raw.calendarId),
1986
+ eventQuery: normalizeOptionalString(params.raw.event),
1987
+ ...window
1988
+ });
1989
+ return {
1990
+ meeting: calendarEvent.meetingUri,
1991
+ calendarEvent
1992
+ };
1993
+ }
1994
+ return { meeting: resolveMeetingInput(params.config, params.raw.meeting) };
1995
+ }
1996
+ async function resolveSpaceFromParams(config, raw) {
1997
+ const token = await resolveGoogleMeetTokenFromParams(config, raw);
1998
+ const { meeting, calendarEvent } = await resolveMeetingFromParams({
1999
+ config,
2000
+ raw,
2001
+ accessToken: token.accessToken
2002
+ });
2003
+ return {
2004
+ meeting,
2005
+ token,
2006
+ space: await fetchGoogleMeetSpace({
2007
+ accessToken: token.accessToken,
2008
+ meeting
2009
+ }),
2010
+ calendarEvent
2011
+ };
2012
+ }
2013
+ async function resolveArtifactQueryFromParams(config, raw) {
2014
+ const meeting = normalizeOptionalString(raw.meeting) ?? config.defaults.meeting;
2015
+ const conferenceRecord = normalizeOptionalString(raw.conferenceRecord);
2016
+ const token = await resolveGoogleMeetTokenFromParams(config, raw);
2017
+ const resolvedMeeting = conferenceRecord ? { meeting } : wantsCalendarLookup(raw) ? await resolveMeetingFromParams({
2018
+ config,
2019
+ raw,
2020
+ accessToken: token.accessToken
2021
+ }) : { meeting };
2022
+ if (!resolvedMeeting.meeting && !conferenceRecord) throw new Error("Meeting input, calendar lookup, or conferenceRecord required");
2023
+ return {
2024
+ token,
2025
+ meeting: resolvedMeeting.meeting,
2026
+ calendarEvent: resolvedMeeting.calendarEvent,
2027
+ conferenceRecord,
2028
+ pageSize: readPositiveIntegerParam(raw, "pageSize"),
2029
+ includeTranscriptEntries: raw.includeTranscriptEntries !== false,
2030
+ includeDocumentBodies: raw.includeDocumentBodies === true,
2031
+ allConferenceRecords: raw.includeAllConferenceRecords === true,
2032
+ mergeDuplicateParticipants: raw.mergeDuplicateParticipants !== false,
2033
+ lateAfterMinutes: readPositiveIntegerParam(raw, "lateAfterMinutes"),
2034
+ earlyBeforeMinutes: readPositiveIntegerParam(raw, "earlyBeforeMinutes")
2035
+ };
2036
+ }
2037
+ async function exportGoogleMeetBundleFromParams(config, raw) {
2038
+ const resolved = await resolveArtifactQueryFromParams(config, raw);
2039
+ const [artifacts, attendance] = await Promise.all([fetchGoogleMeetArtifacts({
2040
+ accessToken: resolved.token.accessToken,
2041
+ meeting: resolved.meeting,
2042
+ conferenceRecord: resolved.conferenceRecord,
2043
+ pageSize: resolved.pageSize,
2044
+ includeTranscriptEntries: resolved.includeTranscriptEntries,
2045
+ includeDocumentBodies: resolved.includeDocumentBodies,
2046
+ allConferenceRecords: resolved.allConferenceRecords
2047
+ }), fetchGoogleMeetAttendance({
2048
+ accessToken: resolved.token.accessToken,
2049
+ meeting: resolved.meeting,
2050
+ conferenceRecord: resolved.conferenceRecord,
2051
+ pageSize: resolved.pageSize,
2052
+ allConferenceRecords: resolved.allConferenceRecords,
2053
+ mergeDuplicateParticipants: resolved.mergeDuplicateParticipants,
2054
+ lateAfterMinutes: resolved.lateAfterMinutes,
2055
+ earlyBeforeMinutes: resolved.earlyBeforeMinutes
2056
+ })]);
2057
+ const { buildGoogleMeetExportManifest, googleMeetExportFileNames, writeMeetExportBundle } = await loadGoogleMeetCliModule();
2058
+ const calendarId = normalizeOptionalString(raw.calendarId);
2059
+ const request = {
2060
+ ...resolved.meeting ? { meeting: resolved.meeting } : {},
2061
+ ...resolved.conferenceRecord ? { conferenceRecord: resolved.conferenceRecord } : {},
2062
+ ...resolved.calendarEvent?.event.id ? { calendarEventId: resolved.calendarEvent.event.id } : {},
2063
+ ...resolved.calendarEvent?.event.summary ? { calendarEventSummary: resolved.calendarEvent.event.summary } : {},
2064
+ ...calendarId ? { calendarId } : {},
2065
+ ...resolved.pageSize !== void 0 ? { pageSize: resolved.pageSize } : {},
2066
+ includeTranscriptEntries: resolved.includeTranscriptEntries,
2067
+ includeDocumentBodies: resolved.includeDocumentBodies,
2068
+ allConferenceRecords: resolved.allConferenceRecords,
2069
+ mergeDuplicateParticipants: resolved.mergeDuplicateParticipants,
2070
+ ...resolved.lateAfterMinutes !== void 0 ? { lateAfterMinutes: resolved.lateAfterMinutes } : {},
2071
+ ...resolved.earlyBeforeMinutes !== void 0 ? { earlyBeforeMinutes: resolved.earlyBeforeMinutes } : {}
2072
+ };
2073
+ const tokenSource = resolved.token.refreshed ? "refresh-token" : "cached-access-token";
2074
+ if (raw.dryRun === true) return {
2075
+ dryRun: true,
2076
+ manifest: buildGoogleMeetExportManifest({
2077
+ artifacts,
2078
+ attendance,
2079
+ files: googleMeetExportFileNames(),
2080
+ request,
2081
+ tokenSource,
2082
+ ...resolved.calendarEvent ? { calendarEvent: resolved.calendarEvent } : {}
2083
+ }),
2084
+ ...resolved.calendarEvent ? { calendarEvent: resolved.calendarEvent } : {},
2085
+ tokenSource
2086
+ };
2087
+ const outputDir = normalizeOptionalString(raw.outputDir) ?? normalizeOptionalString(raw.output);
2088
+ return {
2089
+ ...await writeMeetExportBundle({
2090
+ ...outputDir ? { outputDir } : {},
2091
+ artifacts,
2092
+ attendance,
2093
+ zip: raw.zip === true,
2094
+ request,
2095
+ tokenSource,
2096
+ ...resolved.calendarEvent ? { calendarEvent: resolved.calendarEvent } : {}
2097
+ }),
2098
+ ...resolved.calendarEvent ? { calendarEvent: resolved.calendarEvent } : {},
2099
+ tokenSource
2100
+ };
2101
+ }
2102
+ function createGoogleMeetRuntimeAccessor(params) {
2103
+ let runtime = null;
2104
+ return async () => {
2105
+ if (!params.config.enabled) throw new Error("Google Meet plugin disabled in plugin config");
2106
+ if (!runtime) runtime = new GoogleMeetRuntime({
2107
+ config: params.config,
2108
+ fullConfig: params.api.config,
2109
+ runtime: params.api.runtime,
2110
+ logger: params.api.logger
2111
+ });
2112
+ return runtime;
2113
+ };
2114
+ }
2115
+ function formatGoogleMeetGatewayError(err) {
2116
+ return isGoogleMeetBrowserManualActionError(err) ? err.payload : { error: formatErrorMessage(err) };
2117
+ }
2118
+ function sendGoogleMeetGatewayError(respond, err, code = ErrorCodes.UNAVAILABLE) {
2119
+ const payload = formatGoogleMeetGatewayError(err);
2120
+ respond(false, payload, errorShape(code, typeof payload.error === "string" ? payload.error : "Google Meet request failed", { details: payload }));
2121
+ }
2122
+ //#endregion
2123
+ //#region extensions/google-meet/src/plugin-schema.ts
2124
+ const googleMeetConfigSchema = {
2125
+ parse(value) {
2126
+ return resolveGoogleMeetConfig(value);
2127
+ },
2128
+ uiHints: {
2129
+ "defaults.meeting": {
2130
+ label: "Default Meeting",
2131
+ help: "Meet URL, meeting code, or spaces/{id} used when CLI commands omit a meeting."
2132
+ },
2133
+ "preview.enrollmentAcknowledged": {
2134
+ label: "Preview Acknowledged",
2135
+ help: "Confirms you understand the Google Meet Media API is still Developer Preview.",
2136
+ advanced: true
2137
+ },
2138
+ defaultTransport: {
2139
+ label: "Default Transport",
2140
+ help: "Chrome uses a signed-in browser profile. Chrome-node runs Chrome on a paired node. Twilio uses Meet dial-in numbers."
2141
+ },
2142
+ defaultMode: {
2143
+ label: "Default Mode",
2144
+ help: "Agent uses realtime transcription plus regular OpenClaw TTS. Bidi uses the realtime voice model directly. Transcribe observes only."
2145
+ },
2146
+ "chrome.audioBackend": {
2147
+ label: "Chrome Audio Backend",
2148
+ help: "BlackHole 2ch is required for local duplex audio routing."
2149
+ },
2150
+ "chrome.launch": { label: "Launch Chrome" },
2151
+ "chrome.browserProfile": {
2152
+ label: "Chrome Profile",
2153
+ advanced: true
2154
+ },
2155
+ "chrome.guestName": {
2156
+ label: "Guest Name",
2157
+ help: "Used when Chrome lands on the signed-out Meet guest-name screen."
2158
+ },
2159
+ "chrome.reuseExistingTab": {
2160
+ label: "Reuse Existing Meet Tab",
2161
+ help: "Avoids opening duplicate tabs for the same Meet URL."
2162
+ },
2163
+ "chrome.autoJoin": {
2164
+ label: "Auto Join Guest Screen",
2165
+ help: "Best-effort guest-name fill and Join Now click through OpenClaw browser automation."
2166
+ },
2167
+ "chrome.waitForInCallMs": {
2168
+ label: "Wait For In-Call (ms)",
2169
+ help: "Waits for Chrome to report that the Meet tab is in-call before the realtime intro speaks.",
2170
+ advanced: true
2171
+ },
2172
+ "chrome.audioFormat": {
2173
+ label: "Audio Format",
2174
+ help: "Command-pair audio format. PCM16 24 kHz is the default Chrome/Meet path; G.711 mu-law 8 kHz remains available for legacy command pairs.",
2175
+ advanced: true
2176
+ },
2177
+ "chrome.audioBufferBytes": {
2178
+ label: "Audio Buffer Bytes",
2179
+ help: "SoX processing buffer for generated Chrome command-pair audio commands. Lower values reduce latency but may underrun on busy hosts.",
2180
+ advanced: true
2181
+ },
2182
+ "chrome.audioInputCommand": {
2183
+ label: "Audio Input Command",
2184
+ help: "Command that writes meeting audio to stdout in chrome.audioFormat.",
2185
+ advanced: true
2186
+ },
2187
+ "chrome.audioOutputCommand": {
2188
+ label: "Audio Output Command",
2189
+ help: "Command that reads assistant audio from stdin in chrome.audioFormat.",
2190
+ advanced: true
2191
+ },
2192
+ "chrome.bargeInInputCommand": {
2193
+ label: "Barge-In Input Command",
2194
+ help: "Optional Gateway-hosted microphone command that writes signed 16-bit little-endian mono PCM for human interruption detection while assistant playback is active.",
2195
+ advanced: true
2196
+ },
2197
+ "chrome.bargeInRmsThreshold": {
2198
+ label: "Barge-In RMS Threshold",
2199
+ help: "RMS level on chrome.bargeInInputCommand that counts as a human interruption.",
2200
+ advanced: true
2201
+ },
2202
+ "chrome.bargeInPeakThreshold": {
2203
+ label: "Barge-In Peak Threshold",
2204
+ help: "Peak level on chrome.bargeInInputCommand that counts as a human interruption.",
2205
+ advanced: true
2206
+ },
2207
+ "chrome.bargeInCooldownMs": {
2208
+ label: "Barge-In Cooldown (ms)",
2209
+ help: "Minimum delay between repeated barge-in clears.",
2210
+ advanced: true
2211
+ },
2212
+ "chrome.audioBridgeCommand": {
2213
+ label: "Audio Bridge Command",
2214
+ advanced: true
2215
+ },
2186
2216
  "chrome.audioBridgeHealthCommand": {
2187
2217
  label: "Audio Bridge Health Command",
2188
2218
  advanced: true
@@ -2377,236 +2407,8 @@ const GoogleMeetToolSchema = Type.Object({
2377
2407
  clientSecret: Type.Optional(Type.String({ description: "OAuth client secret override" })),
2378
2408
  expiresAt: Type.Optional(Type.Number({ description: "Cached access token expiry ms" }))
2379
2409
  });
2380
- function asParamRecord(params) {
2381
- return params && typeof params === "object" && !Array.isArray(params) ? params : {};
2382
- }
2383
- function normalizeTransport(value) {
2384
- return value === "chrome" || value === "chrome-node" || value === "twilio" ? value : void 0;
2385
- }
2386
- function normalizeMode(value) {
2387
- if (value === "realtime") return "agent";
2388
- return value === "agent" || value === "bidi" || value === "transcribe" ? value : void 0;
2389
- }
2390
- function isGoogleMeetTalkBackMode(mode) {
2391
- return mode === "agent" || mode === "bidi";
2392
- }
2393
- function resolveMeetingInput(config, value) {
2394
- const meeting = normalizeOptionalString(value) ?? config.defaults.meeting;
2395
- if (!meeting) throw new Error("Meeting input is required");
2396
- return meeting;
2397
- }
2398
- function shouldJoinCreatedMeet(raw) {
2399
- return raw.join !== false && raw.join !== "false";
2400
- }
2401
- const googleMeetToolDeps = {
2402
- callGatewayFromCli,
2403
- platform: () => process.platform
2404
- };
2405
- const testing = {
2406
- setCallGatewayFromCliForTests(next) {
2407
- googleMeetToolDeps.callGatewayFromCli = next ?? callGatewayFromCli;
2408
- },
2409
- setPlatformForTests(next) {
2410
- googleMeetToolDeps.platform = next ?? (() => process.platform);
2411
- },
2412
- isGoogleMeetAgentToolActionUnsupportedOnHost,
2413
- resolveGoogleMeetGatewayOperationTimeoutMs
2414
- };
2415
- function googleMeetGatewayMethodForToolAction(action) {
2416
- switch (action) {
2417
- case "recover_current_tab": return "googlemeet.recoverCurrentTab";
2418
- case "setup_status": return "googlemeet.setup";
2419
- case "test_speech": return "googlemeet.testSpeech";
2420
- case "test_listen": return "googlemeet.testListen";
2421
- case "end_active_conference": return "googlemeet.endActiveConference";
2422
- default: return `googlemeet.${action}`;
2423
- }
2424
- }
2425
- function isGoogleMeetAgentToolActionUnsupportedOnHost(params) {
2426
- if ((params.platform ?? googleMeetToolDeps.platform()) === "darwin") return false;
2427
- const action = params.raw.action;
2428
- if (action !== "join" && action !== "test_speech" && !(action === "create" && shouldJoinCreatedMeet(params.raw))) return false;
2429
- const transport = normalizeTransport(params.raw.transport) ?? params.config.defaultTransport;
2430
- const mode = action === "test_speech" ? "agent" : normalizeMode(params.raw.mode) ?? params.config.defaultMode;
2431
- return transport === "chrome" && isGoogleMeetTalkBackMode(mode);
2432
- }
2433
- function assertGoogleMeetAgentToolActionSupported(params) {
2434
- if (!isGoogleMeetAgentToolActionUnsupportedOnHost(params)) return;
2435
- throw new Error("Google Meet local Chrome talk-back audio is macOS-only. On this host, use mode: transcribe, transport: twilio, or transport: chrome-node backed by a macOS node.");
2436
- }
2437
- function readGatewayErrorDetails(err) {
2438
- if (!err || typeof err !== "object" || !("details" in err)) return;
2439
- return err.details;
2440
- }
2441
- async function callGoogleMeetGatewayFromTool(params) {
2442
- try {
2443
- if (params.runtime) return await params.runtime.gateway.request(googleMeetGatewayMethodForToolAction(params.action), params.raw, {
2444
- timeoutMs: resolveGoogleMeetGatewayOperationTimeoutMs(params.config),
2445
- scopes: ["operator.admin"]
2446
- });
2447
- return await googleMeetToolDeps.callGatewayFromCli(googleMeetGatewayMethodForToolAction(params.action), {
2448
- json: true,
2449
- timeout: String(resolveGoogleMeetGatewayOperationTimeoutMs(params.config))
2450
- }, params.raw, {
2451
- progress: false,
2452
- scopes: ["operator.admin"]
2453
- });
2454
- } catch (err) {
2455
- const details = readGatewayErrorDetails(err);
2456
- if (details && typeof details === "object") return details;
2457
- throw err;
2458
- }
2459
- }
2460
- function keepTrustedToolAgentId(raw, client) {
2461
- const { agentId: rawAgentId, ...rest } = raw;
2462
- if (client?.internal?.pluginRuntimeOwnerId !== "google-meet") return rest;
2463
- const agentId = normalizeOptionalString(rawAgentId);
2464
- return agentId ? {
2465
- ...rest,
2466
- agentId
2467
- } : rest;
2468
- }
2469
- async function createMeetFromParams(params) {
2470
- return (await loadGoogleMeetCreateModule()).createMeetFromParams(params);
2471
- }
2472
- async function createAndJoinMeetFromParams(params) {
2473
- return (await loadGoogleMeetCreateModule()).createAndJoinMeetFromParams(params);
2474
- }
2475
- async function resolveGoogleMeetTokenFromParams(config, raw) {
2476
- const { resolveGoogleMeetAccessToken } = await import("./oauth-DOfMKlGp.js");
2477
- return resolveGoogleMeetAccessToken({
2478
- clientId: normalizeOptionalString(raw.clientId) ?? config.oauth.clientId,
2479
- clientSecret: normalizeOptionalString(raw.clientSecret) ?? config.oauth.clientSecret,
2480
- refreshToken: normalizeOptionalString(raw.refreshToken) ?? config.oauth.refreshToken,
2481
- accessToken: normalizeOptionalString(raw.accessToken) ?? config.oauth.accessToken,
2482
- expiresAt: typeof raw.expiresAt === "number" ? raw.expiresAt : config.oauth.expiresAt
2483
- });
2484
- }
2485
- function wantsCalendarLookup(raw) {
2486
- return raw.today === true || Boolean(normalizeOptionalString(raw.event));
2487
- }
2488
- async function resolveMeetingFromParams(params) {
2489
- if (wantsCalendarLookup(params.raw)) {
2490
- const window = params.raw.today === true ? buildGoogleMeetCalendarDayWindow() : {};
2491
- const calendarEvent = await findGoogleMeetCalendarEvent({
2492
- accessToken: params.accessToken,
2493
- calendarId: normalizeOptionalString(params.raw.calendarId),
2494
- eventQuery: normalizeOptionalString(params.raw.event),
2495
- ...window
2496
- });
2497
- return {
2498
- meeting: calendarEvent.meetingUri,
2499
- calendarEvent
2500
- };
2501
- }
2502
- return { meeting: resolveMeetingInput(params.config, params.raw.meeting) };
2503
- }
2504
- async function resolveSpaceFromParams(config, raw) {
2505
- const token = await resolveGoogleMeetTokenFromParams(config, raw);
2506
- const { meeting, calendarEvent } = await resolveMeetingFromParams({
2507
- config,
2508
- raw,
2509
- accessToken: token.accessToken
2510
- });
2511
- return {
2512
- meeting,
2513
- token,
2514
- space: await fetchGoogleMeetSpace({
2515
- accessToken: token.accessToken,
2516
- meeting
2517
- }),
2518
- calendarEvent
2519
- };
2520
- }
2521
- async function resolveArtifactQueryFromParams(config, raw) {
2522
- const meeting = normalizeOptionalString(raw.meeting) ?? config.defaults.meeting;
2523
- const conferenceRecord = normalizeOptionalString(raw.conferenceRecord);
2524
- const token = await resolveGoogleMeetTokenFromParams(config, raw);
2525
- const resolvedMeeting = conferenceRecord ? { meeting } : wantsCalendarLookup(raw) ? await resolveMeetingFromParams({
2526
- config,
2527
- raw,
2528
- accessToken: token.accessToken
2529
- }) : { meeting };
2530
- if (!resolvedMeeting.meeting && !conferenceRecord) throw new Error("Meeting input, calendar lookup, or conferenceRecord required");
2531
- return {
2532
- token,
2533
- meeting: resolvedMeeting.meeting,
2534
- calendarEvent: resolvedMeeting.calendarEvent,
2535
- conferenceRecord,
2536
- pageSize: readPositiveIntegerParam(raw, "pageSize"),
2537
- includeTranscriptEntries: raw.includeTranscriptEntries !== false,
2538
- includeDocumentBodies: raw.includeDocumentBodies === true,
2539
- allConferenceRecords: raw.includeAllConferenceRecords === true,
2540
- mergeDuplicateParticipants: raw.mergeDuplicateParticipants !== false,
2541
- lateAfterMinutes: readPositiveIntegerParam(raw, "lateAfterMinutes"),
2542
- earlyBeforeMinutes: readPositiveIntegerParam(raw, "earlyBeforeMinutes")
2543
- };
2544
- }
2545
- async function exportGoogleMeetBundleFromParams(config, raw) {
2546
- const resolved = await resolveArtifactQueryFromParams(config, raw);
2547
- const [artifacts, attendance] = await Promise.all([fetchGoogleMeetArtifacts({
2548
- accessToken: resolved.token.accessToken,
2549
- meeting: resolved.meeting,
2550
- conferenceRecord: resolved.conferenceRecord,
2551
- pageSize: resolved.pageSize,
2552
- includeTranscriptEntries: resolved.includeTranscriptEntries,
2553
- includeDocumentBodies: resolved.includeDocumentBodies,
2554
- allConferenceRecords: resolved.allConferenceRecords
2555
- }), fetchGoogleMeetAttendance({
2556
- accessToken: resolved.token.accessToken,
2557
- meeting: resolved.meeting,
2558
- conferenceRecord: resolved.conferenceRecord,
2559
- pageSize: resolved.pageSize,
2560
- allConferenceRecords: resolved.allConferenceRecords,
2561
- mergeDuplicateParticipants: resolved.mergeDuplicateParticipants,
2562
- lateAfterMinutes: resolved.lateAfterMinutes,
2563
- earlyBeforeMinutes: resolved.earlyBeforeMinutes
2564
- })]);
2565
- const { buildGoogleMeetExportManifest, googleMeetExportFileNames, writeMeetExportBundle } = await loadGoogleMeetCliModule();
2566
- const calendarId = normalizeOptionalString(raw.calendarId);
2567
- const request = {
2568
- ...resolved.meeting ? { meeting: resolved.meeting } : {},
2569
- ...resolved.conferenceRecord ? { conferenceRecord: resolved.conferenceRecord } : {},
2570
- ...resolved.calendarEvent?.event.id ? { calendarEventId: resolved.calendarEvent.event.id } : {},
2571
- ...resolved.calendarEvent?.event.summary ? { calendarEventSummary: resolved.calendarEvent.event.summary } : {},
2572
- ...calendarId ? { calendarId } : {},
2573
- ...resolved.pageSize !== void 0 ? { pageSize: resolved.pageSize } : {},
2574
- includeTranscriptEntries: resolved.includeTranscriptEntries,
2575
- includeDocumentBodies: resolved.includeDocumentBodies,
2576
- allConferenceRecords: resolved.allConferenceRecords,
2577
- mergeDuplicateParticipants: resolved.mergeDuplicateParticipants,
2578
- ...resolved.lateAfterMinutes !== void 0 ? { lateAfterMinutes: resolved.lateAfterMinutes } : {},
2579
- ...resolved.earlyBeforeMinutes !== void 0 ? { earlyBeforeMinutes: resolved.earlyBeforeMinutes } : {}
2580
- };
2581
- const tokenSource = resolved.token.refreshed ? "refresh-token" : "cached-access-token";
2582
- if (raw.dryRun === true) return {
2583
- dryRun: true,
2584
- manifest: buildGoogleMeetExportManifest({
2585
- artifacts,
2586
- attendance,
2587
- files: googleMeetExportFileNames(),
2588
- request,
2589
- tokenSource,
2590
- ...resolved.calendarEvent ? { calendarEvent: resolved.calendarEvent } : {}
2591
- }),
2592
- ...resolved.calendarEvent ? { calendarEvent: resolved.calendarEvent } : {},
2593
- tokenSource
2594
- };
2595
- const outputDir = normalizeOptionalString(raw.outputDir) ?? normalizeOptionalString(raw.output);
2596
- return {
2597
- ...await writeMeetExportBundle({
2598
- ...outputDir ? { outputDir } : {},
2599
- artifacts,
2600
- attendance,
2601
- zip: raw.zip === true,
2602
- request,
2603
- tokenSource,
2604
- ...resolved.calendarEvent ? { calendarEvent: resolved.calendarEvent } : {}
2605
- }),
2606
- ...resolved.calendarEvent ? { calendarEvent: resolved.calendarEvent } : {},
2607
- tokenSource
2608
- };
2609
- }
2410
+ //#endregion
2411
+ //#region extensions/google-meet/index.ts
2610
2412
  var google_meet_default = definePluginEntry({
2611
2413
  id: "google-meet",
2612
2414
  name: "Google Meet",
@@ -2614,22 +2416,16 @@ var google_meet_default = definePluginEntry({
2614
2416
  configSchema: googleMeetConfigSchema,
2615
2417
  register(api) {
2616
2418
  const config = googleMeetConfigSchema.parse(api.pluginConfig);
2617
- let runtime = null;
2618
- const ensureRuntime = async () => {
2619
- if (!config.enabled) throw new Error("Google Meet plugin disabled in plugin config");
2620
- if (!runtime) runtime = new GoogleMeetRuntime({
2621
- config,
2622
- fullConfig: api.config,
2623
- runtime: api.runtime,
2624
- logger: api.logger
2625
- });
2626
- return runtime;
2627
- };
2628
- const formatGatewayError = (err) => isGoogleMeetBrowserManualActionError(err) ? err.payload : { error: formatErrorMessage(err) };
2629
- const sendError = (respond, err, code = ErrorCodes.UNAVAILABLE) => {
2630
- const payload = formatGatewayError(err);
2631
- respond(false, payload, errorShape(code, typeof payload.error === "string" ? payload.error : "Google Meet request failed", { details: payload }));
2632
- };
2419
+ const ensureRuntime = createGoogleMeetRuntimeAccessor({
2420
+ api,
2421
+ config
2422
+ });
2423
+ api.registerTranscriptSourceProvider(createMeetingTranscriptSourceProvider({
2424
+ id: "google-meet",
2425
+ aliases: ["googlemeet", "meet"],
2426
+ name: "Google Meet",
2427
+ runtime: async () => (await ensureRuntime()).transcriptSourceRuntime()
2428
+ }));
2633
2429
  api.registerGatewayMethod("googlemeet.join", async ({ params, client, respond }) => {
2634
2430
  try {
2635
2431
  const trustedParams = keepTrustedToolAgentId(asParamRecord(params), client);
@@ -2645,7 +2441,7 @@ var google_meet_default = definePluginEntry({
2645
2441
  agentId: normalizeOptionalString(trustedParams.agentId)
2646
2442
  }));
2647
2443
  } catch (err) {
2648
- sendError(respond, err);
2444
+ sendGoogleMeetGatewayError(respond, err);
2649
2445
  }
2650
2446
  });
2651
2447
  api.registerGatewayMethod("googlemeet.create", async ({ params, client, respond }) => {
@@ -2662,31 +2458,31 @@ var google_meet_default = definePluginEntry({
2662
2458
  raw
2663
2459
  }));
2664
2460
  } catch (err) {
2665
- sendError(respond, err);
2461
+ sendGoogleMeetGatewayError(respond, err);
2666
2462
  }
2667
2463
  });
2668
2464
  api.registerGatewayMethod("googlemeet.status", async ({ params, respond }) => {
2669
2465
  try {
2670
2466
  respond(true, await (await ensureRuntime()).status(normalizeOptionalString(params?.sessionId)));
2671
2467
  } catch (err) {
2672
- sendError(respond, err);
2468
+ sendGoogleMeetGatewayError(respond, err);
2673
2469
  }
2674
2470
  });
2675
2471
  api.registerGatewayMethod("googlemeet.transcript", async ({ params, respond }) => {
2676
2472
  try {
2677
2473
  const sessionId = normalizeOptionalString(params?.sessionId);
2678
2474
  if (!sessionId) {
2679
- sendError(respond, /* @__PURE__ */ new Error("sessionId required"), ErrorCodes.INVALID_REQUEST);
2475
+ sendGoogleMeetGatewayError(respond, /* @__PURE__ */ new Error("sessionId required"), ErrorCodes.INVALID_REQUEST);
2680
2476
  return;
2681
2477
  }
2682
2478
  const sinceIndex = params?.sinceIndex;
2683
2479
  if (sinceIndex !== void 0 && (typeof sinceIndex !== "number" || !Number.isSafeInteger(sinceIndex) || sinceIndex < 0)) {
2684
- sendError(respond, /* @__PURE__ */ new Error("sinceIndex must be a non-negative safe integer"), ErrorCodes.INVALID_REQUEST);
2480
+ sendGoogleMeetGatewayError(respond, /* @__PURE__ */ new Error("sinceIndex must be a non-negative safe integer"), ErrorCodes.INVALID_REQUEST);
2685
2481
  return;
2686
2482
  }
2687
2483
  respond(true, await (await ensureRuntime()).transcript(sessionId, sinceIndex === void 0 ? {} : { sinceIndex }));
2688
2484
  } catch (err) {
2689
- sendError(respond, err);
2485
+ sendGoogleMeetGatewayError(respond, err);
2690
2486
  }
2691
2487
  });
2692
2488
  api.registerGatewayMethod("googlemeet.recoverCurrentTab", async ({ params, respond }) => {
@@ -2696,7 +2492,7 @@ var google_meet_default = definePluginEntry({
2696
2492
  transport: normalizeTransport(params?.transport)
2697
2493
  }));
2698
2494
  } catch (err) {
2699
- sendError(respond, err);
2495
+ sendGoogleMeetGatewayError(respond, err);
2700
2496
  }
2701
2497
  });
2702
2498
  api.registerGatewayMethod("googlemeet.setup", async ({ params, respond }) => {
@@ -2707,7 +2503,7 @@ var google_meet_default = definePluginEntry({
2707
2503
  dialInNumber: normalizeOptionalString(params?.dialInNumber)
2708
2504
  }));
2709
2505
  } catch (err) {
2710
- sendError(respond, err);
2506
+ sendGoogleMeetGatewayError(respond, err);
2711
2507
  }
2712
2508
  });
2713
2509
  api.registerGatewayMethod("googlemeet.latest", async ({ params, respond }) => {
@@ -2727,7 +2523,7 @@ var google_meet_default = definePluginEntry({
2727
2523
  ...resolved.calendarEvent ? { calendarEvent: resolved.calendarEvent } : {}
2728
2524
  });
2729
2525
  } catch (err) {
2730
- sendError(respond, err);
2526
+ sendGoogleMeetGatewayError(respond, err);
2731
2527
  }
2732
2528
  });
2733
2529
  api.registerGatewayMethod("googlemeet.calendarEvents", async ({ params, respond }) => {
@@ -2742,7 +2538,7 @@ var google_meet_default = definePluginEntry({
2742
2538
  ...window
2743
2539
  }));
2744
2540
  } catch (err) {
2745
- sendError(respond, err);
2541
+ sendGoogleMeetGatewayError(respond, err);
2746
2542
  }
2747
2543
  });
2748
2544
  api.registerGatewayMethod("googlemeet.artifacts", async ({ params, respond }) => {
@@ -2759,7 +2555,7 @@ var google_meet_default = definePluginEntry({
2759
2555
  allConferenceRecords: resolved.allConferenceRecords
2760
2556
  }));
2761
2557
  } catch (err) {
2762
- sendError(respond, err);
2558
+ sendGoogleMeetGatewayError(respond, err);
2763
2559
  }
2764
2560
  });
2765
2561
  api.registerGatewayMethod("googlemeet.attendance", async ({ params, respond }) => {
@@ -2777,26 +2573,26 @@ var google_meet_default = definePluginEntry({
2777
2573
  earlyBeforeMinutes: resolved.earlyBeforeMinutes
2778
2574
  }));
2779
2575
  } catch (err) {
2780
- sendError(respond, err);
2576
+ sendGoogleMeetGatewayError(respond, err);
2781
2577
  }
2782
2578
  });
2783
2579
  api.registerGatewayMethod("googlemeet.export", async ({ params, respond }) => {
2784
2580
  try {
2785
2581
  respond(true, await exportGoogleMeetBundleFromParams(config, asParamRecord(params)));
2786
2582
  } catch (err) {
2787
- sendError(respond, err);
2583
+ sendGoogleMeetGatewayError(respond, err);
2788
2584
  }
2789
2585
  });
2790
2586
  api.registerGatewayMethod("googlemeet.leave", async ({ params, respond }) => {
2791
2587
  try {
2792
2588
  const sessionId = normalizeOptionalString(params?.sessionId);
2793
2589
  if (!sessionId) {
2794
- sendError(respond, /* @__PURE__ */ new Error("sessionId required"), ErrorCodes.INVALID_REQUEST);
2590
+ sendGoogleMeetGatewayError(respond, /* @__PURE__ */ new Error("sessionId required"), ErrorCodes.INVALID_REQUEST);
2795
2591
  return;
2796
2592
  }
2797
2593
  respond(true, await (await ensureRuntime()).leave(sessionId));
2798
2594
  } catch (err) {
2799
- sendError(respond, err);
2595
+ sendGoogleMeetGatewayError(respond, err);
2800
2596
  }
2801
2597
  });
2802
2598
  api.registerGatewayMethod("googlemeet.endActiveConference", async ({ params, respond }) => {
@@ -2807,19 +2603,19 @@ var google_meet_default = definePluginEntry({
2807
2603
  meeting: resolveMeetingInput(config, raw.meeting)
2808
2604
  }));
2809
2605
  } catch (err) {
2810
- sendError(respond, err);
2606
+ sendGoogleMeetGatewayError(respond, err);
2811
2607
  }
2812
2608
  });
2813
2609
  api.registerGatewayMethod("googlemeet.speak", async ({ params, respond }) => {
2814
2610
  try {
2815
2611
  const sessionId = normalizeOptionalString(params?.sessionId);
2816
2612
  if (!sessionId) {
2817
- sendError(respond, /* @__PURE__ */ new Error("sessionId required"), ErrorCodes.INVALID_REQUEST);
2613
+ sendGoogleMeetGatewayError(respond, /* @__PURE__ */ new Error("sessionId required"), ErrorCodes.INVALID_REQUEST);
2818
2614
  return;
2819
2615
  }
2820
2616
  respond(true, await (await ensureRuntime()).speak(sessionId, normalizeOptionalString(params?.message)));
2821
2617
  } catch (err) {
2822
- sendError(respond, err);
2618
+ sendGoogleMeetGatewayError(respond, err);
2823
2619
  }
2824
2620
  });
2825
2621
  api.registerGatewayMethod("googlemeet.testSpeech", async ({ params, client, respond }) => {
@@ -2837,7 +2633,7 @@ var google_meet_default = definePluginEntry({
2837
2633
  agentId: normalizeOptionalString(trustedParams.agentId)
2838
2634
  }));
2839
2635
  } catch (err) {
2840
- sendError(respond, err);
2636
+ sendGoogleMeetGatewayError(respond, err);
2841
2637
  }
2842
2638
  });
2843
2639
  api.registerGatewayMethod("googlemeet.testListen", async ({ params, client, respond }) => {
@@ -2851,7 +2647,7 @@ var google_meet_default = definePluginEntry({
2851
2647
  timeoutMs: readPositiveIntegerParam(trustedParams, "timeoutMs")
2852
2648
  }));
2853
2649
  } catch (err) {
2854
- sendError(respond, err);
2650
+ sendGoogleMeetGatewayError(respond, err);
2855
2651
  }
2856
2652
  });
2857
2653
  api.registerTool((toolContext) => ({
@@ -3008,7 +2804,7 @@ var google_meet_default = definePluginEntry({
3008
2804
  default: throw new Error("unknown google_meet action");
3009
2805
  }
3010
2806
  } catch (err) {
3011
- return jsonResult(formatGatewayError(err));
2807
+ return jsonResult(formatGoogleMeetGatewayError(err));
3012
2808
  }
3013
2809
  }
3014
2810
  }), { name: "google_meet" });
@@ -3028,11 +2824,7 @@ var google_meet_default = definePluginEntry({
3028
2824
  });
3029
2825
  }, {
3030
2826
  commands: ["googlemeet"],
3031
- descriptors: [{
3032
- name: "googlemeet",
3033
- description: "Join and manage Google Meet calls",
3034
- hasSubcommands: true
3035
- }]
2827
+ descriptors: [GOOGLE_MEET_CLI_DESCRIPTOR]
3036
2828
  });
3037
2829
  }
3038
2830
  });