@livx.cc/agentx 0.97.5 → 0.97.9
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 +25 -6
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +9 -0
- package/dist/index.js +22 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -4585,6 +4585,8 @@ init_logging();
|
|
|
4585
4585
|
// src/voice/spokenSplitter.ts
|
|
4586
4586
|
var OPEN = "<spoken>";
|
|
4587
4587
|
var CLOSE = "</spoken>";
|
|
4588
|
+
var CLOSERS = `"')]}\xBB\u201D\u2019`;
|
|
4589
|
+
var hasSpeech = (s) => /[\p{L}\p{N}]/u.test(s);
|
|
4588
4590
|
var SentenceCoalescer = class _SentenceCoalescer {
|
|
4589
4591
|
buf = "";
|
|
4590
4592
|
static isEnd(c) {
|
|
@@ -4595,14 +4597,15 @@ var SentenceCoalescer = class _SentenceCoalescer {
|
|
|
4595
4597
|
let cut = -1;
|
|
4596
4598
|
for (let i = 0; i < this.buf.length; i++) if (_SentenceCoalescer.isEnd(this.buf[i])) cut = i;
|
|
4597
4599
|
if (cut < 0) return "";
|
|
4600
|
+
if (this.buf[cut] !== "\n") while (cut + 1 < this.buf.length && CLOSERS.includes(this.buf[cut + 1])) cut++;
|
|
4598
4601
|
const ready = this.buf.slice(0, cut + 1).trim();
|
|
4599
4602
|
this.buf = this.buf.slice(cut + 1);
|
|
4600
|
-
return ready;
|
|
4603
|
+
return hasSpeech(ready) ? ready : "";
|
|
4601
4604
|
}
|
|
4602
4605
|
flush() {
|
|
4603
4606
|
const s = this.buf.trim();
|
|
4604
4607
|
this.buf = "";
|
|
4605
|
-
return s;
|
|
4608
|
+
return hasSpeech(s) ? s : "";
|
|
4606
4609
|
}
|
|
4607
4610
|
};
|
|
4608
4611
|
var SpokenSplitter = class {
|
|
@@ -4945,6 +4948,19 @@ Today's date: ${(/* @__PURE__ */ new Date()).toDateString()}.`;
|
|
|
4945
4948
|
rec.controller.abort();
|
|
4946
4949
|
return `Task ${rec.id} (${rec.label}) cancelled.`;
|
|
4947
4950
|
}
|
|
4951
|
+
/** Barge-in: the user took the floor while task(s) were running. Suppress those tasks' remaining SPOKEN
|
|
4952
|
+
* delivery so a superseded topic never talks over the new one (the debt-after-jokes regression). The
|
|
4953
|
+
* tasks keep running and still fold their result into the transcript — recoverable, just not spoken.
|
|
4954
|
+
* Returns the parked ids (for logging). Does NOT cancel: that's a deliberate reflex/user action. */
|
|
4955
|
+
parkInFlightDeliveries() {
|
|
4956
|
+
const parked = [];
|
|
4957
|
+
for (const rec of this.tasks.values())
|
|
4958
|
+
if (rec.status === "running" && !rec.deliveryParked) {
|
|
4959
|
+
rec.deliveryParked = true;
|
|
4960
|
+
parked.push(rec.id);
|
|
4961
|
+
}
|
|
4962
|
+
return parked;
|
|
4963
|
+
}
|
|
4948
4964
|
/** Resolve when all queued voice turns AND all in-flight worker tasks have settled (tests, graceful shutdown). */
|
|
4949
4965
|
async idle() {
|
|
4950
4966
|
while (true) {
|
|
@@ -5042,7 +5058,7 @@ ${recent}` : brief) + verify + deliverContract;
|
|
|
5042
5058
|
};
|
|
5043
5059
|
const splitter = new SpokenSplitter();
|
|
5044
5060
|
const speak = (seg) => {
|
|
5045
|
-
if (seg) o.host?.notify?.({ kind: "speak_utterance", message: seg });
|
|
5061
|
+
if (seg && !this.tasks.get(id)?.deliveryParked) o.host?.notify?.({ kind: "speak_utterance", message: seg });
|
|
5046
5062
|
};
|
|
5047
5063
|
const coalescer = new SentenceCoalescer();
|
|
5048
5064
|
const feedSpoken = (s) => {
|
|
@@ -5263,9 +5279,9 @@ Another agent just implemented the above. Independently check the CURRENT state
|
|
|
5263
5279
|
return this.queueRevoice(this.integrationPrompt(rec, "incomplete", res.text, res.finishReason), true);
|
|
5264
5280
|
}
|
|
5265
5281
|
const tail = rec.splitter?.flush();
|
|
5266
|
-
if (tail?.spoken) this.options.host?.notify?.({ kind: "speak_utterance", message: tail.spoken });
|
|
5282
|
+
if (tail?.spoken && !rec.deliveryParked) this.options.host?.notify?.({ kind: "speak_utterance", message: tail.spoken });
|
|
5267
5283
|
if (res.text.trim()) this.voice.transcript.push({ role: "assistant", content: res.text });
|
|
5268
|
-
if (!rec.splitter?.spokeAny && res.text.trim())
|
|
5284
|
+
if (!rec.splitter?.spokeAny && res.text.trim() && !rec.deliveryParked)
|
|
5269
5285
|
this.options.host?.notify?.({ kind: "speak_utterance", message: res.text });
|
|
5270
5286
|
}
|
|
5271
5287
|
onWorkerFailed(id, err2) {
|
|
@@ -5822,7 +5838,7 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
5822
5838
|
* If nothing is currently speaking it plays immediately; otherwise it queues and plays after the
|
|
5823
5839
|
* current utterance fully ends (settle → pumpQueue) — never spliced into an open reflex utterance. */
|
|
5824
5840
|
enqueueUtterance(text) {
|
|
5825
|
-
if (!text ||
|
|
5841
|
+
if (!text || !/[\p{L}\p{N}]/u.test(text)) return;
|
|
5826
5842
|
this.uttQueue.push(text);
|
|
5827
5843
|
if (!this.speaking) this.pumpQueue();
|
|
5828
5844
|
}
|
|
@@ -13349,8 +13365,11 @@ ${out}
|
|
|
13349
13365
|
// voiceEchoEnd closes the open echo line; '\r\x1b[0J' wipes the stale prompt/footer before the
|
|
13350
13366
|
// notice — every other async-chrome writer does this, and without it "✋ interrupted" overprints
|
|
13351
13367
|
// the footer's leading chars (the "interrupted% ctx" glue).
|
|
13368
|
+
// Barge-in also parks any in-flight task's spoken delivery so a superseded topic can't keep talking
|
|
13369
|
+
// over the user's new one (debt-after-jokes regression) — the result still lands in the transcript.
|
|
13352
13370
|
onBargeIn: (phase) => {
|
|
13353
13371
|
activeTurn?.abort();
|
|
13372
|
+
dx?.parkInFlightDeliveries();
|
|
13354
13373
|
voiceEchoEnd();
|
|
13355
13374
|
if (phase === "speaking") err("\r\x1B[0J" + yellow(" \u270B interrupted\n"));
|
|
13356
13375
|
},
|