@livx.cc/agentx 0.99.121 → 0.99.122

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.js CHANGED
@@ -2754,7 +2754,7 @@ async function psGroupCpuSeconds(pgid) {
2754
2754
  try {
2755
2755
  return await winTreeCpuSeconds(pgid);
2756
2756
  } catch (e) {
2757
- log9.debug(`cpu probe failed for ${pgid}: ${e}`);
2757
+ log10.debug(`cpu probe failed for ${pgid}: ${e}`);
2758
2758
  return void 0;
2759
2759
  }
2760
2760
  }
@@ -2763,7 +2763,7 @@ async function psGroupCpuSeconds(pgid) {
2763
2763
  const out = await new Promise((res, rej) => execFile2("ps", ["-A", "-o", "pid=,ppid=,pgid=,time="], { timeout: 5e3, env: { ...process.env, LC_ALL: "C" } }, (e, so) => e ? rej(e) : res(String(so))));
2764
2764
  return sumTreeCpu(out, pgid);
2765
2765
  } catch (e) {
2766
- log9.debug(`ps cpu probe failed for group ${pgid}: ${e}`);
2766
+ log10.debug(`ps cpu probe failed for group ${pgid}: ${e}`);
2767
2767
  return void 0;
2768
2768
  }
2769
2769
  }
@@ -2912,7 +2912,7 @@ function makeRealShellTool(options) {
2912
2912
  proc.stderr?.on("data", collect);
2913
2913
  proc.on("error", (err) => {
2914
2914
  if (err?.name === "AbortError" || ctl.signal.aborted) return finish(reasonFor(timedOut, timeoutMs, clean(out)));
2915
- log9.debug("shell spawn error", err);
2915
+ log10.debug("shell spawn error", err);
2916
2916
  finish(`[exit 1] ${err?.message ?? err}${out ? "\n" + clean(out) : ""}`);
2917
2917
  });
2918
2918
  proc.on("close", (code) => {
@@ -2991,7 +2991,7 @@ ${clean(out) || "(no output yet)"}`;
2991
2991
  }
2992
2992
  ];
2993
2993
  }
2994
- var log9, clean, DETACHED, shellArgv, SECRET_ENV_RE, _spawn, ShellJobRegistry, shellStatus, NO_JOB2;
2994
+ var log10, clean, DETACHED, shellArgv, SECRET_ENV_RE, _spawn, ShellJobRegistry, shellStatus, NO_JOB2;
2995
2995
  var init_tools_shell = __esm({
2996
2996
  "src/tools.shell.ts"() {
2997
2997
  "use strict";
@@ -3001,7 +3001,7 @@ var init_tools_shell = __esm({
3001
3001
  init_shell_sandbox();
3002
3002
  init_tools_jobs();
3003
3003
  init_platform();
3004
- log9 = forComponent("shell");
3004
+ log10 = forComponent("shell");
3005
3005
  clean = (s) => truncateOutput(redactSecrets(s.replace(/\n+$/, "")));
3006
3006
  DETACHED = { stdio: ["ignore", "pipe", "pipe"], ...DETACH_OPTIONS };
3007
3007
  shellArgv = (sh, command) => ({ bin: sh.bin, args: sh.args(command), ...sh.spawnOptions ? { spawnOptions: sh.spawnOptions } : {} });
@@ -4276,7 +4276,11 @@ function childOptionsFor(opts, fs, depth, maxDepth, agentType, model, root) {
4276
4276
  if (po) childOpts.providerOptions = po;
4277
4277
  const deny = opts.permissions?.options.rules.filter((r) => r.decision === "deny") ?? [];
4278
4278
  if (deny.length) childOpts.permissions = new PermissionPolicy({ rules: deny, default: "allow" });
4279
- if (opts.hooks?.preToolUse) childOpts.hooks = { preToolUse: opts.hooks.preToolUse.bind(opts.hooks) };
4279
+ const h = opts.hooks;
4280
+ if (h?.preToolUse || opts.inheritPostToolUse && h?.postToolUse) childOpts.hooks = {
4281
+ ...h.preToolUse ? { preToolUse: h.preToolUse.bind(h) } : {},
4282
+ ...opts.inheritPostToolUse && h.postToolUse ? { postToolUse: h.postToolUse.bind(h) } : {}
4283
+ };
4280
4284
  return childOpts;
4281
4285
  }
4282
4286
  function makeTaskTool(opts) {
@@ -4500,6 +4504,147 @@ async function loadAgents(fs, dir) {
4500
4504
  init_OverlayFilesystem();
4501
4505
  init_logging();
4502
4506
 
4507
+ // src/modes.ts
4508
+ init_logging();
4509
+ var log8 = forComponent("modes");
4510
+ var WRITE_TOOLS = /* @__PURE__ */ new Set(["Write", "Edit", "MultiEdit", "ApplyEdits"]);
4511
+ var PLANNER_BLOCKED = /* @__PURE__ */ new Set([...WRITE_TOOLS, "bash", "Shell"]);
4512
+ var PLANNER_REFUSAL = "Refused: you are the planner and do not write files. Delegate the change with Task/TaskBatch (model set to the worker) and give the worker a full spec.";
4513
+ var PLAN_FIRST = "Refused: check your plan with `Advise` before the first write (files, data shape, the hard parts). Then write.";
4514
+ var ADVISE_LIMIT = 3;
4515
+ var SNAPSHOT_CAP = 6e4;
4516
+ function pairOk(kind, main, other) {
4517
+ if (other === main) {
4518
+ log8.warn(`${kind} off: both sides are ${main}`);
4519
+ return false;
4520
+ }
4521
+ if (main.startsWith("cursor/") || other.startsWith("cursor/")) {
4522
+ log8.warn(`${kind} off: cursor models are not supported (${main} / ${other})`);
4523
+ return false;
4524
+ }
4525
+ return true;
4526
+ }
4527
+ function delegateWorkerFor(planner, worker) {
4528
+ return worker && pairOk("delegate", planner, worker) ? worker : void 0;
4529
+ }
4530
+ function advisorFor(worker, advisor) {
4531
+ return advisor && pairOk("advisor", worker, advisor) ? advisor : void 0;
4532
+ }
4533
+ function delegateTools(tools, planner) {
4534
+ return tools.map((t) => !PLANNER_BLOCKED.has(t.name) ? t : {
4535
+ ...t,
4536
+ run: async (...a) => a[1]?.model === planner ? PLANNER_REFUSAL : t.run(...a)
4537
+ });
4538
+ }
4539
+ function delegatePrompt(worker) {
4540
+ return `
4541
+
4542
+ ## You are the PLANNER
4543
+ You cannot write files: Write, Edit, MultiEdit, ApplyEdits and the shells are refused for you. Every file change goes through a worker. Read what you need, decide the design (files, data shape, interfaces, the rules that matter), then hand the writing to workers with \`TaskBatch\` (independent files in parallel) or \`Task\`, with \`model: "${worker}"\` (the default). Give each worker a complete, self-contained spec: it sees the files but not your reasoning. When they finish, read the result and send fix-up tasks for anything wrong or missing. Finish only when the work is complete.`;
4544
+ }
4545
+ function advisorPrompt(advisor) {
4546
+ return `
4547
+
4548
+ ## You have an ADVISOR
4549
+ The \`Advise\` tool asks a stronger model (${advisor}). You do the work yourself. Call \`Advise\` once BEFORE you write \u2014 writes are refused until you do (ask it to check your plan: files, data shape, the hard parts) \u2014 and once AFTER the work is complete (ask it to review for defects), then fix what it finds. Do not call it for small steps.`;
4550
+ }
4551
+ async function textOf(r) {
4552
+ if (r && typeof r[Symbol.asyncIterator] === "function") {
4553
+ let s = "";
4554
+ for await (const c of r) if (typeof c?.content === "string") s += c.content;
4555
+ return s;
4556
+ }
4557
+ return String(r?.content ?? r?.text ?? "");
4558
+ }
4559
+ function makeAdvisor(o) {
4560
+ let used = 0;
4561
+ const limit = o.limit ?? ADVISE_LIMIT;
4562
+ const tool = {
4563
+ name: "Advise",
4564
+ description: `Ask a stronger model (${o.advisor}) for advice. It sees the user's request${o.snapshot ? " and the current state" : ""}, not this conversation, so put everything it needs in \`question\`. Use it at most ${limit} times: once for the plan before you write, once to review the finished work.`,
4565
+ parameters: { type: "object", properties: { question: { type: "string", description: "what you want advice on, with the context it needs" } }, required: ["question"] },
4566
+ async run(args) {
4567
+ const question = String(args?.question ?? "").trim();
4568
+ if (!question) return "No advice: the question is empty.";
4569
+ if (used >= limit) return `No advice: the limit of ${limit} calls is used. Continue with your own judgment.`;
4570
+ used++;
4571
+ let state = "";
4572
+ if (o.snapshot) {
4573
+ const s = String(await Promise.resolve(o.snapshot()).catch((err) => {
4574
+ log8.warn("advisor snapshot failed:", String(err));
4575
+ return "";
4576
+ }) || "(empty)");
4577
+ state = `
4578
+
4579
+ ## Current state
4580
+ ${s.length > SNAPSHOT_CAP ? `${s.slice(0, SNAPSHOT_CAP)}
4581
+ \u2026(cut)` : s}`;
4582
+ }
4583
+ const review = used > 1 ? "" : "\n\n(When the work is complete, call Advise again for a review.)";
4584
+ try {
4585
+ const r = await o.ai.chat({
4586
+ model: o.advisor,
4587
+ messages: [
4588
+ { role: "system", content: "You advise a developer mid-task. Answer the question directly and concretely: the plan, the data shape, the risks, or the defects with their fix. Be brief. Do not write the whole solution." },
4589
+ { role: "user", content: `## User request
4590
+ ${o.request()}${state}
4591
+
4592
+ ## Question
4593
+ ${question}` }
4594
+ ],
4595
+ maxTokens: 2e3
4596
+ });
4597
+ return ((await textOf(r)).trim() || "No advice: the advisor returned nothing.") + review;
4598
+ } catch (err) {
4599
+ log8.warn("advisor call failed:", String(err));
4600
+ return `No advice: the advisor call failed (${String(err?.message ?? err).slice(0, 120)}). Continue with your own judgment.`;
4601
+ }
4602
+ }
4603
+ };
4604
+ const gate = (tools) => tools.map((t) => !WRITE_TOOLS.has(t.name) ? t : { ...t, run: async (...a) => used ? t.run(...a) : PLAN_FIRST });
4605
+ return { tool, gate, used: () => used };
4606
+ }
4607
+ function meterWorkers(ai, main) {
4608
+ let total;
4609
+ let resolved;
4610
+ const seen = () => {
4611
+ resolved = ai.modelUsed ?? resolved;
4612
+ };
4613
+ const add = (u) => {
4614
+ if (!u) return;
4615
+ const t = total ??= { promptTokens: 0, completionTokens: 0, totalTokens: 0, cacheCreationTokens: 0, cacheReadTokens: 0 };
4616
+ t.promptTokens += u.promptTokens ?? 0;
4617
+ t.completionTokens += u.completionTokens ?? 0;
4618
+ t.totalTokens += u.totalTokens ?? 0;
4619
+ t.cacheCreationTokens += u.cacheCreationTokens ?? 0;
4620
+ t.cacheReadTokens += u.cacheReadTokens ?? 0;
4621
+ };
4622
+ async function* tap(it) {
4623
+ let last;
4624
+ try {
4625
+ for await (const c of it) {
4626
+ if (c?.usage) last = c.usage;
4627
+ yield c;
4628
+ }
4629
+ } finally {
4630
+ add(last);
4631
+ seen();
4632
+ }
4633
+ }
4634
+ const chat = async (o) => {
4635
+ const r = await ai.chat(o);
4636
+ if (o?.model === main) return r;
4637
+ if (r && typeof r[Symbol.asyncIterator] === "function") return tap(r);
4638
+ add(r?.usage);
4639
+ seen();
4640
+ return r;
4641
+ };
4642
+ return { ai: new Proxy(ai, { get: (t, k) => k === "chat" ? chat : Reflect.get(t, k) }), usage: () => total && { ...total }, model: () => resolved, reset: () => {
4643
+ total = void 0;
4644
+ resolved = void 0;
4645
+ } };
4646
+ }
4647
+
4503
4648
  // src/lint.ts
4504
4649
  var CODE_RE = /\.(ts|tsx|js|jsx|mjs|cjs)$/;
4505
4650
  function checkSyntax(path, content) {
@@ -4579,7 +4724,7 @@ function reasoningToChatFragment(model, effort) {
4579
4724
  }
4580
4725
 
4581
4726
  // src/Agent.ts
4582
- var log8 = forComponent("Agent");
4727
+ var log9 = forComponent("Agent");
4583
4728
  function envInt(key, fallback) {
4584
4729
  const n = typeof process !== "undefined" ? Number(process.env?.[key]) : NaN;
4585
4730
  return Number.isFinite(n) && n >= 0 ? n : fallback;
@@ -4708,6 +4853,13 @@ var AgentOptions = class {
4708
4853
  hooks;
4709
4854
  /** If true: add the `Task` tool so the agent can spawn depth-limited child agents over the VFS. */
4710
4855
  subagents = false;
4856
+ /** DELEGATE MODE: `model` plans, `worker` writes. The planner's Write/Edit/MultiEdit/ApplyEdits/bash/Shell are
4857
+ * refused at execution; Task/TaskBatch are on (maxDepth 1, children default to `worker`) and children get the
4858
+ * parent's `postToolUse` too. Off when the models are equal or either is cursor/*. Wins over `advisor`. */
4859
+ delegate;
4860
+ /** ADVISOR MODE: `model` does the work; an `Advise` tool asks `advisor.model` (≤`limit`, default 3) with only
4861
+ * the request + `snapshot()` (host state, optional) + the question. The first write waits for one consult. */
4862
+ advisor;
4711
4863
  /** VFS dir of typed-subagent defs (`<dir>/<name>.md`). If set with `subagents`: inject a catalog + enable the `Task` `agentType` param. */
4712
4864
  agentsDir;
4713
4865
  /** Current recursion depth (0 = top-level); spawned children run at depth+1. */
@@ -4816,6 +4968,11 @@ var Agent = class _Agent {
4816
4968
  lastTrimNotified = 0;
4817
4969
  // last auto-trim drop count surfaced via host.notify (dedup)
4818
4970
  activeTools = [];
4971
+ meter;
4972
+ // delegate/advisor: meters the second model's calls
4973
+ modeModel;
4974
+ lastTask = "";
4975
+ // the current turn's text — what the advisor reads as the request
4819
4976
  /** claude-code bridge for agentx-only tools (ccBridge.ts) + the current step's executor it calls. */
4820
4977
  ccServer;
4821
4978
  ccExec;
@@ -4999,7 +5156,7 @@ var Agent = class _Agent {
4999
5156
  const disk = new NodeDiskFilesystem2(process.cwd());
5000
5157
  await disk.init();
5001
5158
  this.options.fs = new JailedFilesystem2(disk);
5002
- log8.info(`no fs provided \u2014 defaulting to jailed real disk at ${process.cwd()}`);
5159
+ log9.info(`no fs provided \u2014 defaulting to jailed real disk at ${process.cwd()}`);
5003
5160
  }
5004
5161
  this.buildCtx();
5005
5162
  }
@@ -5016,6 +5173,12 @@ var Agent = class _Agent {
5016
5173
  const fs = o.fs;
5017
5174
  let systemPrompt = o.systemPrompt;
5018
5175
  let tools = o.tools;
5176
+ this.ctx.model = o.model;
5177
+ const worker = o.delegate && delegateWorkerFor(o.model, o.delegate.worker);
5178
+ const advisor = !worker && o.advisor ? advisorFor(o.model, o.advisor.model) : void 0;
5179
+ this.meter = worker || advisor ? meterWorkers(o.ai, o.model) : void 0;
5180
+ const ai = this.meter?.ai ?? o.ai;
5181
+ const adv = advisor ? makeAdvisor({ ai, advisor, request: () => this.lastTask, snapshot: o.advisor.snapshot, limit: o.advisor.limit }) : void 0;
5019
5182
  if (o.instructionFiles !== false) {
5020
5183
  const names = Array.isArray(o.instructionFiles) ? o.instructionFiles : void 0;
5021
5184
  const ins = await loadInstructions(fs, names);
@@ -5046,7 +5209,7 @@ var Agent = class _Agent {
5046
5209
  if (tool) tools = [...tools, tool];
5047
5210
  }
5048
5211
  if (o.host && o.askUserQuestion) tools = [...tools, askUserQuestionTool];
5049
- if (o.subagents) {
5212
+ if (o.subagents || worker) {
5050
5213
  let agents;
5051
5214
  if (o.agentsDir) {
5052
5215
  const loaded = await loadAgents(fs, o.agentsDir);
@@ -5054,17 +5217,18 @@ var Agent = class _Agent {
5054
5217
  if (loaded.catalog) systemPrompt += "\n\n" + loaded.catalog;
5055
5218
  }
5056
5219
  const taskOpts = {
5057
- ai: o.ai,
5058
- model: o.model,
5220
+ ai,
5221
+ model: worker || o.model,
5059
5222
  fs,
5060
5223
  depth: o.depth,
5061
- maxDepth: o.maxDepth,
5224
+ maxDepth: worker ? Math.min(o.maxDepth, o.depth + 1) : o.maxDepth,
5062
5225
  agents,
5226
+ inheritPostToolUse: !!worker,
5063
5227
  hooks: o.hooks,
5064
5228
  traceDir: o.traceDir,
5065
5229
  permissions: o.permissions,
5066
5230
  // a child obeys the parent's allow/deny rules (else Task is a one-call bypass)
5067
- tools: [...o.tools, ...this.injectedTools],
5231
+ tools: worker ? delegateTools([...o.tools, ...this.injectedTools], o.model) : [...o.tools, ...this.injectedTools],
5068
5232
  systemPrompt: o.systemPrompt,
5069
5233
  // A child never resumes the parent's own claude-code session (the host's `resume` link is the parent's).
5070
5234
  providerOptionsFor: o.providerOptionsFor ?? ((m) => modelProvider(m) === modelProvider(o.model) && o.providerOptions ? { ...o.providerOptions, resume: void 0, onClaudeCodeSession: void 0 } : void 0)
@@ -5077,6 +5241,15 @@ var Agent = class _Agent {
5077
5241
  if (plan) tools = [...tools, plan.tool];
5078
5242
  this.activeHooks = composeHooks(o.hooks, plan?.hooks, o.permissions?.hooks());
5079
5243
  this.activeTools = [...tools, ...this.injectedTools];
5244
+ if (worker) {
5245
+ this.activeTools = delegateTools(this.activeTools, o.model);
5246
+ systemPrompt += delegatePrompt(worker);
5247
+ }
5248
+ if (adv) {
5249
+ this.activeTools = [...adv.gate(this.activeTools), adv.tool];
5250
+ systemPrompt += advisorPrompt(advisor);
5251
+ }
5252
+ this.modeModel = worker || advisor;
5080
5253
  this.systemPromptCache = systemPrompt;
5081
5254
  this.prepared = true;
5082
5255
  return systemPrompt;
@@ -5089,6 +5262,7 @@ var Agent = class _Agent {
5089
5262
  this.started = false;
5090
5263
  this.invokedTools.clear();
5091
5264
  this.fabricatedClaims.clear();
5265
+ this.lastTask = contentText(task);
5092
5266
  const systemPrompt = await this.prepare(contentText(task));
5093
5267
  const startCtx = await this.fireSessionStart();
5094
5268
  const userContent = await this.applyPromptSubmit(task);
@@ -5124,6 +5298,7 @@ var Agent = class _Agent {
5124
5298
  */
5125
5299
  async send(task) {
5126
5300
  await this.ensureFs();
5301
+ this.lastTask = contentText(task);
5127
5302
  const systemPrompt = await this.prepare(contentText(task));
5128
5303
  const startCtx = await this.fireSessionStart();
5129
5304
  const userContent = await this.applyPromptSubmit(task);
@@ -5174,6 +5349,12 @@ var Agent = class _Agent {
5174
5349
  if (next !== this.transcript) this.transcript = next;
5175
5350
  }
5176
5351
  async runLoop() {
5352
+ this.meter?.reset();
5353
+ const res = await this.loop();
5354
+ const workerUsage = this.meter?.usage();
5355
+ return workerUsage ? { ...res, workerModel: this.meter.model() ?? this.modeModel, workerUsage } : res;
5356
+ }
5357
+ async loop() {
5177
5358
  const o = this.options;
5178
5359
  const wireTools = toWireTools(this.activeTools);
5179
5360
  const useStream = o.stream === true && typeof o.host?.notify === "function";
@@ -5193,7 +5374,7 @@ var Agent = class _Agent {
5193
5374
  let lastProgressAt = start;
5194
5375
  const turnUser = this.transcript[this.transcript.length - 1];
5195
5376
  const kill = (finishReason) => {
5196
- log8.warn(`kill-switch: ${finishReason} (steps=${steps}, tokens=${usage.totalTokens}, budgetTokens=${Math.round(spent)}, ms=${Date.now() - start - this.parkedMs}${finishReason === "timeout" ? `, idle=${Date.now() - lastProgressAt - this.parkedMs}ms` : ""}${this.parkedMs ? ` +${this.parkedMs} parked` : ""})`);
5377
+ log9.warn(`kill-switch: ${finishReason} (steps=${steps}, tokens=${usage.totalTokens}, budgetTokens=${Math.round(spent)}, ms=${Date.now() - start - this.parkedMs}${finishReason === "timeout" ? `, idle=${Date.now() - lastProgressAt - this.parkedMs}ms` : ""}${this.parkedMs ? ` +${this.parkedMs} parked` : ""})`);
5197
5378
  this.jobs.killAll(`run ended: ${finishReason}`, true);
5198
5379
  const claims = this.survivingClaims();
5199
5380
  return { text: lastAssistantText(this.transcript, turnUser), steps, finishReason, messages: this.transcript, usage, usageEstimated, ...finishReason === "budget" ? { budget: { spent: Math.round(spent), limit: o.maxTokens, stepPrompt } } : {}, ...claims.length ? { fabricatedToolClaims: claims } : {} };
@@ -5219,9 +5400,9 @@ var Agent = class _Agent {
5219
5400
  this.nativeHoldMs = o.nativeHoldMs;
5220
5401
  if (isCursorWithTools) {
5221
5402
  if (typeof hostToolTimeoutMs === "number" && effectiveToolTimeoutMs !== hostToolTimeoutMs) {
5222
- log8.warn(`providerOptions.toolTimeoutMs=${hostToolTimeoutMs}ms is below what the registered tools need \u2014 RAISED to ${effectiveToolTimeoutMs}ms. ${toolTimeoutViolations(this.activeTools, hostToolTimeoutMs).length} tool(s) could outlive it, and the transport would have destroyed their results. To lower it, lower the tools' own maxDurationMs (e.g. the Shell tool's maxTimeoutMs) instead.`);
5403
+ log9.warn(`providerOptions.toolTimeoutMs=${hostToolTimeoutMs}ms is below what the registered tools need \u2014 RAISED to ${effectiveToolTimeoutMs}ms. ${toolTimeoutViolations(this.activeTools, hostToolTimeoutMs).length} tool(s) could outlive it, and the transport would have destroyed their results. To lower it, lower the tools' own maxDurationMs (e.g. the Shell tool's maxTimeoutMs) instead.`);
5223
5404
  }
5224
- for (const v of toolTimeoutViolations(this.activeTools, effectiveToolTimeoutMs)) log8.warn(v);
5405
+ for (const v of toolTimeoutViolations(this.activeTools, effectiveToolTimeoutMs)) log9.warn(v);
5225
5406
  }
5226
5407
  const hostExec = async (name, args) => {
5227
5408
  const tc = { id: `host-${Date.now()}`, type: "function", function: { name, arguments: JSON.stringify(args) } };
@@ -5280,7 +5461,7 @@ var Agent = class _Agent {
5280
5461
  } catch (err) {
5281
5462
  if (!this.imagesOff && /does not support .*input/i.test(String(err?.message ?? "")) && sent.some((m) => Array.isArray(m.content) && m.role === "tool")) {
5282
5463
  this.imagesOff = true;
5283
- log8.warn(`model refused image input (${err?.message}) \u2014 resending with image Reads as text stubs`);
5464
+ log9.warn(`model refused image input (${err?.message}) \u2014 resending with image Reads as text stubs`);
5284
5465
  sent = this.trimContext();
5285
5466
  attempt--;
5286
5467
  continue;
@@ -5292,11 +5473,11 @@ var Agent = class _Agent {
5292
5473
  const empty = err?.code === "empty" || err?.code === "cursor_silent";
5293
5474
  const maxAttempts = empty ? Math.max(o.emptyRetries, 0) : 2;
5294
5475
  const replaysTool = stalled && this.toolRanThisAttempt;
5295
- if (replaysTool) log8.warn("model stall after tool(s) already ran this step \u2014 not retrying (a retry would replay them)");
5476
+ if (replaysTool) log9.warn("model stall after tool(s) already ran this step \u2014 not retrying (a retry would replay them)");
5296
5477
  const transient = !o.signal?.aborted && !isAbortError(err) && !replaysTool && attempt < maxAttempts && (network || serverSide || stalled || empty);
5297
5478
  if (!transient) throw err;
5298
5479
  const waitMs = (empty ? o.emptyRetryBaseMs : 1e3) * (attempt + 1);
5299
- log8.warn(`${empty ? "empty model turn" : stalled ? "model stall" : "network drop"} mid-step (${err?.message ?? err}) \u2014 retrying in ${waitMs}ms`);
5480
+ log9.warn(`${empty ? "empty model turn" : stalled ? "model stall" : "network drop"} mid-step (${err?.message ?? err}) \u2014 retrying in ${waitMs}ms`);
5300
5481
  o.host?.notify?.({ kind: "retry", message: `${empty ? "model returned nothing" : stalled ? "model stalled" : "connection dropped"} \u2014 retrying step (#${attempt + 1})` });
5301
5482
  await new Promise((r) => setTimeout(r, waitMs));
5302
5483
  }
@@ -5304,12 +5485,12 @@ var Agent = class _Agent {
5304
5485
  } catch (err) {
5305
5486
  if (err?.code === "budget") return kill("budget");
5306
5487
  if (err?.code === "stall") {
5307
- log8.warn(`model stalled out after retries: ${err?.message ?? err}`);
5488
+ log9.warn(`model stalled out after retries: ${err?.message ?? err}`);
5308
5489
  return { ...kill("timeout"), providerFinishReason: "none (stalled)", error: err };
5309
5490
  }
5310
5491
  if (o.signal?.aborted || isAbortError(err)) return kill("aborted");
5311
5492
  if (err?.code === "empty" || err?.code === "cursor_silent") {
5312
- log8.warn(`model returned empty/silent turns after retries \u2014 surfacing as failed`);
5493
+ log9.warn(`model returned empty/silent turns after retries \u2014 surfacing as failed`);
5313
5494
  const pfr = err?.providerFinishReason;
5314
5495
  return { text: "", steps, finishReason: "empty", ...pfr ? { providerFinishReason: pfr } : {}, messages: this.transcript, usage, usageEstimated, error: err };
5315
5496
  }
@@ -5322,13 +5503,13 @@ var Agent = class _Agent {
5322
5503
  }
5323
5504
  if (bodyStr && err instanceof Error && !err.message.includes(bodyStr)) err.detail = bodyStr;
5324
5505
  const errLog = err instanceof Error ? { message: err.message, ...err.detail ? { detail: err.detail } : {}, stack: err.stack } : err;
5325
- log8.error(`chat() failed: ${err?.message ?? err}${bodyStr ? ` \u2014 ${bodyStr}` : ""}`, errLog);
5506
+ log9.error(`chat() failed: ${err?.message ?? err}${bodyStr ? ` \u2014 ${bodyStr}` : ""}`, errLog);
5326
5507
  return { text: "", steps, finishReason: "error", messages: this.transcript, usage, usageEstimated, error: err };
5327
5508
  }
5328
5509
  if (o.signal?.aborted) return kill("aborted");
5329
5510
  if (res.finishReason === "error" && !res.toolCalls?.length) {
5330
5511
  const err = new Error(contentText(res.content ?? "").trim() || "provider error");
5331
- log8.error(`chat() failed: ${err.message}`);
5512
+ log9.error(`chat() failed: ${err.message}`);
5332
5513
  return { text: "", steps, finishReason: "error", messages: this.transcript, usage, usageEstimated, error: err };
5333
5514
  }
5334
5515
  if (!res.usage) {
@@ -5396,21 +5577,21 @@ var Agent = class _Agent {
5396
5577
  if (emptyTurn && o.failEmptyTurns) {
5397
5578
  if (!emptyNudged && canNudge()) {
5398
5579
  emptyNudged = true;
5399
- log8.warn(`empty model turn after tool work (step ${steps}, provider finishReason=${res.finishReason ?? "n/a"}) \u2014 nudging once`);
5580
+ log9.warn(`empty model turn after tool work (step ${steps}, provider finishReason=${res.finishReason ?? "n/a"}) \u2014 nudging once`);
5400
5581
  this.transcript.push({ role: "user", content: "Continue \u2014 you returned nothing. Answer the user now using the tool results above, or keep working if you are not done." });
5401
5582
  continue;
5402
5583
  }
5403
- log8.warn(`model returned empty turns after tool work \u2014 surfacing as failed`);
5584
+ log9.warn(`model returned empty turns after tool work \u2014 surfacing as failed`);
5404
5585
  const e = new Error("empty_response: model returned an empty turn (no text, no tools) after a re-prompt");
5405
5586
  e.code = "empty";
5406
5587
  return { text: "", steps, finishReason: "empty", ...res.finishReason ? { providerFinishReason: res.finishReason } : {}, messages: this.transcript, usage, usageEstimated, error: e };
5407
5588
  }
5408
5589
  const fabricated = o.catchFabricatedToolUse && !closureNudged ? this.fabricatedToolNames(contentText(res.content ?? "")) : [];
5409
5590
  for (const n of fabricated) this.fabricatedClaims.add(n);
5410
- if (fabricated.length && !canNudge()) log8.warn(`fabricated tool use: text names ${fabricated.join(", ")} but no step/budget left to demand the call \u2014 finishing with the existing answer`);
5591
+ if (fabricated.length && !canNudge()) log9.warn(`fabricated tool use: text names ${fabricated.join(", ")} but no step/budget left to demand the call \u2014 finishing with the existing answer`);
5411
5592
  if (fabricated.length && canNudge()) {
5412
5593
  closureNudged = true;
5413
- log8.warn(`fabricated tool use: text names ${fabricated.join(", ")} but the run invoked ${this.invokedTools.size ? [...this.invokedTools].join(", ") : "nothing"} \u2014 demanding the real call`);
5594
+ log9.warn(`fabricated tool use: text names ${fabricated.join(", ")} but the run invoked ${this.invokedTools.size ? [...this.invokedTools].join(", ") : "nothing"} \u2014 demanding the real call`);
5414
5595
  this.transcript.push({
5415
5596
  role: "user",
5416
5597
  content: `You referred to ${fabricated.map((n) => `\`${n}\``).join(", ")} in your reply, but you never actually called ${fabricated.length > 1 ? "those tools" : "that tool"} \u2014 no tool call was made, so any result you reported for ${fabricated.length > 1 ? "them" : "it"} was invented. Either call ${fabricated.length > 1 ? "them" : "it"} for real now, or answer without ${fabricated.length > 1 ? "them" : "it"} and do NOT claim any outcome \u2014 including a failure or a timeout \u2014 for a call you did not make.`
@@ -5419,14 +5600,14 @@ var Agent = class _Agent {
5419
5600
  }
5420
5601
  if (o.closeDelegatedTurns && res.endedWithoutClosing && !closureNudged && canNudge()) {
5421
5602
  closureNudged = true;
5422
- log8.verbose("delegated turn went silent after a tool \u2014 nudging a closing answer");
5603
+ log9.verbose("delegated turn went silent after a tool \u2014 nudging a closing answer");
5423
5604
  this.transcript.push({
5424
5605
  role: "user",
5425
5606
  content: "You ran tools but did not reply. Answer the user now using the tool results above; if a tool failed, say so plainly. Do NOT re-run those tools, do NOT narrate what you did, and do NOT ask for permission to continue."
5426
5607
  });
5427
5608
  continue;
5428
5609
  }
5429
- log8.verbose(`completed in ${steps} step(s)`);
5610
+ log9.verbose(`completed in ${steps} step(s)`);
5430
5611
  const drained = await this.jobs.drain(o.signal);
5431
5612
  if (o.signal?.aborted) return kill("aborted");
5432
5613
  if (drained.length) lastProgressAt = Date.now();
@@ -5460,7 +5641,7 @@ var Agent = class _Agent {
5460
5641
  if (this.ctx.needsInput) {
5461
5642
  const question = this.ctx.needsInput;
5462
5643
  this.ctx.needsInput = void 0;
5463
- log8.warn(`needs_input: ${question.question} (steps=${steps})`);
5644
+ log9.warn(`needs_input: ${question.question} (steps=${steps})`);
5464
5645
  await this.jobs.drain();
5465
5646
  const text = [
5466
5647
  `OPEN QUESTION (run paused \u2014 no human was reachable): ${question.question}`,
@@ -5471,7 +5652,7 @@ var Agent = class _Agent {
5471
5652
  if (this.ctx.endTurn) {
5472
5653
  this.ctx.endTurn = void 0;
5473
5654
  if (this.drainInjections()) continue;
5474
- log8.verbose(`turn ended by a tool (steps=${steps})`);
5655
+ log9.verbose(`turn ended by a tool (steps=${steps})`);
5475
5656
  await this.jobs.drain(o.signal);
5476
5657
  return { text: "", steps, finishReason: "stop", messages: this.transcript, usage, usageEstimated };
5477
5658
  }
@@ -5509,12 +5690,12 @@ var Agent = class _Agent {
5509
5690
  if (this.toolHoldActive()) {
5510
5691
  const now3 = Date.now();
5511
5692
  const who = this.jobs.holding().map((j) => `${j.kind} "${j.label}" (${Math.round((now3 - j.startedAt) / 1e3)}s, quiet ${Math.round((now3 - j.lastActivityAt) / 1e3)}s)`).join(", ");
5512
- log8.warn(`stall watchdog: no stream output for ${stallMs}ms \u2014 held by ${who}`);
5693
+ log9.warn(`stall watchdog: no stream output for ${stallMs}ms \u2014 held by ${who}`);
5513
5694
  this.options.host?.notify?.({ kind: "tool_hold", message: `waiting on ${who}` });
5514
5695
  return poke();
5515
5696
  }
5516
5697
  const inFlight = this.jobs.list().filter((j) => j.status === "running");
5517
- log8.warn(`stall watchdog: no stream output for ${stallMs}ms and no tool holding the stream \u2014 aborting the request` + (inFlight.length ? ` (${inFlight.length} detached job(s) running, not awaited by the stream: ${inFlight.map((j) => `${j.id}/${j.kind}`).join(", ")})` : ""));
5698
+ log9.warn(`stall watchdog: no stream output for ${stallMs}ms and no tool holding the stream \u2014 aborting the request` + (inFlight.length ? ` (${inFlight.length} detached job(s) running, not awaited by the stream: ${inFlight.map((j) => `${j.id}/${j.kind}`).join(", ")})` : ""));
5518
5699
  fired = true;
5519
5700
  ac.abort(stallError());
5520
5701
  }, stallMs);
@@ -5684,13 +5865,13 @@ var Agent = class _Agent {
5684
5865
  const decision = await this.park(Promise.resolve(hooks?.preToolUse?.(call, meta)));
5685
5866
  if (decision?.block) {
5686
5867
  const blocked = `Blocked by hook: ${decision.reason ?? "no reason given"}`;
5687
- log8.debug(`${tc.function.name} -> ${blocked}`);
5868
+ log9.debug(`${tc.function.name} -> ${blocked}`);
5688
5869
  await hooks?.postToolUse?.(call, blocked, meta);
5689
5870
  return blocked;
5690
5871
  }
5691
5872
  this.options.host?.notify?.({ kind: "tool_use", id: tc.id ?? "", name: tc.function.name, input: args });
5692
5873
  if (earlyError) {
5693
- log8.debug(`${tc.function.name} -> ${earlyError}`);
5874
+ log9.debug(`${tc.function.name} -> ${earlyError}`);
5694
5875
  await hooks?.postToolUse?.(call, earlyError, meta);
5695
5876
  this.options.host?.notify?.({ kind: "tool_result", id: tc.id ?? "", output: earlyError, isError: true });
5696
5877
  return earlyError;
@@ -5699,12 +5880,12 @@ var Agent = class _Agent {
5699
5880
  let images;
5700
5881
  let threw = false;
5701
5882
  try {
5702
- log8.debug(`${tc.function.name}(${tc.function.arguments})`);
5883
+ log9.debug(`${tc.function.name}(${tc.function.arguments})`);
5703
5884
  this.ctx.emit = hooks?.onToolOutput ? (chunk) => {
5704
5885
  try {
5705
5886
  hooks.onToolOutput(call, chunk, meta);
5706
5887
  } catch (e) {
5707
- log8.debug(`onToolOutput hook error: ${e}`);
5888
+ log9.debug(`onToolOutput hook error: ${e}`);
5708
5889
  }
5709
5890
  } : void 0;
5710
5891
  const raw = await tool.run(args, this.ctx);
@@ -5716,7 +5897,7 @@ var Agent = class _Agent {
5716
5897
  }
5717
5898
  } catch (e) {
5718
5899
  const msg = e instanceof Error ? e.message : String(e);
5719
- log8.debug(`${tc.function.name} -> error: ${msg}`);
5900
+ log9.debug(`${tc.function.name} -> error: ${msg}`);
5720
5901
  result = `Error: ${msg}`;
5721
5902
  threw = true;
5722
5903
  } finally {
@@ -5993,7 +6174,7 @@ function fitTokenBudget(messages, maxTokens, keepRecentImages = 1, vision = true
5993
6174
  const ids = callIdSet(messages.slice(from));
5994
6175
  while (from < messages.length && messages[from].role === "tool" && !ids.has(messages[from].tool_call_id ?? "")) total -= per[from++];
5995
6176
  if (total > maxTokens)
5996
- log8.warn(`context ~${total} tok still over maxContextTokens=${maxTokens} after trimming (system head can't be dropped)`);
6177
+ log9.warn(`context ~${total} tok still over maxContextTokens=${maxTokens} after trimming (system head can't be dropped)`);
5997
6178
  return [...head, ...messages.slice(from)];
5998
6179
  }
5999
6180
  function compact(m, max, focus) {
@@ -6678,7 +6859,7 @@ init_tools_web();
6678
6859
  init_tools();
6679
6860
  init_tools_structured();
6680
6861
  init_logging();
6681
- var log10 = forComponent("scratch");
6862
+ var log11 = forComponent("scratch");
6682
6863
  var SCRATCH_DIR = "/scratch";
6683
6864
  function shortArgs(args) {
6684
6865
  try {
@@ -6718,7 +6899,7 @@ var Scratch = class {
6718
6899
  await (this.dirReady ??= mkdirp(this.fs, dir));
6719
6900
  await this.fs.writeFile(path, header + raw);
6720
6901
  } catch (e) {
6721
- log10.debug("scratch write failed; returning raw", e);
6902
+ log11.debug("scratch write failed; returning raw", e);
6722
6903
  return raw;
6723
6904
  }
6724
6905
  const preview = raw.slice(0, previewChars).replace(/\s+/g, " ").trim();
@@ -6748,7 +6929,7 @@ To pull a specific detail, Grep/Read ${path}, or call Ask({ question: "\u2026",
6748
6929
  await (this.dirReady ??= mkdirp(this.fs, dir));
6749
6930
  await this.fs.writeFile(path, header + full);
6750
6931
  } catch (e) {
6751
- log10.debug("scratch spill failed; cropping lossy", e);
6932
+ log11.debug("scratch spill failed; cropping lossy", e);
6752
6933
  return full.slice(0, pageBytes) + `
6753
6934
 
6754
6935
  [output cropped to ${pageBytes} of ${full.length} bytes; full output unavailable (scratch write failed) \u2014 refine your query]`;
@@ -6794,7 +6975,7 @@ Question: ${q}`);
6794
6975
  const answer = (res.text ?? "").trim();
6795
6976
  return answer || "(no answer found in scratch)";
6796
6977
  } catch (e) {
6797
- log10.debug("Ask peek failed", e);
6978
+ log11.debug("Ask peek failed", e);
6798
6979
  return `Error querying scratch: ${e?.message ?? e}`;
6799
6980
  }
6800
6981
  }
@@ -6803,7 +6984,7 @@ Question: ${q}`);
6803
6984
 
6804
6985
  // src/lessons.ts
6805
6986
  init_logging();
6806
- var log11 = forComponent("Lessons");
6987
+ var log12 = forComponent("Lessons");
6807
6988
  var LessonOptionsDefaults = class {
6808
6989
  minRepeats = 2;
6809
6990
  };
@@ -6828,8 +7009,8 @@ function lessonCapture(options) {
6828
7009
  counts.set(lesson.slug, n);
6829
7010
  if (n < o.minRepeats) return;
6830
7011
  written.add(lesson.slug);
6831
- await writeFact(o.fs, o.dir, lesson.slug, lesson.body).catch((e) => log11.warn(`could not persist ${lesson.slug}: ${e?.message ?? e}`));
6832
- log11.debug(`captured lesson ${lesson.slug} (recurred ${n}\xD7)`);
7012
+ await writeFact(o.fs, o.dir, lesson.slug, lesson.body).catch((e) => log12.warn(`could not persist ${lesson.slug}: ${e?.message ?? e}`));
7013
+ log12.debug(`captured lesson ${lesson.slug} (recurred ${n}\xD7)`);
6833
7014
  }
6834
7015
  };
6835
7016
  }
@@ -6837,7 +7018,7 @@ function lessonCapture(options) {
6837
7018
  // src/reflect.ts
6838
7019
  init_llm();
6839
7020
  init_logging();
6840
- var log12 = forComponent("Reflect");
7021
+ var log13 = forComponent("Reflect");
6841
7022
  async function reflectOnRun(o) {
6842
7023
  const digest = digestRun(o.result.messages, o.maxDigestChars ?? 6e3);
6843
7024
  if (!digest.trim()) return null;
@@ -6853,7 +7034,7 @@ If the run was fine or the issue was purely task-specific (not generalizable), r
6853
7034
  const r = await o.ai.chat({ model: o.model, messages: [{ role: "user", content: prompt }], stream: false });
6854
7035
  text = r?.content ?? "";
6855
7036
  } catch (e) {
6856
- log12.warn(`reflection call failed: ${e?.message ?? e}`);
7037
+ log13.warn(`reflection call failed: ${e?.message ?? e}`);
6857
7038
  return null;
6858
7039
  }
6859
7040
  const m = text.match(/LESSON:\s*(.+)/i);
@@ -6863,10 +7044,10 @@ If the run was fine or the issue was purely task-specific (not generalizable), r
6863
7044
  try {
6864
7045
  await writeFact(o.fs, o.dir, slug2, lesson);
6865
7046
  } catch (e) {
6866
- log12.warn(`could not persist lesson: ${e?.message ?? e}`);
7047
+ log13.warn(`could not persist lesson: ${e?.message ?? e}`);
6867
7048
  return null;
6868
7049
  }
6869
- log12.debug(`reflection persisted ${slug2}`);
7050
+ log13.debug(`reflection persisted ${slug2}`);
6870
7051
  return slug2;
6871
7052
  }
6872
7053
  function digestRun(messages, maxChars) {
@@ -6916,7 +7097,7 @@ function forComponent2(name) {
6916
7097
  }
6917
7098
 
6918
7099
  // node_modules/@bod.ee/voice/src/core/emotion.ts
6919
- var log13 = forComponent2("Emotion");
7100
+ var log14 = forComponent2("Emotion");
6920
7101
  var EMOTIONS = [
6921
7102
  // primary (best results)
6922
7103
  "neutral",
@@ -7070,7 +7251,7 @@ var EmotionStream = class {
7070
7251
  if (tag) {
7071
7252
  this.pending = tag;
7072
7253
  if (this.show) display += m[0];
7073
- } else log13.debug(`dropping unknown emotion tag ${m[0]}`);
7254
+ } else log14.debug(`dropping unknown emotion tag ${m[0]}`);
7074
7255
  last = m.index + m[0].length;
7075
7256
  }
7076
7257
  this.emit(body.slice(last), (s, d, p) => {
@@ -7217,7 +7398,7 @@ function computeHoldMs(ctx) {
7217
7398
  }
7218
7399
 
7219
7400
  // node_modules/@bod.ee/voice/src/core/engine.ts
7220
- var log14 = forComponent2("VoiceEngine");
7401
+ var log15 = forComponent2("VoiceEngine");
7221
7402
  var realClock = {
7222
7403
  now: () => performance.now(),
7223
7404
  setTimeout: (fn, ms) => setTimeout(fn, ms),
@@ -7605,8 +7786,8 @@ var VoiceEngine = class _VoiceEngine {
7605
7786
  this.tts.warmup?.();
7606
7787
  this.setState("listening");
7607
7788
  if (this.options.bargeInMode === "aec" && !this.stt.usingAec)
7608
- log14.warn("bargeInMode=aec but mic does not report echoCancellation applied \u2014 STT may transcribe the assistant");
7609
- log14.debug(`voice I/O up (${this.usingAec ? "AEC" : "heuristic echo"} capture, mode=${this.options.bargeInMode}, mic reports aec=${this.stt.usingAec})`);
7789
+ log15.warn("bargeInMode=aec but mic does not report echoCancellation applied \u2014 STT may transcribe the assistant");
7790
+ log15.debug(`voice I/O up (${this.usingAec ? "AEC" : "heuristic echo"} capture, mode=${this.options.bargeInMode}, mic reports aec=${this.stt.usingAec})`);
7610
7791
  }
7611
7792
  /** The RESOLVED barge-in tier (the single seam every tier branch reads). `bargeInMode` overrides
7612
7793
  * the mic's self-report — see the option doc for why the report is not trustworthy on mobile. */
@@ -7631,7 +7812,7 @@ var VoiceEngine = class _VoiceEngine {
7631
7812
  this.options.onDiag({ t: this.clock.now(), kind, ...fields });
7632
7813
  } catch (e) {
7633
7814
  this.diagOn = false;
7634
- log14.debug(`onDiag threw \u2014 diagnostics disabled: ${e instanceof Error ? e.message : e}`);
7815
+ log15.debug(`onDiag threw \u2014 diagnostics disabled: ${e instanceof Error ? e.message : e}`);
7635
7816
  }
7636
7817
  }
7637
7818
  idleWaiters = [];
@@ -7770,7 +7951,7 @@ var VoiceEngine = class _VoiceEngine {
7770
7951
  }
7771
7952
  for (const w of this.words(this.reply)) this.echoWords.add(w);
7772
7953
  this.tts.speak(forSpeech(speech), true);
7773
- if (!this.spokeDeltas && this.turnStartAt) log14.debug(`ttft: ${Math.round(this.clock.now() - this.turnStartAt)}ms`);
7954
+ if (!this.spokeDeltas && this.turnStartAt) log15.debug(`ttft: ${Math.round(this.clock.now() - this.turnStartAt)}ms`);
7774
7955
  this.spokeDeltas = true;
7775
7956
  this.setState("speaking");
7776
7957
  if (this.options.revealMode !== "off" && display) {
@@ -7883,13 +8064,13 @@ var VoiceEngine = class _VoiceEngine {
7883
8064
  this.drainTimer = null;
7884
8065
  this.speaking = false;
7885
8066
  this.stopWordReveal(true);
7886
- if (this.turnStartAt) log14.debug(`turn: ${Math.round(this.clock.now() - this.turnStartAt)}ms (incl. playback)`);
8067
+ if (this.turnStartAt) log15.debug(`turn: ${Math.round(this.clock.now() - this.turnStartAt)}ms (incl. playback)`);
7887
8068
  this.echoUntil = this.clock.now() + 2500;
7888
8069
  if (!this.usingAec) this.stt.reset();
7889
8070
  try {
7890
8071
  this.options.onSpeechEnd();
7891
8072
  } catch (e) {
7892
- log14.warn(`onSpeechEnd threw: ${e instanceof Error ? e.message : e}`);
8073
+ log15.warn(`onSpeechEnd threw: ${e instanceof Error ? e.message : e}`);
7893
8074
  }
7894
8075
  this.setState("listening");
7895
8076
  if (this.uttQueue.length) this.pumpQueue();
@@ -7963,7 +8144,7 @@ var VoiceEngine = class _VoiceEngine {
7963
8144
  if (!host) return pick;
7964
8145
  const ctx = { text: this.lastDispatchText, question: this.lastDispatchWasQuestion, barged: this.bargedBeforeDispatch };
7965
8146
  const fallback = (e) => {
7966
- log14.warn(`pickAck failed \u2014 using the default pools: ${e instanceof Error ? e.message : e}`);
8147
+ log15.warn(`pickAck failed \u2014 using the default pools: ${e instanceof Error ? e.message : e}`);
7967
8148
  pick.picked = false;
7968
8149
  return void 0;
7969
8150
  };
@@ -8053,7 +8234,7 @@ var VoiceEngine = class _VoiceEngine {
8053
8234
  this.ackProbe = null;
8054
8235
  const queued = this.uttQueue.splice(0);
8055
8236
  const droppedQueued = queued.length;
8056
- if (droppedQueued) log14.info(`barge-in cut ${droppedQueued} queued worker utterance(s) \u2014 handed to the host`);
8237
+ if (droppedQueued) log15.info(`barge-in cut ${droppedQueued} queued worker utterance(s) \u2014 handed to the host`);
8057
8238
  this.overlapKeptUntil = 0;
8058
8239
  if (!this.speaking && !this.drainTimer) {
8059
8240
  if (droppedQueued) this.lastInterrupted = { full: "", heard: "", queued, queuedTurn: false };
@@ -8282,7 +8463,7 @@ var VoiceEngine = class _VoiceEngine {
8282
8463
  if (flat && this.lastDispatchFlat && this.clock.now() - this.lastDispatchAt < _VoiceEngine.DUP_FINAL_MS && (this.lastDispatchFlat.startsWith(flat) || flat.startsWith(this.lastDispatchFlat))) return;
8283
8464
  this.specSpent = true;
8284
8465
  this.specText = this.specPartial;
8285
- log14.debug(`speculate: "${this.specText.slice(0, 60)}"`);
8466
+ log15.debug(`speculate: "${this.specText.slice(0, 60)}"`);
8286
8467
  this.diag("speculate", { text: this.specText });
8287
8468
  this.options.onSpeculate(this.specText);
8288
8469
  }, this.options.speculativeMs);
@@ -8351,7 +8532,7 @@ var VoiceEngine = class _VoiceEngine {
8351
8532
  };
8352
8533
  this.tts.newContext();
8353
8534
  this.tts.speak(phrase, false);
8354
- log14.debug(`backchannel: "${phrase}"`);
8535
+ log15.debug(`backchannel: "${phrase}"`);
8355
8536
  this.diag("backchannel", { phrase });
8356
8537
  this.options.onBackchannel(phrase);
8357
8538
  }
@@ -8384,7 +8565,7 @@ var VoiceEngine = class _VoiceEngine {
8384
8565
  });
8385
8566
  if (idx < 0) return text;
8386
8567
  toks.splice(idx, 1);
8387
- log14.verbose(`stripped backchannel echo "${this.lastBcPhrase}" from dispatching final`);
8568
+ log15.verbose(`stripped backchannel echo "${this.lastBcPhrase}" from dispatching final`);
8388
8569
  return toks.join(" ");
8389
8570
  }
8390
8571
  /** Merge a resumed utterance into the pending one, deduping any word-overlap. Soniox re-finalizes
@@ -8570,7 +8751,7 @@ var VoiceEngine = class _VoiceEngine {
8570
8751
  }
8571
8752
  const bargeHold = !this.looksIncomplete(this.pendingUtt) && this.unfinishedBargeLine(this.pendingUtt);
8572
8753
  if (this.options.incompleteMergeMs && (bargeHold || this.looksIncomplete(this.pendingUtt))) {
8573
- log14.verbose(`hold: incomplete utterance "${this.pendingUtt.slice(-40)}"`);
8754
+ log15.verbose(`hold: incomplete utterance "${this.pendingUtt.slice(-40)}"`);
8574
8755
  this.diag("hold", { reason: bargeHold ? "barge_unfinished" : "incomplete", tail: this.pendingUtt.slice(-40) });
8575
8756
  this.options.onHold();
8576
8757
  if (this.options.holdFiller && !this.speaking) this.speakFiller(this.options.holdFiller);
@@ -8609,13 +8790,13 @@ var VoiceEngine = class _VoiceEngine {
8609
8790
  const answered = this.answeredSinceDispatch();
8610
8791
  const part = flat !== this.lastDispatchFlat && !answered && this.echoesDispatch(text);
8611
8792
  if (part || flat && flat === this.lastDispatchFlat && !answered && this.clock.now() - this.lastDispatchAt < _VoiceEngine.DUP_FINAL_MS) {
8612
- log14.verbose(`dropped duplicate final "${text.slice(0, 40)}" (identical to the just-dispatched utterance, no reply between)`);
8793
+ log15.verbose(`dropped duplicate final "${text.slice(0, 40)}" (identical to the just-dispatched utterance, no reply between)`);
8613
8794
  this.diag("utterance_drop", { reason: "dup_final", text: text.slice(0, 60) });
8614
8795
  return;
8615
8796
  }
8616
8797
  const refinal = this.refinalizes(text);
8617
8798
  if (refinal && this.repliedSinceDispatch) {
8618
- log14.verbose(`dropped re-finalization "${text.slice(0, 60)}" (superset of the just-dispatched utterance, reply already flowing)`);
8799
+ log15.verbose(`dropped re-finalization "${text.slice(0, 60)}" (superset of the just-dispatched utterance, reply already flowing)`);
8619
8800
  this.diag("utterance_drop", { reason: "refinalized", text: text.slice(0, 60) });
8620
8801
  this.lastDispatchFlat = flat;
8621
8802
  this.lastDispatchWords = text.trim().split(/\s+/);
@@ -8894,7 +9075,7 @@ var SpokenSplitter = class {
8894
9075
  };
8895
9076
 
8896
9077
  // node_modules/@bod.ee/voice/src/adapters/soniox.ts
8897
- var log15 = forComponent2("SonioxSTT");
9078
+ var log16 = forComponent2("SonioxSTT");
8898
9079
  var now = () => performance.now();
8899
9080
  var SonioxSTTOptions = class {
8900
9081
  auth = "";
@@ -8939,7 +9120,7 @@ var SonioxSTT = class {
8939
9120
  this.onDiag({ t: now(), kind, ...fields });
8940
9121
  } catch (e) {
8941
9122
  this.diagOn = false;
8942
- log15.debug(`onDiag threw \u2014 STT diagnostics disabled: ${e instanceof Error ? e.message : e}`);
9123
+ log16.debug(`onDiag threw \u2014 STT diagnostics disabled: ${e instanceof Error ? e.message : e}`);
8943
9124
  }
8944
9125
  }
8945
9126
  lastChunkAt = 0;
@@ -8981,11 +9162,11 @@ var SonioxSTT = class {
8981
9162
  this.ws.onmessage = (ev) => this.handle(JSON.parse(String(ev.data)));
8982
9163
  this.ws.onclose = (ev) => {
8983
9164
  if (this.stopped) return;
8984
- log15.warn(`soniox ws closed (${ev.code} ${ev.reason || ""}) \u2014 reconnecting`);
9165
+ log16.warn(`soniox ws closed (${ev.code} ${ev.reason || ""}) \u2014 reconnecting`);
8985
9166
  this.diag("stt_ws_closed", { code: ev.code, reason: String(ev.reason || ""), reconnecting: true });
8986
9167
  this.reset();
8987
9168
  this.connectWs().catch((e) => {
8988
- log15.error(`soniox reconnect failed: ${e.message}`);
9169
+ log16.error(`soniox reconnect failed: ${e.message}`);
8989
9170
  this.diag("stt_reconnect_failed", { message: e.message });
8990
9171
  });
8991
9172
  };
@@ -8997,7 +9178,7 @@ var SonioxSTT = class {
8997
9178
  this.endpointTimer = setInterval(() => {
8998
9179
  const combined = (this.finalText + this.partialText).trim();
8999
9180
  if (!combined || now() - this.lastChangeAt < this.options.silenceEndpointMs) return;
9000
- if (this.firstTokenAt) log15.debug(`stt: ${Math.round(now() - this.firstTokenAt)}ms first-token\u2192silence-endpoint, "${combined.slice(0, 60)}"`);
9181
+ if (this.firstTokenAt) log16.debug(`stt: ${Math.round(now() - this.firstTokenAt)}ms first-token\u2192silence-endpoint, "${combined.slice(0, 60)}"`);
9001
9182
  this.reset();
9002
9183
  this.onUtterance(combined, now());
9003
9184
  }, 120);
@@ -9009,7 +9190,7 @@ var SonioxSTT = class {
9009
9190
  if (this.stopped) return;
9010
9191
  const ref = this.lastChunkAt || this.startedChunksAt;
9011
9192
  if (now() - ref > noAudioMs) {
9012
- log15.error(`stt: no mic audio for >${Math.round(noAudioMs / 1e3)}s \u2014 capture device stopped delivering`);
9193
+ log16.error(`stt: no mic audio for >${Math.round(noAudioMs / 1e3)}s \u2014 capture device stopped delivering`);
9013
9194
  this.diag("stt_watchdog_fatal", { noAudioMs });
9014
9195
  this.onFatal("microphone stopped delivering audio (try a different input device, e.g. AirPods, or check System Settings \u2192 Sound \u2192 Input)");
9015
9196
  this.stop();
@@ -9032,7 +9213,7 @@ var SonioxSTT = class {
9032
9213
  handle(m) {
9033
9214
  if (m.error_message) {
9034
9215
  this.diag("stt_error", { message: String(m.error_message), code: m.error_code });
9035
- return log15.error(`soniox: ${m.error_message}`);
9216
+ return log16.error(`soniox: ${m.error_message}`);
9036
9217
  }
9037
9218
  let endpoint = false;
9038
9219
  for (const t of m.tokens ?? []) {
@@ -9049,7 +9230,7 @@ var SonioxSTT = class {
9049
9230
  this.onPartial(combined);
9050
9231
  if (endpoint && this.finalText.trim()) {
9051
9232
  const utterance = this.finalText.trim();
9052
- if (this.firstTokenAt) log15.debug(`stt: ${Math.round(now() - this.firstTokenAt)}ms first-token\u2192endpoint, "${utterance.slice(0, 60)}"`);
9233
+ if (this.firstTokenAt) log16.debug(`stt: ${Math.round(now() - this.firstTokenAt)}ms first-token\u2192endpoint, "${utterance.slice(0, 60)}"`);
9053
9234
  this.reset();
9054
9235
  this.onUtterance(utterance, now());
9055
9236
  }
@@ -9071,7 +9252,7 @@ var SonioxSTT = class {
9071
9252
  };
9072
9253
 
9073
9254
  // node_modules/@bod.ee/voice/src/adapters/cartesia.ts
9074
- var log16 = forComponent2("CartesiaTTS");
9255
+ var log17 = forComponent2("CartesiaTTS");
9075
9256
  var now2 = () => performance.now();
9076
9257
  var CartesiaTTSOptions = class {
9077
9258
  auth = "";
@@ -9107,7 +9288,7 @@ var CartesiaTTS = class _CartesiaTTS {
9107
9288
  this.onDiag({ t: now2(), kind, ...fields });
9108
9289
  } catch (e) {
9109
9290
  this.diagOn = false;
9110
- log16.debug(`onDiag threw \u2014 TTS diagnostics disabled: ${e instanceof Error ? e.message : e}`);
9291
+ log17.debug(`onDiag threw \u2014 TTS diagnostics disabled: ${e instanceof Error ? e.message : e}`);
9111
9292
  }
9112
9293
  }
9113
9294
  firstAudioAt = 0;
@@ -9142,11 +9323,11 @@ var CartesiaTTS = class _CartesiaTTS {
9142
9323
  this.ws.onerror = (e) => rej(new Error(`cartesia ws: ${e.message || "connect failed"}`));
9143
9324
  });
9144
9325
  this.ws.onclose = (ev) => {
9145
- log16.warn(`cartesia ws closed (${ev.code} ${ev.reason || ""})`);
9326
+ log17.warn(`cartesia ws closed (${ev.code} ${ev.reason || ""})`);
9146
9327
  this.diag("tts_ws_closed", { code: ev.code, reason: String(ev.reason || ""), reconnecting: !this.closed });
9147
9328
  if (!this.closed) {
9148
9329
  this.connecting = this.doConnect().catch((e) => {
9149
- log16.error(`cartesia reconnect failed: ${e.message}`);
9330
+ log17.error(`cartesia reconnect failed: ${e.message}`);
9150
9331
  this.diag("tts_reconnect_failed", { message: e.message });
9151
9332
  });
9152
9333
  }
@@ -9174,12 +9355,12 @@ var CartesiaTTS = class _CartesiaTTS {
9174
9355
  this.down = true;
9175
9356
  this.downAt = now2();
9176
9357
  this.consecutiveOk = 0;
9177
- log16.warn(`TTS circuit breaker open \u2014 ${this.consecutiveErrors} consecutive errors, switching to text-only`);
9358
+ log17.warn(`TTS circuit breaker open \u2014 ${this.consecutiveErrors} consecutive errors, switching to text-only`);
9178
9359
  this.diag("tts_breaker_open", { errors: this.consecutiveErrors });
9179
9360
  this.onDone();
9180
9361
  this.startProbe();
9181
9362
  } else if (!this.down) {
9182
- (/No valid transcripts/i.test(m.message || "") ? log16.debug : log16.warn)(`cartesia: ${JSON.stringify(m)}`);
9363
+ (/No valid transcripts/i.test(m.message || "") ? log17.debug : log17.warn)(`cartesia: ${JSON.stringify(m)}`);
9183
9364
  }
9184
9365
  }
9185
9366
  };
@@ -9194,7 +9375,7 @@ var CartesiaTTS = class _CartesiaTTS {
9194
9375
  this.stopProbe();
9195
9376
  const downMs = this.downAt ? now2() - this.downAt : 0;
9196
9377
  this.diag("tts_breaker_close", { downMs: Math.round(downMs) });
9197
- (downMs < 2e3 ? log16.debug : log16.info)(`TTS recovered${downMs ? ` (down ${downMs}ms)` : ""}`);
9378
+ (downMs < 2e3 ? log17.debug : log17.info)(`TTS recovered${downMs ? ` (down ${downMs}ms)` : ""}`);
9198
9379
  }
9199
9380
  /** Ensure the WS is open before sending — reconnects if idle-closed. */
9200
9381
  async ensureConnected() {
@@ -9277,13 +9458,13 @@ function base64ToBytes(b64) {
9277
9458
  }
9278
9459
 
9279
9460
  // node_modules/@bod.ee/voice/src/adapters/fish.ts
9280
- var log17 = forComponent2("FishTTS");
9461
+ var log18 = forComponent2("FishTTS");
9281
9462
 
9282
9463
  // node_modules/@bod.ee/voice/src/adapters/grok.ts
9283
- var log18 = forComponent2("GrokTTS");
9464
+ var log19 = forComponent2("GrokTTS");
9284
9465
 
9285
9466
  // node_modules/@bod.ee/voice/src/adapters/grok-stt.ts
9286
- var log19 = forComponent2("GrokSTT");
9467
+ var log20 = forComponent2("GrokSTT");
9287
9468
 
9288
9469
  // src/voice/engine.ts
9289
9470
  init_logging();
@@ -9299,7 +9480,7 @@ import { AIClient, DecideClient, JevAdapter, LlmDecideAdapter } from "ai.libx.js
9299
9480
 
9300
9481
  // src/voice/overlap.ts
9301
9482
  init_logging();
9302
- var log20 = forComponent("Overlap");
9483
+ var log21 = forComponent("Overlap");
9303
9484
  var norm2 = (s) => s.toLowerCase().replace(/[’']/g, "'").replace(/[^\p{L}\p{N}' -]+/gu, " ").replace(/\s+/g, " ").trim();
9304
9485
  var HARD = /\b(?:stop|wait|no|nope|nah|hold on|hang on|hold up|pause|cancel|quiet|enough|shut up|never ?mind|actually|but|sorry what|what|why|how|who|when|where|which|can you|could you|tell me|i mean|excuse me)\b/;
9305
9486
  var ACK = new Set("yeah yea yes yep yup ya ok okay k right sure true totally exactly definitely absolutely indeed mm mmm hmm mhm mm-hm uh-huh uh huh aha ah oh ooh wow nice cool great awesome perfect good fine interesting gotcha got it i see makes sense fair agreed correct of course for sure lol ha haha hahaha heh hehe yeah yeah alright all right sweet neat oh nice oh wow oh cool oh okay oh yeah oh right that's right that's true thats right thats true love it i know me too same".split(" "));
@@ -9379,8 +9560,8 @@ var OverlapJudge = class {
9379
9560
  const t0 = this.o.now();
9380
9561
  const done = (verdict, by) => {
9381
9562
  const d = { verdict, by, ms: Math.round(this.o.now() - t0) };
9382
- if (by === "late" || by === "error" || by === "none") log20.warn(`overlap "${heard.slice(0, 60)}": no verdict (${by}, ${d.ms}ms) \u2192 cut`);
9383
- else log20.debug(`overlap "${heard.slice(0, 60)}": ${verdict} by ${by} ${d.ms}ms`);
9563
+ if (by === "late" || by === "error" || by === "none") log21.warn(`overlap "${heard.slice(0, 60)}": no verdict (${by}, ${d.ms}ms) \u2192 cut`);
9564
+ else log21.debug(`overlap "${heard.slice(0, 60)}": ${verdict} by ${by} ${d.ms}ms`);
9384
9565
  this.o.onDecision(heard, d);
9385
9566
  return d;
9386
9567
  };
@@ -9397,7 +9578,7 @@ var OverlapJudge = class {
9397
9578
  });
9398
9579
  try {
9399
9580
  const v = await Promise.race([this.o.model(heard, ctl.signal).catch((e) => {
9400
- if (!ctl.signal.aborted) log20.warn(`overlap model failed: ${e.message}`);
9581
+ if (!ctl.signal.aborted) log21.warn(`overlap model failed: ${e.message}`);
9401
9582
  return "error";
9402
9583
  }), late]);
9403
9584
  if (v === "late" || v === "error") return done("cut", v);
@@ -9413,14 +9594,14 @@ function overlapModelFor(ai, env = process.env) {
9413
9594
  const m = fastOverlapModel(ai, model);
9414
9595
  const t0 = performance.now();
9415
9596
  void m("yeah", AbortSignal.timeout(1e4)).then(
9416
- () => log20.debug(`fast model warm in ${Math.round(performance.now() - t0)}ms`),
9417
- (e) => log20.warn(`fast model warm-up failed (the first overlap verdict may be late): ${e?.message ?? e}`)
9597
+ () => log21.debug(`fast model warm in ${Math.round(performance.now() - t0)}ms`),
9598
+ (e) => log21.warn(`fast model warm-up failed (the first overlap verdict may be late): ${e?.message ?? e}`)
9418
9599
  );
9419
9600
  return m;
9420
9601
  }
9421
9602
 
9422
9603
  // src/voice/instinct.ts
9423
- var log21 = forComponent("Instinct");
9604
+ var log22 = forComponent("Instinct");
9424
9605
  var ACK_SITUATIONS = {
9425
9606
  hard_question: `a question that takes real thought, judgment or looking something up ("what's the biggest takeaway?", "how should I structure this?")`,
9426
9607
  simple_question: 'a quick question with an instant, obvious answer ("how are you?", "what time is it?", "can you hear me?")',
@@ -9470,7 +9651,7 @@ function interruptionNote(cut, intent) {
9470
9651
 
9471
9652
  // src/voice/unsaid.ts
9472
9653
  init_logging();
9473
- var log22 = forComponent("Unsaid");
9654
+ var log23 = forComponent("Unsaid");
9474
9655
  var STOP2 = new Set("that this with have from they them their there then than what when were will would your about into just also been some only over more most very which while after before because these those does here".split(" "));
9475
9656
  var contentStems = (s) => (s.toLowerCase().match(/[a-z0-9']+/g) ?? []).filter((w) => w.length >= 4 && !STOP2.has(w)).map((w) => w.slice(0, 4));
9476
9657
  var SAID_OVERLAP = 0.6;
@@ -9510,7 +9691,7 @@ async function unsaidClauses(pieces, spoken, decider, timeoutMs = 5e3) {
9510
9691
  return p < CONVEYED_MIN;
9511
9692
  }).map((x) => x.c);
9512
9693
  } catch (e) {
9513
- log22.warn(`unsaid judge failed \u2014 only near-zero-overlap clauses count as unsaid: ${e.message}`);
9694
+ log23.warn(`unsaid judge failed \u2014 only near-zero-overlap clauses count as unsaid: ${e.message}`);
9514
9695
  return fallback();
9515
9696
  } finally {
9516
9697
  clearTimeout(timer);
@@ -9631,7 +9812,7 @@ var RESULT_CLAIM_RE = new RegExp(LEAD_TAGS + String.raw`(?:(?:all\s+)?done|finis
9631
9812
  var DONE_CLAIM_RE = new RegExp(LEAD_TAGS + String.raw`(?:(?:all\s+)?done|finished|it['’]?s\s+(?:done|finished)|completed?)\b`, "i");
9632
9813
 
9633
9814
  // src/voice/channelLang.ts
9634
- var log23 = forComponent("ChannelBanks");
9815
+ var log24 = forComponent("ChannelBanks");
9635
9816
  var LINE_KINDS = ["backchannel", "filler", "bridge"];
9636
9817
  var BANK_KEYS = [...LINE_KINDS, "phatic"];
9637
9818
  var EN_PHATIC = [
@@ -9785,11 +9966,11 @@ function cleanLine(s, lang, maxWords = 6) {
9785
9966
  const raw = s.trim().split("\n")[0];
9786
9967
  const l = raw.replace(/^[-*•\d.)\s]+/, "").replace(/^["'“”«»]+|["'“”«»]+$/g, "").replace(new RegExp("^\\p{L}+:\\s*", "u"), "").trim();
9787
9968
  if (!l || isLeakSuspect(raw) || isLeakSuspect(l)) {
9788
- if (l) log23.debug(`line "${raw}" reads as meta \u2014 rejected`);
9969
+ if (l) log24.debug(`line "${raw}" reads as meta \u2014 rejected`);
9789
9970
  return "";
9790
9971
  }
9791
9972
  if (lang && !fitsLanguage(l, lang)) {
9792
- log23.debug(`line "${l}" is not ${lang} \u2014 rejected`);
9973
+ log24.debug(`line "${l}" is not ${lang} \u2014 rejected`);
9793
9974
  return "";
9794
9975
  }
9795
9976
  return l.split(/\s+/).length > maxWords || l.length > 60 ? "" : l;
@@ -9803,7 +9984,7 @@ function validateBank(raw, lang, minPerKind = 3) {
9803
9984
  const seen = /* @__PURE__ */ new Set();
9804
9985
  out[k] = (Array.isArray(v) ? v : []).filter((x) => typeof x === "string").map((x) => cleanLine(x, lang)).filter((x) => x && !(k === "filler" && SPEAKER_FILLER.test(norm3(x))) && !seen.has(norm3(x)) && seen.add(norm3(x)));
9805
9986
  if (out[k].length < minPerKind) {
9806
- log23.warn(`bank ${lang}: only ${out[k].length} valid ${k} lines \u2014 rejected`);
9987
+ log24.warn(`bank ${lang}: only ${out[k].length} valid ${k} lines \u2014 rejected`);
9807
9988
  return null;
9808
9989
  }
9809
9990
  }
@@ -9879,11 +10060,11 @@ var ChannelBanks = class {
9879
10060
  const f = this.file(lang);
9880
10061
  let cached = null;
9881
10062
  if (f) cached = await readFile(f, "utf8").then((s) => JSON.parse(s)).catch((e) => {
9882
- if (e.code !== "ENOENT") log23.warn(`bank cache ${f} unreadable \u2014 regenerating: ${e.message}`);
10063
+ if (e.code !== "ENOENT") log24.warn(`bank cache ${f} unreadable \u2014 regenerating: ${e.message}`);
9883
10064
  return null;
9884
10065
  });
9885
10066
  if (cached && cached.v !== BANK_VERSION) {
9886
- log23.debug(`bank ${lang}: cache v${cached.v ?? 1} predates v${BANK_VERSION} \u2014 regenerating`);
10067
+ log24.debug(`bank ${lang}: cache v${cached.v ?? 1} predates v${BANK_VERSION} \u2014 regenerating`);
9887
10068
  cached = null;
9888
10069
  }
9889
10070
  const valid = cached && validateBank(cached.bank, lang, 0);
@@ -9891,7 +10072,7 @@ var ChannelBanks = class {
9891
10072
  if (valid) {
9892
10073
  for (const k of BANK_KEYS) if (!valid[k].length) valid[k] = [...this.o.fallback[lang]?.[k] ?? []];
9893
10074
  this.banks.set(lang, valid);
9894
- log23.debug(`bank ${lang}: cache hit (${BANK_KEYS.map((k) => valid[k].length).join("/")})${complete ? "" : " \u2014 missing kinds, refreshing"}`);
10075
+ log24.debug(`bank ${lang}: cache hit (${BANK_KEYS.map((k) => valid[k].length).join("/")})${complete ? "" : " \u2014 missing kinds, refreshing"}`);
9895
10076
  }
9896
10077
  if (complete && this.o.now() - cached.at < this.o.refreshMs) return;
9897
10078
  await this.generate(lang, valid);
@@ -9910,11 +10091,11 @@ var ChannelBanks = class {
9910
10091
  bank[k] = [...fresh[k], ...have?.[k] ?? []].filter((x) => !seen.has(norm3(x)) && seen.add(norm3(x))).slice(0, this.o.maxPerKind);
9911
10092
  }
9912
10093
  this.banks.set(lang, bank);
9913
- log23.debug(`bank ${lang}: ${have ? "refreshed" : "generated"} (${BANK_KEYS.map((k) => bank[k].length).join("/")}) in ${Math.round(this.o.now() - t0)}ms`);
10094
+ log24.debug(`bank ${lang}: ${have ? "refreshed" : "generated"} (${BANK_KEYS.map((k) => bank[k].length).join("/")}) in ${Math.round(this.o.now() - t0)}ms`);
9914
10095
  const f = this.file(lang);
9915
- if (f) await mkdir(this.o.cacheDir, { recursive: true }).then(() => writeFile(f, JSON.stringify({ at: this.o.now(), v: BANK_VERSION, bank }))).catch((e) => log23.warn(`bank cache write ${f} failed: ${e.message}`));
10096
+ if (f) await mkdir(this.o.cacheDir, { recursive: true }).then(() => writeFile(f, JSON.stringify({ at: this.o.now(), v: BANK_VERSION, bank }))).catch((e) => log24.warn(`bank cache write ${f} failed: ${e.message}`));
9916
10097
  } catch (e) {
9917
- log23.warn(`bank ${lang}: generation failed \u2014 ${have ? "keeping the cached bank" : lang in this.o.fallback ? "fallback bank" : "silent"}: ${e.message}`);
10098
+ log24.warn(`bank ${lang}: generation failed \u2014 ${have ? "keeping the cached bank" : lang in this.o.fallback ? "fallback bank" : "silent"}: ${e.message}`);
9918
10099
  } finally {
9919
10100
  clearTimeout(t);
9920
10101
  if (!this.banks.has(lang)) setTimeout(() => this.pending.delete(lang), this.o.retryMs).unref?.();
@@ -10107,7 +10288,7 @@ var TurnGate = class {
10107
10288
  };
10108
10289
 
10109
10290
  // src/duplex.ts
10110
- var log24 = forComponent("DuplexAgent");
10291
+ var log25 = forComponent("DuplexAgent");
10111
10292
  function describeCall(call) {
10112
10293
  const v = call.args && Object.values(call.args).find((x) => typeof x === "string" && x.trim());
10113
10294
  const hint = v ? ` (${String(v).replace(/\s+/g, " ").trim().slice(0, 48)})` : "";
@@ -10442,7 +10623,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
10442
10623
  const m = this.reflexBuf.match(RESERVED_EVENT_MARKER) ?? this.reflexBuf.match(RESERVED_EVENT_OPENER);
10443
10624
  if (m) {
10444
10625
  this.fabricationCut = true;
10445
- log24.warn(`reflex fabricated a [task \u2026] event in its spoken stream \u2014 cutting it (kept ${m.index} chars)`);
10626
+ log25.warn(`reflex fabricated a [task \u2026] event in its spoken stream \u2014 cutting it (kept ${m.index} chars)`);
10446
10627
  this.reflexBuf = this.reflexBuf.slice(0, Math.max(m.index, this.reflexForwarded));
10447
10628
  this.pumpReflex(true);
10448
10629
  return;
@@ -10584,7 +10765,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
10584
10765
  this.leakHit = true;
10585
10766
  this.leakCuts.push(leak.trim().slice(0, 40));
10586
10767
  const why = this.cutKind === "claim" ? "stated a result no task has given yet" : this.cutKind === "restart" ? "started a second, restated reply" : "spoke its own analysis";
10587
- log24.warn(`reflex ${why} ("${leak.trim().slice(0, 40)}\u2026") \u2014 cut before TTS`);
10768
+ log25.warn(`reflex ${why} ("${leak.trim().slice(0, 40)}\u2026") \u2014 cut before TTS`);
10588
10769
  this.notify("diag", this.cutKind === "claim" ? "result_claim" : this.cutKind === "restart" ? "reply_restart" : "analysis_leak", { text: leak.trim().slice(0, 80), at: this.turnSpoken.trim() ? "mid_reply" : "before_speech" });
10589
10770
  const next = this.stepMarks.find((k) => k > at);
10590
10771
  if (next === void 0) {
@@ -10625,7 +10806,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
10625
10806
  this.notify("diag", "leak_judge", { text: s.slice(0, 80), p: Math.round(p * 100) / 100, ms: Math.round(performance.now() - t0) });
10626
10807
  return p >= LEAK_JUDGE_THRESHOLD;
10627
10808
  }, (e) => {
10628
- log24.warn(`leak judge failed \u2014 speaking the sentence: ${e.message}`);
10809
+ log25.warn(`leak judge failed \u2014 speaking the sentence: ${e.message}`);
10629
10810
  return false;
10630
10811
  }).then((leak) => {
10631
10812
  clearTimeout(timer);
@@ -10697,7 +10878,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
10697
10878
  let i = t.length - 1;
10698
10879
  while (i >= this.turnBase && !(t[i].role === "assistant" && typeof t[i].content === "string" && t[i].content.includes(key))) i--;
10699
10880
  if (i < this.turnBase) {
10700
- log24.warn("analysis leak cut, but its text was not found in the transcript to scrub");
10881
+ log25.warn("analysis leak cut, but its text was not found in the transcript to scrub");
10701
10882
  continue;
10702
10883
  }
10703
10884
  const m = t[i];
@@ -10754,7 +10935,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
10754
10935
  const res = await this.sendWithContext(content, context, note);
10755
10936
  const fb = this.options.reflexFallbackModel, model = this.voice.options.model;
10756
10937
  if (res.finishReason !== "error" || !rateLimited(res.error) || !fb || fb === model || fb.includes("/") && model.includes("/") && fb.split("/")[0] !== model.split("/")[0] || this.turnDispatched || this.turnSpoken.trim()) return res;
10757
- log24.warn(`reflex ${model} rate-limited \u2014 re-running the turn on ${fb}`);
10938
+ log25.warn(`reflex ${model} rate-limited \u2014 re-running the turn on ${fb}`);
10758
10939
  this.notify("diag", "reflex_fallback", { from: model, to: fb });
10759
10940
  this.voice.transcript.length = base;
10760
10941
  this.voice.options.model = fb;
@@ -10803,7 +10984,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
10803
10984
  try {
10804
10985
  await this.sendWithContext(reminder, this.lastContext);
10805
10986
  } catch (e) {
10806
- log24.warn(`follow-through nudge failed: ${e instanceof Error ? e.message : e}`);
10987
+ log25.warn(`follow-through nudge failed: ${e instanceof Error ? e.message : e}`);
10807
10988
  } finally {
10808
10989
  const i = this.voice.transcript.findIndex((m, k) => k >= base && m.role === "user" && m.content === reminder);
10809
10990
  if (i >= 0) this.voice.transcript.splice(i, 1);
@@ -10864,7 +11045,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
10864
11045
  return {
10865
11046
  preToolUse: (call) => {
10866
11047
  if (this.spec?.state === "pending" && !_DuplexAgent.SPEC_SAFE_TOOLS.has(call.name)) {
10867
- log24.verbose(`speculation aborted: reflex called ${call.name} on unconfirmed input`);
11048
+ log25.verbose(`speculation aborted: reflex called ${call.name} on unconfirmed input`);
10868
11049
  this.abortSpeculation();
10869
11050
  return { block: true, reason: "Speculative turn aborted." };
10870
11051
  }
@@ -10946,7 +11127,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
10946
11127
  try {
10947
11128
  await this.sendWithContext(reminder, this.lastContext);
10948
11129
  } catch (e) {
10949
- log24.warn(`ack nudge failed: ${e instanceof Error ? e.message : e}`);
11130
+ log25.warn(`ack nudge failed: ${e instanceof Error ? e.message : e}`);
10950
11131
  } finally {
10951
11132
  this.nudging = false;
10952
11133
  const i = this.voice.transcript.findIndex((m, k) => k >= base && m.role === "user" && m.content === reminder);
@@ -11002,7 +11183,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
11002
11183
  const accepted = this.offerOpen && this.parkedRedeliver.length > 0 && this.parkedRedeliver.every((p) => p.task === this.offer.task) && (isOfferAcceptance(content) || isTrivialBarge(content));
11003
11184
  const judge = accepted ? void 0 : this.options.bargeIntent;
11004
11185
  return (accepted ? Promise.resolve("resume") : judge ? judge(content).catch((e) => {
11005
- log24.warn(`bargeIntent judge failed \u2014 using the allowlist: ${e.message}`);
11186
+ log25.warn(`bargeIntent judge failed \u2014 using the allowlist: ${e.message}`);
11006
11187
  return fallback();
11007
11188
  }) : Promise.resolve(fallback())).then((intent) => {
11008
11189
  this.notify("diag", "barge_intent", { intent, text: content.slice(0, 80) });
@@ -11205,7 +11386,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
11205
11386
  try {
11206
11387
  res = await this.sendWithContext(spec.text, this.lastContext);
11207
11388
  } catch (e) {
11208
- log24.warn(`speculative turn failed: ${e instanceof Error ? e.message : e}`);
11389
+ log25.warn(`speculative turn failed: ${e instanceof Error ? e.message : e}`);
11209
11390
  } finally {
11210
11391
  this.voice.options.signal = prevSignal;
11211
11392
  }
@@ -11219,7 +11400,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
11219
11400
  if (d === "abort") {
11220
11401
  if (this.voice.transcript.length > base) this.voice.transcript.length = base;
11221
11402
  this.speculativeAbortedCalls++;
11222
- log24.verbose(`speculation aborted (${this.speculativeAbortedCalls} total): "${spec.text.slice(0, 50)}"`);
11403
+ log25.verbose(`speculation aborted (${this.speculativeAbortedCalls} total): "${spec.text.slice(0, 50)}"`);
11223
11404
  if (this.spec === spec) this.spec = void 0;
11224
11405
  return res ?? empty;
11225
11406
  }
@@ -11605,7 +11786,7 @@ Another agent just implemented the above. Independently check the CURRENT state
11605
11786
  this.notify("task_verify", `task ${id}: verifying`, { id });
11606
11787
  const cres = await new Agent(checkerOpts).run(checkBrief);
11607
11788
  if (cres.finishReason !== "stop") {
11608
- log24.warn(`task ${id}: verify inconclusive (${cres.finishReason})`);
11789
+ log25.warn(`task ${id}: verify inconclusive (${cres.finishReason})`);
11609
11790
  this.notify("task_verify", `task ${id}: verify inconclusive (${cres.finishReason})`, { id, finishReason: cres.finishReason });
11610
11791
  }
11611
11792
  const sum = (a = 0, b = 0) => a + b;
@@ -11741,7 +11922,7 @@ Another agent just implemented the above. Independently check the CURRENT state
11741
11922
  rec.status = "done";
11742
11923
  rec.result = res.text;
11743
11924
  const incomplete = res.finishReason !== "stop" && res.finishReason !== "empty";
11744
- log24.verbose(`task ${id} done (${res.steps} steps${incomplete ? `, INCOMPLETE: ${res.finishReason}` : ""})`);
11925
+ log25.verbose(`task ${id} done (${res.steps} steps${incomplete ? `, INCOMPLETE: ${res.finishReason}` : ""})`);
11745
11926
  this.notify("task_done", `task ${id} (${rec.label}) completed`, {
11746
11927
  id,
11747
11928
  text: res.text,
@@ -11775,14 +11956,14 @@ Another agent just implemented the above. Independently check the CURRENT state
11775
11956
  foldIfSuperseded(rec, note) {
11776
11957
  if (rec.deliveryParked !== "moved_on") return false;
11777
11958
  if (note.trim()) this.voice.transcript.push({ role: "assistant", content: note });
11778
- log24.verbose(`task ${rec.id} settled but superseded (${rec.status}) \u2014 folded into transcript, not voiced`);
11959
+ log25.verbose(`task ${rec.id} settled but superseded (${rec.status}) \u2014 folded into transcript, not voiced`);
11779
11960
  return true;
11780
11961
  }
11781
11962
  failTask(rec, msg) {
11782
11963
  this.dropAsk(rec.id);
11783
11964
  rec.status = "error";
11784
11965
  rec.result = msg;
11785
- log24.warn(`task ${rec.id} failed: ${msg}`);
11966
+ log25.warn(`task ${rec.id} failed: ${msg}`);
11786
11967
  this.notify("task_error", `task ${rec.id} (${rec.label}) failed: ${msg}`, { id: rec.id });
11787
11968
  if (this.foldIfSuperseded(rec, `The "${rec.label}" task failed: ${msg}`)) return;
11788
11969
  this.dropParked(rec);
@@ -12086,7 +12267,7 @@ Another agent just implemented the above. Independently check the CURRENT state
12086
12267
  init_llm();
12087
12268
  init_logging();
12088
12269
  init_models();
12089
- var log25 = forComponent("ModelRouter");
12270
+ var log26 = forComponent("ModelRouter");
12090
12271
  var ModelRouterOptions = class {
12091
12272
  classifier = jevClassifier();
12092
12273
  /** Capability tiers → model id (any provider). basic/standard run the main session; premium is only ever an offload. */
@@ -12170,10 +12351,10 @@ var ModelRouter = class {
12170
12351
  if (tier === "premium") d.offload = this.state.offload && (c.sameThread ?? 0) >= o.sameThreadAt ? "continue" : "fresh";
12171
12352
  } catch (e) {
12172
12353
  const msg = e instanceof Error ? e.message : String(e);
12173
- log25.warn(`classifier failed, falling back to standard: ${msg}`);
12354
+ log26.warn(`classifier failed, falling back to standard: ${msg}`);
12174
12355
  d = { tier: "standard", model: o.models.standard, probs: {}, fallback: msg, classifyMs: Math.round(performance.now() - t0) };
12175
12356
  }
12176
- log25.info(`route ${d.tier}${d.offload ? `(${d.offload})` : ""} \xB7 argmax=${d.choice ?? "-"} \xB7 p=${JSON.stringify(d.probs)}${d.sameThread != null ? ` \xB7 same=${d.sameThread.toFixed(2)}` : ""} \xB7 ${d.classifyMs}ms`);
12357
+ log26.info(`route ${d.tier}${d.offload ? `(${d.offload})` : ""} \xB7 argmax=${d.choice ?? "-"} \xB7 p=${JSON.stringify(d.probs)}${d.sameThread != null ? ` \xB7 same=${d.sameThread.toFixed(2)}` : ""} \xB7 ${d.classifyMs}ms`);
12177
12358
  return d;
12178
12359
  }
12179
12360
  /** Run one user turn on `agent` with routing. Returns a RunResult carrying `costUsd` + `routing`. */
@@ -12225,7 +12406,7 @@ ${text}`;
12225
12406
  const answer = res.text || `(premium sub-agent finished with no reply; finishReason=${res.finishReason})`;
12226
12407
  agent.transcript.push({ role: "user", content }, { role: "assistant", content: answer });
12227
12408
  this.state.offload = { messages: child.transcript, mainLen: agent.transcript.length, question: text };
12228
- log25.info(`offload ${cont ? "continue" : "fresh"} done \xB7 ${res.finishReason} \xB7 ${res.steps} steps`);
12409
+ log26.info(`offload ${cont ? "continue" : "fresh"} done \xB7 ${res.finishReason} \xB7 ${res.steps} steps`);
12229
12410
  const costUsd = o.costOf(main, briefUsage) + o.costOf(o.models.premium, res.usage);
12230
12411
  return { ...res, text: answer, messages: agent.transcript, usage: addUsage(briefUsage, res.usage), costUsd, routing: d };
12231
12412
  }
@@ -12241,7 +12422,7 @@ The request:
12241
12422
  ${text}`);
12242
12423
  return { text: r.text.trim() || text, usage: r.usage };
12243
12424
  } catch (e) {
12244
- log25.warn(`brief failed, sending the raw request: ${e instanceof Error ? e.message : String(e)}`);
12425
+ log26.warn(`brief failed, sending the raw request: ${e instanceof Error ? e.message : String(e)}`);
12245
12426
  return { text };
12246
12427
  } finally {
12247
12428
  agent.transcript = snap;
@@ -12286,7 +12467,7 @@ async function jevAvailable(opts = {}) {
12286
12467
  const { env } = await loadJev(opts);
12287
12468
  return JEV_KEYS.some((k) => env[k]?.trim());
12288
12469
  } catch (e) {
12289
- log25.debug(`jev unavailable: ${e instanceof Error ? e.message : String(e)}`);
12470
+ log26.debug(`jev unavailable: ${e instanceof Error ? e.message : String(e)}`);
12290
12471
  return false;
12291
12472
  }
12292
12473
  }
@@ -12571,7 +12752,7 @@ init_models();
12571
12752
 
12572
12753
  // src/voice/progress.ts
12573
12754
  init_logging();
12574
- var log26 = forComponent("Progress");
12755
+ var log27 = forComponent("Progress");
12575
12756
  var HE_RESULT_CLAIM = /(?:^|\s)(?:מצאתי|סיימתי|בוצע|הנה|אין)(?=\s|$|[.,!…])/u;
12576
12757
  var TOPICS = [
12577
12758
  [/toolsearch|^mcp__.*__(list_commands|call_info)$/, null],
@@ -12716,7 +12897,7 @@ var Progress = class {
12716
12897
  }, this.o.generateBudgetMs);
12717
12898
  });
12718
12899
  const raw = await Promise.race([this.o.generate(tool, argsHint(args), lang, ac.signal).catch((e) => {
12719
- log26.warn(`progress line failed: ${e?.message ?? e}`);
12900
+ log27.warn(`progress line failed: ${e?.message ?? e}`);
12720
12901
  return null;
12721
12902
  }), late]).finally(() => clearTimeout(t));
12722
12903
  const l = cleanLine(raw ?? "", lang, 5);
@@ -12738,7 +12919,7 @@ init_logging();
12738
12919
 
12739
12920
  // src/voice/backchannelPlan.ts
12740
12921
  init_logging();
12741
- var log27 = forComponent("BackchannelPlan");
12922
+ var log28 = forComponent("BackchannelPlan");
12742
12923
  var silentTurn = (p) => p.density === "none" || p.turn === "short_command" || p.turn === "question";
12743
12924
  var cueWords = (s) => s.toLowerCase().replace(/[^\p{L}\p{N}'\s]+/gu, " ").split(/\s+/).filter(Boolean);
12744
12925
  function matchCue(plan, heard, from = 0) {
@@ -12756,7 +12937,7 @@ function matchCue(plan, heard, from = 0) {
12756
12937
  }
12757
12938
 
12758
12939
  // src/voice/secondChannel.ts
12759
- var log28 = forComponent("SecondChannel");
12940
+ var log29 = forComponent("SecondChannel");
12760
12941
  var CHANNEL_MODES = {
12761
12942
  turn: { kinds: ["bridge"], bridgeDelayMs: 1500 },
12762
12943
  normal: {},
@@ -12873,15 +13054,15 @@ function withBudget(p, ms, what, ac) {
12873
13054
  let t;
12874
13055
  const late = new Promise((r) => {
12875
13056
  t = setTimeout(() => {
12876
- log28.debug(`${what}: over the ${ms}ms budget \u2014 dropped`);
13057
+ log29.debug(`${what}: over the ${ms}ms budget \u2014 dropped`);
12877
13058
  ac?.abort();
12878
13059
  r(null);
12879
13060
  }, ms);
12880
13061
  });
12881
13062
  return Promise.race([p.catch((e) => {
12882
13063
  const msg = String(e?.message ?? e);
12883
- if (e?.name === "AbortError" || /aborted/i.test(msg)) log28.debug(`${what}: aborted after its budget`);
12884
- else log28.warn(`${what} failed \u2014 falling back: ${msg}`);
13064
+ if (e?.name === "AbortError" || /aborted/i.test(msg)) log29.debug(`${what}: aborted after its budget`);
13065
+ else log29.warn(`${what} failed \u2014 falling back: ${msg}`);
12885
13066
  return null;
12886
13067
  }), late]).finally(() => clearTimeout(t));
12887
13068
  }
@@ -12960,7 +13141,7 @@ var SecondChannel = class {
12960
13141
  if (host) this.pinned = true;
12961
13142
  const lang = primaryLang(tag);
12962
13143
  if (!lang || lang === this.lang) return;
12963
- log28.debug(`language ${this.lang} \u2192 ${lang}`);
13144
+ log29.debug(`language ${this.lang} \u2192 ${lang}`);
12964
13145
  this.lang = lang;
12965
13146
  this.recent = [];
12966
13147
  this.picks.clear();
@@ -13105,7 +13286,7 @@ var SecondChannel = class {
13105
13286
  try {
13106
13287
  this.o.cancel();
13107
13288
  } catch (e) {
13108
- log28.warn(`cancel threw: ${e.message}`);
13289
+ log29.warn(`cancel threw: ${e.message}`);
13109
13290
  }
13110
13291
  }
13111
13292
  }
@@ -13217,7 +13398,7 @@ var SecondChannel = class {
13217
13398
  const opts = this.options;
13218
13399
  if (opts.mode === "chatty" && !opts.aec && !this.degradedLogged) {
13219
13400
  this.degradedLogged = true;
13220
- log28.info("chatty mode without echo cancellation \u2014 normal timing (our lines would re-trigger the mic)");
13401
+ log29.info("chatty mode without echo cancellation \u2014 normal timing (our lines would re-trigger the mic)");
13221
13402
  }
13222
13403
  const rule = this.rule(trigger, pauseMs, breath, cue);
13223
13404
  if (rule === "silence") {
@@ -13236,7 +13417,7 @@ var SecondChannel = class {
13236
13417
  const d = await withBudget(this.o.decide(this.tail(), { ...this.state(pauseMs), rule }, ac.signal), this.o.decisionBudgetMs, "channel decision", ac);
13237
13418
  choice = d?.choice;
13238
13419
  if (choice && choice in CHANNEL_ACTIONS && (choice === "silence" || this.allowed(choice))) kind = choice;
13239
- else if (choice && choice in CHANNEL_ACTIONS) log28.debug(`decider chose ${choice} for a ${rule} moment \u2014 the rule stands`);
13420
+ else if (choice && choice in CHANNEL_ACTIONS) log29.debug(`decider chose ${choice} for a ${rule} moment \u2014 the rule stands`);
13240
13421
  }
13241
13422
  if (kind === "silence") return ev({ choice, acted: "silence" });
13242
13423
  if (stale()) return ev({ choice, kind, acted: "stale" });
@@ -13274,7 +13455,7 @@ var SecondChannel = class {
13274
13455
  void withBudget(this.o.generate(ctx, ac.signal), Math.max(this.o.generateBudgetMs, this.o.bridgeDelayMs), "bridge generate", ac).then((raw) => {
13275
13456
  const text = cleanLine(raw ?? "", ctx.lang);
13276
13457
  if (text && gen === this.gen) this.bridgeDraft = { gen, text };
13277
- }).catch((e) => log28.debug(`bridge prefetch failed: ${e?.message ?? e}`));
13458
+ }).catch((e) => log29.debug(`bridge prefetch failed: ${e?.message ?? e}`));
13278
13459
  }
13279
13460
  async line(kind, react = true) {
13280
13461
  if (kind === "bridge" && this.o.instantBridge) {
@@ -13290,7 +13471,7 @@ var SecondChannel = class {
13290
13471
  if (raw === null) ac.abort();
13291
13472
  const text = cleanLine(raw ?? "", this.lang);
13292
13473
  if (text && !this.recent.some((r) => norm3(r) === norm3(text))) return text;
13293
- if (text) log28.debug(`generated "${text}" repeats a recent line \u2014 canned`);
13474
+ if (text) log29.debug(`generated "${text}" repeats a recent line \u2014 canned`);
13294
13475
  }
13295
13476
  return this.pick(kind);
13296
13477
  }
@@ -13312,7 +13493,7 @@ var SecondChannel = class {
13312
13493
  try {
13313
13494
  r = this.o.speak(text, kind);
13314
13495
  } catch (e) {
13315
- log28.warn(`speak threw \u2014 line dropped: ${e.message}`);
13496
+ log29.warn(`speak threw \u2014 line dropped: ${e.message}`);
13316
13497
  return false;
13317
13498
  }
13318
13499
  if (r === false) return false;
@@ -13325,7 +13506,7 @@ var SecondChannel = class {
13325
13506
  this.o.show(text, kind);
13326
13507
  const p = Promise.resolve(r).then(() => {
13327
13508
  }, (e) => {
13328
- log28.warn(`channel line playback failed: ${e?.message ?? e}`);
13509
+ log29.warn(`channel line playback failed: ${e?.message ?? e}`);
13329
13510
  }).finally(() => {
13330
13511
  if (this.inflight === p) this.inflight = null;
13331
13512
  });
@@ -13383,7 +13564,7 @@ function closedQuestion(reply) {
13383
13564
  }
13384
13565
 
13385
13566
  // src/voice/call.ts
13386
- var log29 = forComponent("VoiceCall");
13567
+ var log30 = forComponent("VoiceCall");
13387
13568
  var VoiceCallOptions = class {
13388
13569
  /** Host runs MAIN with exactly `text` (a replace: roll the `replaces` turn back first). */
13389
13570
  sendTurn = () => {
@@ -13609,7 +13790,7 @@ var VoiceCall = class {
13609
13790
  try {
13610
13791
  this.o.cancel();
13611
13792
  } catch (e) {
13612
- log29.warn(`cancel threw: ${e.message}`);
13793
+ log30.warn(`cancel threw: ${e.message}`);
13613
13794
  }
13614
13795
  if (this.cur?.running) {
13615
13796
  this.cur.running = false;
@@ -13696,7 +13877,7 @@ var VoiceCall = class {
13696
13877
  try {
13697
13878
  r = this.o.speak(text, kind);
13698
13879
  } catch (e) {
13699
- log29.warn(`speak threw: ${e.message}`);
13880
+ log30.warn(`speak threw: ${e.message}`);
13700
13881
  return false;
13701
13882
  }
13702
13883
  if (r === false) return false;
@@ -13776,6 +13957,7 @@ var VoiceCall = class {
13776
13957
  init_llm();
13777
13958
  import { MemFilesystem as MemFilesystem3, IndexedDbFilesystem, CommandExecutor as CommandExecutor2, registerHeadlessCommands as registerHeadlessCommands2 } from "@livx.cc/wcli/core";
13778
13959
  export {
13960
+ ADVISE_LIMIT,
13779
13961
  Agent,
13780
13962
  AgentOptions,
13781
13963
  BodDbFilesystem,
@@ -13806,6 +13988,9 @@ export {
13806
13988
  MountFilesystem,
13807
13989
  NodeDiskFilesystem,
13808
13990
  OverlayFilesystem,
13991
+ PLANNER_BLOCKED,
13992
+ PLANNER_REFUSAL,
13993
+ PLAN_FIRST,
13809
13994
  PROGRESS_TEMPLATES,
13810
13995
  PermissionOptions,
13811
13996
  PermissionPolicy,
@@ -13830,6 +14015,9 @@ export {
13830
14015
  VoiceCallOptions,
13831
14016
  VoiceEngine,
13832
14017
  VoiceEngineOptions,
14018
+ WRITE_TOOLS,
14019
+ advisorFor,
14020
+ advisorPrompt,
13833
14021
  applyEditsTool,
13834
14022
  askUserQuestionTool,
13835
14023
  bashTool,
@@ -13846,6 +14034,9 @@ export {
13846
14034
  cronMatches,
13847
14035
  decodeDdgUrl,
13848
14036
  defaultTools,
14037
+ delegatePrompt,
14038
+ delegateTools,
14039
+ delegateWorkerFor,
13849
14040
  diskAgentOptions,
13850
14041
  editTool,
13851
14042
  elideImages,
@@ -13885,6 +14076,7 @@ export {
13885
14076
  loadMemory,
13886
14077
  loadSkills,
13887
14078
  log,
14079
+ makeAdvisor,
13888
14080
  makeAskTool,
13889
14081
  makeContext,
13890
14082
  makeGlobTool,
@@ -13904,6 +14096,7 @@ export {
13904
14096
  messageHasImage,
13905
14097
  messageHasImageRef,
13906
14098
  messageHasInlineImage,
14099
+ meterWorkers,
13907
14100
  mintImageRef,
13908
14101
  mkdirp,
13909
14102
  modelShortLabel,