@livx.cc/agentx 0.99.71 → 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/cli.js +32 -7
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +30 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -9167,15 +9167,16 @@ var STRICT = [
|
|
|
9167
9167
|
];
|
|
9168
9168
|
var SENTENCE_END = new RegExp(`[.!?\u2026]+["')\\]]*(?:\\s|(?=\\p{Lu}|\\[))`, "gu");
|
|
9169
9169
|
var SENTENCE_SPLIT = new RegExp(`(?<=[.!?\u2026]+["')\\]]*)(?:\\s+|(?=\\p{Lu}|\\[))`, "u");
|
|
9170
|
+
var BRACKET_NOTE = /^\s*\[\s*(?:[^\]\s]+\s+){3,}[^\]]*(?:\]\s*)?$/;
|
|
9170
9171
|
var clean = (s) => s.replace(/\[[a-z_ ]+\]\s*/gi, "").trim();
|
|
9171
9172
|
function isLeakSuspect(sentence) {
|
|
9172
9173
|
const s = clean(sentence);
|
|
9173
|
-
return SUSPECT.some((re) => re.test(s));
|
|
9174
|
+
return BRACKET_NOTE.test(sentence) || SUSPECT.some((re) => re.test(s));
|
|
9174
9175
|
}
|
|
9175
9176
|
function isStrictLeak(sentence) {
|
|
9176
9177
|
return sentence.split(SENTENCE_SPLIT).some((x) => {
|
|
9177
9178
|
const s = clean(x);
|
|
9178
|
-
return STRICT.some((re) => re.test(s));
|
|
9179
|
+
return BRACKET_NOTE.test(x) || STRICT.some((re) => re.test(s));
|
|
9179
9180
|
});
|
|
9180
9181
|
}
|
|
9181
9182
|
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.`;
|
|
@@ -9778,12 +9779,17 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
9778
9779
|
* `opts.id` names the turn; `opts.replaces` names an earlier one this content REPLACES — the voice engine's
|
|
9779
9780
|
* fuller re-finalization of an utterance whose reply had not started ("…previous" → "…previous
|
|
9780
9781
|
* conversation."). The replaced turn is skipped if it has not run yet, else aborted and rolled back out of
|
|
9781
|
-
* the transcript (kept only if it already dispatched work) — one reply, to the full text.
|
|
9782
|
+
* the transcript (kept only if it already dispatched work) — one reply, to the full text. `opts.followUp` = the new words
|
|
9783
|
+
* beyond the replaced turn (a string), or beyond each earlier turn of a re-final chain ({turnId: words}): when the nearest
|
|
9784
|
+
* turn still in the transcript is KEPT, its words run instead of the merged text (which would repeat its request — a
|
|
9785
|
+
* second Act). The host gets `turn_replace` {id, replaces, rolledBack, followed?}: only a rollback drops the replaced
|
|
9786
|
+
* line; `followed` = the kept turn whose follow-up ran (absent: the merged text ran). */
|
|
9782
9787
|
send(content, opts) {
|
|
9783
9788
|
if (opts?.replaces) this.replaceTurn(opts.replaces);
|
|
9784
9789
|
const cur = this.voice.options.signal;
|
|
9785
9790
|
const ext = cur && cur === this.turnSignal?.own ? this.turnSignal.ext : cur;
|
|
9786
9791
|
const rec = opts?.id ? this.trackTurn(opts.id) : void 0;
|
|
9792
|
+
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 ?? {} };
|
|
9787
9793
|
if (rec?.superseded) {
|
|
9788
9794
|
this.notify("diag", "turn_replaced", { id: opts.id, ran: false });
|
|
9789
9795
|
return Promise.resolve(_DuplexAgent.replacedResult(this.voice.transcript));
|
|
@@ -9841,6 +9847,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
9841
9847
|
if (rec.done) {
|
|
9842
9848
|
const drop = rec.dropped;
|
|
9843
9849
|
rec.dropped = void 0;
|
|
9850
|
+
rec.kept = !drop?.length;
|
|
9844
9851
|
if (drop?.length) void this.enqueue(async () => {
|
|
9845
9852
|
const t = this.voice.transcript;
|
|
9846
9853
|
for (const m of drop) {
|
|
@@ -9862,12 +9869,13 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
9862
9869
|
* carries the user's full words) — unless it already dispatched work or SPOKE: the task is real, and the user
|
|
9863
9870
|
* heard those words, so both stay visible to the replacing turn. An aborted stream never reaches the transcript,
|
|
9864
9871
|
* so what was spoken is recorded as the replaced turn's reply. */
|
|
9865
|
-
dropReplacedTurn() {
|
|
9872
|
+
dropReplacedTurn(rec) {
|
|
9866
9873
|
this.fabricationCut = true;
|
|
9867
9874
|
this.reflexBuf = this.reflexBuf.slice(0, this.reflexForwarded);
|
|
9868
9875
|
this.dropJudge();
|
|
9869
9876
|
const spoke = this.turnSpoken.trim();
|
|
9870
9877
|
const rolledBack = !this.turnDispatched && !spoke;
|
|
9878
|
+
rec.kept = !rolledBack;
|
|
9871
9879
|
if (rolledBack) this.voice.transcript.length = this.turnBase;
|
|
9872
9880
|
else if (spoke) {
|
|
9873
9881
|
const t = this.voice.transcript;
|
|
@@ -9911,6 +9919,22 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
9911
9919
|
this.parkedRedeliver = [];
|
|
9912
9920
|
for (const t of this.tasks.values()) if (t.deliveryParked === "barge") t.deliveryParked = "moved_on";
|
|
9913
9921
|
}
|
|
9922
|
+
if (rec?.replaces) {
|
|
9923
|
+
const { id, of, content: said, followUp } = rec.replaces;
|
|
9924
|
+
let base;
|
|
9925
|
+
for (let t = of, n = 0; t && n < 32; n++) {
|
|
9926
|
+
const r = this.userTurns.get(t);
|
|
9927
|
+
if (r?.kept) {
|
|
9928
|
+
base = t;
|
|
9929
|
+
break;
|
|
9930
|
+
}
|
|
9931
|
+
t = r?.replaces?.of;
|
|
9932
|
+
}
|
|
9933
|
+
const fu = base !== void 0 ? followUp[base] : void 0;
|
|
9934
|
+
if (fu !== void 0 && typeof content === "string" && content.startsWith(said)) content = fu + content.slice(said.length);
|
|
9935
|
+
else base = void 0;
|
|
9936
|
+
this.notify("turn_replace", of, { id, replaces: of, rolledBack: !this.userTurns.get(of)?.kept, ...base !== void 0 ? { followed: base } : {} });
|
|
9937
|
+
}
|
|
9914
9938
|
if (rec?.superseded) {
|
|
9915
9939
|
this.notify("diag", "turn_replaced", { ran: false });
|
|
9916
9940
|
return _DuplexAgent.replacedResult(this.voice.transcript);
|
|
@@ -9930,7 +9954,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
9930
9954
|
}
|
|
9931
9955
|
if (rec) rec.done = true;
|
|
9932
9956
|
}
|
|
9933
|
-
if (rec?.superseded) return this.dropReplacedTurn();
|
|
9957
|
+
if (rec?.superseded) return this.dropReplacedTurn(rec);
|
|
9934
9958
|
if (rec && res.finishReason === "aborted" && !this.turnDispatched && !this.turnSpoken.trim()) rec.dropped = this.voice.transcript.slice(this.turnBase);
|
|
9935
9959
|
await this.endTurn(res);
|
|
9936
9960
|
return res;
|
|
@@ -10000,7 +10024,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
10000
10024
|
}
|
|
10001
10025
|
this.spec = void 0;
|
|
10002
10026
|
if (spec.rec) spec.rec.done = true;
|
|
10003
|
-
if (spec.rec?.superseded) return this.dropReplacedTurn();
|
|
10027
|
+
if (spec.rec?.superseded) return this.dropReplacedTurn(spec.rec);
|
|
10004
10028
|
await this.endTurn(res);
|
|
10005
10029
|
return res ?? empty;
|
|
10006
10030
|
});
|
|
@@ -19319,6 +19343,7 @@ async function duplexServe(args, ai, cfg, cwd) {
|
|
|
19319
19343
|
else if (k === "thinking_delta") out({ type: "thinking", text: e.message });
|
|
19320
19344
|
else if (k === "speak_utterance") out({ type: "speak", text: e.message });
|
|
19321
19345
|
else if (k === "hold_filler") out({ type: "filler", text: e.message });
|
|
19346
|
+
else if (k === "turn_replace") out({ type: "replaced", ...e.data });
|
|
19322
19347
|
else if (k === "revoice_done") {
|
|
19323
19348
|
out({ type: "revoice_done" });
|
|
19324
19349
|
persist();
|
|
@@ -19374,7 +19399,7 @@ async function duplexServe(args, ai, cfg, cwd) {
|
|
|
19374
19399
|
if (delivery || reply) dx.parkCutDelivery(delivery, !!reply, heard);
|
|
19375
19400
|
const note = reply && reply.full.length - reply.heard.length > 40 ? interruptionNote(reply) : "";
|
|
19376
19401
|
const id = m.id ? String(m.id) : void 0;
|
|
19377
|
-
void runTurn(dx.voice, store, session, m.text + note, void 0, cwd, (c) => dx.send(c, { id, replaces: m.replaces ? String(m.replaces) : void 0 })).then(({ res }) => out({ type: "turn_done", id, finishReason: res?.finishReason ?? "error" })).catch((e) => {
|
|
19402
|
+
void runTurn(dx.voice, store, session, m.text + note, void 0, cwd, (c) => dx.send(c, { id, replaces: m.replaces ? String(m.replaces) : void 0, followUp: typeof m.followUp === "string" || m.followUp && typeof m.followUp === "object" ? m.followUp : void 0 })).then(({ res }) => out({ type: "turn_done", id, finishReason: res?.finishReason ?? "error" })).catch((e) => {
|
|
19378
19403
|
log33.error("duplex turn failed", e);
|
|
19379
19404
|
out({ type: "turn_done", id, finishReason: "error", error: String(e?.message ?? e) });
|
|
19380
19405
|
});
|