@inetafrica/open-claudia 3.1.2 → 3.1.3
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.
- package/CHANGELOG.md +7 -0
- package/core/config.js +5 -0
- package/core/media.js +36 -2
- package/core/providers/env.js +1 -0
- package/package.json +1 -1
- package/test-provider-language.js +4 -11
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v3.1.3 — Pocket TTS voice-note provider
|
|
4
|
+
|
|
5
|
+
- Adds Agent Space Pocket TTS as the preferred voice synthesizer for Telegram and streamed voice replies.
|
|
6
|
+
- Requests WAV from the authenticated speech gateway and transcodes it to Ogg/Opus with ffmpeg.
|
|
7
|
+
- Keeps local Whisper transcription unchanged and falls back to ElevenLabs, then macOS `say`, on synthesis failure.
|
|
8
|
+
- Adds configurable `SPEECH_SERVICE_URL`, `SPEECH_API_KEY`, `POCKET_TTS_VOICE`, and `TTS_PROVIDER` settings while stripping the speech credential from provider subprocess environments.
|
|
9
|
+
|
|
3
10
|
## v3.1.2 — On-demand recall query + cold-walker fallback fix
|
|
4
11
|
|
|
5
12
|
Two things fold in: a new `recall query` CLI so the working agent can ask long-term memory a question mid-task, and a latent reliability fix in the recall walker's fallback path.
|
package/core/config.js
CHANGED
|
@@ -141,6 +141,10 @@ const FFMPEG = config.FFMPEG || "";
|
|
|
141
141
|
const ELEVENLABS_API_KEY = config.ELEVENLABS_API_KEY || process.env.ELEVENLABS_API_KEY || "";
|
|
142
142
|
const ELEVENLABS_VOICE_ID = config.ELEVENLABS_VOICE_ID || process.env.ELEVENLABS_VOICE_ID || "EXAVITQu4vr4xnSDxMaL";
|
|
143
143
|
const ELEVENLABS_MODEL = config.ELEVENLABS_MODEL || process.env.ELEVENLABS_MODEL || "eleven_v3";
|
|
144
|
+
const SPEECH_SERVICE_URL = config.SPEECH_SERVICE_URL || process.env.SPEECH_SERVICE_URL || "https://speech.agents.kazee.africa";
|
|
145
|
+
const SPEECH_API_KEY = config.SPEECH_API_KEY || process.env.SPEECH_API_KEY || "";
|
|
146
|
+
const POCKET_TTS_VOICE = config.POCKET_TTS_VOICE || process.env.POCKET_TTS_VOICE || "alba";
|
|
147
|
+
const TTS_PROVIDER = config.TTS_PROVIDER || process.env.TTS_PROVIDER || "pocket";
|
|
144
148
|
const SOUL_FILE = config.SOUL_FILE || path.join(CONFIG_DIR, "soul.md");
|
|
145
149
|
const CRONS_FILE = config.CRONS_FILE || path.join(CONFIG_DIR, "crons.json");
|
|
146
150
|
const JOBS_FILE = config.JOBS_FILE || path.join(CONFIG_DIR, "jobs.json");
|
|
@@ -370,6 +374,7 @@ module.exports = {
|
|
|
370
374
|
TRANSCRIPTS_DIR,
|
|
371
375
|
WHISPER_CLI, WHISPER_MODEL, FFMPEG,
|
|
372
376
|
ELEVENLABS_API_KEY, ELEVENLABS_VOICE_ID, ELEVENLABS_MODEL,
|
|
377
|
+
SPEECH_SERVICE_URL, SPEECH_API_KEY, POCKET_TTS_VOICE, TTS_PROVIDER,
|
|
373
378
|
SOUL_FILE, CRONS_FILE, JOBS_FILE, TASKS_DIR, VAULT_FILE, AUTH_FILE, IDENTITIES_FILE,
|
|
374
379
|
PEOPLE_FILE, INTROS_FILE, AUDIT_FILE,
|
|
375
380
|
STATE_FILE, SESSIONS_FILE,
|
package/core/media.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
const fs = require("fs");
|
|
5
5
|
const path = require("path");
|
|
6
6
|
const { execSync } = require("child_process");
|
|
7
|
-
const { WHISPER_CLI, WHISPER_MODEL, FFMPEG, TEMP_DIR, ELEVENLABS_API_KEY, ELEVENLABS_VOICE_ID, ELEVENLABS_MODEL } = require("./config");
|
|
7
|
+
const { WHISPER_CLI, WHISPER_MODEL, FFMPEG, TEMP_DIR, ELEVENLABS_API_KEY, ELEVENLABS_VOICE_ID, ELEVENLABS_MODEL, SPEECH_SERVICE_URL, SPEECH_API_KEY, POCKET_TTS_VOICE, TTS_PROVIDER } = require("./config");
|
|
8
8
|
|
|
9
9
|
const TTS_CMD = process.platform === "darwin" ? "say" : null;
|
|
10
10
|
|
|
@@ -70,10 +70,42 @@ async function elevenLabsToVoice(clean) {
|
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
// Pocket TTS through the Agent Space speech gateway. The gateway returns WAV;
|
|
74
|
+
// Telegram and voice transports receive a compact Ogg/Opus file.
|
|
75
|
+
async function pocketToVoice(clean) {
|
|
76
|
+
if (TTS_PROVIDER === "elevenlabs" || !SPEECH_API_KEY || !SPEECH_SERVICE_URL || !FFMPEG) return null;
|
|
77
|
+
let wavPath = null;
|
|
78
|
+
try {
|
|
79
|
+
const res = await fetch(`${SPEECH_SERVICE_URL.replace(/\/$/, "")}/v1/audio/speech`, {
|
|
80
|
+
method: "POST",
|
|
81
|
+
headers: { Authorization: `Bearer ${SPEECH_API_KEY}`, "Content-Type": "application/json" },
|
|
82
|
+
body: JSON.stringify({ input: clean, voice: POCKET_TTS_VOICE, response_format: "wav" }),
|
|
83
|
+
signal: AbortSignal.timeout(60000),
|
|
84
|
+
});
|
|
85
|
+
if (!res.ok) {
|
|
86
|
+
console.error(`Pocket TTS failed: HTTP ${res.status}`);
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
const stem = `tts-pocket-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
|
|
90
|
+
wavPath = path.join(TEMP_DIR, `${stem}.wav`);
|
|
91
|
+
const oggPath = path.join(TEMP_DIR, `${stem}.ogg`);
|
|
92
|
+
fs.writeFileSync(wavPath, Buffer.from(await res.arrayBuffer()));
|
|
93
|
+
execSync(`"${FFMPEG}" -i "${wavPath}" -c:a libopus -y "${oggPath}" 2>/dev/null`, { timeout: 30000 });
|
|
94
|
+
return oggPath;
|
|
95
|
+
} catch (e) {
|
|
96
|
+
console.error("Pocket TTS error:", e.message);
|
|
97
|
+
return null;
|
|
98
|
+
} finally {
|
|
99
|
+
if (wavPath) { try { fs.unlinkSync(wavPath); } catch (e) {} }
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
73
103
|
// Natural voice via ElevenLabs, falling back to macOS `say` only on no-key/error.
|
|
74
104
|
async function textToVoice(text) {
|
|
75
105
|
const clean = cleanForTTS(text);
|
|
76
106
|
if (!clean) return null;
|
|
107
|
+
const pocket = await pocketToVoice(clean);
|
|
108
|
+
if (pocket) return pocket;
|
|
77
109
|
const eleven = await elevenLabsToVoice(clean);
|
|
78
110
|
if (eleven) return eleven;
|
|
79
111
|
return sayToVoice(clean);
|
|
@@ -105,6 +137,8 @@ function splitSentences(text, minLen = 30) {
|
|
|
105
137
|
async function synthSentenceMp3(text) {
|
|
106
138
|
const clean = cleanForTTS(text);
|
|
107
139
|
if (!clean) return null;
|
|
140
|
+
const pocket = await pocketToVoice(clean);
|
|
141
|
+
if (pocket) return pocket;
|
|
108
142
|
if (!ELEVENLABS_API_KEY) return sayToVoice(clean); // ogg fallback
|
|
109
143
|
try {
|
|
110
144
|
const res = await fetch(`https://api.elevenlabs.io/v1/text-to-speech/${ELEVENLABS_VOICE_ID}`, {
|
|
@@ -131,4 +165,4 @@ async function synthSentenceMp3(text) {
|
|
|
131
165
|
}
|
|
132
166
|
}
|
|
133
167
|
|
|
134
|
-
module.exports = { transcribeAudio, textToVoice, splitSentences, synthSentenceMp3, TTS_CMD };
|
|
168
|
+
module.exports = { transcribeAudio, textToVoice, splitSentences, synthSentenceMp3, pocketToVoice, TTS_CMD };
|
package/core/providers/env.js
CHANGED
package/package.json
CHANGED
|
@@ -157,17 +157,10 @@ assert.match(pkg.description, /provider-agnostic coding-agent harness/i);
|
|
|
157
157
|
// never re-offers a sign-in); a 🔁 Switch account button on connected
|
|
158
158
|
// providers in /login behind an explicit confirm that names the signed-in
|
|
159
159
|
// account where the CLI exposes it, then chains logout → fresh login.
|
|
160
|
-
// 3.1.
|
|
161
|
-
//
|
|
162
|
-
//
|
|
163
|
-
|
|
164
|
-
// active-provider judge, returns pack sections + why-bullets; closes the
|
|
165
|
-
// cold-start hole where mid-build vocabulary shared no words with the governing
|
|
166
|
-
// pack. Also fixes a latent cold-walker bug: the warm→cold fallback sent an
|
|
167
|
-
// array-typed outputSchema the Anthropic API rejects 400, so every fallback
|
|
168
|
-
// silently failed-open — removed; freeform JSON parse is the proven contract on
|
|
169
|
-
// both paths.
|
|
170
|
-
assert.strictEqual(pkg.version, "3.1.2", "release version must remain unchanged without explicit approval");
|
|
160
|
+
// 3.1.3 approved by Sumeet 2026-07-21 ("ok fine go ahead") for Pocket TTS
|
|
161
|
+
// as the preferred voice-note provider, with ElevenLabs fallback and local
|
|
162
|
+
// Whisper transcription unchanged.
|
|
163
|
+
assert.strictEqual(pkg.version, "3.1.3", "release version must remain unchanged without explicit approval");
|
|
171
164
|
for (const keyword of ["claude", "codex", "coding-agent", "provider-agnostic"]) {
|
|
172
165
|
assert.ok(pkg.keywords.includes(keyword), `package keyword missing: ${keyword}`);
|
|
173
166
|
}
|