@livx.cc/agentx 0.99.44 → 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,15 +1,15 @@
1
- import { a as AgentOptions, H as Hooks, i as RunResult, A as Agent } from './Agent-f4um2q8E.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-f4um2q8E.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';
6
6
  import { BodDB } from '@bod.ee/db';
7
- import { A as AgentTool, C as ChatLike, a as ChatOptions, b as ChatResponse, j as ToolCall, H as HostBridge, U as UserQuestion, g as MessageContent } from './tools-S-ArIR3o.js';
8
- export { c as ContentPart, d as HostEvent, I as IMAGE_ELIDE_STUB, e as IMAGE_REF_SCHEME, f as ImageRef, M as Message, R as Role, S as SandboxJobRegistry, h as StreamChunk, T as TodoItem, i as Tool, k as ToolContext, l as bashTool, m as clearImageRefs, n as contentBytes, o as contentText, p as defaultTools, q as editTool, r as elideImages, s as elideInlineImages, t as elideStaleImages, u as exitSessionTool, v as expandImagesForSend, w as imagePart, x as imageRefPart, y as imageRefResult, z as makeContext, B as makeJobTools, D as messageHasImage, E as messageHasImageRef, F as messageHasInlineImage, G as mintImageRef, J as parseImageRef, K as readTool, L as remintImageRefs, N as sendBytes, O as sendBytesPer, P as toWireTools, Q as todoWriteTool, V as toolImageRef, W as toolRegistry, X as toolsByName } from './tools-S-ArIR3o.js';
7
+ import { A as AgentTool, C as ChatLike, a as ChatOptions, b as ChatResponse, j as ToolCall, H as HostBridge, U as UserQuestion, g as MessageContent } from './tools-BqL8Lk4J.js';
8
+ export { c as ContentPart, d as HostEvent, I as IMAGE_ELIDE_STUB, e as IMAGE_REF_SCHEME, f as ImageRef, M as Message, R as Role, S as SandboxJobRegistry, h as StreamChunk, T as TodoItem, i as Tool, k as ToolContext, l as bashTool, m as clearImageRefs, n as contentBytes, o as contentText, p as defaultTools, q as editTool, r as elideImages, s as elideInlineImages, t as elideStaleImages, u as exitSessionTool, v as expandImagesForSend, w as imagePart, x as imageRefPart, y as imageRefResult, z as makeContext, B as makeJobTools, D as messageHasImage, E as messageHasImageRef, F as messageHasInlineImage, G as mintImageRef, J as parseImageRef, K as readTool, L as remintImageRefs, N as sendBytes, O as sendBytesPer, P as toWireTools, Q as todoWriteTool, V as toolImageRef, W as toolRegistry, X as toolsByName } from './tools-BqL8Lk4J.js';
9
9
  import { ShellJobRegistry } from './tools.shell.js';
10
10
  import { SpokenSplitter } from '@bod.ee/voice';
11
11
  export { AudioSink, AudioSource, AuthProvider, CartesiaTTS, CartesiaTTSOptions, STT_SAMPLE_RATE, SonioxSTT, SonioxSTTOptions, SttLike, TTS_SAMPLE_RATE, TtsLike, VoiceEngine, VoiceEngineOptions, VoiceState, resolveAuth } from '@bod.ee/voice';
12
- export { M as McpCall, a as McpImage, b as McpRoute, c as McpRouteResolver, d as McpToolResult, e as McpToolSearchOptions, f as McpToolSpec, g as MountedMcpLike, h as buildMcpCatalog, m as makeLazyMcpToolSearch, i as makeMcpToolSearch, j as makeMcpToolSearchFromMounted, k as mcpToolToAgentTool, l as mcpToolsToAgentTools } from './mcp-D5eN0JyQ.js';
12
+ export { M as McpCall, a as McpImage, b as McpRoute, c as McpRouteResolver, d as McpToolResult, e as McpToolSearchOptions, f as McpToolSpec, g as MountedMcpLike, h as buildMcpCatalog, m as makeLazyMcpToolSearch, i as makeMcpToolSearch, j as makeMcpToolSearchFromMounted, k as mcpToolToAgentTool, l as mcpToolsToAgentTools } from './mcp-kgG6rtsd.js';
13
13
  import * as libx_js_src_modules_log from 'libx.js/src/modules/log';
14
14
  export { log } from 'libx.js/src/modules/log';
15
15
 
package/dist/index.js CHANGED
@@ -22,6 +22,9 @@ var init_logging = __esm({
22
22
  import { readFileSync, statSync, realpathSync } from "fs";
23
23
  import { resolve, sep } from "path";
24
24
  import { randomBytes } from "crypto";
25
+ function passesBackReasoning(model) {
26
+ return !!model && /^(deepseek|moonshot)\//.test(model);
27
+ }
25
28
  function contentText(content) {
26
29
  if (content == null) return "";
27
30
  if (typeof content === "string") return content;
@@ -205,7 +208,7 @@ function sendBytes(messages, keepRecent = 1, stub = IMAGE_ELIDE_STUB) {
205
208
  }
206
209
  function sendBytesPer(messages, keepRecent = 1, stub = IMAGE_ELIDE_STUB) {
207
210
  const keep = messages.some(bearsImage) ? imageWindows(messages, keepRecent) : null;
208
- return messages.map((m, i) => wireBytes(m.content, keep == null || keep.has(i), stub) + (m.tool_calls ? JSON.stringify(m.tool_calls).length : 0));
211
+ return messages.map((m, i) => wireBytes(m.content, keep == null || keep.has(i), stub) + (m.tool_calls ? JSON.stringify(m.tool_calls).length : 0) + (m.reasoning_content?.length ?? 0));
209
212
  }
210
213
  function wireBytes(content, kept, stub) {
211
214
  const tool = toolImageRef(content);
@@ -3944,6 +3947,10 @@ var AgentOptions = class {
3944
3947
  * not prose: a named tool with zero invocations is provable. On detection the loop nudges ONCE for a real
3945
3948
  * call, and records the names in `RunResult.fabricatedToolClaims` either way. */
3946
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;
3947
3954
  /** Fold the dropped middle of an over-long transcript into a synthetic summary (edge-safe, no LLM). Off => drop-oldest. */
3948
3955
  compaction;
3949
3956
  /** Add `Checkpoint`/`Rollback` tools (requires the fs to be an OverlayFilesystem). */
@@ -4017,6 +4024,14 @@ var Agent = class _Agent {
4017
4024
  /** Per-run ground truth: every tool name actually INVOKED this run (native dispatch + delegated
4018
4025
  * runtime activity). The ledger that makes a fabricated tool claim provable. Reset per run. */
4019
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;
4020
4035
  /** Advertised tools this run's text claimed to have used while the ledger showed zero calls. */
4021
4036
  fabricatedClaims = /* @__PURE__ */ new Set();
4022
4037
  activeHooks;
@@ -4319,6 +4334,7 @@ var Agent = class _Agent {
4319
4334
  let toolCallsTotal = 0;
4320
4335
  let lastFp = "";
4321
4336
  let repeats = 0;
4337
+ let emptyNudged = false;
4322
4338
  let closureNudged = false;
4323
4339
  let lastProgressAt = start;
4324
4340
  const kill = (finishReason) => {
@@ -4327,12 +4343,13 @@ var Agent = class _Agent {
4327
4343
  const claims = this.survivingClaims();
4328
4344
  return { text: lastAssistantText(this.transcript), steps, finishReason, messages: this.transcript, usage, usageEstimated, ...claims.length ? { fabricatedToolClaims: claims } : {} };
4329
4345
  };
4346
+ const overBudget = () => !!o.maxTokens && usage.totalTokens - 0.9 * usage.cacheReadTokens >= o.maxTokens;
4347
+ const canNudge = () => steps < o.maxSteps && !overBudget();
4330
4348
  while (true) {
4331
4349
  if (o.signal?.aborted) return kill("aborted");
4332
4350
  if (steps >= o.maxSteps) return kill("max_steps");
4333
4351
  if (o.timeoutMs && Date.now() - lastProgressAt - this.parkedMs >= o.timeoutMs) return kill("timeout");
4334
- const budgetTokens = usage.totalTokens - 0.9 * usage.cacheReadTokens;
4335
- if (o.maxTokens && budgetTokens >= o.maxTokens) return kill("budget");
4352
+ if (overBudget()) return kill("budget");
4336
4353
  steps++;
4337
4354
  this.options.host?.notify?.({ kind: "turn_start", message: `step ${steps}` });
4338
4355
  let res;
@@ -4353,7 +4370,14 @@ var Agent = class _Agent {
4353
4370
  toolTimeoutMs: effectiveToolTimeoutMs,
4354
4371
  toolExecutor: async (name, args) => {
4355
4372
  const tc = { id: `cursor-${Date.now()}`, type: "function", function: { name, arguments: JSON.stringify(args) } };
4356
- 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
+ }
4357
4381
  }
4358
4382
  } : void 0;
4359
4383
  const claudeSteerPo = o.model.startsWith("claude-code/") ? { steer: () => this.drainPendingSteers() } : void 0;
@@ -4468,14 +4492,28 @@ var Agent = class _Agent {
4468
4492
  this.transcript.push({
4469
4493
  role: "assistant",
4470
4494
  content: res.content ?? "",
4471
- ...toolCalls.length ? { tool_calls: toolCalls } : {}
4495
+ ...toolCalls.length ? { tool_calls: toolCalls } : {},
4496
+ ...res.reasoningContent && passesBackReasoning(o.model) ? { reasoning_content: res.reasoningContent } : {}
4472
4497
  });
4473
4498
  }
4474
4499
  if (toolCalls.length === 0) {
4475
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
+ }
4476
4513
  const fabricated = o.catchFabricatedToolUse && !closureNudged ? this.fabricatedToolNames(contentText(res.content ?? "")) : [];
4477
4514
  for (const n of fabricated) this.fabricatedClaims.add(n);
4478
- 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()) {
4479
4517
  closureNudged = true;
4480
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`);
4481
4519
  this.transcript.push({
@@ -4484,7 +4522,7 @@ var Agent = class _Agent {
4484
4522
  });
4485
4523
  continue;
4486
4524
  }
4487
- if (o.closeDelegatedTurns && res.endedWithoutClosing && !closureNudged && steps < o.maxSteps) {
4525
+ if (o.closeDelegatedTurns && res.endedWithoutClosing && !closureNudged && canNudge()) {
4488
4526
  closureNudged = true;
4489
4527
  log6.verbose("delegated turn went silent after a tool \u2014 nudging a closing answer");
4490
4528
  this.transcript.push({
@@ -4498,7 +4536,7 @@ var Agent = class _Agent {
4498
4536
  this.releaseStaleImages();
4499
4537
  await this.activeHooks?.onStop?.(res.content ?? "");
4500
4538
  const claims = this.survivingClaims();
4501
- 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 } : {} };
4502
4540
  }
4503
4541
  const fp = toolCalls.map((tc) => tc.function.name + ":" + (tc.function.arguments ?? "")).join("|");
4504
4542
  repeats = fp === lastFp ? repeats + 1 : 1;
@@ -4562,10 +4600,12 @@ var Agent = class _Agent {
4562
4600
  if (!stallMs) return;
4563
4601
  if (timer) clearTimeout(timer);
4564
4602
  timer = setTimeout(() => {
4603
+ if (this.hostToolsInFlight > 0 && Date.now() < this.hostToolHoldUntil) return poke();
4565
4604
  fired = true;
4566
4605
  ac.abort(stallError());
4567
4606
  }, stallMs);
4568
4607
  };
4608
+ this.stallPoke = poke;
4569
4609
  poke();
4570
4610
  const stalled = () => fired && !parent?.aborted;
4571
4611
  return {
@@ -4574,6 +4614,7 @@ var Agent = class _Agent {
4574
4614
  stalled,
4575
4615
  clear: () => {
4576
4616
  if (timer) clearTimeout(timer);
4617
+ if (this.stallPoke === poke) this.stallPoke = void 0;
4577
4618
  parent?.removeEventListener("abort", onParentAbort);
4578
4619
  },
4579
4620
  reclassify: (err) => stalled() ? stallError() : err
@@ -4594,10 +4635,14 @@ var Agent = class _Agent {
4594
4635
  const delegatedOrder = [];
4595
4636
  let sawTool = false;
4596
4637
  let trailing = "";
4638
+ let reasoning = "";
4597
4639
  for await (const chunk of stream) {
4598
4640
  poke?.();
4599
4641
  if (this.options.signal?.aborted) break;
4600
- if (chunk.reasoningContent) this.options.host.notify({ kind: "thinking_delta", message: chunk.reasoningContent });
4642
+ if (chunk.reasoningContent) {
4643
+ reasoning += chunk.reasoningContent;
4644
+ this.options.host.notify({ kind: "thinking_delta", message: chunk.reasoningContent });
4645
+ }
4601
4646
  if (chunk.toolActivity) {
4602
4647
  const a = chunk.toolActivity;
4603
4648
  const verb = a.status === "error" ? "\u2717" : a.status === "completed" ? "\u2713" : "\u2192";
@@ -4628,7 +4673,7 @@ var Agent = class _Agent {
4628
4673
  }
4629
4674
  const delegatedTools = delegatedOrder.map((k) => delegated.get(k));
4630
4675
  const endedWithoutClosing = sawTool && (!trailing.trim() || endsOnAnnouncedAction(trailing));
4631
- return { content, ...finishReason ? { finishReason } : {}, ...toolCalls ? { toolCalls } : {}, ...usage ? { usage } : {}, ...delegatedTools.length ? { delegatedTools } : {}, ...endedWithoutClosing ? { endedWithoutClosing: true } : {} };
4676
+ return { content, ...reasoning ? { reasoningContent: reasoning } : {}, ...finishReason ? { finishReason } : {}, ...toolCalls ? { toolCalls } : {}, ...usage ? { usage } : {}, ...delegatedTools.length ? { delegatedTools } : {}, ...endedWithoutClosing ? { endedWithoutClosing: true } : {} };
4632
4677
  }
4633
4678
  /**
4634
4679
  * FABRICATED TOOL USE — the failure no call-reactive layer can see, because there IS no call.
@@ -4665,8 +4710,9 @@ var Agent = class _Agent {
4665
4710
  */
4666
4711
  fabricatedToolNames(text) {
4667
4712
  const out = [];
4713
+ const invoked = new Set([...this.invokedTools].map(toolKey));
4668
4714
  for (const t of this.activeTools) {
4669
- if (this.invokedTools.has(t.name)) continue;
4715
+ if (invoked.has(toolKey(t.name))) continue;
4670
4716
  if (t.name.length < 4 || !/[_\-A-Z]/.test(t.name)) continue;
4671
4717
  const re = new RegExp(`(?:^|[^\\w])${t.name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?:[^\\w]|$)`);
4672
4718
  if (re.test(text)) out.push(t.name);
@@ -4679,7 +4725,8 @@ var Agent = class _Agent {
4679
4725
  * call that demonstrably ran. See `RunResult.fabricatedToolClaims`. */
4680
4726
  survivingClaims() {
4681
4727
  if (!this.fabricatedClaims.size) return [];
4682
- 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)));
4683
4730
  }
4684
4731
  async dispatch(tc) {
4685
4732
  this.invokedTools.add(tc.function.name);
@@ -5067,6 +5114,11 @@ function lastAssistantText(messages) {
5067
5114
  }
5068
5115
  return "";
5069
5116
  }
5117
+ var TOOL_ALIASES = { bash: "shell" };
5118
+ function toolKey(name) {
5119
+ const k = name.toLowerCase();
5120
+ return TOOL_ALIASES[k] ?? k;
5121
+ }
5070
5122
 
5071
5123
  // src/index.ts
5072
5124
  init_NodeDiskFilesystem();
@@ -7835,6 +7887,8 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
7835
7887
  instructionFiles: false,
7836
7888
  maxSteps: 8,
7837
7889
  timeoutMs: 3e4,
7890
+ failEmptyTurns: false,
7891
+ // a silent reflex turn after a dispatch is legitimate — the voice layer repairs it
7838
7892
  ...o.reflexOptions,
7839
7893
  tools,
7840
7894
  // Composed AFTER the spread so the dispatch guard can't be dropped by reflexOptions.