Skip to content

fix(google): bound TTS success JSON response reads#96984

Merged
vincentkoc merged 1 commit into
openclaw:mainfrom
mushuiyu886:fix/followup-96970
Jun 27, 2026
Merged

fix(google): bound TTS success JSON response reads#96984
vincentkoc merged 1 commit into
openclaw:mainfrom
mushuiyu886:fix/followup-96970

Conversation

@mushuiyu886

@mushuiyu886 mushuiyu886 commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Origin / follow-up

Follow-up to #96970 and the same bounded-reader fan-out strategy from #96505. #96970 applied the shared provider JSON cap to ClickClack REST success responses; this PR applies the same source-of-truth reader to Google Gemini TTS success JSON responses.

Summary

  • Route Google Gemini TTS success JSON parsing through readProviderJsonResponse.
  • Keep the existing Google TTS request construction, retry behavior, voice/model selection, and PCM extraction unchanged.
  • Add regression coverage for oversized Gemini TTS success JSON stream cancellation.
  • Add loopback HTTP proof that exercises real postJsonRequest, real fetch, and real readProviderJsonResponse for both a normal small response and an oversized success body.

Maintainer-ready fields

  • Behavior or issue addressed: Google Gemini TTS success responses crossed the provider HTTP boundary through raw response.json() instead of OpenClaw's shared bounded provider JSON reader.
  • Fix classification: Root cause fix
  • Root cause: Root cause source: the Google TTS success-response parser bypassed the canonical provider response-size contract because synthesizeGoogleTtsPcmOnce() called raw await res.json() after postJsonRequest returned a successful Response.
  • Why this is root-cause fix: The Google TTS PCM extraction path has one success JSON parse point, so moving that parse point onto readProviderJsonResponse restores the response-size invariant before downstream audio payload extraction and retry handling.
  • Real environment tested: A deterministic loopback HTTP server was exercised through real postJsonRequest, real fetch, and real readProviderJsonResponse. The proof covers a normal small Google TTS-style JSON envelope and an oversized success JSON stream that closes before the full 18 MiB response is sent.
  • Exact steps or command run after this patch: Ran the Google speech provider Vitest target, ran pnpm check:test-types, then ran the loopback proof with pnpm --dir /media/vdb/code/ai/aispace/openclaw-worktrees/pr-96984 exec tsx /media/vdb/code/ai/aispace/openclaw-pr-96984-evidence/google-tts-loopback-proof.ts.
  • Evidence after fix: The Google speech provider test file passed with 18 tests; typechecking passed; the loopback proof returned small_status=200, small_pcm_bytes=4, and oversized_error=Google TTS response: JSON response exceeds 16777216 bytes.
  • Observed result after fix: The small loopback response parsed normally into the existing inline audio payload shape, while the oversized success response failed at the shared 16 MiB cap and the loopback server observed early stream close at 16,908,288 bytes, before the 18 MiB response completed.
  • What was not tested: External Google account setup, model availability, and hosted speech synthesis semantics remain outside this patch; request construction, endpoint trust validation, model/voice selection, PCM extraction, and audio conversion are unchanged.
  • Why it matters / User impact: Oversized Google TTS success responses now fail deterministically at the shared provider JSON cap instead of being materialized by native JSON parsing before PCM extraction.
  • What did NOT change: Google TTS auth headers, trusted base URL validation, request body shape, retry count, model/voice normalization, PCM extraction, WAV wrapping, Opus transcoding behavior, and persisted runtime data formats are unchanged.
  • Architecture / source-of-truth check: The canonical source-of-truth for provider response-size enforcement is openclaw/plugin-sdk/provider-http; this change moves Google TTS success JSON parsing onto that shared contract before downstream typed response projection.
  • Target test file: extensions/google/speech-provider.test.ts.
  • Scenario locked in: A real Response with a ReadableStream body larger than 16 MiB is returned to the Google TTS success path; the reader rejects at the cap and cancels the stream on both retry attempts.
  • Why this is the smallest reliable guardrail: One parse-site change in synthesizeGoogleTtsPcmOnce() covers the only Google TTS success JSON path without touching provider configuration, request generation, audio extraction, or retry policy.
  • Risk labels considered: merge-risk: 🚨 compatibility, behavior-change: oversized-provider-response, and auth-provider.
  • Risk explanation: The compatibility change is limited to Google TTS success JSON bodies above the shared 16 MiB cap; auth, endpoint trust, schema expectations, and audio conversion behavior are unchanged.
  • Why acceptable: Normal Gemini TTS JSON envelopes should be far below 16 MiB, and early rejection matches the provider response-body policy used by fix(openrouter): bound video catalog JSON reads #96505 and fix(clickclack): bound REST success JSON response reads #96970.
  • Maintainer-ready confidence: High for the code change and loopback proof; maintainers still need to explicitly accept the 16 MiB compatibility boundary before merge.
  • Patch quality notes: Focused three-file diff, no repository evidence files, no unrelated cleanup, RED failure captured raw response.json() behavior, GREEN verification covers target behavior and test typechecking, and loopback proof covers the requested HTTP boundary.

What Problem This Solves

Google TTS already uses the shared provider HTTP request path, but its successful Gemini response body was still parsed with raw native JSON parsing. This left the success response path outside the bounded-reader guardrail used for provider JSON responses elsewhere.

Root Cause

The Google TTS implementation bounded provider errors through the shared HTTP stack but parsed successful JSON locally with await res.json(). That split the response-body contract: unsuccessful responses were normalized through shared helpers, while successful TTS JSON bypassed the shared size cap before audio payload extraction.

Why it matters / User impact

A too-large Gemini TTS success response can otherwise be buffered by native JSON parsing before OpenClaw has a chance to reject it. The patch makes the failure deterministic and closes the body stream once the shared cap is exceeded.

What did NOT change

  • Google TTS model and voice defaults are unchanged.
  • Google trusted base URL validation and request header construction are unchanged.
  • PCM extraction, WAV wrapping, Opus transcoding, and retry count are unchanged.
  • No new Google-specific response-size constant was introduced.
  • No persisted runtime data model or migration path changes were introduced; the helper change is limited to provider test mocks.

Architecture / source-of-truth check

OpenClaw already owns provider response-size enforcement in openclaw/plugin-sdk/provider-http. Google TTS now uses that shared reader for successful Gemini JSON responses instead of maintaining a local native JSON parse path.

Target test file

extensions/google/speech-provider.test.ts

Scenario locked in

The regression returns a real Response with a ReadableStream body of 16 * 1024 * 1024 + 1 bytes to the Google TTS success path. The json() method is a sentinel: the test fails on the old code when the sentinel is called, and passes after the code reads the stream through the bounded reader.

Evidence

RED observation against the raw response.json() implementation:

AssertionError: expected [Function] to throw error including 'Google TTS response: JSON response ex…' but got 'unbounded json reader was used'

Expected: "Google TTS response: JSON response exceeds 16777216 bytes"
Received: "unbounded json reader was used"

GREEN target test:

Test Files  1 passed (1)
Tests  18 passed (18)

Test typecheck:

$ pnpm tsgo:test
$ pnpm tsgo:core:test && pnpm tsgo:extensions:test
$ node scripts/run-tsgo.mjs -p test/tsconfig/tsconfig.core.test.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/core-test.tsbuildinfo
$ node scripts/run-tsgo.mjs -p test/tsconfig/tsconfig.extensions.test.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/extensions-test.tsbuildinfo

Loopback HTTP proof

The follow-up proof uses a local loopback HTTP server and sends requests through real postJsonRequest with real fetch, then parses the returned Response through real readProviderJsonResponse.

The small path returns a Google TTS-style JSON envelope with inline PCM audio data. The oversized path streams an 18 MiB success response and records where the server sees the client close the connection.

Command:

pnpm --dir /media/vdb/code/ai/aispace/openclaw-worktrees/pr-96984 exec tsx /media/vdb/code/ai/aispace/openclaw-pr-96984-evidence/google-tts-loopback-proof.ts

Observed output:

small_status=200
small_mime=audio/L16;codec=pcm;rate=24000
small_pcm_bytes=4
oversized_error=Google TTS response: JSON response exceeds 16777216 bytes
oversized_closed_bytes=16908288
oversized_exceeded_cap=true
oversized_closed_before_full_response=true
small_requests=1
oversized_requests=1

This directly covers the requested loopback proof: normal small response parsing succeeds, the oversized success body fails with the bounded-reader error, and the server observes early close before the full 18 MiB response is sent.

Real behavior proof

The unit regression exercises the actual Google speech provider code path after postJsonRequest returns a successful Response. The follow-up loopback proof additionally exercises the real HTTP request boundary through postJsonRequest, real fetch, and readProviderJsonResponse.

Google TTS response: JSON response exceeds 16777216 bytes
cancelCount = 2
release calls = 2
small_status=200
small_pcm_bytes=4
oversized_closed_before_full_response=true

Review findings addressed

  • RF-001 — fixed: Added loopback HTTP proof that shows both normal small Google TTS-style response parsing and oversized success JSON early close through real postJsonRequest, real fetch, and real readProviderJsonResponse.
  • RF-002 — fixed: The proof now crosses the HTTP boundary through a loopback server instead of stopping at a mocked postJsonRequest unit boundary.
  • RF-003 — fixed: The loopback output includes small_status=200, small_pcm_bytes=4, oversized_error=Google TTS response: JSON response exceeds 16777216 bytes, and oversized_closed_before_full_response=true.
  • RF-004 — maintainer-decision: Existing Google TTS success JSON bodies above the shared 16 MiB cap will now fail early. This is the intentional hardening boundary of the patch and should be explicitly accepted by maintainers before merge.
  • RF-005 — maintainer-decision: The remaining non-code item is maintainer acceptance of the compatibility boundary; the contributor-side proof gap has been addressed with the loopback output above.

External environment disclosure

No external Google account, API credential, or hosted Google TTS synthesis call was used for this follow-up. The completed verification uses the reviewer-requested loopback option and exercises the provider HTTP request path locally through real postJsonRequest, real fetch, and real readProviderJsonResponse.

Verification

pnpm --dir /media/vdb/code/ai/aispace/openclaw-worktrees/followup-96970 exec vitest --config test/vitest/vitest.extension-providers.config.ts run extensions/google/speech-provider.test.ts
pnpm --dir /media/vdb/code/ai/aispace/openclaw-worktrees/followup-96970 check:test-types
pnpm --dir /media/vdb/code/ai/aispace/openclaw-worktrees/pr-96984 exec tsx /media/vdb/code/ai/aispace/openclaw-pr-96984-evidence/google-tts-loopback-proof.ts

Regression Test Plan

  • Target test file: extensions/google/speech-provider.test.ts.
  • Scenario locked in: oversized Gemini TTS success JSON response body crosses the 16 MiB cap.
  • Why this is the smallest reliable guardrail: the test targets the single Google TTS success parse site and verifies both stream cancellation and retry cleanup without changing request construction.

Merge risk

  • Detected category: auth-provider, because the changed production file is the Google provider implementation.
  • Compatibility: Existing Google TTS success JSON bodies above the shared 16 MiB cap now fail early instead of being buffered by native JSON parsing; maintainers should explicitly accept this boundary.
  • Unchanged behavior: Existing auth headers, trusted base URL validation, request payloads, response schema expectations, retry count, and audio conversion behavior are unchanged.
  • Related open PR scan: Alix-007 open PR comparison found no extensions/google/speech-provider.ts overlap; fix(speech): bound TTS response reads #96874 covers Volcengine/Xiaomi TTS, fix(vydra): bound control response reads #96875 covers Vydra speech, and fix(feishu,browser,msteams,azure-speech,bedrock-mantle,googlechat,huggingface,perplexity): bound JSON response reads #96620 covers GoogleChat/Azure Speech rather than Google Gemini TTS.
  • Out-of-scope boundary: This PR does not change Google account setup, provider configuration defaults, endpoint allowlists, model selection, voice selection, speech synthesis semantics, or audio transcoding.

Risk labels considered

  • merge-risk: 🚨 compatibility
  • behavior-change: oversized-provider-response
  • auth-provider detected and explained above

Risk explanation

The only intended behavior change is that Google TTS success JSON bodies over the shared provider cap now fail early with a bounded-reader error. Existing valid responses continue into the same extractGoogleSpeechPcm path.

Why acceptable

The patch aligns Google TTS with OpenClaw's shared provider response-body boundary and keeps the change at the only success JSON parse point. The proof covers both the oversized boundary and a normal small response over loopback HTTP, and existing speech-provider tests continue to pass.

Maintainer-ready confidence

High for contributor-side readiness. The root cause is localized, the diff is small, related open PR overlap was checked, fresh CI is green on commit 9ce6c78521924393bfc72560f5a1a3cb9f6dbf9a, and loopback HTTP proof now covers the requested real boundary. The remaining merge decision is maintainer acceptance of the 16 MiB compatibility boundary.

Patch quality notes

  • No unrelated formatting, docs, lockfile, or runtime config changes.
  • The shared test helper now exports readProviderJsonResponse so existing provider tests can exercise production code that imports the shared reader.
  • The test verifies the old raw-JSON path failed before the fix and the bounded-reader path cancels streams after the fix.
  • The loopback proof verifies small-response parsing and oversized-response early close through the provider HTTP boundary.

Competition / linked PR analysis

This PR is intentionally separate from #96970: #96970 covers ClickClack REST success JSON reads, while this PR covers Google Gemini TTS success JSON reads. Related open PR scan found no competing Google Gemini TTS bounded-reader PR.

@clawsweeper

clawsweeper Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed June 27, 2026, 9:57 AM ET / 13:57 UTC.

Summary
The branch routes Google Gemini TTS success JSON parsing through readProviderJsonResponse and extends Google speech/provider HTTP test coverage for oversized success-body cancellation.

PR surface: Source +49, Tests +55. Total +104 across 3 files.

Reproducibility: yes. Current main and the latest release still show the raw res.json() success parser, and the PR body includes RED sentinel output plus loopback after-fix output for the bounded-reader path.

Review metrics: 1 noteworthy metric.

  • Provider JSON cap: 1 success read changed to shared 16 MiB cap. This is the behavior-changing boundary maintainers should notice because oversized Google TTS JSON envelopes now fail early.

Stored data model
Persistent data-model change detected: serialized state: src/plugin-sdk/test-helpers/provider-http-mocks.ts. Confirm migration or upgrade compatibility proof before merge.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🦞 diamond lobster
Patch quality: 🐚 platinum hermit
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • none.

Risk before merge

  • [P1] Merging intentionally changes compatibility for Google TTS success JSON bodies over the shared 16 MiB provider cap: they now fail early instead of being buffered by native JSON parsing; maintainer acceptance is recorded in the PR discussion.

Maintainer options:

  1. Proceed with the accepted Google TTS cap (recommended)
    Merge with the documented boundary that Google TTS success JSON above 16 MiB now fails through the shared provider reader.
  2. Adjust the cap policy before landing
    If maintainers decide valid Gemini TTS success envelopes can exceed 16 MiB, change the cap policy or add a narrow exception before merge.

Next step before merge

  • [P2] No repair job is needed; the remaining work is ordinary exact-head CI/merge-gate completion and maintainer landing.

Security
Cleared: No concrete security or supply-chain concern was found; the diff reuses an existing bounded provider HTTP reader and changes no dependencies, workflows, secrets, lockfiles, or package execution paths.

Review details

Best possible solution:

Land the narrow shared-reader fix once exact-head checks and normal merge gates are green, preserving the maintainer-accepted 16 MiB provider JSON boundary.

Do we have a high-confidence way to reproduce the issue?

Yes. Current main and the latest release still show the raw res.json() success parser, and the PR body includes RED sentinel output plus loopback after-fix output for the bounded-reader path.

Is this the best way to solve the issue?

Yes. This is the best fix shape because it changes the only Google TTS success JSON parse site to the existing SDK source-of-truth reader rather than adding a Google-local parser or cap.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against a7bfc06f4597.

Label changes

Label justifications:

  • P2: This is a normal-priority provider boundary hardening bugfix with limited blast radius to Google TTS success responses.
  • merge-risk: 🚨 compatibility: The PR intentionally rejects Google TTS success JSON bodies over the shared 16 MiB cap that were previously buffered by native JSON parsing.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (live_output): Sufficient: the PR body includes copied loopback HTTP output exercising real postJsonRequest, real fetch, and readProviderJsonResponse for a small success response and an oversized early-close response.
  • proof: sufficient: Contributor real behavior proof is sufficient. Sufficient: the PR body includes copied loopback HTTP output exercising real postJsonRequest, real fetch, and readProviderJsonResponse for a small success response and an oversized early-close response.
Evidence reviewed

PR surface:

Source +49, Tests +55. Total +104 across 3 files.

View PR surface stats
Area Files Added Removed Net
Source 2 50 1 +49
Tests 1 55 0 +55
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 3 105 1 +104

What I checked:

  • Current main still has unbounded Google TTS success JSON parsing: synthesizeGoogleTtsPcmOnce on current main calls extractGoogleSpeechPcm((await res.json())...) after the successful postJsonRequest response, so the central issue is not already solved on main. (extensions/google/speech-provider.ts:506, a7bfc06f4597)
  • Latest release still has the old parser: v2026.6.10 also has the raw res.json() call on the Google TTS success path, so the PR is not obsolete because the fix has not shipped in the latest release. (extensions/google/speech-provider.ts:505, aa69b12d0086)
  • PR head uses the shared bounded reader at the single parse point: At PR head 9890e08b02f0c56ff5b2835733524f8929797eb3, the success path calls readProviderJsonResponse<GoogleGenerateSpeechResponse>(res, "Google TTS response") before existing PCM extraction. (extensions/google/speech-provider.ts:507, 9890e08b02f0)
  • Shared reader contract enforces the provider JSON cap: readProviderJsonResponse defaults to the 16 MiB provider JSON cap and throws the labeled JSON response exceeds ... bytes error via readResponseWithLimit. (src/agents/provider-http-errors.ts:312, a7bfc06f4597)
  • Underlying bounded reader cancels overflowing streams: readResponseWithLimit cancels the body reader when the next chunk would exceed the byte cap, matching the PR's intended early-close behavior. (packages/media-core/src/read-response-with-limit.ts:96, a7bfc06f4597)
  • Regression test covers the old-path sentinel and retry cleanup: The PR adds a real Response/ReadableStream oversized success body, makes json() a sentinel, expects the shared overflow error, and verifies cancellation plus release across the two-attempt retry loop. (extensions/google/speech-provider.test.ts:174, 9890e08b02f0)

Likely related people:

  • vincentkoc: They authored recent Google TTS request-preparation refactoring in extensions/google/speech-provider.ts, co-authored the current-main sibling Google media bounded-reader change, and recorded maintainer-side acceptance for this PR's compatibility boundary. (role: recent Google TTS area contributor and reviewer; confidence: high; commits: 90cf265f29e7, a7bfc06f4597; files: extensions/google/speech-provider.ts, extensions/google/media-understanding-provider.ts)
  • Alix-007: They introduced the current bounded readProviderJsonResponse contract in src/agents/provider-http-errors.ts, which this PR now applies to Google TTS. (role: shared provider JSON reader contributor; confidence: medium; commits: 2592f8a51a4e; files: src/agents/provider-http-errors.ts)
  • mushuiyu886: Beyond authoring this PR, they authored current-main Google media bounded JSON response work in the same provider family, which makes them relevant to the local bounded-reader fan-out history. (role: recent sibling bounded-reader contributor; confidence: medium; commits: a7bfc06f4597, 9890e08b02f0; files: extensions/google/media-understanding-provider.ts, extensions/google/speech-provider.ts)
  • steipete: They touched the Google speech provider's provider-error path and appear repeatedly in the Google TTS and provider HTTP history around the existing res.json() success parser. (role: adjacent Google/provider HTTP history contributor; confidence: medium; commits: a43c1f880757; files: extensions/google/speech-provider.ts, src/agents/provider-http-errors.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. labels Jun 26, 2026
@mushuiyu886

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jun 26, 2026
@vincentkoc vincentkoc self-assigned this Jun 27, 2026
@vincentkoc

Copy link
Copy Markdown
Member

Maintainer review and merge prep done.

Evidence:

  • Reviewed extensions/google/speech-provider.ts, extensions/google/speech-provider.test.ts, and src/plugin-sdk/test-helpers/provider-http-mocks.ts.
  • node scripts/run-vitest.mjs extensions/google/speech-provider.test.ts passed: 1 file / 18 tests.
  • Autoreview with Codex gpt-5 passed cleanly with no accepted/actionable findings.
  • scripts/pr review-validate-artifacts 96984 passed.
  • OPENCLAW_TESTBOX=1 scripts/pr prepare-run 96984 passed with hosted exact-head gates for 9ce6c78521924393bfc72560f5a1a3cb9f6dbf9a.

Maintainer decision accepted: Google TTS success JSON over the shared 16 MiB provider cap now fails early. Normal request construction, auth headers, model/voice normalization, retry count, PCM extraction, WAV wrapping, and Opus transcoding are unchanged.

Known proof gap: no credential-backed live Google TTS call was run; this PR changes the response-body reader boundary, which is covered by the provider test and PR loopback proof.

@vincentkoc
vincentkoc force-pushed the fix/followup-96970 branch from 9ce6c78 to 9890e08 Compare June 27, 2026 13:51
@vincentkoc

Copy link
Copy Markdown
Member

Refresh after mainline sync: #96984 was rebased and revalidated at exact head 9890e08b02f0c56ff5b2835733524f8929797eb3.

Additional evidence:

  • node scripts/run-vitest.mjs extensions/google/speech-provider.test.ts passed after the rebase: 1 file / 18 tests.
  • Hosted CI run 28291162706 completed successfully for 9890e08b02f0c56ff5b2835733524f8929797eb3.
  • OPENCLAW_TESTBOX=1 scripts/pr prepare-run 96984 passed with hosted exact-head gates.

@vincentkoc
vincentkoc merged commit 5ccfc97 into openclaw:main Jun 27, 2026
94 checks passed
@vincentkoc

Copy link
Copy Markdown
Member

Merged via squash.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

extensions: google merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: S status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants