@wrongstack/tui 0.283.1 → 0.284.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.d.ts CHANGED
@@ -2,7 +2,9 @@ import * as _wrongstack_core from '@wrongstack/core';
2
2
  import { TokenSavingTier, Agent, SlashCommandRegistry, AttachmentStore, EventBus, TokenCounter, QueueStore, AutonomyStage, Director, Message, AutonomousCoordinator, CoordinatorEvent, SessionEvent } from '@wrongstack/core';
3
3
  export { buildGoalPreamble } from '@wrongstack/core';
4
4
  import { VisionAdapters } from '@wrongstack/runtime/vision';
5
+ import { SddRunControl, SddLifecycleResult } from '@wrongstack/sdd';
5
6
  import React from 'react';
7
+ export { ParseNextStepsResult, ParsedNextStep, parseNextSteps } from '@wrongstack/tools/next-steps';
6
8
 
7
9
  /** A single session row from the SessionRegistry, exposed via WebSocket. */
8
10
  interface LiveSessionEntry {
@@ -402,14 +404,14 @@ interface RunTuiOptions {
402
404
  * Access the active SDD parallel run's control surface (or null). The TUI's
403
405
  * SIGINT handler uses it to stop a running `/sdd parallel` on the first Ctrl+C.
404
406
  */
405
- getSddRun?: (() => _wrongstack_core.SddRunControl | null) | undefined;
407
+ getSddRun?: (() => SddRunControl | null) | undefined;
406
408
  /**
407
409
  * Apply a post-run SDD lifecycle op (clean / rollback / destroy) from the host.
408
410
  * Drives the board overlay keys c / z / x so they work after the run ends.
409
411
  */
410
412
  onSddLifecycle?: ((op: 'cleanup_worktrees' | 'rollback' | 'destroy', opts?: {
411
413
  revertMerged?: boolean;
412
- }) => Promise<_wrongstack_core.SddLifecycleResult>) | undefined;
414
+ }) => Promise<SddLifecycleResult>) | undefined;
413
415
  /**
414
416
  * Subscribe to live per-iteration events from the eternal engine.
415
417
  * Returns an unsubscribe function. TUI uses this to render each
@@ -905,53 +907,4 @@ declare function parseInline(text: string): InlineToken[];
905
907
  */
906
908
  declare function replaySessionEvents(events: SessionEvent[], startId: number): HistoryEntry[];
907
909
 
908
- /**
909
- * Unified next-steps suggestion parser.
910
- *
911
- * Three code paths feed into the suggestion store:
912
- * 1. TUI rendering — entry.tsx parses only "<nextsteps>" from assistant output
913
- * 2. REPL store — repl.ts parses only "<nextsteps>" from final agent output
914
- * 3. /suggest output — suggest.ts parses LLM-generated numbered lists
915
- *
916
- * Heading mode (`requireHeading = true`):
917
- * Assistant-output paths accept only balanced <nextsteps>...</nextsteps> blocks.
918
- * Loose headings like "Next steps:" are intentionally ignored so `/next` only
919
- * activates for the canonical machine-readable format.
920
- *
921
- * Raw mode (`requireHeading = false`):
922
- * Parses numbered/bullet items from anywhere in text (subagent /suggest output).
923
- *
924
- * Supported assistant-output format:
925
- * <nextsteps> (canonical XML tag format)
926
- */
927
- interface ParsedNextStep {
928
- index: number;
929
- text: string;
930
- /** Whether this item has auto="true" attribute for YOLO+auto autonomy mode. */
931
- auto?: boolean;
932
- }
933
- interface ParseNextStepsResult {
934
- /** Matched steps with their original index and stripped text. */
935
- steps: ParsedNextStep[];
936
- /** Flat string array — what gets stored in the suggestion store. */
937
- texts: string[];
938
- /**
939
- * Content with the entire canonical "<nextsteps>" block removed.
940
- * Used by entry.tsx to strip suggestions from the rendered message body.
941
- */
942
- stripped: string;
943
- /** Flat string array — texts of items with auto="true" attribute only. */
944
- autoTexts: string[];
945
- }
946
- /**
947
- * Parse canonical "<nextsteps>" blocks from assistant output (or raw numbered lines).
948
- *
949
- * @param content — raw assistant message text or subagent output
950
- * @param strict — retained for compatibility; assistant-output paths always require the canonical XML tag.
951
- * @param requireHeading — when true, a canonical XML tag must precede the item list.
952
- * when false, numbered/bullet items are parsed from anywhere in text
953
- * (used by /suggest subagent output which has no heading).
954
- */
955
- declare function parseNextSteps(content: string, strict?: boolean, requireHeading?: boolean): ParseNextStepsResult;
956
-
957
- export { type AuthCatalogRow, type AuthFlowIo, type AuthFlowResult, type AuthKeyRow, type AuthLocalPresetRow, type AuthOAuthKind, type AuthPanelHost, type AuthProviderRow, type ParseNextStepsResult, type ParsedNextStep, type PluginPickerItem, type RunTuiOptions, type Settings, parseInline, parseNextSteps, replaySessionEvents, runTui };
910
+ export { type AuthCatalogRow, type AuthFlowIo, type AuthFlowResult, type AuthKeyRow, type AuthLocalPresetRow, type AuthOAuthKind, type AuthPanelHost, type AuthProviderRow, type PluginPickerItem, type RunTuiOptions, type Settings, parseInline, replaySessionEvents, runTui };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { hasOpenTodos, writeErr, resolveProjectDir, wstackGlobalRoot, createHqPublisherFromEnv, startFleetTelemetryBridge, startBrainTelemetryBridge, startWorktreeTelemetryBridge, startToolTelemetryBridge, startCostTelemetryBridge, GlobalMailbox, resolveWstackPaths, loadGoal, InputBuilder, DefaultSessionRewinder, writeOut, formatTodosList, buildGoalPreamble, expectDefined as expectDefined$1, InputHistoryStore, DefaultSecretScrubber, INPUT_HISTORY_DEFAULT_MAX, DefaultPromptLoader, PromptUsageStore, getDesignKitLoader, clearActiveKit, clearPersistedActiveKit, recordOverrides, setDesignOverrides, loadActiveKit, isDesignStack, materializeTokens, applyTokenOverrides, setActiveKit, projectSlug, detectContinueIntent, resolveContinuation, shouldEnhance, enhanceUserPrompt, recentTextTurns, normalizedEqual, setBtwNote, listBoards, getBoard, createBoard, addTask, buildChildEnv, removeTask, moveTask, updateTask, duplicateBoard, copyTaskToBoard, transferTaskToBoard } from '@wrongstack/core';
1
+ import { hasOpenTodos, writeErr, resolveProjectDir, wstackGlobalRoot, createHqPublisherFromEnv, startFleetTelemetryBridge, startBrainTelemetryBridge, startWorktreeTelemetryBridge, startToolTelemetryBridge, startCostTelemetryBridge, GlobalMailbox, resolveWstackPaths, loadGoal, InputBuilder, DefaultSessionRewinder, writeOut, formatTodosList, buildGoalPreamble, expectDefined as expectDefined$1, InputHistoryStore, DefaultSecretScrubber, INPUT_HISTORY_DEFAULT_MAX, DefaultPromptLoader, PromptUsageStore, getDesignKitLoader, clearActiveKit, clearPersistedActiveKit, recordOverrides, setDesignOverrides, loadActiveKit, isDesignStack, materializeTokens, applyTokenOverrides, setActiveKit, projectSlug, detectContinueIntent, resolveContinuation, shouldEnhance, enhanceUserPrompt, recentTextTurns, normalizedEqual, setBtwNote, buildChildEnv } from '@wrongstack/core';
2
2
  export { buildGoalPreamble } from '@wrongstack/core';
3
3
  import { Box as Box$1, useStdout, Static, useInput, useStdin, measureElement, Text as Text$1, render, useApp } from 'ink';
4
4
  import { randomUUID } from 'crypto';
@@ -10,6 +10,10 @@ import { routeImagesForModel } from '@wrongstack/runtime/vision';
10
10
  import { getIndexState, onIndexStateChange, getProcessRegistry } from '@wrongstack/tools';
11
11
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
12
12
  import { getToolIcon, TOOL_ICON_CONFIG } from '@wrongstack/tools/tool-icons';
13
+ import { parseUnifiedDiffPreview } from '@wrongstack/tools/tool-diff';
14
+ import { parseNextSteps, stripNextStepsBlock } from '@wrongstack/tools/next-steps';
15
+ export { parseNextSteps } from '@wrongstack/tools/next-steps';
16
+ import { listBoards, getBoard, createBoard, addTask, removeTask, moveTask, updateTask, duplicateBoard, copyTaskToBoard, transferTaskToBoard } from '@wrongstack/kanban';
13
17
  import { spawn } from 'child_process';
14
18
  import * as v8 from 'v8';
15
19
  import { readClipboardText, readClipboardImage } from '@wrongstack/runtime/clipboard';
@@ -1686,6 +1690,7 @@ function clampContextLoad(load) {
1686
1690
  return Math.max(0, Math.min(1, load));
1687
1691
  }
1688
1692
  var MAX_TOOL_STREAM_RETAINED_CHARS = 1e5;
1693
+ var MAX_ASSISTANT_STREAM_RETAINED_CHARS = 16384;
1689
1694
  var MAX_RETAINED_INPUT_CHARS = 2048;
1690
1695
  var MAX_RETAINED_INPUT_DEPTH = 4;
1691
1696
  var MAX_RETAINED_INPUT_ITEMS = 64;
@@ -2120,8 +2125,11 @@ function reducer(state, action) {
2120
2125
  }
2121
2126
  };
2122
2127
  }
2123
- case "streamDelta":
2124
- return { ...state, streamingText: state.streamingText + action.delta };
2128
+ case "streamDelta": {
2129
+ const combined = state.streamingText + action.delta;
2130
+ const streamingText = combined.length > MAX_ASSISTANT_STREAM_RETAINED_CHARS ? combined.slice(-MAX_ASSISTANT_STREAM_RETAINED_CHARS) : combined;
2131
+ return { ...state, streamingText };
2132
+ }
2125
2133
  case "streamReset":
2126
2134
  return { ...state, streamingText: "" };
2127
2135
  case "status":
@@ -4251,7 +4259,7 @@ var SPINNER_INTERVAL_MS = 250;
4251
4259
  function tokenDisplayTotals(usage, currentRequest) {
4252
4260
  const usageInput = usage ? usage.input + (usage.cacheRead ?? 0) + (usage.cacheWrite ?? 0) : 0;
4253
4261
  const usageOutput = usage?.output ?? 0;
4254
- const fallbackInput = currentRequest ? currentRequest.input + currentRequest.cacheRead : 0;
4262
+ const fallbackInput = currentRequest ? currentRequest.input + currentRequest.cacheRead + (currentRequest.cacheWrite ?? 0) : 0;
4255
4263
  return {
4256
4264
  input: usageInput > 0 ? usageInput : fallbackInput,
4257
4265
  output: usageOutput
@@ -6102,6 +6110,10 @@ function renderRow(row, focused, i) {
6102
6110
  return /* @__PURE__ */ jsx(Text, { children: " " }, `r-${i}`);
6103
6111
  }
6104
6112
  }
6113
+ var URL_LINE_RE = /^https?:\/\/\S+$/;
6114
+ function isAuthFlowUrlLine(line) {
6115
+ return URL_LINE_RE.test(line.trim());
6116
+ }
6105
6117
  function viewTitle(panel) {
6106
6118
  switch (panel.view) {
6107
6119
  case "list":
@@ -6177,16 +6189,19 @@ function AuthPanel({ panel }) {
6177
6189
  ] }) }) : null,
6178
6190
  panel.view === "flow" ? /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
6179
6191
  logWindow.length === 0 && !panel.flowDone ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Starting\u2026" }) : null,
6180
- logWindow.map((line) => /* @__PURE__ */ jsx(
6181
- Text,
6182
- {
6183
- wrap: "truncate-end",
6184
- color: line.startsWith("\u2717") ? UI_COLORS.error : line.startsWith("\u2713") ? UI_COLORS.active : void 0,
6185
- dimColor: !line.startsWith("\u2717") && !line.startsWith("\u2713"),
6186
- children: line
6187
- },
6188
- line
6189
- )),
6192
+ logWindow.map((line) => {
6193
+ const isUrl = isAuthFlowUrlLine(line);
6194
+ return /* @__PURE__ */ jsx(
6195
+ Text,
6196
+ {
6197
+ wrap: isUrl ? "wrap" : "truncate-end",
6198
+ color: isUrl ? UI_COLORS.hint : line.startsWith("\u2717") ? UI_COLORS.error : line.startsWith("\u2713") ? UI_COLORS.active : void 0,
6199
+ dimColor: !isUrl && !line.startsWith("\u2717") && !line.startsWith("\u2713"),
6200
+ children: line
6201
+ },
6202
+ line
6203
+ );
6204
+ }),
6190
6205
  panel.flowDone ? /* @__PURE__ */ jsxs(Text, { color: panel.flowOk ? UI_COLORS.active : UI_COLORS.error, children: [
6191
6206
  panel.flowOk ? "\u2713 Done." : "\u2717 Not completed.",
6192
6207
  " ",
@@ -8565,8 +8580,24 @@ function tailForDisplay(text, maxChars) {
8565
8580
  }
8566
8581
  var MAX_CODE_LINES = 80;
8567
8582
  var DIFF_MAX_LINES = Number.POSITIVE_INFINITY;
8568
- var DIFF_LINE_SAFETY_CAP = 4e3;
8569
8583
  var DIFF_FALLBACK_WRAP_WIDTH = 100;
8584
+ var HARD_TAB_WIDTH = 8;
8585
+ function expandTabs(text, tabWidth = HARD_TAB_WIDTH) {
8586
+ if (!text.includes(" ")) return text;
8587
+ let out = "";
8588
+ let col = 0;
8589
+ for (const ch of text) {
8590
+ if (ch === " ") {
8591
+ const advance = tabWidth - col % tabWidth;
8592
+ out += " ".repeat(advance);
8593
+ col += advance;
8594
+ } else {
8595
+ out += ch;
8596
+ col += 1;
8597
+ }
8598
+ }
8599
+ return out;
8600
+ }
8570
8601
  function CodeBlock({
8571
8602
  code,
8572
8603
  lang,
@@ -8580,7 +8611,8 @@ function CodeBlock({
8580
8611
  const maxW = Math.max(20, Math.min(boxWidth - 4 - gutterW - 1, 120));
8581
8612
  let carry = {};
8582
8613
  const rows = lines.map((raw) => {
8583
- const display = raw.length > maxW ? `${raw.slice(0, maxW - 1)}\u2026` : raw;
8614
+ const expanded = expandTabs(raw);
8615
+ const display = expanded.length > maxW ? `${expanded.slice(0, maxW - 1)}\u2026` : expanded;
8584
8616
  const r = highlightLine(display, lang, carry);
8585
8617
  carry = r.carry;
8586
8618
  return r.tokens;
@@ -8759,9 +8791,9 @@ function DiffBlock({
8759
8791
  };
8760
8792
  const textForDisplay = (row) => {
8761
8793
  if ((row.kind === "add" || row.kind === "del" || row.kind === "ctx") && row.text.length > 0) {
8762
- return row.text.slice(1) || " ";
8794
+ return expandTabs(row.text.slice(1)) || " ";
8763
8795
  }
8764
- return row.text || " ";
8796
+ return expandTabs(row.text) || " ";
8765
8797
  };
8766
8798
  const stats = showStats ? formatDiffStats(added, removed) : null;
8767
8799
  const terminalWrapGuard = typeof contentWidth === "number" ? 1 : 0;
@@ -8831,64 +8863,7 @@ function DiffBlock({
8831
8863
  ] });
8832
8864
  }
8833
8865
  function parseUnifiedDiff(diff, maxLines) {
8834
- const all = [];
8835
- let oldLn = 0;
8836
- let newLn = 0;
8837
- for (const raw of diff.split("\n")) {
8838
- const line = raw.replace(/\r$/, "");
8839
- if (line.startsWith("+++") || line.startsWith("---")) continue;
8840
- if (line.startsWith("diff --git") || line.startsWith("index ")) continue;
8841
- if (line.startsWith("@@")) {
8842
- const m = line.match(/^@@\s+-(\d+)(?:,\d+)?\s+\+(\d+)(?:,\d+)?\s+@@/);
8843
- if (m) {
8844
- oldLn = Number.parseInt(m[1] ?? "0", 10) || 0;
8845
- newLn = Number.parseInt(m[2] ?? "0", 10) || 0;
8846
- }
8847
- all.push({ kind: "hunk", text: truncMid(line, 60) });
8848
- continue;
8849
- }
8850
- if (line.startsWith("+")) {
8851
- all.push({ kind: "add", text: truncMid(line, DIFF_LINE_SAFETY_CAP), newLine: newLn });
8852
- newLn++;
8853
- continue;
8854
- }
8855
- if (line.startsWith("-")) {
8856
- all.push({ kind: "del", text: truncMid(line, DIFF_LINE_SAFETY_CAP), oldLine: oldLn });
8857
- oldLn++;
8858
- continue;
8859
- }
8860
- if (line.startsWith("\\ No newline")) {
8861
- all.push({ kind: "meta", text: line });
8862
- continue;
8863
- }
8864
- if (line.length === 0) continue;
8865
- all.push({
8866
- kind: "ctx",
8867
- text: truncMid(line, DIFF_LINE_SAFETY_CAP),
8868
- oldLine: oldLn,
8869
- newLine: newLn
8870
- });
8871
- oldLn++;
8872
- newLn++;
8873
- }
8874
- const added = all.filter((row) => row.kind === "add").length;
8875
- const removed = all.filter((row) => row.kind === "del").length;
8876
- if (all.length === 0) {
8877
- return { rows: [], hidden: 0, added: 0, removed: 0, hiddenAdded: 0, hiddenRemoved: 0 };
8878
- }
8879
- if (all.length <= maxLines) {
8880
- return { rows: all, hidden: 0, added, removed, hiddenAdded: 0, hiddenRemoved: 0 };
8881
- }
8882
- const rows = all.slice(0, maxLines);
8883
- const hiddenRows = all.slice(maxLines);
8884
- return {
8885
- rows,
8886
- hidden: hiddenRows.length,
8887
- added,
8888
- removed,
8889
- hiddenAdded: hiddenRows.filter((row) => row.kind === "add").length,
8890
- hiddenRemoved: hiddenRows.filter((row) => row.kind === "del").length
8891
- };
8866
+ return parseUnifiedDiffPreview(diff, maxLines);
8892
8867
  }
8893
8868
  function extractDiffPreview(toolName, output, input) {
8894
8869
  if (!output) return void 0;
@@ -10940,118 +10915,6 @@ function Banner({
10940
10915
  ] })
10941
10916
  ] });
10942
10917
  }
10943
-
10944
- // src/components/suggestions.ts
10945
- var NEXT_STEPS_TAG_RE = /<nextsteps>\s*\n+/i;
10946
- var ITEM_RE = /^(?:(\d+)[.)]\s*|[-*•]\s*)(.+?)(\s+auto="true")?$/;
10947
- var MAX_STEPS = 6;
10948
- function parseNextSteps(content, strict = false, requireHeading = true) {
10949
- if (requireHeading) {
10950
- return parseWithHeading(content);
10951
- }
10952
- return parseRawNumbered(content);
10953
- }
10954
- function parseRawNumbered(content) {
10955
- const lines = content.split("\n");
10956
- const steps = [];
10957
- const seenNumbers = /* @__PURE__ */ new Set();
10958
- for (const rawLine of lines) {
10959
- const line = rawLine.trim();
10960
- if (!line) continue;
10961
- const m = ITEM_RE.exec(line);
10962
- if (!m) continue;
10963
- const numPart = m[1];
10964
- const text = m[2].trim();
10965
- const hasAuto = !!m[3];
10966
- let index;
10967
- if (numPart !== void 0) {
10968
- index = Number.parseInt(numPart, 10);
10969
- } else {
10970
- index = steps.length + 1;
10971
- }
10972
- if (seenNumbers.has(index)) continue;
10973
- if (text.length < 3) continue;
10974
- seenNumbers.add(index);
10975
- steps.push({ index, text, auto: hasAuto });
10976
- if (steps.length >= MAX_STEPS) break;
10977
- }
10978
- return {
10979
- steps,
10980
- texts: steps.map((s2) => s2.text),
10981
- stripped: content,
10982
- autoTexts: steps.filter((s2) => s2.auto).map((s2) => s2.text)
10983
- };
10984
- }
10985
- function parseWithHeading(content, _strict) {
10986
- const headingMatch = NEXT_STEPS_TAG_RE.exec(content);
10987
- if (!headingMatch) {
10988
- return { steps: [], texts: [], stripped: content, autoTexts: [] };
10989
- }
10990
- const headingEnd = headingMatch.index + headingMatch[0].length;
10991
- const afterHeading = content.slice(headingEnd);
10992
- const lines = afterHeading.split("\n");
10993
- const steps = [];
10994
- const seenNumbers = /* @__PURE__ */ new Set();
10995
- for (const rawLine of lines) {
10996
- const line = rawLine.trim();
10997
- if (!line) continue;
10998
- const m = ITEM_RE.exec(line);
10999
- if (!m) break;
11000
- const numPart = m[1];
11001
- const text = m[2].trim();
11002
- const hasAuto = !!m[3];
11003
- let index;
11004
- if (numPart !== void 0) {
11005
- index = Number.parseInt(numPart, 10);
11006
- } else {
11007
- index = steps.length + 1;
11008
- }
11009
- if (seenNumbers.has(index)) continue;
11010
- if (text.length < 3) continue;
11011
- seenNumbers.add(index);
11012
- steps.push({ index, text, auto: hasAuto });
11013
- if (steps.length >= MAX_STEPS) break;
11014
- }
11015
- if (steps.length === 0) {
11016
- return { steps: [], texts: [], stripped: content, autoTexts: [] };
11017
- }
11018
- if (!afterHeading.includes("</nextsteps>")) {
11019
- return { steps: [], texts: [], stripped: content, autoTexts: [] };
11020
- }
11021
- const texts = steps.map((s2) => s2.text);
11022
- const autoTexts = steps.filter((s2) => s2.auto).map((s2) => s2.text);
11023
- const blockStart = headingMatch.index;
11024
- const blockEnd = headingMatch[0].length + findBlockEnd(afterHeading, steps.length);
11025
- const stripped = (content.slice(0, blockStart) + content.slice(blockStart + blockEnd)).replace(/\n{3,}/g, "\n\n").trim();
11026
- return { steps, texts, stripped, autoTexts };
11027
- }
11028
- function findBlockEnd(afterHeading, stepCount) {
11029
- const closeIdx = afterHeading.indexOf("</nextsteps>");
11030
- if (closeIdx !== -1) {
11031
- let end = closeIdx + "</nextsteps>".length;
11032
- if (afterHeading[end] === "\n") end += 1;
11033
- return end;
11034
- }
11035
- const lines = afterHeading.split("\n");
11036
- let consumed = 0;
11037
- let found = 0;
11038
- for (const rawLine of lines) {
11039
- const line = rawLine.trim();
11040
- if (!line) break;
11041
- const m = ITEM_RE.exec(line);
11042
- if (!m) break;
11043
- consumed += rawLine.length + 1;
11044
- found++;
11045
- if (found >= stepCount) {
11046
- consumed -= 1;
11047
- break;
11048
- }
11049
- }
11050
- return consumed;
11051
- }
11052
- function stripNextStepsBlock(text) {
11053
- return text.replace(/<nextsteps\b[^>]*>[\s\S]*?<\/nextsteps>/gi, "").replace(/<nextsteps\b[^>]*\/?>/gi, "").replace(/\n{3,}/g, "\n\n").trim();
11054
- }
11055
10918
  function brainStatusStyle(status) {
11056
10919
  switch (status) {
11057
10920
  case "thinking":
@@ -18746,7 +18609,9 @@ function App({
18746
18609
  };
18747
18610
  }, [events]);
18748
18611
  const maxContext = activeMaxContext ?? agent.ctx.provider.capabilities.maxContext;
18749
- const currentContextTokens = (tokenCounter?.currentRequestTokens()?.input ?? 0) + (tokenCounter?.currentRequestTokens()?.cacheRead ?? 0);
18612
+ const currentRequestTokens = tokenCounter?.currentRequestTokens();
18613
+ const currentCacheWrite = currentRequestTokens?.cacheWrite ?? 0;
18614
+ const currentContextTokens = (currentRequestTokens?.input ?? 0) + (currentRequestTokens?.cacheRead ?? 0) + currentCacheWrite;
18750
18615
  const contextWindow = useMemo(() => {
18751
18616
  void state.contextChipVersion;
18752
18617
  return currentContextTokens > 0 && maxContext > 0 ? { used: currentContextTokens, max: maxContext } : void 0;
@@ -22911,6 +22776,7 @@ async function runTui(opts) {
22911
22776
  }
22912
22777
  }
22913
22778
  let cleaned = false;
22779
+ let inkInstance = null;
22914
22780
  const cleanup = () => {
22915
22781
  if (cleaned) return;
22916
22782
  cleaned = true;
@@ -22932,12 +22798,21 @@ async function runTui(opts) {
22932
22798
  const forceExitViaRapidCtrlC = () => {
22933
22799
  detachListeners();
22934
22800
  unregisterTuiClient();
22801
+ try {
22802
+ opts.agent.ctx.session.flushSync?.();
22803
+ } catch {
22804
+ }
22935
22805
  stdout.write(BRACKETED_PASTE_OFF);
22936
22806
  stdout.write(MOUSE_OFF);
22937
22807
  process.exit(130);
22938
22808
  };
22939
22809
  const signals = ["SIGTERM", "SIGHUP", "SIGINT"];
22940
- const signalHandler = () => cleanup();
22810
+ const signalHandler = () => {
22811
+ cleanup();
22812
+ inkInstance?.unmount();
22813
+ const sig = setTimeout(() => process.exit(143), 5e3);
22814
+ sig.unref();
22815
+ };
22941
22816
  const exitHandler = () => cleanup();
22942
22817
  const sigintHandler = () => {
22943
22818
  const now = Date.now();
@@ -22949,6 +22824,9 @@ async function runTui(opts) {
22949
22824
  return;
22950
22825
  }
22951
22826
  cleanup();
22827
+ inkInstance?.unmount();
22828
+ const sig = setTimeout(() => process.exit(130), 5e3);
22829
+ sig.unref();
22952
22830
  };
22953
22831
  process.on("SIGINT", sigintHandler);
22954
22832
  for (const s2 of ["SIGTERM", "SIGHUP"]) {
@@ -23090,7 +22968,7 @@ async function runTui(opts) {
23090
22968
  };
23091
22969
  const requestExit = (code) => {
23092
22970
  onExit(code);
23093
- instance?.unmount();
22971
+ inkInstance?.unmount();
23094
22972
  hardExitTimer = setTimeout(() => process.exit(code), 5e3);
23095
22973
  hardExitTimer.unref();
23096
22974
  };
@@ -23211,6 +23089,7 @@ async function runTui(opts) {
23211
23089
  }),
23212
23090
  { exitOnCtrlC: false, stdin: inkStdin }
23213
23091
  );
23092
+ inkInstance = instance;
23214
23093
  } catch (err) {
23215
23094
  writeErr(
23216
23095
  `wstack: TUI failed to start: ${err instanceof Error ? err.message : String(err)}
@@ -23422,6 +23301,6 @@ function eventToEntry(ev, pendingTools, completedTools) {
23422
23301
  }
23423
23302
  }
23424
23303
 
23425
- export { parseInline, parseNextSteps, replaySessionEvents, runTui };
23304
+ export { parseInline, replaySessionEvents, runTui };
23426
23305
  //# sourceMappingURL=index.js.map
23427
23306
  //# sourceMappingURL=index.js.map