@livx.cc/agentx 0.97.3 → 0.97.7

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
@@ -4585,6 +4585,29 @@ 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);
4590
+ var SentenceCoalescer = class _SentenceCoalescer {
4591
+ buf = "";
4592
+ static isEnd(c) {
4593
+ return c === "\n" || c === "." || c === "!" || c === "?" || c === "\u2026";
4594
+ }
4595
+ feed(delta) {
4596
+ if (delta) this.buf += delta;
4597
+ let cut = -1;
4598
+ for (let i = 0; i < this.buf.length; i++) if (_SentenceCoalescer.isEnd(this.buf[i])) cut = i;
4599
+ if (cut < 0) return "";
4600
+ if (this.buf[cut] !== "\n") while (cut + 1 < this.buf.length && CLOSERS.includes(this.buf[cut + 1])) cut++;
4601
+ const ready = this.buf.slice(0, cut + 1).trim();
4602
+ this.buf = this.buf.slice(cut + 1);
4603
+ return hasSpeech(ready) ? ready : "";
4604
+ }
4605
+ flush() {
4606
+ const s = this.buf.trim();
4607
+ this.buf = "";
4608
+ return hasSpeech(s) ? s : "";
4609
+ }
4610
+ };
4588
4611
  var SpokenSplitter = class {
4589
4612
  buf = "";
4590
4613
  inSpoken = false;
@@ -5024,13 +5047,19 @@ ${recent}` : brief) + verify + deliverContract;
5024
5047
  const speak = (seg) => {
5025
5048
  if (seg) o.host?.notify?.({ kind: "speak_utterance", message: seg });
5026
5049
  };
5050
+ const coalescer = new SentenceCoalescer();
5051
+ const feedSpoken = (s) => {
5052
+ const ready = coalescer.feed(s);
5053
+ if (ready) speak(ready);
5054
+ };
5055
+ const flushSpoken = () => speak(coalescer.flush());
5027
5056
  const askBridge = o.askRelay ? { ask: relayAsk } : o.host?.ask ? { ask: (q2) => o.host.ask(q2) } : {};
5028
5057
  const workerHost = {
5029
5058
  ...askBridge,
5030
5059
  notify: (ev) => {
5031
5060
  if (ev?.kind === "text_delta" && typeof ev.message === "string") {
5032
5061
  const { spoken, detail } = splitter.feed(ev.message);
5033
- speak(spoken);
5062
+ feedSpoken(spoken);
5034
5063
  if (detail.trim()) pushTail(detail.trim());
5035
5064
  return;
5036
5065
  }
@@ -5053,7 +5082,13 @@ ${recent}` : brief) + verify + deliverContract;
5053
5082
  signal: controller.signal
5054
5083
  // shared with the checker so a cancel tears down both
5055
5084
  };
5056
- const promise = new Agent(agentOpts).run(briefText).then((res) => this.maybeVerify(id, brief, res, tier, agentOpts, askBridge)).then((res) => this.onWorkerSettled(id, res)).catch((err2) => this.onWorkerFailed(id, err2));
5085
+ const promise = new Agent(agentOpts).run(briefText).then((res) => {
5086
+ const { spoken, detail } = splitter.flush();
5087
+ feedSpoken(spoken);
5088
+ if (detail.trim()) pushTail(detail.trim());
5089
+ flushSpoken();
5090
+ return res;
5091
+ }).then((res) => this.maybeVerify(id, brief, res, tier, agentOpts, askBridge)).then((res) => this.onWorkerSettled(id, res)).catch((err2) => this.onWorkerFailed(id, err2));
5057
5092
  this.tasks.set(id, { id, label, status: "running", controller, promise, tail, brief, followUp, splitter });
5058
5093
  if (this.tasks.size > this.options.maxTaskRecords)
5059
5094
  for (const [tid, rec] of this.tasks) {
@@ -5790,7 +5825,7 @@ var VoiceEngine = class _VoiceEngine {
5790
5825
  * If nothing is currently speaking it plays immediately; otherwise it queues and plays after the
5791
5826
  * current utterance fully ends (settle → pumpQueue) — never spliced into an open reflex utterance. */
5792
5827
  enqueueUtterance(text) {
5793
- if (!text || !text.trim()) return;
5828
+ if (!text || !/[\p{L}\p{N}]/u.test(text)) return;
5794
5829
  this.uttQueue.push(text);
5795
5830
  if (!this.speaking) this.pumpQueue();
5796
5831
  }