agent.libx.js 0.93.26 → 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 +83 -79
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +60 -1
- package/dist/index.js +133 -31
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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) =>
|
|
3499
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
3536
|
+
log6.warn(`could not persist lesson: ${e?.message ?? e}`);
|
|
3533
3537
|
return null;
|
|
3534
3538
|
}
|
|
3535
|
-
|
|
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
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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)
|
|
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)
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
4690
|
+
log9.warn(`soniox ws closed (${ev.code} ${ev.reason || ""}) \u2014 reconnecting`);
|
|
4687
4691
|
this.reset();
|
|
4688
|
-
this.connectWs().catch((e) =>
|
|
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)
|
|
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
|
|
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)
|
|
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
|
|
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
|
-
|
|
4802
|
+
log10.warn(`cartesia ws closed (${ev.code} ${ev.reason || ""})`);
|
|
4799
4803
|
if (!this.closed) {
|
|
4800
|
-
this.connecting = this.doConnect().catch((e) =>
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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) =>
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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) =>
|
|
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
|
-
|
|
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)
|
|
5151
|
-
else
|
|
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
|
-
|
|
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) =>
|
|
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) =>
|
|
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
|
|
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) =>
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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) =>
|
|
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)
|
|
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)
|
|
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())
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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) =>
|
|
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
|
-
|
|
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
|
|
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,
|
|
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
|
-
|
|
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) =>
|
|
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
|
-
|
|
8635
|
+
log17.debug("suppressed unhandledRejection (cursor stream cancel)", e);
|
|
8632
8636
|
return;
|
|
8633
8637
|
}
|
|
8634
|
-
|
|
8638
|
+
log17.error("unhandledRejection", e);
|
|
8635
8639
|
});
|
|
8636
8640
|
process.on("uncaughtException", (e) => {
|
|
8637
8641
|
if (isCancelTeardown(e)) {
|
|
8638
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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) =>
|
|
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
|
-
|
|
9731
|
+
log17.debug("completion readdir failed", absDir, e);
|
|
9728
9732
|
return null;
|
|
9729
9733
|
}
|
|
9730
9734
|
};
|