agent.libx.js 0.92.2 → 0.92.3

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/cli/cli.ts CHANGED
@@ -373,6 +373,10 @@ function displayHooks(fs?: IFilesystem): Hooks {
373
373
  err(cyan(`\n ⚙ ${call.name}`) + dim(' ' + summarizeCall(call.name, call.args)) + '\n');
374
374
  if (EDIT.has(call.name)) before.set(String(call.args?.path), await read(call.args?.path));
375
375
  },
376
+ onToolOutput(_call, chunk) {
377
+ if (!verboseOutput) return; // Ctrl+O verbose: live-tail streaming tool output (default chrome stays calm)
378
+ for (const ln of String(chunk).split('\n')) if (ln.trim()) err(dim(` ⋮ ${ln.length > 200 ? ln.slice(0, 200) + '…' : ln}\n`));
379
+ },
376
380
  async postToolUse(call, result) {
377
381
  spinner.stop();
378
382
  try {
@@ -1,5 +1,5 @@
1
1
  import { IFilesystem } from '@livx.cc/wcli/core';
2
- import { M as Message, H as HostBridge, A as AgentTool, C as ChatLike, e as MessageContent } from './tools-CeK5AquG.js';
2
+ import { M as Message, H as HostBridge, A as AgentTool, C as ChatLike, e as MessageContent } from './tools-GPWp7oXq.js';
3
3
 
4
4
  /**
5
5
  * Hooks — deterministic interception points around tool execution, run by the
@@ -31,6 +31,9 @@ interface Hooks {
31
31
  preToolUse?(call: ToolUse, meta?: ToolUseMeta): Promise<PreToolUseDecision | void> | PreToolUseDecision | void;
32
32
  /** Observe a tool's result after it ran (audit, metrics, side-channel). */
33
33
  postToolUse?(call: ToolUse, result: string, meta?: ToolUseMeta): void | Promise<void>;
34
+ /** Observe a tool's INCREMENTAL output while it runs (only tools that stream, e.g. the real
35
+ * Shell). Fire-and-forget — sync, never awaited, never alters the result. */
36
+ onToolOutput?(call: ToolUse, chunk: string, meta?: ToolUseMeta): void;
34
37
  /** Fired once when the agent loop stops cleanly with the model's final text. */
35
38
  onStop?(finalText: string): void;
36
39
  /** Fired once at session start (a fresh `run()`, or the first `send()`). Return a string to inject
@@ -59,11 +62,17 @@ declare class RecordingHooks implements Hooks {
59
62
  result: string;
60
63
  meta?: ToolUseMeta;
61
64
  }>;
65
+ outputs: Array<{
66
+ call: ToolUse;
67
+ chunk: string;
68
+ meta?: ToolUseMeta;
69
+ }>;
62
70
  stops: string[];
63
71
  /** tool name -> reason; a matching preToolUse call is blocked with that reason. */
64
72
  constructor(blocks?: Record<string, string>);
65
73
  preToolUse(call: ToolUse, meta?: ToolUseMeta): PreToolUseDecision | void;
66
74
  postToolUse(call: ToolUse, result: string, meta?: ToolUseMeta): void;
75
+ onToolOutput(call: ToolUse, chunk: string, meta?: ToolUseMeta): void;
67
76
  onStop(finalText: string): void;
68
77
  }
69
78
  /** Recording lifecycle hooks for tests: capture session-start/prompt-submit/pre-compact + script transforms. */
package/dist/cli.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env bun
2
- import { h as RunResult, R as ReasoningEffort } from './Agent-BzwprwHr.js';
2
+ import { h as RunResult, R as ReasoningEffort } from './Agent-QwBA0wu6.js';
3
3
  import { IFilesystem } from '@livx.cc/wcli/core';
4
- import { M as Message, c as ContentPart } from './tools-CeK5AquG.js';
4
+ import { M as Message, c as ContentPart } from './tools-GPWp7oXq.js';
5
5
 
6
6
  /**
7
7
  * On-disk session store for the CLI: each conversation is one JSON file at
package/dist/cli.js CHANGED
@@ -1386,6 +1386,18 @@ function makeRealShellTool(options) {
1386
1386
  timedOut = true;
1387
1387
  ctl.abort();
1388
1388
  }, timeoutMs);
1389
+ let pend = "";
1390
+ let flushTimer = null;
1391
+ const flushEmit = (ctx2) => {
1392
+ if (flushTimer) {
1393
+ clearTimeout(flushTimer);
1394
+ flushTimer = null;
1395
+ }
1396
+ if (pend) {
1397
+ ctx2.emit?.(redactSecrets(pend));
1398
+ pend = "";
1399
+ }
1400
+ };
1389
1401
  try {
1390
1402
  return await new Promise((resolve4) => {
1391
1403
  let out = "";
@@ -1402,7 +1414,13 @@ function makeRealShellTool(options) {
1402
1414
  return finish(`[exit 1] failed to spawn shell: ${e?.message ?? e}`);
1403
1415
  }
1404
1416
  const collect = (chunk) => {
1405
- out += typeof chunk === "string" ? chunk : chunk?.toString?.("utf8") ?? "";
1417
+ const s = typeof chunk === "string" ? chunk : chunk?.toString?.("utf8") ?? "";
1418
+ out += s;
1419
+ if (ctx.emit && !settled) {
1420
+ pend += s;
1421
+ if (pend.length >= 1024) flushEmit(ctx);
1422
+ else flushTimer ??= setTimeout(() => flushEmit(ctx), 250);
1423
+ }
1406
1424
  };
1407
1425
  proc.stdout?.on("data", collect);
1408
1426
  proc.stderr?.on("data", collect);
@@ -1412,6 +1430,7 @@ function makeRealShellTool(options) {
1412
1430
  finish(`[exit 1] ${err2?.message ?? err2}${out ? "\n" + clean(out) : ""}`);
1413
1431
  });
1414
1432
  proc.on("close", (code) => {
1433
+ flushEmit(ctx);
1415
1434
  if (ctl.signal.aborted) return finish(reasonFor(timedOut, timeoutMs, clean(out)));
1416
1435
  const body = clean(out);
1417
1436
  if (code && code !== 0) return finish(`[exit ${code}]${body ? "\n" + body : ""}`);
@@ -1420,6 +1439,7 @@ function makeRealShellTool(options) {
1420
1439
  });
1421
1440
  } finally {
1422
1441
  clearTimeout(timer);
1442
+ if (flushTimer) clearTimeout(flushTimer);
1423
1443
  ctx.signal?.removeEventListener("abort", onAbort);
1424
1444
  }
1425
1445
  }
@@ -2409,6 +2429,9 @@ function composeHooks(...list) {
2409
2429
  async postToolUse(call, result, meta) {
2410
2430
  for (const h of hooks) await h.postToolUse?.(call, result, meta);
2411
2431
  },
2432
+ onToolOutput(call, chunk, meta) {
2433
+ for (const h of hooks) h.onToolOutput?.(call, chunk, meta);
2434
+ },
2412
2435
  onStop(text) {
2413
2436
  for (const h of hooks) h.onStop?.(text);
2414
2437
  },
@@ -2919,6 +2942,13 @@ var Agent = class _Agent {
2919
2942
  let threw = false;
2920
2943
  try {
2921
2944
  log3.debug(`${tc.function.name}(${tc.function.arguments})`);
2945
+ this.ctx.emit = hooks?.onToolOutput ? (chunk) => {
2946
+ try {
2947
+ hooks.onToolOutput(call, chunk, meta);
2948
+ } catch (e) {
2949
+ log3.debug(`onToolOutput hook error: ${e}`);
2950
+ }
2951
+ } : void 0;
2922
2952
  const raw = await tool.run(args, this.ctx);
2923
2953
  if (typeof raw === "string") {
2924
2954
  result = raw;
@@ -2931,6 +2961,8 @@ var Agent = class _Agent {
2931
2961
  log3.debug(`${tc.function.name} -> error: ${msg}`);
2932
2962
  result = `Error: ${msg}`;
2933
2963
  threw = true;
2964
+ } finally {
2965
+ this.ctx.emit = void 0;
2934
2966
  }
2935
2967
  if (!threw) result = await this.maybeAutoTest(tc.function.name, result);
2936
2968
  await hooks?.postToolUse?.(call, result, meta);
@@ -3514,6 +3546,10 @@ ${recent}` : brief;
3514
3546
  postToolUse: async (call, result, meta) => {
3515
3547
  await base?.postToolUse?.(call, result, meta);
3516
3548
  report.post(call);
3549
+ },
3550
+ onToolOutput: (call, chunk, meta) => {
3551
+ base?.onToolOutput?.(call, chunk, meta);
3552
+ report.output(chunk);
3517
3553
  }
3518
3554
  } : base;
3519
3555
  const worker = new Agent({
@@ -3550,13 +3586,18 @@ ${recent}` : brief;
3550
3586
  const rec = this.tasks.get(id);
3551
3587
  if (!rec || rec.status !== "running") return clearInterval(timer);
3552
3588
  if (!inflight || !due()) return;
3553
- emit(rec, `still inside ${describeCall(inflight.call)} \u2014 ${Math.round((Date.now() - inflight.at) / 1e3)}s on this step`, inflight.call);
3589
+ const last = inflight.tail.trim().split("\n").filter(Boolean).pop()?.slice(-80);
3590
+ emit(rec, `still inside ${describeCall(inflight.call)} \u2014 ${Math.round((Date.now() - inflight.at) / 1e3)}s on this step${last ? `, last output: ${last}` : ""}`, inflight.call);
3554
3591
  }, Math.max(this.options.progressIntervalMs, 250));
3555
3592
  timer.unref?.();
3556
3593
  return {
3557
3594
  pre: (call) => {
3558
- inflight = { call, at: Date.now() };
3595
+ inflight = { call, at: Date.now(), tail: "" };
3559
3596
  },
3597
+ output: (chunk) => {
3598
+ if (inflight) inflight.tail = (inflight.tail + chunk).slice(-500);
3599
+ },
3600
+ // digest only — NEVER re-voices directly
3560
3601
  post: (call) => {
3561
3602
  steps++;
3562
3603
  inflight = null;
@@ -7106,6 +7147,11 @@ function displayHooks(fs) {
7106
7147
  \u2699 ${call.name}`) + dim(" " + summarizeCall(call.name, call.args)) + "\n");
7107
7148
  if (EDIT.has(call.name)) before.set(String(call.args?.path), await read(call.args?.path));
7108
7149
  },
7150
+ onToolOutput(_call, chunk) {
7151
+ if (!verboseOutput) return;
7152
+ for (const ln of String(chunk).split("\n")) if (ln.trim()) err(dim(` \u22EE ${ln.length > 200 ? ln.slice(0, 200) + "\u2026" : ln}
7153
+ `));
7154
+ },
7109
7155
  async postToolUse(call, result) {
7110
7156
  spinner.stop();
7111
7157
  try {