@livx.cc/agentx 0.99.103 → 0.99.104

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
@@ -14206,10 +14206,13 @@ var CHANNEL_MODES = {
14206
14206
  generateFor: ["backchannel", "bridge"],
14207
14207
  reactionGapMs: 6e3,
14208
14208
  generateBudgetMs: 400,
14209
+ // A breath must outlast the word cadence: at ~2.5 words/s interims arrive every ~400ms, so a 250ms "breath" fired after
14210
+ // every word and only the 1.2s gaps held it (~25 lines per 30s, live probe). Lines ≥2.5s apart (light after light 3s), ≤6 in any 30s.
14209
14211
  backchannelMinWords: 5,
14210
- backchannelGapMs: 1200,
14211
- minGapMs: 1200,
14212
- backchannelPauseMs: 250
14212
+ backchannelGapMs: 3e3,
14213
+ minGapMs: 2500,
14214
+ backchannelPauseMs: 500,
14215
+ maxPerWindow: 6
14213
14216
  }
14214
14217
  };
14215
14218
  var CHANNEL_MODE_NAMES = Object.keys(CHANNEL_MODES);
@@ -14258,6 +14261,9 @@ var SecondChannelOptions = class {
14258
14261
  /** Backchannel after backchannel needs this gap (~4-6 cues in a 30s monologue — Para phone call 2026-10-13: 5s left
14259
14262
  * one cue in a long statement). */
14260
14263
  backchannelGapMs = 3e3;
14264
+ /** Soft cap: at most this many backchannels in any `windowMs` (0 = none). */
14265
+ maxPerWindow = 0;
14266
+ windowMs = 3e4;
14261
14267
  /** The user resumes within this long of their last words after a line of theirs was dispatched: the same statement goes
14262
14268
  * on (an STT endpoint mid-monologue) — its words still count toward the backchannel guards, and it is no fresh request. */
14263
14269
  continueMs = 3e3;
@@ -14540,10 +14546,17 @@ var SecondChannel = class {
14540
14546
  allowed(k) {
14541
14547
  if (!this.o.kinds.includes(k)) return false;
14542
14548
  if (this.audioLocked || this.inflight || this.o.now() - this.lastAt < this.o.minGapMs || k === this.lastKind && (k !== "backchannel" || this.o.now() - this.lastAt < this.o.backchannelGapMs)) return false;
14543
- if (k === "backchannel") return this.o.aec && this.userSpeaking && this.main === "idle";
14549
+ if (k === "backchannel") return this.o.aec && this.userSpeaking && this.main === "idle" && !this.capped();
14544
14550
  if (k === "filler") return !this.userSpeaking && this.main === "idle";
14545
14551
  return (this.main === "queued" || this.main === "thinking") && !this.bridged;
14546
14552
  }
14553
+ bcTimes = [];
14554
+ capped() {
14555
+ if (!this.o.maxPerWindow) return false;
14556
+ const from = this.o.now() - this.o.windowMs;
14557
+ this.bcTimes = this.bcTimes.filter((t) => t > from);
14558
+ return this.bcTimes.length >= this.o.maxPerWindow;
14559
+ }
14547
14560
  /** 0ms rule: the candidate line for this trigger, or silence. */
14548
14561
  rule(trigger, pauseMs = 0, breath = false, cue = null) {
14549
14562
  const w = words2(this.heard);
@@ -14670,6 +14683,7 @@ var SecondChannel = class {
14670
14683
  if (r === false) return false;
14671
14684
  this.lastAt = this.o.now();
14672
14685
  this.lastKind = kind;
14686
+ if (kind === "backchannel") this.bcTimes.push(this.lastAt);
14673
14687
  if (kind === "bridge") this.bridged = true;
14674
14688
  this.recent = [...this.recent, text].slice(-this.o.avoidLines);
14675
14689
  this.live = text.trim();