@livx.cc/agentx 0.99.17 → 0.99.19

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, h as RunResult, A as Agent } from './Agent-BfQS-MfB.js';
2
- export { C as ChatFragment, D as DEFAULT_MUTATING, b as Decision, P as PermissionOptions, c as PermissionPolicy, d as PermissionRule, e as PreToolUseDecision, R as ReasoningEffort, f as RecordingHooks, g as RecordingLifecycle, T as ToolUse, i as ToolUseMeta, j as composeHooks, p as planMode, r as reasoningToChatFragment } from './Agent-BfQS-MfB.js';
1
+ import { a as AgentOptions, H as Hooks, h as RunResult, A as Agent } from './Agent-Cn9BxS8h.js';
2
+ export { C as ChatFragment, D as DEFAULT_MUTATING, b as Decision, P as PermissionOptions, c as PermissionPolicy, d as PermissionRule, e as PreToolUseDecision, R as ReasoningEffort, f as RecordingHooks, g as RecordingLifecycle, T as ToolUse, i as ToolUseMeta, j as composeHooks, p as planMode, r as reasoningToChatFragment } from './Agent-Cn9BxS8h.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
@@ -3181,6 +3181,10 @@ var AgentOptions = class {
3181
3181
  maxTokens = 2e5;
3182
3182
  /** Wall-clock ceiling in ms for the whole run. 0 = unbounded. */
3183
3183
  timeoutMs = 12e4;
3184
+ /** Idle-stall watchdog for a SINGLE streamed model request: abort it if no chunk (text OR reasoning)
3185
+ * arrives for this long. The between-steps `timeoutMs` can't preempt an in-flight `chat()`, so a
3186
+ * provider that goes silent mid-stream would otherwise park the loop forever. 0 = off. */
3187
+ stallMs = 6e4;
3184
3188
  /** Stop if the identical tool-call batch (name+args) repeats this many times in a row. 0 = off. */
3185
3189
  maxRepeats = 3;
3186
3190
  /** Cumulative cap on tool calls dispatched across the run. 0 = unbounded. */
@@ -3613,8 +3617,15 @@ var Agent = class _Agent {
3613
3617
  for (let attempt = 0; ; attempt++) {
3614
3618
  try {
3615
3619
  if (useStream) {
3616
- const r = await o.ai.chat({ model: o.model, messages: sent, tools: sentTools, stream: true, signal: o.signal, ...o.toolChoice ? { toolChoice: o.toolChoice } : {}, ...reasonOpts });
3617
- res = await this.consumeStream(r);
3620
+ const stall = this.armStallWatchdog(o.signal, o.stallMs);
3621
+ try {
3622
+ const r = await o.ai.chat({ model: o.model, messages: sent, tools: sentTools, stream: true, signal: stall.signal, ...o.toolChoice ? { toolChoice: o.toolChoice } : {}, ...reasonOpts });
3623
+ res = await this.consumeStream(r, stall.poke);
3624
+ } catch (err) {
3625
+ throw stall.reclassify(err);
3626
+ } finally {
3627
+ stall.clear();
3628
+ }
3618
3629
  } else {
3619
3630
  const r = await o.ai.chat({ model: o.model, messages: sent, tools: sentTools, stream: false, signal: o.signal, ...o.toolChoice ? { toolChoice: o.toolChoice } : {}, ...reasonOpts });
3620
3631
  res = r;
@@ -3624,16 +3635,21 @@ var Agent = class _Agent {
3624
3635
  const sc = err?.statusCode;
3625
3636
  const serverSide = sc >= 500 || sc === 408 || /Service Unavailable|overloaded|Internal server error|Bad gateway|Gateway time/i.test(String(err?.message ?? ""));
3626
3637
  const network = !sc && /ECONNRESET|ECONNREFUSED|ETIMEDOUT|ENOTFOUND|EAI_AGAIN|EPIPE|socket hang up|fetch failed|network|terminated|UND_ERR/i.test(String(err?.message ?? err?.code ?? err));
3627
- const transient = !o.signal?.aborted && !isAbortError(err) && attempt < 2 && (network || serverSide);
3638
+ const stalled = err?.code === "stall";
3639
+ const transient = !o.signal?.aborted && !isAbortError(err) && attempt < 2 && (network || serverSide || stalled);
3628
3640
  if (!transient) throw err;
3629
3641
  const waitMs = 1e3 * (attempt + 1);
3630
- log4.warn(`network drop mid-step (${err?.message ?? err}) \u2014 retrying in ${waitMs}ms`);
3631
- o.host?.notify?.({ kind: "retry", message: `connection dropped \u2014 retrying step (#${attempt + 1})` });
3642
+ log4.warn(`${stalled ? "model stall" : "network drop"} mid-step (${err?.message ?? err}) \u2014 retrying in ${waitMs}ms`);
3643
+ o.host?.notify?.({ kind: "retry", message: `${stalled ? "model stalled" : "connection dropped"} \u2014 retrying step (#${attempt + 1})` });
3632
3644
  await new Promise((r) => setTimeout(r, waitMs));
3633
3645
  }
3634
3646
  }
3635
3647
  } catch (err) {
3636
3648
  if (err?.code === "budget") return kill("budget");
3649
+ if (err?.code === "stall") {
3650
+ log4.warn(`model stalled out after retries: ${err?.message ?? err}`);
3651
+ return kill("timeout");
3652
+ }
3637
3653
  if (o.signal?.aborted || isAbortError(err)) return kill("aborted");
3638
3654
  const body = err?.body ?? err?.response?.data ?? err?.error;
3639
3655
  let bodyStr;
@@ -3704,12 +3720,54 @@ var Agent = class _Agent {
3704
3720
  * (`{kind:'text_delta'}`) and fold all chunks back into the single
3705
3721
  * ChatResponse the non-stream path would have returned.
3706
3722
  */
3707
- async consumeStream(stream) {
3723
+ /**
3724
+ * Idle-stall watchdog for one streamed model request. Returns a linked AbortController `signal` to
3725
+ * hand to `chat()`, a `poke()` to reset the idle timer on each chunk, a `reclassify()` that turns a
3726
+ * watchdog-fired abort into an Error{code:'stall'} (leaving a parent/user abort untouched), and a
3727
+ * `clear()` to disarm. Aborts the request if no `poke()` (or completion) happens within `stallMs`.
3728
+ * `stallMs = 0` disables the timer but still links parent-abort → child so cancellation propagates.
3729
+ */
3730
+ armStallWatchdog(parent, stallMs) {
3731
+ const ac = new AbortController();
3732
+ let fired = false;
3733
+ const onParentAbort = () => ac.abort();
3734
+ if (parent?.aborted) ac.abort();
3735
+ else parent?.addEventListener("abort", onParentAbort, { once: true });
3736
+ let timer;
3737
+ const poke = () => {
3738
+ if (!stallMs) return;
3739
+ if (timer) clearTimeout(timer);
3740
+ timer = setTimeout(() => {
3741
+ fired = true;
3742
+ ac.abort();
3743
+ }, stallMs);
3744
+ };
3745
+ poke();
3746
+ return {
3747
+ signal: ac.signal,
3748
+ poke,
3749
+ clear: () => {
3750
+ if (timer) clearTimeout(timer);
3751
+ parent?.removeEventListener("abort", onParentAbort);
3752
+ },
3753
+ // Distinguish OUR abort (silent provider) from a parent/user cancel, which must stay an abort.
3754
+ reclassify: (err) => {
3755
+ if (fired && !parent?.aborted) {
3756
+ const e = new Error(`model stalled: no stream output for ${stallMs >= 1e3 ? `${Math.round(stallMs / 1e3)}s` : `${stallMs}ms`}`);
3757
+ e.code = "stall";
3758
+ return e;
3759
+ }
3760
+ return err;
3761
+ }
3762
+ };
3763
+ }
3764
+ async consumeStream(stream, poke) {
3708
3765
  let content = "";
3709
3766
  let finishReason;
3710
3767
  let toolCalls;
3711
3768
  let usage;
3712
3769
  for await (const chunk of stream) {
3770
+ poke?.();
3713
3771
  if (this.options.signal?.aborted) break;
3714
3772
  if (chunk.reasoningContent) this.options.host.notify({ kind: "thinking_delta", message: chunk.reasoningContent });
3715
3773
  if (chunk.toolActivity) {