@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/cli.js
CHANGED
|
@@ -19,8 +19,54 @@ var init_logging = __esm({
|
|
|
19
19
|
}
|
|
20
20
|
});
|
|
21
21
|
|
|
22
|
+
// src/imageWire.ts
|
|
23
|
+
import { readFileSync, statSync as statSync2 } from "fs";
|
|
24
|
+
import { imageWireOf, inlineImageForWire, scrubBase64Text, imageSize, imageNote, IMAGE_MAX_SIDE, IMAGE_MAX_BYTES } from "ai.libx.js";
|
|
25
|
+
function remember(id, w) {
|
|
26
|
+
cache.delete(id);
|
|
27
|
+
cache.set(id, w);
|
|
28
|
+
if (cache.size > CACHE_MAX) cache.delete(cache.keys().next().value);
|
|
29
|
+
return w;
|
|
30
|
+
}
|
|
31
|
+
function imageForWire(path, mime, vision) {
|
|
32
|
+
let st;
|
|
33
|
+
try {
|
|
34
|
+
st = statSync2(path);
|
|
35
|
+
} catch (e) {
|
|
36
|
+
log2.warn(`image unreadable: ${path}`, e);
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
const key = `${st.mtimeMs}:${st.size}`;
|
|
40
|
+
let hit = cache.get(path);
|
|
41
|
+
if (!hit || hit.key !== key) hit = remember(path, { key, ...imageWireOf(readFileSync(path), mime, path, path) });
|
|
42
|
+
return vision && hit.url ? { url: hit.url } : { note: hit.note };
|
|
43
|
+
}
|
|
44
|
+
function imageTokens(dims) {
|
|
45
|
+
return dims ? Math.min(IMAGE_TOKENS_MAX, Math.max(1, Math.ceil(dims.w * dims.h / 750))) : IMAGE_TOKENS_MAX;
|
|
46
|
+
}
|
|
47
|
+
function wiredImageWeight(r) {
|
|
48
|
+
if ("note" in r) return r.note.length;
|
|
49
|
+
const b64 = r.url.slice(r.url.indexOf(",") + 1);
|
|
50
|
+
return 4 * imageTokens(imageSize(Buffer.from(b64.slice(0, 4096), "base64")));
|
|
51
|
+
}
|
|
52
|
+
function imageWireWeight(path, mime, vision) {
|
|
53
|
+
const r = imageForWire(path, mime, vision);
|
|
54
|
+
return r ? wiredImageWeight(r) : 0;
|
|
55
|
+
}
|
|
56
|
+
var log2, cache, CACHE_MAX, IMAGE_TOKENS_MAX;
|
|
57
|
+
var init_imageWire = __esm({
|
|
58
|
+
"src/imageWire.ts"() {
|
|
59
|
+
"use strict";
|
|
60
|
+
init_logging();
|
|
61
|
+
log2 = forComponent("imageWire");
|
|
62
|
+
cache = /* @__PURE__ */ new Map();
|
|
63
|
+
CACHE_MAX = 8;
|
|
64
|
+
IMAGE_TOKENS_MAX = 1600;
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
|
|
22
68
|
// src/llm.ts
|
|
23
|
-
import {
|
|
69
|
+
import { statSync as statSync3, realpathSync } from "fs";
|
|
24
70
|
import { resolve, sep } from "path";
|
|
25
71
|
import { randomBytes } from "crypto";
|
|
26
72
|
function passesBackReasoning(model) {
|
|
@@ -77,9 +123,9 @@ function mintImageRef(path, mime) {
|
|
|
77
123
|
const token = randomBytes(16).toString("hex");
|
|
78
124
|
let bytes = 0;
|
|
79
125
|
try {
|
|
80
|
-
bytes = Math.ceil(
|
|
126
|
+
bytes = Math.ceil(statSync3(path).size * 4 / 3);
|
|
81
127
|
} catch (e) {
|
|
82
|
-
|
|
128
|
+
log3.warn(`image ref stat failed, weighing as 0: ${path}`, e);
|
|
83
129
|
}
|
|
84
130
|
refRegistry.set(token, { mime, path, bytes });
|
|
85
131
|
while (refRegistry.size > REF_REGISTRY_MAX) refRegistry.delete(refRegistry.keys().next().value);
|
|
@@ -92,7 +138,7 @@ function parseImageRef(url) {
|
|
|
92
138
|
const parts = refUrlParts(url);
|
|
93
139
|
const ref = parts && refRegistry.get(parts.token);
|
|
94
140
|
if (!ref) {
|
|
95
|
-
|
|
141
|
+
log3.debug("image ref not in registry \u2014 treating as non-image text");
|
|
96
142
|
return null;
|
|
97
143
|
}
|
|
98
144
|
return ref;
|
|
@@ -111,7 +157,7 @@ function remintImageRefs(messages, roots) {
|
|
|
111
157
|
try {
|
|
112
158
|
allowed.push(realpathSync(resolve(r)));
|
|
113
159
|
} catch (e) {
|
|
114
|
-
|
|
160
|
+
log3.debug(`image-ref root unavailable: ${r}`, e);
|
|
115
161
|
}
|
|
116
162
|
}
|
|
117
163
|
if (allowed.length === 0) return messages;
|
|
@@ -128,11 +174,11 @@ function remintImageRefs(messages, roots) {
|
|
|
128
174
|
try {
|
|
129
175
|
real = realpathSync(f.path);
|
|
130
176
|
} catch (e) {
|
|
131
|
-
|
|
177
|
+
log3.warn(`resumed image ref unreadable, will stub: ${f.path}`, e);
|
|
132
178
|
return p;
|
|
133
179
|
}
|
|
134
180
|
if (!inside(real)) {
|
|
135
|
-
|
|
181
|
+
log3.warn(`resumed image ref outside the allowed roots, will stub: ${f.path} -> ${real}`);
|
|
136
182
|
return p;
|
|
137
183
|
}
|
|
138
184
|
touched = true;
|
|
@@ -159,7 +205,7 @@ function toolImageRef(content) {
|
|
|
159
205
|
try {
|
|
160
206
|
return parseImageRef(JSON.parse(content).imageRef);
|
|
161
207
|
} catch (e) {
|
|
162
|
-
|
|
208
|
+
log3.debug("bad imageRef tool result", e);
|
|
163
209
|
return null;
|
|
164
210
|
}
|
|
165
211
|
}
|
|
@@ -187,32 +233,41 @@ function imageWindows(messages, keepRecent) {
|
|
|
187
233
|
const k = Math.max(0, keepRecent);
|
|
188
234
|
return /* @__PURE__ */ new Set([...attach.slice(Math.max(0, attach.length - k)), ...tool.slice(Math.max(0, tool.length - k))]);
|
|
189
235
|
}
|
|
190
|
-
function expandImagesForSend(messages, keepRecent = 1, stub = IMAGE_ELIDE_STUB) {
|
|
236
|
+
function expandImagesForSend(messages, keepRecent = 1, stub = IMAGE_ELIDE_STUB, vision = true) {
|
|
191
237
|
if (!messages.some(bearsImage)) return messages;
|
|
192
238
|
const keep = imageWindows(messages, keepRecent);
|
|
193
239
|
let changed = false;
|
|
194
240
|
const out = messages.map((m, i) => {
|
|
195
|
-
const content = keep.has(i) ? expandContent(m.content,
|
|
241
|
+
const content = keep.has(i) ? expandContent(m.content, vision) : stripContent(m.content, stub);
|
|
196
242
|
if (content === m.content) return m;
|
|
197
243
|
changed = true;
|
|
198
244
|
return { ...m, content };
|
|
199
245
|
});
|
|
200
246
|
return changed ? out : messages;
|
|
201
247
|
}
|
|
202
|
-
function sendBytes(messages, keepRecent = 1, stub = IMAGE_ELIDE_STUB) {
|
|
248
|
+
function sendBytes(messages, keepRecent = 1, vision = true, stub = IMAGE_ELIDE_STUB) {
|
|
203
249
|
let n = 0;
|
|
204
|
-
for (const b of sendBytesPer(messages, keepRecent, stub)) n += b;
|
|
250
|
+
for (const b of sendBytesPer(messages, keepRecent, vision, stub)) n += b;
|
|
205
251
|
return n;
|
|
206
252
|
}
|
|
207
|
-
function sendBytesPer(messages, keepRecent = 1, stub = IMAGE_ELIDE_STUB) {
|
|
253
|
+
function sendBytesPer(messages, keepRecent = 1, vision = true, stub = IMAGE_ELIDE_STUB) {
|
|
208
254
|
const keep = messages.some(bearsImage) ? imageWindows(messages, keepRecent) : null;
|
|
209
|
-
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));
|
|
255
|
+
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));
|
|
210
256
|
}
|
|
211
|
-
function wireBytes(content, kept, stub) {
|
|
257
|
+
function wireBytes(content, kept, vision, stub) {
|
|
212
258
|
const tool = toolImageRef(content);
|
|
213
|
-
if (tool) return kept ? tool.
|
|
259
|
+
if (tool) return kept ? imageWireWeight(tool.path, tool.mime, vision) || stub.length : stub.length;
|
|
214
260
|
if (content == null || typeof content === "string") return contentBytes(content);
|
|
215
|
-
if (kept)
|
|
261
|
+
if (kept) {
|
|
262
|
+
let n2 = 0;
|
|
263
|
+
for (const p of content) {
|
|
264
|
+
const url = p.type === "image_url" ? p.image_url?.url ?? "" : "";
|
|
265
|
+
const ref = url ? parseImageRef(url) : null;
|
|
266
|
+
const inline = url.startsWith("data:") ? inlineImageForWire(url, vision) : null;
|
|
267
|
+
n2 += ref ? imageWireWeight(ref.path, ref.mime, vision) : inline ? wiredImageWeight(inline) : contentBytes([p]);
|
|
268
|
+
}
|
|
269
|
+
return n2;
|
|
270
|
+
}
|
|
216
271
|
let n = 0;
|
|
217
272
|
for (const p of content) {
|
|
218
273
|
if (p.type === "text") {
|
|
@@ -224,31 +279,70 @@ function wireBytes(content, kept, stub) {
|
|
|
224
279
|
}
|
|
225
280
|
return n;
|
|
226
281
|
}
|
|
227
|
-
function
|
|
228
|
-
try {
|
|
229
|
-
return `data:${ref.mime};base64,${readFileSync(ref.path).toString("base64")}`;
|
|
230
|
-
} catch (e) {
|
|
231
|
-
log2.warn(`image ref unreadable, sending stub instead: ${ref.path}`, e);
|
|
232
|
-
return null;
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
function expandContent(content, stub) {
|
|
282
|
+
function expandContent(content, vision) {
|
|
236
283
|
const tool = toolImageRef(content);
|
|
237
284
|
if (tool) {
|
|
238
|
-
const
|
|
239
|
-
|
|
285
|
+
const w = imageForWire(tool.path, tool.mime, vision);
|
|
286
|
+
if (!w) return IMAGE_ELIDE_STUB;
|
|
287
|
+
return "url" in w ? [{ type: "text", text: `image: ${tool.path}` }, imagePart(w.url)] : w.note;
|
|
240
288
|
}
|
|
241
289
|
if (typeof content === "string") return content;
|
|
242
290
|
let changed = false;
|
|
243
291
|
const out = content.map((p) => {
|
|
244
|
-
if (p.type !== "image_url"
|
|
292
|
+
if (p.type !== "image_url") return p;
|
|
293
|
+
const url = p.image_url?.url ?? "";
|
|
294
|
+
if (url.startsWith("data:")) {
|
|
295
|
+
const w2 = inlineImageForWire(url, vision);
|
|
296
|
+
if ("url" in w2 && w2.url === url) return p;
|
|
297
|
+
changed = true;
|
|
298
|
+
return "url" in w2 ? imagePart(w2.url) : { type: "text", text: w2.note };
|
|
299
|
+
}
|
|
300
|
+
if (!isImageRefUrl(url)) return p;
|
|
245
301
|
changed = true;
|
|
246
|
-
const ref = parseImageRef(
|
|
247
|
-
|
|
248
|
-
|
|
302
|
+
const ref = parseImageRef(url);
|
|
303
|
+
if (!ref) return { type: "text", text: IMAGE_ELIDE_STUB };
|
|
304
|
+
const w = imageForWire(ref.path, ref.mime, vision);
|
|
305
|
+
if (!w) return { type: "text", text: IMAGE_ELIDE_STUB };
|
|
306
|
+
return "url" in w ? imagePart(w.url) : { type: "text", text: w.note };
|
|
249
307
|
});
|
|
250
308
|
return changed ? out : content;
|
|
251
309
|
}
|
|
310
|
+
function hoistToolImages(messages) {
|
|
311
|
+
if (!messages.some((m) => m.role === "tool" && Array.isArray(m.content) && m.content.some((p) => p.type === "image_url"))) return messages;
|
|
312
|
+
const out = [];
|
|
313
|
+
let pending = [];
|
|
314
|
+
const flush = () => {
|
|
315
|
+
if (pending.length) out.push({ role: "user", content: [{ type: "text", text: "Image output from the tool call above:" }, ...pending] });
|
|
316
|
+
pending = [];
|
|
317
|
+
};
|
|
318
|
+
for (const m of messages) {
|
|
319
|
+
if (m.role !== "tool") flush();
|
|
320
|
+
if (m.role === "tool" && Array.isArray(m.content) && m.content.some((p) => p.type === "image_url")) {
|
|
321
|
+
const imgs = m.content.filter((p) => p.type === "image_url");
|
|
322
|
+
pending.push(...imgs);
|
|
323
|
+
const text = m.content.filter((p) => p.type === "text").map((p) => p.text).join("\n");
|
|
324
|
+
out.push({ ...m, content: text || `[${imgs.length} image(s) returned; shown below]` });
|
|
325
|
+
} else out.push(m);
|
|
326
|
+
}
|
|
327
|
+
flush();
|
|
328
|
+
return out;
|
|
329
|
+
}
|
|
330
|
+
function scrubBase64(messages) {
|
|
331
|
+
let changed = false;
|
|
332
|
+
const out = messages.map((m) => {
|
|
333
|
+
const c = m.content;
|
|
334
|
+
let next = c;
|
|
335
|
+
if (typeof c === "string") next = scrubBase64Text(c);
|
|
336
|
+
else if (Array.isArray(c)) {
|
|
337
|
+
const parts = c.map((p) => p.type === "text" && p.text ? { ...p, text: scrubBase64Text(p.text) } : p);
|
|
338
|
+
if (parts.some((p, i) => p.text !== c[i].text)) next = parts;
|
|
339
|
+
}
|
|
340
|
+
if (next === c) return m;
|
|
341
|
+
changed = true;
|
|
342
|
+
return { ...m, content: next };
|
|
343
|
+
});
|
|
344
|
+
return changed ? out : messages;
|
|
345
|
+
}
|
|
252
346
|
function stripContent(content, stub) {
|
|
253
347
|
if (toolImageRef(content)) return stub;
|
|
254
348
|
if (typeof content === "string") return content;
|
|
@@ -271,12 +365,13 @@ function endsOnAnnouncedAction(text) {
|
|
|
271
365
|
if (PROGRESSIVE.test(last)) return true;
|
|
272
366
|
return clauses(last).some((c) => !NEXT_STEP.test(c) && !SIGN_OFF.test(c) && COMMITMENT.test(c));
|
|
273
367
|
}
|
|
274
|
-
var
|
|
368
|
+
var log3, p_sep, IMAGE_ELIDE_STUB, IMAGE_REF_SCHEME, REF_REGISTRY_MAX, refRegistry, isImageRefUrl, bearsImage, COMMITMENT, PROGRESSIVE, NEXT_STEP, SIGN_OFF;
|
|
275
369
|
var init_llm = __esm({
|
|
276
370
|
"src/llm.ts"() {
|
|
277
371
|
"use strict";
|
|
278
372
|
init_logging();
|
|
279
|
-
|
|
373
|
+
init_imageWire();
|
|
374
|
+
log3 = forComponent("llm");
|
|
280
375
|
p_sep = (parts) => parts.length > 1 ? "\n" : "";
|
|
281
376
|
IMAGE_ELIDE_STUB = "[image elided \u2014 re-attach @path or /paste to view again]";
|
|
282
377
|
IMAGE_REF_SCHEME = "agentx-image:";
|
|
@@ -963,7 +1058,7 @@ function makeWebFetchTool(options = {}) {
|
|
|
963
1058
|
|
|
964
1059
|
${capped}`;
|
|
965
1060
|
} catch (e) {
|
|
966
|
-
|
|
1061
|
+
log4.debug(`WebFetch ${u} failed`, e);
|
|
967
1062
|
return `Error fetching ${u}: ${e?.name === "AbortError" ? `timed out after ${timeoutMs}ms` : e?.message ?? e}`;
|
|
968
1063
|
} finally {
|
|
969
1064
|
clearTimeout(timer);
|
|
@@ -1087,7 +1182,7 @@ function makeWebSearchTool(options = {}) {
|
|
|
1087
1182
|
if (!res.ok) return `Error: search returned ${res.status} ${res.statusText}`;
|
|
1088
1183
|
return formatHits(parseDdgHtml(await res.text(), maxResults));
|
|
1089
1184
|
} catch (e) {
|
|
1090
|
-
|
|
1185
|
+
log4.debug("WebSearch failed", e);
|
|
1091
1186
|
return `Error searching: ${e?.name === "AbortError" ? `timed out after ${timeoutMs}ms` : e?.message ?? e}`;
|
|
1092
1187
|
} finally {
|
|
1093
1188
|
clearTimeout(timer);
|
|
@@ -1098,12 +1193,12 @@ function makeWebSearchTool(options = {}) {
|
|
|
1098
1193
|
function makeWebSearchAnthropicTool(opts = {}) {
|
|
1099
1194
|
return makeWebSearchTool({ provider: "anthropic", name: "WebSearchAnthropic", description: ANTHROPIC_SEARCH_DESC, anthropicApiKey: opts.anthropicApiKey, model: opts.model });
|
|
1100
1195
|
}
|
|
1101
|
-
var
|
|
1196
|
+
var log4, _dnsLookup, webFetchTool, webSearchTool, ANTHROPIC_SEARCH_DESC, webSearchAnthropicTool;
|
|
1102
1197
|
var init_tools_web = __esm({
|
|
1103
1198
|
"src/tools.web.ts"() {
|
|
1104
1199
|
"use strict";
|
|
1105
1200
|
init_logging();
|
|
1106
|
-
|
|
1201
|
+
log4 = forComponent("web");
|
|
1107
1202
|
webFetchTool = makeWebFetchTool();
|
|
1108
1203
|
webSearchTool = makeWebSearchTool();
|
|
1109
1204
|
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).";
|
|
@@ -1323,7 +1418,7 @@ function realPathOf(fs, path) {
|
|
|
1323
1418
|
try {
|
|
1324
1419
|
return fs.realPath(path);
|
|
1325
1420
|
} catch (e) {
|
|
1326
|
-
|
|
1421
|
+
log5.debug(`realPath unavailable for ${path} \u2014 inlining image bytes instead`, e);
|
|
1327
1422
|
return null;
|
|
1328
1423
|
}
|
|
1329
1424
|
}
|
|
@@ -1375,7 +1470,7 @@ function toolsByName(names, available) {
|
|
|
1375
1470
|
return t;
|
|
1376
1471
|
});
|
|
1377
1472
|
}
|
|
1378
|
-
var
|
|
1473
|
+
var log5, numberLines, bashTool, IMG_MIME, readTool, editTool;
|
|
1379
1474
|
var init_tools = __esm({
|
|
1380
1475
|
"src/tools.ts"() {
|
|
1381
1476
|
"use strict";
|
|
@@ -1386,7 +1481,7 @@ var init_tools = __esm({
|
|
|
1386
1481
|
init_tools_web();
|
|
1387
1482
|
init_OverlayFilesystem();
|
|
1388
1483
|
init_redact();
|
|
1389
|
-
|
|
1484
|
+
log5 = forComponent("tools");
|
|
1390
1485
|
numberLines = (content, offset = 0, limit) => {
|
|
1391
1486
|
const lines = content.split("\n");
|
|
1392
1487
|
const start = Math.max(0, offset);
|
|
@@ -1568,12 +1663,12 @@ ${tail || "(no output)"}`;
|
|
|
1568
1663
|
}
|
|
1569
1664
|
];
|
|
1570
1665
|
}
|
|
1571
|
-
var
|
|
1666
|
+
var log6, JobRegistryOptions, secs, JobRegistry, NO_JOB, describe;
|
|
1572
1667
|
var init_tools_jobs = __esm({
|
|
1573
1668
|
"src/tools.jobs.ts"() {
|
|
1574
1669
|
"use strict";
|
|
1575
1670
|
init_logging();
|
|
1576
|
-
|
|
1671
|
+
log6 = forComponent("jobs");
|
|
1577
1672
|
JobRegistryOptions = class {
|
|
1578
1673
|
/** Tail buffer cap per job (chars). */
|
|
1579
1674
|
maxBuffer = 256 * 1024;
|
|
@@ -1752,7 +1847,7 @@ var init_tools_jobs = __esm({
|
|
|
1752
1847
|
try {
|
|
1753
1848
|
onExit({ id: job.id, kind: job.kind, label: job.label, status, reason: job.reason, tail: job.buf.slice(-4e3), result });
|
|
1754
1849
|
} catch (e) {
|
|
1755
|
-
|
|
1850
|
+
log6.warn(`job onExit handler threw for ${job.id}: ${e}`);
|
|
1756
1851
|
}
|
|
1757
1852
|
}
|
|
1758
1853
|
}
|
|
@@ -1806,7 +1901,7 @@ var init_tools_jobs = __esm({
|
|
|
1806
1901
|
try {
|
|
1807
1902
|
j.onKill?.(reason);
|
|
1808
1903
|
} catch (e) {
|
|
1809
|
-
|
|
1904
|
+
log6.warn(`onKill for ${id} threw: ${e}`);
|
|
1810
1905
|
}
|
|
1811
1906
|
}
|
|
1812
1907
|
return true;
|
|
@@ -1853,7 +1948,7 @@ var init_tools_jobs = __esm({
|
|
|
1853
1948
|
try {
|
|
1854
1949
|
cpu = await j.cpuTime();
|
|
1855
1950
|
} catch (e) {
|
|
1856
|
-
|
|
1951
|
+
log6.debug(`cpu probe for ${j.id} failed: ${e}`);
|
|
1857
1952
|
}
|
|
1858
1953
|
if (cpu != null && j.lastCpu != null && cpu > j.lastCpu) j.lastActivityAt = this.options.now();
|
|
1859
1954
|
const prev = j.lastCpu;
|
|
@@ -1861,7 +1956,7 @@ var init_tools_jobs = __esm({
|
|
|
1861
1956
|
const verdict = this.verdict(j, now4);
|
|
1862
1957
|
if (!verdict?.idle || j.status !== "running") continue;
|
|
1863
1958
|
if (cpu == null) {
|
|
1864
|
-
|
|
1959
|
+
log6.debug(`not reaping ${j.id}: ${verdict.reason} but CPU unknown`);
|
|
1865
1960
|
continue;
|
|
1866
1961
|
}
|
|
1867
1962
|
if (prev == null) continue;
|
|
@@ -1875,7 +1970,7 @@ var init_tools_jobs = __esm({
|
|
|
1875
1970
|
return null;
|
|
1876
1971
|
}
|
|
1877
1972
|
stuck(j, reason, now4) {
|
|
1878
|
-
|
|
1973
|
+
log6.warn(`reaping ${j.id} (${j.kind} "${j.label}"): ${reason} \u2014 age ${secs(now4 - j.startedAt)}, last activity ${secs(now4 - j.lastActivityAt)} ago, idleMs=${j.idleMs}, hardCapMs=${j.hardCapMs}, ${j.bytes} byte(s) output`);
|
|
1879
1974
|
if (j.onKill) j.notify = true;
|
|
1880
1975
|
this.kill(j.id, reason);
|
|
1881
1976
|
}
|
|
@@ -1889,7 +1984,7 @@ var init_tools_jobs = __esm({
|
|
|
1889
1984
|
this.timer = void 0;
|
|
1890
1985
|
return;
|
|
1891
1986
|
}
|
|
1892
|
-
void this.sweep().catch((e) =>
|
|
1987
|
+
void this.sweep().catch((e) => log6.warn(`job sweep failed: ${e}`));
|
|
1893
1988
|
}, this.options.sweepMs);
|
|
1894
1989
|
this.timer.unref?.();
|
|
1895
1990
|
}
|
|
@@ -1917,7 +2012,7 @@ __export(worktree_exports, {
|
|
|
1917
2012
|
worktreeBranchName: () => worktreeBranchName
|
|
1918
2013
|
});
|
|
1919
2014
|
import { spawnSync } from "child_process";
|
|
1920
|
-
import { symlinkSync, mkdirSync, statSync as
|
|
2015
|
+
import { symlinkSync, mkdirSync, statSync as statSync4, readFileSync as readFileSync3, rmSync } from "fs";
|
|
1921
2016
|
import { join as join2 } from "path";
|
|
1922
2017
|
function validateWorktreeSlug(slug2) {
|
|
1923
2018
|
if (!slug2) throw new Error("worktree slug must not be empty");
|
|
@@ -1953,16 +2048,16 @@ function findGitRoot(from) {
|
|
|
1953
2048
|
}
|
|
1954
2049
|
function readWorktreeHead(worktreePath) {
|
|
1955
2050
|
try {
|
|
1956
|
-
const gitFile =
|
|
2051
|
+
const gitFile = readFileSync3(join2(worktreePath, ".git"), "utf-8").trim();
|
|
1957
2052
|
const m = gitFile.match(/^gitdir:\s*(.+)$/);
|
|
1958
2053
|
if (!m) return null;
|
|
1959
2054
|
const headPath = join2(m[1], "HEAD");
|
|
1960
|
-
const head =
|
|
2055
|
+
const head = readFileSync3(headPath, "utf-8").trim();
|
|
1961
2056
|
const refMatch = head.match(/^ref:\s*(.+)$/);
|
|
1962
2057
|
if (!refMatch) return head.length === 40 ? head : null;
|
|
1963
2058
|
const refPath = join2(m[1], "..", refMatch[1]);
|
|
1964
2059
|
try {
|
|
1965
|
-
return
|
|
2060
|
+
return readFileSync3(refPath, "utf-8").trim();
|
|
1966
2061
|
} catch {
|
|
1967
2062
|
}
|
|
1968
2063
|
const r = git(["rev-parse", "HEAD"], worktreePath);
|
|
@@ -1998,7 +2093,7 @@ function symlinkDirs(repoRoot, worktreePath, dirs) {
|
|
|
1998
2093
|
const src = join2(repoRoot, dir);
|
|
1999
2094
|
const dst = join2(worktreePath, dir);
|
|
2000
2095
|
try {
|
|
2001
|
-
|
|
2096
|
+
statSync4(src);
|
|
2002
2097
|
} catch {
|
|
2003
2098
|
continue;
|
|
2004
2099
|
}
|
|
@@ -2014,7 +2109,7 @@ function cleanupWorktree(repoRoot, slug2, opts) {
|
|
|
2014
2109
|
const worktreePath = worktreePathFor(repoRoot, slug2);
|
|
2015
2110
|
const branch = worktreeBranchName(slug2);
|
|
2016
2111
|
try {
|
|
2017
|
-
|
|
2112
|
+
statSync4(worktreePath);
|
|
2018
2113
|
} catch {
|
|
2019
2114
|
return { removed: false, reason: "not found" };
|
|
2020
2115
|
}
|
|
@@ -2561,7 +2656,7 @@ async function psGroupCpuSeconds(pgid) {
|
|
|
2561
2656
|
const out = await new Promise((res, rej) => execFile4("ps", ["-A", "-o", "pid=,ppid=,pgid=,time="], { timeout: 5e3, env: { ...process.env, LC_ALL: "C" } }, (e, so) => e ? rej(e) : res(String(so))));
|
|
2562
2657
|
return sumTreeCpu(out, pgid);
|
|
2563
2658
|
} catch (e) {
|
|
2564
|
-
|
|
2659
|
+
log24.debug(`ps cpu probe failed for group ${pgid}: ${e}`);
|
|
2565
2660
|
return void 0;
|
|
2566
2661
|
}
|
|
2567
2662
|
}
|
|
@@ -2709,7 +2804,7 @@ function makeRealShellTool(options) {
|
|
|
2709
2804
|
proc.stderr?.on("data", collect);
|
|
2710
2805
|
proc.on("error", (err2) => {
|
|
2711
2806
|
if (err2?.name === "AbortError" || ctl.signal.aborted) return finish(reasonFor(timedOut, timeoutMs, clean2(out)));
|
|
2712
|
-
|
|
2807
|
+
log24.debug("shell spawn error", err2);
|
|
2713
2808
|
finish(`[exit 1] ${err2?.message ?? err2}${out ? "\n" + clean2(out) : ""}`);
|
|
2714
2809
|
});
|
|
2715
2810
|
proc.on("close", (code) => {
|
|
@@ -2782,7 +2877,7 @@ ${clean2(out) || "(no output yet)"}`;
|
|
|
2782
2877
|
}
|
|
2783
2878
|
];
|
|
2784
2879
|
}
|
|
2785
|
-
var
|
|
2880
|
+
var log24, clean2, DETACHED, SECRET_ENV_RE, _spawn, ShellJobRegistry, shellStatus, NO_JOB2;
|
|
2786
2881
|
var init_tools_shell = __esm({
|
|
2787
2882
|
"src/tools.shell.ts"() {
|
|
2788
2883
|
"use strict";
|
|
@@ -2791,7 +2886,7 @@ var init_tools_shell = __esm({
|
|
|
2791
2886
|
init_logging();
|
|
2792
2887
|
init_shell_sandbox();
|
|
2793
2888
|
init_tools_jobs();
|
|
2794
|
-
|
|
2889
|
+
log24 = forComponent("shell");
|
|
2795
2890
|
clean2 = (s) => truncateOutput(redactSecrets(s.replace(/\n+$/, "")));
|
|
2796
2891
|
DETACHED = { stdio: ["ignore", "pipe", "pipe"], detached: true };
|
|
2797
2892
|
SECRET_ENV_RE = /(_API_KEY|_TOKEN|_SECRET|_PASSWORD|_PRIVATE_KEY|^AWS_|^GITHUB_TOKEN$|^OPENAI_|^ANTHROPIC_|^GOOGLE_|^GEMINI_|^GROQ_|^NPM_TOKEN$)/i;
|
|
@@ -2939,7 +3034,7 @@ var init_tools_shell = __esm({
|
|
|
2939
3034
|
|
|
2940
3035
|
// cli/cli.ts
|
|
2941
3036
|
import { createInterface } from "readline/promises";
|
|
2942
|
-
import { existsSync as existsSync9, readFileSync as
|
|
3037
|
+
import { existsSync as existsSync9, readFileSync as readFileSync10, appendFileSync, mkdirSync as mkdirSync12, writeFileSync as writeFileSync9, readdirSync as readdirSync4, statSync as statSync7, unlinkSync as unlinkSync5 } from "fs";
|
|
2943
3038
|
import { homedir as homedir10 } from "os";
|
|
2944
3039
|
import { spawnSync as spawnSync6 } from "child_process";
|
|
2945
3040
|
|
|
@@ -3767,7 +3862,7 @@ init_OverlayFilesystem();
|
|
|
3767
3862
|
init_worktree();
|
|
3768
3863
|
init_NodeDiskFilesystem();
|
|
3769
3864
|
init_logging();
|
|
3770
|
-
var
|
|
3865
|
+
var log7 = forComponent("subagent");
|
|
3771
3866
|
async function boundedPool(items, limit, fn) {
|
|
3772
3867
|
const out = new Array(items.length);
|
|
3773
3868
|
let next = 0;
|
|
@@ -3841,7 +3936,7 @@ function traceFileFor(dir, label, depth) {
|
|
|
3841
3936
|
const id = `${Date.now().toString(36)}-${(traceSeq++).toString(36)}`;
|
|
3842
3937
|
return join4(dir, `${id}-d${depth}-${sanitizeSlug(label) || "sub"}.json`);
|
|
3843
3938
|
} catch (e) {
|
|
3844
|
-
|
|
3939
|
+
log7.warn(`subagent trace dir unavailable (${dir}) \u2014 running without a trace: ${e instanceof Error ? e.message : String(e)}`);
|
|
3845
3940
|
return void 0;
|
|
3846
3941
|
}
|
|
3847
3942
|
}
|
|
@@ -3849,7 +3944,7 @@ function flushTrace(file, meta, messages, status) {
|
|
|
3849
3944
|
try {
|
|
3850
3945
|
writeFileSync2(file, JSON.stringify({ ...meta, status, updated: Date.now(), messages }, null, 2));
|
|
3851
3946
|
} catch (e) {
|
|
3852
|
-
|
|
3947
|
+
log7.warn(`subagent trace flush failed (${file}): ${e instanceof Error ? e.message : String(e)}`);
|
|
3853
3948
|
}
|
|
3854
3949
|
}
|
|
3855
3950
|
function installTrace(child, file, meta) {
|
|
@@ -3890,7 +3985,7 @@ async function runChildTraced(args) {
|
|
|
3890
3985
|
if (file) flushTrace(file, meta, child.transcript, wc.timedOut() ? "deadline" : res.finishReason);
|
|
3891
3986
|
let result = res.finishReason === "stop" ? res.text || `(child '${label}' finished with no summary; finishReason=${res.finishReason})` : abortHint(label, res, file);
|
|
3892
3987
|
if (wc.timedOut()) {
|
|
3893
|
-
|
|
3988
|
+
log7.warn(`sub-task '${label}' hit its ${deadlineMs}ms wall-clock deadline (steps=${res.steps})`);
|
|
3894
3989
|
result += deadlineNote(deadlineMs);
|
|
3895
3990
|
}
|
|
3896
3991
|
await opts.hooks?.onSubagentStop?.(result, { label, agentType });
|
|
@@ -4219,7 +4314,7 @@ function reasoningToChatFragment(model, effort) {
|
|
|
4219
4314
|
}
|
|
4220
4315
|
|
|
4221
4316
|
// src/Agent.ts
|
|
4222
|
-
var
|
|
4317
|
+
var log8 = forComponent("Agent");
|
|
4223
4318
|
function envInt(key, fallback) {
|
|
4224
4319
|
const n = typeof process !== "undefined" ? Number(process.env?.[key]) : NaN;
|
|
4225
4320
|
return Number.isFinite(n) && n >= 0 ? n : fallback;
|
|
@@ -4300,6 +4395,9 @@ var AgentOptions = class {
|
|
|
4300
4395
|
* `maxTokens`: that is the cumulative SPEND kill-switch, and a spend budget must never be able to
|
|
4301
4396
|
* drive destructive compaction (`--max-tokens 50000` would otherwise shred the transcript). */
|
|
4302
4397
|
contextWindow = 0;
|
|
4398
|
+
/** Does a model take image input? true/false from the host's model catalog; undefined = unknown (images are tried,
|
|
4399
|
+
* and turned off for the run when the adapter refuses them). Unset = always try. */
|
|
4400
|
+
imageInput;
|
|
4303
4401
|
/** Pagination ceiling for a SINGLE tool result (bytes). A result over this is cropped to page 1 with
|
|
4304
4402
|
* a marker telling the model it was cropped (refine the query, or page further). Guards against one
|
|
4305
4403
|
* Grep/Read/MCP call blowing the whole context window. 0 = off. Default 60k (~15k tokens). */
|
|
@@ -4599,7 +4697,7 @@ var Agent = class _Agent {
|
|
|
4599
4697
|
const disk = new NodeDiskFilesystem2(process.cwd());
|
|
4600
4698
|
await disk.init();
|
|
4601
4699
|
this.options.fs = new JailedFilesystem2(disk);
|
|
4602
|
-
|
|
4700
|
+
log8.info(`no fs provided \u2014 defaulting to jailed real disk at ${process.cwd()}`);
|
|
4603
4701
|
}
|
|
4604
4702
|
this.buildCtx();
|
|
4605
4703
|
}
|
|
@@ -4744,7 +4842,8 @@ var Agent = class _Agent {
|
|
|
4744
4842
|
const cap = _Agent.contextCap(this.options);
|
|
4745
4843
|
const target = Math.round(cap * _Agent.COMPACT_TARGET);
|
|
4746
4844
|
const kr = this.options.keepRecentImages ?? 1;
|
|
4747
|
-
const
|
|
4845
|
+
const vis2 = this.sendsImages();
|
|
4846
|
+
const tokensBefore = estimateTokens(this.transcript, kr, vis2);
|
|
4748
4847
|
const msgsBefore = this.transcript.length;
|
|
4749
4848
|
let max = Math.max(2, maxMessages);
|
|
4750
4849
|
if (this.transcript.length <= max && tokensBefore <= target)
|
|
@@ -4752,18 +4851,18 @@ var Agent = class _Agent {
|
|
|
4752
4851
|
void this.activeHooks?.onPreCompact?.(this.transcript);
|
|
4753
4852
|
let out = this.transcript;
|
|
4754
4853
|
if (out.length > max) out = compact(out, max, focus);
|
|
4755
|
-
if (estimateTokens(out, kr) > target) out = elideOversizedImages(out, target, true, kr);
|
|
4756
|
-
if (estimateTokens(out, kr) > target) out = capOversizedBodies(out, target, kr);
|
|
4757
|
-
if (estimateTokens(out, kr) > target) out = elideOversizedImages(out, target, false, kr);
|
|
4758
|
-
while (estimateTokens(out, kr) > target && max > 2 && out.length > 2) {
|
|
4854
|
+
if (estimateTokens(out, kr, vis2) > target) out = elideOversizedImages(out, target, true, kr);
|
|
4855
|
+
if (estimateTokens(out, kr, vis2) > target) out = capOversizedBodies(out, target, kr);
|
|
4856
|
+
if (estimateTokens(out, kr, vis2) > target) out = elideOversizedImages(out, target, false, kr);
|
|
4857
|
+
while (estimateTokens(out, kr, vis2) > target && max > 2 && out.length > 2) {
|
|
4759
4858
|
max = Math.max(2, Math.floor(max / 2));
|
|
4760
4859
|
out = capOversizedBodies(compact(out, max, focus), target, kr);
|
|
4761
4860
|
}
|
|
4762
|
-
if (estimateTokens(out, kr) > target) out = fitTokenBudget(out, target, kr);
|
|
4861
|
+
if (estimateTokens(out, kr, vis2) > target) out = fitTokenBudget(out, target, kr);
|
|
4763
4862
|
out = dropOrphanToolResults(out);
|
|
4764
4863
|
this.transcript = out;
|
|
4765
4864
|
this.releaseStaleImages();
|
|
4766
|
-
return { tokensBefore, tokensAfter: estimateTokens(this.transcript, kr), target, cap, removed: msgsBefore - this.transcript.length };
|
|
4865
|
+
return { tokensBefore, tokensAfter: estimateTokens(this.transcript, kr, vis2), target, cap, removed: msgsBefore - this.transcript.length };
|
|
4767
4866
|
}
|
|
4768
4867
|
/** Drop inline base64 images from older messages so Bun/JSC can reclaim the heap (see contentBytes). */
|
|
4769
4868
|
releaseStaleImages(keepRecent = 1) {
|
|
@@ -4787,7 +4886,7 @@ var Agent = class _Agent {
|
|
|
4787
4886
|
let closureNudged = false;
|
|
4788
4887
|
let lastProgressAt = start;
|
|
4789
4888
|
const kill = (finishReason) => {
|
|
4790
|
-
|
|
4889
|
+
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` : ""})`);
|
|
4791
4890
|
this.jobs.killAll(`run ended: ${finishReason}`, true);
|
|
4792
4891
|
const claims = this.survivingClaims();
|
|
4793
4892
|
return { text: lastAssistantText(this.transcript), steps, finishReason, messages: this.transcript, usage, usageEstimated, ...claims.length ? { fabricatedToolClaims: claims } : {} };
|
|
@@ -4802,7 +4901,7 @@ var Agent = class _Agent {
|
|
|
4802
4901
|
steps++;
|
|
4803
4902
|
this.options.host?.notify?.({ kind: "turn_start", message: `step ${steps}` });
|
|
4804
4903
|
let res;
|
|
4805
|
-
|
|
4904
|
+
let sent = this.trimContext();
|
|
4806
4905
|
const frag = reasoningToChatFragment(o.model, o.reasoning);
|
|
4807
4906
|
const sentTools = o.model.startsWith("claude-code/") ? [] : wireTools;
|
|
4808
4907
|
const isCursorWithTools = o.model.startsWith("cursor/") && wireTools.length > 0;
|
|
@@ -4813,9 +4912,9 @@ var Agent = class _Agent {
|
|
|
4813
4912
|
this.nativeHoldMs = o.nativeHoldMs;
|
|
4814
4913
|
if (isCursorWithTools) {
|
|
4815
4914
|
if (typeof hostToolTimeoutMs === "number" && effectiveToolTimeoutMs !== hostToolTimeoutMs) {
|
|
4816
|
-
|
|
4915
|
+
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.`);
|
|
4817
4916
|
}
|
|
4818
|
-
for (const v of toolTimeoutViolations(this.activeTools, effectiveToolTimeoutMs))
|
|
4917
|
+
for (const v of toolTimeoutViolations(this.activeTools, effectiveToolTimeoutMs)) log8.warn(v);
|
|
4819
4918
|
}
|
|
4820
4919
|
const hostExec = async (name, args) => {
|
|
4821
4920
|
const tc = { id: `host-${Date.now()}`, type: "function", function: { name, arguments: JSON.stringify(args) } };
|
|
@@ -4874,12 +4973,20 @@ var Agent = class _Agent {
|
|
|
4874
4973
|
res = r;
|
|
4875
4974
|
}
|
|
4876
4975
|
if (steps === 1 && this.isEmptyStop(res) && !this.pendingInjections.length) {
|
|
4877
|
-
const e = new Error(
|
|
4976
|
+
const e = new Error(`model returned an empty turn (finishReason=${res.finishReason ?? "n/a"}, no text, no tools)`);
|
|
4878
4977
|
e.code = "empty";
|
|
4978
|
+
if (res.finishReason) e.providerFinishReason = res.finishReason;
|
|
4879
4979
|
throw e;
|
|
4880
4980
|
}
|
|
4881
4981
|
break;
|
|
4882
4982
|
} catch (err2) {
|
|
4983
|
+
if (!this.imagesOff && /does not support .*input/i.test(String(err2?.message ?? "")) && sent.some((m) => Array.isArray(m.content) && m.role === "tool")) {
|
|
4984
|
+
this.imagesOff = true;
|
|
4985
|
+
log8.warn(`model refused image input (${err2?.message}) \u2014 resending with image Reads as text stubs`);
|
|
4986
|
+
sent = this.trimContext();
|
|
4987
|
+
attempt--;
|
|
4988
|
+
continue;
|
|
4989
|
+
}
|
|
4883
4990
|
const sc = err2?.statusCode;
|
|
4884
4991
|
const serverSide = sc >= 500 || sc === 408 || /Service Unavailable|overloaded|Internal server error|Bad gateway|Gateway time/i.test(String(err2?.message ?? ""));
|
|
4885
4992
|
const network = !sc && /ECONNRESET|ECONNREFUSED|ETIMEDOUT|ENOTFOUND|EAI_AGAIN|EPIPE|socket hang up|fetch failed|network|terminated|UND_ERR/i.test(String(err2?.message ?? err2?.code ?? err2));
|
|
@@ -4887,11 +4994,11 @@ var Agent = class _Agent {
|
|
|
4887
4994
|
const empty = err2?.code === "empty" || err2?.code === "cursor_silent";
|
|
4888
4995
|
const maxAttempts = empty ? Math.max(o.emptyRetries, 0) : 2;
|
|
4889
4996
|
const replaysTool = stalled && this.toolRanThisAttempt;
|
|
4890
|
-
if (replaysTool)
|
|
4997
|
+
if (replaysTool) log8.warn("model stall after tool(s) already ran this step \u2014 not retrying (a retry would replay them)");
|
|
4891
4998
|
const transient = !o.signal?.aborted && !isAbortError(err2) && !replaysTool && attempt < maxAttempts && (network || serverSide || stalled || empty);
|
|
4892
4999
|
if (!transient) throw err2;
|
|
4893
5000
|
const waitMs = (empty ? o.emptyRetryBaseMs : 1e3) * (attempt + 1);
|
|
4894
|
-
|
|
5001
|
+
log8.warn(`${empty ? "empty model turn" : stalled ? "model stall" : "network drop"} mid-step (${err2?.message ?? err2}) \u2014 retrying in ${waitMs}ms`);
|
|
4895
5002
|
o.host?.notify?.({ kind: "retry", message: `${empty ? "model returned nothing" : stalled ? "model stalled" : "connection dropped"} \u2014 retrying step (#${attempt + 1})` });
|
|
4896
5003
|
await new Promise((r) => setTimeout(r, waitMs));
|
|
4897
5004
|
}
|
|
@@ -4899,13 +5006,14 @@ var Agent = class _Agent {
|
|
|
4899
5006
|
} catch (err2) {
|
|
4900
5007
|
if (err2?.code === "budget") return kill("budget");
|
|
4901
5008
|
if (err2?.code === "stall") {
|
|
4902
|
-
|
|
4903
|
-
return { ...kill("timeout"), error: err2 };
|
|
5009
|
+
log8.warn(`model stalled out after retries: ${err2?.message ?? err2}`);
|
|
5010
|
+
return { ...kill("timeout"), providerFinishReason: "none (stalled)", error: err2 };
|
|
4904
5011
|
}
|
|
4905
5012
|
if (o.signal?.aborted || isAbortError(err2)) return kill("aborted");
|
|
4906
5013
|
if (err2?.code === "empty" || err2?.code === "cursor_silent") {
|
|
4907
|
-
|
|
4908
|
-
|
|
5014
|
+
log8.warn(`model returned empty/silent turns after retries \u2014 surfacing as failed`);
|
|
5015
|
+
const pfr = err2?.providerFinishReason;
|
|
5016
|
+
return { text: "", steps, finishReason: "empty", ...pfr ? { providerFinishReason: pfr } : {}, messages: this.transcript, usage, usageEstimated, error: err2 };
|
|
4909
5017
|
}
|
|
4910
5018
|
const body = err2?.body ?? err2?.response?.data ?? err2?.error;
|
|
4911
5019
|
let bodyStr;
|
|
@@ -4916,13 +5024,13 @@ var Agent = class _Agent {
|
|
|
4916
5024
|
}
|
|
4917
5025
|
if (bodyStr && err2 instanceof Error && !err2.message.includes(bodyStr)) err2.detail = bodyStr;
|
|
4918
5026
|
const errLog = err2 instanceof Error ? { message: err2.message, ...err2.detail ? { detail: err2.detail } : {}, stack: err2.stack } : err2;
|
|
4919
|
-
|
|
5027
|
+
log8.error(`chat() failed: ${err2?.message ?? err2}${bodyStr ? ` \u2014 ${bodyStr}` : ""}`, errLog);
|
|
4920
5028
|
return { text: "", steps, finishReason: "error", messages: this.transcript, usage, usageEstimated, error: err2 };
|
|
4921
5029
|
}
|
|
4922
5030
|
if (o.signal?.aborted) return kill("aborted");
|
|
4923
5031
|
if (res.finishReason === "error" && !res.toolCalls?.length) {
|
|
4924
5032
|
const err2 = new Error(contentText(res.content ?? "").trim() || "provider error");
|
|
4925
|
-
|
|
5033
|
+
log8.error(`chat() failed: ${err2.message}`);
|
|
4926
5034
|
return { text: "", steps, finishReason: "error", messages: this.transcript, usage, usageEstimated, error: err2 };
|
|
4927
5035
|
}
|
|
4928
5036
|
if (!res.usage) {
|
|
@@ -4980,21 +5088,21 @@ var Agent = class _Agent {
|
|
|
4980
5088
|
if (emptyTurn && o.failEmptyTurns) {
|
|
4981
5089
|
if (!emptyNudged && canNudge()) {
|
|
4982
5090
|
emptyNudged = true;
|
|
4983
|
-
|
|
5091
|
+
log8.warn(`empty model turn after tool work (step ${steps}, provider finishReason=${res.finishReason ?? "n/a"}) \u2014 nudging once`);
|
|
4984
5092
|
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." });
|
|
4985
5093
|
continue;
|
|
4986
5094
|
}
|
|
4987
|
-
|
|
5095
|
+
log8.warn(`model returned empty turns after tool work \u2014 surfacing as failed`);
|
|
4988
5096
|
const e = new Error("empty_response: model returned an empty turn (no text, no tools) after a re-prompt");
|
|
4989
5097
|
e.code = "empty";
|
|
4990
5098
|
return { text: "", steps, finishReason: "empty", ...res.finishReason ? { providerFinishReason: res.finishReason } : {}, messages: this.transcript, usage, usageEstimated, error: e };
|
|
4991
5099
|
}
|
|
4992
5100
|
const fabricated = o.catchFabricatedToolUse && !closureNudged ? this.fabricatedToolNames(contentText(res.content ?? "")) : [];
|
|
4993
5101
|
for (const n of fabricated) this.fabricatedClaims.add(n);
|
|
4994
|
-
if (fabricated.length && !canNudge())
|
|
5102
|
+
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`);
|
|
4995
5103
|
if (fabricated.length && canNudge()) {
|
|
4996
5104
|
closureNudged = true;
|
|
4997
|
-
|
|
5105
|
+
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`);
|
|
4998
5106
|
this.transcript.push({
|
|
4999
5107
|
role: "user",
|
|
5000
5108
|
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.`
|
|
@@ -5003,14 +5111,14 @@ var Agent = class _Agent {
|
|
|
5003
5111
|
}
|
|
5004
5112
|
if (o.closeDelegatedTurns && res.endedWithoutClosing && !closureNudged && canNudge()) {
|
|
5005
5113
|
closureNudged = true;
|
|
5006
|
-
|
|
5114
|
+
log8.verbose("delegated turn went silent after a tool \u2014 nudging a closing answer");
|
|
5007
5115
|
this.transcript.push({
|
|
5008
5116
|
role: "user",
|
|
5009
5117
|
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."
|
|
5010
5118
|
});
|
|
5011
5119
|
continue;
|
|
5012
5120
|
}
|
|
5013
|
-
|
|
5121
|
+
log8.verbose(`completed in ${steps} step(s)`);
|
|
5014
5122
|
const drained = await this.jobs.drain(o.signal);
|
|
5015
5123
|
if (o.signal?.aborted) return kill("aborted");
|
|
5016
5124
|
if (drained.length) lastProgressAt = Date.now();
|
|
@@ -5044,7 +5152,7 @@ var Agent = class _Agent {
|
|
|
5044
5152
|
if (this.ctx.needsInput) {
|
|
5045
5153
|
const question = this.ctx.needsInput;
|
|
5046
5154
|
this.ctx.needsInput = void 0;
|
|
5047
|
-
|
|
5155
|
+
log8.warn(`needs_input: ${question.question} (steps=${steps})`);
|
|
5048
5156
|
await this.jobs.drain();
|
|
5049
5157
|
const text = [
|
|
5050
5158
|
`OPEN QUESTION (run paused \u2014 no human was reachable): ${question.question}`,
|
|
@@ -5086,12 +5194,12 @@ var Agent = class _Agent {
|
|
|
5086
5194
|
if (this.toolHoldActive()) {
|
|
5087
5195
|
const now4 = Date.now();
|
|
5088
5196
|
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(", ");
|
|
5089
|
-
|
|
5197
|
+
log8.warn(`stall watchdog: no stream output for ${stallMs}ms \u2014 held by ${who}`);
|
|
5090
5198
|
this.options.host?.notify?.({ kind: "tool_hold", message: `waiting on ${who}` });
|
|
5091
5199
|
return poke();
|
|
5092
5200
|
}
|
|
5093
5201
|
const inFlight = this.jobs.list().filter((j) => j.status === "running");
|
|
5094
|
-
|
|
5202
|
+
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(", ")})` : ""));
|
|
5095
5203
|
fired = true;
|
|
5096
5204
|
ac.abort(stallError());
|
|
5097
5205
|
}, stallMs);
|
|
@@ -5261,13 +5369,13 @@ var Agent = class _Agent {
|
|
|
5261
5369
|
const decision = await this.park(Promise.resolve(hooks?.preToolUse?.(call, meta)));
|
|
5262
5370
|
if (decision?.block) {
|
|
5263
5371
|
const blocked = `Blocked by hook: ${decision.reason ?? "no reason given"}`;
|
|
5264
|
-
|
|
5372
|
+
log8.debug(`${tc.function.name} -> ${blocked}`);
|
|
5265
5373
|
await hooks?.postToolUse?.(call, blocked, meta);
|
|
5266
5374
|
return blocked;
|
|
5267
5375
|
}
|
|
5268
5376
|
this.options.host?.notify?.({ kind: "tool_use", id: tc.id ?? "", name: tc.function.name, input: args });
|
|
5269
5377
|
if (earlyError) {
|
|
5270
|
-
|
|
5378
|
+
log8.debug(`${tc.function.name} -> ${earlyError}`);
|
|
5271
5379
|
await hooks?.postToolUse?.(call, earlyError, meta);
|
|
5272
5380
|
this.options.host?.notify?.({ kind: "tool_result", id: tc.id ?? "", output: earlyError, isError: true });
|
|
5273
5381
|
return earlyError;
|
|
@@ -5276,12 +5384,12 @@ var Agent = class _Agent {
|
|
|
5276
5384
|
let images;
|
|
5277
5385
|
let threw = false;
|
|
5278
5386
|
try {
|
|
5279
|
-
|
|
5387
|
+
log8.debug(`${tc.function.name}(${tc.function.arguments})`);
|
|
5280
5388
|
this.ctx.emit = hooks?.onToolOutput ? (chunk) => {
|
|
5281
5389
|
try {
|
|
5282
5390
|
hooks.onToolOutput(call, chunk, meta);
|
|
5283
5391
|
} catch (e) {
|
|
5284
|
-
|
|
5392
|
+
log8.debug(`onToolOutput hook error: ${e}`);
|
|
5285
5393
|
}
|
|
5286
5394
|
} : void 0;
|
|
5287
5395
|
const raw = await tool.run(args, this.ctx);
|
|
@@ -5293,7 +5401,7 @@ var Agent = class _Agent {
|
|
|
5293
5401
|
}
|
|
5294
5402
|
} catch (e) {
|
|
5295
5403
|
const msg = e instanceof Error ? e.message : String(e);
|
|
5296
|
-
|
|
5404
|
+
log8.debug(`${tc.function.name} -> error: ${msg}`);
|
|
5297
5405
|
result = `Error: ${msg}`;
|
|
5298
5406
|
threw = true;
|
|
5299
5407
|
} finally {
|
|
@@ -5353,6 +5461,13 @@ ${out}`;
|
|
|
5353
5461
|
* 3. token-aware backstop (`maxContextTokens`) — drop oldest messages until under an estimated token ceiling.
|
|
5354
5462
|
* With every knob off, returns the transcript unchanged (same reference).
|
|
5355
5463
|
*/
|
|
5464
|
+
/** Set when the model refused image parts this run: tool image Reads go as text stubs from then on. */
|
|
5465
|
+
imagesOff = false;
|
|
5466
|
+
/** Send tool image Reads as images? Known models by their catalog flag; unknown ones try (and latch off on refusal). */
|
|
5467
|
+
sendsImages() {
|
|
5468
|
+
if (this.imagesOff) return false;
|
|
5469
|
+
return this.options.imageInput?.(this.options.model) ?? true;
|
|
5470
|
+
}
|
|
5356
5471
|
trimContext() {
|
|
5357
5472
|
const o = this.options;
|
|
5358
5473
|
const m = this.transcript;
|
|
@@ -5362,17 +5477,19 @@ ${out}`;
|
|
|
5362
5477
|
const browserKeep = this.browserActive ? o.browserTrim?.keepOutputs : void 0;
|
|
5363
5478
|
const keep = browserKeep != null ? o.keepToolOutputs ? Math.min(o.keepToolOutputs, browserKeep) : browserKeep : o.keepToolOutputs;
|
|
5364
5479
|
if (keep) out = stubOldToolResults(out ?? m, keep);
|
|
5365
|
-
out = expandImagesForSend(dropOrphanToolResults(out ?? m), o.keepRecentImages ?? 1);
|
|
5480
|
+
out = expandImagesForSend(dropOrphanToolResults(out ?? m), o.keepRecentImages ?? 1, void 0, this.sendsImages());
|
|
5366
5481
|
if (o.maxContextTokens) {
|
|
5367
5482
|
const pre = out.length;
|
|
5368
|
-
out = fitTokenBudget(out, o.maxContextTokens, o.keepRecentImages ?? 1);
|
|
5483
|
+
out = fitTokenBudget(out, o.maxContextTokens, o.keepRecentImages ?? 1, this.sendsImages());
|
|
5369
5484
|
const dropped = pre - out.length;
|
|
5370
5485
|
if (dropped > 0 && dropped !== this.lastTrimNotified) {
|
|
5371
5486
|
this.lastTrimNotified = dropped;
|
|
5372
5487
|
o.host?.notify?.({ kind: "compaction", message: `context full \u2014 auto-trimmed ${dropped} oldest message(s) to fit ~${Math.round(o.maxContextTokens / 1e3)}k tokens` });
|
|
5373
5488
|
}
|
|
5374
5489
|
}
|
|
5375
|
-
|
|
5490
|
+
out = dropOrphanToolResults(out);
|
|
5491
|
+
if (/^google\//.test(o.model ?? "")) out = hoistToolImages(out);
|
|
5492
|
+
return scrubBase64(out);
|
|
5376
5493
|
}
|
|
5377
5494
|
};
|
|
5378
5495
|
function dropOldest(m, max) {
|
|
@@ -5381,8 +5498,8 @@ function dropOldest(m, max) {
|
|
|
5381
5498
|
}
|
|
5382
5499
|
var ANTHROPIC_WINDOW_WITHOUT_1M_BETA = 2e5;
|
|
5383
5500
|
var isAnthropicModel = (m) => !!m && (m.startsWith("anthropic/") || /(^|\/)claude-/.test(m));
|
|
5384
|
-
function estimateTokens(m, keepRecentImages = 1) {
|
|
5385
|
-
return Math.ceil(sendBytes(m, keepRecentImages) / 4);
|
|
5501
|
+
function estimateTokens(m, keepRecentImages = 1, vision = true) {
|
|
5502
|
+
return Math.ceil(sendBytes(m, keepRecentImages, vision) / 4);
|
|
5386
5503
|
}
|
|
5387
5504
|
var TRANSPORT_TOOL_TIMEOUT_FLOOR_MS = 18e5;
|
|
5388
5505
|
var isMcpTool = (t) => t.name.startsWith("mcp__") || t.name === "McpCall";
|
|
@@ -5551,8 +5668,8 @@ function dropOrphanToolResults(messages) {
|
|
|
5551
5668
|
const ok = (m) => m.role !== "tool" || ids.has(m.tool_call_id ?? "");
|
|
5552
5669
|
return messages.every(ok) ? messages : messages.filter(ok);
|
|
5553
5670
|
}
|
|
5554
|
-
function fitTokenBudget(messages, maxTokens, keepRecentImages = 1) {
|
|
5555
|
-
const per = sendBytesPer(messages, keepRecentImages).map((b) => Math.ceil(b / 4));
|
|
5671
|
+
function fitTokenBudget(messages, maxTokens, keepRecentImages = 1, vision = true) {
|
|
5672
|
+
const per = sendBytesPer(messages, keepRecentImages, vision).map((b) => Math.ceil(b / 4));
|
|
5556
5673
|
let total = per.reduce((a, b) => a + b, 0);
|
|
5557
5674
|
if (total <= maxTokens) return messages;
|
|
5558
5675
|
const head = messages[0]?.role === "system" ? [messages[0]] : [];
|
|
@@ -5561,7 +5678,7 @@ function fitTokenBudget(messages, maxTokens, keepRecentImages = 1) {
|
|
|
5561
5678
|
const ids = callIdSet(messages.slice(from));
|
|
5562
5679
|
while (from < messages.length && messages[from].role === "tool" && !ids.has(messages[from].tool_call_id ?? "")) total -= per[from++];
|
|
5563
5680
|
if (total > maxTokens)
|
|
5564
|
-
|
|
5681
|
+
log8.warn(`context ~${total} tok still over maxContextTokens=${maxTokens} after trimming (system head can't be dropped)`);
|
|
5565
5682
|
return [...head, ...messages.slice(from)];
|
|
5566
5683
|
}
|
|
5567
5684
|
function compact(m, max, focus) {
|
|
@@ -6139,7 +6256,7 @@ init_tools_web();
|
|
|
6139
6256
|
init_tools();
|
|
6140
6257
|
init_tools_structured();
|
|
6141
6258
|
init_logging();
|
|
6142
|
-
var
|
|
6259
|
+
var log9 = forComponent("scratch");
|
|
6143
6260
|
var SCRATCH_DIR = "/scratch";
|
|
6144
6261
|
function shortArgs(args) {
|
|
6145
6262
|
try {
|
|
@@ -6179,7 +6296,7 @@ var Scratch = class {
|
|
|
6179
6296
|
await (this.dirReady ??= mkdirp(this.fs, dir));
|
|
6180
6297
|
await this.fs.writeFile(path, header + raw);
|
|
6181
6298
|
} catch (e) {
|
|
6182
|
-
|
|
6299
|
+
log9.debug("scratch write failed; returning raw", e);
|
|
6183
6300
|
return raw;
|
|
6184
6301
|
}
|
|
6185
6302
|
const preview = raw.slice(0, previewChars).replace(/\s+/g, " ").trim();
|
|
@@ -6209,7 +6326,7 @@ To pull a specific detail, Grep/Read ${path}, or call Ask({ question: "\u2026",
|
|
|
6209
6326
|
await (this.dirReady ??= mkdirp(this.fs, dir));
|
|
6210
6327
|
await this.fs.writeFile(path, header + full);
|
|
6211
6328
|
} catch (e) {
|
|
6212
|
-
|
|
6329
|
+
log9.debug("scratch spill failed; cropping lossy", e);
|
|
6213
6330
|
return full.slice(0, pageBytes) + `
|
|
6214
6331
|
|
|
6215
6332
|
[output cropped to ${pageBytes} of ${full.length} bytes; full output unavailable (scratch write failed) \u2014 refine your query]`;
|
|
@@ -6255,7 +6372,7 @@ Question: ${q2}`);
|
|
|
6255
6372
|
const answer = (res.text ?? "").trim();
|
|
6256
6373
|
return answer || "(no answer found in scratch)";
|
|
6257
6374
|
} catch (e) {
|
|
6258
|
-
|
|
6375
|
+
log9.debug("Ask peek failed", e);
|
|
6259
6376
|
return `Error querying scratch: ${e?.message ?? e}`;
|
|
6260
6377
|
}
|
|
6261
6378
|
}
|
|
@@ -6264,7 +6381,7 @@ Question: ${q2}`);
|
|
|
6264
6381
|
|
|
6265
6382
|
// src/lessons.ts
|
|
6266
6383
|
init_logging();
|
|
6267
|
-
var
|
|
6384
|
+
var log10 = forComponent("Lessons");
|
|
6268
6385
|
var LessonOptionsDefaults = class {
|
|
6269
6386
|
minRepeats = 2;
|
|
6270
6387
|
};
|
|
@@ -6289,8 +6406,8 @@ function lessonCapture(options) {
|
|
|
6289
6406
|
counts.set(lesson.slug, n);
|
|
6290
6407
|
if (n < o.minRepeats) return;
|
|
6291
6408
|
written.add(lesson.slug);
|
|
6292
|
-
await writeFact(o.fs, o.dir, lesson.slug, lesson.body).catch((e) =>
|
|
6293
|
-
|
|
6409
|
+
await writeFact(o.fs, o.dir, lesson.slug, lesson.body).catch((e) => log10.warn(`could not persist ${lesson.slug}: ${e?.message ?? e}`));
|
|
6410
|
+
log10.debug(`captured lesson ${lesson.slug} (recurred ${n}\xD7)`);
|
|
6294
6411
|
}
|
|
6295
6412
|
};
|
|
6296
6413
|
}
|
|
@@ -6298,7 +6415,7 @@ function lessonCapture(options) {
|
|
|
6298
6415
|
// src/reflect.ts
|
|
6299
6416
|
init_llm();
|
|
6300
6417
|
init_logging();
|
|
6301
|
-
var
|
|
6418
|
+
var log11 = forComponent("Reflect");
|
|
6302
6419
|
async function reflectOnRun(o) {
|
|
6303
6420
|
const digest = digestRun(o.result.messages, o.maxDigestChars ?? 6e3);
|
|
6304
6421
|
if (!digest.trim()) return null;
|
|
@@ -6314,7 +6431,7 @@ If the run was fine or the issue was purely task-specific (not generalizable), r
|
|
|
6314
6431
|
const r = await o.ai.chat({ model: o.model, messages: [{ role: "user", content: prompt }], stream: false });
|
|
6315
6432
|
text = r?.content ?? "";
|
|
6316
6433
|
} catch (e) {
|
|
6317
|
-
|
|
6434
|
+
log11.warn(`reflection call failed: ${e?.message ?? e}`);
|
|
6318
6435
|
return null;
|
|
6319
6436
|
}
|
|
6320
6437
|
const m = text.match(/LESSON:\s*(.+)/i);
|
|
@@ -6324,10 +6441,10 @@ If the run was fine or the issue was purely task-specific (not generalizable), r
|
|
|
6324
6441
|
try {
|
|
6325
6442
|
await writeFact(o.fs, o.dir, slug2, lesson);
|
|
6326
6443
|
} catch (e) {
|
|
6327
|
-
|
|
6444
|
+
log11.warn(`could not persist lesson: ${e?.message ?? e}`);
|
|
6328
6445
|
return null;
|
|
6329
6446
|
}
|
|
6330
|
-
|
|
6447
|
+
log11.debug(`reflection persisted ${slug2}`);
|
|
6331
6448
|
return slug2;
|
|
6332
6449
|
}
|
|
6333
6450
|
function digestRun(messages, maxChars) {
|
|
@@ -6376,7 +6493,7 @@ function forComponent2(name) {
|
|
|
6376
6493
|
}
|
|
6377
6494
|
|
|
6378
6495
|
// node_modules/@bod.ee/voice/src/core/emotion.ts
|
|
6379
|
-
var
|
|
6496
|
+
var log12 = forComponent2("Emotion");
|
|
6380
6497
|
var EMOTIONS = [
|
|
6381
6498
|
// primary (best results)
|
|
6382
6499
|
"neutral",
|
|
@@ -6508,7 +6625,7 @@ function renderEmotions(text, opts = { show: true }) {
|
|
|
6508
6625
|
speech += cartesiaTag(tag);
|
|
6509
6626
|
if (opts.show) display += m[0];
|
|
6510
6627
|
} else {
|
|
6511
|
-
|
|
6628
|
+
log12.debug(`dropping unknown emotion tag ${m[0]}`);
|
|
6512
6629
|
}
|
|
6513
6630
|
last = m.index + m[0].length;
|
|
6514
6631
|
}
|
|
@@ -6550,7 +6667,7 @@ var EmotionStream = class {
|
|
|
6550
6667
|
if (tag) {
|
|
6551
6668
|
this.pending = tag;
|
|
6552
6669
|
if (this.show) display += m[0];
|
|
6553
|
-
} else
|
|
6670
|
+
} else log12.debug(`dropping unknown emotion tag ${m[0]}`);
|
|
6554
6671
|
last = m.index + m[0].length;
|
|
6555
6672
|
}
|
|
6556
6673
|
this.emit(body.slice(last), (s, d, p) => {
|
|
@@ -6697,7 +6814,7 @@ function computeHoldMs(ctx) {
|
|
|
6697
6814
|
}
|
|
6698
6815
|
|
|
6699
6816
|
// node_modules/@bod.ee/voice/src/core/engine.ts
|
|
6700
|
-
var
|
|
6817
|
+
var log13 = forComponent2("VoiceEngine");
|
|
6701
6818
|
var realClock = {
|
|
6702
6819
|
now: () => performance.now(),
|
|
6703
6820
|
setTimeout: (fn, ms) => setTimeout(fn, ms),
|
|
@@ -7085,8 +7202,8 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
7085
7202
|
this.tts.warmup?.();
|
|
7086
7203
|
this.setState("listening");
|
|
7087
7204
|
if (this.options.bargeInMode === "aec" && !this.stt.usingAec)
|
|
7088
|
-
|
|
7089
|
-
|
|
7205
|
+
log13.warn("bargeInMode=aec but mic does not report echoCancellation applied \u2014 STT may transcribe the assistant");
|
|
7206
|
+
log13.debug(`voice I/O up (${this.usingAec ? "AEC" : "heuristic echo"} capture, mode=${this.options.bargeInMode}, mic reports aec=${this.stt.usingAec})`);
|
|
7090
7207
|
}
|
|
7091
7208
|
/** The RESOLVED barge-in tier (the single seam every tier branch reads). `bargeInMode` overrides
|
|
7092
7209
|
* the mic's self-report — see the option doc for why the report is not trustworthy on mobile. */
|
|
@@ -7111,7 +7228,7 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
7111
7228
|
this.options.onDiag({ t: this.clock.now(), kind, ...fields });
|
|
7112
7229
|
} catch (e) {
|
|
7113
7230
|
this.diagOn = false;
|
|
7114
|
-
|
|
7231
|
+
log13.debug(`onDiag threw \u2014 diagnostics disabled: ${e instanceof Error ? e.message : e}`);
|
|
7115
7232
|
}
|
|
7116
7233
|
}
|
|
7117
7234
|
idleWaiters = [];
|
|
@@ -7250,7 +7367,7 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
7250
7367
|
}
|
|
7251
7368
|
for (const w of this.words(this.reply)) this.echoWords.add(w);
|
|
7252
7369
|
this.tts.speak(forSpeech(speech), true);
|
|
7253
|
-
if (!this.spokeDeltas && this.turnStartAt)
|
|
7370
|
+
if (!this.spokeDeltas && this.turnStartAt) log13.debug(`ttft: ${Math.round(this.clock.now() - this.turnStartAt)}ms`);
|
|
7254
7371
|
this.spokeDeltas = true;
|
|
7255
7372
|
this.setState("speaking");
|
|
7256
7373
|
if (this.options.revealMode !== "off" && display) {
|
|
@@ -7363,13 +7480,13 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
7363
7480
|
this.drainTimer = null;
|
|
7364
7481
|
this.speaking = false;
|
|
7365
7482
|
this.stopWordReveal(true);
|
|
7366
|
-
if (this.turnStartAt)
|
|
7483
|
+
if (this.turnStartAt) log13.debug(`turn: ${Math.round(this.clock.now() - this.turnStartAt)}ms (incl. playback)`);
|
|
7367
7484
|
this.echoUntil = this.clock.now() + 2500;
|
|
7368
7485
|
if (!this.usingAec) this.stt.reset();
|
|
7369
7486
|
try {
|
|
7370
7487
|
this.options.onSpeechEnd();
|
|
7371
7488
|
} catch (e) {
|
|
7372
|
-
|
|
7489
|
+
log13.warn(`onSpeechEnd threw: ${e instanceof Error ? e.message : e}`);
|
|
7373
7490
|
}
|
|
7374
7491
|
this.setState("listening");
|
|
7375
7492
|
if (this.uttQueue.length) this.pumpQueue();
|
|
@@ -7443,7 +7560,7 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
7443
7560
|
if (!host) return pick;
|
|
7444
7561
|
const ctx = { text: this.lastDispatchText, question: this.lastDispatchWasQuestion, barged: this.bargedBeforeDispatch };
|
|
7445
7562
|
const fallback = (e) => {
|
|
7446
|
-
|
|
7563
|
+
log13.warn(`pickAck failed \u2014 using the default pools: ${e instanceof Error ? e.message : e}`);
|
|
7447
7564
|
pick.picked = false;
|
|
7448
7565
|
return void 0;
|
|
7449
7566
|
};
|
|
@@ -7533,7 +7650,7 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
7533
7650
|
this.ackProbe = null;
|
|
7534
7651
|
const queued = this.uttQueue.splice(0);
|
|
7535
7652
|
const droppedQueued = queued.length;
|
|
7536
|
-
if (droppedQueued)
|
|
7653
|
+
if (droppedQueued) log13.info(`barge-in cut ${droppedQueued} queued worker utterance(s) \u2014 handed to the host`);
|
|
7537
7654
|
this.overlapKeptUntil = 0;
|
|
7538
7655
|
if (!this.speaking && !this.drainTimer) {
|
|
7539
7656
|
if (droppedQueued) this.lastInterrupted = { full: "", heard: "", queued, queuedTurn: false };
|
|
@@ -7762,7 +7879,7 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
7762
7879
|
if (flat && this.lastDispatchFlat && this.clock.now() - this.lastDispatchAt < _VoiceEngine.DUP_FINAL_MS && (this.lastDispatchFlat.startsWith(flat) || flat.startsWith(this.lastDispatchFlat))) return;
|
|
7763
7880
|
this.specSpent = true;
|
|
7764
7881
|
this.specText = this.specPartial;
|
|
7765
|
-
|
|
7882
|
+
log13.debug(`speculate: "${this.specText.slice(0, 60)}"`);
|
|
7766
7883
|
this.diag("speculate", { text: this.specText });
|
|
7767
7884
|
this.options.onSpeculate(this.specText);
|
|
7768
7885
|
}, this.options.speculativeMs);
|
|
@@ -7831,7 +7948,7 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
7831
7948
|
};
|
|
7832
7949
|
this.tts.newContext();
|
|
7833
7950
|
this.tts.speak(phrase, false);
|
|
7834
|
-
|
|
7951
|
+
log13.debug(`backchannel: "${phrase}"`);
|
|
7835
7952
|
this.diag("backchannel", { phrase });
|
|
7836
7953
|
this.options.onBackchannel(phrase);
|
|
7837
7954
|
}
|
|
@@ -7864,7 +7981,7 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
7864
7981
|
});
|
|
7865
7982
|
if (idx < 0) return text;
|
|
7866
7983
|
toks.splice(idx, 1);
|
|
7867
|
-
|
|
7984
|
+
log13.verbose(`stripped backchannel echo "${this.lastBcPhrase}" from dispatching final`);
|
|
7868
7985
|
return toks.join(" ");
|
|
7869
7986
|
}
|
|
7870
7987
|
/** Merge a resumed utterance into the pending one, deduping any word-overlap. Soniox re-finalizes
|
|
@@ -8050,7 +8167,7 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
8050
8167
|
}
|
|
8051
8168
|
const bargeHold = !this.looksIncomplete(this.pendingUtt) && this.unfinishedBargeLine(this.pendingUtt);
|
|
8052
8169
|
if (this.options.incompleteMergeMs && (bargeHold || this.looksIncomplete(this.pendingUtt))) {
|
|
8053
|
-
|
|
8170
|
+
log13.verbose(`hold: incomplete utterance "${this.pendingUtt.slice(-40)}"`);
|
|
8054
8171
|
this.diag("hold", { reason: bargeHold ? "barge_unfinished" : "incomplete", tail: this.pendingUtt.slice(-40) });
|
|
8055
8172
|
this.options.onHold();
|
|
8056
8173
|
if (this.options.holdFiller && !this.speaking) this.speakFiller(this.options.holdFiller);
|
|
@@ -8089,13 +8206,13 @@ var VoiceEngine = class _VoiceEngine {
|
|
|
8089
8206
|
const answered = this.answeredSinceDispatch();
|
|
8090
8207
|
const part = flat !== this.lastDispatchFlat && !answered && this.echoesDispatch(text);
|
|
8091
8208
|
if (part || flat && flat === this.lastDispatchFlat && !answered && this.clock.now() - this.lastDispatchAt < _VoiceEngine.DUP_FINAL_MS) {
|
|
8092
|
-
|
|
8209
|
+
log13.verbose(`dropped duplicate final "${text.slice(0, 40)}" (identical to the just-dispatched utterance, no reply between)`);
|
|
8093
8210
|
this.diag("utterance_drop", { reason: "dup_final", text: text.slice(0, 60) });
|
|
8094
8211
|
return;
|
|
8095
8212
|
}
|
|
8096
8213
|
const refinal = this.refinalizes(text);
|
|
8097
8214
|
if (refinal && this.repliedSinceDispatch) {
|
|
8098
|
-
|
|
8215
|
+
log13.verbose(`dropped re-finalization "${text.slice(0, 60)}" (superset of the just-dispatched utterance, reply already flowing)`);
|
|
8099
8216
|
this.diag("utterance_drop", { reason: "refinalized", text: text.slice(0, 60) });
|
|
8100
8217
|
this.lastDispatchFlat = flat;
|
|
8101
8218
|
this.lastDispatchWords = text.trim().split(/\s+/);
|
|
@@ -8374,7 +8491,7 @@ var SpokenSplitter = class {
|
|
|
8374
8491
|
};
|
|
8375
8492
|
|
|
8376
8493
|
// node_modules/@bod.ee/voice/src/adapters/soniox.ts
|
|
8377
|
-
var
|
|
8494
|
+
var log14 = forComponent2("SonioxSTT");
|
|
8378
8495
|
var now = () => performance.now();
|
|
8379
8496
|
var SonioxSTTOptions = class {
|
|
8380
8497
|
auth = "";
|
|
@@ -8419,7 +8536,7 @@ var SonioxSTT = class {
|
|
|
8419
8536
|
this.onDiag({ t: now(), kind, ...fields });
|
|
8420
8537
|
} catch (e) {
|
|
8421
8538
|
this.diagOn = false;
|
|
8422
|
-
|
|
8539
|
+
log14.debug(`onDiag threw \u2014 STT diagnostics disabled: ${e instanceof Error ? e.message : e}`);
|
|
8423
8540
|
}
|
|
8424
8541
|
}
|
|
8425
8542
|
lastChunkAt = 0;
|
|
@@ -8461,11 +8578,11 @@ var SonioxSTT = class {
|
|
|
8461
8578
|
this.ws.onmessage = (ev) => this.handle(JSON.parse(String(ev.data)));
|
|
8462
8579
|
this.ws.onclose = (ev) => {
|
|
8463
8580
|
if (this.stopped) return;
|
|
8464
|
-
|
|
8581
|
+
log14.warn(`soniox ws closed (${ev.code} ${ev.reason || ""}) \u2014 reconnecting`);
|
|
8465
8582
|
this.diag("stt_ws_closed", { code: ev.code, reason: String(ev.reason || ""), reconnecting: true });
|
|
8466
8583
|
this.reset();
|
|
8467
8584
|
this.connectWs().catch((e) => {
|
|
8468
|
-
|
|
8585
|
+
log14.error(`soniox reconnect failed: ${e.message}`);
|
|
8469
8586
|
this.diag("stt_reconnect_failed", { message: e.message });
|
|
8470
8587
|
});
|
|
8471
8588
|
};
|
|
@@ -8477,7 +8594,7 @@ var SonioxSTT = class {
|
|
|
8477
8594
|
this.endpointTimer = setInterval(() => {
|
|
8478
8595
|
const combined = (this.finalText + this.partialText).trim();
|
|
8479
8596
|
if (!combined || now() - this.lastChangeAt < this.options.silenceEndpointMs) return;
|
|
8480
|
-
if (this.firstTokenAt)
|
|
8597
|
+
if (this.firstTokenAt) log14.debug(`stt: ${Math.round(now() - this.firstTokenAt)}ms first-token\u2192silence-endpoint, "${combined.slice(0, 60)}"`);
|
|
8481
8598
|
this.reset();
|
|
8482
8599
|
this.onUtterance(combined, now());
|
|
8483
8600
|
}, 120);
|
|
@@ -8489,7 +8606,7 @@ var SonioxSTT = class {
|
|
|
8489
8606
|
if (this.stopped) return;
|
|
8490
8607
|
const ref = this.lastChunkAt || this.startedChunksAt;
|
|
8491
8608
|
if (now() - ref > noAudioMs) {
|
|
8492
|
-
|
|
8609
|
+
log14.error(`stt: no mic audio for >${Math.round(noAudioMs / 1e3)}s \u2014 capture device stopped delivering`);
|
|
8493
8610
|
this.diag("stt_watchdog_fatal", { noAudioMs });
|
|
8494
8611
|
this.onFatal("microphone stopped delivering audio (try a different input device, e.g. AirPods, or check System Settings \u2192 Sound \u2192 Input)");
|
|
8495
8612
|
this.stop();
|
|
@@ -8512,7 +8629,7 @@ var SonioxSTT = class {
|
|
|
8512
8629
|
handle(m) {
|
|
8513
8630
|
if (m.error_message) {
|
|
8514
8631
|
this.diag("stt_error", { message: String(m.error_message), code: m.error_code });
|
|
8515
|
-
return
|
|
8632
|
+
return log14.error(`soniox: ${m.error_message}`);
|
|
8516
8633
|
}
|
|
8517
8634
|
let endpoint = false;
|
|
8518
8635
|
for (const t of m.tokens ?? []) {
|
|
@@ -8529,7 +8646,7 @@ var SonioxSTT = class {
|
|
|
8529
8646
|
this.onPartial(combined);
|
|
8530
8647
|
if (endpoint && this.finalText.trim()) {
|
|
8531
8648
|
const utterance = this.finalText.trim();
|
|
8532
|
-
if (this.firstTokenAt)
|
|
8649
|
+
if (this.firstTokenAt) log14.debug(`stt: ${Math.round(now() - this.firstTokenAt)}ms first-token\u2192endpoint, "${utterance.slice(0, 60)}"`);
|
|
8533
8650
|
this.reset();
|
|
8534
8651
|
this.onUtterance(utterance, now());
|
|
8535
8652
|
}
|
|
@@ -8551,7 +8668,7 @@ var SonioxSTT = class {
|
|
|
8551
8668
|
};
|
|
8552
8669
|
|
|
8553
8670
|
// node_modules/@bod.ee/voice/src/adapters/cartesia.ts
|
|
8554
|
-
var
|
|
8671
|
+
var log15 = forComponent2("CartesiaTTS");
|
|
8555
8672
|
var now2 = () => performance.now();
|
|
8556
8673
|
var CartesiaTTSOptions = class {
|
|
8557
8674
|
auth = "";
|
|
@@ -8587,7 +8704,7 @@ var CartesiaTTS = class _CartesiaTTS {
|
|
|
8587
8704
|
this.onDiag({ t: now2(), kind, ...fields });
|
|
8588
8705
|
} catch (e) {
|
|
8589
8706
|
this.diagOn = false;
|
|
8590
|
-
|
|
8707
|
+
log15.debug(`onDiag threw \u2014 TTS diagnostics disabled: ${e instanceof Error ? e.message : e}`);
|
|
8591
8708
|
}
|
|
8592
8709
|
}
|
|
8593
8710
|
firstAudioAt = 0;
|
|
@@ -8622,11 +8739,11 @@ var CartesiaTTS = class _CartesiaTTS {
|
|
|
8622
8739
|
this.ws.onerror = (e) => rej(new Error(`cartesia ws: ${e.message || "connect failed"}`));
|
|
8623
8740
|
});
|
|
8624
8741
|
this.ws.onclose = (ev) => {
|
|
8625
|
-
|
|
8742
|
+
log15.warn(`cartesia ws closed (${ev.code} ${ev.reason || ""})`);
|
|
8626
8743
|
this.diag("tts_ws_closed", { code: ev.code, reason: String(ev.reason || ""), reconnecting: !this.closed });
|
|
8627
8744
|
if (!this.closed) {
|
|
8628
8745
|
this.connecting = this.doConnect().catch((e) => {
|
|
8629
|
-
|
|
8746
|
+
log15.error(`cartesia reconnect failed: ${e.message}`);
|
|
8630
8747
|
this.diag("tts_reconnect_failed", { message: e.message });
|
|
8631
8748
|
});
|
|
8632
8749
|
}
|
|
@@ -8654,12 +8771,12 @@ var CartesiaTTS = class _CartesiaTTS {
|
|
|
8654
8771
|
this.down = true;
|
|
8655
8772
|
this.downAt = now2();
|
|
8656
8773
|
this.consecutiveOk = 0;
|
|
8657
|
-
|
|
8774
|
+
log15.warn(`TTS circuit breaker open \u2014 ${this.consecutiveErrors} consecutive errors, switching to text-only`);
|
|
8658
8775
|
this.diag("tts_breaker_open", { errors: this.consecutiveErrors });
|
|
8659
8776
|
this.onDone();
|
|
8660
8777
|
this.startProbe();
|
|
8661
8778
|
} else if (!this.down) {
|
|
8662
|
-
(/No valid transcripts/i.test(m.message || "") ?
|
|
8779
|
+
(/No valid transcripts/i.test(m.message || "") ? log15.debug : log15.warn)(`cartesia: ${JSON.stringify(m)}`);
|
|
8663
8780
|
}
|
|
8664
8781
|
}
|
|
8665
8782
|
};
|
|
@@ -8674,7 +8791,7 @@ var CartesiaTTS = class _CartesiaTTS {
|
|
|
8674
8791
|
this.stopProbe();
|
|
8675
8792
|
const downMs = this.downAt ? now2() - this.downAt : 0;
|
|
8676
8793
|
this.diag("tts_breaker_close", { downMs: Math.round(downMs) });
|
|
8677
|
-
(downMs < 2e3 ?
|
|
8794
|
+
(downMs < 2e3 ? log15.debug : log15.info)(`TTS recovered${downMs ? ` (down ${downMs}ms)` : ""}`);
|
|
8678
8795
|
}
|
|
8679
8796
|
/** Ensure the WS is open before sending — reconnects if idle-closed. */
|
|
8680
8797
|
async ensureConnected() {
|
|
@@ -8757,13 +8874,13 @@ function base64ToBytes(b64) {
|
|
|
8757
8874
|
}
|
|
8758
8875
|
|
|
8759
8876
|
// node_modules/@bod.ee/voice/src/adapters/fish.ts
|
|
8760
|
-
var
|
|
8877
|
+
var log16 = forComponent2("FishTTS");
|
|
8761
8878
|
|
|
8762
8879
|
// node_modules/@bod.ee/voice/src/adapters/grok.ts
|
|
8763
|
-
var
|
|
8880
|
+
var log17 = forComponent2("GrokTTS");
|
|
8764
8881
|
|
|
8765
8882
|
// node_modules/@bod.ee/voice/src/adapters/grok-stt.ts
|
|
8766
|
-
var
|
|
8883
|
+
var log18 = forComponent2("GrokSTT");
|
|
8767
8884
|
|
|
8768
8885
|
// src/voice/engine.ts
|
|
8769
8886
|
init_logging();
|
|
@@ -8775,7 +8892,7 @@ configureLogging((name) => {
|
|
|
8775
8892
|
|
|
8776
8893
|
// src/voice/instinct.ts
|
|
8777
8894
|
init_logging();
|
|
8778
|
-
var
|
|
8895
|
+
var log19 = forComponent("Instinct");
|
|
8779
8896
|
var InstinctOptions = class {
|
|
8780
8897
|
decider;
|
|
8781
8898
|
/** Partial unchanged this long (a micro-pause) → consult. */
|
|
@@ -9012,13 +9129,13 @@ var Instinct = class {
|
|
|
9012
9129
|
* unfinished partial is no turn to react to — the interjection itself is the reply. */
|
|
9013
9130
|
pickAck(ctx) {
|
|
9014
9131
|
if (this.interjecting) {
|
|
9015
|
-
|
|
9132
|
+
log19.debug(`ack: cut-in turn \u2192 silent (engine context "${ctx.text.slice(0, 40)}" is the previous final)`);
|
|
9016
9133
|
return Promise.resolve(null);
|
|
9017
9134
|
}
|
|
9018
9135
|
const text = ctx.text.trim();
|
|
9019
9136
|
const p = (async () => {
|
|
9020
9137
|
const intentP = ctx.barged ? this.bargeIntent(text).catch((e) => {
|
|
9021
|
-
|
|
9138
|
+
log19.warn(`barge intent for the ack failed \u2014 situational ack: ${e.message}`);
|
|
9022
9139
|
return null;
|
|
9023
9140
|
}) : null;
|
|
9024
9141
|
const d = await this.ask(
|
|
@@ -9040,7 +9157,7 @@ var Instinct = class {
|
|
|
9040
9157
|
settle = r;
|
|
9041
9158
|
});
|
|
9042
9159
|
const timer = setTimeout(() => {
|
|
9043
|
-
|
|
9160
|
+
log19.debug(`ack: no engine outcome in ${this.o.ackSettleMs}ms \u2014 not claimed`);
|
|
9044
9161
|
settle(null);
|
|
9045
9162
|
}, this.o.ackSettleMs);
|
|
9046
9163
|
void spoken.then(() => clearTimeout(timer));
|
|
@@ -9078,7 +9195,7 @@ var Instinct = class {
|
|
|
9078
9195
|
});
|
|
9079
9196
|
return { kind, heard, choice: answers.act.choice, probabilities: answers.act.probabilities, ms: Math.round(this.o.now() - t0) };
|
|
9080
9197
|
} catch (e) {
|
|
9081
|
-
|
|
9198
|
+
log19.warn(`${kind} decision failed \u2014 falling back to engine defaults: ${e.message}`);
|
|
9082
9199
|
if (rethrow) throw e;
|
|
9083
9200
|
return null;
|
|
9084
9201
|
}
|
|
@@ -9087,7 +9204,7 @@ var Instinct = class {
|
|
|
9087
9204
|
|
|
9088
9205
|
// src/voice/unsaid.ts
|
|
9089
9206
|
init_logging();
|
|
9090
|
-
var
|
|
9207
|
+
var log20 = forComponent("Unsaid");
|
|
9091
9208
|
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(" "));
|
|
9092
9209
|
var contentStems = (s) => (s.toLowerCase().match(/[a-z0-9']+/g) ?? []).filter((w) => w.length >= 4 && !STOP2.has(w)).map((w) => w.slice(0, 4));
|
|
9093
9210
|
var SAID_OVERLAP = 0.6;
|
|
@@ -9127,7 +9244,7 @@ async function unsaidClauses(pieces, spoken, decider, timeoutMs = 5e3) {
|
|
|
9127
9244
|
return p < CONVEYED_MIN;
|
|
9128
9245
|
}).map((x) => x.c);
|
|
9129
9246
|
} catch (e) {
|
|
9130
|
-
|
|
9247
|
+
log20.warn(`unsaid judge failed \u2014 only near-zero-overlap clauses count as unsaid: ${e.message}`);
|
|
9131
9248
|
return fallback();
|
|
9132
9249
|
} finally {
|
|
9133
9250
|
clearTimeout(timer);
|
|
@@ -9198,7 +9315,7 @@ async function judgeLeak(decider, sentence, userSaid) {
|
|
|
9198
9315
|
}
|
|
9199
9316
|
|
|
9200
9317
|
// src/duplex.ts
|
|
9201
|
-
var
|
|
9318
|
+
var log21 = forComponent("DuplexAgent");
|
|
9202
9319
|
function describeCall(call) {
|
|
9203
9320
|
const v = call.args && Object.values(call.args).find((x) => typeof x === "string" && x.trim());
|
|
9204
9321
|
const hint = v ? ` (${String(v).replace(/\s+/g, " ").trim().slice(0, 48)})` : "";
|
|
@@ -9458,7 +9575,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
9458
9575
|
const m = this.reflexBuf.match(RESERVED_EVENT_MARKER) ?? this.reflexBuf.match(RESERVED_EVENT_OPENER);
|
|
9459
9576
|
if (m) {
|
|
9460
9577
|
this.fabricationCut = true;
|
|
9461
|
-
|
|
9578
|
+
log21.warn(`reflex fabricated a [task \u2026] event in its spoken stream \u2014 cutting it (kept ${m.index} chars)`);
|
|
9462
9579
|
this.reflexBuf = this.reflexBuf.slice(0, Math.max(m.index, this.reflexForwarded));
|
|
9463
9580
|
this.pumpReflex(true);
|
|
9464
9581
|
return;
|
|
@@ -9550,7 +9667,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
9550
9667
|
const at = from + ok;
|
|
9551
9668
|
this.leakHit = true;
|
|
9552
9669
|
this.leakCuts.push(leak.trim().slice(0, 40));
|
|
9553
|
-
|
|
9670
|
+
log21.warn(`reflex spoke its own analysis ("${leak.trim().slice(0, 40)}\u2026") \u2014 cut before TTS`);
|
|
9554
9671
|
this.notify("diag", "analysis_leak", { text: leak.trim().slice(0, 80), at: this.turnSpoken.trim() ? "mid_reply" : "before_speech" });
|
|
9555
9672
|
const next = this.stepMarks.find((k) => k > at);
|
|
9556
9673
|
if (next === void 0) {
|
|
@@ -9590,7 +9707,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
9590
9707
|
this.notify("diag", "leak_judge", { text: s.slice(0, 80), p: Math.round(p * 100) / 100, ms: Math.round(performance.now() - t0) });
|
|
9591
9708
|
return p >= LEAK_JUDGE_THRESHOLD;
|
|
9592
9709
|
}, (e) => {
|
|
9593
|
-
|
|
9710
|
+
log21.warn(`leak judge failed \u2014 speaking the sentence: ${e.message}`);
|
|
9594
9711
|
return false;
|
|
9595
9712
|
}).then((leak) => {
|
|
9596
9713
|
clearTimeout(timer);
|
|
@@ -9624,7 +9741,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
9624
9741
|
let i = t.length - 1;
|
|
9625
9742
|
while (i >= this.turnBase && !(t[i].role === "assistant" && typeof t[i].content === "string" && t[i].content.includes(key))) i--;
|
|
9626
9743
|
if (i < this.turnBase) {
|
|
9627
|
-
|
|
9744
|
+
log21.warn("analysis leak cut, but its text was not found in the transcript to scrub");
|
|
9628
9745
|
continue;
|
|
9629
9746
|
}
|
|
9630
9747
|
const m = t[i];
|
|
@@ -9704,7 +9821,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
9704
9821
|
return {
|
|
9705
9822
|
preToolUse: (call) => {
|
|
9706
9823
|
if (this.spec?.state === "pending" && !_DuplexAgent.SPEC_SAFE_TOOLS.has(call.name)) {
|
|
9707
|
-
|
|
9824
|
+
log21.verbose(`speculation aborted: reflex called ${call.name} on unconfirmed input`);
|
|
9708
9825
|
this.abortSpeculation();
|
|
9709
9826
|
return { block: true, reason: "Speculative turn aborted." };
|
|
9710
9827
|
}
|
|
@@ -9749,7 +9866,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
9749
9866
|
try {
|
|
9750
9867
|
await this.voice.send(reminder);
|
|
9751
9868
|
} catch (e) {
|
|
9752
|
-
|
|
9869
|
+
log21.warn(`ack nudge failed: ${e instanceof Error ? e.message : e}`);
|
|
9753
9870
|
} finally {
|
|
9754
9871
|
this.nudging = false;
|
|
9755
9872
|
const i = this.voice.transcript.findIndex((m, k) => k >= base && m.role === "user" && m.content === reminder);
|
|
@@ -9801,7 +9918,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
9801
9918
|
const accepted = this.offerOpen && this.parkedRedeliver.length > 0 && this.parkedRedeliver.every((p) => p.task === this.offer.task) && (isOfferAcceptance(content) || isTrivialBarge(content));
|
|
9802
9919
|
const judge = accepted ? void 0 : this.options.bargeIntent;
|
|
9803
9920
|
return (accepted ? Promise.resolve("resume") : judge ? judge(content).catch((e) => {
|
|
9804
|
-
|
|
9921
|
+
log21.warn(`bargeIntent judge failed \u2014 using the allowlist: ${e.message}`);
|
|
9805
9922
|
return fallback();
|
|
9806
9923
|
}) : Promise.resolve(fallback())).then((intent) => {
|
|
9807
9924
|
this.notify("diag", "barge_intent", { intent, text: content.slice(0, 80) });
|
|
@@ -9997,7 +10114,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
9997
10114
|
try {
|
|
9998
10115
|
res = await this.voice.send(spec.text);
|
|
9999
10116
|
} catch (e) {
|
|
10000
|
-
|
|
10117
|
+
log21.warn(`speculative turn failed: ${e instanceof Error ? e.message : e}`);
|
|
10001
10118
|
} finally {
|
|
10002
10119
|
this.voice.options.signal = prevSignal;
|
|
10003
10120
|
}
|
|
@@ -10011,7 +10128,7 @@ ${nowLine()}. Anchor every relative or time-sensitive reference \u2014 "today",
|
|
|
10011
10128
|
if (d === "abort") {
|
|
10012
10129
|
if (this.voice.transcript.length > base) this.voice.transcript.length = base;
|
|
10013
10130
|
this.speculativeAbortedCalls++;
|
|
10014
|
-
|
|
10131
|
+
log21.verbose(`speculation aborted (${this.speculativeAbortedCalls} total): "${spec.text.slice(0, 50)}"`);
|
|
10015
10132
|
if (this.spec === spec) this.spec = void 0;
|
|
10016
10133
|
return res ?? empty;
|
|
10017
10134
|
}
|
|
@@ -10361,7 +10478,7 @@ Another agent just implemented the above. Independently check the CURRENT state
|
|
|
10361
10478
|
this.notify("task_verify", `task ${id}: verifying`, { id });
|
|
10362
10479
|
const cres = await new Agent(checkerOpts).run(checkBrief);
|
|
10363
10480
|
if (cres.finishReason !== "stop") {
|
|
10364
|
-
|
|
10481
|
+
log21.warn(`task ${id}: verify inconclusive (${cres.finishReason})`);
|
|
10365
10482
|
this.notify("task_verify", `task ${id}: verify inconclusive (${cres.finishReason})`, { id, finishReason: cres.finishReason });
|
|
10366
10483
|
}
|
|
10367
10484
|
const sum = (a = 0, b = 0) => a + b;
|
|
@@ -10497,7 +10614,7 @@ Another agent just implemented the above. Independently check the CURRENT state
|
|
|
10497
10614
|
rec.status = "done";
|
|
10498
10615
|
rec.result = res.text;
|
|
10499
10616
|
const incomplete = res.finishReason !== "stop" && res.finishReason !== "empty";
|
|
10500
|
-
|
|
10617
|
+
log21.verbose(`task ${id} done (${res.steps} steps${incomplete ? `, INCOMPLETE: ${res.finishReason}` : ""})`);
|
|
10501
10618
|
this.notify("task_done", `task ${id} (${rec.label}) completed`, {
|
|
10502
10619
|
id,
|
|
10503
10620
|
text: res.text,
|
|
@@ -10531,14 +10648,14 @@ Another agent just implemented the above. Independently check the CURRENT state
|
|
|
10531
10648
|
foldIfSuperseded(rec, note) {
|
|
10532
10649
|
if (rec.deliveryParked !== "moved_on") return false;
|
|
10533
10650
|
if (note.trim()) this.voice.transcript.push({ role: "assistant", content: note });
|
|
10534
|
-
|
|
10651
|
+
log21.verbose(`task ${rec.id} settled but superseded (${rec.status}) \u2014 folded into transcript, not voiced`);
|
|
10535
10652
|
return true;
|
|
10536
10653
|
}
|
|
10537
10654
|
failTask(rec, msg) {
|
|
10538
10655
|
this.dropAsk(rec.id);
|
|
10539
10656
|
rec.status = "error";
|
|
10540
10657
|
rec.result = msg;
|
|
10541
|
-
|
|
10658
|
+
log21.warn(`task ${rec.id} failed: ${msg}`);
|
|
10542
10659
|
this.notify("task_error", `task ${rec.id} (${rec.label}) failed: ${msg}`, { id: rec.id });
|
|
10543
10660
|
if (this.foldIfSuperseded(rec, `The "${rec.label}" task failed: ${msg}`)) return;
|
|
10544
10661
|
this.dropParked(rec);
|
|
@@ -10777,7 +10894,7 @@ Another agent just implemented the above. Independently check the CURRENT state
|
|
|
10777
10894
|
// src/modelRouter.ts
|
|
10778
10895
|
init_llm();
|
|
10779
10896
|
init_logging();
|
|
10780
|
-
var
|
|
10897
|
+
var log22 = forComponent("ModelRouter");
|
|
10781
10898
|
var ModelRouterOptions = class {
|
|
10782
10899
|
classifier = jevClassifier();
|
|
10783
10900
|
/** Capability tiers → model id (any provider). basic/standard run the main session; premium is only ever an offload. */
|
|
@@ -10861,10 +10978,10 @@ var ModelRouter = class {
|
|
|
10861
10978
|
if (tier === "premium") d.offload = this.state.offload && (c.sameThread ?? 0) >= o.sameThreadAt ? "continue" : "fresh";
|
|
10862
10979
|
} catch (e) {
|
|
10863
10980
|
const msg = e instanceof Error ? e.message : String(e);
|
|
10864
|
-
|
|
10981
|
+
log22.warn(`classifier failed, falling back to standard: ${msg}`);
|
|
10865
10982
|
d = { tier: "standard", model: o.models.standard, probs: {}, fallback: msg, classifyMs: Math.round(performance.now() - t0) };
|
|
10866
10983
|
}
|
|
10867
|
-
|
|
10984
|
+
log22.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`);
|
|
10868
10985
|
return d;
|
|
10869
10986
|
}
|
|
10870
10987
|
/** Run one user turn on `agent` with routing. Returns a RunResult carrying `costUsd` + `routing`. */
|
|
@@ -10916,7 +11033,7 @@ ${text}`;
|
|
|
10916
11033
|
const answer = res.text || `(premium sub-agent finished with no reply; finishReason=${res.finishReason})`;
|
|
10917
11034
|
agent.transcript.push({ role: "user", content }, { role: "assistant", content: answer });
|
|
10918
11035
|
this.state.offload = { messages: child.transcript, mainLen: agent.transcript.length, question: text };
|
|
10919
|
-
|
|
11036
|
+
log22.info(`offload ${cont ? "continue" : "fresh"} done \xB7 ${res.finishReason} \xB7 ${res.steps} steps`);
|
|
10920
11037
|
const costUsd = o.costOf(main2, briefUsage) + o.costOf(o.models.premium, res.usage);
|
|
10921
11038
|
return { ...res, text: answer, messages: agent.transcript, usage: addUsage(briefUsage, res.usage), costUsd, routing: d };
|
|
10922
11039
|
}
|
|
@@ -10932,7 +11049,7 @@ The request:
|
|
|
10932
11049
|
${text}`);
|
|
10933
11050
|
return { text: r.text.trim() || text, usage: r.usage };
|
|
10934
11051
|
} catch (e) {
|
|
10935
|
-
|
|
11052
|
+
log22.warn(`brief failed, sending the raw request: ${e instanceof Error ? e.message : String(e)}`);
|
|
10936
11053
|
return { text };
|
|
10937
11054
|
} finally {
|
|
10938
11055
|
agent.transcript = snap;
|
|
@@ -10964,8 +11081,8 @@ async function loadJev(opts) {
|
|
|
10964
11081
|
const env = { ...process.env };
|
|
10965
11082
|
const file = opts.envFile ?? process.env.AGENTX_JEV_ENV;
|
|
10966
11083
|
if (file) {
|
|
10967
|
-
const { readFileSync:
|
|
10968
|
-
for (const l of
|
|
11084
|
+
const { readFileSync: readFileSync11 } = await import("fs");
|
|
11085
|
+
for (const l of readFileSync11(file, "utf8").split("\n")) {
|
|
10969
11086
|
const m = l.match(/^\s*([A-Z0-9_]+)\s*=\s*(.*?)\s*$/);
|
|
10970
11087
|
if (m && !env[m[1]]) env[m[1]] = m[2].replace(/^['"]|['"]$/g, "");
|
|
10971
11088
|
}
|
|
@@ -10977,7 +11094,7 @@ async function jevAvailable(opts = {}) {
|
|
|
10977
11094
|
const { env } = await loadJev(opts);
|
|
10978
11095
|
return JEV_KEYS.some((k) => env[k]?.trim());
|
|
10979
11096
|
} catch (e) {
|
|
10980
|
-
|
|
11097
|
+
log22.debug(`jev unavailable: ${e instanceof Error ? e.message : String(e)}`);
|
|
10981
11098
|
return false;
|
|
10982
11099
|
}
|
|
10983
11100
|
}
|
|
@@ -11205,7 +11322,7 @@ import { MemFilesystem as MemFilesystem3, IndexedDbFilesystem, CommandExecutor a
|
|
|
11205
11322
|
init_logging();
|
|
11206
11323
|
import { spawn } from "child_process";
|
|
11207
11324
|
import { createHash } from "crypto";
|
|
11208
|
-
var
|
|
11325
|
+
var log23 = forComponent("mcp");
|
|
11209
11326
|
var PROTOCOL_VERSION = "2025-06-18";
|
|
11210
11327
|
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
11211
11328
|
var DEFAULT_CALL_TIMEOUT_MS = 30 * 6e4;
|
|
@@ -11235,7 +11352,7 @@ var StdioTransport = class {
|
|
|
11235
11352
|
proc.stdout.setEncoding("utf8");
|
|
11236
11353
|
proc.stdout.on("data", (chunk) => this.onData(chunk));
|
|
11237
11354
|
proc.stderr.setEncoding("utf8");
|
|
11238
|
-
proc.stderr.on("data", (chunk) =>
|
|
11355
|
+
proc.stderr.on("data", (chunk) => log23.debug(`[${command}] stderr:`, chunk.trimEnd()));
|
|
11239
11356
|
proc.on("exit", (code) => this.failAll(new Error(`MCP server "${command}" exited (code ${code})`)));
|
|
11240
11357
|
proc.on("error", (e) => this.failAll(e instanceof Error ? e : new Error(String(e))));
|
|
11241
11358
|
}
|
|
@@ -11249,7 +11366,7 @@ var StdioTransport = class {
|
|
|
11249
11366
|
try {
|
|
11250
11367
|
this.dispatch(JSON.parse(line));
|
|
11251
11368
|
} catch (e) {
|
|
11252
|
-
|
|
11369
|
+
log23.debug("dropping non-JSON line from MCP server:", line, e);
|
|
11253
11370
|
}
|
|
11254
11371
|
}
|
|
11255
11372
|
}
|
|
@@ -11259,7 +11376,7 @@ var StdioTransport = class {
|
|
|
11259
11376
|
try {
|
|
11260
11377
|
p2?.onProgress?.(msg.params ?? {});
|
|
11261
11378
|
} catch (e) {
|
|
11262
|
-
|
|
11379
|
+
log23.debug("onProgress handler threw", e);
|
|
11263
11380
|
}
|
|
11264
11381
|
return;
|
|
11265
11382
|
}
|
|
@@ -11297,7 +11414,7 @@ var StdioTransport = class {
|
|
|
11297
11414
|
const onAbort = () => {
|
|
11298
11415
|
if (!this.pending.delete(id)) return;
|
|
11299
11416
|
clearTimeout(timer);
|
|
11300
|
-
this.notify("notifications/cancelled", { requestId: id, reason: String(signal?.reason?.message ?? "cancelled") }).catch((e) =>
|
|
11417
|
+
this.notify("notifications/cancelled", { requestId: id, reason: String(signal?.reason?.message ?? "cancelled") }).catch((e) => log23.debug("cancel notify failed", e));
|
|
11301
11418
|
reject(abortError(signal));
|
|
11302
11419
|
};
|
|
11303
11420
|
const done = () => signal?.removeEventListener("abort", onAbort);
|
|
@@ -11321,7 +11438,7 @@ var StdioTransport = class {
|
|
|
11321
11438
|
try {
|
|
11322
11439
|
this.proc?.stdin?.end();
|
|
11323
11440
|
} catch (e) {
|
|
11324
|
-
|
|
11441
|
+
log23.debug("stdin end failed", e);
|
|
11325
11442
|
}
|
|
11326
11443
|
this.proc?.kill();
|
|
11327
11444
|
}
|
|
@@ -11356,7 +11473,7 @@ var HttpTransport = class {
|
|
|
11356
11473
|
return await this.post({ jsonrpc: "2.0", id, method, params: opts.onProgress ? withProgressToken(params, id) : params }, false, signal, opts.onProgress);
|
|
11357
11474
|
} catch (e) {
|
|
11358
11475
|
if (opts.signal?.aborted) {
|
|
11359
|
-
void this.notify("notifications/cancelled", { requestId: id, reason: String(opts.signal.reason?.message ?? "cancelled") }).catch((err2) =>
|
|
11476
|
+
void this.notify("notifications/cancelled", { requestId: id, reason: String(opts.signal.reason?.message ?? "cancelled") }).catch((err2) => log23.debug("cancel notify failed", err2));
|
|
11360
11477
|
throw abortError(opts.signal);
|
|
11361
11478
|
}
|
|
11362
11479
|
throw e;
|
|
@@ -11399,7 +11516,7 @@ function sseMessage(line) {
|
|
|
11399
11516
|
try {
|
|
11400
11517
|
return JSON.parse(trimmed.slice(5).trim());
|
|
11401
11518
|
} catch (e) {
|
|
11402
|
-
|
|
11519
|
+
log23.debug("skipping unparseable SSE data line", e);
|
|
11403
11520
|
return void 0;
|
|
11404
11521
|
}
|
|
11405
11522
|
}
|
|
@@ -11409,7 +11526,7 @@ async function readSseResponse(resp, id, onProgress) {
|
|
|
11409
11526
|
try {
|
|
11410
11527
|
onProgress?.(obj.params ?? {});
|
|
11411
11528
|
} catch (e) {
|
|
11412
|
-
|
|
11529
|
+
log23.debug("onProgress handler threw", e);
|
|
11413
11530
|
}
|
|
11414
11531
|
return void 0;
|
|
11415
11532
|
}
|
|
@@ -11508,7 +11625,7 @@ async function mountWithDeadline(name, cfg, mountTimeoutMs) {
|
|
|
11508
11625
|
return { name, client, tools, specs, serverInfo: init?.serverInfo, config: cfg };
|
|
11509
11626
|
})(), mountTimeoutMs, name);
|
|
11510
11627
|
} catch (e) {
|
|
11511
|
-
await client.close().catch((err2) =>
|
|
11628
|
+
await client.close().catch((err2) => log23.debug(`close after failed mount of "${name}": ${err2}`));
|
|
11512
11629
|
throw e;
|
|
11513
11630
|
}
|
|
11514
11631
|
}
|
|
@@ -11519,15 +11636,15 @@ function validEntries(servers) {
|
|
|
11519
11636
|
return Object.entries(servers).filter(([name, cfg]) => {
|
|
11520
11637
|
if (!cfg || cfg.disabled) return false;
|
|
11521
11638
|
if (!cfg.command && !cfg.url) {
|
|
11522
|
-
|
|
11639
|
+
log23.warn(`MCP server "${name}" needs a command (stdio) or url (http) \u2014 skipping`);
|
|
11523
11640
|
return false;
|
|
11524
11641
|
}
|
|
11525
11642
|
return true;
|
|
11526
11643
|
});
|
|
11527
11644
|
}
|
|
11528
11645
|
function logMountFailure(name, e) {
|
|
11529
|
-
if (e instanceof McpAuthError)
|
|
11530
|
-
else
|
|
11646
|
+
if (e instanceof McpAuthError) log23.warn(`MCP "${name}" needs-auth: HTTP ${e.status} \u2014 set bearerToken or headers in its config; skipping`);
|
|
11647
|
+
else log23.error(`MCP server "${name}" failed to mount: ${e?.message ?? e}`);
|
|
11531
11648
|
}
|
|
11532
11649
|
async function mountMcpServers(servers = {}, opts = {}) {
|
|
11533
11650
|
const entries = validEntries(servers);
|
|
@@ -11537,7 +11654,7 @@ async function mountMcpServers(servers = {}, opts = {}) {
|
|
|
11537
11654
|
const name = entries[i][0];
|
|
11538
11655
|
if (r.status === "fulfilled") {
|
|
11539
11656
|
out.push(r.value);
|
|
11540
|
-
|
|
11657
|
+
log23.debug(`MCP "${name}" mounted \u2014 ${r.value.tools.length} tool(s)${r.value.serverInfo?.name ? ` from ${r.value.serverInfo.name}` : ""}`);
|
|
11541
11658
|
} else logMountFailure(name, r.reason);
|
|
11542
11659
|
});
|
|
11543
11660
|
return out;
|
|
@@ -11577,7 +11694,7 @@ var McpPool = class {
|
|
|
11577
11694
|
const prev = this.warm.get(key);
|
|
11578
11695
|
if (prev) {
|
|
11579
11696
|
clearTimeout(prev.timer);
|
|
11580
|
-
if (prev.client !== client) void prev.client.close().catch((err2) =>
|
|
11697
|
+
if (prev.client !== client) void prev.client.close().catch((err2) => log23.debug(`warm-pool replace close failed: ${err2}`));
|
|
11581
11698
|
}
|
|
11582
11699
|
const e = { client, timer: void 0 };
|
|
11583
11700
|
this.warm.set(key, e);
|
|
@@ -11594,7 +11711,7 @@ var McpPool = class {
|
|
|
11594
11711
|
const e = this.warm.get(key);
|
|
11595
11712
|
if (!e) return;
|
|
11596
11713
|
this.warm.delete(key);
|
|
11597
|
-
await e.client.close().catch((err2) =>
|
|
11714
|
+
await e.client.close().catch((err2) => log23.debug(`warm-pool evict close failed: ${err2}`));
|
|
11598
11715
|
}
|
|
11599
11716
|
async closeAll() {
|
|
11600
11717
|
for (const e of this.warm.values()) {
|
|
@@ -11611,7 +11728,7 @@ var defaultPool = new McpPool();
|
|
|
11611
11728
|
// cli/mcpOAuth.ts
|
|
11612
11729
|
import { createServer } from "http";
|
|
11613
11730
|
import { randomBytes as randomBytes2, createHash as createHash2 } from "crypto";
|
|
11614
|
-
import { readFileSync as
|
|
11731
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync } from "fs";
|
|
11615
11732
|
import { dirname as dirname2 } from "path";
|
|
11616
11733
|
var McpOAuthOptions = class {
|
|
11617
11734
|
/** Where to persist tokens. Mode 0600. */
|
|
@@ -11639,7 +11756,7 @@ var McpOAuth = class {
|
|
|
11639
11756
|
// -- store I/O ----------------------------------------------------------------
|
|
11640
11757
|
load() {
|
|
11641
11758
|
try {
|
|
11642
|
-
return existsSync(this.options.storePath) ? JSON.parse(
|
|
11759
|
+
return existsSync(this.options.storePath) ? JSON.parse(readFileSync4(this.options.storePath, "utf8")) : {};
|
|
11643
11760
|
} catch {
|
|
11644
11761
|
return {};
|
|
11645
11762
|
}
|
|
@@ -11812,7 +11929,7 @@ init_tools_shell();
|
|
|
11812
11929
|
// src/tools.notify.ts
|
|
11813
11930
|
init_logging();
|
|
11814
11931
|
import { execFile } from "child_process";
|
|
11815
|
-
var
|
|
11932
|
+
var log25 = forComponent("notify");
|
|
11816
11933
|
function makeNotifyTool(opts = {}) {
|
|
11817
11934
|
const platform2 = opts.platform ?? process.platform;
|
|
11818
11935
|
const run = opts.exec ?? execFile;
|
|
@@ -11836,7 +11953,7 @@ function makeNotifyTool(opts = {}) {
|
|
|
11836
11953
|
return new Promise((resolve5) => {
|
|
11837
11954
|
run(argv[0], argv[1], { timeout: 5e3 }, (e) => {
|
|
11838
11955
|
if (e) {
|
|
11839
|
-
|
|
11956
|
+
log25.debug("notification failed", e);
|
|
11840
11957
|
resolve5(`Notification failed: ${e.message}`);
|
|
11841
11958
|
} else resolve5("Notification shown.");
|
|
11842
11959
|
});
|
|
@@ -11850,9 +11967,9 @@ import { BodDB as BodDB2 } from "@bod.ee/db";
|
|
|
11850
11967
|
|
|
11851
11968
|
// cli/util.ts
|
|
11852
11969
|
init_logging();
|
|
11853
|
-
import { existsSync as existsSync2, readFileSync as
|
|
11970
|
+
import { existsSync as existsSync2, readFileSync as readFileSync5 } from "fs";
|
|
11854
11971
|
import { homedir } from "os";
|
|
11855
|
-
var
|
|
11972
|
+
var log26 = forComponent("cli-util");
|
|
11856
11973
|
function dotDirs(base, sub, opts = {}) {
|
|
11857
11974
|
const home = opts.home ?? homedir();
|
|
11858
11975
|
const dirs = [`${base}/.agent/${sub}`, `${base}/.claude/${sub}`, `${home}/.agent/${sub}`, `${home}/.claude/${sub}`];
|
|
@@ -11869,7 +11986,7 @@ function parseJson(text, fallback, what = "json") {
|
|
|
11869
11986
|
try {
|
|
11870
11987
|
return JSON.parse(text);
|
|
11871
11988
|
} catch (e) {
|
|
11872
|
-
|
|
11989
|
+
log26.debug(`parseJson(${what}) failed: ${e.message}`);
|
|
11873
11990
|
return fallback;
|
|
11874
11991
|
}
|
|
11875
11992
|
}
|
|
@@ -11877,9 +11994,9 @@ function readJsonFile(path, fallback) {
|
|
|
11877
11994
|
if (!existsSync2(path)) return fallback;
|
|
11878
11995
|
let text;
|
|
11879
11996
|
try {
|
|
11880
|
-
text =
|
|
11997
|
+
text = readFileSync5(path, "utf8");
|
|
11881
11998
|
} catch (e) {
|
|
11882
|
-
|
|
11999
|
+
log26.debug(`readJsonFile(${path}) unreadable: ${e.message}`);
|
|
11883
12000
|
return fallback;
|
|
11884
12001
|
}
|
|
11885
12002
|
return parseJson(text, fallback, path);
|
|
@@ -12154,6 +12271,7 @@ The filesystem root '/' is the real machine root \u2014 you have full filesystem
|
|
|
12154
12271
|
keepToolOutputs: o.keepToolOutputs ?? 8,
|
|
12155
12272
|
...o.maxContextTokens != null ? { maxContextTokens: o.maxContextTokens } : {},
|
|
12156
12273
|
...o.contextWindow ? { contextWindow: o.contextWindow } : {},
|
|
12274
|
+
...o.imageInput ? { imageInput: o.imageInput } : {},
|
|
12157
12275
|
...o.browserTrim ? { browserTrim: o.browserTrim } : {}
|
|
12158
12276
|
});
|
|
12159
12277
|
}
|
|
@@ -12174,11 +12292,11 @@ var trunc = (s, n) => (s == null ? "" : String(s).length > n ? String(s).slice(0
|
|
|
12174
12292
|
// cli/voice.ts
|
|
12175
12293
|
init_logging();
|
|
12176
12294
|
import { spawn as spawn2, spawnSync as spawnSync2 } from "child_process";
|
|
12177
|
-
import { existsSync as existsSync4, mkdirSync as mkdirSync5, readFileSync as
|
|
12295
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync5, readFileSync as readFileSync6, statSync as statSync5 } from "fs";
|
|
12178
12296
|
import { homedir as homedir3 } from "os";
|
|
12179
12297
|
import { dirname as dirname3, join as join6 } from "path";
|
|
12180
12298
|
import { fileURLToPath } from "url";
|
|
12181
|
-
var
|
|
12299
|
+
var log27 = forComponent("VoiceIO");
|
|
12182
12300
|
var now3 = () => performance.now();
|
|
12183
12301
|
var Player = class {
|
|
12184
12302
|
proc = null;
|
|
@@ -12192,7 +12310,7 @@ var Player = class {
|
|
|
12192
12310
|
["-loglevel", "quiet", "-nodisp", "-fflags", "nobuffer", "-flags", "low_delay", "-probesize", "32", "-f", "s16le", "-ar", String(TTS_SAMPLE_RATE), "-ch_layout", "mono", "-i", "-"],
|
|
12193
12311
|
{ stdio: ["pipe", "ignore", "ignore"] }
|
|
12194
12312
|
);
|
|
12195
|
-
this.proc.on("error", (e) =>
|
|
12313
|
+
this.proc.on("error", (e) => log27.warn(`ffplay error: ${e.message}`));
|
|
12196
12314
|
this.proc.stdin.on("error", () => {
|
|
12197
12315
|
});
|
|
12198
12316
|
this.bytesWritten = 0;
|
|
@@ -12230,7 +12348,7 @@ function detectFfmpegMic() {
|
|
|
12230
12348
|
const devices = [...audio.matchAll(/\[(\d+)\] (.+)/g)].map(([, idx, name]) => ({ idx, name: name.trim() }));
|
|
12231
12349
|
const mic = devices.find((d) => /microphone|built-in/i.test(d.name) && !/teams|blackhole|loopback/i.test(d.name)) ?? devices[0];
|
|
12232
12350
|
if (!mic) throw new Error("no audio input device found");
|
|
12233
|
-
|
|
12351
|
+
log27.debug(`ffmpeg mic: [${mic.idx}] ${mic.name}`);
|
|
12234
12352
|
return `:${mic.idx}`;
|
|
12235
12353
|
}
|
|
12236
12354
|
function detectedInputDevice() {
|
|
@@ -12263,18 +12381,18 @@ function resolveAecBinary() {
|
|
|
12263
12381
|
if (!existsSync4(src)) return null;
|
|
12264
12382
|
const cacheDir = join6(homedir3(), ".agent", "cache");
|
|
12265
12383
|
const bin = join6(cacheDir, "mic-aec");
|
|
12266
|
-
if (existsSync4(bin) &&
|
|
12384
|
+
if (existsSync4(bin) && statSync5(bin).mtimeMs >= statSync5(src).mtimeMs) return bin;
|
|
12267
12385
|
if (spawnSync2("which", ["swiftc"]).status !== 0) return null;
|
|
12268
12386
|
mkdirSync5(cacheDir, { recursive: true });
|
|
12269
|
-
|
|
12387
|
+
log27.info("compiling AEC mic helper (first run)\u2026");
|
|
12270
12388
|
const build = spawnSync2("swiftc", ["-O", "-o", bin, src, "-Xlinker", "-sectcreate", "-Xlinker", "__TEXT", "-Xlinker", "__info_plist", "-Xlinker", plist], { encoding: "utf8" });
|
|
12271
12389
|
if (build.status !== 0) {
|
|
12272
|
-
|
|
12390
|
+
log27.warn(`AEC build failed: ${build.stderr?.slice(0, 400)}`);
|
|
12273
12391
|
return null;
|
|
12274
12392
|
}
|
|
12275
12393
|
const sign = spawnSync2("codesign", ["-fs", "-", bin], { encoding: "utf8" });
|
|
12276
12394
|
if (sign.status !== 0) {
|
|
12277
|
-
|
|
12395
|
+
log27.warn(`codesign failed: ${sign.stderr?.slice(0, 200)}`);
|
|
12278
12396
|
return null;
|
|
12279
12397
|
}
|
|
12280
12398
|
return bin;
|
|
@@ -12316,16 +12434,16 @@ var NodeMicSource = class {
|
|
|
12316
12434
|
this.proc = spawn2(this.bin, [], { stdio: ["ignore", "pipe", "ignore"] });
|
|
12317
12435
|
} else {
|
|
12318
12436
|
if (spawnSync2("which", ["ffmpeg"]).status !== 0) throw new Error("voice I/O unavailable: no AEC helper and no ffmpeg on PATH");
|
|
12319
|
-
|
|
12437
|
+
log27.info("mic: raw capture (no AEC) \u2014 echo handled heuristically; headphones recommended");
|
|
12320
12438
|
this.proc = spawn2(
|
|
12321
12439
|
"ffmpeg",
|
|
12322
12440
|
["-loglevel", "error", "-f", "avfoundation", "-i", detectFfmpegMic(), "-ar", String(STT_SAMPLE_RATE), "-ac", "1", "-f", "s16le", "-"],
|
|
12323
12441
|
{ stdio: ["ignore", "pipe", "pipe"] }
|
|
12324
12442
|
);
|
|
12325
|
-
this.proc.stderr.on("data", (d) =>
|
|
12443
|
+
this.proc.stderr.on("data", (d) => log27.warn(`ffmpeg: ${String(d).trim()}`));
|
|
12326
12444
|
}
|
|
12327
12445
|
this.proc.on("exit", (c) => {
|
|
12328
|
-
if (c && !this.stopped)
|
|
12446
|
+
if (c && !this.stopped) log27.error(`mic capture exited (${c}) \u2014 check mic permission / MIC_DEVICE / MIC_AEC=0`);
|
|
12329
12447
|
});
|
|
12330
12448
|
this.proc.stdout.on("data", (chunk) => onChunk(chunk));
|
|
12331
12449
|
}
|
|
@@ -12391,7 +12509,7 @@ var AecDuplexAudio = class {
|
|
|
12391
12509
|
this.proc.stdin.on("error", () => {
|
|
12392
12510
|
});
|
|
12393
12511
|
this.proc.on("exit", (c) => {
|
|
12394
|
-
if (c && !this.stopped)
|
|
12512
|
+
if (c && !this.stopped) log27.error(`aec duplex audio exited (${c}) \u2014 check mic permission / MIC_AEC=0`);
|
|
12395
12513
|
});
|
|
12396
12514
|
this.proc.stdout.on("data", (chunk) => {
|
|
12397
12515
|
this.gotChunk = true;
|
|
@@ -12407,7 +12525,7 @@ var AecDuplexAudio = class {
|
|
|
12407
12525
|
openMicSettings();
|
|
12408
12526
|
this.onFatal?.("microphone permission denied \u2014 enable it in System Settings \u2192 Privacy & Security \u2192 Microphone for your terminal, then restart it");
|
|
12409
12527
|
}
|
|
12410
|
-
} else
|
|
12528
|
+
} else log27.debug(`mic-aec: ${s}`);
|
|
12411
12529
|
}
|
|
12412
12530
|
});
|
|
12413
12531
|
if (!this.noVpio && !this.triedFallback) {
|
|
@@ -12416,7 +12534,7 @@ var AecDuplexAudio = class {
|
|
|
12416
12534
|
this.triedFallback = true;
|
|
12417
12535
|
this.noVpio = true;
|
|
12418
12536
|
this._aec = false;
|
|
12419
|
-
|
|
12537
|
+
log27.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)");
|
|
12420
12538
|
this.onDegrade?.();
|
|
12421
12539
|
this.killProc();
|
|
12422
12540
|
this.spawnHelper();
|
|
@@ -12587,7 +12705,7 @@ function bindAckInstinct(v, instinct) {
|
|
|
12587
12705
|
if (ev.kind === "ack_fired") instinct.ackOutcome(String(ev.phrase));
|
|
12588
12706
|
else if (ev.kind === "ack_skipped" || ev.kind === "ack_cancelled") instinct.ackOutcome(null);
|
|
12589
12707
|
} catch (e) {
|
|
12590
|
-
|
|
12708
|
+
log27.warn(`ack outcome tap failed: ${e instanceof Error ? e.message : e}`);
|
|
12591
12709
|
}
|
|
12592
12710
|
onDiag(ev);
|
|
12593
12711
|
};
|
|
@@ -12665,7 +12783,7 @@ function fakeVoiceParts(uttFile) {
|
|
|
12665
12783
|
timer = setInterval(() => {
|
|
12666
12784
|
let buf;
|
|
12667
12785
|
try {
|
|
12668
|
-
buf =
|
|
12786
|
+
buf = readFileSync6(uttFile, "utf8");
|
|
12669
12787
|
} catch {
|
|
12670
12788
|
return;
|
|
12671
12789
|
}
|
|
@@ -12708,7 +12826,7 @@ function fakeVoiceParts(uttFile) {
|
|
|
12708
12826
|
init_logging();
|
|
12709
12827
|
import { appendFile, mkdirSync as mkdirSync6 } from "fs";
|
|
12710
12828
|
import { dirname as dirname4 } from "path";
|
|
12711
|
-
var
|
|
12829
|
+
var log28 = forComponent("VoiceDiag");
|
|
12712
12830
|
var JsonlDiagSink = class {
|
|
12713
12831
|
constructor(path) {
|
|
12714
12832
|
this.path = path;
|
|
@@ -12749,7 +12867,7 @@ var JsonlDiagSink = class {
|
|
|
12749
12867
|
disable(e) {
|
|
12750
12868
|
this.disabled = true;
|
|
12751
12869
|
this.queue.length = 0;
|
|
12752
|
-
|
|
12870
|
+
log28.debug(`voice diag sink disabled (${this.path}): ${e instanceof Error ? e.message : e}`);
|
|
12753
12871
|
}
|
|
12754
12872
|
/** True once a failure permanently disabled the sink (tests/inspection). */
|
|
12755
12873
|
get isDisabled() {
|
|
@@ -12763,7 +12881,7 @@ var JsonlDiagSink = class {
|
|
|
12763
12881
|
|
|
12764
12882
|
// cli/config.ts
|
|
12765
12883
|
import { homedir as homedir4 } from "os";
|
|
12766
|
-
import { existsSync as existsSync5, readFileSync as
|
|
12884
|
+
import { existsSync as existsSync5, readFileSync as readFileSync7 } from "fs";
|
|
12767
12885
|
import { join as join7 } from "path";
|
|
12768
12886
|
import { pathToFileURL } from "url";
|
|
12769
12887
|
var FILES = ["config.ts", "config.js", "config.mjs", "config.json"];
|
|
@@ -12785,7 +12903,7 @@ function loadSettings(dir) {
|
|
|
12785
12903
|
const p = join7(dir, ".agent", "settings.json");
|
|
12786
12904
|
if (!existsSync5(p)) return {};
|
|
12787
12905
|
try {
|
|
12788
|
-
const raw = JSON.parse(
|
|
12906
|
+
const raw = JSON.parse(readFileSync7(p, "utf8"));
|
|
12789
12907
|
const cfg = {};
|
|
12790
12908
|
if (raw.mcpServers && typeof raw.mcpServers === "object") cfg.mcpServers = raw.mcpServers;
|
|
12791
12909
|
if (raw.permissions && typeof raw.permissions === "object") cfg.permissions = raw.permissions;
|
|
@@ -12819,7 +12937,7 @@ async function loadConfig(cwd) {
|
|
|
12819
12937
|
|
|
12820
12938
|
// cli/hooks-config.ts
|
|
12821
12939
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
12822
|
-
var
|
|
12940
|
+
var log29 = forComponent("hooks");
|
|
12823
12941
|
var escapeRegex = (s) => s.replace(/[.+^${}()|[\]\\]/g, "\\$&");
|
|
12824
12942
|
function ruleMatches(rule, toolName) {
|
|
12825
12943
|
if (!rule.tool || rule.tool === "*") return true;
|
|
@@ -12836,7 +12954,7 @@ function runCmd(rule, env) {
|
|
|
12836
12954
|
});
|
|
12837
12955
|
return { code: r.status ?? 1, out: ((r.stdout ?? "") + (r.stderr ?? "")).trim() };
|
|
12838
12956
|
} catch (e) {
|
|
12839
|
-
|
|
12957
|
+
log29.debug(`hook command failed: ${rule.command}`, e);
|
|
12840
12958
|
return { code: 1, out: String(e?.message ?? e) };
|
|
12841
12959
|
}
|
|
12842
12960
|
}
|
|
@@ -12940,10 +13058,10 @@ function formatDiff(ops, opts = {}) {
|
|
|
12940
13058
|
}
|
|
12941
13059
|
|
|
12942
13060
|
// cli/session.ts
|
|
12943
|
-
import { existsSync as existsSync6, mkdirSync as mkdirSync7, readFileSync as
|
|
13061
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync7, readFileSync as readFileSync8, writeFileSync as writeFileSync4, readdirSync, renameSync, symlinkSync as symlinkSync2, unlinkSync, readlinkSync, statSync as statSync6 } from "fs";
|
|
12944
13062
|
import { homedir as homedir5 } from "os";
|
|
12945
13063
|
import { join as join8 } from "path";
|
|
12946
|
-
var
|
|
13064
|
+
var log30 = forComponent("session");
|
|
12947
13065
|
var globalDir = () => join8(homedir5(), ".agent", "sessions");
|
|
12948
13066
|
var SessionStore = class {
|
|
12949
13067
|
dir;
|
|
@@ -12988,15 +13106,15 @@ var SessionStore = class {
|
|
|
12988
13106
|
}
|
|
12989
13107
|
load(id) {
|
|
12990
13108
|
if (!this.safeId(id)) {
|
|
12991
|
-
|
|
13109
|
+
log30.debug(`rejecting unsafe session id: ${id}`);
|
|
12992
13110
|
return void 0;
|
|
12993
13111
|
}
|
|
12994
13112
|
const path = join8(this.dir, `${id}.json`);
|
|
12995
13113
|
if (!existsSync6(path)) return void 0;
|
|
12996
13114
|
try {
|
|
12997
|
-
return JSON.parse(
|
|
13115
|
+
return JSON.parse(readFileSync8(path, "utf8"));
|
|
12998
13116
|
} catch (e) {
|
|
12999
|
-
|
|
13117
|
+
log30.debug(`unreadable session ${id} \u2014 ignoring`, e);
|
|
13000
13118
|
return void 0;
|
|
13001
13119
|
}
|
|
13002
13120
|
}
|
|
@@ -13007,9 +13125,9 @@ var SessionStore = class {
|
|
|
13007
13125
|
for (const f of readdirSync(this.dir)) {
|
|
13008
13126
|
if (!f.endsWith(".json")) continue;
|
|
13009
13127
|
try {
|
|
13010
|
-
metas.push(JSON.parse(
|
|
13128
|
+
metas.push(JSON.parse(readFileSync8(join8(this.dir, f), "utf8")).meta);
|
|
13011
13129
|
} catch (e) {
|
|
13012
|
-
|
|
13130
|
+
log30.debug(`skipping unreadable session file ${f}`, e);
|
|
13013
13131
|
}
|
|
13014
13132
|
}
|
|
13015
13133
|
return metas.sort((a, b) => b.updated - a.updated);
|
|
@@ -13022,7 +13140,7 @@ var SessionStore = class {
|
|
|
13022
13140
|
for (const f of readdirSync(this.dir)) {
|
|
13023
13141
|
if (!f.endsWith(".json")) continue;
|
|
13024
13142
|
try {
|
|
13025
|
-
const st =
|
|
13143
|
+
const st = statSync6(join8(this.dir, f));
|
|
13026
13144
|
out.push({ id: f.slice(0, -5), mtime: st.mtimeMs, size: st.size });
|
|
13027
13145
|
} catch {
|
|
13028
13146
|
}
|
|
@@ -13045,7 +13163,7 @@ function globalSessionLoad(idOrPrefix) {
|
|
|
13045
13163
|
if (existsSync6(exact)) {
|
|
13046
13164
|
try {
|
|
13047
13165
|
const target = readlinkSync(exact);
|
|
13048
|
-
return JSON.parse(
|
|
13166
|
+
return JSON.parse(readFileSync8(target, "utf8"));
|
|
13049
13167
|
} catch {
|
|
13050
13168
|
return void 0;
|
|
13051
13169
|
}
|
|
@@ -13056,7 +13174,7 @@ function globalSessionLoad(idOrPrefix) {
|
|
|
13056
13174
|
const base = f.slice(0, -5);
|
|
13057
13175
|
if (base.includes(idOrPrefix) || base.endsWith(idOrPrefix)) {
|
|
13058
13176
|
const target = readlinkSync(join8(gd, f));
|
|
13059
|
-
return JSON.parse(
|
|
13177
|
+
return JSON.parse(readFileSync8(target, "utf8"));
|
|
13060
13178
|
}
|
|
13061
13179
|
}
|
|
13062
13180
|
} catch {
|
|
@@ -13078,7 +13196,7 @@ function globalSessionList() {
|
|
|
13078
13196
|
}
|
|
13079
13197
|
continue;
|
|
13080
13198
|
}
|
|
13081
|
-
metas.push(JSON.parse(
|
|
13199
|
+
metas.push(JSON.parse(readFileSync8(target, "utf8")).meta);
|
|
13082
13200
|
} catch {
|
|
13083
13201
|
}
|
|
13084
13202
|
}
|
|
@@ -13248,7 +13366,7 @@ import { promisify } from "util";
|
|
|
13248
13366
|
import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync8 } from "fs";
|
|
13249
13367
|
import { homedir as homedir6 } from "os";
|
|
13250
13368
|
import { join as join9, resolve as resolve3, sep as sep3, parse as parsePath } from "path";
|
|
13251
|
-
var
|
|
13369
|
+
var log31 = forComponent("checkpoints");
|
|
13252
13370
|
var exec = promisify(execFile3);
|
|
13253
13371
|
var DEFAULT_EXCLUDE = [".agent/", ".git/", "node_modules/", "dist/", "build/", ".next/", "target/", ".venv/", "__pycache__/", "*.log"];
|
|
13254
13372
|
function isBroadRoot(p) {
|
|
@@ -13294,7 +13412,7 @@ var ShadowRepo = class {
|
|
|
13294
13412
|
writeFileSync5(join9(this.gitDir, "info", "exclude"), this.exclude.join("\n") + "\n");
|
|
13295
13413
|
this.ready = true;
|
|
13296
13414
|
} catch (e) {
|
|
13297
|
-
|
|
13415
|
+
log31.debug(`git checkpoints unavailable for ${this.workTree}`, e);
|
|
13298
13416
|
this.ready = false;
|
|
13299
13417
|
}
|
|
13300
13418
|
return this.ready;
|
|
@@ -13305,7 +13423,7 @@ var ShadowRepo = class {
|
|
|
13305
13423
|
}
|
|
13306
13424
|
async commit(label, forced = []) {
|
|
13307
13425
|
await this.run("add", "-A");
|
|
13308
|
-
for (const p of forced) await this.run("add", "-f", "--", p).catch((e) =>
|
|
13426
|
+
for (const p of forced) await this.run("add", "-f", "--", p).catch((e) => log31.debug(`force-add failed: ${p}`, e));
|
|
13309
13427
|
await this.run("commit", "--allow-empty", "-q", "-m", label);
|
|
13310
13428
|
}
|
|
13311
13429
|
/** Inject the CURRENT (pre-edit) content of `paths` into the turn-open restore point by amending it.
|
|
@@ -13313,8 +13431,8 @@ var ShadowRepo = class {
|
|
|
13313
13431
|
* turn-boundary `add -A` would never have captured it. Amend (vs a new commit) keeps one restore
|
|
13314
13432
|
* point per turn, so the REPL's turn↔frame mapping stays intact. */
|
|
13315
13433
|
async amendForced(paths) {
|
|
13316
|
-
for (const p of paths) await this.run("add", "-f", "--", p).catch((e) =>
|
|
13317
|
-
await this.run("commit", "--amend", "--no-edit", "-q", "--allow-empty").catch((e) =>
|
|
13434
|
+
for (const p of paths) await this.run("add", "-f", "--", p).catch((e) => log31.debug(`force-capture failed: ${p}`, e));
|
|
13435
|
+
await this.run("commit", "--amend", "--no-edit", "-q", "--allow-empty").catch((e) => log31.debug("amend failed", e));
|
|
13318
13436
|
}
|
|
13319
13437
|
/** Commits on `ref`, oldest-first (canonical index space). */
|
|
13320
13438
|
async log(ref) {
|
|
@@ -13374,7 +13492,7 @@ var ShadowRepo = class {
|
|
|
13374
13492
|
await this.run("gc", "--auto", "-q").catch(() => {
|
|
13375
13493
|
});
|
|
13376
13494
|
} catch (e) {
|
|
13377
|
-
|
|
13495
|
+
log31.debug("checkpoint prune failed", e);
|
|
13378
13496
|
}
|
|
13379
13497
|
}
|
|
13380
13498
|
};
|
|
@@ -13442,7 +13560,7 @@ var GitCheckpoints = class {
|
|
|
13442
13560
|
use(sessionId) {
|
|
13443
13561
|
if (sessionId === this.session) return;
|
|
13444
13562
|
this.session = sessionId;
|
|
13445
|
-
if (this.started) for (const r of this.repos) void r.point(this.ref()).catch((e) =>
|
|
13563
|
+
if (this.started) for (const r of this.repos) void r.point(this.ref()).catch((e) => log31.debug("re-point failed", e));
|
|
13446
13564
|
}
|
|
13447
13565
|
async begin(label) {
|
|
13448
13566
|
if (!await this.start()) return;
|
|
@@ -13462,7 +13580,7 @@ var GitCheckpoints = class {
|
|
|
13462
13580
|
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
|
|
13463
13581
|
`);
|
|
13464
13582
|
} else {
|
|
13465
|
-
|
|
13583
|
+
log31.debug("checkpoint commit failed", e);
|
|
13466
13584
|
survivors.push(this.repos[i]);
|
|
13467
13585
|
survivorCaches.push(this.caches[i] ?? []);
|
|
13468
13586
|
}
|
|
@@ -13497,7 +13615,7 @@ var GitCheckpoints = class {
|
|
|
13497
13615
|
if (this.forced.has(abs)) continue;
|
|
13498
13616
|
this.forced.add(abs);
|
|
13499
13617
|
const i = this.repoIndexFor(abs);
|
|
13500
|
-
if (i >= 0) await this.repos[i].amendForced([abs]).catch((e) =>
|
|
13618
|
+
if (i >= 0) await this.repos[i].amendForced([abs]).catch((e) => log31.debug("amendForced failed", e));
|
|
13501
13619
|
}
|
|
13502
13620
|
}
|
|
13503
13621
|
};
|
|
@@ -13697,7 +13815,7 @@ function completePath(listDir, ref) {
|
|
|
13697
13815
|
// cli/lineEditor.ts
|
|
13698
13816
|
import { emitKeypressEvents } from "readline";
|
|
13699
13817
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
13700
|
-
import { writeFileSync as writeFileSync7, readFileSync as
|
|
13818
|
+
import { writeFileSync as writeFileSync7, readFileSync as readFileSync9, unlinkSync as unlinkSync2 } from "fs";
|
|
13701
13819
|
import { tmpdir as tmpdir2 } from "os";
|
|
13702
13820
|
import { join as join11 } from "path";
|
|
13703
13821
|
|
|
@@ -14893,7 +15011,7 @@ function createLineEditor(out, input = keyInput) {
|
|
|
14893
15011
|
out.write("\x1B[?2004l");
|
|
14894
15012
|
const r = spawnSync4(cmd, [...cargs, file], { stdio: "inherit" });
|
|
14895
15013
|
if (r.status === 0) {
|
|
14896
|
-
const text =
|
|
15014
|
+
const text = readFileSync9(file, "utf8").replace(/\n$/, "");
|
|
14897
15015
|
s.reset();
|
|
14898
15016
|
if (text) s.insert(text);
|
|
14899
15017
|
}
|
|
@@ -15350,7 +15468,7 @@ import { spawnSync as spawnSync5 } from "child_process";
|
|
|
15350
15468
|
import { writeFileSync as writeFileSync8, mkdirSync as mkdirSync10, readdirSync as readdirSync2, unlinkSync as unlinkSync3, chmodSync, existsSync as existsSync7 } from "fs";
|
|
15351
15469
|
import { homedir as homedir8 } from "os";
|
|
15352
15470
|
import { join as join12 } from "path";
|
|
15353
|
-
var
|
|
15471
|
+
var log32 = forComponent("os-sched");
|
|
15354
15472
|
var OsScheduler = class {
|
|
15355
15473
|
options;
|
|
15356
15474
|
constructor(options) {
|
|
@@ -15412,7 +15530,7 @@ var OsScheduler = class {
|
|
|
15412
15530
|
}
|
|
15413
15531
|
}
|
|
15414
15532
|
} catch (e) {
|
|
15415
|
-
|
|
15533
|
+
log32.debug(`cancel ${id}`, e);
|
|
15416
15534
|
}
|
|
15417
15535
|
for (const f of [`${id}.json`, `${id}.sh`]) {
|
|
15418
15536
|
try {
|
|
@@ -15529,7 +15647,7 @@ import { spawn as spawn3 } from "child_process";
|
|
|
15529
15647
|
import { existsSync as existsSync8, mkdirSync as mkdirSync11, unlinkSync as unlinkSync4, readdirSync as readdirSync3 } from "fs";
|
|
15530
15648
|
import { homedir as homedir9 } from "os";
|
|
15531
15649
|
import { join as join13 } from "path";
|
|
15532
|
-
var
|
|
15650
|
+
var log33 = forComponent("remote-trigger");
|
|
15533
15651
|
var TRIGGER_DIR = () => join13(homedir9(), ".agent", "triggers");
|
|
15534
15652
|
var sockPath = (sessionId, dir = TRIGGER_DIR()) => join13(dir, `${sessionId}.sock`);
|
|
15535
15653
|
var TriggerServer = class {
|
|
@@ -15567,13 +15685,13 @@ var TriggerServer = class {
|
|
|
15567
15685
|
conn.end(JSON.stringify({ ok: false, error: String(e) }) + "\n");
|
|
15568
15686
|
}
|
|
15569
15687
|
});
|
|
15570
|
-
conn.on("error", (e) =>
|
|
15688
|
+
conn.on("error", (e) => log33.debug("trigger conn error", e));
|
|
15571
15689
|
});
|
|
15572
|
-
this.server.on("error", (e) =>
|
|
15690
|
+
this.server.on("error", (e) => log33.debug("trigger server error", e));
|
|
15573
15691
|
this.server.listen(p);
|
|
15574
15692
|
this.path = p;
|
|
15575
15693
|
} catch (e) {
|
|
15576
|
-
|
|
15694
|
+
log33.debug("trigger server unavailable", e);
|
|
15577
15695
|
}
|
|
15578
15696
|
}
|
|
15579
15697
|
/** Re-bind on /resume (the session id changed). */
|
|
@@ -15695,10 +15813,10 @@ var strike = C("9");
|
|
|
15695
15813
|
var link = (text, url) => useColor ? `\x1B]8;;${url}\x1B\\${cyan(text)}\x1B]8;;\x1B\\` : `${text} (${url})`;
|
|
15696
15814
|
var mdPalette = { bold, dim, cyan, italic, strike, green, link };
|
|
15697
15815
|
var err = (s) => process.stderr.write(s);
|
|
15698
|
-
var
|
|
15816
|
+
var log34 = forComponent("cli");
|
|
15699
15817
|
var VERSION = (() => {
|
|
15700
15818
|
try {
|
|
15701
|
-
return JSON.parse(
|
|
15819
|
+
return JSON.parse(readFileSync10(new URL("../package.json", import.meta.url), "utf8")).version ?? "?";
|
|
15702
15820
|
} catch {
|
|
15703
15821
|
return "?";
|
|
15704
15822
|
}
|
|
@@ -16005,7 +16123,7 @@ function resolveModelOrNewest(model) {
|
|
|
16005
16123
|
var ENV_KEY_ALIASES = { google: ["GEMINI_API_KEY"] };
|
|
16006
16124
|
function loadEnvFile(file) {
|
|
16007
16125
|
if (!existsSync9(file)) return;
|
|
16008
|
-
for (const line of
|
|
16126
|
+
for (const line of readFileSync10(file, "utf8").split("\n")) {
|
|
16009
16127
|
const m = line.match(/^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/);
|
|
16010
16128
|
if (!m || m[1] in process.env) continue;
|
|
16011
16129
|
let val = m[2].trim();
|
|
@@ -16070,6 +16188,7 @@ function makeHost(format = "text", opts) {
|
|
|
16070
16188
|
};
|
|
16071
16189
|
const activitySeen = /* @__PURE__ */ new Set();
|
|
16072
16190
|
const turnActivityIds = /* @__PURE__ */ new Set();
|
|
16191
|
+
const toolNames = /* @__PURE__ */ new Map();
|
|
16073
16192
|
let anonActivityCount = 0;
|
|
16074
16193
|
return {
|
|
16075
16194
|
flushText,
|
|
@@ -16153,7 +16272,17 @@ function makeHost(format = "text", opts) {
|
|
|
16153
16272
|
spinner.start();
|
|
16154
16273
|
return;
|
|
16155
16274
|
}
|
|
16275
|
+
if (streamJson && (e.kind === "tool_use" || e.kind === "tool_result")) {
|
|
16276
|
+
const t = e;
|
|
16277
|
+
if (t.kind === "tool_use") toolNames.set(t.id, t.name ?? "");
|
|
16278
|
+
const name = toolNames.get(t.id);
|
|
16279
|
+
if (t.kind === "tool_result") toolNames.delete(t.id);
|
|
16280
|
+
const line = t.kind === "tool_use" ? { type: "tool_activity", id: t.id, name: t.name, input: t.input, status: "running" } : { type: "tool_activity", id: t.id, ...name ? { name } : {}, output: typeof t.output === "string" ? t.output.slice(0, 4e3) : t.output, status: t.isError ? "error" : "completed" };
|
|
16281
|
+
process.stdout.write(JSON.stringify(line) + "\n");
|
|
16282
|
+
return;
|
|
16283
|
+
}
|
|
16156
16284
|
if (!("message" in e)) return;
|
|
16285
|
+
if (streamJson && e.kind === "retry") process.stdout.write(JSON.stringify({ type: "notice", kind: "retry", text: e.message }) + "\n");
|
|
16157
16286
|
flushLookupGroup();
|
|
16158
16287
|
closeReasonLine();
|
|
16159
16288
|
flushText();
|
|
@@ -16480,7 +16609,7 @@ function turnCost(model, usage) {
|
|
|
16480
16609
|
const priced = model.startsWith("claude-code/") ? `anthropic/${resolveModelAlias(model.slice("claude-code/".length))}` : model;
|
|
16481
16610
|
return costOf(getModelInfo(priced)?.pricing, usage?.promptTokens ?? 0, usage?.completionTokens ?? 0, usage?.cacheCreationTokens ?? 0, usage?.cacheReadTokens ?? 0, priced);
|
|
16482
16611
|
}
|
|
16483
|
-
async function evaluateGoal(ai, condition, transcript,
|
|
16612
|
+
async function evaluateGoal(ai, condition, transcript, log35) {
|
|
16484
16613
|
const recent = transcript.filter((m) => m.role === "assistant").slice(-8).map((m) => {
|
|
16485
16614
|
const text = typeof m.content === "string" ? m.content : m.content.filter((p) => p.type === "text").map((p) => p.text).join(" ");
|
|
16486
16615
|
return text.slice(0, 600);
|
|
@@ -16500,7 +16629,7 @@ ${recent}` }
|
|
|
16500
16629
|
const match = r.content.match(/\{[\s\S]*\}/);
|
|
16501
16630
|
if (match) return JSON.parse(match[0]);
|
|
16502
16631
|
} catch (e) {
|
|
16503
|
-
|
|
16632
|
+
log35(dim(` (goal evaluator error: ${e?.message ?? e})
|
|
16504
16633
|
`));
|
|
16505
16634
|
}
|
|
16506
16635
|
return { met: false, reason: "evaluation unclear" };
|
|
@@ -16511,8 +16640,12 @@ function errInfo(e) {
|
|
|
16511
16640
|
function fmtUsd(n) {
|
|
16512
16641
|
return n >= 1 ? `$${n.toFixed(2)}` : `$${n.toFixed(4)}`;
|
|
16513
16642
|
}
|
|
16514
|
-
|
|
16515
|
-
|
|
16643
|
+
var imageInputOf = (m) => {
|
|
16644
|
+
const i = getModelInfo(m);
|
|
16645
|
+
return i ? !!i.imageInput || !!i.vision : void 0;
|
|
16646
|
+
};
|
|
16647
|
+
function estimateTranscriptTokens(messages, keepRecentImages = 1, vision = true) {
|
|
16648
|
+
return estimateTokens(messages, keepRecentImages, vision);
|
|
16516
16649
|
}
|
|
16517
16650
|
function formatStatus(s) {
|
|
16518
16651
|
const L = (k, v) => ` ${k.padEnd(12)} ${v}
|
|
@@ -16630,6 +16763,7 @@ function optsFor(args, ai, cfg = {}, extraTools = []) {
|
|
|
16630
16763
|
model,
|
|
16631
16764
|
// The real context window drives the context % and `/compact`'s budget (never `maxTokens` — that's spend).
|
|
16632
16765
|
contextWindow: getModelInfo(model)?.contextWindow ?? 0,
|
|
16766
|
+
imageInput: imageInputOf,
|
|
16633
16767
|
cwd: args.cwd,
|
|
16634
16768
|
tools: cfg.tools,
|
|
16635
16769
|
extraTools,
|
|
@@ -16721,7 +16855,7 @@ function mcpAgentTools(mounted, opts) {
|
|
|
16721
16855
|
return tools;
|
|
16722
16856
|
}
|
|
16723
16857
|
async function closeMcp(mounted) {
|
|
16724
|
-
await Promise.all(mounted.map((m) => m.client.close().catch((e) =>
|
|
16858
|
+
await Promise.all(mounted.map((m) => m.client.close().catch((e) => log34.debug("mcp close failed", e))));
|
|
16725
16859
|
}
|
|
16726
16860
|
var IMG_EXT = { ".png": "image/png", ".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".gif": "image/gif", ".webp": "image/webp" };
|
|
16727
16861
|
function mentionRefs(line) {
|
|
@@ -16736,7 +16870,7 @@ function readImageParts(cwd, line) {
|
|
|
16736
16870
|
if (!mime) continue;
|
|
16737
16871
|
const abs = ref.startsWith("~/") ? untilde(ref) : resolve4(cwd, ref);
|
|
16738
16872
|
try {
|
|
16739
|
-
|
|
16873
|
+
statSync7(abs);
|
|
16740
16874
|
} catch {
|
|
16741
16875
|
continue;
|
|
16742
16876
|
}
|
|
@@ -16752,7 +16886,7 @@ function pastePathClassifier(cwd) {
|
|
|
16752
16886
|
if (!/^(\/|~\/|\.\/|\.\.\/)/.test(t)) return null;
|
|
16753
16887
|
const abs = t.startsWith("~/") ? untilde(t) : resolve4(cwd, t);
|
|
16754
16888
|
try {
|
|
16755
|
-
if (!
|
|
16889
|
+
if (!statSync7(abs).isFile()) return null;
|
|
16756
16890
|
} catch {
|
|
16757
16891
|
return null;
|
|
16758
16892
|
}
|
|
@@ -16773,7 +16907,7 @@ async function expandMentions(fs, line) {
|
|
|
16773
16907
|
if (loaded.includes(ref) || missing.includes(ref)) continue;
|
|
16774
16908
|
if (ref.includes(":") && mcpMentionResolver) {
|
|
16775
16909
|
const body = await mcpMentionResolver(ref).catch((e) => {
|
|
16776
|
-
|
|
16910
|
+
log34.debug("mcp mention resolve failed", e);
|
|
16777
16911
|
return null;
|
|
16778
16912
|
});
|
|
16779
16913
|
if (body != null) {
|
|
@@ -16813,7 +16947,8 @@ function jsonResult(res, session) {
|
|
|
16813
16947
|
sessionId: session.meta.id,
|
|
16814
16948
|
// The parked question, machine-readable — the caller answers it and resumes `sessionId`.
|
|
16815
16949
|
...res.finishReason === "needs_input" && res.question ? { question: res.question } : {},
|
|
16816
|
-
|
|
16950
|
+
// 'timeout' too: a provider stall ("model stalled: no stream output for 60s") vs the between-steps idle ceiling (no error).
|
|
16951
|
+
...(res.finishReason === "error" || res.finishReason === "empty" || res.finishReason === "timeout") && res.error ? { error: res.error?.message ?? String(res.error) } : {}
|
|
16817
16952
|
};
|
|
16818
16953
|
}
|
|
16819
16954
|
async function readMultiline(readLine) {
|
|
@@ -16862,7 +16997,7 @@ async function runTurn(agent, store, session, task, cp, cwd = process.cwd(), sen
|
|
|
16862
16997
|
try {
|
|
16863
16998
|
store.save(session);
|
|
16864
16999
|
} catch (ex) {
|
|
16865
|
-
|
|
17000
|
+
log34.debug("mid-turn session flush failed", ex);
|
|
16866
17001
|
}
|
|
16867
17002
|
}
|
|
16868
17003
|
return origNotify(e);
|
|
@@ -16991,7 +17126,7 @@ function initInstructions(cwd) {
|
|
|
16991
17126
|
function persistSetting(cwd, key, value) {
|
|
16992
17127
|
const path = join14(cwd, ".agent", "settings.json");
|
|
16993
17128
|
try {
|
|
16994
|
-
const obj = existsSync9(path) ? JSON.parse(
|
|
17129
|
+
const obj = existsSync9(path) ? JSON.parse(readFileSync10(path, "utf8")) : {};
|
|
16995
17130
|
if (obj[key] === value) return;
|
|
16996
17131
|
obj[key] = value;
|
|
16997
17132
|
mkdirSync12(dirname5(path), { recursive: true });
|
|
@@ -17011,14 +17146,14 @@ var isCancelTeardown = (e) => {
|
|
|
17011
17146
|
function installCancelGuards(mounted) {
|
|
17012
17147
|
process.on("unhandledRejection", (e) => {
|
|
17013
17148
|
if (isCancelTeardown(e)) {
|
|
17014
|
-
|
|
17149
|
+
log34.debug("suppressed unhandledRejection (cursor stream cancel)", e);
|
|
17015
17150
|
return;
|
|
17016
17151
|
}
|
|
17017
|
-
|
|
17152
|
+
log34.error("unhandledRejection", e);
|
|
17018
17153
|
});
|
|
17019
17154
|
process.on("uncaughtException", (e) => {
|
|
17020
17155
|
if (isCancelTeardown(e)) {
|
|
17021
|
-
|
|
17156
|
+
log34.debug("suppressed uncaughtException (cursor stream cancel)", e);
|
|
17022
17157
|
return;
|
|
17023
17158
|
}
|
|
17024
17159
|
console.error(e);
|
|
@@ -17303,7 +17438,7 @@ async function repl(args, ai, cfg, cwd) {
|
|
|
17303
17438
|
quickLook: {
|
|
17304
17439
|
branch: () => {
|
|
17305
17440
|
try {
|
|
17306
|
-
const head =
|
|
17441
|
+
const head = readFileSync10(join14(cwd, ".git", "HEAD"), "utf8").trim();
|
|
17307
17442
|
return head.startsWith("ref: refs/heads/") ? `branch: ${head.slice("ref: refs/heads/".length)}` : `detached HEAD at ${head.slice(0, 12)}`;
|
|
17308
17443
|
} catch {
|
|
17309
17444
|
return "not a git repository";
|
|
@@ -17441,7 +17576,7 @@ async function repl(args, ai, cfg, cwd) {
|
|
|
17441
17576
|
try {
|
|
17442
17577
|
mkdirSync12(dir, { recursive: true });
|
|
17443
17578
|
} catch (e) {
|
|
17444
|
-
|
|
17579
|
+
log34.debug("paste image dir create failed", e);
|
|
17445
17580
|
}
|
|
17446
17581
|
const img = grabClipboardImage(dir, String(Date.now()));
|
|
17447
17582
|
return img ? { display: "Image", ref: "@" + img.path, path: img.path } : null;
|
|
@@ -17548,13 +17683,13 @@ Added entries are loadable now via the Skill/SlashCommand tools; removed ones ar
|
|
|
17548
17683
|
</system-reminder>`;
|
|
17549
17684
|
};
|
|
17550
17685
|
const histPath = join14(cwd, ".agent", "history");
|
|
17551
|
-
const history = existsSync9(histPath) ?
|
|
17552
|
-
const
|
|
17686
|
+
const history = existsSync9(histPath) ? readFileSync10(histPath, "utf8").split("\n").filter(Boolean).reverse().slice(0, 500) : [];
|
|
17687
|
+
const remember2 = (line) => {
|
|
17553
17688
|
try {
|
|
17554
17689
|
mkdirSync12(join14(cwd, ".agent"), { recursive: true });
|
|
17555
17690
|
appendFileSync(histPath, line + "\n");
|
|
17556
17691
|
} catch (e) {
|
|
17557
|
-
|
|
17692
|
+
log34.debug("history write failed", e);
|
|
17558
17693
|
}
|
|
17559
17694
|
};
|
|
17560
17695
|
const ago = (t) => {
|
|
@@ -17626,7 +17761,7 @@ Added entries are loadable now via the Skill/SlashCommand tools; removed ones ar
|
|
|
17626
17761
|
try {
|
|
17627
17762
|
store.save(session);
|
|
17628
17763
|
} catch (e) {
|
|
17629
|
-
|
|
17764
|
+
log34.debug("session save after rewind failed", e);
|
|
17630
17765
|
}
|
|
17631
17766
|
err(green(" \u27F2 jumped back") + dim(` \u2014 ${face.transcript.length} message(s) kept; edit + resend
|
|
17632
17767
|
`));
|
|
@@ -17661,7 +17796,7 @@ ${task}`;
|
|
|
17661
17796
|
bangContext.length = 0;
|
|
17662
17797
|
}
|
|
17663
17798
|
const delta = await refreshCatalogs().catch((e) => {
|
|
17664
|
-
|
|
17799
|
+
log34.debug("catalog refresh failed", e);
|
|
17665
17800
|
return "";
|
|
17666
17801
|
});
|
|
17667
17802
|
if (delta) {
|
|
@@ -17839,7 +17974,7 @@ ${task}`;
|
|
|
17839
17974
|
context: {
|
|
17840
17975
|
desc: "context-window usage (messages + estimated tokens)",
|
|
17841
17976
|
run: () => {
|
|
17842
|
-
const est = estimateTranscriptTokens(face.transcript, face.options.keepRecentImages);
|
|
17977
|
+
const est = estimateTranscriptTokens(face.transcript, face.options.keepRecentImages, face.options.imageInput?.(face.options.model) !== false);
|
|
17843
17978
|
const cap = Agent.contextCap(face.options);
|
|
17844
17979
|
err(dim(` ${face.transcript.length} message(s) \xB7 ~${(est / 1e3).toFixed(1)}k tokens (~${Math.round(est / cap * 100)}% of ${Math.round(cap / 1e3)}k budget)
|
|
17845
17980
|
`));
|
|
@@ -17925,7 +18060,7 @@ ${task}`;
|
|
|
17925
18060
|
desc: "rescan skills/commands dirs and rebuild the system prompt (one cache miss) \u2014 picks up entries created mid-session",
|
|
17926
18061
|
run: async () => {
|
|
17927
18062
|
await refreshCatalogs().catch((e) => {
|
|
17928
|
-
|
|
18063
|
+
log34.debug("catalog refresh failed", e);
|
|
17929
18064
|
});
|
|
17930
18065
|
face.reprepare();
|
|
17931
18066
|
err(green(` \u2713 reloaded \u2014 ${skills.length} skill(s), ${cmds.length} command(s); system prompt rebuilds on next message
|
|
@@ -18427,7 +18562,7 @@ ${task}`;
|
|
|
18427
18562
|
try {
|
|
18428
18563
|
for (const def of (await loadAgents(fs2, d)).agents) if (!seen.has(def.name)) seen.set(def.name, { def, from: d });
|
|
18429
18564
|
} catch (e) {
|
|
18430
|
-
|
|
18565
|
+
log34.debug(`loadAgents(${d}) failed`, e);
|
|
18431
18566
|
}
|
|
18432
18567
|
}
|
|
18433
18568
|
if (!seen.size) {
|
|
@@ -18515,7 +18650,7 @@ ${task}`;
|
|
|
18515
18650
|
}
|
|
18516
18651
|
if (idx >= 0) {
|
|
18517
18652
|
const old = mounted.splice(idx, 1)[0];
|
|
18518
|
-
await old.client.close().catch((e) =>
|
|
18653
|
+
await old.client.close().catch((e) => log34.debug("mcp close failed", e));
|
|
18519
18654
|
}
|
|
18520
18655
|
try {
|
|
18521
18656
|
const m = await mountMcpServer(name, conf);
|
|
@@ -18543,7 +18678,7 @@ ${task}`;
|
|
|
18543
18678
|
}
|
|
18544
18679
|
const m = mounted.splice(idx, 1)[0];
|
|
18545
18680
|
remountMcpTools();
|
|
18546
|
-
await m.client.close().catch((e) =>
|
|
18681
|
+
await m.client.close().catch((e) => log34.debug("mcp close failed", e));
|
|
18547
18682
|
err(dim(` removed "${name}"
|
|
18548
18683
|
`));
|
|
18549
18684
|
return;
|
|
@@ -18746,7 +18881,7 @@ ${task}`;
|
|
|
18746
18881
|
try {
|
|
18747
18882
|
return readdirSync4(join14(cwd, absDir.replace(/^\/+/, "")), { withFileTypes: true }).map((d) => ({ name: d.name, dir: d.isDirectory() }));
|
|
18748
18883
|
} catch (e) {
|
|
18749
|
-
|
|
18884
|
+
log34.debug("completion readdir failed", absDir, e);
|
|
18750
18885
|
return null;
|
|
18751
18886
|
}
|
|
18752
18887
|
};
|
|
@@ -18849,7 +18984,7 @@ ${task}`;
|
|
|
18849
18984
|
if (!opts?.id) takeCut(voiceIO, false);
|
|
18850
18985
|
if (/^[!#/]/.test(line)) dispatchPending = false;
|
|
18851
18986
|
history.unshift(line.replace(/\n+/g, " \u23CE "));
|
|
18852
|
-
|
|
18987
|
+
remember2(line.replace(/\n+/g, " \u23CE "));
|
|
18853
18988
|
if (line.startsWith("!")) {
|
|
18854
18989
|
const cmd = line.slice(1).trim();
|
|
18855
18990
|
if (cmd) {
|
|
@@ -18883,7 +19018,7 @@ ${out}
|
|
|
18883
19018
|
return;
|
|
18884
19019
|
}
|
|
18885
19020
|
await refreshCatalogs().catch((e) => {
|
|
18886
|
-
|
|
19021
|
+
log34.debug("catalog refresh failed", e);
|
|
18887
19022
|
});
|
|
18888
19023
|
const sk = skills.find((x) => x.name === name);
|
|
18889
19024
|
if (sk) {
|
|
@@ -18994,7 +19129,7 @@ ${out}
|
|
|
18994
19129
|
if (delivery || cut) dx?.parkCutDelivery(delivery, !!cut, heard);
|
|
18995
19130
|
const cutReply = cut && cut.full.length - cut.heard.length > 40 ? cut : null;
|
|
18996
19131
|
const noteP = replaces ? replaces.note : !cutReply ? Promise.resolve("") : (instinct ? instinct.bargeIntent(text).catch((e) => {
|
|
18997
|
-
|
|
19132
|
+
log34.warn(`barge intent failed \u2014 model decides: ${e.message}`);
|
|
18998
19133
|
return void 0;
|
|
18999
19134
|
}) : Promise.resolve(void 0)).then(async (intent) => interruptionNote(cutReply, intent) + ackedNote(await instinct?.ackFor(text)));
|
|
19000
19135
|
const cutIn = replaces ? replaces.cutIn : interjectNote;
|
|
@@ -19152,7 +19287,7 @@ ${out}
|
|
|
19152
19287
|
err("\n");
|
|
19153
19288
|
const initial = prefill ?? (carry || void 0);
|
|
19154
19289
|
prefill = void 0;
|
|
19155
|
-
const ctxTok = estimateTranscriptTokens(face.transcript, face.options.keepRecentImages);
|
|
19290
|
+
const ctxTok = estimateTranscriptTokens(face.transcript, face.options.keepRecentImages, face.options.imageInput?.(face.options.model) !== false);
|
|
19156
19291
|
const ctxCap = Agent.contextCap(face.options);
|
|
19157
19292
|
{
|
|
19158
19293
|
const pct = Math.round(ctxTok / ctxCap * 100);
|
|
@@ -19376,7 +19511,7 @@ async function duplexServe(args, ai, cfg, cwd) {
|
|
|
19376
19511
|
try {
|
|
19377
19512
|
store.save(session);
|
|
19378
19513
|
} catch (e) {
|
|
19379
|
-
|
|
19514
|
+
log34.warn("duplex session save failed", e);
|
|
19380
19515
|
}
|
|
19381
19516
|
};
|
|
19382
19517
|
out({ type: "ready", sessionId: session.meta.id, reflexModel: dx.options.reflexModel, actModel: dx.options.actModel });
|
|
@@ -19400,7 +19535,7 @@ async function duplexServe(args, ai, cfg, cwd) {
|
|
|
19400
19535
|
const note = reply && reply.full.length - reply.heard.length > 40 ? interruptionNote(reply) : "";
|
|
19401
19536
|
const id = m.id ? String(m.id) : void 0;
|
|
19402
19537
|
void runTurn(dx.voice, store, session, m.text + note, void 0, cwd, (c) => dx.send(c, { id, replaces: m.replaces ? String(m.replaces) : void 0, followUp: typeof m.followUp === "string" || m.followUp && typeof m.followUp === "object" ? m.followUp : void 0 })).then(({ res }) => out({ type: "turn_done", id, finishReason: res?.finishReason ?? "error" })).catch((e) => {
|
|
19403
|
-
|
|
19538
|
+
log34.error("duplex turn failed", e);
|
|
19404
19539
|
out({ type: "turn_done", id, finishReason: "error", error: String(e?.message ?? e) });
|
|
19405
19540
|
});
|
|
19406
19541
|
}
|