agent.libx.js 0.92.1 → 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 +5 -1
- package/dist/{Agent-BzwprwHr.d.ts → Agent-QwBA0wu6.d.ts} +10 -1
- package/dist/cli.d.ts +2 -2
- package/dist/cli.js +111 -3
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +16 -6
- package/dist/index.js +108 -2
- package/dist/index.js.map +1 -1
- package/dist/{mcp-Bn5TlRbV.d.ts → mcp-wwgXyhbi.d.ts} +1 -1
- package/dist/mcp.client.d.ts +2 -2
- package/dist/{tools-CeK5AquG.d.ts → tools-GPWp7oXq.d.ts} +4 -0
- package/dist/tools.shell.d.ts +1 -1
- package/dist/tools.shell.js +21 -1
- package/dist/tools.shell.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { a as AgentOptions, H as Hooks, h as RunResult, A as Agent } from './Agent-
|
|
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-
|
|
1
|
+
import { a as AgentOptions, H as Hooks, h as RunResult, A as Agent } from './Agent-QwBA0wu6.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-QwBA0wu6.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';
|
|
6
|
-
import { A as AgentTool, C as ChatLike, a as ChatOptions, b as ChatResponse, h as ToolCall, H as HostBridge, U as UserQuestion, e as MessageContent } from './tools-
|
|
7
|
-
export { c as ContentPart, d as HostEvent, M as Message, R as Role, S as SandboxJobRegistry, f as StreamChunk, T as TodoItem, g as Tool, i as ToolContext, j as bashTool, k as contentText, l as defaultTools, m as editTool, n as exitSessionTool, o as imagePart, p as makeContext, q as makeJobTools, r as readTool, t as toWireTools, s as todoWriteTool, u as toolRegistry, v as toolsByName } from './tools-
|
|
8
|
-
export { M as McpCall, a as McpImage, b as McpToolResult, c as McpToolSearchOptions, d as McpToolSpec, m as makeMcpToolSearch, e as mcpToolToAgentTool, f as mcpToolsToAgentTools } from './mcp-
|
|
6
|
+
import { A as AgentTool, C as ChatLike, a as ChatOptions, b as ChatResponse, h as ToolCall, H as HostBridge, U as UserQuestion, e as MessageContent } from './tools-GPWp7oXq.js';
|
|
7
|
+
export { c as ContentPart, d as HostEvent, M as Message, R as Role, S as SandboxJobRegistry, f as StreamChunk, T as TodoItem, g as Tool, i as ToolContext, j as bashTool, k as contentText, l as defaultTools, m as editTool, n as exitSessionTool, o as imagePart, p as makeContext, q as makeJobTools, r as readTool, t as toWireTools, s as todoWriteTool, u as toolRegistry, v as toolsByName } from './tools-GPWp7oXq.js';
|
|
8
|
+
export { M as McpCall, a as McpImage, b as McpToolResult, c as McpToolSearchOptions, d as McpToolSpec, m as makeMcpToolSearch, e as mcpToolToAgentTool, f as mcpToolsToAgentTools } from './mcp-wwgXyhbi.js';
|
|
9
9
|
import * as libx_js_src_modules_log from 'libx.js/src/modules/log';
|
|
10
10
|
export { log } from 'libx.js/src/modules/log';
|
|
11
11
|
|
|
@@ -565,7 +565,7 @@ declare function loadInstructions(fs: IFilesystem, names?: string[]): Promise<st
|
|
|
565
565
|
* re-voiced by the fast persona — push, not poll (unlike subagent.ts's background `Task`).
|
|
566
566
|
*
|
|
567
567
|
* Host events (via the open HostEvent union): the voice agent's standard `text_delta` stream,
|
|
568
|
-
* plus `task_started` / `task_done` / `task_error` / `task_cancelled`.
|
|
568
|
+
* plus `task_started` / `task_progress` / `task_done` / `task_error` / `task_cancelled`.
|
|
569
569
|
*/
|
|
570
570
|
|
|
571
571
|
type DuplexTaskStatus = 'running' | 'done' | 'error' | 'cancelled';
|
|
@@ -597,6 +597,11 @@ declare class DuplexAgentOptions {
|
|
|
597
597
|
/** Awaited BEFORE a delegated worker spawns — open a per-task checkpoint frame, audit, etc.
|
|
598
598
|
* (post-spawn would race the worker's first edits). */
|
|
599
599
|
onTaskStart?: (id: string, label: string) => void | Promise<void>;
|
|
600
|
+
/** Re-voice throttled worker progress asides ('[task t1 progress] …') so long tasks aren't dead
|
|
601
|
+
* air. Off by default — each update costs a voice turn (LLM call + speech). */
|
|
602
|
+
progressUpdates: boolean;
|
|
603
|
+
/** Min ms between progress re-voices per task. */
|
|
604
|
+
progressIntervalMs: number;
|
|
600
605
|
/** Host overrides for QuickLook lookups (keyed by `what`). The engine's defaults go through the
|
|
601
606
|
* (possibly jailed) fs — e.g. `.git/**` is deny-listed, so the CLI supplies 'branch' itself. */
|
|
602
607
|
quickLook?: Record<string, (path?: string) => string | Promise<string>>;
|
|
@@ -630,6 +635,11 @@ declare class DuplexAgent {
|
|
|
630
635
|
private buildBrief;
|
|
631
636
|
/** Spawn a detached worker for task `id`; its settlement notifies + enqueues the re-voice turn. */
|
|
632
637
|
private spawnWorker;
|
|
638
|
+
/** Throttled per-task progress: worker tool calls → at most one progress re-voice per interval.
|
|
639
|
+
* Two sources, one throttle: completed steps (post) and a heartbeat for a SINGLE long tool call
|
|
640
|
+
* (pre records the in-flight call; a self-cleaning timer narrates "still inside Bash — 70s").
|
|
641
|
+
* Completion supersedes: nothing is emitted once the task has settled. */
|
|
642
|
+
private progressReporter;
|
|
633
643
|
private onWorkerSettled;
|
|
634
644
|
private onWorkerFailed;
|
|
635
645
|
private failTask;
|
package/dist/index.js
CHANGED
|
@@ -1391,6 +1391,18 @@ function makeRealShellTool(options) {
|
|
|
1391
1391
|
timedOut = true;
|
|
1392
1392
|
ctl.abort();
|
|
1393
1393
|
}, timeoutMs);
|
|
1394
|
+
let pend = "";
|
|
1395
|
+
let flushTimer = null;
|
|
1396
|
+
const flushEmit = (ctx2) => {
|
|
1397
|
+
if (flushTimer) {
|
|
1398
|
+
clearTimeout(flushTimer);
|
|
1399
|
+
flushTimer = null;
|
|
1400
|
+
}
|
|
1401
|
+
if (pend) {
|
|
1402
|
+
ctx2.emit?.(redactSecrets(pend));
|
|
1403
|
+
pend = "";
|
|
1404
|
+
}
|
|
1405
|
+
};
|
|
1394
1406
|
try {
|
|
1395
1407
|
return await new Promise((resolve) => {
|
|
1396
1408
|
let out = "";
|
|
@@ -1407,7 +1419,13 @@ function makeRealShellTool(options) {
|
|
|
1407
1419
|
return finish(`[exit 1] failed to spawn shell: ${e?.message ?? e}`);
|
|
1408
1420
|
}
|
|
1409
1421
|
const collect = (chunk) => {
|
|
1410
|
-
|
|
1422
|
+
const s = typeof chunk === "string" ? chunk : chunk?.toString?.("utf8") ?? "";
|
|
1423
|
+
out += s;
|
|
1424
|
+
if (ctx.emit && !settled) {
|
|
1425
|
+
pend += s;
|
|
1426
|
+
if (pend.length >= 1024) flushEmit(ctx);
|
|
1427
|
+
else flushTimer ??= setTimeout(() => flushEmit(ctx), 250);
|
|
1428
|
+
}
|
|
1411
1429
|
};
|
|
1412
1430
|
proc.stdout?.on("data", collect);
|
|
1413
1431
|
proc.stderr?.on("data", collect);
|
|
@@ -1417,6 +1435,7 @@ function makeRealShellTool(options) {
|
|
|
1417
1435
|
finish(`[exit 1] ${err?.message ?? err}${out ? "\n" + clean(out) : ""}`);
|
|
1418
1436
|
});
|
|
1419
1437
|
proc.on("close", (code) => {
|
|
1438
|
+
flushEmit(ctx);
|
|
1420
1439
|
if (ctl.signal.aborted) return finish(reasonFor(timedOut, timeoutMs, clean(out)));
|
|
1421
1440
|
const body = clean(out);
|
|
1422
1441
|
if (code && code !== 0) return finish(`[exit ${code}]${body ? "\n" + body : ""}`);
|
|
@@ -1425,6 +1444,7 @@ function makeRealShellTool(options) {
|
|
|
1425
1444
|
});
|
|
1426
1445
|
} finally {
|
|
1427
1446
|
clearTimeout(timer);
|
|
1447
|
+
if (flushTimer) clearTimeout(flushTimer);
|
|
1428
1448
|
ctx.signal?.removeEventListener("abort", onAbort);
|
|
1429
1449
|
}
|
|
1430
1450
|
}
|
|
@@ -2405,6 +2425,9 @@ function composeHooks(...list) {
|
|
|
2405
2425
|
async postToolUse(call, result, meta) {
|
|
2406
2426
|
for (const h of hooks) await h.postToolUse?.(call, result, meta);
|
|
2407
2427
|
},
|
|
2428
|
+
onToolOutput(call, chunk, meta) {
|
|
2429
|
+
for (const h of hooks) h.onToolOutput?.(call, chunk, meta);
|
|
2430
|
+
},
|
|
2408
2431
|
onStop(text) {
|
|
2409
2432
|
for (const h of hooks) h.onStop?.(text);
|
|
2410
2433
|
},
|
|
@@ -2915,6 +2938,13 @@ var Agent = class _Agent {
|
|
|
2915
2938
|
let threw = false;
|
|
2916
2939
|
try {
|
|
2917
2940
|
log3.debug(`${tc.function.name}(${tc.function.arguments})`);
|
|
2941
|
+
this.ctx.emit = hooks?.onToolOutput ? (chunk) => {
|
|
2942
|
+
try {
|
|
2943
|
+
hooks.onToolOutput(call, chunk, meta);
|
|
2944
|
+
} catch (e) {
|
|
2945
|
+
log3.debug(`onToolOutput hook error: ${e}`);
|
|
2946
|
+
}
|
|
2947
|
+
} : void 0;
|
|
2918
2948
|
const raw = await tool.run(args, this.ctx);
|
|
2919
2949
|
if (typeof raw === "string") {
|
|
2920
2950
|
result = raw;
|
|
@@ -2927,6 +2957,8 @@ var Agent = class _Agent {
|
|
|
2927
2957
|
log3.debug(`${tc.function.name} -> error: ${msg}`);
|
|
2928
2958
|
result = `Error: ${msg}`;
|
|
2929
2959
|
threw = true;
|
|
2960
|
+
} finally {
|
|
2961
|
+
this.ctx.emit = void 0;
|
|
2930
2962
|
}
|
|
2931
2963
|
if (!threw) result = await this.maybeAutoTest(tc.function.name, result);
|
|
2932
2964
|
await hooks?.postToolUse?.(call, result, meta);
|
|
@@ -3483,6 +3515,11 @@ function digestRun(messages, maxChars) {
|
|
|
3483
3515
|
import { MemFilesystem as MemFilesystem2 } from "@livx.cc/wcli/core";
|
|
3484
3516
|
init_logging();
|
|
3485
3517
|
var log7 = forComponent("DuplexAgent");
|
|
3518
|
+
function describeCall(call) {
|
|
3519
|
+
const v = call.args && Object.values(call.args).find((x) => typeof x === "string" && x.trim());
|
|
3520
|
+
const hint = v ? ` (${String(v).replace(/\s+/g, " ").trim().slice(0, 48)})` : "";
|
|
3521
|
+
return `${call.name}${hint}`;
|
|
3522
|
+
}
|
|
3486
3523
|
var DuplexAgentOptions = class {
|
|
3487
3524
|
/** Any ai.libx.js AIClient — shared by the voice and worker agents (routed by model). */
|
|
3488
3525
|
ai;
|
|
@@ -3503,11 +3540,16 @@ var DuplexAgentOptions = class {
|
|
|
3503
3540
|
/** Awaited BEFORE a delegated worker spawns — open a per-task checkpoint frame, audit, etc.
|
|
3504
3541
|
* (post-spawn would race the worker's first edits). */
|
|
3505
3542
|
onTaskStart;
|
|
3543
|
+
/** Re-voice throttled worker progress asides ('[task t1 progress] …') so long tasks aren't dead
|
|
3544
|
+
* air. Off by default — each update costs a voice turn (LLM call + speech). */
|
|
3545
|
+
progressUpdates = false;
|
|
3546
|
+
/** Min ms between progress re-voices per task. */
|
|
3547
|
+
progressIntervalMs = 25e3;
|
|
3506
3548
|
/** Host overrides for QuickLook lookups (keyed by `what`). The engine's defaults go through the
|
|
3507
3549
|
* (possibly jailed) fs — e.g. `.git/**` is deny-listed, so the CLI supplies 'branch' itself. */
|
|
3508
3550
|
quickLook;
|
|
3509
3551
|
};
|
|
3510
|
-
var VOICE_SYSTEM_PROMPT = 'You are a spoken voice assistant \u2014 the user HEARS everything you say. Use short sentences. One idea per sentence. No markdown, no bullet lists, no code blocks, no headings, no emoji.\nKeep turns SHORT \u2014 one to three sentences, then stop. Never lecture, enumerate cases, or add caveats unprompted. Conversation is a fast exchange: give the one thing asked, and let the user pull more if they want it.\nYou work in a pair: you talk, and a background worker with FULL access to the user\'s environment (files, shell, web) does the hands-on work. You can find out or do ANYTHING by calling `Delegate` with a clear, self-contained brief \u2014 so NEVER tell the user you can\'t see, access, or do something. Delegate and find out. When the user mentions their project, folder, files, or environment ("this project", "the current folder", "my code"), delegate IMMEDIATELY \u2014 do not ask for paths or details the worker can discover itself. Never pretend to have done the work or invent results \u2014 the worker\'s report is your only source.\nAfter calling Delegate, tell the user you are on it in one short sentence, then end your turn. Do not wait for the result.\nResults arrive later as events like "[task t1 completed] \u2026" or "[task t1 failed] \u2026". When one arrives, summarize it for the ear in one or two short sentences. Never read raw file paths, diffs, or code aloud verbatim.\nDo not fire a second Delegate for work already in flight \u2014 check `TaskStatus` first. Use `CancelTask` when the user asks to stop something.\nPRIORITY: when the user says goodbye or wants to end/finish/wrap up the session ("ok bye", "that\'s all", "let\'s finish", "let\'s end", "goodnight", "exit", "wrap up"), call `ExitSession` IMMEDIATELY \u2014 do not delegate, do not check status, just exit.\nFor TRIVIAL instant lookups only \u2014 current time, git branch, listing a folder, peeking at a small file \u2014 use `QuickLook` (instant, no task). Anything requiring searching, reasoning, running commands, or editing still goes through `Delegate`.\nNEVER claim to have stored, saved, or remembered something durably \u2014 you cannot. Anything the user wants persisted (their name, preferences, notes) must be Delegated so a worker writes it to memory.\nUser messages may arrive via speech-to-text and can carry transcription artifacts \u2014 odd words, cut-offs, homophones ("for you" vs "folder"). Read for INTENT, not surface text. If a message seems garbled or surprising, briefly confirm what they meant ("did you mean\u2026?") instead of answering the literal words.';
|
|
3552
|
+
var VOICE_SYSTEM_PROMPT = 'You are a spoken voice assistant \u2014 the user HEARS everything you say. Use short sentences. One idea per sentence. No markdown, no bullet lists, no code blocks, no headings, no emoji.\nKeep turns SHORT \u2014 one to three sentences, then stop. Never lecture, enumerate cases, or add caveats unprompted. Conversation is a fast exchange: give the one thing asked, and let the user pull more if they want it.\nYou work in a pair: you talk, and a background worker with FULL access to the user\'s environment (files, shell, web) does the hands-on work. You can find out or do ANYTHING by calling `Delegate` with a clear, self-contained brief \u2014 so NEVER tell the user you can\'t see, access, or do something. Delegate and find out. When the user mentions their project, folder, files, or environment ("this project", "the current folder", "my code"), delegate IMMEDIATELY \u2014 do not ask for paths or details the worker can discover itself. Never pretend to have done the work or invent results \u2014 the worker\'s report is your only source.\nAfter calling Delegate, tell the user you are on it in one short sentence, then end your turn. Do not wait for the result.\nResults arrive later as events like "[task t1 completed] \u2026" or "[task t1 failed] \u2026". When one arrives, summarize it for the ear in one or two short sentences. "[task t1 progress] \u2026" events are interim status, NOT results \u2014 give at most a half-sentence aside ("still on it \u2014 running tests now") and end your turn. Never present progress as a finished result.\nNever read raw file paths, diffs, or code aloud verbatim.\nDo not fire a second Delegate for work already in flight \u2014 check `TaskStatus` first. Use `CancelTask` when the user asks to stop something.\nPRIORITY: when the user says goodbye or wants to end/finish/wrap up the session ("ok bye", "that\'s all", "let\'s finish", "let\'s end", "goodnight", "exit", "wrap up"), call `ExitSession` IMMEDIATELY \u2014 do not delegate, do not check status, just exit.\nFor TRIVIAL instant lookups only \u2014 current time, git branch, listing a folder, peeking at a small file \u2014 use `QuickLook` (instant, no task). Anything requiring searching, reasoning, running commands, or editing still goes through `Delegate`.\nNEVER claim to have stored, saved, or remembered something durably \u2014 you cannot. Anything the user wants persisted (their name, preferences, notes) must be Delegated so a worker writes it to memory.\nUser messages may arrive via speech-to-text and can carry transcription artifacts \u2014 odd words, cut-offs, homophones ("for you" vs "folder"). Read for INTENT, not surface text. If a message seems garbled or surprising, briefly confirm what they meant ("did you mean\u2026?") instead of answering the literal words.';
|
|
3511
3553
|
var VOICE_STYLE_CONVERSATIONAL = `Speak like a person in a live conversation, not an assistant reading a script. React first, then deliver: a quick impulsive beat ("oh nice", "hmm, hold on", "ah, got it") before the substance. Use contractions always. Vary sentence length \u2014 some very short. Light fillers and backchannels are fine ("mm-hm", "right", "let's see") but at most one per reply \u2014 never stack them. When you delegate, say it like a human would ("hang on, let me actually dig into that \u2014 gimme a minute") instead of announcing a task. When a result comes back, react to it like you just found out ("okay so \u2014 turns out\u2026"). Match the user's energy: a quick question gets a quick answer \u2014 a few words is a perfectly good turn. Prefer a short answer plus an offer ("want the details?") over covering everything. Never narrate your own mechanics (no "I will now delegate", no task ids out loud).`;
|
|
3512
3554
|
var DuplexAgent = class {
|
|
3513
3555
|
options;
|
|
@@ -3592,18 +3634,78 @@ ${recent}` : brief;
|
|
|
3592
3634
|
spawnWorker(id, label, briefText) {
|
|
3593
3635
|
const o = this.options;
|
|
3594
3636
|
const controller = new AbortController();
|
|
3637
|
+
const base = o.workerOptions?.hooks;
|
|
3638
|
+
const report = o.progressUpdates ? this.progressReporter(id) : void 0;
|
|
3639
|
+
const hooks = report ? {
|
|
3640
|
+
...base,
|
|
3641
|
+
preToolUse: async (call, meta) => {
|
|
3642
|
+
const d = await base?.preToolUse?.(call, meta);
|
|
3643
|
+
report.pre(call);
|
|
3644
|
+
return d;
|
|
3645
|
+
},
|
|
3646
|
+
postToolUse: async (call, result, meta) => {
|
|
3647
|
+
await base?.postToolUse?.(call, result, meta);
|
|
3648
|
+
report.post(call);
|
|
3649
|
+
},
|
|
3650
|
+
onToolOutput: (call, chunk, meta) => {
|
|
3651
|
+
base?.onToolOutput?.(call, chunk, meta);
|
|
3652
|
+
report.output(chunk);
|
|
3653
|
+
}
|
|
3654
|
+
} : base;
|
|
3595
3655
|
const worker = new Agent({
|
|
3596
3656
|
ai: o.ai,
|
|
3597
3657
|
fs: o.fs,
|
|
3598
3658
|
model: o.workerModel,
|
|
3599
3659
|
...o.workerOptions,
|
|
3600
3660
|
// may override ai/fs/model/tools/… —
|
|
3661
|
+
...hooks ? { hooks } : {},
|
|
3601
3662
|
signal: controller.signal
|
|
3602
3663
|
// …but never the per-task cancellation signal
|
|
3603
3664
|
});
|
|
3604
3665
|
const promise = worker.run(briefText).then((res) => this.onWorkerSettled(id, res)).catch((err) => this.onWorkerFailed(id, err));
|
|
3605
3666
|
this.tasks.set(id, { id, label, status: "running", controller, promise });
|
|
3606
3667
|
}
|
|
3668
|
+
/** Throttled per-task progress: worker tool calls → at most one progress re-voice per interval.
|
|
3669
|
+
* Two sources, one throttle: completed steps (post) and a heartbeat for a SINGLE long tool call
|
|
3670
|
+
* (pre records the in-flight call; a self-cleaning timer narrates "still inside Bash — 70s").
|
|
3671
|
+
* Completion supersedes: nothing is emitted once the task has settled. */
|
|
3672
|
+
progressReporter(id) {
|
|
3673
|
+
let lastAt = Date.now();
|
|
3674
|
+
let steps = 0;
|
|
3675
|
+
let inflight = null;
|
|
3676
|
+
const due = () => {
|
|
3677
|
+
const rec = this.tasks.get(id);
|
|
3678
|
+
return rec && rec.status === "running" && Date.now() - lastAt >= this.options.progressIntervalMs ? rec : void 0;
|
|
3679
|
+
};
|
|
3680
|
+
const emit = (rec, line, call) => {
|
|
3681
|
+
lastAt = Date.now();
|
|
3682
|
+
this.notify("task_progress", `task ${id} (${rec.label}): ${line}`, { id, steps, call: call.name });
|
|
3683
|
+
this.queueRevoice(`[task ${id} progress] ${line}`);
|
|
3684
|
+
};
|
|
3685
|
+
const timer = setInterval(() => {
|
|
3686
|
+
const rec = this.tasks.get(id);
|
|
3687
|
+
if (!rec || rec.status !== "running") return clearInterval(timer);
|
|
3688
|
+
if (!inflight || !due()) return;
|
|
3689
|
+
const last = inflight.tail.trim().split("\n").filter(Boolean).pop()?.slice(-80);
|
|
3690
|
+
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);
|
|
3691
|
+
}, Math.max(this.options.progressIntervalMs, 250));
|
|
3692
|
+
timer.unref?.();
|
|
3693
|
+
return {
|
|
3694
|
+
pre: (call) => {
|
|
3695
|
+
inflight = { call, at: Date.now(), tail: "" };
|
|
3696
|
+
},
|
|
3697
|
+
output: (chunk) => {
|
|
3698
|
+
if (inflight) inflight.tail = (inflight.tail + chunk).slice(-500);
|
|
3699
|
+
},
|
|
3700
|
+
// digest only — NEVER re-voices directly
|
|
3701
|
+
post: (call) => {
|
|
3702
|
+
steps++;
|
|
3703
|
+
inflight = null;
|
|
3704
|
+
const rec = due();
|
|
3705
|
+
if (rec) emit(rec, `still running \u2014 ${steps} steps so far, now: ${describeCall(call)}`, call);
|
|
3706
|
+
}
|
|
3707
|
+
};
|
|
3708
|
+
}
|
|
3607
3709
|
onWorkerSettled(id, res) {
|
|
3608
3710
|
const rec = this.tasks.get(id);
|
|
3609
3711
|
if (res.finishReason === "aborted" || rec.status === "cancelled") {
|
|
@@ -3820,6 +3922,7 @@ var RecordingHooks = class {
|
|
|
3820
3922
|
blocks;
|
|
3821
3923
|
pre = [];
|
|
3822
3924
|
post = [];
|
|
3925
|
+
outputs = [];
|
|
3823
3926
|
stops = [];
|
|
3824
3927
|
preToolUse(call, meta) {
|
|
3825
3928
|
this.pre.push({ call, meta });
|
|
@@ -3829,6 +3932,9 @@ var RecordingHooks = class {
|
|
|
3829
3932
|
postToolUse(call, result, meta) {
|
|
3830
3933
|
this.post.push({ call, result, meta });
|
|
3831
3934
|
}
|
|
3935
|
+
onToolOutput(call, chunk, meta) {
|
|
3936
|
+
this.outputs.push({ call, chunk, meta });
|
|
3937
|
+
}
|
|
3832
3938
|
onStop(finalText) {
|
|
3833
3939
|
this.stops.push(finalText);
|
|
3834
3940
|
}
|