agent.libx.js 0.92.2 → 0.92.4
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 +36 -6
- package/dist/{Agent-BzwprwHr.d.ts → Agent-QwBA0wu6.d.ts} +10 -1
- package/dist/cli.d.ts +2 -2
- package/dist/cli.js +80 -8
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.js +48 -3
- 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/cli/cli.ts
CHANGED
|
@@ -358,23 +358,38 @@ function makeHost(format: 'text' | 'json' | 'stream-json' = 'text', opts?: { str
|
|
|
358
358
|
};
|
|
359
359
|
}
|
|
360
360
|
|
|
361
|
-
/** Hooks that render tool activity to stderr: a preToolUse header, and for edits a colorized diff.
|
|
362
|
-
|
|
361
|
+
/** Hooks that render tool activity to stderr: a preToolUse header, and for edits a colorized diff.
|
|
362
|
+
* `opts.background` marks the agent as a BACKGROUND one (duplex workers): its chrome lands async on
|
|
363
|
+
* top of a live prompt, so it must never drive the foreground spinner (the 'agentx › '/'⠹ thinking…'
|
|
364
|
+
* flicker), clears the prompt line before printing, and repaints via the callback after. `opts.gate`
|
|
365
|
+
* is evaluated per call — false renders nothing (minimal mode: task events only). */
|
|
366
|
+
function displayHooks(fs?: IFilesystem, opts?: { gate?: () => boolean; background?: () => void }): Hooks {
|
|
363
367
|
const EDIT = new Set(['Edit', 'MultiEdit', 'Write']);
|
|
364
368
|
const before = new Map<string, string>();
|
|
365
369
|
const MAX = 64 * 1024; // skip diffing very large files
|
|
370
|
+
const bg = opts?.background;
|
|
371
|
+
const on = () => !opts?.gate || opts.gate();
|
|
366
372
|
const read = async (p: unknown): Promise<string> => {
|
|
367
373
|
if (!fs || typeof p !== 'string') return '';
|
|
368
374
|
try { return await fs.readFile(p); } catch { return ''; }
|
|
369
375
|
};
|
|
370
376
|
return {
|
|
371
377
|
async preToolUse(call) {
|
|
372
|
-
|
|
378
|
+
if (!on()) return;
|
|
379
|
+
if (bg) err('\r\x1b[0J'); else spinner.stop(); // foreground: a tool is about to run → stop "thinking…"
|
|
373
380
|
err(cyan(`\n ⚙ ${call.name}`) + dim(' ' + summarizeCall(call.name, call.args)) + '\n');
|
|
374
381
|
if (EDIT.has(call.name)) before.set(String(call.args?.path), await read(call.args?.path));
|
|
382
|
+
bg?.();
|
|
383
|
+
},
|
|
384
|
+
onToolOutput(_call, chunk) {
|
|
385
|
+
if (!verboseOutput || !on()) return; // Ctrl+O verbose: live-tail streaming tool output (default chrome stays calm)
|
|
386
|
+
if (bg) err('\r\x1b[0J');
|
|
387
|
+
for (const ln of String(chunk).split('\n')) if (ln.trim()) err(dim(` ⋮ ${ln.length > 200 ? ln.slice(0, 200) + '…' : ln}\n`));
|
|
388
|
+
bg?.();
|
|
375
389
|
},
|
|
376
390
|
async postToolUse(call, result) {
|
|
377
|
-
|
|
391
|
+
if (!on()) return;
|
|
392
|
+
if (bg) err('\r\x1b[0J'); else spinner.stop();
|
|
378
393
|
try {
|
|
379
394
|
if (EDIT.has(call.name)) {
|
|
380
395
|
const path = String(call.args?.path);
|
|
@@ -401,7 +416,8 @@ function displayHooks(fs?: IFilesystem): Hooks {
|
|
|
401
416
|
err(dim(' ⎿ (no output)\n')); // empty result → confirm completion so it doesn't look hung
|
|
402
417
|
}
|
|
403
418
|
} finally {
|
|
404
|
-
|
|
419
|
+
if (bg) bg(); // background: repaint the live prompt below the chrome — NEVER the spinner
|
|
420
|
+
else spinner.start(); // foreground: tool done → the model is thinking about the next step
|
|
405
421
|
}
|
|
406
422
|
},
|
|
407
423
|
};
|
|
@@ -961,6 +977,9 @@ async function repl(args: Args, ai: ChatLike, cfg: Partial<AgentConfig>, cwd: st
|
|
|
961
977
|
let voiceIO: VoiceIO | undefined; // real voice I/O (--voice + keys): mic→STT in, text_delta→TTS out
|
|
962
978
|
let editorRef: LineEditor | undefined; // bound once the line editor exists — async chrome repaints the prompt via it
|
|
963
979
|
let workerOptions: AgentOptions | undefined;
|
|
980
|
+
// Worker UI verbosity: 'full' = ⚙ tool chrome per worker step; 'minimal' = task events only
|
|
981
|
+
// (started/progress/⦿ done). Voice defaults minimal (chrome is noise next to speech); /workers toggles live.
|
|
982
|
+
let workerChrome: 'full' | 'minimal' = 'full';
|
|
964
983
|
let duplexPersist: () => void = () => {}; // bound once the session exists (re-voice fires async)
|
|
965
984
|
let duplexAccount: (data: any) => void = () => {}; // worker cost → session meta (bound below)
|
|
966
985
|
// Workers are non-interactive: a permission 'ask' can't pop a menu mid-conversation (it would fight
|
|
@@ -978,6 +997,11 @@ async function repl(args: Args, ai: ChatLike, cfg: Partial<AgentConfig>, cwd: st
|
|
|
978
997
|
if (workerOptions.permissions)
|
|
979
998
|
workerOptions.permissions = new PermissionPolicy({ ...workerOptions.permissions.options, host: undefined, ask: duplexAsk });
|
|
980
999
|
workerOptions.planMode = false; // a worker's plan could never be approved (no host) — it would stall to maxSteps
|
|
1000
|
+
// Workers are BACKGROUND agents: rebuild their display hooks so chrome never drives the foreground
|
|
1001
|
+
// spinner (the 'agentx › '/'⠹ thinking…' flicker) and repaints the live prompt after each print.
|
|
1002
|
+
workerChrome = args.voice ? 'minimal' : (cfg.workerChrome ?? 'full');
|
|
1003
|
+
const workerDisplay = displayHooks(agent.options.fs, { gate: () => workerChrome === 'full', background: () => editorRef?.redrawNow() });
|
|
1004
|
+
workerOptions.hooks = cfg.hooks ? composeHooks(workerDisplay, hooksFromConfig(cfg.hooks)) : workerDisplay;
|
|
981
1005
|
// The single voice: markdown-rendered deltas on stdout = the SPOKEN channel. Everything else
|
|
982
1006
|
// (⚙ tool chrome, task events, worker results) is VISUAL chrome on stderr — when wired to a
|
|
983
1007
|
// real voice API, only text_delta becomes speech; the rest feeds the screen.
|
|
@@ -1395,7 +1419,13 @@ async function repl(args: Args, ai: ChatLike, cfg: Partial<AgentConfig>, cwd: st
|
|
|
1395
1419
|
else err(dim(' ' + (duplex ? `voice ${dx!.options.voiceModel} · worker ${work.model}` : work.model) + '\n'));
|
|
1396
1420
|
},
|
|
1397
1421
|
},
|
|
1398
|
-
...(duplex ? {
|
|
1422
|
+
...(duplex ? { workers: {
|
|
1423
|
+
desc: 'duplex worker chrome — /workers <full|minimal>: per-step ⚙ tool activity vs task events only',
|
|
1424
|
+
run: async (a: string[]) => {
|
|
1425
|
+
if (a[0] === 'full' || a[0] === 'minimal') { workerChrome = a[0]; err(green(` ✓ worker chrome → ${a[0]}\n`)); return; }
|
|
1426
|
+
err(dim(` worker chrome: ${workerChrome} (use /workers full|minimal)\n`));
|
|
1427
|
+
},
|
|
1428
|
+
}, 'voice-model': {
|
|
1399
1429
|
desc: 'switch the duplex voice (fast) model — /voice-model <id>, or alone for a picker',
|
|
1400
1430
|
run: async (a: string[]) => {
|
|
1401
1431
|
const apply = (id: string) => {
|
|
@@ -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-
|
|
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-
|
|
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-
|
|
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
|
-
|
|
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
|
-
|
|
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: "" };
|
|
3596
|
+
},
|
|
3597
|
+
output: (chunk) => {
|
|
3598
|
+
if (inflight) inflight.tail = (inflight.tail + chunk).slice(-500);
|
|
3559
3599
|
},
|
|
3600
|
+
// digest only — NEVER re-voices directly
|
|
3560
3601
|
post: (call) => {
|
|
3561
3602
|
steps++;
|
|
3562
3603
|
inflight = null;
|
|
@@ -7087,10 +7128,12 @@ function makeHost(format = "text", opts) {
|
|
|
7087
7128
|
}
|
|
7088
7129
|
};
|
|
7089
7130
|
}
|
|
7090
|
-
function displayHooks(fs) {
|
|
7131
|
+
function displayHooks(fs, opts) {
|
|
7091
7132
|
const EDIT = /* @__PURE__ */ new Set(["Edit", "MultiEdit", "Write"]);
|
|
7092
7133
|
const before = /* @__PURE__ */ new Map();
|
|
7093
7134
|
const MAX = 64 * 1024;
|
|
7135
|
+
const bg = opts?.background;
|
|
7136
|
+
const on = () => !opts?.gate || opts.gate();
|
|
7094
7137
|
const read = async (p) => {
|
|
7095
7138
|
if (!fs || typeof p !== "string") return "";
|
|
7096
7139
|
try {
|
|
@@ -7101,13 +7144,25 @@ function displayHooks(fs) {
|
|
|
7101
7144
|
};
|
|
7102
7145
|
return {
|
|
7103
7146
|
async preToolUse(call) {
|
|
7104
|
-
|
|
7147
|
+
if (!on()) return;
|
|
7148
|
+
if (bg) err("\r\x1B[0J");
|
|
7149
|
+
else spinner.stop();
|
|
7105
7150
|
err(cyan(`
|
|
7106
7151
|
\u2699 ${call.name}`) + dim(" " + summarizeCall(call.name, call.args)) + "\n");
|
|
7107
7152
|
if (EDIT.has(call.name)) before.set(String(call.args?.path), await read(call.args?.path));
|
|
7153
|
+
bg?.();
|
|
7154
|
+
},
|
|
7155
|
+
onToolOutput(_call, chunk) {
|
|
7156
|
+
if (!verboseOutput || !on()) return;
|
|
7157
|
+
if (bg) err("\r\x1B[0J");
|
|
7158
|
+
for (const ln of String(chunk).split("\n")) if (ln.trim()) err(dim(` \u22EE ${ln.length > 200 ? ln.slice(0, 200) + "\u2026" : ln}
|
|
7159
|
+
`));
|
|
7160
|
+
bg?.();
|
|
7108
7161
|
},
|
|
7109
7162
|
async postToolUse(call, result) {
|
|
7110
|
-
|
|
7163
|
+
if (!on()) return;
|
|
7164
|
+
if (bg) err("\r\x1B[0J");
|
|
7165
|
+
else spinner.stop();
|
|
7111
7166
|
try {
|
|
7112
7167
|
if (EDIT.has(call.name)) {
|
|
7113
7168
|
const path = String(call.args?.path);
|
|
@@ -7136,7 +7191,8 @@ function displayHooks(fs) {
|
|
|
7136
7191
|
err(dim(" \u23BF (no output)\n"));
|
|
7137
7192
|
}
|
|
7138
7193
|
} finally {
|
|
7139
|
-
|
|
7194
|
+
if (bg) bg();
|
|
7195
|
+
else spinner.start();
|
|
7140
7196
|
}
|
|
7141
7197
|
}
|
|
7142
7198
|
};
|
|
@@ -7648,6 +7704,7 @@ async function repl(args, ai, cfg, cwd) {
|
|
|
7648
7704
|
let voiceIO;
|
|
7649
7705
|
let editorRef;
|
|
7650
7706
|
let workerOptions;
|
|
7707
|
+
let workerChrome = "full";
|
|
7651
7708
|
let duplexPersist = () => {
|
|
7652
7709
|
};
|
|
7653
7710
|
let duplexAccount = () => {
|
|
@@ -7663,6 +7720,9 @@ async function repl(args, ai, cfg, cwd) {
|
|
|
7663
7720
|
if (workerOptions.permissions)
|
|
7664
7721
|
workerOptions.permissions = new PermissionPolicy({ ...workerOptions.permissions.options, host: void 0, ask: duplexAsk });
|
|
7665
7722
|
workerOptions.planMode = false;
|
|
7723
|
+
workerChrome = args.voice ? "minimal" : cfg.workerChrome ?? "full";
|
|
7724
|
+
const workerDisplay = displayHooks(agent.options.fs, { gate: () => workerChrome === "full", background: () => editorRef?.redrawNow() });
|
|
7725
|
+
workerOptions.hooks = cfg.hooks ? composeHooks(workerDisplay, hooksFromConfig(cfg.hooks)) : workerDisplay;
|
|
7666
7726
|
const base = makeHost("text", { stream: true });
|
|
7667
7727
|
const host = {
|
|
7668
7728
|
...base,
|
|
@@ -8121,7 +8181,19 @@ ${extra}` : body);
|
|
|
8121
8181
|
else err(dim(" " + (duplex ? `voice ${dx.options.voiceModel} \xB7 worker ${work.model}` : work.model) + "\n"));
|
|
8122
8182
|
}
|
|
8123
8183
|
},
|
|
8124
|
-
...duplex ? {
|
|
8184
|
+
...duplex ? { workers: {
|
|
8185
|
+
desc: "duplex worker chrome \u2014 /workers <full|minimal>: per-step \u2699 tool activity vs task events only",
|
|
8186
|
+
run: async (a) => {
|
|
8187
|
+
if (a[0] === "full" || a[0] === "minimal") {
|
|
8188
|
+
workerChrome = a[0];
|
|
8189
|
+
err(green(` \u2713 worker chrome \u2192 ${a[0]}
|
|
8190
|
+
`));
|
|
8191
|
+
return;
|
|
8192
|
+
}
|
|
8193
|
+
err(dim(` worker chrome: ${workerChrome} (use /workers full|minimal)
|
|
8194
|
+
`));
|
|
8195
|
+
}
|
|
8196
|
+
}, "voice-model": {
|
|
8125
8197
|
desc: "switch the duplex voice (fast) model \u2014 /voice-model <id>, or alone for a picker",
|
|
8126
8198
|
run: async (a) => {
|
|
8127
8199
|
const apply = (id) => {
|