agent.libx.js 0.93.25 → 0.93.27

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/cli.js CHANGED
@@ -1466,7 +1466,7 @@ function makeRealShellTool(options) {
1466
1466
  proc.stderr?.on("data", collect);
1467
1467
  proc.on("error", (err2) => {
1468
1468
  if (err2?.name === "AbortError" || ctl.signal.aborted) return finish(reasonFor(timedOut, timeoutMs, clean(out)));
1469
- log11.debug("shell spawn error", err2);
1469
+ log12.debug("shell spawn error", err2);
1470
1470
  finish(`[exit 1] ${err2?.message ?? err2}${out ? "\n" + clean(out) : ""}`);
1471
1471
  });
1472
1472
  proc.on("close", (code) => {
@@ -1528,14 +1528,14 @@ ${clean(out) || "(no output yet)"}`;
1528
1528
  }
1529
1529
  ];
1530
1530
  }
1531
- var log11, clean, SECRET_ENV_RE, _spawn, ShellJobRegistry, NO_JOB2;
1531
+ var log12, clean, SECRET_ENV_RE, _spawn, ShellJobRegistry, NO_JOB2;
1532
1532
  var init_tools_shell = __esm({
1533
1533
  "src/tools.shell.ts"() {
1534
1534
  "use strict";
1535
1535
  init_tools();
1536
1536
  init_redact();
1537
1537
  init_logging();
1538
- log11 = forComponent("shell");
1538
+ log12 = forComponent("shell");
1539
1539
  clean = (s) => truncateOutput(redactSecrets(s.replace(/\n+$/, "")));
1540
1540
  SECRET_ENV_RE = /(_API_KEY|_TOKEN|_SECRET|_PASSWORD|_PRIVATE_KEY|^AWS_|^GITHUB_TOKEN$|^OPENAI_|^ANTHROPIC_|^GOOGLE_|^GEMINI_|^GROQ_|^NPM_TOKEN$)/i;
1541
1541
  ShellJobRegistry = class {
@@ -3468,9 +3468,13 @@ init_tools_structured();
3468
3468
  init_todo();
3469
3469
  init_tools_web();
3470
3470
 
3471
+ // src/artifacts.ts
3472
+ init_logging();
3473
+ var log4 = forComponent("artifacts");
3474
+
3471
3475
  // src/lessons.ts
3472
3476
  init_logging();
3473
- var log4 = forComponent("Lessons");
3477
+ var log5 = forComponent("Lessons");
3474
3478
  var LessonOptionsDefaults = class {
3475
3479
  minRepeats = 2;
3476
3480
  };
@@ -3495,15 +3499,15 @@ function lessonCapture(options) {
3495
3499
  counts.set(lesson.slug, n);
3496
3500
  if (n < o.minRepeats) return;
3497
3501
  written.add(lesson.slug);
3498
- await writeFact(o.fs, o.dir, lesson.slug, lesson.body).catch((e) => log4.warn(`could not persist ${lesson.slug}: ${e?.message ?? e}`));
3499
- log4.debug(`captured lesson ${lesson.slug} (recurred ${n}\xD7)`);
3502
+ await writeFact(o.fs, o.dir, lesson.slug, lesson.body).catch((e) => log5.warn(`could not persist ${lesson.slug}: ${e?.message ?? e}`));
3503
+ log5.debug(`captured lesson ${lesson.slug} (recurred ${n}\xD7)`);
3500
3504
  }
3501
3505
  };
3502
3506
  }
3503
3507
 
3504
3508
  // src/reflect.ts
3505
3509
  init_logging();
3506
- var log5 = forComponent("Reflect");
3510
+ var log6 = forComponent("Reflect");
3507
3511
  async function reflectOnRun(o) {
3508
3512
  const digest = digestRun(o.result.messages, o.maxDigestChars ?? 6e3);
3509
3513
  if (!digest.trim()) return null;
@@ -3519,7 +3523,7 @@ If the run was fine or the issue was purely task-specific (not generalizable), r
3519
3523
  const r = await o.ai.chat({ model: o.model, messages: [{ role: "user", content: prompt }], stream: false });
3520
3524
  text = r?.content ?? "";
3521
3525
  } catch (e) {
3522
- log5.warn(`reflection call failed: ${e?.message ?? e}`);
3526
+ log6.warn(`reflection call failed: ${e?.message ?? e}`);
3523
3527
  return null;
3524
3528
  }
3525
3529
  const m = text.match(/LESSON:\s*(.+)/i);
@@ -3529,10 +3533,10 @@ If the run was fine or the issue was purely task-specific (not generalizable), r
3529
3533
  try {
3530
3534
  await writeFact(o.fs, o.dir, slug, lesson);
3531
3535
  } catch (e) {
3532
- log5.warn(`could not persist lesson: ${e?.message ?? e}`);
3536
+ log6.warn(`could not persist lesson: ${e?.message ?? e}`);
3533
3537
  return null;
3534
3538
  }
3535
- log5.debug(`reflection persisted ${slug}`);
3539
+ log6.debug(`reflection persisted ${slug}`);
3536
3540
  return slug;
3537
3541
  }
3538
3542
  function digestRun(messages, maxChars) {
@@ -3549,7 +3553,7 @@ function digestRun(messages, maxChars) {
3549
3553
  // src/duplex.ts
3550
3554
  import { MemFilesystem as MemFilesystem2 } from "@livx.cc/wcli/core";
3551
3555
  init_logging();
3552
- var log6 = forComponent("DuplexAgent");
3556
+ var log7 = forComponent("DuplexAgent");
3553
3557
  function describeCall(call) {
3554
3558
  const v = call.args && Object.values(call.args).find((x) => typeof x === "string" && x.trim());
3555
3559
  const hint = v ? ` (${String(v).replace(/\s+/g, " ").trim().slice(0, 48)})` : "";
@@ -3603,7 +3607,7 @@ var DuplexAgentOptions = class {
3603
3607
  /** User-scope memory dir for global facts (type=user/feedback). Forwarded to Remember's routing. */
3604
3608
  memoryUserDir;
3605
3609
  };
3606
- var VOICE_SYSTEM_PROMPT = 'You are a spoken voice assistant \u2014 the user HEARS everything you say. Use short sentences. One idea per sentence. No markdown, no bullet lists, no code blocks, no headings, no emoji.\nKeep turns SHORT \u2014 one to three sentences, then stop. Never lecture, enumerate cases, or add caveats unprompted. Conversation is a fast exchange: give the one thing asked, and let the user pull more if they want it.\nYou have three cognitive tiers \u2014 like a human brain:\n\u2022 YOU (reflex) \u2014 instant, lightweight. Handle greetings, simple questions, status checks, QuickLook.\n\u2022 `Act` \u2014 your hands. A background worker with its own configured tools and access to the user\'s environment (files and shell{{WORKER_WEB}}). Use for reading, editing, searching, running tasks, building \u2014 any real work.\n{{THINK_SLOT}}\nWhen you are unsure whether you can do or access something, do NOT assume and do NOT claim a capability you have not confirmed. To check what you can do, QuickLook `capabilities` (instant \u2014 it lists your worker\'s real tools) and answer from that. Never promise an ability that is not in your capabilities; if it is not there, tell the user plainly you can\'t. To actually DO real work, call `Act`. When the user mentions their project, folder, files, or environment ("this project", "the current folder", "my code"), call `Act` IMMEDIATELY \u2014 do not ask for paths or details the worker can discover itself. Never pretend to have done the work or invent results \u2014 the worker\'s report is your only source.\nALWAYS react before you work: the FIRST thing in your turn is a brief spoken acknowledgement of what you heard and what you are about to do ("got it \u2014 opening that now", "sure, let me pull it up", "okay, checking"). NEVER call a tool (Act, Think, QuickLook) silently \u2014 the user must hear you react before you go quiet to work. After dispatching Act or Think, that same one short sentence IS your turn \u2014 end it and do not wait for the result.\nResults arrive later as events like "[task t1 completed] \u2026" or "[task t1 failed] \u2026". When one arrives, summarize it for the ear in one or two short sentences. "[task t1 progress] \u2026" events are interim status, NOT results \u2014 give at most a half-sentence aside ("still on it \u2014 running tests now") and end your turn. Never present progress as a finished result.\nCRITICAL: while a task is still running you have NO answer yet \u2014 never state a specific result of any kind (a number, size, count, name, path, or value). The real answer arrives ONLY in the "[task \u2026 completed]" event; inventing one meanwhile (a made-up disk size, commit count, etc.) is a serious error. Until then, only acknowledge and wait.\nNever read raw file paths, diffs, or code aloud verbatim.\n"[task t1 asks] \u2026" events are QUESTIONS from a background task \u2014 relay to the user in your own words, short, then end your turn. When the user answers, call `AnswerTask` with that id and their answer. NEVER answer on the user\'s behalf for permissions or risky operations; if their reply is ambiguous, confirm first.\nIf the user\'s message sounds INCOMPLETE \u2014 trailing off mid-sentence, a fragment that needs more context ("and then we", "but the problem is"), hesitation fillers ("uh", "um") \u2014 call `Hold` instead of answering. This keeps listening for the rest of their thought. Only respond with substance when you have a complete question or request.\nDispatch discipline: send ONE self-contained task per request \u2014 a single worker with the full brief beats several workers with fragments (each worker starts fresh and re-discovers context). NEVER dispatch a worker just to read files or gather information \u2014 workers explore and discover context themselves; pass on what you already know and let one worker do the whole job. Split into parallel tasks only when the user asks for genuinely independent things. When a task completes, report its result and stop \u2014 do NOT dispatch follow-up work (verification, polish, extras) the user did not ask for, unless the report itself signals failure or doubt.\nDo not fire a second Act/Think for work already in flight, and NEVER spawn a second task to re-count, cross-check, or verify a result a worker already gave you \u2014 trust its answer; a single question gets ONE task. Call `TaskStatus` at most ONCE per turn; if a task is still running, just say "still on it" and end the turn \u2014 never poll it again and again in a loop. Use `CancelTask` when the user asks to stop something.\nPRIORITY: when the user says goodbye or wants to end/finish/wrap up the session ("ok bye", "that\'s all", "let\'s finish", "let\'s end", "goodnight", "exit", "wrap up"), call `ExitSession` IMMEDIATELY \u2014 do not act, do not check status, just exit.\nFor TRIVIAL instant lookups only \u2014 current time, git branch, listing a folder, peeking at a small file, or checking your own `capabilities`/tools \u2014 use `QuickLook` (instant, no task). Whenever the user asks what you can do or whether you have some ability, QuickLook `capabilities` and answer from that \u2014 never guess. Anything requiring searching, reasoning, running commands, or editing goes through `Act`.\n{{MEMORY_SLOT}}\nUser messages may arrive via speech-to-text and can carry transcription artifacts \u2014 odd words, cut-offs, homophones ("for you" vs "folder"). Read for INTENT, not surface text. If a message seems garbled or surprising, briefly confirm what they meant ("did you mean\u2026?") instead of answering the literal words.';
3610
+ var VOICE_SYSTEM_PROMPT = 'You are a spoken voice assistant \u2014 the user HEARS everything you say. Use short sentences. One idea per sentence. No markdown, no bullet lists, no code blocks, no headings, no emoji.\nKeep turns SHORT \u2014 one to three sentences, then stop. Never lecture, enumerate cases, or add caveats unprompted. Conversation is a fast exchange: give the one thing asked, and let the user pull more if they want it.\nYou have three cognitive tiers \u2014 like a human brain:\n\u2022 YOU (reflex) \u2014 instant, lightweight. Handle greetings, simple questions, status checks, QuickLook.\n\u2022 `Act` \u2014 your hands. A background worker with its own configured tools and access to the user\'s environment (files and shell{{WORKER_WEB}}). Use for reading, editing, searching, running tasks, building \u2014 any real work.\n{{THINK_SLOT}}\nWhen you are unsure whether you can do or access something, do NOT assume and do NOT claim a capability you have not confirmed. To check what you can do, QuickLook `capabilities` (instant \u2014 it lists your worker\'s real tools) and answer from that. Never promise an ability that is not in your capabilities; if it is not there, tell the user plainly you can\'t. To actually DO real work, call `Act`. When the user mentions their project, folder, files, or environment ("this project", "the current folder", "my code"), call `Act` IMMEDIATELY \u2014 do not ask for paths or details the worker can discover itself. Never pretend to have done the work or invent results \u2014 the worker\'s report is your only source.\nALWAYS react before you work: the FIRST thing in your turn is a brief spoken acknowledgement of what you heard and what you are about to do ("got it \u2014 opening that now", "sure, let me pull it up", "okay, checking"). NEVER call a tool (Act, Think, QuickLook) silently \u2014 the user must hear you react before you go quiet to work. After dispatching Act or Think, that same one short sentence IS your turn \u2014 end it and do not wait for the result.\nResults arrive later as events like "[task t1 completed] \u2026" or "[task t1 failed] \u2026". When one arrives, speak the USEFUL gist in one or two short sentences \u2014 the actual answer the user wanted (the headline finding, the key numbers), not the thinnest possible "it\'s done". A forecast \u2192 say it\'s calm AND that it\'s good for swimming but not surf; a count \u2192 say the number. Be brief, but do not drop the substance. If the result is a LIST (search results, multiple files/matches), the user CANNOT see it \u2014 there is no screen and no numbered menu to point at. Speak the gist: say what you found and name the top one or two by NAME (the source, not "the first one" or a number), then ask plainly if they want more. Never ask them to "pick which one" or reference items by position. The completed result stays in YOUR context \u2014 it is yours to draw on. When the user follows up ("tell me more", "what else", "and?"), answer FROM that result first: you already have the detail, so elaborate on what you have. Do NOT spawn a fresh worker to re-search or re-gather what you were just handed. Re-dispatch ONLY when genuinely new information is needed \u2014 e.g. the user wants the full contents of a SPECIFIC source, which is one WebFetch of that URL, not a brand-new search. "[task t1 progress] \u2026" events are interim status, NOT results \u2014 give at most a half-sentence aside ("still on it \u2014 running tests now") and end your turn. Never present progress as a finished result.\nCRITICAL: while a task is still running you have NO answer yet \u2014 never state a specific result of any kind (a number, size, count, name, path, or value). The real answer arrives ONLY in the "[task \u2026 completed]" event; inventing one meanwhile (a made-up disk size, commit count, etc.) is a serious error. Until then, only acknowledge and wait.\nNever read raw file paths, diffs, or code aloud verbatim.\n"[task t1 asks] \u2026" events are QUESTIONS from a background task \u2014 relay to the user in your own words, short, then end your turn. When the user answers, call `AnswerTask` with that id and their answer. NEVER answer on the user\'s behalf for permissions or risky operations; if their reply is ambiguous, confirm first.\nIf the user\'s message sounds INCOMPLETE \u2014 trailing off mid-sentence, a fragment that needs more context ("and then we", "but the problem is"), hesitation fillers ("uh", "um") \u2014 call `Hold` instead of answering. This keeps listening for the rest of their thought. Only respond with substance when you have a complete question or request.\nDispatch discipline: send ONE self-contained task per request \u2014 a single worker with the full brief beats several workers with fragments (each worker starts fresh and re-discovers context). NEVER dispatch a worker just to read files or gather information \u2014 workers explore and discover context themselves; pass on what you already know and let one worker do the whole job. Split into parallel tasks only when the user asks for genuinely independent things. When a task completes, report its result and stop \u2014 do NOT dispatch follow-up work (verification, polish, extras) the user did not ask for, unless the report itself signals failure or doubt.\nDo not fire a second Act/Think for work already in flight, and NEVER spawn a second task to re-count, cross-check, or verify a result a worker already gave you \u2014 trust its answer; a single question gets ONE task. Call `TaskStatus` at most ONCE per turn; if a task is still running, just say "still on it" and end the turn \u2014 never poll it again and again in a loop. Use `CancelTask` when the user asks to stop something.\nPRIORITY: when the user says goodbye or wants to end/finish/wrap up the session ("ok bye", "that\'s all", "let\'s finish", "let\'s end", "goodnight", "exit", "wrap up"), call `ExitSession` IMMEDIATELY \u2014 do not act, do not check status, just exit.\nFor TRIVIAL instant lookups only \u2014 current time, git branch, listing a folder, peeking at a small file, or checking your own `capabilities`/tools \u2014 use `QuickLook` (instant, no task). Whenever the user asks what you can do or whether you have some ability, QuickLook `capabilities` and answer from that \u2014 never guess. Anything requiring searching, reasoning, running commands, or editing goes through `Act`.\n{{MEMORY_SLOT}}\nUser messages may arrive via speech-to-text and can carry transcription artifacts \u2014 odd words, cut-offs, homophones ("for you" vs "folder"). Read for INTENT, not surface text. If a message seems garbled or surprising, briefly confirm what they meant ("did you mean\u2026?") instead of answering the literal words.';
3607
3611
  var THINK_GUIDANCE = "\u2022 `Think` \u2014 your brain. A premium reasoning model, FAR more expensive than Act. Reserve it for open-ended architecture/design questions, or a problem Act already FAILED at. ALL implementation work \u2014 coding, refactoring, debugging, edge cases, tests \u2014 goes to Act; Act is highly capable. Never send the same work to both.";
3608
3612
  var THINK_DISABLED_GUIDANCE = "(Think tier is not available \u2014 use Act for all escalations.)";
3609
3613
  var VOICE_STYLE_CONVERSATIONAL = `Speak like a person in a live conversation, not an assistant reading a script. React first, then deliver: a quick impulsive beat ("oh nice", "hmm, hold on", "ah, got it") before the substance. Use contractions always. Vary sentence length \u2014 some very short. Light fillers and backchannels are fine ("mm-hm", "right", "let's see") but at most one per reply \u2014 never stack them. When you escalate to Act or Think, say it like a human would ("hang on, let me actually dig into that \u2014 gimme a minute") instead of announcing a task. When a result comes back, react to it like you just found out ("okay so \u2014 turns out\u2026"). Match the user's energy: a quick question gets a quick answer \u2014 a few words is a perfectly good turn. Prefer a short answer plus an offer ("want the details?") over covering everything. Never narrate your own mechanics (no "I will now act", no task ids out loud).`;
@@ -3726,7 +3730,7 @@ Today's date: ${(/* @__PURE__ */ new Date()).toDateString()}.`;
3726
3730
  try {
3727
3731
  await this.voice.send("[reminder] You dispatched a task but said nothing to the user. Say ONE short spoken acknowledgement now \u2014 no tools.");
3728
3732
  } catch (e) {
3729
- log6.warn(`ack nudge failed: ${e instanceof Error ? e.message : e}`);
3733
+ log7.warn(`ack nudge failed: ${e instanceof Error ? e.message : e}`);
3730
3734
  } finally {
3731
3735
  this.nudging = false;
3732
3736
  }
@@ -3849,7 +3853,7 @@ Another agent just implemented the above. Independently check the CURRENT state
3849
3853
  this.notify("task_verify", `task ${id}: verifying`, { id });
3850
3854
  const cres = await new Agent(agentOpts).run(checkBrief);
3851
3855
  if (cres.finishReason !== "stop") {
3852
- log6.warn(`task ${id}: verify inconclusive (${cres.finishReason})`);
3856
+ log7.warn(`task ${id}: verify inconclusive (${cres.finishReason})`);
3853
3857
  this.notify("task_verify", `task ${id}: verify inconclusive (${cres.finishReason})`, { id, finishReason: cres.finishReason });
3854
3858
  }
3855
3859
  const sum = (a = 0, b = 0) => a + b;
@@ -3951,7 +3955,7 @@ Another agent just implemented the above. Independently check the CURRENT state
3951
3955
  return this.failTask(rec, msg);
3952
3956
  }
3953
3957
  rec.status = "done";
3954
- log6.verbose(`task ${id} done (${res.steps} steps)`);
3958
+ log7.verbose(`task ${id} done (${res.steps} steps)`);
3955
3959
  this.notify("task_done", `task ${id} (${rec.label}) completed`, {
3956
3960
  id,
3957
3961
  text: res.text,
@@ -3968,7 +3972,7 @@ Another agent just implemented the above. Independently check the CURRENT state
3968
3972
  failTask(rec, msg) {
3969
3973
  this.dropAsk(rec.id);
3970
3974
  rec.status = "error";
3971
- log6.warn(`task ${rec.id} failed: ${msg}`);
3975
+ log7.warn(`task ${rec.id} failed: ${msg}`);
3972
3976
  this.notify("task_error", `task ${rec.id} (${rec.label}) failed: ${msg}`);
3973
3977
  this.queueRevoice(`[task ${rec.id} failed] ${msg}`);
3974
3978
  }
@@ -4207,7 +4211,7 @@ init_logging();
4207
4211
 
4208
4212
  // src/voice/engine.ts
4209
4213
  init_logging();
4210
- var log7 = forComponent("VoiceEngine");
4214
+ var log8 = forComponent("VoiceEngine");
4211
4215
  var now = () => performance.now();
4212
4216
  var VoiceEngineOptions = class {
4213
4217
  stt;
@@ -4314,7 +4318,7 @@ var VoiceEngine = class _VoiceEngine {
4314
4318
  this.stt.onLevel = (rms) => this.handleLevel(rms);
4315
4319
  await Promise.all([this.tts.connect(), this.stt.start()]);
4316
4320
  this.setState("listening");
4317
- log7.debug(`voice I/O up (${this.stt.usingAec ? "AEC" : "heuristic echo"} capture)`);
4321
+ log8.debug(`voice I/O up (${this.stt.usingAec ? "AEC" : "heuristic echo"} capture)`);
4318
4322
  }
4319
4323
  get usingAec() {
4320
4324
  return this.stt.usingAec;
@@ -4366,7 +4370,7 @@ var VoiceEngine = class _VoiceEngine {
4366
4370
  this.reply += text;
4367
4371
  for (const w of this.words(this.reply)) this.echoWords.add(w);
4368
4372
  this.tts.speak(text, true);
4369
- if (!this.spokeDeltas && this.turnStartAt) log7.debug(`ttft: ${Math.round(now() - this.turnStartAt)}ms`);
4373
+ if (!this.spokeDeltas && this.turnStartAt) log8.debug(`ttft: ${Math.round(now() - this.turnStartAt)}ms`);
4370
4374
  this.spokeDeltas = true;
4371
4375
  this.setState("speaking");
4372
4376
  }
@@ -4387,7 +4391,7 @@ var VoiceEngine = class _VoiceEngine {
4387
4391
  }
4388
4392
  this.drainTimer = null;
4389
4393
  this.speaking = false;
4390
- if (this.turnStartAt) log7.debug(`turn: ${Math.round(now() - this.turnStartAt)}ms (incl. playback)`);
4394
+ if (this.turnStartAt) log8.debug(`turn: ${Math.round(now() - this.turnStartAt)}ms (incl. playback)`);
4391
4395
  this.echoUntil = now() + 2500;
4392
4396
  if (!this.usingAec) this.stt.reset();
4393
4397
  this.setState("listening");
@@ -4528,7 +4532,7 @@ var VoiceEngine = class _VoiceEngine {
4528
4532
  this.pendingUtt = this.pendingUtt ? `${this.pendingUtt} ${text}` : text;
4529
4533
  if (this.pendingTimer) clearTimeout(this.pendingTimer);
4530
4534
  if (this.options.incompleteMergeMs && this.looksIncomplete(this.pendingUtt)) {
4531
- log7.verbose(`hold: incomplete utterance "${this.pendingUtt.slice(-40)}"`);
4535
+ log8.verbose(`hold: incomplete utterance "${this.pendingUtt.slice(-40)}"`);
4532
4536
  this.options.onHold();
4533
4537
  if (this.options.holdFiller && !this.speaking) {
4534
4538
  this.beginSpeech();
@@ -4626,7 +4630,7 @@ async function resolveAuth(auth) {
4626
4630
  }
4627
4631
 
4628
4632
  // src/voice/soniox.ts
4629
- var log8 = forComponent("SonioxSTT");
4633
+ var log9 = forComponent("SonioxSTT");
4630
4634
  var now2 = () => performance.now();
4631
4635
  var SonioxSTTOptions = class {
4632
4636
  auth = "";
@@ -4683,9 +4687,9 @@ var SonioxSTT = class {
4683
4687
  this.ws.onmessage = (ev) => this.handle(JSON.parse(String(ev.data)));
4684
4688
  this.ws.onclose = (ev) => {
4685
4689
  if (this.stopped) return;
4686
- log8.warn(`soniox ws closed (${ev.code} ${ev.reason || ""}) \u2014 reconnecting`);
4690
+ log9.warn(`soniox ws closed (${ev.code} ${ev.reason || ""}) \u2014 reconnecting`);
4687
4691
  this.reset();
4688
- this.connectWs().catch((e) => log8.error(`soniox reconnect failed: ${e.message}`));
4692
+ this.connectWs().catch((e) => log9.error(`soniox reconnect failed: ${e.message}`));
4689
4693
  };
4690
4694
  }
4691
4695
  async start() {
@@ -4695,7 +4699,7 @@ var SonioxSTT = class {
4695
4699
  this.endpointTimer = setInterval(() => {
4696
4700
  const combined = (this.finalText + this.partialText).trim();
4697
4701
  if (!combined || now2() - this.lastChangeAt < this.options.silenceEndpointMs) return;
4698
- if (this.firstTokenAt) log8.debug(`stt: ${Math.round(now2() - this.firstTokenAt)}ms first-token\u2192silence-endpoint, "${combined.slice(0, 60)}"`);
4702
+ if (this.firstTokenAt) log9.debug(`stt: ${Math.round(now2() - this.firstTokenAt)}ms first-token\u2192silence-endpoint, "${combined.slice(0, 60)}"`);
4699
4703
  this.reset();
4700
4704
  this.onUtterance(combined, now2());
4701
4705
  }, 120);
@@ -4712,7 +4716,7 @@ var SonioxSTT = class {
4712
4716
  });
4713
4717
  }
4714
4718
  handle(m) {
4715
- if (m.error_message) return log8.error(`soniox: ${m.error_message}`);
4719
+ if (m.error_message) return log9.error(`soniox: ${m.error_message}`);
4716
4720
  let endpoint = false;
4717
4721
  for (const t of m.tokens ?? []) {
4718
4722
  if (t.text === "<end>") endpoint = true;
@@ -4728,7 +4732,7 @@ var SonioxSTT = class {
4728
4732
  this.onPartial(combined);
4729
4733
  if (endpoint && this.finalText.trim()) {
4730
4734
  const utterance = this.finalText.trim();
4731
- if (this.firstTokenAt) log8.debug(`stt: ${Math.round(now2() - this.firstTokenAt)}ms first-token\u2192endpoint, "${utterance.slice(0, 60)}"`);
4735
+ if (this.firstTokenAt) log9.debug(`stt: ${Math.round(now2() - this.firstTokenAt)}ms first-token\u2192endpoint, "${utterance.slice(0, 60)}"`);
4732
4736
  this.reset();
4733
4737
  this.onUtterance(utterance, now2());
4734
4738
  }
@@ -4750,7 +4754,7 @@ var SonioxSTT = class {
4750
4754
 
4751
4755
  // src/voice/cartesia.ts
4752
4756
  init_logging();
4753
- var log9 = forComponent("CartesiaTTS");
4757
+ var log10 = forComponent("CartesiaTTS");
4754
4758
  var now3 = () => performance.now();
4755
4759
  var CartesiaTTSOptions = class {
4756
4760
  auth = "";
@@ -4795,9 +4799,9 @@ var CartesiaTTS = class _CartesiaTTS {
4795
4799
  this.ws.onerror = (e) => rej(new Error(`cartesia ws: ${e.message || "connect failed"}`));
4796
4800
  });
4797
4801
  this.ws.onclose = (ev) => {
4798
- log9.warn(`cartesia ws closed (${ev.code} ${ev.reason || ""})`);
4802
+ log10.warn(`cartesia ws closed (${ev.code} ${ev.reason || ""})`);
4799
4803
  if (!this.closed) {
4800
- this.connecting = this.doConnect().catch((e) => log9.error(`cartesia reconnect failed: ${e.message}`));
4804
+ this.connecting = this.doConnect().catch((e) => log10.error(`cartesia reconnect failed: ${e.message}`));
4801
4805
  }
4802
4806
  };
4803
4807
  this.ws.onmessage = (ev) => {
@@ -4807,7 +4811,7 @@ var CartesiaTTS = class _CartesiaTTS {
4807
4811
  this.consecutiveErrors = 0;
4808
4812
  if (this.down) {
4809
4813
  this.down = false;
4810
- log9.info("TTS recovered");
4814
+ log10.info("TTS recovered");
4811
4815
  this.stopProbe();
4812
4816
  }
4813
4817
  if (!this.firstAudioAt) this.firstAudioAt = now3();
@@ -4816,7 +4820,7 @@ var CartesiaTTS = class _CartesiaTTS {
4816
4820
  this.consecutiveErrors = 0;
4817
4821
  if (this.down) {
4818
4822
  this.down = false;
4819
- log9.info("TTS recovered");
4823
+ log10.info("TTS recovered");
4820
4824
  this.stopProbe();
4821
4825
  }
4822
4826
  this.onDone();
@@ -4825,11 +4829,11 @@ var CartesiaTTS = class _CartesiaTTS {
4825
4829
  this.consecutiveErrors++;
4826
4830
  if (!this.down && this.consecutiveErrors >= _CartesiaTTS.CB_THRESHOLD) {
4827
4831
  this.down = true;
4828
- log9.warn(`TTS circuit breaker open \u2014 ${this.consecutiveErrors} consecutive errors, switching to text-only`);
4832
+ log10.warn(`TTS circuit breaker open \u2014 ${this.consecutiveErrors} consecutive errors, switching to text-only`);
4829
4833
  this.onDone();
4830
4834
  this.startProbe();
4831
4835
  } else if (!this.down) {
4832
- log9.warn(`cartesia: ${JSON.stringify(m)}`);
4836
+ log10.warn(`cartesia: ${JSON.stringify(m)}`);
4833
4837
  }
4834
4838
  }
4835
4839
  };
@@ -4910,7 +4914,7 @@ import { MemFilesystem as MemFilesystem3, IndexedDbFilesystem, CommandExecutor a
4910
4914
  init_logging();
4911
4915
  import { spawn } from "child_process";
4912
4916
  import { createHash } from "crypto";
4913
- var log10 = forComponent("mcp");
4917
+ var log11 = forComponent("mcp");
4914
4918
  var PROTOCOL_VERSION = "2025-06-18";
4915
4919
  var DEFAULT_TIMEOUT_MS = 3e4;
4916
4920
  var StdioTransport = class {
@@ -4929,7 +4933,7 @@ var StdioTransport = class {
4929
4933
  proc.stdout.setEncoding("utf8");
4930
4934
  proc.stdout.on("data", (chunk) => this.onData(chunk));
4931
4935
  proc.stderr.setEncoding("utf8");
4932
- proc.stderr.on("data", (chunk) => log10.debug(`[${command}] stderr:`, chunk.trimEnd()));
4936
+ proc.stderr.on("data", (chunk) => log11.debug(`[${command}] stderr:`, chunk.trimEnd()));
4933
4937
  proc.on("exit", (code) => this.failAll(new Error(`MCP server "${command}" exited (code ${code})`)));
4934
4938
  proc.on("error", (e) => this.failAll(e instanceof Error ? e : new Error(String(e))));
4935
4939
  }
@@ -4943,7 +4947,7 @@ var StdioTransport = class {
4943
4947
  try {
4944
4948
  this.dispatch(JSON.parse(line));
4945
4949
  } catch (e) {
4946
- log10.debug("dropping non-JSON line from MCP server:", line, e);
4950
+ log11.debug("dropping non-JSON line from MCP server:", line, e);
4947
4951
  }
4948
4952
  }
4949
4953
  }
@@ -4992,7 +4996,7 @@ var StdioTransport = class {
4992
4996
  try {
4993
4997
  this.proc?.stdin?.end();
4994
4998
  } catch (e) {
4995
- log10.debug("stdin end failed", e);
4999
+ log11.debug("stdin end failed", e);
4996
5000
  }
4997
5001
  this.proc?.kill();
4998
5002
  }
@@ -5061,7 +5065,7 @@ function parseSseResponse(body) {
5061
5065
  const obj = JSON.parse(trimmed.slice(5).trim());
5062
5066
  if (obj && (obj.result !== void 0 || obj.error !== void 0)) return obj;
5063
5067
  } catch (e) {
5064
- log10.debug("skipping unparseable SSE data line", e);
5068
+ log11.debug("skipping unparseable SSE data line", e);
5065
5069
  }
5066
5070
  }
5067
5071
  return {};
@@ -5129,7 +5133,7 @@ async function mountWithDeadline(name, cfg, mountTimeoutMs) {
5129
5133
  return { name, client, tools, specs, serverInfo: init?.serverInfo };
5130
5134
  })(), mountTimeoutMs, name);
5131
5135
  } catch (e) {
5132
- await client.close().catch((err2) => log10.debug(`close after failed mount of "${name}": ${err2}`));
5136
+ await client.close().catch((err2) => log11.debug(`close after failed mount of "${name}": ${err2}`));
5133
5137
  throw e;
5134
5138
  }
5135
5139
  }
@@ -5140,15 +5144,15 @@ function validEntries(servers) {
5140
5144
  return Object.entries(servers).filter(([name, cfg]) => {
5141
5145
  if (!cfg || cfg.disabled) return false;
5142
5146
  if (!cfg.command && !cfg.url) {
5143
- log10.warn(`MCP server "${name}" needs a command (stdio) or url (http) \u2014 skipping`);
5147
+ log11.warn(`MCP server "${name}" needs a command (stdio) or url (http) \u2014 skipping`);
5144
5148
  return false;
5145
5149
  }
5146
5150
  return true;
5147
5151
  });
5148
5152
  }
5149
5153
  function logMountFailure(name, e) {
5150
- if (e instanceof McpAuthError) log10.warn(`MCP "${name}" needs-auth: HTTP ${e.status} \u2014 set bearerToken or headers in its config; skipping`);
5151
- else log10.error(`MCP server "${name}" failed to mount: ${e?.message ?? e}`);
5154
+ if (e instanceof McpAuthError) log11.warn(`MCP "${name}" needs-auth: HTTP ${e.status} \u2014 set bearerToken or headers in its config; skipping`);
5155
+ else log11.error(`MCP server "${name}" failed to mount: ${e?.message ?? e}`);
5152
5156
  }
5153
5157
  async function mountMcpServers(servers = {}, opts = {}) {
5154
5158
  const entries = validEntries(servers);
@@ -5158,7 +5162,7 @@ async function mountMcpServers(servers = {}, opts = {}) {
5158
5162
  const name = entries[i][0];
5159
5163
  if (r.status === "fulfilled") {
5160
5164
  out.push(r.value);
5161
- log10.info(`MCP "${name}" mounted \u2014 ${r.value.tools.length} tool(s)${r.value.serverInfo?.name ? ` from ${r.value.serverInfo.name}` : ""}`);
5165
+ log11.info(`MCP "${name}" mounted \u2014 ${r.value.tools.length} tool(s)${r.value.serverInfo?.name ? ` from ${r.value.serverInfo.name}` : ""}`);
5162
5166
  } else logMountFailure(name, r.reason);
5163
5167
  });
5164
5168
  return out;
@@ -5198,7 +5202,7 @@ var McpPool = class {
5198
5202
  const prev = this.warm.get(key);
5199
5203
  if (prev) {
5200
5204
  clearTimeout(prev.timer);
5201
- if (prev.client !== client) void prev.client.close().catch((err2) => log10.debug(`warm-pool replace close failed: ${err2}`));
5205
+ if (prev.client !== client) void prev.client.close().catch((err2) => log11.debug(`warm-pool replace close failed: ${err2}`));
5202
5206
  }
5203
5207
  const e = { client, timer: void 0 };
5204
5208
  this.warm.set(key, e);
@@ -5215,7 +5219,7 @@ var McpPool = class {
5215
5219
  const e = this.warm.get(key);
5216
5220
  if (!e) return;
5217
5221
  this.warm.delete(key);
5218
- await e.client.close().catch((err2) => log10.debug(`warm-pool evict close failed: ${err2}`));
5222
+ await e.client.close().catch((err2) => log11.debug(`warm-pool evict close failed: ${err2}`));
5219
5223
  }
5220
5224
  async closeAll() {
5221
5225
  for (const e of this.warm.values()) {
@@ -5644,7 +5648,7 @@ import { existsSync as existsSync3, mkdirSync as mkdirSync3, statSync as statSyn
5644
5648
  import { homedir as homedir2 } from "os";
5645
5649
  import { dirname as dirname3, join as join4 } from "path";
5646
5650
  import { fileURLToPath } from "url";
5647
- var log12 = forComponent("VoiceIO");
5651
+ var log13 = forComponent("VoiceIO");
5648
5652
  var now4 = () => performance.now();
5649
5653
  var Player = class {
5650
5654
  proc = null;
@@ -5658,7 +5662,7 @@ var Player = class {
5658
5662
  ["-loglevel", "quiet", "-nodisp", "-fflags", "nobuffer", "-flags", "low_delay", "-probesize", "32", "-f", "s16le", "-ar", String(TTS_SAMPLE_RATE), "-ch_layout", "mono", "-i", "-"],
5659
5663
  { stdio: ["pipe", "ignore", "ignore"] }
5660
5664
  );
5661
- this.proc.on("error", (e) => log12.warn(`ffplay error: ${e.message}`));
5665
+ this.proc.on("error", (e) => log13.warn(`ffplay error: ${e.message}`));
5662
5666
  this.proc.stdin.on("error", () => {
5663
5667
  });
5664
5668
  this.bytesWritten = 0;
@@ -5693,7 +5697,7 @@ function detectFfmpegMic() {
5693
5697
  const devices = [...audio.matchAll(/\[(\d+)\] (.+)/g)].map(([, idx, name]) => ({ idx, name: name.trim() }));
5694
5698
  const mic = devices.find((d) => /microphone|built-in/i.test(d.name) && !/teams|blackhole|loopback/i.test(d.name)) ?? devices[0];
5695
5699
  if (!mic) throw new Error("no audio input device found");
5696
- log12.debug(`ffmpeg mic: [${mic.idx}] ${mic.name}`);
5700
+ log13.debug(`ffmpeg mic: [${mic.idx}] ${mic.name}`);
5697
5701
  return `:${mic.idx}`;
5698
5702
  }
5699
5703
  function resolveAecBinary() {
@@ -5706,15 +5710,15 @@ function resolveAecBinary() {
5706
5710
  if (existsSync3(bin) && statSync2(bin).mtimeMs >= statSync2(src).mtimeMs) return bin;
5707
5711
  if (spawnSync("which", ["swiftc"]).status !== 0) return null;
5708
5712
  mkdirSync3(cacheDir, { recursive: true });
5709
- log12.info("compiling AEC mic helper (first run)\u2026");
5713
+ log13.info("compiling AEC mic helper (first run)\u2026");
5710
5714
  const build = spawnSync("swiftc", ["-O", "-o", bin, src, "-Xlinker", "-sectcreate", "-Xlinker", "__TEXT", "-Xlinker", "__info_plist", "-Xlinker", plist], { encoding: "utf8" });
5711
5715
  if (build.status !== 0) {
5712
- log12.warn(`AEC build failed: ${build.stderr?.slice(0, 400)}`);
5716
+ log13.warn(`AEC build failed: ${build.stderr?.slice(0, 400)}`);
5713
5717
  return null;
5714
5718
  }
5715
5719
  const sign = spawnSync("codesign", ["-fs", "-", bin], { encoding: "utf8" });
5716
5720
  if (sign.status !== 0) {
5717
- log12.warn(`codesign failed: ${sign.stderr?.slice(0, 200)}`);
5721
+ log13.warn(`codesign failed: ${sign.stderr?.slice(0, 200)}`);
5718
5722
  return null;
5719
5723
  }
5720
5724
  return bin;
@@ -5733,16 +5737,16 @@ var NodeMicSource = class {
5733
5737
  this.proc = spawn2(this.bin, [], { stdio: ["ignore", "pipe", "ignore"] });
5734
5738
  } else {
5735
5739
  if (spawnSync("which", ["ffmpeg"]).status !== 0) throw new Error("voice I/O unavailable: no AEC helper and no ffmpeg on PATH");
5736
- log12.info("mic: raw capture (no AEC) \u2014 echo handled heuristically; headphones recommended");
5740
+ log13.info("mic: raw capture (no AEC) \u2014 echo handled heuristically; headphones recommended");
5737
5741
  this.proc = spawn2(
5738
5742
  "ffmpeg",
5739
5743
  ["-loglevel", "error", "-f", "avfoundation", "-i", detectFfmpegMic(), "-ar", String(STT_SAMPLE_RATE), "-ac", "1", "-f", "s16le", "-"],
5740
5744
  { stdio: ["ignore", "pipe", "pipe"] }
5741
5745
  );
5742
- this.proc.stderr.on("data", (d) => log12.warn(`ffmpeg: ${String(d).trim()}`));
5746
+ this.proc.stderr.on("data", (d) => log13.warn(`ffmpeg: ${String(d).trim()}`));
5743
5747
  }
5744
5748
  this.proc.on("exit", (c) => {
5745
- if (c && !this.stopped) log12.error(`mic capture exited (${c}) \u2014 check mic permission / MIC_DEVICE / MIC_AEC=0`);
5749
+ if (c && !this.stopped) log13.error(`mic capture exited (${c}) \u2014 check mic permission / MIC_DEVICE / MIC_AEC=0`);
5746
5750
  });
5747
5751
  this.proc.stdout.on("data", (chunk) => onChunk(chunk));
5748
5752
  }
@@ -5776,11 +5780,11 @@ var AecDuplexAudio = class {
5776
5780
  this.proc.stdin.on("error", () => {
5777
5781
  });
5778
5782
  this.proc.on("exit", (c) => {
5779
- if (c && !this.stopped) log12.error(`aec duplex audio exited (${c}) \u2014 check mic permission / MIC_AEC=0`);
5783
+ if (c && !this.stopped) log13.error(`aec duplex audio exited (${c}) \u2014 check mic permission / MIC_AEC=0`);
5780
5784
  });
5781
5785
  this.proc.stdout.on("data", (chunk) => onChunk(chunk));
5782
5786
  this.proc.stderr.on("data", (d) => {
5783
- for (const ln of String(d).split("\n")) if (ln.trim()) log12.debug(`mic-aec: ${ln.trim()}`);
5787
+ for (const ln of String(d).split("\n")) if (ln.trim()) log13.debug(`mic-aec: ${ln.trim()}`);
5784
5788
  });
5785
5789
  }
5786
5790
  stop() {
@@ -5953,7 +5957,7 @@ async function loadConfig(cwd) {
5953
5957
 
5954
5958
  // cli/hooks-config.ts
5955
5959
  import { spawnSync as spawnSync2 } from "child_process";
5956
- var log13 = forComponent("hooks");
5960
+ var log14 = forComponent("hooks");
5957
5961
  var escapeRegex = (s) => s.replace(/[.+^${}()|[\]\\]/g, "\\$&");
5958
5962
  function ruleMatches(rule, toolName) {
5959
5963
  if (!rule.tool || rule.tool === "*") return true;
@@ -5970,7 +5974,7 @@ function runCmd(rule, env) {
5970
5974
  });
5971
5975
  return { code: r.status ?? 1, out: ((r.stdout ?? "") + (r.stderr ?? "")).trim() };
5972
5976
  } catch (e) {
5973
- log13.debug(`hook command failed: ${rule.command}`, e);
5977
+ log14.debug(`hook command failed: ${rule.command}`, e);
5974
5978
  return { code: 1, out: String(e?.message ?? e) };
5975
5979
  }
5976
5980
  }
@@ -6076,7 +6080,7 @@ function formatDiff(ops, opts = {}) {
6076
6080
  // cli/session.ts
6077
6081
  import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync3, readdirSync, renameSync } from "fs";
6078
6082
  import { join as join6 } from "path";
6079
- var log14 = forComponent("session");
6083
+ var log15 = forComponent("session");
6080
6084
  var SessionStore = class {
6081
6085
  dir;
6082
6086
  constructor(cwd) {
@@ -6102,7 +6106,7 @@ var SessionStore = class {
6102
6106
  }
6103
6107
  load(id) {
6104
6108
  if (!this.safeId(id)) {
6105
- log14.debug(`rejecting unsafe session id: ${id}`);
6109
+ log15.debug(`rejecting unsafe session id: ${id}`);
6106
6110
  return void 0;
6107
6111
  }
6108
6112
  const path = join6(this.dir, `${id}.json`);
@@ -6110,7 +6114,7 @@ var SessionStore = class {
6110
6114
  try {
6111
6115
  return JSON.parse(readFileSync3(path, "utf8"));
6112
6116
  } catch (e) {
6113
- log14.debug(`unreadable session ${id} \u2014 ignoring`, e);
6117
+ log15.debug(`unreadable session ${id} \u2014 ignoring`, e);
6114
6118
  return void 0;
6115
6119
  }
6116
6120
  }
@@ -6123,7 +6127,7 @@ var SessionStore = class {
6123
6127
  try {
6124
6128
  metas.push(JSON.parse(readFileSync3(join6(this.dir, f), "utf8")).meta);
6125
6129
  } catch (e) {
6126
- log14.debug(`skipping unreadable session file ${f}`, e);
6130
+ log15.debug(`skipping unreadable session file ${f}`, e);
6127
6131
  }
6128
6132
  }
6129
6133
  return metas.sort((a, b) => b.updated - a.updated);
@@ -6217,7 +6221,7 @@ import { execFile } from "child_process";
6217
6221
  import { promisify } from "util";
6218
6222
  import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, existsSync as existsSync6 } from "fs";
6219
6223
  import { join as join7, resolve as resolve2, sep as sep2 } from "path";
6220
- var log15 = forComponent("checkpoints");
6224
+ var log16 = forComponent("checkpoints");
6221
6225
  var exec = promisify(execFile);
6222
6226
  var DEFAULT_EXCLUDE = [".agent/", ".git/", "node_modules/", "dist/", "build/", ".next/", "target/", ".venv/", "__pycache__/", "*.log"];
6223
6227
  var ShadowRepo = class {
@@ -6251,7 +6255,7 @@ var ShadowRepo = class {
6251
6255
  writeFileSync4(join7(this.gitDir, "info", "exclude"), this.exclude.join("\n") + "\n");
6252
6256
  this.ready = true;
6253
6257
  } catch (e) {
6254
- log15.debug(`git checkpoints unavailable for ${this.workTree}`, e);
6258
+ log16.debug(`git checkpoints unavailable for ${this.workTree}`, e);
6255
6259
  this.ready = false;
6256
6260
  }
6257
6261
  return this.ready;
@@ -6314,7 +6318,7 @@ var ShadowRepo = class {
6314
6318
  await this.run("gc", "--auto", "-q").catch(() => {
6315
6319
  });
6316
6320
  } catch (e) {
6317
- log15.debug("checkpoint prune failed", e);
6321
+ log16.debug("checkpoint prune failed", e);
6318
6322
  }
6319
6323
  }
6320
6324
  };
@@ -6371,7 +6375,7 @@ var GitCheckpoints = class {
6371
6375
  use(sessionId) {
6372
6376
  if (sessionId === this.session) return;
6373
6377
  this.session = sessionId;
6374
- if (this.started) for (const r of this.repos) void r.point(this.ref()).catch((e) => log15.debug("re-point failed", e));
6378
+ if (this.started) for (const r of this.repos) void r.point(this.ref()).catch((e) => log16.debug("re-point failed", e));
6375
6379
  }
6376
6380
  async begin(label) {
6377
6381
  if (!await this.start()) return;
@@ -6382,7 +6386,7 @@ var GitCheckpoints = class {
6382
6386
  try {
6383
6387
  await r.commit(msg);
6384
6388
  } catch (e) {
6385
- log15.debug("checkpoint commit failed", e);
6389
+ log16.debug("checkpoint commit failed", e);
6386
6390
  }
6387
6391
  }
6388
6392
  if (slow) clearTimeout(slow);
@@ -7759,7 +7763,7 @@ var red = C("31");
7759
7763
  var bold = C("1");
7760
7764
  var yellow = C("33");
7761
7765
  var err = (s) => process.stderr.write(s);
7762
- var log16 = forComponent("cli");
7766
+ var log17 = forComponent("cli");
7763
7767
  var VERSION = (() => {
7764
7768
  try {
7765
7769
  return JSON.parse(readFileSync5(new URL("../package.json", import.meta.url), "utf8")).version ?? "?";
@@ -8193,7 +8197,7 @@ function costOf(pricing, promptTokens = 0, completionTokens = 0, cacheCreationTo
8193
8197
  function turnCost(model, usage) {
8194
8198
  return costOf(getModelInfo(model)?.pricing, usage?.promptTokens ?? 0, usage?.completionTokens ?? 0, usage?.cacheCreationTokens ?? 0, usage?.cacheReadTokens ?? 0, model);
8195
8199
  }
8196
- async function evaluateGoal(ai, condition, transcript, log17) {
8200
+ async function evaluateGoal(ai, condition, transcript, log18) {
8197
8201
  const recent = transcript.filter((m) => m.role === "assistant").slice(-8).map((m) => {
8198
8202
  const text = typeof m.content === "string" ? m.content : m.content.filter((p) => p.type === "text").map((p) => p.text).join(" ");
8199
8203
  return text.slice(0, 600);
@@ -8213,7 +8217,7 @@ ${recent}` }
8213
8217
  const match = r.content.match(/\{[\s\S]*\}/);
8214
8218
  if (match) return JSON.parse(match[0]);
8215
8219
  } catch (e) {
8216
- log17(dim(` (goal evaluator error: ${e?.message ?? e})
8220
+ log18(dim(` (goal evaluator error: ${e?.message ?? e})
8217
8221
  `));
8218
8222
  }
8219
8223
  return { met: false, reason: "evaluation unclear" };
@@ -8396,7 +8400,7 @@ async function mountMcp(cfg, oauth) {
8396
8400
  return mounted;
8397
8401
  }
8398
8402
  async function closeMcp(mounted) {
8399
- await Promise.all(mounted.map((m) => m.client.close().catch((e) => log16.debug("mcp close failed", e))));
8403
+ await Promise.all(mounted.map((m) => m.client.close().catch((e) => log17.debug("mcp close failed", e))));
8400
8404
  }
8401
8405
  var IMG_EXT = { ".png": "image/png", ".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".gif": "image/gif", ".webp": "image/webp" };
8402
8406
  function readImageParts(cwd, line) {
@@ -8628,14 +8632,14 @@ var isCancelTeardown = (e) => {
8628
8632
  function installCancelGuards(mounted) {
8629
8633
  process.on("unhandledRejection", (e) => {
8630
8634
  if (isCancelTeardown(e)) {
8631
- log16.debug("suppressed unhandledRejection (cursor stream cancel)", e);
8635
+ log17.debug("suppressed unhandledRejection (cursor stream cancel)", e);
8632
8636
  return;
8633
8637
  }
8634
- log16.error("unhandledRejection", e);
8638
+ log17.error("unhandledRejection", e);
8635
8639
  });
8636
8640
  process.on("uncaughtException", (e) => {
8637
8641
  if (isCancelTeardown(e)) {
8638
- log16.debug("suppressed uncaughtException (cursor stream cancel)", e);
8642
+ log17.debug("suppressed uncaughtException (cursor stream cancel)", e);
8639
8643
  return;
8640
8644
  }
8641
8645
  console.error(e);
@@ -8928,7 +8932,7 @@ async function repl(args, ai, cfg, cwd) {
8928
8932
  mkdirSync7(join9(cwd, ".agent"), { recursive: true });
8929
8933
  appendFileSync(histPath, line + "\n");
8930
8934
  } catch (e) {
8931
- log16.debug("history write failed", e);
8935
+ log17.debug("history write failed", e);
8932
8936
  }
8933
8937
  };
8934
8938
  const ago = (t) => {
@@ -8993,7 +8997,7 @@ async function repl(args, ai, cfg, cwd) {
8993
8997
  try {
8994
8998
  store.save(session);
8995
8999
  } catch (e) {
8996
- log16.debug("session save after rewind failed", e);
9000
+ log17.debug("session save after rewind failed", e);
8997
9001
  }
8998
9002
  err(green(" \u27F2 jumped back") + dim(` \u2014 ${face.transcript.length} message(s) kept; edit + resend
8999
9003
  `));
@@ -9540,7 +9544,7 @@ ${extra}` : body);
9540
9544
  }
9541
9545
  const m = mounted.splice(idx, 1)[0];
9542
9546
  removeWorkTools(m.tools.map((t) => t.name));
9543
- await m.client.close().catch((e) => log16.debug("mcp close failed", e));
9547
+ await m.client.close().catch((e) => log17.debug("mcp close failed", e));
9544
9548
  err(dim(` removed "${name}"
9545
9549
  `));
9546
9550
  return;
@@ -9724,7 +9728,7 @@ ${extra}` : body);
9724
9728
  try {
9725
9729
  return readdirSync2(join9(cwd, absDir.replace(/^\/+/, "")), { withFileTypes: true }).map((d) => ({ name: d.name, dir: d.isDirectory() }));
9726
9730
  } catch (e) {
9727
- log16.debug("completion readdir failed", absDir, e);
9731
+ log17.debug("completion readdir failed", absDir, e);
9728
9732
  return null;
9729
9733
  }
9730
9734
  };