agentvibes 5.9.0 โ†’ 5.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/.agentvibes/config.json +17 -12
  2. package/.agentvibes/install-manifest.json +98 -86
  3. package/.claude/audio/ui/CREDITS.txt +16 -0
  4. package/.claude/audio/ui/bling-success.wav +0 -0
  5. package/.claude/commands/agent-vibes/receiver.md +64 -0
  6. package/.claude/config/audio-effects.cfg +3 -3
  7. package/.claude/config/personality.txt +1 -0
  8. package/.claude/config/reverb-level.txt +1 -1
  9. package/.claude/github-star-reminder.txt +1 -1
  10. package/.claude/hooks/audio-processor.sh +57 -18
  11. package/.claude/hooks/kokoro-installer.sh +117 -0
  12. package/.claude/hooks/kokoro-server.py +219 -0
  13. package/.claude/hooks/kokoro-tts.py +141 -0
  14. package/.claude/hooks/piper-download-voices.sh +39 -16
  15. package/.claude/hooks/piper-installer.sh +11 -6
  16. package/.claude/hooks/piper-voice-manager.sh +6 -6
  17. package/.claude/hooks/play-tts-agentvibes-receiver-for-voiceless-connections.sh +3 -1
  18. package/.claude/hooks/play-tts-elevenlabs.sh +360 -0
  19. package/.claude/hooks/play-tts-kokoro.sh +127 -0
  20. package/.claude/hooks/play-tts-piper.sh +20 -13
  21. package/.claude/hooks/play-tts-ssh-remote.sh +9 -2
  22. package/.claude/hooks/play-tts-termux-ssh.sh +3 -1
  23. package/.claude/hooks/play-tts.sh +78 -2
  24. package/.claude/hooks/provider-commands.sh +71 -22
  25. package/.claude/hooks/provider-manager.sh +15 -7
  26. package/.claude/hooks/voice-manager.sh +6 -0
  27. package/.claude/hooks-windows/kokoro-server.py +219 -0
  28. package/.claude/hooks-windows/kokoro-tts.py +107 -0
  29. package/.claude/hooks-windows/play-tts-kokoro.ps1 +191 -0
  30. package/.claude/hooks-windows/play-tts-windows-piper.ps1 +22 -16
  31. package/.claude/hooks-windows/play-tts.ps1 +29 -1
  32. package/README.md +12 -86
  33. package/RELEASE_NOTES.md +60 -0
  34. package/mcp-server/server.py +17 -7
  35. package/package.json +4 -3
  36. package/src/commands/install-mcp.js +730 -476
  37. package/src/console/app.js +28 -12
  38. package/src/console/audio-env.js +85 -1
  39. package/src/console/navigation.js +4 -0
  40. package/src/console/tabs/agents-tab.js +18 -12
  41. package/src/console/tabs/music-tab.js +7 -25
  42. package/src/console/tabs/receiver-tab.js +13 -13
  43. package/src/console/tabs/settings-tab.js +2 -2
  44. package/src/console/tabs/setup-tab.js +1708 -124
  45. package/src/console/tabs/voices-tab.js +76 -92
  46. package/src/console/widgets/format-utils.js +14 -2
  47. package/src/console/widgets/help-bar.js +55 -0
  48. package/src/console/widgets/personality-picker.js +2 -2
  49. package/src/console/widgets/reverb-picker.js +429 -41
  50. package/src/console/widgets/track-picker.js +60 -51
  51. package/src/i18n/de.js +204 -203
  52. package/src/i18n/en.js +1 -0
  53. package/src/i18n/es.js +204 -203
  54. package/src/i18n/fr.js +204 -203
  55. package/src/i18n/hi.js +204 -203
  56. package/src/i18n/ja.js +204 -203
  57. package/src/i18n/ko.js +204 -203
  58. package/src/i18n/pt.js +204 -203
  59. package/src/i18n/strings.js +54 -54
  60. package/src/i18n/zh-CN.js +204 -203
  61. package/src/installer.js +127 -34
  62. package/src/services/provider-service.js +178 -143
  63. package/src/services/provider-voice-catalog.js +126 -0
  64. package/src/services/tts-engine-service.js +53 -4
  65. package/src/utils/audio-duration-validator.js +341 -298
  66. package/src/utils/list-formatter.js +200 -194
  67. package/src/utils/platform-resolver.js +369 -0
  68. package/src/utils/preview-list-prompt.js +8 -0
  69. package/src/utils/provider-validator.js +79 -9
  70. package/templates/agentvibes-receiver.sh +6 -2
@@ -0,0 +1,107 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ AgentVibes โ€” Kokoro TTS synthesizer
4
+ Synthesizes text to a WAV file using the kokoro-onnx package.
5
+ Prints the output WAV path to stdout.
6
+
7
+ Usage: kokoro-tts.py <text> <voice> <output_path> [speed]
8
+
9
+ Voices (examples):
10
+ af_heart af_nova af_sky af_bella af_sarah af_nicole
11
+ am_adam am_michael
12
+ bf_emma bf_isabella
13
+ bm_george bm_lewis
14
+
15
+ Install: pip install kokoro-onnx soundfile numpy
16
+ """
17
+
18
+ import sys
19
+ import os
20
+
21
+ def main():
22
+ if len(sys.argv) < 4:
23
+ print("Usage: kokoro-tts.py <text> <voice> <output_path> [speed]", file=sys.stderr)
24
+ sys.exit(1)
25
+
26
+ text = sys.argv[1]
27
+ voice = sys.argv[2]
28
+ output_path = sys.argv[3]
29
+ try:
30
+ speed = float(sys.argv[4]) if len(sys.argv) > 4 else 1.0
31
+ except ValueError:
32
+ speed = 1.0
33
+
34
+ # Determine language code from voice prefix
35
+ # af/am = American English ('a'), bf/bm = British English ('b')
36
+ # jf/jm = Japanese ('j'), kf/km = Korean ('k'), etc.
37
+ prefix = voice[:2].lower() if len(voice) >= 2 else 'af'
38
+ lang_map = {
39
+ 'af': 'a', 'am': 'a', # American English
40
+ 'bf': 'b', 'bm': 'b', # British English
41
+ 'jf': 'j', 'jm': 'j', # Japanese
42
+ 'kf': 'k', 'km': 'k', # Korean
43
+ 'zf': 'z', 'zm': 'z', # Mandarin
44
+ 'ff': 'f', 'fm': 'f', # French
45
+ 'hf': 'h', 'hm': 'h', # Hindi
46
+ 'if': 'i', 'im': 'i', # Italian
47
+ 'pf': 'p', 'pm': 'p', # Brazilian Portuguese
48
+ 'ef': 'e', 'em': 'e', # Spanish (Spain)
49
+ 'nf': 'n', 'nm': 'n', # Spanish (Latin America)
50
+ }
51
+ lang_code = lang_map.get(prefix, 'a')
52
+
53
+ try:
54
+ from kokoro import KPipeline
55
+ except ImportError:
56
+ print("โŒ kokoro-onnx not installed. Run: pip install kokoro-onnx soundfile numpy", file=sys.stderr)
57
+ sys.exit(2)
58
+
59
+ try:
60
+ import numpy as np
61
+ import soundfile as sf
62
+ except ImportError:
63
+ print("โŒ soundfile/numpy not installed. Run: pip install soundfile numpy", file=sys.stderr)
64
+ sys.exit(2)
65
+
66
+ try:
67
+ pipeline = KPipeline(lang_code=lang_code)
68
+ generator = pipeline(text, voice=voice, speed=speed)
69
+
70
+ all_samples = []
71
+ sample_rate = 24000 # kokoro always outputs 24 kHz
72
+
73
+ for result in generator:
74
+ # kokoro >=0.9 yields a KPipeline.Result whose waveform is the
75
+ # `.audio` attribute (a torch.Tensor); older builds yield a
76
+ # (graphemes, phonemes, audio) tuple. Prefer `.audio`, then fall
77
+ # back to the last tuple element.
78
+ audio = getattr(result, "audio", None)
79
+ if audio is None:
80
+ try:
81
+ audio = result[-1]
82
+ except (TypeError, IndexError, KeyError):
83
+ audio = None
84
+ if audio is None:
85
+ continue
86
+ # Tensor -> numpy, force 1-D so np.concatenate never sees a 0-d array
87
+ if hasattr(audio, "detach"):
88
+ audio = audio.detach().cpu().numpy()
89
+ audio = np.asarray(audio).reshape(-1)
90
+ if audio.size > 0:
91
+ all_samples.append(audio)
92
+
93
+ if not all_samples:
94
+ print("โŒ Kokoro returned no audio samples", file=sys.stderr)
95
+ sys.exit(3)
96
+
97
+ combined = np.concatenate(all_samples)
98
+ sf.write(output_path, combined, sample_rate)
99
+ print(output_path)
100
+
101
+ except Exception as e:
102
+ print(f"โŒ Kokoro synthesis failed: {e}", file=sys.stderr)
103
+ sys.exit(3)
104
+
105
+
106
+ if __name__ == '__main__':
107
+ main()
@@ -0,0 +1,191 @@
1
+ #
2
+ # File: .claude/hooks-windows/play-tts-kokoro.ps1
3
+ #
4
+ # AgentVibes - Windows Kokoro TTS Provider
5
+ # High-quality local neural TTS via the `kokoro` Python package (KPipeline).
6
+ # Synthesizes text to a WAV file with kokoro-tts.py and emits the path so the
7
+ # parent play-tts.ps1 router coordinates playback / effects / background music.
8
+ #
9
+ # Contract (mirrors play-tts-piper.ps1):
10
+ # - Receives $Text and optional $VoiceOverride (a Kokoro voice id, e.g. af_heart)
11
+ # - Synthesizes a WAV, optionally applies per-agent effects via audio-processor.ps1
12
+ # - Does NOT play audio; emits the final WAV path via Write-Output for the parent
13
+ #
14
+ # Install deps: pip install kokoro soundfile numpy
15
+ #
16
+
17
+ param(
18
+ [Parameter(Mandatory = $true)]
19
+ [string]$Text,
20
+
21
+ [Parameter(Mandatory = $false)]
22
+ [string]$VoiceOverride
23
+ )
24
+
25
+ # --- Configuration paths (mirror play-tts-piper.ps1) -------------------------
26
+ $ScriptPath = Split-Path -Parent $MyInvocation.MyCommand.Path
27
+ $ProjectClaudeDir = Join-Path (Split-Path -Parent (Split-Path -Parent $ScriptPath)) ".claude"
28
+ if (Test-Path $ProjectClaudeDir) { $ClaudeDir = $ProjectClaudeDir } else { $ClaudeDir = "$env:USERPROFILE\.claude" }
29
+
30
+ $AudioDir = "$ClaudeDir\audio"
31
+ $ConfigDir = "$ClaudeDir\config"
32
+ if (-not (Test-Path $AudioDir)) { New-Item -ItemType Directory -Path $AudioDir -Force | Out-Null }
33
+
34
+ # --- Locate the kokoro synth helper (lives next to this script) --------------
35
+ $KokoroPy = Join-Path $ScriptPath "kokoro-tts.py"
36
+ if (-not (Test-Path $KokoroPy)) {
37
+ Write-Host "[ERROR] kokoro-tts.py not found at: $KokoroPy" -ForegroundColor Red
38
+ exit 1
39
+ }
40
+
41
+ # --- Resolve the Python executable -------------------------------------------
42
+ # SSH-receiver watcher sessions can inherit a minimal PATH; refresh from the
43
+ # registry if the first lookup fails (same pattern as play-tts-piper.ps1).
44
+ function Find-Python {
45
+ foreach ($cand in @("python", "python3", "py")) {
46
+ $c = Get-Command $cand -ErrorAction SilentlyContinue
47
+ if ($c) { return $c.Source }
48
+ }
49
+ return $null
50
+ }
51
+ $PythonExe = Find-Python
52
+ if (-not $PythonExe) {
53
+ $env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" +
54
+ [System.Environment]::GetEnvironmentVariable("Path", "User")
55
+ $PythonExe = Find-Python
56
+ }
57
+ if (-not $PythonExe) {
58
+ Write-Host "[ERROR] Python not found. Install Python 3, then: pip install kokoro soundfile numpy" -ForegroundColor Red
59
+ exit 1
60
+ }
61
+
62
+ # --- Determine the Kokoro voice ----------------------------------------------
63
+ $VoiceName = ""
64
+ if ($VoiceOverride) {
65
+ $VoiceName = $VoiceOverride.Trim()
66
+ } else {
67
+ $VoiceFile = "$ClaudeDir\tts-voice-kokoro.txt"
68
+ if (-not (Test-Path $VoiceFile)) { $VoiceFile = "$ClaudeDir\tts-voice.txt" }
69
+ if (Test-Path $VoiceFile) { $VoiceName = (Get-Content $VoiceFile -Raw).Trim() }
70
+ }
71
+ # Kokoro voice ids look like af_heart, am_michael, bf_emma. Strip any ::display suffix.
72
+ if ($VoiceName -match '::') { $VoiceName = ($VoiceName -split '::')[0] }
73
+ if (-not $VoiceName) { $VoiceName = "af_heart" }
74
+
75
+ # Security: validate voice id (lowercase prefix + underscore + name).
76
+ # Rejects path-traversal / injection before it reaches the command line.
77
+ if ($VoiceName -notmatch '^[a-z]{2}_[a-z0-9_]+$') {
78
+ Write-Host "[WARNING] Invalid Kokoro voice '$VoiceName' - falling back to af_heart" -ForegroundColor Yellow
79
+ $VoiceName = "af_heart"
80
+ }
81
+
82
+ # --- Optional speed ----------------------------------------------------------
83
+ $Speed = "1.0"
84
+ $SpeedFile = "$ConfigDir\tts-speed.txt"
85
+ if (Test-Path $SpeedFile) {
86
+ $s = (Get-Content $SpeedFile -Raw).Trim()
87
+ if ($s -match '^\d+(\.\d+)?$') { $Speed = $s }
88
+ }
89
+
90
+ # --- Sanitize text -----------------------------------------------------------
91
+ $Text = $Text -replace '\\', ' '
92
+ $Text = $Text -replace '[{}<>|`~^;]', ''
93
+ $Text = $Text -replace '\s+', ' '
94
+ $Text = $Text.Trim()
95
+ if (-not $Text) { Write-Host "[ERROR] No text to synthesize" -ForegroundColor Red; exit 1 }
96
+
97
+ # --- Output path (random name, no predictable timestamp; issue #130) ----------
98
+ $AudioFile = "$AudioDir\tts-$([System.IO.Path]::GetRandomFileName() -replace '\..*').wav"
99
+
100
+ # --- Persistent daemon configuration -----------------------------------------
101
+ # A resident kokoro-server keeps the model loaded on the GPU so each request is
102
+ # ~2-3s (just synthesis) instead of ~31s (fresh model load + CUDA init per call).
103
+ # Fast path: POST to the daemon. Fallback: auto-start the daemon for next time,
104
+ # and synthesize this one message directly so nothing is dropped while it warms.
105
+ $KokoroPort = 7855
106
+ $KokoroServerPy = Join-Path $ScriptPath "kokoro-server.py"
107
+
108
+ function Test-KokoroServer {
109
+ try {
110
+ $r = Invoke-WebRequest -Uri "http://127.0.0.1:$KokoroPort/health" -TimeoutSec 2 -UseBasicParsing -ErrorAction Stop
111
+ return $r.StatusCode -eq 200
112
+ } catch { return $false }
113
+ }
114
+
115
+ function Start-KokoroServer {
116
+ if (-not (Test-Path $KokoroServerPy)) { return }
117
+ # Prefer pythonw.exe (no console window); fall back to python.exe hidden.
118
+ $pythonw = $PythonExe -replace 'python\.exe$', 'pythonw.exe'
119
+ $exe = if (Test-Path $pythonw) { $pythonw } else { $PythonExe }
120
+ try {
121
+ Start-Process -FilePath $exe -ArgumentList @($KokoroServerPy, "$KokoroPort") `
122
+ -WindowStyle Hidden -ErrorAction Stop | Out-Null
123
+ } catch {
124
+ Write-Host "[WARNING] Could not auto-start kokoro-server: $_" -ForegroundColor Yellow
125
+ }
126
+ }
127
+
128
+ # --- Synthesize --------------------------------------------------------------
129
+ try {
130
+ $usedServer = $false
131
+ if (Test-KokoroServer) {
132
+ Write-Host "[SYNTH] Synthesizing with Kokoro daemon (voice=$VoiceName speed=$Speed)..." -ForegroundColor Cyan
133
+ try {
134
+ $payload = @{ text = $Text; voice = $VoiceName; speed = [double]$Speed; output = $AudioFile } | ConvertTo-Json -Compress
135
+ $resp = Invoke-WebRequest -Uri "http://127.0.0.1:$KokoroPort/synth" -Method POST `
136
+ -Body $payload -ContentType 'application/json' -TimeoutSec 120 -UseBasicParsing -ErrorAction Stop
137
+ if ($resp.StatusCode -eq 200 -and (Test-Path $AudioFile) -and (Get-Item $AudioFile).Length -gt 0) {
138
+ $usedServer = $true
139
+ } else {
140
+ Write-Host "[WARNING] Kokoro daemon returned no audio; falling back to direct synth" -ForegroundColor Yellow
141
+ }
142
+ } catch {
143
+ Write-Host "[WARNING] Kokoro daemon request failed ($_); falling back to direct synth" -ForegroundColor Yellow
144
+ }
145
+ }
146
+
147
+ if (-not $usedServer) {
148
+ # Daemon not up (or failed): start it for subsequent messages, then
149
+ # synthesize this one directly so the current message is never dropped.
150
+ Start-KokoroServer
151
+ Write-Host "[SYNTH] Synthesizing with Kokoro direct (voice=$VoiceName speed=$Speed)..." -ForegroundColor Cyan
152
+ $synthOut = & $PythonExe $KokoroPy $Text $VoiceName $AudioFile $Speed 2>&1
153
+ if (-not (Test-Path $AudioFile) -or (Get-Item $AudioFile).Length -eq 0) {
154
+ Write-Host "[ERROR] Kokoro synthesis failed" -ForegroundColor Red
155
+ if ($synthOut) { $synthOut | ForEach-Object { Write-Host " $_" -ForegroundColor DarkGray } }
156
+ exit 1
157
+ }
158
+ }
159
+
160
+ Write-Host "[OK] Saved to: $AudioFile" -ForegroundColor Green
161
+ Write-Host "[VOICE] Voice used: $VoiceName (Kokoro TTS)" -ForegroundColor Green
162
+
163
+ # Apply per-agent audio effects via audio-processor.ps1, but SKIP when
164
+ # AGENTVIBES_NO_PLAY is set - that means the parent play-tts.ps1 will do its
165
+ # own reverb / background-music post-processing and running effects here too
166
+ # would double-process the audio.
167
+ $ProcessorScript = Join-Path $ScriptPath "audio-processor.ps1"
168
+ $ProcessedFile = $AudioFile
169
+ if (-not $env:AGENTVIBES_NO_PLAY -and (Test-Path $ProcessorScript)) {
170
+ $AgentName = if ($env:AGENTVIBES_AGENT_NAME) { $env:AGENTVIBES_AGENT_NAME } elseif ($env:AGENTVIBES_LLM_KEY) { $env:AGENTVIBES_LLM_KEY } else { "default" }
171
+ $EffectedFile = "$AudioFile.effected.wav"
172
+ try {
173
+ & $ProcessorScript $AudioFile $AgentName $EffectedFile
174
+ if ((Test-Path $EffectedFile) -and (Get-Item $EffectedFile).Length -gt 0) {
175
+ $ProcessedFile = $EffectedFile
176
+ Write-Host "[EFFECTS] Audio effects applied" -ForegroundColor Cyan
177
+ }
178
+ } catch {
179
+ Write-Host "[WARNING] Audio effects processing skipped: $_" -ForegroundColor Yellow
180
+ }
181
+ }
182
+
183
+ # Emit the final path for the parent router to coordinate playback.
184
+ # DO NOT play here - play-tts.ps1 owns all audio playback.
185
+ Write-Host "[OUTPUT] Processed audio: $ProcessedFile" -ForegroundColor Gray
186
+ Write-Output $ProcessedFile
187
+ }
188
+ catch {
189
+ Write-Host "[ERROR] Error running Kokoro: $_" -ForegroundColor Red
190
+ exit 1
191
+ }
@@ -62,15 +62,20 @@ if (-not $VoiceName) {
62
62
  }
63
63
 
64
64
  # Security: Validate voice name to prevent path traversal
65
- # Only allow alphanumeric, underscore, hyphen, and period
66
- if ($VoiceName -notmatch '^[a-zA-Z0-9_\-\.]+$') {
65
+ # Format: <model> or <model>::<speaker> for multi-speaker Piper models
66
+ # Only allow alphanumeric, underscore, hyphen, period, and the :: separator
67
+ if ($VoiceName -notmatch '^[a-zA-Z0-9_\-\.]+(::[a-zA-Z0-9_\-\.]+)?$') {
67
68
  Write-Host "[ERROR] Invalid voice name: $VoiceName" -ForegroundColor Red
68
69
  exit 1
69
70
  }
70
71
 
72
+ # Extract model name and optional speaker (multi-speaker format: model::Speaker)
73
+ $VoiceModel = ($VoiceName -split '::')[0]
74
+ $SpeakerName = if ($VoiceName -match '::(.+)$') { $Matches[1] } else { "" }
75
+
71
76
  # Resolve voice model path and validate it stays within VoicesDir
72
- $VoiceModelFile = [System.IO.Path]::GetFullPath("$VoicesDir\$VoiceName.onnx")
73
- $VoiceJsonFile = [System.IO.Path]::GetFullPath("$VoicesDir\$VoiceName.onnx.json")
77
+ $VoiceModelFile = [System.IO.Path]::GetFullPath("$VoicesDir\$VoiceModel.onnx")
78
+ $VoiceJsonFile = [System.IO.Path]::GetFullPath("$VoicesDir\$VoiceModel.onnx.json")
74
79
  $ResolvedVoicesDir = [System.IO.Path]::GetFullPath($VoicesDir)
75
80
  if (-not $VoiceModelFile.StartsWith($ResolvedVoicesDir)) {
76
81
  Write-Host "[ERROR] Voice path outside voices directory" -ForegroundColor Red
@@ -79,15 +84,15 @@ if (-not $VoiceModelFile.StartsWith($ResolvedVoicesDir)) {
79
84
 
80
85
  # Check if voice model exists, download if missing
81
86
  if (-not (Test-Path $VoiceModelFile)) {
82
- Write-Host "[DOWNLOAD] Voice model: $VoiceName" -ForegroundColor Yellow
87
+ Write-Host "[DOWNLOAD] Voice model: $VoiceModel" -ForegroundColor Yellow
83
88
 
84
89
  # Try to download from Hugging Face
85
90
  # Voice name format: {lang}_{region}-{speaker}-{quality}
86
91
  # HF path format: {lang}/{lang}_{region}/{speaker}/{quality}/{voicename}.onnx
87
92
  try {
88
- # Parse voice name to build correct HF path
93
+ # Parse model name to build correct HF path (strip ::Speaker suffix first)
89
94
  # e.g. en_US-ryan-high -> en/en_US/ryan/high/en_US-ryan-high.onnx
90
- if ($VoiceName -match '^([a-z]{2})_([A-Z]{2})-([a-zA-Z0-9_]+)-([a-z]+)$') {
95
+ if ($VoiceModel -match '^([a-z]{2})_([A-Z]{2})-([a-zA-Z0-9_]+)-([a-z]+)$') {
91
96
  $Lang = $Matches[1]
92
97
  $LangRegion = "$($Matches[1])_$($Matches[2])"
93
98
  $Speaker = $Matches[3]
@@ -97,8 +102,8 @@ if (-not (Test-Path $VoiceModelFile)) {
97
102
  # Fallback for non-standard voice names
98
103
  $HFBase = "https://huggingface.co/rhasspy/piper-voices/resolve/main/en/en_US/ryan/high"
99
104
  }
100
- $ModelUrl = "$HFBase/$VoiceName.onnx"
101
- $JsonUrl = "$HFBase/$VoiceName.onnx.json"
105
+ $ModelUrl = "$HFBase/$VoiceModel.onnx"
106
+ $JsonUrl = "$HFBase/$VoiceModel.onnx.json"
102
107
 
103
108
  Write-Host " Downloading model..." -ForegroundColor Cyan
104
109
  Invoke-WebRequest -Uri $ModelUrl -OutFile $VoiceModelFile -ErrorAction Stop
@@ -127,11 +132,10 @@ $AudioFile = "$AudioDir\tts-$Timestamp.wav"
127
132
  try {
128
133
  Write-Host "[SYNTH] Synthesizing with Piper..." -ForegroundColor Cyan
129
134
 
130
- # Run Piper with text input
131
- $Text | & $PiperExe `
132
- --model $VoiceModelFile `
133
- --output-file $AudioFile `
134
- 2>$null
135
+ # Run Piper with text input; pass --speaker for multi-speaker models (model::Speaker format)
136
+ $piperArgs = @('--model', $VoiceModelFile, '--output-file', $AudioFile)
137
+ if ($SpeakerName) { $piperArgs += @('--speaker', $SpeakerName) }
138
+ $Text | & $PiperExe @piperArgs 2>$null
135
139
 
136
140
  if (-not (Test-Path $AudioFile)) {
137
141
  Write-Host "[ERROR] Piper synthesis failed" -ForegroundColor Red
@@ -151,12 +155,14 @@ try {
151
155
  if (-not $env:AGENTVIBES_NO_PLAY) {
152
156
  # Prefer ffplay: handles 22050 Hz โ†’ 48000 Hz resampling cleanly (SoundPlayer uses
153
157
  # WinMM's low-quality resampler which produces choppy audio at non-native rates).
154
- $ffplayPath = (Get-Command ffplay -ErrorAction SilentlyContinue)?.Source
158
+ $ffplayCmd = Get-Command ffplay -ErrorAction SilentlyContinue
159
+ $ffplayPath = if ($ffplayCmd) { $ffplayCmd.Source }
155
160
  if (-not $ffplayPath) {
156
161
  # SSH/watcher sessions may have a minimal PATH โ€” refresh from registry
157
162
  $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" +
158
163
  [System.Environment]::GetEnvironmentVariable("Path","User")
159
- $ffplayPath = (Get-Command ffplay -ErrorAction SilentlyContinue)?.Source
164
+ $ffplayCmd = Get-Command ffplay -ErrorAction SilentlyContinue
165
+ $ffplayPath = if ($ffplayCmd) { $ffplayCmd.Source }
160
166
  }
161
167
  if ($ffplayPath) {
162
168
  & $ffplayPath -autoexit -nodisp -loglevel quiet $AudioFile 2>$null
@@ -102,6 +102,9 @@ switch ($ActiveProvider) {
102
102
  "soprano" {
103
103
  $ProviderScript = "$HooksDir\play-tts-soprano.ps1"
104
104
  }
105
+ "kokoro" {
106
+ $ProviderScript = "$HooksDir\play-tts-kokoro.ps1"
107
+ }
105
108
  default {
106
109
  Write-Host "[ERROR] Unknown provider: $ActiveProvider" -ForegroundColor Red
107
110
  Write-Host "Use: .\provider-manager.ps1 list" -ForegroundColor Yellow
@@ -126,6 +129,7 @@ if ($ProviderOverride) {
126
129
  if (-not (Test-Path $ProviderScript)) { $ProviderScript = "$HooksDir\play-tts-windows-sapi.ps1" }
127
130
  }
128
131
  "soprano" { $ProviderScript = "$HooksDir\play-tts-soprano.ps1" }
132
+ "kokoro" { $ProviderScript = "$HooksDir\play-tts-kokoro.ps1" }
129
133
  default {
130
134
  Write-Host "[WARNING] play-tts.ps1: Unknown ProviderOverride '$ProviderOverride' ignored" -ForegroundColor Yellow
131
135
  }
@@ -382,7 +386,25 @@ if ($OverrideEffects -ne "" -and $OverrideEffects -in @("off", "light", "medium"
382
386
  # Transport providers (ssh-remote etc.) are not listed because they forward
383
387
  # TTS to a remote host โ€” overriding with a local engine would synthesize on
384
388
  # the wrong machine.
385
- if ($_LlmEngine) {
389
+ #
390
+ # Voice/engine coupling guard: Kokoro voice ids follow a strict
391
+ # "<2-letter prefix>_<name>" pattern where the 2nd char is the gender (f/m)
392
+ # โ€” e.g. af_river, am_eric, bf_emma, jf_alpha. They are all-lowercase with no
393
+ # locale, hyphen, digit, or "::" multi-speaker separator that Piper/LibriTTS
394
+ # voices always carry, so the pattern can never match a Piper voice. Such a
395
+ # voice can ONLY be synthesised by the Kokoro engine. When the active voice is
396
+ # a Kokoro voice (e.g. the Kokoro voice picker preview, which sends
397
+ # provider=kokoro + a Kokoro voice), the per-LLM ENGINE column โ€” which may name
398
+ # piper/sapi for that LLM's normal text responses that use Piper voices โ€” must
399
+ # NOT redirect it to an incompatible engine, or synthesis fails silently
400
+ # (Piper can't find the Kokoro voice model โ†’ no audio, exit 0).
401
+ $_VoiceIsKokoro = $VoiceOverride -match '^[a-z]{2}_[a-z0-9_]+$'
402
+ if ($_VoiceIsKokoro) {
403
+ # A Kokoro-format voice forces the Kokoro engine regardless of the per-LLM
404
+ # ENGINE column or the global tts-provider.txt default.
405
+ $ProviderScript = "$HooksDir\play-tts-kokoro.ps1"
406
+ }
407
+ elseif ($_LlmEngine) {
386
408
  # Accept both canonical Windows names and the cross-platform aliases the TUI
387
409
  # writes (e.g. "piper" saved on a Linux/WSL install that is later read on
388
410
  # Windows, or "sapi" as a short form). Unknown values keep the global default.
@@ -398,6 +420,7 @@ if ($_LlmEngine) {
398
420
  if (-not (Test-Path $ProviderScript)) { $ProviderScript = "$HooksDir\play-tts-windows-piper.ps1" }
399
421
  }
400
422
  "soprano" { $ProviderScript = "$HooksDir\play-tts-soprano.ps1" }
423
+ "kokoro" { $ProviderScript = "$HooksDir\play-tts-kokoro.ps1" }
401
424
  default {
402
425
  Write-Host "[INFO] play-tts.ps1: Unrecognised engine '$_LlmEngine' โ€” keeping default provider" -ForegroundColor DarkGray
403
426
  }
@@ -428,6 +451,11 @@ if ($env:AGENTVIBES_VERBOSE -eq "1") {
428
451
  # ffplay uses libswresample with sinc resampling โ€” no artefacts.
429
452
  function Invoke-AudioPlay {
430
453
  param([string]$FilePath)
454
+ # Stay silent while the automated test suite is running. The windows-tts/effects
455
+ # tests invoke this script and assert on its [VOICE]/routing output (printed
456
+ # earlier) โ€” they never check playback โ€” so skipping only the audio output keeps
457
+ # them green while preventing real speech on the developer's machine.
458
+ if (Test-Path (Join-Path $env:USERPROFILE ".agentvibes-tests-running")) { return }
431
459
  $ffplayCmd = Get-Command ffplay -ErrorAction SilentlyContinue
432
460
  $fp = if ($ffplayCmd) { $ffplayCmd.Source } else { $null }
433
461
  if (-not $fp) {
package/README.md CHANGED
@@ -11,7 +11,7 @@
11
11
  [![Publish](https://github.com/paulpreibisch/AgentVibes/actions/workflows/publish.yml/badge.svg)](https://github.com/paulpreibisch/AgentVibes/actions/workflows/publish.yml)
12
12
  [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
13
13
 
14
- **Author**: Paul Preibisch ([@997Fire](https://x.com/997Fire)) | **Version**: v5.9.0
14
+ **Author**: Paul Preibisch ([@997Fire](https://x.com/997Fire)) | **Version**: v5.11.0
15
15
 
16
16
  ---
17
17
 
@@ -347,7 +347,7 @@ All 50+ Piper voices AgentVibes provides are sourced from Hugging Face's open-so
347
347
  - [๐ŸŽค Voices Tab](#-voices-tab) - Browse & sample 914 voices in the TUI
348
348
  - [๐Ÿ’ฌ Intro Text](#-intro-text-pretext---your-personal-ai-branding) - Custom TTS prefixes
349
349
  - [๐ŸŽต Custom Background Music](#-custom-background-music---complete-audio-control) - Upload your own tracks
350
- - [๐Ÿ“ฐ Latest Release](#-latest-release) - v5.5.0 with Per-LLM Audio Routing, Windows Installer Resilience
350
+ - [๐Ÿ“ฐ Latest Release](#-latest-release) - v5.11.0 with Kokoro & ElevenLabs Providers, Audio Effects
351
351
  - [๐ŸชŸ Windows Setup Guide for Claude Desktop](mcp-server/WINDOWS_SETUP.md) - Complete Windows installation with WSL & Python
352
352
 
353
353
  ### AgentVibes MCP (Natural Language Control)
@@ -394,94 +394,20 @@ All 50+ Piper voices AgentVibes provides are sourced from Hugging Face's open-so
394
394
 
395
395
  ## ๐Ÿ“ฐ Latest Release
396
396
 
397
- **[v3.6.0 - "Voice Explorer" Release](https://github.com/paulpreibisch/AgentVibes/releases/tag/v5.7.5)** ๐ŸŽ‰
397
+ **[v5.11.0 โ€” Kokoro & ElevenLabs Providers + Audio Effects](https://github.com/paulpreibisch/AgentVibes/releases/tag/v5.11.0)** ๐ŸŽ‰
398
398
 
399
- ### ๐ŸŽค Voices Tab โ€” Browse & Sample 914 Voices
399
+ AgentVibes 5.11.0 adds two new TTS engines, combinable audio effects, a streamlined voices tab, and a deterministically green test suite.
400
400
 
401
- **Built into the TUI โ€” launch with `npx agentvibes` then press V**
401
+ ### โœจ Highlights
402
402
 
403
- ![AgentVibes Voices Tab](https://raw.githubusercontent.com/paulpreibisch/AgentVibes/master/docs/installation-screenshots/screenshot-voices.png)
403
+ - **๐Ÿ†• Kokoro provider** โ€” high-quality **local neural** voices (CPU, no GPU needed), including Chinese / Japanese / Korean, with in-TUI dependency install.
404
+ - **๐Ÿ†• ElevenLabs provider** โ€” premium **cloud** voices via an in-app API-key dialog.
405
+ - **๐ŸŽš๏ธ Combinable audio effects** โ€” stack **Reverb** (Room / Hall / Cathedral), **Echo** (short / cave), and **Chorus**; preview live with Space.
406
+ - **๐ŸชŸ Windows SAPI** routing fixed, and a **๐ŸŽจ unified selector chrome** across every picker.
407
+ - **๐ŸŽค Streamlined voices tab** โ€” keystroke-only (Enter selects, `f` favorites โ˜…); **BMAD auto-assign** now follows the active provider.
408
+ - **๐Ÿงช Rock-solid CI** โ€” eliminated the flaky failures; the test suite is deterministically green.
404
409
 
405
- - ๐ŸŽง Real-time voice sampling - press Space to hear before you choose
406
- - โญ Favorite system - mark your top voices
407
- - ๐Ÿ” Search & filter - find voices by personality, accent, gender
408
- - ๐Ÿ“ฆ One-click select - press Enter to install directly
409
- - ๐ŸŽจ Beautiful UI - built into the AgentVibes TUI
410
-
411
- **914 Total Voices:**
412
- - 904 Piper speaker variations (libritts-high)
413
- - 10 curated personality voices
414
-
415
- ### ๐ŸŽฏ Major Features
416
-
417
- **๐Ÿท๏ธ Friendly Voice Names**
418
- - No more cryptic IDs! Switch voices with names like "Ryan", "Joe", "Sarah"
419
- - All 904+ voices have memorable, personality-matched names
420
- - Voice metadata includes personalities, accents, and recommendations
421
-
422
- ```bash
423
- # Before: /agent-vibes:switch en_US-libritts_r-medium-speaker-123
424
- # After:
425
- /agent-vibes:switch Ryan
426
- ```
427
-
428
- **๐Ÿ’ฌ Intro Text (Pretext) Feature**
429
- - Custom prefix for all TTS announcements
430
- - Set during installation or anytime after
431
- - Perfect for personal branding: "FireBot: Starting analysis..."
432
- - Up to 50 characters, UTF-8 and emoji support
433
-
434
- ```bash
435
- npx agentvibes config intro-text
436
- ```
437
-
438
- **๐ŸŽต Custom Background Music**
439
- - Upload your own audio files (.mp3, .wav, .ogg, .m4a)
440
- - **Battle-tested security:** 180+ attack variations blocked
441
- - Magic number validation ensures real audio files
442
- - File ownership verification (UID checks)
443
- - Audio duration validation (30-90s recommended, 300s max)
444
- - Secure storage with 600 permissions
445
- - Perfect for team audio branding
446
-
447
- ```bash
448
- npx agentvibes config music
449
- ```
450
-
451
- **๐ŸŽจ Interactive Installer**
452
- - Preview voices during installation
453
- - Sample all 16 background music tracks
454
- - Audio environment auto-detection
455
- - Cross-platform preview support
456
-
457
- **๐Ÿ›ก๏ธ Security Hardening**
458
- - **180+ attack variations tested** - Path traversal, symlinks, Unicode, null bytes
459
- - **100% attack rejection rate** - All malicious attempts blocked
460
- - **OWASP compliant** - CWE-22 path traversal prevention verified
461
- - **Production certified** - Comprehensive security audit completed
462
- - **Defense-in-depth** - 7 validation layers protect your system
463
- - File ownership verification and secure storage (600 permissions)
464
- - Security audit report: `docs/security/SECURITY-AUDIT.md`
465
-
466
- ### Quick Install
467
-
468
- ```bash
469
- # Install AgentVibes
470
- npx agentvibes install
471
-
472
- # Browse voices in the TUI
473
- npx agentvibes # press V for Voices tab
474
- ```
475
-
476
- **๐Ÿž Bug Fixes in v3.6.0:**
477
- - Fixed `get_verbosity` MCP tool returning wrong level after fresh install (now reads from correct project directory, defaults to `high`)
478
- - Fixed Voice Browser Soprano TTS detection, Custom Music race conditions, installer emoji rendering
479
-
480
- ๐Ÿ’ก **Tip:** If `npx agentvibes` shows an older version, clear cache: `npm cache clean --force && npx agentvibes@latest --help`
481
-
482
- ๐Ÿ› **Found a bug?** Report at [GitHub Issues](https://github.com/paulpreibisch/AgentVibes/issues)
483
-
484
- [โ†’ View Complete Release Notes](RELEASE_NOTES_v3.6.0.md) | [โ†’ View All Releases](https://github.com/paulpreibisch/AgentVibes/releases)
410
+ ๐Ÿ“– Full details: [RELEASE_NOTES.md](RELEASE_NOTES.md) | [โ†’ All Releases](https://github.com/paulpreibisch/AgentVibes/releases)
485
411
 
486
412
  [โ†‘ Back to top](#-table-of-contents)
487
413
 
package/RELEASE_NOTES.md CHANGED
@@ -1,5 +1,65 @@
1
1
  # AgentVibes Release Notes
2
2
 
3
+ ## ๐ŸŽ™๏ธ v5.11.0 โ€” Kokoro & ElevenLabs Providers, Unified Pickers, Windows SAPI, Rock-Solid CI
4
+
5
+ **Released:** 2026-06-26
6
+
7
+ This is a big one. AgentVibes gains two new TTS engines โ€” **Kokoro** (high-quality local neural voices, including Chinese/Japanese/Korean) and **ElevenLabs** (premium cloud voices) โ€” combinable audio effects, a unified selector UI, a Windows SAPI routing fix, and a deterministically green test suite. It also rolls up everything from the earlier 5.11.0 preview (LibriTTS 900+ voices, setup UX).
8
+
9
+ ### ๐Ÿ†• New TTS Provider: Kokoro (local neural voices)
10
+ - Browse and preview Kokoro voices from the Setup tab using the standardized picker.
11
+ - **CPU synthesis** path so it runs without a GPU.
12
+ - Smart dependency detection with in-TUI install dialogs โ€” installs `kokoro`, `soundfile`, and `numpy` together, with a real progress bar (not just a spinner).
13
+ - **CJK support**: Chinese, Japanese, and Korean voices, with per-language `misaki` / `pyopenjtalk` dependency handling and native sample phrases.
14
+ - Windows playback fixed (Forms assembly load, correct paths, clearer exit-code errors).
15
+ - A readiness "bling" cue (bundled CC0 sound) signals when a voice is ready to preview.
16
+
17
+ ### ๐Ÿ†• New TTS Provider: ElevenLabs (premium cloud voices)
18
+ - API-key dialog built into the Setup tab, with a navigation hint and Escape-to-close.
19
+ - Routed through the same per-LLM voice system as every other provider.
20
+
21
+ ### ๐ŸŽš๏ธ New: Combinable Audio Effects โ€” Reverb ยท Echo ยท Chorus
22
+ Give any voice real character. A new multi-select effects modal lets you stack **reverb**, **echo**, and **chorus** independently:
23
+ - **Reverb:** Room, Hall, Cathedral, and Warm (reverb + bass).
24
+ - **Echo:** Echo (short delay) and Cave Echo (long) โ€” brand new; there was no echo before.
25
+ - **Chorus** for a richer, doubled timbre.
26
+ - Effects **combine** (e.g. a little reverb *plus* a short echo), preview live with Space, and the picker now speaks the effect name and shows a spinner while auditioning.
27
+
28
+ ### ๐ŸŽจ Unified Selector Chrome
29
+ - All selector pickers (voices, tracks, reverb/effects) now share a common help bar, title style, and a fixed-width voice-row formatter โ€” a consistent look and aligned status columns across every picker.
30
+
31
+ ### ๐ŸชŸ Windows SAPI Routing Fixed
32
+ - The bash hook router now recognizes the `sapi` / `windows-sapi` provider and routes to `play-tts-sapi.ps1` correctly (previously errored with "Unknown provider: sapi").
33
+
34
+ ### ๐ŸŽค Voices & BMAD Tab Improvements
35
+ - **Streamlined, keystroke-only voice list.** Removed the redundant Switch / Favorite / Download button row โ€” **Enter** selects (and downloads an uninstalled Piper voice first), **Space** previews, and **`f`** toggles a favorite โ˜…. Thumbs-up/thumbs-down collapsed into a single favorite star.
36
+ - **Removed the confusing "Install BMAD Voices" button.** Bulk voice downloads are available from the CLI (`npx agentvibes voices download`) instead.
37
+ - **BMAD auto-assign now respects the active provider.** Pressing **[A] Auto-assign** previously always picked Piper voices; it now assigns voices from whichever provider is currently selected (Piper, Kokoro, ElevenLabs, soprano, โ€ฆ).
38
+
39
+ ### ๐ŸŽ›๏ธ Picker & Preview Polish
40
+ - **Effects preview:** correct voice + effect routing, an inline spinner on the item row, and a `pyopenjtalk` install prompt where needed.
41
+ - **Track picker:** uses an `ffmpeg โ†’ pacat` pipe on headless PulseAudio TCP servers so previews work in remote/headless setups.
42
+ - **Navigation:** pressing a tab's shortcut key while already on that tab is now a no-op.
43
+
44
+ ### ๐Ÿงช Rock-Solid CI (no more flaky failures)
45
+ - Eliminated the intermittent CI red: fixed a `blessed.log()`-after-teardown crash in the Voices tab that randomly failed an innocent test file, and added a coverage runner (`scripts/run-coverage.mjs`) that gates on the actual reported test results instead of node's flaky `--test-force-exit` exit code. Real failures still fail the build; node's force-exit artifacts are tolerated.
46
+ - Test audio is fully silenced during runs (marker file + env flags) so the suite never bleeds to your speakers.
47
+
48
+ ### ๐Ÿ› Audio Robustness Fixes
49
+ - `getAudioDuration()` now attaches an error listener to its `which ffprobe` probe (previously this could throw an unhandled error and crash on Windows, where `which` doesn't exist) and bounds both spawns with 5s/10s timeouts so a stalled `ffprobe` can never hang the caller.
50
+ - The interactive voice-preview prompt now releases (`pause()`) the stdin handle it opens, so the process exits cleanly instead of lingering.
51
+
52
+ ### ๐Ÿ“ฆ Also in 5.11.0 (earlier preview)
53
+ - LibriTTS 900+ voice library offer during install (~114 MB, 904 speakers).
54
+ - Linux Piper install now uses the project's own `piper-installer.sh --non-interactive`.
55
+ - Setup tab: spinner during engine install, smarter Tab navigation to Continue, install timeout raised to 30 minutes.
56
+
57
+ ### ๐Ÿ›ก๏ธ Quality
58
+ - Full test suite green. New audio code covered: `audio-duration-validator` 96%, `preview-list-prompt` 90%.
59
+ - Known pre-existing: `piper-voice-manager.sh` / `piper-installer.sh` use partial shell strict mode; tracked for a future hardening pass.
60
+
61
+ ---
62
+
3
63
  ## ๐Ÿ”ง v5.9.0 โ€” SSH Remote + Windows Home Directory Fixes
4
64
 
5
65
  **Released:** 2026-05-18