495 Commits
Author SHA1 Message Date
Dave Horton 18917f49ff 1.0.19 v1.0.19 2026-09-29 14:22:33 -04:00
Dave HortonandClaude Opus 5.5 fa705694e1 feat(tts): add kugelaudio as a TTS vendor (#163)
Streaming returns a say: url for the mediajam dialect; the cache render POSTs
/v1/tts/generate for raw 8 kHz PCM (r8). Language is reduced to its primary
subtag and dropped when KugelAudio does not support it.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 14:21:41 -04:00
Dave HortonandClaude Opus 5 89fe0b87e9 fix: stop the RoleArn synth test printing AWS credentials into CI logs (#161)
With renderForCaching unset, synthPolly returns the mediajam streaming params
string, and lib/synth-audio.js:337-340 embeds accessKeyId, secretAccessKey and
sessionToken in it. The test interpolated that value into its assertion message,
so run 32995180996 printed live (1 hour) AWS credentials into a public build log.

Every other synth test in this file passes renderForCaching: true; this one was
the only exception. It now does the same, and the assertion no longer interpolates
opts.filePath at all, so the streaming params string cannot leak this way again.

Latent since the test was written -- it only surfaced once AWS_ROLE_ARN was wired
up and the test actually ran.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 13:44:05 -04:00
Dave HortonandClaude Opus 5 b928820906 ci: authenticate to AWS via GitHub OIDC instead of stored access keys (#160)
* ci: authenticate to AWS via GitHub OIDC instead of stored access keys

CI held a long-lived AWS access key as repository secrets. This replaces it with
a short-lived credential obtained through the GitHub OIDC provider, so no AWS key
is stored in the repo at all.

The tests could not simply inherit the OIDC credentials. They passed
AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY explicitly, which routes
lib/get-aws-sts-token.js down its accessKeyId branch and calls GetSessionToken --
and AWS rejects GetSessionToken when it is called with session credentials.

test/aws-credentials.js centralises the decision. When AWS_SESSION_TOKEN is present
the credentials are temporary and only the region is passed, so the SDK's default
credential provider chain is used. Static keys still work unchanged, which keeps
local run-tests.sh working and also lets it run off an 'aws sso login' session with
no credentials in the file.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* test: supply a cache key when AWS credentials come from the default chain

getAwsAuthToken derives its cache key as roleArn || accessKeyId || speech_credential_sid.
With temporary credentials the test passes none of those, so makeAwsKey received
undefined and hash.update() threw ERR_INVALID_ARG_TYPE.

Production never hits this: the instance-profile path always carries a
speech_credential_sid from the database, which is exactly what the comment above
that line describes. The test now supplies one the same way.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* ci: reference the CI role via secret rather than inlining the account id

speech-utils is public, so the role ARN (and with it the AWS account id) should not
be committed. It now comes from the AWS_ROLE_ARN secret, which also feeds the
'AWS speech synth tests by RoleArn' test -- previously always skipped, so the
AssumeRole credential path had no coverage here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* test: give the AWS RoleArn synth test its own cache key

The RoleArn test synthesized the same vendor/voice/language/text as the plain AWS
synth test that runs before it, so it hit that test's cache entry, servedFromCache
came back true and the !servedFromCache assertion failed.

Latent since the test was written -- it never ran, because AWS_ROLE_ARN was never
supplied. Wiring the secret in activated it and exposed the collision. Distinct text
makes the test independent of execution order.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 13:38:32 -04:00
Dave Horton f4c3c7dc8b 1.0.18 v1.0.18 2026-08-23 13:12:37 -04:00
Dave HortonandClaude Opus 5 a32f71ac5a feat: add fishaudio (Fish Audio) TTS support (#159)
Adds synthFishaudio with both arms: the say: streaming url consumed by the
mediajam dialect, and a POST /v1/tts cache render. The render asks for raw pcm
at 8k and returns extension r8 because fish's wav output carries a placeholder
RIFF size, the same problem gradium has.

Fish is a voice-cloning vendor, so the voice is a reference_id; the sentinel
'default' means send none and use fish's own default voice.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 13:11:59 -04:00
Dave HortonandClaude Opus 5 c8850998b5 fix(tts): use a current nineninesix voice id in the synth test (#158)
The vendor replaced its voice catalog and now rejects unknown ids, so
the env-gated test failed whenever NINENINESIX_API_KEY was set.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 12:19:59 -04:00
Dave Horton e8b2009d29 1.0.17 v1.0.17 2026-08-10 16:45:32 -04:00
Dave HortonandClaude Opus 5 27e07bb551 fix(tts): forward gradium json_config to the streaming path (#157)
synthGradium destructured json_config from options but only forwarded
pronunciation_id into the say: param block, so the setting could never
reach mediajam's streaming dialect — only the cache-render POST. The
say: parser is brace-aware, so a nested json object survives intact.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 16:45:07 -04:00
Dave Horton 33456b93d9 1.0.16 v1.0.16 2026-08-08 23:07:43 -04:00
Dave HortonandClaude Opus 5 70e91b5057 test(inworld): gate the say: param test on INWORLD_API_KEY (#156)
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>
2026-08-08 23:06:52 -04:00
Dave HortonandClaude Opus 5 c066840f85 fix(inworld): read pitch and speakingRate from audioConfig in the say: params (#155)
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>
2026-08-08 22:49:50 -04:00
Dave Horton d89457f67d 1.0.15 v1.0.15 2026-08-06 10:23:47 -04:00
Dave HortonandClaude Opus 5 3c15976669 fix(deps): remove junk "23" dependency (#154)
"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>
2026-08-06 10:23:31 -04:00
Dave Horton 70abb04941 1.0.14 v1.0.14 2026-08-06 10:13:51 -04:00
Dave HortonandClaude Opus 5 724af7fcab fix(deps): drop unused undici dependency (#153)
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>
2026-08-06 10:13:31 -04:00
Dave Horton c0a79090c2 1.0.13 v1.0.13 2026-08-06 10:04:26 -04:00
Dave HortonandClaude Opus 5 7a9a3e2468 fix(deps): bump microsoft-cognitiveservices-speech-sdk to ^1.51.0 (#152)
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>
2026-08-06 10:03:59 -04:00
Dave Horton ab2b3f7fb2 1.0.12 v1.0.12 2026-08-04 09:04:50 -04:00
Dave HortonandClaude Opus 5 e848d0276e feat(tts): add gradium as a TTS vendor (#151)
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>
2026-08-04 09:02:07 -04:00
Dave Horton d2447d477d 1.0.11 v1.0.11 2026-08-03 12:04:47 -04:00
Dave HortonandClaude Opus 5 ec8bbacc2c feat(tts): add nineninesix.ai synthesis (#150)
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>
2026-08-03 12:03:52 -04:00
Dave Horton 9694714a7f 1.0.10 v1.0.10 2026-07-28 20:59:05 -04:00
Hoan Luu Huu 6a6de9b7a1 support google tts configuraiton (#149) 2026-07-28 20:58:26 -04:00
Dave Horton 04d1fb548b 1.0.9 v1.0.9 2026-07-21 06:52:05 -04:00
Hoan Luu Huu bbf0167b40 support deepgramflux tts (#148) 2026-07-21 06:49:38 -04:00
Dave Horton 4cfa286242 1.0.8 v1.0.8 2026-07-05 17:42:09 -04:00
Hoan Luu Huu 42ac63adc6 support xaiTTS (#147) 2026-07-05 17:41:36 -04:00
Dave Horton 12a121672d 1.0.7 v1.0.7 2026-07-03 07:12:20 -04:00
Hoan Luu HuuandClaude Opus 4.8 7b94a5a969 feat(murf): add Murf.ai TTS support to synthAudio (#146)
* 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>
2026-07-03 07:11:39 -04:00
Dave Horton c47b4883c7 1.0.6 v1.0.6 2026-06-17 16:20:30 -04:00
Dave HortonandClaude Opus 4.8 7d076bb8b4 chore: deprecate + remove verbio, nuance, playht speech vendor support (#144)
* 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>
2026-06-17 16:20:00 -04:00
Dave HortonandClaude Opus 4.8 142323a151 fix(synth-audio): include the Polly VoiceId in the aws say: url
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>
2026-06-13 22:31:57 -04:00
Dave Horton d176a644fe 1.0.4 v1.0.4 2026-06-06 10:44:11 +02:00
Hoan Luu Huu 644f2918dc support cartesia sonic3.5 (#143) 2026-06-06 10:42:40 +02:00
Dave HortonandClaude Opus 4.5 4f430b9785 update publish workflow to use actions v4
Fixes npm warning about deprecated always-auth config by updating
setup-node from v3 to v4.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-06-03 08:51:56 -04:00
Dave Horton 18c658d20c 1.0.3 v1.0.3 2026-06-03 07:27:44 -04:00
Dave Horton 062608cf13 update lock file 2026-06-03 07:27:28 -04:00
Hoan Luu Huu a2ea94c14a support rimelabs coda (#142) 2026-06-03 07:24:13 -04:00
Dave Horton 04bb85ef81 1.0.1 v1.0.1 2026-03-25 10:08:57 -04:00
Dave Horton c123f19898 remove ibm speech since it is not used (to my knowledge) and has dependencies with vulnerabilities (#141) 2026-03-25 10:08:30 -04:00
Dave Horton 305695d068 0.2.30 v0.2.30 2026-01-22 08:03:44 -05:00
Dave Horton 1477752d40 update dep 2026-01-22 08:03:24 -05:00
Dave Horton fbedfe947f vuln 2026-01-22 07:58:06 -05:00
Dave Horton ab88facd52 Merge pull request #139 from jambonz/feat/google_gemini_tts
google tts support api_mode
2026-01-22 07:52:04 -05:00
Hoan HL a2be64da89 google tts support api_mode 2026-01-22 16:36:57 +07:00
Dave Horton c5e0f256e6 0.2.28 v0.2.28 2026-01-17 21:39:59 -05:00
Dave Horton d0378751ba Merge pull request #135 from jambonz/feat/gemini_tts
support gemini tts
2026-01-17 21:39:28 -05:00
Hoan HL a7e391fcb2 wip 2026-01-18 08:45:14 +07:00
Hoan HL 10095de25d wip 2026-01-17 16:16:11 +07:00