@wrongstack/tui 0.68.0 → 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 +91 -616
- 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: [
|
|
@@ -2771,7 +2730,7 @@ function formatToolOutput(toolName, output, ok, _outputBytes, outputLines) {
|
|
|
2771
2730
|
}
|
|
2772
2731
|
var MAX_STREAM_DISPLAY_CHARS = 480;
|
|
2773
2732
|
var MAX_STREAM_LINES = 8;
|
|
2774
|
-
var ToolStreamBox =
|
|
2733
|
+
var ToolStreamBox = React5.memo(function ToolStreamBox2({
|
|
2775
2734
|
name,
|
|
2776
2735
|
text,
|
|
2777
2736
|
startedAt,
|
|
@@ -3091,7 +3050,7 @@ function brainRiskColor(risk) {
|
|
|
3091
3050
|
function assistantContentWidth(termWidth) {
|
|
3092
3051
|
return Math.max(20, termWidth - MESSAGE_PANEL_CHROME_WIDTH);
|
|
3093
3052
|
}
|
|
3094
|
-
var Entry =
|
|
3053
|
+
var Entry = React5.memo(function Entry2({
|
|
3095
3054
|
entry,
|
|
3096
3055
|
termWidth
|
|
3097
3056
|
}) {
|
|
@@ -3435,52 +3394,6 @@ function layoutInputRows(prompt, value, cursor, width) {
|
|
|
3435
3394
|
if (row.length > 0 || rows.length === 0) rows.push(row);
|
|
3436
3395
|
return rows;
|
|
3437
3396
|
}
|
|
3438
|
-
function inputIndexAtRowCol(prompt, value, width, row, col) {
|
|
3439
|
-
const w = Math.max(1, Math.floor(width));
|
|
3440
|
-
const flat = [];
|
|
3441
|
-
for (let i = 0; i < prompt.length; i++) flat.push({ ch: prompt[i], buf: -1 });
|
|
3442
|
-
for (let i = 0; i < value.length; i++) flat.push({ ch: value[i], buf: i });
|
|
3443
|
-
const rows = [];
|
|
3444
|
-
const starts = [];
|
|
3445
|
-
let cur = [];
|
|
3446
|
-
let curStart = 0;
|
|
3447
|
-
for (const cell of flat) {
|
|
3448
|
-
if (cell.ch === "\n") {
|
|
3449
|
-
rows.push(cur);
|
|
3450
|
-
starts.push(curStart);
|
|
3451
|
-
cur = [];
|
|
3452
|
-
curStart = cell.buf + 1;
|
|
3453
|
-
continue;
|
|
3454
|
-
}
|
|
3455
|
-
if (cur.length === 0 && cell.buf >= 0) curStart = cell.buf;
|
|
3456
|
-
cur.push({ buf: cell.buf });
|
|
3457
|
-
if (cur.length >= w) {
|
|
3458
|
-
rows.push(cur);
|
|
3459
|
-
starts.push(curStart);
|
|
3460
|
-
cur = [];
|
|
3461
|
-
curStart = -1;
|
|
3462
|
-
}
|
|
3463
|
-
}
|
|
3464
|
-
if (cur.length > 0 || rows.length === 0) {
|
|
3465
|
-
rows.push(cur);
|
|
3466
|
-
starts.push(curStart);
|
|
3467
|
-
}
|
|
3468
|
-
const clamp = (n) => Math.max(0, Math.min(value.length, n));
|
|
3469
|
-
if (row < 0) return 0;
|
|
3470
|
-
if (row >= rows.length) return value.length;
|
|
3471
|
-
const r = rows[row];
|
|
3472
|
-
const c = Math.max(0, col);
|
|
3473
|
-
if (c < r.length) {
|
|
3474
|
-
const b = r[c].buf;
|
|
3475
|
-
return b < 0 ? 0 : clamp(b);
|
|
3476
|
-
}
|
|
3477
|
-
for (let k = r.length - 1; k >= 0; k--) {
|
|
3478
|
-
const b = r[k].buf;
|
|
3479
|
-
if (b >= 0) return clamp(b + 1);
|
|
3480
|
-
}
|
|
3481
|
-
const start = starts[row];
|
|
3482
|
-
return clamp(start !== void 0 && start >= 0 ? start : value.length);
|
|
3483
|
-
}
|
|
3484
3397
|
function renderRow2(cells, rowKey, promptColor) {
|
|
3485
3398
|
const out = [];
|
|
3486
3399
|
let run = "";
|
|
@@ -3620,8 +3533,7 @@ function hintsFor(ctx) {
|
|
|
3620
3533
|
return [
|
|
3621
3534
|
{ key: "\u2191\u2193", label: "move" },
|
|
3622
3535
|
{ key: "\u21B5", label: "select" },
|
|
3623
|
-
{ key: "Esc", label: "cancel" }
|
|
3624
|
-
...ctx.mouse ? [{ key: "click", label: "pick" }] : []
|
|
3536
|
+
{ key: "Esc", label: "cancel" }
|
|
3625
3537
|
];
|
|
3626
3538
|
}
|
|
3627
3539
|
if (ctx.monitor) {
|
|
@@ -3634,7 +3546,6 @@ function hintsFor(ctx) {
|
|
|
3634
3546
|
}
|
|
3635
3547
|
const base = [{ key: "?", label: "help" }];
|
|
3636
3548
|
if (ctx.managed) base.push({ key: "PgUp/PgDn", label: "scroll" });
|
|
3637
|
-
if (ctx.mouse) base.push({ key: "wheel", label: "scroll" }, { key: "click", label: "select" });
|
|
3638
3549
|
base.push({ key: "^G", label: "agents" }, { key: "^C", label: "stop" });
|
|
3639
3550
|
return base;
|
|
3640
3551
|
}
|
|
@@ -3674,7 +3585,7 @@ function fmtRecentMessage(message) {
|
|
|
3674
3585
|
const text = message.text.replace(/\s+/g, " ");
|
|
3675
3586
|
return text.length > 48 ? `${text.slice(0, 47)}...` : text;
|
|
3676
3587
|
}
|
|
3677
|
-
var LiveActivityStrip =
|
|
3588
|
+
var LiveActivityStrip = React5.memo(function LiveActivityStrip2({
|
|
3678
3589
|
entries,
|
|
3679
3590
|
nowTick,
|
|
3680
3591
|
maxRows = 4
|
|
@@ -4000,13 +3911,6 @@ function scrollbarThumb(rows, offset, total) {
|
|
|
4000
3911
|
const top = Math.max(0, Math.min(rawTop, rows - size));
|
|
4001
3912
|
return { top, size, scrollable: true };
|
|
4002
3913
|
}
|
|
4003
|
-
function scrollOffsetForTrackRow(rows, total, cell) {
|
|
4004
|
-
if (total <= rows) return 0;
|
|
4005
|
-
const maxOffset = total - rows;
|
|
4006
|
-
const clampedCell = Math.max(0, Math.min(rows - 1, cell));
|
|
4007
|
-
const windowTop = Math.round(clampedCell / Math.max(1, rows - 1) * maxOffset);
|
|
4008
|
-
return Math.max(0, Math.min(maxOffset, maxOffset - windowTop));
|
|
4009
|
-
}
|
|
4010
3914
|
function Scrollbar({
|
|
4011
3915
|
rows,
|
|
4012
3916
|
offset,
|
|
@@ -4506,7 +4410,11 @@ function labelFor(labelsRef, id, name) {
|
|
|
4506
4410
|
}
|
|
4507
4411
|
function useSubagentEvents(events, dispatch, setActiveMaxContext) {
|
|
4508
4412
|
const labelsRef = useRef(/* @__PURE__ */ new Map());
|
|
4509
|
-
const lbl = (
|
|
4413
|
+
const lbl = useCallback(
|
|
4414
|
+
(id, name) => labelFor(labelsRef, id, name),
|
|
4415
|
+
[]
|
|
4416
|
+
// labelsRef is a stable ref
|
|
4417
|
+
);
|
|
4510
4418
|
useEffect(() => {
|
|
4511
4419
|
const offSpawned = events.on("subagent.spawned", (e) => {
|
|
4512
4420
|
const l = lbl(e.subagentId, e.name);
|
|
@@ -4553,6 +4461,12 @@ function useSubagentEvents(events, dispatch, setActiveMaxContext) {
|
|
|
4553
4461
|
const offCtxPct = events.on("subagent.ctx_pct", (e) => {
|
|
4554
4462
|
dispatch({ type: "fleetCtxPct", id: e.subagentId, load: e.load, tokens: e.tokens, maxContext: e.maxContext });
|
|
4555
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
|
+
});
|
|
4556
4470
|
const offLeaderCtxPct = events.on("ctx.pct", (e) => {
|
|
4557
4471
|
setActiveMaxContext(e.maxContext);
|
|
4558
4472
|
dispatch({ type: "leaderCtxPct", load: e.load, tokens: e.tokens, maxContext: e.maxContext });
|
|
@@ -4572,11 +4486,12 @@ function useSubagentEvents(events, dispatch, setActiveMaxContext) {
|
|
|
4572
4486
|
offBudgetExtended();
|
|
4573
4487
|
offIterationSummary();
|
|
4574
4488
|
offCtxPct();
|
|
4489
|
+
offConcurrencyChanged();
|
|
4575
4490
|
offLeaderCtxPct();
|
|
4576
4491
|
offLeaderMaxContext();
|
|
4577
4492
|
offTool();
|
|
4578
4493
|
};
|
|
4579
|
-
}, [events, dispatch, setActiveMaxContext]);
|
|
4494
|
+
}, [events, dispatch, setActiveMaxContext, lbl]);
|
|
4580
4495
|
}
|
|
4581
4496
|
function useBrainEvents(events, dispatch) {
|
|
4582
4497
|
useEffect(() => {
|
|
@@ -4826,6 +4741,42 @@ function oneLine(s2, max) {
|
|
|
4826
4741
|
return collapsed.length <= max ? collapsed : `${collapsed.slice(0, max - 1)}\u2026`;
|
|
4827
4742
|
}
|
|
4828
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");
|
|
4778
|
+
}
|
|
4779
|
+
|
|
4829
4780
|
// src/app-reducer.ts
|
|
4830
4781
|
function reducer(state, action) {
|
|
4831
4782
|
switch (action.type) {
|
|
@@ -5420,6 +5371,9 @@ function reducer(state, action) {
|
|
|
5420
5371
|
} : state.fleetTokens
|
|
5421
5372
|
};
|
|
5422
5373
|
}
|
|
5374
|
+
case "fleetConcurrency": {
|
|
5375
|
+
return { ...state, fleetConcurrency: action.n };
|
|
5376
|
+
}
|
|
5423
5377
|
case "leaderIterStart": {
|
|
5424
5378
|
return {
|
|
5425
5379
|
...state,
|
|
@@ -5608,7 +5562,7 @@ function reducer(state, action) {
|
|
|
5608
5562
|
case "worktreeMonitorToggle": {
|
|
5609
5563
|
return { ...state, worktreeMonitorOpen: !state.worktreeMonitorOpen };
|
|
5610
5564
|
}
|
|
5611
|
-
// --- In-app chat scroll
|
|
5565
|
+
// --- In-app chat scroll ---
|
|
5612
5566
|
case "scrollBy": {
|
|
5613
5567
|
const maxOffset = Math.max(0, state.totalLines - state.viewportRows);
|
|
5614
5568
|
const next = Math.max(0, Math.min(maxOffset, state.scrollOffset + action.delta));
|
|
@@ -5786,7 +5740,6 @@ function reducer(state, action) {
|
|
|
5786
5740
|
}
|
|
5787
5741
|
}
|
|
5788
5742
|
}
|
|
5789
|
-
var WHEEL_STEP = 3;
|
|
5790
5743
|
var MIN_VIEWPORT = 3;
|
|
5791
5744
|
var INPUT_PROMPT = "\u203A ";
|
|
5792
5745
|
function selectedSlashCommandLine(picker) {
|
|
@@ -5795,40 +5748,6 @@ function selectedSlashCommandLine(picker) {
|
|
|
5795
5748
|
return picked ? `/${picked.name}` : null;
|
|
5796
5749
|
}
|
|
5797
5750
|
var PASTE_THRESHOLD_CHARS = 200;
|
|
5798
|
-
function buildSteeringPreamble(snapshot, newDirection) {
|
|
5799
|
-
const lines = ["[STEERING \u2014 I pressed Esc to interrupt you mid-task on purpose.", ""];
|
|
5800
|
-
const ctx = [];
|
|
5801
|
-
if (snapshot?.runningTools && snapshot.runningTools.length > 0) {
|
|
5802
|
-
ctx.push(`- in-flight tools (now cancelled): ${snapshot.runningTools.join(", ")}`);
|
|
5803
|
-
}
|
|
5804
|
-
if (snapshot?.subagentsTerminated && snapshot.subagentsTerminated > 0) {
|
|
5805
|
-
const subDetails = snapshot.subagents.map((s2) => `${s2.label}${s2.tool ? ` (was running: ${s2.tool})` : ""}`).join(", ");
|
|
5806
|
-
ctx.push(
|
|
5807
|
-
`- subagents (${snapshot.subagentsTerminated} terminated by me, do NOT await them): ${subDetails}`
|
|
5808
|
-
);
|
|
5809
|
-
}
|
|
5810
|
-
if (snapshot?.partialAssistantText && snapshot.partialAssistantText.trim().length > 0) {
|
|
5811
|
-
const tail = snapshot.partialAssistantText.trim().slice(-300);
|
|
5812
|
-
ctx.push(`- your last partial output (truncated, for context only): "${tail}"`);
|
|
5813
|
-
}
|
|
5814
|
-
if (ctx.length > 0) {
|
|
5815
|
-
lines.push("What was happening when I cut you off:");
|
|
5816
|
-
lines.push(...ctx);
|
|
5817
|
-
lines.push("");
|
|
5818
|
-
}
|
|
5819
|
-
lines.push("You have authority to:");
|
|
5820
|
-
lines.push("- Abandon the prior plan entirely if the new direction makes it stale.");
|
|
5821
|
-
lines.push("- Re-spawn fresh subagents (with different roles or tasks) if needed.");
|
|
5822
|
-
lines.push('- Skip a polite "should I continue?" \u2014 just pivot.');
|
|
5823
|
-
lines.push("- Ask me to clarify if the new direction is genuinely ambiguous.");
|
|
5824
|
-
lines.push("");
|
|
5825
|
-
lines.push("New direction:");
|
|
5826
|
-
lines.push("---");
|
|
5827
|
-
lines.push(newDirection);
|
|
5828
|
-
lines.push("---");
|
|
5829
|
-
lines.push("]");
|
|
5830
|
-
return lines.join("\n");
|
|
5831
|
-
}
|
|
5832
5751
|
function App({
|
|
5833
5752
|
agent,
|
|
5834
5753
|
slashRegistry,
|
|
@@ -5872,8 +5791,6 @@ function App({
|
|
|
5872
5791
|
initialGoal,
|
|
5873
5792
|
initialAsk,
|
|
5874
5793
|
sessionsDir,
|
|
5875
|
-
mouse = false,
|
|
5876
|
-
subscribeMouse,
|
|
5877
5794
|
managed = false
|
|
5878
5795
|
}) {
|
|
5879
5796
|
const { exit } = useApp();
|
|
@@ -5885,18 +5802,15 @@ function App({
|
|
|
5885
5802
|
const [hiddenItems, setHiddenItems] = useState(statuslineHiddenItems);
|
|
5886
5803
|
const { stdout } = useStdout();
|
|
5887
5804
|
const [termRows, setTermRows] = useState(stdout?.rows ?? 24);
|
|
5888
|
-
const [termCols, setTermCols] = useState(stdout?.columns ?? 80);
|
|
5889
5805
|
useEffect(() => {
|
|
5890
5806
|
const onResize = () => {
|
|
5891
5807
|
setTermRows(process.stdout.rows ?? 24);
|
|
5892
|
-
setTermCols(process.stdout.columns ?? 80);
|
|
5893
5808
|
};
|
|
5894
5809
|
process.stdout.on("resize", onResize);
|
|
5895
5810
|
return () => {
|
|
5896
5811
|
process.stdout.off("resize", onResize);
|
|
5897
5812
|
};
|
|
5898
5813
|
}, []);
|
|
5899
|
-
const [mouseLive, setMouseLive] = useState(mouse);
|
|
5900
5814
|
const [managedLive, setManagedLive] = useState(managed);
|
|
5901
5815
|
useEffect(() => {
|
|
5902
5816
|
setHiddenItems(statuslineHiddenItems);
|
|
@@ -5983,6 +5897,7 @@ function App({
|
|
|
5983
5897
|
},
|
|
5984
5898
|
fleetCost: 0,
|
|
5985
5899
|
fleetTokens: { input: 0, output: 0 },
|
|
5900
|
+
fleetConcurrency: 4,
|
|
5986
5901
|
streamFleet: true,
|
|
5987
5902
|
monitorOpen: false,
|
|
5988
5903
|
agentsMonitorOpen: false,
|
|
@@ -6011,7 +5926,7 @@ function App({
|
|
|
6011
5926
|
const inputGateRef = useRef(false);
|
|
6012
5927
|
const lastEnterAtRef = useRef(0);
|
|
6013
5928
|
const tokenPreviewsRef = useRef(/* @__PURE__ */ new Map());
|
|
6014
|
-
const projectName =
|
|
5929
|
+
const projectName = React5.useMemo(() => {
|
|
6015
5930
|
const base = path2.basename(projectRoot);
|
|
6016
5931
|
return base && base !== path2.sep ? base : void 0;
|
|
6017
5932
|
}, [projectRoot]);
|
|
@@ -6023,21 +5938,11 @@ function App({
|
|
|
6023
5938
|
const draftRef = useRef({ buffer: state.buffer, cursor: state.cursor });
|
|
6024
5939
|
draftRef.current = { buffer: state.buffer, cursor: state.cursor };
|
|
6025
5940
|
const bottomRef = useRef(null);
|
|
6026
|
-
|
|
6027
|
-
const preRowsRef = useRef(0);
|
|
6028
|
-
const liveStripRef = useRef(null);
|
|
6029
|
-
const liveStripRowsRef = useRef(0);
|
|
6030
|
-
React6.useLayoutEffect(() => {
|
|
5941
|
+
React5.useLayoutEffect(() => {
|
|
6031
5942
|
if (!managedLive) return;
|
|
6032
5943
|
const node = bottomRef.current;
|
|
6033
5944
|
if (!node) return;
|
|
6034
5945
|
const { height } = measureElement(node);
|
|
6035
|
-
if (prePickerRef.current) {
|
|
6036
|
-
preRowsRef.current = measureElement(prePickerRef.current).height;
|
|
6037
|
-
}
|
|
6038
|
-
if (liveStripRef.current) {
|
|
6039
|
-
liveStripRowsRef.current = measureElement(liveStripRef.current).height;
|
|
6040
|
-
}
|
|
6041
5946
|
const s2 = stateRef.current;
|
|
6042
5947
|
const affordance = s2.scrollOffset > 0 && s2.pendingNewLines > 0 ? 1 : 0;
|
|
6043
5948
|
const vp = Math.max(MIN_VIEWPORT, termRows - height - affordance - 1);
|
|
@@ -6046,214 +5951,7 @@ function App({
|
|
|
6046
5951
|
}
|
|
6047
5952
|
}, [managedLive, termRows]);
|
|
6048
5953
|
const handleKeyRef = useRef(null);
|
|
6049
|
-
const
|
|
6050
|
-
const openModelPickerRef = useRef(null);
|
|
6051
|
-
const openAutonomyPickerRef = useRef(null);
|
|
6052
|
-
const handleRewindToRef = useRef(null);
|
|
6053
|
-
const confirmRef = useRef(null);
|
|
6054
|
-
const confirmDecisionRef = useRef(null);
|
|
6055
|
-
const scrollbarDragRef = useRef(false);
|
|
6056
|
-
const lastLeftClickRef = useRef(null);
|
|
6057
|
-
const DOUBLE_CLICK_MS = 500;
|
|
6058
|
-
const DOUBLE_CLICK_DIST = 5;
|
|
6059
|
-
const statusChipRef = useRef({ version: appVersion, model, fleetRunning: 0, yolo, autonomy: "off" });
|
|
6060
|
-
const handleMouse = React6.useCallback(
|
|
6061
|
-
(ev) => {
|
|
6062
|
-
const s2 = stateRef.current;
|
|
6063
|
-
if (ev.type === "wheel") {
|
|
6064
|
-
const up = ev.button === "wheelUp";
|
|
6065
|
-
const step = up ? -1 : 1;
|
|
6066
|
-
if (s2.slashPicker.open) return dispatch({ type: "slashPickerMove", delta: step });
|
|
6067
|
-
if (s2.modelPicker.open) return dispatch({ type: "modelPickerMove", delta: step });
|
|
6068
|
-
if (s2.autonomyPicker.open) return dispatch({ type: "autonomyPickerMove", delta: step });
|
|
6069
|
-
if (s2.settingsPicker.open) return dispatch({ type: "settingsFieldMove", delta: step });
|
|
6070
|
-
if (s2.picker.open) return dispatch({ type: "pickerMove", delta: step });
|
|
6071
|
-
if (s2.rewindOverlay) return dispatch({ type: "rewindOverlayMove", delta: step });
|
|
6072
|
-
return dispatch({ type: "scrollBy", delta: up ? WHEEL_STEP : -WHEEL_STEP });
|
|
6073
|
-
}
|
|
6074
|
-
if (ev.type === "release") {
|
|
6075
|
-
scrollbarDragRef.current = false;
|
|
6076
|
-
return;
|
|
6077
|
-
}
|
|
6078
|
-
if (ev.button !== "left") return;
|
|
6079
|
-
const now = Date.now();
|
|
6080
|
-
const lastClick = lastLeftClickRef.current;
|
|
6081
|
-
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;
|
|
6082
|
-
const clickCount = isMultiClick ? lastClick.count + 1 : 1;
|
|
6083
|
-
lastLeftClickRef.current = { x: ev.x, y: ev.y, ts: now, count: clickCount };
|
|
6084
|
-
{
|
|
6085
|
-
const rows = s2.viewportRows;
|
|
6086
|
-
if (ev.drag) {
|
|
6087
|
-
if (scrollbarDragRef.current && s2.totalLines > rows) {
|
|
6088
|
-
dispatch({
|
|
6089
|
-
type: "scrollTo",
|
|
6090
|
-
offset: scrollOffsetForTrackRow(rows, s2.totalLines, ev.y - 1)
|
|
6091
|
-
});
|
|
6092
|
-
}
|
|
6093
|
-
return;
|
|
6094
|
-
}
|
|
6095
|
-
const cols = termCols || 80;
|
|
6096
|
-
const onScrollbar = cols > 0 && ev.x >= cols - 2 && ev.y >= 1 && ev.y <= rows;
|
|
6097
|
-
if (onScrollbar && s2.totalLines > rows) {
|
|
6098
|
-
scrollbarDragRef.current = true;
|
|
6099
|
-
dispatch({
|
|
6100
|
-
type: "scrollTo",
|
|
6101
|
-
offset: scrollOffsetForTrackRow(rows, s2.totalLines, ev.y - 1)
|
|
6102
|
-
});
|
|
6103
|
-
return;
|
|
6104
|
-
}
|
|
6105
|
-
}
|
|
6106
|
-
if (ev.y > s2.viewportRows) {
|
|
6107
|
-
if (s2.helpOpen) return dispatch({ type: "toggleHelp" });
|
|
6108
|
-
if (s2.agentsMonitorOpen) return dispatch({ type: "toggleAgentsMonitor" });
|
|
6109
|
-
if (s2.monitorOpen) return dispatch({ type: "toggleMonitor" });
|
|
6110
|
-
if (s2.worktreeMonitorOpen) return dispatch({ type: "worktreeMonitorToggle" });
|
|
6111
|
-
if (s2.autoPhase?.monitorOpen) return dispatch({ type: "autoPhaseMonitorToggle" });
|
|
6112
|
-
}
|
|
6113
|
-
const affordance = s2.scrollOffset > 0 && s2.pendingNewLines > 0 ? 1 : 0;
|
|
6114
|
-
if (affordance && ev.y === s2.viewportRows + 1) {
|
|
6115
|
-
return dispatch({ type: "scrollToBottom" });
|
|
6116
|
-
}
|
|
6117
|
-
if (s2.confirmQueue.length > 0) {
|
|
6118
|
-
const node = confirmRef.current;
|
|
6119
|
-
const head = s2.confirmQueue[0];
|
|
6120
|
-
if (node && head) {
|
|
6121
|
-
const { height } = measureElement(node);
|
|
6122
|
-
const top = s2.viewportRows + affordance + preRowsRef.current + 1;
|
|
6123
|
-
const buttonsRow = top + height - 2;
|
|
6124
|
-
if (ev.y === buttonsRow) {
|
|
6125
|
-
const contentX = ev.x - 1 - 2;
|
|
6126
|
-
for (const seg of confirmButtonSegments(head.suggestedPattern)) {
|
|
6127
|
-
if (contentX >= seg.start && contentX < seg.start + seg.len) {
|
|
6128
|
-
confirmDecisionRef.current?.(seg.decision);
|
|
6129
|
-
return;
|
|
6130
|
-
}
|
|
6131
|
-
}
|
|
6132
|
-
}
|
|
6133
|
-
}
|
|
6134
|
-
return;
|
|
6135
|
-
}
|
|
6136
|
-
if (s2.rewindOverlay) {
|
|
6137
|
-
const cps = s2.rewindOverlay.checkpoints;
|
|
6138
|
-
const firstItemRow2 = s2.viewportRows + affordance + preRowsRef.current + 3 + 1;
|
|
6139
|
-
const index2 = ev.y - firstItemRow2;
|
|
6140
|
-
if (index2 < 0 || index2 >= cps.length) return;
|
|
6141
|
-
if (index2 === s2.rewindOverlay.selected) {
|
|
6142
|
-
handleRewindToRef.current?.(cps[index2].promptIndex);
|
|
6143
|
-
} else {
|
|
6144
|
-
dispatch({ type: "rewindOverlayMove", delta: index2 - s2.rewindOverlay.selected });
|
|
6145
|
-
}
|
|
6146
|
-
return;
|
|
6147
|
-
}
|
|
6148
|
-
if (s2.settingsPicker.open) {
|
|
6149
|
-
const firstRow = s2.viewportRows + affordance + preRowsRef.current + 2 + 1;
|
|
6150
|
-
const field = ev.y - firstRow;
|
|
6151
|
-
if (field < 0 || field > 1) return;
|
|
6152
|
-
if (field === s2.settingsPicker.field) {
|
|
6153
|
-
dispatch({ type: "settingsValueChange", delta: 1 });
|
|
6154
|
-
} else {
|
|
6155
|
-
dispatch({ type: "settingsFieldSet", field });
|
|
6156
|
-
}
|
|
6157
|
-
return;
|
|
6158
|
-
}
|
|
6159
|
-
const picker = s2.modelPicker.open ? {
|
|
6160
|
-
header: 2,
|
|
6161
|
-
count: s2.modelPicker.step === "provider" ? s2.modelPicker.providerOptions.length : s2.modelPicker.modelOptions.length,
|
|
6162
|
-
selected: s2.modelPicker.selected,
|
|
6163
|
-
move: (delta) => dispatch({ type: "modelPickerMove", delta })
|
|
6164
|
-
} : s2.autonomyPicker.open ? {
|
|
6165
|
-
header: 2,
|
|
6166
|
-
count: s2.autonomyPicker.options.length,
|
|
6167
|
-
selected: s2.autonomyPicker.selected,
|
|
6168
|
-
move: (delta) => dispatch({ type: "autonomyPickerMove", delta })
|
|
6169
|
-
} : s2.slashPicker.open ? {
|
|
6170
|
-
header: 1,
|
|
6171
|
-
count: s2.slashPicker.matches.length,
|
|
6172
|
-
selected: s2.slashPicker.selected,
|
|
6173
|
-
move: (delta) => dispatch({ type: "slashPickerMove", delta })
|
|
6174
|
-
} : s2.picker.open ? {
|
|
6175
|
-
header: 1,
|
|
6176
|
-
count: s2.picker.matches.length,
|
|
6177
|
-
selected: s2.picker.selected,
|
|
6178
|
-
move: (delta) => dispatch({ type: "pickerMove", delta })
|
|
6179
|
-
} : null;
|
|
6180
|
-
if (!picker || picker.count === 0) {
|
|
6181
|
-
const inputDisabled = s2.status === "aborting" && !s2.steeringPending;
|
|
6182
|
-
if (!inputDisabled) {
|
|
6183
|
-
const cols = termCols || 80;
|
|
6184
|
-
const inputTop = s2.viewportRows + affordance + liveStripRowsRef.current + 1;
|
|
6185
|
-
const inputRows = layoutInputRows(INPUT_PROMPT, s2.buffer, s2.cursor, cols).length;
|
|
6186
|
-
const rowIdx = ev.y - inputTop;
|
|
6187
|
-
if (rowIdx >= 0 && rowIdx < inputRows) {
|
|
6188
|
-
const next = inputIndexAtRowCol(INPUT_PROMPT, s2.buffer, cols, rowIdx, ev.x - 1);
|
|
6189
|
-
return dispatch({ type: "setBuffer", buffer: s2.buffer, cursor: next });
|
|
6190
|
-
}
|
|
6191
|
-
}
|
|
6192
|
-
if (!s2.helpOpen && !s2.agentsMonitorOpen && !s2.monitorOpen && !s2.worktreeMonitorOpen && !s2.autoPhase?.monitorOpen) {
|
|
6193
|
-
const chip = statusChipRef.current;
|
|
6194
|
-
const statusTop = s2.viewportRows + affordance + preRowsRef.current + 1;
|
|
6195
|
-
const contentX = ev.x - 1;
|
|
6196
|
-
if (ev.y === statusTop + 1) {
|
|
6197
|
-
const span = statusBarModelSpan({
|
|
6198
|
-
version: chip.version,
|
|
6199
|
-
state: s2.status,
|
|
6200
|
-
fleetRunning: chip.fleetRunning,
|
|
6201
|
-
model: chip.model
|
|
6202
|
-
});
|
|
6203
|
-
if (contentX >= span.start && contentX < span.start + span.len) {
|
|
6204
|
-
openModelPickerRef.current?.();
|
|
6205
|
-
}
|
|
6206
|
-
} else if (ev.y === statusTop + 2) {
|
|
6207
|
-
const span = statusBarAutonomySpan({ yolo: chip.yolo, autonomy: chip.autonomy });
|
|
6208
|
-
if (span && contentX >= span.start && contentX < span.start + span.len) {
|
|
6209
|
-
openAutonomyPickerRef.current?.();
|
|
6210
|
-
}
|
|
6211
|
-
}
|
|
6212
|
-
}
|
|
6213
|
-
return;
|
|
6214
|
-
}
|
|
6215
|
-
const firstItemRow = s2.viewportRows + affordance + preRowsRef.current + picker.header + 1;
|
|
6216
|
-
const index = ev.y - firstItemRow;
|
|
6217
|
-
if (index < 0 || index >= picker.count) return;
|
|
6218
|
-
if (index === picker.selected) {
|
|
6219
|
-
handleKeyRef.current?.("", { ...EMPTY_KEY, return: true });
|
|
6220
|
-
} else {
|
|
6221
|
-
pendingClickConfirmRef.current = index;
|
|
6222
|
-
picker.move(index - picker.selected);
|
|
6223
|
-
}
|
|
6224
|
-
},
|
|
6225
|
-
// dispatch is stable (useReducer); refs are mutable — no reactive deps.
|
|
6226
|
-
// termCols is stable (useState + resize effect).
|
|
6227
|
-
[termCols]
|
|
6228
|
-
);
|
|
6229
|
-
useEffect(() => {
|
|
6230
|
-
if (!subscribeMouse) return;
|
|
6231
|
-
return subscribeMouse(handleMouse);
|
|
6232
|
-
}, [subscribeMouse, handleMouse]);
|
|
6233
|
-
useEffect(() => {
|
|
6234
|
-
const target = pendingClickConfirmRef.current;
|
|
6235
|
-
if (target === null) return;
|
|
6236
|
-
const open = state.slashPicker.open || state.modelPicker.open || state.autonomyPicker.open || state.picker.open;
|
|
6237
|
-
if (!open) {
|
|
6238
|
-
pendingClickConfirmRef.current = null;
|
|
6239
|
-
return;
|
|
6240
|
-
}
|
|
6241
|
-
const sel = state.slashPicker.open ? state.slashPicker.selected : state.modelPicker.open ? state.modelPicker.selected : state.autonomyPicker.open ? state.autonomyPicker.selected : state.picker.selected;
|
|
6242
|
-
if (sel === target) {
|
|
6243
|
-
pendingClickConfirmRef.current = null;
|
|
6244
|
-
handleKeyRef.current?.("", { ...EMPTY_KEY, return: true });
|
|
6245
|
-
}
|
|
6246
|
-
}, [
|
|
6247
|
-
state.slashPicker.open,
|
|
6248
|
-
state.slashPicker.selected,
|
|
6249
|
-
state.modelPicker.open,
|
|
6250
|
-
state.modelPicker.selected,
|
|
6251
|
-
state.autonomyPicker.open,
|
|
6252
|
-
state.autonomyPicker.selected,
|
|
6253
|
-
state.picker.open,
|
|
6254
|
-
state.picker.selected
|
|
6255
|
-
]);
|
|
6256
|
-
const handleRewindTo = React6.useCallback(
|
|
5954
|
+
const handleRewindTo = React5.useCallback(
|
|
6257
5955
|
async (checkpointIndex) => {
|
|
6258
5956
|
const sessionId = agent.ctx.session.id;
|
|
6259
5957
|
if (!sessionId) return;
|
|
@@ -6272,13 +5970,13 @@ function App({
|
|
|
6272
5970
|
dispatch({ type: "clearInput" });
|
|
6273
5971
|
};
|
|
6274
5972
|
const startedAtRef = useRef(Date.now());
|
|
6275
|
-
const [nowTick, setNowTick] =
|
|
5973
|
+
const [nowTick, setNowTick] = React5.useState(Date.now());
|
|
6276
5974
|
useEffect(() => {
|
|
6277
5975
|
const t = setInterval(() => setNowTick(Date.now()), 1e3);
|
|
6278
5976
|
return () => clearInterval(t);
|
|
6279
5977
|
}, []);
|
|
6280
5978
|
const elapsedMs = nowTick - startedAtRef.current;
|
|
6281
|
-
const [gitInfo, setGitInfo] =
|
|
5979
|
+
const [gitInfo, setGitInfo] = React5.useState(null);
|
|
6282
5980
|
useEffect(() => {
|
|
6283
5981
|
let cancelled = false;
|
|
6284
5982
|
const refresh = () => {
|
|
@@ -6635,60 +6333,6 @@ function App({
|
|
|
6635
6333
|
slashRegistry.unregister("altscreen");
|
|
6636
6334
|
};
|
|
6637
6335
|
}, [slashRegistry]);
|
|
6638
|
-
const mouseLiveRef = useRef(mouseLive);
|
|
6639
|
-
mouseLiveRef.current = mouseLive;
|
|
6640
|
-
useEffect(() => {
|
|
6641
|
-
const MOUSE_ON_SEQ = "\x1B[?1000h\x1B[?1006h";
|
|
6642
|
-
const MOUSE_OFF_SEQ = "\x1B[?1006l\x1B[?1000l";
|
|
6643
|
-
const ALT_ON = "\x1B[?1049h";
|
|
6644
|
-
const ALT_OFF = "\x1B[?1049l";
|
|
6645
|
-
const cmd = {
|
|
6646
|
-
name: "mouse",
|
|
6647
|
-
description: "Toggle mouse mode (clickable menus + wheel-scroll chat). Needs launch with --mouse to enable.",
|
|
6648
|
-
async run(args) {
|
|
6649
|
-
const arg = args.trim().toLowerCase();
|
|
6650
|
-
if (arg !== "on" && arg !== "off") {
|
|
6651
|
-
return {
|
|
6652
|
-
message: `Mouse mode is ${mouseLiveRef.current ? "ON" : "OFF"}. Usage: /mouse on|off`
|
|
6653
|
-
};
|
|
6654
|
-
}
|
|
6655
|
-
if (arg === "on") {
|
|
6656
|
-
if (!mouse) {
|
|
6657
|
-
return {
|
|
6658
|
-
message: "Mouse mode needs the --mouse launch flag (it rewires stdin so mouse bytes never reach the input). Restart with `wstack --tui --mouse`."
|
|
6659
|
-
};
|
|
6660
|
-
}
|
|
6661
|
-
try {
|
|
6662
|
-
writeOut(ALT_ON);
|
|
6663
|
-
writeOut("\x1B[H");
|
|
6664
|
-
writeOut(MOUSE_ON_SEQ);
|
|
6665
|
-
} catch {
|
|
6666
|
-
return { message: "Failed to enable mouse mode." };
|
|
6667
|
-
}
|
|
6668
|
-
setMouseLive(true);
|
|
6669
|
-
setManagedLive(true);
|
|
6670
|
-
return {
|
|
6671
|
-
message: "Mouse mode ON. Click menu items, wheel-scroll the chat (PgUp/PgDn too). Native terminal copy/scroll are suspended until `/mouse off`."
|
|
6672
|
-
};
|
|
6673
|
-
}
|
|
6674
|
-
try {
|
|
6675
|
-
writeOut(MOUSE_OFF_SEQ);
|
|
6676
|
-
writeOut(ALT_OFF);
|
|
6677
|
-
} catch {
|
|
6678
|
-
return { message: "Failed to disable mouse mode." };
|
|
6679
|
-
}
|
|
6680
|
-
setMouseLive(false);
|
|
6681
|
-
setManagedLive(false);
|
|
6682
|
-
return {
|
|
6683
|
-
message: "Mouse mode OFF. Native terminal scroll/copy restored; chat history flows into native scrollback again."
|
|
6684
|
-
};
|
|
6685
|
-
}
|
|
6686
|
-
};
|
|
6687
|
-
slashRegistry.register(cmd);
|
|
6688
|
-
return () => {
|
|
6689
|
-
slashRegistry.unregister("mouse");
|
|
6690
|
-
};
|
|
6691
|
-
}, [slashRegistry, mouse]);
|
|
6692
6336
|
useEffect(() => {
|
|
6693
6337
|
const cmd = {
|
|
6694
6338
|
name: "steer",
|
|
@@ -6795,16 +6439,12 @@ function App({
|
|
|
6795
6439
|
slashRegistry.unregister("agents");
|
|
6796
6440
|
};
|
|
6797
6441
|
}, [slashRegistry]);
|
|
6798
|
-
const openModelPicker =
|
|
6442
|
+
const openModelPicker = React5.useCallback(async () => {
|
|
6799
6443
|
if (!getPickableProviders) return;
|
|
6800
6444
|
const providers = await getPickableProviders();
|
|
6801
6445
|
dispatch({ type: "modelPickerOpen", providers });
|
|
6802
6446
|
}, [getPickableProviders]);
|
|
6803
|
-
const
|
|
6804
|
-
if (!switchAutonomy) return;
|
|
6805
|
-
dispatch({ type: "autonomyPickerOpen", options: AUTONOMY_OPTIONS });
|
|
6806
|
-
}, [switchAutonomy]);
|
|
6807
|
-
const openSettings = React6.useCallback(() => {
|
|
6447
|
+
const openSettings = React5.useCallback(() => {
|
|
6808
6448
|
if (!getSettings) return;
|
|
6809
6449
|
const s2 = getSettings();
|
|
6810
6450
|
dispatch({ type: "settingsOpen", mode: s2.mode, delayMs: s2.delayMs });
|
|
@@ -8547,20 +8187,6 @@ User message:
|
|
|
8547
8187
|
})();
|
|
8548
8188
|
}, [initialAsk, initialGoal]);
|
|
8549
8189
|
handleKeyRef.current = handleKey;
|
|
8550
|
-
openModelPickerRef.current = () => {
|
|
8551
|
-
void openModelPicker();
|
|
8552
|
-
};
|
|
8553
|
-
openAutonomyPickerRef.current = openAutonomyPicker;
|
|
8554
|
-
handleRewindToRef.current = (promptIndex) => {
|
|
8555
|
-
void handleRewindTo(promptIndex);
|
|
8556
|
-
};
|
|
8557
|
-
statusChipRef.current = {
|
|
8558
|
-
version: appVersion,
|
|
8559
|
-
model: `${liveProvider}/${liveModel}`,
|
|
8560
|
-
fleetRunning: fleetCounts?.running ?? 0,
|
|
8561
|
-
yolo: yoloLive,
|
|
8562
|
-
autonomy: autonomyLive
|
|
8563
|
-
};
|
|
8564
8190
|
const inputHint = useMemo(() => {
|
|
8565
8191
|
if (state.status !== "idle") return "";
|
|
8566
8192
|
if (state.buffer.startsWith("/")) return "slash command \u2014 Enter to dispatch";
|
|
@@ -8590,20 +8216,18 @@ User message:
|
|
|
8590
8216
|
),
|
|
8591
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,
|
|
8592
8218
|
/* @__PURE__ */ jsxs(Box, { ref: managedLive ? bottomRef : void 0, flexDirection: "column", flexShrink: 0, children: [
|
|
8593
|
-
/* @__PURE__ */
|
|
8594
|
-
|
|
8595
|
-
|
|
8596
|
-
|
|
8597
|
-
|
|
8598
|
-
|
|
8599
|
-
|
|
8600
|
-
|
|
8601
|
-
|
|
8602
|
-
|
|
8603
|
-
|
|
8604
|
-
|
|
8605
|
-
)
|
|
8606
|
-
] }),
|
|
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
|
+
),
|
|
8607
8231
|
state.picker.open ? /* @__PURE__ */ jsx(
|
|
8608
8232
|
FilePicker,
|
|
8609
8233
|
{
|
|
@@ -8679,8 +8303,7 @@ User message:
|
|
|
8679
8303
|
head.resolve(decision);
|
|
8680
8304
|
dispatch({ type: "confirmClose" });
|
|
8681
8305
|
};
|
|
8682
|
-
|
|
8683
|
-
return /* @__PURE__ */ jsx(Box, { ref: confirmRef, flexDirection: "column", marginY: 1, flexShrink: 0, children: /* @__PURE__ */ jsx(
|
|
8306
|
+
return /* @__PURE__ */ jsx(
|
|
8684
8307
|
ConfirmPrompt,
|
|
8685
8308
|
{
|
|
8686
8309
|
toolName: head.toolName,
|
|
@@ -8688,7 +8311,7 @@ User message:
|
|
|
8688
8311
|
suggestedPattern: head.suggestedPattern,
|
|
8689
8312
|
onDecision
|
|
8690
8313
|
}
|
|
8691
|
-
)
|
|
8314
|
+
);
|
|
8692
8315
|
})(),
|
|
8693
8316
|
/* @__PURE__ */ jsx(
|
|
8694
8317
|
StatusBar,
|
|
@@ -8723,12 +8346,11 @@ User message:
|
|
|
8723
8346
|
confirm: state.confirmQueue.length > 0,
|
|
8724
8347
|
picker: state.picker.open || state.slashPicker.open || state.modelPicker.open || state.autonomyPicker.open || state.settingsPicker.open || !!state.rewindOverlay,
|
|
8725
8348
|
monitor: state.agentsMonitorOpen || state.monitorOpen || state.worktreeMonitorOpen || !!state.autoPhase?.monitorOpen,
|
|
8726
|
-
managed: managedLive
|
|
8727
|
-
mouse: mouseLive
|
|
8349
|
+
managed: managedLive
|
|
8728
8350
|
}
|
|
8729
8351
|
}
|
|
8730
8352
|
) : null,
|
|
8731
|
-
state.helpOpen ? /* @__PURE__ */ jsx(HelpOverlay, { managed: managedLive
|
|
8353
|
+
state.helpOpen ? /* @__PURE__ */ jsx(HelpOverlay, { managed: managedLive }) : null,
|
|
8732
8354
|
state.agentsMonitorOpen ? /* @__PURE__ */ jsx(
|
|
8733
8355
|
AgentsMonitor,
|
|
8734
8356
|
{
|
|
@@ -8760,6 +8382,7 @@ User message:
|
|
|
8760
8382
|
entries: state.fleet,
|
|
8761
8383
|
totalCost: state.fleetCost,
|
|
8762
8384
|
totalTokens: state.fleetTokens,
|
|
8385
|
+
maxConcurrent: state.fleetConcurrency,
|
|
8763
8386
|
nowTick,
|
|
8764
8387
|
collabSession: state.collabSession
|
|
8765
8388
|
}
|
|
@@ -8816,48 +8439,6 @@ function fmtTok3(n) {
|
|
|
8816
8439
|
return `${(n / 1e6).toFixed(1)}M`;
|
|
8817
8440
|
}
|
|
8818
8441
|
|
|
8819
|
-
// src/mouse.ts
|
|
8820
|
-
var SGR_MOUSE_RE = /\x1b?\[<(\d+);(\d+);(\d+)([Mm])/g;
|
|
8821
|
-
function stripSgrMouse(s2) {
|
|
8822
|
-
return s2.replace(SGR_MOUSE_RE, "");
|
|
8823
|
-
}
|
|
8824
|
-
function parseSgrMouse(s2) {
|
|
8825
|
-
const events = [];
|
|
8826
|
-
for (const m of s2.matchAll(SGR_MOUSE_RE)) {
|
|
8827
|
-
const cb = Number.parseInt(m[1] ?? "", 10);
|
|
8828
|
-
const x = Number.parseInt(m[2] ?? "", 10);
|
|
8829
|
-
const y = Number.parseInt(m[3] ?? "", 10);
|
|
8830
|
-
const final = m[4];
|
|
8831
|
-
if (!Number.isFinite(cb) || !Number.isFinite(x) || !Number.isFinite(y)) continue;
|
|
8832
|
-
const isWheel = (cb & 64) !== 0;
|
|
8833
|
-
const drag = (cb & 32) !== 0;
|
|
8834
|
-
const low = cb & 3;
|
|
8835
|
-
let button;
|
|
8836
|
-
let type;
|
|
8837
|
-
if (isWheel) {
|
|
8838
|
-
type = "wheel";
|
|
8839
|
-
button = low === 0 ? "wheelUp" : low === 1 ? "wheelDown" : "other";
|
|
8840
|
-
} else if (final === "m") {
|
|
8841
|
-
type = "release";
|
|
8842
|
-
button = low === 0 ? "left" : low === 1 ? "middle" : low === 2 ? "right" : "other";
|
|
8843
|
-
} else {
|
|
8844
|
-
type = "press";
|
|
8845
|
-
button = low === 0 ? "left" : low === 1 ? "middle" : low === 2 ? "right" : "other";
|
|
8846
|
-
}
|
|
8847
|
-
events.push({
|
|
8848
|
-
type,
|
|
8849
|
-
button,
|
|
8850
|
-
x,
|
|
8851
|
-
y,
|
|
8852
|
-
shift: (cb & 4) !== 0,
|
|
8853
|
-
alt: (cb & 8) !== 0,
|
|
8854
|
-
ctrl: (cb & 16) !== 0,
|
|
8855
|
-
drag
|
|
8856
|
-
});
|
|
8857
|
-
}
|
|
8858
|
-
return events;
|
|
8859
|
-
}
|
|
8860
|
-
|
|
8861
8442
|
// src/terminal-title.ts
|
|
8862
8443
|
var SPINNER = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
8863
8444
|
var setTitle = (s2) => `\x1B]0;${s2}\x07`;
|
|
@@ -8927,8 +8508,6 @@ var BRACKETED_PASTE_OFF = "\x1B[?2004l";
|
|
|
8927
8508
|
var ALT_SCREEN_ON = "\x1B[?1049h";
|
|
8928
8509
|
var ALT_SCREEN_OFF = "\x1B[?1049l";
|
|
8929
8510
|
var CURSOR_HOME = "\x1B[H";
|
|
8930
|
-
var MOUSE_ON = "\x1B[?1000h\x1B[?1002h\x1B[?1006h";
|
|
8931
|
-
var MOUSE_OFF = "\x1B[?1006l\x1B[?1002l\x1B[?1000l";
|
|
8932
8511
|
async function runTui(opts) {
|
|
8933
8512
|
const stdout = process.stdout;
|
|
8934
8513
|
const stdin = process.stdin;
|
|
@@ -8938,107 +8517,13 @@ async function runTui(opts) {
|
|
|
8938
8517
|
);
|
|
8939
8518
|
return 2;
|
|
8940
8519
|
}
|
|
8941
|
-
const
|
|
8942
|
-
const useAltScreen = opts.altScreen === true || useMouse;
|
|
8520
|
+
const useAltScreen = opts.altScreen === true;
|
|
8943
8521
|
if (useAltScreen) {
|
|
8944
8522
|
stdout.write(ALT_SCREEN_ON);
|
|
8945
8523
|
stdout.write(CURSOR_HOME);
|
|
8946
8524
|
}
|
|
8947
8525
|
stdout.write(BRACKETED_PASTE_ON);
|
|
8948
|
-
|
|
8949
|
-
stdout.write(MOUSE_ON);
|
|
8950
|
-
}
|
|
8951
|
-
const mouseListeners = /* @__PURE__ */ new Set();
|
|
8952
|
-
let inkStdin = stdin;
|
|
8953
|
-
let detachMouse = null;
|
|
8954
|
-
if (useMouse) {
|
|
8955
|
-
class KeyboardReadable extends Readable {
|
|
8956
|
-
pendingChunks = [];
|
|
8957
|
-
// eslint-disable-next-line no-useless-constructor
|
|
8958
|
-
constructor() {
|
|
8959
|
-
super({ encoding: "utf8", highWaterMark: 64 * 1024 });
|
|
8960
|
-
}
|
|
8961
|
-
_read(_size) {
|
|
8962
|
-
this.flushPending();
|
|
8963
|
-
}
|
|
8964
|
-
flushPending() {
|
|
8965
|
-
while (this.pendingChunks.length > 0) {
|
|
8966
|
-
const chunk = this.pendingChunks[0];
|
|
8967
|
-
const ok = this.push(chunk);
|
|
8968
|
-
this.pendingChunks.shift();
|
|
8969
|
-
if (!ok) {
|
|
8970
|
-
break;
|
|
8971
|
-
}
|
|
8972
|
-
}
|
|
8973
|
-
}
|
|
8974
|
-
/** Called by the stdin data handler when keyboard bytes are available. */
|
|
8975
|
-
doPush(chunk) {
|
|
8976
|
-
if (chunk.length === 0) return;
|
|
8977
|
-
const ok = this.push(chunk);
|
|
8978
|
-
if (ok) {
|
|
8979
|
-
if (this.pendingChunks.length > 0) {
|
|
8980
|
-
this.flushPending();
|
|
8981
|
-
}
|
|
8982
|
-
} else {
|
|
8983
|
-
if (this.pendingChunks.length >= 100) {
|
|
8984
|
-
this.pendingChunks.shift();
|
|
8985
|
-
}
|
|
8986
|
-
this.pendingChunks.push(chunk);
|
|
8987
|
-
}
|
|
8988
|
-
}
|
|
8989
|
-
/** Called on shutdown so the stream closes cleanly. */
|
|
8990
|
-
doEnd() {
|
|
8991
|
-
this.pendingChunks = [];
|
|
8992
|
-
this.push(null);
|
|
8993
|
-
}
|
|
8994
|
-
}
|
|
8995
|
-
const keyboardStream = new KeyboardReadable();
|
|
8996
|
-
const p = keyboardStream;
|
|
8997
|
-
p.isTTY = true;
|
|
8998
|
-
p.setRawMode = (mode) => {
|
|
8999
|
-
try {
|
|
9000
|
-
stdin.setRawMode?.(mode);
|
|
9001
|
-
} catch {
|
|
9002
|
-
}
|
|
9003
|
-
return p;
|
|
9004
|
-
};
|
|
9005
|
-
const realRef = stdin.ref?.bind(stdin);
|
|
9006
|
-
const realUnref = stdin.unref?.bind(stdin);
|
|
9007
|
-
p.ref = () => {
|
|
9008
|
-
realRef?.();
|
|
9009
|
-
return p;
|
|
9010
|
-
};
|
|
9011
|
-
p.unref = () => {
|
|
9012
|
-
realUnref?.();
|
|
9013
|
-
return p;
|
|
9014
|
-
};
|
|
9015
|
-
stdin.setEncoding("utf8");
|
|
9016
|
-
const onData = (chunk) => {
|
|
9017
|
-
const evs = parseSgrMouse(chunk);
|
|
9018
|
-
for (const ev of evs) {
|
|
9019
|
-
for (const fn of mouseListeners) {
|
|
9020
|
-
try {
|
|
9021
|
-
fn(ev);
|
|
9022
|
-
} catch {
|
|
9023
|
-
}
|
|
9024
|
-
}
|
|
9025
|
-
}
|
|
9026
|
-
const rest = stripSgrMouse(chunk);
|
|
9027
|
-
keyboardStream.doPush(rest);
|
|
9028
|
-
};
|
|
9029
|
-
stdin.on("data", onData);
|
|
9030
|
-
detachMouse = () => {
|
|
9031
|
-
stdin.off("data", onData);
|
|
9032
|
-
keyboardStream.doEnd();
|
|
9033
|
-
};
|
|
9034
|
-
inkStdin = p;
|
|
9035
|
-
}
|
|
9036
|
-
const subscribeMouse = useMouse ? (fn) => {
|
|
9037
|
-
mouseListeners.add(fn);
|
|
9038
|
-
return () => {
|
|
9039
|
-
mouseListeners.delete(fn);
|
|
9040
|
-
};
|
|
9041
|
-
} : void 0;
|
|
8526
|
+
const inkStdin = stdin;
|
|
9042
8527
|
const stopTitle = startTerminalTitle({ stdout, events: opts.events, model: opts.model });
|
|
9043
8528
|
const swallowSignals = ["SIGTSTP", "SIGQUIT", "SIGTTIN", "SIGTTOU"];
|
|
9044
8529
|
const swallow = () => {
|
|
@@ -9057,15 +8542,8 @@ async function runTui(opts) {
|
|
|
9057
8542
|
stopTitle();
|
|
9058
8543
|
} catch {
|
|
9059
8544
|
}
|
|
9060
|
-
try {
|
|
9061
|
-
detachMouse?.();
|
|
9062
|
-
} catch {
|
|
9063
|
-
}
|
|
9064
8545
|
try {
|
|
9065
8546
|
stdout.write(BRACKETED_PASTE_OFF);
|
|
9066
|
-
if (useMouse) {
|
|
9067
|
-
stdout.write(MOUSE_OFF);
|
|
9068
|
-
}
|
|
9069
8547
|
if (useAltScreen) {
|
|
9070
8548
|
stdout.write(ALT_SCREEN_OFF);
|
|
9071
8549
|
}
|
|
@@ -9106,7 +8584,7 @@ async function runTui(opts) {
|
|
|
9106
8584
|
let instance;
|
|
9107
8585
|
try {
|
|
9108
8586
|
instance = render(
|
|
9109
|
-
|
|
8587
|
+
React5.createElement(App, {
|
|
9110
8588
|
agent: opts.agent,
|
|
9111
8589
|
slashRegistry: opts.slashRegistry,
|
|
9112
8590
|
attachments: opts.attachments,
|
|
@@ -9150,11 +8628,8 @@ async function runTui(opts) {
|
|
|
9150
8628
|
getSettings: opts.getSettings,
|
|
9151
8629
|
saveSettings: opts.saveSettings,
|
|
9152
8630
|
predictNext: opts.predictNext,
|
|
9153
|
-
mouse: useMouse,
|
|
9154
|
-
subscribeMouse,
|
|
9155
8631
|
// Managed viewport (in-app scroll + collapsibility) follows
|
|
9156
|
-
// alt-screen: it owns the screen, so there's no native-scrollback
|
|
9157
|
-
// leak. Decoupled from mouse so --alt-screen alone gets it.
|
|
8632
|
+
// alt-screen: it owns the screen, so there's no native-scrollback leak.
|
|
9158
8633
|
managed: useAltScreen
|
|
9159
8634
|
}),
|
|
9160
8635
|
{ exitOnCtrlC: false, stdin: inkStdin }
|