The test I added in #155 ran unconditionally, which broke `npm test` without
credentials — the path husky's pre-commit hook takes, so `npm version patch`
could not commit.
Two causes, both addressed:
- no credential gate, unlike every other vendor test in this file. Now skips
without INWORLD_API_KEY, and closes its redis client on that path so the
run can still exit.
- it assumed streaming was enabled. The Google non-streaming test sets
JAMBONES_DISABLE_TTS_STREAMING and, on its no-credentials skip path,
deletes the env var WITHOUT clearing the require cache (unlike its finally
block, which clears both) — so lib/config still held 'true' further down
the file and synthInworld took the non-streaming branch, attempting a real
vendor call. The test now re-requires with streaming enabled so it does not
depend on what ran before it.
Verified both ways: skips and exits 0 with no key; 11/11 with a key even
under the leaked state. Re-introducing the #155 bug still fails 3 assertions,
so the regression value is intact.
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
The streaming say: path guarded on opts.audioConfig?.pitch and
opts.audioConfig?.speakingRate but interpolated opts.pitch and
opts.speakingRate, which are undefined — so anyone setting them under
audioConfig (what the docs and the portal defaults tell you to do) got
'pitch=undefined,speakingRate=undefined' on the wire and their setting
silently dropped.
Adds a test for the say: params that needs no credentials, since the
streaming branch builds the path without calling the vendor.
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
"23": "^0.0.0" is not a real dependency - it is an empty placeholder
package (0.0.0, no deps, unrelated third-party maintainer) that landed
here from a stray npm install. Nothing in the package references it.
Beyond the noise, it is a small supply-chain liability: a dependency on
a squatted single-number name owned by nobody we know, shipped to every
consumer of speech-utils.
Lint passes; full test suite passes 105/105 with live vendor credentials.
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
undici was declared as a direct dependency but is never required
anywhere in this package - grep across index.js, lib/ and stubs/ finds
no reference to undici, ProxyAgent, setGlobalDispatcher or Dispatcher.
The one HTTP call in lib/synth-audio.js uses the global fetch, and
Azure proxy support goes through the SDK's own setProxy plus the
http_proxy_ip/http_proxy_port params.
Removing it clears all seven open undici advisories from npm audit for
this package and its consumers, and stops speech-utils pulling a
duplicate undici 7.x into trees where the consumer already depends on
undici 8 (feature-server does).
Lint passes; full test suite passes 105/105 with live vendor credentials.
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
The Azure speech SDK was pinned to exactly 1.38.0 since the initial
commit. It pulls in uuid <11.1.1, which carries GHSA-w5hq-g745-h8pq
(missing buffer bounds check in v3/v5/v6 when buf is provided). The
advisory covers Azure SDK 1.14.0-1.50.0; 1.51.0 clears it.
Loosened the exact pin to a caret range so future patches come in
without another PR. The SDK surface used here (SpeechConfig,
SpeechSynthesizer, ResultReason, CancellationDetails,
SpeechSynthesisOutputFormat) is unchanged in 1.51.0.
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Streaming arm returns a say: url for the mediajam dialect; the cache-render
arm posts to /api/post/speech/tts with only_audio and pcm_8000, which is bare
r8 samples and avoids gradium's streaming wav header (0xffffffff RIFF size).
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Streaming goes through mediajam's say: url; the cache render posts to
/tts/bytes for wav, since the service rejects mp3.
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* feat(murf): add Murf.ai TTS support to synthAudio
Add synthMurf() following the rimelabs/cartesia pattern:
- streaming path returns a say:{vendor=murf,...} filePath consumed by the
FreeSWITCH mod_murf_tts module
- non-streaming path calls POST /v1/speech/stream (api-key header) and returns
WAV audio for cache rendering
Register murf in the supported-vendor assert list and the synth switch.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(murf): drop Accept: audio/basic header (caused 406 Not Acceptable)
Murf's /v1/speech/stream rejects an unmatched Accept header with 406; the
response container is chosen by the `format` body field instead. Verified a
WAV request now returns 200 (valid RIFF/WAVE).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore: deprecate and remove verbio, nuance speech vendor support
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore: also deprecate and remove PlayHT speech vendor
PlayHT was acquired and no longer provides the service.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
synthPolly received voice but never emitted it into the say:{...} url
(unlike synthMicrosoft, which includes voice=). Polly's SynthesizeSpeech
requires a VoiceId, so the media-server-native (say:-url) Polly path had no
way to pick a voice. Add voice= to the params.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>