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