@livx.cc/agentx 0.96.14 → 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 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 ---
@@ -7217,8 +7236,13 @@ var AecDuplexAudio = class {
7217
7236
  for (const ln of String(d).split("\n")) {
7218
7237
  const s = ln.trim();
7219
7238
  if (!s) continue;
7220
- if (/mic access granted:\s*false/i.test(s)) log16.warn("mic-aec: microphone permission DENIED \u2014 grant it in System Settings \u2192 Privacy & Security \u2192 Microphone for your terminal, then restart it");
7221
- else log16.debug(`mic-aec: ${s}`);
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}`);
7222
7246
  }
7223
7247
  });
7224
7248
  }
@@ -7319,6 +7343,7 @@ var VoiceIOOptions = class extends VoiceEngineOptions {
7319
7343
  cartesiaVoiceId = process.env.CARTESIA_VOICE_ID ?? "";
7320
7344
  };
7321
7345
  var VoiceIO = class extends VoiceEngine {
7346
+ duplexSource;
7322
7347
  constructor(options) {
7323
7348
  const o = { ...new VoiceIOOptions(), ...options };
7324
7349
  const bin = !o.stt || !o.player ? resolveAecBinary() : null;
@@ -7333,6 +7358,12 @@ var VoiceIO = class extends VoiceEngine {
7333
7358
  overlapEnergyHold: process.env.OVERLAP_ENERGY_HOLD === "1" || o.overlapEnergyHold
7334
7359
  // textless residue pre-pause: opt-in (hiccup source)
7335
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;
7336
7367
  }
7337
7368
  /** ready = keys present (AEC vs heuristic is decided at start()) */
7338
7369
  static available(env = process.env) {
@@ -12020,6 +12051,22 @@ ${task}`;
12020
12051
  const keys = ["ANTHROPIC_API_KEY", "OPENAI_API_KEY", "GOOGLE_API_KEY", "GROQ_API_KEY"].filter((k) => process.env[k]);
12021
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)");
12022
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
+ }
12023
12070
  const info = getModelInfo(work.model);
12024
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)`);
12025
12072
  const cfgFiles = ["ts", "js", "json"].flatMap((e) => [`${cwd}/.agent/config.${e}`, `${homedir9()}/.agent/config.${e}`]).filter((p) => existsSync9(p));
@@ -13073,6 +13120,17 @@ ${out}
13073
13120
  }).finally(() => editorRef?.redrawNow());
13074
13121
  }
13075
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
+ };
13076
13134
  try {
13077
13135
  await voiceIO.start();
13078
13136
  const inDev = voiceIO.usingAec ? detectedInputDevice() : null;