@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/{chrome-create-CBdUFMBH.js → chrome-create-DkWXFsK2.js} +32 -30
- package/dist/{cli-CxRkKrq1.js → cli-CksOXL4n.js} +660 -599
- package/dist/{create-DK5lNDEp.js → create-CNAkrEju.js} +20 -2
- package/dist/index.js +504 -516
- package/node_modules/typebox/build/compile/validator.d.mts +1 -4
- package/node_modules/typebox/build/guard/guard.d.mts +1 -1
- package/node_modules/typebox/build/guard/guard.mjs +5 -2
- package/node_modules/typebox/build/guard/string.mjs +16 -4
- package/node_modules/typebox/build/schema/engine/_functions.mjs +34 -15
- package/node_modules/typebox/build/type/action/readonly_object.d.mts +5 -0
- package/node_modules/typebox/build/type/action/readonly_object.mjs +8 -0
- package/node_modules/typebox/build/typebox.d.mts +1 -1
- package/node_modules/typebox/build/typebox.mjs +1 -1
- package/node_modules/typebox/package.json +1 -1
- package/node_modules/typebox/readme.md +29 -32
- package/npm-shrinkwrap.json +7 -7
- package/openclaw.plugin.json +3 -2
- package/package.json +5 -5
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
|
|
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 {
|
|
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 {
|
|
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$
|
|
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$
|
|
664
|
+
allowsMicrophone: isGoogleMeetTalkBackMode$1,
|
|
646
665
|
buildStatusJoinScript: (params) => meetStatusScript({
|
|
647
|
-
allowMicrophone: isGoogleMeetTalkBackMode$
|
|
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: (
|
|
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
|
|
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
|
|
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 && (
|
|
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 ((
|
|
825
|
+
if (verified()) break;
|
|
800
826
|
}
|
|
801
827
|
}
|
|
802
|
-
const speechOutputVerified = (
|
|
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
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
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
|
-
|
|
887
|
-
|
|
888
|
-
|
|
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
|
-
|
|
1093
|
-
|
|
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$
|
|
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$
|
|
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:
|
|
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$
|
|
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$
|
|
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:
|
|
1381
|
+
logScope: GOOGLE_MEET_PLATFORM_ADAPTER.logScope,
|
|
1411
1382
|
logPrefix: params.mode === "agent" ? "node agent" : "node"
|
|
1412
1383
|
});
|
|
1413
|
-
const bindings =
|
|
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
|
-
|
|
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
|
|
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:
|
|
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:
|
|
1652
|
+
agentId: normalizeAgentId(request.agentId ?? params.config.realtime.agentId ?? this.#agentId)
|
|
1706
1653
|
}),
|
|
1707
|
-
createSession: ({
|
|
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) =>
|
|
1717
|
-
normalizeMeetingUrlForReuse: (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) =>
|
|
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}` :
|
|
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)
|
|
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/
|
|
2092
|
-
const loadGoogleMeetCreateModule = createLazyRuntimeModule(() => import("./create-
|
|
2093
|
-
const loadGoogleMeetCliModule = createLazyRuntimeModule(() => import("./cli-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
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
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
2999
|
+
return jsonResult(formatGoogleMeetGatewayError(err));
|
|
3012
3000
|
}
|
|
3013
3001
|
}
|
|
3014
3002
|
}), { name: "google_meet" });
|