flavor-code 1.4.0-beta.9 → 1.4.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.
@@ -19,9 +19,9 @@ import {
19
19
  thinkingWindow,
20
20
  transcriptReducer,
21
21
  withTimeout
22
- } from "./chunk-CWCYXOH6.js";
22
+ } from "./chunk-O2PC44GY.js";
23
23
  import "./chunk-N5MXHJKD.js";
24
- import "./chunk-EXOSMO57.js";
24
+ import "./chunk-7YFHRORG.js";
25
25
  import {
26
26
  Box_default,
27
27
  ClockContext,
@@ -34,18 +34,21 @@ import {
34
34
  instances_default,
35
35
  osc,
36
36
  shiftAnchor,
37
+ stringWidth,
37
38
  stripAnsi,
38
39
  useStdout,
39
40
  use_app_default,
40
41
  use_input_default
41
- } from "./chunk-XMRKRPNC.js";
42
+ } from "./chunk-VMQWXQEC.js";
42
43
  import {
43
- execFileNoThrow
44
+ execFileNoThrow,
45
+ getGraphemeSegmenter
44
46
  } from "./chunk-ZJCAQXA7.js";
45
47
  import "./chunk-S32JRDZK.js";
46
48
 
47
49
  // src/ui/app.tsx
48
50
  import { basename as basename3 } from "path";
51
+ import { homedir } from "os";
49
52
  import { useEffect as useEffect3, useReducer, useRef as useRef4, useState as useState2 } from "react";
50
53
 
51
54
  // src/claude-ink/hooks/use-animation-frame.ts
@@ -969,7 +972,13 @@ function wrapPromptInput(input, codePointCursor, options) {
969
972
  // src/ui/task-progress.tsx
970
973
  import { useRef as useRef3 } from "react";
971
974
  import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
972
- function TaskStatusLine({ block, interactive }) {
975
+ function TaskStatusLine({
976
+ block,
977
+ interactive,
978
+ textWidth,
979
+ maxTextLines,
980
+ showRoleBadge = true
981
+ }) {
973
982
  const running = block.state === "running";
974
983
  const { stdout } = useStdout();
975
984
  const foreground = interactive && (block.activity === "model" || block.task?.role === "main");
@@ -982,6 +991,8 @@ function TaskStatusLine({ block, interactive }) {
982
991
  const elapsed = block.startedAt === void 0 ? startedAt.current === void 0 ? 0 : Math.max(0, time - startedAt.current) : Math.max(0, Date.now() - block.startedAt);
983
992
  const presentation = statusPresentation(block, elapsed, foreground);
984
993
  const { metaColor, metaLabel, statusLabel, statusColor, text } = presentation;
994
+ const badge = showRoleBadge ? presentation.badge : void 0;
995
+ const badgeColor = presentation.badgeColor;
985
996
  let before = text;
986
997
  let after = "";
987
998
  if (statusLabel !== void 0) {
@@ -1007,14 +1018,26 @@ function TaskStatusLine({ block, interactive }) {
1007
1018
  thinkingScroll.current = { start: state.start, lastTime: time };
1008
1019
  return state.text;
1009
1020
  })() : void 0;
1021
+ const textSegments = [
1022
+ { text: `${presentation.glyph} `, color: presentation.color },
1023
+ ...badge ? [{ text: `${badge} `, color: badgeColor }] : [],
1024
+ { text: before, color: presentation.color },
1025
+ ...statusLabel === void 0 ? [] : [
1026
+ { text: " \xB7 ", color: presentation.color },
1027
+ { text: statusLabel, color: statusColor ?? presentation.color }
1028
+ ],
1029
+ ...after ? [{ text: after, color: presentation.color }] : [],
1030
+ ...metaLabel === void 0 ? [] : [{ text: ` ${metaLabel}`, color: metaColor ?? "ansi:blackBright" }]
1031
+ ];
1032
+ const boundedText = textWidth === void 0 || maxTextLines === void 0 ? void 0 : limitTaskTextSegments(textSegments, textWidth, maxTextLines).segments;
1010
1033
  return /* @__PURE__ */ jsxs2(Box_default, { flexDirection: "column", width: "100%", children: [
1011
- /* @__PURE__ */ jsxs2(Box_default, { ref, flexDirection: "row", children: [
1034
+ /* @__PURE__ */ jsx3(Box_default, { ref, flexDirection: "row", width: "100%", children: boundedText === void 0 ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
1012
1035
  /* @__PURE__ */ jsxs2(Text, { ...presentation.color === void 0 ? {} : { color: presentation.color }, children: [
1013
1036
  presentation.glyph,
1014
1037
  " "
1015
1038
  ] }),
1016
- presentation.badge ? /* @__PURE__ */ jsxs2(Text, { color: presentation.badgeColor, children: [
1017
- presentation.badge,
1039
+ badge ? /* @__PURE__ */ jsxs2(Text, { color: badgeColor, children: [
1040
+ badge,
1018
1041
  " "
1019
1042
  ] }) : null,
1020
1043
  /* @__PURE__ */ jsx3(Text, { ...presentation.color === void 0 ? {} : { color: presentation.color }, children: before }),
@@ -1027,10 +1050,69 @@ function TaskStatusLine({ block, interactive }) {
1027
1050
  " ",
1028
1051
  metaLabel
1029
1052
  ] })
1030
- ] }),
1053
+ ] }) : /* @__PURE__ */ jsx3(Text, { children: boundedText.map((segment, index) => /* @__PURE__ */ jsx3(Text, { ...segment.color === void 0 ? {} : { color: segment.color }, children: segment.text }, index)) }) }),
1031
1054
  thinkingLine === void 0 ? null : /* @__PURE__ */ jsx3(ThinkingLineView, { text: thinkingLine })
1032
1055
  ] });
1033
1056
  }
1057
+ function limitTaskTextSegments(segments, width, maxLines = 3) {
1058
+ const lineWidth = Math.max(3, Math.floor(width));
1059
+ const lineLimit = Math.max(1, Math.floor(maxLines));
1060
+ const lines = [[]];
1061
+ const widths = [0];
1062
+ let truncated = false;
1063
+ outer: for (let segmentIndex = 0; segmentIndex < segments.length; segmentIndex += 1) {
1064
+ const segment = segments[segmentIndex];
1065
+ for (const { segment: grapheme } of getGraphemeSegmenter().segment(segment.text)) {
1066
+ if (grapheme === "\n" || grapheme === "\r" || grapheme === "\r\n") {
1067
+ if (lines.length >= lineLimit) {
1068
+ truncated = true;
1069
+ break outer;
1070
+ }
1071
+ lines.push([]);
1072
+ widths.push(0);
1073
+ continue;
1074
+ }
1075
+ const glyphWidth = stringWidth(grapheme);
1076
+ const lineIndex = lines.length - 1;
1077
+ if (glyphWidth > 0 && widths[lineIndex] + glyphWidth > lineWidth && lines[lineIndex].length > 0) {
1078
+ if (lines.length >= lineLimit) {
1079
+ truncated = true;
1080
+ break outer;
1081
+ }
1082
+ lines.push([]);
1083
+ widths.push(0);
1084
+ }
1085
+ const target = lines.length - 1;
1086
+ lines[target].push({ value: grapheme, width: glyphWidth, segmentIndex });
1087
+ widths[target] = widths[target] + glyphWidth;
1088
+ }
1089
+ }
1090
+ if (truncated) {
1091
+ const lastIndex = lines.length - 1;
1092
+ const last = lines[lastIndex];
1093
+ while (last.length > 0 && widths[lastIndex] > lineWidth - 3) {
1094
+ widths[lastIndex] = widths[lastIndex] - last.pop().width;
1095
+ }
1096
+ while (last.length > 0 && /^\s$/u.test(last.at(-1).value)) {
1097
+ widths[lastIndex] = widths[lastIndex] - last.pop().width;
1098
+ }
1099
+ last.push({ value: "...", width: 3, segmentIndex: last.at(-1)?.segmentIndex ?? 0 });
1100
+ }
1101
+ const output = [];
1102
+ const append = (value, segmentIndex) => {
1103
+ const previous = output.at(-1);
1104
+ if (previous?.segmentIndex === segmentIndex) previous.text += value;
1105
+ else output.push({ text: value, color: segments[segmentIndex]?.color, segmentIndex });
1106
+ };
1107
+ lines.forEach((line, lineIndex) => {
1108
+ if (lineIndex > 0) append("\n", line[0]?.segmentIndex ?? output.at(-1)?.segmentIndex ?? 0);
1109
+ for (const token of line) append(token.value, token.segmentIndex);
1110
+ });
1111
+ return {
1112
+ truncated,
1113
+ segments: output.map(({ text, color }) => ({ text, ...color === void 0 ? {} : { color } }))
1114
+ };
1115
+ }
1034
1116
  var THINKING_RAIL = "#416f84";
1035
1117
  var THINKING_TEXT = "#91a0a8";
1036
1118
  function ThinkingLineView({ text }) {
@@ -1039,13 +1121,16 @@ function ThinkingLineView({ text }) {
1039
1121
  /* @__PURE__ */ jsx3(Text, { color: THINKING_TEXT, wrap: "truncate-end", children: text })
1040
1122
  ] });
1041
1123
  }
1042
- var SPLIT_TRACK_MIN_COLUMNS = 72;
1124
+ var SPLIT_TRACK_MIN_COLUMNS = 96;
1125
+ var SPLIT_TRACK_GUTTER_COLUMNS = 3;
1126
+ var SPLIT_TRACK_MAIN_RATIO = 0.44;
1043
1127
  function TaskProgressPanel({
1044
1128
  blocks,
1045
1129
  interactive,
1046
1130
  maxHeight = 8,
1047
1131
  columns = 80,
1048
- scrollRef,
1132
+ mainScrollRef,
1133
+ subagentScrollRef,
1049
1134
  onHoverChange
1050
1135
  }) {
1051
1136
  if (blocks.length === 0 || maxHeight <= 0) return null;
@@ -1054,54 +1139,210 @@ function TaskProgressPanel({
1054
1139
  const hasTasks = taskBlocks.length > 0;
1055
1140
  const hasSubagents = subagentBlocks.length > 0;
1056
1141
  const split = hasTasks && hasSubagents && columns >= SPLIT_TRACK_MIN_COLUMNS;
1057
- return /* @__PURE__ */ jsxs2(
1142
+ const hoverProps = (track) => ({
1143
+ onMouseEnter: () => onHoverChange?.(track)
1144
+ });
1145
+ return /* @__PURE__ */ jsx3(
1058
1146
  Box_default,
1059
1147
  {
1060
1148
  flexDirection: "column",
1061
1149
  flexShrink: 0,
1062
1150
  width: "100%",
1063
1151
  maxHeight,
1064
- onMouseEnter: () => onHoverChange?.(true),
1065
- onMouseLeave: () => onHoverChange?.(false),
1066
- children: [
1067
- split ? /* @__PURE__ */ jsx3(SplitTrackHeaders, {}) : /* @__PURE__ */ jsx3(TrackHeader, { role: hasTasks ? "main" : "subagent" }),
1068
- maxHeight > 1 ? /* @__PURE__ */ jsx3(
1069
- ScrollBox_default,
1070
- {
1071
- ...scrollRef === void 0 ? {} : { ref: scrollRef },
1072
- flexDirection: "column",
1073
- flexShrink: 1,
1074
- width: "100%",
1075
- maxHeight: maxHeight - 1,
1076
- children: split ? pairedTrackRows(taskBlocks, subagentBlocks).map(([task, subagent], index) => /* @__PURE__ */ jsxs2(Box_default, { flexDirection: "row", width: "100%", columnGap: 2, children: [
1077
- /* @__PURE__ */ jsx3(Box_default, { flexBasis: 0, flexGrow: 1, minWidth: 0, flexDirection: "column", children: task === void 0 ? null : /* @__PURE__ */ jsx3(TaskStatusLine, { block: task, interactive }) }),
1078
- /* @__PURE__ */ jsx3(Box_default, { flexBasis: 0, flexGrow: 1, minWidth: 0, flexDirection: "column", children: subagent === void 0 ? null : /* @__PURE__ */ jsx3(TaskStatusLine, { block: subagent, interactive }) })
1079
- ] }, `${task?.id ?? "task"}:${subagent?.id ?? "subagent"}:${index}`)) : hasTasks && hasSubagents ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
1080
- taskBlocks.map((block) => /* @__PURE__ */ jsx3(TaskStatusLine, { block, interactive }, block.id)),
1081
- /* @__PURE__ */ jsx3(TrackHeader, { role: "subagent" }),
1082
- subagentBlocks.map((block) => /* @__PURE__ */ jsx3(TaskStatusLine, { block, interactive }, block.id))
1083
- ] }) : (hasTasks ? taskBlocks : subagentBlocks).map((block) => /* @__PURE__ */ jsx3(TaskStatusLine, { block, interactive }, block.id))
1084
- }
1085
- ) : null
1086
- ]
1152
+ onMouseLeave: () => onHoverChange?.(null),
1153
+ children: split ? /* @__PURE__ */ jsx3(
1154
+ SplitTracks,
1155
+ {
1156
+ taskBlocks,
1157
+ subagentBlocks,
1158
+ interactive,
1159
+ height: maxHeight,
1160
+ columns,
1161
+ mainScrollRef,
1162
+ subagentScrollRef,
1163
+ hoverProps
1164
+ }
1165
+ ) : hasTasks && hasSubagents ? /* @__PURE__ */ jsx3(
1166
+ StackedTracks,
1167
+ {
1168
+ taskBlocks,
1169
+ subagentBlocks,
1170
+ interactive,
1171
+ height: maxHeight,
1172
+ columns,
1173
+ mainScrollRef,
1174
+ subagentScrollRef,
1175
+ hoverProps
1176
+ }
1177
+ ) : /* @__PURE__ */ jsx3(
1178
+ SingleTrack,
1179
+ {
1180
+ role: hasTasks ? "main" : "subagent",
1181
+ blocks: hasTasks ? taskBlocks : subagentBlocks,
1182
+ interactive,
1183
+ height: maxHeight,
1184
+ width: columns,
1185
+ scrollRef: hasTasks ? mainScrollRef : subagentScrollRef,
1186
+ hoverProps
1187
+ }
1188
+ )
1087
1189
  }
1088
1190
  );
1089
1191
  }
1090
- function TrackHeader({ role }) {
1091
- return /* @__PURE__ */ jsxs2(Text, { dimColor: true, children: [
1092
- "\u2500\u2500 ",
1093
- role === "main" ? "task plan" : "subagent exploration",
1094
- " \u2500\u2500"
1192
+ function SplitTracks({
1193
+ taskBlocks,
1194
+ subagentBlocks,
1195
+ interactive,
1196
+ height,
1197
+ columns,
1198
+ mainScrollRef,
1199
+ subagentScrollRef,
1200
+ hoverProps
1201
+ }) {
1202
+ const contentHeight = Math.max(0, height - 1);
1203
+ const availableWidth = columns - SPLIT_TRACK_GUTTER_COLUMNS;
1204
+ const leftWidth = Math.max(1, Math.floor(availableWidth * SPLIT_TRACK_MAIN_RATIO));
1205
+ const rightWidth = Math.max(1, availableWidth - leftWidth);
1206
+ return /* @__PURE__ */ jsxs2(Fragment2, { children: [
1207
+ /* @__PURE__ */ jsxs2(Box_default, { flexDirection: "row", width: "100%", children: [
1208
+ /* @__PURE__ */ jsx3(Box_default, { width: leftWidth, ...hoverProps("main"), children: /* @__PURE__ */ jsx3(TrackHeader, { role: "main", count: taskBlocks.length, width: leftWidth }) }),
1209
+ /* @__PURE__ */ jsx3(Text, { color: "ansi:white", children: "\u2500\u252C\u2500" }),
1210
+ /* @__PURE__ */ jsx3(Box_default, { width: rightWidth, ...hoverProps("subagent"), children: /* @__PURE__ */ jsx3(TrackHeader, { role: "subagent", count: subagentBlocks.length, width: rightWidth }) })
1211
+ ] }),
1212
+ contentHeight <= 0 ? null : /* @__PURE__ */ jsxs2(Box_default, { flexDirection: "row", width: "100%", height: contentHeight, children: [
1213
+ /* @__PURE__ */ jsx3(Box_default, { width: leftWidth, minWidth: 0, flexDirection: "column", ...hoverProps("main"), children: /* @__PURE__ */ jsx3(
1214
+ TrackScrollBox,
1215
+ {
1216
+ ref: mainScrollRef,
1217
+ blocks: taskBlocks,
1218
+ interactive,
1219
+ height: contentHeight,
1220
+ width: leftWidth,
1221
+ maxTextLines: 2
1222
+ }
1223
+ ) }),
1224
+ /* @__PURE__ */ jsx3(TrackDivider, { height: contentHeight }),
1225
+ /* @__PURE__ */ jsx3(Box_default, { width: rightWidth, minWidth: 0, flexDirection: "column", ...hoverProps("subagent"), children: /* @__PURE__ */ jsx3(
1226
+ TrackScrollBox,
1227
+ {
1228
+ ref: subagentScrollRef,
1229
+ blocks: subagentBlocks,
1230
+ interactive,
1231
+ height: contentHeight,
1232
+ width: rightWidth,
1233
+ maxTextLines: 2
1234
+ }
1235
+ ) })
1236
+ ] })
1237
+ ] });
1238
+ }
1239
+ function StackedTracks({
1240
+ taskBlocks,
1241
+ subagentBlocks,
1242
+ interactive,
1243
+ height,
1244
+ columns,
1245
+ mainScrollRef,
1246
+ subagentScrollRef,
1247
+ hoverProps
1248
+ }) {
1249
+ if (height <= 1) {
1250
+ return /* @__PURE__ */ jsx3(Box_default, { ...hoverProps("main"), children: /* @__PURE__ */ jsx3(TrackHeader, { role: "main", count: taskBlocks.length, width: columns }) });
1251
+ }
1252
+ const mainHeight = Math.ceil(height / 2);
1253
+ const subagentHeight = Math.floor(height / 2);
1254
+ return /* @__PURE__ */ jsxs2(Fragment2, { children: [
1255
+ /* @__PURE__ */ jsx3(
1256
+ SingleTrack,
1257
+ {
1258
+ role: "main",
1259
+ blocks: taskBlocks,
1260
+ interactive,
1261
+ height: mainHeight,
1262
+ width: columns,
1263
+ scrollRef: mainScrollRef,
1264
+ hoverProps
1265
+ }
1266
+ ),
1267
+ /* @__PURE__ */ jsx3(
1268
+ SingleTrack,
1269
+ {
1270
+ role: "subagent",
1271
+ blocks: subagentBlocks,
1272
+ interactive,
1273
+ height: subagentHeight,
1274
+ width: columns,
1275
+ scrollRef: subagentScrollRef,
1276
+ hoverProps
1277
+ }
1278
+ )
1095
1279
  ] });
1096
1280
  }
1097
- function SplitTrackHeaders() {
1098
- return /* @__PURE__ */ jsxs2(Box_default, { flexDirection: "row", width: "100%", columnGap: 2, children: [
1099
- /* @__PURE__ */ jsx3(Box_default, { flexBasis: 0, flexGrow: 1, minWidth: 0, children: /* @__PURE__ */ jsx3(TrackHeader, { role: "main" }) }),
1100
- /* @__PURE__ */ jsx3(Box_default, { flexBasis: 0, flexGrow: 1, minWidth: 0, children: /* @__PURE__ */ jsx3(TrackHeader, { role: "subagent" }) })
1281
+ function SingleTrack({
1282
+ role,
1283
+ blocks,
1284
+ interactive,
1285
+ height,
1286
+ width,
1287
+ scrollRef,
1288
+ hoverProps
1289
+ }) {
1290
+ const contentHeight = Math.max(0, height - 1);
1291
+ return /* @__PURE__ */ jsxs2(Box_default, { flexDirection: "column", width: "100%", height, ...hoverProps(role), children: [
1292
+ /* @__PURE__ */ jsx3(TrackHeader, { role, count: blocks.length, width }),
1293
+ contentHeight <= 0 ? null : /* @__PURE__ */ jsx3(
1294
+ TrackScrollBox,
1295
+ {
1296
+ ref: scrollRef,
1297
+ blocks,
1298
+ interactive,
1299
+ height: contentHeight,
1300
+ width,
1301
+ maxTextLines: 3
1302
+ }
1303
+ )
1101
1304
  ] });
1102
1305
  }
1103
- function pairedTrackRows(tasks, subagents) {
1104
- return Array.from({ length: Math.max(tasks.length, subagents.length) }, (_, index) => [tasks[index], subagents[index]]);
1306
+ function TrackScrollBox({
1307
+ ref,
1308
+ blocks,
1309
+ interactive,
1310
+ height,
1311
+ width,
1312
+ maxTextLines
1313
+ }) {
1314
+ return /* @__PURE__ */ jsx3(
1315
+ ScrollBox_default,
1316
+ {
1317
+ ...ref === void 0 ? {} : { ref },
1318
+ flexDirection: "column",
1319
+ flexShrink: 1,
1320
+ width: "100%",
1321
+ height,
1322
+ maxHeight: height,
1323
+ paddingX: 1,
1324
+ children: blocks.map((block) => /* @__PURE__ */ jsx3(
1325
+ TaskStatusLine,
1326
+ {
1327
+ block,
1328
+ interactive,
1329
+ textWidth: Math.max(3, width - 2),
1330
+ maxTextLines,
1331
+ showRoleBadge: false
1332
+ },
1333
+ block.id
1334
+ ))
1335
+ }
1336
+ );
1337
+ }
1338
+ function TrackHeader({ role, count, width }) {
1339
+ const label = role === "main" ? `task plan (${count})` : `subagents (${count})`;
1340
+ const prefix = `\u2500\u2500 ${label} `;
1341
+ const line = prefix.length >= width ? prefix.slice(0, Math.max(0, width)) : `${prefix}${"\u2500".repeat(width - prefix.length)}`;
1342
+ return /* @__PURE__ */ jsx3(Text, { color: "ansi:white", wrap: "truncate-end", children: line });
1343
+ }
1344
+ function TrackDivider({ height }) {
1345
+ return /* @__PURE__ */ jsx3(Box_default, { flexDirection: "column", width: SPLIT_TRACK_GUTTER_COLUMNS, flexShrink: 0, children: Array.from({ length: height }, (_, index) => /* @__PURE__ */ jsx3(Text, { color: "ansi:white", children: " \u2502 " }, index)) });
1105
1346
  }
1106
1347
 
1107
1348
  // src/ui/file-diff-style.ts
@@ -1117,6 +1358,12 @@ function fileDiffLineStyle(kind) {
1117
1358
  }
1118
1359
 
1119
1360
  // src/ui/slash-completion.ts
1361
+ var CANDIDATE_KIND_PRIORITY = {
1362
+ skill: 0,
1363
+ plugin: 0,
1364
+ command: 1,
1365
+ tool: 2
1366
+ };
1120
1367
  function slashCandidatePresentation(selected) {
1121
1368
  return {
1122
1369
  marker: selected ? "\u203A " : " ",
@@ -1177,6 +1424,8 @@ function deriveSlashCompletion(input, cursor, candidates, selectedIndex, visible
1177
1424
  const leftPrefix = left.name.toLowerCase().startsWith(normalized);
1178
1425
  const rightPrefix = right.name.toLowerCase().startsWith(normalized);
1179
1426
  if (leftPrefix !== rightPrefix) return leftPrefix ? -1 : 1;
1427
+ const kindPriority = CANDIDATE_KIND_PRIORITY[left.kind] - CANDIDATE_KIND_PRIORITY[right.kind];
1428
+ if (kindPriority !== 0) return kindPriority;
1180
1429
  return left.name.localeCompare(right.name);
1181
1430
  });
1182
1431
  if (items.length === 0) return null;
@@ -1651,26 +1900,115 @@ function timestamp(value) {
1651
1900
  return `${year}${month}${day}-${hour}${minute}${second}`;
1652
1901
  }
1653
1902
 
1903
+ // src/ui/prompt-history.ts
1904
+ import { createHash as createHash2, randomUUID as randomUUID2 } from "crypto";
1905
+ import { mkdir as mkdir2, readFile, rename as rename2, writeFile } from "fs/promises";
1906
+ import { join as join2, resolve as resolve2 } from "path";
1907
+ var HISTORY_VERSION = 1;
1908
+ var PromptHistoryStore = class {
1909
+ #directory;
1910
+ #path;
1911
+ #workspace;
1912
+ #maxEntries;
1913
+ #writeChain = Promise.resolve();
1914
+ constructor(options) {
1915
+ this.#workspace = resolve2(options.workspace);
1916
+ this.#maxEntries = Math.max(1, Math.floor(options.maxEntries ?? 200));
1917
+ this.#directory = join2(resolve2(options.home), ".flavor-code", "history");
1918
+ const key = createHash2("sha256").update(this.#workspace).digest("hex").slice(0, 24);
1919
+ this.#path = join2(this.#directory, `${key}.json`);
1920
+ }
1921
+ async load() {
1922
+ try {
1923
+ const parsed = JSON.parse(await readFile(this.#path, "utf8"));
1924
+ if (parsed.version !== HISTORY_VERSION || parsed.workspace !== this.#workspace || !Array.isArray(parsed.entries)) return [];
1925
+ return parsed.entries.filter((entry) => typeof entry === "string" && entry.trim().length > 0).slice(-this.#maxEntries);
1926
+ } catch {
1927
+ return [];
1928
+ }
1929
+ }
1930
+ append(entry) {
1931
+ const normalized = entry.trim();
1932
+ if (normalized.length === 0) return this.#writeChain;
1933
+ this.#writeChain = this.#writeChain.catch(() => void 0).then(async () => {
1934
+ const entries = [...await this.load(), normalized].slice(-this.#maxEntries);
1935
+ await mkdir2(this.#directory, { recursive: true, mode: 448 });
1936
+ const temporary = join2(this.#directory, `.${randomUUID2()}.tmp`);
1937
+ const document = { version: HISTORY_VERSION, workspace: this.#workspace, entries };
1938
+ await writeFile(temporary, `${JSON.stringify(document, null, 2)}
1939
+ `, { encoding: "utf8", mode: 384 });
1940
+ await rename2(temporary, this.#path);
1941
+ });
1942
+ return this.#writeChain;
1943
+ }
1944
+ };
1945
+
1654
1946
  // src/ui/app.tsx
1655
1947
  import { Fragment as Fragment3, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
1656
1948
  var HISTORY_CAP = 200;
1657
1949
  var BUILTIN_SLASH_CANDIDATES = MVP_COMMANDS.map((name) => ({ name, description: COMMAND_DESCRIPTIONS[name] }));
1658
1950
  var PROMPT_HORIZONTAL_PADDING = 1;
1951
+ function clonePromptDraft(state) {
1952
+ return {
1953
+ ...state,
1954
+ pastedBlocks: [...state.pastedBlocks],
1955
+ imageAttachments: [...state.imageAttachments]
1956
+ };
1957
+ }
1958
+ var PromptEditHistory = class {
1959
+ constructor(limit = 100) {
1960
+ this.limit = limit;
1961
+ }
1962
+ limit;
1963
+ #undo = [];
1964
+ #redo = [];
1965
+ record(previous, next) {
1966
+ if (previous.text === next.text && previous.pastedBlocks === next.pastedBlocks && previous.imageAttachments === next.imageAttachments) return;
1967
+ this.#undo.push(clonePromptDraft(previous));
1968
+ if (this.#undo.length > this.limit) this.#undo.shift();
1969
+ this.#redo.length = 0;
1970
+ }
1971
+ undo(current) {
1972
+ const previous = this.#undo.pop();
1973
+ if (previous === void 0) return void 0;
1974
+ this.#redo.push(clonePromptDraft(current));
1975
+ return clonePromptDraft(previous);
1976
+ }
1977
+ redo(current) {
1978
+ const next = this.#redo.pop();
1979
+ if (next === void 0) return void 0;
1980
+ this.#undo.push(clonePromptDraft(current));
1981
+ return clonePromptDraft(next);
1982
+ }
1983
+ reset() {
1984
+ this.#undo.length = 0;
1985
+ this.#redo.length = 0;
1986
+ }
1987
+ };
1988
+ function promptHistoryAction(character, key, platform = process.platform) {
1989
+ const modifier = platform === "darwin" ? key.super : key.ctrl;
1990
+ if (!modifier || character.toLowerCase() !== "z") return null;
1991
+ return key.shift ? "redo" : "undo";
1992
+ }
1993
+ function isCopyShortcut(character, key, platform = process.platform) {
1994
+ return character.toLowerCase() === "c" && (platform === "darwin" ? key.super : key.ctrl);
1995
+ }
1996
+ function isPlatformShortcut(character, key, letter, platform = process.platform) {
1997
+ if (character.toLowerCase() !== letter.toLowerCase()) return false;
1998
+ return platform === "darwin" ? key.super || key.ctrl : key.ctrl;
1999
+ }
1659
2000
  function removeLastCliImageOnBackspace(input, cursor, images) {
1660
2001
  if (input.length === 0 && cursor === 0 && images.length > 0) {
1661
2002
  return { handled: true, images: images.slice(0, -1) };
1662
2003
  }
1663
2004
  return { handled: false, images: [...images] };
1664
2005
  }
1665
- function prepareCliSubmission(input, images, activeSession) {
2006
+ function prepareCliSubmission(input, images) {
1666
2007
  const entered = input.trim();
1667
2008
  if (entered.length === 0 && images.length === 0) return { kind: "empty" };
1668
2009
  if (images.length > 0 && entered.startsWith("/")) {
1669
2010
  return { kind: "error", message: "Image attachments cannot be used with slash commands." };
1670
2011
  }
1671
- if (images.length > 0 && activeSession) {
1672
- return { kind: "error", message: "Images can only be attached to a new prompt." };
1673
- }
1674
2012
  const text = entered || "Analyze the attached image(s).";
1675
2013
  if (images.length === 0) return { kind: "ready", text, displayText: text };
1676
2014
  const content = [{ type: "text", text }, ...images];
@@ -1681,6 +2019,10 @@ function prepareCliSubmission(input, images, activeSession) {
1681
2019
  content
1682
2020
  };
1683
2021
  }
2022
+ function runningEscapeAction(activeSession, pendingCount) {
2023
+ if (!activeSession) return null;
2024
+ return pendingCount > 0 ? "restore-pending" : "interrupt";
2025
+ }
1684
2026
  function promptDelivery(activeSession, key) {
1685
2027
  if (!activeSession) return "prompt";
1686
2028
  return "followUp";
@@ -1694,45 +2036,91 @@ function resolvePromptDelivery(activeSession, key, input) {
1694
2036
  }
1695
2037
  return { delivery: promptDelivery(activeSession, key), prompt: input };
1696
2038
  }
1697
- var SinglePendingPrompt = class {
1698
- #value;
2039
+ function cloneQueuedPrompt(prompt) {
2040
+ return {
2041
+ text: prompt.text,
2042
+ displayText: prompt.displayText,
2043
+ ...prompt.content === void 0 ? {} : { content: [...prompt.content] }
2044
+ };
2045
+ }
2046
+ function normalizeQueuedPrompt(prompt) {
2047
+ if (typeof prompt === "string") {
2048
+ const text2 = prompt.trim();
2049
+ return text2.length === 0 ? void 0 : { text: text2, displayText: text2 };
2050
+ }
2051
+ const text = prompt.text.trim();
2052
+ if (text.length === 0) return void 0;
2053
+ const displayText = prompt.displayText.trim() || text;
2054
+ return cloneQueuedPrompt({
2055
+ text,
2056
+ displayText,
2057
+ ...prompt.content === void 0 ? {} : { content: prompt.content }
2058
+ });
2059
+ }
2060
+ function queuedPromptLabel(prompt) {
2061
+ const imageCount = prompt.content?.filter((block) => block.type === "image").length ?? 0;
2062
+ const text = prompt.text.replace(/\s+/gu, " ").trim();
2063
+ if (imageCount === 0) return text;
2064
+ return `${text} \xB7 ${imageCount} ${imageCount === 1 ? "image" : "images"}`;
2065
+ }
2066
+ function restoreQueuedPrompt(prompt) {
2067
+ const imageAttachments = prompt.content?.filter(
2068
+ (block) => block.type === "image"
2069
+ ) ?? [];
2070
+ return {
2071
+ text: prompt.text,
2072
+ cursor: [...prompt.text].length,
2073
+ pastedBlocks: [],
2074
+ imageAttachments
2075
+ };
2076
+ }
2077
+ var PendingPromptQueue = class {
2078
+ #values = [];
1699
2079
  get value() {
1700
- return this.#value;
2080
+ const value = this.#values.at(-1);
2081
+ return value === void 0 ? void 0 : cloneQueuedPrompt(value);
2082
+ }
2083
+ get values() {
2084
+ return this.#values.map(cloneQueuedPrompt);
2085
+ }
2086
+ get size() {
2087
+ return this.#values.length;
1701
2088
  }
1702
2089
  queue(prompt) {
1703
- const value = prompt.trim();
1704
- if (value.length === 0 || this.#value !== void 0) return false;
1705
- this.#value = value;
2090
+ const value = normalizeQueuedPrompt(prompt);
2091
+ if (value === void 0) return false;
2092
+ this.#values.push(value);
1706
2093
  return true;
1707
2094
  }
1708
2095
  cancel() {
1709
- const value = this.#value;
1710
- this.#value = void 0;
1711
- return value;
2096
+ const value = this.#values.pop();
2097
+ return value === void 0 ? void 0 : cloneQueuedPrompt(value);
1712
2098
  }
1713
2099
  take() {
1714
- return this.cancel();
2100
+ const value = this.#values.shift();
2101
+ return value === void 0 ? void 0 : cloneQueuedPrompt(value);
1715
2102
  }
1716
2103
  };
1717
2104
  async function runTerminalSubmissionChain(options) {
1718
- let prompt = options.initialPrompt;
1719
- let initial = true;
2105
+ let prompt = normalizeQueuedPrompt({
2106
+ text: options.initialPrompt,
2107
+ displayText: options.initialDisplayPrompt ?? options.initialPrompt,
2108
+ ...options.initialContent === void 0 ? {} : { content: options.initialContent }
2109
+ });
1720
2110
  while (prompt !== void 0) {
1721
- options.onStart(initial ? options.initialDisplayPrompt ?? prompt : prompt);
1722
- if (initial && options.initialSubmit !== void 0) {
2111
+ options.onStart(prompt.displayText);
2112
+ if (prompt.content === void 0) await submitSafely(options.session, prompt.text, options.report);
2113
+ else {
1723
2114
  try {
1724
- await options.initialSubmit(prompt);
2115
+ await options.session.submit({ text: prompt.text, content: prompt.content });
1725
2116
  } catch (error) {
1726
2117
  safeReport(options.report, safeUiError(error));
1727
2118
  }
1728
- } else {
1729
- await submitSafely(options.session, prompt, options.report);
1730
2119
  }
1731
2120
  options.onFinish();
1732
2121
  if (options.shouldContinue?.() === false) return;
1733
2122
  prompt = options.pending.take();
1734
- if (prompt !== void 0) options.onPendingConsumed();
1735
- initial = false;
2123
+ if (prompt !== void 0) options.onPendingConsumed(options.pending.values);
1736
2124
  }
1737
2125
  }
1738
2126
  function classifyTerminalInput(key) {
@@ -1755,6 +2143,9 @@ function completionKeyAction(key, menuOpen) {
1755
2143
  function slashKeyAction(key, completion) {
1756
2144
  return completionKeyAction(key, completion !== null);
1757
2145
  }
2146
+ function canShowSlashCompletion(_activeSession, input, dismissedInput, blockedByModal) {
2147
+ return dismissedInput !== input && !blockedByModal;
2148
+ }
1758
2149
  function appRuntimeOptions(props, output, onApprovalChange) {
1759
2150
  return {
1760
2151
  workspace: props.workspace,
@@ -1779,6 +2170,8 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
1779
2170
  const [clipboardNotice, setClipboardNotice] = useState2();
1780
2171
  const [history, setHistory] = useState2([]);
1781
2172
  const [historyCursor, setHistoryCursor] = useState2(0);
2173
+ const [approvalExpanded, setApprovalExpanded] = useState2(false);
2174
+ const [expandedOutput, setExpandedOutput] = useState2(false);
1782
2175
  const [promptCursor, setPromptCursor] = useState2(0);
1783
2176
  const [slashCandidates, setSlashCandidates] = useState2(
1784
2177
  () => buildSlashCandidates(BUILTIN_SLASH_CANDIDATES, [], [])
@@ -1788,7 +2181,7 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
1788
2181
  const [mentionCandidates, setMentionCandidates] = useState2([]);
1789
2182
  const [mentionSelection, setMentionSelection] = useState2(0);
1790
2183
  const [dismissedMentionInput, setDismissedMentionInput] = useState2();
1791
- const [pendingPrompt, setPendingPrompt] = useState2();
2184
+ const [pendingPrompts, setPendingPrompts] = useState2([]);
1792
2185
  const [revision, setRevision] = useState2(0);
1793
2186
  const [ideContext, setIdeContext] = useState2();
1794
2187
  const [questionIndex, setQuestionIndex] = useState2(0);
@@ -1799,11 +2192,12 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
1799
2192
  const [updateTo, setUpdateTo] = useState2();
1800
2193
  const [transcript, dispatch] = useReducer(transcriptReducer, void 0, createTranscriptState);
1801
2194
  const scrollRef = useRef4(null);
1802
- const taskScrollRef = useRef4(null);
1803
- const taskPanelHovered = useRef4(false);
2195
+ const mainTaskScrollRef = useRef4(null);
2196
+ const subagentTaskScrollRef = useRef4(null);
2197
+ const hoveredTaskTrack = useRef4(null);
1804
2198
  const runtimeRef = useRef4(void 0);
1805
2199
  const pendingPromptRef = useRef4(void 0);
1806
- pendingPromptRef.current ??= new SinglePendingPrompt();
2200
+ pendingPromptRef.current ??= new PendingPromptQueue();
1807
2201
  const closing = useRef4(false);
1808
2202
  const clipboardBusy = useRef4(false);
1809
2203
  const textBuf = useRef4({ pending: "", timer: null });
@@ -1814,8 +2208,15 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
1814
2208
  pastedBlocks,
1815
2209
  imageAttachments
1816
2210
  });
2211
+ const promptEditHistory = useRef4(new PromptEditHistory());
2212
+ const historyRef = useRef4([]);
2213
+ const historyCursorRef = useRef4(0);
2214
+ const historyDraftRef = useRef4(void 0);
2215
+ const reverseSearchRef = useRef4(void 0);
2216
+ const promptHistoryStoreRef = useRef4(void 0);
2217
+ promptHistoryStoreRef.current ??= new PromptHistoryStore({ home: home ?? homedir(), workspace, maxEntries: HISTORY_CAP });
1817
2218
  promptDraftRef.current = { text: input, cursor: promptCursor, pastedBlocks, imageAttachments };
1818
- const commitPromptDraft = (next) => {
2219
+ const commitPromptDraft = (next, recordHistory = true) => {
1819
2220
  const current = promptDraftRef.current;
1820
2221
  const resolved = {
1821
2222
  text: next.text,
@@ -1823,6 +2224,7 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
1823
2224
  pastedBlocks: next.pastedBlocks ?? current.pastedBlocks,
1824
2225
  imageAttachments: next.imageAttachments ?? current.imageAttachments
1825
2226
  };
2227
+ if (recordHistory) promptEditHistory.current.record(current, resolved);
1826
2228
  promptDraftRef.current = resolved;
1827
2229
  setInput(resolved.text);
1828
2230
  setPromptCursor(resolved.cursor);
@@ -1830,6 +2232,19 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
1830
2232
  if (next.imageAttachments !== void 0) setImageAttachments(next.imageAttachments);
1831
2233
  };
1832
2234
  useTerminalTitle("Flavor Code");
2235
+ useEffect3(() => {
2236
+ let disposed = false;
2237
+ void promptHistoryStoreRef.current.load().then((entries) => {
2238
+ if (disposed) return;
2239
+ historyRef.current = entries;
2240
+ historyCursorRef.current = entries.length;
2241
+ setHistory(entries);
2242
+ setHistoryCursor(entries.length);
2243
+ });
2244
+ return () => {
2245
+ disposed = true;
2246
+ };
2247
+ }, [workspace, home]);
1833
2248
  const flushText = () => {
1834
2249
  flushThinking();
1835
2250
  const t = textBuf.current;
@@ -2043,8 +2458,14 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2043
2458
  setQuestionAnswers({});
2044
2459
  setCustomQuestionActive(false);
2045
2460
  }, [questions]);
2461
+ useEffect3(() => setApprovalExpanded(false), [approval?.id]);
2046
2462
  const derivedSlashCompletion = deriveSlashCompletion(input, promptCursor, slashCandidates, slashSelection);
2047
- const slashCompletion = dismissedSlashInput === input || transcript.active !== void 0 || approval !== void 0 || questions !== void 0 ? null : derivedSlashCompletion;
2463
+ const slashCompletion = canShowSlashCompletion(
2464
+ transcript.active !== void 0,
2465
+ input,
2466
+ dismissedSlashInput,
2467
+ approval !== void 0 || questions !== void 0
2468
+ ) ? derivedSlashCompletion : null;
2048
2469
  const derivedMentionCompletion = slashCompletion === null ? deriveMentionCompletion(input, promptCursor, mentionCandidates, mentionSelection) : null;
2049
2470
  const mentionCompletion = dismissedMentionInput === input || transcript.active !== void 0 || approval !== void 0 || questions !== void 0 ? null : derivedMentionCompletion;
2050
2471
  const completedTokenLength = completedSlashTokenLength(input, slashCandidates, slashCompletion !== null);
@@ -2062,11 +2483,7 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2062
2483
  setClipboardNotice("Flavor is still starting; try pasting the image again.");
2063
2484
  return;
2064
2485
  }
2065
- if (transcript.active !== void 0) {
2066
- setClipboardNotice("Images can only be attached to a new prompt.");
2067
- return;
2068
- }
2069
- if (imageAttachments.length >= DEFAULT_MAX_IMAGES) {
2486
+ if (promptDraftRef.current.imageAttachments.length >= DEFAULT_MAX_IMAGES) {
2070
2487
  setClipboardNotice(`A prompt can contain at most ${DEFAULT_MAX_IMAGES} images.`);
2071
2488
  return;
2072
2489
  }
@@ -2080,14 +2497,15 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2080
2497
  return;
2081
2498
  }
2082
2499
  const incomingBytes = Buffer.byteLength(attachment.dataBase64, "base64");
2083
- const currentBytes = imageAttachments.reduce((sum, image) => sum + image.bytes, 0);
2500
+ const currentDraft = promptDraftRef.current;
2501
+ const currentBytes = currentDraft.imageAttachments.reduce((sum, image) => sum + image.bytes, 0);
2084
2502
  if (currentBytes + incomingBytes > DEFAULT_MAX_TOTAL_IMAGE_BYTES) {
2085
2503
  throw new Error(`Image attachments exceed the maximum total size of ${DEFAULT_MAX_TOTAL_IMAGE_BYTES} bytes`);
2086
2504
  }
2087
2505
  const [stored] = await new SessionAssetStore({ workspace }).store(active.sessionId, [attachment]);
2088
2506
  if (stored === void 0) throw new Error("Clipboard image could not be stored");
2089
- setImageAttachments((current) => [...current, stored]);
2090
- setClipboardNotice(`Added [Image #${imageAttachments.length + 1}] ${stored.name ?? basename3(stored.source.path)}`);
2507
+ commitPromptDraft({ ...currentDraft, imageAttachments: [...currentDraft.imageAttachments, stored] });
2508
+ setClipboardNotice(`Added [Image #${currentDraft.imageAttachments.length + 1}] ${stored.name ?? basename3(stored.source.path)}`);
2091
2509
  } catch (error) {
2092
2510
  setClipboardNotice(safeUiError(error));
2093
2511
  } finally {
@@ -2097,7 +2515,12 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2097
2515
  use_input_default((character, key, event) => {
2098
2516
  const terminalAction = classifyTerminalInput(key);
2099
2517
  if (terminalAction?.type === "scroll") {
2100
- const scroll = selectWheelScrollTarget(scrollRef.current, taskScrollRef.current, taskPanelHovered.current);
2518
+ const scroll = selectWheelScrollTarget(
2519
+ scrollRef.current,
2520
+ mainTaskScrollRef.current,
2521
+ subagentTaskScrollRef.current,
2522
+ hoveredTaskTrack.current
2523
+ );
2101
2524
  if (scroll !== null) {
2102
2525
  if (terminalAction.rows < 0) scrollUp(scroll, -terminalAction.rows);
2103
2526
  else scrollDown(scroll, terminalAction.rows);
@@ -2110,12 +2533,18 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2110
2533
  return;
2111
2534
  }
2112
2535
  const active = runtimeRef.current;
2113
- if (key.ctrl && character === "c") {
2114
- if (selection.hasSelection()) {
2115
- selection.copySelection();
2116
- } else {
2117
- interrupt();
2118
- }
2536
+ if (isCopyShortcut(character, key)) {
2537
+ if (selection.hasSelection()) selection.copySelection();
2538
+ else if (process.platform !== "darwin") interrupt();
2539
+ return;
2540
+ }
2541
+ if (key.ctrl && character.toLowerCase() === "c") {
2542
+ interrupt();
2543
+ return;
2544
+ }
2545
+ if (isPlatformShortcut(character, key, "o")) {
2546
+ setExpandedOutput((value) => !value);
2547
+ scrollRef.current?.scrollToBottom();
2119
2548
  return;
2120
2549
  }
2121
2550
  if (shouldReadClipboardImage(character, key, event.keypress.isPasted)) {
@@ -2123,8 +2552,15 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2123
2552
  return;
2124
2553
  }
2125
2554
  if (active?.approvals.pending !== void 0) {
2555
+ if (character.toLowerCase() === "v") setApprovalExpanded((value) => !value);
2126
2556
  if (character.toLowerCase() === "y") active.approvals.resolve("once");
2127
2557
  if (character.toLowerCase() === "n" || key.escape) active.approvals.resolve("deny");
2558
+ if (character.toLowerCase() === "e") {
2559
+ void Promise.resolve(active.services.setPermissionMode("acceptEdits")).then(() => {
2560
+ setRevision((value) => value + 1);
2561
+ setClipboardNotice("Permission mode changed to acceptEdits; review the current request, then allow or deny it.");
2562
+ }).catch((error) => dispatch({ type: "submit-error", message: safeUiError(error) }));
2563
+ }
2128
2564
  if (character.toLowerCase() === "a") {
2129
2565
  if (isDestructiveTool(active.approvals.pending.tool)) {
2130
2566
  active.approvals.resolve("once");
@@ -2188,23 +2624,35 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2188
2624
  setPromptCursor(0);
2189
2625
  return;
2190
2626
  }
2191
- if (!key.ctrl && !key.meta && character) setCustomQuestionActive(true);
2627
+ if (!key.ctrl && !key.meta && !key.super && character) setCustomQuestionActive(true);
2192
2628
  else return;
2193
2629
  }
2194
2630
  }
2195
- if (key.escape && transcript.active !== void 0) {
2196
- const restored = pendingPromptRef.current.cancel();
2197
- if (restored !== void 0) {
2198
- setPendingPrompt(void 0);
2199
- setInput(restored);
2200
- setPastedBlocks([]);
2201
- setPromptCursor([...restored].length);
2202
- setSlashSelection(0);
2203
- setDismissedSlashInput(void 0);
2204
- setMentionSelection(0);
2205
- setDismissedMentionInput(void 0);
2206
- return;
2207
- }
2631
+ const historyAction = promptHistoryAction(character, key);
2632
+ if (historyAction !== null) {
2633
+ const draft = promptDraftRef.current;
2634
+ const restored = historyAction === "undo" ? promptEditHistory.current.undo(draft) : promptEditHistory.current.redo(draft);
2635
+ if (restored !== void 0) commitPromptDraft(restored, false);
2636
+ setSlashSelection(0);
2637
+ setDismissedSlashInput(void 0);
2638
+ setMentionSelection(0);
2639
+ setDismissedMentionInput(void 0);
2640
+ return;
2641
+ }
2642
+ if (isPlatformShortcut(character, key, "r")) {
2643
+ const draft = promptDraftRef.current;
2644
+ if (historyCursorRef.current === historyRef.current.length) historyDraftRef.current = clonePromptDraft(draft);
2645
+ const previous = reverseSearchRef.current;
2646
+ const query = previous?.query ?? draft.text;
2647
+ const match = reverseSearchHistory(historyRef.current, query, previous?.cursor ?? historyRef.current.length);
2648
+ if (match !== void 0) {
2649
+ reverseSearchRef.current = { query, cursor: match.cursor };
2650
+ historyCursorRef.current = match.cursor;
2651
+ setHistoryCursor(match.cursor);
2652
+ commitPromptDraft({ text: match.input, cursor: match.promptCursor, pastedBlocks: [], imageAttachments: [] }, false);
2653
+ setClipboardNotice(`reverse-i-search: ${query || "(latest)"} \xB7 Ctrl/Cmd+R for older match`);
2654
+ } else setClipboardNotice(`No older history match for ${query || "(latest)"}.`);
2655
+ return;
2208
2656
  }
2209
2657
  const menuAction = slashKeyAction(key, slashCompletion);
2210
2658
  if (menuAction?.type === "select" && slashCompletion !== null) {
@@ -2215,8 +2663,7 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2215
2663
  const selected = slashCompletion.items[slashCompletion.selectedIndex];
2216
2664
  if (selected !== void 0) {
2217
2665
  const next = completeSlashSelection(input, promptCursor, selected.name);
2218
- setInput(next.text);
2219
- setPromptCursor(next.cursor);
2666
+ commitPromptDraft(next);
2220
2667
  setDismissedSlashInput(next.text);
2221
2668
  }
2222
2669
  return;
@@ -2239,9 +2686,28 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2239
2686
  setDismissedMentionInput(input);
2240
2687
  return;
2241
2688
  }
2689
+ if (key.escape && transcript.active !== void 0) {
2690
+ const escapeAction = runningEscapeAction(true, pendingPromptRef.current.size);
2691
+ if (escapeAction === "restore-pending") {
2692
+ const restored = pendingPromptRef.current.cancel();
2693
+ if (restored === void 0) return;
2694
+ setPendingPrompts([...pendingPromptRef.current.values]);
2695
+ const restoredDraft = restoreQueuedPrompt(restored);
2696
+ promptEditHistory.current.reset();
2697
+ commitPromptDraft(restoredDraft, false);
2698
+ setClipboardNotice(restoredDraft.imageAttachments.length === 0 ? void 0 : `Restored ${restoredDraft.imageAttachments.length} pending ${restoredDraft.imageAttachments.length === 1 ? "image" : "images"}.`);
2699
+ setSlashSelection(0);
2700
+ setDismissedSlashInput(void 0);
2701
+ setMentionSelection(0);
2702
+ setDismissedMentionInput(void 0);
2703
+ return;
2704
+ }
2705
+ interrupt();
2706
+ return;
2707
+ }
2242
2708
  if (key.return) {
2243
2709
  const images = [...imageAttachments];
2244
- const prepared = prepareCliSubmission(input, images, transcript.active !== void 0);
2710
+ const prepared = prepareCliSubmission(input, images);
2245
2711
  if (prepared.kind === "empty" || active === void 0) return;
2246
2712
  if (prepared.kind === "error") {
2247
2713
  setClipboardNotice(prepared.message);
@@ -2251,16 +2717,27 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2251
2717
  const { delivery, prompt } = resolvePromptDelivery(transcript.active !== void 0, key, submittedText);
2252
2718
  if (!prompt) return;
2253
2719
  if (delivery === "followUp" && transcript.active !== void 0) {
2254
- if (!pendingPromptRef.current.queue(prompt)) return;
2255
- setPendingPrompt(prompt);
2720
+ if (!pendingPromptRef.current.queue({
2721
+ text: prompt,
2722
+ displayText: prepared.content === void 0 ? prompt : prepared.displayText,
2723
+ ...prepared.content === void 0 ? {} : { content: prepared.content }
2724
+ })) return;
2725
+ setPendingPrompts([...pendingPromptRef.current.values]);
2256
2726
  }
2257
2727
  scrollRef.current?.scrollToBottom();
2258
- setHistory((current) => [...current, submittedText].slice(-HISTORY_CAP));
2259
- setHistoryCursor(history.length + 1);
2728
+ const nextHistory = [...historyRef.current, submittedText].slice(-HISTORY_CAP);
2729
+ historyRef.current = nextHistory;
2730
+ historyCursorRef.current = nextHistory.length;
2731
+ historyDraftRef.current = void 0;
2732
+ reverseSearchRef.current = void 0;
2733
+ setHistory(nextHistory);
2734
+ setHistoryCursor(nextHistory.length);
2735
+ void promptHistoryStoreRef.current.append(submittedText).catch(() => void 0);
2260
2736
  setInput("");
2261
2737
  setPastedBlocks([]);
2262
2738
  setImageAttachments([]);
2263
2739
  promptDraftRef.current = { text: "", cursor: 0, pastedBlocks: [], imageAttachments: [] };
2740
+ promptEditHistory.current.reset();
2264
2741
  setClipboardNotice(void 0);
2265
2742
  setPromptCursor(0);
2266
2743
  setSlashSelection(0);
@@ -2273,15 +2750,12 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2273
2750
  initialPrompt: prompt,
2274
2751
  ...prepared.content === void 0 ? {} : {
2275
2752
  initialDisplayPrompt: prepared.displayText,
2276
- initialSubmit: (text) => active.session.submit({
2277
- text,
2278
- content: prepared.content
2279
- })
2753
+ initialContent: prepared.content
2280
2754
  },
2281
2755
  pending: pendingPromptRef.current,
2282
2756
  onStart: (next) => dispatch({ type: "submit", prompt: next }),
2283
2757
  onFinish: () => dispatch({ type: "finish" }),
2284
- onPendingConsumed: () => setPendingPrompt(void 0),
2758
+ onPendingConsumed: (remaining) => setPendingPrompts([...remaining]),
2285
2759
  report: (error) => dispatch({ type: "submit-error", message: error }),
2286
2760
  shouldContinue: () => !closing.current
2287
2761
  });
@@ -2295,11 +2769,11 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2295
2769
  return;
2296
2770
  }
2297
2771
  if (key.backspace) {
2772
+ reverseSearchRef.current = void 0;
2298
2773
  const draft = promptDraftRef.current;
2299
2774
  const imageEdit = removeLastCliImageOnBackspace(draft.text, draft.cursor, draft.imageAttachments);
2300
2775
  if (imageEdit.handled) {
2301
- setImageAttachments(imageEdit.images);
2302
- promptDraftRef.current = { ...draft, imageAttachments: imageEdit.images };
2776
+ commitPromptDraft({ ...draft, imageAttachments: imageEdit.images });
2303
2777
  setClipboardNotice(imageEdit.images.length === 0 ? "Removed image attachment." : `Removed image attachment; ${imageEdit.images.length} remaining.`);
2304
2778
  return;
2305
2779
  }
@@ -2314,6 +2788,7 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2314
2788
  setMentionSelection(0);
2315
2789
  setDismissedMentionInput(void 0);
2316
2790
  } else if (key.delete) {
2791
+ reverseSearchRef.current = void 0;
2317
2792
  const draft = promptDraftRef.current;
2318
2793
  commitPromptDraft({ ...editPrompt({ text: draft.text, cursor: draft.cursor }, { type: "delete" }), pastedBlocks: draft.pastedBlocks });
2319
2794
  setSlashSelection(0);
@@ -2321,28 +2796,31 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2321
2796
  setMentionSelection(0);
2322
2797
  setDismissedMentionInput(void 0);
2323
2798
  } else if (key.leftArrow) {
2799
+ reverseSearchRef.current = void 0;
2324
2800
  const draft = promptDraftRef.current;
2325
2801
  commitPromptDraft({ ...editPrompt({ text: draft.text, cursor: draft.cursor }, { type: "left" }), pastedBlocks: draft.pastedBlocks });
2326
2802
  } else if (key.rightArrow) {
2803
+ reverseSearchRef.current = void 0;
2327
2804
  const draft = promptDraftRef.current;
2328
2805
  commitPromptDraft({ ...editPrompt({ text: draft.text, cursor: draft.cursor }, { type: "right" }), pastedBlocks: draft.pastedBlocks });
2329
- } else if (terminalAction?.type === "history" && terminalAction.direction === "up" && history.length) {
2330
- const next = navigateHistory({ history, cursor: historyCursor }, "up");
2331
- setHistoryCursor(next.cursor);
2332
- commitPromptDraft({ text: next.input, cursor: next.promptCursor, pastedBlocks: [] });
2333
- setSlashSelection(0);
2334
- setDismissedSlashInput(void 0);
2335
- setMentionSelection(0);
2336
- setDismissedMentionInput(void 0);
2337
- } else if (terminalAction?.type === "history" && terminalAction.direction === "down" && history.length) {
2338
- const next = navigateHistory({ history, cursor: historyCursor }, "down");
2806
+ } else if (terminalAction?.type === "history" && historyRef.current.length) {
2807
+ const next = navigatePromptHistory({
2808
+ history: historyRef.current,
2809
+ cursor: historyCursorRef.current,
2810
+ current: promptDraftRef.current,
2811
+ ...historyDraftRef.current === void 0 ? {} : { stashed: historyDraftRef.current }
2812
+ }, terminalAction.direction);
2813
+ historyCursorRef.current = next.cursor;
2814
+ historyDraftRef.current = next.stashed;
2815
+ reverseSearchRef.current = void 0;
2339
2816
  setHistoryCursor(next.cursor);
2340
- commitPromptDraft({ text: next.input, cursor: next.promptCursor, pastedBlocks: [] });
2817
+ commitPromptDraft(next.draft, false);
2341
2818
  setSlashSelection(0);
2342
2819
  setDismissedSlashInput(void 0);
2343
2820
  setMentionSelection(0);
2344
2821
  setDismissedMentionInput(void 0);
2345
- } else if (!key.ctrl && !key.meta && character) {
2822
+ } else if (!key.ctrl && !key.meta && !key.super && character) {
2823
+ reverseSearchRef.current = void 0;
2346
2824
  const draft = promptDraftRef.current;
2347
2825
  const nextPastedBlocks = /[\r\n]/u.test(character) ? [...draft.pastedBlocks, { id: draft.pastedBlocks.length + 1, text: character }] : draft.pastedBlocks;
2348
2826
  commitPromptDraft({
@@ -2388,17 +2866,20 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2388
2866
  ...ideContext === void 0 ? {} : { ideContext },
2389
2867
  completedSlashTokenLength: completedTokenLength,
2390
2868
  scrollRef,
2391
- taskScrollRef,
2392
- onTaskPanelHoverChange: (hovered) => {
2393
- taskPanelHovered.current = hovered;
2869
+ mainTaskScrollRef,
2870
+ subagentTaskScrollRef,
2871
+ onTaskPanelHoverChange: (track) => {
2872
+ hoveredTaskTrack.current = track;
2394
2873
  },
2395
2874
  ...slashCompletion === null ? {} : { completion: slashCompletion },
2396
2875
  ...mentionCompletion === null ? {} : { mentionCompletion, onMentionSelect: selectMention },
2397
2876
  ...approval === void 0 ? {} : { approval },
2877
+ approvalExpanded,
2878
+ expandedOutput,
2398
2879
  ...questions === void 0 ? {} : { questions },
2399
2880
  memoryReviews,
2400
2881
  memoryAutoDismissSeconds,
2401
- ...pendingPrompt === void 0 ? {} : { pendingPrompt },
2882
+ pendingPrompts,
2402
2883
  questionIndex,
2403
2884
  questionAnswers,
2404
2885
  customQuestionActive
@@ -2433,6 +2914,46 @@ var CLI_VISIBLE_TURN_LIMIT = 40;
2433
2914
  var CLI_VISIBLE_BLOCK_LIMIT = 320;
2434
2915
  var CLI_VISIBLE_BLOCKS_PER_TURN = 80;
2435
2916
  var CLI_VISIBLE_TEXT_CHARS = 32e3;
2917
+ function approvalDetailLines(approval) {
2918
+ const lines = [];
2919
+ if (approval.agent !== "main") lines.push(`Agent: ${approval.agent}`);
2920
+ if (approval.command !== void 0) {
2921
+ const command = [approval.command, ...(approval.args ?? []).map(formatApprovalArgument)].join(" ");
2922
+ lines.push(`Command: ${command}`);
2923
+ } else if (approval.args !== void 0 && approval.args.length > 0) {
2924
+ lines.push(`Arguments: ${approval.args.map(formatApprovalArgument).join(" ")}`);
2925
+ }
2926
+ if (approval.cwd !== void 0) lines.push(`Working directory: ${approval.cwd}`);
2927
+ for (const [index, path] of (approval.paths ?? []).entries()) {
2928
+ lines.push(`${approval.paths.length === 1 ? "Path" : `Path ${index + 1}`}: ${path}`);
2929
+ }
2930
+ const inputLines = approvalInputLines(approval.input);
2931
+ if (inputLines.length > 0) lines.push("Tool input:", ...inputLines.map((line) => ` ${line}`));
2932
+ if (lines.length === 0) lines.push("No additional parameters were supplied.");
2933
+ return lines;
2934
+ }
2935
+ function formatApprovalArgument(value) {
2936
+ return /\s|["']/u.test(value) ? JSON.stringify(value) : value;
2937
+ }
2938
+ function approvalInputLines(input) {
2939
+ if (input === void 0) return [];
2940
+ if (typeof input !== "object" || input === null || Array.isArray(input)) return [formatApprovalValue(input)];
2941
+ const lines = [];
2942
+ for (const [key, value] of Object.entries(input)) {
2943
+ if (typeof value === "string" && /[\r\n]/u.test(value)) {
2944
+ lines.push(`${key}: |`, ...value.replace(/\r\n/gu, "\n").split("\n").map((line) => ` ${line}`));
2945
+ } else lines.push(`${key}: ${formatApprovalValue(value)}`);
2946
+ }
2947
+ return lines;
2948
+ }
2949
+ function formatApprovalValue(value) {
2950
+ if (typeof value === "string") return value;
2951
+ try {
2952
+ return JSON.stringify(value);
2953
+ } catch {
2954
+ return String(value);
2955
+ }
2956
+ }
2436
2957
  function boundedCliDisplayText(text, limit = CLI_VISIBLE_TEXT_CHARS) {
2437
2958
  if (text.length <= limit) return text;
2438
2959
  const half = Math.floor((limit - 80) / 2);
@@ -2511,8 +3032,10 @@ function TerminalLayout({
2511
3032
  columns,
2512
3033
  rows = 24,
2513
3034
  activeSession,
2514
- pendingPrompt,
3035
+ pendingPrompts = [],
2515
3036
  approval,
3037
+ approvalExpanded = false,
3038
+ expandedOutput = false,
2516
3039
  questions,
2517
3040
  memoryReviews = [],
2518
3041
  memoryAutoDismissSeconds = 0,
@@ -2524,7 +3047,8 @@ function TerminalLayout({
2524
3047
  onMentionSelect,
2525
3048
  completedSlashTokenLength: tokenLength = 0,
2526
3049
  scrollRef,
2527
- taskScrollRef,
3050
+ mainTaskScrollRef,
3051
+ subagentTaskScrollRef,
2528
3052
  onTaskPanelHoverChange,
2529
3053
  onPromptCursorChange,
2530
3054
  ideContext
@@ -2542,11 +3066,12 @@ function TerminalLayout({
2542
3066
  (block) => !(block.kind === "status" && block.task !== void 0)
2543
3067
  )
2544
3068
  };
2545
- const activeWithoutTasks = rawActiveWithoutTasks === void 0 ? void 0 : boundedCliTurn(rawActiveWithoutTasks).turn;
2546
- const completedWindow = cliTranscriptWindow(completed);
3069
+ const activeWithoutTasks = rawActiveWithoutTasks === void 0 ? void 0 : expandedOutput ? rawActiveWithoutTasks : boundedCliTurn(rawActiveWithoutTasks).turn;
3070
+ const completedWindow = expandedOutput ? { turns: completed, hiddenTurns: 0, hiddenBlocks: 0 } : cliTranscriptWindow(completed);
2547
3071
  const questionRows = questions === void 0 ? 0 : 4 + (questions[questionIndex]?.options.length ?? 0) + questions.length * 2;
2548
3072
  const memoryReviewRows = memoryReviews.length === 0 ? 0 : 5;
2549
- const fixedBottomRows = (approval === void 0 ? 0 : 3) + questionRows + memoryReviewRows + menuRows + (pendingPrompt === void 0 ? 0 : 1) + imageAttachments.length + (clipboardNotice === void 0 ? 0 : 1) + 2;
3073
+ const approvalRows = approval === void 0 ? 0 : 3 + (approvalExpanded ? approvalDetailLines(approval).length : 0);
3074
+ const fixedBottomRows = approvalRows + questionRows + memoryReviewRows + menuRows + (pendingPrompts.length === 0 ? 0 : 1) + imageAttachments.length + (clipboardNotice === void 0 ? 0 : 1) + 2;
2550
3075
  const taskPanelRows = taskPanelViewportRows(rows, fixedBottomRows, activeTaskBlocks.length > 0);
2551
3076
  const availableBottomRows = Math.max(1, rows - taskPanelRows - 1);
2552
3077
  const bottomMaxRows = Math.min(availableBottomRows, Math.max(Math.floor(rows / 2), fixedBottomRows + 1));
@@ -2565,15 +3090,15 @@ function TerminalLayout({
2565
3090
  completedWindow.hiddenTurns,
2566
3091
  " earlier turns and ",
2567
3092
  completedWindow.hiddenBlocks,
2568
- " output items are outside the live render window"
3093
+ " output items hidden \xB7 Ctrl/Cmd+O expand all"
2569
3094
  ] }) : null,
2570
3095
  completedWindow.turns.map((turn, index) => /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "column", children: [
2571
3096
  index > 0 ? /* @__PURE__ */ jsx5(TurnSeparator, { width: columns }) : null,
2572
- /* @__PURE__ */ jsx5(TurnView, { turn, interactive: false, workspaceName })
3097
+ /* @__PURE__ */ jsx5(TurnView, { turn, interactive: false, workspaceName, expandedOutput })
2573
3098
  ] }, turn.id)),
2574
3099
  activeWithoutTasks === void 0 ? null : /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "column", children: [
2575
3100
  completedWindow.turns.length > 0 ? /* @__PURE__ */ jsx5(TurnSeparator, { width: columns }) : null,
2576
- /* @__PURE__ */ jsx5(TurnView, { turn: activeWithoutTasks, interactive: activeSession, workspaceName })
3101
+ /* @__PURE__ */ jsx5(TurnView, { turn: activeWithoutTasks, interactive: activeSession, workspaceName, expandedOutput })
2577
3102
  ] })
2578
3103
  ] }),
2579
3104
  /* @__PURE__ */ jsx5(
@@ -2583,7 +3108,8 @@ function TerminalLayout({
2583
3108
  interactive: activeSession,
2584
3109
  maxHeight: taskPanelRows,
2585
3110
  columns,
2586
- ...taskScrollRef === void 0 ? {} : { scrollRef: taskScrollRef },
3111
+ ...mainTaskScrollRef === void 0 ? {} : { mainScrollRef: mainTaskScrollRef },
3112
+ ...subagentTaskScrollRef === void 0 ? {} : { subagentScrollRef: subagentTaskScrollRef },
2587
3113
  ...onTaskPanelHoverChange === void 0 ? {} : { onHoverChange: onTaskPanelHoverChange }
2588
3114
  }
2589
3115
  ),
@@ -2597,18 +3123,30 @@ function TerminalLayout({
2597
3123
  "\u2502 ",
2598
3124
  approval.reason ?? "This action needs permission."
2599
3125
  ] }),
2600
- isDestructiveTool(approval.tool) ? /* @__PURE__ */ jsxs4(Text, { bold: true, color: "magenta", children: [
2601
- "\u2514\u2500 Allow? ",
3126
+ approvalExpanded ? approvalDetailLines(approval).map((line, index) => /* @__PURE__ */ jsxs4(Text, { color: "magentaBright", wrap: "truncate-end", children: [
3127
+ "\u2502 ",
3128
+ line
3129
+ ] }, `${approval.id}:detail:${index}`)) : null,
3130
+ isDestructiveTool(approval.tool) || approval.allowAlways === false ? /* @__PURE__ */ jsxs4(Text, { bold: true, color: "magenta", children: [
3131
+ "\u2514\u2500 ",
3132
+ /* @__PURE__ */ jsx5(Text, { color: "cyan", children: "v" }),
3133
+ "=details / ",
2602
3134
  /* @__PURE__ */ jsx5(Text, { color: "green", children: "y" }),
2603
3135
  "=once / ",
3136
+ /* @__PURE__ */ jsx5(Text, { color: "cyan", children: "e" }),
3137
+ "=accept edits / ",
2604
3138
  /* @__PURE__ */ jsx5(Text, { color: "red", children: "n" }),
2605
3139
  "=deny"
2606
3140
  ] }) : /* @__PURE__ */ jsxs4(Text, { bold: true, color: "magenta", children: [
2607
- "\u2514\u2500 Allow? ",
3141
+ "\u2514\u2500 ",
3142
+ /* @__PURE__ */ jsx5(Text, { color: "cyan", children: "v" }),
3143
+ "=details / ",
2608
3144
  /* @__PURE__ */ jsx5(Text, { color: "green", children: "y" }),
2609
3145
  "=once / ",
2610
3146
  /* @__PURE__ */ jsx5(Text, { color: "yellow", children: "a" }),
2611
3147
  "=same-type / ",
3148
+ /* @__PURE__ */ jsx5(Text, { color: "cyan", children: "e" }),
3149
+ "=accept edits / ",
2612
3150
  /* @__PURE__ */ jsx5(Text, { color: "red", children: "n" }),
2613
3151
  "=deny"
2614
3152
  ] })
@@ -2617,10 +3155,12 @@ function TerminalLayout({
2617
3155
  memoryReviews.length === 0 ? null : /* @__PURE__ */ jsx5(MemoryReviewCards, { reviews: memoryReviews, autoDismissSeconds: memoryAutoDismissSeconds }),
2618
3156
  completion === void 0 ? null : /* @__PURE__ */ jsx5(SlashMenu, { completion }),
2619
3157
  mentionCompletion === void 0 ? null : /* @__PURE__ */ jsx5(MentionMenu, { completion: mentionCompletion, ...onMentionSelect === void 0 ? {} : { onSelect: onMentionSelect } }),
2620
- pendingPrompt === void 0 ? null : /* @__PURE__ */ jsxs4(Text, { color: "yellow", wrap: "truncate-end", children: [
2621
- "Pending \xB7 ",
2622
- pendingPrompt,
2623
- " \xB7 Esc edit"
3158
+ pendingPrompts.length === 0 ? null : /* @__PURE__ */ jsxs4(Text, { color: "yellow", wrap: "truncate-end", children: [
3159
+ "Pending (",
3160
+ pendingPrompts.length,
3161
+ ") \xB7 ",
3162
+ queuedPromptLabel(pendingPrompts.at(-1)),
3163
+ " \xB7 Esc edit latest"
2624
3164
  ] }),
2625
3165
  /* @__PURE__ */ jsx5(Text, { dimColor: true, children: "\u2500".repeat(dividerWidth) }),
2626
3166
  imageAttachments.map((image, index) => /* @__PURE__ */ jsxs4(Text, { color: "cyan", wrap: "truncate-end", children: [
@@ -2645,7 +3185,7 @@ function TerminalLayout({
2645
3185
  /* @__PURE__ */ jsx5(
2646
3186
  FooterStatus,
2647
3187
  {
2648
- hint: activeSession ? "Ctrl+C cancel \xB7 Enter queue \xB7 Esc edit pending \xB7 /steer sends now" : completion !== void 0 ? "\u2191/\u2193 select \xB7 Tab complete \xB7 Esc close" : mentionCompletion !== void 0 ? "\u2191/\u2193 select \xB7 Tab complete \xB7 click choose \xB7 Esc close" : "Enter send \xB7 Ctrl/Cmd+V image \xB7 \u2191\u2193 history \xB7 Ctrl+C exit",
3188
+ hint: activeSession ? `Esc ${pendingPrompts.length > 0 ? "edit latest" : "stop"} \xB7 Ctrl+C cancel \xB7 Enter queue \xB7 Ctrl/Cmd+R history \xB7 Ctrl/Cmd+O ${expandedOutput ? "collapse" : "expand"} output` : completion !== void 0 ? "\u2191/\u2193 select \xB7 Tab complete \xB7 Esc close" : mentionCompletion !== void 0 ? "\u2191/\u2193 select \xB7 Tab complete \xB7 click choose \xB7 Esc close" : `Enter send \xB7 \u2191\u2193/Ctrl/Cmd+R history \xB7 Ctrl/Cmd+O ${expandedOutput ? "collapse" : "expand"} output \xB7 Ctrl+C exit`,
2649
3189
  ...ideContext === void 0 ? {} : { ideContext }
2650
3190
  }
2651
3191
  )
@@ -2749,6 +3289,7 @@ function SlashMenu({ completion }) {
2749
3289
  return /* @__PURE__ */ jsxs4(Text, { ...presentation.rowStyle, wrap: "truncate-end", children: [
2750
3290
  presentation.marker,
2751
3291
  /* @__PURE__ */ jsx5(HighlightedName, { name: candidate.name, query: completion.query, matchStyle: presentation.matchStyle }),
3292
+ candidate.kind === "skill" || candidate.kind === "plugin" ? /* @__PURE__ */ jsx5(Text, { color: candidate.kind === "skill" ? "magentaBright" : "cyanBright", children: ` ${candidate.kind}` }) : null,
2752
3293
  candidate.description === void 0 ? null : /* @__PURE__ */ jsx5(Text, { dimColor: true, children: ` ${candidate.description}` })
2753
3294
  ] }, `${candidate.kind}:${candidate.name}`);
2754
3295
  }) });
@@ -2804,8 +3345,10 @@ function jumpScroll(scroll, delta) {
2804
3345
  scroll.scrollTo(Math.max(0, target));
2805
3346
  }
2806
3347
  }
2807
- function selectWheelScrollTarget(transcript, tasks, taskPanelHovered) {
2808
- return taskPanelHovered && tasks !== null ? tasks : transcript;
3348
+ function selectWheelScrollTarget(transcript, mainTasks, subagentTasks, hoveredTrack) {
3349
+ if (hoveredTrack === "main" && mainTasks !== null) return mainTasks;
3350
+ if (hoveredTrack === "subagent" && subagentTasks !== null) return subagentTasks;
3351
+ return transcript;
2809
3352
  }
2810
3353
  function taskPanelViewportRows(rows, reservedBottomRows, hasTasks) {
2811
3354
  if (!hasTasks) return 0;
@@ -2829,12 +3372,13 @@ function TurnSeparator({ width }) {
2829
3372
  function TurnView({
2830
3373
  turn,
2831
3374
  interactive,
2832
- workspaceName
3375
+ workspaceName,
3376
+ expandedOutput
2833
3377
  }) {
2834
3378
  if (turn.kind === "compaction") {
2835
3379
  return /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "column", marginBottom: 1, borderStyle: "round", borderColor: "ansi:yellowBright", paddingX: 1, children: [
2836
3380
  /* @__PURE__ */ jsx5(Text, { color: "ansi:yellowBright", bold: true, children: turn.prompt }),
2837
- turn.blocks.map((block, index) => block.kind === "status" ? /* @__PURE__ */ jsx5(StatusBlockView, { block, interactive, workspaceName }, block.id) : /* @__PURE__ */ jsx5(Box_default, { children: /* @__PURE__ */ jsx5(AssistantText, { text: block.text }) }, `${turn.id}-text-${index}`))
3381
+ turn.blocks.map((block, index) => block.kind === "status" ? /* @__PURE__ */ jsx5(StatusBlockView, { block, interactive, workspaceName, expandedOutput }, block.id) : /* @__PURE__ */ jsx5(Box_default, { children: /* @__PURE__ */ jsx5(AssistantText, { text: block.text }) }, `${turn.id}-text-${index}`))
2838
3382
  ] });
2839
3383
  }
2840
3384
  if (turn.source?.kind === "pal") {
@@ -2851,7 +3395,7 @@ function TurnView({
2851
3395
  ] }),
2852
3396
  /* @__PURE__ */ jsx5(Text, { color: "ansi:whiteBright", children: turn.prompt })
2853
3397
  ] }),
2854
- /* @__PURE__ */ jsx5(Box_default, { flexDirection: "column", paddingLeft: 2, marginTop: 1, children: turn.blocks.map((block, index) => block.kind === "status" ? /* @__PURE__ */ jsx5(StatusBlockView, { block, interactive, workspaceName }, block.id) : /* @__PURE__ */ jsx5(Box_default, { marginBottom: 1, children: /* @__PURE__ */ jsx5(AssistantText, { text: block.text }) }, `${turn.id}-text-${index}`)) })
3398
+ /* @__PURE__ */ jsx5(Box_default, { flexDirection: "column", paddingLeft: 2, marginTop: 1, children: turn.blocks.map((block, index) => block.kind === "status" ? /* @__PURE__ */ jsx5(StatusBlockView, { block, interactive, workspaceName, expandedOutput }, block.id) : /* @__PURE__ */ jsx5(Box_default, { marginBottom: 1, children: /* @__PURE__ */ jsx5(AssistantText, { text: block.text }) }, `${turn.id}-text-${index}`)) })
2855
3399
  ] });
2856
3400
  }
2857
3401
  return /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "column", marginBottom: 1, children: [
@@ -2862,7 +3406,7 @@ function TurnView({
2862
3406
  turn.prompt
2863
3407
  ] })
2864
3408
  ] }),
2865
- /* @__PURE__ */ jsx5(Box_default, { flexDirection: "column", paddingLeft: 2, marginTop: 1, children: turn.blocks.map((block, index) => block.kind === "status" ? /* @__PURE__ */ jsx5(StatusBlockView, { block, interactive, workspaceName }, block.id) : /* @__PURE__ */ jsx5(Box_default, { marginBottom: 1, children: /* @__PURE__ */ jsx5(AssistantText, { text: block.text }) }, `${turn.id}-text-${index}`)) })
3409
+ /* @__PURE__ */ jsx5(Box_default, { flexDirection: "column", paddingLeft: 2, marginTop: 1, children: turn.blocks.map((block, index) => block.kind === "status" ? /* @__PURE__ */ jsx5(StatusBlockView, { block, interactive, workspaceName, expandedOutput }, block.id) : /* @__PURE__ */ jsx5(Box_default, { marginBottom: 1, children: /* @__PURE__ */ jsx5(AssistantText, { text: block.text }) }, `${turn.id}-text-${index}`)) })
2866
3410
  ] });
2867
3411
  }
2868
3412
  var DIFF_CONTENT = fileDiffLineStyle("context").contentColor;
@@ -3041,11 +3585,12 @@ function PromptLine({
3041
3585
  function StatusBlockView({
3042
3586
  block,
3043
3587
  interactive,
3044
- workspaceName
3588
+ workspaceName,
3589
+ expandedOutput
3045
3590
  }) {
3046
3591
  const visibleBlock = cliToolTitle(block);
3047
3592
  const outcome = cliToolOutcome(block);
3048
- const primary = visibleBlock.activity === "model" || visibleBlock.task !== void 0 ? /* @__PURE__ */ jsx5(TaskStatusLine, { block: visibleBlock, interactive }) : visibleBlock.state === "completed" && visibleBlock.presentation !== void 0 ? /* @__PURE__ */ jsx5(ToolPresentationView, { presentation: visibleBlock.presentation, workspaceName }) : /* @__PURE__ */ jsx5(StatusLine, { block: visibleBlock, interactive });
3593
+ const primary = visibleBlock.activity === "model" || visibleBlock.task !== void 0 ? /* @__PURE__ */ jsx5(TaskStatusLine, { block: visibleBlock, interactive }) : visibleBlock.state === "completed" && visibleBlock.presentation !== void 0 ? /* @__PURE__ */ jsx5(ToolPresentationView, { presentation: visibleBlock.presentation, workspaceName, expandedOutput }) : /* @__PURE__ */ jsx5(StatusLine, { block: visibleBlock, interactive });
3049
3594
  return /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "column", children: [
3050
3595
  primary,
3051
3596
  outcome === void 0 ? null : /* @__PURE__ */ jsx5(Box_default, { paddingLeft: 2, children: /* @__PURE__ */ jsxs4(Text, { dimColor: true, children: [
@@ -3057,13 +3602,14 @@ function StatusBlockView({
3057
3602
  }
3058
3603
  function ToolPresentationView({
3059
3604
  presentation,
3060
- workspaceName
3605
+ workspaceName,
3606
+ expandedOutput
3061
3607
  }) {
3062
- if (presentation.kind === "changeset") return /* @__PURE__ */ jsx5(ChangeSetPresentationView, { presentation, workspaceName });
3608
+ if (presentation.kind === "changeset") return /* @__PURE__ */ jsx5(ChangeSetPresentationView, { presentation, workspaceName, expanded: expandedOutput });
3063
3609
  if (presentation.kind === "file-change") return /* @__PURE__ */ jsx5(FileDiffView, { presentation });
3064
- if (presentation.kind === "terminal") return /* @__PURE__ */ jsx5(CommandPresentationView, { presentation });
3065
- if (presentation.kind === "web") return /* @__PURE__ */ jsx5(WebPresentationView, { presentation });
3066
- if (presentation.kind === "job") return /* @__PURE__ */ jsx5(JobPresentationView, { presentation });
3610
+ if (presentation.kind === "terminal") return /* @__PURE__ */ jsx5(CommandPresentationView, { presentation, expanded: expandedOutput });
3611
+ if (presentation.kind === "web") return /* @__PURE__ */ jsx5(WebPresentationView, { presentation, expanded: expandedOutput });
3612
+ if (presentation.kind === "job") return /* @__PURE__ */ jsx5(JobPresentationView, { presentation, expanded: expandedOutput });
3067
3613
  return /* @__PURE__ */ jsx5(Box_default, { paddingLeft: 2, children: /* @__PURE__ */ jsxs4(Text, { children: [
3068
3614
  presentation.title,
3069
3615
  presentation.summary ? ` \xB7 ${presentation.summary}` : ""
@@ -3073,9 +3619,10 @@ var CLI_CHANGESET_FILE_LIMIT = 8;
3073
3619
  var CHANGESET_TONE = "#b99bf8";
3074
3620
  function ChangeSetPresentationView({
3075
3621
  presentation,
3076
- workspaceName
3622
+ workspaceName,
3623
+ expanded
3077
3624
  }) {
3078
- const visibleFiles = presentation.files.slice(0, CLI_CHANGESET_FILE_LIMIT);
3625
+ const visibleFiles = expanded ? presentation.files : presentation.files.slice(0, CLI_CHANGESET_FILE_LIMIT);
3079
3626
  const added = presentation.files.reduce((total, file) => total + file.added, 0);
3080
3627
  const removed = presentation.files.reduce((total, file) => total + file.removed, 0);
3081
3628
  const fileLabel = presentation.files.length === 1 ? "FILE" : "FILES";
@@ -3144,14 +3691,15 @@ function workspaceRelativePath(value, workspaceName) {
3144
3691
  }
3145
3692
  var CLI_COMMAND_OUTPUT_LINE_LIMIT = 16;
3146
3693
  function CommandPresentationView({
3147
- presentation
3694
+ presentation,
3695
+ expanded
3148
3696
  }) {
3149
3697
  const state = presentation.state ?? commandState(presentation.exitCode);
3150
3698
  const tone = jobStateColor(state);
3151
3699
  const label = presentation.variant === "terminal" ? "TERMINAL" : "COMMAND";
3152
3700
  const hasStdout = jobOutputLines(presentation.stdout ?? "").length > 0;
3153
3701
  const hasStderr = jobOutputLines(presentation.stderr ?? "").length > 0;
3154
- const perStreamLimit = hasStdout && hasStderr ? Math.floor(CLI_COMMAND_OUTPUT_LINE_LIMIT / 2) : CLI_COMMAND_OUTPUT_LINE_LIMIT;
3702
+ const perStreamLimit = expanded ? Number.POSITIVE_INFINITY : hasStdout && hasStderr ? Math.floor(CLI_COMMAND_OUTPUT_LINE_LIMIT / 2) : CLI_COMMAND_OUTPUT_LINE_LIMIT;
3155
3703
  const stdout = boundedCommandLines(presentation.stdout ?? "", perStreamLimit);
3156
3704
  const stderr = boundedCommandLines(presentation.stderr ?? "", perStreamLimit);
3157
3705
  return /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "column", paddingLeft: 2, marginBottom: 1, children: [
@@ -3234,7 +3782,8 @@ function commandFooter(presentation, state) {
3234
3782
  }
3235
3783
  var CLI_WEB_RESULT_LIMIT = 5;
3236
3784
  function WebPresentationView({
3237
- presentation
3785
+ presentation,
3786
+ expanded
3238
3787
  }) {
3239
3788
  const items = presentation.items ?? [];
3240
3789
  if (presentation.items === void 0) return /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "column", paddingLeft: 2, marginBottom: 1, children: [
@@ -3252,7 +3801,7 @@ function WebPresentationView({
3252
3801
  /* @__PURE__ */ jsx5(Text, { dimColor: true, children: "Page content added to context" })
3253
3802
  ] })
3254
3803
  ] });
3255
- const visibleItems = items.slice(0, CLI_WEB_RESULT_LIMIT);
3804
+ const visibleItems = expanded ? items : items.slice(0, CLI_WEB_RESULT_LIMIT);
3256
3805
  const query = presentation.title.replace(/^Search:\s*/iu, "").trim();
3257
3806
  return /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "column", paddingLeft: 2, marginBottom: 1, children: [
3258
3807
  /* @__PURE__ */ jsxs4(Box_default, { children: [
@@ -3298,13 +3847,14 @@ function compactWebSource(value) {
3298
3847
  var CLI_JOB_LOG_LINE_LIMIT = 12;
3299
3848
  var CLI_JOB_LIST_LIMIT = 8;
3300
3849
  function JobPresentationView({
3301
- presentation
3850
+ presentation,
3851
+ expanded
3302
3852
  }) {
3303
- if (presentation.action === "list") return /* @__PURE__ */ jsx5(JobListPresentationView, { presentation });
3853
+ if (presentation.action === "list") return /* @__PURE__ */ jsx5(JobListPresentationView, { presentation, expanded });
3304
3854
  const state = presentation.state ?? "running";
3305
3855
  const tone = jobStateColor(state);
3306
3856
  const lines = jobOutputLines(presentation.output ?? "");
3307
- const visibleLines = lines.slice(-CLI_JOB_LOG_LINE_LIMIT);
3857
+ const visibleLines = expanded ? lines : lines.slice(-CLI_JOB_LOG_LINE_LIMIT);
3308
3858
  const hiddenLines = lines.length - visibleLines.length;
3309
3859
  const showLog = presentation.action === "read" || presentation.action === "kill" || lines.length > 0;
3310
3860
  return /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "column", paddingLeft: 2, marginBottom: 1, children: [
@@ -3353,10 +3903,11 @@ function JobPresentationView({
3353
3903
  ] });
3354
3904
  }
3355
3905
  function JobListPresentationView({
3356
- presentation
3906
+ presentation,
3907
+ expanded
3357
3908
  }) {
3358
3909
  const jobs = presentation.jobs ?? [];
3359
- const visibleJobs = jobs.slice(0, CLI_JOB_LIST_LIMIT);
3910
+ const visibleJobs = expanded ? jobs : jobs.slice(0, CLI_JOB_LIST_LIMIT);
3360
3911
  return /* @__PURE__ */ jsxs4(Box_default, { flexDirection: "column", paddingLeft: 2, marginBottom: 1, children: [
3361
3912
  /* @__PURE__ */ jsxs4(Box_default, { children: [
3362
3913
  /* @__PURE__ */ jsx5(Text, { color: "#d7a657", children: "\u250C\u2500 " }),
@@ -3624,6 +4175,22 @@ function navigateHistory(state, direction) {
3624
4175
  const input = state.history[cursor] ?? "";
3625
4176
  return { cursor, input, promptCursor: [...input].length };
3626
4177
  }
4178
+ function navigatePromptHistory(state, direction) {
4179
+ const stashed = direction === "up" && state.cursor === state.history.length ? clonePromptDraft(state.current) : state.stashed;
4180
+ const next = navigateHistory(state, direction);
4181
+ const draft = next.cursor === state.history.length && stashed !== void 0 ? clonePromptDraft(stashed) : { text: next.input, cursor: next.promptCursor, pastedBlocks: [], imageAttachments: [] };
4182
+ return { cursor: next.cursor, draft, ...stashed === void 0 ? {} : { stashed } };
4183
+ }
4184
+ function reverseSearchHistory(history, query, before = history.length) {
4185
+ const needle = query.toLocaleLowerCase();
4186
+ for (let cursor = Math.min(history.length, before) - 1; cursor >= 0; cursor -= 1) {
4187
+ const input = history[cursor];
4188
+ if (needle.length === 0 || input.toLocaleLowerCase().includes(needle)) {
4189
+ return { cursor, input, promptCursor: [...input].length };
4190
+ }
4191
+ }
4192
+ return void 0;
4193
+ }
3627
4194
  async function submitSafely(session, prompt, report) {
3628
4195
  try {
3629
4196
  await session.submit(prompt);
@@ -3682,12 +4249,15 @@ export {
3682
4249
  FooterStatus,
3683
4250
  HISTORY_CAP,
3684
4251
  MentionMenu,
4252
+ PendingPromptQueue,
4253
+ PromptEditHistory,
3685
4254
  PromptLine,
3686
4255
  SHUTDOWN_TIMEOUT_MS,
3687
- SinglePendingPrompt,
3688
4256
  TerminalLayout,
3689
4257
  appRuntimeOptions,
4258
+ approvalDetailLines,
3690
4259
  boundedCliTurn,
4260
+ canShowSlashCompletion,
3691
4261
  classifyTerminalInput,
3692
4262
  cliTranscriptWindow,
3693
4263
  closeAndDisposeRuntime,
@@ -3695,14 +4265,22 @@ export {
3695
4265
  editPrompt,
3696
4266
  editPromptWithPastedBlocks,
3697
4267
  ideFooterPresentation,
4268
+ isCopyShortcut,
4269
+ isPlatformShortcut,
3698
4270
  navigateHistory,
4271
+ navigatePromptHistory,
3699
4272
  pastedDraftPresentation,
3700
4273
  prepareCliSubmission,
3701
4274
  promptCursorFromClick,
3702
4275
  promptDelivery,
4276
+ promptHistoryAction,
4277
+ queuedPromptLabel,
3703
4278
  removeLastCliImageOnBackspace,
3704
4279
  resolvePromptDelivery,
4280
+ restoreQueuedPrompt,
4281
+ reverseSearchHistory,
3705
4282
  runTerminalSubmissionChain,
4283
+ runningEscapeAction,
3706
4284
  selectWheelScrollTarget,
3707
4285
  shutdownRuntime,
3708
4286
  slashKeyAction,