@ganakailabs/cloudeval-cli 0.19.2 → 0.19.4

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.
@@ -1,11 +1,14 @@
1
1
  import {
2
2
  buildFrontendUrl,
3
3
  getFirstNameForDisplay,
4
+ getSession,
5
+ listSessions,
6
+ recordSessionTurn,
4
7
  resolveFrontendBaseUrl
5
- } from "./chunk-TA4WC462.js";
8
+ } from "./chunk-RCRNSEQS.js";
6
9
  import {
7
10
  Onboarding
8
- } from "./chunk-3DVPEIVB.js";
11
+ } from "./chunk-2GTSKMHA.js";
9
12
  import {
10
13
  checkUserStatus,
11
14
  completeActiveAssistantMessage,
@@ -14,8 +17,11 @@ import {
14
17
  getAuthToken,
15
18
  getBillingEntitlement,
16
19
  getBillingNotifications,
20
+ getBillingUsageCreditsUsed,
17
21
  getBillingUsageLedger,
18
22
  getBillingUsageSummary,
23
+ getBundledAgentProfiles,
24
+ getCLIHeaders,
19
25
  getCostReportFull,
20
26
  getCreditStatus,
21
27
  getProjects,
@@ -23,28 +29,29 @@ import {
23
29
  getTopUpPacks,
24
30
  getWafReportFull,
25
31
  initialChatState,
32
+ isExpiredDeviceTokenStreamError,
26
33
  listConnections,
27
34
  normalizeApiBase,
28
35
  reduceChunk,
29
36
  runReports,
30
37
  streamChat
31
- } from "./chunk-4QIKW5TJ.js";
38
+ } from "./chunk-2D4BE3OS.js";
32
39
  import {
33
40
  Banner
34
- } from "./chunk-UXX36KJO.js";
41
+ } from "./chunk-L2H4P4BP.js";
35
42
  import {
36
43
  CLI_VERSION
37
- } from "./chunk-2O7XF47R.js";
44
+ } from "./chunk-HUE4D6RO.js";
38
45
  import {
39
46
  raisedButtonStyle,
40
47
  terminalTheme
41
- } from "./chunk-UOCT7M4J.js";
48
+ } from "./chunk-QKZCKI55.js";
42
49
 
43
50
  // src/ui/App.tsx
44
51
  import React8, { useEffect as useEffect4, useMemo, useState as useState4, startTransition } from "react";
45
52
  import { Box as Box9, Text as Text10, useApp, useInput as useInput4 } from "ink";
46
53
  import { ScrollView } from "ink-scroll-view";
47
- import { randomUUID } from "crypto";
54
+ import { randomUUID as randomUUID2 } from "crypto";
48
55
  import { spawn } from "child_process";
49
56
  import { mkdirSync, writeFileSync } from "fs";
50
57
  import { join, resolve } from "path";
@@ -594,7 +601,7 @@ var getTuiKeyBindings = (platform = process.platform) => ({
594
601
  newline: platform === "darwin" ? "Option+Enter or Ctrl+J newline" : "Alt+Enter or Ctrl+J newline",
595
602
  quit: "Ctrl+C quit",
596
603
  tabFocus: "Tab focus",
597
- tabSwitch: "Left/Right switch",
604
+ tabSwitch: "Arrow keys move focus",
598
605
  commandComplete: "Tab completes slash commands",
599
606
  historySearch: "Ctrl+R history search",
600
607
  cancel: "Esc cancel response",
@@ -605,15 +612,19 @@ var getTuiKeyBindings = (platform = process.platform) => ({
605
612
  });
606
613
  var getChatInputHelpText = ({
607
614
  isCancelling,
615
+ inputActive = true,
608
616
  promptCount = 0
609
617
  }) => {
610
618
  if (isCancelling) {
611
619
  return "Esc stop | Ctrl+C quit | /stop | /help";
612
620
  }
621
+ if (!inputActive) {
622
+ return "Type to edit | Tab/Arrows controls | 1-8 tabs | /help";
623
+ }
613
624
  if (promptCount <= 0) {
614
- return "Enter send | Ctrl+J newline | Tab focus | /help";
625
+ return "Enter send | Esc controls | Ctrl+J newline | /starter | /help";
615
626
  }
616
- return "Enter send | Ctrl+J newline | Tab focus | Enter choose | 1-8 tabs | /help";
627
+ return "Enter send/choose | Esc controls | Tab focus | 1-8 tabs | /help";
617
628
  };
618
629
 
619
630
  // src/ui/workspaceTabs.ts
@@ -692,8 +703,35 @@ var workspaceTabFromPosition = (column, row, areas) => areas.find(
692
703
  import { Fragment, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
693
704
  var shouldAnimateInputCursor = ({
694
705
  disabled,
706
+ inputActive = true,
695
707
  blinkCursor = false
696
- }) => !disabled && blinkCursor;
708
+ }) => !disabled && inputActive && blinkCursor;
709
+ var shouldBlinkPromptCursor = ({
710
+ animationsEnabled,
711
+ inputActive = true,
712
+ busy = false,
713
+ selectorOpen = false,
714
+ searching = false
715
+ }) => Boolean(animationsEnabled && inputActive && busy && !selectorOpen && !searching);
716
+ var getInputCursorColor = ({
717
+ disabled,
718
+ inputActive = true,
719
+ cursorVisible = true
720
+ }) => {
721
+ if (disabled || !inputActive) {
722
+ return terminalTheme.muted;
723
+ }
724
+ return cursorVisible ? terminalTheme.cursor : terminalTheme.accent;
725
+ };
726
+ var getInputCursorGlyph = ({
727
+ inputActive = true,
728
+ cursorVisible = true
729
+ }) => {
730
+ if (!inputActive) {
731
+ return " ";
732
+ }
733
+ return cursorVisible ? "\u258C" : "\u258F";
734
+ };
697
735
  var Scrollbar = ({
698
736
  totalRows,
699
737
  visibleRows,
@@ -707,7 +745,7 @@ var Scrollbar = ({
707
745
  visibleRows - 1,
708
746
  Math.max(0, Math.round(startRow / maxStart * (visibleRows - 1)))
709
747
  );
710
- return /* @__PURE__ */ jsx5(Box4, { flexDirection: "column", marginLeft: 1, children: Array.from({ length: visibleRows }, (_, index) => /* @__PURE__ */ jsx5(Text5, { color: index === thumbIndex ? terminalTheme.brand : terminalTheme.muted, children: index === thumbIndex ? "\u2503" : "\u2502" }, index)) });
748
+ return /* @__PURE__ */ jsx5(Box4, { flexDirection: "column", marginLeft: 1, children: Array.from({ length: visibleRows }, (_, index) => /* @__PURE__ */ jsx5(Text5, { color: index === thumbIndex ? terminalTheme.focus : terminalTheme.muted, children: index === thumbIndex ? "\u2503" : "\u2502" }, index)) });
711
749
  };
712
750
  var truncateInline = (value, maxLength) => {
713
751
  if (value.length <= maxLength) {
@@ -796,10 +834,13 @@ var getFollowUpRowViewport = ({
796
834
  };
797
835
  };
798
836
  var InputBox = ({
837
+ title = "Prompt",
799
838
  value,
800
839
  onChange,
801
840
  onSubmit,
802
841
  disabled = false,
842
+ inputActive = true,
843
+ onBlurRequest,
803
844
  placeholder = "Ask Cloudeval...",
804
845
  followUps = [],
805
846
  followUpsLabel = "Follow-ups",
@@ -810,9 +851,7 @@ var InputBox = ({
810
851
  helpText,
811
852
  actionLabel,
812
853
  actionHint,
813
- actionTone,
814
854
  onAction,
815
- actionDisabled = false,
816
855
  minInputRows = DEFAULT_INPUT_MIN_ROWS,
817
856
  maxInputRows = DEFAULT_INPUT_MAX_ROWS,
818
857
  scrollOffset,
@@ -825,17 +864,12 @@ var InputBox = ({
825
864
  const compact = terminalColumns < 78;
826
865
  const defaultHelpText = `${keyBindings.submit} | ${keyBindings.newline} | ${keyBindings.quit}`;
827
866
  const resolvedHelpText = helpText ?? defaultHelpText;
828
- const showHeaderRow = followUps.length > 0 || resolvedHelpText.length > 0;
829
867
  const followUpViewport = getFollowUpRowViewport({
830
868
  followUps,
831
869
  focusedFollowUpIndex,
832
870
  terminalColumns
833
871
  });
834
- const actionButtonWidth = actionLabel ? actionLabel.length + 4 : 0;
835
- const inputWidth = Math.max(
836
- 20,
837
- terminalColumns - 14 - (compact ? 0 : actionButtonWidth)
838
- );
872
+ const inputWidth = Math.max(20, terminalColumns - 8);
839
873
  const inputViewport = getInputViewport({
840
874
  value,
841
875
  width: inputWidth,
@@ -848,13 +882,13 @@ var InputBox = ({
848
882
  const startRow = inputViewport.startRow;
849
883
  const visibleRows = inputViewport.visibleRows;
850
884
  useEffect2(() => {
851
- if (!shouldAnimateInputCursor({ disabled, blinkCursor })) {
885
+ if (!shouldAnimateInputCursor({ disabled, inputActive, blinkCursor })) {
852
886
  setCursorVisible(true);
853
887
  return;
854
888
  }
855
889
  const timer = setInterval(() => setCursorVisible((current) => !current), 520);
856
890
  return () => clearInterval(timer);
857
- }, [blinkCursor, disabled, value]);
891
+ }, [blinkCursor, disabled, inputActive, value]);
858
892
  const handleChange = (nextValue) => {
859
893
  const cleanedValue = sanitizeTerminalMultilineInput(nextValue);
860
894
  const shortcutTab = onTabShortcut ? workspaceTabFromPromptChange(value, cleanedValue) : void 0;
@@ -900,6 +934,10 @@ var InputBox = ({
900
934
  onAction();
901
935
  return;
902
936
  }
937
+ if (key.escape) {
938
+ onBlurRequest?.();
939
+ return;
940
+ }
903
941
  if (key.backspace || key.delete) {
904
942
  handleChange(value.slice(0, -1));
905
943
  return;
@@ -909,41 +947,35 @@ var InputBox = ({
909
947
  }
910
948
  insertText(input);
911
949
  },
912
- { isActive: !disabled }
950
+ { isActive: !disabled && inputActive }
913
951
  );
952
+ const cursorGlyph = getInputCursorGlyph({ inputActive, cursorVisible });
953
+ const cursorColor = getInputCursorColor({ disabled, inputActive, cursorVisible });
954
+ const inputBorderColor = disabled ? terminalTheme.muted : followUpsActive || inputActive ? terminalTheme.focus : terminalTheme.muted;
914
955
  return /* @__PURE__ */ jsxs4(
915
956
  TitledBox,
916
957
  {
917
- title: "Prompt",
958
+ title,
918
959
  borderStyle: "round",
919
- borderColor: followUpsActive ? terminalTheme.brand : terminalTheme.muted,
920
- padding: 1,
960
+ borderColor: inputBorderColor,
961
+ padding: 0,
962
+ paddingX: 1,
921
963
  children: [
922
- showHeaderRow ? /* @__PURE__ */ jsxs4(
923
- Box4,
924
- {
925
- flexDirection: compact ? "column" : "row",
926
- justifyContent: "space-between",
927
- columnGap: 1,
928
- children: [
929
- /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: followUps.length ? followUpsLabel : "" }),
930
- resolvedHelpText ? /* @__PURE__ */ jsx5(Text5, { dimColor: true, wrap: "truncate", children: resolvedHelpText }) : null
931
- ]
932
- }
933
- ) : null,
934
- followUps.length ? /* @__PURE__ */ jsxs4(Box4, { flexDirection: "row", columnGap: 1, marginTop: compact ? 1 : 0, children: [
935
- followUpViewport.clippedStart ? /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "<--" }) : null,
964
+ followUps.length ? /* @__PURE__ */ jsxs4(Text5, { wrap: "truncate", children: [
965
+ /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: `${followUpsLabel}: ` }),
966
+ followUpViewport.clippedStart ? /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "<-- " }) : null,
936
967
  followUpViewport.items.map(({ question, index, label }) => {
937
968
  const focused = followUpsActive && focusedFollowUpIndex === index;
938
969
  return /* @__PURE__ */ jsxs4(
939
970
  Text5,
940
971
  {
941
- color: focused ? terminalTheme.brand : void 0,
972
+ color: focused ? terminalTheme.focus : void 0,
942
973
  bold: focused,
943
974
  children: [
944
975
  focused ? raisedButtonStyle.activeMarker : raisedButtonStyle.inactiveMarker,
945
976
  " ",
946
- label
977
+ label,
978
+ " "
947
979
  ]
948
980
  },
949
981
  `${index}-${question}`
@@ -952,20 +984,13 @@ var InputBox = ({
952
984
  followUpViewport.clippedEnd ? /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "-->" }) : null
953
985
  ] }) : null,
954
986
  /* @__PURE__ */ jsxs4(
955
- TitledBox,
987
+ Box4,
956
988
  {
957
- title: "Input",
958
989
  flexDirection: compact ? "column" : "row",
959
- borderStyle: "single",
960
- borderColor: disabled ? terminalTheme.muted : terminalTheme.brand,
961
- padding: 0,
962
- paddingX: 1,
963
- marginTop: 1,
964
990
  children: [
965
- /* @__PURE__ */ jsx5(Box4, { flexDirection: "column", flexGrow: 1, children: !value ? /* @__PURE__ */ jsxs4(Text5, { dimColor: true, wrap: "truncate", children: [
966
- /* @__PURE__ */ jsx5(Text5, { color: cursorVisible ? terminalTheme.brand : void 0, children: cursorVisible ? "\u258C" : " " }),
967
- " ",
968
- placeholder
991
+ /* @__PURE__ */ jsx5(Box4, { flexDirection: "column", flexGrow: 1, children: !value ? /* @__PURE__ */ jsxs4(Text5, { wrap: "truncate", children: [
992
+ /* @__PURE__ */ jsx5(Text5, { color: cursorColor, children: cursorGlyph }),
993
+ /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: ` ${placeholder}` })
969
994
  ] }) : visibleRows.map((line, index) => {
970
995
  const isLastValueRow = startRow + index === inputRows.length - 1;
971
996
  return /* @__PURE__ */ jsxs4(Text5, { wrap: "truncate", children: [
@@ -980,7 +1005,7 @@ var InputBox = ({
980
1005
  children: ghostText ?? ""
981
1006
  }
982
1007
  ),
983
- /* @__PURE__ */ jsx5(Text5, { color: cursorVisible ? terminalTheme.brand : void 0, children: cursorVisible ? "\u258C" : " " })
1008
+ /* @__PURE__ */ jsx5(Text5, { color: cursorColor, children: cursorGlyph })
984
1009
  ] }) : null
985
1010
  ] }, `${startRow}-${index}`);
986
1011
  }) }),
@@ -991,36 +1016,11 @@ var InputBox = ({
991
1016
  visibleRows: visibleRowCount,
992
1017
  startRow
993
1018
  }
994
- ),
995
- actionLabel ? /* @__PURE__ */ jsx5(
996
- Box4,
997
- {
998
- marginLeft: compact ? 0 : 1,
999
- marginTop: compact ? 1 : 0,
1000
- justifyContent: compact ? "flex-end" : "center",
1001
- flexDirection: "row",
1002
- children: /* @__PURE__ */ jsx5(
1003
- Box4,
1004
- {
1005
- borderStyle: raisedButtonStyle.border,
1006
- borderColor: actionDisabled ? terminalTheme.muted : actionTone ?? terminalTheme.brand,
1007
- paddingX: 1,
1008
- children: /* @__PURE__ */ jsx5(
1009
- Text5,
1010
- {
1011
- bold: !actionDisabled,
1012
- color: actionDisabled ? terminalTheme.muted : actionTone ?? terminalTheme.brand,
1013
- children: actionLabel
1014
- }
1015
- )
1016
- }
1017
- )
1018
- }
1019
- ) : null
1019
+ )
1020
1020
  ]
1021
1021
  }
1022
1022
  ),
1023
- actionHint ? /* @__PURE__ */ jsx5(Text5, { dimColor: true, wrap: "truncate", children: actionHint }) : null,
1023
+ actionHint ? /* @__PURE__ */ jsx5(Text5, { dimColor: true, wrap: "truncate", children: actionHint }) : resolvedHelpText ? /* @__PURE__ */ jsx5(Text5, { dimColor: true, wrap: "truncate", children: resolvedHelpText }) : null,
1024
1024
  footerControls ? /* @__PURE__ */ jsx5(Box4, { flexDirection: "column", marginTop: 1, children: footerControls }) : null
1025
1025
  ]
1026
1026
  }
@@ -1054,7 +1054,7 @@ var Scrollbar2 = ({
1054
1054
  return /* @__PURE__ */ jsx6(Box5, { flexDirection: "column", marginLeft: 1, flexShrink: 0, width: 1, children: scrollbarLines.map((line, idx) => /* @__PURE__ */ jsx6(
1055
1055
  Text6,
1056
1056
  {
1057
- color: line === "\u2588" ? terminalTheme.brand : terminalTheme.muted,
1057
+ color: line === "\u2588" ? terminalTheme.focus : terminalTheme.muted,
1058
1058
  children: line
1059
1059
  },
1060
1060
  idx
@@ -1136,7 +1136,7 @@ function ProjectSelector({
1136
1136
  Text7,
1137
1137
  {
1138
1138
  bold: isHighlighted || isSelected,
1139
- color: isSelected ? terminalTheme.success : isHighlighted ? terminalTheme.brand : void 0,
1139
+ color: isSelected ? terminalTheme.selected : isHighlighted ? terminalTheme.focus : void 0,
1140
1140
  children: [
1141
1141
  marker,
1142
1142
  " ",
@@ -1207,7 +1207,7 @@ function SelectPanel({
1207
1207
  Math.max(0, items.length - visibleCount)
1208
1208
  );
1209
1209
  const visibleItems = items.slice(windowStart, windowStart + visibleCount);
1210
- return /* @__PURE__ */ jsxs6(TitledBox, { title, borderStyle: "round", borderColor: terminalTheme.brand, padding: 1, children: [
1210
+ return /* @__PURE__ */ jsxs6(TitledBox, { title, borderStyle: "round", borderColor: terminalTheme.focus, padding: 1, children: [
1211
1211
  items.length === 0 ? /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "No options available." }) : visibleItems.map((item, offset) => {
1212
1212
  const index = windowStart + offset;
1213
1213
  const isHighlighted = index === highlighted;
@@ -1218,7 +1218,7 @@ function SelectPanel({
1218
1218
  Text8,
1219
1219
  {
1220
1220
  bold: isHighlighted || isSelected,
1221
- color: isSelected ? terminalTheme.success : isHighlighted ? terminalTheme.brand : void 0,
1221
+ color: isSelected ? terminalTheme.selected : isHighlighted ? terminalTheme.focus : void 0,
1222
1222
  children: [
1223
1223
  marker,
1224
1224
  " ",
@@ -1239,6 +1239,11 @@ function SelectPanel({
1239
1239
 
1240
1240
  // src/ui/commandCompletion.ts
1241
1241
  var slashCommands = [
1242
+ {
1243
+ name: "/thread",
1244
+ aliases: ["/threads"],
1245
+ description: "Open thread selector or use /thread <title-or-id>."
1246
+ },
1242
1247
  {
1243
1248
  name: "/project",
1244
1249
  aliases: ["/projects"],
@@ -1254,6 +1259,16 @@ var slashCommands = [
1254
1259
  aliases: [],
1255
1260
  description: "Open mode selector or use /mode ask|agent."
1256
1261
  },
1262
+ {
1263
+ name: "/profile",
1264
+ aliases: ["/profiles"],
1265
+ description: "Open Agent Profile selector or use /profile architecture|cost|triage|remediation."
1266
+ },
1267
+ {
1268
+ name: "/starter",
1269
+ aliases: ["/starters"],
1270
+ description: "Show starter prompt selections."
1271
+ },
1257
1272
  {
1258
1273
  name: "/thinking",
1259
1274
  aliases: ["/think"],
@@ -1342,6 +1357,16 @@ var completePromptInput = (input, context, previous) => {
1342
1357
  ghostSuffix: toGhostSuffix(trimmed, `/mode ${completion.candidates[0] ?? ""}`)
1343
1358
  } : null;
1344
1359
  }
1360
+ if (command === "/profile" || command === "/profiles") {
1361
+ const query = normalize(rest);
1362
+ const profileMatches = (context.profiles ?? []).flatMap((item) => item.value ? [item.value] : ["default", "profile"]).filter((value) => normalize(value).startsWith(query));
1363
+ const completion = completeFromCandidates(trimmed, profileMatches, previous);
1364
+ return completion ? {
1365
+ ...completion,
1366
+ value: `/profile ${completion.value}`,
1367
+ ghostSuffix: toGhostSuffix(trimmed, `/profile ${completion.candidates[0] ?? ""}`)
1368
+ } : null;
1369
+ }
1345
1370
  if (command === "/project" || command === "/projects") {
1346
1371
  const query = normalize(rest);
1347
1372
  const projectMatches = context.projects.map(projectName).filter((value) => normalize(value).startsWith(query));
@@ -1352,6 +1377,16 @@ var completePromptInput = (input, context, previous) => {
1352
1377
  ghostSuffix: toGhostSuffix(trimmed, `/project ${completion.candidates[0] ?? ""}`)
1353
1378
  } : null;
1354
1379
  }
1380
+ if (command === "/thread" || command === "/threads") {
1381
+ const query = normalize(rest);
1382
+ const threadMatches = (context.threads ?? []).flatMap((item) => [item.value, item.label]).filter((value) => normalize(value).startsWith(query));
1383
+ const completion = completeFromCandidates(trimmed, threadMatches, previous);
1384
+ return completion ? {
1385
+ ...completion,
1386
+ value: `/thread ${completion.value}`,
1387
+ ghostSuffix: toGhostSuffix(trimmed, `/thread ${completion.candidates[0] ?? ""}`)
1388
+ } : null;
1389
+ }
1355
1390
  return null;
1356
1391
  };
1357
1392
  var matchOne = (items, query, getValues) => {
@@ -1385,6 +1420,20 @@ var resolvePromptCommand = (input, context) => {
1385
1420
  ]);
1386
1421
  return project ? { type: "setProject", project } : { type: "unknown", message: `No unique project match for '${rest}'.` };
1387
1422
  }
1423
+ if (command === "/thread" || command === "/threads") {
1424
+ if (!rest) {
1425
+ return { type: "openSelector", selector: "thread" };
1426
+ }
1427
+ const thread = matchOne(context.threads ?? [], rest, (item) => [
1428
+ item.value,
1429
+ item.label
1430
+ ]);
1431
+ return thread ? {
1432
+ type: "setThread",
1433
+ threadId: thread.value,
1434
+ label: thread.label
1435
+ } : { type: "unknown", message: `No unique thread match for '${rest}'.` };
1436
+ }
1388
1437
  if (command === "/model" || command === "/models") {
1389
1438
  if (!rest) {
1390
1439
  return { type: "openSelector", selector: "model" };
@@ -1409,6 +1458,23 @@ var resolvePromptCommand = (input, context) => {
1409
1458
  ]);
1410
1459
  return mode && (mode.value === "ask" || mode.value === "agent") ? { type: "setMode", mode: mode.value, label: mode.label } : { type: "unknown", message: `No unique mode match for '${rest}'.` };
1411
1460
  }
1461
+ if (command === "/profile" || command === "/profiles") {
1462
+ if (!rest) {
1463
+ return { type: "openSelector", selector: "profile" };
1464
+ }
1465
+ const profile = matchOne(context.profiles ?? [], rest, (item) => [
1466
+ item.value || "default",
1467
+ item.value ? item.label : "profile"
1468
+ ]);
1469
+ return profile ? {
1470
+ type: "setProfile",
1471
+ profileId: profile.value,
1472
+ label: profile.label
1473
+ } : { type: "unknown", message: `No unique profile match for '${rest}'.` };
1474
+ }
1475
+ if (command === "/starter" || command === "/starters") {
1476
+ return { type: "showStarters" };
1477
+ }
1412
1478
  if (command === "/thinking" || command === "/think") {
1413
1479
  return { type: "toggleThinking" };
1414
1480
  }
@@ -1444,13 +1510,14 @@ var billingSummaryText = (billing, progressWidth = 10) => billing ? `Plan: ${bil
1444
1510
  })}` : "Plan: loading | Credits: loading";
1445
1511
 
1446
1512
  // src/ui/interactionModel.ts
1447
- var selectorControls = ["project", "model", "mode"];
1513
+ var selectorControls = ["thread", "project", "model", "mode", "profile"];
1448
1514
  var buildControlFocusOrder = ({
1449
1515
  hasThinkingSteps,
1450
1516
  followUpCount
1451
1517
  }) => [
1452
1518
  ...selectorControls,
1453
1519
  ...hasThinkingSteps ? ["thinking"] : [],
1520
+ "threadPanel",
1454
1521
  ...Array.from({ length: followUpCount }, (_, index) => `followup:${index}`)
1455
1522
  ];
1456
1523
  var focusFollowUpIndex = (focus) => {
@@ -1461,6 +1528,15 @@ var focusFollowUpIndex = (focus) => {
1461
1528
  return Number.isInteger(index) && index >= 0 ? index : void 0;
1462
1529
  };
1463
1530
  var isSelectorControlFocus = (focus) => selectorControls.includes(focus);
1531
+ var chatPanelFocusForControl = (focus) => {
1532
+ if (focus === "threadPanel") {
1533
+ return "thread";
1534
+ }
1535
+ if (focusFollowUpIndex(focus) !== void 0) {
1536
+ return "prompt";
1537
+ }
1538
+ return "settings";
1539
+ };
1464
1540
  var nextControlFocus = (current, order, direction = 1) => {
1465
1541
  if (!order.length) {
1466
1542
  return "project";
@@ -1481,37 +1557,55 @@ var getSelectorControlHitAreas = ({
1481
1557
  }) => {
1482
1558
  const safeTerminalColumns = Math.max(startColumn, terminalColumns);
1483
1559
  if (compact) {
1484
- const targets = [
1485
- ...selectorControls,
1486
- ...hasThinkingSteps ? ["thinking"] : []
1560
+ const compactRows = [
1561
+ { target: "thread", offset: 1 },
1562
+ { target: "project", offset: 2 },
1563
+ { target: "model", offset: 4 },
1564
+ { target: "mode", offset: 5 },
1565
+ { target: "profile", offset: 6 },
1566
+ ...hasThinkingSteps ? [{ target: "thinking", offset: 8 }] : []
1487
1567
  ];
1488
- return targets.map((target, index) => ({
1568
+ return compactRows.map(({ target, offset }) => ({
1489
1569
  target,
1490
1570
  startColumn,
1491
1571
  endColumn: safeTerminalColumns,
1492
- startRow: startRow + index * 3,
1493
- endRow: startRow + index * 3 + 2
1572
+ startRow: startRow + offset,
1573
+ endRow: startRow + offset
1494
1574
  }));
1495
1575
  }
1496
1576
  const fixedAreas = [
1497
1577
  {
1498
- target: "project",
1578
+ target: "thread",
1499
1579
  startColumn,
1500
1580
  endColumn: Math.min(safeTerminalColumns, startColumn + 27),
1501
1581
  startRow,
1502
1582
  endRow: startRow + 2
1503
1583
  },
1504
1584
  {
1505
- target: "model",
1585
+ target: "project",
1506
1586
  startColumn: startColumn + 29,
1507
- endColumn: Math.min(safeTerminalColumns, startColumn + 51),
1587
+ endColumn: Math.min(safeTerminalColumns, startColumn + 56),
1588
+ startRow,
1589
+ endRow: startRow + 2
1590
+ },
1591
+ {
1592
+ target: "model",
1593
+ startColumn: startColumn + 58,
1594
+ endColumn: Math.min(safeTerminalColumns, startColumn + 80),
1508
1595
  startRow,
1509
1596
  endRow: startRow + 2
1510
1597
  },
1511
1598
  {
1512
1599
  target: "mode",
1513
- startColumn: startColumn + 53,
1514
- endColumn: Math.min(safeTerminalColumns, startColumn + 71),
1600
+ startColumn: startColumn + 82,
1601
+ endColumn: Math.min(safeTerminalColumns, startColumn + 100),
1602
+ startRow,
1603
+ endRow: startRow + 2
1604
+ },
1605
+ {
1606
+ target: "profile",
1607
+ startColumn: startColumn + 102,
1608
+ endColumn: Math.min(safeTerminalColumns, startColumn + 124),
1515
1609
  startRow,
1516
1610
  endRow: startRow + 2
1517
1611
  }
@@ -1519,7 +1613,7 @@ var getSelectorControlHitAreas = ({
1519
1613
  if (hasThinkingSteps) {
1520
1614
  fixedAreas.push({
1521
1615
  target: "thinking",
1522
- startColumn: startColumn + 73,
1616
+ startColumn: startColumn + 126,
1523
1617
  endColumn: safeTerminalColumns,
1524
1618
  startRow,
1525
1619
  endRow: startRow + 2
@@ -1530,6 +1624,15 @@ var getSelectorControlHitAreas = ({
1530
1624
  var selectorControlFromMousePosition = (position, areas) => areas.find(
1531
1625
  (area) => position.x >= area.startColumn && position.x <= area.endColumn && position.y >= area.startRow && position.y <= area.endRow
1532
1626
  )?.target;
1627
+ var isPromptTextInput = (input, key) => {
1628
+ if (!input || key.ctrl || key.meta) {
1629
+ return false;
1630
+ }
1631
+ if (key.return || key.tab || key.escape || key.upArrow || key.downArrow || key.leftArrow || key.rightArrow || key.pageUp || key.pageDown || key.backspace || key.delete) {
1632
+ return false;
1633
+ }
1634
+ return input.length > 0;
1635
+ };
1533
1636
 
1534
1637
  // src/ui/layout.ts
1535
1638
  var clamp2 = (value, min, max) => Math.min(max, Math.max(min, value));
@@ -1567,7 +1670,6 @@ var getResponsiveTuiLayout = (size, options = {}) => {
1567
1670
  if (options.hasHitl) reservedRows += 7;
1568
1671
  if (options.hasSelector) reservedRows += 8;
1569
1672
  if (options.isSearching) reservedRows += 3;
1570
- const minThreadHeight = rows < 20 ? 3 : rows < 28 ? 4 : 6;
1571
1673
  const maxThreadHeight = compact ? 14 : 24;
1572
1674
  const availableRows = rows - reservedRows;
1573
1675
  return {
@@ -1575,18 +1677,20 @@ var getResponsiveTuiLayout = (size, options = {}) => {
1575
1677
  paddingX: compact ? 0 : 1,
1576
1678
  selectorLimit: compact ? 6 : 8,
1577
1679
  showBanner,
1578
- threadHeight: clamp2(availableRows, minThreadHeight, maxThreadHeight)
1680
+ threadHeight: clamp2(availableRows, 1, maxThreadHeight)
1579
1681
  };
1580
1682
  };
1683
+ var getMiddleViewportRows = (size, options = {}) => {
1684
+ const rows = normalizeDimension(size.rows, 32);
1685
+ const headerRows = Math.max(0, Math.ceil(options.headerRows ?? 0));
1686
+ const footerRows = Math.max(0, Math.ceil(options.footerRows ?? 0));
1687
+ const safetyRows = Math.max(0, Math.ceil(options.safetyRows ?? 2));
1688
+ return Math.max(1, Math.floor(rows) - headerRows - footerRows - safetyRows);
1689
+ };
1690
+ var getFramedBodyRows = (frameRows, chromeRows = 4) => Math.max(1, Math.floor(frameRows) - Math.max(0, Math.ceil(chromeRows)));
1581
1691
  var getPromptInputRowBudget = (size) => {
1582
1692
  const rows = normalizeDimension(size.rows, 32);
1583
- if (rows < 28) {
1584
- return 4;
1585
- }
1586
- if (rows < 38) {
1587
- return 6;
1588
- }
1589
- return clamp2(Math.floor(rows * 0.22), 8, 16);
1693
+ return clamp2(Math.floor(rows * 0.13), 4, 8);
1590
1694
  };
1591
1695
  var shouldUseSplitChatLayout = (size) => {
1592
1696
  const columns = normalizeDimension(size.columns, 100);
@@ -1857,10 +1961,11 @@ var getStarterPrompts = ({
1857
1961
  };
1858
1962
  var getPromptSuggestions = ({
1859
1963
  latestFollowUps,
1860
- messages,
1964
+ messages: _messages,
1861
1965
  mode,
1862
1966
  project,
1863
- limit
1967
+ limit,
1968
+ showStarters = false
1864
1969
  }) => {
1865
1970
  const followUps = latestFollowUps.map((prompt) => prompt.trim()).filter(Boolean);
1866
1971
  if (followUps.length) {
@@ -1870,8 +1975,7 @@ var getPromptSuggestions = ({
1870
1975
  prompts: followUps
1871
1976
  };
1872
1977
  }
1873
- const hasUserMessages = messages.some((message) => message.role === "user");
1874
- if (!hasUserMessages) {
1978
+ if (showStarters) {
1875
1979
  return {
1876
1980
  kind: "starter",
1877
1981
  label: "Starters",
@@ -1885,6 +1989,193 @@ var getPromptSuggestions = ({
1885
1989
  };
1886
1990
  };
1887
1991
 
1992
+ // src/ui/sessionThreads.ts
1993
+ import { randomUUID } from "crypto";
1994
+ var timestampFromIso = (value, fallbackOffset) => {
1995
+ const parsed = Date.parse(value);
1996
+ return Number.isFinite(parsed) ? parsed : Date.now() + fallbackOffset;
1997
+ };
1998
+ var relativeThreadAge = (value, now = Date.now()) => {
1999
+ if (!value) {
2000
+ return void 0;
2001
+ }
2002
+ const parsed = Date.parse(value);
2003
+ if (!Number.isFinite(parsed)) {
2004
+ return void 0;
2005
+ }
2006
+ const diffMs = Math.max(0, now - parsed);
2007
+ const minutes = Math.floor(diffMs / 6e4);
2008
+ if (minutes < 1) {
2009
+ return "now";
2010
+ }
2011
+ if (minutes < 60) {
2012
+ return `${minutes}m`;
2013
+ }
2014
+ const hours = Math.floor(minutes / 60);
2015
+ if (hours < 24) {
2016
+ return `${hours}h`;
2017
+ }
2018
+ const days = Math.floor(hours / 24);
2019
+ if (days < 7) {
2020
+ return `${days}d`;
2021
+ }
2022
+ const weeks = Math.floor(days / 7);
2023
+ if (weeks < 5) {
2024
+ return `${weeks}w`;
2025
+ }
2026
+ const months = Math.floor(days / 30);
2027
+ if (months < 12) {
2028
+ return `${months}mo`;
2029
+ }
2030
+ return `${Math.floor(days / 365)}y`;
2031
+ };
2032
+ var titleWithAge = (title, age) => {
2033
+ if (!age) {
2034
+ return truncateForTerminal(title, 72);
2035
+ }
2036
+ const suffix = ` \xB7 ${age}`;
2037
+ return `${truncateForTerminal(title, Math.max(12, 72 - suffix.length))}${suffix}`;
2038
+ };
2039
+ var createdAgeDescription = (age) => age ? age === "now" ? "created now" : `created ${age} ago` : void 0;
2040
+ var sessionDescription = (session, age) => {
2041
+ const project = session.projectName || session.projectId;
2042
+ const messages = `${session.messageCount} message${session.messageCount === 1 ? "" : "s"}`;
2043
+ return [createdAgeDescription(age), project, messages].filter(Boolean).join(" \xB7 ");
2044
+ };
2045
+ var remoteTitle = (thread) => {
2046
+ const title = String(thread.title || "").trim();
2047
+ const preview = String(thread.last_message_preview || "").trim();
2048
+ return title || preview || thread.thread_id;
2049
+ };
2050
+ var remoteDescription = (thread, age) => {
2051
+ const project = thread.project_name || thread.project_id;
2052
+ const count = Number(thread.message_count ?? 0);
2053
+ const messages = `${count} message${count === 1 ? "" : "s"}`;
2054
+ const source = thread.is_archived ? "Cloud thread \xB7 archived" : "Cloud thread";
2055
+ return [source, createdAgeDescription(age), project, messages].filter(Boolean).join(" \xB7 ");
2056
+ };
2057
+ var extractMessageText = (content) => {
2058
+ if (typeof content === "string") {
2059
+ return content;
2060
+ }
2061
+ if (Array.isArray(content)) {
2062
+ return content.map((part) => {
2063
+ if (typeof part === "string") {
2064
+ return part;
2065
+ }
2066
+ if (part && typeof part === "object") {
2067
+ const record = part;
2068
+ if (typeof record.text === "string") {
2069
+ return record.text;
2070
+ }
2071
+ if (typeof record.content === "string") {
2072
+ return record.content;
2073
+ }
2074
+ }
2075
+ return "";
2076
+ }).filter(Boolean).join(" ");
2077
+ }
2078
+ if (content && typeof content === "object") {
2079
+ const record = content;
2080
+ if (typeof record.text === "string") {
2081
+ return record.text;
2082
+ }
2083
+ if (typeof record.content === "string") {
2084
+ return record.content;
2085
+ }
2086
+ }
2087
+ return "";
2088
+ };
2089
+ var timestampFromValue = (value, fallback) => {
2090
+ if (typeof value === "number" && Number.isFinite(value)) {
2091
+ return value;
2092
+ }
2093
+ if (typeof value === "string") {
2094
+ const parsed = Date.parse(value);
2095
+ if (Number.isFinite(parsed)) {
2096
+ return parsed;
2097
+ }
2098
+ }
2099
+ return fallback;
2100
+ };
2101
+ var remoteThreadMessagesToChatMessages = (history) => {
2102
+ const rawMessages = history.messages_page ?? history.thread_messages ?? history.messages ?? [];
2103
+ return rawMessages.map((raw, index) => {
2104
+ if (!raw || typeof raw !== "object") {
2105
+ return null;
2106
+ }
2107
+ const message = raw;
2108
+ const role = String(message.role || "").toLowerCase();
2109
+ if (role !== "user" && role !== "assistant") {
2110
+ return null;
2111
+ }
2112
+ const timestamp = timestampFromValue(
2113
+ message.timestamp ?? message.created_at ?? message.createdAt,
2114
+ Date.now() + index
2115
+ );
2116
+ const followUps = message.followUpQuestions ?? message.follow_up_questions;
2117
+ return {
2118
+ id: String(
2119
+ message.id ?? message.message_id ?? message.assistant_message_id ?? `${history.thread_id}-${index}-${randomUUID()}`
2120
+ ),
2121
+ role,
2122
+ content: extractMessageText(message.content),
2123
+ thinkingSteps: Array.isArray(message.thinkingSteps) ? message.thinkingSteps : Array.isArray(message.thinking_steps) ? message.thinking_steps : void 0,
2124
+ followUpQuestions: Array.isArray(followUps) ? followUps.map((item) => String(item)).filter(Boolean) : void 0,
2125
+ createdAt: timestamp,
2126
+ updatedAt: timestampFromValue(message.updated_at ?? message.updatedAt, timestamp)
2127
+ };
2128
+ }).filter((message) => Boolean(message));
2129
+ };
2130
+ var localSessionMessagesToChatMessages = (session) => session.messages.map((message, index) => ({
2131
+ id: `${session.threadId}-${index}-${randomUUID()}`,
2132
+ role: message.role,
2133
+ content: message.content,
2134
+ createdAt: timestampFromIso(message.createdAt, index)
2135
+ }));
2136
+ var buildThreadSelectItems = (sessions, activeThreadId, remoteThreads = [], options = {}) => [
2137
+ {
2138
+ label: "New thread",
2139
+ value: { kind: "new" },
2140
+ description: activeThreadId ? "Start a fresh CloudEval chat thread." : "Current selection."
2141
+ },
2142
+ ...remoteThreads.map((thread) => {
2143
+ const age = relativeThreadAge(thread.created_at ?? thread.updated_at, options.now);
2144
+ return {
2145
+ label: titleWithAge(remoteTitle(thread), age),
2146
+ value: { kind: "remote", thread },
2147
+ description: remoteDescription(thread, age)
2148
+ };
2149
+ }),
2150
+ ...sessions.filter(
2151
+ (session) => !remoteThreads.some((thread) => thread.thread_id === session.threadId)
2152
+ ).map((session) => {
2153
+ const age = relativeThreadAge(session.createdAt ?? session.updatedAt, options.now);
2154
+ return {
2155
+ label: titleWithAge(session.title, age),
2156
+ value: { kind: "session", session },
2157
+ description: `Local session \xB7 ${sessionDescription(session, age)}`
2158
+ };
2159
+ })
2160
+ ];
2161
+ var threadPanelTitle = ({
2162
+ session,
2163
+ remoteThread,
2164
+ threadId,
2165
+ hasMessages
2166
+ }) => {
2167
+ if (session?.title) {
2168
+ return truncateForTerminal(session.title, 70);
2169
+ }
2170
+ if (remoteThread) {
2171
+ return truncateForTerminal(remoteTitle(remoteThread), 70);
2172
+ }
2173
+ if (threadId) {
2174
+ return truncateForTerminal(threadId, 70);
2175
+ }
2176
+ return hasMessages ? "Current chat" : "New thread";
2177
+ };
2178
+
1888
2179
  // src/ui/overviewDashboard.ts
1889
2180
  var toRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
1890
2181
  var asNumber = (value) => {
@@ -2878,6 +3169,11 @@ var loadWorkspacePanelData = async ({
2878
3169
  })
2879
3170
  );
2880
3171
  }
3172
+ if (data.entitlement) {
3173
+ data.creditStatus = deps.getCreditStatus(data.entitlement, {
3174
+ reportedUsedCredits: deps.getBillingUsageCreditsUsed?.(data.usageSummary)
3175
+ });
3176
+ }
2881
3177
  if (deps.getBillingUsageLedger) {
2882
3178
  await capture(
2883
3179
  "ledger",
@@ -3015,6 +3311,17 @@ var firstNumber4 = (value, keys) => {
3015
3311
  }
3016
3312
  return void 0;
3017
3313
  };
3314
+ var billingUsageTrendValues = (usageSummary) => {
3315
+ const record = toRecord4(usageSummary);
3316
+ const buckets = directArray2(record?.buckets);
3317
+ const bucketValues = buckets.map((bucket) => firstNumber4(bucket, ["credits_used", "total_credits", "credits"])).filter((value) => value !== void 0);
3318
+ if (bucketValues.length) {
3319
+ return bucketValues;
3320
+ }
3321
+ const totals = toRecord4(record?.totals);
3322
+ const total = firstNumber4(totals ?? usageSummary, ["credits_used", "total_credits", "credits"]);
3323
+ return total !== void 0 ? [total] : allNumbers(usageSummary);
3324
+ };
3018
3325
  var formatNumber3 = (value) => value === void 0 ? "-" : new Intl.NumberFormat("en-US", { maximumFractionDigits: 2 }).format(
3019
3326
  value
3020
3327
  );
@@ -3389,7 +3696,7 @@ var HelpLegend = ({
3389
3696
  label: "tabs",
3390
3697
  color: terminalTheme.brand
3391
3698
  },
3392
- { key: "Left/Right", label: "switch", color: terminalTheme.brand },
3699
+ { key: "Arrows", label: "move focus", color: terminalTheme.brand },
3393
3700
  { key: "R", label: "refresh", color: terminalTheme.warning },
3394
3701
  { key: "O", label: "open", color: terminalTheme.success },
3395
3702
  { key: "D", label: "download", color: terminalTheme.success },
@@ -3486,7 +3793,7 @@ var BillingView = ({ state, compact, terminalColumns, frontendUrl }) => {
3486
3793
  const creditStatus = toRecord4(state.data.creditStatus);
3487
3794
  const entitlement = toRecord4(state.data.entitlement);
3488
3795
  const plan = toRecord4(entitlement?.plan);
3489
- const usageValues = allNumbers(state.data.usageSummary);
3796
+ const usageValues = billingUsageTrendValues(state.data.usageSummary);
3490
3797
  const ledger = directArray2(state.data.ledger);
3491
3798
  const invoices = directArray2(state.data.billingInfo);
3492
3799
  const topups = directArray2(state.data.topups);
@@ -4131,7 +4438,7 @@ var OptionsView = ({
4131
4438
  workspaceTabs.length,
4132
4439
  " jump tabs | ",
4133
4440
  keys.tabSwitch,
4134
- " tabs |",
4441
+ " |",
4135
4442
  " ",
4136
4443
  keys.refresh,
4137
4444
  " | ",
@@ -4215,14 +4522,14 @@ var WorkspaceTabBar = ({ activeTab, showBrand = false, billingSummary }) => {
4215
4522
  Box8,
4216
4523
  {
4217
4524
  borderStyle: active ? "bold" : raisedButtonStyle.border,
4218
- borderColor: active ? terminalTheme.brand : terminalTheme.muted,
4525
+ borderColor: active ? terminalTheme.focus : terminalTheme.muted,
4219
4526
  paddingX: 1,
4220
4527
  marginRight: 1,
4221
4528
  children: /* @__PURE__ */ jsxs7(
4222
4529
  Text9,
4223
4530
  {
4224
4531
  bold: active,
4225
- color: active ? terminalTheme.brand : void 0,
4532
+ color: active ? terminalTheme.focus : void 0,
4226
4533
  children: [
4227
4534
  active ? raisedButtonStyle.activeMarker : raisedButtonStyle.inactiveMarker,
4228
4535
  " ",
@@ -4242,12 +4549,103 @@ var WorkspacePanel = (props) => {
4242
4549
  const compact = props.terminalColumns < 88;
4243
4550
  const state = props.state;
4244
4551
  const animate = props.animate ?? true;
4552
+ const framed = props.framed ?? true;
4245
4553
  const connections = directArray2(state.data.connections);
4246
4554
  const isInitialLoading = state.status === "loading";
4247
4555
  const isBackgroundRefreshing = Boolean(
4248
4556
  state.isRefreshing && !isInitialLoading
4249
4557
  );
4250
- return /* @__PURE__ */ jsxs7(
4558
+ const content = /* @__PURE__ */ jsxs7(Fragment2, { children: [
4559
+ state.loadedAt && !isInitialLoading && !isBackgroundRefreshing ? /* @__PURE__ */ jsx9(Box8, { flexDirection: "row", justifyContent: "flex-end", children: /* @__PURE__ */ jsxs7(Text9, { dimColor: true, children: [
4560
+ "loaded ",
4561
+ new Date(state.loadedAt).toLocaleTimeString()
4562
+ ] }) }) : null,
4563
+ isInitialLoading ? /* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", gap: 1, children: [
4564
+ /* @__PURE__ */ jsx9(Spinner, { type: "dots", animate }),
4565
+ /* @__PURE__ */ jsx9(Text9, { color: terminalTheme.brand, children: "Loading real API data..." })
4566
+ ] }) : null,
4567
+ isBackgroundRefreshing ? /* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", gap: 1, children: [
4568
+ /* @__PURE__ */ jsx9(Spinner, { type: "dots", animate }),
4569
+ /* @__PURE__ */ jsx9(Text9, { color: terminalTheme.brand, children: "Refreshing in background, showing cached data..." })
4570
+ ] }) : null,
4571
+ state.status === "error" ? /* @__PURE__ */ jsx9(
4572
+ TitledBox,
4573
+ {
4574
+ title: "Backend Data Unavailable",
4575
+ borderStyle: "single",
4576
+ borderColor: terminalTheme.danger,
4577
+ padding: 0,
4578
+ paddingX: 1,
4579
+ children: /* @__PURE__ */ jsxs7(Text9, { color: terminalTheme.danger, wrap: "wrap", children: [
4580
+ " ",
4581
+ state.error ?? "Unable to load this tab."
4582
+ ] })
4583
+ }
4584
+ ) : null,
4585
+ state.warnings.length ? /* @__PURE__ */ jsxs7(
4586
+ TitledBox,
4587
+ {
4588
+ title: "Backend Warnings",
4589
+ borderStyle: "single",
4590
+ borderColor: terminalTheme.warning,
4591
+ padding: 0,
4592
+ paddingX: 1,
4593
+ children: [
4594
+ state.warnings.slice(0, 4).map((warning) => /* @__PURE__ */ jsx9(Text9, { color: terminalTheme.warning, wrap: "truncate", children: cleanBackendWarning(warning, props.terminalColumns) }, warning)),
4595
+ state.warnings.length > 4 ? /* @__PURE__ */ jsxs7(Text9, { dimColor: true, children: [
4596
+ "+",
4597
+ state.warnings.length - 4,
4598
+ " more warning(s)"
4599
+ ] }) : null
4600
+ ]
4601
+ }
4602
+ ) : null,
4603
+ props.tab === "overview" ? /* @__PURE__ */ jsx9(
4604
+ OverviewView,
4605
+ {
4606
+ state,
4607
+ projects: props.projects,
4608
+ selectedProject: props.selectedProject,
4609
+ compact,
4610
+ terminalColumns: props.terminalColumns,
4611
+ tablePage: props.tablePage
4612
+ }
4613
+ ) : null,
4614
+ props.tab === "reports" ? /* @__PURE__ */ jsx9(
4615
+ ReportsView,
4616
+ {
4617
+ state,
4618
+ projects: props.projects,
4619
+ selectedProject: props.selectedProject,
4620
+ compact,
4621
+ terminalColumns: props.terminalColumns,
4622
+ tablePage: props.tablePage
4623
+ }
4624
+ ) : null,
4625
+ props.tab === "projects" ? /* @__PURE__ */ jsx9(
4626
+ ProjectsView,
4627
+ {
4628
+ projects: props.projects,
4629
+ selectedProject: props.selectedProject
4630
+ }
4631
+ ) : null,
4632
+ props.tab === "connections" ? /* @__PURE__ */ jsx9(ConnectionsView, { connections }) : null,
4633
+ props.tab === "billing" ? /* @__PURE__ */ jsx9(
4634
+ BillingView,
4635
+ {
4636
+ state,
4637
+ compact,
4638
+ terminalColumns: props.terminalColumns,
4639
+ frontendUrl: props.frontendUrl
4640
+ }
4641
+ ) : null,
4642
+ props.tab === "options" ? /* @__PURE__ */ jsx9(OptionsView, { ...props }) : null,
4643
+ props.tab === "help" ? /* @__PURE__ */ jsx9(HelpView, {}) : null
4644
+ ] });
4645
+ if (!framed) {
4646
+ return /* @__PURE__ */ jsx9(Box8, { flexDirection: "column", gap: 1, children: content });
4647
+ }
4648
+ return /* @__PURE__ */ jsx9(
4251
4649
  TitledBox,
4252
4650
  {
4253
4651
  title: workspaceTabLabels[props.tab],
@@ -4255,93 +4653,7 @@ var WorkspacePanel = (props) => {
4255
4653
  borderColor: terminalTheme.muted,
4256
4654
  padding: 1,
4257
4655
  gap: 1,
4258
- children: [
4259
- state.loadedAt && !isInitialLoading && !isBackgroundRefreshing ? /* @__PURE__ */ jsx9(Box8, { flexDirection: "row", justifyContent: "flex-end", children: /* @__PURE__ */ jsxs7(Text9, { dimColor: true, children: [
4260
- "loaded ",
4261
- new Date(state.loadedAt).toLocaleTimeString()
4262
- ] }) }) : null,
4263
- isInitialLoading ? /* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", gap: 1, children: [
4264
- /* @__PURE__ */ jsx9(Spinner, { type: "dots", animate }),
4265
- /* @__PURE__ */ jsx9(Text9, { color: terminalTheme.brand, children: "Loading real API data..." })
4266
- ] }) : null,
4267
- isBackgroundRefreshing ? /* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", gap: 1, children: [
4268
- /* @__PURE__ */ jsx9(Spinner, { type: "dots", animate }),
4269
- /* @__PURE__ */ jsx9(Text9, { color: terminalTheme.brand, children: "Refreshing in background, showing cached data..." })
4270
- ] }) : null,
4271
- state.status === "error" ? /* @__PURE__ */ jsx9(
4272
- TitledBox,
4273
- {
4274
- title: "Backend Data Unavailable",
4275
- borderStyle: "single",
4276
- borderColor: terminalTheme.danger,
4277
- padding: 0,
4278
- paddingX: 1,
4279
- children: /* @__PURE__ */ jsxs7(Text9, { color: terminalTheme.danger, wrap: "wrap", children: [
4280
- " ",
4281
- state.error ?? "Unable to load this tab."
4282
- ] })
4283
- }
4284
- ) : null,
4285
- state.warnings.length ? /* @__PURE__ */ jsxs7(
4286
- TitledBox,
4287
- {
4288
- title: "Backend Warnings",
4289
- borderStyle: "single",
4290
- borderColor: terminalTheme.warning,
4291
- padding: 0,
4292
- paddingX: 1,
4293
- children: [
4294
- state.warnings.slice(0, 4).map((warning) => /* @__PURE__ */ jsx9(Text9, { color: terminalTheme.warning, wrap: "truncate", children: cleanBackendWarning(warning, props.terminalColumns) }, warning)),
4295
- state.warnings.length > 4 ? /* @__PURE__ */ jsxs7(Text9, { dimColor: true, children: [
4296
- "+",
4297
- state.warnings.length - 4,
4298
- " more warning(s)"
4299
- ] }) : null
4300
- ]
4301
- }
4302
- ) : null,
4303
- props.tab === "overview" ? /* @__PURE__ */ jsx9(
4304
- OverviewView,
4305
- {
4306
- state,
4307
- projects: props.projects,
4308
- selectedProject: props.selectedProject,
4309
- compact,
4310
- terminalColumns: props.terminalColumns,
4311
- tablePage: props.tablePage
4312
- }
4313
- ) : null,
4314
- props.tab === "reports" ? /* @__PURE__ */ jsx9(
4315
- ReportsView,
4316
- {
4317
- state,
4318
- projects: props.projects,
4319
- selectedProject: props.selectedProject,
4320
- compact,
4321
- terminalColumns: props.terminalColumns,
4322
- tablePage: props.tablePage
4323
- }
4324
- ) : null,
4325
- props.tab === "projects" ? /* @__PURE__ */ jsx9(
4326
- ProjectsView,
4327
- {
4328
- projects: props.projects,
4329
- selectedProject: props.selectedProject
4330
- }
4331
- ) : null,
4332
- props.tab === "connections" ? /* @__PURE__ */ jsx9(ConnectionsView, { connections }) : null,
4333
- props.tab === "billing" ? /* @__PURE__ */ jsx9(
4334
- BillingView,
4335
- {
4336
- state,
4337
- compact,
4338
- terminalColumns: props.terminalColumns,
4339
- frontendUrl: props.frontendUrl
4340
- }
4341
- ) : null,
4342
- props.tab === "options" ? /* @__PURE__ */ jsx9(OptionsView, { ...props }) : null,
4343
- props.tab === "help" ? /* @__PURE__ */ jsx9(HelpView, {}) : null
4344
- ]
4656
+ children: content
4345
4657
  }
4346
4658
  );
4347
4659
  };
@@ -4350,13 +4662,12 @@ var WorkspacePanel = (props) => {
4350
4662
  var truthyEnv = (value) => value !== void 0 && /^(1|true|yes|on)$/i.test(value);
4351
4663
  var shouldEnableTuiAnimations = ({
4352
4664
  disableAnim = false,
4353
- forceAnim = false,
4354
4665
  env = process.env
4355
4666
  } = {}) => {
4356
4667
  if (disableAnim || truthyEnv(env.CLOUDEVAL_NO_ANIM)) {
4357
4668
  return false;
4358
4669
  }
4359
- return forceAnim || truthyEnv(env.CLOUDEVAL_ANIM);
4670
+ return true;
4360
4671
  };
4361
4672
 
4362
4673
  // src/ui/App.tsx
@@ -4371,7 +4682,7 @@ var defaultUser = { id: "cli-user", name: "CLI User" };
4371
4682
  var getUserNameFromToken = async (token) => {
4372
4683
  if (!token) return "You";
4373
4684
  try {
4374
- const { extractEmailFromToken } = await import("./dist-I4IPYCRC.js");
4685
+ const { extractEmailFromToken } = await import("./dist-TBAQ5KOK.js");
4375
4686
  const email = extractEmailFromToken(token);
4376
4687
  return getFirstNameForDisplay({ email: email ?? void 0 });
4377
4688
  } catch {
@@ -4384,8 +4695,21 @@ var defaultProject = {
4384
4695
  user_id: "cli-user",
4385
4696
  cloud_provider: "azure"
4386
4697
  };
4387
- var selectorOrder = ["project", "model", "mode"];
4698
+ var selectorOrder = ["thread", "project", "model", "mode", "profile"];
4388
4699
  var dropdownIndicator = "\u25BE";
4700
+ var bundledAgentProfiles = getBundledAgentProfiles();
4701
+ var agentProfileItems = [
4702
+ {
4703
+ label: "Profile",
4704
+ value: "",
4705
+ description: "Default CloudEval chat flow without an Agent Profile."
4706
+ },
4707
+ ...bundledAgentProfiles.map((profile) => ({
4708
+ label: profile.display_name,
4709
+ value: profile.id,
4710
+ description: profile.personality
4711
+ }))
4712
+ ];
4389
4713
  var workspaceTabDescriptions = {
4390
4714
  chat: "Ask questions, run agent workflows, and inspect project context.",
4391
4715
  overview: "Portfolio dashboard with project health, cost, WAF, and report signals.",
@@ -4416,6 +4740,7 @@ var modeItems = [
4416
4740
  }
4417
4741
  ];
4418
4742
  var commandNotice = (candidates) => candidates.length > 1 ? `Completions: ${candidates.join(" | ")}` : `Completed: ${candidates[0]}`;
4743
+ var isStarterSlashCommandInput = (value) => /^\/starters?(?:\s*)?$/i.test(value.trim());
4419
4744
  var modelNameFromRaw = (raw) => {
4420
4745
  if (typeof raw === "string") return raw;
4421
4746
  if (!raw || typeof raw !== "object") return void 0;
@@ -4454,8 +4779,10 @@ var mergeModelItems = (fetchedItems, cliModel) => {
4454
4779
  return Array.from(merged.values());
4455
4780
  };
4456
4781
  var delay = (ms) => new Promise((res) => setTimeout(res, ms));
4457
- var billingHeaderFromEntitlement = (entitlement) => {
4458
- const creditStatus = getCreditStatus(entitlement);
4782
+ var billingHeaderFromEntitlement = (entitlement, usageSummary) => {
4783
+ const creditStatus = getCreditStatus(entitlement, {
4784
+ reportedUsedCredits: getBillingUsageCreditsUsed(usageSummary)
4785
+ });
4459
4786
  if (!creditStatus) {
4460
4787
  return null;
4461
4788
  }
@@ -4643,8 +4970,10 @@ var estimatePromptControlRows = ({
4643
4970
  compact,
4644
4971
  hasThinkingSteps
4645
4972
  }) => {
4646
- const selectorCount = selectorOrder.length + (hasThinkingSteps ? 1 : 0);
4647
- return compact ? selectorCount * 3 : 3;
4973
+ if (compact) {
4974
+ return 2 + selectorOrder.length + 2;
4975
+ }
4976
+ return 3;
4648
4977
  };
4649
4978
  var estimatePromptPanelRows = ({
4650
4979
  inputRows,
@@ -4653,11 +4982,10 @@ var estimatePromptPanelRows = ({
4653
4982
  hasThinkingSteps,
4654
4983
  includeControls = true
4655
4984
  }) => {
4656
- const outerChromeRows = 4;
4657
- const topHelpRows = suggestionRows > 0 ? 1 : 0;
4658
- const inputBoxRows = inputRows + 3;
4659
- const footerRows = 1 + 1 + (includeControls ? estimatePromptControlRows({ compact, hasThinkingSteps }) : 0);
4660
- return outerChromeRows + topHelpRows + suggestionRows + inputBoxRows + footerRows;
4985
+ const outerChromeRows = 2;
4986
+ const inputRowsWithHint = inputRows + 1;
4987
+ const footerRows = includeControls ? 1 + estimatePromptControlRows({ compact, hasThinkingSteps }) : 0;
4988
+ return outerChromeRows + suggestionRows + inputRowsWithHint + footerRows;
4661
4989
  };
4662
4990
  var useTerminalSize = () => {
4663
4991
  const [size, setSize] = useState4(() => readTerminalSize());
@@ -4675,6 +5003,16 @@ var isMouseTrackingEnabled = () => {
4675
5003
  return value !== "0" && value !== "false" && value !== "no";
4676
5004
  };
4677
5005
  var oneLine = (value) => value.replace(/\s+/g, " ").trim();
5006
+ var collapseRepeatedAssistantText = (value) => {
5007
+ const trimmed = value.trim();
5008
+ if (!trimmed || trimmed.length % 2 !== 0) {
5009
+ return value;
5010
+ }
5011
+ const midpoint = trimmed.length / 2;
5012
+ const first = trimmed.slice(0, midpoint);
5013
+ const second = trimmed.slice(midpoint);
5014
+ return first === second ? first : value;
5015
+ };
4678
5016
  var enableTerminalMouse = () => {
4679
5017
  if (!process.stdin.isTTY || !process.stdout.isTTY) {
4680
5018
  return () => {
@@ -4734,16 +5072,24 @@ var summarizeThinkingSteps = (message) => {
4734
5072
  };
4735
5073
  var selectorValueText = ({
4736
5074
  kind,
5075
+ selectedThreadTitle,
4737
5076
  selectedProject,
4738
5077
  selectedModel,
4739
- selectedMode
5078
+ selectedMode,
5079
+ selectedAgentProfileLabel
4740
5080
  }) => {
5081
+ if (kind === "thread") {
5082
+ return selectedThreadTitle;
5083
+ }
4741
5084
  if (kind === "project") {
4742
5085
  return selectedProject?.name ?? defaultProject.name;
4743
5086
  }
4744
5087
  if (kind === "model") {
4745
5088
  return selectedModel || "auto";
4746
5089
  }
5090
+ if (kind === "profile") {
5091
+ return selectedAgentProfileLabel;
5092
+ }
4747
5093
  return selectedMode;
4748
5094
  };
4749
5095
  var QueuePanel = ({ messages, compact, terminalColumns }) => {
@@ -4785,9 +5131,11 @@ var QueuePanel = ({ messages, compact, terminalColumns }) => {
4785
5131
  };
4786
5132
  var PromptControlBar = ({
4787
5133
  focused,
5134
+ selectedThreadTitle,
4788
5135
  selectedProject,
4789
5136
  selectedModel,
4790
5137
  selectedMode,
5138
+ selectedAgentProfileLabel,
4791
5139
  hasThinkingSteps,
4792
5140
  thinkingExpanded,
4793
5141
  thinkingSummary,
@@ -4799,6 +5147,80 @@ var PromptControlBar = ({
4799
5147
  animate
4800
5148
  }) => {
4801
5149
  const controlGap = compact ? 0 : 1;
5150
+ const compactGroups = [
5151
+ { label: "Session", controls: ["thread", "project"] },
5152
+ { label: "Run", controls: ["model", "mode", "profile"] }
5153
+ ];
5154
+ const showActivity = hasThinkingSteps || busy || statusText !== "Idle";
5155
+ if (compact) {
5156
+ return /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", gap: 0, children: [
5157
+ compactGroups.map((group, index) => /* @__PURE__ */ jsxs8(
5158
+ Box9,
5159
+ {
5160
+ flexDirection: "column",
5161
+ gap: 0,
5162
+ marginTop: index === 0 ? 0 : 1,
5163
+ children: [
5164
+ /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: group.label }),
5165
+ group.controls.map((kind) => {
5166
+ const isFocused = focused === kind;
5167
+ const label = kind.charAt(0).toUpperCase() + kind.slice(1);
5168
+ const rawValue = selectorValueText({
5169
+ kind,
5170
+ selectedThreadTitle,
5171
+ selectedProject,
5172
+ selectedModel,
5173
+ selectedMode,
5174
+ selectedAgentProfileLabel
5175
+ });
5176
+ const valueLimit = Math.max(14, terminalColumns - label.length - 8);
5177
+ const value = truncateForTerminal(rawValue, valueLimit);
5178
+ return /* @__PURE__ */ jsxs8(
5179
+ Text10,
5180
+ {
5181
+ color: isFocused ? terminalTheme.focus : void 0,
5182
+ bold: isFocused,
5183
+ wrap: "truncate",
5184
+ children: [
5185
+ isFocused ? raisedButtonStyle.activeMarker : raisedButtonStyle.inactiveMarker,
5186
+ " ",
5187
+ label,
5188
+ " [",
5189
+ value,
5190
+ "] ",
5191
+ dropdownIndicator
5192
+ ]
5193
+ },
5194
+ kind
5195
+ );
5196
+ })
5197
+ ]
5198
+ },
5199
+ group.label
5200
+ )),
5201
+ showActivity ? /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", gap: 0, marginTop: 1, children: [
5202
+ /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "Activity" }),
5203
+ hasThinkingSteps ? /* @__PURE__ */ jsxs8(
5204
+ Text10,
5205
+ {
5206
+ color: focused === "thinking" ? terminalTheme.focus : void 0,
5207
+ bold: focused === "thinking",
5208
+ wrap: "truncate",
5209
+ children: [
5210
+ focused === "thinking" ? raisedButtonStyle.activeMarker : raisedButtonStyle.inactiveMarker,
5211
+ " ",
5212
+ "Reasoning: ",
5213
+ thinkingExpanded ? "open" : thinkingSummary
5214
+ ]
5215
+ }
5216
+ ) : null,
5217
+ /* @__PURE__ */ jsxs8(Box9, { flexDirection: "row", gap: 1, children: [
5218
+ busy ? /* @__PURE__ */ jsx10(Spinner, { type: "line", animate }) : null,
5219
+ /* @__PURE__ */ jsx10(Text10, { color: statusColor, children: statusText })
5220
+ ] })
5221
+ ] }) : null
5222
+ ] });
5223
+ }
4802
5224
  return /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", gap: 0, children: [
4803
5225
  /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "Settings" }),
4804
5226
  /* @__PURE__ */ jsxs8(Box9, { flexDirection: compact ? "column" : "row", gap: controlGap, flexWrap: "wrap", children: [
@@ -4807,9 +5229,11 @@ var PromptControlBar = ({
4807
5229
  const label = kind.charAt(0).toUpperCase() + kind.slice(1);
4808
5230
  const rawValue = selectorValueText({
4809
5231
  kind,
5232
+ selectedThreadTitle,
4810
5233
  selectedProject,
4811
5234
  selectedModel,
4812
- selectedMode
5235
+ selectedMode,
5236
+ selectedAgentProfileLabel
4813
5237
  });
4814
5238
  const valueLimit = compact ? Math.max(18, terminalColumns - label.length - 8) : kind === "project" ? 30 : 20;
4815
5239
  const value = truncateForTerminal(rawValue, valueLimit);
@@ -4817,9 +5241,9 @@ var PromptControlBar = ({
4817
5241
  Box9,
4818
5242
  {
4819
5243
  borderStyle: raisedButtonStyle.border,
4820
- borderColor: isFocused ? terminalTheme.brand : terminalTheme.muted,
5244
+ borderColor: isFocused ? terminalTheme.focus : terminalTheme.muted,
4821
5245
  paddingX: 1,
4822
- children: /* @__PURE__ */ jsxs8(Text10, { color: isFocused ? terminalTheme.brand : void 0, bold: isFocused, children: [
5246
+ children: /* @__PURE__ */ jsxs8(Text10, { color: isFocused ? terminalTheme.focus : void 0, bold: isFocused, children: [
4823
5247
  isFocused ? raisedButtonStyle.activeMarker : raisedButtonStyle.inactiveMarker,
4824
5248
  " ",
4825
5249
  label,
@@ -4836,12 +5260,12 @@ var PromptControlBar = ({
4836
5260
  Box9,
4837
5261
  {
4838
5262
  borderStyle: raisedButtonStyle.border,
4839
- borderColor: focused === "thinking" ? terminalTheme.brand : terminalTheme.muted,
5263
+ borderColor: focused === "thinking" ? terminalTheme.focus : terminalTheme.muted,
4840
5264
  paddingX: 1,
4841
5265
  children: /* @__PURE__ */ jsxs8(
4842
5266
  Text10,
4843
5267
  {
4844
- color: focused === "thinking" ? terminalTheme.brand : void 0,
5268
+ color: focused === "thinking" ? terminalTheme.focus : void 0,
4845
5269
  bold: focused === "thinking",
4846
5270
  children: [
4847
5271
  focused === "thinking" ? raisedButtonStyle.activeMarker : raisedButtonStyle.inactiveMarker,
@@ -4862,57 +5286,58 @@ var PromptControlBar = ({
4862
5286
  };
4863
5287
  var ChatContextPanel = ({
4864
5288
  width,
5289
+ height,
5290
+ active,
4865
5291
  focused,
4866
5292
  selectedProject,
5293
+ selectedThreadTitle,
4867
5294
  selectedModel,
4868
5295
  selectedMode,
5296
+ selectedAgentProfileLabel,
4869
5297
  hasThinkingSteps,
4870
5298
  thinkingExpanded,
4871
5299
  thinkingSummary,
4872
5300
  statusText,
4873
5301
  statusColor,
4874
5302
  busy,
4875
- animate,
4876
- threadId
5303
+ animate
4877
5304
  }) => {
4878
- const displayThreadId = threadId ? truncateForTerminal(threadId, Math.max(16, width - 14)) : "new thread";
4879
- return /* @__PURE__ */ jsxs8(
5305
+ return /* @__PURE__ */ jsx10(
4880
5306
  TitledBox,
4881
5307
  {
4882
- title: "Context",
5308
+ title: "Settings",
4883
5309
  borderStyle: "round",
4884
- borderColor: terminalTheme.muted,
5310
+ borderColor: active ? terminalTheme.focus : terminalTheme.muted,
4885
5311
  padding: 1,
4886
5312
  width,
4887
- children: [
4888
- /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "Thread" }),
4889
- /* @__PURE__ */ jsx10(Text10, { wrap: "truncate", children: displayThreadId }),
4890
- /* @__PURE__ */ jsx10(
4891
- PromptControlBar,
4892
- {
4893
- focused,
4894
- selectedProject,
4895
- selectedModel,
4896
- selectedMode,
4897
- hasThinkingSteps,
4898
- thinkingExpanded,
4899
- thinkingSummary,
4900
- compact: true,
4901
- terminalColumns: Math.max(24, width - 4),
4902
- statusText,
4903
- statusColor,
4904
- busy,
4905
- animate
4906
- }
4907
- )
4908
- ]
5313
+ height,
5314
+ children: /* @__PURE__ */ jsx10(
5315
+ PromptControlBar,
5316
+ {
5317
+ focused,
5318
+ selectedThreadTitle,
5319
+ selectedProject,
5320
+ selectedModel,
5321
+ selectedMode,
5322
+ selectedAgentProfileLabel,
5323
+ hasThinkingSteps,
5324
+ thinkingExpanded,
5325
+ thinkingSummary,
5326
+ compact: true,
5327
+ terminalColumns: Math.max(24, width - 4),
5328
+ statusText,
5329
+ statusColor,
5330
+ busy,
5331
+ animate
5332
+ }
5333
+ )
4909
5334
  }
4910
5335
  );
4911
5336
  };
4912
5337
  var BottomControls = ({ tab }) => /* @__PURE__ */ jsxs8(Box9, { flexDirection: "row", justifyContent: "space-between", children: [
4913
5338
  /* @__PURE__ */ jsxs8(Text10, { dimColor: true, children: [
4914
5339
  /* @__PURE__ */ jsx10(Text10, { color: terminalTheme.brand, bold: true, children: "Keys" }),
4915
- /* @__PURE__ */ jsx10(Text10, { children: " \u2191/\u2193 scroll | PgUp/PgDn scroll | [/] table page | " }),
5340
+ /* @__PURE__ */ jsx10(Text10, { children: " \u2191/\u2193 move or scroll | PgUp/PgDn scroll | [/] table page | " }),
4916
5341
  /* @__PURE__ */ jsx10(Text10, { color: terminalTheme.brand, bold: true, children: "D" }),
4917
5342
  /* @__PURE__ */ jsx10(Text10, { children: " download | " }),
4918
5343
  /* @__PURE__ */ jsx10(Text10, { color: terminalTheme.brand, bold: true, children: "R" }),
@@ -4960,7 +5385,7 @@ var HitlPanel = ({ hitl, questionIndex, optionIndex, answers, frontendUrl }) =>
4960
5385
  return /* @__PURE__ */ jsxs8(
4961
5386
  Text10,
4962
5387
  {
4963
- color: highlighted ? terminalTheme.brand : void 0,
5388
+ color: highlighted ? terminalTheme.focus : selected ? terminalTheme.selected : void 0,
4964
5389
  dimColor: !highlighted && !selected,
4965
5390
  bold: highlighted,
4966
5391
  inverse: highlighted,
@@ -4994,6 +5419,7 @@ var App = ({
4994
5419
  initialMode = "ask",
4995
5420
  initialTab,
4996
5421
  initialProjectId,
5422
+ configProfile,
4997
5423
  frontendUrl,
4998
5424
  debug = false,
4999
5425
  disableBanner = false,
@@ -5007,6 +5433,8 @@ var App = ({
5007
5433
  const [loaderStep, setLoaderStep] = useState4(0);
5008
5434
  const [bootError, setBootError] = useState4();
5009
5435
  const [input, setInput] = useState4("");
5436
+ const [promptInputActive, setPromptInputActive] = useState4(true);
5437
+ const [starterSelectionsOpen, setStarterSelectionsOpen] = useState4(false);
5010
5438
  const [promptInputScrollOffset, setPromptInputScrollOffset] = useState4(0);
5011
5439
  const [authToken, setAuthToken] = useState4(accessKey);
5012
5440
  const [chatState, setChatState] = useState4({
@@ -5019,6 +5447,7 @@ var App = ({
5019
5447
  const [selectedProject, setSelectedProject] = useState4(null);
5020
5448
  const [selectedModel, setSelectedModel] = useState4(model ?? "");
5021
5449
  const [selectedMode, setSelectedMode] = useState4(initialMode);
5450
+ const [selectedAgentProfileId, setSelectedAgentProfileId] = useState4("");
5022
5451
  const [activeWorkspaceTab, setActiveWorkspaceTab] = useState4(
5023
5452
  () => normalizeWorkspaceTab(initialTab)
5024
5453
  );
@@ -5034,6 +5463,10 @@ var App = ({
5034
5463
  }
5035
5464
  return fallbackModels;
5036
5465
  });
5466
+ const [localSessions, setLocalSessions] = useState4([]);
5467
+ const [remoteThreads, setRemoteThreads] = useState4([]);
5468
+ const [loadingSessions, setLoadingSessions] = useState4(false);
5469
+ const [loadingRemoteThreads, setLoadingRemoteThreads] = useState4(false);
5037
5470
  const [activeSelector, setActiveSelector] = useState4(null);
5038
5471
  const [selectingProject, setSelectingProject] = useState4(false);
5039
5472
  const [loadingProjects, setLoadingProjects] = useState4(false);
@@ -5055,11 +5488,19 @@ var App = ({
5055
5488
  const [workspaceScrollOffset, setWorkspaceScrollOffset] = useState4(0);
5056
5489
  const [workspaceContentHeight, setWorkspaceContentHeight] = useState4(0);
5057
5490
  const [workspaceViewportHeight, setWorkspaceViewportHeight] = useState4(14);
5491
+ const [chatMiddleScrollOffset, setChatMiddleScrollOffset] = useState4(0);
5492
+ const [chatMiddleContentHeight, setChatMiddleContentHeight] = useState4(0);
5493
+ const [chatMiddleViewportHeight, setChatMiddleViewportHeight] = useState4(12);
5058
5494
  const apiBase = useMemo(() => normalizeApiBase(baseUrl), [baseUrl]);
5059
5495
  const frontendBaseUrl = useMemo(
5060
5496
  () => resolveFrontendBaseUrl2(apiBase, frontendUrl),
5061
5497
  [apiBase, frontendUrl]
5062
5498
  );
5499
+ const selectedAgentProfile = useMemo(
5500
+ () => bundledAgentProfiles.find((profile) => profile.id === selectedAgentProfileId),
5501
+ [selectedAgentProfileId]
5502
+ );
5503
+ const selectedAgentProfileLabel = selectedAgentProfile?.display_name ?? "Profile";
5063
5504
  const frontendThreadUrl = useMemo(
5064
5505
  () => buildFrontendThreadUrl(frontendBaseUrl, chatState.threadId),
5065
5506
  [frontendBaseUrl, chatState.threadId]
@@ -5077,10 +5518,12 @@ var App = ({
5077
5518
  const billingHeaderRefreshKey = workspaceRefreshKeys.billing ?? 0;
5078
5519
  const scrollViewRef = React8.useRef(null);
5079
5520
  const workspaceScrollViewRef = React8.useRef(null);
5521
+ const chatMiddleScrollViewRef = React8.useRef(null);
5080
5522
  const controllerRef = React8.useRef(null);
5081
5523
  const queueRef = React8.useRef([]);
5082
5524
  const completionCycleRef = React8.useRef();
5083
5525
  const previousWorkspaceTabRef = React8.useRef(activeWorkspaceTab);
5526
+ const hydratedThreadRef = React8.useRef();
5084
5527
  const [isLoggingIn, setIsLoggingIn] = useState4(false);
5085
5528
  const [expandedThinkingMessageIds, setExpandedThinkingMessageIds] = useState4(
5086
5529
  () => /* @__PURE__ */ new Set()
@@ -5104,10 +5547,31 @@ var App = ({
5104
5547
  const [searchQuery, setSearchQuery] = useState4("");
5105
5548
  const terminalSize = useTerminalSize();
5106
5549
  const mouseTrackingEnabled = isMouseTrackingEnabled();
5107
- const checkHealth = useMemo(() => {
5108
- return async (token) => {
5109
- try {
5110
- const headers = {
5550
+ const selectedLocalSession = useMemo(
5551
+ () => localSessions.find((session) => session.threadId === chatState.threadId),
5552
+ [chatState.threadId, localSessions]
5553
+ );
5554
+ const selectedRemoteThread = useMemo(
5555
+ () => remoteThreads.find((thread) => thread.thread_id === chatState.threadId),
5556
+ [chatState.threadId, remoteThreads]
5557
+ );
5558
+ const selectedThreadTitle = useMemo(
5559
+ () => threadPanelTitle({
5560
+ session: selectedLocalSession,
5561
+ remoteThread: selectedRemoteThread,
5562
+ threadId: chatState.threadId,
5563
+ hasMessages: chatState.messages.length > 0
5564
+ }),
5565
+ [chatState.messages.length, chatState.threadId, selectedLocalSession, selectedRemoteThread]
5566
+ );
5567
+ const threadSelectItems = useMemo(
5568
+ () => buildThreadSelectItems(localSessions, chatState.threadId, remoteThreads),
5569
+ [chatState.threadId, localSessions, remoteThreads]
5570
+ );
5571
+ const checkHealth = useMemo(() => {
5572
+ return async (token) => {
5573
+ try {
5574
+ const headers = {
5111
5575
  "X-Client-Type": "cloudeval-cli",
5112
5576
  "X-Client-Version": CLI_VERSION
5113
5577
  };
@@ -5135,9 +5599,211 @@ var App = ({
5135
5599
  () => ({
5136
5600
  projects: projects.length ? projects : selectedProject ? [selectedProject] : [defaultProject],
5137
5601
  models: modelItems,
5138
- modes: modeItems
5602
+ modes: modeItems,
5603
+ threads: threadSelectItems.map((item) => ({
5604
+ label: item.label,
5605
+ value: item.value.kind === "remote" ? item.value.thread.thread_id : item.value.kind === "session" ? item.value.session.threadId : "new",
5606
+ description: item.description
5607
+ })),
5608
+ profiles: agentProfileItems
5139
5609
  }),
5140
- [modelItems, projects, selectedProject]
5610
+ [modelItems, projects, selectedProject, threadSelectItems]
5611
+ );
5612
+ const refreshLocalSessions = React8.useCallback(async () => {
5613
+ setLoadingSessions(true);
5614
+ try {
5615
+ setLocalSessions(await listSessions(24, configProfile));
5616
+ } finally {
5617
+ setLoadingSessions(false);
5618
+ }
5619
+ }, [configProfile]);
5620
+ const resolveThreadApiToken = React8.useCallback(async () => {
5621
+ if (accessKey) {
5622
+ return accessKey;
5623
+ }
5624
+ try {
5625
+ const token = await getAuthToken({ baseUrl: apiBase });
5626
+ setAuthToken(token);
5627
+ return token;
5628
+ } catch {
5629
+ return authToken;
5630
+ }
5631
+ }, [accessKey, apiBase, authToken]);
5632
+ const fetchThreadApiJson = React8.useCallback(
5633
+ async (path) => {
5634
+ const token = await resolveThreadApiToken();
5635
+ if (!token) {
5636
+ throw new Error("No authentication token is available.");
5637
+ }
5638
+ const response = await fetch(`${apiBase}${path}`, {
5639
+ headers: getCLIHeaders(token)
5640
+ });
5641
+ if (!response.ok) {
5642
+ const detail = await response.text().catch(() => "");
5643
+ throw new Error(
5644
+ `Thread request failed with status ${response.status} ${response.statusText}${detail ? `: ${detail.slice(0, 240)}` : ""}`
5645
+ );
5646
+ }
5647
+ return response.json();
5648
+ },
5649
+ [apiBase, resolveThreadApiToken]
5650
+ );
5651
+ const refreshRemoteThreads = React8.useCallback(async () => {
5652
+ setLoadingRemoteThreads(true);
5653
+ try {
5654
+ const payload = await fetchThreadApiJson(
5655
+ "/chat/threads?limit=50&offset=0&include_archived=true"
5656
+ );
5657
+ const threads = Array.isArray(payload) ? payload : Array.isArray(payload?.threads) ? payload.threads : Array.isArray(payload?.data) ? payload.data : [];
5658
+ setRemoteThreads(
5659
+ threads.filter(
5660
+ (thread) => Boolean(
5661
+ thread && typeof thread === "object" && typeof thread.thread_id === "string"
5662
+ )
5663
+ )
5664
+ );
5665
+ } finally {
5666
+ setLoadingRemoteThreads(false);
5667
+ }
5668
+ }, [fetchThreadApiJson]);
5669
+ const refreshBillingHeader = React8.useCallback(() => {
5670
+ setWorkspaceRefreshKeys((current) => ({
5671
+ ...current,
5672
+ billing: (current.billing ?? 0) + 1
5673
+ }));
5674
+ }, []);
5675
+ const loadRemoteThreadHistory = React8.useCallback(
5676
+ async (threadId) => {
5677
+ const payload = await fetchThreadApiJson(
5678
+ `/chat/threads/${encodeURIComponent(threadId)}?message_limit=80`
5679
+ );
5680
+ return payload;
5681
+ },
5682
+ [fetchThreadApiJson]
5683
+ );
5684
+ const restoreLocalSession = React8.useCallback(
5685
+ (session, options = {}) => {
5686
+ const messages = localSessionMessagesToChatMessages(session);
5687
+ setChatState((prev) => ({
5688
+ ...initialChatState,
5689
+ status: "idle",
5690
+ debug: prev.debug,
5691
+ threadId: session.threadId,
5692
+ messages
5693
+ }));
5694
+ if (session.model) {
5695
+ setSelectedModel(session.model);
5696
+ }
5697
+ if (session.projectId) {
5698
+ const matchingProject = projects.find((project) => project.id === session.projectId);
5699
+ if (matchingProject) {
5700
+ setSelectedProject(matchingProject);
5701
+ }
5702
+ }
5703
+ setActiveSelector(null);
5704
+ autoExpandedThinkingMessageIdsRef.current.clear();
5705
+ setExpandedThinkingMessageIds(/* @__PURE__ */ new Set());
5706
+ setTimeout(() => scrollViewRef.current?.scrollToBottom(), 0);
5707
+ if (!options.silent) {
5708
+ setNotice(`Loaded thread: ${truncateForTerminal(session.title, 90)}`);
5709
+ }
5710
+ },
5711
+ [projects]
5712
+ );
5713
+ const restoreRemoteThread = React8.useCallback(
5714
+ async (thread, options = {}) => {
5715
+ setNotice(`Loading thread: ${truncateForTerminal(thread.title || thread.thread_id, 90)}`);
5716
+ const history = await loadRemoteThreadHistory(thread.thread_id);
5717
+ const messages = remoteThreadMessagesToChatMessages(history);
5718
+ const threadId = history.thread_id || thread.thread_id;
5719
+ const hydratedSummary = {
5720
+ ...thread,
5721
+ thread_id: threadId,
5722
+ title: history.title || history.thread_head?.title || thread.title,
5723
+ updated_at: history.updated_at || history.thread_head?.updated_at || thread.updated_at,
5724
+ created_at: history.created_at || history.thread_head?.created_at || thread.created_at,
5725
+ message_count: history.message_count ?? history.thread_head?.message_count ?? thread.message_count ?? messages.length,
5726
+ project_id: history.project_id || history.thread_head?.project_id || thread.project_id,
5727
+ project_name: history.project_name || history.thread_head?.project_name || thread.project_name,
5728
+ model: history.model || history.thread_head?.model || thread.model
5729
+ };
5730
+ setRemoteThreads((current) => {
5731
+ const withoutCurrent = current.filter((item) => item.thread_id !== threadId);
5732
+ return [hydratedSummary, ...withoutCurrent];
5733
+ });
5734
+ setChatState((prev) => ({
5735
+ ...initialChatState,
5736
+ status: "idle",
5737
+ debug: prev.debug,
5738
+ threadId,
5739
+ messages
5740
+ }));
5741
+ const nextModel = history.model || history.thread_head?.model || thread.model;
5742
+ if (nextModel) {
5743
+ setSelectedModel(nextModel);
5744
+ }
5745
+ const projectId = history.project_id || history.thread_head?.project_id || thread.project_id;
5746
+ if (projectId) {
5747
+ const matchingProject = projects.find((project) => project.id === projectId);
5748
+ if (matchingProject) {
5749
+ setSelectedProject(matchingProject);
5750
+ }
5751
+ }
5752
+ setActiveSelector(null);
5753
+ autoExpandedThinkingMessageIdsRef.current.clear();
5754
+ setExpandedThinkingMessageIds(/* @__PURE__ */ new Set());
5755
+ setTimeout(() => scrollViewRef.current?.scrollToBottom(), 0);
5756
+ if (!options.silent) {
5757
+ setNotice(
5758
+ `Loaded thread: ${truncateForTerminal(
5759
+ history.title || history.thread_head?.title || thread.title || thread.thread_id,
5760
+ 90
5761
+ )}`
5762
+ );
5763
+ }
5764
+ },
5765
+ [loadRemoteThreadHistory, projects]
5766
+ );
5767
+ const startNewThread = React8.useCallback(() => {
5768
+ if (isBusyStatus(chatState.status) || controllerRef.current) {
5769
+ setNotice("Stop the running response before switching threads.");
5770
+ return;
5771
+ }
5772
+ queueRef.current = [];
5773
+ setQueuedMessages([]);
5774
+ setChatState((prev) => ({
5775
+ ...initialChatState,
5776
+ status: "idle",
5777
+ debug: prev.debug,
5778
+ threadId: void 0,
5779
+ messages: []
5780
+ }));
5781
+ hydratedThreadRef.current = void 0;
5782
+ setActiveSelector(null);
5783
+ autoExpandedThinkingMessageIdsRef.current.clear();
5784
+ setExpandedThinkingMessageIds(/* @__PURE__ */ new Set());
5785
+ setNotice("Started a new thread.");
5786
+ setTimeout(() => scrollViewRef.current?.scrollToTop(), 0);
5787
+ }, [chatState.status]);
5788
+ const selectThread = React8.useCallback(
5789
+ (choice) => {
5790
+ if (choice.kind === "new") {
5791
+ startNewThread();
5792
+ return;
5793
+ }
5794
+ if (isBusyStatus(chatState.status) || controllerRef.current) {
5795
+ setNotice("Stop the running response before switching threads.");
5796
+ return;
5797
+ }
5798
+ if (choice.kind === "remote") {
5799
+ void restoreRemoteThread(choice.thread).catch((error) => {
5800
+ setNotice(`Could not load thread: ${error?.message ?? "Unknown error"}`);
5801
+ });
5802
+ return;
5803
+ }
5804
+ restoreLocalSession(choice.session);
5805
+ },
5806
+ [chatState.status, restoreLocalSession, restoreRemoteThread, startNewThread]
5141
5807
  );
5142
5808
  const selectProjectForUser = async (token, user) => {
5143
5809
  setLoadingProjects(true);
@@ -5187,6 +5853,55 @@ var App = ({
5187
5853
  }
5188
5854
  return enableTerminalMouse();
5189
5855
  }, [mouseTrackingEnabled, phase]);
5856
+ useEffect4(() => {
5857
+ if (phase !== "ready") {
5858
+ return;
5859
+ }
5860
+ void refreshLocalSessions().catch((error) => {
5861
+ setNotice(`Local threads unavailable: ${error?.message ?? "Unknown error"}`);
5862
+ });
5863
+ void refreshRemoteThreads().catch((error) => {
5864
+ setNotice(`Cloud threads unavailable: ${error?.message ?? "Unknown error"}`);
5865
+ });
5866
+ }, [phase, refreshLocalSessions, refreshRemoteThreads]);
5867
+ useEffect4(() => {
5868
+ if (phase !== "ready" || !chatState.threadId || chatState.messages.length > 0 || hydratedThreadRef.current === chatState.threadId) {
5869
+ return;
5870
+ }
5871
+ const threadId = chatState.threadId;
5872
+ hydratedThreadRef.current = threadId;
5873
+ void getSession(threadId, configProfile).then((session) => {
5874
+ if (session) {
5875
+ restoreLocalSession(session, { silent: true });
5876
+ return;
5877
+ }
5878
+ return loadRemoteThreadHistory(threadId).then(
5879
+ (history) => restoreRemoteThread(
5880
+ {
5881
+ thread_id: history.thread_id || threadId,
5882
+ title: history.title,
5883
+ updated_at: history.updated_at,
5884
+ created_at: history.created_at,
5885
+ message_count: history.message_count ?? history.messages_page?.length ?? history.thread_messages?.length ?? history.messages?.length ?? 0,
5886
+ project_id: history.project_id,
5887
+ project_name: history.project_name,
5888
+ model: history.model
5889
+ },
5890
+ { silent: true }
5891
+ )
5892
+ );
5893
+ }).catch((error) => {
5894
+ setNotice(`Could not load thread history: ${error?.message ?? "Unknown error"}`);
5895
+ });
5896
+ }, [
5897
+ chatState.messages.length,
5898
+ chatState.threadId,
5899
+ configProfile,
5900
+ loadRemoteThreadHistory,
5901
+ phase,
5902
+ restoreLocalSession,
5903
+ restoreRemoteThread
5904
+ ]);
5190
5905
  useEffect4(() => {
5191
5906
  if (phase !== "ready") {
5192
5907
  return;
@@ -5204,11 +5919,21 @@ var App = ({
5204
5919
  return;
5205
5920
  }
5206
5921
  setBillingHeaderError(void 0);
5207
- getBillingEntitlement({ baseUrl: apiBase, authToken }).then((entitlement) => {
5922
+ const usageRange = thirtyDayUsageRange();
5923
+ Promise.all([
5924
+ getBillingEntitlement({ baseUrl: apiBase, authToken }),
5925
+ getBillingUsageSummary({
5926
+ baseUrl: apiBase,
5927
+ authToken,
5928
+ startAt: usageRange.startAt,
5929
+ endAt: usageRange.endAt,
5930
+ granularity: "day"
5931
+ }).catch(() => null)
5932
+ ]).then(([entitlement, usageSummary]) => {
5208
5933
  if (cancelled) {
5209
5934
  return;
5210
5935
  }
5211
- setBillingHeader(billingHeaderFromEntitlement(entitlement));
5936
+ setBillingHeader(billingHeaderFromEntitlement(entitlement, usageSummary));
5212
5937
  }).catch((error) => {
5213
5938
  if (cancelled) {
5214
5939
  return;
@@ -5239,7 +5964,7 @@ var App = ({
5239
5964
  setIsLoggingIn(true);
5240
5965
  setLoaderStep(1);
5241
5966
  try {
5242
- const { login } = await import("./dist-I4IPYCRC.js");
5967
+ const { login } = await import("./dist-TBAQ5KOK.js");
5243
5968
  const newToken = await login(baseUrl, {
5244
5969
  headless: Boolean(process.env.SSH_TTY || process.env.CI)
5245
5970
  });
@@ -5435,7 +6160,8 @@ var App = ({
5435
6160
  getSubscriptionBillingInfo,
5436
6161
  getTopUpPacks,
5437
6162
  getBillingNotifications,
5438
- getCreditStatus
6163
+ getCreditStatus,
6164
+ getBillingUsageCreditsUsed
5439
6165
  }
5440
6166
  });
5441
6167
  if (!cancelled) {
@@ -5528,7 +6254,7 @@ var App = ({
5528
6254
  };
5529
6255
  const appendUserMessage = (content, options) => {
5530
6256
  const message = {
5531
- id: options?.id ?? randomUUID(),
6257
+ id: options?.id ?? randomUUID2(),
5532
6258
  role: "user",
5533
6259
  content,
5534
6260
  queued: options?.queued,
@@ -5629,7 +6355,7 @@ var App = ({
5629
6355
  const enqueueMessage = (text) => {
5630
6356
  const trimmed = text.trim();
5631
6357
  if (!trimmed) return;
5632
- const queued = { id: randomUUID(), text: trimmed };
6358
+ const queued = { id: randomUUID2(), text: trimmed };
5633
6359
  queueRef.current.push(queued);
5634
6360
  syncQueueState();
5635
6361
  appendUserMessage(trimmed, { id: queued.id, queued: true });
@@ -5737,6 +6463,15 @@ var App = ({
5737
6463
  hasActiveResponse ? "Response canceled. Esc or /stop cancels running chat." : "No running response to cancel."
5738
6464
  );
5739
6465
  };
6466
+ const selectAgentProfileById = (profileId, label) => {
6467
+ setSelectedAgentProfileId(profileId);
6468
+ if (profileId) {
6469
+ setSelectedMode("agent");
6470
+ setNotice(`Profile selected: ${label}. Mode set to Agent.`);
6471
+ return;
6472
+ }
6473
+ setNotice("Agent Profile cleared. Default chat flow active.");
6474
+ };
5740
6475
  const handlePromptSubmit = (value) => {
5741
6476
  const cleanedValue = sanitizeTerminalMultilineInput(value);
5742
6477
  const promptCommand = resolvePromptCommand(cleanedValue, promptCompletionContext);
@@ -5751,13 +6486,43 @@ var App = ({
5751
6486
  setSelectedProject(promptCommand.project);
5752
6487
  setNotice(`Project selected: ${promptCommand.project.name}`);
5753
6488
  return;
6489
+ case "setThread": {
6490
+ if (promptCommand.threadId === "new") {
6491
+ startNewThread();
6492
+ return;
6493
+ }
6494
+ const remoteThread = remoteThreads.find(
6495
+ (candidate) => candidate.thread_id === promptCommand.threadId
6496
+ );
6497
+ if (remoteThread) {
6498
+ selectThread({ kind: "remote", thread: remoteThread });
6499
+ return;
6500
+ }
6501
+ const session = localSessions.find(
6502
+ (candidate) => candidate.threadId === promptCommand.threadId
6503
+ );
6504
+ if (!session) {
6505
+ setNotice(`Thread not loaded: ${promptCommand.label}`);
6506
+ return;
6507
+ }
6508
+ selectThread({ kind: "session", session });
6509
+ return;
6510
+ }
5754
6511
  case "setModel":
5755
6512
  setSelectedModel(promptCommand.model);
5756
6513
  setNotice(`Model selected: ${promptCommand.label}`);
5757
6514
  return;
5758
6515
  case "setMode":
5759
6516
  setSelectedMode(promptCommand.mode);
5760
- setNotice(`Mode selected: ${promptCommand.label}`);
6517
+ if (promptCommand.mode === "ask") {
6518
+ setSelectedAgentProfileId("");
6519
+ }
6520
+ setNotice(
6521
+ promptCommand.mode === "ask" ? "Mode selected: Ask. Agent Profile cleared." : `Mode selected: ${promptCommand.label}`
6522
+ );
6523
+ return;
6524
+ case "setProfile":
6525
+ selectAgentProfileById(promptCommand.profileId, promptCommand.label);
5761
6526
  return;
5762
6527
  case "toggleThinking":
5763
6528
  toggleLatestThinking();
@@ -5772,6 +6537,12 @@ var App = ({
5772
6537
  setActiveWorkspaceTab("help");
5773
6538
  setNotice(void 0);
5774
6539
  return;
6540
+ case "showStarters":
6541
+ setStarterSelectionsOpen(true);
6542
+ setPromptInputActive(true);
6543
+ setFocusedControl("followup:0");
6544
+ setNotice("Starter selections shown. Use Tab/Enter or click a starter.");
6545
+ return;
5775
6546
  case "unknown":
5776
6547
  setNotice(promptCommand.message);
5777
6548
  return;
@@ -5797,6 +6568,10 @@ var App = ({
5797
6568
  const handlePromptChange = (value) => {
5798
6569
  const cleanedValue = sanitizeTerminalMultilineInput(value);
5799
6570
  completionCycleRef.current = void 0;
6571
+ setPromptInputActive(true);
6572
+ if (!isStarterSlashCommandInput(cleanedValue)) {
6573
+ setStarterSelectionsOpen(false);
6574
+ }
5800
6575
  setPromptInputScrollOffset(
5801
6576
  getInputViewport({
5802
6577
  value: cleanedValue,
@@ -5833,15 +6608,15 @@ var App = ({
5833
6608
  return;
5834
6609
  }
5835
6610
  }
5836
- const threadId = chatState.threadId ?? randomUUID();
6611
+ const threadId = chatState.threadId ?? randomUUID2();
5837
6612
  const now = Date.now();
5838
6613
  const userMessage = options.hitlResume || options.queuedMessageId ? void 0 : {
5839
- id: randomUUID(),
6614
+ id: randomUUID2(),
5840
6615
  role: "user",
5841
6616
  content: trimmed,
5842
6617
  createdAt: now
5843
6618
  };
5844
- setChatState((prev) => ({
6619
+ const prepareConnectingState = (prev) => ({
5845
6620
  ...prev,
5846
6621
  threadId,
5847
6622
  status: "connecting",
@@ -5861,7 +6636,12 @@ var App = ({
5861
6636
  updatedAt: now
5862
6637
  } : m.role === "assistant" && m.pending && !options.hitlResume ? { ...m, pending: false, updatedAt: now } : m
5863
6638
  ).concat(userMessage ? [userMessage] : [])
5864
- }));
6639
+ });
6640
+ let latestChatState = prepareConnectingState(chatState);
6641
+ setChatState((prev) => {
6642
+ latestChatState = prepareConnectingState(prev);
6643
+ return latestChatState;
6644
+ });
5865
6645
  const ctrl = new AbortController();
5866
6646
  controllerRef.current = ctrl;
5867
6647
  let shouldDrainQueue = false;
@@ -5870,6 +6650,8 @@ var App = ({
5870
6650
  let rafId = null;
5871
6651
  let lastUpdateTime = Date.now();
5872
6652
  let sawHitlRequest = false;
6653
+ let streamToken = token;
6654
+ let retriedAfterAuthRefresh = false;
5873
6655
  const flushChunks = (immediate = false) => {
5874
6656
  if (pendingChunks.length === 0) {
5875
6657
  rafId = null;
@@ -5879,6 +6661,9 @@ var App = ({
5879
6661
  pendingChunks = [];
5880
6662
  rafId = null;
5881
6663
  lastUpdateTime = Date.now();
6664
+ for (const chunk of chunksToProcess) {
6665
+ latestChatState = reduceChunk(latestChatState, chunk);
6666
+ }
5882
6667
  const applyChunks = () => {
5883
6668
  setChatState((prev) => {
5884
6669
  let state = prev;
@@ -5904,39 +6689,59 @@ var App = ({
5904
6689
  rafId = setTimeout(flushChunks, minInterval - timeSinceLastUpdate);
5905
6690
  }
5906
6691
  };
5907
- for await (const chunk of streamChat({
5908
- baseUrl,
5909
- authToken: token,
5910
- message: options.hitlResume ? "" : trimmed,
5911
- threadId,
5912
- user: {
5913
- id: selectedProject?.user_id ?? currentUserId ?? defaultUser.id,
5914
- name: userName
5915
- },
5916
- project: selectedProject ?? (currentUserId ? { ...defaultProject, user_id: currentUserId } : defaultProject),
5917
- settings: {
5918
- ...selectedModel ? { model: selectedModel } : {},
5919
- mode: selectedMode
5920
- },
5921
- streamingMode: debug ? "DEBUG" : "USER",
5922
- signal: ctrl.signal,
5923
- debug,
5924
- hitlResume: options.hitlResume,
5925
- completeAfterResponse: true,
5926
- responseCompletionGraceMs: 5e3
5927
- })) {
5928
- if (chunk.type === "hitl_request") {
5929
- sawHitlRequest = true;
5930
- pendingChunks.push(chunk);
5931
- if (rafId !== null) {
5932
- clearTimeout(rafId);
5933
- rafId = null;
6692
+ while (true) {
6693
+ try {
6694
+ for await (const chunk of streamChat({
6695
+ baseUrl,
6696
+ authToken: streamToken,
6697
+ message: options.hitlResume ? "" : trimmed,
6698
+ threadId,
6699
+ user: {
6700
+ id: selectedProject?.user_id ?? currentUserId ?? defaultUser.id,
6701
+ name: userName
6702
+ },
6703
+ project: selectedProject ?? (currentUserId ? { ...defaultProject, user_id: currentUserId } : defaultProject),
6704
+ settings: {
6705
+ ...selectedModel ? { model: selectedModel } : {},
6706
+ mode: selectedMode
6707
+ },
6708
+ agentProfileId: selectedAgentProfileId || void 0,
6709
+ streamingMode: debug ? "DEBUG" : "USER",
6710
+ signal: ctrl.signal,
6711
+ debug,
6712
+ hitlResume: options.hitlResume,
6713
+ completeAfterResponse: true,
6714
+ responseCompletionGraceMs: 5e3
6715
+ })) {
6716
+ if (chunk.type === "hitl_request") {
6717
+ sawHitlRequest = true;
6718
+ pendingChunks.push(chunk);
6719
+ if (rafId !== null) {
6720
+ clearTimeout(rafId);
6721
+ rafId = null;
6722
+ }
6723
+ flushChunks(true);
6724
+ break;
6725
+ }
6726
+ pendingChunks.push(chunk);
6727
+ scheduleFlush();
5934
6728
  }
5935
- flushChunks(true);
5936
6729
  break;
6730
+ } catch (error) {
6731
+ if (!accessKey && !retriedAfterAuthRefresh && isExpiredDeviceTokenStreamError(error)) {
6732
+ retriedAfterAuthRefresh = true;
6733
+ if (rafId !== null) {
6734
+ clearTimeout(rafId);
6735
+ rafId = null;
6736
+ }
6737
+ pendingChunks = [];
6738
+ streamToken = await getAuthToken({ baseUrl, forceRefresh: true });
6739
+ setAuthToken(streamToken);
6740
+ setNotice("Session refreshed. Retrying your request...");
6741
+ continue;
6742
+ }
6743
+ throw error;
5937
6744
  }
5938
- pendingChunks.push(chunk);
5939
- scheduleFlush();
5940
6745
  }
5941
6746
  if (rafId !== null) {
5942
6747
  clearTimeout(rafId);
@@ -5946,7 +6751,26 @@ var App = ({
5946
6751
  setNotice(`Action required. Answer in CLI or open frontend: ${frontendThreadUrl}`);
5947
6752
  } else {
5948
6753
  shouldDrainQueue = true;
6754
+ latestChatState = completeActiveAssistantMessage(latestChatState);
5949
6755
  setChatState((prev) => completeActiveAssistantMessage(prev));
6756
+ const finalMessage = [...latestChatState.messages].reverse().find((message) => message.role === "assistant");
6757
+ const finalResponse = collapseRepeatedAssistantText(finalMessage?.content ?? "");
6758
+ if (trimmed && finalResponse.trim()) {
6759
+ try {
6760
+ await recordSessionTurn({
6761
+ threadId,
6762
+ question: trimmed,
6763
+ response: finalResponse,
6764
+ project: selectedProject ? { id: selectedProject.id, name: selectedProject.name } : void 0,
6765
+ model: selectedModel,
6766
+ profile: configProfile
6767
+ });
6768
+ void refreshLocalSessions().catch(() => void 0);
6769
+ void refreshRemoteThreads().catch(() => void 0);
6770
+ } catch (error) {
6771
+ setNotice(`Response complete. Local thread history was not saved: ${error?.message ?? "Unknown error"}`);
6772
+ }
6773
+ }
5950
6774
  }
5951
6775
  } catch (error) {
5952
6776
  const isAbort = ctrl.signal.aborted || error?.name === "AbortError" || error?.message === "This operation was aborted";
@@ -5977,6 +6801,7 @@ var App = ({
5977
6801
  if (controllerRef.current === ctrl) {
5978
6802
  controllerRef.current = null;
5979
6803
  }
6804
+ refreshBillingHeader();
5980
6805
  if (shouldDrainQueue && queueRef.current.length > 0) {
5981
6806
  setTimeout(dequeueAndSend, 0);
5982
6807
  }
@@ -5992,12 +6817,14 @@ var App = ({
5992
6817
  const hasThinkingSteps = Boolean(latestThinkingMessage);
5993
6818
  const hasCancellableReasoning = hasCancellableAssistantWork(chatState.messages);
5994
6819
  const latestFollowUps = latestAssistant?.followUpQuestions?.filter(Boolean) ?? [];
6820
+ const starterSlashCommandActive = isStarterSlashCommandInput(input);
5995
6821
  const promptSuggestions = getPromptSuggestions({
5996
6822
  latestFollowUps,
5997
6823
  messages: chatState.messages,
5998
6824
  mode: selectedMode,
5999
6825
  project: selectedProject,
6000
- limit: terminalSize.columns < 110 ? 3 : 4
6826
+ limit: terminalSize.columns < 110 ? 3 : 4,
6827
+ showStarters: starterSelectionsOpen || starterSlashCommandActive
6001
6828
  });
6002
6829
  const visiblePromptSuggestions = promptSuggestions.prompts.slice(
6003
6830
  0,
@@ -6032,7 +6859,9 @@ var App = ({
6032
6859
  visiblePromptSuggestions.length
6033
6860
  );
6034
6861
  const splitChatLayout = shouldUseSplitChatLayout(terminalSize);
6862
+ const promptControlsCompact = terminalSize.columns < 96;
6035
6863
  const focusedFollowUpIndex = focusFollowUpIndex(focusedControl);
6864
+ const activeChatPanel = chatPanelFocusForControl(focusedControl);
6036
6865
  const controlFocusOrder = buildControlFocusOrder({
6037
6866
  hasThinkingSteps,
6038
6867
  followUpCount: visiblePromptSuggestions.length
@@ -6072,16 +6901,9 @@ var App = ({
6072
6901
  24,
6073
6902
  splitChatLayout ? (chatThreadPanelWidth ?? chatAvailableWidth) - 8 : chatAvailableWidth - 8
6074
6903
  );
6075
- const chatThreadHeight = Math.max(1, tuiLayout.threadHeight - bottomControlsRows);
6076
6904
  const activeWorkspacePanelState = workspacePanelStore[activeWorkspaceTab] ?? createWorkspacePanelState(activeWorkspaceTab, "loading");
6077
6905
  const activeTablePage = tablePageByTab[activeWorkspaceTab] ?? 0;
6078
6906
  const bannerRenderedRows = bannerDisabled ? 0 : estimateBannerRows({ detailsCount: headerDetails.length, columns: bannerContentColumns });
6079
- const workspaceHeaderRows = bannerRenderedRows + 3 + (notice ? 1 : 0);
6080
- const workspaceFooterRows = bottomControlsRows + 1 + (activeSelector === "project" ? tuiLayout.selectorLimit + 4 : 0);
6081
- const workspacePanelViewportRows = Math.max(
6082
- 3,
6083
- terminalSize.rows - workspaceHeaderRows - workspaceFooterRows
6084
- );
6085
6907
  const workspaceContentWidth = Math.max(24, terminalSize.columns - tuiLayout.paddingX * 2 - 3);
6086
6908
  const workspaceTabStartRow = useMemo(() => {
6087
6909
  const rootContentStartRow = 1;
@@ -6107,10 +6929,55 @@ var App = ({
6107
6929
  }),
6108
6930
  [terminalSize.columns, tuiLayout.paddingX, workspaceTabStartRow]
6109
6931
  );
6932
+ const workspaceTabButtonRows = useMemo(() => {
6933
+ if (!workspaceTabHitAreas.length) {
6934
+ return 0;
6935
+ }
6936
+ const firstRow = Math.min(...workspaceTabHitAreas.map((area) => area.startRow));
6937
+ const lastRow = Math.max(...workspaceTabHitAreas.map((area) => area.endRow));
6938
+ return Math.max(0, lastRow - firstRow + 1);
6939
+ }, [workspaceTabHitAreas]);
6940
+ const workspaceTabBarRows = (bannerDisabled ? 3 : 0) + workspaceTabButtonRows + 1 + (bannerDisabled ? 1 : 0);
6941
+ const promptPanelRows = estimatePromptPanelRows({
6942
+ inputRows: promptInputRows,
6943
+ suggestionRows: promptSuggestionRows,
6944
+ compact: promptControlsCompact,
6945
+ hasThinkingSteps,
6946
+ includeControls: !splitChatLayout
6947
+ });
6948
+ const searchPromptPanelRows = estimatePromptPanelRows({
6949
+ inputRows: promptInputRows,
6950
+ suggestionRows: 0,
6951
+ compact: promptControlsCompact,
6952
+ hasThinkingSteps: false,
6953
+ includeControls: false
6954
+ });
6955
+ const workspaceHeaderRows = bannerRenderedRows + workspaceTabBarRows + 2 + (notice ? 1 : 0);
6956
+ const workspaceFooterRows = bottomControlsRows + (activeSelector === "project" ? tuiLayout.selectorLimit + 4 : 0);
6957
+ const workspacePanelViewportRows = getMiddleViewportRows(terminalSize, {
6958
+ headerRows: workspaceHeaderRows,
6959
+ footerRows: workspaceFooterRows
6960
+ });
6961
+ const workspacePanelBodyRows = getFramedBodyRows(workspacePanelViewportRows);
6962
+ const workspacePanelOverflowing = workspaceContentHeight > workspaceViewportHeight;
6963
+ const workspacePanelBodyWidth = Math.max(
6964
+ 24,
6965
+ workspaceContentWidth - (workspacePanelOverflowing ? 7 : 5)
6966
+ );
6967
+ const chatHeaderRows = bannerRenderedRows + workspaceTabBarRows + 2;
6968
+ const chatFooterRows = bottomControlsRows + (isSearching ? searchPromptPanelRows : activeSelector ? 0 : promptPanelRows);
6969
+ const chatMiddleViewportRows = getMiddleViewportRows(terminalSize, {
6970
+ headerRows: chatHeaderRows,
6971
+ footerRows: chatFooterRows
6972
+ });
6973
+ const chatMiddleAuxiliaryRows = (isSearching ? 3 : 0) + (queuedMessages.length > 0 ? 4 : 0) + (notice ? 1 : 0) + (errorText ? 5 : 0) + (chatState.status === "hitl_waiting" && chatState.hitl?.waiting ? 7 : 0) + (activeSelector ? tuiLayout.selectorLimit + 4 : 0);
6974
+ const chatMainPanelRows = Math.max(4, chatMiddleViewportRows - chatMiddleAuxiliaryRows);
6975
+ const chatThreadHeight = getFramedBodyRows(chatMainPanelRows);
6976
+ const chatMiddleOverflowing = chatMiddleContentHeight > chatMiddleViewportHeight;
6110
6977
  const selectorControlStartRow = useMemo(
6111
6978
  () => {
6112
6979
  if (!splitChatLayout) {
6113
- return Math.max(1, terminalSize.rows - (tuiLayout.compact ? 7 : 5));
6980
+ return Math.max(1, terminalSize.rows - (promptControlsCompact ? 7 : 5));
6114
6981
  }
6115
6982
  const auxiliaryRows = (isSearching ? 3 : 0) + (queuedMessages.length > 0 ? 4 : 0) + (notice ? 1 : 0) + (errorText ? 5 : 0);
6116
6983
  return workspaceTabStartRow + 9 + auxiliaryRows;
@@ -6122,13 +6989,13 @@ var App = ({
6122
6989
  queuedMessages.length,
6123
6990
  splitChatLayout,
6124
6991
  terminalSize.rows,
6125
- tuiLayout.compact,
6992
+ promptControlsCompact,
6126
6993
  workspaceTabStartRow
6127
6994
  ]
6128
6995
  );
6129
6996
  const selectorControlHitAreas = useMemo(
6130
6997
  () => getSelectorControlHitAreas({
6131
- compact: tuiLayout.compact || splitChatLayout,
6998
+ compact: promptControlsCompact || splitChatLayout,
6132
6999
  hasThinkingSteps,
6133
7000
  startColumn: tuiLayout.paddingX + 1,
6134
7001
  startRow: selectorControlStartRow,
@@ -6140,7 +7007,7 @@ var App = ({
6140
7007
  selectorControlStartRow,
6141
7008
  splitChatLayout,
6142
7009
  terminalSize.columns,
6143
- tuiLayout.compact,
7010
+ promptControlsCompact,
6144
7011
  tuiLayout.paddingX
6145
7012
  ]
6146
7013
  );
@@ -6150,7 +7017,7 @@ var App = ({
6150
7017
  terminalSize.rows - estimatePromptPanelRows({
6151
7018
  inputRows: promptInputRows,
6152
7019
  suggestionRows: promptSuggestionRows,
6153
- compact: tuiLayout.compact,
7020
+ compact: promptControlsCompact,
6154
7021
  hasThinkingSteps,
6155
7022
  includeControls: !splitChatLayout
6156
7023
  }) + 1
@@ -6160,7 +7027,7 @@ var App = ({
6160
7027
  promptInputRows,
6161
7028
  promptSuggestionRows,
6162
7029
  terminalSize.rows,
6163
- tuiLayout.compact,
7030
+ promptControlsCompact,
6164
7031
  splitChatLayout
6165
7032
  ]
6166
7033
  );
@@ -6174,12 +7041,15 @@ var App = ({
6174
7041
  previousWorkspaceTabRef.current = activeWorkspaceTab;
6175
7042
  setScrollOffset(0);
6176
7043
  setContentHeight(0);
7044
+ setChatMiddleScrollOffset(0);
7045
+ setChatMiddleContentHeight(0);
6177
7046
  setWorkspaceScrollOffset(0);
6178
7047
  setWorkspaceContentHeight(0);
6179
7048
  }
6180
7049
  if (activeWorkspaceTab !== "chat") {
6181
7050
  setTimeout(() => workspaceScrollViewRef.current?.scrollToTop(), 0);
6182
7051
  } else {
7052
+ setTimeout(() => chatMiddleScrollViewRef.current?.scrollToTop(), 0);
6183
7053
  setTimeout(() => scrollViewRef.current?.scrollToTop(), 0);
6184
7054
  }
6185
7055
  }, [activeWorkspaceTab]);
@@ -6189,6 +7059,7 @@ var App = ({
6189
7059
  return;
6190
7060
  }
6191
7061
  setInput("");
7062
+ setStarterSelectionsOpen(false);
6192
7063
  completionCycleRef.current = void 0;
6193
7064
  const noticePrefix = promptSuggestions.kind === "starter" ? "Starter" : "Follow-up";
6194
7065
  setNotice(`${noticePrefix} queued: ${truncateForTerminal(oneLine(question), 90)}`);
@@ -6207,6 +7078,10 @@ var App = ({
6207
7078
  toggleLatestThinking();
6208
7079
  return;
6209
7080
  }
7081
+ if (focusedControl === "threadPanel") {
7082
+ setNotice("Thread selected. Use arrows, Page Up/Down, or mouse wheel to scroll.");
7083
+ return;
7084
+ }
6210
7085
  const followUpIndex = focusFollowUpIndex(focusedControl);
6211
7086
  if (followUpIndex !== void 0) {
6212
7087
  submitFollowUp(followUpIndex);
@@ -6244,6 +7119,11 @@ var App = ({
6244
7119
  setNotice(void 0);
6245
7120
  return;
6246
7121
  }
7122
+ if (splitChatLayout && mouseEvent.y >= selectorControlStartRow && mouseEvent.y < promptPanelStartRow && mouseEvent.x >= tuiLayout.paddingX + chatContextPanelWidth + chatSplitGap) {
7123
+ setFocusedControl("threadPanel");
7124
+ setNotice(void 0);
7125
+ return;
7126
+ }
6247
7127
  }
6248
7128
  if (mouseEvent.released) {
6249
7129
  return;
@@ -6251,7 +7131,7 @@ var App = ({
6251
7131
  if (!visiblePromptSuggestions.length) {
6252
7132
  return;
6253
7133
  }
6254
- const promptAreaTop = Math.max(1, terminalSize.rows - (visiblePromptSuggestions.length ? 8 : 5));
7134
+ const promptAreaTop = promptPanelStartRow;
6255
7135
  if (mouseEvent.y < promptAreaTop) {
6256
7136
  return;
6257
7137
  }
@@ -6296,7 +7176,11 @@ var App = ({
6296
7176
  );
6297
7177
  } else if (activeWorkspaceTab !== "chat") {
6298
7178
  workspaceScrollViewRef.current?.scrollBy(mouseWheelDelta);
7179
+ } else if (chatMiddleOverflowing) {
7180
+ setFocusedControl("threadPanel");
7181
+ chatMiddleScrollViewRef.current?.scrollBy(mouseWheelDelta);
6299
7182
  } else {
7183
+ setFocusedControl("threadPanel");
6300
7184
  scrollViewRef.current?.scrollBy(mouseWheelDelta);
6301
7185
  }
6302
7186
  } else {
@@ -6309,7 +7193,7 @@ var App = ({
6309
7193
  return;
6310
7194
  }
6311
7195
  const lowerInput = inputKey.toLowerCase();
6312
- if (phase === "ready" && !input.trim()) {
7196
+ if (phase === "ready" && (!input.trim() || !promptInputActive)) {
6313
7197
  const shortcutTab = workspaceTabFromShortcut(inputKey);
6314
7198
  if (shortcutTab) {
6315
7199
  setActiveWorkspaceTab(shortcutTab);
@@ -6441,7 +7325,7 @@ var App = ({
6441
7325
  }
6442
7326
  return;
6443
7327
  }
6444
- if ((key.tab || inputKey === " ") && input.trimStart().startsWith("/")) {
7328
+ if (promptInputActive && (key.tab || inputKey === " ") && input.trimStart().startsWith("/")) {
6445
7329
  const completion = completePromptInput(
6446
7330
  input,
6447
7331
  promptCompletionContext,
@@ -6468,7 +7352,7 @@ var App = ({
6468
7352
  }
6469
7353
  return;
6470
7354
  }
6471
- if (key.rightArrow && !key.ctrl && !key.meta && promptGhostSuffix) {
7355
+ if (promptInputActive && key.rightArrow && !key.ctrl && !key.meta && promptGhostSuffix) {
6472
7356
  const nextValue = `${input}${promptGhostSuffix}`;
6473
7357
  setInput(nextValue);
6474
7358
  setPromptInputScrollOffset(
@@ -6515,7 +7399,7 @@ var App = ({
6515
7399
  return;
6516
7400
  }
6517
7401
  }
6518
- if (phase === "ready" && !input.trim()) {
7402
+ if (phase === "ready" && (!input.trim() || !promptInputActive)) {
6519
7403
  if (key.tab || inputKey === " ") {
6520
7404
  setFocusedControl((current) => nextControlFocus(current, controlFocusOrder));
6521
7405
  return;
@@ -6528,6 +7412,30 @@ var App = ({
6528
7412
  setFocusedControl((current) => nextControlFocus(current, controlFocusOrder));
6529
7413
  return;
6530
7414
  }
7415
+ if (key.upArrow && !key.ctrl && !key.meta && !key.shift) {
7416
+ if (focusedControl === "threadPanel") {
7417
+ if (chatMiddleOverflowing) {
7418
+ chatMiddleScrollViewRef.current?.scrollBy(-1);
7419
+ } else {
7420
+ scrollViewRef.current?.scrollBy(-1);
7421
+ }
7422
+ } else {
7423
+ setFocusedControl((current) => nextControlFocus(current, controlFocusOrder, -1));
7424
+ }
7425
+ return;
7426
+ }
7427
+ if (key.downArrow && !key.ctrl && !key.meta && !key.shift) {
7428
+ if (focusedControl === "threadPanel") {
7429
+ if (chatMiddleOverflowing) {
7430
+ chatMiddleScrollViewRef.current?.scrollBy(1);
7431
+ } else {
7432
+ scrollViewRef.current?.scrollBy(1);
7433
+ }
7434
+ } else {
7435
+ setFocusedControl((current) => nextControlFocus(current, controlFocusOrder));
7436
+ }
7437
+ return;
7438
+ }
6531
7439
  if (key.return && !key.meta && !key.ctrl) {
6532
7440
  handleFocusedControlEnter();
6533
7441
  return;
@@ -6535,38 +7443,86 @@ var App = ({
6535
7443
  }
6536
7444
  if (phase === "ready") {
6537
7445
  if (key.ctrl && key.upArrow) {
6538
- scrollViewRef.current?.scrollBy(-1);
7446
+ setFocusedControl("threadPanel");
7447
+ if (chatMiddleOverflowing) {
7448
+ chatMiddleScrollViewRef.current?.scrollBy(-1);
7449
+ } else {
7450
+ scrollViewRef.current?.scrollBy(-1);
7451
+ }
6539
7452
  return;
6540
7453
  }
6541
7454
  if (key.ctrl && key.downArrow) {
6542
- scrollViewRef.current?.scrollBy(1);
7455
+ setFocusedControl("threadPanel");
7456
+ if (chatMiddleOverflowing) {
7457
+ chatMiddleScrollViewRef.current?.scrollBy(1);
7458
+ } else {
7459
+ scrollViewRef.current?.scrollBy(1);
7460
+ }
6543
7461
  return;
6544
7462
  }
6545
- if (!input.trim() && key.upArrow && !key.ctrl && !key.meta && !key.shift) {
6546
- scrollViewRef.current?.scrollBy(-1);
7463
+ if ((!input.trim() || !promptInputActive) && key.upArrow && !key.ctrl && !key.meta && !key.shift) {
7464
+ setFocusedControl("threadPanel");
7465
+ if (chatMiddleOverflowing) {
7466
+ chatMiddleScrollViewRef.current?.scrollBy(-1);
7467
+ } else {
7468
+ scrollViewRef.current?.scrollBy(-1);
7469
+ }
6547
7470
  return;
6548
7471
  }
6549
- if (!input.trim() && key.downArrow && !key.ctrl && !key.meta && !key.shift) {
6550
- scrollViewRef.current?.scrollBy(1);
7472
+ if ((!input.trim() || !promptInputActive) && key.downArrow && !key.ctrl && !key.meta && !key.shift) {
7473
+ setFocusedControl("threadPanel");
7474
+ if (chatMiddleOverflowing) {
7475
+ chatMiddleScrollViewRef.current?.scrollBy(1);
7476
+ } else {
7477
+ scrollViewRef.current?.scrollBy(1);
7478
+ }
6551
7479
  return;
6552
7480
  }
6553
7481
  if (key.pageUp && !key.ctrl && !key.meta) {
6554
- scrollViewRef.current?.scrollBy(-Math.max(1, Math.floor(viewportHeight * 0.8)));
7482
+ setFocusedControl("threadPanel");
7483
+ if (chatMiddleOverflowing) {
7484
+ chatMiddleScrollViewRef.current?.scrollBy(
7485
+ -Math.max(1, Math.floor(chatMiddleViewportHeight * 0.8))
7486
+ );
7487
+ } else {
7488
+ scrollViewRef.current?.scrollBy(-Math.max(1, Math.floor(viewportHeight * 0.8)));
7489
+ }
6555
7490
  return;
6556
7491
  }
6557
7492
  if (key.pageDown && !key.ctrl && !key.meta) {
6558
- scrollViewRef.current?.scrollBy(Math.max(1, Math.floor(viewportHeight * 0.8)));
7493
+ setFocusedControl("threadPanel");
7494
+ if (chatMiddleOverflowing) {
7495
+ chatMiddleScrollViewRef.current?.scrollBy(
7496
+ Math.max(1, Math.floor(chatMiddleViewportHeight * 0.8))
7497
+ );
7498
+ } else {
7499
+ scrollViewRef.current?.scrollBy(Math.max(1, Math.floor(viewportHeight * 0.8)));
7500
+ }
6559
7501
  return;
6560
7502
  }
6561
7503
  if (key.ctrl && inputKey.toLowerCase() === "h" && inputKey.toLowerCase() !== "l") {
6562
- scrollViewRef.current?.scrollToTop();
7504
+ setFocusedControl("threadPanel");
7505
+ if (chatMiddleOverflowing) {
7506
+ chatMiddleScrollViewRef.current?.scrollToTop();
7507
+ } else {
7508
+ scrollViewRef.current?.scrollToTop();
7509
+ }
6563
7510
  return;
6564
7511
  }
6565
7512
  if (key.ctrl && inputKey.toLowerCase() === "e") {
6566
- scrollViewRef.current?.scrollToBottom();
7513
+ setFocusedControl("threadPanel");
7514
+ if (chatMiddleOverflowing) {
7515
+ chatMiddleScrollViewRef.current?.scrollToBottom();
7516
+ } else {
7517
+ scrollViewRef.current?.scrollToBottom();
7518
+ }
6567
7519
  return;
6568
7520
  }
6569
7521
  }
7522
+ if (phase === "ready" && activeWorkspaceTab === "chat" && !promptInputActive && !activeSelector && !isSearching && isPromptTextInput(inputKey, key)) {
7523
+ handlePromptChange(`${input}${inputKey}`);
7524
+ return;
7525
+ }
6570
7526
  if (key.escape && (controllerRef.current || isBusyStatus(chatState.status) || hasCancellableAssistantWork(chatState.messages))) {
6571
7527
  stopActiveChat();
6572
7528
  }
@@ -6588,7 +7544,7 @@ var App = ({
6588
7544
  { isActive: phase === "ready" }
6589
7545
  );
6590
7546
  if (phase === "boot") {
6591
- return /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", paddingX: tuiLayout.paddingX, paddingY: 0, children: [
7547
+ return /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", paddingX: tuiLayout.paddingX, paddingY: 0, height: terminalSize.rows, children: [
6592
7548
  /* @__PURE__ */ jsx10(Banner, { disable: bannerDisabled, details: headerDetails, terminalColumns: bannerContentColumns }),
6593
7549
  isLoggingIn ? /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", gap: 1, padding: 1, children: [
6594
7550
  /* @__PURE__ */ jsx10(Text10, { color: terminalTheme.brand, bold: true, children: "Signing in..." }),
@@ -6617,7 +7573,7 @@ var App = ({
6617
7573
  );
6618
7574
  }
6619
7575
  if (phase === "error") {
6620
- return /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", padding: 1, children: [
7576
+ return /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", padding: 1, height: terminalSize.rows, children: [
6621
7577
  /* @__PURE__ */ jsx10(Text10, { color: terminalTheme.danger, children: "Failed to start CLI." }),
6622
7578
  /* @__PURE__ */ jsx10(Text10, { children: bootError ?? "Unknown error" }),
6623
7579
  /* @__PURE__ */ jsxs8(Text10, { children: [
@@ -6645,11 +7601,12 @@ var App = ({
6645
7601
  const threadPanel = /* @__PURE__ */ jsx10(
6646
7602
  TitledBox,
6647
7603
  {
6648
- title: "Thread",
7604
+ title: selectedThreadTitle,
6649
7605
  borderStyle: "round",
6650
- borderColor: terminalTheme.muted,
7606
+ borderColor: activeChatPanel === "thread" ? terminalTheme.focus : terminalTheme.muted,
6651
7607
  padding: 1,
6652
7608
  width: chatThreadPanelWidth,
7609
+ height: chatMainPanelRows,
6653
7610
  children: /* @__PURE__ */ jsxs8(Box9, { flexDirection: "row", children: [
6654
7611
  /* @__PURE__ */ jsx10(Box9, { flexShrink: 1, width: threadContentWidth, children: /* @__PURE__ */ jsx10(
6655
7612
  ScrollView,
@@ -6702,13 +7659,15 @@ var App = ({
6702
7659
  PromptControlBar,
6703
7660
  {
6704
7661
  focused: focusedControl,
7662
+ selectedThreadTitle,
6705
7663
  selectedProject,
6706
7664
  selectedModel,
6707
7665
  selectedMode,
7666
+ selectedAgentProfileLabel,
6708
7667
  hasThinkingSteps,
6709
7668
  thinkingExpanded,
6710
7669
  thinkingSummary,
6711
- compact: tuiLayout.compact,
7670
+ compact: promptControlsCompact,
6712
7671
  terminalColumns: terminalSize.columns,
6713
7672
  statusText: chatStatusText,
6714
7673
  statusColor: chatStatusColor,
@@ -6721,7 +7680,7 @@ var App = ({
6721
7680
  label: `${p.name} (${p.cloud_provider ?? "cloud"})${p.name === "Playground" ? " [Playground]" : ""}`,
6722
7681
  value: p
6723
7682
  }));
6724
- return /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", paddingX: tuiLayout.paddingX, paddingY: 0, gap: 0, children: [
7683
+ return /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", paddingX: tuiLayout.paddingX, paddingY: 0, gap: 0, height: terminalSize.rows, children: [
6725
7684
  /* @__PURE__ */ jsx10(Banner, { disable: bannerDisabled, details: headerDetails, terminalColumns: bannerContentColumns }),
6726
7685
  /* @__PURE__ */ jsx10(Text10, { children: "Select a project to chat with:" }),
6727
7686
  loadingProjects ? /* @__PURE__ */ jsxs8(Box9, { flexDirection: "row", gap: 1, children: [
@@ -6743,7 +7702,7 @@ var App = ({
6743
7702
  ] });
6744
7703
  }
6745
7704
  if (activeWorkspaceTab !== "chat") {
6746
- return /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", paddingX: tuiLayout.paddingX, paddingY: 0, gap: 0, children: [
7705
+ return /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", paddingX: tuiLayout.paddingX, paddingY: 0, gap: 0, height: terminalSize.rows, children: [
6747
7706
  /* @__PURE__ */ jsx10(Banner, { disable: bannerDisabled, details: headerDetails, terminalColumns: bannerContentColumns }),
6748
7707
  /* @__PURE__ */ jsx10(
6749
7708
  WorkspaceTabBar,
@@ -6755,43 +7714,56 @@ var App = ({
6755
7714
  ),
6756
7715
  /* @__PURE__ */ jsx10(Text10, { dimColor: true, wrap: "wrap", children: workspaceTabDescriptions[activeWorkspaceTab] }),
6757
7716
  notice ? /* @__PURE__ */ jsx10(Text10, { dimColor: true, wrap: "wrap", children: notice }) : null,
6758
- /* @__PURE__ */ jsxs8(Box9, { flexDirection: "row", children: [
6759
- /* @__PURE__ */ jsx10(Box9, { flexShrink: 1, width: workspaceContentWidth, children: /* @__PURE__ */ jsx10(
6760
- ScrollView,
6761
- {
6762
- ref: workspaceScrollViewRef,
6763
- height: workspacePanelViewportRows,
6764
- onScroll: (offset) => setWorkspaceScrollOffset(offset),
6765
- onContentHeightChange: (height) => setWorkspaceContentHeight(height),
6766
- onViewportSizeChange: (size) => setWorkspaceViewportHeight(size.height),
6767
- children: /* @__PURE__ */ jsx10(
6768
- WorkspacePanel,
7717
+ /* @__PURE__ */ jsx10(
7718
+ TitledBox,
7719
+ {
7720
+ title: workspaceTabLabels[activeWorkspaceTab],
7721
+ borderStyle: "round",
7722
+ borderColor: terminalTheme.muted,
7723
+ padding: 1,
7724
+ marginTop: 1,
7725
+ height: workspacePanelViewportRows,
7726
+ width: workspaceContentWidth,
7727
+ children: /* @__PURE__ */ jsxs8(Box9, { flexDirection: "row", children: [
7728
+ /* @__PURE__ */ jsx10(Box9, { flexShrink: 1, width: workspacePanelBodyWidth, children: /* @__PURE__ */ jsx10(
7729
+ ScrollView,
6769
7730
  {
6770
- tab: activeWorkspaceTab,
6771
- state: activeWorkspacePanelState,
6772
- projects,
6773
- selectedProject,
6774
- currentUserId,
6775
- selectedModel,
6776
- selectedMode,
6777
- apiBase,
6778
- frontendUrl: workspaceFrontendUrl,
6779
- terminalColumns: workspaceContentWidth,
6780
- tablePage: activeTablePage,
6781
- animate: animationsEnabled
7731
+ ref: workspaceScrollViewRef,
7732
+ height: workspacePanelBodyRows,
7733
+ onScroll: (offset) => setWorkspaceScrollOffset(offset),
7734
+ onContentHeightChange: (height) => setWorkspaceContentHeight(height),
7735
+ onViewportSizeChange: (size) => setWorkspaceViewportHeight(size.height),
7736
+ children: /* @__PURE__ */ jsx10(
7737
+ WorkspacePanel,
7738
+ {
7739
+ tab: activeWorkspaceTab,
7740
+ state: activeWorkspacePanelState,
7741
+ projects,
7742
+ selectedProject,
7743
+ currentUserId,
7744
+ selectedModel,
7745
+ selectedMode,
7746
+ apiBase,
7747
+ frontendUrl: workspaceFrontendUrl,
7748
+ terminalColumns: workspacePanelBodyWidth,
7749
+ tablePage: activeTablePage,
7750
+ animate: animationsEnabled,
7751
+ framed: false
7752
+ }
7753
+ )
6782
7754
  }
6783
- )
6784
- }
6785
- ) }),
6786
- /* @__PURE__ */ jsx10(
6787
- Scrollbar2,
6788
- {
6789
- scrollOffset: workspaceScrollOffset,
6790
- contentHeight: workspaceContentHeight,
6791
- viewportHeight: workspaceViewportHeight
6792
- }
6793
- )
6794
- ] }),
7755
+ ) }),
7756
+ workspacePanelOverflowing ? /* @__PURE__ */ jsx10(
7757
+ Scrollbar2,
7758
+ {
7759
+ scrollOffset: workspaceScrollOffset,
7760
+ contentHeight: workspaceContentHeight,
7761
+ viewportHeight: workspaceViewportHeight
7762
+ }
7763
+ ) : null
7764
+ ] })
7765
+ }
7766
+ ),
6795
7767
  /* @__PURE__ */ jsx10(BottomControls, { tab: activeWorkspaceTab }),
6796
7768
  activeSelector === "project" ? /* @__PURE__ */ jsx10(
6797
7769
  SelectPanel,
@@ -6819,7 +7791,7 @@ var App = ({
6819
7791
  ) : null
6820
7792
  ] });
6821
7793
  }
6822
- return /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", paddingX: tuiLayout.paddingX, paddingY: 0, gap: 0, children: [
7794
+ return /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", paddingX: tuiLayout.paddingX, paddingY: 0, gap: 0, height: terminalSize.rows, children: [
6823
7795
  /* @__PURE__ */ jsx10(Banner, { disable: bannerDisabled, details: headerDetails, terminalColumns: bannerContentColumns }),
6824
7796
  /* @__PURE__ */ jsx10(
6825
7797
  WorkspaceTabBar,
@@ -6829,150 +7801,224 @@ var App = ({
6829
7801
  billingSummary: bannerDisabled ? billingHeader : void 0
6830
7802
  }
6831
7803
  ),
6832
- isSearching ? /* @__PURE__ */ jsx10(
6833
- TitledBox,
6834
- {
6835
- title: "Search",
6836
- borderStyle: "double",
6837
- borderColor: terminalTheme.warning,
6838
- padding: 0,
6839
- paddingX: 1,
6840
- children: /* @__PURE__ */ jsxs8(Text10, { children: [
6841
- "Found: ",
6842
- displayedMessages.length,
6843
- " matches"
6844
- ] })
6845
- }
6846
- ) : null,
6847
- queuedMessages.length > 0 ? /* @__PURE__ */ jsx10(
6848
- QueuePanel,
6849
- {
6850
- messages: queuedMessages,
6851
- compact: tuiLayout.compact,
6852
- terminalColumns: terminalSize.columns
6853
- }
6854
- ) : null,
6855
- notice ? /* @__PURE__ */ jsx10(Text10, { dimColor: true, wrap: "wrap", children: notice }) : null,
6856
- errorText ? /* @__PURE__ */ jsxs8(
6857
- TitledBox,
6858
- {
6859
- title: "Error Details",
6860
- borderStyle: "round",
6861
- borderColor: terminalTheme.danger,
6862
- padding: 0,
6863
- paddingX: 1,
6864
- marginTop: 1,
6865
- children: [
6866
- /* @__PURE__ */ jsx10(Text10, { color: terminalTheme.danger, wrap: "wrap", children: errorText }),
6867
- !hasThinkingSteps ? /* @__PURE__ */ jsx10(Text10, { dimColor: true, wrap: "wrap", children: "No thinking steps were received before the backend returned this error." }) : null
6868
- ]
6869
- }
6870
- ) : null,
6871
- splitChatLayout ? /* @__PURE__ */ jsxs8(Box9, { flexDirection: "row", columnGap: chatSplitGap, children: [
7804
+ /* @__PURE__ */ jsx10(Text10, { dimColor: true, wrap: "wrap", children: workspaceTabDescriptions.chat }),
7805
+ /* @__PURE__ */ jsxs8(Box9, { flexDirection: "row", marginTop: 1, children: [
6872
7806
  /* @__PURE__ */ jsx10(
6873
- ChatContextPanel,
7807
+ Box9,
6874
7808
  {
6875
- width: chatContextPanelWidth,
6876
- focused: focusedControl,
6877
- selectedProject,
6878
- selectedModel,
6879
- selectedMode,
6880
- hasThinkingSteps,
6881
- thinkingExpanded,
6882
- thinkingSummary,
6883
- statusText: chatStatusText,
6884
- statusColor: chatStatusColor,
6885
- busy: chatBusy,
6886
- animate: animationsEnabled,
6887
- threadId: chatState.threadId
7809
+ flexShrink: 1,
7810
+ width: Math.max(24, chatAvailableWidth - (chatMiddleOverflowing ? 2 : 0)),
7811
+ children: /* @__PURE__ */ jsx10(
7812
+ ScrollView,
7813
+ {
7814
+ ref: chatMiddleScrollViewRef,
7815
+ height: chatMiddleViewportRows,
7816
+ onScroll: (offset) => setChatMiddleScrollOffset(offset),
7817
+ onContentHeightChange: (height) => setChatMiddleContentHeight(height),
7818
+ onViewportSizeChange: (size) => setChatMiddleViewportHeight(size.height),
7819
+ children: /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", children: [
7820
+ isSearching ? /* @__PURE__ */ jsx10(
7821
+ TitledBox,
7822
+ {
7823
+ title: "Search",
7824
+ borderStyle: "double",
7825
+ borderColor: terminalTheme.warning,
7826
+ padding: 0,
7827
+ paddingX: 1,
7828
+ children: /* @__PURE__ */ jsxs8(Text10, { children: [
7829
+ "Found: ",
7830
+ displayedMessages.length,
7831
+ " matches"
7832
+ ] })
7833
+ }
7834
+ ) : null,
7835
+ queuedMessages.length > 0 ? /* @__PURE__ */ jsx10(
7836
+ QueuePanel,
7837
+ {
7838
+ messages: queuedMessages,
7839
+ compact: tuiLayout.compact,
7840
+ terminalColumns: terminalSize.columns
7841
+ }
7842
+ ) : null,
7843
+ notice ? /* @__PURE__ */ jsx10(Text10, { dimColor: true, wrap: "wrap", children: notice }) : null,
7844
+ errorText ? /* @__PURE__ */ jsxs8(
7845
+ TitledBox,
7846
+ {
7847
+ title: "Error Details",
7848
+ borderStyle: "round",
7849
+ borderColor: terminalTheme.danger,
7850
+ padding: 0,
7851
+ paddingX: 1,
7852
+ marginTop: 1,
7853
+ children: [
7854
+ /* @__PURE__ */ jsx10(Text10, { color: terminalTheme.danger, wrap: "wrap", children: errorText }),
7855
+ !hasThinkingSteps ? /* @__PURE__ */ jsx10(Text10, { dimColor: true, wrap: "wrap", children: "No thinking steps were received before the backend returned this error." }) : null
7856
+ ]
7857
+ }
7858
+ ) : null,
7859
+ splitChatLayout ? /* @__PURE__ */ jsxs8(Box9, { flexDirection: "row", columnGap: chatSplitGap, children: [
7860
+ /* @__PURE__ */ jsx10(
7861
+ ChatContextPanel,
7862
+ {
7863
+ width: chatContextPanelWidth,
7864
+ height: chatMainPanelRows,
7865
+ active: activeChatPanel === "settings",
7866
+ focused: focusedControl,
7867
+ selectedThreadTitle,
7868
+ selectedProject,
7869
+ selectedModel,
7870
+ selectedMode,
7871
+ selectedAgentProfileLabel,
7872
+ hasThinkingSteps,
7873
+ thinkingExpanded,
7874
+ thinkingSummary,
7875
+ statusText: chatStatusText,
7876
+ statusColor: chatStatusColor,
7877
+ busy: chatBusy,
7878
+ animate: animationsEnabled
7879
+ }
7880
+ ),
7881
+ threadPanel
7882
+ ] }) : threadPanel,
7883
+ chatState.status === "hitl_waiting" && chatState.hitl?.waiting ? /* @__PURE__ */ jsx10(
7884
+ HitlPanel,
7885
+ {
7886
+ hitl: chatState.hitl,
7887
+ questionIndex: hitlQuestionIndex,
7888
+ optionIndex: hitlOptionIndex,
7889
+ answers: hitlAnswers,
7890
+ frontendUrl: frontendThreadUrl
7891
+ }
7892
+ ) : null,
7893
+ activeSelector === "thread" ? /* @__PURE__ */ jsx10(
7894
+ SelectPanel,
7895
+ {
7896
+ title: "Select Thread",
7897
+ items: loadingSessions || loadingRemoteThreads ? [
7898
+ {
7899
+ label: "Loading threads...",
7900
+ value: { kind: "new" },
7901
+ description: "Reading CloudEval threads and local CLI history."
7902
+ }
7903
+ ] : threadSelectItems,
7904
+ selectedIndex: Math.max(
7905
+ 0,
7906
+ threadSelectItems.findIndex(
7907
+ (item) => item.value.kind === "remote" && item.value.thread.thread_id === chatState.threadId || item.value.kind === "session" && item.value.session.threadId === chatState.threadId
7908
+ )
7909
+ ),
7910
+ onSubmit: (item) => selectThread(item.value),
7911
+ onCancel: () => setActiveSelector(null),
7912
+ limit: tuiLayout.selectorLimit
7913
+ }
7914
+ ) : null,
7915
+ activeSelector === "project" ? /* @__PURE__ */ jsx10(
7916
+ SelectPanel,
7917
+ {
7918
+ title: "Select Project",
7919
+ items: (projects.length ? projects : [defaultProject]).map((project) => ({
7920
+ label: `${project.name} (${project.cloud_provider ?? "cloud"})`,
7921
+ value: project,
7922
+ description: project.id
7923
+ })),
7924
+ selectedIndex: Math.max(
7925
+ 0,
7926
+ (projects.length ? projects : [defaultProject]).findIndex(
7927
+ (project) => project.id === (selectedProject ?? defaultProject).id
7928
+ )
7929
+ ),
7930
+ onSubmit: (item) => {
7931
+ setSelectedProject(item.value);
7932
+ setActiveSelector(null);
7933
+ },
7934
+ onCancel: () => setActiveSelector(null),
7935
+ limit: tuiLayout.selectorLimit
7936
+ }
7937
+ ) : null,
7938
+ activeSelector === "model" ? /* @__PURE__ */ jsx10(
7939
+ SelectPanel,
7940
+ {
7941
+ title: "Select Model",
7942
+ items: modelItems,
7943
+ selectedIndex: Math.max(
7944
+ 0,
7945
+ modelItems.findIndex((item) => item.value === selectedModel)
7946
+ ),
7947
+ onSubmit: (item) => {
7948
+ setSelectedModel(item.value);
7949
+ setActiveSelector(null);
7950
+ },
7951
+ onCancel: () => setActiveSelector(null),
7952
+ limit: tuiLayout.selectorLimit
7953
+ }
7954
+ ) : null,
7955
+ activeSelector === "mode" ? /* @__PURE__ */ jsx10(
7956
+ SelectPanel,
7957
+ {
7958
+ title: "Select Mode",
7959
+ items: modeItems,
7960
+ selectedIndex: Math.max(
7961
+ 0,
7962
+ modeItems.findIndex((item) => item.value === selectedMode)
7963
+ ),
7964
+ onSubmit: (item) => {
7965
+ setSelectedMode(item.value);
7966
+ if (item.value === "ask") {
7967
+ setSelectedAgentProfileId("");
7968
+ }
7969
+ setNotice(
7970
+ item.value === "ask" ? "Mode selected: Ask. Agent Profile cleared." : `Mode selected: ${item.label}`
7971
+ );
7972
+ setActiveSelector(null);
7973
+ },
7974
+ onCancel: () => setActiveSelector(null),
7975
+ limit: tuiLayout.selectorLimit
7976
+ }
7977
+ ) : null,
7978
+ activeSelector === "profile" ? /* @__PURE__ */ jsx10(
7979
+ SelectPanel,
7980
+ {
7981
+ title: "Select Agent Profile",
7982
+ items: agentProfileItems,
7983
+ selectedIndex: Math.max(
7984
+ 0,
7985
+ agentProfileItems.findIndex((item) => item.value === selectedAgentProfileId)
7986
+ ),
7987
+ onSubmit: (item) => {
7988
+ selectAgentProfileById(item.value, item.label);
7989
+ setActiveSelector(null);
7990
+ },
7991
+ onCancel: () => setActiveSelector(null),
7992
+ limit: tuiLayout.selectorLimit
7993
+ }
7994
+ ) : null
7995
+ ] })
7996
+ }
7997
+ )
6888
7998
  }
6889
7999
  ),
6890
- threadPanel
6891
- ] }) : threadPanel,
6892
- chatState.status === "hitl_waiting" && chatState.hitl?.waiting ? /* @__PURE__ */ jsx10(
6893
- HitlPanel,
6894
- {
6895
- hitl: chatState.hitl,
6896
- questionIndex: hitlQuestionIndex,
6897
- optionIndex: hitlOptionIndex,
6898
- answers: hitlAnswers,
6899
- frontendUrl: frontendThreadUrl
6900
- }
6901
- ) : null,
6902
- activeSelector === "project" ? /* @__PURE__ */ jsx10(
6903
- SelectPanel,
6904
- {
6905
- title: "Select Project",
6906
- items: (projects.length ? projects : [defaultProject]).map((project) => ({
6907
- label: `${project.name} (${project.cloud_provider ?? "cloud"})`,
6908
- value: project,
6909
- description: project.id
6910
- })),
6911
- selectedIndex: Math.max(
6912
- 0,
6913
- (projects.length ? projects : [defaultProject]).findIndex(
6914
- (project) => project.id === (selectedProject ?? defaultProject).id
6915
- )
6916
- ),
6917
- onSubmit: (item) => {
6918
- setSelectedProject(item.value);
6919
- setActiveSelector(null);
6920
- },
6921
- onCancel: () => setActiveSelector(null),
6922
- limit: tuiLayout.selectorLimit
6923
- }
6924
- ) : null,
6925
- activeSelector === "model" ? /* @__PURE__ */ jsx10(
6926
- SelectPanel,
6927
- {
6928
- title: "Select Model",
6929
- items: modelItems,
6930
- selectedIndex: Math.max(
6931
- 0,
6932
- modelItems.findIndex((item) => item.value === selectedModel)
6933
- ),
6934
- onSubmit: (item) => {
6935
- setSelectedModel(item.value);
6936
- setActiveSelector(null);
6937
- },
6938
- onCancel: () => setActiveSelector(null),
6939
- limit: tuiLayout.selectorLimit
6940
- }
6941
- ) : null,
6942
- activeSelector === "mode" ? /* @__PURE__ */ jsx10(
6943
- SelectPanel,
6944
- {
6945
- title: "Select Mode",
6946
- items: modeItems,
6947
- selectedIndex: Math.max(
6948
- 0,
6949
- modeItems.findIndex((item) => item.value === selectedMode)
6950
- ),
6951
- onSubmit: (item) => {
6952
- setSelectedMode(item.value);
6953
- setActiveSelector(null);
6954
- },
6955
- onCancel: () => setActiveSelector(null),
6956
- limit: tuiLayout.selectorLimit
6957
- }
6958
- ) : null,
8000
+ chatMiddleOverflowing ? /* @__PURE__ */ jsx10(
8001
+ Scrollbar2,
8002
+ {
8003
+ scrollOffset: chatMiddleScrollOffset,
8004
+ contentHeight: chatMiddleContentHeight,
8005
+ viewportHeight: chatMiddleViewportHeight
8006
+ }
8007
+ ) : null
8008
+ ] }),
6959
8009
  isSearching ? /* @__PURE__ */ jsx10(
6960
- TitledBox,
8010
+ InputBox,
6961
8011
  {
6962
8012
  title: "Search History",
6963
- borderStyle: "round",
6964
- borderColor: terminalTheme.warning,
6965
- padding: 1,
6966
- children: /* @__PURE__ */ jsx10(
6967
- InputBox,
6968
- {
6969
- value: searchQuery,
6970
- onChange: setSearchQuery,
6971
- onSubmit: () => {
6972
- },
6973
- placeholder: "Type to filter history..."
6974
- }
6975
- )
8013
+ value: searchQuery,
8014
+ onChange: setSearchQuery,
8015
+ onSubmit: () => {
8016
+ },
8017
+ placeholder: "Type to filter history...",
8018
+ onBlurRequest: () => {
8019
+ setIsSearching(false);
8020
+ setSearchQuery("");
8021
+ }
6976
8022
  }
6977
8023
  ) : activeSelector ? null : /* @__PURE__ */ jsx10(
6978
8024
  InputBox,
@@ -6982,9 +8028,15 @@ var App = ({
6982
8028
  onSubmit: handlePromptSubmit,
6983
8029
  ghostText: promptGhostSuffix,
6984
8030
  disabled: Boolean(activeSelector),
8031
+ inputActive: promptInputActive,
8032
+ onBlurRequest: () => {
8033
+ setPromptInputActive(false);
8034
+ completionCycleRef.current = void 0;
8035
+ },
6985
8036
  onTabShortcut: (tab) => {
6986
8037
  setActiveWorkspaceTab(tab);
6987
8038
  setActiveSelector(null);
8039
+ setStarterSelectionsOpen(false);
6988
8040
  setNotice(void 0);
6989
8041
  setInput("");
6990
8042
  },
@@ -6996,14 +8048,21 @@ var App = ({
6996
8048
  scrollOffset: promptInputViewport.startRow,
6997
8049
  minInputRows: promptInputRowBudget,
6998
8050
  maxInputRows: promptInputRowBudget,
8051
+ blinkCursor: shouldBlinkPromptCursor({
8052
+ animationsEnabled,
8053
+ inputActive: promptInputActive,
8054
+ busy: chatBusy,
8055
+ selectorOpen: Boolean(activeSelector),
8056
+ searching: isSearching
8057
+ }),
6999
8058
  footerControls: promptFooterControls,
7000
8059
  helpText: "",
7001
- actionLabel: promptActionIsCancel ? "ESC to cancel" : "ENTER to send",
8060
+ actionLabel: promptActionIsCancel ? "ESC to cancel" : void 0,
7002
8061
  actionHint: getChatInputHelpText({
7003
8062
  isCancelling: promptActionIsCancel,
8063
+ inputActive: promptInputActive,
7004
8064
  promptCount: visiblePromptSuggestions.length
7005
8065
  }),
7006
- actionTone: promptActionIsCancel ? terminalTheme.warning : terminalTheme.brand,
7007
8066
  onAction: () => {
7008
8067
  if (promptActionIsCancel) {
7009
8068
  stopActiveChat();
@@ -7011,7 +8070,6 @@ var App = ({
7011
8070
  }
7012
8071
  handlePromptSubmit(input);
7013
8072
  },
7014
- actionDisabled: !promptActionIsCancel && !input.trim(),
7015
8073
  placeholder: chatState.status === "hitl_waiting" ? "Answer HITL prompt, or /open for frontend..." : isBusyStatus(chatState.status) ? "Response in progress. Enter queues next message..." : "Ask Cloudeval..."
7016
8074
  }
7017
8075
  ),