flavor-code 1.4.0-beta.11 → 1.4.0-beta.12

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-BYHO2CA5.js";
22
+ } from "./chunk-AOOVLNX4.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,
@@ -39,7 +39,7 @@ import {
39
39
  useStdout,
40
40
  use_app_default,
41
41
  use_input_default
42
- } from "./chunk-26T23AKR.js";
42
+ } from "./chunk-VMQWXQEC.js";
43
43
  import {
44
44
  execFileNoThrow,
45
45
  getGraphemeSegmenter
@@ -975,7 +975,8 @@ function TaskStatusLine({
975
975
  block,
976
976
  interactive,
977
977
  textWidth,
978
- maxTextLines
978
+ maxTextLines,
979
+ showRoleBadge = true
979
980
  }) {
980
981
  const running = block.state === "running";
981
982
  const { stdout } = useStdout();
@@ -989,6 +990,8 @@ function TaskStatusLine({
989
990
  const elapsed = block.startedAt === void 0 ? startedAt.current === void 0 ? 0 : Math.max(0, time - startedAt.current) : Math.max(0, Date.now() - block.startedAt);
990
991
  const presentation = statusPresentation(block, elapsed, foreground);
991
992
  const { metaColor, metaLabel, statusLabel, statusColor, text } = presentation;
993
+ const badge = showRoleBadge ? presentation.badge : void 0;
994
+ const badgeColor = presentation.badgeColor;
992
995
  let before = text;
993
996
  let after = "";
994
997
  if (statusLabel !== void 0) {
@@ -1016,7 +1019,7 @@ function TaskStatusLine({
1016
1019
  })() : void 0;
1017
1020
  const textSegments = [
1018
1021
  { text: `${presentation.glyph} `, color: presentation.color },
1019
- ...presentation.badge ? [{ text: `${presentation.badge} `, color: presentation.badgeColor }] : [],
1022
+ ...badge ? [{ text: `${badge} `, color: badgeColor }] : [],
1020
1023
  { text: before, color: presentation.color },
1021
1024
  ...statusLabel === void 0 ? [] : [
1022
1025
  { text: " \xB7 ", color: presentation.color },
@@ -1032,8 +1035,8 @@ function TaskStatusLine({
1032
1035
  presentation.glyph,
1033
1036
  " "
1034
1037
  ] }),
1035
- presentation.badge ? /* @__PURE__ */ jsxs2(Text, { color: presentation.badgeColor, children: [
1036
- presentation.badge,
1038
+ badge ? /* @__PURE__ */ jsxs2(Text, { color: badgeColor, children: [
1039
+ badge,
1037
1040
  " "
1038
1041
  ] }) : null,
1039
1042
  /* @__PURE__ */ jsx3(Text, { ...presentation.color === void 0 ? {} : { color: presentation.color }, children: before }),
@@ -1117,7 +1120,9 @@ function ThinkingLineView({ text }) {
1117
1120
  /* @__PURE__ */ jsx3(Text, { color: THINKING_TEXT, wrap: "truncate-end", children: text })
1118
1121
  ] });
1119
1122
  }
1120
- var SPLIT_TRACK_MIN_COLUMNS = 72;
1123
+ var SPLIT_TRACK_MIN_COLUMNS = 96;
1124
+ var SPLIT_TRACK_GUTTER_COLUMNS = 3;
1125
+ var SPLIT_TRACK_MAIN_RATIO = 0.44;
1121
1126
  function TaskProgressPanel({
1122
1127
  blocks,
1123
1128
  interactive,
@@ -1194,13 +1199,14 @@ function SplitTracks({
1194
1199
  hoverProps
1195
1200
  }) {
1196
1201
  const contentHeight = Math.max(0, height - 1);
1197
- const leftWidth = Math.max(1, Math.floor((columns - 1) / 2));
1198
- const rightWidth = Math.max(1, columns - leftWidth - 1);
1202
+ const availableWidth = columns - SPLIT_TRACK_GUTTER_COLUMNS;
1203
+ const leftWidth = Math.max(1, Math.floor(availableWidth * SPLIT_TRACK_MAIN_RATIO));
1204
+ const rightWidth = Math.max(1, availableWidth - leftWidth);
1199
1205
  return /* @__PURE__ */ jsxs2(Fragment2, { children: [
1200
1206
  /* @__PURE__ */ jsxs2(Box_default, { flexDirection: "row", width: "100%", children: [
1201
- /* @__PURE__ */ jsx3(Box_default, { width: leftWidth, ...hoverProps("main"), children: /* @__PURE__ */ jsx3(TrackHeader, { role: "main", width: leftWidth }) }),
1202
- /* @__PURE__ */ jsx3(Text, { color: "ansi:white", children: "\u252C" }),
1203
- /* @__PURE__ */ jsx3(Box_default, { width: rightWidth, ...hoverProps("subagent"), children: /* @__PURE__ */ jsx3(TrackHeader, { role: "subagent", width: rightWidth }) })
1207
+ /* @__PURE__ */ jsx3(Box_default, { width: leftWidth, ...hoverProps("main"), children: /* @__PURE__ */ jsx3(TrackHeader, { role: "main", count: taskBlocks.length, width: leftWidth }) }),
1208
+ /* @__PURE__ */ jsx3(Text, { color: "ansi:white", children: "\u2500\u252C\u2500" }),
1209
+ /* @__PURE__ */ jsx3(Box_default, { width: rightWidth, ...hoverProps("subagent"), children: /* @__PURE__ */ jsx3(TrackHeader, { role: "subagent", count: subagentBlocks.length, width: rightWidth }) })
1204
1210
  ] }),
1205
1211
  contentHeight <= 0 ? null : /* @__PURE__ */ jsxs2(Box_default, { flexDirection: "row", width: "100%", height: contentHeight, children: [
1206
1212
  /* @__PURE__ */ jsx3(Box_default, { width: leftWidth, minWidth: 0, flexDirection: "column", ...hoverProps("main"), children: /* @__PURE__ */ jsx3(
@@ -1210,7 +1216,8 @@ function SplitTracks({
1210
1216
  blocks: taskBlocks,
1211
1217
  interactive,
1212
1218
  height: contentHeight,
1213
- width: leftWidth
1219
+ width: leftWidth,
1220
+ maxTextLines: 2
1214
1221
  }
1215
1222
  ) }),
1216
1223
  /* @__PURE__ */ jsx3(TrackDivider, { height: contentHeight }),
@@ -1221,7 +1228,8 @@ function SplitTracks({
1221
1228
  blocks: subagentBlocks,
1222
1229
  interactive,
1223
1230
  height: contentHeight,
1224
- width: rightWidth
1231
+ width: rightWidth,
1232
+ maxTextLines: 2
1225
1233
  }
1226
1234
  ) })
1227
1235
  ] })
@@ -1238,7 +1246,7 @@ function StackedTracks({
1238
1246
  hoverProps
1239
1247
  }) {
1240
1248
  if (height <= 1) {
1241
- return /* @__PURE__ */ jsx3(Box_default, { ...hoverProps("main"), children: /* @__PURE__ */ jsx3(TrackHeader, { role: "main", width: columns }) });
1249
+ return /* @__PURE__ */ jsx3(Box_default, { ...hoverProps("main"), children: /* @__PURE__ */ jsx3(TrackHeader, { role: "main", count: taskBlocks.length, width: columns }) });
1242
1250
  }
1243
1251
  const mainHeight = Math.ceil(height / 2);
1244
1252
  const subagentHeight = Math.floor(height / 2);
@@ -1280,7 +1288,7 @@ function SingleTrack({
1280
1288
  }) {
1281
1289
  const contentHeight = Math.max(0, height - 1);
1282
1290
  return /* @__PURE__ */ jsxs2(Box_default, { flexDirection: "column", width: "100%", height, ...hoverProps(role), children: [
1283
- /* @__PURE__ */ jsx3(TrackHeader, { role, width }),
1291
+ /* @__PURE__ */ jsx3(TrackHeader, { role, count: blocks.length, width }),
1284
1292
  contentHeight <= 0 ? null : /* @__PURE__ */ jsx3(
1285
1293
  TrackScrollBox,
1286
1294
  {
@@ -1288,7 +1296,8 @@ function SingleTrack({
1288
1296
  blocks,
1289
1297
  interactive,
1290
1298
  height: contentHeight,
1291
- width
1299
+ width,
1300
+ maxTextLines: 3
1292
1301
  }
1293
1302
  )
1294
1303
  ] });
@@ -1298,7 +1307,8 @@ function TrackScrollBox({
1298
1307
  blocks,
1299
1308
  interactive,
1300
1309
  height,
1301
- width
1310
+ width,
1311
+ maxTextLines
1302
1312
  }) {
1303
1313
  return /* @__PURE__ */ jsx3(
1304
1314
  ScrollBox_default,
@@ -1309,27 +1319,29 @@ function TrackScrollBox({
1309
1319
  width: "100%",
1310
1320
  height,
1311
1321
  maxHeight: height,
1322
+ paddingX: 1,
1312
1323
  children: blocks.map((block) => /* @__PURE__ */ jsx3(
1313
1324
  TaskStatusLine,
1314
1325
  {
1315
1326
  block,
1316
1327
  interactive,
1317
- textWidth: width,
1318
- maxTextLines: 3
1328
+ textWidth: Math.max(3, width - 2),
1329
+ maxTextLines,
1330
+ showRoleBadge: false
1319
1331
  },
1320
1332
  block.id
1321
1333
  ))
1322
1334
  }
1323
1335
  );
1324
1336
  }
1325
- function TrackHeader({ role, width }) {
1326
- const label = role === "main" ? "task plan" : "subagent exploration";
1337
+ function TrackHeader({ role, count, width }) {
1338
+ const label = role === "main" ? `task plan (${count})` : `subagents (${count})`;
1327
1339
  const prefix = `\u2500\u2500 ${label} `;
1328
1340
  const line = prefix.length >= width ? prefix.slice(0, Math.max(0, width)) : `${prefix}${"\u2500".repeat(width - prefix.length)}`;
1329
1341
  return /* @__PURE__ */ jsx3(Text, { color: "ansi:white", wrap: "truncate-end", children: line });
1330
1342
  }
1331
1343
  function TrackDivider({ height }) {
1332
- return /* @__PURE__ */ jsx3(Box_default, { flexDirection: "column", width: 1, flexShrink: 0, children: Array.from({ length: height }, (_, index) => /* @__PURE__ */ jsx3(Text, { color: "ansi:white", children: "\u2502" }, index)) });
1344
+ 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)) });
1333
1345
  }
1334
1346
 
1335
1347
  // src/ui/file-diff-style.ts
@@ -1345,6 +1357,12 @@ function fileDiffLineStyle(kind) {
1345
1357
  }
1346
1358
 
1347
1359
  // src/ui/slash-completion.ts
1360
+ var CANDIDATE_KIND_PRIORITY = {
1361
+ skill: 0,
1362
+ plugin: 0,
1363
+ command: 1,
1364
+ tool: 2
1365
+ };
1348
1366
  function slashCandidatePresentation(selected) {
1349
1367
  return {
1350
1368
  marker: selected ? "\u203A " : " ",
@@ -1405,6 +1423,8 @@ function deriveSlashCompletion(input, cursor, candidates, selectedIndex, visible
1405
1423
  const leftPrefix = left.name.toLowerCase().startsWith(normalized);
1406
1424
  const rightPrefix = right.name.toLowerCase().startsWith(normalized);
1407
1425
  if (leftPrefix !== rightPrefix) return leftPrefix ? -1 : 1;
1426
+ const kindPriority = CANDIDATE_KIND_PRIORITY[left.kind] - CANDIDATE_KIND_PRIORITY[right.kind];
1427
+ if (kindPriority !== 0) return kindPriority;
1408
1428
  return left.name.localeCompare(right.name);
1409
1429
  });
1410
1430
  if (items.length === 0) return null;
@@ -1884,21 +1904,63 @@ import { Fragment as Fragment3, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-run
1884
1904
  var HISTORY_CAP = 200;
1885
1905
  var BUILTIN_SLASH_CANDIDATES = MVP_COMMANDS.map((name) => ({ name, description: COMMAND_DESCRIPTIONS[name] }));
1886
1906
  var PROMPT_HORIZONTAL_PADDING = 1;
1907
+ function clonePromptDraft(state) {
1908
+ return {
1909
+ ...state,
1910
+ pastedBlocks: [...state.pastedBlocks],
1911
+ imageAttachments: [...state.imageAttachments]
1912
+ };
1913
+ }
1914
+ var PromptEditHistory = class {
1915
+ constructor(limit = 100) {
1916
+ this.limit = limit;
1917
+ }
1918
+ limit;
1919
+ #undo = [];
1920
+ #redo = [];
1921
+ record(previous, next) {
1922
+ if (previous.text === next.text && previous.pastedBlocks === next.pastedBlocks && previous.imageAttachments === next.imageAttachments) return;
1923
+ this.#undo.push(clonePromptDraft(previous));
1924
+ if (this.#undo.length > this.limit) this.#undo.shift();
1925
+ this.#redo.length = 0;
1926
+ }
1927
+ undo(current) {
1928
+ const previous = this.#undo.pop();
1929
+ if (previous === void 0) return void 0;
1930
+ this.#redo.push(clonePromptDraft(current));
1931
+ return clonePromptDraft(previous);
1932
+ }
1933
+ redo(current) {
1934
+ const next = this.#redo.pop();
1935
+ if (next === void 0) return void 0;
1936
+ this.#undo.push(clonePromptDraft(current));
1937
+ return clonePromptDraft(next);
1938
+ }
1939
+ reset() {
1940
+ this.#undo.length = 0;
1941
+ this.#redo.length = 0;
1942
+ }
1943
+ };
1944
+ function promptHistoryAction(character, key, platform = process.platform) {
1945
+ const modifier = platform === "darwin" ? key.super : key.ctrl;
1946
+ if (!modifier || character.toLowerCase() !== "z") return null;
1947
+ return key.shift ? "redo" : "undo";
1948
+ }
1949
+ function isCopyShortcut(character, key, platform = process.platform) {
1950
+ return character.toLowerCase() === "c" && (platform === "darwin" ? key.super : key.ctrl);
1951
+ }
1887
1952
  function removeLastCliImageOnBackspace(input, cursor, images) {
1888
1953
  if (input.length === 0 && cursor === 0 && images.length > 0) {
1889
1954
  return { handled: true, images: images.slice(0, -1) };
1890
1955
  }
1891
1956
  return { handled: false, images: [...images] };
1892
1957
  }
1893
- function prepareCliSubmission(input, images, activeSession) {
1958
+ function prepareCliSubmission(input, images) {
1894
1959
  const entered = input.trim();
1895
1960
  if (entered.length === 0 && images.length === 0) return { kind: "empty" };
1896
1961
  if (images.length > 0 && entered.startsWith("/")) {
1897
1962
  return { kind: "error", message: "Image attachments cannot be used with slash commands." };
1898
1963
  }
1899
- if (images.length > 0 && activeSession) {
1900
- return { kind: "error", message: "Images can only be attached to a new prompt." };
1901
- }
1902
1964
  const text = entered || "Analyze the attached image(s).";
1903
1965
  if (images.length === 0) return { kind: "ready", text, displayText: text };
1904
1966
  const content = [{ type: "text", text }, ...images];
@@ -1922,45 +1984,91 @@ function resolvePromptDelivery(activeSession, key, input) {
1922
1984
  }
1923
1985
  return { delivery: promptDelivery(activeSession, key), prompt: input };
1924
1986
  }
1925
- var SinglePendingPrompt = class {
1926
- #value;
1987
+ function cloneQueuedPrompt(prompt) {
1988
+ return {
1989
+ text: prompt.text,
1990
+ displayText: prompt.displayText,
1991
+ ...prompt.content === void 0 ? {} : { content: [...prompt.content] }
1992
+ };
1993
+ }
1994
+ function normalizeQueuedPrompt(prompt) {
1995
+ if (typeof prompt === "string") {
1996
+ const text2 = prompt.trim();
1997
+ return text2.length === 0 ? void 0 : { text: text2, displayText: text2 };
1998
+ }
1999
+ const text = prompt.text.trim();
2000
+ if (text.length === 0) return void 0;
2001
+ const displayText = prompt.displayText.trim() || text;
2002
+ return cloneQueuedPrompt({
2003
+ text,
2004
+ displayText,
2005
+ ...prompt.content === void 0 ? {} : { content: prompt.content }
2006
+ });
2007
+ }
2008
+ function queuedPromptLabel(prompt) {
2009
+ const imageCount = prompt.content?.filter((block) => block.type === "image").length ?? 0;
2010
+ const text = prompt.text.replace(/\s+/gu, " ").trim();
2011
+ if (imageCount === 0) return text;
2012
+ return `${text} \xB7 ${imageCount} ${imageCount === 1 ? "image" : "images"}`;
2013
+ }
2014
+ function restoreQueuedPrompt(prompt) {
2015
+ const imageAttachments = prompt.content?.filter(
2016
+ (block) => block.type === "image"
2017
+ ) ?? [];
2018
+ return {
2019
+ text: prompt.text,
2020
+ cursor: [...prompt.text].length,
2021
+ pastedBlocks: [],
2022
+ imageAttachments
2023
+ };
2024
+ }
2025
+ var PendingPromptQueue = class {
2026
+ #values = [];
1927
2027
  get value() {
1928
- return this.#value;
2028
+ const value = this.#values.at(-1);
2029
+ return value === void 0 ? void 0 : cloneQueuedPrompt(value);
2030
+ }
2031
+ get values() {
2032
+ return this.#values.map(cloneQueuedPrompt);
2033
+ }
2034
+ get size() {
2035
+ return this.#values.length;
1929
2036
  }
1930
2037
  queue(prompt) {
1931
- const value = prompt.trim();
1932
- if (value.length === 0 || this.#value !== void 0) return false;
1933
- this.#value = value;
2038
+ const value = normalizeQueuedPrompt(prompt);
2039
+ if (value === void 0) return false;
2040
+ this.#values.push(value);
1934
2041
  return true;
1935
2042
  }
1936
2043
  cancel() {
1937
- const value = this.#value;
1938
- this.#value = void 0;
1939
- return value;
2044
+ const value = this.#values.pop();
2045
+ return value === void 0 ? void 0 : cloneQueuedPrompt(value);
1940
2046
  }
1941
2047
  take() {
1942
- return this.cancel();
2048
+ const value = this.#values.shift();
2049
+ return value === void 0 ? void 0 : cloneQueuedPrompt(value);
1943
2050
  }
1944
2051
  };
1945
2052
  async function runTerminalSubmissionChain(options) {
1946
- let prompt = options.initialPrompt;
1947
- let initial = true;
2053
+ let prompt = normalizeQueuedPrompt({
2054
+ text: options.initialPrompt,
2055
+ displayText: options.initialDisplayPrompt ?? options.initialPrompt,
2056
+ ...options.initialContent === void 0 ? {} : { content: options.initialContent }
2057
+ });
1948
2058
  while (prompt !== void 0) {
1949
- options.onStart(initial ? options.initialDisplayPrompt ?? prompt : prompt);
1950
- if (initial && options.initialSubmit !== void 0) {
2059
+ options.onStart(prompt.displayText);
2060
+ if (prompt.content === void 0) await submitSafely(options.session, prompt.text, options.report);
2061
+ else {
1951
2062
  try {
1952
- await options.initialSubmit(prompt);
2063
+ await options.session.submit({ text: prompt.text, content: prompt.content });
1953
2064
  } catch (error) {
1954
2065
  safeReport(options.report, safeUiError(error));
1955
2066
  }
1956
- } else {
1957
- await submitSafely(options.session, prompt, options.report);
1958
2067
  }
1959
2068
  options.onFinish();
1960
2069
  if (options.shouldContinue?.() === false) return;
1961
2070
  prompt = options.pending.take();
1962
- if (prompt !== void 0) options.onPendingConsumed();
1963
- initial = false;
2071
+ if (prompt !== void 0) options.onPendingConsumed(options.pending.values);
1964
2072
  }
1965
2073
  }
1966
2074
  function classifyTerminalInput(key) {
@@ -1983,6 +2091,9 @@ function completionKeyAction(key, menuOpen) {
1983
2091
  function slashKeyAction(key, completion) {
1984
2092
  return completionKeyAction(key, completion !== null);
1985
2093
  }
2094
+ function canShowSlashCompletion(_activeSession, input, dismissedInput, blockedByModal) {
2095
+ return dismissedInput !== input && !blockedByModal;
2096
+ }
1986
2097
  function appRuntimeOptions(props, output, onApprovalChange) {
1987
2098
  return {
1988
2099
  workspace: props.workspace,
@@ -2016,7 +2127,7 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2016
2127
  const [mentionCandidates, setMentionCandidates] = useState2([]);
2017
2128
  const [mentionSelection, setMentionSelection] = useState2(0);
2018
2129
  const [dismissedMentionInput, setDismissedMentionInput] = useState2();
2019
- const [pendingPrompt, setPendingPrompt] = useState2();
2130
+ const [pendingPrompts, setPendingPrompts] = useState2([]);
2020
2131
  const [revision, setRevision] = useState2(0);
2021
2132
  const [ideContext, setIdeContext] = useState2();
2022
2133
  const [questionIndex, setQuestionIndex] = useState2(0);
@@ -2032,7 +2143,7 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2032
2143
  const hoveredTaskTrack = useRef4(null);
2033
2144
  const runtimeRef = useRef4(void 0);
2034
2145
  const pendingPromptRef = useRef4(void 0);
2035
- pendingPromptRef.current ??= new SinglePendingPrompt();
2146
+ pendingPromptRef.current ??= new PendingPromptQueue();
2036
2147
  const closing = useRef4(false);
2037
2148
  const clipboardBusy = useRef4(false);
2038
2149
  const textBuf = useRef4({ pending: "", timer: null });
@@ -2043,8 +2154,9 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2043
2154
  pastedBlocks,
2044
2155
  imageAttachments
2045
2156
  });
2157
+ const promptEditHistory = useRef4(new PromptEditHistory());
2046
2158
  promptDraftRef.current = { text: input, cursor: promptCursor, pastedBlocks, imageAttachments };
2047
- const commitPromptDraft = (next) => {
2159
+ const commitPromptDraft = (next, recordHistory = true) => {
2048
2160
  const current = promptDraftRef.current;
2049
2161
  const resolved = {
2050
2162
  text: next.text,
@@ -2052,6 +2164,7 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2052
2164
  pastedBlocks: next.pastedBlocks ?? current.pastedBlocks,
2053
2165
  imageAttachments: next.imageAttachments ?? current.imageAttachments
2054
2166
  };
2167
+ if (recordHistory) promptEditHistory.current.record(current, resolved);
2055
2168
  promptDraftRef.current = resolved;
2056
2169
  setInput(resolved.text);
2057
2170
  setPromptCursor(resolved.cursor);
@@ -2273,7 +2386,12 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2273
2386
  setCustomQuestionActive(false);
2274
2387
  }, [questions]);
2275
2388
  const derivedSlashCompletion = deriveSlashCompletion(input, promptCursor, slashCandidates, slashSelection);
2276
- const slashCompletion = dismissedSlashInput === input || transcript.active !== void 0 || approval !== void 0 || questions !== void 0 ? null : derivedSlashCompletion;
2389
+ const slashCompletion = canShowSlashCompletion(
2390
+ transcript.active !== void 0,
2391
+ input,
2392
+ dismissedSlashInput,
2393
+ approval !== void 0 || questions !== void 0
2394
+ ) ? derivedSlashCompletion : null;
2277
2395
  const derivedMentionCompletion = slashCompletion === null ? deriveMentionCompletion(input, promptCursor, mentionCandidates, mentionSelection) : null;
2278
2396
  const mentionCompletion = dismissedMentionInput === input || transcript.active !== void 0 || approval !== void 0 || questions !== void 0 ? null : derivedMentionCompletion;
2279
2397
  const completedTokenLength = completedSlashTokenLength(input, slashCandidates, slashCompletion !== null);
@@ -2291,11 +2409,7 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2291
2409
  setClipboardNotice("Flavor is still starting; try pasting the image again.");
2292
2410
  return;
2293
2411
  }
2294
- if (transcript.active !== void 0) {
2295
- setClipboardNotice("Images can only be attached to a new prompt.");
2296
- return;
2297
- }
2298
- if (imageAttachments.length >= DEFAULT_MAX_IMAGES) {
2412
+ if (promptDraftRef.current.imageAttachments.length >= DEFAULT_MAX_IMAGES) {
2299
2413
  setClipboardNotice(`A prompt can contain at most ${DEFAULT_MAX_IMAGES} images.`);
2300
2414
  return;
2301
2415
  }
@@ -2309,14 +2423,15 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2309
2423
  return;
2310
2424
  }
2311
2425
  const incomingBytes = Buffer.byteLength(attachment.dataBase64, "base64");
2312
- const currentBytes = imageAttachments.reduce((sum, image) => sum + image.bytes, 0);
2426
+ const currentDraft = promptDraftRef.current;
2427
+ const currentBytes = currentDraft.imageAttachments.reduce((sum, image) => sum + image.bytes, 0);
2313
2428
  if (currentBytes + incomingBytes > DEFAULT_MAX_TOTAL_IMAGE_BYTES) {
2314
2429
  throw new Error(`Image attachments exceed the maximum total size of ${DEFAULT_MAX_TOTAL_IMAGE_BYTES} bytes`);
2315
2430
  }
2316
2431
  const [stored] = await new SessionAssetStore({ workspace }).store(active.sessionId, [attachment]);
2317
2432
  if (stored === void 0) throw new Error("Clipboard image could not be stored");
2318
- setImageAttachments((current) => [...current, stored]);
2319
- setClipboardNotice(`Added [Image #${imageAttachments.length + 1}] ${stored.name ?? basename3(stored.source.path)}`);
2433
+ commitPromptDraft({ ...currentDraft, imageAttachments: [...currentDraft.imageAttachments, stored] });
2434
+ setClipboardNotice(`Added [Image #${currentDraft.imageAttachments.length + 1}] ${stored.name ?? basename3(stored.source.path)}`);
2320
2435
  } catch (error) {
2321
2436
  setClipboardNotice(safeUiError(error));
2322
2437
  } finally {
@@ -2344,12 +2459,13 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2344
2459
  return;
2345
2460
  }
2346
2461
  const active = runtimeRef.current;
2347
- if (key.ctrl && character === "c") {
2348
- if (selection.hasSelection()) {
2349
- selection.copySelection();
2350
- } else {
2351
- interrupt();
2352
- }
2462
+ if (isCopyShortcut(character, key)) {
2463
+ if (selection.hasSelection()) selection.copySelection();
2464
+ else if (process.platform !== "darwin") interrupt();
2465
+ return;
2466
+ }
2467
+ if (key.ctrl && character.toLowerCase() === "c") {
2468
+ interrupt();
2353
2469
  return;
2354
2470
  }
2355
2471
  if (shouldReadClipboardImage(character, key, event.keypress.isPasted)) {
@@ -2422,23 +2538,20 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2422
2538
  setPromptCursor(0);
2423
2539
  return;
2424
2540
  }
2425
- if (!key.ctrl && !key.meta && character) setCustomQuestionActive(true);
2541
+ if (!key.ctrl && !key.meta && !key.super && character) setCustomQuestionActive(true);
2426
2542
  else return;
2427
2543
  }
2428
2544
  }
2429
- if (key.escape && transcript.active !== void 0) {
2430
- const restored = pendingPromptRef.current.cancel();
2431
- if (restored !== void 0) {
2432
- setPendingPrompt(void 0);
2433
- setInput(restored);
2434
- setPastedBlocks([]);
2435
- setPromptCursor([...restored].length);
2436
- setSlashSelection(0);
2437
- setDismissedSlashInput(void 0);
2438
- setMentionSelection(0);
2439
- setDismissedMentionInput(void 0);
2440
- return;
2441
- }
2545
+ const historyAction = promptHistoryAction(character, key);
2546
+ if (historyAction !== null) {
2547
+ const draft = promptDraftRef.current;
2548
+ const restored = historyAction === "undo" ? promptEditHistory.current.undo(draft) : promptEditHistory.current.redo(draft);
2549
+ if (restored !== void 0) commitPromptDraft(restored, false);
2550
+ setSlashSelection(0);
2551
+ setDismissedSlashInput(void 0);
2552
+ setMentionSelection(0);
2553
+ setDismissedMentionInput(void 0);
2554
+ return;
2442
2555
  }
2443
2556
  const menuAction = slashKeyAction(key, slashCompletion);
2444
2557
  if (menuAction?.type === "select" && slashCompletion !== null) {
@@ -2449,8 +2562,7 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2449
2562
  const selected = slashCompletion.items[slashCompletion.selectedIndex];
2450
2563
  if (selected !== void 0) {
2451
2564
  const next = completeSlashSelection(input, promptCursor, selected.name);
2452
- setInput(next.text);
2453
- setPromptCursor(next.cursor);
2565
+ commitPromptDraft(next);
2454
2566
  setDismissedSlashInput(next.text);
2455
2567
  }
2456
2568
  return;
@@ -2473,9 +2585,24 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2473
2585
  setDismissedMentionInput(input);
2474
2586
  return;
2475
2587
  }
2588
+ if (key.escape && transcript.active !== void 0) {
2589
+ const restored = pendingPromptRef.current.cancel();
2590
+ if (restored !== void 0) {
2591
+ setPendingPrompts([...pendingPromptRef.current.values]);
2592
+ const restoredDraft = restoreQueuedPrompt(restored);
2593
+ promptEditHistory.current.reset();
2594
+ commitPromptDraft(restoredDraft, false);
2595
+ setClipboardNotice(restoredDraft.imageAttachments.length === 0 ? void 0 : `Restored ${restoredDraft.imageAttachments.length} pending ${restoredDraft.imageAttachments.length === 1 ? "image" : "images"}.`);
2596
+ setSlashSelection(0);
2597
+ setDismissedSlashInput(void 0);
2598
+ setMentionSelection(0);
2599
+ setDismissedMentionInput(void 0);
2600
+ return;
2601
+ }
2602
+ }
2476
2603
  if (key.return) {
2477
2604
  const images = [...imageAttachments];
2478
- const prepared = prepareCliSubmission(input, images, transcript.active !== void 0);
2605
+ const prepared = prepareCliSubmission(input, images);
2479
2606
  if (prepared.kind === "empty" || active === void 0) return;
2480
2607
  if (prepared.kind === "error") {
2481
2608
  setClipboardNotice(prepared.message);
@@ -2485,8 +2612,12 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2485
2612
  const { delivery, prompt } = resolvePromptDelivery(transcript.active !== void 0, key, submittedText);
2486
2613
  if (!prompt) return;
2487
2614
  if (delivery === "followUp" && transcript.active !== void 0) {
2488
- if (!pendingPromptRef.current.queue(prompt)) return;
2489
- setPendingPrompt(prompt);
2615
+ if (!pendingPromptRef.current.queue({
2616
+ text: prompt,
2617
+ displayText: prepared.content === void 0 ? prompt : prepared.displayText,
2618
+ ...prepared.content === void 0 ? {} : { content: prepared.content }
2619
+ })) return;
2620
+ setPendingPrompts([...pendingPromptRef.current.values]);
2490
2621
  }
2491
2622
  scrollRef.current?.scrollToBottom();
2492
2623
  setHistory((current) => [...current, submittedText].slice(-HISTORY_CAP));
@@ -2495,6 +2626,7 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2495
2626
  setPastedBlocks([]);
2496
2627
  setImageAttachments([]);
2497
2628
  promptDraftRef.current = { text: "", cursor: 0, pastedBlocks: [], imageAttachments: [] };
2629
+ promptEditHistory.current.reset();
2498
2630
  setClipboardNotice(void 0);
2499
2631
  setPromptCursor(0);
2500
2632
  setSlashSelection(0);
@@ -2507,15 +2639,12 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2507
2639
  initialPrompt: prompt,
2508
2640
  ...prepared.content === void 0 ? {} : {
2509
2641
  initialDisplayPrompt: prepared.displayText,
2510
- initialSubmit: (text) => active.session.submit({
2511
- text,
2512
- content: prepared.content
2513
- })
2642
+ initialContent: prepared.content
2514
2643
  },
2515
2644
  pending: pendingPromptRef.current,
2516
2645
  onStart: (next) => dispatch({ type: "submit", prompt: next }),
2517
2646
  onFinish: () => dispatch({ type: "finish" }),
2518
- onPendingConsumed: () => setPendingPrompt(void 0),
2647
+ onPendingConsumed: (remaining) => setPendingPrompts([...remaining]),
2519
2648
  report: (error) => dispatch({ type: "submit-error", message: error }),
2520
2649
  shouldContinue: () => !closing.current
2521
2650
  });
@@ -2532,8 +2661,7 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2532
2661
  const draft = promptDraftRef.current;
2533
2662
  const imageEdit = removeLastCliImageOnBackspace(draft.text, draft.cursor, draft.imageAttachments);
2534
2663
  if (imageEdit.handled) {
2535
- setImageAttachments(imageEdit.images);
2536
- promptDraftRef.current = { ...draft, imageAttachments: imageEdit.images };
2664
+ commitPromptDraft({ ...draft, imageAttachments: imageEdit.images });
2537
2665
  setClipboardNotice(imageEdit.images.length === 0 ? "Removed image attachment." : `Removed image attachment; ${imageEdit.images.length} remaining.`);
2538
2666
  return;
2539
2667
  }
@@ -2576,7 +2704,7 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2576
2704
  setDismissedSlashInput(void 0);
2577
2705
  setMentionSelection(0);
2578
2706
  setDismissedMentionInput(void 0);
2579
- } else if (!key.ctrl && !key.meta && character) {
2707
+ } else if (!key.ctrl && !key.meta && !key.super && character) {
2580
2708
  const draft = promptDraftRef.current;
2581
2709
  const nextPastedBlocks = /[\r\n]/u.test(character) ? [...draft.pastedBlocks, { id: draft.pastedBlocks.length + 1, text: character }] : draft.pastedBlocks;
2582
2710
  commitPromptDraft({
@@ -2633,7 +2761,7 @@ function App({ workspace, home, resumeSession, instanceId, palAlias }) {
2633
2761
  ...questions === void 0 ? {} : { questions },
2634
2762
  memoryReviews,
2635
2763
  memoryAutoDismissSeconds,
2636
- ...pendingPrompt === void 0 ? {} : { pendingPrompt },
2764
+ pendingPrompts,
2637
2765
  questionIndex,
2638
2766
  questionAnswers,
2639
2767
  customQuestionActive
@@ -2746,7 +2874,7 @@ function TerminalLayout({
2746
2874
  columns,
2747
2875
  rows = 24,
2748
2876
  activeSession,
2749
- pendingPrompt,
2877
+ pendingPrompts = [],
2750
2878
  approval,
2751
2879
  questions,
2752
2880
  memoryReviews = [],
@@ -2782,7 +2910,7 @@ function TerminalLayout({
2782
2910
  const completedWindow = cliTranscriptWindow(completed);
2783
2911
  const questionRows = questions === void 0 ? 0 : 4 + (questions[questionIndex]?.options.length ?? 0) + questions.length * 2;
2784
2912
  const memoryReviewRows = memoryReviews.length === 0 ? 0 : 5;
2785
- const fixedBottomRows = (approval === void 0 ? 0 : 3) + questionRows + memoryReviewRows + menuRows + (pendingPrompt === void 0 ? 0 : 1) + imageAttachments.length + (clipboardNotice === void 0 ? 0 : 1) + 2;
2913
+ const fixedBottomRows = (approval === void 0 ? 0 : 3) + questionRows + memoryReviewRows + menuRows + (pendingPrompts.length === 0 ? 0 : 1) + imageAttachments.length + (clipboardNotice === void 0 ? 0 : 1) + 2;
2786
2914
  const taskPanelRows = taskPanelViewportRows(rows, fixedBottomRows, activeTaskBlocks.length > 0);
2787
2915
  const availableBottomRows = Math.max(1, rows - taskPanelRows - 1);
2788
2916
  const bottomMaxRows = Math.min(availableBottomRows, Math.max(Math.floor(rows / 2), fixedBottomRows + 1));
@@ -2854,10 +2982,12 @@ function TerminalLayout({
2854
2982
  memoryReviews.length === 0 ? null : /* @__PURE__ */ jsx5(MemoryReviewCards, { reviews: memoryReviews, autoDismissSeconds: memoryAutoDismissSeconds }),
2855
2983
  completion === void 0 ? null : /* @__PURE__ */ jsx5(SlashMenu, { completion }),
2856
2984
  mentionCompletion === void 0 ? null : /* @__PURE__ */ jsx5(MentionMenu, { completion: mentionCompletion, ...onMentionSelect === void 0 ? {} : { onSelect: onMentionSelect } }),
2857
- pendingPrompt === void 0 ? null : /* @__PURE__ */ jsxs4(Text, { color: "yellow", wrap: "truncate-end", children: [
2858
- "Pending \xB7 ",
2859
- pendingPrompt,
2860
- " \xB7 Esc edit"
2985
+ pendingPrompts.length === 0 ? null : /* @__PURE__ */ jsxs4(Text, { color: "yellow", wrap: "truncate-end", children: [
2986
+ "Pending (",
2987
+ pendingPrompts.length,
2988
+ ") \xB7 ",
2989
+ queuedPromptLabel(pendingPrompts.at(-1)),
2990
+ " \xB7 Esc edit latest"
2861
2991
  ] }),
2862
2992
  /* @__PURE__ */ jsx5(Text, { dimColor: true, children: "\u2500".repeat(dividerWidth) }),
2863
2993
  imageAttachments.map((image, index) => /* @__PURE__ */ jsxs4(Text, { color: "cyan", wrap: "truncate-end", children: [
@@ -2882,7 +3012,7 @@ function TerminalLayout({
2882
3012
  /* @__PURE__ */ jsx5(
2883
3013
  FooterStatus,
2884
3014
  {
2885
- 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",
3015
+ hint: activeSession ? "Ctrl+C cancel \xB7 Enter queue \xB7 Esc edit latest \xB7 / menu \xB7 Ctrl/Cmd+V image" : 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",
2886
3016
  ...ideContext === void 0 ? {} : { ideContext }
2887
3017
  }
2888
3018
  )
@@ -2986,6 +3116,7 @@ function SlashMenu({ completion }) {
2986
3116
  return /* @__PURE__ */ jsxs4(Text, { ...presentation.rowStyle, wrap: "truncate-end", children: [
2987
3117
  presentation.marker,
2988
3118
  /* @__PURE__ */ jsx5(HighlightedName, { name: candidate.name, query: completion.query, matchStyle: presentation.matchStyle }),
3119
+ candidate.kind === "skill" || candidate.kind === "plugin" ? /* @__PURE__ */ jsx5(Text, { color: candidate.kind === "skill" ? "magentaBright" : "cyanBright", children: ` ${candidate.kind}` }) : null,
2989
3120
  candidate.description === void 0 ? null : /* @__PURE__ */ jsx5(Text, { dimColor: true, children: ` ${candidate.description}` })
2990
3121
  ] }, `${candidate.kind}:${candidate.name}`);
2991
3122
  }) });
@@ -3921,12 +4052,14 @@ export {
3921
4052
  FooterStatus,
3922
4053
  HISTORY_CAP,
3923
4054
  MentionMenu,
4055
+ PendingPromptQueue,
4056
+ PromptEditHistory,
3924
4057
  PromptLine,
3925
4058
  SHUTDOWN_TIMEOUT_MS,
3926
- SinglePendingPrompt,
3927
4059
  TerminalLayout,
3928
4060
  appRuntimeOptions,
3929
4061
  boundedCliTurn,
4062
+ canShowSlashCompletion,
3930
4063
  classifyTerminalInput,
3931
4064
  cliTranscriptWindow,
3932
4065
  closeAndDisposeRuntime,
@@ -3934,13 +4067,17 @@ export {
3934
4067
  editPrompt,
3935
4068
  editPromptWithPastedBlocks,
3936
4069
  ideFooterPresentation,
4070
+ isCopyShortcut,
3937
4071
  navigateHistory,
3938
4072
  pastedDraftPresentation,
3939
4073
  prepareCliSubmission,
3940
4074
  promptCursorFromClick,
3941
4075
  promptDelivery,
4076
+ promptHistoryAction,
4077
+ queuedPromptLabel,
3942
4078
  removeLastCliImageOnBackspace,
3943
4079
  resolvePromptDelivery,
4080
+ restoreQueuedPrompt,
3944
4081
  runTerminalSubmissionChain,
3945
4082
  selectWheelScrollTarget,
3946
4083
  shutdownRuntime,