@livx.cc/agentx 0.99.24 → 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.d.ts CHANGED
@@ -144,6 +144,7 @@ interface Args {
144
144
  worktree?: string;
145
145
  updateCheck?: boolean;
146
146
  update?: boolean;
147
+ listModels?: boolean;
147
148
  }
148
149
  declare function parseArgs(argv: string[]): Args;
149
150
  declare function makeHost(format?: 'text' | 'json' | 'stream-json', opts?: {
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: the user took the floor while task(s) were running. Suppress those tasks' remaining SPOKEN
7044
- * delivery so a superseded topic never talks over the new one (the debt-after-jokes regression). The
7045
- * tasks keep running and still fold their result into the transcript — recoverable, just not spoken.
7046
- * Returns the parked ids (for logging). Does NOT cancel: that's a deliberate reflex/user action. */
7047
- parkInFlightDeliveries() {
7048
- this.suppressParkedResume = false;
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);
@@ -12075,8 +12100,13 @@ function parseArgs(argv) {
12075
12100
  const x = argv[i];
12076
12101
  if (x === "-h" || x === "--help") a.help = true;
12077
12102
  else if (x === "-v" || x === "--version") a.version = true;
12078
- else if (x === "-m" || x === "--model") a.model = val(++i, x);
12079
- else if (x === "-C" || x === "--cwd") a.cwd = val(++i, x);
12103
+ else if (x === "-m" || x === "--model") {
12104
+ const nxt = argv[i + 1];
12105
+ if (nxt === void 0 || nxt.startsWith("-") || nxt === "list" || nxt === "ls" || nxt === "?") {
12106
+ a.listModels = true;
12107
+ if (nxt === "list" || nxt === "ls" || nxt === "?") i++;
12108
+ } else a.model = argv[++i];
12109
+ } else if (x === "-C" || x === "--cwd") a.cwd = val(++i, x);
12080
12110
  else if (x === "-p" || x === "--print") {
12081
12111
  const nxt = argv[i + 1];
12082
12112
  if (nxt !== void 0 && !nxt.startsWith("-")) a.task = argv[++i];
@@ -12149,7 +12179,7 @@ var HELP = `agentx \u2014 agent runtime CLI
12149
12179
  agentx -c "<task>" continue the most recent session
12150
12180
 
12151
12181
  Flags:
12152
- -m, --model <id> model (default anthropic/claude-sonnet-4-6)
12182
+ -m, --model <id> model (default anthropic/claude-sonnet-4-6); bare -m or -m list = list all models
12153
12183
  -C, --cwd <dir> working dir / project root (default .)
12154
12184
  -c, --continue resume the most recent session in this dir
12155
12185
  -r, --resume [id] resume a session \u2014 with an id, or bare for an interactive picker
@@ -12229,6 +12259,24 @@ REPL paste: large/multi-line pastes collapse to a [Pasted text +N lines] preview
12229
12259
  function newestModel() {
12230
12260
  return listModels().slice().sort((a, b) => (getModelInfo(b)?.releaseDate ?? "").localeCompare(getModelInfo(a)?.releaseDate ?? ""))[0] ?? "";
12231
12261
  }
12262
+ function printModels() {
12263
+ const byProvider = /* @__PURE__ */ new Map();
12264
+ for (const id of listModels()) {
12265
+ const provider = getProviderFromModel(id) ?? "other";
12266
+ if (!byProvider.has(provider)) byProvider.set(provider, []);
12267
+ byProvider.get(provider).push(id);
12268
+ }
12269
+ for (const [provider, ids] of Array.from(byProvider.entries()).sort((a, b) => a[0].localeCompare(b[0]))) {
12270
+ console.log(`
12271
+ ${provider}`);
12272
+ ids.sort((a, b) => (getModelInfo(b)?.releaseDate ?? "").localeCompare(getModelInfo(a)?.releaseDate ?? ""));
12273
+ for (const id of ids) {
12274
+ const info = getModelInfo(id);
12275
+ const meta = [info?.displayName, info?.releaseDate].filter(Boolean).join(" \xB7 ");
12276
+ console.log(` ${id}${meta ? ` (${meta})` : ""}`);
12277
+ }
12278
+ }
12279
+ }
12232
12280
  function resolveModelOrNewest(model) {
12233
12281
  const want = model ?? new AgentOptions().model;
12234
12282
  const resolved = resolveModel(want);
@@ -15492,6 +15540,10 @@ async function main() {
15492
15540
  console.log(HELP);
15493
15541
  return;
15494
15542
  }
15543
+ if (args.listModels) {
15544
+ printModels();
15545
+ return;
15546
+ }
15495
15547
  if (args.update) {
15496
15548
  const msg = await checkForUpdate(VERSION).catch(() => null);
15497
15549
  if (!msg) {