@livx.cc/agentx 0.99.102 → 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 +56 -30
- package/dist/cli.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -9331,7 +9331,14 @@ var OverlapJudge = class {
|
|
|
9331
9331
|
};
|
|
9332
9332
|
function overlapModelFor(ai, env = process.env) {
|
|
9333
9333
|
const model = env.AGENTX_CHANNEL_DECIDER_MODEL || void 0;
|
|
9334
|
-
|
|
9334
|
+
if (!model && !env.GROQ_API_KEY) return void 0;
|
|
9335
|
+
const m = fastOverlapModel(ai, model);
|
|
9336
|
+
const t0 = performance.now();
|
|
9337
|
+
void m("yeah", AbortSignal.timeout(1e4)).then(
|
|
9338
|
+
() => log19.debug(`fast model warm in ${Math.round(performance.now() - t0)}ms`),
|
|
9339
|
+
(e) => log19.warn(`fast model warm-up failed (the first overlap verdict may be late): ${e?.message ?? e}`)
|
|
9340
|
+
);
|
|
9341
|
+
return m;
|
|
9335
9342
|
}
|
|
9336
9343
|
var softBarge = (mode) => mode !== "turn";
|
|
9337
9344
|
|
|
@@ -13606,14 +13613,8 @@ function bindAckInstinct(v, instinct) {
|
|
|
13606
13613
|
}
|
|
13607
13614
|
function bindSecondChannel(v, sc, live) {
|
|
13608
13615
|
const o = v.options;
|
|
13609
|
-
const { onPartial, onUtterance, onState, onBargeIn
|
|
13610
|
-
const { show } = sc.options;
|
|
13616
|
+
const { onPartial, onUtterance, onState, onBargeIn } = o;
|
|
13611
13617
|
let own = false;
|
|
13612
|
-
let blip = "";
|
|
13613
|
-
o.onBackchannel = (p) => {
|
|
13614
|
-
blip = p;
|
|
13615
|
-
onBackchannel(p);
|
|
13616
|
-
};
|
|
13617
13618
|
const take = v.takeInterruptedReply.bind(v);
|
|
13618
13619
|
v.takeInterruptedReply = () => {
|
|
13619
13620
|
const r = take();
|
|
@@ -13621,7 +13622,7 @@ function bindSecondChannel(v, sc, live) {
|
|
|
13621
13622
|
return r.queued.length ? { ...r, full: "", heard: "" } : null;
|
|
13622
13623
|
};
|
|
13623
13624
|
o.onPartial = (t) => {
|
|
13624
|
-
if (live() && !v.audible) sc.onInterim(t);
|
|
13625
|
+
if (live() && (own || !v.audible)) sc.onInterim(t);
|
|
13625
13626
|
onPartial(t);
|
|
13626
13627
|
};
|
|
13627
13628
|
o.onUtterance = ((t, meta) => {
|
|
@@ -13644,25 +13645,34 @@ function bindSecondChannel(v, sc, live) {
|
|
|
13644
13645
|
if (live()) sc.noteReply(t);
|
|
13645
13646
|
return speakDelta(t, ...rest);
|
|
13646
13647
|
});
|
|
13648
|
+
Object.defineProperty(sc.options, "aec", { get: () => v.usingAec, set: () => {
|
|
13649
|
+
}, enumerable: true, configurable: true });
|
|
13647
13650
|
Object.assign(sc.options, {
|
|
13648
|
-
aec: v.usingAec,
|
|
13649
13651
|
speak: (text, kind) => {
|
|
13650
13652
|
if (!live() || v.audible) return false;
|
|
13651
|
-
if (kind === "backchannel")
|
|
13652
|
-
if (sc.language !== "en") return false;
|
|
13653
|
-
blip = "";
|
|
13654
|
-
v.fireBackchannel();
|
|
13655
|
-
return;
|
|
13656
|
-
}
|
|
13653
|
+
if (kind === "backchannel") return speakBlip(v, text);
|
|
13657
13654
|
own = true;
|
|
13658
13655
|
v.speakFiller(text);
|
|
13659
13656
|
return v.awaitIdle().finally(() => {
|
|
13660
13657
|
own = false;
|
|
13661
13658
|
});
|
|
13662
|
-
}
|
|
13663
|
-
show: (text, kind) => show(kind === "backchannel" && blip ? blip : text, kind)
|
|
13659
|
+
}
|
|
13664
13660
|
});
|
|
13665
13661
|
}
|
|
13662
|
+
function speakBlip(v, text) {
|
|
13663
|
+
const E = VoiceEngine;
|
|
13664
|
+
const pool = E.BACKCHANNELS;
|
|
13665
|
+
E.BACKCHANNELS = [text];
|
|
13666
|
+
try {
|
|
13667
|
+
v.fireBackchannel();
|
|
13668
|
+
return true;
|
|
13669
|
+
} catch (e) {
|
|
13670
|
+
log28.warn(`backchannel blip failed \u2014 line dropped: ${e.message}`);
|
|
13671
|
+
return false;
|
|
13672
|
+
} finally {
|
|
13673
|
+
E.BACKCHANNELS = pool;
|
|
13674
|
+
}
|
|
13675
|
+
}
|
|
13666
13676
|
function channelInput(sc, m) {
|
|
13667
13677
|
if (m.type === "interim") {
|
|
13668
13678
|
if (typeof m.aec === "boolean") sc.options.aec = m.aec;
|
|
@@ -13917,8 +13927,10 @@ function requestOpener(text) {
|
|
|
13917
13927
|
return ASK.includes(w[0]) || WANT.test(w.slice(0, 4).join(" ")) || new RegExp("^\u05EA\\p{Script=Hebrew}{2,}$", "u").test(w[0]);
|
|
13918
13928
|
}
|
|
13919
13929
|
function pendingQuestion(text, lastWords = 6) {
|
|
13920
|
-
const
|
|
13921
|
-
|
|
13930
|
+
const all = text.toLowerCase().replace(/[^\p{L}\p{N}'\s.!?]+/gu, " ");
|
|
13931
|
+
const sentence = all.split(/[.!?]/).at(-1).split(/\s+/).filter(Boolean);
|
|
13932
|
+
const t = sentence.slice(-lastWords).join(" ");
|
|
13933
|
+
return /\b(can|could|would|will|do|does|did|is|are|should)\s+(you|we|i|it|there|they|he|she)\b/.test(t) || /(^|\s)(האם|אתה יכול|את יכולה)(\s|$)/u.test(t) || /\b(what|why|how|where|when|who|which)\s+(do|does|did|is|are|should|would|can|could|will)\s+(you|we|i|it|they|he|she)\b/.test(sentence.slice(-4 * lastWords).join(" "));
|
|
13922
13934
|
}
|
|
13923
13935
|
var SCRIPTS = [
|
|
13924
13936
|
[new RegExp("\\p{Script=Hebrew}", "gu"), "he"],
|
|
@@ -14194,10 +14206,13 @@ var CHANNEL_MODES = {
|
|
|
14194
14206
|
generateFor: ["backchannel", "bridge"],
|
|
14195
14207
|
reactionGapMs: 6e3,
|
|
14196
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.
|
|
14197
14211
|
backchannelMinWords: 5,
|
|
14198
|
-
backchannelGapMs:
|
|
14199
|
-
minGapMs:
|
|
14200
|
-
backchannelPauseMs:
|
|
14212
|
+
backchannelGapMs: 3e3,
|
|
14213
|
+
minGapMs: 2500,
|
|
14214
|
+
backchannelPauseMs: 500,
|
|
14215
|
+
maxPerWindow: 6
|
|
14201
14216
|
}
|
|
14202
14217
|
};
|
|
14203
14218
|
var CHANNEL_MODE_NAMES = Object.keys(CHANNEL_MODES);
|
|
@@ -14246,6 +14261,9 @@ var SecondChannelOptions = class {
|
|
|
14246
14261
|
/** Backchannel after backchannel needs this gap (~4-6 cues in a 30s monologue — Para phone call 2026-10-13: 5s left
|
|
14247
14262
|
* one cue in a long statement). */
|
|
14248
14263
|
backchannelGapMs = 3e3;
|
|
14264
|
+
/** Soft cap: at most this many backchannels in any `windowMs` (0 = none). */
|
|
14265
|
+
maxPerWindow = 0;
|
|
14266
|
+
windowMs = 3e4;
|
|
14249
14267
|
/** The user resumes within this long of their last words after a line of theirs was dispatched: the same statement goes
|
|
14250
14268
|
* on (an STT endpoint mid-monologue) — its words still count toward the backchannel guards, and it is no fresh request. */
|
|
14251
14269
|
continueMs = 3e3;
|
|
@@ -14390,12 +14408,7 @@ var SecondChannel = class {
|
|
|
14390
14408
|
}
|
|
14391
14409
|
get o() {
|
|
14392
14410
|
const o = this.options;
|
|
14393
|
-
|
|
14394
|
-
if (!this.degradedLogged) {
|
|
14395
|
-
this.degradedLogged = true;
|
|
14396
|
-
log32.info("chatty mode without echo cancellation \u2014 normal timing (our lines would re-trigger the mic)");
|
|
14397
|
-
}
|
|
14398
|
-
return { ...o, ...this.normalTiming };
|
|
14411
|
+
return o.mode !== "chatty" || o.aec ? o : { ...o, ...this.normalTiming };
|
|
14399
14412
|
}
|
|
14400
14413
|
/** The user's live partial for the current turn (the whole partial, not a delta). */
|
|
14401
14414
|
onInterim(text) {
|
|
@@ -14533,10 +14546,17 @@ var SecondChannel = class {
|
|
|
14533
14546
|
allowed(k) {
|
|
14534
14547
|
if (!this.o.kinds.includes(k)) return false;
|
|
14535
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;
|
|
14536
|
-
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();
|
|
14537
14550
|
if (k === "filler") return !this.userSpeaking && this.main === "idle";
|
|
14538
14551
|
return (this.main === "queued" || this.main === "thinking") && !this.bridged;
|
|
14539
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
|
+
}
|
|
14540
14560
|
/** 0ms rule: the candidate line for this trigger, or silence. */
|
|
14541
14561
|
rule(trigger, pauseMs = 0, breath = false, cue = null) {
|
|
14542
14562
|
const w = words2(this.heard);
|
|
@@ -14579,6 +14599,11 @@ var SecondChannel = class {
|
|
|
14579
14599
|
const n0 = words2(this.heard).length;
|
|
14580
14600
|
this.decidedAt = n0;
|
|
14581
14601
|
if (!(this.o.anticipate && this.plan && trigger === "interim")) cue = null;
|
|
14602
|
+
const opts = this.options;
|
|
14603
|
+
if (opts.mode === "chatty" && !opts.aec && !this.degradedLogged) {
|
|
14604
|
+
this.degradedLogged = true;
|
|
14605
|
+
log32.info("chatty mode without echo cancellation \u2014 normal timing (our lines would re-trigger the mic)");
|
|
14606
|
+
}
|
|
14582
14607
|
const rule = this.rule(trigger, pauseMs, breath, cue);
|
|
14583
14608
|
if (rule === "silence") return;
|
|
14584
14609
|
this.busy = true;
|
|
@@ -14658,6 +14683,7 @@ var SecondChannel = class {
|
|
|
14658
14683
|
if (r === false) return false;
|
|
14659
14684
|
this.lastAt = this.o.now();
|
|
14660
14685
|
this.lastKind = kind;
|
|
14686
|
+
if (kind === "backchannel") this.bcTimes.push(this.lastAt);
|
|
14661
14687
|
if (kind === "bridge") this.bridged = true;
|
|
14662
14688
|
this.recent = [...this.recent, text].slice(-this.o.avoidLines);
|
|
14663
14689
|
this.live = text.trim();
|