mixdog 0.9.4 → 0.9.6

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.
Files changed (121) hide show
  1. package/README.md +112 -38
  2. package/package.json +4 -1
  3. package/scripts/bench/r5-orchestrated-task.json +7 -0
  4. package/scripts/bench/round-r13-clientmeta.json +464 -0
  5. package/scripts/bench/round-r14-betafeatures.json +466 -0
  6. package/scripts/bench/round-r15-fulldefault.json +462 -0
  7. package/scripts/bench/round-r16-sessionid.json +466 -0
  8. package/scripts/bench/round-r17-wirebytes.json +456 -0
  9. package/scripts/bench/round-r18-prewarm.json +468 -0
  10. package/scripts/bench/round-r19-clean.json +472 -0
  11. package/scripts/bench/round-r20-prewarm-clean.json +475 -0
  12. package/scripts/bench/round-r21-delta-retry.json +473 -0
  13. package/scripts/bench/round-r22-full-probe.json +693 -0
  14. package/scripts/bench/round-r23-itemprobe.json +701 -0
  15. package/scripts/bench/round-r24-shapefix.json +677 -0
  16. package/scripts/bench/round-r25-serial.json +464 -0
  17. package/scripts/bench/round-r26-parallel3.json +671 -0
  18. package/scripts/bench/round-r27-parallel10.json +894 -0
  19. package/scripts/bench/round-r28-parallel10-stagger.json +882 -0
  20. package/scripts/bench/round-r29-parallel10-stagger166.json +886 -0
  21. package/scripts/bench/round-r30-instid.json +253 -0
  22. package/scripts/bench/round-r31-upgradeprobe.json +256 -0
  23. package/scripts/bench/round-r32-vs-codex-lead.json +254 -0
  24. package/scripts/bench/round-r33-vs-codex-codex.json +115 -0
  25. package/scripts/bench/round-r34-orchestrated.json +120 -0
  26. package/scripts/bench/round-r35-orchestrated-codex.json +61 -0
  27. package/scripts/bench/round-r36-orchestrated-capped.json +128 -0
  28. package/scripts/bench-run.mjs +40 -7
  29. package/scripts/internal-comms-bench.mjs +2 -4
  30. package/scripts/model-catalog-audit.mjs +209 -0
  31. package/scripts/model-list-sanitize-test.mjs +37 -0
  32. package/scripts/recall-bench.mjs +76 -13
  33. package/scripts/recall-quality-cases.json +12 -0
  34. package/scripts/tool-smoke.mjs +2 -2
  35. package/src/agents/heavy-worker/AGENT.md +7 -7
  36. package/src/agents/scheduler-task/AGENT.md +2 -3
  37. package/src/agents/webhook-handler/AGENT.md +2 -3
  38. package/src/agents/worker/AGENT.md +9 -10
  39. package/src/app.mjs +12 -1
  40. package/src/headless-role.mjs +7 -1
  41. package/src/mixdog-session-runtime.mjs +15 -14
  42. package/src/output-styles/default.md +14 -18
  43. package/src/output-styles/minimal.md +4 -5
  44. package/src/output-styles/simple.md +7 -8
  45. package/src/rules/agent/00-common.md +6 -5
  46. package/src/rules/agent/30-explorer.md +16 -5
  47. package/src/rules/lead/01-general.md +5 -5
  48. package/src/rules/lead/lead-brief.md +6 -3
  49. package/src/rules/lead/lead-tool.md +6 -4
  50. package/src/rules/shared/01-tool.md +17 -21
  51. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +8 -3
  52. package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +19 -0
  53. package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +11 -3
  54. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +9 -1
  55. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +53 -9
  56. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +23 -10
  57. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +11 -9
  58. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +33 -4
  59. package/src/runtime/agent/orchestrator/providers/provider-catalog-cache.mjs +80 -0
  60. package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +93 -0
  61. package/src/runtime/agent/orchestrator/session/loop.mjs +27 -5
  62. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +1 -1
  63. package/src/runtime/agent/orchestrator/stall-policy.mjs +20 -1
  64. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +7 -7
  65. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +1 -1
  66. package/src/runtime/channels/backends/discord-gateway.mjs +2 -2
  67. package/src/runtime/channels/backends/discord.mjs +15 -26
  68. package/src/runtime/channels/backends/telegram.mjs +8 -12
  69. package/src/runtime/channels/index.mjs +23 -43
  70. package/src/runtime/channels/lib/backend-dispatch.mjs +5 -3
  71. package/src/runtime/channels/lib/config.mjs +37 -149
  72. package/src/runtime/channels/lib/event-pipeline.mjs +4 -4
  73. package/src/runtime/channels/lib/event-queue.mjs +15 -9
  74. package/src/runtime/channels/lib/inbound-routing.mjs +2 -39
  75. package/src/runtime/channels/lib/interaction-workflows.mjs +5 -113
  76. package/src/runtime/channels/lib/scheduler.mjs +27 -113
  77. package/src/runtime/channels/lib/webhook/deliveries.mjs +3 -2
  78. package/src/runtime/channels/lib/webhook.mjs +8 -47
  79. package/src/runtime/memory/index.mjs +9 -0
  80. package/src/runtime/memory/lib/core-memory-store.mjs +5 -1
  81. package/src/runtime/memory/lib/ko-morph.mjs +195 -0
  82. package/src/runtime/memory/lib/memory-recall-store.mjs +47 -10
  83. package/src/runtime/memory/lib/memory-text-utils.mjs +46 -0
  84. package/src/runtime/memory/lib/query-handlers.mjs +27 -5
  85. package/src/runtime/memory/tool-defs.mjs +2 -2
  86. package/src/runtime/shared/config.mjs +14 -4
  87. package/src/runtime/shared/markdown-frontmatter.mjs +19 -0
  88. package/src/runtime/shared/schedules-store.mjs +13 -3
  89. package/src/runtime/shared/tool-execution-contract.mjs +2 -2
  90. package/src/session-runtime/config-helpers.mjs +45 -10
  91. package/src/session-runtime/quick-model-rows.mjs +46 -11
  92. package/src/session-runtime/quick-search-models.mjs +28 -27
  93. package/src/session-runtime/settings-api.mjs +34 -1
  94. package/src/session-runtime/tool-defs.mjs +1 -1
  95. package/src/session-runtime/workflow.mjs +1 -1
  96. package/src/standalone/agent-tool/tool-def.mjs +1 -1
  97. package/src/standalone/agent-tool.mjs +30 -0
  98. package/src/standalone/channel-admin.mjs +102 -90
  99. package/src/standalone/explore-tool.mjs +39 -10
  100. package/src/tui/App.jsx +131 -28
  101. package/src/tui/app/app-format.mjs +11 -4
  102. package/src/tui/app/channel-pickers.mjs +2 -4
  103. package/src/tui/app/maintenance-pickers.mjs +60 -28
  104. package/src/tui/app/model-options.mjs +5 -1
  105. package/src/tui/app/settings-picker.mjs +0 -1
  106. package/src/tui/app/transcript-window.mjs +8 -2
  107. package/src/tui/app/use-transcript-scroll.mjs +3 -1
  108. package/src/tui/app/use-transcript-window.mjs +19 -1
  109. package/src/tui/components/ConfirmBar.jsx +10 -7
  110. package/src/tui/components/Picker.jsx +32 -11
  111. package/src/tui/components/PromptInput.jsx +10 -1
  112. package/src/tui/components/StatusLine.jsx +6 -3
  113. package/src/tui/dist/index.mjs +441 -293
  114. package/src/tui/engine.mjs +48 -12
  115. package/src/tui/index.jsx +11 -52
  116. package/src/ui/statusline.mjs +10 -0
  117. package/src/workflows/default/WORKFLOW.md +15 -17
  118. package/src/workflows/sequential/WORKFLOW.md +15 -17
  119. package/vendor/ink/build/ink.js +9 -1
  120. package/scripts/recall-usecase-probe.json +0 -6
  121. package/src/runtime/channels/lib/holidays.mjs +0 -138
@@ -7,7 +7,7 @@ import { dirname as dirname6, join as join8 } from "node:path";
7
7
  import { performance as performance3 } from "node:perf_hooks";
8
8
 
9
9
  // src/tui/App.jsx
10
- import React20, { useState as useState8, useCallback as useCallback6, useEffect as useEffect10, useLayoutEffect as useLayoutEffect4, useMemo as useMemo2, useRef as useRef8 } from "react";
10
+ import React20, { useState as useState8, useCallback as useCallback6, useEffect as useEffect10, useLayoutEffect as useLayoutEffect4, useMemo as useMemo2, useRef as useRef9 } from "react";
11
11
  import { Box as Box18, Text as Text20, useApp, useInput as useInput5, useStdin as useStdin3, useStdout as useStdout2 } from "../../../vendor/ink/build/index.js";
12
12
 
13
13
  // src/tui/themes/base.mjs
@@ -3492,7 +3492,7 @@ var STATUSLINE_BOOT_FULL_DELAY_ACTIVE_MS = 2200;
3492
3492
  var STATUSLINE_BOOT_FULL_RETRY_MS = 2e3;
3493
3493
  var STATUSLINE_BOOT_FULL_RETRY_MAX_MS = 3e4;
3494
3494
  var STATUSLINE_REFRESH_MS = 2e3;
3495
- var STATUSLINE_ACTIVE_REFRESH_MS = 250;
3495
+ var STATUSLINE_ACTIVE_REFRESH_MS = 500;
3496
3496
  function isTerminalStatus(statusText) {
3497
3497
  return /idle|done|complete|success|closed|error|fail|cancel|killed|timeout/.test(String(statusText || "").toLowerCase());
3498
3498
  }
@@ -3571,7 +3571,8 @@ function localContextPct({
3571
3571
  autoCompactTokenLimit = 0
3572
3572
  } = {}) {
3573
3573
  const baseWindow = localNum(compactBoundaryTokens) > 0 ? localNum(compactBoundaryTokens) : localNum(displayContextWindow) > 0 ? localNum(displayContextWindow) : localNum(contextWindow) > 0 ? localNum(contextWindow) : localNum(rawContextWindow) > 0 ? localNum(rawContextWindow) : 2e5;
3574
- const window = baseWindow;
3574
+ const trigger = localNum(autoCompactTokenLimit);
3575
+ const window = trigger > 0 && trigger < baseWindow ? trigger : baseWindow;
3575
3576
  const s = stats && typeof stats === "object" ? stats : {};
3576
3577
  const source = String(s.currentContextSource || "").toLowerCase();
3577
3578
  const estimated = localNum(s.currentEstimatedContextTokens);
@@ -4701,10 +4702,11 @@ function PromptInput({
4701
4702
  if (disabled) return;
4702
4703
  const rawInput = String(input ?? "");
4703
4704
  const inputKey = rawInput.toLowerCase();
4705
+ const rawShiftArrowForGrid = rawInput === "\x1B[1;2A" || rawInput === "\x1B[a" || rawInput === "[1;2A" || rawInput === "\x1B[1;2B" || rawInput === "\x1B[b" || rawInput === "[1;2B" || rawInput === "\x1B[1;2C" || rawInput === "\x1B[c" || rawInput === "[1;2C" || rawInput === "\x1B[1;2D" || rawInput === "\x1B[d" || rawInput === "[1;2D" || rawInput === "\x1B[1;6A" || rawInput === "[1;6A" || rawInput === "\x1B[1;6B" || rawInput === "[1;6B" || rawInput === "\x1B[1;6C" || rawInput === "[1;6C" || rawInput === "\x1B[1;6D" || rawInput === "[1;6D";
4704
4706
  const gridSelectionActive = typeof suppressShiftNavRef === "function" ? suppressShiftNavRef() : typeof suppressShiftNavRef?.current === "function" ? suppressShiftNavRef.current() : Boolean(suppressShiftNavRef?.current);
4705
4707
  if (gridSelectionActive) {
4706
4708
  const isShiftArrow = key.shift && (key.leftArrow || key.rightArrow || key.upArrow || key.downArrow || key.home || key.end);
4707
- if (isShiftArrow) return;
4709
+ if (isShiftArrow || rawShiftArrowForGrid) return;
4708
4710
  }
4709
4711
  if (/(?:\x1b)?\[<\d+;\d+;\d+[Mm]/.test(rawInput) || /^\[?<\d+;\d+;\d+[Mm]?$/.test(rawInput)) {
4710
4712
  return;
@@ -5051,14 +5053,14 @@ function QueuedCommands({ queued, columns }) {
5051
5053
  }
5052
5054
 
5053
5055
  // src/tui/components/Picker.jsx
5054
- import React6, { useState as useState3, useCallback, useEffect as useEffect3 } from "react";
5056
+ import React6, { useState as useState3, useCallback, useEffect as useEffect3, useRef as useRef4 } from "react";
5055
5057
  import { Box as Box6, Text as Text6, useInput as useInput2 } from "../../../vendor/ink/build/index.js";
5056
5058
  import stringWidth2 from "string-width";
5057
5059
 
5058
5060
  // src/tui/components/ConfirmBar.jsx
5059
5061
  import React5 from "react";
5060
5062
  import { Box as Box5, Text as Text5 } from "../../../vendor/ink/build/index.js";
5061
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
5063
+ import { jsx as jsx5 } from "react/jsx-runtime";
5062
5064
  function clampConfirmFocus(index, count) {
5063
5065
  const n = Math.max(0, Number(count) || 0);
5064
5066
  if (n === 0) return -1;
@@ -5069,25 +5071,30 @@ function clampConfirmFocus(index, count) {
5069
5071
  function ConfirmBar({ buttons = [], focusedIndex = -1 }) {
5070
5072
  const list = Array.isArray(buttons) ? buttons.filter(Boolean) : [];
5071
5073
  if (list.length === 0) return null;
5072
- return /* @__PURE__ */ jsx5(Box5, { flexDirection: "row", width: "100%", justifyContent: "flex-start", children: list.map((button, index) => {
5074
+ return /* @__PURE__ */ jsx5(Box5, { flexDirection: "row", flexShrink: 0, justifyContent: "flex-start", alignItems: "center", children: list.map((button, index) => {
5073
5075
  const isFocused = index === focusedIndex;
5074
- return /* @__PURE__ */ jsxs4(Text5, { children: [
5075
- index > 0 ? /* @__PURE__ */ jsx5(Text5, { color: theme.subtle, children: " " }) : null,
5076
- /* @__PURE__ */ jsx5(
5077
- Text5,
5078
- {
5079
- color: isFocused ? theme.selectionText : theme.text,
5080
- backgroundColor: isFocused ? theme.selectionBackground : void 0,
5081
- bold: isFocused,
5082
- children: ` ${button.label} `
5083
- }
5084
- )
5085
- ] }, button.value ?? index);
5076
+ return /* @__PURE__ */ jsx5(
5077
+ Box5,
5078
+ {
5079
+ marginLeft: index > 0 ? 1 : 0,
5080
+ paddingX: 1,
5081
+ backgroundColor: isFocused ? theme.selectionBackground : void 0,
5082
+ children: /* @__PURE__ */ jsx5(
5083
+ Text5,
5084
+ {
5085
+ color: isFocused ? theme.selectionText : theme.subtle,
5086
+ bold: isFocused,
5087
+ children: `[ ${button.label} ]`
5088
+ }
5089
+ )
5090
+ },
5091
+ button.value ?? index
5092
+ );
5086
5093
  }) });
5087
5094
  }
5088
5095
 
5089
5096
  // src/tui/components/Picker.jsx
5090
- import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
5097
+ import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
5091
5098
  var MAX_VISIBLE = 8;
5092
5099
  var DEFAULT_LABEL_WIDTH = 28;
5093
5100
  var SELECT_HELP = "\u2191/\u2193 Select \xB7 Enter Choose \xB7 Esc Back";
@@ -5168,6 +5175,7 @@ function Picker({
5168
5175
  const confirmButtons = Array.isArray(confirmBar?.buttons) ? confirmBar.buttons.filter(Boolean) : [];
5169
5176
  const hasConfirm = confirmButtons.length > 0;
5170
5177
  const [confirmFocus, setConfirmFocus] = useState3(-1);
5178
+ const lastTabAtRef = useRef4(0);
5171
5179
  useEffect3(() => {
5172
5180
  setConfirmFocus(-1);
5173
5181
  }, [confirmButtons.length, confirmBar]);
@@ -5202,10 +5210,11 @@ function Picker({
5202
5210
  if (item) onHighlight(item.value, item, selectedIndex);
5203
5211
  }, [onHighlight, items, selectedIndex]);
5204
5212
  const activeFooter = typeof footer === "function" ? footer(items[selectedIndex], selectedIndex) : footer;
5205
- const footerLines = normalizeFooterLines(activeFooter, columns);
5213
+ const confirmInlineWidth = hasConfirm ? confirmButtons.reduce((sum, button, index) => sum + (index > 0 ? 1 : 0) + stringWidth2(`[ ${button?.label || ""} ]`) + 2, 0) : 0;
5214
+ const footerLines = normalizeFooterLines(activeFooter, Math.max(0, columns - (hasConfirm ? confirmInlineWidth + 1 : 0)));
5206
5215
  const footerGap = footerLines.length > 0 ? Math.max(0, Math.floor(Number(footerGapRows) || 0)) : 0;
5207
5216
  const footerReserveRows = footerLines.length > 0 ? footerLines.length + footerGap : 0;
5208
- const confirmReserveRows = hasConfirm ? 2 : 0;
5217
+ const confirmReserveRows = hasConfirm && footerLines.length === 0 ? 2 : 0;
5209
5218
  const effectiveVisibleLimit = Math.max(1, visibleLimit - footerReserveRows - confirmReserveRows);
5210
5219
  const helpText = help || (hasConfirm ? CONFIRM_HELP : onLeft || onRight || onTab ? ADJUST_HELP : SELECT_HELP);
5211
5220
  useInput2(
@@ -5295,6 +5304,9 @@ function Picker({
5295
5304
  return;
5296
5305
  }
5297
5306
  if (key.tab || input === " ") {
5307
+ const now = Date.now();
5308
+ if (now - lastTabAtRef.current < 120) return;
5309
+ lastTabAtRef.current = now;
5298
5310
  if (hasConfirm) {
5299
5311
  setConfirmFocus((f) => f < 0 ? 0 : f + 1 > confirmButtons.length - 1 ? -1 : f + 1);
5300
5312
  return;
@@ -5329,7 +5341,7 @@ function Picker({
5329
5341
  );
5330
5342
  if (items.length === 0) {
5331
5343
  const emptyLine = truncateText(String(description || "").replace(/\s+/g, " ").trim(), Math.max(0, columns - 4));
5332
- return /* @__PURE__ */ jsx6(Box6, { flexDirection: "column", flexShrink: 0, height: fillHeight ? "100%" : void 0, children: /* @__PURE__ */ jsxs5(
5344
+ return /* @__PURE__ */ jsx6(Box6, { flexDirection: "column", flexShrink: 0, height: fillHeight ? "100%" : void 0, children: /* @__PURE__ */ jsxs4(
5333
5345
  Box6,
5334
5346
  {
5335
5347
  flexDirection: "column",
@@ -5339,7 +5351,7 @@ function Picker({
5339
5351
  height: fillHeight ? "100%" : void 0,
5340
5352
  width: "100%",
5341
5353
  children: [
5342
- /* @__PURE__ */ jsxs5(Box6, { flexDirection: "row", justifyContent: "space-between", children: [
5354
+ /* @__PURE__ */ jsxs4(Box6, { flexDirection: "row", justifyContent: "space-between", children: [
5343
5355
  /* @__PURE__ */ jsx6(Text6, { color: theme.panelTitle, children: title || "Picker" }),
5344
5356
  /* @__PURE__ */ jsx6(Text6, { color: theme.subtle, children: helpText })
5345
5357
  ] }),
@@ -5347,7 +5359,7 @@ function Picker({
5347
5359
  /* @__PURE__ */ jsx6(Text6, { color: theme.text, children: emptyLine || " " }),
5348
5360
  /* @__PURE__ */ jsx6(Text6, { children: " " }),
5349
5361
  /* @__PURE__ */ jsx6(Text6, { color: theme.inactive, children: "(empty)" }),
5350
- hasConfirm ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
5362
+ hasConfirm ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
5351
5363
  /* @__PURE__ */ jsx6(Box6, { flexGrow: 1 }),
5352
5364
  /* @__PURE__ */ jsx6(Text6, { children: " " }),
5353
5365
  /* @__PURE__ */ jsx6(ConfirmBar, { buttons: confirmButtons, focusedIndex: clampConfirmFocus(confirmFocus, confirmButtons.length) })
@@ -5374,7 +5386,7 @@ function Picker({
5374
5386
  const metaWidth = hasMeta ? clampMetaWidth(metaWidthOverride, columns, labelWidth) : 0;
5375
5387
  const descriptionWidth = Math.max(0, columns - indexOffset - markerWidth - labelWidth - (hasMeta ? metaWidth + 14 : 12));
5376
5388
  const panelDescription = truncateText(String(description || "").replace(/\s+/g, " ").trim(), Math.max(0, columns - 4));
5377
- return /* @__PURE__ */ jsx6(Box6, { flexDirection: "column", flexShrink: 0, width: "100%", height: fillHeight ? "100%" : void 0, children: /* @__PURE__ */ jsxs5(
5389
+ return /* @__PURE__ */ jsx6(Box6, { flexDirection: "column", flexShrink: 0, width: "100%", height: fillHeight ? "100%" : void 0, children: /* @__PURE__ */ jsxs4(
5378
5390
  Box6,
5379
5391
  {
5380
5392
  flexDirection: "column",
@@ -5384,7 +5396,7 @@ function Picker({
5384
5396
  width: "100%",
5385
5397
  height: fillHeight ? "100%" : void 0,
5386
5398
  children: [
5387
- /* @__PURE__ */ jsxs5(Box6, { flexDirection: "row", justifyContent: "space-between", children: [
5399
+ /* @__PURE__ */ jsxs4(Box6, { flexDirection: "row", justifyContent: "space-between", children: [
5388
5400
  /* @__PURE__ */ jsx6(Text6, { color: theme.panelTitle, children: title }),
5389
5401
  /* @__PURE__ */ jsx6(Text6, { color: theme.subtle, children: helpText })
5390
5402
  ] }),
@@ -5418,18 +5430,34 @@ function Picker({
5418
5430
  item.value
5419
5431
  );
5420
5432
  }),
5421
- footerLines.length > 0 ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
5433
+ footerLines.length > 0 ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
5422
5434
  /* @__PURE__ */ jsx6(Box6, { flexGrow: 1 }),
5423
- footerLines.map((line, index) => /* @__PURE__ */ jsxs5(Text6, { children: [
5424
- line.glyph ? /* @__PURE__ */ jsxs5(Text6, { color: line.color, children: [
5425
- line.glyph,
5426
- " "
5427
- ] }) : null,
5428
- /* @__PURE__ */ jsx6(Text6, { color: theme.text, children: line.text })
5429
- ] }, `footer-${index}`))
5435
+ footerLines.map((line, index) => {
5436
+ const attachConfirm = hasConfirm && index === footerLines.length - 1;
5437
+ return /* @__PURE__ */ jsxs4(
5438
+ Box6,
5439
+ {
5440
+ flexDirection: "row",
5441
+ width: "100%",
5442
+ justifyContent: attachConfirm ? "space-between" : "flex-start",
5443
+ alignItems: "center",
5444
+ children: [
5445
+ /* @__PURE__ */ jsxs4(Text6, { children: [
5446
+ line.glyph ? /* @__PURE__ */ jsxs4(Text6, { color: line.color, children: [
5447
+ line.glyph,
5448
+ " "
5449
+ ] }) : null,
5450
+ /* @__PURE__ */ jsx6(Text6, { color: theme.text, children: line.text })
5451
+ ] }),
5452
+ attachConfirm ? /* @__PURE__ */ jsx6(ConfirmBar, { buttons: confirmButtons, focusedIndex: clampConfirmFocus(confirmFocus, confirmButtons.length) }) : null
5453
+ ]
5454
+ },
5455
+ `footer-${index}`
5456
+ );
5457
+ })
5430
5458
  ] }) : null,
5431
- hasConfirm ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
5432
- footerLines.length > 0 ? null : /* @__PURE__ */ jsx6(Box6, { flexGrow: 1 }),
5459
+ hasConfirm && footerLines.length === 0 ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
5460
+ /* @__PURE__ */ jsx6(Box6, { flexGrow: 1 }),
5433
5461
  /* @__PURE__ */ jsx6(Text6, { children: " " }),
5434
5462
  /* @__PURE__ */ jsx6(ConfirmBar, { buttons: confirmButtons, focusedIndex: clampConfirmFocus(confirmFocus, confirmButtons.length) })
5435
5463
  ] }) : null
@@ -5450,23 +5478,23 @@ var ItemRow = React6.memo(function ItemRow2({ indexText, indexWidth, marker, mar
5450
5478
  const displayMeta = truncateText(meta, metaWidth);
5451
5479
  const displayDescription = truncateText(description, descriptionWidth);
5452
5480
  const parts = Array.isArray(metaParts) ? metaParts : null;
5453
- return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "row", width: "100%", backgroundColor: isSelected ? theme.selectionBackground : void 0, children: [
5454
- indexWidth > 0 ? /* @__PURE__ */ jsxs5(Text6, { color: rowIndexColor, children: [
5481
+ return /* @__PURE__ */ jsxs4(Box6, { flexDirection: "row", width: "100%", backgroundColor: isSelected ? theme.selectionBackground : void 0, children: [
5482
+ indexWidth > 0 ? /* @__PURE__ */ jsxs4(Text6, { color: rowIndexColor, children: [
5455
5483
  padCells(indexText, indexWidth),
5456
5484
  " "
5457
5485
  ] }) : null,
5458
5486
  markerWidth > 0 ? /* @__PURE__ */ jsx6(Text6, { color: isSelected ? theme.selectionText : marker ? markerColor || theme.success : rowText, children: padCells(displayMarker, markerWidth) }) : null,
5459
5487
  /* @__PURE__ */ jsx6(Text6, { color: rowText, children: displayLabel }),
5460
- suffix ? /* @__PURE__ */ jsxs5(Text6, { color: isSelected ? theme.selectionText : labelSuffixColor || theme.success, children: [
5488
+ suffix ? /* @__PURE__ */ jsxs4(Text6, { color: isSelected ? theme.selectionText : labelSuffixColor || theme.success, children: [
5461
5489
  suffixGap,
5462
5490
  suffix
5463
5491
  ] }) : null,
5464
5492
  /* @__PURE__ */ jsx6(Text6, { color: rowText, children: labelPadding }),
5465
- showMeta ? /* @__PURE__ */ jsxs5(Text6, { color: rowText, children: [
5493
+ showMeta ? /* @__PURE__ */ jsxs4(Text6, { color: rowText, children: [
5466
5494
  " ",
5467
5495
  parts ? padCells(parts.map((part) => padCells(truncateText(part?.text || "", Number(part?.width) || 1), Number(part?.width) || 1)).join(" "), metaWidth) : padCells(displayMeta, metaWidth)
5468
5496
  ] }) : null,
5469
- displayDescription ? /* @__PURE__ */ jsxs5(Text6, { color: rowText, children: [
5497
+ displayDescription ? /* @__PURE__ */ jsxs4(Text6, { color: rowText, children: [
5470
5498
  " ",
5471
5499
  displayDescription
5472
5500
  ] }) : null
@@ -5477,7 +5505,7 @@ var ItemRow = React6.memo(function ItemRow2({ indexText, indexWidth, marker, mar
5477
5505
  import React7 from "react";
5478
5506
  import { Box as Box7, Text as Text7 } from "../../../vendor/ink/build/index.js";
5479
5507
  import stringWidth3 from "string-width";
5480
- import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
5508
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
5481
5509
  var MAX_VISIBLE2 = 8;
5482
5510
  var COMMAND_LABEL_WIDTH = 18;
5483
5511
  var SLASH_HELP = "\u2191/\u2193 select \xB7 \u2190/\u2192 change \xB7 Enter run \xB7 Esc cancel";
@@ -5528,7 +5556,7 @@ function SlashCommandPalette({ commands, selectedIndex = 0, title = "Commands",
5528
5556
  const description = truncateText2(SLASH_DESCRIPTION, Math.max(0, columns - 4));
5529
5557
  const titleWidth = stringWidth3(String(title || ""));
5530
5558
  const help = truncateText2(SLASH_HELP, Math.max(0, columns - titleWidth - 7));
5531
- return /* @__PURE__ */ jsx7(Box7, { flexDirection: "column", flexShrink: 0, width: "100%", children: /* @__PURE__ */ jsxs6(
5559
+ return /* @__PURE__ */ jsx7(Box7, { flexDirection: "column", flexShrink: 0, width: "100%", children: /* @__PURE__ */ jsxs5(
5532
5560
  Box7,
5533
5561
  {
5534
5562
  flexDirection: "column",
@@ -5537,7 +5565,7 @@ function SlashCommandPalette({ commands, selectedIndex = 0, title = "Commands",
5537
5565
  paddingX: 1,
5538
5566
  width: "100%",
5539
5567
  children: [
5540
- /* @__PURE__ */ jsxs6(Box7, { flexDirection: "row", justifyContent: "space-between", children: [
5568
+ /* @__PURE__ */ jsxs5(Box7, { flexDirection: "row", justifyContent: "space-between", children: [
5541
5569
  /* @__PURE__ */ jsx7(Text7, { color: theme.panelTitle, wrap: "truncate", children: title }),
5542
5570
  /* @__PURE__ */ jsx7(Text7, { color: theme.subtle, wrap: "truncate", children: help })
5543
5571
  ] }),
@@ -5565,9 +5593,9 @@ var CommandRow = React7.memo(function CommandRow2({ command, isSelected, labelWi
5565
5593
  const label = truncateText2(commandDisplayLabel(command, query), labelWidth);
5566
5594
  const description = truncateText2(command.description, descriptionWidth);
5567
5595
  const rowText = isSelected ? theme.selectionText : theme.text;
5568
- return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "row", width: "100%", backgroundColor: isSelected ? theme.selectionBackground : void 0, children: [
5596
+ return /* @__PURE__ */ jsxs5(Box7, { flexDirection: "row", width: "100%", backgroundColor: isSelected ? theme.selectionBackground : void 0, children: [
5569
5597
  /* @__PURE__ */ jsx7(Text7, { color: rowText, children: padCells2(label, labelWidth) }),
5570
- description ? /* @__PURE__ */ jsxs6(Text7, { color: rowText, children: [
5598
+ description ? /* @__PURE__ */ jsxs5(Text7, { color: rowText, children: [
5571
5599
  " ",
5572
5600
  description
5573
5601
  ] }) : null
@@ -5578,7 +5606,7 @@ var CommandRow = React7.memo(function CommandRow2({ command, isSelected, labelWi
5578
5606
  import React8 from "react";
5579
5607
  import { Box as Box8, Text as Text8 } from "../../../vendor/ink/build/index.js";
5580
5608
  import stringWidth4 from "string-width";
5581
- import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
5609
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
5582
5610
  function truncateText3(value, width) {
5583
5611
  const text = String(value || "");
5584
5612
  if (!(width > 0)) return "";
@@ -5629,7 +5657,7 @@ function ProgressBar({ value = 0, total = 0, width = 24 }) {
5629
5657
  const safeWidth2 = Math.max(8, Math.floor(width));
5630
5658
  const filled = Math.max(0, Math.min(safeWidth2, Math.round(pct / 100 * safeWidth2)));
5631
5659
  const empty = safeWidth2 - filled;
5632
- return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", width: safeWidth2, children: [
5660
+ return /* @__PURE__ */ jsxs6(Box8, { flexDirection: "row", width: safeWidth2, children: [
5633
5661
  filled > 0 ? /* @__PURE__ */ jsx8(Text8, { color: usageColor(pct), children: "\u2588".repeat(filled) }) : null,
5634
5662
  empty > 0 ? /* @__PURE__ */ jsx8(Text8, { color: theme.inactive, children: "\u2591".repeat(empty) }) : null
5635
5663
  ] });
@@ -5641,7 +5669,7 @@ function DetailLine({ label, value, columns }) {
5641
5669
  const innerWidth = Math.max(24, Math.floor(columns || 80) - 4);
5642
5670
  const labelWidth = 10;
5643
5671
  const valueWidth = Math.max(0, innerWidth - labelWidth - 2);
5644
- return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", width: "100%", children: [
5672
+ return /* @__PURE__ */ jsxs6(Box8, { flexDirection: "row", width: "100%", children: [
5645
5673
  /* @__PURE__ */ jsx8(Text8, { color: theme.subtle, children: padCells3(truncateText3(label, labelWidth), labelWidth) }),
5646
5674
  /* @__PURE__ */ jsx8(Text8, { color: theme.inactive, children: " " }),
5647
5675
  /* @__PURE__ */ jsx8(Text8, { color: theme.text, children: truncateText3(value, valueWidth) })
@@ -5662,7 +5690,7 @@ function CategoryItem({ label, tokens, total, width }) {
5662
5690
  const tokenWidth = 7;
5663
5691
  const barWidth = Math.max(6, Math.min(20, width - labelWidth - pctWidth - tokenWidth - 4));
5664
5692
  const pct = percent(tokens, total);
5665
- return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", width, children: [
5693
+ return /* @__PURE__ */ jsxs6(Box8, { flexDirection: "row", width, children: [
5666
5694
  /* @__PURE__ */ jsx8(Text8, { color: theme.subtle, children: padCells3(truncateText3(label, labelWidth), labelWidth) }),
5667
5695
  /* @__PURE__ */ jsx8(Text8, { color: theme.inactive, children: " " }),
5668
5696
  /* @__PURE__ */ jsx8(Text8, { color: usageColor(pct), children: padCells3(percentLabel(tokens, total), pctWidth) }),
@@ -5682,7 +5710,7 @@ function CategoryGrid({ categories, columns, total }) {
5682
5710
  const rightWidth = innerWidth - gap - leftWidth;
5683
5711
  const pairs = [];
5684
5712
  for (let i = 0; i < categories.length; i += 2) pairs.push(categories.slice(i, i + 2));
5685
- return /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", width: "100%", children: pairs.map((pair, index) => /* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", width: "100%", children: [
5713
+ return /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", width: "100%", children: pairs.map((pair, index) => /* @__PURE__ */ jsxs6(Box8, { flexDirection: "row", width: "100%", children: [
5686
5714
  /* @__PURE__ */ jsx8(CategoryItem, { ...pair[0], total, width: leftWidth }),
5687
5715
  /* @__PURE__ */ jsx8(Text8, { children: " ".repeat(gap) }),
5688
5716
  pair[1] ? /* @__PURE__ */ jsx8(CategoryItem, { ...pair[1], total, width: rightWidth }) : null
@@ -5742,20 +5770,20 @@ function ContextUsageView({ detail, columns }) {
5742
5770
  { label: "Overhead", tokens: finiteNumber(request.overheadTokens), meta: "request frame" },
5743
5771
  { label: "Tool I/O", tokens: semanticTokens(semantic, ["toolResults"]), meta: `${toolIo.calls || 0} calls \xB7 ${toolIo.results || 0} results` }
5744
5772
  ];
5745
- return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", width: "100%", children: [
5746
- /* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", width: "100%", children: [
5773
+ return /* @__PURE__ */ jsxs6(Box8, { flexDirection: "column", width: "100%", children: [
5774
+ /* @__PURE__ */ jsxs6(Box8, { flexDirection: "row", width: "100%", children: [
5747
5775
  /* @__PURE__ */ jsx8(Text8, { color: usageColor(usedPct), bold: true, children: padCells3(pctText, Math.min(10, innerWidth)) }),
5748
5776
  /* @__PURE__ */ jsx8(Text8, { color: theme.inactive, children: " " }),
5749
5777
  /* @__PURE__ */ jsx8(ProgressBar, { value: usedTokens, total: windowTokens, width: barWidth }),
5750
5778
  /* @__PURE__ */ jsx8(Text8, { color: theme.inactive, children: " " }),
5751
5779
  /* @__PURE__ */ jsx8(Text8, { color: theme.text, children: truncateText3(summaryText, Math.max(0, innerWidth - Math.min(10, innerWidth) - barWidth - 3)) })
5752
5780
  ] }),
5753
- /* @__PURE__ */ jsxs7(Box8, { marginTop: 1, flexDirection: "column", width: "100%", children: [
5781
+ /* @__PURE__ */ jsxs6(Box8, { marginTop: 1, flexDirection: "column", width: "100%", children: [
5754
5782
  /* @__PURE__ */ jsx8(DetailLine, { label: "Source", value: sourceLine, columns }),
5755
5783
  /* @__PURE__ */ jsx8(DetailLine, { label: "Compaction", value: compactionLine, columns }),
5756
5784
  /* @__PURE__ */ jsx8(DetailLine, { label: "API/cache", value: apiLine, columns })
5757
5785
  ] }),
5758
- /* @__PURE__ */ jsxs7(Box8, { marginTop: 1, flexDirection: "column", width: "100%", children: [
5786
+ /* @__PURE__ */ jsxs6(Box8, { marginTop: 1, flexDirection: "column", width: "100%", children: [
5759
5787
  /* @__PURE__ */ jsx8(Text8, { color: theme.subtle, children: "Context mix" }),
5760
5788
  /* @__PURE__ */ jsx8(Box8, { marginTop: 1, flexDirection: "column", width: "100%", children: /* @__PURE__ */ jsx8(CategoryGrid, { categories, columns, total: windowTokens }) })
5761
5789
  ] })
@@ -5773,7 +5801,7 @@ function ContextPanel({ rows, title = "Context Usage", columns = 80, fillHeight
5773
5801
  String(description || (isContextUsage ? "Live context window usage by category." : "")).replace(/\s+/g, " ").trim(),
5774
5802
  Math.max(0, columns - 4)
5775
5803
  );
5776
- return /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", flexShrink: 0, width: "100%", height: fillHeight ? "100%" : void 0, children: /* @__PURE__ */ jsxs7(
5804
+ return /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", flexShrink: 0, width: "100%", height: fillHeight ? "100%" : void 0, children: /* @__PURE__ */ jsxs6(
5777
5805
  Box8,
5778
5806
  {
5779
5807
  flexDirection: "column",
@@ -5783,16 +5811,16 @@ function ContextPanel({ rows, title = "Context Usage", columns = 80, fillHeight
5783
5811
  width: "100%",
5784
5812
  height: fillHeight ? "100%" : void 0,
5785
5813
  children: [
5786
- /* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", justifyContent: "space-between", children: [
5814
+ /* @__PURE__ */ jsxs6(Box8, { flexDirection: "row", justifyContent: "space-between", children: [
5787
5815
  /* @__PURE__ */ jsx8(Text8, { color: theme.panelTitle, children: title }),
5788
5816
  /* @__PURE__ */ jsx8(Text8, { color: theme.subtle, children: "Esc back" })
5789
5817
  ] }),
5790
5818
  /* @__PURE__ */ jsx8(Text8, { children: " " }),
5791
5819
  /* @__PURE__ */ jsx8(Text8, { color: theme.subtle, children: panelDescription || " " }),
5792
5820
  /* @__PURE__ */ jsx8(Text8, { children: " " }),
5793
- isContextUsage ? /* @__PURE__ */ jsx8(ContextUsageView, { detail, columns }) : safeRows.map((row) => /* @__PURE__ */ jsxs7(Box8, { flexDirection: "row", width: "100%", children: [
5821
+ isContextUsage ? /* @__PURE__ */ jsx8(ContextUsageView, { detail, columns }) : safeRows.map((row) => /* @__PURE__ */ jsxs6(Box8, { flexDirection: "row", width: "100%", children: [
5794
5822
  /* @__PURE__ */ jsx8(Text8, { color: theme.inactive, children: padCells3(truncateText3(row.label, labelWidth), labelWidth) }),
5795
- /* @__PURE__ */ jsxs7(Text8, { color: theme.text, children: [
5823
+ /* @__PURE__ */ jsxs6(Text8, { color: theme.text, children: [
5796
5824
  " ",
5797
5825
  truncateText3(row.description, valueWidth)
5798
5826
  ] })
@@ -5806,7 +5834,7 @@ function ContextPanel({ rows, title = "Context Usage", columns = 80, fillHeight
5806
5834
  import React9, { useEffect as useEffect4, useState as useState4 } from "react";
5807
5835
  import { Box as Box9, Text as Text9, useInput as useInput3 } from "../../../vendor/ink/build/index.js";
5808
5836
  import stringWidth5 from "string-width";
5809
- import { Fragment as Fragment4, jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
5837
+ import { Fragment as Fragment4, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
5810
5838
  var PROVIDER_LABEL_WIDTH = 28;
5811
5839
  var CREDIT_LABEL = "Credit";
5812
5840
  var STATUS_SEPARATOR2 = " \u2502 ";
@@ -6053,7 +6081,7 @@ function UsagePanel({ dashboard, loading = false, columns = 80, fillHeight = fal
6053
6081
  setScrollOffset(maxScrollOffset);
6054
6082
  }
6055
6083
  });
6056
- return /* @__PURE__ */ jsx9(Box9, { flexDirection: "column", flexShrink: 0, width: "100%", height: fillHeight ? "100%" : void 0, children: /* @__PURE__ */ jsxs8(
6084
+ return /* @__PURE__ */ jsx9(Box9, { flexDirection: "column", flexShrink: 0, width: "100%", height: fillHeight ? "100%" : void 0, children: /* @__PURE__ */ jsxs7(
6057
6085
  Box9,
6058
6086
  {
6059
6087
  flexDirection: "column",
@@ -6063,15 +6091,15 @@ function UsagePanel({ dashboard, loading = false, columns = 80, fillHeight = fal
6063
6091
  width: "100%",
6064
6092
  height: fillHeight ? "100%" : void 0,
6065
6093
  children: [
6066
- /* @__PURE__ */ jsxs8(Box9, { flexDirection: "row", justifyContent: "space-between", marginBottom: 0, children: [
6094
+ /* @__PURE__ */ jsxs7(Box9, { flexDirection: "row", justifyContent: "space-between", marginBottom: 0, children: [
6067
6095
  /* @__PURE__ */ jsx9(Text9, { color: theme.panelTitle, children: panelTitle }),
6068
6096
  /* @__PURE__ */ jsx9(Text9, { color: theme.subtle, children: helpText })
6069
6097
  ] }),
6070
- (isLoading || isChecking) && rows.length === 0 ? /* @__PURE__ */ jsxs8(Fragment4, { children: [
6098
+ (isLoading || isChecking) && rows.length === 0 ? /* @__PURE__ */ jsxs7(Fragment4, { children: [
6071
6099
  /* @__PURE__ */ jsx9(Text9, { children: " " }),
6072
6100
  /* @__PURE__ */ jsx9(Text9, { color: theme.statusSubtle, children: "Checking providers..." }),
6073
6101
  /* @__PURE__ */ jsx9(Text9, { children: " " })
6074
- ] }) : /* @__PURE__ */ jsxs8(Fragment4, { children: [
6102
+ ] }) : /* @__PURE__ */ jsxs7(Fragment4, { children: [
6075
6103
  /* @__PURE__ */ jsx9(Text9, { children: " " }),
6076
6104
  /* @__PURE__ */ jsx9(Text9, { color: theme.text, children: panelDescription }),
6077
6105
  /* @__PURE__ */ jsx9(Text9, { children: " " }),
@@ -6079,8 +6107,8 @@ function UsagePanel({ dashboard, loading = false, columns = 80, fillHeight = fal
6079
6107
  const provider = padCells4(truncate(row.label || row.id, labelWidth), labelWidth);
6080
6108
  const index = padCells4(`${scrollOffset + idx + 1}.`, indexWidth);
6081
6109
  const statusParts = fitParts(rowStatusParts(row, columns, statusWidth), statusWidth);
6082
- return /* @__PURE__ */ jsxs8(Box9, { flexDirection: "row", width: "100%", children: [
6083
- /* @__PURE__ */ jsxs8(Text9, { color: theme.subtle, children: [
6110
+ return /* @__PURE__ */ jsxs7(Box9, { flexDirection: "row", width: "100%", children: [
6111
+ /* @__PURE__ */ jsxs7(Text9, { color: theme.subtle, children: [
6084
6112
  index,
6085
6113
  indexWidth > 0 ? " " : ""
6086
6114
  ] }),
@@ -6097,10 +6125,10 @@ function UsagePanel({ dashboard, loading = false, columns = 80, fillHeight = fal
6097
6125
  }
6098
6126
 
6099
6127
  // src/tui/components/TextEntryPanel.jsx
6100
- import React10, { useEffect as useEffect5, useLayoutEffect as useLayoutEffect2, useRef as useRef4, useState as useState5 } from "react";
6128
+ import React10, { useEffect as useEffect5, useLayoutEffect as useLayoutEffect2, useRef as useRef5, useState as useState5 } from "react";
6101
6129
  import { Box as Box10, Text as Text10, useInput as useInput4, usePaste as usePaste2, useStdin as useStdin2 } from "../../../vendor/ink/build/index.js";
6102
6130
  import stringWidth6 from "string-width";
6103
- import { Fragment as Fragment5, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
6131
+ import { Fragment as Fragment5, jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
6104
6132
  function insertText2(draft, input) {
6105
6133
  if (!input) return draft;
6106
6134
  return replaceSelection(draft, input);
@@ -6109,7 +6137,7 @@ function renderSelectedText2(displayValue, range, trailingSpace = false) {
6109
6137
  if (!range) return trailingSpace ? `${displayValue} ` : displayValue;
6110
6138
  const start = Math.max(0, Math.min(displayValue.length, range.start));
6111
6139
  const end = Math.max(start, Math.min(displayValue.length, range.end));
6112
- return /* @__PURE__ */ jsxs9(Fragment5, { children: [
6140
+ return /* @__PURE__ */ jsxs8(Fragment5, { children: [
6113
6141
  start > 0 ? displayValue.slice(0, start) : null,
6114
6142
  end > start ? /* @__PURE__ */ jsx10(Text10, { color: theme.selectionText, backgroundColor: theme.selectionBackground, children: displayValue.slice(start, end) }) : null,
6115
6143
  displayValue.slice(end),
@@ -6221,11 +6249,11 @@ function TextEntryPanel({
6221
6249
  }) {
6222
6250
  const [draft, setDraft] = useState5(() => ({ value: String(initialValue || ""), cursor: String(initialValue || "").length, selectionAnchor: null }));
6223
6251
  const [, bumpCursorAnchorEpoch] = useState5(0);
6224
- const draftRef = useRef4(draft);
6225
- const boxRef = useRef4(null);
6226
- const cursorEnabledRef = useRef4(false);
6227
- const contentWidthRef = useRef4(80);
6228
- const preferredColumnRef = useRef4(null);
6252
+ const draftRef = useRef5(draft);
6253
+ const boxRef = useRef5(null);
6254
+ const cursorEnabledRef = useRef5(false);
6255
+ const contentWidthRef = useRef5(80);
6256
+ const preferredColumnRef = useRef5(null);
6229
6257
  const { isRawModeSupported } = useStdin2();
6230
6258
  draftRef.current = draft;
6231
6259
  const flushImmediate = () => {
@@ -6466,19 +6494,19 @@ function TextEntryPanel({
6466
6494
  const helpText = `Enter to ${action} \xB7 Esc to cancel`;
6467
6495
  const hintText = truncateText4(singleLine(hint), Math.max(0, columns - 4));
6468
6496
  const detailText = String(detail || "").trim();
6469
- return /* @__PURE__ */ jsx10(Box10, { flexDirection: "column", flexShrink: 0, width: "100%", children: /* @__PURE__ */ jsxs9(Box10, { borderStyle: "round", borderColor: theme.promptBorder, paddingX: 1, width: "100%", flexDirection: "column", children: [
6470
- /* @__PURE__ */ jsxs9(Box10, { flexDirection: "row", justifyContent: "space-between", children: [
6497
+ return /* @__PURE__ */ jsx10(Box10, { flexDirection: "column", flexShrink: 0, width: "100%", children: /* @__PURE__ */ jsxs8(Box10, { borderStyle: "round", borderColor: theme.promptBorder, paddingX: 1, width: "100%", flexDirection: "column", children: [
6498
+ /* @__PURE__ */ jsxs8(Box10, { flexDirection: "row", justifyContent: "space-between", children: [
6471
6499
  /* @__PURE__ */ jsx10(Text10, { color: theme.panelTitle, children: title }),
6472
6500
  /* @__PURE__ */ jsx10(Text10, { color: theme.subtle, children: helpText })
6473
6501
  ] }),
6474
6502
  /* @__PURE__ */ jsx10(Text10, { children: " " }),
6475
6503
  /* @__PURE__ */ jsx10(Text10, { color: theme.subtle, children: hintText || " " }),
6476
- detailText ? /* @__PURE__ */ jsxs9(Box10, { flexDirection: "column", width: "100%", children: [
6504
+ detailText ? /* @__PURE__ */ jsxs8(Box10, { flexDirection: "column", width: "100%", children: [
6477
6505
  /* @__PURE__ */ jsx10(Text10, { children: " " }),
6478
6506
  /* @__PURE__ */ jsx10(Text10, { color: theme.subtle, wrap: "wrap", children: detailText })
6479
6507
  ] }) : null,
6480
6508
  /* @__PURE__ */ jsx10(Text10, { children: " " }),
6481
- /* @__PURE__ */ jsxs9(Box10, { ref: boxRef, flexDirection: "row", width: "100%", backgroundColor: surfaceBackground(), children: [
6509
+ /* @__PURE__ */ jsxs8(Box10, { ref: boxRef, flexDirection: "row", width: "100%", backgroundColor: surfaceBackground(), children: [
6482
6510
  /* @__PURE__ */ jsx10(Text10, { color: theme.inactive, children: promptLabel }),
6483
6511
  /* @__PURE__ */ jsx10(Text10, { color: theme.text, wrap: "truncate", children: renderedValue })
6484
6512
  ] })
@@ -8593,7 +8621,7 @@ function promptContentRows(value, contentColumns) {
8593
8621
  }
8594
8622
 
8595
8623
  // src/tui/app/use-mouse-input.mjs
8596
- import { useCallback as useCallback2, useEffect as useEffect6, useRef as useRef5 } from "react";
8624
+ import { useCallback as useCallback2, useEffect as useEffect6, useRef as useRef6 } from "react";
8597
8625
 
8598
8626
  // src/tui/markdown/measure-rendered-rows.mjs
8599
8627
  import stripAnsi4 from "strip-ansi";
@@ -10863,14 +10891,18 @@ function estimateTranscriptItemRowsCached(item, columns, toolOutputExpanded) {
10863
10891
  transcriptRowsCache.set(item, { id: item.id, kind: item.kind, variantKey, columns, toolExpanded, rows });
10864
10892
  return rows;
10865
10893
  }
10866
- function buildTranscriptRowIndex(items, { columns = 80, toolOutputExpanded = false } = {}) {
10894
+ function buildTranscriptRowIndex(items, {
10895
+ columns = 80,
10896
+ toolOutputExpanded = false,
10897
+ suppressMeasuredRowHeights = false
10898
+ } = {}) {
10867
10899
  const allItems = Array.isArray(items) ? items : [];
10868
10900
  const rows = new Array(allItems.length);
10869
10901
  const prefixRows = new Array(allItems.length + 1);
10870
10902
  prefixRows[0] = 0;
10871
10903
  for (let i = 0; i < allItems.length; i++) {
10872
10904
  const item = allItems[i];
10873
- const measured = measuredTranscriptRows(item, columns, toolOutputExpanded);
10905
+ const measured = suppressMeasuredRowHeights ? null : measuredTranscriptRows(item, columns, toolOutputExpanded);
10874
10906
  const rowCount = measured != null ? measured : estimateTranscriptItemRowsCached(item, columns, toolOutputExpanded);
10875
10907
  rows[i] = rowCount;
10876
10908
  prefixRows[i + 1] = prefixRows[i] + rowCount;
@@ -10985,7 +11017,7 @@ function useMouseInput({
10985
11017
  setSlashIndex,
10986
11018
  setMeasuredRowsVersion
10987
11019
  }) {
10988
- const mouseZoomPassthroughTimerRef = useRef5(null);
11020
+ const mouseZoomPassthroughTimerRef = useRef6(null);
10989
11021
  const passthroughCtrlWheelZoom = useCallback2(() => {
10990
11022
  if (!stdout?.write) return;
10991
11023
  try {
@@ -11266,7 +11298,7 @@ function useMouseInput({
11266
11298
  }
11267
11299
 
11268
11300
  // src/tui/app/use-transcript-scroll.mjs
11269
- import { useCallback as useCallback3, useEffect as useEffect7, useRef as useRef6 } from "react";
11301
+ import { useCallback as useCallback3, useEffect as useEffect7, useRef as useRef7 } from "react";
11270
11302
  function useTranscriptScroll({
11271
11303
  store,
11272
11304
  frameColumns,
@@ -11286,10 +11318,10 @@ function useTranscriptScroll({
11286
11318
  selectionLayoutRef,
11287
11319
  selectionTextRef
11288
11320
  }) {
11289
- const scrollAnimationRef = useRef6(null);
11290
- const selectionPaintRef = useRef6({ t: 0, rect: null, pending: null, timer: null });
11291
- const scrollCoalesceRef = useRef6({ pendingRows: 0, timer: null });
11292
- const selectionTextTimerRef = useRef6(null);
11321
+ const scrollAnimationRef = useRef7(null);
11322
+ const selectionPaintRef = useRef7({ t: 0, rect: null, pending: null, timer: null });
11323
+ const scrollCoalesceRef = useRef7({ pendingRows: 0, timer: null });
11324
+ const selectionTextTimerRef = useRef7(null);
11293
11325
  const stopSmoothScroll = useCallback3(() => {
11294
11326
  if (!scrollAnimationRef.current) return;
11295
11327
  clearInterval(scrollAnimationRef.current);
@@ -11474,7 +11506,7 @@ function useTranscriptScroll({
11474
11506
  if (lr != null && Number.isFinite(lr.x2)) return Math.max(0, lr.x2);
11475
11507
  return Math.max(0, frameColumns - 1);
11476
11508
  }, [store, frameColumns]);
11477
- const gridSelectionActiveRef = useRef6(() => {
11509
+ const gridSelectionActiveRef = useRef7(() => {
11478
11510
  const drag = dragRef.current;
11479
11511
  if (!drag || drag.active) return false;
11480
11512
  if (drag.region !== "transcript" && drag.region !== "status") return false;
@@ -11608,7 +11640,9 @@ function useTranscriptScroll({
11608
11640
  row -= 1;
11609
11641
  } else if (region === "transcript") {
11610
11642
  const beforeTarget = scrollTargetRef.current;
11611
- scrollTranscriptRows(1);
11643
+ const slack = Math.max(0, Number(transcriptBottomSlackRowsRef.current) || 0);
11644
+ const deltaRows = beforeTarget <= slack ? slack + 1 - beforeTarget : 1;
11645
+ scrollTranscriptRows(deltaRows);
11612
11646
  if (scrollTargetRef.current !== beforeTarget) {
11613
11647
  scrolledEdge = true;
11614
11648
  const shiftedRect = dragRef.current.rect;
@@ -11674,7 +11708,7 @@ function useTranscriptScroll({
11674
11708
  }
11675
11709
 
11676
11710
  // src/tui/app/use-transcript-window.mjs
11677
- import { useCallback as useCallback4, useEffect as useEffect8, useLayoutEffect as useLayoutEffect3, useMemo, useRef as useRef7, useState as useState6 } from "react";
11711
+ import { useCallback as useCallback4, useEffect as useEffect8, useLayoutEffect as useLayoutEffect3, useMemo, useRef as useRef8, useState as useState6 } from "react";
11678
11712
  function useTranscriptWindow({
11679
11713
  items,
11680
11714
  themeEpoch,
@@ -11703,12 +11737,12 @@ function useTranscriptWindow({
11703
11737
  measuredRowsVersion,
11704
11738
  setMeasuredRowsVersion
11705
11739
  }) {
11706
- const transcriptTotalRowsRef = useRef7(0);
11707
- const preservedScrollDeltaRef = useRef7(0);
11708
- const prevViewportGeomRef = useRef7({ contentHeight: 0, floatingPanelRows: 0 });
11709
- const transcriptItemElsRef = useRef7(/* @__PURE__ */ new Map());
11710
- const transcriptMeasureRefCache = useRef7(/* @__PURE__ */ new Map());
11711
- const transcriptMeasureItemsRef = useRef7(/* @__PURE__ */ new Map());
11740
+ const transcriptTotalRowsRef = useRef8(0);
11741
+ const preservedScrollDeltaRef = useRef8(0);
11742
+ const prevViewportGeomRef = useRef8({ contentHeight: 0, floatingPanelRows: 0 });
11743
+ const transcriptItemElsRef = useRef8(/* @__PURE__ */ new Map());
11744
+ const transcriptMeasureRefCache = useRef8(/* @__PURE__ */ new Map());
11745
+ const transcriptMeasureItemsRef = useRef8(/* @__PURE__ */ new Map());
11712
11746
  const transcriptMeasureRef = useCallback4((item) => {
11713
11747
  if (!TRANSCRIPT_MEASURED_ROWS || !item || item.id == null) return void 0;
11714
11748
  if (shouldSuppressFullyFailedToolItem(item)) {
@@ -11736,11 +11770,21 @@ function useTranscriptWindow({
11736
11770
  () => transcriptStructureSignature(items, frameColumns, toolOutputExpanded),
11737
11771
  [items, frameColumns, toolOutputExpanded]
11738
11772
  );
11773
+ const transcriptStreamingActive = (items || []).some(
11774
+ (item) => item?.kind === "assistant" && item?.streaming
11775
+ );
11776
+ const scrolledUpRowsForPin = Math.max(0, Number(scrollTargetRef.current) || 0);
11777
+ const transcriptPinnedForStreaming = followingRef.current || scrolledUpRowsForPin <= transcriptBottomSlackRows;
11778
+ const scrolledUpForStreamingMeasure = scrolledUpRowsForPin > transcriptBottomSlackRows;
11779
+ const prevEstimateGeometry = transcriptGeomRef.current?.suppressMeasuredRowHeights === true;
11780
+ const hasStreamingReadingAnchor = !!transcriptAnchorRef.current || transcriptAnchorDirtyRef.current;
11781
+ const suppressMeasuredRowHeights = transcriptStreamingActive && (transcriptPinnedForStreaming || scrolledUpForStreamingMeasure && hasStreamingReadingAnchor || scrolledUpForStreamingMeasure && prevEstimateGeometry && !transcriptPinnedForStreaming);
11739
11782
  const transcriptRowIndex = useMemo(() => buildTranscriptRowIndex(items, {
11740
11783
  columns: frameColumns,
11741
- toolOutputExpanded
11784
+ toolOutputExpanded,
11785
+ suppressMeasuredRowHeights
11742
11786
  // eslint-disable-next-line react-hooks/exhaustive-deps -- intentional: sig captures the relevant item changes; measuredRowsVersion folds in app-level measured height corrections
11743
- }), [transcriptStructureSig, measuredRowsVersion]);
11787
+ }), [transcriptStructureSig, measuredRowsVersion, suppressMeasuredRowHeights]);
11744
11788
  const hasReadingAnchor = !!transcriptAnchorRef.current && !transcriptAnchorDirtyRef.current;
11745
11789
  const scrolledUpRows = Math.max(0, Number(scrollTargetRef.current) || 0);
11746
11790
  const scrolledUp = scrolledUpRows > transcriptBottomSlackRows;
@@ -11849,7 +11893,8 @@ function useTranscriptWindow({
11849
11893
  // frame to reconstruct the exact top-edge row that was on screen, so the
11850
11894
  // capture matches what the user saw rather than the stale scrollOffset
11851
11895
  // state. Bottom-relative, matching transcriptRenderWindow's window math.
11852
- renderOffset: Math.max(0, Number(renderScrollOffset) || 0)
11896
+ renderOffset: Math.max(0, Number(renderScrollOffset) || 0),
11897
+ suppressMeasuredRowHeights
11853
11898
  };
11854
11899
  const transcriptVisibleItems = (items || []).slice(
11855
11900
  transcriptWindow.startIndex,
@@ -11869,6 +11914,7 @@ function useTranscriptWindow({
11869
11914
  useLayoutEffect3(() => {
11870
11915
  if (!TRANSCRIPT_MEASURED_ROWS) return;
11871
11916
  if (dragRef.current.active) return;
11917
+ if (transcriptStreamingActive && transcriptTailPinned) return;
11872
11918
  const els = transcriptItemElsRef.current;
11873
11919
  if (!els || els.size === 0) return;
11874
11920
  const liveItems = transcriptMeasureItemsRef.current;
@@ -12073,10 +12119,10 @@ function workflowDisplayName(workflow = {}) {
12073
12119
  return clean(workflow?.name || workflow?.id) || "Default";
12074
12120
  }
12075
12121
  function workflowSwitchNotice(workflow = {}) {
12076
- return "Workflow updated \xB7 new sessions";
12122
+ return "Workflow saved \xB7 new sessions";
12077
12123
  }
12078
12124
  function modelSwitchNotice() {
12079
- return "Model updated \xB7 new sessions";
12125
+ return "Model saved \xB7 new sessions";
12080
12126
  }
12081
12127
  function compactJson(value, max = 180) {
12082
12128
  let text = "";
@@ -12139,12 +12185,19 @@ function formatSessionMessageCount(count) {
12139
12185
  function fitLine(value, columns, reserve = 4) {
12140
12186
  const text = String(value || "");
12141
12187
  const width = Math.max(1, Number(columns || 80) - reserve);
12142
- return text.length > width ? `${text.slice(0, Math.max(1, width - 1))}\u2026` : text;
12188
+ if (stringWidth8(text) <= width) return text;
12189
+ if (width <= 1) return "\u2026".repeat(Math.max(0, width));
12190
+ let out = "";
12191
+ for (const ch of text) {
12192
+ if (stringWidth8(`${out}${ch}\u2026`) > width) break;
12193
+ out += ch;
12194
+ }
12195
+ return `${out}\u2026`;
12143
12196
  }
12144
12197
  function centerLine(value, columns, reserve = 0) {
12145
12198
  const text = fitLine(value, columns, reserve);
12146
12199
  const width = Math.max(1, Number(columns || 80) - reserve);
12147
- const pad = Math.max(0, Math.floor((width - text.length) / 2));
12200
+ const pad = Math.max(0, Math.floor((width - stringWidth8(text)) / 2));
12148
12201
  return `${" ".repeat(pad)}${text}`;
12149
12202
  }
12150
12203
  var WELCOME_PROMPT_HINTS = [
@@ -12280,6 +12333,7 @@ var modelFamily = (m) => {
12280
12333
  var modelContextWindow = (m) => {
12281
12334
  const raw = Number(m?.contextWindow);
12282
12335
  const n = Number.isFinite(raw) && raw > 0 ? raw : 0;
12336
+ if (n > 0) return n;
12283
12337
  const provider = String(m?.provider || "").toLowerCase();
12284
12338
  const id = String(m?.id || "").toLowerCase();
12285
12339
  const version = parsedModelVersion(id);
@@ -12294,7 +12348,8 @@ var formatContextWindow = (tokens) => {
12294
12348
  if (!Number.isFinite(n) || n <= 0) return "";
12295
12349
  if (n >= 1e6) {
12296
12350
  const m = n / 1e6;
12297
- return `${Number.isInteger(m) ? m.toFixed(0) : m.toFixed(1)}M Context`;
12351
+ const label = Number.isInteger(m) ? m.toFixed(0) : m.toFixed(3).replace(/0+$/, "").replace(/\.$/, "");
12352
+ return `${label}M Context`;
12298
12353
  }
12299
12354
  return `${Math.round(n / 1e3)}k Context`;
12300
12355
  };
@@ -13617,14 +13672,13 @@ function createMaintenancePickers({
13617
13672
  const openAutoClearPicker = (options = {}) => {
13618
13673
  const returnTo = typeof options.returnTo === "function" ? options.returnTo : null;
13619
13674
  const HOUR_MS = 60 * 60 * 1e3;
13620
- const IDLE_WINDOWS = [
13621
- { value: "5m", label: "5m", ms: 5 * 60 * 1e3 },
13622
- { value: "10m", label: "10m", ms: 10 * 60 * 1e3 },
13623
- { value: "30m", label: "30m", ms: 30 * 60 * 1e3 },
13624
- { value: "1h", label: "1h", ms: HOUR_MS },
13625
- { value: "6h", label: "6h", ms: 6 * HOUR_MS },
13626
- { value: "24h", label: "24h", ms: 24 * HOUR_MS }
13627
- ];
13675
+ const formatDurationInput = (ms) => {
13676
+ const value = Math.max(0, Math.round(Number(ms) || 0));
13677
+ if (value > 0 && value % HOUR_MS === 0) return `${value / HOUR_MS}h`;
13678
+ if (value > 0 && value % 6e4 === 0) return `${value / 6e4}m`;
13679
+ if (value > 0 && value % 1e3 === 0) return `${value / 1e3}s`;
13680
+ return `${value}ms`;
13681
+ };
13628
13682
  const readCurrent = () => {
13629
13683
  try {
13630
13684
  return store.getAutoClear?.() || null;
@@ -13645,6 +13699,49 @@ function createMaintenancePickers({
13645
13699
  }
13646
13700
  render2();
13647
13701
  };
13702
+ const openProviderDurationEditor = (entry) => {
13703
+ if (!entry?.provider) return;
13704
+ setPicker(null);
13705
+ setSettingsPrompt({
13706
+ kind: "autoclear-provider",
13707
+ label: `Auto-clear \xB7 ${entry.provider}`,
13708
+ hint: `Type a duration like 10m, 1h, or 24h. Empty resets to built-in ${formatDurationInput(entry.builtInMs)}.`,
13709
+ initialValue: formatDurationInput(entry.idleMs),
13710
+ provider: entry.provider,
13711
+ builtInMs: entry.builtInMs,
13712
+ returnTo
13713
+ });
13714
+ };
13715
+ const renderAdvanced = () => {
13716
+ const current = readCurrent();
13717
+ const provider = current?.provider || "default";
13718
+ const providerDefaults = Array.isArray(current?.providerDefaults) ? current.providerDefaults : [];
13719
+ const items = providerDefaults.map((entry) => ({
13720
+ value: `provider:${entry.provider}`,
13721
+ label: entry.provider,
13722
+ marker: entry.provider === provider ? "\u2713" : "",
13723
+ markerColor: theme2.success,
13724
+ meta: `${formatDuration2(entry.idleMs)}${entry.custom ? " custom" : ""}`,
13725
+ description: `Default idle window for ${entry.provider}. Enter to edit as text.`,
13726
+ _action: "provider-default",
13727
+ _entry: entry
13728
+ }));
13729
+ setPicker({
13730
+ title: "Auto-clear \xB7 Advanced",
13731
+ description: "Provider default idle windows. Enter edits the duration text.",
13732
+ help: "\u2191/\u2193 Select \xB7 Enter Edit \xB7 Esc Back",
13733
+ indexMode: "always",
13734
+ labelWidth: 18,
13735
+ metaWidth: 18,
13736
+ items,
13737
+ onSelect: (_value, item) => {
13738
+ if (item?._action === "provider-default") openProviderDurationEditor(item._entry);
13739
+ },
13740
+ onCancel: () => {
13741
+ render2();
13742
+ }
13743
+ });
13744
+ };
13648
13745
  const render2 = () => {
13649
13746
  const current = readCurrent();
13650
13747
  const enabled = current?.enabled !== false;
@@ -13661,28 +13758,19 @@ function createMaintenancePickers({
13661
13758
  _action: "toggle"
13662
13759
  },
13663
13760
  {
13664
- value: "default",
13665
- label: "Default",
13761
+ value: "advanced",
13762
+ label: "Advanced",
13666
13763
  marker: !custom ? "\u2713" : "",
13667
13764
  markerColor: theme2.success,
13668
- meta: `Provider default \xB7 ${formatDuration2(providerDefault)}`,
13669
- description: `Uses the ${current?.provider || "current"} provider default idle window.`,
13670
- _action: "default"
13671
- },
13672
- ...IDLE_WINDOWS.map((win) => ({
13673
- value: win.value,
13674
- label: win.label,
13675
- marker: custom && idleMs === win.ms ? "\u2713" : "",
13676
- markerColor: theme2.success,
13677
- description: `Clear idle sessions after ${win.label}.`,
13678
- _action: "window",
13679
- _idleMs: win.ms
13680
- }))
13765
+ meta: `${current?.provider || "current"} \xB7 ${formatDuration2(providerDefault)}`,
13766
+ description: "Edit provider-paired default idle windows as text.",
13767
+ _action: "advanced"
13768
+ }
13681
13769
  ];
13682
13770
  setPicker({
13683
13771
  title: "Auto-clear",
13684
13772
  description: `Clear idle context after ${enabled ? formatDuration2(idleMs) : "never"} \xB7 lead cache TTL ${cacheTtlLabel}.`,
13685
- help: "\u2191/\u2193 Select \xB7 \u2190/\u2192 Toggle On/Off \xB7 Enter Apply \xB7 Esc Close",
13773
+ help: "\u2191/\u2193 Select \xB7 \u2190/\u2192 Toggle On/Off \xB7 Enter Open/Toggle \xB7 Esc Close",
13686
13774
  indexMode: "always",
13687
13775
  labelWidth: 10,
13688
13776
  items,
@@ -13695,10 +13783,8 @@ function createMaintenancePickers({
13695
13783
  onSelect: (_value, item) => {
13696
13784
  if (item?._action === "toggle") {
13697
13785
  applyAutoClear({ enabled: !enabled });
13698
- } else if (item?._action === "default") {
13699
- applyAutoClear({ reset: true });
13700
- } else if (item?._action === "window") {
13701
- applyAutoClear({ enabled: true, idleMs: item._idleMs });
13786
+ } else if (item?._action === "advanced") {
13787
+ renderAdvanced();
13702
13788
  }
13703
13789
  },
13704
13790
  onCancel: () => {
@@ -13713,7 +13799,8 @@ function createMaintenancePickers({
13713
13799
  setSettingsPrompt(null);
13714
13800
  setContextPanel(null);
13715
13801
  closeUsagePanel();
13716
- render2();
13802
+ if (options.advanced === true) renderAdvanced();
13803
+ else render2();
13717
13804
  };
13718
13805
  const openProfilePicker = (options = {}) => {
13719
13806
  const returnTo = typeof options.returnTo === "function" ? options.returnTo : null;
@@ -14136,7 +14223,7 @@ function createChannelPickers({
14136
14223
  const isTelegram = backend === "telegram";
14137
14224
  const activeBackend = setup.backend || "discord";
14138
14225
  const tokenDescription = isTelegram ? `${setup.telegram?.status ?? "Off"}${setup.telegram?.problem ? " \xB7 Invalid" : ""}` : `${setup.discord.status}${setup.discord.problem ? " \xB7 Invalid" : ""}`;
14139
- const mainEntry = (setup.channels || []).find((ch) => ch.main) || (setup.channels || []).find((ch) => ch.name === "main");
14226
+ const mainEntry = setup.channel || {};
14140
14227
  const mainTarget = isTelegram ? mainEntry?.telegramChatId || (activeBackend === "telegram" ? mainEntry?.channelId : "") : mainEntry?.discordChannelId || (activeBackend === "discord" ? mainEntry?.channelId : "");
14141
14228
  const mainDescription = isTelegram ? mainTarget ? `Chat ID ${mainTarget}` : "Not set \xB7 Enter Telegram chat ID" : mainTarget ? `Channel ID ${mainTarget}` : "Not set \xB7 Enter Discord channel ID";
14142
14229
  const openChannelPrompt = (prompt) => {
@@ -14218,7 +14305,7 @@ function createChannelPickers({
14218
14305
  return;
14219
14306
  }
14220
14307
  const activeBackend = setup.backend || "discord";
14221
- const mainEntry = (setup.channels || []).find((ch) => ch.main) || (setup.channels || []).find((ch) => ch.name === "main");
14308
+ const mainEntry = setup.channel || {};
14222
14309
  const typeDescription = (backend) => {
14223
14310
  const selected = activeBackend === backend;
14224
14311
  const hasToken = backend === "telegram" ? setup.telegram?.authenticated === true : setup.discord?.authenticated === true;
@@ -16232,7 +16319,6 @@ function createSettingsPicker({
16232
16319
  else if (item?._action === "workflow") cycleWorkflow(1);
16233
16320
  },
16234
16321
  onSelect: (_value, item) => {
16235
- setPicker(null);
16236
16322
  if (item._action === "autoclear") openAutoClearPicker({ returnTo: openSettingsPicker });
16237
16323
  else if (item._action === "profile") openProfilePicker({ returnTo: openSettingsPicker });
16238
16324
  else if (item._action === "autocompact") applyCompaction({ auto: !(compaction.auto !== false) });
@@ -17112,7 +17198,7 @@ function MarkdownTable({ token, forceWidth }) {
17112
17198
  }
17113
17199
 
17114
17200
  // src/tui/components/Markdown.jsx
17115
- import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
17201
+ import { jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
17116
17202
  function renderMarkdownElements(content, trimPartialFences = false, tableWidth) {
17117
17203
  const segments = renderTokenAnsiSegments(content, { trimPartialFences, width: tableWidth || 80 });
17118
17204
  const result = [];
@@ -17143,14 +17229,14 @@ function StreamingMarkdown({ children, themeEpoch = 0, columns, streamKey }) {
17143
17229
  if (parts.plain) {
17144
17230
  return /* @__PURE__ */ jsx13(Markdown, { themeEpoch, columns, children: parts.unstableForRender });
17145
17231
  }
17146
- return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", gap: 1, children: [
17232
+ return /* @__PURE__ */ jsxs9(Box11, { flexDirection: "column", gap: 1, children: [
17147
17233
  parts.stablePrefix ? /* @__PURE__ */ jsx13(Markdown, { themeEpoch, columns, children: parts.stablePrefix }) : null,
17148
17234
  parts.unstableSuffix ? /* @__PURE__ */ jsx13(Markdown, { themeEpoch, columns, trimPartialFences: true, children: parts.unstableForRender }) : null
17149
17235
  ] });
17150
17236
  }
17151
17237
 
17152
17238
  // src/tui/components/Message.jsx
17153
- import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
17239
+ import { jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
17154
17240
  var AssistantMessage = React14.memo(function AssistantMessage2({
17155
17241
  text,
17156
17242
  streaming = false,
@@ -17162,7 +17248,7 @@ var AssistantMessage = React14.memo(function AssistantMessage2({
17162
17248
  if (!streaming && assistantId) resetStreamingMarkdownStablePrefix(assistantId);
17163
17249
  }, [streaming, assistantId]);
17164
17250
  const bodyWidth = assistantBodyWidth(columns);
17165
- return /* @__PURE__ */ jsxs11(Box12, { flexDirection: "row", marginTop: 1, children: [
17251
+ return /* @__PURE__ */ jsxs10(Box12, { flexDirection: "row", marginTop: 1, children: [
17166
17252
  /* @__PURE__ */ jsx14(Box12, { flexShrink: 0, minWidth: 2, children: /* @__PURE__ */ jsx14(Text14, { color: theme.text, children: TURN_MARKER }) }),
17167
17253
  /* @__PURE__ */ jsx14(Box12, { flexDirection: "column", flexShrink: 0, width: bodyWidth, children: streaming ? /* @__PURE__ */ jsx14(StreamingMarkdown, { themeEpoch, columns: bodyWidth, streamKey: assistantId, children: text }) : /* @__PURE__ */ jsx14(Markdown, { themeEpoch, columns: bodyWidth, children: text }) })
17168
17254
  ] });
@@ -17179,7 +17265,7 @@ function NoticeMessage({ text, tone, columns = 80 }) {
17179
17265
  const paddingLeft = 2;
17180
17266
  const rowWidth = Math.max(1, columns - 1);
17181
17267
  const bodyWidth = Math.max(1, rowWidth - paddingLeft - iconWidth);
17182
- return /* @__PURE__ */ jsxs11(Box12, { marginTop: 1, paddingLeft, flexDirection: "row", width: rowWidth, children: [
17268
+ return /* @__PURE__ */ jsxs10(Box12, { marginTop: 1, paddingLeft, flexDirection: "row", width: rowWidth, children: [
17183
17269
  prefix ? /* @__PURE__ */ jsx14(Box12, { flexShrink: 0, width: iconWidth, children: /* @__PURE__ */ jsx14(Text14, { color: accentColor, children: prefix }) }) : null,
17184
17270
  /* @__PURE__ */ jsx14(Box12, { flexDirection: "column", width: bodyWidth, flexShrink: 0, children: /* @__PURE__ */ jsx14(Text14, { color: bodyColor, wrap: "wrap", children: text }) })
17185
17271
  ] });
@@ -17635,7 +17721,7 @@ function toolStatusColor({ pending, groupCount, failedCount, terminalStatus = ""
17635
17721
  // src/tui/components/tool-execution/ResultBody.jsx
17636
17722
  import React15 from "react";
17637
17723
  import { Box as Box13, Text as Text15 } from "../../../vendor/ink/build/index.js";
17638
- import { jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
17724
+ import { jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime";
17639
17725
  function renderDeltaText(text) {
17640
17726
  return deltaTextParts(text).map((part, index) => part.color ? /* @__PURE__ */ jsx15(Text15, { color: part.color, children: part.text }, index) : part.text);
17641
17727
  }
@@ -17646,14 +17732,14 @@ function ResultBody({ lines, rawText, pathArg = "", isShell = false, columns, co
17646
17732
  { isShell }
17647
17733
  ) : lines || [];
17648
17734
  if (!renderLines || renderLines.length === 0) return null;
17649
- return /* @__PURE__ */ jsxs12(Box13, { flexDirection: "row", children: [
17735
+ return /* @__PURE__ */ jsxs11(Box13, { flexDirection: "row", children: [
17650
17736
  /* @__PURE__ */ jsx15(Box13, { flexShrink: 0, flexDirection: "column", children: renderLines.map((_, i) => /* @__PURE__ */ jsx15(Text15, { color: theme.subtle, children: i === 0 ? RESULT_GUTTER : RESULT_GUTTER_CONT }, i)) }),
17651
17737
  /* @__PURE__ */ jsx15(Box13, { flexDirection: "column", flexShrink: 1, flexGrow: 1, children: renderLines.map((line, i) => /* @__PURE__ */ jsx15(Text15, { color: raw ? void 0 : color, wrap: "truncate", children: raw ? line || " " : renderDeltaText(fitResultLine(line || " ", columns)) }, i)) })
17652
17738
  ] });
17653
17739
  }
17654
17740
 
17655
17741
  // src/tui/components/ToolExecution.jsx
17656
- import { jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
17742
+ import { jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
17657
17743
  var TOOL_BLINK_MS = 500;
17658
17744
  var TOOL_BLINK_LIMIT_MS = 3e3;
17659
17745
  var TOOL_PENDING_SHOW_DELAY_MS = 1e3;
@@ -17734,7 +17820,7 @@ function ToolExecution({ name, args, result, rawResult, isError, errorCount, exp
17734
17820
  if (pending && !pendingDisplayReady) {
17735
17821
  const placeholderNormalizedName = String(formatToolSurface(name, args)?.normalizedName || "").toLowerCase();
17736
17822
  const placeholderSingleRow = !aggregate && SKILL_SURFACE_NAMES2.has(placeholderNormalizedName);
17737
- return /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", marginTop: attached ? 0 : 1, width: rowWidth, overflow: "hidden", children: [
17823
+ return /* @__PURE__ */ jsxs12(Box14, { flexDirection: "column", marginTop: attached ? 0 : 1, width: rowWidth, overflow: "hidden", children: [
17738
17824
  /* @__PURE__ */ jsx16(Text16, { children: " " }),
17739
17825
  placeholderSingleRow ? null : /* @__PURE__ */ jsx16(Text16, { children: " " })
17740
17826
  ] });
@@ -17765,10 +17851,10 @@ function ToolExecution({ name, args, result, rawResult, isError, errorCount, exp
17765
17851
  const pendingPlaceholder = pending ? "Running" : "Finished";
17766
17852
  const detailLines2 = showRawAggregate ? rawRt.split("\n") : detailText ? [detailText] : [pendingPlaceholder];
17767
17853
  const aggregateDetailColor = isPlaceholderDetail ? theme.subtle : theme.text;
17768
- return /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", marginTop: attached ? 0 : 1, width: rowWidth, overflow: "hidden", children: [
17769
- /* @__PURE__ */ jsxs13(Box14, { flexDirection: "row", width: rowWidth, overflow: "hidden", children: [
17854
+ return /* @__PURE__ */ jsxs12(Box14, { flexDirection: "column", marginTop: attached ? 0 : 1, width: rowWidth, overflow: "hidden", children: [
17855
+ /* @__PURE__ */ jsxs12(Box14, { flexDirection: "row", width: rowWidth, overflow: "hidden", children: [
17770
17856
  /* @__PURE__ */ jsx16(Box14, { flexShrink: 0, minWidth: 2, children: /* @__PURE__ */ jsx16(Text16, { color: dotColor2, children: dotText2 }) }),
17771
- /* @__PURE__ */ jsxs13(Text16, { wrap: "truncate", children: [
17857
+ /* @__PURE__ */ jsxs12(Text16, { wrap: "truncate", children: [
17772
17858
  /* @__PURE__ */ jsx16(Text16, { bold: true, color: theme.text, children: clippedHeader }),
17773
17859
  trailingText2 ? /* @__PURE__ */ jsx16(Text16, { color: trailingColor2, children: trailingText2 }) : null
17774
17860
  ] })
@@ -17889,10 +17975,10 @@ function ToolExecution({ name, args, result, rawResult, isError, errorCount, exp
17889
17975
  const truncatedSummary = summaryText && summaryWidth > 0 ? truncateToWidth(summaryText, summaryWidth) : "";
17890
17976
  summaryOut = truncatedSummary ? ` (${truncatedSummary})` : "";
17891
17977
  }
17892
- return /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", marginTop: attached ? 0 : 1, width: rowWidth, overflow: "hidden", children: [
17893
- /* @__PURE__ */ jsx16(Box14, { flexDirection: "row", width: "100%", children: /* @__PURE__ */ jsx16(Box14, { flexShrink: 1, flexGrow: 1, overflow: "hidden", minWidth: 0, children: /* @__PURE__ */ jsxs13(Box14, { flexDirection: "row", children: [
17978
+ return /* @__PURE__ */ jsxs12(Box14, { flexDirection: "column", marginTop: attached ? 0 : 1, width: rowWidth, overflow: "hidden", children: [
17979
+ /* @__PURE__ */ jsx16(Box14, { flexDirection: "row", width: "100%", children: /* @__PURE__ */ jsx16(Box14, { flexShrink: 1, flexGrow: 1, overflow: "hidden", minWidth: 0, children: /* @__PURE__ */ jsxs12(Box14, { flexDirection: "row", children: [
17894
17980
  /* @__PURE__ */ jsx16(Box14, { flexShrink: 0, minWidth: 2, children: /* @__PURE__ */ jsx16(Text16, { color: dotColor, children: dotText }) }),
17895
- /* @__PURE__ */ jsxs13(Text16, { wrap: "truncate", children: [
17981
+ /* @__PURE__ */ jsxs12(Text16, { wrap: "truncate", children: [
17896
17982
  /* @__PURE__ */ jsx16(Text16, { bold: true, color: theme.text, children: labelOut }),
17897
17983
  summaryOut ? /* @__PURE__ */ jsx16(Text16, { color: theme.text, children: summaryOut }) : null,
17898
17984
  inlineFailureText ? /* @__PURE__ */ jsx16(Text16, { color: theme.error, children: inlineFailureText }) : null,
@@ -17917,7 +18003,7 @@ function ToolExecution({ name, args, result, rawResult, isError, errorCount, exp
17917
18003
  // src/tui/components/TurnDone.jsx
17918
18004
  import React17 from "react";
17919
18005
  import { Box as Box15, Text as Text17 } from "../../../vendor/ink/build/index.js";
17920
- import { jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
18006
+ import { jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
17921
18007
  function statusMessageColor(tone) {
17922
18008
  if (tone === "error") return theme.error;
17923
18009
  if (tone === "warn" || tone === "cancel") return theme.warning;
@@ -17934,9 +18020,9 @@ function TurnDone({ elapsedMs = 0, status = "done", verb = "Thought", rightMessa
17934
18020
  const copy = cancelled ? elapsed ? `Cancelled after ${elapsed}` : "Cancelled" : elapsed ? `${doneVerb} for ${elapsed}` : doneVerb;
17935
18021
  const rightText = cleanRightMessage(rightMessage);
17936
18022
  const rightWidth = Math.max(1, Number(rightMessageWidth) || 24);
17937
- return /* @__PURE__ */ jsxs14(Box15, { marginTop, flexDirection: "row", width: "100%", children: [
17938
- /* @__PURE__ */ jsx17(Box15, { flexGrow: 1, flexShrink: 1, overflow: "hidden", children: /* @__PURE__ */ jsxs14(Text17, { wrap: "truncate", children: [
17939
- /* @__PURE__ */ jsxs14(Text17, { color: theme.spinnerGlyph, children: [
18023
+ return /* @__PURE__ */ jsxs13(Box15, { marginTop, flexDirection: "row", width: "100%", children: [
18024
+ /* @__PURE__ */ jsx17(Box15, { flexGrow: 1, flexShrink: 1, overflow: "hidden", children: /* @__PURE__ */ jsxs13(Text17, { wrap: "truncate", children: [
18025
+ /* @__PURE__ */ jsxs13(Text17, { color: theme.spinnerGlyph, children: [
17940
18026
  TURN_DONE_MARKER,
17941
18027
  " "
17942
18028
  ] }),
@@ -17950,14 +18036,14 @@ function StatusDone({ label = "Complete", detail = "", rightMessage = "", rightT
17950
18036
  const suffix = String(detail || "").trim();
17951
18037
  const rightText = cleanRightMessage(rightMessage);
17952
18038
  const rightWidth = Math.max(1, Number(rightMessageWidth) || 24);
17953
- return /* @__PURE__ */ jsxs14(Box15, { marginTop, flexDirection: "row", width: "100%", children: [
17954
- /* @__PURE__ */ jsx17(Box15, { flexGrow: 1, flexShrink: 1, overflow: "hidden", children: /* @__PURE__ */ jsxs14(Text17, { wrap: "truncate", children: [
17955
- /* @__PURE__ */ jsxs14(Text17, { color: theme.spinnerGlyph, children: [
18039
+ return /* @__PURE__ */ jsxs13(Box15, { marginTop, flexDirection: "row", width: "100%", children: [
18040
+ /* @__PURE__ */ jsx17(Box15, { flexGrow: 1, flexShrink: 1, overflow: "hidden", children: /* @__PURE__ */ jsxs13(Text17, { wrap: "truncate", children: [
18041
+ /* @__PURE__ */ jsxs13(Text17, { color: theme.spinnerGlyph, children: [
17956
18042
  TURN_DONE_MARKER,
17957
18043
  " "
17958
18044
  ] }),
17959
18045
  /* @__PURE__ */ jsx17(Text17, { color: theme.thinkingAccent, children: copy }),
17960
- suffix ? /* @__PURE__ */ jsxs14(Text17, { color: theme.subtle, children: [
18046
+ suffix ? /* @__PURE__ */ jsxs13(Text17, { color: theme.subtle, children: [
17961
18047
  " \xB7 ",
17962
18048
  suffix
17963
18049
  ] }) : null
@@ -17969,7 +18055,7 @@ function StatusDone({ label = "Complete", detail = "", rightMessage = "", rightT
17969
18055
  // src/tui/components/ItemRightHintOverprint.jsx
17970
18056
  import React18 from "react";
17971
18057
  import { Box as Box16, Text as Text18 } from "../../../vendor/ink/build/index.js";
17972
- import { jsx as jsx18, jsxs as jsxs15 } from "react/jsx-runtime";
18058
+ import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
17973
18059
  function hintColor(tone) {
17974
18060
  if (tone === "error") return theme.error;
17975
18061
  if (tone === "warn" || tone === "cancel") return theme.warning;
@@ -17988,9 +18074,9 @@ function ItemRightHintOverprint({
17988
18074
  const rightText = cleanRightMessage2(rightMessage);
17989
18075
  if (!rightText) return children;
17990
18076
  const rightWidth = Math.max(1, Number(rightMessageWidth) || 24);
17991
- return /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", width: "100%", flexShrink: 0, children: [
18077
+ return /* @__PURE__ */ jsxs14(Box16, { flexDirection: "column", width: "100%", flexShrink: 0, children: [
17992
18078
  children,
17993
- /* @__PURE__ */ jsxs15(
18079
+ /* @__PURE__ */ jsxs14(
17994
18080
  Box16,
17995
18081
  {
17996
18082
  height: 1,
@@ -18020,7 +18106,7 @@ function ItemRightHintOverprint({
18020
18106
  }
18021
18107
 
18022
18108
  // src/tui/components/TranscriptItem.jsx
18023
- import { jsx as jsx19, jsxs as jsxs16 } from "react/jsx-runtime";
18109
+ import { jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
18024
18110
  function ToolHookDenialCard({ item, columns = 80 }) {
18025
18111
  const { label, summary } = formatToolSurface(item.name, item.args);
18026
18112
  const detail = formatHookDenialDetail(toolItemResultText(item));
@@ -18030,16 +18116,16 @@ function ToolHookDenialCard({ item, columns = 80 }) {
18030
18116
  const summaryText = safeSummary ? ` (${safeSummary})` : "";
18031
18117
  const rowWidth = Math.max(1, Number(columns || 80));
18032
18118
  const detailWidth = Math.max(1, rowWidth - stringWidth10(RESULT_GUTTER));
18033
- return /* @__PURE__ */ jsxs16(Box17, { flexDirection: "column", marginTop: 1, width: rowWidth, overflow: "hidden", children: [
18034
- /* @__PURE__ */ jsxs16(Box17, { flexDirection: "row", width: rowWidth, overflow: "hidden", children: [
18119
+ return /* @__PURE__ */ jsxs15(Box17, { flexDirection: "column", marginTop: 1, width: rowWidth, overflow: "hidden", children: [
18120
+ /* @__PURE__ */ jsxs15(Box17, { flexDirection: "row", width: rowWidth, overflow: "hidden", children: [
18035
18121
  /* @__PURE__ */ jsx19(Box17, { flexShrink: 0, minWidth: 2, children: /* @__PURE__ */ jsx19(Text19, { color: theme.error, children: TURN_MARKER }) }),
18036
- /* @__PURE__ */ jsx19(Box17, { flexGrow: 1, flexShrink: 1, overflow: "hidden", minWidth: 0, children: /* @__PURE__ */ jsxs16(Text19, { wrap: "truncate", children: [
18122
+ /* @__PURE__ */ jsx19(Box17, { flexGrow: 1, flexShrink: 1, overflow: "hidden", minWidth: 0, children: /* @__PURE__ */ jsxs15(Text19, { wrap: "truncate", children: [
18037
18123
  /* @__PURE__ */ jsx19(Text19, { bold: true, color: theme.text, children: safeLabel }),
18038
18124
  summaryText ? /* @__PURE__ */ jsx19(Text19, { color: theme.text, children: summaryText }) : null,
18039
18125
  /* @__PURE__ */ jsx19(Text19, { color: theme.error, children: " \xB7 Denied" })
18040
18126
  ] }) })
18041
18127
  ] }),
18042
- safeDetail ? /* @__PURE__ */ jsxs16(Box17, { flexDirection: "row", width: rowWidth, overflow: "hidden", children: [
18128
+ safeDetail ? /* @__PURE__ */ jsxs15(Box17, { flexDirection: "row", width: rowWidth, overflow: "hidden", children: [
18043
18129
  /* @__PURE__ */ jsx19(Box17, { flexShrink: 0, width: stringWidth10(RESULT_GUTTER), children: /* @__PURE__ */ jsx19(Text19, { color: theme.subtle, children: RESULT_GUTTER }) }),
18044
18130
  /* @__PURE__ */ jsx19(Box17, { flexShrink: 0, width: detailWidth, overflow: "hidden", children: /* @__PURE__ */ jsx19(Text19, { color: theme.error, wrap: "truncate", children: safeDetail }) })
18045
18131
  ] }) : null
@@ -18089,7 +18175,7 @@ var Item = React19.memo(function Item2({ item, prevKind, columns, toolOutputExpa
18089
18175
  });
18090
18176
 
18091
18177
  // src/tui/App.jsx
18092
- import { Fragment as Fragment6, jsx as jsx20, jsxs as jsxs17 } from "react/jsx-runtime";
18178
+ import { Fragment as Fragment6, jsx as jsx20, jsxs as jsxs16 } from "react/jsx-runtime";
18093
18179
  function App({ store, initialStatusLine = "", forceOnboarding = false }) {
18094
18180
  const state = useEngine(store);
18095
18181
  const [toolOutputExpanded, setToolOutputExpanded] = useState8(false);
@@ -18099,25 +18185,26 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
18099
18185
  const { stdout } = useStdout2();
18100
18186
  const [exiting, setExiting] = useState8(false);
18101
18187
  const [tuiReady, setTuiReady] = useState8(false);
18102
- const exitRequestedRef = useRef8(false);
18188
+ const exitRequestedRef = useRef9(false);
18103
18189
  const [resizeState, setResizeState] = useState8(() => ({ ...terminalSize(stdout), epoch: 0 }));
18190
+ const [panelTransitionEpoch, setPanelTransitionEpoch] = useState8(0);
18104
18191
  const windowsLikeTerminal = process.platform === "win32" || Boolean(process.env.WT_SESSION);
18105
18192
  const rightSafetyColumns = windowsLikeTerminal ? 1 : 0;
18106
18193
  const frameColumns = Math.max(1, resizeState.columns - rightSafetyColumns);
18107
18194
  const [scrollOffset, setScrollOffset] = useState8(0);
18108
- const scrollPositionRef = useRef8(0);
18109
- const scrollTargetRef = useRef8(0);
18110
- const maxScrollRowsRef = useRef8(0);
18111
- const transcriptBottomSlackRowsRef = useRef8(0);
18112
- const transcriptAnchorRef = useRef8(null);
18113
- const transcriptAnchorDirtyRef = useRef8(false);
18114
- const transcriptGeomRef = useRef8({ prefixRows: null, totalRows: 0, viewRows: 1 });
18195
+ const scrollPositionRef = useRef9(0);
18196
+ const scrollTargetRef = useRef9(0);
18197
+ const maxScrollRowsRef = useRef9(0);
18198
+ const transcriptBottomSlackRowsRef = useRef9(0);
18199
+ const transcriptAnchorRef = useRef9(null);
18200
+ const transcriptAnchorDirtyRef = useRef9(false);
18201
+ const transcriptGeomRef = useRef9({ prefixRows: null, totalRows: 0, viewRows: 1 });
18115
18202
  const [measuredRowsVersion, setMeasuredRowsVersion] = useState8(0);
18116
- const followingRef = useRef8(false);
18117
- const lastItemsCountRef = useRef8(0);
18118
- const pickerOpenedFromEnterRef = useRef8(false);
18119
- const pickerOpenedFromEnterTimerRef = useRef8(null);
18120
- const projectPickerRef = useRef8(null);
18203
+ const followingRef = useRef9(false);
18204
+ const lastItemsCountRef = useRef9(0);
18205
+ const pickerOpenedFromEnterRef = useRef9(false);
18206
+ const pickerOpenedFromEnterTimerRef = useRef9(null);
18207
+ const projectPickerRef = useRef9(null);
18121
18208
  const buildProjectPickerState = (opts) => projectPickerRef.current.buildProjectPickerState(opts);
18122
18209
  const [picker, setPickerState] = useState8(null);
18123
18210
  const setPicker = useCallback6((next) => {
@@ -18136,14 +18223,14 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
18136
18223
  }, []);
18137
18224
  const [contextPanel, setContextPanel] = useState8(null);
18138
18225
  const [usagePanel, setUsagePanel] = useState8(null);
18139
- const usageRequestRef = useRef8(0);
18140
- const settingsHeavyCacheRef = useRef8(null);
18226
+ const usageRequestRef = useRef9(0);
18227
+ const settingsHeavyCacheRef = useRef9(null);
18141
18228
  const closeUsagePanel = useCallback6(() => {
18142
18229
  usageRequestRef.current += 1;
18143
18230
  setUsagePanel(null);
18144
18231
  }, []);
18145
18232
  const [providerPrompt, setProviderPrompt] = useState8(null);
18146
- const oauthSubmitRef = useRef8(false);
18233
+ const oauthSubmitRef = useRef9(false);
18147
18234
  const [channelPrompt, setChannelPrompt] = useState8(null);
18148
18235
  const [hookPrompt, setHookPrompt] = useState8(null);
18149
18236
  const [settingsPrompt, setSettingsPrompt] = useState8(null);
@@ -18165,7 +18252,7 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
18165
18252
  pickFolder
18166
18253
  });
18167
18254
  projectPickerRef.current = projectPicker;
18168
- const initialPickerBuiltRef = useRef8(false);
18255
+ const initialPickerBuiltRef = useRef9(false);
18169
18256
  if (!initialPickerBuiltRef.current) {
18170
18257
  initialPickerBuiltRef.current = true;
18171
18258
  let onboardingOwnsScreen = false;
@@ -18253,29 +18340,29 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
18253
18340
  const toolApproval = state.toolApproval || null;
18254
18341
  const [promptDraft, setPromptDraft] = useState8("");
18255
18342
  const [promptDraftOverride, setPromptDraftOverride] = useState8(null);
18256
- const promptLayoutValueRef = useRef8("");
18343
+ const promptLayoutValueRef = useRef9("");
18257
18344
  const [, setPromptLayoutRows] = useState8(1);
18258
18345
  const [, setPastedImages] = useState8({});
18259
- const pastedImagesRef = useRef8({});
18260
- const nextPastedImageIdRef = useRef8(1);
18346
+ const pastedImagesRef = useRef9({});
18347
+ const nextPastedImageIdRef = useRef9(1);
18261
18348
  const [, setPastedTexts] = useState8({});
18262
- const pastedTextsRef = useRef8({});
18263
- const nextPastedTextIdRef = useRef8(1);
18264
- const promptValueRef = useRef8("");
18265
- const promptSelectionRef = useRef8(null);
18266
- const promptBoxRectRef = useRef8(null);
18267
- const promptMouseSelectionRef = useRef8(null);
18268
- const promptHistoryNavRef = useRef8({ active: false, index: -1, seed: "", lastValue: "" });
18269
- const promptHistoryDraftChangeRef = useRef8(false);
18349
+ const pastedTextsRef = useRef9({});
18350
+ const nextPastedTextIdRef = useRef9(1);
18351
+ const promptValueRef = useRef9("");
18352
+ const promptSelectionRef = useRef9(null);
18353
+ const promptBoxRectRef = useRef9(null);
18354
+ const promptMouseSelectionRef = useRef9(null);
18355
+ const promptHistoryNavRef = useRef9({ active: false, index: -1, seed: "", lastValue: "" });
18356
+ const promptHistoryDraftChangeRef = useRef9(false);
18270
18357
  const [promptHint, setPromptHint] = useState8("");
18271
18358
  const [promptHintTone, setPromptHintTone] = useState8("info");
18272
18359
  const [welcomePromptHintDismissed, setWelcomePromptHintDismissed] = useState8(false);
18273
18360
  const [conditionalWelcomePromptHint, setConditionalWelcomePromptHint] = useState8("");
18274
- const welcomePromptHintRef = useRef8(null);
18361
+ const welcomePromptHintRef = useRef9(null);
18275
18362
  if (welcomePromptHintRef.current === null) {
18276
18363
  welcomePromptHintRef.current = randomWelcomePromptHint();
18277
18364
  }
18278
- const welcomePromptHintVisibleRef = useRef8(false);
18365
+ const welcomePromptHintVisibleRef = useRef9(false);
18279
18366
  const dismissWelcomePromptHint = useCallback6(() => {
18280
18367
  if (!welcomePromptHintVisibleRef.current) return;
18281
18368
  setWelcomePromptHintDismissed((dismissed) => dismissed || true);
@@ -18283,14 +18370,15 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
18283
18370
  const toastErrorSignature = useMemo2(() => (state.toasts || []).filter((toast) => toast?.tone === "error").map((toast) => `${toast.id || ""}:${toast.text || ""}`).join("|"), [state.toasts]);
18284
18371
  const [slashIndex, setSlashIndex] = useState8(0);
18285
18372
  const [slashDismissedFor, setSlashDismissedFor] = useState8("");
18286
- const slashPaletteRef = useRef8({ open: false, count: 0 });
18287
- const scrollFocusRef = useRef8({});
18288
- const onboardingStartedRef = useRef8(false);
18289
- const onboardingRef = useRef8({ defaultRoute: null, searchRoute: null, agentRoutes: {}, agents: [], providerModels: [] });
18290
- const providerModelsCacheRef = useRef8({ models: null, at: 0 });
18291
- const searchModelsCacheRef = useRef8({ models: null, at: 0 });
18292
- const modelPickerRequestRef = useRef8(0);
18293
- const onboardingPrefetchSeqRef = useRef8(0);
18373
+ const slashPaletteRef = useRef9({ open: false, count: 0 });
18374
+ const workflowTabCycleRef = useRef9({ pending: false, lastAt: 0 });
18375
+ const scrollFocusRef = useRef9({});
18376
+ const onboardingStartedRef = useRef9(false);
18377
+ const onboardingRef = useRef9({ defaultRoute: null, searchRoute: null, agentRoutes: {}, agents: [], providerModels: [] });
18378
+ const providerModelsCacheRef = useRef9({ models: null, at: 0 });
18379
+ const searchModelsCacheRef = useRef9({ models: null, at: 0 });
18380
+ const modelPickerRequestRef = useRef9(0);
18381
+ const onboardingPrefetchSeqRef = useRef9(0);
18294
18382
  const clearModelCaches = useCallback6((scope = "all") => {
18295
18383
  if (scope === "all" || scope === "provider") {
18296
18384
  providerModelsCacheRef.current = { models: null, at: 0 };
@@ -18458,11 +18546,12 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
18458
18546
  openUpdatePicker: (...a) => openUpdatePicker(...a),
18459
18547
  requestExit: (...a) => requestExit(...a)
18460
18548
  });
18461
- const promptHintTimerRef = useRef8(null);
18462
- const promptHintActiveRef = useRef8(false);
18463
- const dragRef = useRef8({ anchor: null, anchorScroll: 0, last: null, active: false, rect: null, region: null, anchorSpan: null });
18464
- const transcriptViewportRef = useRef8({ top: 0, bottom: 0 });
18465
- const frameRowsRef = useRef8(24);
18549
+ const promptHintTimerRef = useRef9(null);
18550
+ const promptHintActiveRef = useRef9(false);
18551
+ const dragRef = useRef9({ anchor: null, anchorScroll: 0, last: null, active: false, rect: null, region: null, anchorSpan: null });
18552
+ const transcriptViewportRef = useRef9({ top: 0, bottom: 0 });
18553
+ const panelTransitionRef = useRef9({ signature: "", reserve: 0, clearRows: 0, guardRows: 0, epoch: 0 });
18554
+ const frameRowsRef = useRef9(24);
18466
18555
  const STATUSLINE_BAND_ROWS = 3;
18467
18556
  const [voiceEnabled, setVoiceEnabled] = useState8(() => isVoiceEnabled());
18468
18557
  const voiceIndicatorVisible = Boolean(state.progressHint?.text) || voiceEnabled || getRecorderState() !== "idle";
@@ -18532,9 +18621,9 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
18532
18621
  alive = false;
18533
18622
  };
18534
18623
  }, [store, state.provider, state.model, state.workflow?.id, toastErrorSignature]);
18535
- const selectionLayoutRef = useRef8(null);
18536
- const selectionTextRef = useRef8("");
18537
- const lastClickRef = useRef8({ x: -1, y: -1, t: 0, count: 0 });
18624
+ const selectionLayoutRef = useRef9(null);
18625
+ const selectionTextRef = useRef9("");
18626
+ const lastClickRef = useRef9({ x: -1, y: -1, t: 0, count: 0 });
18538
18627
  const showSelectionCopyHint = useCallback6((text, tone = "plain") => {
18539
18628
  if (promptHintTimerRef.current) clearTimeout(promptHintTimerRef.current);
18540
18629
  promptHintActiveRef.current = true;
@@ -18925,12 +19014,21 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
18925
19014
  toggleExpand();
18926
19015
  return;
18927
19016
  }
18928
- if (!picker && key.shift && (key.leftArrow || key.rightArrow || key.upArrow || key.downArrow || key.home || key.end)) {
19017
+ const rawShiftUp = input === "\x1B[1;2A" || input === "\x1B[a" || input === "[1;2A";
19018
+ const rawShiftDown = input === "\x1B[1;2B" || input === "\x1B[b" || input === "[1;2B";
19019
+ const rawShiftRight = input === "\x1B[1;2C" || input === "\x1B[c" || input === "[1;2C";
19020
+ const rawShiftLeft = input === "\x1B[1;2D" || input === "\x1B[d" || input === "[1;2D";
19021
+ const rawCtrlShiftUp = input === "\x1B[1;6A" || input === "[1;6A";
19022
+ const rawCtrlShiftDown = input === "\x1B[1;6B" || input === "[1;6B";
19023
+ const rawCtrlShiftRight = input === "\x1B[1;6C" || input === "[1;6C";
19024
+ const rawCtrlShiftLeft = input === "\x1B[1;6D" || input === "[1;6D";
19025
+ const rawModifiedShiftArrow = rawShiftUp || rawShiftDown || rawShiftLeft || rawShiftRight || rawCtrlShiftUp || rawCtrlShiftDown || rawCtrlShiftLeft || rawCtrlShiftRight;
19026
+ if (!picker && (key.shift && (key.leftArrow || key.rightArrow || key.upArrow || key.downArrow || key.home || key.end) || rawModifiedShiftArrow)) {
18929
19027
  let move = null;
18930
- if (key.leftArrow) move = "left";
18931
- else if (key.rightArrow) move = "right";
18932
- else if (key.upArrow) move = "up";
18933
- else if (key.downArrow) move = "down";
19028
+ if (key.leftArrow || rawShiftLeft || rawCtrlShiftLeft) move = "left";
19029
+ else if (key.rightArrow || rawShiftRight || rawCtrlShiftRight) move = "right";
19030
+ else if (key.upArrow || rawShiftUp || rawCtrlShiftUp) move = "up";
19031
+ else if (key.downArrow || rawShiftDown || rawCtrlShiftDown) move = "down";
18934
19032
  else if (key.home) move = "lineStart";
18935
19033
  else if (key.end) move = "lineEnd";
18936
19034
  if (move && gridSelectionActiveRef.current()) {
@@ -19364,16 +19462,10 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
19364
19462
  }
19365
19463
  const parts = commandText.split("|").map((part) => part.trim());
19366
19464
  if (channelPrompt.kind === "channel-add") {
19367
- const [first, pipeId, mode, mainFlag] = parts;
19368
19465
  const isPipe = parts.length > 1;
19369
- const name = isPipe ? first : "main";
19370
- const channelId = isPipe ? pipeId : first;
19371
- const main = !isPipe || String(mainFlag || "").toLowerCase() === "main" || String(name || "").toLowerCase() === "main";
19372
- store.saveChannel({
19373
- name,
19466
+ const channelId = isPipe ? parts[1] : parts[0];
19467
+ store.setChannel({
19374
19468
  channelId,
19375
- mode: isPipe ? mode : "interactive",
19376
- main,
19377
19469
  backend: channelPrompt.backend
19378
19470
  });
19379
19471
  resumeAfterChannelPrompt(channelPrompt);
@@ -19500,6 +19592,25 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
19500
19592
  void openSettingsPicker();
19501
19593
  return true;
19502
19594
  }
19595
+ if (settingsPrompt.kind === "autoclear-provider") {
19596
+ const provider = String(settingsPrompt.provider || "").trim();
19597
+ if (!provider) {
19598
+ store.pushNotice("auto-clear provider is missing", "warn");
19599
+ return false;
19600
+ }
19601
+ const text2 = String(commandText || "").trim();
19602
+ try {
19603
+ if (text2) store.setAutoClear?.({ provider, duration: text2 });
19604
+ else store.setAutoClear?.({ provider, resetProvider: true });
19605
+ store.pushNotice(text2 ? `Auto-clear ${provider} default set to ${text2}` : `Auto-clear ${provider} default reset`, "info");
19606
+ } catch (e) {
19607
+ store.pushNotice(`autoclear failed: ${e?.message || e}`, "error");
19608
+ return false;
19609
+ }
19610
+ setSettingsPrompt(null);
19611
+ openAutoClearPicker({ advanced: true, returnTo: settingsPrompt.returnTo });
19612
+ return true;
19613
+ }
19503
19614
  if (settingsPrompt.kind === "profile-title") {
19504
19615
  try {
19505
19616
  store.setProfile?.({ title: commandText });
@@ -19729,6 +19840,8 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
19729
19840
  openProjectPicker();
19730
19841
  } else if (kind === "core-add" || kind === "core-edit" || kind === "core-delete-confirm") {
19731
19842
  openMemoryCorePicker();
19843
+ } else if (kind === "autoclear-provider") {
19844
+ openAutoClearPicker({ advanced: true, returnTo: settingsPrompt?.returnTo });
19732
19845
  }
19733
19846
  }, [settingsPrompt, showPromptHint]);
19734
19847
  const acceptSlashPalette = useCallback6((draftValue = "") => {
@@ -19847,11 +19960,13 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
19847
19960
  const hasTextEntryPrompt = !!textEntryPrompt;
19848
19961
  const hasFloatingPanel = !!(toolApproval || picker || contextPanel || usagePanel || slashPaletteOpen || hasTextEntryPrompt);
19849
19962
  const expandedOptionPanel = !!(toolApproval || picker || contextPanel || usagePanel || hasTextEntryPrompt);
19963
+ const bootSettling = !tuiReady && state.items.length === 0 && !hasFloatingPanel;
19850
19964
  const projectSelectionActive = picker?.kind === "project" || settingsPrompt?.kind === "project-new" || settingsPrompt?.kind === "project-create-confirm" || settingsPrompt?.kind === "project-rename";
19851
- const inputBoxHidden = expandedOptionPanel;
19965
+ const inputBoxHidden = expandedOptionPanel || bootSettling;
19852
19966
  const showWelcomeBanner = state.items.length === 0 && !hasFloatingPanel || projectSelectionActive || onboardingActive;
19853
19967
  const WELCOME_ROWS = showWelcomeBanner ? 11 : 0;
19854
19968
  const liveSpinner = state.spinner?.active ? state.spinner : state.commandStatus?.active ? state.commandStatus : null;
19969
+ const liveSpinnerIsCommand = !state.spinner?.active && !!state.commandStatus?.active;
19855
19970
  const latestToast = state.toasts?.length ? state.toasts[state.toasts.length - 1] : null;
19856
19971
  const toastHint = latestToast ? latestToast.text : "";
19857
19972
  const progressHint = state.progressHint || null;
@@ -19885,10 +20000,41 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
19885
20000
  const desiredFloatingPanelRows = toolApproval ? PANEL_CHROME_ROWS + 2 + OPTION_PANEL_EXTRA_ROWS : picker ? picker.fillAvailable ? maxFloatingPanelRows : PANEL_BASE_ROWS + OPTION_PANEL_EXTRA_ROWS : contextPanel ? PANEL_BASE_ROWS + OPTION_PANEL_EXTRA_ROWS + 3 : usagePanel ? PANEL_BASE_ROWS + OPTION_PANEL_EXTRA_ROWS : slashPaletteOpen ? PANEL_MAX_VISIBLE + PANEL_CHROME_ROWS : hasTextEntryPrompt ? TEXT_ENTRY_ROWS : 0;
19886
20001
  const floatingPanelRows = desiredFloatingPanelRows > 0 ? Math.min(desiredFloatingPanelRows, maxFloatingPanelRows) : 0;
19887
20002
  const pickerVisibleRows = picker ? Math.max(1, floatingPanelRows - PICKER_CHROME_ROWS) : PANEL_MAX_VISIBLE;
19888
- const bottomReserve = baseReserve + floatingPanelRows;
20003
+ const rawBottomReserve = baseReserve + floatingPanelRows;
20004
+ const bottomClusterRows = INPUT_BOX_ROWS + STATUSLINE_ROWS + queuedRows + floatingPanelRows;
20005
+ const panelLayoutSignature = [
20006
+ toolApproval ? "tool" : "",
20007
+ picker ? `picker:${picker.kind || ""}:${picker.fillAvailable ? "fill" : "fit"}` : "",
20008
+ contextPanel ? "context" : "",
20009
+ usagePanel ? "usage" : "",
20010
+ slashPaletteOpen ? "slash" : "",
20011
+ hasTextEntryPrompt ? `text:${textEntryPrompt?.kind || ""}` : "",
20012
+ inputBoxHidden ? "input-hidden" : "input-visible",
20013
+ floatingPanelRows,
20014
+ promptBoxRows,
20015
+ promptMetaRows,
20016
+ queuedRows,
20017
+ WELCOME_ROWS
20018
+ ].join("|");
20019
+ const panelTransition = panelTransitionRef.current;
20020
+ const panelLayoutChanged = Boolean(panelTransition.signature && panelTransition.signature !== panelLayoutSignature);
20021
+ let panelTransitionClearRows = 0;
20022
+ let panelTransitionGuardRows = 0;
20023
+ if (panelLayoutChanged) {
20024
+ panelTransitionClearRows = Math.max(0, panelTransition.reserve - bottomClusterRows);
20025
+ panelTransitionGuardRows = panelTransitionClearRows > 0 ? 1 : 0;
20026
+ panelTransition.clearRows = panelTransitionClearRows;
20027
+ panelTransition.guardRows = panelTransitionGuardRows;
20028
+ panelTransition.epoch = panelTransitionEpoch;
20029
+ } else if (panelTransition.epoch === panelTransitionEpoch) {
20030
+ panelTransitionClearRows = panelTransition.clearRows || 0;
20031
+ panelTransitionGuardRows = panelTransition.guardRows || 0;
20032
+ }
20033
+ const bottomReserve = rawBottomReserve + panelTransitionClearRows;
19889
20034
  const viewportHeight = Math.max(1, resizeState.rows - bottomReserve);
19890
- const baseGuardRows = viewportHeight > 1 ? 1 : 0;
19891
- const transcriptGuardRows = baseGuardRows;
20035
+ const guardCapacityRows = Math.max(0, viewportHeight - 1);
20036
+ const baseGuardRows = guardCapacityRows > 0 ? 1 : 0;
20037
+ const transcriptGuardRows = Math.min(guardCapacityRows, baseGuardRows + panelTransitionGuardRows);
19892
20038
  const transcriptContentHeight = Math.max(1, viewportHeight - transcriptGuardRows);
19893
20039
  const transcriptBottomSlackRows = Math.max(0, baseGuardRows);
19894
20040
  transcriptBottomSlackRowsRef.current = transcriptBottomSlackRows;
@@ -19907,6 +20053,17 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
19907
20053
  welcomePromptHintText && !welcomePromptHintDismissed && state.items.length === 0 && !hasFloatingPanel && !inputBoxHidden && !queuedVisible && !liveSpinner && !inputHint
19908
20054
  );
19909
20055
  welcomePromptHintVisibleRef.current = welcomePromptHintVisible;
20056
+ useEffect10(() => {
20057
+ const transition = panelTransitionRef.current;
20058
+ const hadTransitionClearance = panelTransitionClearRows > 0 || panelTransitionGuardRows > 0;
20059
+ transition.signature = panelLayoutSignature;
20060
+ transition.reserve = bottomClusterRows;
20061
+ transition.clearRows = 0;
20062
+ transition.guardRows = 0;
20063
+ if (!hadTransitionClearance) return void 0;
20064
+ const timer = setTimeout(() => setPanelTransitionEpoch((epoch) => epoch + 1), 0);
20065
+ return () => clearTimeout(timer);
20066
+ }, [panelLayoutSignature, bottomClusterRows, panelTransitionClearRows, panelTransitionGuardRows]);
19910
20067
  const {
19911
20068
  transcriptWindow,
19912
20069
  renderedTranscriptItems,
@@ -19945,6 +20102,14 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
19945
20102
  });
19946
20103
  const cycleWorkflowFromPrompt = useCallback6(() => {
19947
20104
  if (slashPaletteOpen || toolApproval || picker || settingsPrompt || providerPrompt || channelPrompt || hookPrompt || contextPanel || usagePanel) return true;
20105
+ const repeatGuardMs = 300;
20106
+ const cycleGuard = workflowTabCycleRef.current;
20107
+ const now = Date.now();
20108
+ if (state.commandBusy || cycleGuard.pending || now - cycleGuard.lastAt < repeatGuardMs) {
20109
+ cycleGuard.lastAt = now;
20110
+ return true;
20111
+ }
20112
+ cycleGuard.lastAt = now;
19948
20113
  let workflows = [];
19949
20114
  try {
19950
20115
  workflows = store.listWorkflows?.() || [];
@@ -19964,15 +20129,18 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
19964
20129
  const activeIndex = workflows.findIndex((item) => item.active);
19965
20130
  const currentIndex = activeIndex >= 0 ? activeIndex : Math.max(0, workflows.findIndex((item) => item.id === workflow.id));
19966
20131
  const next = workflows[(currentIndex + 1 + workflows.length) % workflows.length];
19967
- void store.setWorkflow?.(next.id).then((result) => {
20132
+ cycleGuard.pending = true;
20133
+ void Promise.resolve().then(() => store.setWorkflow?.(next.id)).then((result) => {
19968
20134
  if (!result) {
19969
- store.pushNotice("Workflow switch is already running.", "warn");
19970
20135
  return;
19971
20136
  }
19972
- store.pushNotice(workflowSwitchNotice(result), "info");
19973
- }).catch((e) => store.pushNotice(`Couldn\u2019t switch workflow: ${e?.message || e}`, "error"));
20137
+ store.pushNotice(workflowSwitchNotice(result), "info", { ttlMs: 1200 });
20138
+ }).catch((e) => store.pushNotice(`Couldn\u2019t switch workflow: ${e?.message || e}`, "error")).finally(() => {
20139
+ cycleGuard.pending = false;
20140
+ cycleGuard.lastAt = Date.now();
20141
+ });
19974
20142
  return true;
19975
- }, [slashPaletteOpen, toolApproval, picker, settingsPrompt, providerPrompt, channelPrompt, hookPrompt, contextPanel, usagePanel, state.workflow, store]);
20143
+ }, [slashPaletteOpen, toolApproval, picker, settingsPrompt, providerPrompt, channelPrompt, hookPrompt, contextPanel, usagePanel, state.commandBusy, state.workflow, store]);
19976
20144
  const promptInputControl = /* @__PURE__ */ jsx20(
19977
20145
  PromptInput,
19978
20146
  {
@@ -20022,8 +20190,8 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
20022
20190
  // stack up from just over the input. A top flexGrow spacer sinks the whole
20023
20191
  // stack to the bottom; the transcript itself is a fixed-height clipping
20024
20192
  // viewport (see viewportHeight above).
20025
- /* @__PURE__ */ jsxs17(Box18, { flexDirection: "column", width: frameColumns, height: resizeState.rows, backgroundColor: surfaceBackground(), children: [
20026
- showWelcomeBanner ? /* @__PURE__ */ jsxs17(Box18, { flexDirection: "column", height: 7, flexShrink: 0, marginTop: 3, marginBottom: 1, backgroundColor: surfaceBackground(), children: [
20193
+ /* @__PURE__ */ jsxs16(Box18, { flexDirection: "column", width: frameColumns, height: resizeState.rows, backgroundColor: surfaceBackground(), children: [
20194
+ showWelcomeBanner ? /* @__PURE__ */ jsxs16(Box18, { flexDirection: "column", height: 7, flexShrink: 0, marginTop: 3, marginBottom: 1, backgroundColor: surfaceBackground(), children: [
20027
20195
  /* @__PURE__ */ jsx20(Text20, { color: theme.text, bold: true, children: centerLine("\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 ", frameColumns) }),
20028
20196
  /* @__PURE__ */ jsx20(Text20, { color: theme.text, bold: true, children: centerLine("\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2554\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D ", frameColumns) }),
20029
20197
  /* @__PURE__ */ jsx20(Text20, { color: theme.logo ?? theme.claude, bold: true, children: centerLine("\u2588\u2588\u2554\u2588\u2588\u2588\u2588\u2554\u2588\u2588\u2551\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2588\u2557", frameColumns) }),
@@ -20032,7 +20200,7 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
20032
20200
  /* @__PURE__ */ jsx20(Box18, { height: 1, flexShrink: 0 }),
20033
20201
  /* @__PURE__ */ jsx20(Text20, { color: theme.inactive, children: centerLine(`mixdog coding agent \xB7 ${state.cwd}`, frameColumns, 4) })
20034
20202
  ] }) : null,
20035
- /* @__PURE__ */ jsxs17(
20203
+ /* @__PURE__ */ jsxs16(
20036
20204
  Box18,
20037
20205
  {
20038
20206
  flexDirection: "column",
@@ -20043,7 +20211,7 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
20043
20211
  overflow: "hidden",
20044
20212
  justifyContent: "flex-end",
20045
20213
  children: [
20046
- /* @__PURE__ */ jsxs17(
20214
+ /* @__PURE__ */ jsxs16(
20047
20215
  Box18,
20048
20216
  {
20049
20217
  flexDirection: "column",
@@ -20053,7 +20221,7 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
20053
20221
  overflow: "hidden",
20054
20222
  justifyContent: "flex-end",
20055
20223
  children: [
20056
- /* @__PURE__ */ jsxs17(Box18, { flexDirection: "column", width: "100%", flexShrink: 0, marginBottom: -transcriptWindow.effectiveScrollOffset, children: [
20224
+ /* @__PURE__ */ jsxs16(Box18, { flexDirection: "column", width: "100%", flexShrink: 0, marginBottom: -transcriptWindow.effectiveScrollOffset, children: [
20057
20225
  renderedTranscriptItems.map((item, i, arr) => {
20058
20226
  const measureRef = transcriptMeasureRef(item);
20059
20227
  const attachOverlayHint = overlayHintOnLastItem && i === overlayHintAttachItemIndex;
@@ -20078,14 +20246,15 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
20078
20246
  ]
20079
20247
  }
20080
20248
  ),
20081
- transcriptGuardRows > 0 ? /* @__PURE__ */ jsxs17(Box18, { height: transcriptGuardRows, flexShrink: 0, backgroundColor: surfaceBackground(), flexDirection: "row", width: "100%", overflow: "hidden", children: [
20249
+ transcriptGuardRows > 0 ? /* @__PURE__ */ jsxs16(Box18, { height: transcriptGuardRows, flexShrink: 0, backgroundColor: surfaceBackground(), flexDirection: "row", width: "100%", overflow: "hidden", children: [
20082
20250
  /* @__PURE__ */ jsx20(Box18, { flexGrow: 1, flexShrink: 1, overflow: "hidden" }),
20083
20251
  overlayHintFallbackRow ? /* @__PURE__ */ jsx20(Box18, { flexShrink: 0, width: guardHintWidth || 1, marginLeft: 1, marginRight: 1, justifyContent: "flex-end", overflow: "hidden", children: /* @__PURE__ */ jsx20(Text20, { color: promptStatusColor(inputHintTone), wrap: "truncate", children: inputHint }) }) : null
20084
20252
  ] }) : null
20085
20253
  ]
20086
20254
  }
20087
20255
  ),
20088
- /* @__PURE__ */ jsxs17(Box18, { flexDirection: "column", flexShrink: 0, width: "100%", backgroundColor: surfaceBackground(), children: [
20256
+ /* @__PURE__ */ jsxs16(Box18, { flexDirection: "column", flexShrink: 0, width: "100%", backgroundColor: surfaceBackground(), children: [
20257
+ panelTransitionClearRows > 0 ? /* @__PURE__ */ jsx20(Box18, { height: panelTransitionClearRows, flexShrink: 0, width: "100%", overflow: "hidden", backgroundColor: surfaceBackground() }) : null,
20089
20258
  floatingPanelRows > 0 ? /* @__PURE__ */ jsx20(Box18, { flexDirection: "column", flexShrink: 0, height: floatingPanelRows, overflow: "hidden", justifyContent: "flex-end", backgroundColor: surfaceBackground(), children: toolApproval ? /* @__PURE__ */ jsx20(
20090
20259
  Picker,
20091
20260
  {
@@ -20238,15 +20407,15 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
20238
20407
  hint: settingsPrompt.hint || "Save setting.",
20239
20408
  columns: frameColumns,
20240
20409
  initialValue: settingsPrompt.initialValue || "",
20241
- actionLabel: settingsPrompt.kind === "skill-use" ? "run" : settingsPrompt.kind === "project-new" ? "open" : settingsPrompt.kind === "project-create-confirm" ? "confirm" : settingsPrompt.kind === "project-rename" ? "rename" : settingsPrompt.kind === "core-add" ? "add" : settingsPrompt.kind === "core-edit" ? "save" : settingsPrompt.kind === "core-delete-confirm" ? "confirm" : "save",
20242
- promptLabel: settingsPrompt.kind === "skill-use" ? "Command > " : settingsPrompt.kind === "project-new" ? "Path > " : settingsPrompt.kind === "project-create-confirm" ? "Create? (y/n) > " : settingsPrompt.kind === "project-rename" ? "Name > " : settingsPrompt.kind === "core-add" ? "Sentence > " : settingsPrompt.kind === "core-edit" ? "Sentence > " : settingsPrompt.kind === "core-delete-confirm" ? "Delete? (y/n) > " : "Value > ",
20410
+ actionLabel: settingsPrompt.kind === "skill-use" ? "run" : settingsPrompt.kind === "autoclear-provider" ? "save" : settingsPrompt.kind === "project-new" ? "open" : settingsPrompt.kind === "project-create-confirm" ? "confirm" : settingsPrompt.kind === "project-rename" ? "rename" : settingsPrompt.kind === "core-add" ? "add" : settingsPrompt.kind === "core-edit" ? "save" : settingsPrompt.kind === "core-delete-confirm" ? "confirm" : "save",
20411
+ promptLabel: settingsPrompt.kind === "skill-use" ? "Command > " : settingsPrompt.kind === "autoclear-provider" ? "Duration > " : settingsPrompt.kind === "project-new" ? "Path > " : settingsPrompt.kind === "project-create-confirm" ? "Create? (y/n) > " : settingsPrompt.kind === "project-rename" ? "Name > " : settingsPrompt.kind === "core-add" ? "Sentence > " : settingsPrompt.kind === "core-edit" ? "Sentence > " : settingsPrompt.kind === "core-delete-confirm" ? "Delete? (y/n) > " : "Value > ",
20243
20412
  onSubmit,
20244
20413
  onCancel: cancelSettingsPrompt
20245
20414
  }
20246
20415
  ) : null }) : null,
20247
- !inputBoxHidden ? /* @__PURE__ */ jsxs17(Fragment6, { children: [
20248
- promptMetaVisible ? /* @__PURE__ */ jsxs17(Fragment6, { children: [
20249
- /* @__PURE__ */ jsxs17(
20416
+ !inputBoxHidden ? /* @__PURE__ */ jsxs16(Fragment6, { children: [
20417
+ promptMetaVisible ? /* @__PURE__ */ jsxs16(Fragment6, { children: [
20418
+ /* @__PURE__ */ jsxs16(
20250
20419
  Box18,
20251
20420
  {
20252
20421
  marginTop: 0,
@@ -21770,7 +21939,7 @@ async function createEngineSession({
21770
21939
  const displayWindow = Number(status.contextWindow || 0);
21771
21940
  const compactBoundary = Number(status.compaction?.boundaryTokens || 0);
21772
21941
  const autoCompact = Number(
21773
- status.compaction?.autoCompactTokenLimit || runtime.session?.autoCompactTokenLimit || 0
21942
+ status.compaction?.triggerTokens || status.compaction?.autoCompactTokenLimit || runtime.session?.autoCompactTokenLimit || 0
21774
21943
  );
21775
21944
  if (displayWindow > 0) state.displayContextWindow = displayWindow;
21776
21945
  if (compactBoundary > 0) state.compactBoundaryTokens = compactBoundary;
@@ -22171,7 +22340,7 @@ async function createEngineSession({
22171
22340
  const earlyResultBuffer = /* @__PURE__ */ new Map();
22172
22341
  const aggregateCards = [];
22173
22342
  let tailAggregate = null;
22174
- const TOOL_CARD_PUSH_DELAY_MS = 1e3;
22343
+ const TOOL_CARD_PUSH_DELAY_MS = 0;
22175
22344
  let deferredSeqCounter = 0;
22176
22345
  const deferredEntries = [];
22177
22346
  const flushDeferredUpTo = (entry) => {
@@ -22408,7 +22577,7 @@ async function createEngineSession({
22408
22577
  thinkingSegmentStartedAt = 0;
22409
22578
  return now;
22410
22579
  };
22411
- const STREAM_BATCH_INTERVAL_MS = 8;
22580
+ const STREAM_BATCH_INTERVAL_MS = 16;
22412
22581
  let _batchTimer = null;
22413
22582
  let _pendingTextFlush = false;
22414
22583
  let _pendingThinkFlush = false;
@@ -22552,6 +22721,7 @@ async function createEngineSession({
22552
22721
  await yieldToRenderer();
22553
22722
  }
22554
22723
  const touchedAggregates = /* @__PURE__ */ new Set();
22724
+ let standaloneReserve = null;
22555
22725
  for (let i = 0; i < batchCalls.length; i++) {
22556
22726
  const c = batchCalls[i];
22557
22727
  const name = toolCallName(c);
@@ -22588,6 +22758,7 @@ async function createEngineSession({
22588
22758
  cardByCallId.set(callId, card2);
22589
22759
  }
22590
22760
  toolCards.push(card2);
22761
+ standaloneReserve = card2;
22591
22762
  tailAggregate = null;
22592
22763
  continue;
22593
22764
  }
@@ -22610,6 +22781,7 @@ async function createEngineSession({
22610
22781
  for (const aggregateCard of touchedAggregates) {
22611
22782
  syncAggregateHeader(aggregateCard);
22612
22783
  }
22784
+ standaloneReserve?.ensureVisible?.();
22613
22785
  if (committedAssistantSegment) {
22614
22786
  const firstTouchedAggregate = [...touchedAggregates][0] || null;
22615
22787
  firstTouchedAggregate?.ensureVisible?.();
@@ -22645,7 +22817,7 @@ async function createEngineSession({
22645
22817
  detail: compactEventDetail(event)
22646
22818
  });
22647
22819
  },
22648
- onStageChange: (stage) => {
22820
+ onStageChange: async (stage) => {
22649
22821
  if (!state.spinner) return;
22650
22822
  const value = String(stage || "");
22651
22823
  if (value === "compacting") {
@@ -22664,6 +22836,7 @@ async function createEngineSession({
22664
22836
  mode: "compacting"
22665
22837
  }
22666
22838
  });
22839
+ await yieldToRenderer();
22667
22840
  return;
22668
22841
  }
22669
22842
  if (value === "requesting" || value === "streaming") compactingActive = false;
@@ -23493,6 +23666,7 @@ async function createEngineSession({
23493
23666
  const startedAt2 = Date.now();
23494
23667
  set({ commandBusy: true, commandStatus: { active: true, verb: "Compacting conversation", startedAt: startedAt2, mode: "compacting" } });
23495
23668
  try {
23669
+ await new Promise((resolve5) => setTimeout(resolve5, 0));
23496
23670
  const result = await runtime.compact({ recoverAgent: true });
23497
23671
  syncContextStats({ allowEstimated: true });
23498
23672
  set({ ...routeState(), stats: { ...state.stats } });
@@ -23516,6 +23690,13 @@ async function createEngineSession({
23516
23690
  error: result.error
23517
23691
  })
23518
23692
  });
23693
+ } else {
23694
+ pushItem({
23695
+ kind: "statusdone",
23696
+ id: nextId(),
23697
+ label: "Compact failed",
23698
+ detail: "no active session"
23699
+ });
23519
23700
  }
23520
23701
  return result;
23521
23702
  } finally {
@@ -23806,14 +23987,9 @@ async function createEngineSession({
23806
23987
  pushNotice("webhook/ngrok authtoken forgotten", "info");
23807
23988
  return result;
23808
23989
  },
23809
- saveChannel: (entry) => {
23810
- const result = runtime.saveChannel(entry);
23811
- pushNotice(`channel saved: ${entry.name}`, "info");
23812
- return result;
23813
- },
23814
- deleteChannel: (name) => {
23815
- const result = runtime.deleteChannel(name);
23816
- pushNotice(`channel deleted: ${name}`, "info");
23990
+ setChannel: (entry) => {
23991
+ const result = runtime.setChannel(entry);
23992
+ pushNotice("channel saved", "info");
23817
23993
  return result;
23818
23994
  },
23819
23995
  setWebhookConfig: (patch) => {
@@ -24339,7 +24515,9 @@ function paintBootSplash() {
24339
24515
  try {
24340
24516
  const cols = Math.max(1, Number(process.stdout.columns) || 80);
24341
24517
  const rows = Math.max(1, Number(process.stdout.rows) || 24);
24342
- const center = (s) => `${" ".repeat(Math.max(0, Math.floor((cols - s.length) / 2)))}${s}`;
24518
+ const windowsLikeTerminal = process.platform === "win32" || Boolean(process.env.WT_SESSION);
24519
+ const frameCols = Math.max(1, cols - (windowsLikeTerminal ? 1 : 0));
24520
+ const center = (s) => `${" ".repeat(Math.max(0, Math.floor((frameCols - displayWidth(s)) / 2)))}${s}`;
24343
24521
  const logo = [
24344
24522
  "\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 ",
24345
24523
  "\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2554\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D ",
@@ -24360,38 +24538,8 @@ function paintBootSplash() {
24360
24538
  }
24361
24539
  out += "\r\n";
24362
24540
  out += `${subtleFg}${center(`mixdog coding agent \xB7 ${process.cwd()}`)}${reset}`;
24363
- const floating = Math.min(17, rows - 15);
24364
- let spinnerRow = 0;
24365
- let spinnerFg = "";
24366
- if (floating >= 3) {
24367
- const borderFg = ansiFg(theme.promptBorder) || subtleFg;
24368
- const boxW = Math.max(4, cols - 1);
24369
- const innerW = boxW - 2;
24370
- const topRow = rows - 3 - floating + 1;
24371
- out += `\x1B[${topRow};1H${borderFg}\u256D${"\u2500".repeat(innerW)}\u256E`;
24372
- for (let i = 1; i < floating - 1; i++) {
24373
- out += `\x1B[${topRow + i};1H\u2502${" ".repeat(innerW)}\u2502`;
24374
- }
24375
- out += `\x1B[${rows - 3};1H\u2570${"\u2500".repeat(innerW)}\u256F${reset}`;
24376
- spinnerRow = topRow + 1;
24377
- spinnerFg = subtleFg;
24378
- out += `\x1B[${spinnerRow};3H${subtleFg} Loading projects\u2026${reset}`;
24379
- }
24380
24541
  out += "\x1B[H";
24381
24542
  process.stdout.write(out);
24382
- if (spinnerRow > 0) {
24383
- const frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
24384
- let tick = 0;
24385
- const timer = setInterval(() => {
24386
- try {
24387
- process.stdout.write(`\x1B[${spinnerRow};3H${spinnerFg}${frames[tick % frames.length]}${reset}\x1B[H`);
24388
- tick += 1;
24389
- } catch {
24390
- }
24391
- }, 80);
24392
- timer.unref?.();
24393
- return { stop: () => clearInterval(timer) };
24394
- }
24395
24543
  return { stop: () => {
24396
24544
  } };
24397
24545
  } catch {
@@ -24591,7 +24739,7 @@ async function runTui({ provider, model, toolMode, remote, forceOnboarding } = {
24591
24739
  registerStdioDeath(process.stderr, "error", { requireCode: true });
24592
24740
  registerStdioDeath(process.stderr, "close");
24593
24741
  try {
24594
- const instance = render(/* @__PURE__ */ jsx21(App, { store, forceOnboarding: forceOnboarding === true }), { exitOnCtrlC: false, maxFps: 120, onRender: makeRenderProfiler() });
24742
+ const instance = render(/* @__PURE__ */ jsx21(App, { store, forceOnboarding: forceOnboarding === true }), { exitOnCtrlC: false, maxFps: 60, onRender: makeRenderProfiler() });
24595
24743
  bootProfile2("render:mounted", { ms: (performance3.now() - startedAt).toFixed(1) });
24596
24744
  const { waitUntilExit } = instance;
24597
24745
  if (mouseTracking && typeof instance.setSelection === "function") {