Skip to content

Gemini 3.1 compatibility: deduplicate tools + upgrade SDK - #259

Merged
sonichi merged 1 commit into
mainfrom
feature/gemini-3.1-quota-investigation
Apr 10, 2026
Merged

sonichi merged 1 commit into
mainfrom
feature/gemini-3.1-quota-investigation

Conversation

@liususan091219

Copy link
Copy Markdown
Collaborator

Summary

  • Deduplicate tool declarations in phone server — getCurrentTimeTool appeared in both anyCallerTools and inlineTools, causing Gemini 3.1 to reject with 1011 "Internal error" (2.5 silently accepted duplicates)
  • Upgrade @google/genai from 1.48.0 to 1.49.0
  • Upgrade bodhi-realtime-agent to commit 26992e35 (sendClientContent → sendRealtimeInput migration)

Root cause

Gemini 3.1 is stricter than 2.5:

  1. Duplicate function declarations → 1011 during WebSocket setup
  2. media_chunks wire format → 1007 (requires audio key in sendRealtimeInput)
  3. sendClientContent for text → 1011 (requires sendRealtimeInput({ text }))

Items 2-3 fixed in bodhi fork. This PR fixes item 1 in sutando.

Depends on

  • sonichi/bodhi_realtime_agent fix/sendAudio-media-to-audio (1-line: media → audio in sendAudio)

Test plan

  • Voice agent on gemini-3.1-flash-live-preview — working
  • Phone agent on 3.1 with dedup — working (test call connected)
  • Reproduced: duplicate tool → 1011; deduplicated → success

🤖 Generated with Claude Code

- Deduplicate tool declarations in phone server: getCurrentTimeTool
  appeared in both anyCallerTools and inlineTools. Gemini 3.1 rejects
  duplicate function declarations with 1011 "Internal error" (2.5
  silently accepted them).
- Upgrade @google/genai from 1.48.0 to 1.49.0
- Upgrade bodhi-realtime-agent to commit 26992e35 (sendClientContent
  → sendRealtimeInput migration for 3.1 text injection)

Verified: voice agent + phone agent both connect and respond on
gemini-3.1-flash-live-preview. Tested with phone call + screen summon.

Requires: sonichi/bodhi_realtime_agent fix/sendAudio-media-to-audio
(changes sendAudio media→audio to avoid deprecated media_chunks wire
format). Without the bodhi fix, audio streaming hits 1007.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

@sonichi sonichi left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed from Mac Mini node. LGTM — the root-cause diagnosis closes yesterday's Gemini 3.1 quota ghost chase. I spent considerable time yesterday thinking the 1011 "exceeded your current quota" error was a billing tier issue; turns out Gemini 3.1 just has strict duplicate-declaration rejection and 2.5 silently accepted dupes. The misleading error text sent us looking in the wrong place.

Verified

  • Duplicate confirmed: getCurrentTimeTool is declared once at src/inline-tools.ts:338, then included in BOTH inlineTools (line 1333) AND anyCallerTools (line 1340). conversation-server at skills/phone-conversation/scripts/conversation-server.ts:517 pushes ...anyCallerTools then later ...inlineTools, so getCurrentTimeTool lands in the agent tool array twice. 3.1 rejects, 2.5 doesn't.
  • Dedup logic is correct — Set-based filter preserves anyCallerTools-first priority (anyCallerTools version wins on conflict).
  • Voice-agent tool list is NOT affected: src/voice-agent.ts uses [workTool, getTaskStatus, ...inlineTools] and workTool/getTaskStatus are defined inline, not in inline-tools.ts. No duplicates. (Grepped src/inline-tools.ts for workTool|getTaskStatus — zero hits.)
  • bodhi bump to 26992e35 matches the merged fork PR #1 from earlier today. Already installed in our node_modules via this morning's npm install.
  • SDK bump 1.48 → 1.49 looks minor, no breaking changes I can see.
  • @cartesia/cartesia-js moved to optionalDependencies in package-lock — that's a benign regen side-effect (PR #239 already moved it to optionalDependencies in package.json, lockfile is catching up).

Concerns (non-blocking)

  1. Depends on bodhi fork PR #2 (liususan091219:fix/sendAudio-media-to-audio) — still OPEN. This PR fixes the duplicate-declaration 1011 but the sendAudio path still uses the media key instead of audio, which 3.1 rejects with 1007. Without bodhi #2 merged + another npm install, voice-agent on 3.1 will fire on the first user audio frame. Worth landing both together OR merging #259 now and documenting that 3.1 still needs bodhi #2 before unpinning .env.

  2. Dedup could live in inline-tools.ts as a phoneToolList export that filters anyCallerTools from inlineTools once. Caller-side dedup repeats the logic at every consumer. Non-blocking — the 1-liner in conversation-server is fine for now.

  3. No tests added for the dedup. A simple unit test asserting no duplicate names in the phone agent tools array would catch regressions if a future inlineTools addition overlaps. Not a blocker, just a nice-to-have.

Post-merge to-do (for whoever lands bodhi #2)

  • Merge bodhi fork #2 → cd ~/Desktop/sutando && npm install github:sonichi/bodhi_realtime_agent → restart voice-agent
  • Unpin VOICE_NATIVE_AUDIO_MODEL in .env to gemini-3.1-flash-live-preview
  • Test full audio-in / audio-out / tool-call / end-session cycle on 3.1

LGTM, merging anytime. Mini can merge it if you want — just say the word.

🤖 Sutando Mac Mini node

sonichi added a commit that referenced this pull request Apr 10, 2026
Scripts the post-merge verification after the 3-PR Gemini 3.1 compat
stack lands:
  - bodhi fork #1 (sendClientContent → sendRealtimeInput text path)
  - bodhi fork #2 (Susan: sendAudio media → audio)
  - bodhi fork #3 (me: sendFile mimeType branching)
  - sutando #259 (Chi: deduplicate tool declarations + SDK bump)

Checks the installed bodhi dist for each wire-format fix using
awk-scoped extraction (simpler grep would false-positive on the
OpenAI realtime transport's sendAudio which also uses `audio:` as a
flat field, so the 3.1 gemini-specific check has to be scoped to the
Gemini transport's function body).

Checks sutando src for the phone conversation-server's tool dedup
comment as a PR #259 marker.

Checks .env for the 2.5 pin (exits green on 2.5, warns on 3.1 — so
the script is safe to run repeatedly before, during, and after the
rollout).

Exits non-zero with "NOT READY for 3.1 rollout" if any wire-format
fix is missing. On success, prints the manual next-steps checklist
for unpinning .env, restarting voice-agent, and running the real
3.1 session test.

Usage:
  bash src/verify-gemini-31.sh              # check current state
  bash src/verify-gemini-31.sh --install    # npm install + check

Tested on the current state: correctly reports PR #1 applied,
PR #2 + #3 + #259 not yet. Will flip to "ready" once those merge
and an `npm install` pulls the updated bodhi SHA.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
@sonichi
sonichi merged commit 46fcffa into main Apr 10, 2026
1 check passed
@sonichi
sonichi deleted the feature/gemini-3.1-quota-investigation branch April 10, 2026 03:12
sonichi added a commit that referenced this pull request Apr 10, 2026
Isolates the second voice-agent-specific 3.1 compat issue discovered
while landing the Gemini 3.1 stack today. Chi's #259 (dedup) +
Susan's bodhi #2 (sendAudio) + my bodhi #3 (sendFile) all merged,
npm installed, and 2.5 continued to work fine — but flipping .env to
gemini-3.1-flash-live-preview still hit 1011 "exceeded your current
quota" within 200ms of WebSocket setup. Misleading error text; the
actual cause turned out to be the googleSearch + native-audio combo.

Experiment: flipped googleSearch: true → false on an investigation
branch, kept everything else identical (.env to 3.1, same dist,
same tool list). Result: setup complete in 315ms, no 1011. Flipped
back to googleSearch: true + 3.1 and the 1011 returned. Reproducible.

So: Gemini 3.1 native audio rejects the googleSearch grounding
tool entry. 2.5 silently accepted it. The rejection manifests as
the same misleading "quota" close code that caught us on #259's
duplicate-declaration bug earlier.

Fix: env-var gate. VOICE_GOOGLE_SEARCH defaults to 'true' to
preserve existing 2.5 behavior. Users unpinning VOICE_NATIVE_AUDIO_MODEL
to 3.1 set VOICE_GOOGLE_SEARCH=false in .env at the same time.
Comment at the declaration explains the constraint and points at
today's investigation.

Voice-agent LOSES google-search grounding on 3.1. Google search was
used for "quick factual lookups" per the system instructions; 3.1
users will need to route those queries through the work tool
(sutando-core) instead. Acceptable trade-off.

Test plan:
- ✓ tsc clean
- ✓ Default (VOICE_GOOGLE_SEARCH unset, 2.5 pinned): setup complete,
  googleSearch: true as before
- ✓ Investigation: VOICE_GOOGLE_SEARCH=false + 3.1 pinned → Gemini
  setup complete in 315ms, no 1011
- Live: user needs to test the full 3.1 session (greeting, tool
  call, voice goodbye, reconnect) before we can call 3.1 "ready"

Follow-up: consider auto-inferring googleSearch=false when
VOICE_NATIVE_AUDIO_MODEL contains "3.1" — would remove the need
for users to set two env vars together. Not doing it here because
the explicit config is more discoverable and easier to revert.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
sonichi added a commit that referenced this pull request Apr 10, 2026
Scripts the post-merge verification after the 3-PR Gemini 3.1 compat
stack lands:
  - bodhi fork #1 (sendClientContent → sendRealtimeInput text path)
  - bodhi fork #2 (Susan: sendAudio media → audio)
  - bodhi fork #3 (me: sendFile mimeType branching)
  - sutando #259 (Chi: deduplicate tool declarations + SDK bump)

Checks the installed bodhi dist for each wire-format fix using
awk-scoped extraction (simpler grep would false-positive on the
OpenAI realtime transport's sendAudio which also uses `audio:` as a
flat field, so the 3.1 gemini-specific check has to be scoped to the
Gemini transport's function body).

Checks sutando src for the phone conversation-server's tool dedup
comment as a PR #259 marker.

Checks .env for the 2.5 pin (exits green on 2.5, warns on 3.1 — so
the script is safe to run repeatedly before, during, and after the
rollout).

Exits non-zero with "NOT READY for 3.1 rollout" if any wire-format
fix is missing. On success, prints the manual next-steps checklist
for unpinning .env, restarting voice-agent, and running the real
3.1 session test.

Usage:
  bash src/verify-gemini-31.sh              # check current state
  bash src/verify-gemini-31.sh --install    # npm install + check

Tested on the current state: correctly reports PR #1 applied,
PR #2 + #3 + #259 not yet. Will flip to "ready" once those merge
and an `npm install` pulls the updated bodhi SHA.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
sonichi added a commit that referenced this pull request Apr 10, 2026
Fixes a false positive in the voice-transport probe (#258). Observed
live 2026-04-09 at 03:30: voice-agent sat idle for ~10 minutes, then:

  03:29:46 [VoiceSession] GoAway from Gemini (timeLeft=50s)
  03:30:46 [VoiceSession] Transport closed (state=ACTIVE code=1011
    reason="The service is currently unavailable.")
  03:30:46 [VoiceSession] Gemini disconnected — will reconnect fresh
    when client connects

That's Google's normal idle-timeout path: Gemini Live fires a GoAway
60s warning, then closes the transport with code=1011 and a
"service is currently unavailable" reason. The bodhi session
transitions to CLOSED and waits for the next client connect. When a
client comes back, `handleClientConnected` reconnects fresh. Not a
failure, just the expected lifecycle for an idle voice-agent.

The probe was flagging every 1011-after-idle as `fail` with
"unrecovered transport close" — which trains users to ignore the
signal exactly when we need them to trust it (real 1011s from #259
duplicate-tool or #262 googleSearch misconfig get lost in the noise).

Fix: track a `goaway_before_close` flag that flips true on GoAway
and false on setup complete. Abnormal closes while the flag is
true get reclassified as expected idle-timeouts (not flagged). The
flag also clears on setup complete so a RECOVERED idle timeout
followed by a fresh unrelated 1011 still correctly fails.

Test plan:
- ✓ Live probe on current log (has idle-timeout 1011 from 03:30):
  voice-transport ok
- ✓ 4-case simulated-log harness:
  1. GoAway → 1011 "service unavailable" unrecovered → ok (was fail)
  2. Unrecovered 1011 quota, no GoAway preceding → fail (as before)
  3. GoAway → 1011 → setup complete (reconnected) → ok
  4. Recovered idle-timeout + fresh 1011 with no new GoAway → fail
     (proves the flag correctly resets on setup complete)

🤖 Generated with [Claude Code](https://claude.com/claude-code)
sonichi added a commit that referenced this pull request Apr 10, 2026
PR #262 branched before #259 merged, so the branch's package-lock
still had @google/genai@1.48 while package.json had 1.49 (inherited
from the rebase pull). CI failed with:

  npm error Invalid: lock file's @google/genai@1.48.0 does not
  satisfy @google/genai@1.49.0

Regenerated lockfile via `npm install` (no args). No other changes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
sonichi added a commit that referenced this pull request Apr 10, 2026
)

Fixes a false positive in the voice-transport probe (#258). Observed
live 2026-04-09 at 03:30: voice-agent sat idle for ~10 minutes, then:

  03:29:46 [VoiceSession] GoAway from Gemini (timeLeft=50s)
  03:30:46 [VoiceSession] Transport closed (state=ACTIVE code=1011
    reason="The service is currently unavailable.")
  03:30:46 [VoiceSession] Gemini disconnected — will reconnect fresh
    when client connects

That's Google's normal idle-timeout path: Gemini Live fires a GoAway
60s warning, then closes the transport with code=1011 and a
"service is currently unavailable" reason. The bodhi session
transitions to CLOSED and waits for the next client connect. When a
client comes back, `handleClientConnected` reconnects fresh. Not a
failure, just the expected lifecycle for an idle voice-agent.

The probe was flagging every 1011-after-idle as `fail` with
"unrecovered transport close" — which trains users to ignore the
signal exactly when we need them to trust it (real 1011s from #259
duplicate-tool or #262 googleSearch misconfig get lost in the noise).

Fix: track a `goaway_before_close` flag that flips true on GoAway
and false on setup complete. Abnormal closes while the flag is
true get reclassified as expected idle-timeouts (not flagged). The
flag also clears on setup complete so a RECOVERED idle timeout
followed by a fresh unrelated 1011 still correctly fails.

Test plan:
- ✓ Live probe on current log (has idle-timeout 1011 from 03:30):
  voice-transport ok
- ✓ 4-case simulated-log harness:
  1. GoAway → 1011 "service unavailable" unrecovered → ok (was fail)
  2. Unrecovered 1011 quota, no GoAway preceding → fail (as before)
  3. GoAway → 1011 → setup complete (reconnected) → ok
  4. Recovered idle-timeout + fresh 1011 with no new GoAway → fail
     (proves the flag correctly resets on setup complete)

🤖 Generated with [Claude Code](https://claude.com/claude-code)
sonichi added a commit that referenced this pull request Apr 10, 2026
…mpat) (#262)

* voice-agent: gate googleSearch on VOICE_GOOGLE_SEARCH env var

Isolates the second voice-agent-specific 3.1 compat issue discovered
while landing the Gemini 3.1 stack today. Chi's #259 (dedup) +
Susan's bodhi #2 (sendAudio) + my bodhi #3 (sendFile) all merged,
npm installed, and 2.5 continued to work fine — but flipping .env to
gemini-3.1-flash-live-preview still hit 1011 "exceeded your current
quota" within 200ms of WebSocket setup. Misleading error text; the
actual cause turned out to be the googleSearch + native-audio combo.

Experiment: flipped googleSearch: true → false on an investigation
branch, kept everything else identical (.env to 3.1, same dist,
same tool list). Result: setup complete in 315ms, no 1011. Flipped
back to googleSearch: true + 3.1 and the 1011 returned. Reproducible.

So: Gemini 3.1 native audio rejects the googleSearch grounding
tool entry. 2.5 silently accepted it. The rejection manifests as
the same misleading "quota" close code that caught us on #259's
duplicate-declaration bug earlier.

Fix: env-var gate. VOICE_GOOGLE_SEARCH defaults to 'true' to
preserve existing 2.5 behavior. Users unpinning VOICE_NATIVE_AUDIO_MODEL
to 3.1 set VOICE_GOOGLE_SEARCH=false in .env at the same time.
Comment at the declaration explains the constraint and points at
today's investigation.

Voice-agent LOSES google-search grounding on 3.1. Google search was
used for "quick factual lookups" per the system instructions; 3.1
users will need to route those queries through the work tool
(sutando-core) instead. Acceptable trade-off.

Test plan:
- ✓ tsc clean
- ✓ Default (VOICE_GOOGLE_SEARCH unset, 2.5 pinned): setup complete,
  googleSearch: true as before
- ✓ Investigation: VOICE_GOOGLE_SEARCH=false + 3.1 pinned → Gemini
  setup complete in 315ms, no 1011
- Live: user needs to test the full 3.1 session (greeting, tool
  call, voice goodbye, reconnect) before we can call 3.1 "ready"

Follow-up: consider auto-inferring googleSearch=false when
VOICE_NATIVE_AUDIO_MODEL contains "3.1" — would remove the need
for users to set two env vars together. Not doing it here because
the explicit config is more discoverable and easier to revert.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

* sync package-lock.json to @google/genai 1.49 from #259

PR #262 branched before #259 merged, so the branch's package-lock
still had @google/genai@1.48 while package.json had 1.49 (inherited
from the rebase pull). CI failed with:

  npm error Invalid: lock file's @google/genai@1.48.0 does not
  satisfy @google/genai@1.49.0

Regenerated lockfile via `npm install` (no args). No other changes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
sonichi added a commit that referenced this pull request Apr 10, 2026
…261)

* Add src/verify-gemini-31.sh — pre-rollout gate for Gemini 3.1 stack

Scripts the post-merge verification after the 3-PR Gemini 3.1 compat
stack lands:
  - bodhi fork #1 (sendClientContent → sendRealtimeInput text path)
  - bodhi fork #2 (Susan: sendAudio media → audio)
  - bodhi fork #3 (me: sendFile mimeType branching)
  - sutando #259 (Chi: deduplicate tool declarations + SDK bump)

Checks the installed bodhi dist for each wire-format fix using
awk-scoped extraction (simpler grep would false-positive on the
OpenAI realtime transport's sendAudio which also uses `audio:` as a
flat field, so the 3.1 gemini-specific check has to be scoped to the
Gemini transport's function body).

Checks sutando src for the phone conversation-server's tool dedup
comment as a PR #259 marker.

Checks .env for the 2.5 pin (exits green on 2.5, warns on 3.1 — so
the script is safe to run repeatedly before, during, and after the
rollout).

Exits non-zero with "NOT READY for 3.1 rollout" if any wire-format
fix is missing. On success, prints the manual next-steps checklist
for unpinning .env, restarting voice-agent, and running the real
3.1 session test.

Usage:
  bash src/verify-gemini-31.sh              # check current state
  bash src/verify-gemini-31.sh --install    # npm install + check

Tested on the current state: correctly reports PR #1 applied,
PR #2 + #3 + #259 not yet. Will flip to "ready" once those merge
and an `npm install` pulls the updated bodhi SHA.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

* verify-gemini-31: check VOICE_GOOGLE_SEARCH pin in .env

Extends the script to enforce the second 3.1 compat requirement
discovered in #262: VOICE_GOOGLE_SEARCH must be 'false' when
VOICE_NATIVE_AUDIO_MODEL is pinned to 3.1. Without it, voice-agent
hits a misleading 1011 close on connect.

Check matrix:
  2.5 + default (googleSearch=true)   → pass
  2.5 + VOICE_GOOGLE_SEARCH=false     → warn (unnecessarily losing search)
  3.1 + VOICE_GOOGLE_SEARCH=false     → pass
  3.1 + default (googleSearch=true)   → fail (1011 on connect)
  3.1 + unset VOICE_GOOGLE_SEARCH     → fail (defaults to true → 1011)

Also updates the 'Manual next steps' tail to tell users to set BOTH
env vars together when flipping to 3.1.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
liususan091219 added a commit to liususan091219/sutando-fork that referenced this pull request May 28, 2026
- Deduplicate tool declarations in phone server: getCurrentTimeTool
  appeared in both anyCallerTools and inlineTools. Gemini 3.1 rejects
  duplicate function declarations with 1011 "Internal error" (2.5
  silently accepted them).
- Upgrade @google/genai from 1.48.0 to 1.49.0
- Upgrade bodhi-realtime-agent to commit 26992e35 (sendClientContent
  → sendRealtimeInput migration for 3.1 text injection)

Verified: voice agent + phone agent both connect and respond on
gemini-3.1-flash-live-preview. Tested with phone call + screen summon.

Requires: sonichi/bodhi_realtime_agent fix/sendAudio-media-to-audio
(changes sendAudio media→audio to avoid deprecated media_chunks wire
format). Without the bodhi fix, audio streaming hits 1007.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
liususan091219 pushed a commit to liususan091219/sutando-fork that referenced this pull request May 28, 2026
…onichi#263)

Fixes a false positive in the voice-transport probe (sonichi#258). Observed
live 2026-04-09 at 03:30: voice-agent sat idle for ~10 minutes, then:

  03:29:46 [VoiceSession] GoAway from Gemini (timeLeft=50s)
  03:30:46 [VoiceSession] Transport closed (state=ACTIVE code=1011
    reason="The service is currently unavailable.")
  03:30:46 [VoiceSession] Gemini disconnected — will reconnect fresh
    when client connects

That's Google's normal idle-timeout path: Gemini Live fires a GoAway
60s warning, then closes the transport with code=1011 and a
"service is currently unavailable" reason. The bodhi session
transitions to CLOSED and waits for the next client connect. When a
client comes back, `handleClientConnected` reconnects fresh. Not a
failure, just the expected lifecycle for an idle voice-agent.

The probe was flagging every 1011-after-idle as `fail` with
"unrecovered transport close" — which trains users to ignore the
signal exactly when we need them to trust it (real 1011s from sonichi#259
duplicate-tool or sonichi#262 googleSearch misconfig get lost in the noise).

Fix: track a `goaway_before_close` flag that flips true on GoAway
and false on setup complete. Abnormal closes while the flag is
true get reclassified as expected idle-timeouts (not flagged). The
flag also clears on setup complete so a RECOVERED idle timeout
followed by a fresh unrelated 1011 still correctly fails.

Test plan:
- ✓ Live probe on current log (has idle-timeout 1011 from 03:30):
  voice-transport ok
- ✓ 4-case simulated-log harness:
  1. GoAway → 1011 "service unavailable" unrecovered → ok (was fail)
  2. Unrecovered 1011 quota, no GoAway preceding → fail (as before)
  3. GoAway → 1011 → setup complete (reconnected) → ok
  4. Recovered idle-timeout + fresh 1011 with no new GoAway → fail
     (proves the flag correctly resets on setup complete)

🤖 Generated with [Claude Code](https://claude.com/claude-code)
liususan091219 pushed a commit to liususan091219/sutando-fork that referenced this pull request May 28, 2026
…mpat) (sonichi#262)

* voice-agent: gate googleSearch on VOICE_GOOGLE_SEARCH env var

Isolates the second voice-agent-specific 3.1 compat issue discovered
while landing the Gemini 3.1 stack today. Chi's sonichi#259 (dedup) +
Susan's bodhi sonichi#2 (sendAudio) + my bodhi sonichi#3 (sendFile) all merged,
npm installed, and 2.5 continued to work fine — but flipping .env to
gemini-3.1-flash-live-preview still hit 1011 "exceeded your current
quota" within 200ms of WebSocket setup. Misleading error text; the
actual cause turned out to be the googleSearch + native-audio combo.

Experiment: flipped googleSearch: true → false on an investigation
branch, kept everything else identical (.env to 3.1, same dist,
same tool list). Result: setup complete in 315ms, no 1011. Flipped
back to googleSearch: true + 3.1 and the 1011 returned. Reproducible.

So: Gemini 3.1 native audio rejects the googleSearch grounding
tool entry. 2.5 silently accepted it. The rejection manifests as
the same misleading "quota" close code that caught us on sonichi#259's
duplicate-declaration bug earlier.

Fix: env-var gate. VOICE_GOOGLE_SEARCH defaults to 'true' to
preserve existing 2.5 behavior. Users unpinning VOICE_NATIVE_AUDIO_MODEL
to 3.1 set VOICE_GOOGLE_SEARCH=false in .env at the same time.
Comment at the declaration explains the constraint and points at
today's investigation.

Voice-agent LOSES google-search grounding on 3.1. Google search was
used for "quick factual lookups" per the system instructions; 3.1
users will need to route those queries through the work tool
(sutando-core) instead. Acceptable trade-off.

Test plan:
- ✓ tsc clean
- ✓ Default (VOICE_GOOGLE_SEARCH unset, 2.5 pinned): setup complete,
  googleSearch: true as before
- ✓ Investigation: VOICE_GOOGLE_SEARCH=false + 3.1 pinned → Gemini
  setup complete in 315ms, no 1011
- Live: user needs to test the full 3.1 session (greeting, tool
  call, voice goodbye, reconnect) before we can call 3.1 "ready"

Follow-up: consider auto-inferring googleSearch=false when
VOICE_NATIVE_AUDIO_MODEL contains "3.1" — would remove the need
for users to set two env vars together. Not doing it here because
the explicit config is more discoverable and easier to revert.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

* sync package-lock.json to @google/genai 1.49 from sonichi#259

PR sonichi#262 branched before sonichi#259 merged, so the branch's package-lock
still had @google/genai@1.48 while package.json had 1.49 (inherited
from the rebase pull). CI failed with:

  npm error Invalid: lock file's @google/genai@1.48.0 does not
  satisfy @google/genai@1.49.0

Regenerated lockfile via `npm install` (no args). No other changes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
liususan091219 pushed a commit to liususan091219/sutando-fork that referenced this pull request May 28, 2026
…onichi#261)

* Add src/verify-gemini-31.sh — pre-rollout gate for Gemini 3.1 stack

Scripts the post-merge verification after the 3-PR Gemini 3.1 compat
stack lands:
  - bodhi fork sonichi#1 (sendClientContent → sendRealtimeInput text path)
  - bodhi fork sonichi#2 (Susan: sendAudio media → audio)
  - bodhi fork sonichi#3 (me: sendFile mimeType branching)
  - sutando sonichi#259 (Chi: deduplicate tool declarations + SDK bump)

Checks the installed bodhi dist for each wire-format fix using
awk-scoped extraction (simpler grep would false-positive on the
OpenAI realtime transport's sendAudio which also uses `audio:` as a
flat field, so the 3.1 gemini-specific check has to be scoped to the
Gemini transport's function body).

Checks sutando src for the phone conversation-server's tool dedup
comment as a PR sonichi#259 marker.

Checks .env for the 2.5 pin (exits green on 2.5, warns on 3.1 — so
the script is safe to run repeatedly before, during, and after the
rollout).

Exits non-zero with "NOT READY for 3.1 rollout" if any wire-format
fix is missing. On success, prints the manual next-steps checklist
for unpinning .env, restarting voice-agent, and running the real
3.1 session test.

Usage:
  bash src/verify-gemini-31.sh              # check current state
  bash src/verify-gemini-31.sh --install    # npm install + check

Tested on the current state: correctly reports PR sonichi#1 applied,
PR sonichi#2 + sonichi#3 + sonichi#259 not yet. Will flip to "ready" once those merge
and an `npm install` pulls the updated bodhi SHA.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

* verify-gemini-31: check VOICE_GOOGLE_SEARCH pin in .env

Extends the script to enforce the second 3.1 compat requirement
discovered in sonichi#262: VOICE_GOOGLE_SEARCH must be 'false' when
VOICE_NATIVE_AUDIO_MODEL is pinned to 3.1. Without it, voice-agent
hits a misleading 1011 close on connect.

Check matrix:
  2.5 + default (googleSearch=true)   → pass
  2.5 + VOICE_GOOGLE_SEARCH=false     → warn (unnecessarily losing search)
  3.1 + VOICE_GOOGLE_SEARCH=false     → pass
  3.1 + default (googleSearch=true)   → fail (1011 on connect)
  3.1 + unset VOICE_GOOGLE_SEARCH     → fail (defaults to true → 1011)

Also updates the 'Manual next steps' tail to tell users to set BOTH
env vars together when flipping to 3.1.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants