@inetafrica/open-claudia 3.1.7 → 3.1.10
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 +15 -0
- package/bin/cli.js +23 -0
- package/core/config.js +3 -1
- package/core/loopback.js +39 -0
- package/core/media.js +10 -1
- package/core/system-prompt.js +6 -2
- package/package.json +2 -2
- package/test-provider-language.js +17 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v3.1.10 — `say` synthesizes in the bot process, not the stripped subprocess
|
|
4
|
+
|
|
5
|
+
- v3.1.9 carried this exact fix but its build never published: the version pin in `test-provider-language.js` still read `3.1.8`, so the guard failed and the docker stage was skipped. Re-cut as v3.1.10 per "never retag a failed version."
|
|
6
|
+
- **Root cause of the persistent "no voice" reports.** `open-claudia say` synthesized the audio *inside the CLI*, which runs in the credential-stripped provider subprocess — with no `AGENTSPACE_POD_TOKEN`. `pocketToVoice` had no speech bearer and returned null, so hosted pods told users they couldn't send voice (the rahil-coders and kazee-people reports), even though the parent bot process — which *does* hold the token — evaluated `canSpeak() === true` and advertised "Voice notes: enabled." The capability check and the actual send were running on opposite sides of the credential boundary.
|
|
7
|
+
- **Fix.** Synthesis moves into the credentialed bot process behind a new loopback `say` JSON kind. The CLI now POSTs only text; `handleJson` runs `textToVoice` in-process, delivers via `adapter.sendVoice`, and cleans up the temp ogg afterward. A restart alone won't adopt this — it ships in the image and every pod is rolled onto it.
|
|
8
|
+
- **Egress guardrail preserved.** The old `say` inherited the R8 external-contact voice-egress default-deny from `handleSend`; the new in-process handler re-applies the same `relationship.guardActive` check, so `say` cannot become a guard bypass to non-owner contacts. Returns 400 (empty text) / 403 (guarded target) / 503 (no TTS route) / 500 (synthesis or delivery failure).
|
|
9
|
+
- Regression coverage in `test-say-loopback.js`: success (synthesis + delivery + temp cleanup), empty text, no-route, delivery failure, the R8 guard, and loopback auth.
|
|
10
|
+
|
|
11
|
+
## v3.1.8 — Hosted pods can actually send voice notes
|
|
12
|
+
|
|
13
|
+
- **FFMPEG is discovered on PATH.** Provisioned pods ship `/usr/bin/ffmpeg` but never set `FFMPEG=` in config, and `config.js` read only the explicit setting — so `FFMPEG` resolved empty and every TTS route (`pocketToVoice`, ElevenLabs, `say`) silently returned null: voice input got text-only replies. `FFMPEG` now falls back to PATH discovery via the existing `executableCandidate` helper; an explicit `FFMPEG=` still wins.
|
|
14
|
+
- **The system prompt's voice-notes flag reflects real TTS capability.** It gated on `WHISPER_CLI && FFMPEG` — the pre-gateway local-Whisper world — so whisper-less hosted pods told the model "Voice notes: disabled" and it refused user requests (the smart-euvy report), even with working gateway speech. New `media.canSpeak()` mirrors the actual pocket → ElevenLabs → macOS `say` fallback chain and now drives the flag; local Whisper is irrelevant to speaking.
|
|
15
|
+
- **New `open-claudia say "<text>"` CLI verb.** `send-voice` needs a ready-made ogg and the model had no way to synthesize one on request. `say` runs `textToVoice` and sends the result through the loopback voice path (same egress guardrail), and is documented in the Delivery prompt block.
|
|
16
|
+
- Regression coverage in `test-media-speech.js`: a whisper-less pod shape must report `canSpeak() === true`, the prompt gate must key on it, and FFMPEG resolution precedence (explicit > PATH discovery > empty) is asserted via subprocess config loads.
|
|
17
|
+
|
|
3
18
|
## v3.1.7 — CI spawn-race hardening (first published build of the v3.1.5 changes)
|
|
4
19
|
|
|
5
20
|
- v3.1.6 also never published. The loaded shared CI runner (node cold-boot 100-370ms vs ~50ms locally) keeps picking off the next-tightest spawn-timing test: this time `test-utility-provider-policy`'s shutdown-drain case, which flat-slept 100ms then cancelled — killing the fake agent before it booted, so its SIGTERM handler never wrote the expected signal capture (pipeline 10383; the v3.1.6 run-lifecycle fix itself passed there).
|
package/bin/cli.js
CHANGED
|
@@ -277,6 +277,28 @@ switch (command) {
|
|
|
277
277
|
break;
|
|
278
278
|
}
|
|
279
279
|
|
|
280
|
+
case "say": {
|
|
281
|
+
const text = args.slice(1).join(" ").trim();
|
|
282
|
+
if (!text) {
|
|
283
|
+
console.error('Usage: open-claudia say "<text>"');
|
|
284
|
+
process.exit(2);
|
|
285
|
+
}
|
|
286
|
+
(async () => {
|
|
287
|
+
// Synthesis happens in the credentialed bot process, not here: this CLI
|
|
288
|
+
// runs in the credential-stripped provider subprocess with no speech
|
|
289
|
+
// token. Send only text through loopback; the bot process makes the audio.
|
|
290
|
+
const { postJson } = require("./loopback-client");
|
|
291
|
+
try {
|
|
292
|
+
await postJson("say", { text });
|
|
293
|
+
process.exit(0);
|
|
294
|
+
} catch (e) {
|
|
295
|
+
console.error(e.message);
|
|
296
|
+
process.exit(1);
|
|
297
|
+
}
|
|
298
|
+
})();
|
|
299
|
+
break;
|
|
300
|
+
}
|
|
301
|
+
|
|
280
302
|
case "tw":
|
|
281
303
|
case "transcript-window": {
|
|
282
304
|
require("./transcript-window").run(args.slice(1));
|
|
@@ -441,6 +463,7 @@ Send tools (only work inside an active bot-spawned task):
|
|
|
441
463
|
open-claudia send-file <path> [caption]
|
|
442
464
|
open-claudia send-photo <path> [caption]
|
|
443
465
|
open-claudia send-voice <path>
|
|
466
|
+
open-claudia say "<text>" Synthesize the text (TTS) and send it as a voice note
|
|
444
467
|
|
|
445
468
|
Memory tools:
|
|
446
469
|
open-claudia transcript-search <query> Ranked FTS5 search over indexed transcripts
|
package/core/config.js
CHANGED
|
@@ -137,7 +137,9 @@ const TRANSCRIPT_MAX_ENTRY_CHARS = parseInt(config.TRANSCRIPT_MAX_ENTRY_CHARS ||
|
|
|
137
137
|
const TRANSCRIPTS_DIR = config.TRANSCRIPTS_DIR || process.env.TRANSCRIPTS_DIR || path.join(CONFIG_DIR, "transcripts");
|
|
138
138
|
const WHISPER_CLI = config.WHISPER_CLI || "";
|
|
139
139
|
const WHISPER_MODEL = config.WHISPER_MODEL || "";
|
|
140
|
-
|
|
140
|
+
// Hosted pods ship /usr/bin/ffmpeg but never set FFMPEG=, so without PATH
|
|
141
|
+
// discovery every TTS route silently no-ops (smart-euvy "voice notes disabled").
|
|
142
|
+
const FFMPEG = config.FFMPEG || executableCandidate("ffmpeg") || "";
|
|
141
143
|
const ELEVENLABS_API_KEY = config.ELEVENLABS_API_KEY || process.env.ELEVENLABS_API_KEY || "";
|
|
142
144
|
const ELEVENLABS_VOICE_ID = config.ELEVENLABS_VOICE_ID || process.env.ELEVENLABS_VOICE_ID || "EXAVITQu4vr4xnSDxMaL";
|
|
143
145
|
const ELEVENLABS_MODEL = config.ELEVENLABS_MODEL || process.env.ELEVENLABS_MODEL || "eleven_v3";
|
package/core/loopback.js
CHANGED
|
@@ -82,6 +82,7 @@ const JSON_KINDS = new Set([
|
|
|
82
82
|
"relay-send", "recent-fetch", "audit-tail",
|
|
83
83
|
"auth-list", "auth-revoke",
|
|
84
84
|
"approval-request",
|
|
85
|
+
"say",
|
|
85
86
|
]);
|
|
86
87
|
|
|
87
88
|
function callerIsOwner(payload) {
|
|
@@ -161,6 +162,44 @@ async function handleJson(req, res, url, kind) {
|
|
|
161
162
|
const adapter = registry.findAdapter(adapterId);
|
|
162
163
|
if (!adapter) return reply(res, 400, { error: `unknown adapter ${adapterId}` });
|
|
163
164
|
|
|
165
|
+
if (kind === "say") {
|
|
166
|
+
const text = String(payload.text || "").trim();
|
|
167
|
+
if (!text) return reply(res, 400, { error: "missing text" });
|
|
168
|
+
|
|
169
|
+
// Synthesized voice is file egress, so apply the same R8 default-deny as
|
|
170
|
+
// handleSend: refuse it to a guarded (external, non-owner) target. Owner /
|
|
171
|
+
// CLI / cron paths are unguarded, so this is a strict no-op for them.
|
|
172
|
+
try {
|
|
173
|
+
const relationship = require("./relationship");
|
|
174
|
+
const speaker = relationship.speakerFor(adapter.type, channelId, userId);
|
|
175
|
+
if (relationship.guardActive(speaker)) {
|
|
176
|
+
return reply(res, 403, { error: "voice egress to an external contact is blocked by the guardrail — ask the owner to send it" });
|
|
177
|
+
}
|
|
178
|
+
} catch (e) { /* classification failure → fall through; owner-scoped in practice */ }
|
|
179
|
+
|
|
180
|
+
// Synthesis MUST happen here (the credentialed bot process). The CLI runs in
|
|
181
|
+
// the credential-stripped provider subprocess with no speech token, so it
|
|
182
|
+
// sends only text and this handler produces the audio.
|
|
183
|
+
let voicePath = null;
|
|
184
|
+
try {
|
|
185
|
+
const { textToVoice } = require("./media");
|
|
186
|
+
voicePath = await textToVoice(text);
|
|
187
|
+
} catch (e) {
|
|
188
|
+
return reply(res, 500, { error: `synthesis failed: ${e.message}` });
|
|
189
|
+
}
|
|
190
|
+
if (!voicePath) return reply(res, 503, { error: "no TTS route available" });
|
|
191
|
+
|
|
192
|
+
let ok = false;
|
|
193
|
+
try {
|
|
194
|
+
ok = await adapter.sendVoice(channelId, voicePath);
|
|
195
|
+
} catch (e) {
|
|
196
|
+
try { fs.unlinkSync(voicePath); } catch (_) {}
|
|
197
|
+
return reply(res, 500, { error: `voice send failed: ${e.message}` });
|
|
198
|
+
}
|
|
199
|
+
try { fs.unlinkSync(voicePath); } catch (e) {}
|
|
200
|
+
return reply(res, ok ? 200 : 500, { ok });
|
|
201
|
+
}
|
|
202
|
+
|
|
164
203
|
if (kind === "schedule-wakeup") {
|
|
165
204
|
const fireAt = Number(payload.fireAt);
|
|
166
205
|
if (!Number.isFinite(fireAt) || fireAt <= 0) return reply(res, 400, { error: "missing/invalid fireAt" });
|
package/core/media.js
CHANGED
|
@@ -129,6 +129,15 @@ async function pocketToVoice(clean) {
|
|
|
129
129
|
}
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
+
// True when at least one textToVoice route can produce audio. Mirrors the
|
|
133
|
+
// pocket -> ElevenLabs -> say fallback chain; local Whisper is deliberately
|
|
134
|
+
// irrelevant here (hosted pods speak via the gateway without it).
|
|
135
|
+
function canSpeak() {
|
|
136
|
+
if (!FFMPEG) return false;
|
|
137
|
+
if (TTS_PROVIDER !== "elevenlabs" && speechBearerToken() && SPEECH_SERVICE_URL) return true;
|
|
138
|
+
return Boolean(ELEVENLABS_API_KEY || TTS_CMD);
|
|
139
|
+
}
|
|
140
|
+
|
|
132
141
|
// Natural voice via ElevenLabs, falling back to macOS `say` only on no-key/error.
|
|
133
142
|
async function textToVoice(text) {
|
|
134
143
|
const clean = cleanForTTS(text);
|
|
@@ -194,4 +203,4 @@ async function synthSentenceMp3(text) {
|
|
|
194
203
|
}
|
|
195
204
|
}
|
|
196
205
|
|
|
197
|
-
module.exports = { transcribeAudio, transcribeAudioLocal, textToVoice, splitSentences, synthSentenceMp3, pocketToVoice, speechBearerToken, TTS_CMD };
|
|
206
|
+
module.exports = { transcribeAudio, transcribeAudioLocal, textToVoice, splitSentences, synthSentenceMp3, pocketToVoice, speechBearerToken, canSpeak, TTS_CMD };
|
package/core/system-prompt.js
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
const fs = require("fs");
|
|
5
5
|
const path = require("path");
|
|
6
|
-
const { SOUL_FILE, CRONS_FILE, VAULT_FILE, FILES_DIR, BOT_DIR,
|
|
6
|
+
const { SOUL_FILE, CRONS_FILE, VAULT_FILE, FILES_DIR, BOT_DIR, config } = require("./config");
|
|
7
|
+
const { canSpeak } = require("./media");
|
|
7
8
|
const CONFIG_DIR = require("../config-dir");
|
|
8
9
|
const { currentState, getProjectKey, getProviderSession } = require("./state");
|
|
9
10
|
const { currentAdapter, currentChannelId, currentUserId } = require("./context");
|
|
@@ -198,7 +199,9 @@ ${listing}`;
|
|
|
198
199
|
|
|
199
200
|
function buildCoreInstructionsUncached() {
|
|
200
201
|
const soul = loadSoul();
|
|
201
|
-
|
|
202
|
+
// Gate on actual TTS capability, not local Whisper: hosted pods hear and
|
|
203
|
+
// speak through the speech gateway with no whisper binary installed.
|
|
204
|
+
const hasVoice = canSpeak();
|
|
202
205
|
return `
|
|
203
206
|
${soul}
|
|
204
207
|
${buildPersonaBlock()}
|
|
@@ -230,6 +233,7 @@ Reply normally in your final answer. To send a file, image, or voice clip back t
|
|
|
230
233
|
- \`open-claudia send-file <path> [caption]\` — any document/binary
|
|
231
234
|
- \`open-claudia send-photo <path> [caption]\` — image with inline preview
|
|
232
235
|
- \`open-claudia send-voice <path>\` — ogg/opus voice note
|
|
236
|
+
- \`open-claudia say "<text>"\` — synthesize the text and send it as a voice note (use this when asked for a voice reply)
|
|
233
237
|
Never print or embed bot tokens in prompts, commands, logs, or messages.
|
|
234
238
|
|
|
235
239
|
## Background work
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inetafrica/open-claudia",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.10",
|
|
4
4
|
"description": "An always-on, provider-agnostic coding-agent harness for Claude Code and OpenAI Codex via chat",
|
|
5
5
|
"main": "bot.js",
|
|
6
6
|
"bin": {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"setup": "node setup.js",
|
|
11
11
|
"start": "node bot.js",
|
|
12
12
|
"pretest": "node test-provider-fixture.js && node test-provider-core-prompt.js && node test-turn-observer.js && node test-provider-prompt-parity.js && node test-provider-env-isolation.js && node test-provider-resume-parity.js && node test-provider-bootstrap.js && node test-provider-registry.js && node test-provider-stream-decoder.js && node test-provider-events.js && node test-provider-claude.js && node test-provider-codex.js && node test-provider-runner.js && node test-provider-refusal-recovery.js && node test-provider-run-lifecycle.js && node test-provider-gateway.js && node test-provider-side-chat.js && node test-single-runtime.js && node test-provider-migration-backup.js && node test-provider-state-migration.js && node test-provider-session-history.js && node test-provider-session-commands.js && node test-provider-compaction.js && node test-provider-scheduler.js && node test-credential-capture.js && node test-cursor-removal.js && node test-utility-provider-policy.js && node test-provider-subagent.js && node test-provider-recall.js && node test-provider-pack-review.js && node test-memory-mutation-queue.js && node test-provider-dream.js && node test-provider-enforcer.js && node test-provider-tool-hooks.js && node test-provider-boot-matrix.js && node test-provider-capabilities.js && node test-provider-language.js && node test-provider-coupling-audit.js && node test-provider-parity-e2e.js && node test-kazee-channel-health.js && node test-kazee-media-caption.js && node test-prompt-budget.js && node test-agency-ledger.js && node test-agency-deliberate.js && node test-guardrail.js && node test-spaces-comment-content.js && node test-boot-health.js && node test-lexical-floor.js && node test-safe-mode.js && node test-logout.js",
|
|
13
|
-
"test": "OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node -e \"require('./vault'); console.log('OK')\" && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-runner-watchdog-static.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-usage-accounting.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-engine.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-graph.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-discoverer.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-query.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-walker-score.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-score-gate.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-cokept-warming.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-project-transcripts-smoke.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-abilities.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-extraction.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-couse.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-transfer.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-tiers.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-merge-guard.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-learning-e2e.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-pack-nesting.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-pack-search.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-extractive-injection.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-journal-dedupe.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-read-signal.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tools.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-graph.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-guard.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tooling-mode.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-manifest.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-evolution.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-approval-async.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-unified-identity.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-queue-routing.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-persona-packs.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-speaker-persona.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-persona-pipeline.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-relationship-gate.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-relationship.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-enforcer.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-identity-prune.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-kazee-message-id.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-delivery-contract.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-run-lock.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-web-sessions.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-telegram-poll-recovery.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-telegram-409-grace.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-telegram-conflict-heal-guard.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-single-instance.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-streaming-split.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-spaces-autoenable.js"
|
|
13
|
+
"test": "OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node -e \"require('./vault'); console.log('OK')\" && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-runner-watchdog-static.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-usage-accounting.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-engine.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-graph.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-discoverer.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-query.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-walker-score.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-score-gate.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-cokept-warming.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-project-transcripts-smoke.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-abilities.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-extraction.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-couse.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-transfer.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-tiers.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-merge-guard.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-learning-e2e.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-pack-nesting.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-pack-search.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-extractive-injection.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-journal-dedupe.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-read-signal.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tools.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-graph.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-guard.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tooling-mode.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-manifest.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-evolution.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-approval-async.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-unified-identity.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-queue-routing.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-persona-packs.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-speaker-persona.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-persona-pipeline.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-relationship-gate.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-relationship.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-enforcer.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-identity-prune.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-kazee-message-id.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-delivery-contract.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-run-lock.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-web-sessions.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-telegram-poll-recovery.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-telegram-409-grace.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-telegram-conflict-heal-guard.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-single-instance.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-streaming-split.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-spaces-autoenable.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-say-loopback.js"
|
|
14
14
|
},
|
|
15
15
|
"files": [
|
|
16
16
|
"bot.js",
|
|
@@ -179,7 +179,23 @@ assert.match(pkg.description, /provider-agnostic coding-agent harness/i);
|
|
|
179
179
|
// v3.1.6 run-lifecycle fix itself passed there). Audited all 13 fake-agent
|
|
180
180
|
// test files and hardened the three racy ones (utility-policy, enforcer,
|
|
181
181
|
// subagent): wait on boot captures / generous deadlines, never tight sleeps.
|
|
182
|
-
|
|
182
|
+
// 3.1.8 approved by Sumeet 2026-07-21 ("Yeh"): pod voice-note fix. Pods ship
|
|
183
|
+
// /usr/bin/ffmpeg but never set FFMPEG=, so every TTS route silently no-opped,
|
|
184
|
+
// and the system-prompt voice flag still gated on local Whisper — hosted bots
|
|
185
|
+
// told users voice notes were disabled despite a working speech gateway.
|
|
186
|
+
// Fix: FFMPEG PATH discovery in config, media.canSpeak() drives the prompt
|
|
187
|
+
// flag, and a new `open-claudia say "<text>"` verb synthesizes + sends a
|
|
188
|
+
// voice note on request.
|
|
189
|
+
// 3.1.9 was cut for the say-loopback voice fix but never published: its build
|
|
190
|
+
// failed on this very version pin (still 3.1.8), so the docker stage was
|
|
191
|
+
// skipped and no image was built. Never retag a failed version — re-cut as +1.
|
|
192
|
+
// 3.1.10 approved by Sumeet 2026-07-21: re-cut of 3.1.9. The say-loopback fix —
|
|
193
|
+
// `say` synthesized audio inside the credential-stripped provider subprocess
|
|
194
|
+
// (no speech token), so hosted pods reported "no TTS route" while the parent
|
|
195
|
+
// bot process advertised voice as enabled. Synthesis moves into the credentialed
|
|
196
|
+
// bot process behind a loopback "say" kind; the CLI now sends only text; the R8
|
|
197
|
+
// external-contact egress guard is preserved. Plus this version-pin bump.
|
|
198
|
+
assert.strictEqual(pkg.version, "3.1.10", "release version must remain unchanged without explicit approval");
|
|
183
199
|
for (const keyword of ["claude", "codex", "coding-agent", "provider-agnostic"]) {
|
|
184
200
|
assert.ok(pkg.keywords.includes(keyword), `package keyword missing: ${keyword}`);
|
|
185
201
|
}
|