erosolar-cli 1.7.410 → 1.7.412
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/README.md +6 -6
- package/dist/StringUtils.d.ts +1 -4
- package/dist/StringUtils.d.ts.map +1 -1
- package/dist/StringUtils.js +2 -8
- package/dist/StringUtils.js.map +1 -1
- package/dist/browser/BrowserSessionManager.d.ts +1 -3
- package/dist/browser/BrowserSessionManager.d.ts.map +1 -1
- package/dist/browser/BrowserSessionManager.js +4 -24
- package/dist/browser/BrowserSessionManager.js.map +1 -1
- package/dist/capabilities/askUserCapability.d.ts.map +1 -1
- package/dist/capabilities/askUserCapability.js +64 -10
- package/dist/capabilities/askUserCapability.js.map +1 -1
- package/dist/capabilities/toolRegistry.d.ts +2 -0
- package/dist/capabilities/toolRegistry.d.ts.map +1 -1
- package/dist/capabilities/toolRegistry.js +40 -5
- package/dist/capabilities/toolRegistry.js.map +1 -1
- package/dist/contracts/agent-profiles.schema.json +5 -5
- package/dist/contracts/agent-schemas.json +6 -16
- package/dist/contracts/schemas/agent.schema.json +1 -5
- package/dist/contracts/schemas/tool-selection.schema.json +1 -7
- package/dist/contracts/tools.schema.json +80 -207
- package/dist/contracts/unified-schema.json +4 -5
- package/dist/contracts/v1/agent.d.ts +0 -3
- package/dist/contracts/v1/agent.d.ts.map +1 -1
- package/dist/contracts/v1/provider.d.ts +1 -2
- package/dist/contracts/v1/provider.d.ts.map +1 -1
- package/dist/contracts/v1/toolAccess.d.ts +1 -1
- package/dist/contracts/v1/toolAccess.d.ts.map +1 -1
- package/dist/core/agent.d.ts +1 -7
- package/dist/core/agent.d.ts.map +1 -1
- package/dist/core/agent.js +2 -131
- package/dist/core/agent.js.map +1 -1
- package/dist/core/alphaZeroEngine.d.ts +0 -8
- package/dist/core/alphaZeroEngine.d.ts.map +1 -1
- package/dist/core/alphaZeroEngine.js +35 -149
- package/dist/core/alphaZeroEngine.js.map +1 -1
- package/dist/core/alphaZeroOrchestrator.d.ts +0 -17
- package/dist/core/alphaZeroOrchestrator.d.ts.map +1 -1
- package/dist/core/alphaZeroOrchestrator.js +8 -95
- package/dist/core/alphaZeroOrchestrator.js.map +1 -1
- package/dist/core/claudeCodeFeatures.d.ts +2 -1
- package/dist/core/claudeCodeFeatures.d.ts.map +1 -1
- package/dist/core/claudeCodeFeatures.js +2 -1
- package/dist/core/claudeCodeFeatures.js.map +1 -1
- package/dist/core/cliTestHarness.d.ts +0 -5
- package/dist/core/cliTestHarness.d.ts.map +1 -1
- package/dist/core/cliTestHarness.js +3 -14
- package/dist/core/cliTestHarness.js.map +1 -1
- package/dist/core/contextManager.d.ts +0 -30
- package/dist/core/contextManager.d.ts.map +1 -1
- package/dist/core/contextManager.js +5 -87
- package/dist/core/contextManager.js.map +1 -1
- package/dist/core/contextWindow.d.ts +4 -4
- package/dist/core/contextWindow.js +9 -9
- package/dist/core/contextWindow.js.map +1 -1
- package/dist/core/modelDiscovery.js +3 -3
- package/dist/core/modelDiscovery.js.map +1 -1
- package/dist/core/preferences.d.ts +2 -3
- package/dist/core/preferences.d.ts.map +1 -1
- package/dist/core/preferences.js +11 -18
- package/dist/core/preferences.js.map +1 -1
- package/dist/core/secretStore.d.ts.map +1 -1
- package/dist/core/secretStore.js +31 -0
- package/dist/core/secretStore.js.map +1 -1
- package/dist/core/toolPreconditions.d.ts +1 -0
- package/dist/core/toolPreconditions.d.ts.map +1 -1
- package/dist/core/toolPreconditions.js +13 -64
- package/dist/core/toolPreconditions.js.map +1 -1
- package/dist/core/toolRuntime.d.ts.map +1 -1
- package/dist/core/toolRuntime.js +0 -17
- package/dist/core/toolRuntime.js.map +1 -1
- package/dist/core/types.d.ts +1 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/headless/headlessApp.d.ts.map +1 -1
- package/dist/headless/headlessApp.js +6 -22
- package/dist/headless/headlessApp.js.map +1 -1
- package/dist/plugins/providers/google/index.js +3 -2
- package/dist/plugins/providers/google/index.js.map +1 -1
- package/dist/providers/openaiChatCompletionsProvider.d.ts.map +1 -1
- package/dist/providers/openaiChatCompletionsProvider.js +6 -60
- package/dist/providers/openaiChatCompletionsProvider.js.map +1 -1
- package/dist/runtime/agentController.d.ts.map +1 -1
- package/dist/runtime/agentController.js +6 -27
- package/dist/runtime/agentController.js.map +1 -1
- package/dist/shell/interactiveShell.d.ts +32 -98
- package/dist/shell/interactiveShell.d.ts.map +1 -1
- package/dist/shell/interactiveShell.js +733 -1645
- package/dist/shell/interactiveShell.js.map +1 -1
- package/dist/shell/shellApp.d.ts.map +1 -1
- package/dist/shell/shellApp.js +41 -15
- package/dist/shell/shellApp.js.map +1 -1
- package/dist/shell/systemPrompt.d.ts.map +1 -1
- package/dist/shell/systemPrompt.js +0 -1
- package/dist/shell/systemPrompt.js.map +1 -1
- package/dist/shell/terminalInput.d.ts +21 -85
- package/dist/shell/terminalInput.d.ts.map +1 -1
- package/dist/shell/terminalInput.js +62 -519
- package/dist/shell/terminalInput.js.map +1 -1
- package/dist/shell/terminalInputAdapter.d.ts +16 -37
- package/dist/shell/terminalInputAdapter.d.ts.map +1 -1
- package/dist/shell/terminalInputAdapter.js +22 -44
- package/dist/shell/terminalInputAdapter.js.map +1 -1
- package/dist/shell/updateManager.d.ts.map +1 -1
- package/dist/shell/updateManager.js +17 -1
- package/dist/shell/updateManager.js.map +1 -1
- package/dist/tools/buildTools.d.ts.map +1 -1
- package/dist/tools/buildTools.js +76 -19
- package/dist/tools/buildTools.js.map +1 -1
- package/dist/tools/editTools.js +1 -1
- package/dist/tools/editTools.js.map +1 -1
- package/dist/tools/enhancedCodeIntelligenceTools.js +2 -1
- package/dist/tools/enhancedCodeIntelligenceTools.js.map +1 -1
- package/dist/tools/fileTools.js +0 -3
- package/dist/tools/fileTools.js.map +1 -1
- package/dist/tools/frontendTestingTools.js +1 -1
- package/dist/tools/frontendTestingTools.js.map +1 -1
- package/dist/tools/interactionTools.d.ts.map +1 -1
- package/dist/tools/interactionTools.js +82 -15
- package/dist/tools/interactionTools.js.map +1 -1
- package/dist/tools/learnTools.d.ts +0 -2
- package/dist/tools/learnTools.d.ts.map +1 -1
- package/dist/tools/learnTools.js +81 -29
- package/dist/tools/learnTools.js.map +1 -1
- package/dist/tools/localExplore.d.ts.map +1 -1
- package/dist/tools/localExplore.js +1 -0
- package/dist/tools/localExplore.js.map +1 -1
- package/dist/tools/notebookEditTools.js.map +1 -1
- package/dist/tools/repoChecksTools.js +3 -4
- package/dist/tools/repoChecksTools.js.map +1 -1
- package/dist/tools/searchTools.js +0 -4
- package/dist/tools/searchTools.js.map +1 -1
- package/dist/tools/softwareEngineeringTools.d.ts.map +1 -1
- package/dist/tools/softwareEngineeringTools.js +0 -1
- package/dist/tools/softwareEngineeringTools.js.map +1 -1
- package/dist/tools/webTools.d.ts.map +1 -1
- package/dist/tools/webTools.js.map +1 -1
- package/dist/ui/ShellUIAdapter.d.ts +13 -52
- package/dist/ui/ShellUIAdapter.d.ts.map +1 -1
- package/dist/ui/ShellUIAdapter.js +72 -373
- package/dist/ui/ShellUIAdapter.js.map +1 -1
- package/dist/ui/display.d.ts +38 -19
- package/dist/ui/display.d.ts.map +1 -1
- package/dist/ui/display.js +310 -96
- package/dist/ui/display.js.map +1 -1
- package/dist/ui/orchestration/UIUpdateCoordinator.d.ts.map +1 -1
- package/dist/ui/orchestration/UIUpdateCoordinator.js +3 -5
- package/dist/ui/orchestration/UIUpdateCoordinator.js.map +1 -1
- package/dist/ui/shortcutsHelp.d.ts +1 -1
- package/dist/ui/shortcutsHelp.d.ts.map +1 -1
- package/dist/ui/shortcutsHelp.js +6 -11
- package/dist/ui/shortcutsHelp.js.map +1 -1
- package/dist/ui/streamingFormatter.d.ts +17 -0
- package/dist/ui/streamingFormatter.d.ts.map +1 -0
- package/dist/ui/streamingFormatter.js +71 -0
- package/dist/ui/streamingFormatter.js.map +1 -0
- package/dist/ui/theme.d.ts +100 -100
- package/dist/ui/theme.d.ts.map +1 -1
- package/dist/ui/theme.js.map +1 -1
- package/dist/ui/toolDisplay.d.ts.map +1 -1
- package/dist/ui/toolDisplay.js +1 -7
- package/dist/ui/toolDisplay.js.map +1 -1
- package/dist/ui/unified/index.d.ts +3 -2
- package/dist/ui/unified/index.d.ts.map +1 -1
- package/dist/ui/unified/index.js +1 -0
- package/dist/ui/unified/index.js.map +1 -1
- package/dist/ui/unified/layout.d.ts +0 -14
- package/dist/ui/unified/layout.d.ts.map +1 -1
- package/dist/ui/unified/layout.js +1 -67
- package/dist/ui/unified/layout.js.map +1 -1
- package/package.json +24 -37
- package/dist/core/alphaZeroConfig.d.ts +0 -11
- package/dist/core/alphaZeroConfig.d.ts.map +0 -1
- package/dist/core/alphaZeroConfig.js +0 -59
- package/dist/core/alphaZeroConfig.js.map +0 -1
- package/dist/core/alphaZeroEnhanced.d.ts +0 -125
- package/dist/core/alphaZeroEnhanced.d.ts.map +0 -1
- package/dist/core/alphaZeroEnhanced.js +0 -386
- package/dist/core/alphaZeroEnhanced.js.map +0 -1
- package/dist/core/autonomousVerification.d.ts +0 -103
- package/dist/core/autonomousVerification.d.ts.map +0 -1
- package/dist/core/autonomousVerification.js +0 -583
- package/dist/core/autonomousVerification.js.map +0 -1
- package/dist/core/offsecAlphaZeroEnhanced.d.ts +0 -98
- package/dist/core/offsecAlphaZeroEnhanced.d.ts.map +0 -1
- package/dist/core/offsecAlphaZeroEnhanced.js +0 -441
- package/dist/core/offsecAlphaZeroEnhanced.js.map +0 -1
- package/dist/core/parallelAgentOrchestrator.d.ts +0 -171
- package/dist/core/parallelAgentOrchestrator.d.ts.map +0 -1
- package/dist/core/parallelAgentOrchestrator.js +0 -459
- package/dist/core/parallelAgentOrchestrator.js.map +0 -1
- package/dist/index.d.ts +0 -5
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -3
- package/dist/index.js.map +0 -1
- package/dist/tools/detectCommands.d.ts +0 -8
- package/dist/tools/detectCommands.d.ts.map +0 -1
- package/dist/tools/detectCommands.js +0 -183
- package/dist/tools/detectCommands.js.map +0 -1
- package/dist/ui/assistantBlockRenderer.d.ts +0 -30
- package/dist/ui/assistantBlockRenderer.d.ts.map +0 -1
- package/dist/ui/assistantBlockRenderer.js +0 -121
- 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,
|
|
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 =
|
|
119
|
+
verificationEnabled = false;
|
|
120
120
|
autoContinueEnabled = false;
|
|
121
121
|
verificationHotkey = 'ctrl+shift+v';
|
|
122
122
|
autoContinueHotkey = 'ctrl+shift+c';
|
|
123
|
-
thinkingHotkey = '
|
|
124
|
-
alphaZeroModeEnabled = false;
|
|
125
|
-
alphaZeroHotkey = 'ctrl+shift+a';
|
|
126
|
-
alphaZeroLabel = null;
|
|
123
|
+
thinkingHotkey = 'tab';
|
|
127
124
|
modelLabel = null;
|
|
128
125
|
providerLabel = null;
|
|
129
|
-
featureStatus = null;
|
|
130
126
|
// Streaming render throttle
|
|
131
127
|
lastStreamingRender = 0;
|
|
132
|
-
streamingRenderInterval =
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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(
|
|
1682
|
-
const
|
|
1683
|
-
const
|
|
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
|
-
|
|
1713
|
-
|
|
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
|
|
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(
|
|
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
|
-
|
|
1727
|
-
|
|
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(
|
|
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();
|
|
@@ -2258,7 +1823,7 @@ export class TerminalInput extends EventEmitter {
|
|
|
2258
1823
|
this.toggleEditMode();
|
|
2259
1824
|
return true;
|
|
2260
1825
|
}
|
|
2261
|
-
this.
|
|
1826
|
+
this.emit('toggleThinking');
|
|
2262
1827
|
return true;
|
|
2263
1828
|
}
|
|
2264
1829
|
return false;
|
|
@@ -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(
|
|
2521
|
+
renderStreamingFrame() {
|
|
2971
2522
|
if (!this.canRender())
|
|
2972
2523
|
return;
|
|
2973
2524
|
if (this.isRendering)
|
|
2974
2525
|
return;
|
|
2975
|
-
const shouldSkip = !
|
|
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(
|
|
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
|
|
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) {
|