@livx.cc/agentx 0.97.7 → 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 CHANGED
@@ -4948,6 +4948,19 @@ Today's date: ${(/* @__PURE__ */ new Date()).toDateString()}.`;
4948
4948
  rec.controller.abort();
4949
4949
  return `Task ${rec.id} (${rec.label}) cancelled.`;
4950
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
+ }
4951
4964
  /** Resolve when all queued voice turns AND all in-flight worker tasks have settled (tests, graceful shutdown). */
4952
4965
  async idle() {
4953
4966
  while (true) {
@@ -5045,7 +5058,7 @@ ${recent}` : brief) + verify + deliverContract;
5045
5058
  };
5046
5059
  const splitter = new SpokenSplitter();
5047
5060
  const speak = (seg) => {
5048
- 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 });
5049
5062
  };
5050
5063
  const coalescer = new SentenceCoalescer();
5051
5064
  const feedSpoken = (s) => {
@@ -5266,9 +5279,9 @@ Another agent just implemented the above. Independently check the CURRENT state
5266
5279
  return this.queueRevoice(this.integrationPrompt(rec, "incomplete", res.text, res.finishReason), true);
5267
5280
  }
5268
5281
  const tail = rec.splitter?.flush();
5269
- 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 });
5270
5283
  if (res.text.trim()) this.voice.transcript.push({ role: "assistant", content: res.text });
5271
- if (!rec.splitter?.spokeAny && res.text.trim())
5284
+ if (!rec.splitter?.spokeAny && res.text.trim() && !rec.deliveryParked)
5272
5285
  this.options.host?.notify?.({ kind: "speak_utterance", message: res.text });
5273
5286
  }
5274
5287
  onWorkerFailed(id, err2) {
@@ -13352,8 +13365,11 @@ ${out}
13352
13365
  // voiceEchoEnd closes the open echo line; '\r\x1b[0J' wipes the stale prompt/footer before the
13353
13366
  // notice — every other async-chrome writer does this, and without it "✋ interrupted" overprints
13354
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.
13355
13370
  onBargeIn: (phase) => {
13356
13371
  activeTurn?.abort();
13372
+ dx?.parkInFlightDeliveries();
13357
13373
  voiceEchoEnd();
13358
13374
  if (phase === "speaking") err("\r\x1B[0J" + yellow(" \u270B interrupted\n"));
13359
13375
  },