Source-grounded rewrite of 529 published docs pages with per-unit information-loss verification: 1,713 factual corrections cited to src/**, generated surfaces regenerated, frontmatter titles preserved for i18n, release notes pages untouched. All docs gates green. Closes #100141
68 KiB
summary, read_when, title
| summary | read_when | title | |||
|---|---|---|---|---|---|
| Google Meet plugin: join explicit Meet URLs through Chrome or Twilio with agent talk-back defaults |
|
Google Meet plugin |
The google-meet plugin joins explicit Meet URLs on behalf of an OpenClaw agent. It is deliberately narrow:
- It only joins
https://meet.google.com/...URLs; it never dials into a meeting from a phone number it discovers itself. googlemeet createcan mint a new Meet URL through the Google Meet API (or a browser fallback) and join it by default.- Chrome participation uses a signed-in Chrome profile, optionally on a paired node. Twilio participation dials a phone number plus PIN/DTMF through the Voice call plugin; it cannot dial a Meet URL directly.
mode: "agent"(default) transcribes participant speech with a realtime provider, routes it to the configured OpenClaw agent, and speaks the answer with regular OpenClaw TTS.mode: "bidi"lets a realtime voice model answer directly.mode: "transcribe"joins observe-only with no talk-back.- There is no automatic consent announcement when the plugin joins a call.
- The CLI command is
googlemeet;meetis reserved for broader agent teleconference workflows.
Quick start
Install the local audio dependencies, then set a realtime provider key. OpenAI is the default transcription provider for agent mode; Google Gemini Live is available as the bidi-mode voice provider:
brew install blackhole-2ch sox
export OPENAI_API_KEY=sk-...
# only needed when realtime.voiceProvider is "google" for bidi mode
export GEMINI_API_KEY=...
blackhole-2ch installs the BlackHole 2ch virtual audio device Chrome routes through. Homebrew's installer requires a reboot before macOS exposes the device:
sudo reboot
After reboot, verify both pieces:
system_profiler SPAudioDataType | grep -i BlackHole
command -v sox
Enable the plugin:
{
plugins: {
entries: {
"google-meet": {
enabled: true,
config: {},
},
},
},
}
Check setup, then join:
openclaw googlemeet setup
openclaw googlemeet join https://meet.google.com/abc-defg-hij
setup output is agent-readable and mode/transport-aware: it reports Chrome profile, node pinning, and, for realtime Chrome joins, the BlackHole/SoX audio bridge and the delayed-intro check. Observe-only joins skip realtime prerequisites:
openclaw googlemeet setup --transport chrome-node --mode transcribe
When Twilio delegation is configured, setup also reports whether voice-call, Twilio credentials, and public webhook exposure are ready. Treat any ok: false check as a blocker for that transport/mode before an agent joins. Use --json for machine-readable output, and --transport chrome|chrome-node|twilio to preflight a specific transport ahead of time:
openclaw googlemeet setup --transport twilio
Or let an agent join through the google_meet tool:
{
"action": "join",
"url": "https://meet.google.com/abc-defg-hij",
"transport": "chrome-node",
"mode": "agent"
}
On non-macOS Gateway hosts, google_meet stays visible for artifact, calendar, setup, transcribe, Twilio, and chrome-node actions, but local Chrome talk-back (transport: "chrome" with mode: "agent" or "bidi") is blocked before it reaches the audio bridge, because that path currently depends on macOS BlackHole 2ch. Use mode: "transcribe", Twilio dial-in, or a macOS chrome-node host instead.
Create a meeting
openclaw googlemeet create --transport chrome-node --mode agent
openclaw googlemeet create --no-join
create has two paths, reported in the result's source field:
api: used when Google Meet OAuth credentials are configured. Deterministic; does not depend on browser UI state.browser: used without OAuth credentials. OpenClaw openshttps://meet.google.com/newon the pinned Chrome node and waits for Google to redirect to a real meeting-code URL; the OpenClaw Chrome profile on that node must already be signed in to Google. Join and create both reuse an existing Meet tab (or an in-progress.../new/ Google account prompt tab) before opening a new one; tab matching ignores harmless query strings likeauthuser.
create joins by default and returns joined: true plus the join session. Pass --no-join (CLI) or "join": false (tool) to mint the URL only.
For API-created rooms, set an explicit access policy instead of inheriting the Google account default:
openclaw googlemeet create --access-type OPEN --transport chrome-node --mode agent
--access-type |
Who can join without knocking |
|---|---|
OPEN |
Anyone with the Meet URL |
TRUSTED |
Host org's trusted users, invited external users, and dial-in users |
RESTRICTED |
Invitees only |
This only applies to API-created rooms, so OAuth must be configured. If you authenticated before this option existed, rerun openclaw googlemeet auth login --json after adding the meetings.space.settings scope to your OAuth consent screen.
If the browser fallback hits a Google login or Meet permission blocker, the tool returns manualActionRequired: true with manualActionReason, manualActionMessage, and the browser.nodeId/browser.targetId/browserUrl. Report that message and stop opening new Meet tabs until the operator finishes the browser step.
Observe-only join
Set "mode": "transcribe" to skip the duplex realtime bridge (no BlackHole/SoX requirement, no talk-back). Transcribe-mode Chrome joins also skip OpenClaw's microphone/camera permission grant and the Meet Use microphone path; if Meet shows the audio-choice interstitial, automation tries Continue without microphone first. Managed Chrome transports in this mode install a best-effort Meet caption observer. googlemeet status --json and googlemeet doctor report captioning, captionsEnabledAttempted, transcriptLines, lastCaptionAt, lastCaptionSpeaker, lastCaptionText, and a recentTranscript tail.
For a yes/no listen probe:
openclaw googlemeet test-listen <meet-url> --transport chrome-node
It joins in transcribe mode, waits for fresh caption/transcript movement, and returns listenVerified, listenTimedOut, manual-action fields, and current caption health.
Realtime session health
During talk-back sessions, google_meet status reports Chrome/audio bridge health: inCall, manualActionRequired, providerConnected, realtimeReady, audioInputActive, audioOutputActive, last input/output timestamps, byte counters, and bridge-closed state. Managed Chrome sessions only speak the intro/test phrase after health reports inCall: true; otherwise speechReady: false and the speech attempt is blocked rather than silently no-opping.
Local Chrome joins through the signed-in OpenClaw browser profile and needs BlackHole 2ch for the mic/speaker path. A single BlackHole device is enough for a first smoke test but can echo; use separate virtual devices or a Loopback-style graph for clean duplex audio.
Local Gateway + Parallels Chrome
A full Gateway or model API key is not required inside a macOS VM just to give it Chrome. Run the Gateway and agent locally; run a node host in the VM.
| Runs where | What |
|---|---|
| Gateway host | OpenClaw Gateway, agent workspace, model/API keys, realtime provider, Google Meet plugin config |
| Parallels macOS VM | OpenClaw CLI/node host, Chrome, SoX, BlackHole 2ch, a Chrome profile signed in to Google |
| Not needed in the VM | Gateway service, agent config, model provider setup |
Install VM dependencies, reboot, verify:
brew install blackhole-2ch sox
sudo reboot
system_profiler SPAudioDataType | grep -i BlackHole
command -v sox
Enable the plugin in the VM and start the node host:
openclaw plugins enable google-meet
openclaw node run --host <gateway-host> --port 18789 --display-name parallels-macos
If <gateway-host> is a LAN IP without TLS, opt in for that trusted private network:
OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1 \
openclaw node run --host <gateway-lan-ip> --port 18789 --display-name parallels-macos
Use the same flag when installing as a LaunchAgent (it is process environment, stored in the LaunchAgent environment when present on the install command, not an openclaw.json setting):
OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1 \
openclaw node install --host <gateway-lan-ip> --port 18789 --display-name parallels-macos --force
openclaw node restart
Approve the node from the Gateway host, then confirm it advertises both googlemeet.chrome and browser capability/browser.proxy:
openclaw devices list
openclaw devices approve <requestId>
openclaw nodes status
Route Meet through that node:
{
gateway: {
nodes: {
allowCommands: ["googlemeet.chrome", "browser.proxy"],
},
},
plugins: {
entries: {
"google-meet": {
enabled: true,
config: {
defaultTransport: "chrome-node",
chrome: {
guestName: "OpenClaw Agent",
autoJoin: true,
reuseExistingTab: true,
},
chromeNode: {
node: "parallels-macos",
},
},
},
},
},
}
Now join normally from the Gateway host:
openclaw googlemeet join https://meet.google.com/abc-defg-hij
For a one-command smoke test that creates or reuses a session, speaks a known phrase, and prints session health:
openclaw googlemeet test-speech https://meet.google.com/abc-defg-hij
During realtime join, browser automation fills the guest name, clicks Join/Ask to join, and accepts Meet's first-run "Use microphone" prompt when it appears (or "Continue without microphone" during observe-only join and browser-only meeting creation). If the profile is signed out, Meet is waiting for host admission, Chrome needs mic/camera permission, or Meet is stuck on an unresolved prompt, the result reports manualActionRequired: true with manualActionReason and manualActionMessage. Stop retrying, report that message plus browserUrl/browserTitle, and retry only after the manual action completes.
If chromeNode.node is omitted, OpenClaw auto-selects only when exactly one connected node advertises both googlemeet.chrome and browser control; pin chromeNode.node (node id, display name, or remote IP) when several capable nodes are connected.
Common failure checks
| Symptom | Fix |
|---|---|
Configured Google Meet node ... is not usable: offline |
The pinned node is known but unavailable. Report the setup blocker; do not silently fall back to another transport unless asked. |
No connected Google Meet-capable node |
Run openclaw node run in the VM, approve pairing, and run openclaw plugins enable google-meet and openclaw plugins enable browser there. Confirm gateway.nodes.allowCommands includes googlemeet.chrome and browser.proxy. |
BlackHole 2ch audio device not found |
Install blackhole-2ch on the host being checked and reboot. |
BlackHole 2ch audio device not found on the node |
Install blackhole-2ch in the VM and reboot the VM. |
| Chrome opens but cannot join | Sign in to the browser profile in the VM, or keep chrome.guestName set. Guest auto-join uses OpenClaw browser automation through the node browser proxy; point the node's browser.defaultProfile (or a named existing-session profile) at the profile you want. |
| Duplicate Meet tabs | Leave chrome.reuseExistingTab: true. OpenClaw activates an existing tab for the same URL, and creation reuses an in-progress .../new or Google account prompt tab, before opening another. |
| No audio | Route Meet mic/speaker through the virtual audio path used by OpenClaw; use separate virtual devices or Loopback-style routing for clean duplex audio. |
Install notes
The Chrome talk-back default uses two external tools that OpenClaw does not bundle or redistribute; install them as host dependencies through Homebrew:
sox: command-line audio utility. The plugin issues explicit CoreAudio device commands for the default 24 kHz PCM16 audio bridge.blackhole-2ch: macOS virtual audio driver providing theBlackHole 2chdevice Chrome/Meet route through.
SoX is licensed LGPL-2.0-only AND GPL-2.0-only; BlackHole is GPL-3.0. If you build an installer or appliance that bundles BlackHole with OpenClaw, review BlackHole's upstream licensing or get a separate license from Existential Audio.
Transports
| Transport | Use when |
|---|---|
chrome |
Chrome/audio live on the Gateway host |
chrome-node |
Chrome/audio live on a paired node (for example a Parallels macOS VM) |
twilio |
Phone dial-in fallback via the Voice Call plugin, when Chrome participation is not available |
Chrome
Opens the Meet URL through OpenClaw browser control and joins as the signed-in OpenClaw browser profile. On macOS, the plugin checks for BlackHole 2ch before launch and, if configured, runs an audio bridge health/startup command before opening Chrome. For local Chrome, pick the profile with browser.defaultProfile; chrome.browserProfile is passed to chrome-node hosts instead.
openclaw googlemeet join https://meet.google.com/abc-defg-hij --transport chrome
openclaw googlemeet join https://meet.google.com/abc-defg-hij --transport chrome-node
Chrome mic/speaker audio routes through the local OpenClaw audio bridge. If BlackHole 2ch is not installed, the join fails with a setup error instead of joining without an audio path.
Twilio
A strict dial plan delegated to the Voice call plugin. It does not parse Meet pages for phone numbers; Google Meet must expose a phone dial-in number and PIN for the meeting.
Enable Voice Call on the Gateway host, not the Chrome node:
{
plugins: {
allow: ["google-meet", "voice-call", "google"],
entries: {
"google-meet": {
enabled: true,
config: {
defaultTransport: "chrome-node",
// or set "twilio" if Twilio should be the default
},
},
"voice-call": {
enabled: true,
config: {
provider: "twilio",
inboundPolicy: "allowlist",
realtime: {
enabled: true,
provider: "google",
instructions: "Join this Google Meet as an OpenClaw agent. Be brief.",
toolPolicy: "safe-read-only",
providers: {
google: {
silenceDurationMs: 500,
startSensitivity: "high",
},
},
},
},
},
google: {
enabled: true,
},
},
},
}
Provide Twilio credentials through environment to keep secrets out of openclaw.json:
export TWILIO_ACCOUNT_SID=AC...
export TWILIO_AUTH_TOKEN=...
export TWILIO_FROM_NUMBER=+15550001234
export GEMINI_API_KEY=...
Use realtime.provider: "openai" with OPENAI_API_KEY instead if OpenAI is the realtime voice provider.
Restart or reload the Gateway after enabling voice-call; plugin config changes do not take effect until reload. Verify:
openclaw config validate
openclaw plugins list | grep -E 'google-meet|voice-call'
openclaw googlemeet setup
When Twilio delegation is wired, googlemeet setup includes twilio-voice-call-plugin, twilio-voice-call-credentials, and twilio-voice-call-webhook checks.
openclaw googlemeet join https://meet.google.com/abc-defg-hij \
--transport twilio \
--dial-in-number +15551234567 \
--pin 123456
Use --dtmf-sequence for a custom sequence, with leading w or commas for a pause before the PIN:
openclaw googlemeet join https://meet.google.com/abc-defg-hij \
--transport twilio \
--dial-in-number +15551234567 \
--dtmf-sequence ww123456#
OAuth and preflight
OAuth is optional for creating a Meet link, because googlemeet create can fall back to browser automation. Configure OAuth for official API create, space resolution, or Meet Media API preflight. Chrome/Chrome-node joins never depend on OAuth; they use a signed-in Chrome profile, BlackHole/SoX, and (for chrome-node) a connected node either way.
Create Google credentials
In Google Cloud Console:
Internal is simplest for a Google Workspace organization. External works for personal/test setups; while the app is in Testing, add each Google account that will authorize it as a test user. - `https://www.googleapis.com/auth/meetings.space.created` - `https://www.googleapis.com/auth/meetings.space.readonly` - `https://www.googleapis.com/auth/meetings.space.settings` - `https://www.googleapis.com/auth/meetings.conference.media.readonly` - `https://www.googleapis.com/auth/calendar.events.readonly` (Calendar lookup) - `https://www.googleapis.com/auth/drive.meet.readonly` (transcript/smart-note document body export) Application type **Web application**. Authorized redirect URI:http://localhost:8085/oauth2callback
meetings.space.created is required by spaces.create. meetings.space.readonly resolves Meet URLs/codes to spaces. meetings.space.settings lets OpenClaw pass SpaceConfig settings such as accessType during API room creation. meetings.conference.media.readonly is for Meet Media API preflight and media work; Google may require Developer Preview enrollment for actual Media API use. calendar.events.readonly is only needed for --today/--event calendar lookup. drive.meet.readonly is only needed for --include-doc-bodies export. If you only need browser-based Chrome joins, skip OAuth entirely.
Mint the refresh token
Configure oauth.clientId and optionally oauth.clientSecret (or pass them as environment variables), then run:
openclaw googlemeet auth login --json
This runs a PKCE flow with a localhost callback on http://localhost:8085/oauth2callback, and prints an oauth config block with a refresh token. Add --manual for a copy/paste flow when the browser cannot reach the local callback:
OPENCLAW_GOOGLE_MEET_CLIENT_ID="your-client-id" \
OPENCLAW_GOOGLE_MEET_CLIENT_SECRET="your-client-secret" \
openclaw googlemeet auth login --json --manual
JSON output:
{
"oauth": {
"clientId": "your-client-id",
"clientSecret": "your-client-secret",
"refreshToken": "refresh-token",
"accessToken": "access-token",
"expiresAt": 1770000000000
},
"scope": "..."
}
Store the oauth object under the plugin config:
{
plugins: {
entries: {
"google-meet": {
enabled: true,
config: {
oauth: {
clientId: "your-client-id",
clientSecret: "your-client-secret",
refreshToken: "refresh-token",
},
},
},
},
},
}
Prefer environment variables when you do not want the refresh token in config; config is resolved first, then environment as fallback. If you authenticated before meeting creation, calendar lookup, or document-body export support existed, rerun openclaw googlemeet auth login --json so the refresh token covers the current scope set.
Verify OAuth with doctor
openclaw googlemeet doctor --oauth --json
This checks OAuth config exists and the refresh token can mint an access token, without loading the Chrome runtime or requiring a connected node. The report includes only status fields (ok, configured, tokenSource, expiresAt, check messages) and never prints the access token, refresh token, or client secret.
| Check | Meaning |
|---|---|
oauth-config |
oauth.clientId plus oauth.refreshToken, or a cached access token, is present |
oauth-token |
The cached access token is still valid, or the refresh token minted a new one |
meet-spaces-get |
Optional --meeting check resolved an existing Meet space |
meet-spaces-create |
Optional --create-space check created a new Meet space |
Prove Meet API enablement and spaces.create scope with the side-effecting create check:
openclaw googlemeet doctor --oauth --create-space --json
Prove read access to an existing space:
openclaw googlemeet doctor --oauth --meeting https://meet.google.com/abc-defg-hij --json
openclaw googlemeet resolve-space --meeting https://meet.google.com/abc-defg-hij
A 403 from these checks usually means the Meet REST API is disabled, the refresh token is missing the required scope, or the Google account cannot access that space. A refresh-token error means rerun openclaw googlemeet auth login --json and store the new oauth block.
No OAuth is needed for the browser fallback; Google auth there comes from the signed-in Chrome profile on the selected node, not OpenClaw config.
These environment variables are accepted as fallbacks:
OPENCLAW_GOOGLE_MEET_CLIENT_IDorGOOGLE_MEET_CLIENT_IDOPENCLAW_GOOGLE_MEET_CLIENT_SECRETorGOOGLE_MEET_CLIENT_SECRETOPENCLAW_GOOGLE_MEET_REFRESH_TOKENorGOOGLE_MEET_REFRESH_TOKENOPENCLAW_GOOGLE_MEET_ACCESS_TOKENorGOOGLE_MEET_ACCESS_TOKENOPENCLAW_GOOGLE_MEET_ACCESS_TOKEN_EXPIRES_ATorGOOGLE_MEET_ACCESS_TOKEN_EXPIRES_ATOPENCLAW_GOOGLE_MEET_DEFAULT_MEETINGorGOOGLE_MEET_DEFAULT_MEETINGOPENCLAW_GOOGLE_MEET_PREVIEW_ACKorGOOGLE_MEET_PREVIEW_ACK
Resolve, preflight, and read artifacts
openclaw googlemeet resolve-space --meeting https://meet.google.com/abc-defg-hij
openclaw googlemeet preflight --meeting https://meet.google.com/abc-defg-hij
After Meet has created conference records:
openclaw googlemeet artifacts --meeting https://meet.google.com/abc-defg-hij
openclaw googlemeet attendance --meeting https://meet.google.com/abc-defg-hij
openclaw googlemeet export --meeting https://meet.google.com/abc-defg-hij --output ./meet-export
With --meeting, artifacts and attendance use the latest conference record by default; pass --all-conference-records for every retained record.
Calendar lookup resolves the meeting URL from Google Calendar before reading artifacts (requires a refresh token that includes the Calendar events readonly scope):
openclaw googlemeet latest --today
openclaw googlemeet calendar-events --today --json
openclaw googlemeet artifacts --event "Weekly sync"
openclaw googlemeet attendance --today --format csv --output attendance.csv
--today searches today's primary calendar for an event with a Meet link; --event <query> searches matching event text; --calendar <id> targets a non-primary calendar. calendar-events previews matching events and marks which one latest/artifacts/attendance/export will choose.
If you already know the conference record id, address it directly:
openclaw googlemeet latest --meeting https://meet.google.com/abc-defg-hij
openclaw googlemeet artifacts --conference-record conferenceRecords/abc123 --json
openclaw googlemeet attendance --conference-record conferenceRecords/abc123 --json
Close the room for an API-created space:
openclaw googlemeet end-active-conference https://meet.google.com/abc-defg-hij
Calls spaces.endActiveConference and requires OAuth with the meetings.space.created scope for a space the authorized account can manage. Accepts a Meet URL, meeting code, or spaces/{id} and resolves it to the API space resource first. This is separate from googlemeet leave: leave stops OpenClaw's local/session participation; end-active-conference asks Google Meet to end the active conference for the space.
Write a readable report:
openclaw googlemeet artifacts --conference-record conferenceRecords/abc123 \
--format markdown --output meet-artifacts.md
openclaw googlemeet attendance --conference-record conferenceRecords/abc123 \
--format csv --output meet-attendance.csv
openclaw googlemeet export --conference-record conferenceRecords/abc123 \
--include-doc-bodies --zip --output meet-export
openclaw googlemeet export --conference-record conferenceRecords/abc123 \
--include-doc-bodies --dry-run
artifacts returns conference record metadata plus participant, recording, transcript, structured transcript-entry, and smart-note resource metadata when Google exposes it. --no-transcript-entries skips entry lookup for large meetings. attendance expands participants into participant-session rows with first/last seen times, total session duration, late/early-leave flags, and duplicate participant resources merged by signed-in user or display name; --no-merge-duplicates keeps raw resources separate, --late-after-minutes/--early-before-minutes tune the thresholds.
export writes a folder with summary.md, attendance.csv, transcript.md, artifacts.json, attendance.json, and manifest.json. manifest.json records the chosen input, export options, conference records, output files, counts, token source, any Calendar event used, and partial-retrieval warnings. --zip also writes a portable archive next to the folder. --include-doc-bodies exports linked transcript/smart-note Google Docs text through Drive files.export (requires the Drive Meet readonly scope); without it, exports include Meet metadata and structured transcript entries only. A partial artifact failure (smart-note listing, transcript-entry, or document-body error) keeps the warning in the summary/manifest instead of failing the whole export. --dry-run fetches the same data and prints the manifest JSON without creating the folder or ZIP.
Agents use the same actions through the google_meet tool (export, create with accessType, end_active_conference, test_listen); see Tool.
Live smoke test
OPENCLAW_LIVE_TEST=1 \
OPENCLAW_GOOGLE_MEET_LIVE_MEETING=https://meet.google.com/abc-defg-hij \
pnpm test:live -- extensions/google-meet/google-meet.live.test.ts
openclaw googlemeet setup --transport chrome-node --mode transcribe
openclaw googlemeet test-listen https://meet.google.com/abc-defg-hij --transport chrome-node --timeout-ms 30000
| Variable | Purpose |
|---|---|
OPENCLAW_LIVE_TEST=1 |
Enables guarded live tests |
OPENCLAW_GOOGLE_MEET_LIVE_MEETING |
Retained Meet URL, code, or spaces/{id} |
OPENCLAW_GOOGLE_MEET_CLIENT_ID / GOOGLE_MEET_CLIENT_ID |
OAuth client id |
OPENCLAW_GOOGLE_MEET_REFRESH_TOKEN / GOOGLE_MEET_REFRESH_TOKEN |
Refresh token |
OPENCLAW_GOOGLE_MEET_CLIENT_SECRET, OPENCLAW_GOOGLE_MEET_ACCESS_TOKEN, OPENCLAW_GOOGLE_MEET_ACCESS_TOKEN_EXPIRES_AT |
Optional; same fallback names without the OPENCLAW_ prefix also work |
The base artifact/attendance smoke needs meetings.space.readonly and meetings.conference.media.readonly. Calendar lookup needs calendar.events.readonly. Drive document-body export needs drive.meet.readonly.
Create examples
openclaw googlemeet create
Prints the new meeting URI, source, and join session. With OAuth it uses the Meet API; without it, the pinned Chrome node's signed-in profile. Browser fallback JSON:
{
"source": "browser",
"meetingUri": "https://meet.google.com/abc-defg-hij",
"joined": true,
"browser": {
"nodeId": "ba0f4e4bc...",
"targetId": "tab-1"
},
"join": {
"session": {
"id": "meet_...",
"url": "https://meet.google.com/abc-defg-hij"
}
}
}
If the browser fallback hits Google login or a Meet permission blocker first, google_meet returns structured details instead of a plain string:
{
"source": "browser",
"error": "google-login-required: Sign in to Google in the OpenClaw browser profile, then retry meeting creation.",
"manualActionRequired": true,
"manualActionReason": "google-login-required",
"manualActionMessage": "Sign in to Google in the OpenClaw browser profile, then retry meeting creation.",
"browser": {
"nodeId": "ba0f4e4bc...",
"targetId": "tab-1",
"browserUrl": "https://accounts.google.com/signin",
"browserTitle": "Sign in - Google Accounts"
}
}
API create JSON:
{
"source": "api",
"meetingUri": "https://meet.google.com/abc-defg-hij",
"joined": true,
"space": {
"name": "spaces/abc-defg-hij",
"meetingCode": "abc-defg-hij",
"meetingUri": "https://meet.google.com/abc-defg-hij"
},
"join": {
"session": {
"id": "meet_...",
"url": "https://meet.google.com/abc-defg-hij"
}
}
}
Creating joins by default, but Chrome/Chrome-node still needs a signed-in Google profile to join through the browser; if signed out, OpenClaw reports manualActionRequired: true or a browser fallback error and asks the operator to finish Google login before retrying.
Set preview.enrollmentAcknowledged: true only after confirming your Cloud project, OAuth principal, and meeting participants are enrolled in the Google Workspace Developer Preview Program for Meet media APIs.
Config
The common Chrome agent path only needs the plugin enabled, BlackHole, SoX, a realtime provider key, and a configured OpenClaw TTS provider:
{
plugins: {
entries: {
"google-meet": {
enabled: true,
config: {},
},
},
},
}
Defaults
| Key | Default | Notes |
|---|---|---|
defaultTransport |
"chrome" |
|
defaultMode |
"agent" |
"realtime" is accepted as a legacy alias for "agent"; new callers should say "agent" |
chromeNode.node |
unset | Node id/name/IP for chrome-node; required when more than one capable node may be connected |
chrome.launch |
true |
Launch Chrome for the join; set false only when reusing an already-open session |
chrome.audioBackend |
"blackhole-2ch" |
|
chrome.guestName |
"OpenClaw Agent" |
Shown on the signed-out Meet guest screen |
chrome.autoJoin |
true |
Best-effort guest-name fill and Join Now click on chrome-node |
chrome.reuseExistingTab |
true |
Activates an existing Meet tab instead of opening duplicates |
chrome.waitForInCallMs |
20000 |
Wait for the Meet tab to report in-call before the talk-back intro fires |
chrome.audioFormat |
"pcm16-24khz" |
Command-pair audio format; "g711-ulaw-8khz" is only for legacy/custom command pairs that emit telephony audio |
chrome.audioBufferBytes |
4096 |
SoX processing buffer for generated command-pair audio commands (half SoX's default 8192-byte buffer, lowering pipe latency); values are clamped to a minimum of 17 bytes |
chrome.audioInputCommand |
generated SoX command | Reads from CoreAudio BlackHole 2ch, writes audio in chrome.audioFormat |
chrome.audioOutputCommand |
generated SoX command | Reads audio in chrome.audioFormat, writes to CoreAudio BlackHole 2ch |
chrome.bargeInInputCommand |
unset | Optional local microphone command writing signed 16-bit little-endian mono PCM for human barge-in detection during assistant playback; applies to the Gateway-hosted command-pair bridge |
chrome.bargeInRmsThreshold |
650 |
RMS level counted as human interruption |
chrome.bargeInPeakThreshold |
2500 |
Peak level counted as human interruption |
chrome.bargeInCooldownMs |
900 |
Minimum delay between repeated interruption clears |
mode (per-request) |
"agent" |
Talk-back mode; see the Agent and bidi modes table |
realtime.provider |
"openai" |
Compatibility fallback used when the scoped fields below are unset |
realtime.transcriptionProvider |
"openai" |
Provider id used by agent mode for realtime transcription |
realtime.voiceProvider |
unset | Provider id used by bidi mode for direct realtime voice; set to "google" for Gemini Live while keeping agent-mode transcription on OpenAI. Pair with realtime.model to pick the specific Gemini Live model. |
realtime.toolPolicy |
"safe-read-only" |
See Agent and bidi modes |
realtime.instructions |
brief spoken-reply instructions | Tells the model to speak briefly and use openclaw_agent_consult for deeper answers |
realtime.introMessage |
"Say exactly: I'm here and listening." |
Spoken once when the realtime bridge connects; set to "" to join silently |
realtime.agentId |
"main" |
OpenClaw agent id used for openclaw_agent_consult |
voiceCall.enabled |
true |
Delegates the Twilio PSTN call, DTMF, and intro greeting to the Voice Call plugin |
voiceCall.dtmfDelayMs |
12000 |
Leading wait before playing a PIN-derived DTMF sequence over Twilio |
voiceCall.postDtmfSpeechDelayMs |
5000 |
Delay before requesting the realtime intro greeting after Voice Call starts the Twilio leg |
chrome.audioBridgeCommand and chrome.audioBridgeHealthCommand let an external bridge own the whole local audio path instead of chrome.audioInputCommand/chrome.audioOutputCommand; see Notes for the constraint on which mode can use them.
An openclaw doctor --fix migration exists for the legacy realtime.provider: "google" shape: it moves that intent to realtime.voiceProvider: "google" plus realtime.transcriptionProvider: "openai" when those fields are not already set.
Optional overrides
{
defaults: {
meeting: "https://meet.google.com/abc-defg-hij",
},
browser: {
defaultProfile: "openclaw",
},
chrome: {
guestName: "OpenClaw Agent",
waitForInCallMs: 30000,
bargeInInputCommand: [
"sox",
"-q",
"-t",
"coreaudio",
"External Microphone",
"-r",
"24000",
"-c",
"1",
"-b",
"16",
"-e",
"signed-integer",
"-t",
"raw",
"-",
],
},
chromeNode: {
node: "parallels-macos",
},
defaultMode: "agent",
realtime: {
provider: "openai",
transcriptionProvider: "openai",
voiceProvider: "google",
model: "gemini-2.5-flash-native-audio-preview-12-2025",
agentId: "jay",
toolPolicy: "owner",
introMessage: "Say exactly: I'm here.",
providers: {
google: {
speakerVoice: "Kore",
},
},
},
}
ElevenLabs for both agent-mode listening and speaking:
{
messages: {
tts: {
provider: "elevenlabs",
providers: {
elevenlabs: {
modelId: "eleven_v3",
speakerVoiceId: "pMsXgVXv3BLzUgSXRplE",
},
},
},
},
plugins: {
entries: {
"google-meet": {
config: {
realtime: {
transcriptionProvider: "elevenlabs",
providers: {
elevenlabs: {
modelId: "scribe_v2_realtime",
audioFormat: "ulaw_8000",
sampleRate: 8000,
commitStrategy: "vad",
},
},
},
},
},
},
},
}
The persistent Meet voice comes from messages.tts.providers.elevenlabs.speakerVoiceId. Agent replies can also use per-reply [[tts:speakerVoiceId=... model=eleven_v3]] directives when TTS model overrides are enabled, but config is the deterministic default for meetings. On join, logs show transcriptionProvider=elevenlabs, and each spoken reply logs provider=elevenlabs model=eleven_v3 speakerVoiceId=<voiceId>.
Twilio-only config:
{
defaultTransport: "twilio",
twilio: {
defaultDialInNumber: "+15551234567",
defaultPin: "123456",
},
voiceCall: {
gatewayUrl: "ws://127.0.0.1:18789",
},
}
With voiceCall.enabled: true (the default) and Twilio transport, Voice Call places the DTMF sequence before opening the realtime media stream, then uses the saved intro text as the initial realtime greeting. If voice-call is not enabled, Google Meet can still validate and record the dial plan but cannot place the Twilio call.
Tool
Agents use the google_meet tool:
{
"action": "join",
"url": "https://meet.google.com/abc-defg-hij",
"transport": "chrome-node",
"mode": "agent"
}
action |
Purpose |
|---|---|
join |
Join an explicit Meet URL |
create |
Create a space (and join by default); supports accessType/entryPointAccess |
status |
List active sessions, or inspect one by sessionId |
setup_status |
Run the same checks as googlemeet setup |
resolve_space |
Resolve a URL/code/spaces/{id} via spaces.get |
preflight |
Validate OAuth + meeting resolution prerequisites |
latest |
Find the latest conference record for a meeting |
calendar_events |
Preview Calendar events with Meet links |
artifacts |
List conference records and participant/recording/transcript/smart-note metadata |
attendance |
List participants and participant sessions |
export |
Write the artifacts/attendance/transcript/manifest bundle; set "dryRun": true for manifest-only |
recover_current_tab |
Focus/inspect an existing Meet tab without opening a new one |
leave |
End a session (hangs up the underlying Twilio call for delegated sessions) |
end_active_conference |
End the active Google Meet conference for an API-managed space |
speak |
Make the realtime agent speak immediately, given sessionId and message |
test_speech |
Create/reuse a session, trigger a known phrase, return Chrome health |
test_listen |
Create/reuse an observe-only session, wait for caption/transcript movement |
test_speech always forces mode: "agent" or "bidi" and fails if asked to run in mode: "transcribe", because observe-only sessions cannot emit speech. Its speechOutputVerified result is based on realtime audio output bytes increasing during that call, so a reused session with older audio does not count as a fresh check.
Use transport: "chrome" when Chrome runs on the Gateway host, transport: "chrome-node" when it runs on a paired node. In both cases the model providers and openclaw_agent_consult run on the Gateway host, so model credentials stay there. Agent-mode logs include the resolved transcription provider/model at bridge startup and the TTS provider/model/voice/output format/sample rate after each synthesized reply. Raw mode: "realtime" is still accepted as a legacy compatibility alias for mode: "agent", but it is no longer advertised in the tool's mode enum.
create with an API-backed room and explicit access policy:
{
"action": "create",
"transport": "chrome-node",
"mode": "agent",
"accessType": "OPEN"
}
Ending a known room's active conference:
{
"action": "end_active_conference",
"meeting": "https://meet.google.com/abc-defg-hij"
}
Listen-first validation before claiming a meeting is useful:
{
"action": "test_listen",
"url": "https://meet.google.com/abc-defg-hij",
"transport": "chrome-node",
"timeoutMs": 30000
}
Speaking on demand:
{
"action": "speak",
"sessionId": "meet_...",
"message": "Say exactly: I'm here and listening."
}
status includes Chrome health when available:
| Field | Meaning |
|---|---|
inCall |
Chrome appears to be inside the Meet call |
micMuted |
Best-effort Meet microphone state |
manualActionRequired / manualActionReason / manualActionMessage |
Browser profile needs manual login, Meet host admission, permissions, or browser-control repair before speech can work |
speechReady / speechBlockedReason / speechBlockedMessage |
Whether managed Chrome speech is allowed now; speechReady: false means OpenClaw did not send the intro/test phrase |
providerConnected / realtimeReady |
Realtime voice bridge state |
lastInputAt / lastOutputAt |
Last audio seen from/sent to the bridge |
audioOutputRouted / audioOutputDeviceLabel |
Whether the Meet tab's media output was actively routed to the bridge's BlackHole device |
lastSuppressedInputAt / suppressedInputBytes |
Loopback input ignored while assistant playback is active |
Agent and bidi modes
| Mode | Who decides the answer | Speech output path | Use when |
|---|---|---|---|
agent |
The configured OpenClaw agent | Normal OpenClaw TTS runtime | You want "my agent is in the meeting" behavior |
bidi |
The realtime voice model | Realtime voice provider audio response | You want the lowest-latency conversational voice loop |
agent mode: the realtime transcription provider hears meeting audio, final participant transcripts route through the configured OpenClaw agent, and the answer is spoken through regular OpenClaw TTS. Nearby final-transcript fragments are coalesced before the consult so one spoken turn does not produce several stale partial answers; realtime input is suppressed while queued assistant audio is still playing, and recent assistant-like transcript echoes are ignored before the consult so BlackHole loopback does not make the agent answer its own speech.
bidi mode: the realtime voice model answers directly and can call openclaw_agent_consult for deeper reasoning, current information, or normal OpenClaw tools. The consult tool runs the regular OpenClaw agent behind the scenes with recent meeting transcript context and returns a concise spoken answer; in agent mode OpenClaw sends that answer directly to TTS, in bidi mode the realtime voice model can speak it back. It uses the same shared consult machinery as Voice Call.
By default consults run against the main agent; set realtime.agentId to point a Meet lane at a dedicated agent workspace, model defaults, tool policy, memory, and session history. Agent-mode consults use a per-meeting agent:<id>:subagent:google-meet:<session> session key so follow-up questions keep meeting context while inheriting normal agent policy. When an agent calls google_meet in agent mode, the consultant session forks the caller's current transcript before answering participant speech; the Meet session stays separate so meeting follow-ups do not mutate the caller transcript directly.
realtime.toolPolicy controls the consult run:
| Policy | Behavior |
|---|---|
safe-read-only |
Expose the consult tool; limit the regular agent to read, web_search, web_fetch, x_search, memory_search, memory_get |
owner |
Expose the consult tool; let the regular agent use its normal tool policy |
none |
Do not expose the consult tool to the realtime voice model |
The consult session key is scoped per Meet session, so follow-up consult calls reuse prior consult context during the same meeting.
Force a spoken readiness check after Chrome has fully joined:
openclaw googlemeet speak meet_... "Say exactly: I'm here and listening."
Full join-and-speak smoke:
openclaw googlemeet test-speech https://meet.google.com/abc-defg-hij \
--transport chrome-node \
--message "Say exactly: I'm here and listening."
Live test checklist
Before handing a meeting to an unattended agent:
openclaw googlemeet setup
openclaw nodes status
openclaw googlemeet test-speech https://meet.google.com/abc-defg-hij \
--transport chrome-node \
--message "Say exactly: Google Meet speech test complete."
Expected Chrome-node state:
googlemeet setupis all green, and includeschrome-node-connectedwhen Chrome-node is the default transport or a node is pinned.nodes statusshows the selected node connected, advertising bothgooglemeet.chromeandbrowser.proxy.- The Meet tab joins, and
test-speechreturns Chrome health withinCall: true.
For a remote Chrome host such as a Parallels macOS VM, the shortest safe check after updating the Gateway or the VM:
openclaw googlemeet setup
openclaw nodes status --connected
openclaw nodes invoke \
--node parallels-macos \
--command googlemeet.chrome \
--params '{"action":"setup"}'
That proves the Gateway plugin is loaded, the VM node is connected with the current token, and the Meet audio bridge is available before an agent opens a real meeting tab.
For a Twilio smoke, use a meeting that exposes phone dial-in details:
openclaw googlemeet setup
openclaw googlemeet join https://meet.google.com/abc-defg-hij \
--transport twilio \
--dial-in-number +15551234567 \
--pin 123456
Expected Twilio state:
googlemeet setupincludes greentwilio-voice-call-plugin,twilio-voice-call-credentials, andtwilio-voice-call-webhookchecks.voicecallis available in the CLI after Gateway reload.- The returned session has
transport: "twilio"and atwilio.voiceCallId. openclaw logs --followshows DTMF TwiML served before realtime TwiML, then a realtime bridge with the initial greeting queued.googlemeet leave <sessionId>hangs up the delegated voice call.
Troubleshooting
Agent cannot see the Google Meet tool
Confirm the plugin is enabled and reload the Gateway; the running agent only sees plugin tools registered by the current Gateway process:
openclaw plugins list | grep google-meet
openclaw googlemeet setup
On non-macOS Gateway hosts, google_meet stays visible, but local Chrome talk-back actions are blocked before they hit the audio bridge. Use mode: "transcribe", Twilio dial-in, or a macOS chrome-node host instead of the default local Chrome agent path.
No connected Google Meet-capable node
On the node host:
openclaw plugins enable google-meet
openclaw plugins enable browser
OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1 \
openclaw node run --host <gateway-lan-ip> --port 18789 --display-name parallels-macos
On the Gateway host:
openclaw devices list
openclaw devices approve <requestId>
openclaw nodes status
The node must be connected and list googlemeet.chrome plus browser.proxy; the Gateway config must allow both:
{
gateway: {
nodes: {
allowCommands: ["browser.proxy", "googlemeet.chrome"],
},
},
}
If googlemeet setup fails chrome-node-connected, or the Gateway log reports gateway token mismatch, reinstall or restart the node with the current Gateway token:
OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1 \
openclaw node install \
--host <gateway-lan-ip> \
--port 18789 \
--display-name parallels-macos \
--force
Then reload the node service and re-run:
openclaw googlemeet setup
openclaw nodes status --connected
Browser opens but agent cannot join
Run googlemeet test-listen for observe-only joins or googlemeet test-speech for realtime joins, then inspect the returned Chrome health. If either reports manualActionRequired: true, show manualActionMessage to the operator and stop retrying until the browser action is complete.
Common manual actions: sign in to the Chrome profile; admit the guest from the Meet host account; grant Chrome microphone/camera permissions when the native prompt appears; close or repair a stuck Meet permission dialog.
Do not report "not signed in" just because Meet asks "Do you want people to hear you in the meeting?"; that is Meet's audio-choice interstitial. OpenClaw clicks Use microphone through browser automation when available and keeps waiting for the real meeting state; for create-only browser fallback it may click Continue without microphone instead, since minting the URL does not need the realtime audio path.
Meeting creation fails
googlemeet create uses the Meet API spaces.create when OAuth is configured, otherwise the pinned Chrome node browser. Confirm:
- API creation:
oauth.clientIdandoauth.refreshToken(or matchingOPENCLAW_GOOGLE_MEET_*env vars) are present, and the refresh token was minted after create support was added; older tokens may lackmeetings.space.created, so rerunopenclaw googlemeet auth login --json. - Browser fallback:
defaultTransport: "chrome-node"andchromeNode.nodepoint at a connected node withbrowser.proxyandgooglemeet.chrome; the OpenClaw Chrome profile on that node is signed in and can openhttps://meet.google.com/new. - Browser fallback retries: reuse an existing
.../newor Google account prompt tab before opening a new one; retry the tool call rather than manually opening another tab. - Manual action: if the tool returns
manualActionRequired: true, usebrowser.nodeId,browser.targetId,browserUrl, andmanualActionMessageto guide the operator; do not retry in a loop. - Audio-choice interstitial: if Meet shows "Do you want people to hear you in the meeting?", leave the tab open. OpenClaw should click Use microphone or (create-only) Continue without microphone and keep waiting for the generated URL; if it cannot, the error should mention
meet-audio-choice-required, notgoogle-login-required.
Agent joins but does not talk
openclaw googlemeet setup
openclaw googlemeet doctor
Use mode: "agent" for the STT -> OpenClaw agent -> TTS path, mode: "bidi" for the direct realtime voice fallback. mode: "transcribe" intentionally starts no talk-back bridge. For observe-only debugging, run openclaw googlemeet status --json <session-id> after participants speak and check captioning, transcriptLines, lastCaptionText. If inCall is true but transcriptLines stays 0, Meet captions may be disabled, no one has spoken since the observer was installed, the Meet UI changed, or live captions are unavailable for the meeting language/account.
googlemeet test-speech always checks the realtime path and reports whether bridge output bytes were observed for that invocation. If speechOutputVerified is false and speechOutputTimedOut is true, the realtime provider may have accepted the utterance but OpenClaw did not see new output bytes reach the Chrome audio bridge.
Also verify: a realtime provider key (OPENAI_API_KEY or GEMINI_API_KEY) is available on the Gateway host; BlackHole 2ch is visible on the Chrome host; sox exists there; Meet mic/speaker are routed through the virtual audio path (doctor should show meet output routed: yes for local Chrome realtime joins).
googlemeet doctor [session-id] prints session, node, in-call state, manual action reason, realtime provider connection, realtimeReady, audio input/output activity, last audio timestamps, byte counters, and browser URL. Use googlemeet status [session-id] --json for raw JSON, and googlemeet doctor --oauth (add --meeting or --create-space) to verify OAuth refresh without exposing tokens.
If an agent timed out and a Meet tab is already open, inspect it without opening another one:
openclaw googlemeet recover-tab
openclaw googlemeet recover-tab https://meet.google.com/abc-defg-hij
The equivalent tool action is recover_current_tab: it focuses and inspects an existing Meet tab for the selected transport (local browser control for chrome, the configured node for chrome-node) without opening a new tab or session, and reports the current blocker (login, admission, permissions, audio-choice state). The CLI command talks to the configured Gateway, which must be running; chrome-node also requires the node to be connected.
Twilio setup checks fail
twilio-voice-call-plugin fails when voice-call is not allowed or not enabled: add it to plugins.allow, enable plugins.entries.voice-call, reload the Gateway.
twilio-voice-call-credentials fails when the Twilio backend is missing account SID, auth token, or caller number:
export TWILIO_ACCOUNT_SID=AC...
export TWILIO_AUTH_TOKEN=...
export TWILIO_FROM_NUMBER=+15550001234
twilio-voice-call-webhook fails when voice-call has no public webhook exposure, or publicUrl points at loopback/private network space. Do not use localhost, 127.0.0.1, 0.0.0.0, 10.x, 172.16.x-172.31.x, 192.168.x, 169.254.x, fc00::/7, or fd00::/8 as publicUrl; carrier callbacks cannot reach those. Set plugins.entries.voice-call.config.publicUrl to a public URL, or configure a tunnel/Tailscale exposure:
{
plugins: {
entries: {
"voice-call": {
enabled: true,
config: {
provider: "twilio",
fromNumber: "+15550001234",
publicUrl: "https://voice.example.com/voice/webhook",
},
},
},
},
}
For local development, use a tunnel or Tailscale exposure instead of a private host URL:
{
plugins: {
entries: {
"voice-call": {
config: {
tunnel: { provider: "ngrok" },
// or
tailscale: { mode: "funnel", path: "/voice/webhook" },
},
},
},
},
}
Restart or reload the Gateway, then:
openclaw googlemeet setup --transport twilio
openclaw voicecall setup
openclaw voicecall smoke
voicecall smoke is readiness-only by default. Dry-run a specific number:
openclaw voicecall smoke --to "+15555550123"
Only add --yes to intentionally place a live outbound call:
openclaw voicecall smoke --to "+15555550123" --yes
Twilio call starts but never enters the meeting
Confirm the Meet event exposes phone dial-in details, and pass the exact dial-in number plus PIN or a custom DTMF sequence:
openclaw googlemeet join https://meet.google.com/abc-defg-hij \
--transport twilio \
--dial-in-number +15551234567 \
--dtmf-sequence ww123456#
Use leading w or commas in --dtmf-sequence for a pause before the PIN.
If the call is created but the Meet roster never shows the dial-in participant:
openclaw googlemeet doctor <session-id>: confirm the delegated Twilio call ID, whether DTMF was queued, and whether the intro greeting was requested.openclaw voicecall status --call-id <id>: confirm the call is still active.openclaw voicecall tail: confirm Twilio webhooks are arriving at the Gateway.openclaw logs --follow: look for the Twilio Meet sequence: Google Meet delegates the join, Voice Call stores and serves pre-connect DTMF TwiML, Voice Call serves realtime TwiML for the Twilio call, then Google Meet requests intro speech withvoicecall.speak.- Re-run
openclaw googlemeet setup --transport twilio; a green setup check is required but does not prove the meeting PIN sequence is correct. - Confirm the dial-in number belongs to the same Meet invitation and region as the PIN.
- Increase
voiceCall.dtmfDelayMsfrom the 12-second default if Meet answers slowly or the call transcript still shows the PIN prompt after pre-connect DTMF was sent. - If the participant joins but you do not hear the greeting, check
openclaw logs --followfor the post-DTMFvoicecall.speakrequest and either media-stream TTS playback or the Twilio<Say>fallback. If the transcript still shows "enter the meeting PIN", the phone leg has not joined the Meet room yet, so participants will not hear speech.
If webhooks do not arrive, debug the Voice Call plugin first: the provider must reach plugins.entries.voice-call.config.publicUrl or the configured tunnel. See Voice call troubleshooting.
Notes
Google Meet's official media API is receive-oriented, so speaking into a call still needs a participant path. This plugin keeps that boundary visible: Chrome handles browser participation and local audio routing; Twilio handles phone dial-in participation.
Chrome talk-back modes need BlackHole 2ch plus either:
chrome.audioInputCommandpluschrome.audioOutputCommand: OpenClaw owns the bridge and pipes audio inchrome.audioFormatbetween those commands and the selected provider.agentmode uses realtime transcription plus regular TTS;bidimode uses the realtime voice provider. The default path is 24 kHz PCM16 withchrome.audioBufferBytes: 4096; 8 kHz G.711 mu-law remains available for legacy command pairs.chrome.audioBridgeCommand: an external bridge command owns the whole local audio path and must exit after starting or validating its daemon. Valid only forbidi, becauseagentmode needs direct command-pair access for TTS.
With the command-pair Chrome bridge, chrome.bargeInInputCommand can listen to a separate local microphone and clear assistant playback when a human starts talking, keeping human speech ahead of assistant output even while the shared BlackHole loopback input is temporarily suppressed during assistant playback. Like chrome.audioInputCommand/chrome.audioOutputCommand, it is an operator-configured local command: use an explicit trusted command path or argument list, never a script from an untrusted location.
For clean duplex audio, route Meet output and Meet microphone through separate virtual devices or a Loopback-style virtual device graph; a single shared BlackHole device can echo other participants back into the call.
googlemeet speak triggers the active talk-back audio bridge for a Chrome session; googlemeet leave stops it (and, for Twilio sessions delegated through Voice Call, hangs up the underlying call). Use googlemeet end-active-conference to also close the active Google Meet conference for an API-managed space.