@livx.cc/agentx 0.99.72 → 0.99.74
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{Agent-CuXPAfSY.d.ts → Agent-Bs2ufrIU.d.ts} +9 -2
- package/dist/cli.d.ts +3 -6
- package/dist/cli.js +430 -295
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.js +295 -178
- package/dist/index.js.map +1 -1
- package/dist/{mcp-DGd2If0Z.d.ts → mcp-BhTxN2I7.d.ts} +1 -1
- package/dist/mcp.client.d.ts +2 -2
- package/dist/{tools-DV4SkkQs.d.ts → tools-lC3jn60o.d.ts} +5 -5
- package/dist/tools.shell.d.ts +1 -1
- package/dist/tools.shell.js +21 -16
- package/dist/tools.shell.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -18,8 +18,54 @@ var init_logging = __esm({
|
|
|
18
18
|
}
|
|
19
19
|
});
|
|
20
20
|
|
|
21
|
+
// src/imageWire.ts
|
|
22
|
+
import { readFileSync, statSync } from "fs";
|
|
23
|
+
import { imageWireOf, inlineImageForWire, scrubBase64Text, imageSize, imageNote, IMAGE_MAX_SIDE, IMAGE_MAX_BYTES } from "ai.libx.js";
|
|
24
|
+
function remember(id, w) {
|
|
25
|
+
cache.delete(id);
|
|
26
|
+
cache.set(id, w);
|
|
27
|
+
if (cache.size > CACHE_MAX) cache.delete(cache.keys().next().value);
|
|
28
|
+
return w;
|
|
29
|
+
}
|
|
30
|
+
function imageForWire(path, mime, vision) {
|
|
31
|
+
let st;
|
|
32
|
+
try {
|
|
33
|
+
st = statSync(path);
|
|
34
|
+
} catch (e) {
|
|
35
|
+
log2.warn(`image unreadable: ${path}`, e);
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
const key = `${st.mtimeMs}:${st.size}`;
|
|
39
|
+
let hit = cache.get(path);
|
|
40
|
+
if (!hit || hit.key !== key) hit = remember(path, { key, ...imageWireOf(readFileSync(path), mime, path, path) });
|
|
41
|
+
return vision && hit.url ? { url: hit.url } : { note: hit.note };
|
|
42
|
+
}
|
|
43
|
+
function imageTokens(dims) {
|
|
44
|
+
return dims ? Math.min(IMAGE_TOKENS_MAX, Math.max(1, Math.ceil(dims.w * dims.h / 750))) : IMAGE_TOKENS_MAX;
|
|
45
|
+
}
|
|
46
|
+
function wiredImageWeight(r) {
|
|
47
|
+
if ("note" in r) return r.note.length;
|
|
48
|
+
const b64 = r.url.slice(r.url.indexOf(",") + 1);
|
|
49
|
+
return 4 * imageTokens(imageSize(Buffer.from(b64.slice(0, 4096), "base64")));
|
|
50
|
+
}
|
|
51
|
+
function imageWireWeight(path, mime, vision) {
|
|
52
|
+
const r = imageForWire(path, mime, vision);
|
|
53
|
+
return r ? wiredImageWeight(r) : 0;
|
|
54
|
+
}
|
|
55
|
+
var log2, cache, CACHE_MAX, IMAGE_TOKENS_MAX;
|
|
56
|
+
var init_imageWire = __esm({
|
|
57
|
+
"src/imageWire.ts"() {
|
|
58
|
+
"use strict";
|
|
59
|
+
init_logging();
|
|
60
|
+
log2 = forComponent("imageWire");
|
|
61
|
+
cache = /* @__PURE__ */ new Map();
|
|
62
|
+
CACHE_MAX = 8;
|
|
63
|
+
IMAGE_TOKENS_MAX = 1600;
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
21
67
|
// src/llm.ts
|
|
22
|
-
import {
|
|
68
|
+
import { statSync as statSync2, realpathSync } from "fs";
|
|
23
69
|
import { resolve, sep } from "path";
|
|
24
70
|
import { randomBytes } from "crypto";
|
|
25
71
|
function passesBackReasoning(model) {
|
|
@@ -76,9 +122,9 @@ function mintImageRef(path, mime) {
|
|
|
76
122
|
const token = randomBytes(16).toString("hex");
|
|
77
123
|
let bytes = 0;
|
|
78
124
|
try {
|
|
79
|
-
bytes = Math.ceil(
|
|
125
|
+
bytes = Math.ceil(statSync2(path).size * 4 / 3);
|
|
80
126
|
} catch (e) {
|
|
81
|
-
|
|
127
|
+
log3.warn(`image ref stat failed, weighing as 0: ${path}`, e);
|
|
82
128
|
}
|
|
83
129
|
refRegistry.set(token, { mime, path, bytes });
|
|
84
130
|
while (refRegistry.size > REF_REGISTRY_MAX) refRegistry.delete(refRegistry.keys().next().value);
|
|
@@ -94,7 +140,7 @@ function parseImageRef(url) {
|
|
|
94
140
|
const parts = refUrlParts(url);
|
|
95
141
|
const ref = parts && refRegistry.get(parts.token);
|
|
96
142
|
if (!ref) {
|
|
97
|
-
|
|
143
|
+
log3.debug("image ref not in registry \u2014 treating as non-image text");
|
|
98
144
|
return null;
|
|
99
145
|
}
|
|
100
146
|
return ref;
|
|
@@ -113,7 +159,7 @@ function remintImageRefs(messages, roots) {
|
|
|
113
159
|
try {
|
|
114
160
|
allowed.push(realpathSync(resolve(r)));
|
|
115
161
|
} catch (e) {
|
|
116
|
-
|
|
162
|
+
log3.debug(`image-ref root unavailable: ${r}`, e);
|
|
117
163
|
}
|
|
118
164
|
}
|
|
119
165
|
if (allowed.length === 0) return messages;
|
|
@@ -130,11 +176,11 @@ function remintImageRefs(messages, roots) {
|
|
|
130
176
|
try {
|
|
131
177
|
real = realpathSync(f.path);
|
|
132
178
|
} catch (e) {
|
|
133
|
-
|
|
179
|
+
log3.warn(`resumed image ref unreadable, will stub: ${f.path}`, e);
|
|
134
180
|
return p;
|
|
135
181
|
}
|
|
136
182
|
if (!inside(real)) {
|
|
137
|
-
|
|
183
|
+
log3.warn(`resumed image ref outside the allowed roots, will stub: ${f.path} -> ${real}`);
|
|
138
184
|
return p;
|
|
139
185
|
}
|
|
140
186
|
touched = true;
|
|
@@ -161,7 +207,7 @@ function toolImageRef(content) {
|
|
|
161
207
|
try {
|
|
162
208
|
return parseImageRef(JSON.parse(content).imageRef);
|
|
163
209
|
} catch (e) {
|
|
164
|
-
|
|
210
|
+
log3.debug("bad imageRef tool result", e);
|
|
165
211
|
return null;
|
|
166
212
|
}
|
|
167
213
|
}
|
|
@@ -189,32 +235,41 @@ function imageWindows(messages, keepRecent) {
|
|
|
189
235
|
const k = Math.max(0, keepRecent);
|
|
190
236
|
return /* @__PURE__ */ new Set([...attach.slice(Math.max(0, attach.length - k)), ...tool.slice(Math.max(0, tool.length - k))]);
|
|
191
237
|
}
|
|
192
|
-
function expandImagesForSend(messages, keepRecent = 1, stub = IMAGE_ELIDE_STUB) {
|
|
238
|
+
function expandImagesForSend(messages, keepRecent = 1, stub = IMAGE_ELIDE_STUB, vision = true) {
|
|
193
239
|
if (!messages.some(bearsImage)) return messages;
|
|
194
240
|
const keep = imageWindows(messages, keepRecent);
|
|
195
241
|
let changed = false;
|
|
196
242
|
const out = messages.map((m, i) => {
|
|
197
|
-
const content = keep.has(i) ? expandContent(m.content,
|
|
243
|
+
const content = keep.has(i) ? expandContent(m.content, vision) : stripContent(m.content, stub);
|
|
198
244
|
if (content === m.content) return m;
|
|
199
245
|
changed = true;
|
|
200
246
|
return { ...m, content };
|
|
201
247
|
});
|
|
202
248
|
return changed ? out : messages;
|
|
203
249
|
}
|
|
204
|
-
function sendBytes(messages, keepRecent = 1, stub = IMAGE_ELIDE_STUB) {
|
|
250
|
+
function sendBytes(messages, keepRecent = 1, vision = true, stub = IMAGE_ELIDE_STUB) {
|
|
205
251
|
let n = 0;
|
|
206
|
-
for (const b of sendBytesPer(messages, keepRecent, stub)) n += b;
|
|
252
|
+
for (const b of sendBytesPer(messages, keepRecent, vision, stub)) n += b;
|
|
207
253
|
return n;
|
|
208
254
|
}
|
|
209
|
-
function sendBytesPer(messages, keepRecent = 1, stub = IMAGE_ELIDE_STUB) {
|
|
255
|
+
function sendBytesPer(messages, keepRecent = 1, vision = true, stub = IMAGE_ELIDE_STUB) {
|
|
210
256
|
const keep = messages.some(bearsImage) ? imageWindows(messages, keepRecent) : null;
|
|
211
|
-
return messages.map((m, i) => wireBytes(m.content, keep == null || keep.has(i), stub) + (m.tool_calls ? JSON.stringify(m.tool_calls).length : 0) + (m.reasoning_content?.length ?? 0));
|
|
257
|
+
return messages.map((m, i) => wireBytes(m.content, keep == null || keep.has(i), vision, stub) + (m.tool_calls ? JSON.stringify(m.tool_calls).length : 0) + (m.reasoning_content?.length ?? 0));
|
|
212
258
|
}
|
|
213
|
-
function wireBytes(content, kept, stub) {
|
|
259
|
+
function wireBytes(content, kept, vision, stub) {
|
|
214
260
|
const tool = toolImageRef(content);
|
|
215
|
-
if (tool) return kept ? tool.
|
|
261
|
+
if (tool) return kept ? imageWireWeight(tool.path, tool.mime, vision) || stub.length : stub.length;
|
|
216
262
|
if (content == null || typeof content === "string") return contentBytes(content);
|
|
217
|
-
if (kept)
|
|
263
|
+
if (kept) {
|
|
264
|
+
let n2 = 0;
|
|
265
|
+
for (const p of content) {
|
|
266
|
+
const url = p.type === "image_url" ? p.image_url?.url ?? "" : "";
|
|
267
|
+
const ref = url ? parseImageRef(url) : null;
|
|
268
|
+
const inline = url.startsWith("data:") ? inlineImageForWire(url, vision) : null;
|
|
269
|
+
n2 += ref ? imageWireWeight(ref.path, ref.mime, vision) : inline ? wiredImageWeight(inline) : contentBytes([p]);
|
|
270
|
+
}
|
|
271
|
+
return n2;
|
|
272
|
+
}
|
|
218
273
|
let n = 0;
|
|
219
274
|
for (const p of content) {
|
|
220
275
|
if (p.type === "text") {
|
|
@@ -226,31 +281,70 @@ function wireBytes(content, kept, stub) {
|
|
|
226
281
|
}
|
|
227
282
|
return n;
|
|
228
283
|
}
|
|
229
|
-
function
|
|
230
|
-
try {
|
|
231
|
-
return `data:${ref.mime};base64,${readFileSync(ref.path).toString("base64")}`;
|
|
232
|
-
} catch (e) {
|
|
233
|
-
log2.warn(`image ref unreadable, sending stub instead: ${ref.path}`, e);
|
|
234
|
-
return null;
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
function expandContent(content, stub) {
|
|
284
|
+
function expandContent(content, vision) {
|
|
238
285
|
const tool = toolImageRef(content);
|
|
239
286
|
if (tool) {
|
|
240
|
-
const
|
|
241
|
-
|
|
287
|
+
const w = imageForWire(tool.path, tool.mime, vision);
|
|
288
|
+
if (!w) return IMAGE_ELIDE_STUB;
|
|
289
|
+
return "url" in w ? [{ type: "text", text: `image: ${tool.path}` }, imagePart(w.url)] : w.note;
|
|
242
290
|
}
|
|
243
291
|
if (typeof content === "string") return content;
|
|
244
292
|
let changed = false;
|
|
245
293
|
const out = content.map((p) => {
|
|
246
|
-
if (p.type !== "image_url"
|
|
294
|
+
if (p.type !== "image_url") return p;
|
|
295
|
+
const url = p.image_url?.url ?? "";
|
|
296
|
+
if (url.startsWith("data:")) {
|
|
297
|
+
const w2 = inlineImageForWire(url, vision);
|
|
298
|
+
if ("url" in w2 && w2.url === url) return p;
|
|
299
|
+
changed = true;
|
|
300
|
+
return "url" in w2 ? imagePart(w2.url) : { type: "text", text: w2.note };
|
|
301
|
+
}
|
|
302
|
+
if (!isImageRefUrl(url)) return p;
|
|
247
303
|
changed = true;
|
|
248
|
-
const ref = parseImageRef(
|
|
249
|
-
|
|
250
|
-
|
|
304
|
+
const ref = parseImageRef(url);
|
|
305
|
+
if (!ref) return { type: "text", text: IMAGE_ELIDE_STUB };
|
|
306
|
+
const w = imageForWire(ref.path, ref.mime, vision);
|
|
307
|
+
if (!w) return { type: "text", text: IMAGE_ELIDE_STUB };
|
|
308
|
+
return "url" in w ? imagePart(w.url) : { type: "text", text: w.note };
|
|
251
309
|
});
|
|
252
310
|
return changed ? out : content;
|
|
253
311
|
}
|
|
312
|
+
function hoistToolImages(messages) {
|
|
313
|
+
if (!messages.some((m) => m.role === "tool" && Array.isArray(m.content) && m.content.some((p) => p.type === "image_url"))) return messages;
|
|
314
|
+
const out = [];
|
|
315
|
+
let pending = [];
|
|
316
|
+
const flush = () => {
|
|
317
|
+
if (pending.length) out.push({ role: "user", content: [{ type: "text", text: "Image output from the tool call above:" }, ...pending] });
|
|
318
|
+
pending = [];
|
|
319
|
+
};
|
|
320
|
+
for (const m of messages) {
|
|
321
|
+
if (m.role !== "tool") flush();
|
|
322
|
+
if (m.role === "tool" && Array.isArray(m.content) && m.content.some((p) => p.type === "image_url")) {
|
|
323
|
+
const imgs = m.content.filter((p) => p.type === "image_url");
|
|
324
|
+
pending.push(...imgs);
|
|
325
|
+
const text = m.content.filter((p) => p.type === "text").map((p) => p.text).join("\n");
|
|
326
|
+
out.push({ ...m, content: text || `[${imgs.length} image(s) returned; shown below]` });
|
|
327
|
+
} else out.push(m);
|
|
328
|
+
}
|
|
329
|
+
flush();
|
|
330
|
+
return out;
|
|
331
|
+
}
|
|
332
|
+
function scrubBase64(messages) {
|
|
333
|
+
let changed = false;
|
|
334
|
+
const out = messages.map((m) => {
|
|
335
|
+
const c = m.content;
|
|
336
|
+
let next = c;
|
|
337
|
+
if (typeof c === "string") next = scrubBase64Text(c);
|
|
338
|
+
else if (Array.isArray(c)) {
|
|
339
|
+
const parts = c.map((p) => p.type === "text" && p.text ? { ...p, text: scrubBase64Text(p.text) } : p);
|
|
340
|
+
if (parts.some((p, i) => p.text !== c[i].text)) next = parts;
|
|
341
|
+
}
|
|
342
|
+
if (next === c) return m;
|
|
343
|
+
changed = true;
|
|
344
|
+
return { ...m, content: next };
|
|
345
|
+
});
|
|
346
|
+
return changed ? out : messages;
|
|
347
|
+
}
|
|
254
348
|
function stripContent(content, stub) {
|
|
255
349
|
if (toolImageRef(content)) return stub;
|
|
256
350
|
if (typeof content === "string") return content;
|
|
@@ -273,12 +367,13 @@ function endsOnAnnouncedAction(text) {
|
|
|
273
367
|
if (PROGRESSIVE.test(last)) return true;
|
|
274
368
|
return clauses(last).some((c) => !NEXT_STEP.test(c) && !SIGN_OFF.test(c) && COMMITMENT.test(c));
|
|
275
369
|
}
|
|
276
|
-
var
|
|
370
|
+
var log3, p_sep, IMAGE_ELIDE_STUB, IMAGE_REF_SCHEME, REF_REGISTRY_MAX, refRegistry, isImageRefUrl, bearsImage, COMMITMENT, PROGRESSIVE, NEXT_STEP, SIGN_OFF;
|
|
277
371
|
var init_llm = __esm({
|
|
278
372
|
"src/llm.ts"() {
|
|
279
373
|
"use strict";
|
|
280
374
|
init_logging();
|
|
281
|
-
|
|
375
|
+
init_imageWire();
|
|
376
|
+
log3 = forComponent("llm");
|
|
282
377
|
p_sep = (parts) => parts.length > 1 ? "\n" : "";
|
|
283
378
|
IMAGE_ELIDE_STUB = "[image elided \u2014 re-attach @path or /paste to view again]";
|
|
284
379
|
IMAGE_REF_SCHEME = "agentx-image:";
|
|
@@ -965,7 +1060,7 @@ function makeWebFetchTool(options = {}) {
|
|
|
965
1060
|
|
|
966
1061
|
${capped}`;
|
|
967
1062
|
} catch (e) {
|
|
968
|
-
|
|
1063
|
+
log4.debug(`WebFetch ${u} failed`, e);
|
|
969
1064
|
return `Error fetching ${u}: ${e?.name === "AbortError" ? `timed out after ${timeoutMs}ms` : e?.message ?? e}`;
|
|
970
1065
|
} finally {
|
|
971
1066
|
clearTimeout(timer);
|
|
@@ -1089,7 +1184,7 @@ function makeWebSearchTool(options = {}) {
|
|
|
1089
1184
|
if (!res.ok) return `Error: search returned ${res.status} ${res.statusText}`;
|
|
1090
1185
|
return formatHits(parseDdgHtml(await res.text(), maxResults));
|
|
1091
1186
|
} catch (e) {
|
|
1092
|
-
|
|
1187
|
+
log4.debug("WebSearch failed", e);
|
|
1093
1188
|
return `Error searching: ${e?.name === "AbortError" ? `timed out after ${timeoutMs}ms` : e?.message ?? e}`;
|
|
1094
1189
|
} finally {
|
|
1095
1190
|
clearTimeout(timer);
|
|
@@ -1100,12 +1195,12 @@ function makeWebSearchTool(options = {}) {
|
|
|
1100
1195
|
function makeWebSearchAnthropicTool(opts = {}) {
|
|
1101
1196
|
return makeWebSearchTool({ provider: "anthropic", name: "WebSearchAnthropic", description: ANTHROPIC_SEARCH_DESC, anthropicApiKey: opts.anthropicApiKey, model: opts.model });
|
|
1102
1197
|
}
|
|
1103
|
-
var
|
|
1198
|
+
var log4, _dnsLookup, webFetchTool, webSearchTool, ANTHROPIC_SEARCH_DESC, webSearchAnthropicTool;
|
|
1104
1199
|
var init_tools_web = __esm({
|
|
1105
1200
|
"src/tools.web.ts"() {
|
|
1106
1201
|
"use strict";
|
|
1107
1202
|
init_logging();
|
|
1108
|
-
|
|
1203
|
+
log4 = forComponent("web");
|
|
1109
1204
|
webFetchTool = makeWebFetchTool();
|
|
1110
1205
|
webSearchTool = makeWebSearchTool();
|
|
1111
1206
|
ANTHROPIC_SEARCH_DESC = "High-quality web search via Anthropic's native search index. Returns concise, sourced findings \u2014 one claim per line, each with its source URL. Prefer this over WebSearch when accuracy and citations matter; it is slower (~3\u20138s) and bills the Anthropic API account per call (one cheap-model turn + search fee).";
|
|
@@ -1325,7 +1420,7 @@ function realPathOf(fs, path) {
|
|
|
1325
1420
|
try {
|
|
1326
1421
|
return fs.realPath(path);
|
|
1327
1422
|
} catch (e) {
|
|
1328
|
-
|
|
1423
|
+
log5.debug(`realPath unavailable for ${path} \u2014 inlining image bytes instead`, e);
|
|
1329
1424
|
return null;
|
|
1330
1425
|
}
|
|
1331
1426
|
}
|
|
@@ -1377,7 +1472,7 @@ function toolsByName(names, available) {
|
|
|
1377
1472
|
return t;
|
|
1378
1473
|
});
|
|
1379
1474
|
}
|
|
1380
|
-
var
|
|
1475
|
+
var log5, numberLines, bashTool, IMG_MIME, readTool, editTool;
|
|
1381
1476
|
var init_tools = __esm({
|
|
1382
1477
|
"src/tools.ts"() {
|
|
1383
1478
|
"use strict";
|
|
@@ -1388,7 +1483,7 @@ var init_tools = __esm({
|
|
|
1388
1483
|
init_tools_web();
|
|
1389
1484
|
init_OverlayFilesystem();
|
|
1390
1485
|
init_redact();
|
|
1391
|
-
|
|
1486
|
+
log5 = forComponent("tools");
|
|
1392
1487
|
numberLines = (content, offset = 0, limit) => {
|
|
1393
1488
|
const lines = content.split("\n");
|
|
1394
1489
|
const start = Math.max(0, offset);
|
|
@@ -1570,12 +1665,12 @@ ${tail || "(no output)"}`;
|
|
|
1570
1665
|
}
|
|
1571
1666
|
];
|
|
1572
1667
|
}
|
|
1573
|
-
var
|
|
1668
|
+
var log6, JobRegistryOptions, secs, JobRegistry, SandboxJobRegistry, NO_JOB, describe;
|
|
1574
1669
|
var init_tools_jobs = __esm({
|
|
1575
1670
|
"src/tools.jobs.ts"() {
|
|
1576
1671
|
"use strict";
|
|
1577
1672
|
init_logging();
|
|
1578
|
-
|
|
1673
|
+
log6 = forComponent("jobs");
|
|
1579
1674
|
JobRegistryOptions = class {
|
|
1580
1675
|
/** Tail buffer cap per job (chars). */
|
|
1581
1676
|
maxBuffer = 256 * 1024;
|
|
@@ -1754,7 +1849,7 @@ var init_tools_jobs = __esm({
|
|
|
1754
1849
|
try {
|
|
1755
1850
|
onExit({ id: job.id, kind: job.kind, label: job.label, status, reason: job.reason, tail: job.buf.slice(-4e3), result });
|
|
1756
1851
|
} catch (e) {
|
|
1757
|
-
|
|
1852
|
+
log6.warn(`job onExit handler threw for ${job.id}: ${e}`);
|
|
1758
1853
|
}
|
|
1759
1854
|
}
|
|
1760
1855
|
}
|
|
@@ -1808,7 +1903,7 @@ var init_tools_jobs = __esm({
|
|
|
1808
1903
|
try {
|
|
1809
1904
|
j.onKill?.(reason);
|
|
1810
1905
|
} catch (e) {
|
|
1811
|
-
|
|
1906
|
+
log6.warn(`onKill for ${id} threw: ${e}`);
|
|
1812
1907
|
}
|
|
1813
1908
|
}
|
|
1814
1909
|
return true;
|
|
@@ -1855,7 +1950,7 @@ var init_tools_jobs = __esm({
|
|
|
1855
1950
|
try {
|
|
1856
1951
|
cpu = await j.cpuTime();
|
|
1857
1952
|
} catch (e) {
|
|
1858
|
-
|
|
1953
|
+
log6.debug(`cpu probe for ${j.id} failed: ${e}`);
|
|
1859
1954
|
}
|
|
1860
1955
|
if (cpu != null && j.lastCpu != null && cpu > j.lastCpu) j.lastActivityAt = this.options.now();
|
|
1861
1956
|
const prev = j.lastCpu;
|
|
@@ -1863,7 +1958,7 @@ var init_tools_jobs = __esm({
|
|
|
1863
1958
|
const verdict = this.verdict(j, now3);
|
|
1864
1959
|
if (!verdict?.idle || j.status !== "running") continue;
|
|
1865
1960
|
if (cpu == null) {
|
|
1866
|
-
|
|
1961
|
+
log6.debug(`not reaping ${j.id}: ${verdict.reason} but CPU unknown`);
|
|
1867
1962
|
continue;
|
|
1868
1963
|
}
|
|
1869
1964
|
if (prev == null) continue;
|
|
@@ -1877,7 +1972,7 @@ var init_tools_jobs = __esm({
|
|
|
1877
1972
|
return null;
|
|
1878
1973
|
}
|
|
1879
1974
|
stuck(j, reason, now3) {
|
|
1880
|
-
|
|
1975
|
+
log6.warn(`reaping ${j.id} (${j.kind} "${j.label}"): ${reason} \u2014 age ${secs(now3 - j.startedAt)}, last activity ${secs(now3 - j.lastActivityAt)} ago, idleMs=${j.idleMs}, hardCapMs=${j.hardCapMs}, ${j.bytes} byte(s) output`);
|
|
1881
1976
|
if (j.onKill) j.notify = true;
|
|
1882
1977
|
this.kill(j.id, reason);
|
|
1883
1978
|
}
|
|
@@ -1891,7 +1986,7 @@ var init_tools_jobs = __esm({
|
|
|
1891
1986
|
this.timer = void 0;
|
|
1892
1987
|
return;
|
|
1893
1988
|
}
|
|
1894
|
-
void this.sweep().catch((e) =>
|
|
1989
|
+
void this.sweep().catch((e) => log6.warn(`job sweep failed: ${e}`));
|
|
1895
1990
|
}, this.options.sweepMs);
|
|
1896
1991
|
this.timer.unref?.();
|
|
1897
1992
|
}
|
|
@@ -2402,7 +2497,7 @@ async function psGroupCpuSeconds(pgid) {
|
|
|
2402
2497
|
const out = await new Promise((res, rej) => execFile("ps", ["-A", "-o", "pid=,ppid=,pgid=,time="], { timeout: 5e3, env: { ...process.env, LC_ALL: "C" } }, (e, so) => e ? rej(e) : res(String(so))));
|
|
2403
2498
|
return sumTreeCpu(out, pgid);
|
|
2404
2499
|
} catch (e) {
|
|
2405
|
-
|
|
2500
|
+
log9.debug(`ps cpu probe failed for group ${pgid}: ${e}`);
|
|
2406
2501
|
return void 0;
|
|
2407
2502
|
}
|
|
2408
2503
|
}
|
|
@@ -2550,7 +2645,7 @@ function makeRealShellTool(options) {
|
|
|
2550
2645
|
proc.stderr?.on("data", collect);
|
|
2551
2646
|
proc.on("error", (err) => {
|
|
2552
2647
|
if (err?.name === "AbortError" || ctl.signal.aborted) return finish(reasonFor(timedOut, timeoutMs, clean(out)));
|
|
2553
|
-
|
|
2648
|
+
log9.debug("shell spawn error", err);
|
|
2554
2649
|
finish(`[exit 1] ${err?.message ?? err}${out ? "\n" + clean(out) : ""}`);
|
|
2555
2650
|
});
|
|
2556
2651
|
proc.on("close", (code) => {
|
|
@@ -2623,7 +2718,7 @@ ${clean(out) || "(no output yet)"}`;
|
|
|
2623
2718
|
}
|
|
2624
2719
|
];
|
|
2625
2720
|
}
|
|
2626
|
-
var
|
|
2721
|
+
var log9, clean, DETACHED, SECRET_ENV_RE, _spawn, ShellJobRegistry, shellStatus, NO_JOB2;
|
|
2627
2722
|
var init_tools_shell = __esm({
|
|
2628
2723
|
"src/tools.shell.ts"() {
|
|
2629
2724
|
"use strict";
|
|
@@ -2632,7 +2727,7 @@ var init_tools_shell = __esm({
|
|
|
2632
2727
|
init_logging();
|
|
2633
2728
|
init_shell_sandbox();
|
|
2634
2729
|
init_tools_jobs();
|
|
2635
|
-
|
|
2730
|
+
log9 = forComponent("shell");
|
|
2636
2731
|
clean = (s) => truncateOutput(redactSecrets(s.replace(/\n+$/, "")));
|
|
2637
2732
|
DETACHED = { stdio: ["ignore", "pipe", "pipe"], detached: true };
|
|
2638
2733
|
SECRET_ENV_RE = /(_API_KEY|_TOKEN|_SECRET|_PASSWORD|_PRIVATE_KEY|^AWS_|^GITHUB_TOKEN$|^OPENAI_|^ANTHROPIC_|^GOOGLE_|^GEMINI_|^GROQ_|^NPM_TOKEN$)/i;
|
|
@@ -3608,7 +3703,7 @@ init_OverlayFilesystem();
|
|
|
3608
3703
|
|
|
3609
3704
|
// src/worktree.ts
|
|
3610
3705
|
import { spawnSync } from "child_process";
|
|
3611
|
-
import { symlinkSync, mkdirSync, statSync as
|
|
3706
|
+
import { symlinkSync, mkdirSync, statSync as statSync3, readFileSync as readFileSync3, rmSync } from "fs";
|
|
3612
3707
|
import { join } from "path";
|
|
3613
3708
|
var VALID_SEGMENT = /^[a-zA-Z0-9._-]+$/;
|
|
3614
3709
|
var MAX_SLUG_LENGTH = 64;
|
|
@@ -3647,16 +3742,16 @@ function findGitRoot(from) {
|
|
|
3647
3742
|
}
|
|
3648
3743
|
function readWorktreeHead(worktreePath) {
|
|
3649
3744
|
try {
|
|
3650
|
-
const gitFile =
|
|
3745
|
+
const gitFile = readFileSync3(join(worktreePath, ".git"), "utf-8").trim();
|
|
3651
3746
|
const m = gitFile.match(/^gitdir:\s*(.+)$/);
|
|
3652
3747
|
if (!m) return null;
|
|
3653
3748
|
const headPath = join(m[1], "HEAD");
|
|
3654
|
-
const head =
|
|
3749
|
+
const head = readFileSync3(headPath, "utf-8").trim();
|
|
3655
3750
|
const refMatch = head.match(/^ref:\s*(.+)$/);
|
|
3656
3751
|
if (!refMatch) return head.length === 40 ? head : null;
|
|
3657
3752
|
const refPath = join(m[1], "..", refMatch[1]);
|
|
3658
3753
|
try {
|
|
3659
|
-
return
|
|
3754
|
+
return readFileSync3(refPath, "utf-8").trim();
|
|
3660
3755
|
} catch {
|
|
3661
3756
|
}
|
|
3662
3757
|
const r = git(["rev-parse", "HEAD"], worktreePath);
|
|
@@ -3692,7 +3787,7 @@ function symlinkDirs(repoRoot, worktreePath, dirs) {
|
|
|
3692
3787
|
const src = join(repoRoot, dir);
|
|
3693
3788
|
const dst = join(worktreePath, dir);
|
|
3694
3789
|
try {
|
|
3695
|
-
|
|
3790
|
+
statSync3(src);
|
|
3696
3791
|
} catch {
|
|
3697
3792
|
continue;
|
|
3698
3793
|
}
|
|
@@ -3708,7 +3803,7 @@ function cleanupWorktree(repoRoot, slug2, opts) {
|
|
|
3708
3803
|
const worktreePath = worktreePathFor(repoRoot, slug2);
|
|
3709
3804
|
const branch = worktreeBranchName(slug2);
|
|
3710
3805
|
try {
|
|
3711
|
-
|
|
3806
|
+
statSync3(worktreePath);
|
|
3712
3807
|
} catch {
|
|
3713
3808
|
return { removed: false, reason: "not found" };
|
|
3714
3809
|
}
|
|
@@ -3758,7 +3853,7 @@ function exitWorktree() {
|
|
|
3758
3853
|
// src/subagent.ts
|
|
3759
3854
|
init_NodeDiskFilesystem();
|
|
3760
3855
|
init_logging();
|
|
3761
|
-
var
|
|
3856
|
+
var log7 = forComponent("subagent");
|
|
3762
3857
|
async function boundedPool(items, limit, fn) {
|
|
3763
3858
|
const out = new Array(items.length);
|
|
3764
3859
|
let next = 0;
|
|
@@ -3832,7 +3927,7 @@ function traceFileFor(dir, label, depth) {
|
|
|
3832
3927
|
const id = `${Date.now().toString(36)}-${(traceSeq++).toString(36)}`;
|
|
3833
3928
|
return join3(dir, `${id}-d${depth}-${sanitizeSlug(label) || "sub"}.json`);
|
|
3834
3929
|
} catch (e) {
|
|
3835
|
-
|
|
3930
|
+
log7.warn(`subagent trace dir unavailable (${dir}) \u2014 running without a trace: ${e instanceof Error ? e.message : String(e)}`);
|
|
3836
3931
|
return void 0;
|
|
3837
3932
|
}
|
|
3838
3933
|
}
|
|
@@ -3840,7 +3935,7 @@ function flushTrace(file, meta, messages, status) {
|
|
|
3840
3935
|
try {
|
|
3841
3936
|
writeFileSync(file, JSON.stringify({ ...meta, status, updated: Date.now(), messages }, null, 2));
|
|
3842
3937
|
} catch (e) {
|
|
3843
|
-
|
|
3938
|
+
log7.warn(`subagent trace flush failed (${file}): ${e instanceof Error ? e.message : String(e)}`);
|
|
3844
3939
|
}
|
|
3845
3940
|
}
|
|
3846
3941
|
function installTrace(child, file, meta) {
|
|
@@ -3881,7 +3976,7 @@ async function runChildTraced(args) {
|
|
|
3881
3976
|
if (file) flushTrace(file, meta, child.transcript, wc.timedOut() ? "deadline" : res.finishReason);
|
|
3882
3977
|
let result = res.finishReason === "stop" ? res.text || `(child '${label}' finished with no summary; finishReason=${res.finishReason})` : abortHint(label, res, file);
|
|
3883
3978
|
if (wc.timedOut()) {
|
|
3884
|
-
|
|
3979
|
+
log7.warn(`sub-task '${label}' hit its ${deadlineMs}ms wall-clock deadline (steps=${res.steps})`);
|
|
3885
3980
|
result += deadlineNote(deadlineMs);
|
|
3886
3981
|
}
|
|
3887
3982
|
await opts.hooks?.onSubagentStop?.(result, { label, agentType });
|
|
@@ -4210,7 +4305,7 @@ function reasoningToChatFragment(model, effort) {
|
|
|
4210
4305
|
}
|
|
4211
4306
|
|
|
4212
4307
|
// src/Agent.ts
|
|
4213
|
-
var
|
|
4308
|
+
var log8 = forComponent("Agent");
|
|
4214
4309
|
function envInt(key, fallback) {
|
|
4215
4310
|
const n = typeof process !== "undefined" ? Number(process.env?.[key]) : NaN;
|
|
4216
4311
|
return Number.isFinite(n) && n >= 0 ? n : fallback;
|
|
@@ -4291,6 +4386,9 @@ var AgentOptions = class {
|
|
|
4291
4386
|
* `maxTokens`: that is the cumulative SPEND kill-switch, and a spend budget must never be able to
|
|
4292
4387
|
* drive destructive compaction (`--max-tokens 50000` would otherwise shred the transcript). */
|
|
4293
4388
|
contextWindow = 0;
|
|
4389
|
+
/** Does a model take image input? true/false from the host's model catalog; undefined = unknown (images are tried,
|
|
4390
|
+
* and turned off for the run when the adapter refuses them). Unset = always try. */
|
|
4391
|
+
imageInput;
|
|
4294
4392
|
/** Pagination ceiling for a SINGLE tool result (bytes). A result over this is cropped to page 1 with
|
|
4295
4393
|
* a marker telling the model it was cropped (refine the query, or page further). Guards against one
|
|
4296
4394
|
* Grep/Read/MCP call blowing the whole context window. 0 = off. Default 60k (~15k tokens). */
|
|
@@ -4590,7 +4688,7 @@ var Agent = class _Agent {
|
|
|
4590
4688
|
const disk = new NodeDiskFilesystem2(process.cwd());
|
|
4591
4689
|
await disk.init();
|
|
4592
4690
|
this.options.fs = new JailedFilesystem2(disk);
|
|
4593
|
-
|
|
4691
|
+
log8.info(`no fs provided \u2014 defaulting to jailed real disk at ${process.cwd()}`);
|
|
4594
4692
|
}
|
|
4595
4693
|
this.buildCtx();
|
|
4596
4694
|
}
|
|
@@ -4735,7 +4833,8 @@ var Agent = class _Agent {
|
|
|
4735
4833
|
const cap = _Agent.contextCap(this.options);
|
|
4736
4834
|
const target = Math.round(cap * _Agent.COMPACT_TARGET);
|
|
4737
4835
|
const kr = this.options.keepRecentImages ?? 1;
|
|
4738
|
-
const
|
|
4836
|
+
const vis = this.sendsImages();
|
|
4837
|
+
const tokensBefore = estimateTokens(this.transcript, kr, vis);
|
|
4739
4838
|
const msgsBefore = this.transcript.length;
|
|
4740
4839
|
let max = Math.max(2, maxMessages);
|
|
4741
4840
|
if (this.transcript.length <= max && tokensBefore <= target)
|
|
@@ -4743,18 +4842,18 @@ var Agent = class _Agent {
|
|
|
4743
4842
|
void this.activeHooks?.onPreCompact?.(this.transcript);
|
|
4744
4843
|
let out = this.transcript;
|
|
4745
4844
|
if (out.length > max) out = compact(out, max, focus);
|
|
4746
|
-
if (estimateTokens(out, kr) > target) out = elideOversizedImages(out, target, true, kr);
|
|
4747
|
-
if (estimateTokens(out, kr) > target) out = capOversizedBodies(out, target, kr);
|
|
4748
|
-
if (estimateTokens(out, kr) > target) out = elideOversizedImages(out, target, false, kr);
|
|
4749
|
-
while (estimateTokens(out, kr) > target && max > 2 && out.length > 2) {
|
|
4845
|
+
if (estimateTokens(out, kr, vis) > target) out = elideOversizedImages(out, target, true, kr);
|
|
4846
|
+
if (estimateTokens(out, kr, vis) > target) out = capOversizedBodies(out, target, kr);
|
|
4847
|
+
if (estimateTokens(out, kr, vis) > target) out = elideOversizedImages(out, target, false, kr);
|
|
4848
|
+
while (estimateTokens(out, kr, vis) > target && max > 2 && out.length > 2) {
|
|
4750
4849
|
max = Math.max(2, Math.floor(max / 2));
|
|
4751
4850
|
out = capOversizedBodies(compact(out, max, focus), target, kr);
|
|
4752
4851
|
}
|
|
4753
|
-
if (estimateTokens(out, kr) > target) out = fitTokenBudget(out, target, kr);
|
|
4852
|
+
if (estimateTokens(out, kr, vis) > target) out = fitTokenBudget(out, target, kr);
|
|
4754
4853
|
out = dropOrphanToolResults(out);
|
|
4755
4854
|
this.transcript = out;
|
|
4756
4855
|
this.releaseStaleImages();
|
|
4757
|
-
return { tokensBefore, tokensAfter: estimateTokens(this.transcript, kr), target, cap, removed: msgsBefore - this.transcript.length };
|
|
4856
|
+
return { tokensBefore, tokensAfter: estimateTokens(this.transcript, kr, vis), target, cap, removed: msgsBefore - this.transcript.length };
|
|
4758
4857
|
}
|
|
4759
4858
|
/** Drop inline base64 images from older messages so Bun/JSC can reclaim the heap (see contentBytes). */
|
|
4760
4859
|
releaseStaleImages(keepRecent = 1) {
|
|
@@ -4778,7 +4877,7 @@ var Agent = class _Agent {
|
|
|
4778
4877
|
let closureNudged = false;
|
|
4779
4878
|
let lastProgressAt = start;
|
|
4780
4879
|
const kill = (finishReason) => {
|
|
4781
|
-
|
|
4880
|
+
log8.warn(`kill-switch: ${finishReason} (steps=${steps}, tokens=${usage.totalTokens}, budgetTokens=${Math.round(usage.totalTokens - 0.9 * usage.cacheReadTokens)}, ms=${Date.now() - start - this.parkedMs}${finishReason === "timeout" ? `, idle=${Date.now() - lastProgressAt - this.parkedMs}ms` : ""}${this.parkedMs ? ` +${this.parkedMs} parked` : ""})`);
|
|
4782
4881
|
this.jobs.killAll(`run ended: ${finishReason}`, true);
|
|
4783
4882
|
const claims = this.survivingClaims();
|
|
4784
4883
|
return { text: lastAssistantText(this.transcript), steps, finishReason, messages: this.transcript, usage, usageEstimated, ...claims.length ? { fabricatedToolClaims: claims } : {} };
|
|
@@ -4793,7 +4892,7 @@ var Agent = class _Agent {
|
|
|
4793
4892
|
steps++;
|
|
4794
4893
|
this.options.host?.notify?.({ kind: "turn_start", message: `step ${steps}` });
|
|
4795
4894
|
let res;
|
|
4796
|
-
|
|
4895
|
+
let sent = this.trimContext();
|
|
4797
4896
|
const frag = reasoningToChatFragment(o.model, o.reasoning);
|
|
4798
4897
|
const sentTools = o.model.startsWith("claude-code/") ? [] : wireTools;
|
|
4799
4898
|
const isCursorWithTools = o.model.startsWith("cursor/") && wireTools.length > 0;
|
|
@@ -4804,9 +4903,9 @@ var Agent = class _Agent {
|
|
|
4804
4903
|
this.nativeHoldMs = o.nativeHoldMs;
|
|
4805
4904
|
if (isCursorWithTools) {
|
|
4806
4905
|
if (typeof hostToolTimeoutMs === "number" && effectiveToolTimeoutMs !== hostToolTimeoutMs) {
|
|
4807
|
-
|
|
4906
|
+
log8.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.`);
|
|
4808
4907
|
}
|
|
4809
|
-
for (const v of toolTimeoutViolations(this.activeTools, effectiveToolTimeoutMs))
|
|
4908
|
+
for (const v of toolTimeoutViolations(this.activeTools, effectiveToolTimeoutMs)) log8.warn(v);
|
|
4810
4909
|
}
|
|
4811
4910
|
const hostExec = async (name, args) => {
|
|
4812
4911
|
const tc = { id: `host-${Date.now()}`, type: "function", function: { name, arguments: JSON.stringify(args) } };
|
|
@@ -4865,12 +4964,20 @@ var Agent = class _Agent {
|
|
|
4865
4964
|
res = r;
|
|
4866
4965
|
}
|
|
4867
4966
|
if (steps === 1 && this.isEmptyStop(res) && !this.pendingInjections.length) {
|
|
4868
|
-
const e = new Error(
|
|
4967
|
+
const e = new Error(`model returned an empty turn (finishReason=${res.finishReason ?? "n/a"}, no text, no tools)`);
|
|
4869
4968
|
e.code = "empty";
|
|
4969
|
+
if (res.finishReason) e.providerFinishReason = res.finishReason;
|
|
4870
4970
|
throw e;
|
|
4871
4971
|
}
|
|
4872
4972
|
break;
|
|
4873
4973
|
} catch (err) {
|
|
4974
|
+
if (!this.imagesOff && /does not support .*input/i.test(String(err?.message ?? "")) && sent.some((m) => Array.isArray(m.content) && m.role === "tool")) {
|
|
4975
|
+
this.imagesOff = true;
|
|
4976
|
+
log8.warn(`model refused image input (${err?.message}) \u2014 resending with image Reads as text stubs`);
|
|
4977
|
+
sent = this.trimContext();
|
|
4978
|
+
attempt--;
|
|
4979
|
+
continue;
|
|
4980
|
+
}
|
|
4874
4981
|
const sc = err?.statusCode;
|
|
4875
4982
|
const serverSide = sc >= 500 || sc === 408 || /Service Unavailable|overloaded|Internal server error|Bad gateway|Gateway time/i.test(String(err?.message ?? ""));
|
|
4876
4983
|
const network = !sc && /ECONNRESET|ECONNREFUSED|ETIMEDOUT|ENOTFOUND|EAI_AGAIN|EPIPE|socket hang up|fetch failed|network|terminated|UND_ERR/i.test(String(err?.message ?? err?.code ?? err));
|
|
@@ -4878,11 +4985,11 @@ var Agent = class _Agent {
|
|
|
4878
4985
|
const empty = err?.code === "empty" || err?.code === "cursor_silent";
|
|
4879
4986
|
const maxAttempts = empty ? Math.max(o.emptyRetries, 0) : 2;
|
|
4880
4987
|
const replaysTool = stalled && this.toolRanThisAttempt;
|
|
4881
|
-
if (replaysTool)
|
|
4988
|
+
if (replaysTool) log8.warn("model stall after tool(s) already ran this step \u2014 not retrying (a retry would replay them)");
|
|
4882
4989
|
const transient = !o.signal?.aborted && !isAbortError(err) && !replaysTool && attempt < maxAttempts && (network || serverSide || stalled || empty);
|
|
4883
4990
|
if (!transient) throw err;
|
|
4884
4991
|
const waitMs = (empty ? o.emptyRetryBaseMs : 1e3) * (attempt + 1);
|
|
4885
|
-
|
|
4992
|
+
log8.warn(`${empty ? "empty model turn" : stalled ? "model stall" : "network drop"} mid-step (${err?.message ?? err}) \u2014 retrying in ${waitMs}ms`);
|
|
4886
4993
|
o.host?.notify?.({ kind: "retry", message: `${empty ? "model returned nothing" : stalled ? "model stalled" : "connection dropped"} \u2014 retrying step (#${attempt + 1})` });
|
|
4887
4994
|
await new Promise((r) => setTimeout(r, waitMs));
|
|
4888
4995
|
}
|
|
@@ -4890,13 +4997,14 @@ var Agent = class _Agent {
|
|
|
4890
4997
|
} catch (err) {
|
|
4891
4998
|
if (err?.code === "budget") return kill("budget");
|
|
4892
4999
|
if (err?.code === "stall") {
|
|
4893
|
-
|
|
4894
|
-
return { ...kill("timeout"), error: err };
|
|
5000
|
+
log8.warn(`model stalled out after retries: ${err?.message ?? err}`);
|
|
5001
|
+
return { ...kill("timeout"), providerFinishReason: "none (stalled)", error: err };
|
|
4895
5002
|
}
|
|
4896
5003
|
if (o.signal?.aborted || isAbortError(err)) return kill("aborted");
|
|
4897
5004
|
if (err?.code === "empty" || err?.code === "cursor_silent") {
|
|
4898
|
-
|
|
4899
|
-
|
|
5005
|
+
log8.warn(`model returned empty/silent turns after retries \u2014 surfacing as failed`);
|
|
5006
|
+
const pfr = err?.providerFinishReason;
|
|
5007
|
+
return { text: "", steps, finishReason: "empty", ...pfr ? { providerFinishReason: pfr } : {}, messages: this.transcript, usage, usageEstimated, error: err };
|
|
4900
5008
|
}
|
|
4901
5009
|
const body = err?.body ?? err?.response?.data ?? err?.error;
|
|
4902
5010
|
let bodyStr;
|
|
@@ -4907,13 +5015,13 @@ var Agent = class _Agent {
|
|
|
4907
5015
|
}
|
|
4908
5016
|
if (bodyStr && err instanceof Error && !err.message.includes(bodyStr)) err.detail = bodyStr;
|
|
4909
5017
|
const errLog = err instanceof Error ? { message: err.message, ...err.detail ? { detail: err.detail } : {}, stack: err.stack } : err;
|
|
4910
|
-
|
|
5018
|
+
log8.error(`chat() failed: ${err?.message ?? err}${bodyStr ? ` \u2014 ${bodyStr}` : ""}`, errLog);
|
|
4911
5019
|
return { text: "", steps, finishReason: "error", messages: this.transcript, usage, usageEstimated, error: err };
|
|
4912
5020
|
}
|
|
4913
5021
|
if (o.signal?.aborted) return kill("aborted");
|
|
4914
5022
|
if (res.finishReason === "error" && !res.toolCalls?.length) {
|
|
4915
5023
|
const err = new Error(contentText(res.content ?? "").trim() || "provider error");
|
|
4916
|
-
|
|
5024
|
+
log8.error(`chat() failed: ${err.message}`);
|
|
4917
5025
|
return { text: "", steps, finishReason: "error", messages: this.transcript, usage, usageEstimated, error: err };
|
|
4918
5026
|
}
|
|
4919
5027
|
if (!res.usage) {
|
|
@@ -4971,21 +5079,21 @@ var Agent = class _Agent {
|
|
|
4971
5079
|
if (emptyTurn && o.failEmptyTurns) {
|
|
4972
5080
|
if (!emptyNudged && canNudge()) {
|
|
4973
5081
|
emptyNudged = true;
|
|
4974
|
-
|
|
5082
|
+
log8.warn(`empty model turn after tool work (step ${steps}, provider finishReason=${res.finishReason ?? "n/a"}) \u2014 nudging once`);
|
|
4975
5083
|
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." });
|
|
4976
5084
|
continue;
|
|
4977
5085
|
}
|
|
4978
|
-
|
|
5086
|
+
log8.warn(`model returned empty turns after tool work \u2014 surfacing as failed`);
|
|
4979
5087
|
const e = new Error("empty_response: model returned an empty turn (no text, no tools) after a re-prompt");
|
|
4980
5088
|
e.code = "empty";
|
|
4981
5089
|
return { text: "", steps, finishReason: "empty", ...res.finishReason ? { providerFinishReason: res.finishReason } : {}, messages: this.transcript, usage, usageEstimated, error: e };
|
|
4982
5090
|
}
|
|
4983
5091
|
const fabricated = o.catchFabricatedToolUse && !closureNudged ? this.fabricatedToolNames(contentText(res.content ?? "")) : [];
|
|
4984
5092
|
for (const n of fabricated) this.fabricatedClaims.add(n);
|
|
4985
|
-
if (fabricated.length && !canNudge())
|
|
5093
|
+
if (fabricated.length && !canNudge()) log8.warn(`fabricated tool use: text names ${fabricated.join(", ")} but no step/budget left to demand the call \u2014 finishing with the existing answer`);
|
|
4986
5094
|
if (fabricated.length && canNudge()) {
|
|
4987
5095
|
closureNudged = true;
|
|
4988
|
-
|
|
5096
|
+
log8.warn(`fabricated tool use: text names ${fabricated.join(", ")} but the run invoked ${this.invokedTools.size ? [...this.invokedTools].join(", ") : "nothing"} \u2014 demanding the real call`);
|
|
4989
5097
|
this.transcript.push({
|
|
4990
5098
|
role: "user",
|
|
4991
5099
|
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.`
|
|
@@ -4994,14 +5102,14 @@ var Agent = class _Agent {
|
|
|
4994
5102
|
}
|
|
4995
5103
|
if (o.closeDelegatedTurns && res.endedWithoutClosing && !closureNudged && canNudge()) {
|
|
4996
5104
|
closureNudged = true;
|
|
4997
|
-
|
|
5105
|
+
log8.verbose("delegated turn went silent after a tool \u2014 nudging a closing answer");
|
|
4998
5106
|
this.transcript.push({
|
|
4999
5107
|
role: "user",
|
|
5000
5108
|
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."
|
|
5001
5109
|
});
|
|
5002
5110
|
continue;
|
|
5003
5111
|
}
|
|
5004
|
-
|
|
5112
|
+
log8.verbose(`completed in ${steps} step(s)`);
|
|
5005
5113
|
const drained = await this.jobs.drain(o.signal);
|
|
5006
5114
|
if (o.signal?.aborted) return kill("aborted");
|
|
5007
5115
|
if (drained.length) lastProgressAt = Date.now();
|
|
@@ -5035,7 +5143,7 @@ var Agent = class _Agent {
|
|
|
5035
5143
|
if (this.ctx.needsInput) {
|
|
5036
5144
|
const question = this.ctx.needsInput;
|
|
5037
5145
|
this.ctx.needsInput = void 0;
|
|
5038
|
-
|
|
5146
|
+
log8.warn(`needs_input: ${question.question} (steps=${steps})`);
|
|
5039
5147
|
await this.jobs.drain();
|
|
5040
5148
|
const text = [
|
|
5041
5149
|
`OPEN QUESTION (run paused \u2014 no human was reachable): ${question.question}`,
|
|
@@ -5077,12 +5185,12 @@ var Agent = class _Agent {
|
|
|
5077
5185
|
if (this.toolHoldActive()) {
|
|
5078
5186
|
const now3 = Date.now();
|
|
5079
5187
|
const who = this.jobs.holding().map((j) => `${j.kind} "${j.label}" (${Math.round((now3 - j.startedAt) / 1e3)}s, quiet ${Math.round((now3 - j.lastActivityAt) / 1e3)}s)`).join(", ");
|
|
5080
|
-
|
|
5188
|
+
log8.warn(`stall watchdog: no stream output for ${stallMs}ms \u2014 held by ${who}`);
|
|
5081
5189
|
this.options.host?.notify?.({ kind: "tool_hold", message: `waiting on ${who}` });
|
|
5082
5190
|
return poke();
|
|
5083
5191
|
}
|
|
5084
5192
|
const inFlight = this.jobs.list().filter((j) => j.status === "running");
|
|
5085
|
-
|
|
5193
|
+
log8.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(", ")})` : ""));
|
|
5086
5194
|
fired = true;
|
|
5087
5195
|
ac.abort(stallError());
|
|
5088
5196
|
}, stallMs);
|
|
@@ -5252,13 +5360,13 @@ var Agent = class _Agent {
|
|
|
5252
5360
|
const decision = await this.park(Promise.resolve(hooks?.preToolUse?.(call, meta)));
|
|
5253
5361
|
if (decision?.block) {
|
|
5254
5362
|
const blocked = `Blocked by hook: ${decision.reason ?? "no reason given"}`;
|
|
5255
|
-
|
|
5363
|
+
log8.debug(`${tc.function.name} -> ${blocked}`);
|
|
5256
5364
|
await hooks?.postToolUse?.(call, blocked, meta);
|
|
5257
5365
|
return blocked;
|
|
5258
5366
|
}
|
|
5259
5367
|
this.options.host?.notify?.({ kind: "tool_use", id: tc.id ?? "", name: tc.function.name, input: args });
|
|
5260
5368
|
if (earlyError) {
|
|
5261
|
-
|
|
5369
|
+
log8.debug(`${tc.function.name} -> ${earlyError}`);
|
|
5262
5370
|
await hooks?.postToolUse?.(call, earlyError, meta);
|
|
5263
5371
|
this.options.host?.notify?.({ kind: "tool_result", id: tc.id ?? "", output: earlyError, isError: true });
|
|
5264
5372
|
return earlyError;
|
|
@@ -5267,12 +5375,12 @@ var Agent = class _Agent {
|
|
|
5267
5375
|
let images;
|
|
5268
5376
|
let threw = false;
|
|
5269
5377
|
try {
|
|
5270
|
-
|
|
5378
|
+
log8.debug(`${tc.function.name}(${tc.function.arguments})`);
|
|
5271
5379
|
this.ctx.emit = hooks?.onToolOutput ? (chunk) => {
|
|
5272
5380
|
try {
|
|
5273
5381
|
hooks.onToolOutput(call, chunk, meta);
|
|
5274
5382
|
} catch (e) {
|
|
5275
|
-
|
|
5383
|
+
log8.debug(`onToolOutput hook error: ${e}`);
|
|
5276
5384
|
}
|
|
5277
5385
|
} : void 0;
|
|
5278
5386
|
const raw = await tool.run(args, this.ctx);
|
|
@@ -5284,7 +5392,7 @@ var Agent = class _Agent {
|
|
|
5284
5392
|
}
|
|
5285
5393
|
} catch (e) {
|
|
5286
5394
|
const msg = e instanceof Error ? e.message : String(e);
|
|
5287
|
-
|
|
5395
|
+
log8.debug(`${tc.function.name} -> error: ${msg}`);
|
|
5288
5396
|
result = `Error: ${msg}`;
|
|
5289
5397
|
threw = true;
|
|
5290
5398
|
} finally {
|
|
@@ -5344,6 +5452,13 @@ ${out}`;
|
|
|
5344
5452
|
* 3. token-aware backstop (`maxContextTokens`) — drop oldest messages until under an estimated token ceiling.
|
|
5345
5453
|
* With every knob off, returns the transcript unchanged (same reference).
|
|
5346
5454
|
*/
|
|
5455
|
+
/** Set when the model refused image parts this run: tool image Reads go as text stubs from then on. */
|
|
5456
|
+
imagesOff = false;
|
|
5457
|
+
/** Send tool image Reads as images? Known models by their catalog flag; unknown ones try (and latch off on refusal). */
|
|
5458
|
+
sendsImages() {
|
|
5459
|
+
if (this.imagesOff) return false;
|
|
5460
|
+
return this.options.imageInput?.(this.options.model) ?? true;
|
|
5461
|
+
}
|
|
5347
5462
|
trimContext() {
|
|
5348
5463
|
const o = this.options;
|
|
5349
5464
|
const m = this.transcript;
|
|
@@ -5353,17 +5468,19 @@ ${out}`;
|
|
|
5353
5468
|
const browserKeep = this.browserActive ? o.browserTrim?.keepOutputs : void 0;
|
|
5354
5469
|
const keep = browserKeep != null ? o.keepToolOutputs ? Math.min(o.keepToolOutputs, browserKeep) : browserKeep : o.keepToolOutputs;
|
|
5355
5470
|
if (keep) out = stubOldToolResults(out ?? m, keep);
|
|
5356
|
-
out = expandImagesForSend(dropOrphanToolResults(out ?? m), o.keepRecentImages ?? 1);
|
|
5471
|
+
out = expandImagesForSend(dropOrphanToolResults(out ?? m), o.keepRecentImages ?? 1, void 0, this.sendsImages());
|
|
5357
5472
|
if (o.maxContextTokens) {
|
|
5358
5473
|
const pre = out.length;
|
|
5359
|
-
out = fitTokenBudget(out, o.maxContextTokens, o.keepRecentImages ?? 1);
|
|
5474
|
+
out = fitTokenBudget(out, o.maxContextTokens, o.keepRecentImages ?? 1, this.sendsImages());
|
|
5360
5475
|
const dropped = pre - out.length;
|
|
5361
5476
|
if (dropped > 0 && dropped !== this.lastTrimNotified) {
|
|
5362
5477
|
this.lastTrimNotified = dropped;
|
|
5363
5478
|
o.host?.notify?.({ kind: "compaction", message: `context full \u2014 auto-trimmed ${dropped} oldest message(s) to fit ~${Math.round(o.maxContextTokens / 1e3)}k tokens` });
|
|
5364
5479
|
}
|
|
5365
5480
|
}
|
|
5366
|
-
|
|
5481
|
+
out = dropOrphanToolResults(out);
|
|
5482
|
+
if (/^google\//.test(o.model ?? "")) out = hoistToolImages(out);
|
|
5483
|
+
return scrubBase64(out);
|
|
5367
5484
|
}
|
|
5368
5485
|
};
|
|
5369
5486
|
function dropOldest(m, max) {
|
|
@@ -5372,8 +5489,8 @@ function dropOldest(m, max) {
|
|
|
5372
5489
|
}
|
|
5373
5490
|
var ANTHROPIC_WINDOW_WITHOUT_1M_BETA = 2e5;
|
|
5374
5491
|
var isAnthropicModel = (m) => !!m && (m.startsWith("anthropic/") || /(^|\/)claude-/.test(m));
|
|
5375
|
-
function estimateTokens(m, keepRecentImages = 1) {
|
|
5376
|
-
return Math.ceil(sendBytes(m, keepRecentImages) / 4);
|
|
5492
|
+
function estimateTokens(m, keepRecentImages = 1, vision = true) {
|
|
5493
|
+
return Math.ceil(sendBytes(m, keepRecentImages, vision) / 4);
|
|
5377
5494
|
}
|
|
5378
5495
|
var TRANSPORT_TOOL_TIMEOUT_FLOOR_MS = 18e5;
|
|
5379
5496
|
var isMcpTool = (t) => t.name.startsWith("mcp__") || t.name === "McpCall";
|
|
@@ -5542,8 +5659,8 @@ function dropOrphanToolResults(messages) {
|
|
|
5542
5659
|
const ok = (m) => m.role !== "tool" || ids.has(m.tool_call_id ?? "");
|
|
5543
5660
|
return messages.every(ok) ? messages : messages.filter(ok);
|
|
5544
5661
|
}
|
|
5545
|
-
function fitTokenBudget(messages, maxTokens, keepRecentImages = 1) {
|
|
5546
|
-
const per = sendBytesPer(messages, keepRecentImages).map((b) => Math.ceil(b / 4));
|
|
5662
|
+
function fitTokenBudget(messages, maxTokens, keepRecentImages = 1, vision = true) {
|
|
5663
|
+
const per = sendBytesPer(messages, keepRecentImages, vision).map((b) => Math.ceil(b / 4));
|
|
5547
5664
|
let total = per.reduce((a, b) => a + b, 0);
|
|
5548
5665
|
if (total <= maxTokens) return messages;
|
|
5549
5666
|
const head = messages[0]?.role === "system" ? [messages[0]] : [];
|
|
@@ -5552,7 +5669,7 @@ function fitTokenBudget(messages, maxTokens, keepRecentImages = 1) {
|
|
|
5552
5669
|
const ids = callIdSet(messages.slice(from));
|
|
5553
5670
|
while (from < messages.length && messages[from].role === "tool" && !ids.has(messages[from].tool_call_id ?? "")) total -= per[from++];
|
|
5554
5671
|
if (total > maxTokens)
|
|
5555
|
-
|
|
5672
|
+
log8.warn(`context ~${total} tok still over maxContextTokens=${maxTokens} after trimming (system head can't be dropped)`);
|
|
5556
5673
|
return [...head, ...messages.slice(from)];
|
|
5557
5674
|
}
|
|
5558
5675
|
function compact(m, max, focus) {
|
|
@@ -6235,7 +6352,7 @@ init_tools_web();
|
|
|
6235
6352
|
init_tools();
|
|
6236
6353
|
init_tools_structured();
|
|
6237
6354
|
init_logging();
|
|
6238
|
-
var
|
|
6355
|
+
var log10 = forComponent("scratch");
|
|
6239
6356
|
var SCRATCH_DIR = "/scratch";
|
|
6240
6357
|
function shortArgs(args) {
|
|
6241
6358
|
try {
|
|
@@ -6275,7 +6392,7 @@ var Scratch = class {
|
|
|
6275
6392
|
await (this.dirReady ??= mkdirp(this.fs, dir));
|
|
6276
6393
|
await this.fs.writeFile(path, header + raw);
|
|
6277
6394
|
} catch (e) {
|
|
6278
|
-
|
|
6395
|
+
log10.debug("scratch write failed; returning raw", e);
|
|
6279
6396
|
return raw;
|
|
6280
6397
|
}
|
|
6281
6398
|
const preview = raw.slice(0, previewChars).replace(/\s+/g, " ").trim();
|
|
@@ -6305,7 +6422,7 @@ To pull a specific detail, Grep/Read ${path}, or call Ask({ question: "\u2026",
|
|
|
6305
6422
|
await (this.dirReady ??= mkdirp(this.fs, dir));
|
|
6306
6423
|
await this.fs.writeFile(path, header + full);
|
|
6307
6424
|
} catch (e) {
|
|
6308
|
-
|
|
6425
|
+
log10.debug("scratch spill failed; cropping lossy", e);
|
|
6309
6426
|
return full.slice(0, pageBytes) + `
|
|
6310
6427
|
|
|
6311
6428
|
[output cropped to ${pageBytes} of ${full.length} bytes; full output unavailable (scratch write failed) \u2014 refine your query]`;
|
|
@@ -6351,7 +6468,7 @@ Question: ${q}`);
|
|
|
6351
6468
|
const answer = (res.text ?? "").trim();
|
|
6352
6469
|
return answer || "(no answer found in scratch)";
|
|
6353
6470
|
} catch (e) {
|
|
6354
|
-
|
|
6471
|
+
log10.debug("Ask peek failed", e);
|
|
6355
6472
|
return `Error querying scratch: ${e?.message ?? e}`;
|
|
6356
6473
|
}
|
|
6357
6474
|
}
|
|
@@ -6360,7 +6477,7 @@ Question: ${q}`);
|
|
|
6360
6477
|
|
|
6361
6478
|
// src/lessons.ts
|
|
6362
6479
|
init_logging();
|
|
6363
|
-
var
|
|
6480
|
+
var log11 = forComponent("Lessons");
|
|
6364
6481
|
var LessonOptionsDefaults = class {
|
|
6365
6482
|
minRepeats = 2;
|
|
6366
6483
|
};
|
|
@@ -6385,8 +6502,8 @@ function lessonCapture(options) {
|
|
|
6385
6502
|
counts.set(lesson.slug, n);
|
|
6386
6503
|
if (n < o.minRepeats) return;
|
|
6387
6504
|
written.add(lesson.slug);
|
|
6388
|
-
await writeFact(o.fs, o.dir, lesson.slug, lesson.body).catch((e) =>
|
|
6389
|
-
|
|
6505
|
+
await writeFact(o.fs, o.dir, lesson.slug, lesson.body).catch((e) => log11.warn(`could not persist ${lesson.slug}: ${e?.message ?? e}`));
|
|
6506
|
+
log11.debug(`captured lesson ${lesson.slug} (recurred ${n}\xD7)`);
|
|
6390
6507
|
}
|
|
6391
6508
|
};
|
|
6392
6509
|
}
|
|
@@ -6394,7 +6511,7 @@ function lessonCapture(options) {
|
|
|
6394
6511
|
// src/reflect.ts
|
|
6395
6512
|
init_llm();
|
|
6396
6513
|
init_logging();
|
|
6397
|
-
var
|
|
6514
|
+
var log12 = forComponent("Reflect");
|
|
6398
6515
|
async function reflectOnRun(o) {
|
|
6399
6516
|
const digest = digestRun(o.result.messages, o.maxDigestChars ?? 6e3);
|
|
6400
6517
|
if (!digest.trim()) return null;
|
|
@@ -6410,7 +6527,7 @@ If the run was fine or the issue was purely task-specific (not generalizable), r
|
|
|
6410
6527
|
const r = await o.ai.chat({ model: o.model, messages: [{ role: "user", content: prompt }], stream: false });
|
|
6411
6528
|
text = r?.content ?? "";
|
|
6412
6529
|
} catch (e) {
|
|
6413
|
-
|
|
6530
|
+
log12.warn(`reflection call failed: ${e?.message ?? e}`);
|
|
6414
6531
|
return null;
|
|
6415
6532
|
}
|
|
6416
6533
|
const m = text.match(/LESSON:\s*(.+)/i);
|
|
@@ -6420,10 +6537,10 @@ If the run was fine or the issue was purely task-specific (not generalizable), r
|
|
|
6420
6537
|
try {
|
|
6421
6538
|
await writeFact(o.fs, o.dir, slug2, lesson);
|
|
6422
6539
|
} catch (e) {
|
|
6423
|
-
|
|
6540
|
+
log12.warn(`could not persist lesson: ${e?.message ?? e}`);
|
|
6424
6541
|
return null;
|
|
6425
6542
|
}
|
|
6426
|
-
|
|
6543
|
+
log12.debug(`reflection persisted ${slug2}`);
|
|
6427
6544
|
return slug2;
|
|
6428
6545
|
}
|
|
6429
6546
|
function digestRun(messages, maxChars) {
|
|
@@ -6472,7 +6589,7 @@ function forComponent2(name) {
|
|
|
6472
6589
|
}
|
|
6473
6590
|
|
|
6474
6591
|
// node_modules/@bod.ee/voice/src/core/emotion.ts
|
|
6475
|
-
var
|
|
6592
|
+
var log13 = forComponent2("Emotion");
|
|
6476
6593
|
var EMOTIONS = [
|
|
6477
6594
|
// primary (best results)
|
|
6478
6595
|
"neutral",
|
|
@@ -6626,7 +6743,7 @@ var EmotionStream = class {
|
|
|
6626
6743
|
if (tag) {
|
|
6627
6744
|
this.pending = tag;
|
|
6628
6745
|
if (this.show) display += m[0];
|
|
6629
|
-
} else
|
|
6746
|
+
} else log13.debug(`dropping unknown emotion tag ${m[0]}`);
|
|
6630
6747
|
last = m.index + m[0].length;
|
|
6631
6748
|
}
|
|
6632
6749
|
this.emit(body.slice(last), (s, d, p) => {
|
|
@@ -6773,7 +6890,7 @@ function computeHoldMs(ctx) {
|
|
|
6773
6890
|
}
|
|
6774
6891
|
|
|
6775
6892
|
// node_modules/@bod.ee/voice/src/core/engine.ts
|
|
6776
|
-
var
|
|
6893
|
+
var log14 = forComponent2("VoiceEngine");
|
|
6777
6894
|
var realClock = {
|
|
6778
6895
|
now: () => performance.now(),
|
|
6779
6896
|
setTimeout: (fn, ms) => setTimeout(fn, ms),
|
|
@@ -7161,8 +7278,8 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
7161
7278
|
this.tts.warmup?.();
|
|
7162
7279
|
this.setState("listening");
|
|
7163
7280
|
if (this.options.bargeInMode === "aec" && !this.stt.usingAec)
|
|
7164
|
-
|
|
7165
|
-
|
|
7281
|
+
log14.warn("bargeInMode=aec but mic does not report echoCancellation applied \u2014 STT may transcribe the assistant");
|
|
7282
|
+
log14.debug(`voice I/O up (${this.usingAec ? "AEC" : "heuristic echo"} capture, mode=${this.options.bargeInMode}, mic reports aec=${this.stt.usingAec})`);
|
|
7166
7283
|
}
|
|
7167
7284
|
/** The RESOLVED barge-in tier (the single seam every tier branch reads). `bargeInMode` overrides
|
|
7168
7285
|
* the mic's self-report — see the option doc for why the report is not trustworthy on mobile. */
|
|
@@ -7187,7 +7304,7 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
7187
7304
|
this.options.onDiag({ t: this.clock.now(), kind, ...fields });
|
|
7188
7305
|
} catch (e) {
|
|
7189
7306
|
this.diagOn = false;
|
|
7190
|
-
|
|
7307
|
+
log14.debug(`onDiag threw \u2014 diagnostics disabled: ${e instanceof Error ? e.message : e}`);
|
|
7191
7308
|
}
|
|
7192
7309
|
}
|
|
7193
7310
|
idleWaiters = [];
|
|
@@ -7326,7 +7443,7 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
7326
7443
|
}
|
|
7327
7444
|
for (const w of this.words(this.reply)) this.echoWords.add(w);
|
|
7328
7445
|
this.tts.speak(forSpeech(speech), true);
|
|
7329
|
-
if (!this.spokeDeltas && this.turnStartAt)
|
|
7446
|
+
if (!this.spokeDeltas && this.turnStartAt) log14.debug(`ttft: ${Math.round(this.clock.now() - this.turnStartAt)}ms`);
|
|
7330
7447
|
this.spokeDeltas = true;
|
|
7331
7448
|
this.setState("speaking");
|
|
7332
7449
|
if (this.options.revealMode !== "off" && display) {
|
|
@@ -7439,13 +7556,13 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
7439
7556
|
this.drainTimer = null;
|
|
7440
7557
|
this.speaking = false;
|
|
7441
7558
|
this.stopWordReveal(true);
|
|
7442
|
-
if (this.turnStartAt)
|
|
7559
|
+
if (this.turnStartAt) log14.debug(`turn: ${Math.round(this.clock.now() - this.turnStartAt)}ms (incl. playback)`);
|
|
7443
7560
|
this.echoUntil = this.clock.now() + 2500;
|
|
7444
7561
|
if (!this.usingAec) this.stt.reset();
|
|
7445
7562
|
try {
|
|
7446
7563
|
this.options.onSpeechEnd();
|
|
7447
7564
|
} catch (e) {
|
|
7448
|
-
|
|
7565
|
+
log14.warn(`onSpeechEnd threw: ${e instanceof Error ? e.message : e}`);
|
|
7449
7566
|
}
|
|
7450
7567
|
this.setState("listening");
|
|
7451
7568
|
if (this.uttQueue.length) this.pumpQueue();
|
|
@@ -7519,7 +7636,7 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
7519
7636
|
if (!host) return pick;
|
|
7520
7637
|
const ctx = { text: this.lastDispatchText, question: this.lastDispatchWasQuestion, barged: this.bargedBeforeDispatch };
|
|
7521
7638
|
const fallback = (e) => {
|
|
7522
|
-
|
|
7639
|
+
log14.warn(`pickAck failed \u2014 using the default pools: ${e instanceof Error ? e.message : e}`);
|
|
7523
7640
|
pick.picked = false;
|
|
7524
7641
|
return void 0;
|
|
7525
7642
|
};
|
|
@@ -7609,7 +7726,7 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
7609
7726
|
this.ackProbe = null;
|
|
7610
7727
|
const queued = this.uttQueue.splice(0);
|
|
7611
7728
|
const droppedQueued = queued.length;
|
|
7612
|
-
if (droppedQueued)
|
|
7729
|
+
if (droppedQueued) log14.info(`barge-in cut ${droppedQueued} queued worker utterance(s) \u2014 handed to the host`);
|
|
7613
7730
|
this.overlapKeptUntil = 0;
|
|
7614
7731
|
if (!this.speaking && !this.drainTimer) {
|
|
7615
7732
|
if (droppedQueued) this.lastInterrupted = { full: "", heard: "", queued, queuedTurn: false };
|
|
@@ -7838,7 +7955,7 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
7838
7955
|
if (flat && this.lastDispatchFlat && this.clock.now() - this.lastDispatchAt < _VoiceEngine.DUP_FINAL_MS && (this.lastDispatchFlat.startsWith(flat) || flat.startsWith(this.lastDispatchFlat))) return;
|
|
7839
7956
|
this.specSpent = true;
|
|
7840
7957
|
this.specText = this.specPartial;
|
|
7841
|
-
|
|
7958
|
+
log14.debug(`speculate: "${this.specText.slice(0, 60)}"`);
|
|
7842
7959
|
this.diag("speculate", { text: this.specText });
|
|
7843
7960
|
this.options.onSpeculate(this.specText);
|
|
7844
7961
|
}, this.options.speculativeMs);
|
|
@@ -7907,7 +8024,7 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
7907
8024
|
};
|
|
7908
8025
|
this.tts.newContext();
|
|
7909
8026
|
this.tts.speak(phrase, false);
|
|
7910
|
-
|
|
8027
|
+
log14.debug(`backchannel: "${phrase}"`);
|
|
7911
8028
|
this.diag("backchannel", { phrase });
|
|
7912
8029
|
this.options.onBackchannel(phrase);
|
|
7913
8030
|
}
|
|
@@ -7940,7 +8057,7 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
7940
8057
|
});
|
|
7941
8058
|
if (idx < 0) return text;
|
|
7942
8059
|
toks.splice(idx, 1);
|
|
7943
|
-
|
|
8060
|
+
log14.verbose(`stripped backchannel echo "${this.lastBcPhrase}" from dispatching final`);
|
|
7944
8061
|
return toks.join(" ");
|
|
7945
8062
|
}
|
|
7946
8063
|
/** Merge a resumed utterance into the pending one, deduping any word-overlap. Soniox re-finalizes
|
|
@@ -8126,7 +8243,7 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
8126
8243
|
}
|
|
8127
8244
|
const bargeHold = !this.looksIncomplete(this.pendingUtt) && this.unfinishedBargeLine(this.pendingUtt);
|
|
8128
8245
|
if (this.options.incompleteMergeMs && (bargeHold || this.looksIncomplete(this.pendingUtt))) {
|
|
8129
|
-
|
|
8246
|
+
log14.verbose(`hold: incomplete utterance "${this.pendingUtt.slice(-40)}"`);
|
|
8130
8247
|
this.diag("hold", { reason: bargeHold ? "barge_unfinished" : "incomplete", tail: this.pendingUtt.slice(-40) });
|
|
8131
8248
|
this.options.onHold();
|
|
8132
8249
|
if (this.options.holdFiller && !this.speaking) this.speakFiller(this.options.holdFiller);
|
|
@@ -8165,13 +8282,13 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
8165
8282
|
const answered = this.answeredSinceDispatch();
|
|
8166
8283
|
const part = flat !== this.lastDispatchFlat && !answered && this.echoesDispatch(text);
|
|
8167
8284
|
if (part || flat && flat === this.lastDispatchFlat && !answered && this.clock.now() - this.lastDispatchAt < _VoiceEngine.DUP_FINAL_MS) {
|
|
8168
|
-
|
|
8285
|
+
log14.verbose(`dropped duplicate final "${text.slice(0, 40)}" (identical to the just-dispatched utterance, no reply between)`);
|
|
8169
8286
|
this.diag("utterance_drop", { reason: "dup_final", text: text.slice(0, 60) });
|
|
8170
8287
|
return;
|
|
8171
8288
|
}
|
|
8172
8289
|
const refinal = this.refinalizes(text);
|
|
8173
8290
|
if (refinal && this.repliedSinceDispatch) {
|
|
8174
|
-
|
|
8291
|
+
log14.verbose(`dropped re-finalization "${text.slice(0, 60)}" (superset of the just-dispatched utterance, reply already flowing)`);
|
|
8175
8292
|
this.diag("utterance_drop", { reason: "refinalized", text: text.slice(0, 60) });
|
|
8176
8293
|
this.lastDispatchFlat = flat;
|
|
8177
8294
|
this.lastDispatchWords = text.trim().split(/\s+/);
|
|
@@ -8450,7 +8567,7 @@ var SpokenSplitter = class {
|
|
|
8450
8567
|
};
|
|
8451
8568
|
|
|
8452
8569
|
// node_modules/@bod.ee/voice/src/adapters/soniox.ts
|
|
8453
|
-
var
|
|
8570
|
+
var log15 = forComponent2("SonioxSTT");
|
|
8454
8571
|
var now = () => performance.now();
|
|
8455
8572
|
var SonioxSTTOptions = class {
|
|
8456
8573
|
auth = "";
|
|
@@ -8495,7 +8612,7 @@ var SonioxSTT = class {
|
|
|
8495
8612
|
this.onDiag({ t: now(), kind, ...fields });
|
|
8496
8613
|
} catch (e) {
|
|
8497
8614
|
this.diagOn = false;
|
|
8498
|
-
|
|
8615
|
+
log15.debug(`onDiag threw \u2014 STT diagnostics disabled: ${e instanceof Error ? e.message : e}`);
|
|
8499
8616
|
}
|
|
8500
8617
|
}
|
|
8501
8618
|
lastChunkAt = 0;
|
|
@@ -8537,11 +8654,11 @@ var SonioxSTT = class {
|
|
|
8537
8654
|
this.ws.onmessage = (ev) => this.handle(JSON.parse(String(ev.data)));
|
|
8538
8655
|
this.ws.onclose = (ev) => {
|
|
8539
8656
|
if (this.stopped) return;
|
|
8540
|
-
|
|
8657
|
+
log15.warn(`soniox ws closed (${ev.code} ${ev.reason || ""}) \u2014 reconnecting`);
|
|
8541
8658
|
this.diag("stt_ws_closed", { code: ev.code, reason: String(ev.reason || ""), reconnecting: true });
|
|
8542
8659
|
this.reset();
|
|
8543
8660
|
this.connectWs().catch((e) => {
|
|
8544
|
-
|
|
8661
|
+
log15.error(`soniox reconnect failed: ${e.message}`);
|
|
8545
8662
|
this.diag("stt_reconnect_failed", { message: e.message });
|
|
8546
8663
|
});
|
|
8547
8664
|
};
|
|
@@ -8553,7 +8670,7 @@ var SonioxSTT = class {
|
|
|
8553
8670
|
this.endpointTimer = setInterval(() => {
|
|
8554
8671
|
const combined = (this.finalText + this.partialText).trim();
|
|
8555
8672
|
if (!combined || now() - this.lastChangeAt < this.options.silenceEndpointMs) return;
|
|
8556
|
-
if (this.firstTokenAt)
|
|
8673
|
+
if (this.firstTokenAt) log15.debug(`stt: ${Math.round(now() - this.firstTokenAt)}ms first-token\u2192silence-endpoint, "${combined.slice(0, 60)}"`);
|
|
8557
8674
|
this.reset();
|
|
8558
8675
|
this.onUtterance(combined, now());
|
|
8559
8676
|
}, 120);
|
|
@@ -8565,7 +8682,7 @@ var SonioxSTT = class {
|
|
|
8565
8682
|
if (this.stopped) return;
|
|
8566
8683
|
const ref = this.lastChunkAt || this.startedChunksAt;
|
|
8567
8684
|
if (now() - ref > noAudioMs) {
|
|
8568
|
-
|
|
8685
|
+
log15.error(`stt: no mic audio for >${Math.round(noAudioMs / 1e3)}s \u2014 capture device stopped delivering`);
|
|
8569
8686
|
this.diag("stt_watchdog_fatal", { noAudioMs });
|
|
8570
8687
|
this.onFatal("microphone stopped delivering audio (try a different input device, e.g. AirPods, or check System Settings \u2192 Sound \u2192 Input)");
|
|
8571
8688
|
this.stop();
|
|
@@ -8588,7 +8705,7 @@ var SonioxSTT = class {
|
|
|
8588
8705
|
handle(m) {
|
|
8589
8706
|
if (m.error_message) {
|
|
8590
8707
|
this.diag("stt_error", { message: String(m.error_message), code: m.error_code });
|
|
8591
|
-
return
|
|
8708
|
+
return log15.error(`soniox: ${m.error_message}`);
|
|
8592
8709
|
}
|
|
8593
8710
|
let endpoint = false;
|
|
8594
8711
|
for (const t of m.tokens ?? []) {
|
|
@@ -8605,7 +8722,7 @@ var SonioxSTT = class {
|
|
|
8605
8722
|
this.onPartial(combined);
|
|
8606
8723
|
if (endpoint && this.finalText.trim()) {
|
|
8607
8724
|
const utterance = this.finalText.trim();
|
|
8608
|
-
if (this.firstTokenAt)
|
|
8725
|
+
if (this.firstTokenAt) log15.debug(`stt: ${Math.round(now() - this.firstTokenAt)}ms first-token\u2192endpoint, "${utterance.slice(0, 60)}"`);
|
|
8609
8726
|
this.reset();
|
|
8610
8727
|
this.onUtterance(utterance, now());
|
|
8611
8728
|
}
|
|
@@ -8627,7 +8744,7 @@ var SonioxSTT = class {
|
|
|
8627
8744
|
};
|
|
8628
8745
|
|
|
8629
8746
|
// node_modules/@bod.ee/voice/src/adapters/cartesia.ts
|
|
8630
|
-
var
|
|
8747
|
+
var log16 = forComponent2("CartesiaTTS");
|
|
8631
8748
|
var now2 = () => performance.now();
|
|
8632
8749
|
var CartesiaTTSOptions = class {
|
|
8633
8750
|
auth = "";
|
|
@@ -8663,7 +8780,7 @@ var CartesiaTTS = class _CartesiaTTS {
|
|
|
8663
8780
|
this.onDiag({ t: now2(), kind, ...fields });
|
|
8664
8781
|
} catch (e) {
|
|
8665
8782
|
this.diagOn = false;
|
|
8666
|
-
|
|
8783
|
+
log16.debug(`onDiag threw \u2014 TTS diagnostics disabled: ${e instanceof Error ? e.message : e}`);
|
|
8667
8784
|
}
|
|
8668
8785
|
}
|
|
8669
8786
|
firstAudioAt = 0;
|
|
@@ -8698,11 +8815,11 @@ var CartesiaTTS = class _CartesiaTTS {
|
|
|
8698
8815
|
this.ws.onerror = (e) => rej(new Error(`cartesia ws: ${e.message || "connect failed"}`));
|
|
8699
8816
|
});
|
|
8700
8817
|
this.ws.onclose = (ev) => {
|
|
8701
|
-
|
|
8818
|
+
log16.warn(`cartesia ws closed (${ev.code} ${ev.reason || ""})`);
|
|
8702
8819
|
this.diag("tts_ws_closed", { code: ev.code, reason: String(ev.reason || ""), reconnecting: !this.closed });
|
|
8703
8820
|
if (!this.closed) {
|
|
8704
8821
|
this.connecting = this.doConnect().catch((e) => {
|
|
8705
|
-
|
|
8822
|
+
log16.error(`cartesia reconnect failed: ${e.message}`);
|
|
8706
8823
|
this.diag("tts_reconnect_failed", { message: e.message });
|
|
8707
8824
|
});
|
|
8708
8825
|
}
|
|
@@ -8730,12 +8847,12 @@ var CartesiaTTS = class _CartesiaTTS {
|
|
|
8730
8847
|
this.down = true;
|
|
8731
8848
|
this.downAt = now2();
|
|
8732
8849
|
this.consecutiveOk = 0;
|
|
8733
|
-
|
|
8850
|
+
log16.warn(`TTS circuit breaker open \u2014 ${this.consecutiveErrors} consecutive errors, switching to text-only`);
|
|
8734
8851
|
this.diag("tts_breaker_open", { errors: this.consecutiveErrors });
|
|
8735
8852
|
this.onDone();
|
|
8736
8853
|
this.startProbe();
|
|
8737
8854
|
} else if (!this.down) {
|
|
8738
|
-
(/No valid transcripts/i.test(m.message || "") ?
|
|
8855
|
+
(/No valid transcripts/i.test(m.message || "") ? log16.debug : log16.warn)(`cartesia: ${JSON.stringify(m)}`);
|
|
8739
8856
|
}
|
|
8740
8857
|
}
|
|
8741
8858
|
};
|
|
@@ -8750,7 +8867,7 @@ var CartesiaTTS = class _CartesiaTTS {
|
|
|
8750
8867
|
this.stopProbe();
|
|
8751
8868
|
const downMs = this.downAt ? now2() - this.downAt : 0;
|
|
8752
8869
|
this.diag("tts_breaker_close", { downMs: Math.round(downMs) });
|
|
8753
|
-
(downMs < 2e3 ?
|
|
8870
|
+
(downMs < 2e3 ? log16.debug : log16.info)(`TTS recovered${downMs ? ` (down ${downMs}ms)` : ""}`);
|
|
8754
8871
|
}
|
|
8755
8872
|
/** Ensure the WS is open before sending — reconnects if idle-closed. */
|
|
8756
8873
|
async ensureConnected() {
|
|
@@ -8833,13 +8950,13 @@ function base64ToBytes(b64) {
|
|
|
8833
8950
|
}
|
|
8834
8951
|
|
|
8835
8952
|
// node_modules/@bod.ee/voice/src/adapters/fish.ts
|
|
8836
|
-
var
|
|
8953
|
+
var log17 = forComponent2("FishTTS");
|
|
8837
8954
|
|
|
8838
8955
|
// node_modules/@bod.ee/voice/src/adapters/grok.ts
|
|
8839
|
-
var
|
|
8956
|
+
var log18 = forComponent2("GrokTTS");
|
|
8840
8957
|
|
|
8841
8958
|
// node_modules/@bod.ee/voice/src/adapters/grok-stt.ts
|
|
8842
|
-
var
|
|
8959
|
+
var log19 = forComponent2("GrokSTT");
|
|
8843
8960
|
|
|
8844
8961
|
// src/voice/engine.ts
|
|
8845
8962
|
init_logging();
|
|
@@ -8851,7 +8968,7 @@ configureLogging((name) => {
|
|
|
8851
8968
|
|
|
8852
8969
|
// src/voice/instinct.ts
|
|
8853
8970
|
init_logging();
|
|
8854
|
-
var
|
|
8971
|
+
var log20 = forComponent("Instinct");
|
|
8855
8972
|
var ACK_SITUATIONS = {
|
|
8856
8973
|
hard_question: `a question that takes real thought, judgment or looking something up ("what's the biggest takeaway?", "how should I structure this?")`,
|
|
8857
8974
|
simple_question: 'a quick question with an instant, obvious answer ("how are you?", "what time is it?", "can you hear me?")',
|
|
@@ -8890,7 +9007,7 @@ function interruptionNote(cut, intent) {
|
|
|
8890
9007
|
|
|
8891
9008
|
// src/voice/unsaid.ts
|
|
8892
9009
|
init_logging();
|
|
8893
|
-
var
|
|
9010
|
+
var log21 = forComponent("Unsaid");
|
|
8894
9011
|
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(" "));
|
|
8895
9012
|
var contentStems = (s) => (s.toLowerCase().match(/[a-z0-9']+/g) ?? []).filter((w) => w.length >= 4 && !STOP2.has(w)).map((w) => w.slice(0, 4));
|
|
8896
9013
|
var SAID_OVERLAP = 0.6;
|
|
@@ -8930,7 +9047,7 @@ async function unsaidClauses(pieces, spoken, decider, timeoutMs = 5e3) {
|
|
|
8930
9047
|
return p < CONVEYED_MIN;
|
|
8931
9048
|
}).map((x) => x.c);
|
|
8932
9049
|
} catch (e) {
|
|
8933
|
-
|
|
9050
|
+
log21.warn(`unsaid judge failed \u2014 only near-zero-overlap clauses count as unsaid: ${e.message}`);
|
|
8934
9051
|
return fallback();
|
|
8935
9052
|
} finally {
|
|
8936
9053
|
clearTimeout(timer);
|
|
@@ -9001,7 +9118,7 @@ async function judgeLeak(decider, sentence, userSaid) {
|
|
|
9001
9118
|
}
|
|
9002
9119
|
|
|
9003
9120
|
// src/duplex.ts
|
|
9004
|
-
var
|
|
9121
|
+
var log22 = forComponent("DuplexAgent");
|
|
9005
9122
|
function describeCall(call) {
|
|
9006
9123
|
const v = call.args && Object.values(call.args).find((x) => typeof x === "string" && x.trim());
|
|
9007
9124
|
const hint = v ? ` (${String(v).replace(/\s+/g, " ").trim().slice(0, 48)})` : "";
|
|
@@ -9261,7 +9378,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
9261
9378
|
const m = this.reflexBuf.match(RESERVED_EVENT_MARKER) ?? this.reflexBuf.match(RESERVED_EVENT_OPENER);
|
|
9262
9379
|
if (m) {
|
|
9263
9380
|
this.fabricationCut = true;
|
|
9264
|
-
|
|
9381
|
+
log22.warn(`reflex fabricated a [task \u2026] event in its spoken stream \u2014 cutting it (kept ${m.index} chars)`);
|
|
9265
9382
|
this.reflexBuf = this.reflexBuf.slice(0, Math.max(m.index, this.reflexForwarded));
|
|
9266
9383
|
this.pumpReflex(true);
|
|
9267
9384
|
return;
|
|
@@ -9353,7 +9470,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
9353
9470
|
const at = from + ok;
|
|
9354
9471
|
this.leakHit = true;
|
|
9355
9472
|
this.leakCuts.push(leak.trim().slice(0, 40));
|
|
9356
|
-
|
|
9473
|
+
log22.warn(`reflex spoke its own analysis ("${leak.trim().slice(0, 40)}\u2026") \u2014 cut before TTS`);
|
|
9357
9474
|
this.notify("diag", "analysis_leak", { text: leak.trim().slice(0, 80), at: this.turnSpoken.trim() ? "mid_reply" : "before_speech" });
|
|
9358
9475
|
const next = this.stepMarks.find((k) => k > at);
|
|
9359
9476
|
if (next === void 0) {
|
|
@@ -9393,7 +9510,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
9393
9510
|
this.notify("diag", "leak_judge", { text: s.slice(0, 80), p: Math.round(p * 100) / 100, ms: Math.round(performance.now() - t0) });
|
|
9394
9511
|
return p >= LEAK_JUDGE_THRESHOLD;
|
|
9395
9512
|
}, (e) => {
|
|
9396
|
-
|
|
9513
|
+
log22.warn(`leak judge failed \u2014 speaking the sentence: ${e.message}`);
|
|
9397
9514
|
return false;
|
|
9398
9515
|
}).then((leak) => {
|
|
9399
9516
|
clearTimeout(timer);
|
|
@@ -9427,7 +9544,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
9427
9544
|
let i = t.length - 1;
|
|
9428
9545
|
while (i >= this.turnBase && !(t[i].role === "assistant" && typeof t[i].content === "string" && t[i].content.includes(key))) i--;
|
|
9429
9546
|
if (i < this.turnBase) {
|
|
9430
|
-
|
|
9547
|
+
log22.warn("analysis leak cut, but its text was not found in the transcript to scrub");
|
|
9431
9548
|
continue;
|
|
9432
9549
|
}
|
|
9433
9550
|
const m = t[i];
|
|
@@ -9507,7 +9624,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
9507
9624
|
return {
|
|
9508
9625
|
preToolUse: (call) => {
|
|
9509
9626
|
if (this.spec?.state === "pending" && !_DuplexAgent.SPEC_SAFE_TOOLS.has(call.name)) {
|
|
9510
|
-
|
|
9627
|
+
log22.verbose(`speculation aborted: reflex called ${call.name} on unconfirmed input`);
|
|
9511
9628
|
this.abortSpeculation();
|
|
9512
9629
|
return { block: true, reason: "Speculative turn aborted." };
|
|
9513
9630
|
}
|
|
@@ -9552,7 +9669,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
9552
9669
|
try {
|
|
9553
9670
|
await this.voice.send(reminder);
|
|
9554
9671
|
} catch (e) {
|
|
9555
|
-
|
|
9672
|
+
log22.warn(`ack nudge failed: ${e instanceof Error ? e.message : e}`);
|
|
9556
9673
|
} finally {
|
|
9557
9674
|
this.nudging = false;
|
|
9558
9675
|
const i = this.voice.transcript.findIndex((m, k) => k >= base && m.role === "user" && m.content === reminder);
|
|
@@ -9604,7 +9721,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
9604
9721
|
const accepted = this.offerOpen && this.parkedRedeliver.length > 0 && this.parkedRedeliver.every((p) => p.task === this.offer.task) && (isOfferAcceptance(content) || isTrivialBarge(content));
|
|
9605
9722
|
const judge = accepted ? void 0 : this.options.bargeIntent;
|
|
9606
9723
|
return (accepted ? Promise.resolve("resume") : judge ? judge(content).catch((e) => {
|
|
9607
|
-
|
|
9724
|
+
log22.warn(`bargeIntent judge failed \u2014 using the allowlist: ${e.message}`);
|
|
9608
9725
|
return fallback();
|
|
9609
9726
|
}) : Promise.resolve(fallback())).then((intent) => {
|
|
9610
9727
|
this.notify("diag", "barge_intent", { intent, text: content.slice(0, 80) });
|
|
@@ -9800,7 +9917,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
9800
9917
|
try {
|
|
9801
9918
|
res = await this.voice.send(spec.text);
|
|
9802
9919
|
} catch (e) {
|
|
9803
|
-
|
|
9920
|
+
log22.warn(`speculative turn failed: ${e instanceof Error ? e.message : e}`);
|
|
9804
9921
|
} finally {
|
|
9805
9922
|
this.voice.options.signal = prevSignal;
|
|
9806
9923
|
}
|
|
@@ -9814,7 +9931,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
9814
9931
|
if (d === "abort") {
|
|
9815
9932
|
if (this.voice.transcript.length > base) this.voice.transcript.length = base;
|
|
9816
9933
|
this.speculativeAbortedCalls++;
|
|
9817
|
-
|
|
9934
|
+
log22.verbose(`speculation aborted (${this.speculativeAbortedCalls} total): "${spec.text.slice(0, 50)}"`);
|
|
9818
9935
|
if (this.spec === spec) this.spec = void 0;
|
|
9819
9936
|
return res ?? empty;
|
|
9820
9937
|
}
|
|
@@ -10164,7 +10281,7 @@ Another agent just implemented the above. Independently check the CURRENT state
|
|
|
10164
10281
|
this.notify("task_verify", `task ${id}: verifying`, { id });
|
|
10165
10282
|
const cres = await new Agent(checkerOpts).run(checkBrief);
|
|
10166
10283
|
if (cres.finishReason !== "stop") {
|
|
10167
|
-
|
|
10284
|
+
log22.warn(`task ${id}: verify inconclusive (${cres.finishReason})`);
|
|
10168
10285
|
this.notify("task_verify", `task ${id}: verify inconclusive (${cres.finishReason})`, { id, finishReason: cres.finishReason });
|
|
10169
10286
|
}
|
|
10170
10287
|
const sum = (a = 0, b = 0) => a + b;
|
|
@@ -10300,7 +10417,7 @@ Another agent just implemented the above. Independently check the CURRENT state
|
|
|
10300
10417
|
rec.status = "done";
|
|
10301
10418
|
rec.result = res.text;
|
|
10302
10419
|
const incomplete = res.finishReason !== "stop" && res.finishReason !== "empty";
|
|
10303
|
-
|
|
10420
|
+
log22.verbose(`task ${id} done (${res.steps} steps${incomplete ? `, INCOMPLETE: ${res.finishReason}` : ""})`);
|
|
10304
10421
|
this.notify("task_done", `task ${id} (${rec.label}) completed`, {
|
|
10305
10422
|
id,
|
|
10306
10423
|
text: res.text,
|
|
@@ -10334,14 +10451,14 @@ Another agent just implemented the above. Independently check the CURRENT state
|
|
|
10334
10451
|
foldIfSuperseded(rec, note) {
|
|
10335
10452
|
if (rec.deliveryParked !== "moved_on") return false;
|
|
10336
10453
|
if (note.trim()) this.voice.transcript.push({ role: "assistant", content: note });
|
|
10337
|
-
|
|
10454
|
+
log22.verbose(`task ${rec.id} settled but superseded (${rec.status}) \u2014 folded into transcript, not voiced`);
|
|
10338
10455
|
return true;
|
|
10339
10456
|
}
|
|
10340
10457
|
failTask(rec, msg) {
|
|
10341
10458
|
this.dropAsk(rec.id);
|
|
10342
10459
|
rec.status = "error";
|
|
10343
10460
|
rec.result = msg;
|
|
10344
|
-
|
|
10461
|
+
log22.warn(`task ${rec.id} failed: ${msg}`);
|
|
10345
10462
|
this.notify("task_error", `task ${rec.id} (${rec.label}) failed: ${msg}`, { id: rec.id });
|
|
10346
10463
|
if (this.foldIfSuperseded(rec, `The "${rec.label}" task failed: ${msg}`)) return;
|
|
10347
10464
|
this.dropParked(rec);
|
|
@@ -10580,7 +10697,7 @@ Another agent just implemented the above. Independently check the CURRENT state
|
|
|
10580
10697
|
// src/modelRouter.ts
|
|
10581
10698
|
init_llm();
|
|
10582
10699
|
init_logging();
|
|
10583
|
-
var
|
|
10700
|
+
var log23 = forComponent("ModelRouter");
|
|
10584
10701
|
var ModelRouterOptions = class {
|
|
10585
10702
|
classifier = jevClassifier();
|
|
10586
10703
|
/** Capability tiers → model id (any provider). basic/standard run the main session; premium is only ever an offload. */
|
|
@@ -10664,10 +10781,10 @@ var ModelRouter = class {
|
|
|
10664
10781
|
if (tier === "premium") d.offload = this.state.offload && (c.sameThread ?? 0) >= o.sameThreadAt ? "continue" : "fresh";
|
|
10665
10782
|
} catch (e) {
|
|
10666
10783
|
const msg = e instanceof Error ? e.message : String(e);
|
|
10667
|
-
|
|
10784
|
+
log23.warn(`classifier failed, falling back to standard: ${msg}`);
|
|
10668
10785
|
d = { tier: "standard", model: o.models.standard, probs: {}, fallback: msg, classifyMs: Math.round(performance.now() - t0) };
|
|
10669
10786
|
}
|
|
10670
|
-
|
|
10787
|
+
log23.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`);
|
|
10671
10788
|
return d;
|
|
10672
10789
|
}
|
|
10673
10790
|
/** Run one user turn on `agent` with routing. Returns a RunResult carrying `costUsd` + `routing`. */
|
|
@@ -10719,7 +10836,7 @@ ${text}`;
|
|
|
10719
10836
|
const answer = res.text || `(premium sub-agent finished with no reply; finishReason=${res.finishReason})`;
|
|
10720
10837
|
agent.transcript.push({ role: "user", content }, { role: "assistant", content: answer });
|
|
10721
10838
|
this.state.offload = { messages: child.transcript, mainLen: agent.transcript.length, question: text };
|
|
10722
|
-
|
|
10839
|
+
log23.info(`offload ${cont ? "continue" : "fresh"} done \xB7 ${res.finishReason} \xB7 ${res.steps} steps`);
|
|
10723
10840
|
const costUsd = o.costOf(main, briefUsage) + o.costOf(o.models.premium, res.usage);
|
|
10724
10841
|
return { ...res, text: answer, messages: agent.transcript, usage: addUsage(briefUsage, res.usage), costUsd, routing: d };
|
|
10725
10842
|
}
|
|
@@ -10735,7 +10852,7 @@ The request:
|
|
|
10735
10852
|
${text}`);
|
|
10736
10853
|
return { text: r.text.trim() || text, usage: r.usage };
|
|
10737
10854
|
} catch (e) {
|
|
10738
|
-
|
|
10855
|
+
log23.warn(`brief failed, sending the raw request: ${e instanceof Error ? e.message : String(e)}`);
|
|
10739
10856
|
return { text };
|
|
10740
10857
|
} finally {
|
|
10741
10858
|
agent.transcript = snap;
|
|
@@ -10767,8 +10884,8 @@ async function loadJev(opts) {
|
|
|
10767
10884
|
const env = { ...process.env };
|
|
10768
10885
|
const file = opts.envFile ?? process.env.AGENTX_JEV_ENV;
|
|
10769
10886
|
if (file) {
|
|
10770
|
-
const { readFileSync:
|
|
10771
|
-
for (const l of
|
|
10887
|
+
const { readFileSync: readFileSync4 } = await import("fs");
|
|
10888
|
+
for (const l of readFileSync4(file, "utf8").split("\n")) {
|
|
10772
10889
|
const m = l.match(/^\s*([A-Z0-9_]+)\s*=\s*(.*?)\s*$/);
|
|
10773
10890
|
if (m && !env[m[1]]) env[m[1]] = m[2].replace(/^['"]|['"]$/g, "");
|
|
10774
10891
|
}
|
|
@@ -10780,7 +10897,7 @@ async function jevAvailable(opts = {}) {
|
|
|
10780
10897
|
const { env } = await loadJev(opts);
|
|
10781
10898
|
return JEV_KEYS.some((k) => env[k]?.trim());
|
|
10782
10899
|
} catch (e) {
|
|
10783
|
-
|
|
10900
|
+
log23.debug(`jev unavailable: ${e instanceof Error ? e.message : String(e)}`);
|
|
10784
10901
|
return false;
|
|
10785
10902
|
}
|
|
10786
10903
|
}
|