@livx.cc/agentx 0.99.111 → 0.99.113
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 +59 -6
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +8 -0
- package/dist/index.js +43 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -9328,14 +9328,40 @@ var ACK = new Set("yeah yea yes yep yup ya ok okay k right sure true totally exa
|
|
|
9328
9328
|
var LEAD = /* @__PURE__ */ new Set(["and", "so", "um", "uh", "er", "well", "oh"]);
|
|
9329
9329
|
var LEADER = /* @__PURE__ */ new Set(["yeah", "yea", "yes", "yep", "yup", "ok", "okay", "right", "sure", "alright", "mm", "mhm", "hmm", "uh-huh", "totally", "exactly", "true"]);
|
|
9330
9330
|
var PHRASES = ["got it", "i see", "makes sense", "of course", "for sure", "all right", "that's right", "that's true", "thats right", "thats true", "love it", "i know", "me too", "uh huh"];
|
|
9331
|
+
var CONTINUE = /^(?:(?:and|so|oh) )?(?:and|so|really|seriously|then what|and then|go on|keep going|ואז|באמת|נו)$/;
|
|
9332
|
+
var AGREE = new Set("yeah yea yes yep yup right sure true totally exactly definitely absolutely agreed correct indeed \u05DB\u05DF \u05E0\u05DB\u05D5\u05DF \u05D1\u05D3\u05D9\u05D5\u05E7 \u05D1\u05E8\u05D5\u05E8 \u05D1\u05D8\u05D7 \u05D0\u05DB\u05DF \u05DC\u05D2\u05DE\u05E8\u05D9 \u05DE\u05E1\u05DB\u05D9\u05DD \u05DE\u05E1\u05DB\u05D9\u05DE\u05D4 \u05DE\u05DE\u05E9".split(" "));
|
|
9333
|
+
var AGREE_PHRASES = ["of course", "for sure", "that's right", "that's true", "thats right", "thats true", "makes sense", "i agree"];
|
|
9334
|
+
var HE_ACK = new Set("\u05DB\u05DF \u05E0\u05DB\u05D5\u05DF \u05D1\u05D3\u05D9\u05D5\u05E7 \u05D1\u05E8\u05D5\u05E8 \u05D1\u05D8\u05D7 \u05D0\u05DB\u05DF \u05DC\u05D2\u05DE\u05E8\u05D9 \u05DE\u05DE\u05E9 \u05D0\u05D5\u05E7\u05D9\u05D9 \u05D0\u05D5\u05E7\u05D9 \u05E1\u05D1\u05D1\u05D4 \u05D9\u05E4\u05D4 \u05DE\u05E2\u05D5\u05DC\u05D4 \u05D5\u05D5\u05D0\u05D5 \u05D0\u05D4\u05D4 \u05D0\u05D4 \u05D4\u05DE\u05DE \u05DE\u05DE\u05DE".split(" "));
|
|
9335
|
+
function cueKind(text) {
|
|
9336
|
+
const t = norm2(text);
|
|
9337
|
+
if (!t) return null;
|
|
9338
|
+
if (CONTINUE.test(t)) return "continue";
|
|
9339
|
+
let r = t;
|
|
9340
|
+
for (const p of AGREE_PHRASES) if (r.includes(p)) return "agree";
|
|
9341
|
+
r = r.split(/[\s-]+/).filter((w) => !LEAD.has(w)).join(" ");
|
|
9342
|
+
return r.split(" ").some((w) => AGREE.has(w)) ? "agree" : null;
|
|
9343
|
+
}
|
|
9344
|
+
var ASIDES = {
|
|
9345
|
+
en: { agree: ["Oh yeah, definitely.", "Yeah, exactly.", "Right, yeah."], continue: ["Yeah, so\u2026", "So, yeah\u2026"] },
|
|
9346
|
+
he: { agree: ["\u05DB\u05DF, \u05D1\u05D3\u05D9\u05D5\u05E7.", "\u05DB\u05DF, \u05DE\u05DE\u05E9.", "\u05E0\u05DB\u05D5\u05DF, \u05DB\u05DF."], continue: ["\u05DB\u05DF, \u05D0\u05D6\u2026", "\u05D0\u05D6 \u05DB\u05DF\u2026"] }
|
|
9347
|
+
};
|
|
9348
|
+
function overlapAside(text, lang, avoid = "", pick = (n) => Math.floor(Math.random() * n)) {
|
|
9349
|
+
const kind = cueKind(text);
|
|
9350
|
+
const l = new RegExp("\\p{Script=Hebrew}", "u").test(text) ? "he" : /[a-z]/i.test(text) ? "en" : lang;
|
|
9351
|
+
const pool = kind && l ? ASIDES[l]?.[kind] : void 0;
|
|
9352
|
+
if (!pool?.length) return null;
|
|
9353
|
+
const fresh = pool.filter((x) => x !== avoid);
|
|
9354
|
+
return fresh[pick(fresh.length)] ?? null;
|
|
9355
|
+
}
|
|
9331
9356
|
function overlapRule(text) {
|
|
9357
|
+
if (CONTINUE.test(norm2(text))) return "keep";
|
|
9332
9358
|
if (/\?/.test(text)) return "cut";
|
|
9333
9359
|
let t = norm2(text);
|
|
9334
9360
|
if (!t) return void 0;
|
|
9335
9361
|
if (HARD.test(t)) return "cut";
|
|
9336
9362
|
for (const p of PHRASES) t = t.split(p).join(" ");
|
|
9337
9363
|
const words4 = t.split(/[\s-]+/).filter(Boolean);
|
|
9338
|
-
const ack = (w) => ACK.has(w) || /^(?:ha)+h?$|^(?:he)+h?$|^m+(?:hm+)?$|^h?m+$/.test(w);
|
|
9364
|
+
const ack = (w) => ACK.has(w) || HE_ACK.has(w) || /^(?:ha)+h?$|^(?:he)+h?$|^m+(?:hm+)?$|^h?m+$/.test(w);
|
|
9339
9365
|
if (words4.every(ack)) return "keep";
|
|
9340
9366
|
const lead2 = words4.findIndex((w) => !LEAD.has(w));
|
|
9341
9367
|
if (lead2 >= 0 && LEADER.has(words4[lead2]) && words4.slice(lead2).filter((w) => !ack(w) && !LEAD.has(w)).length <= 1) return "keep";
|
|
@@ -13616,6 +13642,8 @@ var VoiceCallOptions = class {
|
|
|
13616
13642
|
onMetric = () => {
|
|
13617
13643
|
};
|
|
13618
13644
|
now = () => performance.now();
|
|
13645
|
+
/** An aside (aside()) needs this long since the last one — a cue answered every time sounds scripted. */
|
|
13646
|
+
asideGapMs = 8e3;
|
|
13619
13647
|
};
|
|
13620
13648
|
var VoiceCall = class {
|
|
13621
13649
|
constructor(options) {
|
|
@@ -13659,6 +13687,7 @@ var VoiceCall = class {
|
|
|
13659
13687
|
judge;
|
|
13660
13688
|
cur;
|
|
13661
13689
|
lang = null;
|
|
13690
|
+
lastAside = { text: "", at: -Infinity };
|
|
13662
13691
|
lastAudibleAt = -Infinity;
|
|
13663
13692
|
firstFinalAt = null;
|
|
13664
13693
|
// the first final of the turn being formed (hold/merge)
|
|
@@ -13767,6 +13796,19 @@ var VoiceCall = class {
|
|
|
13767
13796
|
if (d.verdict === "cut") this.barge();
|
|
13768
13797
|
return d.verdict;
|
|
13769
13798
|
}
|
|
13799
|
+
/** The fill channel's spoken aside for an overlap judged 'keep' ("yeah" → "Oh yeah, definitely."): the host plays it
|
|
13800
|
+
* while the reply is held, then resumes the same reply (bod-voice decideOverlap's aside). ≤4 canned words, never a
|
|
13801
|
+
* claim, the cue's language; null = stay quiet (a plain listener cue, too soon after the last, or no line). It is a
|
|
13802
|
+
* fill line: never MAIN history. */
|
|
13803
|
+
aside(text) {
|
|
13804
|
+
const now4 = this.o.now();
|
|
13805
|
+
if (now4 - this.lastAside.at < this.o.asideGapMs) return null;
|
|
13806
|
+
const line = overlapAside(text, this.lang, this.lastAside.text);
|
|
13807
|
+
if (!line) return null;
|
|
13808
|
+
this.lastAside = { text: line, at: now4 };
|
|
13809
|
+
this.o.onMetric({ name: "fill", kind: "aside", text: line, lang: this.lang });
|
|
13810
|
+
return line;
|
|
13811
|
+
}
|
|
13770
13812
|
/** Did the last barge cut OUR fill line `text`? (hosts strip it from the cut reply — it is not MAIN's speech) */
|
|
13771
13813
|
spoke(text) {
|
|
13772
13814
|
return this.fill.spoke(text);
|
|
@@ -15355,7 +15397,9 @@ function bindVoiceCall(v, call, live, decideMs = 900) {
|
|
|
15355
15397
|
if (!live() || ev.kind !== "overlap_pause") return;
|
|
15356
15398
|
const heard = String(ev.text ?? "");
|
|
15357
15399
|
void call.overlap(heard).then((verdict) => {
|
|
15358
|
-
if (live())
|
|
15400
|
+
if (!live()) return;
|
|
15401
|
+
if (verdict === "keep") v.decideOverlap("keep_talking", heard, call.aside(heard) ?? void 0);
|
|
15402
|
+
else v.decideOverlap("yield", heard);
|
|
15359
15403
|
});
|
|
15360
15404
|
};
|
|
15361
15405
|
Object.defineProperty(call.fill.options, "aec", { get: () => v.usingAec, set: () => {
|
|
@@ -15457,6 +15501,8 @@ var VoiceIO = class extends VoiceEngine {
|
|
|
15457
15501
|
...o,
|
|
15458
15502
|
stt: o.stt ?? new SonioxSTT({ auth: o.sonioxApiKey, source: duplex ?? new NodeMicSource(), terms: o.sttTerms }),
|
|
15459
15503
|
tts: o.tts ?? new CartesiaTTS({ auth: o.cartesiaApiKey, voiceId: o.cartesiaVoiceId }),
|
|
15504
|
+
asideTts: o.tts ? null : new CartesiaTTS({ auth: o.cartesiaApiKey, voiceId: o.cartesiaVoiceId }),
|
|
15505
|
+
// asides (VoiceCall.aside): a 2nd client — the reply's context may still stream
|
|
15460
15506
|
player: o.player ?? duplex ?? new Player(),
|
|
15461
15507
|
bargeRmsMult: Number(process.env.BARGE_RMS_MULT || o.bargeRmsMult),
|
|
15462
15508
|
bargeRmsFloor: Number(process.env.BARGE_RMS_FLOOR || o.bargeRmsFloor),
|
|
@@ -22467,11 +22513,14 @@ function channelOptions(ai, model) {
|
|
|
22467
22513
|
onEvent: (e) => log40.debug(`channel ${e.trigger}: rule=${e.rule} choice=${e.choice ?? "-"} \u2192 ${e.acted}${e.text ? ` "${e.text}"` : ""}${e.cue ? " (planned cue)" : ""} ${e.ms}ms`)
|
|
22468
22514
|
};
|
|
22469
22515
|
}
|
|
22470
|
-
function overlapResponder(ai, mode, out) {
|
|
22516
|
+
function overlapResponder(ai, mode, out, aside) {
|
|
22471
22517
|
const judge = softBarge(mode) ? new OverlapJudge({ model: overlapModelFor(ai) }) : void 0;
|
|
22472
22518
|
return (m) => {
|
|
22473
22519
|
const text = String(m.text ?? "");
|
|
22474
|
-
void (judge ? judge.judge(text) : Promise.resolve({ verdict: "cut", by: "mode", ms: 0 })).then((d) =>
|
|
22520
|
+
void (judge ? judge.judge(text) : Promise.resolve({ verdict: "cut", by: "mode", ms: 0 })).then((d) => {
|
|
22521
|
+
const line = d.verdict === "keep" ? aside?.(text) : null;
|
|
22522
|
+
out({ type: "overlap", ...m.id !== void 0 ? { id: m.id } : {}, verdict: d.verdict, by: d.by, ms: d.ms, ...line ? { aside: line } : {} });
|
|
22523
|
+
});
|
|
22475
22524
|
};
|
|
22476
22525
|
}
|
|
22477
22526
|
async function duplexServe(args, ai, cfg, cwd) {
|
|
@@ -22600,10 +22649,14 @@ async function channelServe(args, ai, cfg) {
|
|
|
22600
22649
|
sendTurn: (text, o) => out({ type: "turn", id: o.id, text, ...o.replaces ? { replaces: o.replaces } : {} }),
|
|
22601
22650
|
abortTurn: (id) => out({ type: "abort", id }),
|
|
22602
22651
|
onMetric: (m) => {
|
|
22603
|
-
if (m.name
|
|
22652
|
+
if (m.name !== "lang") out({ type: "metric", ...m });
|
|
22604
22653
|
}
|
|
22654
|
+
// (turn + fill too: the host's log shows which turns formed and which fill lines played)
|
|
22605
22655
|
});
|
|
22606
|
-
const overlap2 = overlapResponder(ai, args.channelMode,
|
|
22656
|
+
const overlap2 = overlapResponder(ai, args.channelMode, (o) => {
|
|
22657
|
+
if (o.verdict === "cut") call.barge();
|
|
22658
|
+
out(o);
|
|
22659
|
+
}, (t) => call.aside(t));
|
|
22607
22660
|
out({ type: "ready", model, channelMode: args.channelMode, softBarge: softBarge(args.channelMode), call: true, ...notes.length ? { notes } : {} });
|
|
22608
22661
|
const rl = createInterface({ input: process.stdin });
|
|
22609
22662
|
rl.on("line", (line) => {
|