agent.libx.js 0.93.26 → 0.93.28

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,15 @@ init_tools_structured();
3468
3468
  init_todo();
3469
3469
  init_tools_web();
3470
3470
 
3471
+ // src/scratch.ts
3472
+ init_tools();
3473
+ init_tools_structured();
3474
+ init_logging();
3475
+ var log4 = forComponent("scratch");
3476
+
3471
3477
  // src/lessons.ts
3472
3478
  init_logging();
3473
- var log4 = forComponent("Lessons");
3479
+ var log5 = forComponent("Lessons");
3474
3480
  var LessonOptionsDefaults = class {
3475
3481
  minRepeats = 2;
3476
3482
  };
@@ -3495,15 +3501,15 @@ function lessonCapture(options) {
3495
3501
  counts.set(lesson.slug, n);
3496
3502
  if (n < o.minRepeats) return;
3497
3503
  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)`);
3504
+ await writeFact(o.fs, o.dir, lesson.slug, lesson.body).catch((e) => log5.warn(`could not persist ${lesson.slug}: ${e?.message ?? e}`));
3505
+ log5.debug(`captured lesson ${lesson.slug} (recurred ${n}\xD7)`);
3500
3506
  }
3501
3507
  };
3502
3508
  }
3503
3509
 
3504
3510
  // src/reflect.ts
3505
3511
  init_logging();
3506
- var log5 = forComponent("Reflect");
3512
+ var log6 = forComponent("Reflect");
3507
3513
  async function reflectOnRun(o) {
3508
3514
  const digest = digestRun(o.result.messages, o.maxDigestChars ?? 6e3);
3509
3515
  if (!digest.trim()) return null;
@@ -3519,7 +3525,7 @@ If the run was fine or the issue was purely task-specific (not generalizable), r
3519
3525
  const r = await o.ai.chat({ model: o.model, messages: [{ role: "user", content: prompt }], stream: false });
3520
3526
  text = r?.content ?? "";
3521
3527
  } catch (e) {
3522
- log5.warn(`reflection call failed: ${e?.message ?? e}`);
3528
+ log6.warn(`reflection call failed: ${e?.message ?? e}`);
3523
3529
  return null;
3524
3530
  }
3525
3531
  const m = text.match(/LESSON:\s*(.+)/i);
@@ -3529,10 +3535,10 @@ If the run was fine or the issue was purely task-specific (not generalizable), r
3529
3535
  try {
3530
3536
  await writeFact(o.fs, o.dir, slug, lesson);
3531
3537
  } catch (e) {
3532
- log5.warn(`could not persist lesson: ${e?.message ?? e}`);
3538
+ log6.warn(`could not persist lesson: ${e?.message ?? e}`);
3533
3539
  return null;
3534
3540
  }
3535
- log5.debug(`reflection persisted ${slug}`);
3541
+ log6.debug(`reflection persisted ${slug}`);
3536
3542
  return slug;
3537
3543
  }
3538
3544
  function digestRun(messages, maxChars) {
@@ -3549,7 +3555,7 @@ function digestRun(messages, maxChars) {
3549
3555
  // src/duplex.ts
3550
3556
  import { MemFilesystem as MemFilesystem2 } from "@livx.cc/wcli/core";
3551
3557
  init_logging();
3552
- var log6 = forComponent("DuplexAgent");
3558
+ var log7 = forComponent("DuplexAgent");
3553
3559
  function describeCall(call) {
3554
3560
  const v = call.args && Object.values(call.args).find((x) => typeof x === "string" && x.trim());
3555
3561
  const hint = v ? ` (${String(v).replace(/\s+/g, " ").trim().slice(0, 48)})` : "";
@@ -3603,7 +3609,7 @@ var DuplexAgentOptions = class {
3603
3609
  /** User-scope memory dir for global facts (type=user/feedback). Forwarded to Remember's routing. */
3604
3610
  memoryUserDir;
3605
3611
  };
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. 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, then ask plainly if they want more ("want me to open the first one?"). Never ask them to "pick which one" or reference items by number. "[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.';
3612
+ 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
3613
  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
3614
  var THINK_DISABLED_GUIDANCE = "(Think tier is not available \u2014 use Act for all escalations.)";
3609
3615
  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 +3732,7 @@ Today's date: ${(/* @__PURE__ */ new Date()).toDateString()}.`;
3726
3732
  try {
3727
3733
  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
3734
  } catch (e) {
3729
- log6.warn(`ack nudge failed: ${e instanceof Error ? e.message : e}`);
3735
+ log7.warn(`ack nudge failed: ${e instanceof Error ? e.message : e}`);
3730
3736
  } finally {
3731
3737
  this.nudging = false;
3732
3738
  }
@@ -3849,7 +3855,7 @@ Another agent just implemented the above. Independently check the CURRENT state
3849
3855
  this.notify("task_verify", `task ${id}: verifying`, { id });
3850
3856
  const cres = await new Agent(agentOpts).run(checkBrief);
3851
3857
  if (cres.finishReason !== "stop") {
3852
- log6.warn(`task ${id}: verify inconclusive (${cres.finishReason})`);
3858
+ log7.warn(`task ${id}: verify inconclusive (${cres.finishReason})`);
3853
3859
  this.notify("task_verify", `task ${id}: verify inconclusive (${cres.finishReason})`, { id, finishReason: cres.finishReason });
3854
3860
  }
3855
3861
  const sum = (a = 0, b = 0) => a + b;
@@ -3951,7 +3957,7 @@ Another agent just implemented the above. Independently check the CURRENT state
3951
3957
  return this.failTask(rec, msg);
3952
3958
  }
3953
3959
  rec.status = "done";
3954
- log6.verbose(`task ${id} done (${res.steps} steps)`);
3960
+ log7.verbose(`task ${id} done (${res.steps} steps)`);
3955
3961
  this.notify("task_done", `task ${id} (${rec.label}) completed`, {
3956
3962
  id,
3957
3963
  text: res.text,
@@ -3968,7 +3974,7 @@ Another agent just implemented the above. Independently check the CURRENT state
3968
3974
  failTask(rec, msg) {
3969
3975
  this.dropAsk(rec.id);
3970
3976
  rec.status = "error";
3971
- log6.warn(`task ${rec.id} failed: ${msg}`);
3977
+ log7.warn(`task ${rec.id} failed: ${msg}`);
3972
3978
  this.notify("task_error", `task ${rec.id} (${rec.label}) failed: ${msg}`);
3973
3979
  this.queueRevoice(`[task ${rec.id} failed] ${msg}`);
3974
3980
  }
@@ -4207,7 +4213,7 @@ init_logging();
4207
4213
 
4208
4214
  // src/voice/engine.ts
4209
4215
  init_logging();
4210
- var log7 = forComponent("VoiceEngine");
4216
+ var log8 = forComponent("VoiceEngine");
4211
4217
  var now = () => performance.now();
4212
4218
  var VoiceEngineOptions = class {
4213
4219
  stt;
@@ -4314,7 +4320,7 @@ var VoiceEngine = class _VoiceEngine {
4314
4320
  this.stt.onLevel = (rms) => this.handleLevel(rms);
4315
4321
  await Promise.all([this.tts.connect(), this.stt.start()]);
4316
4322
  this.setState("listening");
4317
- log7.debug(`voice I/O up (${this.stt.usingAec ? "AEC" : "heuristic echo"} capture)`);
4323
+ log8.debug(`voice I/O up (${this.stt.usingAec ? "AEC" : "heuristic echo"} capture)`);
4318
4324
  }
4319
4325
  get usingAec() {
4320
4326
  return this.stt.usingAec;
@@ -4366,7 +4372,7 @@ var VoiceEngine = class _VoiceEngine {
4366
4372
  this.reply += text;
4367
4373
  for (const w of this.words(this.reply)) this.echoWords.add(w);
4368
4374
  this.tts.speak(text, true);
4369
- if (!this.spokeDeltas && this.turnStartAt) log7.debug(`ttft: ${Math.round(now() - this.turnStartAt)}ms`);
4375
+ if (!this.spokeDeltas && this.turnStartAt) log8.debug(`ttft: ${Math.round(now() - this.turnStartAt)}ms`);
4370
4376
  this.spokeDeltas = true;
4371
4377
  this.setState("speaking");
4372
4378
  }
@@ -4387,7 +4393,7 @@ var VoiceEngine = class _VoiceEngine {
4387
4393
  }
4388
4394
  this.drainTimer = null;
4389
4395
  this.speaking = false;
4390
- if (this.turnStartAt) log7.debug(`turn: ${Math.round(now() - this.turnStartAt)}ms (incl. playback)`);
4396
+ if (this.turnStartAt) log8.debug(`turn: ${Math.round(now() - this.turnStartAt)}ms (incl. playback)`);
4391
4397
  this.echoUntil = now() + 2500;
4392
4398
  if (!this.usingAec) this.stt.reset();
4393
4399
  this.setState("listening");
@@ -4528,7 +4534,7 @@ var VoiceEngine = class _VoiceEngine {
4528
4534
  this.pendingUtt = this.pendingUtt ? `${this.pendingUtt} ${text}` : text;
4529
4535
  if (this.pendingTimer) clearTimeout(this.pendingTimer);
4530
4536
  if (this.options.incompleteMergeMs && this.looksIncomplete(this.pendingUtt)) {
4531
- log7.verbose(`hold: incomplete utterance "${this.pendingUtt.slice(-40)}"`);
4537
+ log8.verbose(`hold: incomplete utterance "${this.pendingUtt.slice(-40)}"`);
4532
4538
  this.options.onHold();
4533
4539
  if (this.options.holdFiller && !this.speaking) {
4534
4540
  this.beginSpeech();
@@ -4626,7 +4632,7 @@ async function resolveAuth(auth) {
4626
4632
  }
4627
4633
 
4628
4634
  // src/voice/soniox.ts
4629
- var log8 = forComponent("SonioxSTT");
4635
+ var log9 = forComponent("SonioxSTT");
4630
4636
  var now2 = () => performance.now();
4631
4637
  var SonioxSTTOptions = class {
4632
4638
  auth = "";
@@ -4683,9 +4689,9 @@ var SonioxSTT = class {
4683
4689
  this.ws.onmessage = (ev) => this.handle(JSON.parse(String(ev.data)));
4684
4690
  this.ws.onclose = (ev) => {
4685
4691
  if (this.stopped) return;
4686
- log8.warn(`soniox ws closed (${ev.code} ${ev.reason || ""}) \u2014 reconnecting`);
4692
+ log9.warn(`soniox ws closed (${ev.code} ${ev.reason || ""}) \u2014 reconnecting`);
4687
4693
  this.reset();
4688
- this.connectWs().catch((e) => log8.error(`soniox reconnect failed: ${e.message}`));
4694
+ this.connectWs().catch((e) => log9.error(`soniox reconnect failed: ${e.message}`));
4689
4695
  };
4690
4696
  }
4691
4697
  async start() {
@@ -4695,7 +4701,7 @@ var SonioxSTT = class {
4695
4701
  this.endpointTimer = setInterval(() => {
4696
4702
  const combined = (this.finalText + this.partialText).trim();
4697
4703
  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)}"`);
4704
+ if (this.firstTokenAt) log9.debug(`stt: ${Math.round(now2() - this.firstTokenAt)}ms first-token\u2192silence-endpoint, "${combined.slice(0, 60)}"`);
4699
4705
  this.reset();
4700
4706
  this.onUtterance(combined, now2());
4701
4707
  }, 120);
@@ -4712,7 +4718,7 @@ var SonioxSTT = class {
4712
4718
  });
4713
4719
  }
4714
4720
  handle(m) {
4715
- if (m.error_message) return log8.error(`soniox: ${m.error_message}`);
4721
+ if (m.error_message) return log9.error(`soniox: ${m.error_message}`);
4716
4722
  let endpoint = false;
4717
4723
  for (const t of m.tokens ?? []) {
4718
4724
  if (t.text === "<end>") endpoint = true;
@@ -4728,7 +4734,7 @@ var SonioxSTT = class {
4728
4734
  this.onPartial(combined);
4729
4735
  if (endpoint && this.finalText.trim()) {
4730
4736
  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)}"`);
4737
+ if (this.firstTokenAt) log9.debug(`stt: ${Math.round(now2() - this.firstTokenAt)}ms first-token\u2192endpoint, "${utterance.slice(0, 60)}"`);
4732
4738
  this.reset();
4733
4739
  this.onUtterance(utterance, now2());
4734
4740
  }
@@ -4750,7 +4756,7 @@ var SonioxSTT = class {
4750
4756
 
4751
4757
  // src/voice/cartesia.ts
4752
4758
  init_logging();
4753
- var log9 = forComponent("CartesiaTTS");
4759
+ var log10 = forComponent("CartesiaTTS");
4754
4760
  var now3 = () => performance.now();
4755
4761
  var CartesiaTTSOptions = class {
4756
4762
  auth = "";
@@ -4795,9 +4801,9 @@ var CartesiaTTS = class _CartesiaTTS {
4795
4801
  this.ws.onerror = (e) => rej(new Error(`cartesia ws: ${e.message || "connect failed"}`));
4796
4802
  });
4797
4803
  this.ws.onclose = (ev) => {
4798
- log9.warn(`cartesia ws closed (${ev.code} ${ev.reason || ""})`);
4804
+ log10.warn(`cartesia ws closed (${ev.code} ${ev.reason || ""})`);
4799
4805
  if (!this.closed) {
4800
- this.connecting = this.doConnect().catch((e) => log9.error(`cartesia reconnect failed: ${e.message}`));
4806
+ this.connecting = this.doConnect().catch((e) => log10.error(`cartesia reconnect failed: ${e.message}`));
4801
4807
  }
4802
4808
  };
4803
4809
  this.ws.onmessage = (ev) => {
@@ -4807,7 +4813,7 @@ var CartesiaTTS = class _CartesiaTTS {
4807
4813
  this.consecutiveErrors = 0;
4808
4814
  if (this.down) {
4809
4815
  this.down = false;
4810
- log9.info("TTS recovered");
4816
+ log10.info("TTS recovered");
4811
4817
  this.stopProbe();
4812
4818
  }
4813
4819
  if (!this.firstAudioAt) this.firstAudioAt = now3();
@@ -4816,7 +4822,7 @@ var CartesiaTTS = class _CartesiaTTS {
4816
4822
  this.consecutiveErrors = 0;
4817
4823
  if (this.down) {
4818
4824
  this.down = false;
4819
- log9.info("TTS recovered");
4825
+ log10.info("TTS recovered");
4820
4826
  this.stopProbe();
4821
4827
  }
4822
4828
  this.onDone();
@@ -4825,11 +4831,11 @@ var CartesiaTTS = class _CartesiaTTS {
4825
4831
  this.consecutiveErrors++;
4826
4832
  if (!this.down && this.consecutiveErrors >= _CartesiaTTS.CB_THRESHOLD) {
4827
4833
  this.down = true;
4828
- log9.warn(`TTS circuit breaker open \u2014 ${this.consecutiveErrors} consecutive errors, switching to text-only`);
4834
+ log10.warn(`TTS circuit breaker open \u2014 ${this.consecutiveErrors} consecutive errors, switching to text-only`);
4829
4835
  this.onDone();
4830
4836
  this.startProbe();
4831
4837
  } else if (!this.down) {
4832
- log9.warn(`cartesia: ${JSON.stringify(m)}`);
4838
+ log10.warn(`cartesia: ${JSON.stringify(m)}`);
4833
4839
  }
4834
4840
  }
4835
4841
  };
@@ -4910,7 +4916,7 @@ import { MemFilesystem as MemFilesystem3, IndexedDbFilesystem, CommandExecutor a
4910
4916
  init_logging();
4911
4917
  import { spawn } from "child_process";
4912
4918
  import { createHash } from "crypto";
4913
- var log10 = forComponent("mcp");
4919
+ var log11 = forComponent("mcp");
4914
4920
  var PROTOCOL_VERSION = "2025-06-18";
4915
4921
  var DEFAULT_TIMEOUT_MS = 3e4;
4916
4922
  var StdioTransport = class {
@@ -4929,7 +4935,7 @@ var StdioTransport = class {
4929
4935
  proc.stdout.setEncoding("utf8");
4930
4936
  proc.stdout.on("data", (chunk) => this.onData(chunk));
4931
4937
  proc.stderr.setEncoding("utf8");
4932
- proc.stderr.on("data", (chunk) => log10.debug(`[${command}] stderr:`, chunk.trimEnd()));
4938
+ proc.stderr.on("data", (chunk) => log11.debug(`[${command}] stderr:`, chunk.trimEnd()));
4933
4939
  proc.on("exit", (code) => this.failAll(new Error(`MCP server "${command}" exited (code ${code})`)));
4934
4940
  proc.on("error", (e) => this.failAll(e instanceof Error ? e : new Error(String(e))));
4935
4941
  }
@@ -4943,7 +4949,7 @@ var StdioTransport = class {
4943
4949
  try {
4944
4950
  this.dispatch(JSON.parse(line));
4945
4951
  } catch (e) {
4946
- log10.debug("dropping non-JSON line from MCP server:", line, e);
4952
+ log11.debug("dropping non-JSON line from MCP server:", line, e);
4947
4953
  }
4948
4954
  }
4949
4955
  }
@@ -4992,7 +4998,7 @@ var StdioTransport = class {
4992
4998
  try {
4993
4999
  this.proc?.stdin?.end();
4994
5000
  } catch (e) {
4995
- log10.debug("stdin end failed", e);
5001
+ log11.debug("stdin end failed", e);
4996
5002
  }
4997
5003
  this.proc?.kill();
4998
5004
  }
@@ -5061,7 +5067,7 @@ function parseSseResponse(body) {
5061
5067
  const obj = JSON.parse(trimmed.slice(5).trim());
5062
5068
  if (obj && (obj.result !== void 0 || obj.error !== void 0)) return obj;
5063
5069
  } catch (e) {
5064
- log10.debug("skipping unparseable SSE data line", e);
5070
+ log11.debug("skipping unparseable SSE data line", e);
5065
5071
  }
5066
5072
  }
5067
5073
  return {};
@@ -5129,7 +5135,7 @@ async function mountWithDeadline(name, cfg, mountTimeoutMs) {
5129
5135
  return { name, client, tools, specs, serverInfo: init?.serverInfo };
5130
5136
  })(), mountTimeoutMs, name);
5131
5137
  } catch (e) {
5132
- await client.close().catch((err2) => log10.debug(`close after failed mount of "${name}": ${err2}`));
5138
+ await client.close().catch((err2) => log11.debug(`close after failed mount of "${name}": ${err2}`));
5133
5139
  throw e;
5134
5140
  }
5135
5141
  }
@@ -5140,15 +5146,15 @@ function validEntries(servers) {
5140
5146
  return Object.entries(servers).filter(([name, cfg]) => {
5141
5147
  if (!cfg || cfg.disabled) return false;
5142
5148
  if (!cfg.command && !cfg.url) {
5143
- log10.warn(`MCP server "${name}" needs a command (stdio) or url (http) \u2014 skipping`);
5149
+ log11.warn(`MCP server "${name}" needs a command (stdio) or url (http) \u2014 skipping`);
5144
5150
  return false;
5145
5151
  }
5146
5152
  return true;
5147
5153
  });
5148
5154
  }
5149
5155
  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}`);
5156
+ if (e instanceof McpAuthError) log11.warn(`MCP "${name}" needs-auth: HTTP ${e.status} \u2014 set bearerToken or headers in its config; skipping`);
5157
+ else log11.error(`MCP server "${name}" failed to mount: ${e?.message ?? e}`);
5152
5158
  }
5153
5159
  async function mountMcpServers(servers = {}, opts = {}) {
5154
5160
  const entries = validEntries(servers);
@@ -5158,7 +5164,7 @@ async function mountMcpServers(servers = {}, opts = {}) {
5158
5164
  const name = entries[i][0];
5159
5165
  if (r.status === "fulfilled") {
5160
5166
  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}` : ""}`);
5167
+ log11.info(`MCP "${name}" mounted \u2014 ${r.value.tools.length} tool(s)${r.value.serverInfo?.name ? ` from ${r.value.serverInfo.name}` : ""}`);
5162
5168
  } else logMountFailure(name, r.reason);
5163
5169
  });
5164
5170
  return out;
@@ -5198,7 +5204,7 @@ var McpPool = class {
5198
5204
  const prev = this.warm.get(key);
5199
5205
  if (prev) {
5200
5206
  clearTimeout(prev.timer);
5201
- if (prev.client !== client) void prev.client.close().catch((err2) => log10.debug(`warm-pool replace close failed: ${err2}`));
5207
+ if (prev.client !== client) void prev.client.close().catch((err2) => log11.debug(`warm-pool replace close failed: ${err2}`));
5202
5208
  }
5203
5209
  const e = { client, timer: void 0 };
5204
5210
  this.warm.set(key, e);
@@ -5215,7 +5221,7 @@ var McpPool = class {
5215
5221
  const e = this.warm.get(key);
5216
5222
  if (!e) return;
5217
5223
  this.warm.delete(key);
5218
- await e.client.close().catch((err2) => log10.debug(`warm-pool evict close failed: ${err2}`));
5224
+ await e.client.close().catch((err2) => log11.debug(`warm-pool evict close failed: ${err2}`));
5219
5225
  }
5220
5226
  async closeAll() {
5221
5227
  for (const e of this.warm.values()) {
@@ -5423,6 +5429,7 @@ function defaultOpenBrowser(url) {
5423
5429
  }
5424
5430
 
5425
5431
  // cli/core.ts
5432
+ import { randomUUID } from "crypto";
5426
5433
  import { resolve, basename, join as join3 } from "path";
5427
5434
  import { existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
5428
5435
  import { platform, arch, release, userInfo, homedir } from "os";
@@ -5567,7 +5574,9 @@ Reference files in them by their mount path (the left side).`;
5567
5574
  // host's MCP servers so the delegated cursor agent runs in the same environment. Gated to cursor:
5568
5575
  // openai/google adapters Object.assign providerOptions into the request body, so a blanket cwd
5569
5576
  // would corrupt those calls.
5570
- ...isCursor ? { providerOptions: { cwd, ...toCursorMcp(o.mcpServers) ?? {} } } : {},
5577
+ // Warm cursor session (default on; CURSOR_WARM=0 to disable): one long-lived agent reused across
5578
+ // turns, amortising the ~2s Agent.create + h2 handshake. Keyed per agentx launch.
5579
+ ...isCursor ? { providerOptions: { cwd, ...toCursorMcp(o.mcpServers) ?? {}, ...process.env.CURSOR_WARM === "0" ? {} : { cursorSession: randomUUID() } } } : {},
5571
5580
  ...(() => {
5572
5581
  const now5 = /* @__PURE__ */ new Date();
5573
5582
  const platformNames = { darwin: "macOS", linux: "Linux", win32: "Windows" };
@@ -5644,7 +5653,7 @@ import { existsSync as existsSync3, mkdirSync as mkdirSync3, statSync as statSyn
5644
5653
  import { homedir as homedir2 } from "os";
5645
5654
  import { dirname as dirname3, join as join4 } from "path";
5646
5655
  import { fileURLToPath } from "url";
5647
- var log12 = forComponent("VoiceIO");
5656
+ var log13 = forComponent("VoiceIO");
5648
5657
  var now4 = () => performance.now();
5649
5658
  var Player = class {
5650
5659
  proc = null;
@@ -5658,7 +5667,7 @@ var Player = class {
5658
5667
  ["-loglevel", "quiet", "-nodisp", "-fflags", "nobuffer", "-flags", "low_delay", "-probesize", "32", "-f", "s16le", "-ar", String(TTS_SAMPLE_RATE), "-ch_layout", "mono", "-i", "-"],
5659
5668
  { stdio: ["pipe", "ignore", "ignore"] }
5660
5669
  );
5661
- this.proc.on("error", (e) => log12.warn(`ffplay error: ${e.message}`));
5670
+ this.proc.on("error", (e) => log13.warn(`ffplay error: ${e.message}`));
5662
5671
  this.proc.stdin.on("error", () => {
5663
5672
  });
5664
5673
  this.bytesWritten = 0;
@@ -5693,7 +5702,7 @@ function detectFfmpegMic() {
5693
5702
  const devices = [...audio.matchAll(/\[(\d+)\] (.+)/g)].map(([, idx, name]) => ({ idx, name: name.trim() }));
5694
5703
  const mic = devices.find((d) => /microphone|built-in/i.test(d.name) && !/teams|blackhole|loopback/i.test(d.name)) ?? devices[0];
5695
5704
  if (!mic) throw new Error("no audio input device found");
5696
- log12.debug(`ffmpeg mic: [${mic.idx}] ${mic.name}`);
5705
+ log13.debug(`ffmpeg mic: [${mic.idx}] ${mic.name}`);
5697
5706
  return `:${mic.idx}`;
5698
5707
  }
5699
5708
  function resolveAecBinary() {
@@ -5706,15 +5715,15 @@ function resolveAecBinary() {
5706
5715
  if (existsSync3(bin) && statSync2(bin).mtimeMs >= statSync2(src).mtimeMs) return bin;
5707
5716
  if (spawnSync("which", ["swiftc"]).status !== 0) return null;
5708
5717
  mkdirSync3(cacheDir, { recursive: true });
5709
- log12.info("compiling AEC mic helper (first run)\u2026");
5718
+ log13.info("compiling AEC mic helper (first run)\u2026");
5710
5719
  const build = spawnSync("swiftc", ["-O", "-o", bin, src, "-Xlinker", "-sectcreate", "-Xlinker", "__TEXT", "-Xlinker", "__info_plist", "-Xlinker", plist], { encoding: "utf8" });
5711
5720
  if (build.status !== 0) {
5712
- log12.warn(`AEC build failed: ${build.stderr?.slice(0, 400)}`);
5721
+ log13.warn(`AEC build failed: ${build.stderr?.slice(0, 400)}`);
5713
5722
  return null;
5714
5723
  }
5715
5724
  const sign = spawnSync("codesign", ["-fs", "-", bin], { encoding: "utf8" });
5716
5725
  if (sign.status !== 0) {
5717
- log12.warn(`codesign failed: ${sign.stderr?.slice(0, 200)}`);
5726
+ log13.warn(`codesign failed: ${sign.stderr?.slice(0, 200)}`);
5718
5727
  return null;
5719
5728
  }
5720
5729
  return bin;
@@ -5733,16 +5742,16 @@ var NodeMicSource = class {
5733
5742
  this.proc = spawn2(this.bin, [], { stdio: ["ignore", "pipe", "ignore"] });
5734
5743
  } else {
5735
5744
  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");
5745
+ log13.info("mic: raw capture (no AEC) \u2014 echo handled heuristically; headphones recommended");
5737
5746
  this.proc = spawn2(
5738
5747
  "ffmpeg",
5739
5748
  ["-loglevel", "error", "-f", "avfoundation", "-i", detectFfmpegMic(), "-ar", String(STT_SAMPLE_RATE), "-ac", "1", "-f", "s16le", "-"],
5740
5749
  { stdio: ["ignore", "pipe", "pipe"] }
5741
5750
  );
5742
- this.proc.stderr.on("data", (d) => log12.warn(`ffmpeg: ${String(d).trim()}`));
5751
+ this.proc.stderr.on("data", (d) => log13.warn(`ffmpeg: ${String(d).trim()}`));
5743
5752
  }
5744
5753
  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`);
5754
+ if (c && !this.stopped) log13.error(`mic capture exited (${c}) \u2014 check mic permission / MIC_DEVICE / MIC_AEC=0`);
5746
5755
  });
5747
5756
  this.proc.stdout.on("data", (chunk) => onChunk(chunk));
5748
5757
  }
@@ -5776,11 +5785,11 @@ var AecDuplexAudio = class {
5776
5785
  this.proc.stdin.on("error", () => {
5777
5786
  });
5778
5787
  this.proc.on("exit", (c) => {
5779
- if (c && !this.stopped) log12.error(`aec duplex audio exited (${c}) \u2014 check mic permission / MIC_AEC=0`);
5788
+ if (c && !this.stopped) log13.error(`aec duplex audio exited (${c}) \u2014 check mic permission / MIC_AEC=0`);
5780
5789
  });
5781
5790
  this.proc.stdout.on("data", (chunk) => onChunk(chunk));
5782
5791
  this.proc.stderr.on("data", (d) => {
5783
- for (const ln of String(d).split("\n")) if (ln.trim()) log12.debug(`mic-aec: ${ln.trim()}`);
5792
+ for (const ln of String(d).split("\n")) if (ln.trim()) log13.debug(`mic-aec: ${ln.trim()}`);
5784
5793
  });
5785
5794
  }
5786
5795
  stop() {
@@ -5953,7 +5962,7 @@ async function loadConfig(cwd) {
5953
5962
 
5954
5963
  // cli/hooks-config.ts
5955
5964
  import { spawnSync as spawnSync2 } from "child_process";
5956
- var log13 = forComponent("hooks");
5965
+ var log14 = forComponent("hooks");
5957
5966
  var escapeRegex = (s) => s.replace(/[.+^${}()|[\]\\]/g, "\\$&");
5958
5967
  function ruleMatches(rule, toolName) {
5959
5968
  if (!rule.tool || rule.tool === "*") return true;
@@ -5970,7 +5979,7 @@ function runCmd(rule, env) {
5970
5979
  });
5971
5980
  return { code: r.status ?? 1, out: ((r.stdout ?? "") + (r.stderr ?? "")).trim() };
5972
5981
  } catch (e) {
5973
- log13.debug(`hook command failed: ${rule.command}`, e);
5982
+ log14.debug(`hook command failed: ${rule.command}`, e);
5974
5983
  return { code: 1, out: String(e?.message ?? e) };
5975
5984
  }
5976
5985
  }
@@ -6076,7 +6085,7 @@ function formatDiff(ops, opts = {}) {
6076
6085
  // cli/session.ts
6077
6086
  import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync3, readdirSync, renameSync } from "fs";
6078
6087
  import { join as join6 } from "path";
6079
- var log14 = forComponent("session");
6088
+ var log15 = forComponent("session");
6080
6089
  var SessionStore = class {
6081
6090
  dir;
6082
6091
  constructor(cwd) {
@@ -6102,7 +6111,7 @@ var SessionStore = class {
6102
6111
  }
6103
6112
  load(id) {
6104
6113
  if (!this.safeId(id)) {
6105
- log14.debug(`rejecting unsafe session id: ${id}`);
6114
+ log15.debug(`rejecting unsafe session id: ${id}`);
6106
6115
  return void 0;
6107
6116
  }
6108
6117
  const path = join6(this.dir, `${id}.json`);
@@ -6110,7 +6119,7 @@ var SessionStore = class {
6110
6119
  try {
6111
6120
  return JSON.parse(readFileSync3(path, "utf8"));
6112
6121
  } catch (e) {
6113
- log14.debug(`unreadable session ${id} \u2014 ignoring`, e);
6122
+ log15.debug(`unreadable session ${id} \u2014 ignoring`, e);
6114
6123
  return void 0;
6115
6124
  }
6116
6125
  }
@@ -6123,7 +6132,7 @@ var SessionStore = class {
6123
6132
  try {
6124
6133
  metas.push(JSON.parse(readFileSync3(join6(this.dir, f), "utf8")).meta);
6125
6134
  } catch (e) {
6126
- log14.debug(`skipping unreadable session file ${f}`, e);
6135
+ log15.debug(`skipping unreadable session file ${f}`, e);
6127
6136
  }
6128
6137
  }
6129
6138
  return metas.sort((a, b) => b.updated - a.updated);
@@ -6217,7 +6226,7 @@ import { execFile } from "child_process";
6217
6226
  import { promisify } from "util";
6218
6227
  import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, existsSync as existsSync6 } from "fs";
6219
6228
  import { join as join7, resolve as resolve2, sep as sep2 } from "path";
6220
- var log15 = forComponent("checkpoints");
6229
+ var log16 = forComponent("checkpoints");
6221
6230
  var exec = promisify(execFile);
6222
6231
  var DEFAULT_EXCLUDE = [".agent/", ".git/", "node_modules/", "dist/", "build/", ".next/", "target/", ".venv/", "__pycache__/", "*.log"];
6223
6232
  var ShadowRepo = class {
@@ -6251,7 +6260,7 @@ var ShadowRepo = class {
6251
6260
  writeFileSync4(join7(this.gitDir, "info", "exclude"), this.exclude.join("\n") + "\n");
6252
6261
  this.ready = true;
6253
6262
  } catch (e) {
6254
- log15.debug(`git checkpoints unavailable for ${this.workTree}`, e);
6263
+ log16.debug(`git checkpoints unavailable for ${this.workTree}`, e);
6255
6264
  this.ready = false;
6256
6265
  }
6257
6266
  return this.ready;
@@ -6314,7 +6323,7 @@ var ShadowRepo = class {
6314
6323
  await this.run("gc", "--auto", "-q").catch(() => {
6315
6324
  });
6316
6325
  } catch (e) {
6317
- log15.debug("checkpoint prune failed", e);
6326
+ log16.debug("checkpoint prune failed", e);
6318
6327
  }
6319
6328
  }
6320
6329
  };
@@ -6371,7 +6380,7 @@ var GitCheckpoints = class {
6371
6380
  use(sessionId) {
6372
6381
  if (sessionId === this.session) return;
6373
6382
  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));
6383
+ if (this.started) for (const r of this.repos) void r.point(this.ref()).catch((e) => log16.debug("re-point failed", e));
6375
6384
  }
6376
6385
  async begin(label) {
6377
6386
  if (!await this.start()) return;
@@ -6382,7 +6391,7 @@ var GitCheckpoints = class {
6382
6391
  try {
6383
6392
  await r.commit(msg);
6384
6393
  } catch (e) {
6385
- log15.debug("checkpoint commit failed", e);
6394
+ log16.debug("checkpoint commit failed", e);
6386
6395
  }
6387
6396
  }
6388
6397
  if (slow) clearTimeout(slow);
@@ -7759,7 +7768,7 @@ var red = C("31");
7759
7768
  var bold = C("1");
7760
7769
  var yellow = C("33");
7761
7770
  var err = (s) => process.stderr.write(s);
7762
- var log16 = forComponent("cli");
7771
+ var log17 = forComponent("cli");
7763
7772
  var VERSION = (() => {
7764
7773
  try {
7765
7774
  return JSON.parse(readFileSync5(new URL("../package.json", import.meta.url), "utf8")).version ?? "?";
@@ -8193,7 +8202,7 @@ function costOf(pricing, promptTokens = 0, completionTokens = 0, cacheCreationTo
8193
8202
  function turnCost(model, usage) {
8194
8203
  return costOf(getModelInfo(model)?.pricing, usage?.promptTokens ?? 0, usage?.completionTokens ?? 0, usage?.cacheCreationTokens ?? 0, usage?.cacheReadTokens ?? 0, model);
8195
8204
  }
8196
- async function evaluateGoal(ai, condition, transcript, log17) {
8205
+ async function evaluateGoal(ai, condition, transcript, log18) {
8197
8206
  const recent = transcript.filter((m) => m.role === "assistant").slice(-8).map((m) => {
8198
8207
  const text = typeof m.content === "string" ? m.content : m.content.filter((p) => p.type === "text").map((p) => p.text).join(" ");
8199
8208
  return text.slice(0, 600);
@@ -8213,7 +8222,7 @@ ${recent}` }
8213
8222
  const match = r.content.match(/\{[\s\S]*\}/);
8214
8223
  if (match) return JSON.parse(match[0]);
8215
8224
  } catch (e) {
8216
- log17(dim(` (goal evaluator error: ${e?.message ?? e})
8225
+ log18(dim(` (goal evaluator error: ${e?.message ?? e})
8217
8226
  `));
8218
8227
  }
8219
8228
  return { met: false, reason: "evaluation unclear" };
@@ -8396,7 +8405,7 @@ async function mountMcp(cfg, oauth) {
8396
8405
  return mounted;
8397
8406
  }
8398
8407
  async function closeMcp(mounted) {
8399
- await Promise.all(mounted.map((m) => m.client.close().catch((e) => log16.debug("mcp close failed", e))));
8408
+ await Promise.all(mounted.map((m) => m.client.close().catch((e) => log17.debug("mcp close failed", e))));
8400
8409
  }
8401
8410
  var IMG_EXT = { ".png": "image/png", ".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".gif": "image/gif", ".webp": "image/webp" };
8402
8411
  function readImageParts(cwd, line) {
@@ -8628,14 +8637,14 @@ var isCancelTeardown = (e) => {
8628
8637
  function installCancelGuards(mounted) {
8629
8638
  process.on("unhandledRejection", (e) => {
8630
8639
  if (isCancelTeardown(e)) {
8631
- log16.debug("suppressed unhandledRejection (cursor stream cancel)", e);
8640
+ log17.debug("suppressed unhandledRejection (cursor stream cancel)", e);
8632
8641
  return;
8633
8642
  }
8634
- log16.error("unhandledRejection", e);
8643
+ log17.error("unhandledRejection", e);
8635
8644
  });
8636
8645
  process.on("uncaughtException", (e) => {
8637
8646
  if (isCancelTeardown(e)) {
8638
- log16.debug("suppressed uncaughtException (cursor stream cancel)", e);
8647
+ log17.debug("suppressed uncaughtException (cursor stream cancel)", e);
8639
8648
  return;
8640
8649
  }
8641
8650
  console.error(e);
@@ -8928,7 +8937,7 @@ async function repl(args, ai, cfg, cwd) {
8928
8937
  mkdirSync7(join9(cwd, ".agent"), { recursive: true });
8929
8938
  appendFileSync(histPath, line + "\n");
8930
8939
  } catch (e) {
8931
- log16.debug("history write failed", e);
8940
+ log17.debug("history write failed", e);
8932
8941
  }
8933
8942
  };
8934
8943
  const ago = (t) => {
@@ -8993,7 +9002,7 @@ async function repl(args, ai, cfg, cwd) {
8993
9002
  try {
8994
9003
  store.save(session);
8995
9004
  } catch (e) {
8996
- log16.debug("session save after rewind failed", e);
9005
+ log17.debug("session save after rewind failed", e);
8997
9006
  }
8998
9007
  err(green(" \u27F2 jumped back") + dim(` \u2014 ${face.transcript.length} message(s) kept; edit + resend
8999
9008
  `));
@@ -9540,7 +9549,7 @@ ${extra}` : body);
9540
9549
  }
9541
9550
  const m = mounted.splice(idx, 1)[0];
9542
9551
  removeWorkTools(m.tools.map((t) => t.name));
9543
- await m.client.close().catch((e) => log16.debug("mcp close failed", e));
9552
+ await m.client.close().catch((e) => log17.debug("mcp close failed", e));
9544
9553
  err(dim(` removed "${name}"
9545
9554
  `));
9546
9555
  return;
@@ -9724,7 +9733,7 @@ ${extra}` : body);
9724
9733
  try {
9725
9734
  return readdirSync2(join9(cwd, absDir.replace(/^\/+/, "")), { withFileTypes: true }).map((d) => ({ name: d.name, dir: d.isDirectory() }));
9726
9735
  } catch (e) {
9727
- log16.debug("completion readdir failed", absDir, e);
9736
+ log17.debug("completion readdir failed", absDir, e);
9728
9737
  return null;
9729
9738
  }
9730
9739
  };