@livx.cc/agentx 0.96.13 → 0.96.15
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/dist/cli.js +64 -1
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js.map +1 -1
- package/dist/native/mic-aec.swift +12 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -7147,6 +7147,23 @@ function resolveAecBinary() {
|
|
|
7147
7147
|
}
|
|
7148
7148
|
return bin;
|
|
7149
7149
|
}
|
|
7150
|
+
function openMicSettings() {
|
|
7151
|
+
if (process.platform !== "darwin") return;
|
|
7152
|
+
try {
|
|
7153
|
+
spawnSync2("open", ["x-apple.systempreferences:com.apple.preference.security?Privacy_Microphone"]);
|
|
7154
|
+
} catch {
|
|
7155
|
+
}
|
|
7156
|
+
}
|
|
7157
|
+
function micPermissionStatus() {
|
|
7158
|
+
const bin = resolveAecBinary();
|
|
7159
|
+
if (!bin) return null;
|
|
7160
|
+
const r = spawnSync2(bin, ["--check-mic"], { encoding: "utf8" });
|
|
7161
|
+
const out = (r.stdout ?? "").trim();
|
|
7162
|
+
if (out === "authorized") return "authorized";
|
|
7163
|
+
if (out === "denied" || out === "restricted") return "denied";
|
|
7164
|
+
if (out === "notDetermined") return "notDetermined";
|
|
7165
|
+
return null;
|
|
7166
|
+
}
|
|
7150
7167
|
function aecUnavailableHint() {
|
|
7151
7168
|
if (process.env.MIC_AEC === "0" || process.platform !== "darwin") return null;
|
|
7152
7169
|
if (resolveAecBinary()) return null;
|
|
@@ -7200,8 +7217,10 @@ var AecDuplexAudio = class {
|
|
|
7200
7217
|
}
|
|
7201
7218
|
bin;
|
|
7202
7219
|
aec = true;
|
|
7220
|
+
onFatal;
|
|
7203
7221
|
proc = null;
|
|
7204
7222
|
stopped = false;
|
|
7223
|
+
micDenied = false;
|
|
7205
7224
|
bytesWritten = 0;
|
|
7206
7225
|
startedAt = 0;
|
|
7207
7226
|
// --- AudioSource ---
|
|
@@ -7214,7 +7233,17 @@ var AecDuplexAudio = class {
|
|
|
7214
7233
|
});
|
|
7215
7234
|
this.proc.stdout.on("data", (chunk) => onChunk(chunk));
|
|
7216
7235
|
this.proc.stderr.on("data", (d) => {
|
|
7217
|
-
for (const ln of String(d).split("\n"))
|
|
7236
|
+
for (const ln of String(d).split("\n")) {
|
|
7237
|
+
const s = ln.trim();
|
|
7238
|
+
if (!s) continue;
|
|
7239
|
+
if (/mic access granted:\s*false/i.test(s)) {
|
|
7240
|
+
if (!this.micDenied) {
|
|
7241
|
+
this.micDenied = true;
|
|
7242
|
+
openMicSettings();
|
|
7243
|
+
this.onFatal?.("microphone permission denied \u2014 enable it in System Settings \u2192 Privacy & Security \u2192 Microphone for your terminal, then restart it");
|
|
7244
|
+
}
|
|
7245
|
+
} else log16.debug(`mic-aec: ${s}`);
|
|
7246
|
+
}
|
|
7218
7247
|
});
|
|
7219
7248
|
}
|
|
7220
7249
|
stop() {
|
|
@@ -7314,6 +7343,7 @@ var VoiceIOOptions = class extends VoiceEngineOptions {
|
|
|
7314
7343
|
cartesiaVoiceId = process.env.CARTESIA_VOICE_ID ?? "";
|
|
7315
7344
|
};
|
|
7316
7345
|
var VoiceIO = class extends VoiceEngine {
|
|
7346
|
+
duplexSource;
|
|
7317
7347
|
constructor(options) {
|
|
7318
7348
|
const o = { ...new VoiceIOOptions(), ...options };
|
|
7319
7349
|
const bin = !o.stt || !o.player ? resolveAecBinary() : null;
|
|
@@ -7328,6 +7358,12 @@ var VoiceIO = class extends VoiceEngine {
|
|
|
7328
7358
|
overlapEnergyHold: process.env.OVERLAP_ENERGY_HOLD === "1" || o.overlapEnergyHold
|
|
7329
7359
|
// textless residue pre-pause: opt-in (hiccup source)
|
|
7330
7360
|
});
|
|
7361
|
+
this.duplexSource = duplex;
|
|
7362
|
+
}
|
|
7363
|
+
/** Host hook for an unrecoverable audio-source failure (e.g. mic permission denied). Only the duplex
|
|
7364
|
+
* AEC source can hit it; a no-op otherwise. */
|
|
7365
|
+
set onFatal(fn) {
|
|
7366
|
+
if (this.duplexSource) this.duplexSource.onFatal = fn;
|
|
7331
7367
|
}
|
|
7332
7368
|
/** ready = keys present (AEC vs heuristic is decided at start()) */
|
|
7333
7369
|
static available(env = process.env) {
|
|
@@ -12015,6 +12051,22 @@ ${task}`;
|
|
|
12015
12051
|
const keys = ["ANTHROPIC_API_KEY", "OPENAI_API_KEY", "GOOGLE_API_KEY", "GROQ_API_KEY"].filter((k) => process.env[k]);
|
|
12016
12052
|
keys.length ? ok(`provider keys: ${keys.join(", ")}`) : bad("no provider keys set (ANTHROPIC_API_KEY / OPENAI_API_KEY / GOOGLE_API_KEY / GROQ_API_KEY)");
|
|
12017
12053
|
process.env.BODIFY_API_KEY && process.env.BODIFY_APP_ID ? ok(`bodify secrets: ${process.env.BODIFY_APP_ID}`) : warn("bodify secrets: not configured (set BODIFY_API_KEY + BODIFY_APP_ID)");
|
|
12054
|
+
{
|
|
12055
|
+
const { spawnSync: spawnSync7 } = await import("child_process");
|
|
12056
|
+
const has = (cmd) => spawnSync7("which", [cmd]).status === 0;
|
|
12057
|
+
if (!VoiceIO.available()) warn("voice: keys missing (SONIOX_API_KEY / CARTESIA_API_KEY / CARTESIA_VOICE_ID) \u2014 voice disabled");
|
|
12058
|
+
else {
|
|
12059
|
+
ok("voice: keys present");
|
|
12060
|
+
has("ffmpeg") ? ok("voice: ffmpeg installed") : bad("voice: ffmpeg missing \u2014 `brew install ffmpeg`");
|
|
12061
|
+
if (process.platform === "darwin") {
|
|
12062
|
+
has("swiftc") ? ok("voice: swiftc present (AEC echo cancellation)") : warn("voice: no swiftc \u2014 `xcode-select --install` for echo cancellation (else heuristic tier)");
|
|
12063
|
+
const mic = micPermissionStatus();
|
|
12064
|
+
if (mic === "authorized") ok("voice: microphone permission granted");
|
|
12065
|
+
else if (mic === "denied") bad("voice: microphone permission DENIED \u2014 System Settings \u2192 Privacy & Security \u2192 Microphone, then restart your terminal");
|
|
12066
|
+
else if (mic === "notDetermined") warn("voice: microphone permission not yet granted \u2014 you'll be prompted on first --voice");
|
|
12067
|
+
}
|
|
12068
|
+
}
|
|
12069
|
+
}
|
|
12018
12070
|
const info = getModelInfo(work.model);
|
|
12019
12071
|
info?.pricing ? ok(`model ${work.model} \u2014 priced (${info.pricing.inputCostPer1K}/${info.pricing.outputCostPer1K} per 1k in/out)`) : warn(`model ${work.model} \u2014 no pricing in the catalog (costs will show ~$0; verify the id)`);
|
|
12020
12072
|
const cfgFiles = ["ts", "js", "json"].flatMap((e) => [`${cwd}/.agent/config.${e}`, `${homedir9()}/.agent/config.${e}`]).filter((p) => existsSync9(p));
|
|
@@ -13068,6 +13120,17 @@ ${out}
|
|
|
13068
13120
|
}).finally(() => editorRef?.redrawNow());
|
|
13069
13121
|
}
|
|
13070
13122
|
});
|
|
13123
|
+
voiceIO.onFatal = (msg) => {
|
|
13124
|
+
err(yellow(`
|
|
13125
|
+
\u26A0 voice off \u2014 ${msg}
|
|
13126
|
+
`));
|
|
13127
|
+
if (voiceIO) {
|
|
13128
|
+
voiceIO.stop();
|
|
13129
|
+
voiceIO = void 0;
|
|
13130
|
+
voicePartial = "";
|
|
13131
|
+
editorRef?.redrawNow();
|
|
13132
|
+
}
|
|
13133
|
+
};
|
|
13071
13134
|
try {
|
|
13072
13135
|
await voiceIO.start();
|
|
13073
13136
|
const inDev = voiceIO.usingAec ? detectedInputDevice() : null;
|