@livx.cc/agentx 0.99.25 → 0.99.26
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 +33 -8
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +25 -5
- package/dist/index.js +33 -8
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -6649,6 +6649,9 @@ var DuplexAgent = class _DuplexAgent {
|
|
|
6649
6649
|
tasks = /* @__PURE__ */ new Map();
|
|
6650
6650
|
queue = Promise.resolve();
|
|
6651
6651
|
seq = 0;
|
|
6652
|
+
/** Monotonic user-turn counter (bumped once per SUBSTANTIVE turn). Stamped onto each task at dispatch
|
|
6653
|
+
* (TaskRecord.createdTurn) so parking can tell a prior-topic task from the current turn's fresh one. */
|
|
6654
|
+
turn = 0;
|
|
6652
6655
|
pendingEvents = [];
|
|
6653
6656
|
/** Spoken text of parked deliveries that have SETTLED, awaiting a trivial-barge resume decision on the
|
|
6654
6657
|
* next turn (a substantive turn clears it — the result stays in the transcript, recoverable by asking). */
|
|
@@ -6942,6 +6945,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
6942
6945
|
return this.enqueue(async () => {
|
|
6943
6946
|
await this.initMemory();
|
|
6944
6947
|
this.resetTurn();
|
|
6948
|
+
this.turn++;
|
|
6945
6949
|
this.awaitTrivialRedeliver = false;
|
|
6946
6950
|
this.parkedRedeliver = [];
|
|
6947
6951
|
this.suppressParkedResume = true;
|
|
@@ -7040,15 +7044,21 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
7040
7044
|
rec.controller.abort();
|
|
7041
7045
|
return `Task ${rec.id} (${rec.label}) cancelled.`;
|
|
7042
7046
|
}
|
|
7043
|
-
/** Barge-in
|
|
7044
|
-
*
|
|
7045
|
-
*
|
|
7046
|
-
*
|
|
7047
|
-
|
|
7048
|
-
|
|
7047
|
+
/** Barge-in (or a dispatch-driven supersede): suppress running tasks' remaining SPOKEN delivery so a
|
|
7048
|
+
* superseded topic never talks over the new one (the debt-after-jokes regression). Tasks keep running and
|
|
7049
|
+
* still fold their result into the transcript — recoverable, just not spoken. Returns parked ids (logging).
|
|
7050
|
+
* Does NOT cancel. Two callers, two shapes:
|
|
7051
|
+
* - BARGE (default): the user interrupted AUDIO. Park EVERY running task (including this turn's) — the
|
|
7052
|
+
* worker they cut off must stop, whatever turn launched it — and re-arm trivial-barge auto-resume so a
|
|
7053
|
+
* following "go on" can bring the parked delivery back.
|
|
7054
|
+
* - SUPERSEDE (`priorTurnsOnly`, `reenableResume:false`, from dispatch()): a new dispatch redirected to
|
|
7055
|
+
* fresh work. Park only PRIOR-turn tasks so parallel siblings dispatched in the SAME turn don't silence
|
|
7056
|
+
* each other, and keep them suppressed (the user moved on — don't resurface on a stray "go on"). */
|
|
7057
|
+
parkInFlightDeliveries(reenableResume = true, priorTurnsOnly = false) {
|
|
7058
|
+
if (reenableResume) this.suppressParkedResume = false;
|
|
7049
7059
|
const parked = [];
|
|
7050
7060
|
for (const rec of this.tasks.values())
|
|
7051
|
-
if (rec.status === "running" && !rec.deliveryParked) {
|
|
7061
|
+
if (rec.status === "running" && !rec.deliveryParked && (!priorTurnsOnly || rec.createdTurn < this.turn)) {
|
|
7052
7062
|
rec.deliveryParked = true;
|
|
7053
7063
|
parked.push(rec.id);
|
|
7054
7064
|
}
|
|
@@ -7234,7 +7244,7 @@ ${recent}` : brief) + verify + deliverContract;
|
|
|
7234
7244
|
flushSpoken();
|
|
7235
7245
|
return res;
|
|
7236
7246
|
}).then((res) => this.maybeVerify(id, brief, res, tier, agentOpts, askBridge)).then((res) => this.onWorkerSettled(id, res)).catch((err2) => this.onWorkerFailed(id, err2));
|
|
7237
|
-
this.tasks.set(id, { id, label, status: "running", controller, promise, tail, brief, followUp, splitter });
|
|
7247
|
+
this.tasks.set(id, { id, label, status: "running", controller, promise, tail, brief, followUp, splitter, createdTurn: this.turn });
|
|
7238
7248
|
if (this.tasks.size > this.options.maxTaskRecords)
|
|
7239
7249
|
for (const [tid, rec] of this.tasks) {
|
|
7240
7250
|
if (this.tasks.size <= this.options.maxTaskRecords) break;
|
|
@@ -7408,6 +7418,7 @@ Another agent just implemented the above. Independently check the CURRENT state
|
|
|
7408
7418
|
toolCalls: res.messages.filter((m) => m.role === "tool").length
|
|
7409
7419
|
});
|
|
7410
7420
|
if (incomplete) {
|
|
7421
|
+
if (this.foldIfSuperseded(rec, res.text)) return;
|
|
7411
7422
|
return this.queueRevoice(this.integrationPrompt(rec, "incomplete", res.text, res.finishReason), true);
|
|
7412
7423
|
}
|
|
7413
7424
|
const tail = rec.splitter?.flush();
|
|
@@ -7429,12 +7440,25 @@ Another agent just implemented the above. Independently check the CURRENT state
|
|
|
7429
7440
|
onWorkerFailed(id, err2) {
|
|
7430
7441
|
this.failTask(this.tasks.get(id), err2 instanceof Error ? err2.message : String(err2));
|
|
7431
7442
|
}
|
|
7443
|
+
/** A delivery the user has SUPERSEDED — parked AND moved on (suppressParkedResume, set by a redirect) —
|
|
7444
|
+
* must not hijack the voice when it settles late (partial or failed): that would talk over the new topic
|
|
7445
|
+
* and, worse, auto-escalate work the user already abandoned. Fold a recoverable note into the transcript
|
|
7446
|
+
* instead (the reflex can surface it if asked) and report true so the caller skips queueRevoice. A merely
|
|
7447
|
+
* barge-parked task (suppressParkedResume false — a "go on" may still resume it) is NOT superseded: it
|
|
7448
|
+
* keeps the normal surfacing path so a failure the user still cares about is not swallowed. */
|
|
7449
|
+
foldIfSuperseded(rec, note) {
|
|
7450
|
+
if (!(rec.deliveryParked && this.suppressParkedResume)) return false;
|
|
7451
|
+
if (note.trim()) this.voice.transcript.push({ role: "assistant", content: note });
|
|
7452
|
+
log12.verbose(`task ${rec.id} settled but superseded (${rec.status}) \u2014 folded into transcript, not voiced`);
|
|
7453
|
+
return true;
|
|
7454
|
+
}
|
|
7432
7455
|
failTask(rec, msg) {
|
|
7433
7456
|
this.dropAsk(rec.id);
|
|
7434
7457
|
rec.status = "error";
|
|
7435
7458
|
rec.result = msg;
|
|
7436
7459
|
log12.warn(`task ${rec.id} failed: ${msg}`);
|
|
7437
7460
|
this.notify("task_error", `task ${rec.id} (${rec.label}) failed: ${msg}`);
|
|
7461
|
+
if (this.foldIfSuperseded(rec, `The "${rec.label}" task failed: ${msg}`)) return;
|
|
7438
7462
|
this.queueRevoice(this.integrationPrompt(rec, "error", msg, "error"), true);
|
|
7439
7463
|
}
|
|
7440
7464
|
// --- voice tools (closures over this instance) ---
|
|
@@ -7455,6 +7479,7 @@ Another agent just implemented the above. Independently check the CURRENT state
|
|
|
7455
7479
|
async dispatch(brief, tier = "act", label, followUp = false) {
|
|
7456
7480
|
if (tier === "think" && this.options.thinkModel === false) tier = "act";
|
|
7457
7481
|
if (followUp) this.autoEscalations++;
|
|
7482
|
+
this.parkInFlightDeliveries(false, true);
|
|
7458
7483
|
const id = `t${++this.seq}`;
|
|
7459
7484
|
const lbl = label ?? tier;
|
|
7460
7485
|
await this.options.onTaskStart?.(id, lbl);
|