erosolar-cli 1.7.410 → 1.7.411

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 (205) hide show
  1. package/README.md +6 -6
  2. package/dist/StringUtils.d.ts +1 -4
  3. package/dist/StringUtils.d.ts.map +1 -1
  4. package/dist/StringUtils.js +2 -8
  5. package/dist/StringUtils.js.map +1 -1
  6. package/dist/browser/BrowserSessionManager.d.ts +1 -3
  7. package/dist/browser/BrowserSessionManager.d.ts.map +1 -1
  8. package/dist/browser/BrowserSessionManager.js +4 -24
  9. package/dist/browser/BrowserSessionManager.js.map +1 -1
  10. package/dist/capabilities/askUserCapability.d.ts.map +1 -1
  11. package/dist/capabilities/askUserCapability.js +64 -10
  12. package/dist/capabilities/askUserCapability.js.map +1 -1
  13. package/dist/capabilities/toolRegistry.d.ts +2 -0
  14. package/dist/capabilities/toolRegistry.d.ts.map +1 -1
  15. package/dist/capabilities/toolRegistry.js +40 -5
  16. package/dist/capabilities/toolRegistry.js.map +1 -1
  17. package/dist/contracts/agent-profiles.schema.json +5 -5
  18. package/dist/contracts/agent-schemas.json +6 -16
  19. package/dist/contracts/schemas/agent.schema.json +1 -5
  20. package/dist/contracts/schemas/tool-selection.schema.json +1 -7
  21. package/dist/contracts/tools.schema.json +80 -207
  22. package/dist/contracts/unified-schema.json +4 -5
  23. package/dist/contracts/v1/agent.d.ts +0 -3
  24. package/dist/contracts/v1/agent.d.ts.map +1 -1
  25. package/dist/contracts/v1/provider.d.ts +1 -2
  26. package/dist/contracts/v1/provider.d.ts.map +1 -1
  27. package/dist/contracts/v1/toolAccess.d.ts +1 -1
  28. package/dist/contracts/v1/toolAccess.d.ts.map +1 -1
  29. package/dist/core/agent.d.ts +1 -7
  30. package/dist/core/agent.d.ts.map +1 -1
  31. package/dist/core/agent.js +2 -131
  32. package/dist/core/agent.js.map +1 -1
  33. package/dist/core/alphaZeroEngine.d.ts +0 -8
  34. package/dist/core/alphaZeroEngine.d.ts.map +1 -1
  35. package/dist/core/alphaZeroEngine.js +35 -149
  36. package/dist/core/alphaZeroEngine.js.map +1 -1
  37. package/dist/core/alphaZeroOrchestrator.d.ts +0 -17
  38. package/dist/core/alphaZeroOrchestrator.d.ts.map +1 -1
  39. package/dist/core/alphaZeroOrchestrator.js +8 -95
  40. package/dist/core/alphaZeroOrchestrator.js.map +1 -1
  41. package/dist/core/claudeCodeFeatures.d.ts +2 -1
  42. package/dist/core/claudeCodeFeatures.d.ts.map +1 -1
  43. package/dist/core/claudeCodeFeatures.js +2 -1
  44. package/dist/core/claudeCodeFeatures.js.map +1 -1
  45. package/dist/core/cliTestHarness.d.ts +0 -5
  46. package/dist/core/cliTestHarness.d.ts.map +1 -1
  47. package/dist/core/cliTestHarness.js +3 -14
  48. package/dist/core/cliTestHarness.js.map +1 -1
  49. package/dist/core/contextManager.d.ts +0 -30
  50. package/dist/core/contextManager.d.ts.map +1 -1
  51. package/dist/core/contextManager.js +5 -87
  52. package/dist/core/contextManager.js.map +1 -1
  53. package/dist/core/contextWindow.d.ts +4 -4
  54. package/dist/core/contextWindow.js +9 -9
  55. package/dist/core/contextWindow.js.map +1 -1
  56. package/dist/core/modelDiscovery.js +3 -3
  57. package/dist/core/modelDiscovery.js.map +1 -1
  58. package/dist/core/preferences.d.ts +2 -3
  59. package/dist/core/preferences.d.ts.map +1 -1
  60. package/dist/core/preferences.js +11 -18
  61. package/dist/core/preferences.js.map +1 -1
  62. package/dist/core/secretStore.d.ts.map +1 -1
  63. package/dist/core/secretStore.js +31 -0
  64. package/dist/core/secretStore.js.map +1 -1
  65. package/dist/core/toolPreconditions.d.ts.map +1 -1
  66. package/dist/core/toolPreconditions.js +0 -60
  67. package/dist/core/toolPreconditions.js.map +1 -1
  68. package/dist/core/toolRuntime.d.ts.map +1 -1
  69. package/dist/core/toolRuntime.js +0 -17
  70. package/dist/core/toolRuntime.js.map +1 -1
  71. package/dist/core/types.d.ts +1 -1
  72. package/dist/core/types.d.ts.map +1 -1
  73. package/dist/headless/headlessApp.d.ts.map +1 -1
  74. package/dist/headless/headlessApp.js +6 -22
  75. package/dist/headless/headlessApp.js.map +1 -1
  76. package/dist/plugins/providers/google/index.js +3 -2
  77. package/dist/plugins/providers/google/index.js.map +1 -1
  78. package/dist/providers/openaiChatCompletionsProvider.d.ts.map +1 -1
  79. package/dist/providers/openaiChatCompletionsProvider.js +6 -60
  80. package/dist/providers/openaiChatCompletionsProvider.js.map +1 -1
  81. package/dist/runtime/agentController.d.ts.map +1 -1
  82. package/dist/runtime/agentController.js +6 -27
  83. package/dist/runtime/agentController.js.map +1 -1
  84. package/dist/shell/interactiveShell.d.ts +30 -79
  85. package/dist/shell/interactiveShell.d.ts.map +1 -1
  86. package/dist/shell/interactiveShell.js +726 -1511
  87. package/dist/shell/interactiveShell.js.map +1 -1
  88. package/dist/shell/shellApp.d.ts.map +1 -1
  89. package/dist/shell/shellApp.js +41 -15
  90. package/dist/shell/shellApp.js.map +1 -1
  91. package/dist/shell/systemPrompt.d.ts.map +1 -1
  92. package/dist/shell/systemPrompt.js +0 -1
  93. package/dist/shell/systemPrompt.js.map +1 -1
  94. package/dist/shell/terminalInput.d.ts +21 -85
  95. package/dist/shell/terminalInput.d.ts.map +1 -1
  96. package/dist/shell/terminalInput.js +60 -517
  97. package/dist/shell/terminalInput.js.map +1 -1
  98. package/dist/shell/terminalInputAdapter.d.ts +16 -37
  99. package/dist/shell/terminalInputAdapter.d.ts.map +1 -1
  100. package/dist/shell/terminalInputAdapter.js +22 -44
  101. package/dist/shell/terminalInputAdapter.js.map +1 -1
  102. package/dist/shell/updateManager.d.ts.map +1 -1
  103. package/dist/shell/updateManager.js +17 -1
  104. package/dist/shell/updateManager.js.map +1 -1
  105. package/dist/subagents/parallelAgentManager.d.ts.map +1 -1
  106. package/dist/subagents/parallelAgentManager.js +2 -1
  107. package/dist/subagents/parallelAgentManager.js.map +1 -1
  108. package/dist/tools/buildTools.d.ts.map +1 -1
  109. package/dist/tools/buildTools.js +76 -19
  110. package/dist/tools/buildTools.js.map +1 -1
  111. package/dist/tools/editTools.js +1 -1
  112. package/dist/tools/editTools.js.map +1 -1
  113. package/dist/tools/enhancedCodeIntelligenceTools.js +2 -1
  114. package/dist/tools/enhancedCodeIntelligenceTools.js.map +1 -1
  115. package/dist/tools/fileTools.js +0 -3
  116. package/dist/tools/fileTools.js.map +1 -1
  117. package/dist/tools/frontendTestingTools.js +1 -1
  118. package/dist/tools/frontendTestingTools.js.map +1 -1
  119. package/dist/tools/interactionTools.d.ts.map +1 -1
  120. package/dist/tools/interactionTools.js +82 -15
  121. package/dist/tools/interactionTools.js.map +1 -1
  122. package/dist/tools/learnTools.d.ts +0 -2
  123. package/dist/tools/learnTools.d.ts.map +1 -1
  124. package/dist/tools/learnTools.js +81 -29
  125. package/dist/tools/learnTools.js.map +1 -1
  126. package/dist/tools/localExplore.d.ts.map +1 -1
  127. package/dist/tools/localExplore.js +1 -0
  128. package/dist/tools/localExplore.js.map +1 -1
  129. package/dist/tools/notebookEditTools.js.map +1 -1
  130. package/dist/tools/repoChecksTools.js +3 -4
  131. package/dist/tools/repoChecksTools.js.map +1 -1
  132. package/dist/tools/searchTools.js +0 -4
  133. package/dist/tools/searchTools.js.map +1 -1
  134. package/dist/tools/softwareEngineeringTools.d.ts.map +1 -1
  135. package/dist/tools/softwareEngineeringTools.js +0 -1
  136. package/dist/tools/softwareEngineeringTools.js.map +1 -1
  137. package/dist/tools/webTools.d.ts.map +1 -1
  138. package/dist/tools/webTools.js.map +1 -1
  139. package/dist/ui/ShellUIAdapter.d.ts +17 -54
  140. package/dist/ui/ShellUIAdapter.d.ts.map +1 -1
  141. package/dist/ui/ShellUIAdapter.js +90 -378
  142. package/dist/ui/ShellUIAdapter.js.map +1 -1
  143. package/dist/ui/display.d.ts +38 -19
  144. package/dist/ui/display.d.ts.map +1 -1
  145. package/dist/ui/display.js +311 -96
  146. package/dist/ui/display.js.map +1 -1
  147. package/dist/ui/orchestration/UIUpdateCoordinator.d.ts.map +1 -1
  148. package/dist/ui/orchestration/UIUpdateCoordinator.js +3 -5
  149. package/dist/ui/orchestration/UIUpdateCoordinator.js.map +1 -1
  150. package/dist/ui/shortcutsHelp.d.ts +11 -1
  151. package/dist/ui/shortcutsHelp.d.ts.map +1 -1
  152. package/dist/ui/shortcutsHelp.js +54 -8
  153. package/dist/ui/shortcutsHelp.js.map +1 -1
  154. package/dist/ui/streamingFormatter.d.ts +16 -0
  155. package/dist/ui/streamingFormatter.d.ts.map +1 -0
  156. package/dist/ui/streamingFormatter.js +62 -0
  157. package/dist/ui/streamingFormatter.js.map +1 -0
  158. package/dist/ui/theme.d.ts +100 -100
  159. package/dist/ui/theme.d.ts.map +1 -1
  160. package/dist/ui/theme.js.map +1 -1
  161. package/dist/ui/toolDisplay.d.ts +3 -3
  162. package/dist/ui/toolDisplay.d.ts.map +1 -1
  163. package/dist/ui/toolDisplay.js +8 -7
  164. package/dist/ui/toolDisplay.js.map +1 -1
  165. package/dist/ui/unified/index.d.ts +3 -2
  166. package/dist/ui/unified/index.d.ts.map +1 -1
  167. package/dist/ui/unified/index.js +1 -0
  168. package/dist/ui/unified/index.js.map +1 -1
  169. package/dist/ui/unified/layout.d.ts +23 -0
  170. package/dist/ui/unified/layout.d.ts.map +1 -1
  171. package/dist/ui/unified/layout.js +113 -11
  172. package/dist/ui/unified/layout.js.map +1 -1
  173. package/package.json +24 -37
  174. package/dist/core/alphaZeroConfig.d.ts +0 -11
  175. package/dist/core/alphaZeroConfig.d.ts.map +0 -1
  176. package/dist/core/alphaZeroConfig.js +0 -59
  177. package/dist/core/alphaZeroConfig.js.map +0 -1
  178. package/dist/core/alphaZeroEnhanced.d.ts +0 -125
  179. package/dist/core/alphaZeroEnhanced.d.ts.map +0 -1
  180. package/dist/core/alphaZeroEnhanced.js +0 -386
  181. package/dist/core/alphaZeroEnhanced.js.map +0 -1
  182. package/dist/core/autonomousVerification.d.ts +0 -103
  183. package/dist/core/autonomousVerification.d.ts.map +0 -1
  184. package/dist/core/autonomousVerification.js +0 -583
  185. package/dist/core/autonomousVerification.js.map +0 -1
  186. package/dist/core/offsecAlphaZeroEnhanced.d.ts +0 -98
  187. package/dist/core/offsecAlphaZeroEnhanced.d.ts.map +0 -1
  188. package/dist/core/offsecAlphaZeroEnhanced.js +0 -441
  189. package/dist/core/offsecAlphaZeroEnhanced.js.map +0 -1
  190. package/dist/core/parallelAgentOrchestrator.d.ts +0 -171
  191. package/dist/core/parallelAgentOrchestrator.d.ts.map +0 -1
  192. package/dist/core/parallelAgentOrchestrator.js +0 -459
  193. package/dist/core/parallelAgentOrchestrator.js.map +0 -1
  194. package/dist/index.d.ts +0 -5
  195. package/dist/index.d.ts.map +0 -1
  196. package/dist/index.js +0 -3
  197. package/dist/index.js.map +0 -1
  198. package/dist/tools/detectCommands.d.ts +0 -8
  199. package/dist/tools/detectCommands.d.ts.map +0 -1
  200. package/dist/tools/detectCommands.js +0 -183
  201. package/dist/tools/detectCommands.js.map +0 -1
  202. package/dist/ui/assistantBlockRenderer.d.ts +0 -30
  203. package/dist/ui/assistantBlockRenderer.d.ts.map +0 -1
  204. package/dist/ui/assistantBlockRenderer.js +0 -121
  205. package/dist/ui/assistantBlockRenderer.js.map +0 -1
@@ -17,7 +17,7 @@
17
17
  import { EventEmitter } from 'node:events';
18
18
  import { isMultilinePaste } from '../core/multilinePasteHandler.js';
19
19
  import { writeLock } from '../ui/writeLock.js';
20
- import { renderDivider, renderSessionFrame, renderStatusLine, renderStatusLines, } from '../ui/unified/layout.js';
20
+ import { renderDivider, renderStatusLine, renderStatusLines } from '../ui/unified/layout.js';
21
21
  import { isStreamingMode } from '../ui/globalWriteLock.js';
22
22
  import { formatThinking } from '../ui/toolDisplay.js';
23
23
  import { theme } from '../ui/theme.js';
@@ -116,20 +116,16 @@ export class TerminalInput extends EventEmitter {
116
116
  // Alternate screen state
117
117
  alternateScreenActive = false;
118
118
  editMode = 'display-edits';
119
- verificationEnabled = true;
119
+ verificationEnabled = false;
120
120
  autoContinueEnabled = false;
121
121
  verificationHotkey = 'ctrl+shift+v';
122
122
  autoContinueHotkey = 'ctrl+shift+c';
123
123
  thinkingHotkey = 'ctrl+shift+t';
124
- alphaZeroModeEnabled = false;
125
- alphaZeroHotkey = 'ctrl+shift+a';
126
- alphaZeroLabel = null;
127
124
  modelLabel = null;
128
125
  providerLabel = null;
129
- featureStatus = null;
130
126
  // Streaming render throttle
131
127
  lastStreamingRender = 0;
132
- streamingRenderInterval = 150; // ms between renders during streaming
128
+ streamingRenderInterval = 250; // ms between renders during streaming
133
129
  streamingRenderTimer = null;
134
130
  // Command autocomplete state
135
131
  commandSuggestions = [];
@@ -141,14 +137,6 @@ export class TerminalInput extends EventEmitter {
141
137
  lastChatBoxStartRow = null;
142
138
  lastChatBoxHeight = 0;
143
139
  displayInterceptorDispose = null;
144
- recentActions = [];
145
- maxRecentActions = 5;
146
- inlineCommandPanelContent = null;
147
- inlineCommandPanelLines = [];
148
- inlineCommandPanelWidth = 0;
149
- maxInlineCommandPanelLines = 30;
150
- sessionFrameProps = null;
151
- inlineCommandPanelIndent = ' ';
152
140
  constructor(writeStream = process.stdout, config = {}) {
153
141
  super();
154
142
  this.out = writeStream;
@@ -305,13 +293,6 @@ export class TerminalInput extends EventEmitter {
305
293
  const normalizedName = key?.name ?? this.getArrowKeyName(key?.sequence ?? str);
306
294
  const effectiveKey = normalizedName ? { ...key, name: normalizedName } : key;
307
295
  const safeStr = this.isArrowEscapeFragment(str) ? undefined : str;
308
- // Some terminals emit raw DEL/backspace bytes without a parsed key name.
309
- // Treat these as backspace to avoid inserting the control character.
310
- const isRawBackspace = !effectiveKey?.name && safeStr && (safeStr === '\b' || safeStr === '\x7f');
311
- if (isRawBackspace) {
312
- this.deleteBackward();
313
- return;
314
- }
315
296
  let handled = false;
316
297
  // Handle control keys
317
298
  if (effectiveKey?.ctrl) {
@@ -359,6 +340,13 @@ export class TerminalInput extends EventEmitter {
359
340
  this.forceRender();
360
341
  }
361
342
  }
343
+ /**
344
+ * Legacy method - no longer used (content flows naturally).
345
+ * @deprecated Use setContentRow instead
346
+ */
347
+ setPinnedHeaderLines(_count) {
348
+ // No-op: scroll region pinning removed
349
+ }
362
350
  /**
363
351
  * Get current mode
364
352
  */
@@ -697,19 +685,13 @@ export class TerminalInput extends EventEmitter {
697
685
  const nextVerifyHotkey = options.verificationHotkey ?? this.verificationHotkey;
698
686
  const nextAutoHotkey = options.autoContinueHotkey ?? this.autoContinueHotkey;
699
687
  const nextThinkingHotkey = options.thinkingHotkey ?? this.thinkingHotkey;
700
- const nextAlphaHotkey = options.alphaZeroHotkey ?? this.alphaZeroHotkey;
701
688
  const nextThinkingLabel = options.thinkingModeLabel === undefined ? this.thinkingModeLabel : (options.thinkingModeLabel || null);
702
- const nextAlphaZeroEnabled = options.alphaZeroEnabled === undefined ? this.alphaZeroModeEnabled : !!options.alphaZeroEnabled;
703
- const nextAlphaZeroLabel = options.alphaZeroLabel === undefined ? this.alphaZeroLabel : (options.alphaZeroLabel || null);
704
689
  if (this.verificationEnabled === nextVerification &&
705
690
  this.autoContinueEnabled === nextAutoContinue &&
706
691
  this.verificationHotkey === nextVerifyHotkey &&
707
692
  this.autoContinueHotkey === nextAutoHotkey &&
708
693
  this.thinkingHotkey === nextThinkingHotkey &&
709
- this.thinkingModeLabel === nextThinkingLabel &&
710
- this.alphaZeroModeEnabled === nextAlphaZeroEnabled &&
711
- this.alphaZeroHotkey === nextAlphaHotkey &&
712
- this.alphaZeroLabel === nextAlphaZeroLabel) {
694
+ this.thinkingModeLabel === nextThinkingLabel) {
713
695
  return;
714
696
  }
715
697
  this.verificationEnabled = nextVerification;
@@ -718,9 +700,6 @@ export class TerminalInput extends EventEmitter {
718
700
  this.autoContinueHotkey = nextAutoHotkey;
719
701
  this.thinkingHotkey = nextThinkingHotkey;
720
702
  this.thinkingModeLabel = nextThinkingLabel;
721
- this.alphaZeroModeEnabled = nextAlphaZeroEnabled;
722
- this.alphaZeroHotkey = nextAlphaHotkey;
723
- this.alphaZeroLabel = nextAlphaZeroLabel;
724
703
  this.scheduleRender();
725
704
  }
726
705
  /**
@@ -745,97 +724,6 @@ export class TerminalInput extends EventEmitter {
745
724
  this.providerLabel = nextProvider;
746
725
  this.scheduleRender();
747
726
  }
748
- /**
749
- * Surface feature flags/status near the controls area.
750
- */
751
- setFeatureStatus(status) {
752
- const normalized = this.normalizeFeatureStatus(status);
753
- if (this.isSameFeatureStatus(this.featureStatus, normalized)) {
754
- return;
755
- }
756
- this.featureStatus = normalized;
757
- this.scheduleRender();
758
- }
759
- /**
760
- * Keep the session frame (banner/context block) pinned under the chat box.
761
- * Width is resolved at render time to match the terminal.
762
- */
763
- setSessionFrame(props) {
764
- const normalized = props ? { ...props } : null;
765
- if (normalized) {
766
- normalized.width = undefined; // Always recompute width at render time
767
- }
768
- if (this.isSameSessionFrameProps(this.sessionFrameProps, normalized)) {
769
- return;
770
- }
771
- this.sessionFrameProps = normalized;
772
- this.scheduleRender();
773
- }
774
- /**
775
- * Surface inline command panel content just above the input prompt.
776
- */
777
- setInlineCommandPanel(content) {
778
- const normalized = this.normalizeInlineCommandContent(content);
779
- if (normalized === this.inlineCommandPanelContent) {
780
- return;
781
- }
782
- this.inlineCommandPanelContent = normalized;
783
- this.inlineCommandPanelLines = [];
784
- this.inlineCommandPanelWidth = 0;
785
- this.scheduleRender();
786
- }
787
- /**
788
- * Clear the inline command panel.
789
- */
790
- clearInlineCommandPanel() {
791
- if (this.inlineCommandPanelContent === null) {
792
- return;
793
- }
794
- this.inlineCommandPanelContent = null;
795
- this.inlineCommandPanelLines = [];
796
- this.inlineCommandPanelWidth = 0;
797
- this.scheduleRender();
798
- }
799
- normalizeFeatureStatus(status) {
800
- if (!status) {
801
- return null;
802
- }
803
- const normalizeCount = (value) => typeof value === 'number' && Number.isFinite(value) ? Math.max(0, Math.round(value)) : undefined;
804
- return {
805
- pluginCount: normalizeCount(status.pluginCount),
806
- toolCount: normalizeCount(status.toolCount),
807
- sessionId: status.sessionId?.trim() || null,
808
- mcpEnabled: status.mcpEnabled ?? undefined,
809
- metricsEnabled: status.metricsEnabled ?? undefined,
810
- autoCompact: status.autoCompact ?? undefined,
811
- dualMode: status.dualMode ?? undefined,
812
- };
813
- }
814
- isSameFeatureStatus(a, b) {
815
- if (!a && !b)
816
- return true;
817
- if (!a || !b)
818
- return false;
819
- return (a.pluginCount === b.pluginCount &&
820
- a.toolCount === b.toolCount &&
821
- a.sessionId === b.sessionId &&
822
- a.mcpEnabled === b.mcpEnabled &&
823
- a.metricsEnabled === b.metricsEnabled &&
824
- a.autoCompact === b.autoCompact &&
825
- a.dualMode === b.dualMode);
826
- }
827
- isSameSessionFrameProps(a, b) {
828
- if (!a && !b)
829
- return true;
830
- if (!a || !b)
831
- return false;
832
- return (a.profileLabel === b.profileLabel &&
833
- a.profileName === b.profileName &&
834
- a.model === b.model &&
835
- a.provider === b.provider &&
836
- a.workspace === b.workspace &&
837
- a.version === b.version);
838
- }
839
727
  /**
840
728
  * Render the floating input area at contentRow.
841
729
  *
@@ -849,10 +737,6 @@ export class TerminalInput extends EventEmitter {
849
737
  if (this.isRendering)
850
738
  return;
851
739
  const streamingActive = this.mode === 'streaming' || isStreamingMode();
852
- if (this.scrollRegionActive && streamingActive) {
853
- this.renderStreamingFrame();
854
- return;
855
- }
856
740
  const bufferChanged = this.buffer !== this.lastRenderContent || this.cursor !== this.lastRenderCursor;
857
741
  // During streaming, throttle re-renders unless the buffer actually changed
858
742
  // (e.g., typing slash commands while streaming should update immediately).
@@ -897,8 +781,6 @@ export class TerminalInput extends EventEmitter {
897
781
  const maxVisible = Math.max(1, Math.min(this.config.maxLines, rows - 3));
898
782
  const displayLines = Math.min(lines.length, maxVisible);
899
783
  const metaLines = this.buildMetaLines(cols - 2);
900
- const inlinePanelLines = this.getInlineCommandPanelLines(cols - 2);
901
- const sessionFrameLines = this.getSessionFrameLines(cols - 2);
902
784
  // Calculate display window (keep cursor visible)
903
785
  let startLine = 0;
904
786
  if (lines.length > displayLines) {
@@ -948,30 +830,16 @@ export class TerminalInput extends EventEmitter {
948
830
  this.write(metaLine);
949
831
  currentRow += 1;
950
832
  }
951
- // Recent actions strip (sits above the divider)
952
- const recentLines = this.buildRecentActionLines(cols - 2);
953
- for (const recentLine of recentLines) {
954
- this.write(ESC.TO(currentRow, 1));
955
- this.write(recentLine);
956
- currentRow += 1;
957
- }
958
833
  // Separator line with scroll status
959
834
  this.write(ESC.TO(currentRow, 1));
960
835
  const dividerLabel = scrollIndicator || undefined;
961
836
  this.write(renderDivider(cols - 2, dividerLabel));
962
837
  currentRow += 1;
963
- // Inline command panel sits above the input prompt
964
- for (const panelLine of inlinePanelLines) {
965
- this.write(ESC.TO(currentRow, 1));
966
- this.write(panelLine);
967
- currentRow += 1;
968
- }
969
838
  // Render input lines
970
- const inputStartRow = currentRow;
971
- let finalRow = inputStartRow;
839
+ let finalRow = currentRow;
972
840
  let finalCol = 3;
973
841
  for (let i = 0; i < visibleLines.length; i++) {
974
- const rowNum = inputStartRow + i;
842
+ const rowNum = currentRow + i;
975
843
  this.write(ESC.TO(rowNum, 1));
976
844
  const line = visibleLines[i] ?? '';
977
845
  const isFirstLine = (startLine + i) === 0;
@@ -1008,7 +876,7 @@ export class TerminalInput extends EventEmitter {
1008
876
  }
1009
877
  // Command suggestions (shown above controls when "/" is typed)
1010
878
  const suggestionLines = this.buildSuggestionLines(cols - 2);
1011
- let suggestionRow = inputStartRow + visibleLines.length;
879
+ let suggestionRow = currentRow + visibleLines.length;
1012
880
  if (suggestionLines.length > 0) {
1013
881
  this.lastSuggestionStartRow = suggestionRow;
1014
882
  }
@@ -1027,12 +895,6 @@ export class TerminalInput extends EventEmitter {
1027
895
  this.write(controlLine);
1028
896
  controlRow += 1;
1029
897
  }
1030
- // Session frame pinned beneath the chat box (always visible)
1031
- for (const frameLine of sessionFrameLines) {
1032
- this.write(ESC.TO(controlRow, 1));
1033
- this.write(frameLine);
1034
- controlRow += 1;
1035
- }
1036
898
  // Restore scroll region and cursor
1037
899
  if (this.scrollRegionActive) {
1038
900
  // Restore scroll region
@@ -1139,18 +1001,6 @@ export class TerminalInput extends EventEmitter {
1139
1001
  }
1140
1002
  return line.slice(0, lastCr);
1141
1003
  }
1142
- /**
1143
- * Build the session frame (banner) lines sized for the current terminal width.
1144
- * Returns an empty array when no frame props have been provided.
1145
- */
1146
- getSessionFrameLines(width) {
1147
- if (!this.sessionFrameProps) {
1148
- return [];
1149
- }
1150
- const effectiveWidth = Math.max(8, width);
1151
- const frame = renderSessionFrame({ ...this.sessionFrameProps, width: effectiveWidth }).trimEnd();
1152
- return frame ? frame.split('\n') : [];
1153
- }
1154
1004
  /**
1155
1005
  * Build compact meta line above the divider.
1156
1006
  * Shows model/provider and key metrics in a single line.
@@ -1199,46 +1049,6 @@ export class TerminalInput extends EventEmitter {
1199
1049
  const spacing = Math.max(1, width - this.visibleLength(leftText) - this.visibleLength(rightText));
1200
1050
  return [`${leftText}${' '.repeat(spacing)}${rightText}`];
1201
1051
  }
1202
- buildFeatureStatusParts() {
1203
- const status = this.featureStatus;
1204
- if (!status) {
1205
- return [];
1206
- }
1207
- const parts = [];
1208
- if (status.toolCount !== undefined) {
1209
- const count = status.toolCount;
1210
- const label = count === 1 ? 'tool' : 'tools';
1211
- parts.push({ text: `⚒ ${count} ${label}`, tone: count > 0 ? 'info' : 'muted' });
1212
- }
1213
- if (status.pluginCount !== undefined) {
1214
- const count = status.pluginCount;
1215
- const label = count === 1 ? 'plugin' : 'plugins';
1216
- parts.push({ text: `🔌 ${count} ${label}`, tone: count > 0 ? 'info' : 'muted' });
1217
- }
1218
- if (status.mcpEnabled !== undefined) {
1219
- parts.push({ text: status.mcpEnabled ? 'MCP on' : 'MCP off', tone: status.mcpEnabled ? 'success' : 'muted' });
1220
- }
1221
- if (status.metricsEnabled !== undefined) {
1222
- parts.push({
1223
- text: status.metricsEnabled ? 'Metrics on' : 'Metrics off',
1224
- tone: status.metricsEnabled ? 'success' : 'muted',
1225
- });
1226
- }
1227
- if (status.autoCompact !== undefined) {
1228
- parts.push({
1229
- text: status.autoCompact ? 'Auto-compact on' : 'Auto-compact off',
1230
- tone: status.autoCompact ? 'info' : 'muted',
1231
- });
1232
- }
1233
- if (status.dualMode !== undefined) {
1234
- parts.push({ text: status.dualMode ? 'Dual mode' : 'Solo mode', tone: status.dualMode ? 'success' : 'muted' });
1235
- }
1236
- if (status.sessionId) {
1237
- const shortId = status.sessionId.slice(0, 8);
1238
- parts.push({ text: `#${shortId}`, tone: 'muted' });
1239
- }
1240
- return parts;
1241
- }
1242
1052
  /**
1243
1053
  * Build mode controls lines with all keyboard shortcuts.
1244
1054
  * Shows status, all toggles, and contextual information.
@@ -1268,9 +1078,6 @@ export class TerminalInput extends EventEmitter {
1268
1078
  statusParts.push({ text: this.statusMessage, tone: this.streamingLabel ? 'muted' : 'info' });
1269
1079
  }
1270
1080
  // Interrupt shortcut (during streaming)
1271
- if (streamingActive) {
1272
- statusParts.push({ text: `Escape:stop`, tone: 'warn' });
1273
- }
1274
1081
  // Queued commands
1275
1082
  if (this.queue.length > 0) {
1276
1083
  const queueIcon = streamingActive ? '⏳' : '▸';
@@ -1300,14 +1107,6 @@ export class TerminalInput extends EventEmitter {
1300
1107
  text: `${autoHotkey} ${autoStatus}`,
1301
1108
  tone: this.autoContinueEnabled ? 'info' : 'muted'
1302
1109
  });
1303
- // AlphaZero RL mode toggle
1304
- const alphaHotkey = this.formatHotkey(this.alphaZeroHotkey);
1305
- const alphaLabel = this.alphaZeroLabel || 'AlphaZero RL';
1306
- const alphaStatus = this.alphaZeroModeEnabled ? `${alphaLabel} on` : `${alphaLabel} off`;
1307
- toggleParts.push({
1308
- text: `${alphaHotkey} ${alphaStatus}`,
1309
- tone: this.alphaZeroModeEnabled ? 'success' : 'muted',
1310
- });
1311
1110
  // Thinking mode toggle
1312
1111
  if (this.thinkingModeLabel) {
1313
1112
  const shortThinking = this.thinkingModeLabel.length > 10
@@ -1318,7 +1117,6 @@ export class TerminalInput extends EventEmitter {
1318
1117
  }
1319
1118
  // Navigation shortcuts - always show these
1320
1119
  toggleParts.push({ text: 'PgUp/Dn:scroll', tone: 'muted' });
1321
- toggleParts.push({ text: 'ESC:stop', tone: 'muted' });
1322
1120
  toggleParts.push({ text: '/help', tone: 'muted' });
1323
1121
  // Multi-line indicator
1324
1122
  if (this.buffer.includes('\n')) {
@@ -1333,7 +1131,6 @@ export class TerminalInput extends EventEmitter {
1333
1131
  tone: 'info',
1334
1132
  });
1335
1133
  }
1336
- const featureParts = this.buildFeatureStatusParts();
1337
1134
  // Build the output lines - use wrapping to never truncate
1338
1135
  const lines = [];
1339
1136
  // During streaming: show status line first, then toggles (wrapped as needed)
@@ -1353,9 +1150,6 @@ export class TerminalInput extends EventEmitter {
1353
1150
  // No streaming, no status - just show toggles (wrapped as needed)
1354
1151
  lines.push(...renderStatusLines(toggleParts, width));
1355
1152
  }
1356
- if (featureParts.length > 0) {
1357
- lines.push(...renderStatusLines(featureParts, width));
1358
- }
1359
1153
  // Ensure at least one line is returned
1360
1154
  if (lines.length === 0) {
1361
1155
  lines.push('');
@@ -1438,149 +1232,6 @@ export class TerminalInput extends EventEmitter {
1438
1232
  const ansiPattern = /\u001B\[[0-?]*[ -/]*[@-~]/g;
1439
1233
  return value.replace(ansiPattern, '').length;
1440
1234
  }
1441
- /**
1442
- * Record a compact recent action for display above the divider.
1443
- */
1444
- recordRecentAction(action) {
1445
- const clean = this.sanitize(action).replace(/\s+/g, ' ').trim();
1446
- if (!clean) {
1447
- return;
1448
- }
1449
- const maxLen = 80;
1450
- const text = clean.length > maxLen ? `${clean.slice(0, maxLen - 1)}…` : clean;
1451
- const last = this.recentActions[this.recentActions.length - 1];
1452
- if (last && last.text === text) {
1453
- last.timestamp = Date.now();
1454
- return;
1455
- }
1456
- if (!this.isTTY()) {
1457
- this.write(`[action] ${text}\n`);
1458
- }
1459
- this.recentActions.push({ text, timestamp: Date.now() });
1460
- while (this.recentActions.length > this.maxRecentActions) {
1461
- this.recentActions.shift();
1462
- }
1463
- this.scheduleRender();
1464
- }
1465
- /**
1466
- * Build the single-line recent actions strip.
1467
- */
1468
- buildRecentActionLines(width) {
1469
- if (this.recentActions.length === 0 || width <= 0) {
1470
- return [];
1471
- }
1472
- const items = this.recentActions.map((entry) => ({ text: entry.text, tone: 'muted' }));
1473
- const parts = [{ text: 'recent', tone: 'info' }, ...items];
1474
- return [renderStatusLine(parts, width)];
1475
- }
1476
- /**
1477
- * Build wrapped inline command panel lines with indentation and a max line cap.
1478
- */
1479
- getInlineCommandPanelLines(width) {
1480
- const content = this.inlineCommandPanelContent;
1481
- if (!content || width <= 0) {
1482
- this.inlineCommandPanelLines = [];
1483
- return [];
1484
- }
1485
- const availableWidth = Math.max(1, width - this.inlineCommandPanelIndent.length);
1486
- if (this.inlineCommandPanelLines.length > 0 && this.inlineCommandPanelWidth === availableWidth) {
1487
- return this.inlineCommandPanelLines;
1488
- }
1489
- const wrapped = [];
1490
- const rawLines = content.split('\n');
1491
- for (const rawLine of rawLines) {
1492
- const normalizedLine = this.normalizeCarriageReturnLine(rawLine ?? '');
1493
- const visibleLine = normalizedLine.length === 0 ? '' : normalizedLine;
1494
- const segments = this.wrapInlineCommandLine(visibleLine, availableWidth);
1495
- if (segments.length === 0) {
1496
- wrapped.push(this.inlineCommandPanelIndent);
1497
- continue;
1498
- }
1499
- for (const segment of segments) {
1500
- wrapped.push(`${this.inlineCommandPanelIndent}${segment}`);
1501
- }
1502
- }
1503
- this.inlineCommandPanelWidth = availableWidth;
1504
- this.inlineCommandPanelLines =
1505
- wrapped.length > this.maxInlineCommandPanelLines
1506
- ? wrapped.slice(wrapped.length - this.maxInlineCommandPanelLines)
1507
- : wrapped;
1508
- return this.inlineCommandPanelLines;
1509
- }
1510
- /**
1511
- * Wrap text that may contain ANSI sequences without breaking the codes.
1512
- */
1513
- wrapInlineCommandLine(line, width) {
1514
- if (width <= 0) {
1515
- return [line];
1516
- }
1517
- if (!line) {
1518
- return [''];
1519
- }
1520
- const tokens = this.tokenizeAnsi(line);
1521
- const lines = [];
1522
- let current = '';
1523
- let currentLen = 0;
1524
- let lastBreakIndex = -1;
1525
- let lastBreakLen = 0;
1526
- const flushLine = () => {
1527
- const trimmed = current.trimEnd();
1528
- lines.push(trimmed);
1529
- current = '';
1530
- currentLen = 0;
1531
- lastBreakIndex = -1;
1532
- lastBreakLen = 0;
1533
- };
1534
- for (const token of tokens) {
1535
- const isAnsi = token.startsWith('\x1b[');
1536
- if (isAnsi) {
1537
- current += token;
1538
- continue;
1539
- }
1540
- current += token;
1541
- currentLen += 1;
1542
- if (token === ' ' || token === '\t') {
1543
- lastBreakIndex = current.length;
1544
- lastBreakLen = currentLen;
1545
- }
1546
- if (currentLen > width) {
1547
- if (lastBreakIndex > -1 && lastBreakLen > 0) {
1548
- const lineBeforeBreak = current.slice(0, lastBreakIndex).trimEnd();
1549
- lines.push(lineBeforeBreak);
1550
- current = current.slice(lastBreakIndex).trimStart();
1551
- currentLen = this.visibleLength(current);
1552
- }
1553
- else {
1554
- flushLine();
1555
- }
1556
- }
1557
- }
1558
- if (current.length > 0 || lines.length === 0) {
1559
- const finalLine = current.trimEnd();
1560
- lines.push(finalLine);
1561
- }
1562
- return lines;
1563
- }
1564
- tokenizeAnsi(value) {
1565
- if (!value) {
1566
- return [];
1567
- }
1568
- const pattern = /\u001B\[[0-?]*[ -/]*[@-~]/g;
1569
- const tokens = [];
1570
- let lastIndex = 0;
1571
- let match;
1572
- while ((match = pattern.exec(value)) !== null) {
1573
- if (match.index > lastIndex) {
1574
- tokens.push(...value.slice(lastIndex, match.index).split(''));
1575
- }
1576
- tokens.push(match[0]);
1577
- lastIndex = match.index + match[0].length;
1578
- }
1579
- if (lastIndex < value.length) {
1580
- tokens.push(...value.slice(lastIndex).split(''));
1581
- }
1582
- return tokens;
1583
- }
1584
1235
  /**
1585
1236
  * Debug-only snapshot used by tests to assert rendered strings without
1586
1237
  * needing a TTY. Not used by production code.
@@ -1613,8 +1264,6 @@ export class TerminalInput extends EventEmitter {
1613
1264
  handleResize() {
1614
1265
  this.lastRenderContent = '';
1615
1266
  this.lastRenderCursor = -1;
1616
- this.inlineCommandPanelWidth = 0;
1617
- this.inlineCommandPanelLines = [];
1618
1267
  this.resetStreamingRenderThrottle();
1619
1268
  // If in scrollback mode, re-render the scrollback view with new dimensions
1620
1269
  if (this.isInScrollbackMode) {
@@ -1629,15 +1278,6 @@ export class TerminalInput extends EventEmitter {
1629
1278
  * Sets up terminal scroll region to exclude chat box.
1630
1279
  */
1631
1280
  enterStreamingScrollRegion() {
1632
- if (!this.isTTY()) {
1633
- this.scrollRegionActive = false;
1634
- this.setStatusMessage('esc to interrupt');
1635
- this.lastChatBoxStartRow = null;
1636
- this.lastChatBoxHeight = 0;
1637
- this.lastSuggestionStartRow = null;
1638
- this.forceRender();
1639
- return;
1640
- }
1641
1281
  const { rows } = this.getSize();
1642
1282
  const chatBoxHeight = this.getChatBoxHeight();
1643
1283
  const scrollEnd = Math.max(1, rows - chatBoxHeight);
@@ -1656,76 +1296,44 @@ export class TerminalInput extends EventEmitter {
1656
1296
  // Render pinned chat box at bottom
1657
1297
  this.forceRender();
1658
1298
  }
1659
- /**
1660
- * Clear the last rendered chat box footprint so old frames don't linger when
1661
- * switching between streaming and idle modes.
1662
- */
1663
- clearChatBoxFootprint(minHeight) {
1664
- const { rows } = this.getSize();
1665
- const height = Math.max(1, Math.max(minHeight, this.lastChatBoxHeight));
1666
- const start = this.lastChatBoxStartRow ?? Math.max(1, rows - height + 1);
1667
- const end = Math.min(rows, start + height - 1);
1668
- for (let row = start; row <= end; row++) {
1669
- this.write(ESC.TO(row, 1));
1670
- this.write(ESC.CLEAR_LINE);
1671
- }
1672
- this.lastChatBoxStartRow = null;
1673
- this.lastChatBoxHeight = 0;
1674
- this.lastSuggestionStartRow = null;
1675
- return start;
1676
- }
1677
1299
  /**
1678
1300
  * Exit streaming mode and restore normal operation.
1679
1301
  * Clears the old pinned chat box area to prevent duplication.
1680
1302
  */
1681
- exitStreamingScrollRegion(options = {}) {
1682
- const skipRender = !!options.skipRender;
1683
- const wasActive = this.scrollRegionActive;
1684
- const lastHeight = Math.max(0, this.lastChatBoxHeight);
1685
- const hadFootprint = wasActive || this.lastChatBoxStartRow !== null || lastHeight > 0;
1686
- this.scrollRegionActive = false;
1687
- if (skipRender) {
1688
- this.statusMessage = 'Ready for prompts';
1689
- this.renderDirty = true;
1690
- }
1691
- else {
1692
- this.setStatusMessage('Ready for prompts');
1693
- }
1694
- if (!hadFootprint) {
1695
- this.lastChatBoxStartRow = null;
1696
- this.lastChatBoxHeight = 0;
1697
- this.lastSuggestionStartRow = null;
1698
- return;
1699
- }
1700
- if (!this.isTTY()) {
1701
- this.lastChatBoxStartRow = null;
1702
- this.lastChatBoxHeight = 0;
1703
- this.lastSuggestionStartRow = null;
1704
- if (!skipRender) {
1705
- this.forceRender();
1706
- }
1707
- return;
1708
- }
1709
- const clearHeight = Math.max(1, Math.max(this.getChatBoxHeight(), lastHeight));
1303
+ exitStreamingScrollRegion() {
1304
+ const { rows } = this.getSize();
1305
+ const chatBoxHeight = this.getChatBoxHeight();
1710
1306
  writeLock.lock('exitStreamingScrollRegion');
1711
1307
  try {
1712
- if (wasActive) {
1713
- // Reset scroll region to full terminal
1714
- this.write(ESC.RESET_SCROLL);
1715
- }
1308
+ // Reset scroll region to full terminal
1309
+ this.write(ESC.RESET_SCROLL);
1716
1310
  // Clear the old pinned chat box area to prevent duplication
1717
1311
  // The old chat box was rendered at fixed bottom positions
1718
- const clearStart = this.clearChatBoxFootprint(clearHeight);
1312
+ const oldChatBoxStart = Math.max(1, rows - chatBoxHeight + 1);
1313
+ for (let i = 0; i < chatBoxHeight; i++) {
1314
+ const row = oldChatBoxStart + i;
1315
+ if (row <= rows) {
1316
+ this.write(ESC.TO(row, 1));
1317
+ this.write(ESC.CLEAR_LINE);
1318
+ }
1319
+ }
1719
1320
  // Move cursor back up to where content should continue
1720
- this.write(ESC.TO(clearStart, 1));
1321
+ this.write(ESC.TO(oldChatBoxStart, 1));
1322
+ this.scrollRegionActive = false;
1323
+ this.setStatusMessage('Ready for prompts');
1721
1324
  }
1722
1325
  finally {
1723
1326
  writeLock.unlock();
1724
1327
  }
1725
1328
  // Final render - will render new chat box at correct position
1726
- if (!skipRender) {
1727
- this.forceRender();
1728
- }
1329
+ this.forceRender();
1330
+ }
1331
+ /**
1332
+ * Render chat box at bottom - now uses unified renderer.
1333
+ * @deprecated Use renderPinnedChatBox() directly via render()/forceRender()
1334
+ */
1335
+ renderChatBoxAtBottom() {
1336
+ this.renderPinnedChatBox();
1729
1337
  }
1730
1338
  /**
1731
1339
  * Stream content within the scroll region.
@@ -1755,9 +1363,21 @@ export class TerminalInput extends EventEmitter {
1755
1363
  // Content moved; force a render so the chat box follows.
1756
1364
  this.renderDirty = true;
1757
1365
  }
1758
- this.scheduleStreamingRender(150);
1366
+ this.scheduleStreamingRender(200);
1759
1367
  }
1760
1368
  }
1369
+ /**
1370
+ * Enable scroll region (no-op in floating mode).
1371
+ */
1372
+ enableScrollRegion() {
1373
+ // No-op: using pure floating approach
1374
+ }
1375
+ /**
1376
+ * Disable scroll region (no-op in floating mode).
1377
+ */
1378
+ disableScrollRegion() {
1379
+ // No-op: using pure floating approach
1380
+ }
1761
1381
  /**
1762
1382
  * Calculate chat box height dynamically.
1763
1383
  * Accounts for:
@@ -1787,23 +1407,9 @@ export class TerminalInput extends EventEmitter {
1787
1407
  const filtered = this.getFilteredCommands();
1788
1408
  suggestionLines = Math.min(filtered.length, this.maxVisibleSuggestions) + 1; // +1 for header
1789
1409
  }
1790
- // Recent actions strip (single line when present)
1791
- const recentLines = this.buildRecentActionLines(cols - 2).length;
1792
- // Inline command panel lines
1793
- const inlinePanelLineCount = this.getInlineCommandPanelLines(cols - 2).length;
1794
- // Session frame lines (pinned below the chat box)
1795
- const sessionFrameLineCount = this.getSessionFrameLines(cols - 2).length;
1796
1410
  // Total: meta + divider + input lines + suggestions + controls + buffer
1797
1411
  // Leave a small buffer so streamed content still has space above the chat box
1798
- const totalHeight = metaLines +
1799
- recentLines +
1800
- 1 +
1801
- inlinePanelLineCount +
1802
- inputLines +
1803
- suggestionLines +
1804
- controlLineCount +
1805
- sessionFrameLineCount +
1806
- 1;
1412
+ const totalHeight = metaLines + 1 + inputLines + suggestionLines + controlLineCount + 1;
1807
1413
  const minContentRows = 2;
1808
1414
  const maxHeight = Math.max(4, rows - minContentRows);
1809
1415
  return Math.min(totalHeight, maxHeight);
@@ -1816,38 +1422,16 @@ export class TerminalInput extends EventEmitter {
1816
1422
  const display = _display;
1817
1423
  if (!display?.registerOutputInterceptor)
1818
1424
  return;
1819
- const readWrittenLines = () => {
1820
- const total = display.getTotalWrittenLines?.();
1821
- return typeof total === 'number' && Number.isFinite(total) ? total : null;
1822
- };
1823
- let beforeLines = null;
1824
1425
  // Remove prior hook if present
1825
1426
  if (this.displayInterceptorDispose) {
1826
1427
  this.displayInterceptorDispose();
1827
1428
  this.displayInterceptorDispose = null;
1828
1429
  }
1829
1430
  this.displayInterceptorDispose = display.registerOutputInterceptor({
1830
- beforeWrite: () => {
1831
- beforeLines = readWrittenLines();
1832
- },
1833
1431
  afterWrite: (content) => {
1834
- if (content) {
1835
- this.trackExternalOutput(content);
1432
+ if (!content)
1836
1433
  return;
1837
- }
1838
- const afterLines = readWrittenLines();
1839
- if (beforeLines !== null && afterLines !== null && afterLines > beforeLines) {
1840
- const delta = afterLines - beforeLines;
1841
- this.addToScrollback('\n'.repeat(delta));
1842
- this.contentRow += delta;
1843
- }
1844
- // When content is unknown, force a prompt redraw so the chat box stays below the latest output.
1845
- if (this.scrollRegionActive) {
1846
- this.scheduleStreamingRender(this.streamingRenderInterval);
1847
- }
1848
- else {
1849
- this.forceRender();
1850
- }
1434
+ this.trackExternalOutput(content);
1851
1435
  },
1852
1436
  });
1853
1437
  }
@@ -1860,12 +1444,6 @@ export class TerminalInput extends EventEmitter {
1860
1444
  return;
1861
1445
  // Capture content in scrollback buffer
1862
1446
  this.addToScrollback(content);
1863
- if (!this.isTTY()) {
1864
- this.write(content);
1865
- const rowsUsed = this.countRenderedRows(content);
1866
- this.contentRow += rowsUsed;
1867
- return;
1868
- }
1869
1447
  writeLock.lock('writeToScrollRegion');
1870
1448
  try {
1871
1449
  // Position cursor at content row and write
@@ -1911,12 +1489,6 @@ export class TerminalInput extends EventEmitter {
1911
1489
  getScrollbackSnapshot() {
1912
1490
  return [...this.scrollbackBuffer];
1913
1491
  }
1914
- /**
1915
- * Check if a streaming scroll region is currently active.
1916
- */
1917
- isScrollRegionActive() {
1918
- return this.scrollRegionActive;
1919
- }
1920
1492
  /**
1921
1493
  * Clear the visible terminal area and reset content position.
1922
1494
  * In terminal-native mode, this just adds newlines to scroll past content
@@ -1967,6 +1539,7 @@ export class TerminalInput extends EventEmitter {
1967
1539
  this.displayInterceptorDispose();
1968
1540
  this.displayInterceptorDispose = null;
1969
1541
  }
1542
+ this.disableScrollRegion();
1970
1543
  this.resetStreamingRenderThrottle();
1971
1544
  this.disableBracketedPaste();
1972
1545
  this.buffer = '';
@@ -1987,10 +1560,6 @@ export class TerminalInput extends EventEmitter {
1987
1560
  }
1988
1561
  return true;
1989
1562
  case 'a': // Home
1990
- if (key.shift) {
1991
- this.emit('toggleAlphaZero');
1992
- return true;
1993
- }
1994
1563
  this.moveCursorToLineStart();
1995
1564
  return true;
1996
1565
  case 'e': // End
@@ -2086,10 +1655,6 @@ export class TerminalInput extends EventEmitter {
2086
1655
  // Alt+T: Toggle/cycle thinking mode
2087
1656
  this.emit('toggleThinking');
2088
1657
  break;
2089
- case 'a':
2090
- // Alt+A: Toggle AlphaZero RL mode
2091
- this.emit('toggleAlphaZero');
2092
- break;
2093
1658
  case 'e':
2094
1659
  // Alt+E: Toggle edit permission mode (ask/auto)
2095
1660
  this.toggleEditMode();
@@ -2787,20 +2352,6 @@ export class TerminalInput extends EventEmitter {
2787
2352
  // ===========================================================================
2788
2353
  // UTILITIES
2789
2354
  // ===========================================================================
2790
- normalizeInlineCommandContent(content) {
2791
- if (content === null || content === undefined) {
2792
- return null;
2793
- }
2794
- const joined = Array.isArray(content) ? content.join('\n') : content;
2795
- if (typeof joined !== 'string') {
2796
- return null;
2797
- }
2798
- const normalized = joined.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
2799
- // Strip control characters except ESC (keeps ANSI styling intact)
2800
- const cleaned = normalized.replace(/[\x00-\x08\x0B-\x0C\x0E-\x1A\x1C-\x1F\x7F]/g, '');
2801
- const trimmed = cleaned.trimEnd();
2802
- return trimmed.length > 0 ? trimmed : null;
2803
- }
2804
2355
  getComposedLength() {
2805
2356
  if (this.pastePlaceholders.length === 0) {
2806
2357
  return this.buffer.length;
@@ -2967,13 +2518,12 @@ export class TerminalInput extends EventEmitter {
2967
2518
  * pending UI changes. Prevents meta/header lines from being echoed into the
2968
2519
  * streamed log when nothing changed.
2969
2520
  */
2970
- renderStreamingFrame(force = false) {
2521
+ renderStreamingFrame() {
2971
2522
  if (!this.canRender())
2972
2523
  return;
2973
2524
  if (this.isRendering)
2974
2525
  return;
2975
- const shouldSkip = !force &&
2976
- !this.renderDirty &&
2526
+ const shouldSkip = !this.renderDirty &&
2977
2527
  this.buffer === this.lastRenderContent &&
2978
2528
  this.cursor === this.lastRenderCursor;
2979
2529
  // Clear the dirty flag even when skipping to avoid runaway retries.
@@ -2982,7 +2532,7 @@ export class TerminalInput extends EventEmitter {
2982
2532
  return;
2983
2533
  }
2984
2534
  if (writeLock.isLocked()) {
2985
- writeLock.safeWrite(() => this.renderStreamingFrame(force));
2535
+ writeLock.safeWrite(() => this.renderStreamingFrame());
2986
2536
  return;
2987
2537
  }
2988
2538
  this.renderPinnedChatBox();
@@ -2998,14 +2548,7 @@ export class TerminalInput extends EventEmitter {
2998
2548
  if (!this.canRender())
2999
2549
  return;
3000
2550
  this.renderDirty = true;
3001
- queueMicrotask(() => {
3002
- const streamingActive = this.scrollRegionActive || this.mode === 'streaming' || isStreamingMode();
3003
- if (streamingActive && this.scrollRegionActive) {
3004
- this.renderStreamingFrame();
3005
- return;
3006
- }
3007
- this.render();
3008
- });
2551
+ queueMicrotask(() => this.render());
3009
2552
  }
3010
2553
  canRender() {
3011
2554
  return !this.disposed && this.enabled && this.isTTY();
@@ -3036,7 +2579,7 @@ export class TerminalInput extends EventEmitter {
3036
2579
  return text
3037
2580
  .replace(/\x1b\][^\x07]*(?:\x07|\x1b\\)/g, '') // OSC sequences
3038
2581
  .replace(/\x1b\[[0-9;]*[A-Za-z]/g, '') // CSI sequences
3039
- .replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F]/g, '') // Control chars except \n and \r (incl. DEL/C1)
2582
+ .replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g, '') // Control chars except \n and \r
3040
2583
  .replace(/\r\n?/g, '\n'); // Normalize line endings
3041
2584
  }
3042
2585
  getArrowKeyName(sequence) {