@livx.cc/agentx 0.99.121 → 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 +664 -240
- 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 +5 -2
- package/dist/index.js +337 -144
- package/dist/index.js.map +1 -1
- package/dist/{secondChannel-D33Wvruo.d.ts → secondChannel-BddfVzTp.d.ts} +101 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2954,7 +2954,7 @@ async function psGroupCpuSeconds(pgid) {
|
|
|
2954
2954
|
try {
|
|
2955
2955
|
return await winTreeCpuSeconds(pgid);
|
|
2956
2956
|
} catch (e) {
|
|
2957
|
-
|
|
2957
|
+
log31.debug(`cpu probe failed for ${pgid}: ${e}`);
|
|
2958
2958
|
return void 0;
|
|
2959
2959
|
}
|
|
2960
2960
|
}
|
|
@@ -2963,7 +2963,7 @@ async function psGroupCpuSeconds(pgid) {
|
|
|
2963
2963
|
const out = await new Promise((res, rej) => execFile5("ps", ["-A", "-o", "pid=,ppid=,pgid=,time="], { timeout: 5e3, env: { ...process.env, LC_ALL: "C" } }, (e, so) => e ? rej(e) : res(String(so))));
|
|
2964
2964
|
return sumTreeCpu(out, pgid);
|
|
2965
2965
|
} catch (e) {
|
|
2966
|
-
|
|
2966
|
+
log31.debug(`ps cpu probe failed for group ${pgid}: ${e}`);
|
|
2967
2967
|
return void 0;
|
|
2968
2968
|
}
|
|
2969
2969
|
}
|
|
@@ -3112,7 +3112,7 @@ function makeRealShellTool(options) {
|
|
|
3112
3112
|
proc.stderr?.on("data", collect);
|
|
3113
3113
|
proc.on("error", (err2) => {
|
|
3114
3114
|
if (err2?.name === "AbortError" || ctl.signal.aborted) return finish(reasonFor(timedOut, timeoutMs, clean2(out)));
|
|
3115
|
-
|
|
3115
|
+
log31.debug("shell spawn error", err2);
|
|
3116
3116
|
finish(`[exit 1] ${err2?.message ?? err2}${out ? "\n" + clean2(out) : ""}`);
|
|
3117
3117
|
});
|
|
3118
3118
|
proc.on("close", (code) => {
|
|
@@ -3191,7 +3191,7 @@ ${clean2(out) || "(no output yet)"}`;
|
|
|
3191
3191
|
}
|
|
3192
3192
|
];
|
|
3193
3193
|
}
|
|
3194
|
-
var
|
|
3194
|
+
var log31, clean2, DETACHED, shellArgv, SECRET_ENV_RE, _spawn, ShellJobRegistry, shellStatus, NO_JOB2;
|
|
3195
3195
|
var init_tools_shell = __esm({
|
|
3196
3196
|
"src/tools.shell.ts"() {
|
|
3197
3197
|
"use strict";
|
|
@@ -3201,7 +3201,7 @@ var init_tools_shell = __esm({
|
|
|
3201
3201
|
init_shell_sandbox();
|
|
3202
3202
|
init_tools_jobs();
|
|
3203
3203
|
init_platform();
|
|
3204
|
-
|
|
3204
|
+
log31 = forComponent("shell");
|
|
3205
3205
|
clean2 = (s) => truncateOutput(redactSecrets(s.replace(/\n+$/, "")));
|
|
3206
3206
|
DETACHED = { stdio: ["ignore", "pipe", "pipe"], ...DETACH_OPTIONS };
|
|
3207
3207
|
shellArgv = (sh, command) => ({ bin: sh.bin, args: sh.args(command), ...sh.spawnOptions ? { spawnOptions: sh.spawnOptions } : {} });
|
|
@@ -4385,7 +4385,11 @@ function childOptionsFor(opts, fs, depth, maxDepth, agentType, model, root) {
|
|
|
4385
4385
|
if (po) childOpts.providerOptions = po;
|
|
4386
4386
|
const deny = opts.permissions?.options.rules.filter((r) => r.decision === "deny") ?? [];
|
|
4387
4387
|
if (deny.length) childOpts.permissions = new PermissionPolicy({ rules: deny, default: "allow" });
|
|
4388
|
-
|
|
4388
|
+
const h = opts.hooks;
|
|
4389
|
+
if (h?.preToolUse || opts.inheritPostToolUse && h?.postToolUse) childOpts.hooks = {
|
|
4390
|
+
...h.preToolUse ? { preToolUse: h.preToolUse.bind(h) } : {},
|
|
4391
|
+
...opts.inheritPostToolUse && h.postToolUse ? { postToolUse: h.postToolUse.bind(h) } : {}
|
|
4392
|
+
};
|
|
4389
4393
|
return childOpts;
|
|
4390
4394
|
}
|
|
4391
4395
|
function makeTaskTool(opts) {
|
|
@@ -4609,6 +4613,147 @@ async function loadAgents(fs, dir) {
|
|
|
4609
4613
|
init_OverlayFilesystem();
|
|
4610
4614
|
init_logging();
|
|
4611
4615
|
|
|
4616
|
+
// src/modes.ts
|
|
4617
|
+
init_logging();
|
|
4618
|
+
var log8 = forComponent("modes");
|
|
4619
|
+
var WRITE_TOOLS = /* @__PURE__ */ new Set(["Write", "Edit", "MultiEdit", "ApplyEdits"]);
|
|
4620
|
+
var PLANNER_BLOCKED = /* @__PURE__ */ new Set([...WRITE_TOOLS, "bash", "Shell"]);
|
|
4621
|
+
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.";
|
|
4622
|
+
var PLAN_FIRST = "Refused: check your plan with `Advise` before the first write (files, data shape, the hard parts). Then write.";
|
|
4623
|
+
var ADVISE_LIMIT = 3;
|
|
4624
|
+
var SNAPSHOT_CAP = 6e4;
|
|
4625
|
+
function pairOk(kind, main2, other) {
|
|
4626
|
+
if (other === main2) {
|
|
4627
|
+
log8.warn(`${kind} off: both sides are ${main2}`);
|
|
4628
|
+
return false;
|
|
4629
|
+
}
|
|
4630
|
+
if (main2.startsWith("cursor/") || other.startsWith("cursor/")) {
|
|
4631
|
+
log8.warn(`${kind} off: cursor models are not supported (${main2} / ${other})`);
|
|
4632
|
+
return false;
|
|
4633
|
+
}
|
|
4634
|
+
return true;
|
|
4635
|
+
}
|
|
4636
|
+
function delegateWorkerFor(planner, worker) {
|
|
4637
|
+
return worker && pairOk("delegate", planner, worker) ? worker : void 0;
|
|
4638
|
+
}
|
|
4639
|
+
function advisorFor(worker, advisor) {
|
|
4640
|
+
return advisor && pairOk("advisor", worker, advisor) ? advisor : void 0;
|
|
4641
|
+
}
|
|
4642
|
+
function delegateTools(tools, planner) {
|
|
4643
|
+
return tools.map((t) => !PLANNER_BLOCKED.has(t.name) ? t : {
|
|
4644
|
+
...t,
|
|
4645
|
+
run: async (...a) => a[1]?.model === planner ? PLANNER_REFUSAL : t.run(...a)
|
|
4646
|
+
});
|
|
4647
|
+
}
|
|
4648
|
+
function delegatePrompt(worker) {
|
|
4649
|
+
return `
|
|
4650
|
+
|
|
4651
|
+
## You are the PLANNER
|
|
4652
|
+
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.`;
|
|
4653
|
+
}
|
|
4654
|
+
function advisorPrompt(advisor) {
|
|
4655
|
+
return `
|
|
4656
|
+
|
|
4657
|
+
## You have an ADVISOR
|
|
4658
|
+
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.`;
|
|
4659
|
+
}
|
|
4660
|
+
async function textOf(r) {
|
|
4661
|
+
if (r && typeof r[Symbol.asyncIterator] === "function") {
|
|
4662
|
+
let s = "";
|
|
4663
|
+
for await (const c of r) if (typeof c?.content === "string") s += c.content;
|
|
4664
|
+
return s;
|
|
4665
|
+
}
|
|
4666
|
+
return String(r?.content ?? r?.text ?? "");
|
|
4667
|
+
}
|
|
4668
|
+
function makeAdvisor(o) {
|
|
4669
|
+
let used = 0;
|
|
4670
|
+
const limit = o.limit ?? ADVISE_LIMIT;
|
|
4671
|
+
const tool = {
|
|
4672
|
+
name: "Advise",
|
|
4673
|
+
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.`,
|
|
4674
|
+
parameters: { type: "object", properties: { question: { type: "string", description: "what you want advice on, with the context it needs" } }, required: ["question"] },
|
|
4675
|
+
async run(args) {
|
|
4676
|
+
const question = String(args?.question ?? "").trim();
|
|
4677
|
+
if (!question) return "No advice: the question is empty.";
|
|
4678
|
+
if (used >= limit) return `No advice: the limit of ${limit} calls is used. Continue with your own judgment.`;
|
|
4679
|
+
used++;
|
|
4680
|
+
let state = "";
|
|
4681
|
+
if (o.snapshot) {
|
|
4682
|
+
const s = String(await Promise.resolve(o.snapshot()).catch((err2) => {
|
|
4683
|
+
log8.warn("advisor snapshot failed:", String(err2));
|
|
4684
|
+
return "";
|
|
4685
|
+
}) || "(empty)");
|
|
4686
|
+
state = `
|
|
4687
|
+
|
|
4688
|
+
## Current state
|
|
4689
|
+
${s.length > SNAPSHOT_CAP ? `${s.slice(0, SNAPSHOT_CAP)}
|
|
4690
|
+
\u2026(cut)` : s}`;
|
|
4691
|
+
}
|
|
4692
|
+
const review = used > 1 ? "" : "\n\n(When the work is complete, call Advise again for a review.)";
|
|
4693
|
+
try {
|
|
4694
|
+
const r = await o.ai.chat({
|
|
4695
|
+
model: o.advisor,
|
|
4696
|
+
messages: [
|
|
4697
|
+
{ 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." },
|
|
4698
|
+
{ role: "user", content: `## User request
|
|
4699
|
+
${o.request()}${state}
|
|
4700
|
+
|
|
4701
|
+
## Question
|
|
4702
|
+
${question}` }
|
|
4703
|
+
],
|
|
4704
|
+
maxTokens: 2e3
|
|
4705
|
+
});
|
|
4706
|
+
return ((await textOf(r)).trim() || "No advice: the advisor returned nothing.") + review;
|
|
4707
|
+
} catch (err2) {
|
|
4708
|
+
log8.warn("advisor call failed:", String(err2));
|
|
4709
|
+
return `No advice: the advisor call failed (${String(err2?.message ?? err2).slice(0, 120)}). Continue with your own judgment.`;
|
|
4710
|
+
}
|
|
4711
|
+
}
|
|
4712
|
+
};
|
|
4713
|
+
const gate = (tools) => tools.map((t) => !WRITE_TOOLS.has(t.name) ? t : { ...t, run: async (...a) => used ? t.run(...a) : PLAN_FIRST });
|
|
4714
|
+
return { tool, gate, used: () => used };
|
|
4715
|
+
}
|
|
4716
|
+
function meterWorkers(ai, main2) {
|
|
4717
|
+
let total;
|
|
4718
|
+
let resolved;
|
|
4719
|
+
const seen = () => {
|
|
4720
|
+
resolved = ai.modelUsed ?? resolved;
|
|
4721
|
+
};
|
|
4722
|
+
const add = (u) => {
|
|
4723
|
+
if (!u) return;
|
|
4724
|
+
const t = total ??= { promptTokens: 0, completionTokens: 0, totalTokens: 0, cacheCreationTokens: 0, cacheReadTokens: 0 };
|
|
4725
|
+
t.promptTokens += u.promptTokens ?? 0;
|
|
4726
|
+
t.completionTokens += u.completionTokens ?? 0;
|
|
4727
|
+
t.totalTokens += u.totalTokens ?? 0;
|
|
4728
|
+
t.cacheCreationTokens += u.cacheCreationTokens ?? 0;
|
|
4729
|
+
t.cacheReadTokens += u.cacheReadTokens ?? 0;
|
|
4730
|
+
};
|
|
4731
|
+
async function* tap(it) {
|
|
4732
|
+
let last;
|
|
4733
|
+
try {
|
|
4734
|
+
for await (const c of it) {
|
|
4735
|
+
if (c?.usage) last = c.usage;
|
|
4736
|
+
yield c;
|
|
4737
|
+
}
|
|
4738
|
+
} finally {
|
|
4739
|
+
add(last);
|
|
4740
|
+
seen();
|
|
4741
|
+
}
|
|
4742
|
+
}
|
|
4743
|
+
const chat = async (o) => {
|
|
4744
|
+
const r = await ai.chat(o);
|
|
4745
|
+
if (o?.model === main2) return r;
|
|
4746
|
+
if (r && typeof r[Symbol.asyncIterator] === "function") return tap(r);
|
|
4747
|
+
add(r?.usage);
|
|
4748
|
+
seen();
|
|
4749
|
+
return r;
|
|
4750
|
+
};
|
|
4751
|
+
return { ai: new Proxy(ai, { get: (t, k) => k === "chat" ? chat : Reflect.get(t, k) }), usage: () => total && { ...total }, model: () => resolved, reset: () => {
|
|
4752
|
+
total = void 0;
|
|
4753
|
+
resolved = void 0;
|
|
4754
|
+
} };
|
|
4755
|
+
}
|
|
4756
|
+
|
|
4612
4757
|
// src/lint.ts
|
|
4613
4758
|
var CODE_RE = /\.(ts|tsx|js|jsx|mjs|cjs)$/;
|
|
4614
4759
|
function checkSyntax(path, content) {
|
|
@@ -4688,7 +4833,7 @@ function reasoningToChatFragment(model, effort) {
|
|
|
4688
4833
|
}
|
|
4689
4834
|
|
|
4690
4835
|
// src/Agent.ts
|
|
4691
|
-
var
|
|
4836
|
+
var log9 = forComponent("Agent");
|
|
4692
4837
|
function envInt(key, fallback) {
|
|
4693
4838
|
const n = typeof process !== "undefined" ? Number(process.env?.[key]) : NaN;
|
|
4694
4839
|
return Number.isFinite(n) && n >= 0 ? n : fallback;
|
|
@@ -4817,6 +4962,13 @@ var AgentOptions = class {
|
|
|
4817
4962
|
hooks;
|
|
4818
4963
|
/** If true: add the `Task` tool so the agent can spawn depth-limited child agents over the VFS. */
|
|
4819
4964
|
subagents = false;
|
|
4965
|
+
/** DELEGATE MODE: `model` plans, `worker` writes. The planner's Write/Edit/MultiEdit/ApplyEdits/bash/Shell are
|
|
4966
|
+
* refused at execution; Task/TaskBatch are on (maxDepth 1, children default to `worker`) and children get the
|
|
4967
|
+
* parent's `postToolUse` too. Off when the models are equal or either is cursor/*. Wins over `advisor`. */
|
|
4968
|
+
delegate;
|
|
4969
|
+
/** ADVISOR MODE: `model` does the work; an `Advise` tool asks `advisor.model` (≤`limit`, default 3) with only
|
|
4970
|
+
* the request + `snapshot()` (host state, optional) + the question. The first write waits for one consult. */
|
|
4971
|
+
advisor;
|
|
4820
4972
|
/** VFS dir of typed-subagent defs (`<dir>/<name>.md`). If set with `subagents`: inject a catalog + enable the `Task` `agentType` param. */
|
|
4821
4973
|
agentsDir;
|
|
4822
4974
|
/** Current recursion depth (0 = top-level); spawned children run at depth+1. */
|
|
@@ -4925,6 +5077,11 @@ var Agent = class _Agent {
|
|
|
4925
5077
|
lastTrimNotified = 0;
|
|
4926
5078
|
// last auto-trim drop count surfaced via host.notify (dedup)
|
|
4927
5079
|
activeTools = [];
|
|
5080
|
+
meter;
|
|
5081
|
+
// delegate/advisor: meters the second model's calls
|
|
5082
|
+
modeModel;
|
|
5083
|
+
lastTask = "";
|
|
5084
|
+
// the current turn's text — what the advisor reads as the request
|
|
4928
5085
|
/** claude-code bridge for agentx-only tools (ccBridge.ts) + the current step's executor it calls. */
|
|
4929
5086
|
ccServer;
|
|
4930
5087
|
ccExec;
|
|
@@ -5108,7 +5265,7 @@ var Agent = class _Agent {
|
|
|
5108
5265
|
const disk = new NodeDiskFilesystem2(process.cwd());
|
|
5109
5266
|
await disk.init();
|
|
5110
5267
|
this.options.fs = new JailedFilesystem2(disk);
|
|
5111
|
-
|
|
5268
|
+
log9.info(`no fs provided \u2014 defaulting to jailed real disk at ${process.cwd()}`);
|
|
5112
5269
|
}
|
|
5113
5270
|
this.buildCtx();
|
|
5114
5271
|
}
|
|
@@ -5125,6 +5282,12 @@ var Agent = class _Agent {
|
|
|
5125
5282
|
const fs = o.fs;
|
|
5126
5283
|
let systemPrompt = o.systemPrompt;
|
|
5127
5284
|
let tools = o.tools;
|
|
5285
|
+
this.ctx.model = o.model;
|
|
5286
|
+
const worker = o.delegate && delegateWorkerFor(o.model, o.delegate.worker);
|
|
5287
|
+
const advisor = !worker && o.advisor ? advisorFor(o.model, o.advisor.model) : void 0;
|
|
5288
|
+
this.meter = worker || advisor ? meterWorkers(o.ai, o.model) : void 0;
|
|
5289
|
+
const ai = this.meter?.ai ?? o.ai;
|
|
5290
|
+
const adv = advisor ? makeAdvisor({ ai, advisor, request: () => this.lastTask, snapshot: o.advisor.snapshot, limit: o.advisor.limit }) : void 0;
|
|
5128
5291
|
if (o.instructionFiles !== false) {
|
|
5129
5292
|
const names = Array.isArray(o.instructionFiles) ? o.instructionFiles : void 0;
|
|
5130
5293
|
const ins = await loadInstructions(fs, names);
|
|
@@ -5155,7 +5318,7 @@ var Agent = class _Agent {
|
|
|
5155
5318
|
if (tool) tools = [...tools, tool];
|
|
5156
5319
|
}
|
|
5157
5320
|
if (o.host && o.askUserQuestion) tools = [...tools, askUserQuestionTool];
|
|
5158
|
-
if (o.subagents) {
|
|
5321
|
+
if (o.subagents || worker) {
|
|
5159
5322
|
let agents;
|
|
5160
5323
|
if (o.agentsDir) {
|
|
5161
5324
|
const loaded = await loadAgents(fs, o.agentsDir);
|
|
@@ -5163,17 +5326,18 @@ var Agent = class _Agent {
|
|
|
5163
5326
|
if (loaded.catalog) systemPrompt += "\n\n" + loaded.catalog;
|
|
5164
5327
|
}
|
|
5165
5328
|
const taskOpts = {
|
|
5166
|
-
ai
|
|
5167
|
-
model: o.model,
|
|
5329
|
+
ai,
|
|
5330
|
+
model: worker || o.model,
|
|
5168
5331
|
fs,
|
|
5169
5332
|
depth: o.depth,
|
|
5170
|
-
maxDepth: o.maxDepth,
|
|
5333
|
+
maxDepth: worker ? Math.min(o.maxDepth, o.depth + 1) : o.maxDepth,
|
|
5171
5334
|
agents,
|
|
5335
|
+
inheritPostToolUse: !!worker,
|
|
5172
5336
|
hooks: o.hooks,
|
|
5173
5337
|
traceDir: o.traceDir,
|
|
5174
5338
|
permissions: o.permissions,
|
|
5175
5339
|
// a child obeys the parent's allow/deny rules (else Task is a one-call bypass)
|
|
5176
|
-
tools: [...o.tools, ...this.injectedTools],
|
|
5340
|
+
tools: worker ? delegateTools([...o.tools, ...this.injectedTools], o.model) : [...o.tools, ...this.injectedTools],
|
|
5177
5341
|
systemPrompt: o.systemPrompt,
|
|
5178
5342
|
// A child never resumes the parent's own claude-code session (the host's `resume` link is the parent's).
|
|
5179
5343
|
providerOptionsFor: o.providerOptionsFor ?? ((m) => modelProvider(m) === modelProvider(o.model) && o.providerOptions ? { ...o.providerOptions, resume: void 0, onClaudeCodeSession: void 0 } : void 0)
|
|
@@ -5186,6 +5350,15 @@ var Agent = class _Agent {
|
|
|
5186
5350
|
if (plan) tools = [...tools, plan.tool];
|
|
5187
5351
|
this.activeHooks = composeHooks(o.hooks, plan?.hooks, o.permissions?.hooks());
|
|
5188
5352
|
this.activeTools = [...tools, ...this.injectedTools];
|
|
5353
|
+
if (worker) {
|
|
5354
|
+
this.activeTools = delegateTools(this.activeTools, o.model);
|
|
5355
|
+
systemPrompt += delegatePrompt(worker);
|
|
5356
|
+
}
|
|
5357
|
+
if (adv) {
|
|
5358
|
+
this.activeTools = [...adv.gate(this.activeTools), adv.tool];
|
|
5359
|
+
systemPrompt += advisorPrompt(advisor);
|
|
5360
|
+
}
|
|
5361
|
+
this.modeModel = worker || advisor;
|
|
5189
5362
|
this.systemPromptCache = systemPrompt;
|
|
5190
5363
|
this.prepared = true;
|
|
5191
5364
|
return systemPrompt;
|
|
@@ -5198,6 +5371,7 @@ var Agent = class _Agent {
|
|
|
5198
5371
|
this.started = false;
|
|
5199
5372
|
this.invokedTools.clear();
|
|
5200
5373
|
this.fabricatedClaims.clear();
|
|
5374
|
+
this.lastTask = contentText(task);
|
|
5201
5375
|
const systemPrompt = await this.prepare(contentText(task));
|
|
5202
5376
|
const startCtx = await this.fireSessionStart();
|
|
5203
5377
|
const userContent = await this.applyPromptSubmit(task);
|
|
@@ -5233,6 +5407,7 @@ var Agent = class _Agent {
|
|
|
5233
5407
|
*/
|
|
5234
5408
|
async send(task) {
|
|
5235
5409
|
await this.ensureFs();
|
|
5410
|
+
this.lastTask = contentText(task);
|
|
5236
5411
|
const systemPrompt = await this.prepare(contentText(task));
|
|
5237
5412
|
const startCtx = await this.fireSessionStart();
|
|
5238
5413
|
const userContent = await this.applyPromptSubmit(task);
|
|
@@ -5283,6 +5458,12 @@ var Agent = class _Agent {
|
|
|
5283
5458
|
if (next !== this.transcript) this.transcript = next;
|
|
5284
5459
|
}
|
|
5285
5460
|
async runLoop() {
|
|
5461
|
+
this.meter?.reset();
|
|
5462
|
+
const res = await this.loop();
|
|
5463
|
+
const workerUsage = this.meter?.usage();
|
|
5464
|
+
return workerUsage ? { ...res, workerModel: this.meter.model() ?? this.modeModel, workerUsage } : res;
|
|
5465
|
+
}
|
|
5466
|
+
async loop() {
|
|
5286
5467
|
const o = this.options;
|
|
5287
5468
|
const wireTools = toWireTools(this.activeTools);
|
|
5288
5469
|
const useStream = o.stream === true && typeof o.host?.notify === "function";
|
|
@@ -5302,7 +5483,7 @@ var Agent = class _Agent {
|
|
|
5302
5483
|
let lastProgressAt = start;
|
|
5303
5484
|
const turnUser = this.transcript[this.transcript.length - 1];
|
|
5304
5485
|
const kill = (finishReason) => {
|
|
5305
|
-
|
|
5486
|
+
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` : ""})`);
|
|
5306
5487
|
this.jobs.killAll(`run ended: ${finishReason}`, true);
|
|
5307
5488
|
const claims = this.survivingClaims();
|
|
5308
5489
|
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 } : {} };
|
|
@@ -5328,9 +5509,9 @@ var Agent = class _Agent {
|
|
|
5328
5509
|
this.nativeHoldMs = o.nativeHoldMs;
|
|
5329
5510
|
if (isCursorWithTools) {
|
|
5330
5511
|
if (typeof hostToolTimeoutMs === "number" && effectiveToolTimeoutMs !== hostToolTimeoutMs) {
|
|
5331
|
-
|
|
5512
|
+
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.`);
|
|
5332
5513
|
}
|
|
5333
|
-
for (const v of toolTimeoutViolations(this.activeTools, effectiveToolTimeoutMs))
|
|
5514
|
+
for (const v of toolTimeoutViolations(this.activeTools, effectiveToolTimeoutMs)) log9.warn(v);
|
|
5334
5515
|
}
|
|
5335
5516
|
const hostExec = async (name, args) => {
|
|
5336
5517
|
const tc = { id: `host-${Date.now()}`, type: "function", function: { name, arguments: JSON.stringify(args) } };
|
|
@@ -5389,7 +5570,7 @@ var Agent = class _Agent {
|
|
|
5389
5570
|
} catch (err2) {
|
|
5390
5571
|
if (!this.imagesOff && /does not support .*input/i.test(String(err2?.message ?? "")) && sent.some((m) => Array.isArray(m.content) && m.role === "tool")) {
|
|
5391
5572
|
this.imagesOff = true;
|
|
5392
|
-
|
|
5573
|
+
log9.warn(`model refused image input (${err2?.message}) \u2014 resending with image Reads as text stubs`);
|
|
5393
5574
|
sent = this.trimContext();
|
|
5394
5575
|
attempt--;
|
|
5395
5576
|
continue;
|
|
@@ -5401,11 +5582,11 @@ var Agent = class _Agent {
|
|
|
5401
5582
|
const empty = err2?.code === "empty" || err2?.code === "cursor_silent";
|
|
5402
5583
|
const maxAttempts = empty ? Math.max(o.emptyRetries, 0) : 2;
|
|
5403
5584
|
const replaysTool = stalled && this.toolRanThisAttempt;
|
|
5404
|
-
if (replaysTool)
|
|
5585
|
+
if (replaysTool) log9.warn("model stall after tool(s) already ran this step \u2014 not retrying (a retry would replay them)");
|
|
5405
5586
|
const transient = !o.signal?.aborted && !isAbortError(err2) && !replaysTool && attempt < maxAttempts && (network || serverSide || stalled || empty);
|
|
5406
5587
|
if (!transient) throw err2;
|
|
5407
5588
|
const waitMs = (empty ? o.emptyRetryBaseMs : 1e3) * (attempt + 1);
|
|
5408
|
-
|
|
5589
|
+
log9.warn(`${empty ? "empty model turn" : stalled ? "model stall" : "network drop"} mid-step (${err2?.message ?? err2}) \u2014 retrying in ${waitMs}ms`);
|
|
5409
5590
|
o.host?.notify?.({ kind: "retry", message: `${empty ? "model returned nothing" : stalled ? "model stalled" : "connection dropped"} \u2014 retrying step (#${attempt + 1})` });
|
|
5410
5591
|
await new Promise((r) => setTimeout(r, waitMs));
|
|
5411
5592
|
}
|
|
@@ -5413,12 +5594,12 @@ var Agent = class _Agent {
|
|
|
5413
5594
|
} catch (err2) {
|
|
5414
5595
|
if (err2?.code === "budget") return kill("budget");
|
|
5415
5596
|
if (err2?.code === "stall") {
|
|
5416
|
-
|
|
5597
|
+
log9.warn(`model stalled out after retries: ${err2?.message ?? err2}`);
|
|
5417
5598
|
return { ...kill("timeout"), providerFinishReason: "none (stalled)", error: err2 };
|
|
5418
5599
|
}
|
|
5419
5600
|
if (o.signal?.aborted || isAbortError(err2)) return kill("aborted");
|
|
5420
5601
|
if (err2?.code === "empty" || err2?.code === "cursor_silent") {
|
|
5421
|
-
|
|
5602
|
+
log9.warn(`model returned empty/silent turns after retries \u2014 surfacing as failed`);
|
|
5422
5603
|
const pfr = err2?.providerFinishReason;
|
|
5423
5604
|
return { text: "", steps, finishReason: "empty", ...pfr ? { providerFinishReason: pfr } : {}, messages: this.transcript, usage, usageEstimated, error: err2 };
|
|
5424
5605
|
}
|
|
@@ -5431,13 +5612,13 @@ var Agent = class _Agent {
|
|
|
5431
5612
|
}
|
|
5432
5613
|
if (bodyStr && err2 instanceof Error && !err2.message.includes(bodyStr)) err2.detail = bodyStr;
|
|
5433
5614
|
const errLog = err2 instanceof Error ? { message: err2.message, ...err2.detail ? { detail: err2.detail } : {}, stack: err2.stack } : err2;
|
|
5434
|
-
|
|
5615
|
+
log9.error(`chat() failed: ${err2?.message ?? err2}${bodyStr ? ` \u2014 ${bodyStr}` : ""}`, errLog);
|
|
5435
5616
|
return { text: "", steps, finishReason: "error", messages: this.transcript, usage, usageEstimated, error: err2 };
|
|
5436
5617
|
}
|
|
5437
5618
|
if (o.signal?.aborted) return kill("aborted");
|
|
5438
5619
|
if (res.finishReason === "error" && !res.toolCalls?.length) {
|
|
5439
5620
|
const err2 = new Error(contentText(res.content ?? "").trim() || "provider error");
|
|
5440
|
-
|
|
5621
|
+
log9.error(`chat() failed: ${err2.message}`);
|
|
5441
5622
|
return { text: "", steps, finishReason: "error", messages: this.transcript, usage, usageEstimated, error: err2 };
|
|
5442
5623
|
}
|
|
5443
5624
|
if (!res.usage) {
|
|
@@ -5505,21 +5686,21 @@ var Agent = class _Agent {
|
|
|
5505
5686
|
if (emptyTurn && o.failEmptyTurns) {
|
|
5506
5687
|
if (!emptyNudged && canNudge()) {
|
|
5507
5688
|
emptyNudged = true;
|
|
5508
|
-
|
|
5689
|
+
log9.warn(`empty model turn after tool work (step ${steps}, provider finishReason=${res.finishReason ?? "n/a"}) \u2014 nudging once`);
|
|
5509
5690
|
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." });
|
|
5510
5691
|
continue;
|
|
5511
5692
|
}
|
|
5512
|
-
|
|
5693
|
+
log9.warn(`model returned empty turns after tool work \u2014 surfacing as failed`);
|
|
5513
5694
|
const e = new Error("empty_response: model returned an empty turn (no text, no tools) after a re-prompt");
|
|
5514
5695
|
e.code = "empty";
|
|
5515
5696
|
return { text: "", steps, finishReason: "empty", ...res.finishReason ? { providerFinishReason: res.finishReason } : {}, messages: this.transcript, usage, usageEstimated, error: e };
|
|
5516
5697
|
}
|
|
5517
5698
|
const fabricated = o.catchFabricatedToolUse && !closureNudged ? this.fabricatedToolNames(contentText(res.content ?? "")) : [];
|
|
5518
5699
|
for (const n of fabricated) this.fabricatedClaims.add(n);
|
|
5519
|
-
if (fabricated.length && !canNudge())
|
|
5700
|
+
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`);
|
|
5520
5701
|
if (fabricated.length && canNudge()) {
|
|
5521
5702
|
closureNudged = true;
|
|
5522
|
-
|
|
5703
|
+
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`);
|
|
5523
5704
|
this.transcript.push({
|
|
5524
5705
|
role: "user",
|
|
5525
5706
|
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.`
|
|
@@ -5528,14 +5709,14 @@ var Agent = class _Agent {
|
|
|
5528
5709
|
}
|
|
5529
5710
|
if (o.closeDelegatedTurns && res.endedWithoutClosing && !closureNudged && canNudge()) {
|
|
5530
5711
|
closureNudged = true;
|
|
5531
|
-
|
|
5712
|
+
log9.verbose("delegated turn went silent after a tool \u2014 nudging a closing answer");
|
|
5532
5713
|
this.transcript.push({
|
|
5533
5714
|
role: "user",
|
|
5534
5715
|
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."
|
|
5535
5716
|
});
|
|
5536
5717
|
continue;
|
|
5537
5718
|
}
|
|
5538
|
-
|
|
5719
|
+
log9.verbose(`completed in ${steps} step(s)`);
|
|
5539
5720
|
const drained = await this.jobs.drain(o.signal);
|
|
5540
5721
|
if (o.signal?.aborted) return kill("aborted");
|
|
5541
5722
|
if (drained.length) lastProgressAt = Date.now();
|
|
@@ -5569,7 +5750,7 @@ var Agent = class _Agent {
|
|
|
5569
5750
|
if (this.ctx.needsInput) {
|
|
5570
5751
|
const question = this.ctx.needsInput;
|
|
5571
5752
|
this.ctx.needsInput = void 0;
|
|
5572
|
-
|
|
5753
|
+
log9.warn(`needs_input: ${question.question} (steps=${steps})`);
|
|
5573
5754
|
await this.jobs.drain();
|
|
5574
5755
|
const text = [
|
|
5575
5756
|
`OPEN QUESTION (run paused \u2014 no human was reachable): ${question.question}`,
|
|
@@ -5580,7 +5761,7 @@ var Agent = class _Agent {
|
|
|
5580
5761
|
if (this.ctx.endTurn) {
|
|
5581
5762
|
this.ctx.endTurn = void 0;
|
|
5582
5763
|
if (this.drainInjections()) continue;
|
|
5583
|
-
|
|
5764
|
+
log9.verbose(`turn ended by a tool (steps=${steps})`);
|
|
5584
5765
|
await this.jobs.drain(o.signal);
|
|
5585
5766
|
return { text: "", steps, finishReason: "stop", messages: this.transcript, usage, usageEstimated };
|
|
5586
5767
|
}
|
|
@@ -5618,12 +5799,12 @@ var Agent = class _Agent {
|
|
|
5618
5799
|
if (this.toolHoldActive()) {
|
|
5619
5800
|
const now4 = Date.now();
|
|
5620
5801
|
const who = this.jobs.holding().map((j) => `${j.kind} "${j.label}" (${Math.round((now4 - j.startedAt) / 1e3)}s, quiet ${Math.round((now4 - j.lastActivityAt) / 1e3)}s)`).join(", ");
|
|
5621
|
-
|
|
5802
|
+
log9.warn(`stall watchdog: no stream output for ${stallMs}ms \u2014 held by ${who}`);
|
|
5622
5803
|
this.options.host?.notify?.({ kind: "tool_hold", message: `waiting on ${who}` });
|
|
5623
5804
|
return poke();
|
|
5624
5805
|
}
|
|
5625
5806
|
const inFlight = this.jobs.list().filter((j) => j.status === "running");
|
|
5626
|
-
|
|
5807
|
+
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(", ")})` : ""));
|
|
5627
5808
|
fired = true;
|
|
5628
5809
|
ac.abort(stallError());
|
|
5629
5810
|
}, stallMs);
|
|
@@ -5793,13 +5974,13 @@ var Agent = class _Agent {
|
|
|
5793
5974
|
const decision = await this.park(Promise.resolve(hooks?.preToolUse?.(call, meta)));
|
|
5794
5975
|
if (decision?.block) {
|
|
5795
5976
|
const blocked = `Blocked by hook: ${decision.reason ?? "no reason given"}`;
|
|
5796
|
-
|
|
5977
|
+
log9.debug(`${tc.function.name} -> ${blocked}`);
|
|
5797
5978
|
await hooks?.postToolUse?.(call, blocked, meta);
|
|
5798
5979
|
return blocked;
|
|
5799
5980
|
}
|
|
5800
5981
|
this.options.host?.notify?.({ kind: "tool_use", id: tc.id ?? "", name: tc.function.name, input: args });
|
|
5801
5982
|
if (earlyError) {
|
|
5802
|
-
|
|
5983
|
+
log9.debug(`${tc.function.name} -> ${earlyError}`);
|
|
5803
5984
|
await hooks?.postToolUse?.(call, earlyError, meta);
|
|
5804
5985
|
this.options.host?.notify?.({ kind: "tool_result", id: tc.id ?? "", output: earlyError, isError: true });
|
|
5805
5986
|
return earlyError;
|
|
@@ -5808,12 +5989,12 @@ var Agent = class _Agent {
|
|
|
5808
5989
|
let images;
|
|
5809
5990
|
let threw = false;
|
|
5810
5991
|
try {
|
|
5811
|
-
|
|
5992
|
+
log9.debug(`${tc.function.name}(${tc.function.arguments})`);
|
|
5812
5993
|
this.ctx.emit = hooks?.onToolOutput ? (chunk) => {
|
|
5813
5994
|
try {
|
|
5814
5995
|
hooks.onToolOutput(call, chunk, meta);
|
|
5815
5996
|
} catch (e) {
|
|
5816
|
-
|
|
5997
|
+
log9.debug(`onToolOutput hook error: ${e}`);
|
|
5817
5998
|
}
|
|
5818
5999
|
} : void 0;
|
|
5819
6000
|
const raw = await tool.run(args, this.ctx);
|
|
@@ -5825,7 +6006,7 @@ var Agent = class _Agent {
|
|
|
5825
6006
|
}
|
|
5826
6007
|
} catch (e) {
|
|
5827
6008
|
const msg = e instanceof Error ? e.message : String(e);
|
|
5828
|
-
|
|
6009
|
+
log9.debug(`${tc.function.name} -> error: ${msg}`);
|
|
5829
6010
|
result = `Error: ${msg}`;
|
|
5830
6011
|
threw = true;
|
|
5831
6012
|
} finally {
|
|
@@ -6102,7 +6283,7 @@ function fitTokenBudget(messages, maxTokens, keepRecentImages = 1, vision = true
|
|
|
6102
6283
|
const ids = callIdSet(messages.slice(from));
|
|
6103
6284
|
while (from < messages.length && messages[from].role === "tool" && !ids.has(messages[from].tool_call_id ?? "")) total -= per[from++];
|
|
6104
6285
|
if (total > maxTokens)
|
|
6105
|
-
|
|
6286
|
+
log9.warn(`context ~${total} tok still over maxContextTokens=${maxTokens} after trimming (system head can't be dropped)`);
|
|
6106
6287
|
return [...head, ...messages.slice(from)];
|
|
6107
6288
|
}
|
|
6108
6289
|
function compact(m, max, focus) {
|
|
@@ -6682,7 +6863,7 @@ init_tools_web();
|
|
|
6682
6863
|
init_tools();
|
|
6683
6864
|
init_tools_structured();
|
|
6684
6865
|
init_logging();
|
|
6685
|
-
var
|
|
6866
|
+
var log10 = forComponent("scratch");
|
|
6686
6867
|
var SCRATCH_DIR = "/scratch";
|
|
6687
6868
|
function shortArgs(args) {
|
|
6688
6869
|
try {
|
|
@@ -6722,7 +6903,7 @@ var Scratch = class {
|
|
|
6722
6903
|
await (this.dirReady ??= mkdirp(this.fs, dir));
|
|
6723
6904
|
await this.fs.writeFile(path, header + raw);
|
|
6724
6905
|
} catch (e) {
|
|
6725
|
-
|
|
6906
|
+
log10.debug("scratch write failed; returning raw", e);
|
|
6726
6907
|
return raw;
|
|
6727
6908
|
}
|
|
6728
6909
|
const preview = raw.slice(0, previewChars).replace(/\s+/g, " ").trim();
|
|
@@ -6752,7 +6933,7 @@ To pull a specific detail, Grep/Read ${path}, or call Ask({ question: "\u2026",
|
|
|
6752
6933
|
await (this.dirReady ??= mkdirp(this.fs, dir));
|
|
6753
6934
|
await this.fs.writeFile(path, header + full);
|
|
6754
6935
|
} catch (e) {
|
|
6755
|
-
|
|
6936
|
+
log10.debug("scratch spill failed; cropping lossy", e);
|
|
6756
6937
|
return full.slice(0, pageBytes) + `
|
|
6757
6938
|
|
|
6758
6939
|
[output cropped to ${pageBytes} of ${full.length} bytes; full output unavailable (scratch write failed) \u2014 refine your query]`;
|
|
@@ -6798,7 +6979,7 @@ Question: ${q2}`);
|
|
|
6798
6979
|
const answer = (res.text ?? "").trim();
|
|
6799
6980
|
return answer || "(no answer found in scratch)";
|
|
6800
6981
|
} catch (e) {
|
|
6801
|
-
|
|
6982
|
+
log10.debug("Ask peek failed", e);
|
|
6802
6983
|
return `Error querying scratch: ${e?.message ?? e}`;
|
|
6803
6984
|
}
|
|
6804
6985
|
}
|
|
@@ -6807,7 +6988,7 @@ Question: ${q2}`);
|
|
|
6807
6988
|
|
|
6808
6989
|
// src/lessons.ts
|
|
6809
6990
|
init_logging();
|
|
6810
|
-
var
|
|
6991
|
+
var log11 = forComponent("Lessons");
|
|
6811
6992
|
var LessonOptionsDefaults = class {
|
|
6812
6993
|
minRepeats = 2;
|
|
6813
6994
|
};
|
|
@@ -6832,8 +7013,8 @@ function lessonCapture(options) {
|
|
|
6832
7013
|
counts.set(lesson.slug, n);
|
|
6833
7014
|
if (n < o.minRepeats) return;
|
|
6834
7015
|
written.add(lesson.slug);
|
|
6835
|
-
await writeFact(o.fs, o.dir, lesson.slug, lesson.body).catch((e) =>
|
|
6836
|
-
|
|
7016
|
+
await writeFact(o.fs, o.dir, lesson.slug, lesson.body).catch((e) => log11.warn(`could not persist ${lesson.slug}: ${e?.message ?? e}`));
|
|
7017
|
+
log11.debug(`captured lesson ${lesson.slug} (recurred ${n}\xD7)`);
|
|
6837
7018
|
}
|
|
6838
7019
|
};
|
|
6839
7020
|
}
|
|
@@ -6841,7 +7022,7 @@ function lessonCapture(options) {
|
|
|
6841
7022
|
// src/reflect.ts
|
|
6842
7023
|
init_llm();
|
|
6843
7024
|
init_logging();
|
|
6844
|
-
var
|
|
7025
|
+
var log12 = forComponent("Reflect");
|
|
6845
7026
|
async function reflectOnRun(o) {
|
|
6846
7027
|
const digest = digestRun(o.result.messages, o.maxDigestChars ?? 6e3);
|
|
6847
7028
|
if (!digest.trim()) return null;
|
|
@@ -6857,7 +7038,7 @@ If the run was fine or the issue was purely task-specific (not generalizable), r
|
|
|
6857
7038
|
const r = await o.ai.chat({ model: o.model, messages: [{ role: "user", content: prompt }], stream: false });
|
|
6858
7039
|
text = r?.content ?? "";
|
|
6859
7040
|
} catch (e) {
|
|
6860
|
-
|
|
7041
|
+
log12.warn(`reflection call failed: ${e?.message ?? e}`);
|
|
6861
7042
|
return null;
|
|
6862
7043
|
}
|
|
6863
7044
|
const m = text.match(/LESSON:\s*(.+)/i);
|
|
@@ -6867,10 +7048,10 @@ If the run was fine or the issue was purely task-specific (not generalizable), r
|
|
|
6867
7048
|
try {
|
|
6868
7049
|
await writeFact(o.fs, o.dir, slug2, lesson);
|
|
6869
7050
|
} catch (e) {
|
|
6870
|
-
|
|
7051
|
+
log12.warn(`could not persist lesson: ${e?.message ?? e}`);
|
|
6871
7052
|
return null;
|
|
6872
7053
|
}
|
|
6873
|
-
|
|
7054
|
+
log12.debug(`reflection persisted ${slug2}`);
|
|
6874
7055
|
return slug2;
|
|
6875
7056
|
}
|
|
6876
7057
|
function digestRun(messages, maxChars) {
|
|
@@ -6920,7 +7101,7 @@ function forComponent2(name) {
|
|
|
6920
7101
|
}
|
|
6921
7102
|
|
|
6922
7103
|
// node_modules/@bod.ee/voice/src/core/emotion.ts
|
|
6923
|
-
var
|
|
7104
|
+
var log13 = forComponent2("Emotion");
|
|
6924
7105
|
var EMOTIONS = [
|
|
6925
7106
|
// primary (best results)
|
|
6926
7107
|
"neutral",
|
|
@@ -7052,7 +7233,7 @@ function renderEmotions(text, opts = { show: true }) {
|
|
|
7052
7233
|
speech += cartesiaTag(tag);
|
|
7053
7234
|
if (opts.show) display += m[0];
|
|
7054
7235
|
} else {
|
|
7055
|
-
|
|
7236
|
+
log13.debug(`dropping unknown emotion tag ${m[0]}`);
|
|
7056
7237
|
}
|
|
7057
7238
|
last = m.index + m[0].length;
|
|
7058
7239
|
}
|
|
@@ -7094,7 +7275,7 @@ var EmotionStream = class {
|
|
|
7094
7275
|
if (tag) {
|
|
7095
7276
|
this.pending = tag;
|
|
7096
7277
|
if (this.show) display += m[0];
|
|
7097
|
-
} else
|
|
7278
|
+
} else log13.debug(`dropping unknown emotion tag ${m[0]}`);
|
|
7098
7279
|
last = m.index + m[0].length;
|
|
7099
7280
|
}
|
|
7100
7281
|
this.emit(body.slice(last), (s, d, p) => {
|
|
@@ -7241,7 +7422,7 @@ function computeHoldMs(ctx) {
|
|
|
7241
7422
|
}
|
|
7242
7423
|
|
|
7243
7424
|
// node_modules/@bod.ee/voice/src/core/engine.ts
|
|
7244
|
-
var
|
|
7425
|
+
var log14 = forComponent2("VoiceEngine");
|
|
7245
7426
|
var realClock = {
|
|
7246
7427
|
now: () => performance.now(),
|
|
7247
7428
|
setTimeout: (fn, ms) => setTimeout(fn, ms),
|
|
@@ -7629,8 +7810,8 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
7629
7810
|
this.tts.warmup?.();
|
|
7630
7811
|
this.setState("listening");
|
|
7631
7812
|
if (this.options.bargeInMode === "aec" && !this.stt.usingAec)
|
|
7632
|
-
|
|
7633
|
-
|
|
7813
|
+
log14.warn("bargeInMode=aec but mic does not report echoCancellation applied \u2014 STT may transcribe the assistant");
|
|
7814
|
+
log14.debug(`voice I/O up (${this.usingAec ? "AEC" : "heuristic echo"} capture, mode=${this.options.bargeInMode}, mic reports aec=${this.stt.usingAec})`);
|
|
7634
7815
|
}
|
|
7635
7816
|
/** The RESOLVED barge-in tier (the single seam every tier branch reads). `bargeInMode` overrides
|
|
7636
7817
|
* the mic's self-report — see the option doc for why the report is not trustworthy on mobile. */
|
|
@@ -7655,7 +7836,7 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
7655
7836
|
this.options.onDiag({ t: this.clock.now(), kind, ...fields });
|
|
7656
7837
|
} catch (e) {
|
|
7657
7838
|
this.diagOn = false;
|
|
7658
|
-
|
|
7839
|
+
log14.debug(`onDiag threw \u2014 diagnostics disabled: ${e instanceof Error ? e.message : e}`);
|
|
7659
7840
|
}
|
|
7660
7841
|
}
|
|
7661
7842
|
idleWaiters = [];
|
|
@@ -7794,7 +7975,7 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
7794
7975
|
}
|
|
7795
7976
|
for (const w of this.words(this.reply)) this.echoWords.add(w);
|
|
7796
7977
|
this.tts.speak(forSpeech(speech), true);
|
|
7797
|
-
if (!this.spokeDeltas && this.turnStartAt)
|
|
7978
|
+
if (!this.spokeDeltas && this.turnStartAt) log14.debug(`ttft: ${Math.round(this.clock.now() - this.turnStartAt)}ms`);
|
|
7798
7979
|
this.spokeDeltas = true;
|
|
7799
7980
|
this.setState("speaking");
|
|
7800
7981
|
if (this.options.revealMode !== "off" && display) {
|
|
@@ -7907,13 +8088,13 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
7907
8088
|
this.drainTimer = null;
|
|
7908
8089
|
this.speaking = false;
|
|
7909
8090
|
this.stopWordReveal(true);
|
|
7910
|
-
if (this.turnStartAt)
|
|
8091
|
+
if (this.turnStartAt) log14.debug(`turn: ${Math.round(this.clock.now() - this.turnStartAt)}ms (incl. playback)`);
|
|
7911
8092
|
this.echoUntil = this.clock.now() + 2500;
|
|
7912
8093
|
if (!this.usingAec) this.stt.reset();
|
|
7913
8094
|
try {
|
|
7914
8095
|
this.options.onSpeechEnd();
|
|
7915
8096
|
} catch (e) {
|
|
7916
|
-
|
|
8097
|
+
log14.warn(`onSpeechEnd threw: ${e instanceof Error ? e.message : e}`);
|
|
7917
8098
|
}
|
|
7918
8099
|
this.setState("listening");
|
|
7919
8100
|
if (this.uttQueue.length) this.pumpQueue();
|
|
@@ -7987,7 +8168,7 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
7987
8168
|
if (!host) return pick;
|
|
7988
8169
|
const ctx = { text: this.lastDispatchText, question: this.lastDispatchWasQuestion, barged: this.bargedBeforeDispatch };
|
|
7989
8170
|
const fallback = (e) => {
|
|
7990
|
-
|
|
8171
|
+
log14.warn(`pickAck failed \u2014 using the default pools: ${e instanceof Error ? e.message : e}`);
|
|
7991
8172
|
pick.picked = false;
|
|
7992
8173
|
return void 0;
|
|
7993
8174
|
};
|
|
@@ -8077,7 +8258,7 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
8077
8258
|
this.ackProbe = null;
|
|
8078
8259
|
const queued = this.uttQueue.splice(0);
|
|
8079
8260
|
const droppedQueued = queued.length;
|
|
8080
|
-
if (droppedQueued)
|
|
8261
|
+
if (droppedQueued) log14.info(`barge-in cut ${droppedQueued} queued worker utterance(s) \u2014 handed to the host`);
|
|
8081
8262
|
this.overlapKeptUntil = 0;
|
|
8082
8263
|
if (!this.speaking && !this.drainTimer) {
|
|
8083
8264
|
if (droppedQueued) this.lastInterrupted = { full: "", heard: "", queued, queuedTurn: false };
|
|
@@ -8306,7 +8487,7 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
8306
8487
|
if (flat && this.lastDispatchFlat && this.clock.now() - this.lastDispatchAt < _VoiceEngine.DUP_FINAL_MS && (this.lastDispatchFlat.startsWith(flat) || flat.startsWith(this.lastDispatchFlat))) return;
|
|
8307
8488
|
this.specSpent = true;
|
|
8308
8489
|
this.specText = this.specPartial;
|
|
8309
|
-
|
|
8490
|
+
log14.debug(`speculate: "${this.specText.slice(0, 60)}"`);
|
|
8310
8491
|
this.diag("speculate", { text: this.specText });
|
|
8311
8492
|
this.options.onSpeculate(this.specText);
|
|
8312
8493
|
}, this.options.speculativeMs);
|
|
@@ -8375,7 +8556,7 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
8375
8556
|
};
|
|
8376
8557
|
this.tts.newContext();
|
|
8377
8558
|
this.tts.speak(phrase, false);
|
|
8378
|
-
|
|
8559
|
+
log14.debug(`backchannel: "${phrase}"`);
|
|
8379
8560
|
this.diag("backchannel", { phrase });
|
|
8380
8561
|
this.options.onBackchannel(phrase);
|
|
8381
8562
|
}
|
|
@@ -8408,7 +8589,7 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
8408
8589
|
});
|
|
8409
8590
|
if (idx < 0) return text;
|
|
8410
8591
|
toks.splice(idx, 1);
|
|
8411
|
-
|
|
8592
|
+
log14.verbose(`stripped backchannel echo "${this.lastBcPhrase}" from dispatching final`);
|
|
8412
8593
|
return toks.join(" ");
|
|
8413
8594
|
}
|
|
8414
8595
|
/** Merge a resumed utterance into the pending one, deduping any word-overlap. Soniox re-finalizes
|
|
@@ -8594,7 +8775,7 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
8594
8775
|
}
|
|
8595
8776
|
const bargeHold = !this.looksIncomplete(this.pendingUtt) && this.unfinishedBargeLine(this.pendingUtt);
|
|
8596
8777
|
if (this.options.incompleteMergeMs && (bargeHold || this.looksIncomplete(this.pendingUtt))) {
|
|
8597
|
-
|
|
8778
|
+
log14.verbose(`hold: incomplete utterance "${this.pendingUtt.slice(-40)}"`);
|
|
8598
8779
|
this.diag("hold", { reason: bargeHold ? "barge_unfinished" : "incomplete", tail: this.pendingUtt.slice(-40) });
|
|
8599
8780
|
this.options.onHold();
|
|
8600
8781
|
if (this.options.holdFiller && !this.speaking) this.speakFiller(this.options.holdFiller);
|
|
@@ -8633,13 +8814,13 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
8633
8814
|
const answered = this.answeredSinceDispatch();
|
|
8634
8815
|
const part = flat !== this.lastDispatchFlat && !answered && this.echoesDispatch(text);
|
|
8635
8816
|
if (part || flat && flat === this.lastDispatchFlat && !answered && this.clock.now() - this.lastDispatchAt < _VoiceEngine.DUP_FINAL_MS) {
|
|
8636
|
-
|
|
8817
|
+
log14.verbose(`dropped duplicate final "${text.slice(0, 40)}" (identical to the just-dispatched utterance, no reply between)`);
|
|
8637
8818
|
this.diag("utterance_drop", { reason: "dup_final", text: text.slice(0, 60) });
|
|
8638
8819
|
return;
|
|
8639
8820
|
}
|
|
8640
8821
|
const refinal = this.refinalizes(text);
|
|
8641
8822
|
if (refinal && this.repliedSinceDispatch) {
|
|
8642
|
-
|
|
8823
|
+
log14.verbose(`dropped re-finalization "${text.slice(0, 60)}" (superset of the just-dispatched utterance, reply already flowing)`);
|
|
8643
8824
|
this.diag("utterance_drop", { reason: "refinalized", text: text.slice(0, 60) });
|
|
8644
8825
|
this.lastDispatchFlat = flat;
|
|
8645
8826
|
this.lastDispatchWords = text.trim().split(/\s+/);
|
|
@@ -8918,7 +9099,7 @@ var SpokenSplitter = class {
|
|
|
8918
9099
|
};
|
|
8919
9100
|
|
|
8920
9101
|
// node_modules/@bod.ee/voice/src/adapters/soniox.ts
|
|
8921
|
-
var
|
|
9102
|
+
var log15 = forComponent2("SonioxSTT");
|
|
8922
9103
|
var now = () => performance.now();
|
|
8923
9104
|
var SonioxSTTOptions = class {
|
|
8924
9105
|
auth = "";
|
|
@@ -8963,7 +9144,7 @@ var SonioxSTT = class {
|
|
|
8963
9144
|
this.onDiag({ t: now(), kind, ...fields });
|
|
8964
9145
|
} catch (e) {
|
|
8965
9146
|
this.diagOn = false;
|
|
8966
|
-
|
|
9147
|
+
log15.debug(`onDiag threw \u2014 STT diagnostics disabled: ${e instanceof Error ? e.message : e}`);
|
|
8967
9148
|
}
|
|
8968
9149
|
}
|
|
8969
9150
|
lastChunkAt = 0;
|
|
@@ -9005,11 +9186,11 @@ var SonioxSTT = class {
|
|
|
9005
9186
|
this.ws.onmessage = (ev) => this.handle(JSON.parse(String(ev.data)));
|
|
9006
9187
|
this.ws.onclose = (ev) => {
|
|
9007
9188
|
if (this.stopped) return;
|
|
9008
|
-
|
|
9189
|
+
log15.warn(`soniox ws closed (${ev.code} ${ev.reason || ""}) \u2014 reconnecting`);
|
|
9009
9190
|
this.diag("stt_ws_closed", { code: ev.code, reason: String(ev.reason || ""), reconnecting: true });
|
|
9010
9191
|
this.reset();
|
|
9011
9192
|
this.connectWs().catch((e) => {
|
|
9012
|
-
|
|
9193
|
+
log15.error(`soniox reconnect failed: ${e.message}`);
|
|
9013
9194
|
this.diag("stt_reconnect_failed", { message: e.message });
|
|
9014
9195
|
});
|
|
9015
9196
|
};
|
|
@@ -9021,7 +9202,7 @@ var SonioxSTT = class {
|
|
|
9021
9202
|
this.endpointTimer = setInterval(() => {
|
|
9022
9203
|
const combined = (this.finalText + this.partialText).trim();
|
|
9023
9204
|
if (!combined || now() - this.lastChangeAt < this.options.silenceEndpointMs) return;
|
|
9024
|
-
if (this.firstTokenAt)
|
|
9205
|
+
if (this.firstTokenAt) log15.debug(`stt: ${Math.round(now() - this.firstTokenAt)}ms first-token\u2192silence-endpoint, "${combined.slice(0, 60)}"`);
|
|
9025
9206
|
this.reset();
|
|
9026
9207
|
this.onUtterance(combined, now());
|
|
9027
9208
|
}, 120);
|
|
@@ -9033,7 +9214,7 @@ var SonioxSTT = class {
|
|
|
9033
9214
|
if (this.stopped) return;
|
|
9034
9215
|
const ref = this.lastChunkAt || this.startedChunksAt;
|
|
9035
9216
|
if (now() - ref > noAudioMs) {
|
|
9036
|
-
|
|
9217
|
+
log15.error(`stt: no mic audio for >${Math.round(noAudioMs / 1e3)}s \u2014 capture device stopped delivering`);
|
|
9037
9218
|
this.diag("stt_watchdog_fatal", { noAudioMs });
|
|
9038
9219
|
this.onFatal("microphone stopped delivering audio (try a different input device, e.g. AirPods, or check System Settings \u2192 Sound \u2192 Input)");
|
|
9039
9220
|
this.stop();
|
|
@@ -9056,7 +9237,7 @@ var SonioxSTT = class {
|
|
|
9056
9237
|
handle(m) {
|
|
9057
9238
|
if (m.error_message) {
|
|
9058
9239
|
this.diag("stt_error", { message: String(m.error_message), code: m.error_code });
|
|
9059
|
-
return
|
|
9240
|
+
return log15.error(`soniox: ${m.error_message}`);
|
|
9060
9241
|
}
|
|
9061
9242
|
let endpoint = false;
|
|
9062
9243
|
for (const t of m.tokens ?? []) {
|
|
@@ -9073,7 +9254,7 @@ var SonioxSTT = class {
|
|
|
9073
9254
|
this.onPartial(combined);
|
|
9074
9255
|
if (endpoint && this.finalText.trim()) {
|
|
9075
9256
|
const utterance = this.finalText.trim();
|
|
9076
|
-
if (this.firstTokenAt)
|
|
9257
|
+
if (this.firstTokenAt) log15.debug(`stt: ${Math.round(now() - this.firstTokenAt)}ms first-token\u2192endpoint, "${utterance.slice(0, 60)}"`);
|
|
9077
9258
|
this.reset();
|
|
9078
9259
|
this.onUtterance(utterance, now());
|
|
9079
9260
|
}
|
|
@@ -9095,7 +9276,7 @@ var SonioxSTT = class {
|
|
|
9095
9276
|
};
|
|
9096
9277
|
|
|
9097
9278
|
// node_modules/@bod.ee/voice/src/adapters/cartesia.ts
|
|
9098
|
-
var
|
|
9279
|
+
var log16 = forComponent2("CartesiaTTS");
|
|
9099
9280
|
var now2 = () => performance.now();
|
|
9100
9281
|
var CartesiaTTSOptions = class {
|
|
9101
9282
|
auth = "";
|
|
@@ -9131,7 +9312,7 @@ var CartesiaTTS = class _CartesiaTTS {
|
|
|
9131
9312
|
this.onDiag({ t: now2(), kind, ...fields });
|
|
9132
9313
|
} catch (e) {
|
|
9133
9314
|
this.diagOn = false;
|
|
9134
|
-
|
|
9315
|
+
log16.debug(`onDiag threw \u2014 TTS diagnostics disabled: ${e instanceof Error ? e.message : e}`);
|
|
9135
9316
|
}
|
|
9136
9317
|
}
|
|
9137
9318
|
firstAudioAt = 0;
|
|
@@ -9166,11 +9347,11 @@ var CartesiaTTS = class _CartesiaTTS {
|
|
|
9166
9347
|
this.ws.onerror = (e) => rej(new Error(`cartesia ws: ${e.message || "connect failed"}`));
|
|
9167
9348
|
});
|
|
9168
9349
|
this.ws.onclose = (ev) => {
|
|
9169
|
-
|
|
9350
|
+
log16.warn(`cartesia ws closed (${ev.code} ${ev.reason || ""})`);
|
|
9170
9351
|
this.diag("tts_ws_closed", { code: ev.code, reason: String(ev.reason || ""), reconnecting: !this.closed });
|
|
9171
9352
|
if (!this.closed) {
|
|
9172
9353
|
this.connecting = this.doConnect().catch((e) => {
|
|
9173
|
-
|
|
9354
|
+
log16.error(`cartesia reconnect failed: ${e.message}`);
|
|
9174
9355
|
this.diag("tts_reconnect_failed", { message: e.message });
|
|
9175
9356
|
});
|
|
9176
9357
|
}
|
|
@@ -9198,12 +9379,12 @@ var CartesiaTTS = class _CartesiaTTS {
|
|
|
9198
9379
|
this.down = true;
|
|
9199
9380
|
this.downAt = now2();
|
|
9200
9381
|
this.consecutiveOk = 0;
|
|
9201
|
-
|
|
9382
|
+
log16.warn(`TTS circuit breaker open \u2014 ${this.consecutiveErrors} consecutive errors, switching to text-only`);
|
|
9202
9383
|
this.diag("tts_breaker_open", { errors: this.consecutiveErrors });
|
|
9203
9384
|
this.onDone();
|
|
9204
9385
|
this.startProbe();
|
|
9205
9386
|
} else if (!this.down) {
|
|
9206
|
-
(/No valid transcripts/i.test(m.message || "") ?
|
|
9387
|
+
(/No valid transcripts/i.test(m.message || "") ? log16.debug : log16.warn)(`cartesia: ${JSON.stringify(m)}`);
|
|
9207
9388
|
}
|
|
9208
9389
|
}
|
|
9209
9390
|
};
|
|
@@ -9218,7 +9399,7 @@ var CartesiaTTS = class _CartesiaTTS {
|
|
|
9218
9399
|
this.stopProbe();
|
|
9219
9400
|
const downMs = this.downAt ? now2() - this.downAt : 0;
|
|
9220
9401
|
this.diag("tts_breaker_close", { downMs: Math.round(downMs) });
|
|
9221
|
-
(downMs < 2e3 ?
|
|
9402
|
+
(downMs < 2e3 ? log16.debug : log16.info)(`TTS recovered${downMs ? ` (down ${downMs}ms)` : ""}`);
|
|
9222
9403
|
}
|
|
9223
9404
|
/** Ensure the WS is open before sending — reconnects if idle-closed. */
|
|
9224
9405
|
async ensureConnected() {
|
|
@@ -9301,13 +9482,13 @@ function base64ToBytes(b64) {
|
|
|
9301
9482
|
}
|
|
9302
9483
|
|
|
9303
9484
|
// node_modules/@bod.ee/voice/src/adapters/fish.ts
|
|
9304
|
-
var
|
|
9485
|
+
var log17 = forComponent2("FishTTS");
|
|
9305
9486
|
|
|
9306
9487
|
// node_modules/@bod.ee/voice/src/adapters/grok.ts
|
|
9307
|
-
var
|
|
9488
|
+
var log18 = forComponent2("GrokTTS");
|
|
9308
9489
|
|
|
9309
9490
|
// node_modules/@bod.ee/voice/src/adapters/grok-stt.ts
|
|
9310
|
-
var
|
|
9491
|
+
var log19 = forComponent2("GrokSTT");
|
|
9311
9492
|
|
|
9312
9493
|
// src/voice/engine.ts
|
|
9313
9494
|
init_logging();
|
|
@@ -9323,7 +9504,7 @@ import { AIClient, DecideClient, JevAdapter, LlmDecideAdapter } from "ai.libx.js
|
|
|
9323
9504
|
|
|
9324
9505
|
// src/voice/overlap.ts
|
|
9325
9506
|
init_logging();
|
|
9326
|
-
var
|
|
9507
|
+
var log20 = forComponent("Overlap");
|
|
9327
9508
|
var norm2 = (s) => s.toLowerCase().replace(/[’']/g, "'").replace(/[^\p{L}\p{N}' -]+/gu, " ").replace(/\s+/g, " ").trim();
|
|
9328
9509
|
var HARD = /\b(?:stop|wait|no|nope|nah|hold on|hang on|hold up|pause|cancel|quiet|enough|shut up|never ?mind|actually|but|sorry what|what|why|how|who|when|where|which|can you|could you|tell me|i mean|excuse me)\b/;
|
|
9329
9510
|
var ACK = new Set("yeah yea yes yep yup ya ok okay k right sure true totally exactly definitely absolutely indeed mm mmm hmm mhm mm-hm uh-huh uh huh aha ah oh ooh wow nice cool great awesome perfect good fine interesting gotcha got it i see makes sense fair agreed correct of course for sure lol ha haha hahaha heh hehe yeah yeah alright all right sweet neat oh nice oh wow oh cool oh okay oh yeah oh right that's right that's true thats right thats true love it i know me too same".split(" "));
|
|
@@ -9403,8 +9584,8 @@ var OverlapJudge = class {
|
|
|
9403
9584
|
const t0 = this.o.now();
|
|
9404
9585
|
const done = (verdict, by) => {
|
|
9405
9586
|
const d = { verdict, by, ms: Math.round(this.o.now() - t0) };
|
|
9406
|
-
if (by === "late" || by === "error" || by === "none")
|
|
9407
|
-
else
|
|
9587
|
+
if (by === "late" || by === "error" || by === "none") log20.warn(`overlap "${heard.slice(0, 60)}": no verdict (${by}, ${d.ms}ms) \u2192 cut`);
|
|
9588
|
+
else log20.debug(`overlap "${heard.slice(0, 60)}": ${verdict} by ${by} ${d.ms}ms`);
|
|
9408
9589
|
this.o.onDecision(heard, d);
|
|
9409
9590
|
return d;
|
|
9410
9591
|
};
|
|
@@ -9421,7 +9602,7 @@ var OverlapJudge = class {
|
|
|
9421
9602
|
});
|
|
9422
9603
|
try {
|
|
9423
9604
|
const v = await Promise.race([this.o.model(heard, ctl.signal).catch((e) => {
|
|
9424
|
-
if (!ctl.signal.aborted)
|
|
9605
|
+
if (!ctl.signal.aborted) log20.warn(`overlap model failed: ${e.message}`);
|
|
9425
9606
|
return "error";
|
|
9426
9607
|
}), late]);
|
|
9427
9608
|
if (v === "late" || v === "error") return done("cut", v);
|
|
@@ -9437,15 +9618,15 @@ function overlapModelFor(ai, env = process.env) {
|
|
|
9437
9618
|
const m = fastOverlapModel(ai, model);
|
|
9438
9619
|
const t0 = performance.now();
|
|
9439
9620
|
void m("yeah", AbortSignal.timeout(1e4)).then(
|
|
9440
|
-
() =>
|
|
9441
|
-
(e) =>
|
|
9621
|
+
() => log20.debug(`fast model warm in ${Math.round(performance.now() - t0)}ms`),
|
|
9622
|
+
(e) => log20.warn(`fast model warm-up failed (the first overlap verdict may be late): ${e?.message ?? e}`)
|
|
9442
9623
|
);
|
|
9443
9624
|
return m;
|
|
9444
9625
|
}
|
|
9445
9626
|
var softBarge = (mode) => mode !== "turn";
|
|
9446
9627
|
|
|
9447
9628
|
// src/voice/instinct.ts
|
|
9448
|
-
var
|
|
9629
|
+
var log21 = forComponent("Instinct");
|
|
9449
9630
|
function voiceDeciderOptions(env = process.env) {
|
|
9450
9631
|
if (env.TYPESAFE_AI_KEY) return { apiKey: env.TYPESAFE_AI_KEY };
|
|
9451
9632
|
if (env.GROQ_API_KEY) return { provider: "llm", model: "groq/openai/gpt-oss-120b", apiKey: env.GROQ_API_KEY };
|
|
@@ -9716,13 +9897,13 @@ var Instinct = class {
|
|
|
9716
9897
|
* unfinished partial is no turn to react to — the interjection itself is the reply. */
|
|
9717
9898
|
pickAck(ctx) {
|
|
9718
9899
|
if (this.interjecting) {
|
|
9719
|
-
|
|
9900
|
+
log21.debug(`ack: cut-in turn \u2192 silent (engine context "${ctx.text.slice(0, 40)}" is the previous final)`);
|
|
9720
9901
|
return Promise.resolve(null);
|
|
9721
9902
|
}
|
|
9722
9903
|
const text = ctx.text.trim();
|
|
9723
9904
|
const p = (async () => {
|
|
9724
9905
|
const intentP = ctx.barged ? this.bargeIntent(text).catch((e) => {
|
|
9725
|
-
|
|
9906
|
+
log21.warn(`barge intent for the ack failed \u2014 situational ack: ${e.message}`);
|
|
9726
9907
|
return null;
|
|
9727
9908
|
}) : null;
|
|
9728
9909
|
const d = await this.ask(
|
|
@@ -9744,7 +9925,7 @@ var Instinct = class {
|
|
|
9744
9925
|
settle = r;
|
|
9745
9926
|
});
|
|
9746
9927
|
const timer = setTimeout(() => {
|
|
9747
|
-
|
|
9928
|
+
log21.debug(`ack: no engine outcome in ${this.o.ackSettleMs}ms \u2014 not claimed`);
|
|
9748
9929
|
settle(null);
|
|
9749
9930
|
}, this.o.ackSettleMs);
|
|
9750
9931
|
void spoken.then(() => clearTimeout(timer));
|
|
@@ -9793,7 +9974,7 @@ var Instinct = class {
|
|
|
9793
9974
|
});
|
|
9794
9975
|
return { kind, heard, choice: answers.act.choice, probabilities: answers.act.probabilities, ms: Math.round(this.o.now() - t0) };
|
|
9795
9976
|
} catch (e) {
|
|
9796
|
-
|
|
9977
|
+
log21.warn(`${kind} decision failed \u2014 falling back to engine defaults: ${e.message}`);
|
|
9797
9978
|
if (rethrow) throw e;
|
|
9798
9979
|
return null;
|
|
9799
9980
|
}
|
|
@@ -9802,7 +9983,7 @@ var Instinct = class {
|
|
|
9802
9983
|
|
|
9803
9984
|
// src/voice/unsaid.ts
|
|
9804
9985
|
init_logging();
|
|
9805
|
-
var
|
|
9986
|
+
var log22 = forComponent("Unsaid");
|
|
9806
9987
|
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(" "));
|
|
9807
9988
|
var contentStems = (s) => (s.toLowerCase().match(/[a-z0-9']+/g) ?? []).filter((w) => w.length >= 4 && !STOP2.has(w)).map((w) => w.slice(0, 4));
|
|
9808
9989
|
var SAID_OVERLAP = 0.6;
|
|
@@ -9842,7 +10023,7 @@ async function unsaidClauses(pieces, spoken, decider, timeoutMs = 5e3) {
|
|
|
9842
10023
|
return p < CONVEYED_MIN;
|
|
9843
10024
|
}).map((x) => x.c);
|
|
9844
10025
|
} catch (e) {
|
|
9845
|
-
|
|
10026
|
+
log22.warn(`unsaid judge failed \u2014 only near-zero-overlap clauses count as unsaid: ${e.message}`);
|
|
9846
10027
|
return fallback();
|
|
9847
10028
|
} finally {
|
|
9848
10029
|
clearTimeout(timer);
|
|
@@ -9963,7 +10144,7 @@ var RESULT_CLAIM_RE = new RegExp(LEAD_TAGS + String.raw`(?:(?:all\s+)?done|finis
|
|
|
9963
10144
|
var DONE_CLAIM_RE = new RegExp(LEAD_TAGS + String.raw`(?:(?:all\s+)?done|finished|it['’]?s\s+(?:done|finished)|completed?)\b`, "i");
|
|
9964
10145
|
|
|
9965
10146
|
// src/voice/channelLang.ts
|
|
9966
|
-
var
|
|
10147
|
+
var log23 = forComponent("ChannelBanks");
|
|
9967
10148
|
var LINE_KINDS = ["backchannel", "filler", "bridge"];
|
|
9968
10149
|
var BANK_KEYS = [...LINE_KINDS, "phatic"];
|
|
9969
10150
|
var EN_PHATIC = [
|
|
@@ -10117,11 +10298,11 @@ function cleanLine(s, lang, maxWords = 6) {
|
|
|
10117
10298
|
const raw = s.trim().split("\n")[0];
|
|
10118
10299
|
const l = raw.replace(/^[-*•\d.)\s]+/, "").replace(/^["'“”«»]+|["'“”«»]+$/g, "").replace(new RegExp("^\\p{L}+:\\s*", "u"), "").trim();
|
|
10119
10300
|
if (!l || isLeakSuspect(raw) || isLeakSuspect(l)) {
|
|
10120
|
-
if (l)
|
|
10301
|
+
if (l) log23.debug(`line "${raw}" reads as meta \u2014 rejected`);
|
|
10121
10302
|
return "";
|
|
10122
10303
|
}
|
|
10123
10304
|
if (lang && !fitsLanguage(l, lang)) {
|
|
10124
|
-
|
|
10305
|
+
log23.debug(`line "${l}" is not ${lang} \u2014 rejected`);
|
|
10125
10306
|
return "";
|
|
10126
10307
|
}
|
|
10127
10308
|
return l.split(/\s+/).length > maxWords || l.length > 60 ? "" : l;
|
|
@@ -10135,7 +10316,7 @@ function validateBank(raw, lang, minPerKind = 3) {
|
|
|
10135
10316
|
const seen = /* @__PURE__ */ new Set();
|
|
10136
10317
|
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)));
|
|
10137
10318
|
if (out[k].length < minPerKind) {
|
|
10138
|
-
|
|
10319
|
+
log23.warn(`bank ${lang}: only ${out[k].length} valid ${k} lines \u2014 rejected`);
|
|
10139
10320
|
return null;
|
|
10140
10321
|
}
|
|
10141
10322
|
}
|
|
@@ -10211,11 +10392,11 @@ var ChannelBanks = class {
|
|
|
10211
10392
|
const f = this.file(lang);
|
|
10212
10393
|
let cached = null;
|
|
10213
10394
|
if (f) cached = await readFile(f, "utf8").then((s) => JSON.parse(s)).catch((e) => {
|
|
10214
|
-
if (e.code !== "ENOENT")
|
|
10395
|
+
if (e.code !== "ENOENT") log23.warn(`bank cache ${f} unreadable \u2014 regenerating: ${e.message}`);
|
|
10215
10396
|
return null;
|
|
10216
10397
|
});
|
|
10217
10398
|
if (cached && cached.v !== BANK_VERSION) {
|
|
10218
|
-
|
|
10399
|
+
log23.debug(`bank ${lang}: cache v${cached.v ?? 1} predates v${BANK_VERSION} \u2014 regenerating`);
|
|
10219
10400
|
cached = null;
|
|
10220
10401
|
}
|
|
10221
10402
|
const valid = cached && validateBank(cached.bank, lang, 0);
|
|
@@ -10223,7 +10404,7 @@ var ChannelBanks = class {
|
|
|
10223
10404
|
if (valid) {
|
|
10224
10405
|
for (const k of BANK_KEYS) if (!valid[k].length) valid[k] = [...this.o.fallback[lang]?.[k] ?? []];
|
|
10225
10406
|
this.banks.set(lang, valid);
|
|
10226
|
-
|
|
10407
|
+
log23.debug(`bank ${lang}: cache hit (${BANK_KEYS.map((k) => valid[k].length).join("/")})${complete ? "" : " \u2014 missing kinds, refreshing"}`);
|
|
10227
10408
|
}
|
|
10228
10409
|
if (complete && this.o.now() - cached.at < this.o.refreshMs) return;
|
|
10229
10410
|
await this.generate(lang, valid);
|
|
@@ -10242,11 +10423,11 @@ var ChannelBanks = class {
|
|
|
10242
10423
|
bank[k] = [...fresh[k], ...have?.[k] ?? []].filter((x) => !seen.has(norm3(x)) && seen.add(norm3(x))).slice(0, this.o.maxPerKind);
|
|
10243
10424
|
}
|
|
10244
10425
|
this.banks.set(lang, bank);
|
|
10245
|
-
|
|
10426
|
+
log23.debug(`bank ${lang}: ${have ? "refreshed" : "generated"} (${BANK_KEYS.map((k) => bank[k].length).join("/")}) in ${Math.round(this.o.now() - t0)}ms`);
|
|
10246
10427
|
const f = this.file(lang);
|
|
10247
|
-
if (f) await mkdir(this.o.cacheDir, { recursive: true }).then(() => writeFile(f, JSON.stringify({ at: this.o.now(), v: BANK_VERSION, bank }))).catch((e) =>
|
|
10428
|
+
if (f) await mkdir(this.o.cacheDir, { recursive: true }).then(() => writeFile(f, JSON.stringify({ at: this.o.now(), v: BANK_VERSION, bank }))).catch((e) => log23.warn(`bank cache write ${f} failed: ${e.message}`));
|
|
10248
10429
|
} catch (e) {
|
|
10249
|
-
|
|
10430
|
+
log23.warn(`bank ${lang}: generation failed \u2014 ${have ? "keeping the cached bank" : lang in this.o.fallback ? "fallback bank" : "silent"}: ${e.message}`);
|
|
10250
10431
|
} finally {
|
|
10251
10432
|
clearTimeout(t);
|
|
10252
10433
|
if (!this.banks.has(lang)) setTimeout(() => this.pending.delete(lang), this.o.retryMs).unref?.();
|
|
@@ -10439,7 +10620,7 @@ var TurnGate = class {
|
|
|
10439
10620
|
};
|
|
10440
10621
|
|
|
10441
10622
|
// src/duplex.ts
|
|
10442
|
-
var
|
|
10623
|
+
var log24 = forComponent("DuplexAgent");
|
|
10443
10624
|
function describeCall(call) {
|
|
10444
10625
|
const v = call.args && Object.values(call.args).find((x) => typeof x === "string" && x.trim());
|
|
10445
10626
|
const hint = v ? ` (${String(v).replace(/\s+/g, " ").trim().slice(0, 48)})` : "";
|
|
@@ -10774,7 +10955,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
10774
10955
|
const m = this.reflexBuf.match(RESERVED_EVENT_MARKER) ?? this.reflexBuf.match(RESERVED_EVENT_OPENER);
|
|
10775
10956
|
if (m) {
|
|
10776
10957
|
this.fabricationCut = true;
|
|
10777
|
-
|
|
10958
|
+
log24.warn(`reflex fabricated a [task \u2026] event in its spoken stream \u2014 cutting it (kept ${m.index} chars)`);
|
|
10778
10959
|
this.reflexBuf = this.reflexBuf.slice(0, Math.max(m.index, this.reflexForwarded));
|
|
10779
10960
|
this.pumpReflex(true);
|
|
10780
10961
|
return;
|
|
@@ -10916,7 +11097,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
10916
11097
|
this.leakHit = true;
|
|
10917
11098
|
this.leakCuts.push(leak.trim().slice(0, 40));
|
|
10918
11099
|
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";
|
|
10919
|
-
|
|
11100
|
+
log24.warn(`reflex ${why} ("${leak.trim().slice(0, 40)}\u2026") \u2014 cut before TTS`);
|
|
10920
11101
|
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" });
|
|
10921
11102
|
const next = this.stepMarks.find((k) => k > at);
|
|
10922
11103
|
if (next === void 0) {
|
|
@@ -10957,7 +11138,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
10957
11138
|
this.notify("diag", "leak_judge", { text: s.slice(0, 80), p: Math.round(p * 100) / 100, ms: Math.round(performance.now() - t0) });
|
|
10958
11139
|
return p >= LEAK_JUDGE_THRESHOLD;
|
|
10959
11140
|
}, (e) => {
|
|
10960
|
-
|
|
11141
|
+
log24.warn(`leak judge failed \u2014 speaking the sentence: ${e.message}`);
|
|
10961
11142
|
return false;
|
|
10962
11143
|
}).then((leak) => {
|
|
10963
11144
|
clearTimeout(timer);
|
|
@@ -11029,7 +11210,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
11029
11210
|
let i = t.length - 1;
|
|
11030
11211
|
while (i >= this.turnBase && !(t[i].role === "assistant" && typeof t[i].content === "string" && t[i].content.includes(key))) i--;
|
|
11031
11212
|
if (i < this.turnBase) {
|
|
11032
|
-
|
|
11213
|
+
log24.warn("analysis leak cut, but its text was not found in the transcript to scrub");
|
|
11033
11214
|
continue;
|
|
11034
11215
|
}
|
|
11035
11216
|
const m = t[i];
|
|
@@ -11086,7 +11267,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
11086
11267
|
const res = await this.sendWithContext(content, context, note);
|
|
11087
11268
|
const fb = this.options.reflexFallbackModel, model = this.voice.options.model;
|
|
11088
11269
|
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;
|
|
11089
|
-
|
|
11270
|
+
log24.warn(`reflex ${model} rate-limited \u2014 re-running the turn on ${fb}`);
|
|
11090
11271
|
this.notify("diag", "reflex_fallback", { from: model, to: fb });
|
|
11091
11272
|
this.voice.transcript.length = base;
|
|
11092
11273
|
this.voice.options.model = fb;
|
|
@@ -11135,7 +11316,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
11135
11316
|
try {
|
|
11136
11317
|
await this.sendWithContext(reminder, this.lastContext);
|
|
11137
11318
|
} catch (e) {
|
|
11138
|
-
|
|
11319
|
+
log24.warn(`follow-through nudge failed: ${e instanceof Error ? e.message : e}`);
|
|
11139
11320
|
} finally {
|
|
11140
11321
|
const i = this.voice.transcript.findIndex((m, k) => k >= base && m.role === "user" && m.content === reminder);
|
|
11141
11322
|
if (i >= 0) this.voice.transcript.splice(i, 1);
|
|
@@ -11196,7 +11377,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
11196
11377
|
return {
|
|
11197
11378
|
preToolUse: (call) => {
|
|
11198
11379
|
if (this.spec?.state === "pending" && !_DuplexAgent.SPEC_SAFE_TOOLS.has(call.name)) {
|
|
11199
|
-
|
|
11380
|
+
log24.verbose(`speculation aborted: reflex called ${call.name} on unconfirmed input`);
|
|
11200
11381
|
this.abortSpeculation();
|
|
11201
11382
|
return { block: true, reason: "Speculative turn aborted." };
|
|
11202
11383
|
}
|
|
@@ -11278,7 +11459,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
11278
11459
|
try {
|
|
11279
11460
|
await this.sendWithContext(reminder, this.lastContext);
|
|
11280
11461
|
} catch (e) {
|
|
11281
|
-
|
|
11462
|
+
log24.warn(`ack nudge failed: ${e instanceof Error ? e.message : e}`);
|
|
11282
11463
|
} finally {
|
|
11283
11464
|
this.nudging = false;
|
|
11284
11465
|
const i = this.voice.transcript.findIndex((m, k) => k >= base && m.role === "user" && m.content === reminder);
|
|
@@ -11334,7 +11515,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
11334
11515
|
const accepted = this.offerOpen && this.parkedRedeliver.length > 0 && this.parkedRedeliver.every((p) => p.task === this.offer.task) && (isOfferAcceptance(content) || isTrivialBarge(content));
|
|
11335
11516
|
const judge = accepted ? void 0 : this.options.bargeIntent;
|
|
11336
11517
|
return (accepted ? Promise.resolve("resume") : judge ? judge(content).catch((e) => {
|
|
11337
|
-
|
|
11518
|
+
log24.warn(`bargeIntent judge failed \u2014 using the allowlist: ${e.message}`);
|
|
11338
11519
|
return fallback();
|
|
11339
11520
|
}) : Promise.resolve(fallback())).then((intent) => {
|
|
11340
11521
|
this.notify("diag", "barge_intent", { intent, text: content.slice(0, 80) });
|
|
@@ -11537,7 +11718,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
11537
11718
|
try {
|
|
11538
11719
|
res = await this.sendWithContext(spec.text, this.lastContext);
|
|
11539
11720
|
} catch (e) {
|
|
11540
|
-
|
|
11721
|
+
log24.warn(`speculative turn failed: ${e instanceof Error ? e.message : e}`);
|
|
11541
11722
|
} finally {
|
|
11542
11723
|
this.voice.options.signal = prevSignal;
|
|
11543
11724
|
}
|
|
@@ -11551,7 +11732,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
11551
11732
|
if (d === "abort") {
|
|
11552
11733
|
if (this.voice.transcript.length > base) this.voice.transcript.length = base;
|
|
11553
11734
|
this.speculativeAbortedCalls++;
|
|
11554
|
-
|
|
11735
|
+
log24.verbose(`speculation aborted (${this.speculativeAbortedCalls} total): "${spec.text.slice(0, 50)}"`);
|
|
11555
11736
|
if (this.spec === spec) this.spec = void 0;
|
|
11556
11737
|
return res ?? empty;
|
|
11557
11738
|
}
|
|
@@ -11937,7 +12118,7 @@ Another agent just implemented the above. Independently check the CURRENT state
|
|
|
11937
12118
|
this.notify("task_verify", `task ${id}: verifying`, { id });
|
|
11938
12119
|
const cres = await new Agent(checkerOpts).run(checkBrief);
|
|
11939
12120
|
if (cres.finishReason !== "stop") {
|
|
11940
|
-
|
|
12121
|
+
log24.warn(`task ${id}: verify inconclusive (${cres.finishReason})`);
|
|
11941
12122
|
this.notify("task_verify", `task ${id}: verify inconclusive (${cres.finishReason})`, { id, finishReason: cres.finishReason });
|
|
11942
12123
|
}
|
|
11943
12124
|
const sum = (a = 0, b = 0) => a + b;
|
|
@@ -12073,7 +12254,7 @@ Another agent just implemented the above. Independently check the CURRENT state
|
|
|
12073
12254
|
rec.status = "done";
|
|
12074
12255
|
rec.result = res.text;
|
|
12075
12256
|
const incomplete = res.finishReason !== "stop" && res.finishReason !== "empty";
|
|
12076
|
-
|
|
12257
|
+
log24.verbose(`task ${id} done (${res.steps} steps${incomplete ? `, INCOMPLETE: ${res.finishReason}` : ""})`);
|
|
12077
12258
|
this.notify("task_done", `task ${id} (${rec.label}) completed`, {
|
|
12078
12259
|
id,
|
|
12079
12260
|
text: res.text,
|
|
@@ -12107,14 +12288,14 @@ Another agent just implemented the above. Independently check the CURRENT state
|
|
|
12107
12288
|
foldIfSuperseded(rec, note) {
|
|
12108
12289
|
if (rec.deliveryParked !== "moved_on") return false;
|
|
12109
12290
|
if (note.trim()) this.voice.transcript.push({ role: "assistant", content: note });
|
|
12110
|
-
|
|
12291
|
+
log24.verbose(`task ${rec.id} settled but superseded (${rec.status}) \u2014 folded into transcript, not voiced`);
|
|
12111
12292
|
return true;
|
|
12112
12293
|
}
|
|
12113
12294
|
failTask(rec, msg) {
|
|
12114
12295
|
this.dropAsk(rec.id);
|
|
12115
12296
|
rec.status = "error";
|
|
12116
12297
|
rec.result = msg;
|
|
12117
|
-
|
|
12298
|
+
log24.warn(`task ${rec.id} failed: ${msg}`);
|
|
12118
12299
|
this.notify("task_error", `task ${rec.id} (${rec.label}) failed: ${msg}`, { id: rec.id });
|
|
12119
12300
|
if (this.foldIfSuperseded(rec, `The "${rec.label}" task failed: ${msg}`)) return;
|
|
12120
12301
|
this.dropParked(rec);
|
|
@@ -12418,7 +12599,7 @@ Another agent just implemented the above. Independently check the CURRENT state
|
|
|
12418
12599
|
init_llm();
|
|
12419
12600
|
init_logging();
|
|
12420
12601
|
init_models();
|
|
12421
|
-
var
|
|
12602
|
+
var log25 = forComponent("ModelRouter");
|
|
12422
12603
|
var ModelRouterOptions = class {
|
|
12423
12604
|
classifier = jevClassifier();
|
|
12424
12605
|
/** Capability tiers → model id (any provider). basic/standard run the main session; premium is only ever an offload. */
|
|
@@ -12502,10 +12683,10 @@ var ModelRouter = class {
|
|
|
12502
12683
|
if (tier === "premium") d.offload = this.state.offload && (c.sameThread ?? 0) >= o.sameThreadAt ? "continue" : "fresh";
|
|
12503
12684
|
} catch (e) {
|
|
12504
12685
|
const msg = e instanceof Error ? e.message : String(e);
|
|
12505
|
-
|
|
12686
|
+
log25.warn(`classifier failed, falling back to standard: ${msg}`);
|
|
12506
12687
|
d = { tier: "standard", model: o.models.standard, probs: {}, fallback: msg, classifyMs: Math.round(performance.now() - t0) };
|
|
12507
12688
|
}
|
|
12508
|
-
|
|
12689
|
+
log25.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`);
|
|
12509
12690
|
return d;
|
|
12510
12691
|
}
|
|
12511
12692
|
/** Run one user turn on `agent` with routing. Returns a RunResult carrying `costUsd` + `routing`. */
|
|
@@ -12557,7 +12738,7 @@ ${text}`;
|
|
|
12557
12738
|
const answer = res.text || `(premium sub-agent finished with no reply; finishReason=${res.finishReason})`;
|
|
12558
12739
|
agent.transcript.push({ role: "user", content }, { role: "assistant", content: answer });
|
|
12559
12740
|
this.state.offload = { messages: child.transcript, mainLen: agent.transcript.length, question: text };
|
|
12560
|
-
|
|
12741
|
+
log25.info(`offload ${cont ? "continue" : "fresh"} done \xB7 ${res.finishReason} \xB7 ${res.steps} steps`);
|
|
12561
12742
|
const costUsd = o.costOf(main2, briefUsage) + o.costOf(o.models.premium, res.usage);
|
|
12562
12743
|
return { ...res, text: answer, messages: agent.transcript, usage: addUsage(briefUsage, res.usage), costUsd, routing: d };
|
|
12563
12744
|
}
|
|
@@ -12573,7 +12754,7 @@ The request:
|
|
|
12573
12754
|
${text}`);
|
|
12574
12755
|
return { text: r.text.trim() || text, usage: r.usage };
|
|
12575
12756
|
} catch (e) {
|
|
12576
|
-
|
|
12757
|
+
log25.warn(`brief failed, sending the raw request: ${e instanceof Error ? e.message : String(e)}`);
|
|
12577
12758
|
return { text };
|
|
12578
12759
|
} finally {
|
|
12579
12760
|
agent.transcript = snap;
|
|
@@ -12618,7 +12799,7 @@ async function jevAvailable(opts = {}) {
|
|
|
12618
12799
|
const { env } = await loadJev(opts);
|
|
12619
12800
|
return JEV_KEYS.some((k) => env[k]?.trim());
|
|
12620
12801
|
} catch (e) {
|
|
12621
|
-
|
|
12802
|
+
log25.debug(`jev unavailable: ${e instanceof Error ? e.message : String(e)}`);
|
|
12622
12803
|
return false;
|
|
12623
12804
|
}
|
|
12624
12805
|
}
|
|
@@ -12845,7 +13026,7 @@ init_models();
|
|
|
12845
13026
|
|
|
12846
13027
|
// src/voice/progress.ts
|
|
12847
13028
|
init_logging();
|
|
12848
|
-
var
|
|
13029
|
+
var log26 = forComponent("Progress");
|
|
12849
13030
|
var HE_RESULT_CLAIM = /(?:^|\s)(?:מצאתי|סיימתי|בוצע|הנה|אין)(?=\s|$|[.,!…])/u;
|
|
12850
13031
|
var TOPICS = [
|
|
12851
13032
|
[/toolsearch|^mcp__.*__(list_commands|call_info)$/, null],
|
|
@@ -12990,7 +13171,7 @@ var Progress = class {
|
|
|
12990
13171
|
}, this.o.generateBudgetMs);
|
|
12991
13172
|
});
|
|
12992
13173
|
const raw = await Promise.race([this.o.generate(tool, argsHint(args), lang, ac.signal).catch((e) => {
|
|
12993
|
-
|
|
13174
|
+
log26.warn(`progress line failed: ${e?.message ?? e}`);
|
|
12994
13175
|
return null;
|
|
12995
13176
|
}), late]).finally(() => clearTimeout(t));
|
|
12996
13177
|
const l = cleanLine(raw ?? "", lang, 5);
|
|
@@ -13012,7 +13193,7 @@ init_logging();
|
|
|
13012
13193
|
|
|
13013
13194
|
// src/voice/backchannelPlan.ts
|
|
13014
13195
|
init_logging();
|
|
13015
|
-
var
|
|
13196
|
+
var log27 = forComponent("BackchannelPlan");
|
|
13016
13197
|
var TURN_TYPES = ["story", "list", "complaint", "explanation", "short_command", "question", "other"];
|
|
13017
13198
|
var DENSITIES = ["none", "low", "normal", "high"];
|
|
13018
13199
|
var silentTurn = (p) => p.density === "none" || p.turn === "short_command" || p.turn === "question";
|
|
@@ -13077,13 +13258,13 @@ Reply JSON only: {"turn":"...","density":"...","cues":[{"when":["phrase",...],"l
|
|
|
13077
13258
|
const reasoning = /gpt-oss/.test(o.model) ? { providerOptions: { reasoning_effort: "low" } } : {};
|
|
13078
13259
|
const r = await this.ai.chat({ model: o.model, messages: [{ role: "user", content: prompt }], stream: false, signal, maxTokens: o.maxTokens, ...reasoning });
|
|
13079
13260
|
const p = parsePlan(r.content ?? "", c.lang);
|
|
13080
|
-
if (!p)
|
|
13261
|
+
if (!p) log27.debug(`unusable plan: ${(r.content ?? "").slice(0, 120)}`);
|
|
13081
13262
|
return p;
|
|
13082
13263
|
}
|
|
13083
13264
|
};
|
|
13084
13265
|
|
|
13085
13266
|
// src/voice/secondChannel.ts
|
|
13086
|
-
var
|
|
13267
|
+
var log28 = forComponent("SecondChannel");
|
|
13087
13268
|
var CHANNEL_MODES = {
|
|
13088
13269
|
turn: { kinds: ["bridge"], bridgeDelayMs: 1500 },
|
|
13089
13270
|
normal: {},
|
|
@@ -13205,15 +13386,15 @@ function withBudget(p, ms, what, ac) {
|
|
|
13205
13386
|
let t;
|
|
13206
13387
|
const late = new Promise((r) => {
|
|
13207
13388
|
t = setTimeout(() => {
|
|
13208
|
-
|
|
13389
|
+
log28.debug(`${what}: over the ${ms}ms budget \u2014 dropped`);
|
|
13209
13390
|
ac?.abort();
|
|
13210
13391
|
r(null);
|
|
13211
13392
|
}, ms);
|
|
13212
13393
|
});
|
|
13213
13394
|
return Promise.race([p.catch((e) => {
|
|
13214
13395
|
const msg = String(e?.message ?? e);
|
|
13215
|
-
if (e?.name === "AbortError" || /aborted/i.test(msg))
|
|
13216
|
-
else
|
|
13396
|
+
if (e?.name === "AbortError" || /aborted/i.test(msg)) log28.debug(`${what}: aborted after its budget`);
|
|
13397
|
+
else log28.warn(`${what} failed \u2014 falling back: ${msg}`);
|
|
13217
13398
|
return null;
|
|
13218
13399
|
}), late]).finally(() => clearTimeout(t));
|
|
13219
13400
|
}
|
|
@@ -13292,7 +13473,7 @@ var SecondChannel = class {
|
|
|
13292
13473
|
if (host) this.pinned = true;
|
|
13293
13474
|
const lang = primaryLang(tag);
|
|
13294
13475
|
if (!lang || lang === this.lang) return;
|
|
13295
|
-
|
|
13476
|
+
log28.debug(`language ${this.lang} \u2192 ${lang}`);
|
|
13296
13477
|
this.lang = lang;
|
|
13297
13478
|
this.recent = [];
|
|
13298
13479
|
this.picks.clear();
|
|
@@ -13437,7 +13618,7 @@ var SecondChannel = class {
|
|
|
13437
13618
|
try {
|
|
13438
13619
|
this.o.cancel();
|
|
13439
13620
|
} catch (e) {
|
|
13440
|
-
|
|
13621
|
+
log28.warn(`cancel threw: ${e.message}`);
|
|
13441
13622
|
}
|
|
13442
13623
|
}
|
|
13443
13624
|
}
|
|
@@ -13549,7 +13730,7 @@ var SecondChannel = class {
|
|
|
13549
13730
|
const opts = this.options;
|
|
13550
13731
|
if (opts.mode === "chatty" && !opts.aec && !this.degradedLogged) {
|
|
13551
13732
|
this.degradedLogged = true;
|
|
13552
|
-
|
|
13733
|
+
log28.info("chatty mode without echo cancellation \u2014 normal timing (our lines would re-trigger the mic)");
|
|
13553
13734
|
}
|
|
13554
13735
|
const rule = this.rule(trigger, pauseMs, breath, cue);
|
|
13555
13736
|
if (rule === "silence") {
|
|
@@ -13568,7 +13749,7 @@ var SecondChannel = class {
|
|
|
13568
13749
|
const d = await withBudget(this.o.decide(this.tail(), { ...this.state(pauseMs), rule }, ac.signal), this.o.decisionBudgetMs, "channel decision", ac);
|
|
13569
13750
|
choice = d?.choice;
|
|
13570
13751
|
if (choice && choice in CHANNEL_ACTIONS && (choice === "silence" || this.allowed(choice))) kind = choice;
|
|
13571
|
-
else if (choice && choice in CHANNEL_ACTIONS)
|
|
13752
|
+
else if (choice && choice in CHANNEL_ACTIONS) log28.debug(`decider chose ${choice} for a ${rule} moment \u2014 the rule stands`);
|
|
13572
13753
|
}
|
|
13573
13754
|
if (kind === "silence") return ev({ choice, acted: "silence" });
|
|
13574
13755
|
if (stale()) return ev({ choice, kind, acted: "stale" });
|
|
@@ -13606,7 +13787,7 @@ var SecondChannel = class {
|
|
|
13606
13787
|
void withBudget(this.o.generate(ctx, ac.signal), Math.max(this.o.generateBudgetMs, this.o.bridgeDelayMs), "bridge generate", ac).then((raw) => {
|
|
13607
13788
|
const text = cleanLine(raw ?? "", ctx.lang);
|
|
13608
13789
|
if (text && gen === this.gen) this.bridgeDraft = { gen, text };
|
|
13609
|
-
}).catch((e) =>
|
|
13790
|
+
}).catch((e) => log28.debug(`bridge prefetch failed: ${e?.message ?? e}`));
|
|
13610
13791
|
}
|
|
13611
13792
|
async line(kind, react = true) {
|
|
13612
13793
|
if (kind === "bridge" && this.o.instantBridge) {
|
|
@@ -13622,7 +13803,7 @@ var SecondChannel = class {
|
|
|
13622
13803
|
if (raw === null) ac.abort();
|
|
13623
13804
|
const text = cleanLine(raw ?? "", this.lang);
|
|
13624
13805
|
if (text && !this.recent.some((r) => norm3(r) === norm3(text))) return text;
|
|
13625
|
-
if (text)
|
|
13806
|
+
if (text) log28.debug(`generated "${text}" repeats a recent line \u2014 canned`);
|
|
13626
13807
|
}
|
|
13627
13808
|
return this.pick(kind);
|
|
13628
13809
|
}
|
|
@@ -13644,7 +13825,7 @@ var SecondChannel = class {
|
|
|
13644
13825
|
try {
|
|
13645
13826
|
r = this.o.speak(text, kind);
|
|
13646
13827
|
} catch (e) {
|
|
13647
|
-
|
|
13828
|
+
log28.warn(`speak threw \u2014 line dropped: ${e.message}`);
|
|
13648
13829
|
return false;
|
|
13649
13830
|
}
|
|
13650
13831
|
if (r === false) return false;
|
|
@@ -13657,7 +13838,7 @@ var SecondChannel = class {
|
|
|
13657
13838
|
this.o.show(text, kind);
|
|
13658
13839
|
const p = Promise.resolve(r).then(() => {
|
|
13659
13840
|
}, (e) => {
|
|
13660
|
-
|
|
13841
|
+
log28.warn(`channel line playback failed: ${e?.message ?? e}`);
|
|
13661
13842
|
}).finally(() => {
|
|
13662
13843
|
if (this.inflight === p) this.inflight = null;
|
|
13663
13844
|
});
|
|
@@ -13715,7 +13896,7 @@ function closedQuestion(reply) {
|
|
|
13715
13896
|
}
|
|
13716
13897
|
|
|
13717
13898
|
// src/voice/call.ts
|
|
13718
|
-
var
|
|
13899
|
+
var log29 = forComponent("VoiceCall");
|
|
13719
13900
|
var VoiceCallOptions = class {
|
|
13720
13901
|
/** Host runs MAIN with exactly `text` (a replace: roll the `replaces` turn back first). */
|
|
13721
13902
|
sendTurn = () => {
|
|
@@ -13941,7 +14122,7 @@ var VoiceCall = class {
|
|
|
13941
14122
|
try {
|
|
13942
14123
|
this.o.cancel();
|
|
13943
14124
|
} catch (e) {
|
|
13944
|
-
|
|
14125
|
+
log29.warn(`cancel threw: ${e.message}`);
|
|
13945
14126
|
}
|
|
13946
14127
|
if (this.cur?.running) {
|
|
13947
14128
|
this.cur.running = false;
|
|
@@ -14028,7 +14209,7 @@ var VoiceCall = class {
|
|
|
14028
14209
|
try {
|
|
14029
14210
|
r = this.o.speak(text, kind);
|
|
14030
14211
|
} catch (e) {
|
|
14031
|
-
|
|
14212
|
+
log29.warn(`speak threw: ${e.message}`);
|
|
14032
14213
|
return false;
|
|
14033
14214
|
}
|
|
14034
14215
|
if (r === false) return false;
|
|
@@ -14114,7 +14295,7 @@ init_logging();
|
|
|
14114
14295
|
init_platform();
|
|
14115
14296
|
import { spawn } from "child_process";
|
|
14116
14297
|
import { createHash } from "crypto";
|
|
14117
|
-
var
|
|
14298
|
+
var log30 = forComponent("mcp");
|
|
14118
14299
|
var PROTOCOL_VERSION = "2025-06-18";
|
|
14119
14300
|
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
14120
14301
|
var DEFAULT_CALL_TIMEOUT_MS = 30 * 6e4;
|
|
@@ -14146,7 +14327,7 @@ var StdioTransport = class {
|
|
|
14146
14327
|
proc.stdout.setEncoding("utf8");
|
|
14147
14328
|
proc.stdout.on("data", (chunk) => this.onData(chunk));
|
|
14148
14329
|
proc.stderr.setEncoding("utf8");
|
|
14149
|
-
proc.stderr.on("data", (chunk) =>
|
|
14330
|
+
proc.stderr.on("data", (chunk) => log30.debug(`[${command}] stderr:`, chunk.trimEnd()));
|
|
14150
14331
|
proc.on("exit", (code) => this.failAll(new Error(`MCP server "${command}" exited (code ${code})`)));
|
|
14151
14332
|
proc.on("error", (e) => this.failAll(e instanceof Error ? e : new Error(String(e))));
|
|
14152
14333
|
}
|
|
@@ -14160,7 +14341,7 @@ var StdioTransport = class {
|
|
|
14160
14341
|
try {
|
|
14161
14342
|
this.dispatch(JSON.parse(line));
|
|
14162
14343
|
} catch (e) {
|
|
14163
|
-
|
|
14344
|
+
log30.debug("dropping non-JSON line from MCP server:", line, e);
|
|
14164
14345
|
}
|
|
14165
14346
|
}
|
|
14166
14347
|
}
|
|
@@ -14170,7 +14351,7 @@ var StdioTransport = class {
|
|
|
14170
14351
|
try {
|
|
14171
14352
|
p2?.onProgress?.(msg.params ?? {});
|
|
14172
14353
|
} catch (e) {
|
|
14173
|
-
|
|
14354
|
+
log30.debug("onProgress handler threw", e);
|
|
14174
14355
|
}
|
|
14175
14356
|
return;
|
|
14176
14357
|
}
|
|
@@ -14208,7 +14389,7 @@ var StdioTransport = class {
|
|
|
14208
14389
|
const onAbort = () => {
|
|
14209
14390
|
if (!this.pending.delete(id)) return;
|
|
14210
14391
|
clearTimeout(timer);
|
|
14211
|
-
this.notify("notifications/cancelled", { requestId: id, reason: String(signal?.reason?.message ?? "cancelled") }).catch((e) =>
|
|
14392
|
+
this.notify("notifications/cancelled", { requestId: id, reason: String(signal?.reason?.message ?? "cancelled") }).catch((e) => log30.debug("cancel notify failed", e));
|
|
14212
14393
|
reject(abortError(signal));
|
|
14213
14394
|
};
|
|
14214
14395
|
const done = () => signal?.removeEventListener("abort", onAbort);
|
|
@@ -14232,7 +14413,7 @@ var StdioTransport = class {
|
|
|
14232
14413
|
try {
|
|
14233
14414
|
this.proc?.stdin?.end();
|
|
14234
14415
|
} catch (e) {
|
|
14235
|
-
|
|
14416
|
+
log30.debug("stdin end failed", e);
|
|
14236
14417
|
}
|
|
14237
14418
|
if (!(IS_WIN && killTree(this.proc?.pid, "SIGTERM"))) this.proc?.kill();
|
|
14238
14419
|
}
|
|
@@ -14267,7 +14448,7 @@ var HttpTransport = class {
|
|
|
14267
14448
|
return await this.post({ jsonrpc: "2.0", id, method, params: opts.onProgress ? withProgressToken(params, id) : params }, false, signal, opts.onProgress);
|
|
14268
14449
|
} catch (e) {
|
|
14269
14450
|
if (opts.signal?.aborted) {
|
|
14270
|
-
void this.notify("notifications/cancelled", { requestId: id, reason: String(opts.signal.reason?.message ?? "cancelled") }).catch((err2) =>
|
|
14451
|
+
void this.notify("notifications/cancelled", { requestId: id, reason: String(opts.signal.reason?.message ?? "cancelled") }).catch((err2) => log30.debug("cancel notify failed", err2));
|
|
14271
14452
|
throw abortError(opts.signal);
|
|
14272
14453
|
}
|
|
14273
14454
|
throw e;
|
|
@@ -14310,7 +14491,7 @@ function sseMessage(line) {
|
|
|
14310
14491
|
try {
|
|
14311
14492
|
return JSON.parse(trimmed.slice(5).trim());
|
|
14312
14493
|
} catch (e) {
|
|
14313
|
-
|
|
14494
|
+
log30.debug("skipping unparseable SSE data line", e);
|
|
14314
14495
|
return void 0;
|
|
14315
14496
|
}
|
|
14316
14497
|
}
|
|
@@ -14320,7 +14501,7 @@ async function readSseResponse(resp, id, onProgress) {
|
|
|
14320
14501
|
try {
|
|
14321
14502
|
onProgress?.(obj.params ?? {});
|
|
14322
14503
|
} catch (e) {
|
|
14323
|
-
|
|
14504
|
+
log30.debug("onProgress handler threw", e);
|
|
14324
14505
|
}
|
|
14325
14506
|
return void 0;
|
|
14326
14507
|
}
|
|
@@ -14419,7 +14600,7 @@ async function mountWithDeadline(name, cfg, mountTimeoutMs) {
|
|
|
14419
14600
|
return { name, client, tools, specs, serverInfo: init?.serverInfo, config: cfg };
|
|
14420
14601
|
})(), mountTimeoutMs, name);
|
|
14421
14602
|
} catch (e) {
|
|
14422
|
-
await client.close().catch((err2) =>
|
|
14603
|
+
await client.close().catch((err2) => log30.debug(`close after failed mount of "${name}": ${err2}`));
|
|
14423
14604
|
throw e;
|
|
14424
14605
|
}
|
|
14425
14606
|
}
|
|
@@ -14430,15 +14611,15 @@ function validEntries(servers) {
|
|
|
14430
14611
|
return Object.entries(servers).filter(([name, cfg]) => {
|
|
14431
14612
|
if (!cfg || cfg.disabled) return false;
|
|
14432
14613
|
if (!cfg.command && !cfg.url) {
|
|
14433
|
-
|
|
14614
|
+
log30.warn(`MCP server "${name}" needs a command (stdio) or url (http) \u2014 skipping`);
|
|
14434
14615
|
return false;
|
|
14435
14616
|
}
|
|
14436
14617
|
return true;
|
|
14437
14618
|
});
|
|
14438
14619
|
}
|
|
14439
14620
|
function logMountFailure(name, e) {
|
|
14440
|
-
if (e instanceof McpAuthError)
|
|
14441
|
-
else
|
|
14621
|
+
if (e instanceof McpAuthError) log30.warn(`MCP "${name}" needs-auth: HTTP ${e.status} \u2014 set bearerToken or headers in its config; skipping`);
|
|
14622
|
+
else log30.error(`MCP server "${name}" failed to mount: ${e?.message ?? e}`);
|
|
14442
14623
|
}
|
|
14443
14624
|
async function mountMcpServers(servers = {}, opts = {}) {
|
|
14444
14625
|
const entries = validEntries(servers);
|
|
@@ -14448,7 +14629,7 @@ async function mountMcpServers(servers = {}, opts = {}) {
|
|
|
14448
14629
|
const name = entries[i][0];
|
|
14449
14630
|
if (r.status === "fulfilled") {
|
|
14450
14631
|
out.push(r.value);
|
|
14451
|
-
|
|
14632
|
+
log30.debug(`MCP "${name}" mounted \u2014 ${r.value.tools.length} tool(s)${r.value.serverInfo?.name ? ` from ${r.value.serverInfo.name}` : ""}`);
|
|
14452
14633
|
} else logMountFailure(name, r.reason);
|
|
14453
14634
|
});
|
|
14454
14635
|
return out;
|
|
@@ -14488,7 +14669,7 @@ var McpPool = class {
|
|
|
14488
14669
|
const prev = this.warm.get(key);
|
|
14489
14670
|
if (prev) {
|
|
14490
14671
|
clearTimeout(prev.timer);
|
|
14491
|
-
if (prev.client !== client) void prev.client.close().catch((err2) =>
|
|
14672
|
+
if (prev.client !== client) void prev.client.close().catch((err2) => log30.debug(`warm-pool replace close failed: ${err2}`));
|
|
14492
14673
|
}
|
|
14493
14674
|
const e = { client, timer: void 0 };
|
|
14494
14675
|
this.warm.set(key, e);
|
|
@@ -14505,7 +14686,7 @@ var McpPool = class {
|
|
|
14505
14686
|
const e = this.warm.get(key);
|
|
14506
14687
|
if (!e) return;
|
|
14507
14688
|
this.warm.delete(key);
|
|
14508
|
-
await e.client.close().catch((err2) =>
|
|
14689
|
+
await e.client.close().catch((err2) => log30.debug(`warm-pool evict close failed: ${err2}`));
|
|
14509
14690
|
}
|
|
14510
14691
|
async closeAll() {
|
|
14511
14692
|
for (const e of this.warm.values()) {
|
|
@@ -14724,7 +14905,7 @@ init_tools_shell();
|
|
|
14724
14905
|
// src/tools.notify.ts
|
|
14725
14906
|
init_logging();
|
|
14726
14907
|
import { execFile as execFile2 } from "child_process";
|
|
14727
|
-
var
|
|
14908
|
+
var log32 = forComponent("notify");
|
|
14728
14909
|
var WIN_TOAST = [
|
|
14729
14910
|
"[Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] | Out-Null",
|
|
14730
14911
|
"$t = [Windows.UI.Notifications.ToastNotificationManager]::GetTemplateContent([Windows.UI.Notifications.ToastTemplateType]::ToastText02)",
|
|
@@ -14755,7 +14936,7 @@ function makeNotifyTool(opts = {}) {
|
|
|
14755
14936
|
return new Promise((resolve6) => {
|
|
14756
14937
|
run(argv[0], argv[1], { timeout: 5e3, ...extra }, (e) => {
|
|
14757
14938
|
if (e) {
|
|
14758
|
-
|
|
14939
|
+
log32.debug("notification failed", e);
|
|
14759
14940
|
resolve6(`Notification failed: ${e.message}`);
|
|
14760
14941
|
} else resolve6("Notification shown.");
|
|
14761
14942
|
});
|
|
@@ -14771,7 +14952,7 @@ import { BodDB as BodDB2 } from "@bod.ee/db";
|
|
|
14771
14952
|
init_logging();
|
|
14772
14953
|
import { existsSync as existsSync2, readFileSync as readFileSync5 } from "fs";
|
|
14773
14954
|
import { homedir } from "os";
|
|
14774
|
-
var
|
|
14955
|
+
var log33 = forComponent("cli-util");
|
|
14775
14956
|
function dotDirs(base, sub, opts = {}) {
|
|
14776
14957
|
const home = opts.home ?? homedir();
|
|
14777
14958
|
const dirs = [`${base}/.agent/${sub}`, `${base}/.claude/${sub}`, `${home}/.agent/${sub}`, `${home}/.claude/${sub}`];
|
|
@@ -14788,7 +14969,7 @@ function parseJson(text, fallback, what = "json") {
|
|
|
14788
14969
|
try {
|
|
14789
14970
|
return JSON.parse(text);
|
|
14790
14971
|
} catch (e) {
|
|
14791
|
-
|
|
14972
|
+
log33.debug(`parseJson(${what}) failed: ${e.message}`);
|
|
14792
14973
|
return fallback;
|
|
14793
14974
|
}
|
|
14794
14975
|
}
|
|
@@ -14798,7 +14979,7 @@ function readJsonFile(path, fallback) {
|
|
|
14798
14979
|
try {
|
|
14799
14980
|
text = readFileSync5(path, "utf8");
|
|
14800
14981
|
} catch (e) {
|
|
14801
|
-
|
|
14982
|
+
log33.debug(`readJsonFile(${path}) unreadable: ${e.message}`);
|
|
14802
14983
|
return fallback;
|
|
14803
14984
|
}
|
|
14804
14985
|
return parseJson(text, fallback, path);
|
|
@@ -15102,7 +15283,7 @@ import { existsSync as existsSync4, mkdirSync as mkdirSync5, readFileSync as rea
|
|
|
15102
15283
|
import { homedir as homedir3 } from "os";
|
|
15103
15284
|
import { dirname as dirname4, join as join8 } from "path";
|
|
15104
15285
|
import { fileURLToPath } from "url";
|
|
15105
|
-
var
|
|
15286
|
+
var log34 = forComponent("VoiceIO");
|
|
15106
15287
|
var now3 = () => performance.now();
|
|
15107
15288
|
var Player = class {
|
|
15108
15289
|
proc = null;
|
|
@@ -15116,7 +15297,7 @@ var Player = class {
|
|
|
15116
15297
|
["-loglevel", "quiet", "-nodisp", "-fflags", "nobuffer", "-flags", "low_delay", "-probesize", "32", "-f", "s16le", "-ar", String(TTS_SAMPLE_RATE), "-ch_layout", "mono", "-i", "-"],
|
|
15117
15298
|
{ stdio: ["pipe", "ignore", "ignore"] }
|
|
15118
15299
|
);
|
|
15119
|
-
this.proc.on("error", (e) =>
|
|
15300
|
+
this.proc.on("error", (e) => log34.warn(`ffplay error: ${e.message}`));
|
|
15120
15301
|
this.proc.stdin.on("error", () => {
|
|
15121
15302
|
});
|
|
15122
15303
|
this.bytesWritten = 0;
|
|
@@ -15154,7 +15335,7 @@ function detectFfmpegMic() {
|
|
|
15154
15335
|
const devices = [...audio.matchAll(/\[(\d+)\] (.+)/g)].map(([, idx, name]) => ({ idx, name: name.trim() }));
|
|
15155
15336
|
const mic = devices.find((d) => /microphone|built-in/i.test(d.name) && !/teams|blackhole|loopback/i.test(d.name)) ?? devices[0];
|
|
15156
15337
|
if (!mic) throw new Error("no audio input device found");
|
|
15157
|
-
|
|
15338
|
+
log34.debug(`ffmpeg mic: [${mic.idx}] ${mic.name}`);
|
|
15158
15339
|
return `:${mic.idx}`;
|
|
15159
15340
|
}
|
|
15160
15341
|
function detectDshowMic() {
|
|
@@ -15165,7 +15346,7 @@ function detectDshowMic() {
|
|
|
15165
15346
|
const devices = [...(r.stderr ?? "").matchAll(/"([^"]+)"\s*\(audio\)/g)].map((m) => m[1]);
|
|
15166
15347
|
const mic = devices.find((d) => !/stereo mix|loopback|virtual|cable|teams/i.test(d)) ?? devices[0];
|
|
15167
15348
|
if (!mic) throw new Error("no audio input device found (set MIC_DEVICE to the DirectShow device name)");
|
|
15168
|
-
|
|
15349
|
+
log34.debug(`ffmpeg dshow mic: ${mic}`);
|
|
15169
15350
|
return `audio=${mic}`;
|
|
15170
15351
|
}
|
|
15171
15352
|
function detectedInputDevice() {
|
|
@@ -15201,15 +15382,15 @@ function resolveAecBinary() {
|
|
|
15201
15382
|
if (existsSync4(bin) && statSync6(bin).mtimeMs >= statSync6(src).mtimeMs) return bin;
|
|
15202
15383
|
if (spawnSync3("which", ["swiftc"]).status !== 0) return null;
|
|
15203
15384
|
mkdirSync5(cacheDir, { recursive: true });
|
|
15204
|
-
|
|
15385
|
+
log34.info("compiling AEC mic helper (first run)\u2026");
|
|
15205
15386
|
const build = spawnSync3("swiftc", ["-O", "-o", bin, src, "-Xlinker", "-sectcreate", "-Xlinker", "__TEXT", "-Xlinker", "__info_plist", "-Xlinker", plist], { encoding: "utf8" });
|
|
15206
15387
|
if (build.status !== 0) {
|
|
15207
|
-
|
|
15388
|
+
log34.warn(`AEC build failed: ${build.stderr?.slice(0, 400)}`);
|
|
15208
15389
|
return null;
|
|
15209
15390
|
}
|
|
15210
15391
|
const sign = spawnSync3("codesign", ["-fs", "-", bin], { encoding: "utf8" });
|
|
15211
15392
|
if (sign.status !== 0) {
|
|
15212
|
-
|
|
15393
|
+
log34.warn(`codesign failed: ${sign.stderr?.slice(0, 200)}`);
|
|
15213
15394
|
return null;
|
|
15214
15395
|
}
|
|
15215
15396
|
return bin;
|
|
@@ -15251,17 +15432,17 @@ var NodeMicSource = class {
|
|
|
15251
15432
|
this.proc = spawn2(this.bin, [], { stdio: ["ignore", "pipe", "ignore"] });
|
|
15252
15433
|
} else {
|
|
15253
15434
|
if (!hasCommand("ffmpeg")) throw new Error("voice I/O unavailable: no AEC helper and no ffmpeg on PATH");
|
|
15254
|
-
|
|
15435
|
+
log34.info("mic: raw capture (no AEC) \u2014 echo handled heuristically; headphones recommended");
|
|
15255
15436
|
const input = process.platform === "win32" ? ["-f", "dshow", "-i", detectDshowMic()] : ["-f", "avfoundation", "-i", detectFfmpegMic()];
|
|
15256
15437
|
this.proc = spawn2(
|
|
15257
15438
|
"ffmpeg",
|
|
15258
15439
|
["-loglevel", "error", ...input, "-ar", String(STT_SAMPLE_RATE), "-ac", "1", "-f", "s16le", "-"],
|
|
15259
15440
|
{ stdio: ["ignore", "pipe", "pipe"], ...process.platform === "win32" ? { windowsHide: true } : {} }
|
|
15260
15441
|
);
|
|
15261
|
-
this.proc.stderr.on("data", (d) =>
|
|
15442
|
+
this.proc.stderr.on("data", (d) => log34.warn(`ffmpeg: ${String(d).trim()}`));
|
|
15262
15443
|
}
|
|
15263
15444
|
this.proc.on("exit", (c) => {
|
|
15264
|
-
if (c && !this.stopped)
|
|
15445
|
+
if (c && !this.stopped) log34.error(`mic capture exited (${c}) \u2014 check mic permission / MIC_DEVICE / MIC_AEC=0`);
|
|
15265
15446
|
});
|
|
15266
15447
|
this.proc.stdout.on("data", (chunk) => onChunk(chunk));
|
|
15267
15448
|
}
|
|
@@ -15327,7 +15508,7 @@ var AecDuplexAudio = class {
|
|
|
15327
15508
|
this.proc.stdin.on("error", () => {
|
|
15328
15509
|
});
|
|
15329
15510
|
this.proc.on("exit", (c) => {
|
|
15330
|
-
if (c && !this.stopped)
|
|
15511
|
+
if (c && !this.stopped) log34.error(`aec duplex audio exited (${c}) \u2014 check mic permission / MIC_AEC=0`);
|
|
15331
15512
|
});
|
|
15332
15513
|
this.proc.stdout.on("data", (chunk) => {
|
|
15333
15514
|
this.gotChunk = true;
|
|
@@ -15343,7 +15524,7 @@ var AecDuplexAudio = class {
|
|
|
15343
15524
|
openMicSettings();
|
|
15344
15525
|
this.onFatal?.("microphone permission denied \u2014 enable it in System Settings \u2192 Privacy & Security \u2192 Microphone for your terminal, then restart it");
|
|
15345
15526
|
}
|
|
15346
|
-
} else
|
|
15527
|
+
} else log34.debug(`mic-aec: ${s}`);
|
|
15347
15528
|
}
|
|
15348
15529
|
});
|
|
15349
15530
|
if (!this.noVpio && !this.triedFallback) {
|
|
@@ -15352,7 +15533,7 @@ var AecDuplexAudio = class {
|
|
|
15352
15533
|
this.triedFallback = true;
|
|
15353
15534
|
this.noVpio = true;
|
|
15354
15535
|
this._aec = false;
|
|
15355
|
-
|
|
15536
|
+
log34.warn("mic-aec: VPIO delivered no audio in 2.5s \u2014 falling back to non-VPIO capture (no AEC \u2192 half-duplex, no barge-in)");
|
|
15356
15537
|
this.onDegrade?.();
|
|
15357
15538
|
this.killProc();
|
|
15358
15539
|
this.spawnHelper();
|
|
@@ -15523,7 +15704,7 @@ function bindAckInstinct(v, instinct) {
|
|
|
15523
15704
|
if (ev.kind === "ack_fired") instinct.ackOutcome(String(ev.phrase));
|
|
15524
15705
|
else if (ev.kind === "ack_skipped" || ev.kind === "ack_cancelled") instinct.ackOutcome(null);
|
|
15525
15706
|
} catch (e) {
|
|
15526
|
-
|
|
15707
|
+
log34.warn(`ack outcome tap failed: ${e instanceof Error ? e.message : e}`);
|
|
15527
15708
|
}
|
|
15528
15709
|
onDiag(ev);
|
|
15529
15710
|
};
|
|
@@ -15645,7 +15826,7 @@ function speakBlip(v, text) {
|
|
|
15645
15826
|
v.fireBackchannel();
|
|
15646
15827
|
return true;
|
|
15647
15828
|
} catch (e) {
|
|
15648
|
-
|
|
15829
|
+
log34.warn(`backchannel blip failed \u2014 line dropped: ${e.message}`);
|
|
15649
15830
|
return false;
|
|
15650
15831
|
} finally {
|
|
15651
15832
|
E.BACKCHANNELS = pool;
|
|
@@ -15664,7 +15845,7 @@ function channelInput(sc, m) {
|
|
|
15664
15845
|
}
|
|
15665
15846
|
if (m.type === "agent_state") {
|
|
15666
15847
|
if (typeof m.state === "string" && MAIN_STATES.has(m.state)) sc.onAgentState(m.state);
|
|
15667
|
-
else
|
|
15848
|
+
else log34.warn(`agent_state: unknown state ${JSON.stringify(m.state)} \u2014 ignored`);
|
|
15668
15849
|
return true;
|
|
15669
15850
|
}
|
|
15670
15851
|
return false;
|
|
@@ -15790,7 +15971,7 @@ function fakeVoiceParts(uttFile) {
|
|
|
15790
15971
|
init_logging();
|
|
15791
15972
|
import { appendFile, mkdirSync as mkdirSync6 } from "fs";
|
|
15792
15973
|
import { dirname as dirname5 } from "path";
|
|
15793
|
-
var
|
|
15974
|
+
var log35 = forComponent("VoiceDiag");
|
|
15794
15975
|
var JsonlDiagSink = class {
|
|
15795
15976
|
constructor(path) {
|
|
15796
15977
|
this.path = path;
|
|
@@ -15831,7 +16012,7 @@ var JsonlDiagSink = class {
|
|
|
15831
16012
|
disable(e) {
|
|
15832
16013
|
this.disabled = true;
|
|
15833
16014
|
this.queue.length = 0;
|
|
15834
|
-
|
|
16015
|
+
log35.debug(`voice diag sink disabled (${this.path}): ${e instanceof Error ? e.message : e}`);
|
|
15835
16016
|
}
|
|
15836
16017
|
/** True once a failure permanently disabled the sink (tests/inspection). */
|
|
15837
16018
|
get isDisabled() {
|
|
@@ -15989,7 +16170,7 @@ function applyOverlay(cfg, file) {
|
|
|
15989
16170
|
// cli/hooks-config.ts
|
|
15990
16171
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
15991
16172
|
init_platform();
|
|
15992
|
-
var
|
|
16173
|
+
var log36 = forComponent("hooks");
|
|
15993
16174
|
var escapeRegex = (s) => s.replace(/[.+^${}()|[\]\\]/g, "\\$&");
|
|
15994
16175
|
function ruleMatches(rule, toolName) {
|
|
15995
16176
|
if (!rule.tool || rule.tool === "*") return true;
|
|
@@ -16008,7 +16189,7 @@ function runCmd(rule, env) {
|
|
|
16008
16189
|
});
|
|
16009
16190
|
return { code: r.status ?? 1, out: ((r.stdout ?? "") + (r.stderr ?? "")).trim() };
|
|
16010
16191
|
} catch (e) {
|
|
16011
|
-
|
|
16192
|
+
log36.debug(`hook command failed: ${rule.command}`, e);
|
|
16012
16193
|
return { code: 1, out: String(e?.message ?? e) };
|
|
16013
16194
|
}
|
|
16014
16195
|
}
|
|
@@ -16116,7 +16297,7 @@ import { existsSync as existsSync6, mkdirSync as mkdirSync8, readFileSync as rea
|
|
|
16116
16297
|
import { homedir as homedir6 } from "os";
|
|
16117
16298
|
import { join as join11 } from "path";
|
|
16118
16299
|
init_platform();
|
|
16119
|
-
var
|
|
16300
|
+
var log37 = forComponent("session");
|
|
16120
16301
|
var globalDir = () => join11(homedir6(), ".agent", "sessions");
|
|
16121
16302
|
function indexLink(target, link2) {
|
|
16122
16303
|
try {
|
|
@@ -16179,7 +16360,7 @@ var SessionStore = class {
|
|
|
16179
16360
|
}
|
|
16180
16361
|
load(id) {
|
|
16181
16362
|
if (!this.safeId(id)) {
|
|
16182
|
-
|
|
16363
|
+
log37.debug(`rejecting unsafe session id: ${id}`);
|
|
16183
16364
|
return void 0;
|
|
16184
16365
|
}
|
|
16185
16366
|
const path = join11(this.dir, `${id}.json`);
|
|
@@ -16187,7 +16368,7 @@ var SessionStore = class {
|
|
|
16187
16368
|
try {
|
|
16188
16369
|
return JSON.parse(readFileSync8(path, "utf8"));
|
|
16189
16370
|
} catch (e) {
|
|
16190
|
-
|
|
16371
|
+
log37.debug(`unreadable session ${id} \u2014 ignoring`, e);
|
|
16191
16372
|
return void 0;
|
|
16192
16373
|
}
|
|
16193
16374
|
}
|
|
@@ -16200,7 +16381,7 @@ var SessionStore = class {
|
|
|
16200
16381
|
try {
|
|
16201
16382
|
metas.push(JSON.parse(readFileSync8(join11(this.dir, f), "utf8")).meta);
|
|
16202
16383
|
} catch (e) {
|
|
16203
|
-
|
|
16384
|
+
log37.debug(`skipping unreadable session file ${f}`, e);
|
|
16204
16385
|
}
|
|
16205
16386
|
}
|
|
16206
16387
|
return metas.sort((a, b) => b.updated - a.updated);
|
|
@@ -16336,6 +16517,199 @@ ${body || "(no conversation)"}
|
|
|
16336
16517
|
(That is everything recorded. Where it does not say why something was done or what was meant, say you do not know \u2014 never supply a reason.)`;
|
|
16337
16518
|
}
|
|
16338
16519
|
|
|
16520
|
+
// src/directives.ts
|
|
16521
|
+
init_models();
|
|
16522
|
+
var VENDOR_SHORTHAND = { cc: "claude-code", cur: "cursor", oa: "openai" };
|
|
16523
|
+
var DIRECTIVE_KEYS = ["model", "turns", "thinking", "think", "effort", "engine", "resume", "delegate", "advisor", "mode"];
|
|
16524
|
+
function splitModePair(token) {
|
|
16525
|
+
const i = token.search(/->|<-/);
|
|
16526
|
+
if (i === -1) return null;
|
|
16527
|
+
const right = token.slice(i + 2).trim();
|
|
16528
|
+
return right ? { left: token.slice(0, i).trim(), op: token.slice(i, i + 2), right } : null;
|
|
16529
|
+
}
|
|
16530
|
+
var isModel = (t, p) => {
|
|
16531
|
+
const h = resolveModelToken(t, p);
|
|
16532
|
+
return !!h && "model" in h;
|
|
16533
|
+
};
|
|
16534
|
+
var defaultResolver = null;
|
|
16535
|
+
var opt = (o) => ({
|
|
16536
|
+
resolveModel: defaultResolver,
|
|
16537
|
+
resolverFirst: false,
|
|
16538
|
+
aliases: MODEL_ALIASES,
|
|
16539
|
+
vendors: VENDOR_SHORTHAND,
|
|
16540
|
+
keys: DIRECTIVE_KEYS,
|
|
16541
|
+
strict: false,
|
|
16542
|
+
catalogOnly: false,
|
|
16543
|
+
warn: (m) => console.warn(m),
|
|
16544
|
+
...o
|
|
16545
|
+
});
|
|
16546
|
+
var DIRECTIVE_RE = /^\s*\[([^\]]*)\](?=\s|$)\s*([\s\S]*)/;
|
|
16547
|
+
var isQualifiedModel = (v) => /^[a-z0-9._-]+\/[a-z0-9./_-]+$/.test(v);
|
|
16548
|
+
function expandVendor(token, vendors = VENDOR_SHORTHAND) {
|
|
16549
|
+
const v = token.trim().toLowerCase();
|
|
16550
|
+
const m = v.match(/^([a-z0-9]+)[-/](.+)$/);
|
|
16551
|
+
if (m && vendors[m[1]]) return `${vendors[m[1]]}/${m[2]}`;
|
|
16552
|
+
const slash = v.indexOf("/");
|
|
16553
|
+
return slash > 0 && Object.values(vendors).includes(v.slice(0, slash)) ? v : null;
|
|
16554
|
+
}
|
|
16555
|
+
function resolveModelToken(token, o) {
|
|
16556
|
+
const p = opt(o);
|
|
16557
|
+
const v = token.trim().toLowerCase();
|
|
16558
|
+
const vendored = expandVendor(v, p.vendors);
|
|
16559
|
+
if (vendored) return p.resolveModel ? p.resolveModel(vendored) : p.catalogOnly ? null : { model: vendored };
|
|
16560
|
+
if (p.catalogOnly) return v.includes("/") || p.aliases[v] || /\d/.test(v) ? p.resolveModel?.(v) ?? null : null;
|
|
16561
|
+
const alias = p.aliases[v] ? { model: p.aliases[v] } : null;
|
|
16562
|
+
if (!p.resolverFirst) return alias ?? p.resolveModel?.(v) ?? null;
|
|
16563
|
+
const hit = p.resolveModel?.(v) ?? null;
|
|
16564
|
+
return hit && "model" in hit ? hit : alias ?? hit;
|
|
16565
|
+
}
|
|
16566
|
+
var THINK = ["think", "adaptive", "nothink", "nothinking"];
|
|
16567
|
+
function isDirectiveGroup(raw, o) {
|
|
16568
|
+
const p = opt(o);
|
|
16569
|
+
const tokens = raw.split(",").map((t) => t.trim()).filter(Boolean);
|
|
16570
|
+
if (!tokens.length) return false;
|
|
16571
|
+
return tokens.every((t) => {
|
|
16572
|
+
const i = t.indexOf(":");
|
|
16573
|
+
if (i !== -1) return p.keys.includes(t.slice(0, i).trim().toLowerCase());
|
|
16574
|
+
const v = t.toLowerCase();
|
|
16575
|
+
const pair = splitModePair(v);
|
|
16576
|
+
if (pair) return p.keys.includes(pair.op === "->" ? "delegate" : "advisor") && (!!pair.left && isModel(pair.left, p) || isModel(pair.right, p));
|
|
16577
|
+
if (v === "off") return p.keys.includes("mode");
|
|
16578
|
+
return !!resolveModelToken(v, p) || p.catalogOnly && !!p.aliases[v] || p.keys.includes("turns") && /^\d+$/.test(v) || p.keys.includes("thinking") && THINK.includes(v);
|
|
16579
|
+
});
|
|
16580
|
+
}
|
|
16581
|
+
function splitDirectiveGroups(text, o) {
|
|
16582
|
+
const p = opt(o);
|
|
16583
|
+
let rest = text;
|
|
16584
|
+
const groups = [];
|
|
16585
|
+
for (; ; ) {
|
|
16586
|
+
const m = rest.match(DIRECTIVE_RE);
|
|
16587
|
+
if (!m) break;
|
|
16588
|
+
const g = m[1].trim();
|
|
16589
|
+
if ((groups.length || p.strict) && g && !isDirectiveGroup(g, p)) break;
|
|
16590
|
+
groups.push(g);
|
|
16591
|
+
rest = m[2];
|
|
16592
|
+
}
|
|
16593
|
+
return { groups, body: rest };
|
|
16594
|
+
}
|
|
16595
|
+
function parseDirectives(text, o) {
|
|
16596
|
+
const p = opt(o);
|
|
16597
|
+
const { groups, body } = splitDirectiveGroups(text, p);
|
|
16598
|
+
if (!groups.length) return { prompt: text, directives: {} };
|
|
16599
|
+
const prompt = body.trim();
|
|
16600
|
+
const raw = groups.filter(Boolean).join(",");
|
|
16601
|
+
if (!raw) return { prompt, directives: {} };
|
|
16602
|
+
const d = {};
|
|
16603
|
+
const out = { prompt, directives: d };
|
|
16604
|
+
const tokens = raw.split(",");
|
|
16605
|
+
const proven = tokens.some((t) => {
|
|
16606
|
+
const i = t.indexOf(":");
|
|
16607
|
+
return i !== -1 && p.keys.includes(t.slice(0, i).trim().toLowerCase());
|
|
16608
|
+
});
|
|
16609
|
+
const fail = (tok, hit) => {
|
|
16610
|
+
out.unresolvedModel ??= tok;
|
|
16611
|
+
if (hit && "ambiguous" in hit) out.ambiguous ??= hit.ambiguous;
|
|
16612
|
+
};
|
|
16613
|
+
const setModel = (val) => {
|
|
16614
|
+
const hit = resolveModelToken(val, p);
|
|
16615
|
+
if (!hit || !("model" in hit)) return hit;
|
|
16616
|
+
d.model = hit.model;
|
|
16617
|
+
if (hit.engine && !d.engine) d.engine = hit.engine;
|
|
16618
|
+
return true;
|
|
16619
|
+
};
|
|
16620
|
+
let pos = 0;
|
|
16621
|
+
for (const token of tokens) {
|
|
16622
|
+
const t = token.trim();
|
|
16623
|
+
if (!t) continue;
|
|
16624
|
+
const i = t.indexOf(":");
|
|
16625
|
+
if (i !== -1) {
|
|
16626
|
+
const key = t.slice(0, i).trim().toLowerCase(), val2 = t.slice(i + 1).trim().toLowerCase();
|
|
16627
|
+
if (!p.keys.includes(key)) {
|
|
16628
|
+
p.warn(`[directives] Unknown directive key: "${key}"`);
|
|
16629
|
+
continue;
|
|
16630
|
+
}
|
|
16631
|
+
if (key === "delegate" || key === "advisor") {
|
|
16632
|
+
const hit = resolveModelToken(val2, p);
|
|
16633
|
+
if (hit && "model" in hit) d[key] = hit.model;
|
|
16634
|
+
else if (!hit && isQualifiedModel(val2) && !p.catalogOnly) d[key] = val2;
|
|
16635
|
+
else fail(val2, hit);
|
|
16636
|
+
continue;
|
|
16637
|
+
}
|
|
16638
|
+
if (key === "model") {
|
|
16639
|
+
const r2 = setModel(val2);
|
|
16640
|
+
if (r2 === true) continue;
|
|
16641
|
+
if (!r2 && isQualifiedModel(val2) && !p.catalogOnly) {
|
|
16642
|
+
d.model = val2;
|
|
16643
|
+
continue;
|
|
16644
|
+
}
|
|
16645
|
+
fail(val2, r2);
|
|
16646
|
+
} else applyKey(d, key, val2, p.warn);
|
|
16647
|
+
continue;
|
|
16648
|
+
}
|
|
16649
|
+
const val = t.toLowerCase();
|
|
16650
|
+
const pair = splitModePair(val);
|
|
16651
|
+
if (pair && p.keys.includes(pair.op === "->" ? "delegate" : "advisor")) {
|
|
16652
|
+
const lhit = pair.left ? resolveModelToken(pair.left, p) : null, rhit = resolveModelToken(pair.right, p);
|
|
16653
|
+
const lok = !pair.left || !!lhit && "model" in lhit, rok = !!rhit && "model" in rhit;
|
|
16654
|
+
if (lok && rok) {
|
|
16655
|
+
if (pair.left) setModel(pair.left);
|
|
16656
|
+
if (pair.op === "->") d.delegate = rhit.model;
|
|
16657
|
+
else d.advisor = rhit.model;
|
|
16658
|
+
} else if (lok || rok) {
|
|
16659
|
+
if (!lok) fail(pair.left, lhit);
|
|
16660
|
+
else fail(pair.right, rhit);
|
|
16661
|
+
} else p.warn(`[directives] Unknown models in "${t}"`);
|
|
16662
|
+
pos++;
|
|
16663
|
+
continue;
|
|
16664
|
+
}
|
|
16665
|
+
if (val === "off" && p.keys.includes("mode")) {
|
|
16666
|
+
d.modeOff = true;
|
|
16667
|
+
pos++;
|
|
16668
|
+
continue;
|
|
16669
|
+
}
|
|
16670
|
+
const r = pos === 0 ? setModel(val) : null;
|
|
16671
|
+
if (r === true) {
|
|
16672
|
+
} else if (pos <= 1 && /^\d+$/.test(val) && p.keys.includes("turns")) d.turns = parseInt(val, 10);
|
|
16673
|
+
else if (["think", "adaptive"].includes(val) && p.keys.includes("thinking")) d.thinking = "adaptive";
|
|
16674
|
+
else if (["nothink", "nothinking"].includes(val) && p.keys.includes("thinking")) d.thinking = "disabled";
|
|
16675
|
+
else if (pos === 0) {
|
|
16676
|
+
if (r || p.catalogOnly && p.aliases[val] || proven && !isQualifiedModel(val)) fail(t, r);
|
|
16677
|
+
p.warn(`[directives] Unknown model: "${t}"`);
|
|
16678
|
+
}
|
|
16679
|
+
pos++;
|
|
16680
|
+
}
|
|
16681
|
+
return out;
|
|
16682
|
+
}
|
|
16683
|
+
function applyKey(d, key, val, warn) {
|
|
16684
|
+
switch (key) {
|
|
16685
|
+
case "turns":
|
|
16686
|
+
if (/^\d+$/.test(val)) d.turns = parseInt(val, 10);
|
|
16687
|
+
else warn(`[directives] Invalid turns value: "${val}"`);
|
|
16688
|
+
break;
|
|
16689
|
+
case "thinking":
|
|
16690
|
+
case "think":
|
|
16691
|
+
if (["adaptive", "enabled", "disabled"].includes(val)) d.thinking = val;
|
|
16692
|
+
else warn(`[directives] Invalid thinking value: "${val}"`);
|
|
16693
|
+
break;
|
|
16694
|
+
case "effort":
|
|
16695
|
+
if (["low", "medium", "high", "max"].includes(val)) d.effort = val;
|
|
16696
|
+
else warn(`[directives] Invalid effort value: "${val}"`);
|
|
16697
|
+
break;
|
|
16698
|
+
case "engine":
|
|
16699
|
+
d.engine = val;
|
|
16700
|
+
break;
|
|
16701
|
+
case "mode":
|
|
16702
|
+
if (val === "off") d.modeOff = true;
|
|
16703
|
+
else warn(`[directives] Invalid mode value: "${val}"`);
|
|
16704
|
+
break;
|
|
16705
|
+
case "resume":
|
|
16706
|
+
if (["on", "true", "1"].includes(val)) d.resume = true;
|
|
16707
|
+
else if (["off", "false", "0"].includes(val)) d.resume = false;
|
|
16708
|
+
else warn(`[directives] Invalid resume value: "${val}"`);
|
|
16709
|
+
break;
|
|
16710
|
+
}
|
|
16711
|
+
}
|
|
16712
|
+
|
|
16339
16713
|
// cli/checkpoints.ts
|
|
16340
16714
|
var MUTATORS = /* @__PURE__ */ new Set(["Write", "Edit", "MultiEdit", "ApplyEdits"]);
|
|
16341
16715
|
function mutatedPaths(call) {
|
|
@@ -16439,7 +16813,7 @@ import { promisify } from "util";
|
|
|
16439
16813
|
import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync9 } from "fs";
|
|
16440
16814
|
import { homedir as homedir7 } from "os";
|
|
16441
16815
|
import { join as join12, resolve as resolve4, sep as sep3, parse as parsePath } from "path";
|
|
16442
|
-
var
|
|
16816
|
+
var log38 = forComponent("checkpoints");
|
|
16443
16817
|
var exec = promisify(execFile4);
|
|
16444
16818
|
var DEFAULT_EXCLUDE = [".agent/", ".git/", "node_modules/", "dist/", "build/", ".next/", "target/", ".venv/", "__pycache__/", "*.log"];
|
|
16445
16819
|
function isBroadRoot(p) {
|
|
@@ -16487,7 +16861,7 @@ var ShadowRepo = class {
|
|
|
16487
16861
|
writeFileSync6(join12(this.gitDir, "info", "exclude"), this.exclude.join("\n") + "\n");
|
|
16488
16862
|
this.ready = true;
|
|
16489
16863
|
} catch (e) {
|
|
16490
|
-
|
|
16864
|
+
log38.debug(`git checkpoints unavailable for ${this.workTree}`, e);
|
|
16491
16865
|
this.ready = false;
|
|
16492
16866
|
}
|
|
16493
16867
|
return this.ready;
|
|
@@ -16498,7 +16872,7 @@ var ShadowRepo = class {
|
|
|
16498
16872
|
}
|
|
16499
16873
|
async commit(label, forced = []) {
|
|
16500
16874
|
await this.run("add", "-A");
|
|
16501
|
-
for (const p of forced) await this.run("add", "-f", "--", p).catch((e) =>
|
|
16875
|
+
for (const p of forced) await this.run("add", "-f", "--", p).catch((e) => log38.debug(`force-add failed: ${p}`, e));
|
|
16502
16876
|
await this.run("commit", "--allow-empty", "-q", "-m", label);
|
|
16503
16877
|
}
|
|
16504
16878
|
/** Inject the CURRENT (pre-edit) content of `paths` into the turn-open restore point by amending it.
|
|
@@ -16506,8 +16880,8 @@ var ShadowRepo = class {
|
|
|
16506
16880
|
* turn-boundary `add -A` would never have captured it. Amend (vs a new commit) keeps one restore
|
|
16507
16881
|
* point per turn, so the REPL's turn↔frame mapping stays intact. */
|
|
16508
16882
|
async amendForced(paths) {
|
|
16509
|
-
for (const p of paths) await this.run("add", "-f", "--", p).catch((e) =>
|
|
16510
|
-
await this.run("commit", "--amend", "--no-edit", "-q", "--allow-empty").catch((e) =>
|
|
16883
|
+
for (const p of paths) await this.run("add", "-f", "--", p).catch((e) => log38.debug(`force-capture failed: ${p}`, e));
|
|
16884
|
+
await this.run("commit", "--amend", "--no-edit", "-q", "--allow-empty").catch((e) => log38.debug("amend failed", e));
|
|
16511
16885
|
}
|
|
16512
16886
|
/** Commits on `ref`, oldest-first (canonical index space). */
|
|
16513
16887
|
async log(ref) {
|
|
@@ -16567,7 +16941,7 @@ var ShadowRepo = class {
|
|
|
16567
16941
|
await this.run("gc", "--auto", "-q").catch(() => {
|
|
16568
16942
|
});
|
|
16569
16943
|
} catch (e) {
|
|
16570
|
-
|
|
16944
|
+
log38.debug("checkpoint prune failed", e);
|
|
16571
16945
|
}
|
|
16572
16946
|
}
|
|
16573
16947
|
};
|
|
@@ -16635,7 +17009,7 @@ var GitCheckpoints = class {
|
|
|
16635
17009
|
use(sessionId) {
|
|
16636
17010
|
if (sessionId === this.session) return;
|
|
16637
17011
|
this.session = sessionId;
|
|
16638
|
-
if (this.started) for (const r of this.repos) void r.point(this.ref()).catch((e) =>
|
|
17012
|
+
if (this.started) for (const r of this.repos) void r.point(this.ref()).catch((e) => log38.debug("re-point failed", e));
|
|
16639
17013
|
}
|
|
16640
17014
|
async begin(label) {
|
|
16641
17015
|
if (!await this.start()) return;
|
|
@@ -16655,7 +17029,7 @@ var GitCheckpoints = class {
|
|
|
16655
17029
|
process.stderr.write(`\x1B[2m checkpoints: '${this.repos[i].root}' too large to snapshot in ${Math.round(this.options.opTimeoutMs / 1e3)}s \u2014 file rewind disabled here\x1B[0m
|
|
16656
17030
|
`);
|
|
16657
17031
|
} else {
|
|
16658
|
-
|
|
17032
|
+
log38.debug("checkpoint commit failed", e);
|
|
16659
17033
|
survivors.push(this.repos[i]);
|
|
16660
17034
|
survivorCaches.push(this.caches[i] ?? []);
|
|
16661
17035
|
}
|
|
@@ -16690,7 +17064,7 @@ var GitCheckpoints = class {
|
|
|
16690
17064
|
if (this.forced.has(abs)) continue;
|
|
16691
17065
|
this.forced.add(abs);
|
|
16692
17066
|
const i = this.repoIndexFor(abs);
|
|
16693
|
-
if (i >= 0) await this.repos[i].amendForced([abs]).catch((e) =>
|
|
17067
|
+
if (i >= 0) await this.repos[i].amendForced([abs]).catch((e) => log38.debug("amendForced failed", e));
|
|
16694
17068
|
}
|
|
16695
17069
|
}
|
|
16696
17070
|
};
|
|
@@ -17801,7 +18175,7 @@ function applyVimNormal(s, key, str) {
|
|
|
17801
18175
|
}
|
|
17802
18176
|
return "none";
|
|
17803
18177
|
}
|
|
17804
|
-
function
|
|
18178
|
+
function applyKey2(s, key, str) {
|
|
17805
18179
|
const k = key?.name;
|
|
17806
18180
|
if (s.vim === "normal" && !s.pasting && !s.searching) {
|
|
17807
18181
|
const r = applyVimNormal(s, key ?? {}, str);
|
|
@@ -18129,7 +18503,7 @@ function createLineEditor(out, input = keyInput) {
|
|
|
18129
18503
|
});
|
|
18130
18504
|
return;
|
|
18131
18505
|
}
|
|
18132
|
-
const action =
|
|
18506
|
+
const action = applyKey2(s, key ?? {}, str);
|
|
18133
18507
|
if (action === "submit") {
|
|
18134
18508
|
finish();
|
|
18135
18509
|
opts.onSubmit?.();
|
|
@@ -18475,7 +18849,7 @@ import { spawnSync as spawnSync6 } from "child_process";
|
|
|
18475
18849
|
import { writeFileSync as writeFileSync8, mkdirSync as mkdirSync10, readdirSync as readdirSync2, unlinkSync as unlinkSync3, chmodSync, existsSync as existsSync7 } from "fs";
|
|
18476
18850
|
import { homedir as homedir8 } from "os";
|
|
18477
18851
|
import { join as join14 } from "path";
|
|
18478
|
-
var
|
|
18852
|
+
var log39 = forComponent("os-sched");
|
|
18479
18853
|
var OsScheduler = class {
|
|
18480
18854
|
options;
|
|
18481
18855
|
constructor(options) {
|
|
@@ -18537,7 +18911,7 @@ var OsScheduler = class {
|
|
|
18537
18911
|
}
|
|
18538
18912
|
}
|
|
18539
18913
|
} catch (e) {
|
|
18540
|
-
|
|
18914
|
+
log39.debug(`cancel ${id}`, e);
|
|
18541
18915
|
}
|
|
18542
18916
|
for (const f of [`${id}.json`, `${id}.sh`]) {
|
|
18543
18917
|
try {
|
|
@@ -18655,7 +19029,7 @@ import { existsSync as existsSync8, mkdirSync as mkdirSync11, unlinkSync as unli
|
|
|
18655
19029
|
import { homedir as homedir9 } from "os";
|
|
18656
19030
|
import { join as join15 } from "path";
|
|
18657
19031
|
init_platform();
|
|
18658
|
-
var
|
|
19032
|
+
var log40 = forComponent("remote-trigger");
|
|
18659
19033
|
var TRIGGER_DIR = () => join15(homedir9(), ".agent", "triggers");
|
|
18660
19034
|
var sockPath = (sessionId, dir = TRIGGER_DIR()) => join15(dir, `${sessionId}.sock`);
|
|
18661
19035
|
var TriggerServer = class {
|
|
@@ -18693,13 +19067,13 @@ var TriggerServer = class {
|
|
|
18693
19067
|
conn.end(JSON.stringify({ ok: false, error: String(e) }) + "\n");
|
|
18694
19068
|
}
|
|
18695
19069
|
});
|
|
18696
|
-
conn.on("error", (e) =>
|
|
19070
|
+
conn.on("error", (e) => log40.debug("trigger conn error", e));
|
|
18697
19071
|
});
|
|
18698
|
-
this.server.on("error", (e) =>
|
|
19072
|
+
this.server.on("error", (e) => log40.debug("trigger server error", e));
|
|
18699
19073
|
this.server.listen(p);
|
|
18700
19074
|
this.path = p;
|
|
18701
19075
|
} catch (e) {
|
|
18702
|
-
|
|
19076
|
+
log40.debug("trigger server unavailable", e);
|
|
18703
19077
|
}
|
|
18704
19078
|
}
|
|
18705
19079
|
/** Re-bind on /resume (the session id changed). */
|
|
@@ -18831,7 +19205,7 @@ var strike = C("9");
|
|
|
18831
19205
|
var link = (text, url) => useColor ? `\x1B]8;;${url}\x1B\\${cyan(text)}\x1B]8;;\x1B\\` : `${text} (${url})`;
|
|
18832
19206
|
var mdPalette = { bold, dim, cyan, italic, strike, green, link };
|
|
18833
19207
|
var err = (s) => process.stderr.write(s);
|
|
18834
|
-
var
|
|
19208
|
+
var log41 = forComponent("cli");
|
|
18835
19209
|
var VERSION = (() => {
|
|
18836
19210
|
try {
|
|
18837
19211
|
return JSON.parse(readFileSync10(new URL("../package.json", import.meta.url), "utf8")).version ?? "?";
|
|
@@ -18971,7 +19345,13 @@ function parseArgs(argv) {
|
|
|
18971
19345
|
a.harden = true;
|
|
18972
19346
|
a.hardenNet = true;
|
|
18973
19347
|
} else if (x === "--subagents") a.subagents = true;
|
|
18974
|
-
else if (x === "--
|
|
19348
|
+
else if (x === "--delegate") a.delegate = argv[++i];
|
|
19349
|
+
else if (x === "--advisor") a.advisor = argv[++i];
|
|
19350
|
+
else if (x === "--mode-scope") {
|
|
19351
|
+
const v = argv[++i];
|
|
19352
|
+
if (v !== "sticky" && v !== "message") throw new Error("--mode-scope must be sticky|message");
|
|
19353
|
+
a.modeScope = v;
|
|
19354
|
+
} else if (x === "--duplex") a.duplex = true;
|
|
18975
19355
|
else if (x === "--duplex-v2") {
|
|
18976
19356
|
a.duplex = true;
|
|
18977
19357
|
a.duplexV2 = true;
|
|
@@ -19105,6 +19485,9 @@ Flags:
|
|
|
19105
19485
|
--add-dir <path> mount another directory into the workspace (repeatable; disk mode only)
|
|
19106
19486
|
--mcp-config <file> JSON { mcpServers?, permissions? } merged over .agent config for this run only
|
|
19107
19487
|
--subagents allow the Task tool (spawn child agents)
|
|
19488
|
+
--delegate <worker> delegate mode: the main model plans (its writes are refused), <worker> writes via Task
|
|
19489
|
+
--advisor <model> advisor mode: the main model works; an Advise tool consults <model> (first write waits for it)
|
|
19490
|
+
--mode-scope <s> sticky (default): a [a->b] / [a<-b] prompt prefix holds for later turns (and -r resumes) until [off]; message: this turn only
|
|
19108
19491
|
--reasoning <e> extended thinking: off|low|medium|high or a token budget (anthropic/openai)
|
|
19109
19492
|
--session-id <id> use this id for the session (instead of an auto-generated one)
|
|
19110
19493
|
--fork-session with -r/-c: branch the resumed session into a new id (source left untouched)
|
|
@@ -19714,7 +20097,7 @@ function turnCost(model, usage) {
|
|
|
19714
20097
|
const priced = model.startsWith("claude-code/") ? `anthropic/${resolveModelAlias(model.slice("claude-code/".length))}` : model;
|
|
19715
20098
|
return costOf(getModelInfo(priced)?.pricing, usage?.promptTokens ?? 0, usage?.completionTokens ?? 0, usage?.cacheCreationTokens ?? 0, usage?.cacheReadTokens ?? 0, priced);
|
|
19716
20099
|
}
|
|
19717
|
-
async function evaluateGoal(ai, condition, transcript,
|
|
20100
|
+
async function evaluateGoal(ai, condition, transcript, log42) {
|
|
19718
20101
|
const recent = transcript.filter((m) => m.role === "assistant").slice(-8).map((m) => {
|
|
19719
20102
|
const text = typeof m.content === "string" ? m.content : m.content.filter((p) => p.type === "text").map((p) => p.text).join(" ");
|
|
19720
20103
|
return text.slice(0, 600);
|
|
@@ -19734,7 +20117,7 @@ ${recent}` }
|
|
|
19734
20117
|
const match = r.content.match(/\{[\s\S]*\}/);
|
|
19735
20118
|
if (match) return JSON.parse(match[0]);
|
|
19736
20119
|
} catch (e) {
|
|
19737
|
-
|
|
20120
|
+
log42(dim(` (goal evaluator error: ${e?.message ?? e})
|
|
19738
20121
|
`));
|
|
19739
20122
|
}
|
|
19740
20123
|
return { met: false, reason: "evaluation unclear" };
|
|
@@ -19961,7 +20344,7 @@ function mcpAgentTools(mounted, opts) {
|
|
|
19961
20344
|
return tools;
|
|
19962
20345
|
}
|
|
19963
20346
|
async function closeMcp(mounted) {
|
|
19964
|
-
await Promise.all(mounted.map((m) => m.client.close().catch((e) =>
|
|
20347
|
+
await Promise.all(mounted.map((m) => m.client.close().catch((e) => log41.debug("mcp close failed", e))));
|
|
19965
20348
|
}
|
|
19966
20349
|
var IMG_EXT = { ".png": "image/png", ".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".gif": "image/gif", ".webp": "image/webp" };
|
|
19967
20350
|
function mentionRefs(line) {
|
|
@@ -20020,7 +20403,7 @@ async function expandMentions(fs, line) {
|
|
|
20020
20403
|
if (loaded.includes(ref) || missing.includes(ref)) continue;
|
|
20021
20404
|
if (ref.includes(":") && mcpMentionResolver) {
|
|
20022
20405
|
const body = await mcpMentionResolver(ref).catch((e) => {
|
|
20023
|
-
|
|
20406
|
+
log41.debug("mcp mention resolve failed", e);
|
|
20024
20407
|
return null;
|
|
20025
20408
|
});
|
|
20026
20409
|
if (body != null) {
|
|
@@ -20045,6 +20428,33 @@ ${await fs.readFile(path)}`);
|
|
|
20045
20428
|
|
|
20046
20429
|
${blocks.join("\n\n")}` : line, loaded, missing };
|
|
20047
20430
|
}
|
|
20431
|
+
function applyTurnMode(agent, session, task, flags = {}, onModel) {
|
|
20432
|
+
const p = parseDirectives(task, { strict: true, keys: ["model", "turns", "delegate", "advisor", "mode"], warn: (m) => log41.debug(m) });
|
|
20433
|
+
const d = p.directives, isMode = !!(d.delegate || d.advisor || d.modeOff);
|
|
20434
|
+
if (p.unresolvedModel && /->|<-|delegate:|advisor:/.test(task.slice(0, task.length - p.prompt.length))) {
|
|
20435
|
+
err(red(` \u2717 unknown model "${p.unresolvedModel}"${p.ambiguous ? ` (could be: ${p.ambiguous.join(", ")})` : ""} \u2014 turn not sent
|
|
20436
|
+
`));
|
|
20437
|
+
return null;
|
|
20438
|
+
}
|
|
20439
|
+
const r = (m) => m ? resolveModelOrNewest(m) : void 0;
|
|
20440
|
+
const fromDirective = isMode ? d.modeOff ? {} : { model: r(d.model), delegate: r(d.delegate), advisor: r(d.advisor) } : void 0;
|
|
20441
|
+
if (fromDirective && flags.modeScope !== "message") session.meta.mode = fromDirective;
|
|
20442
|
+
const mode = fromDirective ?? session.meta.mode ?? { delegate: flags.delegate && r(flags.delegate), advisor: flags.advisor && r(flags.advisor) };
|
|
20443
|
+
const o = agent.options, before = JSON.stringify([o.model, o.delegate, o.advisor]);
|
|
20444
|
+
if (mode.model && mode.model !== o.model) {
|
|
20445
|
+
o.model = mode.model;
|
|
20446
|
+
o.contextWindow = getModelInfo(mode.model)?.contextWindow ?? 0;
|
|
20447
|
+
onModel?.(mode.model);
|
|
20448
|
+
}
|
|
20449
|
+
o.delegate = mode.delegate ? { worker: mode.delegate } : void 0;
|
|
20450
|
+
o.advisor = mode.advisor ? { ...o.advisor, model: mode.advisor } : void 0;
|
|
20451
|
+
if (JSON.stringify([o.model, o.delegate, o.advisor]) !== before) {
|
|
20452
|
+
agent.reprepare();
|
|
20453
|
+
err(dim(` \u21C4 ${o.delegate ? `delegate: ${o.model} plans \u2192 ${o.delegate.worker} writes` : o.advisor ? `advisor: ${o.model} works \u2190 ${o.advisor.model} advises` : "single-model mode"}
|
|
20454
|
+
`));
|
|
20455
|
+
}
|
|
20456
|
+
return isMode ? p.prompt : task;
|
|
20457
|
+
}
|
|
20048
20458
|
function jsonResult(res, session) {
|
|
20049
20459
|
const lastUser = res.messages.map((m) => m.role).lastIndexOf("user");
|
|
20050
20460
|
return {
|
|
@@ -20056,6 +20466,7 @@ function jsonResult(res, session) {
|
|
|
20056
20466
|
tools: res.messages.slice(lastUser).filter((m) => m.role === "tool").length,
|
|
20057
20467
|
usage: res.usage,
|
|
20058
20468
|
...res.budget ? { budget: res.budget } : {},
|
|
20469
|
+
...res.workerUsage ? { workerModel: res.workerModel, workerUsage: res.workerUsage } : {},
|
|
20059
20470
|
...res.routing ? { costUsd: res.costUsd, routing: res.routing } : {},
|
|
20060
20471
|
// cast: tsup dts hoists a modelRouter import above the shebang
|
|
20061
20472
|
sessionId: session.meta.id,
|
|
@@ -20111,7 +20522,7 @@ async function runTurn(agent, store, session, task, cp, cwd = process.cwd(), sen
|
|
|
20111
20522
|
try {
|
|
20112
20523
|
store.save(session);
|
|
20113
20524
|
} catch (ex) {
|
|
20114
|
-
|
|
20525
|
+
log41.debug("mid-turn session flush failed", ex);
|
|
20115
20526
|
}
|
|
20116
20527
|
}
|
|
20117
20528
|
return origNotify(e);
|
|
@@ -20140,7 +20551,7 @@ async function runTurn(agent, store, session, task, cp, cwd = process.cwd(), sen
|
|
|
20140
20551
|
agent.options.host?.flushText?.();
|
|
20141
20552
|
flushLookupGroup();
|
|
20142
20553
|
const secs2 = ((Date.now() - t0) / 1e3).toFixed(1);
|
|
20143
|
-
const cost = res.costUsd ?? turnCost(agent.options.model, res.usage);
|
|
20554
|
+
const cost = (res.costUsd ?? turnCost(agent.options.model, res.usage)) + (res.workerUsage && res.workerModel ? turnCost(res.workerModel, res.workerUsage) : 0);
|
|
20144
20555
|
const m = res.usageEstimated ? "~" : "";
|
|
20145
20556
|
const tok = res.usage?.totalTokens ? `${m}${(res.usage.totalTokens / 1e3).toFixed(1)}k tok \xB7 ${cost > 0 ? m + fmtUsd(cost) + " \xB7 " : ""}` : "";
|
|
20146
20557
|
agent.options.host?.takeToolCount?.();
|
|
@@ -20275,14 +20686,14 @@ var isCancelTeardown = (e) => {
|
|
|
20275
20686
|
function installCancelGuards(mounted) {
|
|
20276
20687
|
process.on("unhandledRejection", (e) => {
|
|
20277
20688
|
if (isCancelTeardown(e)) {
|
|
20278
|
-
|
|
20689
|
+
log41.debug("suppressed unhandledRejection (cursor stream cancel)", e);
|
|
20279
20690
|
return;
|
|
20280
20691
|
}
|
|
20281
|
-
|
|
20692
|
+
log41.error("unhandledRejection", e);
|
|
20282
20693
|
});
|
|
20283
20694
|
process.on("uncaughtException", (e) => {
|
|
20284
20695
|
if (isCancelTeardown(e)) {
|
|
20285
|
-
|
|
20696
|
+
log41.debug("suppressed uncaughtException (cursor stream cancel)", e);
|
|
20286
20697
|
return;
|
|
20287
20698
|
}
|
|
20288
20699
|
console.error(e);
|
|
@@ -20651,7 +21062,7 @@ async function repl(args, ai, cfg, cwd) {
|
|
|
20651
21062
|
editorRef?.abort();
|
|
20652
21063
|
}
|
|
20653
21064
|
} catch (e) {
|
|
20654
|
-
|
|
21065
|
+
log41.error("call turn failed", e);
|
|
20655
21066
|
} finally {
|
|
20656
21067
|
rec.running = false;
|
|
20657
21068
|
call?.main({ type: "done", id });
|
|
@@ -20783,7 +21194,7 @@ async function repl(args, ai, cfg, cwd) {
|
|
|
20783
21194
|
try {
|
|
20784
21195
|
mkdirSync12(dir, { recursive: true });
|
|
20785
21196
|
} catch (e) {
|
|
20786
|
-
|
|
21197
|
+
log41.debug("paste image dir create failed", e);
|
|
20787
21198
|
}
|
|
20788
21199
|
const img = grabClipboardImage(dir, String(Date.now()));
|
|
20789
21200
|
return img ? { display: "Image", ref: "@" + img.path, path: img.path } : null;
|
|
@@ -20896,7 +21307,7 @@ Added entries are loadable now via the Skill/SlashCommand tools; removed ones ar
|
|
|
20896
21307
|
mkdirSync12(join16(cwd, ".agent"), { recursive: true });
|
|
20897
21308
|
appendFileSync(histPath, line + "\n");
|
|
20898
21309
|
} catch (e) {
|
|
20899
|
-
|
|
21310
|
+
log41.debug("history write failed", e);
|
|
20900
21311
|
}
|
|
20901
21312
|
};
|
|
20902
21313
|
const ago = (t) => {
|
|
@@ -20968,7 +21379,7 @@ Added entries are loadable now via the Skill/SlashCommand tools; removed ones ar
|
|
|
20968
21379
|
try {
|
|
20969
21380
|
store.save(session);
|
|
20970
21381
|
} catch (e) {
|
|
20971
|
-
|
|
21382
|
+
log41.debug("session save after rewind failed", e);
|
|
20972
21383
|
}
|
|
20973
21384
|
err(green(" \u27F2 jumped back") + dim(` \u2014 ${face.transcript.length} message(s) kept; edit + resend
|
|
20974
21385
|
`));
|
|
@@ -21003,13 +21414,20 @@ ${task}`;
|
|
|
21003
21414
|
bangContext.length = 0;
|
|
21004
21415
|
}
|
|
21005
21416
|
const delta = await refreshCatalogs().catch((e) => {
|
|
21006
|
-
|
|
21417
|
+
log41.debug("catalog refresh failed", e);
|
|
21007
21418
|
return "";
|
|
21008
21419
|
});
|
|
21009
21420
|
if (delta) {
|
|
21010
21421
|
err(dim(" \u27F3 skill/command catalog changed \u2014 delta attached to this turn\n"));
|
|
21011
21422
|
task += "\n\n" + delta;
|
|
21012
21423
|
}
|
|
21424
|
+
if (!duplex) {
|
|
21425
|
+
const t = applyTurnMode(face, session, task, args, (m) => {
|
|
21426
|
+
face.options.providerOptions = providerOptionsFor(m, cwd, cfg.mcpServers, mounted.map((x) => x.name));
|
|
21427
|
+
});
|
|
21428
|
+
if (t === null) return { ok: false };
|
|
21429
|
+
task = t;
|
|
21430
|
+
}
|
|
21013
21431
|
const r = await runTurn(face, store, session, task, duplex ? void 0 : checkpoints, cwd, sendVia ? (c) => sendVia(c, opts) : routeVia);
|
|
21014
21432
|
if (voiceIO) {
|
|
21015
21433
|
voiceReasonEnd();
|
|
@@ -21266,7 +21684,7 @@ ${task}`;
|
|
|
21266
21684
|
desc: "rescan skills/commands dirs and rebuild the system prompt (one cache miss) \u2014 picks up entries created mid-session",
|
|
21267
21685
|
run: async () => {
|
|
21268
21686
|
await refreshCatalogs().catch((e) => {
|
|
21269
|
-
|
|
21687
|
+
log41.debug("catalog refresh failed", e);
|
|
21270
21688
|
});
|
|
21271
21689
|
face.reprepare();
|
|
21272
21690
|
err(green(` \u2713 reloaded \u2014 ${skills.length} skill(s), ${cmds.length} command(s); system prompt rebuilds on next message
|
|
@@ -21769,7 +22187,7 @@ ${task}`;
|
|
|
21769
22187
|
try {
|
|
21770
22188
|
for (const def of (await loadAgents(fs2, d)).agents) if (!seen.has(def.name)) seen.set(def.name, { def, from: d });
|
|
21771
22189
|
} catch (e) {
|
|
21772
|
-
|
|
22190
|
+
log41.debug(`loadAgents(${d}) failed`, e);
|
|
21773
22191
|
}
|
|
21774
22192
|
}
|
|
21775
22193
|
if (!seen.size) {
|
|
@@ -21857,7 +22275,7 @@ ${task}`;
|
|
|
21857
22275
|
}
|
|
21858
22276
|
if (idx >= 0) {
|
|
21859
22277
|
const old = mounted.splice(idx, 1)[0];
|
|
21860
|
-
await old.client.close().catch((e) =>
|
|
22278
|
+
await old.client.close().catch((e) => log41.debug("mcp close failed", e));
|
|
21861
22279
|
}
|
|
21862
22280
|
try {
|
|
21863
22281
|
const m = await mountMcpServer(name, conf);
|
|
@@ -21885,7 +22303,7 @@ ${task}`;
|
|
|
21885
22303
|
}
|
|
21886
22304
|
const m = mounted.splice(idx, 1)[0];
|
|
21887
22305
|
remountMcpTools();
|
|
21888
|
-
await m.client.close().catch((e) =>
|
|
22306
|
+
await m.client.close().catch((e) => log41.debug("mcp close failed", e));
|
|
21889
22307
|
err(dim(` removed "${name}"
|
|
21890
22308
|
`));
|
|
21891
22309
|
return;
|
|
@@ -22088,7 +22506,7 @@ ${task}`;
|
|
|
22088
22506
|
try {
|
|
22089
22507
|
return readdirSync4(join16(cwd, absDir.replace(/^\/+/, "")), { withFileTypes: true }).map((d) => ({ name: d.name, dir: d.isDirectory() }));
|
|
22090
22508
|
} catch (e) {
|
|
22091
|
-
|
|
22509
|
+
log41.debug("completion readdir failed", absDir, e);
|
|
22092
22510
|
return null;
|
|
22093
22511
|
}
|
|
22094
22512
|
};
|
|
@@ -22148,7 +22566,7 @@ ${task}`;
|
|
|
22148
22566
|
}
|
|
22149
22567
|
return;
|
|
22150
22568
|
}
|
|
22151
|
-
const action =
|
|
22569
|
+
const action = applyKey2(sEd, key ?? {}, _s);
|
|
22152
22570
|
if (action === "submit") {
|
|
22153
22571
|
const text = sEd.expand().trim();
|
|
22154
22572
|
sEd.reset();
|
|
@@ -22225,7 +22643,7 @@ ${out}
|
|
|
22225
22643
|
return;
|
|
22226
22644
|
}
|
|
22227
22645
|
await refreshCatalogs().catch((e) => {
|
|
22228
|
-
|
|
22646
|
+
log41.debug("catalog refresh failed", e);
|
|
22229
22647
|
});
|
|
22230
22648
|
const sk = skills.find((x) => x.name === name);
|
|
22231
22649
|
if (sk) {
|
|
@@ -22350,7 +22768,7 @@ ${out}
|
|
|
22350
22768
|
if (delivery || cut) dx?.parkCutDelivery(delivery, !!cut, heard);
|
|
22351
22769
|
const cutReply = cut && cut.full.length - cut.heard.length > 40 ? cut : null;
|
|
22352
22770
|
const noteP = replaces ? replaces.note : !cutReply ? Promise.resolve("") : (instinct ? instinct.bargeIntent(text).catch((e) => {
|
|
22353
|
-
|
|
22771
|
+
log41.warn(`barge intent failed \u2014 model decides: ${e.message}`);
|
|
22354
22772
|
return void 0;
|
|
22355
22773
|
}) : Promise.resolve(void 0)).then(async (intent) => interruptionNote(cutReply, intent) + ackedNote(await instinct?.ackFor(text)));
|
|
22356
22774
|
const cutIn = replaces ? replaces.cutIn : interjectNote;
|
|
@@ -22729,7 +23147,7 @@ function channelOptions(ai, model, gender) {
|
|
|
22729
23147
|
// Anticipated backchannels: on in chatty mode (its preset), else off until the offline replay proves them (tests/voice-bc-anticipate-eval.ts)
|
|
22730
23148
|
plan: (c, s) => planner.plan(c, s),
|
|
22731
23149
|
...process.env.AGENTX_CHANNEL_ANTICIPATE === "1" ? { anticipate: true } : {},
|
|
22732
|
-
onEvent: (e) =>
|
|
23150
|
+
onEvent: (e) => log41.debug(`channel ${e.trigger}: rule=${e.rule} choice=${e.choice ?? "-"} \u2192 ${e.acted}${e.text ? ` "${e.text}"` : ""}${e.cue ? " (planned cue)" : ""} ${e.ms}ms`)
|
|
22733
23151
|
};
|
|
22734
23152
|
}
|
|
22735
23153
|
function overlapResponder(ai, mode, out, aside) {
|
|
@@ -22800,7 +23218,7 @@ async function duplexServe(args, ai, cfg, cwd) {
|
|
|
22800
23218
|
try {
|
|
22801
23219
|
store.save(session);
|
|
22802
23220
|
} catch (e) {
|
|
22803
|
-
|
|
23221
|
+
log41.warn("duplex session save failed", e);
|
|
22804
23222
|
}
|
|
22805
23223
|
};
|
|
22806
23224
|
const overlap2 = overlapResponder(ai, args.duplexV2 ? args.channelMode : void 0, out);
|
|
@@ -22840,7 +23258,7 @@ async function duplexServe(args, ai, cfg, cwd) {
|
|
|
22840
23258
|
done2();
|
|
22841
23259
|
out({ type: "turn_done", id, finishReason: res?.finishReason ?? "error", ...why ? { error: why } : {} });
|
|
22842
23260
|
}).catch((e) => {
|
|
22843
|
-
|
|
23261
|
+
log41.error("duplex turn failed", e);
|
|
22844
23262
|
done2();
|
|
22845
23263
|
out({ type: "turn_done", id, finishReason: "error", error: String(e?.message ?? e) });
|
|
22846
23264
|
});
|
|
@@ -23095,7 +23513,12 @@ async function main() {
|
|
|
23095
23513
|
const session = startSession(args, store, agent, cwd);
|
|
23096
23514
|
process.once("SIGINT", () => activeTurn?.abort());
|
|
23097
23515
|
steers?.steer((text) => agent.inject(text));
|
|
23098
|
-
const turn =
|
|
23516
|
+
const turn = async (task) => {
|
|
23517
|
+
const t = applyTurnMode(agent, session, task, args, (m) => {
|
|
23518
|
+
agent.options.providerOptions = providerOptionsFor(m, cwd, cfg.mcpServers, mounted.map((x) => x.name));
|
|
23519
|
+
});
|
|
23520
|
+
return t === null ? { ok: false } : runTurn(agent, store, session, t, void 0, cwd, autoRouteSend(args, agent, () => session, (m) => providerOptionsFor(m, cwd, cfg.mcpServers, mounted.map((x) => x.name))));
|
|
23521
|
+
};
|
|
23099
23522
|
if (warmBoot && steers) {
|
|
23100
23523
|
bindClaudeCodeSession(agent, session);
|
|
23101
23524
|
await agent.prewarm().catch((e) => err(dim(` (prewarm failed: ${e?.message ?? e})
|
|
@@ -23157,6 +23580,7 @@ export {
|
|
|
23157
23580
|
abortActiveTurn,
|
|
23158
23581
|
apiKeysFromEnv,
|
|
23159
23582
|
appendMemoryNote,
|
|
23583
|
+
applyTurnMode,
|
|
23160
23584
|
cacheMultipliers,
|
|
23161
23585
|
condenseReplay,
|
|
23162
23586
|
costOf,
|