@livx.cc/agentx 0.99.45 → 0.99.46

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
@@ -1,5 +1,5 @@
1
- import { a as AgentOptions, H as Hooks, i as RunResult, A as Agent } from './Agent-Bzs57-iD.js';
2
- export { C as ChatFragment, b as CompactResult, D as DEFAULT_MUTATING, c as Decision, P as PermissionOptions, d as PermissionPolicy, e as PermissionRule, f as PreToolUseDecision, R as ReasoningEffort, g as RecordingHooks, h as RecordingLifecycle, T as ToolUse, j as ToolUseMeta, k as composeHooks, l as estimateTokens, p as planMode, r as reasoningToChatFragment } from './Agent-Bzs57-iD.js';
1
+ import { a as AgentOptions, H as Hooks, i as RunResult, A as Agent } from './Agent-OcAg2yxA.js';
2
+ export { C as ChatFragment, b as CompactResult, D as DEFAULT_MUTATING, c as Decision, P as PermissionOptions, d as PermissionPolicy, e as PermissionRule, f as PreToolUseDecision, R as ReasoningEffort, g as RecordingHooks, h as RecordingLifecycle, T as ToolUse, j as ToolUseMeta, k as composeHooks, l as estimateTokens, p as planMode, r as reasoningToChatFragment } from './Agent-OcAg2yxA.js';
3
3
  export { MODEL_ALIASES, ModelSwitch, ResolveModelSwitchOpts, modelShortLabel, resolveModelAlias, resolveModelSwitch } from './models.js';
4
4
  import { IFilesystem, FileMetadata } from '@livx.cc/wcli/core';
5
5
  export { CommandExecutor, FileMetadata, IFilesystem, IndexedDbFilesystem, MemFilesystem, registerHeadlessCommands } from '@livx.cc/wcli/core';
package/dist/index.js CHANGED
@@ -3947,6 +3947,10 @@ var AgentOptions = class {
3947
3947
  * not prose: a named tool with zero invocations is provable. On detection the loop nudges ONCE for a real
3948
3948
  * call, and records the names in `RunResult.fabricatedToolClaims` either way. */
3949
3949
  catchFabricatedToolUse = true;
3950
+ /** An empty model turn (no text, no tools) that ends the loop after work is re-prompted ONCE, then finishes
3951
+ * as `finishReason:'empty'` (a failure) instead of a phantom 'stop'. Off for layers that deliberately
3952
+ * allow a silent turn and repair it themselves (the duplex voice reflex). */
3953
+ failEmptyTurns = true;
3950
3954
  /** Fold the dropped middle of an over-long transcript into a synthetic summary (edge-safe, no LLM). Off => drop-oldest. */
3951
3955
  compaction;
3952
3956
  /** Add `Checkpoint`/`Rollback` tools (requires the fs to be an OverlayFilesystem). */
@@ -4020,6 +4024,14 @@ var Agent = class _Agent {
4020
4024
  /** Per-run ground truth: every tool name actually INVOKED this run (native dispatch + delegated
4021
4025
  * runtime activity). The ledger that makes a fabricated tool claim provable. Reset per run. */
4022
4026
  invokedTools = /* @__PURE__ */ new Set();
4027
+ /** Delegated-runtime HOST tools currently executing (cursor toolExecutor). While > 0 the provider is
4028
+ * not silent — it is waiting on US — so the idle-stall watchdog must not fire (see armStallWatchdog). */
4029
+ hostToolsInFlight = 0;
4030
+ /** The hold is bounded by the transport tool deadline: a host tool that never settles (the helper already gave
4031
+ * up on it) must not disable stall protection for the rest of the request — or this instance. */
4032
+ hostToolHoldUntil = 0;
4033
+ /** Resets the active request's stall timer; a host tool's completion restarts the model's idle window. */
4034
+ stallPoke;
4023
4035
  /** Advertised tools this run's text claimed to have used while the ledger showed zero calls. */
4024
4036
  fabricatedClaims = /* @__PURE__ */ new Set();
4025
4037
  activeHooks;
@@ -4322,6 +4334,7 @@ var Agent = class _Agent {
4322
4334
  let toolCallsTotal = 0;
4323
4335
  let lastFp = "";
4324
4336
  let repeats = 0;
4337
+ let emptyNudged = false;
4325
4338
  let closureNudged = false;
4326
4339
  let lastProgressAt = start;
4327
4340
  const kill = (finishReason) => {
@@ -4330,12 +4343,13 @@ var Agent = class _Agent {
4330
4343
  const claims = this.survivingClaims();
4331
4344
  return { text: lastAssistantText(this.transcript), steps, finishReason, messages: this.transcript, usage, usageEstimated, ...claims.length ? { fabricatedToolClaims: claims } : {} };
4332
4345
  };
4346
+ const overBudget = () => !!o.maxTokens && usage.totalTokens - 0.9 * usage.cacheReadTokens >= o.maxTokens;
4347
+ const canNudge = () => steps < o.maxSteps && !overBudget();
4333
4348
  while (true) {
4334
4349
  if (o.signal?.aborted) return kill("aborted");
4335
4350
  if (steps >= o.maxSteps) return kill("max_steps");
4336
4351
  if (o.timeoutMs && Date.now() - lastProgressAt - this.parkedMs >= o.timeoutMs) return kill("timeout");
4337
- const budgetTokens = usage.totalTokens - 0.9 * usage.cacheReadTokens;
4338
- if (o.maxTokens && budgetTokens >= o.maxTokens) return kill("budget");
4352
+ if (overBudget()) return kill("budget");
4339
4353
  steps++;
4340
4354
  this.options.host?.notify?.({ kind: "turn_start", message: `step ${steps}` });
4341
4355
  let res;
@@ -4356,7 +4370,14 @@ var Agent = class _Agent {
4356
4370
  toolTimeoutMs: effectiveToolTimeoutMs,
4357
4371
  toolExecutor: async (name, args) => {
4358
4372
  const tc = { id: `cursor-${Date.now()}`, type: "function", function: { name, arguments: JSON.stringify(args) } };
4359
- return await this.dispatch(tc);
4373
+ this.hostToolsInFlight++;
4374
+ this.hostToolHoldUntil = Math.max(this.hostToolHoldUntil, Date.now() + effectiveToolTimeoutMs);
4375
+ try {
4376
+ return await this.dispatch(tc);
4377
+ } finally {
4378
+ this.hostToolsInFlight--;
4379
+ this.stallPoke?.();
4380
+ }
4360
4381
  }
4361
4382
  } : void 0;
4362
4383
  const claudeSteerPo = o.model.startsWith("claude-code/") ? { steer: () => this.drainPendingSteers() } : void 0;
@@ -4477,9 +4498,22 @@ var Agent = class _Agent {
4477
4498
  }
4478
4499
  if (toolCalls.length === 0) {
4479
4500
  if (this.drainInjections()) continue;
4501
+ if (emptyTurn && o.failEmptyTurns) {
4502
+ if (!emptyNudged && canNudge()) {
4503
+ emptyNudged = true;
4504
+ log6.warn(`empty model turn after tool work (step ${steps}, provider finishReason=${res.finishReason ?? "n/a"}) \u2014 nudging once`);
4505
+ this.transcript.push({ role: "user", content: "Continue \u2014 you returned nothing. Answer the user now using the tool results above, or keep working if you are not done." });
4506
+ continue;
4507
+ }
4508
+ log6.warn(`model returned empty turns after tool work \u2014 surfacing as failed`);
4509
+ const e = new Error("empty_response: model returned an empty turn (no text, no tools) after a re-prompt");
4510
+ e.code = "empty";
4511
+ return { text: "", steps, finishReason: "empty", ...res.finishReason ? { providerFinishReason: res.finishReason } : {}, messages: this.transcript, usage, usageEstimated, error: e };
4512
+ }
4480
4513
  const fabricated = o.catchFabricatedToolUse && !closureNudged ? this.fabricatedToolNames(contentText(res.content ?? "")) : [];
4481
4514
  for (const n of fabricated) this.fabricatedClaims.add(n);
4482
- if (fabricated.length && steps < o.maxSteps) {
4515
+ if (fabricated.length && !canNudge()) log6.warn(`fabricated tool use: text names ${fabricated.join(", ")} but no step/budget left to demand the call \u2014 finishing with the existing answer`);
4516
+ if (fabricated.length && canNudge()) {
4483
4517
  closureNudged = true;
4484
4518
  log6.warn(`fabricated tool use: text names ${fabricated.join(", ")} but the run invoked ${this.invokedTools.size ? [...this.invokedTools].join(", ") : "nothing"} \u2014 demanding the real call`);
4485
4519
  this.transcript.push({
@@ -4488,7 +4522,7 @@ var Agent = class _Agent {
4488
4522
  });
4489
4523
  continue;
4490
4524
  }
4491
- if (o.closeDelegatedTurns && res.endedWithoutClosing && !closureNudged && steps < o.maxSteps) {
4525
+ if (o.closeDelegatedTurns && res.endedWithoutClosing && !closureNudged && canNudge()) {
4492
4526
  closureNudged = true;
4493
4527
  log6.verbose("delegated turn went silent after a tool \u2014 nudging a closing answer");
4494
4528
  this.transcript.push({
@@ -4502,7 +4536,7 @@ var Agent = class _Agent {
4502
4536
  this.releaseStaleImages();
4503
4537
  await this.activeHooks?.onStop?.(res.content ?? "");
4504
4538
  const claims = this.survivingClaims();
4505
- return { text: res.content ?? "", steps, finishReason: "stop", messages: this.transcript, usage, usageEstimated, ...claims.length ? { fabricatedToolClaims: claims } : {} };
4539
+ return { text: res.content ?? "", steps, finishReason: "stop", ...res.finishReason ? { providerFinishReason: res.finishReason } : {}, messages: this.transcript, usage, usageEstimated, ...claims.length ? { fabricatedToolClaims: claims } : {} };
4506
4540
  }
4507
4541
  const fp = toolCalls.map((tc) => tc.function.name + ":" + (tc.function.arguments ?? "")).join("|");
4508
4542
  repeats = fp === lastFp ? repeats + 1 : 1;
@@ -4566,10 +4600,12 @@ var Agent = class _Agent {
4566
4600
  if (!stallMs) return;
4567
4601
  if (timer) clearTimeout(timer);
4568
4602
  timer = setTimeout(() => {
4603
+ if (this.hostToolsInFlight > 0 && Date.now() < this.hostToolHoldUntil) return poke();
4569
4604
  fired = true;
4570
4605
  ac.abort(stallError());
4571
4606
  }, stallMs);
4572
4607
  };
4608
+ this.stallPoke = poke;
4573
4609
  poke();
4574
4610
  const stalled = () => fired && !parent?.aborted;
4575
4611
  return {
@@ -4578,6 +4614,7 @@ var Agent = class _Agent {
4578
4614
  stalled,
4579
4615
  clear: () => {
4580
4616
  if (timer) clearTimeout(timer);
4617
+ if (this.stallPoke === poke) this.stallPoke = void 0;
4581
4618
  parent?.removeEventListener("abort", onParentAbort);
4582
4619
  },
4583
4620
  reclassify: (err) => stalled() ? stallError() : err
@@ -4673,8 +4710,9 @@ var Agent = class _Agent {
4673
4710
  */
4674
4711
  fabricatedToolNames(text) {
4675
4712
  const out = [];
4713
+ const invoked = new Set([...this.invokedTools].map(toolKey));
4676
4714
  for (const t of this.activeTools) {
4677
- if (this.invokedTools.has(t.name)) continue;
4715
+ if (invoked.has(toolKey(t.name))) continue;
4678
4716
  if (t.name.length < 4 || !/[_\-A-Z]/.test(t.name)) continue;
4679
4717
  const re = new RegExp(`(?:^|[^\\w])${t.name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?:[^\\w]|$)`);
4680
4718
  if (re.test(text)) out.push(t.name);
@@ -4687,7 +4725,8 @@ var Agent = class _Agent {
4687
4725
  * call that demonstrably ran. See `RunResult.fabricatedToolClaims`. */
4688
4726
  survivingClaims() {
4689
4727
  if (!this.fabricatedClaims.size) return [];
4690
- return [...this.fabricatedClaims].filter((n) => !this.invokedTools.has(n));
4728
+ const invoked = new Set([...this.invokedTools].map(toolKey));
4729
+ return [...this.fabricatedClaims].filter((n) => !invoked.has(toolKey(n)));
4691
4730
  }
4692
4731
  async dispatch(tc) {
4693
4732
  this.invokedTools.add(tc.function.name);
@@ -5075,6 +5114,11 @@ function lastAssistantText(messages) {
5075
5114
  }
5076
5115
  return "";
5077
5116
  }
5117
+ var TOOL_ALIASES = { bash: "shell" };
5118
+ function toolKey(name) {
5119
+ const k = name.toLowerCase();
5120
+ return TOOL_ALIASES[k] ?? k;
5121
+ }
5078
5122
 
5079
5123
  // src/index.ts
5080
5124
  init_NodeDiskFilesystem();
@@ -7843,6 +7887,8 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
7843
7887
  instructionFiles: false,
7844
7888
  maxSteps: 8,
7845
7889
  timeoutMs: 3e4,
7890
+ failEmptyTurns: false,
7891
+ // a silent reflex turn after a dispatch is legitimate — the voice layer repairs it
7846
7892
  ...o.reflexOptions,
7847
7893
  tools,
7848
7894
  // Composed AFTER the spread so the dispatch guard can't be dropped by reflexOptions.