@livx.cc/agentx 0.99.120 → 0.99.122
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 +29 -2
- package/dist/cli.js +719 -254
- 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 +7 -2
- package/dist/index.js +392 -158
- package/dist/index.js.map +1 -1
- package/dist/{secondChannel-DepBV0Zv.d.ts → secondChannel-BddfVzTp.d.ts} +109 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2754,7 +2754,7 @@ async function psGroupCpuSeconds(pgid) {
|
|
|
2754
2754
|
try {
|
|
2755
2755
|
return await winTreeCpuSeconds(pgid);
|
|
2756
2756
|
} catch (e) {
|
|
2757
|
-
|
|
2757
|
+
log10.debug(`cpu probe failed for ${pgid}: ${e}`);
|
|
2758
2758
|
return void 0;
|
|
2759
2759
|
}
|
|
2760
2760
|
}
|
|
@@ -2763,7 +2763,7 @@ async function psGroupCpuSeconds(pgid) {
|
|
|
2763
2763
|
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
2764
|
return sumTreeCpu(out, pgid);
|
|
2765
2765
|
} catch (e) {
|
|
2766
|
-
|
|
2766
|
+
log10.debug(`ps cpu probe failed for group ${pgid}: ${e}`);
|
|
2767
2767
|
return void 0;
|
|
2768
2768
|
}
|
|
2769
2769
|
}
|
|
@@ -2912,7 +2912,7 @@ function makeRealShellTool(options) {
|
|
|
2912
2912
|
proc.stderr?.on("data", collect);
|
|
2913
2913
|
proc.on("error", (err) => {
|
|
2914
2914
|
if (err?.name === "AbortError" || ctl.signal.aborted) return finish(reasonFor(timedOut, timeoutMs, clean(out)));
|
|
2915
|
-
|
|
2915
|
+
log10.debug("shell spawn error", err);
|
|
2916
2916
|
finish(`[exit 1] ${err?.message ?? err}${out ? "\n" + clean(out) : ""}`);
|
|
2917
2917
|
});
|
|
2918
2918
|
proc.on("close", (code) => {
|
|
@@ -2991,7 +2991,7 @@ ${clean(out) || "(no output yet)"}`;
|
|
|
2991
2991
|
}
|
|
2992
2992
|
];
|
|
2993
2993
|
}
|
|
2994
|
-
var
|
|
2994
|
+
var log10, clean, DETACHED, shellArgv, SECRET_ENV_RE, _spawn, ShellJobRegistry, shellStatus, NO_JOB2;
|
|
2995
2995
|
var init_tools_shell = __esm({
|
|
2996
2996
|
"src/tools.shell.ts"() {
|
|
2997
2997
|
"use strict";
|
|
@@ -3001,7 +3001,7 @@ var init_tools_shell = __esm({
|
|
|
3001
3001
|
init_shell_sandbox();
|
|
3002
3002
|
init_tools_jobs();
|
|
3003
3003
|
init_platform();
|
|
3004
|
-
|
|
3004
|
+
log10 = forComponent("shell");
|
|
3005
3005
|
clean = (s) => truncateOutput(redactSecrets(s.replace(/\n+$/, "")));
|
|
3006
3006
|
DETACHED = { stdio: ["ignore", "pipe", "pipe"], ...DETACH_OPTIONS };
|
|
3007
3007
|
shellArgv = (sh, command) => ({ bin: sh.bin, args: sh.args(command), ...sh.spawnOptions ? { spawnOptions: sh.spawnOptions } : {} });
|
|
@@ -4276,7 +4276,11 @@ function childOptionsFor(opts, fs, depth, maxDepth, agentType, model, root) {
|
|
|
4276
4276
|
if (po) childOpts.providerOptions = po;
|
|
4277
4277
|
const deny = opts.permissions?.options.rules.filter((r) => r.decision === "deny") ?? [];
|
|
4278
4278
|
if (deny.length) childOpts.permissions = new PermissionPolicy({ rules: deny, default: "allow" });
|
|
4279
|
-
|
|
4279
|
+
const h = opts.hooks;
|
|
4280
|
+
if (h?.preToolUse || opts.inheritPostToolUse && h?.postToolUse) childOpts.hooks = {
|
|
4281
|
+
...h.preToolUse ? { preToolUse: h.preToolUse.bind(h) } : {},
|
|
4282
|
+
...opts.inheritPostToolUse && h.postToolUse ? { postToolUse: h.postToolUse.bind(h) } : {}
|
|
4283
|
+
};
|
|
4280
4284
|
return childOpts;
|
|
4281
4285
|
}
|
|
4282
4286
|
function makeTaskTool(opts) {
|
|
@@ -4500,6 +4504,147 @@ async function loadAgents(fs, dir) {
|
|
|
4500
4504
|
init_OverlayFilesystem();
|
|
4501
4505
|
init_logging();
|
|
4502
4506
|
|
|
4507
|
+
// src/modes.ts
|
|
4508
|
+
init_logging();
|
|
4509
|
+
var log8 = forComponent("modes");
|
|
4510
|
+
var WRITE_TOOLS = /* @__PURE__ */ new Set(["Write", "Edit", "MultiEdit", "ApplyEdits"]);
|
|
4511
|
+
var PLANNER_BLOCKED = /* @__PURE__ */ new Set([...WRITE_TOOLS, "bash", "Shell"]);
|
|
4512
|
+
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.";
|
|
4513
|
+
var PLAN_FIRST = "Refused: check your plan with `Advise` before the first write (files, data shape, the hard parts). Then write.";
|
|
4514
|
+
var ADVISE_LIMIT = 3;
|
|
4515
|
+
var SNAPSHOT_CAP = 6e4;
|
|
4516
|
+
function pairOk(kind, main, other) {
|
|
4517
|
+
if (other === main) {
|
|
4518
|
+
log8.warn(`${kind} off: both sides are ${main}`);
|
|
4519
|
+
return false;
|
|
4520
|
+
}
|
|
4521
|
+
if (main.startsWith("cursor/") || other.startsWith("cursor/")) {
|
|
4522
|
+
log8.warn(`${kind} off: cursor models are not supported (${main} / ${other})`);
|
|
4523
|
+
return false;
|
|
4524
|
+
}
|
|
4525
|
+
return true;
|
|
4526
|
+
}
|
|
4527
|
+
function delegateWorkerFor(planner, worker) {
|
|
4528
|
+
return worker && pairOk("delegate", planner, worker) ? worker : void 0;
|
|
4529
|
+
}
|
|
4530
|
+
function advisorFor(worker, advisor) {
|
|
4531
|
+
return advisor && pairOk("advisor", worker, advisor) ? advisor : void 0;
|
|
4532
|
+
}
|
|
4533
|
+
function delegateTools(tools, planner) {
|
|
4534
|
+
return tools.map((t) => !PLANNER_BLOCKED.has(t.name) ? t : {
|
|
4535
|
+
...t,
|
|
4536
|
+
run: async (...a) => a[1]?.model === planner ? PLANNER_REFUSAL : t.run(...a)
|
|
4537
|
+
});
|
|
4538
|
+
}
|
|
4539
|
+
function delegatePrompt(worker) {
|
|
4540
|
+
return `
|
|
4541
|
+
|
|
4542
|
+
## You are the PLANNER
|
|
4543
|
+
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.`;
|
|
4544
|
+
}
|
|
4545
|
+
function advisorPrompt(advisor) {
|
|
4546
|
+
return `
|
|
4547
|
+
|
|
4548
|
+
## You have an ADVISOR
|
|
4549
|
+
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.`;
|
|
4550
|
+
}
|
|
4551
|
+
async function textOf(r) {
|
|
4552
|
+
if (r && typeof r[Symbol.asyncIterator] === "function") {
|
|
4553
|
+
let s = "";
|
|
4554
|
+
for await (const c of r) if (typeof c?.content === "string") s += c.content;
|
|
4555
|
+
return s;
|
|
4556
|
+
}
|
|
4557
|
+
return String(r?.content ?? r?.text ?? "");
|
|
4558
|
+
}
|
|
4559
|
+
function makeAdvisor(o) {
|
|
4560
|
+
let used = 0;
|
|
4561
|
+
const limit = o.limit ?? ADVISE_LIMIT;
|
|
4562
|
+
const tool = {
|
|
4563
|
+
name: "Advise",
|
|
4564
|
+
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.`,
|
|
4565
|
+
parameters: { type: "object", properties: { question: { type: "string", description: "what you want advice on, with the context it needs" } }, required: ["question"] },
|
|
4566
|
+
async run(args) {
|
|
4567
|
+
const question = String(args?.question ?? "").trim();
|
|
4568
|
+
if (!question) return "No advice: the question is empty.";
|
|
4569
|
+
if (used >= limit) return `No advice: the limit of ${limit} calls is used. Continue with your own judgment.`;
|
|
4570
|
+
used++;
|
|
4571
|
+
let state = "";
|
|
4572
|
+
if (o.snapshot) {
|
|
4573
|
+
const s = String(await Promise.resolve(o.snapshot()).catch((err) => {
|
|
4574
|
+
log8.warn("advisor snapshot failed:", String(err));
|
|
4575
|
+
return "";
|
|
4576
|
+
}) || "(empty)");
|
|
4577
|
+
state = `
|
|
4578
|
+
|
|
4579
|
+
## Current state
|
|
4580
|
+
${s.length > SNAPSHOT_CAP ? `${s.slice(0, SNAPSHOT_CAP)}
|
|
4581
|
+
\u2026(cut)` : s}`;
|
|
4582
|
+
}
|
|
4583
|
+
const review = used > 1 ? "" : "\n\n(When the work is complete, call Advise again for a review.)";
|
|
4584
|
+
try {
|
|
4585
|
+
const r = await o.ai.chat({
|
|
4586
|
+
model: o.advisor,
|
|
4587
|
+
messages: [
|
|
4588
|
+
{ 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." },
|
|
4589
|
+
{ role: "user", content: `## User request
|
|
4590
|
+
${o.request()}${state}
|
|
4591
|
+
|
|
4592
|
+
## Question
|
|
4593
|
+
${question}` }
|
|
4594
|
+
],
|
|
4595
|
+
maxTokens: 2e3
|
|
4596
|
+
});
|
|
4597
|
+
return ((await textOf(r)).trim() || "No advice: the advisor returned nothing.") + review;
|
|
4598
|
+
} catch (err) {
|
|
4599
|
+
log8.warn("advisor call failed:", String(err));
|
|
4600
|
+
return `No advice: the advisor call failed (${String(err?.message ?? err).slice(0, 120)}). Continue with your own judgment.`;
|
|
4601
|
+
}
|
|
4602
|
+
}
|
|
4603
|
+
};
|
|
4604
|
+
const gate = (tools) => tools.map((t) => !WRITE_TOOLS.has(t.name) ? t : { ...t, run: async (...a) => used ? t.run(...a) : PLAN_FIRST });
|
|
4605
|
+
return { tool, gate, used: () => used };
|
|
4606
|
+
}
|
|
4607
|
+
function meterWorkers(ai, main) {
|
|
4608
|
+
let total;
|
|
4609
|
+
let resolved;
|
|
4610
|
+
const seen = () => {
|
|
4611
|
+
resolved = ai.modelUsed ?? resolved;
|
|
4612
|
+
};
|
|
4613
|
+
const add = (u) => {
|
|
4614
|
+
if (!u) return;
|
|
4615
|
+
const t = total ??= { promptTokens: 0, completionTokens: 0, totalTokens: 0, cacheCreationTokens: 0, cacheReadTokens: 0 };
|
|
4616
|
+
t.promptTokens += u.promptTokens ?? 0;
|
|
4617
|
+
t.completionTokens += u.completionTokens ?? 0;
|
|
4618
|
+
t.totalTokens += u.totalTokens ?? 0;
|
|
4619
|
+
t.cacheCreationTokens += u.cacheCreationTokens ?? 0;
|
|
4620
|
+
t.cacheReadTokens += u.cacheReadTokens ?? 0;
|
|
4621
|
+
};
|
|
4622
|
+
async function* tap(it) {
|
|
4623
|
+
let last;
|
|
4624
|
+
try {
|
|
4625
|
+
for await (const c of it) {
|
|
4626
|
+
if (c?.usage) last = c.usage;
|
|
4627
|
+
yield c;
|
|
4628
|
+
}
|
|
4629
|
+
} finally {
|
|
4630
|
+
add(last);
|
|
4631
|
+
seen();
|
|
4632
|
+
}
|
|
4633
|
+
}
|
|
4634
|
+
const chat = async (o) => {
|
|
4635
|
+
const r = await ai.chat(o);
|
|
4636
|
+
if (o?.model === main) return r;
|
|
4637
|
+
if (r && typeof r[Symbol.asyncIterator] === "function") return tap(r);
|
|
4638
|
+
add(r?.usage);
|
|
4639
|
+
seen();
|
|
4640
|
+
return r;
|
|
4641
|
+
};
|
|
4642
|
+
return { ai: new Proxy(ai, { get: (t, k) => k === "chat" ? chat : Reflect.get(t, k) }), usage: () => total && { ...total }, model: () => resolved, reset: () => {
|
|
4643
|
+
total = void 0;
|
|
4644
|
+
resolved = void 0;
|
|
4645
|
+
} };
|
|
4646
|
+
}
|
|
4647
|
+
|
|
4503
4648
|
// src/lint.ts
|
|
4504
4649
|
var CODE_RE = /\.(ts|tsx|js|jsx|mjs|cjs)$/;
|
|
4505
4650
|
function checkSyntax(path, content) {
|
|
@@ -4579,7 +4724,7 @@ function reasoningToChatFragment(model, effort) {
|
|
|
4579
4724
|
}
|
|
4580
4725
|
|
|
4581
4726
|
// src/Agent.ts
|
|
4582
|
-
var
|
|
4727
|
+
var log9 = forComponent("Agent");
|
|
4583
4728
|
function envInt(key, fallback) {
|
|
4584
4729
|
const n = typeof process !== "undefined" ? Number(process.env?.[key]) : NaN;
|
|
4585
4730
|
return Number.isFinite(n) && n >= 0 ? n : fallback;
|
|
@@ -4708,6 +4853,13 @@ var AgentOptions = class {
|
|
|
4708
4853
|
hooks;
|
|
4709
4854
|
/** If true: add the `Task` tool so the agent can spawn depth-limited child agents over the VFS. */
|
|
4710
4855
|
subagents = false;
|
|
4856
|
+
/** DELEGATE MODE: `model` plans, `worker` writes. The planner's Write/Edit/MultiEdit/ApplyEdits/bash/Shell are
|
|
4857
|
+
* refused at execution; Task/TaskBatch are on (maxDepth 1, children default to `worker`) and children get the
|
|
4858
|
+
* parent's `postToolUse` too. Off when the models are equal or either is cursor/*. Wins over `advisor`. */
|
|
4859
|
+
delegate;
|
|
4860
|
+
/** ADVISOR MODE: `model` does the work; an `Advise` tool asks `advisor.model` (≤`limit`, default 3) with only
|
|
4861
|
+
* the request + `snapshot()` (host state, optional) + the question. The first write waits for one consult. */
|
|
4862
|
+
advisor;
|
|
4711
4863
|
/** VFS dir of typed-subagent defs (`<dir>/<name>.md`). If set with `subagents`: inject a catalog + enable the `Task` `agentType` param. */
|
|
4712
4864
|
agentsDir;
|
|
4713
4865
|
/** Current recursion depth (0 = top-level); spawned children run at depth+1. */
|
|
@@ -4816,6 +4968,11 @@ var Agent = class _Agent {
|
|
|
4816
4968
|
lastTrimNotified = 0;
|
|
4817
4969
|
// last auto-trim drop count surfaced via host.notify (dedup)
|
|
4818
4970
|
activeTools = [];
|
|
4971
|
+
meter;
|
|
4972
|
+
// delegate/advisor: meters the second model's calls
|
|
4973
|
+
modeModel;
|
|
4974
|
+
lastTask = "";
|
|
4975
|
+
// the current turn's text — what the advisor reads as the request
|
|
4819
4976
|
/** claude-code bridge for agentx-only tools (ccBridge.ts) + the current step's executor it calls. */
|
|
4820
4977
|
ccServer;
|
|
4821
4978
|
ccExec;
|
|
@@ -4999,7 +5156,7 @@ var Agent = class _Agent {
|
|
|
4999
5156
|
const disk = new NodeDiskFilesystem2(process.cwd());
|
|
5000
5157
|
await disk.init();
|
|
5001
5158
|
this.options.fs = new JailedFilesystem2(disk);
|
|
5002
|
-
|
|
5159
|
+
log9.info(`no fs provided \u2014 defaulting to jailed real disk at ${process.cwd()}`);
|
|
5003
5160
|
}
|
|
5004
5161
|
this.buildCtx();
|
|
5005
5162
|
}
|
|
@@ -5016,6 +5173,12 @@ var Agent = class _Agent {
|
|
|
5016
5173
|
const fs = o.fs;
|
|
5017
5174
|
let systemPrompt = o.systemPrompt;
|
|
5018
5175
|
let tools = o.tools;
|
|
5176
|
+
this.ctx.model = o.model;
|
|
5177
|
+
const worker = o.delegate && delegateWorkerFor(o.model, o.delegate.worker);
|
|
5178
|
+
const advisor = !worker && o.advisor ? advisorFor(o.model, o.advisor.model) : void 0;
|
|
5179
|
+
this.meter = worker || advisor ? meterWorkers(o.ai, o.model) : void 0;
|
|
5180
|
+
const ai = this.meter?.ai ?? o.ai;
|
|
5181
|
+
const adv = advisor ? makeAdvisor({ ai, advisor, request: () => this.lastTask, snapshot: o.advisor.snapshot, limit: o.advisor.limit }) : void 0;
|
|
5019
5182
|
if (o.instructionFiles !== false) {
|
|
5020
5183
|
const names = Array.isArray(o.instructionFiles) ? o.instructionFiles : void 0;
|
|
5021
5184
|
const ins = await loadInstructions(fs, names);
|
|
@@ -5046,7 +5209,7 @@ var Agent = class _Agent {
|
|
|
5046
5209
|
if (tool) tools = [...tools, tool];
|
|
5047
5210
|
}
|
|
5048
5211
|
if (o.host && o.askUserQuestion) tools = [...tools, askUserQuestionTool];
|
|
5049
|
-
if (o.subagents) {
|
|
5212
|
+
if (o.subagents || worker) {
|
|
5050
5213
|
let agents;
|
|
5051
5214
|
if (o.agentsDir) {
|
|
5052
5215
|
const loaded = await loadAgents(fs, o.agentsDir);
|
|
@@ -5054,17 +5217,18 @@ var Agent = class _Agent {
|
|
|
5054
5217
|
if (loaded.catalog) systemPrompt += "\n\n" + loaded.catalog;
|
|
5055
5218
|
}
|
|
5056
5219
|
const taskOpts = {
|
|
5057
|
-
ai
|
|
5058
|
-
model: o.model,
|
|
5220
|
+
ai,
|
|
5221
|
+
model: worker || o.model,
|
|
5059
5222
|
fs,
|
|
5060
5223
|
depth: o.depth,
|
|
5061
|
-
maxDepth: o.maxDepth,
|
|
5224
|
+
maxDepth: worker ? Math.min(o.maxDepth, o.depth + 1) : o.maxDepth,
|
|
5062
5225
|
agents,
|
|
5226
|
+
inheritPostToolUse: !!worker,
|
|
5063
5227
|
hooks: o.hooks,
|
|
5064
5228
|
traceDir: o.traceDir,
|
|
5065
5229
|
permissions: o.permissions,
|
|
5066
5230
|
// a child obeys the parent's allow/deny rules (else Task is a one-call bypass)
|
|
5067
|
-
tools: [...o.tools, ...this.injectedTools],
|
|
5231
|
+
tools: worker ? delegateTools([...o.tools, ...this.injectedTools], o.model) : [...o.tools, ...this.injectedTools],
|
|
5068
5232
|
systemPrompt: o.systemPrompt,
|
|
5069
5233
|
// A child never resumes the parent's own claude-code session (the host's `resume` link is the parent's).
|
|
5070
5234
|
providerOptionsFor: o.providerOptionsFor ?? ((m) => modelProvider(m) === modelProvider(o.model) && o.providerOptions ? { ...o.providerOptions, resume: void 0, onClaudeCodeSession: void 0 } : void 0)
|
|
@@ -5077,6 +5241,15 @@ var Agent = class _Agent {
|
|
|
5077
5241
|
if (plan) tools = [...tools, plan.tool];
|
|
5078
5242
|
this.activeHooks = composeHooks(o.hooks, plan?.hooks, o.permissions?.hooks());
|
|
5079
5243
|
this.activeTools = [...tools, ...this.injectedTools];
|
|
5244
|
+
if (worker) {
|
|
5245
|
+
this.activeTools = delegateTools(this.activeTools, o.model);
|
|
5246
|
+
systemPrompt += delegatePrompt(worker);
|
|
5247
|
+
}
|
|
5248
|
+
if (adv) {
|
|
5249
|
+
this.activeTools = [...adv.gate(this.activeTools), adv.tool];
|
|
5250
|
+
systemPrompt += advisorPrompt(advisor);
|
|
5251
|
+
}
|
|
5252
|
+
this.modeModel = worker || advisor;
|
|
5080
5253
|
this.systemPromptCache = systemPrompt;
|
|
5081
5254
|
this.prepared = true;
|
|
5082
5255
|
return systemPrompt;
|
|
@@ -5089,6 +5262,7 @@ var Agent = class _Agent {
|
|
|
5089
5262
|
this.started = false;
|
|
5090
5263
|
this.invokedTools.clear();
|
|
5091
5264
|
this.fabricatedClaims.clear();
|
|
5265
|
+
this.lastTask = contentText(task);
|
|
5092
5266
|
const systemPrompt = await this.prepare(contentText(task));
|
|
5093
5267
|
const startCtx = await this.fireSessionStart();
|
|
5094
5268
|
const userContent = await this.applyPromptSubmit(task);
|
|
@@ -5124,6 +5298,7 @@ var Agent = class _Agent {
|
|
|
5124
5298
|
*/
|
|
5125
5299
|
async send(task) {
|
|
5126
5300
|
await this.ensureFs();
|
|
5301
|
+
this.lastTask = contentText(task);
|
|
5127
5302
|
const systemPrompt = await this.prepare(contentText(task));
|
|
5128
5303
|
const startCtx = await this.fireSessionStart();
|
|
5129
5304
|
const userContent = await this.applyPromptSubmit(task);
|
|
@@ -5174,6 +5349,12 @@ var Agent = class _Agent {
|
|
|
5174
5349
|
if (next !== this.transcript) this.transcript = next;
|
|
5175
5350
|
}
|
|
5176
5351
|
async runLoop() {
|
|
5352
|
+
this.meter?.reset();
|
|
5353
|
+
const res = await this.loop();
|
|
5354
|
+
const workerUsage = this.meter?.usage();
|
|
5355
|
+
return workerUsage ? { ...res, workerModel: this.meter.model() ?? this.modeModel, workerUsage } : res;
|
|
5356
|
+
}
|
|
5357
|
+
async loop() {
|
|
5177
5358
|
const o = this.options;
|
|
5178
5359
|
const wireTools = toWireTools(this.activeTools);
|
|
5179
5360
|
const useStream = o.stream === true && typeof o.host?.notify === "function";
|
|
@@ -5193,7 +5374,7 @@ var Agent = class _Agent {
|
|
|
5193
5374
|
let lastProgressAt = start;
|
|
5194
5375
|
const turnUser = this.transcript[this.transcript.length - 1];
|
|
5195
5376
|
const kill = (finishReason) => {
|
|
5196
|
-
|
|
5377
|
+
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
5378
|
this.jobs.killAll(`run ended: ${finishReason}`, true);
|
|
5198
5379
|
const claims = this.survivingClaims();
|
|
5199
5380
|
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 +5400,9 @@ var Agent = class _Agent {
|
|
|
5219
5400
|
this.nativeHoldMs = o.nativeHoldMs;
|
|
5220
5401
|
if (isCursorWithTools) {
|
|
5221
5402
|
if (typeof hostToolTimeoutMs === "number" && effectiveToolTimeoutMs !== hostToolTimeoutMs) {
|
|
5222
|
-
|
|
5403
|
+
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
5404
|
}
|
|
5224
|
-
for (const v of toolTimeoutViolations(this.activeTools, effectiveToolTimeoutMs))
|
|
5405
|
+
for (const v of toolTimeoutViolations(this.activeTools, effectiveToolTimeoutMs)) log9.warn(v);
|
|
5225
5406
|
}
|
|
5226
5407
|
const hostExec = async (name, args) => {
|
|
5227
5408
|
const tc = { id: `host-${Date.now()}`, type: "function", function: { name, arguments: JSON.stringify(args) } };
|
|
@@ -5280,7 +5461,7 @@ var Agent = class _Agent {
|
|
|
5280
5461
|
} catch (err) {
|
|
5281
5462
|
if (!this.imagesOff && /does not support .*input/i.test(String(err?.message ?? "")) && sent.some((m) => Array.isArray(m.content) && m.role === "tool")) {
|
|
5282
5463
|
this.imagesOff = true;
|
|
5283
|
-
|
|
5464
|
+
log9.warn(`model refused image input (${err?.message}) \u2014 resending with image Reads as text stubs`);
|
|
5284
5465
|
sent = this.trimContext();
|
|
5285
5466
|
attempt--;
|
|
5286
5467
|
continue;
|
|
@@ -5292,11 +5473,11 @@ var Agent = class _Agent {
|
|
|
5292
5473
|
const empty = err?.code === "empty" || err?.code === "cursor_silent";
|
|
5293
5474
|
const maxAttempts = empty ? Math.max(o.emptyRetries, 0) : 2;
|
|
5294
5475
|
const replaysTool = stalled && this.toolRanThisAttempt;
|
|
5295
|
-
if (replaysTool)
|
|
5476
|
+
if (replaysTool) log9.warn("model stall after tool(s) already ran this step \u2014 not retrying (a retry would replay them)");
|
|
5296
5477
|
const transient = !o.signal?.aborted && !isAbortError(err) && !replaysTool && attempt < maxAttempts && (network || serverSide || stalled || empty);
|
|
5297
5478
|
if (!transient) throw err;
|
|
5298
5479
|
const waitMs = (empty ? o.emptyRetryBaseMs : 1e3) * (attempt + 1);
|
|
5299
|
-
|
|
5480
|
+
log9.warn(`${empty ? "empty model turn" : stalled ? "model stall" : "network drop"} mid-step (${err?.message ?? err}) \u2014 retrying in ${waitMs}ms`);
|
|
5300
5481
|
o.host?.notify?.({ kind: "retry", message: `${empty ? "model returned nothing" : stalled ? "model stalled" : "connection dropped"} \u2014 retrying step (#${attempt + 1})` });
|
|
5301
5482
|
await new Promise((r) => setTimeout(r, waitMs));
|
|
5302
5483
|
}
|
|
@@ -5304,12 +5485,12 @@ var Agent = class _Agent {
|
|
|
5304
5485
|
} catch (err) {
|
|
5305
5486
|
if (err?.code === "budget") return kill("budget");
|
|
5306
5487
|
if (err?.code === "stall") {
|
|
5307
|
-
|
|
5488
|
+
log9.warn(`model stalled out after retries: ${err?.message ?? err}`);
|
|
5308
5489
|
return { ...kill("timeout"), providerFinishReason: "none (stalled)", error: err };
|
|
5309
5490
|
}
|
|
5310
5491
|
if (o.signal?.aborted || isAbortError(err)) return kill("aborted");
|
|
5311
5492
|
if (err?.code === "empty" || err?.code === "cursor_silent") {
|
|
5312
|
-
|
|
5493
|
+
log9.warn(`model returned empty/silent turns after retries \u2014 surfacing as failed`);
|
|
5313
5494
|
const pfr = err?.providerFinishReason;
|
|
5314
5495
|
return { text: "", steps, finishReason: "empty", ...pfr ? { providerFinishReason: pfr } : {}, messages: this.transcript, usage, usageEstimated, error: err };
|
|
5315
5496
|
}
|
|
@@ -5322,13 +5503,13 @@ var Agent = class _Agent {
|
|
|
5322
5503
|
}
|
|
5323
5504
|
if (bodyStr && err instanceof Error && !err.message.includes(bodyStr)) err.detail = bodyStr;
|
|
5324
5505
|
const errLog = err instanceof Error ? { message: err.message, ...err.detail ? { detail: err.detail } : {}, stack: err.stack } : err;
|
|
5325
|
-
|
|
5506
|
+
log9.error(`chat() failed: ${err?.message ?? err}${bodyStr ? ` \u2014 ${bodyStr}` : ""}`, errLog);
|
|
5326
5507
|
return { text: "", steps, finishReason: "error", messages: this.transcript, usage, usageEstimated, error: err };
|
|
5327
5508
|
}
|
|
5328
5509
|
if (o.signal?.aborted) return kill("aborted");
|
|
5329
5510
|
if (res.finishReason === "error" && !res.toolCalls?.length) {
|
|
5330
5511
|
const err = new Error(contentText(res.content ?? "").trim() || "provider error");
|
|
5331
|
-
|
|
5512
|
+
log9.error(`chat() failed: ${err.message}`);
|
|
5332
5513
|
return { text: "", steps, finishReason: "error", messages: this.transcript, usage, usageEstimated, error: err };
|
|
5333
5514
|
}
|
|
5334
5515
|
if (!res.usage) {
|
|
@@ -5396,21 +5577,21 @@ var Agent = class _Agent {
|
|
|
5396
5577
|
if (emptyTurn && o.failEmptyTurns) {
|
|
5397
5578
|
if (!emptyNudged && canNudge()) {
|
|
5398
5579
|
emptyNudged = true;
|
|
5399
|
-
|
|
5580
|
+
log9.warn(`empty model turn after tool work (step ${steps}, provider finishReason=${res.finishReason ?? "n/a"}) \u2014 nudging once`);
|
|
5400
5581
|
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
5582
|
continue;
|
|
5402
5583
|
}
|
|
5403
|
-
|
|
5584
|
+
log9.warn(`model returned empty turns after tool work \u2014 surfacing as failed`);
|
|
5404
5585
|
const e = new Error("empty_response: model returned an empty turn (no text, no tools) after a re-prompt");
|
|
5405
5586
|
e.code = "empty";
|
|
5406
5587
|
return { text: "", steps, finishReason: "empty", ...res.finishReason ? { providerFinishReason: res.finishReason } : {}, messages: this.transcript, usage, usageEstimated, error: e };
|
|
5407
5588
|
}
|
|
5408
5589
|
const fabricated = o.catchFabricatedToolUse && !closureNudged ? this.fabricatedToolNames(contentText(res.content ?? "")) : [];
|
|
5409
5590
|
for (const n of fabricated) this.fabricatedClaims.add(n);
|
|
5410
|
-
if (fabricated.length && !canNudge())
|
|
5591
|
+
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
5592
|
if (fabricated.length && canNudge()) {
|
|
5412
5593
|
closureNudged = true;
|
|
5413
|
-
|
|
5594
|
+
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
5595
|
this.transcript.push({
|
|
5415
5596
|
role: "user",
|
|
5416
5597
|
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 +5600,14 @@ var Agent = class _Agent {
|
|
|
5419
5600
|
}
|
|
5420
5601
|
if (o.closeDelegatedTurns && res.endedWithoutClosing && !closureNudged && canNudge()) {
|
|
5421
5602
|
closureNudged = true;
|
|
5422
|
-
|
|
5603
|
+
log9.verbose("delegated turn went silent after a tool \u2014 nudging a closing answer");
|
|
5423
5604
|
this.transcript.push({
|
|
5424
5605
|
role: "user",
|
|
5425
5606
|
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
5607
|
});
|
|
5427
5608
|
continue;
|
|
5428
5609
|
}
|
|
5429
|
-
|
|
5610
|
+
log9.verbose(`completed in ${steps} step(s)`);
|
|
5430
5611
|
const drained = await this.jobs.drain(o.signal);
|
|
5431
5612
|
if (o.signal?.aborted) return kill("aborted");
|
|
5432
5613
|
if (drained.length) lastProgressAt = Date.now();
|
|
@@ -5460,7 +5641,7 @@ var Agent = class _Agent {
|
|
|
5460
5641
|
if (this.ctx.needsInput) {
|
|
5461
5642
|
const question = this.ctx.needsInput;
|
|
5462
5643
|
this.ctx.needsInput = void 0;
|
|
5463
|
-
|
|
5644
|
+
log9.warn(`needs_input: ${question.question} (steps=${steps})`);
|
|
5464
5645
|
await this.jobs.drain();
|
|
5465
5646
|
const text = [
|
|
5466
5647
|
`OPEN QUESTION (run paused \u2014 no human was reachable): ${question.question}`,
|
|
@@ -5471,7 +5652,7 @@ var Agent = class _Agent {
|
|
|
5471
5652
|
if (this.ctx.endTurn) {
|
|
5472
5653
|
this.ctx.endTurn = void 0;
|
|
5473
5654
|
if (this.drainInjections()) continue;
|
|
5474
|
-
|
|
5655
|
+
log9.verbose(`turn ended by a tool (steps=${steps})`);
|
|
5475
5656
|
await this.jobs.drain(o.signal);
|
|
5476
5657
|
return { text: "", steps, finishReason: "stop", messages: this.transcript, usage, usageEstimated };
|
|
5477
5658
|
}
|
|
@@ -5509,12 +5690,12 @@ var Agent = class _Agent {
|
|
|
5509
5690
|
if (this.toolHoldActive()) {
|
|
5510
5691
|
const now3 = Date.now();
|
|
5511
5692
|
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
|
-
|
|
5693
|
+
log9.warn(`stall watchdog: no stream output for ${stallMs}ms \u2014 held by ${who}`);
|
|
5513
5694
|
this.options.host?.notify?.({ kind: "tool_hold", message: `waiting on ${who}` });
|
|
5514
5695
|
return poke();
|
|
5515
5696
|
}
|
|
5516
5697
|
const inFlight = this.jobs.list().filter((j) => j.status === "running");
|
|
5517
|
-
|
|
5698
|
+
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
5699
|
fired = true;
|
|
5519
5700
|
ac.abort(stallError());
|
|
5520
5701
|
}, stallMs);
|
|
@@ -5684,13 +5865,13 @@ var Agent = class _Agent {
|
|
|
5684
5865
|
const decision = await this.park(Promise.resolve(hooks?.preToolUse?.(call, meta)));
|
|
5685
5866
|
if (decision?.block) {
|
|
5686
5867
|
const blocked = `Blocked by hook: ${decision.reason ?? "no reason given"}`;
|
|
5687
|
-
|
|
5868
|
+
log9.debug(`${tc.function.name} -> ${blocked}`);
|
|
5688
5869
|
await hooks?.postToolUse?.(call, blocked, meta);
|
|
5689
5870
|
return blocked;
|
|
5690
5871
|
}
|
|
5691
5872
|
this.options.host?.notify?.({ kind: "tool_use", id: tc.id ?? "", name: tc.function.name, input: args });
|
|
5692
5873
|
if (earlyError) {
|
|
5693
|
-
|
|
5874
|
+
log9.debug(`${tc.function.name} -> ${earlyError}`);
|
|
5694
5875
|
await hooks?.postToolUse?.(call, earlyError, meta);
|
|
5695
5876
|
this.options.host?.notify?.({ kind: "tool_result", id: tc.id ?? "", output: earlyError, isError: true });
|
|
5696
5877
|
return earlyError;
|
|
@@ -5699,12 +5880,12 @@ var Agent = class _Agent {
|
|
|
5699
5880
|
let images;
|
|
5700
5881
|
let threw = false;
|
|
5701
5882
|
try {
|
|
5702
|
-
|
|
5883
|
+
log9.debug(`${tc.function.name}(${tc.function.arguments})`);
|
|
5703
5884
|
this.ctx.emit = hooks?.onToolOutput ? (chunk) => {
|
|
5704
5885
|
try {
|
|
5705
5886
|
hooks.onToolOutput(call, chunk, meta);
|
|
5706
5887
|
} catch (e) {
|
|
5707
|
-
|
|
5888
|
+
log9.debug(`onToolOutput hook error: ${e}`);
|
|
5708
5889
|
}
|
|
5709
5890
|
} : void 0;
|
|
5710
5891
|
const raw = await tool.run(args, this.ctx);
|
|
@@ -5716,7 +5897,7 @@ var Agent = class _Agent {
|
|
|
5716
5897
|
}
|
|
5717
5898
|
} catch (e) {
|
|
5718
5899
|
const msg = e instanceof Error ? e.message : String(e);
|
|
5719
|
-
|
|
5900
|
+
log9.debug(`${tc.function.name} -> error: ${msg}`);
|
|
5720
5901
|
result = `Error: ${msg}`;
|
|
5721
5902
|
threw = true;
|
|
5722
5903
|
} finally {
|
|
@@ -5993,7 +6174,7 @@ function fitTokenBudget(messages, maxTokens, keepRecentImages = 1, vision = true
|
|
|
5993
6174
|
const ids = callIdSet(messages.slice(from));
|
|
5994
6175
|
while (from < messages.length && messages[from].role === "tool" && !ids.has(messages[from].tool_call_id ?? "")) total -= per[from++];
|
|
5995
6176
|
if (total > maxTokens)
|
|
5996
|
-
|
|
6177
|
+
log9.warn(`context ~${total} tok still over maxContextTokens=${maxTokens} after trimming (system head can't be dropped)`);
|
|
5997
6178
|
return [...head, ...messages.slice(from)];
|
|
5998
6179
|
}
|
|
5999
6180
|
function compact(m, max, focus) {
|
|
@@ -6678,7 +6859,7 @@ init_tools_web();
|
|
|
6678
6859
|
init_tools();
|
|
6679
6860
|
init_tools_structured();
|
|
6680
6861
|
init_logging();
|
|
6681
|
-
var
|
|
6862
|
+
var log11 = forComponent("scratch");
|
|
6682
6863
|
var SCRATCH_DIR = "/scratch";
|
|
6683
6864
|
function shortArgs(args) {
|
|
6684
6865
|
try {
|
|
@@ -6718,7 +6899,7 @@ var Scratch = class {
|
|
|
6718
6899
|
await (this.dirReady ??= mkdirp(this.fs, dir));
|
|
6719
6900
|
await this.fs.writeFile(path, header + raw);
|
|
6720
6901
|
} catch (e) {
|
|
6721
|
-
|
|
6902
|
+
log11.debug("scratch write failed; returning raw", e);
|
|
6722
6903
|
return raw;
|
|
6723
6904
|
}
|
|
6724
6905
|
const preview = raw.slice(0, previewChars).replace(/\s+/g, " ").trim();
|
|
@@ -6748,7 +6929,7 @@ To pull a specific detail, Grep/Read ${path}, or call Ask({ question: "\u2026",
|
|
|
6748
6929
|
await (this.dirReady ??= mkdirp(this.fs, dir));
|
|
6749
6930
|
await this.fs.writeFile(path, header + full);
|
|
6750
6931
|
} catch (e) {
|
|
6751
|
-
|
|
6932
|
+
log11.debug("scratch spill failed; cropping lossy", e);
|
|
6752
6933
|
return full.slice(0, pageBytes) + `
|
|
6753
6934
|
|
|
6754
6935
|
[output cropped to ${pageBytes} of ${full.length} bytes; full output unavailable (scratch write failed) \u2014 refine your query]`;
|
|
@@ -6794,7 +6975,7 @@ Question: ${q}`);
|
|
|
6794
6975
|
const answer = (res.text ?? "").trim();
|
|
6795
6976
|
return answer || "(no answer found in scratch)";
|
|
6796
6977
|
} catch (e) {
|
|
6797
|
-
|
|
6978
|
+
log11.debug("Ask peek failed", e);
|
|
6798
6979
|
return `Error querying scratch: ${e?.message ?? e}`;
|
|
6799
6980
|
}
|
|
6800
6981
|
}
|
|
@@ -6803,7 +6984,7 @@ Question: ${q}`);
|
|
|
6803
6984
|
|
|
6804
6985
|
// src/lessons.ts
|
|
6805
6986
|
init_logging();
|
|
6806
|
-
var
|
|
6987
|
+
var log12 = forComponent("Lessons");
|
|
6807
6988
|
var LessonOptionsDefaults = class {
|
|
6808
6989
|
minRepeats = 2;
|
|
6809
6990
|
};
|
|
@@ -6828,8 +7009,8 @@ function lessonCapture(options) {
|
|
|
6828
7009
|
counts.set(lesson.slug, n);
|
|
6829
7010
|
if (n < o.minRepeats) return;
|
|
6830
7011
|
written.add(lesson.slug);
|
|
6831
|
-
await writeFact(o.fs, o.dir, lesson.slug, lesson.body).catch((e) =>
|
|
6832
|
-
|
|
7012
|
+
await writeFact(o.fs, o.dir, lesson.slug, lesson.body).catch((e) => log12.warn(`could not persist ${lesson.slug}: ${e?.message ?? e}`));
|
|
7013
|
+
log12.debug(`captured lesson ${lesson.slug} (recurred ${n}\xD7)`);
|
|
6833
7014
|
}
|
|
6834
7015
|
};
|
|
6835
7016
|
}
|
|
@@ -6837,7 +7018,7 @@ function lessonCapture(options) {
|
|
|
6837
7018
|
// src/reflect.ts
|
|
6838
7019
|
init_llm();
|
|
6839
7020
|
init_logging();
|
|
6840
|
-
var
|
|
7021
|
+
var log13 = forComponent("Reflect");
|
|
6841
7022
|
async function reflectOnRun(o) {
|
|
6842
7023
|
const digest = digestRun(o.result.messages, o.maxDigestChars ?? 6e3);
|
|
6843
7024
|
if (!digest.trim()) return null;
|
|
@@ -6853,7 +7034,7 @@ If the run was fine or the issue was purely task-specific (not generalizable), r
|
|
|
6853
7034
|
const r = await o.ai.chat({ model: o.model, messages: [{ role: "user", content: prompt }], stream: false });
|
|
6854
7035
|
text = r?.content ?? "";
|
|
6855
7036
|
} catch (e) {
|
|
6856
|
-
|
|
7037
|
+
log13.warn(`reflection call failed: ${e?.message ?? e}`);
|
|
6857
7038
|
return null;
|
|
6858
7039
|
}
|
|
6859
7040
|
const m = text.match(/LESSON:\s*(.+)/i);
|
|
@@ -6863,10 +7044,10 @@ If the run was fine or the issue was purely task-specific (not generalizable), r
|
|
|
6863
7044
|
try {
|
|
6864
7045
|
await writeFact(o.fs, o.dir, slug2, lesson);
|
|
6865
7046
|
} catch (e) {
|
|
6866
|
-
|
|
7047
|
+
log13.warn(`could not persist lesson: ${e?.message ?? e}`);
|
|
6867
7048
|
return null;
|
|
6868
7049
|
}
|
|
6869
|
-
|
|
7050
|
+
log13.debug(`reflection persisted ${slug2}`);
|
|
6870
7051
|
return slug2;
|
|
6871
7052
|
}
|
|
6872
7053
|
function digestRun(messages, maxChars) {
|
|
@@ -6916,7 +7097,7 @@ function forComponent2(name) {
|
|
|
6916
7097
|
}
|
|
6917
7098
|
|
|
6918
7099
|
// node_modules/@bod.ee/voice/src/core/emotion.ts
|
|
6919
|
-
var
|
|
7100
|
+
var log14 = forComponent2("Emotion");
|
|
6920
7101
|
var EMOTIONS = [
|
|
6921
7102
|
// primary (best results)
|
|
6922
7103
|
"neutral",
|
|
@@ -7070,7 +7251,7 @@ var EmotionStream = class {
|
|
|
7070
7251
|
if (tag) {
|
|
7071
7252
|
this.pending = tag;
|
|
7072
7253
|
if (this.show) display += m[0];
|
|
7073
|
-
} else
|
|
7254
|
+
} else log14.debug(`dropping unknown emotion tag ${m[0]}`);
|
|
7074
7255
|
last = m.index + m[0].length;
|
|
7075
7256
|
}
|
|
7076
7257
|
this.emit(body.slice(last), (s, d, p) => {
|
|
@@ -7217,7 +7398,7 @@ function computeHoldMs(ctx) {
|
|
|
7217
7398
|
}
|
|
7218
7399
|
|
|
7219
7400
|
// node_modules/@bod.ee/voice/src/core/engine.ts
|
|
7220
|
-
var
|
|
7401
|
+
var log15 = forComponent2("VoiceEngine");
|
|
7221
7402
|
var realClock = {
|
|
7222
7403
|
now: () => performance.now(),
|
|
7223
7404
|
setTimeout: (fn, ms) => setTimeout(fn, ms),
|
|
@@ -7605,8 +7786,8 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
7605
7786
|
this.tts.warmup?.();
|
|
7606
7787
|
this.setState("listening");
|
|
7607
7788
|
if (this.options.bargeInMode === "aec" && !this.stt.usingAec)
|
|
7608
|
-
|
|
7609
|
-
|
|
7789
|
+
log15.warn("bargeInMode=aec but mic does not report echoCancellation applied \u2014 STT may transcribe the assistant");
|
|
7790
|
+
log15.debug(`voice I/O up (${this.usingAec ? "AEC" : "heuristic echo"} capture, mode=${this.options.bargeInMode}, mic reports aec=${this.stt.usingAec})`);
|
|
7610
7791
|
}
|
|
7611
7792
|
/** The RESOLVED barge-in tier (the single seam every tier branch reads). `bargeInMode` overrides
|
|
7612
7793
|
* the mic's self-report — see the option doc for why the report is not trustworthy on mobile. */
|
|
@@ -7631,7 +7812,7 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
7631
7812
|
this.options.onDiag({ t: this.clock.now(), kind, ...fields });
|
|
7632
7813
|
} catch (e) {
|
|
7633
7814
|
this.diagOn = false;
|
|
7634
|
-
|
|
7815
|
+
log15.debug(`onDiag threw \u2014 diagnostics disabled: ${e instanceof Error ? e.message : e}`);
|
|
7635
7816
|
}
|
|
7636
7817
|
}
|
|
7637
7818
|
idleWaiters = [];
|
|
@@ -7770,7 +7951,7 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
7770
7951
|
}
|
|
7771
7952
|
for (const w of this.words(this.reply)) this.echoWords.add(w);
|
|
7772
7953
|
this.tts.speak(forSpeech(speech), true);
|
|
7773
|
-
if (!this.spokeDeltas && this.turnStartAt)
|
|
7954
|
+
if (!this.spokeDeltas && this.turnStartAt) log15.debug(`ttft: ${Math.round(this.clock.now() - this.turnStartAt)}ms`);
|
|
7774
7955
|
this.spokeDeltas = true;
|
|
7775
7956
|
this.setState("speaking");
|
|
7776
7957
|
if (this.options.revealMode !== "off" && display) {
|
|
@@ -7883,13 +8064,13 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
7883
8064
|
this.drainTimer = null;
|
|
7884
8065
|
this.speaking = false;
|
|
7885
8066
|
this.stopWordReveal(true);
|
|
7886
|
-
if (this.turnStartAt)
|
|
8067
|
+
if (this.turnStartAt) log15.debug(`turn: ${Math.round(this.clock.now() - this.turnStartAt)}ms (incl. playback)`);
|
|
7887
8068
|
this.echoUntil = this.clock.now() + 2500;
|
|
7888
8069
|
if (!this.usingAec) this.stt.reset();
|
|
7889
8070
|
try {
|
|
7890
8071
|
this.options.onSpeechEnd();
|
|
7891
8072
|
} catch (e) {
|
|
7892
|
-
|
|
8073
|
+
log15.warn(`onSpeechEnd threw: ${e instanceof Error ? e.message : e}`);
|
|
7893
8074
|
}
|
|
7894
8075
|
this.setState("listening");
|
|
7895
8076
|
if (this.uttQueue.length) this.pumpQueue();
|
|
@@ -7963,7 +8144,7 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
7963
8144
|
if (!host) return pick;
|
|
7964
8145
|
const ctx = { text: this.lastDispatchText, question: this.lastDispatchWasQuestion, barged: this.bargedBeforeDispatch };
|
|
7965
8146
|
const fallback = (e) => {
|
|
7966
|
-
|
|
8147
|
+
log15.warn(`pickAck failed \u2014 using the default pools: ${e instanceof Error ? e.message : e}`);
|
|
7967
8148
|
pick.picked = false;
|
|
7968
8149
|
return void 0;
|
|
7969
8150
|
};
|
|
@@ -8053,7 +8234,7 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
8053
8234
|
this.ackProbe = null;
|
|
8054
8235
|
const queued = this.uttQueue.splice(0);
|
|
8055
8236
|
const droppedQueued = queued.length;
|
|
8056
|
-
if (droppedQueued)
|
|
8237
|
+
if (droppedQueued) log15.info(`barge-in cut ${droppedQueued} queued worker utterance(s) \u2014 handed to the host`);
|
|
8057
8238
|
this.overlapKeptUntil = 0;
|
|
8058
8239
|
if (!this.speaking && !this.drainTimer) {
|
|
8059
8240
|
if (droppedQueued) this.lastInterrupted = { full: "", heard: "", queued, queuedTurn: false };
|
|
@@ -8282,7 +8463,7 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
8282
8463
|
if (flat && this.lastDispatchFlat && this.clock.now() - this.lastDispatchAt < _VoiceEngine.DUP_FINAL_MS && (this.lastDispatchFlat.startsWith(flat) || flat.startsWith(this.lastDispatchFlat))) return;
|
|
8283
8464
|
this.specSpent = true;
|
|
8284
8465
|
this.specText = this.specPartial;
|
|
8285
|
-
|
|
8466
|
+
log15.debug(`speculate: "${this.specText.slice(0, 60)}"`);
|
|
8286
8467
|
this.diag("speculate", { text: this.specText });
|
|
8287
8468
|
this.options.onSpeculate(this.specText);
|
|
8288
8469
|
}, this.options.speculativeMs);
|
|
@@ -8351,7 +8532,7 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
8351
8532
|
};
|
|
8352
8533
|
this.tts.newContext();
|
|
8353
8534
|
this.tts.speak(phrase, false);
|
|
8354
|
-
|
|
8535
|
+
log15.debug(`backchannel: "${phrase}"`);
|
|
8355
8536
|
this.diag("backchannel", { phrase });
|
|
8356
8537
|
this.options.onBackchannel(phrase);
|
|
8357
8538
|
}
|
|
@@ -8384,7 +8565,7 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
8384
8565
|
});
|
|
8385
8566
|
if (idx < 0) return text;
|
|
8386
8567
|
toks.splice(idx, 1);
|
|
8387
|
-
|
|
8568
|
+
log15.verbose(`stripped backchannel echo "${this.lastBcPhrase}" from dispatching final`);
|
|
8388
8569
|
return toks.join(" ");
|
|
8389
8570
|
}
|
|
8390
8571
|
/** Merge a resumed utterance into the pending one, deduping any word-overlap. Soniox re-finalizes
|
|
@@ -8570,7 +8751,7 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
8570
8751
|
}
|
|
8571
8752
|
const bargeHold = !this.looksIncomplete(this.pendingUtt) && this.unfinishedBargeLine(this.pendingUtt);
|
|
8572
8753
|
if (this.options.incompleteMergeMs && (bargeHold || this.looksIncomplete(this.pendingUtt))) {
|
|
8573
|
-
|
|
8754
|
+
log15.verbose(`hold: incomplete utterance "${this.pendingUtt.slice(-40)}"`);
|
|
8574
8755
|
this.diag("hold", { reason: bargeHold ? "barge_unfinished" : "incomplete", tail: this.pendingUtt.slice(-40) });
|
|
8575
8756
|
this.options.onHold();
|
|
8576
8757
|
if (this.options.holdFiller && !this.speaking) this.speakFiller(this.options.holdFiller);
|
|
@@ -8609,13 +8790,13 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
8609
8790
|
const answered = this.answeredSinceDispatch();
|
|
8610
8791
|
const part = flat !== this.lastDispatchFlat && !answered && this.echoesDispatch(text);
|
|
8611
8792
|
if (part || flat && flat === this.lastDispatchFlat && !answered && this.clock.now() - this.lastDispatchAt < _VoiceEngine.DUP_FINAL_MS) {
|
|
8612
|
-
|
|
8793
|
+
log15.verbose(`dropped duplicate final "${text.slice(0, 40)}" (identical to the just-dispatched utterance, no reply between)`);
|
|
8613
8794
|
this.diag("utterance_drop", { reason: "dup_final", text: text.slice(0, 60) });
|
|
8614
8795
|
return;
|
|
8615
8796
|
}
|
|
8616
8797
|
const refinal = this.refinalizes(text);
|
|
8617
8798
|
if (refinal && this.repliedSinceDispatch) {
|
|
8618
|
-
|
|
8799
|
+
log15.verbose(`dropped re-finalization "${text.slice(0, 60)}" (superset of the just-dispatched utterance, reply already flowing)`);
|
|
8619
8800
|
this.diag("utterance_drop", { reason: "refinalized", text: text.slice(0, 60) });
|
|
8620
8801
|
this.lastDispatchFlat = flat;
|
|
8621
8802
|
this.lastDispatchWords = text.trim().split(/\s+/);
|
|
@@ -8894,7 +9075,7 @@ var SpokenSplitter = class {
|
|
|
8894
9075
|
};
|
|
8895
9076
|
|
|
8896
9077
|
// node_modules/@bod.ee/voice/src/adapters/soniox.ts
|
|
8897
|
-
var
|
|
9078
|
+
var log16 = forComponent2("SonioxSTT");
|
|
8898
9079
|
var now = () => performance.now();
|
|
8899
9080
|
var SonioxSTTOptions = class {
|
|
8900
9081
|
auth = "";
|
|
@@ -8939,7 +9120,7 @@ var SonioxSTT = class {
|
|
|
8939
9120
|
this.onDiag({ t: now(), kind, ...fields });
|
|
8940
9121
|
} catch (e) {
|
|
8941
9122
|
this.diagOn = false;
|
|
8942
|
-
|
|
9123
|
+
log16.debug(`onDiag threw \u2014 STT diagnostics disabled: ${e instanceof Error ? e.message : e}`);
|
|
8943
9124
|
}
|
|
8944
9125
|
}
|
|
8945
9126
|
lastChunkAt = 0;
|
|
@@ -8981,11 +9162,11 @@ var SonioxSTT = class {
|
|
|
8981
9162
|
this.ws.onmessage = (ev) => this.handle(JSON.parse(String(ev.data)));
|
|
8982
9163
|
this.ws.onclose = (ev) => {
|
|
8983
9164
|
if (this.stopped) return;
|
|
8984
|
-
|
|
9165
|
+
log16.warn(`soniox ws closed (${ev.code} ${ev.reason || ""}) \u2014 reconnecting`);
|
|
8985
9166
|
this.diag("stt_ws_closed", { code: ev.code, reason: String(ev.reason || ""), reconnecting: true });
|
|
8986
9167
|
this.reset();
|
|
8987
9168
|
this.connectWs().catch((e) => {
|
|
8988
|
-
|
|
9169
|
+
log16.error(`soniox reconnect failed: ${e.message}`);
|
|
8989
9170
|
this.diag("stt_reconnect_failed", { message: e.message });
|
|
8990
9171
|
});
|
|
8991
9172
|
};
|
|
@@ -8997,7 +9178,7 @@ var SonioxSTT = class {
|
|
|
8997
9178
|
this.endpointTimer = setInterval(() => {
|
|
8998
9179
|
const combined = (this.finalText + this.partialText).trim();
|
|
8999
9180
|
if (!combined || now() - this.lastChangeAt < this.options.silenceEndpointMs) return;
|
|
9000
|
-
if (this.firstTokenAt)
|
|
9181
|
+
if (this.firstTokenAt) log16.debug(`stt: ${Math.round(now() - this.firstTokenAt)}ms first-token\u2192silence-endpoint, "${combined.slice(0, 60)}"`);
|
|
9001
9182
|
this.reset();
|
|
9002
9183
|
this.onUtterance(combined, now());
|
|
9003
9184
|
}, 120);
|
|
@@ -9009,7 +9190,7 @@ var SonioxSTT = class {
|
|
|
9009
9190
|
if (this.stopped) return;
|
|
9010
9191
|
const ref = this.lastChunkAt || this.startedChunksAt;
|
|
9011
9192
|
if (now() - ref > noAudioMs) {
|
|
9012
|
-
|
|
9193
|
+
log16.error(`stt: no mic audio for >${Math.round(noAudioMs / 1e3)}s \u2014 capture device stopped delivering`);
|
|
9013
9194
|
this.diag("stt_watchdog_fatal", { noAudioMs });
|
|
9014
9195
|
this.onFatal("microphone stopped delivering audio (try a different input device, e.g. AirPods, or check System Settings \u2192 Sound \u2192 Input)");
|
|
9015
9196
|
this.stop();
|
|
@@ -9032,7 +9213,7 @@ var SonioxSTT = class {
|
|
|
9032
9213
|
handle(m) {
|
|
9033
9214
|
if (m.error_message) {
|
|
9034
9215
|
this.diag("stt_error", { message: String(m.error_message), code: m.error_code });
|
|
9035
|
-
return
|
|
9216
|
+
return log16.error(`soniox: ${m.error_message}`);
|
|
9036
9217
|
}
|
|
9037
9218
|
let endpoint = false;
|
|
9038
9219
|
for (const t of m.tokens ?? []) {
|
|
@@ -9049,7 +9230,7 @@ var SonioxSTT = class {
|
|
|
9049
9230
|
this.onPartial(combined);
|
|
9050
9231
|
if (endpoint && this.finalText.trim()) {
|
|
9051
9232
|
const utterance = this.finalText.trim();
|
|
9052
|
-
if (this.firstTokenAt)
|
|
9233
|
+
if (this.firstTokenAt) log16.debug(`stt: ${Math.round(now() - this.firstTokenAt)}ms first-token\u2192endpoint, "${utterance.slice(0, 60)}"`);
|
|
9053
9234
|
this.reset();
|
|
9054
9235
|
this.onUtterance(utterance, now());
|
|
9055
9236
|
}
|
|
@@ -9071,7 +9252,7 @@ var SonioxSTT = class {
|
|
|
9071
9252
|
};
|
|
9072
9253
|
|
|
9073
9254
|
// node_modules/@bod.ee/voice/src/adapters/cartesia.ts
|
|
9074
|
-
var
|
|
9255
|
+
var log17 = forComponent2("CartesiaTTS");
|
|
9075
9256
|
var now2 = () => performance.now();
|
|
9076
9257
|
var CartesiaTTSOptions = class {
|
|
9077
9258
|
auth = "";
|
|
@@ -9107,7 +9288,7 @@ var CartesiaTTS = class _CartesiaTTS {
|
|
|
9107
9288
|
this.onDiag({ t: now2(), kind, ...fields });
|
|
9108
9289
|
} catch (e) {
|
|
9109
9290
|
this.diagOn = false;
|
|
9110
|
-
|
|
9291
|
+
log17.debug(`onDiag threw \u2014 TTS diagnostics disabled: ${e instanceof Error ? e.message : e}`);
|
|
9111
9292
|
}
|
|
9112
9293
|
}
|
|
9113
9294
|
firstAudioAt = 0;
|
|
@@ -9142,11 +9323,11 @@ var CartesiaTTS = class _CartesiaTTS {
|
|
|
9142
9323
|
this.ws.onerror = (e) => rej(new Error(`cartesia ws: ${e.message || "connect failed"}`));
|
|
9143
9324
|
});
|
|
9144
9325
|
this.ws.onclose = (ev) => {
|
|
9145
|
-
|
|
9326
|
+
log17.warn(`cartesia ws closed (${ev.code} ${ev.reason || ""})`);
|
|
9146
9327
|
this.diag("tts_ws_closed", { code: ev.code, reason: String(ev.reason || ""), reconnecting: !this.closed });
|
|
9147
9328
|
if (!this.closed) {
|
|
9148
9329
|
this.connecting = this.doConnect().catch((e) => {
|
|
9149
|
-
|
|
9330
|
+
log17.error(`cartesia reconnect failed: ${e.message}`);
|
|
9150
9331
|
this.diag("tts_reconnect_failed", { message: e.message });
|
|
9151
9332
|
});
|
|
9152
9333
|
}
|
|
@@ -9174,12 +9355,12 @@ var CartesiaTTS = class _CartesiaTTS {
|
|
|
9174
9355
|
this.down = true;
|
|
9175
9356
|
this.downAt = now2();
|
|
9176
9357
|
this.consecutiveOk = 0;
|
|
9177
|
-
|
|
9358
|
+
log17.warn(`TTS circuit breaker open \u2014 ${this.consecutiveErrors} consecutive errors, switching to text-only`);
|
|
9178
9359
|
this.diag("tts_breaker_open", { errors: this.consecutiveErrors });
|
|
9179
9360
|
this.onDone();
|
|
9180
9361
|
this.startProbe();
|
|
9181
9362
|
} else if (!this.down) {
|
|
9182
|
-
(/No valid transcripts/i.test(m.message || "") ?
|
|
9363
|
+
(/No valid transcripts/i.test(m.message || "") ? log17.debug : log17.warn)(`cartesia: ${JSON.stringify(m)}`);
|
|
9183
9364
|
}
|
|
9184
9365
|
}
|
|
9185
9366
|
};
|
|
@@ -9194,7 +9375,7 @@ var CartesiaTTS = class _CartesiaTTS {
|
|
|
9194
9375
|
this.stopProbe();
|
|
9195
9376
|
const downMs = this.downAt ? now2() - this.downAt : 0;
|
|
9196
9377
|
this.diag("tts_breaker_close", { downMs: Math.round(downMs) });
|
|
9197
|
-
(downMs < 2e3 ?
|
|
9378
|
+
(downMs < 2e3 ? log17.debug : log17.info)(`TTS recovered${downMs ? ` (down ${downMs}ms)` : ""}`);
|
|
9198
9379
|
}
|
|
9199
9380
|
/** Ensure the WS is open before sending — reconnects if idle-closed. */
|
|
9200
9381
|
async ensureConnected() {
|
|
@@ -9277,13 +9458,13 @@ function base64ToBytes(b64) {
|
|
|
9277
9458
|
}
|
|
9278
9459
|
|
|
9279
9460
|
// node_modules/@bod.ee/voice/src/adapters/fish.ts
|
|
9280
|
-
var
|
|
9461
|
+
var log18 = forComponent2("FishTTS");
|
|
9281
9462
|
|
|
9282
9463
|
// node_modules/@bod.ee/voice/src/adapters/grok.ts
|
|
9283
|
-
var
|
|
9464
|
+
var log19 = forComponent2("GrokTTS");
|
|
9284
9465
|
|
|
9285
9466
|
// node_modules/@bod.ee/voice/src/adapters/grok-stt.ts
|
|
9286
|
-
var
|
|
9467
|
+
var log20 = forComponent2("GrokSTT");
|
|
9287
9468
|
|
|
9288
9469
|
// src/voice/engine.ts
|
|
9289
9470
|
init_logging();
|
|
@@ -9299,7 +9480,7 @@ import { AIClient, DecideClient, JevAdapter, LlmDecideAdapter } from "ai.libx.js
|
|
|
9299
9480
|
|
|
9300
9481
|
// src/voice/overlap.ts
|
|
9301
9482
|
init_logging();
|
|
9302
|
-
var
|
|
9483
|
+
var log21 = forComponent("Overlap");
|
|
9303
9484
|
var norm2 = (s) => s.toLowerCase().replace(/[’']/g, "'").replace(/[^\p{L}\p{N}' -]+/gu, " ").replace(/\s+/g, " ").trim();
|
|
9304
9485
|
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
9486
|
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 +9560,8 @@ var OverlapJudge = class {
|
|
|
9379
9560
|
const t0 = this.o.now();
|
|
9380
9561
|
const done = (verdict, by) => {
|
|
9381
9562
|
const d = { verdict, by, ms: Math.round(this.o.now() - t0) };
|
|
9382
|
-
if (by === "late" || by === "error" || by === "none")
|
|
9383
|
-
else
|
|
9563
|
+
if (by === "late" || by === "error" || by === "none") log21.warn(`overlap "${heard.slice(0, 60)}": no verdict (${by}, ${d.ms}ms) \u2192 cut`);
|
|
9564
|
+
else log21.debug(`overlap "${heard.slice(0, 60)}": ${verdict} by ${by} ${d.ms}ms`);
|
|
9384
9565
|
this.o.onDecision(heard, d);
|
|
9385
9566
|
return d;
|
|
9386
9567
|
};
|
|
@@ -9397,7 +9578,7 @@ var OverlapJudge = class {
|
|
|
9397
9578
|
});
|
|
9398
9579
|
try {
|
|
9399
9580
|
const v = await Promise.race([this.o.model(heard, ctl.signal).catch((e) => {
|
|
9400
|
-
if (!ctl.signal.aborted)
|
|
9581
|
+
if (!ctl.signal.aborted) log21.warn(`overlap model failed: ${e.message}`);
|
|
9401
9582
|
return "error";
|
|
9402
9583
|
}), late]);
|
|
9403
9584
|
if (v === "late" || v === "error") return done("cut", v);
|
|
@@ -9413,14 +9594,14 @@ function overlapModelFor(ai, env = process.env) {
|
|
|
9413
9594
|
const m = fastOverlapModel(ai, model);
|
|
9414
9595
|
const t0 = performance.now();
|
|
9415
9596
|
void m("yeah", AbortSignal.timeout(1e4)).then(
|
|
9416
|
-
() =>
|
|
9417
|
-
(e) =>
|
|
9597
|
+
() => log21.debug(`fast model warm in ${Math.round(performance.now() - t0)}ms`),
|
|
9598
|
+
(e) => log21.warn(`fast model warm-up failed (the first overlap verdict may be late): ${e?.message ?? e}`)
|
|
9418
9599
|
);
|
|
9419
9600
|
return m;
|
|
9420
9601
|
}
|
|
9421
9602
|
|
|
9422
9603
|
// src/voice/instinct.ts
|
|
9423
|
-
var
|
|
9604
|
+
var log22 = forComponent("Instinct");
|
|
9424
9605
|
var ACK_SITUATIONS = {
|
|
9425
9606
|
hard_question: `a question that takes real thought, judgment or looking something up ("what's the biggest takeaway?", "how should I structure this?")`,
|
|
9426
9607
|
simple_question: 'a quick question with an instant, obvious answer ("how are you?", "what time is it?", "can you hear me?")',
|
|
@@ -9470,7 +9651,7 @@ function interruptionNote(cut, intent) {
|
|
|
9470
9651
|
|
|
9471
9652
|
// src/voice/unsaid.ts
|
|
9472
9653
|
init_logging();
|
|
9473
|
-
var
|
|
9654
|
+
var log23 = forComponent("Unsaid");
|
|
9474
9655
|
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
9656
|
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
9657
|
var SAID_OVERLAP = 0.6;
|
|
@@ -9510,7 +9691,7 @@ async function unsaidClauses(pieces, spoken, decider, timeoutMs = 5e3) {
|
|
|
9510
9691
|
return p < CONVEYED_MIN;
|
|
9511
9692
|
}).map((x) => x.c);
|
|
9512
9693
|
} catch (e) {
|
|
9513
|
-
|
|
9694
|
+
log23.warn(`unsaid judge failed \u2014 only near-zero-overlap clauses count as unsaid: ${e.message}`);
|
|
9514
9695
|
return fallback();
|
|
9515
9696
|
} finally {
|
|
9516
9697
|
clearTimeout(timer);
|
|
@@ -9631,7 +9812,7 @@ var RESULT_CLAIM_RE = new RegExp(LEAD_TAGS + String.raw`(?:(?:all\s+)?done|finis
|
|
|
9631
9812
|
var DONE_CLAIM_RE = new RegExp(LEAD_TAGS + String.raw`(?:(?:all\s+)?done|finished|it['’]?s\s+(?:done|finished)|completed?)\b`, "i");
|
|
9632
9813
|
|
|
9633
9814
|
// src/voice/channelLang.ts
|
|
9634
|
-
var
|
|
9815
|
+
var log24 = forComponent("ChannelBanks");
|
|
9635
9816
|
var LINE_KINDS = ["backchannel", "filler", "bridge"];
|
|
9636
9817
|
var BANK_KEYS = [...LINE_KINDS, "phatic"];
|
|
9637
9818
|
var EN_PHATIC = [
|
|
@@ -9695,8 +9876,17 @@ function isPhatic(text, bank) {
|
|
|
9695
9876
|
return !!q && (t === q || t.startsWith(q + " "));
|
|
9696
9877
|
});
|
|
9697
9878
|
}
|
|
9879
|
+
var CLOSER = new Set("ok okay k cool great nice perfect awesome amazing lovely excellent alright fine good thanks thank thx you so much very a lot bye goodbye cheers later see ya night got it i that that's thats is it's its all for now sounds understood noted makes sense oh ah wow right then just \u05EA\u05D5\u05D3\u05D4 \u05E8\u05D1\u05D4 \u05E1\u05D1\u05D1\u05D4 \u05D0\u05D5\u05E7\u05D9\u05D9 \u05D0\u05D5\u05E7\u05D9 \u05D1\u05E1\u05D3\u05E8 \u05D9\u05D5\u05E4\u05D9 \u05D0\u05D7\u05DC\u05D4 \u05DE\u05E2\u05D5\u05DC\u05D4 \u05DE\u05E6\u05D5\u05D9\u05DF \u05DE\u05D2\u05E0\u05D9\u05D1 \u05E0\u05D4\u05D3\u05E8 \u05D1\u05D9\u05D9 \u05DC\u05D4\u05EA\u05E8\u05D0\u05D5\u05EA \u05D8\u05D5\u05D1 \u05D6\u05D4\u05D5 \u05D6\u05D4 \u05D4\u05DB\u05DC \u05D1\u05D9\u05E0\u05EA\u05D9\u05D9\u05DD \u05D4\u05D1\u05E0\u05EA\u05D9 \u05DE\u05DE\u05E9 \u05DC\u05DA \u05D0\u05D6".split(" "));
|
|
9880
|
+
function turnIntent(text) {
|
|
9881
|
+
const t = text.trim();
|
|
9882
|
+
if (/\?[\s"')\]]*$/.test(t)) return "ask";
|
|
9883
|
+
const w = norm3(t).split(" ").filter(Boolean);
|
|
9884
|
+
const asks = requestOpener(t) && !/^ת/.test(lead(t)[0] ?? "") || pendingQuestion(t);
|
|
9885
|
+
if (w.length && w.length <= 8 && w.every((x) => CLOSER.has(x)) && !asks) return "closer";
|
|
9886
|
+
return asks || requestOpener(t) ? "ask" : "say";
|
|
9887
|
+
}
|
|
9698
9888
|
var SKIP = new Set("um uh uhm erm hmm mm oh ok okay so no yes yeah yep well hey hi and but right alright actually like now then wait hang on me speaker \u05D0\u05D5\u05E7\u05D9\u05D9 \u05D8\u05D5\u05D1 \u05D0\u05D6 \u05DC\u05D0 \u05DB\u05DF \u05E8\u05D2\u05E2 \u05D0\u05D4 \u05D0\u05DE \u05D9\u05D0\u05DC\u05DC\u05D4 \u05D5".split(" "));
|
|
9699
|
-
var ASK = "what whats how why where when who which whose is are was were do does did can could would will should shall have has may isnt arent dont doesnt didnt cant wont check open show tell find search look send run go lets please take give make write explain select focus order try compare list use set activate highlight outline capture start stop read play call book create add remove delete put type click close switch help get summarize translate mark keep remind schedule \u05DE\u05D4 \u05D0\u05D9\u05DA \u05DC\u05DE\u05D4 \u05D0\u05D9\u05E4\u05D4 \u05DE\u05EA\u05D9 \u05DE\u05D9 \u05D4\u05D0\u05DD \u05DB\u05DE\u05D4 \u05EA\u05DF \u05EA\u05E0\u05D9 \u05EA\u05D2\u05D9\u05D3 \u05EA\u05D2\u05D9\u05D3\u05D9 \u05D1\u05D5\u05D0 \u05D1\u05D5\u05D0\u05D9".split(" ");
|
|
9889
|
+
var ASK = "what whats how why where when who which whose is are was were do does did can could would will should shall have has may isnt arent dont doesnt didnt cant wont check open show tell find search look send run go lets please take give make write explain select focus order try compare list use set activate highlight outline capture start stop read play call book create add remove delete put type click close switch help get name recommend suggest summarize translate mark keep remind schedule \u05DE\u05D4 \u05D0\u05D9\u05DA \u05DC\u05DE\u05D4 \u05D0\u05D9\u05E4\u05D4 \u05DE\u05EA\u05D9 \u05DE\u05D9 \u05D4\u05D0\u05DD \u05DB\u05DE\u05D4 \u05EA\u05DF \u05EA\u05E0\u05D9 \u05EA\u05D2\u05D9\u05D3 \u05EA\u05D2\u05D9\u05D3\u05D9 \u05D1\u05D5\u05D0 \u05D1\u05D5\u05D0\u05D9".split(" ");
|
|
9700
9890
|
var WANT = /^(i (want|need|would like|d like|wanna)|אני (רוצה|צריך|צריכה|מבקש|מבקשת))(\s|$)/u;
|
|
9701
9891
|
var lead = (text) => {
|
|
9702
9892
|
const w = text.toLowerCase().replace(/[^\p{L}\p{N}\s]+/gu, "").split(/\s+/).filter(Boolean);
|
|
@@ -9776,11 +9966,11 @@ function cleanLine(s, lang, maxWords = 6) {
|
|
|
9776
9966
|
const raw = s.trim().split("\n")[0];
|
|
9777
9967
|
const l = raw.replace(/^[-*•\d.)\s]+/, "").replace(/^["'“”«»]+|["'“”«»]+$/g, "").replace(new RegExp("^\\p{L}+:\\s*", "u"), "").trim();
|
|
9778
9968
|
if (!l || isLeakSuspect(raw) || isLeakSuspect(l)) {
|
|
9779
|
-
if (l)
|
|
9969
|
+
if (l) log24.debug(`line "${raw}" reads as meta \u2014 rejected`);
|
|
9780
9970
|
return "";
|
|
9781
9971
|
}
|
|
9782
9972
|
if (lang && !fitsLanguage(l, lang)) {
|
|
9783
|
-
|
|
9973
|
+
log24.debug(`line "${l}" is not ${lang} \u2014 rejected`);
|
|
9784
9974
|
return "";
|
|
9785
9975
|
}
|
|
9786
9976
|
return l.split(/\s+/).length > maxWords || l.length > 60 ? "" : l;
|
|
@@ -9794,7 +9984,7 @@ function validateBank(raw, lang, minPerKind = 3) {
|
|
|
9794
9984
|
const seen = /* @__PURE__ */ new Set();
|
|
9795
9985
|
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)));
|
|
9796
9986
|
if (out[k].length < minPerKind) {
|
|
9797
|
-
|
|
9987
|
+
log24.warn(`bank ${lang}: only ${out[k].length} valid ${k} lines \u2014 rejected`);
|
|
9798
9988
|
return null;
|
|
9799
9989
|
}
|
|
9800
9990
|
}
|
|
@@ -9870,11 +10060,11 @@ var ChannelBanks = class {
|
|
|
9870
10060
|
const f = this.file(lang);
|
|
9871
10061
|
let cached = null;
|
|
9872
10062
|
if (f) cached = await readFile(f, "utf8").then((s) => JSON.parse(s)).catch((e) => {
|
|
9873
|
-
if (e.code !== "ENOENT")
|
|
10063
|
+
if (e.code !== "ENOENT") log24.warn(`bank cache ${f} unreadable \u2014 regenerating: ${e.message}`);
|
|
9874
10064
|
return null;
|
|
9875
10065
|
});
|
|
9876
10066
|
if (cached && cached.v !== BANK_VERSION) {
|
|
9877
|
-
|
|
10067
|
+
log24.debug(`bank ${lang}: cache v${cached.v ?? 1} predates v${BANK_VERSION} \u2014 regenerating`);
|
|
9878
10068
|
cached = null;
|
|
9879
10069
|
}
|
|
9880
10070
|
const valid = cached && validateBank(cached.bank, lang, 0);
|
|
@@ -9882,7 +10072,7 @@ var ChannelBanks = class {
|
|
|
9882
10072
|
if (valid) {
|
|
9883
10073
|
for (const k of BANK_KEYS) if (!valid[k].length) valid[k] = [...this.o.fallback[lang]?.[k] ?? []];
|
|
9884
10074
|
this.banks.set(lang, valid);
|
|
9885
|
-
|
|
10075
|
+
log24.debug(`bank ${lang}: cache hit (${BANK_KEYS.map((k) => valid[k].length).join("/")})${complete ? "" : " \u2014 missing kinds, refreshing"}`);
|
|
9886
10076
|
}
|
|
9887
10077
|
if (complete && this.o.now() - cached.at < this.o.refreshMs) return;
|
|
9888
10078
|
await this.generate(lang, valid);
|
|
@@ -9901,11 +10091,11 @@ var ChannelBanks = class {
|
|
|
9901
10091
|
bank[k] = [...fresh[k], ...have?.[k] ?? []].filter((x) => !seen.has(norm3(x)) && seen.add(norm3(x))).slice(0, this.o.maxPerKind);
|
|
9902
10092
|
}
|
|
9903
10093
|
this.banks.set(lang, bank);
|
|
9904
|
-
|
|
10094
|
+
log24.debug(`bank ${lang}: ${have ? "refreshed" : "generated"} (${BANK_KEYS.map((k) => bank[k].length).join("/")}) in ${Math.round(this.o.now() - t0)}ms`);
|
|
9905
10095
|
const f = this.file(lang);
|
|
9906
|
-
if (f) await mkdir(this.o.cacheDir, { recursive: true }).then(() => writeFile(f, JSON.stringify({ at: this.o.now(), v: BANK_VERSION, bank }))).catch((e) =>
|
|
10096
|
+
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}`));
|
|
9907
10097
|
} catch (e) {
|
|
9908
|
-
|
|
10098
|
+
log24.warn(`bank ${lang}: generation failed \u2014 ${have ? "keeping the cached bank" : lang in this.o.fallback ? "fallback bank" : "silent"}: ${e.message}`);
|
|
9909
10099
|
} finally {
|
|
9910
10100
|
clearTimeout(t);
|
|
9911
10101
|
if (!this.banks.has(lang)) setTimeout(() => this.pending.delete(lang), this.o.retryMs).unref?.();
|
|
@@ -10098,7 +10288,7 @@ var TurnGate = class {
|
|
|
10098
10288
|
};
|
|
10099
10289
|
|
|
10100
10290
|
// src/duplex.ts
|
|
10101
|
-
var
|
|
10291
|
+
var log25 = forComponent("DuplexAgent");
|
|
10102
10292
|
function describeCall(call) {
|
|
10103
10293
|
const v = call.args && Object.values(call.args).find((x) => typeof x === "string" && x.trim());
|
|
10104
10294
|
const hint = v ? ` (${String(v).replace(/\s+/g, " ").trim().slice(0, 48)})` : "";
|
|
@@ -10433,7 +10623,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
10433
10623
|
const m = this.reflexBuf.match(RESERVED_EVENT_MARKER) ?? this.reflexBuf.match(RESERVED_EVENT_OPENER);
|
|
10434
10624
|
if (m) {
|
|
10435
10625
|
this.fabricationCut = true;
|
|
10436
|
-
|
|
10626
|
+
log25.warn(`reflex fabricated a [task \u2026] event in its spoken stream \u2014 cutting it (kept ${m.index} chars)`);
|
|
10437
10627
|
this.reflexBuf = this.reflexBuf.slice(0, Math.max(m.index, this.reflexForwarded));
|
|
10438
10628
|
this.pumpReflex(true);
|
|
10439
10629
|
return;
|
|
@@ -10575,7 +10765,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
10575
10765
|
this.leakHit = true;
|
|
10576
10766
|
this.leakCuts.push(leak.trim().slice(0, 40));
|
|
10577
10767
|
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";
|
|
10578
|
-
|
|
10768
|
+
log25.warn(`reflex ${why} ("${leak.trim().slice(0, 40)}\u2026") \u2014 cut before TTS`);
|
|
10579
10769
|
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" });
|
|
10580
10770
|
const next = this.stepMarks.find((k) => k > at);
|
|
10581
10771
|
if (next === void 0) {
|
|
@@ -10616,7 +10806,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
10616
10806
|
this.notify("diag", "leak_judge", { text: s.slice(0, 80), p: Math.round(p * 100) / 100, ms: Math.round(performance.now() - t0) });
|
|
10617
10807
|
return p >= LEAK_JUDGE_THRESHOLD;
|
|
10618
10808
|
}, (e) => {
|
|
10619
|
-
|
|
10809
|
+
log25.warn(`leak judge failed \u2014 speaking the sentence: ${e.message}`);
|
|
10620
10810
|
return false;
|
|
10621
10811
|
}).then((leak) => {
|
|
10622
10812
|
clearTimeout(timer);
|
|
@@ -10688,7 +10878,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
10688
10878
|
let i = t.length - 1;
|
|
10689
10879
|
while (i >= this.turnBase && !(t[i].role === "assistant" && typeof t[i].content === "string" && t[i].content.includes(key))) i--;
|
|
10690
10880
|
if (i < this.turnBase) {
|
|
10691
|
-
|
|
10881
|
+
log25.warn("analysis leak cut, but its text was not found in the transcript to scrub");
|
|
10692
10882
|
continue;
|
|
10693
10883
|
}
|
|
10694
10884
|
const m = t[i];
|
|
@@ -10745,7 +10935,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
10745
10935
|
const res = await this.sendWithContext(content, context, note);
|
|
10746
10936
|
const fb = this.options.reflexFallbackModel, model = this.voice.options.model;
|
|
10747
10937
|
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;
|
|
10748
|
-
|
|
10938
|
+
log25.warn(`reflex ${model} rate-limited \u2014 re-running the turn on ${fb}`);
|
|
10749
10939
|
this.notify("diag", "reflex_fallback", { from: model, to: fb });
|
|
10750
10940
|
this.voice.transcript.length = base;
|
|
10751
10941
|
this.voice.options.model = fb;
|
|
@@ -10794,7 +10984,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
10794
10984
|
try {
|
|
10795
10985
|
await this.sendWithContext(reminder, this.lastContext);
|
|
10796
10986
|
} catch (e) {
|
|
10797
|
-
|
|
10987
|
+
log25.warn(`follow-through nudge failed: ${e instanceof Error ? e.message : e}`);
|
|
10798
10988
|
} finally {
|
|
10799
10989
|
const i = this.voice.transcript.findIndex((m, k) => k >= base && m.role === "user" && m.content === reminder);
|
|
10800
10990
|
if (i >= 0) this.voice.transcript.splice(i, 1);
|
|
@@ -10855,7 +11045,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
10855
11045
|
return {
|
|
10856
11046
|
preToolUse: (call) => {
|
|
10857
11047
|
if (this.spec?.state === "pending" && !_DuplexAgent.SPEC_SAFE_TOOLS.has(call.name)) {
|
|
10858
|
-
|
|
11048
|
+
log25.verbose(`speculation aborted: reflex called ${call.name} on unconfirmed input`);
|
|
10859
11049
|
this.abortSpeculation();
|
|
10860
11050
|
return { block: true, reason: "Speculative turn aborted." };
|
|
10861
11051
|
}
|
|
@@ -10937,7 +11127,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
10937
11127
|
try {
|
|
10938
11128
|
await this.sendWithContext(reminder, this.lastContext);
|
|
10939
11129
|
} catch (e) {
|
|
10940
|
-
|
|
11130
|
+
log25.warn(`ack nudge failed: ${e instanceof Error ? e.message : e}`);
|
|
10941
11131
|
} finally {
|
|
10942
11132
|
this.nudging = false;
|
|
10943
11133
|
const i = this.voice.transcript.findIndex((m, k) => k >= base && m.role === "user" && m.content === reminder);
|
|
@@ -10993,7 +11183,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
10993
11183
|
const accepted = this.offerOpen && this.parkedRedeliver.length > 0 && this.parkedRedeliver.every((p) => p.task === this.offer.task) && (isOfferAcceptance(content) || isTrivialBarge(content));
|
|
10994
11184
|
const judge = accepted ? void 0 : this.options.bargeIntent;
|
|
10995
11185
|
return (accepted ? Promise.resolve("resume") : judge ? judge(content).catch((e) => {
|
|
10996
|
-
|
|
11186
|
+
log25.warn(`bargeIntent judge failed \u2014 using the allowlist: ${e.message}`);
|
|
10997
11187
|
return fallback();
|
|
10998
11188
|
}) : Promise.resolve(fallback())).then((intent) => {
|
|
10999
11189
|
this.notify("diag", "barge_intent", { intent, text: content.slice(0, 80) });
|
|
@@ -11196,7 +11386,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
11196
11386
|
try {
|
|
11197
11387
|
res = await this.sendWithContext(spec.text, this.lastContext);
|
|
11198
11388
|
} catch (e) {
|
|
11199
|
-
|
|
11389
|
+
log25.warn(`speculative turn failed: ${e instanceof Error ? e.message : e}`);
|
|
11200
11390
|
} finally {
|
|
11201
11391
|
this.voice.options.signal = prevSignal;
|
|
11202
11392
|
}
|
|
@@ -11210,7 +11400,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
11210
11400
|
if (d === "abort") {
|
|
11211
11401
|
if (this.voice.transcript.length > base) this.voice.transcript.length = base;
|
|
11212
11402
|
this.speculativeAbortedCalls++;
|
|
11213
|
-
|
|
11403
|
+
log25.verbose(`speculation aborted (${this.speculativeAbortedCalls} total): "${spec.text.slice(0, 50)}"`);
|
|
11214
11404
|
if (this.spec === spec) this.spec = void 0;
|
|
11215
11405
|
return res ?? empty;
|
|
11216
11406
|
}
|
|
@@ -11596,7 +11786,7 @@ Another agent just implemented the above. Independently check the CURRENT state
|
|
|
11596
11786
|
this.notify("task_verify", `task ${id}: verifying`, { id });
|
|
11597
11787
|
const cres = await new Agent(checkerOpts).run(checkBrief);
|
|
11598
11788
|
if (cres.finishReason !== "stop") {
|
|
11599
|
-
|
|
11789
|
+
log25.warn(`task ${id}: verify inconclusive (${cres.finishReason})`);
|
|
11600
11790
|
this.notify("task_verify", `task ${id}: verify inconclusive (${cres.finishReason})`, { id, finishReason: cres.finishReason });
|
|
11601
11791
|
}
|
|
11602
11792
|
const sum = (a = 0, b = 0) => a + b;
|
|
@@ -11732,7 +11922,7 @@ Another agent just implemented the above. Independently check the CURRENT state
|
|
|
11732
11922
|
rec.status = "done";
|
|
11733
11923
|
rec.result = res.text;
|
|
11734
11924
|
const incomplete = res.finishReason !== "stop" && res.finishReason !== "empty";
|
|
11735
|
-
|
|
11925
|
+
log25.verbose(`task ${id} done (${res.steps} steps${incomplete ? `, INCOMPLETE: ${res.finishReason}` : ""})`);
|
|
11736
11926
|
this.notify("task_done", `task ${id} (${rec.label}) completed`, {
|
|
11737
11927
|
id,
|
|
11738
11928
|
text: res.text,
|
|
@@ -11766,14 +11956,14 @@ Another agent just implemented the above. Independently check the CURRENT state
|
|
|
11766
11956
|
foldIfSuperseded(rec, note) {
|
|
11767
11957
|
if (rec.deliveryParked !== "moved_on") return false;
|
|
11768
11958
|
if (note.trim()) this.voice.transcript.push({ role: "assistant", content: note });
|
|
11769
|
-
|
|
11959
|
+
log25.verbose(`task ${rec.id} settled but superseded (${rec.status}) \u2014 folded into transcript, not voiced`);
|
|
11770
11960
|
return true;
|
|
11771
11961
|
}
|
|
11772
11962
|
failTask(rec, msg) {
|
|
11773
11963
|
this.dropAsk(rec.id);
|
|
11774
11964
|
rec.status = "error";
|
|
11775
11965
|
rec.result = msg;
|
|
11776
|
-
|
|
11966
|
+
log25.warn(`task ${rec.id} failed: ${msg}`);
|
|
11777
11967
|
this.notify("task_error", `task ${rec.id} (${rec.label}) failed: ${msg}`, { id: rec.id });
|
|
11778
11968
|
if (this.foldIfSuperseded(rec, `The "${rec.label}" task failed: ${msg}`)) return;
|
|
11779
11969
|
this.dropParked(rec);
|
|
@@ -12077,7 +12267,7 @@ Another agent just implemented the above. Independently check the CURRENT state
|
|
|
12077
12267
|
init_llm();
|
|
12078
12268
|
init_logging();
|
|
12079
12269
|
init_models();
|
|
12080
|
-
var
|
|
12270
|
+
var log26 = forComponent("ModelRouter");
|
|
12081
12271
|
var ModelRouterOptions = class {
|
|
12082
12272
|
classifier = jevClassifier();
|
|
12083
12273
|
/** Capability tiers → model id (any provider). basic/standard run the main session; premium is only ever an offload. */
|
|
@@ -12161,10 +12351,10 @@ var ModelRouter = class {
|
|
|
12161
12351
|
if (tier === "premium") d.offload = this.state.offload && (c.sameThread ?? 0) >= o.sameThreadAt ? "continue" : "fresh";
|
|
12162
12352
|
} catch (e) {
|
|
12163
12353
|
const msg = e instanceof Error ? e.message : String(e);
|
|
12164
|
-
|
|
12354
|
+
log26.warn(`classifier failed, falling back to standard: ${msg}`);
|
|
12165
12355
|
d = { tier: "standard", model: o.models.standard, probs: {}, fallback: msg, classifyMs: Math.round(performance.now() - t0) };
|
|
12166
12356
|
}
|
|
12167
|
-
|
|
12357
|
+
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`);
|
|
12168
12358
|
return d;
|
|
12169
12359
|
}
|
|
12170
12360
|
/** Run one user turn on `agent` with routing. Returns a RunResult carrying `costUsd` + `routing`. */
|
|
@@ -12216,7 +12406,7 @@ ${text}`;
|
|
|
12216
12406
|
const answer = res.text || `(premium sub-agent finished with no reply; finishReason=${res.finishReason})`;
|
|
12217
12407
|
agent.transcript.push({ role: "user", content }, { role: "assistant", content: answer });
|
|
12218
12408
|
this.state.offload = { messages: child.transcript, mainLen: agent.transcript.length, question: text };
|
|
12219
|
-
|
|
12409
|
+
log26.info(`offload ${cont ? "continue" : "fresh"} done \xB7 ${res.finishReason} \xB7 ${res.steps} steps`);
|
|
12220
12410
|
const costUsd = o.costOf(main, briefUsage) + o.costOf(o.models.premium, res.usage);
|
|
12221
12411
|
return { ...res, text: answer, messages: agent.transcript, usage: addUsage(briefUsage, res.usage), costUsd, routing: d };
|
|
12222
12412
|
}
|
|
@@ -12232,7 +12422,7 @@ The request:
|
|
|
12232
12422
|
${text}`);
|
|
12233
12423
|
return { text: r.text.trim() || text, usage: r.usage };
|
|
12234
12424
|
} catch (e) {
|
|
12235
|
-
|
|
12425
|
+
log26.warn(`brief failed, sending the raw request: ${e instanceof Error ? e.message : String(e)}`);
|
|
12236
12426
|
return { text };
|
|
12237
12427
|
} finally {
|
|
12238
12428
|
agent.transcript = snap;
|
|
@@ -12277,7 +12467,7 @@ async function jevAvailable(opts = {}) {
|
|
|
12277
12467
|
const { env } = await loadJev(opts);
|
|
12278
12468
|
return JEV_KEYS.some((k) => env[k]?.trim());
|
|
12279
12469
|
} catch (e) {
|
|
12280
|
-
|
|
12470
|
+
log26.debug(`jev unavailable: ${e instanceof Error ? e.message : String(e)}`);
|
|
12281
12471
|
return false;
|
|
12282
12472
|
}
|
|
12283
12473
|
}
|
|
@@ -12562,7 +12752,7 @@ init_models();
|
|
|
12562
12752
|
|
|
12563
12753
|
// src/voice/progress.ts
|
|
12564
12754
|
init_logging();
|
|
12565
|
-
var
|
|
12755
|
+
var log27 = forComponent("Progress");
|
|
12566
12756
|
var HE_RESULT_CLAIM = /(?:^|\s)(?:מצאתי|סיימתי|בוצע|הנה|אין)(?=\s|$|[.,!…])/u;
|
|
12567
12757
|
var TOPICS = [
|
|
12568
12758
|
[/toolsearch|^mcp__.*__(list_commands|call_info)$/, null],
|
|
@@ -12707,7 +12897,7 @@ var Progress = class {
|
|
|
12707
12897
|
}, this.o.generateBudgetMs);
|
|
12708
12898
|
});
|
|
12709
12899
|
const raw = await Promise.race([this.o.generate(tool, argsHint(args), lang, ac.signal).catch((e) => {
|
|
12710
|
-
|
|
12900
|
+
log27.warn(`progress line failed: ${e?.message ?? e}`);
|
|
12711
12901
|
return null;
|
|
12712
12902
|
}), late]).finally(() => clearTimeout(t));
|
|
12713
12903
|
const l = cleanLine(raw ?? "", lang, 5);
|
|
@@ -12729,7 +12919,7 @@ init_logging();
|
|
|
12729
12919
|
|
|
12730
12920
|
// src/voice/backchannelPlan.ts
|
|
12731
12921
|
init_logging();
|
|
12732
|
-
var
|
|
12922
|
+
var log28 = forComponent("BackchannelPlan");
|
|
12733
12923
|
var silentTurn = (p) => p.density === "none" || p.turn === "short_command" || p.turn === "question";
|
|
12734
12924
|
var cueWords = (s) => s.toLowerCase().replace(/[^\p{L}\p{N}'\s]+/gu, " ").split(/\s+/).filter(Boolean);
|
|
12735
12925
|
function matchCue(plan, heard, from = 0) {
|
|
@@ -12747,7 +12937,7 @@ function matchCue(plan, heard, from = 0) {
|
|
|
12747
12937
|
}
|
|
12748
12938
|
|
|
12749
12939
|
// src/voice/secondChannel.ts
|
|
12750
|
-
var
|
|
12940
|
+
var log29 = forComponent("SecondChannel");
|
|
12751
12941
|
var CHANNEL_MODES = {
|
|
12752
12942
|
turn: { kinds: ["bridge"], bridgeDelayMs: 1500 },
|
|
12753
12943
|
normal: {},
|
|
@@ -12832,6 +13022,9 @@ var SecondChannelOptions = class {
|
|
|
12832
13022
|
/** Duplex v3 (§3 first sound): a bridge skips the decider (the veto stays for backchannels/fillers) and speaks a
|
|
12833
13023
|
* canned line at once; a generated one is started at dispatch and used only if it already landed. */
|
|
12834
13024
|
instantBridge = false;
|
|
13025
|
+
/** Duplex v3 §3 late bridge: a turn that expects an answer (not a closer) and got no bridge yet gets one canned bridge
|
|
13026
|
+
* this long after the user's last word if channel-1 is still silent — the short turns the 0ms rule skips ("Hi"). 0 = off. */
|
|
13027
|
+
lateBridgeMs = 0;
|
|
12835
13028
|
/** No new interim for this long → a pause (hosts may also call onPause). */
|
|
12836
13029
|
fillerPauseMs = 700;
|
|
12837
13030
|
fillerMinWords = 4;
|
|
@@ -12861,15 +13054,15 @@ function withBudget(p, ms, what, ac) {
|
|
|
12861
13054
|
let t;
|
|
12862
13055
|
const late = new Promise((r) => {
|
|
12863
13056
|
t = setTimeout(() => {
|
|
12864
|
-
|
|
13057
|
+
log29.debug(`${what}: over the ${ms}ms budget \u2014 dropped`);
|
|
12865
13058
|
ac?.abort();
|
|
12866
13059
|
r(null);
|
|
12867
13060
|
}, ms);
|
|
12868
13061
|
});
|
|
12869
13062
|
return Promise.race([p.catch((e) => {
|
|
12870
13063
|
const msg = String(e?.message ?? e);
|
|
12871
|
-
if (e?.name === "AbortError" || /aborted/i.test(msg))
|
|
12872
|
-
else
|
|
13064
|
+
if (e?.name === "AbortError" || /aborted/i.test(msg)) log29.debug(`${what}: aborted after its budget`);
|
|
13065
|
+
else log29.warn(`${what} failed \u2014 falling back: ${msg}`);
|
|
12873
13066
|
return null;
|
|
12874
13067
|
}), late]).finally(() => clearTimeout(t));
|
|
12875
13068
|
}
|
|
@@ -12895,6 +13088,11 @@ var SecondChannel = class {
|
|
|
12895
13088
|
pauseTimer = null;
|
|
12896
13089
|
bcTimer = null;
|
|
12897
13090
|
bridgeTimer = null;
|
|
13091
|
+
lateTimer = null;
|
|
13092
|
+
late = false;
|
|
13093
|
+
// the bridge moment being considered is the late one (no length rule)
|
|
13094
|
+
lastWordAgo = 0;
|
|
13095
|
+
// the dispatched turn: ms since the user's last word (anchors lateBridgeMs)
|
|
12898
13096
|
lastAt = -Infinity;
|
|
12899
13097
|
lastKind = null;
|
|
12900
13098
|
live = null;
|
|
@@ -12943,7 +13141,7 @@ var SecondChannel = class {
|
|
|
12943
13141
|
if (host) this.pinned = true;
|
|
12944
13142
|
const lang = primaryLang(tag);
|
|
12945
13143
|
if (!lang || lang === this.lang) return;
|
|
12946
|
-
|
|
13144
|
+
log29.debug(`language ${this.lang} \u2192 ${lang}`);
|
|
12947
13145
|
this.lang = lang;
|
|
12948
13146
|
this.recent = [];
|
|
12949
13147
|
this.picks.clear();
|
|
@@ -13013,9 +13211,11 @@ var SecondChannel = class {
|
|
|
13013
13211
|
if (s === this.main) return;
|
|
13014
13212
|
this.main = s;
|
|
13015
13213
|
if (s === "first_audio" || s === "speaking") this.live = null;
|
|
13016
|
-
if (s !== "queued" && s !== "thinking"
|
|
13017
|
-
|
|
13018
|
-
|
|
13214
|
+
if (s !== "queued" && s !== "thinking") {
|
|
13215
|
+
for (const k of ["bridgeTimer", "lateTimer"]) if (this[k]) {
|
|
13216
|
+
clearTimeout(this[k]);
|
|
13217
|
+
this[k] = null;
|
|
13218
|
+
}
|
|
13019
13219
|
}
|
|
13020
13220
|
if (s === "idle") {
|
|
13021
13221
|
this.heard = "";
|
|
@@ -13036,7 +13236,7 @@ var SecondChannel = class {
|
|
|
13036
13236
|
this.userSpeaking = false;
|
|
13037
13237
|
this.bridged = false;
|
|
13038
13238
|
if (this.lastKind === "bridge") this.lastKind = null;
|
|
13039
|
-
for (const k of ["pauseTimer", "bcTimer", "bridgeTimer"]) {
|
|
13239
|
+
for (const k of ["pauseTimer", "bcTimer", "bridgeTimer", "lateTimer"]) {
|
|
13040
13240
|
if (this[k]) clearTimeout(this[k]);
|
|
13041
13241
|
this[k] = null;
|
|
13042
13242
|
}
|
|
@@ -13047,11 +13247,21 @@ var SecondChannel = class {
|
|
|
13047
13247
|
void this.consider("working");
|
|
13048
13248
|
}, wait);
|
|
13049
13249
|
else void this.consider("working");
|
|
13250
|
+
const late = this.o.lateBridgeMs - this.lastWordAgo;
|
|
13251
|
+
if (this.o.lateBridgeMs > 0) this.lateTimer = setTimeout(() => {
|
|
13252
|
+
this.lateTimer = null;
|
|
13253
|
+
if (this.bridged || this.busy || turnIntent(this.heard) === "closer" || this.blocked("bridge")) return;
|
|
13254
|
+
this.late = true;
|
|
13255
|
+
void this.consider("working").finally(() => {
|
|
13256
|
+
this.late = false;
|
|
13257
|
+
});
|
|
13258
|
+
}, Math.max(late, wait + 1));
|
|
13050
13259
|
}
|
|
13051
13260
|
}
|
|
13052
13261
|
/** The user's final line was dispatched to the main agent (its words are the bridge's micro-context). */
|
|
13053
|
-
onUserTurn(text) {
|
|
13262
|
+
onUserTurn(text, lastWordAgoMs = 0) {
|
|
13054
13263
|
this.heard = text.trim();
|
|
13264
|
+
this.lastWordAgo = Math.max(0, lastWordAgoMs);
|
|
13055
13265
|
this.floorWords = this.carried + words2(this.heard).length;
|
|
13056
13266
|
this.lastUser = this.heard;
|
|
13057
13267
|
this.detect(this.heard);
|
|
@@ -13076,7 +13286,7 @@ var SecondChannel = class {
|
|
|
13076
13286
|
try {
|
|
13077
13287
|
this.o.cancel();
|
|
13078
13288
|
} catch (e) {
|
|
13079
|
-
|
|
13289
|
+
log29.warn(`cancel threw: ${e.message}`);
|
|
13080
13290
|
}
|
|
13081
13291
|
}
|
|
13082
13292
|
}
|
|
@@ -13119,12 +13329,19 @@ var SecondChannel = class {
|
|
|
13119
13329
|
}
|
|
13120
13330
|
/** Why this turn gets no bridge by rule ('' = it may) — language, a quick/phatic turn, or blocked(). */
|
|
13121
13331
|
bridgeBlock() {
|
|
13122
|
-
const w = words2(this.heard);
|
|
13332
|
+
const w = words2(this.heard), intent = turnIntent(this.heard);
|
|
13123
13333
|
if (!this.lang) return "language unknown";
|
|
13124
|
-
if (
|
|
13125
|
-
if (
|
|
13334
|
+
if (intent === "closer") return "no answer due";
|
|
13335
|
+
if (this.late) return this.blocked("bridge");
|
|
13336
|
+
const why = intent === "ask" ? w.length <= 6 && isPhatic(this.heard, this.bank) ? "phatic" : "" : this.shortTurn(w.length);
|
|
13337
|
+
if (why) return this.o.lateBridgeMs > 0 ? `${why} (late bridge armed)` : why;
|
|
13126
13338
|
return this.blocked("bridge");
|
|
13127
13339
|
}
|
|
13340
|
+
shortTurn(n) {
|
|
13341
|
+
if (n <= 3 || n <= 6 && isPhatic(this.heard, this.bank)) return n <= 3 ? "quick turn" : "phatic";
|
|
13342
|
+
if (n < this.o.bridgeMinWords) return `under ${this.o.bridgeMinWords} words`;
|
|
13343
|
+
return "";
|
|
13344
|
+
}
|
|
13128
13345
|
bcTimes = [];
|
|
13129
13346
|
capped() {
|
|
13130
13347
|
if (!this.o.maxPerWindow) return false;
|
|
@@ -13136,8 +13353,9 @@ var SecondChannel = class {
|
|
|
13136
13353
|
rule(trigger, pauseMs = 0, breath = false, cue = null) {
|
|
13137
13354
|
const w = words2(this.heard);
|
|
13138
13355
|
if (!this.lang) return "silence";
|
|
13356
|
+
if (trigger === "working") return this.bridgeBlock() ? "silence" : "bridge";
|
|
13139
13357
|
if (w.length <= 3 || w.length <= 6 && isPhatic(this.heard, this.bank)) return "silence";
|
|
13140
|
-
const k = trigger === "interim" ? this.backchannelMoment(w.length, breath) && (!this.o.anticipate || !this.plan || !silentTurn(this.plan) && (!!cue || !this.o.cueOnly && this.plan.density !== "low")) ? "backchannel" : null : trigger === "pause" ? pauseMs >= this.o.fillerPauseMs && w.length >= this.o.fillerMinWords ? "filler" : null :
|
|
13358
|
+
const k = trigger === "interim" ? this.backchannelMoment(w.length, breath) && (!this.o.anticipate || !this.plan || !silentTurn(this.plan) && (!!cue || !this.o.cueOnly && this.plan.density !== "low")) ? "backchannel" : null : trigger === "pause" ? pauseMs >= this.o.fillerPauseMs && w.length >= this.o.fillerMinWords ? "filler" : null : null;
|
|
13141
13359
|
return k && this.allowed(k) ? k : "silence";
|
|
13142
13360
|
}
|
|
13143
13361
|
/** Plan (or, with `heard`, revise) the backchannels of the user's turn — async, never on the hot path. */
|
|
@@ -13180,7 +13398,7 @@ var SecondChannel = class {
|
|
|
13180
13398
|
const opts = this.options;
|
|
13181
13399
|
if (opts.mode === "chatty" && !opts.aec && !this.degradedLogged) {
|
|
13182
13400
|
this.degradedLogged = true;
|
|
13183
|
-
|
|
13401
|
+
log29.info("chatty mode without echo cancellation \u2014 normal timing (our lines would re-trigger the mic)");
|
|
13184
13402
|
}
|
|
13185
13403
|
const rule = this.rule(trigger, pauseMs, breath, cue);
|
|
13186
13404
|
if (rule === "silence") {
|
|
@@ -13199,7 +13417,7 @@ var SecondChannel = class {
|
|
|
13199
13417
|
const d = await withBudget(this.o.decide(this.tail(), { ...this.state(pauseMs), rule }, ac.signal), this.o.decisionBudgetMs, "channel decision", ac);
|
|
13200
13418
|
choice = d?.choice;
|
|
13201
13419
|
if (choice && choice in CHANNEL_ACTIONS && (choice === "silence" || this.allowed(choice))) kind = choice;
|
|
13202
|
-
else if (choice && choice in CHANNEL_ACTIONS)
|
|
13420
|
+
else if (choice && choice in CHANNEL_ACTIONS) log29.debug(`decider chose ${choice} for a ${rule} moment \u2014 the rule stands`);
|
|
13203
13421
|
}
|
|
13204
13422
|
if (kind === "silence") return ev({ choice, acted: "silence" });
|
|
13205
13423
|
if (stale()) return ev({ choice, kind, acted: "stale" });
|
|
@@ -13237,7 +13455,7 @@ var SecondChannel = class {
|
|
|
13237
13455
|
void withBudget(this.o.generate(ctx, ac.signal), Math.max(this.o.generateBudgetMs, this.o.bridgeDelayMs), "bridge generate", ac).then((raw) => {
|
|
13238
13456
|
const text = cleanLine(raw ?? "", ctx.lang);
|
|
13239
13457
|
if (text && gen === this.gen) this.bridgeDraft = { gen, text };
|
|
13240
|
-
}).catch((e) =>
|
|
13458
|
+
}).catch((e) => log29.debug(`bridge prefetch failed: ${e?.message ?? e}`));
|
|
13241
13459
|
}
|
|
13242
13460
|
async line(kind, react = true) {
|
|
13243
13461
|
if (kind === "bridge" && this.o.instantBridge) {
|
|
@@ -13253,7 +13471,7 @@ var SecondChannel = class {
|
|
|
13253
13471
|
if (raw === null) ac.abort();
|
|
13254
13472
|
const text = cleanLine(raw ?? "", this.lang);
|
|
13255
13473
|
if (text && !this.recent.some((r) => norm3(r) === norm3(text))) return text;
|
|
13256
|
-
if (text)
|
|
13474
|
+
if (text) log29.debug(`generated "${text}" repeats a recent line \u2014 canned`);
|
|
13257
13475
|
}
|
|
13258
13476
|
return this.pick(kind);
|
|
13259
13477
|
}
|
|
@@ -13275,7 +13493,7 @@ var SecondChannel = class {
|
|
|
13275
13493
|
try {
|
|
13276
13494
|
r = this.o.speak(text, kind);
|
|
13277
13495
|
} catch (e) {
|
|
13278
|
-
|
|
13496
|
+
log29.warn(`speak threw \u2014 line dropped: ${e.message}`);
|
|
13279
13497
|
return false;
|
|
13280
13498
|
}
|
|
13281
13499
|
if (r === false) return false;
|
|
@@ -13288,7 +13506,7 @@ var SecondChannel = class {
|
|
|
13288
13506
|
this.o.show(text, kind);
|
|
13289
13507
|
const p = Promise.resolve(r).then(() => {
|
|
13290
13508
|
}, (e) => {
|
|
13291
|
-
|
|
13509
|
+
log29.warn(`channel line playback failed: ${e?.message ?? e}`);
|
|
13292
13510
|
}).finally(() => {
|
|
13293
13511
|
if (this.inflight === p) this.inflight = null;
|
|
13294
13512
|
});
|
|
@@ -13346,7 +13564,7 @@ function closedQuestion(reply) {
|
|
|
13346
13564
|
}
|
|
13347
13565
|
|
|
13348
13566
|
// src/voice/call.ts
|
|
13349
|
-
var
|
|
13567
|
+
var log30 = forComponent("VoiceCall");
|
|
13350
13568
|
var VoiceCallOptions = class {
|
|
13351
13569
|
/** Host runs MAIN with exactly `text` (a replace: roll the `replaces` turn back first). */
|
|
13352
13570
|
sendTurn = () => {
|
|
@@ -13404,8 +13622,11 @@ var VoiceCall = class {
|
|
|
13404
13622
|
aec: o.aec,
|
|
13405
13623
|
// v3 §3: first sound <1.2s p50 from the last word — the endpoint takes ~0.6s and the bridge's TTS ~0.2s, so the bridge
|
|
13406
13624
|
// goes 300ms after the turn if MAIN is still silent (it always is that early: warm MAIN's first token is ≥1s), no veto
|
|
13625
|
+
// A short question gets that bridge too (by intent, not length); any other turn expecting an answer that got none
|
|
13626
|
+
// gets a late one at 1.3s from the last word while MAIN is still silent (API first byte runs 1-8s); a closer, none.
|
|
13407
13627
|
bridgeDelayMs: 300,
|
|
13408
13628
|
instantBridge: true,
|
|
13629
|
+
lateBridgeMs: 1300,
|
|
13409
13630
|
...this.lang ? { lang: this.lang } : {},
|
|
13410
13631
|
...o.channel,
|
|
13411
13632
|
// the ledger: a turn's bridge moment that stayed silent, and why (its wait then has no first sound until MAIN speaks)
|
|
@@ -13569,7 +13790,7 @@ var VoiceCall = class {
|
|
|
13569
13790
|
try {
|
|
13570
13791
|
this.o.cancel();
|
|
13571
13792
|
} catch (e) {
|
|
13572
|
-
|
|
13793
|
+
log30.warn(`cancel threw: ${e.message}`);
|
|
13573
13794
|
}
|
|
13574
13795
|
if (this.cur?.running) {
|
|
13575
13796
|
this.cur.running = false;
|
|
@@ -13629,7 +13850,7 @@ var VoiceCall = class {
|
|
|
13629
13850
|
if (this.cur) this.close(this.cur, t.replaces === this.cur.id ? "replaced" : void 0);
|
|
13630
13851
|
this.follow(t.text);
|
|
13631
13852
|
this.clearProgress();
|
|
13632
|
-
this.cur = { id: t.id, text: t.text, at, finalAt, lastWord, running: true, streamed: false, sounded: false, worded: false, done: false, marks: { sent: now3 }, closed: false };
|
|
13853
|
+
this.cur = { id: t.id, text: t.text, at, finalAt, lastWord, running: true, streamed: false, sounded: false, worded: false, done: false, marks: { sent: now3 }, closed: false, due: turnIntent(t.text) !== "closer" };
|
|
13633
13854
|
const early = this.earlyHeard;
|
|
13634
13855
|
this.earlyHeard = null;
|
|
13635
13856
|
if (early && early.at >= at) {
|
|
@@ -13639,7 +13860,7 @@ var VoiceCall = class {
|
|
|
13639
13860
|
this.progress.turn();
|
|
13640
13861
|
this.o.onMetric({ name: "turn", id: t.id, kind: t.kind, text: t.text });
|
|
13641
13862
|
this.o.sendTurn(t.text, { id: t.id, ...t.replaces ? { replaces: t.replaces } : {} });
|
|
13642
|
-
if (t.kind === "new") this.fill.onUserTurn(t.text);
|
|
13863
|
+
if (t.kind === "new") this.fill.onUserTurn(t.text, now3 - at);
|
|
13643
13864
|
}
|
|
13644
13865
|
onDrop(text, why) {
|
|
13645
13866
|
if (!this.gate.holding) {
|
|
@@ -13656,7 +13877,7 @@ var VoiceCall = class {
|
|
|
13656
13877
|
try {
|
|
13657
13878
|
r = this.o.speak(text, kind);
|
|
13658
13879
|
} catch (e) {
|
|
13659
|
-
|
|
13880
|
+
log30.warn(`speak threw: ${e.message}`);
|
|
13660
13881
|
return false;
|
|
13661
13882
|
}
|
|
13662
13883
|
if (r === false) return false;
|
|
@@ -13678,7 +13899,7 @@ var VoiceCall = class {
|
|
|
13678
13899
|
sounded(c, by, at = this.o.now()) {
|
|
13679
13900
|
if (c.sounded) return;
|
|
13680
13901
|
c.sounded = true;
|
|
13681
|
-
this.o.onMetric({ name: "first_sound", id: c.id, ms: Math.round(at - c.at), by });
|
|
13902
|
+
this.o.onMetric(c.due ? { name: "first_sound", id: c.id, ms: Math.round(at - c.at), by } : { name: "first_sound", id: c.id, by, kind: "no answer due" });
|
|
13682
13903
|
}
|
|
13683
13904
|
/** The turn is over (answered + audible, replaced, the next turn, the call's end): its timeline, once. A turn whose MAIN
|
|
13684
13905
|
* never became audible still reports first_word — with no ms and why (cut / replaced / silent) — so every turn has one. */
|
|
@@ -13698,7 +13919,8 @@ var VoiceCall = class {
|
|
|
13698
13919
|
mainText: rel(m.mainText),
|
|
13699
13920
|
mainAudio: rel(m.mainAudio),
|
|
13700
13921
|
lastWord: c.lastWord,
|
|
13701
|
-
end: c.end
|
|
13922
|
+
end: c.end,
|
|
13923
|
+
...c.due ? {} : { due: false }
|
|
13702
13924
|
};
|
|
13703
13925
|
for (const k of Object.keys(tl)) if (tl[k] === void 0) delete tl[k];
|
|
13704
13926
|
this.o.onMetric({ name: "timeline", id: c.id, text: c.text, timeline: tl });
|
|
@@ -13735,6 +13957,7 @@ var VoiceCall = class {
|
|
|
13735
13957
|
init_llm();
|
|
13736
13958
|
import { MemFilesystem as MemFilesystem3, IndexedDbFilesystem, CommandExecutor as CommandExecutor2, registerHeadlessCommands as registerHeadlessCommands2 } from "@livx.cc/wcli/core";
|
|
13737
13959
|
export {
|
|
13960
|
+
ADVISE_LIMIT,
|
|
13738
13961
|
Agent,
|
|
13739
13962
|
AgentOptions,
|
|
13740
13963
|
BodDbFilesystem,
|
|
@@ -13765,6 +13988,9 @@ export {
|
|
|
13765
13988
|
MountFilesystem,
|
|
13766
13989
|
NodeDiskFilesystem,
|
|
13767
13990
|
OverlayFilesystem,
|
|
13991
|
+
PLANNER_BLOCKED,
|
|
13992
|
+
PLANNER_REFUSAL,
|
|
13993
|
+
PLAN_FIRST,
|
|
13768
13994
|
PROGRESS_TEMPLATES,
|
|
13769
13995
|
PermissionOptions,
|
|
13770
13996
|
PermissionPolicy,
|
|
@@ -13789,6 +14015,9 @@ export {
|
|
|
13789
14015
|
VoiceCallOptions,
|
|
13790
14016
|
VoiceEngine,
|
|
13791
14017
|
VoiceEngineOptions,
|
|
14018
|
+
WRITE_TOOLS,
|
|
14019
|
+
advisorFor,
|
|
14020
|
+
advisorPrompt,
|
|
13792
14021
|
applyEditsTool,
|
|
13793
14022
|
askUserQuestionTool,
|
|
13794
14023
|
bashTool,
|
|
@@ -13805,6 +14034,9 @@ export {
|
|
|
13805
14034
|
cronMatches,
|
|
13806
14035
|
decodeDdgUrl,
|
|
13807
14036
|
defaultTools,
|
|
14037
|
+
delegatePrompt,
|
|
14038
|
+
delegateTools,
|
|
14039
|
+
delegateWorkerFor,
|
|
13808
14040
|
diskAgentOptions,
|
|
13809
14041
|
editTool,
|
|
13810
14042
|
elideImages,
|
|
@@ -13844,6 +14076,7 @@ export {
|
|
|
13844
14076
|
loadMemory,
|
|
13845
14077
|
loadSkills,
|
|
13846
14078
|
log,
|
|
14079
|
+
makeAdvisor,
|
|
13847
14080
|
makeAskTool,
|
|
13848
14081
|
makeContext,
|
|
13849
14082
|
makeGlobTool,
|
|
@@ -13863,6 +14096,7 @@ export {
|
|
|
13863
14096
|
messageHasImage,
|
|
13864
14097
|
messageHasImageRef,
|
|
13865
14098
|
messageHasInlineImage,
|
|
14099
|
+
meterWorkers,
|
|
13866
14100
|
mintImageRef,
|
|
13867
14101
|
mkdirp,
|
|
13868
14102
|
modelShortLabel,
|