google_meet
When to use
The user says any of:
- "join my Meet at "
- "take notes on this meeting"
- "summarize the meeting and send followups"
- "sit in on my standup"
- "be a bot in this call and speak up when X"
Two modes
| Mode |
What the bot does |
transcribe (default) |
Joins, enables captions, scrapes a transcript. Listen-only. |
realtime |
Same as transcribe PLUS speaks into the meeting via OpenAI Realtime. The agent calls meet_say(text) and the bot's voice comes out of the call. |
Pick realtime only when the user actually wants the agent to speak. It costs real money (OpenAI Realtime is pay-per-audio-minute) and requires a virtual audio device set up on the machine running the bot.
Two locations
| Location |
When |
| Local (default) |
Gateway machine runs the Playwright bot directly. |
Remote node (node="<name>") |
Bot runs on a different machine that has a signed-in Chrome and (for realtime) a configured audio bridge. Useful when the gateway runs on a headless Linux box but the user's real signed-in Chrome lives on their Mac. |
Prerequisites the user must handle once
Easiest path — run the built-in installer:
sonic plugins enable google_meet
sonic meet install # pip deps + Chromium (transcribe only)
sonic meet install --realtime # + pulseaudio-utils / brew blackhole+ffmpeg
sonic meet auth # optional; skips guest-lobby wait
sonic meet setup # preflight checks
sonic meet install --realtime prompts before running sudo apt-get (Linux)
or brew install (macOS). Pass --yes to skip the prompt. It will NOT touch
your macOS default-input setting — you have to select BlackHole 2ch in
System Settings yourself before starting a realtime meeting.
Or do it manually:
pip install playwright websockets && python -m playwright install chromium
# For realtime mode, additionally:
# Linux: sudo apt install pulseaudio-utils
# macOS: brew install blackhole-2ch ffmpeg
# → System Settings → Sound → Input → BlackHole 2ch
# Then set OPENAI_API_KEY or SONIC_MEET_REALTIME_KEY in ~/.sonic/.env
For a remote node:
# on the user's Mac (where Chrome is signed in):
pip install playwright websockets && python -m playwright install chromium
sonic plugins enable google_meet
sonic meet node run --display-name my-mac # persistent server
# copy the printed token
# on the gateway:
sonic meet node approve my-mac ws://<mac-ip>:18789 <token>
sonic meet node ping my-mac # confirm reachable
Run sonic meet setup to preflight local prereqs.
Flow
- Join — call
meet_join(url=..., mode=..., node=...). Returns immediately.
- Announce yourself — no auto-consent. Say (in whatever channel the user is watching): "A Sonic agent bot is in this call taking notes."
- Poll —
meet_status() for liveness, meet_transcript(last=20) for recent captions. Don't re-read the whole transcript every turn.
- Speak (realtime only) —
meet_say(text="...") queues text for TTS. The speech lags by ~2s. Don't spam it.
- Leave —
meet_leave() when done, or set duration="30m" on meet_join for auto-leave.
- Follow up — read
meet_transcript() in full, summarize, and use regular tools to send the recap, file issues, schedule followups.
Tool reference
| Tool |
Parameters |
Use |
meet_join |
url, mode?, guest_name?, duration?, headed?, node? |
Start bot |
meet_status |
node? |
Liveness + progress |
meet_transcript |
last?, node? |
Read captions |
meet_leave |
node? |
Close bot |
meet_say |
text, node? |
Speak in realtime meeting |
node? on all tools: pass a registered node name (or "auto" for the sole node) to operate a remote bot instead of a local one. Omit for local.
Important limits
- Captions are only as good as Google Meet's live captions. English-biased, lossy on overlapping speakers.
- Guest mode sits in the lobby until a host admits. Warn the user;
sonic meet auth avoids this.
- Lobby timeout: if the host doesn't admit the bot within 5 minutes (configurable via
SONIC_MEET_LOBBY_TIMEOUT env), the bot leaves and meet_status reports leaveReason: "lobby_timeout".
- One active meeting per install per location. A second
meet_join leaves the first.
- Windows not supported.
- Realtime mode needs a virtual audio device. If the audio bridge setup fails, the bot falls back to transcribe mode and flags it in
meet_status().error.
meet_say requires mode='realtime' on the originating meet_join. Calling it against a transcribe-mode meeting returns a clear error.
- Barge-in is best-effort. When a caption arrives attributed to a real participant while the bot is generating audio, the bot sends
response.cancel to OpenAI Realtime. Captions take ~500ms to show up, so the bot will talk over the first second or so of a human interruption.
Status dict reference
meet_status() returns (subset shown, there are more):
| Key |
Meaning |
inCall |
Past the lobby. False while waiting for admission. |
lobbyWaiting |
Clicked "Ask to join", waiting on host. |
joinAttemptedAt / joinedAt |
Timestamps for lobby-click and actual admission. |
captioning |
Caption observer is installed. |
transcriptLines / lastCaptionAt |
Transcript progress. |
realtime / realtimeReady |
Realtime mode provisioned / WS connected. |
realtimeDevice |
Audio device name the bot is feeding (e.g. sonic_meet_src). |
audioBytesOut / lastAudioOutAt |
How much PCM the OpenAI session has produced. |
lastBargeInAt |
Timestamp of the most recent response.cancel sent. |
leaveReason |
duration_expired, lobby_timeout, denied, page_closed, or null. |
error |
Last error (soft — bot may still be running). |
Transcript location
Local:
$SONIC_HOME/workspace/meetings/<meeting-id>/transcript.txt
Remote node: transcript lives on the node host's disk. Use meet_transcript(node=...) to read it over RPC.
Safety
- URL regex: only
https://meet.google.com/... URLs pass.
- No calendar scanning. No auto-dial.
- Remote nodes use bearer-token auth; tokens are generated on the node (32 hex chars, persisted in
$SONIC_HOME/workspace/meetings/node_token.json) and must be copied to the gateway via sonic meet node approve.
meet_say text is rate-limited by the OpenAI Realtime session; spam-protection is the bot's problem, not yours, but still — don't queue hundreds of lines.
1---2name: google-meet3description: Join a Google Meet call, transcribe live captions, optionally speak in realtime, and do the followup work afterwards. Use when the user asks the agent to sit in on a meeting, take notes, summarize, respond in-call, or action items from it.4---56# google_meet78## When to use910The user says any of:1112- "join my Meet at <url>"13- "take notes on this meeting"14- "summarize the meeting and send followups"15- "sit in on my standup"16- "be a bot in this call and speak up when X"1718## Two modes1920| Mode | What the bot does |21|---|---|22| `transcribe` (default) | Joins, enables captions, scrapes a transcript. Listen-only. |23| `realtime` | Same as transcribe PLUS speaks into the meeting via OpenAI Realtime. The agent calls `meet_say(text)` and the bot's voice comes out of the call. |2425Pick `realtime` only when the user actually wants the agent to speak. It costs real money (OpenAI Realtime is pay-per-audio-minute) and requires a virtual audio device set up on the machine running the bot.2627## Two locations2829| Location | When |30|---|---|31| Local (default) | Gateway machine runs the Playwright bot directly. |32| Remote node (`node="<name>"`) | Bot runs on a different machine that has a signed-in Chrome and (for realtime) a configured audio bridge. Useful when the gateway runs on a headless Linux box but the user's real signed-in Chrome lives on their Mac. |3334## Prerequisites the user must handle once3536Easiest path — run the built-in installer:3738```bash39sonic plugins enable google_meet40sonic meet install # pip deps + Chromium (transcribe only)41sonic meet install --realtime # + pulseaudio-utils / brew blackhole+ffmpeg42sonic meet auth # optional; skips guest-lobby wait43sonic meet setup # preflight checks44```4546`sonic meet install --realtime` prompts before running `sudo apt-get` (Linux)47or `brew install` (macOS). Pass `--yes` to skip the prompt. It will NOT touch48your macOS default-input setting — you have to select BlackHole 2ch in49System Settings yourself before starting a realtime meeting.5051Or do it manually:52```bash53pip install playwright websockets && python -m playwright install chromium5455# For realtime mode, additionally:56# Linux: sudo apt install pulseaudio-utils57# macOS: brew install blackhole-2ch ffmpeg58# → System Settings → Sound → Input → BlackHole 2ch59# Then set OPENAI_API_KEY or SONIC_MEET_REALTIME_KEY in ~/.sonic/.env60```6162For a remote node:63```bash64# on the user's Mac (where Chrome is signed in):65pip install playwright websockets && python -m playwright install chromium66sonic plugins enable google_meet67sonic meet node run --display-name my-mac # persistent server68# copy the printed token6970# on the gateway:71sonic meet node approve my-mac ws://<mac-ip>:18789 <token>72sonic meet node ping my-mac # confirm reachable73```7475Run `sonic meet setup` to preflight local prereqs.7677## Flow78791. **Join** — call `meet_join(url=..., mode=..., node=...)`. Returns immediately.802. **Announce yourself** — no auto-consent. Say (in whatever channel the user is watching): "A Sonic agent bot is in this call taking notes."813. **Poll** — `meet_status()` for liveness, `meet_transcript(last=20)` for recent captions. Don't re-read the whole transcript every turn.824. **Speak (realtime only)** — `meet_say(text="...")` queues text for TTS. The speech lags by ~2s. Don't spam it.835. **Leave** — `meet_leave()` when done, or set `duration="30m"` on `meet_join` for auto-leave.846. **Follow up** — read `meet_transcript()` in full, summarize, and use regular tools to send the recap, file issues, schedule followups.8586## Tool reference8788| Tool | Parameters | Use |89|---|---|---|90| `meet_join` | `url`, `mode?`, `guest_name?`, `duration?`, `headed?`, `node?` | Start bot |91| `meet_status` | `node?` | Liveness + progress |92| `meet_transcript` | `last?`, `node?` | Read captions |93| `meet_leave` | `node?` | Close bot |94| `meet_say` | `text`, `node?` | Speak in realtime meeting |9596`node?` on all tools: pass a registered node name (or `"auto"` for the sole node) to operate a remote bot instead of a local one. Omit for local.9798## Important limits99100- Captions are only as good as Google Meet's live captions. English-biased, lossy on overlapping speakers.101- Guest mode sits in the lobby until a host admits. Warn the user; `sonic meet auth` avoids this.102- **Lobby timeout**: if the host doesn't admit the bot within 5 minutes (configurable via `SONIC_MEET_LOBBY_TIMEOUT` env), the bot leaves and `meet_status` reports `leaveReason: "lobby_timeout"`.103- **One active meeting per install per location.** A second `meet_join` leaves the first.104- **Windows not supported.**105- Realtime mode needs a virtual audio device. If the audio bridge setup fails, the bot falls back to transcribe mode and flags it in `meet_status().error`.106- `meet_say` requires `mode='realtime'` on the originating `meet_join`. Calling it against a transcribe-mode meeting returns a clear error.107- **Barge-in is best-effort.** When a caption arrives attributed to a real participant while the bot is generating audio, the bot sends `response.cancel` to OpenAI Realtime. Captions take ~500ms to show up, so the bot will talk over the first second or so of a human interruption.108109## Status dict reference110111`meet_status()` returns (subset shown, there are more):112113| Key | Meaning |114|---|---|115| `inCall` | Past the lobby. False while waiting for admission. |116| `lobbyWaiting` | Clicked "Ask to join", waiting on host. |117| `joinAttemptedAt` / `joinedAt` | Timestamps for lobby-click and actual admission. |118| `captioning` | Caption observer is installed. |119| `transcriptLines` / `lastCaptionAt` | Transcript progress. |120| `realtime` / `realtimeReady` | Realtime mode provisioned / WS connected. |121| `realtimeDevice` | Audio device name the bot is feeding (e.g. `sonic_meet_src`). |122| `audioBytesOut` / `lastAudioOutAt` | How much PCM the OpenAI session has produced. |123| `lastBargeInAt` | Timestamp of the most recent `response.cancel` sent. |124| `leaveReason` | `duration_expired`, `lobby_timeout`, `denied`, `page_closed`, or null. |125| `error` | Last error (soft — bot may still be running). |126127## Transcript location128129Local:130```131$SONIC_HOME/workspace/meetings/<meeting-id>/transcript.txt132```133134Remote node: transcript lives on the node host's disk. Use `meet_transcript(node=...)` to read it over RPC.135136## Safety137138- URL regex: only `https://meet.google.com/...` URLs pass.139- No calendar scanning. No auto-dial.140- Remote nodes use bearer-token auth; tokens are generated on the node (32 hex chars, persisted in `$SONIC_HOME/workspace/meetings/node_token.json`) and must be copied to the gateway via `sonic meet node approve`.141- `meet_say` text is rate-limited by the OpenAI Realtime session; spam-protection is the bot's problem, not yours, but still — don't queue hundreds of lines.