@wrongstack/tui 0.66.13 → 0.73.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -15
- package/dist/index.d.ts +1 -9
- package/dist/index.js +1102 -1771
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { Readable } from 'stream';
|
|
2
1
|
import { writeErr, InputBuilder, DefaultSessionRewinder, writeOut, formatTodosList, buildGoalPreamble, buildChildEnv } from '@wrongstack/core';
|
|
3
2
|
export { buildGoalPreamble } from '@wrongstack/core';
|
|
4
3
|
import { Box, Text, render, useApp, useStdout, measureElement, Static, useInput, useStdin } from 'ink';
|
|
5
|
-
import
|
|
4
|
+
import React5, { useState, useEffect, useReducer, useRef, useMemo, useCallback, useLayoutEffect } from 'react';
|
|
6
5
|
import * as fs2 from 'fs/promises';
|
|
7
6
|
import * as path2 from 'path';
|
|
8
7
|
import { routeImagesForModel } from '@wrongstack/runtime/vision';
|
|
@@ -450,28 +449,6 @@ function ContextChip({ ctx }) {
|
|
|
450
449
|
] })
|
|
451
450
|
] });
|
|
452
451
|
}
|
|
453
|
-
var SB_GAP = 2;
|
|
454
|
-
var SB_PADX = 1;
|
|
455
|
-
function statusBarModelSpan(opts) {
|
|
456
|
-
let col = SB_PADX;
|
|
457
|
-
if (opts.version) {
|
|
458
|
-
col += `WS v${opts.version}`.length + SB_GAP;
|
|
459
|
-
col += 1 + SB_GAP;
|
|
460
|
-
}
|
|
461
|
-
const { label } = stateChip(opts.state, opts.fleetRunning ?? 0);
|
|
462
|
-
col += 2 + label.length + SB_GAP;
|
|
463
|
-
col += 1 + SB_GAP;
|
|
464
|
-
return { start: col, len: opts.model.length };
|
|
465
|
-
}
|
|
466
|
-
function statusBarAutonomySpan(opts) {
|
|
467
|
-
if (!opts.autonomy || opts.autonomy === "off") return null;
|
|
468
|
-
let col = SB_PADX;
|
|
469
|
-
if (opts.yolo) {
|
|
470
|
-
col += "\u26A0 YOLO".length + SB_GAP;
|
|
471
|
-
col += 1 + SB_GAP;
|
|
472
|
-
}
|
|
473
|
-
return { start: col, len: 2 + opts.autonomy.toUpperCase().length };
|
|
474
|
-
}
|
|
475
452
|
function stateChip(state, fleetRunning) {
|
|
476
453
|
if (state === "idle" && fleetRunning > 0) {
|
|
477
454
|
return { label: `agents \u25B6${fleetRunning}`, color: "magenta" };
|
|
@@ -1140,16 +1117,6 @@ function buttonLabels(suggestedPattern) {
|
|
|
1140
1117
|
{ decision: "deny", bracket: "[d]", rest: "eny" }
|
|
1141
1118
|
];
|
|
1142
1119
|
}
|
|
1143
|
-
function confirmButtonSegments(suggestedPattern) {
|
|
1144
|
-
const out = [];
|
|
1145
|
-
let col = 0;
|
|
1146
|
-
for (const l of buttonLabels(suggestedPattern)) {
|
|
1147
|
-
const len = l.bracket.length + l.rest.length;
|
|
1148
|
-
out.push({ decision: l.decision, start: col, len });
|
|
1149
|
-
col += len;
|
|
1150
|
-
}
|
|
1151
|
-
return out;
|
|
1152
|
-
}
|
|
1153
1120
|
function stringifyInput(input) {
|
|
1154
1121
|
if (!input || typeof input !== "object") return "";
|
|
1155
1122
|
const obj = input;
|
|
@@ -1180,7 +1147,7 @@ function ConfirmPrompt({
|
|
|
1180
1147
|
suggestedPattern,
|
|
1181
1148
|
onDecision
|
|
1182
1149
|
}) {
|
|
1183
|
-
|
|
1150
|
+
React5.useEffect(() => {
|
|
1184
1151
|
writeOut("\x07");
|
|
1185
1152
|
}, []);
|
|
1186
1153
|
useInput((input2, _key) => {
|
|
@@ -1209,7 +1176,7 @@ function ConfirmPrompt({
|
|
|
1209
1176
|
inputSummary ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: inputSummary }) : null,
|
|
1210
1177
|
showDiff && diff ? /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginY: 1, children: renderDiff(diff) }) : null,
|
|
1211
1178
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }),
|
|
1212
|
-
/* @__PURE__ */ jsx(Box, { flexDirection: "row", children: /* @__PURE__ */ jsx(Text, { children: buttonLabels(suggestedPattern).map((l) => /* @__PURE__ */ jsxs(
|
|
1179
|
+
/* @__PURE__ */ jsx(Box, { flexDirection: "row", children: /* @__PURE__ */ jsx(Text, { children: buttonLabels(suggestedPattern).map((l) => /* @__PURE__ */ jsxs(React5.Fragment, { children: [
|
|
1213
1180
|
/* @__PURE__ */ jsx(Text, { bold: true, color: BUTTON_COLOR[l.decision], children: l.bracket }),
|
|
1214
1181
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: l.rest })
|
|
1215
1182
|
] }, l.decision)) }) })
|
|
@@ -1306,13 +1273,6 @@ function FleetPanel({
|
|
|
1306
1273
|
function helpSections(opts) {
|
|
1307
1274
|
const nav = [];
|
|
1308
1275
|
if (opts.managed) nav.push({ keys: "PgUp/PgDn", desc: "scroll chat history" });
|
|
1309
|
-
if (opts.mouse)
|
|
1310
|
-
nav.push(
|
|
1311
|
-
{ keys: "wheel", desc: "scroll chat history" },
|
|
1312
|
-
{ keys: "drag scrollbar", desc: "scrub to any position" },
|
|
1313
|
-
{ keys: "click input", desc: "move the caret" },
|
|
1314
|
-
{ keys: "click", desc: "select / confirm" }
|
|
1315
|
-
);
|
|
1316
1276
|
nav.push(
|
|
1317
1277
|
{ keys: "\u2191/\u2193", desc: "previous / next input (empty prompt)" },
|
|
1318
1278
|
{ keys: "?", desc: "open this help (empty prompt)" }
|
|
@@ -1353,10 +1313,9 @@ function helpSections(opts) {
|
|
|
1353
1313
|
];
|
|
1354
1314
|
}
|
|
1355
1315
|
function HelpOverlay({
|
|
1356
|
-
managed
|
|
1357
|
-
mouse
|
|
1316
|
+
managed
|
|
1358
1317
|
}) {
|
|
1359
|
-
const sections = helpSections({ managed
|
|
1318
|
+
const sections = helpSections({ managed });
|
|
1360
1319
|
const keyWidth = Math.max(...sections.flatMap((s2) => s2.entries.map((e) => e.keys.length)), 0);
|
|
1361
1320
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
|
|
1362
1321
|
/* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
|
|
@@ -2218,553 +2177,115 @@ function MarkdownView({
|
|
|
2218
2177
|
}
|
|
2219
2178
|
return /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: rows });
|
|
2220
2179
|
}
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
return
|
|
2180
|
+
function shortenPath(p, max) {
|
|
2181
|
+
if (p.length <= max) return p;
|
|
2182
|
+
return `\u2026${p.slice(p.length - (max - 1))}`;
|
|
2224
2183
|
}
|
|
2225
|
-
function
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
});
|
|
2231
|
-
useEffect(() => {
|
|
2232
|
-
const handleResize = () => {
|
|
2233
|
-
setTermSize({ columns: stdout?.columns ?? 80, rows: stdout?.rows ?? 24 });
|
|
2234
|
-
};
|
|
2235
|
-
process.stdout.on("resize", handleResize);
|
|
2236
|
-
return () => {
|
|
2237
|
-
process.stdout.off("resize", handleResize);
|
|
2238
|
-
};
|
|
2239
|
-
}, [stdout]);
|
|
2240
|
-
const termWidth = termSize.columns;
|
|
2241
|
-
const tail = streamingText ? tailForDisplay(streamingText, MAX_STREAM_DISPLAY_CHARS) : "";
|
|
2242
|
-
const toolTail = toolStream?.text ? tailForDisplay(toolStream.text, MAX_STREAM_DISPLAY_CHARS) : "";
|
|
2243
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2244
|
-
/* @__PURE__ */ jsx(Static, { items: entries, children: (entry) => /* @__PURE__ */ jsx(Box, { marginBottom: entry.kind === "turn-summary" ? 1 : 0, children: /* @__PURE__ */ jsx(Entry, { entry, termWidth }) }, entry.id) }),
|
|
2245
|
-
tail ? /* @__PURE__ */ jsx(AssistantTail, { text: tail }) : null,
|
|
2246
|
-
toolTail ? /* @__PURE__ */ jsx(
|
|
2247
|
-
ToolStreamBox,
|
|
2248
|
-
{
|
|
2249
|
-
name: toolStream.name,
|
|
2250
|
-
text: toolTail,
|
|
2251
|
-
startedAt: toolStream.startedAt,
|
|
2252
|
-
termWidth
|
|
2253
|
-
}
|
|
2254
|
-
) : null
|
|
2255
|
-
] });
|
|
2184
|
+
function fmtTok2(n) {
|
|
2185
|
+
if (!Number.isFinite(n) || n <= 0) return "0";
|
|
2186
|
+
if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
|
|
2187
|
+
if (n >= 1e3) return `${(n / 1e3).toFixed(n >= 1e4 ? 0 : 1)}k`;
|
|
2188
|
+
return String(n);
|
|
2256
2189
|
}
|
|
2257
|
-
function
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
marginY: 1,
|
|
2263
|
-
borderStyle: "single",
|
|
2264
|
-
borderTop: false,
|
|
2265
|
-
borderRight: false,
|
|
2266
|
-
borderBottom: false,
|
|
2267
|
-
borderColor: theme.assistant,
|
|
2268
|
-
paddingLeft: 1,
|
|
2269
|
-
children: [
|
|
2270
|
-
/* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
|
|
2271
|
-
/* @__PURE__ */ jsx(Text, { bold: true, color: theme.assistant, children: "ASSISTANT" }),
|
|
2272
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " (streaming\u2026)" })
|
|
2273
|
-
] }),
|
|
2274
|
-
/* @__PURE__ */ jsx(Text, { color: "white", children: text })
|
|
2275
|
-
]
|
|
2276
|
-
}
|
|
2277
|
-
);
|
|
2190
|
+
function fmtDuration(ms) {
|
|
2191
|
+
if (ms < 1e3) return `${ms}ms`;
|
|
2192
|
+
if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
|
|
2193
|
+
const totalSec = Math.floor(ms / 1e3);
|
|
2194
|
+
return `${Math.floor(totalSec / 60)}m${totalSec % 60}s`;
|
|
2278
2195
|
}
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
let prose = [];
|
|
2284
|
-
let code = null;
|
|
2285
|
-
let lang = "plain";
|
|
2286
|
-
const flushProse = () => {
|
|
2287
|
-
if (prose.length > 0) {
|
|
2288
|
-
segs.push({ type: "prose", text: prose.join("\n") });
|
|
2289
|
-
prose = [];
|
|
2290
|
-
}
|
|
2291
|
-
};
|
|
2292
|
-
for (const line of lines) {
|
|
2293
|
-
const fence = line.match(/^\s*```(.*)$/);
|
|
2294
|
-
if (fence) {
|
|
2295
|
-
if (code === null) {
|
|
2296
|
-
flushProse();
|
|
2297
|
-
code = [];
|
|
2298
|
-
lang = detectLang(fence[1] ?? "");
|
|
2299
|
-
} else {
|
|
2300
|
-
segs.push({ type: "code", text: code.join("\n"), lang });
|
|
2301
|
-
code = null;
|
|
2302
|
-
lang = "plain";
|
|
2303
|
-
}
|
|
2304
|
-
continue;
|
|
2305
|
-
}
|
|
2306
|
-
if (code !== null) code.push(line);
|
|
2307
|
-
else prose.push(line);
|
|
2308
|
-
}
|
|
2309
|
-
if (code !== null) segs.push({ type: "code", text: code.join("\n"), lang });
|
|
2310
|
-
flushProse();
|
|
2311
|
-
return segs;
|
|
2196
|
+
function fmtBytes(n) {
|
|
2197
|
+
if (n < 1024) return `${n}B`;
|
|
2198
|
+
if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)}KB`;
|
|
2199
|
+
return `${(n / (1024 * 1024)).toFixed(1)}MB`;
|
|
2312
2200
|
}
|
|
2313
|
-
function
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
contentWidth
|
|
2317
|
-
}) {
|
|
2318
|
-
let lines = code.replace(/\n+$/, "").split("\n");
|
|
2319
|
-
const hidden = Math.max(0, lines.length - MAX_CODE_LINES);
|
|
2320
|
-
if (hidden > 0) lines = lines.slice(0, MAX_CODE_LINES);
|
|
2321
|
-
const gutterW = String(lines.length).length;
|
|
2322
|
-
const maxW = Math.max(20, Math.min(contentWidth - 6 - gutterW - 1, 120));
|
|
2323
|
-
let carry = {};
|
|
2324
|
-
const rows = lines.map((raw) => {
|
|
2325
|
-
const display = raw.length > maxW ? `${raw.slice(0, maxW - 1)}\u2026` : raw;
|
|
2326
|
-
const r = highlightLine(display, lang, carry);
|
|
2327
|
-
carry = r.carry;
|
|
2328
|
-
return r.tokens;
|
|
2329
|
-
});
|
|
2330
|
-
return /* @__PURE__ */ jsxs(
|
|
2331
|
-
Box,
|
|
2332
|
-
{
|
|
2333
|
-
flexDirection: "column",
|
|
2334
|
-
marginLeft: 2,
|
|
2335
|
-
marginY: 0,
|
|
2336
|
-
borderStyle: "round",
|
|
2337
|
-
borderColor: theme.borderDefault,
|
|
2338
|
-
paddingX: 1,
|
|
2339
|
-
children: [
|
|
2340
|
-
lang !== "plain" ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: lang }) : null,
|
|
2341
|
-
rows.map((tokens, i) => (
|
|
2342
|
-
// biome-ignore lint/suspicious/noArrayIndexKey: code lines are positional
|
|
2343
|
-
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2344
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: `${String(i + 1).padStart(gutterW, " ")} ` }),
|
|
2345
|
-
tokens.length === 0 ? " " : tokens.map((t, j) => (
|
|
2346
|
-
// biome-ignore lint/suspicious/noArrayIndexKey: token order is stable per line
|
|
2347
|
-
/* @__PURE__ */ jsx(Text, { color: t.color, dimColor: t.dim, bold: t.bold, children: t.text }, j)
|
|
2348
|
-
))
|
|
2349
|
-
] }, i)
|
|
2350
|
-
)),
|
|
2351
|
-
hidden > 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, italic: true, children: `\u2026 +${hidden} more line${hidden === 1 ? "" : "s"}` }) : null
|
|
2352
|
-
]
|
|
2353
|
-
}
|
|
2354
|
-
);
|
|
2201
|
+
function truncMid(s2, max) {
|
|
2202
|
+
if (s2.length <= max) return s2;
|
|
2203
|
+
return `${s2.slice(0, max - 1)}\u2026`;
|
|
2355
2204
|
}
|
|
2356
|
-
function
|
|
2357
|
-
|
|
2358
|
-
termWidth,
|
|
2359
|
-
contentWidth
|
|
2360
|
-
}) {
|
|
2361
|
-
const segments = splitFencedBlocks(text);
|
|
2362
|
-
const inner = contentWidth ?? termWidth;
|
|
2363
|
-
return /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: segments.map(
|
|
2364
|
-
(seg, i) => seg.type === "code" ? (
|
|
2365
|
-
// biome-ignore lint/suspicious/noArrayIndexKey: segment order is stable
|
|
2366
|
-
/* @__PURE__ */ jsx(CodeBlock, { code: seg.text, lang: seg.lang ?? "plain", contentWidth: inner }, i)
|
|
2367
|
-
) : (
|
|
2368
|
-
// biome-ignore lint/suspicious/noArrayIndexKey: segment order is stable
|
|
2369
|
-
/* @__PURE__ */ jsx(MarkdownView, { text: seg.text, termWidth: inner }, i)
|
|
2370
|
-
)
|
|
2371
|
-
) });
|
|
2205
|
+
function stringOf(v) {
|
|
2206
|
+
return typeof v === "string" && v.length > 0 ? v : void 0;
|
|
2372
2207
|
}
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
const t = setInterval(() => setTick((n) => n + 1), 500);
|
|
2384
|
-
return () => clearInterval(t);
|
|
2385
|
-
}, []);
|
|
2386
|
-
const elapsedMs = Date.now() - startedAt;
|
|
2387
|
-
const lines = text.split("\n");
|
|
2388
|
-
const totalLines = lines.length;
|
|
2389
|
-
const hidden = Math.max(0, totalLines - MAX_STREAM_LINES);
|
|
2390
|
-
const visible = hidden > 0 ? lines.slice(hidden) : lines;
|
|
2391
|
-
const contentWidth = Math.max(20, Math.min(termWidth - 4, 100));
|
|
2392
|
-
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 0, children: [
|
|
2393
|
-
/* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
|
|
2394
|
-
/* @__PURE__ */ jsx(Text, { color: theme.warn, children: "\u25C6 " }),
|
|
2395
|
-
/* @__PURE__ */ jsx(Text, { bold: true, color: theme.tool, children: name }),
|
|
2396
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \u23F1 ${fmtDuration(elapsedMs)}` }),
|
|
2397
|
-
hidden > 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` (${totalLines} lines, showing last ${MAX_STREAM_LINES})` }) : null
|
|
2398
|
-
] }),
|
|
2399
|
-
/* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [
|
|
2400
|
-
hidden > 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, italic: true, children: ` \u2026 ${hidden} more line${hidden === 1 ? "" : "s"} above` }) : null,
|
|
2401
|
-
visible.map((line, i) => {
|
|
2402
|
-
const key = i;
|
|
2403
|
-
const trimmed = line.length > contentWidth ? `${line.slice(0, contentWidth - 1)}\u2026` : line;
|
|
2404
|
-
return /* @__PURE__ */ jsx(Text, { dimColor: true, children: trimmed || " " }, key);
|
|
2405
|
-
})
|
|
2406
|
-
] })
|
|
2407
|
-
] });
|
|
2408
|
-
});
|
|
2409
|
-
function tailForDisplay(text, maxChars) {
|
|
2410
|
-
if (text.length <= maxChars) return text;
|
|
2411
|
-
const cut = text.length - maxChars;
|
|
2412
|
-
const nl = text.indexOf("\n", cut);
|
|
2413
|
-
if (nl !== -1 && nl < cut + 80) {
|
|
2414
|
-
return `\u2026 ${text.slice(nl + 1)}`;
|
|
2208
|
+
function numOf(v) {
|
|
2209
|
+
return typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
2210
|
+
}
|
|
2211
|
+
function tryParseJson(s2) {
|
|
2212
|
+
const t = s2.trimStart();
|
|
2213
|
+
if (!t.startsWith("{") && !t.startsWith("[")) return void 0;
|
|
2214
|
+
try {
|
|
2215
|
+
return JSON.parse(s2);
|
|
2216
|
+
} catch {
|
|
2217
|
+
return void 0;
|
|
2415
2218
|
}
|
|
2416
|
-
return `\u2026 ${text.slice(cut)}`;
|
|
2417
2219
|
}
|
|
2418
|
-
function
|
|
2419
|
-
let
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2220
|
+
function scanNumberedRange(text) {
|
|
2221
|
+
let first;
|
|
2222
|
+
let last;
|
|
2223
|
+
let count = 0;
|
|
2224
|
+
for (const line of text.split("\n")) {
|
|
2225
|
+
const m = line.match(/^\s*(\d+)→/);
|
|
2226
|
+
if (m?.[1]) {
|
|
2227
|
+
const n = Number.parseInt(m[1], 10);
|
|
2228
|
+
if (Number.isFinite(n)) {
|
|
2229
|
+
if (first === void 0) first = n;
|
|
2230
|
+
last = n;
|
|
2231
|
+
count++;
|
|
2232
|
+
}
|
|
2425
2233
|
}
|
|
2426
2234
|
}
|
|
2427
|
-
|
|
2428
|
-
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginLeft: 4, marginTop: 0, children: [
|
|
2429
|
-
rows.map((row, i) => {
|
|
2430
|
-
const key = i;
|
|
2431
|
-
if (row.kind === "hunk") {
|
|
2432
|
-
return /* @__PURE__ */ jsx(Text, { color: "cyan", dimColor: true, children: row.text }, key);
|
|
2433
|
-
}
|
|
2434
|
-
if (row.kind === "meta") {
|
|
2435
|
-
return /* @__PURE__ */ jsx(Text, { dimColor: true, children: `${blank} ${row.text}` }, key);
|
|
2436
|
-
}
|
|
2437
|
-
const lnNumber = row.kind === "del" ? row.oldLine : row.newLine;
|
|
2438
|
-
const lnText = typeof lnNumber === "number" ? String(lnNumber).padStart(gutterWidth, " ") : blank;
|
|
2439
|
-
if (row.kind === "ctx") {
|
|
2440
|
-
return /* @__PURE__ */ jsx(Text, { dimColor: true, children: `${lnText} ${row.text}` }, key);
|
|
2441
|
-
}
|
|
2442
|
-
const bg = row.kind === "add" ? theme.diffAddBg : theme.diffDelBg;
|
|
2443
|
-
return /* @__PURE__ */ jsxs(Text, { children: [
|
|
2444
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: `${lnText} ` }),
|
|
2445
|
-
/* @__PURE__ */ jsx(Text, { backgroundColor: bg, color: "black", children: row.text })
|
|
2446
|
-
] }, key);
|
|
2447
|
-
}),
|
|
2448
|
-
hidden > 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, italic: true, children: `${blank} \u2026 ${hidden} more line${hidden === 1 ? "" : "s"}` }) : null
|
|
2449
|
-
] });
|
|
2235
|
+
return { first, last, count };
|
|
2450
2236
|
}
|
|
2451
|
-
function
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
return { icon: "\u2026", color: "magenta" };
|
|
2455
|
-
case "answered":
|
|
2456
|
-
return { icon: "\u2696", color: "cyan" };
|
|
2457
|
-
case "ask_human":
|
|
2458
|
-
return { icon: "?", color: "yellow" };
|
|
2459
|
-
case "denied":
|
|
2460
|
-
return { icon: "\xD7", color: "red" };
|
|
2461
|
-
}
|
|
2237
|
+
function countLines(text) {
|
|
2238
|
+
if (!text) return 0;
|
|
2239
|
+
return text.replace(/\n$/, "").split("\n").length;
|
|
2462
2240
|
}
|
|
2463
|
-
function
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2241
|
+
function firstNonEmpty(text) {
|
|
2242
|
+
if (!text) return void 0;
|
|
2243
|
+
const line = text.split("\n").find((l) => l.trim());
|
|
2244
|
+
return line ? line.replace(/\s+/g, " ").trim() : void 0;
|
|
2245
|
+
}
|
|
2246
|
+
function formatMatchHit(hit) {
|
|
2247
|
+
if (typeof hit === "string") return truncMid(hit, 70);
|
|
2248
|
+
if (hit && typeof hit === "object") {
|
|
2249
|
+
const o = hit;
|
|
2250
|
+
const file = stringOf(o["file"]) ?? stringOf(o["path"]);
|
|
2251
|
+
const line = numOf(o["line"]) ?? numOf(o["lineNumber"]);
|
|
2252
|
+
const snippet2 = stringOf(o["text"]) ?? stringOf(o["match"]) ?? stringOf(o["preview"]);
|
|
2253
|
+
if (file) {
|
|
2254
|
+
const head = line !== void 0 ? `${shortenPath(file, 40)}:${line}` : shortenPath(file, 50);
|
|
2255
|
+
return snippet2 ? `${head} ${truncMid(snippet2.replace(/\s+/g, " "), 40)}` : head;
|
|
2256
|
+
}
|
|
2257
|
+
if (snippet2) return truncMid(snippet2, 70);
|
|
2473
2258
|
}
|
|
2259
|
+
return void 0;
|
|
2474
2260
|
}
|
|
2475
|
-
var
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
switch (
|
|
2480
|
-
case "
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
children: /* @__PURE__ */ jsxs(Text, { children: [
|
|
2491
|
-
/* @__PURE__ */ jsx(Text, { bold: true, color: theme.user, children: "USER " }),
|
|
2492
|
-
/* @__PURE__ */ jsx(Text, { color: "white", children: entry.text }),
|
|
2493
|
-
entry.queued ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: " (queued)" }) : null,
|
|
2494
|
-
entry.pasteContent ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2495
|
-
entry.text ? "\n" : null,
|
|
2496
|
-
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
2497
|
-
" \u21B3 ",
|
|
2498
|
-
entry.pasteContent
|
|
2499
|
-
] })
|
|
2500
|
-
] }) : null
|
|
2501
|
-
] })
|
|
2502
|
-
}
|
|
2503
|
-
);
|
|
2504
|
-
case "assistant": {
|
|
2505
|
-
const contentWidth = assistantContentWidth(termWidth);
|
|
2506
|
-
return /* @__PURE__ */ jsxs(
|
|
2507
|
-
Box,
|
|
2508
|
-
{
|
|
2509
|
-
flexDirection: "column",
|
|
2510
|
-
marginY: 1,
|
|
2511
|
-
borderStyle: "single",
|
|
2512
|
-
borderTop: false,
|
|
2513
|
-
borderRight: false,
|
|
2514
|
-
borderBottom: false,
|
|
2515
|
-
borderColor: theme.assistant,
|
|
2516
|
-
paddingLeft: 1,
|
|
2517
|
-
children: [
|
|
2518
|
-
/* @__PURE__ */ jsx(Box, { flexDirection: "row", children: /* @__PURE__ */ jsx(Text, { bold: true, color: theme.assistant, children: "ASSISTANT" }) }),
|
|
2519
|
-
/* @__PURE__ */ jsx(AssistantBody, { text: entry.text, termWidth, contentWidth })
|
|
2520
|
-
]
|
|
2521
|
-
}
|
|
2522
|
-
);
|
|
2261
|
+
var ARG_BUDGET = 60;
|
|
2262
|
+
function formatToolArgs(toolName, input) {
|
|
2263
|
+
if (!input || typeof input !== "object") return "";
|
|
2264
|
+
const obj = input;
|
|
2265
|
+
switch (toolName) {
|
|
2266
|
+
case "read":
|
|
2267
|
+
case "write":
|
|
2268
|
+
case "edit":
|
|
2269
|
+
case "patch":
|
|
2270
|
+
case "document":
|
|
2271
|
+
case "list_dir":
|
|
2272
|
+
case "ls":
|
|
2273
|
+
case "tree": {
|
|
2274
|
+
const p = stringOf(obj["path"]) ?? stringOf(obj["file"]);
|
|
2275
|
+
return p ? shortenPath(p, ARG_BUDGET) : "";
|
|
2523
2276
|
}
|
|
2524
|
-
case "
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
);
|
|
2533
|
-
const diff = entry.ok ? extractDiffPreview(entry.name, entry.output) : void 0;
|
|
2534
|
-
const sizeChip = (() => {
|
|
2535
|
-
if (!entry.ok) return "";
|
|
2536
|
-
const parts = [];
|
|
2537
|
-
if (entry.outputLines !== void 0 && entry.outputLines > 0) {
|
|
2538
|
-
parts.push(`${entry.outputLines} L`);
|
|
2539
|
-
}
|
|
2540
|
-
if (entry.outputBytes && entry.outputBytes > 0) {
|
|
2541
|
-
parts.push(fmtBytes(entry.outputBytes));
|
|
2542
|
-
}
|
|
2543
|
-
if (entry.outputTokens && entry.outputTokens > 0) {
|
|
2544
|
-
parts.push(`\u2248${fmtTok2(entry.outputTokens)} tok`);
|
|
2545
|
-
}
|
|
2546
|
-
return parts.join(" \xB7 ");
|
|
2547
|
-
})();
|
|
2548
|
-
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
2549
|
-
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2550
|
-
/* @__PURE__ */ jsx(Text, { color: entry.ok ? theme.success : theme.error, children: entry.ok ? "\u25CF" : "\u2717" }),
|
|
2551
|
-
" ",
|
|
2552
|
-
/* @__PURE__ */ jsx(Text, { bold: true, color: theme.tool, children: entry.name }),
|
|
2553
|
-
argSummary ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2554
|
-
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
2555
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: argSummary })
|
|
2556
|
-
] }) : null,
|
|
2557
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \xB7 ${fmtDuration(entry.durationMs)}` }),
|
|
2558
|
-
sizeChip ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \xB7 ${sizeChip}` }) : null
|
|
2559
|
-
] }),
|
|
2560
|
-
outLines.map((line, i) => (
|
|
2561
|
-
// biome-ignore lint/suspicious/noArrayIndexKey: tool output lines are static, index is stable
|
|
2562
|
-
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2563
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: i === outLines.length - 1 && !diff ? " \u2514\u2500 " : " \u251C\u2500 " }),
|
|
2564
|
-
/* @__PURE__ */ jsx(
|
|
2565
|
-
Text,
|
|
2566
|
-
{
|
|
2567
|
-
color: !entry.ok || line.startsWith("!") ? "red" : void 0,
|
|
2568
|
-
dimColor: entry.ok && !line.startsWith("!"),
|
|
2569
|
-
children: line
|
|
2570
|
-
}
|
|
2571
|
-
)
|
|
2572
|
-
] }, i)
|
|
2573
|
-
)),
|
|
2574
|
-
diff ? /* @__PURE__ */ jsx(DiffBlock, { rows: diff.rows, hidden: diff.hidden }) : null
|
|
2575
|
-
] });
|
|
2277
|
+
case "grep":
|
|
2278
|
+
case "search":
|
|
2279
|
+
case "replace": {
|
|
2280
|
+
const pat = stringOf(obj["pattern"]) ?? stringOf(obj["query"]);
|
|
2281
|
+
const scope = stringOf(obj["path"]) ?? stringOf(obj["glob"]);
|
|
2282
|
+
const head = pat ? `"${truncMid(pat, 36)}"` : "";
|
|
2283
|
+
const tail = scope ? ` in ${shortenPath(scope, 28)}` : "";
|
|
2284
|
+
return `${head}${tail}` || (stringOf(obj["command"]) ?? "");
|
|
2576
2285
|
}
|
|
2577
|
-
case "
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
return /* @__PURE__ */ jsx(
|
|
2581
|
-
Box,
|
|
2582
|
-
{
|
|
2583
|
-
borderStyle: "single",
|
|
2584
|
-
borderTop: false,
|
|
2585
|
-
borderRight: false,
|
|
2586
|
-
borderBottom: false,
|
|
2587
|
-
borderColor: theme.warn,
|
|
2588
|
-
paddingLeft: 1,
|
|
2589
|
-
children: /* @__PURE__ */ jsx(Text, { color: theme.warn, children: entry.text })
|
|
2590
|
-
}
|
|
2591
|
-
);
|
|
2592
|
-
case "error":
|
|
2593
|
-
return /* @__PURE__ */ jsx(
|
|
2594
|
-
Box,
|
|
2595
|
-
{
|
|
2596
|
-
borderStyle: "single",
|
|
2597
|
-
borderTop: false,
|
|
2598
|
-
borderRight: false,
|
|
2599
|
-
borderBottom: false,
|
|
2600
|
-
borderColor: theme.error,
|
|
2601
|
-
paddingLeft: 1,
|
|
2602
|
-
children: /* @__PURE__ */ jsx(Text, { color: theme.error, children: entry.text })
|
|
2603
|
-
}
|
|
2604
|
-
);
|
|
2605
|
-
case "turn-summary":
|
|
2606
|
-
return /* @__PURE__ */ jsx(Text, { dimColor: true, children: entry.text });
|
|
2607
|
-
case "brain": {
|
|
2608
|
-
const statusStyle = brainStatusStyle(entry.status);
|
|
2609
|
-
const riskColor2 = brainRiskColor(entry.risk);
|
|
2610
|
-
return /* @__PURE__ */ jsxs(
|
|
2611
|
-
Box,
|
|
2612
|
-
{
|
|
2613
|
-
flexDirection: "column",
|
|
2614
|
-
marginY: 1,
|
|
2615
|
-
borderStyle: "single",
|
|
2616
|
-
borderTop: false,
|
|
2617
|
-
borderRight: false,
|
|
2618
|
-
borderBottom: false,
|
|
2619
|
-
borderColor: "magenta",
|
|
2620
|
-
paddingLeft: 1,
|
|
2621
|
-
children: [
|
|
2622
|
-
/* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
|
|
2623
|
-
/* @__PURE__ */ jsx(Text, { bold: true, color: "magenta", children: "BRAIN" }),
|
|
2624
|
-
/* @__PURE__ */ jsx(Text, { color: statusStyle.color, children: statusStyle.icon }),
|
|
2625
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: entry.source }),
|
|
2626
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
|
|
2627
|
-
/* @__PURE__ */ jsx(Text, { color: riskColor2, children: entry.risk })
|
|
2628
|
-
] }),
|
|
2629
|
-
/* @__PURE__ */ jsx(Text, { color: "white", children: entry.question }),
|
|
2630
|
-
entry.decision ? /* @__PURE__ */ jsxs(Text, { children: [
|
|
2631
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "Decision: " }),
|
|
2632
|
-
/* @__PURE__ */ jsx(Text, { color: statusStyle.color, children: entry.decision })
|
|
2633
|
-
] }) : null,
|
|
2634
|
-
entry.rationale ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: entry.rationale }) : null
|
|
2635
|
-
]
|
|
2636
|
-
}
|
|
2637
|
-
);
|
|
2638
|
-
}
|
|
2639
|
-
case "confirm":
|
|
2640
|
-
return /* @__PURE__ */ jsxs(
|
|
2641
|
-
Box,
|
|
2642
|
-
{
|
|
2643
|
-
flexDirection: "column",
|
|
2644
|
-
borderStyle: "round",
|
|
2645
|
-
borderColor: "yellow",
|
|
2646
|
-
paddingX: 1,
|
|
2647
|
-
marginY: 1,
|
|
2648
|
-
children: [
|
|
2649
|
-
/* @__PURE__ */ jsxs(Text, { bold: true, color: "yellow", children: [
|
|
2650
|
-
"\u26A0 Confirm: ",
|
|
2651
|
-
entry.toolName
|
|
2652
|
-
] }),
|
|
2653
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "Waiting for y / n / a / d..." })
|
|
2654
|
-
]
|
|
2655
|
-
}
|
|
2656
|
-
);
|
|
2657
|
-
case "banner":
|
|
2658
|
-
return /* @__PURE__ */ jsx(Banner, { entry });
|
|
2659
|
-
case "subagent": {
|
|
2660
|
-
const lines = entry.text.split("\n");
|
|
2661
|
-
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
2662
|
-
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2663
|
-
/* @__PURE__ */ jsx(Text, { color: entry.agentColor, bold: true, children: `[${entry.agentLabel}]` }),
|
|
2664
|
-
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
2665
|
-
/* @__PURE__ */ jsx(Text, { color: entry.agentColor, children: entry.icon }),
|
|
2666
|
-
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
2667
|
-
/* @__PURE__ */ jsx(Text, { children: lines[0] ?? "" }),
|
|
2668
|
-
entry.detail ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2669
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " \xB7 " }),
|
|
2670
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: entry.detail })
|
|
2671
|
-
] }) : null
|
|
2672
|
-
] }),
|
|
2673
|
-
lines.slice(1).map((line, i) => (
|
|
2674
|
-
// biome-ignore lint/suspicious/noArrayIndexKey: stable line index
|
|
2675
|
-
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2676
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " " }),
|
|
2677
|
-
/* @__PURE__ */ jsx(Text, { children: line })
|
|
2678
|
-
] }, i)
|
|
2679
|
-
))
|
|
2680
|
-
] });
|
|
2681
|
-
}
|
|
2682
|
-
}
|
|
2683
|
-
});
|
|
2684
|
-
function Banner({
|
|
2685
|
-
entry
|
|
2686
|
-
}) {
|
|
2687
|
-
const cwdShort = shortenPath(entry.cwd, 48);
|
|
2688
|
-
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", paddingX: 2, paddingY: 0, children: [
|
|
2689
|
-
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2690
|
-
/* @__PURE__ */ jsx(Text, { color: "magenta", bold: true, children: " \u259F\u259B " }),
|
|
2691
|
-
/* @__PURE__ */ jsx(Text, { color: "magenta", bold: true, children: "WrongStack" }),
|
|
2692
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " v" }),
|
|
2693
|
-
/* @__PURE__ */ jsx(Text, { children: entry.version })
|
|
2694
|
-
] }),
|
|
2695
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, italic: true, children: " Built on the wrong stack. Shipped anyway." }),
|
|
2696
|
-
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2697
|
-
/* @__PURE__ */ jsx(Text, { color: "cyan", children: " provider " }),
|
|
2698
|
-
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2699
|
-
entry.provider,
|
|
2700
|
-
"/",
|
|
2701
|
-
entry.model
|
|
2702
|
-
] })
|
|
2703
|
-
] }),
|
|
2704
|
-
entry.family ? /* @__PURE__ */ jsxs(Text, { children: [
|
|
2705
|
-
/* @__PURE__ */ jsx(Text, { color: "cyan", children: " family " }),
|
|
2706
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: entry.family })
|
|
2707
|
-
] }) : null,
|
|
2708
|
-
entry.keyTail ? /* @__PURE__ */ jsxs(Text, { children: [
|
|
2709
|
-
/* @__PURE__ */ jsx(Text, { color: "cyan", children: " key " }),
|
|
2710
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u25CF\u25CF\u25CF\u2026" }),
|
|
2711
|
-
/* @__PURE__ */ jsx(Text, { children: entry.keyTail })
|
|
2712
|
-
] }) : null,
|
|
2713
|
-
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2714
|
-
/* @__PURE__ */ jsx(Text, { color: "cyan", children: " cwd " }),
|
|
2715
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: cwdShort })
|
|
2716
|
-
] }),
|
|
2717
|
-
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2718
|
-
/* @__PURE__ */ jsx(Text, { color: "cyan", children: " hints " }),
|
|
2719
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "/help \xB7 /init \xB7 /memory \xB7 /queue \xB7 /exit" })
|
|
2720
|
-
] })
|
|
2721
|
-
] });
|
|
2722
|
-
}
|
|
2723
|
-
function shortenPath(p, max) {
|
|
2724
|
-
if (p.length <= max) return p;
|
|
2725
|
-
return `\u2026${p.slice(p.length - (max - 1))}`;
|
|
2726
|
-
}
|
|
2727
|
-
function fmtTok2(n) {
|
|
2728
|
-
if (!Number.isFinite(n) || n <= 0) return "0";
|
|
2729
|
-
if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
|
|
2730
|
-
if (n >= 1e3) return `${(n / 1e3).toFixed(n >= 1e4 ? 0 : 1)}k`;
|
|
2731
|
-
return String(n);
|
|
2732
|
-
}
|
|
2733
|
-
function fmtDuration(ms) {
|
|
2734
|
-
if (ms < 1e3) return `${ms}ms`;
|
|
2735
|
-
if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
|
|
2736
|
-
const totalSec = Math.floor(ms / 1e3);
|
|
2737
|
-
return `${Math.floor(totalSec / 60)}m${totalSec % 60}s`;
|
|
2738
|
-
}
|
|
2739
|
-
var ARG_BUDGET = 60;
|
|
2740
|
-
var OUT_BUDGET = 80;
|
|
2741
|
-
function formatToolArgs(toolName, input) {
|
|
2742
|
-
if (!input || typeof input !== "object") return "";
|
|
2743
|
-
const obj = input;
|
|
2744
|
-
switch (toolName) {
|
|
2745
|
-
case "read":
|
|
2746
|
-
case "write":
|
|
2747
|
-
case "edit":
|
|
2748
|
-
case "patch":
|
|
2749
|
-
case "document":
|
|
2750
|
-
case "list_dir":
|
|
2751
|
-
case "ls":
|
|
2752
|
-
case "tree": {
|
|
2753
|
-
const p = stringOf(obj["path"]) ?? stringOf(obj["file"]);
|
|
2754
|
-
return p ? shortenPath(p, ARG_BUDGET) : "";
|
|
2755
|
-
}
|
|
2756
|
-
case "grep":
|
|
2757
|
-
case "search":
|
|
2758
|
-
case "replace": {
|
|
2759
|
-
const pat = stringOf(obj["pattern"]) ?? stringOf(obj["query"]);
|
|
2760
|
-
const scope = stringOf(obj["path"]) ?? stringOf(obj["glob"]);
|
|
2761
|
-
const head = pat ? `"${truncMid(pat, 36)}"` : "";
|
|
2762
|
-
const tail = scope ? ` in ${shortenPath(scope, 28)}` : "";
|
|
2763
|
-
return `${head}${tail}` || (stringOf(obj["command"]) ?? "");
|
|
2764
|
-
}
|
|
2765
|
-
case "glob": {
|
|
2766
|
-
const pat = stringOf(obj["pattern"]) ?? stringOf(obj["glob"]);
|
|
2767
|
-
return pat ? `"${truncMid(pat, ARG_BUDGET - 2)}"` : "";
|
|
2286
|
+
case "glob": {
|
|
2287
|
+
const pat = stringOf(obj["pattern"]) ?? stringOf(obj["glob"]);
|
|
2288
|
+
return pat ? `"${truncMid(pat, ARG_BUDGET - 2)}"` : "";
|
|
2768
2289
|
}
|
|
2769
2290
|
case "bash":
|
|
2770
2291
|
case "shell":
|
|
@@ -2847,77 +2368,104 @@ function formatToolArgs(toolName, input) {
|
|
|
2847
2368
|
return "";
|
|
2848
2369
|
}
|
|
2849
2370
|
}
|
|
2371
|
+
var OUT_BUDGET = 80;
|
|
2372
|
+
var GENERIC_BUDGET = 240;
|
|
2373
|
+
function summarizeJsonObject(obj) {
|
|
2374
|
+
const keys = Object.keys(obj);
|
|
2375
|
+
if (keys.length === 0) return null;
|
|
2376
|
+
const priority = [
|
|
2377
|
+
"ok",
|
|
2378
|
+
"status",
|
|
2379
|
+
"timedOut",
|
|
2380
|
+
"stopReason",
|
|
2381
|
+
"reason",
|
|
2382
|
+
"error",
|
|
2383
|
+
"message",
|
|
2384
|
+
"result",
|
|
2385
|
+
"summary",
|
|
2386
|
+
"iterations",
|
|
2387
|
+
"toolCalls",
|
|
2388
|
+
"durationMs",
|
|
2389
|
+
"subagentId",
|
|
2390
|
+
"taskId"
|
|
2391
|
+
];
|
|
2392
|
+
const ordered = [
|
|
2393
|
+
...priority.filter((k) => keys.includes(k)),
|
|
2394
|
+
...keys.filter((k) => !priority.includes(k))
|
|
2395
|
+
];
|
|
2396
|
+
const parts = [];
|
|
2397
|
+
let used = 0;
|
|
2398
|
+
for (const key of ordered) {
|
|
2399
|
+
const v = obj[key];
|
|
2400
|
+
if (v === void 0 || v === null) continue;
|
|
2401
|
+
const rendered = typeof v === "string" ? `${key}="${truncMid(v.replace(/\s+/g, " "), 80)}"` : typeof v === "number" || typeof v === "boolean" ? `${key}=${v}` : Array.isArray(v) ? `${key}=[${v.length}]` : `${key}={\u2026}`;
|
|
2402
|
+
if (used + rendered.length > GENERIC_BUDGET) {
|
|
2403
|
+
parts.push("\u2026");
|
|
2404
|
+
break;
|
|
2405
|
+
}
|
|
2406
|
+
parts.push(rendered);
|
|
2407
|
+
used += rendered.length + 3;
|
|
2408
|
+
}
|
|
2409
|
+
return parts.length > 0 ? parts.join(" \xB7 ") : null;
|
|
2410
|
+
}
|
|
2850
2411
|
function formatToolOutput(toolName, output, ok, _outputBytes, outputLines) {
|
|
2851
2412
|
if (!output) return ok ? [] : ["failed"];
|
|
2852
2413
|
const text = output.trim();
|
|
2853
2414
|
if (!text) return ok ? [] : ["failed"];
|
|
2854
2415
|
const json = tryParseJson(text);
|
|
2855
|
-
if (toolName === "write") {
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
if (bytes !== void 0) return [`${tag} \xB7 ${fmtBytes(bytes)}`];
|
|
2862
|
-
return [tag];
|
|
2863
|
-
}
|
|
2416
|
+
if (toolName === "write" && json && typeof json === "object") {
|
|
2417
|
+
const o = json;
|
|
2418
|
+
const bytes = numOf(o["bytes_written"]) ?? numOf(o["bytes"]);
|
|
2419
|
+
const created = o["created"] === true;
|
|
2420
|
+
const tag = created ? "created" : "updated";
|
|
2421
|
+
return bytes !== void 0 ? [`${tag} \xB7 ${fmtBytes(bytes)}`] : [tag];
|
|
2864
2422
|
}
|
|
2865
|
-
if (toolName === "edit") {
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
if (reps !== void 0) return [`${reps} replacement${reps === 1 ? "" : "s"}`];
|
|
2870
|
-
}
|
|
2423
|
+
if (toolName === "edit" && json && typeof json === "object") {
|
|
2424
|
+
const o = json;
|
|
2425
|
+
const reps = numOf(o["replacements"]);
|
|
2426
|
+
if (reps !== void 0) return [`${reps} replacement${reps === 1 ? "" : "s"}`];
|
|
2871
2427
|
}
|
|
2872
|
-
if (toolName === "patch") {
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
lines.push(parts.join(" \xB7 "));
|
|
2884
|
-
}
|
|
2885
|
-
if (files && files.length > 0) {
|
|
2886
|
-
const first = stringOf(files[0]) ?? "";
|
|
2887
|
-
const more = files.length > 1 ? ` (+${files.length - 1})` : "";
|
|
2888
|
-
lines.push(`${shortenPath(first, 60)}${more}`);
|
|
2889
|
-
}
|
|
2890
|
-
if (lines.length > 0) return lines;
|
|
2428
|
+
if (toolName === "patch" && json && typeof json === "object") {
|
|
2429
|
+
const o = json;
|
|
2430
|
+
const applied = numOf(o["applied"]);
|
|
2431
|
+
const rejected = numOf(o["rejected"]);
|
|
2432
|
+
const files = Array.isArray(o["files"]) ? o["files"] : void 0;
|
|
2433
|
+
const lines = [];
|
|
2434
|
+
if (applied !== void 0 || rejected !== void 0) {
|
|
2435
|
+
const parts = [];
|
|
2436
|
+
if (applied !== void 0) parts.push(`${applied} applied`);
|
|
2437
|
+
if (rejected !== void 0 && rejected > 0) parts.push(`${rejected} rejected`);
|
|
2438
|
+
lines.push(parts.join(" \xB7 "));
|
|
2891
2439
|
}
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
const files = numOf(o["files_modified"]);
|
|
2897
|
-
const reps = numOf(o["total_replacements"]);
|
|
2898
|
-
if (files !== void 0 && reps !== void 0) {
|
|
2899
|
-
return [
|
|
2900
|
-
`${reps} replacement${reps === 1 ? "" : "s"} in ${files} file${files === 1 ? "" : "s"}`
|
|
2901
|
-
];
|
|
2902
|
-
}
|
|
2440
|
+
if (files && files.length > 0) {
|
|
2441
|
+
const first = stringOf(files[0]) ?? "";
|
|
2442
|
+
const more = files.length > 1 ? ` (+${files.length - 1})` : "";
|
|
2443
|
+
lines.push(`${shortenPath(first, 60)}${more}`);
|
|
2903
2444
|
}
|
|
2445
|
+
if (lines.length > 0) return lines;
|
|
2904
2446
|
}
|
|
2905
|
-
if (toolName === "
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
const diff = stringOf(o["diff"]);
|
|
2912
|
-
if (!diff) return [files && files.length === 0 ? "no changes" : "empty diff"];
|
|
2913
|
-
const head = [];
|
|
2914
|
-
if (mode) head.push(mode);
|
|
2915
|
-
if (files && files.length > 0)
|
|
2916
|
-
head.push(`${files.length} file${files.length === 1 ? "" : "s"}`);
|
|
2917
|
-
if (truncated) head.push("truncated");
|
|
2918
|
-
return head.length > 0 ? [head.join(" \xB7 ")] : [];
|
|
2447
|
+
if (toolName === "replace" && json && typeof json === "object") {
|
|
2448
|
+
const o = json;
|
|
2449
|
+
const files = numOf(o["files_modified"]);
|
|
2450
|
+
const reps = numOf(o["total_replacements"]);
|
|
2451
|
+
if (files !== void 0 && reps !== void 0) {
|
|
2452
|
+
return [`${reps} replacement${reps === 1 ? "" : "s"} in ${files} file${files === 1 ? "" : "s"}`];
|
|
2919
2453
|
}
|
|
2920
2454
|
}
|
|
2455
|
+
if (toolName === "diff" && json && typeof json === "object") {
|
|
2456
|
+
const o = json;
|
|
2457
|
+
const diffFiles = Array.isArray(o["files"]) ? o["files"] : void 0;
|
|
2458
|
+
const truncated = o["truncated"] === true;
|
|
2459
|
+
const mode = stringOf(o["mode"]);
|
|
2460
|
+
const diff = stringOf(o["diff"]);
|
|
2461
|
+
if (!diff) return [diffFiles && diffFiles.length === 0 ? "no changes" : "empty diff"];
|
|
2462
|
+
const head = [];
|
|
2463
|
+
if (mode) head.push(mode);
|
|
2464
|
+
if (diffFiles && diffFiles.length > 0)
|
|
2465
|
+
head.push(`${diffFiles.length} file${diffFiles.length === 1 ? "" : "s"}`);
|
|
2466
|
+
if (truncated) head.push("truncated");
|
|
2467
|
+
return head.length > 0 ? [head.join(" \xB7 ")] : [];
|
|
2468
|
+
}
|
|
2921
2469
|
if (toolName === "read") {
|
|
2922
2470
|
if (outputLines !== void 0) return [];
|
|
2923
2471
|
if (json && typeof json === "object") {
|
|
@@ -2927,9 +2475,7 @@ function formatToolOutput(toolName, output, ok, _outputBytes, outputLines) {
|
|
|
2927
2475
|
}
|
|
2928
2476
|
const range = scanNumberedRange(text);
|
|
2929
2477
|
if (range.count > 0 && range.first !== void 0 && range.last !== void 0) {
|
|
2930
|
-
if (range.first === range.last) {
|
|
2931
|
-
return [`L${range.first} \xB7 ${fmtBytes(text.length)}`];
|
|
2932
|
-
}
|
|
2478
|
+
if (range.first === range.last) return [`L${range.first} \xB7 ${fmtBytes(text.length)}`];
|
|
2933
2479
|
const contiguous = range.count === range.last - range.first + 1;
|
|
2934
2480
|
const head = `L${range.first}\u2013${range.last}`;
|
|
2935
2481
|
const tail = contiguous ? `${range.count} line${range.count === 1 ? "" : "s"}` : `${range.count} lines (gaps)`;
|
|
@@ -2978,9 +2524,7 @@ function formatToolOutput(toolName, output, ok, _outputBytes, outputLines) {
|
|
|
2978
2524
|
if (lines.length > 0) return lines;
|
|
2979
2525
|
}
|
|
2980
2526
|
}
|
|
2981
|
-
if (toolName === "todo")
|
|
2982
|
-
return ok ? [] : [text.split("\n")[0] ?? ""];
|
|
2983
|
-
}
|
|
2527
|
+
if (toolName === "todo") return ok ? [] : [text.split("\n")[0] ?? ""];
|
|
2984
2528
|
if (toolName === "fetch" || toolName === "webfetch" || toolName === "web_fetch") {
|
|
2985
2529
|
if (json && typeof json === "object") {
|
|
2986
2530
|
const o = json;
|
|
@@ -3000,200 +2544,174 @@ function formatToolOutput(toolName, output, ok, _outputBytes, outputLines) {
|
|
|
3000
2544
|
if (lines.length > 0) return lines;
|
|
3001
2545
|
}
|
|
3002
2546
|
}
|
|
3003
|
-
if (toolName === "git") {
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
if (lines.length > 0) return lines;
|
|
3022
|
-
}
|
|
2547
|
+
if (toolName === "git" && json && typeof json === "object") {
|
|
2548
|
+
const o = json;
|
|
2549
|
+
const exit = numOf(o["exitCode"]) ?? numOf(o["exit_code"]);
|
|
2550
|
+
const stdout = stringOf(o["stdout"]) ?? "";
|
|
2551
|
+
const stderr = stringOf(o["stderr"]) ?? "";
|
|
2552
|
+
const head = [];
|
|
2553
|
+
if (exit !== void 0) head.push(`exit ${exit}`);
|
|
2554
|
+
const stdoutLines = countLines(stdout);
|
|
2555
|
+
const stderrLines = countLines(stderr);
|
|
2556
|
+
const lparts = [];
|
|
2557
|
+
if (stdoutLines > 0) lparts.push(`${stdoutLines} out`);
|
|
2558
|
+
if (stderrLines > 0) lparts.push(`${stderrLines} err`);
|
|
2559
|
+
if (lparts.length > 0) head.push(lparts.join(" \xB7 "));
|
|
2560
|
+
const lines = [];
|
|
2561
|
+
if (head.length > 0) lines.push(head.join(" \xB7 "));
|
|
2562
|
+
const preview = firstNonEmpty(stdout) ?? firstNonEmpty(stderr);
|
|
2563
|
+
if (preview) lines.push(`"${truncMid(preview, 70)}"`);
|
|
2564
|
+
if (lines.length > 0) return lines;
|
|
3023
2565
|
}
|
|
3024
|
-
if (toolName === "lint") {
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
return [head.join(" \xB7 ")];
|
|
3039
|
-
}
|
|
2566
|
+
if (toolName === "lint" && json && typeof json === "object") {
|
|
2567
|
+
const o = json;
|
|
2568
|
+
const linter = stringOf(o["linter"]);
|
|
2569
|
+
const files = numOf(o["files_checked"]);
|
|
2570
|
+
const errors = numOf(o["errors"]) ?? 0;
|
|
2571
|
+
const warnings = numOf(o["warnings"]) ?? 0;
|
|
2572
|
+
const fix = o["fix_applied"] === true;
|
|
2573
|
+
const head = [];
|
|
2574
|
+
if (linter && linter !== "none") head.push(linter);
|
|
2575
|
+
head.push(`${errors} error${errors === 1 ? "" : "s"}`);
|
|
2576
|
+
head.push(`${warnings} warning${warnings === 1 ? "" : "s"}`);
|
|
2577
|
+
if (files !== void 0) head.push(`${files} file${files === 1 ? "" : "s"}`);
|
|
2578
|
+
if (fix) head.push("fixed");
|
|
2579
|
+
return [head.join(" \xB7 ")];
|
|
3040
2580
|
}
|
|
3041
|
-
if (toolName === "format") {
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
}
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
return head.length > 0 ? [head.join(" \xB7 ")] : [];
|
|
3055
|
-
}
|
|
2581
|
+
if (toolName === "format" && json && typeof json === "object") {
|
|
2582
|
+
const o = json;
|
|
2583
|
+
const fixer = stringOf(o["fixer"]);
|
|
2584
|
+
const checked = numOf(o["files_checked"]);
|
|
2585
|
+
const changed = numOf(o["files_changed"]);
|
|
2586
|
+
const head = [];
|
|
2587
|
+
if (fixer && fixer !== "none") head.push(fixer);
|
|
2588
|
+
if (changed !== void 0 && checked !== void 0) {
|
|
2589
|
+
head.push(`${changed}/${checked} changed`);
|
|
2590
|
+
} else if (changed !== void 0) {
|
|
2591
|
+
head.push(`${changed} changed`);
|
|
2592
|
+
}
|
|
2593
|
+
return head.length > 0 ? [head.join(" \xB7 ")] : [];
|
|
3056
2594
|
}
|
|
3057
|
-
if (toolName === "typecheck") {
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
if (lines.length > 0) return lines;
|
|
3071
|
-
}
|
|
2595
|
+
if (toolName === "typecheck" && json && typeof json === "object") {
|
|
2596
|
+
const o = json;
|
|
2597
|
+
const exit = numOf(o["exit_code"]) ?? numOf(o["exitCode"]);
|
|
2598
|
+
const errors = numOf(o["errors"]);
|
|
2599
|
+
const head = [];
|
|
2600
|
+
if (errors !== void 0) head.push(`${errors} error${errors === 1 ? "" : "s"}`);
|
|
2601
|
+
if (exit !== void 0) head.push(`exit ${exit}`);
|
|
2602
|
+
const stdout = stringOf(o["output"]) ?? stringOf(o["stdout"]) ?? "";
|
|
2603
|
+
const lines = [];
|
|
2604
|
+
if (head.length > 0) lines.push(head.join(" \xB7 "));
|
|
2605
|
+
const preview = firstNonEmpty(stdout);
|
|
2606
|
+
if (preview && (!errors || errors > 0)) lines.push(`"${truncMid(preview, 70)}"`);
|
|
2607
|
+
if (lines.length > 0) return lines;
|
|
3072
2608
|
}
|
|
3073
|
-
if (toolName === "test") {
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
return [head.join(" \xB7 ")];
|
|
3087
|
-
}
|
|
2609
|
+
if (toolName === "test" && json && typeof json === "object") {
|
|
2610
|
+
const o = json;
|
|
2611
|
+
const runner = stringOf(o["runner"]);
|
|
2612
|
+
const total = numOf(o["tests_run"]) ?? 0;
|
|
2613
|
+
const passed = numOf(o["passed"]) ?? 0;
|
|
2614
|
+
const failed = numOf(o["failed"]) ?? 0;
|
|
2615
|
+
const duration = numOf(o["duration_ms"]);
|
|
2616
|
+
const head = [];
|
|
2617
|
+
if (runner && runner !== "none") head.push(runner);
|
|
2618
|
+
head.push(`${passed}/${total} passed`);
|
|
2619
|
+
if (failed > 0) head.push(`${failed} failed`);
|
|
2620
|
+
if (duration !== void 0) head.push(fmtDuration(duration));
|
|
2621
|
+
return [head.join(" \xB7 ")];
|
|
3088
2622
|
}
|
|
3089
|
-
if (toolName === "audit") {
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
return summary && summary.toLowerCase() !== head.toLowerCase() ? [head, truncMid(summary, OUT_BUDGET)] : [head];
|
|
3097
|
-
}
|
|
2623
|
+
if (toolName === "audit" && json && typeof json === "object") {
|
|
2624
|
+
const o = json;
|
|
2625
|
+
const total = numOf(o["total"]) ?? 0;
|
|
2626
|
+
const summary = stringOf(o["summary"]);
|
|
2627
|
+
if (total === 0) return ["no vulnerabilities"];
|
|
2628
|
+
const head = `${total} vulnerabilit${total === 1 ? "y" : "ies"}`;
|
|
2629
|
+
return summary && summary.toLowerCase() !== head.toLowerCase() ? [head, truncMid(summary, OUT_BUDGET)] : [head];
|
|
3098
2630
|
}
|
|
3099
|
-
if (toolName === "outdated") {
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
return lines;
|
|
3118
|
-
}
|
|
2631
|
+
if (toolName === "outdated" && json && typeof json === "object") {
|
|
2632
|
+
const o = json;
|
|
2633
|
+
const total = numOf(o["total"]) ?? 0;
|
|
2634
|
+
const pkgs = Array.isArray(o["packages"]) ? o["packages"] : void 0;
|
|
2635
|
+
if (total === 0) return ["all up to date"];
|
|
2636
|
+
const lines = [`${total} outdated`];
|
|
2637
|
+
if (pkgs && pkgs.length > 0) {
|
|
2638
|
+
const first = pkgs[0];
|
|
2639
|
+
if (first && typeof first === "object") {
|
|
2640
|
+
const p = first;
|
|
2641
|
+
const name = stringOf(p["name"]) ?? stringOf(p["package"]);
|
|
2642
|
+
const cur = stringOf(p["current"]);
|
|
2643
|
+
const wanted = stringOf(p["wanted"]) ?? stringOf(p["latest"]);
|
|
2644
|
+
if (name && cur && wanted) lines.push(`${name}: ${cur} \u2192 ${wanted}`);
|
|
2645
|
+
else if (name) lines.push(name);
|
|
2646
|
+
}
|
|
2647
|
+
}
|
|
2648
|
+
return lines;
|
|
3119
2649
|
}
|
|
3120
|
-
if (toolName === "tree") {
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
return parts.length > 0 ? [parts.join(" \xB7 ")] : [];
|
|
3131
|
-
}
|
|
2650
|
+
if (toolName === "tree" && json && typeof json === "object") {
|
|
2651
|
+
const o = json;
|
|
2652
|
+
const files = numOf(o["total_files"]);
|
|
2653
|
+
const dirs = numOf(o["total_dirs"]);
|
|
2654
|
+
const truncated = o["truncated"] === true;
|
|
2655
|
+
const parts = [];
|
|
2656
|
+
if (files !== void 0) parts.push(`${files} file${files === 1 ? "" : "s"}`);
|
|
2657
|
+
if (dirs !== void 0) parts.push(`${dirs} dir${dirs === 1 ? "" : "s"}`);
|
|
2658
|
+
if (truncated) parts.push("truncated");
|
|
2659
|
+
return parts.length > 0 ? [parts.join(" \xB7 ")] : [];
|
|
3132
2660
|
}
|
|
3133
|
-
if (toolName === "json") {
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
return parts.length > 0 ? [parts.join(" \xB7 ")] : [];
|
|
3144
|
-
}
|
|
2661
|
+
if (toolName === "json" && json && typeof json === "object") {
|
|
2662
|
+
const o = json;
|
|
2663
|
+
const err = stringOf(o["error"]);
|
|
2664
|
+
if (err) return [truncMid(err, OUT_BUDGET)];
|
|
2665
|
+
const type = stringOf(o["type"]);
|
|
2666
|
+
const keys = Array.isArray(o["keys"]) ? o["keys"] : void 0;
|
|
2667
|
+
const parts = [];
|
|
2668
|
+
if (type) parts.push(type);
|
|
2669
|
+
if (keys) parts.push(`${keys.length} key${keys.length === 1 ? "" : "s"}`);
|
|
2670
|
+
return parts.length > 0 ? [parts.join(" \xB7 ")] : [];
|
|
3145
2671
|
}
|
|
3146
|
-
if (toolName === "install") {
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
if (lines.length > 0) return lines;
|
|
3162
|
-
}
|
|
2672
|
+
if (toolName === "install" && json && typeof json === "object") {
|
|
2673
|
+
const o = json;
|
|
2674
|
+
const exit = numOf(o["exit_code"]) ?? numOf(o["exitCode"]);
|
|
2675
|
+
const added = numOf(o["added"]);
|
|
2676
|
+
const removed = numOf(o["removed"]);
|
|
2677
|
+
const head = [];
|
|
2678
|
+
if (exit !== void 0) head.push(`exit ${exit}`);
|
|
2679
|
+
if (added !== void 0) head.push(`+${added}`);
|
|
2680
|
+
if (removed !== void 0) head.push(`-${removed}`);
|
|
2681
|
+
const stdout = stringOf(o["stdout"]) ?? stringOf(o["output"]) ?? "";
|
|
2682
|
+
const lines = [];
|
|
2683
|
+
if (head.length > 0) lines.push(head.join(" \xB7 "));
|
|
2684
|
+
const preview = firstNonEmpty(stdout);
|
|
2685
|
+
if (preview) lines.push(`"${truncMid(preview, 70)}"`);
|
|
2686
|
+
if (lines.length > 0) return lines;
|
|
3163
2687
|
}
|
|
3164
|
-
if (toolName === "scaffold") {
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
if (parts.length > 0) return [parts.join(" \xB7 ")];
|
|
3173
|
-
}
|
|
2688
|
+
if (toolName === "scaffold" && json && typeof json === "object") {
|
|
2689
|
+
const o = json;
|
|
2690
|
+
const created = Array.isArray(o["created"]) ? o["created"] : void 0;
|
|
2691
|
+
const skipped = Array.isArray(o["skipped"]) ? o["skipped"] : void 0;
|
|
2692
|
+
const parts = [];
|
|
2693
|
+
if (created !== void 0) parts.push(`${created.length} created`);
|
|
2694
|
+
if (skipped !== void 0 && skipped.length > 0) parts.push(`${skipped.length} skipped`);
|
|
2695
|
+
if (parts.length > 0) return [parts.join(" \xB7 ")];
|
|
3174
2696
|
}
|
|
3175
2697
|
if (toolName === "remember" || toolName === "forget" || toolName === "memory") {
|
|
3176
2698
|
return ok ? [toolName === "forget" ? "removed" : "saved"] : [text.split("\n")[0] ?? ""];
|
|
3177
2699
|
}
|
|
3178
|
-
if (toolName === "mode") {
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
const
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
const firstHit = matches && matches.length > 0 ? formatMatchHit(matches[0]) : void 0;
|
|
3194
|
-
if (firstHit) lines.push(firstHit);
|
|
3195
|
-
return lines;
|
|
3196
|
-
}
|
|
2700
|
+
if (toolName === "mode" && json && typeof json === "object") {
|
|
2701
|
+
const o = json;
|
|
2702
|
+
const mode = stringOf(o["mode"]) ?? stringOf(o["active"]) ?? stringOf(o["name"]);
|
|
2703
|
+
if (mode) return [`mode: ${mode}`];
|
|
2704
|
+
}
|
|
2705
|
+
if (toolName === "search" && json && typeof json === "object") {
|
|
2706
|
+
const o = json;
|
|
2707
|
+
const matches = Array.isArray(o["matches"]) ? o["matches"] : Array.isArray(o["results"]) ? o["results"] : void 0;
|
|
2708
|
+
const count = numOf(o["count"]) ?? matches?.length;
|
|
2709
|
+
if (count !== void 0) {
|
|
2710
|
+
if (count === 0) return ["no results"];
|
|
2711
|
+
const lines = [`${count} result${count === 1 ? "" : "s"}`];
|
|
2712
|
+
const firstHit = matches && matches.length > 0 ? formatMatchHit(matches[0]) : void 0;
|
|
2713
|
+
if (firstHit) lines.push(firstHit);
|
|
2714
|
+
return lines;
|
|
3197
2715
|
}
|
|
3198
2716
|
}
|
|
3199
2717
|
if (toolName === "logs") {
|
|
@@ -3210,86 +2728,127 @@ function formatToolOutput(toolName, output, ok, _outputBytes, outputLines) {
|
|
|
3210
2728
|
const collapsed = text.replace(/\s+/g, " ").trim();
|
|
3211
2729
|
return [truncMid(collapsed, GENERIC_BUDGET)];
|
|
3212
2730
|
}
|
|
3213
|
-
var
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
const line = text.split("\n").find((l) => l.trim());
|
|
3255
|
-
return line ? line.replace(/\s+/g, " ").trim() : void 0;
|
|
3256
|
-
}
|
|
3257
|
-
function formatMatchHit(hit) {
|
|
3258
|
-
if (typeof hit === "string") return truncMid(hit, 70);
|
|
3259
|
-
if (hit && typeof hit === "object") {
|
|
3260
|
-
const o = hit;
|
|
3261
|
-
const file = stringOf(o["file"]) ?? stringOf(o["path"]);
|
|
3262
|
-
const line = numOf(o["line"]) ?? numOf(o["lineNumber"]);
|
|
3263
|
-
const snippet2 = stringOf(o["text"]) ?? stringOf(o["match"]) ?? stringOf(o["preview"]);
|
|
3264
|
-
if (file) {
|
|
3265
|
-
const head = line !== void 0 ? `${shortenPath(file, 40)}:${line}` : shortenPath(file, 50);
|
|
3266
|
-
return snippet2 ? `${head} ${truncMid(snippet2.replace(/\s+/g, " "), 40)}` : head;
|
|
3267
|
-
}
|
|
3268
|
-
if (snippet2) return truncMid(snippet2, 70);
|
|
2731
|
+
var MAX_STREAM_DISPLAY_CHARS = 480;
|
|
2732
|
+
var MAX_STREAM_LINES = 8;
|
|
2733
|
+
var ToolStreamBox = React5.memo(function ToolStreamBox2({
|
|
2734
|
+
name,
|
|
2735
|
+
text,
|
|
2736
|
+
startedAt,
|
|
2737
|
+
termWidth
|
|
2738
|
+
}) {
|
|
2739
|
+
const [tick, setTick] = useState(0);
|
|
2740
|
+
useEffect(() => {
|
|
2741
|
+
const t = setInterval(() => setTick((n) => n + 1), 500);
|
|
2742
|
+
return () => clearInterval(t);
|
|
2743
|
+
}, []);
|
|
2744
|
+
const elapsedMs = Date.now() - startedAt;
|
|
2745
|
+
const lines = text.split("\n");
|
|
2746
|
+
const totalLines = lines.length;
|
|
2747
|
+
const hidden = Math.max(0, totalLines - MAX_STREAM_LINES);
|
|
2748
|
+
const visible = hidden > 0 ? lines.slice(hidden) : lines;
|
|
2749
|
+
const contentWidth = Math.max(20, Math.min(termWidth - 4, 100));
|
|
2750
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 0, children: [
|
|
2751
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
|
|
2752
|
+
/* @__PURE__ */ jsx(Text, { color: theme.warn, children: "\u25C6 " }),
|
|
2753
|
+
/* @__PURE__ */ jsx(Text, { bold: true, color: theme.tool, children: name }),
|
|
2754
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \u23F1 ${fmtDuration(elapsedMs)}` }),
|
|
2755
|
+
hidden > 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` (${totalLines} lines, showing last ${MAX_STREAM_LINES})` }) : null
|
|
2756
|
+
] }),
|
|
2757
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [
|
|
2758
|
+
hidden > 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, italic: true, children: ` \u2026 ${hidden} more line${hidden === 1 ? "" : "s"} above` }) : null,
|
|
2759
|
+
visible.map((line, i) => {
|
|
2760
|
+
const trimmed = line.length > contentWidth ? `${line.slice(0, contentWidth - 1)}\u2026` : line;
|
|
2761
|
+
return /* @__PURE__ */ jsx(Text, { dimColor: true, children: trimmed || " " }, i);
|
|
2762
|
+
})
|
|
2763
|
+
] })
|
|
2764
|
+
] });
|
|
2765
|
+
});
|
|
2766
|
+
function tailForDisplay(text, maxChars) {
|
|
2767
|
+
if (text.length <= maxChars) return text;
|
|
2768
|
+
const cut = text.length - maxChars;
|
|
2769
|
+
const nl = text.indexOf("\n", cut);
|
|
2770
|
+
if (nl !== -1 && nl < cut + 80) {
|
|
2771
|
+
return `\u2026 ${text.slice(nl + 1)}`;
|
|
3269
2772
|
}
|
|
3270
|
-
return
|
|
2773
|
+
return `\u2026 ${text.slice(cut)}`;
|
|
3271
2774
|
}
|
|
2775
|
+
var MAX_CODE_LINES = 80;
|
|
3272
2776
|
var DIFF_MAX_LINES = 8;
|
|
3273
|
-
function
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
2777
|
+
function CodeBlock({
|
|
2778
|
+
code,
|
|
2779
|
+
lang,
|
|
2780
|
+
contentWidth
|
|
2781
|
+
}) {
|
|
2782
|
+
let lines = code.replace(/\n+$/, "").split("\n");
|
|
2783
|
+
const hidden = Math.max(0, lines.length - MAX_CODE_LINES);
|
|
2784
|
+
if (hidden > 0) lines = lines.slice(0, MAX_CODE_LINES);
|
|
2785
|
+
const gutterW = String(lines.length).length;
|
|
2786
|
+
const maxW = Math.max(20, Math.min(contentWidth - 6 - gutterW - 1, 120));
|
|
2787
|
+
let carry = {};
|
|
2788
|
+
const rows = lines.map((raw) => {
|
|
2789
|
+
const display = raw.length > maxW ? `${raw.slice(0, maxW - 1)}\u2026` : raw;
|
|
2790
|
+
const r = highlightLine(display, lang, carry);
|
|
2791
|
+
carry = r.carry;
|
|
2792
|
+
return r.tokens;
|
|
2793
|
+
});
|
|
2794
|
+
return /* @__PURE__ */ jsxs(
|
|
2795
|
+
Box,
|
|
2796
|
+
{
|
|
2797
|
+
flexDirection: "column",
|
|
2798
|
+
marginLeft: 2,
|
|
2799
|
+
marginY: 0,
|
|
2800
|
+
borderStyle: "round",
|
|
2801
|
+
borderColor: theme.borderDefault,
|
|
2802
|
+
paddingX: 1,
|
|
2803
|
+
children: [
|
|
2804
|
+
lang !== "plain" ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: lang }) : null,
|
|
2805
|
+
rows.map((tokens, i) => (
|
|
2806
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: code lines are positional
|
|
2807
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2808
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: `${String(i + 1).padStart(gutterW, " ")} ` }),
|
|
2809
|
+
tokens.length === 0 ? " " : tokens.map((t, j) => (
|
|
2810
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: token order is stable per line
|
|
2811
|
+
/* @__PURE__ */ jsx(Text, { color: t.color, dimColor: t.dim, bold: t.bold, children: t.text }, j)
|
|
2812
|
+
))
|
|
2813
|
+
] }, i)
|
|
2814
|
+
)),
|
|
2815
|
+
hidden > 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, italic: true, children: `\u2026 +${hidden} more line${hidden === 1 ? "" : "s"}` }) : null
|
|
2816
|
+
]
|
|
3282
2817
|
}
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
2818
|
+
);
|
|
2819
|
+
}
|
|
2820
|
+
function DiffBlock({ rows, hidden }) {
|
|
2821
|
+
let gutterWidth = 1;
|
|
2822
|
+
for (const r of rows) {
|
|
2823
|
+
const n = r.kind === "del" ? r.oldLine : r.newLine;
|
|
2824
|
+
if (typeof n === "number") {
|
|
2825
|
+
const w = String(n).length;
|
|
2826
|
+
if (w > gutterWidth) gutterWidth = w;
|
|
3289
2827
|
}
|
|
3290
2828
|
}
|
|
3291
|
-
|
|
3292
|
-
return
|
|
2829
|
+
const blank = " ".repeat(gutterWidth);
|
|
2830
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginLeft: 4, marginTop: 0, children: [
|
|
2831
|
+
rows.map((row, i) => {
|
|
2832
|
+
const key = i;
|
|
2833
|
+
if (row.kind === "hunk") {
|
|
2834
|
+
return /* @__PURE__ */ jsx(Text, { color: "cyan", dimColor: true, children: row.text }, key);
|
|
2835
|
+
}
|
|
2836
|
+
if (row.kind === "meta") {
|
|
2837
|
+
return /* @__PURE__ */ jsx(Text, { dimColor: true, children: `${blank} ${row.text}` }, key);
|
|
2838
|
+
}
|
|
2839
|
+
const lnNumber = row.kind === "del" ? row.oldLine : row.newLine;
|
|
2840
|
+
const lnText = typeof lnNumber === "number" ? String(lnNumber).padStart(gutterWidth, " ") : blank;
|
|
2841
|
+
if (row.kind === "ctx") {
|
|
2842
|
+
return /* @__PURE__ */ jsx(Text, { dimColor: true, children: `${lnText} ${row.text}` }, key);
|
|
2843
|
+
}
|
|
2844
|
+
const bg = row.kind === "add" ? theme.diffAddBg : theme.diffDelBg;
|
|
2845
|
+
return /* @__PURE__ */ jsxs(Text, { children: [
|
|
2846
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: `${lnText} ` }),
|
|
2847
|
+
/* @__PURE__ */ jsx(Text, { backgroundColor: bg, color: "black", children: row.text })
|
|
2848
|
+
] }, key);
|
|
2849
|
+
}),
|
|
2850
|
+
hidden > 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, italic: true, children: `${blank} \u2026 ${hidden} more line${hidden === 1 ? "" : "s"}` }) : null
|
|
2851
|
+
] });
|
|
3293
2852
|
}
|
|
3294
2853
|
function parseUnifiedDiff(diff, maxLines) {
|
|
3295
2854
|
const all = [];
|
|
@@ -3331,50 +2890,406 @@ function parseUnifiedDiff(diff, maxLines) {
|
|
|
3331
2890
|
if (all.length <= maxLines) return { rows: all, hidden: 0 };
|
|
3332
2891
|
return { rows: all.slice(0, maxLines), hidden: all.length - maxLines };
|
|
3333
2892
|
}
|
|
3334
|
-
function
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
}
|
|
3349
|
-
|
|
3350
|
-
let first;
|
|
3351
|
-
let last;
|
|
3352
|
-
let count = 0;
|
|
3353
|
-
for (const line of text.split("\n")) {
|
|
3354
|
-
const m = line.match(/^\s*(\d+)→/);
|
|
3355
|
-
if (m?.[1]) {
|
|
3356
|
-
const n = Number.parseInt(m[1], 10);
|
|
3357
|
-
if (Number.isFinite(n)) {
|
|
3358
|
-
if (first === void 0) first = n;
|
|
3359
|
-
last = n;
|
|
3360
|
-
count++;
|
|
3361
|
-
}
|
|
2893
|
+
function extractDiffPreview(toolName, output) {
|
|
2894
|
+
if (!output) return void 0;
|
|
2895
|
+
const text = output.trim();
|
|
2896
|
+
if (!text) return void 0;
|
|
2897
|
+
let diff;
|
|
2898
|
+
if (toolName === "edit" || toolName === "diff") {
|
|
2899
|
+
const parsed = tryParseJson(text);
|
|
2900
|
+
if (parsed && typeof parsed === "object") {
|
|
2901
|
+
diff = stringOf(parsed["diff"]);
|
|
2902
|
+
}
|
|
2903
|
+
} else if (toolName === "patch") {
|
|
2904
|
+
const parsed = tryParseJson(text);
|
|
2905
|
+
if (parsed && typeof parsed === "object") {
|
|
2906
|
+
diff = stringOf(parsed["diff"]) ?? stringOf(parsed["stdout"]);
|
|
2907
|
+
} else if (text.includes("@@") || text.startsWith("---")) {
|
|
2908
|
+
diff = text;
|
|
3362
2909
|
}
|
|
3363
2910
|
}
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
function countLines(text) {
|
|
3367
|
-
if (!text) return 0;
|
|
3368
|
-
return text.replace(/\n$/, "").split("\n").length;
|
|
3369
|
-
}
|
|
3370
|
-
function fmtBytes(n) {
|
|
3371
|
-
if (n < 1024) return `${n}B`;
|
|
3372
|
-
if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)}KB`;
|
|
3373
|
-
return `${(n / (1024 * 1024)).toFixed(1)}MB`;
|
|
2911
|
+
if (!diff || !diff.trim() || diff.startsWith("(no-op")) return void 0;
|
|
2912
|
+
return parseUnifiedDiff(diff, DIFF_MAX_LINES);
|
|
3374
2913
|
}
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
2914
|
+
var MESSAGE_PANEL_CHROME_WIDTH = 2;
|
|
2915
|
+
function splitFencedBlocks(text) {
|
|
2916
|
+
const lines = text.split("\n");
|
|
2917
|
+
const segs = [];
|
|
2918
|
+
let prose = [];
|
|
2919
|
+
let code = null;
|
|
2920
|
+
let lang = "plain";
|
|
2921
|
+
const flushProse = () => {
|
|
2922
|
+
if (prose.length > 0) {
|
|
2923
|
+
segs.push({ type: "prose", text: prose.join("\n") });
|
|
2924
|
+
prose = [];
|
|
2925
|
+
}
|
|
2926
|
+
};
|
|
2927
|
+
for (const line of lines) {
|
|
2928
|
+
const fence = line.match(/^\s*```(.*)$/);
|
|
2929
|
+
if (fence) {
|
|
2930
|
+
if (code === null) {
|
|
2931
|
+
flushProse();
|
|
2932
|
+
code = [];
|
|
2933
|
+
lang = detectLang(fence[1] ?? "");
|
|
2934
|
+
} else {
|
|
2935
|
+
segs.push({ type: "code", text: code.join("\n"), lang });
|
|
2936
|
+
code = null;
|
|
2937
|
+
lang = "plain";
|
|
2938
|
+
}
|
|
2939
|
+
continue;
|
|
2940
|
+
}
|
|
2941
|
+
if (code !== null) code.push(line);
|
|
2942
|
+
else prose.push(line);
|
|
2943
|
+
}
|
|
2944
|
+
if (code !== null) segs.push({ type: "code", text: code.join("\n"), lang });
|
|
2945
|
+
flushProse();
|
|
2946
|
+
return segs;
|
|
2947
|
+
}
|
|
2948
|
+
function AssistantBody({
|
|
2949
|
+
text,
|
|
2950
|
+
termWidth,
|
|
2951
|
+
contentWidth
|
|
2952
|
+
}) {
|
|
2953
|
+
const segments = splitFencedBlocks(text);
|
|
2954
|
+
const inner = contentWidth ?? termWidth;
|
|
2955
|
+
return /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: segments.map(
|
|
2956
|
+
(seg, i) => seg.type === "code" ? (
|
|
2957
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: segment order is stable
|
|
2958
|
+
/* @__PURE__ */ jsx(CodeBlock, { code: seg.text, lang: seg.lang ?? "plain", contentWidth: inner }, i)
|
|
2959
|
+
) : (
|
|
2960
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: segment order is stable
|
|
2961
|
+
/* @__PURE__ */ jsx(MarkdownView, { text: seg.text, termWidth: inner }, i)
|
|
2962
|
+
)
|
|
2963
|
+
) });
|
|
2964
|
+
}
|
|
2965
|
+
function AssistantTail({ text }) {
|
|
2966
|
+
return /* @__PURE__ */ jsxs(
|
|
2967
|
+
Box,
|
|
2968
|
+
{
|
|
2969
|
+
flexDirection: "column",
|
|
2970
|
+
marginY: 1,
|
|
2971
|
+
borderStyle: "single",
|
|
2972
|
+
borderTop: false,
|
|
2973
|
+
borderRight: false,
|
|
2974
|
+
borderBottom: false,
|
|
2975
|
+
borderColor: theme.assistant,
|
|
2976
|
+
paddingLeft: 1,
|
|
2977
|
+
children: [
|
|
2978
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
|
|
2979
|
+
/* @__PURE__ */ jsx(Text, { bold: true, color: theme.assistant, children: "ASSISTANT" }),
|
|
2980
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " (streaming\u2026)" })
|
|
2981
|
+
] }),
|
|
2982
|
+
/* @__PURE__ */ jsx(Text, { color: "white", children: text })
|
|
2983
|
+
]
|
|
2984
|
+
}
|
|
2985
|
+
);
|
|
2986
|
+
}
|
|
2987
|
+
function Banner({
|
|
2988
|
+
entry
|
|
2989
|
+
}) {
|
|
2990
|
+
const cwdShort = shortenPath(entry.cwd, 48);
|
|
2991
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", paddingX: 2, paddingY: 0, children: [
|
|
2992
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
2993
|
+
/* @__PURE__ */ jsx(Text, { color: "magenta", bold: true, children: " \u259F\u259B " }),
|
|
2994
|
+
/* @__PURE__ */ jsx(Text, { color: "magenta", bold: true, children: "WrongStack" }),
|
|
2995
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " v" }),
|
|
2996
|
+
/* @__PURE__ */ jsx(Text, { children: entry.version })
|
|
2997
|
+
] }),
|
|
2998
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, italic: true, children: " Built on the wrong stack. Shipped anyway." }),
|
|
2999
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
3000
|
+
/* @__PURE__ */ jsx(Text, { color: "cyan", children: " provider " }),
|
|
3001
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
3002
|
+
entry.provider,
|
|
3003
|
+
"/",
|
|
3004
|
+
entry.model
|
|
3005
|
+
] })
|
|
3006
|
+
] }),
|
|
3007
|
+
entry.family ? /* @__PURE__ */ jsxs(Text, { children: [
|
|
3008
|
+
/* @__PURE__ */ jsx(Text, { color: "cyan", children: " family " }),
|
|
3009
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: entry.family })
|
|
3010
|
+
] }) : null,
|
|
3011
|
+
entry.keyTail ? /* @__PURE__ */ jsxs(Text, { children: [
|
|
3012
|
+
/* @__PURE__ */ jsx(Text, { color: "cyan", children: " key " }),
|
|
3013
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u25CF\u25CF\u25CF\u2026" }),
|
|
3014
|
+
/* @__PURE__ */ jsx(Text, { children: entry.keyTail })
|
|
3015
|
+
] }) : null,
|
|
3016
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
3017
|
+
/* @__PURE__ */ jsx(Text, { color: "cyan", children: " cwd " }),
|
|
3018
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: cwdShort })
|
|
3019
|
+
] }),
|
|
3020
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
3021
|
+
/* @__PURE__ */ jsx(Text, { color: "cyan", children: " hints " }),
|
|
3022
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "/help \xB7 /init \xB7 /memory \xB7 /queue \xB7 /exit" })
|
|
3023
|
+
] })
|
|
3024
|
+
] });
|
|
3025
|
+
}
|
|
3026
|
+
function brainStatusStyle(status) {
|
|
3027
|
+
switch (status) {
|
|
3028
|
+
case "thinking":
|
|
3029
|
+
return { icon: "\u2026", color: "magenta" };
|
|
3030
|
+
case "answered":
|
|
3031
|
+
return { icon: "\u2696", color: "cyan" };
|
|
3032
|
+
case "ask_human":
|
|
3033
|
+
return { icon: "?", color: "yellow" };
|
|
3034
|
+
case "denied":
|
|
3035
|
+
return { icon: "\xD7", color: "red" };
|
|
3036
|
+
}
|
|
3037
|
+
}
|
|
3038
|
+
function brainRiskColor(risk) {
|
|
3039
|
+
switch (risk) {
|
|
3040
|
+
case "low":
|
|
3041
|
+
return "green";
|
|
3042
|
+
case "medium":
|
|
3043
|
+
return "cyan";
|
|
3044
|
+
case "high":
|
|
3045
|
+
return "yellow";
|
|
3046
|
+
case "critical":
|
|
3047
|
+
return "red";
|
|
3048
|
+
}
|
|
3049
|
+
}
|
|
3050
|
+
function assistantContentWidth(termWidth) {
|
|
3051
|
+
return Math.max(20, termWidth - MESSAGE_PANEL_CHROME_WIDTH);
|
|
3052
|
+
}
|
|
3053
|
+
var Entry = React5.memo(function Entry2({
|
|
3054
|
+
entry,
|
|
3055
|
+
termWidth
|
|
3056
|
+
}) {
|
|
3057
|
+
switch (entry.kind) {
|
|
3058
|
+
case "user":
|
|
3059
|
+
return /* @__PURE__ */ jsx(
|
|
3060
|
+
Box,
|
|
3061
|
+
{
|
|
3062
|
+
borderStyle: "single",
|
|
3063
|
+
borderTop: false,
|
|
3064
|
+
borderRight: false,
|
|
3065
|
+
borderBottom: false,
|
|
3066
|
+
borderColor: theme.user,
|
|
3067
|
+
paddingLeft: 1,
|
|
3068
|
+
children: /* @__PURE__ */ jsxs(Text, { children: [
|
|
3069
|
+
/* @__PURE__ */ jsx(Text, { bold: true, color: theme.user, children: "USER " }),
|
|
3070
|
+
/* @__PURE__ */ jsx(Text, { color: "white", children: entry.text }),
|
|
3071
|
+
entry.queued ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: " (queued)" }) : null,
|
|
3072
|
+
entry.pasteContent ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3073
|
+
entry.text ? "\n" : null,
|
|
3074
|
+
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
3075
|
+
" \u21B3 ",
|
|
3076
|
+
entry.pasteContent
|
|
3077
|
+
] })
|
|
3078
|
+
] }) : null
|
|
3079
|
+
] })
|
|
3080
|
+
}
|
|
3081
|
+
);
|
|
3082
|
+
case "assistant": {
|
|
3083
|
+
const contentWidth = assistantContentWidth(termWidth);
|
|
3084
|
+
return /* @__PURE__ */ jsxs(
|
|
3085
|
+
Box,
|
|
3086
|
+
{
|
|
3087
|
+
flexDirection: "column",
|
|
3088
|
+
marginY: 1,
|
|
3089
|
+
borderStyle: "single",
|
|
3090
|
+
borderTop: false,
|
|
3091
|
+
borderRight: false,
|
|
3092
|
+
borderBottom: false,
|
|
3093
|
+
borderColor: theme.assistant,
|
|
3094
|
+
paddingLeft: 1,
|
|
3095
|
+
children: [
|
|
3096
|
+
/* @__PURE__ */ jsx(Box, { flexDirection: "row", children: /* @__PURE__ */ jsx(Text, { bold: true, color: theme.assistant, children: "ASSISTANT" }) }),
|
|
3097
|
+
/* @__PURE__ */ jsx(AssistantBody, { text: entry.text, termWidth, contentWidth })
|
|
3098
|
+
]
|
|
3099
|
+
}
|
|
3100
|
+
);
|
|
3101
|
+
}
|
|
3102
|
+
case "tool": {
|
|
3103
|
+
const argSummary = formatToolArgs(entry.name, entry.input);
|
|
3104
|
+
const outLines = formatToolOutput(
|
|
3105
|
+
entry.name,
|
|
3106
|
+
entry.output,
|
|
3107
|
+
entry.ok,
|
|
3108
|
+
entry.outputBytes,
|
|
3109
|
+
entry.outputLines
|
|
3110
|
+
);
|
|
3111
|
+
const diff = entry.ok ? extractDiffPreview(entry.name, entry.output) : void 0;
|
|
3112
|
+
const sizeChip = (() => {
|
|
3113
|
+
if (!entry.ok) return "";
|
|
3114
|
+
const parts = [];
|
|
3115
|
+
if (entry.outputLines !== void 0 && entry.outputLines > 0) {
|
|
3116
|
+
parts.push(`${entry.outputLines} L`);
|
|
3117
|
+
}
|
|
3118
|
+
if (entry.outputBytes && entry.outputBytes > 0) {
|
|
3119
|
+
parts.push(fmtBytes(entry.outputBytes));
|
|
3120
|
+
}
|
|
3121
|
+
if (entry.outputTokens && entry.outputTokens > 0) {
|
|
3122
|
+
parts.push(`\u2248${fmtTok2(entry.outputTokens)} tok`);
|
|
3123
|
+
}
|
|
3124
|
+
return parts.join(" \xB7 ");
|
|
3125
|
+
})();
|
|
3126
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
3127
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
3128
|
+
/* @__PURE__ */ jsx(Text, { color: entry.ok ? theme.success : theme.error, children: entry.ok ? "\u25CF" : "\u2717" }),
|
|
3129
|
+
" ",
|
|
3130
|
+
/* @__PURE__ */ jsx(Text, { bold: true, color: theme.tool, children: entry.name }),
|
|
3131
|
+
argSummary ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3132
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
3133
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: argSummary })
|
|
3134
|
+
] }) : null,
|
|
3135
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \xB7 ${fmtDuration(entry.durationMs)}` }),
|
|
3136
|
+
sizeChip ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \xB7 ${sizeChip}` }) : null
|
|
3137
|
+
] }),
|
|
3138
|
+
outLines.map((line, i) => (
|
|
3139
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: tool output lines are static, index is stable
|
|
3140
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
3141
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: i === outLines.length - 1 && !diff ? " \u2514\u2500 " : " \u251C\u2500 " }),
|
|
3142
|
+
/* @__PURE__ */ jsx(
|
|
3143
|
+
Text,
|
|
3144
|
+
{
|
|
3145
|
+
color: !entry.ok || line.startsWith("!") ? "red" : void 0,
|
|
3146
|
+
dimColor: entry.ok && !line.startsWith("!"),
|
|
3147
|
+
children: line
|
|
3148
|
+
}
|
|
3149
|
+
)
|
|
3150
|
+
] }, i)
|
|
3151
|
+
)),
|
|
3152
|
+
diff ? /* @__PURE__ */ jsx(DiffBlock, { rows: diff.rows, hidden: diff.hidden }) : null
|
|
3153
|
+
] });
|
|
3154
|
+
}
|
|
3155
|
+
case "info":
|
|
3156
|
+
return /* @__PURE__ */ jsx(Text, { dimColor: true, children: entry.text });
|
|
3157
|
+
case "warn":
|
|
3158
|
+
return /* @__PURE__ */ jsx(
|
|
3159
|
+
Box,
|
|
3160
|
+
{
|
|
3161
|
+
borderStyle: "single",
|
|
3162
|
+
borderTop: false,
|
|
3163
|
+
borderRight: false,
|
|
3164
|
+
borderBottom: false,
|
|
3165
|
+
borderColor: theme.warn,
|
|
3166
|
+
paddingLeft: 1,
|
|
3167
|
+
children: /* @__PURE__ */ jsx(Text, { color: theme.warn, children: entry.text })
|
|
3168
|
+
}
|
|
3169
|
+
);
|
|
3170
|
+
case "error":
|
|
3171
|
+
return /* @__PURE__ */ jsx(
|
|
3172
|
+
Box,
|
|
3173
|
+
{
|
|
3174
|
+
borderStyle: "single",
|
|
3175
|
+
borderTop: false,
|
|
3176
|
+
borderRight: false,
|
|
3177
|
+
borderBottom: false,
|
|
3178
|
+
borderColor: theme.error,
|
|
3179
|
+
paddingLeft: 1,
|
|
3180
|
+
children: /* @__PURE__ */ jsx(Text, { color: theme.error, children: entry.text })
|
|
3181
|
+
}
|
|
3182
|
+
);
|
|
3183
|
+
case "turn-summary":
|
|
3184
|
+
return /* @__PURE__ */ jsx(Text, { dimColor: true, children: entry.text });
|
|
3185
|
+
case "brain": {
|
|
3186
|
+
const statusStyle = brainStatusStyle(entry.status);
|
|
3187
|
+
const riskColor2 = brainRiskColor(entry.risk);
|
|
3188
|
+
return /* @__PURE__ */ jsxs(
|
|
3189
|
+
Box,
|
|
3190
|
+
{
|
|
3191
|
+
flexDirection: "column",
|
|
3192
|
+
marginY: 1,
|
|
3193
|
+
borderStyle: "single",
|
|
3194
|
+
borderTop: false,
|
|
3195
|
+
borderRight: false,
|
|
3196
|
+
borderBottom: false,
|
|
3197
|
+
borderColor: "magenta",
|
|
3198
|
+
paddingLeft: 1,
|
|
3199
|
+
children: [
|
|
3200
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
|
|
3201
|
+
/* @__PURE__ */ jsx(Text, { bold: true, color: "magenta", children: "BRAIN" }),
|
|
3202
|
+
/* @__PURE__ */ jsx(Text, { color: statusStyle.color, children: statusStyle.icon }),
|
|
3203
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: entry.source }),
|
|
3204
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
|
|
3205
|
+
/* @__PURE__ */ jsx(Text, { color: riskColor2, children: entry.risk })
|
|
3206
|
+
] }),
|
|
3207
|
+
/* @__PURE__ */ jsx(Text, { color: "white", children: entry.question }),
|
|
3208
|
+
entry.decision ? /* @__PURE__ */ jsxs(Text, { children: [
|
|
3209
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "Decision: " }),
|
|
3210
|
+
/* @__PURE__ */ jsx(Text, { color: statusStyle.color, children: entry.decision })
|
|
3211
|
+
] }) : null,
|
|
3212
|
+
entry.rationale ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: entry.rationale }) : null
|
|
3213
|
+
]
|
|
3214
|
+
}
|
|
3215
|
+
);
|
|
3216
|
+
}
|
|
3217
|
+
case "confirm":
|
|
3218
|
+
return /* @__PURE__ */ jsxs(
|
|
3219
|
+
Box,
|
|
3220
|
+
{
|
|
3221
|
+
flexDirection: "column",
|
|
3222
|
+
borderStyle: "round",
|
|
3223
|
+
borderColor: "yellow",
|
|
3224
|
+
paddingX: 1,
|
|
3225
|
+
marginY: 1,
|
|
3226
|
+
children: [
|
|
3227
|
+
/* @__PURE__ */ jsxs(Text, { bold: true, color: "yellow", children: [
|
|
3228
|
+
"\u26A0 Confirm: ",
|
|
3229
|
+
entry.toolName
|
|
3230
|
+
] }),
|
|
3231
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "Waiting for y / n / a / d..." })
|
|
3232
|
+
]
|
|
3233
|
+
}
|
|
3234
|
+
);
|
|
3235
|
+
case "banner":
|
|
3236
|
+
return /* @__PURE__ */ jsx(Banner, { entry });
|
|
3237
|
+
case "subagent": {
|
|
3238
|
+
const lines = entry.text.split("\n");
|
|
3239
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
3240
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
3241
|
+
/* @__PURE__ */ jsx(Text, { color: entry.agentColor, bold: true, children: `[${entry.agentLabel}]` }),
|
|
3242
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
3243
|
+
/* @__PURE__ */ jsx(Text, { color: entry.agentColor, children: entry.icon }),
|
|
3244
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
3245
|
+
/* @__PURE__ */ jsx(Text, { children: lines[0] ?? "" }),
|
|
3246
|
+
entry.detail ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3247
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " \xB7 " }),
|
|
3248
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: entry.detail })
|
|
3249
|
+
] }) : null
|
|
3250
|
+
] }),
|
|
3251
|
+
lines.slice(1).map((line, i) => (
|
|
3252
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: stable line index
|
|
3253
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
3254
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " " }),
|
|
3255
|
+
/* @__PURE__ */ jsx(Text, { children: line })
|
|
3256
|
+
] }, i)
|
|
3257
|
+
))
|
|
3258
|
+
] });
|
|
3259
|
+
}
|
|
3260
|
+
}
|
|
3261
|
+
});
|
|
3262
|
+
function History({ entries, streamingText, toolStream }) {
|
|
3263
|
+
const { stdout } = useStdout();
|
|
3264
|
+
const [termSize, setTermSize] = useState({
|
|
3265
|
+
columns: stdout?.columns ?? 80,
|
|
3266
|
+
rows: stdout?.rows ?? 24
|
|
3267
|
+
});
|
|
3268
|
+
useEffect(() => {
|
|
3269
|
+
const handleResize = () => {
|
|
3270
|
+
setTermSize({ columns: stdout?.columns ?? 80, rows: stdout?.rows ?? 24 });
|
|
3271
|
+
};
|
|
3272
|
+
process.stdout.on("resize", handleResize);
|
|
3273
|
+
return () => {
|
|
3274
|
+
process.stdout.off("resize", handleResize);
|
|
3275
|
+
};
|
|
3276
|
+
}, [stdout]);
|
|
3277
|
+
const termWidth = termSize.columns;
|
|
3278
|
+
const tail = streamingText ? tailForDisplay(streamingText, MAX_STREAM_DISPLAY_CHARS) : "";
|
|
3279
|
+
const toolTail = toolStream?.text ? tailForDisplay(toolStream.text, MAX_STREAM_DISPLAY_CHARS) : "";
|
|
3280
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3281
|
+
/* @__PURE__ */ jsx(Static, { items: entries, children: (entry) => /* @__PURE__ */ jsx(Box, { marginBottom: entry.kind === "turn-summary" ? 1 : 0, children: /* @__PURE__ */ jsx(Entry, { entry, termWidth }) }, entry.id) }),
|
|
3282
|
+
tail ? /* @__PURE__ */ jsx(AssistantTail, { text: tail }) : null,
|
|
3283
|
+
toolTail ? /* @__PURE__ */ jsx(
|
|
3284
|
+
ToolStreamBox,
|
|
3285
|
+
{
|
|
3286
|
+
name: toolStream.name,
|
|
3287
|
+
text: toolTail,
|
|
3288
|
+
startedAt: toolStream.startedAt,
|
|
3289
|
+
termWidth
|
|
3290
|
+
}
|
|
3291
|
+
) : null
|
|
3292
|
+
] });
|
|
3378
3293
|
}
|
|
3379
3294
|
|
|
3380
3295
|
// src/fn-keys.ts
|
|
@@ -3479,52 +3394,6 @@ function layoutInputRows(prompt, value, cursor, width) {
|
|
|
3479
3394
|
if (row.length > 0 || rows.length === 0) rows.push(row);
|
|
3480
3395
|
return rows;
|
|
3481
3396
|
}
|
|
3482
|
-
function inputIndexAtRowCol(prompt, value, width, row, col) {
|
|
3483
|
-
const w = Math.max(1, Math.floor(width));
|
|
3484
|
-
const flat = [];
|
|
3485
|
-
for (let i = 0; i < prompt.length; i++) flat.push({ ch: prompt[i], buf: -1 });
|
|
3486
|
-
for (let i = 0; i < value.length; i++) flat.push({ ch: value[i], buf: i });
|
|
3487
|
-
const rows = [];
|
|
3488
|
-
const starts = [];
|
|
3489
|
-
let cur = [];
|
|
3490
|
-
let curStart = 0;
|
|
3491
|
-
for (const cell of flat) {
|
|
3492
|
-
if (cell.ch === "\n") {
|
|
3493
|
-
rows.push(cur);
|
|
3494
|
-
starts.push(curStart);
|
|
3495
|
-
cur = [];
|
|
3496
|
-
curStart = cell.buf + 1;
|
|
3497
|
-
continue;
|
|
3498
|
-
}
|
|
3499
|
-
if (cur.length === 0 && cell.buf >= 0) curStart = cell.buf;
|
|
3500
|
-
cur.push({ buf: cell.buf });
|
|
3501
|
-
if (cur.length >= w) {
|
|
3502
|
-
rows.push(cur);
|
|
3503
|
-
starts.push(curStart);
|
|
3504
|
-
cur = [];
|
|
3505
|
-
curStart = -1;
|
|
3506
|
-
}
|
|
3507
|
-
}
|
|
3508
|
-
if (cur.length > 0 || rows.length === 0) {
|
|
3509
|
-
rows.push(cur);
|
|
3510
|
-
starts.push(curStart);
|
|
3511
|
-
}
|
|
3512
|
-
const clamp = (n) => Math.max(0, Math.min(value.length, n));
|
|
3513
|
-
if (row < 0) return 0;
|
|
3514
|
-
if (row >= rows.length) return value.length;
|
|
3515
|
-
const r = rows[row];
|
|
3516
|
-
const c = Math.max(0, col);
|
|
3517
|
-
if (c < r.length) {
|
|
3518
|
-
const b = r[c].buf;
|
|
3519
|
-
return b < 0 ? 0 : clamp(b);
|
|
3520
|
-
}
|
|
3521
|
-
for (let k = r.length - 1; k >= 0; k--) {
|
|
3522
|
-
const b = r[k].buf;
|
|
3523
|
-
if (b >= 0) return clamp(b + 1);
|
|
3524
|
-
}
|
|
3525
|
-
const start = starts[row];
|
|
3526
|
-
return clamp(start !== void 0 && start >= 0 ? start : value.length);
|
|
3527
|
-
}
|
|
3528
3397
|
function renderRow2(cells, rowKey, promptColor) {
|
|
3529
3398
|
const out = [];
|
|
3530
3399
|
let run = "";
|
|
@@ -3664,8 +3533,7 @@ function hintsFor(ctx) {
|
|
|
3664
3533
|
return [
|
|
3665
3534
|
{ key: "\u2191\u2193", label: "move" },
|
|
3666
3535
|
{ key: "\u21B5", label: "select" },
|
|
3667
|
-
{ key: "Esc", label: "cancel" }
|
|
3668
|
-
...ctx.mouse ? [{ key: "click", label: "pick" }] : []
|
|
3536
|
+
{ key: "Esc", label: "cancel" }
|
|
3669
3537
|
];
|
|
3670
3538
|
}
|
|
3671
3539
|
if (ctx.monitor) {
|
|
@@ -3678,7 +3546,6 @@ function hintsFor(ctx) {
|
|
|
3678
3546
|
}
|
|
3679
3547
|
const base = [{ key: "?", label: "help" }];
|
|
3680
3548
|
if (ctx.managed) base.push({ key: "PgUp/PgDn", label: "scroll" });
|
|
3681
|
-
if (ctx.mouse) base.push({ key: "wheel", label: "scroll" }, { key: "click", label: "select" });
|
|
3682
3549
|
base.push({ key: "^G", label: "agents" }, { key: "^C", label: "stop" });
|
|
3683
3550
|
return base;
|
|
3684
3551
|
}
|
|
@@ -3718,7 +3585,7 @@ function fmtRecentMessage(message) {
|
|
|
3718
3585
|
const text = message.text.replace(/\s+/g, " ");
|
|
3719
3586
|
return text.length > 48 ? `${text.slice(0, 47)}...` : text;
|
|
3720
3587
|
}
|
|
3721
|
-
var LiveActivityStrip =
|
|
3588
|
+
var LiveActivityStrip = React5.memo(function LiveActivityStrip2({
|
|
3722
3589
|
entries,
|
|
3723
3590
|
nowTick,
|
|
3724
3591
|
maxRows = 4
|
|
@@ -4044,13 +3911,6 @@ function scrollbarThumb(rows, offset, total) {
|
|
|
4044
3911
|
const top = Math.max(0, Math.min(rawTop, rows - size));
|
|
4045
3912
|
return { top, size, scrollable: true };
|
|
4046
3913
|
}
|
|
4047
|
-
function scrollOffsetForTrackRow(rows, total, cell) {
|
|
4048
|
-
if (total <= rows) return 0;
|
|
4049
|
-
const maxOffset = total - rows;
|
|
4050
|
-
const clampedCell = Math.max(0, Math.min(rows - 1, cell));
|
|
4051
|
-
const windowTop = Math.round(clampedCell / Math.max(1, rows - 1) * maxOffset);
|
|
4052
|
-
return Math.max(0, Math.min(maxOffset, maxOffset - windowTop));
|
|
4053
|
-
}
|
|
4054
3914
|
function Scrollbar({
|
|
4055
3915
|
rows,
|
|
4056
3916
|
offset,
|
|
@@ -4535,6 +4395,132 @@ function runGit(cwd, args) {
|
|
|
4535
4395
|
}
|
|
4536
4396
|
});
|
|
4537
4397
|
}
|
|
4398
|
+
var STREAM_COLORS = ["cyan", "magenta", "yellow", "green", "blue"];
|
|
4399
|
+
function labelFor(labelsRef, id, name) {
|
|
4400
|
+
const m = labelsRef.current;
|
|
4401
|
+
const existing = m.get(id);
|
|
4402
|
+
if (existing) return existing;
|
|
4403
|
+
const n = m.size + 1;
|
|
4404
|
+
const v = {
|
|
4405
|
+
label: name && name !== id ? name : `AGENT#${n}`,
|
|
4406
|
+
color: STREAM_COLORS[(n - 1) % STREAM_COLORS.length]
|
|
4407
|
+
};
|
|
4408
|
+
m.set(id, v);
|
|
4409
|
+
return v;
|
|
4410
|
+
}
|
|
4411
|
+
function useSubagentEvents(events, dispatch, setActiveMaxContext) {
|
|
4412
|
+
const labelsRef = useRef(/* @__PURE__ */ new Map());
|
|
4413
|
+
const lbl = useCallback(
|
|
4414
|
+
(id, name) => labelFor(labelsRef, id, name),
|
|
4415
|
+
[]
|
|
4416
|
+
// labelsRef is a stable ref
|
|
4417
|
+
);
|
|
4418
|
+
useEffect(() => {
|
|
4419
|
+
const offSpawned = events.on("subagent.spawned", (e) => {
|
|
4420
|
+
const l = lbl(e.subagentId, e.name);
|
|
4421
|
+
dispatch({ type: "fleetSpawn", id: e.subagentId, name: e.name, provider: e.provider, model: e.model, transcriptPath: e.transcriptPath });
|
|
4422
|
+
const where = e.provider && e.model ? `${e.provider}/${e.model}` : "spawned";
|
|
4423
|
+
const desc = e.description ? ` \u2014 ${e.description.slice(0, 80)}` : "";
|
|
4424
|
+
dispatch({ type: "addEntry", entry: { kind: "subagent", agentLabel: l.label, agentColor: l.color, icon: "\u25B6", text: `${where}${desc}` } });
|
|
4425
|
+
});
|
|
4426
|
+
const offStarted = events.on("subagent.task_started", (e) => {
|
|
4427
|
+
const l = lbl(e.subagentId);
|
|
4428
|
+
dispatch({ type: "fleetStart", id: e.subagentId, taskId: e.taskId });
|
|
4429
|
+
const desc = e.description ? ` \u2014 ${e.description.slice(0, 80)}` : "";
|
|
4430
|
+
dispatch({ type: "addEntry", entry: { kind: "subagent", agentLabel: l.label, agentColor: l.color, icon: "\u25CF", text: `task started${desc}` } });
|
|
4431
|
+
});
|
|
4432
|
+
const offCompleted = events.on("subagent.task_completed", (e) => {
|
|
4433
|
+
const l = lbl(e.subagentId);
|
|
4434
|
+
const errKind = e.error?.kind;
|
|
4435
|
+
dispatch({ type: "fleetDone", id: e.subagentId, status: e.status, iterations: e.iterations, toolCalls: e.toolCalls, failureReason: errKind });
|
|
4436
|
+
const icon = e.status === "success" ? "\u2713" : e.status === "timeout" ? "\u23F1" : e.status === "stopped" ? "\u2298" : "\u2717";
|
|
4437
|
+
const errMsg = e.error?.message;
|
|
4438
|
+
const errMsgTail = errMsg ? ` \u2014 ${errMsg.replace(/\s+/g, " ").slice(0, 100)}${errMsg.length > 100 ? "\u2026" : ""}` : "";
|
|
4439
|
+
const errChip = errKind ? ` [${errKind}]` : "";
|
|
4440
|
+
const secs = (e.durationMs / 1e3).toFixed(e.durationMs < 1e4 ? 1 : 0);
|
|
4441
|
+
dispatch({ type: "addEntry", entry: { kind: "subagent", agentLabel: l.label, agentColor: l.color, icon, text: `${e.status} (${e.iterations} iter \xB7 ${e.toolCalls} tools \xB7 ${secs}s)${errChip}${errMsgTail}` } });
|
|
4442
|
+
});
|
|
4443
|
+
const offBudgetWarning = events.on("subagent.budget_warning", (e) => {
|
|
4444
|
+
const l = lbl(e.subagentId);
|
|
4445
|
+
dispatch({ type: "fleetBudgetWarning", id: e.subagentId, kind: e.kind, used: e.used, limit: e.limit });
|
|
4446
|
+
const timeoutSuffix = e.kind === "timeout" ? " (subagent continues running)" : " \u2014 extending";
|
|
4447
|
+
dispatch({ type: "addEntry", entry: { kind: "subagent", agentLabel: l.label, agentColor: l.color, icon: "\u26A1", text: `hitting ${e.kind} limit (${e.used}/${e.limit})${timeoutSuffix}` } });
|
|
4448
|
+
});
|
|
4449
|
+
const offBudgetExtended = events.on("subagent.budget_extended", (e) => {
|
|
4450
|
+
const l = lbl(e.subagentId);
|
|
4451
|
+
dispatch({ type: "fleetBudgetExtended", id: e.subagentId, totalExtensions: e.totalExtensions });
|
|
4452
|
+
dispatch({ type: "addEntry", entry: { kind: "subagent", agentLabel: l.label, agentColor: l.color, icon: "\u26A1", text: `extended ${e.kind} \u2192 ${e.newLimit} (\xD7${e.totalExtensions})` } });
|
|
4453
|
+
});
|
|
4454
|
+
const offIterationSummary = events.on("subagent.iteration_summary", (e) => {
|
|
4455
|
+
const l = lbl(e.subagentId);
|
|
4456
|
+
const costStr = e.costUsd > 0 ? ` \xB7 ${e.costUsd.toFixed(3)}` : "";
|
|
4457
|
+
const toolStr = e.currentTool ? ` \xB7 doing ${e.currentTool}` : "";
|
|
4458
|
+
const partial = e.partialText ? ` \xB7 "${e.partialText.slice(0, 60)}${e.partialText.length > 60 ? "\u2026" : ""}"` : "";
|
|
4459
|
+
dispatch({ type: "addEntry", entry: { kind: "subagent", agentLabel: l.label, agentColor: l.color, icon: "\u{1F4AC}", text: `L${e.iteration} \xB7 ${e.toolCalls} tools${costStr}${toolStr}${partial}` } });
|
|
4460
|
+
});
|
|
4461
|
+
const offCtxPct = events.on("subagent.ctx_pct", (e) => {
|
|
4462
|
+
dispatch({ type: "fleetCtxPct", id: e.subagentId, load: e.load, tokens: e.tokens, maxContext: e.maxContext });
|
|
4463
|
+
});
|
|
4464
|
+
const offConcurrencyChanged = events.on("concurrency.changed", (e) => {
|
|
4465
|
+
const { n } = e;
|
|
4466
|
+
if (typeof n === "number" && n > 0) {
|
|
4467
|
+
dispatch({ type: "fleetConcurrency", n });
|
|
4468
|
+
}
|
|
4469
|
+
});
|
|
4470
|
+
const offLeaderCtxPct = events.on("ctx.pct", (e) => {
|
|
4471
|
+
setActiveMaxContext(e.maxContext);
|
|
4472
|
+
dispatch({ type: "leaderCtxPct", load: e.load, tokens: e.tokens, maxContext: e.maxContext });
|
|
4473
|
+
});
|
|
4474
|
+
const offLeaderMaxContext = events.on("ctx.max_context", (e) => {
|
|
4475
|
+
if (e.maxContext > 0) setActiveMaxContext(e.maxContext);
|
|
4476
|
+
});
|
|
4477
|
+
const offTool = events.on("subagent.tool_executed", (e) => {
|
|
4478
|
+
dispatch({ type: "fleetTool", id: e.subagentId, name: e.name, ok: e.ok, durationMs: e.durationMs, outputBytes: e.outputBytes });
|
|
4479
|
+
dispatch({ type: "fleetToolEnd", id: e.subagentId });
|
|
4480
|
+
});
|
|
4481
|
+
return () => {
|
|
4482
|
+
offSpawned();
|
|
4483
|
+
offStarted();
|
|
4484
|
+
offCompleted();
|
|
4485
|
+
offBudgetWarning();
|
|
4486
|
+
offBudgetExtended();
|
|
4487
|
+
offIterationSummary();
|
|
4488
|
+
offCtxPct();
|
|
4489
|
+
offConcurrencyChanged();
|
|
4490
|
+
offLeaderCtxPct();
|
|
4491
|
+
offLeaderMaxContext();
|
|
4492
|
+
offTool();
|
|
4493
|
+
};
|
|
4494
|
+
}, [events, dispatch, setActiveMaxContext, lbl]);
|
|
4495
|
+
}
|
|
4496
|
+
function useBrainEvents(events, dispatch) {
|
|
4497
|
+
useEffect(() => {
|
|
4498
|
+
const requestSummary = (request) => `${request.source}: ${request.question}`.slice(0, 80);
|
|
4499
|
+
const addBrainEntry = (status, payload) => {
|
|
4500
|
+
const p = payload;
|
|
4501
|
+
const decision = p.decision.optionId ?? p.decision.text ?? p.decision.reason ?? p.decision.prompt ?? p.decision.type;
|
|
4502
|
+
dispatch({ type: "brainStatus", state: status, source: p.request.source, risk: p.request.risk, summary: decision });
|
|
4503
|
+
if (status === "ask_human") {
|
|
4504
|
+
dispatch({ type: "brainPromptSet", prompt: { requestId: p.request.id, source: p.request.source, risk: p.request.risk, question: p.request.question, context: p.request.context, options: p.request.options } });
|
|
4505
|
+
} else {
|
|
4506
|
+
dispatch({ type: "brainPromptClear" });
|
|
4507
|
+
}
|
|
4508
|
+
dispatch({ type: "addEntry", entry: { kind: "brain", status, source: p.request.source, risk: p.request.risk, question: p.request.question, decision, rationale: p.decision.rationale } });
|
|
4509
|
+
};
|
|
4510
|
+
const offRequested = events.on("brain.decision_requested", ({ request }) => {
|
|
4511
|
+
dispatch({ type: "brainStatus", state: "deciding", source: request.source, risk: request.risk, summary: requestSummary(request) });
|
|
4512
|
+
});
|
|
4513
|
+
const offAnswered = events.on("brain.decision_answered", (payload) => addBrainEntry("answered", payload));
|
|
4514
|
+
const offAskHuman = events.on("brain.decision_ask_human", (payload) => addBrainEntry("ask_human", payload));
|
|
4515
|
+
const offDenied = events.on("brain.decision_denied", (payload) => addBrainEntry("denied", payload));
|
|
4516
|
+
return () => {
|
|
4517
|
+
offRequested();
|
|
4518
|
+
offAnswered();
|
|
4519
|
+
offAskHuman();
|
|
4520
|
+
offDenied();
|
|
4521
|
+
};
|
|
4522
|
+
}, [events, dispatch]);
|
|
4523
|
+
}
|
|
4538
4524
|
var USAGE = "Usage:\n /kill \u2014 list active processes + breaker state\n /kill list \u2014 same as /kill\n /kill all \u2014 kill all tracked processes (SIGTERM \u2192 SIGKILL)\n /kill force \u2014 kill all with SIGKILL immediately\n /kill reset \u2014 reset the circuit breaker to closed\n /kill <pid> \u2014 kill a specific process by PID";
|
|
4539
4525
|
function createKillSlashCommand() {
|
|
4540
4526
|
return {
|
|
@@ -4754,14 +4740,44 @@ function oneLine(s2, max) {
|
|
|
4754
4740
|
const collapsed = s2.replace(/\s+/g, " ").trim();
|
|
4755
4741
|
return collapsed.length <= max ? collapsed : `${collapsed.slice(0, max - 1)}\u2026`;
|
|
4756
4742
|
}
|
|
4757
|
-
|
|
4758
|
-
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4743
|
+
|
|
4744
|
+
// src/steering-preamble.ts
|
|
4745
|
+
function buildSteeringPreamble(snapshot, newDirection) {
|
|
4746
|
+
const lines = ["[STEERING \u2014 I pressed Esc to interrupt you mid-task on purpose.", ""];
|
|
4747
|
+
const ctx = [];
|
|
4748
|
+
if (snapshot?.runningTools && snapshot.runningTools.length > 0) {
|
|
4749
|
+
ctx.push(`- in-flight tools (now cancelled): ${snapshot.runningTools.join(", ")}`);
|
|
4750
|
+
}
|
|
4751
|
+
if (snapshot?.subagentsTerminated && snapshot.subagentsTerminated > 0) {
|
|
4752
|
+
const subDetails = snapshot.subagents.map((s2) => `${s2.label}${s2.tool ? ` (was running: ${s2.tool})` : ""}`).join(", ");
|
|
4753
|
+
ctx.push(
|
|
4754
|
+
`- subagents (${snapshot.subagentsTerminated} terminated by me, do NOT await them): ${subDetails}`
|
|
4755
|
+
);
|
|
4756
|
+
}
|
|
4757
|
+
if (snapshot?.partialAssistantText && snapshot.partialAssistantText.trim().length > 0) {
|
|
4758
|
+
const tail = snapshot.partialAssistantText.trim().slice(-300);
|
|
4759
|
+
ctx.push(`- your last partial output (truncated, for context only): "${tail}"`);
|
|
4760
|
+
}
|
|
4761
|
+
if (ctx.length > 0) {
|
|
4762
|
+
lines.push("What was happening when I cut you off:");
|
|
4763
|
+
lines.push(...ctx);
|
|
4764
|
+
lines.push("");
|
|
4765
|
+
}
|
|
4766
|
+
lines.push("You have authority to:");
|
|
4767
|
+
lines.push("- Abandon the prior plan entirely if the new direction makes it stale.");
|
|
4768
|
+
lines.push("- Re-spawn fresh subagents (with different roles or tasks) if needed.");
|
|
4769
|
+
lines.push('- Skip a polite "should I continue?" \u2014 just pivot.');
|
|
4770
|
+
lines.push("- Ask me to clarify if the new direction is genuinely ambiguous.");
|
|
4771
|
+
lines.push("");
|
|
4772
|
+
lines.push("New direction:");
|
|
4773
|
+
lines.push("---");
|
|
4774
|
+
lines.push(newDirection);
|
|
4775
|
+
lines.push("---");
|
|
4776
|
+
lines.push("]");
|
|
4777
|
+
return lines.join("\n");
|
|
4764
4778
|
}
|
|
4779
|
+
|
|
4780
|
+
// src/app-reducer.ts
|
|
4765
4781
|
function reducer(state, action) {
|
|
4766
4782
|
switch (action.type) {
|
|
4767
4783
|
case "addEntry": {
|
|
@@ -5355,6 +5371,9 @@ function reducer(state, action) {
|
|
|
5355
5371
|
} : state.fleetTokens
|
|
5356
5372
|
};
|
|
5357
5373
|
}
|
|
5374
|
+
case "fleetConcurrency": {
|
|
5375
|
+
return { ...state, fleetConcurrency: action.n };
|
|
5376
|
+
}
|
|
5358
5377
|
case "leaderIterStart": {
|
|
5359
5378
|
return {
|
|
5360
5379
|
...state,
|
|
@@ -5543,7 +5562,7 @@ function reducer(state, action) {
|
|
|
5543
5562
|
case "worktreeMonitorToggle": {
|
|
5544
5563
|
return { ...state, worktreeMonitorOpen: !state.worktreeMonitorOpen };
|
|
5545
5564
|
}
|
|
5546
|
-
// --- In-app chat scroll
|
|
5565
|
+
// --- In-app chat scroll ---
|
|
5547
5566
|
case "scrollBy": {
|
|
5548
5567
|
const maxOffset = Math.max(0, state.totalLines - state.viewportRows);
|
|
5549
5568
|
const next = Math.max(0, Math.min(maxOffset, state.scrollOffset + action.delta));
|
|
@@ -5721,41 +5740,14 @@ function reducer(state, action) {
|
|
|
5721
5740
|
}
|
|
5722
5741
|
}
|
|
5723
5742
|
}
|
|
5724
|
-
var
|
|
5725
|
-
|
|
5726
|
-
|
|
5727
|
-
|
|
5728
|
-
|
|
5729
|
-
|
|
5730
|
-
}
|
|
5731
|
-
if (snapshot?.subagentsTerminated && snapshot.subagentsTerminated > 0) {
|
|
5732
|
-
const subDetails = snapshot.subagents.map((s2) => `${s2.label}${s2.tool ? ` (was running: ${s2.tool})` : ""}`).join(", ");
|
|
5733
|
-
ctx.push(
|
|
5734
|
-
`- subagents (${snapshot.subagentsTerminated} terminated by me, do NOT await them): ${subDetails}`
|
|
5735
|
-
);
|
|
5736
|
-
}
|
|
5737
|
-
if (snapshot?.partialAssistantText && snapshot.partialAssistantText.trim().length > 0) {
|
|
5738
|
-
const tail = snapshot.partialAssistantText.trim().slice(-300);
|
|
5739
|
-
ctx.push(`- your last partial output (truncated, for context only): "${tail}"`);
|
|
5740
|
-
}
|
|
5741
|
-
if (ctx.length > 0) {
|
|
5742
|
-
lines.push("What was happening when I cut you off:");
|
|
5743
|
-
lines.push(...ctx);
|
|
5744
|
-
lines.push("");
|
|
5745
|
-
}
|
|
5746
|
-
lines.push("You have authority to:");
|
|
5747
|
-
lines.push("- Abandon the prior plan entirely if the new direction makes it stale.");
|
|
5748
|
-
lines.push("- Re-spawn fresh subagents (with different roles or tasks) if needed.");
|
|
5749
|
-
lines.push('- Skip a polite "should I continue?" \u2014 just pivot.');
|
|
5750
|
-
lines.push("- Ask me to clarify if the new direction is genuinely ambiguous.");
|
|
5751
|
-
lines.push("");
|
|
5752
|
-
lines.push("New direction:");
|
|
5753
|
-
lines.push("---");
|
|
5754
|
-
lines.push(newDirection);
|
|
5755
|
-
lines.push("---");
|
|
5756
|
-
lines.push("]");
|
|
5757
|
-
return lines.join("\n");
|
|
5743
|
+
var MIN_VIEWPORT = 3;
|
|
5744
|
+
var INPUT_PROMPT = "\u203A ";
|
|
5745
|
+
function selectedSlashCommandLine(picker) {
|
|
5746
|
+
if (!picker.open || picker.matches.length === 0) return null;
|
|
5747
|
+
const picked = picker.matches[picker.selected];
|
|
5748
|
+
return picked ? `/${picked.name}` : null;
|
|
5758
5749
|
}
|
|
5750
|
+
var PASTE_THRESHOLD_CHARS = 200;
|
|
5759
5751
|
function App({
|
|
5760
5752
|
agent,
|
|
5761
5753
|
slashRegistry,
|
|
@@ -5799,8 +5791,6 @@ function App({
|
|
|
5799
5791
|
initialGoal,
|
|
5800
5792
|
initialAsk,
|
|
5801
5793
|
sessionsDir,
|
|
5802
|
-
mouse = false,
|
|
5803
|
-
subscribeMouse,
|
|
5804
5794
|
managed = false
|
|
5805
5795
|
}) {
|
|
5806
5796
|
const { exit } = useApp();
|
|
@@ -5812,18 +5802,15 @@ function App({
|
|
|
5812
5802
|
const [hiddenItems, setHiddenItems] = useState(statuslineHiddenItems);
|
|
5813
5803
|
const { stdout } = useStdout();
|
|
5814
5804
|
const [termRows, setTermRows] = useState(stdout?.rows ?? 24);
|
|
5815
|
-
const [termCols, setTermCols] = useState(stdout?.columns ?? 80);
|
|
5816
5805
|
useEffect(() => {
|
|
5817
5806
|
const onResize = () => {
|
|
5818
5807
|
setTermRows(process.stdout.rows ?? 24);
|
|
5819
|
-
setTermCols(process.stdout.columns ?? 80);
|
|
5820
5808
|
};
|
|
5821
5809
|
process.stdout.on("resize", onResize);
|
|
5822
5810
|
return () => {
|
|
5823
5811
|
process.stdout.off("resize", onResize);
|
|
5824
5812
|
};
|
|
5825
5813
|
}, []);
|
|
5826
|
-
const [mouseLive, setMouseLive] = useState(mouse);
|
|
5827
5814
|
const [managedLive, setManagedLive] = useState(managed);
|
|
5828
5815
|
useEffect(() => {
|
|
5829
5816
|
setHiddenItems(statuslineHiddenItems);
|
|
@@ -5910,6 +5897,7 @@ function App({
|
|
|
5910
5897
|
},
|
|
5911
5898
|
fleetCost: 0,
|
|
5912
5899
|
fleetTokens: { input: 0, output: 0 },
|
|
5900
|
+
fleetConcurrency: 4,
|
|
5913
5901
|
streamFleet: true,
|
|
5914
5902
|
monitorOpen: false,
|
|
5915
5903
|
agentsMonitorOpen: false,
|
|
@@ -5936,251 +5924,34 @@ function App({
|
|
|
5936
5924
|
const activeCtrlRef = useRef(null);
|
|
5937
5925
|
const exitRequestedRef = useRef(false);
|
|
5938
5926
|
const inputGateRef = useRef(false);
|
|
5939
|
-
const lastEnterAtRef = useRef(0);
|
|
5940
|
-
const tokenPreviewsRef = useRef(/* @__PURE__ */ new Map());
|
|
5941
|
-
const projectName =
|
|
5942
|
-
const base = path2.basename(projectRoot);
|
|
5943
|
-
return base && base !== path2.sep ? base : void 0;
|
|
5944
|
-
}, [projectRoot]);
|
|
5945
|
-
const streamingTextRef = useRef("");
|
|
5946
|
-
const pendingDeltaRef = useRef("");
|
|
5947
|
-
const flushTimerRef = useRef(null);
|
|
5948
|
-
const stateRef = useRef(state);
|
|
5949
|
-
stateRef.current = state;
|
|
5950
|
-
const draftRef = useRef({ buffer: state.buffer, cursor: state.cursor });
|
|
5951
|
-
draftRef.current = { buffer: state.buffer, cursor: state.cursor };
|
|
5952
|
-
const bottomRef = useRef(null);
|
|
5953
|
-
|
|
5954
|
-
|
|
5955
|
-
|
|
5956
|
-
|
|
5957
|
-
|
|
5958
|
-
|
|
5959
|
-
const
|
|
5960
|
-
|
|
5961
|
-
|
|
5962
|
-
|
|
5963
|
-
preRowsRef.current = measureElement(prePickerRef.current).height;
|
|
5964
|
-
}
|
|
5965
|
-
if (liveStripRef.current) {
|
|
5966
|
-
liveStripRowsRef.current = measureElement(liveStripRef.current).height;
|
|
5967
|
-
}
|
|
5968
|
-
const s2 = stateRef.current;
|
|
5969
|
-
const affordance = s2.scrollOffset > 0 && s2.pendingNewLines > 0 ? 1 : 0;
|
|
5970
|
-
const vp = Math.max(MIN_VIEWPORT, termRows - height - affordance - 1);
|
|
5971
|
-
if (vp !== s2.viewportRows) {
|
|
5972
|
-
dispatch({ type: "setViewportRows", rows: vp });
|
|
5973
|
-
}
|
|
5974
|
-
}, [managedLive, termRows]);
|
|
5975
|
-
const handleKeyRef = useRef(null);
|
|
5976
|
-
const pendingClickConfirmRef = useRef(null);
|
|
5977
|
-
const openModelPickerRef = useRef(null);
|
|
5978
|
-
const openAutonomyPickerRef = useRef(null);
|
|
5979
|
-
const handleRewindToRef = useRef(null);
|
|
5980
|
-
const confirmRef = useRef(null);
|
|
5981
|
-
const confirmDecisionRef = useRef(null);
|
|
5982
|
-
const scrollbarDragRef = useRef(false);
|
|
5983
|
-
const lastLeftClickRef = useRef(null);
|
|
5984
|
-
const DOUBLE_CLICK_MS = 500;
|
|
5985
|
-
const DOUBLE_CLICK_DIST = 5;
|
|
5986
|
-
const statusChipRef = useRef({ version: appVersion, model, fleetRunning: 0, yolo, autonomy: "off" });
|
|
5987
|
-
const handleMouse = React4.useCallback(
|
|
5988
|
-
(ev) => {
|
|
5989
|
-
const s2 = stateRef.current;
|
|
5990
|
-
if (ev.type === "wheel") {
|
|
5991
|
-
const up = ev.button === "wheelUp";
|
|
5992
|
-
const step = up ? -1 : 1;
|
|
5993
|
-
if (s2.slashPicker.open) return dispatch({ type: "slashPickerMove", delta: step });
|
|
5994
|
-
if (s2.modelPicker.open) return dispatch({ type: "modelPickerMove", delta: step });
|
|
5995
|
-
if (s2.autonomyPicker.open) return dispatch({ type: "autonomyPickerMove", delta: step });
|
|
5996
|
-
if (s2.settingsPicker.open) return dispatch({ type: "settingsFieldMove", delta: step });
|
|
5997
|
-
if (s2.picker.open) return dispatch({ type: "pickerMove", delta: step });
|
|
5998
|
-
if (s2.rewindOverlay) return dispatch({ type: "rewindOverlayMove", delta: step });
|
|
5999
|
-
return dispatch({ type: "scrollBy", delta: up ? WHEEL_STEP : -WHEEL_STEP });
|
|
6000
|
-
}
|
|
6001
|
-
if (ev.type === "release") {
|
|
6002
|
-
scrollbarDragRef.current = false;
|
|
6003
|
-
return;
|
|
6004
|
-
}
|
|
6005
|
-
if (ev.button !== "left") return;
|
|
6006
|
-
const now = Date.now();
|
|
6007
|
-
const lastClick = lastLeftClickRef.current;
|
|
6008
|
-
const isMultiClick = lastClick !== null && now - lastClick.ts < DOUBLE_CLICK_MS && Math.abs(ev.x - lastClick.x) <= DOUBLE_CLICK_DIST && Math.abs(ev.y - lastClick.y) <= DOUBLE_CLICK_DIST;
|
|
6009
|
-
const clickCount = isMultiClick ? lastClick.count + 1 : 1;
|
|
6010
|
-
lastLeftClickRef.current = { x: ev.x, y: ev.y, ts: now, count: clickCount };
|
|
6011
|
-
{
|
|
6012
|
-
const rows = s2.viewportRows;
|
|
6013
|
-
if (ev.drag) {
|
|
6014
|
-
if (scrollbarDragRef.current && s2.totalLines > rows) {
|
|
6015
|
-
dispatch({
|
|
6016
|
-
type: "scrollTo",
|
|
6017
|
-
offset: scrollOffsetForTrackRow(rows, s2.totalLines, ev.y - 1)
|
|
6018
|
-
});
|
|
6019
|
-
}
|
|
6020
|
-
return;
|
|
6021
|
-
}
|
|
6022
|
-
const cols = termCols || 80;
|
|
6023
|
-
const onScrollbar = cols > 0 && ev.x >= cols - 2 && ev.y >= 1 && ev.y <= rows;
|
|
6024
|
-
if (onScrollbar && s2.totalLines > rows) {
|
|
6025
|
-
scrollbarDragRef.current = true;
|
|
6026
|
-
dispatch({
|
|
6027
|
-
type: "scrollTo",
|
|
6028
|
-
offset: scrollOffsetForTrackRow(rows, s2.totalLines, ev.y - 1)
|
|
6029
|
-
});
|
|
6030
|
-
return;
|
|
6031
|
-
}
|
|
6032
|
-
}
|
|
6033
|
-
if (ev.y > s2.viewportRows) {
|
|
6034
|
-
if (s2.helpOpen) return dispatch({ type: "toggleHelp" });
|
|
6035
|
-
if (s2.agentsMonitorOpen) return dispatch({ type: "toggleAgentsMonitor" });
|
|
6036
|
-
if (s2.monitorOpen) return dispatch({ type: "toggleMonitor" });
|
|
6037
|
-
if (s2.worktreeMonitorOpen) return dispatch({ type: "worktreeMonitorToggle" });
|
|
6038
|
-
if (s2.autoPhase?.monitorOpen) return dispatch({ type: "autoPhaseMonitorToggle" });
|
|
6039
|
-
}
|
|
6040
|
-
const affordance = s2.scrollOffset > 0 && s2.pendingNewLines > 0 ? 1 : 0;
|
|
6041
|
-
if (affordance && ev.y === s2.viewportRows + 1) {
|
|
6042
|
-
return dispatch({ type: "scrollToBottom" });
|
|
6043
|
-
}
|
|
6044
|
-
if (s2.confirmQueue.length > 0) {
|
|
6045
|
-
const node = confirmRef.current;
|
|
6046
|
-
const head = s2.confirmQueue[0];
|
|
6047
|
-
if (node && head) {
|
|
6048
|
-
const { height } = measureElement(node);
|
|
6049
|
-
const top = s2.viewportRows + affordance + preRowsRef.current + 1;
|
|
6050
|
-
const buttonsRow = top + height - 2;
|
|
6051
|
-
if (ev.y === buttonsRow) {
|
|
6052
|
-
const contentX = ev.x - 1 - 2;
|
|
6053
|
-
for (const seg of confirmButtonSegments(head.suggestedPattern)) {
|
|
6054
|
-
if (contentX >= seg.start && contentX < seg.start + seg.len) {
|
|
6055
|
-
confirmDecisionRef.current?.(seg.decision);
|
|
6056
|
-
return;
|
|
6057
|
-
}
|
|
6058
|
-
}
|
|
6059
|
-
}
|
|
6060
|
-
}
|
|
6061
|
-
return;
|
|
6062
|
-
}
|
|
6063
|
-
if (s2.rewindOverlay) {
|
|
6064
|
-
const cps = s2.rewindOverlay.checkpoints;
|
|
6065
|
-
const firstItemRow2 = s2.viewportRows + affordance + preRowsRef.current + 3 + 1;
|
|
6066
|
-
const index2 = ev.y - firstItemRow2;
|
|
6067
|
-
if (index2 < 0 || index2 >= cps.length) return;
|
|
6068
|
-
if (index2 === s2.rewindOverlay.selected) {
|
|
6069
|
-
handleRewindToRef.current?.(cps[index2].promptIndex);
|
|
6070
|
-
} else {
|
|
6071
|
-
dispatch({ type: "rewindOverlayMove", delta: index2 - s2.rewindOverlay.selected });
|
|
6072
|
-
}
|
|
6073
|
-
return;
|
|
6074
|
-
}
|
|
6075
|
-
if (s2.settingsPicker.open) {
|
|
6076
|
-
const firstRow = s2.viewportRows + affordance + preRowsRef.current + 2 + 1;
|
|
6077
|
-
const field = ev.y - firstRow;
|
|
6078
|
-
if (field < 0 || field > 1) return;
|
|
6079
|
-
if (field === s2.settingsPicker.field) {
|
|
6080
|
-
dispatch({ type: "settingsValueChange", delta: 1 });
|
|
6081
|
-
} else {
|
|
6082
|
-
dispatch({ type: "settingsFieldSet", field });
|
|
6083
|
-
}
|
|
6084
|
-
return;
|
|
6085
|
-
}
|
|
6086
|
-
const picker = s2.modelPicker.open ? {
|
|
6087
|
-
header: 2,
|
|
6088
|
-
count: s2.modelPicker.step === "provider" ? s2.modelPicker.providerOptions.length : s2.modelPicker.modelOptions.length,
|
|
6089
|
-
selected: s2.modelPicker.selected,
|
|
6090
|
-
move: (delta) => dispatch({ type: "modelPickerMove", delta })
|
|
6091
|
-
} : s2.autonomyPicker.open ? {
|
|
6092
|
-
header: 2,
|
|
6093
|
-
count: s2.autonomyPicker.options.length,
|
|
6094
|
-
selected: s2.autonomyPicker.selected,
|
|
6095
|
-
move: (delta) => dispatch({ type: "autonomyPickerMove", delta })
|
|
6096
|
-
} : s2.slashPicker.open ? {
|
|
6097
|
-
header: 1,
|
|
6098
|
-
count: s2.slashPicker.matches.length,
|
|
6099
|
-
selected: s2.slashPicker.selected,
|
|
6100
|
-
move: (delta) => dispatch({ type: "slashPickerMove", delta })
|
|
6101
|
-
} : s2.picker.open ? {
|
|
6102
|
-
header: 1,
|
|
6103
|
-
count: s2.picker.matches.length,
|
|
6104
|
-
selected: s2.picker.selected,
|
|
6105
|
-
move: (delta) => dispatch({ type: "pickerMove", delta })
|
|
6106
|
-
} : null;
|
|
6107
|
-
if (!picker || picker.count === 0) {
|
|
6108
|
-
const inputDisabled = s2.status === "aborting" && !s2.steeringPending;
|
|
6109
|
-
if (!inputDisabled) {
|
|
6110
|
-
const cols = termCols || 80;
|
|
6111
|
-
const inputTop = s2.viewportRows + affordance + liveStripRowsRef.current + 1;
|
|
6112
|
-
const inputRows = layoutInputRows(INPUT_PROMPT, s2.buffer, s2.cursor, cols).length;
|
|
6113
|
-
const rowIdx = ev.y - inputTop;
|
|
6114
|
-
if (rowIdx >= 0 && rowIdx < inputRows) {
|
|
6115
|
-
const next = inputIndexAtRowCol(INPUT_PROMPT, s2.buffer, cols, rowIdx, ev.x - 1);
|
|
6116
|
-
return dispatch({ type: "setBuffer", buffer: s2.buffer, cursor: next });
|
|
6117
|
-
}
|
|
6118
|
-
}
|
|
6119
|
-
if (!s2.helpOpen && !s2.agentsMonitorOpen && !s2.monitorOpen && !s2.worktreeMonitorOpen && !s2.autoPhase?.monitorOpen) {
|
|
6120
|
-
const chip = statusChipRef.current;
|
|
6121
|
-
const statusTop = s2.viewportRows + affordance + preRowsRef.current + 1;
|
|
6122
|
-
const contentX = ev.x - 1;
|
|
6123
|
-
if (ev.y === statusTop + 1) {
|
|
6124
|
-
const span = statusBarModelSpan({
|
|
6125
|
-
version: chip.version,
|
|
6126
|
-
state: s2.status,
|
|
6127
|
-
fleetRunning: chip.fleetRunning,
|
|
6128
|
-
model: chip.model
|
|
6129
|
-
});
|
|
6130
|
-
if (contentX >= span.start && contentX < span.start + span.len) {
|
|
6131
|
-
openModelPickerRef.current?.();
|
|
6132
|
-
}
|
|
6133
|
-
} else if (ev.y === statusTop + 2) {
|
|
6134
|
-
const span = statusBarAutonomySpan({ yolo: chip.yolo, autonomy: chip.autonomy });
|
|
6135
|
-
if (span && contentX >= span.start && contentX < span.start + span.len) {
|
|
6136
|
-
openAutonomyPickerRef.current?.();
|
|
6137
|
-
}
|
|
6138
|
-
}
|
|
6139
|
-
}
|
|
6140
|
-
return;
|
|
6141
|
-
}
|
|
6142
|
-
const firstItemRow = s2.viewportRows + affordance + preRowsRef.current + picker.header + 1;
|
|
6143
|
-
const index = ev.y - firstItemRow;
|
|
6144
|
-
if (index < 0 || index >= picker.count) return;
|
|
6145
|
-
if (index === picker.selected) {
|
|
6146
|
-
handleKeyRef.current?.("", { ...EMPTY_KEY, return: true });
|
|
6147
|
-
} else {
|
|
6148
|
-
pendingClickConfirmRef.current = index;
|
|
6149
|
-
picker.move(index - picker.selected);
|
|
6150
|
-
}
|
|
6151
|
-
},
|
|
6152
|
-
// dispatch is stable (useReducer); refs are mutable — no reactive deps.
|
|
6153
|
-
// termCols is stable (useState + resize effect).
|
|
6154
|
-
[termCols]
|
|
6155
|
-
);
|
|
6156
|
-
useEffect(() => {
|
|
6157
|
-
if (!subscribeMouse) return;
|
|
6158
|
-
return subscribeMouse(handleMouse);
|
|
6159
|
-
}, [subscribeMouse, handleMouse]);
|
|
6160
|
-
useEffect(() => {
|
|
6161
|
-
const target = pendingClickConfirmRef.current;
|
|
6162
|
-
if (target === null) return;
|
|
6163
|
-
const open = state.slashPicker.open || state.modelPicker.open || state.autonomyPicker.open || state.picker.open;
|
|
6164
|
-
if (!open) {
|
|
6165
|
-
pendingClickConfirmRef.current = null;
|
|
6166
|
-
return;
|
|
6167
|
-
}
|
|
6168
|
-
const sel = state.slashPicker.open ? state.slashPicker.selected : state.modelPicker.open ? state.modelPicker.selected : state.autonomyPicker.open ? state.autonomyPicker.selected : state.picker.selected;
|
|
6169
|
-
if (sel === target) {
|
|
6170
|
-
pendingClickConfirmRef.current = null;
|
|
6171
|
-
handleKeyRef.current?.("", { ...EMPTY_KEY, return: true });
|
|
5927
|
+
const lastEnterAtRef = useRef(0);
|
|
5928
|
+
const tokenPreviewsRef = useRef(/* @__PURE__ */ new Map());
|
|
5929
|
+
const projectName = React5.useMemo(() => {
|
|
5930
|
+
const base = path2.basename(projectRoot);
|
|
5931
|
+
return base && base !== path2.sep ? base : void 0;
|
|
5932
|
+
}, [projectRoot]);
|
|
5933
|
+
const streamingTextRef = useRef("");
|
|
5934
|
+
const pendingDeltaRef = useRef("");
|
|
5935
|
+
const flushTimerRef = useRef(null);
|
|
5936
|
+
const stateRef = useRef(state);
|
|
5937
|
+
stateRef.current = state;
|
|
5938
|
+
const draftRef = useRef({ buffer: state.buffer, cursor: state.cursor });
|
|
5939
|
+
draftRef.current = { buffer: state.buffer, cursor: state.cursor };
|
|
5940
|
+
const bottomRef = useRef(null);
|
|
5941
|
+
React5.useLayoutEffect(() => {
|
|
5942
|
+
if (!managedLive) return;
|
|
5943
|
+
const node = bottomRef.current;
|
|
5944
|
+
if (!node) return;
|
|
5945
|
+
const { height } = measureElement(node);
|
|
5946
|
+
const s2 = stateRef.current;
|
|
5947
|
+
const affordance = s2.scrollOffset > 0 && s2.pendingNewLines > 0 ? 1 : 0;
|
|
5948
|
+
const vp = Math.max(MIN_VIEWPORT, termRows - height - affordance - 1);
|
|
5949
|
+
if (vp !== s2.viewportRows) {
|
|
5950
|
+
dispatch({ type: "setViewportRows", rows: vp });
|
|
6172
5951
|
}
|
|
6173
|
-
}, [
|
|
6174
|
-
|
|
6175
|
-
|
|
6176
|
-
state.modelPicker.open,
|
|
6177
|
-
state.modelPicker.selected,
|
|
6178
|
-
state.autonomyPicker.open,
|
|
6179
|
-
state.autonomyPicker.selected,
|
|
6180
|
-
state.picker.open,
|
|
6181
|
-
state.picker.selected
|
|
6182
|
-
]);
|
|
6183
|
-
const handleRewindTo = React4.useCallback(
|
|
5952
|
+
}, [managedLive, termRows]);
|
|
5953
|
+
const handleKeyRef = useRef(null);
|
|
5954
|
+
const handleRewindTo = React5.useCallback(
|
|
6184
5955
|
async (checkpointIndex) => {
|
|
6185
5956
|
const sessionId = agent.ctx.session.id;
|
|
6186
5957
|
if (!sessionId) return;
|
|
@@ -6199,13 +5970,13 @@ function App({
|
|
|
6199
5970
|
dispatch({ type: "clearInput" });
|
|
6200
5971
|
};
|
|
6201
5972
|
const startedAtRef = useRef(Date.now());
|
|
6202
|
-
const [nowTick, setNowTick] =
|
|
5973
|
+
const [nowTick, setNowTick] = React5.useState(Date.now());
|
|
6203
5974
|
useEffect(() => {
|
|
6204
5975
|
const t = setInterval(() => setNowTick(Date.now()), 1e3);
|
|
6205
5976
|
return () => clearInterval(t);
|
|
6206
5977
|
}, []);
|
|
6207
5978
|
const elapsedMs = nowTick - startedAtRef.current;
|
|
6208
|
-
const [gitInfo, setGitInfo] =
|
|
5979
|
+
const [gitInfo, setGitInfo] = React5.useState(null);
|
|
6209
5980
|
useEffect(() => {
|
|
6210
5981
|
let cancelled = false;
|
|
6211
5982
|
const refresh = () => {
|
|
@@ -6270,16 +6041,16 @@ function App({
|
|
|
6270
6041
|
};
|
|
6271
6042
|
return { leader: leaderEntry, ...state.fleet };
|
|
6272
6043
|
}, [state.fleet, state.leader, state.status, provider, model, effectiveMaxContext]);
|
|
6273
|
-
const
|
|
6044
|
+
const STREAM_COLORS2 = ["cyan", "magenta", "yellow", "green", "blue"];
|
|
6274
6045
|
const labelsRef = useRef(/* @__PURE__ */ new Map());
|
|
6275
|
-
const
|
|
6046
|
+
const labelFor2 = (id, name) => {
|
|
6276
6047
|
const m = labelsRef.current;
|
|
6277
6048
|
const existing = m.get(id);
|
|
6278
6049
|
if (existing) return existing;
|
|
6279
6050
|
const n = m.size + 1;
|
|
6280
6051
|
const v = {
|
|
6281
6052
|
label: name && name !== id ? name : `AGENT#${n}`,
|
|
6282
|
-
color:
|
|
6053
|
+
color: STREAM_COLORS2[(n - 1) % STREAM_COLORS2.length]
|
|
6283
6054
|
};
|
|
6284
6055
|
m.set(id, v);
|
|
6285
6056
|
return v;
|
|
@@ -6562,60 +6333,6 @@ function App({
|
|
|
6562
6333
|
slashRegistry.unregister("altscreen");
|
|
6563
6334
|
};
|
|
6564
6335
|
}, [slashRegistry]);
|
|
6565
|
-
const mouseLiveRef = useRef(mouseLive);
|
|
6566
|
-
mouseLiveRef.current = mouseLive;
|
|
6567
|
-
useEffect(() => {
|
|
6568
|
-
const MOUSE_ON_SEQ = "\x1B[?1000h\x1B[?1006h";
|
|
6569
|
-
const MOUSE_OFF_SEQ = "\x1B[?1006l\x1B[?1000l";
|
|
6570
|
-
const ALT_ON = "\x1B[?1049h";
|
|
6571
|
-
const ALT_OFF = "\x1B[?1049l";
|
|
6572
|
-
const cmd = {
|
|
6573
|
-
name: "mouse",
|
|
6574
|
-
description: "Toggle mouse mode (clickable menus + wheel-scroll chat). Needs launch with --mouse to enable.",
|
|
6575
|
-
async run(args) {
|
|
6576
|
-
const arg = args.trim().toLowerCase();
|
|
6577
|
-
if (arg !== "on" && arg !== "off") {
|
|
6578
|
-
return {
|
|
6579
|
-
message: `Mouse mode is ${mouseLiveRef.current ? "ON" : "OFF"}. Usage: /mouse on|off`
|
|
6580
|
-
};
|
|
6581
|
-
}
|
|
6582
|
-
if (arg === "on") {
|
|
6583
|
-
if (!mouse) {
|
|
6584
|
-
return {
|
|
6585
|
-
message: "Mouse mode needs the --mouse launch flag (it rewires stdin so mouse bytes never reach the input). Restart with `wstack --tui --mouse`."
|
|
6586
|
-
};
|
|
6587
|
-
}
|
|
6588
|
-
try {
|
|
6589
|
-
writeOut(ALT_ON);
|
|
6590
|
-
writeOut("\x1B[H");
|
|
6591
|
-
writeOut(MOUSE_ON_SEQ);
|
|
6592
|
-
} catch {
|
|
6593
|
-
return { message: "Failed to enable mouse mode." };
|
|
6594
|
-
}
|
|
6595
|
-
setMouseLive(true);
|
|
6596
|
-
setManagedLive(true);
|
|
6597
|
-
return {
|
|
6598
|
-
message: "Mouse mode ON. Click menu items, wheel-scroll the chat (PgUp/PgDn too). Native terminal copy/scroll are suspended until `/mouse off`."
|
|
6599
|
-
};
|
|
6600
|
-
}
|
|
6601
|
-
try {
|
|
6602
|
-
writeOut(MOUSE_OFF_SEQ);
|
|
6603
|
-
writeOut(ALT_OFF);
|
|
6604
|
-
} catch {
|
|
6605
|
-
return { message: "Failed to disable mouse mode." };
|
|
6606
|
-
}
|
|
6607
|
-
setMouseLive(false);
|
|
6608
|
-
setManagedLive(false);
|
|
6609
|
-
return {
|
|
6610
|
-
message: "Mouse mode OFF. Native terminal scroll/copy restored; chat history flows into native scrollback again."
|
|
6611
|
-
};
|
|
6612
|
-
}
|
|
6613
|
-
};
|
|
6614
|
-
slashRegistry.register(cmd);
|
|
6615
|
-
return () => {
|
|
6616
|
-
slashRegistry.unregister("mouse");
|
|
6617
|
-
};
|
|
6618
|
-
}, [slashRegistry, mouse]);
|
|
6619
6336
|
useEffect(() => {
|
|
6620
6337
|
const cmd = {
|
|
6621
6338
|
name: "steer",
|
|
@@ -6722,16 +6439,12 @@ function App({
|
|
|
6722
6439
|
slashRegistry.unregister("agents");
|
|
6723
6440
|
};
|
|
6724
6441
|
}, [slashRegistry]);
|
|
6725
|
-
const openModelPicker =
|
|
6442
|
+
const openModelPicker = React5.useCallback(async () => {
|
|
6726
6443
|
if (!getPickableProviders) return;
|
|
6727
6444
|
const providers = await getPickableProviders();
|
|
6728
6445
|
dispatch({ type: "modelPickerOpen", providers });
|
|
6729
6446
|
}, [getPickableProviders]);
|
|
6730
|
-
const
|
|
6731
|
-
if (!switchAutonomy) return;
|
|
6732
|
-
dispatch({ type: "autonomyPickerOpen", options: AUTONOMY_OPTIONS });
|
|
6733
|
-
}, [switchAutonomy]);
|
|
6734
|
-
const openSettings = React4.useCallback(() => {
|
|
6447
|
+
const openSettings = React5.useCallback(() => {
|
|
6735
6448
|
if (!getSettings) return;
|
|
6736
6449
|
const s2 = getSettings();
|
|
6737
6450
|
dispatch({ type: "settingsOpen", mode: s2.mode, delayMs: s2.delayMs });
|
|
@@ -6915,172 +6628,7 @@ function App({
|
|
|
6915
6628
|
useEffect(() => {
|
|
6916
6629
|
streamFleetRef.current = state.streamFleet;
|
|
6917
6630
|
}, [state.streamFleet]);
|
|
6918
|
-
|
|
6919
|
-
const offSpawned = events.on("subagent.spawned", (e) => {
|
|
6920
|
-
const lbl = labelFor(e.subagentId, e.name);
|
|
6921
|
-
dispatch({
|
|
6922
|
-
type: "fleetSpawn",
|
|
6923
|
-
id: e.subagentId,
|
|
6924
|
-
name: e.name,
|
|
6925
|
-
provider: e.provider,
|
|
6926
|
-
model: e.model,
|
|
6927
|
-
transcriptPath: e.transcriptPath
|
|
6928
|
-
});
|
|
6929
|
-
const where = e.provider && e.model ? `${e.provider}/${e.model}` : "spawned";
|
|
6930
|
-
const desc = e.description ? ` \u2014 ${e.description.slice(0, 80)}` : "";
|
|
6931
|
-
dispatch({
|
|
6932
|
-
type: "addEntry",
|
|
6933
|
-
entry: {
|
|
6934
|
-
kind: "subagent",
|
|
6935
|
-
agentLabel: lbl.label,
|
|
6936
|
-
agentColor: lbl.color,
|
|
6937
|
-
icon: "\u25B6",
|
|
6938
|
-
text: `${where}${desc}`
|
|
6939
|
-
}
|
|
6940
|
-
});
|
|
6941
|
-
});
|
|
6942
|
-
const offStarted = events.on("subagent.task_started", (e) => {
|
|
6943
|
-
const lbl = labelFor(e.subagentId);
|
|
6944
|
-
dispatch({ type: "fleetStart", id: e.subagentId, taskId: e.taskId });
|
|
6945
|
-
const desc = e.description ? ` \u2014 ${e.description.slice(0, 80)}` : "";
|
|
6946
|
-
dispatch({
|
|
6947
|
-
type: "addEntry",
|
|
6948
|
-
entry: {
|
|
6949
|
-
kind: "subagent",
|
|
6950
|
-
agentLabel: lbl.label,
|
|
6951
|
-
agentColor: lbl.color,
|
|
6952
|
-
icon: "\u25CF",
|
|
6953
|
-
text: `task started${desc}`
|
|
6954
|
-
}
|
|
6955
|
-
});
|
|
6956
|
-
});
|
|
6957
|
-
const offCompleted = events.on("subagent.task_completed", (e) => {
|
|
6958
|
-
const lbl = labelFor(e.subagentId);
|
|
6959
|
-
const errKind = e.error?.kind;
|
|
6960
|
-
dispatch({
|
|
6961
|
-
type: "fleetDone",
|
|
6962
|
-
id: e.subagentId,
|
|
6963
|
-
status: e.status,
|
|
6964
|
-
iterations: e.iterations,
|
|
6965
|
-
toolCalls: e.toolCalls,
|
|
6966
|
-
failureReason: errKind
|
|
6967
|
-
});
|
|
6968
|
-
const icon = e.status === "success" ? "\u2713" : e.status === "timeout" ? "\u23F1" : e.status === "stopped" ? "\u2298" : "\u2717";
|
|
6969
|
-
const errMsg = e.error?.message;
|
|
6970
|
-
const errMsgTail = errMsg ? ` \u2014 ${errMsg.replace(/\s+/g, " ").slice(0, 100)}${errMsg.length > 100 ? "\u2026" : ""}` : "";
|
|
6971
|
-
const errChip = errKind ? ` [${errKind}]` : "";
|
|
6972
|
-
const secs = (e.durationMs / 1e3).toFixed(e.durationMs < 1e4 ? 1 : 0);
|
|
6973
|
-
dispatch({
|
|
6974
|
-
type: "addEntry",
|
|
6975
|
-
entry: {
|
|
6976
|
-
kind: "subagent",
|
|
6977
|
-
agentLabel: lbl.label,
|
|
6978
|
-
agentColor: lbl.color,
|
|
6979
|
-
icon,
|
|
6980
|
-
text: `${e.status} (${e.iterations} iter \xB7 ${e.toolCalls} tools \xB7 ${secs}s)${errChip}${errMsgTail}`
|
|
6981
|
-
}
|
|
6982
|
-
});
|
|
6983
|
-
});
|
|
6984
|
-
const offBudgetWarning = events.on("subagent.budget_warning", (e) => {
|
|
6985
|
-
const lbl = labelFor(e.subagentId);
|
|
6986
|
-
dispatch({
|
|
6987
|
-
type: "fleetBudgetWarning",
|
|
6988
|
-
id: e.subagentId,
|
|
6989
|
-
kind: e.kind,
|
|
6990
|
-
used: e.used,
|
|
6991
|
-
limit: e.limit
|
|
6992
|
-
});
|
|
6993
|
-
const timeoutSuffix = e.kind === "timeout" ? " (subagent continues running)" : " \u2014 extending";
|
|
6994
|
-
dispatch({
|
|
6995
|
-
type: "addEntry",
|
|
6996
|
-
entry: {
|
|
6997
|
-
kind: "subagent",
|
|
6998
|
-
agentLabel: lbl.label,
|
|
6999
|
-
agentColor: lbl.color,
|
|
7000
|
-
icon: "\u26A1",
|
|
7001
|
-
text: `hitting ${e.kind} limit (${e.used}/${e.limit})${timeoutSuffix}`
|
|
7002
|
-
}
|
|
7003
|
-
});
|
|
7004
|
-
});
|
|
7005
|
-
const offBudgetExtended = events.on("subagent.budget_extended", (e) => {
|
|
7006
|
-
const lbl = labelFor(e.subagentId);
|
|
7007
|
-
dispatch({
|
|
7008
|
-
type: "fleetBudgetExtended",
|
|
7009
|
-
id: e.subagentId,
|
|
7010
|
-
totalExtensions: e.totalExtensions
|
|
7011
|
-
});
|
|
7012
|
-
dispatch({
|
|
7013
|
-
type: "addEntry",
|
|
7014
|
-
entry: {
|
|
7015
|
-
kind: "subagent",
|
|
7016
|
-
agentLabel: lbl.label,
|
|
7017
|
-
agentColor: lbl.color,
|
|
7018
|
-
icon: "\u26A1",
|
|
7019
|
-
text: `extended ${e.kind} \u2192 ${e.newLimit} (\xD7${e.totalExtensions})`
|
|
7020
|
-
}
|
|
7021
|
-
});
|
|
7022
|
-
});
|
|
7023
|
-
const offIterationSummary = events.on("subagent.iteration_summary", (e) => {
|
|
7024
|
-
const lbl = labelFor(e.subagentId);
|
|
7025
|
-
const costStr = e.costUsd > 0 ? ` \xB7 ${e.costUsd.toFixed(3)}` : "";
|
|
7026
|
-
const toolStr = e.currentTool ? ` \xB7 doing ${e.currentTool}` : "";
|
|
7027
|
-
const partial = e.partialText ? ` \xB7 "${e.partialText.slice(0, 60)}${e.partialText.length > 60 ? "\u2026" : ""}"` : "";
|
|
7028
|
-
dispatch({
|
|
7029
|
-
type: "addEntry",
|
|
7030
|
-
entry: {
|
|
7031
|
-
kind: "subagent",
|
|
7032
|
-
agentLabel: lbl.label,
|
|
7033
|
-
agentColor: lbl.color,
|
|
7034
|
-
icon: "\u{1F4AC}",
|
|
7035
|
-
text: `L${e.iteration} \xB7 ${e.toolCalls} tools${costStr}${toolStr}${partial}`
|
|
7036
|
-
}
|
|
7037
|
-
});
|
|
7038
|
-
});
|
|
7039
|
-
const offCtxPct = events.on("subagent.ctx_pct", (e) => {
|
|
7040
|
-
dispatch({
|
|
7041
|
-
type: "fleetCtxPct",
|
|
7042
|
-
id: e.subagentId,
|
|
7043
|
-
load: e.load,
|
|
7044
|
-
tokens: e.tokens,
|
|
7045
|
-
maxContext: e.maxContext
|
|
7046
|
-
});
|
|
7047
|
-
});
|
|
7048
|
-
const offLeaderCtxPct = events.on("ctx.pct", (e) => {
|
|
7049
|
-
setActiveMaxContext(e.maxContext);
|
|
7050
|
-
dispatch({
|
|
7051
|
-
type: "leaderCtxPct",
|
|
7052
|
-
load: e.load,
|
|
7053
|
-
tokens: e.tokens,
|
|
7054
|
-
maxContext: e.maxContext
|
|
7055
|
-
});
|
|
7056
|
-
});
|
|
7057
|
-
const offLeaderMaxContext = events.on("ctx.max_context", (e) => {
|
|
7058
|
-
if (e.maxContext > 0) setActiveMaxContext(e.maxContext);
|
|
7059
|
-
});
|
|
7060
|
-
const offTool = events.on("subagent.tool_executed", (e) => {
|
|
7061
|
-
dispatch({
|
|
7062
|
-
type: "fleetTool",
|
|
7063
|
-
id: e.subagentId,
|
|
7064
|
-
name: e.name,
|
|
7065
|
-
ok: e.ok,
|
|
7066
|
-
durationMs: e.durationMs,
|
|
7067
|
-
outputBytes: e.outputBytes
|
|
7068
|
-
});
|
|
7069
|
-
dispatch({ type: "fleetToolEnd", id: e.subagentId });
|
|
7070
|
-
});
|
|
7071
|
-
return () => {
|
|
7072
|
-
offSpawned();
|
|
7073
|
-
offStarted();
|
|
7074
|
-
offCompleted();
|
|
7075
|
-
offBudgetWarning();
|
|
7076
|
-
offBudgetExtended();
|
|
7077
|
-
offIterationSummary();
|
|
7078
|
-
offCtxPct();
|
|
7079
|
-
offLeaderCtxPct();
|
|
7080
|
-
offLeaderMaxContext();
|
|
7081
|
-
offTool();
|
|
7082
|
-
};
|
|
7083
|
-
}, [events, director]);
|
|
6631
|
+
useSubagentEvents(events, dispatch, setActiveMaxContext);
|
|
7084
6632
|
useEffect(() => {
|
|
7085
6633
|
const offCheckpoint = events.on("checkpoint.written", (e) => {
|
|
7086
6634
|
dispatch({
|
|
@@ -7105,71 +6653,7 @@ function App({
|
|
|
7105
6653
|
offRewound();
|
|
7106
6654
|
};
|
|
7107
6655
|
}, [events, onClearHistory]);
|
|
7108
|
-
|
|
7109
|
-
const requestSummary = (request) => `${request.source}: ${request.question}`.slice(0, 80);
|
|
7110
|
-
const addBrainEntry = (status, payload) => {
|
|
7111
|
-
const p = payload;
|
|
7112
|
-
const decision = p.decision.optionId ?? p.decision.text ?? p.decision.reason ?? p.decision.prompt ?? p.decision.type;
|
|
7113
|
-
dispatch({
|
|
7114
|
-
type: "brainStatus",
|
|
7115
|
-
state: status,
|
|
7116
|
-
source: p.request.source,
|
|
7117
|
-
risk: p.request.risk,
|
|
7118
|
-
summary: decision
|
|
7119
|
-
});
|
|
7120
|
-
if (status === "ask_human") {
|
|
7121
|
-
dispatch({
|
|
7122
|
-
type: "brainPromptSet",
|
|
7123
|
-
prompt: {
|
|
7124
|
-
requestId: p.request.id,
|
|
7125
|
-
source: p.request.source,
|
|
7126
|
-
risk: p.request.risk,
|
|
7127
|
-
question: p.request.question,
|
|
7128
|
-
context: p.request.context,
|
|
7129
|
-
options: p.request.options
|
|
7130
|
-
}
|
|
7131
|
-
});
|
|
7132
|
-
} else {
|
|
7133
|
-
dispatch({ type: "brainPromptClear" });
|
|
7134
|
-
}
|
|
7135
|
-
dispatch({
|
|
7136
|
-
type: "addEntry",
|
|
7137
|
-
entry: {
|
|
7138
|
-
kind: "brain",
|
|
7139
|
-
status,
|
|
7140
|
-
source: p.request.source,
|
|
7141
|
-
risk: p.request.risk,
|
|
7142
|
-
question: p.request.question,
|
|
7143
|
-
decision,
|
|
7144
|
-
rationale: p.decision.rationale
|
|
7145
|
-
}
|
|
7146
|
-
});
|
|
7147
|
-
};
|
|
7148
|
-
const offRequested = events.on("brain.decision_requested", ({ request }) => {
|
|
7149
|
-
dispatch({
|
|
7150
|
-
type: "brainStatus",
|
|
7151
|
-
state: "deciding",
|
|
7152
|
-
source: request.source,
|
|
7153
|
-
risk: request.risk,
|
|
7154
|
-
summary: requestSummary(request)
|
|
7155
|
-
});
|
|
7156
|
-
});
|
|
7157
|
-
const offAnswered = events.on("brain.decision_answered", (payload) => {
|
|
7158
|
-
addBrainEntry("answered", payload);
|
|
7159
|
-
});
|
|
7160
|
-
const offAskHuman = events.on("brain.decision_ask_human", (payload) => {
|
|
7161
|
-
addBrainEntry("ask_human", payload);
|
|
7162
|
-
});
|
|
7163
|
-
const offDenied = events.on("brain.decision_denied", (payload) => {
|
|
7164
|
-
addBrainEntry("denied", payload);
|
|
7165
|
-
});
|
|
7166
|
-
return () => {
|
|
7167
|
-
offRequested();
|
|
7168
|
-
offAnswered();
|
|
7169
|
-
offAskHuman();
|
|
7170
|
-
offDenied();
|
|
7171
|
-
};
|
|
7172
|
-
}, [events]);
|
|
6656
|
+
useBrainEvents(events, dispatch);
|
|
7173
6657
|
useEffect(() => {
|
|
7174
6658
|
if (!subscribeAutoPhase) return;
|
|
7175
6659
|
const handler = (event, payload) => {
|
|
@@ -7414,7 +6898,7 @@ function App({
|
|
|
7414
6898
|
for (const [id, text] of streamBuf) {
|
|
7415
6899
|
const trimmed = text.trim();
|
|
7416
6900
|
if (!trimmed) continue;
|
|
7417
|
-
const lbl =
|
|
6901
|
+
const lbl = labelFor2(id);
|
|
7418
6902
|
enq({ type: "fleetMessage", id, text: trimmed });
|
|
7419
6903
|
if (streamFleetRef.current) {
|
|
7420
6904
|
enq({
|
|
@@ -7442,7 +6926,7 @@ function App({
|
|
|
7442
6926
|
provider: meta?.provider,
|
|
7443
6927
|
model: meta?.model
|
|
7444
6928
|
});
|
|
7445
|
-
|
|
6929
|
+
labelFor2(s2.id, meta?.name ?? s2.name);
|
|
7446
6930
|
}
|
|
7447
6931
|
dispatch({
|
|
7448
6932
|
type: "fleetCost",
|
|
@@ -7473,7 +6957,7 @@ function App({
|
|
|
7473
6957
|
provider: meta?.provider,
|
|
7474
6958
|
model: meta?.model
|
|
7475
6959
|
});
|
|
7476
|
-
const lbl =
|
|
6960
|
+
const lbl = labelFor2(e.subagentId, meta?.name);
|
|
7477
6961
|
if (streamFleetRef.current) {
|
|
7478
6962
|
const where = meta?.provider && meta?.model ? `${meta.provider}/${meta.model}` : "spawned";
|
|
7479
6963
|
dispatch2({
|
|
@@ -7558,7 +7042,7 @@ function App({
|
|
|
7558
7042
|
});
|
|
7559
7043
|
dispatch2({ type: "fleetToolEnd", id: e.subagentId });
|
|
7560
7044
|
if (streamFleetRef.current && p?.name) {
|
|
7561
|
-
const lbl =
|
|
7045
|
+
const lbl = labelFor2(e.subagentId);
|
|
7562
7046
|
dispatch2({
|
|
7563
7047
|
type: "addEntry",
|
|
7564
7048
|
entry: {
|
|
@@ -7951,6 +7435,9 @@ function App({
|
|
|
7951
7435
|
return;
|
|
7952
7436
|
}
|
|
7953
7437
|
if (isEnter) {
|
|
7438
|
+
const now = Date.now();
|
|
7439
|
+
if (now - lastEnterAtRef.current < 50) return;
|
|
7440
|
+
lastEnterAtRef.current = now;
|
|
7954
7441
|
inputGateRef.current = true;
|
|
7955
7442
|
try {
|
|
7956
7443
|
if (state.modelPicker.step === "provider") {
|
|
@@ -8000,6 +7487,9 @@ function App({
|
|
|
8000
7487
|
return;
|
|
8001
7488
|
}
|
|
8002
7489
|
if (isEnter) {
|
|
7490
|
+
const now = Date.now();
|
|
7491
|
+
if (now - lastEnterAtRef.current < 50) return;
|
|
7492
|
+
lastEnterAtRef.current = now;
|
|
8003
7493
|
const opt = state.autonomyPicker.options[state.autonomyPicker.selected];
|
|
8004
7494
|
if (!opt) return;
|
|
8005
7495
|
const err = switchAutonomy?.(opt.mode);
|
|
@@ -8034,6 +7524,9 @@ function App({
|
|
|
8034
7524
|
return;
|
|
8035
7525
|
}
|
|
8036
7526
|
if (isEnter) {
|
|
7527
|
+
const now = Date.now();
|
|
7528
|
+
if (now - lastEnterAtRef.current < 50) return;
|
|
7529
|
+
lastEnterAtRef.current = now;
|
|
8037
7530
|
const { mode, delayMs } = state.settingsPicker;
|
|
8038
7531
|
const err = await saveSettings?.({ mode, delayMs });
|
|
8039
7532
|
if (err) {
|
|
@@ -8095,6 +7588,9 @@ function App({
|
|
|
8095
7588
|
return;
|
|
8096
7589
|
}
|
|
8097
7590
|
if (isEnter) {
|
|
7591
|
+
const now = Date.now();
|
|
7592
|
+
if (now - lastEnterAtRef.current < 50) return;
|
|
7593
|
+
lastEnterAtRef.current = now;
|
|
8098
7594
|
inputGateRef.current = true;
|
|
8099
7595
|
try {
|
|
8100
7596
|
await acceptPickerSelection();
|
|
@@ -8691,20 +8187,6 @@ User message:
|
|
|
8691
8187
|
})();
|
|
8692
8188
|
}, [initialAsk, initialGoal]);
|
|
8693
8189
|
handleKeyRef.current = handleKey;
|
|
8694
|
-
openModelPickerRef.current = () => {
|
|
8695
|
-
void openModelPicker();
|
|
8696
|
-
};
|
|
8697
|
-
openAutonomyPickerRef.current = openAutonomyPicker;
|
|
8698
|
-
handleRewindToRef.current = (promptIndex) => {
|
|
8699
|
-
void handleRewindTo(promptIndex);
|
|
8700
|
-
};
|
|
8701
|
-
statusChipRef.current = {
|
|
8702
|
-
version: appVersion,
|
|
8703
|
-
model: `${liveProvider}/${liveModel}`,
|
|
8704
|
-
fleetRunning: fleetCounts?.running ?? 0,
|
|
8705
|
-
yolo: yoloLive,
|
|
8706
|
-
autonomy: autonomyLive
|
|
8707
|
-
};
|
|
8708
8190
|
const inputHint = useMemo(() => {
|
|
8709
8191
|
if (state.status !== "idle") return "";
|
|
8710
8192
|
if (state.buffer.startsWith("/")) return "slash command \u2014 Enter to dispatch";
|
|
@@ -8734,20 +8216,18 @@ User message:
|
|
|
8734
8216
|
),
|
|
8735
8217
|
affordanceShown ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \u2193 ${state.pendingNewLines} new line${state.pendingNewLines === 1 ? "" : "s"} \u2014 PgDn or click to jump to bottom` }) : null,
|
|
8736
8218
|
/* @__PURE__ */ jsxs(Box, { ref: managedLive ? bottomRef : void 0, flexDirection: "column", flexShrink: 0, children: [
|
|
8737
|
-
/* @__PURE__ */
|
|
8738
|
-
|
|
8739
|
-
|
|
8740
|
-
|
|
8741
|
-
|
|
8742
|
-
|
|
8743
|
-
|
|
8744
|
-
|
|
8745
|
-
|
|
8746
|
-
|
|
8747
|
-
|
|
8748
|
-
|
|
8749
|
-
)
|
|
8750
|
-
] }),
|
|
8219
|
+
/* @__PURE__ */ jsx(LiveActivityStrip, { entries: state.fleet, nowTick }),
|
|
8220
|
+
/* @__PURE__ */ jsx(
|
|
8221
|
+
Input,
|
|
8222
|
+
{
|
|
8223
|
+
prompt: INPUT_PROMPT,
|
|
8224
|
+
value: state.buffer,
|
|
8225
|
+
cursor: state.cursor,
|
|
8226
|
+
disabled: state.status === "aborting" && !state.steeringPending || state.confirmQueue.length > 0,
|
|
8227
|
+
hint: inputHint,
|
|
8228
|
+
onKey: handleKey
|
|
8229
|
+
}
|
|
8230
|
+
),
|
|
8751
8231
|
state.picker.open ? /* @__PURE__ */ jsx(
|
|
8752
8232
|
FilePicker,
|
|
8753
8233
|
{
|
|
@@ -8823,8 +8303,7 @@ User message:
|
|
|
8823
8303
|
head.resolve(decision);
|
|
8824
8304
|
dispatch({ type: "confirmClose" });
|
|
8825
8305
|
};
|
|
8826
|
-
|
|
8827
|
-
return /* @__PURE__ */ jsx(Box, { ref: confirmRef, flexDirection: "column", marginY: 1, flexShrink: 0, children: /* @__PURE__ */ jsx(
|
|
8306
|
+
return /* @__PURE__ */ jsx(
|
|
8828
8307
|
ConfirmPrompt,
|
|
8829
8308
|
{
|
|
8830
8309
|
toolName: head.toolName,
|
|
@@ -8832,7 +8311,7 @@ User message:
|
|
|
8832
8311
|
suggestedPattern: head.suggestedPattern,
|
|
8833
8312
|
onDecision
|
|
8834
8313
|
}
|
|
8835
|
-
)
|
|
8314
|
+
);
|
|
8836
8315
|
})(),
|
|
8837
8316
|
/* @__PURE__ */ jsx(
|
|
8838
8317
|
StatusBar,
|
|
@@ -8867,12 +8346,11 @@ User message:
|
|
|
8867
8346
|
confirm: state.confirmQueue.length > 0,
|
|
8868
8347
|
picker: state.picker.open || state.slashPicker.open || state.modelPicker.open || state.autonomyPicker.open || state.settingsPicker.open || !!state.rewindOverlay,
|
|
8869
8348
|
monitor: state.agentsMonitorOpen || state.monitorOpen || state.worktreeMonitorOpen || !!state.autoPhase?.monitorOpen,
|
|
8870
|
-
managed: managedLive
|
|
8871
|
-
mouse: mouseLive
|
|
8349
|
+
managed: managedLive
|
|
8872
8350
|
}
|
|
8873
8351
|
}
|
|
8874
8352
|
) : null,
|
|
8875
|
-
state.helpOpen ? /* @__PURE__ */ jsx(HelpOverlay, { managed: managedLive
|
|
8353
|
+
state.helpOpen ? /* @__PURE__ */ jsx(HelpOverlay, { managed: managedLive }) : null,
|
|
8876
8354
|
state.agentsMonitorOpen ? /* @__PURE__ */ jsx(
|
|
8877
8355
|
AgentsMonitor,
|
|
8878
8356
|
{
|
|
@@ -8904,6 +8382,7 @@ User message:
|
|
|
8904
8382
|
entries: state.fleet,
|
|
8905
8383
|
totalCost: state.fleetCost,
|
|
8906
8384
|
totalTokens: state.fleetTokens,
|
|
8385
|
+
maxConcurrent: state.fleetConcurrency,
|
|
8907
8386
|
nowTick,
|
|
8908
8387
|
collabSession: state.collabSession
|
|
8909
8388
|
}
|
|
@@ -8960,48 +8439,6 @@ function fmtTok3(n) {
|
|
|
8960
8439
|
return `${(n / 1e6).toFixed(1)}M`;
|
|
8961
8440
|
}
|
|
8962
8441
|
|
|
8963
|
-
// src/mouse.ts
|
|
8964
|
-
var SGR_MOUSE_RE = /\x1b?\[<(\d+);(\d+);(\d+)([Mm])/g;
|
|
8965
|
-
function stripSgrMouse(s2) {
|
|
8966
|
-
return s2.replace(SGR_MOUSE_RE, "");
|
|
8967
|
-
}
|
|
8968
|
-
function parseSgrMouse(s2) {
|
|
8969
|
-
const events = [];
|
|
8970
|
-
for (const m of s2.matchAll(SGR_MOUSE_RE)) {
|
|
8971
|
-
const cb = Number.parseInt(m[1] ?? "", 10);
|
|
8972
|
-
const x = Number.parseInt(m[2] ?? "", 10);
|
|
8973
|
-
const y = Number.parseInt(m[3] ?? "", 10);
|
|
8974
|
-
const final = m[4];
|
|
8975
|
-
if (!Number.isFinite(cb) || !Number.isFinite(x) || !Number.isFinite(y)) continue;
|
|
8976
|
-
const isWheel = (cb & 64) !== 0;
|
|
8977
|
-
const drag = (cb & 32) !== 0;
|
|
8978
|
-
const low = cb & 3;
|
|
8979
|
-
let button;
|
|
8980
|
-
let type;
|
|
8981
|
-
if (isWheel) {
|
|
8982
|
-
type = "wheel";
|
|
8983
|
-
button = low === 0 ? "wheelUp" : low === 1 ? "wheelDown" : "other";
|
|
8984
|
-
} else if (final === "m") {
|
|
8985
|
-
type = "release";
|
|
8986
|
-
button = low === 0 ? "left" : low === 1 ? "middle" : low === 2 ? "right" : "other";
|
|
8987
|
-
} else {
|
|
8988
|
-
type = "press";
|
|
8989
|
-
button = low === 0 ? "left" : low === 1 ? "middle" : low === 2 ? "right" : "other";
|
|
8990
|
-
}
|
|
8991
|
-
events.push({
|
|
8992
|
-
type,
|
|
8993
|
-
button,
|
|
8994
|
-
x,
|
|
8995
|
-
y,
|
|
8996
|
-
shift: (cb & 4) !== 0,
|
|
8997
|
-
alt: (cb & 8) !== 0,
|
|
8998
|
-
ctrl: (cb & 16) !== 0,
|
|
8999
|
-
drag
|
|
9000
|
-
});
|
|
9001
|
-
}
|
|
9002
|
-
return events;
|
|
9003
|
-
}
|
|
9004
|
-
|
|
9005
8442
|
// src/terminal-title.ts
|
|
9006
8443
|
var SPINNER = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
9007
8444
|
var setTitle = (s2) => `\x1B]0;${s2}\x07`;
|
|
@@ -9071,8 +8508,6 @@ var BRACKETED_PASTE_OFF = "\x1B[?2004l";
|
|
|
9071
8508
|
var ALT_SCREEN_ON = "\x1B[?1049h";
|
|
9072
8509
|
var ALT_SCREEN_OFF = "\x1B[?1049l";
|
|
9073
8510
|
var CURSOR_HOME = "\x1B[H";
|
|
9074
|
-
var MOUSE_ON = "\x1B[?1000h\x1B[?1002h\x1B[?1006h";
|
|
9075
|
-
var MOUSE_OFF = "\x1B[?1006l\x1B[?1002l\x1B[?1000l";
|
|
9076
8511
|
async function runTui(opts) {
|
|
9077
8512
|
const stdout = process.stdout;
|
|
9078
8513
|
const stdin = process.stdin;
|
|
@@ -9082,107 +8517,13 @@ async function runTui(opts) {
|
|
|
9082
8517
|
);
|
|
9083
8518
|
return 2;
|
|
9084
8519
|
}
|
|
9085
|
-
const
|
|
9086
|
-
const useAltScreen = opts.altScreen === true || useMouse;
|
|
8520
|
+
const useAltScreen = opts.altScreen === true;
|
|
9087
8521
|
if (useAltScreen) {
|
|
9088
8522
|
stdout.write(ALT_SCREEN_ON);
|
|
9089
8523
|
stdout.write(CURSOR_HOME);
|
|
9090
8524
|
}
|
|
9091
8525
|
stdout.write(BRACKETED_PASTE_ON);
|
|
9092
|
-
|
|
9093
|
-
stdout.write(MOUSE_ON);
|
|
9094
|
-
}
|
|
9095
|
-
const mouseListeners = /* @__PURE__ */ new Set();
|
|
9096
|
-
let inkStdin = stdin;
|
|
9097
|
-
let detachMouse = null;
|
|
9098
|
-
if (useMouse) {
|
|
9099
|
-
class KeyboardReadable extends Readable {
|
|
9100
|
-
pendingChunks = [];
|
|
9101
|
-
// eslint-disable-next-line no-useless-constructor
|
|
9102
|
-
constructor() {
|
|
9103
|
-
super({ encoding: "utf8", highWaterMark: 64 * 1024 });
|
|
9104
|
-
}
|
|
9105
|
-
_read(_size) {
|
|
9106
|
-
this.flushPending();
|
|
9107
|
-
}
|
|
9108
|
-
flushPending() {
|
|
9109
|
-
while (this.pendingChunks.length > 0) {
|
|
9110
|
-
const chunk = this.pendingChunks[0];
|
|
9111
|
-
const ok = this.push(chunk);
|
|
9112
|
-
this.pendingChunks.shift();
|
|
9113
|
-
if (!ok) {
|
|
9114
|
-
break;
|
|
9115
|
-
}
|
|
9116
|
-
}
|
|
9117
|
-
}
|
|
9118
|
-
/** Called by the stdin data handler when keyboard bytes are available. */
|
|
9119
|
-
doPush(chunk) {
|
|
9120
|
-
if (chunk.length === 0) return;
|
|
9121
|
-
const ok = this.push(chunk);
|
|
9122
|
-
if (ok) {
|
|
9123
|
-
if (this.pendingChunks.length > 0) {
|
|
9124
|
-
this.flushPending();
|
|
9125
|
-
}
|
|
9126
|
-
} else {
|
|
9127
|
-
if (this.pendingChunks.length >= 100) {
|
|
9128
|
-
this.pendingChunks.shift();
|
|
9129
|
-
}
|
|
9130
|
-
this.pendingChunks.push(chunk);
|
|
9131
|
-
}
|
|
9132
|
-
}
|
|
9133
|
-
/** Called on shutdown so the stream closes cleanly. */
|
|
9134
|
-
doEnd() {
|
|
9135
|
-
this.pendingChunks = [];
|
|
9136
|
-
this.push(null);
|
|
9137
|
-
}
|
|
9138
|
-
}
|
|
9139
|
-
const keyboardStream = new KeyboardReadable();
|
|
9140
|
-
const p = keyboardStream;
|
|
9141
|
-
p.isTTY = true;
|
|
9142
|
-
p.setRawMode = (mode) => {
|
|
9143
|
-
try {
|
|
9144
|
-
stdin.setRawMode?.(mode);
|
|
9145
|
-
} catch {
|
|
9146
|
-
}
|
|
9147
|
-
return p;
|
|
9148
|
-
};
|
|
9149
|
-
const realRef = stdin.ref?.bind(stdin);
|
|
9150
|
-
const realUnref = stdin.unref?.bind(stdin);
|
|
9151
|
-
p.ref = () => {
|
|
9152
|
-
realRef?.();
|
|
9153
|
-
return p;
|
|
9154
|
-
};
|
|
9155
|
-
p.unref = () => {
|
|
9156
|
-
realUnref?.();
|
|
9157
|
-
return p;
|
|
9158
|
-
};
|
|
9159
|
-
stdin.setEncoding("utf8");
|
|
9160
|
-
const onData = (chunk) => {
|
|
9161
|
-
const evs = parseSgrMouse(chunk);
|
|
9162
|
-
for (const ev of evs) {
|
|
9163
|
-
for (const fn of mouseListeners) {
|
|
9164
|
-
try {
|
|
9165
|
-
fn(ev);
|
|
9166
|
-
} catch {
|
|
9167
|
-
}
|
|
9168
|
-
}
|
|
9169
|
-
}
|
|
9170
|
-
const rest = stripSgrMouse(chunk);
|
|
9171
|
-
keyboardStream.doPush(rest);
|
|
9172
|
-
};
|
|
9173
|
-
stdin.on("data", onData);
|
|
9174
|
-
detachMouse = () => {
|
|
9175
|
-
stdin.off("data", onData);
|
|
9176
|
-
keyboardStream.doEnd();
|
|
9177
|
-
};
|
|
9178
|
-
inkStdin = p;
|
|
9179
|
-
}
|
|
9180
|
-
const subscribeMouse = useMouse ? (fn) => {
|
|
9181
|
-
mouseListeners.add(fn);
|
|
9182
|
-
return () => {
|
|
9183
|
-
mouseListeners.delete(fn);
|
|
9184
|
-
};
|
|
9185
|
-
} : void 0;
|
|
8526
|
+
const inkStdin = stdin;
|
|
9186
8527
|
const stopTitle = startTerminalTitle({ stdout, events: opts.events, model: opts.model });
|
|
9187
8528
|
const swallowSignals = ["SIGTSTP", "SIGQUIT", "SIGTTIN", "SIGTTOU"];
|
|
9188
8529
|
const swallow = () => {
|
|
@@ -9201,15 +8542,8 @@ async function runTui(opts) {
|
|
|
9201
8542
|
stopTitle();
|
|
9202
8543
|
} catch {
|
|
9203
8544
|
}
|
|
9204
|
-
try {
|
|
9205
|
-
detachMouse?.();
|
|
9206
|
-
} catch {
|
|
9207
|
-
}
|
|
9208
8545
|
try {
|
|
9209
8546
|
stdout.write(BRACKETED_PASTE_OFF);
|
|
9210
|
-
if (useMouse) {
|
|
9211
|
-
stdout.write(MOUSE_OFF);
|
|
9212
|
-
}
|
|
9213
8547
|
if (useAltScreen) {
|
|
9214
8548
|
stdout.write(ALT_SCREEN_OFF);
|
|
9215
8549
|
}
|
|
@@ -9250,7 +8584,7 @@ async function runTui(opts) {
|
|
|
9250
8584
|
let instance;
|
|
9251
8585
|
try {
|
|
9252
8586
|
instance = render(
|
|
9253
|
-
|
|
8587
|
+
React5.createElement(App, {
|
|
9254
8588
|
agent: opts.agent,
|
|
9255
8589
|
slashRegistry: opts.slashRegistry,
|
|
9256
8590
|
attachments: opts.attachments,
|
|
@@ -9294,11 +8628,8 @@ async function runTui(opts) {
|
|
|
9294
8628
|
getSettings: opts.getSettings,
|
|
9295
8629
|
saveSettings: opts.saveSettings,
|
|
9296
8630
|
predictNext: opts.predictNext,
|
|
9297
|
-
mouse: useMouse,
|
|
9298
|
-
subscribeMouse,
|
|
9299
8631
|
// Managed viewport (in-app scroll + collapsibility) follows
|
|
9300
|
-
// alt-screen: it owns the screen, so there's no native-scrollback
|
|
9301
|
-
// leak. Decoupled from mouse so --alt-screen alone gets it.
|
|
8632
|
+
// alt-screen: it owns the screen, so there's no native-scrollback leak.
|
|
9302
8633
|
managed: useAltScreen
|
|
9303
8634
|
}),
|
|
9304
8635
|
{ exitOnCtrlC: false, stdin: inkStdin }
|