Files
openclaw/docs/plugins/google-meet.md
2026-04-24 18:51:39 +01:00

557 lines
17 KiB
Markdown

---
summary: "Google Meet plugin: join explicit Meet URLs through Chrome or Twilio with realtime voice defaults"
read_when:
- You want an OpenClaw agent to join a Google Meet call
- You are configuring Chrome, Chrome node, or Twilio as a Google Meet transport
title: "Google Meet plugin"
---
Google Meet participant support for OpenClaw — the plugin is explicit by design:
- It only joins an explicit `https://meet.google.com/...` URL.
- `realtime` voice is the default mode.
- Realtime voice can call back into the full OpenClaw agent when deeper
reasoning or tools are needed.
- Agents choose the join behavior with `mode`: use `realtime` for live
listen/talk-back, or `transcribe` to join/control the browser without the
realtime voice bridge.
- Auth starts as personal Google OAuth or an already signed-in Chrome profile.
- There is no automatic consent announcement.
- The default Chrome audio backend is `BlackHole 2ch`.
- Chrome can run locally or on a paired node host.
- Twilio accepts a dial-in number plus optional PIN or DTMF sequence.
- The CLI command is `googlemeet`; `meet` is reserved for broader agent
teleconference workflows.
## Quick start
Install the local audio dependencies and configure a backend realtime voice
provider. OpenAI is the default; Google Gemini Live also works with
`realtime.provider: "google"`:
```bash
brew install blackhole-2ch sox
export OPENAI_API_KEY=sk-...
# or
export GEMINI_API_KEY=...
```
`blackhole-2ch` installs the `BlackHole 2ch` virtual audio device. Homebrew's
installer requires a reboot before macOS exposes the device:
```bash
sudo reboot
```
After reboot, verify both pieces:
```bash
system_profiler SPAudioDataType | grep -i BlackHole
command -v rec play
```
Enable the plugin:
```json5
{
plugins: {
entries: {
"google-meet": {
enabled: true,
config: {},
},
},
},
}
```
Check setup:
```bash
openclaw googlemeet setup
```
Join a meeting:
```bash
openclaw googlemeet join https://meet.google.com/abc-defg-hij
```
Or let an agent join through the `google_meet` tool:
```json
{
"action": "join",
"url": "https://meet.google.com/abc-defg-hij",
"transport": "chrome-node",
"mode": "realtime"
}
```
For an observe-only/browser-control join, set `"mode": "transcribe"`. That does
not start the duplex realtime model bridge, so it will not talk back into the
meeting.
Chrome joins as the signed-in Chrome profile. In Meet, pick `BlackHole 2ch` for
the microphone/speaker path used by OpenClaw. For clean duplex audio, use
separate virtual devices or a Loopback-style graph; a single BlackHole device is
enough for a first smoke test but can echo.
### Local Gateway + Parallels Chrome
You do **not** need a full OpenClaw Gateway or model API key inside a macOS VM
just to make the VM own Chrome. Run the Gateway and agent locally, then run a
node host in the VM. Enable the bundled plugin on the VM once so the node
advertises the Chrome command:
What runs where:
- Gateway host: OpenClaw Gateway, agent workspace, model/API keys, realtime
provider, and the Google Meet plugin config.
- Parallels macOS VM: OpenClaw CLI/node host, Google Chrome, SoX, BlackHole 2ch,
and a Chrome profile signed in to Google.
- Not needed in the VM: Gateway service, agent config, OpenAI/GPT key, or model
provider setup.
Install the VM dependencies:
```bash
brew install blackhole-2ch sox
```
Reboot the VM after installing BlackHole so macOS exposes `BlackHole 2ch`:
```bash
sudo reboot
```
After reboot, verify the VM can see the audio device and SoX commands:
```bash
system_profiler SPAudioDataType | grep -i BlackHole
command -v rec play
```
Install or update OpenClaw in the VM, then enable the bundled plugin there:
```bash
openclaw plugins enable google-meet
```
Start the node host in the VM:
```bash
openclaw node run --host <gateway-host> --port 18789 --display-name parallels-macos
```
If `<gateway-host>` is a LAN IP and you are not using TLS, the node refuses the
plaintext WebSocket unless you opt in for that trusted private network:
```bash
OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1 \
openclaw node run --host <gateway-lan-ip> --port 18789 --display-name parallels-macos
```
Use the same environment variable when installing the node as a LaunchAgent:
```bash
OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1 \
openclaw node install --host <gateway-lan-ip> --port 18789 --display-name parallels-macos --force
openclaw node restart
```
`OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1` is process environment, not an
`openclaw.json` setting. `openclaw node install` stores it in the LaunchAgent
environment when it is present on the install command.
Approve the node from the Gateway host:
```bash
openclaw devices list
openclaw devices approve <requestId>
```
Confirm the Gateway sees the node and that it advertises both `googlemeet.chrome`
and browser capability/`browser.proxy`:
```bash
openclaw nodes status
```
Route Meet through that node on the Gateway host:
```json5
{
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:
```bash
openclaw googlemeet join https://meet.google.com/abc-defg-hij
```
or ask the agent to use the `google_meet` tool with `transport: "chrome-node"`.
For a one-command smoke test that creates or reuses a session, speaks a known
phrase, and prints session health:
```bash
openclaw googlemeet test-speech https://meet.google.com/abc-defg-hij
```
If the browser profile is not signed in, Meet is waiting for host admission, or
Chrome needs microphone/camera permission, the join/test-speech result reports
`manualActionRequired: true` with `manualActionReason` and
`manualActionMessage`. Agents should stop retrying the join, report that message
to the operator, and retry only after the manual browser action is complete.
If `chromeNode.node` is omitted, OpenClaw auto-selects only when exactly one
connected node advertises both `googlemeet.chrome` and browser control. If
several capable nodes are connected, set `chromeNode.node` to the node id,
display name, or remote IP.
Common failure checks:
- `No connected Google Meet-capable node`: start `openclaw node run` in the VM,
approve pairing, and make sure `openclaw plugins enable google-meet` and
`openclaw plugins enable browser` were run in the VM. Also confirm the
Gateway host allows both node commands with
`gateway.nodes.allowCommands: ["googlemeet.chrome", "browser.proxy"]`.
- `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 inside the VM, or
keep `chrome.guestName` set for guest join. Guest auto-join uses OpenClaw
browser automation through the node browser proxy; make sure the node browser
config points at the profile you want, for example
`browser.defaultProfile: "user"` or a named existing-session profile.
- Duplicate Meet tabs: leave `chrome.reuseExistingTab: true` enabled. OpenClaw
activates an existing tab for the same Meet URL before opening a new one.
- No audio: in Meet, route microphone/speaker through the virtual audio device
path used by OpenClaw; use separate virtual devices or Loopback-style routing
for clean duplex audio.
## Install notes
The Chrome realtime default uses two external tools:
- `sox`: command-line audio utility. The plugin uses its `rec` and `play`
commands for the default 8 kHz G.711 mu-law audio bridge.
- `blackhole-2ch`: macOS virtual audio driver. It creates the `BlackHole 2ch`
audio device that Chrome/Meet can route through.
OpenClaw does not bundle or redistribute either package. The docs ask users to
install them as host dependencies through Homebrew. SoX is licensed as
`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 terms or get a separate license from Existential Audio.
## Transports
### Chrome
Chrome transport opens the Meet URL in Google Chrome and joins as the signed-in
Chrome profile. On macOS, the plugin checks for `BlackHole 2ch` before launch.
If configured, it also runs an audio bridge health command and startup command
before opening Chrome. Use `chrome` when Chrome/audio live on the Gateway host;
use `chrome-node` when Chrome/audio live on a paired node such as a Parallels
macOS VM.
```bash
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
```
Route Chrome microphone and speaker audio through the local OpenClaw audio
bridge. If `BlackHole 2ch` is not installed, the join fails with a setup error
instead of silently joining without an audio path.
### Twilio
Twilio transport is a strict dial plan delegated to the Voice Call plugin. It
does not parse Meet pages for phone numbers.
```bash
openclaw googlemeet join https://meet.google.com/abc-defg-hij \
--transport twilio \
--dial-in-number +15551234567 \
--pin 123456
```
Use `--dtmf-sequence` when the meeting needs a custom sequence:
```bash
openclaw googlemeet join https://meet.google.com/abc-defg-hij \
--transport twilio \
--dial-in-number +15551234567 \
--dtmf-sequence ww123456#
```
## OAuth and preflight
Google Meet Media API access uses a personal OAuth client first. Configure
`oauth.clientId` and optionally `oauth.clientSecret`, then run:
```bash
openclaw googlemeet auth login --json
```
The command prints an `oauth` config block with a refresh token. It uses PKCE,
localhost callback on `http://localhost:8085/oauth2callback`, and a manual
copy/paste flow with `--manual`.
These environment variables are accepted as fallbacks:
- `OPENCLAW_GOOGLE_MEET_CLIENT_ID` or `GOOGLE_MEET_CLIENT_ID`
- `OPENCLAW_GOOGLE_MEET_CLIENT_SECRET` or `GOOGLE_MEET_CLIENT_SECRET`
- `OPENCLAW_GOOGLE_MEET_REFRESH_TOKEN` or `GOOGLE_MEET_REFRESH_TOKEN`
- `OPENCLAW_GOOGLE_MEET_ACCESS_TOKEN` or `GOOGLE_MEET_ACCESS_TOKEN`
- `OPENCLAW_GOOGLE_MEET_ACCESS_TOKEN_EXPIRES_AT` or
`GOOGLE_MEET_ACCESS_TOKEN_EXPIRES_AT`
- `OPENCLAW_GOOGLE_MEET_DEFAULT_MEETING` or `GOOGLE_MEET_DEFAULT_MEETING`
- `OPENCLAW_GOOGLE_MEET_PREVIEW_ACK` or `GOOGLE_MEET_PREVIEW_ACK`
Resolve a Meet URL, code, or `spaces/{id}` through `spaces.get`:
```bash
openclaw googlemeet resolve-space --meeting https://meet.google.com/abc-defg-hij
```
Run preflight before media work:
```bash
openclaw googlemeet preflight --meeting https://meet.google.com/abc-defg-hij
```
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 realtime path only needs the plugin enabled, BlackHole, SoX,
and a backend realtime voice provider key. OpenAI is the default; set
`realtime.provider: "google"` to use Google Gemini Live:
```bash
brew install blackhole-2ch sox
export OPENAI_API_KEY=sk-...
# or
export GEMINI_API_KEY=...
```
Set the plugin config under `plugins.entries.google-meet.config`:
```json5
{
plugins: {
entries: {
"google-meet": {
enabled: true,
config: {},
},
},
},
}
```
Defaults:
- `defaultTransport: "chrome"`
- `defaultMode: "realtime"`
- `chromeNode.node`: optional node id/name/IP for `chrome-node`
- `chrome.audioBackend: "blackhole-2ch"`
- `chrome.guestName: "OpenClaw Agent"`: name used on the signed-out Meet guest
screen
- `chrome.autoJoin: true`: best-effort guest-name fill and Join Now click
through OpenClaw browser automation on `chrome-node`
- `chrome.reuseExistingTab: true`: activate an existing Meet tab instead of
opening duplicates
- `chrome.waitForInCallMs: 20000`: wait for the Meet tab to report in-call
before the realtime intro is triggered
- `chrome.audioInputCommand`: SoX `rec` command writing 8 kHz G.711 mu-law
audio to stdout
- `chrome.audioOutputCommand`: SoX `play` command reading 8 kHz G.711 mu-law
audio from stdin
- `realtime.provider: "openai"`
- `realtime.toolPolicy: "safe-read-only"`
- `realtime.instructions`: brief spoken replies, with
`openclaw_agent_consult` for deeper answers
- `realtime.introMessage`: short spoken readiness check when the realtime bridge
connects; set it to `""` to join silently
Optional overrides:
```json5
{
defaults: {
meeting: "https://meet.google.com/abc-defg-hij",
},
chrome: {
browserProfile: "Default",
guestName: "OpenClaw Agent",
waitForInCallMs: 30000,
},
chromeNode: {
node: "parallels-macos",
},
realtime: {
provider: "google",
toolPolicy: "owner",
introMessage: "Say exactly: I'm here.",
providers: {
google: {
model: "gemini-2.5-flash-native-audio-preview-12-2025",
voice: "Kore",
},
},
},
}
```
Twilio-only config:
```json5
{
defaultTransport: "twilio",
twilio: {
defaultDialInNumber: "+15551234567",
defaultPin: "123456",
},
voiceCall: {
gatewayUrl: "ws://127.0.0.1:18789",
},
}
```
## Tool
Agents can use the `google_meet` tool:
```json
{
"action": "join",
"url": "https://meet.google.com/abc-defg-hij",
"transport": "chrome-node",
"mode": "realtime"
}
```
Use `transport: "chrome"` when Chrome runs on the Gateway host. Use
`transport: "chrome-node"` when Chrome runs on a paired node such as a Parallels
VM. In both cases the realtime model and `openclaw_agent_consult` run on the
Gateway host, so model credentials stay there.
Use `action: "status"` to list active sessions or inspect a session ID. Use
`action: "speak"` with `sessionId` and `message` to make the realtime agent
speak immediately. Use `action: "test_speech"` to create or reuse the session,
trigger a known phrase, and return `inCall` health when the Chrome host can
report it. Use `action: "leave"` to mark a session ended.
`status` includes Chrome health when available:
- `inCall`: Chrome appears to be inside the Meet call
- `micMuted`: best-effort Meet microphone state
- `manualActionRequired` / `manualActionReason` / `manualActionMessage`: the
browser profile needs manual login, Meet host admission, permissions, or
browser-control repair before speech can work
- `providerConnected` / `realtimeReady`: realtime voice bridge state
- `lastInputAt` / `lastOutputAt`: last audio seen from or sent to the bridge
```json
{
"action": "speak",
"sessionId": "meet_...",
"message": "Say exactly: I'm here and listening."
}
```
## Realtime agent consult
Chrome realtime mode is optimized for a live voice loop. The realtime voice
provider hears the meeting audio and speaks through the configured audio bridge.
When the realtime model needs deeper reasoning, current information, or normal
OpenClaw tools, it can call `openclaw_agent_consult`.
The consult tool runs the regular OpenClaw agent behind the scenes with recent
meeting transcript context and returns a concise spoken answer to the realtime
voice session. The voice model can then speak that answer back into the meeting.
`realtime.toolPolicy` controls the consult run:
- `safe-read-only`: expose the consult tool and limit the regular agent to
`read`, `web_search`, `web_fetch`, `x_search`, `memory_search`, and
`memory_get`.
- `owner`: expose the consult tool and let the regular agent use the normal
agent 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
can reuse prior consult context during the same meeting.
To force a spoken readiness check after Chrome has fully joined the call:
```bash
openclaw googlemeet speak meet_... "Say exactly: I'm here and listening."
```
For the full join-and-speak smoke:
```bash
openclaw googlemeet test-speech https://meet.google.com/abc-defg-hij \
--transport chrome-node \
--message "Say exactly: I'm here and listening."
```
## Notes
Google Meet's official media API is receive-oriented, so speaking into a Meet
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 realtime mode needs either:
- `chrome.audioInputCommand` plus `chrome.audioOutputCommand`: OpenClaw owns the
realtime model bridge and pipes 8 kHz G.711 mu-law audio between those
commands and the selected realtime voice provider.
- `chrome.audioBridgeCommand`: an external bridge command owns the whole local
audio path and must exit after starting or validating its daemon.
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 realtime audio bridge for a Chrome
session. `googlemeet leave` stops that bridge. For Twilio sessions delegated
through the Voice Call plugin, `leave` also hangs up the underlying voice call.
## Related
- [Voice call plugin](/plugins/voice-call)
- [Talk mode](/nodes/talk)
- [Building plugins](/plugins/building-plugins)