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

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 { _ 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-DkWXFsK2.js";
3
3
  import { optionalPositiveIntegerSchema, readPositiveIntegerParam } from "openclaw/plugin-sdk/channel-actions";
4
- import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
5
4
  import { ErrorCodes, GatewayClient, callGatewayFromCli, errorShape, startGatewayClientWhenEventLoopReady } from "openclaw/plugin-sdk/gateway-runtime";
6
- import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
7
5
  import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
8
6
  import { normalizeAgentId, parseAgentSessionKey } from "openclaw/plugin-sdk/routing";
9
7
  import { asRecord, normalizeOptionalString, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
10
8
  import { jsonResult } from "openclaw/plugin-sdk/tool-results";
11
- import { Type } from "typebox";
9
+ import { createMeetingTranscriptSourceProvider, resolveTranscriptsConfig } from "openclaw/plugin-sdk/transcripts";
12
10
  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
11
  import { spawnSync } from "node:child_process";
12
+ import { 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";
13
+ import { addTimerTimeoutGraceMs } from "openclaw/plugin-sdk/number-runtime";
14
+ import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
16
15
  import { sleep } from "openclaw/plugin-sdk/runtime-env";
17
- import { randomUUID } from "node:crypto";
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) {
@@ -518,7 +519,7 @@ function prefixDtmfWait(sequence, delayMs) {
518
519
  }
519
520
  //#endregion
520
521
  //#region extensions/google-meet/src/transports/google-meet-platform-adapter.ts
521
- function isGoogleMeetTalkBackMode$2(mode) {
522
+ function isGoogleMeetTalkBackMode$1(mode) {
522
523
  return mode === "agent" || mode === "bidi";
523
524
  }
524
525
  function parseMeetBrowserStatus(result) {
@@ -622,6 +623,24 @@ const GOOGLE_MEET_PLATFORM_ADAPTER = {
622
623
  displayName: "Google Meet",
623
624
  browserLabel: "Meet",
624
625
  logScope: "[google-meet]",
626
+ agentConsult: {
627
+ surface: "a private Google Meet",
628
+ userLabel: "Participant",
629
+ assistantLabel: "Agent",
630
+ questionSourceLabel: "participant",
631
+ workingResponseLabel: "participant",
632
+ extraSystemPrompt: [
633
+ "You are a behind-the-scenes consultant for a live meeting voice agent.",
634
+ "Prioritize a fast, speakable answer over exhaustive investigation.",
635
+ "For tool-backed status checks, prefer one or two bounded read-only queries before answering.",
636
+ "Do not print secret values or dump environment variables; only check whether required configuration is present.",
637
+ "Be accurate, brief, and speakable."
638
+ ].join(" ")
639
+ },
640
+ session: {
641
+ idPrefix: "meet",
642
+ participantIdentity: (transport) => transport === "twilio" ? "Twilio phone participant" : transport === "chrome-node" ? "signed-in Google Chrome profile on a paired node" : "signed-in Google Chrome profile"
643
+ },
625
644
  nodeCommandName: GOOGLE_MEET_NODE_COMMAND,
626
645
  nodeConfigPath: "plugins.entries.google-meet.config.chromeNode.node",
627
646
  urls: {
@@ -642,9 +661,9 @@ const GOOGLE_MEET_PLATFORM_ADAPTER = {
642
661
  }
643
662
  },
644
663
  browser: {
645
- allowsMicrophone: isGoogleMeetTalkBackMode$2,
664
+ allowsMicrophone: isGoogleMeetTalkBackMode$1,
646
665
  buildStatusJoinScript: (params) => meetStatusScript({
647
- allowMicrophone: isGoogleMeetTalkBackMode$2(params.mode),
666
+ allowMicrophone: isGoogleMeetTalkBackMode$1(params.mode),
648
667
  autoJoin: params.autoJoin,
649
668
  captionSessionId: params.meetingSessionId || void 0,
650
669
  captureCaptions: params.captureCaptions,
@@ -661,7 +680,7 @@ const GOOGLE_MEET_PLATFORM_ADAPTER = {
661
680
  buildLeaveScript: meetLeaveScript,
662
681
  parseLeaveResult: parseMeetLeaveResult,
663
682
  captions: {
664
- enabled: (mode) => mode === "transcribe",
683
+ enabled: (_mode) => true,
665
684
  buildTranscriptScript: ({ finalize, meetingSessionId, meetingUrl }) => meetTranscriptScript(meetingUrl, meetingSessionId, finalize),
666
685
  parseTranscript: parseMeetTranscriptSnapshot
667
686
  },
@@ -781,25 +800,32 @@ async function testGoogleMeetSpeech(context, request) {
781
800
  const beforeSessions = context.list();
782
801
  const before = new Set(beforeSessions.map((session) => session.id));
783
802
  const existing = beforeSessions.find((session) => context.isReusable(session, resolved));
784
- const existingOutputBytes = existing?.chrome?.health?.lastOutputBytes ?? 0;
803
+ const existingBaseline = {
804
+ outputBytes: existing?.chrome?.health?.lastOutputBytes ?? 0,
805
+ outputGeneration: existing?.chrome?.health?.outputGeneration ?? 0
806
+ };
785
807
  const result = await context.join({
786
808
  ...request,
787
809
  ...resolved,
788
810
  message: request.message ?? "Say exactly: Google Meet speech test complete."
789
811
  });
790
- const startOutputBytes = existing?.id === result.session.id ? existingOutputBytes : 0;
812
+ const baseline = existing?.id === result.session.id ? existingBaseline : {
813
+ outputBytes: 0,
814
+ outputGeneration: 0
815
+ };
791
816
  let health = result.session.chrome?.health;
817
+ const verified = () => (health?.lastOutputBytes ?? 0) > baseline.outputBytes && (health?.outputGeneration ?? 0) > baseline.outputGeneration && health?.verifiedOutputGeneration === health?.outputGeneration;
792
818
  const shouldWait = result.spoken === true && health?.manualActionRequired !== true && context.hasHealthHandle(result.session.id);
793
- if (shouldWait && (health?.lastOutputBytes ?? 0) <= startOutputBytes) {
819
+ if (shouldWait && !verified()) {
794
820
  const deadline = Date.now() + Math.min(context.config.chrome.joinTimeoutMs, 5e3);
795
821
  while (Date.now() < deadline) {
796
822
  await sleep(100);
797
823
  context.refreshHealth(result.session.id);
798
824
  health = result.session.chrome?.health;
799
- if ((health?.lastOutputBytes ?? 0) > startOutputBytes) break;
825
+ if (verified()) break;
800
826
  }
801
827
  }
802
- const speechOutputVerified = (health?.lastOutputBytes ?? 0) > startOutputBytes;
828
+ const speechOutputVerified = verified();
803
829
  return {
804
830
  createdSession: !before.has(result.session.id),
805
831
  inCall: health?.inCall,
@@ -814,6 +840,13 @@ async function testGoogleMeetSpeech(context, request) {
814
840
  speechBlockedMessage: health?.speechBlockedMessage,
815
841
  audioOutputActive: health?.audioOutputActive,
816
842
  lastOutputBytes: health?.lastOutputBytes,
843
+ outputLoopbackSignalBytes: health?.outputLoopbackSignalBytes,
844
+ lastOutputLoopbackAt: health?.lastOutputLoopbackAt,
845
+ lastOutputLoopbackCorrelation: health?.lastOutputLoopbackCorrelation,
846
+ lastOutputLoopbackRms: health?.lastOutputLoopbackRms,
847
+ lastOutputLoopbackPeak: health?.lastOutputLoopbackPeak,
848
+ outputGeneration: health?.outputGeneration,
849
+ verifiedOutputGeneration: health?.verifiedOutputGeneration,
817
850
  session: result.session
818
851
  };
819
852
  }
@@ -873,26 +906,27 @@ async function testGoogleMeetListening(context, request) {
873
906
  }
874
907
  //#endregion
875
908
  //#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",
909
+ function resolveTransport(input, config) {
910
+ return input ?? config.defaultTransport;
911
+ }
912
+ function resolveMode(input, config) {
913
+ return input === "realtime" ? "agent" : input ?? config.defaultMode;
914
+ }
915
+ function withSessionAgentConfig(config, agentId) {
916
+ return config.realtime.agentId === agentId ? config : {
917
+ ...config,
885
918
  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: []
919
+ ...config.realtime,
920
+ agentId
921
+ }
894
922
  };
895
923
  }
924
+ function isBrowserTransport(transport) {
925
+ return transport === "chrome" || transport === "chrome-node";
926
+ }
927
+ function noteSession(session, note) {
928
+ session.notes = [...session.notes.filter((item) => item !== note), note];
929
+ }
896
930
  //#endregion
897
931
  //#region extensions/google-meet/src/setup.ts
898
932
  function resolveUserPath(input) {
@@ -1035,84 +1069,9 @@ function addGoogleMeetSetupCheck(status, check) {
1035
1069
  return addMeetingSetupCheck(status, check);
1036
1070
  }
1037
1071
  //#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
1072
  //#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
- };
1073
+ function shouldCaptureCaptions(mode, fullConfig) {
1074
+ return mode === "transcribe" || !fullConfig || resolveTranscriptsConfig(fullConfig.transcripts).enabled;
1116
1075
  }
1117
1076
  async function assertBlackHole2chAvailable(params) {
1118
1077
  if (process.platform !== "darwin") throw new Error("Chrome Meet transport with blackhole-2ch audio is currently macOS-only");
@@ -1132,7 +1091,7 @@ async function assertBlackHole2chAvailable(params) {
1132
1091
  }
1133
1092
  async function launchChromeMeet(params) {
1134
1093
  const checkRealtimeAudioPrerequisites = async () => {
1135
- if (!isGoogleMeetTalkBackMode$2(params.mode)) return;
1094
+ if (!isGoogleMeetTalkBackMode$1(params.mode)) return;
1136
1095
  await assertBlackHole2chAvailable({
1137
1096
  runtime: params.runtime,
1138
1097
  timeoutMs: Math.min(params.config.chrome.joinTimeoutMs, 1e4)
@@ -1143,7 +1102,7 @@ async function launchChromeMeet(params) {
1143
1102
  }
1144
1103
  };
1145
1104
  const startRealtimeAudioBridge = async () => {
1146
- if (!isGoogleMeetTalkBackMode$2(params.mode)) return;
1105
+ if (!isGoogleMeetTalkBackMode$1(params.mode)) return;
1147
1106
  if (params.config.chrome.audioBridgeCommand) {
1148
1107
  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
1108
  const bridge = await params.runtime.system.runCommandWithTimeout(params.config.chrome.audioBridgeCommand, { timeoutMs: params.config.chrome.joinTimeoutMs });
@@ -1154,14 +1113,18 @@ async function launchChromeMeet(params) {
1154
1113
  const transport = createLocalMeetingRealtimeAudioTransport({
1155
1114
  inputCommand: params.config.chrome.audioInputCommand,
1156
1115
  outputCommand: params.config.chrome.audioOutputCommand,
1116
+ audioFormat: params.config.chrome.audioFormat,
1157
1117
  bargeInInputCommand: params.config.chrome.bargeInInputCommand,
1158
1118
  bargeInRmsThreshold: params.config.chrome.bargeInRmsThreshold,
1159
1119
  bargeInPeakThreshold: params.config.chrome.bargeInPeakThreshold,
1160
1120
  bargeInCooldownMs: params.config.chrome.bargeInCooldownMs,
1161
1121
  logger: params.logger,
1162
- logScope: GOOGLE_MEET_RUNTIME_PLATFORM.logScope
1122
+ logScope: GOOGLE_MEET_PLATFORM_ADAPTER.logScope
1123
+ });
1124
+ const bindings = createMeetingRealtimeEngineBindings({
1125
+ platform: GOOGLE_MEET_PLATFORM_ADAPTER,
1126
+ ...params
1163
1127
  });
1164
- const bindings = createGoogleMeetRealtimeEngineBindings(params);
1165
1128
  const engine = params.mode === "agent" ? await startMeetingAgentRealtimeEngine({
1166
1129
  config: params.config,
1167
1130
  fullConfig: params.fullConfig,
@@ -1205,12 +1168,13 @@ async function launchChromeMeet(params) {
1205
1168
  callBrowser: await resolveLocalMeetingBrowserRequest(params.runtime),
1206
1169
  config: params.config.chrome,
1207
1170
  session: {
1171
+ captureCaptions: shouldCaptureCaptions(params.mode, params.fullConfig),
1208
1172
  meetingSessionId: params.meetingSessionId,
1209
1173
  mode: params.mode,
1210
1174
  url: params.url
1211
1175
  }
1212
1176
  });
1213
- const audioBridge = isGoogleMeetTalkBackMode$2(params.mode) && result.browser?.inCall === true && result.browser.micMuted === false && result.browser.manualActionRequired !== true ? await startRealtimeAudioBridge() : void 0;
1177
+ const audioBridge = isGoogleMeetTalkBackMode$1(params.mode) && result.browser?.inCall === true && result.browser.micMuted === false && result.browser.manualActionRequired !== true ? await startRealtimeAudioBridge() : void 0;
1214
1178
  return {
1215
1179
  ...result,
1216
1180
  audioBridge
@@ -1226,6 +1190,7 @@ async function leaveChromeMeet(params) {
1226
1190
  adapter: GOOGLE_MEET_PLATFORM_ADAPTER,
1227
1191
  callBrowser: await resolveLocalMeetingBrowserRequest(params.runtime),
1228
1192
  launch: params.config.chrome.launch,
1193
+ meetingSessionId: params.meetingSessionId,
1229
1194
  meetingUrl: params.meetingUrl,
1230
1195
  tab: params.tab,
1231
1196
  timeoutMs: params.config.chrome.joinTimeoutMs
@@ -1281,6 +1246,7 @@ async function leaveChromeMeetOnNode(params) {
1281
1246
  timeoutMs: request.timeoutMs
1282
1247
  }),
1283
1248
  launch: params.config.chrome.launch,
1249
+ meetingSessionId: params.meetingSessionId,
1284
1250
  meetingUrl: params.meetingUrl,
1285
1251
  tab: params.tab,
1286
1252
  timeoutMs: params.config.chrome.joinTimeoutMs
@@ -1299,6 +1265,7 @@ async function openMeetWithBrowserProxy(params) {
1299
1265
  }),
1300
1266
  config: params.config.chrome,
1301
1267
  session: {
1268
+ captureCaptions: params.captureCaptions,
1302
1269
  mode: params.mode,
1303
1270
  meetingSessionId: params.meetingSessionId,
1304
1271
  url: params.url
@@ -1311,6 +1278,7 @@ async function recoverCurrentMeetTab(params) {
1311
1278
  ...await recoverMeetingBrowserTab({
1312
1279
  adapter: GOOGLE_MEET_PLATFORM_ADAPTER,
1313
1280
  callBrowser: await resolveLocalMeetingBrowserRequest(params.runtime),
1281
+ captureCaptions: shouldCaptureCaptions(params.mode ?? "bidi", params.fullConfig),
1314
1282
  config: params.config.chrome,
1315
1283
  locationLabel: "in local Chrome",
1316
1284
  mode: params.mode ?? "bidi",
@@ -1339,6 +1307,7 @@ async function recoverCurrentMeetTabOnNode(params) {
1339
1307
  body: request.body,
1340
1308
  timeoutMs: request.timeoutMs
1341
1309
  }),
1310
+ captureCaptions: shouldCaptureCaptions(params.mode ?? "bidi", params.fullConfig),
1342
1311
  config: params.config.chrome,
1343
1312
  locationLabel: "on the selected Chrome node",
1344
1313
  mode: params.mode ?? "bidi",
@@ -1372,11 +1341,12 @@ async function launchChromeMeetOnNode(params) {
1372
1341
  runtime: params.runtime,
1373
1342
  nodeId,
1374
1343
  config: params.config,
1344
+ captureCaptions: shouldCaptureCaptions(params.mode, params.fullConfig),
1375
1345
  mode: params.mode,
1376
1346
  meetingSessionId: params.meetingSessionId,
1377
1347
  url: params.url
1378
1348
  });
1379
- if (params.config.chrome.launch && isGoogleMeetTalkBackMode$2(params.mode) && (browserControl.browser?.inCall !== true || browserControl.browser.micMuted !== false || browserControl.browser.manualActionRequired === true)) return {
1349
+ if (params.config.chrome.launch && isGoogleMeetTalkBackMode$1(params.mode) && (browserControl.browser?.inCall !== true || browserControl.browser.micMuted !== false || browserControl.browser.manualActionRequired === true)) return {
1380
1350
  nodeId,
1381
1351
  launched: browserControl.launched,
1382
1352
  browser: browserControl.browser,
@@ -1405,12 +1375,16 @@ async function launchChromeMeetOnNode(params) {
1405
1375
  runtime: params.runtime,
1406
1376
  nodeId,
1407
1377
  bridgeId: result.bridgeId,
1378
+ audioFormat: params.config.chrome.audioFormat,
1408
1379
  logger: params.logger,
1409
1380
  commandName: GOOGLE_MEET_NODE_COMMAND,
1410
- logScope: GOOGLE_MEET_RUNTIME_PLATFORM.logScope,
1381
+ logScope: GOOGLE_MEET_PLATFORM_ADAPTER.logScope,
1411
1382
  logPrefix: params.mode === "agent" ? "node agent" : "node"
1412
1383
  });
1413
- const bindings = createGoogleMeetRealtimeEngineBindings(params);
1384
+ const bindings = createMeetingRealtimeEngineBindings({
1385
+ platform: GOOGLE_MEET_PLATFORM_ADAPTER,
1386
+ ...params
1387
+ });
1414
1388
  const engine = params.mode === "agent" ? await startMeetingAgentRealtimeEngine({
1415
1389
  config: params.config,
1416
1390
  fullConfig: params.fullConfig,
@@ -1627,45 +1601,18 @@ async function speakMeetViaVoiceCallGateway(params) {
1627
1601
  }
1628
1602
  //#endregion
1629
1603
  //#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
- }
1604
+ const nowIso = () => (/* @__PURE__ */ new Date()).toISOString();
1663
1605
  var GoogleMeetRuntime = class {
1664
- #createdBrowserTabs = /* @__PURE__ */ new Map();
1606
+ #createdBrowserTabs;
1607
+ #agentId;
1665
1608
  #voiceCallGateway;
1666
1609
  #sessions;
1667
1610
  constructor(params) {
1668
1611
  this.params = params;
1612
+ this.#createdBrowserTabs = /* @__PURE__ */ new Map();
1613
+ this.transcriptSourceRuntime = () => this.#sessions;
1614
+ const adapter = GOOGLE_MEET_PLATFORM_ADAPTER;
1615
+ this.#agentId = resolveDefaultAgentId(params.fullConfig);
1669
1616
  this.#voiceCallGateway = createVoiceCallGateway(params);
1670
1617
  this.#sessions = new MeetingSessionRuntime({
1671
1618
  logger: params.logger,
@@ -1699,12 +1646,13 @@ var GoogleMeetRuntime = class {
1699
1646
  }
1700
1647
  },
1701
1648
  resolveJoin: (request) => ({
1702
- url: GOOGLE_MEET_PLATFORM_ADAPTER.urls.validateAndNormalize(request.url),
1649
+ url: adapter.urls.validateAndNormalize(request.url),
1703
1650
  transport: resolveTransport(request.transport, params.config),
1704
1651
  mode: resolveMode(request.mode, params.config),
1705
- agentId: resolveSessionAgentId(request, params.config)
1652
+ agentId: normalizeAgentId(request.agentId ?? params.config.realtime.agentId ?? this.#agentId)
1706
1653
  }),
1707
- createSession: ({ request: _request, resolved, createdAt }) => createGoogleMeetSession({
1654
+ createSession: ({ resolved, createdAt }) => createMeetingSession({
1655
+ platform: adapter,
1708
1656
  config: params.config,
1709
1657
  resolved,
1710
1658
  createdAt
@@ -1713,8 +1661,8 @@ var GoogleMeetRuntime = class {
1713
1661
  isBrowserTransport,
1714
1662
  isTalkBackMode: isGoogleMeetTalkBackMode$1,
1715
1663
  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),
1664
+ sameMeetingUrl: (left, right) => adapter.urls.isSameMeeting(left, right),
1665
+ normalizeMeetingUrlForReuse: (url) => adapter.urls.normalizeForReuse(url),
1718
1666
  getBrowser: (session) => session.chrome ? {
1719
1667
  launched: session.chrome.launched,
1720
1668
  nodeId: session.chrome.nodeId,
@@ -1732,12 +1680,17 @@ var GoogleMeetRuntime = class {
1732
1680
  releaseBrowserTab: async (session) => await this.#releaseBrowserTab(session),
1733
1681
  refreshBrowserHealth: async (session, options) => await this.#refreshBrowserHealth(session, options),
1734
1682
  refreshStatus: async (session) => await this.#refreshStatus(session),
1735
- refreshReusableSession: async (session) => {
1683
+ refreshReusableSession: async (session, _request, _resolved) => {
1736
1684
  if (session.transport === "twilio") await this.#refreshTwilioVoiceCallStatus(session);
1737
1685
  },
1738
1686
  ensureRealtimeBridge: async (session) => await this.#ensureChromeRealtimeBridge(session),
1739
1687
  captureTranscript: async (session, options) => await this.#captureTranscript(session, options),
1740
- speakViaTransport: async (session, instructions) => await this.#speakViaTransport(session, instructions)
1688
+ speakViaTransport: async (session, instructions) => await this.#speakViaTransport(session, instructions),
1689
+ durableTranscripts: {
1690
+ config: params.fullConfig.transcripts,
1691
+ providerId: "google-meet",
1692
+ providerName: "Google Meet"
1693
+ }
1741
1694
  });
1742
1695
  }
1743
1696
  list() {
@@ -1772,10 +1725,12 @@ var GoogleMeetRuntime = class {
1772
1725
  return transport === "chrome-node" ? await recoverCurrentMeetTabOnNode({
1773
1726
  runtime: this.params.runtime,
1774
1727
  config: this.params.config,
1728
+ fullConfig: this.params.fullConfig,
1775
1729
  url
1776
1730
  }) : await recoverCurrentMeetTab({
1777
1731
  runtime: this.params.runtime,
1778
1732
  config: this.params.config,
1733
+ fullConfig: this.params.fullConfig,
1779
1734
  url
1780
1735
  });
1781
1736
  }
@@ -1797,7 +1752,7 @@ var GoogleMeetRuntime = class {
1797
1752
  #probeContext() {
1798
1753
  return {
1799
1754
  config: this.params.config,
1800
- resolveAgentId: (request) => resolveSessionAgentId(request, this.params.config),
1755
+ resolveAgentId: (request) => normalizeAgentId(request.agentId ?? this.params.config.realtime.agentId ?? this.#agentId),
1801
1756
  list: () => this.list(),
1802
1757
  join: async (request) => await this.join(request),
1803
1758
  isReusable: (session, resolved) => this.#sessions.isReusableSession(session, resolved),
@@ -1879,7 +1834,7 @@ var GoogleMeetRuntime = class {
1879
1834
  logger: this.params.logger,
1880
1835
  ...request.requesterSessionKey ? { requesterSessionKey: request.requesterSessionKey } : {},
1881
1836
  agentId: delegatedAgentId,
1882
- sessionKey: delegatedAgentId ? `agent:${delegatedAgentId}:google-meet:${session.id}` : buildTwilioVoiceCallSessionKey(session.id),
1837
+ sessionKey: delegatedAgentId ? `agent:${delegatedAgentId}:google-meet:${session.id}` : `voice:google-meet:${session.id}`,
1883
1838
  message: isGoogleMeetTalkBackMode$1(session.mode) ? request.message ?? this.params.config.voiceCall.introMessage ?? this.params.config.realtime.introMessage : void 0
1884
1839
  }) : void 0;
1885
1840
  session.twilio = {
@@ -1950,6 +1905,7 @@ var GoogleMeetRuntime = class {
1950
1905
  const result = session.transport === "chrome-node" ? await recoverCurrentMeetTabOnNode({
1951
1906
  runtime: this.params.runtime,
1952
1907
  config: this.params.config,
1908
+ fullConfig: this.params.fullConfig,
1953
1909
  mode: session.mode,
1954
1910
  readOnly: options.readOnly,
1955
1911
  trackedMeetingUrl: session.url,
@@ -1958,6 +1914,7 @@ var GoogleMeetRuntime = class {
1958
1914
  }) : await recoverCurrentMeetTab({
1959
1915
  runtime: this.params.runtime,
1960
1916
  config: this.params.config,
1917
+ fullConfig: this.params.fullConfig,
1961
1918
  mode: session.mode,
1962
1919
  readOnly: options.readOnly,
1963
1920
  trackedMeetingUrl: session.url,
@@ -2068,11 +2025,13 @@ var GoogleMeetRuntime = class {
2068
2025
  runtime: this.params.runtime,
2069
2026
  nodeId: session.chrome?.nodeId,
2070
2027
  config: this.params.config,
2028
+ meetingSessionId: session.id,
2071
2029
  meetingUrl: session.url,
2072
2030
  tab
2073
2031
  }) : await leaveChromeMeet({
2074
2032
  runtime: this.params.runtime,
2075
2033
  config: this.params.config,
2034
+ meetingSessionId: session.id,
2076
2035
  meetingUrl: session.url,
2077
2036
  tab
2078
2037
  });
@@ -2082,329 +2041,54 @@ var GoogleMeetRuntime = class {
2082
2041
  noteSession(session, `Browser control could not leave the Meet tab: ${formatErrorMessage(error)}`);
2083
2042
  left = false;
2084
2043
  }
2085
- if (session.chrome && left) session.chrome.browserTab = void 0;
2044
+ if (session.chrome && left) {
2045
+ session.chrome.browserTab = void 0;
2046
+ if (session.chrome.health) session.chrome.health = {
2047
+ ...session.chrome.health,
2048
+ captioning: false,
2049
+ audioOutputRouted: false,
2050
+ providerConnected: false,
2051
+ realtimeReady: false,
2052
+ audioInputActive: false,
2053
+ audioOutputActive: false
2054
+ };
2055
+ }
2086
2056
  session.browserLeft = left;
2087
2057
  return left;
2088
2058
  }
2089
2059
  };
2090
2060
  //#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);
2097
- },
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
- },
2186
- "chrome.audioBridgeHealthCommand": {
2187
- label: "Audio Bridge Health Command",
2188
- advanced: true
2189
- },
2190
- "chromeNode.node": {
2191
- label: "Chrome Node",
2192
- help: "Node id/name/IP that owns Chrome, BlackHole, and SoX for chrome-node transport.",
2193
- advanced: true
2194
- },
2195
- "twilio.defaultDialInNumber": {
2196
- label: "Default Dial-In Number",
2197
- placeholder: "+15551234567"
2198
- },
2199
- "twilio.defaultPin": {
2200
- label: "Default PIN",
2201
- advanced: true
2202
- },
2203
- "twilio.defaultDtmfSequence": {
2204
- label: "Default DTMF Sequence",
2205
- advanced: true
2206
- },
2207
- "voiceCall.enabled": { label: "Delegate To Voice Call" },
2208
- "voiceCall.gatewayUrl": {
2209
- label: "Voice Call Gateway URL",
2210
- advanced: true
2211
- },
2212
- "voiceCall.token": {
2213
- label: "Voice Call Gateway Token",
2214
- sensitive: true,
2215
- advanced: true
2216
- },
2217
- "voiceCall.requestTimeoutMs": {
2218
- label: "Voice Call Request Timeout (ms)",
2219
- advanced: true
2220
- },
2221
- "voiceCall.dtmfDelayMs": {
2222
- label: "DTMF Wait Before PIN (ms)",
2223
- help: "Leading Twilio wait time before playing a PIN-derived Meet DTMF sequence. Increase it if Meet asks for the PIN after DTMF was sent.",
2224
- advanced: true
2225
- },
2226
- "voiceCall.postDtmfSpeechDelayMs": {
2227
- label: "Post-DTMF Speech Delay (ms)",
2228
- help: "Delay before requesting the realtime intro greeting after Voice Call starts the Twilio leg.",
2229
- advanced: true
2230
- },
2231
- "voiceCall.introMessage": {
2232
- label: "Voice Call Intro Message",
2233
- advanced: true
2234
- },
2235
- "realtime.strategy": {
2236
- label: "Realtime Strategy",
2237
- help: "Legacy realtime alias setting. Use mode=agent or mode=bidi for new Meet joins."
2238
- },
2239
- "realtime.provider": {
2240
- label: "Speech Provider",
2241
- help: "Compatibility fallback for both realtime transcription and bidi voice. Prefer realtime.transcriptionProvider and realtime.voiceProvider for new configs."
2242
- },
2243
- "realtime.transcriptionProvider": {
2244
- label: "Realtime Transcription Provider",
2245
- help: "Agent mode uses this provider to transcribe meeting audio before regular OpenClaw TTS answers."
2246
- },
2247
- "realtime.voiceProvider": {
2248
- label: "Bidi Voice Provider",
2249
- help: "Bidi mode uses this realtime voice provider. Falls back to realtime.provider when unset."
2250
- },
2251
- "realtime.model": {
2252
- label: "Bidi Realtime Model",
2253
- help: "Only used by mode=bidi. Agent mode answers with the configured OpenClaw agent and regular TTS.",
2254
- advanced: true
2255
- },
2256
- "realtime.instructions": {
2257
- label: "Realtime Instructions",
2258
- advanced: true
2259
- },
2260
- "realtime.introMessage": {
2261
- label: "Realtime Intro Message",
2262
- help: "Spoken once when the realtime bridge is ready. Set to an empty string to join silently."
2263
- },
2264
- "realtime.agentId": {
2265
- label: "Realtime Consult Agent",
2266
- help: "OpenClaw agent id used by openclaw_agent_consult. Defaults to \"main\".",
2267
- advanced: true
2268
- },
2269
- "realtime.toolPolicy": {
2270
- label: "Realtime Tool Policy",
2271
- help: "Safe read-only tools are available by default; owner requests can unlock broader tools.",
2272
- advanced: true
2273
- },
2274
- "oauth.clientId": { label: "OAuth Client ID" },
2275
- "oauth.clientSecret": {
2276
- label: "OAuth Client Secret",
2277
- sensitive: true
2278
- },
2279
- "oauth.refreshToken": {
2280
- label: "OAuth Refresh Token",
2281
- sensitive: true
2282
- },
2283
- "oauth.accessToken": {
2284
- label: "Cached Access Token",
2285
- sensitive: true,
2286
- advanced: true
2287
- },
2288
- "oauth.expiresAt": {
2289
- label: "Cached Access Token Expiry",
2290
- help: "Unix epoch milliseconds used only for the cached access-token fast path.",
2291
- advanced: true
2292
- }
2293
- }
2294
- };
2295
- const GoogleMeetToolSchema = Type.Object({
2296
- action: Type.String({
2297
- enum: [
2298
- "join",
2299
- "create",
2300
- "status",
2301
- "transcript",
2302
- "setup_status",
2303
- "resolve_space",
2304
- "preflight",
2305
- "latest",
2306
- "calendar_events",
2307
- "artifacts",
2308
- "attendance",
2309
- "export",
2310
- "recover_current_tab",
2311
- "leave",
2312
- "end_active_conference",
2313
- "speak",
2314
- "test_speech",
2315
- "test_listen"
2316
- ],
2317
- description: "Google Meet action to run. create creates and joins by default; pass join=false to only mint a URL. After a timeout or unclear browser state, call recover_current_tab before retrying join."
2318
- }),
2319
- join: Type.Optional(Type.Boolean({ description: "For action=create, set false to create the URL without joining." })),
2320
- accessType: Type.Optional(Type.String({
2321
- enum: [
2322
- "OPEN",
2323
- "TRUSTED",
2324
- "RESTRICTED"
2325
- ],
2326
- description: "For action=create with Google Meet OAuth, configure who can join without knocking."
2327
- })),
2328
- entryPointAccess: Type.Optional(Type.String({
2329
- enum: ["ALL", "CREATOR_APP_ONLY"],
2330
- description: "For action=create with Google Meet OAuth, configure allowed join entry points."
2331
- })),
2332
- url: Type.Optional(Type.String({ description: "Explicit https://meet.google.com/... URL" })),
2333
- transport: Type.Optional(Type.String({
2334
- enum: [
2335
- "chrome",
2336
- "chrome-node",
2337
- "twilio"
2338
- ],
2339
- description: "Join transport"
2340
- })),
2341
- mode: Type.Optional(Type.String({
2342
- enum: [
2343
- "agent",
2344
- "bidi",
2345
- "transcribe"
2346
- ],
2347
- description: "Join mode. agent uses realtime transcription, the configured OpenClaw agent, and regular TTS. bidi uses the realtime voice model directly. transcribe joins observe-only."
2348
- })),
2349
- dialInNumber: Type.Optional(Type.String({ description: "Meet dial-in phone number for Twilio. Required for Twilio unless twilio.defaultDialInNumber is configured; Meet URLs cannot be dialed directly." })),
2350
- pin: Type.Optional(Type.String({ description: "Meet phone PIN for Twilio; # is appended if omitted" })),
2351
- dtmfSequence: Type.Optional(Type.String({ description: "Explicit DTMF sequence for Twilio" })),
2352
- sessionId: Type.Optional(Type.String({ description: "Meet session ID" })),
2353
- sinceIndex: Type.Optional(Type.Integer({
2354
- description: "For transcript, resume from the previous response's nextIndex.",
2355
- minimum: 0
2356
- })),
2357
- message: Type.Optional(Type.String({ description: "Realtime instructions to speak now" })),
2358
- timeoutMs: optionalPositiveIntegerSchema({ description: "Probe timeout in milliseconds" }),
2359
- meeting: Type.Optional(Type.String({ description: "Meet URL, meeting code, or spaces/{id}" })),
2360
- today: Type.Optional(Type.Boolean({ description: "For latest, artifacts, or attendance, find a Meet link on today's calendar." })),
2361
- event: Type.Optional(Type.String({ description: "For latest, artifacts, or attendance, find a matching Calendar event." })),
2362
- calendarId: Type.Optional(Type.String({ description: "Calendar id for today/event lookup" })),
2363
- conferenceRecord: Type.Optional(Type.String({ description: "Meet conferenceRecords/{id} resource name or id" })),
2364
- pageSize: optionalPositiveIntegerSchema({ description: "Meet API page size for list actions" }),
2365
- includeTranscriptEntries: Type.Optional(Type.Boolean({ description: "For artifacts, include structured transcript entries" })),
2366
- includeDocumentBodies: Type.Optional(Type.Boolean({ description: "For artifacts/export, export linked transcript and smart-note Google Docs text through Drive." })),
2367
- outputDir: Type.Optional(Type.String({ description: "For export, output directory" })),
2368
- zip: Type.Optional(Type.Boolean({ description: "For export, also write a .zip archive" })),
2369
- dryRun: Type.Optional(Type.Boolean({ description: "For export, return the manifest without writing files." })),
2370
- includeAllConferenceRecords: Type.Optional(Type.Boolean({ description: "For artifacts, attendance, or export with meeting input, fetch all conference records instead of only the latest." })),
2371
- mergeDuplicateParticipants: Type.Optional(Type.Boolean({ description: "For attendance, merge duplicate participant resources." })),
2372
- lateAfterMinutes: optionalPositiveIntegerSchema({ description: "For attendance, mark participants late after this many minutes." }),
2373
- earlyBeforeMinutes: optionalPositiveIntegerSchema({ description: "For attendance, mark early leavers before this many minutes." }),
2374
- accessToken: Type.Optional(Type.String({ description: "Access token override" })),
2375
- refreshToken: Type.Optional(Type.String({ description: "Refresh token override" })),
2376
- clientId: Type.Optional(Type.String({ description: "OAuth client id override" })),
2377
- clientSecret: Type.Optional(Type.String({ description: "OAuth client secret override" })),
2378
- expiresAt: Type.Optional(Type.Number({ description: "Cached access token expiry ms" }))
2379
- });
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;
2061
+ //#region extensions/google-meet/src/plugin-helpers.ts
2062
+ const loadGoogleMeetCreateModule = createLazyRuntimeModule(() => import("./create-CNAkrEju.js").then((n) => n.t));
2063
+ const loadGoogleMeetCliModule = createLazyRuntimeModule(() => import("./cli-CksOXL4n.js"));
2064
+ function asParamRecord(params) {
2065
+ return params && typeof params === "object" && !Array.isArray(params) ? params : {};
2066
+ }
2067
+ function normalizeTransport(value) {
2068
+ return value === "chrome" || value === "chrome-node" || value === "twilio" ? value : void 0;
2069
+ }
2070
+ function normalizeMode(value) {
2071
+ if (value === "realtime") return "agent";
2072
+ return value === "agent" || value === "bidi" || value === "transcribe" ? value : void 0;
2073
+ }
2074
+ function isGoogleMeetTalkBackMode(mode) {
2075
+ return mode === "agent" || mode === "bidi";
2076
+ }
2077
+ function resolveMeetingInput(config, value) {
2078
+ const meeting = normalizeOptionalString(value) ?? config.defaults.meeting;
2079
+ if (!meeting) throw new Error("Meeting input is required");
2080
+ return meeting;
2081
+ }
2082
+ function shouldJoinCreatedMeet(raw) {
2083
+ return raw.join !== false && raw.join !== "false";
2084
+ }
2085
+ const googleMeetToolDeps = {
2086
+ callGatewayFromCli,
2087
+ platform: () => process.platform
2088
+ };
2089
+ const testing = {
2090
+ setCallGatewayFromCliForTests(next) {
2091
+ googleMeetToolDeps.callGatewayFromCli = next ?? callGatewayFromCli;
2408
2092
  },
2409
2093
  setPlatformForTests(next) {
2410
2094
  googleMeetToolDeps.platform = next ?? (() => process.platform);
@@ -2607,6 +2291,316 @@ async function exportGoogleMeetBundleFromParams(config, raw) {
2607
2291
  tokenSource
2608
2292
  };
2609
2293
  }
2294
+ function createGoogleMeetRuntimeAccessor(params) {
2295
+ let runtime = null;
2296
+ return async () => {
2297
+ if (!params.config.enabled) throw new Error("Google Meet plugin disabled in plugin config");
2298
+ if (!runtime) runtime = new GoogleMeetRuntime({
2299
+ config: params.config,
2300
+ fullConfig: params.api.config,
2301
+ runtime: params.api.runtime,
2302
+ logger: params.api.logger
2303
+ });
2304
+ return runtime;
2305
+ };
2306
+ }
2307
+ function formatGoogleMeetGatewayError(err) {
2308
+ return isGoogleMeetBrowserManualActionError(err) ? err.payload : { error: formatErrorMessage(err) };
2309
+ }
2310
+ function sendGoogleMeetGatewayError(respond, err, code = ErrorCodes.UNAVAILABLE) {
2311
+ const payload = formatGoogleMeetGatewayError(err);
2312
+ respond(false, payload, errorShape(code, typeof payload.error === "string" ? payload.error : "Google Meet request failed", { details: payload }));
2313
+ }
2314
+ //#endregion
2315
+ //#region extensions/google-meet/src/plugin-schema.ts
2316
+ const googleMeetConfigSchema = {
2317
+ parse(value) {
2318
+ return resolveGoogleMeetConfig(value);
2319
+ },
2320
+ uiHints: {
2321
+ "defaults.meeting": {
2322
+ label: "Default Meeting",
2323
+ help: "Meet URL, meeting code, or spaces/{id} used when CLI commands omit a meeting."
2324
+ },
2325
+ "preview.enrollmentAcknowledged": {
2326
+ label: "Preview Acknowledged",
2327
+ help: "Confirms you understand the Google Meet Media API is still Developer Preview.",
2328
+ advanced: true
2329
+ },
2330
+ defaultTransport: {
2331
+ label: "Default Transport",
2332
+ help: "Chrome uses a signed-in browser profile. Chrome-node runs Chrome on a paired node. Twilio uses Meet dial-in numbers."
2333
+ },
2334
+ defaultMode: {
2335
+ label: "Default Mode",
2336
+ help: "Agent uses realtime transcription plus regular OpenClaw TTS. Bidi uses the realtime voice model directly. Transcribe observes only."
2337
+ },
2338
+ "chrome.audioBackend": {
2339
+ label: "Chrome Audio Backend",
2340
+ help: "BlackHole 2ch is required for local duplex audio routing."
2341
+ },
2342
+ "chrome.launch": { label: "Launch Chrome" },
2343
+ "chrome.browserProfile": {
2344
+ label: "Chrome Profile",
2345
+ advanced: true
2346
+ },
2347
+ "chrome.guestName": {
2348
+ label: "Guest Name",
2349
+ help: "Used when Chrome lands on the signed-out Meet guest-name screen."
2350
+ },
2351
+ "chrome.reuseExistingTab": {
2352
+ label: "Reuse Existing Meet Tab",
2353
+ help: "Avoids opening duplicate tabs for the same Meet URL."
2354
+ },
2355
+ "chrome.autoJoin": {
2356
+ label: "Auto Join Guest Screen",
2357
+ help: "Best-effort guest-name fill and Join Now click through OpenClaw browser automation."
2358
+ },
2359
+ "chrome.waitForInCallMs": {
2360
+ label: "Wait For In-Call (ms)",
2361
+ help: "Waits for Chrome to report that the Meet tab is in-call before the realtime intro speaks.",
2362
+ advanced: true
2363
+ },
2364
+ "chrome.audioFormat": {
2365
+ label: "Audio Format",
2366
+ 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.",
2367
+ advanced: true
2368
+ },
2369
+ "chrome.audioBufferBytes": {
2370
+ label: "Audio Buffer Bytes",
2371
+ help: "SoX processing buffer for generated Chrome command-pair audio commands. Lower values reduce latency but may underrun on busy hosts.",
2372
+ advanced: true
2373
+ },
2374
+ "chrome.audioInputCommand": {
2375
+ label: "Audio Input Command",
2376
+ help: "Command that writes meeting audio to stdout in chrome.audioFormat.",
2377
+ advanced: true
2378
+ },
2379
+ "chrome.audioOutputCommand": {
2380
+ label: "Audio Output Command",
2381
+ help: "Command that reads assistant audio from stdin in chrome.audioFormat.",
2382
+ advanced: true
2383
+ },
2384
+ "chrome.bargeInInputCommand": {
2385
+ label: "Barge-In Input Command",
2386
+ help: "Optional Gateway-hosted microphone command that writes signed 16-bit little-endian mono PCM for human interruption detection while assistant playback is active.",
2387
+ advanced: true
2388
+ },
2389
+ "chrome.bargeInRmsThreshold": {
2390
+ label: "Barge-In RMS Threshold",
2391
+ help: "RMS level on chrome.bargeInInputCommand that counts as a human interruption.",
2392
+ advanced: true
2393
+ },
2394
+ "chrome.bargeInPeakThreshold": {
2395
+ label: "Barge-In Peak Threshold",
2396
+ help: "Peak level on chrome.bargeInInputCommand that counts as a human interruption.",
2397
+ advanced: true
2398
+ },
2399
+ "chrome.bargeInCooldownMs": {
2400
+ label: "Barge-In Cooldown (ms)",
2401
+ help: "Minimum delay between repeated barge-in clears.",
2402
+ advanced: true
2403
+ },
2404
+ "chrome.audioBridgeCommand": {
2405
+ label: "Audio Bridge Command",
2406
+ advanced: true
2407
+ },
2408
+ "chrome.audioBridgeHealthCommand": {
2409
+ label: "Audio Bridge Health Command",
2410
+ advanced: true
2411
+ },
2412
+ "chromeNode.node": {
2413
+ label: "Chrome Node",
2414
+ help: "Node id/name/IP that owns Chrome, BlackHole, and SoX for chrome-node transport.",
2415
+ advanced: true
2416
+ },
2417
+ "twilio.defaultDialInNumber": {
2418
+ label: "Default Dial-In Number",
2419
+ placeholder: "+15551234567"
2420
+ },
2421
+ "twilio.defaultPin": {
2422
+ label: "Default PIN",
2423
+ advanced: true
2424
+ },
2425
+ "twilio.defaultDtmfSequence": {
2426
+ label: "Default DTMF Sequence",
2427
+ advanced: true
2428
+ },
2429
+ "voiceCall.enabled": { label: "Delegate To Voice Call" },
2430
+ "voiceCall.gatewayUrl": {
2431
+ label: "Voice Call Gateway URL",
2432
+ advanced: true
2433
+ },
2434
+ "voiceCall.token": {
2435
+ label: "Voice Call Gateway Token",
2436
+ sensitive: true,
2437
+ advanced: true
2438
+ },
2439
+ "voiceCall.requestTimeoutMs": {
2440
+ label: "Voice Call Request Timeout (ms)",
2441
+ advanced: true
2442
+ },
2443
+ "voiceCall.dtmfDelayMs": {
2444
+ label: "DTMF Wait Before PIN (ms)",
2445
+ help: "Leading Twilio wait time before playing a PIN-derived Meet DTMF sequence. Increase it if Meet asks for the PIN after DTMF was sent.",
2446
+ advanced: true
2447
+ },
2448
+ "voiceCall.postDtmfSpeechDelayMs": {
2449
+ label: "Post-DTMF Speech Delay (ms)",
2450
+ help: "Delay before requesting the realtime intro greeting after Voice Call starts the Twilio leg.",
2451
+ advanced: true
2452
+ },
2453
+ "voiceCall.introMessage": {
2454
+ label: "Voice Call Intro Message",
2455
+ advanced: true
2456
+ },
2457
+ "realtime.strategy": {
2458
+ label: "Realtime Strategy",
2459
+ help: "Legacy realtime alias setting. Use mode=agent or mode=bidi for new Meet joins."
2460
+ },
2461
+ "realtime.provider": {
2462
+ label: "Speech Provider",
2463
+ help: "Compatibility fallback for both realtime transcription and bidi voice. Prefer realtime.transcriptionProvider and realtime.voiceProvider for new configs."
2464
+ },
2465
+ "realtime.transcriptionProvider": {
2466
+ label: "Realtime Transcription Provider",
2467
+ help: "Agent mode uses this provider to transcribe meeting audio before regular OpenClaw TTS answers."
2468
+ },
2469
+ "realtime.voiceProvider": {
2470
+ label: "Bidi Voice Provider",
2471
+ help: "Bidi mode uses this realtime voice provider. Falls back to realtime.provider when unset."
2472
+ },
2473
+ "realtime.model": {
2474
+ label: "Bidi Realtime Model",
2475
+ help: "Only used by mode=bidi. Agent mode answers with the configured OpenClaw agent and regular TTS.",
2476
+ advanced: true
2477
+ },
2478
+ "realtime.instructions": {
2479
+ label: "Realtime Instructions",
2480
+ advanced: true
2481
+ },
2482
+ "realtime.introMessage": {
2483
+ label: "Realtime Intro Message",
2484
+ help: "Spoken once when the realtime bridge is ready. Set to an empty string to join silently."
2485
+ },
2486
+ "realtime.agentId": {
2487
+ label: "Realtime Consult Agent",
2488
+ help: "OpenClaw agent id used by openclaw_agent_consult. Defaults to \"main\".",
2489
+ advanced: true
2490
+ },
2491
+ "realtime.toolPolicy": {
2492
+ label: "Realtime Tool Policy",
2493
+ help: "Safe read-only tools are available by default; owner requests can unlock broader tools.",
2494
+ advanced: true
2495
+ },
2496
+ "oauth.clientId": { label: "OAuth Client ID" },
2497
+ "oauth.clientSecret": {
2498
+ label: "OAuth Client Secret",
2499
+ sensitive: true
2500
+ },
2501
+ "oauth.refreshToken": {
2502
+ label: "OAuth Refresh Token",
2503
+ sensitive: true
2504
+ },
2505
+ "oauth.accessToken": {
2506
+ label: "Cached Access Token",
2507
+ sensitive: true,
2508
+ advanced: true
2509
+ },
2510
+ "oauth.expiresAt": {
2511
+ label: "Cached Access Token Expiry",
2512
+ help: "Unix epoch milliseconds used only for the cached access-token fast path.",
2513
+ advanced: true
2514
+ }
2515
+ }
2516
+ };
2517
+ const GoogleMeetToolSchema = Type.Object({
2518
+ action: Type.String({
2519
+ enum: [
2520
+ "join",
2521
+ "create",
2522
+ "status",
2523
+ "transcript",
2524
+ "setup_status",
2525
+ "resolve_space",
2526
+ "preflight",
2527
+ "latest",
2528
+ "calendar_events",
2529
+ "artifacts",
2530
+ "attendance",
2531
+ "export",
2532
+ "recover_current_tab",
2533
+ "leave",
2534
+ "end_active_conference",
2535
+ "speak",
2536
+ "test_speech",
2537
+ "test_listen"
2538
+ ],
2539
+ description: "Google Meet action to run. create creates and joins by default; pass join=false to only mint a URL. After a timeout or unclear browser state, call recover_current_tab before retrying join."
2540
+ }),
2541
+ join: Type.Optional(Type.Boolean({ description: "For action=create, set false to create the URL without joining." })),
2542
+ accessType: Type.Optional(Type.String({
2543
+ enum: [
2544
+ "OPEN",
2545
+ "TRUSTED",
2546
+ "RESTRICTED"
2547
+ ],
2548
+ description: "For action=create with Google Meet OAuth, configure who can join without knocking."
2549
+ })),
2550
+ entryPointAccess: Type.Optional(Type.String({
2551
+ enum: ["ALL", "CREATOR_APP_ONLY"],
2552
+ description: "For action=create with Google Meet OAuth, configure allowed join entry points."
2553
+ })),
2554
+ url: Type.Optional(Type.String({ description: "Explicit https://meet.google.com/... URL" })),
2555
+ transport: Type.Optional(Type.String({
2556
+ enum: [
2557
+ "chrome",
2558
+ "chrome-node",
2559
+ "twilio"
2560
+ ],
2561
+ description: "Join transport"
2562
+ })),
2563
+ mode: Type.Optional(Type.String({
2564
+ enum: [
2565
+ "agent",
2566
+ "bidi",
2567
+ "transcribe"
2568
+ ],
2569
+ description: "Join mode. agent uses realtime transcription, the configured OpenClaw agent, and regular TTS. bidi uses the realtime voice model directly. transcribe joins observe-only."
2570
+ })),
2571
+ dialInNumber: Type.Optional(Type.String({ description: "Meet dial-in phone number for Twilio. Required for Twilio unless twilio.defaultDialInNumber is configured; Meet URLs cannot be dialed directly." })),
2572
+ pin: Type.Optional(Type.String({ description: "Meet phone PIN for Twilio; # is appended if omitted" })),
2573
+ dtmfSequence: Type.Optional(Type.String({ description: "Explicit DTMF sequence for Twilio" })),
2574
+ sessionId: Type.Optional(Type.String({ description: "Meet session ID" })),
2575
+ sinceIndex: Type.Optional(Type.Integer({
2576
+ description: "For transcript, resume from the previous response's nextIndex.",
2577
+ minimum: 0
2578
+ })),
2579
+ message: Type.Optional(Type.String({ description: "Realtime instructions to speak now" })),
2580
+ timeoutMs: optionalPositiveIntegerSchema({ description: "Probe timeout in milliseconds" }),
2581
+ meeting: Type.Optional(Type.String({ description: "Meet URL, meeting code, or spaces/{id}" })),
2582
+ today: Type.Optional(Type.Boolean({ description: "For latest, artifacts, or attendance, find a Meet link on today's calendar." })),
2583
+ event: Type.Optional(Type.String({ description: "For latest, artifacts, or attendance, find a matching Calendar event." })),
2584
+ calendarId: Type.Optional(Type.String({ description: "Calendar id for today/event lookup" })),
2585
+ conferenceRecord: Type.Optional(Type.String({ description: "Meet conferenceRecords/{id} resource name or id" })),
2586
+ pageSize: optionalPositiveIntegerSchema({ description: "Meet API page size for list actions" }),
2587
+ includeTranscriptEntries: Type.Optional(Type.Boolean({ description: "For artifacts, include structured transcript entries" })),
2588
+ includeDocumentBodies: Type.Optional(Type.Boolean({ description: "For artifacts/export, export linked transcript and smart-note Google Docs text through Drive." })),
2589
+ outputDir: Type.Optional(Type.String({ description: "For export, output directory" })),
2590
+ zip: Type.Optional(Type.Boolean({ description: "For export, also write a .zip archive" })),
2591
+ dryRun: Type.Optional(Type.Boolean({ description: "For export, return the manifest without writing files." })),
2592
+ includeAllConferenceRecords: Type.Optional(Type.Boolean({ description: "For artifacts, attendance, or export with meeting input, fetch all conference records instead of only the latest." })),
2593
+ mergeDuplicateParticipants: Type.Optional(Type.Boolean({ description: "For attendance, merge duplicate participant resources." })),
2594
+ lateAfterMinutes: optionalPositiveIntegerSchema({ description: "For attendance, mark participants late after this many minutes." }),
2595
+ earlyBeforeMinutes: optionalPositiveIntegerSchema({ description: "For attendance, mark early leavers before this many minutes." }),
2596
+ accessToken: Type.Optional(Type.String({ description: "Access token override" })),
2597
+ refreshToken: Type.Optional(Type.String({ description: "Refresh token override" })),
2598
+ clientId: Type.Optional(Type.String({ description: "OAuth client id override" })),
2599
+ clientSecret: Type.Optional(Type.String({ description: "OAuth client secret override" })),
2600
+ expiresAt: Type.Optional(Type.Number({ description: "Cached access token expiry ms" }))
2601
+ });
2602
+ //#endregion
2603
+ //#region extensions/google-meet/index.ts
2610
2604
  var google_meet_default = definePluginEntry({
2611
2605
  id: "google-meet",
2612
2606
  name: "Google Meet",
@@ -2614,22 +2608,16 @@ var google_meet_default = definePluginEntry({
2614
2608
  configSchema: googleMeetConfigSchema,
2615
2609
  register(api) {
2616
2610
  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
- };
2611
+ const ensureRuntime = createGoogleMeetRuntimeAccessor({
2612
+ api,
2613
+ config
2614
+ });
2615
+ api.registerTranscriptSourceProvider(createMeetingTranscriptSourceProvider({
2616
+ id: "google-meet",
2617
+ aliases: ["googlemeet", "meet"],
2618
+ name: "Google Meet",
2619
+ runtime: async () => (await ensureRuntime()).transcriptSourceRuntime()
2620
+ }));
2633
2621
  api.registerGatewayMethod("googlemeet.join", async ({ params, client, respond }) => {
2634
2622
  try {
2635
2623
  const trustedParams = keepTrustedToolAgentId(asParamRecord(params), client);
@@ -2645,7 +2633,7 @@ var google_meet_default = definePluginEntry({
2645
2633
  agentId: normalizeOptionalString(trustedParams.agentId)
2646
2634
  }));
2647
2635
  } catch (err) {
2648
- sendError(respond, err);
2636
+ sendGoogleMeetGatewayError(respond, err);
2649
2637
  }
2650
2638
  });
2651
2639
  api.registerGatewayMethod("googlemeet.create", async ({ params, client, respond }) => {
@@ -2662,31 +2650,31 @@ var google_meet_default = definePluginEntry({
2662
2650
  raw
2663
2651
  }));
2664
2652
  } catch (err) {
2665
- sendError(respond, err);
2653
+ sendGoogleMeetGatewayError(respond, err);
2666
2654
  }
2667
2655
  });
2668
2656
  api.registerGatewayMethod("googlemeet.status", async ({ params, respond }) => {
2669
2657
  try {
2670
2658
  respond(true, await (await ensureRuntime()).status(normalizeOptionalString(params?.sessionId)));
2671
2659
  } catch (err) {
2672
- sendError(respond, err);
2660
+ sendGoogleMeetGatewayError(respond, err);
2673
2661
  }
2674
2662
  });
2675
2663
  api.registerGatewayMethod("googlemeet.transcript", async ({ params, respond }) => {
2676
2664
  try {
2677
2665
  const sessionId = normalizeOptionalString(params?.sessionId);
2678
2666
  if (!sessionId) {
2679
- sendError(respond, /* @__PURE__ */ new Error("sessionId required"), ErrorCodes.INVALID_REQUEST);
2667
+ sendGoogleMeetGatewayError(respond, /* @__PURE__ */ new Error("sessionId required"), ErrorCodes.INVALID_REQUEST);
2680
2668
  return;
2681
2669
  }
2682
2670
  const sinceIndex = params?.sinceIndex;
2683
2671
  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);
2672
+ sendGoogleMeetGatewayError(respond, /* @__PURE__ */ new Error("sinceIndex must be a non-negative safe integer"), ErrorCodes.INVALID_REQUEST);
2685
2673
  return;
2686
2674
  }
2687
2675
  respond(true, await (await ensureRuntime()).transcript(sessionId, sinceIndex === void 0 ? {} : { sinceIndex }));
2688
2676
  } catch (err) {
2689
- sendError(respond, err);
2677
+ sendGoogleMeetGatewayError(respond, err);
2690
2678
  }
2691
2679
  });
2692
2680
  api.registerGatewayMethod("googlemeet.recoverCurrentTab", async ({ params, respond }) => {
@@ -2696,7 +2684,7 @@ var google_meet_default = definePluginEntry({
2696
2684
  transport: normalizeTransport(params?.transport)
2697
2685
  }));
2698
2686
  } catch (err) {
2699
- sendError(respond, err);
2687
+ sendGoogleMeetGatewayError(respond, err);
2700
2688
  }
2701
2689
  });
2702
2690
  api.registerGatewayMethod("googlemeet.setup", async ({ params, respond }) => {
@@ -2707,7 +2695,7 @@ var google_meet_default = definePluginEntry({
2707
2695
  dialInNumber: normalizeOptionalString(params?.dialInNumber)
2708
2696
  }));
2709
2697
  } catch (err) {
2710
- sendError(respond, err);
2698
+ sendGoogleMeetGatewayError(respond, err);
2711
2699
  }
2712
2700
  });
2713
2701
  api.registerGatewayMethod("googlemeet.latest", async ({ params, respond }) => {
@@ -2727,7 +2715,7 @@ var google_meet_default = definePluginEntry({
2727
2715
  ...resolved.calendarEvent ? { calendarEvent: resolved.calendarEvent } : {}
2728
2716
  });
2729
2717
  } catch (err) {
2730
- sendError(respond, err);
2718
+ sendGoogleMeetGatewayError(respond, err);
2731
2719
  }
2732
2720
  });
2733
2721
  api.registerGatewayMethod("googlemeet.calendarEvents", async ({ params, respond }) => {
@@ -2742,7 +2730,7 @@ var google_meet_default = definePluginEntry({
2742
2730
  ...window
2743
2731
  }));
2744
2732
  } catch (err) {
2745
- sendError(respond, err);
2733
+ sendGoogleMeetGatewayError(respond, err);
2746
2734
  }
2747
2735
  });
2748
2736
  api.registerGatewayMethod("googlemeet.artifacts", async ({ params, respond }) => {
@@ -2759,7 +2747,7 @@ var google_meet_default = definePluginEntry({
2759
2747
  allConferenceRecords: resolved.allConferenceRecords
2760
2748
  }));
2761
2749
  } catch (err) {
2762
- sendError(respond, err);
2750
+ sendGoogleMeetGatewayError(respond, err);
2763
2751
  }
2764
2752
  });
2765
2753
  api.registerGatewayMethod("googlemeet.attendance", async ({ params, respond }) => {
@@ -2777,26 +2765,26 @@ var google_meet_default = definePluginEntry({
2777
2765
  earlyBeforeMinutes: resolved.earlyBeforeMinutes
2778
2766
  }));
2779
2767
  } catch (err) {
2780
- sendError(respond, err);
2768
+ sendGoogleMeetGatewayError(respond, err);
2781
2769
  }
2782
2770
  });
2783
2771
  api.registerGatewayMethod("googlemeet.export", async ({ params, respond }) => {
2784
2772
  try {
2785
2773
  respond(true, await exportGoogleMeetBundleFromParams(config, asParamRecord(params)));
2786
2774
  } catch (err) {
2787
- sendError(respond, err);
2775
+ sendGoogleMeetGatewayError(respond, err);
2788
2776
  }
2789
2777
  });
2790
2778
  api.registerGatewayMethod("googlemeet.leave", async ({ params, respond }) => {
2791
2779
  try {
2792
2780
  const sessionId = normalizeOptionalString(params?.sessionId);
2793
2781
  if (!sessionId) {
2794
- sendError(respond, /* @__PURE__ */ new Error("sessionId required"), ErrorCodes.INVALID_REQUEST);
2782
+ sendGoogleMeetGatewayError(respond, /* @__PURE__ */ new Error("sessionId required"), ErrorCodes.INVALID_REQUEST);
2795
2783
  return;
2796
2784
  }
2797
2785
  respond(true, await (await ensureRuntime()).leave(sessionId));
2798
2786
  } catch (err) {
2799
- sendError(respond, err);
2787
+ sendGoogleMeetGatewayError(respond, err);
2800
2788
  }
2801
2789
  });
2802
2790
  api.registerGatewayMethod("googlemeet.endActiveConference", async ({ params, respond }) => {
@@ -2807,19 +2795,19 @@ var google_meet_default = definePluginEntry({
2807
2795
  meeting: resolveMeetingInput(config, raw.meeting)
2808
2796
  }));
2809
2797
  } catch (err) {
2810
- sendError(respond, err);
2798
+ sendGoogleMeetGatewayError(respond, err);
2811
2799
  }
2812
2800
  });
2813
2801
  api.registerGatewayMethod("googlemeet.speak", async ({ params, respond }) => {
2814
2802
  try {
2815
2803
  const sessionId = normalizeOptionalString(params?.sessionId);
2816
2804
  if (!sessionId) {
2817
- sendError(respond, /* @__PURE__ */ new Error("sessionId required"), ErrorCodes.INVALID_REQUEST);
2805
+ sendGoogleMeetGatewayError(respond, /* @__PURE__ */ new Error("sessionId required"), ErrorCodes.INVALID_REQUEST);
2818
2806
  return;
2819
2807
  }
2820
2808
  respond(true, await (await ensureRuntime()).speak(sessionId, normalizeOptionalString(params?.message)));
2821
2809
  } catch (err) {
2822
- sendError(respond, err);
2810
+ sendGoogleMeetGatewayError(respond, err);
2823
2811
  }
2824
2812
  });
2825
2813
  api.registerGatewayMethod("googlemeet.testSpeech", async ({ params, client, respond }) => {
@@ -2837,7 +2825,7 @@ var google_meet_default = definePluginEntry({
2837
2825
  agentId: normalizeOptionalString(trustedParams.agentId)
2838
2826
  }));
2839
2827
  } catch (err) {
2840
- sendError(respond, err);
2828
+ sendGoogleMeetGatewayError(respond, err);
2841
2829
  }
2842
2830
  });
2843
2831
  api.registerGatewayMethod("googlemeet.testListen", async ({ params, client, respond }) => {
@@ -2851,7 +2839,7 @@ var google_meet_default = definePluginEntry({
2851
2839
  timeoutMs: readPositiveIntegerParam(trustedParams, "timeoutMs")
2852
2840
  }));
2853
2841
  } catch (err) {
2854
- sendError(respond, err);
2842
+ sendGoogleMeetGatewayError(respond, err);
2855
2843
  }
2856
2844
  });
2857
2845
  api.registerTool((toolContext) => ({
@@ -3008,7 +2996,7 @@ var google_meet_default = definePluginEntry({
3008
2996
  default: throw new Error("unknown google_meet action");
3009
2997
  }
3010
2998
  } catch (err) {
3011
- return jsonResult(formatGatewayError(err));
2999
+ return jsonResult(formatGoogleMeetGatewayError(err));
3012
3000
  }
3013
3001
  }
3014
3002
  }), { name: "google_meet" });