@wrongstack/tui 0.270.0 → 0.272.1

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
@@ -268,7 +268,7 @@ interface RunTuiOptions {
268
268
  /** Snapshot of keyed providers + their model lists for the `/model` picker. Async — the catalog fetch may need to hit disk/network. */
269
269
  getPickableProviders?: (() => Promise<ProviderOption[]>) | undefined;
270
270
  /** Apply a (provider, model) pair after the picker confirms. Returns an error string on failure. */
271
- switchProviderAndModel?: ((providerId: string, modelId: string) => string | null) | undefined;
271
+ switchProviderAndModel?: ((providerId: string, modelId: string) => string | null | Promise<string | null>) | undefined;
272
272
  /** Apply an autonomy mode after the picker confirms. Returns an error string on failure. */
273
273
  switchAutonomy?: ((mode: 'off' | 'suggest' | 'auto' | 'eternal' | 'eternal-parallel') => string | null) | undefined;
274
274
  /**
package/dist/index.js CHANGED
@@ -7689,24 +7689,30 @@ function useDirectorFleetBridge({
7689
7689
  for (const [id, text] of streamBuf) {
7690
7690
  const cleaned = stripNextStepsBlock(text);
7691
7691
  if (!cleaned) continue;
7692
- const label = labelFor(labelsRef, id);
7693
7692
  enq({ type: "fleetMessage", id, text: cleaned });
7694
- if (streamFleetRef.current) {
7695
- enq({
7696
- type: "addEntry",
7697
- entry: {
7698
- kind: "subagent",
7699
- agentLabel: label.label,
7700
- agentColor: label.color,
7701
- icon: "\u{1F4AC}",
7702
- text: cleaned
7703
- }
7704
- });
7705
- }
7706
7693
  }
7707
7694
  streamBuf.clear();
7708
7695
  streamFlushTimer = null;
7709
7696
  };
7697
+ const historyBuf = /* @__PURE__ */ new Map();
7698
+ const finalizeHistory = (id) => {
7699
+ const text = historyBuf.get(id);
7700
+ historyBuf.delete(id);
7701
+ if (!text || !streamFleetRef.current) return;
7702
+ const cleaned = stripNextStepsBlock(text);
7703
+ if (!cleaned.trim()) return;
7704
+ const label = labelFor(labelsRef, id);
7705
+ enq({
7706
+ type: "addEntry",
7707
+ entry: {
7708
+ kind: "subagent",
7709
+ agentLabel: label.label,
7710
+ agentColor: label.color,
7711
+ icon: "\u{1F4AC}",
7712
+ text: cleaned
7713
+ }
7714
+ });
7715
+ };
7710
7716
  const status = d.status();
7711
7717
  for (const subagent of status.subagents) {
7712
7718
  const meta = d.getSubagentMeta(subagent.id);
@@ -7778,6 +7784,10 @@ function useDirectorFleetBridge({
7778
7784
  event.subagentId,
7779
7785
  (streamBuf.get(event.subagentId) ?? "") + payload.text
7780
7786
  );
7787
+ historyBuf.set(
7788
+ event.subagentId,
7789
+ (historyBuf.get(event.subagentId) ?? "") + payload.text
7790
+ );
7781
7791
  if (streamFlushTimer) clearTimeout(streamFlushTimer);
7782
7792
  streamFlushTimer = setTimeout(flushStreamBufs, FLUSH_MS * 4);
7783
7793
  }
@@ -7820,6 +7830,7 @@ function useDirectorFleetBridge({
7820
7830
  case "tool.started": {
7821
7831
  const payload = event.payload;
7822
7832
  if (payload?.name) {
7833
+ finalizeHistory(event.subagentId);
7823
7834
  enqueue({ type: "fleetToolStart", id: event.subagentId, name: payload.name });
7824
7835
  }
7825
7836
  break;
@@ -7861,6 +7872,7 @@ function useDirectorFleetBridge({
7861
7872
  });
7862
7873
  break;
7863
7874
  case "session.ended":
7875
+ finalizeHistory(event.subagentId);
7864
7876
  break;
7865
7877
  case "compaction.fired":
7866
7878
  enqueue({
@@ -7945,6 +7957,7 @@ function useDirectorFleetBridge({
7945
7957
  output: d.snapshot().total.output,
7946
7958
  perAgent: d.snapshot().perSubagent
7947
7959
  });
7960
+ finalizeHistory(payload.result.subagentId);
7948
7961
  if (streamFlushTimer) {
7949
7962
  clearTimeout(streamFlushTimer);
7950
7963
  flushStreamBufs();
@@ -7959,6 +7972,7 @@ function useDirectorFleetBridge({
7959
7972
  doFlush();
7960
7973
  if (streamFlushTimer) clearTimeout(streamFlushTimer);
7961
7974
  flushStreamBufs();
7975
+ for (const id of [...historyBuf.keys()]) finalizeHistory(id);
7962
7976
  if (batchTimer) clearTimeout(batchTimer);
7963
7977
  flushBatch();
7964
7978
  };
@@ -10350,6 +10364,7 @@ function App({
10350
10364
  enhanceEnabled = true,
10351
10365
  enhanceController,
10352
10366
  enhanceDelayMs = 15e3,
10367
+ getEnhancerReasoning,
10353
10368
  getYolo,
10354
10369
  getAutonomy,
10355
10370
  getEternalEngine,
@@ -12559,7 +12574,7 @@ function App({
12559
12574
  const providerId = state.modelPicker.pickedProviderId;
12560
12575
  const modelId = state.modelPicker.filteredOptions[state.modelPicker.selected];
12561
12576
  if (!providerId || !modelId) return;
12562
- const err = switchProviderAndModel?.(providerId, modelId);
12577
+ const err = await switchProviderAndModel?.(providerId, modelId);
12563
12578
  if (err) {
12564
12579
  dispatch({ type: "modelPickerHint", text: err });
12565
12580
  return;
@@ -14057,6 +14072,7 @@ ${content}
14057
14072
  enhanceAbortRef.current = ac;
14058
14073
  let result = null;
14059
14074
  let enhanceErr = null;
14075
+ const enhanceReasoning = getEnhancerReasoning?.();
14060
14076
  try {
14061
14077
  result = await enhanceUserPrompt({
14062
14078
  provider: agent.ctx.provider,
@@ -14068,7 +14084,8 @@ ${content}
14068
14084
  },
14069
14085
  // Feed recent conversation so follow-ups ("do the same", "that file")
14070
14086
  // resolve against context instead of being refined blind.
14071
- history: recentTextTurns(agent.ctx.messages)
14087
+ history: recentTextTurns(agent.ctx.messages),
14088
+ ...enhanceReasoning ? { reasoning: enhanceReasoning } : {}
14072
14089
  });
14073
14090
  } finally {
14074
14091
  enhanceAbortRef.current = null;
@@ -14831,10 +14848,37 @@ async function runTui(opts) {
14831
14848
  } catch {
14832
14849
  }
14833
14850
  };
14851
+ const RAPID_EXIT_WINDOW_MS = 2e3;
14852
+ const RAPID_EXIT_THRESHOLD = 3;
14853
+ let ctrlCPressTimestamps = [];
14854
+ const forceExitViaRapidCtrlC = () => {
14855
+ detachListeners();
14856
+ unregisterTuiClient();
14857
+ stdout.write(BRACKETED_PASTE_OFF);
14858
+ stdout.write(MOUSE_OFF);
14859
+ process.exit(130);
14860
+ };
14834
14861
  const signals = ["SIGTERM", "SIGHUP", "SIGINT"];
14835
14862
  const signalHandler = () => cleanup();
14836
14863
  const exitHandler = () => cleanup();
14837
- for (const s2 of signals) process.on(s2, signalHandler);
14864
+ const sigintHandler = () => {
14865
+ const now = Date.now();
14866
+ ctrlCPressTimestamps = ctrlCPressTimestamps.filter((t) => now - t < RAPID_EXIT_WINDOW_MS);
14867
+ ctrlCPressTimestamps.push(now);
14868
+ if (ctrlCPressTimestamps.length >= RAPID_EXIT_THRESHOLD) {
14869
+ ctrlCPressTimestamps = [];
14870
+ forceExitViaRapidCtrlC();
14871
+ return;
14872
+ }
14873
+ cleanup();
14874
+ };
14875
+ process.on("SIGINT", sigintHandler);
14876
+ for (const s2 of ["SIGTERM", "SIGHUP"]) {
14877
+ try {
14878
+ process.on(s2, signalHandler);
14879
+ } catch {
14880
+ }
14881
+ }
14838
14882
  process.on("exit", exitHandler);
14839
14883
  const detachListeners = () => {
14840
14884
  for (const s2 of signals) process.off(s2, signalHandler);