@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/index.d.ts CHANGED
@@ -858,6 +858,10 @@ interface TaskRecord {
858
858
  /** Per-worker `<spoken>` splitter — the worker OWNS delivery: spoken segments stream during its run.
859
859
  * Read at settle (spokeAny) to decide the no-spoken fallback. */
860
860
  splitter?: SpokenSplitter;
861
+ /** Set when the user barged in / took the floor while this task was in flight: its remaining SPOKEN
862
+ * delivery is suppressed (don't talk over the new topic), but its full result still lands in the
863
+ * transcript so the reflex can surface it on request. See parkInFlightDeliveries(). */
864
+ deliveryParked?: boolean;
861
865
  }
862
866
  type WorkerTier = 'act' | 'think';
863
867
  declare class DuplexAgentOptions {
@@ -998,6 +1002,11 @@ declare class DuplexAgent {
998
1002
  send(content: MessageContent): Promise<RunResult>;
999
1003
  /** Cancel a running background task — shared by the CancelTask tool and the CLI /tasks picker. */
1000
1004
  cancelTask(id: string): string;
1005
+ /** Barge-in: the user took the floor while task(s) were running. Suppress those tasks' remaining SPOKEN
1006
+ * delivery so a superseded topic never talks over the new one (the debt-after-jokes regression). The
1007
+ * tasks keep running and still fold their result into the transcript — recoverable, just not spoken.
1008
+ * Returns the parked ids (for logging). Does NOT cancel: that's a deliberate reflex/user action. */
1009
+ parkInFlightDeliveries(): string[];
1001
1010
  /** Resolve when all queued voice turns AND all in-flight worker tasks have settled (tests, graceful shutdown). */
1002
1011
  idle(): Promise<void>;
1003
1012
  /** Promise-chain mutex: turns run strictly one at a time; a failed turn doesn't poison the chain. */
package/dist/index.js CHANGED
@@ -4651,6 +4651,8 @@ 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);
4654
4656
  var SentenceCoalescer = class _SentenceCoalescer {
4655
4657
  buf = "";
4656
4658
  static isEnd(c) {
@@ -4661,14 +4663,15 @@ var SentenceCoalescer = class _SentenceCoalescer {
4661
4663
  let cut = -1;
4662
4664
  for (let i = 0; i < this.buf.length; i++) if (_SentenceCoalescer.isEnd(this.buf[i])) cut = i;
4663
4665
  if (cut < 0) return "";
4666
+ if (this.buf[cut] !== "\n") while (cut + 1 < this.buf.length && CLOSERS.includes(this.buf[cut + 1])) cut++;
4664
4667
  const ready = this.buf.slice(0, cut + 1).trim();
4665
4668
  this.buf = this.buf.slice(cut + 1);
4666
- return ready;
4669
+ return hasSpeech(ready) ? ready : "";
4667
4670
  }
4668
4671
  flush() {
4669
4672
  const s = this.buf.trim();
4670
4673
  this.buf = "";
4671
- return s;
4674
+ return hasSpeech(s) ? s : "";
4672
4675
  }
4673
4676
  };
4674
4677
  var SpokenSplitter = class {
@@ -5011,6 +5014,19 @@ Today's date: ${(/* @__PURE__ */ new Date()).toDateString()}.`;
5011
5014
  rec.controller.abort();
5012
5015
  return `Task ${rec.id} (${rec.label}) cancelled.`;
5013
5016
  }
5017
+ /** Barge-in: the user took the floor while task(s) were running. Suppress those tasks' remaining SPOKEN
5018
+ * delivery so a superseded topic never talks over the new one (the debt-after-jokes regression). The
5019
+ * tasks keep running and still fold their result into the transcript — recoverable, just not spoken.
5020
+ * Returns the parked ids (for logging). Does NOT cancel: that's a deliberate reflex/user action. */
5021
+ parkInFlightDeliveries() {
5022
+ const parked = [];
5023
+ for (const rec of this.tasks.values())
5024
+ if (rec.status === "running" && !rec.deliveryParked) {
5025
+ rec.deliveryParked = true;
5026
+ parked.push(rec.id);
5027
+ }
5028
+ return parked;
5029
+ }
5014
5030
  /** Resolve when all queued voice turns AND all in-flight worker tasks have settled (tests, graceful shutdown). */
5015
5031
  async idle() {
5016
5032
  while (true) {
@@ -5108,7 +5124,7 @@ ${recent}` : brief) + verify + deliverContract;
5108
5124
  };
5109
5125
  const splitter = new SpokenSplitter();
5110
5126
  const speak = (seg) => {
5111
- if (seg) o.host?.notify?.({ kind: "speak_utterance", message: seg });
5127
+ if (seg && !this.tasks.get(id)?.deliveryParked) o.host?.notify?.({ kind: "speak_utterance", message: seg });
5112
5128
  };
5113
5129
  const coalescer = new SentenceCoalescer();
5114
5130
  const feedSpoken = (s) => {
@@ -5329,9 +5345,9 @@ Another agent just implemented the above. Independently check the CURRENT state
5329
5345
  return this.queueRevoice(this.integrationPrompt(rec, "incomplete", res.text, res.finishReason), true);
5330
5346
  }
5331
5347
  const tail = rec.splitter?.flush();
5332
- if (tail?.spoken) this.options.host?.notify?.({ kind: "speak_utterance", message: tail.spoken });
5348
+ if (tail?.spoken && !rec.deliveryParked) this.options.host?.notify?.({ kind: "speak_utterance", message: tail.spoken });
5333
5349
  if (res.text.trim()) this.voice.transcript.push({ role: "assistant", content: res.text });
5334
- if (!rec.splitter?.spokeAny && res.text.trim())
5350
+ if (!rec.splitter?.spokeAny && res.text.trim() && !rec.deliveryParked)
5335
5351
  this.options.host?.notify?.({ kind: "speak_utterance", message: res.text });
5336
5352
  }
5337
5353
  onWorkerFailed(id, err) {
@@ -5946,7 +5962,7 @@ var VoiceEngine = class _VoiceEngine {
5946
5962
  * If nothing is currently speaking it plays immediately; otherwise it queues and plays after the
5947
5963
  * current utterance fully ends (settle → pumpQueue) — never spliced into an open reflex utterance. */
5948
5964
  enqueueUtterance(text) {
5949
- if (!text || !text.trim()) return;
5965
+ if (!text || !/[\p{L}\p{N}]/u.test(text)) return;
5950
5966
  this.uttQueue.push(text);
5951
5967
  if (!this.speaking) this.pumpQueue();
5952
5968
  }