@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/index.js CHANGED
@@ -4651,6 +4651,29 @@ init_logging();
4651
4651
  // src/voice/spokenSplitter.ts
4652
4652
  var OPEN = "<spoken>";
4653
4653
  var CLOSE = "</spoken>";
4654
+ var CLOSERS = `"')]}\xBB\u201D\u2019`;
4655
+ var hasSpeech = (s) => /[\p{L}\p{N}]/u.test(s);
4656
+ var SentenceCoalescer = class _SentenceCoalescer {
4657
+ buf = "";
4658
+ static isEnd(c) {
4659
+ return c === "\n" || c === "." || c === "!" || c === "?" || c === "\u2026";
4660
+ }
4661
+ feed(delta) {
4662
+ if (delta) this.buf += delta;
4663
+ let cut = -1;
4664
+ for (let i = 0; i < this.buf.length; i++) if (_SentenceCoalescer.isEnd(this.buf[i])) cut = i;
4665
+ if (cut < 0) return "";
4666
+ if (this.buf[cut] !== "\n") while (cut + 1 < this.buf.length && CLOSERS.includes(this.buf[cut + 1])) cut++;
4667
+ const ready = this.buf.slice(0, cut + 1).trim();
4668
+ this.buf = this.buf.slice(cut + 1);
4669
+ return hasSpeech(ready) ? ready : "";
4670
+ }
4671
+ flush() {
4672
+ const s = this.buf.trim();
4673
+ this.buf = "";
4674
+ return hasSpeech(s) ? s : "";
4675
+ }
4676
+ };
4654
4677
  var SpokenSplitter = class {
4655
4678
  buf = "";
4656
4679
  inSpoken = false;
@@ -5090,13 +5113,19 @@ ${recent}` : brief) + verify + deliverContract;
5090
5113
  const speak = (seg) => {
5091
5114
  if (seg) o.host?.notify?.({ kind: "speak_utterance", message: seg });
5092
5115
  };
5116
+ const coalescer = new SentenceCoalescer();
5117
+ const feedSpoken = (s) => {
5118
+ const ready = coalescer.feed(s);
5119
+ if (ready) speak(ready);
5120
+ };
5121
+ const flushSpoken = () => speak(coalescer.flush());
5093
5122
  const askBridge = o.askRelay ? { ask: relayAsk } : o.host?.ask ? { ask: (q) => o.host.ask(q) } : {};
5094
5123
  const workerHost = {
5095
5124
  ...askBridge,
5096
5125
  notify: (ev) => {
5097
5126
  if (ev?.kind === "text_delta" && typeof ev.message === "string") {
5098
5127
  const { spoken, detail } = splitter.feed(ev.message);
5099
- speak(spoken);
5128
+ feedSpoken(spoken);
5100
5129
  if (detail.trim()) pushTail(detail.trim());
5101
5130
  return;
5102
5131
  }
@@ -5119,7 +5148,13 @@ ${recent}` : brief) + verify + deliverContract;
5119
5148
  signal: controller.signal
5120
5149
  // shared with the checker so a cancel tears down both
5121
5150
  };
5122
- const promise = new Agent(agentOpts).run(briefText).then((res) => this.maybeVerify(id, brief, res, tier, agentOpts, askBridge)).then((res) => this.onWorkerSettled(id, res)).catch((err) => this.onWorkerFailed(id, err));
5151
+ const promise = new Agent(agentOpts).run(briefText).then((res) => {
5152
+ const { spoken, detail } = splitter.flush();
5153
+ feedSpoken(spoken);
5154
+ if (detail.trim()) pushTail(detail.trim());
5155
+ flushSpoken();
5156
+ return res;
5157
+ }).then((res) => this.maybeVerify(id, brief, res, tier, agentOpts, askBridge)).then((res) => this.onWorkerSettled(id, res)).catch((err) => this.onWorkerFailed(id, err));
5123
5158
  this.tasks.set(id, { id, label, status: "running", controller, promise, tail, brief, followUp, splitter });
5124
5159
  if (this.tasks.size > this.options.maxTaskRecords)
5125
5160
  for (const [tid, rec] of this.tasks) {
@@ -5914,7 +5949,7 @@ var VoiceEngine = class _VoiceEngine {
5914
5949
  * If nothing is currently speaking it plays immediately; otherwise it queues and plays after the
5915
5950
  * current utterance fully ends (settle → pumpQueue) — never spliced into an open reflex utterance. */
5916
5951
  enqueueUtterance(text) {
5917
- if (!text || !text.trim()) return;
5952
+ if (!text || !/[\p{L}\p{N}]/u.test(text)) return;
5918
5953
  this.uttQueue.push(text);
5919
5954
  if (!this.speaking) this.pumpQueue();
5920
5955
  }