@livx.cc/agentx 0.99.70 → 0.99.72

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
@@ -1207,10 +1207,15 @@ declare class DuplexAgent {
1207
1207
  * `opts.id` names the turn; `opts.replaces` names an earlier one this content REPLACES — the voice engine's
1208
1208
  * fuller re-finalization of an utterance whose reply had not started ("…previous" → "…previous
1209
1209
  * conversation."). The replaced turn is skipped if it has not run yet, else aborted and rolled back out of
1210
- * the transcript (kept only if it already dispatched work) — one reply, to the full text. */
1210
+ * the transcript (kept only if it already dispatched work) — one reply, to the full text. `opts.followUp` = the new words
1211
+ * beyond the replaced turn (a string), or beyond each earlier turn of a re-final chain ({turnId: words}): when the nearest
1212
+ * turn still in the transcript is KEPT, its words run instead of the merged text (which would repeat its request — a
1213
+ * second Act). The host gets `turn_replace` {id, replaces, rolledBack, followed?}: only a rollback drops the replaced
1214
+ * line; `followed` = the kept turn whose follow-up ran (absent: the merged text ran). */
1211
1215
  send(content: MessageContent, opts?: {
1212
1216
  id?: string;
1213
1217
  replaces?: string;
1218
+ followUp?: string | Record<string, string>;
1214
1219
  }): Promise<RunResult>;
1215
1220
  /** Host-named user turns (send opts.id), so a later send can replace one. Bounded; a replace that arrives
1216
1221
  * BEFORE its target (the host's dispatch hops can reorder) leaves a pre-superseded record behind. */
package/dist/index.js CHANGED
@@ -8970,15 +8970,16 @@ var STRICT = [
8970
8970
  ];
8971
8971
  var SENTENCE_END = new RegExp(`[.!?\u2026]+["')\\]]*(?:\\s|(?=\\p{Lu}|\\[))`, "gu");
8972
8972
  var SENTENCE_SPLIT = new RegExp(`(?<=[.!?\u2026]+["')\\]]*)(?:\\s+|(?=\\p{Lu}|\\[))`, "u");
8973
+ var BRACKET_NOTE = /^\s*\[\s*(?:[^\]\s]+\s+){3,}[^\]]*(?:\]\s*)?$/;
8973
8974
  var clean2 = (s) => s.replace(/\[[a-z_ ]+\]\s*/gi, "").trim();
8974
8975
  function isLeakSuspect(sentence) {
8975
8976
  const s = clean2(sentence);
8976
- return SUSPECT.some((re) => re.test(s));
8977
+ return BRACKET_NOTE.test(sentence) || SUSPECT.some((re) => re.test(s));
8977
8978
  }
8978
8979
  function isStrictLeak(sentence) {
8979
8980
  return sentence.split(SENTENCE_SPLIT).some((x) => {
8980
8981
  const s = clean2(x);
8981
- return STRICT.some((re) => re.test(s));
8982
+ return BRACKET_NOTE.test(x) || STRICT.some((re) => re.test(s));
8982
8983
  });
8983
8984
  }
8984
8985
  var JUDGE_INSTRUCTIONS = `This sentence was produced by a voice assistant's reply stream and is about to be read aloud to the user. Is it the assistant's private scratchpad reasoning that leaked out \u2014 talking ABOUT the conversation instead of TO the listener? Leaked reasoning: refers to the listener in the third person ("the user wants\u2026", "user said\u2026"), plans its own reply ("we need to respond\u2026", "we should output\u2026", "I should answer briefly"), cites its own guidelines/instructions, or narrates its internal tool calls and task events (QuickLook, Act, Think, Hold, TaskStatus, "t2 completed") as steps it is taking. Speech: anything said TO the listener \u2014 answers, questions, offers, news, advice, explaining what it can do when asked, even if it mentions a user, an assistant, a policy, instructions, replying, or a tool.`;
@@ -9581,12 +9582,17 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
9581
9582
  * `opts.id` names the turn; `opts.replaces` names an earlier one this content REPLACES — the voice engine's
9582
9583
  * fuller re-finalization of an utterance whose reply had not started ("…previous" → "…previous
9583
9584
  * conversation."). The replaced turn is skipped if it has not run yet, else aborted and rolled back out of
9584
- * the transcript (kept only if it already dispatched work) — one reply, to the full text. */
9585
+ * the transcript (kept only if it already dispatched work) — one reply, to the full text. `opts.followUp` = the new words
9586
+ * beyond the replaced turn (a string), or beyond each earlier turn of a re-final chain ({turnId: words}): when the nearest
9587
+ * turn still in the transcript is KEPT, its words run instead of the merged text (which would repeat its request — a
9588
+ * second Act). The host gets `turn_replace` {id, replaces, rolledBack, followed?}: only a rollback drops the replaced
9589
+ * line; `followed` = the kept turn whose follow-up ran (absent: the merged text ran). */
9585
9590
  send(content, opts) {
9586
9591
  if (opts?.replaces) this.replaceTurn(opts.replaces);
9587
9592
  const cur = this.voice.options.signal;
9588
9593
  const ext = cur && cur === this.turnSignal?.own ? this.turnSignal.ext : cur;
9589
9594
  const rec = opts?.id ? this.trackTurn(opts.id) : void 0;
9595
+ if (rec && opts?.replaces && typeof content === "string") rec.replaces = { id: opts.id, of: opts.replaces, content, followUp: typeof opts.followUp === "string" ? { [opts.replaces]: opts.followUp } : opts.followUp ?? {} };
9590
9596
  if (rec?.superseded) {
9591
9597
  this.notify("diag", "turn_replaced", { id: opts.id, ran: false });
9592
9598
  return Promise.resolve(_DuplexAgent.replacedResult(this.voice.transcript));
@@ -9644,6 +9650,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
9644
9650
  if (rec.done) {
9645
9651
  const drop = rec.dropped;
9646
9652
  rec.dropped = void 0;
9653
+ rec.kept = !drop?.length;
9647
9654
  if (drop?.length) void this.enqueue(async () => {
9648
9655
  const t = this.voice.transcript;
9649
9656
  for (const m of drop) {
@@ -9665,12 +9672,13 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
9665
9672
  * carries the user's full words) — unless it already dispatched work or SPOKE: the task is real, and the user
9666
9673
  * heard those words, so both stay visible to the replacing turn. An aborted stream never reaches the transcript,
9667
9674
  * so what was spoken is recorded as the replaced turn's reply. */
9668
- dropReplacedTurn() {
9675
+ dropReplacedTurn(rec) {
9669
9676
  this.fabricationCut = true;
9670
9677
  this.reflexBuf = this.reflexBuf.slice(0, this.reflexForwarded);
9671
9678
  this.dropJudge();
9672
9679
  const spoke = this.turnSpoken.trim();
9673
9680
  const rolledBack = !this.turnDispatched && !spoke;
9681
+ rec.kept = !rolledBack;
9674
9682
  if (rolledBack) this.voice.transcript.length = this.turnBase;
9675
9683
  else if (spoke) {
9676
9684
  const t = this.voice.transcript;
@@ -9714,6 +9722,22 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
9714
9722
  this.parkedRedeliver = [];
9715
9723
  for (const t of this.tasks.values()) if (t.deliveryParked === "barge") t.deliveryParked = "moved_on";
9716
9724
  }
9725
+ if (rec?.replaces) {
9726
+ const { id, of, content: said, followUp } = rec.replaces;
9727
+ let base;
9728
+ for (let t = of, n = 0; t && n < 32; n++) {
9729
+ const r = this.userTurns.get(t);
9730
+ if (r?.kept) {
9731
+ base = t;
9732
+ break;
9733
+ }
9734
+ t = r?.replaces?.of;
9735
+ }
9736
+ const fu = base !== void 0 ? followUp[base] : void 0;
9737
+ if (fu !== void 0 && typeof content === "string" && content.startsWith(said)) content = fu + content.slice(said.length);
9738
+ else base = void 0;
9739
+ this.notify("turn_replace", of, { id, replaces: of, rolledBack: !this.userTurns.get(of)?.kept, ...base !== void 0 ? { followed: base } : {} });
9740
+ }
9717
9741
  if (rec?.superseded) {
9718
9742
  this.notify("diag", "turn_replaced", { ran: false });
9719
9743
  return _DuplexAgent.replacedResult(this.voice.transcript);
@@ -9733,7 +9757,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
9733
9757
  }
9734
9758
  if (rec) rec.done = true;
9735
9759
  }
9736
- if (rec?.superseded) return this.dropReplacedTurn();
9760
+ if (rec?.superseded) return this.dropReplacedTurn(rec);
9737
9761
  if (rec && res.finishReason === "aborted" && !this.turnDispatched && !this.turnSpoken.trim()) rec.dropped = this.voice.transcript.slice(this.turnBase);
9738
9762
  await this.endTurn(res);
9739
9763
  return res;
@@ -9803,7 +9827,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
9803
9827
  }
9804
9828
  this.spec = void 0;
9805
9829
  if (spec.rec) spec.rec.done = true;
9806
- if (spec.rec?.superseded) return this.dropReplacedTurn();
9830
+ if (spec.rec?.superseded) return this.dropReplacedTurn(spec.rec);
9807
9831
  await this.endTurn(res);
9808
9832
  return res ?? empty;
9809
9833
  });