@wrongstack/tui 0.257.2 → 0.260.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { writeErr, resolveProjectDir, wstackGlobalRoot, GlobalMailbox, resolveWstackPaths, loadGoal, InputBuilder, DefaultSessionRewinder, writeOut, formatTodosList, buildGoalPreamble, expectDefined as expectDefined$1, shouldEnhance, enhanceUserPrompt, recentTextTurns, normalizedEqual, truncate, buildChildEnv } from '@wrongstack/core';
1
+ import { writeErr, resolveProjectDir, wstackGlobalRoot, GlobalMailbox, resolveWstackPaths, loadGoal, InputBuilder, DefaultSessionRewinder, writeOut, formatTodosList, buildGoalPreamble, expectDefined as expectDefined$1, shouldEnhance, enhanceUserPrompt, recentTextTurns, normalizedEqual, buildChildEnv } from '@wrongstack/core';
2
2
  export { buildGoalPreamble } from '@wrongstack/core';
3
3
  import { Box as Box$1, useInput, useStdin, useStdout, Text as Text$1, render, useApp, measureElement, Static } from 'ink';
4
4
  import { randomUUID } from 'crypto';
@@ -126,9 +126,13 @@ function modeIcon(label) {
126
126
  const icon = MODE_ICONS[label] ?? "\u25AA";
127
127
  return `${icon} ${label}`;
128
128
  }
129
- function truncateLabel(label, maxWidth) {
130
- const stripped = label.replace(/^\/next\s+[\d\s]+\s*/, "");
131
- return truncate(stripped, maxWidth);
129
+ function formatSuggestionLabel(label, maxLen = 28) {
130
+ const stripped = label.replace(/^\/next\s+[\d\s]+\s*/, "").trim();
131
+ if (!stripped) return "";
132
+ if (stripped.length <= maxLen) return stripped;
133
+ const shortened = stripped.slice(0, maxLen);
134
+ const lastSpace = shortened.lastIndexOf(" ");
135
+ return lastSpace > 10 ? `${shortened.slice(0, lastSpace)}\u2026` : `${shortened}\u2026`;
132
136
  }
133
137
  var COMPACT_THRESHOLD = 50;
134
138
  var COMFORTABLE_THRESHOLD = 90;
@@ -215,6 +219,7 @@ function StatusBar({
215
219
  const hasDebugStream = !!debugStreamStats;
216
220
  const hasEnhanceCountdown = enhanceCountdown != null && enhanceCountdown > 0;
217
221
  const hasNextStepsAutoSubmit = nextStepsAutoSubmitCountdown != null && nextStepsAutoSubmitCountdown > 0;
222
+ const countdownColor = nextStepsAutoSubmitCountdown != null ? nextStepsAutoSubmitCountdown > 20 ? "green" : nextStepsAutoSubmitCountdown > 10 ? "yellow" : "red" : "green";
218
223
  const hasTaskActivity = tasks && (tasks.pending > 0 || tasks.inProgress > 0 || tasks.completed > 0 || tasks.blocked > 0 || tasks.failed > 0);
219
224
  const hasThirdLine = todos && (todos.pending > 0 || todos.inProgress > 0 || todos.completed > 0) || plan && (plan.open > 0 || plan.inProgress > 0 || plan.done > 0) || hasTaskActivity || fleetHasActivity || hasBrainActivity || hasDebugStream || hasEnhanceCountdown || hasNextStepsAutoSubmit;
220
225
  return /* @__PURE__ */ jsxs(
@@ -574,7 +579,7 @@ function StatusBar({
574
579
  ] }) : null,
575
580
  hasEnhanceCountdown && enhanceCountdown != null ? /* @__PURE__ */ jsxs(Fragment, { children: [
576
581
  todos && (todos.pending > 0 || todos.inProgress > 0 || todos.completed > 0) || plan && (plan.open > 0 || plan.inProgress > 0 || plan.done > 0) || fleetHasActivity || hasBrainActivity || hasDebugStream ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
577
- /* @__PURE__ */ jsxs(Text, { color: enhanceCountdown <= 5 ? "yellow" : "cyan", children: [
582
+ /* @__PURE__ */ jsxs(Text, { color: enhanceCountdown > 15 ? "green" : enhanceCountdown > 5 ? "yellow" : "red", children: [
578
583
  "\u23F3 auto-send in ",
579
584
  enhanceCountdown,
580
585
  "s"
@@ -582,12 +587,14 @@ function StatusBar({
582
587
  ] }) : null,
583
588
  hasNextStepsAutoSubmit && nextStepsAutoSubmitCountdown != null ? /* @__PURE__ */ jsxs(Fragment, { children: [
584
589
  todos && (todos.pending > 0 || todos.inProgress > 0 || todos.completed > 0) || plan && (plan.open > 0 || plan.inProgress > 0 || plan.done > 0) || fleetHasActivity || hasBrainActivity || hasDebugStream || hasEnhanceCountdown ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
585
- /* @__PURE__ */ jsxs(Text, { color: nextStepsAutoSubmitCountdown <= 3 ? "yellow" : "cyan", children: [
590
+ /* @__PURE__ */ jsxs(Text, { color: countdownColor, bold: true, children: [
586
591
  "\u23F3 ",
587
- nextStepsAutoSubmitLabel ? truncateLabel(nextStepsAutoSubmitLabel, 30) : "next step",
588
- " in ",
589
592
  nextStepsAutoSubmitCountdown,
590
593
  "s"
594
+ ] }),
595
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
596
+ " ",
597
+ nextStepsAutoSubmitLabel ? formatSuggestionLabel(nextStepsAutoSubmitLabel) : ""
591
598
  ] })
592
599
  ] }) : null
593
600
  ] }) : /* @__PURE__ */ jsx(Box, { height: 1, children: /* @__PURE__ */ jsx(Text, { children: " " }) }),
@@ -1564,9 +1571,9 @@ function buttonLabels(suggestedPattern) {
1564
1571
  function stringifyInput(input) {
1565
1572
  if (!input || typeof input !== "object") return "";
1566
1573
  const obj = input;
1567
- return Object.entries(obj).filter(([k]) => k !== "content" && k !== "new_string").map(([k, v]) => `${k}: ${truncate2(JSON.stringify(v), 80)}`).join(" ");
1574
+ return Object.entries(obj).filter(([k]) => k !== "content" && k !== "new_string").map(([k, v]) => `${k}: ${truncate(JSON.stringify(v), 80)}`).join(" ");
1568
1575
  }
1569
- function truncate2(s2, max) {
1576
+ function truncate(s2, max) {
1570
1577
  return s2.length <= max ? s2 : `${s2.slice(0, max - 1)}\u2026`;
1571
1578
  }
1572
1579
  function hasDiff(input) {
@@ -3915,6 +3922,9 @@ function parseWithHeading(content, strict) {
3915
3922
  if (steps.length === 0) {
3916
3923
  return { steps: [], texts: [], stripped: content, autoTexts: [] };
3917
3924
  }
3925
+ if (strict && !afterHeading.includes("</next_steps>")) {
3926
+ return { steps: [], texts: [], stripped: content, autoTexts: [] };
3927
+ }
3918
3928
  const texts = steps.map((s2) => s2.text);
3919
3929
  const autoTexts = steps.filter((s2) => s2.auto).map((s2) => s2.text);
3920
3930
  const blockStart = headingMatch.index;
@@ -3948,6 +3958,9 @@ function findBlockEnd(afterHeading, stepCount) {
3948
3958
  }
3949
3959
  return consumed;
3950
3960
  }
3961
+ function stripNextStepsBlock(text) {
3962
+ return text.replace(/<next_steps\b[^>]*>[\s\S]*?<\/next_steps>/gi, "").replace(/<next_steps\b[^>]*\/?>/gi, "").replace(/\n{3,}/g, "\n\n").trim();
3963
+ }
3951
3964
  function brainStatusStyle(status) {
3952
3965
  switch (status) {
3953
3966
  case "thinking":
@@ -3977,7 +3990,9 @@ function brainRiskColor(risk) {
3977
3990
  var Entry = React5.memo(function Entry2({
3978
3991
  entry,
3979
3992
  termWidth,
3980
- setSuggestions
3993
+ setSuggestions,
3994
+ autonomyMode,
3995
+ autoSubmitCountdown
3981
3996
  }) {
3982
3997
  const nextSteps = useMemo(() => {
3983
3998
  if (entry.kind !== "assistant") return { steps: [], stripped: "" };
@@ -3985,6 +4000,7 @@ var Entry = React5.memo(function Entry2({
3985
4000
  }, [entry.kind, entry.text]);
3986
4001
  useEffect(() => {
3987
4002
  if (!setSuggestions) return;
4003
+ if (entry.kind !== "assistant") return;
3988
4004
  const text = entry.text ?? "";
3989
4005
  const { texts } = parseNextSteps(text, true);
3990
4006
  if (texts.length > 0) setSuggestions(texts);
@@ -4055,10 +4071,11 @@ var Entry = React5.memo(function Entry2({
4055
4071
  /* @__PURE__ */ jsx(Text, { bold: true, color: theme.accent, children: "\u{1F4A1} NEXT STEPS " }),
4056
4072
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "(use /next 1, /next 1 2 3 to select)" })
4057
4073
  ] }),
4058
- steps.map((s2) => /* @__PURE__ */ jsx(Box, { flexDirection: "row", marginTop: 0, children: /* @__PURE__ */ jsxs(Text, { children: [
4074
+ steps.map((s2, i) => /* @__PURE__ */ jsx(Box, { flexDirection: "row", marginTop: 0, children: /* @__PURE__ */ jsxs(Text, { children: [
4059
4075
  /* @__PURE__ */ jsx(Text, { bold: true, color: theme.accent, children: ` ${s2.index}. ` }),
4060
4076
  /* @__PURE__ */ jsx(Text, { children: s2.text }),
4061
- s2.auto ? /* @__PURE__ */ jsx(Text, { color: "cyan", dimColor: true, children: " auto" }) : null
4077
+ s2.auto ? /* @__PURE__ */ jsx(Text, { color: "cyan", dimColor: true, children: " auto" }) : null,
4078
+ autonomyMode === "auto" && i === 0 ? autoSubmitCountdown != null && autoSubmitCountdown > 0 ? /* @__PURE__ */ jsx(Text, { color: autoSubmitCountdown > 20 ? "green" : autoSubmitCountdown > 10 ? "yellow" : "red", children: ` auto in ${autoSubmitCountdown}s` }) : /* @__PURE__ */ jsx(Text, { color: "cyan", children: " \u23E9" }) : null
4062
4079
  ] }) }, s2.index))
4063
4080
  ]
4064
4081
  }
@@ -4230,7 +4247,7 @@ var Entry = React5.memo(function Entry2({
4230
4247
  }
4231
4248
  }
4232
4249
  });
4233
- function History({ entries, generation, streamingText, toolStream, setSuggestions }) {
4250
+ function History({ entries, generation, streamingText, toolStream, setSuggestions, autonomyMode, autoSubmitCountdown }) {
4234
4251
  const { stdout } = useStdout();
4235
4252
  const [termSize, setTermSize] = useState({
4236
4253
  columns: stdout?.columns ?? 80,
@@ -4248,7 +4265,7 @@ function History({ entries, generation, streamingText, toolStream, setSuggestion
4248
4265
  const termWidth = termSize.columns;
4249
4266
  const tail = streamingText ? tailForDisplay(streamingText, MAX_STREAM_DISPLAY_CHARS) : "";
4250
4267
  return /* @__PURE__ */ jsxs(Fragment, { children: [
4251
- /* @__PURE__ */ jsx(Static, { items: entries, children: (entry) => /* @__PURE__ */ jsx(Box, { marginBottom: entry.kind === "turn-summary" ? 1 : 0, children: /* @__PURE__ */ jsx(Entry, { entry, termWidth, setSuggestions }) }, entry.id) }, generation ?? 0),
4268
+ /* @__PURE__ */ jsx(Static, { items: entries, children: (entry) => /* @__PURE__ */ jsx(Box, { marginBottom: entry.kind === "turn-summary" ? 1 : 0, children: /* @__PURE__ */ jsx(Entry, { entry, termWidth, setSuggestions, autonomyMode, autoSubmitCountdown }) }, entry.id) }, generation ?? 0),
4252
4269
  /* @__PURE__ */ jsx(Box, { flexGrow: 1, children: tail ? /* @__PURE__ */ jsx(AssistantTail, { text: tail, termWidth }) : null })
4253
4270
  ] });
4254
4271
  }
@@ -4298,7 +4315,9 @@ function ScrollableHistory({
4298
4315
  totalLines,
4299
4316
  onMeasure,
4300
4317
  maxWidth,
4301
- setSuggestions
4318
+ setSuggestions,
4319
+ autonomyMode,
4320
+ autoSubmitCountdown
4302
4321
  }) {
4303
4322
  const { stdout } = useStdout();
4304
4323
  const rawWidth = stdout?.columns ?? 80;
@@ -4337,7 +4356,7 @@ function ScrollableHistory({
4337
4356
  flexShrink: 0,
4338
4357
  children: [
4339
4358
  hiddenCount > 0 ? /* @__PURE__ */ jsx(Box, { flexShrink: 0, children: /* @__PURE__ */ jsx(Text, { dimColor: true, italic: true, children: ` \u2191 ${hiddenCount} earlier ${hiddenCount === 1 ? "entry" : "entries"} (scroll lives in this session; full log on disk)` }) }) : null,
4340
- shown.map((entry) => /* @__PURE__ */ jsx(Box, { marginBottom: entry.kind === "turn-summary" ? 1 : 0, flexShrink: 0, children: /* @__PURE__ */ jsx(Entry, { entry, termWidth, setSuggestions }) }, entry.id)),
4359
+ shown.map((entry) => /* @__PURE__ */ jsx(Box, { marginBottom: entry.kind === "turn-summary" ? 1 : 0, flexShrink: 0, children: /* @__PURE__ */ jsx(Entry, { entry, termWidth, setSuggestions, autonomyMode, autoSubmitCountdown }) }, entry.id)),
4341
4360
  tail ? /* @__PURE__ */ jsx(AssistantTail, { text: tail, termWidth }) : null,
4342
4361
  toolTail && toolStream ? /* @__PURE__ */ jsx(
4343
4362
  ToolStreamBox,
@@ -6456,10 +6475,10 @@ function useDirectorFleetBridge({
6456
6475
  let streamFlushTimer = null;
6457
6476
  const flushStreamBufs = () => {
6458
6477
  for (const [id, text] of streamBuf) {
6459
- const trimmed = text.trim();
6460
- if (!trimmed) continue;
6478
+ const cleaned = stripNextStepsBlock(text);
6479
+ if (!cleaned) continue;
6461
6480
  const label = labelFor(labelsRef, id);
6462
- enq({ type: "fleetMessage", id, text: trimmed });
6481
+ enq({ type: "fleetMessage", id, text: cleaned });
6463
6482
  if (streamFleetRef.current) {
6464
6483
  enq({
6465
6484
  type: "addEntry",
@@ -6468,7 +6487,7 @@ function useDirectorFleetBridge({
6468
6487
  agentLabel: label.label,
6469
6488
  agentColor: label.color,
6470
6489
  icon: "\u{1F4AC}",
6471
- text: trimmed
6490
+ text: cleaned
6472
6491
  }
6473
6492
  });
6474
6493
  }
@@ -10100,7 +10119,6 @@ function App({
10100
10119
  nextStepsAutoSubmitSuggestionRef.current = null;
10101
10120
  if (suggestion) {
10102
10121
  autoSubmitStreakRef.current += 1;
10103
- setDraft(suggestion, suggestion.length);
10104
10122
  void (async () => {
10105
10123
  const trimmed = suggestion.trim();
10106
10124
  if (!trimmed) {
@@ -12014,7 +12032,6 @@ ${content}
12014
12032
  while (stateRef.current.status !== "idle" && Date.now() - start < 1500) {
12015
12033
  await new Promise((r) => setTimeout(r, 25));
12016
12034
  }
12017
- setDraft(res.runText, res.runText.length);
12018
12035
  try {
12019
12036
  await runBlocks(blocks2);
12020
12037
  } finally {
@@ -12215,7 +12232,9 @@ User message:
12215
12232
  viewportRows: state.viewportRows,
12216
12233
  totalLines: state.totalLines,
12217
12234
  onMeasure: (totalLines) => dispatch({ type: "setMeasuredLines", totalLines }),
12218
- setSuggestions
12235
+ setSuggestions,
12236
+ autonomyMode: autonomyLive,
12237
+ autoSubmitCountdown: nextStepsAutoSubmitCountdown
12219
12238
  }
12220
12239
  ) : /* @__PURE__ */ jsx(
12221
12240
  History,
@@ -12224,7 +12243,9 @@ User message:
12224
12243
  generation: state.historyGen,
12225
12244
  streamingText: state.streamingText,
12226
12245
  toolStream: state.toolStream,
12227
- setSuggestions
12246
+ setSuggestions,
12247
+ autonomyMode: autonomyLive,
12248
+ autoSubmitCountdown: nextStepsAutoSubmitCountdown
12228
12249
  }
12229
12250
  ),
12230
12251
  /* @__PURE__ */ jsxs(Box, { flexDirection: "column", flexShrink: 0, ref: bottomRegionRef, children: [