@livx.cc/agentx 0.99.121 → 0.99.123

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
@@ -799,6 +799,7 @@ var init_tools_structured = __esm({
799
799
  };
800
800
  writeTool = {
801
801
  name: "Write",
802
+ writes: true,
802
803
  description: "Create or overwrite a file with the given contents, creating parent directories as needed. Use for new files instead of `bash echo >`. To replace an EXISTING file you must Read it first \u2014 overwriting an un-Read file is refused (it would destroy unseen content).",
803
804
  parameters: {
804
805
  type: "object",
@@ -824,6 +825,7 @@ var init_tools_structured = __esm({
824
825
  };
825
826
  multiEditTool = {
826
827
  name: "MultiEdit",
828
+ writes: true,
827
829
  description: "Apply several exact-substring replacements to one file in order, in a single call. Requires a prior Read. Each `old_string` must be unique at the time it is applied. All-or-nothing: if any edit fails, none are written.",
828
830
  parameters: {
829
831
  type: "object",
@@ -865,6 +867,7 @@ var init_tools_structured = __esm({
865
867
  };
866
868
  applyEditsTool = {
867
869
  name: "ApplyEdits",
870
+ writes: true,
868
871
  description: "Apply edits across one or MORE files in a single call \u2014 for cross-file refactors (rename/extract/move). edits=[{path, old_string?, new_string}]. WITH old_string: replace that exact substring (must be UNIQUE in the file \u2014 add surrounding context; read fresh + verified, no prior Read needed). WITHOUT old_string: write new_string as the whole file (creates it + parent dirs; but to OVERWRITE a file that already exists you must Read it first, else use old_string to edit part of it). Locate sites first with Grep (its output shows the exact text). Atomic: validated across all files before any write.",
869
872
  parameters: {
870
873
  type: "object",
@@ -1579,6 +1582,7 @@ var init_tools = __esm({
1579
1582
  };
1580
1583
  bashTool = {
1581
1584
  name: "bash",
1585
+ writes: true,
1582
1586
  description: "Run a shell command. Supports ls, cat, grep, find, head, tail, echo, mkdir, rm, mv, cp, wc, pipes (|), redirects (>, >>), and chaining (&&, ||, ;). Best for: running tests/builds, file operations (mkdir/mv/rm), and piped workflows. For searching file contents, prefer `Grep` (structured results, no re-parse). For finding files by name, prefer `Glob`.",
1583
1587
  parameters: {
1584
1588
  type: "object",
@@ -1656,6 +1660,7 @@ var init_tools = __esm({
1656
1660
  };
1657
1661
  editTool = {
1658
1662
  name: "Edit",
1663
+ writes: true,
1659
1664
  description: "Replace an exact substring in a file. Requires a prior Read of the same file. `old_string` must occur exactly once \u2014 include surrounding context to disambiguate.",
1660
1665
  parameters: {
1661
1666
  type: "object",
@@ -2754,7 +2759,7 @@ async function psGroupCpuSeconds(pgid) {
2754
2759
  try {
2755
2760
  return await winTreeCpuSeconds(pgid);
2756
2761
  } catch (e) {
2757
- log9.debug(`cpu probe failed for ${pgid}: ${e}`);
2762
+ log10.debug(`cpu probe failed for ${pgid}: ${e}`);
2758
2763
  return void 0;
2759
2764
  }
2760
2765
  }
@@ -2763,7 +2768,7 @@ async function psGroupCpuSeconds(pgid) {
2763
2768
  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
2769
  return sumTreeCpu(out, pgid);
2765
2770
  } catch (e) {
2766
- log9.debug(`ps cpu probe failed for group ${pgid}: ${e}`);
2771
+ log10.debug(`ps cpu probe failed for group ${pgid}: ${e}`);
2767
2772
  return void 0;
2768
2773
  }
2769
2774
  }
@@ -2792,6 +2797,7 @@ function makeRealShellTool(options) {
2792
2797
  };
2793
2798
  return {
2794
2799
  name: "Shell",
2800
+ writes: true,
2795
2801
  // Rebind for an isolated child agent (git worktree): same policy/env/timeouts, new cwd. The
2796
2802
  // `registry` is deliberately dropped — it is bound to the PARENT's cwd (and, in hosts like
2797
2803
  // shraga-ee, to the parent's session), so a background job started from the child would run
@@ -2912,7 +2918,7 @@ function makeRealShellTool(options) {
2912
2918
  proc.stderr?.on("data", collect);
2913
2919
  proc.on("error", (err) => {
2914
2920
  if (err?.name === "AbortError" || ctl.signal.aborted) return finish(reasonFor(timedOut, timeoutMs, clean(out)));
2915
- log9.debug("shell spawn error", err);
2921
+ log10.debug("shell spawn error", err);
2916
2922
  finish(`[exit 1] ${err?.message ?? err}${out ? "\n" + clean(out) : ""}`);
2917
2923
  });
2918
2924
  proc.on("close", (code) => {
@@ -2991,7 +2997,7 @@ ${clean(out) || "(no output yet)"}`;
2991
2997
  }
2992
2998
  ];
2993
2999
  }
2994
- var log9, clean, DETACHED, shellArgv, SECRET_ENV_RE, _spawn, ShellJobRegistry, shellStatus, NO_JOB2;
3000
+ var log10, clean, DETACHED, shellArgv, SECRET_ENV_RE, _spawn, ShellJobRegistry, shellStatus, NO_JOB2;
2995
3001
  var init_tools_shell = __esm({
2996
3002
  "src/tools.shell.ts"() {
2997
3003
  "use strict";
@@ -3001,7 +3007,7 @@ var init_tools_shell = __esm({
3001
3007
  init_shell_sandbox();
3002
3008
  init_tools_jobs();
3003
3009
  init_platform();
3004
- log9 = forComponent("shell");
3010
+ log10 = forComponent("shell");
3005
3011
  clean = (s) => truncateOutput(redactSecrets(s.replace(/\n+$/, "")));
3006
3012
  DETACHED = { stdio: ["ignore", "pipe", "pipe"], ...DETACH_OPTIONS };
3007
3013
  shellArgv = (sh, command) => ({ bin: sh.bin, args: sh.args(command), ...sh.spawnOptions ? { spawnOptions: sh.spawnOptions } : {} });
@@ -3708,8 +3714,8 @@ function memorySearchTool(fs, dirs) {
3708
3714
  return `Error: invalid regex: ${e}`;
3709
3715
  }
3710
3716
  } else {
3711
- const lower = q.toLowerCase();
3712
- matcher = (l) => l.toLowerCase().includes(lower);
3717
+ const lower2 = q.toLowerCase();
3718
+ matcher = (l) => l.toLowerCase().includes(lower2);
3713
3719
  }
3714
3720
  const loaded = [];
3715
3721
  for (const slug2 of slugs) {
@@ -4276,7 +4282,11 @@ function childOptionsFor(opts, fs, depth, maxDepth, agentType, model, root) {
4276
4282
  if (po) childOpts.providerOptions = po;
4277
4283
  const deny = opts.permissions?.options.rules.filter((r) => r.decision === "deny") ?? [];
4278
4284
  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) };
4285
+ const h = opts.hooks;
4286
+ if (h?.preToolUse || opts.inheritPostToolUse && h?.postToolUse) childOpts.hooks = {
4287
+ ...h.preToolUse ? { preToolUse: h.preToolUse.bind(h) } : {},
4288
+ ...opts.inheritPostToolUse && h.postToolUse ? { postToolUse: h.postToolUse.bind(h) } : {}
4289
+ };
4280
4290
  return childOpts;
4281
4291
  }
4282
4292
  function makeTaskTool(opts) {
@@ -4500,6 +4510,159 @@ async function loadAgents(fs, dir) {
4500
4510
  init_OverlayFilesystem();
4501
4511
  init_logging();
4502
4512
 
4513
+ // src/modes.ts
4514
+ init_logging();
4515
+ var log8 = forComponent("modes");
4516
+ var WRITE_TOOLS = /* @__PURE__ */ new Set(["Write", "Edit", "MultiEdit", "ApplyEdits"]);
4517
+ var PLANNER_BLOCKED = /* @__PURE__ */ new Set([...WRITE_TOOLS, "bash", "Shell"]);
4518
+ var lower = (xs) => new Set([...xs].map((x) => x.toLowerCase()));
4519
+ var PLANNER_BLOCKED_LC = lower(PLANNER_BLOCKED);
4520
+ var WRITE_TOOLS_LC = lower(WRITE_TOOLS);
4521
+ var plannerBlocks = (t) => !!t.writes || PLANNER_BLOCKED_LC.has(t.name.toLowerCase());
4522
+ var isFileWrite = (t) => WRITE_TOOLS_LC.has(t.name.toLowerCase());
4523
+ 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.";
4524
+ var PLAN_FIRST = "Refused: check your plan with `Advise` before the first write (files, data shape, the hard parts). Then write.";
4525
+ var ADVISE_LIMIT = 3;
4526
+ var SNAPSHOT_CAP = 6e4;
4527
+ function pairOk(kind, main, other) {
4528
+ if (other === main) {
4529
+ log8.warn(`${kind} off: both sides are ${main}`);
4530
+ return false;
4531
+ }
4532
+ if (main.startsWith("cursor/") || other.startsWith("cursor/")) {
4533
+ log8.warn(`${kind} off: cursor models are not supported (${main} / ${other})`);
4534
+ return false;
4535
+ }
4536
+ return true;
4537
+ }
4538
+ function delegateWorkerFor(planner, worker) {
4539
+ return worker && pairOk("delegate", planner, worker) ? worker : void 0;
4540
+ }
4541
+ function advisorFor(worker, advisor) {
4542
+ return advisor && pairOk("advisor", worker, advisor) ? advisor : void 0;
4543
+ }
4544
+ function delegateTools(tools, planner) {
4545
+ return tools.map((t) => !plannerBlocks(t) ? t : {
4546
+ ...t,
4547
+ run: async (...a) => a[1]?.model === planner ? PLANNER_REFUSAL : t.run(...a)
4548
+ });
4549
+ }
4550
+ function delegatePrompt(worker) {
4551
+ return `
4552
+
4553
+ ## You are the PLANNER
4554
+ 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.`;
4555
+ }
4556
+ function advisorPrompt(advisor) {
4557
+ return `
4558
+
4559
+ ## You have an ADVISOR
4560
+ 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.`;
4561
+ }
4562
+ async function textOf(r) {
4563
+ if (r && typeof r[Symbol.asyncIterator] === "function") {
4564
+ let s = "";
4565
+ for await (const c of r) if (typeof c?.content === "string") s += c.content;
4566
+ return s;
4567
+ }
4568
+ return String(r?.content ?? r?.text ?? "");
4569
+ }
4570
+ function makeAdvisor(o) {
4571
+ let used = 0;
4572
+ const limit = o.limit ?? ADVISE_LIMIT;
4573
+ const tool = {
4574
+ name: "Advise",
4575
+ 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.`,
4576
+ parameters: { type: "object", properties: { question: { type: "string", description: "what you want advice on, with the context it needs" } }, required: ["question"] },
4577
+ async run(args) {
4578
+ const question = String(args?.question ?? "").trim();
4579
+ if (!question) return "No advice: the question is empty.";
4580
+ if (used >= limit) return `No advice: the limit of ${limit} calls is used. Continue with your own judgment.`;
4581
+ used++;
4582
+ let state = "";
4583
+ if (o.snapshot) {
4584
+ const s = String(await Promise.resolve(o.snapshot()).catch((err) => {
4585
+ log8.warn("advisor snapshot failed:", String(err));
4586
+ return "";
4587
+ }) || "(empty)");
4588
+ state = `
4589
+
4590
+ ## Current state
4591
+ ${s.length > SNAPSHOT_CAP ? `${s.slice(0, SNAPSHOT_CAP)}
4592
+ \u2026(cut)` : s}`;
4593
+ }
4594
+ const review = used > 1 ? "" : "\n\n(When the work is complete, call Advise again for a review.)";
4595
+ try {
4596
+ const r = await o.ai.chat({
4597
+ model: o.advisor,
4598
+ messages: [
4599
+ { 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." },
4600
+ { role: "user", content: `## User request
4601
+ ${o.request()}${state}
4602
+
4603
+ ## Question
4604
+ ${question}` }
4605
+ ],
4606
+ maxTokens: 2e3
4607
+ });
4608
+ return ((await textOf(r)).trim() || "No advice: the advisor returned nothing.") + review;
4609
+ } catch (err) {
4610
+ log8.warn("advisor call failed:", String(err));
4611
+ return `No advice: the advisor call failed (${String(err?.message ?? err).slice(0, 120)}). Continue with your own judgment.`;
4612
+ }
4613
+ }
4614
+ };
4615
+ const gate = (tools) => tools.map((t) => !isFileWrite(t) ? t : { ...t, run: async (...a) => used ? t.run(...a) : PLAN_FIRST });
4616
+ return { tool, gate, used: () => used };
4617
+ }
4618
+ function meterWorkers(ai, main) {
4619
+ let total;
4620
+ let resolved;
4621
+ const seen = () => {
4622
+ resolved = ai.modelUsed ?? resolved;
4623
+ };
4624
+ const add = (u) => {
4625
+ if (!u) return;
4626
+ const t = total ??= { promptTokens: 0, completionTokens: 0, totalTokens: 0, cacheCreationTokens: 0, cacheReadTokens: 0 };
4627
+ t.promptTokens += u.promptTokens ?? 0;
4628
+ t.completionTokens += u.completionTokens ?? 0;
4629
+ t.totalTokens += u.totalTokens ?? 0;
4630
+ t.cacheCreationTokens += u.cacheCreationTokens ?? 0;
4631
+ t.cacheReadTokens += u.cacheReadTokens ?? 0;
4632
+ };
4633
+ async function* tap(it) {
4634
+ let last;
4635
+ try {
4636
+ for await (const c of it) {
4637
+ if (c?.usage) last = c.usage;
4638
+ yield c;
4639
+ }
4640
+ } finally {
4641
+ add(last);
4642
+ seen();
4643
+ }
4644
+ }
4645
+ const chat = async (o) => {
4646
+ const r = await ai.chat(o);
4647
+ if (o?.model === main) return r;
4648
+ if (r && typeof r[Symbol.asyncIterator] === "function") return tap(r);
4649
+ add(r?.usage);
4650
+ seen();
4651
+ return r;
4652
+ };
4653
+ return { ai: new Proxy(ai, { get: (t, k) => k === "chat" ? chat : Reflect.get(t, k) }), usage: () => total && { ...total }, model: () => resolved, reset: () => {
4654
+ total = void 0;
4655
+ resolved = void 0;
4656
+ } };
4657
+ }
4658
+ var CC_WRITE_TOOLS = ["Write", "Edit", "MultiEdit", "NotebookEdit"];
4659
+ function ccModeOptions(kind, hostHooks, used) {
4660
+ if (kind === "delegate") return { bridge: /* @__PURE__ */ new Set(["Task", "TaskBatch"]), disallow: [...CC_WRITE_TOOLS, "Bash", "Task", "Agent"] };
4661
+ if (kind !== "advisor") return { bridge: /* @__PURE__ */ new Set(), disallow: [] };
4662
+ const gate = { matcher: CC_WRITE_TOOLS.join("|"), hooks: [async () => used() ? {} : { hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "deny", permissionDecisionReason: PLAN_FIRST.replace("`Advise`", "`mcp__agentx__Advise`") } }] };
4663
+ return { bridge: /* @__PURE__ */ new Set(["Advise"]), disallow: [], hooks: { ...hostHooks, PreToolUse: [...hostHooks?.PreToolUse ?? [], gate] } };
4664
+ }
4665
+
4503
4666
  // src/lint.ts
4504
4667
  var CODE_RE = /\.(ts|tsx|js|jsx|mjs|cjs)$/;
4505
4668
  function checkSyntax(path, content) {
@@ -4579,7 +4742,7 @@ function reasoningToChatFragment(model, effort) {
4579
4742
  }
4580
4743
 
4581
4744
  // src/Agent.ts
4582
- var log8 = forComponent("Agent");
4745
+ var log9 = forComponent("Agent");
4583
4746
  function envInt(key, fallback) {
4584
4747
  const n = typeof process !== "undefined" ? Number(process.env?.[key]) : NaN;
4585
4748
  return Number.isFinite(n) && n >= 0 ? n : fallback;
@@ -4708,6 +4871,13 @@ var AgentOptions = class {
4708
4871
  hooks;
4709
4872
  /** If true: add the `Task` tool so the agent can spawn depth-limited child agents over the VFS. */
4710
4873
  subagents = false;
4874
+ /** DELEGATE MODE: `model` plans, `worker` writes. The planner's Write/Edit/MultiEdit/ApplyEdits/bash/Shell are
4875
+ * refused at execution; Task/TaskBatch are on (maxDepth 1, children default to `worker`) and children get the
4876
+ * parent's `postToolUse` too. Off when the models are equal or either is cursor/*. Wins over `advisor`. */
4877
+ delegate;
4878
+ /** ADVISOR MODE: `model` does the work; an `Advise` tool asks `advisor.model` (≤`limit`, default 3) with only
4879
+ * the request + `snapshot()` (host state, optional) + the question. The first write waits for one consult. */
4880
+ advisor;
4711
4881
  /** VFS dir of typed-subagent defs (`<dir>/<name>.md`). If set with `subagents`: inject a catalog + enable the `Task` `agentType` param. */
4712
4882
  agentsDir;
4713
4883
  /** Current recursion depth (0 = top-level); spawned children run at depth+1. */
@@ -4816,6 +4986,13 @@ var Agent = class _Agent {
4816
4986
  lastTrimNotified = 0;
4817
4987
  // last auto-trim drop count surfaced via host.notify (dedup)
4818
4988
  activeTools = [];
4989
+ meter;
4990
+ // delegate/advisor: meters the second model's calls
4991
+ modeModel;
4992
+ modeKind;
4993
+ adv;
4994
+ lastTask = "";
4995
+ // the current turn's text — what the advisor reads as the request
4819
4996
  /** claude-code bridge for agentx-only tools (ccBridge.ts) + the current step's executor it calls. */
4820
4997
  ccServer;
4821
4998
  ccExec;
@@ -4918,12 +5095,15 @@ var Agent = class _Agent {
4918
5095
  const o = this.options;
4919
5096
  if (!o.model.startsWith("claude-code/")) return void 0;
4920
5097
  const po = { steer: () => this.drainPendingSteers() };
4921
- const bridged = (t) => o.ccBridge === "all" || CC_BRIDGED.has(t.name);
5098
+ const mode = ccModeOptions(this.modeKind, o.providerOptions?.hooks, () => this.adv?.used() ?? 0);
5099
+ const bridged = (t) => o.ccBridge === "all" || CC_BRIDGED.has(t.name) || mode.bridge.has(t.name);
4922
5100
  if (o.providerOptions?.claudeCodeSession && this.activeTools.some(bridged)) {
4923
5101
  this.ccServer ??= ccToolServer("agentx", () => this.activeTools.filter(bridged), (n, a) => this.ccExec(n, a));
4924
5102
  po.mcpServers = { ...o.providerOptions?.mcpServers, agentx: this.ccServer };
4925
5103
  }
4926
- const denyTools = claudeCodeDisallowedTools(o.permissions);
5104
+ const denyTools = [...claudeCodeDisallowedTools(o.permissions), ...mode.disallow];
5105
+ if (mode.hooks) po.hooks = mode.hooks;
5106
+ if (this.modeKind && o.providerOptions?.claudeCodeSession) po.claudeCodeSession = `${o.providerOptions.claudeCodeSession}:${this.modeKind}`;
4927
5107
  if (denyTools.length) {
4928
5108
  const prior = o.providerOptions?.disallowedTools ?? [];
4929
5109
  po.disallowedTools = [.../* @__PURE__ */ new Set([...prior, ...denyTools])];
@@ -4999,7 +5179,7 @@ var Agent = class _Agent {
4999
5179
  const disk = new NodeDiskFilesystem2(process.cwd());
5000
5180
  await disk.init();
5001
5181
  this.options.fs = new JailedFilesystem2(disk);
5002
- log8.info(`no fs provided \u2014 defaulting to jailed real disk at ${process.cwd()}`);
5182
+ log9.info(`no fs provided \u2014 defaulting to jailed real disk at ${process.cwd()}`);
5003
5183
  }
5004
5184
  this.buildCtx();
5005
5185
  }
@@ -5016,6 +5196,13 @@ var Agent = class _Agent {
5016
5196
  const fs = o.fs;
5017
5197
  let systemPrompt = o.systemPrompt;
5018
5198
  let tools = o.tools;
5199
+ this.ctx.model = o.model;
5200
+ const worker = o.delegate && delegateWorkerFor(o.model, o.delegate.worker);
5201
+ const advisor = !worker && o.advisor ? advisorFor(o.model, o.advisor.model) : void 0;
5202
+ this.meter = worker || advisor ? meterWorkers(o.ai, o.model) : void 0;
5203
+ const ai = this.meter?.ai ?? o.ai;
5204
+ this.modeKind = worker ? "delegate" : advisor ? "advisor" : void 0;
5205
+ const adv = this.adv = advisor ? makeAdvisor({ ai, advisor, request: () => this.lastTask, snapshot: o.advisor.snapshot, limit: o.advisor.limit }) : void 0;
5019
5206
  if (o.instructionFiles !== false) {
5020
5207
  const names = Array.isArray(o.instructionFiles) ? o.instructionFiles : void 0;
5021
5208
  const ins = await loadInstructions(fs, names);
@@ -5046,7 +5233,7 @@ var Agent = class _Agent {
5046
5233
  if (tool) tools = [...tools, tool];
5047
5234
  }
5048
5235
  if (o.host && o.askUserQuestion) tools = [...tools, askUserQuestionTool];
5049
- if (o.subagents) {
5236
+ if (o.subagents || worker) {
5050
5237
  let agents;
5051
5238
  if (o.agentsDir) {
5052
5239
  const loaded = await loadAgents(fs, o.agentsDir);
@@ -5054,17 +5241,18 @@ var Agent = class _Agent {
5054
5241
  if (loaded.catalog) systemPrompt += "\n\n" + loaded.catalog;
5055
5242
  }
5056
5243
  const taskOpts = {
5057
- ai: o.ai,
5058
- model: o.model,
5244
+ ai,
5245
+ model: worker || o.model,
5059
5246
  fs,
5060
5247
  depth: o.depth,
5061
- maxDepth: o.maxDepth,
5248
+ maxDepth: worker ? Math.min(o.maxDepth, o.depth + 1) : o.maxDepth,
5062
5249
  agents,
5250
+ inheritPostToolUse: !!worker,
5063
5251
  hooks: o.hooks,
5064
5252
  traceDir: o.traceDir,
5065
5253
  permissions: o.permissions,
5066
5254
  // a child obeys the parent's allow/deny rules (else Task is a one-call bypass)
5067
- tools: [...o.tools, ...this.injectedTools],
5255
+ tools: worker ? delegateTools([...o.tools, ...this.injectedTools], o.model) : [...o.tools, ...this.injectedTools],
5068
5256
  systemPrompt: o.systemPrompt,
5069
5257
  // A child never resumes the parent's own claude-code session (the host's `resume` link is the parent's).
5070
5258
  providerOptionsFor: o.providerOptionsFor ?? ((m) => modelProvider(m) === modelProvider(o.model) && o.providerOptions ? { ...o.providerOptions, resume: void 0, onClaudeCodeSession: void 0 } : void 0)
@@ -5077,6 +5265,15 @@ var Agent = class _Agent {
5077
5265
  if (plan) tools = [...tools, plan.tool];
5078
5266
  this.activeHooks = composeHooks(o.hooks, plan?.hooks, o.permissions?.hooks());
5079
5267
  this.activeTools = [...tools, ...this.injectedTools];
5268
+ if (worker) {
5269
+ this.activeTools = delegateTools(this.activeTools, o.model);
5270
+ systemPrompt += delegatePrompt(worker);
5271
+ }
5272
+ if (adv) {
5273
+ this.activeTools = [...adv.gate(this.activeTools), adv.tool];
5274
+ systemPrompt += advisorPrompt(advisor);
5275
+ }
5276
+ this.modeModel = worker || advisor;
5080
5277
  this.systemPromptCache = systemPrompt;
5081
5278
  this.prepared = true;
5082
5279
  return systemPrompt;
@@ -5089,6 +5286,7 @@ var Agent = class _Agent {
5089
5286
  this.started = false;
5090
5287
  this.invokedTools.clear();
5091
5288
  this.fabricatedClaims.clear();
5289
+ this.lastTask = contentText(task);
5092
5290
  const systemPrompt = await this.prepare(contentText(task));
5093
5291
  const startCtx = await this.fireSessionStart();
5094
5292
  const userContent = await this.applyPromptSubmit(task);
@@ -5124,6 +5322,7 @@ var Agent = class _Agent {
5124
5322
  */
5125
5323
  async send(task) {
5126
5324
  await this.ensureFs();
5325
+ this.lastTask = contentText(task);
5127
5326
  const systemPrompt = await this.prepare(contentText(task));
5128
5327
  const startCtx = await this.fireSessionStart();
5129
5328
  const userContent = await this.applyPromptSubmit(task);
@@ -5174,6 +5373,12 @@ var Agent = class _Agent {
5174
5373
  if (next !== this.transcript) this.transcript = next;
5175
5374
  }
5176
5375
  async runLoop() {
5376
+ this.meter?.reset();
5377
+ const res = await this.loop();
5378
+ const workerUsage = this.meter?.usage();
5379
+ return workerUsage ? { ...res, workerModel: this.meter.model() ?? this.modeModel, workerUsage } : res;
5380
+ }
5381
+ async loop() {
5177
5382
  const o = this.options;
5178
5383
  const wireTools = toWireTools(this.activeTools);
5179
5384
  const useStream = o.stream === true && typeof o.host?.notify === "function";
@@ -5193,7 +5398,7 @@ var Agent = class _Agent {
5193
5398
  let lastProgressAt = start;
5194
5399
  const turnUser = this.transcript[this.transcript.length - 1];
5195
5400
  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` : ""})`);
5401
+ 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
5402
  this.jobs.killAll(`run ended: ${finishReason}`, true);
5198
5403
  const claims = this.survivingClaims();
5199
5404
  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 +5424,9 @@ var Agent = class _Agent {
5219
5424
  this.nativeHoldMs = o.nativeHoldMs;
5220
5425
  if (isCursorWithTools) {
5221
5426
  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.`);
5427
+ 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
5428
  }
5224
- for (const v of toolTimeoutViolations(this.activeTools, effectiveToolTimeoutMs)) log8.warn(v);
5429
+ for (const v of toolTimeoutViolations(this.activeTools, effectiveToolTimeoutMs)) log9.warn(v);
5225
5430
  }
5226
5431
  const hostExec = async (name, args) => {
5227
5432
  const tc = { id: `host-${Date.now()}`, type: "function", function: { name, arguments: JSON.stringify(args) } };
@@ -5280,7 +5485,7 @@ var Agent = class _Agent {
5280
5485
  } catch (err) {
5281
5486
  if (!this.imagesOff && /does not support .*input/i.test(String(err?.message ?? "")) && sent.some((m) => Array.isArray(m.content) && m.role === "tool")) {
5282
5487
  this.imagesOff = true;
5283
- log8.warn(`model refused image input (${err?.message}) \u2014 resending with image Reads as text stubs`);
5488
+ log9.warn(`model refused image input (${err?.message}) \u2014 resending with image Reads as text stubs`);
5284
5489
  sent = this.trimContext();
5285
5490
  attempt--;
5286
5491
  continue;
@@ -5292,11 +5497,11 @@ var Agent = class _Agent {
5292
5497
  const empty = err?.code === "empty" || err?.code === "cursor_silent";
5293
5498
  const maxAttempts = empty ? Math.max(o.emptyRetries, 0) : 2;
5294
5499
  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)");
5500
+ if (replaysTool) log9.warn("model stall after tool(s) already ran this step \u2014 not retrying (a retry would replay them)");
5296
5501
  const transient = !o.signal?.aborted && !isAbortError(err) && !replaysTool && attempt < maxAttempts && (network || serverSide || stalled || empty);
5297
5502
  if (!transient) throw err;
5298
5503
  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`);
5504
+ log9.warn(`${empty ? "empty model turn" : stalled ? "model stall" : "network drop"} mid-step (${err?.message ?? err}) \u2014 retrying in ${waitMs}ms`);
5300
5505
  o.host?.notify?.({ kind: "retry", message: `${empty ? "model returned nothing" : stalled ? "model stalled" : "connection dropped"} \u2014 retrying step (#${attempt + 1})` });
5301
5506
  await new Promise((r) => setTimeout(r, waitMs));
5302
5507
  }
@@ -5304,12 +5509,12 @@ var Agent = class _Agent {
5304
5509
  } catch (err) {
5305
5510
  if (err?.code === "budget") return kill("budget");
5306
5511
  if (err?.code === "stall") {
5307
- log8.warn(`model stalled out after retries: ${err?.message ?? err}`);
5512
+ log9.warn(`model stalled out after retries: ${err?.message ?? err}`);
5308
5513
  return { ...kill("timeout"), providerFinishReason: "none (stalled)", error: err };
5309
5514
  }
5310
5515
  if (o.signal?.aborted || isAbortError(err)) return kill("aborted");
5311
5516
  if (err?.code === "empty" || err?.code === "cursor_silent") {
5312
- log8.warn(`model returned empty/silent turns after retries \u2014 surfacing as failed`);
5517
+ log9.warn(`model returned empty/silent turns after retries \u2014 surfacing as failed`);
5313
5518
  const pfr = err?.providerFinishReason;
5314
5519
  return { text: "", steps, finishReason: "empty", ...pfr ? { providerFinishReason: pfr } : {}, messages: this.transcript, usage, usageEstimated, error: err };
5315
5520
  }
@@ -5322,13 +5527,13 @@ var Agent = class _Agent {
5322
5527
  }
5323
5528
  if (bodyStr && err instanceof Error && !err.message.includes(bodyStr)) err.detail = bodyStr;
5324
5529
  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);
5530
+ log9.error(`chat() failed: ${err?.message ?? err}${bodyStr ? ` \u2014 ${bodyStr}` : ""}`, errLog);
5326
5531
  return { text: "", steps, finishReason: "error", messages: this.transcript, usage, usageEstimated, error: err };
5327
5532
  }
5328
5533
  if (o.signal?.aborted) return kill("aborted");
5329
5534
  if (res.finishReason === "error" && !res.toolCalls?.length) {
5330
5535
  const err = new Error(contentText(res.content ?? "").trim() || "provider error");
5331
- log8.error(`chat() failed: ${err.message}`);
5536
+ log9.error(`chat() failed: ${err.message}`);
5332
5537
  return { text: "", steps, finishReason: "error", messages: this.transcript, usage, usageEstimated, error: err };
5333
5538
  }
5334
5539
  if (!res.usage) {
@@ -5396,21 +5601,21 @@ var Agent = class _Agent {
5396
5601
  if (emptyTurn && o.failEmptyTurns) {
5397
5602
  if (!emptyNudged && canNudge()) {
5398
5603
  emptyNudged = true;
5399
- log8.warn(`empty model turn after tool work (step ${steps}, provider finishReason=${res.finishReason ?? "n/a"}) \u2014 nudging once`);
5604
+ log9.warn(`empty model turn after tool work (step ${steps}, provider finishReason=${res.finishReason ?? "n/a"}) \u2014 nudging once`);
5400
5605
  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
5606
  continue;
5402
5607
  }
5403
- log8.warn(`model returned empty turns after tool work \u2014 surfacing as failed`);
5608
+ log9.warn(`model returned empty turns after tool work \u2014 surfacing as failed`);
5404
5609
  const e = new Error("empty_response: model returned an empty turn (no text, no tools) after a re-prompt");
5405
5610
  e.code = "empty";
5406
5611
  return { text: "", steps, finishReason: "empty", ...res.finishReason ? { providerFinishReason: res.finishReason } : {}, messages: this.transcript, usage, usageEstimated, error: e };
5407
5612
  }
5408
5613
  const fabricated = o.catchFabricatedToolUse && !closureNudged ? this.fabricatedToolNames(contentText(res.content ?? "")) : [];
5409
5614
  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`);
5615
+ 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
5616
  if (fabricated.length && canNudge()) {
5412
5617
  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`);
5618
+ 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
5619
  this.transcript.push({
5415
5620
  role: "user",
5416
5621
  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 +5624,14 @@ var Agent = class _Agent {
5419
5624
  }
5420
5625
  if (o.closeDelegatedTurns && res.endedWithoutClosing && !closureNudged && canNudge()) {
5421
5626
  closureNudged = true;
5422
- log8.verbose("delegated turn went silent after a tool \u2014 nudging a closing answer");
5627
+ log9.verbose("delegated turn went silent after a tool \u2014 nudging a closing answer");
5423
5628
  this.transcript.push({
5424
5629
  role: "user",
5425
5630
  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
5631
  });
5427
5632
  continue;
5428
5633
  }
5429
- log8.verbose(`completed in ${steps} step(s)`);
5634
+ log9.verbose(`completed in ${steps} step(s)`);
5430
5635
  const drained = await this.jobs.drain(o.signal);
5431
5636
  if (o.signal?.aborted) return kill("aborted");
5432
5637
  if (drained.length) lastProgressAt = Date.now();
@@ -5460,7 +5665,7 @@ var Agent = class _Agent {
5460
5665
  if (this.ctx.needsInput) {
5461
5666
  const question = this.ctx.needsInput;
5462
5667
  this.ctx.needsInput = void 0;
5463
- log8.warn(`needs_input: ${question.question} (steps=${steps})`);
5668
+ log9.warn(`needs_input: ${question.question} (steps=${steps})`);
5464
5669
  await this.jobs.drain();
5465
5670
  const text = [
5466
5671
  `OPEN QUESTION (run paused \u2014 no human was reachable): ${question.question}`,
@@ -5471,7 +5676,7 @@ var Agent = class _Agent {
5471
5676
  if (this.ctx.endTurn) {
5472
5677
  this.ctx.endTurn = void 0;
5473
5678
  if (this.drainInjections()) continue;
5474
- log8.verbose(`turn ended by a tool (steps=${steps})`);
5679
+ log9.verbose(`turn ended by a tool (steps=${steps})`);
5475
5680
  await this.jobs.drain(o.signal);
5476
5681
  return { text: "", steps, finishReason: "stop", messages: this.transcript, usage, usageEstimated };
5477
5682
  }
@@ -5509,12 +5714,12 @@ var Agent = class _Agent {
5509
5714
  if (this.toolHoldActive()) {
5510
5715
  const now3 = Date.now();
5511
5716
  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}`);
5717
+ log9.warn(`stall watchdog: no stream output for ${stallMs}ms \u2014 held by ${who}`);
5513
5718
  this.options.host?.notify?.({ kind: "tool_hold", message: `waiting on ${who}` });
5514
5719
  return poke();
5515
5720
  }
5516
5721
  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(", ")})` : ""));
5722
+ 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
5723
  fired = true;
5519
5724
  ac.abort(stallError());
5520
5725
  }, stallMs);
@@ -5684,13 +5889,13 @@ var Agent = class _Agent {
5684
5889
  const decision = await this.park(Promise.resolve(hooks?.preToolUse?.(call, meta)));
5685
5890
  if (decision?.block) {
5686
5891
  const blocked = `Blocked by hook: ${decision.reason ?? "no reason given"}`;
5687
- log8.debug(`${tc.function.name} -> ${blocked}`);
5892
+ log9.debug(`${tc.function.name} -> ${blocked}`);
5688
5893
  await hooks?.postToolUse?.(call, blocked, meta);
5689
5894
  return blocked;
5690
5895
  }
5691
5896
  this.options.host?.notify?.({ kind: "tool_use", id: tc.id ?? "", name: tc.function.name, input: args });
5692
5897
  if (earlyError) {
5693
- log8.debug(`${tc.function.name} -> ${earlyError}`);
5898
+ log9.debug(`${tc.function.name} -> ${earlyError}`);
5694
5899
  await hooks?.postToolUse?.(call, earlyError, meta);
5695
5900
  this.options.host?.notify?.({ kind: "tool_result", id: tc.id ?? "", output: earlyError, isError: true });
5696
5901
  return earlyError;
@@ -5699,12 +5904,12 @@ var Agent = class _Agent {
5699
5904
  let images;
5700
5905
  let threw = false;
5701
5906
  try {
5702
- log8.debug(`${tc.function.name}(${tc.function.arguments})`);
5907
+ log9.debug(`${tc.function.name}(${tc.function.arguments})`);
5703
5908
  this.ctx.emit = hooks?.onToolOutput ? (chunk) => {
5704
5909
  try {
5705
5910
  hooks.onToolOutput(call, chunk, meta);
5706
5911
  } catch (e) {
5707
- log8.debug(`onToolOutput hook error: ${e}`);
5912
+ log9.debug(`onToolOutput hook error: ${e}`);
5708
5913
  }
5709
5914
  } : void 0;
5710
5915
  const raw = await tool.run(args, this.ctx);
@@ -5716,7 +5921,7 @@ var Agent = class _Agent {
5716
5921
  }
5717
5922
  } catch (e) {
5718
5923
  const msg = e instanceof Error ? e.message : String(e);
5719
- log8.debug(`${tc.function.name} -> error: ${msg}`);
5924
+ log9.debug(`${tc.function.name} -> error: ${msg}`);
5720
5925
  result = `Error: ${msg}`;
5721
5926
  threw = true;
5722
5927
  } finally {
@@ -5993,7 +6198,7 @@ function fitTokenBudget(messages, maxTokens, keepRecentImages = 1, vision = true
5993
6198
  const ids = callIdSet(messages.slice(from));
5994
6199
  while (from < messages.length && messages[from].role === "tool" && !ids.has(messages[from].tool_call_id ?? "")) total -= per[from++];
5995
6200
  if (total > maxTokens)
5996
- log8.warn(`context ~${total} tok still over maxContextTokens=${maxTokens} after trimming (system head can't be dropped)`);
6201
+ log9.warn(`context ~${total} tok still over maxContextTokens=${maxTokens} after trimming (system head can't be dropped)`);
5997
6202
  return [...head, ...messages.slice(from)];
5998
6203
  }
5999
6204
  function compact(m, max, focus) {
@@ -6678,7 +6883,7 @@ init_tools_web();
6678
6883
  init_tools();
6679
6884
  init_tools_structured();
6680
6885
  init_logging();
6681
- var log10 = forComponent("scratch");
6886
+ var log11 = forComponent("scratch");
6682
6887
  var SCRATCH_DIR = "/scratch";
6683
6888
  function shortArgs(args) {
6684
6889
  try {
@@ -6718,7 +6923,7 @@ var Scratch = class {
6718
6923
  await (this.dirReady ??= mkdirp(this.fs, dir));
6719
6924
  await this.fs.writeFile(path, header + raw);
6720
6925
  } catch (e) {
6721
- log10.debug("scratch write failed; returning raw", e);
6926
+ log11.debug("scratch write failed; returning raw", e);
6722
6927
  return raw;
6723
6928
  }
6724
6929
  const preview = raw.slice(0, previewChars).replace(/\s+/g, " ").trim();
@@ -6748,7 +6953,7 @@ To pull a specific detail, Grep/Read ${path}, or call Ask({ question: "\u2026",
6748
6953
  await (this.dirReady ??= mkdirp(this.fs, dir));
6749
6954
  await this.fs.writeFile(path, header + full);
6750
6955
  } catch (e) {
6751
- log10.debug("scratch spill failed; cropping lossy", e);
6956
+ log11.debug("scratch spill failed; cropping lossy", e);
6752
6957
  return full.slice(0, pageBytes) + `
6753
6958
 
6754
6959
  [output cropped to ${pageBytes} of ${full.length} bytes; full output unavailable (scratch write failed) \u2014 refine your query]`;
@@ -6794,7 +6999,7 @@ Question: ${q}`);
6794
6999
  const answer = (res.text ?? "").trim();
6795
7000
  return answer || "(no answer found in scratch)";
6796
7001
  } catch (e) {
6797
- log10.debug("Ask peek failed", e);
7002
+ log11.debug("Ask peek failed", e);
6798
7003
  return `Error querying scratch: ${e?.message ?? e}`;
6799
7004
  }
6800
7005
  }
@@ -6803,7 +7008,7 @@ Question: ${q}`);
6803
7008
 
6804
7009
  // src/lessons.ts
6805
7010
  init_logging();
6806
- var log11 = forComponent("Lessons");
7011
+ var log12 = forComponent("Lessons");
6807
7012
  var LessonOptionsDefaults = class {
6808
7013
  minRepeats = 2;
6809
7014
  };
@@ -6828,8 +7033,8 @@ function lessonCapture(options) {
6828
7033
  counts.set(lesson.slug, n);
6829
7034
  if (n < o.minRepeats) return;
6830
7035
  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)`);
7036
+ await writeFact(o.fs, o.dir, lesson.slug, lesson.body).catch((e) => log12.warn(`could not persist ${lesson.slug}: ${e?.message ?? e}`));
7037
+ log12.debug(`captured lesson ${lesson.slug} (recurred ${n}\xD7)`);
6833
7038
  }
6834
7039
  };
6835
7040
  }
@@ -6837,7 +7042,7 @@ function lessonCapture(options) {
6837
7042
  // src/reflect.ts
6838
7043
  init_llm();
6839
7044
  init_logging();
6840
- var log12 = forComponent("Reflect");
7045
+ var log13 = forComponent("Reflect");
6841
7046
  async function reflectOnRun(o) {
6842
7047
  const digest = digestRun(o.result.messages, o.maxDigestChars ?? 6e3);
6843
7048
  if (!digest.trim()) return null;
@@ -6853,7 +7058,7 @@ If the run was fine or the issue was purely task-specific (not generalizable), r
6853
7058
  const r = await o.ai.chat({ model: o.model, messages: [{ role: "user", content: prompt }], stream: false });
6854
7059
  text = r?.content ?? "";
6855
7060
  } catch (e) {
6856
- log12.warn(`reflection call failed: ${e?.message ?? e}`);
7061
+ log13.warn(`reflection call failed: ${e?.message ?? e}`);
6857
7062
  return null;
6858
7063
  }
6859
7064
  const m = text.match(/LESSON:\s*(.+)/i);
@@ -6863,10 +7068,10 @@ If the run was fine or the issue was purely task-specific (not generalizable), r
6863
7068
  try {
6864
7069
  await writeFact(o.fs, o.dir, slug2, lesson);
6865
7070
  } catch (e) {
6866
- log12.warn(`could not persist lesson: ${e?.message ?? e}`);
7071
+ log13.warn(`could not persist lesson: ${e?.message ?? e}`);
6867
7072
  return null;
6868
7073
  }
6869
- log12.debug(`reflection persisted ${slug2}`);
7074
+ log13.debug(`reflection persisted ${slug2}`);
6870
7075
  return slug2;
6871
7076
  }
6872
7077
  function digestRun(messages, maxChars) {
@@ -6916,7 +7121,7 @@ function forComponent2(name) {
6916
7121
  }
6917
7122
 
6918
7123
  // node_modules/@bod.ee/voice/src/core/emotion.ts
6919
- var log13 = forComponent2("Emotion");
7124
+ var log14 = forComponent2("Emotion");
6920
7125
  var EMOTIONS = [
6921
7126
  // primary (best results)
6922
7127
  "neutral",
@@ -7070,7 +7275,7 @@ var EmotionStream = class {
7070
7275
  if (tag) {
7071
7276
  this.pending = tag;
7072
7277
  if (this.show) display += m[0];
7073
- } else log13.debug(`dropping unknown emotion tag ${m[0]}`);
7278
+ } else log14.debug(`dropping unknown emotion tag ${m[0]}`);
7074
7279
  last = m.index + m[0].length;
7075
7280
  }
7076
7281
  this.emit(body.slice(last), (s, d, p) => {
@@ -7184,8 +7389,8 @@ function isTranscriptComplete(text) {
7184
7389
  const lastWord = words3[words3.length - 1];
7185
7390
  if (COMPLETE_ENDINGS.has(lastWord)) return true;
7186
7391
  if (INCOMPLETE_ENDINGS.has(lastWord)) return false;
7187
- const lower = trimmed.toLowerCase();
7188
- for (const marker of CONTINUATION_MARKERS) if (lower.endsWith(marker)) return false;
7392
+ const lower2 = trimmed.toLowerCase();
7393
+ for (const marker of CONTINUATION_MARKERS) if (lower2.endsWith(marker)) return false;
7189
7394
  if (words3.length <= 2) return false;
7190
7395
  return true;
7191
7396
  }
@@ -7217,7 +7422,7 @@ function computeHoldMs(ctx) {
7217
7422
  }
7218
7423
 
7219
7424
  // node_modules/@bod.ee/voice/src/core/engine.ts
7220
- var log14 = forComponent2("VoiceEngine");
7425
+ var log15 = forComponent2("VoiceEngine");
7221
7426
  var realClock = {
7222
7427
  now: () => performance.now(),
7223
7428
  setTimeout: (fn, ms) => setTimeout(fn, ms),
@@ -7605,8 +7810,8 @@ var VoiceEngine = class _VoiceEngine {
7605
7810
  this.tts.warmup?.();
7606
7811
  this.setState("listening");
7607
7812
  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})`);
7813
+ log15.warn("bargeInMode=aec but mic does not report echoCancellation applied \u2014 STT may transcribe the assistant");
7814
+ log15.debug(`voice I/O up (${this.usingAec ? "AEC" : "heuristic echo"} capture, mode=${this.options.bargeInMode}, mic reports aec=${this.stt.usingAec})`);
7610
7815
  }
7611
7816
  /** The RESOLVED barge-in tier (the single seam every tier branch reads). `bargeInMode` overrides
7612
7817
  * the mic's self-report — see the option doc for why the report is not trustworthy on mobile. */
@@ -7631,7 +7836,7 @@ var VoiceEngine = class _VoiceEngine {
7631
7836
  this.options.onDiag({ t: this.clock.now(), kind, ...fields });
7632
7837
  } catch (e) {
7633
7838
  this.diagOn = false;
7634
- log14.debug(`onDiag threw \u2014 diagnostics disabled: ${e instanceof Error ? e.message : e}`);
7839
+ log15.debug(`onDiag threw \u2014 diagnostics disabled: ${e instanceof Error ? e.message : e}`);
7635
7840
  }
7636
7841
  }
7637
7842
  idleWaiters = [];
@@ -7770,7 +7975,7 @@ var VoiceEngine = class _VoiceEngine {
7770
7975
  }
7771
7976
  for (const w of this.words(this.reply)) this.echoWords.add(w);
7772
7977
  this.tts.speak(forSpeech(speech), true);
7773
- if (!this.spokeDeltas && this.turnStartAt) log14.debug(`ttft: ${Math.round(this.clock.now() - this.turnStartAt)}ms`);
7978
+ if (!this.spokeDeltas && this.turnStartAt) log15.debug(`ttft: ${Math.round(this.clock.now() - this.turnStartAt)}ms`);
7774
7979
  this.spokeDeltas = true;
7775
7980
  this.setState("speaking");
7776
7981
  if (this.options.revealMode !== "off" && display) {
@@ -7883,13 +8088,13 @@ var VoiceEngine = class _VoiceEngine {
7883
8088
  this.drainTimer = null;
7884
8089
  this.speaking = false;
7885
8090
  this.stopWordReveal(true);
7886
- if (this.turnStartAt) log14.debug(`turn: ${Math.round(this.clock.now() - this.turnStartAt)}ms (incl. playback)`);
8091
+ if (this.turnStartAt) log15.debug(`turn: ${Math.round(this.clock.now() - this.turnStartAt)}ms (incl. playback)`);
7887
8092
  this.echoUntil = this.clock.now() + 2500;
7888
8093
  if (!this.usingAec) this.stt.reset();
7889
8094
  try {
7890
8095
  this.options.onSpeechEnd();
7891
8096
  } catch (e) {
7892
- log14.warn(`onSpeechEnd threw: ${e instanceof Error ? e.message : e}`);
8097
+ log15.warn(`onSpeechEnd threw: ${e instanceof Error ? e.message : e}`);
7893
8098
  }
7894
8099
  this.setState("listening");
7895
8100
  if (this.uttQueue.length) this.pumpQueue();
@@ -7963,7 +8168,7 @@ var VoiceEngine = class _VoiceEngine {
7963
8168
  if (!host) return pick;
7964
8169
  const ctx = { text: this.lastDispatchText, question: this.lastDispatchWasQuestion, barged: this.bargedBeforeDispatch };
7965
8170
  const fallback = (e) => {
7966
- log14.warn(`pickAck failed \u2014 using the default pools: ${e instanceof Error ? e.message : e}`);
8171
+ log15.warn(`pickAck failed \u2014 using the default pools: ${e instanceof Error ? e.message : e}`);
7967
8172
  pick.picked = false;
7968
8173
  return void 0;
7969
8174
  };
@@ -8053,7 +8258,7 @@ var VoiceEngine = class _VoiceEngine {
8053
8258
  this.ackProbe = null;
8054
8259
  const queued = this.uttQueue.splice(0);
8055
8260
  const droppedQueued = queued.length;
8056
- if (droppedQueued) log14.info(`barge-in cut ${droppedQueued} queued worker utterance(s) \u2014 handed to the host`);
8261
+ if (droppedQueued) log15.info(`barge-in cut ${droppedQueued} queued worker utterance(s) \u2014 handed to the host`);
8057
8262
  this.overlapKeptUntil = 0;
8058
8263
  if (!this.speaking && !this.drainTimer) {
8059
8264
  if (droppedQueued) this.lastInterrupted = { full: "", heard: "", queued, queuedTurn: false };
@@ -8282,7 +8487,7 @@ var VoiceEngine = class _VoiceEngine {
8282
8487
  if (flat && this.lastDispatchFlat && this.clock.now() - this.lastDispatchAt < _VoiceEngine.DUP_FINAL_MS && (this.lastDispatchFlat.startsWith(flat) || flat.startsWith(this.lastDispatchFlat))) return;
8283
8488
  this.specSpent = true;
8284
8489
  this.specText = this.specPartial;
8285
- log14.debug(`speculate: "${this.specText.slice(0, 60)}"`);
8490
+ log15.debug(`speculate: "${this.specText.slice(0, 60)}"`);
8286
8491
  this.diag("speculate", { text: this.specText });
8287
8492
  this.options.onSpeculate(this.specText);
8288
8493
  }, this.options.speculativeMs);
@@ -8351,7 +8556,7 @@ var VoiceEngine = class _VoiceEngine {
8351
8556
  };
8352
8557
  this.tts.newContext();
8353
8558
  this.tts.speak(phrase, false);
8354
- log14.debug(`backchannel: "${phrase}"`);
8559
+ log15.debug(`backchannel: "${phrase}"`);
8355
8560
  this.diag("backchannel", { phrase });
8356
8561
  this.options.onBackchannel(phrase);
8357
8562
  }
@@ -8384,7 +8589,7 @@ var VoiceEngine = class _VoiceEngine {
8384
8589
  });
8385
8590
  if (idx < 0) return text;
8386
8591
  toks.splice(idx, 1);
8387
- log14.verbose(`stripped backchannel echo "${this.lastBcPhrase}" from dispatching final`);
8592
+ log15.verbose(`stripped backchannel echo "${this.lastBcPhrase}" from dispatching final`);
8388
8593
  return toks.join(" ");
8389
8594
  }
8390
8595
  /** Merge a resumed utterance into the pending one, deduping any word-overlap. Soniox re-finalizes
@@ -8570,7 +8775,7 @@ var VoiceEngine = class _VoiceEngine {
8570
8775
  }
8571
8776
  const bargeHold = !this.looksIncomplete(this.pendingUtt) && this.unfinishedBargeLine(this.pendingUtt);
8572
8777
  if (this.options.incompleteMergeMs && (bargeHold || this.looksIncomplete(this.pendingUtt))) {
8573
- log14.verbose(`hold: incomplete utterance "${this.pendingUtt.slice(-40)}"`);
8778
+ log15.verbose(`hold: incomplete utterance "${this.pendingUtt.slice(-40)}"`);
8574
8779
  this.diag("hold", { reason: bargeHold ? "barge_unfinished" : "incomplete", tail: this.pendingUtt.slice(-40) });
8575
8780
  this.options.onHold();
8576
8781
  if (this.options.holdFiller && !this.speaking) this.speakFiller(this.options.holdFiller);
@@ -8609,13 +8814,13 @@ var VoiceEngine = class _VoiceEngine {
8609
8814
  const answered = this.answeredSinceDispatch();
8610
8815
  const part = flat !== this.lastDispatchFlat && !answered && this.echoesDispatch(text);
8611
8816
  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)`);
8817
+ log15.verbose(`dropped duplicate final "${text.slice(0, 40)}" (identical to the just-dispatched utterance, no reply between)`);
8613
8818
  this.diag("utterance_drop", { reason: "dup_final", text: text.slice(0, 60) });
8614
8819
  return;
8615
8820
  }
8616
8821
  const refinal = this.refinalizes(text);
8617
8822
  if (refinal && this.repliedSinceDispatch) {
8618
- log14.verbose(`dropped re-finalization "${text.slice(0, 60)}" (superset of the just-dispatched utterance, reply already flowing)`);
8823
+ log15.verbose(`dropped re-finalization "${text.slice(0, 60)}" (superset of the just-dispatched utterance, reply already flowing)`);
8619
8824
  this.diag("utterance_drop", { reason: "refinalized", text: text.slice(0, 60) });
8620
8825
  this.lastDispatchFlat = flat;
8621
8826
  this.lastDispatchWords = text.trim().split(/\s+/);
@@ -8894,7 +9099,7 @@ var SpokenSplitter = class {
8894
9099
  };
8895
9100
 
8896
9101
  // node_modules/@bod.ee/voice/src/adapters/soniox.ts
8897
- var log15 = forComponent2("SonioxSTT");
9102
+ var log16 = forComponent2("SonioxSTT");
8898
9103
  var now = () => performance.now();
8899
9104
  var SonioxSTTOptions = class {
8900
9105
  auth = "";
@@ -8939,7 +9144,7 @@ var SonioxSTT = class {
8939
9144
  this.onDiag({ t: now(), kind, ...fields });
8940
9145
  } catch (e) {
8941
9146
  this.diagOn = false;
8942
- log15.debug(`onDiag threw \u2014 STT diagnostics disabled: ${e instanceof Error ? e.message : e}`);
9147
+ log16.debug(`onDiag threw \u2014 STT diagnostics disabled: ${e instanceof Error ? e.message : e}`);
8943
9148
  }
8944
9149
  }
8945
9150
  lastChunkAt = 0;
@@ -8981,11 +9186,11 @@ var SonioxSTT = class {
8981
9186
  this.ws.onmessage = (ev) => this.handle(JSON.parse(String(ev.data)));
8982
9187
  this.ws.onclose = (ev) => {
8983
9188
  if (this.stopped) return;
8984
- log15.warn(`soniox ws closed (${ev.code} ${ev.reason || ""}) \u2014 reconnecting`);
9189
+ log16.warn(`soniox ws closed (${ev.code} ${ev.reason || ""}) \u2014 reconnecting`);
8985
9190
  this.diag("stt_ws_closed", { code: ev.code, reason: String(ev.reason || ""), reconnecting: true });
8986
9191
  this.reset();
8987
9192
  this.connectWs().catch((e) => {
8988
- log15.error(`soniox reconnect failed: ${e.message}`);
9193
+ log16.error(`soniox reconnect failed: ${e.message}`);
8989
9194
  this.diag("stt_reconnect_failed", { message: e.message });
8990
9195
  });
8991
9196
  };
@@ -8997,7 +9202,7 @@ var SonioxSTT = class {
8997
9202
  this.endpointTimer = setInterval(() => {
8998
9203
  const combined = (this.finalText + this.partialText).trim();
8999
9204
  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)}"`);
9205
+ if (this.firstTokenAt) log16.debug(`stt: ${Math.round(now() - this.firstTokenAt)}ms first-token\u2192silence-endpoint, "${combined.slice(0, 60)}"`);
9001
9206
  this.reset();
9002
9207
  this.onUtterance(combined, now());
9003
9208
  }, 120);
@@ -9009,7 +9214,7 @@ var SonioxSTT = class {
9009
9214
  if (this.stopped) return;
9010
9215
  const ref = this.lastChunkAt || this.startedChunksAt;
9011
9216
  if (now() - ref > noAudioMs) {
9012
- log15.error(`stt: no mic audio for >${Math.round(noAudioMs / 1e3)}s \u2014 capture device stopped delivering`);
9217
+ log16.error(`stt: no mic audio for >${Math.round(noAudioMs / 1e3)}s \u2014 capture device stopped delivering`);
9013
9218
  this.diag("stt_watchdog_fatal", { noAudioMs });
9014
9219
  this.onFatal("microphone stopped delivering audio (try a different input device, e.g. AirPods, or check System Settings \u2192 Sound \u2192 Input)");
9015
9220
  this.stop();
@@ -9032,7 +9237,7 @@ var SonioxSTT = class {
9032
9237
  handle(m) {
9033
9238
  if (m.error_message) {
9034
9239
  this.diag("stt_error", { message: String(m.error_message), code: m.error_code });
9035
- return log15.error(`soniox: ${m.error_message}`);
9240
+ return log16.error(`soniox: ${m.error_message}`);
9036
9241
  }
9037
9242
  let endpoint = false;
9038
9243
  for (const t of m.tokens ?? []) {
@@ -9049,7 +9254,7 @@ var SonioxSTT = class {
9049
9254
  this.onPartial(combined);
9050
9255
  if (endpoint && this.finalText.trim()) {
9051
9256
  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)}"`);
9257
+ if (this.firstTokenAt) log16.debug(`stt: ${Math.round(now() - this.firstTokenAt)}ms first-token\u2192endpoint, "${utterance.slice(0, 60)}"`);
9053
9258
  this.reset();
9054
9259
  this.onUtterance(utterance, now());
9055
9260
  }
@@ -9071,7 +9276,7 @@ var SonioxSTT = class {
9071
9276
  };
9072
9277
 
9073
9278
  // node_modules/@bod.ee/voice/src/adapters/cartesia.ts
9074
- var log16 = forComponent2("CartesiaTTS");
9279
+ var log17 = forComponent2("CartesiaTTS");
9075
9280
  var now2 = () => performance.now();
9076
9281
  var CartesiaTTSOptions = class {
9077
9282
  auth = "";
@@ -9107,7 +9312,7 @@ var CartesiaTTS = class _CartesiaTTS {
9107
9312
  this.onDiag({ t: now2(), kind, ...fields });
9108
9313
  } catch (e) {
9109
9314
  this.diagOn = false;
9110
- log16.debug(`onDiag threw \u2014 TTS diagnostics disabled: ${e instanceof Error ? e.message : e}`);
9315
+ log17.debug(`onDiag threw \u2014 TTS diagnostics disabled: ${e instanceof Error ? e.message : e}`);
9111
9316
  }
9112
9317
  }
9113
9318
  firstAudioAt = 0;
@@ -9142,11 +9347,11 @@ var CartesiaTTS = class _CartesiaTTS {
9142
9347
  this.ws.onerror = (e) => rej(new Error(`cartesia ws: ${e.message || "connect failed"}`));
9143
9348
  });
9144
9349
  this.ws.onclose = (ev) => {
9145
- log16.warn(`cartesia ws closed (${ev.code} ${ev.reason || ""})`);
9350
+ log17.warn(`cartesia ws closed (${ev.code} ${ev.reason || ""})`);
9146
9351
  this.diag("tts_ws_closed", { code: ev.code, reason: String(ev.reason || ""), reconnecting: !this.closed });
9147
9352
  if (!this.closed) {
9148
9353
  this.connecting = this.doConnect().catch((e) => {
9149
- log16.error(`cartesia reconnect failed: ${e.message}`);
9354
+ log17.error(`cartesia reconnect failed: ${e.message}`);
9150
9355
  this.diag("tts_reconnect_failed", { message: e.message });
9151
9356
  });
9152
9357
  }
@@ -9174,12 +9379,12 @@ var CartesiaTTS = class _CartesiaTTS {
9174
9379
  this.down = true;
9175
9380
  this.downAt = now2();
9176
9381
  this.consecutiveOk = 0;
9177
- log16.warn(`TTS circuit breaker open \u2014 ${this.consecutiveErrors} consecutive errors, switching to text-only`);
9382
+ log17.warn(`TTS circuit breaker open \u2014 ${this.consecutiveErrors} consecutive errors, switching to text-only`);
9178
9383
  this.diag("tts_breaker_open", { errors: this.consecutiveErrors });
9179
9384
  this.onDone();
9180
9385
  this.startProbe();
9181
9386
  } else if (!this.down) {
9182
- (/No valid transcripts/i.test(m.message || "") ? log16.debug : log16.warn)(`cartesia: ${JSON.stringify(m)}`);
9387
+ (/No valid transcripts/i.test(m.message || "") ? log17.debug : log17.warn)(`cartesia: ${JSON.stringify(m)}`);
9183
9388
  }
9184
9389
  }
9185
9390
  };
@@ -9194,7 +9399,7 @@ var CartesiaTTS = class _CartesiaTTS {
9194
9399
  this.stopProbe();
9195
9400
  const downMs = this.downAt ? now2() - this.downAt : 0;
9196
9401
  this.diag("tts_breaker_close", { downMs: Math.round(downMs) });
9197
- (downMs < 2e3 ? log16.debug : log16.info)(`TTS recovered${downMs ? ` (down ${downMs}ms)` : ""}`);
9402
+ (downMs < 2e3 ? log17.debug : log17.info)(`TTS recovered${downMs ? ` (down ${downMs}ms)` : ""}`);
9198
9403
  }
9199
9404
  /** Ensure the WS is open before sending — reconnects if idle-closed. */
9200
9405
  async ensureConnected() {
@@ -9277,13 +9482,13 @@ function base64ToBytes(b64) {
9277
9482
  }
9278
9483
 
9279
9484
  // node_modules/@bod.ee/voice/src/adapters/fish.ts
9280
- var log17 = forComponent2("FishTTS");
9485
+ var log18 = forComponent2("FishTTS");
9281
9486
 
9282
9487
  // node_modules/@bod.ee/voice/src/adapters/grok.ts
9283
- var log18 = forComponent2("GrokTTS");
9488
+ var log19 = forComponent2("GrokTTS");
9284
9489
 
9285
9490
  // node_modules/@bod.ee/voice/src/adapters/grok-stt.ts
9286
- var log19 = forComponent2("GrokSTT");
9491
+ var log20 = forComponent2("GrokSTT");
9287
9492
 
9288
9493
  // src/voice/engine.ts
9289
9494
  init_logging();
@@ -9299,7 +9504,7 @@ import { AIClient, DecideClient, JevAdapter, LlmDecideAdapter } from "ai.libx.js
9299
9504
 
9300
9505
  // src/voice/overlap.ts
9301
9506
  init_logging();
9302
- var log20 = forComponent("Overlap");
9507
+ var log21 = forComponent("Overlap");
9303
9508
  var norm2 = (s) => s.toLowerCase().replace(/[’']/g, "'").replace(/[^\p{L}\p{N}' -]+/gu, " ").replace(/\s+/g, " ").trim();
9304
9509
  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
9510
  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 +9584,8 @@ var OverlapJudge = class {
9379
9584
  const t0 = this.o.now();
9380
9585
  const done = (verdict, by) => {
9381
9586
  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`);
9587
+ if (by === "late" || by === "error" || by === "none") log21.warn(`overlap "${heard.slice(0, 60)}": no verdict (${by}, ${d.ms}ms) \u2192 cut`);
9588
+ else log21.debug(`overlap "${heard.slice(0, 60)}": ${verdict} by ${by} ${d.ms}ms`);
9384
9589
  this.o.onDecision(heard, d);
9385
9590
  return d;
9386
9591
  };
@@ -9397,7 +9602,7 @@ var OverlapJudge = class {
9397
9602
  });
9398
9603
  try {
9399
9604
  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}`);
9605
+ if (!ctl.signal.aborted) log21.warn(`overlap model failed: ${e.message}`);
9401
9606
  return "error";
9402
9607
  }), late]);
9403
9608
  if (v === "late" || v === "error") return done("cut", v);
@@ -9413,14 +9618,14 @@ function overlapModelFor(ai, env = process.env) {
9413
9618
  const m = fastOverlapModel(ai, model);
9414
9619
  const t0 = performance.now();
9415
9620
  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}`)
9621
+ () => log21.debug(`fast model warm in ${Math.round(performance.now() - t0)}ms`),
9622
+ (e) => log21.warn(`fast model warm-up failed (the first overlap verdict may be late): ${e?.message ?? e}`)
9418
9623
  );
9419
9624
  return m;
9420
9625
  }
9421
9626
 
9422
9627
  // src/voice/instinct.ts
9423
- var log21 = forComponent("Instinct");
9628
+ var log22 = forComponent("Instinct");
9424
9629
  var ACK_SITUATIONS = {
9425
9630
  hard_question: `a question that takes real thought, judgment or looking something up ("what's the biggest takeaway?", "how should I structure this?")`,
9426
9631
  simple_question: 'a quick question with an instant, obvious answer ("how are you?", "what time is it?", "can you hear me?")',
@@ -9470,7 +9675,7 @@ function interruptionNote(cut, intent) {
9470
9675
 
9471
9676
  // src/voice/unsaid.ts
9472
9677
  init_logging();
9473
- var log22 = forComponent("Unsaid");
9678
+ var log23 = forComponent("Unsaid");
9474
9679
  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
9680
  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
9681
  var SAID_OVERLAP = 0.6;
@@ -9510,7 +9715,7 @@ async function unsaidClauses(pieces, spoken, decider, timeoutMs = 5e3) {
9510
9715
  return p < CONVEYED_MIN;
9511
9716
  }).map((x) => x.c);
9512
9717
  } catch (e) {
9513
- log22.warn(`unsaid judge failed \u2014 only near-zero-overlap clauses count as unsaid: ${e.message}`);
9718
+ log23.warn(`unsaid judge failed \u2014 only near-zero-overlap clauses count as unsaid: ${e.message}`);
9514
9719
  return fallback();
9515
9720
  } finally {
9516
9721
  clearTimeout(timer);
@@ -9631,7 +9836,7 @@ var RESULT_CLAIM_RE = new RegExp(LEAD_TAGS + String.raw`(?:(?:all\s+)?done|finis
9631
9836
  var DONE_CLAIM_RE = new RegExp(LEAD_TAGS + String.raw`(?:(?:all\s+)?done|finished|it['’]?s\s+(?:done|finished)|completed?)\b`, "i");
9632
9837
 
9633
9838
  // src/voice/channelLang.ts
9634
- var log23 = forComponent("ChannelBanks");
9839
+ var log24 = forComponent("ChannelBanks");
9635
9840
  var LINE_KINDS = ["backchannel", "filler", "bridge"];
9636
9841
  var BANK_KEYS = [...LINE_KINDS, "phatic"];
9637
9842
  var EN_PHATIC = [
@@ -9785,11 +9990,11 @@ function cleanLine(s, lang, maxWords = 6) {
9785
9990
  const raw = s.trim().split("\n")[0];
9786
9991
  const l = raw.replace(/^[-*•\d.)\s]+/, "").replace(/^["'“”«»]+|["'“”«»]+$/g, "").replace(new RegExp("^\\p{L}+:\\s*", "u"), "").trim();
9787
9992
  if (!l || isLeakSuspect(raw) || isLeakSuspect(l)) {
9788
- if (l) log23.debug(`line "${raw}" reads as meta \u2014 rejected`);
9993
+ if (l) log24.debug(`line "${raw}" reads as meta \u2014 rejected`);
9789
9994
  return "";
9790
9995
  }
9791
9996
  if (lang && !fitsLanguage(l, lang)) {
9792
- log23.debug(`line "${l}" is not ${lang} \u2014 rejected`);
9997
+ log24.debug(`line "${l}" is not ${lang} \u2014 rejected`);
9793
9998
  return "";
9794
9999
  }
9795
10000
  return l.split(/\s+/).length > maxWords || l.length > 60 ? "" : l;
@@ -9803,7 +10008,7 @@ function validateBank(raw, lang, minPerKind = 3) {
9803
10008
  const seen = /* @__PURE__ */ new Set();
9804
10009
  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
10010
  if (out[k].length < minPerKind) {
9806
- log23.warn(`bank ${lang}: only ${out[k].length} valid ${k} lines \u2014 rejected`);
10011
+ log24.warn(`bank ${lang}: only ${out[k].length} valid ${k} lines \u2014 rejected`);
9807
10012
  return null;
9808
10013
  }
9809
10014
  }
@@ -9879,11 +10084,11 @@ var ChannelBanks = class {
9879
10084
  const f = this.file(lang);
9880
10085
  let cached = null;
9881
10086
  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}`);
10087
+ if (e.code !== "ENOENT") log24.warn(`bank cache ${f} unreadable \u2014 regenerating: ${e.message}`);
9883
10088
  return null;
9884
10089
  });
9885
10090
  if (cached && cached.v !== BANK_VERSION) {
9886
- log23.debug(`bank ${lang}: cache v${cached.v ?? 1} predates v${BANK_VERSION} \u2014 regenerating`);
10091
+ log24.debug(`bank ${lang}: cache v${cached.v ?? 1} predates v${BANK_VERSION} \u2014 regenerating`);
9887
10092
  cached = null;
9888
10093
  }
9889
10094
  const valid = cached && validateBank(cached.bank, lang, 0);
@@ -9891,7 +10096,7 @@ var ChannelBanks = class {
9891
10096
  if (valid) {
9892
10097
  for (const k of BANK_KEYS) if (!valid[k].length) valid[k] = [...this.o.fallback[lang]?.[k] ?? []];
9893
10098
  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"}`);
10099
+ log24.debug(`bank ${lang}: cache hit (${BANK_KEYS.map((k) => valid[k].length).join("/")})${complete ? "" : " \u2014 missing kinds, refreshing"}`);
9895
10100
  }
9896
10101
  if (complete && this.o.now() - cached.at < this.o.refreshMs) return;
9897
10102
  await this.generate(lang, valid);
@@ -9910,11 +10115,11 @@ var ChannelBanks = class {
9910
10115
  bank[k] = [...fresh[k], ...have?.[k] ?? []].filter((x) => !seen.has(norm3(x)) && seen.add(norm3(x))).slice(0, this.o.maxPerKind);
9911
10116
  }
9912
10117
  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`);
10118
+ log24.debug(`bank ${lang}: ${have ? "refreshed" : "generated"} (${BANK_KEYS.map((k) => bank[k].length).join("/")}) in ${Math.round(this.o.now() - t0)}ms`);
9914
10119
  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}`));
10120
+ 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
10121
  } 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}`);
10122
+ log24.warn(`bank ${lang}: generation failed \u2014 ${have ? "keeping the cached bank" : lang in this.o.fallback ? "fallback bank" : "silent"}: ${e.message}`);
9918
10123
  } finally {
9919
10124
  clearTimeout(t);
9920
10125
  if (!this.banks.has(lang)) setTimeout(() => this.pending.delete(lang), this.o.retryMs).unref?.();
@@ -10107,7 +10312,7 @@ var TurnGate = class {
10107
10312
  };
10108
10313
 
10109
10314
  // src/duplex.ts
10110
- var log24 = forComponent("DuplexAgent");
10315
+ var log25 = forComponent("DuplexAgent");
10111
10316
  function describeCall(call) {
10112
10317
  const v = call.args && Object.values(call.args).find((x) => typeof x === "string" && x.trim());
10113
10318
  const hint = v ? ` (${String(v).replace(/\s+/g, " ").trim().slice(0, 48)})` : "";
@@ -10442,7 +10647,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
10442
10647
  const m = this.reflexBuf.match(RESERVED_EVENT_MARKER) ?? this.reflexBuf.match(RESERVED_EVENT_OPENER);
10443
10648
  if (m) {
10444
10649
  this.fabricationCut = true;
10445
- log24.warn(`reflex fabricated a [task \u2026] event in its spoken stream \u2014 cutting it (kept ${m.index} chars)`);
10650
+ log25.warn(`reflex fabricated a [task \u2026] event in its spoken stream \u2014 cutting it (kept ${m.index} chars)`);
10446
10651
  this.reflexBuf = this.reflexBuf.slice(0, Math.max(m.index, this.reflexForwarded));
10447
10652
  this.pumpReflex(true);
10448
10653
  return;
@@ -10584,7 +10789,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
10584
10789
  this.leakHit = true;
10585
10790
  this.leakCuts.push(leak.trim().slice(0, 40));
10586
10791
  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`);
10792
+ log25.warn(`reflex ${why} ("${leak.trim().slice(0, 40)}\u2026") \u2014 cut before TTS`);
10588
10793
  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
10794
  const next = this.stepMarks.find((k) => k > at);
10590
10795
  if (next === void 0) {
@@ -10625,7 +10830,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
10625
10830
  this.notify("diag", "leak_judge", { text: s.slice(0, 80), p: Math.round(p * 100) / 100, ms: Math.round(performance.now() - t0) });
10626
10831
  return p >= LEAK_JUDGE_THRESHOLD;
10627
10832
  }, (e) => {
10628
- log24.warn(`leak judge failed \u2014 speaking the sentence: ${e.message}`);
10833
+ log25.warn(`leak judge failed \u2014 speaking the sentence: ${e.message}`);
10629
10834
  return false;
10630
10835
  }).then((leak) => {
10631
10836
  clearTimeout(timer);
@@ -10697,7 +10902,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
10697
10902
  let i = t.length - 1;
10698
10903
  while (i >= this.turnBase && !(t[i].role === "assistant" && typeof t[i].content === "string" && t[i].content.includes(key))) i--;
10699
10904
  if (i < this.turnBase) {
10700
- log24.warn("analysis leak cut, but its text was not found in the transcript to scrub");
10905
+ log25.warn("analysis leak cut, but its text was not found in the transcript to scrub");
10701
10906
  continue;
10702
10907
  }
10703
10908
  const m = t[i];
@@ -10754,7 +10959,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
10754
10959
  const res = await this.sendWithContext(content, context, note);
10755
10960
  const fb = this.options.reflexFallbackModel, model = this.voice.options.model;
10756
10961
  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}`);
10962
+ log25.warn(`reflex ${model} rate-limited \u2014 re-running the turn on ${fb}`);
10758
10963
  this.notify("diag", "reflex_fallback", { from: model, to: fb });
10759
10964
  this.voice.transcript.length = base;
10760
10965
  this.voice.options.model = fb;
@@ -10803,7 +11008,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
10803
11008
  try {
10804
11009
  await this.sendWithContext(reminder, this.lastContext);
10805
11010
  } catch (e) {
10806
- log24.warn(`follow-through nudge failed: ${e instanceof Error ? e.message : e}`);
11011
+ log25.warn(`follow-through nudge failed: ${e instanceof Error ? e.message : e}`);
10807
11012
  } finally {
10808
11013
  const i = this.voice.transcript.findIndex((m, k) => k >= base && m.role === "user" && m.content === reminder);
10809
11014
  if (i >= 0) this.voice.transcript.splice(i, 1);
@@ -10864,7 +11069,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
10864
11069
  return {
10865
11070
  preToolUse: (call) => {
10866
11071
  if (this.spec?.state === "pending" && !_DuplexAgent.SPEC_SAFE_TOOLS.has(call.name)) {
10867
- log24.verbose(`speculation aborted: reflex called ${call.name} on unconfirmed input`);
11072
+ log25.verbose(`speculation aborted: reflex called ${call.name} on unconfirmed input`);
10868
11073
  this.abortSpeculation();
10869
11074
  return { block: true, reason: "Speculative turn aborted." };
10870
11075
  }
@@ -10946,7 +11151,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
10946
11151
  try {
10947
11152
  await this.sendWithContext(reminder, this.lastContext);
10948
11153
  } catch (e) {
10949
- log24.warn(`ack nudge failed: ${e instanceof Error ? e.message : e}`);
11154
+ log25.warn(`ack nudge failed: ${e instanceof Error ? e.message : e}`);
10950
11155
  } finally {
10951
11156
  this.nudging = false;
10952
11157
  const i = this.voice.transcript.findIndex((m, k) => k >= base && m.role === "user" && m.content === reminder);
@@ -11002,7 +11207,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
11002
11207
  const accepted = this.offerOpen && this.parkedRedeliver.length > 0 && this.parkedRedeliver.every((p) => p.task === this.offer.task) && (isOfferAcceptance(content) || isTrivialBarge(content));
11003
11208
  const judge = accepted ? void 0 : this.options.bargeIntent;
11004
11209
  return (accepted ? Promise.resolve("resume") : judge ? judge(content).catch((e) => {
11005
- log24.warn(`bargeIntent judge failed \u2014 using the allowlist: ${e.message}`);
11210
+ log25.warn(`bargeIntent judge failed \u2014 using the allowlist: ${e.message}`);
11006
11211
  return fallback();
11007
11212
  }) : Promise.resolve(fallback())).then((intent) => {
11008
11213
  this.notify("diag", "barge_intent", { intent, text: content.slice(0, 80) });
@@ -11205,7 +11410,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
11205
11410
  try {
11206
11411
  res = await this.sendWithContext(spec.text, this.lastContext);
11207
11412
  } catch (e) {
11208
- log24.warn(`speculative turn failed: ${e instanceof Error ? e.message : e}`);
11413
+ log25.warn(`speculative turn failed: ${e instanceof Error ? e.message : e}`);
11209
11414
  } finally {
11210
11415
  this.voice.options.signal = prevSignal;
11211
11416
  }
@@ -11219,7 +11424,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
11219
11424
  if (d === "abort") {
11220
11425
  if (this.voice.transcript.length > base) this.voice.transcript.length = base;
11221
11426
  this.speculativeAbortedCalls++;
11222
- log24.verbose(`speculation aborted (${this.speculativeAbortedCalls} total): "${spec.text.slice(0, 50)}"`);
11427
+ log25.verbose(`speculation aborted (${this.speculativeAbortedCalls} total): "${spec.text.slice(0, 50)}"`);
11223
11428
  if (this.spec === spec) this.spec = void 0;
11224
11429
  return res ?? empty;
11225
11430
  }
@@ -11605,7 +11810,7 @@ Another agent just implemented the above. Independently check the CURRENT state
11605
11810
  this.notify("task_verify", `task ${id}: verifying`, { id });
11606
11811
  const cres = await new Agent(checkerOpts).run(checkBrief);
11607
11812
  if (cres.finishReason !== "stop") {
11608
- log24.warn(`task ${id}: verify inconclusive (${cres.finishReason})`);
11813
+ log25.warn(`task ${id}: verify inconclusive (${cres.finishReason})`);
11609
11814
  this.notify("task_verify", `task ${id}: verify inconclusive (${cres.finishReason})`, { id, finishReason: cres.finishReason });
11610
11815
  }
11611
11816
  const sum = (a = 0, b = 0) => a + b;
@@ -11741,7 +11946,7 @@ Another agent just implemented the above. Independently check the CURRENT state
11741
11946
  rec.status = "done";
11742
11947
  rec.result = res.text;
11743
11948
  const incomplete = res.finishReason !== "stop" && res.finishReason !== "empty";
11744
- log24.verbose(`task ${id} done (${res.steps} steps${incomplete ? `, INCOMPLETE: ${res.finishReason}` : ""})`);
11949
+ log25.verbose(`task ${id} done (${res.steps} steps${incomplete ? `, INCOMPLETE: ${res.finishReason}` : ""})`);
11745
11950
  this.notify("task_done", `task ${id} (${rec.label}) completed`, {
11746
11951
  id,
11747
11952
  text: res.text,
@@ -11775,14 +11980,14 @@ Another agent just implemented the above. Independently check the CURRENT state
11775
11980
  foldIfSuperseded(rec, note) {
11776
11981
  if (rec.deliveryParked !== "moved_on") return false;
11777
11982
  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`);
11983
+ log25.verbose(`task ${rec.id} settled but superseded (${rec.status}) \u2014 folded into transcript, not voiced`);
11779
11984
  return true;
11780
11985
  }
11781
11986
  failTask(rec, msg) {
11782
11987
  this.dropAsk(rec.id);
11783
11988
  rec.status = "error";
11784
11989
  rec.result = msg;
11785
- log24.warn(`task ${rec.id} failed: ${msg}`);
11990
+ log25.warn(`task ${rec.id} failed: ${msg}`);
11786
11991
  this.notify("task_error", `task ${rec.id} (${rec.label}) failed: ${msg}`, { id: rec.id });
11787
11992
  if (this.foldIfSuperseded(rec, `The "${rec.label}" task failed: ${msg}`)) return;
11788
11993
  this.dropParked(rec);
@@ -12086,7 +12291,7 @@ Another agent just implemented the above. Independently check the CURRENT state
12086
12291
  init_llm();
12087
12292
  init_logging();
12088
12293
  init_models();
12089
- var log25 = forComponent("ModelRouter");
12294
+ var log26 = forComponent("ModelRouter");
12090
12295
  var ModelRouterOptions = class {
12091
12296
  classifier = jevClassifier();
12092
12297
  /** Capability tiers → model id (any provider). basic/standard run the main session; premium is only ever an offload. */
@@ -12170,10 +12375,10 @@ var ModelRouter = class {
12170
12375
  if (tier === "premium") d.offload = this.state.offload && (c.sameThread ?? 0) >= o.sameThreadAt ? "continue" : "fresh";
12171
12376
  } catch (e) {
12172
12377
  const msg = e instanceof Error ? e.message : String(e);
12173
- log25.warn(`classifier failed, falling back to standard: ${msg}`);
12378
+ log26.warn(`classifier failed, falling back to standard: ${msg}`);
12174
12379
  d = { tier: "standard", model: o.models.standard, probs: {}, fallback: msg, classifyMs: Math.round(performance.now() - t0) };
12175
12380
  }
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`);
12381
+ 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
12382
  return d;
12178
12383
  }
12179
12384
  /** Run one user turn on `agent` with routing. Returns a RunResult carrying `costUsd` + `routing`. */
@@ -12225,7 +12430,7 @@ ${text}`;
12225
12430
  const answer = res.text || `(premium sub-agent finished with no reply; finishReason=${res.finishReason})`;
12226
12431
  agent.transcript.push({ role: "user", content }, { role: "assistant", content: answer });
12227
12432
  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`);
12433
+ log26.info(`offload ${cont ? "continue" : "fresh"} done \xB7 ${res.finishReason} \xB7 ${res.steps} steps`);
12229
12434
  const costUsd = o.costOf(main, briefUsage) + o.costOf(o.models.premium, res.usage);
12230
12435
  return { ...res, text: answer, messages: agent.transcript, usage: addUsage(briefUsage, res.usage), costUsd, routing: d };
12231
12436
  }
@@ -12241,7 +12446,7 @@ The request:
12241
12446
  ${text}`);
12242
12447
  return { text: r.text.trim() || text, usage: r.usage };
12243
12448
  } catch (e) {
12244
- log25.warn(`brief failed, sending the raw request: ${e instanceof Error ? e.message : String(e)}`);
12449
+ log26.warn(`brief failed, sending the raw request: ${e instanceof Error ? e.message : String(e)}`);
12245
12450
  return { text };
12246
12451
  } finally {
12247
12452
  agent.transcript = snap;
@@ -12286,7 +12491,7 @@ async function jevAvailable(opts = {}) {
12286
12491
  const { env } = await loadJev(opts);
12287
12492
  return JEV_KEYS.some((k) => env[k]?.trim());
12288
12493
  } catch (e) {
12289
- log25.debug(`jev unavailable: ${e instanceof Error ? e.message : String(e)}`);
12494
+ log26.debug(`jev unavailable: ${e instanceof Error ? e.message : String(e)}`);
12290
12495
  return false;
12291
12496
  }
12292
12497
  }
@@ -12571,7 +12776,7 @@ init_models();
12571
12776
 
12572
12777
  // src/voice/progress.ts
12573
12778
  init_logging();
12574
- var log26 = forComponent("Progress");
12779
+ var log27 = forComponent("Progress");
12575
12780
  var HE_RESULT_CLAIM = /(?:^|\s)(?:מצאתי|סיימתי|בוצע|הנה|אין)(?=\s|$|[.,!…])/u;
12576
12781
  var TOPICS = [
12577
12782
  [/toolsearch|^mcp__.*__(list_commands|call_info)$/, null],
@@ -12716,7 +12921,7 @@ var Progress = class {
12716
12921
  }, this.o.generateBudgetMs);
12717
12922
  });
12718
12923
  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}`);
12924
+ log27.warn(`progress line failed: ${e?.message ?? e}`);
12720
12925
  return null;
12721
12926
  }), late]).finally(() => clearTimeout(t));
12722
12927
  const l = cleanLine(raw ?? "", lang, 5);
@@ -12738,7 +12943,7 @@ init_logging();
12738
12943
 
12739
12944
  // src/voice/backchannelPlan.ts
12740
12945
  init_logging();
12741
- var log27 = forComponent("BackchannelPlan");
12946
+ var log28 = forComponent("BackchannelPlan");
12742
12947
  var silentTurn = (p) => p.density === "none" || p.turn === "short_command" || p.turn === "question";
12743
12948
  var cueWords = (s) => s.toLowerCase().replace(/[^\p{L}\p{N}'\s]+/gu, " ").split(/\s+/).filter(Boolean);
12744
12949
  function matchCue(plan, heard, from = 0) {
@@ -12756,7 +12961,7 @@ function matchCue(plan, heard, from = 0) {
12756
12961
  }
12757
12962
 
12758
12963
  // src/voice/secondChannel.ts
12759
- var log28 = forComponent("SecondChannel");
12964
+ var log29 = forComponent("SecondChannel");
12760
12965
  var CHANNEL_MODES = {
12761
12966
  turn: { kinds: ["bridge"], bridgeDelayMs: 1500 },
12762
12967
  normal: {},
@@ -12873,15 +13078,15 @@ function withBudget(p, ms, what, ac) {
12873
13078
  let t;
12874
13079
  const late = new Promise((r) => {
12875
13080
  t = setTimeout(() => {
12876
- log28.debug(`${what}: over the ${ms}ms budget \u2014 dropped`);
13081
+ log29.debug(`${what}: over the ${ms}ms budget \u2014 dropped`);
12877
13082
  ac?.abort();
12878
13083
  r(null);
12879
13084
  }, ms);
12880
13085
  });
12881
13086
  return Promise.race([p.catch((e) => {
12882
13087
  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}`);
13088
+ if (e?.name === "AbortError" || /aborted/i.test(msg)) log29.debug(`${what}: aborted after its budget`);
13089
+ else log29.warn(`${what} failed \u2014 falling back: ${msg}`);
12885
13090
  return null;
12886
13091
  }), late]).finally(() => clearTimeout(t));
12887
13092
  }
@@ -12960,7 +13165,7 @@ var SecondChannel = class {
12960
13165
  if (host) this.pinned = true;
12961
13166
  const lang = primaryLang(tag);
12962
13167
  if (!lang || lang === this.lang) return;
12963
- log28.debug(`language ${this.lang} \u2192 ${lang}`);
13168
+ log29.debug(`language ${this.lang} \u2192 ${lang}`);
12964
13169
  this.lang = lang;
12965
13170
  this.recent = [];
12966
13171
  this.picks.clear();
@@ -13105,7 +13310,7 @@ var SecondChannel = class {
13105
13310
  try {
13106
13311
  this.o.cancel();
13107
13312
  } catch (e) {
13108
- log28.warn(`cancel threw: ${e.message}`);
13313
+ log29.warn(`cancel threw: ${e.message}`);
13109
13314
  }
13110
13315
  }
13111
13316
  }
@@ -13217,7 +13422,7 @@ var SecondChannel = class {
13217
13422
  const opts = this.options;
13218
13423
  if (opts.mode === "chatty" && !opts.aec && !this.degradedLogged) {
13219
13424
  this.degradedLogged = true;
13220
- log28.info("chatty mode without echo cancellation \u2014 normal timing (our lines would re-trigger the mic)");
13425
+ log29.info("chatty mode without echo cancellation \u2014 normal timing (our lines would re-trigger the mic)");
13221
13426
  }
13222
13427
  const rule = this.rule(trigger, pauseMs, breath, cue);
13223
13428
  if (rule === "silence") {
@@ -13236,7 +13441,7 @@ var SecondChannel = class {
13236
13441
  const d = await withBudget(this.o.decide(this.tail(), { ...this.state(pauseMs), rule }, ac.signal), this.o.decisionBudgetMs, "channel decision", ac);
13237
13442
  choice = d?.choice;
13238
13443
  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`);
13444
+ else if (choice && choice in CHANNEL_ACTIONS) log29.debug(`decider chose ${choice} for a ${rule} moment \u2014 the rule stands`);
13240
13445
  }
13241
13446
  if (kind === "silence") return ev({ choice, acted: "silence" });
13242
13447
  if (stale()) return ev({ choice, kind, acted: "stale" });
@@ -13274,7 +13479,7 @@ var SecondChannel = class {
13274
13479
  void withBudget(this.o.generate(ctx, ac.signal), Math.max(this.o.generateBudgetMs, this.o.bridgeDelayMs), "bridge generate", ac).then((raw) => {
13275
13480
  const text = cleanLine(raw ?? "", ctx.lang);
13276
13481
  if (text && gen === this.gen) this.bridgeDraft = { gen, text };
13277
- }).catch((e) => log28.debug(`bridge prefetch failed: ${e?.message ?? e}`));
13482
+ }).catch((e) => log29.debug(`bridge prefetch failed: ${e?.message ?? e}`));
13278
13483
  }
13279
13484
  async line(kind, react = true) {
13280
13485
  if (kind === "bridge" && this.o.instantBridge) {
@@ -13290,7 +13495,7 @@ var SecondChannel = class {
13290
13495
  if (raw === null) ac.abort();
13291
13496
  const text = cleanLine(raw ?? "", this.lang);
13292
13497
  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`);
13498
+ if (text) log29.debug(`generated "${text}" repeats a recent line \u2014 canned`);
13294
13499
  }
13295
13500
  return this.pick(kind);
13296
13501
  }
@@ -13312,7 +13517,7 @@ var SecondChannel = class {
13312
13517
  try {
13313
13518
  r = this.o.speak(text, kind);
13314
13519
  } catch (e) {
13315
- log28.warn(`speak threw \u2014 line dropped: ${e.message}`);
13520
+ log29.warn(`speak threw \u2014 line dropped: ${e.message}`);
13316
13521
  return false;
13317
13522
  }
13318
13523
  if (r === false) return false;
@@ -13325,7 +13530,7 @@ var SecondChannel = class {
13325
13530
  this.o.show(text, kind);
13326
13531
  const p = Promise.resolve(r).then(() => {
13327
13532
  }, (e) => {
13328
- log28.warn(`channel line playback failed: ${e?.message ?? e}`);
13533
+ log29.warn(`channel line playback failed: ${e?.message ?? e}`);
13329
13534
  }).finally(() => {
13330
13535
  if (this.inflight === p) this.inflight = null;
13331
13536
  });
@@ -13383,7 +13588,7 @@ function closedQuestion(reply) {
13383
13588
  }
13384
13589
 
13385
13590
  // src/voice/call.ts
13386
- var log29 = forComponent("VoiceCall");
13591
+ var log30 = forComponent("VoiceCall");
13387
13592
  var VoiceCallOptions = class {
13388
13593
  /** Host runs MAIN with exactly `text` (a replace: roll the `replaces` turn back first). */
13389
13594
  sendTurn = () => {
@@ -13609,7 +13814,7 @@ var VoiceCall = class {
13609
13814
  try {
13610
13815
  this.o.cancel();
13611
13816
  } catch (e) {
13612
- log29.warn(`cancel threw: ${e.message}`);
13817
+ log30.warn(`cancel threw: ${e.message}`);
13613
13818
  }
13614
13819
  if (this.cur?.running) {
13615
13820
  this.cur.running = false;
@@ -13696,7 +13901,7 @@ var VoiceCall = class {
13696
13901
  try {
13697
13902
  r = this.o.speak(text, kind);
13698
13903
  } catch (e) {
13699
- log29.warn(`speak threw: ${e.message}`);
13904
+ log30.warn(`speak threw: ${e.message}`);
13700
13905
  return false;
13701
13906
  }
13702
13907
  if (r === false) return false;
@@ -13776,9 +13981,11 @@ var VoiceCall = class {
13776
13981
  init_llm();
13777
13982
  import { MemFilesystem as MemFilesystem3, IndexedDbFilesystem, CommandExecutor as CommandExecutor2, registerHeadlessCommands as registerHeadlessCommands2 } from "@livx.cc/wcli/core";
13778
13983
  export {
13984
+ ADVISE_LIMIT,
13779
13985
  Agent,
13780
13986
  AgentOptions,
13781
13987
  BodDbFilesystem,
13988
+ CC_WRITE_TOOLS,
13782
13989
  CartesiaTTS,
13783
13990
  CartesiaTTSOptions,
13784
13991
  CommandExecutor2 as CommandExecutor,
@@ -13806,6 +14013,9 @@ export {
13806
14013
  MountFilesystem,
13807
14014
  NodeDiskFilesystem,
13808
14015
  OverlayFilesystem,
14016
+ PLANNER_BLOCKED,
14017
+ PLANNER_REFUSAL,
14018
+ PLAN_FIRST,
13809
14019
  PROGRESS_TEMPLATES,
13810
14020
  PermissionOptions,
13811
14021
  PermissionPolicy,
@@ -13830,10 +14040,14 @@ export {
13830
14040
  VoiceCallOptions,
13831
14041
  VoiceEngine,
13832
14042
  VoiceEngineOptions,
14043
+ WRITE_TOOLS,
14044
+ advisorFor,
14045
+ advisorPrompt,
13833
14046
  applyEditsTool,
13834
14047
  askUserQuestionTool,
13835
14048
  bashTool,
13836
14049
  buildMcpCatalog,
14050
+ ccModeOptions,
13837
14051
  checkpointTool,
13838
14052
  checkpointTools,
13839
14053
  claudeCodeDisallowedTools,
@@ -13846,6 +14060,9 @@ export {
13846
14060
  cronMatches,
13847
14061
  decodeDdgUrl,
13848
14062
  defaultTools,
14063
+ delegatePrompt,
14064
+ delegateTools,
14065
+ delegateWorkerFor,
13849
14066
  diskAgentOptions,
13850
14067
  editTool,
13851
14068
  elideImages,
@@ -13885,6 +14102,7 @@ export {
13885
14102
  loadMemory,
13886
14103
  loadSkills,
13887
14104
  log,
14105
+ makeAdvisor,
13888
14106
  makeAskTool,
13889
14107
  makeContext,
13890
14108
  makeGlobTool,
@@ -13904,6 +14122,7 @@ export {
13904
14122
  messageHasImage,
13905
14123
  messageHasImageRef,
13906
14124
  messageHasInlineImage,
14125
+ meterWorkers,
13907
14126
  mintImageRef,
13908
14127
  mkdirp,
13909
14128
  modelShortLabel,
@@ -13914,6 +14133,7 @@ export {
13914
14133
  parseImageRef,
13915
14134
  pickTier,
13916
14135
  planMode,
14136
+ plannerBlocks,
13917
14137
  raceAttempts,
13918
14138
  readTool,
13919
14139
  reasoningToChatFragment,