fix(google-meet): hide realtime alias from agent schema

This commit is contained in:
Peter Steinberger
2026-05-04 03:21:46 +01:00
parent b0b5983ce3
commit 30b201eff0
8 changed files with 76 additions and 50 deletions

View File

@@ -386,6 +386,7 @@ describe("google-meet plugin", () => {
oauth: {},
auth: { provider: "google-oauth" },
});
expect(resolveGoogleMeetConfig({ defaultMode: "realtime" }).defaultMode).toBe("agent");
expect(resolveGoogleMeetConfig({}).realtime.instructions).toContain("openclaw_agent_consult");
});
@@ -621,7 +622,7 @@ describe("google-meet plugin", () => {
description: expect.stringContaining("recover_current_tab"),
},
transport: { type: "string", enum: ["chrome", "chrome-node", "twilio"] },
mode: { type: "string", enum: ["agent", "bidi", "realtime", "transcribe"] },
mode: { type: "string", enum: ["agent", "bidi", "transcribe"] },
},
});
});
@@ -3154,7 +3155,12 @@ describe("google-meet plugin", () => {
createdAt: "2026-04-27T00:00:00.000Z",
updatedAt: "2026-04-27T00:00:00.000Z",
participantIdentity: "signed-in Google Chrome profile",
realtime: { enabled: true, provider: "openai", toolPolicy: "safe-read-only" },
realtime: {
enabled: true,
strategy: "agent",
transcriptionProvider: "openai",
toolPolicy: "safe-read-only",
},
chrome: {
audioBackend: "blackhole-2ch",
launched: true,

View File

@@ -155,10 +155,14 @@ const googleMeetConfigSchema = {
help: "Legacy realtime alias setting. Use mode=agent or mode=bidi for new Meet joins.",
},
"realtime.provider": {
label: "Realtime Provider",
help: "Defaults to OpenAI; uses OPENAI_API_KEY when no provider config is set.",
label: "Speech Provider",
help: "Agent mode uses this for realtime transcription. Bidi mode uses it as the realtime voice provider.",
},
"realtime.model": {
label: "Bidi Realtime Model",
help: "Only used by mode=bidi. Agent mode answers with the configured OpenClaw agent and regular TTS.",
advanced: true,
},
"realtime.model": { label: "Realtime Model", advanced: true },
"realtime.instructions": { label: "Realtime Instructions", advanced: true },
"realtime.introMessage": {
label: "Realtime Intro Message",
@@ -238,9 +242,9 @@ const GoogleMeetToolSchema = Type.Object({
),
mode: Type.Optional(
Type.String({
enum: ["agent", "bidi", "realtime", "transcribe"],
enum: ["agent", "bidi", "transcribe"],
description:
"Join mode. agent uses realtime transcription, the configured OpenClaw agent, and regular TTS. bidi uses the realtime voice model directly. realtime is a compatibility alias for agent. transcribe joins observe-only.",
"Join mode. agent uses realtime transcription, the configured OpenClaw agent, and regular TTS. bidi uses the realtime voice model directly. transcribe joins observe-only.",
}),
),
dialInNumber: Type.Optional(

View File

@@ -148,11 +148,12 @@
"help": "Legacy realtime alias setting. Use mode=agent or mode=bidi for new Meet joins."
},
"realtime.provider": {
"label": "Realtime Provider",
"help": "Defaults to OpenAI; uses OPENAI_API_KEY when no provider config is set."
"label": "Speech Provider",
"help": "Agent mode uses this for realtime transcription. Bidi mode uses it as the realtime voice provider."
},
"realtime.model": {
"label": "Realtime Model",
"label": "Bidi Realtime Model",
"help": "Only used by mode=bidi. Agent mode answers with the configured OpenClaw agent and regular TTS.",
"advanced": true
},
"realtime.instructions": {
@@ -227,7 +228,7 @@
},
"defaultMode": {
"type": "string",
"enum": ["agent", "bidi", "realtime", "transcribe"],
"enum": ["agent", "bidi", "transcribe"],
"default": "agent"
},
"chrome": {

View File

@@ -11,7 +11,7 @@ import {
listGoogleMeetCalendarEvents,
type GoogleMeetCalendarLookupResult,
} from "./calendar.js";
import type { GoogleMeetConfig, GoogleMeetMode, GoogleMeetTransport } from "./config.js";
import type { GoogleMeetConfig, GoogleMeetModeInput, GoogleMeetTransport } from "./config.js";
import { hasCreateSpaceConfigInput, resolveCreateSpaceConfig } from "./create.js";
import {
buildGoogleMeetPreflightReport,
@@ -37,7 +37,7 @@ import type { GoogleMeetRuntime } from "./runtime.js";
type JoinOptions = {
transport?: GoogleMeetTransport;
mode?: GoogleMeetMode;
mode?: GoogleMeetModeInput;
message?: string;
timeoutMs?: string;
dialInNumber?: string;
@@ -134,7 +134,7 @@ export type GoogleMeetExportManifest = {
type SetupOptions = {
json?: boolean;
mode?: GoogleMeetMode;
mode?: GoogleMeetModeInput;
transport?: GoogleMeetTransport;
};
@@ -181,7 +181,7 @@ type CreateOptions = {
entryPointAccess?: string;
join?: boolean;
transport?: GoogleMeetTransport;
mode?: GoogleMeetMode;
mode?: GoogleMeetModeInput;
message?: string;
dialInNumber?: string;
pin?: string;
@@ -349,12 +349,17 @@ function writeDoctorStatus(status: Awaited<ReturnType<GoogleMeetRuntime["status"
}
writeStdoutLine("node: %s", session.chrome?.nodeId ?? "local/none");
writeStdoutLine("audio bridge: %s", session.chrome?.audioBridge?.type ?? "none");
const bridgeProvider =
session.chrome?.audioBridge?.provider ??
session.realtime.transcriptionProvider ??
session.realtime.provider ??
"n/a";
writeStdoutLine(
"provider: %s",
session.chrome?.audioBridge?.provider ?? session.realtime.provider ?? "n/a",
session.mode === "agent" ? "transcription provider: %s" : "provider: %s",
bridgeProvider,
);
if (session.realtime.enabled) {
writeStdoutLine("realtime strategy: %s", session.realtime.strategy ?? "agent");
writeStdoutLine("talk-back mode: %s", session.realtime.strategy ?? session.mode);
}
writeStdoutLine("in call: %s", formatBoolean(health?.inCall));
writeStdoutLine("lobby waiting: %s", formatBoolean(health?.lobbyWaiting));
@@ -2268,7 +2273,7 @@ export function registerGoogleMeetCli(params: {
.command("setup")
.description("Show Google Meet transport setup status")
.option("--transport <transport>", "Transport to check: chrome, chrome-node, or twilio")
.option("--mode <mode>", "Mode to check: realtime or transcribe")
.option("--mode <mode>", "Mode to check: agent, bidi, or transcribe")
.option("--json", "Print JSON output", false)
.action(async (options: SetupOptions) => {
const rt = await params.ensureRuntime();

View File

@@ -3,7 +3,12 @@ import type { OpenClawConfig } from "openclaw/plugin-sdk/config-types";
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
import type { PluginRuntime, RuntimeLogger } from "openclaw/plugin-sdk/plugin-runtime";
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import type { GoogleMeetConfig, GoogleMeetMode, GoogleMeetTransport } from "./config.js";
import type {
GoogleMeetConfig,
GoogleMeetMode,
GoogleMeetModeInput,
GoogleMeetTransport,
} from "./config.js";
import { addGoogleMeetSetupCheck, getGoogleMeetSetupStatus } from "./setup.js";
import { isSameMeetUrlForReuse, resolveChromeNodeInfo } from "./transports/chrome-browser-proxy.js";
import { createMeetWithBrowserProxyOnNode } from "./transports/chrome-create.js";
@@ -60,8 +65,8 @@ function resolveTransport(input: GoogleMeetTransport | undefined, config: Google
return input ?? config.defaultTransport;
}
function resolveMode(input: GoogleMeetMode | undefined, config: GoogleMeetConfig) {
return input ?? config.defaultMode;
function resolveMode(input: GoogleMeetModeInput | undefined, config: GoogleMeetConfig) {
return input === "realtime" ? "agent" : (input ?? config.defaultMode);
}
function isGoogleMeetTalkBackMode(mode: GoogleMeetMode): boolean {
@@ -245,7 +250,7 @@ export class GoogleMeetRuntime {
async setupStatus(
options: {
transport?: GoogleMeetTransport;
mode?: GoogleMeetMode;
mode?: GoogleMeetModeInput;
dialInNumber?: string;
} = {},
) {
@@ -397,8 +402,9 @@ export class GoogleMeetRuntime {
realtime: {
enabled: isGoogleMeetTalkBackMode(mode),
strategy: mode === "bidi" ? "bidi" : "agent",
provider: this.params.config.realtime.provider,
model: this.params.config.realtime.model,
provider: mode === "bidi" ? this.params.config.realtime.provider : undefined,
model: mode === "bidi" ? this.params.config.realtime.model : undefined,
transcriptionProvider: mode === "agent" ? this.params.config.realtime.provider : undefined,
toolPolicy: this.params.config.realtime.toolPolicy,
},
notes: [],
@@ -690,7 +696,8 @@ export class GoogleMeetRuntime {
recentTranscript?: GoogleMeetChromeHealth["recentTranscript"];
session: GoogleMeetSession;
}> {
if (request.mode && isGoogleMeetTalkBackMode(request.mode)) {
const requestedMode = request.mode ? resolveMode(request.mode, this.params.config) : undefined;
if (requestedMode && isGoogleMeetTalkBackMode(requestedMode)) {
throw new Error(
"test_listen requires mode: transcribe; use test_speech for talk-back sessions.",
);

View File

@@ -1,11 +1,11 @@
import type { GoogleMeetMode, GoogleMeetTransport } from "../config.js";
import type { GoogleMeetMode, GoogleMeetModeInput, GoogleMeetTransport } from "../config.js";
type GoogleMeetSessionState = "active" | "ended";
export type GoogleMeetJoinRequest = {
url: string;
transport?: GoogleMeetTransport;
mode?: GoogleMeetMode;
mode?: GoogleMeetModeInput;
message?: string;
timeoutMs?: number;
dialInNumber?: string;
@@ -106,6 +106,7 @@ export type GoogleMeetSession = {
strategy?: string;
provider?: string;
model?: string;
transcriptionProvider?: string;
toolPolicy: string;
};
chrome?: {