@livx.cc/agentx 0.96.16 → 0.96.17

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
@@ -7267,22 +7267,46 @@ var AecDuplexAudio = class {
7267
7267
  this.bin = bin;
7268
7268
  }
7269
7269
  bin;
7270
- aec = true;
7270
+ /** Mutable: starts true (VPIO/AEC). Flips false if we fall back to non-VPIO capture (heuristic tier). */
7271
+ _aec = true;
7272
+ get aec() {
7273
+ return this._aec;
7274
+ }
7271
7275
  onFatal;
7272
7276
  proc = null;
7273
7277
  stopped = false;
7274
7278
  micDenied = false;
7279
+ noVpio = false;
7280
+ // currently running the non-VPIO fallback
7281
+ triedFallback = false;
7282
+ // one-shot guard
7283
+ gotChunk = false;
7284
+ // any mic audio since (re)spawn?
7285
+ onChunk = () => {
7286
+ };
7287
+ fallbackTimer = null;
7275
7288
  bytesWritten = 0;
7276
7289
  startedAt = 0;
7277
7290
  // --- AudioSource ---
7278
7291
  start(onChunk) {
7279
- this.proc = spawn2(this.bin, [], { stdio: ["pipe", "pipe", "pipe"] });
7292
+ this.onChunk = onChunk;
7293
+ this.spawnHelper();
7294
+ }
7295
+ /** (Re)spawn the helper. On the first spawn, arm a fast watchdog: if VPIO delivers NO audio within
7296
+ * ~2.5s, the VP input tap is dead on this machine (seen on macOS 26.5.x) — respawn once with
7297
+ * MIC_NO_VPIO=1 (plain capture, heuristic echo) so the mic actually works instead of starving STT. */
7298
+ spawnHelper() {
7299
+ const env = this.noVpio ? { ...process.env, MIC_NO_VPIO: "1" } : process.env;
7300
+ this.proc = spawn2(this.bin, [], { stdio: ["pipe", "pipe", "pipe"], env });
7280
7301
  this.proc.stdin.on("error", () => {
7281
7302
  });
7282
7303
  this.proc.on("exit", (c) => {
7283
7304
  if (c && !this.stopped) log16.error(`aec duplex audio exited (${c}) \u2014 check mic permission / MIC_AEC=0`);
7284
7305
  });
7285
- this.proc.stdout.on("data", (chunk) => onChunk(chunk));
7306
+ this.proc.stdout.on("data", (chunk) => {
7307
+ this.gotChunk = true;
7308
+ this.onChunk(chunk);
7309
+ });
7286
7310
  this.proc.stderr.on("data", (d) => {
7287
7311
  for (const ln of String(d).split("\n")) {
7288
7312
  const s = ln.trim();
@@ -7296,9 +7320,21 @@ var AecDuplexAudio = class {
7296
7320
  } else log16.debug(`mic-aec: ${s}`);
7297
7321
  }
7298
7322
  });
7299
- }
7300
- stop() {
7301
- this.stopped = true;
7323
+ if (!this.noVpio && !this.triedFallback) {
7324
+ this.fallbackTimer = setTimeout(() => {
7325
+ if (this.stopped || this.gotChunk) return;
7326
+ this.triedFallback = true;
7327
+ this.noVpio = true;
7328
+ this._aec = false;
7329
+ log16.warn("mic-aec: VPIO delivered no audio in 2.5s \u2014 falling back to non-VPIO capture (heuristic echo; headphones recommended)");
7330
+ this.killProc();
7331
+ this.spawnHelper();
7332
+ }, 2500);
7333
+ this.fallbackTimer.unref?.();
7334
+ }
7335
+ }
7336
+ /** Kill the current child WITHOUT marking the whole source stopped (used for the fallback respawn). */
7337
+ killProc() {
7302
7338
  const p = this.proc;
7303
7339
  this.proc = null;
7304
7340
  if (!p) return;
@@ -7310,6 +7346,11 @@ var AecDuplexAudio = class {
7310
7346
  }
7311
7347
  }, 500).unref?.();
7312
7348
  }
7349
+ stop() {
7350
+ this.stopped = true;
7351
+ if (this.fallbackTimer) clearTimeout(this.fallbackTimer);
7352
+ this.killProc();
7353
+ }
7313
7354
  // --- AudioSink (frame writer; same played/drain byte-math as the ffplay Player) ---
7314
7355
  frame(payload) {
7315
7356
  const stdin = this.proc?.stdin;