@nomad-e/bluma-cli 0.1.30 → 0.1.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.js CHANGED
@@ -328,33 +328,71 @@ var init_async_command = __esm({
328
328
  });
329
329
 
330
330
  // src/main.ts
331
- import React11 from "react";
331
+ import React12 from "react";
332
332
  import { render } from "ink";
333
- import { EventEmitter as EventEmitter2 } from "events";
333
+ import { EventEmitter as EventEmitter3 } from "events";
334
334
  import fs15 from "fs";
335
335
  import { v4 as uuidv46 } from "uuid";
336
336
 
337
337
  // src/app/ui/App.tsx
338
- import { useState as useState7, useEffect as useEffect6, useRef as useRef5, useCallback as useCallback2, memo as memo11 } from "react";
339
- import { Box as Box17, Text as Text16, Static } from "ink";
338
+ import { useState as useState6, useEffect as useEffect7, useRef as useRef5, useCallback as useCallback2, memo as memo12 } from "react";
339
+ import { Box as Box20, Text as Text19, Static } from "ink";
340
340
 
341
341
  // src/app/ui/layout.tsx
342
342
  import { Box, Text } from "ink";
343
343
  import { memo } from "react";
344
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
344
+
345
+ // src/app/ui/theme/blumaTerminal.ts
346
+ var BLUMA_TERMINAL = {
347
+ brandBlue: "blue",
348
+ brandMagenta: "magenta",
349
+ text: "white",
350
+ muted: "gray",
351
+ dim: "gray",
352
+ code: "gray",
353
+ codeLabel: "blue",
354
+ link: "blue",
355
+ linkUnderline: true,
356
+ success: "green",
357
+ warn: "yellow",
358
+ err: "red",
359
+ panelBorder: "blue",
360
+ toolLabel: "magenta",
361
+ toolMeta: "gray",
362
+ heading1: "blue",
363
+ heading2: "blue",
364
+ headingDeep: "magenta",
365
+ listBullet: "magenta",
366
+ listBulletSub: "blue",
367
+ rule: "gray",
368
+ /** M3 CLI — superfícies e hierarquia */
369
+ m3Outline: "blue",
370
+ m3TonalOutline: "magenta",
371
+ m3Rail: "blue",
372
+ m3Label: "gray",
373
+ m3OnSurface: "white"
374
+ };
375
+
376
+ // src/app/ui/layout.tsx
377
+ import { jsx, jsxs } from "react/jsx-runtime";
345
378
  var HeaderComponent = ({
346
379
  sessionId,
347
380
  workdir
348
381
  }) => {
349
382
  const dirName = workdir.split("/").pop() || workdir;
350
383
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
351
- /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Text, { color: "magenta", bold: true, children: "bluma \u2014 coding agent" }) }),
352
- /* @__PURE__ */ jsxs(Box, { children: [
384
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "row", flexWrap: "wrap", children: [
385
+ /* @__PURE__ */ jsx(Text, { color: BLUMA_TERMINAL.brandBlue, bold: true, children: "BluMa" }),
386
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \u2014 " }),
387
+ /* @__PURE__ */ jsx(Text, { children: "Base Language Unit" }),
388
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \xB7 " }),
389
+ /* @__PURE__ */ jsx(Text, { color: BLUMA_TERMINAL.brandMagenta, bold: true, children: "Model Agent" })
390
+ ] }),
391
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "row", flexWrap: "wrap", children: [
353
392
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "cwd " }),
354
- /* @__PURE__ */ jsx(Text, { color: "cyan", children: dirName }),
355
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: " | " }),
356
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "session " }),
357
- /* @__PURE__ */ jsx(Text, { color: "gray", children: sessionId.slice(0, 8) })
393
+ /* @__PURE__ */ jsx(Text, { color: BLUMA_TERMINAL.brandBlue, children: dirName }),
394
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \u2502 session " }),
395
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: sessionId.slice(0, 8) })
358
396
  ] })
359
397
  ] });
360
398
  };
@@ -366,12 +404,12 @@ var SessionInfoComponent = ({
366
404
  mcpStatus
367
405
  }) => /* @__PURE__ */ jsxs(Box, { paddingX: 1, children: [
368
406
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "mcp " }),
369
- /* @__PURE__ */ jsx(Text, { color: mcpStatus === "connected" ? "green" : "yellow", children: mcpStatus === "connected" ? "+" : "-" }),
370
- toolsCount !== null && /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
407
+ /* @__PURE__ */ jsx(Text, { color: mcpStatus === "connected" ? BLUMA_TERMINAL.success : BLUMA_TERMINAL.warn, children: mcpStatus === "connected" ? "on" : "\u2026" }),
408
+ toolsCount !== null ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
371
409
  " ",
372
410
  toolsCount,
373
411
  " tools"
374
- ] }) })
412
+ ] }) : null
375
413
  ] });
376
414
  var SessionInfo = memo(SessionInfoComponent);
377
415
  var TaskStatusBarComponent = ({
@@ -380,9 +418,9 @@ var TaskStatusBarComponent = ({
380
418
  status
381
419
  }) => {
382
420
  const modeColors = {
383
- PLANNING: "blue",
384
- EXECUTION: "green",
385
- VERIFICATION: "yellow"
421
+ PLANNING: BLUMA_TERMINAL.brandBlue,
422
+ EXECUTION: BLUMA_TERMINAL.brandBlue,
423
+ VERIFICATION: BLUMA_TERMINAL.brandMagenta
386
424
  };
387
425
  return /* @__PURE__ */ jsxs(Box, { paddingX: 1, children: [
388
426
  /* @__PURE__ */ jsxs(Text, { color: modeColors[mode], bold: true, children: [
@@ -394,10 +432,10 @@ var TaskStatusBarComponent = ({
394
432
  " ",
395
433
  taskName
396
434
  ] }),
397
- status && /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
398
- " - ",
435
+ status ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
436
+ " \u2014 ",
399
437
  status
400
- ] })
438
+ ] }) : null
401
439
  ] });
402
440
  };
403
441
  var TaskStatusBar = memo(TaskStatusBarComponent);
@@ -408,6 +446,12 @@ import { Box as Box2, Text as Text2, useStdout, useInput as useInput2 } from "in
408
446
  // src/app/ui/utils/useSimpleInputBuffer.ts
409
447
  import { useReducer, useRef, useCallback, useEffect } from "react";
410
448
  import { useInput } from "ink";
449
+
450
+ // src/app/ui/utils/expandPreviewHotkey.ts
451
+ import { EventEmitter } from "events";
452
+ var expandPreviewHotkeyBus = new EventEmitter();
453
+
454
+ // src/app/ui/utils/useSimpleInputBuffer.ts
411
455
  function getLineStart(text, cursorPos) {
412
456
  let pos = cursorPos - 1;
413
457
  while (pos >= 0 && text[pos] !== "\n") {
@@ -546,7 +590,12 @@ function inputReducer(state, action, viewWidth) {
546
590
  return state;
547
591
  }
548
592
  }
549
- var useCustomInput = ({ onSubmit, viewWidth, isReadOnly, onInterrupt }) => {
593
+ var useCustomInput = ({
594
+ onSubmit,
595
+ viewWidth,
596
+ isReadOnly,
597
+ onInterrupt
598
+ }) => {
550
599
  const [state, dispatch] = useReducer(
551
600
  (s, a) => inputReducer(s, a, viewWidth),
552
601
  { text: "", cursorPosition: 0, viewStart: 0 }
@@ -597,6 +646,10 @@ var useCustomInput = ({ onSubmit, viewWidth, isReadOnly, onInterrupt }) => {
597
646
  if (inputBuffer.current.length > 0 && (key.ctrl || key.meta || key.escape || key.return || key.leftArrow || key.rightArrow || key.upArrow || key.downArrow || key.tab || key.shift)) {
598
647
  flushInputBuffer();
599
648
  }
649
+ if (key.ctrl && input === "o") {
650
+ expandPreviewHotkeyBus.emit("expand");
651
+ return;
652
+ }
600
653
  if (key.escape) {
601
654
  onInterrupt();
602
655
  return;
@@ -685,7 +738,7 @@ var useCustomInput = ({ onSubmit, viewWidth, isReadOnly, onInterrupt }) => {
685
738
 
686
739
  // src/app/ui/components/InputPrompt.tsx
687
740
  import { useEffect as useEffect3, useMemo, useState as useState2, memo as memo2 } from "react";
688
- import { EventEmitter } from "events";
741
+ import { EventEmitter as EventEmitter2 } from "events";
689
742
 
690
743
  // src/app/ui/utils/slashRegistry.ts
691
744
  var getSlashCommands = () => [
@@ -884,9 +937,14 @@ function useAtCompletion({
884
937
  return { open, suggestions, selected, setSelected, insertAtSelection, close, update };
885
938
  }
886
939
 
940
+ // src/app/ui/constants/toolUiPreview.ts
941
+ var TOOL_PREVIEW_MAX_LINES = 8;
942
+ var EXPAND_OVERLAY_MAX_LINES = 60;
943
+ var TERMINAL_RULE_CHAR = "\u2500";
944
+
887
945
  // src/app/ui/components/InputPrompt.tsx
888
- import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
889
- var uiEventBus = global.__bluma_ui_eventbus__ || new EventEmitter();
946
+ import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
947
+ var uiEventBus = global.__bluma_ui_eventbus__ || new EventEmitter2();
890
948
  global.__bluma_ui_eventbus__ = uiEventBus;
891
949
  var TextLine = memo2(({
892
950
  line,
@@ -931,8 +989,8 @@ var PathSuggestions = memo2(({
931
989
  const realIdx = start + idx;
932
990
  const isSelected = realIdx === selected;
933
991
  return /* @__PURE__ */ jsxs2(Box2, { paddingLeft: 1, paddingY: 0, children: [
934
- /* @__PURE__ */ jsx2(Text2, { color: isSelected ? "magenta" : "gray", children: isSelected ? "\u276F " : " " }),
935
- /* @__PURE__ */ jsx2(Text2, { color: isSelected ? "magenta" : "white", bold: isSelected, dimColor: !isSelected, children: s.label })
992
+ /* @__PURE__ */ jsx2(Text2, { color: isSelected ? BLUMA_TERMINAL.brandMagenta : BLUMA_TERMINAL.muted, children: isSelected ? "> " : " " }),
993
+ /* @__PURE__ */ jsx2(Text2, { color: isSelected ? BLUMA_TERMINAL.brandBlue : void 0, bold: isSelected, dimColor: !isSelected, children: s.label })
936
994
  ] }, s.fullPath);
937
995
  }) });
938
996
  });
@@ -943,11 +1001,11 @@ var SlashSuggestions = memo2(({
943
1001
  }) => /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", marginTop: 1, children: suggestions.map((s, idx) => {
944
1002
  const isSelected = idx === selectedIndex;
945
1003
  return /* @__PURE__ */ jsxs2(Box2, { paddingLeft: 1, paddingY: 0, children: [
946
- /* @__PURE__ */ jsx2(Text2, { color: isSelected ? "magenta" : "gray", children: isSelected ? "\u276F " : " " }),
947
- /* @__PURE__ */ jsxs2(Text2, { color: isSelected ? "magenta" : "white", bold: isSelected, dimColor: !isSelected, children: [
1004
+ /* @__PURE__ */ jsx2(Text2, { color: isSelected ? BLUMA_TERMINAL.brandMagenta : BLUMA_TERMINAL.muted, children: isSelected ? "> " : " " }),
1005
+ /* @__PURE__ */ jsxs2(Text2, { color: isSelected ? BLUMA_TERMINAL.brandBlue : void 0, bold: isSelected, dimColor: !isSelected, children: [
948
1006
  s.name,
949
1007
  " ",
950
- /* @__PURE__ */ jsxs2(Text2, { color: "gray", children: [
1008
+ /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
951
1009
  "- ",
952
1010
  s.description
953
1011
  ] })
@@ -955,7 +1013,14 @@ var SlashSuggestions = memo2(({
955
1013
  ] }, s.name);
956
1014
  }) }));
957
1015
  SlashSuggestions.displayName = "SlashSuggestions";
958
- var Footer = memo2(({ isReadOnly }) => /* @__PURE__ */ jsx2(Box2, { paddingX: 1, justifyContent: "center", children: /* @__PURE__ */ jsx2(Text2, { color: "gray", dimColor: true, children: isReadOnly ? "ctrl+c to exit | Enter to send message | Shift+Enter for new line | esc interrupt" : "ctrl+c to exit | Enter to submit | Shift+Enter for new line | /help commands | esc interrupt" }) }));
1016
+ var InputRuleLine = memo2(() => {
1017
+ const { stdout } = useStdout();
1018
+ const cols = stdout?.columns ?? 80;
1019
+ const n = Math.max(8, cols);
1020
+ return /* @__PURE__ */ jsx2(Text2, { color: "white", children: TERMINAL_RULE_CHAR.repeat(n) });
1021
+ });
1022
+ InputRuleLine.displayName = "InputRuleLine";
1023
+ var Footer = memo2(({ isReadOnly }) => /* @__PURE__ */ jsx2(Box2, { justifyContent: "center", children: /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: isReadOnly ? "ctrl+c to exit | Enter to send message | Shift+Enter for new line | esc interrupt | Ctrl+O expand last clip" : "ctrl+c to exit | Enter to submit | Shift+Enter for new line | /help commands | esc interrupt | Ctrl+O expand last clip" }) }));
959
1024
  Footer.displayName = "Footer";
960
1025
  var TextLinesRenderer = memo2(({
961
1026
  lines,
@@ -965,17 +1030,14 @@ var TextLinesRenderer = memo2(({
965
1030
  showPlaceholder,
966
1031
  placeholder
967
1032
  }) => {
968
- return /* @__PURE__ */ jsx2(Fragment2, { children: lines.map((line, idx) => {
1033
+ return /* @__PURE__ */ jsx2(Fragment, { children: lines.map((line, idx) => {
969
1034
  const isFirstLine = idx === 0;
970
- return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "row", paddingX: 1, children: [
971
- isFirstLine && /* @__PURE__ */ jsxs2(Text2, { color: "white", children: [
1035
+ return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "row", children: [
1036
+ isFirstLine && /* @__PURE__ */ jsxs2(Text2, { color: "white", bold: true, children: [
972
1037
  ">",
973
1038
  " "
974
1039
  ] }),
975
- !isFirstLine && /* @__PURE__ */ jsxs2(Text2, { color: "gray", children: [
976
- "\u2502",
977
- " "
978
- ] }),
1040
+ !isFirstLine && /* @__PURE__ */ jsx2(Text2, { color: BLUMA_TERMINAL.brandMagenta, children: "\u2502 " }),
979
1041
  showPlaceholder && isFirstLine && line.length === 0 ? /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: placeholder }) : /* @__PURE__ */ jsx2(
980
1042
  TextLine,
981
1043
  {
@@ -1116,14 +1178,15 @@ var InputPrompt = memo2(({
1116
1178
  return;
1117
1179
  }
1118
1180
  }, { isActive: true });
1119
- return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
1120
- disableWhileProcessing ? /* @__PURE__ */ jsx2(Box2, { children: /* @__PURE__ */ jsxs2(Box2, { flexDirection: "row", paddingX: 1, flexWrap: "nowrap", children: [
1121
- /* @__PURE__ */ jsxs2(Text2, { color: "white", children: [
1181
+ return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginTop: 1, children: [
1182
+ /* @__PURE__ */ jsx2(InputRuleLine, {}),
1183
+ /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", marginY: 0, children: disableWhileProcessing ? /* @__PURE__ */ jsxs2(Box2, { flexDirection: "row", flexWrap: "nowrap", children: [
1184
+ /* @__PURE__ */ jsxs2(Text2, { color: "white", bold: true, children: [
1122
1185
  ">",
1123
1186
  " "
1124
1187
  ] }),
1125
1188
  /* @__PURE__ */ jsx2(Text2, { dimColor: true, children: "ctrl+c to exit" })
1126
- ] }) }) : /* @__PURE__ */ jsxs2(Fragment2, { children: [
1189
+ ] }) : /* @__PURE__ */ jsxs2(Fragment, { children: [
1127
1190
  /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", children: /* @__PURE__ */ jsx2(
1128
1191
  TextLinesRenderer,
1129
1192
  {
@@ -1142,14 +1205,9 @@ var InputPrompt = memo2(({
1142
1205
  selected: pathAutocomplete.selected
1143
1206
  }
1144
1207
  ),
1145
- slashOpen && slashSuggestions.length > 0 && /* @__PURE__ */ jsx2(
1146
- SlashSuggestions,
1147
- {
1148
- suggestions: slashSuggestions,
1149
- selectedIndex: slashIndex
1150
- }
1151
- )
1152
- ] }),
1208
+ slashOpen && slashSuggestions.length > 0 && /* @__PURE__ */ jsx2(SlashSuggestions, { suggestions: slashSuggestions, selectedIndex: slashIndex })
1209
+ ] }) }),
1210
+ /* @__PURE__ */ jsx2(InputRuleLine, {}),
1153
1211
  /* @__PURE__ */ jsx2(Footer, { isReadOnly })
1154
1212
  ] });
1155
1213
  });
@@ -1157,7 +1215,7 @@ InputPrompt.displayName = "InputPrompt";
1157
1215
 
1158
1216
  // src/app/ui/ConfirmationPrompt.tsx
1159
1217
  import { memo as memo4 } from "react";
1160
- import { Box as Box6, Text as Text6 } from "ink";
1218
+ import { Box as Box7, Text as Text7 } from "ink";
1161
1219
 
1162
1220
  // src/app/ui/InteractiveMenu.tsx
1163
1221
  import { useState as useState3, memo as memo3 } from "react";
@@ -1165,9 +1223,9 @@ import { Box as Box3, Text as Text3, useInput as useInput3 } from "ink";
1165
1223
  import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
1166
1224
  var InteractiveMenuComponent = ({ onDecision }) => {
1167
1225
  const options = [
1168
- { key: "y", label: "yes", value: "accept", color: "green" },
1169
- { key: "n", label: "no", value: "decline", color: "red" },
1170
- { key: "a", label: "always", value: "accept_always", color: "yellow" }
1226
+ { key: "y", label: "yes", value: "accept" },
1227
+ { key: "n", label: "no", value: "decline" },
1228
+ { key: "a", label: "always", value: "accept_always" }
1171
1229
  ];
1172
1230
  const [selected, setSelected] = useState3(0);
1173
1231
  useInput3((input, key) => {
@@ -1188,12 +1246,12 @@ var InteractiveMenuComponent = ({ onDecision }) => {
1188
1246
  onDecision(opt.value);
1189
1247
  }
1190
1248
  });
1191
- return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", paddingX: 1, marginTop: 1, children: [
1192
- /* @__PURE__ */ jsxs3(Box3, { children: [
1193
- /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "approve? " }),
1249
+ return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", marginTop: 1, children: [
1250
+ /* @__PURE__ */ jsxs3(Box3, { flexDirection: "row", flexWrap: "wrap", children: [
1251
+ /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "approve \xB7 " }),
1194
1252
  options.map((opt, idx) => {
1195
1253
  const isSelected = idx === selected;
1196
- return /* @__PURE__ */ jsx3(Box3, { marginRight: 1, children: /* @__PURE__ */ jsxs3(Text3, { color: isSelected ? opt.color : "gray", bold: isSelected, children: [
1254
+ return /* @__PURE__ */ jsx3(Box3, { marginRight: 1, children: /* @__PURE__ */ jsxs3(Text3, { color: isSelected ? BLUMA_TERMINAL.brandMagenta : BLUMA_TERMINAL.muted, bold: isSelected, children: [
1197
1255
  "[",
1198
1256
  opt.key,
1199
1257
  "]",
@@ -1201,7 +1259,7 @@ var InteractiveMenuComponent = ({ onDecision }) => {
1201
1259
  ] }) }, opt.value);
1202
1260
  })
1203
1261
  ] }),
1204
- /* @__PURE__ */ jsx3(Box3, { children: /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "arrows to select, enter to confirm, esc to cancel" }) })
1262
+ /* @__PURE__ */ jsx3(Box3, { marginTop: 0, children: /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "arrows \xB7 enter \xB7 esc cancel" }) })
1205
1263
  ] });
1206
1264
  };
1207
1265
  var InteractiveMenu = memo3(InteractiveMenuComponent);
@@ -1354,7 +1412,7 @@ var renderEditTool = ({ toolCall, preview }) => {
1354
1412
  var renderGeneric = ({ toolCall }) => {
1355
1413
  const toolName = toolCall.function.name;
1356
1414
  const rawArguments = toolCall.function.arguments;
1357
- const MAX_LINES2 = 5;
1415
+ const MAX_LINES = 5;
1358
1416
  let formattedArgsString;
1359
1417
  if (!rawArguments) {
1360
1418
  formattedArgsString = "";
@@ -1369,9 +1427,9 @@ var renderGeneric = ({ toolCall }) => {
1369
1427
  formattedArgsString = JSON.stringify(rawArguments, null, 2);
1370
1428
  }
1371
1429
  const lines = formattedArgsString.split("\n");
1372
- const isTruncated = lines.length > MAX_LINES2;
1373
- const visibleLines = isTruncated ? lines.slice(0, MAX_LINES2) : lines;
1374
- const remainingCount = lines.length - MAX_LINES2;
1430
+ const isTruncated = lines.length > MAX_LINES;
1431
+ const visibleLines = isTruncated ? lines.slice(0, MAX_LINES) : lines;
1432
+ const remainingCount = lines.length - MAX_LINES;
1375
1433
  return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
1376
1434
  /* @__PURE__ */ jsxs5(Box5, { children: [
1377
1435
  /* @__PURE__ */ jsx5(Text5, { color: "blue", children: "\u25B8" }),
@@ -1411,12 +1469,12 @@ var renderTodoTool = ({ toolCall }) => {
1411
1469
  /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: " todo" })
1412
1470
  ] }),
1413
1471
  /* @__PURE__ */ jsxs5(Box5, { paddingLeft: 2, flexDirection: "column", children: [
1414
- /* @__PURE__ */ jsxs5(Text5, { color: "magenta", children: [
1415
- "\u{1F4CB} ",
1472
+ /* @__PURE__ */ jsxs5(Text5, { color: "blue", children: [
1473
+ "todo ",
1416
1474
  pending,
1417
- " pending, ",
1475
+ " open \xB7 ",
1418
1476
  completed,
1419
- " completed"
1477
+ " done"
1420
1478
  ] }),
1421
1479
  tasks.length > 0 && tasks.length <= 10 && /* @__PURE__ */ jsx5(Box5, { paddingLeft: 2, flexDirection: "column", marginTop: 1, children: tasks.map((task, idx) => {
1422
1480
  const isComplete = task.isComplete === true;
@@ -1466,19 +1524,60 @@ var promptRenderers = {
1466
1524
  // <--- ADICIONE ESTA LINHA
1467
1525
  };
1468
1526
 
1469
- // src/app/ui/ConfirmationPrompt.tsx
1527
+ // src/app/ui/theme/m3Layout.tsx
1528
+ import { Box as Box6, Text as Text6 } from "ink";
1470
1529
  import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1530
+ function ChatBlock({
1531
+ children,
1532
+ marginBottom = 1
1533
+ }) {
1534
+ return /* @__PURE__ */ jsx6(Box6, { flexDirection: "column", marginBottom, children });
1535
+ }
1536
+ function ChatUserMessage({ children }) {
1537
+ return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "row", marginBottom: 1, flexWrap: "wrap", alignItems: "flex-start", children: [
1538
+ /* @__PURE__ */ jsxs6(Text6, { color: "white", bold: true, children: [
1539
+ ">",
1540
+ " "
1541
+ ] }),
1542
+ /* @__PURE__ */ jsx6(Box6, { flexDirection: "column", flexGrow: 1, children })
1543
+ ] });
1544
+ }
1545
+ function ChatMeta({ children }) {
1546
+ return /* @__PURE__ */ jsx6(Box6, { marginBottom: 1, children: /* @__PURE__ */ jsx6(Text6, { dimColor: true, children }) });
1547
+ }
1548
+ function ChatStatusRow({ children }) {
1549
+ return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "row", marginBottom: 1, flexWrap: "wrap", children: [
1550
+ /* @__PURE__ */ jsxs6(Text6, { color: BLUMA_TERMINAL.brandMagenta, bold: true, children: [
1551
+ "*",
1552
+ " "
1553
+ ] }),
1554
+ children
1555
+ ] });
1556
+ }
1557
+ function ChatTurnDuration({ secondsFormatted }) {
1558
+ return /* @__PURE__ */ jsx6(Box6, { marginBottom: 1, children: /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
1559
+ /* @__PURE__ */ jsx6(Text6, { color: BLUMA_TERMINAL.brandBlue, children: "\xB7 " }),
1560
+ /* @__PURE__ */ jsxs6(Text6, { color: BLUMA_TERMINAL.brandMagenta, children: [
1561
+ secondsFormatted,
1562
+ "s"
1563
+ ] })
1564
+ ] }) });
1565
+ }
1566
+ var M3StatusStrip = ChatStatusRow;
1567
+
1568
+ // src/app/ui/ConfirmationPrompt.tsx
1569
+ import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
1471
1570
  var ConfirmationPromptComponent = ({ toolCalls, preview, onDecision }) => {
1472
- const toolCall = toolCalls && toolCalls.length > 0 ? toolCalls[0] : null;
1473
- if (!toolCall) {
1474
- return /* @__PURE__ */ jsx6(Box6, { paddingX: 1, children: /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "waiting..." }) });
1571
+ const toolCall = toolCalls && Array.isArray(toolCalls) && toolCalls.length > 0 ? toolCalls[0] : null;
1572
+ if (!toolCall?.function) {
1573
+ return /* @__PURE__ */ jsx7(Box7, { paddingX: 1, children: /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "waiting\u2026" }) });
1475
1574
  }
1476
- const toolName = toolCall.function.name;
1575
+ const toolName = toolCall.function.name || "tool";
1477
1576
  const renderFunction = promptRenderers[toolName] || renderGeneric;
1478
- return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", paddingX: 1, children: [
1577
+ return /* @__PURE__ */ jsx7(ChatBlock, { marginBottom: 1, children: /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", paddingLeft: 1, children: [
1479
1578
  renderFunction({ toolCall, preview }),
1480
- /* @__PURE__ */ jsx6(InteractiveMenu, { onDecision })
1481
- ] });
1579
+ /* @__PURE__ */ jsx7(InteractiveMenu, { onDecision })
1580
+ ] }) });
1482
1581
  };
1483
1582
  var ConfirmationPrompt = memo4(ConfirmationPromptComponent);
1484
1583
 
@@ -3605,14 +3704,84 @@ function searchNotes(args) {
3605
3704
  matched: matches
3606
3705
  };
3607
3706
  }
3608
- function clearNotes() {
3707
+ function removeNote(args) {
3609
3708
  loadMemoryFromFile();
3610
- memoryStore = [];
3611
- nextId2 = 1;
3709
+ const id = args.id;
3710
+ if (id == null || typeof id !== "number" || !Number.isFinite(id)) {
3711
+ return {
3712
+ success: false,
3713
+ message: "id is required for action=remove (use list or search to get ids)",
3714
+ entries: memoryStore
3715
+ };
3716
+ }
3717
+ const idx = memoryStore.findIndex((e) => e.id === id);
3718
+ if (idx === -1) {
3719
+ return {
3720
+ success: false,
3721
+ message: `No coding memory entry with id=${id}`,
3722
+ entries: memoryStore
3723
+ };
3724
+ }
3725
+ memoryStore.splice(idx, 1);
3612
3726
  saveMemoryToFile();
3613
3727
  return {
3614
3728
  success: true,
3615
- message: "All coding memory entries cleared",
3729
+ message: `Removed coding memory entry id=${id}`,
3730
+ entries: memoryStore
3731
+ };
3732
+ }
3733
+ function updateNote(args) {
3734
+ loadMemoryFromFile();
3735
+ const id = args.id;
3736
+ if (id == null || typeof id !== "number" || !Number.isFinite(id)) {
3737
+ return {
3738
+ success: false,
3739
+ message: "id is required for action=update",
3740
+ entries: memoryStore
3741
+ };
3742
+ }
3743
+ const entry = memoryStore.find((e) => e.id === id);
3744
+ if (!entry) {
3745
+ return {
3746
+ success: false,
3747
+ message: `No coding memory entry with id=${id}`,
3748
+ entries: memoryStore
3749
+ };
3750
+ }
3751
+ const hasNote = typeof args.note === "string";
3752
+ const hasTags = args.tags !== void 0;
3753
+ if (!hasNote && !hasTags) {
3754
+ return {
3755
+ success: false,
3756
+ message: "update requires `note` and/or `tags` (omit fields you do not want to change)",
3757
+ entries: memoryStore
3758
+ };
3759
+ }
3760
+ if (hasNote) {
3761
+ const newNote = args.note.trim();
3762
+ if (!newNote) {
3763
+ return {
3764
+ success: false,
3765
+ message: "note cannot be empty; omit `note` to keep the existing text",
3766
+ entries: memoryStore
3767
+ };
3768
+ }
3769
+ if (newNote.length > 4e3) {
3770
+ return {
3771
+ success: false,
3772
+ message: "note too long (max 4000 chars)",
3773
+ entries: memoryStore
3774
+ };
3775
+ }
3776
+ entry.note = newNote;
3777
+ }
3778
+ if (hasTags) {
3779
+ entry.tags = normalizeTags(args.tags);
3780
+ }
3781
+ saveMemoryToFile();
3782
+ return {
3783
+ success: true,
3784
+ message: `Updated coding memory id=${id}`,
3616
3785
  entries: memoryStore
3617
3786
  };
3618
3787
  }
@@ -3625,8 +3794,10 @@ async function coding_memory(args) {
3625
3794
  return listNotes();
3626
3795
  case "search":
3627
3796
  return searchNotes(args);
3628
- case "clear":
3629
- return clearNotes();
3797
+ case "remove":
3798
+ return removeNote(args);
3799
+ case "update":
3800
+ return updateNote(args);
3630
3801
  default:
3631
3802
  return {
3632
3803
  success: false,
@@ -3939,20 +4110,21 @@ async function withFileLock(file, fn) {
3939
4110
  }
3940
4111
  var pendingSaves = /* @__PURE__ */ new Map();
3941
4112
  var DEBOUNCE_DELAY_MS = 100;
3942
- function debouncedSave(sessionFile, history) {
4113
+ function debouncedSave(sessionFile, history, memory) {
3943
4114
  const existing = pendingSaves.get(sessionFile);
3944
4115
  if (existing) {
3945
4116
  clearTimeout(existing.timer);
3946
4117
  }
4118
+ const resolvedMemory = memory !== void 0 ? memory : existing?.memory;
3947
4119
  const timer = setTimeout(async () => {
3948
4120
  pendingSaves.delete(sessionFile);
3949
4121
  try {
3950
- await doSaveSessionHistory(sessionFile, history);
4122
+ await doSaveSessionHistory(sessionFile, history, resolvedMemory);
3951
4123
  } catch (e) {
3952
4124
  console.warn(`Debounced save failed for ${sessionFile}: ${e.message}`);
3953
4125
  }
3954
4126
  }, DEBOUNCE_DELAY_MS);
3955
- pendingSaves.set(sessionFile, { history: [...history], timer });
4127
+ pendingSaves.set(sessionFile, { history: [...history], memory: resolvedMemory, timer });
3956
4128
  }
3957
4129
  function expandHome(p) {
3958
4130
  if (!p) return p;
@@ -4017,7 +4189,11 @@ async function loadOrcreateSession(sessionId) {
4017
4189
  await fs11.access(sessionFile);
4018
4190
  const fileContent = await fs11.readFile(sessionFile, "utf-8");
4019
4191
  const sessionData = JSON.parse(fileContent);
4020
- return [sessionFile, sessionData.conversation_history || [], []];
4192
+ const memory = {
4193
+ historyAnchor: sessionData.history_anchor ?? null,
4194
+ compressedTurnSliceCount: sessionData.compressed_turn_slice_count ?? 0
4195
+ };
4196
+ return [sessionFile, sessionData.conversation_history || [], memory];
4021
4197
  } catch (error) {
4022
4198
  const newSessionData = {
4023
4199
  session_id: sessionId,
@@ -4025,10 +4201,14 @@ async function loadOrcreateSession(sessionId) {
4025
4201
  conversation_history: []
4026
4202
  };
4027
4203
  await fs11.writeFile(sessionFile, JSON.stringify(newSessionData, null, 2), "utf-8");
4028
- return [sessionFile, [], []];
4204
+ const emptyMemory = {
4205
+ historyAnchor: null,
4206
+ compressedTurnSliceCount: 0
4207
+ };
4208
+ return [sessionFile, [], emptyMemory];
4029
4209
  }
4030
4210
  }
4031
- async function doSaveSessionHistory(sessionFile, history) {
4211
+ async function doSaveSessionHistory(sessionFile, history, memory) {
4032
4212
  await withFileLock(sessionFile, async () => {
4033
4213
  let sessionData;
4034
4214
  try {
@@ -4061,6 +4241,14 @@ async function doSaveSessionHistory(sessionFile, history) {
4061
4241
  }
4062
4242
  sessionData.conversation_history = history;
4063
4243
  sessionData.last_updated = (/* @__PURE__ */ new Date()).toISOString();
4244
+ if (memory) {
4245
+ if (memory.historyAnchor) {
4246
+ sessionData.history_anchor = memory.historyAnchor;
4247
+ } else {
4248
+ delete sessionData.history_anchor;
4249
+ }
4250
+ sessionData.compressed_turn_slice_count = memory.compressedTurnSliceCount;
4251
+ }
4064
4252
  const tempSessionFile = `${sessionFile}.${Date.now()}.tmp`;
4065
4253
  try {
4066
4254
  await fs11.writeFile(tempSessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
@@ -4078,14 +4266,14 @@ async function doSaveSessionHistory(sessionFile, history) {
4078
4266
  }
4079
4267
  });
4080
4268
  }
4081
- async function saveSessionHistory(sessionFile, history) {
4269
+ async function saveSessionHistory(sessionFile, history, memory) {
4082
4270
  const cleanHistory = history.filter((msg) => {
4083
4271
  if (msg.role === "user" && typeof msg.content === "string") {
4084
4272
  return !msg.content.startsWith("[SKILL:");
4085
4273
  }
4086
4274
  return true;
4087
4275
  });
4088
- debouncedSave(sessionFile, cleanHistory);
4276
+ debouncedSave(sessionFile, cleanHistory, memory);
4089
4277
  }
4090
4278
 
4091
4279
  // src/app/agent/core/prompt/prompt_builder.ts
@@ -4098,7 +4286,6 @@ import { execSync } from "child_process";
4098
4286
  import fs12 from "fs";
4099
4287
  import path14 from "path";
4100
4288
  import os7 from "os";
4101
- import { fileURLToPath as fileURLToPath3 } from "url";
4102
4289
  var SkillLoader = class _SkillLoader {
4103
4290
  bundledSkillsDir;
4104
4291
  projectSkillsDir;
@@ -4115,19 +4302,26 @@ var SkillLoader = class _SkillLoader {
4115
4302
  * Funciona tanto em ESM como quando executado a partir de dist/.
4116
4303
  */
4117
4304
  static resolveBundledDir() {
4118
- if (process.env.NODE_ENV === "test" || process.env.JEST_WORKER_ID !== void 0) {
4119
- if (typeof __dirname !== "undefined") {
4120
- return path14.join(__dirname, "config", "skills");
4121
- }
4305
+ if (process.env.JEST_WORKER_ID !== void 0 || process.env.NODE_ENV === "test") {
4122
4306
  return path14.join(process.cwd(), "dist", "config", "skills");
4123
4307
  }
4124
- try {
4125
- const currentFile = fileURLToPath3(import.meta.url);
4126
- const distDir = path14.dirname(currentFile);
4127
- return path14.join(distDir, "config", "skills");
4128
- } catch {
4129
- return path14.join(process.cwd(), "dist", "config", "skills");
4308
+ const candidates = [
4309
+ path14.join(process.cwd(), "dist", "config", "skills"),
4310
+ path14.join(process.cwd(), "node_modules", "@nomad-e", "bluma-cli", "dist", "config", "skills")
4311
+ ];
4312
+ if (typeof __dirname !== "undefined") {
4313
+ candidates.push(
4314
+ path14.join(__dirname, "config", "skills"),
4315
+ path14.join(__dirname, "..", "..", "..", "config", "skills")
4316
+ );
4130
4317
  }
4318
+ for (const c of candidates) {
4319
+ const abs = path14.resolve(c);
4320
+ if (fs12.existsSync(abs)) {
4321
+ return abs;
4322
+ }
4323
+ }
4324
+ return path14.join(process.cwd(), "dist", "config", "skills");
4131
4325
  }
4132
4326
  /**
4133
4327
  * Lista skills disponíveis de todas as fontes.
@@ -4553,19 +4747,35 @@ You MUST adapt all commands to this environment. Use the correct package manager
4553
4747
  <coding_memory>
4554
4748
  ## Persistent coding memory (tool: \`coding_memory\`)
4555
4749
 
4556
- You have **durable notes** on disk (typically \`~/.bluma/coding_memory.json\`), separate from chat history. They **survive new sessions**.
4750
+ This is your **long-term scratchpad** (usually \`~/.bluma/coding_memory.json\`). It is **not** the chat log: it persists across sessions so you can behave like a coding agent that **actually recalls** stable project facts\u2014**but only if you use the tool**.
4751
+
4752
+ ### Ground rules
4753
+ - **Never invent** what is stored: if you are not sure, run \`action: "search"\` (or \`list\`) before stating a remembered fact.
4754
+ - The **<coding_memory_snapshot>** below is a **one-time snapshot at session start**. After any \`add\`, \`remove\`, or \`update\` in this session, treat the snapshot as **possibly stale** and **search** or **list** before relying on it.
4755
+ - Do **not** ask the user for permission to search memory when it reduces risk (e.g. they mention a prior decision, branch name, or "as we agreed").
4756
+ - **There is no "clear all"**: you can only **remove** entries **one id at a time** (\`action: "remove"\`, \`id\`). To fix bad data, \`update\` or \`remove\` specific ids.
4557
4757
 
4558
- ### Baseline vs live refresh
4559
- - The **<coding_memory_snapshot>** block (appended after this prompt) is loaded **once at session start** from disk. It can become stale if memory changes later or if the topic was never saved.
4560
- - Act like a teammate who **checks their notes**: before relying on "what we agreed" or project-specific lore, use \`coding_memory\` with \`action: "search"\` and a short \`query\` \u2014 especially when the user asks if you remember something, or when switching back to a topic after a long stretch of work.
4758
+ ### When to search first
4759
+ - User: "lembra-te / remember / last time / o que combin\xE1mos / qual era o comando".
4760
+ - You are about to edit, run tests, or refactor using **assumptions** that came from earlier in this project (stack, scripts, env vars, URLs).
4761
+ - You return to a task after many unrelated turns\u2014the chat may have been compressed; **memory on disk still holds** the durable notes if you saved them.
4561
4762
 
4562
- ### When to write
4563
- - After you learn **stable** facts (architecture, conventions, important URLs, decisions, repeated user preferences), call \`coding_memory\` with \`action: "add"\`, a concise \`note\`, and \`tags\` for later search.
4564
- - When something **changes**, add an updated note (and do not treat outdated snapshot text as truth without searching).
4763
+ ### When to add
4764
+ - **Stable** facts: architecture, naming conventions, test commands, ports, API bases, repo layout, user preferences that repeat.
4765
+ - **Pointers**, not novels: one short \`note\` + \`tags\` (e.g. \`auth\`, \`deploy\`, \`bluma\`). Put long specs in the repo; here only **reminders**.
4766
+
4767
+ ### CRUD (tool actions)
4768
+ - **Create:** \`add\` + \`note\` (+ optional \`tags\`).
4769
+ - **Read:** \`list\` (all ids) or \`search\` + \`query\`.
4770
+ - **Update:** \`update\` + \`id\` + new \`note\` and/or \`tags\` (omit fields you leave unchanged).
4771
+ - **Delete:** \`remove\` + \`id\` only \u2014 **never** bulk-wipe; the product intentionally forbids it.
4772
+
4773
+ ### When something changes
4774
+ - Prefer \`update\` on the same \`id\` when correcting a note; add a **new** note if the old one should remain as history; \`remove\` obsolete ids.
4565
4775
 
4566
4776
  ### Habits (continuity)
4567
- - Prefer **search** or **list** over guessing when answering "remember / last time / what we did".
4568
- - Long specs belong in the repo or artifacts; use \`coding_memory\` for **short** reminders and pointers.
4777
+ - Prefer **search** or **list** over guessing whenever "memory" matters.
4778
+ - After important milestones (feature done, bug fixed, migration applied), consider a **brief add** so future-you in a new session is not blind.
4569
4779
  </coding_memory>
4570
4780
 
4571
4781
  ---
@@ -4601,6 +4811,7 @@ Run tests when modifying code. If a testing skill is listed in available_skills,
4601
4811
  - **ALWAYS read a file before editing** - Use read_file_lines or ls_tool first
4602
4812
  - **Use absolute paths** when possible to avoid ambiguity
4603
4813
  - **For edit_tool**: Provide exact content with correct whitespace (read first!)
4814
+ - **Truncated CLI preview** (user may press Ctrl+O for more lines): still not a substitute for \`read_file_lines\` \u2014 use the tool for authoritative content before editing
4604
4815
  - **Check file exists** before attempting edits
4605
4816
 
4606
4817
  ### Safe Auto-Approved Tools (no confirmation needed):
@@ -5080,7 +5291,7 @@ User: "Publish the package"
5080
5291
  <coding_memory_snapshot>
5081
5292
  ## Persistent notes (loaded at session start from disk)
5082
5293
 
5083
- ${memorySnapshot.trim().length > 0 ? memorySnapshot : "(No entries yet. Use coding_memory with action add or search to build continuity across sessions.)"}
5294
+ ${memorySnapshot.trim().length > 0 ? memorySnapshot : "(No entries yet. Use coding_memory: add, list, search \u2014 remove/update by id when needed.)"}
5084
5295
  </coding_memory_snapshot>
5085
5296
  `;
5086
5297
  return prompt;
@@ -5094,6 +5305,161 @@ function isGitRepo(dir) {
5094
5305
  }
5095
5306
  }
5096
5307
 
5308
+ // src/app/agent/core/context-api/token_counter.ts
5309
+ import { getEncoding } from "js-tiktoken";
5310
+ var MESSAGE_OVERHEAD_TOKENS = 4;
5311
+ var CONVERSATION_BASE_OVERHEAD = 3;
5312
+ var cachedEncoding = null;
5313
+ function getO200kEncoding() {
5314
+ if (!cachedEncoding) {
5315
+ cachedEncoding = getEncoding("o200k_base");
5316
+ }
5317
+ return cachedEncoding;
5318
+ }
5319
+ function messageBodyForTokens(msg) {
5320
+ const c = msg.content;
5321
+ if (c == null) {
5322
+ return "";
5323
+ }
5324
+ if (typeof c === "string") {
5325
+ return c;
5326
+ }
5327
+ return JSON.stringify(c);
5328
+ }
5329
+ function messageExtraForTokens(msg) {
5330
+ const m = msg;
5331
+ const parts = [String(m.role ?? "")];
5332
+ if (Array.isArray(m.tool_calls)) {
5333
+ parts.push(JSON.stringify(m.tool_calls));
5334
+ }
5335
+ if (typeof m.tool_call_id === "string") {
5336
+ parts.push(m.tool_call_id);
5337
+ }
5338
+ if (typeof m.name === "string") {
5339
+ parts.push(m.name);
5340
+ }
5341
+ return parts.join("\0");
5342
+ }
5343
+ function countTokens(messages) {
5344
+ if (messages.length === 0) {
5345
+ return CONVERSATION_BASE_OVERHEAD;
5346
+ }
5347
+ const enc = getO200kEncoding();
5348
+ let total = CONVERSATION_BASE_OVERHEAD;
5349
+ for (const msg of messages) {
5350
+ const body = messageBodyForTokens(msg);
5351
+ const extra = messageExtraForTokens(msg);
5352
+ const nBody = body ? enc.encode(body).length : 0;
5353
+ const nExtra = extra ? enc.encode(extra).length : 0;
5354
+ total += nBody + nExtra + MESSAGE_OVERHEAD_TOKENS;
5355
+ }
5356
+ return total;
5357
+ }
5358
+
5359
+ // src/app/agent/core/context-api/history_anchor.ts
5360
+ function stripJsonFence(text) {
5361
+ let s = text.trim();
5362
+ const fence = /^```(?:json)?\s*([\s\S]*?)```\s*$/im.exec(s);
5363
+ if (fence) {
5364
+ s = fence[1].trim();
5365
+ }
5366
+ return s;
5367
+ }
5368
+ function parseAnchorJson(raw) {
5369
+ const cleaned = stripJsonFence(raw);
5370
+ const start = cleaned.indexOf("{");
5371
+ const end = cleaned.lastIndexOf("}");
5372
+ if (start === -1 || end === -1 || end <= start) {
5373
+ throw new Error("compressToAnchor: resposta sem JSON object");
5374
+ }
5375
+ const parsed = JSON.parse(cleaned.slice(start, end + 1));
5376
+ return {
5377
+ intent: String(parsed.intent ?? ""),
5378
+ filesModified: Array.isArray(parsed.filesModified) ? parsed.filesModified.map(String) : [],
5379
+ decisionsMade: Array.isArray(parsed.decisionsMade) ? parsed.decisionsMade.map(String) : [],
5380
+ errorsEncountered: Array.isArray(parsed.errorsEncountered) ? parsed.errorsEncountered.map(String) : [],
5381
+ currentState: String(parsed.currentState ?? ""),
5382
+ nextSteps: String(parsed.nextSteps ?? ""),
5383
+ compressedAt: typeof parsed.compressedAt === "number" ? parsed.compressedAt : Date.now(),
5384
+ turnsCompressed: typeof parsed.turnsCompressed === "number" ? parsed.turnsCompressed : 0
5385
+ };
5386
+ }
5387
+ function formatMessagesForPrompt(msgs) {
5388
+ return msgs.map((m, i) => {
5389
+ const role = m.role ?? "?";
5390
+ const content = m.content;
5391
+ const text = typeof content === "string" ? content : content == null ? "" : JSON.stringify(content);
5392
+ const toolCalls = m.tool_calls;
5393
+ const tail = toolCalls ? `
5394
+ [tool_calls]: ${JSON.stringify(toolCalls)}` : "";
5395
+ return `--- msg ${i + 1} (${role}) ---
5396
+ ${text.slice(0, 12e4)}${tail}`;
5397
+ }).join("\n\n");
5398
+ }
5399
+ async function compressToAnchor(spansToCompress, existingAnchor, llmService, userContext, turnSlicesInBatch) {
5400
+ const messagesBlock = formatMessagesForPrompt(spansToCompress);
5401
+ const existingBlock = existingAnchor ? `ANCHOR EXISTENTE (estende este, n\xE3o substitui \u2014 merge sem perder informa\xE7\xE3o):
5402
+ ${JSON.stringify(existingAnchor, null, 2)}` : "N\xE3o existe anchor anterior.";
5403
+ const userPrompt = `Tu \xE9s um compressor de hist\xF3rico para um agente de c\xF3digo CLI.
5404
+
5405
+ ${existingBlock}
5406
+
5407
+ MENSAGENS A COMPRIMIR (turnos antigos da conversa):
5408
+ ${messagesBlock}
5409
+
5410
+ Produz um JSON com esta estrutura exata:
5411
+ {
5412
+ "intent": "objetivo principal da sess\xE3o",
5413
+ "filesModified": ["path1", "path2"],
5414
+ "decisionsMade": ["decis\xE3o 1", "decis\xE3o 2"],
5415
+ "errorsEncountered": ["erro X \u2192 resolvido com Y"],
5416
+ "currentState": "onde estamos agora em 2-3 frases",
5417
+ "nextSteps": "o que est\xE1 pendente",
5418
+ "compressedAt": ${Date.now()},
5419
+ "turnsCompressed": 0
5420
+ }
5421
+
5422
+ Regras:
5423
+ - Responde APENAS com JSON v\xE1lido, sem markdown, sem texto extra
5424
+ - filesModified: inclui TODOS os paths de ficheiros mencionados
5425
+ - decisionsMade: preserva decis\xF5es de arquitetura, abordagem, estrutura
5426
+ - errorsEncountered: formato "erro \u2192 solu\xE7\xE3o"
5427
+ - Se j\xE1 existe anchor, faz merge \u2014 n\xE3o percas informa\xE7\xE3o anterior
5428
+ - O campo turnsCompressed na resposta ser\xE1 ignorado; usa 0.`;
5429
+ const resp = await llmService.chatCompletion({
5430
+ messages: [
5431
+ {
5432
+ role: "system",
5433
+ content: "\xC9s um extrator factual. Sa\xEDda: apenas um objeto JSON v\xE1lido, sem markdown."
5434
+ },
5435
+ { role: "user", content: userPrompt }
5436
+ ],
5437
+ temperature: 0,
5438
+ max_tokens: 4096,
5439
+ userContext
5440
+ });
5441
+ const text = resp.choices[0]?.message?.content;
5442
+ if (typeof text !== "string" || !text.trim()) {
5443
+ throw new Error("compressToAnchor: modelo n\xE3o devolveu conte\xFAdo textual");
5444
+ }
5445
+ const anchor = parseAnchorJson(text);
5446
+ const prevTurns = existingAnchor?.turnsCompressed ?? 0;
5447
+ anchor.turnsCompressed = prevTurns + Math.max(0, turnSlicesInBatch);
5448
+ anchor.compressedAt = Date.now();
5449
+ return anchor;
5450
+ }
5451
+ function anchorToSystemMessage(anchor) {
5452
+ const date = new Date(anchor.compressedAt).toISOString();
5453
+ const content = `[MEM\xD3RIA DE SESS\xC3O \u2014 ${anchor.turnsCompressed} turnos comprimidos em ${date}]
5454
+ Objetivo: ${anchor.intent}
5455
+ Ficheiros modificados: ${anchor.filesModified.join(", ") || "(nenhum)"}
5456
+ Decis\xF5es tomadas: ${anchor.decisionsMade.join(" | ") || "(nenhuma)"}
5457
+ Erros resolvidos: ${anchor.errorsEncountered.join(" | ") || "(nenhum)"}
5458
+ Estado atual: ${anchor.currentState}
5459
+ Pr\xF3ximos passos: ${anchor.nextSteps}`;
5460
+ return { role: "system", content };
5461
+ }
5462
+
5097
5463
  // src/app/agent/core/context-api/context_manager.ts
5098
5464
  function isEndTurnToolCall(tc) {
5099
5465
  if (tc.function.name === "agent_end_turn") {
@@ -5109,40 +5475,26 @@ function isEndTurnToolCall(tc) {
5109
5475
  }
5110
5476
  return false;
5111
5477
  }
5112
- function createApiContextWindow(fullHistory, maxTurns) {
5113
- if (!fullHistory.length) {
5114
- return [];
5115
- }
5116
- if (maxTurns === null || maxTurns === void 0) {
5117
- return [...fullHistory];
5118
- }
5119
- const systemMessages = [];
5120
- let historyStartIndex = 0;
5121
- while (historyStartIndex < fullHistory.length && fullHistory[historyStartIndex].role === "system") {
5122
- systemMessages.push(fullHistory[historyStartIndex]);
5123
- historyStartIndex++;
5124
- }
5125
- const conversationHistory = fullHistory.slice(historyStartIndex);
5478
+ function partitionConversationIntoTurnSlices(conversationHistory) {
5126
5479
  const turns = [];
5127
5480
  let currentTurn = [];
5128
- let turnsFound = 0;
5129
5481
  const isDevOverlay = (m) => m?.role === "user" && m?.name === "user_overlay";
5130
5482
  for (let i = conversationHistory.length - 1; i >= 0; i--) {
5131
5483
  const msg = conversationHistory[i];
5132
5484
  currentTurn.unshift(msg);
5133
- const endsWithAgentEnd = msg.role === "assistant" && msg.tool_calls?.some((tc) => isEndTurnToolCall(tc));
5485
+ const endsWithAgentEnd = msg.role === "assistant" && msg.tool_calls?.some(
5486
+ (tc) => isEndTurnToolCall(tc)
5487
+ );
5134
5488
  if (endsWithAgentEnd) {
5135
5489
  turns.unshift([...currentTurn]);
5136
5490
  currentTurn = [];
5137
- turnsFound++;
5138
- if (turnsFound >= maxTurns) {
5139
- break;
5140
- }
5141
5491
  continue;
5142
5492
  }
5143
5493
  const prev = conversationHistory[i - 1];
5144
5494
  if (msg.role === "user" && !isDevOverlay(msg)) {
5145
- if (prev && prev.role === "assistant" && !prev.tool_calls?.some((tc) => isEndTurnToolCall(tc))) {
5495
+ if (prev && prev.role === "assistant" && !prev.tool_calls?.some(
5496
+ (tc) => isEndTurnToolCall(tc)
5497
+ )) {
5146
5498
  const hasNonOverlay = currentTurn.some((m) => m.role !== "user" || !isDevOverlay(m));
5147
5499
  if (hasNonOverlay) {
5148
5500
  turns.unshift([...currentTurn]);
@@ -5154,8 +5506,76 @@ function createApiContextWindow(fullHistory, maxTurns) {
5154
5506
  if (currentTurn.length > 0) {
5155
5507
  turns.unshift(currentTurn);
5156
5508
  }
5157
- const finalContext = systemMessages.concat(turns.flat());
5158
- return finalContext;
5509
+ return turns;
5510
+ }
5511
+ var DEFAULT_TOKEN_BUDGET = 6e4;
5512
+ var DEFAULT_COMPRESS_THRESHOLD = 0.7;
5513
+ var DEFAULT_KEEP_RECENT_TURNS = 8;
5514
+ function readContextOptionInt(envKey, fallback) {
5515
+ const raw = (process.env[envKey] ?? "").trim();
5516
+ if (!raw) return fallback;
5517
+ const n = Number(raw);
5518
+ return Number.isFinite(n) && n > 0 ? Math.floor(n) : fallback;
5519
+ }
5520
+ function readContextOptionFloat(envKey, fallback) {
5521
+ const raw = (process.env[envKey] ?? "").trim();
5522
+ if (!raw) return fallback;
5523
+ const n = Number(raw);
5524
+ return Number.isFinite(n) && n > 0 && n <= 1 ? n : fallback;
5525
+ }
5526
+ function buildContextMessages(systemMessages, anchor, pendingRaw, recentFlat) {
5527
+ const anchorMsg = anchor ? [anchorToSystemMessage(anchor)] : [];
5528
+ return [...systemMessages, ...anchorMsg, ...pendingRaw, ...recentFlat];
5529
+ }
5530
+ async function createApiContextWindow(fullHistory, currentAnchor, compressedTurnSliceCount, llmService, userContext, options) {
5531
+ if (!fullHistory.length) {
5532
+ return {
5533
+ messages: [],
5534
+ newAnchor: currentAnchor,
5535
+ newCompressedTurnSliceCount: compressedTurnSliceCount
5536
+ };
5537
+ }
5538
+ const tokenBudget = options?.tokenBudget ?? readContextOptionInt("BLUMA_CONTEXT_TOKEN_BUDGET", DEFAULT_TOKEN_BUDGET);
5539
+ const compressThreshold = options?.compressThreshold ?? readContextOptionFloat("BLUMA_COMPRESS_THRESHOLD", DEFAULT_COMPRESS_THRESHOLD);
5540
+ const keepRecentTurns = options?.keepRecentTurns ?? readContextOptionInt("BLUMA_KEEP_RECENT_TURNS", DEFAULT_KEEP_RECENT_TURNS);
5541
+ const systemMessages = [];
5542
+ let historyStartIndex = 0;
5543
+ while (historyStartIndex < fullHistory.length && fullHistory[historyStartIndex].role === "system") {
5544
+ systemMessages.push(fullHistory[historyStartIndex]);
5545
+ historyStartIndex++;
5546
+ }
5547
+ const conversationHistory = fullHistory.slice(historyStartIndex);
5548
+ const turnSlices = partitionConversationIntoTurnSlices(conversationHistory);
5549
+ const n = turnSlices.length;
5550
+ const recentStart = Math.max(0, n - keepRecentTurns);
5551
+ let sliceCount = Math.min(Math.max(0, compressedTurnSliceCount), recentStart);
5552
+ let anchor = currentAnchor;
5553
+ const recentSlices = turnSlices.slice(recentStart);
5554
+ const recentFlat = recentSlices.flat();
5555
+ const thresholdTokens = tokenBudget * compressThreshold;
5556
+ let pendingSlices = turnSlices.slice(sliceCount, recentStart);
5557
+ let pendingFlat = pendingSlices.flat();
5558
+ let messages = buildContextMessages(systemMessages, anchor, pendingFlat, recentFlat);
5559
+ let tokens = countTokens(messages);
5560
+ while (tokens >= thresholdTokens && pendingSlices.length > 0) {
5561
+ anchor = await compressToAnchor(
5562
+ pendingFlat,
5563
+ anchor,
5564
+ llmService,
5565
+ userContext,
5566
+ pendingSlices.length
5567
+ );
5568
+ sliceCount = recentStart;
5569
+ pendingSlices = [];
5570
+ pendingFlat = [];
5571
+ messages = buildContextMessages(systemMessages, anchor, pendingFlat, recentFlat);
5572
+ tokens = countTokens(messages);
5573
+ }
5574
+ return {
5575
+ messages,
5576
+ newAnchor: anchor,
5577
+ newCompressedTurnSliceCount: sliceCount
5578
+ };
5159
5579
  }
5160
5580
 
5161
5581
  // src/app/agent/core/llm/llm.ts
@@ -5538,7 +5958,9 @@ var BluMaAgent = class {
5538
5958
  mcpClient;
5539
5959
  feedbackSystem;
5540
5960
  skillLoader;
5541
- maxContextTurns = 5;
5961
+ /** Memória comprimida persistida (turnos antigos) + cursor de fatias já absorvidas. */
5962
+ sessionAnchor = null;
5963
+ compressedTurnSliceCount = 0;
5542
5964
  isInterrupted = false;
5543
5965
  /** Mesmo turnId durante processTurn + todo o loop de tool_calls (FactorRouter). */
5544
5966
  activeTurnContext = null;
@@ -5562,23 +5984,38 @@ var BluMaAgent = class {
5562
5984
  this.eventBus.emit("backend_message", { type: "user_overlay", payload: clean, ts: data.ts || Date.now() });
5563
5985
  try {
5564
5986
  if (this.sessionFile) {
5565
- await saveSessionHistory(this.sessionFile, this.history);
5987
+ this.persistSession();
5566
5988
  } else {
5567
- const [sessionFile, _] = await loadOrcreateSession(this.sessionId);
5989
+ const [sessionFile, , mem] = await loadOrcreateSession(this.sessionId);
5568
5990
  this.sessionFile = sessionFile;
5569
- await saveSessionHistory(this.sessionFile, this.history);
5991
+ this.sessionAnchor = mem.historyAnchor;
5992
+ this.compressedTurnSliceCount = mem.compressedTurnSliceCount;
5993
+ this.persistSession();
5570
5994
  }
5571
5995
  } catch (e) {
5572
5996
  this.eventBus.emit("backend_message", { type: "error", message: `Falha ao salvar hist\xF3rico ap\xF3s user_overlay: ${e.message}` });
5573
5997
  }
5574
5998
  });
5575
5999
  }
6000
+ getMemorySnapshot() {
6001
+ return {
6002
+ historyAnchor: this.sessionAnchor,
6003
+ compressedTurnSliceCount: this.compressedTurnSliceCount
6004
+ };
6005
+ }
6006
+ /** Debounced: grava histórico + estado de compressão no mesmo ficheiro de sessão. */
6007
+ persistSession() {
6008
+ if (!this.sessionFile) return;
6009
+ void saveSessionHistory(this.sessionFile, this.history, this.getMemorySnapshot());
6010
+ }
5576
6011
  async initialize() {
5577
6012
  await this.mcpClient.nativeToolInvoker.initialize();
5578
6013
  await this.mcpClient.initialize();
5579
- const [sessionFile, history] = await loadOrcreateSession(this.sessionId);
6014
+ const [sessionFile, history, mem] = await loadOrcreateSession(this.sessionId);
5580
6015
  this.sessionFile = sessionFile;
5581
6016
  this.history = history;
6017
+ this.sessionAnchor = mem.historyAnchor;
6018
+ this.compressedTurnSliceCount = mem.compressedTurnSliceCount;
5582
6019
  initializeSkillContext({
5583
6020
  history: this.history,
5584
6021
  skillLoader: this.skillLoader
@@ -5604,7 +6041,7 @@ var BluMaAgent = class {
5604
6041
  }
5605
6042
  const systemPrompt = getUnifiedSystemPrompt(availableSkills);
5606
6043
  this.history.push({ role: "system", content: systemPrompt });
5607
- await saveSessionHistory(this.sessionFile, this.history);
6044
+ this.persistSession();
5608
6045
  }
5609
6046
  }
5610
6047
  getAvailableTools() {
@@ -5634,11 +6071,13 @@ var BluMaAgent = class {
5634
6071
  let toolResultContent;
5635
6072
  let shouldContinueConversation = true;
5636
6073
  if (!this.sessionFile) {
5637
- const [sessionFile, history] = await loadOrcreateSession(this.sessionId);
6074
+ const [sessionFile, history, mem] = await loadOrcreateSession(this.sessionId);
5638
6075
  this.sessionFile = sessionFile;
5639
6076
  if (this.history.length === 0 && history.length > 0) {
5640
6077
  this.history = history;
5641
6078
  }
6079
+ this.sessionAnchor = mem.historyAnchor;
6080
+ this.compressedTurnSliceCount = mem.compressedTurnSliceCount;
5642
6081
  }
5643
6082
  if (decisionData.type === "user_decision_execute") {
5644
6083
  const toolName = toolCall.function.name;
@@ -5660,7 +6099,7 @@ var BluMaAgent = class {
5660
6099
  raw_arguments: toolCall.function.arguments
5661
6100
  });
5662
6101
  this.history.push({ role: "tool", tool_call_id: toolCall.id, content: toolResultContent });
5663
- await saveSessionHistory(this.sessionFile, this.history);
6102
+ this.persistSession();
5664
6103
  await this._continueConversation();
5665
6104
  return;
5666
6105
  }
@@ -5672,7 +6111,7 @@ var BluMaAgent = class {
5672
6111
  received: toolArgs
5673
6112
  });
5674
6113
  this.history.push({ role: "tool", tool_call_id: toolCall.id, content: toolResultContent });
5675
- await saveSessionHistory(this.sessionFile, this.history);
6114
+ this.persistSession();
5676
6115
  await this._continueConversation();
5677
6116
  return;
5678
6117
  }
@@ -5683,7 +6122,7 @@ var BluMaAgent = class {
5683
6122
  received: toolArgs
5684
6123
  });
5685
6124
  this.history.push({ role: "tool", tool_call_id: toolCall.id, content: toolResultContent });
5686
- await saveSessionHistory(this.sessionFile, this.history);
6125
+ this.persistSession();
5687
6126
  await this._continueConversation();
5688
6127
  return;
5689
6128
  }
@@ -5753,7 +6192,7 @@ var BluMaAgent = class {
5753
6192
  toolResultContent = "The system rejected this action. Verify that the command you are executing contributes to the tasks intent and try again.";
5754
6193
  }
5755
6194
  this.history.push({ role: "tool", tool_call_id: toolCall.id, content: toolResultContent });
5756
- await saveSessionHistory(this.sessionFile, this.history);
6195
+ this.persistSession();
5757
6196
  if (shouldContinueConversation && !this.isInterrupted) {
5758
6197
  await this._continueConversation();
5759
6198
  }
@@ -5803,7 +6242,16 @@ ${editData.error.display}`;
5803
6242
  this.eventBus.emit("backend_message", { type: "info", message: "Task Canceled." });
5804
6243
  return;
5805
6244
  }
5806
- const contextWindow = createApiContextWindow(this.history, this.maxContextTurns);
6245
+ const { messages: contextWindow, newAnchor, newCompressedTurnSliceCount } = await createApiContextWindow(
6246
+ this.history,
6247
+ this.sessionAnchor,
6248
+ this.compressedTurnSliceCount,
6249
+ this.llm,
6250
+ this.getLlmUserContext()
6251
+ );
6252
+ this.sessionAnchor = newAnchor;
6253
+ this.compressedTurnSliceCount = newCompressedTurnSliceCount;
6254
+ this.persistSession();
5807
6255
  const llmService = this.llm;
5808
6256
  if (typeof llmService.chatCompletionStream === "function") {
5809
6257
  await this._handleStreamingResponse(contextWindow);
@@ -5814,7 +6262,7 @@ ${editData.error.display}`;
5814
6262
  const errorMessage = error instanceof Error ? error.message : "An unknown API error occurred.";
5815
6263
  this.eventBus.emit("backend_message", { type: "error", message: errorMessage });
5816
6264
  } finally {
5817
- await saveSessionHistory(this.sessionFile, this.history);
6265
+ this.persistSession();
5818
6266
  }
5819
6267
  }
5820
6268
  async _handleStreamingResponse(contextWindow) {
@@ -6623,18 +7071,15 @@ var Agent = class {
6623
7071
 
6624
7072
  // src/app/ui/WorkingTimer.tsx
6625
7073
  import { useState as useState4, useEffect as useEffect4, memo as memo5 } from "react";
6626
- import { Box as Box7, Text as Text7 } from "ink";
6627
- import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
7074
+ import { Box as Box8, Text as Text8 } from "ink";
7075
+ import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
6628
7076
  var WorkingTimerComponent = ({ eventBus, taskName, taskStatus }) => {
6629
- const [currentAction, setCurrentAction] = useState4("Thinking...");
7077
+ const [currentAction, setCurrentAction] = useState4("working");
6630
7078
  const [shinePosition, setShinePosition] = useState4(0);
6631
- const [dots, setDots] = useState4("");
6632
7079
  useEffect4(() => {
6633
7080
  if (!eventBus) return;
6634
7081
  const handleActionStatus = (data) => {
6635
- if (data.action) {
6636
- setCurrentAction(data.action);
6637
- }
7082
+ if (data.action) setCurrentAction(data.action);
6638
7083
  };
6639
7084
  eventBus.on("action_status", handleActionStatus);
6640
7085
  return () => {
@@ -6647,12 +7092,6 @@ var WorkingTimerComponent = ({ eventBus, taskName, taskStatus }) => {
6647
7092
  }, 120);
6648
7093
  return () => clearInterval(shineTimer);
6649
7094
  }, []);
6650
- useEffect4(() => {
6651
- const dotsTimer = setInterval(() => {
6652
- setDots((prev) => prev.length >= 3 ? "" : prev + ".");
6653
- }, 500);
6654
- return () => clearInterval(dotsTimer);
6655
- }, []);
6656
7095
  const displayAction = taskStatus || currentAction;
6657
7096
  const renderShineText = (text) => {
6658
7097
  const chars = text.split("");
@@ -6661,29 +7100,28 @@ var WorkingTimerComponent = ({ eventBus, taskName, taskStatus }) => {
6661
7100
  return chars.map((char, i) => {
6662
7101
  const distance = Math.abs(i - shineIdx);
6663
7102
  if (distance <= 1) {
6664
- return /* @__PURE__ */ jsx7(Text7, { color: "white", dimColor: true, children: char }, i);
6665
- } else {
6666
- return /* @__PURE__ */ jsx7(Text7, { color: "gray", dimColor: true, children: char }, i);
7103
+ return /* @__PURE__ */ jsx8(Text8, { color: BLUMA_TERMINAL.brandMagenta, children: char }, i);
6667
7104
  }
7105
+ return /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: char }, i);
6668
7106
  });
6669
7107
  };
6670
- return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", paddingX: 1, children: [
6671
- /* @__PURE__ */ jsx7(Box7, { children: renderShineText(displayAction) }),
6672
- taskName && /* @__PURE__ */ jsx7(Box7, { paddingLeft: 2, children: /* @__PURE__ */ jsxs7(Text7, { dimColor: true, children: [
6673
- "\u203A ",
7108
+ return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginBottom: 1, children: [
7109
+ /* @__PURE__ */ jsx8(M3StatusStrip, { children: renderShineText(displayAction) }),
7110
+ taskName ? /* @__PURE__ */ jsx8(Box8, { paddingLeft: 2, children: /* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
7111
+ /* @__PURE__ */ jsx8(Text8, { color: BLUMA_TERMINAL.brandBlue, children: "\u2514 " }),
6674
7112
  taskName
6675
- ] }) })
7113
+ ] }) }) : null
6676
7114
  ] });
6677
7115
  };
6678
7116
  var WorkingTimer = memo5(WorkingTimerComponent);
6679
7117
 
6680
7118
  // src/app/ui/components/ToolCallDisplay.tsx
6681
7119
  import { memo as memo6 } from "react";
6682
- import { Box as Box9 } from "ink";
7120
+ import { Box as Box10 } from "ink";
6683
7121
 
6684
7122
  // src/app/ui/components/toolCallRenderers.tsx
6685
- import { Box as Box8, Text as Text8 } from "ink";
6686
- import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
7123
+ import { Box as Box9, Text as Text9 } from "ink";
7124
+ import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
6687
7125
  var parseArgs = (args) => {
6688
7126
  if (typeof args === "string") {
6689
7127
  try {
@@ -6705,9 +7143,9 @@ var getBasename = (filepath) => {
6705
7143
  var renderShellCommand2 = ({ args }) => {
6706
7144
  const parsed = parseArgs(args);
6707
7145
  const command = parsed.command || "[no command]";
6708
- return /* @__PURE__ */ jsxs8(Box8, { paddingX: 1, children: [
6709
- /* @__PURE__ */ jsx8(Text8, { color: "white", bold: true, children: "$" }),
6710
- /* @__PURE__ */ jsxs8(Text8, { color: "white", children: [
7146
+ return /* @__PURE__ */ jsxs9(Box9, { children: [
7147
+ /* @__PURE__ */ jsx9(Text9, { color: BLUMA_TERMINAL.brandBlue, bold: true, children: "$" }),
7148
+ /* @__PURE__ */ jsxs9(Text9, { children: [
6711
7149
  " ",
6712
7150
  truncate(command, 70)
6713
7151
  ] })
@@ -6716,9 +7154,9 @@ var renderShellCommand2 = ({ args }) => {
6716
7154
  var renderLsTool2 = ({ args }) => {
6717
7155
  const parsed = parseArgs(args);
6718
7156
  const path19 = parsed.directory_path || ".";
6719
- return /* @__PURE__ */ jsxs8(Box8, { paddingX: 1, children: [
6720
- /* @__PURE__ */ jsx8(Text8, { color: "white", bold: true, children: "ls" }),
6721
- /* @__PURE__ */ jsxs8(Text8, { children: [
7157
+ return /* @__PURE__ */ jsxs9(Box9, { children: [
7158
+ /* @__PURE__ */ jsx9(Text9, { color: BLUMA_TERMINAL.brandBlue, bold: true, children: "ls" }),
7159
+ /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
6722
7160
  " ",
6723
7161
  path19
6724
7162
  ] })
@@ -6727,9 +7165,9 @@ var renderLsTool2 = ({ args }) => {
6727
7165
  var renderCountFilesLines = ({ args }) => {
6728
7166
  const parsed = parseArgs(args);
6729
7167
  const filepath = parsed.filepath || "[no file]";
6730
- return /* @__PURE__ */ jsxs8(Box8, { paddingX: 1, children: [
6731
- /* @__PURE__ */ jsx8(Text8, { color: "white", bold: true, children: "wc -l" }),
6732
- /* @__PURE__ */ jsxs8(Text8, { children: [
7168
+ return /* @__PURE__ */ jsxs9(Box9, { children: [
7169
+ /* @__PURE__ */ jsx9(Text9, { color: BLUMA_TERMINAL.brandBlue, bold: true, children: "wc -l" }),
7170
+ /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
6733
7171
  " ",
6734
7172
  getBasename(filepath)
6735
7173
  ] })
@@ -6740,14 +7178,15 @@ var renderReadFileLines2 = ({ args }) => {
6740
7178
  const filepath = parsed.filepath || "[no file]";
6741
7179
  const start = parsed.start_line || 1;
6742
7180
  const end = parsed.end_line || start;
6743
- return /* @__PURE__ */ jsxs8(Box8, { paddingX: 1, children: [
6744
- /* @__PURE__ */ jsx8(Text8, { color: "white", bold: true, children: "cat" }),
6745
- /* @__PURE__ */ jsxs8(Text8, { children: [
7181
+ return /* @__PURE__ */ jsxs9(Box9, { children: [
7182
+ /* @__PURE__ */ jsx9(Text9, { color: BLUMA_TERMINAL.brandBlue, bold: true, children: "cat" }),
7183
+ /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
6746
7184
  " ",
6747
7185
  getBasename(filepath)
6748
7186
  ] }),
6749
- /* @__PURE__ */ jsxs8(Text8, { color: "white", children: [
6750
- " :",
7187
+ /* @__PURE__ */ jsxs9(Text9, { color: BLUMA_TERMINAL.brandMagenta, children: [
7188
+ " ",
7189
+ ":",
6751
7190
  start,
6752
7191
  "-",
6753
7192
  end
@@ -6758,9 +7197,9 @@ var renderBlumaNotebook = ({ args }) => {
6758
7197
  const parsed = parseArgs(args);
6759
7198
  const thought = parsed.thought || parsed.content?.thought || "[thinking...]";
6760
7199
  const truncated = truncate(thought, 100);
6761
- return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", paddingX: 1, children: [
6762
- /* @__PURE__ */ jsx8(Box8, { children: /* @__PURE__ */ jsx8(Text8, { color: "white", bold: true, children: "thinking" }) }),
6763
- /* @__PURE__ */ jsx8(Box8, { paddingLeft: 2, children: /* @__PURE__ */ jsx8(Text8, { dimColor: true, italic: true, children: truncated }) })
7200
+ return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
7201
+ /* @__PURE__ */ jsx9(Box9, { children: /* @__PURE__ */ jsx9(Text9, { color: BLUMA_TERMINAL.brandMagenta, bold: true, children: "thinking" }) }),
7202
+ /* @__PURE__ */ jsx9(Box9, { paddingLeft: 2, children: /* @__PURE__ */ jsx9(Text9, { dimColor: true, italic: true, children: truncated }) })
6764
7203
  ] });
6765
7204
  };
6766
7205
  var renderEditToolCall = ({ args, preview }) => {
@@ -6768,20 +7207,20 @@ var renderEditToolCall = ({ args, preview }) => {
6768
7207
  const filepath = parsed.file_path || "[no file]";
6769
7208
  const oldStr = parsed.old_string || "";
6770
7209
  const newStr = parsed.new_string || "";
6771
- return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", paddingX: 1, children: [
6772
- /* @__PURE__ */ jsxs8(Box8, { children: [
6773
- /* @__PURE__ */ jsx8(Text8, { color: "white", bold: true, children: "edit" }),
6774
- /* @__PURE__ */ jsxs8(Text8, { children: [
7210
+ return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
7211
+ /* @__PURE__ */ jsxs9(Box9, { children: [
7212
+ /* @__PURE__ */ jsx9(Text9, { color: BLUMA_TERMINAL.brandMagenta, bold: true, children: "edit" }),
7213
+ /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
6775
7214
  " ",
6776
7215
  getBasename(filepath)
6777
7216
  ] })
6778
7217
  ] }),
6779
- preview ? /* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(SimpleDiff, { text: preview, maxHeight: 8 }) }) : /* @__PURE__ */ jsxs8(Box8, { paddingLeft: 2, flexDirection: "column", children: [
6780
- /* @__PURE__ */ jsxs8(Text8, { color: "red", dimColor: true, children: [
7218
+ preview ? /* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx9(SimpleDiff, { text: preview, maxHeight: 8 }) }) : /* @__PURE__ */ jsxs9(Box9, { paddingLeft: 2, flexDirection: "column", children: [
7219
+ /* @__PURE__ */ jsxs9(Text9, { color: "red", dimColor: true, children: [
6781
7220
  "- ",
6782
7221
  truncate(oldStr, 50)
6783
7222
  ] }),
6784
- /* @__PURE__ */ jsxs8(Text8, { color: "green", bold: true, children: [
7223
+ /* @__PURE__ */ jsxs9(Text9, { color: "green", bold: true, children: [
6785
7224
  "+ ",
6786
7225
  truncate(newStr, 50)
6787
7226
  ] })
@@ -6797,36 +7236,40 @@ var renderTodoTool2 = ({ args }) => {
6797
7236
  const barWidth = 10;
6798
7237
  const filled = Math.round(progress / 100 * barWidth);
6799
7238
  const bar = "=".repeat(filled) + " ".repeat(barWidth - filled);
6800
- return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", paddingX: 1, children: [
6801
- /* @__PURE__ */ jsxs8(Box8, { children: [
6802
- /* @__PURE__ */ jsx8(Text8, { color: "white", bold: true, children: "todo" }),
6803
- /* @__PURE__ */ jsxs8(Text8, { children: [
6804
- " [",
7239
+ return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
7240
+ /* @__PURE__ */ jsxs9(Box9, { flexDirection: "row", flexWrap: "wrap", children: [
7241
+ /* @__PURE__ */ jsx9(Text9, { color: BLUMA_TERMINAL.brandBlue, bold: true, children: pending }),
7242
+ /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: " open \xB7 " }),
7243
+ /* @__PURE__ */ jsx9(Text9, { color: BLUMA_TERMINAL.brandMagenta, bold: true, children: completed }),
7244
+ /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: " done \xB7 " }),
7245
+ /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
7246
+ "[",
6805
7247
  bar,
6806
7248
  "] ",
6807
7249
  progress,
6808
7250
  "%"
6809
7251
  ] })
6810
7252
  ] }),
6811
- tasks.length > 0 && /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", paddingLeft: 2, children: [
6812
- tasks.slice(0, 15).map((task, i) => /* @__PURE__ */ jsxs8(
6813
- Text8,
6814
- {
6815
- color: task.isComplete === true ? "gray" : "white",
6816
- dimColor: task.isComplete === true,
6817
- strikethrough: task.isComplete === true,
6818
- children: [
6819
- task.isComplete === true ? "[x]" : "[ ]",
6820
- " ",
6821
- task.description
6822
- ]
6823
- },
6824
- i
6825
- )),
6826
- tasks.length > 15 && /* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
6827
- "... +",
7253
+ tasks.length > 0 && /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginTop: 1, paddingLeft: 1, children: [
7254
+ tasks.slice(0, 15).map((task, i) => {
7255
+ const done = task.isComplete === true;
7256
+ return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "row", children: [
7257
+ /* @__PURE__ */ jsx9(Text9, { color: done ? BLUMA_TERMINAL.brandBlue : BLUMA_TERMINAL.brandMagenta, children: done ? "\u25A0 " : "\u25A1 " }),
7258
+ /* @__PURE__ */ jsx9(
7259
+ Text9,
7260
+ {
7261
+ dimColor: done,
7262
+ strikethrough: done,
7263
+ color: done ? BLUMA_TERMINAL.muted : void 0,
7264
+ children: task.description
7265
+ }
7266
+ )
7267
+ ] }, i);
7268
+ }),
7269
+ tasks.length > 15 && /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
7270
+ "\u2026 +",
6828
7271
  tasks.length - 15,
6829
- " more tasks"
7272
+ " tasks"
6830
7273
  ] })
6831
7274
  ] })
6832
7275
  ] });
@@ -6835,14 +7278,14 @@ var renderFindByName = ({ args }) => {
6835
7278
  const parsed = parseArgs(args);
6836
7279
  const pattern = parsed.pattern || "*";
6837
7280
  const dir = parsed.directory || ".";
6838
- return /* @__PURE__ */ jsxs8(Box8, { paddingX: 1, children: [
6839
- /* @__PURE__ */ jsx8(Text8, { color: "white", bold: true, children: "find" }),
6840
- /* @__PURE__ */ jsxs8(Text8, { color: "white", children: [
7281
+ return /* @__PURE__ */ jsxs9(Box9, { children: [
7282
+ /* @__PURE__ */ jsx9(Text9, { color: BLUMA_TERMINAL.brandBlue, bold: true, children: "find" }),
7283
+ /* @__PURE__ */ jsxs9(Text9, { color: BLUMA_TERMINAL.brandMagenta, children: [
6841
7284
  ' "',
6842
7285
  pattern,
6843
7286
  '"'
6844
7287
  ] }),
6845
- /* @__PURE__ */ jsxs8(Text8, { children: [
7288
+ /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
6846
7289
  " in ",
6847
7290
  dir
6848
7291
  ] })
@@ -6852,14 +7295,14 @@ var renderGrepSearch = ({ args }) => {
6852
7295
  const parsed = parseArgs(args);
6853
7296
  const query = parsed.query || "";
6854
7297
  const path19 = parsed.path || ".";
6855
- return /* @__PURE__ */ jsxs8(Box8, { paddingX: 1, children: [
6856
- /* @__PURE__ */ jsx8(Text8, { color: "white", bold: true, children: "grep" }),
6857
- /* @__PURE__ */ jsxs8(Text8, { color: "white", children: [
7298
+ return /* @__PURE__ */ jsxs9(Box9, { children: [
7299
+ /* @__PURE__ */ jsx9(Text9, { color: BLUMA_TERMINAL.brandBlue, bold: true, children: "grep" }),
7300
+ /* @__PURE__ */ jsxs9(Text9, { color: BLUMA_TERMINAL.brandMagenta, children: [
6858
7301
  ' "',
6859
7302
  truncate(query, 30),
6860
7303
  '"'
6861
7304
  ] }),
6862
- /* @__PURE__ */ jsxs8(Text8, { children: [
7305
+ /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
6863
7306
  " ",
6864
7307
  path19
6865
7308
  ] })
@@ -6868,9 +7311,9 @@ var renderGrepSearch = ({ args }) => {
6868
7311
  var renderViewFileOutline = ({ args }) => {
6869
7312
  const parsed = parseArgs(args);
6870
7313
  const filepath = parsed.file_path || "[no file]";
6871
- return /* @__PURE__ */ jsxs8(Box8, { paddingX: 1, children: [
6872
- /* @__PURE__ */ jsx8(Text8, { color: "white", bold: true, children: "outline" }),
6873
- /* @__PURE__ */ jsxs8(Text8, { children: [
7314
+ return /* @__PURE__ */ jsxs9(Box9, { children: [
7315
+ /* @__PURE__ */ jsx9(Text9, { color: BLUMA_TERMINAL.brandBlue, bold: true, children: "outline" }),
7316
+ /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
6874
7317
  " ",
6875
7318
  getBasename(filepath)
6876
7319
  ] })
@@ -6879,9 +7322,9 @@ var renderViewFileOutline = ({ args }) => {
6879
7322
  var renderCommandStatus = ({ args }) => {
6880
7323
  const parsed = parseArgs(args);
6881
7324
  const id = parsed.command_id || "[no id]";
6882
- return /* @__PURE__ */ jsxs8(Box8, { paddingX: 1, children: [
6883
- /* @__PURE__ */ jsx8(Text8, { color: "white", bold: true, children: "status" }),
6884
- /* @__PURE__ */ jsxs8(Text8, { children: [
7325
+ return /* @__PURE__ */ jsxs9(Box9, { children: [
7326
+ /* @__PURE__ */ jsx9(Text9, { color: BLUMA_TERMINAL.brandBlue, bold: true, children: "status" }),
7327
+ /* @__PURE__ */ jsxs9(Text9, { color: BLUMA_TERMINAL.brandMagenta, children: [
6885
7328
  " #",
6886
7329
  id
6887
7330
  ] })
@@ -6893,36 +7336,36 @@ var renderTaskBoundary = ({ args }) => {
6893
7336
  const mode = parsed.mode || "EXECUTION";
6894
7337
  const status = parsed.task_status || "";
6895
7338
  const modeColors = {
6896
- PLANNING: "blue",
6897
- EXECUTION: "green",
6898
- VERIFICATION: "yellow"
7339
+ PLANNING: BLUMA_TERMINAL.brandBlue,
7340
+ EXECUTION: BLUMA_TERMINAL.brandBlue,
7341
+ VERIFICATION: BLUMA_TERMINAL.brandMagenta
6899
7342
  };
6900
7343
  const modeSymbols = {
6901
7344
  PLANNING: "P",
6902
7345
  EXECUTION: "E",
6903
7346
  VERIFICATION: "V"
6904
7347
  };
6905
- return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", paddingX: 1, children: [
6906
- /* @__PURE__ */ jsxs8(Box8, { children: [
6907
- /* @__PURE__ */ jsxs8(Text8, { color: modeColors[mode] || "gray", bold: true, children: [
7348
+ return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
7349
+ /* @__PURE__ */ jsxs9(Box9, { children: [
7350
+ /* @__PURE__ */ jsxs9(Text9, { color: modeColors[mode] || BLUMA_TERMINAL.muted, bold: true, children: [
6908
7351
  "[",
6909
7352
  modeSymbols[mode] || "?",
6910
7353
  "]"
6911
7354
  ] }),
6912
- /* @__PURE__ */ jsxs8(Text8, { children: [
7355
+ /* @__PURE__ */ jsxs9(Text9, { children: [
6913
7356
  " ",
6914
7357
  name
6915
7358
  ] })
6916
7359
  ] }),
6917
- status && /* @__PURE__ */ jsx8(Box8, { paddingLeft: 4, children: /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: truncate(status, 60) }) })
7360
+ status && /* @__PURE__ */ jsx9(Box9, { paddingLeft: 4, children: /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: truncate(status, 60) }) })
6918
7361
  ] });
6919
7362
  };
6920
7363
  var renderSearchWeb = ({ args }) => {
6921
7364
  const parsed = parseArgs(args);
6922
7365
  const query = parsed.query || "[no query]";
6923
- return /* @__PURE__ */ jsxs8(Box8, { paddingX: 1, children: [
6924
- /* @__PURE__ */ jsx8(Text8, { color: "white", bold: true, children: "search" }),
6925
- /* @__PURE__ */ jsxs8(Text8, { color: "white", children: [
7366
+ return /* @__PURE__ */ jsxs9(Box9, { children: [
7367
+ /* @__PURE__ */ jsx9(Text9, { color: BLUMA_TERMINAL.brandBlue, bold: true, children: "search" }),
7368
+ /* @__PURE__ */ jsxs9(Text9, { color: BLUMA_TERMINAL.brandMagenta, children: [
6926
7369
  ' "',
6927
7370
  truncate(query, 40),
6928
7371
  '"'
@@ -6932,9 +7375,9 @@ var renderSearchWeb = ({ args }) => {
6932
7375
  var renderLoadSkill = ({ args }) => {
6933
7376
  const parsed = parseArgs(args);
6934
7377
  const skillName = parsed.skill_name || "[no skill]";
6935
- return /* @__PURE__ */ jsxs8(Box8, { paddingX: 1, children: [
6936
- /* @__PURE__ */ jsx8(Text8, { color: "white", bold: true, children: "load skill" }),
6937
- /* @__PURE__ */ jsxs8(Text8, { color: "white", children: [
7378
+ return /* @__PURE__ */ jsxs9(Box9, { children: [
7379
+ /* @__PURE__ */ jsx9(Text9, { color: BLUMA_TERMINAL.brandBlue, bold: true, children: "load skill" }),
7380
+ /* @__PURE__ */ jsxs9(Text9, { color: BLUMA_TERMINAL.brandMagenta, children: [
6938
7381
  " ",
6939
7382
  skillName
6940
7383
  ] })
@@ -6943,13 +7386,7 @@ var renderLoadSkill = ({ args }) => {
6943
7386
  var renderGeneric2 = ({ toolName, args }) => {
6944
7387
  const parsed = parseArgs(args);
6945
7388
  const keys = Object.keys(parsed).slice(0, 2);
6946
- return /* @__PURE__ */ jsxs8(Box8, { paddingX: 1, children: [
6947
- /* @__PURE__ */ jsx8(Text8, { color: "white", bold: true, children: toolName }),
6948
- keys.length > 0 && /* @__PURE__ */ jsxs8(Text8, { children: [
6949
- " ",
6950
- keys.map((k) => `${k}:${truncate(String(parsed[k]), 20)}`).join(" ")
6951
- ] })
6952
- ] });
7389
+ return /* @__PURE__ */ jsx9(Box9, { children: keys.length > 0 ? /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: keys.map((k) => `${k}:${truncate(String(parsed[k]), 20)}`).join(" ") }) : /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "\u2014" }) });
6953
7390
  };
6954
7391
  var ToolRenderDisplay = {
6955
7392
  shell_command: renderShellCommand2,
@@ -6969,76 +7406,230 @@ var ToolRenderDisplay = {
6969
7406
  };
6970
7407
 
6971
7408
  // src/app/ui/components/ToolCallDisplay.tsx
6972
- import { jsx as jsx9 } from "react/jsx-runtime";
7409
+ import { jsx as jsx10 } from "react/jsx-runtime";
6973
7410
  var ToolCallDisplayComponent = ({ toolName, args, preview }) => {
6974
- if (toolName.includes("message")) {
7411
+ if (toolName.includes("message") || toolName.includes("task_boundary") || toolName === "todo") {
6975
7412
  return null;
6976
7413
  }
6977
7414
  const Renderer = ToolRenderDisplay[toolName] || ((props) => renderGeneric2({ ...props, toolName }));
6978
- return /* @__PURE__ */ jsx9(Box9, { marginBottom: 1, children: /* @__PURE__ */ jsx9(Renderer, { toolName, args, preview }) });
7415
+ return /* @__PURE__ */ jsx10(ChatBlock, { marginBottom: 1, children: /* @__PURE__ */ jsx10(Box10, { flexDirection: "column", paddingLeft: 1, children: /* @__PURE__ */ jsx10(Renderer, { toolName, args, preview }) }) });
6979
7416
  };
6980
7417
  var ToolCallDisplay = memo6(ToolCallDisplayComponent);
6981
7418
 
6982
7419
  // src/app/ui/components/ToolResultDisplay.tsx
6983
- import { memo as memo8 } from "react";
6984
- import { Box as Box11, Text as Text10 } from "ink";
7420
+ import { memo as memo8, useEffect as useEffect5 } from "react";
7421
+ import { Box as Box12, Text as Text11 } from "ink";
6985
7422
 
6986
7423
  // src/app/ui/components/MarkdownRenderer.tsx
6987
- import { memo as memo7 } from "react";
6988
- import { Box as Box10, Text as Text9 } from "ink";
7424
+ import { cloneElement, isValidElement, memo as memo7 } from "react";
7425
+ import { Box as Box11, Text as Text10 } from "ink";
6989
7426
  import { marked } from "marked";
6990
7427
  import { highlight } from "cli-highlight";
6991
- import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
6992
- var COLORS = {
6993
- heading: "cyan",
6994
- code: "gray",
6995
- link: "blue",
6996
- quote: "gray",
6997
- listBullet: "gray"
6998
- };
6999
- function renderTokens(tokens) {
7428
+ import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
7429
+ marked.setOptions({ gfm: true });
7430
+ function styleInlineNodes(nodes, keyPrefix, style) {
7431
+ return nodes.map(
7432
+ (n, j) => isValidElement(n) ? cloneElement(n, {
7433
+ key: `${keyPrefix}-${j}`,
7434
+ ...style
7435
+ }) : n
7436
+ );
7437
+ }
7438
+ function headingColor(depth) {
7439
+ if (depth <= 1) return BLUMA_TERMINAL.heading1;
7440
+ if (depth === 2) return BLUMA_TERMINAL.heading2;
7441
+ return BLUMA_TERMINAL.headingDeep;
7442
+ }
7443
+ function bulletGlyph(ordered, index, start, task, checked, depth) {
7444
+ if (task) return checked ? "[x]" : "[ ]";
7445
+ if (ordered) {
7446
+ const n = typeof start === "number" ? start + index : index + 1;
7447
+ return `${n}.`;
7448
+ }
7449
+ return depth === 0 ? "\xB7" : "\u2514";
7450
+ }
7451
+ function walkInline(tokens, keyBase, opts = {}) {
7452
+ if (!tokens?.length) return [];
7453
+ const out = [];
7454
+ tokens.forEach((t, i) => {
7455
+ const k = `${keyBase}-i${i}`;
7456
+ switch (t.type) {
7457
+ case "text":
7458
+ out.push(
7459
+ /* @__PURE__ */ jsx11(
7460
+ Text10,
7461
+ {
7462
+ bold: opts.bold,
7463
+ italic: opts.italic,
7464
+ strikethrough: opts.strike,
7465
+ underline: opts.link,
7466
+ color: opts.link ? BLUMA_TERMINAL.link : opts.bold ? BLUMA_TERMINAL.brandBlue : void 0,
7467
+ children: t.text
7468
+ },
7469
+ k
7470
+ )
7471
+ );
7472
+ break;
7473
+ case "strong":
7474
+ out.push(...walkInline(t.tokens, k, { ...opts, bold: true }));
7475
+ break;
7476
+ case "em":
7477
+ out.push(...walkInline(t.tokens, k, { ...opts, italic: true }));
7478
+ break;
7479
+ case "codespan":
7480
+ out.push(
7481
+ /* @__PURE__ */ jsx11(
7482
+ Text10,
7483
+ {
7484
+ color: opts.link ? BLUMA_TERMINAL.link : BLUMA_TERMINAL.code,
7485
+ backgroundColor: opts.link ? void 0 : "black",
7486
+ underline: opts.link,
7487
+ children: opts.link ? t.text : `\xA0${t.text}\xA0`
7488
+ },
7489
+ k
7490
+ )
7491
+ );
7492
+ break;
7493
+ case "link": {
7494
+ const L = t;
7495
+ const inner = walkInline(L.tokens, k + "l", { ...opts, link: true });
7496
+ out.push(
7497
+ /* @__PURE__ */ jsx11(Box11, { flexDirection: "row", flexWrap: "wrap", children: inner.length > 0 ? inner : /* @__PURE__ */ jsx11(Text10, { color: BLUMA_TERMINAL.link, underline: true, children: L.text }) }, k)
7498
+ );
7499
+ break;
7500
+ }
7501
+ case "br":
7502
+ out.push(
7503
+ /* @__PURE__ */ jsx11(Text10, { dimColor: true, children: "\n" }, k)
7504
+ );
7505
+ break;
7506
+ case "escape":
7507
+ out.push(
7508
+ /* @__PURE__ */ jsx11(
7509
+ Text10,
7510
+ {
7511
+ bold: opts.bold,
7512
+ italic: opts.italic,
7513
+ underline: opts.link,
7514
+ color: opts.link ? BLUMA_TERMINAL.link : void 0,
7515
+ children: t.text
7516
+ },
7517
+ k
7518
+ )
7519
+ );
7520
+ break;
7521
+ case "del":
7522
+ out.push(...walkInline(t.tokens, k, { ...opts, strike: true }));
7523
+ break;
7524
+ case "image": {
7525
+ const Im = t;
7526
+ out.push(
7527
+ /* @__PURE__ */ jsxs10(Text10, { dimColor: true, italic: true, children: [
7528
+ "[",
7529
+ Im.text || "img",
7530
+ "]"
7531
+ ] }, k)
7532
+ );
7533
+ break;
7534
+ }
7535
+ default:
7536
+ if ("text" in t && typeof t.text === "string") {
7537
+ out.push(
7538
+ /* @__PURE__ */ jsx11(Text10, { bold: opts.bold, italic: opts.italic, children: t.text }, k)
7539
+ );
7540
+ }
7541
+ }
7542
+ });
7543
+ return out;
7544
+ }
7545
+ function renderParagraph(p, key) {
7546
+ const nodes = walkInline(p.tokens, key);
7547
+ return /* @__PURE__ */ jsx11(Box11, { marginBottom: 1, flexDirection: "row", flexWrap: "wrap", children: nodes.length > 0 ? nodes : /* @__PURE__ */ jsx11(Text10, { dimColor: true, children: p.text }) }, key);
7548
+ }
7549
+ function renderListItemBlocks(item, depth, keyBase) {
7550
+ if (!item.tokens?.length) {
7551
+ const nodes = walkInline(
7552
+ [{ type: "text", raw: item.text, text: item.text }],
7553
+ keyBase
7554
+ );
7555
+ return nodes.length ? [/* @__PURE__ */ jsx11(Box11, { flexDirection: "row", flexWrap: "wrap", children: nodes }, keyBase)] : [];
7556
+ }
7557
+ const blocks = [];
7558
+ item.tokens.forEach((sub, si) => {
7559
+ const sk = `${keyBase}-b${si}`;
7560
+ if (sub.type === "paragraph") {
7561
+ blocks.push(renderParagraph(sub, sk));
7562
+ } else if (sub.type === "list") {
7563
+ blocks.push(renderListBlock(sub, depth + 1, sk));
7564
+ } else if (sub.type === "text") {
7565
+ const n = walkInline([sub], sk);
7566
+ if (n.length) blocks.push(/* @__PURE__ */ jsx11(Box11, { flexDirection: "row", flexWrap: "wrap", children: n }, sk));
7567
+ }
7568
+ });
7569
+ return blocks;
7570
+ }
7571
+ function renderListBlock(list, depth, keyBase) {
7572
+ return /* @__PURE__ */ jsx11(Box11, { flexDirection: "column", marginBottom: 1, paddingLeft: depth > 0 ? 2 : 0, children: list.items.map((item, idx) => {
7573
+ const glyph = bulletGlyph(
7574
+ list.ordered,
7575
+ idx,
7576
+ list.start,
7577
+ item.task,
7578
+ item.checked,
7579
+ depth
7580
+ );
7581
+ const gColor = depth === 0 ? BLUMA_TERMINAL.listBullet : BLUMA_TERMINAL.listBulletSub;
7582
+ const body = renderListItemBlocks(item, depth, `${keyBase}-it${idx}`);
7583
+ return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "row", alignItems: "flex-start", marginBottom: 0, children: [
7584
+ /* @__PURE__ */ jsx11(Text10, { color: gColor, children: (item.task ? glyph : `${glyph} `).padEnd(item.task ? 4 : 3, " ") }),
7585
+ /* @__PURE__ */ jsx11(Box11, { flexDirection: "column", flexGrow: 1, children: body.length > 0 ? body : /* @__PURE__ */ jsx11(Text10, { dimColor: true, children: item.text }) })
7586
+ ] }, `${keyBase}-row-${idx}`);
7587
+ }) }, keyBase);
7588
+ }
7589
+ function renderBlockquote(q, key) {
7590
+ const inner = q.tokens && q.tokens.length > 0 ? renderBlockTokens(q.tokens, `${key}-inner`) : /* @__PURE__ */ jsx11(Text10, { dimColor: true, italic: true, children: q.text });
7591
+ return /* @__PURE__ */ jsxs10(
7592
+ Box11,
7593
+ {
7594
+ flexDirection: "row",
7595
+ marginBottom: 1,
7596
+ borderStyle: "single",
7597
+ borderColor: BLUMA_TERMINAL.brandMagenta,
7598
+ paddingLeft: 1,
7599
+ paddingY: 0,
7600
+ children: [
7601
+ /* @__PURE__ */ jsx11(Text10, { color: BLUMA_TERMINAL.brandMagenta, children: "\u2502 " }),
7602
+ /* @__PURE__ */ jsx11(Box11, { flexDirection: "column", flexGrow: 1, children: inner })
7603
+ ]
7604
+ },
7605
+ key
7606
+ );
7607
+ }
7608
+ function renderBlockTokens(tokens, keyRoot) {
7000
7609
  const elements = [];
7001
7610
  for (let i = 0; i < tokens.length; i++) {
7002
7611
  const token = tokens[i];
7003
- const key = `token-${i}`;
7612
+ const key = `${keyRoot}-${i}`;
7004
7613
  switch (token.type) {
7005
7614
  case "heading": {
7006
- const heading = token;
7007
- const prefix = "#".repeat(heading.depth);
7615
+ const h = token;
7616
+ const color = headingColor(h.depth);
7617
+ const prefix = h.depth <= 2 ? `${"#".repeat(h.depth)} ` : "";
7618
+ const inner = walkInline(h.tokens, `${key}-h`);
7008
7619
  elements.push(
7009
- /* @__PURE__ */ jsx10(Box10, { marginBottom: 1, children: /* @__PURE__ */ jsxs9(Text9, { color: COLORS.heading, bold: true, children: [
7010
- prefix,
7011
- " ",
7012
- heading.text
7013
- ] }) }, key)
7620
+ /* @__PURE__ */ jsxs10(Box11, { marginBottom: 1, flexDirection: "row", flexWrap: "wrap", alignItems: "flex-start", children: [
7621
+ /* @__PURE__ */ jsx11(Text10, { color, bold: true, children: prefix }),
7622
+ inner.length > 0 ? inner : /* @__PURE__ */ jsx11(Text10, { color, bold: true, children: h.text })
7623
+ ] }, key)
7014
7624
  );
7015
7625
  break;
7016
7626
  }
7017
- case "paragraph": {
7018
- const para = token;
7019
- elements.push(
7020
- /* @__PURE__ */ jsx10(Box10, { marginBottom: 1, children: renderInline(para.text) }, key)
7021
- );
7627
+ case "paragraph":
7628
+ elements.push(renderParagraph(token, key));
7022
7629
  break;
7023
- }
7024
- case "list": {
7025
- const list = token;
7026
- list.items.forEach((item, idx) => {
7027
- const bullet = list.ordered ? `${idx + 1}.` : "\u2022";
7028
- const text = item.tokens?.filter((t) => t.type === "text").map((t) => t.text).join("") || item.text;
7029
- elements.push(
7030
- /* @__PURE__ */ jsxs9(Box10, { paddingLeft: 1, children: [
7031
- /* @__PURE__ */ jsx10(Text9, { color: COLORS.listBullet, children: bullet }),
7032
- /* @__PURE__ */ jsxs9(Text9, { children: [
7033
- " ",
7034
- renderInline(text)
7035
- ] })
7036
- ] }, `${key}-item-${idx}`)
7037
- );
7038
- });
7039
- elements.push(/* @__PURE__ */ jsx10(Box10, { marginBottom: 1 }, `${key}-spacer`));
7630
+ case "list":
7631
+ elements.push(renderListBlock(token, 0, key));
7040
7632
  break;
7041
- }
7042
7633
  case "code": {
7043
7634
  const code = token;
7044
7635
  let highlighted;
@@ -7051,110 +7642,144 @@ function renderTokens(tokens) {
7051
7642
  highlighted = code.text;
7052
7643
  }
7053
7644
  elements.push(
7054
- /* @__PURE__ */ jsxs9(Box10, { flexDirection: "column", marginBottom: 1, children: [
7055
- code.lang && /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: code.lang }),
7056
- /* @__PURE__ */ jsx10(Box10, { paddingLeft: 1, children: /* @__PURE__ */ jsx10(Text9, { children: highlighted }) })
7057
- ] }, key)
7645
+ /* @__PURE__ */ jsxs10(
7646
+ Box11,
7647
+ {
7648
+ flexDirection: "column",
7649
+ marginBottom: 1,
7650
+ borderStyle: "single",
7651
+ borderColor: BLUMA_TERMINAL.panelBorder,
7652
+ paddingX: 1,
7653
+ children: [
7654
+ code.lang ? /* @__PURE__ */ jsx11(Text10, { color: BLUMA_TERMINAL.codeLabel, dimColor: true, children: code.lang }) : null,
7655
+ /* @__PURE__ */ jsx11(Text10, { dimColor: true, children: highlighted })
7656
+ ]
7657
+ },
7658
+ key
7659
+ )
7058
7660
  );
7059
7661
  break;
7060
7662
  }
7061
- case "blockquote": {
7062
- const quote = token;
7063
- elements.push(
7064
- /* @__PURE__ */ jsxs9(Box10, { marginBottom: 1, children: [
7065
- /* @__PURE__ */ jsx10(Text9, { color: COLORS.quote, children: "\u2502 " }),
7066
- /* @__PURE__ */ jsx10(Text9, { dimColor: true, italic: true, children: quote.text })
7067
- ] }, key)
7068
- );
7663
+ case "blockquote":
7664
+ elements.push(renderBlockquote(token, key));
7069
7665
  break;
7070
- }
7071
7666
  case "table": {
7072
7667
  const table = token;
7073
7668
  elements.push(
7074
- /* @__PURE__ */ jsx10(Box10, { flexDirection: "row", children: table.header.map((cell, idx) => /* @__PURE__ */ jsx10(Box10, { paddingRight: 2, children: /* @__PURE__ */ jsx10(Text9, { bold: true, children: cell.text }) }, idx)) }, `${key}-header`)
7669
+ /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", marginBottom: 1, children: [
7670
+ /* @__PURE__ */ jsx11(Box11, { flexDirection: "row", children: table.header.map((cell, idx) => {
7671
+ const headerNodes = walkInline(cell.tokens, `${key}-h${idx}`);
7672
+ const styled = headerNodes.length > 0 ? styleInlineNodes(headerNodes, `${key}-h${idx}`, { bold: true, color: BLUMA_TERMINAL.brandBlue }) : [/* @__PURE__ */ jsx11(Text10, { bold: true, color: BLUMA_TERMINAL.brandBlue, children: cell.text }, `${key}-h${idx}-t`)];
7673
+ return /* @__PURE__ */ jsx11(Box11, { paddingRight: 2, flexDirection: "row", flexWrap: "wrap", children: styled }, idx);
7674
+ }) }),
7675
+ table.rows.map((row, rowIdx) => /* @__PURE__ */ jsx11(Box11, { flexDirection: "row", children: row.map((cell, cellIdx) => {
7676
+ const cellNodes = walkInline(cell.tokens, `${key}-c${rowIdx}-${cellIdx}`);
7677
+ const styled = cellNodes.length > 0 ? styleInlineNodes(cellNodes, `${key}-c${rowIdx}-${cellIdx}`, { dimColor: true }) : [/* @__PURE__ */ jsx11(Text10, { dimColor: true, children: cell.text }, `${key}-c${rowIdx}-${cellIdx}-t`)];
7678
+ return /* @__PURE__ */ jsx11(Box11, { paddingRight: 2, flexDirection: "row", flexWrap: "wrap", children: styled }, cellIdx);
7679
+ }) }, `${key}-r${rowIdx}`))
7680
+ ] }, key)
7075
7681
  );
7076
- table.rows.forEach((row, rowIdx) => {
7077
- elements.push(
7078
- /* @__PURE__ */ jsx10(Box10, { flexDirection: "row", children: row.map((cell, cellIdx) => /* @__PURE__ */ jsx10(Box10, { paddingRight: 2, children: /* @__PURE__ */ jsx10(Text9, { children: cell.text }) }, cellIdx)) }, `${key}-row-${rowIdx}`)
7079
- );
7080
- });
7081
- elements.push(/* @__PURE__ */ jsx10(Box10, { marginBottom: 1 }, `${key}-spacer`));
7082
7682
  break;
7083
7683
  }
7084
- case "hr": {
7684
+ case "hr":
7085
7685
  elements.push(
7086
- /* @__PURE__ */ jsx10(Box10, { marginBottom: 1, children: /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: "\u2500".repeat(40) }) }, key)
7686
+ /* @__PURE__ */ jsx11(Box11, { marginY: 1, children: /* @__PURE__ */ jsx11(Text10, { dimColor: true, children: "\u2500".repeat(42) }) }, key)
7087
7687
  );
7088
7688
  break;
7089
- }
7090
- case "space": {
7689
+ case "space":
7091
7690
  break;
7092
- }
7093
- default: {
7691
+ default:
7094
7692
  if ("text" in token && typeof token.text === "string") {
7095
- elements.push(/* @__PURE__ */ jsx10(Text9, { children: token.text }, key));
7693
+ elements.push(
7694
+ /* @__PURE__ */ jsx11(Text10, { dimColor: true, children: token.text }, key)
7695
+ );
7096
7696
  }
7097
- }
7098
7697
  }
7099
7698
  }
7100
7699
  return elements;
7101
7700
  }
7102
- function renderInline(text) {
7103
- const parts = [];
7104
- let remaining = text;
7105
- let partIndex = 0;
7106
- const inlineRegex = /(\*\*(.+?)\*\*|\*(.+?)\*|`(.+?)`|\[(.+?)\]\((.+?)\))/;
7107
- while (remaining.length > 0) {
7108
- const match = remaining.match(inlineRegex);
7109
- if (!match) {
7110
- parts.push(/* @__PURE__ */ jsx10(Text9, { children: remaining }, `inline-${partIndex++}`));
7111
- break;
7112
- }
7113
- const matchIndex = match.index ?? 0;
7114
- if (matchIndex > 0) {
7115
- parts.push(
7116
- /* @__PURE__ */ jsx10(Text9, { children: remaining.slice(0, matchIndex) }, `inline-${partIndex++}`)
7117
- );
7118
- }
7119
- const fullMatch = match[0];
7120
- if (fullMatch.startsWith("**")) {
7121
- parts.push(
7122
- /* @__PURE__ */ jsx10(Text9, { bold: true, children: match[2] }, `inline-${partIndex++}`)
7123
- );
7124
- } else if (fullMatch.startsWith("*")) {
7125
- parts.push(
7126
- /* @__PURE__ */ jsx10(Text9, { italic: true, children: match[3] }, `inline-${partIndex++}`)
7127
- );
7128
- } else if (fullMatch.startsWith("`")) {
7129
- parts.push(
7130
- /* @__PURE__ */ jsxs9(Text9, { color: COLORS.code, children: [
7131
- "`",
7132
- match[4],
7133
- "`"
7134
- ] }, `inline-${partIndex++}`)
7135
- );
7136
- } else if (fullMatch.startsWith("[")) {
7137
- parts.push(
7138
- /* @__PURE__ */ jsx10(Text9, { color: COLORS.link, underline: true, children: match[5] }, `inline-${partIndex++}`)
7139
- );
7140
- }
7141
- remaining = remaining.slice(matchIndex + fullMatch.length);
7142
- }
7143
- return parts.length === 1 ? parts[0] : /* @__PURE__ */ jsx10(Fragment3, { children: parts });
7144
- }
7145
7701
  var MarkdownRendererComponent = ({ markdown }) => {
7146
7702
  if (!markdown || markdown.trim() === "") {
7147
7703
  return null;
7148
7704
  }
7149
7705
  const tokens = marked.lexer(markdown);
7150
- return /* @__PURE__ */ jsx10(Box10, { flexDirection: "column", children: renderTokens(tokens) });
7706
+ return /* @__PURE__ */ jsx11(Box11, { flexDirection: "column", paddingRight: 1, children: renderBlockTokens(tokens, "md") });
7151
7707
  };
7152
7708
  var MarkdownRenderer = memo7(MarkdownRendererComponent);
7153
7709
 
7710
+ // src/app/ui/utils/expandablePreviewStore.ts
7711
+ var latest = null;
7712
+ function parseResult(result) {
7713
+ try {
7714
+ return JSON.parse(result);
7715
+ } catch {
7716
+ return null;
7717
+ }
7718
+ }
7719
+ function truncatedLineCount(text, maxVisible) {
7720
+ const allLines = text.split("\n").filter((l) => l.trim());
7721
+ if (allLines.length <= maxVisible) {
7722
+ return { truncated: 0, totalKept: allLines.length };
7723
+ }
7724
+ return { truncated: allLines.length - maxVisible, totalKept: maxVisible };
7725
+ }
7726
+ function refreshExpandableFromToolResult(toolName, result) {
7727
+ if (toolName.includes("task_boundary")) {
7728
+ return;
7729
+ }
7730
+ const parsed = parseResult(result);
7731
+ if (toolName.includes("read_file") && parsed && typeof parsed.content === "string") {
7732
+ const { truncated } = truncatedLineCount(parsed.content, TOOL_PREVIEW_MAX_LINES);
7733
+ if (truncated > 0) {
7734
+ const fp = typeof parsed.filepath === "string" ? parsed.filepath : "file";
7735
+ const sl = parsed.start_line;
7736
+ const el = parsed.end_line;
7737
+ const range = typeof sl === "number" && typeof el === "number" ? ` :${sl}-${el}` : "";
7738
+ latest = {
7739
+ fullText: parsed.content,
7740
+ title: `${fp}${range}`,
7741
+ toolName,
7742
+ linesHidden: truncated
7743
+ };
7744
+ } else {
7745
+ latest = null;
7746
+ }
7747
+ return;
7748
+ }
7749
+ if ((toolName.includes("shell_command") || toolName.includes("run_command") || toolName.includes("command_status")) && parsed) {
7750
+ const output = String(parsed.stdout || parsed.output || "");
7751
+ const stderr = String(parsed.stderr || "");
7752
+ const status = String(parsed.status || "");
7753
+ if (parsed.command_id && !output && !stderr && !status) {
7754
+ return;
7755
+ }
7756
+ if (status === "running") {
7757
+ return;
7758
+ }
7759
+ const blob = output || stderr;
7760
+ if (!blob) {
7761
+ return;
7762
+ }
7763
+ const { truncated } = truncatedLineCount(blob, TOOL_PREVIEW_MAX_LINES);
7764
+ if (truncated > 0) {
7765
+ latest = {
7766
+ fullText: blob,
7767
+ title: "shell output",
7768
+ toolName,
7769
+ linesHidden: truncated
7770
+ };
7771
+ } else {
7772
+ latest = null;
7773
+ }
7774
+ }
7775
+ }
7776
+ function peekLatestExpandable() {
7777
+ return latest;
7778
+ }
7779
+
7154
7780
  // src/app/ui/components/ToolResultDisplay.tsx
7155
- import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
7156
- var MAX_LINES = 8;
7157
- var parseResult = (result) => {
7781
+ import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
7782
+ var parseResult2 = (result) => {
7158
7783
  try {
7159
7784
  return JSON.parse(result);
7160
7785
  } catch {
@@ -7171,153 +7796,222 @@ var truncateLines = (text, max) => {
7171
7796
  truncated: allLines.length - max
7172
7797
  };
7173
7798
  };
7799
+ function TodoTaskLine({
7800
+ done,
7801
+ label
7802
+ }) {
7803
+ return /* @__PURE__ */ jsxs11(Box12, { flexDirection: "row", children: [
7804
+ /* @__PURE__ */ jsx12(Text11, { color: done ? BLUMA_TERMINAL.brandBlue : BLUMA_TERMINAL.brandMagenta, children: done ? "\u25A0 " : "\u25A1 " }),
7805
+ /* @__PURE__ */ jsx12(Text11, { dimColor: done, strikethrough: done, color: done ? BLUMA_TERMINAL.muted : void 0, children: label })
7806
+ ] });
7807
+ }
7174
7808
  var ToolResultDisplayComponent = ({ toolName, result }) => {
7809
+ useEffect5(() => {
7810
+ refreshExpandableFromToolResult(toolName, result);
7811
+ }, [toolName, result]);
7175
7812
  if (toolName.includes("task_boundary")) {
7176
7813
  return null;
7177
7814
  }
7178
- const parsed = parseResult(result);
7815
+ const parsed = parseResult2(result);
7179
7816
  if (toolName.includes("message")) {
7180
- const body = parsed?.content?.body || parsed?.body || parsed?.message;
7817
+ const body = parsed?.content?.body ?? parsed?.body ?? parsed?.message;
7181
7818
  if (!body) return null;
7182
- return /* @__PURE__ */ jsx11(Box11, { paddingX: 1, marginBottom: 1, flexDirection: "column", children: /* @__PURE__ */ jsx11(MarkdownRenderer, { markdown: body }) });
7183
- }
7184
- if (toolName.includes("read_file") && parsed?.content) {
7185
- const { lines, truncated } = truncateLines(parsed.content, MAX_LINES);
7186
- return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", paddingLeft: 3, children: [
7187
- lines.map((line, i) => /* @__PURE__ */ jsx11(Text10, { dimColor: true, color: "gray", children: line.slice(0, 80) }, i)),
7188
- truncated > 0 && /* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
7189
- "... +",
7819
+ return /* @__PURE__ */ jsx12(ChatBlock, { marginBottom: 1, children: /* @__PURE__ */ jsx12(Box12, { paddingLeft: 2, flexDirection: "column", children: /* @__PURE__ */ jsx12(MarkdownRenderer, { markdown: String(body) }) }) });
7820
+ }
7821
+ if (toolName.includes("read_file") && parsed && typeof parsed.content === "string") {
7822
+ const { lines, truncated } = truncateLines(parsed.content, TOOL_PREVIEW_MAX_LINES);
7823
+ return /* @__PURE__ */ jsx12(ChatBlock, { marginBottom: 1, children: /* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", paddingLeft: 2, children: [
7824
+ lines.map((line, i) => /* @__PURE__ */ jsx12(Text11, { dimColor: true, children: line.slice(0, 120) }, i)),
7825
+ truncated > 0 ? /* @__PURE__ */ jsxs11(Text11, { dimColor: true, children: [
7826
+ "\u2026 +",
7190
7827
  truncated,
7191
- " lines"
7192
- ] })
7193
- ] });
7828
+ " lines \xB7 Ctrl+O expand"
7829
+ ] }) : null
7830
+ ] }) });
7194
7831
  }
7195
7832
  if ((toolName.includes("shell_command") || toolName.includes("run_command") || toolName.includes("command_status")) && parsed) {
7196
- const output = parsed.stdout || parsed.output || "";
7197
- const stderr = parsed.stderr || "";
7833
+ const output = String(parsed.stdout || parsed.output || "");
7834
+ const stderr = String(parsed.stderr || "");
7198
7835
  const exitCode = parsed.exit_code ?? parsed.exitCode ?? 0;
7199
- const status = parsed.status || "";
7836
+ const status = String(parsed.status || "");
7200
7837
  if (parsed.command_id && !output && !stderr && !status) {
7201
- return /* @__PURE__ */ jsx11(Box11, { paddingLeft: 3, children: /* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
7838
+ return /* @__PURE__ */ jsx12(ChatBlock, { marginBottom: 1, children: /* @__PURE__ */ jsx12(Box12, { paddingLeft: 2, children: /* @__PURE__ */ jsxs11(Text11, { dimColor: true, children: [
7202
7839
  "started #",
7203
- parsed.command_id.slice(0, 8)
7204
- ] }) });
7840
+ String(parsed.command_id).slice(0, 8)
7841
+ ] }) }) });
7205
7842
  }
7206
7843
  if (status === "running") {
7207
- return /* @__PURE__ */ jsx11(Box11, { paddingLeft: 3, children: /* @__PURE__ */ jsx11(Text10, { dimColor: true, color: "yellow", children: "still running..." }) });
7844
+ return /* @__PURE__ */ jsx12(ChatBlock, { marginBottom: 1, children: /* @__PURE__ */ jsx12(Box12, { paddingLeft: 2, children: /* @__PURE__ */ jsx12(Text11, { color: BLUMA_TERMINAL.warn, dimColor: true, children: "still running\u2026" }) }) });
7208
7845
  }
7209
7846
  if (!output && !stderr) return null;
7210
- const { lines, truncated } = truncateLines(output || stderr, MAX_LINES);
7847
+ const { lines, truncated } = truncateLines(output || stderr, TOOL_PREVIEW_MAX_LINES);
7211
7848
  const isError = exitCode !== 0;
7212
7849
  const isSuccess = exitCode === 0 && status !== "running";
7213
- const textColor = isError ? "red" : isSuccess ? "green" : "gray";
7214
- return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", paddingLeft: 3, children: [
7215
- lines.map((line, i) => /* @__PURE__ */ jsx11(Text10, { dimColor: true, color: textColor, children: line.slice(0, 80) }, i)),
7216
- truncated > 0 && /* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
7217
- "... +",
7850
+ const lineColor = isError ? BLUMA_TERMINAL.err : isSuccess ? BLUMA_TERMINAL.success : BLUMA_TERMINAL.muted;
7851
+ return /* @__PURE__ */ jsx12(ChatBlock, { marginBottom: 1, children: /* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", paddingLeft: 2, children: [
7852
+ lines.map((line, i) => /* @__PURE__ */ jsx12(Text11, { dimColor: true, color: lineColor, children: line.slice(0, 120) }, i)),
7853
+ truncated > 0 ? /* @__PURE__ */ jsxs11(Text11, { dimColor: true, children: [
7854
+ "\u2026 +",
7218
7855
  truncated,
7219
- " lines"
7220
- ] }),
7221
- exitCode !== 0 && /* @__PURE__ */ jsxs10(Text10, { dimColor: true, color: "red", children: [
7222
- "exit code: ",
7856
+ " lines \xB7 Ctrl+O expand"
7857
+ ] }) : null,
7858
+ exitCode !== 0 ? /* @__PURE__ */ jsxs11(Text11, { dimColor: true, color: BLUMA_TERMINAL.err, children: [
7859
+ "exit ",
7223
7860
  exitCode
7224
- ] })
7225
- ] });
7861
+ ] }) : null
7862
+ ] }) });
7226
7863
  }
7227
7864
  if ((toolName.includes("grep") || toolName.includes("find_by_name")) && parsed) {
7228
7865
  const matches = parsed.matches || parsed.results || parsed.files || [];
7229
7866
  if (matches.length === 0) {
7230
- return /* @__PURE__ */ jsx11(Box11, { paddingLeft: 3, children: /* @__PURE__ */ jsx11(Text10, { dimColor: true, children: "no matches" }) });
7867
+ return /* @__PURE__ */ jsx12(ChatBlock, { marginBottom: 1, children: /* @__PURE__ */ jsx12(Box12, { paddingLeft: 2, children: /* @__PURE__ */ jsx12(Text11, { dimColor: true, children: "no matches" }) }) });
7231
7868
  }
7232
- return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", paddingLeft: 3, children: [
7233
- /* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
7869
+ return /* @__PURE__ */ jsx12(ChatBlock, { marginBottom: 1, children: /* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", paddingLeft: 2, children: [
7870
+ /* @__PURE__ */ jsxs11(Text11, { dimColor: true, children: [
7234
7871
  matches.length,
7235
7872
  " matches"
7236
7873
  ] }),
7237
- matches.slice(0, 5).map((m, i) => /* @__PURE__ */ jsxs10(Text10, { dimColor: true, color: "gray", children: [
7238
- m.file || m.path || m.name || m,
7239
- m.line ? `:${m.line}` : ""
7240
- ] }, i)),
7241
- matches.length > 5 && /* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
7242
- "... +",
7243
- matches.length - 5,
7244
- " more"
7245
- ] })
7246
- ] });
7874
+ matches.slice(0, 5).map((m, i) => {
7875
+ const row = m;
7876
+ const path19 = row.file || row.path || row.name || m;
7877
+ const line = row.line;
7878
+ return /* @__PURE__ */ jsxs11(Text11, { dimColor: true, children: [
7879
+ String(path19),
7880
+ line != null ? `:${line}` : ""
7881
+ ] }, i);
7882
+ }),
7883
+ matches.length > 5 ? /* @__PURE__ */ jsxs11(Text11, { dimColor: true, children: [
7884
+ "\u2026 +",
7885
+ matches.length - 5
7886
+ ] }) : null
7887
+ ] }) });
7247
7888
  }
7248
7889
  if (toolName.includes("ls_tool") && parsed) {
7249
7890
  const entries = parsed.entries || parsed.files || [];
7250
7891
  if (entries.length === 0) return null;
7251
- return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", paddingLeft: 3, children: [
7252
- entries.slice(0, 6).map((e, i) => /* @__PURE__ */ jsxs10(Text10, { dimColor: true, color: e.isDirectory ? "blue" : "gray", children: [
7253
- e.isDirectory ? "d" : "f",
7254
- " ",
7255
- e.name || e
7256
- ] }, i)),
7257
- entries.length > 6 && /* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
7258
- "... +",
7259
- entries.length - 6,
7260
- " more"
7261
- ] })
7262
- ] });
7892
+ return /* @__PURE__ */ jsx12(ChatBlock, { marginBottom: 1, children: /* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", paddingLeft: 2, children: [
7893
+ entries.slice(0, 6).map((e, i) => {
7894
+ const row = e;
7895
+ const name = row.name ?? e;
7896
+ const isDir = Boolean(row.isDirectory);
7897
+ return /* @__PURE__ */ jsxs11(Text11, { dimColor: true, color: isDir ? BLUMA_TERMINAL.brandBlue : BLUMA_TERMINAL.muted, children: [
7898
+ isDir ? "d " : "f ",
7899
+ String(name)
7900
+ ] }, i);
7901
+ }),
7902
+ entries.length > 6 ? /* @__PURE__ */ jsxs11(Text11, { dimColor: true, children: [
7903
+ "\u2026 +",
7904
+ entries.length - 6
7905
+ ] }) : null
7906
+ ] }) });
7263
7907
  }
7264
7908
  if (toolName.includes("load_skill") && parsed) {
7265
7909
  if (!parsed.success) {
7266
- return /* @__PURE__ */ jsx11(Box11, { paddingLeft: 3, children: /* @__PURE__ */ jsxs10(Text10, { dimColor: true, color: "red", children: [
7267
- "\u2717 Skill not found: ",
7268
- parsed.message
7269
- ] }) });
7270
- }
7271
- return /* @__PURE__ */ jsxs10(Box11, { paddingLeft: 3, marginBottom: 2, children: [
7272
- /* @__PURE__ */ jsx11(Text10, { dimColor: true, color: "green", children: "Skill loaded: " }),
7273
- /* @__PURE__ */ jsx11(Text10, { color: "green", bold: true, children: parsed.skill_name }),
7274
- /* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
7275
- " \u2014 ",
7276
- parsed.description?.slice(0, 50),
7277
- parsed.description?.length > 100 ? "..." : ""
7910
+ return /* @__PURE__ */ jsx12(ChatBlock, { marginBottom: 1, children: /* @__PURE__ */ jsx12(Box12, { paddingLeft: 2, children: /* @__PURE__ */ jsxs11(Text11, { dimColor: true, color: BLUMA_TERMINAL.err, children: [
7911
+ "Not found: ",
7912
+ String(parsed.message || "")
7913
+ ] }) }) });
7914
+ }
7915
+ const desc = parsed.description ? String(parsed.description) : "";
7916
+ return /* @__PURE__ */ jsx12(ChatBlock, { marginBottom: 1, children: /* @__PURE__ */ jsxs11(Box12, { paddingLeft: 2, flexDirection: "row", flexWrap: "wrap", children: [
7917
+ /* @__PURE__ */ jsx12(Text11, { color: BLUMA_TERMINAL.brandMagenta, bold: true, children: String(parsed.skill_name || "") }),
7918
+ /* @__PURE__ */ jsxs11(Text11, { dimColor: true, children: [
7919
+ " ",
7920
+ "\u2014 ",
7921
+ desc.slice(0, 80),
7922
+ desc.length > 80 ? "\u2026" : ""
7278
7923
  ] })
7279
- ] });
7924
+ ] }) });
7280
7925
  }
7281
7926
  if (toolName.includes("todo")) {
7282
- return null;
7927
+ if (parsed && Array.isArray(parsed.tasks)) {
7928
+ const tasks = parsed.tasks;
7929
+ const stats = parsed.stats;
7930
+ const msg = typeof parsed.message === "string" ? parsed.message : "";
7931
+ return /* @__PURE__ */ jsx12(ChatBlock, { marginBottom: 1, children: /* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", paddingLeft: 2, children: [
7932
+ msg ? /* @__PURE__ */ jsx12(Box12, { marginBottom: 1, children: /* @__PURE__ */ jsx12(Text11, { dimColor: true, children: msg }) }) : null,
7933
+ stats && typeof stats.progress === "number" ? /* @__PURE__ */ jsxs11(Text11, { dimColor: true, children: [
7934
+ stats.completed ?? 0,
7935
+ "/",
7936
+ stats.total ?? tasks.length,
7937
+ " done \xB7 ",
7938
+ stats.progress,
7939
+ "%"
7940
+ ] }) : null,
7941
+ /* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", marginTop: 1, children: [
7942
+ tasks.slice(0, 12).map((t, i) => {
7943
+ const done = t.status === "completed" || t.isComplete === true;
7944
+ const line = `#${t.id ?? i + 1} ${t.description ?? ""}`;
7945
+ return /* @__PURE__ */ jsx12(TodoTaskLine, { done, label: line }, i);
7946
+ }),
7947
+ tasks.length > 12 ? /* @__PURE__ */ jsxs11(Text11, { dimColor: true, children: [
7948
+ "\u2026 +",
7949
+ tasks.length - 12,
7950
+ " tasks"
7951
+ ] }) : null
7952
+ ] })
7953
+ ] }) });
7954
+ }
7955
+ const lines = result.split("\n").filter(Boolean);
7956
+ if (lines.length === 0) return null;
7957
+ return /* @__PURE__ */ jsx12(ChatBlock, { marginBottom: 1, children: /* @__PURE__ */ jsx12(Box12, { flexDirection: "column", paddingLeft: 2, children: lines.slice(0, 14).map((line, i) => /* @__PURE__ */ jsx12(Text11, { dimColor: true, children: line.slice(0, 100) }, i)) }) });
7283
7958
  }
7284
7959
  if (toolName.includes("view_file_outline") && parsed) {
7285
7960
  const symbols = parsed.symbols || parsed.outline || [];
7286
7961
  if (symbols.length === 0) return null;
7287
- return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", paddingLeft: 3, children: [
7288
- symbols.slice(0, 5).map((s, i) => /* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
7289
- s.kind || "sym",
7290
- " ",
7291
- s.name
7292
- ] }, i)),
7293
- symbols.length > 5 && /* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
7294
- "... +",
7295
- symbols.length - 5,
7296
- " more"
7297
- ] })
7298
- ] });
7962
+ return /* @__PURE__ */ jsx12(ChatBlock, { marginBottom: 1, children: /* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", paddingLeft: 2, children: [
7963
+ symbols.slice(0, 5).map((s, i) => {
7964
+ const row = s;
7965
+ return /* @__PURE__ */ jsxs11(Text11, { dimColor: true, children: [
7966
+ String(row.kind || "sym"),
7967
+ " ",
7968
+ String(row.name || "")
7969
+ ] }, i);
7970
+ }),
7971
+ symbols.length > 5 ? /* @__PURE__ */ jsxs11(Text11, { dimColor: true, children: [
7972
+ "\u2026 +",
7973
+ symbols.length - 5
7974
+ ] }) : null
7975
+ ] }) });
7299
7976
  }
7300
7977
  return null;
7301
7978
  };
7302
7979
  var ToolResultDisplay = memo8(ToolResultDisplayComponent);
7303
7980
 
7981
+ // src/app/ui/SessionInfoConnectingMCP.tsx
7982
+ import { Box as Box13, Text as Text12 } from "ink";
7983
+ import Spinner from "ink-spinner";
7984
+ import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
7985
+ var SessionInfoConnectingMCP = ({
7986
+ sessionId,
7987
+ workdir,
7988
+ statusMessage
7989
+ }) => /* @__PURE__ */ jsx13(ChatBlock, { marginBottom: 1, children: /* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", paddingLeft: 1, children: [
7990
+ /* @__PURE__ */ jsxs12(Text12, { children: [
7991
+ /* @__PURE__ */ jsx13(Text12, { bold: true, color: BLUMA_TERMINAL.brandBlue, children: "session" }),
7992
+ /* @__PURE__ */ jsx13(Text12, { dimColor: true, children: " \xB7 " }),
7993
+ /* @__PURE__ */ jsx13(Text12, { color: BLUMA_TERMINAL.brandMagenta, children: sessionId.slice(0, 12) })
7994
+ ] }),
7995
+ /* @__PURE__ */ jsxs12(Text12, { dimColor: true, children: [
7996
+ /* @__PURE__ */ jsx13(Text12, { color: BLUMA_TERMINAL.brandMagenta, children: "\u2514 " }),
7997
+ workdir
7998
+ ] }),
7999
+ /* @__PURE__ */ jsxs12(Box13, { marginTop: 1, flexDirection: "row", flexWrap: "wrap", children: [
8000
+ /* @__PURE__ */ jsxs12(Text12, { color: BLUMA_TERMINAL.warn, children: [
8001
+ /* @__PURE__ */ jsx13(Spinner, { type: "dots" }),
8002
+ " "
8003
+ ] }),
8004
+ /* @__PURE__ */ jsx13(Text12, { dimColor: true, children: statusMessage || "Establishing MCP\u2026" })
8005
+ ] })
8006
+ ] }) });
8007
+ var SessionInfoConnectingMCP_default = SessionInfoConnectingMCP;
8008
+
7304
8009
  // src/app/ui/components/SlashCommands.tsx
7305
- import { Box as Box12, Text as Text11 } from "ink";
7306
- import { Fragment as Fragment4, jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
8010
+ import { Box as Box14, Text as Text13 } from "ink";
8011
+ import { Fragment as Fragment2, jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
7307
8012
  var SlashCommands = ({ input, setHistory, agentRef }) => {
7308
8013
  const [cmd, ...args] = input.slice(1).trim().split(/\s+/);
7309
- const outBox = (children) => /* @__PURE__ */ jsx12(
7310
- Box12,
7311
- {
7312
- borderStyle: "single",
7313
- borderColor: "gray",
7314
- paddingX: 2,
7315
- paddingY: 0,
7316
- marginBottom: 1,
7317
- flexDirection: "column",
7318
- children
7319
- }
7320
- );
8014
+ const outBox = (children) => /* @__PURE__ */ jsx14(ChatBlock, { marginBottom: 1, children: /* @__PURE__ */ jsx14(Box14, { paddingLeft: 1, flexDirection: "column", children }) });
7321
8015
  const render2 = () => {
7322
8016
  if (!cmd) {
7323
8017
  return null;
@@ -7325,11 +8019,11 @@ var SlashCommands = ({ input, setHistory, agentRef }) => {
7325
8019
  if (cmd === "help") {
7326
8020
  const cmds = getSlashCommands();
7327
8021
  return outBox(
7328
- /* @__PURE__ */ jsxs11(Fragment4, { children: [
7329
- /* @__PURE__ */ jsx12(Box12, { marginBottom: 1, children: /* @__PURE__ */ jsx12(Text11, { bold: true, color: "magenta", children: "Available Commands" }) }),
7330
- /* @__PURE__ */ jsx12(Box12, { flexDirection: "column", children: cmds.map((c, i) => /* @__PURE__ */ jsxs11(Box12, { children: [
7331
- /* @__PURE__ */ jsx12(Text11, { color: "cyan", children: c.name.padEnd(12) }),
7332
- /* @__PURE__ */ jsx12(Text11, { dimColor: true, children: c.description })
8022
+ /* @__PURE__ */ jsxs13(Fragment2, { children: [
8023
+ /* @__PURE__ */ jsx14(Box14, { marginBottom: 1, children: /* @__PURE__ */ jsx14(Text13, { bold: true, color: BLUMA_TERMINAL.brandMagenta, children: "Available Commands" }) }),
8024
+ /* @__PURE__ */ jsx14(Box14, { flexDirection: "column", children: cmds.map((c, i) => /* @__PURE__ */ jsxs13(Box14, { children: [
8025
+ /* @__PURE__ */ jsx14(Text13, { color: BLUMA_TERMINAL.brandBlue, children: c.name.padEnd(12) }),
8026
+ /* @__PURE__ */ jsx14(Text13, { dimColor: true, children: c.description })
7333
8027
  ] }, i)) })
7334
8028
  ] })
7335
8029
  );
@@ -7337,9 +8031,9 @@ var SlashCommands = ({ input, setHistory, agentRef }) => {
7337
8031
  if (cmd === "clear") {
7338
8032
  setHistory((prev) => prev.filter((item) => item.id === 0 || item.id === 1));
7339
8033
  return outBox(
7340
- /* @__PURE__ */ jsxs11(Box12, { children: [
7341
- /* @__PURE__ */ jsx12(Text11, { color: "green", children: "\u2713" }),
7342
- /* @__PURE__ */ jsx12(Text11, { dimColor: true, children: " History cleared" })
8034
+ /* @__PURE__ */ jsxs13(Box14, { children: [
8035
+ /* @__PURE__ */ jsx14(Text13, { color: "green", children: "[ok]" }),
8036
+ /* @__PURE__ */ jsx14(Text13, { dimColor: true, children: " History cleared" })
7343
8037
  ] })
7344
8038
  );
7345
8039
  }
@@ -7351,8 +8045,8 @@ var SlashCommands = ({ input, setHistory, agentRef }) => {
7351
8045
  setHistory((prev) => prev.concat({
7352
8046
  id: Date.now(),
7353
8047
  component: outBox(
7354
- /* @__PURE__ */ jsx12(Box12, { children: /* @__PURE__ */ jsxs11(Text11, { color: "red", children: [
7355
- "\u2716 Failed to execute /init: ",
8048
+ /* @__PURE__ */ jsx14(Box14, { children: /* @__PURE__ */ jsxs13(Text13, { color: "red", children: [
8049
+ "Failed to execute /init: ",
7356
8050
  e?.message || String(e)
7357
8051
  ] }) })
7358
8052
  )
@@ -7372,41 +8066,41 @@ var SlashCommands = ({ input, setHistory, agentRef }) => {
7372
8066
  const colType = 10;
7373
8067
  const colSource = 18;
7374
8068
  return outBox(
7375
- /* @__PURE__ */ jsxs11(Fragment4, { children: [
7376
- /* @__PURE__ */ jsxs11(Box12, { marginBottom: 1, children: [
7377
- /* @__PURE__ */ jsx12(Text11, { bold: true, color: "magenta", children: "MCP Tools" }),
7378
- /* @__PURE__ */ jsx12(Text11, { dimColor: true, children: " \u2022 " }),
7379
- /* @__PURE__ */ jsxs11(Text11, { dimColor: true, children: [
8069
+ /* @__PURE__ */ jsxs13(Fragment2, { children: [
8070
+ /* @__PURE__ */ jsxs13(Box14, { marginBottom: 1, children: [
8071
+ /* @__PURE__ */ jsx14(Text13, { bold: true, color: BLUMA_TERMINAL.brandMagenta, children: "MCP Tools" }),
8072
+ /* @__PURE__ */ jsx14(Text13, { dimColor: true, children: " \u2022 " }),
8073
+ /* @__PURE__ */ jsxs13(Text13, { dimColor: true, children: [
7380
8074
  tools.length,
7381
8075
  " total"
7382
8076
  ] }),
7383
- term && /* @__PURE__ */ jsxs11(Fragment4, { children: [
7384
- /* @__PURE__ */ jsx12(Text11, { dimColor: true, children: " \u2022 filter: " }),
7385
- /* @__PURE__ */ jsxs11(Text11, { color: "cyan", children: [
8077
+ term && /* @__PURE__ */ jsxs13(Fragment2, { children: [
8078
+ /* @__PURE__ */ jsx14(Text13, { dimColor: true, children: " \u2022 filter: " }),
8079
+ /* @__PURE__ */ jsxs13(Text13, { color: BLUMA_TERMINAL.brandBlue, children: [
7386
8080
  '"',
7387
8081
  term,
7388
8082
  '"'
7389
8083
  ] }),
7390
- /* @__PURE__ */ jsxs11(Text11, { dimColor: true, children: [
8084
+ /* @__PURE__ */ jsxs13(Text13, { dimColor: true, children: [
7391
8085
  " \u2022 showing: ",
7392
8086
  filtered.length
7393
8087
  ] })
7394
8088
  ] })
7395
8089
  ] }),
7396
- filtered.length === 0 ? /* @__PURE__ */ jsx12(Text11, { color: "yellow", children: "No MCP tools found" }) : /* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", children: [
7397
- /* @__PURE__ */ jsx12(Box12, { children: /* @__PURE__ */ jsxs11(Text11, { color: "gray", children: [
8090
+ filtered.length === 0 ? /* @__PURE__ */ jsx14(Text13, { color: "yellow", children: "No MCP tools found" }) : /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", children: [
8091
+ /* @__PURE__ */ jsx14(Box14, { children: /* @__PURE__ */ jsxs13(Text13, { color: "gray", children: [
7398
8092
  pad("Name", colName),
7399
8093
  " \u2502 ",
7400
8094
  pad("Type", colType),
7401
8095
  " \u2502 ",
7402
8096
  pad("Source", colSource)
7403
8097
  ] }) }),
7404
- /* @__PURE__ */ jsx12(Text11, { color: "gray", children: "\u2500".repeat(colName + colType + colSource + 6) }),
8098
+ /* @__PURE__ */ jsx14(Text13, { color: "gray", children: "\u2500".repeat(colName + colType + colSource + 6) }),
7405
8099
  filtered.map((t, i) => {
7406
8100
  const name = t.function?.name || t.name || "tool";
7407
8101
  const type = t.function?.name ? "fn" : t.type || "tool";
7408
8102
  const source = t.source || t.provider || "mcp";
7409
- return /* @__PURE__ */ jsxs11(Text11, { color: "white", children: [
8103
+ return /* @__PURE__ */ jsxs13(Text13, { color: "white", children: [
7410
8104
  pad(name, colName),
7411
8105
  " \u2502 ",
7412
8106
  pad(String(type), colType),
@@ -7429,22 +8123,22 @@ var SlashCommands = ({ input, setHistory, agentRef }) => {
7429
8123
  const colType = 10;
7430
8124
  const colSource = 18;
7431
8125
  return outBox(
7432
- /* @__PURE__ */ jsxs11(Fragment4, { children: [
7433
- /* @__PURE__ */ jsx12(Text11, { color: "magenta", bold: true, children: "Native Tools" }),
7434
- /* @__PURE__ */ jsxs11(Text11, { color: "gray", children: [
8126
+ /* @__PURE__ */ jsxs13(Fragment2, { children: [
8127
+ /* @__PURE__ */ jsx14(Text13, { color: BLUMA_TERMINAL.brandMagenta, bold: true, children: "Native Tools" }),
8128
+ /* @__PURE__ */ jsxs13(Text13, { color: "gray", children: [
7435
8129
  "Total Native: ",
7436
8130
  tools.length,
7437
8131
  term ? ` | Filter: "${term}" | Showing: ${filtered.length}` : ""
7438
8132
  ] }),
7439
- filtered.length === 0 ? /* @__PURE__ */ jsx12(Text11, { color: "yellow", children: "No native tools to display." }) : /* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", children: [
7440
- /* @__PURE__ */ jsxs11(Text11, { color: "gray", children: [
8133
+ filtered.length === 0 ? /* @__PURE__ */ jsx14(Text13, { color: "yellow", children: "No native tools to display." }) : /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", children: [
8134
+ /* @__PURE__ */ jsxs13(Text13, { color: "gray", children: [
7441
8135
  pad("Name", colName),
7442
8136
  " | ",
7443
8137
  pad("Type", colType),
7444
8138
  " | ",
7445
8139
  pad("Source", colSource)
7446
8140
  ] }),
7447
- /* @__PURE__ */ jsxs11(Text11, { color: "gray", children: [
8141
+ /* @__PURE__ */ jsxs13(Text13, { color: "gray", children: [
7448
8142
  "".padEnd(colName, "-"),
7449
8143
  "---",
7450
8144
  "".padEnd(colType, "-"),
@@ -7455,7 +8149,7 @@ var SlashCommands = ({ input, setHistory, agentRef }) => {
7455
8149
  const name = t.function?.name || t.name || "tool";
7456
8150
  const type = t.function?.name ? "fn" : t.type || "tool";
7457
8151
  const source = t.source || "native";
7458
- return /* @__PURE__ */ jsxs11(Text11, { color: "white", children: [
8152
+ return /* @__PURE__ */ jsxs13(Text13, { color: "white", children: [
7459
8153
  pad(name, colName),
7460
8154
  " | ",
7461
8155
  pad(String(type), colType),
@@ -7467,18 +8161,18 @@ var SlashCommands = ({ input, setHistory, agentRef }) => {
7467
8161
  ] })
7468
8162
  );
7469
8163
  }
7470
- return outBox(/* @__PURE__ */ jsxs11(Text11, { color: "red", children: [
8164
+ return outBox(/* @__PURE__ */ jsxs13(Text13, { color: "red", children: [
7471
8165
  "Command not recognized: /",
7472
8166
  cmd
7473
8167
  ] }));
7474
8168
  };
7475
- return /* @__PURE__ */ jsx12(Fragment4, { children: render2() });
8169
+ return /* @__PURE__ */ jsx14(Fragment2, { children: render2() });
7476
8170
  };
7477
8171
  var SlashCommands_default = SlashCommands;
7478
8172
 
7479
8173
  // src/app/agent/utils/update_check.ts
7480
8174
  import updateNotifier from "update-notifier";
7481
- import { fileURLToPath as fileURLToPath4 } from "url";
8175
+ import { fileURLToPath as fileURLToPath3 } from "url";
7482
8176
  import path18 from "path";
7483
8177
  import fs14 from "fs";
7484
8178
  var BLUMA_PACKAGE_NAME = "@nomad-e/bluma-cli";
@@ -7513,7 +8207,7 @@ async function checkForUpdates() {
7513
8207
  pkg = findBlumaPackageJson(path18.dirname(binPath));
7514
8208
  }
7515
8209
  if (!pkg) {
7516
- const __filename = fileURLToPath4(import.meta.url);
8210
+ const __filename = fileURLToPath3(import.meta.url);
7517
8211
  const __dirname2 = path18.dirname(__filename);
7518
8212
  pkg = findBlumaPackageJson(__dirname2);
7519
8213
  }
@@ -7542,86 +8236,88 @@ Run: npm i -g ${BLUMA_PACKAGE_NAME} to update.`;
7542
8236
  }
7543
8237
 
7544
8238
  // src/app/ui/components/UpdateNotice.tsx
7545
- import { Box as Box13, Text as Text12 } from "ink";
7546
- import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
8239
+ import { Box as Box15, Text as Text14 } from "ink";
8240
+ import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
7547
8241
  function parseUpdateMessage(msg) {
7548
8242
  const lines = msg.split(/\r?\n/).map((l) => l.trim());
7549
8243
  const first = lines[0] || "";
7550
8244
  const hintLine = lines.slice(1).join(" ") || "";
7551
8245
  const nameMatch = first.match(/Update available for\s+([^!]+)!/i);
7552
8246
  const versionMatch = first.match(/!\s*([^\s]+)\s*→\s*([^\s]+)/);
7553
- const name = nameMatch?.[1]?.trim();
7554
- const current = versionMatch?.[1]?.trim();
7555
- const latest = versionMatch?.[2]?.trim();
7556
8247
  return {
7557
- name,
7558
- current,
7559
- latest,
8248
+ name: nameMatch?.[1]?.trim(),
8249
+ current: versionMatch?.[1]?.trim(),
8250
+ latest: versionMatch?.[2]?.trim(),
7560
8251
  hint: hintLine || void 0
7561
8252
  };
7562
8253
  }
7563
8254
  var UpdateNotice = ({ message: message2 }) => {
7564
- const { name, current, latest, hint } = parseUpdateMessage(message2);
7565
- return /* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", marginBottom: 1, children: [
7566
- /* @__PURE__ */ jsx13(Text12, { color: "yellow", bold: true, children: "Update Available" }),
7567
- name && current && latest ? /* @__PURE__ */ jsx13(Text12, { color: "gray", children: `${name}: ${current} \u2192 ${latest}` }) : /* @__PURE__ */ jsx13(Text12, { color: "gray", children: message2 }),
7568
- hint ? /* @__PURE__ */ jsx13(Text12, { color: "gray", children: hint }) : null
7569
- ] });
8255
+ const { name, current, latest: latest2, hint } = parseUpdateMessage(message2);
8256
+ return /* @__PURE__ */ jsx15(ChatBlock, { marginBottom: 1, children: /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", paddingLeft: 2, children: [
8257
+ name && current && latest2 ? /* @__PURE__ */ jsx15(Text14, { color: BLUMA_TERMINAL.brandMagenta, bold: true, children: name }) : null,
8258
+ name && current && latest2 ? /* @__PURE__ */ jsxs14(Text14, { dimColor: true, children: [
8259
+ current,
8260
+ " \u2192 ",
8261
+ latest2
8262
+ ] }) : /* @__PURE__ */ jsx15(Text14, { dimColor: true, children: message2 }),
8263
+ hint ? /* @__PURE__ */ jsx15(Box15, { marginTop: 1, children: /* @__PURE__ */ jsx15(Text14, { dimColor: true, children: hint }) }) : null
8264
+ ] }) });
7570
8265
  };
7571
8266
  var UpdateNotice_default = UpdateNotice;
7572
8267
 
7573
8268
  // src/app/ui/components/ErrorMessage.tsx
7574
- import { Box as Box14, Text as Text13 } from "ink";
7575
- import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
7576
- var ErrorMessage = ({ message: message2, details, hint }) => {
7577
- return /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", paddingX: 1, marginBottom: 1, children: [
7578
- /* @__PURE__ */ jsxs13(Box14, { children: [
7579
- /* @__PURE__ */ jsx14(Text13, { color: "red", children: "\u2717" }),
7580
- /* @__PURE__ */ jsx14(Text13, { color: "red", bold: true, children: " Error" })
7581
- ] }),
7582
- /* @__PURE__ */ jsx14(Box14, { paddingLeft: 2, children: /* @__PURE__ */ jsx14(Text13, { color: "red", children: message2 }) }),
7583
- details && /* @__PURE__ */ jsx14(Box14, { paddingLeft: 2, children: /* @__PURE__ */ jsx14(Text13, { dimColor: true, children: details }) }),
7584
- hint && /* @__PURE__ */ jsx14(Box14, { paddingLeft: 2, children: /* @__PURE__ */ jsxs13(Text13, { color: "gray", children: [
7585
- "hint: ",
7586
- hint
7587
- ] }) })
7588
- ] });
7589
- };
8269
+ import { Box as Box16, Text as Text15 } from "ink";
8270
+ import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
8271
+ var ErrorMessage = ({ message: message2, details, hint }) => /* @__PURE__ */ jsx16(ChatBlock, { marginBottom: 1, children: /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", paddingLeft: 2, children: [
8272
+ /* @__PURE__ */ jsx16(Text15, { color: BLUMA_TERMINAL.err, children: message2 }),
8273
+ details ? /* @__PURE__ */ jsx16(Box16, { marginTop: 1, children: /* @__PURE__ */ jsx16(Text15, { dimColor: true, children: details }) }) : null,
8274
+ hint ? /* @__PURE__ */ jsx16(Box16, { marginTop: 1, children: /* @__PURE__ */ jsxs15(Text15, { dimColor: true, children: [
8275
+ "hint: ",
8276
+ hint
8277
+ ] }) }) : null
8278
+ ] }) });
7590
8279
  var ErrorMessage_default = ErrorMessage;
7591
8280
 
7592
8281
  // src/app/ui/components/ReasoningDisplay.tsx
7593
- import { memo as memo9, useState as useState5 } from "react";
7594
- import { Box as Box15, Text as Text14 } from "ink";
7595
- import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
8282
+ import { memo as memo9 } from "react";
8283
+ import { Box as Box17, Text as Text16 } from "ink";
8284
+ import { jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
7596
8285
  var ReasoningDisplayComponent = ({
7597
8286
  reasoning,
7598
8287
  collapsed = false
7599
8288
  }) => {
7600
- const [isExpanded, setIsExpanded] = useState5(!collapsed);
7601
8289
  if (!reasoning || reasoning.trim() === "") {
7602
8290
  return null;
7603
8291
  }
7604
- const maxLines = 5;
8292
+ const maxLines = collapsed ? 6 : 200;
7605
8293
  const lines = reasoning.split("\n");
7606
- const displayText = isExpanded ? reasoning : lines.slice(0, maxLines).join("\n") + (lines.length > maxLines ? "..." : "");
7607
- return /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", paddingX: 1, marginBottom: 1, marginTop: 1, children: [
7608
- /* @__PURE__ */ jsx15(Box15, {}),
7609
- /* @__PURE__ */ jsx15(Box15, { paddingLeft: 2, flexDirection: "column", children: displayText.split("\n").map((line, i) => /* @__PURE__ */ jsx15(Text14, { color: "gray", dimColor: true, children: line }, i)) })
7610
- ] });
8294
+ const displayLines = lines.slice(0, maxLines);
8295
+ const truncated = lines.length > maxLines;
8296
+ return /* @__PURE__ */ jsx17(ChatBlock, { marginBottom: 1, children: /* @__PURE__ */ jsxs16(Box17, { flexDirection: "column", paddingLeft: 2, children: [
8297
+ displayLines.map((line, i) => /* @__PURE__ */ jsx17(Text16, { dimColor: true, children: line }, i)),
8298
+ truncated ? /* @__PURE__ */ jsxs16(Text16, { dimColor: true, children: [
8299
+ "\u2026 +",
8300
+ lines.length - maxLines,
8301
+ " lines"
8302
+ ] }) : null
8303
+ ] }) });
7611
8304
  };
7612
8305
  var ReasoningDisplay = memo9(ReasoningDisplayComponent);
7613
8306
 
7614
8307
  // src/app/ui/components/StreamingText.tsx
7615
- import { useState as useState6, useEffect as useEffect5, useRef as useRef4, memo as memo10 } from "react";
7616
- import { Box as Box16, Text as Text15 } from "ink";
7617
- import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
7618
- var StreamingTextComponent = ({ eventBus, onReasoningComplete }) => {
7619
- const [reasoning, setReasoning] = useState6("");
7620
- const [isStreaming, setIsStreaming] = useState6(false);
8308
+ import { useState as useState5, useEffect as useEffect6, useRef as useRef4, memo as memo10 } from "react";
8309
+ import { Box as Box18, Text as Text17 } from "ink";
8310
+ import { jsx as jsx18, jsxs as jsxs17 } from "react/jsx-runtime";
8311
+ var StreamingTextComponent = ({
8312
+ eventBus,
8313
+ onReasoningComplete
8314
+ }) => {
8315
+ const [reasoning, setReasoning] = useState5("");
8316
+ const [isStreaming, setIsStreaming] = useState5(false);
7621
8317
  const reasoningRef = useRef4("");
7622
8318
  const lastUpdateRef = useRef4(0);
7623
8319
  const pendingUpdateRef = useRef4(null);
7624
- useEffect5(() => {
8320
+ useEffect6(() => {
7625
8321
  const handleStart = () => {
7626
8322
  setReasoning("");
7627
8323
  reasoningRef.current = "";
@@ -7677,19 +8373,56 @@ var StreamingTextComponent = ({ eventBus, onReasoningComplete }) => {
7677
8373
  truncatedCount = lines.length - MAX_VISIBLE_LINES;
7678
8374
  displayLines = lines.slice(-MAX_VISIBLE_LINES);
7679
8375
  }
7680
- return /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", paddingX: 1, marginBottom: 1, marginTop: 1, children: [
7681
- truncatedCount > 0 && /* @__PURE__ */ jsxs15(Text15, { color: "gray", dimColor: true, children: [
7682
- "... (ocultando ",
8376
+ return /* @__PURE__ */ jsx18(ChatBlock, { marginBottom: 1, children: /* @__PURE__ */ jsxs17(Box18, { flexDirection: "column", paddingLeft: 2, children: [
8377
+ truncatedCount > 0 ? /* @__PURE__ */ jsxs17(Text17, { dimColor: true, children: [
8378
+ "\u2026 ",
7683
8379
  truncatedCount,
7684
- " linhas anteriores para performance) ..."
7685
- ] }),
7686
- /* @__PURE__ */ jsx16(Box16, { paddingLeft: 2, flexDirection: "column", children: displayLines.map((line, i) => /* @__PURE__ */ jsx16(Text15, { color: "gray", dimColor: true, children: line }, i)) })
7687
- ] });
8380
+ " lines above hidden"
8381
+ ] }) : null,
8382
+ displayLines.map((line, i) => /* @__PURE__ */ jsx18(Text17, { dimColor: true, children: line }, i))
8383
+ ] }) });
7688
8384
  };
7689
8385
  var StreamingText = memo10(StreamingTextComponent);
7690
8386
 
8387
+ // src/app/ui/components/ExpandedPreviewBlock.tsx
8388
+ import { memo as memo11 } from "react";
8389
+ import { Box as Box19, Text as Text18 } from "ink";
8390
+ import { jsx as jsx19, jsxs as jsxs18 } from "react/jsx-runtime";
8391
+ function ExpandedPreviewBlockComponent({ data }) {
8392
+ const cols = typeof process.stdout?.columns === "number" ? process.stdout.columns : 80;
8393
+ const rule = TERMINAL_RULE_CHAR.repeat(Math.max(8, cols));
8394
+ const lines = data.fullText.split("\n");
8395
+ const cap = EXPAND_OVERLAY_MAX_LINES;
8396
+ const shown = lines.slice(0, cap);
8397
+ const rest = lines.length - cap;
8398
+ return /* @__PURE__ */ jsxs18(ChatBlock, { marginBottom: 1, children: [
8399
+ /* @__PURE__ */ jsx19(Text18, { color: "white", children: rule }),
8400
+ /* @__PURE__ */ jsxs18(Box19, { flexDirection: "column", paddingLeft: 1, children: [
8401
+ /* @__PURE__ */ jsx19(Text18, { color: BLUMA_TERMINAL.brandMagenta, bold: true, children: "expand (Ctrl+O)" }),
8402
+ /* @__PURE__ */ jsx19(Text18, { dimColor: true, children: data.title }),
8403
+ /* @__PURE__ */ jsxs18(Text18, { dimColor: true, children: [
8404
+ "+",
8405
+ data.linesHidden,
8406
+ " lines were clipped in chat \xB7 below: up to ",
8407
+ cap,
8408
+ " lines \xB7 use read_file_lines before edit_tool"
8409
+ ] }),
8410
+ /* @__PURE__ */ jsxs18(Box19, { flexDirection: "column", marginTop: 1, children: [
8411
+ shown.map((line, i) => /* @__PURE__ */ jsx19(Text18, { dimColor: true, children: line.slice(0, 200) }, i)),
8412
+ rest > 0 ? /* @__PURE__ */ jsxs18(Text18, { dimColor: true, children: [
8413
+ "\u2026 +",
8414
+ rest,
8415
+ " more lines in this chunk"
8416
+ ] }) : null
8417
+ ] })
8418
+ ] }),
8419
+ /* @__PURE__ */ jsx19(Text18, { color: "white", children: rule })
8420
+ ] });
8421
+ }
8422
+ var ExpandedPreviewBlock = memo11(ExpandedPreviewBlockComponent);
8423
+
7691
8424
  // src/app/ui/App.tsx
7692
- import { jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
8425
+ import { jsx as jsx20, jsxs as jsxs19 } from "react/jsx-runtime";
7693
8426
  var SAFE_AUTO_APPROVE_TOOLS = [
7694
8427
  // Comunicação/UI
7695
8428
  "message",
@@ -7707,36 +8440,64 @@ var SAFE_AUTO_APPROVE_TOOLS = [
7707
8440
  ];
7708
8441
  var AppComponent = ({ eventBus, sessionId }) => {
7709
8442
  const agentInstance = useRef5(null);
7710
- const [history, setHistory] = useState7([]);
7711
- const [statusMessage, setStatusMessage] = useState7(
8443
+ const [history, setHistory] = useState6([]);
8444
+ const [statusMessage, setStatusMessage] = useState6(
7712
8445
  "Initializing agent..."
7713
8446
  );
7714
- const [toolsCount, setToolsCount] = useState7(null);
7715
- const [mcpStatus, setMcpStatus] = useState7(
8447
+ const [toolsCount, setToolsCount] = useState6(null);
8448
+ const [mcpStatus, setMcpStatus] = useState6(
7716
8449
  "connecting"
7717
8450
  );
7718
- const [isProcessing, setIsProcessing] = useState7(true);
7719
- const [pendingConfirmation, setPendingConfirmation] = useState7(
8451
+ const [isProcessing, setIsProcessing] = useState6(true);
8452
+ const [pendingConfirmation, setPendingConfirmation] = useState6(
7720
8453
  null
7721
8454
  );
7722
- const [confirmationPreview, setConfirmationPreview] = useState7(
8455
+ const [confirmationPreview, setConfirmationPreview] = useState6(
7723
8456
  null
7724
8457
  );
7725
- const [isInitAgentActive, setIsInitAgentActive] = useState7(false);
8458
+ const [isInitAgentActive, setIsInitAgentActive] = useState6(false);
7726
8459
  const alwaysAcceptList = useRef5([]);
7727
8460
  const workdir = process.cwd();
7728
8461
  const updateCheckRan = useRef5(false);
7729
- const sessionStartTime = useRef5(Date.now());
7730
- const [toolCallCount, setToolCallCount] = useState7(0);
8462
+ const turnStartedAtRef = useRef5(null);
8463
+ const appendExpandPreviewToHistory = useCallback2(() => {
8464
+ const p = peekLatestExpandable();
8465
+ setHistory((prev) => {
8466
+ const id = prev.length;
8467
+ if (!p) {
8468
+ return [
8469
+ ...prev,
8470
+ {
8471
+ id,
8472
+ component: /* @__PURE__ */ jsx20(ChatMeta, { children: "Ctrl+O: no truncated preview to expand" }, id)
8473
+ }
8474
+ ];
8475
+ }
8476
+ return [
8477
+ ...prev,
8478
+ {
8479
+ id,
8480
+ component: /* @__PURE__ */ jsx20(ExpandedPreviewBlock, { data: p }, id)
8481
+ }
8482
+ ];
8483
+ });
8484
+ }, []);
8485
+ useEffect7(() => {
8486
+ expandPreviewHotkeyBus.on("expand", appendExpandPreviewToHistory);
8487
+ return () => {
8488
+ expandPreviewHotkeyBus.off("expand", appendExpandPreviewToHistory);
8489
+ };
8490
+ }, [appendExpandPreviewToHistory]);
7731
8491
  const handleInterrupt = useCallback2(() => {
7732
8492
  if (!isProcessing) return;
7733
8493
  eventBus.emit("user_interrupt");
8494
+ turnStartedAtRef.current = null;
7734
8495
  setIsProcessing(false);
7735
8496
  setHistory((prev) => [
7736
8497
  ...prev,
7737
8498
  {
7738
8499
  id: prev.length,
7739
- component: /* @__PURE__ */ jsx17(Text16, { color: "yellow", children: "-- Task cancelled by dev. --" })
8500
+ component: /* @__PURE__ */ jsx20(ChatMeta, { children: "cancelled (Esc)" })
7740
8501
  }
7741
8502
  ]);
7742
8503
  }, [isProcessing, eventBus]);
@@ -7752,6 +8513,7 @@ var AppComponent = ({ eventBus, sessionId }) => {
7752
8513
  if (cmd === "init") {
7753
8514
  setIsInitAgentActive(true);
7754
8515
  setIsProcessing(true);
8516
+ turnStartedAtRef.current = Date.now();
7755
8517
  } else {
7756
8518
  setIsProcessing(false);
7757
8519
  setIsInitAgentActive(false);
@@ -7760,11 +8522,11 @@ var AppComponent = ({ eventBus, sessionId }) => {
7760
8522
  ...prev,
7761
8523
  {
7762
8524
  id: prev.length,
7763
- component: /* @__PURE__ */ jsx17(Box17, { marginBottom: 1, children: /* @__PURE__ */ jsx17(Text16, { color: "white", dimColor: true, children: text }) })
8525
+ component: /* @__PURE__ */ jsx20(ChatUserMessage, { children: /* @__PURE__ */ jsx20(Text19, { dimColor: true, children: text }) })
7764
8526
  },
7765
8527
  {
7766
8528
  id: prev.length + 1,
7767
- component: /* @__PURE__ */ jsx17(
8529
+ component: /* @__PURE__ */ jsx20(
7768
8530
  SlashCommands_default,
7769
8531
  {
7770
8532
  input: text,
@@ -7783,17 +8545,15 @@ var AppComponent = ({ eventBus, sessionId }) => {
7783
8545
  return;
7784
8546
  }
7785
8547
  setIsProcessing(true);
8548
+ turnStartedAtRef.current = Date.now();
7786
8549
  setHistory((prev) => [
7787
8550
  ...prev,
7788
8551
  {
7789
8552
  id: prev.length,
7790
- component: /* @__PURE__ */ jsxs16(Box17, { marginBottom: 1, children: [
7791
- /* @__PURE__ */ jsx17(Text16, { color: "white", bold: true, children: "$ " }),
7792
- /* @__PURE__ */ jsxs16(Text16, { color: "white", children: [
7793
- "!",
7794
- command
7795
- ] })
7796
- ] })
8553
+ component: /* @__PURE__ */ jsx20(ChatUserMessage, { children: /* @__PURE__ */ jsxs19(Text19, { bold: true, color: "white", children: [
8554
+ "$ !",
8555
+ command
8556
+ ] }) })
7797
8557
  }
7798
8558
  ]);
7799
8559
  Promise.resolve().then(() => (init_async_command(), async_command_exports)).then(async ({ runCommandAsync: runCommandAsync2 }) => {
@@ -7807,11 +8567,12 @@ Command ID: ${result.command_id}
7807
8567
  Please use command_status to check the result and report back to the user.`;
7808
8568
  agentInstance.current?.processTurn({ content: contextMessage });
7809
8569
  } else {
8570
+ turnStartedAtRef.current = null;
7810
8571
  setHistory((prev) => [
7811
8572
  ...prev,
7812
8573
  {
7813
8574
  id: prev.length,
7814
- component: /* @__PURE__ */ jsxs16(Text16, { color: "red", children: [
8575
+ component: /* @__PURE__ */ jsxs19(Text19, { color: "red", children: [
7815
8576
  "Failed to execute: ",
7816
8577
  result.error || result.message
7817
8578
  ] })
@@ -7820,11 +8581,12 @@ Please use command_status to check the result and report back to the user.`;
7820
8581
  setIsProcessing(false);
7821
8582
  }
7822
8583
  } catch (err) {
8584
+ turnStartedAtRef.current = null;
7823
8585
  setHistory((prev) => [
7824
8586
  ...prev,
7825
8587
  {
7826
8588
  id: prev.length,
7827
- component: /* @__PURE__ */ jsxs16(Text16, { color: "red", children: [
8589
+ component: /* @__PURE__ */ jsxs19(Text19, { color: "red", children: [
7828
8590
  "Error: ",
7829
8591
  err.message
7830
8592
  ] })
@@ -7836,21 +8598,13 @@ Please use command_status to check the result and report back to the user.`;
7836
8598
  return;
7837
8599
  }
7838
8600
  setIsProcessing(true);
8601
+ turnStartedAtRef.current = Date.now();
7839
8602
  const displayText = text.length > 1e4 ? text.substring(0, 1e4) + "..." : text;
7840
8603
  setHistory((prev) => [
7841
8604
  ...prev,
7842
8605
  {
7843
8606
  id: prev.length,
7844
- component: (
7845
- // Uma única Box para o espaçamento
7846
- /* @__PURE__ */ jsx17(Box17, { marginBottom: 1, children: /* @__PURE__ */ jsxs16(Text16, { color: "white", dimColor: true, children: [
7847
- /* @__PURE__ */ jsxs16(Text16, { color: "white", children: [
7848
- ">",
7849
- " "
7850
- ] }),
7851
- displayText
7852
- ] }) })
7853
- )
8607
+ component: /* @__PURE__ */ jsx20(ChatUserMessage, { children: /* @__PURE__ */ jsx20(Text19, { children: displayText }) })
7854
8608
  }
7855
8609
  ]);
7856
8610
  agentInstance.current.processTurn({ content: text });
@@ -7878,8 +8632,8 @@ Please use command_status to check the result and report back to the user.`;
7878
8632
  },
7879
8633
  []
7880
8634
  );
7881
- useEffect6(() => {
7882
- setHistory([{ id: 0, component: /* @__PURE__ */ jsx17(Header, { sessionId, workdir }) }]);
8635
+ useEffect7(() => {
8636
+ setHistory([{ id: 0, component: /* @__PURE__ */ jsx20(Header, { sessionId, workdir }) }]);
7883
8637
  const initializeAgent = async () => {
7884
8638
  try {
7885
8639
  agentInstance.current = new Agent(sessionId, eventBus);
@@ -7899,6 +8653,20 @@ Please use command_status to check the result and report back to the user.`;
7899
8653
  };
7900
8654
  const handleBackendMessage = (parsed) => {
7901
8655
  try {
8656
+ const appendTurnDurationIfAny = () => {
8657
+ const t = turnStartedAtRef.current;
8658
+ if (t == null) return;
8659
+ turnStartedAtRef.current = null;
8660
+ const ms = Date.now() - t;
8661
+ const sec = ms < 1e4 ? (ms / 1e3).toFixed(1) : String(Math.round(ms / 1e3));
8662
+ setHistory((prev) => [
8663
+ ...prev,
8664
+ {
8665
+ id: prev.length,
8666
+ component: /* @__PURE__ */ jsx20(ChatTurnDuration, { secondsFormatted: sec })
8667
+ }
8668
+ ]);
8669
+ };
7902
8670
  if (parsed.type === "done" || parsed.type === "error") {
7903
8671
  setIsInitAgentActive(false);
7904
8672
  }
@@ -7924,6 +8692,7 @@ Please use command_status to check the result and report back to the user.`;
7924
8692
  if (parsed.type === "done") {
7925
8693
  if (parsed.status !== "awaiting_confirmation") {
7926
8694
  setStatusMessage(null);
8695
+ appendTurnDurationIfAny();
7927
8696
  }
7928
8697
  setIsProcessing(false);
7929
8698
  return;
@@ -7945,7 +8714,7 @@ Please use command_status to check the result and report back to the user.`;
7945
8714
  ...prev,
7946
8715
  {
7947
8716
  id: prev.length,
7948
- component: /* @__PURE__ */ jsx17(UpdateNotice_default, { message: msg })
8717
+ component: /* @__PURE__ */ jsx20(UpdateNotice_default, { message: msg })
7949
8718
  }
7950
8719
  ]);
7951
8720
  }
@@ -7959,25 +8728,14 @@ Please use command_status to check the result and report back to the user.`;
7959
8728
  }
7960
8729
  let newComponent = null;
7961
8730
  if (parsed.type === "debug") {
7962
- newComponent = /* @__PURE__ */ jsx17(Text16, { color: "gray", children: parsed.message });
8731
+ newComponent = /* @__PURE__ */ jsx20(ChatMeta, { children: parsed.message });
7963
8732
  } else if (parsed.type === "protocol_violation") {
7964
- newComponent = /* @__PURE__ */ jsxs16(
7965
- Box17,
7966
- {
7967
- borderStyle: "round",
7968
- borderColor: "yellow",
7969
- flexDirection: "column",
7970
- marginBottom: 1,
7971
- paddingX: 1,
7972
- children: [
7973
- /* @__PURE__ */ jsx17(Text16, { color: "yellow", bold: true, children: "Protocol Violation" }),
7974
- /* @__PURE__ */ jsx17(Text16, { color: "gray", children: parsed.content }),
7975
- /* @__PURE__ */ jsx17(Text16, { color: "yellow", children: parsed.message })
7976
- ]
7977
- }
7978
- );
8733
+ newComponent = /* @__PURE__ */ jsx20(ChatBlock, { marginBottom: 1, children: /* @__PURE__ */ jsxs19(Box20, { flexDirection: "column", paddingLeft: 2, children: [
8734
+ /* @__PURE__ */ jsx20(Text19, { dimColor: true, children: parsed.content }),
8735
+ /* @__PURE__ */ jsx20(Text19, { dimColor: true, children: parsed.message })
8736
+ ] }) });
7979
8737
  } else if (parsed.type === "error") {
7980
- newComponent = /* @__PURE__ */ jsx17(
8738
+ newComponent = /* @__PURE__ */ jsx20(
7981
8739
  ErrorMessage_default,
7982
8740
  {
7983
8741
  message: parsed.message,
@@ -7987,7 +8745,7 @@ Please use command_status to check the result and report back to the user.`;
7987
8745
  );
7988
8746
  } else if (parsed.type === "tool_call") {
7989
8747
  const nextId3 = history.length;
7990
- newComponent = /* @__PURE__ */ jsx17(
8748
+ newComponent = /* @__PURE__ */ jsx20(
7991
8749
  ToolCallDisplay,
7992
8750
  {
7993
8751
  toolName: parsed.tool_name,
@@ -7996,7 +8754,7 @@ Please use command_status to check the result and report back to the user.`;
7996
8754
  }
7997
8755
  );
7998
8756
  } else if (parsed.type === "tool_result") {
7999
- newComponent = /* @__PURE__ */ jsx17(
8757
+ newComponent = /* @__PURE__ */ jsx20(
8000
8758
  ToolResultDisplay,
8001
8759
  {
8002
8760
  toolName: parsed.tool_name,
@@ -8004,18 +8762,11 @@ Please use command_status to check the result and report back to the user.`;
8004
8762
  }
8005
8763
  );
8006
8764
  } else if (parsed.type === "user_overlay") {
8007
- newComponent = /* @__PURE__ */ jsx17(Box17, { marginBottom: 1, children: /* @__PURE__ */ jsxs16(Text16, { color: "gray", children: [
8008
- /* @__PURE__ */ jsxs16(Text16, { color: "magenta", children: [
8009
- ">",
8010
- " "
8011
- ] }),
8012
- parsed.payload
8013
- ] }) });
8765
+ newComponent = /* @__PURE__ */ jsx20(ChatUserMessage, { children: /* @__PURE__ */ jsx20(Text19, { dimColor: true, children: parsed.payload }) });
8014
8766
  } else if (parsed.type === "reasoning") {
8015
- newComponent = /* @__PURE__ */ jsx17(ReasoningDisplay, { reasoning: parsed.content });
8767
+ newComponent = /* @__PURE__ */ jsx20(ReasoningDisplay, { reasoning: parsed.content });
8016
8768
  } else if (parsed.type === "log") {
8017
- newComponent = /* @__PURE__ */ jsxs16(Text16, { color: "gray", children: [
8018
- "\u2139\uFE0F ",
8769
+ newComponent = /* @__PURE__ */ jsxs19(ChatMeta, { children: [
8019
8770
  parsed.message,
8020
8771
  parsed.payload ? `: ${parsed.payload}` : ""
8021
8772
  ] });
@@ -8023,10 +8774,25 @@ Please use command_status to check the result and report back to the user.`;
8023
8774
  newComponent = null;
8024
8775
  }
8025
8776
  if (newComponent) {
8026
- setHistory((prev) => [
8027
- ...prev,
8028
- { id: prev.length, component: newComponent }
8029
- ]);
8777
+ setHistory((prev) => {
8778
+ const next = [
8779
+ ...prev,
8780
+ { id: prev.length, component: newComponent }
8781
+ ];
8782
+ if (parsed.type === "error") {
8783
+ const t = turnStartedAtRef.current;
8784
+ if (t != null) {
8785
+ turnStartedAtRef.current = null;
8786
+ const ms = Date.now() - t;
8787
+ const sec = ms < 1e4 ? (ms / 1e3).toFixed(1) : String(Math.round(ms / 1e3));
8788
+ next.push({
8789
+ id: next.length,
8790
+ component: /* @__PURE__ */ jsx20(ChatTurnDuration, { secondsFormatted: sec })
8791
+ });
8792
+ }
8793
+ }
8794
+ return next;
8795
+ });
8030
8796
  }
8031
8797
  } catch (error) {
8032
8798
  }
@@ -8044,10 +8810,17 @@ Please use command_status to check the result and report back to the user.`;
8044
8810
  }, [eventBus, sessionId, handleConfirmation]);
8045
8811
  const renderInteractiveComponent = () => {
8046
8812
  if (mcpStatus !== "connected") {
8047
- return;
8813
+ return /* @__PURE__ */ jsx20(
8814
+ SessionInfoConnectingMCP_default,
8815
+ {
8816
+ sessionId,
8817
+ workdir,
8818
+ statusMessage
8819
+ }
8820
+ );
8048
8821
  }
8049
8822
  if (pendingConfirmation) {
8050
- return /* @__PURE__ */ jsx17(
8823
+ return /* @__PURE__ */ jsx20(
8051
8824
  ConfirmationPrompt,
8052
8825
  {
8053
8826
  toolCalls: pendingConfirmation,
@@ -8059,9 +8832,9 @@ Please use command_status to check the result and report back to the user.`;
8059
8832
  }
8060
8833
  );
8061
8834
  }
8062
- return /* @__PURE__ */ jsxs16(Box17, { flexDirection: "column", children: [
8063
- isProcessing && !pendingConfirmation && /* @__PURE__ */ jsx17(WorkingTimer, { eventBus }),
8064
- /* @__PURE__ */ jsx17(
8835
+ return /* @__PURE__ */ jsxs19(Box20, { flexDirection: "column", children: [
8836
+ isProcessing && !pendingConfirmation && /* @__PURE__ */ jsx20(WorkingTimer, { eventBus }),
8837
+ /* @__PURE__ */ jsx20(
8065
8838
  InputPrompt,
8066
8839
  {
8067
8840
  onSubmit: handleSubmit,
@@ -8072,9 +8845,9 @@ Please use command_status to check the result and report back to the user.`;
8072
8845
  )
8073
8846
  ] });
8074
8847
  };
8075
- return /* @__PURE__ */ jsxs16(Box17, { flexDirection: "column", children: [
8076
- /* @__PURE__ */ jsx17(Static, { items: history, children: (item) => /* @__PURE__ */ jsx17(Box17, { children: item.component }, item.id) }),
8077
- /* @__PURE__ */ jsx17(
8848
+ return /* @__PURE__ */ jsxs19(Box20, { flexDirection: "column", children: [
8849
+ /* @__PURE__ */ jsx20(Static, { items: history, children: (item) => /* @__PURE__ */ jsx20(Box20, { children: item.component }, item.id) }),
8850
+ /* @__PURE__ */ jsx20(
8078
8851
  StreamingText,
8079
8852
  {
8080
8853
  eventBus,
@@ -8084,7 +8857,7 @@ Please use command_status to check the result and report back to the user.`;
8084
8857
  ...prev,
8085
8858
  {
8086
8859
  id: prev.length,
8087
- component: /* @__PURE__ */ jsx17(ReasoningDisplay, { reasoning })
8860
+ component: /* @__PURE__ */ jsx20(ReasoningDisplay, { reasoning })
8088
8861
  }
8089
8862
  ]);
8090
8863
  }
@@ -8094,7 +8867,7 @@ Please use command_status to check the result and report back to the user.`;
8094
8867
  renderInteractiveComponent()
8095
8868
  ] });
8096
8869
  };
8097
- var App = memo11(AppComponent);
8870
+ var App = memo12(AppComponent);
8098
8871
  var App_default = App;
8099
8872
 
8100
8873
  // src/app/ui/utils/terminalTitle.ts
@@ -8218,7 +8991,7 @@ async function runAgentMode() {
8218
8991
  process.env.BLUMA_SANDBOX_NAME = String(envelope.metadata.sandbox_name);
8219
8992
  }
8220
8993
  }
8221
- const eventBus = new EventEmitter2();
8994
+ const eventBus = new EventEmitter3();
8222
8995
  const sessionId = envelope.session_id || envelope.message_id || uuidv46();
8223
8996
  const uc = envelope.user_context;
8224
8997
  const userContextInput = {
@@ -8350,13 +9123,13 @@ async function runAgentMode() {
8350
9123
  function runCliMode() {
8351
9124
  const BLUMA_TITLE = process.env.BLUMA_TITLE || "BluMa - NomadEngenuity";
8352
9125
  startTitleKeeper(BLUMA_TITLE);
8353
- const eventBus = new EventEmitter2();
9126
+ const eventBus = new EventEmitter3();
8354
9127
  const sessionId = uuidv46();
8355
9128
  const props = {
8356
9129
  eventBus,
8357
9130
  sessionId
8358
9131
  };
8359
- render(React11.createElement(App_default, props));
9132
+ render(React12.createElement(App_default, props));
8360
9133
  }
8361
9134
  var argv = process.argv.slice(2);
8362
9135
  if (argv[0] === "agent") {