@livx.cc/agentx 0.96.15 → 0.96.16

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-DRe91tAy.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-DRe91tAy.js';
1
+ import { a as AgentOptions, H as Hooks, h as RunResult, A as Agent } from './Agent-DdhD1pGw.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-DdhD1pGw.js';
3
3
  import { IFilesystem, FileMetadata } from '@livx.cc/wcli/core';
4
4
  export { CommandExecutor, FileMetadata, IFilesystem, IndexedDbFilesystem, MemFilesystem, registerHeadlessCommands } from '@livx.cc/wcli/core';
5
5
  import { BodDB } from '@bod.ee/db';
@@ -1166,6 +1166,8 @@ interface SttLike {
1166
1166
  onPartial: (text: string) => void;
1167
1167
  onUtterance: (text: string, endpointAt: number) => void;
1168
1168
  onLevel: (rms: number) => void;
1169
+ /** Optional: unrecoverable capture failure (e.g. mic produced no audio) — host tears voice down. */
1170
+ onFatal?: (message: string) => void;
1169
1171
  start(): Promise<void> | void;
1170
1172
  reset(): void;
1171
1173
  stop(): void;
@@ -1326,6 +1328,9 @@ declare class SonioxSTTOptions {
1326
1328
  /** Client-side endpoint: finalized text + no new tokens for this long = utterance (don't wait for
1327
1329
  * Soniox's semantic <end>, which adds 0.5-1.5s — the difference between ping-pong and lag). */
1328
1330
  silenceEndpointMs: number;
1331
+ /** No-audio watchdog: if the mic source stops delivering chunks for this long, capture is dead →
1332
+ * fire onFatal + stop (else Soniox idle-timeouts and reconnect-loops forever). 0 = disable. */
1333
+ noAudioTimeoutMs: number;
1329
1334
  }
1330
1335
  declare class SonioxSTT {
1331
1336
  options: SonioxSTTOptions;
@@ -1336,6 +1341,12 @@ declare class SonioxSTT {
1336
1341
  onUtterance: (text: string, endpointAt: number) => void;
1337
1342
  /** mic energy (RMS) per chunk — drives the energy-based heuristic barge-in tier */
1338
1343
  onLevel: (rms: number) => void;
1344
+ /** Unrecoverable: the mic source stopped delivering audio (Soniox starves → idle-timeout reconnect
1345
+ * loop). The host tears voice down instead of spinning forever. */
1346
+ onFatal: (message: string) => void;
1347
+ private lastChunkAt;
1348
+ private startedChunksAt;
1349
+ private noAudioTimer;
1339
1350
  private finalText;
1340
1351
  private partialText;
1341
1352
  private lastChangeAt;
package/dist/index.js CHANGED
@@ -3103,6 +3103,9 @@ function reasoningToChatFragment(model, effort) {
3103
3103
 
3104
3104
  // src/Agent.ts
3105
3105
  var log4 = forComponent("Agent");
3106
+ function isBrowserTool(name) {
3107
+ return name.startsWith("mcp__") && /browser/i.test(name.slice(5).split("__")[0]);
3108
+ }
3106
3109
  function isAbortError(err) {
3107
3110
  const e = err;
3108
3111
  const blob = `${e?.message ?? ""} ${e?.name ?? ""} ${e?.code ?? ""} ${e?.cause?.name ?? ""}`;
@@ -3150,6 +3153,17 @@ var AgentOptions = class {
3150
3153
  * and returns the (cropped) string to put in context — e.g. spill to scratch and return a recoverable,
3151
3154
  * paginated stub. Called only when a result exceeds `maxToolResultBytes`. */
3152
3155
  capToolResult;
3156
+ /** Browser-run adaptive trimming. Browser MCP tools (`mcp__*browser*`) return huge per-call results
3157
+ * (full DOM text + screenshots) that accumulate per step and blow the cumulative `maxTokens` budget
3158
+ * kill-switch mid-browse. The fix attacks PER-STEP TOKEN GROWTH, and engages ONLY once a browser tool
3159
+ * is ACTUALLY invoked (not on mere config presence — so normal coding is untouched):
3160
+ * - `resultBytes`: tighter `maxToolResultBytes` applied to BROWSER tool results (each oversized DOM is
3161
+ * cropped/spilled to a small stub → small fresh-token tail per step).
3162
+ * - `keepOutputs`: keep only the most-recent N tool-result bodies verbatim ONCE a browser tool has run
3163
+ * (older huge DOMs collapse to one-line stubs → small cached prefix → small 0.1×cacheRead/step).
3164
+ * Explicit `keepToolOutputs`/`maxToolResultBytes` are NOT overridden below their browser values — the
3165
+ * tighter of (user, browser) wins, never loosening a user's cap. Unset = off (no adaptive behavior). */
3166
+ browserTrim;
3153
3167
  /** VFS dir(s) of skills (`<dir>/<id>/SKILL.md`). If set: inject a catalog + add the `Skill` tool. Multiple dirs are merged (first wins on name collisions). */
3154
3168
  skillsDir;
3155
3169
  /** VFS dir(s) of slash-command templates (`<dir>/<name>.md`). If set: inject a catalog + add the `SlashCommand` tool. Multiple dirs are merged (first wins). */
@@ -3234,6 +3248,8 @@ var Agent = class _Agent {
3234
3248
  // session-start lifecycle hook fires once per conversation
3235
3249
  parkedMs = 0;
3236
3250
  // cumulative time blocked on the HUMAN (permission/plan prompts) — excluded from the timeout
3251
+ browserActive = false;
3252
+ // flips true once a browser MCP tool is actually invoked → adaptive trimming engages (browserTrim)
3237
3253
  /** Time a human-blocking await (a permission/plan prompt) and bank it in `parkedMs` so idle prompt
3238
3254
  * time never trips the wall-clock kill-switch. The agent did no work while parked on the user. */
3239
3255
  async park(p) {
@@ -3642,7 +3658,11 @@ var Agent = class _Agent {
3642
3658
  }
3643
3659
  if (!threw) result = await this.maybeAutoTest(tc.function.name, result);
3644
3660
  if (images?.length && !result) result = `[${images.length} image${images.length > 1 ? "s" : ""} attached]`;
3645
- const cap = this.options.maxToolResultBytes ?? 0;
3661
+ const browser = isBrowserTool(tc.function.name);
3662
+ if (browser && this.options.browserTrim) this.browserActive = true;
3663
+ const browserBytes = browser ? this.options.browserTrim?.resultBytes : void 0;
3664
+ const baseCap = this.options.maxToolResultBytes ?? 0;
3665
+ const cap = browserBytes ? baseCap > 0 ? Math.min(baseCap, browserBytes) : browserBytes : baseCap;
3646
3666
  if (!threw && cap > 0 && result.length > cap) {
3647
3667
  const info = { tool: tc.function.name, args };
3648
3668
  result = this.options.capToolResult ? await this.options.capToolResult(result, info) : cropResult(result, cap);
@@ -3686,7 +3706,9 @@ ${out}`;
3686
3706
  let out = null;
3687
3707
  if (o.compaction?.maxMessages && m.length > o.compaction.maxMessages) out = compact(m, o.compaction.maxMessages);
3688
3708
  else if (o.maxContextMessages && m.length > o.maxContextMessages) out = dropOldest(m, o.maxContextMessages);
3689
- if (o.keepToolOutputs) out = stubOldToolResults(out ?? m, o.keepToolOutputs);
3709
+ const browserKeep = this.browserActive ? o.browserTrim?.keepOutputs : void 0;
3710
+ const keep = browserKeep != null ? o.keepToolOutputs ? Math.min(o.keepToolOutputs, browserKeep) : browserKeep : o.keepToolOutputs;
3711
+ if (keep) out = stubOldToolResults(out ?? m, keep);
3690
3712
  if (o.maxContextTokens) {
3691
3713
  const pre = (out ?? m).length;
3692
3714
  out = fitTokenBudget(out ?? m, o.maxContextTokens);
@@ -6042,6 +6064,9 @@ var SonioxSTTOptions = class {
6042
6064
  /** Client-side endpoint: finalized text + no new tokens for this long = utterance (don't wait for
6043
6065
  * Soniox's semantic <end>, which adds 0.5-1.5s — the difference between ping-pong and lag). */
6044
6066
  silenceEndpointMs = 500;
6067
+ /** No-audio watchdog: if the mic source stops delivering chunks for this long, capture is dead →
6068
+ * fire onFatal + stop (else Soniox idle-timeouts and reconnect-loops forever). 0 = disable. */
6069
+ noAudioTimeoutMs = 1e4;
6045
6070
  };
6046
6071
  var SonioxSTT = class {
6047
6072
  options;
@@ -6055,6 +6080,15 @@ var SonioxSTT = class {
6055
6080
  /** mic energy (RMS) per chunk — drives the energy-based heuristic barge-in tier */
6056
6081
  onLevel = () => {
6057
6082
  };
6083
+ /** Unrecoverable: the mic source stopped delivering audio (Soniox starves → idle-timeout reconnect
6084
+ * loop). The host tears voice down instead of spinning forever. */
6085
+ onFatal = () => {
6086
+ };
6087
+ lastChunkAt = 0;
6088
+ // timestamp of the most recent mic chunk (0 = none yet)
6089
+ startedChunksAt = 0;
6090
+ // when capture started (grace before the first chunk)
6091
+ noAudioTimer = null;
6058
6092
  finalText = "";
6059
6093
  partialText = "";
6060
6094
  lastChangeAt = 0;
@@ -6106,7 +6140,22 @@ var SonioxSTT = class {
6106
6140
  this.onUtterance(combined, now2());
6107
6141
  }, 120);
6108
6142
  this.endpointTimer.unref?.();
6143
+ this.startedChunksAt = now2();
6144
+ const noAudioMs = this.options.noAudioTimeoutMs;
6145
+ if (noAudioMs > 0) {
6146
+ this.noAudioTimer = setInterval(() => {
6147
+ if (this.stopped) return;
6148
+ const ref = this.lastChunkAt || this.startedChunksAt;
6149
+ if (now2() - ref > noAudioMs) {
6150
+ log11.error(`stt: no mic audio for >${Math.round(noAudioMs / 1e3)}s \u2014 capture device stopped delivering`);
6151
+ this.onFatal("microphone stopped delivering audio (try a different input device, e.g. AirPods, or check System Settings \u2192 Sound \u2192 Input)");
6152
+ this.stop();
6153
+ }
6154
+ }, Math.max(250, Math.min(2e3, noAudioMs / 4)));
6155
+ this.noAudioTimer.unref?.();
6156
+ }
6109
6157
  await this.options.source.start((chunk) => {
6158
+ this.lastChunkAt = now2();
6110
6159
  let sum = 0;
6111
6160
  const view = new DataView(chunk.buffer, chunk.byteOffset, chunk.byteLength);
6112
6161
  for (let i = 0; i + 1 < chunk.byteLength; i += 2) {
@@ -6148,6 +6197,7 @@ var SonioxSTT = class {
6148
6197
  stop() {
6149
6198
  this.stopped = true;
6150
6199
  if (this.endpointTimer) clearInterval(this.endpointTimer);
6200
+ if (this.noAudioTimer) clearInterval(this.noAudioTimer);
6151
6201
  this.options.source?.stop();
6152
6202
  if (this.ws) this.ws.onclose = null;
6153
6203
  this.ws?.close();