@livx.cc/agentx 0.96.17 → 0.96.18

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
@@ -5500,6 +5500,11 @@ var VoiceEngineOptions = class {
5500
5500
  * as a barge and abort the fresh turn (live: mid-sentence self-interruption + steps=1→steps=0 double
5501
5501
  * abort). Short enough that a genuine immediate barge ("no wait—") still lands right after. */
5502
5502
  bargeGraceMs = 600;
5503
+ /** Barge-in (talk over the assistant to interrupt). true = full-duplex (needs echo cancellation, or
5504
+ * the assistant's own TTS bleeds back and self-interrupts). false = HALF-DUPLEX: the engine is deaf
5505
+ * while audible (speaking + drain tail), so echo can never become a phantom turn — the right mode
5506
+ * when there's no AEC (e.g. the non-VPIO mic fallback) and no headphones. Cost: can't interrupt. */
5507
+ bargeIn = true;
5503
5508
  /** Filler phrase spoken when holding for an incomplete utterance ('' disables). */
5504
5509
  holdFiller = "";
5505
5510
  /** Called when the engine holds an incomplete utterance (host can render a visual cue). */
@@ -5593,6 +5598,10 @@ var VoiceEngine = class _VoiceEngine {
5593
5598
  get usingAec() {
5594
5599
  return this.stt.usingAec;
5595
5600
  }
5601
+ /** Flip barge-in at runtime (e.g. the mic fell back to non-VPIO → go half-duplex so echo can't leak). */
5602
+ setBargeIn(on) {
5603
+ this.options.bargeIn = on;
5604
+ }
5596
5605
  idleWaiters = [];
5597
5606
  setState(s) {
5598
5607
  if (this.state === s) return;
@@ -5744,6 +5753,7 @@ var VoiceEngine = class _VoiceEngine {
5744
5753
  }
5745
5754
  handlePartial(text) {
5746
5755
  if (this.speaking) {
5756
+ if (!this.options.bargeIn) return;
5747
5757
  if (now() < this.bargeGraceUntil) {
5748
5758
  if (!this.echoActive() || (this.usingAec ? this.genuine(text) : this.novelWords(text).length >= 1)) this.options.onPartial(text);
5749
5759
  return;
@@ -5819,7 +5829,7 @@ var VoiceEngine = class _VoiceEngine {
5819
5829
  this.stt.reset();
5820
5830
  return;
5821
5831
  }
5822
- if (this.echoActive() && (this.usingAec ? !this.genuine(text) : this.novelWords(text).length < 2)) {
5832
+ if (this.echoActive() && (!this.options.bargeIn || (this.usingAec ? !this.genuine(text) : this.novelWords(text).length < 2))) {
5823
5833
  this.stt.reset();
5824
5834
  return;
5825
5835
  }
@@ -7273,6 +7283,9 @@ var AecDuplexAudio = class {
7273
7283
  return this._aec;
7274
7284
  }
7275
7285
  onFatal;
7286
+ /** Fired once when capture degrades to the non-VPIO (no-AEC) fallback — the host switches to
7287
+ * half-duplex so the assistant's own TTS can't bleed back as a phantom turn. */
7288
+ onDegrade;
7276
7289
  proc = null;
7277
7290
  stopped = false;
7278
7291
  micDenied = false;
@@ -7290,6 +7303,12 @@ var AecDuplexAudio = class {
7290
7303
  // --- AudioSource ---
7291
7304
  start(onChunk) {
7292
7305
  this.onChunk = onChunk;
7306
+ if (process.env.MIC_NO_VPIO === "1") {
7307
+ this.noVpio = true;
7308
+ this.triedFallback = true;
7309
+ this._aec = false;
7310
+ this.onDegrade?.();
7311
+ }
7293
7312
  this.spawnHelper();
7294
7313
  }
7295
7314
  /** (Re)spawn the helper. On the first spawn, arm a fast watchdog: if VPIO delivers NO audio within
@@ -7326,7 +7345,8 @@ var AecDuplexAudio = class {
7326
7345
  this.triedFallback = true;
7327
7346
  this.noVpio = true;
7328
7347
  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)");
7348
+ log16.warn("mic-aec: VPIO delivered no audio in 2.5s \u2014 falling back to non-VPIO capture (no AEC \u2192 half-duplex, no barge-in)");
7349
+ this.onDegrade?.();
7330
7350
  this.killProc();
7331
7351
  this.spawnHelper();
7332
7352
  }, 2500);
@@ -7451,6 +7471,7 @@ var VoiceIO = class extends VoiceEngine {
7451
7471
  // textless residue pre-pause: opt-in (hiccup source)
7452
7472
  });
7453
7473
  this.duplexSource = duplex;
7474
+ if (duplex) duplex.onDegrade = () => this.setBargeIn(false);
7454
7475
  }
7455
7476
  /** Host hook for an unrecoverable audio failure — mic permission denied (duplex source) or no mic
7456
7477
  * audio at all (STT watchdog). Routed to whichever can detect it. */